mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-12 06:12:18 +00:00
The import-direction check now tells dynamic imports apart
(analyzeModule(source, { dynamic: true })) and rejects any static import
or re-export of src/globe/navara/** from outside that directory, so the
Navara engine can only be a lazily loaded chunk of the app.
Navara's prebuilt workers load their wasm by file name next to
themselves, which the bundler cannot see; a build-only Rollup plugin
copies the missing ones (draco decoder, font worker) into assets/ when
the bundle includes Navara, and adds nothing to a build without it.
91 lines
2.8 KiB
JavaScript
91 lines
2.8 KiB
JavaScript
import { parsers } from 'prettier/plugins/babel.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 import() specifiers apart.
|
|
*/
|
|
export function analyzeModule(source, { dynamic = false } = {}) {
|
|
const imports = [];
|
|
const dynamicImports = [];
|
|
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')
|
|
dynamicImports.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);
|
|
}
|
|
}
|
|
walk(parsers.babel.parse(source));
|
|
return {
|
|
imports,
|
|
browser: [...browser],
|
|
...(dynamic && { dynamicImports }),
|
|
};
|
|
}
|
|
|
|
/** Return static imports, literal dynamic imports and re-exports. */
|
|
export function moduleImports(source) {
|
|
return analyzeModule(source).imports;
|
|
}
|