astro 3.4.4 → 3.5.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 (143) hide show
  1. package/client.d.ts +86 -0
  2. package/components/Code.astro +15 -2
  3. package/components/Picture.astro +2 -1
  4. package/components/ViewTransitions.astro +39 -38
  5. package/content-module.template.mjs +4 -14
  6. package/dist/@types/astro.d.ts +227 -5
  7. package/dist/assets/build/generate.d.ts +2 -1
  8. package/dist/assets/build/generate.js +16 -5
  9. package/dist/assets/consts.d.ts +1 -0
  10. package/dist/assets/consts.js +2 -0
  11. package/dist/assets/endpoint/generic.js +6 -2
  12. package/dist/assets/internal.js +9 -2
  13. package/dist/assets/services/service.d.ts +8 -3
  14. package/dist/assets/services/service.js +2 -1
  15. package/dist/assets/services/vendor/squoosh/image-pool.d.ts +1 -2
  16. package/dist/assets/types.d.ts +9 -5
  17. package/dist/assets/utils/emitAsset.js +5 -0
  18. package/dist/assets/utils/metadata.d.ts +1 -2
  19. package/dist/assets/utils/proxy.d.ts +1 -0
  20. package/dist/assets/utils/proxy.js +16 -0
  21. package/dist/assets/utils/queryParams.d.ts +1 -1
  22. package/dist/assets/utils/transformToPath.d.ts +1 -1
  23. package/dist/assets/utils/transformToPath.js +8 -3
  24. package/dist/assets/vite-plugin-assets.js +33 -12
  25. package/dist/cli/build/index.js +1 -1
  26. package/dist/cli/info/index.js +7 -3
  27. package/dist/content/consts.d.ts +1 -0
  28. package/dist/content/consts.js +2 -0
  29. package/dist/content/runtime-assets.d.ts +9 -1
  30. package/dist/content/runtime-assets.js +1 -1
  31. package/dist/content/runtime.d.ts +1 -0
  32. package/dist/content/runtime.js +8 -2
  33. package/dist/content/utils.d.ts +1 -0
  34. package/dist/content/utils.js +9 -0
  35. package/dist/content/vite-plugin-content-assets.js +49 -23
  36. package/dist/content/vite-plugin-content-imports.js +9 -3
  37. package/dist/content/vite-plugin-content-virtual-mod.d.ts +17 -12
  38. package/dist/content/vite-plugin-content-virtual-mod.js +136 -57
  39. package/dist/core/app/index.js +19 -4
  40. package/dist/core/app/types.d.ts +7 -1
  41. package/dist/core/build/buildPipeline.js +17 -4
  42. package/dist/core/build/common.js +2 -0
  43. package/dist/core/build/generate.js +64 -34
  44. package/dist/core/build/index.d.ts +0 -8
  45. package/dist/core/build/index.js +9 -2
  46. package/dist/core/build/internal.d.ts +11 -1
  47. package/dist/core/build/internal.js +23 -1
  48. package/dist/core/build/page-data.js +46 -18
  49. package/dist/core/build/plugin.d.ts +12 -10
  50. package/dist/core/build/plugin.js +14 -22
  51. package/dist/core/build/plugins/index.js +4 -0
  52. package/dist/core/build/plugins/plugin-alias-resolve.js +1 -1
  53. package/dist/core/build/plugins/plugin-analyzer.js +1 -1
  54. package/dist/core/build/plugins/plugin-chunks.d.ts +4 -0
  55. package/dist/core/build/plugins/plugin-chunks.js +31 -0
  56. package/dist/core/build/plugins/plugin-component-entry.js +1 -1
  57. package/dist/core/build/plugins/plugin-content.d.ts +4 -0
  58. package/dist/core/build/plugins/plugin-content.js +273 -0
  59. package/dist/core/build/plugins/plugin-css.js +9 -4
  60. package/dist/core/build/plugins/plugin-hoisted-scripts.js +1 -1
  61. package/dist/core/build/plugins/plugin-internals.js +1 -1
  62. package/dist/core/build/plugins/plugin-manifest.js +14 -5
  63. package/dist/core/build/plugins/plugin-middleware.d.ts +1 -3
  64. package/dist/core/build/plugins/plugin-middleware.js +5 -57
  65. package/dist/core/build/plugins/plugin-pages.js +3 -3
  66. package/dist/core/build/plugins/plugin-prerender.js +2 -5
  67. package/dist/core/build/plugins/plugin-renderers.js +1 -1
  68. package/dist/core/build/plugins/plugin-ssr.js +6 -5
  69. package/dist/core/build/plugins/util.d.ts +3 -3
  70. package/dist/core/build/static-build.d.ts +2 -1
  71. package/dist/core/build/static-build.js +52 -28
  72. package/dist/core/build/types.d.ts +1 -1
  73. package/dist/core/build/util.d.ts +7 -0
  74. package/dist/core/build/util.js +37 -1
  75. package/dist/core/compile/compile.js +1 -0
  76. package/dist/core/config/config.js +3 -0
  77. package/dist/core/config/schema.d.ts +208 -0
  78. package/dist/core/config/schema.js +55 -2
  79. package/dist/core/config/settings.js +1 -0
  80. package/dist/core/constants.js +1 -1
  81. package/dist/core/create-vite.js +9 -3
  82. package/dist/core/dev/dev.js +1 -1
  83. package/dist/core/endpoint/index.d.ts +4 -3
  84. package/dist/core/endpoint/index.js +29 -3
  85. package/dist/core/errors/errors-data.d.ts +11 -0
  86. package/dist/core/errors/errors-data.js +17 -0
  87. package/dist/core/logger/node.js +1 -0
  88. package/dist/core/messages.js +2 -2
  89. package/dist/core/middleware/index.d.ts +7 -3
  90. package/dist/core/middleware/index.js +3 -2
  91. package/dist/core/middleware/loadMiddleware.d.ts +1 -2
  92. package/dist/core/middleware/loadMiddleware.js +3 -4
  93. package/dist/core/middleware/sequence.d.ts +2 -2
  94. package/dist/core/middleware/sequence.js +3 -2
  95. package/dist/core/middleware/vite-plugin.d.ts +9 -0
  96. package/dist/core/middleware/vite-plugin.js +101 -0
  97. package/dist/core/pipeline.d.ts +1 -1
  98. package/dist/core/pipeline.js +6 -4
  99. package/dist/core/redirects/helpers.d.ts +1 -0
  100. package/dist/core/redirects/helpers.js +4 -0
  101. package/dist/core/render/context.d.ts +24 -1
  102. package/dist/core/render/context.js +96 -2
  103. package/dist/core/render/core.d.ts +2 -14
  104. package/dist/core/render/core.js +12 -52
  105. package/dist/core/render/index.d.ts +2 -3
  106. package/dist/core/render/index.js +3 -4
  107. package/dist/core/render/params-and-props.d.ts +1 -1
  108. package/dist/core/render/params-and-props.js +5 -2
  109. package/dist/core/render/result.d.ts +1 -0
  110. package/dist/core/render/result.js +23 -0
  111. package/dist/core/render/route-cache.d.ts +1 -1
  112. package/dist/core/render/route-cache.js +6 -2
  113. package/dist/core/routing/manifest/create.js +117 -4
  114. package/dist/core/sync/index.d.ts +2 -24
  115. package/dist/i18n/index.d.ts +54 -0
  116. package/dist/i18n/index.js +91 -0
  117. package/dist/i18n/middleware.d.ts +2 -0
  118. package/dist/i18n/middleware.js +62 -0
  119. package/dist/i18n/vite-plugin-i18n.d.ts +7 -0
  120. package/dist/i18n/vite-plugin-i18n.js +62 -0
  121. package/dist/integrations/astroFeaturesValidation.js +4 -1
  122. package/dist/integrations/index.js +12 -0
  123. package/dist/prefetch/index.d.ts +31 -0
  124. package/dist/prefetch/index.js +176 -0
  125. package/dist/prefetch/vite-plugin-prefetch.d.ts +5 -0
  126. package/dist/prefetch/vite-plugin-prefetch.js +43 -0
  127. package/dist/runtime/client/dev-overlay/plugins/audit.js +17 -9
  128. package/dist/runtime/server/index.d.ts +0 -2
  129. package/dist/runtime/server/render/component.js +3 -5
  130. package/dist/transitions/router.d.ts +1 -0
  131. package/dist/transitions/router.js +9 -4
  132. package/dist/transitions/vite-plugin-transitions.d.ts +4 -1
  133. package/dist/transitions/vite-plugin-transitions.js +7 -1
  134. package/dist/vite-plugin-astro-server/devPipeline.d.ts +1 -0
  135. package/dist/vite-plugin-astro-server/devPipeline.js +2 -0
  136. package/dist/vite-plugin-astro-server/plugin.js +11 -1
  137. package/dist/vite-plugin-astro-server/route.js +113 -51
  138. package/dist/vite-plugin-head/index.js +1 -1
  139. package/dist/vite-plugin-markdown/index.js +1 -0
  140. package/package.json +8 -6
  141. package/tsconfigs/base.json +1 -1
  142. package/dist/core/endpoint/dev/index.d.ts +0 -2
  143. package/dist/core/endpoint/dev/index.js +0 -17
