fix(router): 补回 BinReplenishMonitor 的 lazy 声明,修复全站白屏
现象:生产 https://ck.yydd.ai:8443 打开任意页面都是白屏,连登录页都进不去。 控制台报 `BinReplenishMonitor is not defined`,一个 /api 请求都没发出去。 根因:PR #74 新增「缺货补货监控」时,在 router/index.jsx 的路由表里写了 `element: renderLazyComponent(BinReplenishMonitor)`,但没有像其它 22 个页面那样 在文件顶部补 `const BinReplenishMonitor = lazy(() => import('../pages/BinReplenishMonitor'))`。 该行在模块顶层求值 → 加载即抛 ReferenceError → 整个 React 应用挂掉。 `vite build` 不会报错(未声明标识符在 JS 里是合法语法),所以一路构建通过、部署后白屏。 PR #75 只补了后端的 binReplenishRoutes 引入,前端的这处漏声明一直没人发现。 修复: 1. router/index.jsx 补上 lazy 声明,并留下注释说明「漏一个就是全站白屏」。 2. 新增 frontend/scripts/verify-router-lazy.mjs,静态扫描 router/index.jsx: 凡是 renderLazyComponent(X) 用到的 X,必须在同文件有 `const X = lazy(` 声明, 否则构建直接失败并指出缺少的组件名与行号。已挂到 `npm run build` 前置。 验证: - 该检查对修复前的 origin/main 版本报错并准确指出 `BinReplenishMonitor (router/index.jsx:143)`;对修复后版本通过(23 used / 23 declared)。 - eslint 通过;vite build 成功(4270 modules / 34s)。 - playwright 真渲染:修复前 /login 白屏 + pageerror;修复后 /login 完整渲染登录表单 (123 字、3 个输入框),两个业务路由正常回到登录引导,无致命错误。
This commit is contained in:
parent
03fd3f97f3
commit
9b1ec36c70
@ -7,8 +7,9 @@
|
||||
"test": "node --test test/*.test.mjs",
|
||||
"prebuild": "node ./scripts/write-build-version.mjs",
|
||||
"dev": "node ./scripts/write-build-version.mjs && vite --configLoader native",
|
||||
"build": "vite build && node ./scripts/verify-planning-entry.mjs",
|
||||
"build": "node ./scripts/verify-router-lazy.mjs && vite build && node ./scripts/verify-planning-entry.mjs",
|
||||
"check": "npm run lint && npm test && npm run build",
|
||||
"verify:router-lazy": "node ./scripts/verify-router-lazy.mjs",
|
||||
"verify:public-build": "node ./scripts/verify-public-build.mjs",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
|
||||
70
frontend/scripts/verify-router-lazy.mjs
Normal file
70
frontend/scripts/verify-router-lazy.mjs
Normal file
@ -0,0 +1,70 @@
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import process from 'node:process';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
|
||||
/**
|
||||
* 校验路由表里每个 renderLazyComponent(X) 用到的组件 X 都在同文件里
|
||||
* 用 `const X = lazy(() => import(...))` 声明过。
|
||||
*
|
||||
* 为什么要这个检查(2026-10-22 生产事故):
|
||||
* PR #74 新增「缺货补货监控」时,router/index.jsx 里写了
|
||||
* `element: renderLazyComponent(BinReplenishMonitor)` 却漏了 lazy 声明。
|
||||
* 这行在模块顶层执行 → 加载即抛 `ReferenceError: BinReplenishMonitor is not defined`
|
||||
* → 整个 React 应用挂掉、**全站白屏,连登录页都渲染不出来**。
|
||||
* 而 `vite build` 完全能构建成功(未声明标识符在 JS 里是合法语法),
|
||||
* 所以必须在构建期用静态扫描拦住。
|
||||
*/
|
||||
export async function verifyRouterLazy(routerPath = path.resolve(process.cwd(), 'src/router/index.jsx')) {
|
||||
const source = await readFile(routerPath, 'utf8');
|
||||
|
||||
const used = new Set();
|
||||
for (const match of source.matchAll(/renderLazyComponent\s*\(\s*([A-Za-z_$][\w$]*)/g)) {
|
||||
used.add(match[1]);
|
||||
}
|
||||
|
||||
const declared = new Set();
|
||||
for (const match of source.matchAll(/const\s+([A-Za-z_$][\w$]*)\s*=\s*lazy\s*\(/g)) {
|
||||
declared.add(match[1]);
|
||||
}
|
||||
|
||||
const missing = [...used].filter((name) => !declared.has(name)).sort();
|
||||
|
||||
if (missing.length > 0) {
|
||||
const lines = missing.map((name) => {
|
||||
const idx = source.indexOf(`renderLazyComponent(${name}`);
|
||||
const line = idx === -1 ? '?' : source.slice(0, idx).split('\n').length;
|
||||
return ` - ${name} (router/index.jsx:${line} 使用,缺少 const ${name} = lazy(() => import('...')) 声明)`;
|
||||
});
|
||||
throw new Error(
|
||||
'router/index.jsx 有组件被 renderLazyComponent 使用但未声明为 lazy,' +
|
||||
'这会导致运行时 ReferenceError 并让整站白屏:\n' +
|
||||
lines.join('\n'),
|
||||
);
|
||||
}
|
||||
|
||||
return { routerPath, used: [...used].sort(), declared: [...declared].sort() };
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const routerArg = process.argv[2];
|
||||
const routerPath = routerArg
|
||||
? path.resolve(process.cwd(), routerArg)
|
||||
: path.resolve(process.cwd(), 'src/router/index.jsx');
|
||||
const result = await verifyRouterLazy(routerPath);
|
||||
process.stdout.write(
|
||||
`Verified router lazy declarations: ${result.used.length} used, ${result.declared.length} declared ` +
|
||||
`(${path.relative(process.cwd(), result.routerPath)})\n`,
|
||||
);
|
||||
}
|
||||
|
||||
const isDirectExecution = process.argv[1]
|
||||
? pathToFileURL(path.resolve(process.argv[1])).href === import.meta.url
|
||||
: false;
|
||||
|
||||
if (isDirectExecution) {
|
||||
main().catch((error) => {
|
||||
process.stderr.write(`${error.message}\n`);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
}
|
||||
@ -25,6 +25,10 @@ const DailyInventory = lazy(() => import('../pages/DailyInventory'));
|
||||
const StockSalesAnalysis = lazy(() => import('../pages/StockSalesAnalysis'));
|
||||
const PickingBinSalesAnalysis = lazy(() => import('../pages/PickingBinSalesAnalysis'));
|
||||
const ReturnsInventoryMonitor = lazy(() => import('../pages/ReturnsInventoryWarning/Monitor'));
|
||||
// ⚠️ 每个在下面路由表里被 renderLazyComponent(...) 用到的页面都必须先在这里声明。
|
||||
// 漏一个 = 模块顶层抛 ReferenceError(xxx is not defined)→ 整个应用白屏、连登录页都进不去。
|
||||
// PR #74 就漏了 BinReplenishMonitor,导致生产 10-22 全站白屏(2026-10-22 修复)。
|
||||
const BinReplenishMonitor = lazy(() => import('../pages/BinReplenishMonitor'));
|
||||
|
||||
const lazyLoadingElement = <PageLoadingFallback />;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user