astro 4.8.3 → 4.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/astro-jsx.d.ts +3 -0
- package/client.d.ts +1 -1
- package/config.d.ts +5 -1
- package/dist/@types/astro.d.ts +7 -5
- package/dist/actions/index.js +8 -1
- package/dist/actions/runtime/middleware.js +4 -1
- package/dist/actions/runtime/route.js +20 -9
- package/dist/actions/runtime/utils.d.ts +1 -1
- package/dist/actions/runtime/utils.js +2 -2
- package/dist/actions/runtime/virtual/shared.d.ts +1 -0
- package/dist/actions/runtime/virtual/shared.js +14 -7
- package/dist/assets/utils/metadata.js +17 -10
- package/dist/core/app/index.js +12 -0
- package/dist/core/app/pipeline.d.ts +1 -1
- package/dist/core/app/pipeline.js +7 -4
- package/dist/core/base-pipeline.d.ts +1 -1
- package/dist/core/build/internal.d.ts +8 -0
- package/dist/core/build/internal.js +15 -1
- package/dist/core/build/pipeline.d.ts +1 -1
- package/dist/core/build/pipeline.js +7 -4
- package/dist/core/build/plugins/plugin-analyzer.js +15 -4
- package/dist/core/build/plugins/plugin-css.js +14 -3
- package/dist/core/constants.js +1 -1
- package/dist/core/dev/dev.js +2 -1
- package/dist/core/errors/errors-data.d.ts +13 -0
- package/dist/core/errors/errors-data.js +7 -0
- package/dist/core/messages.js +2 -2
- package/dist/core/render-context.js +3 -3
- package/dist/core/routing/manifest/generator.d.ts +1 -1
- package/dist/core/routing/manifest/generator.js +12 -1
- package/dist/i18n/index.d.ts +1 -0
- package/dist/i18n/index.js +6 -1
- package/dist/i18n/middleware.js +5 -1
- package/dist/prefetch/index.d.ts +6 -2
- package/dist/prefetch/index.js +10 -12
- package/dist/runtime/server/render/astro/render.js +13 -6
- package/dist/runtime/server/render/script.js +6 -2
- package/dist/vite-plugin-astro-server/pipeline.d.ts +1 -1
- package/dist/vite-plugin-astro-server/pipeline.js +7 -4
- package/package.json +4 -2
- package/templates/actions.mjs +4 -0
package/astro-jsx.d.ts
CHANGED
|
@@ -605,6 +605,7 @@ declare namespace astroHTML.JSX {
|
|
|
605
605
|
href?: string | URL | undefined | null;
|
|
606
606
|
hreflang?: string | undefined | null;
|
|
607
607
|
media?: string | undefined | null;
|
|
608
|
+
name?: string | undefined | null;
|
|
608
609
|
ping?: string | undefined | null;
|
|
609
610
|
rel?: string | undefined | null;
|
|
610
611
|
target?: HTMLAttributeAnchorTarget | undefined | null;
|
|
@@ -649,6 +650,7 @@ declare namespace astroHTML.JSX {
|
|
|
649
650
|
type?: 'submit' | 'reset' | 'button' | undefined | null;
|
|
650
651
|
value?: string | string[] | number | undefined | null;
|
|
651
652
|
popovertarget?: string | undefined | null;
|
|
653
|
+
popovertargetaction?: 'hide' | 'show' | 'toggle' | undefined | null;
|
|
652
654
|
}
|
|
653
655
|
|
|
654
656
|
interface CanvasHTMLAttributes extends HTMLAttributes {
|
|
@@ -815,6 +817,7 @@ declare namespace astroHTML.JSX {
|
|
|
815
817
|
value?: string | string[] | number | undefined | null;
|
|
816
818
|
width?: number | string | undefined | null;
|
|
817
819
|
popovertarget?: string | undefined | null;
|
|
820
|
+
popovertargetaction?: 'hide' | 'show' | 'toggle' | undefined | null;
|
|
818
821
|
}
|
|
819
822
|
|
|
820
823
|
interface KeygenHTMLAttributes extends HTMLAttributes {
|
package/client.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ interface ImportMetaEnv {
|
|
|
12
12
|
/**
|
|
13
13
|
* The prefix for Astro-generated asset links if the build.assetsPrefix config option is set. This can be used to create asset links not handled by Astro.
|
|
14
14
|
*/
|
|
15
|
-
readonly ASSETS_PREFIX: string
|
|
15
|
+
readonly ASSETS_PREFIX: string | Record<string, string>;
|
|
16
16
|
/**
|
|
17
17
|
* This is set to the site option specified in your project’s Astro config file.
|
|
18
18
|
*/
|
package/config.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
type ViteUserConfig = import('vite').UserConfig;
|
|
2
2
|
type ViteUserConfigFn = import('vite').UserConfigFn;
|
|
3
3
|
type AstroUserConfig = import('./dist/@types/astro.js').AstroUserConfig;
|
|
4
|
+
type AstroInlineConfig = import('./dist/@types/astro.js').AstroInlineConfig;
|
|
4
5
|
type ImageServiceConfig = import('./dist/@types/astro.js').ImageServiceConfig;
|
|
5
6
|
type SharpImageServiceConfig = import('./dist/assets/services/sharp.js').SharpImageServiceConfig;
|
|
6
7
|
|
|
@@ -13,7 +14,10 @@ export function defineConfig(config: AstroUserConfig): AstroUserConfig;
|
|
|
13
14
|
/**
|
|
14
15
|
* Use Astro to generate a fully resolved Vite config
|
|
15
16
|
*/
|
|
16
|
-
export function getViteConfig(
|
|
17
|
+
export function getViteConfig(
|
|
18
|
+
config: ViteUserConfig,
|
|
19
|
+
inlineAstroConfig?: AstroInlineConfig
|
|
20
|
+
): ViteUserConfigFn;
|
|
17
21
|
|
|
18
22
|
/**
|
|
19
23
|
* Return the configuration needed to use the Sharp-based image service
|
package/dist/@types/astro.d.ts
CHANGED
|
@@ -1612,7 +1612,7 @@ export interface AstroUserConfig {
|
|
|
1612
1612
|
*
|
|
1613
1613
|
* Declare all your actions in `src/actions/index.ts`. This file is the global actions handler.
|
|
1614
1614
|
*
|
|
1615
|
-
* Define an action using the `defineAction()` utility from the `astro:actions` module.
|
|
1615
|
+
* Define an action using the `defineAction()` utility from the `astro:actions` module. An action accepts the `handler` property to define your server-side request handler. If your action accepts arguments, apply the `input` property to validate parameters with Zod.
|
|
1616
1616
|
*
|
|
1617
1617
|
* This example defines two actions: `like` and `comment`. The `like` action accepts a JSON object with a `postId` string, while the `comment` action accepts [FormData](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest_API/Using_FormData_Objects) with `postId`, `author`, and `body` strings. Each `handler` updates your database and return a type-safe response.
|
|
1618
1618
|
*
|
|
@@ -1645,12 +1645,14 @@ export interface AstroUserConfig {
|
|
|
1645
1645
|
* };
|
|
1646
1646
|
* ```
|
|
1647
1647
|
*
|
|
1648
|
-
* Then, call an action from your client components using the `actions` object from `astro:actions`. You can pass a type-safe object when using JSON, or a [FormData](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest_API/Using_FormData_Objects) object when using `accept: 'form'` in your action definition
|
|
1648
|
+
* Then, call an action from your client components using the `actions` object from `astro:actions`. You can pass a type-safe object when using JSON, or a [FormData](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest_API/Using_FormData_Objects) object when using `accept: 'form'` in your action definition.
|
|
1649
|
+
*
|
|
1650
|
+
* This example calls the `like` and `comment` actions from a React component:
|
|
1649
1651
|
*
|
|
1650
1652
|
* ```tsx "actions"
|
|
1651
1653
|
* // src/components/blog.tsx
|
|
1652
1654
|
* import { actions } from "astro:actions";
|
|
1653
|
-
* import { useState } from "
|
|
1655
|
+
* import { useState } from "react";
|
|
1654
1656
|
*
|
|
1655
1657
|
* export function Like({ postId }: { postId: string }) {
|
|
1656
1658
|
* const [likes, setLikes] = useState(0);
|
|
@@ -1671,13 +1673,13 @@ export interface AstroUserConfig {
|
|
|
1671
1673
|
* <form
|
|
1672
1674
|
* onSubmit={async (e) => {
|
|
1673
1675
|
* e.preventDefault();
|
|
1674
|
-
* const formData = new FormData(e.target);
|
|
1676
|
+
* const formData = new FormData(e.target as HTMLFormElement);
|
|
1675
1677
|
* const result = await actions.blog.comment(formData);
|
|
1676
1678
|
* // handle result
|
|
1677
1679
|
* }}
|
|
1678
1680
|
* >
|
|
1679
1681
|
* <input type="hidden" name="postId" value={postId} />
|
|
1680
|
-
* <label
|
|
1682
|
+
* <label htmlFor="author">Author</label>
|
|
1681
1683
|
* <input id="author" type="text" name="author" />
|
|
1682
1684
|
* <textarea rows={10} name="body"></textarea>
|
|
1683
1685
|
* <button type="submit">Post</button>
|
package/dist/actions/index.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
-
import {
|
|
2
|
+
import { ActionsWithoutServerOutputError } from "../core/errors/errors-data.js";
|
|
3
|
+
import { AstroError } from "../core/errors/errors.js";
|
|
4
|
+
import { isServerLikeOutput, viteID } from "../core/util.js";
|
|
3
5
|
import { ACTIONS_TYPES_FILE, RESOLVED_VIRTUAL_MODULE_ID, VIRTUAL_MODULE_ID } from "./consts.js";
|
|
4
6
|
function astroActions() {
|
|
5
7
|
return {
|
|
6
8
|
name: VIRTUAL_MODULE_ID,
|
|
7
9
|
hooks: {
|
|
8
10
|
async "astro:config:setup"(params) {
|
|
11
|
+
if (!isServerLikeOutput(params.config)) {
|
|
12
|
+
const error = new AstroError(ActionsWithoutServerOutputError);
|
|
13
|
+
error.stack = void 0;
|
|
14
|
+
throw error;
|
|
15
|
+
}
|
|
9
16
|
const stringifiedActionsImport = JSON.stringify(
|
|
10
17
|
viteID(new URL("./actions", params.config.srcDir))
|
|
11
18
|
);
|
|
@@ -4,16 +4,19 @@ import { formContentTypes, getAction, hasContentType } from "./utils.js";
|
|
|
4
4
|
import { callSafely } from "./virtual/shared.js";
|
|
5
5
|
const onRequest = defineMiddleware(async (context, next) => {
|
|
6
6
|
const locals = context.locals;
|
|
7
|
+
if (locals._actionsInternal) return next();
|
|
7
8
|
const { request, url } = context;
|
|
8
9
|
const contentType = request.headers.get("Content-Type");
|
|
9
10
|
if (url.pathname.startsWith("/_actions")) return nextWithLocalsStub(next, locals);
|
|
10
|
-
if (!contentType || !hasContentType(contentType, formContentTypes))
|
|
11
|
+
if (!contentType || !hasContentType(contentType, formContentTypes)) {
|
|
11
12
|
return nextWithLocalsStub(next, locals);
|
|
13
|
+
}
|
|
12
14
|
const formData = await request.clone().formData();
|
|
13
15
|
const actionPath = formData.get("_astroAction");
|
|
14
16
|
if (typeof actionPath !== "string") return nextWithLocalsStub(next, locals);
|
|
15
17
|
const actionPathKeys = actionPath.replace("/_actions/", "").split(".");
|
|
16
18
|
const action = await getAction(actionPathKeys);
|
|
19
|
+
if (!action) return nextWithLocalsStub(next, locals);
|
|
17
20
|
const result = await ApiContextStorage.run(context, () => callSafely(() => action(formData)));
|
|
18
21
|
const actionsInternal = {
|
|
19
22
|
getActionResult: (actionFn) => {
|
|
@@ -5,9 +5,15 @@ const POST = async (context) => {
|
|
|
5
5
|
const { request, url } = context;
|
|
6
6
|
const actionPathKeys = url.pathname.replace("/_actions/", "").split(".");
|
|
7
7
|
const action = await getAction(actionPathKeys);
|
|
8
|
+
if (!action) {
|
|
9
|
+
return new Response(null, { status: 404 });
|
|
10
|
+
}
|
|
8
11
|
const contentType = request.headers.get("Content-Type");
|
|
12
|
+
const contentLength = request.headers.get("Content-Length");
|
|
9
13
|
let args;
|
|
10
|
-
if (
|
|
14
|
+
if (contentLength === "0") {
|
|
15
|
+
args = void 0;
|
|
16
|
+
} else if (contentType && hasContentType(contentType, formContentTypes)) {
|
|
11
17
|
args = await request.clone().formData();
|
|
12
18
|
} else if (contentType && hasContentType(contentType, ["application/json"])) {
|
|
13
19
|
args = await request.clone().json();
|
|
@@ -16,17 +22,22 @@ const POST = async (context) => {
|
|
|
16
22
|
}
|
|
17
23
|
const result = await ApiContextStorage.run(context, () => callSafely(() => action(args)));
|
|
18
24
|
if (result.error) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
return new Response(
|
|
26
|
+
JSON.stringify({
|
|
27
|
+
...result.error,
|
|
28
|
+
message: result.error.message,
|
|
29
|
+
stack: import.meta.env.PROD ? void 0 : result.error.stack
|
|
30
|
+
}),
|
|
31
|
+
{
|
|
32
|
+
status: result.error.status,
|
|
33
|
+
headers: {
|
|
34
|
+
"Content-Type": "application/json"
|
|
35
|
+
}
|
|
26
36
|
}
|
|
27
|
-
|
|
37
|
+
);
|
|
28
38
|
}
|
|
29
39
|
return new Response(JSON.stringify(result.data), {
|
|
40
|
+
status: result.data ? 200 : 204,
|
|
30
41
|
headers: {
|
|
31
42
|
"Content-Type": "application/json"
|
|
32
43
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export declare const formContentTypes: string[];
|
|
2
2
|
export declare function hasContentType(contentType: string, expected: string[]): boolean;
|
|
3
3
|
export type MaybePromise<T> = T | Promise<T>;
|
|
4
|
-
export declare function getAction(pathKeys: string[]): Promise<(param: unknown) => MaybePromise<unknown
|
|
4
|
+
export declare function getAction(pathKeys: string[]): Promise<((param: unknown) => MaybePromise<unknown>) | undefined>;
|
|
@@ -7,12 +7,12 @@ async function getAction(pathKeys) {
|
|
|
7
7
|
let { server: actionLookup } = await import(import.meta.env.ACTIONS_PATH);
|
|
8
8
|
for (const key of pathKeys) {
|
|
9
9
|
if (!(key in actionLookup)) {
|
|
10
|
-
|
|
10
|
+
return void 0;
|
|
11
11
|
}
|
|
12
12
|
actionLookup = actionLookup[key];
|
|
13
13
|
}
|
|
14
14
|
if (typeof actionLookup !== "function") {
|
|
15
|
-
|
|
15
|
+
return void 0;
|
|
16
16
|
}
|
|
17
17
|
return actionLookup;
|
|
18
18
|
}
|
|
@@ -9,6 +9,7 @@ export declare class ActionError<T extends ErrorInferenceObject = ErrorInference
|
|
|
9
9
|
constructor(params: {
|
|
10
10
|
message?: string;
|
|
11
11
|
code: ActionErrorCode;
|
|
12
|
+
stack?: string;
|
|
12
13
|
});
|
|
13
14
|
static codeToStatus(code: ActionErrorCode): number;
|
|
14
15
|
static statusToCode(status: number): ActionErrorCode;
|
|
@@ -28,6 +28,9 @@ class ActionError extends Error {
|
|
|
28
28
|
super(params.message);
|
|
29
29
|
this.code = params.code;
|
|
30
30
|
this.status = ActionError.codeToStatus(params.code);
|
|
31
|
+
if (params.stack) {
|
|
32
|
+
this.stack = params.stack;
|
|
33
|
+
}
|
|
31
34
|
}
|
|
32
35
|
static codeToStatus(code) {
|
|
33
36
|
return codeToStatusMap[code];
|
|
@@ -36,15 +39,16 @@ class ActionError extends Error {
|
|
|
36
39
|
return statusToCodeMap[status] ?? "INTERNAL_SERVER_ERROR";
|
|
37
40
|
}
|
|
38
41
|
static async fromResponse(res) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
const body = await res.clone().json();
|
|
43
|
+
if (typeof body === "object" && body?.type === "AstroActionInputError" && Array.isArray(body.issues)) {
|
|
44
|
+
return new ActionInputError(body.issues);
|
|
45
|
+
}
|
|
46
|
+
if (typeof body === "object" && body?.type === "AstroActionError") {
|
|
47
|
+
return new ActionError(body);
|
|
44
48
|
}
|
|
45
49
|
return new ActionError({
|
|
46
50
|
message: res.statusText,
|
|
47
|
-
code:
|
|
51
|
+
code: ActionError.statusToCode(res.status)
|
|
48
52
|
});
|
|
49
53
|
}
|
|
50
54
|
}
|
|
@@ -59,7 +63,10 @@ class ActionInputError extends ActionError {
|
|
|
59
63
|
issues;
|
|
60
64
|
fields;
|
|
61
65
|
constructor(issues) {
|
|
62
|
-
super({
|
|
66
|
+
super({
|
|
67
|
+
message: `Failed to validate: ${JSON.stringify(issues, null, 2)}`,
|
|
68
|
+
code: "BAD_REQUEST"
|
|
69
|
+
});
|
|
63
70
|
this.issues = issues;
|
|
64
71
|
this.fields = {};
|
|
65
72
|
for (const issue of issues) {
|
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
import { AstroError, AstroErrorData } from "../../core/errors/index.js";
|
|
2
2
|
import { lookup as probe } from "../utils/vendor/image-size/lookup.js";
|
|
3
3
|
async function imageMetadata(data, src) {
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
try {
|
|
5
|
+
const result = probe(data);
|
|
6
|
+
if (!result.height || !result.width || !result.type) {
|
|
7
|
+
throw new AstroError({
|
|
8
|
+
...AstroErrorData.NoImageMetadata,
|
|
9
|
+
message: AstroErrorData.NoImageMetadata.message(src)
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
const { width, height, type, orientation } = result;
|
|
13
|
+
const isPortrait = (orientation || 0) >= 5;
|
|
14
|
+
return {
|
|
15
|
+
width: isPortrait ? height : width,
|
|
16
|
+
height: isPortrait ? width : height,
|
|
17
|
+
format: type,
|
|
18
|
+
orientation
|
|
19
|
+
};
|
|
20
|
+
} catch (e) {
|
|
6
21
|
throw new AstroError({
|
|
7
22
|
...AstroErrorData.NoImageMetadata,
|
|
8
23
|
message: AstroErrorData.NoImageMetadata.message(src)
|
|
9
24
|
});
|
|
10
25
|
}
|
|
11
|
-
const { width, height, type, orientation } = result;
|
|
12
|
-
const isPortrait = (orientation || 0) >= 5;
|
|
13
|
-
return {
|
|
14
|
-
width: isPortrait ? height : width,
|
|
15
|
-
height: isPortrait ? width : height,
|
|
16
|
-
format: type,
|
|
17
|
-
orientation
|
|
18
|
-
};
|
|
19
26
|
}
|
|
20
27
|
export {
|
|
21
28
|
imageMetadata
|
package/dist/core/app/index.js
CHANGED
|
@@ -186,6 +186,14 @@ class App {
|
|
|
186
186
|
this.#logRenderOptionsDeprecationWarning();
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
|
+
if (routeData) {
|
|
190
|
+
this.#logger.debug(
|
|
191
|
+
"router",
|
|
192
|
+
"The adapter " + this.#manifest.adapterName + " provided a custom RouteData for ",
|
|
193
|
+
request.url
|
|
194
|
+
);
|
|
195
|
+
this.#logger.debug("router", "RouteData:\n" + routeData);
|
|
196
|
+
}
|
|
189
197
|
if (locals) {
|
|
190
198
|
if (typeof locals !== "object") {
|
|
191
199
|
this.#logger.error(null, new AstroError(AstroErrorData.LocalsNotAnObject).stack);
|
|
@@ -201,8 +209,12 @@ class App {
|
|
|
201
209
|
}
|
|
202
210
|
if (!routeData) {
|
|
203
211
|
routeData = this.match(request);
|
|
212
|
+
this.#logger.debug("router", "Astro matched the following route for " + request.url);
|
|
213
|
+
this.#logger.debug("router", "RouteData:\n" + routeData);
|
|
204
214
|
}
|
|
205
215
|
if (!routeData) {
|
|
216
|
+
this.#logger.debug("router", "Astro hasn't found routes that match " + request.url);
|
|
217
|
+
this.#logger.debug("router", "Here's the available routes:\n", this.#manifestData);
|
|
206
218
|
return this.#renderError(request, { locals, status: 404 });
|
|
207
219
|
}
|
|
208
220
|
const pathname = this.#getPathnameFromRequest(request);
|
|
@@ -7,6 +7,6 @@ export declare class AppPipeline extends Pipeline {
|
|
|
7
7
|
headElements(routeData: RouteData): Pick<SSRResult, 'scripts' | 'styles' | 'links'>;
|
|
8
8
|
componentMetadata(): void;
|
|
9
9
|
getComponentByRoute(routeData: RouteData): Promise<ComponentInstance>;
|
|
10
|
-
tryRewrite(payload: RewritePayload): Promise<[RouteData, ComponentInstance]>;
|
|
10
|
+
tryRewrite(payload: RewritePayload, request: Request): Promise<[RouteData, ComponentInstance]>;
|
|
11
11
|
getModuleForRoute(route: RouteData): Promise<SinglePageBuiltModule>;
|
|
12
12
|
}
|
|
@@ -50,7 +50,7 @@ class AppPipeline extends Pipeline {
|
|
|
50
50
|
const module = await this.getModuleForRoute(routeData);
|
|
51
51
|
return module.page();
|
|
52
52
|
}
|
|
53
|
-
async tryRewrite(payload) {
|
|
53
|
+
async tryRewrite(payload, request) {
|
|
54
54
|
let foundRoute;
|
|
55
55
|
for (const route of this.#manifestData.routes) {
|
|
56
56
|
if (payload instanceof URL) {
|
|
@@ -64,9 +64,12 @@ class AppPipeline extends Pipeline {
|
|
|
64
64
|
foundRoute = route;
|
|
65
65
|
break;
|
|
66
66
|
}
|
|
67
|
-
} else
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
} else {
|
|
68
|
+
const newUrl = new URL(payload, new URL(request.url).origin);
|
|
69
|
+
if (route.pattern.test(decodeURI(newUrl.pathname))) {
|
|
70
|
+
foundRoute = route;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
70
73
|
}
|
|
71
74
|
}
|
|
72
75
|
if (foundRoute) {
|
|
@@ -64,7 +64,7 @@ export declare abstract class Pipeline {
|
|
|
64
64
|
*
|
|
65
65
|
* @param {RewritePayload} rewritePayload
|
|
66
66
|
*/
|
|
67
|
-
abstract tryRewrite(rewritePayload: RewritePayload): Promise<[RouteData, ComponentInstance]>;
|
|
67
|
+
abstract tryRewrite(rewritePayload: RewritePayload, request: Request): Promise<[RouteData, ComponentInstance]>;
|
|
68
68
|
/**
|
|
69
69
|
* Tells the pipeline how to retrieve a component give a `RouteData`
|
|
70
70
|
* @param routeData
|
|
@@ -40,6 +40,10 @@ export interface BuildInternals {
|
|
|
40
40
|
* A map for page-specific information by a client:only component
|
|
41
41
|
*/
|
|
42
42
|
pagesByClientOnly: Map<string, Set<PageBuildData>>;
|
|
43
|
+
/**
|
|
44
|
+
* A map for page-specific information by a script in an Astro file
|
|
45
|
+
*/
|
|
46
|
+
pagesByScriptId: Map<string, Set<PageBuildData>>;
|
|
43
47
|
/**
|
|
44
48
|
* A map of hydrated components to export names that are discovered during the SSR build.
|
|
45
49
|
* These will be used as the top-level entrypoints for the client build.
|
|
@@ -96,6 +100,10 @@ export declare function trackPageData(internals: BuildInternals, component: stri
|
|
|
96
100
|
* Tracks client-only components to the pages they are associated with.
|
|
97
101
|
*/
|
|
98
102
|
export declare function trackClientOnlyPageDatas(internals: BuildInternals, pageData: PageBuildData, clientOnlys: string[]): void;
|
|
103
|
+
/**
|
|
104
|
+
* Tracks scripts to the pages they are associated with. (experimental.directRenderScript)
|
|
105
|
+
*/
|
|
106
|
+
export declare function trackScriptPageDatas(internals: BuildInternals, pageData: PageBuildData, scriptIds: string[]): void;
|
|
99
107
|
export declare function getPageDatasByChunk(internals: BuildInternals, chunk: Rollup.RenderedChunk): Generator<PageBuildData, void, unknown>;
|
|
100
108
|
export declare function getPageDatasByClientOnlyID(internals: BuildInternals, viteid: ViteID): Generator<PageBuildData, void, unknown>;
|
|
101
109
|
/**
|
|
@@ -16,6 +16,7 @@ function createBuildInternals() {
|
|
|
16
16
|
pageOptionsByPage: /* @__PURE__ */ new Map(),
|
|
17
17
|
pagesByViteID: /* @__PURE__ */ new Map(),
|
|
18
18
|
pagesByClientOnly: /* @__PURE__ */ new Map(),
|
|
19
|
+
pagesByScriptId: /* @__PURE__ */ new Map(),
|
|
19
20
|
propagatedStylesMap: /* @__PURE__ */ new Map(),
|
|
20
21
|
propagatedScriptsMap: /* @__PURE__ */ new Map(),
|
|
21
22
|
discoveredHydratedComponents: /* @__PURE__ */ new Map(),
|
|
@@ -45,6 +46,18 @@ function trackClientOnlyPageDatas(internals, pageData, clientOnlys) {
|
|
|
45
46
|
pageDataSet.add(pageData);
|
|
46
47
|
}
|
|
47
48
|
}
|
|
49
|
+
function trackScriptPageDatas(internals, pageData, scriptIds) {
|
|
50
|
+
for (const scriptId of scriptIds) {
|
|
51
|
+
let pageDataSet;
|
|
52
|
+
if (internals.pagesByScriptId.has(scriptId)) {
|
|
53
|
+
pageDataSet = internals.pagesByScriptId.get(scriptId);
|
|
54
|
+
} else {
|
|
55
|
+
pageDataSet = /* @__PURE__ */ new Set();
|
|
56
|
+
internals.pagesByScriptId.set(scriptId, pageDataSet);
|
|
57
|
+
}
|
|
58
|
+
pageDataSet.add(pageData);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
48
61
|
function* getPageDatasByChunk(internals, chunk) {
|
|
49
62
|
const pagesByViteID = internals.pagesByViteID;
|
|
50
63
|
for (const [modulePath] of Object.entries(chunk.modules)) {
|
|
@@ -180,5 +193,6 @@ export {
|
|
|
180
193
|
hasPrerenderedPages,
|
|
181
194
|
mergeInlineCss,
|
|
182
195
|
trackClientOnlyPageDatas,
|
|
183
|
-
trackPageData
|
|
196
|
+
trackPageData,
|
|
197
|
+
trackScriptPageDatas
|
|
184
198
|
};
|
|
@@ -38,6 +38,6 @@ export declare class BuildPipeline extends Pipeline {
|
|
|
38
38
|
*/
|
|
39
39
|
retrieveRoutesToGenerate(): Map<PageBuildData, string>;
|
|
40
40
|
getComponentByRoute(routeData: RouteData): Promise<ComponentInstance>;
|
|
41
|
-
tryRewrite(payload: RewritePayload): Promise<[RouteData, ComponentInstance]>;
|
|
41
|
+
tryRewrite(payload: RewritePayload, request: Request): Promise<[RouteData, ComponentInstance]>;
|
|
42
42
|
retrieveSsrEntry(route: RouteData, filePath: string): Promise<SinglePageBuiltModule>;
|
|
43
43
|
}
|
|
@@ -206,7 +206,7 @@ class BuildPipeline extends Pipeline {
|
|
|
206
206
|
return module.page();
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
|
-
async tryRewrite(payload) {
|
|
209
|
+
async tryRewrite(payload, request) {
|
|
210
210
|
let foundRoute;
|
|
211
211
|
for (const route of this.options.manifest.routes) {
|
|
212
212
|
if (payload instanceof URL) {
|
|
@@ -220,9 +220,12 @@ class BuildPipeline extends Pipeline {
|
|
|
220
220
|
foundRoute = route;
|
|
221
221
|
break;
|
|
222
222
|
}
|
|
223
|
-
} else
|
|
224
|
-
|
|
225
|
-
|
|
223
|
+
} else {
|
|
224
|
+
const newUrl = new URL(payload, new URL(request.url).origin);
|
|
225
|
+
if (route.pattern.test(decodeURI(newUrl.pathname))) {
|
|
226
|
+
foundRoute = route;
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
226
229
|
}
|
|
227
230
|
}
|
|
228
231
|
if (foundRoute) {
|
|
@@ -5,7 +5,11 @@ import {
|
|
|
5
5
|
getTopLevelPageModuleInfos,
|
|
6
6
|
moduleIsTopLevelPage
|
|
7
7
|
} from "../graph.js";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
getPageDataByViteID,
|
|
10
|
+
trackClientOnlyPageDatas,
|
|
11
|
+
trackScriptPageDatas
|
|
12
|
+
} from "../internal.js";
|
|
9
13
|
function isPropagatedAsset(id) {
|
|
10
14
|
try {
|
|
11
15
|
return new URL("file://" + id).searchParams.has(PROPAGATED_ASSET_FLAG);
|
|
@@ -120,9 +124,16 @@ function vitePluginAnalyzer(options, internals) {
|
|
|
120
124
|
}
|
|
121
125
|
}
|
|
122
126
|
if (options.settings.config.experimental.directRenderScript && astro.scripts.length) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
127
|
+
const scriptIds = astro.scripts.map(
|
|
128
|
+
(_, i) => `${id.replace("/@fs", "")}?astro&type=script&index=${i}&lang.ts`
|
|
129
|
+
);
|
|
130
|
+
for (const scriptId of scriptIds) {
|
|
131
|
+
internals.discoveredScripts.add(scriptId);
|
|
132
|
+
}
|
|
133
|
+
for (const pageInfo of getTopLevelPageModuleInfos(id, this)) {
|
|
134
|
+
const newPageData = getPageDataByViteID(internals, pageInfo.id);
|
|
135
|
+
if (!newPageData) continue;
|
|
136
|
+
trackScriptPageDatas(internals, newPageData, scriptIds);
|
|
126
137
|
}
|
|
127
138
|
}
|
|
128
139
|
}
|
|
@@ -92,9 +92,20 @@ function rollupPluginAstroBuildCSS(options) {
|
|
|
92
92
|
if (pageData) {
|
|
93
93
|
appendCSSToPage(pageData, meta, pagesToCss, depth, order);
|
|
94
94
|
}
|
|
95
|
-
} else if (options.target === "client"
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
} else if (options.target === "client") {
|
|
96
|
+
if (buildOptions.settings.config.experimental.directRenderScript) {
|
|
97
|
+
const pageDatas = internals.pagesByScriptId.get(pageInfo.id);
|
|
98
|
+
if (pageDatas) {
|
|
99
|
+
for (const pageData of pageDatas) {
|
|
100
|
+
appendCSSToPage(pageData, meta, pagesToCss, -1, order);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
} else {
|
|
104
|
+
if (internals.hoistedScriptIdToPagesMap.has(pageInfo.id)) {
|
|
105
|
+
for (const pageData of getPageDatasByHoistedScriptId(internals, pageInfo.id)) {
|
|
106
|
+
appendCSSToPage(pageData, meta, pagesToCss, -1, order);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
98
109
|
}
|
|
99
110
|
}
|
|
100
111
|
}
|
package/dist/core/constants.js
CHANGED
package/dist/core/dev/dev.js
CHANGED
|
@@ -19,7 +19,7 @@ async function dev(inlineConfig) {
|
|
|
19
19
|
await telemetry.record([]);
|
|
20
20
|
const restart = await createContainerWithAutomaticRestart({ inlineConfig, fs });
|
|
21
21
|
const logger = restart.container.logger;
|
|
22
|
-
const currentVersion = "4.8.
|
|
22
|
+
const currentVersion = "4.8.5";
|
|
23
23
|
const isPrerelease = currentVersion.includes("-");
|
|
24
24
|
if (!isPrerelease) {
|
|
25
25
|
try {
|
|
@@ -42,6 +42,7 @@ async function dev(inlineConfig) {
|
|
|
42
42
|
);
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
+
}).catch(() => {
|
|
45
46
|
});
|
|
46
47
|
} catch (e) {
|
|
47
48
|
}
|
|
@@ -1334,6 +1334,19 @@ export declare const DuplicateContentEntrySlugError: {
|
|
|
1334
1334
|
title: string;
|
|
1335
1335
|
message(collection: string, slug: string, preExisting: string, alsoFound: string): string;
|
|
1336
1336
|
};
|
|
1337
|
+
/**
|
|
1338
|
+
* @docs
|
|
1339
|
+
* @see
|
|
1340
|
+
* - [On-demand rendering](https://docs.astro.build/en/basics/rendering-modes/#on-demand-rendered)
|
|
1341
|
+
* @description
|
|
1342
|
+
* Your project must have a server output to create backend functions with Actions.
|
|
1343
|
+
*/
|
|
1344
|
+
export declare const ActionsWithoutServerOutputError: {
|
|
1345
|
+
name: string;
|
|
1346
|
+
title: string;
|
|
1347
|
+
message: string;
|
|
1348
|
+
hint: string;
|
|
1349
|
+
};
|
|
1337
1350
|
/**
|
|
1338
1351
|
* @docs
|
|
1339
1352
|
* @see
|
|
@@ -502,6 +502,12 @@ Entries:
|
|
|
502
502
|
- ${alsoFound}`;
|
|
503
503
|
}
|
|
504
504
|
};
|
|
505
|
+
const ActionsWithoutServerOutputError = {
|
|
506
|
+
name: "ActionsWithoutServerOutputError",
|
|
507
|
+
title: "Actions must be used with server output.",
|
|
508
|
+
message: "Actions enabled without setting a server build output. A server is required to create callable backend functions. To deploy routes to a server, add a server adapter to your astro config.",
|
|
509
|
+
hint: "Learn about on-demand rendering: https://docs.astro.build/en/basics/rendering-modes/#on-demand-rendered"
|
|
510
|
+
};
|
|
505
511
|
const UnsupportedConfigTransformError = {
|
|
506
512
|
name: "UnsupportedConfigTransformError",
|
|
507
513
|
title: "Unsupported transform in content config.",
|
|
@@ -511,6 +517,7 @@ Full error: ${parseError}`,
|
|
|
511
517
|
};
|
|
512
518
|
const UnknownError = { name: "UnknownError", title: "Unknown Error." };
|
|
513
519
|
export {
|
|
520
|
+
ActionsWithoutServerOutputError,
|
|
514
521
|
AstroGlobNoMatch,
|
|
515
522
|
AstroGlobUsedOutside,
|
|
516
523
|
AstroResponseHeadersReassigned,
|
package/dist/core/messages.js
CHANGED
|
@@ -37,7 +37,7 @@ function serverStart({
|
|
|
37
37
|
host,
|
|
38
38
|
base
|
|
39
39
|
}) {
|
|
40
|
-
const version = "4.8.
|
|
40
|
+
const version = "4.8.5";
|
|
41
41
|
const localPrefix = `${dim("\u2503")} Local `;
|
|
42
42
|
const networkPrefix = `${dim("\u2503")} Network `;
|
|
43
43
|
const emptyPrefix = " ".repeat(11);
|
|
@@ -269,7 +269,7 @@ function printHelp({
|
|
|
269
269
|
message.push(
|
|
270
270
|
linebreak(),
|
|
271
271
|
` ${bgGreen(black(` ${commandName} `))} ${green(
|
|
272
|
-
`v${"4.8.
|
|
272
|
+
`v${"4.8.5"}`
|
|
273
273
|
)} ${headline}`
|
|
274
274
|
);
|
|
275
275
|
}
|
|
@@ -95,7 +95,7 @@ class RenderContext {
|
|
|
95
95
|
if (payload) {
|
|
96
96
|
if (this.pipeline.manifest.rewritingEnabled) {
|
|
97
97
|
try {
|
|
98
|
-
const [routeData, component] = await pipeline.tryRewrite(payload);
|
|
98
|
+
const [routeData, component] = await pipeline.tryRewrite(payload, this.request);
|
|
99
99
|
this.routeData = routeData;
|
|
100
100
|
componentInstance = component;
|
|
101
101
|
} catch (e) {
|
|
@@ -173,7 +173,7 @@ class RenderContext {
|
|
|
173
173
|
const rewrite = async (reroutePayload) => {
|
|
174
174
|
pipeline.logger.debug("router", "Called rewriting to:", reroutePayload);
|
|
175
175
|
try {
|
|
176
|
-
const [routeData, component] = await pipeline.tryRewrite(reroutePayload);
|
|
176
|
+
const [routeData, component] = await pipeline.tryRewrite(reroutePayload, this.request);
|
|
177
177
|
this.routeData = routeData;
|
|
178
178
|
if (reroutePayload instanceof Request) {
|
|
179
179
|
this.request = reroutePayload;
|
|
@@ -328,7 +328,7 @@ class RenderContext {
|
|
|
328
328
|
const rewrite = async (reroutePayload) => {
|
|
329
329
|
try {
|
|
330
330
|
pipeline.logger.debug("router", "Calling rewrite: ", reroutePayload);
|
|
331
|
-
const [routeData, component] = await pipeline.tryRewrite(reroutePayload);
|
|
331
|
+
const [routeData, component] = await pipeline.tryRewrite(reroutePayload, this.request);
|
|
332
332
|
this.routeData = routeData;
|
|
333
333
|
if (reroutePayload instanceof Request) {
|
|
334
334
|
this.request = reroutePayload;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { AstroConfig, RoutePart } from '../../../@types/astro.js';
|
|
2
|
-
export declare function getRouteGenerator(segments: RoutePart[][], addTrailingSlash: AstroConfig['trailingSlash']): (params:
|
|
2
|
+
export declare function getRouteGenerator(segments: RoutePart[][], addTrailingSlash: AstroConfig['trailingSlash']): (params: Record<string, string | number | undefined>) => string;
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import { compile } from "path-to-regexp";
|
|
2
|
+
function sanitizeParams(params) {
|
|
3
|
+
return Object.fromEntries(
|
|
4
|
+
Object.entries(params).map(([key, value]) => {
|
|
5
|
+
if (typeof value === "string") {
|
|
6
|
+
return [key, value.normalize().replace(/#/g, "%23").replace(/\?/g, "%3F")];
|
|
7
|
+
}
|
|
8
|
+
return [key, value];
|
|
9
|
+
})
|
|
10
|
+
);
|
|
11
|
+
}
|
|
2
12
|
function getRouteGenerator(segments, addTrailingSlash) {
|
|
3
13
|
const template = segments.map((segment) => {
|
|
4
14
|
return "/" + segment.map((part) => {
|
|
@@ -17,7 +27,8 @@ function getRouteGenerator(segments, addTrailingSlash) {
|
|
|
17
27
|
}
|
|
18
28
|
const toPath = compile(template + trailing);
|
|
19
29
|
return (params) => {
|
|
20
|
-
const
|
|
30
|
+
const sanitizedParams = sanitizeParams(params);
|
|
31
|
+
const path = toPath(sanitizedParams);
|
|
21
32
|
return path || "/";
|
|
22
33
|
};
|
|
23
34
|
}
|
package/dist/i18n/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { APIContext, AstroConfig, Locales, SSRManifest, ValidRedirectStatus } from '../@types/astro.js';
|
|
2
2
|
import type { RoutingStrategies } from './utils.js';
|
|
3
3
|
export declare function requestHasLocale(locales: Locales): (context: APIContext) => boolean;
|
|
4
|
+
export declare function requestIs404Or500(request: Request, base?: string): boolean;
|
|
4
5
|
export declare function pathHasLocale(path: string, locales: Locales): boolean;
|
|
5
6
|
type GetLocaleRelativeUrl = GetLocaleOptions & {
|
|
6
7
|
locale: string;
|
package/dist/i18n/index.js
CHANGED
|
@@ -9,6 +9,10 @@ function requestHasLocale(locales) {
|
|
|
9
9
|
return pathHasLocale(context.url.pathname, locales);
|
|
10
10
|
};
|
|
11
11
|
}
|
|
12
|
+
function requestIs404Or500(request, base = "") {
|
|
13
|
+
const url = new URL(request.url);
|
|
14
|
+
return url.pathname.startsWith(`${base}/404`) || url.pathname.startsWith(`${base}/500`);
|
|
15
|
+
}
|
|
12
16
|
function pathHasLocale(path, locales) {
|
|
13
17
|
const segments = path.split("/");
|
|
14
18
|
for (const segment of segments) {
|
|
@@ -188,7 +192,7 @@ function notFound({ base, locales }) {
|
|
|
188
192
|
if (!(isRoot || pathHasLocale(url.pathname, locales))) {
|
|
189
193
|
if (response) {
|
|
190
194
|
response.headers.set(REROUTE_DIRECTIVE_HEADER, "no");
|
|
191
|
-
return new Response(
|
|
195
|
+
return new Response(response.body, {
|
|
192
196
|
status: 404,
|
|
193
197
|
headers: response.headers
|
|
194
198
|
});
|
|
@@ -263,6 +267,7 @@ export {
|
|
|
263
267
|
redirectToDefaultLocale,
|
|
264
268
|
redirectToFallback,
|
|
265
269
|
requestHasLocale,
|
|
270
|
+
requestIs404Or500,
|
|
266
271
|
toCodes,
|
|
267
272
|
toPaths
|
|
268
273
|
};
|
package/dist/i18n/middleware.js
CHANGED
|
@@ -4,7 +4,8 @@ import {
|
|
|
4
4
|
notFound,
|
|
5
5
|
redirectToDefaultLocale,
|
|
6
6
|
redirectToFallback,
|
|
7
|
-
requestHasLocale
|
|
7
|
+
requestHasLocale,
|
|
8
|
+
requestIs404Or500
|
|
8
9
|
} from "./index.js";
|
|
9
10
|
function createI18nMiddleware(i18n, base, trailingSlash, format) {
|
|
10
11
|
if (!i18n) return (_, next) => next();
|
|
@@ -50,6 +51,9 @@ function createI18nMiddleware(i18n, base, trailingSlash, format) {
|
|
|
50
51
|
if (type !== "page" && type !== "fallback") {
|
|
51
52
|
return response;
|
|
52
53
|
}
|
|
54
|
+
if (requestIs404Or500(context.request, base)) {
|
|
55
|
+
return response;
|
|
56
|
+
}
|
|
53
57
|
const { currentLocale } = context;
|
|
54
58
|
switch (i18n.strategy) {
|
|
55
59
|
case "manual": {
|
package/dist/prefetch/index.d.ts
CHANGED
|
@@ -11,8 +11,12 @@ export declare function init(defaultOpts?: InitOptions): void;
|
|
|
11
11
|
export interface PrefetchOptions {
|
|
12
12
|
/**
|
|
13
13
|
* How the prefetch should prioritize the URL. (default `'link'`)
|
|
14
|
-
* - `'link'`: use `<link rel="prefetch"
|
|
15
|
-
* - `'fetch'`: use `fetch()
|
|
14
|
+
* - `'link'`: use `<link rel="prefetch">`.
|
|
15
|
+
* - `'fetch'`: use `fetch()`.
|
|
16
|
+
*
|
|
17
|
+
* @deprecated It is recommended to not use this option, and let prefetch use `'link'` whenever it's supported,
|
|
18
|
+
* or otherwise fall back to `'fetch'`. `'link'` works better if the URL doesn't set an appropriate cache header,
|
|
19
|
+
* as the browser will continue to cache it as long as it's used subsequently.
|
|
16
20
|
*/
|
|
17
21
|
with?: 'link' | 'fetch';
|
|
18
22
|
/**
|
package/dist/prefetch/index.js
CHANGED
|
@@ -24,7 +24,7 @@ function initTapStrategy() {
|
|
|
24
24
|
event,
|
|
25
25
|
(e) => {
|
|
26
26
|
if (elMatchesStrategy(e.target, "tap")) {
|
|
27
|
-
prefetch(e.target.href, {
|
|
27
|
+
prefetch(e.target.href, { ignoreSlowConnection: true });
|
|
28
28
|
}
|
|
29
29
|
},
|
|
30
30
|
{ passive: true }
|
|
@@ -59,7 +59,7 @@ function initHoverStrategy() {
|
|
|
59
59
|
clearTimeout(timeout);
|
|
60
60
|
}
|
|
61
61
|
timeout = setTimeout(() => {
|
|
62
|
-
prefetch(href
|
|
62
|
+
prefetch(href);
|
|
63
63
|
}, 80);
|
|
64
64
|
}
|
|
65
65
|
function handleHoverOut() {
|
|
@@ -97,7 +97,7 @@ function createViewportIntersectionObserver() {
|
|
|
97
97
|
setTimeout(() => {
|
|
98
98
|
observer.unobserve(anchor);
|
|
99
99
|
timeouts.delete(anchor);
|
|
100
|
-
prefetch(anchor.href
|
|
100
|
+
prefetch(anchor.href);
|
|
101
101
|
}, 300)
|
|
102
102
|
);
|
|
103
103
|
} else {
|
|
@@ -113,7 +113,7 @@ function initLoadStrategy() {
|
|
|
113
113
|
onPageLoad(() => {
|
|
114
114
|
for (const anchor of document.getElementsByTagName("a")) {
|
|
115
115
|
if (elMatchesStrategy(anchor, "load")) {
|
|
116
|
-
prefetch(anchor.href
|
|
116
|
+
prefetch(anchor.href);
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
});
|
|
@@ -122,20 +122,18 @@ function prefetch(url, opts) {
|
|
|
122
122
|
const ignoreSlowConnection = opts?.ignoreSlowConnection ?? false;
|
|
123
123
|
if (!canPrefetchUrl(url, ignoreSlowConnection)) return;
|
|
124
124
|
prefetchedUrls.add(url);
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (clientPrerender && HTMLScriptElement.supports && HTMLScriptElement.supports("speculationrules")) {
|
|
125
|
+
if (clientPrerender && HTMLScriptElement.supports?.("speculationrules")) {
|
|
126
|
+
debug?.(`[astro] Prefetching ${url} with <script type="speculationrules">`);
|
|
128
127
|
appendSpeculationRules(url);
|
|
129
|
-
} else if (
|
|
128
|
+
} else if (document.createElement("link").relList?.supports?.("prefetch") && opts?.with !== "fetch") {
|
|
129
|
+
debug?.(`[astro] Prefetching ${url} with <link rel="prefetch">`);
|
|
130
130
|
const link = document.createElement("link");
|
|
131
131
|
link.rel = "prefetch";
|
|
132
132
|
link.setAttribute("href", url);
|
|
133
133
|
document.head.append(link);
|
|
134
134
|
} else {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
console.error(e);
|
|
138
|
-
});
|
|
135
|
+
debug?.(`[astro] Prefetching ${url} with fetch`);
|
|
136
|
+
fetch(url, { priority: "low" });
|
|
139
137
|
}
|
|
140
138
|
}
|
|
141
139
|
function canPrefetchUrl(url, ignoreSlowConnection) {
|
|
@@ -128,12 +128,18 @@ async function renderToAsyncIterable(result, componentFactory, props, children,
|
|
|
128
128
|
await bufferHeadContent(result);
|
|
129
129
|
}
|
|
130
130
|
let error = null;
|
|
131
|
-
let next =
|
|
131
|
+
let next = null;
|
|
132
132
|
const buffer = [];
|
|
133
|
+
let renderingComplete = false;
|
|
133
134
|
const iterator = {
|
|
134
135
|
async next() {
|
|
135
136
|
if (result.cancelled) return { done: true, value: void 0 };
|
|
136
|
-
|
|
137
|
+
if (next !== null) {
|
|
138
|
+
await next.promise;
|
|
139
|
+
}
|
|
140
|
+
if (!renderingComplete) {
|
|
141
|
+
next = promiseWithResolvers();
|
|
142
|
+
}
|
|
137
143
|
if (error) {
|
|
138
144
|
throw error;
|
|
139
145
|
}
|
|
@@ -176,17 +182,18 @@ async function renderToAsyncIterable(result, componentFactory, props, children,
|
|
|
176
182
|
const bytes = chunkToByteArray(result, chunk);
|
|
177
183
|
if (bytes.length > 0) {
|
|
178
184
|
buffer.push(bytes);
|
|
179
|
-
next
|
|
180
|
-
next = promiseWithResolvers();
|
|
185
|
+
next?.resolve();
|
|
181
186
|
}
|
|
182
187
|
}
|
|
183
188
|
};
|
|
184
189
|
const renderPromise = templateResult.render(destination);
|
|
185
190
|
renderPromise.then(() => {
|
|
186
|
-
|
|
191
|
+
renderingComplete = true;
|
|
192
|
+
next?.resolve();
|
|
187
193
|
}).catch((err) => {
|
|
188
194
|
error = err;
|
|
189
|
-
|
|
195
|
+
renderingComplete = true;
|
|
196
|
+
next?.resolve();
|
|
190
197
|
});
|
|
191
198
|
return {
|
|
192
199
|
[Symbol.asyncIterator]() {
|
|
@@ -3,8 +3,12 @@ async function renderScript(result, id) {
|
|
|
3
3
|
if (result._metadata.renderedScripts.has(id)) return;
|
|
4
4
|
result._metadata.renderedScripts.add(id);
|
|
5
5
|
const inlined = result.inlinedScripts.get(id);
|
|
6
|
-
if (inlined) {
|
|
7
|
-
|
|
6
|
+
if (inlined != null) {
|
|
7
|
+
if (inlined) {
|
|
8
|
+
return markHTMLString(`<script type="module">${inlined}</script>`);
|
|
9
|
+
} else {
|
|
10
|
+
return "";
|
|
11
|
+
}
|
|
8
12
|
}
|
|
9
13
|
const resolved = await result.resolve(id);
|
|
10
14
|
return markHTMLString(`<script type="module" src="${resolved}"></script>`);
|
|
@@ -19,6 +19,6 @@ export declare class DevPipeline extends Pipeline {
|
|
|
19
19
|
preload(routeData: RouteData, filePath: URL): Promise<ComponentInstance>;
|
|
20
20
|
clearRouteCache(): void;
|
|
21
21
|
getComponentByRoute(routeData: RouteData): Promise<ComponentInstance>;
|
|
22
|
-
tryRewrite(payload: RewritePayload): Promise<[RouteData, ComponentInstance]>;
|
|
22
|
+
tryRewrite(payload: RewritePayload, request: Request): Promise<[RouteData, ComponentInstance]>;
|
|
23
23
|
setManifestData(manifestData: ManifestData): void;
|
|
24
24
|
}
|
|
@@ -132,7 +132,7 @@ class DevPipeline extends Pipeline {
|
|
|
132
132
|
return await this.preload(routeData, filePath);
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
|
-
async tryRewrite(payload) {
|
|
135
|
+
async tryRewrite(payload, request) {
|
|
136
136
|
let foundRoute;
|
|
137
137
|
if (!this.manifestData) {
|
|
138
138
|
throw new Error("Missing manifest data. This is an internal error, please file an issue.");
|
|
@@ -149,9 +149,12 @@ class DevPipeline extends Pipeline {
|
|
|
149
149
|
foundRoute = route;
|
|
150
150
|
break;
|
|
151
151
|
}
|
|
152
|
-
} else
|
|
153
|
-
|
|
154
|
-
|
|
152
|
+
} else {
|
|
153
|
+
const newUrl = new URL(payload, new URL(request.url).origin);
|
|
154
|
+
if (route.pattern.test(decodeURI(newUrl.pathname))) {
|
|
155
|
+
foundRoute = route;
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
155
158
|
}
|
|
156
159
|
}
|
|
157
160
|
if (foundRoute) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro",
|
|
3
|
-
"version": "4.8.
|
|
3
|
+
"version": "4.8.5",
|
|
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",
|
|
@@ -229,8 +229,10 @@
|
|
|
229
229
|
"postbuild": "astro-scripts copy \"src/**/*.astro\" && astro-scripts copy \"src/**/*.wasm\"",
|
|
230
230
|
"test": "pnpm run test:node",
|
|
231
231
|
"test:match": "pnpm run test:node --match",
|
|
232
|
-
"test:e2e": "
|
|
232
|
+
"test:e2e": "pnpm test:e2e:chrome && pnpm test:e2e:firefox",
|
|
233
233
|
"test:e2e:match": "playwright test -g",
|
|
234
|
+
"test:e2e:chrome": "playwright test",
|
|
235
|
+
"test:e2e:firefox": "playwright test --config playwright.firefox.config.js",
|
|
234
236
|
"test:node": "astro-scripts test \"test/**/*.test.js\""
|
|
235
237
|
}
|
|
236
238
|
}
|
package/templates/actions.mjs
CHANGED
|
@@ -45,6 +45,7 @@ async function actionHandler(clientParam, path) {
|
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
47
|
headers.set('Content-Type', 'application/json');
|
|
48
|
+
headers.set('Content-Length', body?.length.toString() ?? '0');
|
|
48
49
|
}
|
|
49
50
|
const res = await fetch(path, {
|
|
50
51
|
method: 'POST',
|
|
@@ -54,6 +55,9 @@ async function actionHandler(clientParam, path) {
|
|
|
54
55
|
if (!res.ok) {
|
|
55
56
|
throw await ActionError.fromResponse(res);
|
|
56
57
|
}
|
|
58
|
+
// Check if response body is empty before parsing.
|
|
59
|
+
if (res.status === 204) return;
|
|
60
|
+
|
|
57
61
|
const json = await res.json();
|
|
58
62
|
return json;
|
|
59
63
|
}
|