astro 2.9.0 → 2.9.1

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.
@@ -47,6 +47,19 @@ const { fallback = 'animate' } = Astro.props as Props;
47
47
  doc.documentElement.dataset.astroTransition = dir;
48
48
  const swap = () => document.documentElement.replaceWith(doc.documentElement);
49
49
 
50
+ // Wait on links to finish, to prevent FOUC
51
+ const links = Array.from(doc.querySelectorAll('head link[rel=stylesheet]')).map(
52
+ (link) =>
53
+ new Promise((resolve) => {
54
+ const c = link.cloneNode();
55
+ ['load', 'error'].forEach((evName) => c.addEventListener(evName, resolve));
56
+ document.head.append(c);
57
+ })
58
+ );
59
+ if (links.length) {
60
+ await Promise.all(links);
61
+ }
62
+
50
63
  if (fallback === 'animate') {
51
64
  let isAnimating = false;
52
65
  addEventListener('animationstart', () => (isAnimating = true), { once: true });
@@ -128,7 +141,12 @@ const { fallback = 'animate' } = Astro.props as Props;
128
141
  }
129
142
  });
130
143
  window.addEventListener('popstate', () => {
131
- if (!transitionEnabledOnThisPage()) return;
144
+ if (!transitionEnabledOnThisPage()) {
145
+ // The current page doesn't haven't View Transitions,
146
+ // respect that with a full page reload
147
+ location.reload();
148
+ return;
149
+ }
132
150
  const nextIndex = history.state?.index ?? currentHistoryIndex + 1;
133
151
  const direction: Direction = nextIndex > currentHistoryIndex ? 'forward' : 'back';
134
152
  navigate(direction, location.href);
@@ -42,3 +42,15 @@
42
42
  transform: translateX(-100%);
43
43
  }
44
44
  }
45
+
46
+ @media (prefers-reduced-motion) {
47
+ ::view-transition-group(*),
48
+ ::view-transition-old(*),
49
+ ::view-transition-new(*) {
50
+ animation: none !important;
51
+ }
52
+
53
+ [data-astro-transition-scope] {
54
+ animation: none !important;
55
+ }
56
+ }
@@ -1173,7 +1173,7 @@ export interface AstroUserConfig {
1173
1173
  * @version 2.9.0
1174
1174
  * @description
1175
1175
  * Enable experimental support for the `<ViewTransitions / >` component. With this enabled
1176
- * you can opt-in to [client-side routing](https://docs.astro.build/en/guides/client-side-routing/) on a per-page basis using this component
1176
+ * you can opt-in to [view transitions](https://docs.astro.build/en/guides/view-transitions/) on a per-page basis using this component
1177
1177
  * and enable animations with the `transition:animate` directive.
1178
1178
  *
1179
1179
  * ```js
@@ -1,4 +1,4 @@
1
- const ASTRO_VERSION = "2.9.0";
1
+ const ASTRO_VERSION = "2.9.1";
2
2
  const SUPPORTED_MARKDOWN_FILE_EXTENSIONS = [
3
3
  ".markdown",
4
4
  ".mdown",
@@ -54,7 +54,7 @@ async function dev(settings, options) {
54
54
  isRestart: options.isRestart
55
55
  })
56
56
  );
57
- const currentVersion = "2.9.0";
57
+ const currentVersion = "2.9.1";
58
58
  if (currentVersion.includes("-")) {
59
59
  warn(options.logging, null, msg.prerelease({ currentVersion }));
60
60
  }
@@ -47,7 +47,7 @@ function serverStart({
47
47
  base,
48
48
  isRestart = false
49
49
  }) {
50
- const version = "2.9.0";
50
+ const version = "2.9.1";
51
51
  const localPrefix = `${dim("\u2503")} Local `;
52
52
  const networkPrefix = `${dim("\u2503")} Network `;
53
53
  const emptyPrefix = " ".repeat(11);
@@ -233,7 +233,7 @@ function printHelp({
233
233
  message.push(
234
234
  linebreak(),
235
235
  ` ${bgGreen(black(` ${commandName} `))} ${green(
236
- `v${"2.9.0"}`
236
+ `v${"2.9.1"}`
237
237
  )} ${headline}`
238
238
  );
239
239
  }
@@ -77,7 +77,11 @@ Did you forget to import the component or is it possible there is a typo?`);
77
77
  props[key] = value;
78
78
  }
79
79
  }
