astro 7.0.0-beta.4 → 7.0.0-beta.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/dist/cli/infra/build-time-astro-version-provider.js +1 -1
- package/dist/content/content-layer.js +3 -3
- package/dist/core/app/prepare-response.d.ts +2 -3
- package/dist/core/app/prepare-response.js +1 -6
- package/dist/core/build/generate.js +1 -1
- package/dist/core/config/schemas/base.d.ts +1 -1
- package/dist/core/config/schemas/base.js +1 -1
- package/dist/core/constants.d.ts +0 -41
- package/dist/core/constants.js +1 -18
- package/dist/core/dev/dev.js +1 -1
- package/dist/core/errors/default-handler.js +1 -0
- package/dist/core/fetch/fetch-state.d.ts +11 -0
- package/dist/core/fetch/fetch-state.js +19 -1
- package/dist/core/fetch/index.d.ts +8 -3
- package/dist/core/fetch/index.js +2 -2
- package/dist/core/i18n/handler.js +7 -13
- package/dist/core/messages/runtime.js +1 -1
- package/dist/core/middleware/astro-middleware.d.ts +19 -0
- package/dist/core/middleware/astro-middleware.js +43 -0
- package/dist/core/middleware/noop-middleware.js +0 -2
- package/dist/core/pages/handler.d.ts +13 -0
- package/dist/core/pages/handler.js +35 -14
- package/dist/core/routing/handler.js +6 -8
- package/dist/core/routing/rewrite.js +2 -1
- package/dist/core/util/pathname.d.ts +17 -16
- package/dist/core/util/pathname.js +6 -2
- package/dist/i18n/index.js +5 -7
- package/dist/runtime/server/endpoint.d.ts +2 -1
- package/dist/runtime/server/endpoint.js +4 -13
- package/dist/types/public/config.d.ts +6 -6
- package/dist/virtual-modules/container.d.ts +1 -1
- package/dist/vite-plugin-head/index.js +5 -11
- package/package.json +6 -6
- package/templates/content/module.mjs +0 -1
- package/dist/core/head-propagation/hint.d.ts +0 -4
- package/dist/core/head-propagation/hint.js +0 -7
|
@@ -197,7 +197,7 @@ ${contentConfig.error.message}`
|
|
|
197
197
|
logger.info("Content config changed");
|
|
198
198
|
shouldClear = true;
|
|
199
199
|
}
|
|
200
|
-
if (previousAstroVersion && previousAstroVersion !== "7.0.0-beta.
|
|
200
|
+
if (previousAstroVersion && previousAstroVersion !== "7.0.0-beta.5") {
|
|
201
201
|
logger.info("Astro version changed");
|
|
202
202
|
shouldClear = true;
|
|
203
203
|
}
|
|
@@ -205,8 +205,8 @@ ${contentConfig.error.message}`
|
|
|
205
205
|
logger.info("Clearing content store");
|
|
206
206
|
this.#store.clearAll();
|
|
207
207
|
}
|
|
208
|
-
if ("7.0.0-beta.
|
|
209
|
-
this.#store.metaStore().set("astro-version", "7.0.0-beta.
|
|
208
|
+
if ("7.0.0-beta.5") {
|
|
209
|
+
this.#store.metaStore().set("astro-version", "7.0.0-beta.5");
|
|
210
210
|
}
|
|
211
211
|
if (currentConfigDigest) {
|
|
212
212
|
this.#store.metaStore().set("content-config-digest", currentConfigDigest);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* to the `Set-Cookie` header.
|
|
2
|
+
* Appends cookies written via `Astro.cookie.set()` to the `Set-Cookie` header
|
|
3
|
+
* and marks the response as sent.
|
|
5
4
|
*
|
|
6
5
|
* This is a pure function with no dependencies on the app; it is shared by
|
|
7
6
|
* `AstroHandler` and the various error handlers.
|
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { responseSentSymbol } from "../constants.js";
|
|
2
2
|
import { getSetCookiesFromResponse } from "../cookies/index.js";
|
|
3
3
|
function prepareResponse(response, { addCookieHeader }) {
|
|
4
|
-
for (const headerName of INTERNAL_RESPONSE_HEADERS) {
|
|
5
|
-
if (response.headers.has(headerName)) {
|
|
6
|
-
response.headers.delete(headerName);
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
4
|
if (addCookieHeader) {
|
|
10
5
|
for (const setCookieHeaderValue of getSetCookiesFromResponse(response)) {
|
|
11
6
|
response.headers.append("set-cookie", setCookieHeaderValue);
|
package/dist/core/constants.d.ts
CHANGED
|
@@ -1,46 +1,5 @@
|
|
|
1
1
|
export declare const ASTRO_VERSION: string;
|
|
2
2
|
export declare const ASTRO_GENERATOR: string;
|
|
3
|
-
/**
|
|
4
|
-
* The name for the header used to help rerouting behavior.
|
|
5
|
-
* When set to "no", astro will NOT try to reroute an error response to the corresponding error page, which is the default behavior that can sometimes lead to loops.
|
|
6
|
-
*
|
|
7
|
-
* ```ts
|
|
8
|
-
* const response = new Response("keep this content as-is", {
|
|
9
|
-
* status: 404,
|
|
10
|
-
* headers: {
|
|
11
|
-
* // note that using a variable name as the key of an object needs to be wrapped in square brackets in javascript
|
|
12
|
-
* // without them, the header name will be interpreted as "REROUTE_DIRECTIVE_HEADER" instead of "X-Astro-Reroute"
|
|
13
|
-
* [REROUTE_DIRECTIVE_HEADER]: 'no',
|
|
14
|
-
* }
|
|
15
|
-
* })
|
|
16
|
-
* ```
|
|
17
|
-
* Alternatively...
|
|
18
|
-
* ```ts
|
|
19
|
-
* response.headers.set(REROUTE_DIRECTIVE_HEADER, 'no');
|
|
20
|
-
* ```
|
|
21
|
-
*/
|
|
22
|
-
export declare const REROUTE_DIRECTIVE_HEADER = "X-Astro-Reroute";
|
|
23
|
-
/**
|
|
24
|
-
* Header and value that are attached to a Response object when a **user rewrite** occurs.
|
|
25
|
-
*
|
|
26
|
-
* This metadata is used to determine the origin of a Response. If a rewrite has occurred, it should be prioritised over other logic.
|
|
27
|
-
*/
|
|
28
|
-
export declare const REWRITE_DIRECTIVE_HEADER_KEY = "X-Astro-Rewrite";
|
|
29
|
-
export declare const REWRITE_DIRECTIVE_HEADER_VALUE = "yes";
|
|
30
|
-
/**
|
|
31
|
-
* This header is set by the no-op Astro middleware.
|
|
32
|
-
*/
|
|
33
|
-
export declare const NOOP_MIDDLEWARE_HEADER = "X-Astro-Noop";
|
|
34
|
-
/**
|
|
35
|
-
* The name for the header used to help i18n middleware, which only needs to act on "page" and "fallback" route types.
|
|
36
|
-
*/
|
|
37
|
-
export declare const ROUTE_TYPE_HEADER = "X-Astro-Route-Type";
|
|
38
|
-
/**
|
|
39
|
-
* Internal headers that should be stripped from the response before
|
|
40
|
-
* sending it to the user agent. Add new internal headers here so
|
|
41
|
-
* `prepareResponse` removes them automatically.
|
|
42
|
-
*/
|
|
43
|
-
export declare const INTERNAL_RESPONSE_HEADERS: readonly ["X-Astro-Reroute", "X-Astro-Rewrite", "X-Astro-Noop", "X-Astro-Route-Type"];
|
|
44
3
|
/**
|
|
45
4
|
* Set by internal handlers (e.g. PagesHandler) to signal that a
|
|
46
5
|
* response should be replaced with the corresponding error page.
|
package/dist/core/constants.js
CHANGED
|
@@ -1,16 +1,5 @@
|
|
|
1
|
-
const ASTRO_VERSION = "7.0.0-beta.
|
|
1
|
+
const ASTRO_VERSION = "7.0.0-beta.5";
|
|
2
2
|
const ASTRO_GENERATOR = `Astro v${ASTRO_VERSION}`;
|
|
3
|
-
const REROUTE_DIRECTIVE_HEADER = "X-Astro-Reroute";
|
|
4
|
-
const REWRITE_DIRECTIVE_HEADER_KEY = "X-Astro-Rewrite";
|
|
5
|
-
const REWRITE_DIRECTIVE_HEADER_VALUE = "yes";
|
|
6
|
-
const NOOP_MIDDLEWARE_HEADER = "X-Astro-Noop";
|
|
7
|
-
const ROUTE_TYPE_HEADER = "X-Astro-Route-Type";
|
|
8
|
-
const INTERNAL_RESPONSE_HEADERS = [
|
|
9
|
-
REROUTE_DIRECTIVE_HEADER,
|
|
10
|
-
REWRITE_DIRECTIVE_HEADER_KEY,
|
|
11
|
-
NOOP_MIDDLEWARE_HEADER,
|
|
12
|
-
ROUTE_TYPE_HEADER
|
|
13
|
-
];
|
|
14
3
|
const ASTRO_ERROR_HEADER = "X-Astro-Error";
|
|
15
4
|
const DEFAULT_404_COMPONENT = "astro-default-404.astro";
|
|
16
5
|
const REDIRECT_STATUS_CODES = [301, 302, 303, 307, 308, 300, 304];
|
|
@@ -57,15 +46,9 @@ export {
|
|
|
57
46
|
ASTRO_VERSION,
|
|
58
47
|
ASTRO_VITE_ENVIRONMENT_NAMES,
|
|
59
48
|
DEFAULT_404_COMPONENT,
|
|
60
|
-
INTERNAL_RESPONSE_HEADERS,
|
|
61
49
|
MIDDLEWARE_PATH_SEGMENT_NAME,
|
|
62
|
-
NOOP_MIDDLEWARE_HEADER,
|
|
63
50
|
REDIRECT_STATUS_CODES,
|
|
64
51
|
REROUTABLE_STATUS_CODES,
|
|
65
|
-
REROUTE_DIRECTIVE_HEADER,
|
|
66
|
-
REWRITE_DIRECTIVE_HEADER_KEY,
|
|
67
|
-
REWRITE_DIRECTIVE_HEADER_VALUE,
|
|
68
|
-
ROUTE_TYPE_HEADER,
|
|
69
52
|
SUPPORTED_MARKDOWN_FILE_EXTENSIONS,
|
|
70
53
|
appSymbol,
|
|
71
54
|
clientAddressSymbol,
|
package/dist/core/dev/dev.js
CHANGED
|
@@ -26,7 +26,7 @@ async function dev(inlineConfig) {
|
|
|
26
26
|
await telemetry.record([]);
|
|
27
27
|
const restart = await createContainerWithAutomaticRestart({ inlineConfig, fs });
|
|
28
28
|
const logger = restart.container.logger;
|
|
29
|
-
const currentVersion = "7.0.0-beta.
|
|
29
|
+
const currentVersion = "7.0.0-beta.5";
|
|
30
30
|
const isPrerelease = currentVersion.includes("-");
|
|
31
31
|
if (!isPrerelease) {
|
|
32
32
|
try {
|
|
@@ -108,6 +108,12 @@ export declare class FetchState implements AstroFetchState {
|
|
|
108
108
|
status: number;
|
|
109
109
|
/** Whether user middleware should be skipped for this request. */
|
|
110
110
|
skipMiddleware: boolean;
|
|
111
|
+
/**
|
|
112
|
+
* Set to `true` when the request path was encoded too many times to fully
|
|
113
|
+
* decode (see {@link validateAndDecodePathname}). These requests are
|
|
114
|
+
* rejected with a `400` before middleware or routing run.
|
|
115
|
+
*/
|
|
116
|
+
invalidEncoding: boolean;
|
|
111
117
|
/** A flag that tells the render content if the rewriting was triggered. */
|
|
112
118
|
isRewriting: boolean;
|
|
113
119
|
/** A safety net in case of loops (rewrite counter). */
|
|
@@ -122,6 +128,10 @@ export declare class FetchState implements AstroFetchState {
|
|
|
122
128
|
clientAddress: string | undefined;
|
|
123
129
|
/** Whether this is a partial render (container API). */
|
|
124
130
|
partial: boolean | undefined;
|
|
131
|
+
/** Internal metadata about the current response route type. */
|
|
132
|
+
responseRouteType: 'page' | 'fallback' | undefined;
|
|
133
|
+
/** Internal flag to prevent rerouting this response to an error page. */
|
|
134
|
+
skipErrorReroute: boolean;
|
|
125
135
|
/** Whether to inject CSP meta tags. */
|
|
126
136
|
shouldInjectCspMetaTags: boolean | undefined;
|
|
127
137
|
/** Request-scoped locals object, shared with user middleware. */
|
|
@@ -227,4 +237,5 @@ export declare class FetchState implements AstroFetchState {
|
|
|
227
237
|
* after an in-flight rewrite swaps the route / request / params.
|
|
228
238
|
*/
|
|
229
239
|
invalidateContexts(): void;
|
|
240
|
+
resetResponseMetadata(): void;
|
|
230
241
|
}
|
|
@@ -29,7 +29,7 @@ import { getParams, getProps } from "../render/index.js";
|
|
|
29
29
|
import { Rewrites } from "../rewrites/handler.js";
|
|
30
30
|
import { isRoute404or500, isRouteServerIsland } from "../routing/match.js";
|
|
31
31
|
import { normalizeUrl } from "../util/normalized-url.js";
|
|
32
|
-
import { validateAndDecodePathname } from "../util/pathname.js";
|
|
32
|
+
import { MultiLevelEncodingError, validateAndDecodePathname } from "../util/pathname.js";
|
|
33
33
|
import { getOriginPathname, setOriginPathname } from "../routing/rewrite.js";
|
|
34
34
|
import { computePathnameFromDomain } from "../i18n/domain.js";
|
|
35
35
|
import { getCustom404Route, routeHasHtmlExtension } from "../routing/helpers.js";
|
|
@@ -88,6 +88,12 @@ class FetchState {
|
|
|
88
88
|
status = 200;
|
|
89
89
|
/** Whether user middleware should be skipped for this request. */
|
|
90
90
|
skipMiddleware = false;
|
|
91
|
+
/**
|
|
92
|
+
* Set to `true` when the request path was encoded too many times to fully
|
|
93
|
+
* decode (see {@link validateAndDecodePathname}). These requests are
|
|
94
|
+
* rejected with a `400` before middleware or routing run.
|
|
95
|
+
*/
|
|
96
|
+
invalidEncoding = false;
|
|
91
97
|
/** A flag that tells the render content if the rewriting was triggered. */
|
|
92
98
|
isRewriting = false;
|
|
93
99
|
/** A safety net in case of loops (rewrite counter). */
|
|
@@ -111,6 +117,10 @@ class FetchState {
|
|
|
111
117
|
clientAddress;
|
|
112
118
|
/** Whether this is a partial render (container API). */
|
|
113
119
|
partial;
|
|
120
|
+
/** Internal metadata about the current response route type. */
|
|
121
|
+
responseRouteType;
|
|
122
|
+
/** Internal flag to prevent rerouting this response to an error page. */
|
|
123
|
+
skipErrorReroute = false;
|
|
114
124
|
/** Whether to inject CSP meta tags. */
|
|
115
125
|
shouldInjectCspMetaTags;
|
|
116
126
|
/** Request-scoped locals object, shared with user middleware. */
|
|
@@ -697,6 +707,10 @@ class FetchState {
|
|
|
697
707
|
try {
|
|
698
708
|
return validateAndDecodePathname(pathname);
|
|
699
709
|
} catch (e) {
|
|
710
|
+
if (e instanceof MultiLevelEncodingError) {
|
|
711
|
+
this.invalidEncoding = true;
|
|
712
|
+
return pathname;
|
|
713
|
+
}
|
|
700
714
|
this.pipeline.logger.error(null, e.toString());
|
|
701
715
|
return pathname;
|
|
702
716
|
}
|
|
@@ -876,6 +890,10 @@ class FetchState {
|
|
|
876
890
|
this.actionApiContext = null;
|
|
877
891
|
this.apiContext = null;
|
|
878
892
|
}
|
|
893
|
+
resetResponseMetadata() {
|
|
894
|
+
this.responseRouteType = void 0;
|
|
895
|
+
this.skipErrorReroute = false;
|
|
896
|
+
}
|
|
879
897
|
}
|
|
880
898
|
export {
|
|
881
899
|
FetchState,
|
|
@@ -13,13 +13,18 @@ export declare function astro(state: FetchState): Promise<Response>;
|
|
|
13
13
|
export declare function trailingSlash(state: FetchState): Response | undefined;
|
|
14
14
|
/**
|
|
15
15
|
* Runs Astro's middleware chain for the given state, calling `next` at
|
|
16
|
-
* the bottom of the chain to produce the response. Lazily creates
|
|
17
|
-
*
|
|
16
|
+
* the bottom of the chain to produce the response. Lazily creates the
|
|
17
|
+
* render context if needed. Unmatched routes render the 404 error page;
|
|
18
|
+
* errors thrown by user middleware are logged and render the 500 error
|
|
19
|
+
* page; errors surfaced through `next` (the host framework's downstream
|
|
20
|
+
* chain) propagate to the host instead.
|
|
18
21
|
*/
|
|
19
22
|
export declare function middleware(state: FetchState, next: (state: FetchState) => Promise<Response>): Promise<Response>;
|
|
20
23
|
/**
|
|
21
24
|
* Dispatches the request to the matched route (endpoint, page, redirect,
|
|
22
|
-
* or fallback). Lazily creates the render context if needed.
|
|
25
|
+
* or fallback). Lazily creates the render context if needed. Unmatched
|
|
26
|
+
* routes render the 404 error page; render-time errors are logged and
|
|
27
|
+
* render the 500 error page.
|
|
23
28
|
*/
|
|
24
29
|
export declare function pages(state: FetchState): Promise<Response>;
|
|
25
30
|
/**
|
package/dist/core/fetch/index.js
CHANGED
|
@@ -51,7 +51,7 @@ function middleware(state, next) {
|
|
|
51
51
|
mw = new AstroMiddleware(app.pipeline);
|
|
52
52
|
middlewareInstances.set(app, mw);
|
|
53
53
|
}
|
|
54
|
-
return mw.
|
|
54
|
+
return mw.handleWithErrorFallback(app, state, (s, _ctx) => next(s));
|
|
55
55
|
}
|
|
56
56
|
const pagesHandlers = /* @__PURE__ */ new WeakMap();
|
|
57
57
|
function pages(state) {
|
|
@@ -61,7 +61,7 @@ function pages(state) {
|
|
|
61
61
|
handler = new PagesHandler(app.pipeline);
|
|
62
62
|
pagesHandlers.set(app, handler);
|
|
63
63
|
}
|
|
64
|
-
return handler.
|
|
64
|
+
return handler.handleWithErrorFallback(app, state);
|
|
65
65
|
}
|
|
66
66
|
function sessions(state) {
|
|
67
67
|
return provideSession(state);
|
|
@@ -3,7 +3,6 @@ import { computeFallbackRoute } from "../../i18n/fallback.js";
|
|
|
3
3
|
import { I18nRouter } from "../../i18n/router.js";
|
|
4
4
|
import { PipelineFeatures } from "../base-pipeline.js";
|
|
5
5
|
import { shouldAppendForwardSlash } from "../build/util.js";
|
|
6
|
-
import { REROUTE_DIRECTIVE_HEADER, ROUTE_TYPE_HEADER } from "../constants.js";
|
|
7
6
|
class I18n {
|
|
8
7
|
#i18n;
|
|
9
8
|
#base;
|
|
@@ -36,25 +35,20 @@ class I18n {
|
|
|
36
35
|
async finalize(state, response) {
|
|
37
36
|
state.pipeline.usedFeatures |= PipelineFeatures.i18n;
|
|
38
37
|
const i18n = this.#i18n;
|
|
39
|
-
|
|
40
|
-
if (typeHeader) {
|
|
41
|
-
response.headers.delete(ROUTE_TYPE_HEADER);
|
|
42
|
-
}
|
|
43
|
-
const isReroute = response.headers.get(REROUTE_DIRECTIVE_HEADER);
|
|
44
|
-
if (isReroute === "no" && typeof i18n.fallback === "undefined") {
|
|
38
|
+
if (state.skipErrorReroute && typeof i18n.fallback === "undefined") {
|
|
45
39
|
return response;
|
|
46
40
|
}
|
|
47
|
-
if (
|
|
41
|
+
if (state.responseRouteType !== "page" && state.responseRouteType !== "fallback") {
|
|
48
42
|
return response;
|
|
49
43
|
}
|
|
50
|
-
const url =
|
|
44
|
+
const url = state.url;
|
|
51
45
|
const currentLocale = state.computeCurrentLocale();
|
|
52
46
|
const isPrerendered = state.routeData.prerender;
|
|
53
47
|
const routerContext = {
|
|
54
48
|
currentLocale,
|
|
55
49
|
currentDomain: url.hostname,
|
|
56
|
-
routeType:
|
|
57
|
-
isReroute:
|
|
50
|
+
routeType: state.responseRouteType,
|
|
51
|
+
isReroute: false
|
|
58
52
|
};
|
|
59
53
|
const routeDecision = this.#router.match(url.pathname, routerContext);
|
|
60
54
|
switch (routeDecision.type) {
|
|
@@ -74,7 +68,7 @@ class I18n {
|
|
|
74
68
|
status: 404,
|
|
75
69
|
headers: response.headers
|
|
76
70
|
});
|
|
77
|
-
|
|
71
|
+
state.skipErrorReroute = true;
|
|
78
72
|
if (routeDecision.location) {
|
|
79
73
|
prerenderedRes.headers.set("Location", routeDecision.location);
|
|
80
74
|
}
|
|
@@ -90,7 +84,7 @@ class I18n {
|
|
|
90
84
|
break;
|
|
91
85
|
}
|
|
92
86
|
if (i18n.fallback && i18n.fallbackType) {
|
|
93
|
-
const effectiveStatus =
|
|
87
|
+
const effectiveStatus = state.responseRouteType === "fallback" ? 404 : response.status;
|
|
94
88
|
const fallbackDecision = computeFallbackRoute({
|
|
95
89
|
pathname: url.pathname,
|
|
96
90
|
responseStatus: effectiveStatus,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { FetchState } from '../fetch/fetch-state.js';
|
|
2
2
|
import type { APIContext } from '../../types/public/context.js';
|
|
3
|
+
import type { BaseApp } from '../app/base.js';
|
|
3
4
|
import { type Pipeline } from '../base-pipeline.js';
|
|
4
5
|
/**
|
|
5
6
|
* Callback invoked at the bottom of the middleware chain to dispatch the
|
|
@@ -24,4 +25,22 @@ export declare class AstroMiddleware {
|
|
|
24
25
|
#private;
|
|
25
26
|
constructor(pipeline: Pipeline);
|
|
26
27
|
handle(state: FetchState, renderRouteCallback: RenderRouteCallback): Promise<Response>;
|
|
28
|
+
/**
|
|
29
|
+
* Like `handle`, but mirrors the app-level error handling that
|
|
30
|
+
* `AstroHandler` provides on the standard path, the same way
|
|
31
|
+
* `PagesHandler.handleWithErrorFallback` does for `pages()`. When no
|
|
32
|
+
* route matched it returns a 404 marked with `X-Astro-Error` for the
|
|
33
|
+
* app's post-check; when Astro's own middleware chain throws it logs the
|
|
34
|
+
* error and renders the custom `500.astro`.
|
|
35
|
+
*
|
|
36
|
+
* Errors surfaced through `renderRouteCallback` (the host framework's
|
|
37
|
+
* `next`, e.g. host middleware mounted below `middleware()`) are
|
|
38
|
+
* re-thrown instead, so the host's own error handling still runs rather
|
|
39
|
+
* than being swallowed into Astro's 500 page. A sentinel tells the two
|
|
40
|
+
* apart.
|
|
41
|
+
*
|
|
42
|
+
* Used by the composable `astro/fetch` `middleware()` entry point, where
|
|
43
|
+
* there is no surrounding `AstroHandler` to supply this fallback.
|
|
44
|
+
*/
|
|
45
|
+
handleWithErrorFallback(app: BaseApp<Pipeline>, state: FetchState, renderRouteCallback: RenderRouteCallback): Promise<Response>;
|
|
27
46
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PipelineFeatures } from "../base-pipeline.js";
|
|
2
|
+
import { ASTRO_ERROR_HEADER } from "../constants.js";
|
|
2
3
|
import { attachCookiesToResponse } from "../cookies/index.js";
|
|
3
4
|
import { applyRewriteToState } from "../rewrites/handler.js";
|
|
4
5
|
import { callMiddleware } from "./callMiddleware.js";
|
|
@@ -41,6 +42,48 @@ class AstroMiddleware {
|
|
|
41
42
|
state.response = response;
|
|
42
43
|
return response;
|
|
43
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Like `handle`, but mirrors the app-level error handling that
|
|
47
|
+
* `AstroHandler` provides on the standard path, the same way
|
|
48
|
+
* `PagesHandler.handleWithErrorFallback` does for `pages()`. When no
|
|
49
|
+
* route matched it returns a 404 marked with `X-Astro-Error` for the
|
|
50
|
+
* app's post-check; when Astro's own middleware chain throws it logs the
|
|
51
|
+
* error and renders the custom `500.astro`.
|
|
52
|
+
*
|
|
53
|
+
* Errors surfaced through `renderRouteCallback` (the host framework's
|
|
54
|
+
* `next`, e.g. host middleware mounted below `middleware()`) are
|
|
55
|
+
* re-thrown instead, so the host's own error handling still runs rather
|
|
56
|
+
* than being swallowed into Astro's 500 page. A sentinel tells the two
|
|
57
|
+
* apart.
|
|
58
|
+
*
|
|
59
|
+
* Used by the composable `astro/fetch` `middleware()` entry point, where
|
|
60
|
+
* there is no surrounding `AstroHandler` to supply this fallback.
|
|
61
|
+
*/
|
|
62
|
+
async handleWithErrorFallback(app, state, renderRouteCallback) {
|
|
63
|
+
if (!state.routeData) {
|
|
64
|
+
return new Response(null, { status: 404, headers: { [ASTRO_ERROR_HEADER]: "true" } });
|
|
65
|
+
}
|
|
66
|
+
let nextError;
|
|
67
|
+
try {
|
|
68
|
+
return await this.handle(state, async (s, ctx) => {
|
|
69
|
+
try {
|
|
70
|
+
return await renderRouteCallback(s, ctx);
|
|
71
|
+
} catch (err) {
|
|
72
|
+
nextError = err;
|
|
73
|
+
throw err;
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
} catch (err) {
|
|
77
|
+
if (err === nextError) throw err;
|
|
78
|
+
app.logger.error(null, err.stack || err.message || String(err));
|
|
79
|
+
return app.renderError(state.request, {
|
|
80
|
+
...state.renderOptions,
|
|
81
|
+
status: 500,
|
|
82
|
+
error: err,
|
|
83
|
+
pathname: state.pathname
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
44
87
|
#finalize(state, response) {
|
|
45
88
|
attachCookiesToResponse(response, state.cookies);
|
|
46
89
|
return response;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { APIContext } from '../../types/public/context.js';
|
|
2
|
+
import type { BaseApp } from '../app/base.js';
|
|
2
3
|
import type { FetchState } from '../fetch/fetch-state.js';
|
|
3
4
|
import type { Pipeline } from '../base-pipeline.js';
|
|
4
5
|
/**
|
|
@@ -17,4 +18,16 @@ export declare class PagesHandler {
|
|
|
17
18
|
#private;
|
|
18
19
|
constructor(pipeline: Pipeline);
|
|
19
20
|
handle(state: FetchState, ctx: APIContext): Promise<Response>;
|
|
21
|
+
/**
|
|
22
|
+
* Like `handle`, but mirrors the app-level error handling that
|
|
23
|
+
* `AstroHandler` provides on the standard path: unmatched routes
|
|
24
|
+
* return a 404 marked with `X-Astro-Error` for the app's post-check
|
|
25
|
+
* to render the 404 error page, and render-time errors are logged
|
|
26
|
+
* and render the 500 error page instead of propagating to the host
|
|
27
|
+
* framework.
|
|
28
|
+
*
|
|
29
|
+
* Used by the composable `astro/fetch` `pages()` entry point, where
|
|
30
|
+
* there is no surrounding `AstroHandler` to supply this fallback.
|
|
31
|
+
*/
|
|
32
|
+
handleWithErrorFallback(app: BaseApp<Pipeline>, state: FetchState): Promise<Response>;
|
|
20
33
|
}
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
import { renderEndpoint } from "../../runtime/server/endpoint.js";
|
|
2
2
|
import { renderPage } from "../../runtime/server/index.js";
|
|
3
|
-
import {
|
|
4
|
-
ASTRO_ERROR_HEADER,
|
|
5
|
-
REROUTE_DIRECTIVE_HEADER,
|
|
6
|
-
REWRITE_DIRECTIVE_HEADER_KEY,
|
|
7
|
-
REWRITE_DIRECTIVE_HEADER_VALUE,
|
|
8
|
-
ROUTE_TYPE_HEADER
|
|
9
|
-
} from "../constants.js";
|
|
3
|
+
import { ASTRO_ERROR_HEADER } from "../constants.js";
|
|
10
4
|
import { getCookiesFromResponse } from "../cookies/response.js";
|
|
11
5
|
const EMPTY_SLOTS = Object.freeze({});
|
|
12
6
|
class PagesHandler {
|
|
@@ -17,6 +11,7 @@ class PagesHandler {
|
|
|
17
11
|
async handle(state, ctx) {
|
|
18
12
|
const pipeline = this.#pipeline;
|
|
19
13
|
const { logger, streaming } = pipeline;
|
|
14
|
+
state.resetResponseMetadata();
|
|
20
15
|
let response;
|
|
21
16
|
const componentInstance = await state.loadComponentInstance();
|
|
22
17
|
switch (state.routeData.type) {
|
|
@@ -25,7 +20,8 @@ class PagesHandler {
|
|
|
25
20
|
componentInstance,
|
|
26
21
|
ctx,
|
|
27
22
|
state.routeData.prerender,
|
|
28
|
-
logger
|
|
23
|
+
logger,
|
|
24
|
+
state
|
|
29
25
|
);
|
|
30
26
|
break;
|
|
31
27
|
}
|
|
@@ -46,12 +42,9 @@ class PagesHandler {
|
|
|
46
42
|
result.cancelled = true;
|
|
47
43
|
throw e;
|
|
48
44
|
}
|
|
49
|
-
|
|
45
|
+
state.responseRouteType = "page";
|
|
50
46
|
if (state.routeData.route === "/404" || state.routeData.route === "/500") {
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
if (state.isRewriting) {
|
|
54
|
-
response.headers.set(REWRITE_DIRECTIVE_HEADER_KEY, REWRITE_DIRECTIVE_HEADER_VALUE);
|
|
47
|
+
state.skipErrorReroute = true;
|
|
55
48
|
}
|
|
56
49
|
break;
|
|
57
50
|
}
|
|
@@ -59,7 +52,8 @@ class PagesHandler {
|
|
|
59
52
|
return new Response(null, { status: 404, headers: { [ASTRO_ERROR_HEADER]: "true" } });
|
|
60
53
|
}
|
|
61
54
|
case "fallback": {
|
|
62
|
-
|
|
55
|
+
state.responseRouteType = "fallback";
|
|
56
|
+
return new Response(null, { status: 500 });
|
|
63
57
|
}
|
|
64
58
|
}
|
|
65
59
|
const responseCookies = getCookiesFromResponse(response);
|
|
@@ -69,6 +63,33 @@ class PagesHandler {
|
|
|
69
63
|
state.response = response;
|
|
70
64
|
return response;
|
|
71
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Like `handle`, but mirrors the app-level error handling that
|
|
68
|
+
* `AstroHandler` provides on the standard path: unmatched routes
|
|
69
|
+
* return a 404 marked with `X-Astro-Error` for the app's post-check
|
|
70
|
+
* to render the 404 error page, and render-time errors are logged
|
|
71
|
+
* and render the 500 error page instead of propagating to the host
|
|
72
|
+
* framework.
|
|
73
|
+
*
|
|
74
|
+
* Used by the composable `astro/fetch` `pages()` entry point, where
|
|
75
|
+
* there is no surrounding `AstroHandler` to supply this fallback.
|
|
76
|
+
*/
|
|
77
|
+
async handleWithErrorFallback(app, state) {
|
|
78
|
+
if (!state.routeData) {
|
|
79
|
+
return new Response(null, { status: 404, headers: { [ASTRO_ERROR_HEADER]: "true" } });
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
return await this.handle(state, state.getAPIContext());
|
|
83
|
+
} catch (err) {
|
|
84
|
+
app.logger.error(null, err.stack || err.message || String(err));
|
|
85
|
+
return app.renderError(state.request, {
|
|
86
|
+
...state.renderOptions,
|
|
87
|
+
status: 500,
|
|
88
|
+
error: err,
|
|
89
|
+
pathname: state.pathname
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
72
93
|
}
|
|
73
94
|
export {
|
|
74
95
|
PagesHandler
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { ActionHandler } from "../../actions/handler.js";
|
|
2
|
-
import {
|
|
3
|
-
REROUTABLE_STATUS_CODES,
|
|
4
|
-
REROUTE_DIRECTIVE_HEADER,
|
|
5
|
-
REWRITE_DIRECTIVE_HEADER_KEY
|
|
6
|
-
} from "../constants.js";
|
|
2
|
+
import { REROUTABLE_STATUS_CODES } from "../constants.js";
|
|
7
3
|
import { TrailingSlashHandler } from "./trailing-slash-handler.js";
|
|
8
4
|
import { CacheHandler, provideCache } from "../cache/handler.js";
|
|
9
5
|
import { I18n } from "../i18n/handler.js";
|
|
@@ -65,6 +61,9 @@ class AstroHandler {
|
|
|
65
61
|
}
|
|
66
62
|
async handle(state) {
|
|
67
63
|
state.pipeline.usedFeatures |= ALL_PIPELINE_FEATURES;
|
|
64
|
+
if (state.invalidEncoding) {
|
|
65
|
+
return new Response(null, { status: 400, statusText: "Bad Request" });
|
|
66
|
+
}
|
|
68
67
|
const trailingSlashRedirect = this.#trailingSlashHandler.handle(state);
|
|
69
68
|
if (trailingSlashRedirect) {
|
|
70
69
|
return trailingSlashRedirect;
|
|
@@ -128,12 +127,11 @@ class AstroHandler {
|
|
|
128
127
|
};
|
|
129
128
|
response = await this.#cacheHandler.handle(state, runPipeline);
|
|
130
129
|
}
|
|
131
|
-
const isRewrite = response.headers.has(REWRITE_DIRECTIVE_HEADER_KEY);
|
|
132
130
|
this.#app.logThisRequest({
|
|
133
131
|
pathname,
|
|
134
132
|
method: request.method,
|
|
135
133
|
statusCode: response.status,
|
|
136
|
-
isRewrite,
|
|
134
|
+
isRewrite: state.isRewriting,
|
|
137
135
|
timeStart: state.timeStart
|
|
138
136
|
});
|
|
139
137
|
} catch (err) {
|
|
@@ -150,7 +148,7 @@ class AstroHandler {
|
|
|
150
148
|
}
|
|
151
149
|
if (REROUTABLE_STATUS_CODES.includes(response.status) && // If the body isn't null, that means the user sets the 404 status
|
|
152
150
|
// but uses the current route to handle the 404
|
|
153
|
-
response.body === null &&
|
|
151
|
+
response.body === null && !state.skipErrorReroute) {
|
|
154
152
|
return this.#app.renderError(request, {
|
|
155
153
|
...state.renderOptions,
|
|
156
154
|
response,
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
trimSlashes
|
|
11
11
|
} from "../path.js";
|
|
12
12
|
import { createRequest } from "../request.js";
|
|
13
|
+
import { validateAndDecodePathname } from "../util/pathname.js";
|
|
13
14
|
import { DEFAULT_404_ROUTE } from "./internal/astro-designed-error-pages.js";
|
|
14
15
|
import { isRoute404, isRoute500 } from "./internal/route-errors.js";
|
|
15
16
|
function findRouteToRewrite({
|
|
@@ -36,7 +37,7 @@ function findRouteToRewrite({
|
|
|
36
37
|
buildFormat
|
|
37
38
|
);
|
|
38
39
|
newUrl.pathname = resolvedUrlPathname;
|
|
39
|
-
const decodedPathname =
|
|
40
|
+
const decodedPathname = validateAndDecodePathname(pathname);
|
|
40
41
|
if (isRoute404(decodedPathname)) {
|
|
41
42
|
const errorRoute = routes.find((route) => route.route === "/404");
|
|
42
43
|
if (errorRoute) {
|
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* decoded iteratively instead of rejected. Kept for backwards compatibility
|
|
8
|
-
* in case third-party code references the class.
|
|
2
|
+
* Thrown when a URL path is encoded so many times that we give up decoding it
|
|
3
|
+
* (see {@link validateAndDecodePathname}). When this happens we reject the
|
|
4
|
+
* request with a `400` instead of guessing the path. If we let a half-decoded
|
|
5
|
+
* path through, your middleware might check one path while Astro routes to a
|
|
6
|
+
* different one.
|
|
9
7
|
*/
|
|
10
8
|
export declare class MultiLevelEncodingError extends Error {
|
|
11
9
|
constructor();
|
|
12
10
|
}
|
|
13
11
|
/**
|
|
14
|
-
* Decodes a
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* fully resolve it so middleware always sees the true decoded path.
|
|
12
|
+
* Decodes a URL path over and over until it stops changing, so a path that was
|
|
13
|
+
* encoded several times ends up as a single, final path. This stops someone
|
|
14
|
+
* from sneaking a path like `/admin` past middleware by encoding it multiple
|
|
15
|
+
* times — middleware always sees the real, decoded path.
|
|
19
16
|
*
|
|
20
|
-
* @param pathname - The
|
|
21
|
-
* @returns The fully decoded
|
|
22
|
-
* @throws Error if the
|
|
23
|
-
*
|
|
17
|
+
* @param pathname - The path to decode
|
|
18
|
+
* @returns The final, fully decoded path
|
|
19
|
+
* @throws Error if the path has broken encoding that can't be decoded at all
|
|
20
|
+
* (for example a lone `%` that isn't followed by two hex digits)
|
|
21
|
+
* @throws MultiLevelEncodingError if the path is still changing after
|
|
22
|
+
* {@link MAX_DECODE_ITERATIONS} tries (it was encoded too many times).
|
|
23
|
+
* Handing back a half-decoded path here would bring back the security hole
|
|
24
|
+
* this function exists to close.
|
|
24
25
|
*/
|
|
25
26
|
export declare function validateAndDecodePathname(pathname: string): string;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
class MultiLevelEncodingError extends Error {
|
|
2
2
|
constructor() {
|
|
3
|
-
super("
|
|
3
|
+
super("URL encoding depth exceeded the maximum number of decode iterations");
|
|
4
4
|
this.name = "MultiLevelEncodingError";
|
|
5
5
|
}
|
|
6
6
|
}
|
|
7
|
+
const MAX_DECODE_ITERATIONS = 10;
|
|
7
8
|
function validateAndDecodePathname(pathname) {
|
|
8
9
|
let decoded;
|
|
9
10
|
try {
|
|
@@ -12,7 +13,10 @@ function validateAndDecodePathname(pathname) {
|
|
|
12
13
|
throw new Error("Invalid URL encoding");
|
|
13
14
|
}
|
|
14
15
|
let iterations = 0;
|
|
15
|
-
while (decoded !== pathname
|
|
16
|
+
while (decoded !== pathname) {
|
|
17
|
+
if (iterations >= MAX_DECODE_ITERATIONS) {
|
|
18
|
+
throw new MultiLevelEncodingError();
|
|
19
|
+
}
|
|
16
20
|
pathname = decoded;
|
|
17
21
|
try {
|
|
18
22
|
decoded = decodeURI(pathname);
|
package/dist/i18n/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
removeTrailingForwardSlash
|
|
5
5
|
} from "@astrojs/internal-helpers/path";
|
|
6
6
|
import { shouldAppendForwardSlash } from "../core/build/util.js";
|
|
7
|
-
import {
|
|
7
|
+
import { getFetchStateFromAPIContext } from "../core/fetch/fetch-state.js";
|
|
8
8
|
import { i18nNoLocaleFoundInPath, MissingLocale } from "../core/errors/errors-data.js";
|
|
9
9
|
import { AstroError } from "../core/errors/index.js";
|
|
10
10
|
import { createI18nMiddleware } from "./middleware.js";
|
|
@@ -200,24 +200,22 @@ function redirectToDefaultLocale({
|
|
|
200
200
|
}
|
|
201
201
|
function notFound({ base, locales, fallback }) {
|
|
202
202
|
return function(context, response) {
|
|
203
|
-
|
|
203
|
+
const fetchState = getFetchStateFromAPIContext(context);
|
|
204
|
+
if (fetchState.skipErrorReroute && typeof fallback === "undefined") {
|
|
204
205
|
return response;
|
|
205
206
|
}
|
|
206
207
|
const url = context.url;
|
|
207
208
|
const isRoot = url.pathname === base + "/" || url.pathname === base;
|
|
208
209
|
if (!(isRoot || pathHasLocale(url.pathname, locales))) {
|
|
210
|
+
fetchState.skipErrorReroute = true;
|
|
209
211
|
if (response) {
|
|
210
|
-
response.headers.set(REROUTE_DIRECTIVE_HEADER, "no");
|
|
211
212
|
return new Response(response.body, {
|
|
212
213
|
status: 404,
|
|
213
214
|
headers: response.headers
|
|
214
215
|
});
|
|
215
216
|
} else {
|
|
216
217
|
return new Response(null, {
|
|
217
|
-
status: 404
|
|
218
|
-
headers: {
|
|
219
|
-
[REROUTE_DIRECTIVE_HEADER]: "no"
|
|
220
|
-
}
|
|
218
|
+
status: 404
|
|
221
219
|
});
|
|
222
220
|
}
|
|
223
221
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import type { FetchState } from '../../core/fetch/fetch-state.js';
|
|
1
2
|
import type { AstroLogger } from '../../core/logger/core.js';
|
|
2
3
|
import type { APIRoute } from '../../types/public/common.js';
|
|
3
4
|
import type { APIContext } from '../../types/public/context.js';
|
|
4
5
|
/** Renders an endpoint request to completion, returning the body. */
|
|
5
6
|
export declare function renderEndpoint(mod: {
|
|
6
7
|
[method: string]: APIRoute;
|
|
7
|
-
}, context: APIContext, isPrerendered: boolean, logger: AstroLogger): Promise<Response>;
|
|
8
|
+
}, context: APIContext, isPrerendered: boolean, logger: AstroLogger, state?: FetchState): Promise<Response>;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import colors from "piccolore";
|
|
2
|
-
import { REROUTABLE_STATUS_CODES
|
|
2
|
+
import { REROUTABLE_STATUS_CODES } from "../../core/constants.js";
|
|
3
3
|
import { AstroError } from "../../core/errors/errors.js";
|
|
4
4
|
import { EndpointDidNotReturnAResponse } from "../../core/errors/errors-data.js";
|
|
5
|
-
async function renderEndpoint(mod, context, isPrerendered, logger) {
|
|
5
|
+
async function renderEndpoint(mod, context, isPrerendered, logger, state) {
|
|
6
6
|
const { request, url } = context;
|
|
7
7
|
const method = request.method.toUpperCase();
|
|
8
8
|
let handler = mod[method] ?? mod["ALL"];
|
|
@@ -38,17 +38,8 @@ Found handlers: ${Object.keys(mod).map((exp) => JSON.stringify(exp)).join(", ")}
|
|
|
38
38
|
if (!response || response instanceof Response === false) {
|
|
39
39
|
throw new AstroError(EndpointDidNotReturnAResponse);
|
|
40
40
|
}
|
|
41
|
-
if (REROUTABLE_STATUS_CODES.includes(response.status)) {
|
|
42
|
-
|
|
43
|
-
response.headers.set(REROUTE_DIRECTIVE_HEADER, "no");
|
|
44
|
-
} catch (err) {
|
|
45
|
-
if (err.message?.includes("immutable")) {
|
|
46
|
-
response = new Response(response.body, response);
|
|
47
|
-
response.headers.set(REROUTE_DIRECTIVE_HEADER, "no");
|
|
48
|
-
} else {
|
|
49
|
-
throw err;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
41
|
+
if (state && REROUTABLE_STATUS_CODES.includes(response.status)) {
|
|
42
|
+
state.skipErrorReroute = true;
|
|
52
43
|
}
|
|
53
44
|
if (method === "HEAD") {
|
|
54
45
|
return new Response(null, response);
|
|
@@ -420,22 +420,22 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
|
|
|
420
420
|
* @docs
|
|
421
421
|
* @name compressHTML
|
|
422
422
|
* @type {boolean | "jsx"}
|
|
423
|
-
* @default `
|
|
423
|
+
* @default `'jsx'`
|
|
424
424
|
* @description
|
|
425
425
|
*
|
|
426
426
|
* Controls how Astro handles whitespace in your HTML. This affects both development mode and the final build output.
|
|
427
427
|
*
|
|
428
|
-
*
|
|
428
|
+
* Since v7.0, Astro applies by default the JSX whitespace rules used by frameworks like React. This removes whitespace and line breaks around elements, collapses multi-line text onto a single line, and preserves whitespace within a single line (e.g. a space between two inline elements). To keep a space that would otherwise be removed, include it explicitly in the source through constructs such as `{" "}`.
|
|
429
429
|
*
|
|
430
|
-
*
|
|
430
|
+
* Setting this option to `true` instead removes whitespace, including line breaks, in a lossless manner from `.astro` components. Some whitespace may be preserved as needed to maintain the visual rendering of your HTML.
|
|
431
431
|
*
|
|
432
|
-
* Setting this option to false disables HTML compression and preserves all whitespace.
|
|
432
|
+
* Setting this option to `false` disables HTML compression and preserves all whitespace.
|
|
433
433
|
*
|
|
434
434
|
* ```js
|
|
435
435
|
* {
|
|
436
|
-
* compressHTML:
|
|
436
|
+
* compressHTML: true
|
|
437
437
|
* // or:
|
|
438
|
-
* // compressHTML:
|
|
438
|
+
* // compressHTML: false
|
|
439
439
|
* }
|
|
440
440
|
* ```
|
|
441
441
|
*/
|
|
@@ -4,7 +4,7 @@ import type { SSRLoadedRenderer } from '../types/public/internal.js';
|
|
|
4
4
|
* Use this function to provide renderers to the `AstroContainer`:
|
|
5
5
|
*
|
|
6
6
|
* ```js
|
|
7
|
-
* import { getContainerRenderer } from "@astrojs/react";
|
|
7
|
+
* import { getContainerRenderer } from "@astrojs/react/container-renderer";
|
|
8
8
|
* import { experimental_AstroContainer as AstroContainer } from "astro/container";
|
|
9
9
|
* import { loadRenderers } from "astro:container"; // use this only when using vite/vitest
|
|
10
10
|
*
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { hasHeadPropagationCall } from "../core/head-propagation/hint.js";
|
|
2
1
|
import {
|
|
3
2
|
buildImporterGraphFromModuleInfo,
|
|
4
3
|
computeInTreeAncestors
|
|
5
4
|
} from "../core/head-propagation/graph.js";
|
|
6
5
|
import { getTopLevelPageModuleInfos } from "../core/build/graph.js";
|
|
6
|
+
import { PROPAGATED_ASSET_FLAG } from "../content/consts.js";
|
|
7
7
|
import { getAstroMetadata } from "../vite-plugin-astro/index.js";
|
|
8
8
|
import { ASTRO_VITE_ENVIRONMENT_NAMES } from "../core/constants.js";
|
|
9
9
|
const VIRTUAL_COMPONENT_METADATA = "virtual:astro:component-metadata";
|
|
@@ -127,12 +127,12 @@ function configHeadVitePlugin() {
|
|
|
127
127
|
});
|
|
128
128
|
}
|
|
129
129
|
},
|
|
130
|
-
transform(
|
|
130
|
+
transform(_source, id) {
|
|
131
131
|
let info = this.getModuleInfo(id);
|
|
132
132
|
if (info && getAstroMetadata(info)?.containsHead) {
|
|
133
133
|
propagateMetadata.call(this, id, "containsHead", true);
|
|
134
134
|
}
|
|
135
|
-
if (
|
|
135
|
+
if (id.includes(PROPAGATED_ASSET_FLAG)) {
|
|
136
136
|
propagateMetadata.call(this, id, "propagation", "in-tree");
|
|
137
137
|
}
|
|
138
138
|
invalidateComponentMetadataModule();
|
|
@@ -140,17 +140,11 @@ function configHeadVitePlugin() {
|
|
|
140
140
|
};
|
|
141
141
|
}
|
|
142
142
|
function astroHeadBuildPlugin(internals) {
|
|
143
|
-
const headPropagationModuleIds = /* @__PURE__ */ new Set();
|
|
144
143
|
return {
|
|
145
144
|
name: "astro:head-metadata-build",
|
|
146
145
|
applyToEnvironment(environment) {
|
|
147
146
|
return environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.ssr || environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.prerender;
|
|
148
147
|
},
|
|
149
|
-
transform(source, id) {
|
|
150
|
-
if (hasHeadPropagationCall(source)) {
|
|
151
|
-
headPropagationModuleIds.add(id);
|
|
152
|
-
}
|
|
153
|
-
},
|
|
154
148
|
generateBundle(_opts, bundle) {
|
|
155
149
|
const map = internals.componentMetadata;
|
|
156
150
|
const moduleIds = /* @__PURE__ */ new Set();
|
|
@@ -167,7 +161,7 @@ function astroHeadBuildPlugin(internals) {
|
|
|
167
161
|
}
|
|
168
162
|
for (const [, output] of Object.entries(bundle)) {
|
|
169
163
|
if (output.type !== "chunk") continue;
|
|
170
|
-
for (const [id
|
|
164
|
+
for (const [id] of Object.entries(output.modules)) {
|
|
171
165
|
moduleIds.add(id);
|
|
172
166
|
const modinfo = this.getModuleInfo(id);
|
|
173
167
|
if (modinfo) {
|
|
@@ -182,7 +176,7 @@ function astroHeadBuildPlugin(internals) {
|
|
|
182
176
|
selfPropagationSeeds.add(id);
|
|
183
177
|
}
|
|
184
178
|
}
|
|
185
|
-
if (
|
|
179
|
+
if (id.includes(PROPAGATED_ASSET_FLAG)) {
|
|
186
180
|
commentPropagationSeeds.add(id);
|
|
187
181
|
}
|
|
188
182
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro",
|
|
3
|
-
"version": "7.0.0-beta.
|
|
3
|
+
"version": "7.0.0-beta.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",
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
"README.md"
|
|
113
113
|
],
|
|
114
114
|
"dependencies": {
|
|
115
|
-
"@astrojs/compiler-rs": "^0.
|
|
115
|
+
"@astrojs/compiler-rs": "^0.2.2",
|
|
116
116
|
"@capsizecss/unpack": "^4.0.0",
|
|
117
117
|
"@clack/prompts": "^1.1.0",
|
|
118
118
|
"@oslojs/encoding": "^1.1.0",
|
|
@@ -166,8 +166,8 @@
|
|
|
166
166
|
"yargs-parser": "^22.0.0",
|
|
167
167
|
"zod": "^4.3.6",
|
|
168
168
|
"@astrojs/internal-helpers": "0.10.0",
|
|
169
|
-
"@astrojs/
|
|
170
|
-
"@astrojs/
|
|
169
|
+
"@astrojs/markdown-satteri": "0.3.1-beta.1",
|
|
170
|
+
"@astrojs/telemetry": "3.3.2"
|
|
171
171
|
},
|
|
172
172
|
"optionalDependencies": {
|
|
173
173
|
"sharp": "^0.34.0"
|
|
@@ -208,8 +208,8 @@
|
|
|
208
208
|
"unified": "^11.0.5",
|
|
209
209
|
"vitest": "^4.1.0",
|
|
210
210
|
"@astrojs/check": "0.9.9",
|
|
211
|
-
"
|
|
212
|
-
"
|
|
211
|
+
"@astrojs/markdown-remark": "7.2.0",
|
|
212
|
+
"astro-scripts": "0.0.14"
|
|
213
213
|
},
|
|
214
214
|
"engines": {
|
|
215
215
|
"node": ">=22.12.0",
|