{"record":{"id":"8eebdc39e258771a","repo":"koala73/worldmonitor","slug":"no-module-entry-script-in-dashboardpath-run-build-commands","errorCode":null,"errorMessage":"no module entry script in ${dashboardPath} — run: ${BUILD_COMMANDS.dashboard}","messagePattern":"no module entry script in (.+?) — run: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/bundle-budgets.mjs","lineNumber":201,"sourceCode":"\n/**\n * The asset files Vite preloads for the dashboard entry's deferred\n * `import('./App-<hash>.js')`, in preload-list order and including CSS, or\n * null when the entry has no such import (App is then in the initial payload).\n *\n * Vite emits the loader as `P(() => import('./App-x.js')…, __vite__mapDeps([…]))`,\n * where the indices point into one `m.f=[…]` table and the list starts with\n * the imported chunk itself. Anything that breaks that shape throws: a parser\n * that silently measured the wrong list would let the application graph grow\n * unseen.\n */\nexport function deferredDashboardAppDependencies(distDir) {\n  const { dashboardPath, html } = readDashboardHtml(distDir);\n  const entryUrl = /<script\\b[^>]*\\btype=[\"']module[\"'][^>]*\\bsrc=[\"']([^\"']+\\.js(?:[?#][^\"']*)?)[\"']/i\n    .exec(html)?.[1];\n  const entryName = entryUrl ? assetFileNameFromUrl(entryUrl) : null;\n  if (!entryName) {\n    throw new Error(`no module entry script in ${dashboardPath} — run: ${BUILD_COMMANDS.dashboard}`);\n  }\n  const entryPath = join(distDir, 'assets', entryName);\n  let source;\n  try {\n    source = readFileSync(entryPath, 'utf8');\n  } catch (error) {\n    throw new Error(`cannot read dashboard entry ${entryPath}: ${error.message}`);\n  }\n\n  const appImport = /import\\(\\s*[\"']\\.\\/(App-[A-Za-z0-9_-]{8}\\.js)[\"']\\s*\\)/.exec(source);\n  if (!appImport) return null;\n  const appFile = appImport[1];\n  const unsupported = (reason) => new Error(\n    `${entryPath}: ${reason} for the deferred ./${appFile} import — the Vite loader shape changed; `\n    + 'update deferredDashboardAppDependencies in scripts/bundle-budgets.mjs',\n  );\n\n  const afterImport = source.slice(appImport.index + appImport[0].length);","sourceCodeStart":183,"sourceCodeEnd":219,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/scripts/bundle-budgets.mjs#L183-L219","documentation":"deferredDashboardAppDependencies locates the dashboard's `<script type=\"module\" src=...>` entry in dashboard.html to inspect its loader for the deferred App chunk import. If no module entry script tag (or an src outside assets/) is found, it throws with a hint to run the dashboard build. This keeps the preload-dependency measurement anchored to a real production entry chunk.","triggerScenarios":"Calling deferredDashboardAppDependencies on dashboard.html lacking a `<script type=\"module\" src=\".../assets/*.js\">` tag — dev-mode HTML (no hashed module script), HTML where the module script lost its type=\"module\" attribute, or the src URL not under /assets/ so assetFileNameFromUrl returns null.","commonSituations":"Budget check run before the production build; Vite config switched to a legacy/no-module output; someone hand-edited index.html template and removed or altered the module script tag; regex mismatch after changing attribute order/quotes in the template.","solutions":["Run BUILD_COMMANDS.dashboard to regenerate a production dashboard.html with a hashed module entry","Inspect the <script type=\"module\"> tag in dist/dashboard.html and restore it if the template was edited","Ensure the entry src resolves under assets/ (check Vite base/assetsDir) so assetFileNameFromUrl can extract the file name","If the HTML shape legitimately changed, update the regex in deferredDashboardAppDependencies"],"exampleFix":"<!-- before: template missing module entry -->\n<!-- no script tag -->\n<!-- after -->\n<script type=\"module\" src=\"/assets/index-abc123.js\"></script>","handlingStrategy":"validation","validationCode":"const html = readFileSync(join(distDir, 'dashboard.html'), 'utf8');\nif (!/<script\\b[^>]*type=[\"']module[\"'][^>]*src=[\"'][^\"']+\\.js[\"']/i.test(html)) throw new Error('dashboard.html has no module entry script — run the production build');","typeGuard":"const hasModuleEntry = (html) => /<script\\b[^>]*\\btype=[\"']module[\"'][^>]*\\bsrc=[\"'][^\"']+\\.js[\"']/i.test(html);","tryCatchPattern":"try {\n  const deps = deferredDashboardAppDependencies(distDir);\n} catch (e) {\n  if (e.message.includes('no module entry script')) {\n    console.error('Production dashboard build missing module entry; rebuild.');\n    process.exitCode = 1;\n  } else throw e;\n}","preventionTips":["Preserve the `<script type=\"module\" src=\"...\">` tag in the HTML template","Never strip the type=\"module\" attribute or move the entry outside assets/","Rebuild the dashboard after any Vite config or template change","Run budget checks only on fresh production output"],"tags":["build","vite","html","regex"],"backgroundTag":"unexpected-response-shape","analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-09-15T16:44:39.439Z","contentChangedAt":"2026-09-15T16:44:39.439Z","schemaVersion":2},"datasetVersion":"2026-09-15T18:17:12.389Z"}