astro 4.2.8 → 4.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/astro-jsx.d.ts +1 -0
  2. package/astro.js +1 -1
  3. package/dist/@types/astro.d.ts +93 -10
  4. package/dist/content/types-generator.js +2 -1
  5. package/dist/core/app/index.js +55 -2
  6. package/dist/core/app/node.d.ts +1 -1
  7. package/dist/core/app/node.js +18 -18
  8. package/dist/core/app/types.d.ts +2 -1
  9. package/dist/core/build/buildPipeline.js +3 -1
  10. package/dist/core/build/common.d.ts +3 -3
  11. package/dist/core/build/common.js +20 -2
  12. package/dist/core/build/generate.js +12 -3
  13. package/dist/core/build/plugins/plugin-manifest.js +12 -3
  14. package/dist/core/build/plugins/plugin-ssr.js +8 -1
  15. package/dist/core/build/util.js +1 -0
  16. package/dist/core/config/schema.d.ts +305 -36
  17. package/dist/core/config/schema.js +95 -18
  18. package/dist/core/constants.js +1 -1
  19. package/dist/core/dev/dev.js +1 -1
  20. package/dist/core/dev/restart.js +11 -3
  21. package/dist/core/errors/errors-data.d.ts +20 -10
  22. package/dist/core/errors/errors-data.js +8 -2
  23. package/dist/core/errors/errors.js +1 -1
  24. package/dist/core/logger/vite.js +7 -1
  25. package/dist/core/messages.d.ts +5 -0
  26. package/dist/core/messages.js +6 -2
  27. package/dist/core/render/context.js +1 -1
  28. package/dist/core/routing/manifest/serialization.js +2 -1
  29. package/dist/core/routing/match.js +1 -1
  30. package/dist/core/sync/index.js +12 -1
  31. package/dist/i18n/index.d.ts +9 -7
  32. package/dist/i18n/index.js +61 -12
  33. package/dist/i18n/middleware.js +82 -24
  34. package/dist/i18n/vite-plugin-i18n.d.ts +1 -0
  35. package/dist/i18n/vite-plugin-i18n.js +9 -2
  36. package/dist/integrations/astroFeaturesValidation.d.ts +2 -2
  37. package/dist/integrations/astroFeaturesValidation.js +20 -2
  38. package/dist/integrations/index.d.ts +0 -1
  39. package/dist/integrations/index.js +2 -8
  40. package/dist/virtual-modules/i18n.js +12 -3
  41. package/dist/vite-plugin-astro-server/base.js +7 -2
  42. package/dist/vite-plugin-astro-server/plugin.js +2 -1
  43. package/dist/vite-plugin-astro-server/route.js +3 -2
  44. package/dist/vite-plugin-markdown/images.js +5 -2
  45. package/package.json +3 -3
  46. package/types.d.ts +4 -1
