diff --git a/packages/graphql-config/package.json b/packages/graphql-config/package.json index 87c60357..ad9d3391 100644 --- a/packages/graphql-config/package.json +++ b/packages/graphql-config/package.json @@ -1,6 +1,7 @@ { "name": "@fabrix-framework/graphql-config", "private": false, + "type": "module", "version": "0.1.0", "description": "GraphQL configuration for fabrix", "exports": { @@ -21,19 +22,22 @@ "build": "tsup", "lint": "eslint '**/*.{ts,tsx}' --ignore-pattern 'dist/*' --max-warnings=0", "type-check": "tsc --noEmit --incremental --pretty", - "test": "exit 0" + "test": "vitest run" }, "dependencies": { "graphql": "^16.9.0" }, "devDependencies": { + "@fabrix-framework/eslint-config": "workspace:*", + "@fabrix-framework/prettier-config": "workspace:*", "@types/node": "^22.7.5", "eslint": "^9.6.0", + "memfs": "^4.14.0", "prettier": "^3.3.3", "tsup": "^8.1.0", "typescript": "^5.5.3", - "@fabrix-framework/eslint-config": "workspace:*", - "@fabrix-framework/prettier-config": "workspace:*" + "vite-plugin-graphql-loader": "^4.0.4", + "vitest": "^2.0.3" }, "prettier": "@fabrix-framework/prettier-config" } diff --git a/packages/graphql-config/src/config.test.ts b/packages/graphql-config/src/config.test.ts new file mode 100644 index 00000000..e5dd1cda --- /dev/null +++ b/packages/graphql-config/src/config.test.ts @@ -0,0 +1,25 @@ +import { vol } from "memfs"; +import { beforeEach, describe, expect, test, vi } from "vitest"; +import { generateConfig } from "./config"; + +vi.mock("node:fs", async () => { + const { fs } = await vi.importActual("memfs"); + return fs; +}); +vi.mock("node:os", () => ({ + tmpdir: () => "/tmp", +})); + +beforeEach(() => { + vol.fromJSON({ + "/tmp": null, + }); +}); + +describe("generateConfig", () => { + test("should generate a config", () => { + const filePath = generateConfig(); + const dirs = vol.toJSON(); + expect(dirs[filePath.directiveSchema]).not.toHaveLength(0); + }); +}); diff --git a/packages/graphql-config/src/config.ts b/packages/graphql-config/src/config.ts index 2ebf32e9..9ebfb8be 100644 --- a/packages/graphql-config/src/config.ts +++ b/packages/graphql-config/src/config.ts @@ -1,12 +1,14 @@ import * as os from "node:os"; import * as path from "node:path"; import * as fs from "node:fs"; -import Document from "./directive.graphql"; +import { print } from "graphql"; +import { schemaDefinition } from "./schema"; export const generateConfig = () => { const tempGQLFile = path.join(os.tmpdir(), "fabrix-graphql-config.graphql"); + const content = schemaDefinition.definitions.map(print).join("\n"); - fs.writeFileSync(tempGQLFile, Document); + fs.writeFileSync(tempGQLFile, content); return { directiveSchema: tempGQLFile, diff --git a/packages/graphql-config/src/directive.graphql b/packages/graphql-config/src/directive.graphql deleted file mode 100644 index 142e6cf4..00000000 --- a/packages/graphql-config/src/directive.graphql +++ /dev/null @@ -1,123 +0,0 @@ -input FabrixComponentType { - """ - Component name to render the field - """ - name: String! - - """ - Component props - """ - props: [FabrixComponentProps] -} - -input FabrixComponentProps { - """ - The property name for the component - """ - name: String! - - """ - The value for the property - """ - value: String! -} - -input FabrixViewConfig { - """ - The number of grid columns the field (max: 12) - """ - gridCol: Int - - """ - The 0-based index of the field - """ - index: Int - - """ - The label of the field on UI - """ - label: String - - """ - Hide the field on UI - """ - hidden: Boolean - - """ - The component to render for the field - """ - componentType: FabrixComponentType -} - -input FabrixView { - """ - The field name in the schema - """ - field: String! - - """ - The configuration for the field - """ - config: FabrixViewConfig! -} - -""" -Fabrix directive for fields -""" -directive @fabrixView(input: [FabrixView!]) on FIELD - -input FabrixFormFieldConfig { - """ - The number of grid columns the field (max: 12) - """ - gridCol: Int - - """ - The 0-based index of the field - """ - index: Int - - """ - The label of the field on UI - """ - label: String - - """ - Placeholder text for the field - """ - placeholder: String - - """ - Hide the field on UI - """ - hidden: Boolean - - """ - The default value for the field - - The value will automatically be converted to the type of the field - """ - defaultValue: String - - """ - The component to render for the field - """ - componentType: FabrixComponentType -} - -input FabrixFormField { - """ - The field name in the schema - """ - field: String! - - """ - The configuration for the field - """ - config: FabrixFormFieldConfig! -} - -""" -Fabrix directive for form -""" -directive @fabrixForm(input: [FabrixFormField!]) on FIELD diff --git a/packages/graphql-config/src/graphql.d.ts b/packages/graphql-config/src/graphql.d.ts index dc2883a8..50b008e1 100644 --- a/packages/graphql-config/src/graphql.d.ts +++ b/packages/graphql-config/src/graphql.d.ts @@ -1,4 +1,4 @@ declare module "*.graphql" { - const Document: string; + const Document: import("graphql").DocumentNode; export default Document; } diff --git a/packages/graphql-config/src/schema.ts b/packages/graphql-config/src/schema.ts index 80a60984..05eb2284 100644 --- a/packages/graphql-config/src/schema.ts +++ b/packages/graphql-config/src/schema.ts @@ -1,4 +1,15 @@ -import { parse } from "graphql"; -import Document from "./directive.graphql"; +import { DocumentNode, Kind } from "graphql"; +import CommonSchema from "./schema/common.graphql"; +import ViewDirectiveSchema from "./schema/view.graphql"; +import FormDirectiveSchema from "./schema/form.graphql"; -export const schemaDefinition = parse(Document); +const mergeDocumentNodes = (docs: DocumentNode[]) => ({ + kind: Kind.DOCUMENT, + definitions: docs.flatMap((doc) => doc.definitions), +}); + +export const schemaDefinition = mergeDocumentNodes([ + CommonSchema, + ViewDirectiveSchema, + FormDirectiveSchema, +]); diff --git a/packages/graphql-config/src/schema/common.graphql b/packages/graphql-config/src/schema/common.graphql new file mode 100644 index 00000000..f799f2b4 --- /dev/null +++ b/packages/graphql-config/src/schema/common.graphql @@ -0,0 +1,23 @@ +input FabrixComponentType { + """ + Component name to render the field + """ + name: String! + + """ + Component props + """ + props: [FabrixComponentProps] +} + +input FabrixComponentProps { + """ + The property name for the component + """ + name: String! + + """ + The value for the property + """ + value: String! +} diff --git a/packages/graphql-config/src/schema/form.graphql b/packages/graphql-config/src/schema/form.graphql new file mode 100644 index 00000000..eb0eed01 --- /dev/null +++ b/packages/graphql-config/src/schema/form.graphql @@ -0,0 +1,55 @@ +input FabrixFormFieldConfig { + """ + The number of grid columns the field (max: 12) + """ + gridCol: Int + + """ + The 0-based index of the field + """ + index: Int + + """ + The label of the field on UI + """ + label: String + + """ + Placeholder text for the field + """ + placeholder: String + + """ + Hide the field on UI + """ + hidden: Boolean + + """ + The default value for the field + + The value will automatically be converted to the type of the field + """ + defaultValue: String + + """ + The component to render for the field + """ + componentType: FabrixComponentType +} + +input FabrixFormField { + """ + The field name in the schema + """ + field: String! + + """ + The configuration for the field + """ + config: FabrixFormFieldConfig! +} + +""" +Fabrix directive for form +""" +directive @fabrixForm(input: [FabrixFormField!]) on FIELD diff --git a/packages/graphql-config/src/schema/view.graphql b/packages/graphql-config/src/schema/view.graphql new file mode 100644 index 00000000..da37795a --- /dev/null +++ b/packages/graphql-config/src/schema/view.graphql @@ -0,0 +1,43 @@ +input FabrixViewConfig { + """ + The number of grid columns the field (max: 12) + """ + gridCol: Int + + """ + The 0-based index of the field + """ + index: Int + + """ + The label of the field on UI + """ + label: String + + """ + Hide the field on UI + """ + hidden: Boolean + + """ + The component to render for the field + """ + componentType: FabrixComponentType +} + +input FabrixView { + """ + The field name in the schema + """ + field: String! + + """ + The configuration for the field + """ + config: FabrixViewConfig! +} + +""" +Fabrix directive for fields +""" +directive @fabrixView(input: [FabrixView!]) on FIELD diff --git a/packages/graphql-config/tsconfig.json b/packages/graphql-config/tsconfig.json index f67c6684..0bd58694 100644 --- a/packages/graphql-config/tsconfig.json +++ b/packages/graphql-config/tsconfig.json @@ -1,7 +1,9 @@ { "compilerOptions": { + "target": "ES6", "moduleResolution": "Bundler", "module": "ES2015" }, - "include": ["src", "tsup.config.ts"], + "include": ["."], + "exclude": ["dist", "node_modules"] } diff --git a/packages/graphql-config/vitest.config.ts b/packages/graphql-config/vitest.config.ts new file mode 100644 index 00000000..394fbe94 --- /dev/null +++ b/packages/graphql-config/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; +import graphqlLoader from "vite-plugin-graphql-loader"; + +export default defineConfig({ + plugins: [graphqlLoader()], + test: { + include: ["src/**/*.test.ts"], + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 04c7b750..d62cca57 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -235,7 +235,7 @@ importers: version: 18.3.1 '@vitejs/plugin-react': specifier: ^4.3.1 - version: 4.3.2(vite@5.4.6(@types/node@22.7.5)) + version: 4.3.2(vite@5.4.8(@types/node@22.7.5)) eslint: specifier: ^9.6.0 version: 9.12.0 @@ -256,7 +256,7 @@ importers: version: 5.6.3 vite-tsconfig-paths: specifier: ^4.3.2 - version: 4.3.2(typescript@5.6.3)(vite@5.4.6(@types/node@22.7.5)) + version: 4.3.2(typescript@5.6.3)(vite@5.4.8(@types/node@22.7.5)) vitest: specifier: ^2.0.3 version: 2.1.2(@types/node@22.7.5)(happy-dom@15.7.4)(msw@2.4.9(typescript@5.6.3)) @@ -279,6 +279,9 @@ importers: eslint: specifier: ^9.6.0 version: 9.12.0 + memfs: + specifier: ^4.14.0 + version: 4.14.0 prettier: specifier: ^3.3.3 version: 3.3.3 @@ -288,6 +291,12 @@ importers: typescript: specifier: ^5.5.3 version: 5.6.3 + vite-plugin-graphql-loader: + specifier: ^4.0.4 + version: 4.0.4 + vitest: + specifier: ^2.0.3 + version: 2.1.2(@types/node@22.7.5)(happy-dom@15.7.4)(msw@2.4.9(typescript@5.6.3)) shared/eslint: dependencies: @@ -302,7 +311,7 @@ importers: version: 9.1.0(eslint@9.12.0) eslint-plugin-import: specifier: ^2.29.1 - version: 2.31.0(eslint@9.12.0) + version: 2.31.0(@typescript-eslint/parser@8.8.1(eslint@9.12.0)(typescript@5.6.3))(eslint@9.12.0) typescript: specifier: ^5 version: 5.6.3 @@ -1053,6 +1062,24 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jsonjoy.com/base64@1.1.2': + resolution: {integrity: sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pack@1.1.0': + resolution: {integrity: sha512-zlQONA+msXPPwHWZMKFVS78ewFczIll5lXiVPwFPCZUsrOKdxc2AvxU1HoNBmMRhqDZUR9HkC3UOm+6pME6Xsg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/util@1.5.0': + resolution: {integrity: sha512-ojoNsrIuPI9g6o8UxhraZQSyF2ByJanAY4cTFbc8Mf2AXEF4aQRGY1dJxyJpuyav8r9FGflEt/Ff3u5Nt6YMPA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + '@mswjs/interceptors@0.35.9': resolution: {integrity: sha512-SSnyl/4ni/2ViHKkiZb8eajA/eN1DNFaHjhGiLUdZvDz6PKF4COSf/17xqSz64nOo2Ia29SA6B2KNCsyCbVmaQ==} engines: {node: '>=18'} @@ -2333,6 +2360,10 @@ packages: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} + hyperdyperid@1.2.0: + resolution: {integrity: sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==} + engines: {node: '>=10.18'} + iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} @@ -2607,6 +2638,10 @@ packages: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} + memfs@4.14.0: + resolution: {integrity: sha512-JUeY0F/fQZgIod31Ja1eJgiSxLn7BfQlCnqhwXFBzFHEw63OdLK7VJUJ7bnzNsWgCyoUP5tEp1VRY8rDaYzqOA==} + engines: {node: '>= 4.0.0'} + memoize-one@6.0.0: resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} @@ -3251,6 +3286,12 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + thingies@1.21.0: + resolution: {integrity: sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==} + engines: {node: '>=10.18'} + peerDependencies: + tslib: ^2 + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -3298,6 +3339,12 @@ packages: tr46@1.0.1: resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + tree-dump@1.0.2: + resolution: {integrity: sha512-dpev9ABuLWdEubk+cIaI9cHwRNNDjkBBLXTwI4UCUFdQ5xXKqNXoK4FEciw/vxf+NQ7Cb7sGUyeUtORvHIdRXQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true @@ -3516,6 +3563,9 @@ packages: engines: {node: ^18.0.0 || >=20.0.0} hasBin: true + vite-plugin-graphql-loader@4.0.4: + resolution: {integrity: sha512-lYnpQ2luV2fcuXmOJADljuktfMbDW00Y+6QS+Ek8Jz1Vdzlj/51LSGJwZqyjJ24a5YQ+o29Hr6el/5+nlZetvg==} + vite-tsconfig-paths@4.3.2: resolution: {integrity: sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA==} peerDependencies: @@ -4442,6 +4492,22 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@jsonjoy.com/base64@1.1.2(tslib@2.7.0)': + dependencies: + tslib: 2.7.0 + + '@jsonjoy.com/json-pack@1.1.0(tslib@2.7.0)': + dependencies: + '@jsonjoy.com/base64': 1.1.2(tslib@2.7.0) + '@jsonjoy.com/util': 1.5.0(tslib@2.7.0) + hyperdyperid: 1.2.0 + thingies: 1.21.0(tslib@2.7.0) + tslib: 2.7.0 + + '@jsonjoy.com/util@1.5.0(tslib@2.7.0)': + dependencies: + tslib: 2.7.0 + '@mswjs/interceptors@0.35.9': dependencies: '@open-draft/deferred-promise': 2.2.0 @@ -4854,17 +4920,6 @@ snapshots: transitivePeerDependencies: - graphql - '@vitejs/plugin-react@4.3.2(vite@5.4.6(@types/node@22.7.5))': - dependencies: - '@babel/core': 7.25.2 - '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.25.2) - '@types/babel__core': 7.20.5 - react-refresh: 0.14.2 - vite: 5.4.6(@types/node@22.7.5) - transitivePeerDependencies: - - supports-color - '@vitejs/plugin-react@4.3.2(vite@5.4.8(@types/node@22.7.5))': dependencies: '@babel/core': 7.25.2 @@ -5525,16 +5580,17 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(eslint-import-resolver-node@0.3.9)(eslint@9.12.0): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.8.1(eslint@9.12.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint@9.12.0): dependencies: debug: 3.2.7 optionalDependencies: + '@typescript-eslint/parser': 8.8.1(eslint@9.12.0)(typescript@5.6.3) eslint: 9.12.0 eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color - eslint-plugin-import@2.31.0(eslint@9.12.0): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@9.12.0)(typescript@5.6.3))(eslint@9.12.0): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -5545,7 +5601,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.12.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(eslint-import-resolver-node@0.3.9)(eslint@9.12.0) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.8.1(eslint@9.12.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint@9.12.0) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -5556,6 +5612,8 @@ snapshots: semver: 6.3.1 string.prototype.trimend: 1.0.8 tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.8.1(eslint@9.12.0)(typescript@5.6.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -5924,6 +5982,8 @@ snapshots: human-signals@2.1.0: {} + hyperdyperid@1.2.0: {} + iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 @@ -6161,6 +6221,13 @@ snapshots: media-typer@0.3.0: {} + memfs@4.14.0: + dependencies: + '@jsonjoy.com/json-pack': 1.1.0(tslib@2.7.0) + '@jsonjoy.com/util': 1.5.0(tslib@2.7.0) + tree-dump: 1.0.2(tslib@2.7.0) + tslib: 2.7.0 + memoize-one@6.0.0: {} merge-descriptors@1.0.3: {} @@ -6826,6 +6893,10 @@ snapshots: dependencies: any-promise: 1.3.0 + thingies@1.21.0(tslib@2.7.0): + dependencies: + tslib: 2.7.0 + tinybench@2.9.0: {} tinyexec@0.3.0: {} @@ -6864,6 +6935,10 @@ snapshots: dependencies: punycode: 2.3.1 + tree-dump@1.0.2(tslib@2.7.0): + dependencies: + tslib: 2.7.0 + tree-kill@1.2.2: {} ts-api-utils@1.3.0(typescript@5.6.3): @@ -7087,13 +7162,19 @@ snapshots: - supports-color - terser - vite-tsconfig-paths@4.3.2(typescript@5.6.3)(vite@5.4.6(@types/node@22.7.5)): + vite-plugin-graphql-loader@4.0.4: + dependencies: + graphql: 16.9.0 + graphql-tag: 2.12.6(graphql@16.9.0) + magic-string: 0.30.11 + + vite-tsconfig-paths@4.3.2(typescript@5.6.3)(vite@5.4.8(@types/node@22.7.5)): dependencies: debug: 4.3.7 globrex: 0.1.2 tsconfck: 3.1.3(typescript@5.6.3) optionalDependencies: - vite: 5.4.6(@types/node@22.7.5) + vite: 5.4.8(@types/node@22.7.5) transitivePeerDependencies: - supports-color - typescript