@soda-gql/common 0.12.2 → 0.12.4

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.
Files changed (58) hide show
  1. package/dist/{canonical-id-U2VNaQ86.mjs → canonical-id-BNdkINTi.mjs} +2 -2
  2. package/dist/{canonical-id-U2VNaQ86.mjs.map → canonical-id-BNdkINTi.mjs.map} +1 -1
  3. package/dist/canonical-id.d.cts +1 -1
  4. package/dist/canonical-id.d.mts +1 -1
  5. package/dist/canonical-id.mjs +2 -2
  6. package/dist/{index-BMa2_rDb.d.mts → index-B7A6qmX_.d.mts} +1 -1
  7. package/dist/{index-BMa2_rDb.d.mts.map → index-B7A6qmX_.d.mts.map} +1 -1
  8. package/dist/{index-CbQyueYV.d.cts → index-B9cqW0Kp.d.cts} +1 -1
  9. package/dist/{index-CbQyueYV.d.cts.map → index-B9cqW0Kp.d.cts.map} +1 -1
  10. package/dist/{index-B1w5x8e3.d.mts → index-BIgjHIdk.d.mts} +2 -25
  11. package/dist/index-BIgjHIdk.d.mts.map +1 -0
  12. package/dist/{index-Dit86qkX.d.mts → index-DO6j6Cif.d.cts} +1 -1
  13. package/dist/{index-Dit86qkX.d.mts.map → index-DO6j6Cif.d.cts.map} +1 -1
  14. package/dist/{index-LHYortIn.d.cts → index-DeJfMmkU.d.mts} +1 -1
  15. package/dist/{index-LHYortIn.d.cts.map → index-DeJfMmkU.d.mts.map} +1 -1
  16. package/dist/{index-BDPAGTeS.d.cts → index-DpSxWrZ_.d.cts} +2 -25
  17. package/dist/index-DpSxWrZ_.d.cts.map +1 -0
  18. package/dist/{index-DZSebwar.d.cts → index-KB_f9ZJF.d.mts} +1 -1
  19. package/dist/{index-DZSebwar.d.cts.map → index-KB_f9ZJF.d.mts.map} +1 -1
  20. package/dist/{index-AqkJhrm3.d.mts → index-UauBErGY.d.cts} +1 -1
  21. package/dist/{index-AqkJhrm3.d.mts.map → index-UauBErGY.d.cts.map} +1 -1
  22. package/dist/index.d.cts +5 -4
  23. package/dist/index.d.cts.map +1 -1
  24. package/dist/index.d.mts +5 -4
  25. package/dist/index.d.mts.map +1 -1
  26. package/dist/index.mjs +4 -4
  27. package/dist/{portable-BFrcBOaX.mjs → portable-BDL1bEL6.mjs} +1 -1
  28. package/dist/{portable-BFrcBOaX.mjs.map → portable-BDL1bEL6.mjs.map} +1 -1
  29. package/dist/portable.d.cts +1 -1
  30. package/dist/portable.d.mts +1 -1
  31. package/dist/portable.mjs +1 -1
  32. package/dist/swc-span-Bi4N-vaI.d.cts +25 -0
  33. package/dist/swc-span-Bi4N-vaI.d.cts.map +1 -0
  34. package/dist/swc-span-C4QReqWr.d.mts +25 -0
  35. package/dist/swc-span-C4QReqWr.d.mts.map +1 -0
  36. package/dist/template-extraction.cjs +356 -0
  37. package/dist/template-extraction.cjs.map +1 -0
  38. package/dist/template-extraction.d.cts +131 -0
  39. package/dist/template-extraction.d.cts.map +1 -0
  40. package/dist/template-extraction.d.mts +131 -0
  41. package/dist/template-extraction.d.mts.map +1 -0
  42. package/dist/template-extraction.mjs +344 -0
  43. package/dist/template-extraction.mjs.map +1 -0
  44. package/dist/{utils-DlhM4qvh.mjs → utils-DOz9GTVx.mjs} +1 -1
  45. package/dist/{utils-DlhM4qvh.mjs.map → utils-DOz9GTVx.mjs.map} +1 -1
  46. package/dist/utils.d.cts +2 -1
  47. package/dist/utils.d.mts +2 -1
  48. package/dist/utils.mjs +1 -1
  49. package/dist/{zod-DeSimXdI.mjs → zod-nQLTnNYB.mjs} +1 -1
  50. package/dist/{zod-DeSimXdI.mjs.map → zod-nQLTnNYB.mjs.map} +1 -1
  51. package/dist/zod.d.cts +1 -1
  52. package/dist/zod.d.mts +1 -1
  53. package/dist/zod.mjs +1 -1
  54. package/package.json +13 -2
  55. package/template-extraction.d.ts +2 -0
  56. package/template-extraction.js +1 -0
  57. package/dist/index-B1w5x8e3.d.mts.map +0 -1
  58. package/dist/index-BDPAGTeS.d.cts.map +0 -1
