astro 4.1.3 → 4.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 (42) hide show
  1. package/dist/@types/astro.d.ts +101 -0
  2. package/dist/content/types-generator.js +0 -26
  3. package/dist/content/utils.d.ts +1 -1
  4. package/dist/content/utils.js +3 -10
  5. package/dist/core/app/createOutgoingHttpHeaders.d.ts +9 -0
  6. package/dist/core/app/createOutgoingHttpHeaders.js +19 -0
  7. package/dist/core/app/index.d.ts +38 -1
  8. package/dist/core/app/index.js +38 -8
  9. package/dist/core/app/node.d.ts +42 -10
  10. package/dist/core/app/node.js +87 -46
  11. package/dist/core/app/types.d.ts +2 -1
  12. package/dist/core/config/schema.d.ts +146 -66
  13. package/dist/core/config/schema.js +24 -6
  14. package/dist/core/config/vite-load.js +1 -1
  15. package/dist/core/constants.js +1 -1
  16. package/dist/core/dev/dev.js +1 -1
  17. package/dist/core/endpoint/index.d.ts +2 -1
  18. package/dist/core/errors/dev/vite.js +5 -11
  19. package/dist/core/errors/overlay.js +2 -2
  20. package/dist/core/logger/vite.d.ts +0 -1
  21. package/dist/core/logger/vite.js +1 -2
  22. package/dist/core/messages.js +2 -2
  23. package/dist/core/render/context.d.ts +3 -2
  24. package/dist/core/render/context.js +1 -1
  25. package/dist/core/render/result.d.ts +2 -1
  26. package/dist/core/routing/manifest/create.d.ts +1 -1
  27. package/dist/core/routing/manifest/create.js +161 -118
  28. package/dist/core/sync/index.js +1 -1
  29. package/dist/i18n/index.d.ts +3 -2
  30. package/dist/i18n/index.js +4 -4
  31. package/dist/i18n/middleware.js +36 -19
  32. package/dist/prefetch/index.js +17 -1
  33. package/dist/prefetch/vite-plugin-prefetch.js +4 -1
  34. package/dist/runtime/client/dev-toolbar/apps/audit/a11y.js +52 -4
  35. package/dist/vite-plugin-astro/compile.js +0 -4
  36. package/dist/vite-plugin-astro/hmr.d.ts +3 -2
  37. package/dist/vite-plugin-astro/hmr.js +20 -41
  38. package/dist/vite-plugin-astro/index.js +24 -1
  39. package/dist/vite-plugin-astro/query.d.ts +0 -1
  40. package/dist/vite-plugin-astro/query.js +0 -5
  41. package/dist/vite-plugin-markdown/images.js +46 -19
  42. package/package.json +4 -3
@@ -1,11 +1,12 @@
1
1
  import type { AstroConfig, Locales } from '../@types/astro.js';
2
+ import type { RoutingStrategies } from '../core/config/schema.js';
2
3
  type GetLocaleRelativeUrl = GetLocaleOptions & {
3
4
  locale: string;
4
5
  base: string;
5
6
  locales: Locales;
6
7
  trailingSlash: AstroConfig['trailingSlash'];
7
8
  format: AstroConfig['build']['format'];
8
- routing?: 'prefix-always' | 'prefix-other-locales';
9
+ routing?: RoutingStrategies;
9
10
  defaultLocale: string;
10
11
  };
