@soda-gql/common 0.12.5 → 0.12.6

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index-DO6j6Cif.d.cts","names":[],"sources":["../src/zod/schema-helper.ts"],"sourcesContent":[],"mappings":";;;KAGY,qBAAqB,CAAA,CAAE,QAAQ;KAE/B,2CAFA,MAEiD,OAFxC,KAEoD,SAF9B,CAEwC,OAFlD,CAE0D,CAFxD,CAAA,CAAA,EAEnC;AAA6D,iBAE7C,eAF6C,CAAA,gBAAA,MAAA,CAAA,CAAA,CAAA,EAAA,CAAA,eAGpC,QAHoC,CAG3B,OAH2B,CAGnB,OAHmB,CAAA,CAAA,CAAA,CAAA,KAAA,EAGD,MAHC,GAAA,QAGgB,OAHhB,CAAA,MAG8B,MAH9B,EAAA,MAG4C,OAH5C,CAAA,GAAA,KAAA,EAAA,EAAA,GAG8D,CAAA,CAAA,SAH9D,CAG8D,MAH9D,GAAA,QAGY,OAHZ,CAAA,MAGY,MAHZ,EAAA,MAGY,OAHZ,CAAA,GAAA,KAAA,EAAA,SAAA,KAAA,EAAA,GAAA,kBAAA,MAGY,CAHZ,GAGY,CAHZ,CAGY,CAHZ,CAAA,EAAA,GAAA,KAAA,EAGY,CAAA,CAAA,IAAA,CAAA,OAHZ,CAAA"}
1
+ {"version":3,"file":"index-DO6j6Cif.d.cts","names":[],"sources":["../src/zod/schema-helper.ts"],"sourcesContent":[],"mappings":";;;KAGY,qBAAqB,CAAA,CAAE,QAAQ;KAE/B,2CAFA,MAEiD,OAFxC,KAEoD,SAF9B,CAEwC,OAFhD,CAEwD,CAFxD,CAAA,CAAA,EAEnC;AAA6D,iBAE7C,eAF6C,CAAA,gBAAA,MAAA,CAAA,CAAA,CAAA,EAAA,CAAA,eAGpC,QAHoC,CAG3B,OAH2B,CAGnB,OAHmB,CAAA,CAAA,CAAA,CAAA,KAAA,EAGD,MAHC,GAAA,QAGgB,OAHhB,CAAA,MAG8B,MAH9B,EAAA,MAG4C,OAH5C,CAAA,GAAA,KAAA,EAAA,EAAA,GAG8D,CAAA,CAAA,SAH9D,CAG8D,MAH9D,GAAA,QAGY,OAHZ,CAAA,MAGY,MAHZ,EAAA,MAGY,OAHZ,CAAA,GAAA,KAAA,EAAA,SAAA,KAAA,EAAA,GAAA,kBAAA,MAGY,CAHZ,GAGY,CAHZ,CAGY,CAHZ,CAAA,EAAA,GAAA,KAAA,EAGY,CAAA,CAAA,IAAA,CAAA,OAHZ,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"index-DeJfMmkU.d.mts","names":[],"sources":["../src/zod/schema-helper.ts"],"sourcesContent":[],"mappings":";;;KAGY,qBAAqB,CAAA,CAAE,QAAQ;KAE/B,2CAFA,MAEiD,OAFxC,KAEoD,SAF9B,CAEwC,OAFlD,CAE0D,CAFxD,CAAA,CAAA,EAEnC;AAA6D,iBAE7C,eAF6C,CAAA,gBAAA,MAAA,CAAA,CAAA,CAAA,EAAA,CAAA,eAGpC,QAHoC,CAG3B,OAH2B,CAGnB,OAHmB,CAAA,CAAA,CAAA,CAAA,KAAA,EAGD,MAHC,GAAA,QAGgB,OAHhB,CAAA,MAG8B,MAH9B,EAAA,MAG4C,OAH5C,CAAA,GAAA,KAAA,EAAA,EAAA,GAG8D,CAAA,CAAA,SAH9D,CAG8D,MAH9D,GAAA,QAGY,OAHZ,CAAA,MAGY,MAHZ,EAAA,MAGY,OAHZ,CAAA,GAAA,KAAA,EAAA,SAAA,KAAA,EAAA,GAAA,kBAAA,MAGY,CAHZ,GAGY,CAHZ,CAGY,CAHZ,CAAA,EAAA,GAAA,KAAA,EAGY,CAAA,CAAA,IAAA,CAAA,OAHZ,CAAA"}
1
+ {"version":3,"file":"index-DeJfMmkU.d.mts","names":[],"sources":["../src/zod/schema-helper.ts"],"sourcesContent":[],"mappings":";;;KAGY,qBAAqB,CAAA,CAAE,QAAQ;KAE/B,2CAFA,MAEiD,OAFxC,KAEoD,SAF9B,CAEwC,OAFhD,CAEwD,CAFxD,CAAA,CAAA,EAEnC;AAA6D,iBAE7C,eAF6C,CAAA,gBAAA,MAAA,CAAA,CAAA,CAAA,EAAA,CAAA,eAGpC,QAHoC,CAG3B,OAH2B,CAGnB,OAHmB,CAAA,CAAA,CAAA,CAAA,KAAA,EAGD,MAHC,GAAA,QAGgB,OAHhB,CAAA,MAG8B,MAH9B,EAAA,MAG4C,OAH5C,CAAA,GAAA,KAAA,EAAA,EAAA,GAG8D,CAAA,CAAA,SAH9D,CAG8D,MAH9D,GAAA,QAGY,OAHZ,CAAA,MAGY,MAHZ,EAAA,MAGY,OAHZ,CAAA,GAAA,KAAA,EAAA,SAAA,KAAA,EAAA,GAAA,kBAAA,MAGY,CAHZ,GAGY,CAHZ,CAGY,CAHZ,CAAA,EAAA,GAAA,KAAA,EAGY,CAAA,CAAA,IAAA,CAAA,OAHZ,CAAA"}
@@ -226,7 +226,7 @@ const reindent = (formatted, baseIndent, originalContent) => {
226
226
  const lines = trimmedFormatted.split("\n");
227
227
  const indentedLines = lines.map((line) => line.trim() === "" ? "" : indent + line);
228
228
  const startsWithNewline = originalContent.startsWith("\n");
229
- const endsWithNewline = originalContent.endsWith("\n");
229
+ const endsWithNewline = /\n\s*$/.test(originalContent);
230
230
  let result = indentedLines.join("\n");
231
231
  if (startsWithNewline) {
232
232
  result = `\n${result}`;
@@ -1 +1 @@
1
- {"version":3,"file":"template-extraction.cjs","names":["templates: ExtractedTemplate[]","kind: string","elementName: string | undefined","typeName: string | undefined","expressionRanges: { start: number; end: number }[]","parts: string[]","template: ExtractedTemplate","prefix","prefixPattern","edits: TemplateFormatEdit[]","formatted: string"],"sources":["../src/template-extraction/extract.ts","../src/template-extraction/format.ts"],"sourcesContent":["/**\n * Template extraction from TypeScript source using SWC AST.\n *\n * Based on the typegen extractor (superset): supports both bare-tag\n * (`query\\`...\\``) and curried (`query(\"Name\")\\`...\\``) syntax.\n *\n * Position tracking is optional — pass spanOffset + converter to\n * populate contentRange on extracted templates.\n *\n * @module\n */\n\n// Re-export for convenience — SWC types are type-only imports\nimport type { ArrowFunctionExpression, CallExpression, MemberExpression, Node, TaggedTemplateExpression } from \"@swc/types\";\nimport type { SwcSpanConverter } from \"../utils/swc-span\";\nimport type { ExtractedTemplate, ExtractedTemplateWithPosition, OperationKind } from \"./types\";\n\nexport const OPERATION_KINDS = new Set<string>([\"query\", \"mutation\", \"subscription\", \"fragment\"]);\n\nexport const isOperationKind = (value: string): value is OperationKind => OPERATION_KINDS.has(value);\n\n/** Optional position tracking context for extraction. */\nexport type PositionTrackingContext = {\n readonly spanOffset: number;\n readonly converter: SwcSpanConverter;\n};\n\n/**\n * Check if a call expression is a gql.{schemaName}(...) call.\n * Returns the schema name if it is, null otherwise.\n */\nexport const getGqlCallSchemaName = (identifiers: ReadonlySet<string>, call: CallExpression): string | null => {\n const callee = call.callee;\n if (callee.type !== \"MemberExpression\") {\n return null;\n }\n\n const member = callee as MemberExpression;\n if (member.object.type !== \"Identifier\" || !identifiers.has(member.object.value)) {\n return null;\n }\n\n if (member.property.type !== \"Identifier\") {\n return null;\n }\n\n const firstArg = call.arguments[0];\n if (!firstArg?.expression || firstArg.expression.type !== \"ArrowFunctionExpression\") {\n return null;\n }\n\n return member.property.value;\n};\n\n/**\n * Extract templates from a gql callback's arrow function body.\n * Handles both expression bodies and block bodies with return statements.\n */\nexport const extractTemplatesFromCallback = (\n arrow: ArrowFunctionExpression,\n schemaName: string,\n positionCtx?: PositionTrackingContext,\n): ExtractedTemplate[] => {\n const templates: ExtractedTemplate[] = [];\n\n const processExpression = (expr: Node): void => {\n // Direct tagged template: query(\"Name\")`...`\n if (expr.type === \"TaggedTemplateExpression\") {\n const tagged = expr as unknown as TaggedTemplateExpression;\n extractFromTaggedTemplate(tagged, schemaName, templates, positionCtx);\n return;\n }\n\n // Metadata chaining: query(\"Name\")`...`({ metadata: {} })\n if (expr.type === \"CallExpression\") {\n const call = expr as unknown as CallExpression;\n if (call.callee.type === \"TaggedTemplateExpression\") {\n extractFromTaggedTemplate(call.callee as TaggedTemplateExpression, schemaName, templates, positionCtx);\n }\n }\n };\n\n // Expression body: ({ query }) => query(\"Name\")`...`\n if (arrow.body.type !== \"BlockStatement\") {\n processExpression(arrow.body);\n return templates;\n }\n\n // Block body: ({ query }) => { return query(\"Name\")`...`; }\n for (const stmt of arrow.body.stmts) {\n if (stmt.type === \"ReturnStatement\" && stmt.argument) {\n processExpression(stmt.argument);\n }\n }\n\n return templates;\n};\n\n/**\n * Extract a single template from a tagged template expression.\n * Supports both bare-tag (Identifier) and curried (CallExpression) tag forms.\n */\nexport const extractFromTaggedTemplate = (\n tagged: TaggedTemplateExpression,\n schemaName: string,\n templates: ExtractedTemplate[],\n positionCtx?: PositionTrackingContext,\n): void => {\n // Tag can be:\n // - CallExpression: query(\"name\")`...` or fragment(\"name\", \"type\")`...` (curried syntax)\n // - Identifier: legacy bare-tag form (skipped if it contains interpolations)\n let kind: string;\n let elementName: string | undefined;\n let typeName: string | undefined;\n\n if (tagged.tag.type === \"Identifier\") {\n kind = tagged.tag.value;\n } else if (tagged.tag.type === \"CallExpression\") {\n const tagCall = tagged.tag as CallExpression;\n if (tagCall.callee.type === \"Identifier\") {\n kind = tagCall.callee.value;\n } else {\n return;\n }\n // Extract elementName and typeName from call arguments\n const firstArg = tagCall.arguments[0]?.expression;\n if (firstArg?.type === \"StringLiteral\") {\n elementName = (firstArg as { value: string }).value;\n }\n const secondArg = tagCall.arguments[1]?.expression;\n if (secondArg?.type === \"StringLiteral\") {\n typeName = (secondArg as { value: string }).value;\n }\n } else {\n return;\n }\n\n if (!isOperationKind(kind)) {\n return;\n }\n\n const { quasis, expressions } = tagged.template;\n\n // For legacy Identifier tag, skip templates with interpolations\n if (tagged.tag.type === \"Identifier\" && expressions.length > 0) {\n return;\n }\n\n if (quasis.length === 0) {\n return;\n }\n\n // Build content and optionally track position\n let contentStart = -1;\n let contentEnd = -1;\n const expressionRanges: { start: number; end: number }[] = [];\n\n const parts: string[] = [];\n for (let i = 0; i < quasis.length; i++) {\n const quasi = quasis[i];\n if (!quasi) continue;\n\n if (positionCtx) {\n const quasiStart = positionCtx.converter.byteOffsetToCharIndex(quasi.span.start - positionCtx.spanOffset);\n const quasiEnd = positionCtx.converter.byteOffsetToCharIndex(quasi.span.end - positionCtx.spanOffset);\n if (contentStart === -1) contentStart = quasiStart;\n contentEnd = quasiEnd;\n }\n\n parts.push(quasi.cooked ?? quasi.raw);\n if (i < expressions.length) {\n parts.push(`__FRAG_SPREAD_${i}__`);\n if (positionCtx) {\n // All SWC AST nodes have span; cast needed because Expression union type doesn't expose it uniformly\n const expr = expressions[i] as unknown as { span: { start: number; end: number } };\n const exprStart = positionCtx.converter.byteOffsetToCharIndex(expr.span.start - positionCtx.spanOffset);\n const exprEnd = positionCtx.converter.byteOffsetToCharIndex(expr.span.end - positionCtx.spanOffset);\n expressionRanges.push({ start: exprStart, end: exprEnd });\n }\n }\n }\n const content = parts.join(\"\");\n\n const template: ExtractedTemplate = {\n schemaName,\n kind,\n content,\n ...(elementName !== undefined ? { elementName } : {}),\n ...(typeName !== undefined ? { typeName } : {}),\n ...(positionCtx && contentStart !== -1 && contentEnd !== -1\n ? { contentRange: { start: contentStart, end: contentEnd } }\n : {}),\n ...(expressionRanges.length > 0 ? { expressionRanges } : {}),\n };\n\n templates.push(template);\n};\n\n/**\n * Find the innermost gql call, unwrapping method chains like .attach().\n */\nexport const findGqlCall = (identifiers: ReadonlySet<string>, node: Node): CallExpression | null => {\n if (!node || node.type !== \"CallExpression\") {\n return null;\n }\n\n const call = node as unknown as CallExpression;\n if (getGqlCallSchemaName(identifiers, call) !== null) {\n return call;\n }\n\n const callee = call.callee;\n if (callee.type !== \"MemberExpression\") {\n return null;\n }\n\n return findGqlCall(identifiers, callee.object as unknown as Node);\n};\n\n/**\n * Walk AST to find gql calls and extract templates.\n */\nexport function walkAndExtract(\n node: Node,\n identifiers: ReadonlySet<string>,\n positionCtx: PositionTrackingContext,\n): ExtractedTemplateWithPosition[];\nexport function walkAndExtract(\n node: Node,\n identifiers: ReadonlySet<string>,\n positionCtx?: PositionTrackingContext,\n): ExtractedTemplate[];\nexport function walkAndExtract(\n node: Node,\n identifiers: ReadonlySet<string>,\n positionCtx?: PositionTrackingContext,\n): ExtractedTemplate[] {\n const templates: ExtractedTemplate[] = [];\n\n const visit = (n: Node | ReadonlyArray<Node> | Record<string, unknown>): void => {\n if (!n || typeof n !== \"object\") {\n return;\n }\n\n if (\"type\" in n && n.type === \"CallExpression\") {\n const gqlCall = findGqlCall(identifiers, n as Node);\n if (gqlCall) {\n const schemaName = getGqlCallSchemaName(identifiers, gqlCall);\n if (schemaName) {\n const arrow = gqlCall.arguments[0]?.expression as ArrowFunctionExpression;\n templates.push(...extractTemplatesFromCallback(arrow, schemaName, positionCtx));\n }\n return; // Don't recurse into gql calls\n }\n }\n\n // Recurse into all array and object properties\n if (Array.isArray(n)) {\n for (const item of n) {\n visit(item as Node);\n }\n return;\n }\n\n for (const key of Object.keys(n)) {\n if (key === \"span\" || key === \"type\") {\n continue;\n }\n const value = (n as Record<string, unknown>)[key];\n if (value && typeof value === \"object\") {\n visit(value as Node);\n }\n }\n };\n\n visit(node);\n return templates;\n}\n","/**\n * GraphQL template formatting utilities.\n *\n * Pure string operations — no dependency on `graphql` package.\n * Consumers provide their own format function (e.g., graphql-js parse/print).\n *\n * @module\n */\n\nimport type { ExtractedTemplate, TemplateFormatEdit } from \"./types\";\n\n/** A function that formats GraphQL source text. */\nexport type FormatGraphqlFn = (source: string) => string;\n\n/**\n * Detect the base indentation for a template by looking at the line\n * containing the opening backtick.\n */\nexport const detectBaseIndent = (tsSource: string, contentStartOffset: number): string => {\n // Find the start of the line containing contentStartOffset\n let lineStart = contentStartOffset;\n while (lineStart > 0 && tsSource.charCodeAt(lineStart - 1) !== 10) {\n lineStart--;\n }\n\n // Extract leading whitespace from that line\n let i = lineStart;\n while (i < tsSource.length && (tsSource.charCodeAt(i) === 32 || tsSource.charCodeAt(i) === 9)) {\n i++;\n }\n\n return tsSource.slice(lineStart, i);\n};\n\n/**\n * Re-indent formatted GraphQL to match the embedding context.\n *\n * - If original was single-line and formatted is single-line, keep as-is\n * - Otherwise, apply base indent + one level to each line, preserving\n * original leading/trailing newline pattern\n */\nexport const reindent = (formatted: string, baseIndent: string, originalContent: string): string => {\n const trimmedFormatted = formatted.trim();\n\n // If original was single-line and formatted is also single-line, keep it\n if (!originalContent.includes(\"\\n\") && !trimmedFormatted.includes(\"\\n\")) {\n return trimmedFormatted;\n }\n\n // For multi-line: use the indentation pattern from the original content\n const indent = `${baseIndent} `; // add one level of indentation\n const lines = trimmedFormatted.split(\"\\n\");\n const indentedLines = lines.map((line) => (line.trim() === \"\" ? \"\" : indent + line));\n\n // Match original leading/trailing newline pattern\n const startsWithNewline = originalContent.startsWith(\"\\n\");\n const endsWithNewline = originalContent.endsWith(\"\\n\");\n\n let result = indentedLines.join(\"\\n\");\n if (startsWithNewline) {\n result = `\\n${result}`;\n }\n if (endsWithNewline) {\n result = `${result}\\n${baseIndent}`;\n }\n\n return result;\n};\n\nconst GRAPHQL_KEYWORDS = new Set([\"query\", \"mutation\", \"subscription\", \"fragment\"]);\n\n/**\n * Reconstruct a full GraphQL document from template content + metadata.\n *\n * For curried syntax, the template `content` is only the body (e.g., `{ user { id } }`).\n * GraphQL parsers need a full document (e.g., `query GetUser { user { id } }`).\n *\n * For bare-tag syntax, the content already starts with a keyword and is a full document.\n *\n * @returns The wrapped source and a regex pattern to strip the synthetic prefix after formatting\n */\nexport const buildGraphqlWrapper = (template: ExtractedTemplate): { wrapped: string; prefixPattern: RegExp | null } => {\n const content = template.content.trimStart();\n const firstWord = content.split(/[\\s({]/)[0] ?? \"\";\n\n // If content already starts with a GraphQL keyword, it's a full document (bare-tag)\n if (GRAPHQL_KEYWORDS.has(firstWord)) {\n return { wrapped: template.content, prefixPattern: null };\n }\n\n // Curried syntax — reconstruct the header\n if (template.elementName) {\n if (template.kind === \"fragment\" && template.typeName) {\n const prefix = `fragment ${template.elementName} on ${template.typeName} `;\n const prefixPattern = new RegExp(`^fragment\\\\s+${template.elementName}\\\\s+on\\\\s+${template.typeName}\\\\s*`);\n return { wrapped: prefix + template.content, prefixPattern };\n }\n const prefix = `${template.kind} ${template.elementName} `;\n const prefixPattern = new RegExp(`^${template.kind}\\\\s+${template.elementName}\\\\s*`);\n return { wrapped: prefix + template.content, prefixPattern };\n }\n\n // No elementName — try wrapping with just the kind\n const prefix = `${template.kind} `;\n const prefixPattern = new RegExp(`^${template.kind}\\\\s*`);\n return { wrapped: prefix + template.content, prefixPattern };\n};\n\n/**\n * Strip the reconstructed prefix from formatted output to get back the template body.\n * Uses regex pattern matching to handle whitespace normalization by the formatter\n * (e.g., `query Foo ($id: ID!)` → `query Foo($id: ID!)`).\n */\nexport const unwrapFormattedContent = (formatted: string, prefixPattern: RegExp | null): string => {\n if (!prefixPattern) return formatted;\n const match = formatted.match(prefixPattern);\n if (!match) return formatted;\n return formatted.slice(match[0].length);\n};\n\n/**\n * Format GraphQL templates within TypeScript source.\n *\n * Applies the given format function to each template's content, handles\n * wrapper reconstruction for curried syntax, and re-indents the result\n * to match the TypeScript embedding context.\n *\n * @returns Array of edits to apply to the source (sorted by position, not yet applied)\n */\nexport const formatTemplatesInSource = (\n templates: readonly ExtractedTemplate[],\n tsSource: string,\n formatGraphql: FormatGraphqlFn,\n): readonly TemplateFormatEdit[] => {\n const edits: TemplateFormatEdit[] = [];\n\n for (const template of templates) {\n if (!template.contentRange) continue;\n\n // Wrap the content for formatting\n const { wrapped, prefixPattern } = buildGraphqlWrapper(template);\n\n let formatted: string;\n try {\n formatted = formatGraphql(wrapped);\n } catch {\n continue;\n }\n\n // Unwrap the prefix\n let unwrapped = unwrapFormattedContent(formatted, prefixPattern);\n\n // Restore interpolation expressions: replace __FRAG_SPREAD_N__ with original ${...} syntax\n if (template.expressionRanges && template.expressionRanges.length > 0) {\n for (let i = 0; i < template.expressionRanges.length; i++) {\n const range = template.expressionRanges[i]!;\n const exprText = tsSource.slice(range.start, range.end);\n unwrapped = unwrapped.replace(`__FRAG_SPREAD_${i}__`, `\\${${exprText}}`);\n }\n }\n\n // Fast path: skip if formatter produces identical output\n if (unwrapped === template.content) {\n continue;\n }\n\n // Detect base indentation from the TS source\n const baseIndent = detectBaseIndent(tsSource, template.contentRange.start);\n\n // Re-indent the formatted output\n const reindented = reindent(unwrapped, baseIndent, template.content);\n\n // Skip if no changes after re-indentation\n if (reindented === template.content) {\n continue;\n }\n\n edits.push({\n start: template.contentRange.start,\n end: template.contentRange.end,\n newText: reindented,\n });\n }\n\n return edits;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"template-extraction.cjs","names":["templates: ExtractedTemplate[]","kind: string","elementName: string | undefined","typeName: string | undefined","expressionRanges: { start: number; end: number }[]","parts: string[]","template: ExtractedTemplate","prefix","prefixPattern","edits: TemplateFormatEdit[]","formatted: string"],"sources":["../src/template-extraction/extract.ts","../src/template-extraction/format.ts"],"sourcesContent":["/**\n * Template extraction from TypeScript source using SWC AST.\n *\n * Based on the typegen extractor (superset): supports both bare-tag\n * (`query\\`...\\``) and curried (`query(\"Name\")\\`...\\``) syntax.\n *\n * Position tracking is optional — pass spanOffset + converter to\n * populate contentRange on extracted templates.\n *\n * @module\n */\n\n// Re-export for convenience — SWC types are type-only imports\nimport type { ArrowFunctionExpression, CallExpression, MemberExpression, Node, TaggedTemplateExpression } from \"@swc/types\";\nimport type { SwcSpanConverter } from \"../utils/swc-span\";\nimport type { ExtractedTemplate, ExtractedTemplateWithPosition, OperationKind } from \"./types\";\n\nexport const OPERATION_KINDS = new Set<string>([\"query\", \"mutation\", \"subscription\", \"fragment\"]);\n\nexport const isOperationKind = (value: string): value is OperationKind => OPERATION_KINDS.has(value);\n\n/** Optional position tracking context for extraction. */\nexport type PositionTrackingContext = {\n readonly spanOffset: number;\n readonly converter: SwcSpanConverter;\n};\n\n/**\n * Check if a call expression is a gql.{schemaName}(...) call.\n * Returns the schema name if it is, null otherwise.\n */\nexport const getGqlCallSchemaName = (identifiers: ReadonlySet<string>, call: CallExpression): string | null => {\n const callee = call.callee;\n if (callee.type !== \"MemberExpression\") {\n return null;\n }\n\n const member = callee as MemberExpression;\n if (member.object.type !== \"Identifier\" || !identifiers.has(member.object.value)) {\n return null;\n }\n\n if (member.property.type !== \"Identifier\") {\n return null;\n }\n\n const firstArg = call.arguments[0];\n if (!firstArg?.expression || firstArg.expression.type !== \"ArrowFunctionExpression\") {\n return null;\n }\n\n return member.property.value;\n};\n\n/**\n * Extract templates from a gql callback's arrow function body.\n * Handles both expression bodies and block bodies with return statements.\n */\nexport const extractTemplatesFromCallback = (\n arrow: ArrowFunctionExpression,\n schemaName: string,\n positionCtx?: PositionTrackingContext,\n): ExtractedTemplate[] => {\n const templates: ExtractedTemplate[] = [];\n\n const processExpression = (expr: Node): void => {\n // Direct tagged template: query(\"Name\")`...`\n if (expr.type === \"TaggedTemplateExpression\") {\n const tagged = expr as unknown as TaggedTemplateExpression;\n extractFromTaggedTemplate(tagged, schemaName, templates, positionCtx);\n return;\n }\n\n // Metadata chaining: query(\"Name\")`...`({ metadata: {} })\n if (expr.type === \"CallExpression\") {\n const call = expr as unknown as CallExpression;\n if (call.callee.type === \"TaggedTemplateExpression\") {\n extractFromTaggedTemplate(call.callee as TaggedTemplateExpression, schemaName, templates, positionCtx);\n }\n }\n };\n\n // Expression body: ({ query }) => query(\"Name\")`...`\n if (arrow.body.type !== \"BlockStatement\") {\n processExpression(arrow.body);\n return templates;\n }\n\n // Block body: ({ query }) => { return query(\"Name\")`...`; }\n for (const stmt of arrow.body.stmts) {\n if (stmt.type === \"ReturnStatement\" && stmt.argument) {\n processExpression(stmt.argument);\n }\n }\n\n return templates;\n};\n\n/**\n * Extract a single template from a tagged template expression.\n * Supports both bare-tag (Identifier) and curried (CallExpression) tag forms.\n */\nexport const extractFromTaggedTemplate = (\n tagged: TaggedTemplateExpression,\n schemaName: string,\n templates: ExtractedTemplate[],\n positionCtx?: PositionTrackingContext,\n): void => {\n // Tag can be:\n // - CallExpression: query(\"name\")`...` or fragment(\"name\", \"type\")`...` (curried syntax)\n // - Identifier: legacy bare-tag form (skipped if it contains interpolations)\n let kind: string;\n let elementName: string | undefined;\n let typeName: string | undefined;\n\n if (tagged.tag.type === \"Identifier\") {\n kind = tagged.tag.value;\n } else if (tagged.tag.type === \"CallExpression\") {\n const tagCall = tagged.tag as CallExpression;\n if (tagCall.callee.type === \"Identifier\") {\n kind = tagCall.callee.value;\n } else {\n return;\n }\n // Extract elementName and typeName from call arguments\n const firstArg = tagCall.arguments[0]?.expression;\n if (firstArg?.type === \"StringLiteral\") {\n elementName = (firstArg as { value: string }).value;\n }\n const secondArg = tagCall.arguments[1]?.expression;\n if (secondArg?.type === \"StringLiteral\") {\n typeName = (secondArg as { value: string }).value;\n }\n } else {\n return;\n }\n\n if (!isOperationKind(kind)) {\n return;\n }\n\n const { quasis, expressions } = tagged.template;\n\n // For legacy Identifier tag, skip templates with interpolations\n if (tagged.tag.type === \"Identifier\" && expressions.length > 0) {\n return;\n }\n\n if (quasis.length === 0) {\n return;\n }\n\n // Build content and optionally track position\n let contentStart = -1;\n let contentEnd = -1;\n const expressionRanges: { start: number; end: number }[] = [];\n\n const parts: string[] = [];\n for (let i = 0; i < quasis.length; i++) {\n const quasi = quasis[i];\n if (!quasi) continue;\n\n if (positionCtx) {\n const quasiStart = positionCtx.converter.byteOffsetToCharIndex(quasi.span.start - positionCtx.spanOffset);\n const quasiEnd = positionCtx.converter.byteOffsetToCharIndex(quasi.span.end - positionCtx.spanOffset);\n if (contentStart === -1) contentStart = quasiStart;\n contentEnd = quasiEnd;\n }\n\n parts.push(quasi.cooked ?? quasi.raw);\n if (i < expressions.length) {\n parts.push(`__FRAG_SPREAD_${i}__`);\n if (positionCtx) {\n // All SWC AST nodes have span; cast needed because Expression union type doesn't expose it uniformly\n const expr = expressions[i] as unknown as { span: { start: number; end: number } };\n const exprStart = positionCtx.converter.byteOffsetToCharIndex(expr.span.start - positionCtx.spanOffset);\n const exprEnd = positionCtx.converter.byteOffsetToCharIndex(expr.span.end - positionCtx.spanOffset);\n expressionRanges.push({ start: exprStart, end: exprEnd });\n }\n }\n }\n const content = parts.join(\"\");\n\n const template: ExtractedTemplate = {\n schemaName,\n kind,\n content,\n ...(elementName !== undefined ? { elementName } : {}),\n ...(typeName !== undefined ? { typeName } : {}),\n ...(positionCtx && contentStart !== -1 && contentEnd !== -1\n ? { contentRange: { start: contentStart, end: contentEnd } }\n : {}),\n ...(expressionRanges.length > 0 ? { expressionRanges } : {}),\n };\n\n templates.push(template);\n};\n\n/**\n * Find the innermost gql call, unwrapping method chains like .attach().\n */\nexport const findGqlCall = (identifiers: ReadonlySet<string>, node: Node): CallExpression | null => {\n if (!node || node.type !== \"CallExpression\") {\n return null;\n }\n\n const call = node as unknown as CallExpression;\n if (getGqlCallSchemaName(identifiers, call) !== null) {\n return call;\n }\n\n const callee = call.callee;\n if (callee.type !== \"MemberExpression\") {\n return null;\n }\n\n return findGqlCall(identifiers, callee.object as unknown as Node);\n};\n\n/**\n * Walk AST to find gql calls and extract templates.\n */\nexport function walkAndExtract(\n node: Node,\n identifiers: ReadonlySet<string>,\n positionCtx: PositionTrackingContext,\n): ExtractedTemplateWithPosition[];\nexport function walkAndExtract(\n node: Node,\n identifiers: ReadonlySet<string>,\n positionCtx?: PositionTrackingContext,\n): ExtractedTemplate[];\nexport function walkAndExtract(\n node: Node,\n identifiers: ReadonlySet<string>,\n positionCtx?: PositionTrackingContext,\n): ExtractedTemplate[] {\n const templates: ExtractedTemplate[] = [];\n\n const visit = (n: Node | ReadonlyArray<Node> | Record<string, unknown>): void => {\n if (!n || typeof n !== \"object\") {\n return;\n }\n\n if (\"type\" in n && n.type === \"CallExpression\") {\n const gqlCall = findGqlCall(identifiers, n as Node);\n if (gqlCall) {\n const schemaName = getGqlCallSchemaName(identifiers, gqlCall);\n if (schemaName) {\n const arrow = gqlCall.arguments[0]?.expression as ArrowFunctionExpression;\n templates.push(...extractTemplatesFromCallback(arrow, schemaName, positionCtx));\n }\n return; // Don't recurse into gql calls\n }\n }\n\n // Recurse into all array and object properties\n if (Array.isArray(n)) {\n for (const item of n) {\n visit(item as Node);\n }\n return;\n }\n\n for (const key of Object.keys(n)) {\n if (key === \"span\" || key === \"type\") {\n continue;\n }\n const value = (n as Record<string, unknown>)[key];\n if (value && typeof value === \"object\") {\n visit(value as Node);\n }\n }\n };\n\n visit(node);\n return templates;\n}\n","/**\n * GraphQL template formatting utilities.\n *\n * Pure string operations — no dependency on `graphql` package.\n * Consumers provide their own format function (e.g., graphql-js parse/print).\n *\n * @module\n */\n\nimport type { ExtractedTemplate, TemplateFormatEdit } from \"./types\";\n\n/** A function that formats GraphQL source text. */\nexport type FormatGraphqlFn = (source: string) => string;\n\n/**\n * Detect the base indentation for a template by looking at the line\n * containing the opening backtick.\n */\nexport const detectBaseIndent = (tsSource: string, contentStartOffset: number): string => {\n // Find the start of the line containing contentStartOffset\n let lineStart = contentStartOffset;\n while (lineStart > 0 && tsSource.charCodeAt(lineStart - 1) !== 10) {\n lineStart--;\n }\n\n // Extract leading whitespace from that line\n let i = lineStart;\n while (i < tsSource.length && (tsSource.charCodeAt(i) === 32 || tsSource.charCodeAt(i) === 9)) {\n i++;\n }\n\n return tsSource.slice(lineStart, i);\n};\n\n/**\n * Re-indent formatted GraphQL to match the embedding context.\n *\n * - If original was single-line and formatted is single-line, keep as-is\n * - Otherwise, apply base indent + one level to each line, preserving\n * original leading/trailing newline pattern\n */\nexport const reindent = (formatted: string, baseIndent: string, originalContent: string): string => {\n const trimmedFormatted = formatted.trim();\n\n // If original was single-line and formatted is also single-line, keep it\n if (!originalContent.includes(\"\\n\") && !trimmedFormatted.includes(\"\\n\")) {\n return trimmedFormatted;\n }\n\n // For multi-line: use the indentation pattern from the original content\n const indent = `${baseIndent} `; // add one level of indentation\n const lines = trimmedFormatted.split(\"\\n\");\n const indentedLines = lines.map((line) => (line.trim() === \"\" ? \"\" : indent + line));\n\n // Match original leading/trailing newline pattern\n const startsWithNewline = originalContent.startsWith(\"\\n\");\n const endsWithNewline = /\\n\\s*$/.test(originalContent);\n\n let result = indentedLines.join(\"\\n\");\n if (startsWithNewline) {\n result = `\\n${result}`;\n }\n if (endsWithNewline) {\n result = `${result}\\n${baseIndent}`;\n }\n\n return result;\n};\n\nconst GRAPHQL_KEYWORDS = new Set([\"query\", \"mutation\", \"subscription\", \"fragment\"]);\n\n/**\n * Reconstruct a full GraphQL document from template content + metadata.\n *\n * For curried syntax, the template `content` is only the body (e.g., `{ user { id } }`).\n * GraphQL parsers need a full document (e.g., `query GetUser { user { id } }`).\n *\n * For bare-tag syntax, the content already starts with a keyword and is a full document.\n *\n * @returns The wrapped source and a regex pattern to strip the synthetic prefix after formatting\n */\nexport const buildGraphqlWrapper = (template: ExtractedTemplate): { wrapped: string; prefixPattern: RegExp | null } => {\n const content = template.content.trimStart();\n const firstWord = content.split(/[\\s({]/)[0] ?? \"\";\n\n // If content already starts with a GraphQL keyword, it's a full document (bare-tag)\n if (GRAPHQL_KEYWORDS.has(firstWord)) {\n return { wrapped: template.content, prefixPattern: null };\n }\n\n // Curried syntax — reconstruct the header\n if (template.elementName) {\n if (template.kind === \"fragment\" && template.typeName) {\n const prefix = `fragment ${template.elementName} on ${template.typeName} `;\n const prefixPattern = new RegExp(`^fragment\\\\s+${template.elementName}\\\\s+on\\\\s+${template.typeName}\\\\s*`);\n return { wrapped: prefix + template.content, prefixPattern };\n }\n const prefix = `${template.kind} ${template.elementName} `;\n const prefixPattern = new RegExp(`^${template.kind}\\\\s+${template.elementName}\\\\s*`);\n return { wrapped: prefix + template.content, prefixPattern };\n }\n\n // No elementName — try wrapping with just the kind\n const prefix = `${template.kind} `;\n const prefixPattern = new RegExp(`^${template.kind}\\\\s*`);\n return { wrapped: prefix + template.content, prefixPattern };\n};\n\n/**\n * Strip the reconstructed prefix from formatted output to get back the template body.\n * Uses regex pattern matching to handle whitespace normalization by the formatter\n * (e.g., `query Foo ($id: ID!)` → `query Foo($id: ID!)`).\n */\nexport const unwrapFormattedContent = (formatted: string, prefixPattern: RegExp | null): string => {\n if (!prefixPattern) return formatted;\n const match = formatted.match(prefixPattern);\n if (!match) return formatted;\n return formatted.slice(match[0].length);\n};\n\n/**\n * Format GraphQL templates within TypeScript source.\n *\n * Applies the given format function to each template's content, handles\n * wrapper reconstruction for curried syntax, and re-indents the result\n * to match the TypeScript embedding context.\n *\n * @returns Array of edits to apply to the source (sorted by position, not yet applied)\n */\nexport const formatTemplatesInSource = (\n templates: readonly ExtractedTemplate[],\n tsSource: string,\n formatGraphql: FormatGraphqlFn,\n): readonly TemplateFormatEdit[] => {\n const edits: TemplateFormatEdit[] = [];\n\n for (const template of templates) {\n if (!template.contentRange) continue;\n\n // Wrap the content for formatting\n const { wrapped, prefixPattern } = buildGraphqlWrapper(template);\n\n let formatted: string;\n try {\n formatted = formatGraphql(wrapped);\n } catch {\n continue;\n }\n\n // Unwrap the prefix\n let unwrapped = unwrapFormattedContent(formatted, prefixPattern);\n\n // Restore interpolation expressions: replace __FRAG_SPREAD_N__ with original ${...} syntax\n if (template.expressionRanges && template.expressionRanges.length > 0) {\n for (let i = 0; i < template.expressionRanges.length; i++) {\n const range = template.expressionRanges[i]!;\n const exprText = tsSource.slice(range.start, range.end);\n unwrapped = unwrapped.replace(`__FRAG_SPREAD_${i}__`, `\\${${exprText}}`);\n }\n }\n\n // Fast path: skip if formatter produces identical output\n if (unwrapped === template.content) {\n continue;\n }\n\n // Detect base indentation from the TS source\n const baseIndent = detectBaseIndent(tsSource, template.contentRange.start);\n\n // Re-indent the formatted output\n const reindented = reindent(unwrapped, baseIndent, template.content);\n\n // Skip if no changes after re-indentation\n if (reindented === template.content) {\n continue;\n }\n\n edits.push({\n start: template.contentRange.start,\n end: template.contentRange.end,\n newText: reindented,\n });\n }\n\n return edits;\n};\n"],"mappings":"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"}
@@ -225,7 +225,7 @@ const reindent = (formatted, baseIndent, originalContent) => {
225
225
  const lines = trimmedFormatted.split("\n");
226
226
  const indentedLines = lines.map((line) => line.trim() === "" ? "" : indent + line);
227
227
  const startsWithNewline = originalContent.startsWith("\n");
228
- const endsWithNewline = originalContent.endsWith("\n");
228
+ const endsWithNewline = /\n\s*$/.test(originalContent);
229
229
  let result = indentedLines.join("\n");
230
230
  if (startsWithNewline) {
231
231
  result = `\n${result}`;
@@ -1 +1 @@
1
- {"version":3,"file":"template-extraction.mjs","names":["templates: ExtractedTemplate[]","kind: string","elementName: string | undefined","typeName: string | undefined","expressionRanges: { start: number; end: number }[]","parts: string[]","template: ExtractedTemplate","prefix","prefixPattern","edits: TemplateFormatEdit[]","formatted: string"],"sources":["../src/template-extraction/extract.ts","../src/template-extraction/format.ts"],"sourcesContent":["/**\n * Template extraction from TypeScript source using SWC AST.\n *\n * Based on the typegen extractor (superset): supports both bare-tag\n * (`query\\`...\\``) and curried (`query(\"Name\")\\`...\\``) syntax.\n *\n * Position tracking is optional — pass spanOffset + converter to\n * populate contentRange on extracted templates.\n *\n * @module\n */\n\n// Re-export for convenience — SWC types are type-only imports\nimport type { ArrowFunctionExpression, CallExpression, MemberExpression, Node, TaggedTemplateExpression } from \"@swc/types\";\nimport type { SwcSpanConverter } from \"../utils/swc-span\";\nimport type { ExtractedTemplate, ExtractedTemplateWithPosition, OperationKind } from \"./types\";\n\nexport const OPERATION_KINDS = new Set<string>([\"query\", \"mutation\", \"subscription\", \"fragment\"]);\n\nexport const isOperationKind = (value: string): value is OperationKind => OPERATION_KINDS.has(value);\n\n/** Optional position tracking context for extraction. */\nexport type PositionTrackingContext = {\n readonly spanOffset: number;\n readonly converter: SwcSpanConverter;\n};\n\n/**\n * Check if a call expression is a gql.{schemaName}(...) call.\n * Returns the schema name if it is, null otherwise.\n */\nexport const getGqlCallSchemaName = (identifiers: ReadonlySet<string>, call: CallExpression): string | null => {\n const callee = call.callee;\n if (callee.type !== \"MemberExpression\") {\n return null;\n }\n\n const member = callee as MemberExpression;\n if (member.object.type !== \"Identifier\" || !identifiers.has(member.object.value)) {\n return null;\n }\n\n if (member.property.type !== \"Identifier\") {\n return null;\n }\n\n const firstArg = call.arguments[0];\n if (!firstArg?.expression || firstArg.expression.type !== \"ArrowFunctionExpression\") {\n return null;\n }\n\n return member.property.value;\n};\n\n/**\n * Extract templates from a gql callback's arrow function body.\n * Handles both expression bodies and block bodies with return statements.\n */\nexport const extractTemplatesFromCallback = (\n arrow: ArrowFunctionExpression,\n schemaName: string,\n positionCtx?: PositionTrackingContext,\n): ExtractedTemplate[] => {\n const templates: ExtractedTemplate[] = [];\n\n const processExpression = (expr: Node): void => {\n // Direct tagged template: query(\"Name\")`...`\n if (expr.type === \"TaggedTemplateExpression\") {\n const tagged = expr as unknown as TaggedTemplateExpression;\n extractFromTaggedTemplate(tagged, schemaName, templates, positionCtx);\n return;\n }\n\n // Metadata chaining: query(\"Name\")`...`({ metadata: {} })\n if (expr.type === \"CallExpression\") {\n const call = expr as unknown as CallExpression;\n if (call.callee.type === \"TaggedTemplateExpression\") {\n extractFromTaggedTemplate(call.callee as TaggedTemplateExpression, schemaName, templates, positionCtx);\n }\n }\n };\n\n // Expression body: ({ query }) => query(\"Name\")`...`\n if (arrow.body.type !== \"BlockStatement\") {\n processExpression(arrow.body);\n return templates;\n }\n\n // Block body: ({ query }) => { return query(\"Name\")`...`; }\n for (const stmt of arrow.body.stmts) {\n if (stmt.type === \"ReturnStatement\" && stmt.argument) {\n processExpression(stmt.argument);\n }\n }\n\n return templates;\n};\n\n/**\n * Extract a single template from a tagged template expression.\n * Supports both bare-tag (Identifier) and curried (CallExpression) tag forms.\n */\nexport const extractFromTaggedTemplate = (\n tagged: TaggedTemplateExpression,\n schemaName: string,\n templates: ExtractedTemplate[],\n positionCtx?: PositionTrackingContext,\n): void => {\n // Tag can be:\n // - CallExpression: query(\"name\")`...` or fragment(\"name\", \"type\")`...` (curried syntax)\n // - Identifier: legacy bare-tag form (skipped if it contains interpolations)\n let kind: string;\n let elementName: string | undefined;\n let typeName: string | undefined;\n\n if (tagged.tag.type === \"Identifier\") {\n kind = tagged.tag.value;\n } else if (tagged.tag.type === \"CallExpression\") {\n const tagCall = tagged.tag as CallExpression;\n if (tagCall.callee.type === \"Identifier\") {\n kind = tagCall.callee.value;\n } else {\n return;\n }\n // Extract elementName and typeName from call arguments\n const firstArg = tagCall.arguments[0]?.expression;\n if (firstArg?.type === \"StringLiteral\") {\n elementName = (firstArg as { value: string }).value;\n }\n const secondArg = tagCall.arguments[1]?.expression;\n if (secondArg?.type === \"StringLiteral\") {\n typeName = (secondArg as { value: string }).value;\n }\n } else {\n return;\n }\n\n if (!isOperationKind(kind)) {\n return;\n }\n\n const { quasis, expressions } = tagged.template;\n\n // For legacy Identifier tag, skip templates with interpolations\n if (tagged.tag.type === \"Identifier\" && expressions.length > 0) {\n return;\n }\n\n if (quasis.length === 0) {\n return;\n }\n\n // Build content and optionally track position\n let contentStart = -1;\n let contentEnd = -1;\n const expressionRanges: { start: number; end: number }[] = [];\n\n const parts: string[] = [];\n for (let i = 0; i < quasis.length; i++) {\n const quasi = quasis[i];\n if (!quasi) continue;\n\n if (positionCtx) {\n const quasiStart = positionCtx.converter.byteOffsetToCharIndex(quasi.span.start - positionCtx.spanOffset);\n const quasiEnd = positionCtx.converter.byteOffsetToCharIndex(quasi.span.end - positionCtx.spanOffset);\n if (contentStart === -1) contentStart = quasiStart;\n contentEnd = quasiEnd;\n }\n\n parts.push(quasi.cooked ?? quasi.raw);\n if (i < expressions.length) {\n parts.push(`__FRAG_SPREAD_${i}__`);\n if (positionCtx) {\n // All SWC AST nodes have span; cast needed because Expression union type doesn't expose it uniformly\n const expr = expressions[i] as unknown as { span: { start: number; end: number } };\n const exprStart = positionCtx.converter.byteOffsetToCharIndex(expr.span.start - positionCtx.spanOffset);\n const exprEnd = positionCtx.converter.byteOffsetToCharIndex(expr.span.end - positionCtx.spanOffset);\n expressionRanges.push({ start: exprStart, end: exprEnd });\n }\n }\n }\n const content = parts.join(\"\");\n\n const template: ExtractedTemplate = {\n schemaName,\n kind,\n content,\n ...(elementName !== undefined ? { elementName } : {}),\n ...(typeName !== undefined ? { typeName } : {}),\n ...(positionCtx && contentStart !== -1 && contentEnd !== -1\n ? { contentRange: { start: contentStart, end: contentEnd } }\n : {}),\n ...(expressionRanges.length > 0 ? { expressionRanges } : {}),\n };\n\n templates.push(template);\n};\n\n/**\n * Find the innermost gql call, unwrapping method chains like .attach().\n */\nexport const findGqlCall = (identifiers: ReadonlySet<string>, node: Node): CallExpression | null => {\n if (!node || node.type !== \"CallExpression\") {\n return null;\n }\n\n const call = node as unknown as CallExpression;\n if (getGqlCallSchemaName(identifiers, call) !== null) {\n return call;\n }\n\n const callee = call.callee;\n if (callee.type !== \"MemberExpression\") {\n return null;\n }\n\n return findGqlCall(identifiers, callee.object as unknown as Node);\n};\n\n/**\n * Walk AST to find gql calls and extract templates.\n */\nexport function walkAndExtract(\n node: Node,\n identifiers: ReadonlySet<string>,\n positionCtx: PositionTrackingContext,\n): ExtractedTemplateWithPosition[];\nexport function walkAndExtract(\n node: Node,\n identifiers: ReadonlySet<string>,\n positionCtx?: PositionTrackingContext,\n): ExtractedTemplate[];\nexport function walkAndExtract(\n node: Node,\n identifiers: ReadonlySet<string>,\n positionCtx?: PositionTrackingContext,\n): ExtractedTemplate[] {\n const templates: ExtractedTemplate[] = [];\n\n const visit = (n: Node | ReadonlyArray<Node> | Record<string, unknown>): void => {\n if (!n || typeof n !== \"object\") {\n return;\n }\n\n if (\"type\" in n && n.type === \"CallExpression\") {\n const gqlCall = findGqlCall(identifiers, n as Node);\n if (gqlCall) {\n const schemaName = getGqlCallSchemaName(identifiers, gqlCall);\n if (schemaName) {\n const arrow = gqlCall.arguments[0]?.expression as ArrowFunctionExpression;\n templates.push(...extractTemplatesFromCallback(arrow, schemaName, positionCtx));\n }\n return; // Don't recurse into gql calls\n }\n }\n\n // Recurse into all array and object properties\n if (Array.isArray(n)) {\n for (const item of n) {\n visit(item as Node);\n }\n return;\n }\n\n for (const key of Object.keys(n)) {\n if (key === \"span\" || key === \"type\") {\n continue;\n }\n const value = (n as Record<string, unknown>)[key];\n if (value && typeof value === \"object\") {\n visit(value as Node);\n }\n }\n };\n\n visit(node);\n return templates;\n}\n","/**\n * GraphQL template formatting utilities.\n *\n * Pure string operations — no dependency on `graphql` package.\n * Consumers provide their own format function (e.g., graphql-js parse/print).\n *\n * @module\n */\n\nimport type { ExtractedTemplate, TemplateFormatEdit } from \"./types\";\n\n/** A function that formats GraphQL source text. */\nexport type FormatGraphqlFn = (source: string) => string;\n\n/**\n * Detect the base indentation for a template by looking at the line\n * containing the opening backtick.\n */\nexport const detectBaseIndent = (tsSource: string, contentStartOffset: number): string => {\n // Find the start of the line containing contentStartOffset\n let lineStart = contentStartOffset;\n while (lineStart > 0 && tsSource.charCodeAt(lineStart - 1) !== 10) {\n lineStart--;\n }\n\n // Extract leading whitespace from that line\n let i = lineStart;\n while (i < tsSource.length && (tsSource.charCodeAt(i) === 32 || tsSource.charCodeAt(i) === 9)) {\n i++;\n }\n\n return tsSource.slice(lineStart, i);\n};\n\n/**\n * Re-indent formatted GraphQL to match the embedding context.\n *\n * - If original was single-line and formatted is single-line, keep as-is\n * - Otherwise, apply base indent + one level to each line, preserving\n * original leading/trailing newline pattern\n */\nexport const reindent = (formatted: string, baseIndent: string, originalContent: string): string => {\n const trimmedFormatted = formatted.trim();\n\n // If original was single-line and formatted is also single-line, keep it\n if (!originalContent.includes(\"\\n\") && !trimmedFormatted.includes(\"\\n\")) {\n return trimmedFormatted;\n }\n\n // For multi-line: use the indentation pattern from the original content\n const indent = `${baseIndent} `; // add one level of indentation\n const lines = trimmedFormatted.split(\"\\n\");\n const indentedLines = lines.map((line) => (line.trim() === \"\" ? \"\" : indent + line));\n\n // Match original leading/trailing newline pattern\n const startsWithNewline = originalContent.startsWith(\"\\n\");\n const endsWithNewline = originalContent.endsWith(\"\\n\");\n\n let result = indentedLines.join(\"\\n\");\n if (startsWithNewline) {\n result = `\\n${result}`;\n }\n if (endsWithNewline) {\n result = `${result}\\n${baseIndent}`;\n }\n\n return result;\n};\n\nconst GRAPHQL_KEYWORDS = new Set([\"query\", \"mutation\", \"subscription\", \"fragment\"]);\n\n/**\n * Reconstruct a full GraphQL document from template content + metadata.\n *\n * For curried syntax, the template `content` is only the body (e.g., `{ user { id } }`).\n * GraphQL parsers need a full document (e.g., `query GetUser { user { id } }`).\n *\n * For bare-tag syntax, the content already starts with a keyword and is a full document.\n *\n * @returns The wrapped source and a regex pattern to strip the synthetic prefix after formatting\n */\nexport const buildGraphqlWrapper = (template: ExtractedTemplate): { wrapped: string; prefixPattern: RegExp | null } => {\n const content = template.content.trimStart();\n const firstWord = content.split(/[\\s({]/)[0] ?? \"\";\n\n // If content already starts with a GraphQL keyword, it's a full document (bare-tag)\n if (GRAPHQL_KEYWORDS.has(firstWord)) {\n return { wrapped: template.content, prefixPattern: null };\n }\n\n // Curried syntax — reconstruct the header\n if (template.elementName) {\n if (template.kind === \"fragment\" && template.typeName) {\n const prefix = `fragment ${template.elementName} on ${template.typeName} `;\n const prefixPattern = new RegExp(`^fragment\\\\s+${template.elementName}\\\\s+on\\\\s+${template.typeName}\\\\s*`);\n return { wrapped: prefix + template.content, prefixPattern };\n }\n const prefix = `${template.kind} ${template.elementName} `;\n const prefixPattern = new RegExp(`^${template.kind}\\\\s+${template.elementName}\\\\s*`);\n return { wrapped: prefix + template.content, prefixPattern };\n }\n\n // No elementName — try wrapping with just the kind\n const prefix = `${template.kind} `;\n const prefixPattern = new RegExp(`^${template.kind}\\\\s*`);\n return { wrapped: prefix + template.content, prefixPattern };\n};\n\n/**\n * Strip the reconstructed prefix from formatted output to get back the template body.\n * Uses regex pattern matching to handle whitespace normalization by the formatter\n * (e.g., `query Foo ($id: ID!)` → `query Foo($id: ID!)`).\n */\nexport const unwrapFormattedContent = (formatted: string, prefixPattern: RegExp | null): string => {\n if (!prefixPattern) return formatted;\n const match = formatted.match(prefixPattern);\n if (!match) return formatted;\n return formatted.slice(match[0].length);\n};\n\n/**\n * Format GraphQL templates within TypeScript source.\n *\n * Applies the given format function to each template's content, handles\n * wrapper reconstruction for curried syntax, and re-indents the result\n * to match the TypeScript embedding context.\n *\n * @returns Array of edits to apply to the source (sorted by position, not yet applied)\n */\nexport const formatTemplatesInSource = (\n templates: readonly ExtractedTemplate[],\n tsSource: string,\n formatGraphql: FormatGraphqlFn,\n): readonly TemplateFormatEdit[] => {\n const edits: TemplateFormatEdit[] = [];\n\n for (const template of templates) {\n if (!template.contentRange) continue;\n\n // Wrap the content for formatting\n const { wrapped, prefixPattern } = buildGraphqlWrapper(template);\n\n let formatted: string;\n try {\n formatted = formatGraphql(wrapped);\n } catch {\n continue;\n }\n\n // Unwrap the prefix\n let unwrapped = unwrapFormattedContent(formatted, prefixPattern);\n\n // Restore interpolation expressions: replace __FRAG_SPREAD_N__ with original ${...} syntax\n if (template.expressionRanges && template.expressionRanges.length > 0) {\n for (let i = 0; i < template.expressionRanges.length; i++) {\n const range = template.expressionRanges[i]!;\n const exprText = tsSource.slice(range.start, range.end);\n unwrapped = unwrapped.replace(`__FRAG_SPREAD_${i}__`, `\\${${exprText}}`);\n }\n }\n\n // Fast path: skip if formatter produces identical output\n if (unwrapped === template.content) {\n continue;\n }\n\n // Detect base indentation from the TS source\n const baseIndent = detectBaseIndent(tsSource, template.contentRange.start);\n\n // Re-indent the formatted output\n const reindented = reindent(unwrapped, baseIndent, template.content);\n\n // Skip if no changes after re-indentation\n if (reindented === template.content) {\n continue;\n }\n\n edits.push({\n start: template.contentRange.start,\n end: template.contentRange.end,\n newText: reindented,\n });\n }\n\n return edits;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"template-extraction.mjs","names":["templates: ExtractedTemplate[]","kind: string","elementName: string | undefined","typeName: string | undefined","expressionRanges: { start: number; end: number }[]","parts: string[]","template: ExtractedTemplate","prefix","prefixPattern","edits: TemplateFormatEdit[]","formatted: string"],"sources":["../src/template-extraction/extract.ts","../src/template-extraction/format.ts"],"sourcesContent":["/**\n * Template extraction from TypeScript source using SWC AST.\n *\n * Based on the typegen extractor (superset): supports both bare-tag\n * (`query\\`...\\``) and curried (`query(\"Name\")\\`...\\``) syntax.\n *\n * Position tracking is optional — pass spanOffset + converter to\n * populate contentRange on extracted templates.\n *\n * @module\n */\n\n// Re-export for convenience — SWC types are type-only imports\nimport type { ArrowFunctionExpression, CallExpression, MemberExpression, Node, TaggedTemplateExpression } from \"@swc/types\";\nimport type { SwcSpanConverter } from \"../utils/swc-span\";\nimport type { ExtractedTemplate, ExtractedTemplateWithPosition, OperationKind } from \"./types\";\n\nexport const OPERATION_KINDS = new Set<string>([\"query\", \"mutation\", \"subscription\", \"fragment\"]);\n\nexport const isOperationKind = (value: string): value is OperationKind => OPERATION_KINDS.has(value);\n\n/** Optional position tracking context for extraction. */\nexport type PositionTrackingContext = {\n readonly spanOffset: number;\n readonly converter: SwcSpanConverter;\n};\n\n/**\n * Check if a call expression is a gql.{schemaName}(...) call.\n * Returns the schema name if it is, null otherwise.\n */\nexport const getGqlCallSchemaName = (identifiers: ReadonlySet<string>, call: CallExpression): string | null => {\n const callee = call.callee;\n if (callee.type !== \"MemberExpression\") {\n return null;\n }\n\n const member = callee as MemberExpression;\n if (member.object.type !== \"Identifier\" || !identifiers.has(member.object.value)) {\n return null;\n }\n\n if (member.property.type !== \"Identifier\") {\n return null;\n }\n\n const firstArg = call.arguments[0];\n if (!firstArg?.expression || firstArg.expression.type !== \"ArrowFunctionExpression\") {\n return null;\n }\n\n return member.property.value;\n};\n\n/**\n * Extract templates from a gql callback's arrow function body.\n * Handles both expression bodies and block bodies with return statements.\n */\nexport const extractTemplatesFromCallback = (\n arrow: ArrowFunctionExpression,\n schemaName: string,\n positionCtx?: PositionTrackingContext,\n): ExtractedTemplate[] => {\n const templates: ExtractedTemplate[] = [];\n\n const processExpression = (expr: Node): void => {\n // Direct tagged template: query(\"Name\")`...`\n if (expr.type === \"TaggedTemplateExpression\") {\n const tagged = expr as unknown as TaggedTemplateExpression;\n extractFromTaggedTemplate(tagged, schemaName, templates, positionCtx);\n return;\n }\n\n // Metadata chaining: query(\"Name\")`...`({ metadata: {} })\n if (expr.type === \"CallExpression\") {\n const call = expr as unknown as CallExpression;\n if (call.callee.type === \"TaggedTemplateExpression\") {\n extractFromTaggedTemplate(call.callee as TaggedTemplateExpression, schemaName, templates, positionCtx);\n }\n }\n };\n\n // Expression body: ({ query }) => query(\"Name\")`...`\n if (arrow.body.type !== \"BlockStatement\") {\n processExpression(arrow.body);\n return templates;\n }\n\n // Block body: ({ query }) => { return query(\"Name\")`...`; }\n for (const stmt of arrow.body.stmts) {\n if (stmt.type === \"ReturnStatement\" && stmt.argument) {\n processExpression(stmt.argument);\n }\n }\n\n return templates;\n};\n\n/**\n * Extract a single template from a tagged template expression.\n * Supports both bare-tag (Identifier) and curried (CallExpression) tag forms.\n */\nexport const extractFromTaggedTemplate = (\n tagged: TaggedTemplateExpression,\n schemaName: string,\n templates: ExtractedTemplate[],\n positionCtx?: PositionTrackingContext,\n): void => {\n // Tag can be:\n // - CallExpression: query(\"name\")`...` or fragment(\"name\", \"type\")`...` (curried syntax)\n // - Identifier: legacy bare-tag form (skipped if it contains interpolations)\n let kind: string;\n let elementName: string | undefined;\n let typeName: string | undefined;\n\n if (tagged.tag.type === \"Identifier\") {\n kind = tagged.tag.value;\n } else if (tagged.tag.type === \"CallExpression\") {\n const tagCall = tagged.tag as CallExpression;\n if (tagCall.callee.type === \"Identifier\") {\n kind = tagCall.callee.value;\n } else {\n return;\n }\n // Extract elementName and typeName from call arguments\n const firstArg = tagCall.arguments[0]?.expression;\n if (firstArg?.type === \"StringLiteral\") {\n elementName = (firstArg as { value: string }).value;\n }\n const secondArg = tagCall.arguments[1]?.expression;\n if (secondArg?.type === \"StringLiteral\") {\n typeName = (secondArg as { value: string }).value;\n }\n } else {\n return;\n }\n\n if (!isOperationKind(kind)) {\n return;\n }\n\n const { quasis, expressions } = tagged.template;\n\n // For legacy Identifier tag, skip templates with interpolations\n if (tagged.tag.type === \"Identifier\" && expressions.length > 0) {\n return;\n }\n\n if (quasis.length === 0) {\n return;\n }\n\n // Build content and optionally track position\n let contentStart = -1;\n let contentEnd = -1;\n const expressionRanges: { start: number; end: number }[] = [];\n\n const parts: string[] = [];\n for (let i = 0; i < quasis.length; i++) {\n const quasi = quasis[i];\n if (!quasi) continue;\n\n if (positionCtx) {\n const quasiStart = positionCtx.converter.byteOffsetToCharIndex(quasi.span.start - positionCtx.spanOffset);\n const quasiEnd = positionCtx.converter.byteOffsetToCharIndex(quasi.span.end - positionCtx.spanOffset);\n if (contentStart === -1) contentStart = quasiStart;\n contentEnd = quasiEnd;\n }\n\n parts.push(quasi.cooked ?? quasi.raw);\n if (i < expressions.length) {\n parts.push(`__FRAG_SPREAD_${i}__`);\n if (positionCtx) {\n // All SWC AST nodes have span; cast needed because Expression union type doesn't expose it uniformly\n const expr = expressions[i] as unknown as { span: { start: number; end: number } };\n const exprStart = positionCtx.converter.byteOffsetToCharIndex(expr.span.start - positionCtx.spanOffset);\n const exprEnd = positionCtx.converter.byteOffsetToCharIndex(expr.span.end - positionCtx.spanOffset);\n expressionRanges.push({ start: exprStart, end: exprEnd });\n }\n }\n }\n const content = parts.join(\"\");\n\n const template: ExtractedTemplate = {\n schemaName,\n kind,\n content,\n ...(elementName !== undefined ? { elementName } : {}),\n ...(typeName !== undefined ? { typeName } : {}),\n ...(positionCtx && contentStart !== -1 && contentEnd !== -1\n ? { contentRange: { start: contentStart, end: contentEnd } }\n : {}),\n ...(expressionRanges.length > 0 ? { expressionRanges } : {}),\n };\n\n templates.push(template);\n};\n\n/**\n * Find the innermost gql call, unwrapping method chains like .attach().\n */\nexport const findGqlCall = (identifiers: ReadonlySet<string>, node: Node): CallExpression | null => {\n if (!node || node.type !== \"CallExpression\") {\n return null;\n }\n\n const call = node as unknown as CallExpression;\n if (getGqlCallSchemaName(identifiers, call) !== null) {\n return call;\n }\n\n const callee = call.callee;\n if (callee.type !== \"MemberExpression\") {\n return null;\n }\n\n return findGqlCall(identifiers, callee.object as unknown as Node);\n};\n\n/**\n * Walk AST to find gql calls and extract templates.\n */\nexport function walkAndExtract(\n node: Node,\n identifiers: ReadonlySet<string>,\n positionCtx: PositionTrackingContext,\n): ExtractedTemplateWithPosition[];\nexport function walkAndExtract(\n node: Node,\n identifiers: ReadonlySet<string>,\n positionCtx?: PositionTrackingContext,\n): ExtractedTemplate[];\nexport function walkAndExtract(\n node: Node,\n identifiers: ReadonlySet<string>,\n positionCtx?: PositionTrackingContext,\n): ExtractedTemplate[] {\n const templates: ExtractedTemplate[] = [];\n\n const visit = (n: Node | ReadonlyArray<Node> | Record<string, unknown>): void => {\n if (!n || typeof n !== \"object\") {\n return;\n }\n\n if (\"type\" in n && n.type === \"CallExpression\") {\n const gqlCall = findGqlCall(identifiers, n as Node);\n if (gqlCall) {\n const schemaName = getGqlCallSchemaName(identifiers, gqlCall);\n if (schemaName) {\n const arrow = gqlCall.arguments[0]?.expression as ArrowFunctionExpression;\n templates.push(...extractTemplatesFromCallback(arrow, schemaName, positionCtx));\n }\n return; // Don't recurse into gql calls\n }\n }\n\n // Recurse into all array and object properties\n if (Array.isArray(n)) {\n for (const item of n) {\n visit(item as Node);\n }\n return;\n }\n\n for (const key of Object.keys(n)) {\n if (key === \"span\" || key === \"type\") {\n continue;\n }\n const value = (n as Record<string, unknown>)[key];\n if (value && typeof value === \"object\") {\n visit(value as Node);\n }\n }\n };\n\n visit(node);\n return templates;\n}\n","/**\n * GraphQL template formatting utilities.\n *\n * Pure string operations — no dependency on `graphql` package.\n * Consumers provide their own format function (e.g., graphql-js parse/print).\n *\n * @module\n */\n\nimport type { ExtractedTemplate, TemplateFormatEdit } from \"./types\";\n\n/** A function that formats GraphQL source text. */\nexport type FormatGraphqlFn = (source: string) => string;\n\n/**\n * Detect the base indentation for a template by looking at the line\n * containing the opening backtick.\n */\nexport const detectBaseIndent = (tsSource: string, contentStartOffset: number): string => {\n // Find the start of the line containing contentStartOffset\n let lineStart = contentStartOffset;\n while (lineStart > 0 && tsSource.charCodeAt(lineStart - 1) !== 10) {\n lineStart--;\n }\n\n // Extract leading whitespace from that line\n let i = lineStart;\n while (i < tsSource.length && (tsSource.charCodeAt(i) === 32 || tsSource.charCodeAt(i) === 9)) {\n i++;\n }\n\n return tsSource.slice(lineStart, i);\n};\n\n/**\n * Re-indent formatted GraphQL to match the embedding context.\n *\n * - If original was single-line and formatted is single-line, keep as-is\n * - Otherwise, apply base indent + one level to each line, preserving\n * original leading/trailing newline pattern\n */\nexport const reindent = (formatted: string, baseIndent: string, originalContent: string): string => {\n const trimmedFormatted = formatted.trim();\n\n // If original was single-line and formatted is also single-line, keep it\n if (!originalContent.includes(\"\\n\") && !trimmedFormatted.includes(\"\\n\")) {\n return trimmedFormatted;\n }\n\n // For multi-line: use the indentation pattern from the original content\n const indent = `${baseIndent} `; // add one level of indentation\n const lines = trimmedFormatted.split(\"\\n\");\n const indentedLines = lines.map((line) => (line.trim() === \"\" ? \"\" : indent + line));\n\n // Match original leading/trailing newline pattern\n const startsWithNewline = originalContent.startsWith(\"\\n\");\n const endsWithNewline = /\\n\\s*$/.test(originalContent);\n\n let result = indentedLines.join(\"\\n\");\n if (startsWithNewline) {\n result = `\\n${result}`;\n }\n if (endsWithNewline) {\n result = `${result}\\n${baseIndent}`;\n }\n\n return result;\n};\n\nconst GRAPHQL_KEYWORDS = new Set([\"query\", \"mutation\", \"subscription\", \"fragment\"]);\n\n/**\n * Reconstruct a full GraphQL document from template content + metadata.\n *\n * For curried syntax, the template `content` is only the body (e.g., `{ user { id } }`).\n * GraphQL parsers need a full document (e.g., `query GetUser { user { id } }`).\n *\n * For bare-tag syntax, the content already starts with a keyword and is a full document.\n *\n * @returns The wrapped source and a regex pattern to strip the synthetic prefix after formatting\n */\nexport const buildGraphqlWrapper = (template: ExtractedTemplate): { wrapped: string; prefixPattern: RegExp | null } => {\n const content = template.content.trimStart();\n const firstWord = content.split(/[\\s({]/)[0] ?? \"\";\n\n // If content already starts with a GraphQL keyword, it's a full document (bare-tag)\n if (GRAPHQL_KEYWORDS.has(firstWord)) {\n return { wrapped: template.content, prefixPattern: null };\n }\n\n // Curried syntax — reconstruct the header\n if (template.elementName) {\n if (template.kind === \"fragment\" && template.typeName) {\n const prefix = `fragment ${template.elementName} on ${template.typeName} `;\n const prefixPattern = new RegExp(`^fragment\\\\s+${template.elementName}\\\\s+on\\\\s+${template.typeName}\\\\s*`);\n return { wrapped: prefix + template.content, prefixPattern };\n }\n const prefix = `${template.kind} ${template.elementName} `;\n const prefixPattern = new RegExp(`^${template.kind}\\\\s+${template.elementName}\\\\s*`);\n return { wrapped: prefix + template.content, prefixPattern };\n }\n\n // No elementName — try wrapping with just the kind\n const prefix = `${template.kind} `;\n const prefixPattern = new RegExp(`^${template.kind}\\\\s*`);\n return { wrapped: prefix + template.content, prefixPattern };\n};\n\n/**\n * Strip the reconstructed prefix from formatted output to get back the template body.\n * Uses regex pattern matching to handle whitespace normalization by the formatter\n * (e.g., `query Foo ($id: ID!)` → `query Foo($id: ID!)`).\n */\nexport const unwrapFormattedContent = (formatted: string, prefixPattern: RegExp | null): string => {\n if (!prefixPattern) return formatted;\n const match = formatted.match(prefixPattern);\n if (!match) return formatted;\n return formatted.slice(match[0].length);\n};\n\n/**\n * Format GraphQL templates within TypeScript source.\n *\n * Applies the given format function to each template's content, handles\n * wrapper reconstruction for curried syntax, and re-indents the result\n * to match the TypeScript embedding context.\n *\n * @returns Array of edits to apply to the source (sorted by position, not yet applied)\n */\nexport const formatTemplatesInSource = (\n templates: readonly ExtractedTemplate[],\n tsSource: string,\n formatGraphql: FormatGraphqlFn,\n): readonly TemplateFormatEdit[] => {\n const edits: TemplateFormatEdit[] = [];\n\n for (const template of templates) {\n if (!template.contentRange) continue;\n\n // Wrap the content for formatting\n const { wrapped, prefixPattern } = buildGraphqlWrapper(template);\n\n let formatted: string;\n try {\n formatted = formatGraphql(wrapped);\n } catch {\n continue;\n }\n\n // Unwrap the prefix\n let unwrapped = unwrapFormattedContent(formatted, prefixPattern);\n\n // Restore interpolation expressions: replace __FRAG_SPREAD_N__ with original ${...} syntax\n if (template.expressionRanges && template.expressionRanges.length > 0) {\n for (let i = 0; i < template.expressionRanges.length; i++) {\n const range = template.expressionRanges[i]!;\n const exprText = tsSource.slice(range.start, range.end);\n unwrapped = unwrapped.replace(`__FRAG_SPREAD_${i}__`, `\\${${exprText}}`);\n }\n }\n\n // Fast path: skip if formatter produces identical output\n if (unwrapped === template.content) {\n continue;\n }\n\n // Detect base indentation from the TS source\n const baseIndent = detectBaseIndent(tsSource, template.contentRange.start);\n\n // Re-indent the formatted output\n const reindented = reindent(unwrapped, baseIndent, template.content);\n\n // Skip if no changes after re-indentation\n if (reindented === template.content) {\n continue;\n }\n\n edits.push({\n start: template.contentRange.start,\n end: template.contentRange.end,\n newText: reindented,\n });\n }\n\n return edits;\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soda-gql/common",
3
- "version": "0.12.5",
3
+ "version": "0.12.6",
4
4
  "description": "Shared utilities for soda-gql packages",
5
5
  "type": "module",
6
6
  "private": false,