astro 6.3.7 → 6.3.8

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.
@@ -1,6 +1,6 @@
1
1
  class BuildTimeAstroVersionProvider {
2
2
  // Injected during the build through esbuild define
3
- version = "6.3.7";
3
+ version = "6.3.8";
4
4
  }
5
5
  export {
6
6
  BuildTimeAstroVersionProvider
@@ -191,7 +191,7 @@ ${contentConfig.error.message}`
191
191
  logger.info("Content config changed");
192
192
  shouldClear = true;
193
193
  }
194
- if (previousAstroVersion && previousAstroVersion !== "6.3.7") {
194
+ if (previousAstroVersion && previousAstroVersion !== "6.3.8") {
195
195
  logger.info("Astro version changed");
196
196
  shouldClear = true;
197
197
  }
@@ -199,8 +199,8 @@ ${contentConfig.error.message}`
199
199
  logger.info("Clearing content store");
200
200
  this.#store.clearAll();
201
201
  }
202
- if ("6.3.7") {
203
- this.#store.metaStore().set("astro-version", "6.3.7");
202
+ if ("6.3.8") {
203
+ this.#store.metaStore().set("astro-version", "6.3.8");
204
204
  }
205
205
  if (currentConfigDigest) {
206
206
  this.#store.metaStore().set("content-config-digest", currentConfigDigest);
@@ -182,7 +182,8 @@ class Pipeline {
182
182
  if (this.resolvedActions) {
183
183
  return this.resolvedActions;
184
184
  } else if (this.actions) {
185
- return this.actions();
185
+ this.resolvedActions = await this.actions();
186
+ return this.resolvedActions;
186
187
  }
187
188
  return NOOP_ACTIONS_MOD;
188
189
  }
@@ -92,6 +92,13 @@ export interface BuildInternals {
92
92
  moduleIds: string[];
93
93
  prerender: boolean;
94
94
  }>;
95
+ /**
96
+ * Component exports that were rendered during the SSR build.
97
+ * Used by the client build's cssScopeTo recovery to distinguish between
98
+ * CSS that was tree-shaken because the component wasn't rendered in SSR
99
+ * vs CSS that was included in SSR.
100
+ */
101
+ ssrRenderedExports?: Map<string, Set<string>>;
95
102
  }
96
103
  /**
97
104
  * Creates internal maps used to coordinate the CSS and HTML plugins.
@@ -6,5 +6,11 @@ import type { StaticBuildOptions } from '../types.js';
6
6
  * bypass the HTML rendering pipeline and miss skew protection query params.
7
7
  *
8
8
  * Uses es-module-lexer to reliably parse both static and dynamic imports.
9
+ *
10
+ * This runs in `generateBundle` (not `renderChunk`) so that Vite's CSS plugin
11
+ * can first remove pure-CSS wrapper chunks and replace their imports with
12
+ * `/* empty css * /` comments. If we rewrote imports earlier (in `renderChunk`),
13
+ * the appended query params would break Vite's regex-based CSS chunk cleanup,
14
+ * leaving dangling imports to deleted chunks that 404 at runtime.
9
15
  */
10
16
  export declare function pluginChunkImports(options: StaticBuildOptions): VitePlugin | undefined;
@@ -12,25 +12,24 @@ function pluginChunkImports(options) {
12
12
  applyToEnvironment(environment) {
13
13
  return environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.client;
14
14
  },
15
- async renderChunk(code, _chunk) {
16
- if (!code.includes("./")) {
17
- return null;
18
- }
15
+ async generateBundle(_options, bundle) {
19
16
  await init;
20
- const [imports] = parse(code);
21
- const relativeImports = imports.filter(
22
- (imp) => imp.n && /^\.\.?\//.test(imp.n) && /\.(?:js|mjs)$/.test(imp.n)
23
- );
24
- if (relativeImports.length === 0) {
25
- return null;
26
- }
27
- let rewritten = code;
28
- for (let i = relativeImports.length - 1; i >= 0; i--) {
29
- const imp = relativeImports[i];
30
- const insertAt = imp.d > -1 ? imp.e - 1 : imp.e;
31
- rewritten = rewritten.slice(0, insertAt) + "?" + queryString + rewritten.slice(insertAt);
17
+ for (const [, chunk] of Object.entries(bundle)) {
18
+ if (chunk.type !== "chunk") continue;
19
+ if (!chunk.code.includes("./")) continue;
20
+ const [imports] = parse(chunk.code);
21
+ const relativeImports = imports.filter(
22
+ (imp) => imp.n && /^\.\.?\//.test(imp.n) && /\.(?:js|mjs)$/.test(imp.n)
23
+ );
24
+ if (relativeImports.length === 0) continue;
25
+ let rewritten = chunk.code;
26
+ for (let i = relativeImports.length - 1; i >= 0; i--) {
27
+ const imp = relativeImports[i];
28
+ const insertAt = imp.d > -1 ? imp.e - 1 : imp.e;
29
+ rewritten = rewritten.slice(0, insertAt) + "?" + queryString + rewritten.slice(insertAt);
30
+ }
31
+ chunk.code = rewritten;
32
32
  }
33
- return { code: rewritten, map: null };
34
33
  }
35
34
  };
36
35
  }
@@ -46,10 +46,25 @@ function rollupPluginAstroBuildCSS(options) {
46
46
  internals.cssModuleToChunkIdMap.set(moduleId, chunk.fileName);
47
47
  }
48
48
  }
49
+ for (const [moduleId, moduleInfo] of Object.entries(chunk.modules || {})) {
50
+ if (moduleInfo.renderedExports.length > 0) {
51
+ const existing = internals.ssrRenderedExports?.get(moduleId);
52
+ if (existing) {
53
+ for (const exp of moduleInfo.renderedExports) {
54
+ existing.add(exp);
55
+ }
56
+ } else {
57
+ internals.ssrRenderedExports ??= /* @__PURE__ */ new Map();
58
+ internals.ssrRenderedExports.set(moduleId, new Set(moduleInfo.renderedExports));
59
+ }
60
+ }
61
+ }
49
62
  }