80
- const html = markHTMLString(await renderToString(result, vnode.type, props, slots));
80
+ const str = await renderToString(result, vnode.type, props, slots);
81
+ if (str instanceof Response) {
82
+ throw str;
83
+ }
84
+ const html = markHTMLString(str);
81
85
  return html;
82
86
  }
83
87
  case (!vnode.type && vnode.type !== 0):
@@ -9,5 +9,4 @@ export interface AstroComponentFactory {
9
9
  propagation?: PropagationHint;
10
10
  }
11
11
  export declare function isAstroComponentFactory(obj: any): obj is AstroComponentFactory;
12
- export declare function renderToString(result: SSRResult, componentFactory: AstroComponentFactory, props: any, children: any): Promise<string>;
13
12
  export declare function isAPropagatingComponent(result: SSRResult, factory: AstroComponentFactory): boolean;
@@ -1,22 +1,6 @@
1
- import { HTMLParts } from "../common.js";
2
- import { isHeadAndContent } from "./head-and-content.js";
3
- import { renderAstroTemplateResult } from "./render-template.js";
4
1
  function isAstroComponentFactory(obj) {
5
2
  return obj == null ? false : obj.isAstroComponentFactory === true;
6
3
  }
7
- async function renderToString(result, componentFactory, props, children) {
8
- const factoryResult = await componentFactory(result, props, children);
9
- if (factoryResult instanceof Response) {
10
- const response = factoryResult;
11
- throw response;
12
- }
13
- let parts = new HTMLParts();
14
- const templateResult = isHeadAndContent(factoryResult) ? factoryResult.content : factoryResult;
15
- for await (const chunk of renderAstroTemplateResult(templateResult)) {
16
- parts.append(chunk, result);
17
- }
18
- return parts.toString();
19
- }
20
4
  function isAPropagatingComponent(result, factory) {
21
5
  let hint = factory.propagation || "none";
22
6
  if (factory.moduleId && result.componentMetadata.has(factory.moduleId) && hint === "none") {
@@ -26,6 +10,5 @@ function isAPropagatingComponent(result, factory) {
26
10
  }
27
11
  export {
28
12
  isAPropagatingComponent,
29
- isAstroComponentFactory,
30
- renderToString
13
+ isAstroComponentFactory
31
14
  };
@@ -1,6 +1,7 @@
1
1
  export type { AstroComponentFactory } from './factory';
2
- export { isAstroComponentFactory, renderToString } from './factory.js';
2
+ export { isAstroComponentFactory } from './factory.js';
3
3
  export { createHeadAndContent, isHeadAndContent } from './head-and-content.js';
4
4
  export type { AstroComponentInstance } from './instance';
5
5
  export { createAstroComponentInstance, isAstroComponentInstance } from './instance.js';
6
6
  export { isRenderTemplateResult, renderAstroTemplateResult, renderTemplate, } from './render-template.js';
7
+ export { renderToReadableStream, renderToString } from './render.js';
@@ -1,4 +1,4 @@
1
- import { isAstroComponentFactory, renderToString } from "./factory.js";
1
+ import { isAstroComponentFactory } from "./factory.js";
2
2
  import { createHeadAndContent, isHeadAndContent } from "./head-and-content.js";
3
3
  import { createAstroComponentInstance, isAstroComponentInstance } from "./instance.js";
4
4
  import {
@@ -6,6 +6,7 @@ import {
6
6
  renderAstroTemplateResult,
7
7
  renderTemplate
8
8
  } from "./render-template.js";
9
+ import { renderToReadableStream, renderToString } from "./render.js";
9
10
  export {
10
11
  createAstroComponentInstance,
11
12
  createHeadAndContent,
@@ -15,5 +16,6 @@ export {
15
16
  isRenderTemplateResult,
16
17
  renderAstroTemplateResult,
17
18
  renderTemplate,
19
+ renderToReadableStream,
18
20
  renderToString
19
21
  };
@@ -0,0 +1,4 @@
1
+ import type { RouteData, SSRResult } from '../../../../@types/astro';
2
+ import type { AstroComponentFactory } from './factory.js';
3
+ export declare function renderToString(result: SSRResult, componentFactory: AstroComponentFactory, props: any, children: any, isPage?: boolean, route?: RouteData): Promise<string | Response>;
4
+ export declare function renderToReadableStream(result: SSRResult, componentFactory: AstroComponentFactory, props: any, children: any, isPage?: boolean, route?: RouteData): Promise<ReadableStream | Response>;
@@ -0,0 +1,119 @@
1
+ import { AstroError, AstroErrorData } from "../../../../core/errors/index.js";
2
+ import { chunkToByteArray, chunkToString, encoder } from "../common.js";
3
+ import { isHeadAndContent } from "./head-and-content.js";
4
+ import { isRenderTemplateResult, renderAstroTemplateResult } from "./render-template.js";
5
+ async function renderToString(result, componentFactory, props, children, isPage = false, route) {
6
+ const templateResult = await callComponentAsTemplateResultOrResponse(
7
+ result,
8
+ componentFactory,
9
+ props,
10
+ children,
11
+ route
12
+ );
13
+ if (templateResult instanceof Response)
14
+ return templateResult;
15
+ let str = "";
16
+ let renderedFirstPageChunk = false;
17
+ const destination = {
18
+ write(chunk) {
19
+ if (isPage && !renderedFirstPageChunk) {
20
+ renderedFirstPageChunk = true;
21
+ if (!/<!doctype html/i.test(String(chunk))) {
22
+ const doctype = result.compressHTML ? "<!DOCTYPE html>" : "<!DOCTYPE html>\n";
23
+ str += doctype;
24
+ }
25
+ }
26
+ if (chunk instanceof Response)
27
+ return;
28
+ str += chunkToString(result, chunk);
29
+ }
30
+ };
31
+ for await (const chunk of renderAstroTemplateResult(templateResult)) {
32
+ destination.write(chunk);
33
+ }
34
+ return str;
35
+ }
36
+ async function renderToReadableStream(result, componentFactory, props, children, isPage = false, route) {
37
+ const templateResult = await callComponentAsTemplateResultOrResponse(
38
+ result,
39
+ componentFactory,
40
+ props,
41
+ children,
42
+ route
43
+ );
44
+ if (templateResult instanceof Response)
45
+ return templateResult;
46
+ if (isPage) {
47
+ await bufferHeadContent(result);
48
+ }
49
+ let renderedFirstPageChunk = false;
50
+ return new ReadableStream({
51
+ start(controller) {
52
+ const destination = {
53
+ write(chunk) {
54
+ if (isPage && !renderedFirstPageChunk) {
55
+ renderedFirstPageChunk = true;
56
+ if (!/<!doctype html/i.test(String(chunk))) {
57
+ const doctype = result.compressHTML ? "<!DOCTYPE html>" : "<!DOCTYPE html>\n";
58
+ controller.enqueue(encoder.encode(doctype));
59
+ }
60
+ }
61
+ if (chunk instanceof Response) {
62
+ throw new AstroError({
63
+ ...AstroErrorData.ResponseSentError
64
+ });
65
+ }
66
+ const bytes = chunkToByteArray(result, chunk);
67
+ controller.enqueue(bytes);
68
+ }
69
+ };
70
+ (async () => {
71
+ try {
72
+ for await (const chunk of renderAstroTemplateResult(templateResult)) {
73
+ destination.write(chunk);
74
+ }
75
+ controller.close();
76
+ } catch (e) {
77
+ if (AstroError.is(e) && !e.loc) {
78
+ e.setLocation({
79
+ file: route == null ? void 0 : route.component
80
+ });
81
+ }
82
+ controller.error(e);
83
+ }
84
+ })();
85
+ }
86
+ });
87
+ }
88
+ async function callComponentAsTemplateResultOrResponse(result, componentFactory, props, children, route) {
89
+ const factoryResult = await componentFactory(result, props, children);
90
+ if (factoryResult instanceof Response) {
91
+ return factoryResult;
92
+ } else if (!isRenderTemplateResult(factoryResult)) {
93
+ throw new AstroError({
94
+ ...AstroErrorData.OnlyResponseCanBeReturned,
95
+ message: AstroErrorData.OnlyResponseCanBeReturned.message(route == null ? void 0 : route.route, typeof factoryResult),
96
+ location: {
97
+ file: route == null ? void 0 : route.component
98
+ }
99
+ });
100
+ }
101
+ return isHeadAndContent(factoryResult) ? factoryResult.content : factoryResult;
102
+ }
103
+ async function bufferHeadContent(result) {
104
+ const iterator = result._metadata.propagators.values();
105
+ while (true) {
106
+ const { value, done } = iterator.next();
107
+ if (done) {
108
+ break;
109
+ }
110
+ const returnValue = await value.init(result);
111
+ if (isHeadAndContent(returnValue)) {
112
+ result._metadata.extraHead.push(returnValue.head);
113
+ }
114
+ }
115
+ }
116
+ export {
117
+ renderToReadableStream,
118
+ renderToString
119
+ };
@@ -2,6 +2,13 @@ import type { SSRResult } from '../../../@types/astro';
2
2
  import type { RenderInstruction } from './types.js';
3
3
  import { HTMLBytes } from '../escape.js';
4
4
  import { type SlotString } from './slot.js';
5
+ export interface RenderDestination {
6
+ /**
7
+ * Any rendering logic should call this to construct the HTML output.
8
+ * See the `chunk` parameter for possible writable values
9
+ */
10
+ write(chunk: string | HTMLBytes | RenderInstruction | Response): void;
11
+ }
5
12
  export declare const Fragment: unique symbol;
6
13
  export declare const Renderer: unique symbol;
7
14
  export declare const encoder: TextEncoder;
@@ -14,4 +21,5 @@ export declare class HTMLParts {
14
21
  toString(): string;
15
22
  toArrayBuffer(): Uint8Array;
16
23
  }
24
+ export declare function chunkToString(result: SSRResult, chunk: string | HTMLBytes | RenderInstruction): string;
17
25
  export declare function chunkToByteArray(result: SSRResult, chunk: string | HTMLBytes | RenderInstruction): Uint8Array;
@@ -78,18 +78,26 @@ class HTMLParts {
78
78
  return encoder.encode(this.parts);
79
79
  }
80
80
  }
81
+ function chunkToString(result, chunk) {
82
+ if (ArrayBuffer.isView(chunk)) {
83
+ return decoder.decode(chunk);
84
+ } else {
85
+ return stringifyChunk(result, chunk);
86
+ }
87
+ }
81
88
  function chunkToByteArray(result, chunk) {
82
- if (chunk instanceof Uint8Array) {
89
+ if (ArrayBuffer.isView(chunk)) {
83
90
  return chunk;
91
+ } else {
92
+ return encoder.encode(stringifyChunk(result, chunk));
84
93
  }
85
- let stringified = stringifyChunk(result, chunk);
86
- return encoder.encode(stringified.toString());
87
94
  }
88
95
  export {
89
96
  Fragment,
90
97
  HTMLParts,
91
98
  Renderer,
92
99
  chunkToByteArray,
100
+ chunkToString,
93
101
  decoder,
94
102
  encoder,
95
103
  stringifyChunk
@@ -1,14 +1,9 @@
1
- import { AstroError, AstroErrorData } from "../../../core/errors/index.js";
1
+ import { AstroError } from "../../../core/errors/index.js";
2
2
  import { isHTMLString } from "../escape.js";
3
3
  import { createResponse } from "../response.js";
4
- import {
5
- isAstroComponentFactory,
6
- isAstroComponentInstance,
7
- isHeadAndContent,
8
- isRenderTemplateResult,
9
- renderAstroTemplateResult
10
- } from "./astro/index.js";
11
- import { HTMLParts, chunkToByteArray, encoder } from "./common.js";
4
+ import { isAstroComponentFactory, isAstroComponentInstance } from "./astro/index.js";
5
+ import { renderToReadableStream, renderToString } from "./astro/render.js";
6
+ import { HTMLParts, encoder } from "./common.js";
12
7
  import { renderComponent } from "./component.js";
13
8
  import { maybeRenderHead } from "./head.js";
14
9
  const needsHeadRenderingSymbol = Symbol.for("astro.needsHeadRendering");
@@ -34,19 +29,6 @@ async function iterableToHTMLBytes(result, iterable, onDocTypeInjection) {
34
29
  }
35
30
  return parts.toArrayBuffer();
36
31
  }
37
- async function bufferHeadContent(result) {
38
- const iterator = result._metadata.propagators.values();
39
- while (true) {
40
- const { value, done } = iterator.next();
41
- if (done) {
42
- break;
43
- }
44
- const returnValue = await value.init(result);
45
- if (isHeadAndContent(returnValue)) {
46
- result._metadata.extraHead.push(returnValue.head);
47
- }
48
- }
49
- }
50
32
  async function renderPage(result, componentFactory, props, children, streaming, route) {
51
33
  var _a, _b;
52
34
  if (!isAstroComponentFactory(componentFactory)) {
@@ -93,75 +75,22 @@ async function renderPage(result, componentFactory, props, children, streaming,
93
75
  });
94
76
  }
95
77
  result._metadata.headInTree = ((_b = result.componentMetadata.get(componentFactory.moduleId)) == null ? void 0 : _b.containsHead) ?? false;
96
- const factoryReturnValue = await componentFactory(result, props, children);
97
- const factoryIsHeadAndContent = isHeadAndContent(factoryReturnValue);
98
- if (isRenderTemplateResult(factoryReturnValue) || factoryIsHeadAndContent) {
99
- await bufferHeadContent(result);
100
- const templateResult = factoryIsHeadAndContent ? factoryReturnValue.content : factoryReturnValue;
101
- let iterable = renderAstroTemplateResult(templateResult);
102
- let init = result.response;
103
- let headers = new Headers(init.headers);
104
- let body;
105
- if (streaming) {
106
- body = new ReadableStream({
107
- start(controller) {
108
- async function read() {
109
- let i = 0;
110
- try {
111
- for await (const chunk of iterable) {
112
- if (isHTMLString(chunk)) {
113
- if (i === 0) {
114
- if (!/<!doctype html/i.test(String(chunk))) {
115
- controller.enqueue(
116
- encoder.encode(
117
- `${result.compressHTML ? "<!DOCTYPE html>" : "<!DOCTYPE html>\n"}`
118
- )
119
- );
120
- }
121
- }
122
- }
123
- if (chunk instanceof Response) {
124
- throw new AstroError({
125
- ...AstroErrorData.ResponseSentError
126
- });
127
- }
128
- const bytes = chunkToByteArray(result, chunk);
129
- controller.enqueue(bytes);
130
- i++;
131
- }
132
- controller.close();
133
- } catch (e) {
134
- if (AstroError.is(e) && !e.loc) {
135
- e.setLocation({
136
- file: route == null ? void 0 : route.component
137
- });
138
- }
139
- controller.error(e);
140
- }
141
- }
142
- read();
143
- }
144
- });
145
- } else {
146
- body = await iterableToHTMLBytes(result, iterable);
147
- headers.set("Content-Length", body.byteLength.toString());
148
- }
149
- let response = createResponse(body, { ...init, headers });
150
- return response;
78
+ let body;
79
+ if (streaming) {
80
+ body = await renderToReadableStream(result, componentFactory, props, children, true, route);
81
+ } else {
82
+ body = await renderToString(result, componentFactory, props, children, true, route);
151
83
  }
152
- if (!(factoryReturnValue instanceof Response)) {
153
- throw new AstroError({
154
- ...AstroErrorData.OnlyResponseCanBeReturned,
155
- message: AstroErrorData.OnlyResponseCanBeReturned.message(
156
- route == null ? void 0 : route.route,
157
- typeof factoryReturnValue
158
- ),
159
- location: {
160
- file: route == null ? void 0 : route.component
161
- }
162
- });
84
+ if (body instanceof Response)
85
+ return body;
86
+ const init = result.response;
87
+ const headers = new Headers(init.headers);
88
+ if (!streaming && typeof body === "string") {
89
+ body = encoder.encode(body);
90
+ headers.set("Content-Length", body.byteLength.toString());
163
91
  }
164
- return factoryReturnValue;
92
+ const response = createResponse(body, { ...init, headers });
93
+ return response;
165
94
  }
166
95
  export {
167
96
  renderPage
@@ -118,7 +118,7 @@ function markdown({ settings, logging }) {
118
118
 
119
119
  function updateImageReferences(html) {
120
120
  return html.replaceAll(
121
- /__ASTRO_IMAGE_="(.+)"/gm,
121
+ /__ASTRO_IMAGE_="([^"]+)"/gm,
122
122
  (full, imagePath) => spreadAttributes({src: images[imagePath].src, ...images[imagePath].attributes})
123
123
  );
124
124
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "2.9.0",
3
+ "version": "2.9.1",
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",