astro 4.10.1 → 4.10.3

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 (68) hide show
  1. package/dist/@types/astro.d.ts +43 -39
  2. package/dist/config/index.d.ts +2 -2
  3. package/dist/config/index.js +4 -4
  4. package/dist/container/index.d.ts +32 -1
  5. package/dist/container/index.js +45 -0
  6. package/dist/container/pipeline.d.ts +1 -1
  7. package/dist/container/pipeline.js +17 -18
  8. package/dist/container/vite-plugin-container.d.ts +2 -0
  9. package/dist/container/vite-plugin-container.js +15 -0
  10. package/dist/content/runtime.js +2 -2
  11. package/dist/content/types-generator.js +30 -32
  12. package/dist/content/utils.d.ts +11 -0
  13. package/dist/content/utils.js +49 -0
  14. package/dist/content/vite-plugin-content-imports.d.ts +3 -1
  15. package/dist/content/vite-plugin-content-imports.js +15 -4
  16. package/dist/core/app/index.js +0 -4
  17. package/dist/core/app/pipeline.d.ts +1 -1
  18. package/dist/core/app/pipeline.js +4 -4
  19. package/dist/core/base-pipeline.d.ts +1 -1
  20. package/dist/core/base-pipeline.js +1 -1
  21. package/dist/core/build/generate.js +2 -1
  22. package/dist/core/build/internal.d.ts +4 -0
  23. package/dist/core/build/internal.js +2 -1
  24. package/dist/core/build/page-data.js +2 -4
  25. package/dist/core/build/pipeline.d.ts +1 -1
  26. package/dist/core/build/pipeline.js +4 -4
  27. package/dist/core/build/plugins/plugin-chunks.js +6 -0
  28. package/dist/core/build/plugins/plugin-prerender.js +55 -48
  29. package/dist/core/build/plugins/plugin-ssr.js +15 -12
  30. package/dist/core/build/static-build.js +36 -44
  31. package/dist/core/build/types.d.ts +0 -1
  32. package/dist/core/config/schema.d.ts +422 -78
  33. package/dist/core/constants.d.ts +4 -0
  34. package/dist/core/constants.js +3 -1
  35. package/dist/core/create-vite.js +4 -2
  36. package/dist/core/dev/dev.js +1 -1
  37. package/dist/core/errors/errors-data.d.ts +31 -0
  38. package/dist/core/errors/errors-data.js +12 -0
  39. package/dist/core/messages.js +2 -2
  40. package/dist/core/render-context.d.ts +1 -1
  41. package/dist/core/render-context.js +28 -22
  42. package/dist/core/routing/astro-designed-error-pages.d.ts +1 -0
  43. package/dist/core/routing/astro-designed-error-pages.js +15 -1
  44. package/dist/core/routing/params.js +1 -1
  45. package/dist/core/util.js +5 -2
  46. package/dist/env/config.d.ts +2 -1
  47. package/dist/env/config.js +4 -0
  48. package/dist/env/constants.d.ts +0 -1
  49. package/dist/env/constants.js +0 -2
  50. package/dist/env/runtime.d.ts +3 -1
  51. package/dist/env/runtime.js +8 -1
  52. package/dist/env/schema.d.ts +198 -220
  53. package/dist/env/schema.js +47 -79
  54. package/dist/env/validators.js +73 -10
  55. package/dist/env/vite-plugin-env.js +15 -15
  56. package/dist/i18n/index.d.ts +1 -1
  57. package/dist/i18n/index.js +4 -11
  58. package/dist/jsx/server.d.ts +3 -5
  59. package/dist/jsx/server.js +3 -1
  60. package/dist/runtime/server/render/astro/render.js +8 -2
  61. package/dist/vite-plugin-astro/index.js +1 -1
  62. package/dist/vite-plugin-astro-server/pipeline.d.ts +1 -1
  63. package/dist/vite-plugin-astro-server/pipeline.js +4 -4
  64. package/dist/vite-plugin-astro-server/request.js +2 -2
  65. package/dist/vite-plugin-astro-server/route.js +18 -1
  66. package/package.json +16 -16
  67. package/templates/env/module.mjs +14 -5
  68. package/templates/env/types.d.ts +1 -12
