astro 2.1.7 → 2.1.9

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 (39) hide show
  1. package/astro-jsx.d.ts +3 -3
  2. package/dist/@types/astro.d.ts +13 -7
  3. package/dist/assets/internal.d.ts +7 -1
  4. package/dist/assets/internal.js +22 -3
  5. package/dist/assets/services/service.js +6 -0
  6. package/dist/assets/services/vendor/squoosh/copy-wasm.d.ts +1 -0
  7. package/dist/assets/services/vendor/squoosh/copy-wasm.js +19 -6
  8. package/dist/assets/types.d.ts +4 -1
  9. package/dist/assets/utils/transformToPath.d.ts +2 -1
  10. package/dist/assets/utils/transformToPath.js +8 -2
  11. package/dist/assets/vite-plugin-assets.js +11 -12
  12. package/dist/cli/index.js +1 -1
  13. package/dist/content/runtime-assets.d.ts +2 -2
  14. package/dist/content/runtime-assets.js +15 -4
  15. package/dist/content/types-generator.js +7 -1
  16. package/dist/content/utils.js +2 -2
  17. package/dist/content/vite-plugin-content-imports.js +1 -1
  18. package/dist/core/app/node.js +1 -1
  19. package/dist/core/build/generate.js +5 -1
  20. package/dist/core/config/config.js +4 -0
  21. package/dist/core/config/schema.d.ts +11 -0
  22. package/dist/core/config/schema.js +4 -1
  23. package/dist/core/constants.js +1 -1
  24. package/dist/core/create-vite.js +0 -2
  25. package/dist/core/dev/container.js +2 -2
  26. package/dist/core/dev/dev.js +2 -1
  27. package/dist/core/errors/dev/utils.js +6 -2
  28. package/dist/core/errors/errors-data.d.ts +81 -2
  29. package/dist/core/errors/errors-data.js +81 -2
  30. package/dist/core/errors/overlay.js +8 -6
  31. package/dist/core/messages.js +2 -2
  32. package/dist/core/preview/index.js +4 -1
  33. package/dist/core/preview/static-preview-server.js +2 -1
  34. package/dist/core/render/core.js +15 -0
  35. package/dist/core/render/paginate.js +15 -8
  36. package/dist/vite-plugin-markdown/index.js +24 -4
  37. package/package.json +1 -1
  38. package/dist/vite-plugin-integrations-container/index.d.ts +0 -8
  39. package/dist/vite-plugin-integrations-container/index.js +0 -15
