astro 2.5.7 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/@types/astro.d.ts +109 -90
- package/dist/assets/generate.js +2 -2
- package/dist/core/app/index.js +27 -18
- package/dist/core/app/types.d.ts +1 -2
- package/dist/core/build/common.js +2 -0
- package/dist/core/build/generate.js +76 -13
- package/dist/core/build/internal.d.ts +2 -0
- package/dist/core/build/internal.js +18 -1
- package/dist/core/build/plugins/plugin-css.js +1 -1
- package/dist/core/build/plugins/plugin-middleware.js +1 -7
- package/dist/core/build/plugins/plugin-pages.d.ts +1 -0
- package/dist/core/build/plugins/plugin-pages.js +14 -4
- package/dist/core/build/plugins/plugin-ssr.js +10 -14
- package/dist/core/build/static-build.js +9 -10
- package/dist/core/config/config.js +1 -10
- package/dist/core/config/schema.d.ts +48 -64
- package/dist/core/config/schema.js +13 -11
- package/dist/core/config/settings.js +2 -2
- package/dist/core/constants.js +1 -1
- package/dist/core/dev/dev.js +1 -1
- package/dist/core/endpoint/index.js +2 -2
- package/dist/core/errors/errors-data.d.ts +12 -20
- package/dist/core/errors/errors-data.js +13 -1
- package/dist/core/messages.js +2 -2
- package/dist/core/path.d.ts +1 -15
- package/dist/core/path.js +1 -80
- package/dist/core/redirects/component.d.ts +4 -0
- package/dist/core/redirects/component.js +19 -0
- package/dist/core/redirects/helpers.d.ts +4 -0
- package/dist/core/redirects/helpers.js +29 -0
- package/dist/core/redirects/index.d.ts +3 -0
- package/dist/core/redirects/index.js +11 -0
- package/dist/core/redirects/validate.d.ts +1 -0
- package/dist/core/redirects/validate.js +13 -0
- package/dist/core/render/context.d.ts +2 -1
- package/dist/core/render/core.d.ts +2 -1
- package/dist/core/render/core.js +18 -1
- package/dist/core/render/dev/environment.js +2 -2
- package/dist/core/render/result.d.ts +2 -0
- package/dist/core/render/result.js +3 -3
- package/dist/core/routing/manifest/create.js +50 -4
- package/dist/core/util.js +10 -3
- package/dist/integrations/index.js +3 -8
- package/dist/prerender/utils.d.ts +2 -2
- package/dist/prerender/utils.js +6 -6
- package/dist/runtime/server/astro-island.js +7 -4
- package/dist/runtime/server/astro-island.prebuilt.d.ts +1 -1
- package/dist/runtime/server/astro-island.prebuilt.js +1 -1
- package/dist/runtime/server/render/common.js +6 -0
- package/dist/runtime/server/render/component.js +2 -2
- package/dist/runtime/server/render/page.d.ts +1 -1
- package/dist/runtime/server/render/page.js +10 -6
- package/dist/vite-plugin-astro-server/request.js +2 -2
- package/dist/vite-plugin-astro-server/route.js +16 -9
- package/dist/vite-plugin-html/transform/index.js +3 -7
- package/dist/vite-plugin-scanner/index.js +4 -4
- package/env.d.ts +2 -2
- package/package.json +2 -1
- package/tsconfigs/base.json +3 -1
- package/tsconfigs/strictest.json +3 -1
package/dist/@types/astro.d.ts
CHANGED
|
@@ -73,7 +73,7 @@ export interface CLIFlags {
|
|
|
73
73
|
drafts?: boolean;
|
|
74
74
|
open?: boolean;
|
|
75
75
|
experimentalAssets?: boolean;
|
|
76
|
-
|
|
76
|
+
experimentalRedirects?: boolean;
|
|
77
77
|
}
|
|
78
78
|
export interface BuildConfig {
|
|
79
79
|
/**
|
|
@@ -396,6 +396,50 @@ export interface AstroUserConfig {
|
|
|
396
396
|
* ```
|
|
397
397
|
*/
|
|
398
398
|
cacheDir?: string;
|
|
399
|
+
/**
|
|
400
|
+
* @docs
|
|
401
|
+
* @name redirects (Experimental)
|
|
402
|
+
* @type {RedirectConfig}
|
|
403
|
+
* @default `{}`
|
|
404
|
+
* @version 2.6.0
|
|
405
|
+
* @description Specify a mapping of redirects where the key is the route to match
|
|
406
|
+
* and the value is the path to redirect to.
|
|
407
|
+
*
|
|
408
|
+
* You can redirect both static and dynamic routes, but only to the same kind of route.
|
|
409
|
+
* For example you cannot have a `'/article': '/blog/[...slug]'` redirect.
|
|
410
|
+
*
|
|
411
|
+
*
|
|
412
|
+
* ```js
|
|
413
|
+
* {
|
|
414
|
+
* redirects: {
|
|
415
|
+
* '/old': '/new',
|
|
416
|
+
* '/blog/[...slug]': '/articles/[...slug]',
|
|
417
|
+
* }
|
|
418
|
+
* }
|
|
419
|
+
* ```
|
|
420
|
+
*
|
|
421
|
+
*
|
|
422
|
+
* For statically-generated sites with no adapter installed, this will produce a client redirect using a [`<meta http-equiv="refresh">` tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta#http-equiv) and does not support status codes.
|
|
423
|
+
*
|
|
424
|
+
* When using SSR or with a static adapter in `output: static`
|
|
425
|
+
* mode, status codes are supported.
|
|
426
|
+
* Astro will serve redirected GET requests with a status of `301`
|
|
427
|
+
* and use a status of `308` for any other request method.
|
|
428
|
+
*
|
|
429
|
+
* You can customize the [redirection status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#redirection_messages) using an object in the redirect config:
|
|
430
|
+
*
|
|
431
|
+
* ```js
|
|
432
|
+
* {
|
|
433
|
+
* redirects: {
|
|
434
|
+
* '/other': {
|
|
435
|
+
* status: 302,
|
|
436
|
+
* destination: '/place',
|
|
437
|
+
* },
|
|
438
|
+
* }
|
|
439
|
+
* }
|
|
440
|
+
* ```
|
|
441
|
+
*/
|
|
442
|
+
redirects?: RedirectConfig;
|
|
399
443
|
/**
|
|
400
444
|
* @docs
|
|
401
445
|
* @name site
|
|
@@ -670,6 +714,50 @@ export interface AstroUserConfig {
|
|
|
670
714
|
* ```
|
|
671
715
|
*/
|
|
672
716
|
serverEntry?: string;
|
|
717
|
+
/**
|
|
718
|
+
* @docs
|
|
719
|
+
* @name build.redirects
|
|
720
|
+
* @type {boolean}
|
|
721
|
+
* @default `true`
|
|
722
|
+
* @version 2.6.0
|
|
723
|
+
* @description
|
|
724
|
+
* Specifies whether redirects will be output to HTML during the build.
|
|
725
|
+
* This option only applies to `output: 'static'` mode; in SSR redirects
|
|
726
|
+
* are treated the same as all responses.
|
|
727
|
+
*
|
|
728
|
+
* This option is mostly meant to be used by adapters that have special
|
|
729
|
+
* configuration files for redirects and do not need/want HTML based redirects.
|
|
730
|
+
*
|
|
731
|
+
* ```js
|
|
732
|
+
* {
|
|
733
|
+
* build: {
|
|
734
|
+
* redirects: false
|
|
735
|
+
* }
|
|
736
|
+
* }
|
|
737
|
+
* ```
|
|
738
|
+
*/
|
|
739
|
+
redirects?: boolean;
|
|
740
|
+
/**
|
|
741
|
+
* @docs
|
|
742
|
+
* @name build.inlineStylesheets
|
|
743
|
+
* @type {('always' | 'auto' | 'never')}
|
|
744
|
+
* @default `never`
|
|
745
|
+
* @version 2.6.0
|
|
746
|
+
* @description
|
|
747
|
+
* Control whether styles are sent to the browser in a separate css file or inlined into `<style>` tags. Choose from the following options:
|
|
748
|
+
* - `'always'` - all styles are inlined into `<style>` tags
|
|
749
|
+
* - `'auto'` - only stylesheets smaller than `ViteConfig.build.assetsInlineLimit` (default: 4kb) are inlined. Otherwise, styles are sent in external stylesheets.
|
|
750
|
+
* - `'never'` - all styles are sent in external stylesheets
|
|
751
|
+
*
|
|
752
|
+
* ```js
|
|
753
|
+
* {
|
|
754
|
+
* build: {
|
|
755
|
+
* inlineStylesheets: `auto`,
|
|
756
|
+
* },
|
|
757
|
+
* }
|
|
758
|
+
* ```
|
|
759
|
+
*/
|
|
760
|
+
inlineStylesheets?: 'always' | 'auto' | 'never';
|
|
673
761
|
};
|
|
674
762
|
/**
|
|
675
763
|
* @docs
|
|
@@ -1001,103 +1089,24 @@ export interface AstroUserConfig {
|
|
|
1001
1089
|
assets?: boolean;
|
|
1002
1090
|
/**
|
|
1003
1091
|
* @docs
|
|
1004
|
-
* @name experimental.
|
|
1005
|
-
* @type {('always' | 'auto' | 'never')}
|
|
1006
|
-
* @default `never`
|
|
1007
|
-
* @version 2.4.0
|
|
1008
|
-
* @description
|
|
1009
|
-
* Control whether styles are sent to the browser in a separate css file or inlined into `<style>` tags. Choose from the following options:
|
|
1010
|
-
* - `'always'` - all styles are inlined into `<style>` tags
|
|
1011
|
-
* - `'auto'` - only stylesheets smaller than `ViteConfig.build.assetsInlineLimit` (default: 4kb) are inlined. Otherwise, styles are sent in external stylesheets.
|
|
1012
|
-
* - `'never'` - all styles are sent in external stylesheets
|
|
1013
|
-
*
|
|
1014
|
-
* ```js
|
|
1015
|
-
* {
|
|
1016
|
-
* experimental: {
|
|
1017
|
-
* inlineStylesheets: `auto`,
|
|
1018
|
-
* },
|
|
1019
|
-
* }
|
|
1020
|
-
* ```
|
|
1021
|
-
*/
|
|
1022
|
-
inlineStylesheets?: 'always' | 'auto' | 'never';
|
|
1023
|
-
/**
|
|
1024
|
-
* @docs
|
|
1025
|
-
* @name experimental.customClientDirectives
|
|
1092
|
+
* @name experimental.redirects
|
|
1026
1093
|
* @type {boolean}
|
|
1027
1094
|
* @default `false`
|
|
1028
|
-
* @version 2.
|
|
1095
|
+
* @version 2.6.0
|
|
1029
1096
|
* @description
|
|
1030
|
-
*
|
|
1031
|
-
*
|
|
1032
|
-
*
|
|
1033
|
-
* To enable this feature, set `experimental.customClientDirectives` to `true` in your Astro config:
|
|
1097
|
+
* Enable experimental support for redirect configuration. With this enabled
|
|
1098
|
+
* you can set redirects via the top-level `redirects` property. To enable
|
|
1099
|
+
* this feature, set `experimental.redirects` to `true`.
|
|
1034
1100
|
*
|
|
1035
1101
|
* ```js
|
|
1036
1102
|
* {
|
|
1037
1103
|
* experimental: {
|
|
1038
|
-
*
|
|
1104
|
+
* redirects: true,
|
|
1039
1105
|
* },
|
|
1040
1106
|
* }
|
|
1041
1107
|
* ```
|
|
1042
1108
|
*/
|
|
1043
|
-
|
|
1044
|
-
/**
|
|
1045
|
-
* @docs
|
|
1046
|
-
* @name experimental.middleware
|
|
1047
|
-
* @type {boolean}
|
|
1048
|
-
* @default `false`
|
|
1049
|
-
* @version 2.4.0
|
|
1050
|
-
* @description
|
|
1051
|
-
* Enable experimental support for Astro middleware.
|
|
1052
|
-
*
|
|
1053
|
-
* To enable this feature, set `experimental.middleware` to `true` in your Astro config:
|
|
1054
|
-
*
|
|
1055
|
-
* ```js
|
|
1056
|
-
* {
|
|
1057
|
-
* experimental: {
|
|
1058
|
-
* middleware: true,
|
|
1059
|
-
* },
|
|
1060
|
-
* }
|
|
1061
|
-
* ```
|
|
1062
|
-
*/
|
|
1063
|
-
middleware?: boolean;
|
|
1064
|
-
/**
|
|
1065
|
-
* @docs
|
|
1066
|
-
* @name experimental.hybridOutput
|
|
1067
|
-
* @type {boolean}
|
|
1068
|
-
* @default `false`
|
|
1069
|
-
* @version 2.5.0
|
|
1070
|
-
* @description
|
|
1071
|
-
* Enable experimental support for hybrid SSR with pre-rendering enabled by default.
|
|
1072
|
-
*
|
|
1073
|
-
* To enable this feature, first set `experimental.hybridOutput` to `true` in your Astro config, and set `output` to `hybrid`.
|
|
1074
|
-
*
|
|
1075
|
-
* ```js
|
|
1076
|
-
* {
|
|
1077
|
-
* output: 'hybrid',
|
|
1078
|
-
* experimental: {
|
|
1079
|
-
* hybridOutput: true,
|
|
1080
|
-
* },
|
|
1081
|
-
* }
|
|
1082
|
-
* ```
|
|
1083
|
-
* Then add `export const prerender = false` to any page or endpoint you want to opt-out of pre-rendering.
|
|
1084
|
-
* ```astro
|
|
1085
|
-
* ---
|
|
1086
|
-
* // pages/contact.astro
|
|
1087
|
-
* export const prerender = false
|
|
1088
|
-
*
|
|
1089
|
-
* if (Astro.request.method === 'POST') {
|
|
1090
|
-
* // handle form submission
|
|
1091
|
-
* }
|
|
1092
|
-
* ---
|
|
1093
|
-
* <form method="POST">
|
|
1094
|
-
* <input type="text" name="name" />
|
|
1095
|
-
* <input type="email" name="email" />
|
|
1096
|
-
* <button type="submit">Submit</button>
|
|
1097
|
-
* </form>
|
|
1098
|
-
* ```
|
|
1099
|
-
*/
|
|
1100
|
-
hybridOutput?: boolean;
|
|
1109
|
+
redirects?: boolean;
|
|
1101
1110
|
};
|
|
1102
1111
|
/** @deprecated - Use "integrations" instead. Run Astro to learn more about migrating. */
|
|
1103
1112
|
renderers?: never;
|
|
@@ -1420,6 +1429,7 @@ export interface AstroAdapter {
|
|
|
1420
1429
|
args?: any;
|
|
1421
1430
|
}
|
|
1422
1431
|
type Body = string;
|
|
1432
|
+
export type ValidRedirectStatus = 300 | 301 | 302 | 303 | 304 | 307 | 308;
|
|
1423
1433
|
interface AstroSharedContext<Props extends Record<string, any> = Record<string, any>> {
|
|
1424
1434
|
/**
|
|
1425
1435
|
* The address (usually IP address) of the user. Used with SSR only.
|
|
@@ -1448,7 +1458,7 @@ interface AstroSharedContext<Props extends Record<string, any> = Record<string,
|
|
|
1448
1458
|
/**
|
|
1449
1459
|
* Redirect to another page (**SSR Only**).
|
|
1450
1460
|
*/
|
|
1451
|
-
redirect(path: string, status?:
|
|
1461
|
+
redirect(path: string, status?: ValidRedirectStatus): Response;
|
|
1452
1462
|
/**
|
|
1453
1463
|
* Object accessed via Astro middleware
|
|
1454
1464
|
*/
|
|
@@ -1631,7 +1641,7 @@ export interface AstroIntegration {
|
|
|
1631
1641
|
};
|
|
1632
1642
|
}
|
|
1633
1643
|
export type MiddlewareNext<R> = () => Promise<R>;
|
|
1634
|
-
export type MiddlewareHandler<R> = (context: APIContext, next: MiddlewareNext<R>) => Promise<R> | Promise<void> | void;
|
|
1644
|
+
export type MiddlewareHandler<R> = (context: APIContext, next: MiddlewareNext<R>) => Promise<R> | R | Promise<void> | void;
|
|
1635
1645
|
export type MiddlewareResponseHandler = MiddlewareHandler<Response>;
|
|
1636
1646
|
export type MiddlewareEndpointHandler = MiddlewareHandler<Response | EndpointOutput>;
|
|
1637
1647
|
export type MiddlewareNextResponse = MiddlewareNext<Response>;
|
|
@@ -1642,12 +1652,16 @@ export interface AstroPluginOptions {
|
|
|
1642
1652
|
settings: AstroSettings;
|
|
1643
1653
|
logging: LogOptions;
|
|
1644
1654
|
}
|
|
1645
|
-
export type RouteType = 'page' | 'endpoint';
|
|
1655
|
+
export type RouteType = 'page' | 'endpoint' | 'redirect';
|
|
1646
1656
|
export interface RoutePart {
|
|
1647
1657
|
content: string;
|
|
1648
1658
|
dynamic: boolean;
|
|
1649
1659
|
spread: boolean;
|
|
1650
1660
|
}
|
|
1661
|
+
type RedirectConfig = string | {
|
|
1662
|
+
status: ValidRedirectStatus;
|
|
1663
|
+
destination: string;
|
|
1664
|
+
};
|
|
1651
1665
|
export interface RouteData {
|
|
1652
1666
|
route: string;
|
|
1653
1667
|
component: string;
|
|
@@ -1659,7 +1673,12 @@ export interface RouteData {
|
|
|
1659
1673
|
segments: RoutePart[][];
|
|
1660
1674
|
type: RouteType;
|
|
1661
1675
|
prerender: boolean;
|
|
1676
|
+
redirect?: RedirectConfig;
|
|
1677
|
+
redirectRoute?: RouteData;
|
|
1662
1678
|
}
|
|
1679
|
+
export type RedirectRouteData = RouteData & {
|
|
1680
|
+
redirect: string;
|
|
1681
|
+
};
|
|
1663
1682
|
export type SerializedRouteData = Omit<RouteData, 'generate' | 'pattern'> & {
|
|
1664
1683
|
generate: undefined;
|
|
1665
1684
|
pattern: string;
|
package/dist/assets/generate.js
CHANGED
|
@@ -2,7 +2,7 @@ import fs from "node:fs";
|
|
|
2
2
|
import { basename, join } from "node:path/posix";
|
|
3
3
|
import { warn } from "../core/logger/core.js";
|
|
4
4
|
import { prependForwardSlash } from "../core/path.js";
|
|
5
|
-
import {
|
|
5
|
+
import { isServerLikeOutput } from "../prerender/utils.js";
|
|
6
6
|
import { getConfiguredImageService, isESMImportedImage } from "./internal.js";
|
|
7
7
|
async function generateImage(buildOpts, options, filepath) {
|
|
8
8
|
if (!isESMImportedImage(options.src)) {
|
|
@@ -21,7 +21,7 @@ async function generateImage(buildOpts, options, filepath) {
|
|
|
21
21
|
useCache = false;
|
|
22
22
|
}
|
|
23
23
|
let serverRoot, clientRoot;
|
|
24
|
-
if (
|
|
24
|
+
if (isServerLikeOutput(buildOpts.settings.config)) {
|
|
25
25
|
serverRoot = buildOpts.settings.config.build.server;
|
|
26
26
|
clientRoot = buildOpts.settings.config.build.client;
|
|
27
27
|
} else {
|
package/dist/core/app/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { consoleLogDestination } from "../logger/console.js";
|
|
|
5
5
|
import { error } from "../logger/core.js";
|
|
6
6
|
import { callMiddleware } from "../middleware/callMiddleware.js";
|
|
7
7
|
import { prependForwardSlash, removeTrailingForwardSlash } from "../path.js";
|
|
8
|
+
import { RedirectSinglePageBuiltModule } from "../redirects/index.js";
|
|
8
9
|
import {
|
|
9
10
|
createEnvironment,
|
|
10
11
|
createRenderContext,
|
|
@@ -113,19 +114,17 @@ class App {
|
|
|
113
114
|
if (routeData.route === "/404") {
|
|
114
115
|
defaultStatus = 404;
|
|
115
116
|
}
|
|
116
|
-
let
|
|
117
|
-
|
|
118
|
-
if (routeData.type === "page") {
|
|
117
|
+
let mod = await this.#getModuleForRoute(routeData);
|
|
118
|
+
if (routeData.type === "page" || routeData.type === "redirect") {
|
|
119
119
|
let response = await this.#renderPage(request, routeData, mod, defaultStatus);
|
|
120
120
|
if (response.status === 500 || response.status === 404) {
|
|
121
|
-
const
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
-
mod = await page.page();
|
|
121
|
+
const errorRouteData = matchRoute("/" + response.status, this.#manifestData);
|
|
122
|
+
if (errorRouteData && errorRouteData.route !== routeData.route) {
|
|
123
|
+
mod = await this.#getModuleForRoute(errorRouteData);
|
|
125
124
|
try {
|
|
126
125
|
let errorResponse = await this.#renderPage(
|
|
127
126
|
request,
|
|
128
|
-
|
|
127
|
+
errorRouteData,
|
|
129
128
|
mod,
|
|
130
129
|
response.status
|
|
131
130
|
);
|
|
@@ -144,7 +143,21 @@ class App {
|
|
|
144
143
|
setCookieHeaders(response) {
|
|
145
144
|
return getSetCookiesFromResponse(response);
|
|
146
145
|
}
|
|
147
|
-
async #
|
|
146
|
+
async #getModuleForRoute(route) {
|
|
147
|
+
if (route.type === "redirect") {
|
|
148
|
+
return RedirectSinglePageBuiltModule;
|
|
149
|
+
} else {
|
|
150
|
+
const importComponentInstance = this.#manifest.pageMap.get(route.component);
|
|
151
|
+
if (!importComponentInstance) {
|
|
152
|
+
throw new Error(
|
|
153
|
+
`Unexpectedly unable to find a component instance for route ${route.route}`
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
const built = await importComponentInstance();
|
|
157
|
+
return built;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
async #renderPage(request, routeData, page, status = 200) {
|
|
148
161
|
var _a;
|
|
149
162
|
const url = new URL(request.url);
|
|
150
163
|
const pathname = prependForwardSlash(this.removeBase(url.pathname));
|
|
@@ -165,6 +178,7 @@ class App {
|
|
|
165
178
|
}
|
|
166
179
|
}
|
|
167
180
|
try {
|
|
181
|
+
const mod = await page.page();
|
|
168
182
|
const renderContext = await createRenderContext({
|
|
169
183
|
request,
|
|
170
184
|
origin: url.origin,
|
|
@@ -185,7 +199,7 @@ class App {
|
|
|
185
199
|
site: this.#env.site,
|
|
186
200
|
adapterName: this.#env.adapterName
|
|
187
201
|
});
|
|
188
|
-
const onRequest = (_a =
|
|
202
|
+
const onRequest = (_a = page.middleware) == null ? void 0 : _a.onRequest;
|
|
189
203
|
let response;
|
|
190
204
|
if (onRequest) {
|
|
191
205
|
response = await callMiddleware(
|
|
@@ -214,9 +228,10 @@ class App {
|
|
|
214
228
|
});
|
|
215
229
|
}
|
|
216
230
|
}
|
|
217
|
-
async #callEndpoint(request, routeData,
|
|
231
|
+
async #callEndpoint(request, routeData, page, status = 200) {
|
|
218
232
|
const url = new URL(request.url);
|
|
219
233
|
const pathname = "/" + this.removeBase(url.pathname);
|
|
234
|
+
const mod = await page.page();
|
|
220
235
|
const handler = mod;
|
|
221
236
|
const ctx = await createRenderContext({
|
|
222
237
|
request,
|
|
@@ -227,13 +242,7 @@ class App {
|
|
|
227
242
|
env: this.#env,
|
|
228
243
|
mod: handler
|
|
229
244
|
});
|
|
230
|
-
const result = await callEndpoint(
|
|
231
|
-
handler,
|
|
232
|
-
this.#env,
|
|
233
|
-
ctx,
|
|
234
|
-
this.#logging,
|
|
235
|
-
this.#manifest.middleware
|
|
236
|
-
);
|
|
245
|
+
const result = await callEndpoint(handler, this.#env, ctx, this.#logging, page.middleware);
|
|
237
246
|
if (result.type === "response") {
|
|
238
247
|
if (result.response.headers.get("X-Astro-Response") === "Not-Found") {
|
|
239
248
|
const fourOhFourRequest = new Request(new URL("/404", request.url));
|
package/dist/core/app/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { MarkdownRenderingOptions } from '@astrojs/markdown-remark';
|
|
2
|
-
import type {
|
|
2
|
+
import type { RouteData, SerializedRouteData, SSRComponentMetadata, SSRLoadedRenderer, SSRResult } from '../../@types/astro';
|
|
3
3
|
import type { SinglePageBuiltModule } from '../build/types';
|
|
4
4
|
export type ComponentPath = string;
|
|
5
5
|
export type StylesheetAsset = {
|
|
@@ -42,7 +42,6 @@ export interface SSRManifest {
|
|
|
42
42
|
entryModules: Record<string, string>;
|
|
43
43
|
assets: Set<string>;
|
|
44
44
|
componentMetadata: SSRResult['componentMetadata'];
|
|
45
|
-
middleware?: AstroMiddlewareInstance<unknown>;
|
|
46
45
|
}
|
|
47
46
|
export type SerializedSSRManifest = Omit<SSRManifest, 'routes' | 'assets' | 'componentMetadata' | 'clientDirectives'> & {
|
|
48
47
|
routes: SerializedRouteInfo[];
|
|
@@ -16,6 +16,7 @@ function getOutFolder(astroConfig, pathname, routeType) {
|
|
|
16
16
|
case "endpoint":
|
|
17
17
|
return new URL("." + appendForwardSlash(npath.dirname(pathname)), outRoot);
|
|
18
18
|
case "page":
|
|
19
|
+
case "redirect":
|
|
19
20
|
switch (astroConfig.build.format) {
|
|
20
21
|
case "directory": {
|
|
21
22
|
if (STATUS_CODE_PAGES.has(pathname)) {
|
|
@@ -35,6 +36,7 @@ function getOutFile(astroConfig, outFolder, pathname, routeType) {
|
|
|
35
36
|
case "endpoint":
|
|
36
37
|
return new URL(npath.basename(pathname), outFolder);
|
|
37
38
|
case "page":
|
|
39
|
+
case "redirect":
|
|
38
40
|
switch (astroConfig.build.format) {
|
|
39
41
|
case "directory": {
|
|
40
42
|
if (STATUS_CODE_PAGES.has(pathname)) {
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
} from "../../assets/generate.js";
|
|
9
9
|
import {
|
|
10
10
|
eachPageDataFromEntryPoint,
|
|
11
|
+
eachRedirectPageData,
|
|
11
12
|
hasPrerenderedPages
|
|
12
13
|
} from "../../core/build/internal.js";
|
|
13
14
|
import {
|
|
@@ -16,12 +17,17 @@ import {
|
|
|
16
17
|
removeTrailingForwardSlash
|
|
17
18
|
} from "../../core/path.js";
|
|
18
19
|
import { runHookBuildGenerated } from "../../integrations/index.js";
|
|
19
|
-
import {
|
|
20
|
+
import { isServerLikeOutput } from "../../prerender/utils.js";
|
|
20
21
|
import { BEFORE_HYDRATION_SCRIPT_ID, PAGE_SCRIPT_ID } from "../../vite-plugin-scripts/index.js";
|
|
21
22
|
import { callEndpoint, createAPIContext, throwIfRedirectNotAllowed } from "../endpoint/index.js";
|
|
22
23
|
import { AstroError } from "../errors/index.js";
|
|
23
24
|
import { debug, info } from "../logger/core.js";
|
|
24
25
|
import { callMiddleware } from "../middleware/callMiddleware.js";
|
|
26
|
+
import {
|
|
27
|
+
getRedirectLocationOrThrow,
|
|
28
|
+
RedirectSinglePageBuiltModule,
|
|
29
|
+
routeIsRedirect
|
|
30
|
+
} from "../redirects/index.js";
|
|
25
31
|
import { createEnvironment, createRenderContext, renderPage } from "../render/index.js";
|
|
26
32
|
import { callGetStaticPaths } from "../render/route-cache.js";
|
|
27
33
|
import {
|
|
@@ -33,8 +39,30 @@ import { createRequest } from "../request.js";
|
|
|
33
39
|
import { matchRoute } from "../routing/match.js";
|
|
34
40
|
import { getOutputFilename } from "../util.js";
|
|
35
41
|
import { getOutDirWithinCwd, getOutFile, getOutFolder } from "./common.js";
|
|
36
|
-
import {
|
|
42
|
+
import {
|
|
43
|
+
cssOrder,
|
|
44
|
+
getEntryFilePathFromComponentPath,
|
|
45
|
+
getPageDataByComponent,
|
|
46
|
+
mergeInlineCss
|
|
47
|
+
} from "./internal.js";
|
|
37
48
|
import { getTimeStat } from "./util.js";
|
|
49
|
+
function createEntryURL(filePath, outFolder) {
|
|
50
|
+
return new URL("./" + filePath + `?time=${Date.now()}`, outFolder);
|
|
51
|
+
}
|
|
52
|
+
async function getEntryForRedirectRoute(route, internals, outFolder) {
|
|
53
|
+
if (route.type !== "redirect") {
|
|
54
|
+
throw new Error(`Expected a redirect route.`);
|
|
55
|
+
}
|
|
56
|
+
if (route.redirectRoute) {
|
|
57
|
+
const filePath = getEntryFilePathFromComponentPath(internals, route.redirectRoute.component);
|
|
58
|
+
if (filePath) {
|
|
59
|
+
const url = createEntryURL(filePath, outFolder);
|
|
60
|
+
const ssrEntryPage = await import(url.toString());
|
|
61
|
+
return ssrEntryPage;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return RedirectSinglePageBuiltModule;
|
|
65
|
+
}
|
|
38
66
|
function shouldSkipDraft(pageModule, settings) {
|
|
39
67
|
var _a;
|
|
40
68
|
return (
|
|
@@ -61,7 +89,7 @@ function chunkIsPage(settings, output, internals) {
|
|
|
61
89
|
}
|
|
62
90
|
async function generatePages(opts, internals) {
|
|
63
91
|
const timer = performance.now();
|
|
64
|
-
const ssr =
|
|
92
|
+
const ssr = isServerLikeOutput(opts.settings.config);
|
|
65
93
|
const serverEntry = opts.buildConfig.serverEntry;
|
|
66
94
|
const outFolder = ssr ? opts.buildConfig.server : getOutDirWithinCwd(opts.settings.config.outDir);
|
|
67
95
|
if (ssr && !hasPrerenderedPages(internals))
|
|
@@ -73,17 +101,25 @@ ${bgGreen(black(` ${verb} static routes `))}`);
|
|
|
73
101
|
if (ssr) {
|
|
74
102
|
for (const [pageData, filePath] of eachPageDataFromEntryPoint(internals)) {
|
|
75
103
|
if (pageData.route.prerender) {
|
|
76
|
-
const ssrEntryURLPage =
|
|
104
|
+
const ssrEntryURLPage = createEntryURL(filePath, outFolder);
|
|
77
105
|
const ssrEntryPage = await import(ssrEntryURLPage.toString());
|
|
78
106
|
await generatePage(opts, internals, pageData, ssrEntryPage, builtPaths);
|
|
79
107
|
}
|
|
80
108
|
}
|
|
109
|
+
for (const pageData of eachRedirectPageData(internals)) {
|
|
110
|
+
const entry = await getEntryForRedirectRoute(pageData.route, internals, outFolder);
|
|
111
|
+
await generatePage(opts, internals, pageData, entry, builtPaths);
|
|
112
|
+
}
|
|
81
113
|
} else {
|
|
82
114
|
for (const [pageData, filePath] of eachPageDataFromEntryPoint(internals)) {
|
|
83
|
-
const ssrEntryURLPage =
|
|
115
|
+
const ssrEntryURLPage = createEntryURL(filePath, outFolder);
|
|
84
116
|
const ssrEntryPage = await import(ssrEntryURLPage.toString());
|
|
85
117
|
await generatePage(opts, internals, pageData, ssrEntryPage, builtPaths);
|
|
86
118
|
}
|
|
119
|
+
for (const pageData of eachRedirectPageData(internals)) {
|
|
120
|
+
const entry = await getEntryForRedirectRoute(pageData.route, internals, outFolder);
|
|
121
|
+
await generatePage(opts, internals, pageData, entry, builtPaths);
|
|
122
|
+
}
|
|
87
123
|
}
|
|
88
124
|
if (opts.settings.config.experimental.assets) {
|
|
89
125
|
info(opts.logging, null, `
|
|
@@ -114,6 +150,9 @@ async function generateImage(opts, transform, path) {
|
|
|
114
150
|
info(opts.logging, null, ` ${green("\u25B6")} ${path} ${dim(statsText)} ${dim(timeIncrease)}`);
|
|
115
151
|
}
|
|
116
152
|
async function generatePage(opts, internals, pageData, ssrEntry, builtPaths) {
|
|
153
|
+
if (routeIsRedirect(pageData.route) && !opts.settings.config.experimental.redirects) {
|
|
154
|
+
throw new Error(`To use redirects first set experimental.redirects to \`true\``);
|
|
155
|
+
}
|
|
117
156
|
let timeStart = performance.now();
|
|
118
157
|
const renderers = ssrEntry.renderers;
|
|
119
158
|
const pageInfo = getPageDataByComponent(internals, pageData.route.component);
|
|
@@ -167,7 +206,7 @@ async function getPathsForRoute(pageData, mod, opts, builtPaths) {
|
|
|
167
206
|
route: pageData.route,
|
|
168
207
|
isValidate: false,
|
|
169
208
|
logging: opts.logging,
|
|
170
|
-
ssr:
|
|
209
|
+
ssr: isServerLikeOutput(opts.settings.config)
|
|
171
210
|
}).then((_result) => {
|
|
172
211
|
const label = _result.staticPaths.length === 1 ? "page" : "pages";
|
|
173
212
|
debug(
|
|
@@ -281,7 +320,7 @@ async function generatePath(pathname, opts, gopts, middleware) {
|
|
|
281
320
|
});
|
|
282
321
|
}
|
|
283
322
|
}
|
|
284
|
-
const ssr =
|
|
323
|
+
const ssr = isServerLikeOutput(settings.config);
|
|
285
324
|
const url = getUrlForPath(
|
|
286
325
|
pathname,
|
|
287
326
|
opts.settings.config.base,
|
|
@@ -365,11 +404,23 @@ async function generatePath(pathname, opts, gopts, middleware) {
|
|
|
365
404
|
onRequest,
|
|
366
405
|
apiContext,
|
|
367
406
|
() => {
|
|
368
|
-
return renderPage({
|
|
407
|
+
return renderPage({
|
|
408
|
+
mod,
|
|
409
|
+
renderContext,
|
|
410
|
+
env,
|
|
411
|
+
apiContext,
|
|
412
|
+
isCompressHTML: settings.config.compressHTML
|
|
413
|
+
});
|
|
369
414
|
}
|
|
370
415
|
);
|
|
371
416
|
} else {
|
|
372
|
-
response = await renderPage({
|
|
417
|
+
response = await renderPage({
|
|
418
|
+
mod,
|
|
419
|
+
renderContext,
|
|
420
|
+
env,
|
|
421
|
+
apiContext,
|
|
422
|
+
isCompressHTML: settings.config.compressHTML
|
|
423
|
+
});
|
|
373
424
|
}
|
|
374
425
|
} catch (err) {
|
|
375
426
|
if (!AstroError.is(err) && !err.id && typeof err === "object") {
|
|
@@ -377,10 +428,22 @@ async function generatePath(pathname, opts, gopts, middleware) {
|
|
|
377
428
|
}
|
|
378
429
|
throw err;
|
|
379
430
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
431
|
+
if (response.status >= 300 && response.status < 400) {
|
|
432
|
+
if (!opts.settings.config.build.redirects) {
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
const location = getRedirectLocationOrThrow(response.headers);
|
|
436
|
+
body = `<!doctype html>
|
|
437
|
+
<title>Redirecting to: ${location}</title>
|
|
438
|
+
<meta http-equiv="refresh" content="0;url=${location}" />`;
|
|
439
|
+
if (pageData.route.type !== "redirect") {
|
|
440
|
+
pageData.route.redirect = location;
|
|
441
|
+
}
|
|
442
|
+
} else {
|
|
443
|
+
if (!response.body)
|
|
444
|
+
return;
|
|
445
|
+
body = await response.text();
|
|
446
|
+
}
|
|
384
447
|
}
|
|
385
448
|
const outFolder = getOutFolder(settings.config, pathname, pageData.route.type);
|
|
386
449
|
const outFile = getOutFile(settings.config, outFolder, pathname, pageData.route.type);
|
|
@@ -79,6 +79,7 @@ export declare function getPageDataByComponent(internals: BuildInternals, compon
|
|
|
79
79
|
export declare function getPageDataByViteID(internals: BuildInternals, viteid: ViteID): PageBuildData | undefined;
|
|
80
80
|
export declare function hasPageDataByViteID(internals: BuildInternals, viteid: ViteID): boolean;
|
|
81
81
|
export declare function eachPageData(internals: BuildInternals): Generator<PageBuildData, void, undefined>;
|
|
82
|
+
export declare function eachRedirectPageData(internals: BuildInternals): Generator<PageBuildData, void, unknown>;
|
|
82
83
|
export declare function eachPageDataFromEntryPoint(internals: BuildInternals): Generator<[PageBuildData, string]>;
|
|
83
84
|
export declare function hasPrerenderedPages(internals: BuildInternals): boolean;
|
|
84
85
|
interface OrderInfo {
|
|
@@ -95,4 +96,5 @@ export declare function cssOrder(a: OrderInfo, b: OrderInfo): 1 | -1;
|
|
|
95
96
|
export declare function mergeInlineCss(acc: Array<StylesheetAsset>, current: StylesheetAsset): Array<StylesheetAsset>;
|
|
96
97
|
export declare function isHoistedScript(internals: BuildInternals, id: string): boolean;
|
|
97
98
|
export declare function getPageDatasByHoistedScriptId(internals: BuildInternals, id: string): Generator<PageBuildData, void, unknown>;
|
|
99
|
+
export declare function getEntryFilePathFromComponentPath(internals: BuildInternals, path: string): string | undefined;
|
|
98
100
|
export {};
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { prependForwardSlash, removeFileExtension } from "../path.js";
|
|
2
2
|
import { viteID } from "../util.js";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
ASTRO_PAGE_EXTENSION_POST_PATTERN,
|
|
5
|
+
ASTRO_PAGE_MODULE_ID,
|
|
6
|
+
getVirtualModulePageIdFromPath
|
|
7
|
+
} from "./plugins/plugin-pages.js";
|
|
4
8
|
function createBuildInternals() {
|
|
5
9
|
const hoistedScriptIdToHoistedMap = /* @__PURE__ */ new Map();
|
|
6
10
|
const hoistedScriptIdToPagesMap = /* @__PURE__ */ new Map();
|
|
@@ -83,6 +87,13 @@ function hasPageDataByViteID(internals, viteid) {
|
|
|
83
87
|
function* eachPageData(internals) {
|
|
84
88
|
yield* internals.pagesByComponent.values();
|
|
85
89
|
}
|
|
90
|
+
function* eachRedirectPageData(internals) {
|
|
91
|
+
for (const pageData of eachPageData(internals)) {
|
|
92
|
+
if (pageData.route.type === "redirect") {
|
|
93
|
+
yield pageData;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
86
97
|
function* eachPageDataFromEntryPoint(internals) {
|
|
87
98
|
for (const [entryPoint, filePath] of internals.entrySpecifierToBundleMap) {
|
|
88
99
|
if (entryPoint.includes(ASTRO_PAGE_MODULE_ID)) {
|
|
@@ -153,11 +164,17 @@ function* getPageDatasByHoistedScriptId(internals, id) {
|
|
|
153
164
|
}
|
|
154
165
|
}
|
|
155
166
|
}
|
|
167
|
+
function getEntryFilePathFromComponentPath(internals, path) {
|
|
168
|
+
const id = getVirtualModulePageIdFromPath(path);
|
|
169
|
+
return internals.entrySpecifierToBundleMap.get(id);
|
|
170
|
+
}
|
|
156
171
|
export {
|
|
157
172
|
createBuildInternals,
|
|
158
173
|
cssOrder,
|
|
159
174
|
eachPageData,
|
|
160
175
|
eachPageDataFromEntryPoint,
|
|
176
|
+
eachRedirectPageData,
|
|
177
|
+
getEntryFilePathFromComponentPath,
|
|
161
178
|
getPageDataByComponent,
|
|
162
179
|
getPageDataByViteID,
|
|
163
180
|
getPageDatasByChunk,
|
|
@@ -146,7 +146,7 @@ function rollupPluginAstroBuildCSS(options) {
|
|
|
146
146
|
enforce: "post",
|
|
147
147
|
async generateBundle(_outputOptions, bundle) {
|
|
148
148
|
var _a;
|
|
149
|
-
const inlineConfig = settings.config.
|
|
149
|
+
const inlineConfig = settings.config.build.inlineStylesheets;
|
|
150
150
|
const { assetsInlineLimit = 4096 } = ((_a = settings.config.vite) == null ? void 0 : _a.build) ?? {};
|
|
151
151
|
Object.entries(bundle).forEach(([id, stylesheet]) => {
|
|
152
152
|
var _a2;
|