astro 2.1.8 → 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.
@@ -1086,11 +1086,13 @@ export type GetStaticPaths = (options: GetStaticPathsOptions) => Promise<GetStat
1086
1086
  *
1087
1087
  * @example
1088
1088
  * ```ts
1089
- * export async function getStaticPaths() {
1089
+ * import type { GetStaticPaths } from 'astro';
1090
+ *
1091
+ * export const getStaticPaths = (() => {
1090
1092
  * return results.map((entry) => ({
1091
1093
  * params: { slug: entry.slug },
1092
1094
  * }));
1093
- * }
1095
+ * }) satisfies GetStaticPaths;
1094
1096
  *
1095
1097
  * type Params = InferGetStaticParamsType<typeof getStaticPaths>;
1096
1098
  * // ^? { slug: string; }
@@ -1098,7 +1100,7 @@ export type GetStaticPaths = (options: GetStaticPathsOptions) => Promise<GetStat
1098
1100
  * const { slug } = Astro.params as Params;
1099
1101
  * ```
1100
1102
  */
1101
- 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 {
1102
1104
  params: infer P;
1103
1105
  } ? P : never : never : never;
1104
1106
  /**
@@ -1106,7 +1108,9 @@ export type InferGetStaticParamsType<T> = T extends () => Promise<infer R> ? R e
1106
1108
  *
1107
1109
  * @example
1108
1110
  * ```ts
1109
- * export async function getStaticPaths() {
1111
+ * import type { GetStaticPaths } from 'astro';
1112
+ *
1113
+ * export const getStaticPaths = (() => {
1110
1114
  * return results.map((entry) => ({
1111
1115
  * params: { slug: entry.slug },
1112
1116
  * props: {
@@ -1114,15 +1118,15 @@ export type InferGetStaticParamsType<T> = T extends () => Promise<infer R> ? R e
1114
1118
  * propB: 42
1115
1119
  * },
1116
1120
  * }));
1117
- * }
1121
+ * }) satisfies GetStaticPaths;
1118
1122
  *
1119
1123
  * type Props = InferGetStaticPropsType<typeof getStaticPaths>;
1120
1124
  * // ^? { propA: boolean; propB: number; }
1121
1125
  *
1122
- * const { propA, propB } = Astro.props as Props;
1126
+ * const { propA, propB } = Astro.props;
1123
1127
  * ```
1124
1128
  */
1125
- 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 {
1126
1130
  props: infer P;
1127
1131
  } ? P : never : never : never;
1128
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;
@@ -7,7 +7,8 @@ function isESMImportedImage(src) {
7
7
  return typeof src === "object";
8
8
  }
9
9
  async function getConfiguredImageService() {
10
- if (!globalThis.astroAsset.imageService) {
10
+ var _a;
11
+ if (!((_a = globalThis == null ? void 0 : globalThis.astroAsset) == null ? void 0 : _a.imageService)) {
11
12
  const { default: service } = await import(
12
13
  // @ts-expect-error
13
14
  "virtual:image-service"
@@ -16,12 +17,20 @@ async function getConfiguredImageService() {
16
17
  error.cause = e;
17
18
  throw error;
18
19
  });
20
+ if (!globalThis.astroAsset)
21
+ globalThis.astroAsset = {};
19
22
  globalThis.astroAsset.imageService = service;
20
23
  return service;
21
24
  }
22
25
  return globalThis.astroAsset.imageService;
23
26
  }
24
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
+ }
25
34
  const service = await getConfiguredImageService();
26
35
  const validatedOptions = service.validateOptions ? service.validateOptions(options) : options;
27
36
  let imageURL = service.getURL(validatedOptions);
@@ -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, imageService: string): string;
2
+ export declare function propsToFilename(transform: ImageTransform, hash: string): string;
3
+ export declare function hashTransform(transform: ImageTransform, imageService: string): string;
@@ -2,18 +2,22 @@ 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, imageService) {
5
+ function propsToFilename(transform, hash) {
6
6
  if (!isESMImportedImage(transform.src)) {
7
7
  return transform.src;
8
8
  }
9
9
  let filename = removeQueryString(transform.src.src);
10
10
  const ext = extname(filename);
11
11
  filename = basename(filename, ext);
12
+ const outputExt = transform.format ? `.${transform.format}` : ext;
13
+ return `/${filename}_${hash}${outputExt}`;
14
+ }
15
+ function hashTransform(transform, imageService) {
12
16
  const { alt, ...rest } = transform;
13
17
  const hashFields = { ...rest, imageService };
14
- const outputExt = transform.format ? `.${transform.format}` : ext;
15
- return `/${filename}_${shorthash(JSON.stringify(hashFields))}${outputExt}`;
18
+ return shorthash(JSON.stringify(hashFields));
16
19
  }
17
20
  export {
21
+ hashTransform,
18
22
  propsToFilename
19
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,20 +126,18 @@ 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.build.assets,
139
- propsToFilename(options, settings.config.image.service)
140
- )
138
+ joinPaths(settings.config.build.assets, propsToFilename(options, hash))
141
139
  );
142
- globalThis.astroAsset.staticImages.set(options, filePath);
140
+ globalThis.astroAsset.staticImages.set(hash, { path: filePath, options });
143
141
  }
144
142
  return prependForwardSlash(joinPaths(settings.config.base, filePath));
145
143
  };
@@ -148,8 +146,10 @@ function assets({
148
146
  if (mode != "build") {
149
147
  return;
150
148
  }
151
- const dir = settings.config.output === "server" ? settings.config.build.server : settings.config.outDir;
152
- 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
+ }
153
153
  },
154
154
  // In build, rewrite paths to ESM imported images in code to their final location
155
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
  }
@@ -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);
@@ -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
  }
@@ -1,4 +1,4 @@
1
- const ASTRO_VERSION = "2.1.8";
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 }),
@@ -53,7 +53,7 @@ async function dev(settings, options) {
53
53
  isRestart: options.isRestart
54
54
  })
55
55
  );
56
- const currentVersion = "2.1.8";
56
+ const currentVersion = "2.1.9";
57
57
  if (currentVersion.includes("-")) {
58
58
  warn(options.logging, null, msg.prerelease({ currentVersion }));
59
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
  };
@@ -482,6 +482,69 @@ export declare const AstroErrorData: {
482
482
  readonly message: (pathname: string) => string;
483
483
  readonly hint: (filename: string) => string;
484
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
+ };
485
548
  /**
486
549
  * @docs
487
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
  },
@@ -495,6 +495,69 @@ Expected \`true\` value but got \`${suffix}\`.`;
495
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
496
  hint: (filename) => `Rename \`${filename}\` to \`${filename.replace(/\.(js|ts)/, (m) => `.json` + m)}\``
497
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
+ },
498
561
  // No headings here, that way Vite errors are merged with Astro ones in the docs, which makes more sense to users.
499
562
  // Vite Errors - 4xxx
500
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.8";
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.8"}`
236
+ `v${"2.1.9"}`
237
237
  )} ${headline}`
238
238
  );
239
239
  }
@@ -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.8",
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
- };