astro 4.7.0 → 4.8.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 (235) hide show
  1. package/client.d.ts +1 -0
  2. package/content-module.template.mjs +2 -0
  3. package/dist/@types/astro.d.ts +220 -4
  4. package/dist/actions/consts.d.ts +3 -0
  5. package/dist/actions/consts.js +8 -0
  6. package/dist/actions/index.d.ts +2 -0
  7. package/dist/actions/index.js +72 -0
  8. package/dist/actions/runtime/middleware.d.ts +7 -0
  9. package/dist/actions/runtime/middleware.js +38 -0
  10. package/dist/actions/runtime/route.d.ts +2 -0
  11. package/dist/actions/runtime/route.js +37 -0
  12. package/dist/actions/runtime/store.d.ts +6 -0
  13. package/dist/actions/runtime/store.js +18 -0
  14. package/dist/actions/runtime/utils.d.ts +4 -0
  15. package/dist/actions/runtime/utils.js +23 -0
  16. package/dist/actions/runtime/virtual/client.d.ts +4 -0
  17. package/dist/actions/runtime/virtual/client.js +20 -0
  18. package/dist/actions/runtime/virtual/server.d.ts +21 -0
  19. package/dist/actions/runtime/virtual/server.js +98 -0
  20. package/dist/actions/runtime/virtual/shared.d.ts +37 -0
  21. package/dist/actions/runtime/virtual/shared.js +104 -0
  22. package/dist/actions/utils.d.ts +2 -0
  23. package/dist/actions/utils.js +18 -0
  24. package/dist/assets/build/generate.js +1 -1
  25. package/dist/assets/internal.js +1 -2
  26. package/dist/assets/services/service.js +2 -4
  27. package/dist/assets/services/sharp.js +2 -4
  28. package/dist/assets/services/squoosh.js +2 -4
  29. package/dist/assets/services/vendor/squoosh/avif/avif_node_dec.js +34 -68
  30. package/dist/assets/services/vendor/squoosh/avif/avif_node_enc.js +39 -78
  31. package/dist/assets/services/vendor/squoosh/mozjpeg/mozjpeg_node_dec.js +32 -64
  32. package/dist/assets/services/vendor/squoosh/mozjpeg/mozjpeg_node_enc.js +32 -64
  33. package/dist/assets/services/vendor/squoosh/png/squoosh_png.js +2 -4
  34. package/dist/assets/services/vendor/squoosh/webp/webp_node_dec.js +29 -58
  35. package/dist/assets/services/vendor/squoosh/webp/webp_node_enc.js +29 -58
  36. package/dist/assets/utils/getAssetsPrefix.js +2 -4
  37. package/dist/assets/utils/remotePattern.js +1 -2
  38. package/dist/assets/utils/remoteProbe.js +1 -2
  39. package/dist/assets/utils/vendor/image-size/types/cur.js +1 -2
  40. package/dist/assets/utils/vendor/image-size/types/icns.js +1 -2
  41. package/dist/assets/utils/vendor/image-size/types/ico.js +2 -4
  42. package/dist/assets/utils/vendor/image-size/types/jp2.js +2 -4
  43. package/dist/assets/utils/vendor/image-size/types/utils.js +4 -8
  44. package/dist/assets/vite-plugin-assets.js +1 -1
  45. package/dist/cli/add/babel.d.ts +1 -1
  46. package/dist/cli/add/imports.js +4 -8
  47. package/dist/cli/add/index.js +23 -46
  48. package/dist/cli/add/wrapper.js +1 -2
  49. package/dist/cli/index.js +1 -2
  50. package/dist/cli/info/index.js +1 -2
  51. package/dist/cli/install-package.js +3 -6
  52. package/dist/cli/throw-and-exit.js +1 -2
  53. package/dist/config/index.d.ts +2 -2
  54. package/dist/config/index.js +3 -3
  55. package/dist/content/index.d.ts +1 -2
  56. package/dist/content/index.js +1 -9
  57. package/dist/content/runtime.d.ts +2 -1
  58. package/dist/content/runtime.js +11 -20
  59. package/dist/content/server-listeners.js +5 -10
  60. package/dist/content/types-generator.js +5 -10
  61. package/dist/content/utils.d.ts +0 -4
  62. package/dist/content/utils.js +5 -18
  63. package/dist/content/vite-plugin-content-assets.d.ts +1 -1
  64. package/dist/content/vite-plugin-content-assets.js +14 -47
  65. package/dist/content/vite-plugin-content-imports.js +6 -11
  66. package/dist/content/vite-plugin-content-virtual-mod.js +7 -14
  67. package/dist/core/app/index.js +11 -46
  68. package/dist/core/app/node.js +4 -3
  69. package/dist/core/app/pipeline.d.ts +7 -2
  70. package/dist/core/app/pipeline.js +70 -2
  71. package/dist/core/app/types.d.ts +1 -0
  72. package/dist/core/base-pipeline.d.ts +16 -1
  73. package/dist/core/build/generate.js +16 -62
  74. package/dist/core/build/index.js +3 -5
  75. package/dist/core/build/internal.d.ts +39 -9
  76. package/dist/core/build/internal.js +43 -54
  77. package/dist/core/build/page-data.js +6 -6
  78. package/dist/core/build/pipeline.d.ts +7 -3
  79. package/dist/core/build/pipeline.js +134 -23
  80. package/dist/core/build/plugins/plugin-analyzer.js +11 -32
  81. package/dist/core/build/plugins/plugin-content.d.ts +1 -0
  82. package/dist/core/build/plugins/plugin-content.js +34 -32
  83. package/dist/core/build/plugins/plugin-css.js +23 -51
  84. package/dist/core/build/plugins/plugin-manifest.js +8 -9
  85. package/dist/core/build/plugins/plugin-pages.d.ts +0 -1
  86. package/dist/core/build/plugins/plugin-pages.js +10 -12
  87. package/dist/core/build/plugins/plugin-ssr.js +16 -14
  88. package/dist/core/build/plugins/util.d.ts +26 -11
  89. package/dist/core/build/plugins/util.js +22 -6
  90. package/dist/core/build/static-build.js +31 -26
  91. package/dist/core/build/types.d.ts +6 -6
  92. package/dist/core/client-directive/build.js +1 -2
  93. package/dist/core/compile/compile.js +1 -1
  94. package/dist/core/config/config.js +2 -7
  95. package/dist/core/config/logging.js +1 -2
  96. package/dist/core/config/schema.d.ts +92 -60
  97. package/dist/core/config/schema.js +6 -2
  98. package/dist/core/config/settings.js +1 -2
  99. package/dist/core/config/timer.js +4 -8
  100. package/dist/core/constants.d.ts +1 -1
  101. package/dist/core/constants.js +1 -1
  102. package/dist/core/cookies/cookies.js +3 -6
  103. package/dist/core/dev/container.js +1 -1
  104. package/dist/core/dev/dev.js +1 -1
  105. package/dist/core/dev/restart.js +1 -2
  106. package/dist/core/errors/errors-data.d.ts +25 -1
  107. package/dist/core/errors/errors-data.js +15 -4
  108. package/dist/core/errors/errors.js +1 -2
  109. package/dist/core/errors/index.d.ts +1 -0
  110. package/dist/core/errors/index.js +2 -0
  111. package/dist/core/errors/overlay.js +3 -4
  112. package/dist/core/errors/printer.js +2 -4
  113. package/dist/{content/error-map.js → core/errors/zod-error-map.js} +2 -4
  114. package/dist/core/fs/index.js +2 -4
  115. package/dist/core/logger/vite.js +9 -18
  116. package/dist/core/messages.js +2 -2
  117. package/dist/core/middleware/callMiddleware.d.ts +3 -2
  118. package/dist/core/middleware/callMiddleware.js +13 -3
  119. package/dist/core/middleware/index.js +12 -8
  120. package/dist/core/middleware/sequence.js +22 -4
  121. package/dist/core/module-loader/vite.js +1 -2
  122. package/dist/core/preview/index.js +1 -1
  123. package/dist/core/render/params-and-props.js +2 -4
  124. package/dist/core/render/slots.js +4 -8
  125. package/dist/core/render-context.d.ts +15 -5
  126. package/dist/core/render-context.js +134 -28
  127. package/dist/core/request.js +1 -2
  128. package/dist/core/routing/manifest/create.js +3 -6
  129. package/dist/core/routing/priority.d.ts +1 -1
  130. package/dist/core/sync/index.js +11 -4
  131. package/dist/core/util.d.ts +2 -0
  132. package/dist/core/util.js +18 -19
  133. package/dist/i18n/index.js +2 -4
  134. package/dist/i18n/middleware.js +1 -2
  135. package/dist/i18n/utils.js +1 -2
  136. package/dist/i18n/vite-plugin-i18n.js +1 -2
  137. package/dist/integrations/{index.js → hooks.js} +6 -2
  138. package/dist/jsx/babel.d.ts +3 -0
  139. package/dist/jsx/babel.js +9 -18
  140. package/dist/jsx/rehype.d.ts +11 -0
  141. package/dist/jsx/rehype.js +197 -0
  142. package/dist/jsx/server.js +20 -14
  143. package/dist/jsx/transform-options.d.ts +3 -0
  144. package/dist/jsx-runtime/index.js +8 -16
  145. package/dist/preferences/index.js +3 -6
  146. package/dist/preferences/store.js +3 -6
  147. package/dist/prefetch/index.js +8 -16
  148. package/dist/prefetch/vite-plugin-prefetch.js +2 -4
  149. package/dist/prerender/metadata.js +1 -2
  150. package/dist/prerender/routing.js +1 -1
  151. package/dist/prerender/utils.d.ts +1 -2
  152. package/dist/prerender/utils.js +2 -5
  153. package/dist/runtime/client/dev-toolbar/apps/astro.js +5 -10
  154. package/dist/runtime/client/dev-toolbar/apps/audit/index.js +5 -10
  155. package/dist/runtime/client/dev-toolbar/apps/audit/rules/a11y.js +45 -90
  156. package/dist/runtime/client/dev-toolbar/apps/audit/rules/index.js +1 -2
  157. package/dist/runtime/client/dev-toolbar/apps/audit/rules/perf.js +16 -32
  158. package/dist/runtime/client/dev-toolbar/apps/audit/ui/audit-list-window.js +2 -4
  159. package/dist/runtime/client/dev-toolbar/apps/audit/ui/audit-ui.js +3 -6
  160. package/dist/runtime/client/dev-toolbar/apps/utils/highlight.js +1 -2
  161. package/dist/runtime/client/dev-toolbar/apps/utils/window.js +4 -8
  162. package/dist/runtime/client/dev-toolbar/entrypoint.js +8 -11
  163. package/dist/runtime/client/dev-toolbar/helpers.d.ts +1 -1
  164. package/dist/runtime/client/dev-toolbar/helpers.js +3 -5
  165. package/dist/runtime/client/dev-toolbar/toolbar.js +11 -22
  166. package/dist/runtime/client/dev-toolbar/ui-library/badge.js +1 -2
  167. package/dist/runtime/client/dev-toolbar/ui-library/button.d.ts +5 -0
  168. package/dist/runtime/client/dev-toolbar/ui-library/button.js +26 -5
  169. package/dist/runtime/client/dev-toolbar/ui-library/index.d.ts +1 -0
  170. package/dist/runtime/client/dev-toolbar/ui-library/index.js +2 -0
  171. package/dist/runtime/client/dev-toolbar/ui-library/radio-checkbox.d.ts +13 -0
  172. package/dist/runtime/client/dev-toolbar/ui-library/radio-checkbox.js +109 -0
  173. package/dist/runtime/client/visible.js +1 -2
  174. package/dist/runtime/server/astro-component.js +2 -4
  175. package/dist/runtime/server/astro-global.js +1 -1
  176. package/dist/runtime/server/astro-island.js +7 -14
  177. package/dist/runtime/server/astro-island.prebuilt-dev.d.ts +1 -1
  178. package/dist/runtime/server/astro-island.prebuilt-dev.js +1 -1
  179. package/dist/runtime/server/astro-island.prebuilt.d.ts +1 -1
  180. package/dist/runtime/server/astro-island.prebuilt.js +1 -1
  181. package/dist/runtime/server/index.js +3 -6
  182. package/dist/runtime/server/jsx.js +1 -2
  183. package/dist/runtime/server/render/any.js +1 -2
  184. package/dist/runtime/server/render/astro/instance.js +11 -12
  185. package/dist/runtime/server/render/astro/render.js +5 -10
  186. package/dist/runtime/server/render/component.js +6 -11
  187. package/dist/runtime/server/render/dom.js +1 -2
  188. package/dist/runtime/server/render/page.js +1 -2
  189. package/dist/runtime/server/render/script.js +1 -2
  190. package/dist/runtime/server/render/slot.js +1 -2
  191. package/dist/runtime/server/render/tags.js +2 -4
  192. package/dist/runtime/server/render/util.js +5 -5
  193. package/dist/runtime/server/shorthash.js +1 -2
  194. package/dist/runtime/server/transition.js +4 -8
  195. package/dist/runtime/server/util.js +1 -2
  196. package/dist/transitions/events.d.ts +6 -5
  197. package/dist/transitions/events.js +17 -10
  198. package/dist/transitions/router.js +100 -132
  199. package/dist/transitions/swap-functions.d.ts +12 -0
  200. package/dist/transitions/swap-functions.js +105 -0
  201. package/dist/vite-plugin-astro/compile.js +1 -2
  202. package/dist/vite-plugin-astro/hmr.js +5 -10
  203. package/dist/vite-plugin-astro/index.js +2 -4
  204. package/dist/vite-plugin-astro-server/pipeline.d.ts +8 -3
  205. package/dist/vite-plugin-astro-server/pipeline.js +59 -11
  206. package/dist/vite-plugin-astro-server/plugin.js +6 -6
  207. package/dist/vite-plugin-astro-server/response.js +1 -2
  208. package/dist/vite-plugin-astro-server/route.js +36 -42
  209. package/dist/vite-plugin-astro-server/vite.js +5 -7
  210. package/dist/vite-plugin-config-alias/index.js +7 -14
  211. package/dist/vite-plugin-head/index.js +3 -6
  212. package/dist/vite-plugin-html/index.js +1 -2
  213. package/dist/vite-plugin-html/transform/escape.js +2 -4
  214. package/dist/vite-plugin-html/transform/slots.js +1 -2
  215. package/dist/vite-plugin-html/transform/utils.js +1 -2
  216. package/dist/vite-plugin-inject-env-ts/index.js +37 -11
  217. package/dist/vite-plugin-integrations-container/index.js +4 -7
  218. package/dist/vite-plugin-load-fallback/index.js +1 -2
  219. package/dist/vite-plugin-markdown/index.js +1 -2
  220. package/dist/vite-plugin-mdx/index.d.ts +3 -0
  221. package/dist/vite-plugin-mdx/tag.d.ts +2 -0
  222. package/dist/vite-plugin-mdx/tag.js +3 -6
  223. package/dist/vite-plugin-mdx/transform-jsx.d.ts +3 -0
  224. package/dist/vite-plugin-mdx/transform-jsx.js +1 -2
  225. package/dist/vite-plugin-scanner/index.js +4 -6
  226. package/dist/vite-plugin-scanner/scan.js +2 -4
  227. package/dist/vite-plugin-scripts/page-ssr.js +3 -6
  228. package/package.json +26 -22
  229. package/templates/actions.mjs +61 -0
  230. package/types/actions.d.ts +3 -0
  231. package/types/content.d.ts +2 -2
  232. /package/dist/{content/error-map.d.ts → core/errors/zod-error-map.d.ts} +0 -0
  233. /package/dist/integrations/{astroFeaturesValidation.d.ts → features-validation.d.ts} +0 -0
  234. /package/dist/integrations/{astroFeaturesValidation.js → features-validation.js} +0 -0
  235. /package/dist/integrations/{index.d.ts → hooks.d.ts} +0 -0