package/astro-jsx.d.ts CHANGED
@@ -789,9 +789,9 @@ declare namespace astroHTML.JSX {
789
789
  fetchpriority?: 'auto' | 'high' | 'low' | undefined | null;
790
790
  integrity?: string | undefined | null;
791
791
  media?: string | undefined | null;
792
- imageSrcSet?: string | undefined | null;
793
- imageSizes?: string | undefined | null;
794
- referrerPolicy?: HTMLAttributeReferrerPolicy | undefined | null;
792
+ imagesrcset?: string | undefined | null;
793
+ imagesizes?: string | undefined | null;
794
+ referrerpolicy?: HTMLAttributeReferrerPolicy | undefined | null;
795
795
  rel?: string | undefined | null;
796
796
  sizes?: string | undefined | null;
797
797
  type?: string | undefined | null;
@@ -68,6 +68,7 @@ export interface CLIFlags {
68
68
  port?: number;
69
69
  config?: string;
70
70
  drafts?: boolean;
71
+ open?: boolean;
71
72
  experimentalAssets?: boolean;
72
73
  }
73
74
  export interface BuildConfig {
@@ -975,6 +976,7 @@ export interface ContentEntryType {
975
976
  contents: string;
976
977
  }): GetEntryInfoReturnType | Promise<GetEntryInfoReturnType>;
977
978
  getRenderModule?(this: rollup.PluginContext, params: {
979
+ viteId: string;
978
980
  entry: ContentEntryModule;
979
981
  }): rollup.LoadResult | Promise<rollup.LoadResult>;
980
982
  contentModuleTypes?: string;
@@ -1084,11 +1086,13 @@ export type GetStaticPaths = (options: GetStaticPathsOptions) => Promise<GetStat
1084
1086
  *
1085
1087
  * @example
1086
1088
  * ```ts
1087
- * export async function getStaticPaths() {
1089
+ * import type { GetStaticPaths } from 'astro';
1090
+ *
1091
+ * export const getStaticPaths = (() => {
1088
1092
  * return results.map((entry) => ({
1089
1093
  * params: { slug: entry.slug },
1090
1094
  * }));
1091
- * }
1095
+ * }) satisfies GetStaticPaths;
1092
1096
  *
1093
1097
  * type Params = InferGetStaticParamsType<typeof getStaticPaths>;
1094
1098
  * // ^? { slug: string; }
@@ -1096,7 +1100,7 @@ export type GetStaticPaths = (options: GetStaticPathsOptions) => Promise<GetStat
1096
1100
  * const { slug } = Astro.params as Params;
1097
1101
  * ```
1098
1102
  */
1099
- export type InferGetStaticParamsType<T> = T extends () => Promise<infer R> ? R extends Array<infer U> ? U extends {
1103
+ export type InferGetStaticParamsType<T> = T extends () => infer R | Promise<infer R> ? R extends Array<infer U> ? U extends {
1100
1104
  params: infer P;
1101
1105
  } ? P : never : never : never;
1102
1106
  /**
@@ -1104,7 +1108,9 @@ export type InferGetStaticParamsType<T> = T extends () => Promise<infer R> ? R e
1104
1108
  *
1105
1109
  * @example
1106
1110
  * ```ts
1107
- * export async function getStaticPaths() {
1111
+ * import type { GetStaticPaths } from 'astro';
1112
+ *
1113
+ * export const getStaticPaths = (() => {
1108
1114
  * return results.map((entry) => ({
1109
1115
  * params: { slug: entry.slug },
1110
1116
  * props: {
@@ -1112,15 +1118,15 @@ export type InferGetStaticParamsType<T> = T extends () => Promise<infer R> ? R e
1112
1118
  * propB: 42
1113
1119
  * },
1114
1120
  * }));
1115
- * }
1121
+ * }) satisfies GetStaticPaths;
1116
1122
  *
1117
1123
  * type Props = InferGetStaticPropsType<typeof getStaticPaths>;
1118
1124
  * // ^? { propA: boolean; propB: number; }
1119
1125
  *
1120
- * const { propA, propB } = Astro.props as Props;
1126
+ * const { propA, propB } = Astro.props;
1121
1127
  * ```
1122
1128
  */
1123
- export type InferGetStaticPropsType<T> = T extends () => Promise<infer R> ? R extends Array<infer U> ? U extends {
1129
+ export type InferGetStaticPropsType<T> = T extends () => infer R | Promise<infer R> ? R extends Array<infer U> ? U extends {
1124
1130
  props: infer P;
1125
1131
  } ? P : never : never : never;
1126
1132
  export interface HydrateOptions {
@@ -26,7 +26,13 @@ interface GetImageResult {
26
26
  * This is functionally equivalent to using the `<Image />` component, as the component calls this function internally.
27
27
  */
28
28
  export declare function getImage(options: ImageTransform): Promise<GetImageResult>;
29
- export declare function getStaticImageList(): Iterable<[ImageTransform, string]>;
29
+ export declare function getStaticImageList(): Iterable<[
30
+ string,
31
+ {
32
+ path: string;
33
+ options: ImageTransform;
34
+ }
35
+ ]>;
30
36
  interface GenerationData {
31
37
  weight: {
32
38
  before: number;
@@ -1,11 +1,14 @@
1
1
  import fs from "node:fs";
2
+ import { basename, join } from "node:path/posix";
2
3
  import { AstroError, AstroErrorData } from "../core/errors/index.js";
4
+ import { prependForwardSlash } from "../core/path.js";
3
5
  import { isLocalService } from "./services/service.js";
4
6
  function isESMImportedImage(src) {
5
7
  return typeof src === "object";
6
8
  }
7
9
  async function getConfiguredImageService() {
8
- if (!globalThis.astroAsset.imageService) {
10
+ var _a;
11
+ if (!((_a = globalThis == null ? void 0 : globalThis.astroAsset) == null ? void 0 : _a.imageService)) {
9
12
  const { default: service } = await import(
10
13
  // @ts-expect-error
11
14
  "virtual:image-service"
@@ -14,12 +17,20 @@ async function getConfiguredImageService() {
14
17
  error.cause = e;
15
18
  throw error;
16
19
  });
20
+ if (!globalThis.astroAsset)
21
+ globalThis.astroAsset = {};
17
22
  globalThis.astroAsset.imageService = service;
18
23
  return service;
19
24
  }
20
25
  return globalThis.astroAsset.imageService;
21
26
  }
22
27
  async function getImage(options) {
28
+ if (!options || typeof options !== "object") {
29
+ throw new AstroError({
30
+ ...AstroErrorData.ExpectedImageOptions,
31
+ message: AstroErrorData.ExpectedImageOptions.message(JSON.stringify(options))
32
+ });
33
+ }
23
34
  const service = await getConfiguredImageService();
24
35
  const validatedOptions = service.validateOptions ? service.validateOptions(options) : options;
25
36
  let imageURL = service.getURL(validatedOptions);
@@ -53,8 +64,16 @@ async function generateImage(buildOpts, options, filepath) {
53
64
  serverRoot = buildOpts.settings.config.outDir;
54
65
  clientRoot = buildOpts.settings.config.outDir;
55
66
  }
56
- const fileData = await fs.promises.readFile(new URL("." + options.src.src, serverRoot));
57
- const resultData = await imageService.transform(fileData, { ...options, src: options.src.src });
67
+ const originalImagePath = options.src.src;
68
+ const fileData = await fs.promises.readFile(
69
+ new URL(
70
+ "." + prependForwardSlash(
71
+ join(buildOpts.settings.config.build.assets, basename(originalImagePath))
72
+ ),
73
+ serverRoot
74
+ )
75
+ );
76
+ const resultData = await imageService.transform(fileData, { ...options, src: originalImagePath });
58
77
  const finalFileURL = new URL("." + filepath, clientRoot);
59
78
  const finalFolderURL = new URL("./", finalFileURL);
60
79
  await fs.promises.mkdir(finalFolderURL, { recursive: true });
@@ -16,6 +16,12 @@ function parseQuality(quality) {
16
16
  }
17
17
  const baseService = {
18
18
  validateOptions(options) {
19
+ if (!options.src || typeof options.src !== "string" && typeof options.src !== "object") {
20
+ throw new AstroError({
21
+ ...AstroErrorData.ExpectedImage,
22
+ message: AstroErrorData.ExpectedImage.message(JSON.stringify(options.src))
23
+ });
24
+ }
19
25
  if (!isESMImportedImage(options.src)) {
20
26
  let missingDimension;
21
27
  if (!options.width && !options.height) {
@@ -1 +1,2 @@
1
1
  export declare function copyWasmFiles(dir: URL): Promise<void>;
2
+ export declare function deleteWasmFiles(dir: URL): Promise<void>;
@@ -3,22 +3,35 @@ import path from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  async function copyWasmFiles(dir) {
5
5
  const src = new URL("./", import.meta.url);
6
- await copyDir(fileURLToPath(src), fileURLToPath(dir));
6
+ const fileList = await listFiles(fileURLToPath(src), fileURLToPath(dir));
7
+ for (let file of fileList) {
8
+ await fs.mkdir(path.dirname(file.dest), { recursive: true });
9
+ await fs.copyFile(file.src, file.dest);
10
+ }
7
11
  }
8
- async function copyDir(src, dest) {
12
+ async function deleteWasmFiles(dir) {
13
+ const src = new URL("./", import.meta.url);
14
+ const fileList = await listFiles(fileURLToPath(src), fileURLToPath(dir));
15
+ for (let file of fileList) {
16
+ await fs.rm(file.dest);
17
+ }
18
+ }
19
+ async function listFiles(src, dest) {
9
20
  const itemNames = await fs.readdir(src);
21
+ const copiedFiles = [];
10
22
  await Promise.all(itemNames.map(async (srcName) => {
11
23
  const srcPath = path.join(src, srcName);
12
24
  const destPath = path.join(dest, srcName);
13
25
  const s = await fs.stat(srcPath);
14
26
  if (s.isFile() && /.wasm$/.test(srcPath)) {
15
- await fs.mkdir(path.dirname(destPath), { recursive: true });
16
- await fs.copyFile(srcPath, destPath);
27
+ copiedFiles.push({ src: srcPath, dest: destPath });
17
28
  } else if (s.isDirectory()) {
18
- await copyDir(srcPath, destPath);
29
+ copiedFiles.push(...await listFiles(srcPath, destPath));
19
30
  }
20
31
  }));
32
+ return copiedFiles;
21
33
  }
22
34
  export {
23
- copyWasmFiles
35
+ copyWasmFiles,
36
+ deleteWasmFiles
24
37
  };
@@ -8,7 +8,10 @@ declare global {
8
8
  var astroAsset: {
9
9
  imageService?: ImageService;
10
10
  addStaticImage?: ((options: ImageTransform) => string) | undefined;
11
- staticImages?: Map<ImageTransform, string>;
11
+ staticImages?: Map<string, {
12
+ path: string;
13
+ options: ImageTransform;
14
+ }>;
12
15
  };
13
16
  }
14
17
  /**
@@ -1,2 +1,3 @@
1
1
  import type { ImageTransform } from '../types.js';
2
- export declare function propsToFilename(transform: ImageTransform): string;
2
+ export declare function propsToFilename(transform: ImageTransform, hash: string): string;
3
+ export declare function hashTransform(transform: ImageTransform, imageService: string): string;
@@ -2,7 +2,7 @@ import { basename, extname } from "path";
2
2
  import { removeQueryString } from "../../core/path.js";
3
3
  import { shorthash } from "../../runtime/server/shorthash.js";
4
4
  import { isESMImportedImage } from "../internal.js";
5
- function propsToFilename(transform) {
5
+ function propsToFilename(transform, hash) {
6
6
  if (!isESMImportedImage(transform.src)) {
7
7
  return transform.src;
8
8
  }
@@ -10,8 +10,14 @@ function propsToFilename(transform) {
10
10
  const ext = extname(filename);
11
11
  filename = basename(filename, ext);
12
12
  const outputExt = transform.format ? `.${transform.format}` : ext;
13
- return `/${filename}_${shorthash(JSON.stringify(transform))}${outputExt}`;
13
+ return `/${filename}_${hash}${outputExt}`;
14
+ }
15
+ function hashTransform(transform, imageService) {
16
+ const { alt, ...rest } = transform;
17
+ const hashFields = { ...rest, imageService };
18
+ return shorthash(JSON.stringify(hashFields));
14
19
  }
15
20
  export {
21
+ hashTransform,
16
22
  propsToFilename
17
23
  };
@@ -14,7 +14,7 @@ import { copyWasmFiles } from "./services/vendor/squoosh/copy-wasm.js";
14
14
  import { emitESMImage } from "./utils/emitAsset.js";
15
15
  import { imageMetadata } from "./utils/metadata.js";
16
16
  import { getOrigQueryParams } from "./utils/queryParams.js";
17
- import { propsToFilename } from "./utils/transformToPath.js";
17
+ import { hashTransform, propsToFilename } from "./utils/transformToPath.js";
18
18
  const resolvedVirtualModuleId = "\0" + VIRTUAL_MODULE_ID;
19
19
  function assets({
20
20
  settings,
@@ -126,31 +126,30 @@ function assets({
126
126
  if (!globalThis.astroAsset.staticImages) {
127
127
  globalThis.astroAsset.staticImages = /* @__PURE__ */ new Map();
128
128
  }
129
+ const hash = hashTransform(options, settings.config.image.service);
129
130
  let filePath;
130
- if (globalThis.astroAsset.staticImages.has(options)) {
131
- filePath = globalThis.astroAsset.staticImages.get(options);
131
+ if (globalThis.astroAsset.staticImages.has(hash)) {
132
+ filePath = globalThis.astroAsset.staticImages.get(hash).path;
132
133
  } else {
133
134
  if (!isESMImportedImage(options.src)) {
134
135
  return options.src;
135
136
  }
136
137
  filePath = prependForwardSlash(
137
- joinPaths(
138
- settings.config.base,
139
- settings.config.build.assets,
140
- propsToFilename(options)
141
- )
138
+ joinPaths(settings.config.build.assets, propsToFilename(options, hash))
142
139
  );
143
- globalThis.astroAsset.staticImages.set(options, filePath);
140
+ globalThis.astroAsset.staticImages.set(hash, { path: filePath, options });
144
141
  }
145
- return filePath;
142
+ return prependForwardSlash(joinPaths(settings.config.base, filePath));
146
143
  };
147
144
  },
148
145
  async buildEnd() {
149
146
  if (mode != "build") {
150
147
  return;
151
148
  }
152
- const dir = settings.config.output === "server" ? settings.config.build.server : settings.config.outDir;
153
- await copyWasmFiles(new URL("./chunks", dir));
149
+ if (settings.config.image.service === "astro/assets/services/squoosh") {
150
+ const dir = settings.config.output === "server" ? settings.config.build.server : settings.config.outDir;
151
+ await copyWasmFiles(new URL("./chunks", dir));
152
+ }
154
153
  },
155
154
  // In build, rewrite paths to ESM imported images in code to their final location
156
155
  async renderChunk(code) {
package/dist/cli/index.js CHANGED
@@ -16,7 +16,6 @@ import { enableVerboseLogging, nodeLogDestination } from "../core/logger/node.js
16
16
  import { formatConfigErrorMessage, formatErrorMessage, printHelp } from "../core/messages.js";
17
17
  import * as event from "../events/index.js";
18
18
  import { eventConfigError, eventError, telemetry } from "../events/index.js";
19
- import { check } from "./check/index.js";
20
19
  import { openInBrowser } from "./open.js";
21
20
  function printAstroHelp() {
22
21
  printHelp({
@@ -174,6 +173,7 @@ async function runCommand(cmd, flags) {
174
173
  });
175
174
  }
176
175
  case "check": {
176
+ const { check } = await import("./check/index.js");
177
177
  const checkServer = await check(settings, { flags, logging });
178
178
  if (checkServer) {
179
179
  if (checkServer.isWatchMode) {
@@ -3,6 +3,6 @@ import { type Metadata } from '../assets/utils/metadata.js';
3
3
  export declare function createImage(options: {
4
4
  assetsDir: string;
5
5
  relAssetsDir: string;
6
- }): () => z.ZodEffects<z.ZodString, (Metadata & {
6
+ }): () => z.ZodEffects<z.ZodString, Metadata & {
7
7
  __astro_asset: true;
8
- }) | undefined, string>;
8
+ }, string>;
@@ -1,18 +1,29 @@
1
1
  import { pathToFileURL } from "url";
2
2
  import { z } from "zod";
3
- import { imageMetadata } from "../assets/utils/metadata.js";
3
+ import {
4
+ imageMetadata as internalGetImageMetadata
5
+ } from "../assets/utils/metadata.js";
4
6
  function createImage(options) {
5
7
  return () => {
6
8
  if (options.assetsDir === "undefined") {
7
9
  throw new Error("Enable `experimental.assets` in your Astro config to use image()");
8
10
  }
9
- return z.string({ description: "__image" }).transform(async (imagePath) => {
10
- return await getImageMetadata(pathToFileURL(imagePath));
11
+ return z.string({ description: "__image" }).transform(async (imagePath, ctx) => {
12
+ const imageMetadata = await getImageMetadata(pathToFileURL(imagePath));
13
+ if (!imageMetadata) {
14
+ ctx.addIssue({
15
+ code: "custom",
16
+ message: `Image ${imagePath} does not exist. Is the path correct?`,
17
+ fatal: true
18
+ });
19
+ return z.NEVER;
20
+ }
21
+ return imageMetadata;
11
22
  });
12
23
  };
13
24
  }
14
25
  async function getImageMetadata(imagePath) {
15
- const meta = await imageMetadata(imagePath);
26
+ const meta = await internalGetImageMetadata(imagePath);
16
27
  if (!meta) {
17
28
  return void 0;
18
29
  }
@@ -173,8 +173,14 @@ async function createContentTypesGenerator({
173
173
  return;
174
174
  events.push(event);
175
175
  debounceTimeout && clearTimeout(debounceTimeout);
176
+ const runEventsSafe = async () => {
177
+ try {
178
+ await runEvents(opts);
179
+ } catch {
180
+ }
181
+ };
176
182
  debounceTimeout = setTimeout(
177
- async () => runEvents(opts),
183
+ runEventsSafe,
178
184
  50
179
185
  /* debounce to batch chokidar events */
180
186
  );
@@ -83,10 +83,10 @@ async function getEntryData(entry, collectionConfig, resolver) {
83
83
  var _a;
84
84
  if (!value || typeof value !== "string")
85
85
  return value;
86
- return (_a = await resolver(value)) == null ? void 0 : _a.id;
86
+ return ((_a = await resolver(value)) == null ? void 0 : _a.id) ?? path.join(path.dirname(entry._internal.filePath), value);
87
87
  },
88
88
  schema,
89
- { description: void 0 }
89
+ { description: "__image" }
90
90
  );
91
91
  } else if ("shape" in schema) {
92
92
  await preprocessAssetPaths(schema.shape);
@@ -106,7 +106,7 @@ export const _internal = {
106
106
  )}. Did you import this module directly without using a content collection query?`
107
107
  });
108
108
  }
109
- return contentRenderer.bind(this)({ entry });
109
+ return contentRenderer.bind(this)({ entry, viteId });
110
110
  }
111
111
  });
112
112
  }
@@ -34,7 +34,7 @@ class NodeApp extends App {
34
34
  routeData
35
35
  );
36
36
  }
37
- if (typeof req.body === "object" && Object.keys(req.body).length > 0) {
37
+ if (typeof req.body === "object" && req.body !== null && Object.keys(req.body).length > 0) {
38
38
  return super.render(
39
39
  req instanceof Request ? req : createRequestFromNodeRequest(req, Buffer.from(JSON.stringify(req.body))),
40
40
  routeData
@@ -7,6 +7,7 @@ import {
7
7
  generateImage as generateImageInternal,
8
8
  getStaticImageList
9
9
  } from "../../assets/internal.js";
10
+ import { deleteWasmFiles } from "../../assets/services/vendor/squoosh/copy-wasm.js";
10
11
  import { hasPrerenderedPages } from "../../core/build/internal.js";
11
12
  import {
12
13
  prependForwardSlash,
@@ -82,7 +83,10 @@ ${bgGreen(black(` ${verb} static routes `))}`);
82
83
  info(opts.logging, null, `
83
84
  ${bgGreen(black(` generating optimized images `))}`);
84
85
  for (const imageData of getStaticImageList()) {
85
- await generateImage(opts, imageData[0], imageData[1]);
86
+ await generateImage(opts, imageData[1].options, imageData[1].path);
87
+ }
88
+ if (opts.settings.config.image.service === "astro/assets/services/squoosh" && opts.settings.config.output === "static") {
89
+ await deleteWasmFiles(new URL("./chunks", opts.settings.config.outDir));
86
90
  }
87
91
  delete globalThis.astroAsset.addStaticImage;
88
92
  }
@@ -71,6 +71,7 @@ function resolveFlags(flags) {
71
71
  site: typeof flags.site === "string" ? flags.site : void 0,
72
72
  base: typeof flags.base === "string" ? flags.base : void 0,
73
73
  port: typeof flags.port === "number" ? flags.port : void 0,
74
+ open: typeof flags.open === "boolean" ? flags.open : void 0,
74
75
  config: typeof flags.config === "string" ? flags.config : void 0,
75
76
  host: typeof flags.host === "string" || typeof flags.host === "boolean" ? flags.host : void 0,
76
77
  drafts: typeof flags.drafts === "boolean" ? flags.drafts : void 0,
@@ -99,6 +100,9 @@ function mergeCLIFlags(astroConfig, flags) {
99
100
  if (typeof flags.host === "string" || typeof flags.host === "boolean") {
100
101
  astroConfig.server.host = flags.host;
101
102
  }
103
+ if (typeof flags.open === "boolean") {
104
+ astroConfig.server.open = flags.open;
105
+ }
102
106
  return astroConfig;
103
107
  }
104
108
  async function search(fsMod, root) {
@@ -56,19 +56,23 @@ export declare const AstroConfigSchema: z.ZodObject<{
56
56
  serverEntry?: string | undefined;
57
57
  }>>>;
58
58
  server: z.ZodEffects<z.ZodDefault<z.ZodOptional<z.ZodObject<{
59
+ open: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
59
60
  host: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodBoolean]>>>;
60
61
  port: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
61
62
  headers: z.ZodOptional<z.ZodType<OutgoingHttpHeaders, z.ZodTypeDef, OutgoingHttpHeaders>>;
62
63
  }, "strip", z.ZodTypeAny, {
63
64
  headers?: OutgoingHttpHeaders | undefined;
65
+ open: boolean;
64
66
  host: string | boolean;
65
67
  port: number;
66
68
  }, {
69
+ open?: boolean | undefined;
67
70
  host?: string | boolean | undefined;
68
71
  port?: number | undefined;
69
72
  headers?: OutgoingHttpHeaders | undefined;
70
73
  }>>>, {
71
74
  headers?: OutgoingHttpHeaders | undefined;
75
+ open: boolean;
72
76
  host: string | boolean;
73
77
  port: number;
74
78
  }, unknown>;
@@ -165,6 +169,7 @@ export declare const AstroConfigSchema: z.ZodObject<{
165
169
  trailingSlash: "never" | "always" | "ignore";
166
170
  server: {
167
171
  headers?: OutgoingHttpHeaders | undefined;
172
+ open: boolean;
168
173
  host: string | boolean;
169
174
  port: number;
170
175
  };
@@ -350,20 +355,24 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: URL)
350
355
  server: z.ZodEffects<z.ZodDefault<z.ZodOptional<z.ZodObject<{
351
356
  host: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodBoolean]>>>;
352
357
  port: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
358
+ open: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
353
359
  headers: z.ZodOptional<z.ZodType<OutgoingHttpHeaders, z.ZodTypeDef, OutgoingHttpHeaders>>;
354
360
  streaming: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
355
361
  }, "strip", z.ZodTypeAny, {
356
362
  headers?: OutgoingHttpHeaders | undefined;
363
+ open: boolean;
357
364
  host: string | boolean;
358
365
  port: number;
359
366
  streaming: boolean;
360
367
  }, {
368
+ open?: boolean | undefined;
361
369
  host?: string | boolean | undefined;
362
370
  port?: number | undefined;
363
371
  headers?: OutgoingHttpHeaders | undefined;
364
372
  streaming?: boolean | undefined;
365
373
  }>>>, {
366
374
  headers?: OutgoingHttpHeaders | undefined;
375
+ open: boolean;
367
376
  host: string | boolean;
368
377
  port: number;
369
378
  streaming: boolean;
@@ -397,6 +406,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: URL)
397
406
  trailingSlash: "never" | "always" | "ignore";
398
407
  server: {
399
408
  headers?: OutgoingHttpHeaders | undefined;
409
+ open: boolean;
400
410
  host: string | boolean;
401
411
  port: number;
402
412
  streaming: boolean;
@@ -493,6 +503,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: URL)
493
503
  trailingSlash: "never" | "always" | "ignore";
494
504
  server: {
495
505
  headers?: OutgoingHttpHeaders | undefined;
506
+ open: boolean;
496
507
  host: string | boolean;
497
508
  port: number;
498
509
  streaming: boolean;
@@ -19,7 +19,8 @@ const ASTRO_CONFIG_DEFAULTS = {
19
19
  server: {
20
20
  host: false,
21
21
  port: 3e3,
22
- streaming: true
22
+ streaming: true,
23
+ open: false
23
24
  },
24
25
  integrations: [],
25
26
  markdown: {
@@ -62,6 +63,7 @@ const AstroConfigSchema = z.object({
62
63
  (val) => typeof val === "function" ? val({ command: "error" }) : val,
63
64
  // validate
64
65
  z.object({
66
+ open: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.server.open),
65
67
  host: z.union([z.string(), z.boolean()]).optional().default(ASTRO_CONFIG_DEFAULTS.server.host),
66
68
  port: z.number().optional().default(ASTRO_CONFIG_DEFAULTS.server.port),
67
69
  headers: z.custom().optional()
@@ -137,6 +139,7 @@ function createRelativeSchema(cmd, fileProtocolRoot) {
137
139
  z.object({
138
140
  host: z.union([z.string(), z.boolean()]).optional().default(ASTRO_CONFIG_DEFAULTS.server.host),
139
141
  port: z.number().optional().default(ASTRO_CONFIG_DEFAULTS.server.port),
142
+ open: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.server.open),
140
143
  headers: z.custom().optional(),
141
144
  streaming: z.boolean().optional().default(true)
142
145
  }).optional().default({})
@@ -1,4 +1,4 @@
1
- const ASTRO_VERSION = "2.1.7";
1
+ const ASTRO_VERSION = "2.1.9";
2
2
  const SUPPORTED_MARKDOWN_FILE_EXTENSIONS = [
3
3
  ".markdown",
4
4
  ".mdown",
@@ -16,7 +16,6 @@ import envVitePlugin from "../vite-plugin-env/index.js";
16
16
  import astroHeadPlugin from "../vite-plugin-head/index.js";
17
17
  import htmlVitePlugin from "../vite-plugin-html/index.js";
18
18
  import { astroInjectEnvTsPlugin } from "../vite-plugin-inject-env-ts/index.js";
19
- import astroIntegrationsContainerPlugin from "../vite-plugin-integrations-container/index.js";
20
19
  import jsxVitePlugin from "../vite-plugin-jsx/index.js";
21
20
  import astroLoadFallbackPlugin from "../vite-plugin-load-fallback/index.js";
22
21
  import markdownVitePlugin from "../vite-plugin-markdown/index.js";
@@ -93,7 +92,6 @@ async function createVite(commandConfig, { settings, logging, mode, command, fs
93
92
  htmlVitePlugin(),
94
93
  jsxVitePlugin({ settings, logging }),
95
94
  astroPostprocessVitePlugin({ settings }),
96
- astroIntegrationsContainerPlugin({ settings, logging }),
97
95
  astroScriptsPageSSRPlugin({ settings }),
98
96
  astroHeadPlugin({ settings }),
99
97
  astroScannerPlugin({ settings }),
@@ -34,12 +34,12 @@ async function createContainer(params = {}) {
34
34
  logging,
35
35
  isRestart
36
36
  });
37
- const { host, headers } = settings.config.server;
37
+ const { host, headers, open } = settings.config.server;
38
38
  const rendererClientEntries = settings.renderers.map((r) => r.clientEntrypoint).filter(Boolean);
39
39
  const viteConfig = await createVite(
40
40
  {
41
41
  mode: "development",
42
- server: { host, headers },
42
+ server: { host, headers, open },
43
43
  optimizeDeps: {
44
44
  include: rendererClientEntries
45
45
  },
@@ -17,6 +17,7 @@ async function dev(settings, options) {
17
17
  ["--port", `Specify which port to run on. Defaults to 3000.`],
18
18
  ["--host", `Listen on all addresses, including LAN and public addresses.`],
19
19
  ["--host <custom-address>", `Expose on a network IP address at <custom-address>`],
20
+ ["--open", "Automatically open the app in the browser on server start"],
20
21
  ["--help (-h)", "See all available flags."]
21
22
  ]
22
23
  },
@@ -52,7 +53,7 @@ async function dev(settings, options) {
52
53
  isRestart: options.isRestart
53
54
  })
54
55
  );
55
- const currentVersion = "2.1.7";
56
+ const currentVersion = "2.1.9";
56
57
  if (currentVersion.includes("-")) {
57
58
  warn(options.logging, null, msg.prerelease({ currentVersion }));
58
59
  }
@@ -4,17 +4,21 @@ import * as fs from "node:fs";
4
4
  import { isAbsolute, join } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import stripAnsi from "strip-ansi";
7
+ import { normalizePath } from "vite";
8
+ import { removeLeadingForwardSlashWindows } from "../../path.js";
7
9
  import { AggregateError } from "../errors.js";
8
10
  import { codeFrame } from "../printer.js";
9
11
  import { normalizeLF } from "../utils.js";
10
12
  function collectErrorMetadata(e, rootFolder) {
11
13
  const err = AggregateError.is(e) || Array.isArray(e.errors) ? e.errors : [e];
12
14
  err.forEach((error) => {
13
- var _a;
15
+ var _a, _b;
14
16
  if (error.stack) {
15
17
  error = collectInfoFromStacktrace(e);
16
18
  }
17
- if (((_a = error.loc) == null ? void 0 : _a.file) && rootFolder && (!error.loc.file.startsWith(rootFolder.pathname) || !isAbsolute(error.loc.file))) {
19
+ const normalizedFile = normalizePath(((_a = error.loc) == null ? void 0 : _a.file) || "");
20
+ const normalizedRootFolder = removeLeadingForwardSlashWindows((rootFolder == null ? void 0 : rootFolder.pathname) || "");
21
+ if (((_b = error.loc) == null ? void 0 : _b.file) && rootFolder && (!(normalizedFile == null ? void 0 : normalizedFile.startsWith(normalizedRootFolder)) || !isAbsolute(normalizedFile))) {
18
22
  error.loc.file = join(fileURLToPath(rootFolder), error.loc.file);
19
23
  }
20
24
  if (error.loc && (!error.frame || !error.fullCode)) {
@@ -409,7 +409,7 @@ export declare const AstroErrorData: {
409
409
  * If the image is merely decorative (i.e. doesn’t contribute to the understanding of the page), set `alt=""` so that screen readers know to ignore the image.
410
410
  */
411
411
  readonly ImageMissingAlt: {
412
- readonly title: "Missing alt property";
412
+ readonly title: "Missing alt property.";
413
413
  readonly code: 3022;
414
414
  readonly message: "The alt property is required.";
415
415
  readonly hint: "The `alt` property is important for the purpose of accessibility, without it users using screen readers or other assistive technologies won't be able to understand what your image is supposed to represent. See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-alt for more information.";
@@ -424,7 +424,7 @@ export declare const AstroErrorData: {
424
424
  * If you believe that your service is properly configured and this error is wrong, please [open an issue](https://astro.build/issues/).
425
425
  */
426
426
  readonly InvalidImageService: {
427
- readonly title: "Error while loading image service";
427
+ readonly title: "Error while loading image service.";
428
428
  readonly code: 3023;
429
429
  readonly message: "There was an error loading the configured image service. Please see the stack trace for more information.";
430
430
  };
@@ -466,6 +466,85 @@ export declare const AstroErrorData: {
466
466
  readonly message: (format: string, imagePath: string, supportedFormats: readonly string[]) => string;
467
467
  readonly hint: "If you do not need optimization, using an `img` tag directly instead of the `Image` component might be what you're looking for.";
468
468
  };
469
+ /**
470
+ * @docs
471
+ * @see
472
+ * - [`getStaticPaths()`](https://docs.astro.build/en/reference/api-reference/#getstaticpaths)
473
+ * - [`params`](https://docs.astro.build/en/reference/api-reference/#params)
474
+ * @description
475
+ * The endpoint is prerendered with an `undefined` param so the generated path will collide with another route.
476
+ *
477
+ * If you cannot prevent passing `undefined`, then an additional extension can be added to the endpoint file name to generate the file with a different name. For example, renaming `pages/api/[slug].ts` to `pages/api/[slug].json.ts`.
478
+ */
479
+ readonly PrerenderDynamicEndpointPathCollide: {
480
+ readonly title: "Prerendered dynamic endpoint has path collision.";
481
+ readonly code: 3026;
482
+ readonly message: (pathname: string) => string;
483
+ readonly hint: (filename: string) => string;
484
+ };
485
+ /**
486
+ * @docs
487
+ * @see
488
+ * - [Assets (Experimental)](https://docs.astro.build/en/guides/assets/)
489
+ * @description
490
+ * An image's `src` property is not valid. The Image component requires the `src` attribute to be either an image that has been ESM imported or a string. This is also true for the first parameter of `getImage()`.
491
+ *
492
+ * ```astro
493
+ * ---
494
+ * import { Image } from "astro:assets";
495
+ * import myImage from "../assets/my_image.png";
496
+ * ---
497
+ *
498
+ * <Image src={myImage} alt="..." />
499
+ * <Image src="https://example.com/logo.png" width={300} height={300} alt="..." />
500
+ * ```
501
+ *
502
+ * In most cases, this error happens when the value passed to `src` is undefined.
503
+ */
504
+ readonly ExpectedImage: {
505
+ readonly title: "Expected src to be an image.";
506
+ readonly code: 3027;
507
+ readonly message: (options: string) => string;
508
+ readonly hint: "This error can often happen because of a wrong path. Make sure the path to your image is correct.";
509
+ };
510
+ /**
511
+ * @docs
512
+ * @see
513
+ * - [Assets (Experimental)](https://docs.astro.build/en/guides/assets/)
514
+ * @description
515
+ * `getImage()`'s first parameter should be an object with the different properties to apply to your image.
516
+ *
517
+ * ```ts
518
+ * import { getImage } from "astro:assets";
519
+ * import myImage from "../assets/my_image.png";
520
+ *
521
+ * const optimizedImage = await getImage({src: myImage, width: 300, height: 300});
522
+ * ```
523
+ *
524
+ * In most cases, this error happens because parameters were passed directly instead of inside an object.
525
+ */
526
+ readonly ExpectedImageOptions: {
527
+ readonly title: "Expected image options.";
528
+ readonly code: 3028;
529
+ readonly message: (options: string) => string;
530
+ };
531
+ /**
532
+ * @docs
533
+ * @message
534
+ * Could not find requested image `IMAGE_PATH` at `FULL_IMAGE_PATH`.
535
+ * @see
536
+ * - [Assets (Experimental)](https://docs.astro.build/en/guides/assets/)
537
+ * @description
538
+ * Astro could not find an image you included in your Markdown content. Usually, this is simply caused by a typo in the path.
539
+ *
540
+ * Images in Markdown are relative to the current file. To refer to an image that is located in the same folder as the `.md` file, the path should start with `./`
541
+ */
542
+ readonly MarkdownImageNotFound: {
543
+ readonly title: "Image not found.";
544
+ readonly code: 3029;
545
+ readonly message: (imagePath: string, fullImagePath: string | undefined) => string;
546
+ readonly hint: "This is often caused by a typo in the image path. Please make sure the file exists, and is spelled correctly.";
547
+ };
469
548
  /**
470
549
  * @docs
471
550
  * @see
@@ -420,7 +420,7 @@ Expected \`true\` value but got \`${suffix}\`.`;
420
420
  * If the image is merely decorative (i.e. doesn’t contribute to the understanding of the page), set `alt=""` so that screen readers know to ignore the image.
421
421
  */
422
422
  ImageMissingAlt: {
423
- title: "Missing alt property",
423
+ title: "Missing alt property.",
424
424
  code: 3022,
425
425
  message: "The alt property is required.",
426
426
  hint: "The `alt` property is important for the purpose of accessibility, without it users using screen readers or other assistive technologies won't be able to understand what your image is supposed to represent. See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-alt for more information."
@@ -435,7 +435,7 @@ Expected \`true\` value but got \`${suffix}\`.`;
435
435
  * If you believe that your service is properly configured and this error is wrong, please [open an issue](https://astro.build/issues/).
436
436
  */
437
437
  InvalidImageService: {
438
- title: "Error while loading image service",
438
+ title: "Error while loading image service.",
439
439
  code: 3023,
440
440
  message: "There was an error loading the configured image service. Please see the stack trace for more information."
441
441
  },
@@ -479,6 +479,85 @@ Expected \`true\` value but got \`${suffix}\`.`;
479
479
  )} are supported for optimization.`,
480
480
  hint: "If you do not need optimization, using an `img` tag directly instead of the `Image` component might be what you're looking for."
481
481
  },
482
+ /**
483
+ * @docs
484
+ * @see
485
+ * - [`getStaticPaths()`](https://docs.astro.build/en/reference/api-reference/#getstaticpaths)
486
+ * - [`params`](https://docs.astro.build/en/reference/api-reference/#params)
487
+ * @description
488
+ * The endpoint is prerendered with an `undefined` param so the generated path will collide with another route.
489
+ *
490
+ * If you cannot prevent passing `undefined`, then an additional extension can be added to the endpoint file name to generate the file with a different name. For example, renaming `pages/api/[slug].ts` to `pages/api/[slug].json.ts`.
491
+ */
492
+ PrerenderDynamicEndpointPathCollide: {
493
+ title: "Prerendered dynamic endpoint has path collision.",
494
+ code: 3026,
495
+ message: (pathname) => `Could not render \`${pathname}\` with an \`undefined\` param as the generated path will collide during prerendering. Prevent passing \`undefined\` as \`params\` for the endpoint's \`getStaticPaths()\` function, or add an additional extension to the endpoint's filename.`,
496
+ hint: (filename) => `Rename \`${filename}\` to \`${filename.replace(/\.(js|ts)/, (m) => `.json` + m)}\``
497
+ },
498
+ /**
499
+ * @docs
500
+ * @see
501
+ * - [Assets (Experimental)](https://docs.astro.build/en/guides/assets/)
502
+ * @description
503
+ * An image's `src` property is not valid. The Image component requires the `src` attribute to be either an image that has been ESM imported or a string. This is also true for the first parameter of `getImage()`.
504
+ *
505
+ * ```astro
506
+ * ---
507
+ * import { Image } from "astro:assets";
508
+ * import myImage from "../assets/my_image.png";
509
+ * ---
510
+ *
511
+ * <Image src={myImage} alt="..." />
512
+ * <Image src="https://example.com/logo.png" width={300} height={300} alt="..." />
513
+ * ```
514
+ *
515
+ * In most cases, this error happens when the value passed to `src` is undefined.
516
+ */
517
+ ExpectedImage: {
518
+ title: "Expected src to be an image.",
519
+ code: 3027,
520
+ message: (options) => `Expected \`src\` property to be either an ESM imported image or a string with the path of a remote image. Received \`${options}\`.`,
521
+ hint: "This error can often happen because of a wrong path. Make sure the path to your image is correct."
522
+ },
523
+ /**
524
+ * @docs
525
+ * @see
526
+ * - [Assets (Experimental)](https://docs.astro.build/en/guides/assets/)
527
+ * @description
528
+ * `getImage()`'s first parameter should be an object with the different properties to apply to your image.
529
+ *
530
+ * ```ts
531
+ * import { getImage } from "astro:assets";
532
+ * import myImage from "../assets/my_image.png";
533
+ *
534
+ * const optimizedImage = await getImage({src: myImage, width: 300, height: 300});
535
+ * ```
536
+ *
537
+ * In most cases, this error happens because parameters were passed directly instead of inside an object.
538
+ */
539
+ ExpectedImageOptions: {
540
+ title: "Expected image options.",
541
+ code: 3028,
542
+ message: (options) => `Expected getImage() parameter to be an object. Received \`${options}\`.`
543
+ },
544
+ /**
545
+ * @docs
546
+ * @message
547
+ * Could not find requested image `IMAGE_PATH` at `FULL_IMAGE_PATH`.
548
+ * @see
549
+ * - [Assets (Experimental)](https://docs.astro.build/en/guides/assets/)
550
+ * @description
551
+ * Astro could not find an image you included in your Markdown content. Usually, this is simply caused by a typo in the path.
552
+ *
553
+ * Images in Markdown are relative to the current file. To refer to an image that is located in the same folder as the `.md` file, the path should start with `./`
554
+ */
555
+ MarkdownImageNotFound: {
556
+ title: "Image not found.",
557
+ code: 3029,
558
+ message: (imagePath, fullImagePath) => `Could not find requested image \`${imagePath}\`${fullImagePath ? ` at \`${fullImagePath}\`.` : "."}`,
559
+ hint: "This is often caused by a typo in the image path. Please make sure the file exists, and is spelled correctly."
560
+ },
482
561
  // No headings here, that way Vite errors are merged with Astro ones in the docs, which makes more sense to users.
483
562
  // Vite Errors - 4xxx
484
563
  /**
@@ -77,7 +77,7 @@ const style = (
77
77
  --shiki-token-function: #4ca48f;
78
78
  --shiki-token-string-expression: #9f722a;
79
79
  --shiki-token-punctuation: #ffffff;
80
- --shiki-token-link: #ee0000;
80
+ --shiki-token-link: #9f722a;
81
81
  }
82
82
 
83
83
  :host(.astro-dark) {
@@ -130,9 +130,9 @@ const style = (
130
130
  --shiki-token-function: #90f4e3;
131
131
  --shiki-token-string-expression: #f4cf90;
132
132
  --shiki-token-punctuation: #ffffff;
133
- --shiki-token-link: #ee0000;
133
+ --shiki-token-link: #f4cf90;
134
134
  }
135
-
135
+
136
136
  #theme-toggle-wrapper{
137
137
  position: relative;
138
138
  display: inline-block
@@ -143,7 +143,7 @@ const style = (
143
143
  right: 3px;
144
144
  margin-top: 3px;
145
145
  }
146
-
146
+
147
147
  .theme-toggle-checkbox {
148
148
  opacity: 0;
149
149
  position: absolute;
@@ -249,7 +249,7 @@ const style = (
249
249
  padding: 12px;
250
250
  margin-top: 12px;
251
251
  }
252
-
252
+
253
253
  #theme-toggle-wrapper > div{
254
254
  position: absolute;
255
255
  right: 22px;
@@ -371,6 +371,7 @@ const style = (
371
371
  background-color: var(--border);
372
372
  padding: 4px;
373
373
  border-radius: var(--roundiness);
374
+ white-space: nowrap;
374
375
  }
375
376
 
376
377
  .link {
@@ -623,7 +624,8 @@ class ErrorOverlay extends HTMLElement {
623
624
  const codeHeader = code.querySelector("#code header");
624
625
  const codeContent = code.querySelector("#code-content");
625
626
  if (codeHeader) {
626
- const cleanFile = err.loc.file.split("/").slice(-2).join("/");
627
+ const separator = err.loc.file.includes("/") ? "/" : "\\";
628
+ const cleanFile = err.loc.file.split(separator).slice(-2).join("/");
627
629
  const fileLocation = [cleanFile, err.loc.line, err.loc.column].filter(Boolean).join(":");
628
630
  const absoluteFileLocation = [err.loc.file, err.loc.line, err.loc.column].filter(Boolean).join(":");
629
631
  const codeFile = codeHeader.getElementsByTagName("h2")[0];
@@ -47,7 +47,7 @@ function serverStart({
47
47
  base,
48
48
  isRestart = false
49
49
  }) {
50
- const version = "2.1.7";
50
+ const version = "2.1.9";
51
51
  const localPrefix = `${dim("\u2503")} Local `;
52
52
  const networkPrefix = `${dim("\u2503")} Network `;
53
53
  const emptyPrefix = " ".repeat(11);
@@ -233,7 +233,7 @@ function printHelp({
233
233
  message.push(
234
234
  linebreak(),
235
235
  ` ${bgGreen(black(` ${commandName} `))} ${green(
236
- `v${"2.1.7"}`
236
+ `v${"2.1.9"}`
237
237
  )} ${headline}`
238
238
  );
239
239
  }
@@ -11,7 +11,10 @@ async function preview(_settings, { logging, flags }) {
11
11
  commandName: "astro preview",
12
12
  usage: "[...flags]",
13
13
  tables: {
14
- Flags: [["--help (-h)", "See all available flags."]]
14
+ Flags: [
15
+ ["--open", "Automatically open the app in the browser on server start"],
16
+ ["--help (-h)", "See all available flags."]
17
+ ]
15
18
  },
16
19
  description: `Starts a local server to serve your static dist/ directory. Check ${cyan(
17
20
  "https://docs.astro.build/en/reference/cli-reference/#astro-preview"
@@ -20,7 +20,8 @@ async function createStaticPreviewServer(settings, logging) {
20
20
  preview: {
21
21
  host: settings.config.server.host,
22
22
  port: settings.config.server.port,
23
- headers: settings.config.server.headers
23
+ headers: settings.config.server.headers,
24
+ open: settings.config.server.open
24
25
  },
25
26
  plugins: [vitePluginAstroPreview(settings)]
26
27
  });
@@ -17,6 +17,21 @@ async function getParamsAndProps(opts) {
17
17
  const paramsMatch = route.pattern.exec(pathname);
18
18
  if (paramsMatch) {
19
19
  params = getParams(route.params)(paramsMatch);
20
+ if (route.type === "endpoint" && mod.getStaticPaths) {
21
+ const lastSegment = route.segments[route.segments.length - 1];
22
+ const paramValues = Object.values(params);
23
+ const lastParam = paramValues[paramValues.length - 1];
24
+ if (lastSegment.length === 1 && lastSegment[0].dynamic && lastParam === void 0) {
25
+ throw new AstroError({
26
+ ...AstroErrorData.PrerenderDynamicEndpointPathCollide,
27
+ message: AstroErrorData.PrerenderDynamicEndpointPathCollide.message(route.route),
28
+ hint: AstroErrorData.PrerenderDynamicEndpointPathCollide.hint(route.component),
29
+ location: {
30
+ file: route.component
31
+ }
32
+ });
33
+ }
34
+ }
20
35
  }
21
36
  }
22
37
  let routeCacheEntry = routeCache.get(route);
@@ -26,6 +26,14 @@ function generatePaginateFunction(routeMatch) {
26
26
  ...additionalParams,
27
27
  [paramName]: includesFirstPageNumber || pageNum > 1 ? String(pageNum) : void 0
28
28
  };
29
+ const current = correctIndexRoute(routeMatch.generate({ ...params }));
30
+ const next = pageNum === lastPage ? void 0 : correctIndexRoute(routeMatch.generate({ ...params, page: String(pageNum + 1) }));
31
+ const prev = pageNum === 1 ? void 0 : correctIndexRoute(
32
+ routeMatch.generate({
33
+ ...params,
34
+ page: !includesFirstPageNumber && pageNum - 1 === 1 ? void 0 : String(pageNum - 1)
35
+ })
36
+ );
29
37
  return {
30
38
  params,
31
39
  props: {
@@ -38,14 +46,7 @@ function generatePaginateFunction(routeMatch) {
38
46
  total: data.length,
39
47
  currentPage: pageNum,
40
48
  lastPage,
41
- url: {
42
- current: routeMatch.generate({ ...params }),
43
- next: pageNum === lastPage ? void 0 : routeMatch.generate({ ...params, page: String(pageNum + 1) }),
44
- prev: pageNum === 1 ? void 0 : routeMatch.generate({
45
- ...params,
46
- page: !includesFirstPageNumber && pageNum - 1 === 1 ? "" : String(pageNum - 1)
47
- })
48
- }
49
+ url: { current, next, prev }
49
50
  }
50
51
  }
51
52
  };
@@ -53,6 +54,12 @@ function generatePaginateFunction(routeMatch) {
53
54
  return result;
54
55
  };
55
56
  }
57
+ function correctIndexRoute(route) {
58
+ if (route === "") {
59
+ return "/";
60
+ }
61
+ return route;
62
+ }
56
63
  export {
57
64
  generatePaginateFunction
58
65
  };
@@ -3,13 +3,14 @@ import {
3
3
  InvalidAstroDataError,
4
4
  safelyGetAstroData
5
5
  } from "@astrojs/markdown-remark/dist/internal.js";
6
- import fs from "fs";
7
6
  import matter from "gray-matter";
7
+ import fs from "node:fs";
8
+ import path from "node:path";
8
9
  import { fileURLToPath } from "node:url";
9
10
  import { normalizePath } from "vite";
10
11
  import { AstroError, AstroErrorData, MarkdownError } from "../core/errors/index.js";
11
12
  import { warn } from "../core/logger/core.js";
12
- import { isMarkdownFile } from "../core/util.js";
13
+ import { isMarkdownFile, rootRelativePath } from "../core/util.js";
13
14
  import { escapeViteEnvReferences, getFileInfo } from "../vite-plugin-utils/index.js";
14
15
  function safeMatter(source, id) {
15
16
  try {
@@ -41,6 +42,9 @@ const astroJsxRuntimeModulePath = normalizePath(
41
42
  const astroServerRuntimeModulePath = normalizePath(
42
43
  fileURLToPath(new URL("../runtime/server/index.js", import.meta.url))
43
44
  );
45
+ const astroErrorModulePath = normalizePath(
46
+ fileURLToPath(new URL("../core/errors/index.js", import.meta.url))
47
+ );
44
48
  function markdown({ settings, logging }) {
45
49
  return {
46
50
  enforce: "pre",
@@ -70,7 +74,7 @@ function markdown({ settings, logging }) {
70
74
  var _a;
71
75
  return {
72
76
  raw: imagePath,
73
- absolute: ((_a = await this.resolve(imagePath, id)) == null ? void 0 : _a.id) ?? imagePath
77
+ resolved: ((_a = await this.resolve(imagePath, id)) == null ? void 0 : _a.id) ?? path.join(path.dirname(id), imagePath)
74
78
  };
75
79
  })
76
80
  );
@@ -91,16 +95,32 @@ function markdown({ settings, logging }) {
91
95
  const code = escapeViteEnvReferences(`
92
96
  import { Fragment, jsx as h } from ${JSON.stringify(astroJsxRuntimeModulePath)};
93
97
  import { spreadAttributes } from ${JSON.stringify(astroServerRuntimeModulePath)};
98
+ import { AstroError, AstroErrorData } from ${JSON.stringify(astroErrorModulePath)};
94
99
 
95
100
  ${layout ? `import Layout from ${JSON.stringify(layout)};` : ""}
96
101
  ${settings.config.experimental.assets ? 'import { getImage } from "astro:assets";' : ""}
97
102
 
98
103
  export const images = {
99
104
  ${imagePaths.map(
100
- (entry) => `'${entry.raw}': await getImage({src: (await import("${entry.absolute}")).default})`
105
+ (entry) => `'${entry.raw}': await getImageSafely((await import("${entry.raw}")).default, "${entry.raw}", "${rootRelativePath(settings.config, entry.resolved)}")`
101
106
  )}
102
107
  }
103
108
 
109
+ async function getImageSafely(imageSrc, imagePath, resolvedImagePath) {
110
+ if (!imageSrc) {
111
+ throw new AstroError({
112
+ ...AstroErrorData.MarkdownImageNotFound,
113
+ message: AstroErrorData.MarkdownImageNotFound.message(
114
+ imagePath,
115
+ resolvedImagePath
116
+ ),
117
+ location: { file: "${id}" },
118
+ });
119
+ }
120
+
121
+ return await getImage({src: imageSrc})
122
+ }
123
+
104
124
  function updateImageReferences(html) {
105
125
  return html.replaceAll(
106
126
  /__ASTRO_IMAGE_="(.+)"/gm,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "2.1.7",
3
+ "version": "2.1.9",
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",
@@ -1,8 +0,0 @@
1
- import type { Plugin as VitePlugin } from 'vite';
2
- import type { AstroSettings } from '../@types/astro.js';
3
- import type { LogOptions } from '../core/logger/core.js';
4
- /** Connect Astro integrations into Vite, as needed. */
5
- export default function astroIntegrationsContainerPlugin({ settings, logging, }: {
6
- settings: AstroSettings;
7
- logging: LogOptions;
8
- }): VitePlugin;
@@ -1,15 +0,0 @@
1
- import { runHookServerSetup } from "../integrations/index.js";
2
- function astroIntegrationsContainerPlugin({
3
- settings,
4
- logging
5
- }) {
6
- return {
7
- name: "astro:integration-container",
8
- configureServer(server) {
9
- runHookServerSetup({ config: settings.config, server, logging });
10
- }
11
- };
12
- }
13
- export {
14
- astroIntegrationsContainerPlugin as default
15
- };