@@ -0,0 +1,356 @@
1
+
2
+ //#region packages/common/src/template-extraction/extract.ts
3
+ const OPERATION_KINDS = new Set([
4
+ "query",
5
+ "mutation",
6
+ "subscription",
7
+ "fragment"
8
+ ]);
9
+ const isOperationKind = (value) => OPERATION_KINDS.has(value);
10
+ /**
11
+ * Check if a call expression is a gql.{schemaName}(...) call.
12
+ * Returns the schema name if it is, null otherwise.
13
+ */
14
+ const getGqlCallSchemaName = (identifiers, call) => {
15
+ const callee = call.callee;
16
+ if (callee.type !== "MemberExpression") {
17
+ return null;
18
+ }
19
+ const member = callee;
20
+ if (member.object.type !== "Identifier" || !identifiers.has(member.object.value)) {
21
+ return null;
22
+ }
23
+ if (member.property.type !== "Identifier") {
24
+ return null;
25
+ }
26
+ const firstArg = call.arguments[0];
27
+ if (!firstArg?.expression || firstArg.expression.type !== "ArrowFunctionExpression") {
28
+ return null;
29
+ }
30
+ return member.property.value;
31
+ };
32
+ /**
33
+ * Extract templates from a gql callback's arrow function body.
34
+ * Handles both expression bodies and block bodies with return statements.
35
+ */
36
+ const extractTemplatesFromCallback = (arrow, schemaName, positionCtx) => {
37
+ const templates = [];
38
+ const processExpression = (expr) => {
39
+ if (expr.type === "TaggedTemplateExpression") {
40
+ const tagged = expr;
41
+ extractFromTaggedTemplate(tagged, schemaName, templates, positionCtx);
42
+ return;
43
+ }
44
+ if (expr.type === "CallExpression") {
45
+ const call = expr;
46
+ if (call.callee.type === "TaggedTemplateExpression") {
47
+ extractFromTaggedTemplate(call.callee, schemaName, templates, positionCtx);
48
+ }
49
+ }
50
+ };
51
+ if (arrow.body.type !== "BlockStatement") {
52
+ processExpression(arrow.body);
53
+ return templates;
54
+ }
55
+ for (const stmt of arrow.body.stmts) {
56
+ if (stmt.type === "ReturnStatement" && stmt.argument) {
57
+ processExpression(stmt.argument);
58
+ }
59
+ }
60
+ return templates;
61
+ };
62
+ /**
63
+ * Extract a single template from a tagged template expression.
64
+ * Supports both bare-tag (Identifier) and curried (CallExpression) tag forms.
65
+ */
66
+ const extractFromTaggedTemplate = (tagged, schemaName, templates, positionCtx) => {
67
+ let kind;
68
+ let elementName;
69
+ let typeName;
70
+ if (tagged.tag.type === "Identifier") {
71
+ kind = tagged.tag.value;
72
+ } else if (tagged.tag.type === "CallExpression") {
73
+ const tagCall = tagged.tag;
74
+ if (tagCall.callee.type === "Identifier") {
75
+ kind = tagCall.callee.value;
76
+ } else {
77
+ return;
78
+ }
79
+ const firstArg = tagCall.arguments[0]?.expression;
80
+ if (firstArg?.type === "StringLiteral") {
81
+ elementName = firstArg.value;
82
+ }
83
+ const secondArg = tagCall.arguments[1]?.expression;
84
+ if (secondArg?.type === "StringLiteral") {
85
+ typeName = secondArg.value;
86
+ }
87
+ } else {
88
+ return;
89
+ }
90
+ if (!isOperationKind(kind)) {
91
+ return;
92
+ }
93
+ const { quasis, expressions } = tagged.template;
94
+ if (tagged.tag.type === "Identifier" && expressions.length > 0) {
95
+ return;
96
+ }
97
+ if (quasis.length === 0) {
98
+ return;
99
+ }
100
+ let contentStart = -1;
101
+ let contentEnd = -1;
102
+ const expressionRanges = [];
103
+ const parts = [];
104
+ for (let i = 0; i < quasis.length; i++) {
105
+ const quasi = quasis[i];
106
+ if (!quasi) continue;
107
+ if (positionCtx) {
108
+ const quasiStart = positionCtx.converter.byteOffsetToCharIndex(quasi.span.start - positionCtx.spanOffset);
109
+ const quasiEnd = positionCtx.converter.byteOffsetToCharIndex(quasi.span.end - positionCtx.spanOffset);
110
+ if (contentStart === -1) contentStart = quasiStart;
111
+ contentEnd = quasiEnd;
112
+ }
113
+ parts.push(quasi.cooked ?? quasi.raw);
114
+ if (i < expressions.length) {
115
+ parts.push(`__FRAG_SPREAD_${i}__`);
116
+ if (positionCtx) {
117
+ const expr = expressions[i];
118
+ const exprStart = positionCtx.converter.byteOffsetToCharIndex(expr.span.start - positionCtx.spanOffset);
119
+ const exprEnd = positionCtx.converter.byteOffsetToCharIndex(expr.span.end - positionCtx.spanOffset);
120
+ expressionRanges.push({
121
+ start: exprStart,
122
+ end: exprEnd
123
+ });
124
+ }
125
+ }
126
+ }
127
+ const content = parts.join("");
128
+ const template = {
129
+ schemaName,
130
+ kind,
131
+ content,
132
+ ...elementName !== undefined ? { elementName } : {},
133
+ ...typeName !== undefined ? { typeName } : {},
134
+ ...positionCtx && contentStart !== -1 && contentEnd !== -1 ? { contentRange: {
135
+ start: contentStart,
136
+ end: contentEnd
137
+ } } : {},
138
+ ...expressionRanges.length > 0 ? { expressionRanges } : {}
139
+ };
140
+ templates.push(template);
141
+ };
142
+ /**
143
+ * Find the innermost gql call, unwrapping method chains like .attach().
144
+ */
145
+ const findGqlCall = (identifiers, node) => {
146
+ if (!node || node.type !== "CallExpression") {
147
+ return null;
148
+ }
149
+ const call = node;
150
+ if (getGqlCallSchemaName(identifiers, call) !== null) {
151
+ return call;
152
+ }
153
+ const callee = call.callee;
154
+ if (callee.type !== "MemberExpression") {
155
+ return null;
156
+ }
157
+ return findGqlCall(identifiers, callee.object);
158
+ };
159
+ function walkAndExtract(node, identifiers, positionCtx) {
160
+ const templates = [];
161
+ const visit = (n) => {
162
+ if (!n || typeof n !== "object") {
163
+ return;
164
+ }
165
+ if ("type" in n && n.type === "CallExpression") {
166
+ const gqlCall = findGqlCall(identifiers, n);
167
+ if (gqlCall) {
168
+ const schemaName = getGqlCallSchemaName(identifiers, gqlCall);
169
+ if (schemaName) {
170
+ const arrow = gqlCall.arguments[0]?.expression;
171
+ templates.push(...extractTemplatesFromCallback(arrow, schemaName, positionCtx));
172
+ }
173
+ return;
174
+ }
175
+ }
176
+ if (Array.isArray(n)) {
177
+ for (const item of n) {
178
+ visit(item);
179
+ }
180
+ return;
181
+ }
182
+ for (const key of Object.keys(n)) {
183
+ if (key === "span" || key === "type") {
184
+ continue;
185
+ }
186
+ const value = n[key];
187
+ if (value && typeof value === "object") {
188
+ visit(value);
189
+ }
190
+ }
191
+ };
192
+ visit(node);
193
+ return templates;
194
+ }
195
+
196
+ //#endregion
197
+ //#region packages/common/src/template-extraction/format.ts
198
+ /**
199
+ * Detect the base indentation for a template by looking at the line
200
+ * containing the opening backtick.
201
+ */
202
+ const detectBaseIndent = (tsSource, contentStartOffset) => {
203
+ let lineStart = contentStartOffset;
204
+ while (lineStart > 0 && tsSource.charCodeAt(lineStart - 1) !== 10) {
205
+ lineStart--;
206
+ }
207
+ let i = lineStart;
208
+ while (i < tsSource.length && (tsSource.charCodeAt(i) === 32 || tsSource.charCodeAt(i) === 9)) {
209
+ i++;
210
+ }
211
+ return tsSource.slice(lineStart, i);
212
+ };
213
+ /**
214
+ * Re-indent formatted GraphQL to match the embedding context.
215
+ *
216
+ * - If original was single-line and formatted is single-line, keep as-is
217
+ * - Otherwise, apply base indent + one level to each line, preserving
218
+ * original leading/trailing newline pattern
219
+ */
220
+ const reindent = (formatted, baseIndent, originalContent) => {
221
+ const trimmedFormatted = formatted.trim();
222
+ if (!originalContent.includes("\n") && !trimmedFormatted.includes("\n")) {
223
+ return trimmedFormatted;
224
+ }
225
+ const indent = `${baseIndent} `;
226
+ const lines = trimmedFormatted.split("\n");
227
+ const indentedLines = lines.map((line) => line.trim() === "" ? "" : indent + line);
228
+ const startsWithNewline = originalContent.startsWith("\n");
229
+ const endsWithNewline = originalContent.endsWith("\n");
230
+ let result = indentedLines.join("\n");
231
+ if (startsWithNewline) {
232
+ result = `\n${result}`;
233
+ }
234
+ if (endsWithNewline) {
235
+ result = `${result}\n${baseIndent}`;
236
+ }
237
+ return result;
238
+ };
239
+ const GRAPHQL_KEYWORDS = new Set([
240
+ "query",
241
+ "mutation",
242
+ "subscription",
243
+ "fragment"
244
+ ]);
245
+ /**
246
+ * Reconstruct a full GraphQL document from template content + metadata.
247
+ *
248
+ * For curried syntax, the template `content` is only the body (e.g., `{ user { id } }`).
249
+ * GraphQL parsers need a full document (e.g., `query GetUser { user { id } }`).
250
+ *
251
+ * For bare-tag syntax, the content already starts with a keyword and is a full document.
252
+ *
253
+ * @returns The wrapped source and a regex pattern to strip the synthetic prefix after formatting
254
+ */
255
+ const buildGraphqlWrapper = (template) => {
256
+ const content = template.content.trimStart();
257
+ const firstWord = content.split(/[\s({]/)[0] ?? "";
258
+ if (GRAPHQL_KEYWORDS.has(firstWord)) {
259
+ return {
260
+ wrapped: template.content,
261
+ prefixPattern: null
262
+ };
263
+ }
264
+ if (template.elementName) {
265
+ if (template.kind === "fragment" && template.typeName) {
266
+ const prefix$2 = `fragment ${template.elementName} on ${template.typeName} `;
267
+ const prefixPattern$2 = new RegExp(`^fragment\\s+${template.elementName}\\s+on\\s+${template.typeName}\\s*`);
268
+ return {
269
+ wrapped: prefix$2 + template.content,
270
+ prefixPattern: prefixPattern$2
271
+ };
272
+ }
273
+ const prefix$1 = `${template.kind} ${template.elementName} `;
274
+ const prefixPattern$1 = new RegExp(`^${template.kind}\\s+${template.elementName}\\s*`);
275
+ return {
276
+ wrapped: prefix$1 + template.content,
277
+ prefixPattern: prefixPattern$1
278
+ };
279
+ }
280
+ const prefix = `${template.kind} `;
281
+ const prefixPattern = new RegExp(`^${template.kind}\\s*`);
282
+ return {
283
+ wrapped: prefix + template.content,
284
+ prefixPattern
285
+ };
286
+ };
287
+ /**
288
+ * Strip the reconstructed prefix from formatted output to get back the template body.
289
+ * Uses regex pattern matching to handle whitespace normalization by the formatter
290
+ * (e.g., `query Foo ($id: ID!)` → `query Foo($id: ID!)`).
291
+ */
292
+ const unwrapFormattedContent = (formatted, prefixPattern) => {
293
+ if (!prefixPattern) return formatted;
294
+ const match = formatted.match(prefixPattern);
295
+ if (!match) return formatted;
296
+ return formatted.slice(match[0].length);
297
+ };
298
+ /**
299
+ * Format GraphQL templates within TypeScript source.
300
+ *
301
+ * Applies the given format function to each template's content, handles
302
+ * wrapper reconstruction for curried syntax, and re-indents the result
303
+ * to match the TypeScript embedding context.
304
+ *
305
+ * @returns Array of edits to apply to the source (sorted by position, not yet applied)
306
+ */
307
+ const formatTemplatesInSource = (templates, tsSource, formatGraphql) => {
308
+ const edits = [];
309
+ for (const template of templates) {
310
+ if (!template.contentRange) continue;
311
+ const { wrapped, prefixPattern } = buildGraphqlWrapper(template);
312
+ let formatted;
313
+ try {
314
+ formatted = formatGraphql(wrapped);
315
+ } catch {
316
+ continue;
317
+ }
318
+ let unwrapped = unwrapFormattedContent(formatted, prefixPattern);
319
+ if (template.expressionRanges && template.expressionRanges.length > 0) {
320
+ for (let i = 0; i < template.expressionRanges.length; i++) {
321
+ const range = template.expressionRanges[i];
322
+ const exprText = tsSource.slice(range.start, range.end);
323
+ unwrapped = unwrapped.replace(`__FRAG_SPREAD_${i}__`, `\${${exprText}}`);
324
+ }
325
+ }
326
+ if (unwrapped === template.content) {
327
+ continue;
328
+ }
329
+ const baseIndent = detectBaseIndent(tsSource, template.contentRange.start);
330
+ const reindented = reindent(unwrapped, baseIndent, template.content);
331
+ if (reindented === template.content) {
332
+ continue;
333
+ }
334
+ edits.push({
335
+ start: template.contentRange.start,
336
+ end: template.contentRange.end,
337
+ newText: reindented
338
+ });
339
+ }
340
+ return edits;
341
+ };
342
+
343
+ //#endregion
344
+ exports.OPERATION_KINDS = OPERATION_KINDS;
345
+ exports.buildGraphqlWrapper = buildGraphqlWrapper;
346
+ exports.detectBaseIndent = detectBaseIndent;
347
+ exports.extractFromTaggedTemplate = extractFromTaggedTemplate;
348
+ exports.extractTemplatesFromCallback = extractTemplatesFromCallback;
349
+ exports.findGqlCall = findGqlCall;
350
+ exports.formatTemplatesInSource = formatTemplatesInSource;
351
+ exports.getGqlCallSchemaName = getGqlCallSchemaName;
352
+ exports.isOperationKind = isOperationKind;
353
+ exports.reindent = reindent;
354
+ exports.unwrapFormattedContent = unwrapFormattedContent;
355
+ exports.walkAndExtract = walkAndExtract;
356
+ //# sourceMappingURL=template-extraction.cjs.map
@@ -0,0 +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":";;AAiBA,MAAa,kBAAkB,IAAI,IAAY;CAAC;CAAS;CAAY;CAAgB;CAAW,CAAC;AAEjG,MAAa,mBAAmB,UAA0C,gBAAgB,IAAI,MAAM;;;;;AAYpG,MAAa,wBAAwB,aAAkC,SAAwC;CAC7G,MAAM,SAAS,KAAK;AACpB,KAAI,OAAO,SAAS,oBAAoB;AACtC,SAAO;;CAGT,MAAM,SAAS;AACf,KAAI,OAAO,OAAO,SAAS,gBAAgB,CAAC,YAAY,IAAI,OAAO,OAAO,MAAM,EAAE;AAChF,SAAO;;AAGT,KAAI,OAAO,SAAS,SAAS,cAAc;AACzC,SAAO;;CAGT,MAAM,WAAW,KAAK,UAAU;AAChC,KAAI,CAAC,UAAU,cAAc,SAAS,WAAW,SAAS,2BAA2B;AACnF,SAAO;;AAGT,QAAO,OAAO,SAAS;;;;;;AAOzB,MAAa,gCACX,OACA,YACA,gBACwB;CACxB,MAAMA,YAAiC,EAAE;CAEzC,MAAM,qBAAqB,SAAqB;AAE9C,MAAI,KAAK,SAAS,4BAA4B;GAC5C,MAAM,SAAS;AACf,6BAA0B,QAAQ,YAAY,WAAW,YAAY;AACrE;;AAIF,MAAI,KAAK,SAAS,kBAAkB;GAClC,MAAM,OAAO;AACb,OAAI,KAAK,OAAO,SAAS,4BAA4B;AACnD,8BAA0B,KAAK,QAAoC,YAAY,WAAW,YAAY;;;;AAM5G,KAAI,MAAM,KAAK,SAAS,kBAAkB;AACxC,oBAAkB,MAAM,KAAK;AAC7B,SAAO;;AAIT,MAAK,MAAM,QAAQ,MAAM,KAAK,OAAO;AACnC,MAAI,KAAK,SAAS,qBAAqB,KAAK,UAAU;AACpD,qBAAkB,KAAK,SAAS;;;AAIpC,QAAO;;;;;;AAOT,MAAa,6BACX,QACA,YACA,WACA,gBACS;CAIT,IAAIC;CACJ,IAAIC;CACJ,IAAIC;AAEJ,KAAI,OAAO,IAAI,SAAS,cAAc;AACpC,SAAO,OAAO,IAAI;YACT,OAAO,IAAI,SAAS,kBAAkB;EAC/C,MAAM,UAAU,OAAO;AACvB,MAAI,QAAQ,OAAO,SAAS,cAAc;AACxC,UAAO,QAAQ,OAAO;SACjB;AACL;;EAGF,MAAM,WAAW,QAAQ,UAAU,IAAI;AACvC,MAAI,UAAU,SAAS,iBAAiB;AACtC,iBAAe,SAA+B;;EAEhD,MAAM,YAAY,QAAQ,UAAU,IAAI;AACxC,MAAI,WAAW,SAAS,iBAAiB;AACvC,cAAY,UAAgC;;QAEzC;AACL;;AAGF,KAAI,CAAC,gBAAgB,KAAK,EAAE;AAC1B;;CAGF,MAAM,EAAE,QAAQ,gBAAgB,OAAO;AAGvC,KAAI,OAAO,IAAI,SAAS,gBAAgB,YAAY,SAAS,GAAG;AAC9D;;AAGF,KAAI,OAAO,WAAW,GAAG;AACvB;;CAIF,IAAI,eAAe,CAAC;CACpB,IAAI,aAAa,CAAC;CAClB,MAAMC,mBAAqD,EAAE;CAE7D,MAAMC,QAAkB,EAAE;AAC1B,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;EACtC,MAAM,QAAQ,OAAO;AACrB,MAAI,CAAC,MAAO;AAEZ,MAAI,aAAa;GACf,MAAM,aAAa,YAAY,UAAU,sBAAsB,MAAM,KAAK,QAAQ,YAAY,WAAW;GACzG,MAAM,WAAW,YAAY,UAAU,sBAAsB,MAAM,KAAK,MAAM,YAAY,WAAW;AACrG,OAAI,iBAAiB,CAAC,EAAG,gBAAe;AACxC,gBAAa;;AAGf,QAAM,KAAK,MAAM,UAAU,MAAM,IAAI;AACrC,MAAI,IAAI,YAAY,QAAQ;AAC1B,SAAM,KAAK,iBAAiB,EAAE,IAAI;AAClC,OAAI,aAAa;IAEf,MAAM,OAAO,YAAY;IACzB,MAAM,YAAY,YAAY,UAAU,sBAAsB,KAAK,KAAK,QAAQ,YAAY,WAAW;IACvG,MAAM,UAAU,YAAY,UAAU,sBAAsB,KAAK,KAAK,MAAM,YAAY,WAAW;AACnG,qBAAiB,KAAK;KAAE,OAAO;KAAW,KAAK;KAAS,CAAC;;;;CAI/D,MAAM,UAAU,MAAM,KAAK,GAAG;CAE9B,MAAMC,WAA8B;EAClC;EACA;EACA;EACA,GAAI,gBAAgB,YAAY,EAAE,aAAa,GAAG,EAAE;EACpD,GAAI,aAAa,YAAY,EAAE,UAAU,GAAG,EAAE;EAC9C,GAAI,eAAe,iBAAiB,CAAC,KAAK,eAAe,CAAC,IACtD,EAAE,cAAc;GAAE,OAAO;GAAc,KAAK;GAAY,EAAE,GAC1D,EAAE;EACN,GAAI,iBAAiB,SAAS,IAAI,EAAE,kBAAkB,GAAG,EAAE;EAC5D;AAED,WAAU,KAAK,SAAS;;;;;AAM1B,MAAa,eAAe,aAAkC,SAAsC;AAClG,KAAI,CAAC,QAAQ,KAAK,SAAS,kBAAkB;AAC3C,SAAO;;CAGT,MAAM,OAAO;AACb,KAAI,qBAAqB,aAAa,KAAK,KAAK,MAAM;AACpD,SAAO;;CAGT,MAAM,SAAS,KAAK;AACpB,KAAI,OAAO,SAAS,oBAAoB;AACtC,SAAO;;AAGT,QAAO,YAAY,aAAa,OAAO,OAA0B;;AAgBnE,SAAgB,eACd,MACA,aACA,aACqB;CACrB,MAAMN,YAAiC,EAAE;CAEzC,MAAM,SAAS,MAAkE;AAC/E,MAAI,CAAC,KAAK,OAAO,MAAM,UAAU;AAC/B;;AAGF,MAAI,UAAU,KAAK,EAAE,SAAS,kBAAkB;GAC9C,MAAM,UAAU,YAAY,aAAa,EAAU;AACnD,OAAI,SAAS;IACX,MAAM,aAAa,qBAAqB,aAAa,QAAQ;AAC7D,QAAI,YAAY;KACd,MAAM,QAAQ,QAAQ,UAAU,IAAI;AACpC,eAAU,KAAK,GAAG,6BAA6B,OAAO,YAAY,YAAY,CAAC;;AAEjF;;;AAKJ,MAAI,MAAM,QAAQ,EAAE,EAAE;AACpB,QAAK,MAAM,QAAQ,GAAG;AACpB,UAAM,KAAa;;AAErB;;AAGF,OAAK,MAAM,OAAO,OAAO,KAAK,EAAE,EAAE;AAChC,OAAI,QAAQ,UAAU,QAAQ,QAAQ;AACpC;;GAEF,MAAM,QAAS,EAA8B;AAC7C,OAAI,SAAS,OAAO,UAAU,UAAU;AACtC,UAAM,MAAc;;;;AAK1B,OAAM,KAAK;AACX,QAAO;;;;;;;;;AClQT,MAAa,oBAAoB,UAAkB,uBAAuC;CAExF,IAAI,YAAY;AAChB,QAAO,YAAY,KAAK,SAAS,WAAW,YAAY,EAAE,KAAK,IAAI;AACjE;;CAIF,IAAI,IAAI;AACR,QAAO,IAAI,SAAS,WAAW,SAAS,WAAW,EAAE,KAAK,MAAM,SAAS,WAAW,EAAE,KAAK,IAAI;AAC7F;;AAGF,QAAO,SAAS,MAAM,WAAW,EAAE;;;;;;;;;AAUrC,MAAa,YAAY,WAAmB,YAAoB,oBAAoC;CAClG,MAAM,mBAAmB,UAAU,MAAM;AAGzC,KAAI,CAAC,gBAAgB,SAAS,KAAK,IAAI,CAAC,iBAAiB,SAAS,KAAK,EAAE;AACvE,SAAO;;CAIT,MAAM,SAAS,GAAG,WAAW;CAC7B,MAAM,QAAQ,iBAAiB,MAAM,KAAK;CAC1C,MAAM,gBAAgB,MAAM,KAAK,SAAU,KAAK,MAAM,KAAK,KAAK,KAAK,SAAS,KAAM;CAGpF,MAAM,oBAAoB,gBAAgB,WAAW,KAAK;CAC1D,MAAM,kBAAkB,gBAAgB,SAAS,KAAK;CAEtD,IAAI,SAAS,cAAc,KAAK,KAAK;AACrC,KAAI,mBAAmB;AACrB,WAAS,KAAK;;AAEhB,KAAI,iBAAiB;AACnB,WAAS,GAAG,OAAO,IAAI;;AAGzB,QAAO;;AAGT,MAAM,mBAAmB,IAAI,IAAI;CAAC;CAAS;CAAY;CAAgB;CAAW,CAAC;;;;;;;;;;;AAYnF,MAAa,uBAAuB,aAAmF;CACrH,MAAM,UAAU,SAAS,QAAQ,WAAW;CAC5C,MAAM,YAAY,QAAQ,MAAM,SAAS,CAAC,MAAM;AAGhD,KAAI,iBAAiB,IAAI,UAAU,EAAE;AACnC,SAAO;GAAE,SAAS,SAAS;GAAS,eAAe;GAAM;;AAI3D,KAAI,SAAS,aAAa;AACxB,MAAI,SAAS,SAAS,cAAc,SAAS,UAAU;GACrD,MAAMO,WAAS,YAAY,SAAS,YAAY,MAAM,SAAS,SAAS;GACxE,MAAMC,kBAAgB,IAAI,OAAO,gBAAgB,SAAS,YAAY,YAAY,SAAS,SAAS,MAAM;AAC1G,UAAO;IAAE,SAASD,WAAS,SAAS;IAAS;IAAe;;EAE9D,MAAMA,WAAS,GAAG,SAAS,KAAK,GAAG,SAAS,YAAY;EACxD,MAAMC,kBAAgB,IAAI,OAAO,IAAI,SAAS,KAAK,MAAM,SAAS,YAAY,MAAM;AACpF,SAAO;GAAE,SAASD,WAAS,SAAS;GAAS;GAAe;;CAI9D,MAAM,SAAS,GAAG,SAAS,KAAK;CAChC,MAAM,gBAAgB,IAAI,OAAO,IAAI,SAAS,KAAK,MAAM;AACzD,QAAO;EAAE,SAAS,SAAS,SAAS;EAAS;EAAe;;;;;;;AAQ9D,MAAa,0BAA0B,WAAmB,kBAAyC;AACjG,KAAI,CAAC,cAAe,QAAO;CAC3B,MAAM,QAAQ,UAAU,MAAM,cAAc;AAC5C,KAAI,CAAC,MAAO,QAAO;AACnB,QAAO,UAAU,MAAM,MAAM,GAAG,OAAO;;;;;;;;;;;AAYzC,MAAa,2BACX,WACA,UACA,kBACkC;CAClC,MAAME,QAA8B,EAAE;AAEtC,MAAK,MAAM,YAAY,WAAW;AAChC,MAAI,CAAC,SAAS,aAAc;EAG5B,MAAM,EAAE,SAAS,kBAAkB,oBAAoB,SAAS;EAEhE,IAAIC;AACJ,MAAI;AACF,eAAY,cAAc,QAAQ;UAC5B;AACN;;EAIF,IAAI,YAAY,uBAAuB,WAAW,cAAc;AAGhE,MAAI,SAAS,oBAAoB,SAAS,iBAAiB,SAAS,GAAG;AACrE,QAAK,IAAI,IAAI,GAAG,IAAI,SAAS,iBAAiB,QAAQ,KAAK;IACzD,MAAM,QAAQ,SAAS,iBAAiB;IACxC,MAAM,WAAW,SAAS,MAAM,MAAM,OAAO,MAAM,IAAI;AACvD,gBAAY,UAAU,QAAQ,iBAAiB,EAAE,KAAK,MAAM,SAAS,GAAG;;;AAK5E,MAAI,cAAc,SAAS,SAAS;AAClC;;EAIF,MAAM,aAAa,iBAAiB,UAAU,SAAS,aAAa,MAAM;EAG1E,MAAM,aAAa,SAAS,WAAW,YAAY,SAAS,QAAQ;AAGpE,MAAI,eAAe,SAAS,SAAS;AACnC;;AAGF,QAAM,KAAK;GACT,OAAO,SAAS,aAAa;GAC7B,KAAK,SAAS,aAAa;GAC3B,SAAS;GACV,CAAC;;AAGJ,QAAO"}
@@ -0,0 +1,131 @@
1
+ import { t as SwcSpanConverter } from "./swc-span-Bi4N-vaI.cjs";
2
+ import { ArrowFunctionExpression, CallExpression, Node, TaggedTemplateExpression } from "@swc/types";
3
+
4
+ //#region packages/common/src/template-extraction/types.d.ts
5
+
6
+ /**
7
+ * Shared types for tagged template extraction and formatting.
8
+ * @module
9
+ */
10
+ /** Operation kind extracted from tagged template tag name. */
11
+ type OperationKind = "query" | "mutation" | "subscription" | "fragment";
12
+ /** A single tagged template extracted from a TypeScript source file. */
13
+ type ExtractedTemplate = {
14
+ /** Resolved schema name from gql.{schemaName}. */
15
+ readonly schemaName: string;
16
+ /** Operation kind from tag name. */
17
+ readonly kind: OperationKind;
18
+ /** Raw GraphQL content between backticks (may contain __FRAG_SPREAD_N__ placeholders). */
19
+ readonly content: string;
20
+ /** Element name from curried tag call (e.g., "GetUser" from query("GetUser")). */
21
+ readonly elementName?: string;
22
+ /** Type name from curried fragment call (e.g., "User" from fragment("UserFields", "User")). */
23
+ readonly typeName?: string;
24
+ /** Character offset range of GraphQL content within TS source (excludes backticks). */
25
+ readonly contentRange?: {
26
+ readonly start: number;
27
+ readonly end: number;
28
+ };
29
+ /** Character offset ranges of interpolation expressions within TS source (for __FRAG_SPREAD_N__ restoration). */
30
+ readonly expressionRanges?: readonly {
31
+ readonly start: number;
32
+ readonly end: number;
33
+ }[];
34
+ };
35
+ /** ExtractedTemplate with guaranteed position information (when positionCtx is provided). */
36
+ type ExtractedTemplateWithPosition = ExtractedTemplate & {
37
+ readonly contentRange: {
38
+ readonly start: number;
39
+ readonly end: number;
40
+ };
41
+ };
42
+ /** A text edit to apply to source code for template formatting. */
43
+ type TemplateFormatEdit = {
44
+ readonly start: number;
45
+ readonly end: number;
46
+ readonly newText: string;
47
+ };
48
+ //#endregion
49
+ //#region packages/common/src/template-extraction/extract.d.ts
50
+
51
+ declare const OPERATION_KINDS: Set<string>;
52
+ declare const isOperationKind: (value: string) => value is OperationKind;
53
+ /** Optional position tracking context for extraction. */
54
+ type PositionTrackingContext = {
55
+ readonly spanOffset: number;
56
+ readonly converter: SwcSpanConverter;
57
+ };
58
+ /**
59
+ * Check if a call expression is a gql.{schemaName}(...) call.
60
+ * Returns the schema name if it is, null otherwise.
61
+ */
62
+ declare const getGqlCallSchemaName: (identifiers: ReadonlySet<string>, call: CallExpression) => string | null;
63
+ /**
64
+ * Extract templates from a gql callback's arrow function body.
65
+ * Handles both expression bodies and block bodies with return statements.
66
+ */
67
+ declare const extractTemplatesFromCallback: (arrow: ArrowFunctionExpression, schemaName: string, positionCtx?: PositionTrackingContext) => ExtractedTemplate[];
68
+ /**
69
+ * Extract a single template from a tagged template expression.
70
+ * Supports both bare-tag (Identifier) and curried (CallExpression) tag forms.
71
+ */
72
+ declare const extractFromTaggedTemplate: (tagged: TaggedTemplateExpression, schemaName: string, templates: ExtractedTemplate[], positionCtx?: PositionTrackingContext) => void;
73
+ /**
74
+ * Find the innermost gql call, unwrapping method chains like .attach().
75
+ */
76
+ declare const findGqlCall: (identifiers: ReadonlySet<string>, node: Node) => CallExpression | null;
77
+ /**
78
+ * Walk AST to find gql calls and extract templates.
79
+ */
80
+ declare function walkAndExtract(node: Node, identifiers: ReadonlySet<string>, positionCtx: PositionTrackingContext): ExtractedTemplateWithPosition[];
81
+ declare function walkAndExtract(node: Node, identifiers: ReadonlySet<string>, positionCtx?: PositionTrackingContext): ExtractedTemplate[];
82
+ //#endregion
83
+ //#region packages/common/src/template-extraction/format.d.ts
84
+ /** A function that formats GraphQL source text. */
85
+ type FormatGraphqlFn = (source: string) => string;
86
+ /**
87
+ * Detect the base indentation for a template by looking at the line
88
+ * containing the opening backtick.
89
+ */
90
+ declare const detectBaseIndent: (tsSource: string, contentStartOffset: number) => string;
91
+ /**
92
+ * Re-indent formatted GraphQL to match the embedding context.
93
+ *
94
+ * - If original was single-line and formatted is single-line, keep as-is
95
+ * - Otherwise, apply base indent + one level to each line, preserving
96
+ * original leading/trailing newline pattern
97
+ */
98
+ declare const reindent: (formatted: string, baseIndent: string, originalContent: string) => string;
99
+ /**
100
+ * Reconstruct a full GraphQL document from template content + metadata.
101
+ *
102
+ * For curried syntax, the template `content` is only the body (e.g., `{ user { id } }`).
103
+ * GraphQL parsers need a full document (e.g., `query GetUser { user { id } }`).
104
+ *
105
+ * For bare-tag syntax, the content already starts with a keyword and is a full document.
106
+ *
107
+ * @returns The wrapped source and a regex pattern to strip the synthetic prefix after formatting
108
+ */
109
+ declare const buildGraphqlWrapper: (template: ExtractedTemplate) => {
110
+ wrapped: string;
111
+ prefixPattern: RegExp | null;
112
+ };
113
+ /**
114
+ * Strip the reconstructed prefix from formatted output to get back the template body.
115
+ * Uses regex pattern matching to handle whitespace normalization by the formatter
116
+ * (e.g., `query Foo ($id: ID!)` → `query Foo($id: ID!)`).
117
+ */
118
+ declare const unwrapFormattedContent: (formatted: string, prefixPattern: RegExp | null) => string;
119
+ /**
120
+ * Format GraphQL templates within TypeScript source.
121
+ *
122
+ * Applies the given format function to each template's content, handles
123
+ * wrapper reconstruction for curried syntax, and re-indents the result
124
+ * to match the TypeScript embedding context.
125
+ *
126
+ * @returns Array of edits to apply to the source (sorted by position, not yet applied)
127
+ */
128
+ declare const formatTemplatesInSource: (templates: readonly ExtractedTemplate[], tsSource: string, formatGraphql: FormatGraphqlFn) => readonly TemplateFormatEdit[];
129
+ //#endregion
130
+ export { ExtractedTemplate, ExtractedTemplateWithPosition, FormatGraphqlFn, OPERATION_KINDS, OperationKind, PositionTrackingContext, TemplateFormatEdit, buildGraphqlWrapper, detectBaseIndent, extractFromTaggedTemplate, extractTemplatesFromCallback, findGqlCall, formatTemplatesInSource, getGqlCallSchemaName, isOperationKind, reindent, unwrapFormattedContent, walkAndExtract };
131
+ //# sourceMappingURL=template-extraction.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-extraction.d.cts","names":[],"sources":["../src/template-extraction/types.ts","../src/template-extraction/extract.ts","../src/template-extraction/format.ts"],"sourcesContent":[],"mappings":"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"}
@@ -0,0 +1,131 @@
1
+ import { t as SwcSpanConverter } from "./swc-span-C4QReqWr.mjs";
2
+ import { ArrowFunctionExpression, CallExpression, Node, TaggedTemplateExpression } from "@swc/types";
3
+
4
+ //#region packages/common/src/template-extraction/types.d.ts
5
+
6
+ /**
7
+ * Shared types for tagged template extraction and formatting.
8
+ * @module
9
+ */
10
+ /** Operation kind extracted from tagged template tag name. */
11
+ type OperationKind = "query" | "mutation" | "subscription" | "fragment";
12
+ /** A single tagged template extracted from a TypeScript source file. */
13
+ type ExtractedTemplate = {
14
+ /** Resolved schema name from gql.{schemaName}. */
15
+ readonly schemaName: string;
16
+ /** Operation kind from tag name. */
17
+ readonly kind: OperationKind;
18
+ /** Raw GraphQL content between backticks (may contain __FRAG_SPREAD_N__ placeholders). */
19
+ readonly content: string;
20
+ /** Element name from curried tag call (e.g., "GetUser" from query("GetUser")). */
21
+ readonly elementName?: string;
22
+ /** Type name from curried fragment call (e.g., "User" from fragment("UserFields", "User")). */
23
+ readonly typeName?: string;
24
+ /** Character offset range of GraphQL content within TS source (excludes backticks). */
25
+ readonly contentRange?: {
26
+ readonly start: number;
27
+ readonly end: number;
28
+ };
29
+ /** Character offset ranges of interpolation expressions within TS source (for __FRAG_SPREAD_N__ restoration). */
30
+ readonly expressionRanges?: readonly {
31
+ readonly start: number;
32
+ readonly end: number;
33
+ }[];
34
+ };
35
+ /** ExtractedTemplate with guaranteed position information (when positionCtx is provided). */
36
+ type ExtractedTemplateWithPosition = ExtractedTemplate & {
37
+ readonly contentRange: {
38
+ readonly start: number;
39
+ readonly end: number;
40
+ };
41
+ };
42
+ /** A text edit to apply to source code for template formatting. */
43
+ type TemplateFormatEdit = {
44
+ readonly start: number;
45
+ readonly end: number;
46
+ readonly newText: string;
47
+ };
48
+ //#endregion
49
+ //#region packages/common/src/template-extraction/extract.d.ts
50
+
51
+ declare const OPERATION_KINDS: Set<string>;
52
+ declare const isOperationKind: (value: string) => value is OperationKind;
53
+ /** Optional position tracking context for extraction. */
54
+ type PositionTrackingContext = {
55
+ readonly spanOffset: number;
56
+ readonly converter: SwcSpanConverter;
57
+ };
58
+ /**
59
+ * Check if a call expression is a gql.{schemaName}(...) call.
60
+ * Returns the schema name if it is, null otherwise.
61
+ */
62
+ declare const getGqlCallSchemaName: (identifiers: ReadonlySet<string>, call: CallExpression) => string | null;
63
+ /**
64
+ * Extract templates from a gql callback's arrow function body.
65
+ * Handles both expression bodies and block bodies with return statements.
66
+ */
67
+ declare const extractTemplatesFromCallback: (arrow: ArrowFunctionExpression, schemaName: string, positionCtx?: PositionTrackingContext) => ExtractedTemplate[];
68
+ /**
69
+ * Extract a single template from a tagged template expression.
70
+ * Supports both bare-tag (Identifier) and curried (CallExpression) tag forms.
71
+ */
72
+ declare const extractFromTaggedTemplate: (tagged: TaggedTemplateExpression, schemaName: string, templates: ExtractedTemplate[], positionCtx?: PositionTrackingContext) => void;
73
+ /**
74
+ * Find the innermost gql call, unwrapping method chains like .attach().
75
+ */
76
+ declare const findGqlCall: (identifiers: ReadonlySet<string>, node: Node) => CallExpression | null;
77
+ /**
78
+ * Walk AST to find gql calls and extract templates.
79
+ */
80
+ declare function walkAndExtract(node: Node, identifiers: ReadonlySet<string>, positionCtx: PositionTrackingContext): ExtractedTemplateWithPosition[];
81
+ declare function walkAndExtract(node: Node, identifiers: ReadonlySet<string>, positionCtx?: PositionTrackingContext): ExtractedTemplate[];
82
+ //#endregion
83
+ //#region packages/common/src/template-extraction/format.d.ts
84
+ /** A function that formats GraphQL source text. */
85
+ type FormatGraphqlFn = (source: string) => string;
86
+ /**
87
+ * Detect the base indentation for a template by looking at the line
88
+ * containing the opening backtick.
89
+ */
90
+ declare const detectBaseIndent: (tsSource: string, contentStartOffset: number) => string;
91
+ /**
92
+ * Re-indent formatted GraphQL to match the embedding context.
93
+ *
94
+ * - If original was single-line and formatted is single-line, keep as-is
95
+ * - Otherwise, apply base indent + one level to each line, preserving
96
+ * original leading/trailing newline pattern
97
+ */
98
+ declare const reindent: (formatted: string, baseIndent: string, originalContent: string) => string;
99
+ /**
100
+ * Reconstruct a full GraphQL document from template content + metadata.
101
+ *
102
+ * For curried syntax, the template `content` is only the body (e.g., `{ user { id } }`).
103
+ * GraphQL parsers need a full document (e.g., `query GetUser { user { id } }`).
104
+ *
105
+ * For bare-tag syntax, the content already starts with a keyword and is a full document.
106
+ *
107
+ * @returns The wrapped source and a regex pattern to strip the synthetic prefix after formatting
108
+ */
109
+ declare const buildGraphqlWrapper: (template: ExtractedTemplate) => {
110
+ wrapped: string;
111
+ prefixPattern: RegExp | null;
112
+ };
113
+ /**
114
+ * Strip the reconstructed prefix from formatted output to get back the template body.
115
+ * Uses regex pattern matching to handle whitespace normalization by the formatter
116
+ * (e.g., `query Foo ($id: ID!)` → `query Foo($id: ID!)`).
117
+ */
118
+ declare const unwrapFormattedContent: (formatted: string, prefixPattern: RegExp | null) => string;
119
+ /**
120
+ * Format GraphQL templates within TypeScript source.
121
+ *
122
+ * Applies the given format function to each template's content, handles
123
+ * wrapper reconstruction for curried syntax, and re-indents the result
124
+ * to match the TypeScript embedding context.
125
+ *
126
+ * @returns Array of edits to apply to the source (sorted by position, not yet applied)
127
+ */
128
+ declare const formatTemplatesInSource: (templates: readonly ExtractedTemplate[], tsSource: string, formatGraphql: FormatGraphqlFn) => readonly TemplateFormatEdit[];
129
+ //#endregion
130
+ export { ExtractedTemplate, ExtractedTemplateWithPosition, FormatGraphqlFn, OPERATION_KINDS, OperationKind, PositionTrackingContext, TemplateFormatEdit, buildGraphqlWrapper, detectBaseIndent, extractFromTaggedTemplate, extractTemplatesFromCallback, findGqlCall, formatTemplatesInSource, getGqlCallSchemaName, isOperationKind, reindent, unwrapFormattedContent, walkAndExtract };
131
+ //# sourceMappingURL=template-extraction.d.mts.map