astro 6.0.3 → 6.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/actions/integration.js +9 -6
  2. package/dist/assets/runtime.d.ts +2 -1
  3. package/dist/assets/runtime.js +15 -4
  4. package/dist/assets/utils/svg.js +25 -7
  5. package/dist/cli/infra/build-time-astro-version-provider.js +1 -1
  6. package/dist/content/content-layer.js +3 -3
  7. package/dist/content/server-listeners.js +4 -0
  8. package/dist/content/types-generator.js +4 -1
  9. package/dist/core/build/generate.js +92 -84
  10. package/dist/core/build/index.d.ts +39 -1
  11. package/dist/core/build/index.js +17 -10
  12. package/dist/core/build/plugins/plugin-internals.js +12 -13
  13. package/dist/core/build/static-build.js +33 -15
  14. package/dist/core/constants.js +1 -1
  15. package/dist/core/dev/dev.js +1 -1
  16. package/dist/core/errors/dev/utils.js +1 -1
  17. package/dist/core/messages/runtime.js +1 -1
  18. package/dist/core/routing/helpers.d.ts +7 -0
  19. package/dist/core/routing/helpers.js +9 -0
  20. package/dist/core/server-islands/vite-plugin-server-islands.d.ts +1 -0
  21. package/dist/core/server-islands/vite-plugin-server-islands.js +89 -57
  22. package/dist/i18n/router.js +2 -1
  23. package/dist/prerender/utils.d.ts +4 -0
  24. package/dist/prerender/utils.js +4 -0
  25. package/dist/toolbar/vite-plugin-dev-toolbar.js +5 -1
  26. package/dist/vite-plugin-astro-server/plugin.js +2 -2
  27. package/dist/vite-plugin-astro-server/route-guard.js +7 -3
  28. package/dist/vite-plugin-environment/index.js +1 -1
  29. package/dist/vite-plugin-renderers/index.js +4 -6
  30. package/dist/vite-plugin-routes/index.js +5 -0
  31. package/dist/vite-plugin-scripts/index.js +1 -1
  32. package/package.json +4 -4
  33. package/templates/env.mjs +0 -1
@@ -1,5 +1,6 @@
1
1
  import { AstroError } from "../core/errors/errors.js";
2
2
  import { ActionsWithoutServerOutputError } from "../core/errors/errors-data.js";
3
+ import { hasNonPrerenderedProjectRoute } from "../core/routing/helpers.js";
3
4
  import { viteID } from "../core/util.js";
4
5
  import { ACTION_RPC_ROUTE_PATTERN, ACTIONS_TYPES_FILE, VIRTUAL_MODULE_ID } from "./consts.js";
5
6
  function astroIntegrationActionsRouteHandler({
@@ -18,20 +19,22 @@ function astroIntegrationActionsRouteHandler({
18
19
  });
19
20
  },
20
21
  "astro:config:done": async (params) => {
21
- if (params.buildOutput === "static") {
22
- const error = new AstroError(ActionsWithoutServerOutputError);
23
- error.stack = void 0;
24
- throw error;
25
- }
26
22
  const stringifiedActionsImport = JSON.stringify(
27
23
  viteID(new URL(`./${filename}`, params.config.srcDir))
28
24
  );
29
25
  settings.injectedTypes.push({
30
26
  filename: ACTIONS_TYPES_FILE,
31
27
  content: `declare module "astro:actions" {
32
- export const actions: typeof import(${stringifiedActionsImport})["server"];
28
+ export const actions: typeof import(${stringifiedActionsImport})["server"];
33
29
  }`
34
30
  });
31
+ },
32
+ "astro:routes:resolved": ({ routes }) => {
33
+ if (!hasNonPrerenderedProjectRoute(routes)) {
34
+ const error = new AstroError(ActionsWithoutServerOutputError);
35
+ error.stack = void 0;
36
+ throw error;
37
+ }
35
38
  }
36
39
  }
37
40
  };
@@ -3,8 +3,9 @@ export interface SvgComponentProps {
3
3
  meta: ImageMetadata;
4
4
  attributes: Record<string, string>;
5
5
  children: string;
6
+ styles: string[];
6
7
  }
7
- export declare function createSvgComponent({ meta, attributes, children }: SvgComponentProps): import("../runtime/server/index.js").AstroComponentFactory & ImageMetadata;
8
+ export declare function createSvgComponent({ meta, attributes, children, styles }: SvgComponentProps): import("../runtime/server/index.js").AstroComponentFactory & ImageMetadata;
8
9
  type SvgAttributes = Record<string, any>;
9
10
  export declare function dropAttributes(attributes: SvgAttributes): SvgAttributes;
10
11
  export {};
@@ -1,13 +1,24 @@
1
+ import { generateCspDigest } from "../core/encryption.js";
1
2
  import {
2
3
  createComponent,
3
4
  render,
4
5
  spreadAttributes,
5
6
  unescapeHTML
6
7
  } from "../runtime/server/index.js";