@@ -35,8 +35,7 @@ async function renderSlotToString(result, slotted, fallback) {
35
35
  instructions ??= [];
36
36
  instructions.push(...chunk.instructions);
37
37
  }
38
- } else if (chunk instanceof Response)
39
- return;
38
+ } else if (chunk instanceof Response) return;
40
39
  else if (typeof chunk === "object" && "type" in chunk && typeof chunk.type === "string") {
41
40
  if (instructions === null) {
42
41
  instructions = [];
@@ -7,13 +7,11 @@ function renderScriptElement({ props, children }) {
7
7
  }
8
8
  function renderUniqueStylesheet(result, sheet) {
9
9
  if (sheet.type === "external") {
10
- if (Array.from(result.styles).some((s) => s.props.href === sheet.src))
11
- return "";
10
+ if (Array.from(result.styles).some((s) => s.props.href === sheet.src)) return "";
12
11
  return renderElement("link", { props: { rel: "stylesheet", href: sheet.src }, children: "" });
13
12
  }
14
13
  if (sheet.type === "inline") {
15
- if (Array.from(result.styles).some((s) => s.children.includes(sheet.content)))
16
- return "";
14
+ if (Array.from(result.styles).some((s) => s.children.includes(sheet.content))) return "";
17
15
  return renderElement("style", { props: {}, children: sheet.content });
18
16
  }
19
17
  }
@@ -4,17 +4,17 @@ const voidElementNames = /^(area|base|br|col|command|embed|hr|img|input|keygen|l
4
4
  const htmlBooleanAttributes = /^(?:allowfullscreen|async|autofocus|autoplay|controls|default|defer|disabled|disablepictureinpicture|disableremoteplayback|formnovalidate|hidden|loop|nomodule|novalidate|open|playsinline|readonly|required|reversed|scoped|seamless|itemscope)$/i;
5
5
  const htmlEnumAttributes = /^(?:contenteditable|draggable|spellcheck|value)$/i;
6
6
  const svgEnumAttributes = /^(?:autoReverse|externalResourcesRequired|focusable|preserveAlpha)$/i;
7
+ const AMPERSAND_REGEX = /&/g;
8
+ const DOUBLE_QUOTE_REGEX = /"/g;
7
9
  const STATIC_DIRECTIVES = /* @__PURE__ */ new Set(["set:html", "set:text"]);
8
10
  const toIdent = (k) => k.trim().replace(/(?!^)\b\w|\s+|\W+/g, (match, index) => {
9
- if (/\W/.test(match))
10
- return "";
11
+ if (/\W/.test(match)) return "";
11
12
  return index === 0 ? match : match.toUpperCase();
12
13
  });
13
- const toAttributeString = (value, shouldEscape = true) => shouldEscape ? String(value).replace(/&/g, "&").replace(/"/g, """) : value;
14
+ const toAttributeString = (value, shouldEscape = true) => shouldEscape ? String(value).replace(AMPERSAND_REGEX, "&").replace(DOUBLE_QUOTE_REGEX, """) : value;
14
15
  const kebab = (k) => k.toLowerCase() === k ? k : k.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
15
16
  const toStyleString = (obj) => Object.entries(obj).filter(([_, v]) => typeof v === "string" && v.trim() || typeof v === "number").map(([k, v]) => {
16
- if (k[0] !== "-" && k[1] !== "-")
17
- return `${kebab(k)}:${v}`;
17
+ if (k[0] !== "-" && k[1] !== "-") return `${kebab(k)}:${v}`;
18
18
  return `${k}:${v}`;
19
19
  }).join(";");
20
20
  function defineScriptVars(vars) {
@@ -32,8 +32,7 @@ const dictionary = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWX
32
32
  const binary = dictionary.length;
33
33
  function bitwise(str) {
34
34
  let hash = 0;
35
- if (str.length === 0)
36
- return hash;
35
+ if (str.length === 0) return hash;
37
36
  for (let i = 0; i < str.length; i++) {
38
37
  const ch = str.charCodeAt(i);
39
38
  hash = (hash << 5) - hash + ch;
@@ -15,12 +15,9 @@ function createTransitionScope(result, hash) {
15
15
  return `astro-${hash}-${num}`;
16
16
  }
17
17
  const getAnimations = (name) => {
18
- if (name === "fade")
19
- return fade();
20
- if (name === "slide")
21
- return slide();
22
- if (typeof name === "object")
23
- return name;
18
+ if (name === "fade") return fade();
19
+ if (name === "slide") return slide();
20
+ if (typeof name === "object") return name;
24
21
  };
25
22
  const addPairs = (animations, stylesheet) => {
26
23
  for (const [direction, images] of Object.entries(animations)) {
@@ -46,8 +43,7 @@ function renderTransition(result, hash, animationName, transitionName) {
46
43
  if (typeof (transitionName ?? "") !== "string") {
47
44
  throw new Error(`Invalid transition name {${transitionName}}`);
48
45
  }
49
- if (!animationName)
50
- animationName = "fade";
46
+ if (!animationName) animationName = "fade";
51
47
  const scope = createTransitionScope(result, hash);
52
48
  const name = transitionName ? cssesc(reEncode(transitionName), { isIdentifier: true }) : scope;
53
49
  const sheet = new ViewTransitionStyleSheet(scope, name);
@@ -6,8 +6,7 @@ async function* streamAsyncIterator(stream) {
6
6
  try {
7
7
  while (true) {
8
8
  const { done, value } = await reader.read();
9
- if (done)
10
- return;
9
+ if (done) return;
11
10
  yield value;
12
11
  }
13
12
  } finally {
@@ -16,21 +16,22 @@ declare class BeforeEvent extends Event {
16
16
  readonly sourceElement: Element | undefined;
17
17
  readonly info: any;
18
18
  newDocument: Document;
19
- constructor(type: string, eventInitDict: EventInit | undefined, from: URL, to: URL, direction: Direction | string, navigationType: NavigationTypeString, sourceElement: Element | undefined, info: any, newDocument: Document);
19
+ readonly signal: AbortSignal;
20
+ constructor(type: string, eventInitDict: EventInit | undefined, from: URL, to: URL, direction: Direction | string, navigationType: NavigationTypeString, sourceElement: Element | undefined, info: any, newDocument: Document, signal: AbortSignal);
20
21
  }
21
22
  export declare const isTransitionBeforePreparationEvent: (value: any) => value is TransitionBeforePreparationEvent;
22
23
  export declare class TransitionBeforePreparationEvent extends BeforeEvent {
23
24
  formData: FormData | undefined;
24
25
  loader: () => Promise<void>;
25
- constructor(from: URL, to: URL, direction: Direction | string, navigationType: NavigationTypeString, sourceElement: Element | undefined, info: any, newDocument: Document, formData: FormData | undefined, loader: (event: TransitionBeforePreparationEvent) => Promise<void>);
26
+ constructor(from: URL, to: URL, direction: Direction | string, navigationType: NavigationTypeString, sourceElement: Element | undefined, info: any, newDocument: Document, signal: AbortSignal, formData: FormData | undefined, loader: (event: TransitionBeforePreparationEvent) => Promise<void>);
26
27
  }
27
28
  export declare const isTransitionBeforeSwapEvent: (value: any) => value is TransitionBeforeSwapEvent;
28
29
  export declare class TransitionBeforeSwapEvent extends BeforeEvent {
29
30
  readonly direction: Direction | string;
30
31
  readonly viewTransition: ViewTransition;
31
32
  swap: () => void;
32
- constructor(afterPreparation: BeforeEvent, viewTransition: ViewTransition, swap: (event: TransitionBeforeSwapEvent) => void);
33
+ constructor(afterPreparation: BeforeEvent, viewTransition: ViewTransition);
33
34
  }
34
- export declare function doPreparation(from: URL, to: URL, direction: Direction | string, navigationType: NavigationTypeString, sourceElement: Element | undefined, info: any, formData: FormData | undefined, defaultLoader: (event: TransitionBeforePreparationEvent) => Promise<void>): Promise<TransitionBeforePreparationEvent>;
35
- export declare function doSwap(afterPreparation: BeforeEvent, viewTransition: ViewTransition, defaultSwap: (event: TransitionBeforeSwapEvent) => void): Promise<TransitionBeforeSwapEvent>;
35
+ export declare function doPreparation(from: URL, to: URL, direction: Direction | string, navigationType: NavigationTypeString, sourceElement: Element | undefined, info: any, signal: AbortSignal, formData: FormData | undefined, defaultLoader: (event: TransitionBeforePreparationEvent) => Promise<void>): Promise<TransitionBeforePreparationEvent>;
36
+ export declare function doSwap(afterPreparation: BeforeEvent, viewTransition: ViewTransition): TransitionBeforeSwapEvent;
36
37
  export {};
@@ -1,4 +1,5 @@
1
1
  import { updateScrollPosition } from "./router.js";
2
+ import { swap } from "./swap-functions.js";
2
3
  const TRANSITION_BEFORE_PREPARATION = "astro:before-preparation";
3
4
  const TRANSITION_AFTER_PREPARATION = "astro:after-preparation";
4
5
  const TRANSITION_BEFORE_SWAP = "astro:before-swap";
@@ -14,7 +15,8 @@ class BeforeEvent extends Event {
14
15
  sourceElement;
15
16
  info;
16
17
  newDocument;
17
- constructor(type, eventInitDict, from, to, direction, navigationType, sourceElement, info, newDocument) {
18
+ signal;
19
+ constructor(type, eventInitDict, from, to, direction, navigationType, sourceElement, info, newDocument, signal) {
18
20
  super(type, eventInitDict);
19
21
  this.from = from;
20
22
  this.to = to;
@@ -23,6 +25,7 @@ class BeforeEvent extends Event {
23
25
  this.sourceElement = sourceElement;
24
26
  this.info = info;
25
27
  this.newDocument = newDocument;
28
+ this.signal = signal;
26
29
  Object.defineProperties(this, {
27
30
  from: { enumerable: true },
28
31
  to: { enumerable: true, writable: true },
@@ -30,7 +33,8 @@ class BeforeEvent extends Event {
30
33
  navigationType: { enumerable: true },
31
34
  sourceElement: { enumerable: true },
32
35
  info: { enumerable: true },
33
- newDocument: { enumerable: true, writable: true }
36
+ newDocument: { enumerable: true, writable: true },
37
+ signal: { enumerable: true }
34
38
  });
35
39
  }
36
40
  }
@@ -38,7 +42,7 @@ const isTransitionBeforePreparationEvent = (value) => value.type === TRANSITION_
38
42
  class TransitionBeforePreparationEvent extends BeforeEvent {
39
43
  formData;
40
44
  loader;
41
- constructor(from, to, direction, navigationType, sourceElement, info, newDocument, formData, loader) {
45
+ constructor(from, to, direction, navigationType, sourceElement, info, newDocument, signal, formData, loader) {
42
46
  super(
43
47
  TRANSITION_BEFORE_PREPARATION,
44
48
  { cancelable: true },
@@ -48,7 +52,8 @@ class TransitionBeforePreparationEvent extends BeforeEvent {
48
52
  navigationType,
49
53
  sourceElement,
50
54
  info,
51
- newDocument
55
+ newDocument,
56
+ signal
52
57
  );
53
58
  this.formData = formData;
54
59
  this.loader = loader.bind(this, this);
@@ -63,7 +68,7 @@ class TransitionBeforeSwapEvent extends BeforeEvent {
63
68
  direction;
64
69
  viewTransition;
65
70
  swap;
66
- constructor(afterPreparation, viewTransition, swap) {
71
+ constructor(afterPreparation, viewTransition) {
67
72
  super(
68
73
  TRANSITION_BEFORE_SWAP,
69
74
  void 0,
@@ -73,11 +78,12 @@ class TransitionBeforeSwapEvent extends BeforeEvent {
73
78
  afterPreparation.navigationType,
74
79
  afterPreparation.sourceElement,
75
80
  afterPreparation.info,
76
- afterPreparation.newDocument
81
+ afterPreparation.newDocument,
82
+ afterPreparation.signal
77
83
  );
78
84
  this.direction = afterPreparation.direction;
79
85
  this.viewTransition = viewTransition;
80
- this.swap = swap.bind(this, this);
86
+ this.swap = () => swap(this.newDocument);
81
87
  Object.defineProperties(this, {
82
88
  direction: { enumerable: true },
83
89
  viewTransition: { enumerable: true },
@@ -85,7 +91,7 @@ class TransitionBeforeSwapEvent extends BeforeEvent {
85
91
  });
86
92
  }
87
93
  }
88
- async function doPreparation(from, to, direction, navigationType, sourceElement, info, formData, defaultLoader) {
94
+ async function doPreparation(from, to, direction, navigationType, sourceElement, info, signal, formData, defaultLoader) {
89
95
  const event = new TransitionBeforePreparationEvent(
90
96
  from,
91
97
  to,
@@ -94,6 +100,7 @@ async function doPreparation(from, to, direction, navigationType, sourceElement,
94
100
  sourceElement,
95
101
  info,
96
102
  window.document,
103
+ signal,
97
104
  formData,
98
105
  defaultLoader
99
106
  );
@@ -108,8 +115,8 @@ async function doPreparation(from, to, direction, navigationType, sourceElement,
108
115
  }
109
116
  return event;
110
117
  }
111
- async function doSwap(afterPreparation, viewTransition, defaultSwap) {
112
- const event = new TransitionBeforeSwapEvent(afterPreparation, viewTransition, defaultSwap);
118
+ function doSwap(afterPreparation, viewTransition) {
119
+ const event = new TransitionBeforeSwapEvent(afterPreparation, viewTransition);
113
120
  document.dispatchEvent(event);
114
121
  event.swap();
115
122
  return event;
@@ -1,4 +1,12 @@
1
1
  import { TRANSITION_AFTER_SWAP, doPreparation, doSwap } from "./events.js";
2
+ import {
3
+ deselectScripts,
4
+ restoreFocus,
5
+ saveFocus,
6
+ swapBodyElement,
7
+ swapHeadElements,
8
+ swapRootAttributes
9
+ } from "./swap-functions.js";
2
10
  const inBrowser = import.meta.env.SSR === false;
3
11
  const pushState = inBrowser && history.pushState.bind(history);
4
12
  const replaceState = inBrowser && history.replaceState.bind(history);
@@ -11,10 +19,9 @@ const updateScrollPosition = (positions) => {
11
19
  const supportsViewTransitions = inBrowser && !!document.startViewTransition;
12
20
  const transitionEnabledOnThisPage = () => inBrowser && !!document.querySelector('[name="astro-view-transitions-enabled"]');
13
21
  const samePage = (thisLocation, otherLocation) => thisLocation.pathname === otherLocation.pathname && thisLocation.search === otherLocation.search;
22
+ let mostRecentNavigation;
23
+ let mostRecentTransition;
14
24
  let originalLocation;
15
- let viewTransition;
16
- let skipTransition = false;
17
- let viewTransitionFinished;
18
25
  const triggerEvent = (name) => document.dispatchEvent(new Event(name));
19
26
  const onPageLoad = () => triggerEvent("astro:page-load");
20
27
  const announce = () => {
@@ -77,11 +84,9 @@ function getFallback() {
77
84
  function runScripts() {
78
85
  let wait = Promise.resolve();
79
86
  for (const script of Array.from(document.scripts)) {
80
- if (script.dataset.astroExec === "")
81
- continue;
87
+ if (script.dataset.astroExec === "") continue;
82
88
  const type = script.getAttribute("type");
83
- if (type && type !== "module" && type !== "text/javascript")
84
- continue;
89
+ if (type && type !== "module" && type !== "text/javascript") continue;
85
90
  const newScript = document.createElement("script");
86
91
  newScript.innerHTML = script.innerHTML;
87
92
  for (const attr of script.attributes) {
@@ -173,98 +178,11 @@ function preloadStyleLinks(newDocument) {
173
178
  }
174
179
  return links;
175
180
  }
176
- async function updateDOM(preparationEvent, options, historyState, fallback) {
177
- const persistedHeadElement = (el, newDoc) => {
178
- const id = el.getAttribute(PERSIST_ATTR);
179
- const newEl = id && newDoc.head.querySelector(`[${PERSIST_ATTR}="${id}"]`);
180
- if (newEl) {
181
- return newEl;
182
- }
183
- if (el.matches("link[rel=stylesheet]")) {
184
- const href = el.getAttribute("href");
185
- return newDoc.head.querySelector(`link[rel=stylesheet][href="${href}"]`);
186
- }
187
- return null;
188
- };
189
- const saveFocus = () => {
190
- const activeElement = document.activeElement;
191
- if (activeElement?.closest(`[${PERSIST_ATTR}]`)) {
192
- if (activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement) {
193
- const start = activeElement.selectionStart;
194
- const end = activeElement.selectionEnd;
195
- return { activeElement, start, end };
196
- }
197
- return { activeElement };
198
- } else {
199
- return { activeElement: null };
200
- }
201
- };
202
- const restoreFocus = ({ activeElement, start, end }) => {
203
- if (activeElement) {
204
- activeElement.focus();
205
- if (activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement) {
206
- if (typeof start === "number")
207
- activeElement.selectionStart = start;
208
- if (typeof end === "number")
209
- activeElement.selectionEnd = end;
210
- }
211
- }
212
- };
213
- const shouldCopyProps = (el) => {
214
- const persistProps = el.dataset.astroTransitionPersistProps;
215
- return persistProps == null || persistProps === "false";
216
- };
217
- const defaultSwap = (beforeSwapEvent) => {
218
- const html = document.documentElement;
219
- const astroAttributes = [...html.attributes].filter(
220
- ({ name }) => (html.removeAttribute(name), name.startsWith("data-astro-"))
221
- );
222
- [...beforeSwapEvent.newDocument.documentElement.attributes, ...astroAttributes].forEach(
223
- ({ name, value }) => html.setAttribute(name, value)
224
- );
225
- for (const s1 of document.scripts) {
226
- for (const s2 of beforeSwapEvent.newDocument.scripts) {
227
- if (
228
- // Check if the script should be rerun regardless of it being the same
229
- !s2.hasAttribute("data-astro-rerun") && // Inline
230
- (!s1.src && s1.textContent === s2.textContent || // External
231
- s1.src && s1.type === s2.type && s1.src === s2.src)
232
- ) {
233
- s2.dataset.astroExec = "";
234
- break;
235
- }
236
- }
237
- }
238
- for (const el of Array.from(document.head.children)) {
239
- const newEl = persistedHeadElement(el, beforeSwapEvent.newDocument);
240
- if (newEl) {
241
- newEl.remove();
242
- } else {
243
- el.remove();
244
- }
245
- }
246
- document.head.append(...beforeSwapEvent.newDocument.head.children);
247
- const oldBody = document.body;
248
- const savedFocus = saveFocus();
249
- document.body.replaceWith(beforeSwapEvent.newDocument.body);
250
- for (const el of oldBody.querySelectorAll(`[${PERSIST_ATTR}]`)) {
251
- const id = el.getAttribute(PERSIST_ATTR);
252
- const newEl = document.querySelector(`[${PERSIST_ATTR}="${id}"]`);
253
- if (newEl) {
254
- newEl.replaceWith(el);
255
- if (newEl.localName === "astro-island" && shouldCopyProps(el)) {
256
- el.setAttribute("ssr", "");
257
- el.setAttribute("props", newEl.getAttribute("props"));
258
- }
259
- }
260
- }
261
- restoreFocus(savedFocus);
262
- };
181
+ async function updateDOM(preparationEvent, options, currentTransition, historyState, fallback) {
263
182
  async function animate(phase) {
264
183
  function isInfinite(animation) {
265
184
  const effect = animation.effect;
266
- if (!effect || !(effect instanceof KeyframeEffect) || !effect.target)
267
- return false;
185
+ if (!effect || !(effect instanceof KeyframeEffect) || !effect.target) return false;
268
186
  const style = window.getComputedStyle(effect.target, effect.pseudoElement);
269
187
  return style.animationIterationCount === "infinite";
270
188
  }
@@ -274,26 +192,36 @@ async function updateDOM(preparationEvent, options, historyState, fallback) {
274
192
  const newAnimations = nextAnimations.filter(
275
193
  (a) => !currentAnimations.includes(a) && !isInfinite(a)
276
194
  );
277
- return Promise.all(newAnimations.map((a) => a.finished));
195
+ return Promise.allSettled(newAnimations.map((a) => a.finished));
278
196
  }
279
- if (!skipTransition) {
280
- document.documentElement.setAttribute(DIRECTION_ATTR, preparationEvent.direction);
281
- if (fallback === "animate") {
197
+ if (fallback === "animate" && !currentTransition.transitionSkipped && !preparationEvent.signal.aborted) {
198
+ try {
282
199
  await animate("old");
200
+ } catch (err) {
283
201
  }
284
- } else {
285
- throw new DOMException("Transition was skipped");
286
202
  }
287
203
  const pageTitleForBrowserHistory = document.title;
288
- const swapEvent = await doSwap(preparationEvent, viewTransition, defaultSwap);
204
+ const swapEvent = doSwap(preparationEvent, currentTransition.viewTransition);
289
205
  moveToLocation(swapEvent.to, swapEvent.from, options, pageTitleForBrowserHistory, historyState);
290
206
  triggerEvent(TRANSITION_AFTER_SWAP);
291
- if (fallback === "animate" && !skipTransition) {
292
- animate("new").then(() => viewTransitionFinished());
207
+ if (fallback === "animate") {
208
+ if (!currentTransition.transitionSkipped && !swapEvent.signal.aborted) {
209
+ animate("new").finally(() => currentTransition.viewTransitionFinished());
210
+ } else {
211
+ currentTransition.viewTransitionFinished();
212
+ }
293
213
  }
294
214
  }
215
+ function abortAndRecreateMostRecentNavigation() {
216
+ mostRecentNavigation?.controller.abort();
217
+ return mostRecentNavigation = {
218
+ controller: new AbortController()
219
+ };
220
+ }
295
221
  async function transition(direction, from, to, options, historyState) {
222
+ const currentNavigation = abortAndRecreateMostRecentNavigation();
296
223
  if (!transitionEnabledOnThisPage() || location.origin !== to.origin) {
224
+ if (currentNavigation === mostRecentNavigation) mostRecentNavigation = void 0;
297
225
  location.href = to.href;
298
226
  return;
299
227
  }
@@ -304,6 +232,7 @@ async function transition(direction, from, to, options, historyState) {
304
232
  if (samePage(from, to)) {
305
233
  if (direction !== "back" && to.hash || direction === "back" && from.hash) {
306
234
  moveToLocation(to, from, options, document.title, historyState);
235
+ if (currentNavigation === mostRecentNavigation) mostRecentNavigation = void 0;
307
236
  return;
308
237
  }
309
238
  }
@@ -314,16 +243,20 @@ async function transition(direction, from, to, options, historyState) {
314
243
  navigationType,
315
244
  options.sourceElement,
316
245
  options.info,
246
+ currentNavigation.controller.signal,
317
247
  options.formData,
318
248
  defaultLoader
319
249
  );
320
- if (prepEvent.defaultPrevented) {
321
- location.href = to.href;
250
+ if (prepEvent.defaultPrevented || prepEvent.signal.aborted) {
251
+ if (currentNavigation === mostRecentNavigation) mostRecentNavigation = void 0;
252
+ if (!prepEvent.signal.aborted) {
253
+ location.href = to.href;
254
+ }
322
255
  return;
323
256
  }
324
257
  async function defaultLoader(preparationEvent) {
325
258
  const href = preparationEvent.to.href;
326
- const init = {};
259
+ const init = { signal: preparationEvent.signal };
327
260
  if (preparationEvent.formData) {
328
261
  init.method = "POST";
329
262
  const form = preparationEvent.sourceElement instanceof HTMLFormElement ? preparationEvent.sourceElement : preparationEvent.sourceElement instanceof HTMLElement && "form" in preparationEvent.sourceElement ? preparationEvent.sourceElement.form : preparationEvent.sourceElement?.closest("form");
@@ -345,42 +278,77 @@ async function transition(direction, from, to, options, historyState) {
345
278
  return;
346
279
  }
347
280
  const links = preloadStyleLinks(preparationEvent.newDocument);
348
- links.length && await Promise.all(links);
349
- if (import.meta.env.DEV)
350
- await prepareForClientOnlyComponents(preparationEvent.newDocument, preparationEvent.to);
281
+ links.length && !preparationEvent.signal.aborted && await Promise.all(links);
282
+ if (import.meta.env.DEV && !preparationEvent.signal.aborted)
283
+ await prepareForClientOnlyComponents(
284
+ preparationEvent.newDocument,
285
+ preparationEvent.to,
286
+ preparationEvent.signal
287
+ );
351
288
  }
352
- skipTransition = false;
289
+ async function abortAndRecreateMostRecentTransition() {
290
+ if (mostRecentTransition) {
291
+ if (mostRecentTransition.viewTransition) {
292
+ try {
293
+ mostRecentTransition.viewTransition.skipTransition();
294
+ } catch {
295
+ }
296
+ try {
297
+ await mostRecentTransition.viewTransition.updateCallbackDone;
298
+ } catch {
299
+ }
300
+ }
301
+ }
302
+ return mostRecentTransition = { transitionSkipped: false };
303
+ }
304
+ const currentTransition = await abortAndRecreateMostRecentTransition();
305
+ if (prepEvent.signal.aborted) {
306
+ if (currentNavigation === mostRecentNavigation) mostRecentNavigation = void 0;
307
+ return;
308
+ }
309
+ document.documentElement.setAttribute(DIRECTION_ATTR, prepEvent.direction);
353
310
  if (supportsViewTransitions) {
354
- viewTransition = document.startViewTransition(
355
- async () => await updateDOM(prepEvent, options, historyState)
311
+ currentTransition.viewTransition = document.startViewTransition(
312
+ async () => await updateDOM(prepEvent, options, currentTransition, historyState)
356
313
  );
357
314
  } else {
358
315
  const updateDone = (async () => {
359
- await new Promise((r) => setTimeout(r));
360
- await updateDOM(prepEvent, options, historyState, getFallback());
316
+ await Promise.resolve();
317
+ await updateDOM(prepEvent, options, currentTransition, historyState, getFallback());
361
318
  })();
362
- viewTransition = {
319
+ currentTransition.viewTransition = {
363
320
  updateCallbackDone: updateDone,
364
321
  // this is about correct
365
322
  ready: updateDone,
366
323
  // good enough
367
- finished: new Promise((r) => viewTransitionFinished = r),
324
+ // Finished promise could have been done better: finished rejects iff updateDone does.
325
+ // Our simulation always resolves, never rejects.
326
+ finished: new Promise((r) => currentTransition.viewTransitionFinished = r),
368
327
  // see end of updateDOM
369
328
  skipTransition: () => {
370
- skipTransition = true;
329
+ currentTransition.transitionSkipped = true;
330
+ document.documentElement.removeAttribute(OLD_NEW_ATTR);
371
331
  }
372
332
  };
373
333
  }
374
- viewTransition.ready.then(async () => {
334
+ currentTransition.viewTransition.updateCallbackDone.finally(async () => {
375
335
  await runScripts();
376
336
  onPageLoad();
377
337
  announce();
378
338
  });
379
- viewTransition.finished.then(() => {
339
+ currentTransition.viewTransition.finished.finally(() => {
340
+ currentTransition.viewTransition = void 0;
341
+ if (currentTransition === mostRecentTransition) mostRecentTransition = void 0;
342
+ if (currentNavigation === mostRecentNavigation) mostRecentNavigation = void 0;
380
343
  document.documentElement.removeAttribute(DIRECTION_ATTR);
381
344
  document.documentElement.removeAttribute(OLD_NEW_ATTR);
382
345
  });
383
- await viewTransition.ready;
346
+ try {
347
+ await currentTransition.viewTransition.updateCallbackDone;
348
+ } catch (e) {
349
+ const err = e;
350
+ console.log("[astro]", err.name, err.message, err.stack);
351
+ }
384
352
  }
385
353
  let navigateOnServerWarned = false;
386
354
  async function navigate(href, options) {
@@ -421,8 +389,7 @@ if (inBrowser) {
421
389
  originalLocation = new URL(location.href);
422
390
  addEventListener("popstate", onPopState);
423
391
  addEventListener("load", onPageLoad);
424
- if ("onscrollend" in window)
425
- addEventListener("scrollend", onScrollEnd);
392
+ if ("onscrollend" in window) addEventListener("scrollend", onScrollEnd);
426
393
  else {
427
394
  let intervalId, lastY, lastX, lastIndex;
428
395
  const scrollInterval = () => {
@@ -443,8 +410,7 @@ if (inBrowser) {
443
410
  addEventListener(
444
411
  "scroll",
445
412
  () => {
446
- if (intervalId !== void 0)
447
- return;
413
+ if (intervalId !== void 0) return;
448
414
  lastIndex = history.state.index, lastY = scrollY, lastX = scrollX;
449
415
  intervalId = window.setInterval(scrollInterval, 50);
450
416
  },
@@ -456,7 +422,7 @@ if (inBrowser) {
456
422
  script.dataset.astroExec = "";
457
423
  }
458
424
  }
459
- async function prepareForClientOnlyComponents(newDocument, toLocation) {
425
+ async function prepareForClientOnlyComponents(newDocument, toLocation, signal) {
460
426
  if (newDocument.body.querySelector(`astro-island[client='only']`)) {
461
427
  const nextPage = document.createElement("iframe");
462
428
  nextPage.src = toLocation.href;
@@ -481,13 +447,15 @@ async function prepareForClientOnlyComponents(newDocument, toLocation) {
481
447
  });
482
448
  }
483
449
  async function hydrationDone(loadingPage) {
484
- await new Promise(
485
- (r) => loadingPage.contentWindow?.addEventListener("load", r, { once: true })
486
- );
450
+ if (!signal.aborted) {
451
+ await new Promise(
452
+ (r) => loadingPage.contentWindow?.addEventListener("load", r, { once: true })
453
+ );
454
+ }
487
455
  return new Promise(async (r) => {
488
456
  for (let count = 0; count <= 20; ++count) {
489
- if (!loadingPage.contentDocument.body.querySelector("astro-island[ssr]"))
490
- break;
457
+ if (signal.aborted) break;
458
+ if (!loadingPage.contentDocument.body.querySelector("astro-island[ssr]")) break;
491
459
  await new Promise((r2) => setTimeout(r2, 50));
492
460
  }
493
461
  r();
@@ -0,0 +1,12 @@
1
+ export type SavedFocus = {
2
+ activeElement: HTMLElement | null;
3
+ start?: number | null;
4
+ end?: number | null;
5
+ };
6
+ export declare function deselectScripts(doc: Document): void;
7
+ export declare function swapRootAttributes(doc: Document): void;
8
+ export declare function swapHeadElements(doc: Document): void;
9
+ export declare function swapBodyElement(newElement: Element, oldElement: Element): void;
10
+ export declare const saveFocus: () => (() => void);
11
+ export declare const restoreFocus: ({ activeElement, start, end }: SavedFocus) => void;
12
+ export declare const swap: (doc: Document) => void;