11
12
  export type GetLocaleOptions = {
@@ -39,7 +40,7 @@ type GetLocalesBaseUrl = GetLocaleOptions & {
39
40
  locales: Locales;
40
41
  trailingSlash: AstroConfig['trailingSlash'];
41
42
  format: AstroConfig['build']['format'];
42
- routing?: 'prefix-always' | 'prefix-other-locales';
43
+ routing?: RoutingStrategies;
43
44
  defaultLocale: string;
44
45
  };
45
46
  export declare function getLocaleRelativeUrlList({ base, locales: _locales, trailingSlash, format, path, prependWith, normalizeLocale, routing, defaultLocale, }: GetLocalesBaseUrl): string[];
@@ -11,7 +11,7 @@ function getLocaleRelativeUrl({
11
11
  path,
12
12
  prependWith,
13
13
  normalizeLocale = true,
14
- routing = "prefix-other-locales",
14
+ routing = "pathname-prefix-other-locales",
15
15
  defaultLocale
16
16
  }) {
17
17
  const codeToUse = peekCodePathToUse(_locales, locale);
@@ -23,7 +23,7 @@ function getLocaleRelativeUrl({
23
23
  }
24
24
  const pathsToJoin = [base, prependWith];
25
25
  const normalizedLocale = normalizeLocale ? normalizeTheLocale(codeToUse) : codeToUse;
26
- if (routing === "prefix-always") {
26
+ if (routing === "pathname-prefix-always") {
27
27
  pathsToJoin.push(normalizedLocale);
28
28
  } else if (locale !== defaultLocale) {
29
29
  pathsToJoin.push(normalizedLocale);
@@ -51,14 +51,14 @@ function getLocaleRelativeUrlList({
51
51
  path,
52
52
  prependWith,
53
53
  normalizeLocale = false,
54
- routing = "prefix-other-locales",
54
+ routing = "pathname-prefix-other-locales",
55
55
  defaultLocale
56
56
  }) {
57
57
  const locales = toPaths(_locales);
58
58
  return locales.map((locale) => {
59
59
  const pathsToJoin = [base, prependWith];
60
60
  const normalizedLocale = normalizeLocale ? normalizeTheLocale(locale) : locale;
61
- if (routing === "prefix-always") {
61
+ if (routing === "pathname-prefix-always") {
62
62
  pathsToJoin.push(normalizedLocale);
63
63
  } else if (locale !== defaultLocale) {
64
64
  pathsToJoin.push(normalizedLocale);
@@ -1,5 +1,6 @@
1
1
  import { appendForwardSlash, joinPaths } from "@astrojs/internal-helpers/path";
2
2
  import { getPathByLocale, normalizeTheLocale } from "./index.js";
3
+ import { shouldAppendForwardSlash } from "../core/build/util.js";
3
4
  const routeDataSymbol = Symbol.for("astro.routeData");
4
5
  function pathnameHasLocale(pathname, locales) {
5
6
  const segments = pathname.split("/");
@@ -35,25 +36,41 @@ function createI18nMiddleware(i18n, base, trailingSlash) {
35
36
  const response = await next();
36
37
  if (response instanceof Response) {
37
38
  const pathnameContainsDefaultLocale = url.pathname.includes(`/${defaultLocale}`);
38
- if (i18n.routing === "prefix-other-locales" && pathnameContainsDefaultLocale) {
39
- const newLocation = url.pathname.replace(`/${defaultLocale}`, "");
40
- response.headers.set("Location", newLocation);
41
- return new Response(null, {
42
- status: 404,
43
- headers: response.headers
44
- });
45
- } else if (i18n.routing === "prefix-always") {
46
- if (url.pathname === base + "/" || url.pathname === base) {
47
- if (trailingSlash === "always") {
48
- return context.redirect(`${appendForwardSlash(joinPaths(base, i18n.defaultLocale))}`);
49
- } else {
50
- return context.redirect(`${joinPaths(base, i18n.defaultLocale)}`);
39
+ switch (i18n.routing) {
40
+ case "pathname-prefix-other-locales": {
41
+ if (pathnameContainsDefaultLocale) {
42
+ const newLocation = url.pathname.replace(`/${defaultLocale}`, "");
43
+ response.headers.set("Location", newLocation);
44
+ return new Response(null, {
45
+ status: 404,
46
+ headers: response.headers
47
+ });
48
+ }
49
+ break;
50
+ }
51
+ case "pathname-prefix-always-no-redirect": {
52
+ const isRoot = url.pathname === base + "/" || url.pathname === base;
53
+ if (!(isRoot || pathnameHasLocale(url.pathname, i18n.locales))) {
54
+ return new Response(null, {
55
+ status: 404,
56
+ headers: response.headers
57
+ });
58
+ }
59
+ break;
60
+ }
61
+ case "pathname-prefix-always": {
62
+ if (url.pathname === base + "/" || url.pathname === base) {
63
+ if (trailingSlash === "always") {
64
+ return context.redirect(`${appendForwardSlash(joinPaths(base, i18n.defaultLocale))}`);
65
+ } else {
66
+ return context.redirect(`${joinPaths(base, i18n.defaultLocale)}`);
67
+ }
68
+ } else if (!pathnameHasLocale(url.pathname, i18n.locales)) {
69
+ return new Response(null, {
70
+ status: 404,
71
+ headers: response.headers
72
+ });
51
73
  }
52
- } else if (!pathnameHasLocale(url.pathname, i18n.locales)) {
53
- return new Response(null, {
54
- status: 404,
55
- headers: response.headers
56
- });
57
74
  }
58
75
  }
59
76
  if (response.status >= 300 && fallback) {
@@ -75,7 +92,7 @@ function createI18nMiddleware(i18n, base, trailingSlash) {
75
92
  const fallbackLocale = fallback[urlLocale];
76
93
  const pathFallbackLocale = getPathByLocale(fallbackLocale, locales);
77
94
  let newPathname;
78
- if (pathFallbackLocale === defaultLocale && routing === "prefix-other-locales") {
95
+ if (pathFallbackLocale === defaultLocale && routing === "pathname-prefix-other-locales") {
79
96
  newPathname = url.pathname.replace(`/${urlLocale}`, ``);
80
97
  } else {
81
98
  newPathname = url.pathname.replace(`/${urlLocale}`, `/${pathFallbackLocale}`);
@@ -4,6 +4,7 @@ const prefetchedUrls = /* @__PURE__ */ new Set();
4
4
  const listenedAnchors = /* @__PURE__ */ new WeakSet();
5
5
  let prefetchAll = __PREFETCH_PREFETCH_ALL__;
6
6
  let defaultStrategy = __PREFETCH_DEFAULT_STRATEGY__;
7
+ let clientPrerender = __EXPERIMENTAL_CLIENT_PRERENDER__;
7
8
  let inited = false;
8
9
  function init(defaultOpts) {
9
10
  if (!inBrowser)
@@ -128,7 +129,9 @@ function prefetch(url, opts) {
128
129
  prefetchedUrls.add(url);
129
130
  const priority = opts?.with ?? "link";
130
131
  debug?.(`[astro] Prefetching ${url} with ${priority}`);
131
- if (priority === "link") {
132
+ if (clientPrerender && HTMLScriptElement.supports && HTMLScriptElement.supports("speculationrules")) {
133
+ appendSpeculationRules(url);
134
+ } else if (priority === "link") {
132
135
  const link = document.createElement("link");
133
136
  link.rel = "prefetch";
134
137
  link.setAttribute("href", url);
@@ -188,6 +191,19 @@ function onPageLoad(cb) {
188
191
  cb();
189
192
  });
190
193
  }
194
+ function appendSpeculationRules(url) {
195
+ const script = document.createElement("script");
196
+ script.type = "speculationrules";
197
+ script.textContent = JSON.stringify({
198
+ prerender: [
199
+ {
200
+ source: "list",
201
+ urls: [url]
202
+ }
203
+ ]
204
+ });
205
+ document.head.append(script);
206
+ }
191
207
  export {
192
208
  init,
193
209
  prefetch
@@ -32,7 +32,10 @@ function astroPrefetch({ settings }) {
32
32
  },
33
33
  transform(code, id) {
34
34
  if (id.includes(prefetchInternalModuleFsSubpath)) {
35
- return code.replace("__PREFETCH_PREFETCH_ALL__", JSON.stringify(prefetch?.prefetchAll)).replace("__PREFETCH_DEFAULT_STRATEGY__", JSON.stringify(prefetch?.defaultStrategy));
35
+ return code.replace("__PREFETCH_PREFETCH_ALL__", JSON.stringify(prefetch?.prefetchAll)).replace("__PREFETCH_DEFAULT_STRATEGY__", JSON.stringify(prefetch?.defaultStrategy)).replace(
36
+ "__EXPERIMENTAL_CLIENT_PRERENDER__",
37
+ JSON.stringify(settings.config.experimental.clientPrerender)
38
+ );
36
39
  }
37
40
  }
38
41
  };
@@ -330,13 +330,61 @@ const a11y = [
330
330
  },
331
331
  {
332
332
  code: "a11y-missing-content",
333
- title: "Missing content on element important for accessibility",
334
- message: "Headings and anchors must have content to be accessible.",
333
+ title: "Missing content",
334
+ message: "Headings and anchors must have an accessible name, which can come from: inner text, aria-label, aria-labelledby, an img with alt property, or an svg with a tag <title></title>.",
335
335
  selector: a11y_required_content.join(","),
336
336
  match(element) {
337
337
  const innerText = element.innerText.trim();
338
- if (innerText === "")
339
- return true;
338
+ if (innerText !== "")
339
+ return false;
340
+ const ariaLabel = element.getAttribute("aria-label")?.trim();
341
+ if (ariaLabel && ariaLabel !== "")
342
+ return false;
343
+ const ariaLabelledby = element.getAttribute("aria-labelledby")?.trim();
344
+ if (ariaLabelledby) {
345
+ const ids = ariaLabelledby.split(" ");
346
+ for (const id of ids) {
347
+ const referencedElement = document.getElementById(id);
348
+ if (referencedElement && referencedElement.innerText.trim() !== "")
349
+ return false;
350
+ }
351
+ }
352
+ const imgElements = element.querySelectorAll("img");
353
+ for (const img of imgElements) {
354
+ const altAttribute = img.getAttribute("alt");
355
+ if (altAttribute && altAttribute.trim() !== "")
356
+ return false;
357
+ }
358
+ const svgElements = element.querySelectorAll("svg");
359
+ for (const svg of svgElements) {
360
+ const titleText = svg.querySelector("title");
361
+ if (titleText && titleText.textContent && titleText.textContent.trim() !== "")
362
+ return false;
363
+ }
364
+ const inputElements = element.querySelectorAll("input");
365
+ for (const input of inputElements) {
366
+ if (input.type === "image") {
367
+ const altAttribute = input.getAttribute("alt");
368
+ if (altAttribute && altAttribute.trim() !== "")
369
+ return false;
370
+ }
371
+ const inputAriaLabel = input.getAttribute("aria-label")?.trim();
372
+ if (inputAriaLabel && inputAriaLabel !== "")
373
+ return false;
374
+ const inputAriaLabelledby = input.getAttribute("aria-labelledby")?.trim();
375
+ if (inputAriaLabelledby) {
376
+ const ids = inputAriaLabelledby.split(" ");
377
+ for (const id of ids) {
378
+ const referencedElement = document.getElementById(id);
379
+ if (referencedElement && referencedElement.innerText.trim() !== "")
380
+ return false;
381
+ }
382
+ }
383
+ const title = input.getAttribute("title")?.trim();
384
+ if (title && title !== "")
385
+ return false;
386
+ }
387
+ return true;
340
388
  }
341
389
  },
342
390
  {
@@ -50,10 +50,6 @@ const $$url = ${JSON.stringify(
50
50
  i++;
51
51
  }
52
52
  }
53
- if (!compileProps.viteConfig.isProduction) {
54
- SUFFIX += `
55
- if (import.meta.hot) { import.meta.hot.decline() }`;
56
- }
57
53
  return {
58
54
  ...transformResult,
59
55
  code: esbuildResult.code + SUFFIX,
@@ -1,11 +1,12 @@
1
- import type { HmrContext, ModuleNode } from 'vite';
1
+ import type { HmrContext } from 'vite';
2
2
  import type { AstroConfig } from '../@types/astro.js';
3
3
  import type { cachedCompilation } from '../core/compile/index.js';
4
4
  import type { Logger } from '../core/logger/core.js';
5
5
  export interface HandleHotUpdateOptions {
6
6
  config: AstroConfig;
7
7
  logger: Logger;
8
+ astroFileToCssAstroDeps: Map<string, Set<string>>;
8
9
  compile: () => ReturnType<typeof cachedCompilation>;
9
10
  source: string;
10
11
  }
11
- export declare function handleHotUpdate(ctx: HmrContext, { config, logger, compile, source }: HandleHotUpdateOptions): Promise<ModuleNode[] | undefined>;
12
+ export declare function handleHotUpdate(ctx: HmrContext, { config, logger, astroFileToCssAstroDeps, compile, source }: HandleHotUpdateOptions): Promise<import("vite").ModuleNode[] | undefined>;
@@ -1,7 +1,7 @@
1
+ import path from "node:path";
2
+ import { appendForwardSlash } from "@astrojs/internal-helpers/path";
1
3
  import { invalidateCompilation, isCached } from "../core/compile/index.js";
2
- import { isAstroSrcFile } from "../core/logger/vite.js";
3
- import { isAstroScript } from "./query.js";
4
- async function handleHotUpdate(ctx, { config, logger, compile, source }) {
4
+ async function handleHotUpdate(ctx, { config, logger, astroFileToCssAstroDeps, compile, source }) {
5
5
  let isStyleOnlyChange = false;
6
6
  if (ctx.file.endsWith(".astro") && isCached(config, ctx.file)) {
7
7
  const oldResult = await compile();
@@ -15,51 +15,27 @@ async function handleHotUpdate(ctx, { config, logger, compile, source }) {
15
15
  } else {
16
16
  invalidateCompilation(config, ctx.file);
17
17
  }
18
- if (isAstroSrcFile(ctx.file)) {
19
- return;
20
- }
21
- const filtered = new Set(ctx.modules);
22
- const files = /* @__PURE__ */ new Set();
23
- for (const mod of ctx.modules) {
24
- if (isAstroSrcFile(mod.id ?? mod.file)) {
25
- filtered.delete(mod);
26
- continue;
27
- }
28
- if (mod.file && isCached(config, mod.file)) {
29
- filtered.add(mod);
30
- files.add(mod.file);
31
- }
32
- for (const imp of mod.importers) {
33
- if (imp.file && isCached(config, imp.file)) {
34
- filtered.add(imp);
35
- files.add(imp.file);
36
- }
37
- }
38
- }
39
- for (const file of files) {
40
- if (isStyleOnlyChange && file === ctx.file)
41
- continue;
42
- invalidateCompilation(config, file);
43
- if (file.endsWith(".astro")) {
44
- ctx.server.moduleGraph.onFileChange(file);
45
- }
46
- }
47
- const mods = [...filtered].filter((m) => !m.url.endsWith("="));
48
18
  if (isStyleOnlyChange) {
49
19
  logger.debug("watch", "style-only change");
50
- return mods.filter((mod) => mod.id?.includes("astro&type=style"));
51
- }
52
- for (const mod of mods) {
53
- for (const imp of mod.importedModules) {
54
- if (imp.id && isAstroScript(imp.id)) {
55
- mods.push(imp);
20
+ return ctx.modules.filter((mod) => mod.id?.includes("astro&type=style"));
21
+ }
22
+ for (const [astroFile, cssAstroDeps] of astroFileToCssAstroDeps) {
23
+ if (cssAstroDeps.has(ctx.file)) {
24
+ logger.info("watch", getShortName(ctx.file, ctx.server.config.root));
25
+ const parentModules = ctx.server.moduleGraph.getModulesByFile(astroFile);
26
+ if (parentModules) {
27
+ for (const mod of parentModules) {
28
+ ctx.server.moduleGraph.invalidateModule(mod);
29
+ }
56
30
  }
31
+ ctx.server.ws.send({ type: "full-reload", path: "*" });
57
32
  }
58
33
  }
59
- return mods;
60
34
  }
61
35
  function isStyleOnlyChanged(oldResult, newResult) {
62
- return normalizeCode(oldResult.code) === normalizeCode(newResult.code) && !isArrayEqual(oldResult.css, newResult.css);
36
+ return normalizeCode(oldResult.code) === normalizeCode(newResult.code) && // If style tags are added/removed, we need to regenerate the main Astro file
37
+ // so that its CSS imports are also added/removed
38
+ oldResult.css.length === newResult.css.length && !isArrayEqual(oldResult.css, newResult.css);
63
39
  }
64
40
  const astroStyleImportRE = /import\s*"[^"]+astro&type=style[^"]+";/g;
65
41
  const sourceMappingUrlRE = /\/\/# sourceMappingURL=[^ ]+$/gm;
@@ -77,6 +53,9 @@ function isArrayEqual(a, b) {
77
53
  }
78
54
  return true;
79
55
  }
56
+ function getShortName(file, root) {
57
+ return file.startsWith(appendForwardSlash(root)) ? path.posix.relative(root, file) : file;
58
+ }
80
59
  export {
81
60
  handleHotUpdate
82
61
  };
@@ -1,7 +1,8 @@
1
1
  import { normalizePath } from "vite";
2
2
  import {
3
3
  cachedCompilation,
4
- getCachedCompileResult
4
+ getCachedCompileResult,
5
+ invalidateCompilation
5
6
  } from "../core/compile/index.js";
6
7
  import { isRelativePath } from "../core/path.js";
7
8
  import { normalizeFilename } from "../vite-plugin-utils/index.js";
@@ -13,6 +14,7 @@ function astro({ settings, logger }) {
13
14
  const { config } = settings;
14
15
  let resolvedConfig;
15
16
  let server;
17
+ let astroFileToCssAstroDeps = /* @__PURE__ */ new Map();
16
18
  const srcRootWeb = config.srcDir.pathname.slice(config.root.pathname.length - 1);
17
19
  const isBrowserPath = (path) => path.startsWith(srcRootWeb) && srcRootWeb !== "/";
18
20
  const prePlugin = {
@@ -25,6 +27,9 @@ function astro({ settings, logger }) {
25
27
  configureServer(_server) {
26
28
  server = _server;
27
29
  },
30
+ buildStart() {
31
+ astroFileToCssAstroDeps = /* @__PURE__ */ new Map();
32
+ },
28
33
  async load(id, opts) {
29
34
  const parsedId = parseAstroRequest(id);
30
35
  const query = parsedId.query;
@@ -127,10 +132,27 @@ File: ${id}`
127
132
  filename: normalizePath(parsedId.filename),
128
133
  source
129
134
  };
135
+ invalidateCompilation(config, compileProps.filename);
130
136
  const transformResult = await cachedFullCompilation({ compileProps, logger });
137
+ const astroDeps = /* @__PURE__ */ new Set();
131
138
  for (const dep of transformResult.cssDeps) {
139
+ if (dep.endsWith(".astro")) {
140
+ astroDeps.add(dep);
141
+ }
132
142
  this.addWatchFile(dep);
133
143
  }
144
+ astroFileToCssAstroDeps.set(id, astroDeps);
145
+ if (server) {
146
+ const mods = server.moduleGraph.getModulesByFile(compileProps.filename);
147
+ if (mods) {
148
+ const seen = new Set(mods);
149
+ for (const mod of mods) {
150
+ if (mod.url.includes("?astro")) {
151
+ server.moduleGraph.invalidateModule(mod, seen);
152
+ }
153
+ }
154
+ }
155
+ }
134
156
  const astroMetadata = {
135
157
  clientOnlyComponents: transformResult.clientOnlyComponents,
136
158
  hydratedComponents: transformResult.hydratedComponents,
@@ -167,6 +189,7 @@ File: ${id}`
167
189
  return handleHotUpdate(context, {
168
190
  config,
169
191
  logger,
192
+ astroFileToCssAstroDeps,
170
193
  compile,
171
194
  source
172
195
  });
@@ -11,4 +11,3 @@ export interface ParsedRequestResult {
11
11
  query: AstroQuery;
12
12
  }
13
13
  export declare function parseAstroRequest(id: string): ParsedRequestResult;
14
- export declare function isAstroScript(id: string): boolean;
@@ -18,11 +18,6 @@ function parseAstroRequest(id) {
18
18
  query
19
19
  };
20
20
  }
21
- function isAstroScript(id) {
22
- const parsed = parseAstroRequest(id);
23
- return parsed.query.type === "script";
24
- }
25
21
  export {
26
- isAstroScript,
27
22
  parseAstroRequest
28
23
  };
@@ -1,30 +1,57 @@
1
1
  function getMarkdownCodeForImages(imagePaths, html) {
2
2
  return `
3
- import { getImage } from "astro:assets";
4
- ${imagePaths.map((entry) => `import Astro__${entry.safeName} from ${JSON.stringify(entry.raw)};`).join("\n")}
3
+ import { getImage } from "astro:assets";
4
+ ${imagePaths.map((entry) => `import Astro__${entry.safeName} from ${JSON.stringify(entry.raw)};`).join("\n")}
5
5
 
6
- const images = async function() {
7
- return {
8
- ${imagePaths.map((entry) => `"${entry.raw}": await getImage({src: Astro__${entry.safeName}})`).join(",\n")}
9
- }
10
- }
6
+ const images = async function(html) {
7
+ const imageSources = {};
8
+ ${imagePaths.map((entry) => {
9
+ const rawUrl = JSON.stringify(entry.raw);
10
+ return `{
11
+ const regex = new RegExp('__ASTRO_IMAGE_="([^"]*' + ${rawUrl} + '[^"]*)"', 'g');
12
+ let match;
13
+ let occurrenceCounter = 0;
14
+ while ((match = regex.exec(html)) !== null) {
15
+ const matchKey = ${rawUrl} + '_' + occurrenceCounter;
16
+ const imageProps = JSON.parse(match[1].replace(/&#x22;/g, '"'));
17
+ const { src, ...props } = imageProps;
18
+
19
+ imageSources[matchKey] = await getImage({src: Astro__${entry.safeName}, ...props});
20
+ occurrenceCounter++;
21
+ }
22
+ }`;
23
+ }).join("\n")}
24
+ return imageSources;
25
+ };
11
26
 
12
27
  async function updateImageReferences(html) {
13
- return images().then((images) => {
14
- return html.replaceAll(/__ASTRO_IMAGE_="([^"]+)"/gm, (full, imagePath) =>
15
- spreadAttributes({
16
- src: images[imagePath].src,
17
- ...images[imagePath].attributes,
18
- })
19
- );
28
+ return images(html).then((imageSources) => {
29
+ return html.replaceAll(/__ASTRO_IMAGE_="([^"]+)"/gm, (full, imagePath) => {
30
+ const decodedImagePath = JSON.parse(imagePath.replace(/&#x22;/g, '"'));
31
+
32
+ // Use the 'index' property for each image occurrence
33
+ const srcKey = decodedImagePath.src + '_' + decodedImagePath.index;
34
+
35
+ if (imageSources[srcKey].srcSet && imageSources[srcKey].srcSet.values.length > 0) {
36
+ imageSources[srcKey].attributes.srcset = imageSources[srcKey].srcSet.attribute;
37
+ }
38
+
39
+ const { index, ...attributesWithoutIndex } = imageSources[srcKey].attributes;
40
+
41
+ return spreadAttributes({
42
+ src: imageSources[srcKey].src,
43
+ ...attributesWithoutIndex,
44
+ });
45
+ });
20
46
  });
21
- }
47
+ }
48
+
22
49
 
23
- // NOTE: This causes a top-level await to appear in the user's code, which can break very easily due to a Rollup
24
- // bug and certain adapters not supporting it correctly. See: https://github.com/rollup/rollup/issues/4708
25
- // Tread carefully!
50
+ // NOTE: This causes a top-level await to appear in the user's code, which can break very easily due to a Rollup
51
+ // bug and certain adapters not supporting it correctly. See: https://github.com/rollup/rollup/issues/4708
52
+ // Tread carefully!
26
53
  const html = await updateImageReferences(${JSON.stringify(html)});
27
- `;
54
+ `;
28
55
  }
29
56
  export {
30
57
  getMarkdownCodeForImages
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "4.1.3",
3
+ "version": "4.2.0",
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",
@@ -150,7 +150,7 @@
150
150
  "resolve": "^1.22.4",
151
151
  "semver": "^7.5.4",
152
152
  "server-destroy": "^1.0.1",
153
- "shikiji": "^0.6.13",
153
+ "shikiji": "^0.9.18",
154
154
  "string-width": "^7.0.0",
155
155
  "strip-ansi": "^7.1.0",
156
156
  "tsconfck": "^3.0.0",
@@ -162,7 +162,7 @@
162
162
  "yargs-parser": "^21.1.1",
163
163
  "zod": "^3.22.4",
164
164
  "@astrojs/internal-helpers": "0.2.1",
165
- "@astrojs/markdown-remark": "4.0.1",
165
+ "@astrojs/markdown-remark": "4.1.0",
166
166
  "@astrojs/telemetry": "3.0.4"
167
167
  },
168
168
  "optionalDependencies": {
@@ -210,6 +210,7 @@
210
210
  "remark-code-titles": "^0.1.2",
211
211
  "rollup": "^4.5.0",
212
212
  "sass": "^1.69.5",
213
+ "shikiji-core": "^0.9.18",
213
214
  "srcset-parse": "^1.1.0",
214
215
  "unified": "^11.0.4",
215
216
  "astro-scripts": "0.0.14"