astro 6.2.0 → 6.2.2

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 (41) hide show
  1. package/dist/assets/internal.js +16 -1
  2. package/dist/assets/services/sharp.js +9 -1
  3. package/dist/cli/add/index.js +7 -4
  4. package/dist/cli/infra/build-time-astro-version-provider.js +1 -1
  5. package/dist/content/content-layer.js +3 -3
  6. package/dist/content/loaders/types.d.ts +1 -1
  7. package/dist/core/build/plugins/plugin-css.js +7 -1
  8. package/dist/core/build/plugins/plugin-manifest.js +11 -1
  9. package/dist/core/build/static-build.js +16 -2
  10. package/dist/core/compile/style.js +18 -1
  11. package/dist/core/config/index.d.ts +1 -1
  12. package/dist/core/config/index.js +4 -1
  13. package/dist/core/config/schemas/base.d.ts +4 -4
  14. package/dist/core/config/schemas/base.js +7 -7
  15. package/dist/core/config/schemas/relative.d.ts +4 -7
  16. package/dist/core/config/schemas/relative.js +2 -3
  17. package/dist/core/config/settings.js +2 -4
  18. package/dist/core/config/tsconfig.d.ts +24 -9
  19. package/dist/core/config/tsconfig.js +54 -45
  20. package/dist/core/constants.js +1 -1
  21. package/dist/core/dev/dev.js +1 -1
  22. package/dist/core/errors/zod-error-map.js +27 -0
  23. package/dist/core/messages/runtime.js +1 -1
  24. package/dist/core/render/params-and-props.js +1 -1
  25. package/dist/core/render/slots.js +9 -2
  26. package/dist/core/routing/pattern.js +1 -1
  27. package/dist/core/routing/rewrite.js +1 -4
  28. package/dist/core/session/runtime.js +7 -2
  29. package/dist/i18n/middleware.js +1 -1
  30. package/dist/prefetch/index.js +12 -7
  31. package/dist/runtime/server/render/common.js +10 -4
  32. package/dist/runtime/server/scripts.js +6 -0
  33. package/dist/types/public/content.d.ts +4 -4
  34. package/dist/vite-plugin-app/app.js +0 -3
  35. package/dist/vite-plugin-astro/compile.js +2 -2
  36. package/dist/vite-plugin-astro/utils.d.ts +1 -0
  37. package/dist/vite-plugin-astro/utils.js +9 -1
  38. package/dist/vite-plugin-head/index.js +36 -19
  39. package/dist/vite-plugin-utils/index.d.ts +1 -0
  40. package/dist/vite-plugin-utils/index.js +3 -0
  41. package/package.json +7 -6
@@ -122,8 +122,23 @@ async function getImage(options, imageConfig) {
122
122
  if (resolvedOptions.fit && cssFitValues.includes(resolvedOptions.fit)) {
123
123
  resolvedOptions["data-astro-image-fit"] = resolvedOptions.fit;
124
124
  }
125
+ const currentPosition = resolvedOptions.position || "center";
126
+ resolvedOptions["data-astro-image-pos"] = currentPosition.replace(/\s+/g, "-");
125
127
  if (resolvedOptions.position) {
126
- resolvedOptions["data-astro-image-pos"] = resolvedOptions.position.replace(/\s+/g, "-");
128
+ if (typeof resolvedOptions.style === "object" && resolvedOptions.style !== null) {
129
+ if (!("objectPosition" in resolvedOptions.style)) {
130
+ resolvedOptions.style = {
131
+ ...resolvedOptions.style,
132
+ objectPosition: resolvedOptions.position
133
+ };
134
+ }
135
+ } else {
136
+ const existingStyle = typeof resolvedOptions.style === "string" ? resolvedOptions.style : "";
137
+ if (!existingStyle.includes("object-position")) {
138
+ const positionStyle = `object-position: ${resolvedOptions.position}`;
139
+ resolvedOptions.style = existingStyle ? existingStyle.replace(/;?\s*$/, "; ") + positionStyle : positionStyle;
140
+ }
141
+ }
127
142
  }
128
143
  }
129
144
  const validatedOptions = service.validateOptions ? await service.validateOptions(resolvedOptions, imageConfig) : resolvedOptions;
@@ -88,7 +88,15 @@ const sharpService = {
88
88
  limitInputPixels: config.service.config.limitInputPixels
89
89
  });
90
90
  result.rotate();
