@soda-gql/tools 0.13.0 → 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.
@@ -2,10 +2,10 @@
2
2
  const require_bin = require('./bin.cjs');
3
3
  let neverthrow = require("neverthrow");
4
4
  let node_crypto = require("node:crypto");
5
- let __soda_gql_common_template_extraction = require("@soda-gql/common/template-extraction");
6
- let __swc_core = require("@swc/core");
7
5
  let node_module = require("node:module");
6
+ let __soda_gql_common_template_extraction = require("@soda-gql/common/template-extraction");
8
7
  let __soda_gql_common_utils = require("@soda-gql/common/utils");
8
+ let __swc_core = require("@swc/core");
9
9
 
10
10
  //#region packages/tools/src/formatter/detection.ts
11
11
  /**
@@ -507,4 +507,4 @@ const needsFormat = (options) => {
507
507
  //#endregion
508
508
  exports.format = format;
509
509
  exports.needsFormat = needsFormat;
510
- //# sourceMappingURL=formatter-Glj5a663.cjs.map
510
+ //# sourceMappingURL=formatter-D5rL2R7k.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"formatter-Glj5a663.cjs","names":["bodyObject: ObjectExpression | null","module","require","_graphqlModule: GraphqlModule | undefined","_graphqlModuleError: Error | undefined","initialContext: TraversalContext","module","module: Module","insertionPoints: InsertionPoint[]","positionCtx: PositionTrackingContext","defaultFormat: FormatGraphqlFn"],"sources":["../src/formatter/detection.ts","../src/formatter/insertion.ts","../src/formatter/format.ts"],"sourcesContent":["import type {\n ArrowFunctionExpression,\n CallExpression,\n Expression,\n Module,\n ObjectExpression,\n ObjectPatternProperty,\n Pattern,\n} from \"@swc/types\";\n\n/**\n * Check if an expression is a reference to gql\n * Handles: gql, namespace.gql\n */\nexport const isGqlReference = (node: Expression, gqlIdentifiers: ReadonlySet<string>): boolean => {\n // Unwrap TsNonNullExpression: gql! -> gql\n const unwrapped = node.type === \"TsNonNullExpression\" ? (node as { type: string; expression: Expression }).expression : node;\n if (unwrapped.type === \"Identifier\") {\n return gqlIdentifiers.has(unwrapped.value);\n }\n if (unwrapped.type === \"MemberExpression\" && unwrapped.property.type === \"Identifier\" && unwrapped.property.value === \"gql\") {\n return true;\n }\n return false;\n};\n\n/**\n * Check if a call expression is a gql definition call\n * Handles: gql.default(...), gql.model(...), gql.schemaName(...) (multi-schema)\n */\nexport const isGqlDefinitionCall = (node: CallExpression, gqlIdentifiers: ReadonlySet<string>): boolean => {\n if (node.callee.type !== \"MemberExpression\") return false;\n const { object } = node.callee;\n\n // Check object is a gql reference first\n if (!isGqlReference(object, gqlIdentifiers)) return false;\n\n // Verify first argument is an arrow function (factory pattern)\n // SWC's arguments are ExprOrSpread[], so access via .expression\n const firstArg = node.arguments[0];\n if (!firstArg || (firstArg.expression.type !== \"ArrowFunctionExpression\" && firstArg.expression.type !== \"FunctionExpression\"))\n return false;\n\n return true;\n};\n\n/**\n * Check if an object expression is a field selection object\n * Field selection objects are returned from arrow functions with ({ f }) or ({ f, $ }) parameter\n */\nexport const isFieldSelectionObject = (object: ObjectExpression, parent: ArrowFunctionExpression): boolean => {\n // The object must be the body of the arrow function\n // Handle both direct ObjectExpression and parenthesized ObjectExpression: `({ ... })`\n let bodyObject: ObjectExpression | null = null;\n\n if (parent.body.type === \"ObjectExpression\") {\n bodyObject = parent.body;\n } else if (parent.body.type === \"ParenthesisExpression\") {\n // Handle `({ f }) => ({ ...f.id() })` pattern where body is parenthesized\n const inner = parent.body.expression;\n if (inner.type === \"ObjectExpression\") {\n bodyObject = inner;\n }\n }\n\n if (!bodyObject) return false;\n if (bodyObject.span.start !== object.span.start) return false;\n\n // Check if first parameter has 'f' destructured\n const param = parent.params[0];\n if (!param || param.type !== \"ObjectPattern\") return false;\n\n return param.properties.some((p: ObjectPatternProperty) => {\n if (p.type === \"KeyValuePatternProperty\" && p.key.type === \"Identifier\") {\n return p.key.value === \"f\";\n }\n if (p.type === \"AssignmentPatternProperty\" && p.key.type === \"Identifier\") {\n return p.key.value === \"f\";\n }\n return false;\n });\n};\n\n/**\n * Collect gql identifiers from import declarations\n */\nexport const collectGqlIdentifiers = (module: Module): Set<string> => {\n const gqlIdentifiers = new Set<string>();\n\n for (const item of module.body) {\n if (item.type !== \"ImportDeclaration\") continue;\n\n for (const specifier of item.specifiers) {\n if (specifier.type === \"ImportSpecifier\") {\n const imported = specifier.imported?.value ?? specifier.local.value;\n if (imported === \"gql\") {\n gqlIdentifiers.add(specifier.local.value);\n }\n }\n if (specifier.type === \"ImportDefaultSpecifier\") {\n // Check if default import might be gql\n if (specifier.local.value === \"gql\") {\n gqlIdentifiers.add(specifier.local.value);\n }\n }\n if (specifier.type === \"ImportNamespaceSpecifier\") {\n // namespace import: import * as ns from \"...\"\n // Would need ns.gql pattern - handled by isGqlReference\n }\n }\n }\n\n return gqlIdentifiers;\n};\n\n/**\n * Check if a call expression is a fragment definition call.\n * Pattern: fragment.TypeName({ ... }) where `fragment` comes from gql factory destructuring.\n */\nexport const isFragmentDefinitionCall = (node: CallExpression, fragmentIdentifiers: ReadonlySet<string>): boolean => {\n if (node.callee.type !== \"MemberExpression\") return false;\n\n const { object } = node.callee;\n\n if (object.type !== \"Identifier\") return false;\n if (!fragmentIdentifiers.has(object.value)) return false;\n\n const firstArg = node.arguments[0];\n if (!firstArg || firstArg.expression.type !== \"ObjectExpression\") return false;\n\n return true;\n};\n\n/**\n * Check if an object expression already has a `key` property.\n */\nexport const hasKeyProperty = (obj: ObjectExpression): boolean => {\n return obj.properties.some((prop) => {\n if (prop.type === \"KeyValueProperty\" && prop.key.type === \"Identifier\") {\n return prop.key.value === \"key\";\n }\n return false;\n });\n};\n\n/**\n * Collect fragment identifiers from gql factory arrow function parameter.\n * Looks for patterns like: ({ fragment }) => ... or ({ fragment: f }) => ...\n */\nexport const collectFragmentIdentifiers = (arrowFunction: ArrowFunctionExpression): Set<string> => {\n const fragmentIdentifiers = new Set<string>();\n\n const param = arrowFunction.params[0];\n if (param?.type !== \"ObjectPattern\") return fragmentIdentifiers;\n\n for (const p of param.properties as ObjectPatternProperty[]) {\n if (p.type === \"KeyValuePatternProperty\" && p.key.type === \"Identifier\" && p.key.value === \"fragment\") {\n const localName = extractIdentifierName(p.value);\n if (localName) fragmentIdentifiers.add(localName);\n }\n if (p.type === \"AssignmentPatternProperty\" && p.key.type === \"Identifier\" && p.key.value === \"fragment\") {\n fragmentIdentifiers.add(p.key.value);\n }\n }\n\n return fragmentIdentifiers;\n};\n\n/**\n * Extract identifier name from a pattern node.\n */\nconst extractIdentifierName = (pattern: Pattern): string | null => {\n if (pattern.type === \"Identifier\") return pattern.value;\n // biome-ignore lint/suspicious/noExplicitAny: SWC types\n if ((pattern as any).type === \"BindingIdentifier\") return (pattern as any).value;\n return null;\n};\n","import { randomBytes } from \"node:crypto\";\n\n/**\n * The newline string to insert after object opening brace\n */\nexport const NEWLINE_INSERTION = \"\\n\";\n\n/**\n * Generate a random 8-character hex key for fragment identification.\n * Uses Node.js crypto for cryptographically secure random bytes.\n */\nexport const generateFragmentKey = (): string => {\n return randomBytes(4).toString(\"hex\");\n};\n\n/**\n * Create the key property insertion string.\n *\n * For single-line objects: returns ` key: \"xxxxxxxx\",\\n`\n * For multi-line objects (with indentation): returns `{indentation}key: \"xxxxxxxx\",\\n`\n * (the existing indentation before the next property is preserved)\n *\n * @param key - The hex key string\n * @param indentation - Optional indentation string for multi-line objects\n */\nexport const createKeyInsertion = (key: string, indentation?: string): string => {\n if (indentation !== undefined) {\n // Multi-line: key goes on its own line with indentation\n // The next property's indentation is already in the source, so don't duplicate it\n return `${indentation}key: \"${key}\",\\n`;\n }\n // Single-line: space before key, newline after\n return ` key: \"${key}\",\\n`;\n};\n\n/**\n * Check if there's already a newline after the opening brace.\n * Uses string inspection rather than AST.\n *\n * @param source - The source code string\n * @param objectStartPos - The position of the `{` character in the source\n */\nexport const hasExistingNewline = (source: string, objectStartPos: number): boolean => {\n // Skip the `{` character\n const pos = objectStartPos + 1;\n\n // Check if next character is a newline\n const nextChar = source[pos];\n return nextChar === \"\\n\" || nextChar === \"\\r\";\n};\n\n/**\n * Detect the indentation used after an opening brace.\n * Returns the whitespace string after the newline, or null if no newline.\n *\n * @param source - The source code string\n * @param objectStartPos - The position of the `{` character\n */\nexport const detectIndentationAfterBrace = (source: string, objectStartPos: number): string | null => {\n const afterBrace = objectStartPos + 1;\n const char = source[afterBrace];\n\n // Check for newline (handles both \\n and \\r\\n)\n if (char !== \"\\n\" && char !== \"\\r\") {\n return null;\n }\n\n // Skip past the newline character(s)\n let pos = afterBrace + 1;\n if (char === \"\\r\" && source[pos] === \"\\n\") {\n pos++;\n }\n\n // Collect whitespace (indentation)\n let indentation = \"\";\n while (pos < source.length) {\n const c = source[pos];\n if (c === \" \" || c === \"\\t\") {\n indentation += c;\n pos++;\n } else {\n break;\n }\n }\n\n return indentation;\n};\n","import { createRequire } from \"node:module\";\nimport {\n type FormatGraphqlFn,\n formatTemplatesInSource,\n type PositionTrackingContext,\n walkAndExtract,\n} from \"@soda-gql/common/template-extraction\";\nimport { createSwcSpanConverter } from \"@soda-gql/common/utils\";\nimport { parseSync } from \"@swc/core\";\nimport type { ArrowFunctionExpression, CallExpression, Module, Node, ObjectExpression } from \"@swc/types\";\nimport { err, ok, type Result } from \"neverthrow\";\n\nconst require = createRequire(import.meta.url);\n\ntype GraphqlModule = {\n parse: (source: string, options?: { noLocation?: boolean }) => unknown;\n print: (ast: unknown) => string;\n};\n\nlet _graphqlModule: GraphqlModule | undefined;\nlet _graphqlModuleError: Error | undefined;\n\nconst getGraphqlModule = (): Result<GraphqlModule, FormatError> => {\n if (_graphqlModuleError) {\n return err({\n type: \"FormatError\",\n code: \"MISSING_DEPENDENCY\",\n message: 'The \"graphql\" package is required for soda-gql formatter. Install it with: bun add graphql',\n });\n }\n if (!_graphqlModule) {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n _graphqlModule = require(\"graphql\") as GraphqlModule;\n } catch (cause) {\n _graphqlModuleError = cause instanceof Error ? cause : new Error(String(cause));\n return err({\n type: \"FormatError\",\n code: \"MISSING_DEPENDENCY\",\n message: 'The \"graphql\" package is required for soda-gql formatter. Install it with: bun add graphql',\n cause,\n });\n }\n }\n return ok(_graphqlModule);\n};\n\nimport {\n collectFragmentIdentifiers,\n collectGqlIdentifiers,\n hasKeyProperty,\n isFieldSelectionObject,\n isFragmentDefinitionCall,\n isGqlDefinitionCall,\n} from \"./detection\";\nimport {\n createKeyInsertion,\n detectIndentationAfterBrace,\n generateFragmentKey,\n hasExistingNewline,\n NEWLINE_INSERTION,\n} from \"./insertion\";\nimport type { FormatError, FormatOptions, FormatResult } from \"./types\";\n\ntype InsertionPoint = {\n readonly position: number;\n readonly content: string;\n readonly endPosition?: number;\n};\n\ntype TraversalContext = {\n insideGqlDefinition: boolean;\n currentArrowFunction: ArrowFunctionExpression | null;\n fragmentIdentifiers: ReadonlySet<string>;\n};\n\ntype TraversalCallbackContext = {\n readonly isFragmentConfig: boolean;\n};\n\ntype TraversalCallback = (\n object: ObjectExpression,\n parent: ArrowFunctionExpression,\n callbackContext: TraversalCallbackContext,\n) => void;\n\n/**\n * Simple recursive AST traversal\n */\nconst traverseNode = (\n node: Node,\n context: TraversalContext,\n gqlIdentifiers: ReadonlySet<string>,\n onObjectExpression: TraversalCallback,\n): void => {\n // Check for gql definition call entry and collect fragment identifiers\n if (node.type === \"CallExpression\" && isGqlDefinitionCall(node as CallExpression, gqlIdentifiers)) {\n const call = node as CallExpression;\n const firstArg = call.arguments[0];\n if (firstArg?.expression.type === \"ArrowFunctionExpression\") {\n const arrow = firstArg.expression as ArrowFunctionExpression;\n const fragmentIds = collectFragmentIdentifiers(arrow);\n context = {\n ...context,\n insideGqlDefinition: true,\n fragmentIdentifiers: new Set([...context.fragmentIdentifiers, ...fragmentIds]),\n };\n } else {\n context = { ...context, insideGqlDefinition: true };\n }\n }\n\n // Check for fragment definition call: fragment.TypeName({ ... })\n if (\n node.type === \"CallExpression\" &&\n context.insideGqlDefinition &&\n isFragmentDefinitionCall(node as CallExpression, context.fragmentIdentifiers)\n ) {\n const call = node as CallExpression;\n const firstArg = call.arguments[0];\n if (firstArg?.expression.type === \"ObjectExpression\" && context.currentArrowFunction) {\n onObjectExpression(firstArg.expression as ObjectExpression, context.currentArrowFunction, {\n isFragmentConfig: true,\n });\n }\n }\n\n // Handle object expressions - check if it's the body of the current arrow function\n if (\n node.type === \"ObjectExpression\" &&\n context.insideGqlDefinition &&\n context.currentArrowFunction &&\n isFieldSelectionObject(node as ObjectExpression, context.currentArrowFunction)\n ) {\n onObjectExpression(node as ObjectExpression, context.currentArrowFunction, { isFragmentConfig: false });\n }\n\n // Recursively visit children\n if (node.type === \"CallExpression\") {\n const call = node as CallExpression;\n traverseNode(call.callee as Node, context, gqlIdentifiers, onObjectExpression);\n for (const arg of call.arguments) {\n traverseNode(arg.expression as Node, context, gqlIdentifiers, onObjectExpression);\n }\n } else if (node.type === \"ArrowFunctionExpression\") {\n const arrow = node as ArrowFunctionExpression;\n // Update context with the new arrow function\n const childContext = { ...context, currentArrowFunction: arrow };\n if (arrow.body.type !== \"BlockStatement\") {\n traverseNode(arrow.body as Node, childContext, gqlIdentifiers, onObjectExpression);\n }\n } else if (node.type === \"ParenthesisExpression\") {\n // Handle parenthesized expressions like `({ ...f.id() })`\n // biome-ignore lint/suspicious/noExplicitAny: SWC types\n const paren = node as any;\n if (paren.expression) {\n traverseNode(paren.expression as Node, context, gqlIdentifiers, onObjectExpression);\n }\n } else if (node.type === \"MemberExpression\") {\n // biome-ignore lint/suspicious/noExplicitAny: SWC types\n const member = node as any;\n traverseNode(member.object as Node, context, gqlIdentifiers, onObjectExpression);\n } else if (node.type === \"ObjectExpression\") {\n const obj = node as ObjectExpression;\n for (const prop of obj.properties) {\n if (prop.type === \"SpreadElement\") {\n traverseNode(prop.arguments as Node, context, gqlIdentifiers, onObjectExpression);\n } else if (prop.type === \"KeyValueProperty\") {\n traverseNode(prop.value as Node, context, gqlIdentifiers, onObjectExpression);\n }\n }\n } else {\n // Generic traversal for other node types\n for (const value of Object.values(node)) {\n if (Array.isArray(value)) {\n for (const child of value) {\n if (child && typeof child === \"object\" && \"type\" in child) {\n traverseNode(child as Node, context, gqlIdentifiers, onObjectExpression);\n }\n }\n } else if (value && typeof value === \"object\" && \"type\" in value) {\n traverseNode(value as Node, context, gqlIdentifiers, onObjectExpression);\n }\n }\n }\n};\n\nconst traverse = (module: Module, gqlIdentifiers: ReadonlySet<string>, onObjectExpression: TraversalCallback): void => {\n const initialContext: TraversalContext = {\n insideGqlDefinition: false,\n currentArrowFunction: null,\n fragmentIdentifiers: new Set(),\n };\n for (const statement of module.body) {\n traverseNode(statement, initialContext, gqlIdentifiers, onObjectExpression);\n }\n};\n\n/**\n * Format soda-gql field selection objects by inserting newlines.\n * Optionally injects fragment keys for anonymous fragments.\n */\nexport const format = (options: FormatOptions): Result<FormatResult, FormatError> => {\n const { sourceCode, filePath, injectFragmentKeys = false } = options;\n\n // Parse source code with SWC\n let module: Module;\n try {\n const program = parseSync(sourceCode, {\n syntax: \"typescript\",\n tsx: filePath?.endsWith(\".tsx\") ?? true,\n target: \"es2022\",\n decorators: false,\n dynamicImport: true,\n });\n\n if (program.type !== \"Module\") {\n return err({\n type: \"FormatError\",\n code: \"PARSE_ERROR\",\n message: `Not a module${filePath ? ` (${filePath})` : \"\"}`,\n });\n }\n module = program;\n } catch (cause) {\n return err({\n type: \"FormatError\",\n code: \"PARSE_ERROR\",\n message: `Failed to parse source code${filePath ? ` (${filePath})` : \"\"}`,\n cause,\n });\n }\n\n // Calculate span offset for position normalization\n // SWC's BytePos counter accumulates across parseSync calls within the same process\n // Using module.span.start ensures correct position calculation regardless of accumulation\n const spanOffset = module.span.start;\n\n // SWC returns UTF-8 byte offsets; JS strings use UTF-16 code units.\n // The converter handles this mapping (fast path for ASCII-only sources).\n const converter = createSwcSpanConverter(sourceCode);\n\n // Collect gql identifiers from imports\n const gqlIdentifiers = collectGqlIdentifiers(module);\n if (gqlIdentifiers.size === 0) {\n return ok({ modified: false, sourceCode });\n }\n\n // Collect insertion points\n const insertionPoints: InsertionPoint[] = [];\n\n traverse(module, gqlIdentifiers, (object, _parent, callbackContext) => {\n // Calculate actual position in source (byte offset → char index)\n const objectStart = converter.byteOffsetToCharIndex(object.span.start - spanOffset);\n\n // For fragment config objects, inject key if enabled and not present\n if (callbackContext.isFragmentConfig && injectFragmentKeys && !hasKeyProperty(object)) {\n const key = generateFragmentKey();\n\n if (hasExistingNewline(sourceCode, objectStart)) {\n // Multi-line: insert after newline, preserving indentation\n const indentation = detectIndentationAfterBrace(sourceCode, objectStart) ?? \"\";\n let insertPos = objectStart + 2; // Skip { and \\n\n if (sourceCode[objectStart + 1] === \"\\r\") insertPos++;\n\n insertionPoints.push({\n position: insertPos,\n content: createKeyInsertion(key, indentation),\n });\n } else {\n // Single-line: insert right after {\n insertionPoints.push({\n position: objectStart + 1,\n content: createKeyInsertion(key),\n });\n }\n }\n\n // For field selection objects, insert newline if not present\n if (!callbackContext.isFragmentConfig && !hasExistingNewline(sourceCode, objectStart)) {\n insertionPoints.push({\n position: objectStart + 1,\n content: NEWLINE_INSERTION,\n });\n }\n });\n\n // Tagged template formatting\n const graphqlResult = getGraphqlModule();\n if (graphqlResult.isErr()) {\n return err(graphqlResult.error);\n }\n const { parse: parseGraphql, print: printGraphql } = graphqlResult.value;\n\n const positionCtx: PositionTrackingContext = { spanOffset, converter };\n const templates = walkAndExtract(module as unknown as Node, gqlIdentifiers, positionCtx);\n\n if (templates.length > 0) {\n const defaultFormat: FormatGraphqlFn = (source) => {\n const ast = parseGraphql(source, { noLocation: false });\n return printGraphql(ast);\n };\n\n const templateEdits = formatTemplatesInSource(templates, sourceCode, defaultFormat);\n\n for (const edit of templateEdits) {\n insertionPoints.push({\n position: edit.start,\n content: edit.newText,\n endPosition: edit.end,\n });\n }\n }\n\n // Apply insertions\n if (insertionPoints.length === 0) {\n return ok({ modified: false, sourceCode });\n }\n\n // Sort in descending order to insert from end to beginning\n // This preserves earlier positions while modifying later parts\n const sortedPoints = [...insertionPoints].sort((a, b) => b.position - a.position);\n\n let result = sourceCode;\n for (const point of sortedPoints) {\n const end = point.endPosition ?? point.position;\n result = result.slice(0, point.position) + point.content + result.slice(end);\n }\n\n return ok({ modified: true, sourceCode: result });\n};\n\n/**\n * Check if a file needs formatting (has unformatted field selections).\n * Useful for pre-commit hooks or CI checks.\n */\nexport const needsFormat = (options: FormatOptions): Result<boolean, FormatError> => {\n const { sourceCode, filePath } = options;\n\n // Parse source code with SWC\n let module: Module;\n try {\n const program = parseSync(sourceCode, {\n syntax: \"typescript\",\n tsx: filePath?.endsWith(\".tsx\") ?? true,\n target: \"es2022\",\n decorators: false,\n dynamicImport: true,\n });\n\n if (program.type !== \"Module\") {\n return err({\n type: \"FormatError\",\n code: \"PARSE_ERROR\",\n message: `Not a module${filePath ? ` (${filePath})` : \"\"}`,\n });\n }\n module = program;\n } catch (cause) {\n return err({\n type: \"FormatError\",\n code: \"PARSE_ERROR\",\n message: `Failed to parse source code${filePath ? ` (${filePath})` : \"\"}`,\n cause,\n });\n }\n\n const spanOffset = module.span.start;\n const converter = createSwcSpanConverter(sourceCode);\n const gqlIdentifiers = collectGqlIdentifiers(module);\n\n if (gqlIdentifiers.size === 0) {\n return ok(false);\n }\n\n let needsFormatting = false;\n\n traverse(module, gqlIdentifiers, (object, _parent, callbackContext) => {\n if (needsFormatting) return; // Early exit\n\n // Skip fragment config objects for needsFormat check (key injection is optional)\n if (callbackContext.isFragmentConfig) return;\n\n const objectStart = converter.byteOffsetToCharIndex(object.span.start - spanOffset);\n if (!hasExistingNewline(sourceCode, objectStart)) {\n needsFormatting = true;\n }\n });\n\n // Check tagged templates\n if (!needsFormatting) {\n const graphqlResult = getGraphqlModule();\n if (graphqlResult.isErr()) {\n return err(graphqlResult.error);\n }\n const { parse: parseGraphql, print: printGraphql } = graphqlResult.value;\n\n const positionCtx: PositionTrackingContext = { spanOffset, converter };\n const templates = walkAndExtract(module as unknown as Node, gqlIdentifiers, positionCtx);\n\n if (templates.length > 0) {\n const defaultFormat: FormatGraphqlFn = (source) => {\n const ast = parseGraphql(source, { noLocation: false });\n return printGraphql(ast);\n };\n\n const templateEdits = formatTemplatesInSource(templates, sourceCode, defaultFormat);\n if (templateEdits.length > 0) {\n needsFormatting = true;\n }\n }\n }\n\n return ok(needsFormatting);\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"formatter-D5rL2R7k.cjs","names":["bodyObject: ObjectExpression | null","module","require","_graphqlModule: GraphqlModule | undefined","_graphqlModuleError: Error | undefined","initialContext: TraversalContext","module","module: Module","insertionPoints: InsertionPoint[]","positionCtx: PositionTrackingContext","defaultFormat: FormatGraphqlFn"],"sources":["../src/formatter/detection.ts","../src/formatter/insertion.ts","../src/formatter/format.ts"],"sourcesContent":["import type {\n ArrowFunctionExpression,\n CallExpression,\n Expression,\n Module,\n ObjectExpression,\n ObjectPatternProperty,\n Pattern,\n} from \"@swc/types\";\n\n/**\n * Check if an expression is a reference to gql\n * Handles: gql, namespace.gql\n */\nexport const isGqlReference = (node: Expression, gqlIdentifiers: ReadonlySet<string>): boolean => {\n // Unwrap TsNonNullExpression: gql! -> gql\n const unwrapped = node.type === \"TsNonNullExpression\" ? (node as { type: string; expression: Expression }).expression : node;\n if (unwrapped.type === \"Identifier\") {\n return gqlIdentifiers.has(unwrapped.value);\n }\n if (unwrapped.type === \"MemberExpression\" && unwrapped.property.type === \"Identifier\" && unwrapped.property.value === \"gql\") {\n return true;\n }\n return false;\n};\n\n/**\n * Check if a call expression is a gql definition call\n * Handles: gql.default(...), gql.model(...), gql.schemaName(...) (multi-schema)\n */\nexport const isGqlDefinitionCall = (node: CallExpression, gqlIdentifiers: ReadonlySet<string>): boolean => {\n if (node.callee.type !== \"MemberExpression\") return false;\n const { object } = node.callee;\n\n // Check object is a gql reference first\n if (!isGqlReference(object, gqlIdentifiers)) return false;\n\n // Verify first argument is an arrow function (factory pattern)\n // SWC's arguments are ExprOrSpread[], so access via .expression\n const firstArg = node.arguments[0];\n if (!firstArg || (firstArg.expression.type !== \"ArrowFunctionExpression\" && firstArg.expression.type !== \"FunctionExpression\"))\n return false;\n\n return true;\n};\n\n/**\n * Check if an object expression is a field selection object\n * Field selection objects are returned from arrow functions with ({ f }) or ({ f, $ }) parameter\n */\nexport const isFieldSelectionObject = (object: ObjectExpression, parent: ArrowFunctionExpression): boolean => {\n // The object must be the body of the arrow function\n // Handle both direct ObjectExpression and parenthesized ObjectExpression: `({ ... })`\n let bodyObject: ObjectExpression | null = null;\n\n if (parent.body.type === \"ObjectExpression\") {\n bodyObject = parent.body;\n } else if (parent.body.type === \"ParenthesisExpression\") {\n // Handle `({ f }) => ({ ...f.id() })` pattern where body is parenthesized\n const inner = parent.body.expression;\n if (inner.type === \"ObjectExpression\") {\n bodyObject = inner;\n }\n }\n\n if (!bodyObject) return false;\n if (bodyObject.span.start !== object.span.start) return false;\n\n // Check if first parameter has 'f' destructured\n const param = parent.params[0];\n if (!param || param.type !== \"ObjectPattern\") return false;\n\n return param.properties.some((p: ObjectPatternProperty) => {\n if (p.type === \"KeyValuePatternProperty\" && p.key.type === \"Identifier\") {\n return p.key.value === \"f\";\n }\n if (p.type === \"AssignmentPatternProperty\" && p.key.type === \"Identifier\") {\n return p.key.value === \"f\";\n }\n return false;\n });\n};\n\n/**\n * Collect gql identifiers from import declarations\n */\nexport const collectGqlIdentifiers = (module: Module): Set<string> => {\n const gqlIdentifiers = new Set<string>();\n\n for (const item of module.body) {\n if (item.type !== \"ImportDeclaration\") continue;\n\n for (const specifier of item.specifiers) {\n if (specifier.type === \"ImportSpecifier\") {\n const imported = specifier.imported?.value ?? specifier.local.value;\n if (imported === \"gql\") {\n gqlIdentifiers.add(specifier.local.value);\n }\n }\n if (specifier.type === \"ImportDefaultSpecifier\") {\n // Check if default import might be gql\n if (specifier.local.value === \"gql\") {\n gqlIdentifiers.add(specifier.local.value);\n }\n }\n if (specifier.type === \"ImportNamespaceSpecifier\") {\n // namespace import: import * as ns from \"...\"\n // Would need ns.gql pattern - handled by isGqlReference\n }\n }\n }\n\n return gqlIdentifiers;\n};\n\n/**\n * Check if a call expression is a fragment definition call.\n * Pattern: fragment.TypeName({ ... }) where `fragment` comes from gql factory destructuring.\n */\nexport const isFragmentDefinitionCall = (node: CallExpression, fragmentIdentifiers: ReadonlySet<string>): boolean => {\n if (node.callee.type !== \"MemberExpression\") return false;\n\n const { object } = node.callee;\n\n if (object.type !== \"Identifier\") return false;\n if (!fragmentIdentifiers.has(object.value)) return false;\n\n const firstArg = node.arguments[0];\n if (!firstArg || firstArg.expression.type !== \"ObjectExpression\") return false;\n\n return true;\n};\n\n/**\n * Check if an object expression already has a `key` property.\n */\nexport const hasKeyProperty = (obj: ObjectExpression): boolean => {\n return obj.properties.some((prop) => {\n if (prop.type === \"KeyValueProperty\" && prop.key.type === \"Identifier\") {\n return prop.key.value === \"key\";\n }\n return false;\n });\n};\n\n/**\n * Collect fragment identifiers from gql factory arrow function parameter.\n * Looks for patterns like: ({ fragment }) => ... or ({ fragment: f }) => ...\n */\nexport const collectFragmentIdentifiers = (arrowFunction: ArrowFunctionExpression): Set<string> => {\n const fragmentIdentifiers = new Set<string>();\n\n const param = arrowFunction.params[0];\n if (param?.type !== \"ObjectPattern\") return fragmentIdentifiers;\n\n for (const p of param.properties as ObjectPatternProperty[]) {\n if (p.type === \"KeyValuePatternProperty\" && p.key.type === \"Identifier\" && p.key.value === \"fragment\") {\n const localName = extractIdentifierName(p.value);\n if (localName) fragmentIdentifiers.add(localName);\n }\n if (p.type === \"AssignmentPatternProperty\" && p.key.type === \"Identifier\" && p.key.value === \"fragment\") {\n fragmentIdentifiers.add(p.key.value);\n }\n }\n\n return fragmentIdentifiers;\n};\n\n/**\n * Extract identifier name from a pattern node.\n */\nconst extractIdentifierName = (pattern: Pattern): string | null => {\n if (pattern.type === \"Identifier\") return pattern.value;\n // biome-ignore lint/suspicious/noExplicitAny: SWC types\n if ((pattern as any).type === \"BindingIdentifier\") return (pattern as any).value;\n return null;\n};\n","import { randomBytes } from \"node:crypto\";\n\n/**\n * The newline string to insert after object opening brace\n */\nexport const NEWLINE_INSERTION = \"\\n\";\n\n/**\n * Generate a random 8-character hex key for fragment identification.\n * Uses Node.js crypto for cryptographically secure random bytes.\n */\nexport const generateFragmentKey = (): string => {\n return randomBytes(4).toString(\"hex\");\n};\n\n/**\n * Create the key property insertion string.\n *\n * For single-line objects: returns ` key: \"xxxxxxxx\",\\n`\n * For multi-line objects (with indentation): returns `{indentation}key: \"xxxxxxxx\",\\n`\n * (the existing indentation before the next property is preserved)\n *\n * @param key - The hex key string\n * @param indentation - Optional indentation string for multi-line objects\n */\nexport const createKeyInsertion = (key: string, indentation?: string): string => {\n if (indentation !== undefined) {\n // Multi-line: key goes on its own line with indentation\n // The next property's indentation is already in the source, so don't duplicate it\n return `${indentation}key: \"${key}\",\\n`;\n }\n // Single-line: space before key, newline after\n return ` key: \"${key}\",\\n`;\n};\n\n/**\n * Check if there's already a newline after the opening brace.\n * Uses string inspection rather than AST.\n *\n * @param source - The source code string\n * @param objectStartPos - The position of the `{` character in the source\n */\nexport const hasExistingNewline = (source: string, objectStartPos: number): boolean => {\n // Skip the `{` character\n const pos = objectStartPos + 1;\n\n // Check if next character is a newline\n const nextChar = source[pos];\n return nextChar === \"\\n\" || nextChar === \"\\r\";\n};\n\n/**\n * Detect the indentation used after an opening brace.\n * Returns the whitespace string after the newline, or null if no newline.\n *\n * @param source - The source code string\n * @param objectStartPos - The position of the `{` character\n */\nexport const detectIndentationAfterBrace = (source: string, objectStartPos: number): string | null => {\n const afterBrace = objectStartPos + 1;\n const char = source[afterBrace];\n\n // Check for newline (handles both \\n and \\r\\n)\n if (char !== \"\\n\" && char !== \"\\r\") {\n return null;\n }\n\n // Skip past the newline character(s)\n let pos = afterBrace + 1;\n if (char === \"\\r\" && source[pos] === \"\\n\") {\n pos++;\n }\n\n // Collect whitespace (indentation)\n let indentation = \"\";\n while (pos < source.length) {\n const c = source[pos];\n if (c === \" \" || c === \"\\t\") {\n indentation += c;\n pos++;\n } else {\n break;\n }\n }\n\n return indentation;\n};\n","import { createRequire } from \"node:module\";\nimport {\n type FormatGraphqlFn,\n formatTemplatesInSource,\n type PositionTrackingContext,\n walkAndExtract,\n} from \"@soda-gql/common/template-extraction\";\nimport { createSwcSpanConverter } from \"@soda-gql/common/utils\";\nimport { parseSync } from \"@swc/core\";\nimport type { ArrowFunctionExpression, CallExpression, Module, Node, ObjectExpression } from \"@swc/types\";\nimport { err, ok, type Result } from \"neverthrow\";\n\nconst require = createRequire(import.meta.url);\n\ntype GraphqlModule = {\n parse: (source: string, options?: { noLocation?: boolean }) => unknown;\n print: (ast: unknown) => string;\n};\n\nlet _graphqlModule: GraphqlModule | undefined;\nlet _graphqlModuleError: Error | undefined;\n\nconst getGraphqlModule = (): Result<GraphqlModule, FormatError> => {\n if (_graphqlModuleError) {\n return err({\n type: \"FormatError\",\n code: \"MISSING_DEPENDENCY\",\n message: 'The \"graphql\" package is required for soda-gql formatter. Install it with: bun add graphql',\n });\n }\n if (!_graphqlModule) {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n _graphqlModule = require(\"graphql\") as GraphqlModule;\n } catch (cause) {\n _graphqlModuleError = cause instanceof Error ? cause : new Error(String(cause));\n return err({\n type: \"FormatError\",\n code: \"MISSING_DEPENDENCY\",\n message: 'The \"graphql\" package is required for soda-gql formatter. Install it with: bun add graphql',\n cause,\n });\n }\n }\n return ok(_graphqlModule);\n};\n\nimport {\n collectFragmentIdentifiers,\n collectGqlIdentifiers,\n hasKeyProperty,\n isFieldSelectionObject,\n isFragmentDefinitionCall,\n isGqlDefinitionCall,\n} from \"./detection\";\nimport {\n createKeyInsertion,\n detectIndentationAfterBrace,\n generateFragmentKey,\n hasExistingNewline,\n NEWLINE_INSERTION,\n} from \"./insertion\";\nimport type { FormatError, FormatOptions, FormatResult } from \"./types\";\n\ntype InsertionPoint = {\n readonly position: number;\n readonly content: string;\n readonly endPosition?: number;\n};\n\ntype TraversalContext = {\n insideGqlDefinition: boolean;\n currentArrowFunction: ArrowFunctionExpression | null;\n fragmentIdentifiers: ReadonlySet<string>;\n};\n\ntype TraversalCallbackContext = {\n readonly isFragmentConfig: boolean;\n};\n\ntype TraversalCallback = (\n object: ObjectExpression,\n parent: ArrowFunctionExpression,\n callbackContext: TraversalCallbackContext,\n) => void;\n\n/**\n * Simple recursive AST traversal\n */\nconst traverseNode = (\n node: Node,\n context: TraversalContext,\n gqlIdentifiers: ReadonlySet<string>,\n onObjectExpression: TraversalCallback,\n): void => {\n // Check for gql definition call entry and collect fragment identifiers\n if (node.type === \"CallExpression\" && isGqlDefinitionCall(node as CallExpression, gqlIdentifiers)) {\n const call = node as CallExpression;\n const firstArg = call.arguments[0];\n if (firstArg?.expression.type === \"ArrowFunctionExpression\") {\n const arrow = firstArg.expression as ArrowFunctionExpression;\n const fragmentIds = collectFragmentIdentifiers(arrow);\n context = {\n ...context,\n insideGqlDefinition: true,\n fragmentIdentifiers: new Set([...context.fragmentIdentifiers, ...fragmentIds]),\n };\n } else {\n context = { ...context, insideGqlDefinition: true };\n }\n }\n\n // Check for fragment definition call: fragment.TypeName({ ... })\n if (\n node.type === \"CallExpression\" &&\n context.insideGqlDefinition &&\n isFragmentDefinitionCall(node as CallExpression, context.fragmentIdentifiers)\n ) {\n const call = node as CallExpression;\n const firstArg = call.arguments[0];\n if (firstArg?.expression.type === \"ObjectExpression\" && context.currentArrowFunction) {\n onObjectExpression(firstArg.expression as ObjectExpression, context.currentArrowFunction, {\n isFragmentConfig: true,\n });\n }\n }\n\n // Handle object expressions - check if it's the body of the current arrow function\n if (\n node.type === \"ObjectExpression\" &&\n context.insideGqlDefinition &&\n context.currentArrowFunction &&\n isFieldSelectionObject(node as ObjectExpression, context.currentArrowFunction)\n ) {\n onObjectExpression(node as ObjectExpression, context.currentArrowFunction, { isFragmentConfig: false });\n }\n\n // Recursively visit children\n if (node.type === \"CallExpression\") {\n const call = node as CallExpression;\n traverseNode(call.callee as Node, context, gqlIdentifiers, onObjectExpression);\n for (const arg of call.arguments) {\n traverseNode(arg.expression as Node, context, gqlIdentifiers, onObjectExpression);\n }\n } else if (node.type === \"ArrowFunctionExpression\") {\n const arrow = node as ArrowFunctionExpression;\n // Update context with the new arrow function\n const childContext = { ...context, currentArrowFunction: arrow };\n if (arrow.body.type !== \"BlockStatement\") {\n traverseNode(arrow.body as Node, childContext, gqlIdentifiers, onObjectExpression);\n }\n } else if (node.type === \"ParenthesisExpression\") {\n // Handle parenthesized expressions like `({ ...f.id() })`\n // biome-ignore lint/suspicious/noExplicitAny: SWC types\n const paren = node as any;\n if (paren.expression) {\n traverseNode(paren.expression as Node, context, gqlIdentifiers, onObjectExpression);\n }\n } else if (node.type === \"MemberExpression\") {\n // biome-ignore lint/suspicious/noExplicitAny: SWC types\n const member = node as any;\n traverseNode(member.object as Node, context, gqlIdentifiers, onObjectExpression);\n } else if (node.type === \"ObjectExpression\") {\n const obj = node as ObjectExpression;\n for (const prop of obj.properties) {\n if (prop.type === \"SpreadElement\") {\n traverseNode(prop.arguments as Node, context, gqlIdentifiers, onObjectExpression);\n } else if (prop.type === \"KeyValueProperty\") {\n traverseNode(prop.value as Node, context, gqlIdentifiers, onObjectExpression);\n }\n }\n } else {\n // Generic traversal for other node types\n for (const value of Object.values(node)) {\n if (Array.isArray(value)) {\n for (const child of value) {\n if (child && typeof child === \"object\" && \"type\" in child) {\n traverseNode(child as Node, context, gqlIdentifiers, onObjectExpression);\n }\n }\n } else if (value && typeof value === \"object\" && \"type\" in value) {\n traverseNode(value as Node, context, gqlIdentifiers, onObjectExpression);\n }\n }\n }\n};\n\nconst traverse = (module: Module, gqlIdentifiers: ReadonlySet<string>, onObjectExpression: TraversalCallback): void => {\n const initialContext: TraversalContext = {\n insideGqlDefinition: false,\n currentArrowFunction: null,\n fragmentIdentifiers: new Set(),\n };\n for (const statement of module.body) {\n traverseNode(statement, initialContext, gqlIdentifiers, onObjectExpression);\n }\n};\n\n/**\n * Format soda-gql field selection objects by inserting newlines.\n * Optionally injects fragment keys for anonymous fragments.\n */\nexport const format = (options: FormatOptions): Result<FormatResult, FormatError> => {\n const { sourceCode, filePath, injectFragmentKeys = false } = options;\n\n // Parse source code with SWC\n let module: Module;\n try {\n const program = parseSync(sourceCode, {\n syntax: \"typescript\",\n tsx: filePath?.endsWith(\".tsx\") ?? true,\n target: \"es2022\",\n decorators: false,\n dynamicImport: true,\n });\n\n if (program.type !== \"Module\") {\n return err({\n type: \"FormatError\",\n code: \"PARSE_ERROR\",\n message: `Not a module${filePath ? ` (${filePath})` : \"\"}`,\n });\n }\n module = program;\n } catch (cause) {\n return err({\n type: \"FormatError\",\n code: \"PARSE_ERROR\",\n message: `Failed to parse source code${filePath ? ` (${filePath})` : \"\"}`,\n cause,\n });\n }\n\n // Calculate span offset for position normalization\n // SWC's BytePos counter accumulates across parseSync calls within the same process\n // Using module.span.start ensures correct position calculation regardless of accumulation\n const spanOffset = module.span.start;\n\n // SWC returns UTF-8 byte offsets; JS strings use UTF-16 code units.\n // The converter handles this mapping (fast path for ASCII-only sources).\n const converter = createSwcSpanConverter(sourceCode);\n\n // Collect gql identifiers from imports\n const gqlIdentifiers = collectGqlIdentifiers(module);\n if (gqlIdentifiers.size === 0) {\n return ok({ modified: false, sourceCode });\n }\n\n // Collect insertion points\n const insertionPoints: InsertionPoint[] = [];\n\n traverse(module, gqlIdentifiers, (object, _parent, callbackContext) => {\n // Calculate actual position in source (byte offset → char index)\n const objectStart = converter.byteOffsetToCharIndex(object.span.start - spanOffset);\n\n // For fragment config objects, inject key if enabled and not present\n if (callbackContext.isFragmentConfig && injectFragmentKeys && !hasKeyProperty(object)) {\n const key = generateFragmentKey();\n\n if (hasExistingNewline(sourceCode, objectStart)) {\n // Multi-line: insert after newline, preserving indentation\n const indentation = detectIndentationAfterBrace(sourceCode, objectStart) ?? \"\";\n let insertPos = objectStart + 2; // Skip { and \\n\n if (sourceCode[objectStart + 1] === \"\\r\") insertPos++;\n\n insertionPoints.push({\n position: insertPos,\n content: createKeyInsertion(key, indentation),\n });\n } else {\n // Single-line: insert right after {\n insertionPoints.push({\n position: objectStart + 1,\n content: createKeyInsertion(key),\n });\n }\n }\n\n // For field selection objects, insert newline if not present\n if (!callbackContext.isFragmentConfig && !hasExistingNewline(sourceCode, objectStart)) {\n insertionPoints.push({\n position: objectStart + 1,\n content: NEWLINE_INSERTION,\n });\n }\n });\n\n // Tagged template formatting\n const graphqlResult = getGraphqlModule();\n if (graphqlResult.isErr()) {\n return err(graphqlResult.error);\n }\n const { parse: parseGraphql, print: printGraphql } = graphqlResult.value;\n\n const positionCtx: PositionTrackingContext = { spanOffset, converter };\n const templates = walkAndExtract(module as unknown as Node, gqlIdentifiers, positionCtx);\n\n if (templates.length > 0) {\n const defaultFormat: FormatGraphqlFn = (source) => {\n const ast = parseGraphql(source, { noLocation: false });\n return printGraphql(ast);\n };\n\n const templateEdits = formatTemplatesInSource(templates, sourceCode, defaultFormat);\n\n for (const edit of templateEdits) {\n insertionPoints.push({\n position: edit.start,\n content: edit.newText,\n endPosition: edit.end,\n });\n }\n }\n\n // Apply insertions\n if (insertionPoints.length === 0) {\n return ok({ modified: false, sourceCode });\n }\n\n // Sort in descending order to insert from end to beginning\n // This preserves earlier positions while modifying later parts\n const sortedPoints = [...insertionPoints].sort((a, b) => b.position - a.position);\n\n let result = sourceCode;\n for (const point of sortedPoints) {\n const end = point.endPosition ?? point.position;\n result = result.slice(0, point.position) + point.content + result.slice(end);\n }\n\n return ok({ modified: true, sourceCode: result });\n};\n\n/**\n * Check if a file needs formatting (has unformatted field selections).\n * Useful for pre-commit hooks or CI checks.\n */\nexport const needsFormat = (options: FormatOptions): Result<boolean, FormatError> => {\n const { sourceCode, filePath } = options;\n\n // Parse source code with SWC\n let module: Module;\n try {\n const program = parseSync(sourceCode, {\n syntax: \"typescript\",\n tsx: filePath?.endsWith(\".tsx\") ?? true,\n target: \"es2022\",\n decorators: false,\n dynamicImport: true,\n });\n\n if (program.type !== \"Module\") {\n return err({\n type: \"FormatError\",\n code: \"PARSE_ERROR\",\n message: `Not a module${filePath ? ` (${filePath})` : \"\"}`,\n });\n }\n module = program;\n } catch (cause) {\n return err({\n type: \"FormatError\",\n code: \"PARSE_ERROR\",\n message: `Failed to parse source code${filePath ? ` (${filePath})` : \"\"}`,\n cause,\n });\n }\n\n const spanOffset = module.span.start;\n const converter = createSwcSpanConverter(sourceCode);\n const gqlIdentifiers = collectGqlIdentifiers(module);\n\n if (gqlIdentifiers.size === 0) {\n return ok(false);\n }\n\n let needsFormatting = false;\n\n traverse(module, gqlIdentifiers, (object, _parent, callbackContext) => {\n if (needsFormatting) return; // Early exit\n\n // Skip fragment config objects for needsFormat check (key injection is optional)\n if (callbackContext.isFragmentConfig) return;\n\n const objectStart = converter.byteOffsetToCharIndex(object.span.start - spanOffset);\n if (!hasExistingNewline(sourceCode, objectStart)) {\n needsFormatting = true;\n }\n });\n\n // Check tagged templates\n if (!needsFormatting) {\n const graphqlResult = getGraphqlModule();\n if (graphqlResult.isErr()) {\n return err(graphqlResult.error);\n }\n const { parse: parseGraphql, print: printGraphql } = graphqlResult.value;\n\n const positionCtx: PositionTrackingContext = { spanOffset, converter };\n const templates = walkAndExtract(module as unknown as Node, gqlIdentifiers, positionCtx);\n\n if (templates.length > 0) {\n const defaultFormat: FormatGraphqlFn = (source) => {\n const ast = parseGraphql(source, { noLocation: false });\n return printGraphql(ast);\n };\n\n const templateEdits = formatTemplatesInSource(templates, sourceCode, defaultFormat);\n if (templateEdits.length > 0) {\n needsFormatting = true;\n }\n }\n }\n\n return ok(needsFormatting);\n};\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- const require_chunk = require('./chunk-BrXtsOCC.cjs');
1
+ const require_codegen = require('./codegen.cjs');
2
2
  let neverthrow = require("neverthrow");
3
3
  let node_crypto = require("node:crypto");
4
4
  let node_module = require("node:module");
package/dist/typegen.cjs CHANGED
@@ -1,15 +1,11 @@
1
- const require_chunk = require('./chunk-BrXtsOCC.cjs');
1
+ const require_codegen = require('./codegen.cjs');
2
2
  let neverthrow = require("neverthrow");
3
3
  let __soda_gql_core = require("@soda-gql/core");
4
4
  let graphql = require("graphql");
5
5
  let node_fs = require("node:fs");
6
6
  let node_path = require("node:path");
7
- let __soda_gql_common_template_extraction = require("@soda-gql/common/template-extraction");
8
- let __swc_core = require("@swc/core");
9
7
  let node_fs_promises = require("node:fs/promises");
10
8
  let __soda_gql_builder = require("@soda-gql/builder");
11
- let fast_glob = require("fast-glob");
12
- fast_glob = require_chunk.__toESM(fast_glob);
13
9
 
14
10
  //#region packages/tools/src/typegen/emitter.ts
15
11
  /**
@@ -380,313 +376,6 @@ const formatTypegenError = (error) => {
380
376
  return lines.join("\n");
381
377
  };
382
378
 
383
- //#endregion
384
- //#region packages/tools/src/typegen/template-extractor.ts
385
- /**
386
- * Parse TypeScript source with SWC, returning null on failure.
387
- */
388
- const safeParseSync = (source, tsx) => {
389
- try {
390
- return (0, __swc_core.parseSync)(source, {
391
- syntax: "typescript",
392
- tsx,
393
- decorators: false,
394
- dynamicImport: true
395
- });
396
- } catch {
397
- return null;
398
- }
399
- };
400
- /**
401
- * Collect gql identifiers from import declarations.
402
- * Finds imports like `import { gql } from "./graphql-system"`.
403
- */
404
- const collectGqlIdentifiers = (module$1, filePath, helper) => {
405
- const identifiers = new Set();
406
- for (const item of module$1.body) {
407
- let declaration = null;
408
- if (item.type === "ImportDeclaration") {
409
- declaration = item;
410
- } else if ("declaration" in item && item.declaration && item.declaration.type === "ImportDeclaration") {
411
- declaration = item.declaration;
412
- }
413
- if (!declaration) {
414
- continue;
415
- }
416
- if (!helper.isGraphqlSystemImportSpecifier({
417
- filePath,
418
- specifier: declaration.source.value
419
- })) {
420
- continue;
421
- }
422
- for (const specifier of declaration.specifiers ?? []) {
423
- if (specifier.type === "ImportSpecifier") {
424
- const imported = specifier.imported ? specifier.imported.value : specifier.local.value;
425
- if (imported === "gql" && !specifier.imported) {
426
- identifiers.add(specifier.local.value);
427
- }
428
- }
429
- }
430
- }
431
- return identifiers;
432
- };
433
- /**
434
- * Extract all tagged templates from a TypeScript source file.
435
- *
436
- * @param filePath - Absolute path to the source file (used for import resolution)
437
- * @param source - TypeScript source code
438
- * @param helper - GraphQL system identifier for resolving gql imports
439
- * @returns Extracted templates and any warnings
440
- */
441
- const extractTemplatesFromSource = (filePath, source, helper) => {
442
- const warnings = [];
443
- const isTsx = filePath.endsWith(".tsx");
444
- const program = safeParseSync(source, isTsx);
445
- if (!program || program.type !== "Module") {
446
- if (source.includes("gql")) {
447
- warnings.push(`[typegen-extract] Failed to parse ${filePath}`);
448
- }
449
- return {
450
- templates: [],
451
- warnings
452
- };
453
- }
454
- const gqlIdentifiers = collectGqlIdentifiers(program, filePath, helper);
455
- if (gqlIdentifiers.size === 0) {
456
- return {
457
- templates: [],
458
- warnings
459
- };
460
- }
461
- return {
462
- templates: (0, __soda_gql_common_template_extraction.walkAndExtract)(program, gqlIdentifiers),
463
- warnings
464
- };
465
- };
466
-
467
- //#endregion
468
- //#region packages/tools/src/typegen/template-scanner.ts
469
- /**
470
- * Source file scanner for tagged template extraction.
471
- *
472
- * Discovers source files from config include/exclude patterns,
473
- * reads them, and extracts tagged templates using the template extractor.
474
- *
475
- * @module
476
- */
477
- /**
478
- * Scan source files for tagged templates.
479
- *
480
- * Uses fast-glob to discover files matching include/exclude patterns,
481
- * then extracts tagged templates from each file.
482
- */
483
- const scanSourceFiles = (options) => {
484
- const { include, exclude, baseDir, helper } = options;
485
- const warnings = [];
486
- const ignorePatterns = exclude.map((pattern) => pattern.startsWith("!") ? pattern.slice(1) : pattern);
487
- const matchedFiles = fast_glob.default.sync(include, {
488
- cwd: baseDir,
489
- ignore: ignorePatterns,
490
- onlyFiles: true,
491
- absolute: true
492
- });
493
- const templates = new Map();
494
- for (const filePath of matchedFiles) {
495
- const normalizedPath = (0, node_path.normalize)((0, node_path.resolve)(filePath)).replace(/\\/g, "/");
496
- try {
497
- const source = (0, node_fs.readFileSync)(normalizedPath, "utf-8");
498
- const { templates: extracted, warnings: extractionWarnings } = extractTemplatesFromSource(normalizedPath, source, helper);
499
- warnings.push(...extractionWarnings);
500
- if (extracted.length > 0) {
501
- templates.set(normalizedPath, extracted);
502
- }
503
- } catch (error) {
504
- const message = error instanceof Error ? error.message : String(error);
505
- warnings.push(`[typegen-scan] Failed to read ${normalizedPath}: ${message}`);
506
- }
507
- }
508
- return {
509
- templates,
510
- warnings
511
- };
512
- };
513
-
514
- //#endregion
515
- //#region packages/tools/src/typegen/template-to-selections.ts
516
- /**
517
- * Convert extracted templates into field selections for the emitter.
518
- *
519
- * @param templates - Templates extracted from source files, keyed by file path
520
- * @param schemas - Loaded schema objects keyed by schema name
521
- * @returns Map of canonical IDs to field selection data, plus any warnings
522
- */
523
- const convertTemplatesToSelections = (templates, schemas) => {
524
- const selections = new Map();
525
- const warnings = [];
526
- const schemaIndexes = new Map(Object.entries(schemas).map(([name, schema]) => [name, (0, __soda_gql_core.createSchemaIndexFromSchema)(schema)]));
527
- for (const [filePath, fileTemplates] of templates) {
528
- for (const template of fileTemplates) {
529
- const schema = schemas[template.schemaName];
530
- if (!schema) {
531
- warnings.push(`[typegen-template] Unknown schema "${template.schemaName}" in ${filePath}`);
532
- continue;
533
- }
534
- const schemaIndex = schemaIndexes.get(template.schemaName);
535
- if (!schemaIndex) {
536
- continue;
537
- }
538
- try {
539
- if (template.kind === "fragment") {
540
- const selection = convertFragmentTemplate(template, schema, filePath);
541
- if (selection) {
542
- selections.set(selection.id, selection.data);
543
- }
544
- } else {
545
- const selection = convertOperationTemplate(template, schema, filePath);
546
- if (selection) {
547
- selections.set(selection.id, selection.data);
548
- }
549
- }
550
- } catch (error) {
551
- const message = error instanceof Error ? error.message : String(error);
552
- warnings.push(`[typegen-template] Failed to process ${template.kind} in ${filePath}: ${message}`);
553
- }
554
- }
555
- }
556
- return {
557
- selections,
558
- warnings
559
- };
560
- };
561
- /**
562
- * Recursively filter out __FRAG_SPREAD_ placeholder nodes from a selection set.
563
- * These placeholders are created by template-extractor for interpolated fragment references.
564
- * buildFieldsFromSelectionSet would throw on them since no interpolationMap is available.
565
- */
566
- const filterPlaceholderSpreads = (selectionSet) => ({
567
- ...selectionSet,
568
- selections: selectionSet.selections.filter((sel) => !(sel.kind === graphql.Kind.FRAGMENT_SPREAD && sel.name.value.startsWith("__FRAG_SPREAD_"))).map((sel) => {
569
- if (sel.kind === graphql.Kind.FIELD && sel.selectionSet) {
570
- return {
571
- ...sel,
572
- selectionSet: filterPlaceholderSpreads(sel.selectionSet)
573
- };
574
- }
575
- if (sel.kind === graphql.Kind.INLINE_FRAGMENT && sel.selectionSet) {
576
- return {
577
- ...sel,
578
- selectionSet: filterPlaceholderSpreads(sel.selectionSet)
579
- };
580
- }
581
- return sel;
582
- })
583
- });
584
- /** Simple matching-paren finder for template content (no comments/strings to handle). */
585
- const findClosingParen = (source, openIndex) => {
586
- let depth = 0;
587
- for (let i = openIndex; i < source.length; i++) {
588
- if (source[i] === "(") depth++;
589
- else if (source[i] === ")") {
590
- depth--;
591
- if (depth === 0) return i;
592
- }
593
- }
594
- return -1;
595
- };
596
- /**
597
- * Reconstruct full GraphQL source from an extracted template.
598
- * For curried syntax (new), prepends the definition header from tag call arguments.
599
- * For curried fragments with Fragment Arguments, repositions variable declarations
600
- * before the on-clause to produce RFC-compliant syntax.
601
- * For old syntax, returns content as-is.
602
- */
603
- const reconstructGraphql = (template) => {
604
- if (template.elementName) {
605
- if (template.kind === "fragment" && template.typeName) {
606
- const trimmed = template.content.trim();
607
- if (trimmed.startsWith("(")) {
608
- const closeIdx = findClosingParen(trimmed, 0);
609
- if (closeIdx !== -1) {
610
- const varDecls = trimmed.slice(0, closeIdx + 1);
611
- const selectionSet = trimmed.slice(closeIdx + 1).trim();
612
- return `fragment ${template.elementName}${varDecls} on ${template.typeName} ${selectionSet}`;
613
- }
614
- }
615
- return `fragment ${template.elementName} on ${template.typeName} ${trimmed}`;
616
- }
617
- return `${template.kind} ${template.elementName} ${template.content}`;
618
- }
619
- return template.content;
620
- };
621
- /**
622
- * Convert a fragment template into FieldSelectionData.
623
- */
624
- const convertFragmentTemplate = (template, schema, filePath) => {
625
- const schemaIndex = (0, __soda_gql_core.createSchemaIndexFromSchema)(schema);
626
- const graphqlSource = reconstructGraphql(template);
627
- const variableDefinitions = (0, __soda_gql_core.extractFragmentVariables)(graphqlSource, schemaIndex);
628
- const { preprocessed } = (0, __soda_gql_core.preprocessFragmentArgs)(graphqlSource);
629
- const document = (0, graphql.parse)(preprocessed);
630
- const fragDef = document.definitions.find((d) => d.kind === graphql.Kind.FRAGMENT_DEFINITION);
631
- if (!fragDef || fragDef.kind !== graphql.Kind.FRAGMENT_DEFINITION) {
632
- return null;
633
- }
634
- const fragmentName = fragDef.name.value;
635
- const onType = fragDef.typeCondition.name.value;
636
- const fields = (0, __soda_gql_core.buildFieldsFromSelectionSet)(filterPlaceholderSpreads(fragDef.selectionSet), schema, onType);
637
- const id = `${filePath}::${fragmentName}`;
638
- return {
639
- id,
640
- data: {
641
- type: "fragment",
642
- schemaLabel: schema.label,
643
- key: fragmentName,
644
- typename: onType,
645
- fields,
646
- variableDefinitions
647
- }
648
- };
649
- };
650
- /**
651
- * Convert an operation template into FieldSelectionData.
652
- */
653
- const convertOperationTemplate = (template, schema, filePath) => {
654
- const graphqlSource = reconstructGraphql(template);
655
- const document = (0, graphql.parse)(graphqlSource);
656
- const opDef = document.definitions.find((d) => d.kind === graphql.Kind.OPERATION_DEFINITION);
657
- if (!opDef || opDef.kind !== graphql.Kind.OPERATION_DEFINITION) {
658
- return null;
659
- }
660
- const operationName = opDef.name?.value ?? "Anonymous";
661
- const operationType = opDef.operation;
662
- const rootTypeName = getRootTypeName(schema, operationType);
663
- const fields = (0, __soda_gql_core.buildFieldsFromSelectionSet)(filterPlaceholderSpreads(opDef.selectionSet), schema, rootTypeName);
664
- const variableDefinitions = opDef.variableDefinitions ?? [];
665
- const id = `${filePath}::${operationName}`;
666
- return {
667
- id,
668
- data: {
669
- type: "operation",
670
- schemaLabel: schema.label,
671
- operationName,
672
- operationType,
673
- fields,
674
- variableDefinitions: [...variableDefinitions]
675
- }
676
- };
677
- };
678
- /**
679
- * Get the root type name for an operation type from the schema.
680
- */
681
- const getRootTypeName = (schema, operationType) => {
682
- switch (operationType) {
683
- case "query": return schema.operations.query ?? "Query";
684
- case "mutation": return schema.operations.mutation ?? "Mutation";
685
- case "subscription": return schema.operations.subscription ?? "Subscription";
686
- default: return "Query";
687
- }
688
- };
689
-
690
379
  //#endregion
691
380
  //#region packages/tools/src/typegen/runner.ts
692
381
  /**
@@ -696,8 +385,7 @@ const getRootTypeName = (schema, operationType) => {
696
385
  * 1. Load schemas from generated CJS bundle
697
386
  * 2. Build artifact to evaluate elements
698
387
  * 3. Extract field selections from builder
699
- * 4. Scan source files for tagged templates and merge selections
700
- * 5. Emit types.prebuilt.ts
388
+ * 4. Emit types.prebuilt.ts
701
389
  *
702
390
  * @module
703
391
  */
@@ -745,8 +433,7 @@ const toImportSpecifier = (fromPath, targetPath, options) => {
745
433
  * 1. Loads schemas from the generated CJS bundle
746
434
  * 2. Creates a BuilderService and builds the artifact
747
435
  * 3. Extracts field selections from the artifact
748
- * 4. Scans source files for tagged templates and merges selections
749
- * 5. Emits types.prebuilt.ts using emitPrebuiltTypes
436
+ * 4. Emits types.prebuilt.ts using emitPrebuiltTypes
750
437
  *
751
438
  * @param options - Typegen options including config
752
439
  * @returns Result containing success data or error
@@ -778,16 +465,7 @@ const runTypegen = async (options) => {
778
465
  }
779
466
  const fieldSelectionsResult = (0, __soda_gql_builder.extractFieldSelections)(intermediateElements);
780
467
  const { selections: builderSelections, warnings: extractWarnings } = fieldSelectionsResult;
781
- const graphqlHelper = (0, __soda_gql_builder.createGraphqlSystemIdentifyHelper)(config);
782
- const scanResult = scanSourceFiles({
783
- include: [...config.include],
784
- exclude: [...config.exclude],
785
- baseDir: config.baseDir,
786
- helper: graphqlHelper
787
- });
788
- const templateSelections = convertTemplatesToSelections(scanResult.templates, schemas);
789
- const fieldSelections = mergeSelections(builderSelections, templateSelections.selections, config.baseDir);
790
- const scanWarnings = [...scanResult.warnings, ...templateSelections.warnings];
468
+ const fieldSelections = new Map(builderSelections);
791
469
  const emitResult = await emitPrebuiltTypes({
792
470
  schemas,
793
471
  fieldSelections,
@@ -807,11 +485,7 @@ const runTypegen = async (options) => {
807
485
  operationCount++;
808
486
  }
809
487
  }
810
- const allWarnings = [
811
- ...extractWarnings,
812
- ...scanWarnings,
813
- ...emitWarnings
814
- ];
488
+ const allWarnings = [...extractWarnings, ...emitWarnings];
815
489
  return (0, neverthrow.ok)({
816
490
  prebuiltTypesPath,
817
491
  fragmentCount,
@@ -820,41 +494,6 @@ const runTypegen = async (options) => {
820
494
  warnings: allWarnings
821
495
  });
822
496
  };
823
- const extractElementName = (data) => data.type === "fragment" ? data.key : data.operationName;
824
- /**
825
- * Merge builder and template selections into a combined map.
826
- *
827
- * Builder selections are authoritative — VM evaluation correctly resolves
828
- * fragment spreads that static template analysis cannot handle.
829
- * Template selections serve as fallback for elements only found by the scanner.
830
- *
831
- * Deduplication is per-element (file + GraphQL name), not per-file, so that
832
- * callback-builder operations in files that also contain tagged templates are preserved.
833
- */
834
- const mergeSelections = (builderSelections, templateSelections, baseDir) => {
835
- const extractFilePart = (id) => {
836
- const filePart = id.split("::")[0] ?? "";
837
- if (filePart.startsWith("/")) {
838
- return (0, node_path.relative)(baseDir, filePart);
839
- }
840
- return filePart;
841
- };
842
- const builderElements = new Set();
843
- for (const [id, data] of builderSelections) {
844
- const name = extractElementName(data);
845
- if (name) builderElements.add(`${extractFilePart(id)}::${name}`);
846
- }
847
- const fieldSelections = new Map();
848
- for (const [id, data] of builderSelections) {
849
- fieldSelections.set(id, data);
850
- }
851
- for (const [id, data] of templateSelections) {
852
- const name = extractElementName(data);
853
- if (name && builderElements.has(`${extractFilePart(id)}::${name}`)) continue;
854
- fieldSelections.set(id, data);
855
- }
856
- return fieldSelections;
857
- };
858
497
 
859
498
  //#endregion
860
499
  exports.emitPrebuiltTypes = emitPrebuiltTypes;