@@ -16,10 +16,12 @@ import {
16
16
  getEntryConfigByExtMap,
17
17
  getEntryData,
18
18
  getEntryType,
19
+ getSymlinkedContentCollections,
19
20
  globalContentConfigObserver,
20
21
  hasContentFlag,
21
22
  parseEntrySlug,
22
- reloadContentConfigObserver
23
+ reloadContentConfigObserver,
24
+ reverseSymlink
23
25
  } from "./utils.js";
24
26
  function getContentRendererByViteId(viteId, settings) {
25
27
  let ext = viteId.split(".").pop();
@@ -35,7 +37,8 @@ const CHOKIDAR_MODIFIED_EVENTS = ["add", "unlink", "change"];
35
37
  const COLLECTION_TYPES_TO_INVALIDATE_ON = ["data", "content", "config"];
36
38
  function astroContentImportPlugin({
37
39
  fs,
38
- settings
40
+ settings,
41
+ logger
39
42
  }) {
40
43
  const contentPaths = getContentPaths(settings.config, fs);
41
44
  const contentEntryExts = getContentEntryExts(settings);
@@ -44,15 +47,23 @@ function astroContentImportPlugin({
44
47
  const dataEntryConfigByExt = getEntryConfigByExtMap(settings.dataEntryTypes);
45
48
  const { contentDir } = contentPaths;
46
49
  let shouldEmitFile = false;
50
+ let symlinks;
47
51
  const plugins = [
48
52
  {
49
53
  name: "astro:content-imports",
50
54
  config(_config, env) {
51
55
  shouldEmitFile = env.command === "build";
52
56
  },
57
+ async buildStart() {
58
+ symlinks = await getSymlinkedContentCollections({ contentDir, logger, fs });
59
+ },
53
60
  async transform(_, viteId) {
54
61
  if (hasContentFlag(viteId, DATA_FLAG)) {
55
- const fileId = viteId.split("?")[0] ?? viteId;
62
+ const fileId = reverseSymlink({
63
+ entry: viteId.split("?")[0] ?? viteId,
64
+ contentDir,
65
+ symlinks
66
+ });
56
67
  const { id, data, collection, _internal } = await getDataEntryModule({
57
68
  fileId,
58
69
  entryConfigByExt: dataEntryConfigByExt,
@@ -74,7 +85,7 @@ export const _internal = {
74
85
  `;
75
86
  return code;
76
87
  } else if (hasContentFlag(viteId, CONTENT_FLAG)) {
77
- const fileId = viteId.split("?")[0];
88
+ const fileId = reverseSymlink({ entry: viteId.split("?")[0], contentDir, symlinks });
78
89
  const { id, slug, collection, body, data, _internal } = await getContentEntryModule({
79
90
  fileId,
80
91
  entryConfigByExt: contentEntryConfigByExt,
@@ -13,7 +13,6 @@ import { AstroIntegrationLogger, Logger } from "../logger/core.js";
13
13
  import { sequence } from "../middleware/index.js";
14
14
  import {
15
15
  appendForwardSlash,
16
- collapseDuplicateSlashes,
17
16
  joinPaths,
18
17
  prependForwardSlash,
19
18
  removeTrailingForwardSlash
@@ -204,9 +203,6 @@ class App {
204
203
  if (clientAddress) {
205
204
  Reflect.set(request, clientAddressSymbol, clientAddress);
206
205
  }
207
- if (request.url !== collapseDuplicateSlashes(request.url)) {
208
- request = new Request(collapseDuplicateSlashes(request.url), request);
209
- }
210
206
  if (!routeData) {
211
207
  routeData = this.match(request);
212
208
  this.#logger.debug("router", "Astro matched the following route for " + request.url);
@@ -7,7 +7,7 @@ export declare class AppPipeline extends Pipeline {
7
7
  headElements(routeData: RouteData): Pick<SSRResult, 'scripts' | 'styles' | 'links'>;
8
8
  componentMetadata(): void;
9
9
  getComponentByRoute(routeData: RouteData): Promise<ComponentInstance>;
10
- tryRewrite(payload: RewritePayload, request: Request, sourceRoute: RouteData): Promise<[RouteData, ComponentInstance]>;
10
+ tryRewrite(payload: RewritePayload, request: Request, sourceRoute: RouteData): Promise<[RouteData, ComponentInstance, URL]>;
11
11
  getModuleForRoute(route: RouteData): Promise<SinglePageBuiltModule>;
12
12
  rewriteKnownRoute(pathname: string, _sourceRoute: RouteData): ComponentInstance;
13
13
  }
@@ -64,8 +64,8 @@ class AppPipeline extends Pipeline {
64
64
  }
65
65
  async tryRewrite(payload, request, sourceRoute) {
66
66
  let foundRoute;
67
+ let finalUrl = void 0;
67
68
  for (const route of this.#manifestData.routes) {
68
- let finalUrl = void 0;
69
69
  if (payload instanceof URL) {
70
70
  finalUrl = payload;
71
71
  } else if (payload instanceof Request) {
@@ -81,13 +81,13 @@ class AppPipeline extends Pipeline {
81
81
  break;
82
82
  }
83
83
  }
84
- if (foundRoute) {
84
+ if (foundRoute && finalUrl) {
85
85
  if (foundRoute.pathname === "/404") {
86
86
  const componentInstance = this.rewriteKnownRoute(foundRoute.pathname, sourceRoute);
87
- return [foundRoute, componentInstance];
87
+ return [foundRoute, componentInstance, finalUrl];
88
88
  } else {
89
89
  const componentInstance = await this.getComponentByRoute(foundRoute);
90
- return [foundRoute, componentInstance];
90
+ return [foundRoute, componentInstance, finalUrl];
91
91
  }
92
92
  }
93
93
  throw new AstroError({
@@ -67,7 +67,7 @@ export declare abstract class Pipeline {
67
67
  * @param {Request} request The original request
68
68
  * @param {RouteData} sourceRoute The original `RouteData`
69
69
  */
70
- abstract tryRewrite(rewritePayload: RewritePayload, request: Request, sourceRoute: RouteData): Promise<[RouteData, ComponentInstance]>;
70
+ abstract tryRewrite(rewritePayload: RewritePayload, request: Request, sourceRoute: RouteData): Promise<[RouteData, ComponentInstance, URL]>;
71
71
  /**
72
72
  * Tells the pipeline how to retrieve a component give a `RouteData`
73
73
  * @param routeData
@@ -30,7 +30,7 @@ class Pipeline {
30
30
  if (callSetGetEnv && manifest.experimentalEnvGetSecretEnabled) {
31
31
  setGetEnv(() => {
32
32
  throw new AstroError(AstroErrorData.EnvUnsupportedGetSecret);
33
- });
33
+ }, true);
34
34
  }
35
35
  }
36
36
  internalMiddleware;
@@ -26,6 +26,7 @@ import { RenderContext } from "../render-context.js";
26
26
  import { callGetStaticPaths } from "../render/route-cache.js";
27
27
  import { createRequest } from "../request.js";
28
28
  import { matchRoute } from "../routing/match.js";
29
+ import { stringifyParams } from "../routing/params.js";
29
30
  import { getOutputFilename, isServerLikeOutput } from "../util.js";
30
31
  import { getOutDirWithinCwd, getOutFile, getOutFolder } from "./common.js";
31
32
  import { cssOrder, mergeInlineCss } from "./internal.js";
@@ -212,7 +213,7 @@ async function getPathsForRoute(route, mod, pipeline, builtPaths) {
212
213
  );
213
214
  paths = staticPaths.map((staticPath) => {
214
215
  try {
215
- return route.generate(staticPath.params);
216
+ return stringifyParams(staticPath.params, route);
216
217
  } catch (e) {
217
218
  if (e instanceof TypeError) {
218
219
  throw getInvalidRouteSegmentError(e, route, staticPath);
@@ -89,6 +89,10 @@ export interface BuildInternals {
89
89
  ssrSplitEntryChunks: Map<string, Rollup.OutputChunk>;
90
90
  componentMetadata: SSRResult['componentMetadata'];
91
91
  middlewareEntryPoint?: URL;
92
+ /**
93
+ * Chunks in the bundle that are only used in prerendering that we can delete later
94
+ */
95
+ prerenderOnlyChunks: Rollup.OutputChunk[];
92
96
  }
93
97
  /**
94
98
  * Creates internal maps used to coordinate the CSS and HTML plugins.
@@ -26,7 +26,8 @@ function createBuildInternals() {
26
26
  componentMetadata: /* @__PURE__ */ new Map(),
27
27
  ssrSplitEntryChunks: /* @__PURE__ */ new Map(),
28
28
  entryPoints: /* @__PURE__ */ new Map(),
29
- cacheManifestUsed: false
29
+ cacheManifestUsed: false,
30
+ prerenderOnlyChunks: []
30
31
  };
31
32
  }
32
33
  function trackPageData(internals, component, pageData, componentModuleId, componentURL) {
@@ -29,8 +29,7 @@ async function collectPagesData(opts) {
29
29
  route,
30
30
  moduleSpecifier: "",
31
31
  styles: [],
32
- hoistedScript: void 0,
33
- hasSharedModules: false
32
+ hoistedScript: void 0
34
33
  };
35
34
  clearInterval(routeCollectionLogTimeout);
36
35
  if (settings.config.output === "static") {
@@ -50,8 +49,7 @@ async function collectPagesData(opts) {
50
49
  route,
51
50
  moduleSpecifier: "",
52
51
  styles: [],
53
- hoistedScript: void 0,
54
- hasSharedModules: false
52
+ hoistedScript: void 0
55
53
  };
56
54
  }
57
55
  clearInterval(dataCollectionLogTimeout);
@@ -38,7 +38,7 @@ export declare class BuildPipeline extends Pipeline {
38
38
  */
39
39
  retrieveRoutesToGenerate(): Map<PageBuildData, string>;
40
40
  getComponentByRoute(routeData: RouteData): Promise<ComponentInstance>;
41
- tryRewrite(payload: RewritePayload, request: Request, sourceRoute: RouteData): Promise<[RouteData, ComponentInstance]>;
41
+ tryRewrite(payload: RewritePayload, request: Request, sourceRoute: RouteData): Promise<[RouteData, ComponentInstance, URL]>;
42
42
  retrieveSsrEntry(route: RouteData, filePath: string): Promise<SinglePageBuiltModule>;
43
43
  rewriteKnownRoute(_pathname: string, sourceRoute: RouteData): ComponentInstance;
44
44
  }
@@ -209,8 +209,8 @@ class BuildPipeline extends Pipeline {
209
209
  }
210
210
  async tryRewrite(payload, request, sourceRoute) {
211
211
  let foundRoute;
212
+ let finalUrl = void 0;
212
213
  for (const route of this.options.manifest.routes) {
213
- let finalUrl = void 0;
214
214
  if (payload instanceof URL) {
215
215
  finalUrl = payload;
216
216
  } else if (payload instanceof Request) {
@@ -226,13 +226,13 @@ class BuildPipeline extends Pipeline {
226
226
  break;
227
227
  }
228
228
  }
229
- if (foundRoute) {
229
+ if (foundRoute && finalUrl) {
230
230
  if (foundRoute.pathname === "/404") {
231
231
  const componentInstance = await this.rewriteKnownRoute(foundRoute.pathname, sourceRoute);
232
- return [foundRoute, componentInstance];
232
+ return [foundRoute, componentInstance, finalUrl];
233
233
  } else {
234
234
  const componentInstance = await this.getComponentByRoute(foundRoute);
235
- return [foundRoute, componentInstance];
235
+ return [foundRoute, componentInstance, finalUrl];
236
236
  }
237
237
  } else {
238
238
  throw new AstroError({
@@ -8,6 +8,12 @@ function vitePluginChunks() {
8
8
  if (id.includes("astro/dist/runtime/server/")) {
9
9
  return "astro/server";
10
10
  }
11
+ if (id.includes("astro/dist/runtime")) {
12
+ return "astro";
13
+ }
14
+ if (id.includes("astro/dist/env/setup")) {
15
+ return "astro/env-setup";
16
+ }
11
17
  }
12
18
  });
13
19
  }
@@ -1,57 +1,64 @@
1
- import path from "node:path";
2
1
  import { getPrerenderMetadata } from "../../../prerender/metadata.js";
3
- import { extendManualChunks } from "./util.js";
4
- function vitePluginPrerender(opts, internals) {
2
+ import { ASTRO_PAGE_RESOLVED_MODULE_ID } from "./plugin-pages.js";
3
+ import { getPagesFromVirtualModulePageName } from "./util.js";
4
+ function vitePluginPrerender(internals) {
5
5
  return {
6
6
  name: "astro:rollup-plugin-prerender",
7
- outputOptions(outputOptions) {
8
- extendManualChunks(outputOptions, {
9
- after(id, meta) {
10
- if (id.includes("astro/dist/runtime")) {
11
- return "astro";
12
- }
13
- const pageInfo = internals.pagesByViteID.get(id);
14
- let hasSharedModules = false;
15
- if (pageInfo) {
16
- if (getPrerenderMetadata(meta.getModuleInfo(id))) {
17
- const infoMeta = meta.getModuleInfo(id);
18
- for (const moduleId of infoMeta.importedIds) {
19
- const moduleMeta = meta.getModuleInfo(moduleId);
20
- if (
21
- // a shared modules should be inside the `src/` folder, at least
22
- moduleMeta.id.startsWith(opts.settings.config.srcDir.pathname) && // and has at least two importers: the current page and something else
23
- moduleMeta.importers.length > 1
24
- ) {
25
- for (const importer of moduleMeta.importedIds) {
26
- if (importer !== id) {
27
- const importerModuleMeta = meta.getModuleInfo(importer);
28
- if (importerModuleMeta) {
29
- if (importerModuleMeta.id.includes("/pages")) {
30
- if (getPrerenderMetadata(importerModuleMeta) === false) {
31
- hasSharedModules = true;
32
- break;
33
- }
34
- } else if (importerModuleMeta.id.includes("/middleware")) {
35
- hasSharedModules = true;
36
- break;
37
- }
38
- }
39
- }
40
- }
41
- }
42
- }
43
- pageInfo.hasSharedModules = hasSharedModules;
44
- pageInfo.route.prerender = true;
45
- return "prerender";
46
- }
47
- pageInfo.route.prerender = false;
48
- return `pages/${path.basename(pageInfo.component)}`;
49
- }
50
- }
7
+ generateBundle(_, bundle) {
8
+ const moduleIds = this.getModuleIds();
9
+ for (const id of moduleIds) {
10
+ const pageInfo = internals.pagesByViteID.get(id);
11
+ if (!pageInfo) continue;
12
+ const moduleInfo = this.getModuleInfo(id);
13
+ if (!moduleInfo) continue;
14
+ const prerender = !!getPrerenderMetadata(moduleInfo);
15
+ pageInfo.route.prerender = prerender;
16
+ }
17
+ const nonPrerenderOnlyChunks = getNonPrerenderOnlyChunks(bundle, internals);
18
+ internals.prerenderOnlyChunks = Object.values(bundle).filter((chunk) => {
19
+ return chunk.type === "chunk" && !nonPrerenderOnlyChunks.has(chunk);
51
20
  });
52
21
  }
53
22
  };
54
23
  }
24
+ function getNonPrerenderOnlyChunks(bundle, internals) {
25
+ const chunks = Object.values(bundle);
26
+ const prerenderOnlyEntryChunks = /* @__PURE__ */ new Set();
27
+ const nonPrerenderOnlyEntryChunks = /* @__PURE__ */ new Set();
28
+ for (const chunk of chunks) {
29
+ if (chunk.type === "chunk" && (chunk.isEntry || chunk.isDynamicEntry)) {
30
+ if (chunk.facadeModuleId?.startsWith(ASTRO_PAGE_RESOLVED_MODULE_ID)) {
31
+ const pageDatas = getPagesFromVirtualModulePageName(
32
+ internals,
33
+ ASTRO_PAGE_RESOLVED_MODULE_ID,
34
+ chunk.facadeModuleId
35
+ );
36
+ const prerender = pageDatas.every((pageData) => pageData.route.prerender);
37
+ if (prerender) {
38
+ prerenderOnlyEntryChunks.add(chunk);
39
+ continue;
40
+ }
41
+ }
42
+ nonPrerenderOnlyEntryChunks.add(chunk);
43
+ }
44
+ }
45
+ const nonPrerenderOnlyChunks = new Set(nonPrerenderOnlyEntryChunks);
46
+ for (const chunk of nonPrerenderOnlyChunks) {
47
+ for (const importFileName of chunk.imports) {
48
+ const importChunk = bundle[importFileName];
49
+ if (importChunk?.type === "chunk") {
50
+ nonPrerenderOnlyChunks.add(importChunk);
51
+ }
52
+ }
53
+ for (const dynamicImportFileName of chunk.dynamicImports) {
54
+ const dynamicImportChunk = bundle[dynamicImportFileName];
55
+ if (dynamicImportChunk?.type === "chunk" && !prerenderOnlyEntryChunks.has(dynamicImportChunk)) {
56
+ nonPrerenderOnlyChunks.add(dynamicImportChunk);
57
+ }
58
+ }
59
+ }
60
+ return nonPrerenderOnlyChunks;
61
+ }
55
62
  function pluginPrerender(opts, internals) {
56
63
  if (opts.settings.config.output === "static") {
57
64
  return { targets: ["server"] };
@@ -61,7 +68,7 @@ function pluginPrerender(opts, internals) {
61
68
  hooks: {
62
69
  "build:before": () => {
63
70
  return {
64
- vitePlugin: vitePluginPrerender(opts, internals)
71
+ vitePlugin: vitePluginPrerender(internals)
65
72
  };
66
73
  }
67
74
  }
@@ -16,7 +16,15 @@ function vitePluginSSR(internals, adapter, options) {
16
16
  name: "@astrojs/vite-plugin-astro-ssr-server",
17
17
  enforce: "post",
18
18
  options(opts) {
19
- return addRollupInput(opts, [SSR_VIRTUAL_MODULE_ID]);
19
+ const inputs = /* @__PURE__ */ new Set();
20
+ for (const pageData of Object.values(options.allPages)) {
21
+ if (routeIsRedirect(pageData.route)) {
22
+ continue;
23
+ }
24
+ inputs.add(getVirtualModulePageName(ASTRO_PAGE_MODULE_ID, pageData.component));
25
+ }
26
+ inputs.add(SSR_VIRTUAL_MODULE_ID);
27
+ return addRollupInput(opts, Array.from(inputs));
20
28
  },
21
29
  resolveId(id) {
22
30
  if (id === SSR_VIRTUAL_MODULE_ID) {
@@ -60,7 +68,6 @@ function vitePluginSSR(internals, adapter, options) {
60
68
  contents.push(...ssrCode.contents);
61
69
  return [...imports, ...contents, ...exports].join("\n");
62
70
  }
63
- return void 0;
64
71
  },
65
72
  async generateBundle(_opts, bundle) {
66
73
  for (const [, chunk] of Object.entries(bundle)) {
@@ -110,21 +117,18 @@ function pluginSSR(options, internals) {
110
117
  const SPLIT_MODULE_ID = "@astro-page-split:";
111
118
  const RESOLVED_SPLIT_MODULE_ID = "\0@astro-page-split:";
112
119
  function vitePluginSSRSplit(internals, adapter, options) {
113
- const functionPerRouteEnabled = isFunctionPerRouteEnabled(options.settings.adapter);
114
120
  return {
115
121
  name: "@astrojs/vite-plugin-astro-ssr-split",
116
122
  enforce: "post",
117
123
  options(opts) {
118
- if (functionPerRouteEnabled) {
119
- const inputs = /* @__PURE__ */ new Set();
120
- for (const pageData of Object.values(options.allPages)) {
121
- if (routeIsRedirect(pageData.route)) {
122
- continue;
123
- }
124
- inputs.add(getVirtualModulePageName(SPLIT_MODULE_ID, pageData.component));
124
+ const inputs = /* @__PURE__ */ new Set();
125
+ for (const pageData of Object.values(options.allPages)) {
126
+ if (routeIsRedirect(pageData.route)) {
127
+ continue;
125
128
  }
126
- return addRollupInput(opts, Array.from(inputs));
129
+ inputs.add(getVirtualModulePageName(SPLIT_MODULE_ID, pageData.component));
127
130
  }
131
+ return addRollupInput(opts, Array.from(inputs));
128
132
  },
129
133
  resolveId(id) {
130
134
  if (id.startsWith(SPLIT_MODULE_ID)) {
@@ -149,7 +153,6 @@ function vitePluginSSRSplit(internals, adapter, options) {
149
153
  exports.push("export { pageModule }");
150
154
  return [...imports, ...contents, ...exports].join("\n");
151
155
  }
152
- return void 0;
153
156
  },
154
157
  async generateBundle(_opts, bundle) {
155
158
  for (const [, chunk] of Object.entries(bundle)) {
@@ -2,12 +2,15 @@ import fs from "node:fs";
2
2
  import path, { extname } from "node:path";
3
3
  import { fileURLToPath, pathToFileURL } from "node:url";
4
4
  import { teardown } from "@astrojs/compiler";
5
- import * as eslexer from "es-module-lexer";
6
5
  import glob from "fast-glob";
7
6
  import { bgGreen, bgMagenta, black, green } from "kleur/colors";
8
7
  import * as vite from "vite";
9
8
  import { PROPAGATED_ASSET_FLAG } from "../../content/consts.js";
10
- import { hasAnyContentFlag } from "../../content/utils.js";
9
+ import {
10
+ getSymlinkedContentCollections,
11
+ hasAnyContentFlag,
12
+ reverseSymlink
13
+ } from "../../content/utils.js";
11
14
  import {
12
15
  createBuildInternals,
13
16
  getPageDatasWithPublicKey
@@ -34,7 +37,7 @@ import { RESOLVED_SPLIT_MODULE_ID, RESOLVED_SSR_VIRTUAL_MODULE_ID } from "./plug
34
37
  import { ASTRO_PAGE_EXTENSION_POST_PATTERN } from "./plugins/util.js";
35
38
  import { encodeName, getTimeStat, viteBuildReturnToRollupOutputs } from "./util.js";
36
39
  async function viteBuild(opts) {
37
- const { allPages, settings } = opts;
40
+ const { allPages, settings, logger } = opts;
38
41
  if (isModeServerWithNoAdapter(opts.settings)) {
39
42
  throw new AstroError(AstroErrorData.NoAdapterInstalled);
40
43
  }
@@ -56,7 +59,7 @@ async function viteBuild(opts) {
56
59
  registerAllPlugins(container);
57
60
  const ssrTime = performance.now();
58
61
  opts.logger.info("build", `Building ${settings.config.output} entrypoints...`);
59
- const ssrOutput = await ssrBuild(opts, internals, pageInput, container);
62
+ const ssrOutput = await ssrBuild(opts, internals, pageInput, container, logger);
60
63
  opts.logger.info("build", green(`\u2713 Completed in ${getTimeStat(ssrTime, performance.now())}.`));
61
64
  settings.timer.end("SSR build");
62
65
  settings.timer.start("Client build");
@@ -107,7 +110,7 @@ async function staticBuild(opts, internals, ssrOutputChunkNames, contentFileName
107
110
  case isServerLikeOutput(settings.config): {
108
111
  settings.timer.start("Server generate");
109
112
  await generatePages(opts, internals);
110
- await cleanStaticOutput(opts, internals, ssrOutputChunkNames);
113
+ await cleanStaticOutput(opts, internals);
111
114
  opts.logger.info(null, `
112
115
  ${bgMagenta(black(" finalizing server assets "))}
113
116
  `);
@@ -119,7 +122,7 @@ ${bgMagenta(black(" finalizing server assets "))}
119
122
  return;
120
123
  }
121
124
  }
122
- async function ssrBuild(opts, internals, input, container) {
125
+ async function ssrBuild(opts, internals, input, container, logger) {
123
126
  const buildID = Date.now().toString();
124
127
  const { allPages, settings, viteConfig } = opts;
125
128
  const ssr = isServerLikeOutput(settings.config);
@@ -127,6 +130,8 @@ async function ssrBuild(opts, internals, input, container) {
127
130
  const routes = Object.values(allPages).flatMap((pageData) => pageData.route);
128
131
  const isContentCache = !ssr && settings.config.experimental.contentCollectionCache;
129
132
  const { lastVitePlugins, vitePlugins } = await container.runBeforeHook("server", input);
133
+ const contentDir = new URL("./src/content", settings.config.root);
134
+ const symlinks = await getSymlinkedContentCollections({ contentDir, logger, fs });
130
135
  const viteBuildConfig = {
131
136
  ...viteConfig,
132
137
  mode: viteConfig.mode || "production",
@@ -143,6 +148,8 @@ async function ssrBuild(opts, internals, input, container) {
143
148
  copyPublicDir: !ssr,
144
149
  rollupOptions: {
145
150
  ...viteConfig.build?.rollupOptions,
151
+ // Setting as `exports-only` allows us to safely delete inputs that are only used during prerendering
152
+ preserveEntrySignatures: "exports-only",
146
153
  input: [],
147
154
  output: {
148
155
  hoistTransitiveImports: isContentCache,
@@ -193,7 +200,12 @@ async function ssrBuild(opts, internals, input, container) {
193
200
  } else if (chunkInfo.facadeModuleId === RESOLVED_SSR_MANIFEST_VIRTUAL_MODULE_ID) {
194
201
  return "manifest_[hash].mjs";
195
202
  } else if (settings.config.experimental.contentCollectionCache && chunkInfo.facadeModuleId && hasAnyContentFlag(chunkInfo.facadeModuleId)) {
196
- const [srcRelative, flag] = chunkInfo.facadeModuleId.split("/src/")[1].split("?");
203
+ const moduleId = reverseSymlink({
204
+ symlinks,
205
+ entry: chunkInfo.facadeModuleId,
206
+ contentDir
207
+ });
208
+ const [srcRelative, flag] = moduleId.split("/src/")[1].split("?");
197
209
  if (flag === PROPAGATED_ASSET_FLAG) {
198
210
  return encodeName(`${removeFileExtension(srcRelative)}.entry.mjs`);
199
211
  }
@@ -285,46 +297,26 @@ async function runPostBuildHooks(container, ssrOutputs, clientOutputs) {
285
297
  await fs.promises.writeFile(fileURL, mutation.code, "utf-8");
286
298
  }
287
299
  }
288
- async function cleanStaticOutput(opts, internals, ssrOutputChunkNames) {
289
- const prerenderedFiles = /* @__PURE__ */ new Set();
290
- const onDemandsFiles = /* @__PURE__ */ new Set();
291
- for (const pageData of internals.pagesByKeys.values()) {
292
- const { moduleSpecifier } = pageData;
293
- const bundleId = internals.pageToBundleMap.get(moduleSpecifier) ?? internals.entrySpecifierToBundleMap.get(moduleSpecifier);
294
- if (pageData.route.prerender && !pageData.hasSharedModules && !onDemandsFiles.has(bundleId)) {
295
- prerenderedFiles.add(bundleId);
296
- } else {
297
- onDemandsFiles.add(bundleId);
298
- if (prerenderedFiles.has(bundleId)) {
299
- prerenderedFiles.delete(bundleId);
300
- }
301
- }
302
- }
300
+ async function cleanStaticOutput(opts, internals) {
303
301
  const ssr = isServerLikeOutput(opts.settings.config);
304
302
  const out = ssr ? opts.settings.config.build.server : getOutDirWithinCwd(opts.settings.config.outDir);
305
- const files = ssrOutputChunkNames.filter((f) => f.endsWith(".mjs"));
306
- if (files.length) {
307
- await eslexer.init;
308
- await Promise.all(
309
- files.map(async (filename) => {
310
- if (!prerenderedFiles.has(filename)) {
311
- return;
312
- }
313
- const url = new URL(filename, out);
314
- const text = await fs.promises.readFile(url, { encoding: "utf8" });
315
- const [, exports] = eslexer.parse(text);
316
- let value = "const noop = () => {};";
317
- for (const e of exports) {
318
- if (e.n === "default") value += `
319
- export default noop;`;
320
- else value += `
321
- export const ${e.n} = noop;`;
303
+ await Promise.all(
304
+ internals.prerenderOnlyChunks.map(async (chunk) => {
305
+ const url = new URL(chunk.fileName, out);
306
+ try {
307
+ if (chunk.isEntry || chunk.isDynamicEntry) {
308
+ await fs.promises.writeFile(
309
+ url,
310
+ "// Contents removed by Astro as it's used for prerendering only",
311
+ "utf-8"
312
+ );
313
+ } else {
314
+ await fs.promises.unlink(url);
322
315
  }
323
- await fs.promises.writeFile(url, value, { encoding: "utf8" });
324
- })
325
- );
326
- removeEmptyDirs(out);
327
- }
316
+ } catch {
317
+ }
318
+ })
319
+ );
328
320
  }
329
321
  async function cleanServerOutput(opts, ssrOutputChunkNames, contentFileNames, internals) {
330
322
  const out = getOutDirWithinCwd(opts.settings.config.outDir);
@@ -27,7 +27,6 @@ export interface PageBuildData {
27
27
  order: number;
28
28
  sheet: StylesheetAsset;
29
29
  }>;
30
- hasSharedModules: boolean;
31
30
  }
32
31
  export type AllPagesData = Record<ComponentPath, PageBuildData>;
33
32
  /** Options for the static build */