astro 3.3.2 → 3.3.4

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.
@@ -86,7 +86,7 @@ function assets({
86
86
  },
87
87
  // In build, rewrite paths to ESM imported images in code to their final location
88
88
  async renderChunk(code) {
89
- const assetUrlRE = /__ASTRO_ASSET_IMAGE__([a-z\d]{8})__(?:_(.*?)__)?/g;
89
+ const assetUrlRE = /__ASTRO_ASSET_IMAGE__([\w$]{8})__(?:_(.*?)__)?/g;
90
90
  let match;
91
91
  let s;
92
92
  while (match = assetUrlRE.exec(code)) {
@@ -583,7 +583,11 @@ ${message}`
583
583
  ...inheritedFlags,
584
584
  ...installCommand.dependencies
585
585
  ],
586
- { cwd }
586
+ {
587
+ cwd,
588
+ // reset NODE_ENV to ensure install command run in dev mode
589
+ env: { NODE_ENV: void 0 }
590
+ }
587
591
  );
588
592
  spinner.succeed();
589
593
  return 1 /* updated */;
@@ -8,6 +8,10 @@ export interface RenderErrorOptions {
8
8
  routeData?: RouteData;
9
9
  response?: Response;
10
10
  status: 404 | 500;
11
+ /**
12
+ * Whether to skip onRequest() while rendering the error page. Defaults to false.
13
+ */
14
+ skipMiddleware?: boolean;
11
15
  }
12
16
  export declare class App {
13
17
  #private;
@@ -208,7 +208,7 @@ class App {
208
208
  * If it is a known error code, try sending the according page (e.g. 404.astro / 500.astro).
209
209
  * This also handles pre-rendered /404 or /500 routes
210
210
  */
211
- async #renderError(request, { status, response: originalResponse }) {
211
+ async #renderError(request, { status, response: originalResponse, skipMiddleware = false }) {
212
212
  const errorRouteData = matchRoute("/" + status, this.#manifestData);
213
213
  const url = new URL(request.url);
214
214
  if (errorRouteData) {
@@ -232,12 +232,22 @@ class App {
232
232
  status
233
233
  );
234
234
  const page = await mod.page();
235
- if (mod.onRequest) {
235
+ if (skipMiddleware === false && mod.onRequest) {
236
236
  this.#pipeline.setMiddlewareFunction(mod.onRequest);
237
237
  }
238
+ if (skipMiddleware) {
239
+ this.#pipeline.unsetMiddlewareFunction();
240
+ }
238
241
  const response2 = await this.#pipeline.renderRoute(newRenderContext, page);
239
242
  return this.#mergeResponses(response2, originalResponse);
240
243
  } catch {
244
+ if (skipMiddleware === false && mod.onRequest) {
245
+ return this.#renderError(request, {
246
+ status,
247
+ response: originalResponse,
248
+ skipMiddleware: true
249
+ });
250
+ }
241
251
  }
242
252
  }
243
253
  const response = this.#mergeResponses(new Response(null, { status }), originalResponse);
@@ -1,4 +1,4 @@
1
- const ASTRO_VERSION = "3.3.2";
1
+ const ASTRO_VERSION = "3.3.4";
2
2
  const SUPPORTED_MARKDOWN_FILE_EXTENSIONS = [
3
3
  ".markdown",
4
4
  ".mdown",
@@ -20,7 +20,7 @@ async function dev(inlineConfig) {
20
20
  base: restart.container.settings.config.base
21
21
  })
22
22
  );
23
- const currentVersion = "3.3.2";
23
+ const currentVersion = "3.3.4";
24
24
  if (currentVersion.includes("-")) {
25
25
  logger.warn(null, msg.prerelease({ currentVersion }));
26
26
  }
@@ -50,7 +50,7 @@ function serverStart({
50
50
  base,
51
51
  isRestart = false
52
52
  }) {
53
- const version = "3.3.2";
53
+ const version = "3.3.4";
54
54
  const localPrefix = `${dim("\u2503")} Local `;
55
55
  const networkPrefix = `${dim("\u2503")} Network `;
56
56
  const emptyPrefix = " ".repeat(11);
@@ -235,7 +235,7 @@ function printHelp({
235
235
  message.push(
236
236
  linebreak(),
237
237
  ` ${bgGreen(black(` ${commandName} `))} ${green(
238
- `v${"3.3.2"}`
238
+ `v${"3.3.4"}`
239
239
  )} ${headline}`
240
240
  );
241
241
  }
@@ -25,6 +25,10 @@ export declare class Pipeline {
25
25
  * A middleware function that will be called before each request.
26
26
  */
27
27
  setMiddlewareFunction(onRequest: MiddlewareEndpointHandler): void;
28
+ /**
29
+ * Removes the current middleware function. Subsequent requests won't trigger any middleware.
30
+ */
31
+ unsetMiddlewareFunction(): void;
28
32
  /**
29
33
  * Returns the current environment
30
34
  */
@@ -33,6 +33,12 @@ class Pipeline {
33
33
  setMiddlewareFunction(onRequest) {
34
34
  this.#onRequest = onRequest;
35
35
  }
36
+ /**
37
+ * Removes the current middleware function. Subsequent requests won't trigger any middleware.
38
+ */
39
+ unsetMiddlewareFunction() {
40
+ this.#onRequest = void 0;
41
+ }
36
42
  /**
37
43
  * Returns the current environment
38
44
  */
@@ -16,7 +16,7 @@ function renderAllHeadContent(result) {
16
16
  return renderElement("script", script, false);
17
17
  });
18
18
  const links = Array.from(result.links).filter(uniqueElements).map((link) => renderElement("link", link, false));
19
- let content = links.join("\n") + styles.join("\n") + scripts.join("\n");
19
+ let content = styles.join("\n") + links.join("\n") + scripts.join("\n");
20
20
  if (result._metadata.extraHead.length > 0) {
21
21
  for (const part of result._metadata.extraHead) {
22
22
  content += part;
@@ -1,4 +1,4 @@
1
- const persistState = (state) => history.state && history.replaceState(state, "");
1
+ const updateScrollPosition = (positions) => history.state && history.replaceState({ ...history.state, ...positions }, "");
2
2
  const inBrowser = import.meta.env.SSR === false;
3
3
  const supportsViewTransitions = inBrowser && !!document.startViewTransition;
4
4
  const transitionEnabledOnThisPage = () => inBrowser && !!document.querySelector('[name="astro-view-transitions-enabled"]');
@@ -26,6 +26,7 @@ const announce = () => {
26
26
  );
27
27
  };
28
28
  const PERSIST_ATTR = "data-astro-transition-persist";
29
+ const VITE_ID = "data-vite-dev-id";
29
30
  let parser;
30
31
  let currentHistoryIndex = 0;
31
32
  if (inBrowser) {
@@ -79,11 +80,6 @@ function getFallback() {
79
80
  }
80
81
  return "animate";
81
82
  }
82
- function markScriptsExec() {
83
- for (const script of document.scripts) {
84
- script.dataset.astroExec = "";
85
- }
86
- }
87
83
  function runScripts() {
88
84
  let wait = Promise.resolve();
89
85
  for (const script of Array.from(document.scripts)) {
@@ -112,7 +108,7 @@ function isInfinite(animation) {
112
108
  const style = window.getComputedStyle(effect.target, effect.pseudoElement);
113
109
  return style.animationIterationCount === "infinite";
114
110
  }
115
- const updateHistoryAndScrollPosition = (toLocation, replace, intraPage) => {
111
+ const moveToLocation = (toLocation, replace, intraPage) => {
116
112
  const fresh = !samePage(toLocation);
117
113
  let scrolledToTop = false;
118
114
  if (toLocation.href !== location.href) {
@@ -139,6 +135,28 @@ const updateHistoryAndScrollPosition = (toLocation, replace, intraPage) => {
139
135
  }
140
136
  }
141
137
  };
138
+ function stylePreloadLinks(newDocument) {
139
+ const links = [];
140
+ for (const el of newDocument.querySelectorAll("head link[rel=stylesheet]")) {
141
+ if (!document.querySelector(
142
+ `[${PERSIST_ATTR}="${el.getAttribute(
143
+ PERSIST_ATTR
144
+ )}"], link[rel=stylesheet][href="${el.getAttribute("href")}"]`
145
+ )) {
146
+ const c = document.createElement("link");
147
+ c.setAttribute("rel", "preload");
148
+ c.setAttribute("as", "style");
149
+ c.setAttribute("href", el.getAttribute("href"));
150
+ links.push(
151
+ new Promise((resolve) => {
152
+ ["load", "error"].forEach((evName) => c.addEventListener(evName, resolve));
153
+ document.head.append(c);
154
+ })
155
+ );
156
+ }
157
+ }
158
+ return links;
159
+ }
142
160
  async function updateDOM(newDocument, toLocation, options, popState, fallback) {
143
161
  const persistedHeadElement = (el) => {
144
162
  const id = el.getAttribute(PERSIST_ATTR);
@@ -154,7 +172,7 @@ async function updateDOM(newDocument, toLocation, options, popState, fallback) {
154
172
  };
155
173
  const saveFocus = () => {
156
174
  const activeElement = document.activeElement;
157
- if (activeElement?.closest("[data-astro-transition-persist]")) {
175
+ if (activeElement?.closest(`[${PERSIST_ATTR}]`)) {
158
176
  if (activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement) {
159
177
  const start = activeElement.selectionStart;
160
178
  const end = activeElement.selectionEnd;
@@ -217,41 +235,20 @@ async function updateDOM(newDocument, toLocation, options, popState, fallback) {
217
235
  if (popState) {
218
236
  scrollTo(popState.scrollX, popState.scrollY);
219
237
  } else {
220
- updateHistoryAndScrollPosition(toLocation, options.history === "replace", false);
238
+ moveToLocation(toLocation, options.history === "replace", false);
221
239
  }
222
240
  triggerEvent("astro:after-swap");
223
241
  };
224
- const links = [];
225
- for (const el of newDocument.querySelectorAll("head link[rel=stylesheet]")) {
226
- if (!document.querySelector(
227
- `[${PERSIST_ATTR}="${el.getAttribute(
228
- PERSIST_ATTR
229
- )}"], link[rel=stylesheet][href="${el.getAttribute("href")}"]`
230
- )) {
231
- const c = document.createElement("link");
232
- c.setAttribute("rel", "preload");
233
- c.setAttribute("as", "style");
234
- c.setAttribute("href", el.getAttribute("href"));
235
- links.push(
236
- new Promise((resolve) => {
237
- ["load", "error"].forEach((evName) => c.addEventListener(evName, resolve));
238
- document.head.append(c);
239
- })
240
- );
241
- }
242
- }
242
+ const links = stylePreloadLinks(newDocument);
243
243
  links.length && await Promise.all(links);
244
244
  if (fallback === "animate") {
245
245
  const currentAnimations = document.getAnimations();
246
246
  document.documentElement.dataset.astroTransitionFallback = "old";
247
247
  const newAnimations = document.getAnimations().filter((a) => !currentAnimations.includes(a) && !isInfinite(a));
248
248
  const finished = Promise.all(newAnimations.map((a) => a.finished));
249
- const fallbackSwap = () => {
250
- swap();
251
- document.documentElement.dataset.astroTransitionFallback = "new";
252
- };
253
249
  await finished;
254
- fallbackSwap();
250
+ swap();
251
+ document.documentElement.dataset.astroTransitionFallback = "new";
255
252
  } else {
256
253
  swap();
257
254
  }
@@ -274,6 +271,8 @@ async function transition(direction, toLocation, options, popState) {
274
271
  location.href = href;
275
272
  return;
276
273
  }
274
+ if (import.meta.env.DEV)
275
+ await prepareForClientOnlyComponents(newDocument, toLocation);
277
276
  if (!popState) {
278
277
  history.replaceState({ ...history.state, scrollX, scrollY }, "");
279
278
  }
@@ -289,7 +288,6 @@ async function transition(direction, toLocation, options, popState) {
289
288
  await finished;
290
289
  } finally {
291
290
  await runScripts();
292
- markScriptsExec();
293
291
  onPageLoad();
294
292
  announce();
295
293
  }
@@ -313,7 +311,7 @@ function navigate(href, options) {
313
311
  }
314
312
  const toLocation = new URL(href, location.href);
315
313
  if (location.origin === toLocation.origin && samePage(toLocation)) {
316
- updateHistoryAndScrollPosition(toLocation, options?.history === "replace", true);
314
+ moveToLocation(toLocation, options?.history === "replace", true);
317
315
  } else {
318
316
  transition("forward", toLocation, options ?? {});
319
317
  }
@@ -345,18 +343,55 @@ function onPopState(ev) {
345
343
  transition(direction, new URL(location.href), {}, state);
346
344
  }
347
345
  }
346
+ const onScroll = () => {
347
+ updateScrollPosition({ scrollX, scrollY });
348
+ };
348
349
  if (inBrowser) {
349
350
  if (supportsViewTransitions || getFallback() !== "none") {
350
351
  addEventListener("popstate", onPopState);
351
352
  addEventListener("load", onPageLoad);
352
- const updateState = () => {
353
- persistState({ ...history.state, scrollX, scrollY });
354
- };
355
353
  if ("onscrollend" in window)
356
- addEventListener("scrollend", updateState);
354
+ addEventListener("scrollend", onScroll);
357
355
  else
358
- addEventListener("scroll", throttle(updateState, 300));
359
- markScriptsExec();
356
+ addEventListener("scroll", throttle(onScroll, 300));
357
+ }
358
+ for (const script of document.scripts) {
359
+ script.dataset.astroExec = "";
360
+ }
361
+ }
362
+ async function prepareForClientOnlyComponents(newDocument, toLocation) {
363
+ if (newDocument.body.querySelector(`astro-island[client='only']`)) {
364
+ const nextPage = document.createElement("iframe");
365
+ nextPage.setAttribute("src", toLocation.href);
366
+ nextPage.style.display = "none";
367
+ document.body.append(nextPage);
368
+ await hydrationDone(nextPage);
369
+ const nextHead = nextPage.contentDocument?.head;
370
+ if (nextHead) {
371
+ document.head.querySelectorAll(`style[${PERSIST_ATTR}=""]`).forEach((s) => s.removeAttribute(PERSIST_ATTR));
372
+ const viteIds = [...nextHead.querySelectorAll(`style[${VITE_ID}]`)].map(
373
+ (style) => style.getAttribute(VITE_ID)
374
+ );
375
+ viteIds.forEach((id) => {
376
+ const style = document.head.querySelector(`style[${VITE_ID}="${id}"]`);
377
+ if (style && !newDocument.head.querySelector(`style[${VITE_ID}="${id}"]`)) {
378
+ newDocument.head.appendChild(style);
379
+ }
380
+ });
381
+ }
382
+ async function hydrationDone(loadingPage) {
383
+ await new Promise(
384
+ (r) => loadingPage.contentWindow?.addEventListener("load", r, { once: true })
385
+ );
386
+ return new Promise(async (r) => {
387
+ for (let count = 0; count <= 20; ++count) {
388
+ if (!loadingPage.contentDocument.body.querySelector("astro-island[ssr]"))
389
+ break;
390
+ await new Promise((r2) => setTimeout(r2, 50));
391
+ }
392
+ r();
393
+ });
394
+ }
360
395
  }
361
396
  }
362
397
  export {
@@ -1,3 +1,4 @@
1
+ import { slash } from "@astrojs/internal-helpers/path";
1
2
  import { fileURLToPath } from "node:url";
2
3
  import {
3
4
  cachedCompilation,
@@ -55,7 +56,7 @@ async function handleHotUpdate(ctx, { config, logger, compile, source }) {
55
56
  }
56
57
  }
57
58
  const mods = [...filtered].filter((m) => !m.url.endsWith("="));
58
- const file = ctx.file.replace(config.root.pathname, "/");
59
+ const file = ctx.file.replace(slash(fileURLToPath(config.root)), "/");
59
60
  if (isStyleOnlyChange) {
60
61
  logger.info("astro", msg.hmr({ file, style: true }));
61
62
  return mods.filter((mod) => mod.id !== ctx.file && !mod.id?.endsWith(".ts"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "3.3.2",
3
+ "version": "3.3.4",
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.21.1",
163
163
  "@astrojs/internal-helpers": "0.2.1",
164
164
  "@astrojs/markdown-remark": "3.3.0",
165
- "@astrojs/telemetry": "3.0.3"
165
+ "@astrojs/telemetry": "3.0.4"
166
166
  },
167
167
  "optionalDependencies": {
168
168
  "sharp": "^0.32.5"