mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 11:24:00 +00:00
test(qa): W1 baseline and the main-chunk check
scripts/check-main-chunk.mjs (npm run check:main-chunk) finds the main chunk of a build (the Vite manifest's entry, or the module script dist/index.html loads), follows its static imports and fails when any of them carries three or Navara markers; it prints the sizes. Base build: main chunk 2,850,229 bytes (gzip 881,783), no static imports, clean. scripts/qa-w1-baseline.mjs records Cesium mode for the W1 layers (flights, military, local ADS-B, vessels, satellites, launches): counts, five known objects with positions and screen points, JPEGs (path, size, sha256) at the global / New York (JFK) / orbit poses, a tracked object's camera pose after 2 s and what its share link restores. Live sources until the fixture sources of Task 8.
This commit is contained in:
1 parent
d50eaa28af
commit
27e28373d0
5 files changed
+265
-1
No files matched your search
@@ -50,6 +50,7 @@
|
|||||||
"qa:street-level": "node scripts/qa-street-level.mjs",
|
"qa:street-level": "node scripts/qa-street-level.mjs",
|
||||||
"qa:street-level:fixtures": "node scripts/qa-street-level.mjs --fixtures",
|
"qa:street-level:fixtures": "node scripts/qa-street-level.mjs --fixtures",
|
||||||
"check:boundaries": "node scripts/check-import-directions.mjs && node scripts/check-package-boundaries.mjs",
|
"check:boundaries": "node scripts/check-import-directions.mjs && node scripts/check-package-boundaries.mjs",
|
||||||
|
"check:main-chunk": "node scripts/check-main-chunk.mjs",
|
||||||
"qa:transit": "node scripts/qa-transit.mjs",
|
"qa:transit": "node scripts/qa-transit.mjs",
|
||||||
"mcp": "node server/mcp/stdio.js"
|
"mcp": "node server/mcp/stdio.js"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* The build gate for the Cesium-mode bundle: the main chunk (the module
|
||||||
|
* script dist/index.html loads, or the Vite manifest's entry when the build
|
||||||
|
* writes one) and every chunk it imports statically must carry no three or
|
||||||
|
* Navara code; those load only through navaraMode.js's import(). Prints the
|
||||||
|
* sizes (raw and gzip) and fails when a marker is found.
|
||||||
|
*
|
||||||
|
* npm run build && node scripts/check-main-chunk.mjs [--dist=dist] [--json]
|
||||||
|
*/
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
|
import { gzipSync } from 'node:zlib';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
import {
|
||||||
|
entryFromHtml,
|
||||||
|
entryFromManifest,
|
||||||
|
forbiddenMarkersIn,
|
||||||
|
initialChunks,
|
||||||
|
} from '../src/tooling/mainChunk.js';
|
||||||
|
|
||||||
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||||
|
const option = (name) =>
|
||||||
|
process.argv.find((arg) => arg.startsWith(`--${name}=`))?.split('=')[1];
|
||||||
|
const dist = path.resolve(root, option('dist') ?? 'dist');
|
||||||
|
const asJson = process.argv.includes('--json');
|
||||||
|
|
||||||
|
function findEntry() {
|
||||||
|
const manifestPath = path.join(dist, '.vite', 'manifest.json');
|
||||||
|
if (fs.existsSync(manifestPath)) {
|
||||||
|
const entry = entryFromManifest(
|
||||||
|
JSON.parse(fs.readFileSync(manifestPath, 'utf8')),
|
||||||
|
);
|
||||||
|
if (entry) return { entry, via: 'manifest' };
|
||||||
|
}
|
||||||
|
const htmlPath = path.join(dist, 'index.html');
|
||||||
|
if (!fs.existsSync(htmlPath))
|
||||||
|
throw new Error(`No build at ${dist}: run npm run build first`);
|
||||||
|
const entry = entryFromHtml(fs.readFileSync(htmlPath, 'utf8'));
|
||||||
|
if (!entry) throw new Error(`${htmlPath} loads no module script`);
|
||||||
|
return { entry, via: 'index.html' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const read = (file) => {
|
||||||
|
const absolute = path.join(dist, file);
|
||||||
|
return fs.existsSync(absolute) ? fs.readFileSync(absolute, 'utf8') : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const { entry, via } = findEntry();
|
||||||
|
const chunks = initialChunks(entry, read).map((file) => {
|
||||||
|
const code = read(file);
|
||||||
|
if (code == null)
|
||||||
|
return { file, missing: true, bytes: 0, gzipBytes: 0, markers: [] };
|
||||||
|
return {
|
||||||
|
file,
|
||||||
|
bytes: Buffer.byteLength(code),
|
||||||
|
gzipBytes: gzipSync(code).length,
|
||||||
|
markers: forbiddenMarkersIn(code),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const main = chunks[0];
|
||||||
|
const failures = chunks.filter(
|
||||||
|
(chunk) => chunk.missing || chunk.markers.length,
|
||||||
|
);
|
||||||
|
const result = {
|
||||||
|
entry,
|
||||||
|
via,
|
||||||
|
mainBytes: main.bytes,
|
||||||
|
mainGzipBytes: main.gzipBytes,
|
||||||
|
initialBytes: chunks.reduce((sum, chunk) => sum + chunk.bytes, 0),
|
||||||
|
chunks,
|
||||||
|
ok: failures.length === 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (asJson) console.log(JSON.stringify(result, null, 2));
|
||||||
|
else {
|
||||||
|
const kb = (bytes) => `${(bytes / 1000).toFixed(2)} kB`;
|
||||||
|
console.log(`[main-chunk] entry ${entry} (via ${via})`);
|
||||||
|
for (const chunk of chunks)
|
||||||
|
console.log(
|
||||||
|
`[main-chunk] ${chunk.file}: ${kb(chunk.bytes)} (gzip ${kb(chunk.gzipBytes)})` +
|
||||||
|
(chunk.missing ? ' MISSING' : '') +
|
||||||
|
(chunk.markers.length ? ` FORBIDDEN: ${chunk.markers.join(', ')}` : ''),
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
`[main-chunk] main ${main.bytes} bytes (gzip ${main.gzipBytes}); initial chunks ${result.initialBytes} bytes`,
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
result.ok
|
||||||
|
? '[main-chunk] ok: no three or Navara in the main chunk'
|
||||||
|
: '[main-chunk] FAIL',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
process.exitCode = result.ok ? 0 : 1;
|
||||||
@@ -1063,5 +1063,8 @@
|
|||||||
"src/services/groundSnap.test.mjs",
|
"src/services/groundSnap.test.mjs",
|
||||||
"src/sharelinkPose.test.mjs",
|
"src/sharelinkPose.test.mjs",
|
||||||
"src/tooling/navaraAssets.test.mjs",
|
"src/tooling/navaraAssets.test.mjs",
|
||||||
"src/tooling/tileCache.test.mjs"
|
"src/tooling/tileCache.test.mjs",
|
||||||
|
"scripts/check-main-chunk.mjs",
|
||||||
|
"src/tooling/mainChunk.test.mjs",
|
||||||
|
"scripts/qa-w1-baseline.mjs"
|
||||||
]
|
]
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
// The main chunk of a Vite build and what it must not carry: the Cesium-mode
|
||||||
|
// page loads the entry chunk and every chunk it imports statically; Navara and
|
||||||
|
// three belong in the lazy chunk navaraMode.js loads with import(). Pure
|
||||||
|
// helpers for scripts/check-main-chunk.mjs.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Markers that only three or Navara code leaves in a bundle (minification
|
||||||
|
* keeps string literals): three's own warnings name their classes, Navara's
|
||||||
|
* package names, wasm modules and attribution card classes. Plain engine
|
||||||
|
* names ('navara' as an engine kind) are not markers.
|
||||||
|
*/
|
||||||
|
export const FORBIDDEN_MARKERS = Object.freeze([
|
||||||
|
Object.freeze({
|
||||||
|
name: 'three',
|
||||||
|
pattern: /THREE\.(?:WebGLRenderer|GLTFLoader|EXRLoader|Vector\d)/,
|
||||||
|
}),
|
||||||
|
Object.freeze({
|
||||||
|
name: 'navara',
|
||||||
|
pattern: /@navaramap\/|navara_wasm|navara-attr-/,
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
/** The module script a built index.html loads, relative to dist ('assets/…'), or null. */
|
||||||
|
export function entryFromHtml(html) {
|
||||||
|
const scripts = String(html).match(/<script\b[^>]*>/g) ?? [];
|
||||||
|
for (const tag of scripts) {
|
||||||
|
if (!/\btype\s*=\s*["']module["']/.test(tag)) continue;
|
||||||
|
const src = tag.match(/\bsrc\s*=\s*["']([^"']+)["']/);
|
||||||
|
if (src) return src[1].replace(/^\.?\//, '');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The file of the manifest chunk marked isEntry for index.html (or any entry), or null. */
|
||||||
|
export function entryFromManifest(manifest) {
|
||||||
|
const chunks = Object.values(manifest ?? {});
|
||||||
|
const entry =
|
||||||
|
chunks.find((chunk) => chunk.isEntry && chunk.src === 'index.html') ??
|
||||||
|
chunks.find((chunk) => chunk.isEntry);
|
||||||
|
return entry?.file ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sibling chunks a chunk imports statically (`from"./x.js"`, `import"./x.js"`). */
|
||||||
|
export function staticImportsOf(code) {
|
||||||
|
const found = [];
|
||||||
|
const pattern = /(?:\bfrom|\bimport)\s*(["'])\.\/([^"']+\.js)\1/g;
|
||||||
|
for (const match of String(code).matchAll(pattern)) {
|
||||||
|
if (!found.includes(match[2])) found.push(match[2]);
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The entry and every chunk reachable from it by static imports, in
|
||||||
|
* discovery order. `read(file)` returns a chunk's code (file relative to
|
||||||
|
* dist, in the entry's directory).
|
||||||
|
*/
|
||||||
|
export function initialChunks(entry, read) {
|
||||||
|
const directory = entry.includes('/')
|
||||||
|
? entry.slice(0, entry.lastIndexOf('/') + 1)
|
||||||
|
: '';
|
||||||
|
const seen = [entry];
|
||||||
|
for (let index = 0; index < seen.length; index++) {
|
||||||
|
const code = read(seen[index]);
|
||||||
|
if (code == null) continue;
|
||||||
|
for (const name of staticImportsOf(code)) {
|
||||||
|
const file = directory + name;
|
||||||
|
if (!seen.includes(file)) seen.push(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return seen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The names of FORBIDDEN_MARKERS found in code. */
|
||||||
|
export function forbiddenMarkersIn(code) {
|
||||||
|
const text = String(code);
|
||||||
|
return FORBIDDEN_MARKERS.filter(({ pattern }) => pattern.test(text)).map(
|
||||||
|
({ name }) => name,
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import test from 'node:test';
|
||||||
|
import {
|
||||||
|
FORBIDDEN_MARKERS,
|
||||||
|
entryFromHtml,
|
||||||
|
entryFromManifest,
|
||||||
|
forbiddenMarkersIn,
|
||||||
|
initialChunks,
|
||||||
|
staticImportsOf,
|
||||||
|
} from './mainChunk.js';
|
||||||
|
|
||||||
|
test('the entry is the module script the built index.html loads', () => {
|
||||||
|
const html = `<!doctype html><head>
|
||||||
|
<script src="/cesium/Cesium.js"></script>
|
||||||
|
<script type="module" crossorigin src="/assets/index--RGa-KXE.js"></script>
|
||||||
|
<link rel="modulepreload" crossorigin href="/assets/vendor-Ab.js">
|
||||||
|
</head>`;
|
||||||
|
assert.equal(entryFromHtml(html), 'assets/index--RGa-KXE.js');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('an index.html without a module script has no entry', () => {
|
||||||
|
assert.equal(
|
||||||
|
entryFromHtml('<script src="/cesium/Cesium.js"></script>'),
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('the entry of a Vite manifest is the chunk marked isEntry for index.html', () => {
|
||||||
|
const manifest = {
|
||||||
|
'src/app/navaraMode.js': {
|
||||||
|
file: 'assets/navaraMode-B.js',
|
||||||
|
isDynamicEntry: true,
|
||||||
|
},
|
||||||
|
'index.html': {
|
||||||
|
file: 'assets/index-A.js',
|
||||||
|
isEntry: true,
|
||||||
|
src: 'index.html',
|
||||||
|
imports: ['_vendor-C.js'],
|
||||||
|
},
|
||||||
|
'_vendor-C.js': { file: 'assets/vendor-C.js' },
|
||||||
|
};
|
||||||
|
assert.equal(entryFromManifest(manifest), 'assets/index-A.js');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('static imports are found, dynamic imports are not', () => {
|
||||||
|
const code =
|
||||||
|
'import{a as b}from"./vendor-C.js";import"./side-D.js";' +
|
||||||
|
'const x=()=>import("./navaraMode-B.js");' +
|
||||||
|
"import { y } from './spaced-E.js';";
|
||||||
|
assert.deepEqual(staticImportsOf(code), [
|
||||||
|
'vendor-C.js',
|
||||||
|
'side-D.js',
|
||||||
|
'spaced-E.js',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('the initial chunks are the entry and everything it imports statically', () => {
|
||||||
|
const files = {
|
||||||
|
'assets/index-A.js': 'import"./vendor-C.js";import("./lazy-L.js")',
|
||||||
|
'assets/vendor-C.js': 'import{z}from"./deep-D.js"',
|
||||||
|
'assets/deep-D.js': 'import"./vendor-C.js"',
|
||||||
|
'assets/lazy-L.js': 'THREE.WebGLRenderer',
|
||||||
|
};
|
||||||
|
assert.deepEqual(
|
||||||
|
initialChunks('assets/index-A.js', (file) => files[file]),
|
||||||
|
['assets/index-A.js', 'assets/vendor-C.js', 'assets/deep-D.js'],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('three and Navara markers are reported, ordinary engine names are not', () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
forbiddenMarkersIn(
|
||||||
|
'B4=Object.freeze(["cesium","navara"]);FP={cesium:49,navara:80}',
|
||||||
|
),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
assert.deepEqual(
|
||||||
|
forbiddenMarkersIn('console.warn("THREE.WebGLRenderer: Context Lost.")'),
|
||||||
|
['three'],
|
||||||
|
);
|
||||||
|
assert.deepEqual(forbiddenMarkersIn('import "@navaramap/three"'), ['navara']);
|
||||||
|
assert.deepEqual(forbiddenMarkersIn('new URL("navara_wasm_bg.wasm")'), [
|
||||||
|
'navara',
|
||||||
|
]);
|
||||||
|
assert.ok(FORBIDDEN_MARKERS.length >= 2);
|
||||||
|
});
|
||||||
Reference in new issue
Block a user