Files
Genarrative/apps/mobile-shell/scripts/check-expo-export.mjs
T
kdletters 84156e7d86 补齐移动壳Metro导出烟测
新增 Expo iOS 和 Android production bundle 导出检查

将 mobile-shell:export 纳入原生壳统一验收

同步宿主壳方案、能力协议和共享记忆验收口径
2026-06-18 09:55:49 +08:00

97 lines
2.5 KiB
JavaScript

import {spawnSync} from 'node:child_process';
import fs from 'node:fs';
const shellRoot = new URL('../', import.meta.url);
const outputRoot = new URL('../.expo-export-smoke/', import.meta.url);
const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
const platforms = ['android', 'ios'];
function runExpoExport(platform) {
const outputDir = `.expo-export-smoke/${platform}`;
const result = spawnSync(
npmCommand,
[
'exec',
'expo',
'export',
'--',
'--platform',
platform,
'--output-dir',
outputDir,
],
{
cwd: shellRoot,
encoding: 'utf8',
stdio: 'pipe',
},
);
if (result.error) {
throw new Error(
`failed to start Expo ${platform} export smoke: ${result.error.message}`,
);
}
if (result.signal) {
throw new Error(
`Expo ${platform} export smoke was terminated by signal ${result.signal}`,
);
}
if ((result.status ?? 0) !== 0) {
process.stdout.write(result.stdout ?? '');
process.stderr.write(result.stderr ?? '');
process.exit(result.status ?? 1);
}
}
function readMetadata(platform) {
const metadataPath = new URL(`${platform}/metadata.json`, outputRoot);
if (!fs.existsSync(metadataPath)) {
throw new Error(`Expo ${platform} export did not produce metadata.json`);
}
const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8'));
if (metadata.bundler !== 'metro') {
throw new Error(`Expo ${platform} export must use the Metro bundler`);
}
const bundlePath = metadata.fileMetadata?.[platform]?.bundle;
if (typeof bundlePath !== 'string' || bundlePath.length === 0) {
throw new Error(`Expo ${platform} export metadata is missing bundle path`);
}
return bundlePath;
}
function assertBundle(platform, bundlePath) {
const bundleFile = new URL(`${platform}/${bundlePath}`, outputRoot);
if (!fs.existsSync(bundleFile)) {
throw new Error(`Expo ${platform} bundle is missing: ${bundlePath}`);
}
const stats = fs.statSync(bundleFile);
if (stats.size < 100_000) {
throw new Error(`Expo ${platform} bundle is unexpectedly small`);
}
if (!bundlePath.includes(`/static/js/${platform}/AppEntry-`)) {
throw new Error(`Expo ${platform} bundle path does not target AppEntry`);
}
}
fs.rmSync(outputRoot, {recursive: true, force: true});
try {
for (const platform of platforms) {
runExpoExport(platform);
assertBundle(platform, readMetadata(platform));
}
console.log('[mobile-shell:expo-export] OK');
} finally {
fs.rmSync(outputRoot, {recursive: true, force: true});
}