astro 2.1.9 → 2.2.0

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 (59) hide show
  1. package/config.d.ts +2 -1
  2. package/dist/@types/astro.d.ts +25 -2
  3. package/dist/assets/image-endpoint.js +2 -2
  4. package/dist/assets/index.d.ts +1 -1
  5. package/dist/assets/index.js +3 -2
  6. package/dist/assets/internal.d.ts +2 -8
  7. package/dist/assets/services/service.d.ts +2 -2
  8. package/dist/assets/services/vendor/squoosh/image-pool.d.ts +2 -2
  9. package/dist/assets/services/vendor/squoosh/image.d.ts +2 -2
  10. package/dist/assets/types.d.ts +15 -9
  11. package/dist/assets/utils/emitAsset.d.ts +2 -1
  12. package/dist/assets/utils/emitAsset.js +4 -1
  13. package/dist/assets/vite-plugin-assets.js +11 -9
  14. package/dist/content/runtime-assets.d.ts +3 -7
  15. package/dist/content/runtime-assets.js +13 -20
  16. package/dist/content/template/virtual-mod.d.mts +1 -0
  17. package/dist/content/utils.d.ts +2 -6
  18. package/dist/content/utils.js +16 -56
  19. package/dist/content/vite-plugin-content-assets.js +13 -4
  20. package/dist/content/vite-plugin-content-imports.js +3 -4
  21. package/dist/content/vite-plugin-content-virtual-mod.js +1 -4
  22. package/dist/core/app/index.js +2 -0
  23. package/dist/core/build/add-rollup-input.d.ts +2 -2
  24. package/dist/core/build/generate.js +22 -6
  25. package/dist/core/build/internal.d.ts +3 -3
  26. package/dist/core/build/plugins/plugin-css.js +0 -29
  27. package/dist/core/build/plugins/plugin-internals.js +0 -7
  28. package/dist/core/build/plugins/plugin-ssr.js +12 -7
  29. package/dist/core/build/static-build.js +7 -3
  30. package/dist/core/config/schema.d.ts +12 -0
  31. package/dist/core/config/schema.js +2 -0
  32. package/dist/core/constants.js +1 -1
  33. package/dist/core/cookies/cookies.js +7 -0
  34. package/dist/core/create-vite.js +11 -0
  35. package/dist/core/dev/container.js +0 -3
  36. package/dist/core/dev/dev.js +1 -1
  37. package/dist/core/errors/errors-data.d.ts +11 -1
  38. package/dist/core/errors/errors-data.js +11 -1
  39. package/dist/core/messages.js +2 -2
  40. package/dist/core/render/dev/index.js +1 -1
  41. package/dist/core/render/dev/scripts.d.ts +1 -1
  42. package/dist/core/render/dev/scripts.js +7 -6
  43. package/dist/core/render/result.js +6 -0
  44. package/dist/core/render/ssr-element.d.ts +7 -6
  45. package/dist/core/render/ssr-element.js +28 -20
  46. package/dist/core/sync/index.js +9 -2
  47. package/dist/core/util.d.ts +1 -1
  48. package/dist/core/util.js +2 -2
  49. package/dist/runtime/server/render/common.d.ts +1 -1
  50. package/dist/vite-plugin-astro-server/response.d.ts +1 -1
  51. package/dist/vite-plugin-astro-server/response.js +2 -1
  52. package/dist/vite-plugin-astro-server/route.js +1 -1
  53. package/dist/vite-plugin-env/index.js +9 -0
  54. package/dist/vite-plugin-markdown/index.js +9 -12
  55. package/package.json +3 -3
  56. package/src/content/template/types.d.ts +23 -3
  57. package/src/content/template/virtual-mod.mjs +7 -0
  58. package/dist/content/template/virtual-mod-assets.d.mts +0 -1
  59. package/src/content/template/virtual-mod-assets.mjs +0 -7
package/config.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  type ViteUserConfig = import('vite').UserConfig;
2
+ type ViteUserConfigFn = import('vite').UserConfigFn;
2
3
  type AstroUserConfig = import('./dist/@types/astro.js').AstroUserConfig;
3
4
 
4
5
  /**
@@ -10,4 +11,4 @@ export function defineConfig(config: AstroUserConfig): AstroUserConfig;
10
11
  /**
11
12
  * Use Astro to generate a fully resolved Vite config
12
13
  */
