原生壳补齐能力清单门禁
在原生壳方案文档中补齐移动端和桌面端真实能力完整清单 让统一原生壳检查从源码能力清单反查方案文档 防止 HostBridge 能力实现、入口 URL 和文档再次漂移
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -5,6 +5,8 @@ import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
||||
const nativeShellPlanPath =
|
||||
'docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md';
|
||||
|
||||
const productionShellRoots = [
|
||||
'apps/mobile-shell',
|
||||
@@ -89,6 +91,11 @@ const expectedDesktopShellRustFiles = [
|
||||
'webview.rs',
|
||||
'window_state.rs',
|
||||
];
|
||||
const capabilityListMarkers = {
|
||||
desktop: '桌面壳当前真实能力完整清单为',
|
||||
mobile: '移动壳当前通用真实能力完整清单为',
|
||||
mobileIosExtra: '移动壳 iOS 额外真实能力为',
|
||||
};
|
||||
const productionShellExtensions = new Set([
|
||||
'.json',
|
||||
'.mjs',
|
||||
@@ -241,6 +248,95 @@ function assertSameList(actual, expected, label) {
|
||||
}
|
||||
}
|
||||
|
||||
function extractTsStringArray(source, exportName, seen = new Set()) {
|
||||
if (seen.has(exportName)) {
|
||||
throw new Error(`cyclic string array export ${exportName}`);
|
||||
}
|
||||
|
||||
const match = source.match(
|
||||
new RegExp(`export const ${exportName}[^=]*= \\[([\\s\\S]*?)\\](?: as const)?;`),
|
||||
);
|
||||
if (!match) {
|
||||
throw new Error(`unable to read ${exportName}`);
|
||||
}
|
||||
|
||||
const nextSeen = new Set(seen);
|
||||
nextSeen.add(exportName);
|
||||
const entries = [];
|
||||
for (const entry of match[1].matchAll(/\.\.\s*([A-Z0-9_]+)|'([^']+)'/g)) {
|
||||
if (entry[1]) {
|
||||
entries.push(...extractTsStringArray(source, entry[1], nextSeen));
|
||||
} else {
|
||||
entries.push(entry[2]);
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
function extractRustCapabilities(source) {
|
||||
const match = source.match(/fn capabilities\(\)[^{]*\{[\s\S]*?vec!\[([\s\S]*?)\]\s*\}/);
|
||||
if (!match) {
|
||||
throw new Error('unable to read desktop shell capabilities');
|
||||
}
|
||||
|
||||
return [...match[1].matchAll(/"([^"]+)"/g)].map((entry) => entry[1]);
|
||||
}
|
||||
|
||||
function extractDocumentCapabilityList(source, marker) {
|
||||
const markerIndex = source.indexOf(marker);
|
||||
if (markerIndex === -1) {
|
||||
throw new Error(`native shell plan missing ${marker}`);
|
||||
}
|
||||
|
||||
const sentenceEnd = source.indexOf('。', markerIndex);
|
||||
const sentence = source.slice(
|
||||
markerIndex,
|
||||
sentenceEnd === -1 ? undefined : sentenceEnd,
|
||||
);
|
||||
return [...sentence.matchAll(/`([^`]+)`/g)].map((entry) => entry[1]);
|
||||
}
|
||||
|
||||
function assertNativeShellCapabilityPlan() {
|
||||
const planSource = fs.readFileSync(nativeShellPlanPath, 'utf8');
|
||||
const mobileCapabilitySource = fs.readFileSync(
|
||||
'apps/mobile-shell/src/host-bridge/capabilities.ts',
|
||||
'utf8',
|
||||
);
|
||||
const desktopCapabilitySource = fs.readFileSync(
|
||||
'apps/desktop-shell/src-tauri/src/host_bridge/capabilities.rs',
|
||||
'utf8',
|
||||
);
|
||||
const mobileCapabilities = extractTsStringArray(
|
||||
mobileCapabilitySource,
|
||||
'MOBILE_HOST_CAPABILITIES',
|
||||
);
|
||||
const iosMobileCapabilities = extractTsStringArray(
|
||||
mobileCapabilitySource,
|
||||
'IOS_MOBILE_HOST_CAPABILITIES',
|
||||
);
|
||||
const iosExtraCapabilities = iosMobileCapabilities.filter(
|
||||
(capability) => !mobileCapabilities.includes(capability),
|
||||
);
|
||||
const desktopCapabilities = extractRustCapabilities(desktopCapabilitySource);
|
||||
|
||||
assertSameList(
|
||||
extractDocumentCapabilityList(planSource, capabilityListMarkers.mobile),
|
||||
mobileCapabilities,
|
||||
'mobile shell documented common capabilities',
|
||||
);
|
||||
assertSameList(
|
||||
extractDocumentCapabilityList(planSource, capabilityListMarkers.mobileIosExtra),
|
||||
iosExtraCapabilities,
|
||||
'mobile shell documented iOS extra capabilities',
|
||||
);
|
||||
assertSameList(
|
||||
extractDocumentCapabilityList(planSource, capabilityListMarkers.desktop),
|
||||
desktopCapabilities,
|
||||
'desktop shell documented capabilities',
|
||||
);
|
||||
}
|
||||
|
||||
function assertHostBridgeLayerLayout() {
|
||||
const wechatBridgeFiles = fs
|
||||
.readdirSync('miniprogram/host-bridge', { withFileTypes: true })
|
||||
@@ -376,6 +472,9 @@ for (const step of steps) {
|
||||
console.log('[check:native-shells] host-bridge-layer-layout');
|
||||
assertHostBridgeLayerLayout();
|
||||
|
||||
console.log('[check:native-shells] native-shell-capability-plan');
|
||||
assertNativeShellCapabilityPlan();
|
||||
|
||||
console.log('[check:native-shells] production-shell-dev-scaffold-scan');
|
||||
assertNoProductionShellDevScaffoldTerms();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user