Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"commit": false,
"linked": [],
"access": "public",
"baseBranch": "origin/next",
"baseBranch": "origin/main",
"updateInternalDependencies": "patch",
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
"onlyUpdatePeerDependentsWhenOutOfRange": true
Expand Down
5 changes: 5 additions & 0 deletions .changeset/fix-dev-toolbar-optimize-deps.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Prebundle `astro/toolbar` in dev when custom dev toolbar apps are registered, preventing re-optimization reloads that can hide or break the toolbar.
5 changes: 5 additions & 0 deletions .changeset/fix-i18n-redirect-double-slash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Fixes `redirectToDefaultLocale` producing a protocol-relative URL (`//locale`) instead of an absolute path (`/locale`) when `base` is `'/'`.
5 changes: 5 additions & 0 deletions .changeset/fix-server-islands-prerender.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Fixes server islands (`server:defer`) not working when only used in prerendered pages with `output: 'server'`.
5 changes: 5 additions & 0 deletions .changeset/new-routes-dev-css.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Fix a dev server bug where newly created pages could miss layout-imported CSS until restart.
5 changes: 5 additions & 0 deletions .changeset/sparkly-peas-mix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/cloudflare': patch
---

Fixes images not working in dev mode when using the `cloudflare` option
5 changes: 5 additions & 0 deletions .changeset/swift-terms-lose.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Fixes a warning when using `prefetchAll`
5 changes: 5 additions & 0 deletions .changeset/tricky-otters-go.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Fixes a bug where a directory at the project root sharing the same name as a page route would cause the dev server to return a 404 instead of serving the page.
5 changes: 5 additions & 0 deletions .changeset/wise-cats-check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Update the unknown file extension error hint to recommend `vite.resolve.noExternal`, which is the correct Vite 7 config key.
6 changes: 6 additions & 0 deletions .changeset/wise-turkeys-tan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@astrojs/netlify': patch
'@astrojs/node': patch
---

Fixes an issue where the adapter would cause a series of warnings during the build.
40 changes: 29 additions & 11 deletions packages/astro/src/core/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,19 @@ interface AstroBuilderOptions extends BuildOptions {
logger: Logger;
mode: string;
runtimeMode: RuntimeMode;
/**
* Provide a pre-built routes list to skip filesystem route scanning.
* Useful for testing builds with in-memory virtual modules.
*/
routesList?: RoutesList;
/**
* Whether to run `syncInternal` during setup. Defaults to true.
* Set to false for in-memory builds that don't need type generation.
*/
sync?: boolean;
}

class AstroBuilder {
export class AstroBuilder {
private settings: AstroSettings;
private logger: Logger;
private mode: string;
Expand All @@ -103,17 +113,19 @@ class AstroBuilder {
private routesList: RoutesList;
private timer: Record<string, number>;
private teardownCompiler: boolean;
private sync: boolean;

constructor(settings: AstroSettings, options: AstroBuilderOptions) {
this.mode = options.mode;
this.runtimeMode = options.runtimeMode;
this.settings = settings;
this.logger = options.logger;
this.teardownCompiler = options.teardownCompiler ?? true;
this.sync = options.sync ?? true;
this.origin = settings.config.site
? new URL(settings.config.site).origin
: `http://localhost:${settings.config.server.port}`;
this.routesList = { routes: [] };
this.routesList = options.routesList ?? { routes: [] };
this.timer = {};
}

Expand All @@ -128,7 +140,11 @@ class AstroBuilder {
logger: logger,
});
this.settings.buildOutput = getPrerenderDefault(this.settings.config) ? 'static' : 'server';
this.routesList = await createRoutesList({ settings: this.settings }, this.logger);

// Skip filesystem route scanning if routesList was pre-populated (e.g. in-memory builds)
if (this.routesList.routes.length === 0) {
this.routesList = await createRoutesList({ settings: this.settings }, this.logger);
}

await runHookConfigDone({ settings: this.settings, logger: logger, command: 'build' });

Expand All @@ -155,14 +171,16 @@ class AstroBuilder {
},
);

const { syncInternal } = await import('../sync/index.js');
await syncInternal({
mode: this.mode,
settings: this.settings,
logger,
fs,
command: 'build',
});
if (this.sync) {
const { syncInternal } = await import('../sync/index.js');
await syncInternal({
mode: this.mode,
settings: this.settings,
logger,
fs,
command: 'build',
});
}

return { viteConfig };
}
Expand Down
44 changes: 37 additions & 7 deletions packages/astro/src/core/build/static-build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import { emptyDir, removeEmptyDirs } from '../../core/fs/index.js';
import { appendForwardSlash, prependForwardSlash } from '../../core/path.js';
import { runHookBuildSetup } from '../../integrations/hooks.js';
import { SERIALIZED_MANIFEST_RESOLVED_ID } from '../../manifest/serialized.js';
import { getClientOutputDirectory, getServerOutputDirectory } from '../../prerender/utils.js';
import {
getClientOutputDirectory,
getPrerenderOutputDirectory,
getServerOutputDirectory,
} from '../../prerender/utils.js';
import type { RouteData } from '../../types/public/internal.js';
import { VIRTUAL_PAGE_RESOLVED_MODULE_ID } from '../../vite-plugin-pages/const.js';
import { PAGE_SCRIPT_ID } from '../../vite-plugin-scripts/index.js';
Expand All @@ -33,6 +37,7 @@ import { NOOP_MODULE_ID } from './plugins/plugin-noop.js';
import { ASTRO_VITE_ENVIRONMENT_NAMES } from '../constants.js';
import type { InputOption } from 'rollup';
import { getSSRAssets } from './internal.js';
import { serverIslandPlaceholderMap } from '../server-islands/vite-plugin-server-islands.js';

