Run the Navara paths without the Cesium SDK, and load it on demand for the features that still build on it

Every Cesium read that a Navara page made was surveyed with the strict acceptance (GEV_QA_CESIUM_TRAP=record). Math on those paths now uses src/geo: screen annotations, weather and wind center readings, cyclone picks, scene data pack polygons and images (new navaraGeometry.js), the awareness subject, the orbit offset of camera verbs, voice frame_overhead, the agent world packet and Directions colours and ground picks. The celestial ring's Sun and Moon come from a bit-identical port of Cesium's Simon1994 model, JulianDate and TEME rotation (src/geo/simon1994.js, julianDate.js, transforms.js); a Cesium globe still applies its ICRF rotation once loaded.

Traffic, Street Level, the Nepal event and locator and geo import build on Cesium's math or entity model on either globe; their enable (or the import) now loads the SDK first through src/app/loadCesium.js, synchronously when it is already there. A production page guards the global so a missed read is reported and starts the load. No module reads Cesium while it loads (scripts/check-cesium-load-time.mjs, run by npm test). Cesium.js is still loaded by index.html here.
This commit is contained in:
German Khokhlov committed 2026-11-04 17:15:21 +04:00
1 parent f87da8523e
commit b8305de14b
58 files changed
+2130 -325

No files matched your search