13
- export function getViteConfig(config: ViteUserConfig): ViteUserConfig;
14
+ export function getViteConfig(config: ViteUserConfig): ViteUserConfigFn;
@@ -18,8 +18,8 @@ import type { LogOptions } from '../core/logger/core';
18
18
  import type { AstroComponentFactory, AstroComponentInstance } from '../runtime/server';
19
19
  import type { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from './../core/constants.js';
20
20
  export type { MarkdownHeading, MarkdownMetadata, MarkdownRenderingResult, RehypePlugins, RemarkPlugins, ShikiConfig, } from '@astrojs/markdown-remark';
21
- export type { ExternalImageService, LocalImageService } from '../assets/services/service';
22
- export type { ImageMetadata, ImageTransform } from '../assets/types';
21
+ export type { ExternalImageService, ImageService, LocalImageService, } from '../assets/services/service';
22
+ export type { GetImageResult, ImageInputFormat, ImageMetadata, ImageOutputFormat, ImageQuality, ImageQualityPreset, ImageTransform, } from '../assets/types';
23
23
  export type { SSRManifest } from '../core/app/types';
24
24
  export type { AstroCookies } from '../core/cookies';
25
25
  export interface AstroBuiltinProps {
@@ -568,6 +568,29 @@ export interface AstroUserConfig {
568
568
  * ```
569
569
  */
570
570
  assets?: string;
571
+ /**
572
+ * @docs
573
+ * @name build.assetsPrefix
574
+ * @type {string}
575
+ * @default `undefined`
576
+ * @version 2.2.0
577
+ * @description
578
+ * Specifies the prefix for Astro-generated asset links. This can be used if assets are served from a different domain than the current site.
579
+ *
580
+ * For example, if this is set to `https://cdn.example.com`, assets will be fetched from `https://cdn.example.com/_astro/...` (regardless of the `base` option).
581
+ * You would need to upload the files in `./dist/_astro/` to `https://cdn.example.com/_astro/` to serve the assets.
582
+ * The process varies depending on how the third-party domain is hosted.
583
+ * To rename the `_astro` path, specify a new directory in `build.assets`.
584
+ *
585
+ * ```js
586
+ * {
587
+ * build: {
588
+ * assetsPrefix: 'https://cdn.example.com'
589
+ * }
590
+ * }
591
+ * ```
592
+ */
593
+ assetsPrefix?: string;
571
594
  /**
572
595
  * @docs
573
596
  * @name build.serverEntry
@@ -1,4 +1,4 @@
1
- import mime from "mime";
1
+ import mime from "mime/lite.js";
2
2
  import { isRemotePath } from "../core/path.js";
3
3
  import { getConfiguredImageService } from "./internal.js";
4
4
  import { isLocalService } from "./services/service.js";
@@ -35,7 +35,7 @@ const get = async ({ request }) => {
35
35
  return new Response(data, {
36
36
  status: 200,
37
37
  headers: {
38
- "Content-Type": mime.getType(format) || "",
38
+ "Content-Type": mime.getType(format) ?? `image/${format}`,
39
39
  "Cache-Control": "public, max-age=31536000",
40
40
  ETag: etag(data.toString()),
41
41
  Date: (/* @__PURE__ */ new Date()).toUTCString()
@@ -1,5 +1,5 @@
1
1
  export { getConfiguredImageService, getImage } from './internal.js';
2
- export { baseService } from './services/service.js';
2
+ export { baseService, isLocalService } from './services/service.js';
3
3
  export { type LocalImageProps, type RemoteImageProps } from './types.js';
4
4
  export { emitESMImage } from './utils/emitAsset.js';
5
5
  export { imageMetadata } from './utils/metadata.js';
@@ -1,5 +1,5 @@
1
1
  import { getConfiguredImageService, getImage } from "./internal.js";
2
- import { baseService } from "./services/service.js";
2
+ import { baseService, isLocalService } from "./services/service.js";
3
3
  import {} from "./types.js";
4
4
  import { emitESMImage } from "./utils/emitAsset.js";
5
5
  import { imageMetadata } from "./utils/metadata.js";
@@ -8,5 +8,6 @@ export {
8
8
  emitESMImage,
9
9
  getConfiguredImageService,
10
10
  getImage,
11
- imageMetadata
11
+ imageMetadata,
12
+ isLocalService
12
13
  };
@@ -1,14 +1,8 @@
1
1
  import type { StaticBuildOptions } from '../core/build/types.js';
2
2
  import { type ImageService } from './services/service.js';
3
- import type { ImageMetadata, ImageTransform } from './types.js';
3
+ import type { GetImageResult, ImageMetadata, ImageTransform } from './types.js';
4
4
  export declare function isESMImportedImage(src: ImageMetadata | string): src is ImageMetadata;
5
5
  export declare function getConfiguredImageService(): Promise<ImageService>;
6
- interface GetImageResult {
7
- rawOptions: ImageTransform;
8
- options: ImageTransform;
9
- src: string;
10
- attributes: Record<string, any>;
11
- }
12
6
  /**
13
7
  * Get an optimized image and the necessary attributes to render it.
14
8
  *
@@ -18,7 +12,7 @@ interface GetImageResult {
18
12
  * import { getImage } from 'astro:assets';
19
13
  * import originalImage from '../assets/image.png';
20
14
  *
21
- * const optimizedImage = await getImage({src: originalImage, width: 1280 })
15
+ * const optimizedImage = await getImage({src: originalImage, width: 1280 });
22
16
  * ---
23
17
  * <img src={optimizedImage.src} {...optimizedImage.attributes} />
24
18
  * ```
@@ -1,5 +1,5 @@
1
1
  /// <reference types="node" />
2
- import type { ImageTransform, OutputFormat } from '../types.js';
2
+ import type { ImageOutputFormat, ImageTransform } from '../types.js';
3
3
  export type ImageService = LocalImageService | ExternalImageService;
4
4
  export declare function isLocalService(service: ImageService | undefined): service is LocalImageService;
5
5
  export declare function parseQuality(quality: string): string | number;
@@ -48,7 +48,7 @@ export interface LocalImageService extends SharedServiceProps {
48
48
  */
49
49
  transform: (inputBuffer: Buffer, transform: LocalImageTransform) => Promise<{
50
50
  data: Buffer;
51
- format: OutputFormat;
51
+ format: ImageOutputFormat;
52
52
  }>;
53
53
  }
54
54
  export type BaseServiceTransform = {
@@ -1,4 +1,4 @@
1
1
  /// <reference types="node" />
2
- import type { OutputFormat } from '../../../types.js';
2
+ import type { ImageOutputFormat } from '../../../types.js';
3
3
  import type { Operation } from './image.js';
4
- export declare function processBuffer(buffer: Buffer, operations: Operation[], encoding: OutputFormat, quality?: number): Promise<Uint8Array>;
4
+ export declare function processBuffer(buffer: Buffer, operations: Operation[], encoding: ImageOutputFormat, quality?: number): Promise<Uint8Array>;
@@ -1,5 +1,5 @@
1
1
  /// <reference types="node" />
2
- import type { OutputFormat } from '../../../types.js';
2
+ import type { ImageOutputFormat } from '../../../types.js';
3
3
  type RotateOperation = {
4
4
  type: 'rotate';
5
5
  numRotations: number;
@@ -10,5 +10,5 @@ type ResizeOperation = {
10
10
  height?: number;
11
11
  };
12
12
  export type Operation = RotateOperation | ResizeOperation;
13
- export declare function processBuffer(buffer: Buffer, operations: Operation[], encoding: OutputFormat, quality?: number): Promise<Uint8Array>;
13
+ export declare function processBuffer(buffer: Buffer, operations: Operation[], encoding: ImageOutputFormat, quality?: number): Promise<Uint8Array>;
14
14
  export {};
@@ -2,8 +2,8 @@ import type { VALID_INPUT_FORMATS, VALID_OUTPUT_FORMATS } from './consts.js';
2
2
  import type { ImageService } from './services/service.js';
3
3
  export type ImageQualityPreset = 'low' | 'mid' | 'high' | 'max' | (string & {});
4
4
  export type ImageQuality = ImageQualityPreset | number;
5
- export type InputFormat = (typeof VALID_INPUT_FORMATS)[number] | 'svg';
6
- export type OutputFormat = (typeof VALID_OUTPUT_FORMATS)[number] | (string & {});
5
+ export type ImageInputFormat = (typeof VALID_INPUT_FORMATS)[number] | 'svg';
6
+ export type ImageOutputFormat = (typeof VALID_OUTPUT_FORMATS)[number] | (string & {});
7
7
  declare global {
8
8
  var astroAsset: {
9
9
  imageService?: ImageService;
@@ -21,19 +21,25 @@ export interface ImageMetadata {
21
21
  src: string;
22
22
  width: number;
23
23
  height: number;
24
- format: InputFormat;
24
+ format: ImageInputFormat;
25
25
  }
26
26
  /**
27
27
  * Options accepted by the image transformation service.
28
28
  */
29
29
  export type ImageTransform = {
30
30
  src: ImageMetadata | string;
31
- width?: number;
32
- height?: number;
33
- quality?: ImageQuality;
34
- format?: OutputFormat;
31
+ width?: number | undefined;
32
+ height?: number | undefined;
33
+ quality?: ImageQuality | undefined;
34
+ format?: ImageOutputFormat | undefined;
35
35
  [key: string]: any;
36
36
  };
37
+ export interface GetImageResult {
38
+ rawOptions: ImageTransform;
39
+ options: ImageTransform;
40
+ src: string;
41
+ attributes: Record<string, any>;
42
+ }
37
43
  type WithRequired<T, K extends keyof T> = T & {
38
44
  [P in K]-?: T[P];
39
45
  };
@@ -91,11 +97,11 @@ export type LocalImageProps<T> = ImageSharedProps<T> & {
91
97
  * <Image src={...} format="avif" alt="..." />
92
98
  * ```
93
99
  */
94
- format?: OutputFormat;
100
+ format?: ImageOutputFormat;
95
101
  /**
96
102
  * Desired quality for the image. Value can either be a preset such as `low` or `high`, or a numeric value from 0 to 100.
97
103
  *
98
- * The perceptual quality of the output image is loader-specific.
104
+ * The perceptual quality of the output image is service-specific.
99
105
  * For instance, a certain service might decide that `high` results in a very beautiful image, but another could choose for it to be at best passable.
100
106
  *
101
107
  * **Example**:
@@ -1,3 +1,4 @@
1
1
  import type { AstroSettings } from '../../@types/astro';
2
- export declare function emitESMImage(id: string, watchMode: boolean, fileEmitter: any, settings: Pick<AstroSettings, 'config'>): Promise<import("./metadata.js").Metadata | undefined>;
2
+ import { type Metadata } from './metadata.js';
3
+ export declare function emitESMImage(id: string | undefined, watchMode: boolean, fileEmitter: any, settings: Pick<AstroSettings, 'config'>): Promise<Metadata | undefined>;
3
4
  export declare function emoji(char: string, fallback: string): string;
@@ -4,10 +4,13 @@ import { fileURLToPath, pathToFileURL } from "node:url";
4
4
  import slash from "slash";
5
5
  import { imageMetadata } from "./metadata.js";
6
6
  async function emitESMImage(id, watchMode, fileEmitter, settings) {
7
+ if (!id) {
8
+ return void 0;
9
+ }
7
10
  const url = pathToFileURL(id);
8
11
  const meta = await imageMetadata(url);
9
12
  if (!meta) {
10
- return;
13
+ return void 0;
11
14
  }
12
15
  if (!watchMode) {
13
16
  const pathname = decodeURI(url.pathname);
@@ -1,12 +1,12 @@
1
1
  import { bold } from "kleur/colors";
2
2
  import MagicString from "magic-string";
3
- import mime from "mime";
3
+ import mime from "mime/lite.js";
4
4
  import fs from "node:fs/promises";
5
5
  import { Readable } from "node:stream";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { normalizePath } from "vite";
8
8
  import { error } from "../core/logger/core.js";
9
- import { joinPaths, prependForwardSlash } from "../core/path.js";
9
+ import { appendForwardSlash, joinPaths, prependForwardSlash } from "../core/path.js";
10
10
  import { VIRTUAL_MODULE_ID, VIRTUAL_SERVICE_ID } from "./consts.js";
11
11
  import { isESMImportedImage } from "./internal.js";
12
12
  import { isLocalService } from "./services/service.js";
@@ -69,7 +69,7 @@ function assets({
69
69
  load(id) {
70
70
  if (id === resolvedVirtualModuleId) {
71
71
  return `
72
- export { getImage, getConfiguredImageService } from "astro/assets";
72
+ export { getImage, getConfiguredImageService, isLocalService } from "astro/assets";
73
73
  export { default as Image } from "astro/components/Image.astro";
74
74
  `;
75
75
  }
@@ -107,10 +107,7 @@ function assets({
107
107
  data = result.data;
108
108
  format = result.format;
109
109
  }
110
- res.setHeader(
111
- "Content-Type",
112
- mime.getType(fileURLToPath(filePathURL)) || `image/${format}`
113
- );
110
+ res.setHeader("Content-Type", mime.getType(format) ?? `image/${format}`);
114
111
  res.setHeader("Cache-Control", "max-age=360000");
115
112
  const stream = Readable.from(data);
116
113
  return stream.pipe(res);
@@ -139,7 +136,11 @@ function assets({
139
136
  );
140
137
  globalThis.astroAsset.staticImages.set(hash, { path: filePath, options });
141
138
  }
142
- return prependForwardSlash(joinPaths(settings.config.base, filePath));
139
+ if (settings.config.build.assetsPrefix) {
140
+ return joinPaths(settings.config.build.assetsPrefix, filePath);
141
+ } else {
142
+ return prependForwardSlash(joinPaths(settings.config.base, filePath));
143
+ }
143
144
  };
144
145
  },
145
146
  async buildEnd() {
@@ -160,7 +161,8 @@ function assets({
160
161
  s = s || (s = new MagicString(code));
161
162
  const [full, hash, postfix = ""] = match;
162
163
  const file = this.getFileName(hash);
163
- const outputFilepath = normalizePath(resolvedConfig.base + file + postfix);
164
+ const prefix = settings.config.build.assetsPrefix ? appendForwardSlash(settings.config.build.assetsPrefix) : resolvedConfig.base;
165
+ const outputFilepath = prefix + normalizePath(file + postfix);
164
166
  s.overwrite(match.index, match.index + full.length, outputFilepath);
165
167
  }
166
168
  if (s) {
@@ -1,8 +1,4 @@
1
+ import type { PluginContext } from 'rollup';
1
2
  import { z } from 'zod';
2
- import { type Metadata } from '../assets/utils/metadata.js';
3
- export declare function createImage(options: {
4
- assetsDir: string;
5
- relAssetsDir: string;
6
- }): () => z.ZodEffects<z.ZodString, Metadata & {
7
- __astro_asset: true;
8
- }, string>;
3
+ import type { AstroSettings } from '../@types/astro.js';
4
+ export declare function createImage(settings: AstroSettings, pluginContext: PluginContext, entryFilePath: string): () => z.ZodEffects<z.ZodString, import("../assets/utils/metadata.js").Metadata, string>;
@@ -1,16 +1,17 @@
1
- import { pathToFileURL } from "url";
2
1
  import { z } from "zod";
3
- import {
4
- imageMetadata as internalGetImageMetadata
5
- } from "../assets/utils/metadata.js";
6
- function createImage(options) {
2
+ import { emitESMImage } from "../assets/index.js";
3
+ function createImage(settings, pluginContext, entryFilePath) {
7
4
  return () => {
8
- if (options.assetsDir === "undefined") {
9
- throw new Error("Enable `experimental.assets` in your Astro config to use image()");
10
- }
11
- return z.string({ description: "__image" }).transform(async (imagePath, ctx) => {
12
- const imageMetadata = await getImageMetadata(pathToFileURL(imagePath));
13
- if (!imageMetadata) {
5
+ return z.string().transform(async (imagePath, ctx) => {
6
+ var _a;
7
+ const resolvedFilePath = (_a = await pluginContext.resolve(imagePath, entryFilePath)) == null ? void 0 : _a.id;
8
+ const metadata = await emitESMImage(
9
+ resolvedFilePath,
10
+ pluginContext.meta.watchMode,
11
+ pluginContext.emitFile,
12
+ settings
13
+ );
14
+ if (!metadata) {
14
15
  ctx.addIssue({
15
16
  code: "custom",
16
17
  message: `Image ${imagePath} does not exist. Is the path correct?`,
@@ -18,18 +19,10 @@ function createImage(options) {
18
19
  });
19
20
  return z.NEVER;
20
21
  }
21
- return imageMetadata;
22
+ return metadata;
22
23
  });
23
24
  };
24
25
  }
25
- async function getImageMetadata(imagePath) {
26
- const meta = await internalGetImageMetadata(imagePath);
27
- if (!meta) {
28
- return void 0;
29
- }
30
- delete meta.orientation;
31
- return { ...meta, __astro_asset: true };
32
- }
33
26
  export {
34
27
  createImage
35
28
  };
@@ -1,3 +1,4 @@
1
1
  export function defineCollection(config: any): any;
2
+ export function image(): never;
2
3
  export const getCollection: any;
3
4
  export const getEntryBySlug: any;
@@ -1,7 +1,7 @@
1
1
  /// <reference types="node" />
2
2
  import matter from 'gray-matter';
3
3
  import fsMod from 'node:fs';
4
- import type { EmitFile, PluginContext } from 'rollup';
4
+ import type { PluginContext } from 'rollup';
5
5
  import { type ViteDevServer } from 'vite';
6
6
  import { z } from 'zod';
7
7
  import type { AstroConfig, AstroSettings } from '../@types/astro.js';
@@ -47,17 +47,13 @@ export type EntryInfo = {
47
47
  export declare const msg: {
48
48
  collectionConfigMissing: (collection: string) => string;
49
49
  };
50
- /**
51
- * Mutate (arf) the entryData to reroute assets to their final paths
52
- */
53
- export declare function patchAssets(frontmatterEntry: Record<string, any>, watchMode: boolean, fileEmitter: EmitFile, astroSettings: AstroSettings): Promise<void>;
54
50
  export declare function getEntrySlug({ id, collection, slug, unvalidatedSlug, }: EntryInfo & {
55
51
  unvalidatedSlug?: unknown;
56
52
  }): string;
57
53
  export declare function getEntryData(entry: EntryInfo & {
58
54
  unvalidatedData: Record<string, unknown>;
59
55
  _internal: EntryInternal;
60
- }, collectionConfig: CollectionConfig, resolver: (idToResolve: string) => ReturnType<PluginContext['resolve']>): Promise<{
56
+ }, collectionConfig: CollectionConfig, pluginContext: PluginContext, settings: AstroSettings): Promise<{
61
57
  [x: string]: unknown;
62
58
  }>;
63
59
  export declare function getContentEntryExts(settings: Pick<AstroSettings, 'contentEntryTypes'>): string[];
@@ -5,10 +5,10 @@ import path from "node:path";
5
5
  import { fileURLToPath, pathToFileURL } from "node:url";
6
6
  import { normalizePath } from "vite";
7
7
  import { z } from "zod";
8
- import { emitESMImage } from "../assets/utils/emitAsset.js";
9
8
  import { AstroError, AstroErrorData } from "../core/errors/index.js";
10
9
  import { CONTENT_TYPES_FILE } from "./consts.js";
11
10
  import { errorMap } from "./error-map.js";
11
+ import { createImage } from "./runtime-assets.js";
12
12
  const collectionConfigParser = z.object({
13
13
  schema: z.any().optional()
14
14
  });
@@ -25,22 +25,6 @@ const contentConfigParser = z.object({
25
25
  const msg = {
26
26
  collectionConfigMissing: (collection) => `${collection} does not have a config. We suggest adding one for type safety!`
27
27
  };
28
- async function patchAssets(frontmatterEntry, watchMode, fileEmitter, astroSettings) {
29
- for (const key of Object.keys(frontmatterEntry)) {
30
- if (typeof frontmatterEntry[key] === "object" && frontmatterEntry[key] !== null) {
31
- if (frontmatterEntry[key]["__astro_asset"]) {
32
- frontmatterEntry[key] = await emitESMImage(
33
- frontmatterEntry[key].src,
34
- watchMode,
35
- fileEmitter,
36
- astroSettings
37
- );
38
- } else {
39
- await patchAssets(frontmatterEntry[key], watchMode, fileEmitter, astroSettings);
40
- }
41
- }
42
- }
43
- }
44
28
  function getEntrySlug({
45
29
  id,
46
30
  collection,
@@ -56,50 +40,27 @@ function getEntrySlug({
56
40
  });
57
41
  }
58
42
  }
59
- async function getEntryData(entry, collectionConfig, resolver) {
43
+ async function getEntryData(entry, collectionConfig, pluginContext, settings) {
60
44
  let { slug, ...data } = entry.unvalidatedData;
61
- if (collectionConfig.schema) {
62
- if (typeof collectionConfig.schema === "object" && !("safeParseAsync" in collectionConfig.schema)) {
63
- throw new AstroError({
64
- title: "Invalid content collection config",
65
- message: `New: Content collection schemas must be Zod objects. Update your collection config to use \`schema: z.object({...})\` instead of \`schema: {...}\`.`,
66
- hint: "See https://docs.astro.build/en/reference/api-reference/#definecollection for an example.",
67
- code: 99999
68
- });
45
+ let schema = collectionConfig.schema;
46
+ if (typeof schema === "function") {
47
+ if (!settings.config.experimental.assets) {
48
+ throw new Error(
49
+ "The function shape for schema can only be used when `experimental.assets` is enabled."
50
+ );
69
51
  }
70
- if (typeof collectionConfig.schema === "object" && "shape" in collectionConfig.schema && collectionConfig.schema.shape.slug) {
52
+ schema = schema({
53
+ image: createImage(settings, pluginContext, entry._internal.filePath)
54
+ });
55
+ }
56
+ if (schema) {
57
+ if (typeof schema === "object" && "shape" in schema && schema.shape.slug) {
71
58
  throw new AstroError({
72
59
  ...AstroErrorData.ContentSchemaContainsSlugError,
73
60
  message: AstroErrorData.ContentSchemaContainsSlugError.message(entry.collection)
74
61
  });
75
62
  }
76
- async function preprocessAssetPaths(object) {
77
- if (typeof object !== "object" || object === null)
78
- return;
79
- for (let [schemaName, schema] of Object.entries(object)) {
80
- if (schema._def.description === "__image") {
81
- object[schemaName] = z.preprocess(
82
- async (value) => {
83
- var _a;
84
- if (!value || typeof value !== "string")
85
- return value;
86
- return ((_a = await resolver(value)) == null ? void 0 : _a.id) ?? path.join(path.dirname(entry._internal.filePath), value);
87
- },
88
- schema,
89
- { description: "__image" }
90
- );
91
- } else if ("shape" in schema) {
92
- await preprocessAssetPaths(schema.shape);
93
- } else if ("unwrap" in schema) {
94
- const unwrapped = schema.unwrap().shape;
95
- if (unwrapped) {
96
- await preprocessAssetPaths(unwrapped);
97
- }
98
- }
99
- }
100
- }
101
- await preprocessAssetPaths(collectionConfig.schema.shape);
102
- const parsed = await collectionConfig.schema.safeParseAsync(entry.unvalidatedData, {
63
+ const parsed = await schema.safeParseAsync(entry.unvalidatedData, {
103
64
  errorMap
104
65
  });
105
66
  if (parsed.success) {
@@ -288,6 +249,5 @@ export {
288
249
  globalContentConfigObserver,
289
250
  loadContentConfig,
290
251
  msg,
291
- parseFrontmatter,
292
- patchAssets
252
+ parseFrontmatter
293
253
  };
@@ -1,9 +1,8 @@
1
- import npath from "node:path";
2
1
  import { pathToFileURL } from "url";
3
2
  import { moduleIsTopLevelPage, walkParentInfos } from "../core/build/graph.js";
4
3
  import { getPageDataByViteID } from "../core/build/internal.js";
5
4
  import { createViteLoader } from "../core/module-loader/vite.js";
6
- import { prependForwardSlash } from "../core/path.js";
5
+ import { joinPaths, prependForwardSlash } from "../core/path.js";
7
6
  import { getStylesForURL } from "../core/render/dev/css.js";
8
7
  import { getScriptsForURL } from "../core/render/dev/scripts.js";
9
8
  import {
@@ -59,7 +58,11 @@ function astroContentAssetPropagationPlugin({
59
58
  devModuleLoader,
60
59
  "development"
61
60
  );
62
- const hoistedScripts = await getScriptsForURL(pathToFileURL(basePath), devModuleLoader);
61
+ const hoistedScripts = await getScriptsForURL(
62
+ pathToFileURL(basePath),
63
+ settings.config.root,
64
+ devModuleLoader
65
+ );
63
66
  return {
64
67
  code: code.replace(JSON.stringify(LINKS_PLACEHOLDER), JSON.stringify([...urls])).replace(JSON.stringify(STYLES_PLACEHOLDER), JSON.stringify([...stylesMap.values()])).replace(JSON.stringify(SCRIPTS_PLACEHOLDER), JSON.stringify([...hoistedScripts]))
65
68
  };
@@ -87,7 +90,13 @@ function astroConfigBuildPlugin(options, internals) {
87
90
  "build:post": ({ ssrOutputs, clientOutputs, mutate }) => {
88
91
  var _a, _b;
89
92
  const outputs = ssrOutputs.flatMap((o) => o.output);
90
- const prependBase = (src) => prependForwardSlash(npath.posix.join(options.settings.config.base, src));
93
+ const prependBase = (src) => {
94
+ if (options.settings.config.build.assetsPrefix) {
95
+ return joinPaths(options.settings.config.build.assetsPrefix, src);
96
+ } else {
97
+ return prependForwardSlash(joinPaths(options.settings.config.base, src));
98
+ }
99
+ };
91
100
  for (const chunk of outputs) {
92
101
  if (chunk.type === "chunk" && (chunk.code.includes(LINKS_PLACEHOLDER) || chunk.code.includes(SCRIPTS_PLACEHOLDER))) {
93
102
  let entryCSS = /* @__PURE__ */ new Set();
@@ -13,8 +13,7 @@ import {
13
13
  getEntrySlug,
14
14
  getEntryType,
15
15
  globalContentConfigObserver,
16
- NoCollectionError,
17
- patchAssets
16
+ NoCollectionError
18
17
  } from "./utils.js";
19
18
  function isContentFlagImport(viteId, contentEntryExts) {
20
19
  const { searchParams, pathname } = new URL(viteId, "file://");
@@ -164,9 +163,9 @@ export const _internal = {
164
163
  let data = collectionConfig ? await getEntryData(
165
164
  { id, collection, slug, _internal, unvalidatedData },
166
165
  collectionConfig,
167
- (idToResolve) => pluginContext.resolve(idToResolve, fileId)
166
+ pluginContext,
167
+ settings
168
168
  ) : unvalidatedData;
169
- await patchAssets(data, pluginContext.meta.watchMode, pluginContext.emitFile, settings);
170
169
  const contentEntryModule = {
171
170
  id,
172
171
  slug,
@@ -16,16 +16,13 @@ function astroContentVirtualModPlugin({
16
16
  )
17
17
  );
18
18
  const contentEntryExts = getContentEntryExts(settings);
19
- const assetsDir = settings.config.experimental.assets ? contentPaths.assetsDir.toString() : "undefined";
20
19
  const extGlob = contentEntryExts.length === 1 ? (
21
20
  // Wrapping {...} breaks when there is only one extension
22
21
  contentEntryExts[0]
23
22
  ) : `{${contentEntryExts.join(",")}}`;
24
23
  const entryGlob = `${relContentDir}**/*${extGlob}`;
25
24
  const virtualModContents = fsMod.readFileSync(contentPaths.virtualModTemplate, "utf-8").replace("@@CONTENT_DIR@@", relContentDir).replace("@@ENTRY_GLOB_PATH@@", entryGlob).replace("@@RENDER_ENTRY_GLOB_PATH@@", entryGlob);
26
- const virtualAssetsModContents = fsMod.readFileSync(contentPaths.virtualAssetsModTemplate, "utf-8").replace("@@ASSETS_DIR@@", assetsDir);
27
25
  const astroContentVirtualModuleId = "\0" + VIRTUAL_MODULE_ID;
28
- const allContents = settings.config.experimental.assets ? virtualModContents + virtualAssetsModContents : virtualModContents;
29
26
  return {
30
27
  name: "astro-content-virtual-mod-plugin",
31
28
  enforce: "pre",
@@ -37,7 +34,7 @@ function astroContentVirtualModPlugin({
37
34
  load(id) {
38
35
  if (id === astroContentVirtualModuleId) {
39
36
  return {
40
- code: allContents
37
+ code: virtualModContents
41
38
  };
42
39
  }
43
40
  }
@@ -18,6 +18,7 @@ import { matchRoute } from "../routing/match.js";
18
18
  import { deserializeManifest } from "./common.js";
19
19
  const pagesVirtualModuleId = "@astrojs-pages-virtual-entry";
20
20
  const resolvedPagesVirtualModuleId = "\0" + pagesVirtualModuleId;
21
+ const responseSentSymbol = Symbol.for("astro.responseSent");
21
22
  class App {
22
23
  #env;
23
24
  #manifest;
@@ -168,6 +169,7 @@ class App {
168
169
  status
169
170
  });
170
171
  const response = await renderPage(mod, ctx, this.#env);
172
+ Reflect.set(request, responseSentSymbol, true);
171
173
  return response;
172
174
  } catch (err) {
173
175
  error(this.#logging, "ssr", err.stack || err.message || String(err));
@@ -1,2 +1,2 @@
1
- import type { InputOptions } from 'rollup';
2
- export declare function addRollupInput(inputOptions: InputOptions, newInputs: string[]): InputOptions;
1
+ import type { Rollup } from 'vite';
2
+ export declare function addRollupInput(inputOptions: Rollup.InputOptions, newInputs: string[]): Rollup.InputOptions;