const PRERENDER_ENTRY_FILENAME_PREFIX = 'prerender-entry';

Expand All @@ -47,6 +52,11 @@ export interface ExtractedChunk {
prerender: boolean;
}

type BuildPostHook = (params: {
chunks: ExtractedChunk[];
mutate: (fileName: string, code: string, prerender: boolean) => void;
}) => void | Promise<void>;

/**
* Extracts only the chunks that need post-build injection from RollupOutput.
* This allows releasing the full RollupOutput to reduce memory usage.
Expand All @@ -63,8 +73,9 @@ function extractRelevantChunks(

const needsContentInjection = chunk.code.includes(LINKS_PLACEHOLDER);
const needsManifestInjection = chunk.moduleIds.includes(SERIALIZED_MANIFEST_RESOLVED_ID);
const needsServerIslandInjection = chunk.code.includes(serverIslandPlaceholderMap);

if (needsContentInjection || needsManifestInjection) {
if (needsContentInjection || needsManifestInjection || needsServerIslandInjection) {
extracted.push({
fileName: chunk.fileName,
code: chunk.code,
Expand Down Expand Up @@ -159,6 +170,7 @@ async function buildEnvironments(opts: StaticBuildOptions, internals: BuildInter
const flatPlugins = buildPlugins.flat().filter(Boolean);
const plugins = [...flatPlugins, ...(viteConfig.plugins || [])];
let currentRollupInput: InputOption | undefined = undefined;
let buildPostHooks: BuildPostHook[] = [];
plugins.push({
name: 'astro:resolve-input',
// When the rollup input is safe to update, we normalize it to always be an object
Expand Down Expand Up @@ -187,10 +199,15 @@ async function buildEnvironments(opts: StaticBuildOptions, internals: BuildInter
order: 'post',
async handler() {
// Inject manifest and content placeholders into extracted chunks
await runManifestInjection(opts, internals, internals.extractedChunks ?? []);
await runManifestInjection(
opts,
internals,
internals.extractedChunks ?? [],
buildPostHooks,
);

// Generation and cleanup
const prerenderOutputDir = new URL('./.prerender/', getServerOutputDirectory(settings));
const prerenderOutputDir = getPrerenderOutputDirectory(settings);

// TODO: The `static` and `server` branches below are nearly identical now.
// Consider refactoring to remove the else-if and unify the logic.
Expand Down Expand Up @@ -335,6 +352,14 @@ async function buildEnvironments(opts: StaticBuildOptions, internals: BuildInter
const prerenderChunks = extractRelevantChunks(prerenderOutputs, true);
prerenderOutput = undefined as any;

const ssrPlugins =
builder.environments[ASTRO_VITE_ENVIRONMENT_NAMES.ssr]?.config.plugins ?? [];
buildPostHooks = ssrPlugins
.map((plugin) =>
typeof plugin.api?.buildPostHook === 'function' ? plugin.api.buildPostHook : undefined,
)
.filter(Boolean) as BuildPostHook[];

// Build client environment
// We must discover client inputs after SSR build because hydration/client-only directives
// are only detected during SSR. We mutate the config here since the builder was already created
Expand Down Expand Up @@ -364,7 +389,7 @@ async function buildEnvironments(opts: StaticBuildOptions, internals: BuildInter
[ASTRO_VITE_ENVIRONMENT_NAMES.prerender]: {
build: {
emitAssets: true,
outDir: fileURLToPath(new URL('./.prerender/', getServerOutputDirectory(settings))),
outDir: fileURLToPath(getPrerenderOutputDirectory(settings)),
rollupOptions: {
// Only skip the default prerender entrypoint if an adapter with `entrypointResolution: 'self'` is used
// AND provides a custom prerenderer. Otherwise, use the default.
Expand Down Expand Up @@ -470,6 +495,7 @@ async function runManifestInjection(
opts: StaticBuildOptions,
internals: BuildInternals,
chunks: ExtractedChunk[],
buildPostHooks: BuildPostHook[],
) {
const mutations = new Map<string, { code: string; prerender: boolean }>();

Expand All @@ -484,6 +510,11 @@ async function runManifestInjection(
internals,
{ chunks, mutate },
);

for (const buildPostHook of buildPostHooks) {
await buildPostHook({ chunks, mutate });
}

await writeMutatedChunks(opts, mutations);
}

Expand All @@ -498,14 +529,13 @@ async function writeMutatedChunks(
) {
const { settings } = opts;
const config = settings.config;
const serverOutputDir = getServerOutputDirectory(settings);

for (const [fileName, mutation] of mutations) {
let root: URL;

if (mutation.prerender) {
// Write to prerender directory
root = new URL('./.prerender/', serverOutputDir);
root = getPrerenderOutputDirectory(settings);
} else if (settings.buildOutput === 'server') {
root = config.build.server;
} else {
Expand Down
2 changes: 1 addition & 1 deletion packages/astro/src/core/errors/dev/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ function generateHint(err: ErrorWithMetadata): string | undefined {
const commonBrowserAPIs = ['document', 'window'];

if (/Unknown file extension "\.(?:jsx|vue|svelte|astro|css)" for /.test(err.message)) {
return 'You likely need to add this package to `vite.ssr.noExternal` in your astro config file.';
return 'You likely need to add this package to `vite.resolve.noExternal` in your astro config file.';
} else if (commonBrowserAPIs.some((api) => err.toString().includes(api))) {
const hint = `Browser APIs are not available on the server.

Expand Down
Loading
Loading