{"record":{"id":"abce95707ca993dc","repo":"asgeirtj/system_prompts_leaks","slug":"rel-split-0-not-found-under-node-module","errorCode":null,"errorMessage":"${rel.split('/')[0]} not found under --node-modules (no ${join(nodeModules, rel)}). In a hoisted monorepo the package's own node_modules is sparse — pass the repo-root node_modules instead.","messagePattern":"(.+?) not found under --node-modules \\(no (.+?)\\)\\. In a hoisted monorepo the package's own node_modules is sparse — pass the repo-root node_modules instead\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"Anthropic/Claude Code/bundled-skills/design-sync/lib/emit.mjs","lineNumber":34,"sourceCode":"} from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport { escapeHtml, IIFE_IMPORT_META_DEFINE, readText } from './common.mjs';\nimport { previewExamples } from './docs.mjs';\n\n// React ≤18 ships UMD; React 19 dropped it, so we bundle our own IIFE.\nexport async function vendorReact({ nodeModules, out }) {\n  // Hoisted monorepos (yarn node-modules linker, npm workspaces) keep react\n  // — or just react-dom, when it's only a peerDependency — in the REPO-ROOT\n  // node_modules; the synced package's own dir is sparse. Fail fast with the\n  // remedy rather than walking up: the rest of the pipeline (esbuild\n  // nodePaths, token/css scrapes) runs against the same root, so healing\n  // only this read would leave the build half-resolved.\n  const readOrRemedy = (rel) => {\n    try {\n      return readFileSync(join(nodeModules, rel), 'utf8');\n    } catch (e) {\n      if (e?.code !== 'ENOENT') throw e;\n      throw new Error(\n        `${rel.split('/')[0]} not found under --node-modules (no ${join(nodeModules, rel)}). ` +\n        'In a hoisted monorepo the package\\'s own node_modules is sparse — pass the repo-root node_modules instead.',\n      );\n    }\n  };\n  const reactPkg = JSON.parse(readOrRemedy('react/package.json'));\n  // Both branches assign under a temp global then `||=`-merge so a host\n  // page's existing React isn't clobbered.\n  const noClobber =\n    ';window.React=window.React||window.__dsReact;' +\n    'window.ReactDOM=window.ReactDOM||window.__dsReactDOM;' +\n    'try{delete window.__dsReact;delete window.__dsReactDOM;}catch(e){}';\n  const reactUmd = join(nodeModules, 'react/umd/react.development.js');\n  if (existsSync(reactUmd)) {\n    writeFileSync(\n      join(out, '_vendor', 'react.js'),\n      ';(function(){var __r=window.React,__rd=window.ReactDOM;' +\n      readFileSync(reactUmd, 'utf8') + '\\n' +","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/asgeirtj/system_prompts_leaks/blob/93c999115b300a6faac567830b0450a5478800cd/Anthropic/Claude Code/bundled-skills/design-sync/lib/emit.mjs#L16-L52","documentation":"vendorReact() in emit.mjs reads react and react-dom files from the path passed via --node-modules using a readOrRemedy helper (line 29). In hoisted monorepos (yarn node-modules linker, npm workspaces), react — or just react-dom when it is only a peerDependency — lives in the repo-root node_modules while the synced package's own node_modules is sparse. The helper catches ENOENT and rethrows with the remedy message rather than walking up the directory tree: the comment at lines 25-28 explains the rest of the pipeline (esbuild nodePaths, token/css scrapes) runs against the same root, so silently healing only this one read would leave the build half-resolved.","triggerScenarios":"Passing --node-modules <package-dir>/node_modules in a hoisted monorepo where react was hoisted to the workspace root. The first readOrRemedy call (react/package.json at line 40, or react-dom/umd/react-dom.development.js at line 53) hits ENOENT and throws the remedy error.","commonSituations":"Yarn workspaces (node-modules linker) or npm workspaces that hoist react to the repo root. Pointing --node-modules at a synced/vendored package directory instead of the repo root. A package that lists react-dom only as a peerDependency, so it is never installed in the package's own node_modules. Running design-sync against a single workspace package path rather than the monorepo root.","solutions":["Pass the repo-root node_modules to --node-modules (e.g. ./node_modules, not ./packages/my-ds/node_modules).","If react is not at the repo root either, install it there: run `npm install react react-dom` (or the workspace-equivalent install) at the monorepo root.","Verify the path resolves: confirm <node-modules>/react/package.json and <node-modules>/react-dom/umd/react-dom.development.js (for React ≤18) exist at the path you pass."],"exampleFix":"# before — points at the sparse package-local node_modules\ndesign-sync ... --node-modules ./packages/design-system/node_modules\n\n# after — points at the hoisted repo-root node_modules\ndesign-sync ... --node-modules ./node_modules","handlingStrategy":"validation","validationCode":"import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\n// Validate the --node-modules path points at a dir that actually\n// contains react BEFORE invoking vendorReact / design-sync.\nfunction validateNodeModules(nodeModules) {\n  const reactPkg = join(nodeModules, 'react', 'package.json');\n  if (!existsSync(reactPkg)) {\n    throw new Error(\n      `react not found under ${nodeModules}. ` +\n      'In a hoisted monorepo, pass the repo-root node_modules.'\n    );\n  }\n  return true;\n}","typeGuard":null,"tryCatchPattern":"// Intentional fail-fast — do NOT catch and walk up the tree.\n// The comment in emit.mjs explains: the rest of the pipeline uses\n// the same root, so healing only this read leaves the build half-\n// resolved. Fix the --node-modules argument instead.","preventionTips":["In a hoisted monorepo, always pass the repo-root node_modules to --node-modules, not a workspace package's own directory.","Before running design-sync, verify with `ls node_modules/react/package.json` at the path you intend to pass.","If react is only a peerDependency of your package, install it at the monorepo root (`npm install -w root react react-dom`) so it lands in the hoisted node_modules.","Script the design-sync invocation from the repo root so the relative --node-modules path always resolves to the hoisted store."],"tags":["monorepo","node-modules","react","build","hoisting","workspaces"],"backgroundTag":null,"analyzedSha":"93c999115b300a6faac567830b0450a5478800cd","analyzedAt":"2026-08-13T00:22:15.267Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}