diff --git a/site/build/prepare/body-additions.test.mts b/site/build/prepare/body-additions.test.mts index bbc2c5a791e..8cc801f8e03 100644 --- a/site/build/prepare/body-additions.test.mts +++ b/site/build/prepare/body-additions.test.mts @@ -48,6 +48,14 @@ test('the build reads three named prepared files and the source manifest per con assert.doesNotMatch(source, /prepared\/\*\.json/u); }); +test('each generated glob names a base the dev server accepts', () => { + const source = contextObjectModule([{ id: 'nebula' }], {}) + contextObjectJsonModule([{ id: 'm33' }]); + const bases = [...source.matchAll(/import\.meta\.glob\(.*?\{ base: '([^']*)'/gu)].map(match => match[1]!); + assert.equal(bases.length, 3); + // Vite's dev transform refuses a base that does not start with '/', './' or '../'; the build's own glob plugin takes '..'. + for (const base of bases) assert.match(base, /^(?:\/|\.\/|\.\.\/)/u, `import.meta.glob base '${base}' fails in astro dev.`); +}); + test('asset-origin context resources come from inventories without local prepared bytes', async t => { const root = await mkdtemp(resolve(tmpdir(), 'cssearth-context-assets-')); t.after(() => rm(root, { recursive: true, force: true })); diff --git a/site/build/prepare/prepare-catalog.mts b/site/build/prepare/prepare-catalog.mts index 72256a67027..d5fa67a8c9f 100644 --- a/site/build/prepare/prepare-catalog.mts +++ b/site/build/prepare/prepare-catalog.mts @@ -33,7 +33,8 @@ export function dotCatalogueIds(contexts: readonly { id: string; type?: string } /** Every object shares one folder, so the application globs only the context objects it loads, each by name. Their * prepared resources are a literal map from each inventory (contextObjectAssetUrls), never a module per file: an eager * `?url` glob made every page import all 2,071 files of 29 context objects in dev, 4 s of Safari's module loading. - * The module sits in site/prepared; each glob takes `base: '..'` so its keys stay `../src/objects//...`. */ + * The module sits in site/prepared; each glob takes `base: '../'` so its keys stay `../src/objects//...`. The dev + * server refuses `'..'` (a base starts with `/`, `./` or `../`), though the build takes it. */ export function contextObjectModule(contexts: readonly { id: string; type?: string }[], assets: Readonly>) { const list = (patterns: readonly string[]) => `[${patterns.map(pattern => `'${pattern}'`).join(', ')}]`; const galaxies = dotCatalogueIds(contexts).galaxies; @@ -41,7 +42,7 @@ export function contextObjectModule(contexts: readonly { id: string; type?: stri // The galaxy catalogue's display sample (which of its rows are drawn as dots) comes with the application. (galaxies === undefined ? '' : `import galaxySample from '../../src/objects/${galaxies}/prepared/display-sample.json' with { type: 'json' };\n`) + `export const CONTEXT_GALAXY_SAMPLE: unknown = ${galaxies === undefined ? 'undefined' : 'galaxySample'};\n` + - `export const CONTEXT_OBJECT_DESCRIPTORS: Record = import.meta.glob(${list(contexts.map(({ id }) => `../src/objects/${id}/object.json`))}, { base: '..', eager: true, import: 'default' });\n` + + `export const CONTEXT_OBJECT_DESCRIPTORS: Record = import.meta.glob(${list(contexts.map(({ id }) => `../src/objects/${id}/object.json`))}, { base: '../', eager: true, import: 'default' });\n` + // Every prepared resource stays an independently cached, demand-loaded file; the loaders decide which bank is fetched. `export const CONTEXT_OBJECT_ASSET_URLS: Record = ${JSON.stringify(assets)};\n`; } @@ -55,8 +56,8 @@ export const CONTEXT_OBJECT_PREPARED_FILES = ['datasets', 'datasets', 'presentat export function contextObjectJsonModule(contexts: readonly { id: string }[]) { const files = `{${CONTEXT_OBJECT_PREPARED_FILES.join(',')}}.json`; return '// Generated by site/build/prepare/prepare-catalog.mts for the build only: the prepared files pages read while rendering.\n' + - `export const CONTEXT_OBJECT_PREPARED_JSON: Record = import.meta.glob([${contexts.map(({ id }) => `'../src/objects/${id}/prepared/${files}'`).join(', ')}], { base: '..', eager: true, import: 'default' });\n` + - `export const CONTEXT_OBJECT_SOURCE_MANIFESTS: Record = import.meta.glob([${contexts.map(({ id }) => `'../src/objects/${id}/source/manifest.json'`).join(', ')}], { base: '..', eager: true, import: 'default' });\n`; + `export const CONTEXT_OBJECT_PREPARED_JSON: Record = import.meta.glob([${contexts.map(({ id }) => `'../src/objects/${id}/prepared/${files}'`).join(', ')}], { base: '../', eager: true, import: 'default' });\n` + + `export const CONTEXT_OBJECT_SOURCE_MANIFESTS: Record = import.meta.glob([${contexts.map(({ id }) => `'../src/objects/${id}/source/manifest.json'`).join(', ')}], { base: '../', eager: true, import: 'default' });\n`; } /** A bank a body's dataset shows (a galaxy's image layers, a nebula's volume). Its files are named when it is loaded, not