50
63
  }
51
64
  const renderedComponentExports = /* @__PURE__ */ new Map();
52
65
  const componentToPages = /* @__PURE__ */ new Map();
66
+ const deletedCssAssets = /* @__PURE__ */ new Map();
67
+ const cssScopeToAddedCss = /* @__PURE__ */ new Set();
53
68
  if (this.environment?.name === ASTRO_VITE_ENVIRONMENT_NAMES.client) {
54
69
  for (const [, item] of Object.entries(bundle)) {
55
70
  if (item.type !== "chunk") continue;
@@ -76,6 +91,9 @@ function rollupPluginAstroBuildCSS(options) {
76
91
  );
77
92
  if (allCssInSSR && shouldDeleteCSSChunk(allModules, internals)) {
78
93
  for (const cssId of meta.importedCss) {
94
+ if (bundle[cssId]) {
95
+ deletedCssAssets.set(cssId, bundle[cssId]);
96
+ }
79
97
  delete bundle[cssId];
80
98
  }
81
99
  }
@@ -157,6 +175,14 @@ function rollupPluginAstroBuildCSS(options) {
157
175
  }
158
176
  }
159
177
  }
178
+ const ssrExports = internals.ssrRenderedExports?.get(scopedToModule);
179
+ if (!ssrExports || !ssrExports.has(scopedToExport)) {
180
+ for (const cssId of meta.importedCss) {
181
+ if (deletedCssAssets.has(cssId)) {
182
+ cssScopeToAddedCss.add(cssId);
183
+ }
184
+ }
185
+ }
160
186
  }
161
187
  }
162
188
  }
@@ -191,6 +217,13 @@ function rollupPluginAstroBuildCSS(options) {
191
217
  }
192
218
  }
193
219
  }
220
+ if (cssScopeToAddedCss.size > 0) {
221
+ for (const cssId of cssScopeToAddedCss) {
222
+ if (deletedCssAssets.has(cssId) && !bundle[cssId]) {
223
+ bundle[cssId] = deletedCssAssets.get(cssId);
224
+ }
225
+ }
226
+ }
194
227
  }
195
228
  };