+179
View File
@@ -0,0 +1,179 @@
// The production build replaces every `cesium` import with the global that
// /cesium/Cesium.js defines, and that script loads only when a Cesium globe
// first starts (src/app/loadCesium.js). A module that reads a Cesium binding
// while it loads would therefore throw on a page that starts on Navara. This
// check lists such reads: a reference to a binding imported from `cesium`, or
// to the `Cesium` global, that runs when its module is evaluated (top level,
// class heritage, static members, immediately invoked functions) rather than
// inside a function that runs later.
//
// node scripts/check-cesium-load-time.mjs [--json]
import { readFileSync } from 'node:fs';
import { execFileSync } from 'node:child_process';
import { parse } from '@babel/parser';
const root = new URL('..', import.meta.url);
/** Files under src/ that the app ships (tests and test support excluded). */
export function sourceFiles() {
return execFileSync('git', ['ls-files', 'src'], {
cwd: root,
encoding: 'utf8',
})
.split('\n')
.filter(
(path) =>
/\.(?:js|mjs)$/.test(path) &&
!/\.test\.m?js$/.test(path) &&
!path.startsWith('src/testSupport/') &&
!path.includes('/testing/'),
);
}
const FUNCTION_TYPES = new Set([
'FunctionDeclaration',
'FunctionExpression',
'ArrowFunctionExpression',
'ObjectMethod',
'ClassMethod',
'ClassPrivateMethod',
]);
/** Names a module binds to `cesium` imports. */
function cesiumBindings(program) {
const names = new Set();
for (const node of program.body) {
if (node.type !== 'ImportDeclaration' || node.source.value !== 'cesium')
continue;
for (const specifier of node.specifiers) names.add(specifier.local.name);
}
return names;
}
function isReference(node, parent, key) {
if (node.type !== 'Identifier') return false;
if (!parent) return true;
if (
(parent.type === 'MemberExpression' ||
parent.type === 'OptionalMemberExpression') &&
key === 'property' &&
!parent.computed
)
return false;
if (
(parent.type === 'ObjectProperty' ||
parent.type === 'ClassProperty' ||
parent.type === 'ObjectMethod' ||
parent.type === 'ClassMethod') &&
key === 'key' &&
!parent.computed
)
return false;
if (
parent.type === 'ImportSpecifier' ||
parent.type === 'ImportNamespaceSpecifier' ||
parent.type === 'ImportDefaultSpecifier' ||
parent.type === 'ExportSpecifier'
)
return false;
if (parent.type === 'LabeledStatement' || parent.type === 'BreakStatement')
return false;
return true;
}
/**
* Load-time references in one module. `deferred` is true below a function
* body that runs later; an immediately invoked function and a class's static
* members and heritage run at load and stay not deferred.
*/
export function loadTimeReferences(code, path = '<module>') {
const program = parse(code, {
sourceType: 'module',
plugins: ['classProperties', 'classStaticBlock', 'importAttributes'],
}).program;
const names = cesiumBindings(program);
const found = [];
// A re-export from `cesium` becomes a read of the global's property when
// the module loads.
for (const node of program.body)
if (
(node.type === 'ExportNamedDeclaration' ||
node.type === 'ExportAllDeclaration') &&
node.source?.value === 'cesium'
)
found.push({
path,
line: node.loc.start.line,
name: 'export from cesium',
});
const hasGlobal = /\bCesium\b/.test(code);
if (!names.size && !hasGlobal) return found;
// A `Cesium` global reference counts when the module does not bind it.
const watched = (name) =>
names.has(name) || (name === 'Cesium' && !names.has('Cesium'));
const visit = (node, parent, key, deferred) => {
if (!node || typeof node.type !== 'string') return;
if (node.type === 'ImportDeclaration') return;
if (!deferred && isReference(node, parent, key) && watched(node.name))
found.push({ path, line: node.loc.start.line, name: node.name });
let childDeferred = deferred;
if (FUNCTION_TYPES.has(node.type)) {
const invoked =
parent &&
(parent.type === 'CallExpression' || parent.type === 'NewExpression') &&
key === 'callee';
childDeferred = deferred || !invoked;
}
if (
(node.type === 'ClassProperty' || node.type === 'ClassPrivateProperty') &&
!node.static
)
childDeferred = true;
for (const [childKey, value] of Object.entries(node)) {
if (
childKey === 'loc' ||
childKey === 'leadingComments' ||
childKey === 'trailingComments' ||
childKey === 'innerComments'
)
continue;
// Computed keys and parameter defaults of a deferred function: its key
// is evaluated with the enclosing scope, its body later.
const keyDeferred =
FUNCTION_TYPES.has(node.type) && childKey === 'key'
? deferred
: childDeferred;
if (Array.isArray(value))
for (const child of value) visit(child, node, childKey, keyDeferred);
else if (value && typeof value === 'object')
visit(value, node, childKey, keyDeferred);
}
};
visit(program, null, null, false);
return found;
}
if (import.meta.url === `file://${process.argv[1]}`) {
const found = [];
for (const path of sourceFiles()) {
const code = readFileSync(new URL(path, root), 'utf8');
try {
found.push(...loadTimeReferences(code, path));
} catch (error) {
console.error(`${path}: ${error.message}`);
process.exitCode = 1;
}
}
if (process.argv.includes('--json'))
console.log(JSON.stringify(found, null, 2));
else
for (const { path, line, name } of found)
console.log(`${path}:${line} reads ${name} while the module loads`);
if (found.length) {
console.error(
`${found.length} load-time Cesium read(s); a page that starts on Navara has no Cesium global yet.`,
);
process.exitCode = 1;
} else console.log('No module reads Cesium while it loads.');
}
+42
View File
@@ -0,0 +1,42 @@
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import test from 'node:test';
import { loadTimeReferences, sourceFiles } from './check-cesium-load-time.mjs';
const reads = (code) =>
loadTimeReferences(code).map(({ line, name }) => `${line}:${name}`);
test('reads that run while a module loads are found; reads inside later code are not', () => {
assert.deepEqual(
reads(`import * as Cesium from 'cesium';
const color = Cesium.Color.RED;
export function later() { return Cesium.Color.BLUE; }
const lazy = () => Cesium.Math.PI;
class Shape { static axis = Cesium.Axis.Y; field = Cesium.Axis.Z; draw() { return Cesium; } }
(() => Cesium.VERSION)();
export const fallback = (sdk = Cesium) => sdk;`),
['2:Cesium', '5:Cesium', '6:Cesium'],
);
assert.deepEqual(
reads(`import { Cartesian3 as C3, Color } from 'cesium';
export const ORIGIN = C3.ZERO;
export function white() { return Color.WHITE; }`),
['2:C3'],
);
assert.deepEqual(reads(`export { Axis } from 'cesium';`), [
'1:export from cesium',
]);
assert.deepEqual(
reads(`window.__CESIUM__ = Cesium;`),
['1:Cesium'],
'the global counts where the module does not import it',
);
assert.deepEqual(reads(`const geo = { Cesium: 1 }; geo.Cesium;`), []);
});
test('no shipped module reads Cesium while it loads', () => {
const found = sourceFiles().flatMap((path) =>
loadTimeReferences(readFileSync(path, 'utf8'), path),
);
assert.deepEqual(found, []);
});
+118 -3
View File
@@ -889,6 +889,8 @@
"src/geo/matrix4.js",
"src/geo/quaternion.js",
"src/geo/transforms.js",
"src/geo/simon1994.js",
"src/geo/julianDate.js",
"src/geo/scaleToGeodeticSurface.js",
"src/geo/check.js",
"src/geo/debug.js",
@@ -1163,6 +1165,8 @@
"src/geo/matrix4.js",
"src/geo/quaternion.js",
"src/geo/transforms.js",
"src/geo/simon1994.js",
"src/geo/julianDate.js",
"src/geo/scaleToGeodeticSurface.js",
"src/geo/check.js",
"src/geo/debug.js",
@@ -1317,6 +1321,8 @@
"src/geo/matrix4.js",
"src/geo/quaternion.js",
"src/geo/transforms.js",
"src/geo/simon1994.js",
"src/geo/julianDate.js",
"src/geo/scaleToGeodeticSurface.js",
"src/geo/check.js",
"src/geo/debug.js",
@@ -1413,6 +1419,8 @@
"src/geo/matrix4.js",
"src/geo/quaternion.js",
"src/geo/transforms.js",
"src/geo/simon1994.js",
"src/geo/julianDate.js",
"src/geo/scaleToGeodeticSurface.js",
"src/geo/check.js",
"src/geo/debug.js",
@@ -1494,6 +1502,8 @@
"src/geo/matrix4.js",
"src/geo/quaternion.js",
"src/geo/transforms.js",
"src/geo/simon1994.js",
"src/geo/julianDate.js",
"src/geo/scaleToGeodeticSurface.js",
"src/geo/check.js",
"src/geo/debug.js",
@@ -1546,6 +1556,8 @@
"src/geo/matrix4.js",
"src/geo/quaternion.js",
"src/geo/transforms.js",
"src/geo/simon1994.js",
"src/geo/julianDate.js",
"src/geo/scaleToGeodeticSurface.js",
"src/geo/check.js",
"src/geo/debug.js",
@@ -1608,6 +1620,8 @@
"src/geo/matrix4.js",
"src/geo/quaternion.js",
"src/geo/transforms.js",
"src/geo/simon1994.js",
"src/geo/julianDate.js",
"src/geo/scaleToGeodeticSurface.js",
"src/geo/check.js",
"src/geo/debug.js",
@@ -1640,6 +1654,8 @@
"src/geo/quaternion.js",
"src/geo/scaleToGeodeticSurface.js",
"src/geo/transforms.js",
"src/geo/simon1994.js",
"src/geo/julianDate.js",
"src/layers/shared/color.js"
],
"external": []
@@ -1717,6 +1733,8 @@
"src/geo/matrix4.js",
"src/geo/quaternion.js",
"src/geo/transforms.js",
"src/geo/simon1994.js",
"src/geo/julianDate.js",
"src/geo/scaleToGeodeticSurface.js",
"src/geo/check.js",
"src/geo/debug.js",
@@ -1774,6 +1792,8 @@
"src/geo/matrix4.js",
"src/geo/quaternion.js",
"src/geo/transforms.js",
"src/geo/simon1994.js",
"src/geo/julianDate.js",
"src/geo/scaleToGeodeticSurface.js",
"src/geo/check.js",
"src/geo/debug.js",
@@ -1834,6 +1854,8 @@
"src/geo/matrix4.js",
"src/geo/quaternion.js",
"src/geo/transforms.js",
"src/geo/simon1994.js",
"src/geo/julianDate.js",
"src/geo/scaleToGeodeticSurface.js",
"src/geo/check.js",
"src/geo/debug.js",
@@ -1906,6 +1928,8 @@
"src/geo/matrix4.js",
"src/geo/quaternion.js",
"src/geo/transforms.js",
"src/geo/simon1994.js",
"src/geo/julianDate.js",
"src/geo/scaleToGeodeticSurface.js",
"src/geo/check.js",
"src/geo/debug.js",
@@ -1972,6 +1996,8 @@
"src/geo/matrix4.js",
"src/geo/quaternion.js",
"src/geo/transforms.js",
"src/geo/simon1994.js",
"src/geo/julianDate.js",
"src/geo/scaleToGeodeticSurface.js",
"src/geo/check.js",
"src/geo/debug.js",
@@ -1984,6 +2010,7 @@
"traffic-layer": {
"exports": ["./layers/traffic"],
"modules": [
"src/layers/shared/color.js",
"src/data/cameraSensitivity.js",
"src/data/flowMatch.js",
"src/data/tomtomTiles.js",
@@ -2002,6 +2029,7 @@
"src/layers/traffic/ingestion.js",
"src/sources/requestDeadline.js",
"src/layers/traffic/lifecycle.js",
"src/app/loadCesium.js",
"src/layers/traffic/model.js",
"src/layers/traffic/policy.js",
"src/layers/traffic/rendering.js",
@@ -2047,6 +2075,8 @@
"src/geo/matrix4.js",
"src/geo/quaternion.js",
"src/geo/transforms.js",
"src/geo/simon1994.js",
"src/geo/julianDate.js",
"src/geo/scaleToGeodeticSurface.js",
"src/geo/check.js",
"src/geo/debug.js",
@@ -2102,6 +2132,8 @@
"src/geo/matrix4.js",
"src/geo/quaternion.js",
"src/geo/transforms.js",
"src/geo/simon1994.js",
"src/geo/julianDate.js",
"src/geo/scaleToGeodeticSurface.js",
"src/geo/check.js",
"src/geo/debug.js",
@@ -2176,6 +2208,8 @@
"src/geo/matrix4.js",
"src/geo/quaternion.js",
"src/geo/transforms.js",
"src/geo/simon1994.js",
"src/geo/julianDate.js",
"src/geo/scaleToGeodeticSurface.js",
"src/geo/check.js",
"src/geo/debug.js",
@@ -2194,6 +2228,7 @@
],
"modules": [
"src/layers/streetLevel/index.js",
"src/app/loadCesium.js",
"src/layers/streetLevel/credits.js",
"src/layers/streetLevel/providers/mapillary/index.js",
"src/layers/streetLevel/viewerHost.js",
@@ -2264,6 +2299,8 @@
"src/geo/matrix4.js",
"src/geo/quaternion.js",
"src/geo/transforms.js",
"src/geo/simon1994.js",
"src/geo/julianDate.js",
"src/geo/scaleToGeodeticSurface.js",
"src/geo/check.js",
"src/geo/debug.js",
@@ -2315,6 +2352,8 @@
"src/geo/matrix4.js",
"src/geo/quaternion.js",
"src/geo/transforms.js",
"src/geo/simon1994.js",
"src/geo/julianDate.js",
"src/geo/scaleToGeodeticSurface.js",
"src/geo/check.js",
"src/geo/debug.js",
@@ -2378,6 +2417,8 @@
"src/geo/matrix4.js",
"src/geo/quaternion.js",
"src/geo/transforms.js",
"src/geo/simon1994.js",
"src/geo/julianDate.js",
"src/geo/scaleToGeodeticSurface.js",
"src/geo/check.js",
"src/geo/debug.js",
@@ -2425,6 +2466,8 @@
"src/geo/matrix4.js",
"src/geo/quaternion.js",
"src/geo/transforms.js",
"src/geo/simon1994.js",
"src/geo/julianDate.js",
"src/geo/scaleToGeodeticSurface.js",
"src/geo/check.js",
"src/geo/debug.js",
@@ -2657,6 +2700,7 @@
"src/app/layers/traffic.js",
"src/app/operations.js",
"src/app/scene.js",
"src/app/loadCesium.js",
"src/sources/sourceSlot.js",
"src/app/sources.js",
"src/app/startupChrome.js",
@@ -2678,6 +2722,7 @@
"src/cctvFocusPolicy.js",
"src/cctvFocusRequest.js",
"src/celestialRing.js",
"src/celestialRing/ephemeris.js",
"src/cockpitCloudEffects.js",
"src/cockpitMath.js",
"src/cockpitTracking.js",
@@ -3646,6 +3691,8 @@
"src/geo/matrix4.js",
"src/geo/quaternion.js",
"src/geo/transforms.js",
"src/geo/simon1994.js",
"src/geo/julianDate.js",
"src/geo/scaleToGeodeticSurface.js",
"src/geo/check.js",
"src/geo/debug.js",
@@ -3721,6 +3768,7 @@
"src/layers/traffic/render/navara.js",
"src/layers/streetLevel/render/navara.js",
"src/scenes/dataPacks/navara.js",
"src/scenes/dataPacks/navaraGeometry.js",
"src/layers/shared/render/navaraStory.js",
"src/layers/bhoteKoshi/render/navara.js",
"src/layers/weather/render/navara.js",
@@ -3845,6 +3893,7 @@
"src/sources/sourceSlot.js",
"src/cctvFocusRequest.js",
"src/celestialRing.js",
"src/celestialRing/ephemeris.js",
"src/cockpitMath.js",
"src/data/aircraftClass.js",
"src/data/aircraftEmergency.js",
@@ -4108,6 +4157,7 @@
"src/layers/traffic/ingestion.js",
"src/sources/requestDeadline.js",
"src/layers/traffic/lifecycle.js",
"src/app/loadCesium.js",
"src/layers/traffic/model.js",
"src/layers/traffic/policy.js",
"src/layers/traffic/rendering.js",
@@ -4431,6 +4481,8 @@
"src/geo/matrix4.js",
"src/geo/quaternion.js",
"src/geo/transforms.js",
"src/geo/simon1994.js",
"src/geo/julianDate.js",
"src/geo/scaleToGeodeticSurface.js",
"src/geo/check.js",
"src/geo/debug.js",
@@ -4552,6 +4604,8 @@
"src/geo/matrix4.js",
"src/geo/quaternion.js",
"src/geo/transforms.js",
"src/geo/simon1994.js",
"src/geo/julianDate.js",
"src/geo/scaleToGeodeticSurface.js",
"src/geo/check.js",
"src/geo/debug.js",
@@ -4817,6 +4871,7 @@
"src/data/distanceFormat.js",
"src/i18n/marker.js",
"src/layers/directions/index.js",
"src/layers/shared/color.js",
"src/data/pointerOwnerNames.js",
"src/layers/directions/source.js",
"src/data/routeSteps.js",
@@ -4833,7 +4888,25 @@
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
"src/ui/unitLabels.js"
"src/ui/unitLabels.js",
"src/geo/julianDate.js",
"src/geo/simon1994.js",
"src/geo/developerError.js",
"src/geo/check.js",
"src/geo/math.js",
"src/geo/cartesian2.js",
"src/geo/cartographic.js",
"src/geo/cartesian3.js",
"src/geo/matrix3.js",
"src/geo/quaternion.js",
"src/geo/matrix4.js",
"src/geo/ellipsoid.js",
"src/geo/transforms.js",
"src/geo/ellipsoidGeodesic.js",
"src/geo/boundingSphere.js",
"src/geo/debug.js",
"src/geo/scaleToGeodeticSurface.js",
"src/geo/index.js"
],
"external": ["cesium"]
},
@@ -5094,6 +5167,7 @@
"src/data/headingDegrees.js",
"src/cyberSonar.js",
"src/celestialRing.js",
"src/celestialRing/ephemeris.js",
"src/data/approxDistance.js",
"src/data/cameraSensitivity.js",
"src/data/contactPlayback.js",
@@ -5203,6 +5277,8 @@
"src/geo/matrix4.js",
"src/geo/quaternion.js",
"src/geo/transforms.js",
"src/geo/simon1994.js",
"src/geo/julianDate.js",
"src/geo/scaleToGeodeticSurface.js",
"src/geo/check.js",
"src/geo/debug.js",
@@ -5273,6 +5349,8 @@
"src/geo/matrix4.js",
"src/geo/quaternion.js",
"src/geo/transforms.js",
"src/geo/simon1994.js",
"src/geo/julianDate.js",
"src/geo/scaleToGeodeticSurface.js",
"src/geo/check.js",
"src/geo/debug.js",
@@ -5429,6 +5507,7 @@
"src/geo/math.js",
"src/sources/httpBody.js",
"src/data/geoFileImport.js",
"src/app/loadCesium.js",
"src/i18n/marker.js",
"src/i18n/index.js",
"src/i18n/locale.js",
@@ -5523,7 +5602,25 @@
"src/tools/globePanel.js",
"src/tools/queries/panelRequest.js",
"src/geoDistance.js",
"src/visualStylePolicy.js"
"src/visualStylePolicy.js",
"src/geo/boundingSphere.js",
"src/geo/cartesian2.js",
"src/geo/cartesian3.js",
"src/geo/cartographic.js",
"src/geo/check.js",
"src/geo/debug.js",
"src/geo/developerError.js",
"src/geo/ellipsoid.js",
"src/geo/ellipsoidGeodesic.js",
"src/geo/index.js",
"src/geo/julianDate.js",
"src/geo/math.js",
"src/geo/matrix3.js",
"src/geo/matrix4.js",
"src/geo/quaternion.js",
"src/geo/scaleToGeodeticSurface.js",
"src/geo/simon1994.js",
"src/geo/transforms.js"
],
"external": ["satellite.js"]
},
@@ -5681,7 +5778,25 @@
"src/contextModePolicy.js",
"src/loadingFeedback.js",
"src/data/installationFeedback.js",
"src/i18n/marker.js"
"src/i18n/marker.js",
"src/geo/boundingSphere.js",
"src/geo/cartesian2.js",
"src/geo/cartesian3.js",
"src/geo/cartographic.js",
"src/geo/check.js",
"src/geo/debug.js",
"src/geo/developerError.js",
"src/geo/ellipsoid.js",
"src/geo/ellipsoidGeodesic.js",
"src/geo/index.js",
"src/geo/julianDate.js",
"src/geo/math.js",
"src/geo/matrix3.js",
"src/geo/matrix4.js",
"src/geo/quaternion.js",
"src/geo/scaleToGeodeticSurface.js",
"src/geo/simon1994.js",
"src/geo/transforms.js"
],
"external": ["cesium"]
}