astro 2.1.0 → 2.1.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.
@@ -658,6 +658,27 @@ export interface AstroUserConfig {
658
658
  * @name Image options
659
659
  */
660
660
  image?: {
661
+ /**
662
+ * @docs
663
+ * @name image.service (Experimental)
664
+ * @type {'astro/assets/services/sharp' | 'astro/assets/services/squoosh' | string}
665
+ * @default `'astro/assets/services/squoosh'`
666
+ * @version 2.1.0
667
+ * @description
668
+ * Set which image service is used for Astro’s experimental assets support.
669
+ *
670
+ * The value should be a module specifier for the image service to use:
671
+ * either one of Astro’s two built-in services, or a third-party implementation.
672
+ *
673
+ * ```js
674
+ * {
675
+ * image: {
676
+ * // Example: Enable the Sharp-based image service
677
+ * service: 'astro/assets/services/sharp',
678
+ * },
679
+ * }
680
+ * ```
681
+ */
661
682
  service: 'astro/assets/services/sharp' | 'astro/assets/services/squoosh' | (string & {});
662
683
  };
663
684
  /**
@@ -867,6 +888,7 @@ export interface AstroUserConfig {
867
888
  */
868
889
  legacy?: object;
869
890
  /**
891
+ * @docs
870
892
  * @kind heading
871
893
  * @name Experimental Flags
872
894
  * @description
@@ -1,5 +1,6 @@
1
1
  import { AstroError, AstroErrorData } from "../../core/errors/index.js";
2
2
  import { isRemotePath } from "../../core/path.js";
3
+ import { VALID_INPUT_FORMATS } from "../consts.js";
3
4
  import { isESMImportedImage } from "../internal.js";
4
5
  function isLocalService(service) {
5
6
  if (!service) {
@@ -51,13 +52,23 @@ const baseService = {
51
52
  if (missingDimension) {
52
53
  throw new AstroError({
53
54
  ...AstroErrorData.MissingImageDimension,
54
- message: AstroErrorData.MissingImageDimension.message(missingDimension)
55
+ message: AstroErrorData.MissingImageDimension.message(missingDimension, options.src)
55
56
  });
56
57
  }
57
58
  }
58
59
  if (!isESMImportedImage(options.src) && isRemotePath(options.src)) {
59
60
  return options.src;
60
61
  }
62
+ if (isESMImportedImage(options.src) && !VALID_INPUT_FORMATS.includes(options.src.format)) {
63
+ throw new AstroError({
64
+ ...AstroErrorData.UnsupportedImageFormat,
65
+ message: AstroErrorData.UnsupportedImageFormat.message(
66
+ options.src.format,
67
+ options.src.src,
68
+ VALID_INPUT_FORMATS
69
+ )
70
+ });
71
+ }
61
72
  const searchParams = new URLSearchParams();
62
73
  searchParams.append("href", isESMImportedImage(options.src) ? options.src.src : options.src);
63
74
  options.width && searchParams.append("w", options.width.toString());
@@ -7,6 +7,7 @@ import { fileURLToPath, pathToFileURL } from "node:url";
7
7
  import { normalizePath } from "vite";
8
8
  import { error } from "../core/logger/core.js";
9
9
  import { joinPaths, prependForwardSlash } from "../core/path.js";
10
+ import { rootRelativePath } from "../core/util.js";
10
11
  import { VIRTUAL_MODULE_ID, VIRTUAL_SERVICE_ID } from "./consts.js";
11
12
  import { isESMImportedImage } from "./internal.js";
12
13
  import { isLocalService } from "./services/service.js";
@@ -65,8 +66,8 @@ function assets({
65
66
  if (!filePath) {
66
67
  return next();
67
68
  }
68
- const filePathURL = new URL(filePath, "file:");
69
- const file = await fs.readFile(filePathURL.pathname);
69
+ const filePathURL = new URL("." + filePath, settings.config.root);
70
+ const file = await fs.readFile(filePathURL);
70
71
  let meta = getOrigQueryParams(filePathURL.searchParams);
71
72
  if (!meta) {
72
73
  meta = await imageMetadata(filePathURL, file);
@@ -85,7 +86,10 @@ function assets({
85
86
  data = result.data;
86
87
  format = result.format;
87
88
  }
88
- res.setHeader("Content-Type", mime.getType(fileURLToPath(url)) || `image/${format}`);
89
+ res.setHeader(
90
+ "Content-Type",
91
+ mime.getType(fileURLToPath(filePathURL)) || `image/${format}`
92
+ );
89
93
  res.setHeader("Cache-Control", "max-age=360000");
90
94
  const stream = Readable.from(data);
91
95
  return stream.pipe(res);
@@ -155,7 +159,7 @@ function assets({
155
159
  resolvedConfig = viteConfig;
156
160
  },
157
161
  async load(id) {
158
- if (/\.(heic|heif|avif|jpeg|jpg|png|tiff|webp|gif)$/.test(id)) {
162
+ if (/\.(jpeg|jpg|png|tiff|webp|gif|svg)$/.test(id)) {
159
163
  const url = pathToFileURL(id);
160
164
  const meta = await imageMetadata(url);
161
165
  if (!meta) {
@@ -174,7 +178,7 @@ function assets({
174
178
  url.searchParams.append("origWidth", meta.width.toString());
175
179
  url.searchParams.append("origHeight", meta.height.toString());
176
180
  url.searchParams.append("origFormat", meta.format);
177
- meta.src = url.toString();
181
+ meta.src = rootRelativePath(settings.config, url);
178
182
  }
179
183
  return `export default ${JSON.stringify(meta)}`;
180
184
  }
@@ -1,4 +1,4 @@
1
- const ASTRO_VERSION = "2.1.0";
1
+ const ASTRO_VERSION = "2.1.2";
2
2
  const SUPPORTED_MARKDOWN_FILE_EXTENSIONS = [
3
3
  ".markdown",
4
4
  ".mdown",
@@ -51,7 +51,7 @@ async function dev(settings, options) {
51
51
  isRestart: options.isRestart
52
52
  })
53
53
  );
54
- const currentVersion = "2.1.0";
54
+ const currentVersion = "2.1.2";
55
55
  if (currentVersion.includes("-")) {
56
56
  warn(options.logging, null, msg.prerelease({ currentVersion }));
57
57
  }
@@ -397,21 +397,74 @@ export declare const AstroErrorData: {
397
397
  readonly message: (paramName: string) => string;
398
398
  readonly hint: "Rename your file to `[page].astro` or `[...page].astro`.";
399
399
  };
400
+ /**
401
+ * @docs
402
+ * @see
403
+ * - [Assets (Experimental)](https://docs.astro.build/en/guides/assets/)
404
+ * - [Image component](https://docs.astro.build/en/guides/assets/#image--astroassets)
405
+ * - [Image component#alt](https://docs.astro.build/en/guides/assets/#alt-required)
406
+ * @description
407
+ * The `alt` property allows you to provide descriptive alt text to users of screen readers and other assistive technologies. In order to ensure your images are accessible, the `Image` component requires that an `alt` be specified.
408
+ *
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
+ */
400
411
  readonly ImageMissingAlt: {
401
412
  readonly title: "Missing alt property";
402
413
  readonly code: 3022;
403
414
  readonly message: "The alt property is required.";
404
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.";
405
416
  };
417
+ /**
418
+ * @docs
419
+ * @see
420
+ * - [Image Service API](https://docs.astro.build/en/reference/image-service-reference/)
421
+ * @description
422
+ * There was an error while loading the configured image service. This can be caused by various factors, such as your image service not properly exporting a compatible object in its default export, or an incorrect path.
423
+ *
424
+ * If you believe that your service is properly configured and this error is wrong, please [open an issue](https://astro.build/issues/).
425
+ */
406
426
  readonly InvalidImageService: {
407
427
  readonly title: "Error while loading image service";
408
428
  readonly code: 3023;
409
- readonly message: "There was an error loading the configured image service. Please see the stack trace for more information";
429
+ readonly message: "There was an error loading the configured image service. Please see the stack trace for more information.";
410
430
  };
431
+ /**
432
+ * @docs
433
+ * @message
434
+ * Missing width and height attributes for `IMAGE_URL`. When using remote images, both dimensions are always required in order to avoid cumulative layout shift (CLS).
435
+ * @see
436
+ * - [Assets (Experimental)](https://docs.astro.build/en/guides/assets/)
437
+ * - [Image component#width-and-height](https://docs.astro.build/en/guides/assets/#width-and-height)
438
+ * @description
439
+ * For remote images, `width` and `height` cannot be inferred from the original file. As such, in order to avoid CLS, those two properties are always required.
440
+ *
441
+ * If your image is inside your `src` folder, you probably meant to import it instead. See [the Imports guide for more information](https://docs.astro.build/en/guides/imports/#other-assets).
442
+ */
411
443
  readonly MissingImageDimension: {
412
444
  readonly title: "Missing image dimensions";
413
445
  readonly code: 3024;
414
- readonly message: (missingDimension: 'width' | 'height' | 'both') => string;
446
+ readonly message: (missingDimension: 'width' | 'height' | 'both', imageURL: string) => string;
447
+ readonly hint: "If your image is inside your `src` folder, you probably meant to import it instead. See [the Imports guide for more information](https://docs.astro.build/en/guides/imports/#other-assets).";
448
+ };
449
+ /**
450
+ * @docs
451
+ * @description
452
+ * The built-in image services do not currently support optimizing all image formats.
453
+ *
454
+ * For unsupported formats such as SVGs and GIFs, you may be able to use an `img` tag directly:
455
+ * ```astro
456
+ * ---
457
+ * import rocket from '../assets/images/rocket.svg'
458
+ * ---
459
+ *
460
+ * <img src={rocket.src} width={rocket.width} height={rocket.height} alt="A rocketship in space." />
461
+ * ```
462
+ */
463
+ readonly UnsupportedImageFormat: {
464
+ readonly title: "Unsupported image format";
465
+ readonly code: 3025;
466
+ readonly message: (format: string, imagePath: string, supportedFormats: readonly string[]) => string;
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.";
415
468
  };
416
469
  /**
417
470
  * @docs
@@ -154,12 +154,21 @@ Expected \`true\` value but got \`${suffix}\`.`;
154
154
  InvalidImageService: {
155
155
  title: "Error while loading image service",
156
156
  code: 3023,
157
- message: "There was an error loading the configured image service. Please see the stack trace for more information"
157
+ message: "There was an error loading the configured image service. Please see the stack trace for more information."
158
158
  },
159
159
  MissingImageDimension: {
160
160
  title: "Missing image dimensions",
161
161
  code: 3024,
162
- message: (missingDimension) => `For remote images, ${missingDimension === "both" ? "width and height are" : `${missingDimension} is`} required.`
162
+ message: (missingDimension, imageURL) => `Missing ${missingDimension === "both" ? "width and height attributes" : `${missingDimension} attribute`} for ${imageURL}. When using remote images, both dimensions are always required in order to avoid CLS.`,
163
+ hint: "If your image is inside your `src` folder, you probably meant to import it instead. See [the Imports guide for more information](https://docs.astro.build/en/guides/imports/#other-assets)."
164
+ },
165
+ UnsupportedImageFormat: {
166
+ title: "Unsupported image format",
167
+ code: 3025,
168
+ message: (format, imagePath, supportedFormats) => `Received unsupported format \`${format}\` from \`${imagePath}\`. Currently only ${supportedFormats.join(
169
+ ", "
170
+ )} are supported for optimization.`,
171
+ hint: "If you do not need optimization, using an `img` tag directly instead of the `Image` component might be what you're looking for."
163
172
  },
164
173
  UnknownViteError: {
165
174
  title: "Unknown Vite Error.",
@@ -47,7 +47,7 @@ function serverStart({
47
47
  base,
48
48
  isRestart = false
49
49
  }) {
50
- const version = "2.1.0";
50
+ const version = "2.1.2";
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.0"}`
236
+ `v${"2.1.2"}`
237
237
  )} ${headline}`
238
238
  );
239
239
  }
@@ -37,6 +37,7 @@ export declare function isPage(file: URL, settings: AstroSettings): boolean;
37
37
  export declare function isEndpoint(file: URL, settings: AstroSettings): boolean;
38
38
  export declare function isModeServerWithNoAdapter(settings: AstroSettings): boolean;
39
39
  export declare function relativeToSrcDir(config: AstroConfig, idOrUrl: URL | string): string;
40
+ export declare function rootRelativePath(config: AstroConfig, idOrUrl: URL | string): string;
40
41
  export declare function emoji(char: string, fallback: string): string;
41
42
  /**
42
43
  * Simulate Vite's resolve and import analysis so we can import the id as an URL
package/dist/core/util.js CHANGED
@@ -114,6 +114,15 @@ function relativeToSrcDir(config, idOrUrl) {
114
114
  }
115
115
  return id.slice(slash(fileURLToPath(config.srcDir)).length);
116
116
  }
117
+ function rootRelativePath(config, idOrUrl) {
118
+ let id;
119
+ if (typeof idOrUrl !== "string") {
120
+ id = unwrapId(viteID(idOrUrl));
121
+ } else {
122
+ id = idOrUrl;
123
+ }
124
+ return prependForwardSlash(id.slice(normalizePath(fileURLToPath(config.root)).length));
125
+ }
117
126
  function emoji(char, fallback) {
118
127
  return process.platform !== "win32" ? char : fallback;
119
128
  }
@@ -170,6 +179,7 @@ export {
170
179
  resolveJsToTs,
171
180
  resolvePages,
172
181
  resolvePath,
182
+ rootRelativePath,
173
183
  unwrapId,
174
184
  viteID
175
185
  };
@@ -194,10 +194,17 @@ Did you forget to import the component or is it possible there is a typo?`);
194
194
  async function renderElement(result, tag, { children, ...props }) {
195
195
  return markHTMLString(
196
196
  `<${tag}${spreadAttributes(props)}${markHTMLString(
197
- (children == null || children == "") && voidElementNames.test(tag) ? `/>` : `>${children == null ? "" : await renderJSX(result, children)}</${tag}>`
197
+ (children == null || children == "") && voidElementNames.test(tag) ? `/>` : `>${children == null ? "" : await renderJSX(result, prerenderElementChildren(tag, children))}</${tag}>`
198
198
  )}`
199
199
  );
200
200
  }
201
+ function prerenderElementChildren(tag, children) {
202
+ if (typeof children === "string" && (tag === "style" || tag === "script")) {
203
+ return markHTMLString(children);
204
+ } else {
205
+ return children;
206
+ }
207
+ }
201
208
  function useConsoleFilter() {
202
209
  consoleFilterRefs++;
203
210
  if (!originalConsoleError) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "2.1.0",
3
+ "version": "2.1.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",