196
229
  const singleCssPlugin = {
@@ -1,4 +1,4 @@
1
- const ASTRO_VERSION = "6.3.7";
1
+ const ASTRO_VERSION = "6.3.8";
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";
@@ -37,7 +37,7 @@ async function dev(inlineConfig) {
37
37
  await telemetry.record([]);
38
38
  const restart = await createContainerWithAutomaticRestart({ inlineConfig, fs });
39
39
  const logger = restart.container.logger;
40
- const currentVersion = "6.3.7";
40
+ const currentVersion = "6.3.8";
41
41
  const isPrerelease = currentVersion.includes("-");
42
42
  if (!isPrerelease) {
43
43
  try {
@@ -946,17 +946,22 @@ export declare const UnsupportedExternalRedirect: {
946
946
  };
947
947
  /**
948
948
  * @docs
949
+ * @message
950
+ * **Example error message:**<br/>
951
+ * InvalidRedirectDestination: The redirect from "/posts" to "/blog" is invalid. The destination "/blog" does not match any existing route in your project.
949
952
  * @see
950
953
  * - [Configured redirects](https://docs.astro.build/en/guides/routing/#configured-redirects)
951
954
  * @description
952
- * A dynamic redirect destination must match an existing route pattern.
955
+ * A dynamic redirect destination must match an existing route pattern and include
956
+ * all dynamic parameters from the source route.
953
957
  * This error occurs when a redirect with dynamic parameters points to a destination
954
- * that doesn't correspond to any page in your project.
958
+ * that doesn't correspond to any page in your project, or when the destination route
959
+ * has fewer dynamic parameters than the source route.
955
960
  */
956
961
  export declare const InvalidRedirectDestination: {
957
962
  name: string;
958
963
  title: string;
959
- message: (from: string, to: string) => string;
964
+ message(from: string, to: string, missingParams?: string[]): string;
960
965
  hint: string;
961
966
  };
962
967
  /**
@@ -335,8 +335,14 @@ const UnsupportedExternalRedirect = {
335
335
  const InvalidRedirectDestination = {
336
336
  name: "InvalidRedirectDestination",
337
337
  title: "Invalid redirect destination.",
338
- message: (from, to) => `The redirect from "${from}" to "${to}" is invalid. The destination "${to}" does not match any existing route in your project.`,
339
- hint: 'If you are redirecting to a specific page of a dynamic route (e.g., "/posts/[slug]/1"), this is not supported. The destination must be either a static path or a route pattern that matches an existing page (e.g., "/posts/[slug]/[page]").'
338
+ message(from, to, missingParams) {
339
+ if (missingParams && missingParams.length > 0) {
340
+ const formatted = missingParams.map((p) => `[${p}]`).join(", ");
341
+ return `The redirect from "${from}" to "${to}" is invalid. The destination route is missing dynamic parameter(s) ${formatted} required by the source route "${from}".`;
342
+ }
343
+ return `The redirect from "${from}" to "${to}" is invalid. The destination "${to}" does not match any existing route in your project.`;
344
+ },
345
+ hint: 'The destination of a dynamic redirect must include all dynamic parameters from the source route. For example, a redirect from "/old/[slug]" must go to a route that also has a [slug] parameter, like "/new/[slug]".'
340
346
  };
341
347
  const InvalidDynamicRoute = {
342
348
  name: "InvalidDynamicRoute",
@@ -276,7 +276,7 @@ function printHelp({
276
276
  message.push(
277
277
  linebreak(),
278
278
  ` ${bgGreen(black(` ${commandName} `))} ${green(
279
- `v${"6.3.7"}`
279
+ `v${"6.3.8"}`
280
280
  )} ${headline}`
281
281
  );
282
282
  }
@@ -13,7 +13,7 @@ interface CallGetStaticPathsOptions {
13
13
  }
14
14
  export declare function callGetStaticPaths({ mod, route, routeCache, ssr, base, trailingSlash, }: CallGetStaticPathsOptions): Promise<GetStaticPathsResultKeyed>;
15
15
  interface RouteCacheEntry {
16
- mod?: ComponentInstance;
16
+ mod: ComponentInstance;
17
17
  staticPaths: GetStaticPathsResultKeyed;
18
18
  }
19
19
  /**
@@ -19,7 +19,7 @@ async function callGetStaticPaths({
19
19
  validateDynamicRouteModule(mod, { ssr, route });
20
20
  if (ssr && !route.prerender) {
21
21
  const entry = Object.assign([], { keyed: /* @__PURE__ */ new Map() });
22
- routeCache.set(route, { ...cached, staticPaths: entry });
22
+ routeCache.set(route, { ...cached, mod, staticPaths: entry });
23
23
  return entry;
24
24
  }
25
25
  let staticPaths = [];
@@ -388,6 +388,16 @@ function createRedirectRoutes({ settings }, routeMap) {
388
388
  message: InvalidRedirectDestination.message(from, destination)
389
389
  });
