@gqlkit-ts/cli 0.0.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/auto-type-generator/auto-type-generator.d.ts +62 -0
- package/dist/auto-type-generator/auto-type-generator.d.ts.map +1 -0
- package/dist/auto-type-generator/auto-type-generator.js +540 -0
- package/dist/auto-type-generator/auto-type-generator.js.map +1 -0
- package/dist/auto-type-generator/index.d.ts +4 -0
- package/dist/auto-type-generator/index.d.ts.map +1 -0
- package/dist/auto-type-generator/index.js +3 -0
- package/dist/auto-type-generator/index.js.map +1 -0
- package/dist/auto-type-generator/inline-enum-collector.d.ts +31 -0
- package/dist/auto-type-generator/inline-enum-collector.d.ts.map +1 -0
- package/dist/auto-type-generator/inline-enum-collector.js +157 -0
- package/dist/auto-type-generator/inline-enum-collector.js.map +1 -0
- package/dist/auto-type-generator/name-collision-validator.d.ts +17 -0
- package/dist/auto-type-generator/name-collision-validator.d.ts.map +1 -0
- package/dist/auto-type-generator/name-collision-validator.js +68 -0
- package/dist/auto-type-generator/name-collision-validator.js.map +1 -0
- package/dist/auto-type-generator/naming-convention.d.ts +44 -0
- package/dist/auto-type-generator/naming-convention.d.ts.map +1 -0
- package/dist/auto-type-generator/naming-convention.js +67 -0
- package/dist/auto-type-generator/naming-convention.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +11 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/gen.d.ts +32 -0
- package/dist/commands/gen.d.ts.map +1 -0
- package/dist/commands/gen.js +101 -0
- package/dist/commands/gen.js.map +1 -0
- package/dist/commands/main.d.ts +12 -0
- package/dist/commands/main.d.ts.map +1 -0
- package/dist/commands/main.js +5 -0
- package/dist/commands/main.js.map +1 -0
- package/dist/config/define-config.d.ts +26 -0
- package/dist/config/define-config.d.ts.map +1 -0
- package/dist/config/define-config.js +27 -0
- package/dist/config/define-config.js.map +1 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +2 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/types.d.ts +131 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +2 -0
- package/dist/config/types.js.map +1 -0
- package/dist/config-loader/index.d.ts +3 -0
- package/dist/config-loader/index.d.ts.map +1 -0
- package/dist/config-loader/index.js +2 -0
- package/dist/config-loader/index.js.map +1 -0
- package/dist/config-loader/loader.d.ts +50 -0
- package/dist/config-loader/loader.d.ts.map +1 -0
- package/dist/config-loader/loader.js +78 -0
- package/dist/config-loader/loader.js.map +1 -0
- package/dist/config-loader/validator.d.ts +13 -0
- package/dist/config-loader/validator.d.ts.map +1 -0
- package/dist/config-loader/validator.js +497 -0
- package/dist/config-loader/validator.js.map +1 -0
- package/dist/gen-orchestrator/hook-executor/hook-executor.d.ts +25 -0
- package/dist/gen-orchestrator/hook-executor/hook-executor.d.ts.map +1 -0
- package/dist/gen-orchestrator/hook-executor/hook-executor.js +68 -0
- package/dist/gen-orchestrator/hook-executor/hook-executor.js.map +1 -0
- package/dist/gen-orchestrator/orchestrator.d.ts +30 -0
- package/dist/gen-orchestrator/orchestrator.d.ts.map +1 -0
- package/dist/gen-orchestrator/orchestrator.js +505 -0
- package/dist/gen-orchestrator/orchestrator.js.map +1 -0
- package/dist/gen-orchestrator/reporter/diagnostic-reporter.d.ts +9 -0
- package/dist/gen-orchestrator/reporter/diagnostic-reporter.d.ts.map +1 -0
- package/dist/gen-orchestrator/reporter/diagnostic-reporter.js +32 -0
- package/dist/gen-orchestrator/reporter/diagnostic-reporter.js.map +1 -0
- package/dist/gen-orchestrator/reporter/progress-reporter.d.ts +19 -0
- package/dist/gen-orchestrator/reporter/progress-reporter.d.ts.map +1 -0
- package/dist/gen-orchestrator/reporter/progress-reporter.js +38 -0
- package/dist/gen-orchestrator/reporter/progress-reporter.js.map +1 -0
- package/dist/gen-orchestrator/writer/file-writer.d.ts +13 -0
- package/dist/gen-orchestrator/writer/file-writer.d.ts.map +1 -0
- package/dist/gen-orchestrator/writer/file-writer.js +22 -0
- package/dist/gen-orchestrator/writer/file-writer.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/resolver-extractor/extract-resolvers.d.ts +49 -0
- package/dist/resolver-extractor/extract-resolvers.d.ts.map +1 -0
- package/dist/resolver-extractor/extract-resolvers.js +2 -0
- package/dist/resolver-extractor/extract-resolvers.js.map +1 -0
- package/dist/resolver-extractor/extractor/define-api-extractor.d.ts +60 -0
- package/dist/resolver-extractor/extractor/define-api-extractor.d.ts.map +1 -0
- package/dist/resolver-extractor/extractor/define-api-extractor.js +509 -0
- package/dist/resolver-extractor/extractor/define-api-extractor.js.map +1 -0
- package/dist/resolver-extractor/index.d.ts +5 -0
- package/dist/resolver-extractor/index.d.ts.map +1 -0
- package/dist/resolver-extractor/index.js +2 -0
- package/dist/resolver-extractor/index.js.map +1 -0
- package/dist/resolver-extractor/validator/abstract-resolver-validator.d.ts +25 -0
- package/dist/resolver-extractor/validator/abstract-resolver-validator.d.ts.map +1 -0
- package/dist/resolver-extractor/validator/abstract-resolver-validator.js +172 -0
- package/dist/resolver-extractor/validator/abstract-resolver-validator.js.map +1 -0
- package/dist/resolver-extractor/validator/only-validator.d.ts +61 -0
- package/dist/resolver-extractor/validator/only-validator.d.ts.map +1 -0
- package/dist/resolver-extractor/validator/only-validator.js +76 -0
- package/dist/resolver-extractor/validator/only-validator.js.map +1 -0
- package/dist/schema-generator/builder/ast-builder.d.ts +7 -0
- package/dist/schema-generator/builder/ast-builder.d.ts.map +1 -0
- package/dist/schema-generator/builder/ast-builder.js +417 -0
- package/dist/schema-generator/builder/ast-builder.js.map +1 -0
- package/dist/schema-generator/emitter/code-emitter.d.ts +15 -0
- package/dist/schema-generator/emitter/code-emitter.d.ts.map +1 -0
- package/dist/schema-generator/emitter/code-emitter.js +216 -0
- package/dist/schema-generator/emitter/code-emitter.js.map +1 -0
- package/dist/schema-generator/emitter/sdl-emitter.d.ts +7 -0
- package/dist/schema-generator/emitter/sdl-emitter.d.ts.map +1 -0
- package/dist/schema-generator/emitter/sdl-emitter.js +11 -0
- package/dist/schema-generator/emitter/sdl-emitter.js.map +1 -0
- package/dist/schema-generator/generate-schema.d.ts +26 -0
- package/dist/schema-generator/generate-schema.d.ts.map +1 -0
- package/dist/schema-generator/generate-schema.js +93 -0
- package/dist/schema-generator/generate-schema.js.map +1 -0
- package/dist/schema-generator/index.d.ts +4 -0
- package/dist/schema-generator/index.d.ts.map +1 -0
- package/dist/schema-generator/index.js +2 -0
- package/dist/schema-generator/index.js.map +1 -0
- package/dist/schema-generator/integrator/result-integrator.d.ts +113 -0
- package/dist/schema-generator/integrator/result-integrator.d.ts.map +1 -0
- package/dist/schema-generator/integrator/result-integrator.js +438 -0
- package/dist/schema-generator/integrator/result-integrator.js.map +1 -0
- package/dist/schema-generator/pruner/schema-pruner.d.ts +16 -0
- package/dist/schema-generator/pruner/schema-pruner.d.ts.map +1 -0
- package/dist/schema-generator/pruner/schema-pruner.js +66 -0
- package/dist/schema-generator/pruner/schema-pruner.js.map +1 -0
- package/dist/schema-generator/resolver-collector/resolver-collector.d.ts +24 -0
- package/dist/schema-generator/resolver-collector/resolver-collector.d.ts.map +1 -0
- package/dist/schema-generator/resolver-collector/resolver-collector.js +61 -0
- package/dist/schema-generator/resolver-collector/resolver-collector.js.map +1 -0
- package/dist/shared/constants.d.ts +54 -0
- package/dist/shared/constants.d.ts.map +1 -0
- package/dist/shared/constants.js +109 -0
- package/dist/shared/constants.js.map +1 -0
- package/dist/shared/default-value-detector.d.ts +40 -0
- package/dist/shared/default-value-detector.d.ts.map +1 -0
- package/dist/shared/default-value-detector.js +124 -0
- package/dist/shared/default-value-detector.js.map +1 -0
- package/dist/shared/diagnostics.d.ts +4 -0
- package/dist/shared/diagnostics.d.ts.map +1 -0
- package/dist/shared/diagnostics.js +25 -0
- package/dist/shared/diagnostics.js.map +1 -0
- package/dist/shared/directive-definition-extractor.d.ts +64 -0
- package/dist/shared/directive-definition-extractor.d.ts.map +1 -0
- package/dist/shared/directive-definition-extractor.js +399 -0
- package/dist/shared/directive-definition-extractor.js.map +1 -0
- package/dist/shared/directive-detector.d.ts +102 -0
- package/dist/shared/directive-detector.d.ts.map +1 -0
- package/dist/shared/directive-detector.js +415 -0
- package/dist/shared/directive-detector.js.map +1 -0
- package/dist/shared/file-scanner.d.ts +25 -0
- package/dist/shared/file-scanner.d.ts.map +1 -0
- package/dist/shared/file-scanner.js +101 -0
- package/dist/shared/file-scanner.js.map +1 -0
- package/dist/shared/index.d.ts +10 -0
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/index.js +6 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/shared/inline-object-extractor.d.ts +13 -0
- package/dist/shared/inline-object-extractor.d.ts.map +1 -0
- package/dist/shared/inline-object-extractor.js +65 -0
- package/dist/shared/inline-object-extractor.js.map +1 -0
- package/dist/shared/inline-object-utils.d.ts +7 -0
- package/dist/shared/inline-object-utils.d.ts.map +1 -0
- package/dist/shared/inline-object-utils.js +23 -0
- package/dist/shared/inline-object-utils.js.map +1 -0
- package/dist/shared/interface-detector.d.ts +23 -0
- package/dist/shared/interface-detector.d.ts.map +1 -0
- package/dist/shared/interface-detector.js +133 -0
- package/dist/shared/interface-detector.js.map +1 -0
- package/dist/shared/interface-validator.d.ts +9 -0
- package/dist/shared/interface-validator.d.ts.map +1 -0
- package/dist/shared/interface-validator.js +152 -0
- package/dist/shared/interface-validator.js.map +1 -0
- package/dist/shared/metadata-detector.d.ts +65 -0
- package/dist/shared/metadata-detector.d.ts.map +1 -0
- package/dist/shared/metadata-detector.js +333 -0
- package/dist/shared/metadata-detector.js.map +1 -0
- package/dist/shared/path-utils.d.ts +2 -0
- package/dist/shared/path-utils.d.ts.map +1 -0
- package/dist/shared/path-utils.js +4 -0
- package/dist/shared/path-utils.js.map +1 -0
- package/dist/shared/program-factory.d.ts +14 -0
- package/dist/shared/program-factory.d.ts.map +1 -0
- package/dist/shared/program-factory.js +29 -0
- package/dist/shared/program-factory.js.map +1 -0
- package/dist/shared/source-location.d.ts +11 -0
- package/dist/shared/source-location.d.ts.map +1 -0
- package/dist/shared/source-location.js +15 -0
- package/dist/shared/source-location.js.map +1 -0
- package/dist/shared/tsconfig-loader.d.ts +13 -0
- package/dist/shared/tsconfig-loader.d.ts.map +1 -0
- package/dist/shared/tsconfig-loader.js +90 -0
- package/dist/shared/tsconfig-loader.js.map +1 -0
- package/dist/shared/tsdoc-parser.d.ts +12 -0
- package/dist/shared/tsdoc-parser.d.ts.map +1 -0
- package/dist/shared/tsdoc-parser.js +101 -0
- package/dist/shared/tsdoc-parser.js.map +1 -0
- package/dist/shared/type-converter.d.ts +3 -0
- package/dist/shared/type-converter.d.ts.map +1 -0
- package/dist/shared/type-converter.js +83 -0
- package/dist/shared/type-converter.js.map +1 -0
- package/dist/shared/typescript-utils.d.ts +82 -0
- package/dist/shared/typescript-utils.d.ts.map +1 -0
- package/dist/shared/typescript-utils.js +197 -0
- package/dist/shared/typescript-utils.js.map +1 -0
- package/dist/type-extractor/collector/result-collector.d.ts +7 -0
- package/dist/type-extractor/collector/result-collector.d.ts.map +1 -0
- package/dist/type-extractor/collector/result-collector.js +35 -0
- package/dist/type-extractor/collector/result-collector.js.map +1 -0
- package/dist/type-extractor/collector/scalar-collector.d.ts +108 -0
- package/dist/type-extractor/collector/scalar-collector.d.ts.map +1 -0
- package/dist/type-extractor/collector/scalar-collector.js +123 -0
- package/dist/type-extractor/collector/scalar-collector.js.map +1 -0
- package/dist/type-extractor/converter/field-eligibility.d.ts +34 -0
- package/dist/type-extractor/converter/field-eligibility.d.ts.map +1 -0
- package/dist/type-extractor/converter/field-eligibility.js +89 -0
- package/dist/type-extractor/converter/field-eligibility.js.map +1 -0
- package/dist/type-extractor/converter/graphql-converter.d.ts +7 -0
- package/dist/type-extractor/converter/graphql-converter.d.ts.map +1 -0
- package/dist/type-extractor/converter/graphql-converter.js +338 -0
- package/dist/type-extractor/converter/graphql-converter.js.map +1 -0
- package/dist/type-extractor/extract-types.d.ts +2 -0
- package/dist/type-extractor/extract-types.d.ts.map +1 -0
- package/dist/type-extractor/extract-types.js +2 -0
- package/dist/type-extractor/extract-types.js.map +1 -0
- package/dist/type-extractor/extractor/field-type-resolver.d.ts +28 -0
- package/dist/type-extractor/extractor/field-type-resolver.d.ts.map +1 -0
- package/dist/type-extractor/extractor/field-type-resolver.js +394 -0
- package/dist/type-extractor/extractor/field-type-resolver.js.map +1 -0
- package/dist/type-extractor/extractor/type-extractor.d.ts +36 -0
- package/dist/type-extractor/extractor/type-extractor.d.ts.map +1 -0
- package/dist/type-extractor/extractor/type-extractor.js +1082 -0
- package/dist/type-extractor/extractor/type-extractor.js.map +1 -0
- package/dist/type-extractor/extractor/type-name-collector.d.ts +24 -0
- package/dist/type-extractor/extractor/type-name-collector.d.ts.map +1 -0
- package/dist/type-extractor/extractor/type-name-collector.js +102 -0
- package/dist/type-extractor/extractor/type-name-collector.js.map +1 -0
- package/dist/type-extractor/index.d.ts +4 -0
- package/dist/type-extractor/index.d.ts.map +1 -0
- package/dist/type-extractor/index.js +2 -0
- package/dist/type-extractor/index.js.map +1 -0
- package/dist/type-extractor/mapper/scalar-base-type-mapper.d.ts +89 -0
- package/dist/type-extractor/mapper/scalar-base-type-mapper.d.ts.map +1 -0
- package/dist/type-extractor/mapper/scalar-base-type-mapper.js +158 -0
- package/dist/type-extractor/mapper/scalar-base-type-mapper.js.map +1 -0
- package/dist/type-extractor/types/diagnostics.d.ts +17 -0
- package/dist/type-extractor/types/diagnostics.d.ts.map +1 -0
- package/dist/type-extractor/types/diagnostics.js +2 -0
- package/dist/type-extractor/types/diagnostics.js.map +1 -0
- package/dist/type-extractor/types/graphql.d.ts +42 -0
- package/dist/type-extractor/types/graphql.d.ts.map +1 -0
- package/dist/type-extractor/types/graphql.js +2 -0
- package/dist/type-extractor/types/graphql.js.map +1 -0
- package/dist/type-extractor/types/index.d.ts +6 -0
- package/dist/type-extractor/types/index.d.ts.map +1 -0
- package/dist/type-extractor/types/index.js +2 -0
- package/dist/type-extractor/types/index.js.map +1 -0
- package/dist/type-extractor/types/ts-type-reference-factory.d.ts +39 -0
- package/dist/type-extractor/types/ts-type-reference-factory.d.ts.map +1 -0
- package/dist/type-extractor/types/ts-type-reference-factory.js +69 -0
- package/dist/type-extractor/types/ts-type-reference-factory.js.map +1 -0
- package/dist/type-extractor/types/typescript.d.ts +106 -0
- package/dist/type-extractor/types/typescript.d.ts.map +1 -0
- package/dist/type-extractor/types/typescript.js +2 -0
- package/dist/type-extractor/types/typescript.js.map +1 -0
- package/dist/type-extractor/validator/type-validator.d.ts +11 -0
- package/dist/type-extractor/validator/type-validator.d.ts.map +1 -0
- package/dist/type-extractor/validator/type-validator.js +53 -0
- package/dist/type-extractor/validator/type-validator.js.map +1 -0
- package/docs/configuration.md +163 -0
- package/docs/getting-started.md +117 -0
- package/docs/index.md +33 -0
- package/docs/integration/apollo.md +109 -0
- package/docs/integration/drizzle.md +191 -0
- package/docs/integration/yoga.md +108 -0
- package/docs/schema/abstract-resolvers.md +146 -0
- package/docs/schema/directives.md +196 -0
- package/docs/schema/documentation.md +176 -0
- package/docs/schema/enums.md +370 -0
- package/docs/schema/fields.md +186 -0
- package/docs/schema/index.md +38 -0
- package/docs/schema/inputs.md +279 -0
- package/docs/schema/interfaces.md +178 -0
- package/docs/schema/objects.md +188 -0
- package/docs/schema/queries-mutations.md +207 -0
- package/docs/schema/scalars.md +194 -0
- package/docs/schema/unions.md +90 -0
- package/docs/what-is-gqlkit.md +22 -0
- package/package.json +66 -7
- package/src/auto-type-generator/auto-type-generator.ts +925 -0
- package/src/auto-type-generator/index.ts +21 -0
- package/src/auto-type-generator/inline-enum-collector.ts +290 -0
- package/src/auto-type-generator/name-collision-validator.ts +119 -0
- package/src/auto-type-generator/naming-convention.ts +126 -0
- package/src/cli.ts +11 -0
- package/src/commands/gen.ts +141 -0
- package/src/commands/main.ts +5 -0
- package/src/config/define-config.ts +28 -0
- package/src/config/index.ts +7 -0
- package/src/config/types.ts +144 -0
- package/src/config-loader/index.ts +14 -0
- package/src/config-loader/loader.ts +143 -0
- package/src/config-loader/validator.ts +672 -0
- package/src/gen-orchestrator/hook-executor/hook-executor.ts +117 -0
- package/src/gen-orchestrator/orchestrator.ts +784 -0
- package/src/gen-orchestrator/reporter/diagnostic-reporter.ts +44 -0
- package/src/gen-orchestrator/reporter/progress-reporter.ts +61 -0
- package/src/gen-orchestrator/writer/file-writer.ts +38 -0
- package/src/index.ts +2 -0
- package/src/resolver-extractor/extract-resolvers.ts +63 -0
- package/src/resolver-extractor/extractor/define-api-extractor.ts +806 -0
- package/src/resolver-extractor/index.ts +19 -0
- package/src/resolver-extractor/validator/abstract-resolver-validator.ts +251 -0
- package/src/resolver-extractor/validator/only-validator.ts +158 -0
- package/src/schema-generator/builder/ast-builder.ts +706 -0
- package/src/schema-generator/emitter/code-emitter.ts +351 -0
- package/src/schema-generator/emitter/sdl-emitter.ts +13 -0
- package/src/schema-generator/generate-schema.ts +170 -0
- package/src/schema-generator/index.ts +19 -0
- package/src/schema-generator/integrator/result-integrator.ts +690 -0
- package/src/schema-generator/pruner/schema-pruner.ts +112 -0
- package/src/schema-generator/resolver-collector/resolver-collector.ts +123 -0
- package/src/shared/constants.ts +122 -0
- package/src/shared/default-value-detector.ts +172 -0
- package/src/shared/diagnostics.ts +35 -0
- package/src/shared/directive-definition-extractor.ts +564 -0
- package/src/shared/directive-detector.ts +556 -0
- package/src/shared/file-scanner.ts +170 -0
- package/src/shared/index.ts +32 -0
- package/src/shared/inline-object-extractor.ts +102 -0
- package/src/shared/inline-object-utils.ts +23 -0
- package/src/shared/interface-detector.ts +176 -0
- package/src/shared/interface-validator.ts +211 -0
- package/src/shared/metadata-detector.ts +443 -0
- package/src/shared/path-utils.ts +3 -0
- package/src/shared/program-factory.ts +51 -0
- package/src/shared/source-location.ts +27 -0
- package/src/shared/tsconfig-loader.ts +126 -0
- package/src/shared/tsdoc-parser.ts +155 -0
- package/src/shared/type-converter.ts +99 -0
- package/src/shared/typescript-utils.ts +246 -0
- package/src/type-extractor/collector/result-collector.ts +57 -0
- package/src/type-extractor/collector/scalar-collector.ts +254 -0
- package/src/type-extractor/converter/field-eligibility.ts +112 -0
- package/src/type-extractor/converter/graphql-converter.ts +459 -0
- package/src/type-extractor/extract-types.ts +1 -0
- package/src/type-extractor/extractor/field-type-resolver.ts +569 -0
- package/src/type-extractor/extractor/type-extractor.ts +1567 -0
- package/src/type-extractor/extractor/type-name-collector.ts +130 -0
- package/src/type-extractor/index.ts +20 -0
- package/src/type-extractor/mapper/scalar-base-type-mapper.ts +265 -0
- package/src/type-extractor/types/diagnostics.ts +99 -0
- package/src/type-extractor/types/graphql.ts +55 -0
- package/src/type-extractor/types/index.ts +37 -0
- package/src/type-extractor/types/ts-type-reference-factory.ts +137 -0
- package/src/type-extractor/types/typescript.ts +133 -0
- package/src/type-extractor/validator/type-validator.ts +77 -0
- package/README.md +0 -45
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { readdir, stat } from "node:fs/promises";
|
|
2
|
+
import { join, matchesGlob, relative, resolve } from "node:path";
|
|
3
|
+
import type { Diagnostic } from "../type-extractor/types/index.js";
|
|
4
|
+
import { toPosixPath } from "./path-utils.js";
|
|
5
|
+
|
|
6
|
+
export interface ScanResult {
|
|
7
|
+
readonly files: ReadonlyArray<string>;
|
|
8
|
+
readonly errors: ReadonlyArray<Diagnostic>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface ScanOptions {
|
|
12
|
+
/**
|
|
13
|
+
* Glob patterns to exclude files.
|
|
14
|
+
* Patterns are matched against file paths relative to the scan directory.
|
|
15
|
+
*/
|
|
16
|
+
readonly excludeGlobs?: ReadonlyArray<string>;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* File paths to exclude (for generated files).
|
|
20
|
+
* Each path is individually excluded from scanning.
|
|
21
|
+
*/
|
|
22
|
+
readonly excludePaths?: ReadonlyArray<string>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Simple suffix patterns to exclude (existing behavior).
|
|
26
|
+
* @deprecated Use excludeGlobs instead
|
|
27
|
+
*/
|
|
28
|
+
readonly excludePatterns?: ReadonlyArray<string>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const TS_SOURCE_EXTENSIONS = [".ts", ".cts", ".mts"];
|
|
32
|
+
const TS_DEFINITION_SUFFIXES = [".d.ts", ".d.cts", ".d.mts"];
|
|
33
|
+
|
|
34
|
+
export function isTypeScriptSourceFile(fileName: string): boolean {
|
|
35
|
+
if (TS_DEFINITION_SUFFIXES.some((suffix) => fileName.endsWith(suffix))) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
return TS_SOURCE_EXTENSIONS.some((ext) => fileName.endsWith(ext));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function matchesSuffixPattern(fileName: string, pattern: string): boolean {
|
|
42
|
+
return fileName.endsWith(pattern);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function shouldExcludeBySuffix(
|
|
46
|
+
fileName: string,
|
|
47
|
+
excludePatterns: ReadonlyArray<string>,
|
|
48
|
+
): boolean {
|
|
49
|
+
return excludePatterns.some((pattern) =>
|
|
50
|
+
matchesSuffixPattern(fileName, pattern),
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function shouldExcludeByGlobs(
|
|
55
|
+
filePath: string,
|
|
56
|
+
rootDir: string,
|
|
57
|
+
excludeGlobs: ReadonlyArray<string>,
|
|
58
|
+
): boolean {
|
|
59
|
+
if (excludeGlobs.length === 0) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const relativePath = toPosixPath(relative(rootDir, filePath));
|
|
64
|
+
return excludeGlobs.some((pattern) => matchesGlob(relativePath, pattern));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function shouldExcludeByPaths(
|
|
68
|
+
filePath: string,
|
|
69
|
+
excludePaths: ReadonlyArray<string>,
|
|
70
|
+
): boolean {
|
|
71
|
+
const normalizedFilePath = toPosixPath(filePath);
|
|
72
|
+
return excludePaths.some(
|
|
73
|
+
(excludePath) => normalizedFilePath === toPosixPath(excludePath),
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
interface CollectFilesContext {
|
|
78
|
+
readonly rootDir: string;
|
|
79
|
+
readonly excludePatterns: ReadonlyArray<string>;
|
|
80
|
+
readonly excludeGlobs: ReadonlyArray<string>;
|
|
81
|
+
readonly excludePaths: ReadonlyArray<string>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function collectFiles(
|
|
85
|
+
directory: string,
|
|
86
|
+
files: string[],
|
|
87
|
+
context: CollectFilesContext,
|
|
88
|
+
): Promise<void> {
|
|
89
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
90
|
+
|
|
91
|
+
for (const entry of entries) {
|
|
92
|
+
const fullPath = join(directory, entry.name);
|
|
93
|
+
|
|
94
|
+
if (entry.isDirectory()) {
|
|
95
|
+
if (entry.name === "node_modules") {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
await collectFiles(fullPath, files, context);
|
|
99
|
+
} else if (entry.isFile()) {
|
|
100
|
+
if (!isTypeScriptSourceFile(entry.name)) {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if (shouldExcludeBySuffix(entry.name, context.excludePatterns)) {
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (
|
|
107
|
+
shouldExcludeByGlobs(fullPath, context.rootDir, context.excludeGlobs)
|
|
108
|
+
) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (shouldExcludeByPaths(fullPath, context.excludePaths)) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
files.push(toPosixPath(fullPath));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export async function scanDirectory(
|
|
120
|
+
directory: string,
|
|
121
|
+
options: ScanOptions = {},
|
|
122
|
+
): Promise<ScanResult> {
|
|
123
|
+
const absolutePath = resolve(directory);
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
const stats = await stat(absolutePath);
|
|
127
|
+
if (!stats.isDirectory()) {
|
|
128
|
+
return {
|
|
129
|
+
files: [],
|
|
130
|
+
errors: [
|
|
131
|
+
{
|
|
132
|
+
code: "DIRECTORY_NOT_FOUND",
|
|
133
|
+
message: `Path is not a directory: ${absolutePath}`,
|
|
134
|
+
severity: "error",
|
|
135
|
+
location: null,
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
} catch {
|
|
141
|
+
return {
|
|
142
|
+
files: [],
|
|
143
|
+
errors: [
|
|
144
|
+
{
|
|
145
|
+
code: "DIRECTORY_NOT_FOUND",
|
|
146
|
+
message: `Directory not found: ${absolutePath}`,
|
|
147
|
+
severity: "error",
|
|
148
|
+
location: null,
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const context: CollectFilesContext = {
|
|
155
|
+
rootDir: absolutePath,
|
|
156
|
+
excludePatterns: options.excludePatterns ?? [],
|
|
157
|
+
excludeGlobs: options.excludeGlobs ?? [],
|
|
158
|
+
excludePaths: options.excludePaths ?? [],
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const files: string[] = [];
|
|
162
|
+
await collectFiles(absolutePath, files, context);
|
|
163
|
+
|
|
164
|
+
files.sort();
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
files,
|
|
168
|
+
errors: [],
|
|
169
|
+
};
|
|
170
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
DefaultValueDetectionError,
|
|
3
|
+
DefaultValueDetectionErrorCode,
|
|
4
|
+
DefaultValueDetectionResult,
|
|
5
|
+
} from "./default-value-detector.js";
|
|
6
|
+
export { collectDiagnostics, deduplicateDiagnostics } from "./diagnostics.js";
|
|
7
|
+
export {
|
|
8
|
+
type DirectiveArgumentDefinition,
|
|
9
|
+
type DirectiveDefinitionError,
|
|
10
|
+
type DirectiveDefinitionErrorCode,
|
|
11
|
+
type DirectiveDefinitionExtractionResult,
|
|
12
|
+
type DirectiveDefinitionInfo,
|
|
13
|
+
type DirectiveLocation,
|
|
14
|
+
extractDirectiveDefinitions,
|
|
15
|
+
} from "./directive-definition-extractor.js";
|
|
16
|
+
export type {
|
|
17
|
+
DirectiveArgument,
|
|
18
|
+
DirectiveArgumentValue,
|
|
19
|
+
DirectiveDetectionError,
|
|
20
|
+
DirectiveDetectionErrorCode,
|
|
21
|
+
DirectiveDetectionResult,
|
|
22
|
+
DirectiveInfo,
|
|
23
|
+
} from "./directive-detector.js";
|
|
24
|
+
export {
|
|
25
|
+
type ScanOptions,
|
|
26
|
+
type ScanResult,
|
|
27
|
+
scanDirectory,
|
|
28
|
+
} from "./file-scanner.js";
|
|
29
|
+
export type { TypeConverter } from "./inline-object-extractor.js";
|
|
30
|
+
export { toPosixPath } from "./path-utils.js";
|
|
31
|
+
export type { SourceLocation } from "./source-location.js";
|
|
32
|
+
export { convertTsTypeToGraphQLType } from "./type-converter.js";
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type ts from "typescript";
|
|
2
|
+
import type {
|
|
3
|
+
InlineObjectPropertyDef,
|
|
4
|
+
TSTypeReference,
|
|
5
|
+
} from "../type-extractor/types/index.js";
|
|
6
|
+
import { detectDefaultValueMetadata } from "./default-value-detector.js";
|
|
7
|
+
import {
|
|
8
|
+
type DirectiveArgumentValue,
|
|
9
|
+
type DirectiveInfo,
|
|
10
|
+
detectDirectiveMetadata,
|
|
11
|
+
hasDirectiveMetadata,
|
|
12
|
+
unwrapDirectiveType,
|
|
13
|
+
} from "./directive-detector.js";
|
|
14
|
+
import { getSourceLocationFromNode } from "./source-location.js";
|
|
15
|
+
import { extractTsDocFromSymbol } from "./tsdoc-parser.js";
|
|
16
|
+
import {
|
|
17
|
+
extractPropertySymbols,
|
|
18
|
+
hasUndefinedInType,
|
|
19
|
+
isNullableUnion,
|
|
20
|
+
} from "./typescript-utils.js";
|
|
21
|
+
|
|
22
|
+
export type TypeConverter = (
|
|
23
|
+
type: ts.Type,
|
|
24
|
+
checker: ts.TypeChecker,
|
|
25
|
+
) => TSTypeReference;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Extracts inline object properties from a TypeScript type.
|
|
29
|
+
*
|
|
30
|
+
* @param type - The TypeScript type to extract properties from
|
|
31
|
+
* @param checker - The TypeScript type checker
|
|
32
|
+
* @param convertType - A function to convert TypeScript types to TSTypeReference
|
|
33
|
+
* @returns An array of inline object property definitions
|
|
34
|
+
*/
|
|
35
|
+
export function extractInlineObjectProperties(
|
|
36
|
+
type: ts.Type,
|
|
37
|
+
checker: ts.TypeChecker,
|
|
38
|
+
convertType: TypeConverter,
|
|
39
|
+
): InlineObjectPropertyDef[] {
|
|
40
|
+
const properties: InlineObjectPropertyDef[] = [];
|
|
41
|
+
const typeProperties = extractPropertySymbols(type, checker);
|
|
42
|
+
|
|
43
|
+
for (const prop of typeProperties) {
|
|
44
|
+
const propName = prop.getName();
|
|
45
|
+
if (propName.startsWith(" $")) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const propType = checker.getTypeOfSymbol(prop);
|
|
50
|
+
const declarations = prop.getDeclarations();
|
|
51
|
+
const declaration = declarations?.[0];
|
|
52
|
+
|
|
53
|
+
const optional = hasUndefinedInType(propType);
|
|
54
|
+
|
|
55
|
+
const tsdocInfo = extractTsDocFromSymbol(prop, checker);
|
|
56
|
+
|
|
57
|
+
let actualPropType = propType;
|
|
58
|
+
let directives: ReadonlyArray<DirectiveInfo> | null = null;
|
|
59
|
+
let directiveNullable = false;
|
|
60
|
+
let defaultValue: DirectiveArgumentValue | null = null;
|
|
61
|
+
|
|
62
|
+
if (hasDirectiveMetadata(propType)) {
|
|
63
|
+
const directiveResult = detectDirectiveMetadata(propType, checker);
|
|
64
|
+
if (directiveResult.directives.length > 0) {
|
|
65
|
+
directives = directiveResult.directives;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const defaultValueResult = detectDefaultValueMetadata(propType, checker);
|
|
69
|
+
if (defaultValueResult.defaultValue) {
|
|
70
|
+
defaultValue = defaultValueResult.defaultValue;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (isNullableUnion(propType)) {
|
|
74
|
+
directiveNullable = true;
|
|
75
|
+
}
|
|
76
|
+
actualPropType = unwrapDirectiveType(propType, checker);
|
|
77
|
+
|
|
78
|
+
if (!directiveNullable && isNullableUnion(actualPropType)) {
|
|
79
|
+
directiveNullable = true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const tsType = convertType(actualPropType, checker);
|
|
84
|
+
const finalTsType =
|
|
85
|
+
directiveNullable && !tsType.nullable
|
|
86
|
+
? { ...tsType, nullable: true }
|
|
87
|
+
: tsType;
|
|
88
|
+
|
|
89
|
+
properties.push({
|
|
90
|
+
name: propName,
|
|
91
|
+
tsType: finalTsType,
|
|
92
|
+
optional,
|
|
93
|
+
description: tsdocInfo.description ?? null,
|
|
94
|
+
deprecated: tsdocInfo.deprecated ?? null,
|
|
95
|
+
directives,
|
|
96
|
+
defaultValue,
|
|
97
|
+
sourceLocation: getSourceLocationFromNode(declaration),
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return properties;
|
|
102
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Checks if a TypeScript type is an inline object type (anonymous type literal).
|
|
5
|
+
* Returns true for types like `{ foo: string; bar: number }` but not for named types.
|
|
6
|
+
*/
|
|
7
|
+
export function isInlineObjectType(type: ts.Type): boolean {
|
|
8
|
+
if (type.aliasSymbol) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
if (!type.symbol) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
const symbolName = type.symbol.getName();
|
|
15
|
+
if (symbolName !== "__type") {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
if (!(type.flags & ts.TypeFlags.Object)) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
const objectType = type as ts.ObjectType;
|
|
22
|
+
return (objectType.objectFlags & ts.ObjectFlags.Anonymous) !== 0;
|
|
23
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interface metadata detector.
|
|
3
|
+
*
|
|
4
|
+
* This module provides functions to detect interface metadata embedded
|
|
5
|
+
* in TypeScript intersection types using the $gqlkitInterfaceMeta property,
|
|
6
|
+
* and to extract implements declarations from types using $gqlkitTypeMeta.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import ts from "typescript";
|
|
10
|
+
import { METADATA_PROPERTIES } from "./constants.js";
|
|
11
|
+
|
|
12
|
+
const INTERFACE_META_PROPERTY = METADATA_PROPERTIES.INTERFACE_META;
|
|
13
|
+
const TYPE_META_PROPERTY = METADATA_PROPERTIES.TYPE_META;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Checks if a type alias declaration uses GqlInterface.
|
|
17
|
+
* Detects by checking for the $gqlkitInterfaceMeta property in the resolved type.
|
|
18
|
+
*/
|
|
19
|
+
export function isDefineInterfaceTypeAlias(
|
|
20
|
+
node: ts.TypeAliasDeclaration,
|
|
21
|
+
checker: ts.TypeChecker,
|
|
22
|
+
): boolean {
|
|
23
|
+
const symbol = checker.getSymbolAtLocation(node.name);
|
|
24
|
+
if (!symbol) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const type = checker.getDeclaredTypeOfSymbol(symbol);
|
|
29
|
+
return hasInterfaceMetadata(type);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Checks if a type has the $gqlkitInterfaceMeta property.
|
|
34
|
+
*/
|
|
35
|
+
function hasInterfaceMetadata(type: ts.Type): boolean {
|
|
36
|
+
const metaProp = type.getProperty(INTERFACE_META_PROPERTY);
|
|
37
|
+
if (metaProp) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (type.isIntersection()) {
|
|
42
|
+
for (const member of type.types) {
|
|
43
|
+
const prop = member.getProperty(INTERFACE_META_PROPERTY);
|
|
44
|
+
if (prop) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Checks if a type has the $gqlkitTypeMeta property (GqlObject).
|
|
55
|
+
*/
|
|
56
|
+
function hasTypeMetadata(type: ts.Type): boolean {
|
|
57
|
+
const metaProp = type.getProperty(TYPE_META_PROPERTY);
|
|
58
|
+
if (metaProp) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (type.isIntersection()) {
|
|
63
|
+
for (const member of type.types) {
|
|
64
|
+
const prop = member.getProperty(TYPE_META_PROPERTY);
|
|
65
|
+
if (prop) {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Extracts implements from GqlInterface type alias.
|
|
76
|
+
*/
|
|
77
|
+
export function extractImplementsFromDefineInterface(
|
|
78
|
+
node: ts.TypeAliasDeclaration,
|
|
79
|
+
sourceFile: ts.SourceFile,
|
|
80
|
+
checker: ts.TypeChecker,
|
|
81
|
+
): ReadonlyArray<string> {
|
|
82
|
+
const typeNode = node.type;
|
|
83
|
+
if (!ts.isTypeReferenceNode(typeNode)) {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const typeArgs = typeNode.typeArguments;
|
|
88
|
+
if (!typeArgs || typeArgs.length < 2) {
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const metaArg = typeArgs[1];
|
|
93
|
+
if (!metaArg || !ts.isTypeLiteralNode(metaArg)) {
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return extractImplementsFromTypeLiteral(metaArg, sourceFile, checker);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Extracts implements from GqlObject type alias.
|
|
102
|
+
* Detects GqlObject by checking for the $gqlkitTypeMeta property in the resolved type.
|
|
103
|
+
*/
|
|
104
|
+
export function extractImplementsFromGqlTypeDef(
|
|
105
|
+
node: ts.TypeAliasDeclaration,
|
|
106
|
+
sourceFile: ts.SourceFile,
|
|
107
|
+
checker: ts.TypeChecker,
|
|
108
|
+
): ReadonlyArray<string> {
|
|
109
|
+
const symbol = checker.getSymbolAtLocation(node.name);
|
|
110
|
+
if (!symbol) {
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const type = checker.getDeclaredTypeOfSymbol(symbol);
|
|
115
|
+
if (!hasTypeMetadata(type)) {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const typeNode = node.type;
|
|
120
|
+
if (!ts.isTypeReferenceNode(typeNode)) {
|
|
121
|
+
return [];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const typeArgs = typeNode.typeArguments;
|
|
125
|
+
if (!typeArgs || typeArgs.length < 2) {
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const metaArg = typeArgs[1];
|
|
130
|
+
if (!metaArg || !ts.isTypeLiteralNode(metaArg)) {
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return extractImplementsFromTypeLiteral(metaArg, sourceFile, checker);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function extractImplementsFromTypeLiteral(
|
|
138
|
+
typeLiteral: ts.TypeLiteralNode,
|
|
139
|
+
sourceFile: ts.SourceFile,
|
|
140
|
+
_checker: ts.TypeChecker,
|
|
141
|
+
): ReadonlyArray<string> {
|
|
142
|
+
for (const member of typeLiteral.members) {
|
|
143
|
+
if (ts.isPropertySignature(member) && member.name) {
|
|
144
|
+
const propName = member.name.getText(sourceFile);
|
|
145
|
+
if (propName === "implements" && member.type) {
|
|
146
|
+
return extractInterfaceNamesFromTupleNode(member.type, sourceFile);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return [];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function extractInterfaceNamesFromTupleNode(
|
|
154
|
+
typeNode: ts.TypeNode,
|
|
155
|
+
sourceFile: ts.SourceFile,
|
|
156
|
+
): ReadonlyArray<string> {
|
|
157
|
+
if (ts.isTupleTypeNode(typeNode)) {
|
|
158
|
+
return typeNode.elements
|
|
159
|
+
.map((elem) => {
|
|
160
|
+
if (ts.isTypeReferenceNode(elem)) {
|
|
161
|
+
return elem.typeName.getText(sourceFile);
|
|
162
|
+
}
|
|
163
|
+
return null;
|
|
164
|
+
})
|
|
165
|
+
.filter((name): name is string => name !== null);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (
|
|
169
|
+
ts.isArrayTypeNode(typeNode) &&
|
|
170
|
+
ts.isTypeReferenceNode(typeNode.elementType)
|
|
171
|
+
) {
|
|
172
|
+
return [typeNode.elementType.typeName.getText(sourceFile)];
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return [];
|
|
176
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import type { GraphQLTypeInfo } from "../type-extractor/types/graphql.js";
|
|
2
|
+
import type { Diagnostic } from "../type-extractor/types/index.js";
|
|
3
|
+
|
|
4
|
+
export interface InterfaceValidationResult {
|
|
5
|
+
readonly isValid: boolean;
|
|
6
|
+
readonly diagnostics: ReadonlyArray<Diagnostic>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function validateInterfaceImplementations(
|
|
10
|
+
types: ReadonlyArray<GraphQLTypeInfo>,
|
|
11
|
+
): InterfaceValidationResult {
|
|
12
|
+
const diagnostics: Diagnostic[] = [];
|
|
13
|
+
const typeMap = new Map<string, GraphQLTypeInfo>();
|
|
14
|
+
|
|
15
|
+
for (const type of types) {
|
|
16
|
+
typeMap.set(type.name, type);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
for (const type of types) {
|
|
20
|
+
if (
|
|
21
|
+
!type.implementedInterfaces ||
|
|
22
|
+
type.implementedInterfaces.length === 0
|
|
23
|
+
) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
for (const interfaceName of type.implementedInterfaces) {
|
|
28
|
+
const interfaceType = typeMap.get(interfaceName);
|
|
29
|
+
|
|
30
|
+
if (!interfaceType) {
|
|
31
|
+
diagnostics.push({
|
|
32
|
+
code: "INTERFACE_NOT_FOUND",
|
|
33
|
+
message: `Type '${type.name}' implements unknown interface '${interfaceName}'`,
|
|
34
|
+
severity: "error",
|
|
35
|
+
location: {
|
|
36
|
+
file: type.sourceFile,
|
|
37
|
+
line: 1,
|
|
38
|
+
column: 1,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (interfaceType.kind !== "Interface") {
|
|
45
|
+
diagnostics.push({
|
|
46
|
+
code: "INTERFACE_NOT_INTERFACE",
|
|
47
|
+
message: `Type '${type.name}' attempts to implement '${interfaceName}' which is not an interface`,
|
|
48
|
+
severity: "error",
|
|
49
|
+
location: {
|
|
50
|
+
file: type.sourceFile,
|
|
51
|
+
line: 1,
|
|
52
|
+
column: 1,
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!interfaceType.fields) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const typeFields = new Map(type.fields?.map((f) => [f.name, f]) ?? []);
|
|
63
|
+
|
|
64
|
+
for (const interfaceField of interfaceType.fields) {
|
|
65
|
+
const typeField = typeFields.get(interfaceField.name);
|
|
66
|
+
|
|
67
|
+
if (!typeField) {
|
|
68
|
+
diagnostics.push({
|
|
69
|
+
code: "INTERFACE_MISSING_FIELD",
|
|
70
|
+
message: `Type '${type.name}' implements interface '${interfaceName}' but is missing field '${interfaceField.name}'`,
|
|
71
|
+
severity: "error",
|
|
72
|
+
location: {
|
|
73
|
+
file: type.sourceFile,
|
|
74
|
+
line: 1,
|
|
75
|
+
column: 1,
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (!isTypeCompatible(typeField.type, interfaceField.type)) {
|
|
82
|
+
diagnostics.push({
|
|
83
|
+
code: "INTERFACE_FIELD_TYPE_MISMATCH",
|
|
84
|
+
message: `Type '${type.name}' field '${interfaceField.name}' has incompatible type with interface '${interfaceName}'`,
|
|
85
|
+
severity: "error",
|
|
86
|
+
location: {
|
|
87
|
+
file: type.sourceFile,
|
|
88
|
+
line: 1,
|
|
89
|
+
column: 1,
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
isValid: diagnostics.length === 0,
|
|
99
|
+
diagnostics,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
interface FieldType {
|
|
104
|
+
readonly typeName: string;
|
|
105
|
+
readonly nullable: boolean;
|
|
106
|
+
readonly list: boolean;
|
|
107
|
+
readonly listItemNullable?: boolean | null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function isTypeCompatible(
|
|
111
|
+
implementingType: FieldType,
|
|
112
|
+
interfaceType: FieldType,
|
|
113
|
+
): boolean {
|
|
114
|
+
if (implementingType.typeName !== interfaceType.typeName) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (implementingType.list !== interfaceType.list) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (implementingType.nullable && !interfaceType.nullable) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (implementingType.list) {
|
|
127
|
+
if (implementingType.listItemNullable && !interfaceType.listItemNullable) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function detectCircularInterfaceReferences(
|
|
136
|
+
types: ReadonlyArray<GraphQLTypeInfo>,
|
|
137
|
+
): InterfaceValidationResult {
|
|
138
|
+
const diagnostics: Diagnostic[] = [];
|
|
139
|
+
const typeMap = new Map<string, GraphQLTypeInfo>();
|
|
140
|
+
const reportedCycles = new Set<string>();
|
|
141
|
+
|
|
142
|
+
for (const type of types) {
|
|
143
|
+
if (type.kind === "Interface") {
|
|
144
|
+
typeMap.set(type.name, type);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
for (const type of types) {
|
|
149
|
+
if (type.kind !== "Interface" || !type.implementedInterfaces) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const cyclePath = findCircularReference(type.name, typeMap, new Set(), []);
|
|
154
|
+
if (cyclePath) {
|
|
155
|
+
const cycleStart = cyclePath.indexOf(cyclePath[cyclePath.length - 1]!);
|
|
156
|
+
const cycleMembers = cyclePath.slice(cycleStart, -1);
|
|
157
|
+
const cycleKey = [...cycleMembers].sort().join(",");
|
|
158
|
+
if (!reportedCycles.has(cycleKey)) {
|
|
159
|
+
reportedCycles.add(cycleKey);
|
|
160
|
+
diagnostics.push({
|
|
161
|
+
code: "INTERFACE_CIRCULAR_REFERENCE",
|
|
162
|
+
message: `Circular interface inheritance detected: ${cyclePath.join(" -> ")}`,
|
|
163
|
+
severity: "error",
|
|
164
|
+
location: {
|
|
165
|
+
file: type.sourceFile,
|
|
166
|
+
line: 1,
|
|
167
|
+
column: 1,
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
isValid: diagnostics.length === 0,
|
|
176
|
+
diagnostics,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function findCircularReference(
|
|
181
|
+
typeName: string,
|
|
182
|
+
typeMap: Map<string, GraphQLTypeInfo>,
|
|
183
|
+
visited: Set<string>,
|
|
184
|
+
path: string[],
|
|
185
|
+
): string[] | null {
|
|
186
|
+
if (visited.has(typeName)) {
|
|
187
|
+
return [...path, typeName];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
visited.add(typeName);
|
|
191
|
+
path.push(typeName);
|
|
192
|
+
|
|
193
|
+
const type = typeMap.get(typeName);
|
|
194
|
+
if (!type || !type.implementedInterfaces) {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
for (const interfaceName of type.implementedInterfaces) {
|
|
199
|
+
const result = findCircularReference(
|
|
200
|
+
interfaceName,
|
|
201
|
+
typeMap,
|
|
202
|
+
new Set(visited),
|
|
203
|
+
[...path],
|
|
204
|
+
);
|
|
205
|
+
if (result) {
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return null;
|
|
211
|
+
}
|