@@ -1,22 +1,27 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
- import fsMod from 'node:fs';
3
- import type { Plugin } from 'vite';
4
- import type { AstroSettings, ContentEntryType } from '../@types/astro.js';
5
- import { type ContentPaths } from './utils.js';
2
+ import nodeFs from 'node:fs';
3
+ import { type Plugin } from 'vite';
4
+ import type { AstroSettings } from '../@types/astro.js';
5
+ import { type ContentLookupMap } from './utils.js';
6
6
  interface AstroContentVirtualModPluginParams {
7
7
  settings: AstroSettings;
8
+ fs: typeof nodeFs;
8
9
  }
9
- export declare function astroContentVirtualModPlugin({ settings, }: AstroContentVirtualModPluginParams): Plugin;
10
+ export declare function astroContentVirtualModPlugin({ settings, fs, }: AstroContentVirtualModPluginParams): Plugin;
11
+ export declare function generateContentEntryFile({ settings, lookupMap, IS_DEV, IS_SERVER, }: {
12
+ settings: AstroSettings;
13
+ fs: typeof nodeFs;
14
+ lookupMap: ContentLookupMap;
15
+ IS_DEV: boolean;
16
+ IS_SERVER: boolean;
17
+ }): Promise<string>;
10
18
  /**
11
19
  * Generate a map from a collection + slug to the local file path.
12
20
  * This is used internally to resolve entry imports when using `getEntry()`.
13
21
  * @see `content-module.template.mjs`
14
22
  */
15
- export declare function getStringifiedLookupMap({ contentPaths, contentEntryConfigByExt, dataEntryExts, root, fs, }: {
16
- contentEntryConfigByExt: Map<string, ContentEntryType>;
17
- dataEntryExts: string[];
18
- contentPaths: Pick<ContentPaths, 'contentDir' | 'config'>;
19
- root: URL;
20
- fs: typeof fsMod;
21
- }): Promise<string>;
23
+ export declare function generateLookupMap({ settings, fs, }: {
24
+ settings: AstroSettings;
25
+ fs: typeof nodeFs;
26
+ }): Promise<ContentLookupMap>;
22
27
  export {};
@@ -1,12 +1,21 @@
1
1
  import glob from "fast-glob";
2
- import fsMod from "node:fs";
2
+ import nodeFs from "node:fs";
3
3
  import { extname } from "node:path";
4
4
  import { fileURLToPath, pathToFileURL } from "node:url";
5
5
  import pLimit from "p-limit";
6
+ import {} from "vite";
7
+ import { encodeName } from "../core/build/util.js";
6
8
  import { AstroError, AstroErrorData } from "../core/errors/index.js";
7
- import { appendForwardSlash } from "../core/path.js";
9
+ import { appendForwardSlash, removeFileExtension } from "../core/path.js";
8
10
  import { rootRelativePath } from "../core/util.js";
