@soda-gql/tools 0.13.1 → 0.13.2

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/dist/bin.cjs CHANGED
@@ -37,11 +37,9 @@ let graphql = require("graphql");
37
37
  let node_fs = require("node:fs");
38
38
  let esbuild = require("esbuild");
39
39
  let node_crypto = require("node:crypto");
40
+ let __soda_gql_common = require("@soda-gql/common");
40
41
  let fast_glob = require("fast-glob");
41
42
  fast_glob = __toESM(fast_glob);
42
- let __soda_gql_common_template_extraction = require("@soda-gql/common/template-extraction");
43
- let __swc_core = require("@swc/core");
44
- let __soda_gql_common = require("@soda-gql/common");
45
43
  let zod = require("zod");
46
44
 
47
45
  //#region packages/tools/src/cli/errors.ts
@@ -3924,7 +3922,7 @@ const collectFragmentDependencies = (selections) => {
3924
3922
  * Map operation kind to root type name.
3925
3923
  * Uses schema.operationTypes if available, falls back to standard names.
3926
3924
  */
3927
- const getRootTypeName$1 = (schema, kind) => {
3925
+ const getRootTypeName = (schema, kind) => {
3928
3926
  if (schema) {
3929
3927
  switch (kind) {
3930
3928
  case "query": return schema.operationTypes.query ?? "Query";
@@ -3952,7 +3950,7 @@ const emitOperation = (operation, options) => {
3952
3950
  if (operation.variables.length > 0) {
3953
3951
  lines.push(` variables: { ${emitVariables(operation.variables)} },`);
3954
3952
  }
3955
- const rootTypeName = getRootTypeName$1(schema, operation.kind);
3953
+ const rootTypeName = getRootTypeName(schema, operation.kind);
3956
3954
  lines.push(` fields: ({ f, $ }) => ({`);
3957
3955
  const fieldLinesResult = emitSelections(operation.selections, 3, operation.variables, schema, rootTypeName);
3958
3956
  if (fieldLinesResult.isErr()) {
@@ -5450,313 +5448,6 @@ const formatTypegenError = (error) => {
5450
5448
  return lines.join("\n");
5451
5449
  };
5452
5450
 
5453
- //#endregion
5454
- //#region packages/tools/src/typegen/template-extractor.ts
5455
- /**
5456
- * Parse TypeScript source with SWC, returning null on failure.
5457
- */
5458
- const safeParseSync = (source, tsx) => {
5459
- try {
5460
- return (0, __swc_core.parseSync)(source, {
5461
- syntax: "typescript",
5462
- tsx,
5463
- decorators: false,
5464
- dynamicImport: true
5465
- });
5466
- } catch {
5467
- return null;
5468
- }
5469
- };
5470
- /**
5471
- * Collect gql identifiers from import declarations.
5472
- * Finds imports like `import { gql } from "./graphql-system"`.
5473
- */
5474
- const collectGqlIdentifiers = (module$1, filePath, helper) => {
5475
- const identifiers = new Set();
5476
- for (const item of module$1.body) {
5477
- let declaration = null;
5478
- if (item.type === "ImportDeclaration") {
5479
- declaration = item;
5480
- } else if ("declaration" in item && item.declaration && item.declaration.type === "ImportDeclaration") {
5481
- declaration = item.declaration;
5482
- }
5483
- if (!declaration) {
5484
- continue;
5485
- }
5486
- if (!helper.isGraphqlSystemImportSpecifier({
5487
- filePath,
5488
- specifier: declaration.source.value
5489
- })) {
5490
- continue;
5491
- }
5492
- for (const specifier of declaration.specifiers ?? []) {
5493
- if (specifier.type === "ImportSpecifier") {
5494
- const imported = specifier.imported ? specifier.imported.value : specifier.local.value;
5495
- if (imported === "gql" && !specifier.imported) {
5496
- identifiers.add(specifier.local.value);
5497
- }
5498
- }
5499
- }
5500
- }
5501
- return identifiers;
5502
- };
5503
- /**
5504
- * Extract all tagged templates from a TypeScript source file.
5505
- *
5506
- * @param filePath - Absolute path to the source file (used for import resolution)
5507
- * @param source - TypeScript source code
5508
- * @param helper - GraphQL system identifier for resolving gql imports
5509
- * @returns Extracted templates and any warnings
5510
- */
5511
- const extractTemplatesFromSource = (filePath, source, helper) => {
5512
- const warnings = [];
5513
- const isTsx = filePath.endsWith(".tsx");
5514
- const program = safeParseSync(source, isTsx);
5515
- if (!program || program.type !== "Module") {
5516
- if (source.includes("gql")) {
5517
- warnings.push(`[typegen-extract] Failed to parse ${filePath}`);
5518
- }
5519
- return {
5520
- templates: [],
5521
- warnings
5522
- };
5523
- }
5524
- const gqlIdentifiers = collectGqlIdentifiers(program, filePath, helper);
5525
- if (gqlIdentifiers.size === 0) {
5526
- return {
5527
- templates: [],
5528
- warnings
5529
- };
5530
- }
5531
- return {
5532
- templates: (0, __soda_gql_common_template_extraction.walkAndExtract)(program, gqlIdentifiers),
5533
- warnings
5534
- };
5535
- };
5536
-
5537
- //#endregion
5538
- //#region packages/tools/src/typegen/template-scanner.ts
5539
- /**
5540
- * Source file scanner for tagged template extraction.
5541
- *
5542
- * Discovers source files from config include/exclude patterns,
5543
- * reads them, and extracts tagged templates using the template extractor.
5544
- *
5545
- * @module
5546
- */
5547
- /**
5548
- * Scan source files for tagged templates.
5549
- *
5550
- * Uses fast-glob to discover files matching include/exclude patterns,
5551
- * then extracts tagged templates from each file.
5552
- */
5553
- const scanSourceFiles = (options) => {
5554
- const { include, exclude, baseDir, helper } = options;
5555
- const warnings = [];
5556
- const ignorePatterns = exclude.map((pattern) => pattern.startsWith("!") ? pattern.slice(1) : pattern);
5557
- const matchedFiles = fast_glob.default.sync(include, {
5558
- cwd: baseDir,
5559
- ignore: ignorePatterns,
5560
- onlyFiles: true,
5561
- absolute: true
5562
- });
5563
- const templates = new Map();
5564
- for (const filePath of matchedFiles) {
5565
- const normalizedPath = (0, node_path.normalize)((0, node_path.resolve)(filePath)).replace(/\\/g, "/");
5566
- try {
5567
- const source = (0, node_fs.readFileSync)(normalizedPath, "utf-8");
5568
- const { templates: extracted, warnings: extractionWarnings } = extractTemplatesFromSource(normalizedPath, source, helper);
5569
- warnings.push(...extractionWarnings);
5570
- if (extracted.length > 0) {
5571
- templates.set(normalizedPath, extracted);
5572
- }
5573
- } catch (error) {
5574
- const message = error instanceof Error ? error.message : String(error);
5575
- warnings.push(`[typegen-scan] Failed to read ${normalizedPath}: ${message}`);
5576
- }
5577
- }
5578
- return {
5579
- templates,
5580
- warnings
5581
- };
5582
- };
5583
-
5584
- //#endregion
5585
- //#region packages/tools/src/typegen/template-to-selections.ts
5586
- /**
5587
- * Convert extracted templates into field selections for the emitter.
5588
- *
5589
- * @param templates - Templates extracted from source files, keyed by file path
5590
- * @param schemas - Loaded schema objects keyed by schema name
5591
- * @returns Map of canonical IDs to field selection data, plus any warnings
5592
- */
5593
- const convertTemplatesToSelections = (templates, schemas) => {
5594
- const selections = new Map();
5595
- const warnings = [];
5596
- const schemaIndexes = new Map(Object.entries(schemas).map(([name, schema]) => [name, (0, __soda_gql_core.createSchemaIndexFromSchema)(schema)]));
5597
- for (const [filePath, fileTemplates] of templates) {
5598
- for (const template of fileTemplates) {
5599
- const schema = schemas[template.schemaName];
5600
- if (!schema) {
5601
- warnings.push(`[typegen-template] Unknown schema "${template.schemaName}" in ${filePath}`);
5602
- continue;
5603
- }
5604
- const schemaIndex = schemaIndexes.get(template.schemaName);
5605
- if (!schemaIndex) {
5606
- continue;
5607
- }
5608
- try {
5609
- if (template.kind === "fragment") {
5610
- const selection = convertFragmentTemplate(template, schema, filePath);
5611
- if (selection) {
5612
- selections.set(selection.id, selection.data);
5613
- }
5614
- } else {
5615
- const selection = convertOperationTemplate(template, schema, filePath);
5616
- if (selection) {
5617
- selections.set(selection.id, selection.data);
5618
- }
5619
- }
5620
- } catch (error) {
5621
- const message = error instanceof Error ? error.message : String(error);
5622
- warnings.push(`[typegen-template] Failed to process ${template.kind} in ${filePath}: ${message}`);
5623
- }
5624
- }
5625
- }
5626
- return {
5627
- selections,
5628
- warnings
5629
- };
5630
- };
5631
- /**
5632
- * Recursively filter out __FRAG_SPREAD_ placeholder nodes from a selection set.
5633
- * These placeholders are created by template-extractor for interpolated fragment references.
5634
- * buildFieldsFromSelectionSet would throw on them since no interpolationMap is available.
5635
- */
5636
- const filterPlaceholderSpreads = (selectionSet) => ({
5637
- ...selectionSet,
5638
- selections: selectionSet.selections.filter((sel) => !(sel.kind === graphql.Kind.FRAGMENT_SPREAD && sel.name.value.startsWith("__FRAG_SPREAD_"))).map((sel) => {
5639
- if (sel.kind === graphql.Kind.FIELD && sel.selectionSet) {
5640
- return {
5641
- ...sel,
5642
- selectionSet: filterPlaceholderSpreads(sel.selectionSet)
5643
- };
5644
- }
5645
- if (sel.kind === graphql.Kind.INLINE_FRAGMENT && sel.selectionSet) {
5646
- return {
5647
- ...sel,
5648
- selectionSet: filterPlaceholderSpreads(sel.selectionSet)
5649
- };
5650
- }
5651
- return sel;
5652
- })
5653
- });
5654
- /** Simple matching-paren finder for template content (no comments/strings to handle). */
5655
- const findClosingParen = (source, openIndex) => {
5656
- let depth$3 = 0;
5657
- for (let i = openIndex; i < source.length; i++) {
5658
- if (source[i] === "(") depth$3++;
5659
- else if (source[i] === ")") {
5660
- depth$3--;
5661
- if (depth$3 === 0) return i;
5662
- }
5663
- }
5664
- return -1;
5665
- };
5666
- /**
5667
- * Reconstruct full GraphQL source from an extracted template.
5668
- * For curried syntax (new), prepends the definition header from tag call arguments.
5669
- * For curried fragments with Fragment Arguments, repositions variable declarations
5670
- * before the on-clause to produce RFC-compliant syntax.
5671
- * For old syntax, returns content as-is.
5672
- */
5673
- const reconstructGraphql = (template) => {
5674
- if (template.elementName) {
5675
- if (template.kind === "fragment" && template.typeName) {
5676
- const trimmed = template.content.trim();
5677
- if (trimmed.startsWith("(")) {
5678
- const closeIdx = findClosingParen(trimmed, 0);
5679
- if (closeIdx !== -1) {
5680
- const varDecls = trimmed.slice(0, closeIdx + 1);
5681
- const selectionSet = trimmed.slice(closeIdx + 1).trim();
5682
- return `fragment ${template.elementName}${varDecls} on ${template.typeName} ${selectionSet}`;
5683
- }
5684
- }
5685
- return `fragment ${template.elementName} on ${template.typeName} ${trimmed}`;
5686
- }
5687
- return `${template.kind} ${template.elementName} ${template.content}`;
5688
- }
5689
- return template.content;
5690
- };
5691
- /**
5692
- * Convert a fragment template into FieldSelectionData.
5693
- */
5694
- const convertFragmentTemplate = (template, schema, filePath) => {
5695
- const schemaIndex = (0, __soda_gql_core.createSchemaIndexFromSchema)(schema);
5696
- const graphqlSource = reconstructGraphql(template);
5697
- const variableDefinitions = (0, __soda_gql_core.extractFragmentVariables)(graphqlSource, schemaIndex);
5698
- const { preprocessed } = (0, __soda_gql_core.preprocessFragmentArgs)(graphqlSource);
5699
- const document = (0, graphql.parse)(preprocessed);
5700
- const fragDef = document.definitions.find((d) => d.kind === graphql.Kind.FRAGMENT_DEFINITION);
5701
- if (!fragDef || fragDef.kind !== graphql.Kind.FRAGMENT_DEFINITION) {
5702
- return null;
5703
- }
5704
- const fragmentName = fragDef.name.value;
5705
- const onType = fragDef.typeCondition.name.value;
5706
- const fields = (0, __soda_gql_core.buildFieldsFromSelectionSet)(filterPlaceholderSpreads(fragDef.selectionSet), schema, onType);
5707
- const id = `${filePath}::${fragmentName}`;
5708
- return {
5709
- id,
5710
- data: {
5711
- type: "fragment",
5712
- schemaLabel: schema.label,
5713
- key: fragmentName,
5714
- typename: onType,
5715
- fields,
5716
- variableDefinitions
5717
- }
5718
- };
5719
- };
5720
- /**
5721
- * Convert an operation template into FieldSelectionData.
5722
- */
5723
- const convertOperationTemplate = (template, schema, filePath) => {
5724
- const graphqlSource = reconstructGraphql(template);
5725
- const document = (0, graphql.parse)(graphqlSource);
5726
- const opDef = document.definitions.find((d) => d.kind === graphql.Kind.OPERATION_DEFINITION);
5727
- if (!opDef || opDef.kind !== graphql.Kind.OPERATION_DEFINITION) {
5728
- return null;
5729
- }
5730
- const operationName = opDef.name?.value ?? "Anonymous";
5731
- const operationType = opDef.operation;
5732
- const rootTypeName = getRootTypeName(schema, operationType);
5733
- const fields = (0, __soda_gql_core.buildFieldsFromSelectionSet)(filterPlaceholderSpreads(opDef.selectionSet), schema, rootTypeName);
5734
- const variableDefinitions = opDef.variableDefinitions ?? [];
5735
- const id = `${filePath}::${operationName}`;
5736
- return {
5737
- id,
5738
- data: {
5739
- type: "operation",
5740
- schemaLabel: schema.label,
5741
- operationName,
5742
- operationType,
5743
- fields,
5744
- variableDefinitions: [...variableDefinitions]
5745
- }
5746
- };
5747
- };
5748
- /**
5749
- * Get the root type name for an operation type from the schema.
5750
- */
5751
- const getRootTypeName = (schema, operationType) => {
5752
- switch (operationType) {
5753
- case "query": return schema.operations.query ?? "Query";
5754
- case "mutation": return schema.operations.mutation ?? "Mutation";
5755
- case "subscription": return schema.operations.subscription ?? "Subscription";
5756
- default: return "Query";
5757
- }
5758
- };
5759
-
5760
5451
  //#endregion
5761
5452
  //#region packages/tools/src/typegen/runner.ts
5762
5453
  /**
@@ -5766,8 +5457,7 @@ const getRootTypeName = (schema, operationType) => {
5766
5457
  * 1. Load schemas from generated CJS bundle
5767
5458
  * 2. Build artifact to evaluate elements
5768
5459
  * 3. Extract field selections from builder
5769
- * 4. Scan source files for tagged templates and merge selections
5770
- * 5. Emit types.prebuilt.ts
5460
+ * 4. Emit types.prebuilt.ts
5771
5461
  *
5772
5462
  * @module
5773
5463
  */
@@ -5815,8 +5505,7 @@ const toImportSpecifier = (fromPath, targetPath, options) => {
5815
5505
  * 1. Loads schemas from the generated CJS bundle
5816
5506
  * 2. Creates a BuilderService and builds the artifact
5817
5507
  * 3. Extracts field selections from the artifact
5818
- * 4. Scans source files for tagged templates and merges selections
5819
- * 5. Emits types.prebuilt.ts using emitPrebuiltTypes
5508
+ * 4. Emits types.prebuilt.ts using emitPrebuiltTypes
5820
5509
  *
5821
5510
  * @param options - Typegen options including config
5822
5511
  * @returns Result containing success data or error
@@ -5848,16 +5537,7 @@ const runTypegen = async (options) => {
5848
5537
  }
5849
5538
  const fieldSelectionsResult = (0, __soda_gql_builder.extractFieldSelections)(intermediateElements);
5850
5539
  const { selections: builderSelections, warnings: extractWarnings } = fieldSelectionsResult;
5851
- const graphqlHelper = (0, __soda_gql_builder.createGraphqlSystemIdentifyHelper)(config);
5852
- const scanResult = scanSourceFiles({
5853
- include: [...config.include],
5854
- exclude: [...config.exclude],
5855
- baseDir: config.baseDir,
5856
- helper: graphqlHelper
5857
- });
5858
- const templateSelections = convertTemplatesToSelections(scanResult.templates, schemas);
5859
- const fieldSelections = mergeSelections(builderSelections, templateSelections.selections, config.baseDir);
5860
- const scanWarnings = [...scanResult.warnings, ...templateSelections.warnings];
5540
+ const fieldSelections = new Map(builderSelections);
5861
5541
  const emitResult = await emitPrebuiltTypes({
5862
5542
  schemas,
5863
5543
  fieldSelections,
@@ -5877,11 +5557,7 @@ const runTypegen = async (options) => {
5877
5557
  operationCount++;
5878
5558
  }
5879
5559
  }
5880
- const allWarnings = [
5881
- ...extractWarnings,
5882
- ...scanWarnings,
5883
- ...emitWarnings
5884
- ];
5560
+ const allWarnings = [...extractWarnings, ...emitWarnings];
5885
5561
  return (0, neverthrow.ok)({
5886
5562
  prebuiltTypesPath,
5887
5563
  fragmentCount,
@@ -5890,41 +5566,6 @@ const runTypegen = async (options) => {
5890
5566
  warnings: allWarnings
5891
5567
  });
5892
5568
  };
5893
- const extractElementName = (data) => data.type === "fragment" ? data.key : data.operationName;
5894
- /**
5895
- * Merge builder and template selections into a combined map.
5896
- *
5897
- * Builder selections are authoritative — VM evaluation correctly resolves
5898
- * fragment spreads that static template analysis cannot handle.
5899
- * Template selections serve as fallback for elements only found by the scanner.
5900
- *
5901
- * Deduplication is per-element (file + GraphQL name), not per-file, so that
5902
- * callback-builder operations in files that also contain tagged templates are preserved.
5903
- */
5904
- const mergeSelections = (builderSelections, templateSelections, baseDir) => {
5905
- const extractFilePart = (id) => {
5906
- const filePart = id.split("::")[0] ?? "";
5907
- if (filePart.startsWith("/")) {
5908
- return (0, node_path.relative)(baseDir, filePart);
5909
- }
5910
- return filePart;
5911
- };
5912
- const builderElements = new Set();
5913
- for (const [id, data] of builderSelections) {
5914
- const name = extractElementName(data);
5915
- if (name) builderElements.add(`${extractFilePart(id)}::${name}`);
5916
- }
5917
- const fieldSelections = new Map();
5918
- for (const [id, data] of builderSelections) {
5919
- fieldSelections.set(id, data);
5920
- }
5921
- for (const [id, data] of templateSelections) {
5922
- const name = extractElementName(data);
5923
- if (name && builderElements.has(`${extractFilePart(id)}::${name}`)) continue;
5924
- fieldSelections.set(id, data);
5925
- }
5926
- return fieldSelections;
5927
- };
5928
5569
 
5929
5570
  //#endregion
5930
5571
  //#region packages/tools/src/cli/schemas/args.ts
@@ -7136,7 +6777,7 @@ const doctorCommand = (argv) => {
7136
6777
  //#region packages/tools/src/cli/commands/format.ts
7137
6778
  const loadFormatter = async () => {
7138
6779
  try {
7139
- return await Promise.resolve().then(() => require("./formatter-Glj5a663.cjs"));
6780
+ return await Promise.resolve().then(() => require("./formatter-D5rL2R7k.cjs"));
7140
6781
  } catch {
7141
6782
  return null;
7142
6783
  }
@@ -9603,9 +9244,9 @@ var require_readdirp = /* @__PURE__ */ __commonJSMin(((exports, module) => {
9603
9244
  return new ReaddirpStream(options);
9604
9245
  };
9605
9246
  const readdirpPromise = (root, options = {}) => {
9606
- return new Promise((resolve$14, reject) => {
9247
+ return new Promise((resolve$13, reject) => {
9607
9248
  const files = [];
9608
- readdirp$1(root, options).on("data", (entry) => files.push(entry)).on("end", () => resolve$14(files)).on("error", (error) => reject(error));
9249
+ readdirp$1(root, options).on("data", (entry) => files.push(entry)).on("end", () => resolve$13(files)).on("error", (error) => reject(error));
9609
9250
  });
9610
9251
  };
9611
9252
  readdirp$1.promise = readdirpPromise;
@@ -13757,13 +13398,13 @@ var require_nodefs_handler = /* @__PURE__ */ __commonJSMin(((exports, module) =>
13757
13398
  this._addToNodeFs(path$7, initialAdd, wh, depth$3 + 1);
13758
13399
  }
13759
13400
  }).on(EV_ERROR$2, this._boundHandleError);
13760
- return new Promise((resolve$14) => stream.once(STR_END$2, () => {
13401
+ return new Promise((resolve$13) => stream.once(STR_END$2, () => {
13761
13402
  if (this.fsw.closed) {
13762
13403
  stream = undefined;
13763
13404
  return;
13764
13405
  }
13765
13406
  const wasThrottled = throttler ? throttler.clear() : false;
13766
- resolve$14();
13407
+ resolve$13();
13767
13408
  previous.getChildren().filter((item) => {
13768
13409
  return item !== directory && !current.has(item) && (!wh.hasGlob || wh.filterPath({ fullPath: sysPath$2.resolve(directory, item) }));
13769
13410
  }).forEach((item) => {