Files
gods-eye-view/scripts/module-analysis.mjs
T
German Khokhlov 60fb8fee07 Fix the follow-up review findings in import, layers, startup and engines
- KML/KMZ import: a per-file canvas budget and cache for ground overlays,
  limits on rings and vertices of raised polygons, overlays across 360° and
  near the poles, degenerate holes, polar and single-latitude fills,
  absolute overlays at their altitude, overlay order by drawOrder as on
  Cesium, and pins on tiles keep their measured height.
- Points on the surface: OSM Places and Street Level cones share the
  surface sampler and sit on the terrain before they are measured, within
  a sampling budget per pass; Cockpit's contact-dot bank keeps the dot
  atlas, and a failed fleet model loads again after a pause.
- Startup: the loading screen lifts after the share-link view is restored,
  the retry screen names the failed engine and takes focus, and the
  fallback toast stays for 6 s.
- Engines: an engine switch lands a running camera flight at its
  destination (camera.completeFlight on both engines), the sighting card
  leaves focus on the engine picker after a switch, and a Cesium model
  error stays with the instance that failed.
- The MCP panel builds again: its wind-mesh alias resolves to the stub.
- QA: the wildlife switch waits for the camera to rest, GBFS --w2 says it
  needs the dev server, and test fixtures are shared instead of copied.
2026-11-10 01:57:11 +04:00

96 lines
3.0 KiB
JavaScript

import { parsers } from 'prettier/plugins/babel.mjs';
import { vueScripts } from './vue-scripts.mjs';
const browserGlobals = new Set([
'window',
'document',
'navigator',
'HTMLElement',
'HTMLCanvasElement',
'Image',
'Audio',
'localStorage',
'sessionStorage',
'requestAnimationFrame',
'cancelAnimationFrame',
'getComputedStyle',
]);
/**
* Inspect literal module dependencies and browser-platform references without
* executing code. With `dynamic`, also list the static (not import())
* specifiers apart.
*/
export function analyzeModule(source, { dynamic = false, filename = '' } = {}) {
const imports = [];
const staticImports = [];
const browser = new Set();
function walk(node, parent, field) {
if (!node || typeof node !== 'object') return;
if (
[
'ImportDeclaration',
'ExportNamedDeclaration',
'ExportAllDeclaration',
'ImportExpression',
].includes(node.type) &&
node.source
) {
if (node.source.type !== 'StringLiteral')
throw new Error(
'Computed module imports are not allowed in runtime code',
);
imports.push(node.source.value);
if (node.type !== 'ImportExpression')
staticImports.push(node.source.value);
}
if (node.type === 'CallExpression' && node.callee?.name === 'require')
throw new Error('Runtime modules must use ES imports');
if (node.type === 'Identifier' && browserGlobals.has(node.name)) {
const property =
(parent?.type === 'MemberExpression' ||
parent?.type === 'OptionalMemberExpression') &&
field === 'property' &&
!parent.computed;
const key =
['ObjectProperty', 'ObjectMethod', 'ClassMethod'].includes(
parent?.type,
) &&
field === 'key' &&
!parent.computed;
const declaration =
field === 'id' ||
field === 'params' ||
parent?.type?.startsWith('Import');
if (!property && !key && !declaration) browser.add(node.name);
}
if (
['MemberExpression', 'OptionalMemberExpression'].includes(node.type) &&
['globalThis', 'self'].includes(node.object?.name)
) {
const name = node.computed ? node.property?.value : node.property?.name;
if (browserGlobals.has(name)) browser.add(name);
}
for (const [key, value] of Object.entries(node)) {
if (['loc', 'comments', 'tokens', 'extra'].includes(key)) continue;
if (Array.isArray(value))
value.forEach((child) => walk(child, node, key));
else if (value && typeof value === 'object') walk(value, node, key);
}
}
for (const script of filename.endsWith('.vue')
? vueScripts(source, filename)
: [source])
walk(parsers.babel.parse(script));
return {
imports,
browser: [...browser],
...(dynamic && { staticImports }),
};
}
/** Return static imports, literal dynamic imports and re-exports. */
export function moduleImports(source) {
return analyzeModule(source).imports;
}