{"record":{"id":"ed7a3d1345d9ab59","repo":"JuliusBrussee/caveman","slug":"caveman-build-computed-source-dependency-is-not-l","errorCode":null,"errorMessage":"caveman build: computed source dependency is not lockable in ${JSON.stringify(path)}","messagePattern":"caveman build: computed source dependency is not lockable in (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/source-graph.ts","lineNumber":285,"sourceCode":"  return child === \"\" || (!isAbsolute(child) && child !== \"..\" &&\n    !child.startsWith(\"../\") && !child.startsWith(\"..\\\\\"));\n}\n\nfunction esmSourceSpecifiers(source: string, path: string): string[] {\n  let imports: ReturnType<typeof parse>[0];\n  try {\n    [imports] = parse(source, path);\n  } catch (error) {\n    throw new Error(\n      `caveman build: source module syntax is not lexable in ${JSON.stringify(path)}`,\n      { cause: error },\n    );\n  }\n  const specifiers: string[] = [];\n  for (const item of imports) {\n    if (item.d === -2) continue; // import.meta is metadata, not a dependency.\n    if (item.n === undefined) {\n      throw new Error(`caveman build: computed source dependency is not lockable in ${JSON.stringify(path)}`);\n    }\n    specifiers.push(item.n);\n  }\n  return specifiers;\n}\n\nfunction typescriptSourceSyntax(\n  source: string,\n  path: string,\n  code: Uint8Array,\n): { lexableSource: string; specifiers: string[] } {\n  if (!TYPESCRIPT_SOURCE_EXTENSIONS.has(extname(path))) {\n    return { lexableSource: source, specifiers: [] };\n  }\n  const masks: Array<{ start: number; end: number }> = [];\n  const specifiers: string[] = [];\n  let precedingExportEnd: number | undefined;\n  for (const match of source.matchAll(TYPESCRIPT_IMPORT_EXPORT_PATTERN)) {","sourceCodeStart":267,"sourceCodeEnd":303,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/agent/src/source-graph.ts#L267-L303","documentation":"Thrown by esmSourceSpecifiers when the lexer reports an import whose specifier string could not be statically determined (item.n === undefined, e.g. dynamic import(importExpr) or import(variable)). The build requires a concrete, lockable specifier for every dependency; computed ones are rejected rather than guessed. import.meta (d === -2) is explicitly exempted as metadata.","triggerScenarios":"specifiers -> esmSourceSpecifiers iterates lexer results and finds a dynamic import whose `n` (specifier) is undefined because the argument is an expression, not a string literal: await import(bases[name]), import(`./x/${id}`), etc.","commonSituations":"Dynamic imports with computed paths (plugin loaders, i18n locale loading, route code-splitting); template-literal import paths; imports from a config map.","solutions":["Rewrite computed dynamic imports to static string literals so they can be locked.","For template literals with a static prefix, enumerate the possible modules in a literal map: const mods = { a: () => import(\"./a.js\"), ... }.","Use static import() entries per candidate instead of one computed path.","If the module is truly runtime-only, load it outside the locked graph (e.g. via a runtime loader) rather than a source dependency."],"exampleFix":"// before\nconst mod = await import(`./locales/${locale}.js`);\n\n// after\nconst loaders = { en: () => import(\"./locales/en.js\"), fr: () => import(\"./locales/fr.js\") };\nconst mod = await loaders[locale]();","handlingStrategy":"validation","validationCode":"// Ensure every dynamic import in source uses a string literal\nfunction hasOnlyLiteralDynamicImports(source) {\n  return !/import\\s*\\(\\s*[^\"']/.test(source);\n}","typeGuard":"null","tryCatchPattern":"try {\n  await graph.expand();\n} catch (e) {\n  if (e instanceof Error && e.message.includes(\"not lockable\")) {\n    console.error(\"Rewrite computed dynamic imports to static string literals or an explicit module map.\");\n  }\n  throw e;\n}","preventionTips":["Never interpolate variables or template expressions into import() paths.","Use explicit loader maps enumerating statically importable modules.","Lint with rules flagging dynamic imports without literal arguments."],"tags":["dynamic-import","module-resolution","build"],"backgroundTag":"missing-dependency","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}