390
390
  }
391
+ if (params.length > 0 && redirectRoute && !URL.canParse(destination) && getPrerenderDefault(config)) {
392
+ const destParams = redirectRoute.params ?? [];
393
+ const missingParams = params.filter((p) => !destParams.includes(p));
394
+ if (missingParams.length > 0) {
395
+ throw new AstroError({
396
+ ...InvalidRedirectDestination,
397
+ message: InvalidRedirectDestination.message(from, destination, missingParams)
398
+ });
399
+ }
400
+ }
391
401
  routes.push({
392
402
  type: "redirect",
393
403
  // For backwards compatibility, a redirect is never considered an index route.
@@ -18,7 +18,8 @@ import { componentIsHTMLElement, renderHTMLElement } from "./dom.js";
18
18
  import { maybeRenderHead } from "./head.js";
19
19
  import { createRenderInstruction } from "./instruction.js";
20
20
  import { containsServerDirective, ServerIslandComponent } from "./server-islands.js";
21
- import { renderSlot, renderSlots, renderSlotToString } from "./slot.js";
21
+ import { renderChild } from "./any.js";
22
+ import { renderSlots, renderSlotToString } from "./slot.js";
22
23
  import { formatList, internalSpreadAttributes, renderElement, voidElementNames } from "./util.js";
23
24
  const needsHeadRenderingSymbol = /* @__PURE__ */ Symbol.for("astro.needsHeadRendering");
24
25
  const rendererAliases = /* @__PURE__ */ new Map([["solid", "solid-js"]]);
@@ -333,10 +334,11 @@ function sanitizeElementName(tag) {
333
334
  }
334
335
  function renderFragmentComponent(result, slots = {}) {
335
336
  const slot = slots?.default;
337
+ const preRendered = slot?.(result);
336
338
  return {
337
339
  render(destination) {
338
- if (slot == null) return;
339
- return renderSlot(result, slot).render(destination);
340
+ if (preRendered == null) return;
341
+ return renderChild(destination, preRendered);
340
342
  }
341
343
  };
342
344
  }
@@ -426,7 +426,7 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
426
426
  *
427
427
  * By default, Astro removes whitespace from your HTML, including line breaks, in a lossless manner from `.astro` components. Some whitespace may be preserved as needed to maintain the visual rendering of your HTML.
428
428
  *
429
- * Setting this option to `"jsx"` instead applies the JSX whitespace stripping rules used by frameworks like React. Leading and trailing whitespace is only preserved when explicitly included in the source code through constructs such as `{" "}`, and is otherwise removed entirely.
429
+ * Since 6.2.0, this option can also be set to `"jsx"`, Astro will apply the JSX whitespace stripping rules used by frameworks like React. Leading and trailing whitespace is only preserved when explicitly included in the source code through constructs such as `{" "}`, and is otherwise removed entirely.
430
430
  *
431
431
  * Setting this option to false disables HTML compression and preserves all whitespace.
432
432
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "6.3.7",
3
+ "version": "6.3.8",
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",
@@ -165,8 +165,8 @@
165
165
  "yargs-parser": "^22.0.0",
166
166
  "zod": "^4.3.6",
167
167
  "@astrojs/internal-helpers": "0.9.1",
168
- "@astrojs/telemetry": "3.3.2",
169
- "@astrojs/markdown-remark": "7.1.2"
168
+ "@astrojs/markdown-remark": "7.1.2",
169
+ "@astrojs/telemetry": "3.3.2"
170
170
  },
171
171
  "optionalDependencies": {
172
172
  "sharp": "^0.34.0"