diff --git a/packages/astro/src/core/build/app.ts b/packages/astro/src/core/build/app.ts index f0d4958836f9..2110b706a5c1 100644 --- a/packages/astro/src/core/build/app.ts +++ b/packages/astro/src/core/build/app.ts @@ -17,6 +17,7 @@ export class BuildApp extends BaseApp { public setOptions(options: StaticBuildOptions) { this.pipeline.setOptions(options); + this.logger = options.logger; } public getOptions() { diff --git a/packages/astro/src/core/build/plugins/index.ts b/packages/astro/src/core/build/plugins/index.ts index f8b4c118dd14..d33173049f96 100644 --- a/packages/astro/src/core/build/plugins/index.ts +++ b/packages/astro/src/core/build/plugins/index.ts @@ -11,6 +11,7 @@ import { pluginMiddleware } from './plugin-middleware.js'; import { pluginPrerender } from './plugin-prerender.js'; import { pluginScripts } from './plugin-scripts.js'; import { pluginSSR } from './plugin-ssr.js'; +import { pluginNoop } from './plugin-noop.js'; export function getAllBuildPlugins( internals: BuildInternals, @@ -27,5 +28,6 @@ export function getAllBuildPlugins( pluginPrerender(options, internals), pluginScripts(internals), ...pluginSSR(options, internals), + pluginNoop(), ].filter(Boolean); } diff --git a/packages/astro/src/core/build/plugins/plugin-manifest.ts b/packages/astro/src/core/build/plugins/plugin-manifest.ts index eb1e038d3e94..c443370ea948 100644 --- a/packages/astro/src/core/build/plugins/plugin-manifest.ts +++ b/packages/astro/src/core/build/plugins/plugin-manifest.ts @@ -188,6 +188,8 @@ async function buildManifest( const assetQueryParams = settings.adapter?.client?.assetQueryParams; const assetQueryString = assetQueryParams ? assetQueryParams.toString() : undefined; + const appendAssetQuery = (pth: string) => assetQueryString ? `${pth}?${assetQueryString}` : pth; + const prefixAssetPath = (pth: string) => { let result = ''; if (settings.config.build.assetsPrefix) { @@ -228,7 +230,7 @@ async function buildManifest( scripts.push({ type: 'external', - value: src, + value: appendAssetQuery(src), }); } @@ -238,6 +240,7 @@ async function buildManifest( const styles = pageData.styles .sort(cssOrder) .map(({ sheet }) => sheet) + .map((s) => (s.type === 'external' ? { ...s, src: appendAssetQuery(s.src) } : s)) .reduce(mergeInlineCss, []); routes.push({ diff --git a/packages/astro/src/core/build/plugins/plugin-noop.ts b/packages/astro/src/core/build/plugins/plugin-noop.ts new file mode 100644 index 000000000000..195e31959e04 --- /dev/null +++ b/packages/astro/src/core/build/plugins/plugin-noop.ts @@ -0,0 +1,33 @@ +import type * as vite from 'vite'; + +export const NOOP_MODULE_ID = 'virtual:astro:noop'; +const RESOLVED_NOOP_MODULE_ID = '\0' + NOOP_MODULE_ID; + +// An empty module that does nothing. This can be used as a placeholder +// when you just need a module to be in the graph. +// We use this for the client build when there are no client modules, +// because the publicDir copying happens in the client build. +export function pluginNoop(): vite.Plugin { + return { + name: 'plugin-noop', + resolveId(id) { + if(id === NOOP_MODULE_ID) { + return RESOLVED_NOOP_MODULE_ID; + } + }, + load(id) { + if(id === RESOLVED_NOOP_MODULE_ID) { + return ''; + } + }, + generateBundle(_options, bundle) { + // Delete this bundle so that its not written out to disk. + for(const [name, chunk] of Object.entries(bundle)) { + if(chunk.type === 'asset') continue; + if(chunk.facadeModuleId === RESOLVED_NOOP_MODULE_ID) { + delete bundle[name]; + } + } + } + } +} diff --git a/packages/astro/src/core/build/static-build.ts b/packages/astro/src/core/build/static-build.ts index 8e26db1888f0..ae9da7eeb5e7 100644 --- a/packages/astro/src/core/build/static-build.ts +++ b/packages/astro/src/core/build/static-build.ts @@ -26,6 +26,7 @@ import { RESOLVED_SSR_VIRTUAL_MODULE_ID } from './plugins/plugin-ssr.js'; import { ASTRO_PAGE_EXTENSION_POST_PATTERN } from './plugins/util.js'; import type { StaticBuildOptions } from './types.js'; import { encodeName, getTimeStat, viteBuildReturnToRollupOutputs } from './util.js'; +import { NOOP_MODULE_ID } from './plugins/plugin-noop.js'; const PRERENDER_ENTRY_FILENAME_PREFIX = 'prerender-entry'; @@ -120,8 +121,6 @@ export async function staticBuild( */ async function buildEnvironments(opts: StaticBuildOptions, internals: BuildInternals) { const { allPages, settings, viteConfig } = opts; - const ssr = settings.buildOutput === 'server'; - const out = getServerOutputDirectory(settings); const routes = Object.values(allPages).flatMap((pageData) => pageData.route); // Determine if we should use the legacy-dynamic entrypoint @@ -141,9 +140,8 @@ async function buildEnvironments(opts: StaticBuildOptions, internals: BuildInter cssMinify: viteConfig.build?.minify == null ? true : !!viteConfig.build?.minify, ...viteConfig.build, emptyOutDir: false, + copyPublicDir: false, manifest: false, - outDir: fileURLToPath(out), - copyPublicDir: !ssr, rollupOptions: { ...viteConfig.build?.rollupOptions, // Setting as `exports-only` allows us to safely delete inputs that are only used during prerendering @@ -212,7 +210,7 @@ async function buildEnvironments(opts: StaticBuildOptions, internals: BuildInter prerender: { build: { emitAssets: true, - outDir: fileURLToPath(new URL('./.prerender/', out)), + outDir: fileURLToPath(new URL('./.prerender/', getServerOutputDirectory(settings))), rollupOptions: { input: 'astro/entrypoints/prerender', output: { @@ -228,9 +226,8 @@ async function buildEnvironments(opts: StaticBuildOptions, internals: BuildInter build: { emitAssets: true, target: 'esnext', - emptyOutDir: false, outDir: fileURLToPath(getClientOutputDirectory(settings)), - copyPublicDir: ssr, + copyPublicDir: true, sourcemap: viteConfig.environments?.client?.build?.sourcemap ?? false, minify: true, rollupOptions: { @@ -282,9 +279,14 @@ async function buildEnvironments(opts: StaticBuildOptions, internals: BuildInter // are only detected during SSR. We mutate the config here since the builder was already created // and this is the only way to update the input after instantiation. internals.clientInput = getClientInput(internals, settings); + if(!internals.clientInput.size) { + // At least 1 input is required to do a build, otherwise Vite throws. + // We need the client build to happen in order to copy over the `public/` folder + // So using the noop plugin here which will give us an input that just gets thrown away. + internals.clientInput.add(NOOP_MODULE_ID); + } builder.environments.client.config.build.rollupOptions.input = Array.from(internals.clientInput); - const clientOutput = - internals.clientInput.size === 0 ? [] : await builder.build(builder.environments.client); + const clientOutput = await builder.build(builder.environments.client); return { ssrOutput, prerenderOutput, clientOutput }; } diff --git a/packages/astro/test/fixtures/ssr-params/src/pages/[category].astro b/packages/astro/test/fixtures/ssr-params/src/pages/[category].astro index 07e3150f5393..2e2ca3a82dc9 100644 --- a/packages/astro/test/fixtures/ssr-params/src/pages/[category].astro +++ b/packages/astro/test/fixtures/ssr-params/src/pages/[category].astro @@ -5,7 +5,6 @@ export function getStaticPaths() { { params: { category: "%23something" } }, { params: { category: "%2Fsomething" } }, { params: { category: "%3Fsomething" } }, - { params: { category: "%25something" } }, { params: { category: "[page]" } }, { params: { category: "你好" } }, ] diff --git a/packages/astro/test/params.test.js b/packages/astro/test/params.test.js index 60890637d357..7508b7352814 100644 --- a/packages/astro/test/params.test.js +++ b/packages/astro/test/params.test.js @@ -148,10 +148,4 @@ describe('Astro.params in static mode', () => { const $ = cheerio.load(html); assert.equal($('.category').text(), '%3Fsomething'); }); - - it("It doesn't encode/decode URI characters such as %25 (%)", async () => { - const html = await fixture.readFile(encodeURI('/%25something/index.html')); - const $ = cheerio.load(html); - assert.equal($('.category').text(), '%25something'); - }); });