mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 19:34:01 +00:00
Load the Cesium SDK and its widget styles only when a Cesium globe or a feature that needs it first runs
index.html no longer loads /cesium/Cesium.js and widgets.css: the Cesium scene loads both before it builds its viewer. A cold Navara start requests nothing of the Cesium library again, and qa-strict-cold-chunks asserts that without the known-gap allowance. The strict harness puts an accessor in place of the Cesium global on every page and fails a scenario that reads it before Cesium.js has run; GEV_QA_CESIUM_TRAP=record lists every such read site instead.
This commit is contained in:
1 parent
b8305de14b
commit
f89aee1eac
4 files changed
+93
-26
No files matched your search
+3
-1
@@ -186,7 +186,9 @@ export function createBrowserViteConfig({
|
||||
apiResponseHeadersPlugin(),
|
||||
// Next, so no provider route answers a Host the server does not allow.
|
||||
hostCheckPlugin(),
|
||||
cesium(),
|
||||
// Keep the SDK asset serving/copying and external imports, but let the
|
||||
// scene load the script and stylesheet only when Cesium is requested.
|
||||
{ ...cesium(), transformIndexHtml: undefined },
|
||||
// Application views are Vue single-file components.
|
||||
vue(VUE_PLUGIN_OPTIONS),
|
||||
...plugins,
|
||||
|
||||
@@ -7,13 +7,6 @@
|
||||
// Control: after a pick to the other engine, its code is requested, so the
|
||||
// detection sees what it is looking for.
|
||||
//
|
||||
// Known gap: the production build externalizes Cesium to the global its
|
||||
// index.html script defines, and engine-neutral modules still read that
|
||||
// global when they load, so a cold Navara start loads Cesium.js and its
|
||||
// widget styles too. That row is reported as a known gap (its control is
|
||||
// that the detection does see Cesium.js) instead of failing the run; any
|
||||
// other Cesium request at a cold Navara start still fails it.
|
||||
//
|
||||
// node scripts/qa-strict-cold-chunks.mjs http://127.0.0.1:4208
|
||||
|
||||
import assert from 'node:assert/strict';
|
||||
@@ -46,10 +39,6 @@ const navaraCode = async (url) => {
|
||||
if (!pathname.endsWith('.js') || pathname.startsWith('/cesium/')) return [];
|
||||
return markersOf(url);
|
||||
};
|
||||
const KNOWN_COLD_NAVARA_CESIUM = new Set([
|
||||
'/cesium/Cesium.js',
|
||||
'/cesium/Widgets/widgets.css',
|
||||
]);
|
||||
const results = [];
|
||||
strict.extra.contracts = results;
|
||||
await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
||||
@@ -80,33 +69,23 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
||||
found.push(new URL(url).pathname);
|
||||
return [...new Set(found)];
|
||||
};
|
||||
const loaded = await otherCode(before);
|
||||
const known =
|
||||
start === 'navara'
|
||||
? loaded.filter((path) => KNOWN_COLD_NAVARA_CESIUM.has(path))
|
||||
: [];
|
||||
const row = {
|
||||
start,
|
||||
otherEngine: other,
|
||||
loadedAtColdStart: loaded.filter((path) => !known.includes(path)),
|
||||
knownGap: known,
|
||||
loadedAtColdStart: await otherCode(before),
|
||||
loadedAfterPick: (await otherCode(after)).length,
|
||||
};
|
||||
results.push(row);
|
||||
console.log(
|
||||
row.loadedAtColdStart.length
|
||||
? 'FAIL'
|
||||
: row.knownGap.length
|
||||
? 'KNOWN GAP'
|
||||
: 'PASS',
|
||||
row.loadedAtColdStart.length ? 'FAIL' : 'PASS',
|
||||
JSON.stringify(row),
|
||||
);
|
||||
await p.close();
|
||||
}
|
||||
for (const row of results)
|
||||
assert.ok(
|
||||
row.loadedAfterPick > 0 || row.knownGap.includes('/cesium/Cesium.js'),
|
||||
`control: ${row.otherEngine} code is detected when it loads`,
|
||||
row.loadedAfterPick > 0,
|
||||
`control: picking ${row.otherEngine} loads its code`,
|
||||
);
|
||||
assert.deepEqual(
|
||||
results.filter((row) => row.loadedAtColdStart.length),
|
||||
|
||||
@@ -281,6 +281,7 @@ export async function installStrictNetwork(
|
||||
// requests, and a live answer would still show up as a violation.
|
||||
if (type === 'page' || type === 'iframe')
|
||||
steps.push(intercept(session, type));
|
||||
if (type === 'page') steps.push(trapCesiumGlobal(session, ledger));
|
||||
await Promise.all(steps);
|
||||
};
|
||||
const attach = async (sessionId, info, waiting) => {
|
||||
@@ -317,6 +318,78 @@ export async function installStrictNetwork(
|
||||
return ledger;
|
||||
}
|
||||
|
||||
/**
|
||||
* The production build reads every `cesium` import from the global that
|
||||
* /cesium/Cesium.js defines, and the page loads that script only when a
|
||||
* Cesium globe first starts. A read of the global before then throws in the
|
||||
* app (a Navara page has no Cesium at all), wherever the app catches it, so
|
||||
* each page gets an accessor in place of the global: reads before the script
|
||||
* has assigned it are logged with their stack in `ledger.cesiumBeforeLoad`
|
||||
* and answer `undefined`, as production would.
|
||||
*
|
||||
* GEV_QA_CESIUM_TRAP=record (a survey, not a check) answers such a read with
|
||||
* the SDK instead, loaded synchronously on the first one, and logs every
|
||||
* distinct read site until the page itself loads Cesium.js, so one run lists
|
||||
* all the Navara paths that still need Cesium.
|
||||
*/
|
||||
async function trapCesiumGlobal(session, ledger) {
|
||||
const binding = '__gevStrictCesiumRead';
|
||||
const record = process.env.GEV_QA_CESIUM_TRAP === 'record';
|
||||
session.on('Runtime.bindingCalled', (event) => {
|
||||
if (event.name !== binding) return;
|
||||
const stack = String(event.payload).slice(0, 1500);
|
||||
const known = ledger.cesiumBeforeLoad.find((read) => read.stack === stack);
|
||||
if (known) known.count++;
|
||||
else if (ledger.cesiumBeforeLoad.length < (record ? 2000 : 50))
|
||||
ledger.cesiumBeforeLoad.push({ stack, count: 1 });
|
||||
});
|
||||
await session.send('Runtime.enable');
|
||||
await session.send('Runtime.addBinding', { name: binding });
|
||||
await session.send('Page.enable');
|
||||
await session.send('Page.addScriptToEvaluateOnNewDocument', {
|
||||
source: `(() => {
|
||||
if (window.top !== window) return;
|
||||
const record = ${record};
|
||||
const seen = new Set();
|
||||
let sdk;
|
||||
let pageLoaded = false;
|
||||
const report = () => {
|
||||
const limit = Error.stackTraceLimit;
|
||||
Error.stackTraceLimit = 12;
|
||||
const stack = String(new Error('Cesium read before it loaded').stack);
|
||||
Error.stackTraceLimit = limit;
|
||||
const site = stack.split('\\n').slice(2, 6).join('\\n');
|
||||
if (seen.has(site)) return;
|
||||
seen.add(site);
|
||||
window.${binding}?.(stack);
|
||||
};
|
||||
Object.defineProperty(window, 'Cesium', {
|
||||
configurable: true,
|
||||
get() {
|
||||
if (!record) {
|
||||
if (sdk === undefined) report();
|
||||
return sdk;
|
||||
}
|
||||
if (!pageLoaded) {
|
||||
pageLoaded = !!document.querySelector('script[src$="/Cesium.js"]');
|
||||
if (!pageLoaded) report();
|
||||
}
|
||||
if (sdk === undefined) {
|
||||
const request = new XMLHttpRequest();
|
||||
request.open('GET', '/cesium/Cesium.js', false);
|
||||
request.send();
|
||||
(0, eval)(request.responseText);
|
||||
}
|
||||
return sdk;
|
||||
},
|
||||
set(value) {
|
||||
sdk = value;
|
||||
},
|
||||
});
|
||||
})();`,
|
||||
});
|
||||
}
|
||||
|
||||
/** A new, empty network ledger. */
|
||||
export function networkLedger() {
|
||||
return {
|
||||
@@ -336,6 +409,7 @@ export function networkLedger() {
|
||||
},
|
||||
violations: [],
|
||||
setupErrors: [],
|
||||
cesiumBeforeLoad: [],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -681,6 +755,7 @@ export function createStrict({
|
||||
tileCache: ledger.tileCache,
|
||||
violations: ledger.violations,
|
||||
setupErrors: ledger.setupErrors,
|
||||
cesiumBeforeLoad: ledger.cesiumBeforeLoad,
|
||||
fixtures,
|
||||
tileHosts: [...tileHosts],
|
||||
};
|
||||
@@ -714,10 +789,13 @@ export function createStrict({
|
||||
picks: picks.length,
|
||||
requests: ledger.requests,
|
||||
violations: ledger.violations.length,
|
||||
cesiumBeforeLoad: ledger.cesiumBeforeLoad.length,
|
||||
}),
|
||||
);
|
||||
for (const v of ledger.violations.slice(0, 20))
|
||||
console.log('NETWORK VIOLATION', JSON.stringify(v));
|
||||
for (const read of ledger.cesiumBeforeLoad.slice(0, 10))
|
||||
console.log('CESIUM BEFORE LOAD', JSON.stringify(read));
|
||||
if (failed) return;
|
||||
assert.equal(
|
||||
ledger.violations.length,
|
||||
@@ -725,6 +803,11 @@ export function createStrict({
|
||||
'network policy violations: ' +
|
||||
JSON.stringify(ledger.violations.slice(0, 5)),
|
||||
);
|
||||
assert.deepEqual(
|
||||
ledger.cesiumBeforeLoad,
|
||||
[],
|
||||
'the page read the Cesium global before Cesium.js loaded',
|
||||
);
|
||||
assert.deepEqual(missing, [], 'layers without an assertion');
|
||||
assert.ok(
|
||||
picks.some((pick) => pick.via === 'pointer+keyboard'),
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
import { describeError } from './errors.js';
|
||||
import { createSceneEngine } from './globeEngine.js';
|
||||
import { onLocaleChange, t } from '../i18n/index.js';
|
||||
import { loadCesium } from './loadCesium.js';
|
||||
|
||||
/**
|
||||
* Construct the application globe using the caller's local configuration.
|
||||
@@ -139,6 +140,8 @@ async function createRenderer({
|
||||
}
|
||||
if (engineKind !== 'cesium')
|
||||
throw new TypeError(`Unknown globe engine: ${engineKind}`);
|
||||
await loadCesium();
|
||||
signal.throwIfAborted();
|
||||
loaderStatus.textContent = t('Configuring viewer...');
|
||||
// Provider attribution stays visible, including clean-view and recording.
|
||||
const creditContainer = document.createElement('div');
|
||||
|
||||
Reference in new issue
Block a user