{"version":3,"file":"thread-chunk3.mjs","sources":["../../src/commands/scan/thread.ts"],"sourcesContent":["import ts from 'typescript';\nimport v8 from 'node:v8';\nimport vm from 'node:vm';\nimport { getSchemaNamesFromConfig } from '@gql.tada/internal';\nimport { findAllCallExpressions } from '@0no-co/graphqlsp/api';\n\nimport type { ProgramContainer, PluginCreateInfo } from '../../ts';\nimport { programFactory } from '../../ts';\nimport { expose } from '../../threads';\nimport { hasGraphQLDocumentCandidate } from '../turbo/scan';\n\nimport type { ScanParams, ScanSignal, RawScanDocument, ScanWarning } from './types';\n\nconst HEAP_SOFT_LIMIT_BYTES = 2_500 * 1_024 * 1_024;\nconst SCAN_MAX_BATCH = 1_000;\n\nfunction shouldScanFile(fileName: string): boolean {\n  if (fileName.endsWith('.d.ts') || fileName.endsWith('.d.mts') || fileName.endsWith('.d.cts')) {\n    return false;\n  }\n  return !/(^|[/\\\\])node_modules([/\\\\]|$)/.test(fileName);\n}\n\nfunction collectModuleImports(\n  sourceFile: ts.SourceFile,\n  resolveModulePath: (importSpecifier: string, fromPath: string) => string | undefined\n): string[] {\n  const imports: string[] = [];\n  for (const node of sourceFile.statements) {\n    if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {\n      const resolved = resolveModulePath(node.moduleSpecifier.text, sourceFile.fileName);\n      if (resolved && shouldScanFile(resolved)) imports.push(resolved);\n    }\n  }\n  return imports;\n}\n\nasync function* _runScan(params: ScanParams): AsyncIterableIterator<ScanSignal> {\n  const schemaNames = getSchemaNamesFromConfig(params.pluginConfig);\n  const factory = programFactory(params);\n\n  const externalFiles = factory.createExternalFiles();\n  if (externalFiles.length) {\n    yield { kind: 'EXTERNAL_WARNING' };\n    await factory.addVirtualFiles(externalFiles);\n  }\n\n  let container = factory.build();\n  let pluginInfo = container.buildPluginInfo(params.pluginConfig);\n  const fileNames = factory.rootFileNames.filter(shouldScanFile);\n\n  yield { kind: 'FILE_COUNT', fileCount: fileNames.length };\n\n  const processSourceFile = (\n    sourceFile: ts.SourceFile,\n    container: ProgramContainer,\n    pluginInfo: PluginCreateInfo\n  ): {\n    filePath: string;\n    documents: RawScanDocument[];\n    imports: string[];\n    warnings: ScanWarning[];\n  } => {\n    let filePath = sourceFile.fileName;\n    const documents: RawScanDocument[] = [];\n    const warnings: ScanWarning[] = [];\n\n    // The full import graph is emitted for every project file (not just those\n    // with documents), so the analysis layer can compute reachability.\n    const imports = collectModuleImports(sourceFile, factory.resolveModulePath.bind(factory));\n\n    if (!hasGraphQLDocumentCandidate(sourceFile)) {\n      return { filePath, documents, imports, warnings };\n    }\n\n    const calls = findAllCallExpressions(sourceFile, pluginInfo, {\n      searchExternal: false,\n      collectFragments: false,\n    }).nodes;\n\n    for (const call of calls) {\n      const callExpression = call.node.parent;\n      if (!ts.isCallExpression(callExpression)) continue;\n\n      const position = container.getSourcePosition(sourceFile, callExpression.getStart());\n      filePath = position.fileName;\n\n      if (!schemaNames.has(call.schema)) {\n        warnings.push({\n          message: call.schema\n            ? `The '${call.schema}' schema is not in the configuration but was referenced by a document.`\n            : schemaNames.size > 1\n              ? 'The document is not for a known schema. Have you re-generated the output file?'\n              : 'Multiple schemas are configured, but the document is not for a specific schema.',\n          file: position.fileName,\n          line: position.line,\n          col: position.col,\n        });\n        continue;\n      }\n\n      documents.push({\n        schemaName: call.schema,\n        document: call.node.text,\n        filePath: position.fileName,\n        line: position.line,\n        col: position.col,\n      });\n    }\n\n    return { filePath, documents, imports, warnings };\n  };\n\n  const isHeapOverSoftLimit = (): boolean => {\n    if (process.memoryUsage().heapUsed < HEAP_SOFT_LIMIT_BYTES) return false;\n    forceGc();\n    return process.memoryUsage().heapUsed >= HEAP_SOFT_LIMIT_BYTES;\n  };\n\n  let filesInBatch = 0;\n\n  for (const fileName of fileNames) {\n    // When heap or batch limit is reached, rotate the program to free source files.\n    if (filesInBatch > 0 && (filesInBatch >= SCAN_MAX_BATCH || isHeapOverSoftLimit())) {\n      container = factory.build();\n      pluginInfo = container.buildPluginInfo(params.pluginConfig);\n      forceGc();\n      filesInBatch = 0;\n    }\n\n    const sourceFile = container.getSourceFile(fileName);\n    if (!sourceFile) continue;\n\n    const { filePath, documents, imports, warnings } = processSourceFile(\n      sourceFile,\n      container,\n      pluginInfo\n    );\n    filesInBatch++;\n\n    yield { kind: 'FILE_SCAN', filePath, documents, imports, warnings };\n  }\n}\n\nlet cachedGc: (() => void) | null | undefined;\n\nfunction forceGc(): void {\n  if (cachedGc === undefined) {\n    const existing = (globalThis as { gc?: () => void }).gc;\n    if (typeof existing === 'function') {\n      cachedGc = existing;\n    } else {\n      try {\n        v8.setFlagsFromString('--expose-gc');\n        cachedGc = vm.runInNewContext('gc') as () => void;\n        v8.setFlagsFromString('--no-expose-gc');\n      } catch {\n        cachedGc = null;\n      }\n    }\n  }\n  if (cachedGc) cachedGc();\n}\n\nexport const runScan = expose(_runScan);\n"],"names":["HEAP_SOFT_LIMIT_BYTES","shouldScanFile","fileName","endsWith","test","cachedGc","forceGc","undefined","existing","globalThis","gc","v8","setFlagsFromString","vm","runInNewContext","runScan","expose","async","_runScan","params","schemaNames","getSchemaNamesFromConfig","pluginConfig","factory","programFactory","externalFiles","createExternalFiles","length","kind","addVirtualFiles","container","build","pluginInfo","buildPluginInfo","fileNames","rootFileNames","filter","fileCount","processSourceFile","sourceFile","filePath","documents","warnings","imports","collectModuleImports","resolveModulePath","node","statements","ts","isImportDeclaration","isStringLiteral","moduleSpecifier","resolved","text","push","bind","hasGraphQLDocumentCandidate","calls","findAllCallExpressions","searchExternal","collectFragments","nodes","call","callExpression","parent","isCallExpression","position","getSourcePosition","getStart","has","schema","message","size","file","line","col","schemaName","document","isHeapOverSoftLimit","process","memoryUsage","heapUsed","filesInBatch","getSourceFile"],"mappings":"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"}