Files
qipai/scripts/check-admin-m08-d-r1.mjs
T

74 lines
3.8 KiB
JavaScript

import assert from 'node:assert/strict';
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
import { gzipSync } from 'node:zlib';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
const root = fileURLToPath(new URL('..', import.meta.url));
const read = (path) => readFileSync(join(root, path), 'utf8');
const sourceOnly = process.argv.includes('--source-only');
const distOnly = process.argv.includes('--dist-only');
if (!distOnly) {
const packageJson = JSON.parse(read('admin/package.json'));
assert.ok(packageJson.dependencies['vue-router']);
assert.ok(packageJson.devDependencies['unplugin-vue-components']);
const main = read('admin/src/main.ts');
assert.match(main, /use\(router\)/);
assert.doesNotMatch(main, /ElementPlus|element-plus\/dist\/index\.css/);
const app = read('admin/src/App.vue');
for (const pattern of ['RouterLink', 'RouterView', 'Suspense', 'routeLoadError',
'retryLazyRoute', 'restoreSession', 'qipai:admin-refreshed']) assert.match(app, new RegExp(pattern));
assert.doesNotMatch(app, /import\s+(?!AdminLoginPanel\b)\w+Panel\s+from/);
const router = read('admin/src/router.ts');
for (const path of ['/overview', '/stores', '/orders', '/payments', '/third-party',
'/people', '/cleaning', '/devices', '/apps', '/content', '/franchise', '/system']) {
assert.match(read('admin/src/navigation.mjs'), new RegExp(`path: '${path.replace('/', '\\/')}'`));
}
assert.equal((router.match(/\(\) => import\('/g) || []).length, 12);
assert.match(router, /router\.beforeEach/);
assert.match(router, /isNavigationAllowed/);
const api = read('admin/src/api.ts');
assert.match(api, /let refreshInFlight: Promise<AdminSessionPayload> \| null = null/);
assert.match(api, /if \(!refreshInFlight\)/);
assert.match(api, /refreshInFlight = publicRequest<AdminSessionPayload>\('\/auth\/refresh'/);
assert.match(api, /\.finally\(\(\) => \{ refreshInFlight = null; \}\)/);
assert.match(api, /return refreshInFlight/);
const vite = read('admin/vite.config.ts');
assert.match(vite, /ElementPlusResolver/);
assert.doesNotMatch(vite, /chunkSizeWarningLimit/);
assert.doesNotMatch(vite, /element:\s*\['element-plus'\]/);
const styles = read('admin/src/styles.css');
for (const pattern of ['@media (max-width: 980px)', '@media (max-width: 768px)',
'@media (max-width: 560px)', '.workspace .data-table .el-table__row',
'.workspace .el-dialog', '.session-recovery', '.route-state']) assert.ok(styles.includes(pattern));
}
if (!sourceOnly) {
const dist = join(root, 'admin/dist');
assert.ok(existsSync(join(dist, 'index.html')), 'admin production build is missing');
const assets = join(dist, 'assets');
const javascript = readdirSync(assets).filter((name) => name.endsWith('.js'));
const largest = javascript
.map((name) => ({ name, bytes: statSync(join(assets, name)).size }))
.sort((left, right) => right.bytes - left.bytes)[0];
assert.ok(largest.bytes < 500 * 1024, `chunk ${largest.name} exceeds 500 KiB`);
for (const chunk of ['OperationsOverviewPanel', 'StoresRoomsPanel', 'OrdersPanel',
'PaymentsPanel', 'ThirdPartyPanel', 'MembersStaffPanel', 'CleaningWorkspace',
'DevicesPanel', 'PlatformAppsPanel', 'ContentManagementPanel', 'FranchisePanel',
'LogsSystemPanel']) assert.ok(javascript.some((name) => name.startsWith(`${chunk}-`)), `${chunk} is not lazy`);
const entry = javascript.find((name) => name.startsWith('index-'));
assert.ok(entry);
assert.ok(gzipSync(readFileSync(join(assets, entry))).length < 120 * 1024, 'entry gzip exceeds 120 KiB');
assert.doesNotMatch(read('admin/dist/index.html'), /CleaningWorkspace|StoresRoomsPanel|OrdersPanel/);
console.log(`PASS: M08-D-R1 lazy chunks are below 500 KiB; largest=${largest.name}:${largest.bytes}.`);
} else {
console.log('PASS: M08-D-R1 route, permission, recovery and responsive source gates are present.');
}