7
- function createSvgComponent({ meta, attributes, children }) {
8
- const Component = createComponent((_, props) => {
9
- const normalizedProps = normalizeProps(attributes, props);
10
- return render`<svg${spreadAttributes(normalizedProps)}>${unescapeHTML(children)}</svg>`;
8
+ function createSvgComponent({ meta, attributes, children, styles }) {
9
+ const hasStyles = styles.length > 0;
10
+ const Component = createComponent({
11
+ async factory(result, props) {
12
+ const normalizedProps = normalizeProps(attributes, props);
13
+ if (hasStyles && result.cspDestination) {
14
+ for (const style of styles) {
15
+ const hash = await generateCspDigest(style, result.cspAlgorithm);
16
+ result._metadata.extraStyleHashes.push(hash);
17
+ }
18
+ }
19
+ return render`<svg${spreadAttributes(normalizedProps)}>${unescapeHTML(children)}</svg>`;
20
+ },
21
+ propagation: hasStyles ? "self" : "none"
11
22
  });
12
23
  if (import.meta.env.DEV) {
13
24
  makeNonEnumerable(Component);
@@ -1,5 +1,5 @@
1
1
  import { optimize } from "svgo";
2
- import { parse, renderSync } from "ultrahtml";
2
+ import { ELEMENT_NODE, TEXT_NODE, parse, renderSync } from "ultrahtml";
3
3
  import { AstroError, AstroErrorData } from "../../core/errors/index.js";
4
4
  import { dropAttributes } from "../runtime.js";
5
5
  function parseSvg({
@@ -25,18 +25,31 @@ function parseSvg({
25
25
  }
26
26
  const root = parse(processedContents);
27
27
  const svgNode = root.children.find(
28
- ({ name, type }) => type === 1 && name === "svg"
28
+ ({ name, type }) => type === ELEMENT_NODE && name === "svg"
29
29
  );
30
30
  if (!svgNode) {
31
31
  throw new Error("SVG file does not contain an <svg> element");
32
32
  }
33
33
  const { attributes, children } = svgNode;
34
34
  const body = renderSync({ ...root, children });
35
- return { attributes, body };
35
+ const styles = [];
36
+ for (const child of children) {
37
+ if (child.type === ELEMENT_NODE && child.name === "style") {
38
+ const textContent = child.children?.filter((c) => c.type === TEXT_NODE).map((c) => c.value).join("");
39
+ if (textContent) {
40
+ styles.push(textContent);
41
+ }
42
+ }
43
+ }
44
+ return { attributes, body, styles };
36
45
  }
37
46
  function makeSvgComponent(meta, contents, svgoConfig) {
38
47
  const file = typeof contents === "string" ? contents : contents.toString("utf-8");
39
- const { attributes, body: children } = parseSvg({
48
+ const {
49
+ attributes,
50
+ body: children,
51
+ styles
52
+ } = parseSvg({
40
53
  path: meta.fsPath,
41
54
  contents: file,
42
55
  svgoConfig
@@ -44,19 +57,24 @@ function makeSvgComponent(meta, contents, svgoConfig) {
44
57
  const props = {
45
58
  meta,
46
59
  attributes: dropAttributes(attributes),
47
- children
60
+ children,
61
+ styles
48
62
  };
49
63
  return `import { createSvgComponent } from 'astro/assets/runtime';
50
64
  export default createSvgComponent(${JSON.stringify(props)})`;
51
65
  }
52
66
  function parseSvgComponentData(meta, contents, svgoConfig) {
53
67
  const file = typeof contents === "string" ? contents : contents.toString("utf-8");
54
- const { attributes, body: children } = parseSvg({
68
+ const {
69
+ attributes,
70
+ body: children,
71
+ styles
72
+ } = parseSvg({
55
73
  path: meta.fsPath,
56
74
  contents: file,
57
75
  svgoConfig
58
76
  });
59
- return { attributes: dropAttributes(attributes), children };
77
+ return { attributes: dropAttributes(attributes), children, styles };
60
78
  }
61
79
  export {
62
80
  makeSvgComponent,
@@ -1,6 +1,6 @@
1
1
  class BuildTimeAstroVersionProvider {
2
2
  // Injected during the build through esbuild define
3
- version = "6.0.3";
3
+ version = "6.0.5";
4
4
  }
5
5
  export {
6
6
  BuildTimeAstroVersionProvider
@@ -189,7 +189,7 @@ ${contentConfig.error.message}`
189
189
  logger.info("Content config changed");
190
190
  shouldClear = true;
191
191
  }
192
- if (previousAstroVersion && previousAstroVersion !== "6.0.3") {
192
+ if (previousAstroVersion && previousAstroVersion !== "6.0.5") {
193
193
  logger.info("Astro version changed");
194
194
  shouldClear = true;
195
195
  }
@@ -197,8 +197,8 @@ ${contentConfig.error.message}`
197
197
  logger.info("Clearing content store");
198
198
  this.#store.clearAll();
199
199
  }
200
- if ("6.0.3") {
201
- this.#store.metaStore().set("astro-version", "6.0.3");
200
+ if ("6.0.5") {
201
+ this.#store.metaStore().set("astro-version", "6.0.5");
202
202
  }
203
203
  if (currentConfigDigest) {
204
204
  this.#store.metaStore().set("content-config-digest", currentConfigDigest);
@@ -6,6 +6,10 @@ async function attachContentServerListeners({
6
6
  logger,
7
7
  settings
8
8
  }) {
9
+ const maxListeners = viteServer.watcher.getMaxListeners();
10
+ if (maxListeners !== 0 && maxListeners < 50) {
11
+ viteServer.watcher.setMaxListeners(50);
12
+ }
9
13
  const contentGenerator = await createContentTypesGenerator({
10
14
  fs,
11
15
  settings,
@@ -463,7 +463,10 @@ async function generateJSONSchema(fsMod, collectionConfig, collectionKey, collec
463
463
  ctx.jsonSchema.type = "string";
464
464
  ctx.jsonSchema.format = "date-time";
465
465
  }
466
- }
466
+ },
467
+ // Collection schemas are used for parsing collection input, so we need to tell Zod to use the
468
+ // input shape when generating a JSON schema.
469
+ io: "input"
467
470
  });
468
471
  const schemaStr = JSON.stringify(schema, null, 2);
469
472
  const schemaJsonPath = new URL(
@@ -61,102 +61,106 @@ async function generatePages(options, internals, prerenderOutputDir) {
61
61
  const verb = ssr ? "prerendering" : "generating";
62
62
  logger.info("SKIP_FORMAT", `
63
63
  ${colors.bgGreen(colors.black(` ${verb} static routes `))}`);
64
- const pathsWithRoutes = await prerenderer.getStaticPaths();
65
64
  const routeToHeaders = /* @__PURE__ */ new Map();
66
- const hasI18nDomains = ssr && options.settings.config.i18n?.domains && Object.keys(options.settings.config.i18n.domains).length > 0;
67
- const { config } = options.settings;
68
- const builtPaths = /* @__PURE__ */ new Set();
69
- const filteredPaths = pathsWithRoutes.filter(({ pathname, route }) => {
70
- if (hasI18nDomains && route.prerender) {
71
- throw new AstroError({
72
- ...AstroErrorData.NoPrerenderedRoutesWithDomains,
73
- message: AstroErrorData.NoPrerenderedRoutesWithDomains.message(route.component)
74
- });
75
- }
76
- const normalized = removeTrailingForwardSlash(pathname);
77
- if (!builtPaths.has(normalized)) {
78
- builtPaths.add(normalized);
79
- return true;
80
- }
81
- const matchedRoute = matchRoute(decodeURI(pathname), options.routesList);
82
- if (!matchedRoute) {
83
- return false;
84
- }
85
- if (matchedRoute === route) {
86
- return true;
87
- }
88
- if (config.prerenderConflictBehavior === "error") {
89
- throw new AstroError({
90
- ...AstroErrorData.PrerenderRouteConflict,
91
- message: AstroErrorData.PrerenderRouteConflict.message(
65
+ let staticImageList = getStaticImageList();
66
+ try {
67
+ const pathsWithRoutes = await prerenderer.getStaticPaths();
68
+ const hasI18nDomains = ssr && options.settings.config.i18n?.domains && Object.keys(options.settings.config.i18n.domains).length > 0;
69
+ const { config } = options.settings;
70
+ const builtPaths = /* @__PURE__ */ new Set();
71
+ const filteredPaths = pathsWithRoutes.filter(({ pathname, route }) => {
72
+ if (hasI18nDomains && route.prerender) {
73
+ throw new AstroError({
74
+ ...AstroErrorData.NoPrerenderedRoutesWithDomains,
75
+ message: AstroErrorData.NoPrerenderedRoutesWithDomains.message(route.component)
76
+ });
77
+ }
78
+ const normalized = removeTrailingForwardSlash(pathname);
79
+ if (!builtPaths.has(normalized)) {
80
+ builtPaths.add(normalized);
81
+ return true;
82
+ }
83
+ const matchedRoute = matchRoute(decodeURI(pathname), options.routesList);
84
+ if (!matchedRoute) {
85
+ return false;
86
+ }
87
+ if (matchedRoute === route) {
88
+ return true;
89
+ }
90
+ if (config.prerenderConflictBehavior === "error") {
91
+ throw new AstroError({
92
+ ...AstroErrorData.PrerenderRouteConflict,
93
+ message: AstroErrorData.PrerenderRouteConflict.message(
94
+ matchedRoute.route,
95
+ route.route,
96
+ normalized
97
+ ),
98
+ hint: AstroErrorData.PrerenderRouteConflict.hint(matchedRoute.route, route.route)
99
+ });
100
+ } else if (config.prerenderConflictBehavior === "warn") {
101
+ const msg = AstroErrorData.PrerenderRouteConflict.message(
92
102
  matchedRoute.route,
93
103
  route.route,
94
104
  normalized
95
- ),
96
- hint: AstroErrorData.PrerenderRouteConflict.hint(matchedRoute.route, route.route)
97
- });
98
- } else if (config.prerenderConflictBehavior === "warn") {
99
- const msg = AstroErrorData.PrerenderRouteConflict.message(
100
- matchedRoute.route,
101
- route.route,
102
- normalized
103
- );
104
- logger.warn("build", msg);
105
- }
106
- return false;
107
- });
108
- if (config.build.concurrency > 1) {
109
- const limit = PLimit(config.build.concurrency);
110
- const BATCH_SIZE = 1e5;
111
- for (let i = 0; i < filteredPaths.length; i += BATCH_SIZE) {
112
- const batch = filteredPaths.slice(i, i + BATCH_SIZE);
113
- const promises = [];
114
- for (const { pathname, route } of batch) {
115
- promises.push(
116
- limit(
117
- () => generatePathWithPrerenderer(
118
- prerenderer,
119
- pathname,
120
- route,
121
- options,
122
- routeToHeaders,
123
- logger
105
+ );
106
+ logger.warn("build", msg);
107
+ }
108
+ return false;
109
+ });
110
+ if (config.build.concurrency > 1) {
111
+ const limit = PLimit(config.build.concurrency);
112
+ const BATCH_SIZE = 1e5;
113
+ for (let i = 0; i < filteredPaths.length; i += BATCH_SIZE) {
114
+ const batch = filteredPaths.slice(i, i + BATCH_SIZE);
115
+ const promises = [];
116
+ for (const { pathname, route } of batch) {
117
+ promises.push(
118
+ limit(
119
+ () => generatePathWithPrerenderer(
120
+ prerenderer,
121
+ pathname,
122
+ route,
123
+ options,
124
+ routeToHeaders,
125
+ logger
126
+ )
124
127
  )
125
- )
128
+ );
129
+ }
130
+ await Promise.all(promises);
131
+ }
132
+ } else {
133
+ for (const { pathname, route } of filteredPaths) {
134
+ await generatePathWithPrerenderer(
135
+ prerenderer,
136
+ pathname,
137
+ route,
138
+ options,
139
+ routeToHeaders,
140
+ logger
126
141
  );
127
142
  }
128
- await Promise.all(promises);
129
- }
130
- } else {
131
- for (const { pathname, route } of filteredPaths) {
132
- await generatePathWithPrerenderer(
133
- prerenderer,
134
- pathname,
135
- route,
136
- options,
137
- routeToHeaders,
138
- logger
139
- );
140
143
  }
141
- }
142
- for (const { route: generatedRoute } of filteredPaths) {
143
- if (generatedRoute.distURL && generatedRoute.distURL.length > 0) {
144
- for (const pageData of Object.values(options.allPages)) {
145
- if (pageData.route.route === generatedRoute.route && pageData.route.component === generatedRoute.component) {
146
- pageData.route.distURL = generatedRoute.distURL;
147
- break;
144
+ for (const { route: generatedRoute } of filteredPaths) {
145
+ if (generatedRoute.distURL && generatedRoute.distURL.length > 0) {
146
+ for (const pageData of Object.values(options.allPages)) {
147
+ if (pageData.route.route === generatedRoute.route && pageData.route.component === generatedRoute.component) {
148
+ pageData.route.distURL = generatedRoute.distURL;
149
+ break;
150
+ }
148
151
  }
149
152
  }
150
153
  }
151
- }
152
- const staticImageList = getStaticImageList();
153
- if (prerenderer.collectStaticImages) {
154
- const adapterImages = await prerenderer.collectStaticImages();
155
- for (const [path, entry] of adapterImages) {
156
- staticImageList.set(path, entry);
154
+ staticImageList = getStaticImageList();
155
+ if (prerenderer.collectStaticImages) {
156
+ const adapterImages = await prerenderer.collectStaticImages();
157
+ for (const [path, entry] of adapterImages) {
158
+ staticImageList.set(path, entry);
159
+ }
157
160
  }
161
+ } finally {
162
+ await prerenderer.teardown?.();
158
163
  }
159
- await prerenderer.teardown?.();
160
164
  logger.info(
161
165
  null,
162
166
  colors.green(`\u2713 Completed in ${getTimeStat(generatePagesTimer, performance.now())}.
@@ -328,7 +332,11 @@ function getUrlForPath(pathname, base, origin, format, trailingSlash, routeType)
328
332
  }
329
333
  let buildPathname;
330
334
  if (pathname === "/" || pathname === "") {
331
- buildPathname = collapseDuplicateTrailingSlashes(base + ending, trailingSlash !== "never");
335
+ if (format === "file") {
336
+ buildPathname = joinPaths(base, "index.html");
337
+ } else {
338
+ buildPathname = collapseDuplicateTrailingSlashes(base + ending, trailingSlash !== "never");
339
+ }
332
340
  } else if (routeType === "endpoint") {
333
341
  const buildPathRelative = removeLeadingForwardSlash(pathname);
334
342
  buildPathname = joinPaths(base, buildPathRelative);
@@ -1,4 +1,6 @@
1
- import type { AstroInlineConfig } from '../../types/public/config.js';
1
+ import type { AstroSettings, RoutesList } from '../../types/astro.js';
2
+ import type { AstroInlineConfig, RuntimeMode } from '../../types/public/config.js';
3
+ import type { Logger } from '../logger/core.js';
2
4
  interface BuildOptions {
3
5
  /**
4
6
  * Output a development-based build similar to code transformed in `astro dev`. This
@@ -25,4 +27,40 @@ interface BuildOptions {
25
27
  * @experimental The JavaScript API is experimental
26
28
  */
27
29
  export default function build(inlineConfig: AstroInlineConfig, options?: BuildOptions): Promise<void>;
30
+ interface AstroBuilderOptions extends BuildOptions {
31
+ logger: Logger;
32
+ mode: string;
33
+ runtimeMode: RuntimeMode;
34
+ /**
35
+ * Provide a pre-built routes list to skip filesystem route scanning.
36
+ * Useful for testing builds with in-memory virtual modules.
37
+ */
38
+ routesList?: RoutesList;
39
+ /**
40
+ * Whether to run `syncInternal` during setup. Defaults to true.
41
+ * Set to false for in-memory builds that don't need type generation.
42
+ */
43
+ sync?: boolean;
44
+ }
45
+ export declare class AstroBuilder {
46
+ private settings;
47
+ private logger;
48
+ private mode;
49
+ private runtimeMode;
50
+ private origin;
51
+ private routesList;
52
+ private timer;
53
+ private teardownCompiler;
54
+ private sync;
55
+ constructor(settings: AstroSettings, options: AstroBuilderOptions);
56
+ /** Setup Vite and run any async setup logic that couldn't run inside of the constructor. */
57
+ private setup;
58
+ /** Run the build logic. build() is marked private because usage should go through ".run()" */
59
+ private build;
60
+ /** Build the given Astro project. */
61
+ run(): Promise<void>;
62
+ private validateConfig;
63
+ /** Stats */
64
+ private printStats;
65
+ }
28
66
  export {};
@@ -56,14 +56,16 @@ class AstroBuilder {
56
56
  routesList;
57
57
  timer;
58
58
  teardownCompiler;
59
+ sync;
59
60
  constructor(settings, options) {
60
61
  this.mode = options.mode;
61
62
  this.runtimeMode = options.runtimeMode;
62
63
  this.settings = settings;
63
64
  this.logger = options.logger;
64
65
  this.teardownCompiler = options.teardownCompiler ?? true;
66
+ this.sync = options.sync ?? true;
65
67
  this.origin = settings.config.site ? new URL(settings.config.site).origin : `http://localhost:${settings.config.server.port}`;
66
- this.routesList = { routes: [] };
68
+ this.routesList = options.routesList ?? { routes: [] };
67
69
  this.timer = {};
68
70
  }
69
71
  /** Setup Vite and run any async setup logic that couldn't run inside of the constructor. */
@@ -77,7 +79,9 @@ class AstroBuilder {
77
79
  logger
78
80
  });
79
81
  this.settings.buildOutput = getPrerenderDefault(this.settings.config) ? "static" : "server";
80
- this.routesList = await createRoutesList({ settings: this.settings }, this.logger);
82
+ if (this.routesList.routes.length === 0) {
83
+ this.routesList = await createRoutesList({ settings: this.settings }, this.logger);
84
+ }
81
85
  await runHookConfigDone({ settings: this.settings, logger, command: "build" });
82
86
  if (!this.settings.config.adapter && this.settings.buildOutput === "server") {
83
87
  throw new AstroError(AstroErrorData.NoAdapterInstalled);
@@ -98,14 +102,16 @@ class AstroBuilder {
98
102
  sync: false
99
103
  }
100
104
  );
101
- const { syncInternal } = await import("../sync/index.js");
102
- await syncInternal({
103
- mode: this.mode,
104
- settings: this.settings,
105
- logger,
106
- fs,
107
- command: "build"
108
- });
105
+ if (this.sync) {
106
+ const { syncInternal } = await import("../sync/index.js");
107
+ await syncInternal({
108
+ mode: this.mode,
109
+ settings: this.settings,
110
+ logger,
111
+ fs,
112
+ command: "build"
113
+ });
114
+ }
109
115
  return { viteConfig };
110
116
  }
111
117
  /** Run the build logic. build() is marked private because usage should go through ".run()" */
@@ -215,5 +221,6 @@ class AstroBuilder {
215
221
  }
216
222
  }
217
223
  export {
224
+ AstroBuilder,
218
225
  build as default
219
226
  };
@@ -1,7 +1,17 @@
1
1
  import { normalizeEntryId } from "./plugin-component-entry.js";
2
2
  import { ASTRO_VITE_ENVIRONMENT_NAMES } from "../../constants.js";
3
+ function getRollupInputAsSet(rollupInput) {
4
+ if (Array.isArray(rollupInput)) {
5
+ return new Set(rollupInput);
6
+ } else if (typeof rollupInput === "string") {
7
+ return /* @__PURE__ */ new Set([rollupInput]);
8
+ } else if (rollupInput && typeof rollupInput === "object") {
9
+ return new Set(Object.values(rollupInput));
10
+ } else {
11
+ return /* @__PURE__ */ new Set();
12
+ }
13
+ }
3
14
  function pluginInternals(options, internals) {
4
- let input;
5
15
  return {
6
16
  name: "@astro/plugin-build-internals",
7
17
  applyToEnvironment(environment) {
@@ -27,19 +37,8 @@ function pluginInternals(options, internals) {
27
37
  };
28
38
  }
29
39
  },
30
- configResolved(config) {
31
- const rollupInput = config.build?.rollupOptions?.input;
32
- if (Array.isArray(rollupInput)) {
33
- input = new Set(rollupInput);
34
- } else if (typeof rollupInput === "string") {
35
- input = /* @__PURE__ */ new Set([rollupInput]);
36
- } else if (rollupInput && typeof rollupInput === "object") {
37
- input = new Set(Object.values(rollupInput));
38
- } else {
39
- input = /* @__PURE__ */ new Set();
40
- }
41
- },
42
40
  async generateBundle(_options, bundle) {
41
+ const input = getRollupInputAsSet(this.environment?.config.build.rollupOptions.input);
43
42
  const promises = [];
44
43
  const mapping = /* @__PURE__ */ new Map();
45
44
  const allInput = /* @__PURE__ */ new Set([...input, ...internals.clientInput]);
@@ -10,7 +10,11 @@ import { emptyDir, removeEmptyDirs } from "../../core/fs/index.js";
10
10
  import { appendForwardSlash, prependForwardSlash } from "../../core/path.js";
11
11
  import { runHookBuildSetup } from "../../integrations/hooks.js";
12
12
  import { SERIALIZED_MANIFEST_RESOLVED_ID } from "../../manifest/serialized.js";
13
- import { getClientOutputDirectory, getServerOutputDirectory } from "../../prerender/utils.js";
13
+ import {
14
+ getClientOutputDirectory,
15
+ getPrerenderOutputDirectory,
16
+ getServerOutputDirectory
17
+ } from "../../prerender/utils.js";
14
18
  import { VIRTUAL_PAGE_RESOLVED_MODULE_ID } from "../../vite-plugin-pages/const.js";
15
19
  import { PAGE_SCRIPT_ID } from "../../vite-plugin-scripts/index.js";
16
20
  import { routeIsRedirect } from "../routing/helpers.js";
@@ -30,6 +34,7 @@ import { encodeName, getTimeStat, viteBuildReturnToRollupOutputs } from "./util.
30
34
  import { NOOP_MODULE_ID } from "./plugins/plugin-noop.js";
31
35
  import { ASTRO_VITE_ENVIRONMENT_NAMES } from "../constants.js";
32
36
  import { getSSRAssets } from "./internal.js";
37
+ import { SERVER_ISLAND_MAP_MARKER } from "../server-islands/vite-plugin-server-islands.js";
33
38
  const PRERENDER_ENTRY_FILENAME_PREFIX = "prerender-entry";
34
39
  function extractRelevantChunks(outputs, prerender) {
35
40
  const extracted = [];
@@ -38,7 +43,8 @@ function extractRelevantChunks(outputs, prerender) {
38
43
  if (chunk.type === "asset") continue;
39
44
  const needsContentInjection = chunk.code.includes(LINKS_PLACEHOLDER);
40
45
  const needsManifestInjection = chunk.moduleIds.includes(SERIALIZED_MANIFEST_RESOLVED_ID);
41
- if (needsContentInjection || needsManifestInjection) {
46
+ const needsServerIslandInjection = chunk.code.includes(SERVER_ISLAND_MAP_MARKER);
47
+ if (needsContentInjection || needsManifestInjection || needsServerIslandInjection) {
42
48
  extracted.push({
43
49
  fileName: chunk.fileName,
44
50
  code: chunk.code,
@@ -82,6 +88,7 @@ async function buildEnvironments(opts, internals) {
82
88
  const flatPlugins = buildPlugins.flat().filter(Boolean);
83
89
  const plugins = [...flatPlugins, ...viteConfig.plugins || []];
84
90
  let currentRollupInput = void 0;
91
+ let buildPostHooks = [];
85
92
  plugins.push({
86
93
  name: "astro:resolve-input",
87
94
  // When the rollup input is safe to update, we normalize it to always be an object
@@ -107,8 +114,13 @@ async function buildEnvironments(opts, internals) {
107
114
  buildApp: {
108
115
  order: "post",
109
116
  async handler() {
110
- await runManifestInjection(opts, internals, internals.extractedChunks ?? []);
111
- const prerenderOutputDir = new URL("./.prerender/", getServerOutputDirectory(settings));
117
+ await runManifestInjection(
118
+ opts,
119
+ internals,
120
+ internals.extractedChunks ?? [],
121
+ buildPostHooks
122
+ );
123
+ const prerenderOutputDir = getPrerenderOutputDirectory(settings);
112
124
  if (settings.buildOutput === "static") {
113
125
  settings.timer.start("Static generate");
114
126
  await ssrMoveAssets(opts, internals, prerenderOutputDir);
@@ -206,6 +218,13 @@ async function buildEnvironments(opts, internals) {
206
218
  // This takes precedence over platform plugin fallbacks (e.g., Cloudflare)
207
219
  builder: {
208
220
  async buildApp(builder2) {
221
+ settings.timer.start("Prerender build");
222
+ let prerenderOutput = await builder2.build(builder2.environments.prerender);
223
+ settings.timer.end("Prerender build");
224
+ extractPrerenderEntryFileName(internals, prerenderOutput);
225
+ const prerenderOutputs = viteBuildReturnToRollupOutputs(prerenderOutput);
226
+ const prerenderChunks = extractRelevantChunks(prerenderOutputs, true);
227
+ prerenderOutput = void 0;
209
228
  let ssrChunks = [];
210
229
  if (settings.buildOutput !== "static") {
211
230
  settings.timer.start("SSR build");
@@ -217,13 +236,10 @@ async function buildEnvironments(opts, internals) {
217
236
  ssrChunks = extractRelevantChunks(ssrOutputs, false);
218
237
  ssrOutput = void 0;
219
238
  }
220
- settings.timer.start("Prerender build");
221
- let prerenderOutput = await builder2.build(builder2.environments.prerender);
222
- settings.timer.end("Prerender build");
223
- extractPrerenderEntryFileName(internals, prerenderOutput);
224
- const prerenderOutputs = viteBuildReturnToRollupOutputs(prerenderOutput);
225
- const prerenderChunks = extractRelevantChunks(prerenderOutputs, true);
226
- prerenderOutput = void 0;
239
+ const ssrPlugins = builder2.environments[ASTRO_VITE_ENVIRONMENT_NAMES.ssr]?.config.plugins ?? [];
240
+ buildPostHooks = ssrPlugins.map(
241
+ (plugin) => typeof plugin.api?.buildPostHook === "function" ? plugin.api.buildPostHook : void 0
242
+ ).filter(Boolean);
227
243
  internals.clientInput = getClientInput(internals, settings);
228
244
  if (!internals.clientInput.size) {
229
245
  internals.clientInput.add(NOOP_MODULE_ID);
@@ -244,7 +260,7 @@ async function buildEnvironments(opts, internals) {
244
260
  [ASTRO_VITE_ENVIRONMENT_NAMES.prerender]: {
245
261
  build: {
246
262
  emitAssets: true,
247
- outDir: fileURLToPath(new URL("./.prerender/", getServerOutputDirectory(settings))),
263
+ outDir: fileURLToPath(getPrerenderOutputDirectory(settings)),
248
264
  rollupOptions: {
249
265
  // Only skip the default prerender entrypoint if an adapter with `entrypointResolution: 'self'` is used
250
266
  // AND provides a custom prerenderer. Otherwise, use the default.
@@ -318,7 +334,7 @@ function getPrerenderEntryFileName(prerenderOutput) {
318
334
  function extractPrerenderEntryFileName(internals, prerenderOutput) {
319
335
  internals.prerenderEntryFileName = getPrerenderEntryFileName(prerenderOutput);
320
336
  }
321
- async function runManifestInjection(opts, internals, chunks) {
337
+ async function runManifestInjection(opts, internals, chunks, buildPostHooks) {
322
338
  const mutations = /* @__PURE__ */ new Map();
323
339
  const mutate = (fileName, newCode, prerender) => {
324
340
  mutations.set(fileName, { code: newCode, prerender });
@@ -330,16 +346,18 @@ async function runManifestInjection(opts, internals, chunks) {
330
346
  internals,
331
347
  { chunks, mutate }
332
348
  );
349
+ for (const buildPostHook of buildPostHooks) {
350
+ await buildPostHook({ chunks, mutate });
351
+ }
333
352
  await writeMutatedChunks(opts, mutations);
334
353
  }
335
354
  async function writeMutatedChunks(opts, mutations) {
336
355
  const { settings } = opts;
337
356
  const config = settings.config;
338
- const serverOutputDir = getServerOutputDirectory(settings);
339
357
  for (const [fileName, mutation] of mutations) {
340
358
  let root;
341
359
  if (mutation.prerender) {
342
- root = new URL("./.prerender/", serverOutputDir);
360
+ root = getPrerenderOutputDirectory(settings);
343
361
  } else if (settings.buildOutput === "server") {
344
362
  root = config.build.server;
345
363
  } else {
@@ -1,4 +1,4 @@
1
- const ASTRO_VERSION = "6.0.3";
1
+ const ASTRO_VERSION = "6.0.5";
2
2
  const ASTRO_GENERATOR = `Astro v${ASTRO_VERSION}`;
3
3
  const REROUTE_DIRECTIVE_HEADER = "X-Astro-Reroute";
4
4
  const REWRITE_DIRECTIVE_HEADER_KEY = "X-Astro-Rewrite";
@@ -26,7 +26,7 @@ async function dev(inlineConfig) {
26
26
  await telemetry.record([]);
27
27
  const restart = await createContainerWithAutomaticRestart({ inlineConfig, fs });
28
28
  const logger = restart.container.logger;
29
- const currentVersion = "6.0.3";
29
+ const currentVersion = "6.0.5";
30
30
  const isPrerelease = currentVersion.includes("-");
31
31
  if (!isPrerelease) {
32
32
  try {
@@ -90,7 +90,7 @@ function collectErrorMetadata(e, rootFolder) {
90
90
  function generateHint(err) {
91
91
  const commonBrowserAPIs = ["document", "window"];
92
92
  if (/Unknown file extension "\.(?:jsx|vue|svelte|astro|css)" for /.test(err.message)) {
93
- return "You likely need to add this package to `vite.ssr.noExternal` in your astro config file.";
93
+ return "You likely need to add this package to `vite.resolve.noExternal` in your astro config file.";
94
94
  } else if (commonBrowserAPIs.some((api) => err.toString().includes(api))) {
95
95
  const hint = `Browser APIs are not available on the server.
96
96
 
@@ -269,7 +269,7 @@ function printHelp({
269
269
  message.push(
270
270
  linebreak(),
271
271
  ` ${bgGreen(black(` ${commandName} `))} ${green(
272
- `v${"6.0.3"}`
272
+ `v${"6.0.5"}`
273
273
  )} ${headline}`
274
274
  );
275
275
  }
@@ -1,4 +1,5 @@
1
1
  import type { RouteData } from '../../types/public/internal.js';
2
+ import type { IntegrationResolvedRoute } from '../../types/public/integrations.js';
2
3
  import type { RouteInfo } from '../app/types.js';
3
4
  import type { RoutesList } from '../../types/astro.js';
4
5
  type RedirectRouteData = RouteData & {
@@ -31,4 +32,10 @@ export declare function getCustom404Route(manifestData: RoutesList): RouteData |
31
32
  * Return a user-provided 500 route if one exists.
32
33
  */
33
34
  export declare function getCustom500Route(manifestData: RoutesList): RouteData | undefined;
35
+ export declare function hasNonPrerenderedProjectRoute(routes: Array<Pick<RouteData, 'type' | 'origin' | 'prerender'>>, options?: {
36
+ includeEndpoints?: boolean;
37
+ }): boolean;
38
+ export declare function hasNonPrerenderedProjectRoute(routes: Array<Pick<IntegrationResolvedRoute, 'type' | 'origin' | 'isPrerendered'>>, options?: {
39
+ includeEndpoints?: boolean;
40
+ }): boolean;
34
41
  export {};
@@ -25,10 +25,19 @@ function getCustom404Route(manifestData) {
25
25
  function getCustom500Route(manifestData) {
26
26
  return manifestData.routes.find((r) => isRoute500(r.route));
27
27
  }
28
+ function hasNonPrerenderedProjectRoute(routes, options) {
29
+ const includeEndpoints = options?.includeEndpoints ?? true;
30
+ const routeTypes = includeEndpoints ? ["page", "endpoint"] : ["page"];
31
+ return routes.some((route) => {
32
+ const isPrerendered = "isPrerendered" in route ? route.isPrerendered : route.prerender;
33
+ return routeTypes.includes(route.type) && route.origin === "project" && !isPrerendered;
34
+ });
35
+ }
28
36
  export {
29
37
  getCustom404Route,
30
38
  getCustom500Route,
31
39
  getFallbackRoute,
40
+ hasNonPrerenderedProjectRoute,
32
41
  routeIsFallback,
33
42
  routeIsRedirect
34
43
  };
@@ -1,4 +1,5 @@
1
1
  import type { Plugin as VitePlugin } from 'vite';
2
2
  import type { AstroPluginOptions } from '../../types/astro.js';
3
3
  export declare const SERVER_ISLAND_MANIFEST = "virtual:astro:server-island-manifest";
4
+ export declare const SERVER_ISLAND_MAP_MARKER = "$$server-islands-map$$";
4
5
  export declare function vitePluginServerIslands({ settings }: AstroPluginOptions): VitePlugin;
@@ -1,22 +1,56 @@
1
- import MagicString from "magic-string";
2
- import { AstroError, AstroErrorData } from "../errors/index.js";
3
1
  import { ASTRO_VITE_ENVIRONMENT_NAMES } from "../constants.js";
2
+ import { AstroError, AstroErrorData } from "../errors/index.js";
4
3
  const SERVER_ISLAND_MANIFEST = "virtual:astro:server-island-manifest";
5
4
  const RESOLVED_SERVER_ISLAND_MANIFEST = "\0" + SERVER_ISLAND_MANIFEST;
6
5
  const serverIslandPlaceholderMap = "'$$server-islands-map$$'";
7
6
  const serverIslandPlaceholderNameMap = "'$$server-islands-name-map$$'";
7
+ const SERVER_ISLAND_MAP_MARKER = "$$server-islands-map$$";
8
+ const serverIslandMapReplaceExp = /['"]\$\$server-islands-map\$\$['"]/g;
9
+ const serverIslandNameMapReplaceExp = /['"]\$\$server-islands-name-map\$\$['"]/g;
10
+ function createServerIslandImportMapSource(entries, toImportPath) {
11
+ const mappings = Array.from(entries, ([islandName, fileName]) => {
12
+ const importPath = toImportPath(fileName);
13
+ return ` [${JSON.stringify(islandName)}, () => import(${JSON.stringify(importPath)})],`;
14
+ });
15
+ return `new Map([
16
+ ${mappings.join("\n")}
17
+ ])`;
18
+ }
19
+ function createNameMapSource(entries) {
20
+ return `new Map(${JSON.stringify(Array.from(entries), null, 2)})`;
21
+ }
8
22
  function vitePluginServerIslands({ settings }) {
9
23
  let command = "serve";
10
24
  let ssrEnvironment = null;
11
- const referenceIdMap = /* @__PURE__ */ new Map();
12
25
  const serverIslandMap = /* @__PURE__ */ new Map();
13
26
  const serverIslandNameMap = /* @__PURE__ */ new Map();
27
+ const serverIslandSourceMap = /* @__PURE__ */ new Map();
28
+ const referenceIdMap = /* @__PURE__ */ new Map();
29
+ function ensureServerIslandReferenceIds(ctx) {
30
+ for (const [resolvedPath, islandName] of serverIslandNameMap) {
31
+ if (referenceIdMap.has(resolvedPath)) continue;
32
+ const source = serverIslandSourceMap.get(resolvedPath);
33
+ const referenceId = ctx.emitFile({
34
+ type: "chunk",
35
+ id: source?.id ?? resolvedPath,
36
+ importer: source?.importer,
37
+ name: islandName
38
+ });
39
+ referenceIdMap.set(resolvedPath, referenceId);
40
+ }
41
+ }
14
42
  return {
15
43
  name: "astro:server-islands",
16
44
  enforce: "post",
17
45
  config(_config, { command: _command }) {
18
46
  command = _command;
19
47
  },
48
+ buildStart() {
49
+ if (command !== "build" || this.environment?.name !== ASTRO_VITE_ENVIRONMENT_NAMES.ssr) {
50
+ return;
51
+ }
52
+ ensureServerIslandReferenceIds(this);
53
+ },
20
54
  configureServer(server) {
21
55
  ssrEnvironment = server.environments[ASTRO_VITE_ENVIRONMENT_NAMES.ssr];
22
56
  },
@@ -43,16 +77,13 @@ export const serverIslandNameMap = ${serverIslandPlaceholderNameMap};`
43
77
  transform: {
44
78
  filter: {
45
79
  id: {
46
- include: [
47
- // Allows server islands in astro and mdx files
48
- /\.(astro|mdx)$/,
49
- new RegExp(`^${RESOLVED_SERVER_ISLAND_MANIFEST}$`)
50
- ]
80
+ include: [/\.(astro|mdx)$/, new RegExp(`^${RESOLVED_SERVER_ISLAND_MANIFEST}$`)]
51
81
  }
52
82
  },
53
83
  async handler(_code, id) {
54
84
  const info = this.getModuleInfo(id);
55
85
  const astro = info ? info.meta.astro : void 0;
86
+ const isBuildSsr = command === "build" && this.environment?.name === ASTRO_VITE_ENVIRONMENT_NAMES.ssr;
56
87
  if (astro) {
57
88
  for (const comp of astro.serverComponents) {
58
89
  if (!serverIslandNameMap.has(comp.resolvedPath)) {
@@ -61,23 +92,23 @@ export const serverIslandNameMap = ${serverIslandPlaceholderNameMap};`
61
92
  }
62
93
  let name = comp.localName;
63
94
  let idx = 1;
64
- while (true) {
65
- if (!serverIslandMap.has(name)) {
66
- break;
67
- }
95
+ while (serverIslandMap.has(name)) {
68
96
  name += idx++;
69
97
  }
70
98
  serverIslandNameMap.set(comp.resolvedPath, name);
71
99
  serverIslandMap.set(name, comp.resolvedPath);
72
- if (command === "build") {
73
- let referenceId = this.emitFile({
74
- type: "chunk",
75
- id: comp.specifier,
76
- importer: id,
77
- name: comp.localName
78
- });
79
- referenceIdMap.set(comp.resolvedPath, referenceId);
80
- }
100
+ serverIslandSourceMap.set(comp.resolvedPath, { id: comp.specifier, importer: id });
101
+ }
102
+ if (isBuildSsr && !referenceIdMap.has(comp.resolvedPath)) {
103
+ const islandName = serverIslandNameMap.get(comp.resolvedPath);
104
+ const source = serverIslandSourceMap.get(comp.resolvedPath);
105
+ const referenceId = this.emitFile({
106
+ type: "chunk",
107
+ id: source?.id ?? comp.resolvedPath,
108
+ importer: source?.importer,
109
+ name: islandName
110
+ });
111
+ referenceIdMap.set(comp.resolvedPath, referenceId);
81
112
  }
82
113
  }
83
114
  }
@@ -94,59 +125,60 @@ export const serverIslandNameMap = ${serverIslandPlaceholderNameMap};`
94
125
  throw new AstroError(AstroErrorData.NoAdapterInstalledServerIslands);
95
126
  }
96
127
  }
97
- if (serverIslandNameMap.size > 0 && serverIslandMap.size > 0) {
98
- let mapSource = "new Map([\n ";
99
- for (let [name, path] of serverIslandMap) {
100
- mapSource += `
101
- ['${name}', () => import('${path}')],`;
102
- }
103
- mapSource += "]);";
128
+ if (command !== "build" && serverIslandNameMap.size > 0 && serverIslandMap.size > 0) {
129
+ const mapSource = createServerIslandImportMapSource(
130
+ serverIslandMap,
131
+ (fileName) => fileName
132
+ );
133
+ const nameMapSource = createNameMapSource(serverIslandNameMap);
104
134
  return {
105
135
  code: `
106
- export const serverIslandMap = ${mapSource};
107
-
136
+ export const serverIslandMap = ${mapSource};
108
137
 
109
- export const serverIslandNameMap = new Map(${JSON.stringify(Array.from(serverIslandNameMap.entries()), null, 2)});
110
- `
138
+ export const serverIslandNameMap = ${nameMapSource};
139
+ `
111
140
  };
112
141
  }
113
142
  }
114
143
  }
115
144
  },
116
145
  renderChunk(code, chunk) {
117
- if (code.includes(serverIslandPlaceholderMap)) {
118
- if (referenceIdMap.size === 0) {
119
- return {
120
- code: code.replace(serverIslandPlaceholderMap, "new Map();").replace(serverIslandPlaceholderNameMap, "new Map()"),
121
- map: null
122
- };
123
- }
124
- const isRelativeChunk = !chunk.isEntry;
125
- const dots = isRelativeChunk ? ".." : ".";
126
- let mapSource = "new Map([";
127
- let nameMapSource = "new Map(";
128
- for (let [resolvedPath, referenceId] of referenceIdMap) {
129
- const fileName = this.getFileName(referenceId);
130
- const islandName = serverIslandNameMap.get(resolvedPath);
131
- mapSource += `
132
- ['${islandName}', () => import('${dots}/${fileName}')],`;
146
+ if (!code.includes(SERVER_ISLAND_MAP_MARKER)) return;
147
+ if (command === "build") {
148
+ const envName = this.environment?.name;
149
+ let mapSource;
150
+ if (envName === ASTRO_VITE_ENVIRONMENT_NAMES.ssr) {
151
+ const isRelativeChunk = !chunk.isEntry;
152
+ const dots = isRelativeChunk ? ".." : ".";
153
+ const mapEntries = [];
154
+ for (const [resolvedPath, referenceId] of referenceIdMap) {
155
+ const fileName = this.getFileName(referenceId);
156
+ const islandName = serverIslandNameMap.get(resolvedPath);
157
+ if (!islandName) continue;
158
+ mapEntries.push([islandName, fileName]);
159
+ }
160
+ mapSource = createServerIslandImportMapSource(
161
+ mapEntries,
162
+ (fileName) => `${dots}/${fileName}`
163
+ );
164
+ } else {
165
+ mapSource = createServerIslandImportMapSource(serverIslandMap, (fileName) => fileName);
133
166
  }
134
- nameMapSource += `${JSON.stringify(Array.from(serverIslandNameMap.entries()), null, 2)}`;
135
- mapSource += "\n])";
136
- nameMapSource += "\n)";
137
- referenceIdMap.clear();
138
- const ms = new MagicString(code);
139
- ms.replace(serverIslandPlaceholderMap, mapSource);
140
- ms.replace(serverIslandPlaceholderNameMap, nameMapSource);
167
+ const nameMapSource = createNameMapSource(serverIslandNameMap);
141
168
  return {
142
- code: ms.toString(),
143
- map: ms.generateMap({ hires: "boundary" })
169
+ code: code.replace(serverIslandMapReplaceExp, mapSource).replace(serverIslandNameMapReplaceExp, nameMapSource),
170
+ map: null
144
171
  };
145
172
  }
173
+ return {
174
+ code: code.replace(serverIslandMapReplaceExp, "new Map();").replace(serverIslandNameMapReplaceExp, "new Map()"),
175
+ map: null
176
+ };
146
177
  }
147
178
  };
148
179
  }
149
180
  export {
150
181
  SERVER_ISLAND_MANIFEST,
182
+ SERVER_ISLAND_MAP_MARKER,
151
183
  vitePluginServerIslands
152
184
  };
@@ -74,9 +74,10 @@ class I18nRouter {
74
74
  matchPrefixAlways(pathname, _context) {
75
75
  const isRoot = pathname === this.#base + "/" || pathname === this.#base;
76
76
  if (isRoot) {
77
+ const basePrefix = this.#base === "/" ? "" : this.#base;
77
78
  return {
78
79
  type: "redirect",
79
- location: `${this.#base}/${this.#defaultLocale}`
80
+ location: `${basePrefix}/${this.#defaultLocale}`
80
81
  };
81
82
  }
82
83
  if (!pathHasLocale(pathname, this.#locales)) {
@@ -5,6 +5,10 @@ export declare function getPrerenderDefault(config: AstroConfig): boolean;
5
5
  * Returns the correct output directory of the SSR build based on the configuration
6
6
  */
7
7
  export declare function getServerOutputDirectory(settings: AstroSettings): URL;
8
+ /**
9
+ * Returns the output directory used by the prerender environment.
10
+ */
11
+ export declare function getPrerenderOutputDirectory(settings: AstroSettings): URL;
8
12
  /**
9
13
  * Returns the correct output directory of the client build based on the configuration
10
14
  */
@@ -5,6 +5,9 @@ function getPrerenderDefault(config) {
5
5
  function getServerOutputDirectory(settings) {
6
6
  return settings.buildOutput === "server" ? settings.config.build.server : getOutDirWithinCwd(settings.config.outDir);
7
7
  }
8
+ function getPrerenderOutputDirectory(settings) {
9
+ return new URL("./.prerender/", getServerOutputDirectory(settings));
10
+ }
8
11
  function getClientOutputDirectory(settings) {
9
12
  const preserveStructure = settings.adapter?.adapterFeatures?.preserveBuildClientDir;
10
13
  if (settings.buildOutput === "server" || preserveStructure) {
@@ -15,5 +18,6 @@ function getClientOutputDirectory(settings) {
15
18
  export {
16
19
  getClientOutputDirectory,
17
20
  getPrerenderDefault,
21
+ getPrerenderOutputDirectory,
18
22
  getServerOutputDirectory
19
23
  };
@@ -10,7 +10,11 @@ function astroDevToolbar({ settings, logger }) {
10
10
  return {
11
11
  optimizeDeps: {
12
12
  // Optimize CJS dependencies used by the dev toolbar
13
- include: ["astro > aria-query", "astro > axobject-query"]
13
+ include: [
14
+ "astro > aria-query",
15
+ "astro > axobject-query",
16
+ ...settings.devToolbarApps.length > 0 ? ["astro/toolbar"] : []
17
+ ]
14
18
  }
15
19
  };
16
20
  },
@@ -19,7 +19,7 @@ import { getViteErrorPayload } from "../core/errors/dev/index.js";
19
19
  import { AstroError, AstroErrorData } from "../core/errors/index.js";
20
20
  import { NOOP_MIDDLEWARE_FN } from "../core/middleware/noop-middleware.js";
21
21
  import { createViteLoader } from "../core/module-loader/index.js";
22
- import { matchAllRoutes } from "../core/routing/match.js";
22
+ import { isRouteServerIsland, matchAllRoutes } from "../core/routing/match.js";
23
23
  import { resolveMiddlewareMode } from "../integrations/adapter-utils.js";
24
24
  import { SERIALIZED_MANIFEST_ID } from "../manifest/serialized.js";
25
25
  import { ASTRO_DEV_SERVER_APP_ID } from "../vite-plugin-app/index.js";
@@ -130,7 +130,7 @@ function createVitePluginAstroServer({
130
130
  const { routes } = await prerenderHandler.environment.runner.import("virtual:astro:routes");
131
131
  const routesList = { routes: routes.map((r) => r.routeData) };
132
132
  const matches = matchAllRoutes(pathname, routesList);
133
- if (!matches.some((route) => route.prerender)) {
133
+ if (!matches.some((route) => route.prerender || isRouteServerIsland(route))) {
134
134
  return next();
135
135
  }
136
136
  localStorage.run(request, () => {
@@ -42,9 +42,13 @@ function routeGuardMiddleware(settings) {
42
42
  return next();
43
43
  }
44
44
  const rootFilePath = new URL("." + pathname, config.root);
45
- if (fs.existsSync(rootFilePath)) {
46
- const html = notFoundTemplate(pathname);
47
- return writeHtmlResponse(res, 404, html);
45
+ try {
46
+ const stat = fs.statSync(rootFilePath);
47
+ if (stat.isFile()) {
48
+ const html = notFoundTemplate(pathname);
49
+ return writeHtmlResponse(res, 404, html);
50
+ }
51
+ } catch {
48
52
  }
49
53
  return next();
50
54
  };
@@ -62,7 +62,7 @@ function vitePluginEnvironment({
62
62
  // For the dev toolbar
63
63
  "astro > html-escaper"
64
64
  ],
65
- exclude: ["astro:*", "virtual:astro:*"],
65
+ exclude: ["astro:*", "virtual:astro:*", "astro/virtual-modules/prefetch.js"],
66
66
  // Astro files can't be rendered on the client
67
67
  entries: [`${srcDirPattern}**/*.{jsx,tsx,vue,svelte,html}`]
68
68
  };
@@ -1,11 +1,7 @@
1
1
  import { ASTRO_VITE_ENVIRONMENT_NAMES } from "../core/constants.js";
2
+ import { hasNonPrerenderedProjectRoute } from "../core/routing/helpers.js";
2
3
  const ASTRO_RENDERERS_MODULE_ID = "virtual:astro:renderers";
3
4
  const RESOLVED_ASTRO_RENDERERS_MODULE_ID = `\0${ASTRO_RENDERERS_MODULE_ID}`;
4
- function ssrBuildNeedsRenderers(routesList) {
5
- return routesList.routes.some(
6
- (route) => route.type === "page" && !route.prerender && route.origin !== "internal"
7
- );
8
- }
9
5
  function vitePluginRenderers(options) {
10
6
  const renderers = options.settings.renderers;
11
7
  return {
@@ -24,7 +20,9 @@ function vitePluginRenderers(options) {
24
20
  id: new RegExp(`^${RESOLVED_ASTRO_RENDERERS_MODULE_ID}$`)
25
21
  },
26
22
  handler() {
27
- if (options.command === "build" && this.environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.ssr && renderers.length > 0 && !ssrBuildNeedsRenderers(options.routesList)) {
23
+ if (options.command === "build" && this.environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.ssr && renderers.length > 0 && !hasNonPrerenderedProjectRoute(options.routesList.routes, {
24
+ includeEndpoints: false
25
+ })) {
28
26
  return { code: `export const renderers = [];` };
29
27
  }
30
28
  if (renderers.length > 0) {
@@ -11,6 +11,7 @@ import { rootRelativePath } from "../core/viteUtils.js";
11
11
  import { createDefaultAstroMetadata } from "../vite-plugin-astro/metadata.js";
12
12
  import { ASTRO_VITE_ENVIRONMENT_NAMES } from "../core/constants.js";
13
13
  import { isAstroServerEnvironment } from "../environments.js";
14
+ import { RESOLVED_MODULE_DEV_CSS_ALL } from "../vite-plugin-css/const.js";
14
15
  import { PAGE_SCRIPT_ID } from "../vite-plugin-scripts/index.js";
15
16
  const ASTRO_ROUTES_MODULE_ID = "virtual:astro:routes";
16
17
  const ASTRO_ROUTES_MODULE_ID_RESOLVED = "\0" + ASTRO_ROUTES_MODULE_ID;
@@ -91,6 +92,10 @@ async function astroPluginRoutes({
91
92
  const virtualMod = environment.moduleGraph.getModuleById(ASTRO_ROUTES_MODULE_ID_RESOLVED);
92
93
  if (!virtualMod) continue;
93
94
  environment.moduleGraph.invalidateModule(virtualMod);
95
+ const cssMod = environment.moduleGraph.getModuleById(RESOLVED_MODULE_DEV_CSS_ALL);
96
+ if (cssMod) {
97
+ environment.moduleGraph.invalidateModule(cssMod);
98
+ }
94
99
  environment.hot.send("astro:routes-updated", {});
95
100
  }
96
101
  }
@@ -38,7 +38,7 @@ function astroScriptsPlugin({ settings }) {
38
38
  },
39
39
  buildStart() {
40
40
  const hasHydrationScripts = settings.scripts.some((s) => s.stage === "before-hydration");
41
- if (hasHydrationScripts && (this.environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.prerender || this.environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.ssr)) {
41
+ if (hasHydrationScripts && (this.environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.client || this.environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.prerender || this.environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.ssr)) {
42
42
  this.emitFile({
43
43
  type: "chunk",
44
44
  id: BEFORE_HYDRATION_SCRIPT_ID,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "6.0.3",
3
+ "version": "6.0.5",
4
4
  "description": "Astro is a modern site builder with web best practices, performance, and DX front-of-mind.",
5
5
  "type": "module",
6
6
  "author": "withastro",
@@ -154,8 +154,8 @@
154
154
  "yargs-parser": "^22.0.0",
155
155
  "zod": "^4.3.6",
156
156
  "@astrojs/markdown-remark": "7.0.0",
157
- "@astrojs/telemetry": "3.3.0",
158
- "@astrojs/internal-helpers": "0.8.0"
157
+ "@astrojs/internal-helpers": "0.8.0",
158
+ "@astrojs/telemetry": "3.3.0"
159
159
  },
160
160
  "optionalDependencies": {
161
161
  "sharp": "^0.34.0"
@@ -190,7 +190,7 @@
190
190
  "undici": "^7.22.0",
191
191
  "unified": "^11.0.5",
192
192
  "vitest": "^3.2.4",
193
- "@astrojs/check": "0.9.7",
193
+ "@astrojs/check": "0.9.8",
194
194
  "astro-scripts": "0.0.14"
195
195
  },
196
196
  "engines": {
package/templates/env.mjs CHANGED
@@ -1,4 +1,3 @@
1
- /* eslint-disable @typescript-eslint/no-unused-vars */
2
1
  // @ts-check
3
2
  import { schema } from 'virtual:astro:env/internal';
4
3
  import {