91
- const { format } = await result.metadata();
91
+ let format;
92
+ try {
93
+ ({ format } = await result.metadata());
94
+ } catch {
95
+ console.warn(
96
+ `\u26A0\uFE0F Astro could not optimize image "${transform.src}". Sharp doesn't support this format. The image will be used unoptimized. Consider converting to WebP or placing in the public/ folder.`
97
+ );
98
+ return { data: inputBuffer, format: transform.format };
99
+ }
92
100
  if (transform.width && transform.height) {
93
101
  const fit = transform.fit ? fitMap[transform.fit] ?? "inside" : void 0;
94
102
  result.resize({
@@ -898,14 +898,17 @@ async function updateTSConfig(cwd = process.cwd(), logger, integrationsInfo, fla
898
898
  }
899
899
  let inputConfig = await loadTSConfig(cwd);
900
900
  let inputConfigText = "";
901
- if (inputConfig === "invalid-config" || inputConfig === "unknown-error") {
901
+ if (inputConfig.error === "invalid-config") {
902
+ logger.warn(`add`, `Couldn't parse tsconfig.json or jsconfig.json: ${inputConfig.message}`);
902
903
  return "failure";
903
- } else if (inputConfig === "missing-config") {
904
+ } else if (inputConfig.error === "missing-config") {
904
905
  logger.debug("add", "Couldn't find tsconfig.json or jsconfig.json, generating one");
906
+ const tsconfigFile = path.join(cwd, "tsconfig.json");
905
907
  inputConfig = {
906
908
  tsconfig: defaultTSConfig,
907
- tsconfigFile: path.join(cwd, "tsconfig.json"),
908
- rawConfig: defaultTSConfig
909
+ tsconfigFile,
910
+ rawConfig: defaultTSConfig,
911
+ sources: [tsconfigFile]
909
912
  };
910
913
  } else {
911
914
  inputConfigText = JSON.stringify(inputConfig.rawConfig, null, 2);
@@ -1,6 +1,6 @@
1
1
  class BuildTimeAstroVersionProvider {
2
2
  // Injected during the build through esbuild define
3
- version = "6.2.0";
3
+ version = "6.2.2";
4
4
  }
5
5
  export {
6
6
  BuildTimeAstroVersionProvider
@@ -192,7 +192,7 @@ ${contentConfig.error.message}`
192
192
  logger.info("Content config changed");
193
193
  shouldClear = true;
194
194
  }
195
- if (previousAstroVersion && previousAstroVersion !== "6.2.0") {
195
+ if (previousAstroVersion && previousAstroVersion !== "6.2.2") {
196
196
  logger.info("Astro version changed");
197
197
  shouldClear = true;
198
198
  }
@@ -200,8 +200,8 @@ ${contentConfig.error.message}`
200
200
  logger.info("Clearing content store");
201
201
  this.#store.clearAll();
202
202
  }
203
- if ("6.2.0") {
204
- this.#store.metaStore().set("astro-version", "6.2.0");
203
+ if ("6.2.2") {
204
+ this.#store.metaStore().set("astro-version", "6.2.2");
205
205
  }
206
206
  if (currentConfigDigest) {
207
207
  this.#store.metaStore().set("content-config-digest", currentConfigDigest);
@@ -64,7 +64,7 @@ export interface LoadCollectionContext<TCollectionFilter = unknown> {
64
64
  filter?: TCollectionFilter;
65
65
  collection: string;
66
66
  }
67
- export interface LiveLoader<TData extends Record<string, any> = Record<string, unknown>, TEntryFilter extends Record<string, any> | never = never, TCollectionFilter extends Record<string, any> | never = never, TError extends Error = Error> {
67
+ export interface LiveLoader<TData extends Record<string, any> = Record<string, any>, TEntryFilter extends Record<string, any> | never = never, TCollectionFilter extends Record<string, any> | never = never, TError extends Error = Error> {
68
68
  /** Unique name of the loader, e.g. the npm package name */
69
69
  name: string;
70
70
  /** Load a single entry */
@@ -1,6 +1,7 @@
1
1
  import { isCSSRequest } from "vite";
2
2
  import { ASTRO_VITE_ENVIRONMENT_NAMES } from "../../constants.js";
3
3
  import { isPropagatedAssetBoundary } from "../../head-propagation/boundary.js";
4
+ import { VIRTUAL_PAGE_RESOLVED_MODULE_ID } from "../../../vite-plugin-pages/const.js";
4
5
  import {
5
6
  getParentExtendedModuleInfos,
6
7
  getParentModuleInfos,
@@ -18,7 +19,12 @@ function pluginCSS(options, internals) {
18
19
  function isBuildCssBoundary(id, ctx) {
19
20
  if (isPropagatedAssetBoundary(id)) return true;
20
21
  const info = ctx.getModuleInfo(id);
21
- return info ? moduleIsTopLevelPage(info) : false;
22
+ if (!info || !moduleIsTopLevelPage(info)) return false;
23
+ const allImporters = info.importers.concat(info.dynamicImporters);
24
+ const hasNonVirtualPageImporter = allImporters.some(
25
+ (importer) => !importer.includes(VIRTUAL_PAGE_RESOLVED_MODULE_ID)
26
+ );
27
+ return !hasNonVirtualPageImporter;
22
28
  }
23
29
  function rollupPluginAstroBuildCSS(options) {
24
30
  const { internals, buildOptions } = options;
@@ -47,7 +47,8 @@ async function manifestBuildPostHook(options, internals, {
47
47
  logger: options.logger,
48
48
  middlewareEntryPoint: shouldPassMiddlewareEntryPoint ? internals.middlewareEntryPoint : void 0
49
49
  });
50
- const code = injectManifest(manifest, ssrManifestChunk.code);
50
+ const ssrManifest = stripPrerenderedRouteStyles(manifest);
51
+ const code = injectManifest(ssrManifest, ssrManifestChunk.code);
51
52
  mutate(ssrManifestChunk.fileName, code, false);
52
53
  }
53
54
  const prerenderManifestChunk = chunks.find(
@@ -82,6 +83,15 @@ function injectManifest(manifest, code) {
82
83
  return JSON.stringify(manifest);
83
84
  });
84
85
  }
86
+ function stripPrerenderedRouteStyles(manifest) {
87
+ let stripped = false;
88
+ const routes = manifest.routes.map((route) => {
89
+ if (!route.routeData.prerender || route.styles.length === 0) return route;
90
+ stripped = true;
91
+ return { ...route, styles: [] };
92
+ });
93
+ return stripped ? { ...manifest, routes } : manifest;
94
+ }
85
95
  async function buildManifest(opts, internals, staticFiles, encodedKey) {
86
96
  const { settings } = opts;
87
97
  const routes = [];
@@ -186,7 +186,14 @@ async function buildEnvironments(opts, internals) {
186
186
  }
187
187
  return [prefix, cleanChunkName(name), suffix].join("");
188
188
  },
189
- assetFileNames: `${settings.config.build.assets}/[name].[hash][extname]`,
189
+ assetFileNames(assetInfo) {
190
+ const name = assetInfo.names?.[0] ?? "";
191
+ if (name.includes(ASTRO_PAGE_EXTENSION_POST_PATTERN)) {
192
+ const [sanitizedName] = name.split(ASTRO_PAGE_EXTENSION_POST_PATTERN);
193
+ return `${settings.config.build.assets}/${sanitizedName}.[hash][extname]`;
194
+ }
195
+ return `${settings.config.build.assets}/[name].[hash][extname]`;
196
+ },
190
197
  ...viteConfig.build?.rollupOptions?.output,
191
198
  entryFileNames(chunkInfo) {
192
199
  if (chunkInfo.facadeModuleId?.startsWith(VIRTUAL_PAGE_RESOLVED_MODULE_ID)) {
@@ -289,7 +296,14 @@ async function buildEnvironments(opts, internals) {
289
296
  chunkFileNames(chunkInfo) {
290
297
  return `${settings.config.build.assets}/${cleanChunkName(chunkInfo.name)}.[hash].js`;
291
298
  },
292
- assetFileNames: `${settings.config.build.assets}/[name].[hash][extname]`,
299
+ assetFileNames(assetInfo) {
300
+ const name = assetInfo.names?.[0] ?? "";
301
+ if (name.includes(ASTRO_PAGE_EXTENSION_POST_PATTERN)) {
302
+ const [sanitizedName] = name.split(ASTRO_PAGE_EXTENSION_POST_PATTERN);
303
+ return `${settings.config.build.assets}/${sanitizedName}.[hash][extname]`;
304
+ }
305
+ return `${settings.config.build.assets}/[name].[hash][extname]`;
306
+ },
293
307
  ...viteConfig.environments?.client?.build?.rollupOptions?.output
294
308
  }
295
309
  }
@@ -1,4 +1,5 @@
1
1
  import fs from "node:fs";
2
+ import { createRequire } from "node:module";
2
3
  import { preprocessCSS } from "vite";
3
4
  import { AstroErrorData, CSSError, positionAt } from "../errors/index.js";
4
5
  import { normalizePath } from "../viteUtils.js";
@@ -31,6 +32,21 @@ function rewriteCssUrls(css, base) {
31
32
  return match;
32
33
  });
33
34
  }
35
+ function withNestingExcluded(viteConfig) {
36
+ let Features;
37
+ try {
38
+ const requireFromRoot = createRequire(viteConfig.root + "/");
39
+ Features = requireFromRoot("lightningcss").Features;
40
+ } catch {
41
+ return void 0;
42
+ }
43
+ const lcss = viteConfig.css?.lightningcss ?? {};
44
+ const prevExclude = lcss.exclude ?? 0;
45
+ return {
46
+ ...viteConfig,
47
+ css: { ...viteConfig.css, lightningcss: { ...lcss, exclude: prevExclude | Features.Nesting } }
48
+ };
49
+ }
34
50
  function createStylePreprocessor({
35
51
  filename,
36
52
  viteConfig,
@@ -44,7 +60,8 @@ function createStylePreprocessor({
44
60
  const lang = `.${attrs?.lang || "css"}`.toLowerCase();
45
61
  const id = `${filename}?astro&type=style&index=${index}&lang${lang}`;
46
62
  try {
47
- const result = await preprocessCSS(content, id, viteConfig);
63
+ const effectiveViteConfig = viteConfig.css?.transformer === "lightningcss" ? withNestingExcluded(viteConfig) ?? viteConfig : viteConfig;
64
+ const result = await preprocessCSS(content, id, effectiveViteConfig);
48
65
  const rewrittenCode = rewriteCssUrls(result.code, astroConfig.base);
49
66
  cssPartialCompileResults[index] = {
50
67
  // Use `in` operator to handle both Go compiler (boolean `true`) and
@@ -1,4 +1,4 @@
1
1
  export { resolveConfig, resolveConfigPath, resolveRoot, } from './config.js';
2
2
  export { mergeConfig } from './merge.js';
3
3
  export { createSettings } from './settings.js';
4
- export { loadTSConfig, updateTSConfigForFramework } from './tsconfig.js';
4
+ export { loadTSConfig, updateTSConfigForFramework, type TSConfigLoadedResult, type TSConfigResult, } from './tsconfig.js';
@@ -5,7 +5,10 @@ import {
5
5
  } from "./config.js";
6
6
  import { mergeConfig } from "./merge.js";
7
7
  import { createSettings } from "./settings.js";
8
- import { loadTSConfig, updateTSConfigForFramework } from "./tsconfig.js";
8
+ import {
9
+ loadTSConfig,
10
+ updateTSConfigForFramework
11
+ } from "./tsconfig.js";
9
12
  export {
10
13
  createSettings,
11
14
  loadTSConfig,
@@ -1,5 +1,5 @@
1
+ import type { RehypePlugin as _RehypePlugin, RemarkPlugin as _RemarkPlugin, RemarkRehype as _RemarkRehype, Smartypants as _Smartypants, ShikiConfig } from '@astrojs/markdown-remark';
1
2
  import type { OutgoingHttpHeaders } from 'node:http';
2
- import type { RehypePlugin as _RehypePlugin, RemarkPlugin as _RemarkPlugin, RemarkRehype as _RemarkRehype, ShikiConfig, Smartypants as _Smartypants } from '@astrojs/markdown-remark';
3
3
  import * as z from 'zod/v4';
4
4
  import type { ViteUserConfig } from '../../../types/public/config.js';
5
5
  /** @lintignore */
@@ -103,10 +103,10 @@ export declare const AstroConfigSchema: z.ZodObject<{
103
103
  name: z.ZodString;
104
104
  hooks: z.ZodDefault<z.ZodObject<{}, z.core.$loose>>;
105
105
  }, z.core.$strip>>;
106
- integrations: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodDefault<z.ZodArray<z.ZodObject<{
106
+ integrations: z.ZodDefault<z.ZodOptional<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodArray<z.ZodObject<{
107
107
  name: z.ZodString;
108
108
  hooks: z.ZodDefault<z.ZodObject<{}, z.core.$loose>>;
109
- }, z.core.$strip>>>>;
109
+ }, z.core.$strip>>>>>;
110
110
  build: z.ZodPrefault<z.ZodObject<{
111
111
  format: z.ZodDefault<z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"file">, z.ZodLiteral<"directory">, z.ZodLiteral<"preserve">]>>>;
112
112
  client: z.ZodPipe<z.ZodDefault<z.ZodOptional<z.ZodString>>, z.ZodTransform<URL, string>>;
@@ -124,7 +124,7 @@ export declare const AstroConfigSchema: z.ZodObject<{
124
124
  }>>>;
125
125
  concurrency: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
126
126
  }, z.core.$strip>>;
127
- server: z.ZodPipe<z.ZodTransform<any, unknown>, z.ZodPrefault<z.ZodObject<{
127
+ server: z.ZodPrefault<z.ZodPipe<z.ZodTransform<any, unknown>, z.ZodObject<{
128
128
  open: z.ZodDefault<z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodBoolean]>>>;
129
129
  host: z.ZodDefault<z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodBoolean]>>>;
130
130
  port: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
@@ -2,11 +2,11 @@ import { markdownConfigDefaults, syntaxHighlightDefaults } from "@astrojs/markdo
2
2
  import { bundledThemes } from "shiki";
3
3
  import * as z from "zod/v4";
4
4
  import { FontFamilySchema } from "../../../assets/fonts/config.js";
5
+ import { SvgOptimizerSchema } from "../../../assets/svg/config.js";
5
6
  import { EnvSchema } from "../../../env/schema.js";
6
- import { allowedDirectivesSchema, cspAlgorithmSchema, cspHashSchema } from "../../csp/config.js";
7
7
  import { CacheSchema, RouteRulesSchema } from "../../cache/config.js";
8
+ import { allowedDirectivesSchema, cspAlgorithmSchema, cspHashSchema } from "../../csp/config.js";
8
9
  import { SessionSchema } from "../../session/config.js";
9
- import { SvgOptimizerSchema } from "../../../assets/svg/config.js";
10
10
  const ASTRO_CONFIG_DEFAULTS = {
11
11
  root: ".",
12
12
  srcDir: "./src",
@@ -105,13 +105,13 @@ const AstroConfigSchema = z.object({
105
105
  message: 'The `output: "hybrid"` option has been removed. Use `output: "static"` (the default) instead, which now behaves the same way.'
106
106
  }),
107
107
  scopedStyleStrategy: z.union([z.literal("where"), z.literal("class"), z.literal("attribute")]).optional().default("attribute"),
108
- adapter: z.object({ name: z.string(), hooks: z.object({}).passthrough().default({}) }).optional(),
108
+ adapter: z.object({ name: z.string(), hooks: z.object({}).loose().default({}) }).optional(),
109
109
  integrations: z.preprocess(
110
110
  // preprocess
111
111
  (val) => Array.isArray(val) ? val.flat(Number.POSITIVE_INFINITY).filter(Boolean) : val,
112
112
  // validate
113
- z.array(z.object({ name: z.string(), hooks: z.object({}).passthrough().default({}) })).default(ASTRO_CONFIG_DEFAULTS.integrations)
114
- ),
113
+ z.array(z.object({ name: z.string(), hooks: z.object({}).loose().default({}) }))
114
+ ).optional().default(ASTRO_CONFIG_DEFAULTS.integrations),
115
115
  build: z.object({
116
116
  format: z.union([z.literal("file"), z.literal("directory"), z.literal("preserve")]).optional().default(ASTRO_CONFIG_DEFAULTS.build.format),
117
117
  client: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.client).transform((val) => new URL(val)),
@@ -135,8 +135,8 @@ const AstroConfigSchema = z.object({
135
135
  port: z.number().optional().default(ASTRO_CONFIG_DEFAULTS.server.port),
136
136
  headers: z.custom().optional(),
137
137
  allowedHosts: z.union([z.array(z.string()), z.literal(true)]).optional().default(ASTRO_CONFIG_DEFAULTS.server.allowedHosts)
138
- }).prefault({})
139
- ),
138
+ })
139
+ ).prefault({}),
140
140
  redirects: z.record(
141
141
  z.string(),
142
142
  z.union([
@@ -10,10 +10,10 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
10
10
  name: z.ZodString;
11
11
  hooks: z.ZodDefault<z.ZodObject<{}, z.core.$loose>>;
12
12
  }, z.core.$strip>>;
13
- integrations: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodDefault<z.ZodArray<z.ZodObject<{
13
+ integrations: z.ZodDefault<z.ZodOptional<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodArray<z.ZodObject<{
14
14
  name: z.ZodString;
15
15
  hooks: z.ZodDefault<z.ZodObject<{}, z.core.$loose>>;
16
- }, z.core.$strip>>>>;
16
+ }, z.core.$strip>>>>>;
17
17
  redirects: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
18
18
  status: z.ZodUnion<readonly [z.ZodLiteral<300>, z.ZodLiteral<301>, z.ZodLiteral<302>, z.ZodLiteral<303>, z.ZodLiteral<304>, z.ZodLiteral<307>, z.ZodLiteral<308>]>;
19
19
  destination: z.ZodString;
@@ -437,14 +437,13 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
437
437
  }>>>;
438
438
  concurrency: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
439
439
  }, z.core.$strip>>>;
440
- server: z.ZodPipe<z.ZodTransform<any, unknown>, z.ZodPrefault<z.ZodOptional<z.ZodObject<{
440
+ server: z.ZodPrefault<z.ZodPipe<z.ZodTransform<any, unknown>, z.ZodObject<{
441
441
  open: z.ZodDefault<z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodBoolean]>>>;
442
442
  host: z.ZodDefault<z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodBoolean]>>>;
443
443
  port: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
444
444
  headers: z.ZodOptional<z.ZodCustom<OutgoingHttpHeaders, OutgoingHttpHeaders>>;
445
- streaming: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
446
445
  allowedHosts: z.ZodDefault<z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodString>, z.ZodLiteral<true>]>>>;
447
- }, z.core.$strip>>>>;
446
+ }, z.core.$strip>>>;
448
447
  }, z.core.$strip>, z.ZodTransform<{
449
448
  base: string;
450
449
  trailingSlash: "never" | "ignore" | "always";
@@ -630,7 +629,6 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
630
629
  open: string | boolean;
631
630
  host: string | boolean;
632
631
  port: number;
633
- streaming: boolean;
634
632
  allowedHosts: true | string[];
635
633
  headers?: OutgoingHttpHeaders | undefined;
636
634
  };
@@ -879,7 +877,6 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
879
877
  open: string | boolean;
880
878
  host: string | boolean;
881
879
  port: number;
882
- streaming: boolean;
883
880
  allowedHosts: true | string[];
884
881
  headers?: OutgoingHttpHeaders | undefined;
885
882
  };
@@ -59,10 +59,9 @@ function createRelativeSchema(cmd, fileProtocolRoot) {
59
59
  host: z.union([z.string(), z.boolean()]).optional().default(ASTRO_CONFIG_DEFAULTS.server.host),
60
60
  port: z.number().optional().default(ASTRO_CONFIG_DEFAULTS.server.port),
61
61
  headers: z.custom().optional(),
62
- streaming: z.boolean().optional().default(true),
63
62
  allowedHosts: z.union([z.array(z.string()), z.literal(true)]).optional().default(ASTRO_CONFIG_DEFAULTS.server.allowedHosts)
64
- }).optional().prefault({})
65
- )
63
+ })
64
+ ).prefault({})
66
65
  }).transform((config) => {
67
66
  if (config.outDir.toString() !== resolveDirAsUrl(ASTRO_CONFIG_DEFAULTS.outDir, fileProtocolRoot).toString()) {
68
67
  const outDirPath = fileURLToPath(config.outDir);
@@ -149,10 +149,8 @@ async function createSettings(config, logLevel, cwd) {
149
149
  if (cwd) {
150
150
  watchFiles.push(fileURLToPath(new URL("./package.json", pathToFileURL(cwd))));
151
151
  }
152
- if (typeof tsconfig !== "string") {
153
- watchFiles.push(
154
- ...[tsconfig.tsconfigFile, ...(tsconfig.extended ?? []).map((e) => e.tsconfigFile)]
155
- );
152
+ if (!tsconfig.error) {
153
+ watchFiles.push(...tsconfig.sources);
156
154
  settings.tsConfig = tsconfig.tsconfig;
157
155
  settings.tsConfigPath = tsconfig.tsconfigFile;
158
156
  }
@@ -1,17 +1,32 @@
1
- import { type TSConfckParseResult } from 'tsconfck';
2
1
  import type { CompilerOptions, TypeAcquisition } from 'typescript';
3
2
  export declare const defaultTSConfig: TSConfig;
4
3
  export type frameworkWithTSSettings = 'vue' | 'react' | 'preact' | 'solid-js';
5
4
  export declare const presets: Map<frameworkWithTSSettings, TSConfig>;
6
- type TSConfigResult<T = object> = Promise<(TSConfckParseResult & T) | 'invalid-config' | 'missing-config' | 'unknown-error'>;
5
+ export interface TSConfigLoadedResult {
6
+ error?: undefined;
7
+ /** Absolute path of the root tsconfig/jsconfig file that was loaded. */
8
+ tsconfigFile: string;
9
+ /** The merged/resolved config (after `extends` are walked). */
10
+ tsconfig: TSConfig;
11
+ /** The user-written, un-merged config. Used by `astro add` to round-trip. */
12
+ rawConfig: TSConfig;
13
+ /**
14
+ * Every tsconfig file that contributed via `extends`, root-first.
15
+ * Includes `tsconfigFile`. Used to populate the dev-server watch list.
16
+ */
17
+ sources: string[];
18
+ }
19
+ export type TSConfigResult = TSConfigLoadedResult | {
20
+ error: 'invalid-config';
21
+ message: string;
22
+ } | {
23
+ error: 'missing-config';
24
+ };
7
25
  /**
8
- * Load a tsconfig.json or jsconfig.json is the former is not found
9
- * @param root The root directory to search in, defaults to `process.cwd()`.
10
- * @param findUp Whether to search for the config file in parent directories, by default only the root directory is searched.
26
+ * Load a tsconfig.json or jsconfig.json if the former is not found.
27
+ * @param root The directory to search in, defaults to `process.cwd()`.
11
28
  */
12
- export declare function loadTSConfig(root: string | undefined, findUp?: boolean): Promise<TSConfigResult<{
13
- rawConfig: TSConfig;
14
- }>>;
29
+ export declare function loadTSConfig(root: string | undefined): Promise<TSConfigResult>;
15
30
  export declare function updateTSConfigForFramework(target: TSConfig, framework: frameworkWithTSSettings): TSConfig;
16
31
  type StripEnums<T extends Record<string, any>> = {
17
32
  [K in keyof T]: T[K] extends boolean ? T[K] : T[K] extends string ? T[K] : T[K] extends object ? T[K] : T[K] extends Array<any> ? T[K] : T[K] extends undefined ? undefined : any;
@@ -19,7 +34,7 @@ type StripEnums<T extends Record<string, any>> = {
19
34
  export interface TSConfig {
20
35
  compilerOptions?: StripEnums<CompilerOptions>;
21
36
  compileOnSave?: boolean;
22
- extends?: string;
37
+ extends?: string | string[];
23
38
  files?: string[];
24
39
  include?: string[];
25
40
  exclude?: string[];
@@ -1,11 +1,7 @@
1
- import { readFile } from "node:fs/promises";
2
- import { join } from "node:path";
3
- import {
4
- find,
5
- parse,
6
- TSConfckParseError,
7
- toJson
8
- } from "tsconfck";
1
+ import { readFileSync, existsSync } from "node:fs";
2
+ import { join, normalize } from "node:path";
3
+ import { readTsconfig } from "get-tsconfig";
4
+ import { parse as parseJsonc } from "jsonc-parser";
9
5
  const defaultTSConfig = { extends: "astro/tsconfigs/base" };
10
6
  const presets = /* @__PURE__ */ new Map([
11
7
  [
@@ -48,50 +44,63 @@ const presets = /* @__PURE__ */ new Map([
48
44
  }
49
45
  ]
50
46
  ]);
51
- async function loadTSConfig(root, findUp = false) {
52
- const safeCwd = root ?? process.cwd();
53
- const [jsconfig, tsconfig] = await Promise.all(
54
- ["jsconfig.json", "tsconfig.json"].map(
55
- (configName) => (
56
- // `tsconfck` expects its first argument to be a file path, not a directory path, so we'll fake one
57
- find(join(safeCwd, "./dummy.txt"), {
58
- root: findUp ? void 0 : root,
59
- configName
60
- })
61
- )
62
- )
63
- );
64
- if (tsconfig) {
65
- const parsedConfig = await safeParse(tsconfig, { root });
66
- if (typeof parsedConfig === "string") {
67
- return parsedConfig;
47
+ async function loadTSConfig(root) {
48
+ const safeCwd = root || process.cwd();
49
+ let tsconfigPath;
50
+ for (const configName of ["tsconfig.json", "jsconfig.json"]) {
51
+ const possiblePath = join(safeCwd, configName);
52
+ if (existsSync(possiblePath)) {
53
+ tsconfigPath = possiblePath;
54
+ break;
68
55
  }
69
- const rawConfig = await readFile(tsconfig, "utf-8").then(toJson).then((content) => JSON.parse(content));
70
- return { ...parsedConfig, rawConfig };
71
56
  }
72
- if (jsconfig) {
73
- const parsedConfig = await safeParse(jsconfig, { root });
74
- if (typeof parsedConfig === "string") {
75
- return parsedConfig;
76
- }
77
- const rawConfig = await readFile(jsconfig, "utf-8").then(toJson).then((content) => JSON.parse(content));
78
- return { ...parsedConfig, rawConfig };
57
+ if (!tsconfigPath) {
58
+ return {
59
+ error: "missing-config"
60
+ };
79
61
  }
80
- return "missing-config";
81
- }
82
- async function safeParse(tsconfigPath, options = {}) {
62
+ let rawConfig;
83
63
  try {
84
- const parseResult = await parse(tsconfigPath, options);
85
- if (parseResult.tsconfig == null) {
86
- return "missing-config";
64
+ const text = readFileSync(tsconfigPath, "utf-8");
65
+ const errors = [];
66
+ const parsed = parseJsonc(text, errors, { allowTrailingComma: true });
67
+ if (errors.length > 0) {
68
+ const first = errors[0];
69
+ return {
70
+ error: "invalid-config",
71
+ message: `Failed to parse ${tsconfigPath}: Malformed JSONC (error code ${first.error}) at offset ${first.offset}`
72
+ };
87
73
  }
88
- return parseResult;
74
+ rawConfig = parsed;
89
75
  } catch (e) {
90
- if (e instanceof TSConfckParseError) {
91
- return "invalid-config";
92
- }
93
- return "unknown-error";
76
+ const message = e instanceof Error ? e.message : String(e);
77
+ return {
78
+ error: "invalid-config",
79
+ message: `Failed to parse ${tsconfigPath}: ${message}`
80
+ };
81
+ }
82
+ if (!rawConfig) {
83
+ return {
84
+ error: "invalid-config",
85
+ message: `Failed to parse ${tsconfigPath}: Unknown error`
86
+ };
87
+ }
88
+ let resolved;
89
+ try {
90
+ resolved = readTsconfig(tsconfigPath);
91
+ } catch (e) {
92
+ const message = e instanceof Error ? e.message : String(e);
93
+ return {
94
+ error: "invalid-config",
95
+ message: `Failed to resolve ${tsconfigPath}: ${message}`
96
+ };
94
97
  }
98
+ return {
99
+ tsconfigFile: normalize(resolved.path),
100
+ tsconfig: resolved.config,
101
+ rawConfig,
102
+ sources: (resolved.sources || [resolved.path]).map(normalize)
103
+ };
95
104
  }
96
105
  function updateTSConfigForFramework(target, framework) {
97
106
  if (!presets.has(framework)) {
@@ -1,4 +1,4 @@
1
- const ASTRO_VERSION = "6.2.0";
1
+ const ASTRO_VERSION = "6.2.2";
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.2.0";
40
+ const currentVersion = "6.2.2";
41
41
  const isPrerelease = currentVersion.includes("-");
42
42
  if (!isPrerelease) {
43
43
  try {
@@ -24,6 +24,17 @@ const errorMap = (issue) => {
24
24
  `> ${getTypeOrLiteralMsg(error)}`
25
25
  ) : `> ${prefix(key, getTypeOrLiteralMsg(error))}`
26
26
  );
27
+ if (details.length === 0) {
28
+ if ("discriminator" in issue && issue.discriminator && "options" in issue) {
29
+ const options = issue.options;
30
+ if (Array.isArray(options)) {
31
+ details.push(
32
+ `> Expected \`${issue.discriminator}\` to be ${options.map((o) => `\`${stringify(o)}\``).join(" | ")}`
33
+ );
34
+ details.push("> Received `" + stringify(issue.input) + "`");
35
+ }
36
+ }
37
+ }
27
38
  if (details.length === 0) {
28
39
  const expectedShapes = [];
29
40
  for (const unionErrors of issue.errors) {
@@ -59,6 +70,22 @@ const errorMap = (issue) => {
59
70
  return {
60
71
  message: messages.concat(details).join("\n")
61
72
  };
73
+ } else if (issue.code === "invalid_key") {
74
+ const keyIssues = issue.issues;
75
+ if (Array.isArray(keyIssues) && keyIssues.length > 0) {
76
+ const firstIssue = keyIssues[0];
77
+ const msg = firstIssue.message || "Invalid key in record";
78
+ return { message: prefix(baseErrorPath, msg) };
79
+ }
80
+ return { message: prefix(baseErrorPath, "Invalid key in record") };
81
+ } else if (issue.code === "invalid_element") {
82
+ const elementIssues = issue.issues;
83
+ if (Array.isArray(elementIssues) && elementIssues.length > 0) {
84
+ const firstIssue = elementIssues[0];
85
+ const msg = firstIssue.message || "Invalid element";
86
+ return { message: prefix(baseErrorPath, msg) };
87
+ }
88
+ return { message: prefix(baseErrorPath, "Invalid element") };
62
89
  } else if (issue.code === "invalid_type") {
63
90
  return {
64
91
  message: prefix(
@@ -276,7 +276,7 @@ function printHelp({
276
276
  message.push(
277
277
  linebreak(),
278
278
  ` ${bgGreen(black(` ${commandName} `))} ${green(
279
- `v${"6.2.0"}`
279
+ `v${"6.2.2"}`
280
280
  )} ${headline}`
281
281
  );
282
282
  }
@@ -44,7 +44,7 @@ async function getProps(opts) {
44
44
  }
45
45
  function getParams(route, pathname) {
46
46
  if (!route.params.length) return {};
47
- const path = pathname.endsWith(".html") && !routeHasHtmlExtension(route) ? pathname.slice(0, -5) : pathname;
47
+ const path = pathname.endsWith(".html") && route.type === "page" && !routeHasHtmlExtension(route) ? pathname.slice(0, -5) : pathname;
48
48
  const allPatterns = [route, ...route.fallbackRoutes].map((r) => r.pattern);
49
49
  const paramsMatch = allPatterns.map((pattern) => pattern.exec(path)).find((x) => x);
50
50
  if (!paramsMatch) return {};
@@ -1,13 +1,20 @@
1
1
  import { renderSlotToString } from "../../runtime/server/index.js";
2
2
  import { renderJSX } from "../../runtime/server/jsx.js";
3
+ import { isRenderTemplateResult } from "../../runtime/server/render/astro/index.js";
3
4
  import { chunkToString } from "../../runtime/server/render/index.js";
4
5
  import { isRenderInstruction } from "../../runtime/server/render/instruction.js";
5
6
  import { AstroError, AstroErrorData } from "../errors/index.js";
6
7
  function getFunctionExpression(slot) {
7
8
  if (!slot) return;
8
- const expressions = slot?.expressions?.filter((e) => isRenderInstruction(e) === false);
9
+ const expressions = slot?.expressions?.filter(
10
+ (e) => isRenderInstruction(e) === false || isRenderTemplateResult(e)
11
+ );
9
12
  if (expressions?.length !== 1) return;
10
- return expressions[0];
13
+ const expression = expressions[0];
14
+ if (isRenderTemplateResult(expression)) {
15
+ return getFunctionExpression(expression);
16
+ }
17
+ return expression;
11
18
  }
12
19
  class Slots {
13
20
  #result;
@@ -16,7 +16,7 @@ function getPattern(segments, base, addTrailingSlash) {
16
16
  }).join("");
17
17
  const trailing = addTrailingSlash && segments.length ? getTrailingSlashPattern(addTrailingSlash) : "$";
18
18
  let initial = "\\/";
19
- if (addTrailingSlash === "never" && base !== "/") {
19
+ if (addTrailingSlash === "never" && base !== "/" && pathname !== "") {
20
20
  initial = "";
21
21
  }
22
22
  return new RegExp(`^${pathname || initial}${trailing}`);
@@ -134,7 +134,7 @@ function normalizeRewritePathname(urlPathname, base, trailingSlash, buildFormat)
134
134
  if (base !== "/") {
135
135
  const isBasePathRequest = urlPathname === base || urlPathname === removeTrailingForwardSlash(base);
136
136
  if (isBasePathRequest) {
137
- pathname = shouldAppendSlash ? "/" : "";
137
+ pathname = "/";
138
138
  } else if (urlPathname.startsWith(base)) {
139
139
  pathname = shouldAppendSlash ? appendForwardSlash(urlPathname) : removeTrailingForwardSlash(urlPathname);
140
140
  pathname = pathname.slice(base.length);
@@ -143,9 +143,6 @@ function normalizeRewritePathname(urlPathname, base, trailingSlash, buildFormat)
143
143
  if (!pathname.startsWith("/") && shouldAppendSlash && urlPathname.endsWith("/")) {
144
144
  pathname = prependForwardSlash(pathname);
145
145
  }
146
- if (pathname === "/" && base !== "/" && !shouldAppendSlash) {
147
- pathname = "";
148
- }
149
146
  if (buildFormat === "file") {
150
147
  pathname = pathname.replace(/\.html$/, "");
151
148
  }
@@ -121,7 +121,8 @@ class AstroSession {
121
121
  * Deletes a session value.
122
122
  */
123
123
  delete(key) {
124
- this.#data?.delete(key);
124
+ this.#data ??= /* @__PURE__ */ new Map();
125
+ this.#data.delete(key);
125
126
  if (this.#partial) {
126
127
  this.#toDelete.add(key);
127
128
  }
@@ -272,11 +273,15 @@ class AstroSession {
272
273
  * If there is existing partial data, it will be merged into the new data object.
273
274
  */
274
275
  async #ensureData() {
275
- const storage = await this.#ensureStorage();
276
276
  if (this.#data && !this.#partial) {
277
277
  return this.#data;
278
278
  }
279
279
  this.#data ??= /* @__PURE__ */ new Map();
280
+ if (!this.#sessionID && !this.#cookies.get(this.#cookieName)?.value) {
281
+ this.#partial = false;
282
+ return this.#data;
283
+ }
284
+ const storage = await this.#ensureStorage();
280
285
  const raw = await storage.get(this.#ensureSessionID());
281
286
  if (!raw) {
282
287
  if (this.#sessionIDFromCookie) {
@@ -71,7 +71,7 @@ function createI18nMiddleware(i18n, base, trailingSlash, format) {
71
71
  if (i18n.fallback && i18n.fallbackType) {
72
72
  const fallbackDecision = computeFallbackRoute({
73
73
  pathname: context.url.pathname,
74
- responseStatus: response.status,
74
+ responseStatus: typeHeader === "fallback" ? 404 : response.status,
75
75
  currentLocale: context.currentLocale,
76
76
  fallback: i18n.fallback,
77
77
  fallbackType: i18n.fallbackType,
@@ -24,8 +24,9 @@ function initTapStrategy() {
24
24
  document.addEventListener(
25
25
  event,
26
26
  (e) => {
27
- if (elMatchesStrategy(e.target, "tap")) {
28
- prefetch(e.target.href, { ignoreSlowConnection: true });
27
+ const anchor = e.target.closest("a");
28
+ if (elMatchesStrategy(anchor, "tap")) {
29
+ prefetch(anchor.href, { ignoreSlowConnection: true });
29
30
  }
30
31
  },
31
32
  { passive: true }
@@ -37,8 +38,9 @@ function initHoverStrategy() {
37
38
  document.body.addEventListener(
38
39
  "focusin",
39
40
  (e) => {
40
- if (elMatchesStrategy(e.target, "hover")) {
41
- handleHoverIn(e);
41
+ const anchor = e.target.closest("a");
42
+ if (elMatchesStrategy(anchor, "hover")) {
43
+ handleHoverIn(anchor.href);
42
44
  }
43
45
  },
44
46
  { passive: true }
@@ -49,13 +51,16 @@ function initHoverStrategy() {
49
51
  if (listenedAnchors.has(anchor)) continue;
50
52
  if (elMatchesStrategy(anchor, "hover")) {
51
53
  listenedAnchors.add(anchor);
52
- anchor.addEventListener("mouseenter", handleHoverIn, { passive: true });
54
+ anchor.addEventListener(
55
+ "mouseenter",
56
+ (e) => handleHoverIn(e.currentTarget.href),
57
+ { passive: true }
58
+ );
53
59
  anchor.addEventListener("mouseleave", handleHoverOut, { passive: true });
54
60
  }
55
61
  }
56
62
  });
57
- function handleHoverIn(e) {
58
- const href = e.target.href;
63
+ function handleHoverIn(href) {
59
64
  if (timeout) {
60
65
  clearTimeout(timeout);
61
66
  }
@@ -19,13 +19,13 @@ function stringifyChunk(result, chunk) {
19
19
  switch (instruction.type) {
20
20
  case "directive": {
21
21
  const { hydration } = instruction;
22
- let needsHydrationScript = hydration && determineIfNeedsHydrationScript(result);
23
- let needsDirectiveScript = hydration && determinesIfNeedsDirectiveScript(result, hydration.directive);
22
+ const needsHydrationScript = hydration && determineIfNeedsHydrationScript(result);
23
+ const needsDirectiveScript = hydration && determinesIfNeedsDirectiveScript(result, hydration.directive);
24
24
  if (needsHydrationScript) {
25
- let prescripts = getPrescripts(result, "both", hydration.directive);
25
+ const prescripts = getPrescripts(result, "both", hydration.directive);
26
26
  return markHTMLString(prescripts);
27
27
  } else if (needsDirectiveScript) {
28
- let prescripts = getPrescripts(result, "directive", hydration.directive);
28
+ const prescripts = getPrescripts(result, "directive", hydration.directive);
29
29
  return markHTMLString(prescripts);
30
30
  } else {
31
31
  return "";
@@ -46,6 +46,9 @@ function stringifyChunk(result, chunk) {
46
46
  case "renderer-hydration-script": {
47
47
  const { rendererSpecificHydrationScripts } = result._metadata;
48
48
  const { rendererName } = instruction;
49
+ if (result._metadata.templateDepth > 0) {
50
+ return instruction.render();
51
+ }
49
52
  if (!rendererSpecificHydrationScripts.has(rendererName)) {
50
53
  rendererSpecificHydrationScripts.add(rendererName);
51
54
  return instruction.render();
@@ -53,6 +56,9 @@ function stringifyChunk(result, chunk) {
53
56
  return "";
54
57
  }
55
58
  case "server-island-runtime": {
59
+ if (result._metadata.templateDepth > 0) {
60
+ return renderServerIslandRuntime();
61
+ }
56
62
  if (result._metadata.hasRenderedServerIslandRuntime) {
57
63
  return "";
58
64
  }
@@ -2,12 +2,18 @@ import islandScript from "./astro-island.prebuilt.js";
2
2
  import islandScriptDev from "./astro-island.prebuilt-dev.js";
3
3
  import { ISLAND_STYLES } from "./astro-island-styles.js";
4
4
  function determineIfNeedsHydrationScript(result) {
5
+ if (result._metadata.templateDepth > 0) {
6
+ return !result._metadata.hasHydrationScript;
7
+ }
5
8
  if (result._metadata.hasHydrationScript) {
6
9
  return false;
7
10
  }
8
11
  return result._metadata.hasHydrationScript = true;
9
12
  }
10
13
  function determinesIfNeedsDirectiveScript(result, directive) {
14
+ if (result._metadata.templateDepth > 0) {
15
+ return !result._metadata.hasDirectives.has(directive);
16
+ }
11
17
  if (result._metadata.hasDirectives.has(directive)) {
12
18
  return false;
13
19
  }
@@ -142,7 +142,7 @@ export interface CacheHint {
142
142
  /** Last modified time of the content */
143
143
  lastModified?: Date;
144
144
  }
145
- export interface LiveDataEntry<TData extends Record<string, any> = Record<string, unknown>> {
145
+ export interface LiveDataEntry<TData extends Record<string, any> = Record<string, any>> {
146
146
  /** The ID of the entry. Unique per collection. */
147
147
  id: string;
148
148
  /** The parsed entry data */
@@ -154,17 +154,17 @@ export interface LiveDataEntry<TData extends Record<string, any> = Record<string
154
154
  /** A hint for how to cache this entry */
155
155
  cacheHint?: CacheHint;
156
156
  }
157
- export interface LiveDataCollection<TData extends Record<string, any> = Record<string, unknown>> {
157
+ export interface LiveDataCollection<TData extends Record<string, any> = Record<string, any>> {
158
158
  entries: Array<LiveDataEntry<TData>>;
159
159
  /** A hint for how to cache this collection. Individual entries can also have cache hints */
160
160
  cacheHint?: CacheHint;
161
161
  }
162
- export interface LiveDataCollectionResult<TData extends Record<string, any> = Record<string, unknown>, TError extends Error = Error> {
162
+ export interface LiveDataCollectionResult<TData extends Record<string, any> = Record<string, any>, TError extends Error = Error> {
163
163
  entries?: Array<LiveDataEntry<TData>>;
164
164
  error?: TError | LiveCollectionError;
165
165
  cacheHint?: CacheHint;
166
166
  }
167
- export interface LiveDataEntryResult<TData extends Record<string, any> = Record<string, unknown>, TError extends Error = Error> {
167
+ export interface LiveDataEntryResult<TData extends Record<string, any> = Record<string, any>, TError extends Error = Error> {
168
168
  entry?: LiveDataEntry<TData>;
169
169
  error?: TError | LiveCollectionError;
170
170
  cacheHint?: CacheHint;
@@ -108,9 +108,6 @@ class AstroServerApp extends BaseApp {
108
108
  } else {
109
109
  pathname = decodeURI(url.pathname);
110
110
  }
111
- if (this.manifest.trailingSlash === "never" && pathname === "/" && this.manifest.base !== "/") {
112
- pathname = "";
113
- }
114
111
  url.pathname = removeTrailingForwardSlash(this.manifest.base) + url.pathname;
115
112
  if (url.pathname.endsWith("/") && !shouldAppendForwardSlash(this.manifest.trailingSlash, this.manifest.buildFormat)) {
116
113
  url.pathname = url.pathname.slice(0, -1);
@@ -1,7 +1,7 @@
1
1
  import { transformWithEsbuild } from "vite";
2
2
  import { compile } from "../core/compile/index.js";
3
3
  import { getFileInfo } from "../vite-plugin-utils/index.js";
4
- import { frontmatterRE } from "./utils.js";
4
+ import { frontmatterRE, replaceTopLevelReturns } from "./utils.js";
5
5
  async function compileAstro({
6
6
  compileProps,
7
7
  astroFileToCompileMetadata,
@@ -70,7 +70,7 @@ async function enhanceCompileError({
70
70
  const lineText = err.loc?.lineText;
71
71
  const scannedFrontmatter = frontmatterRE.exec(source);
72
72
  if (scannedFrontmatter) {
73
- const frontmatter = scannedFrontmatter[1].replace(/\breturn\s*;/g, "throw 0;").replace(/\breturn\b/g, "throw ");
73
+ const frontmatter = replaceTopLevelReturns(scannedFrontmatter[1]);
74
74
  if (lineText && !frontmatter.includes(lineText)) throw err;
75
75
  try {
76
76
  await transformWithEsbuild(frontmatter, id, {
@@ -1,3 +1,4 @@
1
1
  import type { PluginContainer } from 'vite';
2
2
  export declare const frontmatterRE: RegExp;
3
+ export declare function replaceTopLevelReturns(code: string): string;
3
4
  export declare function loadId(pluginContainer: PluginContainer, id: string): Promise<string | undefined>;
@@ -1,5 +1,12 @@
1
1
  import fs from "node:fs/promises";
2
2
  const frontmatterRE = /^---(.*?)^---/ms;
3
+ const RETURN_REPLACE_RE = /(\/\/[^\n]*|\/\*[\s\S]*?\*\/|`(?:[^`\\]|\\.)*`|"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')|(?<!\.)\breturn(\s*;|\b)/g;
4
+ function replaceTopLevelReturns(code) {
5
+ return code.replace(RETURN_REPLACE_RE, (_match, skip, tail) => {
6
+ if (skip !== void 0) return skip;
7
+ return tail.trim() === ";" ? "throw 0;" : "throw ";
8
+ });
9
+ }
3
10
  async function loadId(pluginContainer, id) {
4
11
  const result = await pluginContainer.load(id, { ssr: true });
5
12
  if (result) {
@@ -16,5 +23,6 @@ async function loadId(pluginContainer, id) {
16
23
  }
17
24
  export {
18
25
  frontmatterRE,
19
- loadId
26
+ loadId,
27
+ replaceTopLevelReturns
20
28
  };
@@ -9,11 +9,20 @@ import { ASTRO_VITE_ENVIRONMENT_NAMES } from "../core/constants.js";
9
9
  const VIRTUAL_COMPONENT_METADATA = "virtual:astro:component-metadata";
10
10
  const RESOLVED_VIRTUAL_COMPONENT_METADATA = `\0${VIRTUAL_COMPONENT_METADATA}`;
11
11
  function configHeadVitePlugin() {
12
- let environment;
12
+ let environments = [];
13
+ function findModule(id) {
14
+ for (const env of environments) {
15
+ const mod = env.moduleGraph.getModuleById(id);
16
+ if (mod) return mod;
17
+ }
18
+ return void 0;
19
+ }
13
20
  function invalidateComponentMetadataModule() {
14
- const virtualMod = environment.moduleGraph.getModuleById(RESOLVED_VIRTUAL_COMPONENT_METADATA);
15
- if (virtualMod) {
16
- environment.moduleGraph.invalidateModule(virtualMod);
21
+ for (const env of environments) {
22
+ const virtualMod = env.moduleGraph.getModuleById(RESOLVED_VIRTUAL_COMPONENT_METADATA);
23
+ if (virtualMod) {
24
+ env.moduleGraph.invalidateModule(virtualMod);
25
+ }
17
26
  }
18
27
  }
19
28
  function buildImporterGraphFromEnvironment(seed) {
@@ -23,7 +32,7 @@ function configHeadVitePlugin() {
23
32
  const current = queue.pop();
24
33
  if (collected.has(current)) continue;
25
34
  collected.add(current);
26
- const mod = environment.moduleGraph.getModuleById(current);
35
+ const mod = findModule(current);
27
36
  for (const importer of mod?.importers ?? []) {
28
37
  if (importer.id) {
29
38
  queue.push(importer.id);
@@ -31,7 +40,7 @@ function configHeadVitePlugin() {
31
40
  }
32
41
  }
33
42
  return buildImporterGraphFromModuleInfo(collected, (id) => {
34
- const mod = environment.moduleGraph.getModuleById(id);
43
+ const mod = findModule(id);
35
44
  if (!mod) return null;
36
45
  return {
37
46
  importers: Array.from(mod.importers).map((importer) => importer.id).filter((moduleId) => !!moduleId),
@@ -61,7 +70,10 @@ function configHeadVitePlugin() {
61
70
  enforce: "pre",
62
71
  apply: "serve",
63
72
  configureServer(devServer) {
64
- environment = devServer.environments[ASTRO_VITE_ENVIRONMENT_NAMES.ssr];
73
+ environments = [
74
+ devServer.environments[ASTRO_VITE_ENVIRONMENT_NAMES.ssr],
75
+ devServer.environments[ASTRO_VITE_ENVIRONMENT_NAMES.prerender]
76
+ ].filter((e) => !!e);
65
77
  devServer.watcher.on("add", invalidateComponentMetadataModule);
66
78
  devServer.watcher.on("unlink", invalidateComponentMetadataModule);
67
79
  devServer.watcher.on("change", invalidateComponentMetadataModule);
@@ -70,19 +82,24 @@ function configHeadVitePlugin() {
70
82
  if (id !== RESOLVED_VIRTUAL_COMPONENT_METADATA) {
71
83
  return;
72
84
  }
85
+ const seen = /* @__PURE__ */ new Set();
73
86
  const componentMetadataEntries = [];
74
- for (const [moduleId, mod] of environment.moduleGraph.idToModuleMap) {
75
- const info = this.getModuleInfo(moduleId) ?? (mod.id ? this.getModuleInfo(mod.id) : null);
76
- if (!info) continue;
77
- const astro = getAstroMetadata(info);
78
- if (!astro) continue;
79
- componentMetadataEntries.push([
80
- moduleId,
81
- {
82
- containsHead: astro.containsHead,
83
- propagation: astro.propagation
84
- }
85
- ]);
87
+ for (const env of environments) {
88
+ for (const [moduleId, mod] of env.moduleGraph.idToModuleMap) {
89
+ if (seen.has(moduleId)) continue;
90
+ const info = this.getModuleInfo(moduleId) ?? (mod.id ? this.getModuleInfo(mod.id) : null);
91
+ if (!info) continue;
92
+ const astro = getAstroMetadata(info);
93
+ if (!astro) continue;
94
+ seen.add(moduleId);
95
+ componentMetadataEntries.push([
96
+ moduleId,
97
+ {
98
+ containsHead: astro.containsHead,
99
+ propagation: astro.propagation
100
+ }
101
+ ]);
102
+ }
86
103
  }
87
104
  return {
88
105
  code: `export const componentMetadataEntries = ${JSON.stringify(componentMetadataEntries)};`
@@ -8,6 +8,7 @@ export declare function getFileInfo(id: string, config: AstroConfig): {
8
8
  *
9
9
  * - /@fs/home/user/project/src/pages/index.astro
10
10
  * - /src/pages/index.astro
11
+ * - ./src/pages/index.astro
11
12
  *
12
13
  * as absolute file paths with forward slashes.
13
14
  */
@@ -23,6 +23,9 @@ function getFileInfo(id, config) {
23
23
  function normalizeFilename(filename, root) {
24
24
  if (filename.startsWith("/@fs")) {
25
25
  filename = filename.slice("/@fs".length);
26
+ } else if (filename.startsWith(".")) {
27
+ const url = new URL(filename, root);
28
+ filename = viteID(url);
26
29
  } else if (filename.startsWith("/") && !commonAncestorPath(filename, fileURLToPath(root))) {
27
30
  const url = new URL("." + filename, root);
28
31
  filename = viteID(url);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "6.2.0",
3
+ "version": "6.2.2",
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",
@@ -124,10 +124,12 @@
124
124
  "esbuild": "^0.27.3",
125
125
  "flattie": "^1.1.1",
126
126
  "fontace": "~0.4.1",
127
+ "get-tsconfig": "5.0.0-beta.4",
127
128
  "github-slugger": "^2.0.0",
128
129
  "html-escaper": "3.0.3",
129
130
  "http-cache-semantics": "^4.2.0",
130
131
  "js-yaml": "^4.1.1",
132
+ "jsonc-parser": "^3.3.1",
131
133
  "magic-string": "^0.30.21",
132
134
  "magicast": "^0.5.2",
133
135
  "mrmime": "^2.0.1",
@@ -146,7 +148,6 @@
146
148
  "tinyclip": "^0.1.12",
147
149
  "tinyexec": "^1.0.4",
148
150
  "tinyglobby": "^0.2.15",
149
- "tsconfck": "^3.1.6",
150
151
  "ultrahtml": "^1.6.0",
151
152
  "unifont": "~0.7.4",
152
153
  "unist-util-visit": "^5.1.0",
@@ -158,8 +159,8 @@
158
159
  "yargs-parser": "^22.0.0",
159
160
  "zod": "^4.3.6",
160
161
  "@astrojs/internal-helpers": "0.9.0",
161
- "@astrojs/telemetry": "3.3.1",
162
- "@astrojs/markdown-remark": "7.1.1"
162
+ "@astrojs/markdown-remark": "7.1.1",
163
+ "@astrojs/telemetry": "3.3.1"
163
164
  },
164
165
  "optionalDependencies": {
165
166
  "sharp": "^0.34.0"
@@ -194,8 +195,8 @@
194
195
  "undici": "^7.22.0",
195
196
  "unified": "^11.0.5",
196
197
  "vitest": "^4.1.0",
197
- "astro-scripts": "0.0.14",
198
- "@astrojs/check": "0.9.9"
198
+ "@astrojs/check": "0.9.9",
199
+ "astro-scripts": "0.0.14"
199
200
  },
200
201
  "engines": {
201
202
  "node": ">=22.12.0",