package/astro-jsx.d.ts CHANGED
@@ -670,6 +670,7 @@ declare namespace astroHTML.JSX {
670
670
 
671
671
  interface DetailsHTMLAttributes extends HTMLAttributes {
672
672
  open?: boolean | string | undefined | null;
673
+ name?: string | undefined | null;
673
674
  }
674
675
 
675
676
  interface DelHTMLAttributes extends HTMLAttributes {
package/astro.js CHANGED
@@ -20,8 +20,8 @@ async function main() {
20
20
  const version = process.versions.node;
21
21
  // Fast-path for higher Node.js versions
22
22
  if ((parseInt(version) || 0) <= skipSemverCheckIfAbove) {
23
+ const semver = await import('semver');
23
24
  try {
24
- const semver = await import('semver');
25
25
  if (!semver.satisfies(version, engines)) {
26
26
  await errorNodeUnsupported();
27
27
  return;
@@ -137,7 +137,7 @@ export interface AstroGlobal<Props extends Record<string, any> = Record<string,
137
137
  * const { name } = Astro.props
138
138
  * ```
139
139
  *
140
- * [Astro reference](https://docs.astro.build/en/core-concepts/astro-components/#component-props)
140
+ * [Astro reference](https://docs.astro.build/en/basics/astro-components/#component-props)
141
141
  */
142
142
  props: AstroSharedContext<Props, Params>['props'];
143
143
  /** Information about the current request. This is a standard [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object
@@ -685,12 +685,13 @@ export interface AstroUserConfig {
685
685
  /**
686
686
  * @docs
687
687
  * @name build.format
688
- * @typeraw {('file' | 'directory')}
688
+ * @typeraw {('file' | 'directory' | 'preserve')}
689
689
  * @default `'directory'`
690
690
  * @description
691
691
  * Control the output file format of each page. This value may be set by an adapter for you.
692
- * - If `'file'`, Astro will generate an HTML file (ex: "/foo.html") for each page.
693
- * - If `'directory'`, Astro will generate a directory with a nested `index.html` file (ex: "/foo/index.html") for each page.
692
+ * - `'file'`: Astro will generate an HTML file named for each page route. (e.g. `src/pages/about.astro` and `src/pages/about/index.astro` both build the file `/about.html`)
693
+ * - `'directory'`: Astro will generate a directory with a nested `index.html` file for each page. (e.g. `src/pages/about.astro` and `src/pages/about/index.astro` both build the file `/about/index.html`)
694
+ * - `'preserve'`: Astro will generate HTML files exactly as they appear in your source folder. (e.g. `src/pages/about.astro` builds `/about.html` and `src/pages/about/index.astro` builds the file `/about/index.html`)
694
695
  *
695
696
  * ```js
696
697
  * {
@@ -714,7 +715,7 @@ export interface AstroUserConfig {
714
715
  * - `directory` - Set `trailingSlash: 'always'`
715
716
  * - `file` - Set `trailingSlash: 'never'`
716
717
  */
717
- format?: 'file' | 'directory';
718
+ format?: 'file' | 'directory' | 'preserve';
718
719
  /**
719
720
  * @docs
720
721
  * @name build.client
@@ -1412,6 +1413,45 @@ export interface AstroUserConfig {
1412
1413
  * - `"pathanme": The strategy is applied to the pathname of the URLs
1413
1414
  */
1414
1415
  strategy: 'pathname';
1416
+ /**
1417
+ * @name i18n.domains
1418
+ * @type {Record<string, string> }
1419
+ * @default '{}'
1420
+ * @version 4.3.0
1421
+ * @description
1422
+ *
1423
+ * Configures the URL pattern of one or more supported languages to use a custom domain (or sub-domain).
1424
+ *
1425
+ * When a locale is mapped to a domain, a `/[locale]/` path prefix will not be used.
1426
+ * However, localized folders within `src/pages/` are still required, including for your configured `defaultLocale`.
1427
+ *
1428
+ * Any other locale not configured will default to a localized path-based URL according to your `prefixDefaultLocale` strategy (e.g. `https://example.com/[locale]/blog`).
1429
+ *
1430
+ * ```js
1431
+ * //astro.config.mjs
1432
+ * export default defineConfig({
1433
+ * site: "https://example.com",
1434
+ * output: "server", // required, with no prerendered pages
1435
+ * adapter: node({
1436
+ * mode: 'standalone',
1437
+ * }),
1438
+ * i18n: {
1439
+ * defaultLocale: "en",
1440
+ * locales: ["en", "fr", "pt-br", "es"],
1441
+ * prefixDefaultLocale: false,
1442
+ * domains: {
1443
+ * fr: "https://fr.example.com",
1444
+ * es: "https://example.es"
1445
+ * }
1446
+ * },
1447
+ * })
1448
+ * ```
1449
+ *
1450
+ * Both page routes built and URLs returned by the `astro:i18n` helper functions [`getAbsoluteLocaleUrl()`](https://docs.astro.build/en/guides/internationalization/#getabsolutelocaleurl) and [`getAbsoluteLocaleUrlList()`](https://docs.astro.build/en/guides/internationalization/#getabsolutelocaleurllist) will use the options set in `i18n.domains`.
1451
+ *
1452
+ * See the [Internationalization Guide](https://docs.astro.build/en/guides/internationalization/#domains) for more details, including the limitations of this feature.
1453
+ */
1454
+ domains?: Record<string, string>;
1415
1455
  };
1416
1456
  };
1417
1457
  /** ⚠️ WARNING: SUBJECT TO CHANGE */
@@ -1517,7 +1557,7 @@ export interface AstroUserConfig {
1517
1557
  * @version 4.2.0
1518
1558
  * @description
1519
1559
  *
1520
- * Prioritizes redirects and injected routes equally alongside file-based project routes, following the same [route priority order rules](https://docs.astro.build/en/core-concepts/routing/#route-priority-order) for all routes.
1560
+ * Prioritizes redirects and injected routes equally alongside file-based project routes, following the same [route priority order rules](https://docs.astro.build/en/guides/routing/#route-priority-order) for all routes.
1521
1561
  *
1522
1562
  * This allows more control over routing in your project by not automatically prioritizing certain types of routes, and standardizes the route priority ordering for all routes.
1523
1563
  *
@@ -1538,6 +1578,48 @@ export interface AstroUserConfig {
1538
1578
  * In the event of route collisions, where two routes of equal route priority attempt to build the same URL, Astro will log a warning identifying the conflicting routes.
1539
1579
  */
1540
1580
  globalRoutePriority?: boolean;
1581
+ /**
1582
+ * @docs
1583
+ * @name experimental.i18nDomains
1584
+ * @type {boolean}
1585
+ * @default `false`
1586
+ * @version 4.3.0
1587
+ * @description
1588
+ *
1589
+ * Enables domain support for the [experimental `domains` routing strategy](https://docs.astro.build/en/guides/internationalization/#domains-experimental) which allows you to configure the URL pattern of one or more supported languages to use a custom domain (or sub-domain).
1590
+ *
1591
+ * When a locale is mapped to a domain, a `/[locale]/` path prefix will not be used. However, localized folders within `src/pages/` are still required, including for your configured `defaultLocale`.
1592
+ *
1593
+ * Any other locale not configured will default to a localized path-based URL according to your `prefixDefaultLocale` strategy (e.g. `https://example.com/[locale]/blog`).
1594
+ *
1595
+ * ```js
1596
+ * //astro.config.mjs
1597
+ * export default defineConfig({
1598
+ * site: "https://example.com",
1599
+ * output: "server", // required, with no prerendered pages
1600
+ * adapter: node({
1601
+ * mode: 'standalone',
1602
+ * }),
1603
+ * i18n: {
1604
+ * defaultLocale: "en",
1605
+ * locales: ["en", "fr", "pt-br", "es"],
1606
+ * prefixDefaultLocale: false,
1607
+ * domains: {
1608
+ * fr: "https://fr.example.com",
1609
+ * es: "https://example.es",
1610
+ * },
1611
+ * },
1612
+ * experimental: {
1613
+ * i18nDomains: true,
1614
+ * },
1615
+ * });
1616
+ * ```
1617
+ *
1618
+ * Both page routes built and URLs returned by the `astro:i18n` helper functions [`getAbsoluteLocaleUrl()`](https://docs.astro.build/en/guides/internationalization/#getabsolutelocaleurl) and [`getAbsoluteLocaleUrlList()`](https://docs.astro.build/en/guides/internationalization/#getabsolutelocaleurllist) will use the options set in `i18n.domains`.
1619
+ *
1620
+ * See the [Internationalization Guide](https://docs.astro.build/en/guides/internationalization/#domains-experimental) for more details, including the limitations of this experimental feature.
1621
+ */
1622
+ i18nDomains?: boolean;
1541
1623
  };
1542
1624
  }
1543
1625
  /**
@@ -1767,7 +1849,7 @@ export type GetStaticPathsResultKeyed = GetStaticPathsResult & {
1767
1849
  keyed: Map<string, GetStaticPathsItem>;
1768
1850
  };
1769
1851
  /**
1770
- * Return an array of pages to generate for a [dynamic route](https://docs.astro.build/en/core-concepts/routing/#dynamic-routes). (**SSG Only**)
1852
+ * Return an array of pages to generate for a [dynamic route](https://docs.astro.build/en/guides/routing/#dynamic-routes). (**SSG Only**)
1771
1853
  *
1772
1854
  * [Astro Reference](https://docs.astro.build/en/reference/api-reference/#getstaticpaths)
1773
1855
  */
@@ -1918,7 +2000,7 @@ export type AstroFeatureMap = {
1918
2000
  /**
1919
2001
  * List of features that orbit around the i18n routing
1920
2002
  */
1921
- i18n?: AstroInternationalizationFeature;
2003
+ i18nDomains?: SupportsKind;
1922
2004
  };
1923
2005
  export interface AstroAssetsFeature {
1924
2006
  supportKind?: SupportsKind;
@@ -1933,9 +2015,9 @@ export interface AstroAssetsFeature {
1933
2015
  }
1934
2016
  export interface AstroInternationalizationFeature {
1935
2017
  /**
1936
- * Whether the adapter is able to detect the language of the browser, usually using the `Accept-Language` header.
2018
+ * The adapter should be able to create the proper redirects
1937
2019
  */
1938
- detectBrowserLanguage?: SupportsKind;
2020
+ domains?: SupportsKind;
1939
2021
  }
1940
2022
  export type Locales = (string | {
1941
2023
  codes: string[];
@@ -2288,6 +2370,7 @@ export interface RouteData {
2288
2370
  redirect?: RedirectConfig;
2289
2371
  redirectRoute?: RouteData;
2290
2372
  fallbackRoutes: RouteData[];
2373
+ isIndex: boolean;
2291
2374
  }
2292
2375
  export type RedirectRouteData = RouteData & {
2293
2376
  redirect: string;
@@ -266,7 +266,8 @@ function invalidateVirtualMod(viteServer) {
266
266
  }
267
267
  function normalizeConfigPath(from, to) {
268
268
  const configPath = path.relative(from, to).replace(/\.ts$/, ".js");
269
- return `"${isRelativePath(configPath) ? "" : "./"}${configPath}"`;
269
+ const normalizedPath = configPath.replaceAll("\\", "/");
270
+ return `"${isRelativePath(configPath) ? "" : "./"}${normalizedPath}"`;
270
271
  }
271
272
  async function writeContentFiles({
272
273
  fs,
@@ -5,7 +5,9 @@ import { consoleLogDestination } from "../logger/console.js";
5
5
  import { AstroIntegrationLogger, Logger } from "../logger/core.js";
6
6
  import { sequence } from "../middleware/index.js";
7
7
  import {
8
+ appendForwardSlash,
8
9
  collapseDuplicateSlashes,
10
+ joinPaths,
9
11
  prependForwardSlash,
10
12
  removeTrailingForwardSlash
11
13
  } from "../path.js";
@@ -19,6 +21,7 @@ import {
19
21
  } from "../render/ssr-element.js";
20
22
  import { matchRoute } from "../routing/match.js";
21
23
  import { SSRRoutePipeline } from "./ssrPipeline.js";
24
+ import { normalizeTheLocale } from "../../i18n/index.js";
22
25
  import { deserializeManifest } from "./common.js";
23
26
  const localsSymbol = Symbol.for("astro.locals");
24
27
  const clientAddressSymbol = Symbol.for("astro.clientAddress");
@@ -108,12 +111,62 @@ class App {
108
111
  const url = new URL(request.url);
109
112
  if (this.#manifest.assets.has(url.pathname))
110
113
  return void 0;
111
- const pathname = prependForwardSlash(this.removeBase(url.pathname));
112
- const routeData = matchRoute(pathname, this.#manifestData);
114
+ let pathname = this.#computePathnameFromDomain(request);
115
+ if (!pathname) {
116
+ pathname = prependForwardSlash(this.removeBase(url.pathname));
117
+ }
118
+ let routeData = matchRoute(pathname, this.#manifestData);
113
119
  if (!routeData || routeData.prerender)
114
120
  return void 0;
115
121
  return routeData;
116
122
  }
123
+ #computePathnameFromDomain(request) {
124
+ let pathname = void 0;
125
+ const url = new URL(request.url);
126
+ if (this.#manifest.i18n && (this.#manifest.i18n.routing === "domains-prefix-always" || this.#manifest.i18n.routing === "domains-prefix-other-locales" || this.#manifest.i18n.routing === "domains-prefix-other-no-redirect")) {
127
+ let host = request.headers.get("X-Forwarded-Host");
128
+ let protocol = request.headers.get("X-Forwarded-Proto");
129
+ if (protocol) {
130
+ protocol = protocol + ":";
131
+ } else {
132
+ protocol = url.protocol;
133
+ }
134
+ if (!host) {
135
+ host = request.headers.get("Host");
136
+ }
137
+ if (host && protocol) {
138
+ host = host.split(":")[0];
139
+ try {
140
+ let locale;
141
+ const hostAsUrl = new URL(`${protocol}//${host}`);
142
+ for (const [domainKey, localeValue] of Object.entries(
143
+ this.#manifest.i18n.domainLookupTable
144
+ )) {
145
+ const domainKeyAsUrl = new URL(domainKey);
146
+ if (hostAsUrl.host === domainKeyAsUrl.host && hostAsUrl.protocol === domainKeyAsUrl.protocol) {
147
+ locale = localeValue;
148
+ break;
149
+ }
150
+ }
151
+ if (locale) {
152
+ pathname = prependForwardSlash(
153
+ joinPaths(normalizeTheLocale(locale), this.removeBase(url.pathname))
154
+ );
155
+ if (url.pathname.endsWith("/")) {
156
+ pathname = appendForwardSlash(pathname);
157
+ }
158
+ }
159
+ } catch (e) {
160
+ this.#logger.error(
161
+ "router",
162
+ `Astro tried to parse ${protocol}//${host} as an URL, but it threw a parsing error. Check the X-Forwarded-Host and X-Forwarded-Proto headers.`
163
+ );
164
+ this.#logger.error("router", `Error: ${e}`);
165
+ }
166
+ }
167
+ }
168
+ return pathname;
169
+ }
117
170
  async render(request, routeDataOrOptions, maybeLocals) {
118
171
  let routeData;
119
172
  let locals;
@@ -51,7 +51,7 @@ export declare class NodeApp extends App {
51
51
  * @param source WhatWG Response
52
52
  * @param destination NodeJS ServerResponse
53
53
  */
54
- static writeResponse(source: Response, destination: ServerResponse): Promise<void>;
54
+ static writeResponse(source: Response, destination: ServerResponse): Promise<ServerResponse<IncomingMessage> | undefined>;
55
55
  }
56
56
  export declare function loadManifest(rootFolder: URL): Promise<SSRManifest>;
57
57
  export declare function loadApp(rootFolder: URL): Promise<NodeApp>;
@@ -68,27 +68,27 @@ class NodeApp extends App {
68
68
  static async writeResponse(source, destination) {
69
69
  const { status, headers, body } = source;
70
70
  destination.writeHead(status, createOutgoingHttpHeaders(headers));
71
- if (body) {
72
- try {
73
- const reader = body.getReader();
74
- destination.on("close", () => {
75
- reader.cancel().catch((err) => {
76
- console.error(
77
- `There was an uncaught error in the middle of the stream while rendering ${destination.req.url}.`,
78
- err
79
- );
80
- });
71
+ if (!body)
72
+ return destination.end();
73
+ try {
74
+ const reader = body.getReader();
75
+ destination.on("close", () => {
76
+ reader.cancel().catch((err) => {
77
+ console.error(
78
+ `There was an uncaught error in the middle of the stream while rendering ${destination.req.url}.`,
79
+ err
80
+ );
81
81
  });
82
- let result = await reader.read();
83
- while (!result.done) {
84
- destination.write(result.value);
85
- result = await reader.read();
86
- }
87
- } catch {
88
- destination.write("Internal server error");
82
+ });
83
+ let result = await reader.read();
84
+ while (!result.done) {
85
+ destination.write(result.value);
86
+ result = await reader.read();
89
87
  }
88
+ destination.end();
89
+ } catch {
90
+ destination.end("Internal server error");
90
91
  }
91
- destination.end();
92
92
  }
93
93
  }
94
94
  function makeRequestHeaders(req) {
@@ -32,7 +32,7 @@ export type SSRManifest = {
32
32
  site?: string;
33
33
  base: string;
34
34
  trailingSlash: 'always' | 'never' | 'ignore';
35
- buildFormat: 'file' | 'directory';
35
+ buildFormat: 'file' | 'directory' | 'preserve';
36
36
  compressHTML: boolean;
37
37
  assetsPrefix?: string;
38
38
  renderers: SSRLoadedRenderer[];
@@ -53,6 +53,7 @@ export type SSRManifestI18n = {
53
53
  routing: RoutingStrategies;
54
54
  locales: Locales;
55
55
  defaultLocale: string;
56
+ domainLookupTable: Record<string, string>;
56
57
  };
57
58
  export type SerializedSSRManifest = Omit<SSRManifest, 'middleware' | 'routes' | 'assets' | 'componentMetadata' | 'clientDirectives'> & {
58
59
  routes: SerializedRouteInfo[];
@@ -98,6 +98,7 @@ class BuildPipeline extends Pipeline {
98
98
  }
99
99
  const renderersEntryUrl = new URL(`renderers.mjs?time=${Date.now()}`, baseDirectory);
100
100
  const renderers = await import(renderersEntryUrl.toString());
101
+ const middleware = await import(new URL("middleware.mjs", baseDirectory).toString()).then((mod) => mod.onRequest).catch(() => manifest.middleware);
101
102
  if (!renderers) {
102
103
  throw new Error(
103
104
  "Astro couldn't find the emitted renderers. This is an internal error, please file an issue."
@@ -105,7 +106,8 @@ class BuildPipeline extends Pipeline {
105
106
  }
106
107
  return {
107
108
  ...manifest,
108
- renderers: renderers.renderers
109
+ renderers: renderers.renderers,
110
+ middleware
109
111
  };
110
112
  }
111
113
  /**
@@ -1,6 +1,6 @@
1
- import type { AstroConfig, RouteType } from '../../@types/astro.js';
2
- export declare function getOutFolder(astroConfig: AstroConfig, pathname: string, routeType: RouteType): URL;
3
- export declare function getOutFile(astroConfig: AstroConfig, outFolder: URL, pathname: string, routeType: RouteType): URL;
1
+ import type { AstroConfig, RouteData } from '../../@types/astro.js';
2
+ export declare function getOutFolder(astroConfig: AstroConfig, pathname: string, routeData: RouteData): URL;
3
+ export declare function getOutFile(astroConfig: AstroConfig, outFolder: URL, pathname: string, routeData: RouteData): URL;
4
4
  /**
5
5
  * Ensures the `outDir` is within `process.cwd()`. If not it will fallback to `<cwd>/.astro`.
6
6
  * This is used for static `ssrBuild` so the output can access node_modules when we import
@@ -10,8 +10,9 @@ function getOutRoot(astroConfig) {
10
10
  return new URL("./", astroConfig.build.client);
11
11
  }
12
12
  }
13
- function getOutFolder(astroConfig, pathname, routeType) {
13
+ function getOutFolder(astroConfig, pathname, routeData) {
14
14
  const outRoot = getOutRoot(astroConfig);
15
+ const routeType = routeData.type;
15
16
  switch (routeType) {
16
17
  case "endpoint":
17
18
  return new URL("." + appendForwardSlash(npath.dirname(pathname)), outRoot);
@@ -29,10 +30,20 @@ function getOutFolder(astroConfig, pathname, routeType) {
29
30
  const d = pathname === "" ? pathname : npath.dirname(pathname);
30
31
  return new URL("." + appendForwardSlash(d), outRoot);
31
32
  }
33
+ case "preserve": {
34
+ let dir;
35
+ if (pathname === "" || routeData.isIndex) {
36
+ dir = pathname;
37
+ } else {
38
+ dir = npath.dirname(pathname);
39
+ }
40
+ return new URL("." + appendForwardSlash(dir), outRoot);
41
+ }
32
42
  }
33
43
  }
34
44
  }
35
- function getOutFile(astroConfig, outFolder, pathname, routeType) {
45
+ function getOutFile(astroConfig, outFolder, pathname, routeData) {
46
+ const routeType = routeData.type;
36
47
  switch (routeType) {
37
48
  case "endpoint":
38
49
  return new URL(npath.basename(pathname), outFolder);
@@ -51,6 +62,13 @@ function getOutFile(astroConfig, outFolder, pathname, routeType) {
51
62
  const baseName = npath.basename(pathname);
52
63
  return new URL("./" + (baseName || "index") + ".html", outFolder);
53
64
  }
65
+ case "preserve": {
66
+ let baseName = npath.basename(pathname);
67
+ if (!baseName || routeData.isIndex) {
68
+ baseName = "index";
69
+ }
70
+ return new URL(`./${baseName}.html`, outFolder);
71
+ }
54
72
  }
55
73
  }
56
74
  }
@@ -47,6 +47,7 @@ import {
47
47
  mergeInlineCss
48
48
  } from "./internal.js";
49
49
  import { getTimeStat, shouldAppendForwardSlash } from "./util.js";
50
+ import { NoPrerenderedRoutesWithDomains } from "../errors/errors-data.js";
50
51
  function createEntryURL(filePath, outFolder) {
51
52
  return new URL("./" + filePath + `?time=${Date.now()}`, outFolder);
52
53
  }
@@ -130,9 +131,16 @@ async function generatePages(opts, internals) {
130
131
  ${bgGreen(black(` ${verb} static routes `))}`);
131
132
  const builtPaths = /* @__PURE__ */ new Set();
132
133
  const pagesToGenerate = pipeline.retrieveRoutesToGenerate();
134
+ const config = pipeline.getConfig();
133
135
  if (ssr) {
134
136
  for (const [pageData, filePath] of pagesToGenerate) {
135
137
  if (pageData.route.prerender) {
138
+ if (config.experimental.i18nDomains) {
139
+ throw new AstroError({
140
+ ...NoPrerenderedRoutesWithDomains,
141
+ message: NoPrerenderedRoutesWithDomains.message(pageData.component)
142
+ });
143
+ }
136
144
  const ssrEntryURLPage = createEntryURL(filePath, outFolder);
137
145
  const ssrEntryPage = await import(ssrEntryURLPage.toString());
138
146
  if (opts.settings.adapter?.adapterFeatures?.functionPerRoute) {
@@ -451,8 +459,8 @@ async function generatePath(pathname, pipeline, gopts, route) {
451
459
  return;
452
460
  body = Buffer.from(await response.arrayBuffer());
453
461
  }
454
- const outFolder = getOutFolder(pipeline.getConfig(), pathname, route.type);
455
- const outFile = getOutFile(pipeline.getConfig(), outFolder, pathname, route.type);
462
+ const outFolder = getOutFolder(pipeline.getConfig(), pathname, route);
463
+ const outFile = getOutFile(pipeline.getConfig(), outFolder, pathname, route);
456
464
  route.distURL = outFile;
457
465
  await fs.promises.mkdir(outFolder, { recursive: true });
458
466
  await fs.promises.writeFile(outFile, body);
@@ -473,7 +481,8 @@ function createBuildManifest(settings, internals, renderers, middleware) {
473
481
  fallback: settings.config.i18n.fallback,
474
482
  routing: settings.config.i18n.routing,
475
483
  defaultLocale: settings.config.i18n.defaultLocale,
476
- locales: settings.config.i18n.locales
484
+ locales: settings.config.i18n.locales,
485
+ domainLookupTable: {}
477
486
  };
478
487
  }
479
488
  return {
@@ -8,6 +8,7 @@ import { serializeRouteData } from "../../routing/index.js";
8
8
  import { addRollupInput } from "../add-rollup-input.js";
9
9
  import { getOutFile, getOutFolder } from "../common.js";
10
10
  import { cssOrder, mergeInlineCss } from "../internal.js";
11
+ import { normalizeTheLocale } from "../../../i18n/index.js";
11
12
  const manifestReplace = "@@ASTRO_MANIFEST_REPLACE@@";
12
13
  const replaceExp = new RegExp(`['"](${manifestReplace})['"]`, "g");
13
14
  const SSR_MANIFEST_VIRTUAL_MODULE_ID = "@astrojs-manifest";
@@ -111,6 +112,7 @@ function injectManifest(manifest, chunk) {
111
112
  function buildManifest(opts, internals, staticFiles) {
112
113
  const { settings } = opts;
113
114
  const routes = [];
115
+ const domainLookupTable = {};
114
116
  const entryModules = Object.fromEntries(internals.entrySpecifierToBundleMap.entries());
115
117
  if (settings.scripts.some((script) => script.stage === "page")) {
116
118
  staticFiles.push(entryModules[PAGE_SCRIPT_ID]);
@@ -127,8 +129,8 @@ function buildManifest(opts, internals, staticFiles) {
127
129
  continue;
128
130
  if (!route.pathname)
129
131
  continue;
130
- const outFolder = getOutFolder(opts.settings.config, route.pathname, route.type);
131
- const outFile = getOutFile(opts.settings.config, outFolder, route.pathname, route.type);
132
+ const outFolder = getOutFolder(opts.settings.config, route.pathname, route);
133
+ const outFile = getOutFile(opts.settings.config, outFolder, route.pathname, route);
132
134
  const file = outFile.toString().replace(opts.settings.config.build.client.toString(), "");
133
135
  routes.push({
134
136
  file,
@@ -174,6 +176,12 @@ function buildManifest(opts, internals, staticFiles) {
174
176
  routeData: serializeRouteData(route, settings.config.trailingSlash)
175
177
  });
176
178
  }
179
+ const i18n = settings.config.i18n;
180
+ if (settings.config.experimental.i18nDomains && i18n && i18n.domains && (i18n.routing === "domains-prefix-always" || i18n.routing === "domains-prefix-other-locales" || i18n.routing === "domains-prefix-other-no-redirect")) {
181
+ for (const [locale, domainValue] of Object.entries(i18n.domains)) {
182
+ domainLookupTable[domainValue] = normalizeTheLocale(locale);
183
+ }
184
+ }
177
185
  if (!(BEFORE_HYDRATION_SCRIPT_ID in entryModules)) {
178
186
  entryModules[BEFORE_HYDRATION_SCRIPT_ID] = "";
179
187
  }
@@ -183,7 +191,8 @@ function buildManifest(opts, internals, staticFiles) {
183
191
  fallback: settings.config.i18n.fallback,
184
192
  routing: settings.config.i18n.routing,
185
193
  locales: settings.config.i18n.locales,
186
- defaultLocale: settings.config.i18n.defaultLocale
194
+ defaultLocale: settings.config.i18n.defaultLocale,
195
+ domainLookupTable
187
196
  };
188
197
  }
189
198
  return {
@@ -210,7 +210,14 @@ function generateSSRCode(adapter, middlewareId) {
210
210
  return `export const ${name} = _exports['${name}'];`;
211
211
  }
212
212
  }) ?? [],
213
- `serverEntrypointModule.start?.(_manifest, _args);`
213
+ // NOTE: This is intentionally obfuscated!
214
+ // Do NOT simplify this to something like `serverEntrypointModule.start?.(_manifest, _args)`
215
+ // They are NOT equivalent! Some bundlers will throw if `start` is not exported, but we
216
+ // only want to silently ignore it... hence the dynamic, obfuscated weirdness.
217
+ `const _start = 'start';
218
+ if (_start in serverEntrypointModule) {
219
+ serverEntrypointModule[_start](_manifest, _args);
220
+ }`
214
221
  ];
215
222
  return {
216
223
  imports,
@@ -12,6 +12,7 @@ function shouldAppendForwardSlash(trailingSlash, buildFormat) {
12
12
  switch (buildFormat) {
13
13
  case "directory":
14
14
  return true;
15
+ case "preserve":
15
16
  case "file":
16
17
  return false;
17
18
  }