mirror of
https://github.com/withastro/astro.git
synced 2024-12-16 21:46:22 -05:00
Refactor prerendering chunk handling (#11245)
This commit is contained in:
parent
68f1d0d13e
commit
e22be22e57
33 changed files with 692 additions and 193 deletions
5
.changeset/three-boxes-sniff.md
Normal file
5
.changeset/three-boxes-sniff.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"astro": patch
|
||||
---
|
||||
|
||||
Refactors prerendering chunk handling to correctly remove unused code during the SSR runtime
|
|
@ -176,6 +176,12 @@ Any tests for `astro build` output should use the main `mocha` tests rather than
|
|||
|
||||
If a test needs to validate what happens on the page after it's loading in the browser, that's a perfect use for E2E dev server tests, i.e. to verify that hot-module reloading works in `astro dev` or that components were client hydrated and are interactive.
|
||||
|
||||
#### Creating tests
|
||||
|
||||
When creating new tests, it's best to reference other existing test files and replicate the same setup. Some other tips include:
|
||||
|
||||
- When re-using a fixture multiple times with different configurations, you should also configure unique `outDir`, `build.client`, and `build.server` values so the build output runtime isn't cached and shared by ESM between test runs.
|
||||
|
||||
### Other useful commands
|
||||
|
||||
```shell
|
||||
|
|
|
@ -113,6 +113,11 @@ export interface BuildInternals {
|
|||
ssrSplitEntryChunks: Map<string, Rollup.OutputChunk>;
|
||||
componentMetadata: SSRResult['componentMetadata'];
|
||||
middlewareEntryPoint?: URL;
|
||||
|
||||
/**
|
||||
* Chunks in the bundle that are only used in prerendering that we can delete later
|
||||
*/
|
||||
prerenderOnlyChunks: Rollup.OutputChunk[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -151,6 +156,7 @@ export function createBuildInternals(): BuildInternals {
|
|||
ssrSplitEntryChunks: new Map(),
|
||||
entryPoints: new Map(),
|
||||
cacheManifestUsed: false,
|
||||
prerenderOnlyChunks: [],
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -57,7 +57,6 @@ export async function collectPagesData(
|
|||
moduleSpecifier: '',
|
||||
styles: [],
|
||||
hoistedScript: undefined,
|
||||
hasSharedModules: false,
|
||||
};
|
||||
|
||||
clearInterval(routeCollectionLogTimeout);
|
||||
|
@ -80,7 +79,6 @@ export async function collectPagesData(
|
|||
moduleSpecifier: '',
|
||||
styles: [],
|
||||
hoistedScript: undefined,
|
||||
hasSharedModules: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,15 @@ export function vitePluginChunks(): VitePlugin {
|
|||
if (id.includes('astro/dist/runtime/server/')) {
|
||||
return 'astro/server';
|
||||
}
|
||||
// Split the Astro runtime into a separate chunk for readability
|
||||
if (id.includes('astro/dist/runtime')) {
|
||||
return 'astro';
|
||||
}
|
||||
// Place `astro/env/setup` import in its own chunk to prevent Rollup's TLA bug
|
||||
// https://github.com/rollup/rollup/issues/4708
|
||||
if (id.includes('astro/dist/env/setup')) {
|
||||
return 'astro/env-setup';
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
|
@ -1,87 +1,105 @@
|
|||
import path from 'node:path';
|
||||
import type { Plugin as VitePlugin } from 'vite';
|
||||
import type { Rollup, Plugin as VitePlugin } from 'vite';
|
||||
import { getPrerenderMetadata } from '../../../prerender/metadata.js';
|
||||
import type { BuildInternals } from '../internal.js';
|
||||
import type { AstroBuildPlugin } from '../plugin.js';
|
||||
import type { StaticBuildOptions } from '../types.js';
|
||||
import { extendManualChunks } from './util.js';
|
||||
import { ASTRO_PAGE_RESOLVED_MODULE_ID } from './plugin-pages.js';
|
||||
import { getPagesFromVirtualModulePageName } from './util.js';
|
||||
|
||||
function vitePluginPrerender(opts: StaticBuildOptions, internals: BuildInternals): VitePlugin {
|
||||
function vitePluginPrerender(internals: BuildInternals): VitePlugin {
|
||||
return {
|
||||
name: 'astro:rollup-plugin-prerender',
|
||||
|
||||
outputOptions(outputOptions) {
|
||||
extendManualChunks(outputOptions, {
|
||||
after(id, meta) {
|
||||
// Split the Astro runtime into a separate chunk for readability
|
||||
if (id.includes('astro/dist/runtime')) {
|
||||
return 'astro';
|
||||
}
|
||||
const pageInfo = internals.pagesByViteID.get(id);
|
||||
let hasSharedModules = false;
|
||||
if (pageInfo) {
|
||||
// prerendered pages should be split into their own chunk
|
||||
// Important: this can't be in the `pages/` directory!
|
||||
if (getPrerenderMetadata(meta.getModuleInfo(id)!)) {
|
||||
const infoMeta = meta.getModuleInfo(id)!;
|
||||
generateBundle(_, bundle) {
|
||||
const moduleIds = this.getModuleIds();
|
||||
for (const id of moduleIds) {
|
||||
const pageInfo = internals.pagesByViteID.get(id);
|
||||
if (!pageInfo) continue;
|
||||
const moduleInfo = this.getModuleInfo(id);
|
||||
if (!moduleInfo) continue;
|
||||
|
||||
// Here, we check if this page is importing modules that are shared among other modules e.g. middleware, other SSR pages, etc.
|
||||
// we loop the modules that the current page imports
|
||||
for (const moduleId of infoMeta.importedIds) {
|
||||
// we retrieve the metadata of the module
|
||||
const moduleMeta = meta.getModuleInfo(moduleId)!;
|
||||
if (
|
||||
// a shared modules should be inside the `src/` folder, at least
|
||||
moduleMeta.id.startsWith(opts.settings.config.srcDir.pathname) &&
|
||||
// and has at least two importers: the current page and something else
|
||||
moduleMeta.importers.length > 1
|
||||
) {
|
||||
// Now, we have to trace back the modules imported and analyze them;
|
||||
// understanding if a module is eventually shared between two pages isn't easy, because a module could
|
||||
// be imported by a page and a component that is eventually imported by a page.
|
||||
//
|
||||
// Given the previous statement, we only check if
|
||||
// - the module is a page, and it's not pre-rendered
|
||||
// - the module is the middleware
|
||||
// If one of these conditions is met, we need a separate chunk
|
||||
for (const importer of moduleMeta.importedIds) {
|
||||
// we don't want to analyze the same module again, so we skip it
|
||||
if (importer !== id) {
|
||||
const importerModuleMeta = meta.getModuleInfo(importer);
|
||||
if (importerModuleMeta) {
|
||||
// if the module is inside the pages
|
||||
if (importerModuleMeta.id.includes('/pages')) {
|
||||
// we check if it's not pre-rendered
|
||||
if (getPrerenderMetadata(importerModuleMeta) === false) {
|
||||
hasSharedModules = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// module isn't an Astro route/page, it could be a middleware
|
||||
else if (importerModuleMeta.id.includes('/middleware')) {
|
||||
hasSharedModules = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const prerender = !!getPrerenderMetadata(moduleInfo);
|
||||
pageInfo.route.prerender = prerender;
|
||||
}
|
||||
|
||||
pageInfo.hasSharedModules = hasSharedModules;
|
||||
pageInfo.route.prerender = true;
|
||||
return 'prerender';
|
||||
}
|
||||
pageInfo.route.prerender = false;
|
||||
// dynamic pages should all go in their own chunk in the pages/* directory
|
||||
return `pages/${path.basename(pageInfo.component)}`;
|
||||
}
|
||||
},
|
||||
});
|
||||
// Find all chunks used in the SSR runtime (that aren't used for prerendering only), then use
|
||||
// the Set to find the inverse, where chunks that are only used for prerendering. It's faster
|
||||
// to compute `internals.prerenderOnlyChunks` this way. The prerendered chunks will be deleted
|
||||
// after we finish prerendering.
|
||||
const nonPrerenderOnlyChunks = getNonPrerenderOnlyChunks(bundle, internals);
|
||||
internals.prerenderOnlyChunks = Object.values(bundle).filter((chunk) => {
|
||||
return chunk.type === 'chunk' && !nonPrerenderOnlyChunks.has(chunk);
|
||||
}) as Rollup.OutputChunk[];
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function getNonPrerenderOnlyChunks(bundle: Rollup.OutputBundle, internals: BuildInternals) {
|
||||
const chunks = Object.values(bundle);
|
||||
|
||||
const prerenderOnlyEntryChunks = new Set<Rollup.OutputChunk>();
|
||||
const nonPrerenderOnlyEntryChunks = new Set<Rollup.OutputChunk>();
|
||||
for (const chunk of chunks) {
|
||||
if (chunk.type === 'chunk' && (chunk.isEntry || chunk.isDynamicEntry)) {
|
||||
// See if this entry chunk is prerendered, if so, skip it
|
||||
if (chunk.facadeModuleId?.startsWith(ASTRO_PAGE_RESOLVED_MODULE_ID)) {
|
||||
const pageDatas = getPagesFromVirtualModulePageName(
|
||||
internals,
|
||||
ASTRO_PAGE_RESOLVED_MODULE_ID,
|
||||
chunk.facadeModuleId
|
||||
);
|
||||
const prerender = pageDatas.every((pageData) => pageData.route.prerender);
|
||||
if (prerender) {
|
||||
prerenderOnlyEntryChunks.add(chunk);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Ideally we should record entries when `functionPerRoute` is enabled, but this breaks some tests
|
||||
// that expect the entrypoint to still exist even if it should be unused.
|
||||
// TODO: Revisit this so we can delete additional unused chunks
|
||||
// else if (chunk.facadeModuleId?.startsWith(RESOLVED_SPLIT_MODULE_ID)) {
|
||||
// const pageDatas = getPagesFromVirtualModulePageName(
|
||||
// internals,
|
||||
// RESOLVED_SPLIT_MODULE_ID,
|
||||
// chunk.facadeModuleId
|
||||
// );
|
||||
// const prerender = pageDatas.every((pageData) => pageData.route.prerender);
|
||||
// if (prerender) {
|
||||
// prerenderOnlyEntryChunks.add(chunk);
|
||||
// continue;
|
||||
// }
|
||||
// }
|
||||
|
||||
nonPrerenderOnlyEntryChunks.add(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
// From the `nonPrerenderedEntryChunks`, we crawl all the imports/dynamicImports to find all
|
||||
// other chunks that are use by the non-prerendered runtime
|
||||
const nonPrerenderOnlyChunks = new Set(nonPrerenderOnlyEntryChunks);
|
||||
for (const chunk of nonPrerenderOnlyChunks) {
|
||||
for (const importFileName of chunk.imports) {
|
||||
const importChunk = bundle[importFileName];
|
||||
if (importChunk?.type === 'chunk') {
|
||||
nonPrerenderOnlyChunks.add(importChunk);
|
||||
}
|
||||
}
|
||||
for (const dynamicImportFileName of chunk.dynamicImports) {
|
||||
const dynamicImportChunk = bundle[dynamicImportFileName];
|
||||
// The main server entry (entry.mjs) may import a prerender-only entry chunk, we skip in this case
|
||||
// to prevent incorrectly marking it as non-prerendered.
|
||||
if (
|
||||
dynamicImportChunk?.type === 'chunk' &&
|
||||
!prerenderOnlyEntryChunks.has(dynamicImportChunk)
|
||||
) {
|
||||
nonPrerenderOnlyChunks.add(dynamicImportChunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nonPrerenderOnlyChunks;
|
||||
}
|
||||
|
||||
export function pluginPrerender(
|
||||
opts: StaticBuildOptions,
|
||||
internals: BuildInternals
|
||||
|
@ -96,7 +114,7 @@ export function pluginPrerender(
|
|||
hooks: {
|
||||
'build:before': () => {
|
||||
return {
|
||||
vitePlugin: vitePluginPrerender(opts, internals),
|
||||
vitePlugin: vitePluginPrerender(internals),
|
||||
};
|
||||
},
|
||||
},
|
||||
|
|
|
@ -27,7 +27,17 @@ function vitePluginSSR(
|
|||
name: '@astrojs/vite-plugin-astro-ssr-server',
|
||||
enforce: 'post',
|
||||
options(opts) {
|
||||
return addRollupInput(opts, [SSR_VIRTUAL_MODULE_ID]);
|
||||
const inputs = new Set<string>();
|
||||
|
||||
for (const pageData of Object.values(options.allPages)) {
|
||||
if (routeIsRedirect(pageData.route)) {
|
||||
continue;
|
||||
}
|
||||
inputs.add(getVirtualModulePageName(ASTRO_PAGE_MODULE_ID, pageData.component));
|
||||
}
|
||||
|
||||
inputs.add(SSR_VIRTUAL_MODULE_ID);
|
||||
return addRollupInput(opts, Array.from(inputs));
|
||||
},
|
||||
resolveId(id) {
|
||||
if (id === SSR_VIRTUAL_MODULE_ID) {
|
||||
|
@ -72,7 +82,6 @@ function vitePluginSSR(
|
|||
contents.push(...ssrCode.contents);
|
||||
return [...imports, ...contents, ...exports].join('\n');
|
||||
}
|
||||
return void 0;
|
||||
},
|
||||
async generateBundle(_opts, bundle) {
|
||||
// Add assets from this SSR chunk as well.
|
||||
|
@ -141,23 +150,20 @@ function vitePluginSSRSplit(
|
|||
adapter: AstroAdapter,
|
||||
options: StaticBuildOptions
|
||||
): VitePlugin {
|
||||
const functionPerRouteEnabled = isFunctionPerRouteEnabled(options.settings.adapter);
|
||||
return {
|
||||
name: '@astrojs/vite-plugin-astro-ssr-split',
|
||||
enforce: 'post',
|
||||
options(opts) {
|
||||
if (functionPerRouteEnabled) {
|
||||
const inputs = new Set<string>();
|
||||
const inputs = new Set<string>();
|
||||
|
||||
for (const pageData of Object.values(options.allPages)) {
|
||||
if (routeIsRedirect(pageData.route)) {
|
||||
continue;
|
||||
}
|
||||
inputs.add(getVirtualModulePageName(SPLIT_MODULE_ID, pageData.component));
|
||||
for (const pageData of Object.values(options.allPages)) {
|
||||
if (routeIsRedirect(pageData.route)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return addRollupInput(opts, Array.from(inputs));
|
||||
inputs.add(getVirtualModulePageName(SPLIT_MODULE_ID, pageData.component));
|
||||
}
|
||||
|
||||
return addRollupInput(opts, Array.from(inputs));
|
||||
},
|
||||
resolveId(id) {
|
||||
if (id.startsWith(SPLIT_MODULE_ID)) {
|
||||
|
@ -185,7 +191,6 @@ function vitePluginSSRSplit(
|
|||
|
||||
return [...imports, ...contents, ...exports].join('\n');
|
||||
}
|
||||
return void 0;
|
||||
},
|
||||
async generateBundle(_opts, bundle) {
|
||||
// Add assets from this SSR chunk as well.
|
||||
|
|
|
@ -2,7 +2,6 @@ import fs from 'node:fs';
|
|||
import path, { extname } from 'node:path';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
import { teardown } from '@astrojs/compiler';
|
||||
import * as eslexer from 'es-module-lexer';
|
||||
import glob from 'fast-glob';
|
||||
import { bgGreen, bgMagenta, black, green } from 'kleur/colors';
|
||||
import * as vite from 'vite';
|
||||
|
@ -156,7 +155,7 @@ export async function staticBuild(
|
|||
case isServerLikeOutput(settings.config): {
|
||||
settings.timer.start('Server generate');
|
||||
await generatePages(opts, internals);
|
||||
await cleanStaticOutput(opts, internals, ssrOutputChunkNames);
|
||||
await cleanStaticOutput(opts, internals);
|
||||
opts.logger.info(null, `\n${bgMagenta(black(' finalizing server assets '))}\n`);
|
||||
await ssrMoveAssets(opts);
|
||||
settings.timer.end('Server generate');
|
||||
|
@ -199,6 +198,8 @@ async function ssrBuild(
|
|||
copyPublicDir: !ssr,
|
||||
rollupOptions: {
|
||||
...viteConfig.build?.rollupOptions,
|
||||
// Setting as `exports-only` allows us to safely delete inputs that are only used during prerendering
|
||||
preserveEntrySignatures: 'exports-only',
|
||||
input: [],
|
||||
output: {
|
||||
hoistTransitiveImports: isContentCache,
|
||||
|
@ -381,65 +382,35 @@ async function runPostBuildHooks(
|
|||
}
|
||||
|
||||
/**
|
||||
* For each statically prerendered page, replace their SSR file with a noop.
|
||||
* This allows us to run the SSR build only once, but still remove dependencies for statically rendered routes.
|
||||
* If a component is shared between a statically rendered route and a SSR route, it will still be included in the SSR build.
|
||||
* Remove chunks that are used for prerendering only
|
||||
*/
|
||||
async function cleanStaticOutput(
|
||||
opts: StaticBuildOptions,
|
||||
internals: BuildInternals,
|
||||
ssrOutputChunkNames: string[]
|
||||
) {
|
||||
const prerenderedFiles = new Set();
|
||||
const onDemandsFiles = new Set();
|
||||
for (const pageData of internals.pagesByKeys.values()) {
|
||||
const { moduleSpecifier } = pageData;
|
||||
const bundleId =
|
||||
internals.pageToBundleMap.get(moduleSpecifier) ??
|
||||
internals.entrySpecifierToBundleMap.get(moduleSpecifier);
|
||||
if (pageData.route.prerender && !pageData.hasSharedModules && !onDemandsFiles.has(bundleId)) {
|
||||
prerenderedFiles.add(bundleId);
|
||||
} else {
|
||||
onDemandsFiles.add(bundleId);
|
||||
// Check if the component was not previously added to the static build by a statically rendered route
|
||||
if (prerenderedFiles.has(bundleId)) {
|
||||
prerenderedFiles.delete(bundleId);
|
||||
}
|
||||
}
|
||||
}
|
||||
async function cleanStaticOutput(opts: StaticBuildOptions, internals: BuildInternals) {
|
||||
const ssr = isServerLikeOutput(opts.settings.config);
|
||||
const out = ssr
|
||||
? opts.settings.config.build.server
|
||||
: getOutDirWithinCwd(opts.settings.config.outDir);
|
||||
// The SSR output chunks for Astro are all .mjs files
|
||||
const files = ssrOutputChunkNames.filter((f) => f.endsWith('.mjs'));
|
||||
|
||||
if (files.length) {
|
||||
await eslexer.init;
|
||||
|
||||
// Cleanup prerendered chunks.
|
||||
// This has to happen AFTER the SSR build runs as a final step, because we need the code in order to generate the pages.
|
||||
// These chunks should only contain prerendering logic, so they are safe to modify.
|
||||
await Promise.all(
|
||||
files.map(async (filename) => {
|
||||
if (!prerenderedFiles.has(filename)) {
|
||||
return;
|
||||
await Promise.all(
|
||||
internals.prerenderOnlyChunks.map(async (chunk) => {
|
||||
const url = new URL(chunk.fileName, out);
|
||||
try {
|
||||
// Entry chunks may be referenced by non-deleted code, so we don't actually delete it
|
||||
// but only empty its content. These chunks should never be executed in practice, but
|
||||
// it should prevent broken import paths if adapters do a secondary bundle.
|
||||
if (chunk.isEntry || chunk.isDynamicEntry) {
|
||||
await fs.promises.writeFile(
|
||||
url,
|
||||
"// Contents removed by Astro as it's used for prerendering only",
|
||||
'utf-8'
|
||||
);
|
||||
} else {
|
||||
await fs.promises.unlink(url);
|
||||
}
|
||||
const url = new URL(filename, out);
|
||||
const text = await fs.promises.readFile(url, { encoding: 'utf8' });
|
||||
const [, exports] = eslexer.parse(text);
|
||||
// Replace exports (only prerendered pages) with a noop
|
||||
let value = 'const noop = () => {};';
|
||||
for (const e of exports) {
|
||||
if (e.n === 'default') value += `\n export default noop;`;
|
||||
else value += `\nexport const ${e.n} = noop;`;
|
||||
}
|
||||
await fs.promises.writeFile(url, value, { encoding: 'utf8' });
|
||||
})
|
||||
);
|
||||
|
||||
removeEmptyDirs(out);
|
||||
}
|
||||
} catch {
|
||||
// Best-effort only. Sometimes some chunks may be deleted by other plugins, like pure CSS chunks,
|
||||
// so they may already not exist.
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
async function cleanServerOutput(
|
||||
|
|
|
@ -29,7 +29,6 @@ export interface PageBuildData {
|
|||
moduleSpecifier: string;
|
||||
hoistedScript: HoistedScriptAsset | undefined;
|
||||
styles: Array<{ depth: number; order: number; sheet: StylesheetAsset }>;
|
||||
hasSharedModules: boolean;
|
||||
}
|
||||
|
||||
export type AllPagesData = Record<ComponentPath, PageBuildData>;
|
||||
|
|
|
@ -14,6 +14,11 @@ describe('Assets Prefix - Static', () => {
|
|||
before(async () => {
|
||||
fixture = await loadFixture({
|
||||
root: './fixtures/astro-assets-prefix/',
|
||||
outDir: './dist/static',
|
||||
build: {
|
||||
client: './dist/static/client',
|
||||
server: './dist/static/server',
|
||||
},
|
||||
});
|
||||
await fixture.build();
|
||||
});
|
||||
|
@ -72,7 +77,10 @@ describe('Assets Prefix - with path prefix', () => {
|
|||
before(async () => {
|
||||
fixture = await loadFixture({
|
||||
root: './fixtures/astro-assets-prefix/',
|
||||
outDir: './dist/server',
|
||||
build: {
|
||||
client: './dist/server/client',
|
||||
server: './dist/server/server',
|
||||
assetsPrefix: '/starting-slash',
|
||||
},
|
||||
});
|
||||
|
@ -97,6 +105,11 @@ describe('Assets Prefix, server', () => {
|
|||
root: './fixtures/astro-assets-prefix/',
|
||||
output: 'server',
|
||||
adapter: testAdapter(),
|
||||
outDir: './dist/server',
|
||||
build: {
|
||||
client: './dist/server/client',
|
||||
server: './dist/server/server',
|
||||
},
|
||||
});
|
||||
await fixture.build();
|
||||
app = await fixture.loadTestAdapterApp();
|
||||
|
@ -154,7 +167,10 @@ describe('Assets Prefix, with path prefix', () => {
|
|||
root: './fixtures/astro-assets-prefix/',
|
||||
output: 'server',
|
||||
adapter: testAdapter(),
|
||||
outDir: './dist/server-path-prefix',
|
||||
build: {
|
||||
client: './dist/server-path-prefix/client',
|
||||
server: './dist/server-path-prefix/server',
|
||||
assetsPrefix: '/starting-slash',
|
||||
},
|
||||
});
|
||||
|
|
|
@ -14,6 +14,11 @@ describe('Astro Scripts before-hydration', () => {
|
|||
before(async () => {
|
||||
fixture = await loadFixture({
|
||||
root: './fixtures/before-hydration/',
|
||||
outDir: './dist/static-integration',
|
||||
build: {
|
||||
client: './dist/static-integration/client',
|
||||
server: './dist/static-integration/server',
|
||||
},
|
||||
integrations: [
|
||||
preact(),
|
||||
{
|
||||
|
@ -68,6 +73,11 @@ describe('Astro Scripts before-hydration', () => {
|
|||
before(async () => {
|
||||
fixture = await loadFixture({
|
||||
root: './fixtures/before-hydration/',
|
||||
outDir: './dist/static-no-integration',
|
||||
build: {
|
||||
client: './dist/static-no-integration/client',
|
||||
server: './dist/static-no-integration/server',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -115,6 +125,11 @@ describe('Astro Scripts before-hydration', () => {
|
|||
root: './fixtures/before-hydration/',
|
||||
output: 'server',
|
||||
adapter: testAdapter(),
|
||||
outDir: './dist/server-integration',
|
||||
build: {
|
||||
client: './dist/server-integration/client',
|
||||
server: './dist/server-integration/server',
|
||||
},
|
||||
integrations: [
|
||||
preact(),
|
||||
{
|
||||
|
@ -153,6 +168,11 @@ describe('Astro Scripts before-hydration', () => {
|
|||
fixture = await loadFixture({
|
||||
root: './fixtures/before-hydration/',
|
||||
output: 'server',
|
||||
outDir: './dist/static-no-integration',
|
||||
build: {
|
||||
client: './dist/static-no-integration/client',
|
||||
server: './dist/static-no-integration/server',
|
||||
},
|
||||
adapter: testAdapter(),
|
||||
});
|
||||
});
|
||||
|
|
|
@ -799,6 +799,11 @@ describe('astro:image', () => {
|
|||
const fixtureWithBase = await loadFixture({
|
||||
root: './fixtures/core-image-ssr/',
|
||||
output: 'server',
|
||||
outDir: './dist/server-base-path',
|
||||
build: {
|
||||
client: './dist/server-base-path/client',
|
||||
server: './dist/server-base-path/server',
|
||||
},
|
||||
adapter: testAdapter(),
|
||||
image: {
|
||||
service: testImageService(),
|
||||
|
@ -1080,6 +1085,11 @@ describe('astro:image', () => {
|
|||
fixture = await loadFixture({
|
||||
root: './fixtures/core-image-ssr/',
|
||||
output: 'server',
|
||||
outDir: './dist/server-dev',
|
||||
build: {
|
||||
client: './dist/server-dev/client',
|
||||
server: './dist/server-dev/server',
|
||||
},
|
||||
adapter: testAdapter(),
|
||||
base: 'some-base',
|
||||
image: {
|
||||
|
@ -1114,6 +1124,11 @@ describe('astro:image', () => {
|
|||
fixture = await loadFixture({
|
||||
root: './fixtures/core-image-ssr/',
|
||||
output: 'server',
|
||||
outDir: './dist/server-prod',
|
||||
build: {
|
||||
client: './dist/server-prod/client',
|
||||
server: './dist/server-prod/server',
|
||||
},
|
||||
adapter: testAdapter(),
|
||||
image: {
|
||||
endpoint: 'astro/assets/endpoint/node',
|
||||
|
@ -1127,6 +1142,7 @@ describe('astro:image', () => {
|
|||
const app = await fixture.loadTestAdapterApp();
|
||||
let request = new Request('http://example.com/');
|
||||
let response = await app.render(request);
|
||||
console.log
|
||||
assert.equal(response.status, 200);
|
||||
const html = await response.text();
|
||||
const $ = cheerio.load(html);
|
||||
|
|
|
@ -15,7 +15,10 @@ describe('Setting inlineStylesheets to never in static output', () => {
|
|||
site: 'https://test.dev/',
|
||||
root: './fixtures/css-inline-stylesheets/',
|
||||
output: 'static',
|
||||
outDir: './dist/static-inline-stylesheets-never',
|
||||
build: {
|
||||
client: './dist/static-inline-stylesheets-never/client',
|
||||
server: './dist/static-inline-stylesheets-never/server',
|
||||
inlineStylesheets: 'never',
|
||||
},
|
||||
});
|
||||
|
@ -53,7 +56,10 @@ describe('Setting inlineStylesheets to never in server output', () => {
|
|||
root: './fixtures/css-inline-stylesheets/',
|
||||
output: 'server',
|
||||
adapter: testAdapter(),
|
||||
outDir: './dist/server-inline-stylesheets-never',
|
||||
build: {
|
||||
client: './dist/server-inline-stylesheets-never/client',
|
||||
server: './dist/server-inline-stylesheets-never/server',
|
||||
inlineStylesheets: 'never',
|
||||
},
|
||||
});
|
||||
|
@ -92,7 +98,10 @@ describe('Setting inlineStylesheets to auto in static output', () => {
|
|||
site: 'https://test.info/',
|
||||
root: './fixtures/css-inline-stylesheets/',
|
||||
output: 'static',
|
||||
outDir: './dist/static-inline-stylesheets-auto',
|
||||
build: {
|
||||
client: './dist/static-inline-stylesheets-auto/client',
|
||||
server: './dist/static-inline-stylesheets-auto/server',
|
||||
inlineStylesheets: 'auto',
|
||||
},
|
||||
vite: {
|
||||
|
@ -137,7 +146,10 @@ describe('Setting inlineStylesheets to auto in server output', () => {
|
|||
root: './fixtures/css-inline-stylesheets/',
|
||||
output: 'server',
|
||||
adapter: testAdapter(),
|
||||
outDir: './dist/server-inline-stylesheets-auto',
|
||||
build: {
|
||||
client: './dist/server-inline-stylesheets-auto/client',
|
||||
server: './dist/server-inline-stylesheets-auto/server',
|
||||
inlineStylesheets: 'auto',
|
||||
},
|
||||
vite: {
|
||||
|
@ -184,7 +196,10 @@ describe('Setting inlineStylesheets to always in static output', () => {
|
|||
site: 'https://test.net/',
|
||||
root: './fixtures/css-inline-stylesheets/',
|
||||
output: 'static',
|
||||
outDir: './dist/static-inline-stylesheets-always',
|
||||
build: {
|
||||
client: './dist/static-inline-stylesheets-always/client',
|
||||
server: './dist/static-inline-stylesheets-always/server',
|
||||
inlineStylesheets: 'always',
|
||||
},
|
||||
});
|
||||
|
@ -221,7 +236,10 @@ describe('Setting inlineStylesheets to always in server output', () => {
|
|||
root: './fixtures/css-inline-stylesheets/',
|
||||
output: 'server',
|
||||
adapter: testAdapter(),
|
||||
outDir: './dist/server-inline-stylesheets-always',
|
||||
build: {
|
||||
client: './dist/server-inline-stylesheets-always/client',
|
||||
server: './dist/server-inline-stylesheets-always/server',
|
||||
inlineStylesheets: 'always',
|
||||
},
|
||||
});
|
||||
|
|
|
@ -59,7 +59,10 @@ describe('Experimental Content Collections cache - inlineStylesheets to never in
|
|||
root: './fixtures/css-inline-stylesheets/',
|
||||
output: 'server',
|
||||
adapter: testAdapter(),
|
||||
outDir: './dist/inline-stylesheets-never',
|
||||
build: {
|
||||
client: './dist/inline-stylesheets-never/client',
|
||||
server: './dist/inline-stylesheets-never/server',
|
||||
inlineStylesheets: 'never',
|
||||
},
|
||||
experimental: {
|
||||
|
@ -103,7 +106,10 @@ describe('Experimental Content Collections cache - inlineStylesheets to auto in
|
|||
site: 'https://test.info/',
|
||||
root: './fixtures/css-inline-stylesheets/',
|
||||
output: 'static',
|
||||
outDir: './dist/inline-stylesheets-auto',
|
||||
build: {
|
||||
client: './dist/inline-stylesheets-auto/client',
|
||||
server: './dist/inline-stylesheets-auto/server',
|
||||
inlineStylesheets: 'auto',
|
||||
},
|
||||
vite: {
|
||||
|
@ -202,7 +208,10 @@ describe('Setting inlineStylesheets to always in server output', () => {
|
|||
root: './fixtures/css-inline-stylesheets/',
|
||||
output: 'server',
|
||||
adapter: testAdapter(),
|
||||
outDir: './dist/inline-stylesheets-always',
|
||||
build: {
|
||||
client: './dist/inline-stylesheets-always/client',
|
||||
server: './dist/inline-stylesheets-always/server',
|
||||
inlineStylesheets: 'always',
|
||||
},
|
||||
experimental: {
|
||||
|
|
10
packages/astro/test/fixtures/ssr-prerender-chunks/astro.config.mjs
vendored
Normal file
10
packages/astro/test/fixtures/ssr-prerender-chunks/astro.config.mjs
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
import serverlessAdapter from '@test/ssr-prerender-chunks-test-adapter';
|
||||
import { defineConfig } from 'astro/config';
|
||||
import react from "@astrojs/react";
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
adapter: serverlessAdapter(),
|
||||
output: 'server',
|
||||
integrations: [react()]
|
||||
})
|
85
packages/astro/test/fixtures/ssr-prerender-chunks/deps/test-adapter/index.js
vendored
Normal file
85
packages/astro/test/fixtures/ssr-prerender-chunks/deps/test-adapter/index.js
vendored
Normal file
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
*
|
||||
* @returns {import('../src/@types/astro').AstroIntegration}
|
||||
*/
|
||||
export default function () {
|
||||
return {
|
||||
name: '@test/ssr-prerender-chunks-test-adapter',
|
||||
hooks: {
|
||||
'astro:config:setup': ({ updateConfig, config }) => {
|
||||
updateConfig({
|
||||
build: {
|
||||
client: config.outDir,
|
||||
server: new URL('./_worker.js/', config.outDir),
|
||||
serverEntry: 'index.js',
|
||||
redirects: false,
|
||||
}
|
||||
});
|
||||
},
|
||||
'astro:config:done': ({ setAdapter }) => {
|
||||
setAdapter({
|
||||
name: '@test/ssr-prerender-chunks-test-adapter',
|
||||
serverEntrypoint: '@test/ssr-prerender-chunks-test-adapter/server.js',
|
||||
exports: ['default'],
|
||||
supportedAstroFeatures: {
|
||||
serverOutput: 'stable',
|
||||
},
|
||||
});
|
||||
},
|
||||
'astro:build:setup': ({ vite, target }) => {
|
||||
if (target === 'server') {
|
||||
vite.resolve ||= {};
|
||||
vite.resolve.alias ||= {};
|
||||
|
||||
const aliases = [
|
||||
{
|
||||
find: 'react-dom/server',
|
||||
replacement: 'react-dom/server.browser',
|
||||
},
|
||||
];
|
||||
|
||||
if (Array.isArray(vite.resolve.alias)) {
|
||||
vite.resolve.alias = [...vite.resolve.alias, ...aliases];
|
||||
} else {
|
||||
for (const alias of aliases) {
|
||||
(vite.resolve.alias)[alias.find] = alias.replacement;
|
||||
}
|
||||
}
|
||||
|
||||
vite.resolve.conditions ||= [];
|
||||
// We need those conditions, previous these conditions where applied at the esbuild step which we removed
|
||||
// https://github.com/withastro/astro/pull/7092
|
||||
vite.resolve.conditions.push('workerd', 'worker');
|
||||
|
||||
vite.ssr ||= {};
|
||||
vite.ssr.target = 'webworker';
|
||||
vite.ssr.noExternal = true;
|
||||
|
||||
vite.build ||= {};
|
||||
vite.build.rollupOptions ||= {};
|
||||
vite.build.rollupOptions.output ||= {};
|
||||
vite.build.rollupOptions.output.banner ||=
|
||||
'globalThis.process ??= {}; globalThis.process.env ??= {};';
|
||||
|
||||
// Cloudflare env is only available per request. This isn't feasible for code that access env vars
|
||||
// in a global way, so we shim their access as `process.env.*`. This is not the recommended way for users to access environment variables. But we'll add this for compatibility for chosen variables. Mainly to support `@astrojs/db`
|
||||
vite.define = {
|
||||
'process.env': 'process.env',
|
||||
...vite.define,
|
||||
};
|
||||
}
|
||||
// we thought that vite config inside `if (target === 'server')` would not apply for client
|
||||
// but it seems like the same `vite` reference is used for both
|
||||
// so we need to reset the previous conflicting setting
|
||||
// in the future we should look into a more robust solution
|
||||
if (target === 'client') {
|
||||
vite.resolve ||= {};
|
||||
vite.resolve.conditions ||= [];
|
||||
vite.resolve.conditions = vite.resolve.conditions.filter(
|
||||
(c) => c !== 'workerd' && c !== 'worker'
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
10
packages/astro/test/fixtures/ssr-prerender-chunks/deps/test-adapter/package.json
vendored
Normal file
10
packages/astro/test/fixtures/ssr-prerender-chunks/deps/test-adapter/package.json
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"name": "@test/ssr-prerender-chunks-test-adapter",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./index.js",
|
||||
"./server.js": "./server.js"
|
||||
}
|
||||
}
|
65
packages/astro/test/fixtures/ssr-prerender-chunks/deps/test-adapter/server.js
vendored
Normal file
65
packages/astro/test/fixtures/ssr-prerender-chunks/deps/test-adapter/server.js
vendored
Normal file
|
@ -0,0 +1,65 @@
|
|||
import { App } from 'astro/app';
|
||||
|
||||
export function createExports(manifest) {
|
||||
const app = new App(manifest);
|
||||
|
||||
const fetch = async (
|
||||
request,
|
||||
env,
|
||||
context
|
||||
) => {
|
||||
const { pathname } = new URL(request.url);
|
||||
|
||||
// static assets fallback, in case default _routes.json is not used
|
||||
if (manifest.assets.has(pathname)) {
|
||||
return env.ASSETS.fetch(request.url.replace(/\.html$/, ''));
|
||||
}
|
||||
|
||||
const routeData = app.match(request);
|
||||
if (!routeData) {
|
||||
// https://developers.cloudflare.com/pages/functions/api-reference/#envassetsfetch
|
||||
const asset = await env.ASSETS.fetch(
|
||||
request.url.replace(/index.html$/, '').replace(/\.html$/, '')
|
||||
);
|
||||
if (asset.status !== 404) {
|
||||
return asset;
|
||||
}
|
||||
}
|
||||
|
||||
Reflect.set(
|
||||
request,
|
||||
Symbol.for('astro.clientAddress'),
|
||||
request.headers.get('cf-connecting-ip')
|
||||
);
|
||||
|
||||
process.env.ASTRO_STUDIO_APP_TOKEN ??= (() => {
|
||||
if (typeof env.ASTRO_STUDIO_APP_TOKEN === 'string') {
|
||||
return env.ASTRO_STUDIO_APP_TOKEN;
|
||||
}
|
||||
})();
|
||||
|
||||
const locals = {
|
||||
runtime: {
|
||||
env: env,
|
||||
cf: request.cf,
|
||||
caches,
|
||||
ctx: {
|
||||
waitUntil: (promise) => context.waitUntil(promise),
|
||||
passThroughOnException: () => context.passThroughOnException(),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const response = await app.render(request, { routeData, locals });
|
||||
|
||||
if (app.setCookieHeaders) {
|
||||
for (const setCookieHeader of app.setCookieHeaders(response)) {
|
||||
response.headers.append('Set-Cookie', setCookieHeader);
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
return { default: { fetch } };
|
||||
}
|
14
packages/astro/test/fixtures/ssr-prerender-chunks/package.json
vendored
Normal file
14
packages/astro/test/fixtures/ssr-prerender-chunks/package.json
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "@test/ssr-prerender-chunks",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@astrojs/react": "workspace:*",
|
||||
"@test/ssr-prerender-chunks-test-adapter": "link:./deps/test-adapter",
|
||||
"@types/react": "^18.2.75",
|
||||
"@types/react-dom": "^18.2.24",
|
||||
"astro": "workspace:*",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
}
|
||||
}
|
26
packages/astro/test/fixtures/ssr-prerender-chunks/src/components/Counter.tsx
vendored
Normal file
26
packages/astro/test/fixtures/ssr-prerender-chunks/src/components/Counter.tsx
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
import React, { useState } from "react";
|
||||
|
||||
const Counter: React.FC = () => {
|
||||
const [count, setCount] = useState<number>(0);
|
||||
|
||||
const increment = () => {
|
||||
setCount((prevCount) => prevCount + 1);
|
||||
};
|
||||
|
||||
const decrement = () => {
|
||||
setCount((prevCount) => prevCount - 1);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>Counter</h2>
|
||||
<div>
|
||||
<button onClick={decrement}>-</button>
|
||||
<span>{count}</span>
|
||||
<button onClick={increment}>+</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Counter;
|
13
packages/astro/test/fixtures/ssr-prerender-chunks/src/pages/index.astro
vendored
Normal file
13
packages/astro/test/fixtures/ssr-prerender-chunks/src/pages/index.astro
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
export const prerender = true;
|
||||
import Counter from "../components/Counter";
|
||||
---
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Static Page</title>
|
||||
</head>
|
||||
<body>
|
||||
<Counter client:load />
|
||||
</body>
|
||||
</html>
|
7
packages/astro/test/fixtures/ssr-prerender-chunks/tsconfig.json
vendored
Normal file
7
packages/astro/test/fixtures/ssr-prerender-chunks/tsconfig.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"extends": "astro/tsconfigs/base",
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "react"
|
||||
}
|
||||
}
|
|
@ -1336,6 +1336,11 @@ describe('[SSR] i18n routing', () => {
|
|||
fixture = await loadFixture({
|
||||
root: './fixtures/i18n-routing-prefix-always/',
|
||||
output: 'server',
|
||||
outDir: './dist/pathname-prefix-always-no-redirect',
|
||||
build: {
|
||||
client: './dist/pathname-prefix-always-no-redirect/client',
|
||||
server: './dist/pathname-prefix-always-no-redirect/server',
|
||||
},
|
||||
adapter: testAdapter(),
|
||||
i18n: {
|
||||
routing: {
|
||||
|
@ -1622,6 +1627,11 @@ describe('[SSR] i18n routing', () => {
|
|||
fixture = await loadFixture({
|
||||
root: './fixtures/i18n-routing/',
|
||||
output: 'server',
|
||||
outDir: './dist/locales-underscore',
|
||||
build: {
|
||||
client: './dist/locales-underscore/client',
|
||||
server: './dist/locales-underscore/server',
|
||||
},
|
||||
adapter: testAdapter(),
|
||||
i18n: {
|
||||
defaultLocale: 'en',
|
||||
|
@ -1891,6 +1901,11 @@ describe('SSR fallback from missing locale index to default locale index', () =>
|
|||
fixture = await loadFixture({
|
||||
root: './fixtures/i18n-routing-prefix-other-locales/',
|
||||
output: 'server',
|
||||
outDir: './dist/missing-locale-to-default',
|
||||
build: {
|
||||
client: './dist/missing-locale-to-default/client',
|
||||
server: './dist/missing-locale-to-default/server',
|
||||
},
|
||||
adapter: testAdapter(),
|
||||
i18n: {
|
||||
defaultLocale: 'en',
|
||||
|
|
|
@ -25,7 +25,14 @@ describe('Hoisted inline scripts in SSR', () => {
|
|||
|
||||
describe('without base path', () => {
|
||||
before(async () => {
|
||||
fixture = await loadFixture(defaultFixtureOptions);
|
||||
fixture = await loadFixture({
|
||||
...defaultFixtureOptions,
|
||||
outDir: './dist/inline-scripts-without-base-path',
|
||||
build: {
|
||||
client: './dist/inline-scripts-without-base-path/client',
|
||||
server: './dist/inline-scripts-without-base-path/server',
|
||||
},
|
||||
});
|
||||
await fixture.build();
|
||||
});
|
||||
|
||||
|
@ -42,6 +49,11 @@ describe('Hoisted inline scripts in SSR', () => {
|
|||
before(async () => {
|
||||
fixture = await loadFixture({
|
||||
...defaultFixtureOptions,
|
||||
outDir: './dist/inline-scripts-with-base-path',
|
||||
build: {
|
||||
client: './dist/inline-scripts-with-base-path/client',
|
||||
server: './dist/inline-scripts-with-base-path/server',
|
||||
},
|
||||
base,
|
||||
});
|
||||
await fixture.build();
|
||||
|
@ -63,6 +75,11 @@ describe('Hoisted external scripts in SSR', () => {
|
|||
before(async () => {
|
||||
fixture = await loadFixture({
|
||||
...defaultFixtureOptions,
|
||||
outDir: './dist/external-scripts-without-base-path',
|
||||
build: {
|
||||
client: './dist/external-scripts-without-base-path/client',
|
||||
server: './dist/external-scripts-without-base-path/server',
|
||||
},
|
||||
vite: {
|
||||
build: {
|
||||
assetsInlineLimit: 0,
|
||||
|
@ -83,6 +100,11 @@ describe('Hoisted external scripts in SSR', () => {
|
|||
before(async () => {
|
||||
fixture = await loadFixture({
|
||||
...defaultFixtureOptions,
|
||||
outDir: './dist/external-scripts-with-base-path',
|
||||
build: {
|
||||
client: './dist/external-scripts-with-base-path/client',
|
||||
server: './dist/external-scripts-with-base-path/server',
|
||||
},
|
||||
vite: {
|
||||
build: {
|
||||
assetsInlineLimit: 0,
|
||||
|
@ -104,14 +126,17 @@ describe('Hoisted external scripts in SSR', () => {
|
|||
before(async () => {
|
||||
fixture = await loadFixture({
|
||||
...defaultFixtureOptions,
|
||||
outDir: './dist/with-assets-prefix',
|
||||
build: {
|
||||
client: './dist/with-assets-prefix/client',
|
||||
server: './dist/with-assets-prefix/server',
|
||||
assetsPrefix: 'https://cdn.example.com',
|
||||
},
|
||||
vite: {
|
||||
build: {
|
||||
assetsInlineLimit: 0,
|
||||
},
|
||||
},
|
||||
build: {
|
||||
assetsPrefix: 'https://cdn.example.com',
|
||||
},
|
||||
});
|
||||
await fixture.build();
|
||||
});
|
||||
|
@ -130,6 +155,11 @@ describe('Hoisted external scripts in SSR', () => {
|
|||
before(async () => {
|
||||
fixture = await loadFixture({
|
||||
...defaultFixtureOptions,
|
||||
outDir: './dist/with-rollup-output-file-names',
|
||||
build: {
|
||||
client: './dist/with-rollup-output-file-names/client',
|
||||
server: './dist/with-rollup-output-file-names/server',
|
||||
},
|
||||
vite: {
|
||||
build: {
|
||||
assetsInlineLimit: 0,
|
||||
|
@ -157,6 +187,11 @@ describe('Hoisted external scripts in SSR', () => {
|
|||
before(async () => {
|
||||
fixture = await loadFixture({
|
||||
...defaultFixtureOptions,
|
||||
outDir: './dist/with-rollup-output-file-names-and-base',
|
||||
build: {
|
||||
client: './dist/with-rollup-output-file-names-and-base/client',
|
||||
server: './dist/with-rollup-output-file-names-and-base/server',
|
||||
},
|
||||
vite: {
|
||||
build: {
|
||||
assetsInlineLimit: 0,
|
||||
|
@ -185,6 +220,12 @@ describe('Hoisted external scripts in SSR', () => {
|
|||
before(async () => {
|
||||
fixture = await loadFixture({
|
||||
...defaultFixtureOptions,
|
||||
outDir: './dist/with-rollup-output-file-names-and-assets-prefix',
|
||||
build: {
|
||||
client: './dist/with-rollup-output-file-names-and-assets-prefix/client',
|
||||
server: './dist/with-rollup-output-file-names-and-assets-prefix/server',
|
||||
assetsPrefix: 'https://cdn.example.com',
|
||||
},
|
||||
vite: {
|
||||
build: {
|
||||
assetsInlineLimit: 0,
|
||||
|
@ -197,9 +238,6 @@ describe('Hoisted external scripts in SSR', () => {
|
|||
},
|
||||
},
|
||||
},
|
||||
build: {
|
||||
assetsPrefix: 'https://cdn.example.com',
|
||||
},
|
||||
});
|
||||
await fixture.build();
|
||||
});
|
||||
|
|
21
packages/astro/test/ssr-prerender-chunks.test.js
Normal file
21
packages/astro/test/ssr-prerender-chunks.test.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
import assert from 'node:assert/strict';
|
||||
import { before, describe, it } from 'node:test';
|
||||
import { loadFixture } from './test-utils.js';
|
||||
|
||||
describe('Chunks', () => {
|
||||
/** @type {import('./test-utils').Fixture} */
|
||||
let fixture;
|
||||
|
||||
before(async () => {
|
||||
fixture = await loadFixture({
|
||||
root: './fixtures/ssr-prerender-chunks/',
|
||||
});
|
||||
await fixture.build();
|
||||
});
|
||||
|
||||
it('does not have wrong chunks', async () => {
|
||||
const content = await fixture.readFile('_worker.js/renderers.mjs');
|
||||
const hasImportFromPrerender = !content.includes(`React } from './chunks/prerender`);
|
||||
assert.ok(hasImportFromPrerender);
|
||||
});
|
||||
});
|
|
@ -12,6 +12,11 @@ describe('SSR: prerender', () => {
|
|||
fixture = await loadFixture({
|
||||
root: './fixtures/ssr-prerender/',
|
||||
output: 'server',
|
||||
outDir: './dist/normal',
|
||||
build: {
|
||||
client: './dist/normal/client',
|
||||
server: './dist/normal/server',
|
||||
},
|
||||
adapter: testAdapter(),
|
||||
});
|
||||
await fixture.build();
|
||||
|
@ -61,7 +66,11 @@ describe('SSR: prerender', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('Integrations can hook into the prerendering decision', () => {
|
||||
// NOTE: This test doesn't make sense as it relies on the fact that on the client build,
|
||||
// you can change the prerender state of pages from the SSR build, however, the client build
|
||||
// is not always guaranteed to run. If we want to support this feature, we may want to only allow
|
||||
// editing `route.prerender` on the `astro:build:done` hook.
|
||||
describe.skip('Integrations can hook into the prerendering decision', () => {
|
||||
/** @type {import('./test-utils').Fixture} */
|
||||
let fixture;
|
||||
|
||||
|
@ -83,6 +92,11 @@ describe('Integrations can hook into the prerendering decision', () => {
|
|||
fixture = await loadFixture({
|
||||
root: './fixtures/ssr-prerender/',
|
||||
output: 'server',
|
||||
outDir: './dist/integration-prerender',
|
||||
build: {
|
||||
client: './dist/integration-prerender/client',
|
||||
server: './dist/integration-prerender/server',
|
||||
},
|
||||
integrations: [testIntegration],
|
||||
adapter: testAdapter(),
|
||||
});
|
||||
|
|
|
@ -216,6 +216,10 @@ export async function loadFixture(inlineConfig) {
|
|||
});
|
||||
}
|
||||
},
|
||||
loadAdapterEntryModule: async () => {
|
||||
const url = new URL(`./server/entry.mjs?id=${fixtureId}`, config.outDir);
|
||||
return await import(url);
|
||||
},
|
||||
loadNodeAdapterHandler: async () => {
|
||||
const url = new URL(`./server/entry.mjs?id=${fixtureId}`, config.outDir);
|
||||
const { handler } = await import(url);
|
||||
|
|
|
@ -9,13 +9,6 @@ import { loadFixture, waitServerListen } from './test-utils.js';
|
|||
* @typedef {import('../../../astro/test/test-utils').Fixture} Fixture
|
||||
*/
|
||||
|
||||
async function load() {
|
||||
const mod = await import(
|
||||
`./fixtures/node-middleware/dist/server/entry.mjs?dropcache=${Date.now()}`
|
||||
);
|
||||
return mod;
|
||||
}
|
||||
|
||||
describe('behavior from middleware, standalone', () => {
|
||||
/** @type {import('./test-utils').Fixture} */
|
||||
let fixture;
|
||||
|
@ -29,7 +22,7 @@ describe('behavior from middleware, standalone', () => {
|
|||
adapter: nodejs({ mode: 'standalone' }),
|
||||
});
|
||||
await fixture.build();
|
||||
const { startServer } = await load();
|
||||
const { startServer } = await fixture.loadAdapterEntryModule();
|
||||
let res = startServer();
|
||||
server = res.server;
|
||||
await waitServerListen(server.server);
|
||||
|
@ -69,7 +62,7 @@ describe('behavior from middleware, middleware', () => {
|
|||
adapter: nodejs({ mode: 'middleware' }),
|
||||
});
|
||||
await fixture.build();
|
||||
const { handler } = await load();
|
||||
const { handler } = await fixture.loadAdapterEntryModule();
|
||||
const app = express();
|
||||
app.use(handler);
|
||||
server = app.listen(8888);
|
||||
|
|
|
@ -8,13 +8,6 @@ import { loadFixture, waitServerListen } from './test-utils.js';
|
|||
* @typedef {import('../../../astro/test/test-utils').Fixture} Fixture
|
||||
*/
|
||||
|
||||
async function load() {
|
||||
const mod = await import(
|
||||
`./fixtures/prerender-404-500/dist/server/entry.mjs?dropcache=${Date.now()}`
|
||||
);
|
||||
return mod;
|
||||
}
|
||||
|
||||
describe('Prerender 404', () => {
|
||||
/** @type {import('./test-utils').Fixture} */
|
||||
let fixture;
|
||||
|
@ -32,10 +25,15 @@ describe('Prerender 404', () => {
|
|||
base: '/some-base',
|
||||
root: './fixtures/prerender-404-500/',
|
||||
output: 'server',
|
||||
outDir: './dist/server-with-base',
|
||||
build: {
|
||||
client: './dist/server-with-base/client',
|
||||
server: './dist/server-with-base/server',
|
||||
},
|
||||
adapter: nodejs({ mode: 'standalone' }),
|
||||
});
|
||||
await fixture.build();
|
||||
const { startServer } = await load();
|
||||
const { startServer } = await fixture.loadAdapterEntryModule();
|
||||
let res = startServer();
|
||||
server = res.server;
|
||||
await waitServerListen(server.server);
|
||||
|
@ -117,10 +115,15 @@ describe('Prerender 404', () => {
|
|||
site: 'https://test.info/',
|
||||
root: './fixtures/prerender-404-500/',
|
||||
output: 'server',
|
||||
outDir: './dist/server-without-base',
|
||||
build: {
|
||||
client: './dist/server-without-base/client',
|
||||
server: './dist/server-without-base/server',
|
||||
},
|
||||
adapter: nodejs({ mode: 'standalone' }),
|
||||
});
|
||||
await fixture.build();
|
||||
const { startServer } = await load();
|
||||
const { startServer } = await fixture.loadAdapterEntryModule();
|
||||
let res = startServer();
|
||||
server = res.server;
|
||||
await waitServerListen(server.server);
|
||||
|
@ -181,10 +184,15 @@ describe('Hybrid 404', () => {
|
|||
base: '/some-base',
|
||||
root: './fixtures/prerender-404-500/',
|
||||
output: 'hybrid',
|
||||
outDir: './dist/hybrid-with-base',
|
||||
build: {
|
||||
client: './dist/hybrid-with-base/client',
|
||||
server: './dist/hybrid-with-base/server',
|
||||
},
|
||||
adapter: nodejs({ mode: 'standalone' }),
|
||||
});
|
||||
await fixture.build();
|
||||
const { startServer } = await load();
|
||||
const { startServer } = await fixture.loadAdapterEntryModule();
|
||||
let res = startServer();
|
||||
server = res.server;
|
||||
await waitServerListen(server.server);
|
||||
|
@ -238,10 +246,15 @@ describe('Hybrid 404', () => {
|
|||
site: 'https://test.net/',
|
||||
root: './fixtures/prerender-404-500/',
|
||||
output: 'hybrid',
|
||||
outDir: './dist/hybrid-without-base',
|
||||
build: {
|
||||
client: './dist/hybrid-without-base/client',
|
||||
server: './dist/hybrid-without-base/server',
|
||||
},
|
||||
adapter: nodejs({ mode: 'standalone' }),
|
||||
});
|
||||
await fixture.build();
|
||||
const { startServer } = await load();
|
||||
const { startServer } = await fixture.loadAdapterEntryModule();
|
||||
let res = startServer();
|
||||
server = res.server;
|
||||
await waitServerListen(server.server);
|
||||
|
|
|
@ -8,10 +8,6 @@ import { loadFixture, waitServerListen } from './test-utils.js';
|
|||
* @typedef {import('../../../astro/test/test-utils').Fixture} Fixture
|
||||
*/
|
||||
|
||||
async function load() {
|
||||
const mod = await import(`./fixtures/prerender/dist/server/entry.mjs?dropcache=${Date.now()}`);
|
||||
return mod;
|
||||
}
|
||||
describe('Prerendering', () => {
|
||||
/** @type {import('./test-utils').Fixture} */
|
||||
let fixture;
|
||||
|
@ -25,10 +21,15 @@ describe('Prerendering', () => {
|
|||
base: '/some-base',
|
||||
root: './fixtures/prerender/',
|
||||
output: 'server',
|
||||
outDir: './dist/with-base',
|
||||
build: {
|
||||
client: './dist/with-base/client',
|
||||
server: './dist/with-base/server',
|
||||
},
|
||||
adapter: nodejs({ mode: 'standalone' }),
|
||||
});
|
||||
await fixture.build();
|
||||
const { startServer } = await load();
|
||||
const { startServer } = await fixture.loadAdapterEntryModule();
|
||||
let res = startServer();
|
||||
server = res.server;
|
||||
await waitServerListen(server.server);
|
||||
|
@ -94,10 +95,15 @@ describe('Prerendering', () => {
|
|||
fixture = await loadFixture({
|
||||
root: './fixtures/prerender/',
|
||||
output: 'server',
|
||||
outDir: './dist/without-base',
|
||||
build: {
|
||||
client: './dist/without-base/client',
|
||||
server: './dist/without-base/server',
|
||||
},
|
||||
adapter: nodejs({ mode: 'standalone' }),
|
||||
});
|
||||
await fixture.build();
|
||||
const { startServer } = await await load();
|
||||
const { startServer } = await fixture.loadAdapterEntryModule();
|
||||
let res = startServer();
|
||||
server = res.server;
|
||||
await waitServerListen(server.server);
|
||||
|
@ -155,6 +161,11 @@ describe('Prerendering', () => {
|
|||
fixture = await loadFixture({
|
||||
root: './fixtures/prerender/',
|
||||
output: 'server',
|
||||
outDir: './dist/dev',
|
||||
build: {
|
||||
client: './dist/dev/client',
|
||||
server: './dist/dev/server',
|
||||
},
|
||||
adapter: nodejs({ mode: 'standalone' }),
|
||||
});
|
||||
devServer = await fixture.startDevServer();
|
||||
|
@ -197,10 +208,15 @@ describe('Hybrid rendering', () => {
|
|||
base: '/some-base',
|
||||
root: './fixtures/prerender/',
|
||||
output: 'hybrid',
|
||||
outDir: './dist/hybrid-with-base',
|
||||
build: {
|
||||
client: './dist/hybrid-with-base/client',
|
||||
server: './dist/hybrid-with-base/server',
|
||||
},
|
||||
adapter: nodejs({ mode: 'standalone' }),
|
||||
});
|
||||
await fixture.build();
|
||||
const { startServer } = await load();
|
||||
const { startServer } = await fixture.loadAdapterEntryModule();
|
||||
let res = startServer();
|
||||
server = res.server;
|
||||
await waitServerListen(server.server);
|
||||
|
@ -264,10 +280,15 @@ describe('Hybrid rendering', () => {
|
|||
fixture = await loadFixture({
|
||||
root: './fixtures/prerender/',
|
||||
output: 'hybrid',
|
||||
outDir: './dist/hybrid-without-base',
|
||||
build: {
|
||||
client: './dist/hybrid-without-base/client',
|
||||
server: './dist/hybrid-without-base/server',
|
||||
},
|
||||
adapter: nodejs({ mode: 'standalone' }),
|
||||
});
|
||||
await fixture.build();
|
||||
const { startServer } = await load();
|
||||
const { startServer } = await fixture.loadAdapterEntryModule();
|
||||
let res = startServer();
|
||||
server = res.server;
|
||||
await waitServerListen(server.server);
|
||||
|
@ -323,10 +344,15 @@ describe('Hybrid rendering', () => {
|
|||
fixture = await loadFixture({
|
||||
root: './fixtures/prerender/',
|
||||
output: 'hybrid',
|
||||
outDir: './dist/hybrid-shared-modules',
|
||||
build: {
|
||||
client: './dist/hybrid-shared-modules/client',
|
||||
server: './dist/hybrid-shared-modules/server',
|
||||
},
|
||||
adapter: nodejs({ mode: 'standalone' }),
|
||||
});
|
||||
await fixture.build();
|
||||
const { startServer } = await load();
|
||||
const { startServer } = await fixture.loadAdapterEntryModule();
|
||||
let res = startServer();
|
||||
server = res.server;
|
||||
await waitServerListen(server.server);
|
||||
|
|
|
@ -8,13 +8,6 @@ import { loadFixture, waitServerListen } from './test-utils.js';
|
|||
* @typedef {import('../../../astro/test/test-utils').Fixture} Fixture
|
||||
*/
|
||||
|
||||
async function load() {
|
||||
const mod = await import(
|
||||
`./fixtures/trailing-slash/dist/server/entry.mjs?dropcache=${Date.now()}`
|
||||
);
|
||||
return mod;
|
||||
}
|
||||
|
||||
describe('Trailing slash', () => {
|
||||
/** @type {import('./test-utils').Fixture} */
|
||||
let fixture;
|
||||
|
@ -30,10 +23,15 @@ describe('Trailing slash', () => {
|
|||
base: '/some-base',
|
||||
output: 'hybrid',
|
||||
trailingSlash: 'always',
|
||||
outDir: './dist/always-with-base',
|
||||
build: {
|
||||
client: './dist/always-with-base/client',
|
||||
server: './dist/always-with-base/server',
|
||||
},
|
||||
adapter: nodejs({ mode: 'standalone' }),
|
||||
});
|
||||
await fixture.build();
|
||||
const { startServer } = await load();
|
||||
const { startServer } = await fixture.loadAdapterEntryModule();
|
||||
let res = startServer();
|
||||
server = res.server;
|
||||
await waitServerListen(server.server);
|
||||
|
@ -96,10 +94,15 @@ describe('Trailing slash', () => {
|
|||
root: './fixtures/trailing-slash/',
|
||||
output: 'hybrid',
|
||||
trailingSlash: 'always',
|
||||
outDir: './dist/always-without-base',
|
||||
build: {
|
||||
client: './dist/always-without-base/client',
|
||||
server: './dist/always-without-base/server',
|
||||
},
|
||||
adapter: nodejs({ mode: 'standalone' }),
|
||||
});
|
||||
await fixture.build();
|
||||
const { startServer } = await load();
|
||||
const { startServer } = await fixture.loadAdapterEntryModule();
|
||||
let res = startServer();
|
||||
server = res.server;
|
||||
await waitServerListen(server.server);
|
||||
|
@ -165,10 +168,15 @@ describe('Trailing slash', () => {
|
|||
base: '/some-base',
|
||||
output: 'hybrid',
|
||||
trailingSlash: 'never',
|
||||
outDir: './dist/never-with-base',
|
||||
build: {
|
||||
client: './dist/never-with-base/client',
|
||||
server: './dist/never-with-base/server',
|
||||
},
|
||||
adapter: nodejs({ mode: 'standalone' }),
|
||||
});
|
||||
await fixture.build();
|
||||
const { startServer } = await load();
|
||||
const { startServer } = await fixture.loadAdapterEntryModule();
|
||||
let res = startServer();
|
||||
server = res.server;
|
||||
await waitServerListen(server.server);
|
||||
|
@ -224,10 +232,15 @@ describe('Trailing slash', () => {
|
|||
root: './fixtures/trailing-slash/',
|
||||
output: 'hybrid',
|
||||
trailingSlash: 'never',
|
||||
outDir: './dist/never-without-base',
|
||||
build: {
|
||||
client: './dist/never-without-base/client',
|
||||
server: './dist/never-without-base/server',
|
||||
},
|
||||
adapter: nodejs({ mode: 'standalone' }),
|
||||
});
|
||||
await fixture.build();
|
||||
const { startServer } = await load();
|
||||
const { startServer } = await fixture.loadAdapterEntryModule();
|
||||
let res = startServer();
|
||||
server = res.server;
|
||||
await waitServerListen(server.server);
|
||||
|
@ -286,10 +299,15 @@ describe('Trailing slash', () => {
|
|||
base: '/some-base',
|
||||
output: 'hybrid',
|
||||
trailingSlash: 'ignore',
|
||||
outDir: './dist/ignore-with-base',
|
||||
build: {
|
||||
client: './dist/ignore-with-base/client',
|
||||
server: './dist/ignore-with-base/server',
|
||||
},
|
||||
adapter: nodejs({ mode: 'standalone' }),
|
||||
});
|
||||
await fixture.build();
|
||||
const { startServer } = await load();
|
||||
const { startServer } = await fixture.loadAdapterEntryModule();
|
||||
let res = startServer();
|
||||
server = res.server;
|
||||
await waitServerListen(server.server);
|
||||
|
@ -363,10 +381,15 @@ describe('Trailing slash', () => {
|
|||
root: './fixtures/trailing-slash/',
|
||||
output: 'hybrid',
|
||||
trailingSlash: 'ignore',
|
||||
outDir: './dist/ignore-without-base',
|
||||
build: {
|
||||
client: './dist/ignore-without-base/client',
|
||||
server: './dist/ignore-without-base/server',
|
||||
},
|
||||
adapter: nodejs({ mode: 'standalone' }),
|
||||
});
|
||||
await fixture.build();
|
||||
const { startServer } = await load();
|
||||
const { startServer } = await fixture.loadAdapterEntryModule();
|
||||
let res = startServer();
|
||||
server = res.server;
|
||||
await waitServerListen(server.server);
|
||||
|
|
|
@ -20,7 +20,7 @@ describe('Serverless prerender', () => {
|
|||
|
||||
it('outDir is tree-shaken if not needed', async () => {
|
||||
const [file] = await fixture.glob(
|
||||
'../.vercel/output/functions/_render.func/packages/integrations/vercel/test/fixtures/serverless-prerender/.vercel/output/_functions/chunks/pages/generic_*.mjs'
|
||||
'../.vercel/output/functions/_render.func/packages/integrations/vercel/test/fixtures/serverless-prerender/.vercel/output/_functions/pages/_image.astro.mjs'
|
||||
);
|
||||
const contents = await fixture.readFile(file);
|
||||
assert.ok(!contents.includes('const outDir ='), "outDir is tree-shaken if it's not imported");
|
||||
|
|
|
@ -3781,6 +3781,32 @@ importers:
|
|||
specifier: workspace:*
|
||||
version: link:../../..
|
||||
|
||||
packages/astro/test/fixtures/ssr-prerender-chunks:
|
||||
dependencies:
|
||||
'@astrojs/react':
|
||||
specifier: workspace:*
|
||||
version: link:../../../../integrations/react
|
||||
'@test/ssr-prerender-chunks-test-adapter':
|
||||
specifier: link:./deps/test-adapter
|
||||
version: link:deps/test-adapter
|
||||
'@types/react':
|
||||
specifier: ^18.2.75
|
||||
version: 18.3.3
|
||||
'@types/react-dom':
|
||||
specifier: ^18.2.24
|
||||
version: 18.3.0
|
||||
astro:
|
||||
specifier: workspace:*
|
||||
version: link:../../..
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
react-dom:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1(react@18.3.1)
|
||||
|
||||
packages/astro/test/fixtures/ssr-prerender-chunks/deps/test-adapter: {}
|
||||
|
||||
packages/astro/test/fixtures/ssr-prerender-get-static-paths:
|
||||
dependencies:
|
||||
astro:
|
||||
|
|
Loading…
Reference in a new issue