astro 4.5.9 → 4.5.11

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.
package/astro-jsx.d.ts CHANGED
@@ -526,7 +526,7 @@ declare namespace astroHTML.JSX {
526
526
  | 'search'
527
527
  | 'send'
528
528
  | undefined
529
- | null;
529
+ | null;
530
530
  exportparts?: string | undefined | null;
531
531
  hidden?: boolean | string | undefined | null;
532
532
  id?: string | undefined | null;
@@ -584,6 +584,9 @@ declare namespace astroHTML.JSX {
584
584
  results?: number | string | undefined | null;
585
585
  security?: string | undefined | null;
586
586
  unselectable?: 'on' | 'off' | undefined | null; // Internet Explorer
587
+
588
+ // Allow data- attribute
589
+ [key: `data-${string}`]: any;
587
590
  }
588
591
 
589
592
  type HTMLAttributeReferrerPolicy =
@@ -1344,6 +1347,9 @@ declare namespace astroHTML.JSX {
1344
1347
  yChannelSelector?: string | undefined | null;
1345
1348
  z?: number | string | undefined | null;
1346
1349
  zoomAndPan?: string | undefined | null;
1350
+
1351
+ // Allow data- attribute
1352
+ [key: `data-${string}`]: any;
1347
1353
  }
1348
1354
 
1349
1355
  interface DefinedIntrinsicElements {
@@ -80,7 +80,7 @@ const highlighter = await getCachedHighlighter({
80
80
  ? Object.keys(bundledLanguages).includes(lang)
81
81
  ? lang
82
82
  : 'plaintext'
83
- : lang,
83
+ : (lang as any),
84
84
  ],
85
85
  theme,
86
86
  themes,
@@ -89,7 +89,7 @@ const highlighter = await getCachedHighlighter({
89
89
 
90
90
  const html = highlighter.highlight(code, typeof lang === 'string' ? lang : lang.name, {
91
91
  inline,
92
- attributes: rest,
92
+ attributes: rest as any,
93
93
  });
94
94
  ---
95
95
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  import { getImage, type LocalImageProps, type RemoteImageProps } from 'astro:assets';
3
3
  import type { GetImageResult, ImageOutputFormat } from '../dist/@types/astro';
4
- import { isESMImportedImage } from '../dist/assets/utils/imageKind';
4
+ import { isESMImportedImage, resolveSrc } from '../dist/assets/utils/imageKind';
5
5
  import { AstroError, AstroErrorData } from '../dist/core/errors/index.js';
6
6
  import type { HTMLAttributes } from '../types';
7
7
 
@@ -27,20 +27,27 @@ if (props.alt === undefined || props.alt === null) {
27
27
  throw new AstroError(AstroErrorData.ImageMissingAlt);
28
28
  }
29
29
 
30
+ const originalSrc = await resolveSrc(props.src);
30
31
  const optimizedImages: GetImageResult[] = await Promise.all(
31
32
  formats.map(
32
33
  async (format) =>
33
- await getImage({ ...props, format: format, widths: props.widths, densities: props.densities })
34
+ await getImage({
35
+ ...props,
36
+ src: originalSrc,
37
+ format: format,
38
+ widths: props.widths,
39
+ densities: props.densities,
40
+ })
34
41
  )
35
42
  );
36
43
 
37
44
  let resultFallbackFormat = fallbackFormat ?? defaultFallbackFormat;
38
45
  if (
39
46
  !fallbackFormat &&
40
- isESMImportedImage(props.src) &&
41
- specialFormatsFallback.includes(props.src.format)
47
+ isESMImportedImage(originalSrc) &&
48
+ originalSrc.format in specialFormatsFallback
42
49
  ) {
43
- resultFallbackFormat = props.src.format;
50
+ resultFallbackFormat = originalSrc.format;
44
51
  }
45
52
 
46
53
  const fallbackImage = await getImage({
@@ -1,11 +1,11 @@
1
1
  import fs, { readFileSync } from "node:fs";
2
- import { basename, join } from "node:path/posix";
2
+ import { basename } from "node:path/posix";
3
3
  import { dim, green } from "kleur/colors";
4
4
  import { getOutDirWithinCwd } from "../../core/build/common.js";
5
5
  import { getTimeStat } from "../../core/build/util.js";
6
6
  import { AstroError } from "../../core/errors/errors.js";
7
7
  import { AstroErrorData } from "../../core/errors/index.js";
8
- import { isRemotePath, prependForwardSlash } from "../../core/path.js";
8
+ import { isRemotePath, removeLeadingForwardSlash } from "../../core/path.js";
9
9
  import { isServerLikeOutput } from "../../prerender/utils.js";
10
10
  import { getConfiguredImageService } from "../internal.js";
11
11
  import { isESMImportedImage } from "../utils/imageKind.js";
@@ -45,10 +45,7 @@ async function prepareAssetsGenerationEnv(pipeline, totalCount) {
45
45
  };
46
46
  }
47
47
  function getFullImagePath(originalFilePath, env) {
48
- return new URL(
49
- "." + prependForwardSlash(join(env.assetsFolder, basename(originalFilePath))),
50
- env.serverRoot
51
- );
48
+ return new URL(removeLeadingForwardSlash(originalFilePath), env.serverRoot);
52
49
  }
53
50
  async function generateImagesForPath(originalFilePath, transformsAndPath, env, queue) {
54
51
  const originalImageData = await loadImage(originalFilePath, env);
@@ -57,7 +54,7 @@ async function generateImagesForPath(originalFilePath, transformsAndPath, env, q
57
54
  throw e;
58
55
  });
59
56
  }
60
- if (!env.isSSR && !isRemotePath(originalFilePath) && !globalThis.astroAsset.referencedImages?.has(transformsAndPath.originalSrcPath)) {
57
+ if (!env.isSSR && transformsAndPath.originalSrcPath && !globalThis.astroAsset.referencedImages?.has(transformsAndPath.originalSrcPath)) {
61
58
  try {
62
59
  if (transformsAndPath.originalSrcPath) {
63
60
  env.logger.debug(
@@ -1,7 +1,7 @@
1
1
  import { AstroError, AstroErrorData } from "../core/errors/index.js";
2
2
  import { DEFAULT_HASH_PROPS } from "./consts.js";
3
3
  import { isLocalService } from "./services/service.js";
4
- import { isESMImportedImage, isRemoteImage } from "./utils/imageKind.js";
4
+ import { isESMImportedImage, isRemoteImage, resolveSrc } from "./utils/imageKind.js";
5
5
  import { probe } from "./utils/remoteProbe.js";
6
6
  async function getConfiguredImageService() {
7
7
  if (!globalThis?.astroAsset?.imageService) {
@@ -40,7 +40,7 @@ async function getImage(options, imageConfig) {
40
40
  const service = await getConfiguredImageService();
41
41
  const resolvedOptions = {
42
42
  ...options,
43
- src: typeof options.src === "object" && "then" in options.src ? (await options.src).default ?? await options.src : options.src
43
+ src: await resolveSrc(options.src)
44
44
  };
45
45
  if (options.inferSize && isRemoteImage(resolvedOptions.src)) {
46
46
  try {
@@ -55,7 +55,7 @@ async function getImage(options, imageConfig) {
55
55
  });
56
56
  }
57
57
  }
58
- const originalPath = isESMImportedImage(resolvedOptions.src) ? resolvedOptions.src.fsPath : resolvedOptions.src;
58
+ const originalFilePath = isESMImportedImage(resolvedOptions.src) ? resolvedOptions.src.fsPath : void 0;
59
59
  const clonedSrc = isESMImportedImage(resolvedOptions.src) ? (
60
60
  // @ts-expect-error - clone is a private, hidden prop
61
61
  resolvedOptions.src.clone ?? resolvedOptions.src
@@ -74,10 +74,14 @@ async function getImage(options, imageConfig) {
74
74
  );
75
75
  if (isLocalService(service) && globalThis.astroAsset.addStaticImage && !(isRemoteImage(validatedOptions.src) && imageURL === validatedOptions.src)) {
76
76
  const propsToHash = service.propertiesToHash ?? DEFAULT_HASH_PROPS;
77
- imageURL = globalThis.astroAsset.addStaticImage(validatedOptions, propsToHash, originalPath);
77
+ imageURL = globalThis.astroAsset.addStaticImage(
78
+ validatedOptions,
79
+ propsToHash,
80
+ originalFilePath
81
+ );
78
82
  srcSets = srcSetTransforms.map((srcSet) => ({
79
83
  transform: srcSet.transform,
80
- url: globalThis.astroAsset.addStaticImage(srcSet.transform, propsToHash, originalPath),
84
+ url: globalThis.astroAsset.addStaticImage(srcSet.transform, propsToHash, originalFilePath),
81
85
  descriptor: srcSet.descriptor,
82
86
  attributes: srcSet.attributes
83
87
  }));
@@ -6,7 +6,7 @@ export type ImageQuality = ImageQualityPreset | number;
6
6
  export type ImageInputFormat = (typeof VALID_INPUT_FORMATS)[number];
7
7
  export type ImageOutputFormat = (typeof VALID_OUTPUT_FORMATS)[number] | (string & {});
8
8
  export type AssetsGlobalStaticImagesList = Map<string, {
9
- originalSrcPath: string;
9
+ originalSrcPath: string | undefined;
10
10
  transforms: Map<string, {
11
11
  finalPath: string;
12
12
  transform: ImageTransform;
@@ -15,7 +15,7 @@ export type AssetsGlobalStaticImagesList = Map<string, {
15
15
  declare global {
16
16
  var astroAsset: {
17
17
  imageService?: ImageService;
18
- addStaticImage?: ((options: ImageTransform, hashProperties: string[], fsPath: string) => string) | undefined;
18
+ addStaticImage?: ((options: ImageTransform, hashProperties: string[], fsPath: string | undefined) => string) | undefined;
19
19
  staticImages?: AssetsGlobalStaticImagesList;
20
20
  referencedImages?: Set<string>;
21
21
  };
@@ -1,3 +1,4 @@
1
- import type { ImageMetadata } from '../types.js';
1
+ import type { ImageMetadata, UnresolvedImageTransform } from '../types.js';
2
2
  export declare function isESMImportedImage(src: ImageMetadata | string): src is ImageMetadata;
3
3
  export declare function isRemoteImage(src: ImageMetadata | string): src is string;
4
+ export declare function resolveSrc(src: UnresolvedImageTransform['src']): Promise<string | ImageMetadata>;
@@ -4,7 +4,11 @@ function isESMImportedImage(src) {
4
4
  function isRemoteImage(src) {
5
5
  return typeof src === "string";
6
6
  }
7
+ async function resolveSrc(src) {
8
+ return typeof src === "object" && "then" in src ? (await src).default ?? await src : src;
9
+ }
7
10
  export {
8
11
  isESMImportedImage,
9
- isRemoteImage
12
+ isRemoteImage,
13
+ resolveSrc
10
14
  };
@@ -1,3 +1,3 @@
1
1
  import type { ImageTransform } from '../types.js';
2
- export declare function propsToFilename(transform: ImageTransform, hash: string): string;
2
+ export declare function propsToFilename(filePath: string, transform: ImageTransform, hash: string): string;
3
3
  export declare function hashTransform(transform: ImageTransform, imageService: string, propertiesToHash: string[]): string;
@@ -1,16 +1,15 @@
1
- import { basename, extname } from "node:path";
1
+ import { basename, dirname, extname } from "node:path";
2
2
  import { deterministicString } from "deterministic-object-hash";
3
3
  import { removeQueryString } from "../../core/path.js";
4
4
  import { shorthash } from "../../runtime/server/shorthash.js";
5
5
  import { isESMImportedImage } from "./imageKind.js";
6
- function propsToFilename(transform, hash) {
7
- let filename = removeQueryString(
8
- isESMImportedImage(transform.src) ? transform.src.src : transform.src
9
- );
6
+ function propsToFilename(filePath, transform, hash) {
7
+ let filename = decodeURIComponent(removeQueryString(filePath));
10
8
  const ext = extname(filename);
11
- filename = decodeURIComponent(basename(filename, ext));
9
+ filename = basename(filename, ext);
10
+ const prefixDirname = isESMImportedImage(transform.src) ? dirname(filePath) : "";
12
11
  let outputExt = transform.format ? `.${transform.format}` : ext;
13
- return `/${filename}_${hash}${outputExt}`;
12
+ return decodeURIComponent(`${prefixDirname}/${filename}_${hash}${outputExt}`);
14
13
  }
15
14
  function hashTransform(transform, imageService, propertiesToHash) {
16
15
  const hashFields = propertiesToHash.reduce(
@@ -7,6 +7,7 @@ import {
7
7
  appendForwardSlash,
8
8
  joinPaths,
9
9
  prependForwardSlash,
10
+ removeBase,
10
11
  removeQueryString
11
12
  } from "../core/path.js";
12
13
  import { isServerLikeOutput } from "../prerender/utils.js";
@@ -57,12 +58,18 @@ function assets({
57
58
  export { default as Picture } from "astro/components/Picture.astro";
58
59
 
59
60
  export const imageConfig = ${JSON.stringify(settings.config.image)};
60
- export const outDir = new URL(${JSON.stringify(
61
+ // This is used by the @astrojs/node integration to locate images.
62
+ // It's unused on other platforms, but on some platforms like Netlify (and presumably also Vercel)
63
+ // new URL("dist/...") is interpreted by the bundler as a signal to include that directory
64
+ // in the Lambda bundle, which would bloat the bundle with images.
65
+ // To prevent this, we mark the URL construction as pure,
66
+ // so that it's tree-shaken away for all platforms that don't need it.
67
+ export const outDir = /* #__PURE__ */ new URL(${JSON.stringify(
61
68
  new URL(
62
69
  isServerLikeOutput(settings.config) ? settings.config.build.client : settings.config.outDir
63
70
  )
64
71
  )});
65
- export const assetsDir = new URL(${JSON.stringify(settings.config.build.assets)}, outDir);
72
+ export const assetsDir = /* #__PURE__ */ new URL(${JSON.stringify(settings.config.build.assets)}, outDir);
66
73
  export const getImage = async (options) => await getImageInternal(options, imageConfig);
67
74
  `;
68
75
  }
@@ -71,34 +78,40 @@ function assets({
71
78
  if (mode != "build") {
72
79
  return;
73
80
  }
74
- globalThis.astroAsset.addStaticImage = (options, hashProperties, originalPath) => {
81
+ globalThis.astroAsset.addStaticImage = (options, hashProperties, originalFSPath) => {
75
82
  if (!globalThis.astroAsset.staticImages) {
76
83
  globalThis.astroAsset.staticImages = /* @__PURE__ */ new Map();
77
84
  }
78
85
  const ESMImportedImageSrc = isESMImportedImage(options.src) ? options.src.src : options.src;
79
86
  const fileExtension = extname(ESMImportedImageSrc);
80
- const pf = getAssetsPrefix(fileExtension, settings.config.build.assetsPrefix);
81
- const finalOriginalImagePath = ESMImportedImageSrc.replace(pf, "");
87
+ const assetPrefix = getAssetsPrefix(fileExtension, settings.config.build.assetsPrefix);
88
+ const finalOriginalPath = removeBase(
89
+ removeBase(ESMImportedImageSrc, settings.config.base),
90
+ assetPrefix
91
+ );
82
92
  const hash = hashTransform(
83
93
  options,
84
94
  settings.config.image.service.entrypoint,
85
95
  hashProperties
86
96
  );
87
97
  let finalFilePath;
88
- let transformsForPath = globalThis.astroAsset.staticImages.get(finalOriginalImagePath);
98
+ let transformsForPath = globalThis.astroAsset.staticImages.get(finalOriginalPath);
89
99
  let transformForHash = transformsForPath?.transforms.get(hash);
90
100
  if (transformsForPath && transformForHash) {
91
101
  finalFilePath = transformForHash.finalPath;
92
102
  } else {
93
103
  finalFilePath = prependForwardSlash(
94
- joinPaths(settings.config.build.assets, propsToFilename(options, hash))
104
+ joinPaths(
105
+ isESMImportedImage(options.src) ? "" : settings.config.build.assets,
106
+ prependForwardSlash(propsToFilename(finalOriginalPath, options, hash))
107
+ )
95
108
  );
96
109
  if (!transformsForPath) {
97
- globalThis.astroAsset.staticImages.set(finalOriginalImagePath, {
98
- originalSrcPath: originalPath,
110
+ globalThis.astroAsset.staticImages.set(finalOriginalPath, {
111
+ originalSrcPath: originalFSPath,
99
112
  transforms: /* @__PURE__ */ new Map()
100
113
  });
101
- transformsForPath = globalThis.astroAsset.staticImages.get(finalOriginalImagePath);
114
+ transformsForPath = globalThis.astroAsset.staticImages.get(finalOriginalPath);
102
115
  }
103
116
  transformsForPath.transforms.set(hash, {
104
117
  finalPath: finalFilePath,
@@ -106,7 +119,7 @@ function assets({
106
119
  });
107
120
  }
108
121
  if (settings.config.build.assetsPrefix) {
109
- return encodeURI(joinPaths(pf, finalFilePath));
122
+ return encodeURI(joinPaths(assetPrefix, finalFilePath));
110
123
  } else {
111
124
  return encodeURI(prependForwardSlash(joinPaths(settings.config.base, finalFilePath)));
112
125
  }
@@ -2,7 +2,7 @@ export { CONTENT_FLAG, PROPAGATED_ASSET_FLAG } from './consts.js';
2
2
  export { errorMap } from './error-map.js';
3
3
  export { attachContentServerListeners } from './server-listeners.js';
4
4
  export { createContentTypesGenerator } from './types-generator.js';
5
- export { contentObservable, getContentPaths, getDotAstroTypeReference } from './utils.js';
5
+ export { contentObservable, getContentPaths, getDotAstroTypeReference, hasAssetPropagationFlag, } from './utils.js';
6
6
  export { astroContentAssetPropagationPlugin } from './vite-plugin-content-assets.js';
7
7
  export { astroContentImportPlugin } from './vite-plugin-content-imports.js';
8
8
  export { astroContentVirtualModPlugin } from './vite-plugin-content-virtual-mod.js';
@@ -2,7 +2,12 @@ import { CONTENT_FLAG, PROPAGATED_ASSET_FLAG } from "./consts.js";
2
2
  import { errorMap } from "./error-map.js";
3
3
  import { attachContentServerListeners } from "./server-listeners.js";
4
4
  import { createContentTypesGenerator } from "./types-generator.js";
5
- import { contentObservable, getContentPaths, getDotAstroTypeReference } from "./utils.js";
5
+ import {
6
+ contentObservable,
7
+ getContentPaths,
8
+ getDotAstroTypeReference,
9
+ hasAssetPropagationFlag
10
+ } from "./utils.js";
6
11
  import { astroContentAssetPropagationPlugin } from "./vite-plugin-content-assets.js";
7
12
  import { astroContentImportPlugin } from "./vite-plugin-content-imports.js";
8
13
  import { astroContentVirtualModPlugin } from "./vite-plugin-content-virtual-mod.js";
@@ -17,5 +22,6 @@ export {
17
22
  createContentTypesGenerator,
18
23
  errorMap,
19
24
  getContentPaths,
20
- getDotAstroTypeReference
25
+ getDotAstroTypeReference,
26
+ hasAssetPropagationFlag
21
27
  };
@@ -184,4 +184,5 @@ export declare function getEntrySlug({ id, collection, generatedSlug, contentEnt
184
184
  contentEntryType: Pick<ContentEntryType, 'getEntryInfo'>;
185
185
  }): Promise<string>;
186
186
  export declare function getExtGlob(exts: string[]): string;
187
+ export declare function hasAssetPropagationFlag(id: string): boolean;
187
188
  export {};
@@ -8,7 +8,7 @@ import { z } from "zod";
8
8
  import { AstroError, AstroErrorData } from "../core/errors/index.js";
9
9
  import { MarkdownError } from "../core/errors/index.js";
10
10
  import { isYAMLException } from "../core/errors/utils.js";
11
- import { CONTENT_FLAGS, CONTENT_TYPES_FILE } from "./consts.js";
11
+ import { CONTENT_FLAGS, CONTENT_TYPES_FILE, PROPAGATED_ASSET_FLAG } from "./consts.js";
12
12
  import { errorMap } from "./error-map.js";
13
13
  import { createImage } from "./runtime-assets.js";
14
14
  const collectionConfigParser = z.union([
@@ -345,6 +345,13 @@ function getExtGlob(exts) {
345
345
  exts[0]
346
346
  ) : `{${exts.join(",")}}`;
347
347
  }
348
+ function hasAssetPropagationFlag(id) {
349
+ try {
350
+ return new URL(id, "file://").searchParams.has(PROPAGATED_ASSET_FLAG);
351
+ } catch {
352
+ return false;
353
+ }
354
+ }
348
355
  export {
349
356
  collectionConfigParser,
350
357
  contentConfigParser,
@@ -363,6 +370,7 @@ export {
363
370
  getExtGlob,
364
371
  globalContentConfigObserver,
365
372
  hasAnyContentFlag,
373
+ hasAssetPropagationFlag,
366
374
  hasContentFlag,
367
375
  hasUnderscoreBelowContentDirectoryPath,
368
376
  loadContentConfig,
@@ -1,5 +1,6 @@
1
1
  import { isBuildableCSSRequest } from "../../../vite-plugin-astro-server/util.js";
2
- import { PROPAGATED_ASSET_FLAG } from "../../../content/consts.js";
2
+ import { RESOLVED_VIRTUAL_MODULE_ID as ASTRO_CONTENT_VIRTUAL_MODULE_ID } from "../../../content/consts.js";
3
+ import { hasAssetPropagationFlag } from "../../../content/index.js";
3
4
  import * as assetName from "../css-asset-name.js";
4
5
  import {
5
6
  getParentExtendedModuleInfos,
@@ -52,14 +53,9 @@ function rollupPluginAstroBuildCSS(options) {
52
53
  }
53
54
  const ctx = { getModuleInfo: meta.getModuleInfo };
54
55
  for (const pageInfo of getParentModuleInfos(id, ctx)) {
55
- if (new URL(pageInfo.id, "file://").searchParams.has(PROPAGATED_ASSET_FLAG)) {
56
+ if (hasAssetPropagationFlag(pageInfo.id)) {
56
57
  const chunkId2 = assetName.createNameHash(id, [id]);
57
58
  internals.cssModuleToChunkIdMap.set(id, chunkId2);
58
- if (isContentCollectionCache) {
59
- const propagatedStyles = internals.propagatedStylesMap.get(pageInfo.id) ?? /* @__PURE__ */ new Set();
60
- propagatedStyles.add({ type: "external", src: chunkId2 });
61
- internals.propagatedStylesMap.set(pageInfo.id, propagatedStyles);
62
- }
63
59
  return chunkId2;
64
60
  }
65
61
  }
@@ -90,15 +86,11 @@ function rollupPluginAstroBuildCSS(options) {
90
86
  }
91
87
  }
92
88
  for (const id of Object.keys(chunk.modules)) {
93
- for (const { info: pageInfo, depth, order } of getParentExtendedModuleInfos(
94
- id,
95
- this,
96
- function until(importer) {
97
- return new URL(importer, "file://").searchParams.has(PROPAGATED_ASSET_FLAG);
98
- }
99
- )) {
100
- if (new URL(pageInfo.id, "file://").searchParams.has(PROPAGATED_ASSET_FLAG)) {
101
- for (const parentInfo of getParentModuleInfos(id, this)) {
89
+ const parentModuleInfos = getParentExtendedModuleInfos(id, this, hasAssetPropagationFlag);
90
+ for (const { info: pageInfo, depth, order } of parentModuleInfos) {
91
+ if (hasAssetPropagationFlag(pageInfo.id)) {
92
+ const walkId = isContentCollectionCache ? ASTRO_CONTENT_VIRTUAL_MODULE_ID : id;
93
+ for (const parentInfo of getParentModuleInfos(walkId, this)) {
102
94
  if (moduleIsTopLevelPage(parentInfo) === false)
103
95
  continue;
104
96
  const pageViteID = parentInfo.id;
@@ -1,4 +1,4 @@
1
- const ASTRO_VERSION = "4.5.9";
1
+ const ASTRO_VERSION = "4.5.11";
2
2
  const REROUTE_DIRECTIVE_HEADER = "X-Astro-Reroute";
3
3
  const ROUTE_TYPE_HEADER = "X-Astro-Route-Type";
4
4
  const DEFAULT_404_COMPONENT = "astro-default-404";
@@ -23,7 +23,7 @@ async function dev(inlineConfig) {
23
23
  base: restart.container.settings.config.base
24
24
  })
25
25
  );
26
- const currentVersion = "4.5.9";
26
+ const currentVersion = "4.5.11";
27
27
  if (currentVersion.includes("-")) {
28
28
  logger.warn("SKIP_FORMAT", msg.prerelease({ currentVersion }));
29
29
  }
@@ -36,7 +36,7 @@ function serverStart({
36
36
  host,
37
37
  base
38
38
  }) {
39
- const version = "4.5.9";
39
+ const version = "4.5.11";
40
40
  const localPrefix = `${dim("\u2503")} Local `;
41
41
  const networkPrefix = `${dim("\u2503")} Network `;
42
42
  const emptyPrefix = " ".repeat(11);
@@ -261,7 +261,7 @@ function printHelp({
261
261
  message.push(
262
262
  linebreak(),
263
263
  ` ${bgGreen(black(` ${commandName} `))} ${green(
264
- `v${"4.5.9"}`
264
+ `v${"4.5.11"}`
265
265
  )} ${headline}`
266
266
  );
267
267
  }
@@ -33,7 +33,7 @@ function createRequest({
33
33
  get() {
34
34
  logger.warn(
35
35
  null,
36
- `\`Astro.request.headers\` is not available in "static" output mode. To enable header access: set \`output: "server"\` or \`output: "hybrid"\` in your config file.`
36
+ `\`Astro.request.headers\` is unavailable in "static" output mode, and in prerendered pages within "hybrid" and "server" output modes. If you need access to request headers, make sure that \`output\` is configured as either \`"server"\` or \`output: "hybrid"\` in your config file, and that the page accessing the headers is rendered on-demand.`
37
37
  );
38
38
  return _headers;
39
39
  }
@@ -7,7 +7,7 @@ function ensure404Route(manifest) {
7
7
  params: [],
8
8
  pattern: /\/404/,
9
9
  prerender: false,
10
- segments: [],
10
+ segments: [[{ content: "404", dynamic: false, spread: false }]],
11
11
  type: "page",
12
12
  route: "/404",
13
13
  fallbackRoutes: [],
@@ -10,6 +10,7 @@ import { MissingIndexForInternationalization } from "../../errors/errors-data.js
10
10
  import { AstroError } from "../../errors/index.js";
11
11
  import { removeLeadingForwardSlash, slash } from "../../path.js";
12
12
  import { resolvePages } from "../../util.js";
13
+ import { routeComparator } from "../priority.js";
13
14
  import { getRouteGenerator } from "./generator.js";
14
15
  const require2 = createRequire(import.meta.url);
15
16
  function countOccurrences(needle, haystack) {
@@ -101,54 +102,6 @@ function isSemanticallyEqualSegment(segmentA, segmentB) {
101
102
  }
102
103
  return true;
103
104
  }
104
- function routeComparator(a, b) {
105
- const commonLength = Math.min(a.segments.length, b.segments.length);
106
- for (let index = 0; index < commonLength; index++) {
107
- const aSegment = a.segments[index];
108
- const bSegment = b.segments[index];
109
- const aIsStatic = aSegment.every((part) => !part.dynamic && !part.spread);
110
- const bIsStatic = bSegment.every((part) => !part.dynamic && !part.spread);
111
- if (aIsStatic && bIsStatic) {
112
- const aContent = aSegment.map((part) => part.content).join("");
113
- const bContent = bSegment.map((part) => part.content).join("");
114
- if (aContent !== bContent) {
115
- return aContent.localeCompare(bContent);
116
- }
117
- }
118
- if (aIsStatic !== bIsStatic) {
119
- return aIsStatic ? -1 : 1;
120
- }
121
- const aAllDynamic = aSegment.every((part) => part.dynamic);
122
- const bAllDynamic = bSegment.every((part) => part.dynamic);
123
- if (aAllDynamic !== bAllDynamic) {
124
- return aAllDynamic ? 1 : -1;
125
- }
126
- const aHasSpread = aSegment.some((part) => part.spread);
127
- const bHasSpread = bSegment.some((part) => part.spread);
128
- if (aHasSpread !== bHasSpread) {
129
- return aHasSpread ? 1 : -1;
130
- }
131
- }
132
- const aLength = a.segments.length;
133
- const bLength = b.segments.length;
134
- if (aLength !== bLength) {
135
- const aEndsInRest = a.segments.at(-1)?.some((part) => part.spread);
136
- const bEndsInRest = b.segments.at(-1)?.some((part) => part.spread);
137
- if (aEndsInRest !== bEndsInRest && Math.abs(aLength - bLength) === 1) {
138
- if (aLength > bLength && aEndsInRest) {
139
- return 1;
140
- }
141
- if (bLength > aLength && bEndsInRest) {
142
- return -1;
143
- }
144
- }
145
- return aLength > bLength ? -1 : 1;
146
- }
147
- if (a.type === "endpoint" !== (b.type === "endpoint")) {
148
- return a.type === "endpoint" ? -1 : 1;
149
- }
150
- return a.route.localeCompare(b.route);
151
- }
152
105
  function createFileBasedRoutes({ settings, cwd, fsMod }, logger) {
153
106
  const components = [];
154
107
  const routes = [];
@@ -0,0 +1,23 @@
1
+ import type { RouteData } from '../../@types/astro.js';
2
+ /**
3
+ * Comparator for sorting routes in resolution order.
4
+ *
5
+ * The routes are sorted in by the following rules in order, following the first rule that
6
+ * applies:
7
+ * - More specific routes are sorted before less specific routes. Here, "specific" means
8
+ * the number of segments in the route, so a parent route is always sorted after its children.
9
+ * For example, `/foo/bar` is sorted before `/foo`.
10
+ * Index routes, originating from a file named `index.astro`, are considered to have one more
11
+ * segment than the URL they represent.
12
+ * - Static routes are sorted before dynamic routes.
13
+ * For example, `/foo/bar` is sorted before `/foo/[bar]`.
14
+ * - Dynamic routes with single parameters are sorted before dynamic routes with rest parameters.
15
+ * For example, `/foo/[bar]` is sorted before `/foo/[...bar]`.
16
+ * - Prerendered routes are sorted before non-prerendered routes.
17
+ * - Endpoints are sorted before pages.
18
+ * For example, a file `/foo.ts` is sorted before `/bar.astro`.
19
+ * - If both routes are equal regarding all previosu conditions, they are sorted alphabetically.
20
+ * For example, `/bar` is sorted before `/foo`.
21
+ * The definition of "alphabetically" is dependent on the default locale of the running system.
22
+ */
23
+ export declare function routeComparator(a: RouteData, b: RouteData): number;
@@ -0,0 +1,51 @@
1
+ function routeComparator(a, b) {
2
+ const commonLength = Math.min(a.segments.length, b.segments.length);
3
+ for (let index = 0; index < commonLength; index++) {
4
+ const aSegment = a.segments[index];
5
+ const bSegment = b.segments[index];
6
+ const aIsStatic = aSegment.every((part) => !part.dynamic && !part.spread);
7
+ const bIsStatic = bSegment.every((part) => !part.dynamic && !part.spread);
8
+ if (aIsStatic && bIsStatic) {
9
+ const aContent = aSegment.map((part) => part.content).join("");
10
+ const bContent = bSegment.map((part) => part.content).join("");
11
+ if (aContent !== bContent) {
12
+ return aContent.localeCompare(bContent);
13
+ }
14
+ }
15
+ if (aIsStatic !== bIsStatic) {
16
+ return aIsStatic ? -1 : 1;
17
+ }
18
+ const aAllDynamic = aSegment.every((part) => part.dynamic);
19
+ const bAllDynamic = bSegment.every((part) => part.dynamic);
20
+ if (aAllDynamic !== bAllDynamic) {
21
+ return aAllDynamic ? 1 : -1;
22
+ }
23
+ const aHasSpread = aSegment.some((part) => part.spread);
24
+ const bHasSpread = bSegment.some((part) => part.spread);
25
+ if (aHasSpread !== bHasSpread) {
26
+ return aHasSpread ? 1 : -1;
27
+ }
28
+ }
29
+ const aLength = a.segments.length;
30
+ const bLength = b.segments.length;
31
+ if (aLength !== bLength) {
32
+ const aEndsInRest = a.segments.at(-1)?.some((part) => part.spread);
33
+ const bEndsInRest = b.segments.at(-1)?.some((part) => part.spread);
34
+ if (aEndsInRest !== bEndsInRest && Math.abs(aLength - bLength) === 1) {
35
+ if (aLength > bLength && aEndsInRest) {
36
+ return 1;
37
+ }
38
+ if (bLength > aLength && bEndsInRest) {
39
+ return -1;
40
+ }
41
+ }
42
+ return aLength > bLength ? -1 : 1;
43
+ }
44
+ if (a.type === "endpoint" !== (b.type === "endpoint")) {
45
+ return a.type === "endpoint" ? -1 : 1;
46
+ }
47
+ return a.route.localeCompare(b.route);
48
+ }
49
+ export {
50
+ routeComparator
51
+ };
@@ -1,4 +1,5 @@
1
1
  import { RedirectComponentInstance, routeIsRedirect } from "../core/redirects/index.js";
2
+ import { routeComparator } from "../core/routing/priority.js";
2
3
  import { getPrerenderStatus } from "./metadata.js";
3
4
  async function getSortedPreloadedMatches({
4
5
  pipeline,
@@ -9,7 +10,7 @@ async function getSortedPreloadedMatches({
9
10
  pipeline,
10
11
  matches,
11
12
  settings
12
- })).sort((a, b) => prioritizePrerenderedMatchesComparator(a.route, b.route));
13
+ })).sort((a, b) => routeComparator(a.route, b.route)).sort((a, b) => prioritizePrerenderedMatchesComparator(a.route, b.route));
13
14
  }
14
15
  async function preloadAndSetPrerenderStatus({
15
16
  pipeline,
@@ -222,7 +222,7 @@ async function handleRoute({
222
222
  })
223
223
  );
224
224
  }
225
- if (response.status === 404 && has404Route(manifestData) && response.headers.get(REROUTE_DIRECTIVE_HEADER) !== "no") {
225
+ if (response.status === 404 && response.headers.get(REROUTE_DIRECTIVE_HEADER) !== "no") {
226
226
  const fourOhFourRoute = await matchRoute("/404", manifestData, pipeline);
227
227
  if (options)
228
228
  return handleRoute({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "4.5.9",
3
+ "version": "4.5.11",
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",
@@ -162,7 +162,7 @@
162
162
  "zod": "^3.22.4",
163
163
  "zod-to-json-schema": "^3.22.4",
164
164
  "@astrojs/internal-helpers": "0.3.0",
165
- "@astrojs/markdown-remark": "4.3.1",
165
+ "@astrojs/markdown-remark": "4.3.2",
166
166
  "@astrojs/telemetry": "3.0.4"
167
167
  },
168
168
  "optionalDependencies": {
package/types.d.ts CHANGED
@@ -1,12 +1,13 @@
1
1
  import './astro-jsx';
2
2
  import type { AstroBuiltinAttributes } from './dist/@types/astro.js';
3
- import type { Simplify } from './dist/type-utils.js';
3
+ import type { OmitIndexSignature, Simplify } from './dist/type-utils.js';
4
4
 
5
5
  /** Any supported HTML or SVG element name, as defined by the HTML specification */
6
6
  export type HTMLTag = keyof astroHTML.JSX.DefinedIntrinsicElements;
7
+
7
8
  /** The built-in attributes for any known HTML or SVG element name */
8
9
  export type HTMLAttributes<Tag extends HTMLTag> = Omit<
9
- astroHTML.JSX.IntrinsicElements[Tag],
10
+ astroHTML.JSX.DefinedIntrinsicElements[Tag],
10
11
  keyof Omit<AstroBuiltinAttributes, 'class:list'>
11
12
  >;
12
13
 
@@ -15,9 +16,15 @@ export type HTMLAttributes<Tag extends HTMLTag> = Omit<
15
16
  */
16
17
  export type CSSProperty = keyof astroHTML.JSX.KebabCSSDOMProperties;
17
18
 
18
- type PolymorphicAttributes<P extends { as: HTMLTag }> = Omit<P & HTMLAttributes<P['as']>, 'as'> & {
19
+ type PolymorphicAttributes<P extends { as: HTMLTag }> = Omit<P, 'as'> & {
19
20
  as?: P['as'];
20
- };
21
+ } & Omit<
22
+ // This is the same as HTMLAttributes<P['as']>, except we're using OmitIndexSignature to remove the index signature,
23
+ // used for data attribute, because it seems like it get too complex for TypeScript with it, not sure why.
24
+ OmitIndexSignature<astroHTML.JSX.DefinedIntrinsicElements[P['as']]>,
25
+ keyof Omit<AstroBuiltinAttributes, 'class:list'>
26
+ >;
27
+
21
28
  export type Polymorphic<P extends { as: HTMLTag }> = PolymorphicAttributes<
22
29
  Omit<P, 'as'> & { as: NonNullable<P['as']> }
23
30
  >;