diff --git a/.github/workflows/nx-report.yml b/.github/workflows/nx-report.yml
index 188f5af..b089c8c 100644
--- a/.github/workflows/nx-report.yml
+++ b/.github/workflows/nx-report.yml
@@ -14,11 +14,11 @@ jobs:
init-commands: |
npx nx-cloud start-ci-run --agent-count=3
parallel-commands: |
- npx nx-cloud record -- npx nx affected --target=build
+ npx nx-cloud record -- npx nx affected --target=build:all
npx nx-cloud record -- npx nx affected --target=typecheck
npx nx-cloud record -- npx nx affected --target=test
parallel-commands-on-agents: |
- npx nx affected --target=build --parallel=3
+ npx nx affected --target=build:all --parallel=3
npx nx affected --target=typecheck --parallel=3
npx nx affected --target=test --parallel=3
diff --git a/.vscode/settings.json b/.vscode/settings.json
index a8122d6..0387840 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -3,5 +3,8 @@
"Parser.ts": "i18n",
"Tokenizer.ts": "i18n"
},
- "material-icon-theme.folders.associations": {}
+ "material-icon-theme.folders.associations": {},
+ "editor.insertSpaces": false,
+ "editor.detectIndentation": false,
+ "editor.trimAutoWhitespace": false
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 84b75d7..77d88e5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,21 +1,42 @@
-## [1.0.0-rc.2](https://github.com/formkl/formkl/compare/1.0.0-rc.1...1.0.0-rc.2) (2022-11-23)
+## [1.0.0-rc.3](https://github.com/imrim12/formkl/compare/1.0.0-rc.2...1.0.0-rc.3) (2022-12-25)
### Features
-* added husky git hooks ([966c15f](https://github.com/formkl/formkl/commit/966c15f7dae5aeebb7525104a135c4c9e7a4faad))
-* added release-it package for release ([e46d2c0](https://github.com/formkl/formkl/commit/e46d2c06161baea229213e5bb26be6386f44bd10))
-* support form lifecycle hooks ([d2aff34](https://github.com/formkl/formkl/commit/d2aff3436ad0c9e77b4d3728f0dcfe915f6be804))
+* add stringifier ([8c8d13e](https://github.com/imrim12/formkl/commit/8c8d13e22c4f6cfd2707ec0423700590bc7bb62a))
+* added husky git hooks ([966c15f](https://github.com/imrim12/formkl/commit/966c15f7dae5aeebb7525104a135c4c9e7a4faad))
+* added release-it package for release ([e46d2c0](https://github.com/imrim12/formkl/commit/e46d2c06161baea229213e5bb26be6386f44bd10))
+* handle keyword value in validation ([45972c6](https://github.com/imrim12/formkl/commit/45972c6a4116fe34881898adb1d2554f15bd9e6c))
+* support form lifecycle hooks ([d2aff34](https://github.com/imrim12/formkl/commit/d2aff3436ad0c9e77b4d3728f0dcfe915f6be804))
### Bug Fixes
-* always return true when use regex with logic validator ([3dd1e41](https://github.com/formkl/formkl/commit/3dd1e418f181390600c656f6956b28d1bc8f99ec))
-* build fail ([51298d8](https://github.com/formkl/formkl/commit/51298d80524a2359195d3f861208c1b06e0108df))
-* circular dependencies build fail nx ([d6c59a6](https://github.com/formkl/formkl/commit/d6c59a68df2c44b9f1d42018bb95ffef7b435605))
-* editor web component lifecycle works differently on vue/react ([7d006b7](https://github.com/formkl/formkl/commit/7d006b776c28ef33682cd0906c241ae4e78cb5e7))
-* setup vitest component testing ([8a3f07d](https://github.com/formkl/formkl/commit/8a3f07df969299dadb78e4c3b8e96b3ccbaf3dac))
-* tsconfig.json ([d7657fa](https://github.com/formkl/formkl/commit/d7657fa536358d70136f328b12c8b5d52b600f77))
-* vercel build fail ([8ad35a6](https://github.com/formkl/formkl/commit/8ad35a6a93a25e6d1ae65b2731db73360e870a70))
\ No newline at end of file
+* always return true when use regex with logic validator ([3dd1e41](https://github.com/imrim12/formkl/commit/3dd1e418f181390600c656f6956b28d1bc8f99ec))
+* circular dependencies build fail nx ([d6c59a6](https://github.com/imrim12/formkl/commit/d6c59a68df2c44b9f1d42018bb95ffef7b435605))
+* editor web component lifecycle works differently on vue/react ([7d006b7](https://github.com/imrim12/formkl/commit/7d006b776c28ef33682cd0906c241ae4e78cb5e7))
+* setup vitest component testing ([8a3f07d](https://github.com/imrim12/formkl/commit/8a3f07df969299dadb78e4c3b8e96b3ccbaf3dac))
+* tsconfig.json ([d7657fa](https://github.com/imrim12/formkl/commit/d7657fa536358d70136f328b12c8b5d52b600f77))
+* vercel build fail ([8ad35a6](https://github.com/imrim12/formkl/commit/8ad35a6a93a25e6d1ae65b2731db73360e870a70))
+
+## [1.0.0-rc.2](https://github.com/imrim12/formkl/compare/1.0.0-rc.1...1.0.0-rc.2) (2022-11-23)
+
+
+### Features
+
+* added husky git hooks ([966c15f](https://github.com/imrim12/formkl/commit/966c15f7dae5aeebb7525104a135c4c9e7a4faad))
+* added release-it package for release ([e46d2c0](https://github.com/imrim12/formkl/commit/e46d2c06161baea229213e5bb26be6386f44bd10))
+* support form lifecycle hooks ([d2aff34](https://github.com/imrim12/formkl/commit/d2aff3436ad0c9e77b4d3728f0dcfe915f6be804))
+
+
+### Bug Fixes
+
+* always return true when use regex with logic validator ([3dd1e41](https://github.com/imrim12/formkl/commit/3dd1e418f181390600c656f6956b28d1bc8f99ec))
+* build fail ([51298d8](https://github.com/imrim12/formkl/commit/51298d80524a2359195d3f861208c1b06e0108df))
+* circular dependencies build fail nx ([d6c59a6](https://github.com/imrim12/formkl/commit/d6c59a68df2c44b9f1d42018bb95ffef7b435605))
+* editor web component lifecycle works differently on vue/react ([7d006b7](https://github.com/imrim12/formkl/commit/7d006b776c28ef33682cd0906c241ae4e78cb5e7))
+* setup vitest component testing ([8a3f07d](https://github.com/imrim12/formkl/commit/8a3f07df969299dadb78e4c3b8e96b3ccbaf3dac))
+* tsconfig.json ([d7657fa](https://github.com/imrim12/formkl/commit/d7657fa536358d70136f328b12c8b5d52b600f77))
+* vercel build fail ([8ad35a6](https://github.com/imrim12/formkl/commit/8ad35a6a93a25e6d1ae65b2731db73360e870a70))
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 6899358..16b06fd 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -8,8 +8,8 @@ Please make sure to read the [Contributing Guide](https://formkl.org/learning/co
Thank you to all the people who already contributed to Vue!
-
-
+
+
Made with [contrib.rocks](https://contrib.rocks).
diff --git a/README.md b/README.md
index 30abe77..7104edc 100644
--- a/README.md
+++ b/README.md
@@ -14,8 +14,8 @@ Please make sure to read the [Contributing Guide](https://formkl.org/learning/co
Thank you to all the people who already contributed to the Formkl - Form Markup Language project!
-
-
+
+
Made with [contrib.rocks](https://contrib.rocks).
diff --git a/docs/.vitepress/config.js b/docs/.vitepress/config.js
index fe8be8e..1829d96 100644
--- a/docs/.vitepress/config.js
+++ b/docs/.vitepress/config.js
@@ -9,10 +9,10 @@ export default {
nav: [{ text: "Syntax guide", link: "/syntax/form" }],
- socialLinks: [{ icon: "github", link: "https://github.com/formkl/formkl" }],
+ socialLinks: [{ icon: "github", link: "https://github.com/imrim12/formkl" }],
editLink: {
- pattern: "https://github.com/formkl/formkl/edit/main/docs/:path",
+ pattern: "https://github.com/imrim12/formkl/edit/main/docs/:path",
text: "Edit this page on GitHub",
},
diff --git a/docs/index.md b/docs/index.md
index ef87dba..d2204d9 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -17,7 +17,7 @@ hero:
link: /introduction
- theme: alt
text: Star on GitHub
- link: https://github.com/formkl/formkl
+ link: https://github.com/imrim12/formkl
features:
- icon: ⚡️
diff --git a/docs/learning/contribution-guide.md b/docs/learning/contribution-guide.md
index 93ef8f8..e19cf05 100644
--- a/docs/learning/contribution-guide.md
+++ b/docs/learning/contribution-guide.md
@@ -27,9 +27,9 @@ More about pnpm, please refer to [pnpm.io](https://pnpm.io), if you don't have i
npm i -g pnpm
```
-Then clone the [repository](https://github.com/formkl/formkl) and open the cloned directory with your code editor or IDE.
+Then clone the [repository](https://github.com/imrim12/formkl) and open the cloned directory with your code editor or IDE.
```bash
-git clone git@github.com:formkl/formkl.git
+git clone git@github.com:imrim12/formkl.git
```
Install the workspace dependencies
diff --git a/docs/learning/editor.md b/docs/learning/editor.md
index 9a67084..91c0efc 100644
--- a/docs/learning/editor.md
+++ b/docs/learning/editor.md
@@ -17,7 +17,7 @@ But in our case, this is a brand new language and we need to build the syntax hi
We use [`@codemirror/autocomplete`](https://codemirror.net/6/docs/ref/#autocomplete) to define a set of keyword and definitions for auto complete.
-The code is located at [`packages/editor/src/extensions/autocomplete.ts`](https://github.com/formkl/formkl/blob/9b5537cd326534208e2154b50664d9d098fb7113/packages/editor/src/extensions/autocomplete.ts)
+The code is located at [`packages/editor/src/extensions/autocomplete.ts`](https://github.com/imrim12/formkl/blob/9b5537cd326534208e2154b50664d9d098fb7113/packages/editor/src/extensions/autocomplete.ts)
## Syntax Error Checker
@@ -25,4 +25,4 @@ Error Checker or as known as Linter is a very important part of the editor. It h
We use our own [FORMKL Parser](/introduction#basic-example) to parse the syntax, it would be the correct syntax if it's parsed successfully. Otherwise, the parser will throw a syntax error.
-The code is located at [`packages/editor/src/extensions/lint.ts`](https://github.com/formkl/formkl/blob/9b5537cd326534208e2154b50664d9d098fb7113/packages/editor/src/extensions/lint.ts).
+The code is located at [`packages/editor/src/extensions/lint.ts`](https://github.com/imrim12/formkl/blob/9b5537cd326534208e2154b50664d9d098fb7113/packages/editor/src/extensions/lint.ts).
diff --git a/package.json b/package.json
index d15eab9..23cb105 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,9 @@
"description": "Form markup language",
"packageManager": "pnpm@7.12.2",
"scripts": {
- "build": "nx run-many --target=build --all",
+ "nx": "nx",
+ "build": "nx build",
+ "build:all": "nx run-many --target=build --all",
"typecheck": "nx run-many --target=typecheck --all",
"test": "vitest run",
"test:ui": "vitest --ui",
@@ -17,7 +19,7 @@
},
"repository": {
"type": "git",
- "url": "git+https://github.com/formkl/formkl.git"
+ "url": "git+https://github.com/imrim12/formkl.git"
},
"keywords": [
"form",
@@ -30,9 +32,9 @@
"author": "thecodeorigin",
"license": "MIT",
"bugs": {
- "url": "https://github.com/formkl/formkl/issues"
+ "url": "https://github.com/imrim12/formkl/issues"
},
- "homepage": "https://github.com/formkl/formkl#readme",
+ "homepage": "https://github.com/imrim12/formkl#readme",
"workspaces": [
"packages/**",
"sandbox",
@@ -64,10 +66,11 @@
"nx": "^15.0.0",
"release-it": "^15.5.0",
"rimraf": "^3.0.2",
+ "ts-node": "^10.9.1",
"typescript": "^4.8.4",
"vitest": "^0.24.4",
"vue": "^3.2.41",
"vue-tsc": "^1.0.9"
},
- "version": "1.0.0-rc.2"
+ "version": "1.0.0-rc.3"
}
diff --git a/packages/adapters/vue/package.json b/packages/adapters/vue/package.json
index c9ae61d..b8bd82f 100644
--- a/packages/adapters/vue/package.json
+++ b/packages/adapters/vue/package.json
@@ -1,6 +1,6 @@
{
"name": "@formkl/vue",
- "version": "1.0.0-rc.2",
+ "version": "1.0.0-rc.3",
"description": "A Vue adapter to generate usable Vue from component from Formkl syntax/schema",
"type": "module",
"main": "dist/index.es.js",
@@ -12,7 +12,7 @@
},
"repository": {
"type": "git",
- "url": "git+https://github.com/formkl/formkl.git",
+ "url": "git+https://github.com/imrim12/formkl.git",
"directory": "packages/adapters/vue"
},
"keywords": [
@@ -21,9 +21,9 @@
"author": "thecodeorigin",
"license": "MIT",
"bugs": {
- "url": "https://github.com/formkl/formkl/issues"
+ "url": "https://github.com/imrim12/formkl/issues"
},
- "homepage": "https://github.com/formkl/formkl#readme",
+ "homepage": "https://github.com/imrim12/formkl#readme",
"dependencies": {
"@formkl/plugin-vue": "workspace:*",
"@formkl/shared": "workspace:*",
diff --git a/packages/editor/package.json b/packages/editor/package.json
index a81753d..37c5f71 100644
--- a/packages/editor/package.json
+++ b/packages/editor/package.json
@@ -1,6 +1,6 @@
{
"name": "@formkl/editor",
- "version": "1.0.0-rc.2",
+ "version": "1.0.0-rc.3",
"description": "A universal Editor for Formkl using web component API",
"type": "module",
"exports": {
@@ -20,7 +20,7 @@
},
"repository": {
"type": "git",
- "url": "git+ssh://git@github.com/formkl/formkl.git",
+ "url": "git+ssh://git@github.com/imrim12/formkl.git",
"directory": "packages/editor"
},
"keywords": [
@@ -30,9 +30,9 @@
"author": "thecodeorigin",
"license": "MIT",
"bugs": {
- "url": "https://github.com/formkl/formkl/issues"
+ "url": "https://github.com/imrim12/formkl/issues"
},
- "homepage": "https://github.com/formkl/formkl#readme",
+ "homepage": "https://github.com/imrim12/formkl#readme",
"dependencies": {
"@codemirror/autocomplete": "^6.2.0",
"@codemirror/commands": "^6.1.0",
diff --git a/packages/language/README.md b/packages/language/README.md
index 30abe77..7104edc 100644
--- a/packages/language/README.md
+++ b/packages/language/README.md
@@ -14,8 +14,8 @@ Please make sure to read the [Contributing Guide](https://formkl.org/learning/co
Thank you to all the people who already contributed to the Formkl - Form Markup Language project!
-
-
+
+
Made with [contrib.rocks](https://contrib.rocks).
diff --git a/packages/language/package.json b/packages/language/package.json
index 0bf539b..acc27e6 100644
--- a/packages/language/package.json
+++ b/packages/language/package.json
@@ -1,6 +1,6 @@
{
"name": "formkl",
- "version": "1.0.0-rc.2",
+ "version": "1.0.0-rc.3",
"description": "Form markup language",
"type": "module",
"exports": {
@@ -20,7 +20,7 @@
},
"repository": {
"type": "git",
- "url": "git+https://github.com/formkl/formkl.git",
+ "url": "git+https://github.com/imrim12/formkl.git",
"directory": "language"
},
"keywords": [
@@ -34,9 +34,9 @@
"author": "thecodeorigin",
"license": "MIT",
"bugs": {
- "url": "https://github.com/formkl/formkl/issues"
+ "url": "https://github.com/imrim12/formkl/issues"
},
- "homepage": "https://github.com/formkl/formkl#readme",
+ "homepage": "https://github.com/imrim12/formkl#readme",
"dependencies": {
"@formkl/shared": "workspace:*",
"slugify": "^1.6.5"
diff --git a/packages/language/src/__tests__/capitalized-syntax.test.ts b/packages/language/src/__tests__/capitalized-syntax.test.ts
index 46f1e5b..5d3d3ca 100644
--- a/packages/language/src/__tests__/capitalized-syntax.test.ts
+++ b/packages/language/src/__tests__/capitalized-syntax.test.ts
@@ -1,4 +1,4 @@
-import parser from "formkl";
+import parser, { defineForm } from "../";
describe("Used with Capitalized syntax", () => {
it("should parse the form syntax correctly", () => {
@@ -9,24 +9,26 @@ describe("Used with Capitalized syntax", () => {
}
}`);
- expect(result).toStrictEqual({
- model: "base",
- sections: [
- {
- fields: [
- {
- type: "text",
- label: "Text",
- key: "text",
- },
- {
- type: "text",
- label: "Another",
- key: "another",
- },
- ],
- },
- ],
- });
+ expect(result).toStrictEqual(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ },
+ {
+ type: "text",
+ label: "Another",
+ key: "another",
+ },
+ ],
+ },
+ ],
+ }),
+ );
});
});
diff --git a/packages/language/src/__tests__/field-multiple-responses.test.ts b/packages/language/src/__tests__/field-multiple-responses.test.ts
index e469a34..33595de 100644
--- a/packages/language/src/__tests__/field-multiple-responses.test.ts
+++ b/packages/language/src/__tests__/field-multiple-responses.test.ts
@@ -1,4 +1,4 @@
-import parser from "formkl";
+import parser, { defineForm } from "../";
describe("Field with multiple responses support", () => {
it("should parse the form syntax correctly", () => {
@@ -8,21 +8,23 @@ describe("Field with multiple responses support", () => {
}
}`);
- expect(result).toStrictEqual({
- model: "base",
- sections: [
- {
- fields: [
- {
- type: "text",
- label: "Text",
- key: "text",
- multiple: true,
- },
- ],
- },
- ],
- });
+ expect(result).toStrictEqual(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ multiple: true,
+ },
+ ],
+ },
+ ],
+ }),
+ );
});
it("should parse the form syntax correctly with multiple required fields", () => {
@@ -32,22 +34,24 @@ describe("Field with multiple responses support", () => {
}
}`);
- expect(result).toStrictEqual({
- model: "base",
- sections: [
- {
- fields: [
- {
- type: "text",
- label: "Text",
- key: "text",
- required: true,
- multiple: true,
- },
- ],
- },
- ],
- });
+ expect(result).toStrictEqual(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ required: true,
+ multiple: true,
+ },
+ ],
+ },
+ ],
+ }),
+ );
});
it("should parse the form syntax correctly with multiple required fields", () => {
@@ -57,21 +61,52 @@ describe("Field with multiple responses support", () => {
}
}`);
- expect(result).toStrictEqual({
- model: "base",
- sections: [
- {
- fields: [
- {
- type: "text",
- label: "Text",
- key: "text",
- required: true,
- multiple: true,
- },
- ],
- },
- ],
- });
+ expect(result).toStrictEqual(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ required: true,
+ multiple: true,
+ },
+ ],
+ },
+ ],
+ }),
+ );
+ });
+
+ it("should stringify the formkl object correctly", () => {
+ const result = parser.stringify(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ required: true,
+ multiple: true,
+ },
+ ],
+ },
+ ],
+ }),
+ );
+
+ expect(result).toBe(
+ `formkl {
+ includes {
+ require multiple text;
+ }
+}`,
+ );
});
});
diff --git a/packages/language/src/__tests__/field-required.test.ts b/packages/language/src/__tests__/field-required.test.ts
index aac30a8..1310f0b 100644
--- a/packages/language/src/__tests__/field-required.test.ts
+++ b/packages/language/src/__tests__/field-required.test.ts
@@ -1,4 +1,4 @@
-import parser from "formkl";
+import parser, { defineForm } from "../";
describe("Required field", () => {
it("should parse the form syntax correctly", () => {
@@ -9,25 +9,59 @@ describe("Required field", () => {
}
}`);
- expect(result).toStrictEqual({
- model: "base",
- sections: [
- {
- fields: [
- {
- type: "text",
- label: "Text",
- key: "text",
- required: true,
- },
- {
- type: "text",
- label: "Not required",
- key: "not-required",
- },
- ],
- },
- ],
- });
+ expect(result).toStrictEqual(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ required: true,
+ },
+ {
+ type: "text",
+ label: "Not required",
+ key: "not-required",
+ },
+ ],
+ },
+ ],
+ }),
+ );
+ });
+
+ it("should stringify the form syntax correctly", () => {
+ const result = parser.stringify(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ required: true,
+ },
+ {
+ type: "text",
+ label: "Not required",
+ key: "not-required",
+ },
+ ],
+ },
+ ],
+ }),
+ );
+
+ expect(result).toBe(`formkl {
+ includes {
+ require text;
+ "Not required" text;
+ }
+}`);
});
});
diff --git a/packages/language/src/__tests__/field-validation-with-keyword-value.test.ts b/packages/language/src/__tests__/field-validation-with-keyword-value.test.ts
new file mode 100644
index 0000000..92571da
--- /dev/null
+++ b/packages/language/src/__tests__/field-validation-with-keyword-value.test.ts
@@ -0,0 +1,197 @@
+import parser, { defineForm } from "../";
+
+describe("Field validation using keyword value like null, undefined, NaN", () => {
+ it("should parse the form syntax correctly", () => {
+ const result = parser.parse(`formkl {
+ includes {
+ text valid(== null);
+ "Test with OR" text valid(> 5 or == NaN or has "Keyword");
+ "Test with AND" text valid(> 5 and == undefined and has "Keyword");
+ "Test with Both" text valid(> 5 or == null and has "Keyword");
+ }
+ }`);
+
+ expect(result).toStrictEqual(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ validation: {
+ logic: {
+ $eq: null,
+ },
+ },
+ },
+ {
+ type: "text",
+ label: "Test with OR",
+ key: "test-with-or",
+ validation: {
+ logic: {
+ $or: [
+ {
+ $gt: 5,
+ },
+ {
+ $eq: NaN,
+ },
+ {
+ $has: "Keyword",
+ },
+ ],
+ },
+ },
+ },
+ {
+ type: "text",
+ label: "Test with AND",
+ key: "test-with-and",
+ validation: {
+ logic: {
+ $and: [
+ {
+ $gt: 5,
+ },
+ {
+ $eq: undefined,
+ },
+ {
+ $has: "Keyword",
+ },
+ ],
+ },
+ },
+ },
+ {
+ type: "text",
+ label: "Test with Both",
+ key: "test-with-both",
+ validation: {
+ logic: {
+ $or: [
+ {
+ $gt: 5,
+ },
+ {
+ $and: [
+ {
+ $eq: null,
+ },
+ {
+ $has: "Keyword",
+ },
+ ],
+ },
+ ],
+ },
+ },
+ },
+ ],
+ },
+ ],
+ }),
+ );
+ });
+
+ it("should stringify the formkl object correctly", () => {
+ const result = parser.stringify(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ validation: {
+ logic: {
+ $eq: null,
+ },
+ },
+ },
+ {
+ type: "text",
+ label: "Test with OR",
+ key: "test-with-or",
+ validation: {
+ logic: {
+ $or: [
+ {
+ $gt: 5,
+ },
+ {
+ $eq: NaN,
+ },
+ {
+ $has: "Keyword",
+ },
+ ],
+ },
+ },
+ },
+ {
+ type: "text",
+ label: "Test with AND",
+ key: "test-with-and",
+ validation: {
+ logic: {
+ $and: [
+ {
+ $gt: 5,
+ },
+ {
+ $eq: undefined,
+ },
+ {
+ $has: "Keyword",
+ },
+ ],
+ },
+ },
+ },
+ {
+ type: "text",
+ label: "Test with Both",
+ key: "test-with-both",
+ validation: {
+ logic: {
+ $or: [
+ {
+ $gt: 5,
+ },
+ {
+ $and: [
+ {
+ $eq: null,
+ },
+ {
+ $has: "Keyword",
+ },
+ ],
+ },
+ ],
+ },
+ },
+ },
+ ],
+ },
+ ],
+ }),
+ );
+
+ expect(result).toBe(`formkl {
+ includes {
+ text valid(== null);
+ "Test with OR" text valid(> 5 or == NaN or has "Keyword");
+ "Test with AND" text valid(> 5 and == undefined and has "Keyword");
+ "Test with Both" text valid(> 5 or == null and has "Keyword");
+ }
+}`);
+ });
+});
diff --git a/packages/language/src/__tests__/field-with-alias.test.ts b/packages/language/src/__tests__/field-with-alias.test.ts
index 064c339..93539de 100644
--- a/packages/language/src/__tests__/field-with-alias.test.ts
+++ b/packages/language/src/__tests__/field-with-alias.test.ts
@@ -1,4 +1,4 @@
-import parser from "formkl";
+import parser, { defineForm } from "../";
describe("Field with alias (Custom key)", () => {
it("should parse the form syntax correctly", () => {
@@ -8,19 +8,46 @@ describe("Field with alias (Custom key)", () => {
}
}`);
- expect(result).toStrictEqual({
- model: "base",
- sections: [
- {
- fields: [
- {
- type: "text",
- label: "Text",
- key: "custom-key",
- },
- ],
- },
- ],
- });
+ expect(result).toStrictEqual(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "custom-key",
+ },
+ ],
+ },
+ ],
+ }),
+ );
+ });
+
+ it("should stringify the form syntax correctly", () => {
+ const result = parser.stringify(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "custom-key",
+ },
+ ],
+ },
+ ],
+ }),
+ );
+
+ expect(result).toBe(`formkl {
+ includes {
+ text as "custom-key";
+ }
+}`);
});
});
diff --git a/packages/language/src/__tests__/field-with-label.test.ts b/packages/language/src/__tests__/field-with-label.test.ts
index 2c319b4..81c75b4 100644
--- a/packages/language/src/__tests__/field-with-label.test.ts
+++ b/packages/language/src/__tests__/field-with-label.test.ts
@@ -1,4 +1,4 @@
-import parser from "formkl";
+import parser, { defineForm } from "../";
describe("Field with label", () => {
it("should parse the form syntax correctly", () => {
@@ -8,19 +8,46 @@ describe("Field with label", () => {
}
}`);
- expect(result).toStrictEqual({
- model: "base",
- sections: [
- {
- fields: [
- {
- type: "text",
- label: "Some field",
- key: "some-field",
- },
- ],
- },
- ],
- });
+ expect(result).toStrictEqual(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Some field",
+ key: "some-field",
+ },
+ ],
+ },
+ ],
+ }),
+ );
+ });
+
+ it("should stringify the form syntax correctly", () => {
+ const result = parser.stringify(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Some field",
+ key: "some-field",
+ },
+ ],
+ },
+ ],
+ }),
+ );
+
+ expect(result).toBe(`formkl {
+ includes {
+ "Some field" text;
+ }
+}`);
});
});
diff --git a/packages/language/src/__tests__/field-with-logic-validation.test.ts b/packages/language/src/__tests__/field-with-logic-validation.test.ts
index 5c15e95..a9025b7 100644
--- a/packages/language/src/__tests__/field-with-logic-validation.test.ts
+++ b/packages/language/src/__tests__/field-with-logic-validation.test.ts
@@ -1,4 +1,4 @@
-import parser from "formkl";
+import parser, { defineForm } from "../";
describe("Field with use of validation", () => {
it("should parse the form syntax correctly", () => {
@@ -11,88 +11,187 @@ describe("Field with use of validation", () => {
}
}`);
- expect(result).toStrictEqual({
- model: "base",
- sections: [
- {
- fields: [
- {
- type: "text",
- label: "Text",
- key: "text",
- validation: {
- logic: {
- $gt: 5,
+ expect(result).toStrictEqual(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ validation: {
+ logic: {
+ $gt: 5,
+ },
},
},
- },
- {
- type: "text",
- label: "Test with OR",
- key: "test-with-or",
- validation: {
- logic: {
- $or: [
- {
- $gt: 5,
- },
- {
- $eq: "Some value",
- },
- {
- $has: "Keyword",
- },
- ],
+ {
+ type: "text",
+ label: "Test with OR",
+ key: "test-with-or",
+ validation: {
+ logic: {
+ $or: [
+ {
+ $gt: 5,
+ },
+ {
+ $eq: "Some value",
+ },
+ {
+ $has: "Keyword",
+ },
+ ],
+ },
},
},
- },
- {
- type: "text",
- label: "Test with AND",
- key: "test-with-and",
- validation: {
- logic: {
- $and: [
- {
- $gt: 5,
- },
- {
- $eq: "Some value",
- },
- {
- $has: "Keyword",
- },
- ],
+ {
+ type: "text",
+ label: "Test with AND",
+ key: "test-with-and",
+ validation: {
+ logic: {
+ $and: [
+ {
+ $gt: 5,
+ },
+ {
+ $eq: "Some value",
+ },
+ {
+ $has: "Keyword",
+ },
+ ],
+ },
},
},
- },
- {
- type: "text",
- label: "Test with Both",
- key: "test-with-both",
- validation: {
- logic: {
- $or: [
- {
- $gt: 5,
- },
- {
- $and: [
- {
- $eq: "Some value",
- },
- {
- $has: "Keyword",
- },
- ],
- },
- ],
+ {
+ type: "text",
+ label: "Test with Both",
+ key: "test-with-both",
+ validation: {
+ logic: {
+ $or: [
+ {
+ $gt: 5,
+ },
+ {
+ $and: [
+ {
+ $eq: "Some value",
+ },
+ {
+ $has: "Keyword",
+ },
+ ],
+ },
+ ],
+ },
},
},
- },
- ],
- },
- ],
- });
+ ],
+ },
+ ],
+ }),
+ );
+ });
+
+ it("should stringify the form syntax correctly", () => {
+ const result = parser.stringify(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ validation: {
+ logic: {
+ $gt: 5,
+ },
+ },
+ },
+ {
+ type: "text",
+ label: "Test with OR",
+ key: "test-with-or",
+ validation: {
+ logic: {
+ $or: [
+ {
+ $gt: 5,
+ },
+ {
+ $eq: "Some value",
+ },
+ {
+ $has: "Keyword",
+ },
+ ],
+ },
+ },
+ },
+ {
+ type: "text",
+ label: "Test with AND",
+ key: "test-with-and",
+ validation: {
+ logic: {
+ $and: [
+ {
+ $gt: 5,
+ },
+ {
+ $eq: "Some value",
+ },
+ {
+ $has: "Keyword",
+ },
+ ],
+ },
+ },
+ },
+ {
+ type: "text",
+ label: "Test with Both",
+ key: "test-with-both",
+ validation: {
+ logic: {
+ $or: [
+ {
+ $gt: 5,
+ },
+ {
+ $and: [
+ {
+ $eq: "Some value",
+ },
+ {
+ $has: "Keyword",
+ },
+ ],
+ },
+ ],
+ },
+ },
+ },
+ ],
+ },
+ ],
+ }),
+ );
+
+ expect(result).toBe(`formkl {
+ includes {
+ text valid(> 5);
+ "Test with OR" text valid(> 5 or == "Some value" or has "Keyword");
+ "Test with AND" text valid(> 5 and == "Some value" and has "Keyword");
+ "Test with Both" text valid(> 5 or == "Some value" and has "Keyword");
+ }
+}`);
});
});
diff --git a/packages/language/src/__tests__/field-with-regex-validation.test.ts b/packages/language/src/__tests__/field-with-regex-validation.test.ts
index 31d1a3d..37d57fd 100644
--- a/packages/language/src/__tests__/field-with-regex-validation.test.ts
+++ b/packages/language/src/__tests__/field-with-regex-validation.test.ts
@@ -1,4 +1,4 @@
-import parser from "formkl";
+import parser, { defineForm } from "../";
describe("Field with use of validation", () => {
it("should parse the form syntax correctly", () => {
@@ -8,22 +8,52 @@ describe("Field with use of validation", () => {
}
}`);
- expect(result).toStrictEqual({
- model: "base",
- sections: [
- {
- fields: [
- {
- type: "text",
- label: "Test with regex",
- key: "test-with-regex",
- validation: {
- regex: /^[0-9]+$/,
+ expect(result).toStrictEqual(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Test with regex",
+ key: "test-with-regex",
+ validation: {
+ regex: /^[0-9]+$/,
+ },
},
- },
- ],
- },
- ],
- });
+ ],
+ },
+ ],
+ }),
+ );
+ });
+
+ it("should stringify the form syntax correctly", () => {
+ const result = parser.stringify(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Test with regex",
+ key: "test-with-regex",
+ validation: {
+ regex: /^[0-9]+$/,
+ },
+ },
+ ],
+ },
+ ],
+ }),
+ );
+
+ expect(result).toBe(`formkl {
+ includes {
+ "Test with regex" text regex("^[0-9]+$");
+ }
+}`);
});
});
diff --git a/packages/language/src/__tests__/form-with-description.test.ts b/packages/language/src/__tests__/form-with-description.test.ts
index e3d1763..65a60f8 100644
--- a/packages/language/src/__tests__/form-with-description.test.ts
+++ b/packages/language/src/__tests__/form-with-description.test.ts
@@ -1,4 +1,4 @@
-import parser from "formkl";
+import parser, { defineForm } from "../";
describe("Form with description", () => {
it("should parse the form syntax correctly", () => {
@@ -12,21 +12,50 @@ describe("Form with description", () => {
}
}`);
- expect(result).toStrictEqual({
- model: "base",
- title: "Form title (Must has)",
- description: "Form description",
- sections: [
- {
- fields: [
- {
- type: "text",
- label: "Text",
- key: "text",
- },
- ],
- },
- ],
- });
+ expect(result).toStrictEqual(
+ defineForm({
+ model: "base",
+ title: "Form title (Must has)",
+ description: "Form description",
+ sections: [
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ },
+ ],
+ },
+ ],
+ }),
+ );
+ });
+
+ it("should stringify the form syntax correctly", () => {
+ const result = parser.stringify(
+ defineForm({
+ model: "base",
+ title: "Form title (Must has)",
+ description: "Form description",
+ sections: [
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ },
+ ],
+ },
+ ],
+ }),
+ );
+
+ expect(result).toBe(`formkl "Form title (Must has)" "Form description" {
+ includes {
+ text;
+ }
+}`);
});
});
diff --git a/packages/language/src/__tests__/form-with-flattened-model.test.ts b/packages/language/src/__tests__/form-with-flattened-model.test.ts
index 1b11956..e2d2eee 100644
--- a/packages/language/src/__tests__/form-with-flattened-model.test.ts
+++ b/packages/language/src/__tests__/form-with-flattened-model.test.ts
@@ -1,4 +1,4 @@
-import parser from "formkl";
+import parser, { defineForm } from "../";
describe("Form with flatten model", () => {
it("should parse the form syntax correctly", () => {
@@ -8,19 +8,46 @@ describe("Form with flatten model", () => {
}
}`);
- expect(result).toStrictEqual({
- model: "flat",
- sections: [
- {
- fields: [
- {
- type: "text",
- label: "Text",
- key: "text",
- },
- ],
- },
- ],
- });
+ expect(result).toStrictEqual(
+ defineForm({
+ model: "flat",
+ sections: [
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ },
+ ],
+ },
+ ],
+ }),
+ );
+ });
+
+ it("should stringify the form syntax correctly", () => {
+ const result = parser.stringify(
+ defineForm({
+ model: "flat",
+ sections: [
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ },
+ ],
+ },
+ ],
+ }),
+ );
+
+ expect(result).toBe(`formkl flat {
+ includes {
+ text;
+ }
+}`);
});
});
diff --git a/packages/language/src/__tests__/form-with-title.test.ts b/packages/language/src/__tests__/form-with-title.test.ts
index 1ae8c98..05505bc 100644
--- a/packages/language/src/__tests__/form-with-title.test.ts
+++ b/packages/language/src/__tests__/form-with-title.test.ts
@@ -1,4 +1,4 @@
-import parser from "formkl";
+import parser, { defineForm } from "../";
describe("Form with title", () => {
it("should parse the form syntax correctly", () => {
@@ -8,20 +8,48 @@ describe("Form with title", () => {
}
}`);
- expect(result).toStrictEqual({
- model: "base",
- title: "Form title",
- sections: [
- {
- fields: [
- {
- type: "text",
- label: "Text",
- key: "text",
- },
- ],
- },
- ],
- });
+ expect(result).toStrictEqual(
+ defineForm({
+ model: "base",
+ title: "Form title",
+ sections: [
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ },
+ ],
+ },
+ ],
+ }),
+ );
+ });
+
+ it("should stringify the form syntax correctly", () => {
+ const result = parser.stringify(
+ defineForm({
+ model: "base",
+ title: "Form title",
+ sections: [
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ },
+ ],
+ },
+ ],
+ }),
+ );
+
+ expect(result).toBe(`formkl "Form title" {
+ includes {
+ text;
+ }
+}`);
});
});
diff --git a/packages/language/src/__tests__/minimal.test.ts b/packages/language/src/__tests__/minimal.test.ts
index 4bbcdfc..79cfbc4 100644
--- a/packages/language/src/__tests__/minimal.test.ts
+++ b/packages/language/src/__tests__/minimal.test.ts
@@ -1,4 +1,4 @@
-import parser from "formkl";
+import parser, { defineForm } from "../";
describe("Minimal test", () => {
it("should parse the form syntax correctly", () => {
@@ -8,19 +8,46 @@ describe("Minimal test", () => {
}
}`);
- expect(result).toStrictEqual({
- model: "base",
- sections: [
- {
- fields: [
- {
- type: "text",
- label: "Text",
- key: "text",
- },
- ],
- },
- ],
- });
+ expect(result).toStrictEqual(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ },
+ ],
+ },
+ ],
+ }),
+ );
+ });
+
+ it("should stringify the form syntax correctly", () => {
+ const result = parser.stringify(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ },
+ ],
+ },
+ ],
+ }),
+ );
+
+ expect(result).toBe(`formkl {
+ includes {
+ text;
+ }
+}`);
});
});
diff --git a/packages/language/src/__tests__/multiple-field.test.ts b/packages/language/src/__tests__/multiple-field.test.ts
index 55d7c3c..974b9b1 100644
--- a/packages/language/src/__tests__/multiple-field.test.ts
+++ b/packages/language/src/__tests__/multiple-field.test.ts
@@ -1,4 +1,4 @@
-import parser from "formkl";
+import parser, { defineForm } from "../";
describe("Multiple fields in a section", () => {
it("should parse the form syntax correctly", () => {
@@ -9,25 +9,58 @@ describe("Multiple fields in a section", () => {
}
}`);
- expect(result).toStrictEqual({
- model: "base",
- sections: [
- {
- fields: [
- {
- type: "text",
- label: "Text",
- key: "text",
- },
- {
- type: "text",
- label: "Another text",
- key: "another-text",
- },
- ],
- },
- ],
- });
+ expect(result).toStrictEqual(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ },
+ {
+ type: "text",
+ label: "Another text",
+ key: "another-text",
+ },
+ ],
+ },
+ ],
+ }),
+ );
+ });
+
+ it("should stringify the form syntax correctly", () => {
+ const result = parser.stringify(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ },
+ {
+ type: "text",
+ label: "Another text",
+ key: "another-text",
+ },
+ ],
+ },
+ ],
+ }),
+ );
+
+ expect(result).toBe(`formkl {
+ includes {
+ text;
+ "Another text" text;
+ }
+}`);
});
it("should emit syntax error for duplicated field key", () => {
diff --git a/packages/language/src/__tests__/multiple-section.test.ts b/packages/language/src/__tests__/multiple-section.test.ts
index e077566..37842c3 100644
--- a/packages/language/src/__tests__/multiple-section.test.ts
+++ b/packages/language/src/__tests__/multiple-section.test.ts
@@ -1,4 +1,4 @@
-import parser from "formkl";
+import parser, { defineForm } from "../";
describe("Multiple section in one form", () => {
it("should parse the form syntax correctly", () => {
@@ -11,31 +11,72 @@ describe("Multiple section in one form", () => {
}
}`);
- expect(result).toStrictEqual({
- model: "base",
- sections: [
- {
- title: "Personal information",
- key: "personal-information",
- fields: [
- {
- type: "text",
- label: "Text",
- key: "text",
- },
- ],
- },
- {
- fields: [
- {
- type: "text",
- label: "Text",
- key: "text",
- },
- ],
- },
- ],
- });
+ expect(result).toStrictEqual(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ title: "Personal information",
+ key: "personal-information",
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ },
+ ],
+ },
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ },
+ ],
+ },
+ ],
+ }),
+ );
+ });
+
+ it("should stringify the form syntax correctly", () => {
+ const result = parser.stringify(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ title: "Personal information",
+ key: "personal-information",
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ },
+ ],
+ },
+ {
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ },
+ ],
+ },
+ ],
+ }),
+ );
+
+ expect(result).toBe(`formkl {
+ "Personal information" includes {
+ text;
+ }
+ includes {
+ text;
+ }
+}`);
});
it("should emit syntax error for duplicated section key", () => {
diff --git a/packages/language/src/__tests__/section-multiple-response.test.ts b/packages/language/src/__tests__/section-multiple-response.test.ts
index 041923c..a84b45b 100644
--- a/packages/language/src/__tests__/section-multiple-response.test.ts
+++ b/packages/language/src/__tests__/section-multiple-response.test.ts
@@ -1,4 +1,4 @@
-import parser from "formkl";
+import parser, { defineForm } from "../";
describe("Section with multiple responses support", () => {
it("should parse the form syntax correctly", () => {
@@ -8,21 +8,49 @@ describe("Section with multiple responses support", () => {
}
}`);
- expect(result).toStrictEqual({
- model: "base",
- sections: [
- {
- multiple: true,
- fields: [
- {
- type: "text",
- label: "Text",
- key: "text",
- },
- ],
- },
- ],
- });
+ expect(result).toStrictEqual(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ multiple: true,
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ },
+ ],
+ },
+ ],
+ }),
+ );
+ });
+
+ it("should stringify the form syntax correctly", () => {
+ const result = parser.stringify(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ multiple: true,
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ },
+ ],
+ },
+ ],
+ }),
+ );
+
+ expect(result).toBe(`formkl {
+ multiple includes {
+ text;
+ }
+}`);
});
it("should emit syntax error for multiple response field in multiple response section.", () => {
diff --git a/packages/language/src/__tests__/section-with-alias.test.ts b/packages/language/src/__tests__/section-with-alias.test.ts
index 0b46988..6b23423 100644
--- a/packages/language/src/__tests__/section-with-alias.test.ts
+++ b/packages/language/src/__tests__/section-with-alias.test.ts
@@ -1,4 +1,4 @@
-import parser from "formkl";
+import parser, { defineForm } from "../";
describe("Section with alias", () => {
it("should parse the form syntax correctly", () => {
@@ -11,44 +11,86 @@ describe("Section with alias", () => {
} as "different-section"
}`);
- expect(result).toStrictEqual({
- model: "base",
- sections: [
- {
- title: "Personal information",
- key: "personal-information",
- fields: [
- {
- type: "text",
- label: "Text",
- key: "text",
- },
- ],
- },
- {
- key: "different-section",
- fields: [
- {
- type: "text",
- label: "Text",
- key: "text",
- },
- ],
- },
- ],
- });
+ expect(result).toStrictEqual(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ title: "Personal information",
+ key: "personal-information",
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ },
+ ],
+ },
+ {
+ key: "different-section",
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ },
+ ],
+ },
+ ],
+ }),
+ );
+ });
+
+ it("should stringify the form syntax correctly", () => {
+ const result = parser.stringify(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ title: "Personal information",
+ key: "personal-information",
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ },
+ ],
+ },
+ {
+ key: "different-section",
+ fields: [
+ {
+ type: "text",
+ label: "Text",
+ key: "text",
+ },
+ ],
+ },
+ ],
+ }),
+ );
+
+ expect(result).toBe(`formkl {
+ "Personal information" includes {
+ text;
+ }
+ includes {
+ text;
+ } as "different-section"
+}`);
});
it("should emit syntax error for duplicated section key", () => {
expect(() =>
parser.parse(`formkl {
- includes {
- text;
- } as "duplicated-section"
- includes {
- text;
- } as "duplicated-section"
- }`),
+ includes {
+ text;
+ } as "duplicated-section"
+ includes {
+ text;
+ } as "duplicated-section"
+ }`),
).toThrowError(/Duplicate section key "duplicated-section"/);
});
});
diff --git a/packages/language/src/__tests__/section-with-title.test.ts b/packages/language/src/__tests__/section-with-title.test.ts
index dd58a3e..2602691 100644
--- a/packages/language/src/__tests__/section-with-title.test.ts
+++ b/packages/language/src/__tests__/section-with-title.test.ts
@@ -1,4 +1,4 @@
-import parser from "formkl";
+import parser, { defineForm } from "../";
describe("Section with title", () => {
it("should parse the form syntax correctly", () => {
@@ -8,21 +8,50 @@ describe("Section with title", () => {
}
}`);
- expect(result).toStrictEqual({
- model: "base",
- sections: [
- {
- title: "Personal Information",
- key: "personal-information",
- fields: [
- {
- type: "text",
- label: "Fullname",
- key: "fullname",
- },
- ],
- },
- ],
- });
+ expect(result).toStrictEqual(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ title: "Personal Information",
+ key: "personal-information",
+ fields: [
+ {
+ type: "text",
+ label: "Fullname",
+ key: "fullname",
+ },
+ ],
+ },
+ ],
+ }),
+ );
+ });
+
+ it("should stringify the form syntax correctly", () => {
+ const result = parser.stringify(
+ defineForm({
+ model: "base",
+ sections: [
+ {
+ title: "Personal Information",
+ key: "personal-information",
+ fields: [
+ {
+ type: "text",
+ label: "Fullname",
+ key: "fullname",
+ },
+ ],
+ },
+ ],
+ }),
+ );
+
+ expect(result).toBe(`formkl {
+ "Personal Information" includes {
+ "Fullname" text;
+ }
+}`);
});
});
diff --git a/packages/language/src/__tests__/uppercase-syntax.test.ts b/packages/language/src/__tests__/uppercase-syntax.test.ts
index da99a44..e4b1ade 100644
--- a/packages/language/src/__tests__/uppercase-syntax.test.ts
+++ b/packages/language/src/__tests__/uppercase-syntax.test.ts
@@ -1,4 +1,4 @@
-import parser from "formkl";
+import parser, { defineForm } from "../";
describe("Used with UPPERCASE syntax", () => {
it("should parse the form syntax correctly", () => {
@@ -9,7 +9,7 @@ describe("Used with UPPERCASE syntax", () => {
}
}`);
- expect(result).toStrictEqual({
+ expect(result).toStrictEqual(defineForm({
model: "base",
sections: [
{
@@ -27,6 +27,6 @@ describe("Used with UPPERCASE syntax", () => {
],
},
],
- });
+ }));
});
});
diff --git a/packages/language/src/define.ts b/packages/language/src/define.ts
new file mode 100644
index 0000000..ba538ab
--- /dev/null
+++ b/packages/language/src/define.ts
@@ -0,0 +1,24 @@
+import {
+ FieldDefault,
+ FieldSelection,
+ Formkl,
+ SchemaBase,
+ SchemaFlat,
+ Section,
+} from "@formkl/shared";
+
+export function defineForm(form: Formkl) {
+ return form;
+}
+
+export function defineSection(section: Section) {
+ return section;
+}
+
+export function defineField(field: FieldDefault | FieldSelection) {
+ return field;
+}
+
+export function defineModel(model: SchemaBase | SchemaFlat) {
+ return model;
+}
diff --git a/packages/language/src/index.ts b/packages/language/src/index.ts
index d4733c9..136b742 100644
--- a/packages/language/src/index.ts
+++ b/packages/language/src/index.ts
@@ -1,8 +1,10 @@
-import { Parser } from "./Parser";
-import { Tokenizer } from "./Tokenizer";
+import { Parser } from "./parser";
+import { Tokenizer } from "./tokenizer";
const parser = new Parser();
export default parser;
+export * from "./define";
+
export { Parser, Tokenizer };
diff --git a/packages/language/src/Parser.ts b/packages/language/src/parser.ts
similarity index 78%
rename from packages/language/src/Parser.ts
rename to packages/language/src/parser.ts
index 8cf1166..476e0a2 100644
--- a/packages/language/src/Parser.ts
+++ b/packages/language/src/parser.ts
@@ -1,8 +1,9 @@
import { Formkl, Section, FieldDefault, FieldSelection, HttpMethod } from "@formkl/shared";
-import { Tokenizer } from "./Tokenizer";
+import { Tokenizer } from "./tokenizer";
import { Token } from "./types";
import slugify from "slugify";
+import { Stringifier } from "./stringifier";
export class Parser {
private _string: string;
@@ -19,7 +20,7 @@ export class Parser {
}
/**
- * Parses a string into an AST
+ * Parse a Formkl syntax string into Formkl object
*/
parse(string: string): Formkl {
this._string = "";
@@ -39,6 +40,15 @@ export class Parser {
return this.FormBlock();
}
+ /**
+ * Stringify a Formkl object to a Formkl syntax string
+ */
+ stringify(formkl: Formkl) {
+ const stringifier = new Stringifier();
+
+ return stringifier.stringify(formkl);
+ }
+
/**
* Main entry point.
*
@@ -110,7 +120,7 @@ export class Parser {
*/
private SectionBlockList(stopLookAhead = "}") {
const sectionList = [this.SectionBlock()];
- while (this._lookahead != null && this._lookahead.type !== stopLookAhead) {
+ while (this._lookahead != null && this._lookahead?.type !== stopLookAhead) {
sectionList.push(this.SectionBlock());
}
return sectionList;
@@ -180,7 +190,7 @@ export class Parser {
* */
private FieldStatementList(stopLookAhead = "}") {
const fieldStatementList = [this.FieldStatement()];
- while (this._lookahead !== null && this._lookahead.type !== stopLookAhead) {
+ while (this._lookahead !== null && this._lookahead?.type !== stopLookAhead) {
fieldStatementList.push(this.FieldStatement());
}
return fieldStatementList;
@@ -206,19 +216,15 @@ export class Parser {
}
do {
- const expression = {
- REQUIRE: () => {
+ switch (this._lookahead?.type) {
+ case "REQUIRE":
this._eat("REQUIRE");
field.required = true;
- },
- MULTIPLE: () => {
+ break;
+ case "MULTIPLE":
this._eat("MULTIPLE");
field.multiple = true;
- },
- }[String(this._lookahead?.type)];
-
- if (expression) {
- Object.assign(field, expression());
+ break;
}
} while (["REQUIRE", "MULTIPLE"].includes(String(this._lookahead?.type)));
@@ -451,43 +457,70 @@ export class Parser {
* ;
*/
private RelationalExpression() {
- if (this._lookahead?.type) {
- const expresion = {
- OPERATOR_RELATIONAL: () => {
- const operator = {
- ">": "$gt",
- ">=": "$gte",
- "<": "$lt",
- "<=": "$lte",
- }[this._eat("OPERATOR_RELATIONAL").value] as string;
-
- return {
- [operator]: this.NumericLiteral(),
- };
- },
- OPERATOR_EQUALITY: () => {
- const operator = {
- "==": "$eq",
- "!=": "$neq",
- }[this._eat("OPERATOR_EQUALITY").value] as string;
-
- return {
- [operator]: isNaN(this._lookahead?.value as number)
- ? this.StringLiteral()
- : this.NumericLiteral(),
- };
- },
- HAS: () => {
- this._eat("HAS");
- return {
- $has: isNaN(this._lookahead?.value as number)
- ? this.StringLiteral()
- : this.NumericLiteral(),
- };
- },
- }[this._lookahead.type];
-
- if (expresion) return expresion();
+ switch (this._lookahead?.type) {
+ case "OPERATOR_RELATIONAL":
+ const relationalOperator = this._eat("OPERATOR_RELATIONAL").value;
+
+ switch (relationalOperator) {
+ case ">":
+ return { $gt: this.NumericLiteral() };
+ case ">=":
+ return { $gte: this.NumericLiteral() };
+ case "<":
+ return { $lt: this.NumericLiteral() };
+ case "<=":
+ return { $lte: this.NumericLiteral() };
+ default:
+ throw new SyntaxError(`Unknown relational operator: ${relationalOperator}`);
+ }
+ case "OPERATOR_EQUALITY":
+ const equalityOperator = this._eat("OPERATOR_EQUALITY").value;
+ const equalityOperatorKey = equalityOperator === "==" ? "$eq" : "$neq";
+
+ switch (equalityOperator) {
+ case "==":
+ case "!=":
+ switch (
+ this._lookahead?.type as
+ | "NUMBER"
+ | "NAN"
+ | "NULL"
+ | "UNDEFINED"
+ | "TRUE"
+ | "FALSE"
+ | "STRING"
+ ) {
+ case "NUMBER":
+ return { [equalityOperatorKey]: this.NumericLiteral() };
+ case "NAN":
+ return { [equalityOperatorKey]: this.NaNLiteral() };
+ case "NULL":
+ return { [equalityOperatorKey]: this.NullLiteral() };
+ case "UNDEFINED":
+ return { [equalityOperatorKey]: this.UndefinedLiteral() };
+ case "TRUE":
+ case "FALSE":
+ return {
+ [equalityOperatorKey]: this.BooleanLiteral(
+ this._lookahead?.type as "TRUE" | "FALSE",
+ ),
+ };
+ case "STRING":
+ return { [equalityOperatorKey]: this.StringLiteral() };
+ default:
+ throw new SyntaxError(`Unknown equality value type: ${this._lookahead?.type}`);
+ }
+ default:
+ throw new SyntaxError(`Unknown equality operator: ${equalityOperator}`);
+ }
+ case "HAS":
+ this._eat("HAS");
+
+ return {
+ $has: isNaN(this._lookahead?.value as number)
+ ? this.StringLiteral()
+ : this.NumericLiteral(),
+ };
}
}
@@ -506,6 +539,16 @@ export class Parser {
return strings;
}
+ /**
+ * NaNLiteral
+ * : 'NaN'
+ * ;
+ */
+ private NaNLiteral() {
+ this._eat("NAN");
+ return NaN;
+ }
+
/*
* NumericLiteral
* : NUMBER
@@ -532,8 +575,8 @@ export class Parser {
* | FALSE
* ;
*/
- private BooleanLiteral(value: boolean) {
- this._eat(value ? "true" : "false");
+ private BooleanLiteral(value: "TRUE" | "FALSE") {
+ this._eat(value ? "TRUE" : "FALSE");
return value;
}
@@ -543,10 +586,20 @@ export class Parser {
* ;
*/
private NullLiteral() {
- this._eat("null");
+ this._eat("NULL");
return null;
}
+ /**
+ * UndefinedLiteral
+ * : 'undefined'
+ * ;
+ */
+ private UndefinedLiteral() {
+ this._eat("UNDEFINED");
+ return undefined;
+ }
+
/**
* Expects a token of a given type.
*/
diff --git a/packages/language/src/stringifier.ts b/packages/language/src/stringifier.ts
new file mode 100644
index 0000000..2c64d36
--- /dev/null
+++ b/packages/language/src/stringifier.ts
@@ -0,0 +1,110 @@
+import {
+ FieldDefault,
+ FieldSelection,
+ Formkl,
+ Section,
+ Validation,
+ ValidationLogic,
+} from "@formkl/shared";
+import slugify from "slugify";
+
+export class Stringifier {
+ constructor() {}
+
+ validateLogicAnd(andLogic: Validation["logic"]["$and"]): string {
+ const results = andLogic.map((e) => this.validationLogic(e));
+
+ return results.join(`%(s)and%(s)`);
+ }
+
+ validateLogicOr(orLogic: Validation["logic"]["$or"]): string {
+ const results = orLogic.map((e) => this.validationLogic(e));
+
+ return results.join(`%(s)or%(s)`);
+ }
+
+ validationLogic(logic: Validation["logic"]): string {
+ const operators = Object.keys(logic) as Array;
+ const operator = operators[0];
+ const val = logic[operator] as string | number;
+
+ return {
+ $gt: () => `>%(s)${val}`,
+ $lt: () => `>=%(s)${val}`,
+ $gteq: () => `<%(s)${val}`,
+ $lteq: () => `<=%(s)${val}`,
+ $eq: () => `==%(s)${typeof val === "string" ? JSON.stringify(val) : val}`,
+ $has: () => `has%(s)${typeof val === "string" ? JSON.stringify(val) : val}`,
+ $and: () => logic.$and && this.validateLogicAnd(logic.$and),
+ $or: () => logic.$or && this.validateLogicOr(logic.$or),
+ }[operator]();
+ }
+
+ validation(validation: Validation) {
+ return [
+ validation.regex && `regex("${validation.regex.source}")`,
+ validation.logic && `valid(${this.validationLogic(validation.logic)})`,
+ ]
+ .filter((i) => i)
+ .join("%(s)");
+ }
+
+ fields(fields: Array) {
+ return (
+ "%(t)%(t)" +
+ fields
+ .map(
+ (field) =>
+ [
+ field.required && "require",
+ field.maxResponseAllowed ? field.maxResponseAllowed : field.multiple && "multiple",
+ slugify(field.label).toLowerCase() !== field.type && `"${field.label}"`,
+ field.type,
+ field.validation && this.validation(field.validation),
+ slugify(field.label).toLowerCase() !== field.key && `as%(s)"${field.key}"`,
+ ]
+ .filter((i) => i)
+ .join("%(s)") + ";",
+ )
+ .join("%(n)%(t)%(t)")
+ );
+ }
+
+ sections(sections: Array) {
+ return sections
+ .map((section) =>
+ [
+ "%(t)",
+ section.multiple && "multiple%(s)",
+ section.title && `"${section.title}"%(s)`,
+ "includes",
+ "%(s)",
+ "{",
+ "%(n)",
+ this.fields(section.fields),
+ "%(n)",
+ "%(t)",
+ "}",
+ (!section.title && section.key) ||
+ (section.title && slugify(section.title).toLowerCase() !== section.key)
+ ? `%(s)as%(s)"${section.key}"`
+ : "",
+ ].join(""),
+ )
+ .join("%(n)");
+ }
+
+ stringify(formkl: Formkl) {
+ return `${[
+ "formkl",
+ formkl.model === "flat" && "flat",
+ formkl.title && JSON.stringify(formkl.title),
+ formkl.description && JSON.stringify(formkl.description),
+ ]
+ .filter((i) => i)
+ .join("%(s)")}%(s){%(n)${this.sections(formkl.sections)}%(n)}`
+ .replace(/\%\(s\)/g, " ")
+ .replace(/\%\(t\)/g, "\t")
+ .replace(/\%\(n\)/g, "\n");
+ }
+}
diff --git a/packages/language/src/Tokenizer.ts b/packages/language/src/tokenizer.ts
similarity index 94%
rename from packages/language/src/Tokenizer.ts
rename to packages/language/src/tokenizer.ts
index 12948f5..aa6a039 100644
--- a/packages/language/src/Tokenizer.ts
+++ b/packages/language/src/tokenizer.ts
@@ -93,6 +93,14 @@ const Specs: Array = [
// Single quoted String:
[/^'[^']*'/, "STRING"],
+ // --------------------------------------
+ // Values by Keyword:
+ [createKeywordRegex("NaN"), "NAN"],
+ [createKeywordRegex("FALSE"), "FALSE"],
+ [createKeywordRegex("TRUE"), "TRUE"],
+ [createKeywordRegex("NULL"), "NULL"],
+ [createKeywordRegex("UNDEFINED"), "UNDEFINED"],
+
// --------------------------------------
// Identifier
[/^\w+/, "IDENTIFIER"],
diff --git a/packages/language/src/utils/createKeywordRegex.ts b/packages/language/src/utils/createKeywordRegex.ts
index 067d9e1..12a3d44 100644
--- a/packages/language/src/utils/createKeywordRegex.ts
+++ b/packages/language/src/utils/createKeywordRegex.ts
@@ -1,6 +1,7 @@
export const createKeywordRegex = (keyword: string) => {
return new RegExp(
`^\\b(${[
+ keyword,
keyword.toLowerCase(),
keyword.toUpperCase(),
keyword.toLowerCase().charAt(0).toUpperCase() + keyword.toLowerCase().slice(1),
diff --git a/packages/plugins/vue/package.json b/packages/plugins/vue/package.json
index 4165bc8..a104918 100644
--- a/packages/plugins/vue/package.json
+++ b/packages/plugins/vue/package.json
@@ -1,6 +1,6 @@
{
"name": "@formkl/plugin-vue",
- "version": "1.0.0-rc.2",
+ "version": "1.0.0-rc.3",
"description": "A Formkl components plugin to register components that used in @formkl/vue adapter",
"type": "module",
"main": "dist/index.es.js",
diff --git a/packages/shared/package.json b/packages/shared/package.json
index 1858d6c..b72ed6d 100644
--- a/packages/shared/package.json
+++ b/packages/shared/package.json
@@ -1,6 +1,6 @@
{
"name": "@formkl/shared",
- "version": "1.0.0-rc.2",
+ "version": "1.0.0-rc.3",
"type": "module",
"main": "./src/index.ts",
"scripts": {
diff --git a/packages/shared/src/__tests__/with-parser/validate-logic-and-n-or.test.ts b/packages/shared/src/__tests__/with-parser/validate-logic-and-n-or.test.ts
index 797115b..cbf5628 100644
--- a/packages/shared/src/__tests__/with-parser/validate-logic-and-n-or.test.ts
+++ b/packages/shared/src/__tests__/with-parser/validate-logic-and-n-or.test.ts
@@ -1,6 +1,6 @@
import { isValueValidated } from "@formkl/shared";
-import parser from "formkl";
+import parser from "../../../../language";
describe("Test recursive validator", () => {
it("should return true", () => {
diff --git a/packages/shared/src/__tests__/with-parser/validate-logic-and.test.ts b/packages/shared/src/__tests__/with-parser/validate-logic-and.test.ts
index cf9cddb..0a4566f 100644
--- a/packages/shared/src/__tests__/with-parser/validate-logic-and.test.ts
+++ b/packages/shared/src/__tests__/with-parser/validate-logic-and.test.ts
@@ -1,6 +1,6 @@
import { isValueValidated } from "@formkl/shared";
-import parser from "formkl";
+import parser from "../../../../language";
describe("Test recursive validator", () => {
it("should return false", () => {
diff --git a/packages/shared/src/__tests__/with-parser/validate-logic-gt.test.ts b/packages/shared/src/__tests__/with-parser/validate-logic-gt.test.ts
index 79a5ccb..4e2db66 100644
--- a/packages/shared/src/__tests__/with-parser/validate-logic-gt.test.ts
+++ b/packages/shared/src/__tests__/with-parser/validate-logic-gt.test.ts
@@ -1,6 +1,6 @@
import { isValueValidated } from "@formkl/shared";
-import parser from "formkl";
+import parser from "../../../../language";
describe("Test recursive validator", () => {
it("should return true", () => {
diff --git a/packages/shared/src/__tests__/with-parser/validate-logic-lt.test.ts b/packages/shared/src/__tests__/with-parser/validate-logic-lt.test.ts
index e273e0a..2f0e3ed 100644
--- a/packages/shared/src/__tests__/with-parser/validate-logic-lt.test.ts
+++ b/packages/shared/src/__tests__/with-parser/validate-logic-lt.test.ts
@@ -1,6 +1,6 @@
import { isValueValidated } from "@formkl/shared";
-import parser from "formkl";
+import parser from "../../../../language";
describe("Test recursive validator", () => {
it("should return false", () => {
diff --git a/packages/shared/src/__tests__/with-parser/validate-logic-n-regex.test.ts b/packages/shared/src/__tests__/with-parser/validate-logic-n-regex.test.ts
index 6fb6336..26ae5d8 100644
--- a/packages/shared/src/__tests__/with-parser/validate-logic-n-regex.test.ts
+++ b/packages/shared/src/__tests__/with-parser/validate-logic-n-regex.test.ts
@@ -1,6 +1,6 @@
import { isValueValidated } from "@formkl/shared";
-import parser from "formkl";
+import parser from "../../../../language";
describe("Test logic and regex", () => {
it("should return false", () => {
diff --git a/packages/shared/src/__tests__/with-parser/validate-logic-or.test.ts b/packages/shared/src/__tests__/with-parser/validate-logic-or.test.ts
index 33660d1..344a286 100644
--- a/packages/shared/src/__tests__/with-parser/validate-logic-or.test.ts
+++ b/packages/shared/src/__tests__/with-parser/validate-logic-or.test.ts
@@ -1,6 +1,6 @@
import { isValueValidated } from "@formkl/shared";
-import parser from "formkl";
+import parser from "../../../../language";
describe("Test recursive validator", () => {
it("should return true", () => {
diff --git a/packages/shared/src/__tests__/with-parser/validate-regex.test.ts b/packages/shared/src/__tests__/with-parser/validate-regex.test.ts
index 6243999..fc10fe2 100644
--- a/packages/shared/src/__tests__/with-parser/validate-regex.test.ts
+++ b/packages/shared/src/__tests__/with-parser/validate-regex.test.ts
@@ -1,6 +1,6 @@
import { isValueValidated } from "@formkl/shared";
-import parser from "formkl";
+import parser from "../../../../language";
describe("Test recursive validator", () => {
it("should return true", () => {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b7e7edd..cec5fe3 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -26,6 +26,7 @@ importers:
nx: ^15.0.0
release-it: ^15.5.0
rimraf: ^3.0.2
+ ts-node: ^10.9.1
typescript: ^4.8.4
vitepress: 1.0.0-alpha.20
vitest: ^0.24.4
@@ -56,6 +57,7 @@ importers:
nx: 15.0.0
release-it: 15.5.0
rimraf: 3.0.2
+ ts-node: 10.9.1_7jzoohtnaegavowzoeccrsbhty
typescript: 4.8.4
vitest: 0.24.4_dof64qmg5cgec6bfb7aeo2w7mu
vue: 3.2.41
@@ -1183,7 +1185,7 @@ packages:
lodash.merge: 4.6.2
lodash.uniq: 4.5.0
resolve-from: 5.0.0
- ts-node: 10.9.1_7jzoohtnaegavowzoeccrsbhty
+ ts-node: 10.9.1_yodorn5kzjgomblrsstrk2spaa
typescript: 4.8.4
transitivePeerDependencies:
- '@swc/core'
@@ -3847,7 +3849,7 @@ packages:
dependencies:
'@types/node': 14.18.33
cosmiconfig: 7.1.0
- ts-node: 10.9.1_7jzoohtnaegavowzoeccrsbhty
+ ts-node: 10.9.1_yodorn5kzjgomblrsstrk2spaa
typescript: 4.8.4
dev: true
@@ -8763,6 +8765,37 @@ packages:
yn: 3.1.1
dev: true
+ /ts-node/10.9.1_yodorn5kzjgomblrsstrk2spaa:
+ resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==}
+ hasBin: true
+ peerDependencies:
+ '@swc/core': '>=1.2.50'
+ '@swc/wasm': '>=1.2.50'
+ '@types/node': '*'
+ typescript: '>=2.7'
+ peerDependenciesMeta:
+ '@swc/core':
+ optional: true
+ '@swc/wasm':
+ optional: true
+ dependencies:
+ '@cspotcode/source-map-support': 0.8.1
+ '@tsconfig/node10': 1.0.9
+ '@tsconfig/node12': 1.0.11
+ '@tsconfig/node14': 1.0.3
+ '@tsconfig/node16': 1.0.3
+ '@types/node': 14.18.33
+ acorn: 8.8.0
+ acorn-walk: 8.2.0
+ arg: 4.1.3
+ create-require: 1.1.1
+ diff: 4.0.2
+ make-error: 1.3.6
+ typescript: 4.8.4
+ v8-compile-cache-lib: 3.0.1
+ yn: 3.1.1
+ dev: true
+
/tsconfig-paths/3.14.1:
resolution: {integrity: sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==}
dependencies: