@soda-gql/formatter 0.12.4 → 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.
package/dist/index.cjs CHANGED
@@ -187,7 +187,7 @@ const getGraphqlModule = () => {
187
187
  if (_graphqlModuleError) return (0, neverthrow.err)({
188
188
  type: "FormatError",
189
189
  code: "MISSING_DEPENDENCY",
190
- message: "The \"graphql\" package is required for --format-tagged-templates. Install it with: bun add graphql"
190
+ message: "The \"graphql\" package is required for soda-gql formatter. Install it with: bun add graphql"
191
191
  });
192
192
  if (!_graphqlModule) try {
193
193
  _graphqlModule = require$1("graphql");
@@ -196,7 +196,7 @@ const getGraphqlModule = () => {
196
196
  return (0, neverthrow.err)({
197
197
  type: "FormatError",
198
198
  code: "MISSING_DEPENDENCY",
199
- message: "The \"graphql\" package is required for --format-tagged-templates. Install it with: bun add graphql",
199
+ message: "The \"graphql\" package is required for soda-gql formatter. Install it with: bun add graphql",
200
200
  cause
201
201
  });
202
202
  }
@@ -262,7 +262,7 @@ const traverse = (module$1, gqlIdentifiers, onObjectExpression) => {
262
262
  * Optionally injects fragment keys for anonymous fragments.
263
263
  */
264
264
  const format = (options) => {
265
- const { sourceCode, filePath, injectFragmentKeys = false, formatTaggedTemplates = false } = options;
265
+ const { sourceCode, filePath, injectFragmentKeys = false } = options;
266
266
  let module$1;
267
267
  try {
268
268
  const program = (0, __swc_core.parseSync)(sourceCode, {
@@ -287,6 +287,7 @@ const format = (options) => {
287
287
  });
288
288
  }
289
289
  const spanOffset = module$1.span.start;
290
+ const converter = (0, __soda_gql_common_utils.createSwcSpanConverter)(sourceCode);
290
291
  const gqlIdentifiers = collectGqlIdentifiers(module$1);
291
292
  if (gqlIdentifiers.size === 0) return (0, neverthrow.ok)({
292
293
  modified: false,
@@ -294,7 +295,7 @@ const format = (options) => {
294
295
  });
295
296
  const insertionPoints = [];
296
297
  traverse(module$1, gqlIdentifiers, (object, _parent, callbackContext) => {
297
- const objectStart = object.span.start - spanOffset;
298
+ const objectStart = converter.byteOffsetToCharIndex(object.span.start - spanOffset);
298
299
  if (callbackContext.isFragmentConfig && injectFragmentKeys && !hasKeyProperty(object)) {
299
300
  const key = generateFragmentKey();
300
301
  if (hasExistingNewline(sourceCode, objectStart)) {
@@ -315,26 +316,23 @@ const format = (options) => {
315
316
  content: NEWLINE_INSERTION
316
317
  });
317
318
  });
318
- if (formatTaggedTemplates) {
319
- const graphqlResult = getGraphqlModule();
320
- if (graphqlResult.isErr()) return (0, neverthrow.err)(graphqlResult.error);
321
- const { parse: parseGraphql, print: printGraphql } = graphqlResult.value;
322
- const positionCtx = {
323
- spanOffset,
324
- converter: (0, __soda_gql_common_utils.createSwcSpanConverter)(sourceCode)
319
+ const graphqlResult = getGraphqlModule();
320
+ if (graphqlResult.isErr()) return (0, neverthrow.err)(graphqlResult.error);
321
+ const { parse: parseGraphql, print: printGraphql } = graphqlResult.value;
322
+ const templates = (0, __soda_gql_common_template_extraction.walkAndExtract)(module$1, gqlIdentifiers, {
323
+ spanOffset,
324
+ converter
325
+ });
326
+ if (templates.length > 0) {
327
+ const defaultFormat = (source) => {
328
+ return printGraphql(parseGraphql(source, { noLocation: false }));
325
329
  };
326
- const templates = (0, __soda_gql_common_template_extraction.walkAndExtract)(module$1, gqlIdentifiers, positionCtx);
327
- if (templates.length > 0) {
328
- const defaultFormat = (source) => {
329
- return printGraphql(parseGraphql(source, { noLocation: false }));
330
- };
331
- const templateEdits = (0, __soda_gql_common_template_extraction.formatTemplatesInSource)(templates, sourceCode, defaultFormat);
332
- for (const edit of templateEdits) insertionPoints.push({
333
- position: edit.start,
334
- content: edit.newText,
335
- endPosition: edit.end
336
- });
337
- }
330
+ const templateEdits = (0, __soda_gql_common_template_extraction.formatTemplatesInSource)(templates, sourceCode, defaultFormat);
331
+ for (const edit of templateEdits) insertionPoints.push({
332
+ position: edit.start,
333
+ content: edit.newText,
334
+ endPosition: edit.end
335
+ });
338
336
  }
339
337
  if (insertionPoints.length === 0) return (0, neverthrow.ok)({
340
338
  modified: false,
@@ -356,7 +354,7 @@ const format = (options) => {
356
354
  * Useful for pre-commit hooks or CI checks.
357
355
  */
358
356
  const needsFormat = (options) => {
359
- const { sourceCode, filePath, formatTaggedTemplates = false } = options;
357
+ const { sourceCode, filePath } = options;
360
358
  let module$1;
361
359
  try {
362
360
  const program = (0, __swc_core.parseSync)(sourceCode, {
@@ -381,23 +379,23 @@ const needsFormat = (options) => {
381
379
  });
382
380
  }
383
381
  const spanOffset = module$1.span.start;
382
+ const converter = (0, __soda_gql_common_utils.createSwcSpanConverter)(sourceCode);
384
383
  const gqlIdentifiers = collectGqlIdentifiers(module$1);
385
384
  if (gqlIdentifiers.size === 0) return (0, neverthrow.ok)(false);
386
385
  let needsFormatting = false;
387
386
  traverse(module$1, gqlIdentifiers, (object, _parent, callbackContext) => {
388
387
  if (needsFormatting) return;
389
388
  if (callbackContext.isFragmentConfig) return;
390
- if (!hasExistingNewline(sourceCode, object.span.start - spanOffset)) needsFormatting = true;
389
+ if (!hasExistingNewline(sourceCode, converter.byteOffsetToCharIndex(object.span.start - spanOffset))) needsFormatting = true;
391
390
  });
392
- if (!needsFormatting && formatTaggedTemplates) {
391
+ if (!needsFormatting) {
393
392
  const graphqlResult = getGraphqlModule();
394
393
  if (graphqlResult.isErr()) return (0, neverthrow.err)(graphqlResult.error);
395
394
  const { parse: parseGraphql, print: printGraphql } = graphqlResult.value;
396
- const positionCtx = {
395
+ const templates = (0, __soda_gql_common_template_extraction.walkAndExtract)(module$1, gqlIdentifiers, {
397
396
  spanOffset,
398
- converter: (0, __soda_gql_common_utils.createSwcSpanConverter)(sourceCode)
399
- };
400
- const templates = (0, __soda_gql_common_template_extraction.walkAndExtract)(module$1, gqlIdentifiers, positionCtx);
397
+ converter
398
+ });
401
399
  if (templates.length > 0) {
402
400
  const defaultFormat = (source) => {
403
401
  return printGraphql(parseGraphql(source, { noLocation: false }));
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["bodyObject: ObjectExpression | null","module","require","_graphqlModule: GraphqlModule | undefined","_graphqlModuleError: Error | undefined","initialContext: TraversalContext","module","module: Module","insertionPoints: InsertionPoint[]","positionCtx: PositionTrackingContext","defaultFormat: FormatGraphqlFn"],"sources":["../src/detection.ts","../src/insertion.ts","../src/format.ts"],"sourcesContent":["import type {\n ArrowFunctionExpression,\n CallExpression,\n Expression,\n Module,\n ObjectExpression,\n ObjectPatternProperty,\n Pattern,\n} from \"@swc/types\";\n\n/**\n * Check if an expression is a reference to gql\n * Handles: gql, namespace.gql\n */\nexport const isGqlReference = (node: Expression, gqlIdentifiers: ReadonlySet<string>): boolean => {\n // Unwrap TsNonNullExpression: gql! -> gql\n const unwrapped = node.type === \"TsNonNullExpression\" ? (node as { type: string; expression: Expression }).expression : node;\n if (unwrapped.type === \"Identifier\") {\n return gqlIdentifiers.has(unwrapped.value);\n }\n if (unwrapped.type === \"MemberExpression\" && unwrapped.property.type === \"Identifier\" && unwrapped.property.value === \"gql\") {\n return true;\n }\n return false;\n};\n\n/**\n * Check if a call expression is a gql definition call\n * Handles: gql.default(...), gql.model(...), gql.schemaName(...) (multi-schema)\n */\nexport const isGqlDefinitionCall = (node: CallExpression, gqlIdentifiers: ReadonlySet<string>): boolean => {\n if (node.callee.type !== \"MemberExpression\") return false;\n const { object } = node.callee;\n\n // Check object is a gql reference first\n if (!isGqlReference(object, gqlIdentifiers)) return false;\n\n // Verify first argument is an arrow function (factory pattern)\n // SWC's arguments are ExprOrSpread[], so access via .expression\n const firstArg = node.arguments[0];\n if (!firstArg || (firstArg.expression.type !== \"ArrowFunctionExpression\" && firstArg.expression.type !== \"FunctionExpression\"))\n return false;\n\n return true;\n};\n\n/**\n * Check if an object expression is a field selection object\n * Field selection objects are returned from arrow functions with ({ f }) or ({ f, $ }) parameter\n */\nexport const isFieldSelectionObject = (object: ObjectExpression, parent: ArrowFunctionExpression): boolean => {\n // The object must be the body of the arrow function\n // Handle both direct ObjectExpression and parenthesized ObjectExpression: `({ ... })`\n let bodyObject: ObjectExpression | null = null;\n\n if (parent.body.type === \"ObjectExpression\") {\n bodyObject = parent.body;\n } else if (parent.body.type === \"ParenthesisExpression\") {\n // Handle `({ f }) => ({ ...f.id() })` pattern where body is parenthesized\n const inner = parent.body.expression;\n if (inner.type === \"ObjectExpression\") {\n bodyObject = inner;\n }\n }\n\n if (!bodyObject) return false;\n if (bodyObject.span.start !== object.span.start) return false;\n\n // Check if first parameter has 'f' destructured\n const param = parent.params[0];\n if (!param || param.type !== \"ObjectPattern\") return false;\n\n return param.properties.some((p: ObjectPatternProperty) => {\n if (p.type === \"KeyValuePatternProperty\" && p.key.type === \"Identifier\") {\n return p.key.value === \"f\";\n }\n if (p.type === \"AssignmentPatternProperty\" && p.key.type === \"Identifier\") {\n return p.key.value === \"f\";\n }\n return false;\n });\n};\n\n/**\n * Collect gql identifiers from import declarations\n */\nexport const collectGqlIdentifiers = (module: Module): Set<string> => {\n const gqlIdentifiers = new Set<string>();\n\n for (const item of module.body) {\n if (item.type !== \"ImportDeclaration\") continue;\n\n for (const specifier of item.specifiers) {\n if (specifier.type === \"ImportSpecifier\") {\n const imported = specifier.imported?.value ?? specifier.local.value;\n if (imported === \"gql\") {\n gqlIdentifiers.add(specifier.local.value);\n }\n }\n if (specifier.type === \"ImportDefaultSpecifier\") {\n // Check if default import might be gql\n if (specifier.local.value === \"gql\") {\n gqlIdentifiers.add(specifier.local.value);\n }\n }\n if (specifier.type === \"ImportNamespaceSpecifier\") {\n // namespace import: import * as ns from \"...\"\n // Would need ns.gql pattern - handled by isGqlReference\n }\n }\n }\n\n return gqlIdentifiers;\n};\n\n/**\n * Check if a call expression is a fragment definition call.\n * Pattern: fragment.TypeName({ ... }) where `fragment` comes from gql factory destructuring.\n */\nexport const isFragmentDefinitionCall = (node: CallExpression, fragmentIdentifiers: ReadonlySet<string>): boolean => {\n if (node.callee.type !== \"MemberExpression\") return false;\n\n const { object } = node.callee;\n\n if (object.type !== \"Identifier\") return false;\n if (!fragmentIdentifiers.has(object.value)) return false;\n\n const firstArg = node.arguments[0];\n if (!firstArg || firstArg.expression.type !== \"ObjectExpression\") return false;\n\n return true;\n};\n\n/**\n * Check if an object expression already has a `key` property.\n */\nexport const hasKeyProperty = (obj: ObjectExpression): boolean => {\n return obj.properties.some((prop) => {\n if (prop.type === \"KeyValueProperty\" && prop.key.type === \"Identifier\") {\n return prop.key.value === \"key\";\n }\n return false;\n });\n};\n\n/**\n * Collect fragment identifiers from gql factory arrow function parameter.\n * Looks for patterns like: ({ fragment }) => ... or ({ fragment: f }) => ...\n */\nexport const collectFragmentIdentifiers = (arrowFunction: ArrowFunctionExpression): Set<string> => {\n const fragmentIdentifiers = new Set<string>();\n\n const param = arrowFunction.params[0];\n if (param?.type !== \"ObjectPattern\") return fragmentIdentifiers;\n\n for (const p of param.properties as ObjectPatternProperty[]) {\n if (p.type === \"KeyValuePatternProperty\" && p.key.type === \"Identifier\" && p.key.value === \"fragment\") {\n const localName = extractIdentifierName(p.value);\n if (localName) fragmentIdentifiers.add(localName);\n }\n if (p.type === \"AssignmentPatternProperty\" && p.key.type === \"Identifier\" && p.key.value === \"fragment\") {\n fragmentIdentifiers.add(p.key.value);\n }\n }\n\n return fragmentIdentifiers;\n};\n\n/**\n * Extract identifier name from a pattern node.\n */\nconst extractIdentifierName = (pattern: Pattern): string | null => {\n if (pattern.type === \"Identifier\") return pattern.value;\n // biome-ignore lint/suspicious/noExplicitAny: SWC types\n if ((pattern as any).type === \"BindingIdentifier\") return (pattern as any).value;\n return null;\n};\n","import { randomBytes } from \"node:crypto\";\n\n/**\n * The newline string to insert after object opening brace\n */\nexport const NEWLINE_INSERTION = \"\\n\";\n\n/**\n * Generate a random 8-character hex key for fragment identification.\n * Uses Node.js crypto for cryptographically secure random bytes.\n */\nexport const generateFragmentKey = (): string => {\n return randomBytes(4).toString(\"hex\");\n};\n\n/**\n * Create the key property insertion string.\n *\n * For single-line objects: returns ` key: \"xxxxxxxx\",\\n`\n * For multi-line objects (with indentation): returns `{indentation}key: \"xxxxxxxx\",\\n`\n * (the existing indentation before the next property is preserved)\n *\n * @param key - The hex key string\n * @param indentation - Optional indentation string for multi-line objects\n */\nexport const createKeyInsertion = (key: string, indentation?: string): string => {\n if (indentation !== undefined) {\n // Multi-line: key goes on its own line with indentation\n // The next property's indentation is already in the source, so don't duplicate it\n return `${indentation}key: \"${key}\",\\n`;\n }\n // Single-line: space before key, newline after\n return ` key: \"${key}\",\\n`;\n};\n\n/**\n * Check if there's already a newline after the opening brace.\n * Uses string inspection rather than AST.\n *\n * @param source - The source code string\n * @param objectStartPos - The position of the `{` character in the source\n */\nexport const hasExistingNewline = (source: string, objectStartPos: number): boolean => {\n // Skip the `{` character\n const pos = objectStartPos + 1;\n\n // Check if next character is a newline\n const nextChar = source[pos];\n return nextChar === \"\\n\" || nextChar === \"\\r\";\n};\n\n/**\n * Detect the indentation used after an opening brace.\n * Returns the whitespace string after the newline, or null if no newline.\n *\n * @param source - The source code string\n * @param objectStartPos - The position of the `{` character\n */\nexport const detectIndentationAfterBrace = (source: string, objectStartPos: number): string | null => {\n const afterBrace = objectStartPos + 1;\n const char = source[afterBrace];\n\n // Check for newline (handles both \\n and \\r\\n)\n if (char !== \"\\n\" && char !== \"\\r\") {\n return null;\n }\n\n // Skip past the newline character(s)\n let pos = afterBrace + 1;\n if (char === \"\\r\" && source[pos] === \"\\n\") {\n pos++;\n }\n\n // Collect whitespace (indentation)\n let indentation = \"\";\n while (pos < source.length) {\n const c = source[pos];\n if (c === \" \" || c === \"\\t\") {\n indentation += c;\n pos++;\n } else {\n break;\n }\n }\n\n return indentation;\n};\n","import { createRequire } from \"node:module\";\nimport {\n type FormatGraphqlFn,\n formatTemplatesInSource,\n type PositionTrackingContext,\n walkAndExtract,\n} from \"@soda-gql/common/template-extraction\";\nimport { createSwcSpanConverter } from \"@soda-gql/common/utils\";\nimport { parseSync } from \"@swc/core\";\nimport type { ArrowFunctionExpression, CallExpression, Module, Node, ObjectExpression } from \"@swc/types\";\nimport { err, ok, type Result } from \"neverthrow\";\n\nconst require = createRequire(import.meta.url);\n\ntype GraphqlModule = {\n parse: (source: string, options?: { noLocation?: boolean }) => unknown;\n print: (ast: unknown) => string;\n};\n\nlet _graphqlModule: GraphqlModule | undefined;\nlet _graphqlModuleError: Error | undefined;\n\nconst getGraphqlModule = (): Result<GraphqlModule, FormatError> => {\n if (_graphqlModuleError) {\n return err({\n type: \"FormatError\",\n code: \"MISSING_DEPENDENCY\",\n message: 'The \"graphql\" package is required for --format-tagged-templates. Install it with: bun add graphql',\n });\n }\n if (!_graphqlModule) {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n _graphqlModule = require(\"graphql\") as GraphqlModule;\n } catch (cause) {\n _graphqlModuleError = cause instanceof Error ? cause : new Error(String(cause));\n return err({\n type: \"FormatError\",\n code: \"MISSING_DEPENDENCY\",\n message: 'The \"graphql\" package is required for --format-tagged-templates. Install it with: bun add graphql',\n cause,\n });\n }\n }\n return ok(_graphqlModule);\n};\n\nimport {\n collectFragmentIdentifiers,\n collectGqlIdentifiers,\n hasKeyProperty,\n isFieldSelectionObject,\n isFragmentDefinitionCall,\n isGqlDefinitionCall,\n} from \"./detection\";\nimport {\n createKeyInsertion,\n detectIndentationAfterBrace,\n generateFragmentKey,\n hasExistingNewline,\n NEWLINE_INSERTION,\n} from \"./insertion\";\nimport type { FormatError, FormatOptions, FormatResult } from \"./types\";\n\ntype InsertionPoint = {\n readonly position: number;\n readonly content: string;\n readonly endPosition?: number;\n};\n\ntype TraversalContext = {\n insideGqlDefinition: boolean;\n currentArrowFunction: ArrowFunctionExpression | null;\n fragmentIdentifiers: ReadonlySet<string>;\n};\n\ntype TraversalCallbackContext = {\n readonly isFragmentConfig: boolean;\n};\n\ntype TraversalCallback = (\n object: ObjectExpression,\n parent: ArrowFunctionExpression,\n callbackContext: TraversalCallbackContext,\n) => void;\n\n/**\n * Simple recursive AST traversal\n */\nconst traverseNode = (\n node: Node,\n context: TraversalContext,\n gqlIdentifiers: ReadonlySet<string>,\n onObjectExpression: TraversalCallback,\n): void => {\n // Check for gql definition call entry and collect fragment identifiers\n if (node.type === \"CallExpression\" && isGqlDefinitionCall(node as CallExpression, gqlIdentifiers)) {\n const call = node as CallExpression;\n const firstArg = call.arguments[0];\n if (firstArg?.expression.type === \"ArrowFunctionExpression\") {\n const arrow = firstArg.expression as ArrowFunctionExpression;\n const fragmentIds = collectFragmentIdentifiers(arrow);\n context = {\n ...context,\n insideGqlDefinition: true,\n fragmentIdentifiers: new Set([...context.fragmentIdentifiers, ...fragmentIds]),\n };\n } else {\n context = { ...context, insideGqlDefinition: true };\n }\n }\n\n // Check for fragment definition call: fragment.TypeName({ ... })\n if (\n node.type === \"CallExpression\" &&\n context.insideGqlDefinition &&\n isFragmentDefinitionCall(node as CallExpression, context.fragmentIdentifiers)\n ) {\n const call = node as CallExpression;\n const firstArg = call.arguments[0];\n if (firstArg?.expression.type === \"ObjectExpression\" && context.currentArrowFunction) {\n onObjectExpression(firstArg.expression as ObjectExpression, context.currentArrowFunction, {\n isFragmentConfig: true,\n });\n }\n }\n\n // Handle object expressions - check if it's the body of the current arrow function\n if (\n node.type === \"ObjectExpression\" &&\n context.insideGqlDefinition &&\n context.currentArrowFunction &&\n isFieldSelectionObject(node as ObjectExpression, context.currentArrowFunction)\n ) {\n onObjectExpression(node as ObjectExpression, context.currentArrowFunction, { isFragmentConfig: false });\n }\n\n // Recursively visit children\n if (node.type === \"CallExpression\") {\n const call = node as CallExpression;\n traverseNode(call.callee as Node, context, gqlIdentifiers, onObjectExpression);\n for (const arg of call.arguments) {\n traverseNode(arg.expression as Node, context, gqlIdentifiers, onObjectExpression);\n }\n } else if (node.type === \"ArrowFunctionExpression\") {\n const arrow = node as ArrowFunctionExpression;\n // Update context with the new arrow function\n const childContext = { ...context, currentArrowFunction: arrow };\n if (arrow.body.type !== \"BlockStatement\") {\n traverseNode(arrow.body as Node, childContext, gqlIdentifiers, onObjectExpression);\n }\n } else if (node.type === \"ParenthesisExpression\") {\n // Handle parenthesized expressions like `({ ...f.id() })`\n // biome-ignore lint/suspicious/noExplicitAny: SWC types\n const paren = node as any;\n if (paren.expression) {\n traverseNode(paren.expression as Node, context, gqlIdentifiers, onObjectExpression);\n }\n } else if (node.type === \"MemberExpression\") {\n // biome-ignore lint/suspicious/noExplicitAny: SWC types\n const member = node as any;\n traverseNode(member.object as Node, context, gqlIdentifiers, onObjectExpression);\n } else if (node.type === \"ObjectExpression\") {\n const obj = node as ObjectExpression;\n for (const prop of obj.properties) {\n if (prop.type === \"SpreadElement\") {\n traverseNode(prop.arguments as Node, context, gqlIdentifiers, onObjectExpression);\n } else if (prop.type === \"KeyValueProperty\") {\n traverseNode(prop.value as Node, context, gqlIdentifiers, onObjectExpression);\n }\n }\n } else {\n // Generic traversal for other node types\n for (const value of Object.values(node)) {\n if (Array.isArray(value)) {\n for (const child of value) {\n if (child && typeof child === \"object\" && \"type\" in child) {\n traverseNode(child as Node, context, gqlIdentifiers, onObjectExpression);\n }\n }\n } else if (value && typeof value === \"object\" && \"type\" in value) {\n traverseNode(value as Node, context, gqlIdentifiers, onObjectExpression);\n }\n }\n }\n};\n\nconst traverse = (module: Module, gqlIdentifiers: ReadonlySet<string>, onObjectExpression: TraversalCallback): void => {\n const initialContext: TraversalContext = {\n insideGqlDefinition: false,\n currentArrowFunction: null,\n fragmentIdentifiers: new Set(),\n };\n for (const statement of module.body) {\n traverseNode(statement, initialContext, gqlIdentifiers, onObjectExpression);\n }\n};\n\n/**\n * Format soda-gql field selection objects by inserting newlines.\n * Optionally injects fragment keys for anonymous fragments.\n */\nexport const format = (options: FormatOptions): Result<FormatResult, FormatError> => {\n const { sourceCode, filePath, injectFragmentKeys = false, formatTaggedTemplates = false } = options;\n\n // Parse source code with SWC\n let module: Module;\n try {\n const program = parseSync(sourceCode, {\n syntax: \"typescript\",\n tsx: filePath?.endsWith(\".tsx\") ?? true,\n target: \"es2022\",\n decorators: false,\n dynamicImport: true,\n });\n\n if (program.type !== \"Module\") {\n return err({\n type: \"FormatError\",\n code: \"PARSE_ERROR\",\n message: `Not a module${filePath ? ` (${filePath})` : \"\"}`,\n });\n }\n module = program;\n } catch (cause) {\n return err({\n type: \"FormatError\",\n code: \"PARSE_ERROR\",\n message: `Failed to parse source code${filePath ? ` (${filePath})` : \"\"}`,\n cause,\n });\n }\n\n // Calculate span offset for position normalization\n // SWC's BytePos counter accumulates across parseSync calls within the same process\n // Using module.span.start ensures correct position calculation regardless of accumulation\n const spanOffset = module.span.start;\n\n // Collect gql identifiers from imports\n const gqlIdentifiers = collectGqlIdentifiers(module);\n if (gqlIdentifiers.size === 0) {\n return ok({ modified: false, sourceCode });\n }\n\n // Collect insertion points\n const insertionPoints: InsertionPoint[] = [];\n\n traverse(module, gqlIdentifiers, (object, _parent, callbackContext) => {\n // Calculate actual position in source\n const objectStart = object.span.start - spanOffset;\n\n // For fragment config objects, inject key if enabled and not present\n if (callbackContext.isFragmentConfig && injectFragmentKeys && !hasKeyProperty(object)) {\n const key = generateFragmentKey();\n\n if (hasExistingNewline(sourceCode, objectStart)) {\n // Multi-line: insert after newline, preserving indentation\n const indentation = detectIndentationAfterBrace(sourceCode, objectStart) ?? \"\";\n let insertPos = objectStart + 2; // Skip { and \\n\n if (sourceCode[objectStart + 1] === \"\\r\") insertPos++;\n\n insertionPoints.push({\n position: insertPos,\n content: createKeyInsertion(key, indentation),\n });\n } else {\n // Single-line: insert right after {\n insertionPoints.push({\n position: objectStart + 1,\n content: createKeyInsertion(key),\n });\n }\n }\n\n // For field selection objects, insert newline if not present\n if (!callbackContext.isFragmentConfig && !hasExistingNewline(sourceCode, objectStart)) {\n insertionPoints.push({\n position: objectStart + 1,\n content: NEWLINE_INSERTION,\n });\n }\n });\n\n // Tagged template formatting (when enabled)\n if (formatTaggedTemplates) {\n const graphqlResult = getGraphqlModule();\n if (graphqlResult.isErr()) {\n return err(graphqlResult.error);\n }\n const { parse: parseGraphql, print: printGraphql } = graphqlResult.value;\n\n const converter = createSwcSpanConverter(sourceCode);\n const positionCtx: PositionTrackingContext = { spanOffset, converter };\n const templates = walkAndExtract(module as unknown as Node, gqlIdentifiers, positionCtx);\n\n if (templates.length > 0) {\n const defaultFormat: FormatGraphqlFn = (source) => {\n const ast = parseGraphql(source, { noLocation: false });\n return printGraphql(ast);\n };\n\n const templateEdits = formatTemplatesInSource(templates, sourceCode, defaultFormat);\n\n for (const edit of templateEdits) {\n insertionPoints.push({\n position: edit.start,\n content: edit.newText,\n endPosition: edit.end,\n });\n }\n }\n }\n\n // Apply insertions\n if (insertionPoints.length === 0) {\n return ok({ modified: false, sourceCode });\n }\n\n // Sort in descending order to insert from end to beginning\n // This preserves earlier positions while modifying later parts\n const sortedPoints = [...insertionPoints].sort((a, b) => b.position - a.position);\n\n let result = sourceCode;\n for (const point of sortedPoints) {\n const end = point.endPosition ?? point.position;\n result = result.slice(0, point.position) + point.content + result.slice(end);\n }\n\n return ok({ modified: true, sourceCode: result });\n};\n\n/**\n * Check if a file needs formatting (has unformatted field selections).\n * Useful for pre-commit hooks or CI checks.\n */\nexport const needsFormat = (options: FormatOptions): Result<boolean, FormatError> => {\n const { sourceCode, filePath, formatTaggedTemplates = false } = options;\n\n // Parse source code with SWC\n let module: Module;\n try {\n const program = parseSync(sourceCode, {\n syntax: \"typescript\",\n tsx: filePath?.endsWith(\".tsx\") ?? true,\n target: \"es2022\",\n decorators: false,\n dynamicImport: true,\n });\n\n if (program.type !== \"Module\") {\n return err({\n type: \"FormatError\",\n code: \"PARSE_ERROR\",\n message: `Not a module${filePath ? ` (${filePath})` : \"\"}`,\n });\n }\n module = program;\n } catch (cause) {\n return err({\n type: \"FormatError\",\n code: \"PARSE_ERROR\",\n message: `Failed to parse source code${filePath ? ` (${filePath})` : \"\"}`,\n cause,\n });\n }\n\n const spanOffset = module.span.start;\n const gqlIdentifiers = collectGqlIdentifiers(module);\n\n if (gqlIdentifiers.size === 0) {\n return ok(false);\n }\n\n let needsFormatting = false;\n\n traverse(module, gqlIdentifiers, (object, _parent, callbackContext) => {\n if (needsFormatting) return; // Early exit\n\n // Skip fragment config objects for needsFormat check (key injection is optional)\n if (callbackContext.isFragmentConfig) return;\n\n const objectStart = object.span.start - spanOffset;\n if (!hasExistingNewline(sourceCode, objectStart)) {\n needsFormatting = true;\n }\n });\n\n // Check tagged templates when enabled\n if (!needsFormatting && formatTaggedTemplates) {\n const graphqlResult = getGraphqlModule();\n if (graphqlResult.isErr()) {\n return err(graphqlResult.error);\n }\n const { parse: parseGraphql, print: printGraphql } = graphqlResult.value;\n\n const converter = createSwcSpanConverter(sourceCode);\n const positionCtx: PositionTrackingContext = { spanOffset, converter };\n const templates = walkAndExtract(module as unknown as Node, gqlIdentifiers, positionCtx);\n\n if (templates.length > 0) {\n const defaultFormat: FormatGraphqlFn = (source) => {\n const ast = parseGraphql(source, { noLocation: false });\n return printGraphql(ast);\n };\n\n const templateEdits = formatTemplatesInSource(templates, sourceCode, defaultFormat);\n if (templateEdits.length > 0) {\n needsFormatting = true;\n }\n }\n }\n\n return ok(needsFormatting);\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["bodyObject: ObjectExpression | null","module","require","_graphqlModule: GraphqlModule | undefined","_graphqlModuleError: Error | undefined","initialContext: TraversalContext","module","module: Module","insertionPoints: InsertionPoint[]","defaultFormat: FormatGraphqlFn"],"sources":["../src/detection.ts","../src/insertion.ts","../src/format.ts"],"sourcesContent":["import type {\n ArrowFunctionExpression,\n CallExpression,\n Expression,\n Module,\n ObjectExpression,\n ObjectPatternProperty,\n Pattern,\n} from \"@swc/types\";\n\n/**\n * Check if an expression is a reference to gql\n * Handles: gql, namespace.gql\n */\nexport const isGqlReference = (node: Expression, gqlIdentifiers: ReadonlySet<string>): boolean => {\n // Unwrap TsNonNullExpression: gql! -> gql\n const unwrapped = node.type === \"TsNonNullExpression\" ? (node as { type: string; expression: Expression }).expression : node;\n if (unwrapped.type === \"Identifier\") {\n return gqlIdentifiers.has(unwrapped.value);\n }\n if (unwrapped.type === \"MemberExpression\" && unwrapped.property.type === \"Identifier\" && unwrapped.property.value === \"gql\") {\n return true;\n }\n return false;\n};\n\n/**\n * Check if a call expression is a gql definition call\n * Handles: gql.default(...), gql.model(...), gql.schemaName(...) (multi-schema)\n */\nexport const isGqlDefinitionCall = (node: CallExpression, gqlIdentifiers: ReadonlySet<string>): boolean => {\n if (node.callee.type !== \"MemberExpression\") return false;\n const { object } = node.callee;\n\n // Check object is a gql reference first\n if (!isGqlReference(object, gqlIdentifiers)) return false;\n\n // Verify first argument is an arrow function (factory pattern)\n // SWC's arguments are ExprOrSpread[], so access via .expression\n const firstArg = node.arguments[0];\n if (!firstArg || (firstArg.expression.type !== \"ArrowFunctionExpression\" && firstArg.expression.type !== \"FunctionExpression\"))\n return false;\n\n return true;\n};\n\n/**\n * Check if an object expression is a field selection object\n * Field selection objects are returned from arrow functions with ({ f }) or ({ f, $ }) parameter\n */\nexport const isFieldSelectionObject = (object: ObjectExpression, parent: ArrowFunctionExpression): boolean => {\n // The object must be the body of the arrow function\n // Handle both direct ObjectExpression and parenthesized ObjectExpression: `({ ... })`\n let bodyObject: ObjectExpression | null = null;\n\n if (parent.body.type === \"ObjectExpression\") {\n bodyObject = parent.body;\n } else if (parent.body.type === \"ParenthesisExpression\") {\n // Handle `({ f }) => ({ ...f.id() })` pattern where body is parenthesized\n const inner = parent.body.expression;\n if (inner.type === \"ObjectExpression\") {\n bodyObject = inner;\n }\n }\n\n if (!bodyObject) return false;\n if (bodyObject.span.start !== object.span.start) return false;\n\n // Check if first parameter has 'f' destructured\n const param = parent.params[0];\n if (!param || param.type !== \"ObjectPattern\") return false;\n\n return param.properties.some((p: ObjectPatternProperty) => {\n if (p.type === \"KeyValuePatternProperty\" && p.key.type === \"Identifier\") {\n return p.key.value === \"f\";\n }\n if (p.type === \"AssignmentPatternProperty\" && p.key.type === \"Identifier\") {\n return p.key.value === \"f\";\n }\n return false;\n });\n};\n\n/**\n * Collect gql identifiers from import declarations\n */\nexport const collectGqlIdentifiers = (module: Module): Set<string> => {\n const gqlIdentifiers = new Set<string>();\n\n for (const item of module.body) {\n if (item.type !== \"ImportDeclaration\") continue;\n\n for (const specifier of item.specifiers) {\n if (specifier.type === \"ImportSpecifier\") {\n const imported = specifier.imported?.value ?? specifier.local.value;\n if (imported === \"gql\") {\n gqlIdentifiers.add(specifier.local.value);\n }\n }\n if (specifier.type === \"ImportDefaultSpecifier\") {\n // Check if default import might be gql\n if (specifier.local.value === \"gql\") {\n gqlIdentifiers.add(specifier.local.value);\n }\n }\n if (specifier.type === \"ImportNamespaceSpecifier\") {\n // namespace import: import * as ns from \"...\"\n // Would need ns.gql pattern - handled by isGqlReference\n }\n }\n }\n\n return gqlIdentifiers;\n};\n\n/**\n * Check if a call expression is a fragment definition call.\n * Pattern: fragment.TypeName({ ... }) where `fragment` comes from gql factory destructuring.\n */\nexport const isFragmentDefinitionCall = (node: CallExpression, fragmentIdentifiers: ReadonlySet<string>): boolean => {\n if (node.callee.type !== \"MemberExpression\") return false;\n\n const { object } = node.callee;\n\n if (object.type !== \"Identifier\") return false;\n if (!fragmentIdentifiers.has(object.value)) return false;\n\n const firstArg = node.arguments[0];\n if (!firstArg || firstArg.expression.type !== \"ObjectExpression\") return false;\n\n return true;\n};\n\n/**\n * Check if an object expression already has a `key` property.\n */\nexport const hasKeyProperty = (obj: ObjectExpression): boolean => {\n return obj.properties.some((prop) => {\n if (prop.type === \"KeyValueProperty\" && prop.key.type === \"Identifier\") {\n return prop.key.value === \"key\";\n }\n return false;\n });\n};\n\n/**\n * Collect fragment identifiers from gql factory arrow function parameter.\n * Looks for patterns like: ({ fragment }) => ... or ({ fragment: f }) => ...\n */\nexport const collectFragmentIdentifiers = (arrowFunction: ArrowFunctionExpression): Set<string> => {\n const fragmentIdentifiers = new Set<string>();\n\n const param = arrowFunction.params[0];\n if (param?.type !== \"ObjectPattern\") return fragmentIdentifiers;\n\n for (const p of param.properties as ObjectPatternProperty[]) {\n if (p.type === \"KeyValuePatternProperty\" && p.key.type === \"Identifier\" && p.key.value === \"fragment\") {\n const localName = extractIdentifierName(p.value);\n if (localName) fragmentIdentifiers.add(localName);\n }\n if (p.type === \"AssignmentPatternProperty\" && p.key.type === \"Identifier\" && p.key.value === \"fragment\") {\n fragmentIdentifiers.add(p.key.value);\n }\n }\n\n return fragmentIdentifiers;\n};\n\n/**\n * Extract identifier name from a pattern node.\n */\nconst extractIdentifierName = (pattern: Pattern): string | null => {\n if (pattern.type === \"Identifier\") return pattern.value;\n // biome-ignore lint/suspicious/noExplicitAny: SWC types\n if ((pattern as any).type === \"BindingIdentifier\") return (pattern as any).value;\n return null;\n};\n","import { randomBytes } from \"node:crypto\";\n\n/**\n * The newline string to insert after object opening brace\n */\nexport const NEWLINE_INSERTION = \"\\n\";\n\n/**\n * Generate a random 8-character hex key for fragment identification.\n * Uses Node.js crypto for cryptographically secure random bytes.\n */\nexport const generateFragmentKey = (): string => {\n return randomBytes(4).toString(\"hex\");\n};\n\n/**\n * Create the key property insertion string.\n *\n * For single-line objects: returns ` key: \"xxxxxxxx\",\\n`\n * For multi-line objects (with indentation): returns `{indentation}key: \"xxxxxxxx\",\\n`\n * (the existing indentation before the next property is preserved)\n *\n * @param key - The hex key string\n * @param indentation - Optional indentation string for multi-line objects\n */\nexport const createKeyInsertion = (key: string, indentation?: string): string => {\n if (indentation !== undefined) {\n // Multi-line: key goes on its own line with indentation\n // The next property's indentation is already in the source, so don't duplicate it\n return `${indentation}key: \"${key}\",\\n`;\n }\n // Single-line: space before key, newline after\n return ` key: \"${key}\",\\n`;\n};\n\n/**\n * Check if there's already a newline after the opening brace.\n * Uses string inspection rather than AST.\n *\n * @param source - The source code string\n * @param objectStartPos - The position of the `{` character in the source\n */\nexport const hasExistingNewline = (source: string, objectStartPos: number): boolean => {\n // Skip the `{` character\n const pos = objectStartPos + 1;\n\n // Check if next character is a newline\n const nextChar = source[pos];\n return nextChar === \"\\n\" || nextChar === \"\\r\";\n};\n\n/**\n * Detect the indentation used after an opening brace.\n * Returns the whitespace string after the newline, or null if no newline.\n *\n * @param source - The source code string\n * @param objectStartPos - The position of the `{` character\n */\nexport const detectIndentationAfterBrace = (source: string, objectStartPos: number): string | null => {\n const afterBrace = objectStartPos + 1;\n const char = source[afterBrace];\n\n // Check for newline (handles both \\n and \\r\\n)\n if (char !== \"\\n\" && char !== \"\\r\") {\n return null;\n }\n\n // Skip past the newline character(s)\n let pos = afterBrace + 1;\n if (char === \"\\r\" && source[pos] === \"\\n\") {\n pos++;\n }\n\n // Collect whitespace (indentation)\n let indentation = \"\";\n while (pos < source.length) {\n const c = source[pos];\n if (c === \" \" || c === \"\\t\") {\n indentation += c;\n pos++;\n } else {\n break;\n }\n }\n\n return indentation;\n};\n","import { createRequire } from \"node:module\";\nimport {\n type FormatGraphqlFn,\n formatTemplatesInSource,\n type PositionTrackingContext,\n walkAndExtract,\n} from \"@soda-gql/common/template-extraction\";\nimport { createSwcSpanConverter } from \"@soda-gql/common/utils\";\nimport { parseSync } from \"@swc/core\";\nimport type { ArrowFunctionExpression, CallExpression, Module, Node, ObjectExpression } from \"@swc/types\";\nimport { err, ok, type Result } from \"neverthrow\";\n\nconst require = createRequire(import.meta.url);\n\ntype GraphqlModule = {\n parse: (source: string, options?: { noLocation?: boolean }) => unknown;\n print: (ast: unknown) => string;\n};\n\nlet _graphqlModule: GraphqlModule | undefined;\nlet _graphqlModuleError: Error | undefined;\n\nconst getGraphqlModule = (): Result<GraphqlModule, FormatError> => {\n if (_graphqlModuleError) {\n return err({\n type: \"FormatError\",\n code: \"MISSING_DEPENDENCY\",\n message: 'The \"graphql\" package is required for soda-gql formatter. Install it with: bun add graphql',\n });\n }\n if (!_graphqlModule) {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n _graphqlModule = require(\"graphql\") as GraphqlModule;\n } catch (cause) {\n _graphqlModuleError = cause instanceof Error ? cause : new Error(String(cause));\n return err({\n type: \"FormatError\",\n code: \"MISSING_DEPENDENCY\",\n message: 'The \"graphql\" package is required for soda-gql formatter. Install it with: bun add graphql',\n cause,\n });\n }\n }\n return ok(_graphqlModule);\n};\n\nimport {\n collectFragmentIdentifiers,\n collectGqlIdentifiers,\n hasKeyProperty,\n isFieldSelectionObject,\n isFragmentDefinitionCall,\n isGqlDefinitionCall,\n} from \"./detection\";\nimport {\n createKeyInsertion,\n detectIndentationAfterBrace,\n generateFragmentKey,\n hasExistingNewline,\n NEWLINE_INSERTION,\n} from \"./insertion\";\nimport type { FormatError, FormatOptions, FormatResult } from \"./types\";\n\ntype InsertionPoint = {\n readonly position: number;\n readonly content: string;\n readonly endPosition?: number;\n};\n\ntype TraversalContext = {\n insideGqlDefinition: boolean;\n currentArrowFunction: ArrowFunctionExpression | null;\n fragmentIdentifiers: ReadonlySet<string>;\n};\n\ntype TraversalCallbackContext = {\n readonly isFragmentConfig: boolean;\n};\n\ntype TraversalCallback = (\n object: ObjectExpression,\n parent: ArrowFunctionExpression,\n callbackContext: TraversalCallbackContext,\n) => void;\n\n/**\n * Simple recursive AST traversal\n */\nconst traverseNode = (\n node: Node,\n context: TraversalContext,\n gqlIdentifiers: ReadonlySet<string>,\n onObjectExpression: TraversalCallback,\n): void => {\n // Check for gql definition call entry and collect fragment identifiers\n if (node.type === \"CallExpression\" && isGqlDefinitionCall(node as CallExpression, gqlIdentifiers)) {\n const call = node as CallExpression;\n const firstArg = call.arguments[0];\n if (firstArg?.expression.type === \"ArrowFunctionExpression\") {\n const arrow = firstArg.expression as ArrowFunctionExpression;\n const fragmentIds = collectFragmentIdentifiers(arrow);\n context = {\n ...context,\n insideGqlDefinition: true,\n fragmentIdentifiers: new Set([...context.fragmentIdentifiers, ...fragmentIds]),\n };\n } else {\n context = { ...context, insideGqlDefinition: true };\n }\n }\n\n // Check for fragment definition call: fragment.TypeName({ ... })\n if (\n node.type === \"CallExpression\" &&\n context.insideGqlDefinition &&\n isFragmentDefinitionCall(node as CallExpression, context.fragmentIdentifiers)\n ) {\n const call = node as CallExpression;\n const firstArg = call.arguments[0];\n if (firstArg?.expression.type === \"ObjectExpression\" && context.currentArrowFunction) {\n onObjectExpression(firstArg.expression as ObjectExpression, context.currentArrowFunction, {\n isFragmentConfig: true,\n });\n }\n }\n\n // Handle object expressions - check if it's the body of the current arrow function\n if (\n node.type === \"ObjectExpression\" &&\n context.insideGqlDefinition &&\n context.currentArrowFunction &&\n isFieldSelectionObject(node as ObjectExpression, context.currentArrowFunction)\n ) {\n onObjectExpression(node as ObjectExpression, context.currentArrowFunction, { isFragmentConfig: false });\n }\n\n // Recursively visit children\n if (node.type === \"CallExpression\") {\n const call = node as CallExpression;\n traverseNode(call.callee as Node, context, gqlIdentifiers, onObjectExpression);\n for (const arg of call.arguments) {\n traverseNode(arg.expression as Node, context, gqlIdentifiers, onObjectExpression);\n }\n } else if (node.type === \"ArrowFunctionExpression\") {\n const arrow = node as ArrowFunctionExpression;\n // Update context with the new arrow function\n const childContext = { ...context, currentArrowFunction: arrow };\n if (arrow.body.type !== \"BlockStatement\") {\n traverseNode(arrow.body as Node, childContext, gqlIdentifiers, onObjectExpression);\n }\n } else if (node.type === \"ParenthesisExpression\") {\n // Handle parenthesized expressions like `({ ...f.id() })`\n // biome-ignore lint/suspicious/noExplicitAny: SWC types\n const paren = node as any;\n if (paren.expression) {\n traverseNode(paren.expression as Node, context, gqlIdentifiers, onObjectExpression);\n }\n } else if (node.type === \"MemberExpression\") {\n // biome-ignore lint/suspicious/noExplicitAny: SWC types\n const member = node as any;\n traverseNode(member.object as Node, context, gqlIdentifiers, onObjectExpression);\n } else if (node.type === \"ObjectExpression\") {\n const obj = node as ObjectExpression;\n for (const prop of obj.properties) {\n if (prop.type === \"SpreadElement\") {\n traverseNode(prop.arguments as Node, context, gqlIdentifiers, onObjectExpression);\n } else if (prop.type === \"KeyValueProperty\") {\n traverseNode(prop.value as Node, context, gqlIdentifiers, onObjectExpression);\n }\n }\n } else {\n // Generic traversal for other node types\n for (const value of Object.values(node)) {\n if (Array.isArray(value)) {\n for (const child of value) {\n if (child && typeof child === \"object\" && \"type\" in child) {\n traverseNode(child as Node, context, gqlIdentifiers, onObjectExpression);\n }\n }\n } else if (value && typeof value === \"object\" && \"type\" in value) {\n traverseNode(value as Node, context, gqlIdentifiers, onObjectExpression);\n }\n }\n }\n};\n\nconst traverse = (module: Module, gqlIdentifiers: ReadonlySet<string>, onObjectExpression: TraversalCallback): void => {\n const initialContext: TraversalContext = {\n insideGqlDefinition: false,\n currentArrowFunction: null,\n fragmentIdentifiers: new Set(),\n };\n for (const statement of module.body) {\n traverseNode(statement, initialContext, gqlIdentifiers, onObjectExpression);\n }\n};\n\n/**\n * Format soda-gql field selection objects by inserting newlines.\n * Optionally injects fragment keys for anonymous fragments.\n */\nexport const format = (options: FormatOptions): Result<FormatResult, FormatError> => {\n const { sourceCode, filePath, injectFragmentKeys = false } = options;\n\n // Parse source code with SWC\n let module: Module;\n try {\n const program = parseSync(sourceCode, {\n syntax: \"typescript\",\n tsx: filePath?.endsWith(\".tsx\") ?? true,\n target: \"es2022\",\n decorators: false,\n dynamicImport: true,\n });\n\n if (program.type !== \"Module\") {\n return err({\n type: \"FormatError\",\n code: \"PARSE_ERROR\",\n message: `Not a module${filePath ? ` (${filePath})` : \"\"}`,\n });\n }\n module = program;\n } catch (cause) {\n return err({\n type: \"FormatError\",\n code: \"PARSE_ERROR\",\n message: `Failed to parse source code${filePath ? ` (${filePath})` : \"\"}`,\n cause,\n });\n }\n\n // Calculate span offset for position normalization\n // SWC's BytePos counter accumulates across parseSync calls within the same process\n // Using module.span.start ensures correct position calculation regardless of accumulation\n const spanOffset = module.span.start;\n\n // SWC returns UTF-8 byte offsets; JS strings use UTF-16 code units.\n // The converter handles this mapping (fast path for ASCII-only sources).\n const converter = createSwcSpanConverter(sourceCode);\n\n // Collect gql identifiers from imports\n const gqlIdentifiers = collectGqlIdentifiers(module);\n if (gqlIdentifiers.size === 0) {\n return ok({ modified: false, sourceCode });\n }\n\n // Collect insertion points\n const insertionPoints: InsertionPoint[] = [];\n\n traverse(module, gqlIdentifiers, (object, _parent, callbackContext) => {\n // Calculate actual position in source (byte offset → char index)\n const objectStart = converter.byteOffsetToCharIndex(object.span.start - spanOffset);\n\n // For fragment config objects, inject key if enabled and not present\n if (callbackContext.isFragmentConfig && injectFragmentKeys && !hasKeyProperty(object)) {\n const key = generateFragmentKey();\n\n if (hasExistingNewline(sourceCode, objectStart)) {\n // Multi-line: insert after newline, preserving indentation\n const indentation = detectIndentationAfterBrace(sourceCode, objectStart) ?? \"\";\n let insertPos = objectStart + 2; // Skip { and \\n\n if (sourceCode[objectStart + 1] === \"\\r\") insertPos++;\n\n insertionPoints.push({\n position: insertPos,\n content: createKeyInsertion(key, indentation),\n });\n } else {\n // Single-line: insert right after {\n insertionPoints.push({\n position: objectStart + 1,\n content: createKeyInsertion(key),\n });\n }\n }\n\n // For field selection objects, insert newline if not present\n if (!callbackContext.isFragmentConfig && !hasExistingNewline(sourceCode, objectStart)) {\n insertionPoints.push({\n position: objectStart + 1,\n content: NEWLINE_INSERTION,\n });\n }\n });\n\n // Tagged template formatting\n const graphqlResult = getGraphqlModule();\n if (graphqlResult.isErr()) {\n return err(graphqlResult.error);\n }\n const { parse: parseGraphql, print: printGraphql } = graphqlResult.value;\n\n const positionCtx: PositionTrackingContext = { spanOffset, converter };\n const templates = walkAndExtract(module as unknown as Node, gqlIdentifiers, positionCtx);\n\n if (templates.length > 0) {\n const defaultFormat: FormatGraphqlFn = (source) => {\n const ast = parseGraphql(source, { noLocation: false });\n return printGraphql(ast);\n };\n\n const templateEdits = formatTemplatesInSource(templates, sourceCode, defaultFormat);\n\n for (const edit of templateEdits) {\n insertionPoints.push({\n position: edit.start,\n content: edit.newText,\n endPosition: edit.end,\n });\n }\n }\n\n // Apply insertions\n if (insertionPoints.length === 0) {\n return ok({ modified: false, sourceCode });\n }\n\n // Sort in descending order to insert from end to beginning\n // This preserves earlier positions while modifying later parts\n const sortedPoints = [...insertionPoints].sort((a, b) => b.position - a.position);\n\n let result = sourceCode;\n for (const point of sortedPoints) {\n const end = point.endPosition ?? point.position;\n result = result.slice(0, point.position) + point.content + result.slice(end);\n }\n\n return ok({ modified: true, sourceCode: result });\n};\n\n/**\n * Check if a file needs formatting (has unformatted field selections).\n * Useful for pre-commit hooks or CI checks.\n */\nexport const needsFormat = (options: FormatOptions): Result<boolean, FormatError> => {\n const { sourceCode, filePath } = options;\n\n // Parse source code with SWC\n let module: Module;\n try {\n const program = parseSync(sourceCode, {\n syntax: \"typescript\",\n tsx: filePath?.endsWith(\".tsx\") ?? true,\n target: \"es2022\",\n decorators: false,\n dynamicImport: true,\n });\n\n if (program.type !== \"Module\") {\n return err({\n type: \"FormatError\",\n code: \"PARSE_ERROR\",\n message: `Not a module${filePath ? ` (${filePath})` : \"\"}`,\n });\n }\n module = program;\n } catch (cause) {\n return err({\n type: \"FormatError\",\n code: \"PARSE_ERROR\",\n message: `Failed to parse source code${filePath ? ` (${filePath})` : \"\"}`,\n cause,\n });\n }\n\n const spanOffset = module.span.start;\n const converter = createSwcSpanConverter(sourceCode);\n const gqlIdentifiers = collectGqlIdentifiers(module);\n\n if (gqlIdentifiers.size === 0) {\n return ok(false);\n }\n\n let needsFormatting = false;\n\n traverse(module, gqlIdentifiers, (object, _parent, callbackContext) => {\n if (needsFormatting) return; // Early exit\n\n // Skip fragment config objects for needsFormat check (key injection is optional)\n if (callbackContext.isFragmentConfig) return;\n\n const objectStart = converter.byteOffsetToCharIndex(object.span.start - spanOffset);\n if (!hasExistingNewline(sourceCode, objectStart)) {\n needsFormatting = true;\n }\n });\n\n // Check tagged templates\n if (!needsFormatting) {\n const graphqlResult = getGraphqlModule();\n if (graphqlResult.isErr()) {\n return err(graphqlResult.error);\n }\n const { parse: parseGraphql, print: printGraphql } = graphqlResult.value;\n\n const positionCtx: PositionTrackingContext = { spanOffset, converter };\n const templates = walkAndExtract(module as unknown as Node, gqlIdentifiers, positionCtx);\n\n if (templates.length > 0) {\n const defaultFormat: FormatGraphqlFn = (source) => {\n const ast = parseGraphql(source, { noLocation: false });\n return printGraphql(ast);\n };\n\n const templateEdits = formatTemplatesInSource(templates, sourceCode, defaultFormat);\n if (templateEdits.length > 0) {\n needsFormatting = true;\n }\n }\n }\n\n return ok(needsFormatting);\n};\n"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -5,7 +5,6 @@ type FormatOptions = {
5
5
  readonly sourceCode: string;
6
6
  readonly filePath?: string;
7
7
  readonly injectFragmentKeys?: boolean;
8
- readonly formatTaggedTemplates?: boolean;
9
8
  };
10
9
  type FormatResult = {
11
10
  readonly modified: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/format.ts"],"sourcesContent":[],"mappings":";;;KAAY,aAAA;;;EAAA,SAAA,kBAAa,CAAA,EAAA,OAAA;EAOb,SAAA,qBAAY,CAAA,EAAA,OAAA;AAKxB,CAAA;KALY,YAAA;;;ACmMZ,CAAA;AAAgC,KD9LpB,WAAA,GC8LoB;EAAuB,SAAA,IAAA,EAAA,aAAA;EAAc,SAAA,IAAA,EAAA,aAAA,GAAA,iBAAA,GAAA,oBAAA;EAArB,SAAA,OAAA,EAAA,MAAA;EAAM,SAAA,KAAA,CAAA,EAAA,OAAA;AAqItD,CAAA;;;;AD/UA;AAOA;AAKA;cC8La,kBAAmB,kBAAgB,OAAO,cAAc;;;AAArE;;AAAuD,cAqI1C,WArI0C,EAAA,CAAA,OAAA,EAqIlB,aArIkB,EAAA,GAqIF,MArIE,CAAA,OAAA,EAqIc,WArId,CAAA"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/format.ts"],"sourcesContent":[],"mappings":";;;KAAY,aAAA;;;EAAA,SAAA,kBAAa,CAAA,EAAA,OAAA;AAMzB,CAAA;AAKY,KALA,YAAA,GAKW;;;;AC+LV,KD/LD,WAAA,GC+TX;EAhI+B,SAAA,IAAA,EAAA,aAAA;EAAuB,SAAA,IAAA,EAAA,aAAA,GAAA,iBAAA,GAAA,oBAAA;EAAc,SAAA,OAAA,EAAA,MAAA;EAArB,SAAA,KAAA,CAAA,EAAA,OAAA;CAAM;;;;AD1MtD;AAMA;AAKA;cC+La,kBAAmB,kBAAgB,OAAO,cAAc;;;AAArE;;AAAuD,cAsI1C,WAtI0C,EAAA,CAAA,OAAA,EAsIlB,aAtIkB,EAAA,GAsIF,MAtIE,CAAA,OAAA,EAsIc,WAtId,CAAA"}
package/dist/index.d.mts CHANGED
@@ -5,7 +5,6 @@ type FormatOptions = {
5
5
  readonly sourceCode: string;
6
6
  readonly filePath?: string;
7
7
  readonly injectFragmentKeys?: boolean;
8
- readonly formatTaggedTemplates?: boolean;
9
8
  };
10
9
  type FormatResult = {
11
10
  readonly modified: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/format.ts"],"sourcesContent":[],"mappings":";;;KAAY,aAAA;;;EAAA,SAAA,kBAAa,CAAA,EAAA,OAAA;EAOb,SAAA,qBAAY,CAAA,EAAA,OAAA;AAKxB,CAAA;KALY,YAAA;;;ACmMZ,CAAA;AAAgC,KD9LpB,WAAA,GC8LoB;EAAuB,SAAA,IAAA,EAAA,aAAA;EAAc,SAAA,IAAA,EAAA,aAAA,GAAA,iBAAA,GAAA,oBAAA;EAArB,SAAA,OAAA,EAAA,MAAA;EAAM,SAAA,KAAA,CAAA,EAAA,OAAA;AAqItD,CAAA;;;;AD/UA;AAOA;AAKA;cC8La,kBAAmB,kBAAgB,OAAO,cAAc;;;AAArE;;AAAuD,cAqI1C,WArI0C,EAAA,CAAA,OAAA,EAqIlB,aArIkB,EAAA,GAqIF,MArIE,CAAA,OAAA,EAqIc,WArId,CAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/format.ts"],"sourcesContent":[],"mappings":";;;KAAY,aAAA;;;EAAA,SAAA,kBAAa,CAAA,EAAA,OAAA;AAMzB,CAAA;AAKY,KALA,YAAA,GAKW;;;;AC+LV,KD/LD,WAAA,GC+TX;EAhI+B,SAAA,IAAA,EAAA,aAAA;EAAuB,SAAA,IAAA,EAAA,aAAA,GAAA,iBAAA,GAAA,oBAAA;EAAc,SAAA,OAAA,EAAA,MAAA;EAArB,SAAA,KAAA,CAAA,EAAA,OAAA;CAAM;;;;AD1MtD;AAMA;AAKA;cC+La,kBAAmB,kBAAgB,OAAO,cAAc;;;AAArE;;AAAuD,cAsI1C,WAtI0C,EAAA,CAAA,OAAA,EAsIlB,aAtIkB,EAAA,GAsIF,MAtIE,CAAA,OAAA,EAsIc,WAtId,CAAA"}
package/dist/index.mjs CHANGED
@@ -187,7 +187,7 @@ const getGraphqlModule = () => {
187
187
  if (_graphqlModuleError) return err({
188
188
  type: "FormatError",
189
189
  code: "MISSING_DEPENDENCY",
190
- message: "The \"graphql\" package is required for --format-tagged-templates. Install it with: bun add graphql"
190
+ message: "The \"graphql\" package is required for soda-gql formatter. Install it with: bun add graphql"
191
191
  });
192
192
  if (!_graphqlModule) try {
193
193
  _graphqlModule = require("graphql");
@@ -196,7 +196,7 @@ const getGraphqlModule = () => {
196
196
  return err({
197
197
  type: "FormatError",
198
198
  code: "MISSING_DEPENDENCY",
199
- message: "The \"graphql\" package is required for --format-tagged-templates. Install it with: bun add graphql",
199
+ message: "The \"graphql\" package is required for soda-gql formatter. Install it with: bun add graphql",
200
200
  cause
201
201
  });
202
202
  }
@@ -262,7 +262,7 @@ const traverse = (module, gqlIdentifiers, onObjectExpression) => {
262
262
  * Optionally injects fragment keys for anonymous fragments.
263
263
  */
264
264
  const format = (options) => {
265
- const { sourceCode, filePath, injectFragmentKeys = false, formatTaggedTemplates = false } = options;
265
+ const { sourceCode, filePath, injectFragmentKeys = false } = options;
266
266
  let module;
267
267
  try {
268
268
  const program = parseSync(sourceCode, {
@@ -287,6 +287,7 @@ const format = (options) => {
287
287
  });
288
288
  }
289
289
  const spanOffset = module.span.start;
290
+ const converter = createSwcSpanConverter(sourceCode);
290
291
  const gqlIdentifiers = collectGqlIdentifiers(module);
291
292
  if (gqlIdentifiers.size === 0) return ok({
292
293
  modified: false,
@@ -294,7 +295,7 @@ const format = (options) => {
294
295
  });
295
296
  const insertionPoints = [];
296
297
  traverse(module, gqlIdentifiers, (object, _parent, callbackContext) => {
297
- const objectStart = object.span.start - spanOffset;
298
+ const objectStart = converter.byteOffsetToCharIndex(object.span.start - spanOffset);
298
299
  if (callbackContext.isFragmentConfig && injectFragmentKeys && !hasKeyProperty(object)) {
299
300
  const key = generateFragmentKey();
300
301
  if (hasExistingNewline(sourceCode, objectStart)) {
@@ -315,26 +316,23 @@ const format = (options) => {
315
316
  content: NEWLINE_INSERTION
316
317
  });
317
318
  });
318
- if (formatTaggedTemplates) {
319
- const graphqlResult = getGraphqlModule();
320
- if (graphqlResult.isErr()) return err(graphqlResult.error);
321
- const { parse: parseGraphql, print: printGraphql } = graphqlResult.value;
322
- const positionCtx = {
323
- spanOffset,
324
- converter: createSwcSpanConverter(sourceCode)
319
+ const graphqlResult = getGraphqlModule();
320
+ if (graphqlResult.isErr()) return err(graphqlResult.error);
321
+ const { parse: parseGraphql, print: printGraphql } = graphqlResult.value;
322
+ const templates = walkAndExtract(module, gqlIdentifiers, {
323
+ spanOffset,
324
+ converter
325
+ });
326
+ if (templates.length > 0) {
327
+ const defaultFormat = (source) => {
328
+ return printGraphql(parseGraphql(source, { noLocation: false }));
325
329
  };
326
- const templates = walkAndExtract(module, gqlIdentifiers, positionCtx);
327
- if (templates.length > 0) {
328
- const defaultFormat = (source) => {
329
- return printGraphql(parseGraphql(source, { noLocation: false }));
330
- };
331
- const templateEdits = formatTemplatesInSource(templates, sourceCode, defaultFormat);
332
- for (const edit of templateEdits) insertionPoints.push({
333
- position: edit.start,
334
- content: edit.newText,
335
- endPosition: edit.end
336
- });
337
- }
330
+ const templateEdits = formatTemplatesInSource(templates, sourceCode, defaultFormat);
331
+ for (const edit of templateEdits) insertionPoints.push({
332
+ position: edit.start,
333
+ content: edit.newText,
334
+ endPosition: edit.end
335
+ });
338
336
  }
339
337
  if (insertionPoints.length === 0) return ok({
340
338
  modified: false,
@@ -356,7 +354,7 @@ const format = (options) => {
356
354
  * Useful for pre-commit hooks or CI checks.
357
355
  */
358
356
  const needsFormat = (options) => {
359
- const { sourceCode, filePath, formatTaggedTemplates = false } = options;
357
+ const { sourceCode, filePath } = options;
360
358
  let module;
361
359
  try {
362
360
  const program = parseSync(sourceCode, {
@@ -381,23 +379,23 @@ const needsFormat = (options) => {
381
379
  });
382
380
  }
383
381
  const spanOffset = module.span.start;
382
+ const converter = createSwcSpanConverter(sourceCode);
384
383
  const gqlIdentifiers = collectGqlIdentifiers(module);
385
384
  if (gqlIdentifiers.size === 0) return ok(false);
386
385
  let needsFormatting = false;
387
386
  traverse(module, gqlIdentifiers, (object, _parent, callbackContext) => {
388
387
  if (needsFormatting) return;
389
388
  if (callbackContext.isFragmentConfig) return;
390
- if (!hasExistingNewline(sourceCode, object.span.start - spanOffset)) needsFormatting = true;
389
+ if (!hasExistingNewline(sourceCode, converter.byteOffsetToCharIndex(object.span.start - spanOffset))) needsFormatting = true;
391
390
  });
392
- if (!needsFormatting && formatTaggedTemplates) {
391
+ if (!needsFormatting) {
393
392
  const graphqlResult = getGraphqlModule();
394
393
  if (graphqlResult.isErr()) return err(graphqlResult.error);
395
394
  const { parse: parseGraphql, print: printGraphql } = graphqlResult.value;
396
- const positionCtx = {
395
+ const templates = walkAndExtract(module, gqlIdentifiers, {
397
396
  spanOffset,
398
- converter: createSwcSpanConverter(sourceCode)
399
- };
400
- const templates = walkAndExtract(module, gqlIdentifiers, positionCtx);
397
+ converter
398
+ });
401
399
  if (templates.length > 0) {
402
400
  const defaultFormat = (source) => {
403
401
  return printGraphql(parseGraphql(source, { noLocation: false }));
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["bodyObject: ObjectExpression | null","_graphqlModule: GraphqlModule | undefined","_graphqlModuleError: Error | undefined","initialContext: TraversalContext","module: Module","insertionPoints: InsertionPoint[]","positionCtx: PositionTrackingContext","defaultFormat: FormatGraphqlFn"],"sources":["../src/detection.ts","../src/insertion.ts","../src/format.ts"],"sourcesContent":["import type {\n ArrowFunctionExpression,\n CallExpression,\n Expression,\n Module,\n ObjectExpression,\n ObjectPatternProperty,\n Pattern,\n} from \"@swc/types\";\n\n/**\n * Check if an expression is a reference to gql\n * Handles: gql, namespace.gql\n */\nexport const isGqlReference = (node: Expression, gqlIdentifiers: ReadonlySet<string>): boolean => {\n // Unwrap TsNonNullExpression: gql! -> gql\n const unwrapped = node.type === \"TsNonNullExpression\" ? (node as { type: string; expression: Expression }).expression : node;\n if (unwrapped.type === \"Identifier\") {\n return gqlIdentifiers.has(unwrapped.value);\n }\n if (unwrapped.type === \"MemberExpression\" && unwrapped.property.type === \"Identifier\" && unwrapped.property.value === \"gql\") {\n return true;\n }\n return false;\n};\n\n/**\n * Check if a call expression is a gql definition call\n * Handles: gql.default(...), gql.model(...), gql.schemaName(...) (multi-schema)\n */\nexport const isGqlDefinitionCall = (node: CallExpression, gqlIdentifiers: ReadonlySet<string>): boolean => {\n if (node.callee.type !== \"MemberExpression\") return false;\n const { object } = node.callee;\n\n // Check object is a gql reference first\n if (!isGqlReference(object, gqlIdentifiers)) return false;\n\n // Verify first argument is an arrow function (factory pattern)\n // SWC's arguments are ExprOrSpread[], so access via .expression\n const firstArg = node.arguments[0];\n if (!firstArg || (firstArg.expression.type !== \"ArrowFunctionExpression\" && firstArg.expression.type !== \"FunctionExpression\"))\n return false;\n\n return true;\n};\n\n/**\n * Check if an object expression is a field selection object\n * Field selection objects are returned from arrow functions with ({ f }) or ({ f, $ }) parameter\n */\nexport const isFieldSelectionObject = (object: ObjectExpression, parent: ArrowFunctionExpression): boolean => {\n // The object must be the body of the arrow function\n // Handle both direct ObjectExpression and parenthesized ObjectExpression: `({ ... })`\n let bodyObject: ObjectExpression | null = null;\n\n if (parent.body.type === \"ObjectExpression\") {\n bodyObject = parent.body;\n } else if (parent.body.type === \"ParenthesisExpression\") {\n // Handle `({ f }) => ({ ...f.id() })` pattern where body is parenthesized\n const inner = parent.body.expression;\n if (inner.type === \"ObjectExpression\") {\n bodyObject = inner;\n }\n }\n\n if (!bodyObject) return false;\n if (bodyObject.span.start !== object.span.start) return false;\n\n // Check if first parameter has 'f' destructured\n const param = parent.params[0];\n if (!param || param.type !== \"ObjectPattern\") return false;\n\n return param.properties.some((p: ObjectPatternProperty) => {\n if (p.type === \"KeyValuePatternProperty\" && p.key.type === \"Identifier\") {\n return p.key.value === \"f\";\n }\n if (p.type === \"AssignmentPatternProperty\" && p.key.type === \"Identifier\") {\n return p.key.value === \"f\";\n }\n return false;\n });\n};\n\n/**\n * Collect gql identifiers from import declarations\n */\nexport const collectGqlIdentifiers = (module: Module): Set<string> => {\n const gqlIdentifiers = new Set<string>();\n\n for (const item of module.body) {\n if (item.type !== \"ImportDeclaration\") continue;\n\n for (const specifier of item.specifiers) {\n if (specifier.type === \"ImportSpecifier\") {\n const imported = specifier.imported?.value ?? specifier.local.value;\n if (imported === \"gql\") {\n gqlIdentifiers.add(specifier.local.value);\n }\n }\n if (specifier.type === \"ImportDefaultSpecifier\") {\n // Check if default import might be gql\n if (specifier.local.value === \"gql\") {\n gqlIdentifiers.add(specifier.local.value);\n }\n }\n if (specifier.type === \"ImportNamespaceSpecifier\") {\n // namespace import: import * as ns from \"...\"\n // Would need ns.gql pattern - handled by isGqlReference\n }\n }\n }\n\n return gqlIdentifiers;\n};\n\n/**\n * Check if a call expression is a fragment definition call.\n * Pattern: fragment.TypeName({ ... }) where `fragment` comes from gql factory destructuring.\n */\nexport const isFragmentDefinitionCall = (node: CallExpression, fragmentIdentifiers: ReadonlySet<string>): boolean => {\n if (node.callee.type !== \"MemberExpression\") return false;\n\n const { object } = node.callee;\n\n if (object.type !== \"Identifier\") return false;\n if (!fragmentIdentifiers.has(object.value)) return false;\n\n const firstArg = node.arguments[0];\n if (!firstArg || firstArg.expression.type !== \"ObjectExpression\") return false;\n\n return true;\n};\n\n/**\n * Check if an object expression already has a `key` property.\n */\nexport const hasKeyProperty = (obj: ObjectExpression): boolean => {\n return obj.properties.some((prop) => {\n if (prop.type === \"KeyValueProperty\" && prop.key.type === \"Identifier\") {\n return prop.key.value === \"key\";\n }\n return false;\n });\n};\n\n/**\n * Collect fragment identifiers from gql factory arrow function parameter.\n * Looks for patterns like: ({ fragment }) => ... or ({ fragment: f }) => ...\n */\nexport const collectFragmentIdentifiers = (arrowFunction: ArrowFunctionExpression): Set<string> => {\n const fragmentIdentifiers = new Set<string>();\n\n const param = arrowFunction.params[0];\n if (param?.type !== \"ObjectPattern\") return fragmentIdentifiers;\n\n for (const p of param.properties as ObjectPatternProperty[]) {\n if (p.type === \"KeyValuePatternProperty\" && p.key.type === \"Identifier\" && p.key.value === \"fragment\") {\n const localName = extractIdentifierName(p.value);\n if (localName) fragmentIdentifiers.add(localName);\n }\n if (p.type === \"AssignmentPatternProperty\" && p.key.type === \"Identifier\" && p.key.value === \"fragment\") {\n fragmentIdentifiers.add(p.key.value);\n }\n }\n\n return fragmentIdentifiers;\n};\n\n/**\n * Extract identifier name from a pattern node.\n */\nconst extractIdentifierName = (pattern: Pattern): string | null => {\n if (pattern.type === \"Identifier\") return pattern.value;\n // biome-ignore lint/suspicious/noExplicitAny: SWC types\n if ((pattern as any).type === \"BindingIdentifier\") return (pattern as any).value;\n return null;\n};\n","import { randomBytes } from \"node:crypto\";\n\n/**\n * The newline string to insert after object opening brace\n */\nexport const NEWLINE_INSERTION = \"\\n\";\n\n/**\n * Generate a random 8-character hex key for fragment identification.\n * Uses Node.js crypto for cryptographically secure random bytes.\n */\nexport const generateFragmentKey = (): string => {\n return randomBytes(4).toString(\"hex\");\n};\n\n/**\n * Create the key property insertion string.\n *\n * For single-line objects: returns ` key: \"xxxxxxxx\",\\n`\n * For multi-line objects (with indentation): returns `{indentation}key: \"xxxxxxxx\",\\n`\n * (the existing indentation before the next property is preserved)\n *\n * @param key - The hex key string\n * @param indentation - Optional indentation string for multi-line objects\n */\nexport const createKeyInsertion = (key: string, indentation?: string): string => {\n if (indentation !== undefined) {\n // Multi-line: key goes on its own line with indentation\n // The next property's indentation is already in the source, so don't duplicate it\n return `${indentation}key: \"${key}\",\\n`;\n }\n // Single-line: space before key, newline after\n return ` key: \"${key}\",\\n`;\n};\n\n/**\n * Check if there's already a newline after the opening brace.\n * Uses string inspection rather than AST.\n *\n * @param source - The source code string\n * @param objectStartPos - The position of the `{` character in the source\n */\nexport const hasExistingNewline = (source: string, objectStartPos: number): boolean => {\n // Skip the `{` character\n const pos = objectStartPos + 1;\n\n // Check if next character is a newline\n const nextChar = source[pos];\n return nextChar === \"\\n\" || nextChar === \"\\r\";\n};\n\n/**\n * Detect the indentation used after an opening brace.\n * Returns the whitespace string after the newline, or null if no newline.\n *\n * @param source - The source code string\n * @param objectStartPos - The position of the `{` character\n */\nexport const detectIndentationAfterBrace = (source: string, objectStartPos: number): string | null => {\n const afterBrace = objectStartPos + 1;\n const char = source[afterBrace];\n\n // Check for newline (handles both \\n and \\r\\n)\n if (char !== \"\\n\" && char !== \"\\r\") {\n return null;\n }\n\n // Skip past the newline character(s)\n let pos = afterBrace + 1;\n if (char === \"\\r\" && source[pos] === \"\\n\") {\n pos++;\n }\n\n // Collect whitespace (indentation)\n let indentation = \"\";\n while (pos < source.length) {\n const c = source[pos];\n if (c === \" \" || c === \"\\t\") {\n indentation += c;\n pos++;\n } else {\n break;\n }\n }\n\n return indentation;\n};\n","import { createRequire } from \"node:module\";\nimport {\n type FormatGraphqlFn,\n formatTemplatesInSource,\n type PositionTrackingContext,\n walkAndExtract,\n} from \"@soda-gql/common/template-extraction\";\nimport { createSwcSpanConverter } from \"@soda-gql/common/utils\";\nimport { parseSync } from \"@swc/core\";\nimport type { ArrowFunctionExpression, CallExpression, Module, Node, ObjectExpression } from \"@swc/types\";\nimport { err, ok, type Result } from \"neverthrow\";\n\nconst require = createRequire(import.meta.url);\n\ntype GraphqlModule = {\n parse: (source: string, options?: { noLocation?: boolean }) => unknown;\n print: (ast: unknown) => string;\n};\n\nlet _graphqlModule: GraphqlModule | undefined;\nlet _graphqlModuleError: Error | undefined;\n\nconst getGraphqlModule = (): Result<GraphqlModule, FormatError> => {\n if (_graphqlModuleError) {\n return err({\n type: \"FormatError\",\n code: \"MISSING_DEPENDENCY\",\n message: 'The \"graphql\" package is required for --format-tagged-templates. Install it with: bun add graphql',\n });\n }\n if (!_graphqlModule) {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n _graphqlModule = require(\"graphql\") as GraphqlModule;\n } catch (cause) {\n _graphqlModuleError = cause instanceof Error ? cause : new Error(String(cause));\n return err({\n type: \"FormatError\",\n code: \"MISSING_DEPENDENCY\",\n message: 'The \"graphql\" package is required for --format-tagged-templates. Install it with: bun add graphql',\n cause,\n });\n }\n }\n return ok(_graphqlModule);\n};\n\nimport {\n collectFragmentIdentifiers,\n collectGqlIdentifiers,\n hasKeyProperty,\n isFieldSelectionObject,\n isFragmentDefinitionCall,\n isGqlDefinitionCall,\n} from \"./detection\";\nimport {\n createKeyInsertion,\n detectIndentationAfterBrace,\n generateFragmentKey,\n hasExistingNewline,\n NEWLINE_INSERTION,\n} from \"./insertion\";\nimport type { FormatError, FormatOptions, FormatResult } from \"./types\";\n\ntype InsertionPoint = {\n readonly position: number;\n readonly content: string;\n readonly endPosition?: number;\n};\n\ntype TraversalContext = {\n insideGqlDefinition: boolean;\n currentArrowFunction: ArrowFunctionExpression | null;\n fragmentIdentifiers: ReadonlySet<string>;\n};\n\ntype TraversalCallbackContext = {\n readonly isFragmentConfig: boolean;\n};\n\ntype TraversalCallback = (\n object: ObjectExpression,\n parent: ArrowFunctionExpression,\n callbackContext: TraversalCallbackContext,\n) => void;\n\n/**\n * Simple recursive AST traversal\n */\nconst traverseNode = (\n node: Node,\n context: TraversalContext,\n gqlIdentifiers: ReadonlySet<string>,\n onObjectExpression: TraversalCallback,\n): void => {\n // Check for gql definition call entry and collect fragment identifiers\n if (node.type === \"CallExpression\" && isGqlDefinitionCall(node as CallExpression, gqlIdentifiers)) {\n const call = node as CallExpression;\n const firstArg = call.arguments[0];\n if (firstArg?.expression.type === \"ArrowFunctionExpression\") {\n const arrow = firstArg.expression as ArrowFunctionExpression;\n const fragmentIds = collectFragmentIdentifiers(arrow);\n context = {\n ...context,\n insideGqlDefinition: true,\n fragmentIdentifiers: new Set([...context.fragmentIdentifiers, ...fragmentIds]),\n };\n } else {\n context = { ...context, insideGqlDefinition: true };\n }\n }\n\n // Check for fragment definition call: fragment.TypeName({ ... })\n if (\n node.type === \"CallExpression\" &&\n context.insideGqlDefinition &&\n isFragmentDefinitionCall(node as CallExpression, context.fragmentIdentifiers)\n ) {\n const call = node as CallExpression;\n const firstArg = call.arguments[0];\n if (firstArg?.expression.type === \"ObjectExpression\" && context.currentArrowFunction) {\n onObjectExpression(firstArg.expression as ObjectExpression, context.currentArrowFunction, {\n isFragmentConfig: true,\n });\n }\n }\n\n // Handle object expressions - check if it's the body of the current arrow function\n if (\n node.type === \"ObjectExpression\" &&\n context.insideGqlDefinition &&\n context.currentArrowFunction &&\n isFieldSelectionObject(node as ObjectExpression, context.currentArrowFunction)\n ) {\n onObjectExpression(node as ObjectExpression, context.currentArrowFunction, { isFragmentConfig: false });\n }\n\n // Recursively visit children\n if (node.type === \"CallExpression\") {\n const call = node as CallExpression;\n traverseNode(call.callee as Node, context, gqlIdentifiers, onObjectExpression);\n for (const arg of call.arguments) {\n traverseNode(arg.expression as Node, context, gqlIdentifiers, onObjectExpression);\n }\n } else if (node.type === \"ArrowFunctionExpression\") {\n const arrow = node as ArrowFunctionExpression;\n // Update context with the new arrow function\n const childContext = { ...context, currentArrowFunction: arrow };\n if (arrow.body.type !== \"BlockStatement\") {\n traverseNode(arrow.body as Node, childContext, gqlIdentifiers, onObjectExpression);\n }\n } else if (node.type === \"ParenthesisExpression\") {\n // Handle parenthesized expressions like `({ ...f.id() })`\n // biome-ignore lint/suspicious/noExplicitAny: SWC types\n const paren = node as any;\n if (paren.expression) {\n traverseNode(paren.expression as Node, context, gqlIdentifiers, onObjectExpression);\n }\n } else if (node.type === \"MemberExpression\") {\n // biome-ignore lint/suspicious/noExplicitAny: SWC types\n const member = node as any;\n traverseNode(member.object as Node, context, gqlIdentifiers, onObjectExpression);\n } else if (node.type === \"ObjectExpression\") {\n const obj = node as ObjectExpression;\n for (const prop of obj.properties) {\n if (prop.type === \"SpreadElement\") {\n traverseNode(prop.arguments as Node, context, gqlIdentifiers, onObjectExpression);\n } else if (prop.type === \"KeyValueProperty\") {\n traverseNode(prop.value as Node, context, gqlIdentifiers, onObjectExpression);\n }\n }\n } else {\n // Generic traversal for other node types\n for (const value of Object.values(node)) {\n if (Array.isArray(value)) {\n for (const child of value) {\n if (child && typeof child === \"object\" && \"type\" in child) {\n traverseNode(child as Node, context, gqlIdentifiers, onObjectExpression);\n }\n }\n } else if (value && typeof value === \"object\" && \"type\" in value) {\n traverseNode(value as Node, context, gqlIdentifiers, onObjectExpression);\n }\n }\n }\n};\n\nconst traverse = (module: Module, gqlIdentifiers: ReadonlySet<string>, onObjectExpression: TraversalCallback): void => {\n const initialContext: TraversalContext = {\n insideGqlDefinition: false,\n currentArrowFunction: null,\n fragmentIdentifiers: new Set(),\n };\n for (const statement of module.body) {\n traverseNode(statement, initialContext, gqlIdentifiers, onObjectExpression);\n }\n};\n\n/**\n * Format soda-gql field selection objects by inserting newlines.\n * Optionally injects fragment keys for anonymous fragments.\n */\nexport const format = (options: FormatOptions): Result<FormatResult, FormatError> => {\n const { sourceCode, filePath, injectFragmentKeys = false, formatTaggedTemplates = false } = options;\n\n // Parse source code with SWC\n let module: Module;\n try {\n const program = parseSync(sourceCode, {\n syntax: \"typescript\",\n tsx: filePath?.endsWith(\".tsx\") ?? true,\n target: \"es2022\",\n decorators: false,\n dynamicImport: true,\n });\n\n if (program.type !== \"Module\") {\n return err({\n type: \"FormatError\",\n code: \"PARSE_ERROR\",\n message: `Not a module${filePath ? ` (${filePath})` : \"\"}`,\n });\n }\n module = program;\n } catch (cause) {\n return err({\n type: \"FormatError\",\n code: \"PARSE_ERROR\",\n message: `Failed to parse source code${filePath ? ` (${filePath})` : \"\"}`,\n cause,\n });\n }\n\n // Calculate span offset for position normalization\n // SWC's BytePos counter accumulates across parseSync calls within the same process\n // Using module.span.start ensures correct position calculation regardless of accumulation\n const spanOffset = module.span.start;\n\n // Collect gql identifiers from imports\n const gqlIdentifiers = collectGqlIdentifiers(module);\n if (gqlIdentifiers.size === 0) {\n return ok({ modified: false, sourceCode });\n }\n\n // Collect insertion points\n const insertionPoints: InsertionPoint[] = [];\n\n traverse(module, gqlIdentifiers, (object, _parent, callbackContext) => {\n // Calculate actual position in source\n const objectStart = object.span.start - spanOffset;\n\n // For fragment config objects, inject key if enabled and not present\n if (callbackContext.isFragmentConfig && injectFragmentKeys && !hasKeyProperty(object)) {\n const key = generateFragmentKey();\n\n if (hasExistingNewline(sourceCode, objectStart)) {\n // Multi-line: insert after newline, preserving indentation\n const indentation = detectIndentationAfterBrace(sourceCode, objectStart) ?? \"\";\n let insertPos = objectStart + 2; // Skip { and \\n\n if (sourceCode[objectStart + 1] === \"\\r\") insertPos++;\n\n insertionPoints.push({\n position: insertPos,\n content: createKeyInsertion(key, indentation),\n });\n } else {\n // Single-line: insert right after {\n insertionPoints.push({\n position: objectStart + 1,\n content: createKeyInsertion(key),\n });\n }\n }\n\n // For field selection objects, insert newline if not present\n if (!callbackContext.isFragmentConfig && !hasExistingNewline(sourceCode, objectStart)) {\n insertionPoints.push({\n position: objectStart + 1,\n content: NEWLINE_INSERTION,\n });\n }\n });\n\n // Tagged template formatting (when enabled)\n if (formatTaggedTemplates) {\n const graphqlResult = getGraphqlModule();\n if (graphqlResult.isErr()) {\n return err(graphqlResult.error);\n }\n const { parse: parseGraphql, print: printGraphql } = graphqlResult.value;\n\n const converter = createSwcSpanConverter(sourceCode);\n const positionCtx: PositionTrackingContext = { spanOffset, converter };\n const templates = walkAndExtract(module as unknown as Node, gqlIdentifiers, positionCtx);\n\n if (templates.length > 0) {\n const defaultFormat: FormatGraphqlFn = (source) => {\n const ast = parseGraphql(source, { noLocation: false });\n return printGraphql(ast);\n };\n\n const templateEdits = formatTemplatesInSource(templates, sourceCode, defaultFormat);\n\n for (const edit of templateEdits) {\n insertionPoints.push({\n position: edit.start,\n content: edit.newText,\n endPosition: edit.end,\n });\n }\n }\n }\n\n // Apply insertions\n if (insertionPoints.length === 0) {\n return ok({ modified: false, sourceCode });\n }\n\n // Sort in descending order to insert from end to beginning\n // This preserves earlier positions while modifying later parts\n const sortedPoints = [...insertionPoints].sort((a, b) => b.position - a.position);\n\n let result = sourceCode;\n for (const point of sortedPoints) {\n const end = point.endPosition ?? point.position;\n result = result.slice(0, point.position) + point.content + result.slice(end);\n }\n\n return ok({ modified: true, sourceCode: result });\n};\n\n/**\n * Check if a file needs formatting (has unformatted field selections).\n * Useful for pre-commit hooks or CI checks.\n */\nexport const needsFormat = (options: FormatOptions): Result<boolean, FormatError> => {\n const { sourceCode, filePath, formatTaggedTemplates = false } = options;\n\n // Parse source code with SWC\n let module: Module;\n try {\n const program = parseSync(sourceCode, {\n syntax: \"typescript\",\n tsx: filePath?.endsWith(\".tsx\") ?? true,\n target: \"es2022\",\n decorators: false,\n dynamicImport: true,\n });\n\n if (program.type !== \"Module\") {\n return err({\n type: \"FormatError\",\n code: \"PARSE_ERROR\",\n message: `Not a module${filePath ? ` (${filePath})` : \"\"}`,\n });\n }\n module = program;\n } catch (cause) {\n return err({\n type: \"FormatError\",\n code: \"PARSE_ERROR\",\n message: `Failed to parse source code${filePath ? ` (${filePath})` : \"\"}`,\n cause,\n });\n }\n\n const spanOffset = module.span.start;\n const gqlIdentifiers = collectGqlIdentifiers(module);\n\n if (gqlIdentifiers.size === 0) {\n return ok(false);\n }\n\n let needsFormatting = false;\n\n traverse(module, gqlIdentifiers, (object, _parent, callbackContext) => {\n if (needsFormatting) return; // Early exit\n\n // Skip fragment config objects for needsFormat check (key injection is optional)\n if (callbackContext.isFragmentConfig) return;\n\n const objectStart = object.span.start - spanOffset;\n if (!hasExistingNewline(sourceCode, objectStart)) {\n needsFormatting = true;\n }\n });\n\n // Check tagged templates when enabled\n if (!needsFormatting && formatTaggedTemplates) {\n const graphqlResult = getGraphqlModule();\n if (graphqlResult.isErr()) {\n return err(graphqlResult.error);\n }\n const { parse: parseGraphql, print: printGraphql } = graphqlResult.value;\n\n const converter = createSwcSpanConverter(sourceCode);\n const positionCtx: PositionTrackingContext = { spanOffset, converter };\n const templates = walkAndExtract(module as unknown as Node, gqlIdentifiers, positionCtx);\n\n if (templates.length > 0) {\n const defaultFormat: FormatGraphqlFn = (source) => {\n const ast = parseGraphql(source, { noLocation: false });\n return printGraphql(ast);\n };\n\n const templateEdits = formatTemplatesInSource(templates, sourceCode, defaultFormat);\n if (templateEdits.length > 0) {\n needsFormatting = true;\n }\n }\n }\n\n return ok(needsFormatting);\n};\n"],"mappings":";;;;;;;;;;;;AAcA,MAAa,kBAAkB,MAAkB,mBAAiD;CAEhG,MAAM,YAAY,KAAK,SAAS,wBAAyB,KAAkD,aAAa;AACxH,KAAI,UAAU,SAAS,aACrB,QAAO,eAAe,IAAI,UAAU,MAAM;AAE5C,KAAI,UAAU,SAAS,sBAAsB,UAAU,SAAS,SAAS,gBAAgB,UAAU,SAAS,UAAU,MACpH,QAAO;AAET,QAAO;;;;;;AAOT,MAAa,uBAAuB,MAAsB,mBAAiD;AACzG,KAAI,KAAK,OAAO,SAAS,mBAAoB,QAAO;CACpD,MAAM,EAAE,WAAW,KAAK;AAGxB,KAAI,CAAC,eAAe,QAAQ,eAAe,CAAE,QAAO;CAIpD,MAAM,WAAW,KAAK,UAAU;AAChC,KAAI,CAAC,YAAa,SAAS,WAAW,SAAS,6BAA6B,SAAS,WAAW,SAAS,qBACvG,QAAO;AAET,QAAO;;;;;;AAOT,MAAa,0BAA0B,QAA0B,WAA6C;CAG5G,IAAIA,aAAsC;AAE1C,KAAI,OAAO,KAAK,SAAS,mBACvB,cAAa,OAAO;UACX,OAAO,KAAK,SAAS,yBAAyB;EAEvD,MAAM,QAAQ,OAAO,KAAK;AAC1B,MAAI,MAAM,SAAS,mBACjB,cAAa;;AAIjB,KAAI,CAAC,WAAY,QAAO;AACxB,KAAI,WAAW,KAAK,UAAU,OAAO,KAAK,MAAO,QAAO;CAGxD,MAAM,QAAQ,OAAO,OAAO;AAC5B,KAAI,CAAC,SAAS,MAAM,SAAS,gBAAiB,QAAO;AAErD,QAAO,MAAM,WAAW,MAAM,MAA6B;AACzD,MAAI,EAAE,SAAS,6BAA6B,EAAE,IAAI,SAAS,aACzD,QAAO,EAAE,IAAI,UAAU;AAEzB,MAAI,EAAE,SAAS,+BAA+B,EAAE,IAAI,SAAS,aAC3D,QAAO,EAAE,IAAI,UAAU;AAEzB,SAAO;GACP;;;;;AAMJ,MAAa,yBAAyB,WAAgC;CACpE,MAAM,iCAAiB,IAAI,KAAa;AAExC,MAAK,MAAM,QAAQ,OAAO,MAAM;AAC9B,MAAI,KAAK,SAAS,oBAAqB;AAEvC,OAAK,MAAM,aAAa,KAAK,YAAY;AACvC,OAAI,UAAU,SAAS,mBAErB;SADiB,UAAU,UAAU,SAAS,UAAU,MAAM,WAC7C,MACf,gBAAe,IAAI,UAAU,MAAM,MAAM;;AAG7C,OAAI,UAAU,SAAS,0BAErB;QAAI,UAAU,MAAM,UAAU,MAC5B,gBAAe,IAAI,UAAU,MAAM,MAAM;;AAG7C,OAAI,UAAU,SAAS,4BAA4B;;;AAOvD,QAAO;;;;;;AAOT,MAAa,4BAA4B,MAAsB,wBAAsD;AACnH,KAAI,KAAK,OAAO,SAAS,mBAAoB,QAAO;CAEpD,MAAM,EAAE,WAAW,KAAK;AAExB,KAAI,OAAO,SAAS,aAAc,QAAO;AACzC,KAAI,CAAC,oBAAoB,IAAI,OAAO,MAAM,CAAE,QAAO;CAEnD,MAAM,WAAW,KAAK,UAAU;AAChC,KAAI,CAAC,YAAY,SAAS,WAAW,SAAS,mBAAoB,QAAO;AAEzE,QAAO;;;;;AAMT,MAAa,kBAAkB,QAAmC;AAChE,QAAO,IAAI,WAAW,MAAM,SAAS;AACnC,MAAI,KAAK,SAAS,sBAAsB,KAAK,IAAI,SAAS,aACxD,QAAO,KAAK,IAAI,UAAU;AAE5B,SAAO;GACP;;;;;;AAOJ,MAAa,8BAA8B,kBAAwD;CACjG,MAAM,sCAAsB,IAAI,KAAa;CAE7C,MAAM,QAAQ,cAAc,OAAO;AACnC,KAAI,OAAO,SAAS,gBAAiB,QAAO;AAE5C,MAAK,MAAM,KAAK,MAAM,YAAuC;AAC3D,MAAI,EAAE,SAAS,6BAA6B,EAAE,IAAI,SAAS,gBAAgB,EAAE,IAAI,UAAU,YAAY;GACrG,MAAM,YAAY,sBAAsB,EAAE,MAAM;AAChD,OAAI,UAAW,qBAAoB,IAAI,UAAU;;AAEnD,MAAI,EAAE,SAAS,+BAA+B,EAAE,IAAI,SAAS,gBAAgB,EAAE,IAAI,UAAU,WAC3F,qBAAoB,IAAI,EAAE,IAAI,MAAM;;AAIxC,QAAO;;;;;AAMT,MAAM,yBAAyB,YAAoC;AACjE,KAAI,QAAQ,SAAS,aAAc,QAAO,QAAQ;AAElD,KAAK,QAAgB,SAAS,oBAAqB,QAAQ,QAAgB;AAC3E,QAAO;;;;;;;;AC1KT,MAAa,oBAAoB;;;;;AAMjC,MAAa,4BAAoC;AAC/C,QAAO,YAAY,EAAE,CAAC,SAAS,MAAM;;;;;;;;;;;;AAavC,MAAa,sBAAsB,KAAa,gBAAiC;AAC/E,KAAI,gBAAgB,OAGlB,QAAO,GAAG,YAAY,QAAQ,IAAI;AAGpC,QAAO,UAAU,IAAI;;;;;;;;;AAUvB,MAAa,sBAAsB,QAAgB,mBAAoC;CAKrF,MAAM,WAAW,OAHL,iBAAiB;AAI7B,QAAO,aAAa,QAAQ,aAAa;;;;;;;;;AAU3C,MAAa,+BAA+B,QAAgB,mBAA0C;CACpG,MAAM,aAAa,iBAAiB;CACpC,MAAM,OAAO,OAAO;AAGpB,KAAI,SAAS,QAAQ,SAAS,KAC5B,QAAO;CAIT,IAAI,MAAM,aAAa;AACvB,KAAI,SAAS,QAAQ,OAAO,SAAS,KACnC;CAIF,IAAI,cAAc;AAClB,QAAO,MAAM,OAAO,QAAQ;EAC1B,MAAM,IAAI,OAAO;AACjB,MAAI,MAAM,OAAO,MAAM,KAAM;AAC3B,kBAAe;AACf;QAEA;;AAIJ,QAAO;;;;;ACzET,MAAM,UAAU,cAAc,OAAO,KAAK,IAAI;AAO9C,IAAIC;AACJ,IAAIC;AAEJ,MAAM,yBAA6D;AACjE,KAAI,oBACF,QAAO,IAAI;EACT,MAAM;EACN,MAAM;EACN,SAAS;EACV,CAAC;AAEJ,KAAI,CAAC,eACH,KAAI;AAEF,mBAAiB,QAAQ,UAAU;UAC5B,OAAO;AACd,wBAAsB,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC;AAC/E,SAAO,IAAI;GACT,MAAM;GACN,MAAM;GACN,SAAS;GACT;GACD,CAAC;;AAGN,QAAO,GAAG,eAAe;;;;;AA6C3B,MAAM,gBACJ,MACA,SACA,gBACA,uBACS;AAET,KAAI,KAAK,SAAS,oBAAoB,oBAAoB,MAAwB,eAAe,EAAE;EAEjG,MAAM,WADO,KACS,UAAU;AAChC,MAAI,UAAU,WAAW,SAAS,2BAA2B;GAC3D,MAAM,QAAQ,SAAS;GACvB,MAAM,cAAc,2BAA2B,MAAM;AACrD,aAAU;IACR,GAAG;IACH,qBAAqB;IACrB,qBAAqB,IAAI,IAAI,CAAC,GAAG,QAAQ,qBAAqB,GAAG,YAAY,CAAC;IAC/E;QAED,WAAU;GAAE,GAAG;GAAS,qBAAqB;GAAM;;AAKvD,KACE,KAAK,SAAS,oBACd,QAAQ,uBACR,yBAAyB,MAAwB,QAAQ,oBAAoB,EAC7E;EAEA,MAAM,WADO,KACS,UAAU;AAChC,MAAI,UAAU,WAAW,SAAS,sBAAsB,QAAQ,qBAC9D,oBAAmB,SAAS,YAAgC,QAAQ,sBAAsB,EACxF,kBAAkB,MACnB,CAAC;;AAKN,KACE,KAAK,SAAS,sBACd,QAAQ,uBACR,QAAQ,wBACR,uBAAuB,MAA0B,QAAQ,qBAAqB,CAE9E,oBAAmB,MAA0B,QAAQ,sBAAsB,EAAE,kBAAkB,OAAO,CAAC;AAIzG,KAAI,KAAK,SAAS,kBAAkB;EAClC,MAAM,OAAO;AACb,eAAa,KAAK,QAAgB,SAAS,gBAAgB,mBAAmB;AAC9E,OAAK,MAAM,OAAO,KAAK,UACrB,cAAa,IAAI,YAAoB,SAAS,gBAAgB,mBAAmB;YAE1E,KAAK,SAAS,2BAA2B;EAClD,MAAM,QAAQ;EAEd,MAAM,eAAe;GAAE,GAAG;GAAS,sBAAsB;GAAO;AAChE,MAAI,MAAM,KAAK,SAAS,iBACtB,cAAa,MAAM,MAAc,cAAc,gBAAgB,mBAAmB;YAE3E,KAAK,SAAS,yBAAyB;EAGhD,MAAM,QAAQ;AACd,MAAI,MAAM,WACR,cAAa,MAAM,YAAoB,SAAS,gBAAgB,mBAAmB;YAE5E,KAAK,SAAS,mBAGvB,cADe,KACK,QAAgB,SAAS,gBAAgB,mBAAmB;UACvE,KAAK,SAAS,oBAAoB;EAC3C,MAAM,MAAM;AACZ,OAAK,MAAM,QAAQ,IAAI,WACrB,KAAI,KAAK,SAAS,gBAChB,cAAa,KAAK,WAAmB,SAAS,gBAAgB,mBAAmB;WACxE,KAAK,SAAS,mBACvB,cAAa,KAAK,OAAe,SAAS,gBAAgB,mBAAmB;OAKjF,MAAK,MAAM,SAAS,OAAO,OAAO,KAAK,CACrC,KAAI,MAAM,QAAQ,MAAM,EACtB;OAAK,MAAM,SAAS,MAClB,KAAI,SAAS,OAAO,UAAU,YAAY,UAAU,MAClD,cAAa,OAAe,SAAS,gBAAgB,mBAAmB;YAGnE,SAAS,OAAO,UAAU,YAAY,UAAU,MACzD,cAAa,OAAe,SAAS,gBAAgB,mBAAmB;;AAMhF,MAAM,YAAY,QAAgB,gBAAqC,uBAAgD;CACrH,MAAMC,iBAAmC;EACvC,qBAAqB;EACrB,sBAAsB;EACtB,qCAAqB,IAAI,KAAK;EAC/B;AACD,MAAK,MAAM,aAAa,OAAO,KAC7B,cAAa,WAAW,gBAAgB,gBAAgB,mBAAmB;;;;;;AAQ/E,MAAa,UAAU,YAA8D;CACnF,MAAM,EAAE,YAAY,UAAU,qBAAqB,OAAO,wBAAwB,UAAU;CAG5F,IAAIC;AACJ,KAAI;EACF,MAAM,UAAU,UAAU,YAAY;GACpC,QAAQ;GACR,KAAK,UAAU,SAAS,OAAO,IAAI;GACnC,QAAQ;GACR,YAAY;GACZ,eAAe;GAChB,CAAC;AAEF,MAAI,QAAQ,SAAS,SACnB,QAAO,IAAI;GACT,MAAM;GACN,MAAM;GACN,SAAS,eAAe,WAAW,KAAK,SAAS,KAAK;GACvD,CAAC;AAEJ,WAAS;UACF,OAAO;AACd,SAAO,IAAI;GACT,MAAM;GACN,MAAM;GACN,SAAS,8BAA8B,WAAW,KAAK,SAAS,KAAK;GACrE;GACD,CAAC;;CAMJ,MAAM,aAAa,OAAO,KAAK;CAG/B,MAAM,iBAAiB,sBAAsB,OAAO;AACpD,KAAI,eAAe,SAAS,EAC1B,QAAO,GAAG;EAAE,UAAU;EAAO;EAAY,CAAC;CAI5C,MAAMC,kBAAoC,EAAE;AAE5C,UAAS,QAAQ,iBAAiB,QAAQ,SAAS,oBAAoB;EAErE,MAAM,cAAc,OAAO,KAAK,QAAQ;AAGxC,MAAI,gBAAgB,oBAAoB,sBAAsB,CAAC,eAAe,OAAO,EAAE;GACrF,MAAM,MAAM,qBAAqB;AAEjC,OAAI,mBAAmB,YAAY,YAAY,EAAE;IAE/C,MAAM,cAAc,4BAA4B,YAAY,YAAY,IAAI;IAC5E,IAAI,YAAY,cAAc;AAC9B,QAAI,WAAW,cAAc,OAAO,KAAM;AAE1C,oBAAgB,KAAK;KACnB,UAAU;KACV,SAAS,mBAAmB,KAAK,YAAY;KAC9C,CAAC;SAGF,iBAAgB,KAAK;IACnB,UAAU,cAAc;IACxB,SAAS,mBAAmB,IAAI;IACjC,CAAC;;AAKN,MAAI,CAAC,gBAAgB,oBAAoB,CAAC,mBAAmB,YAAY,YAAY,CACnF,iBAAgB,KAAK;GACnB,UAAU,cAAc;GACxB,SAAS;GACV,CAAC;GAEJ;AAGF,KAAI,uBAAuB;EACzB,MAAM,gBAAgB,kBAAkB;AACxC,MAAI,cAAc,OAAO,CACvB,QAAO,IAAI,cAAc,MAAM;EAEjC,MAAM,EAAE,OAAO,cAAc,OAAO,iBAAiB,cAAc;EAGnE,MAAMC,cAAuC;GAAE;GAAY,WADzC,uBAAuB,WAAW;GACkB;EACtE,MAAM,YAAY,eAAe,QAA2B,gBAAgB,YAAY;AAExF,MAAI,UAAU,SAAS,GAAG;GACxB,MAAMC,iBAAkC,WAAW;AAEjD,WAAO,aADK,aAAa,QAAQ,EAAE,YAAY,OAAO,CAAC,CAC/B;;GAG1B,MAAM,gBAAgB,wBAAwB,WAAW,YAAY,cAAc;AAEnF,QAAK,MAAM,QAAQ,cACjB,iBAAgB,KAAK;IACnB,UAAU,KAAK;IACf,SAAS,KAAK;IACd,aAAa,KAAK;IACnB,CAAC;;;AAMR,KAAI,gBAAgB,WAAW,EAC7B,QAAO,GAAG;EAAE,UAAU;EAAO;EAAY,CAAC;CAK5C,MAAM,eAAe,CAAC,GAAG,gBAAgB,CAAC,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,SAAS;CAEjF,IAAI,SAAS;AACb,MAAK,MAAM,SAAS,cAAc;EAChC,MAAM,MAAM,MAAM,eAAe,MAAM;AACvC,WAAS,OAAO,MAAM,GAAG,MAAM,SAAS,GAAG,MAAM,UAAU,OAAO,MAAM,IAAI;;AAG9E,QAAO,GAAG;EAAE,UAAU;EAAM,YAAY;EAAQ,CAAC;;;;;;AAOnD,MAAa,eAAe,YAAyD;CACnF,MAAM,EAAE,YAAY,UAAU,wBAAwB,UAAU;CAGhE,IAAIH;AACJ,KAAI;EACF,MAAM,UAAU,UAAU,YAAY;GACpC,QAAQ;GACR,KAAK,UAAU,SAAS,OAAO,IAAI;GACnC,QAAQ;GACR,YAAY;GACZ,eAAe;GAChB,CAAC;AAEF,MAAI,QAAQ,SAAS,SACnB,QAAO,IAAI;GACT,MAAM;GACN,MAAM;GACN,SAAS,eAAe,WAAW,KAAK,SAAS,KAAK;GACvD,CAAC;AAEJ,WAAS;UACF,OAAO;AACd,SAAO,IAAI;GACT,MAAM;GACN,MAAM;GACN,SAAS,8BAA8B,WAAW,KAAK,SAAS,KAAK;GACrE;GACD,CAAC;;CAGJ,MAAM,aAAa,OAAO,KAAK;CAC/B,MAAM,iBAAiB,sBAAsB,OAAO;AAEpD,KAAI,eAAe,SAAS,EAC1B,QAAO,GAAG,MAAM;CAGlB,IAAI,kBAAkB;AAEtB,UAAS,QAAQ,iBAAiB,QAAQ,SAAS,oBAAoB;AACrE,MAAI,gBAAiB;AAGrB,MAAI,gBAAgB,iBAAkB;AAGtC,MAAI,CAAC,mBAAmB,YADJ,OAAO,KAAK,QAAQ,WACQ,CAC9C,mBAAkB;GAEpB;AAGF,KAAI,CAAC,mBAAmB,uBAAuB;EAC7C,MAAM,gBAAgB,kBAAkB;AACxC,MAAI,cAAc,OAAO,CACvB,QAAO,IAAI,cAAc,MAAM;EAEjC,MAAM,EAAE,OAAO,cAAc,OAAO,iBAAiB,cAAc;EAGnE,MAAME,cAAuC;GAAE;GAAY,WADzC,uBAAuB,WAAW;GACkB;EACtE,MAAM,YAAY,eAAe,QAA2B,gBAAgB,YAAY;AAExF,MAAI,UAAU,SAAS,GAAG;GACxB,MAAMC,iBAAkC,WAAW;AAEjD,WAAO,aADK,aAAa,QAAQ,EAAE,YAAY,OAAO,CAAC,CAC/B;;AAI1B,OADsB,wBAAwB,WAAW,YAAY,cAAc,CACjE,SAAS,EACzB,mBAAkB;;;AAKxB,QAAO,GAAG,gBAAgB"}
1
+ {"version":3,"file":"index.mjs","names":["bodyObject: ObjectExpression | null","_graphqlModule: GraphqlModule | undefined","_graphqlModuleError: Error | undefined","initialContext: TraversalContext","module: Module","insertionPoints: InsertionPoint[]","defaultFormat: FormatGraphqlFn"],"sources":["../src/detection.ts","../src/insertion.ts","../src/format.ts"],"sourcesContent":["import type {\n ArrowFunctionExpression,\n CallExpression,\n Expression,\n Module,\n ObjectExpression,\n ObjectPatternProperty,\n Pattern,\n} from \"@swc/types\";\n\n/**\n * Check if an expression is a reference to gql\n * Handles: gql, namespace.gql\n */\nexport const isGqlReference = (node: Expression, gqlIdentifiers: ReadonlySet<string>): boolean => {\n // Unwrap TsNonNullExpression: gql! -> gql\n const unwrapped = node.type === \"TsNonNullExpression\" ? (node as { type: string; expression: Expression }).expression : node;\n if (unwrapped.type === \"Identifier\") {\n return gqlIdentifiers.has(unwrapped.value);\n }\n if (unwrapped.type === \"MemberExpression\" && unwrapped.property.type === \"Identifier\" && unwrapped.property.value === \"gql\") {\n return true;\n }\n return false;\n};\n\n/**\n * Check if a call expression is a gql definition call\n * Handles: gql.default(...), gql.model(...), gql.schemaName(...) (multi-schema)\n */\nexport const isGqlDefinitionCall = (node: CallExpression, gqlIdentifiers: ReadonlySet<string>): boolean => {\n if (node.callee.type !== \"MemberExpression\") return false;\n const { object } = node.callee;\n\n // Check object is a gql reference first\n if (!isGqlReference(object, gqlIdentifiers)) return false;\n\n // Verify first argument is an arrow function (factory pattern)\n // SWC's arguments are ExprOrSpread[], so access via .expression\n const firstArg = node.arguments[0];\n if (!firstArg || (firstArg.expression.type !== \"ArrowFunctionExpression\" && firstArg.expression.type !== \"FunctionExpression\"))\n return false;\n\n return true;\n};\n\n/**\n * Check if an object expression is a field selection object\n * Field selection objects are returned from arrow functions with ({ f }) or ({ f, $ }) parameter\n */\nexport const isFieldSelectionObject = (object: ObjectExpression, parent: ArrowFunctionExpression): boolean => {\n // The object must be the body of the arrow function\n // Handle both direct ObjectExpression and parenthesized ObjectExpression: `({ ... })`\n let bodyObject: ObjectExpression | null = null;\n\n if (parent.body.type === \"ObjectExpression\") {\n bodyObject = parent.body;\n } else if (parent.body.type === \"ParenthesisExpression\") {\n // Handle `({ f }) => ({ ...f.id() })` pattern where body is parenthesized\n const inner = parent.body.expression;\n if (inner.type === \"ObjectExpression\") {\n bodyObject = inner;\n }\n }\n\n if (!bodyObject) return false;\n if (bodyObject.span.start !== object.span.start) return false;\n\n // Check if first parameter has 'f' destructured\n const param = parent.params[0];\n if (!param || param.type !== \"ObjectPattern\") return false;\n\n return param.properties.some((p: ObjectPatternProperty) => {\n if (p.type === \"KeyValuePatternProperty\" && p.key.type === \"Identifier\") {\n return p.key.value === \"f\";\n }\n if (p.type === \"AssignmentPatternProperty\" && p.key.type === \"Identifier\") {\n return p.key.value === \"f\";\n }\n return false;\n });\n};\n\n/**\n * Collect gql identifiers from import declarations\n */\nexport const collectGqlIdentifiers = (module: Module): Set<string> => {\n const gqlIdentifiers = new Set<string>();\n\n for (const item of module.body) {\n if (item.type !== \"ImportDeclaration\") continue;\n\n for (const specifier of item.specifiers) {\n if (specifier.type === \"ImportSpecifier\") {\n const imported = specifier.imported?.value ?? specifier.local.value;\n if (imported === \"gql\") {\n gqlIdentifiers.add(specifier.local.value);\n }\n }\n if (specifier.type === \"ImportDefaultSpecifier\") {\n // Check if default import might be gql\n if (specifier.local.value === \"gql\") {\n gqlIdentifiers.add(specifier.local.value);\n }\n }\n if (specifier.type === \"ImportNamespaceSpecifier\") {\n // namespace import: import * as ns from \"...\"\n // Would need ns.gql pattern - handled by isGqlReference\n }\n }\n }\n\n return gqlIdentifiers;\n};\n\n/**\n * Check if a call expression is a fragment definition call.\n * Pattern: fragment.TypeName({ ... }) where `fragment` comes from gql factory destructuring.\n */\nexport const isFragmentDefinitionCall = (node: CallExpression, fragmentIdentifiers: ReadonlySet<string>): boolean => {\n if (node.callee.type !== \"MemberExpression\") return false;\n\n const { object } = node.callee;\n\n if (object.type !== \"Identifier\") return false;\n if (!fragmentIdentifiers.has(object.value)) return false;\n\n const firstArg = node.arguments[0];\n if (!firstArg || firstArg.expression.type !== \"ObjectExpression\") return false;\n\n return true;\n};\n\n/**\n * Check if an object expression already has a `key` property.\n */\nexport const hasKeyProperty = (obj: ObjectExpression): boolean => {\n return obj.properties.some((prop) => {\n if (prop.type === \"KeyValueProperty\" && prop.key.type === \"Identifier\") {\n return prop.key.value === \"key\";\n }\n return false;\n });\n};\n\n/**\n * Collect fragment identifiers from gql factory arrow function parameter.\n * Looks for patterns like: ({ fragment }) => ... or ({ fragment: f }) => ...\n */\nexport const collectFragmentIdentifiers = (arrowFunction: ArrowFunctionExpression): Set<string> => {\n const fragmentIdentifiers = new Set<string>();\n\n const param = arrowFunction.params[0];\n if (param?.type !== \"ObjectPattern\") return fragmentIdentifiers;\n\n for (const p of param.properties as ObjectPatternProperty[]) {\n if (p.type === \"KeyValuePatternProperty\" && p.key.type === \"Identifier\" && p.key.value === \"fragment\") {\n const localName = extractIdentifierName(p.value);\n if (localName) fragmentIdentifiers.add(localName);\n }\n if (p.type === \"AssignmentPatternProperty\" && p.key.type === \"Identifier\" && p.key.value === \"fragment\") {\n fragmentIdentifiers.add(p.key.value);\n }\n }\n\n return fragmentIdentifiers;\n};\n\n/**\n * Extract identifier name from a pattern node.\n */\nconst extractIdentifierName = (pattern: Pattern): string | null => {\n if (pattern.type === \"Identifier\") return pattern.value;\n // biome-ignore lint/suspicious/noExplicitAny: SWC types\n if ((pattern as any).type === \"BindingIdentifier\") return (pattern as any).value;\n return null;\n};\n","import { randomBytes } from \"node:crypto\";\n\n/**\n * The newline string to insert after object opening brace\n */\nexport const NEWLINE_INSERTION = \"\\n\";\n\n/**\n * Generate a random 8-character hex key for fragment identification.\n * Uses Node.js crypto for cryptographically secure random bytes.\n */\nexport const generateFragmentKey = (): string => {\n return randomBytes(4).toString(\"hex\");\n};\n\n/**\n * Create the key property insertion string.\n *\n * For single-line objects: returns ` key: \"xxxxxxxx\",\\n`\n * For multi-line objects (with indentation): returns `{indentation}key: \"xxxxxxxx\",\\n`\n * (the existing indentation before the next property is preserved)\n *\n * @param key - The hex key string\n * @param indentation - Optional indentation string for multi-line objects\n */\nexport const createKeyInsertion = (key: string, indentation?: string): string => {\n if (indentation !== undefined) {\n // Multi-line: key goes on its own line with indentation\n // The next property's indentation is already in the source, so don't duplicate it\n return `${indentation}key: \"${key}\",\\n`;\n }\n // Single-line: space before key, newline after\n return ` key: \"${key}\",\\n`;\n};\n\n/**\n * Check if there's already a newline after the opening brace.\n * Uses string inspection rather than AST.\n *\n * @param source - The source code string\n * @param objectStartPos - The position of the `{` character in the source\n */\nexport const hasExistingNewline = (source: string, objectStartPos: number): boolean => {\n // Skip the `{` character\n const pos = objectStartPos + 1;\n\n // Check if next character is a newline\n const nextChar = source[pos];\n return nextChar === \"\\n\" || nextChar === \"\\r\";\n};\n\n/**\n * Detect the indentation used after an opening brace.\n * Returns the whitespace string after the newline, or null if no newline.\n *\n * @param source - The source code string\n * @param objectStartPos - The position of the `{` character\n */\nexport const detectIndentationAfterBrace = (source: string, objectStartPos: number): string | null => {\n const afterBrace = objectStartPos + 1;\n const char = source[afterBrace];\n\n // Check for newline (handles both \\n and \\r\\n)\n if (char !== \"\\n\" && char !== \"\\r\") {\n return null;\n }\n\n // Skip past the newline character(s)\n let pos = afterBrace + 1;\n if (char === \"\\r\" && source[pos] === \"\\n\") {\n pos++;\n }\n\n // Collect whitespace (indentation)\n let indentation = \"\";\n while (pos < source.length) {\n const c = source[pos];\n if (c === \" \" || c === \"\\t\") {\n indentation += c;\n pos++;\n } else {\n break;\n }\n }\n\n return indentation;\n};\n","import { createRequire } from \"node:module\";\nimport {\n type FormatGraphqlFn,\n formatTemplatesInSource,\n type PositionTrackingContext,\n walkAndExtract,\n} from \"@soda-gql/common/template-extraction\";\nimport { createSwcSpanConverter } from \"@soda-gql/common/utils\";\nimport { parseSync } from \"@swc/core\";\nimport type { ArrowFunctionExpression, CallExpression, Module, Node, ObjectExpression } from \"@swc/types\";\nimport { err, ok, type Result } from \"neverthrow\";\n\nconst require = createRequire(import.meta.url);\n\ntype GraphqlModule = {\n parse: (source: string, options?: { noLocation?: boolean }) => unknown;\n print: (ast: unknown) => string;\n};\n\nlet _graphqlModule: GraphqlModule | undefined;\nlet _graphqlModuleError: Error | undefined;\n\nconst getGraphqlModule = (): Result<GraphqlModule, FormatError> => {\n if (_graphqlModuleError) {\n return err({\n type: \"FormatError\",\n code: \"MISSING_DEPENDENCY\",\n message: 'The \"graphql\" package is required for soda-gql formatter. Install it with: bun add graphql',\n });\n }\n if (!_graphqlModule) {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n _graphqlModule = require(\"graphql\") as GraphqlModule;\n } catch (cause) {\n _graphqlModuleError = cause instanceof Error ? cause : new Error(String(cause));\n return err({\n type: \"FormatError\",\n code: \"MISSING_DEPENDENCY\",\n message: 'The \"graphql\" package is required for soda-gql formatter. Install it with: bun add graphql',\n cause,\n });\n }\n }\n return ok(_graphqlModule);\n};\n\nimport {\n collectFragmentIdentifiers,\n collectGqlIdentifiers,\n hasKeyProperty,\n isFieldSelectionObject,\n isFragmentDefinitionCall,\n isGqlDefinitionCall,\n} from \"./detection\";\nimport {\n createKeyInsertion,\n detectIndentationAfterBrace,\n generateFragmentKey,\n hasExistingNewline,\n NEWLINE_INSERTION,\n} from \"./insertion\";\nimport type { FormatError, FormatOptions, FormatResult } from \"./types\";\n\ntype InsertionPoint = {\n readonly position: number;\n readonly content: string;\n readonly endPosition?: number;\n};\n\ntype TraversalContext = {\n insideGqlDefinition: boolean;\n currentArrowFunction: ArrowFunctionExpression | null;\n fragmentIdentifiers: ReadonlySet<string>;\n};\n\ntype TraversalCallbackContext = {\n readonly isFragmentConfig: boolean;\n};\n\ntype TraversalCallback = (\n object: ObjectExpression,\n parent: ArrowFunctionExpression,\n callbackContext: TraversalCallbackContext,\n) => void;\n\n/**\n * Simple recursive AST traversal\n */\nconst traverseNode = (\n node: Node,\n context: TraversalContext,\n gqlIdentifiers: ReadonlySet<string>,\n onObjectExpression: TraversalCallback,\n): void => {\n // Check for gql definition call entry and collect fragment identifiers\n if (node.type === \"CallExpression\" && isGqlDefinitionCall(node as CallExpression, gqlIdentifiers)) {\n const call = node as CallExpression;\n const firstArg = call.arguments[0];\n if (firstArg?.expression.type === \"ArrowFunctionExpression\") {\n const arrow = firstArg.expression as ArrowFunctionExpression;\n const fragmentIds = collectFragmentIdentifiers(arrow);\n context = {\n ...context,\n insideGqlDefinition: true,\n fragmentIdentifiers: new Set([...context.fragmentIdentifiers, ...fragmentIds]),\n };\n } else {\n context = { ...context, insideGqlDefinition: true };\n }\n }\n\n // Check for fragment definition call: fragment.TypeName({ ... })\n if (\n node.type === \"CallExpression\" &&\n context.insideGqlDefinition &&\n isFragmentDefinitionCall(node as CallExpression, context.fragmentIdentifiers)\n ) {\n const call = node as CallExpression;\n const firstArg = call.arguments[0];\n if (firstArg?.expression.type === \"ObjectExpression\" && context.currentArrowFunction) {\n onObjectExpression(firstArg.expression as ObjectExpression, context.currentArrowFunction, {\n isFragmentConfig: true,\n });\n }\n }\n\n // Handle object expressions - check if it's the body of the current arrow function\n if (\n node.type === \"ObjectExpression\" &&\n context.insideGqlDefinition &&\n context.currentArrowFunction &&\n isFieldSelectionObject(node as ObjectExpression, context.currentArrowFunction)\n ) {\n onObjectExpression(node as ObjectExpression, context.currentArrowFunction, { isFragmentConfig: false });\n }\n\n // Recursively visit children\n if (node.type === \"CallExpression\") {\n const call = node as CallExpression;\n traverseNode(call.callee as Node, context, gqlIdentifiers, onObjectExpression);\n for (const arg of call.arguments) {\n traverseNode(arg.expression as Node, context, gqlIdentifiers, onObjectExpression);\n }\n } else if (node.type === \"ArrowFunctionExpression\") {\n const arrow = node as ArrowFunctionExpression;\n // Update context with the new arrow function\n const childContext = { ...context, currentArrowFunction: arrow };\n if (arrow.body.type !== \"BlockStatement\") {\n traverseNode(arrow.body as Node, childContext, gqlIdentifiers, onObjectExpression);\n }\n } else if (node.type === \"ParenthesisExpression\") {\n // Handle parenthesized expressions like `({ ...f.id() })`\n // biome-ignore lint/suspicious/noExplicitAny: SWC types\n const paren = node as any;\n if (paren.expression) {\n traverseNode(paren.expression as Node, context, gqlIdentifiers, onObjectExpression);\n }\n } else if (node.type === \"MemberExpression\") {\n // biome-ignore lint/suspicious/noExplicitAny: SWC types\n const member = node as any;\n traverseNode(member.object as Node, context, gqlIdentifiers, onObjectExpression);\n } else if (node.type === \"ObjectExpression\") {\n const obj = node as ObjectExpression;\n for (const prop of obj.properties) {\n if (prop.type === \"SpreadElement\") {\n traverseNode(prop.arguments as Node, context, gqlIdentifiers, onObjectExpression);\n } else if (prop.type === \"KeyValueProperty\") {\n traverseNode(prop.value as Node, context, gqlIdentifiers, onObjectExpression);\n }\n }\n } else {\n // Generic traversal for other node types\n for (const value of Object.values(node)) {\n if (Array.isArray(value)) {\n for (const child of value) {\n if (child && typeof child === \"object\" && \"type\" in child) {\n traverseNode(child as Node, context, gqlIdentifiers, onObjectExpression);\n }\n }\n } else if (value && typeof value === \"object\" && \"type\" in value) {\n traverseNode(value as Node, context, gqlIdentifiers, onObjectExpression);\n }\n }\n }\n};\n\nconst traverse = (module: Module, gqlIdentifiers: ReadonlySet<string>, onObjectExpression: TraversalCallback): void => {\n const initialContext: TraversalContext = {\n insideGqlDefinition: false,\n currentArrowFunction: null,\n fragmentIdentifiers: new Set(),\n };\n for (const statement of module.body) {\n traverseNode(statement, initialContext, gqlIdentifiers, onObjectExpression);\n }\n};\n\n/**\n * Format soda-gql field selection objects by inserting newlines.\n * Optionally injects fragment keys for anonymous fragments.\n */\nexport const format = (options: FormatOptions): Result<FormatResult, FormatError> => {\n const { sourceCode, filePath, injectFragmentKeys = false } = options;\n\n // Parse source code with SWC\n let module: Module;\n try {\n const program = parseSync(sourceCode, {\n syntax: \"typescript\",\n tsx: filePath?.endsWith(\".tsx\") ?? true,\n target: \"es2022\",\n decorators: false,\n dynamicImport: true,\n });\n\n if (program.type !== \"Module\") {\n return err({\n type: \"FormatError\",\n code: \"PARSE_ERROR\",\n message: `Not a module${filePath ? ` (${filePath})` : \"\"}`,\n });\n }\n module = program;\n } catch (cause) {\n return err({\n type: \"FormatError\",\n code: \"PARSE_ERROR\",\n message: `Failed to parse source code${filePath ? ` (${filePath})` : \"\"}`,\n cause,\n });\n }\n\n // Calculate span offset for position normalization\n // SWC's BytePos counter accumulates across parseSync calls within the same process\n // Using module.span.start ensures correct position calculation regardless of accumulation\n const spanOffset = module.span.start;\n\n // SWC returns UTF-8 byte offsets; JS strings use UTF-16 code units.\n // The converter handles this mapping (fast path for ASCII-only sources).\n const converter = createSwcSpanConverter(sourceCode);\n\n // Collect gql identifiers from imports\n const gqlIdentifiers = collectGqlIdentifiers(module);\n if (gqlIdentifiers.size === 0) {\n return ok({ modified: false, sourceCode });\n }\n\n // Collect insertion points\n const insertionPoints: InsertionPoint[] = [];\n\n traverse(module, gqlIdentifiers, (object, _parent, callbackContext) => {\n // Calculate actual position in source (byte offset → char index)\n const objectStart = converter.byteOffsetToCharIndex(object.span.start - spanOffset);\n\n // For fragment config objects, inject key if enabled and not present\n if (callbackContext.isFragmentConfig && injectFragmentKeys && !hasKeyProperty(object)) {\n const key = generateFragmentKey();\n\n if (hasExistingNewline(sourceCode, objectStart)) {\n // Multi-line: insert after newline, preserving indentation\n const indentation = detectIndentationAfterBrace(sourceCode, objectStart) ?? \"\";\n let insertPos = objectStart + 2; // Skip { and \\n\n if (sourceCode[objectStart + 1] === \"\\r\") insertPos++;\n\n insertionPoints.push({\n position: insertPos,\n content: createKeyInsertion(key, indentation),\n });\n } else {\n // Single-line: insert right after {\n insertionPoints.push({\n position: objectStart + 1,\n content: createKeyInsertion(key),\n });\n }\n }\n\n // For field selection objects, insert newline if not present\n if (!callbackContext.isFragmentConfig && !hasExistingNewline(sourceCode, objectStart)) {\n insertionPoints.push({\n position: objectStart + 1,\n content: NEWLINE_INSERTION,\n });\n }\n });\n\n // Tagged template formatting\n const graphqlResult = getGraphqlModule();\n if (graphqlResult.isErr()) {\n return err(graphqlResult.error);\n }\n const { parse: parseGraphql, print: printGraphql } = graphqlResult.value;\n\n const positionCtx: PositionTrackingContext = { spanOffset, converter };\n const templates = walkAndExtract(module as unknown as Node, gqlIdentifiers, positionCtx);\n\n if (templates.length > 0) {\n const defaultFormat: FormatGraphqlFn = (source) => {\n const ast = parseGraphql(source, { noLocation: false });\n return printGraphql(ast);\n };\n\n const templateEdits = formatTemplatesInSource(templates, sourceCode, defaultFormat);\n\n for (const edit of templateEdits) {\n insertionPoints.push({\n position: edit.start,\n content: edit.newText,\n endPosition: edit.end,\n });\n }\n }\n\n // Apply insertions\n if (insertionPoints.length === 0) {\n return ok({ modified: false, sourceCode });\n }\n\n // Sort in descending order to insert from end to beginning\n // This preserves earlier positions while modifying later parts\n const sortedPoints = [...insertionPoints].sort((a, b) => b.position - a.position);\n\n let result = sourceCode;\n for (const point of sortedPoints) {\n const end = point.endPosition ?? point.position;\n result = result.slice(0, point.position) + point.content + result.slice(end);\n }\n\n return ok({ modified: true, sourceCode: result });\n};\n\n/**\n * Check if a file needs formatting (has unformatted field selections).\n * Useful for pre-commit hooks or CI checks.\n */\nexport const needsFormat = (options: FormatOptions): Result<boolean, FormatError> => {\n const { sourceCode, filePath } = options;\n\n // Parse source code with SWC\n let module: Module;\n try {\n const program = parseSync(sourceCode, {\n syntax: \"typescript\",\n tsx: filePath?.endsWith(\".tsx\") ?? true,\n target: \"es2022\",\n decorators: false,\n dynamicImport: true,\n });\n\n if (program.type !== \"Module\") {\n return err({\n type: \"FormatError\",\n code: \"PARSE_ERROR\",\n message: `Not a module${filePath ? ` (${filePath})` : \"\"}`,\n });\n }\n module = program;\n } catch (cause) {\n return err({\n type: \"FormatError\",\n code: \"PARSE_ERROR\",\n message: `Failed to parse source code${filePath ? ` (${filePath})` : \"\"}`,\n cause,\n });\n }\n\n const spanOffset = module.span.start;\n const converter = createSwcSpanConverter(sourceCode);\n const gqlIdentifiers = collectGqlIdentifiers(module);\n\n if (gqlIdentifiers.size === 0) {\n return ok(false);\n }\n\n let needsFormatting = false;\n\n traverse(module, gqlIdentifiers, (object, _parent, callbackContext) => {\n if (needsFormatting) return; // Early exit\n\n // Skip fragment config objects for needsFormat check (key injection is optional)\n if (callbackContext.isFragmentConfig) return;\n\n const objectStart = converter.byteOffsetToCharIndex(object.span.start - spanOffset);\n if (!hasExistingNewline(sourceCode, objectStart)) {\n needsFormatting = true;\n }\n });\n\n // Check tagged templates\n if (!needsFormatting) {\n const graphqlResult = getGraphqlModule();\n if (graphqlResult.isErr()) {\n return err(graphqlResult.error);\n }\n const { parse: parseGraphql, print: printGraphql } = graphqlResult.value;\n\n const positionCtx: PositionTrackingContext = { spanOffset, converter };\n const templates = walkAndExtract(module as unknown as Node, gqlIdentifiers, positionCtx);\n\n if (templates.length > 0) {\n const defaultFormat: FormatGraphqlFn = (source) => {\n const ast = parseGraphql(source, { noLocation: false });\n return printGraphql(ast);\n };\n\n const templateEdits = formatTemplatesInSource(templates, sourceCode, defaultFormat);\n if (templateEdits.length > 0) {\n needsFormatting = true;\n }\n }\n }\n\n return ok(needsFormatting);\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soda-gql/formatter",
3
- "version": "0.12.4",
3
+ "version": "0.12.6",
4
4
  "description": "GraphQL document formatter for soda-gql",
5
5
  "type": "module",
6
6
  "private": false,
@@ -48,7 +48,7 @@
48
48
  "./package.json": "./package.json"
49
49
  },
50
50
  "dependencies": {
51
- "@soda-gql/common": "0.12.4",
51
+ "@soda-gql/common": "0.12.6",
52
52
  "neverthrow": "^8.1.1"
53
53
  },
54
54
  "devDependencies": {
@@ -59,10 +59,5 @@
59
59
  "@swc/core": "^1.0.0",
60
60
  "graphql": "^16.11.0"
61
61
  },
62
- "optionalDependencies": {},
63
- "peerDependenciesMeta": {
64
- "graphql": {
65
- "optional": true
66
- }
67
- }
62
+ "optionalDependencies": {}
68
63
  }