9
- import { VIRTUAL_MODULE_ID } from "./consts.js";
11
+ import { isServerLikeOutput } from "../prerender/utils.js";
12
+ import {
13
+ CONTENT_FLAG,
14
+ CONTENT_RENDER_FLAG,
15
+ DATA_FLAG,
16
+ RESOLVED_VIRTUAL_MODULE_ID,
17
+ VIRTUAL_MODULE_ID
18
+ } from "./consts.js";
10
19
  import {
11
20
  getContentEntryIdAndSlug,
12
21
  getContentPaths,
@@ -19,68 +28,140 @@ import {
19
28
  getExtGlob
20
29
  } from "./utils.js";
21
30
  function astroContentVirtualModPlugin({
22
- settings
31
+ settings,
32
+ fs
23
33
  }) {
24
- const contentPaths = getContentPaths(settings.config);
25
- const relContentDir = rootRelativePath(settings.config.root, contentPaths.contentDir);
26
- const contentEntryConfigByExt = getEntryConfigByExtMap(settings.contentEntryTypes);
27
- const contentEntryExts = [...contentEntryConfigByExt.keys()];
28
- const dataEntryExts = getDataEntryExts(settings);
29
- const virtualModContents = fsMod.readFileSync(contentPaths.virtualModTemplate, "utf-8").replace(
30
- "@@COLLECTION_NAME_BY_REFERENCE_KEY@@",
31
- new URL("reference-map.json", contentPaths.cacheDir).pathname
32
- ).replace("@@CONTENT_DIR@@", relContentDir).replace(
33
- "'@@CONTENT_ENTRY_GLOB_PATH@@'",
34
- JSON.stringify(globWithUnderscoresIgnored(relContentDir, contentEntryExts))
35
- ).replace(
36
- "'@@DATA_ENTRY_GLOB_PATH@@'",
37
- JSON.stringify(globWithUnderscoresIgnored(relContentDir, dataEntryExts))
38
- ).replace(
39
- "'@@RENDER_ENTRY_GLOB_PATH@@'",
40
- JSON.stringify(
41
- globWithUnderscoresIgnored(
42
- relContentDir,
43
- /** Note: data collections excluded */
44
- contentEntryExts
45
- )
46
- )
47
- );
48
- const astroContentVirtualModuleId = "\0" + VIRTUAL_MODULE_ID;
34
+ let IS_DEV = false;
35
+ const IS_SERVER = isServerLikeOutput(settings.config);
49
36
  return {
50
37
  name: "astro-content-virtual-mod-plugin",
38
+ enforce: "pre",
39
+ configResolved(config) {
40
+ IS_DEV = config.mode === "development";
41
+ },
51
42
  resolveId(id) {
52
43
  if (id === VIRTUAL_MODULE_ID) {
53
- return astroContentVirtualModuleId;
44
+ if (!settings.config.experimental.contentCollectionCache) {
45
+ return RESOLVED_VIRTUAL_MODULE_ID;
46
+ }
47
+ if (IS_DEV || IS_SERVER) {
48
+ return RESOLVED_VIRTUAL_MODULE_ID;
49
+ } else {
50
+ return { id: RESOLVED_VIRTUAL_MODULE_ID, external: true };
51
+ }
54
52
  }
55
53
  },
56
54
  async load(id) {
57
- if (id === astroContentVirtualModuleId) {
58
- const stringifiedLookupMap = await getStringifiedLookupMap({
59
- fs: fsMod,
60
- contentPaths,
61
- contentEntryConfigByExt,
62
- dataEntryExts,
63
- root: settings.config.root
55
+ if (id === RESOLVED_VIRTUAL_MODULE_ID) {
56
+ const lookupMap = await generateLookupMap({
57
+ settings,
58
+ fs
64
59
  });
60
+ const code = await generateContentEntryFile({ settings, fs, lookupMap, IS_DEV, IS_SERVER });
65
61
  return {
66
- code: virtualModContents.replace(
67
- "/* @@LOOKUP_MAP_ASSIGNMENT@@ */",
68
- `lookupMap = ${stringifiedLookupMap};`
69
- )
62
+ code,
63
+ meta: {
64
+ astro: {
65
+ hydratedComponents: [],
66
+ clientOnlyComponents: [],
67
+ scripts: [],
68
+ containsHead: true,
69
+ propagation: "in-tree",
70
+ pageOptions: {}
71
+ }
72
+ }
70
73
  };
71
74
  }
75
+ },
76
+ renderChunk(code, chunk) {
77
+ if (!settings.config.experimental.contentCollectionCache) {
78
+ return;
79
+ }
80
+ if (code.includes(RESOLVED_VIRTUAL_MODULE_ID)) {
81
+ const depth = chunk.fileName.split("/").length - 1;
82
+ const prefix = depth > 0 ? "../".repeat(depth) : "./";
83
+ return code.replaceAll(RESOLVED_VIRTUAL_MODULE_ID, `${prefix}content/entry.mjs`);
84
+ }
72
85
  }
73
86
  };
74
87
  }
75
- async function getStringifiedLookupMap({
76
- contentPaths,
77
- contentEntryConfigByExt,
78
- dataEntryExts,
79
- root,
88
+ async function generateContentEntryFile({
89
+ settings,
90
+ lookupMap,
91
+ IS_DEV,
92
+ IS_SERVER
93
+ }) {
94
+ const contentPaths = getContentPaths(settings.config);
95
+ const relContentDir = rootRelativePath(settings.config.root, contentPaths.contentDir);
96
+ let contentEntryGlobResult;
97
+ let dataEntryGlobResult;
98
+ let renderEntryGlobResult;
99
+ if (IS_DEV || IS_SERVER || !settings.config.experimental.contentCollectionCache) {
100
+ const contentEntryConfigByExt = getEntryConfigByExtMap(settings.contentEntryTypes);
101
+ const contentEntryExts = [...contentEntryConfigByExt.keys()];
102
+ const dataEntryExts = getDataEntryExts(settings);
103
+ const createGlob = (value, flag) => `import.meta.glob(${JSON.stringify(value)}, { query: { ${flag}: true } })`;
104
+ contentEntryGlobResult = createGlob(
105
+ globWithUnderscoresIgnored(relContentDir, contentEntryExts),
106
+ CONTENT_FLAG
107
+ );
108
+ dataEntryGlobResult = createGlob(
109
+ globWithUnderscoresIgnored(relContentDir, dataEntryExts),
110
+ DATA_FLAG
111
+ );
112
+ renderEntryGlobResult = createGlob(
113
+ globWithUnderscoresIgnored(relContentDir, contentEntryExts),
114
+ CONTENT_RENDER_FLAG
115
+ );
116
+ } else {
117
+ contentEntryGlobResult = getStringifiedCollectionFromLookup(
118
+ "content",
119
+ relContentDir,
120
+ lookupMap
121
+ );
122
+ dataEntryGlobResult = getStringifiedCollectionFromLookup("data", relContentDir, lookupMap);
123
+ renderEntryGlobResult = getStringifiedCollectionFromLookup("render", relContentDir, lookupMap);
124
+ }
125
+ const virtualModContents = nodeFs.readFileSync(contentPaths.virtualModTemplate, "utf-8").replace("@@CONTENT_DIR@@", relContentDir).replace("'@@CONTENT_ENTRY_GLOB_PATH@@'", contentEntryGlobResult).replace("'@@DATA_ENTRY_GLOB_PATH@@'", dataEntryGlobResult).replace("'@@RENDER_ENTRY_GLOB_PATH@@'", renderEntryGlobResult).replace("/* @@LOOKUP_MAP_ASSIGNMENT@@ */", `lookupMap = ${JSON.stringify(lookupMap)};`);
126
+ return virtualModContents;
127
+ }
128
+ function getStringifiedCollectionFromLookup(wantedType, relContentDir, lookupMap) {
129
+ let str = "{";
130
+ let normalize = (slug) => slug;
131
+ if (process.env.NODE_ENV === "production") {
132
+ const suffix = wantedType === "render" ? ".entry.mjs" : ".mjs";
133
+ normalize = (slug) => `${removeFileExtension(encodeName(slug)).replace(relContentDir, "./")}${suffix}`;
134
+ } else {
135
+ let suffix = "";
136
+ if (wantedType === "content")
137
+ suffix = CONTENT_FLAG;
138
+ else if (wantedType === "data")
139
+ suffix = DATA_FLAG;
140
+ else if (wantedType === "render")
141
+ suffix = CONTENT_RENDER_FLAG;
142
+ normalize = (slug) => `${slug}?${suffix}`;
143
+ }
144
+ for (const { type, entries } of Object.values(lookupMap)) {
145
+ if (type === wantedType || wantedType === "render" && type === "content") {
146
+ for (const slug of Object.values(entries)) {
147
+ str += `
148
+ "${slug}": () => import("${normalize(slug)}"),`;
149
+ }
150
+ }
151
+ }
152
+ str += "\n}";
153
+ return str;
154
+ }
155
+ async function generateLookupMap({
156
+ settings,
80
157
  fs
81
158
  }) {
159
+ const { root } = settings.config;
160
+ const contentPaths = getContentPaths(settings.config);
161
+ const relContentDir = rootRelativePath(root, contentPaths.contentDir, false);
162
+ const contentEntryConfigByExt = getEntryConfigByExtMap(settings.contentEntryTypes);
163
+ const dataEntryExts = getDataEntryExts(settings);
82
164
  const { contentDir } = contentPaths;
83
- const relContentDir = rootRelativePath(root, contentDir, false);
84
165
  const contentEntryExts = [...contentEntryConfigByExt.keys()];
85
166
  let lookupMap = {};
86
167
  const contentGlob = await glob(
@@ -88,10 +169,7 @@ async function getStringifiedLookupMap({
88
169
  {
89
170
  absolute: true,
90
171
  cwd: fileURLToPath(root),
91
- fs: {
92
- readdir: fs.readdir.bind(fs),
93
- readdirSync: fs.readdirSync.bind(fs)
94
- }
172
+ fs
95
173
  }
96
174
  );
97
175
  const limit = pLimit(10);
@@ -156,12 +234,8 @@ async function getStringifiedLookupMap({
156
234
  );
157
235
  }
158
236
  await Promise.all(promises);
159
- return JSON.stringify(lookupMap);
237
+ return lookupMap;
160
238
  }
161
- const UnexpectedLookupMapError = new AstroError({
162
- ...AstroErrorData.UnknownContentCollectionError,
163
- message: `Unexpected error while parsing content entry IDs and slugs.`
164
- });
165
239
  function globWithUnderscoresIgnored(relContentDir, exts) {
166
240
  const extGlob = getExtGlob(exts);
167
241
  const contentDir = appendForwardSlash(relContentDir);
@@ -171,7 +245,12 @@ function globWithUnderscoresIgnored(relContentDir, exts) {
171
245
  `!${contentDir}**/_*${extGlob}`
172
246
  ];
173
247
  }
248
+ const UnexpectedLookupMapError = new AstroError({
249
+ ...AstroErrorData.UnknownContentCollectionError,
250
+ message: `Unexpected error while parsing content entry IDs and slugs.`
251
+ });
174
252
  export {
175
253
  astroContentVirtualModPlugin,
176
- getStringifiedLookupMap
254
+ generateContentEntryFile,
255
+ generateLookupMap
177
256
  };
@@ -1,6 +1,8 @@
1
+ import { createI18nMiddleware } from "../../i18n/middleware.js";
1
2
  import { getSetCookiesFromResponse } from "../cookies/index.js";
2
3
  import { consoleLogDestination } from "../logger/console.js";
3
4
  import { AstroIntegrationLogger, Logger } from "../logger/core.js";
5
+ import { sequence } from "../middleware/index.js";
4
6
  import {
5
7
  collapseDuplicateSlashes,
6
8
  prependForwardSlash,
@@ -128,8 +130,19 @@ class App {
128
130
  );
129
131
  let response;
130
132
  try {
131
- if (mod.onRequest) {
132
- this.#pipeline.setMiddlewareFunction(mod.onRequest);
133
+ let i18nMiddleware = createI18nMiddleware(this.#manifest.i18n, this.#manifest.base);
134
+ if (i18nMiddleware) {
135
+ if (mod.onRequest) {
136
+ this.#pipeline.setMiddlewareFunction(
137
+ sequence(i18nMiddleware, mod.onRequest)
138
+ );
139
+ } else {
140
+ this.#pipeline.setMiddlewareFunction(i18nMiddleware);
141
+ }
142
+ } else {
143
+ if (mod.onRequest) {
144
+ this.#pipeline.setMiddlewareFunction(mod.onRequest);
145
+ }
133
146
  }
134
147
  response = await this.#pipeline.renderRoute(renderContext, pageModule);
135
148
  } catch (err) {
@@ -169,7 +182,8 @@ class App {
169
182
  route: routeData,
170
183
  status,
171
184
  env: this.#pipeline.env,
172
- mod: handler
185
+ mod: handler,
186
+ locales: this.#manifest.i18n ? this.#manifest.i18n.locales : void 0
173
187
  });
174
188
  } else {
175
189
  const pathname = prependForwardSlash(this.removeBase(url.pathname));
@@ -200,7 +214,8 @@ class App {
200
214
  route: routeData,
201
215
  status,
202
216
  mod,
203
- env: this.#pipeline.env
217
+ env: this.#pipeline.env,
218
+ locales: this.#manifest.i18n ? this.#manifest.i18n.locales : void 0
204
219
  });
205
220
  }
206
221
  }
@@ -42,6 +42,13 @@ export type SSRManifest = {
42
42
  componentMetadata: SSRResult['componentMetadata'];
43
43
  pageModule?: SinglePageBuiltModule;
44
44
  pageMap?: Map<ComponentPath, ImportComponentInstance>;
45
+ i18n: SSRManifestI18n | undefined;
46
+ };
47
+ export type SSRManifestI18n = {
48
+ fallback?: Record<string, string>;
49
+ routingStrategy?: 'prefix-always' | 'prefix-other-locales';
50
+ locales: string[];
51
+ defaultLocale: string;
45
52
  };
46
53
  export type SerializedSSRManifest = Omit<SSRManifest, 'routes' | 'assets' | 'componentMetadata' | 'clientDirectives'> & {
47
54
  routes: SerializedRouteInfo[];
@@ -49,4 +56,3 @@ export type SerializedSSRManifest = Omit<SSRManifest, 'routes' | 'assets' | 'com
49
56
  componentMetadata: [string, SSRComponentMetadata][];
50
57
  clientDirectives: [string, string][];
51
58
  };
52
- export type AdapterCreateExports<T = any> = (manifest: SSRManifest, args?: T) => Record<string, any>;
@@ -2,17 +2,20 @@ import { getOutputDirectory, isServerLikeOutput } from "../../prerender/utils.js
2
2
  import { BEFORE_HYDRATION_SCRIPT_ID } from "../../vite-plugin-scripts/index.js";
3
3
  import { Logger } from "../logger/core.js";
4
4
  import { Pipeline } from "../pipeline.js";
5
+ import { routeIsFallback, routeIsRedirect } from "../redirects/helpers.js";
5
6
  import { createEnvironment } from "../render/index.js";
6
7
  import { createAssetLink } from "../render/ssr-element.js";
7
8
  import { ASTRO_PAGE_RESOLVED_MODULE_ID } from "./plugins/plugin-pages.js";
8
9
  import { RESOLVED_SPLIT_MODULE_ID } from "./plugins/plugin-ssr.js";
9
10
  import { ASTRO_PAGE_EXTENSION_POST_PATTERN } from "./plugins/util.js";
11
+ import { i18nHasFallback } from "./util.js";
10
12
  class BuildPipeline extends Pipeline {
11
13
  #internals;
12
14
  #staticBuildOptions;
13
15
  #manifest;
14
16
  constructor(staticBuildOptions, internals, manifest) {
15
17
  const ssr = isServerLikeOutput(staticBuildOptions.settings.config);
18
+ const resolveCache = /* @__PURE__ */ new Map();
16
19
  super(
17
20
  createEnvironment({
18
21
  adapterName: manifest.adapterName,
@@ -22,14 +25,20 @@ class BuildPipeline extends Pipeline {
22
25
  clientDirectives: manifest.clientDirectives,
23
26
  compressHTML: manifest.compressHTML,
24
27
  async resolve(specifier) {
28
+ if (resolveCache.has(specifier)) {
29
+ return resolveCache.get(specifier);
30
+ }
25
31
  const hashedFilePath = manifest.entryModules[specifier];
26
32
  if (typeof hashedFilePath !== "string" || hashedFilePath === "") {
27
33
  if (specifier === BEFORE_HYDRATION_SCRIPT_ID) {
34
+ resolveCache.set(specifier, "");
28
35
  return "";
29
36
  }
30
37
  throw new Error(`Cannot find the built path for ${specifier}`);
31
38
  }
32
- return createAssetLink(hashedFilePath, manifest.base, manifest.assetsPrefix);
39
+ const assetLink = createAssetLink(hashedFilePath, manifest.base, manifest.assetsPrefix);
40
+ resolveCache.set(specifier, assetLink);
41
+ return assetLink;
33
42
  },
34
43
  routeCache: staticBuildOptions.routeCache,
35
44
  site: manifest.site,
@@ -119,9 +128,13 @@ class BuildPipeline extends Pipeline {
119
128
  pages.set(pageData, filePath);
120
129
  }
121
130
  }
122
- for (const [path, pageData] of this.#internals.pagesByComponent.entries()) {
123
- if (pageData.route.type === "redirect") {
124
- pages.set(pageData, path);
131
+ for (const [path, pageDataList] of this.#internals.pagesByComponents.entries()) {
132
+ for (const pageData of pageDataList) {
133
+ if (routeIsRedirect(pageData.route)) {
134
+ pages.set(pageData, path);
135
+ } else if (routeIsFallback(pageData.route) && (i18nHasFallback(this.getConfig()) || routeIsFallback(pageData.route) && pageData.route.route === "/")) {
136
+ pages.set(pageData, path);
137
+ }
125
138
  }
126
139
  }
127
140
  return pages;
@@ -15,6 +15,7 @@ function getOutFolder(astroConfig, pathname, routeType) {
15
15
  switch (routeType) {
16
16
  case "endpoint":
17
17
  return new URL("." + appendForwardSlash(npath.dirname(pathname)), outRoot);
18
+ case "fallback":
18
19
  case "page":
19
20
  case "redirect":
20
21
  switch (astroConfig.build.format) {
@@ -36,6 +37,7 @@ function getOutFile(astroConfig, outFolder, pathname, routeType) {
36
37
  case "endpoint":
37
38
  return new URL(npath.basename(pathname), outFolder);
38
39
  case "page":
40
+ case "fallback":
39
41
  case "redirect":
40
42
  switch (astroConfig.build.format) {
41
43
  case "directory": {
@@ -17,11 +17,18 @@ import {
17
17
  removeLeadingForwardSlash,
18
18
  removeTrailingForwardSlash
19
19
  } from "../../core/path.js";
20
+ import { createI18nMiddleware } from "../../i18n/middleware.js";
20
21
  import { runHookBuildGenerated } from "../../integrations/index.js";
21
22
  import { getOutputDirectory, isServerLikeOutput } from "../../prerender/utils.js";
22
23
  import { PAGE_SCRIPT_ID } from "../../vite-plugin-scripts/index.js";
23
24
  import { AstroError, AstroErrorData } from "../errors/index.js";
24
- import { RedirectSinglePageBuiltModule, getRedirectLocationOrThrow } from "../redirects/index.js";
25
+ import { sequence } from "../middleware/index.js";
26
+ import { routeIsFallback } from "../redirects/helpers.js";
27
+ import {
28
+ RedirectSinglePageBuiltModule,
29
+ getRedirectLocationOrThrow,
30
+ routeIsRedirect
31
+ } from "../redirects/index.js";
25
32
  import { createRenderContext } from "../render/index.js";
26
33
  import { callGetStaticPaths } from "../render/route-cache.js";
27
34
  import {
@@ -40,7 +47,7 @@ import {
40
47
  getPageDataByComponent,
41
48
  mergeInlineCss
42
49
  } from "./internal.js";
43
- import { getTimeStat } from "./util.js";
50
+ import { getTimeStat, shouldAppendForwardSlash } from "./util.js";
44
51
  function createEntryURL(filePath, outFolder) {
45
52
  return new URL("./" + filePath + `?time=${Date.now()}`, outFolder);
46
53
  }
@@ -58,6 +65,20 @@ async function getEntryForRedirectRoute(route, internals, outFolder) {
58
65
  }
59
66
  return RedirectSinglePageBuiltModule;
60
67
  }
68
+ async function getEntryForFallbackRoute(route, internals, outFolder) {
69
+ if (route.type !== "fallback") {
70
+ throw new Error(`Expected a redirect route.`);
71
+ }
72
+ if (route.redirectRoute) {
73
+ const filePath = getEntryFilePathFromComponentPath(internals, route.redirectRoute.component);
74
+ if (filePath) {
75
+ const url = createEntryURL(filePath, outFolder);
76
+ const ssrEntryPage = await import(url.toString());
77
+ return ssrEntryPage;
78
+ }
79
+ }
80
+ return RedirectSinglePageBuiltModule;
81
+ }
61
82
  function shouldSkipDraft(pageModule, settings) {
62
83
  return (
63
84
  // Drafts are disabled
@@ -131,16 +152,15 @@ ${bgGreen(black(` ${verb} static routes `))}`);
131
152
  await generatePage(pageData, ssrEntry, builtPaths, pipeline);
132
153
  }
133
154
  }
134
- if (pageData.route.type === "redirect") {
135
- const entry = await getEntryForRedirectRoute(pageData.route, internals, outFolder);
136
- await generatePage(pageData, entry, builtPaths, pipeline);
137
- }
138
155
  }
139
156
  } else {
140
157
  for (const [pageData, filePath] of pagesToGenerate) {
141
- if (pageData.route.type === "redirect") {
158
+ if (routeIsRedirect(pageData.route)) {
142
159
  const entry = await getEntryForRedirectRoute(pageData.route, internals, outFolder);
143
160
  await generatePage(pageData, entry, builtPaths, pipeline);
161
+ } else if (routeIsFallback(pageData.route)) {
162
+ const entry = await getEntryForFallbackRoute(pageData.route, internals, outFolder);
163
+ await generatePage(pageData, entry, builtPaths, pipeline);
144
164
  } else {
145
165
  const ssrEntryURLPage = createEntryURL(filePath, outFolder);
146
166
  const entry = await import(ssrEntryURLPage.toString());
@@ -154,7 +174,7 @@ ${bgGreen(black(` ${verb} static routes `))}`);
154
174
  if (staticImageList.size) {
155
175
  logger.info(null, `
156
176
  ${bgGreen(black(` generating optimized images `))}`);
157
- const totalCount = Array.from(staticImageList.values()).map((x) => x.size).reduce((a, b) => a + b, 0);
177
+ const totalCount = Array.from(staticImageList.values()).map((x) => x.transforms.size).reduce((a, b) => a + b, 0);
158
178
  const cpuCount = os.cpus().length;
159
179
  const assetsCreationEnvironment = await prepareAssetsGenerationEnv(pipeline, totalCount);
160
180
  const queue = new PQueue({ concurrency: cpuCount });
@@ -176,13 +196,26 @@ ${bgGreen(black(` generating optimized images `))}`);
176
196
  async function generatePage(pageData, ssrEntry, builtPaths, pipeline) {
177
197
  let timeStart = performance.now();
178
198
  const logger = pipeline.getLogger();
199
+ const config = pipeline.getConfig();
179
200
  const pageInfo = getPageDataByComponent(pipeline.getInternals(), pageData.route.component);
180
201
  const linkIds = [];
181
202
  const scripts = pageInfo?.hoistedScript ?? null;
182
203
  const styles = pageData.styles.sort(cssOrder).map(({ sheet }) => sheet).reduce(mergeInlineCss, []);
183
204
  const pageModulePromise = ssrEntry.page;
184
205
  const onRequest = ssrEntry.onRequest;
185
- if (onRequest) {
206
+ const i18nMiddleware = createI18nMiddleware(
207
+ pipeline.getManifest().i18n,
208
+ pipeline.getManifest().base
209
+ );
210
+ if (config.experimental.i18n && i18nMiddleware) {
211
+ if (onRequest) {
212
+ pipeline.setMiddlewareFunction(
213
+ sequence(i18nMiddleware, onRequest)
214
+ );
215
+ } else {
216
+ pipeline.setMiddlewareFunction(i18nMiddleware);
217
+ }
218
+ } else if (onRequest) {
186
219
  pipeline.setMiddlewareFunction(onRequest);
187
220
  }
188
221
  if (!pageModulePromise) {
@@ -206,7 +239,7 @@ async function generatePage(pageData, ssrEntry, builtPaths, pipeline) {
206
239
  styles,
207
240
  mod: pageModule
208
241
  };
209
- const icon = pageData.route.type === "page" || pageData.route.type === "redirect" ? green("\u25B6") : magenta("\u03BB");
242
+ const icon = pageData.route.type === "page" || pageData.route.type === "redirect" || pageData.route.type === "fallback" ? green("\u25B6") : magenta("\u03BB");
210
243
  if (isRelativePath(pageData.route.component)) {
211
244
  logger.info(null, `${icon} ${pageData.route.route}`);
212
245
  } else {
@@ -297,22 +330,6 @@ function getInvalidRouteSegmentError(e, route, staticPath) {
297
330
  hint
298
331
  });
299
332
  }
300
- function shouldAppendForwardSlash(trailingSlash, buildFormat) {
301
- switch (trailingSlash) {
302
- case "always":
303
- return true;
304
- case "never":
305
- return false;
306
- case "ignore": {
307
- switch (buildFormat) {
308
- case "directory":
309
- return true;
310
- case "file":
311
- return false;
312
- }
313
- }
314
- }
315
- }
316
333
  function addPageName(pathname, opts) {
317
334
  const trailingSlash = opts.settings.config.trailingSlash;
318
335
  const buildFormat = opts.settings.config.build.format;
@@ -375,21 +392,24 @@ async function generatePath(pathname, gopts, pipeline) {
375
392
  pipeline.getConfig().build.format,
376
393
  pageData.route.type
377
394
  );
395
+ const request = createRequest({
396
+ url,
397
+ headers: new Headers(),
398
+ logger: pipeline.getLogger(),
399
+ ssr
400
+ });
401
+ const i18n = pipeline.getConfig().experimental.i18n;
378
402
  const renderContext = await createRenderContext({
379
403
  pathname,
380
- request: createRequest({
381
- url,
382
- headers: new Headers(),
383
- logger: pipeline.getLogger(),
384
- ssr
385
- }),
404
+ request,
386
405
  componentMetadata: manifest.componentMetadata,
387
406
  scripts,
388
407
  styles,
389
408
  links,
390
409
  route: pageData.route,
391
410
  env: pipeline.getEnvironment(),
392
- mod
411
+ mod,
412
+ locales: i18n ? i18n.locales : void 0
393
413
  });
394
414
  let body;
395
415
  let encoding;
@@ -438,6 +458,15 @@ async function generatePath(pathname, gopts, pipeline) {
438
458
  await fs.promises.writeFile(outFile, body, encoding);
439
459
  }
440
460
  function createBuildManifest(settings, internals, renderers) {
461
+ let i18nManifest = void 0;
462
+ if (settings.config.experimental.i18n) {
463
+ i18nManifest = {
464
+ fallback: settings.config.experimental.i18n.fallback,
465
+ routingStrategy: settings.config.experimental.i18n.routingStrategy,
466
+ defaultLocale: settings.config.experimental.i18n.defaultLocale,
467
+ locales: settings.config.experimental.i18n.locales
468
+ };
469
+ }
441
470
  return {
442
471
  assets: /* @__PURE__ */ new Set(),
443
472
  entryModules: Object.fromEntries(internals.entrySpecifierToBundleMap.entries()),
@@ -449,7 +478,8 @@ function createBuildManifest(settings, internals, renderers) {
449
478
  base: settings.config.base,
450
479
  assetsPrefix: settings.config.build.assetsPrefix,
451
480
  site: settings.config.site ? new URL(settings.config.base, settings.config.site).toString() : settings.config.site,
452
- componentMetadata: internals.componentMetadata
481
+ componentMetadata: internals.componentMetadata,
482
+ i18n: i18nManifest
453
483
  };
454
484
  }
455
485
  export {
@@ -1,13 +1,5 @@
1
1
  import type { AstroInlineConfig } from '../../@types/astro.js';
2
2
  export interface BuildOptions {
3
- /**
4
- * Teardown the compiler WASM instance after build. This can improve performance when
5
- * building once, but may cause a performance hit if building multiple times in a row.
6
- *
7
- * @internal only used for testing
8
- * @default true
9
- */
10
- teardownCompiler?: boolean;
11
3
  }
12
4
  /**
13
5
  * Builds your site for deployment. By default, this will generate static files and place them in a dist/ directory.
@@ -23,11 +23,18 @@ import { createRouteManifest } from "../routing/index.js";
23
23
  import { collectPagesData } from "./page-data.js";
24
24
  import { staticBuild, viteBuild } from "./static-build.js";
25
25
  import { getTimeStat } from "./util.js";
26
- async function build(inlineConfig, options) {
26
+ async function build(inlineConfig, options = {}) {
27
27
  applyPolyfill();
28
28
  const logger = createNodeLogger(inlineConfig);
29
29
  const { userConfig, astroConfig } = await resolveConfig(inlineConfig, "build");
30
30
  telemetry.record(eventCliSession("build", userConfig));
31
+ if (astroConfig.experimental.contentCollectionCache && options.force) {
32
+ const contentCacheDir = new URL("./content/", astroConfig.cacheDir);
33
+ if (fs.existsSync(contentCacheDir)) {
34
+ logger.warn("content", "clearing cache");
35
+ await fs.promises.rm(contentCacheDir, { force: true, recursive: true });
36
+ }
37
+ }
31
38
  const settings = await createSettings(astroConfig, fileURLToPath(astroConfig.root));
32
39
  const builder = new AstroBuilder(settings, {
33
40
  ...options,
@@ -138,7 +145,7 @@ class AstroBuilder {
138
145
  await runHookBuildDone({
139
146
  config: this.settings.config,
140
147
  pages: pageNames,
141
- routes: Object.values(allPages).map((pd) => pd.route),
148
+ routes: Object.values(allPages).flat().map((pageData) => pageData.route),
142
149
  logging: this.logger
143
150
  });
144
151
  if (this.logger.level && levels[this.logger.level()] <= levels["info"]) {