astro 7.1.5 → 7.1.6

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.
@@ -1,6 +1,6 @@
1
1
  class BuildTimeAstroVersionProvider {
2
2
  // Injected during the build through esbuild define
3
- version = "7.1.5";
3
+ version = "7.1.6";
4
4
  }
5
5
  export {
6
6
  BuildTimeAstroVersionProvider
@@ -28,3 +28,7 @@ export declare const COLLECTIONS_MANIFEST_FILE = "collections/collections.json";
28
28
  export declare const COLLECTIONS_DIR = "collections/";
29
29
  export declare const CONTENT_LAYER_TYPE = "content_layer";
30
30
  export declare const LIVE_CONTENT_TYPE = "live";
31
+ export declare const DATA_STORE_CHUNK_VIRTUAL_ID_PREFIX = "astro:data-layer-content-chunk:";
32
+ export declare const RESOLVED_DATA_STORE_CHUNK_VIRTUAL_ID_PREFIX = "\0astro:data-layer-content-chunk:";
33
+ export declare const RESOLVED_DATA_STORE_CHUNK_VIRTUAL_ID_SUFFIX = ".mjs";
34
+ export declare const DATA_STORE_CHUNK_FILE_NAME_PATTERN: RegExp;
@@ -35,6 +35,10 @@ const COLLECTIONS_MANIFEST_FILE = "collections/collections.json";
35
35
  const COLLECTIONS_DIR = "collections/";
36
36
  const CONTENT_LAYER_TYPE = "content_layer";
37
37
  const LIVE_CONTENT_TYPE = "live";
38
+ const DATA_STORE_CHUNK_VIRTUAL_ID_PREFIX = `${DATA_STORE_VIRTUAL_ID}-chunk:`;
39
+ const RESOLVED_DATA_STORE_CHUNK_VIRTUAL_ID_PREFIX = `\0${DATA_STORE_CHUNK_VIRTUAL_ID_PREFIX}`;
40
+ const RESOLVED_DATA_STORE_CHUNK_VIRTUAL_ID_SUFFIX = ".mjs";
41
+ const DATA_STORE_CHUNK_FILE_NAME_PATTERN = /^[a-f0-9]{16}\.txt$/;
38
42
  export {
39
43
  ASSET_IMPORTS_FILE,
40
44
  ASSET_IMPORTS_RESOLVED_STUB_ID,
@@ -49,6 +53,8 @@ export {
49
53
  CONTENT_RENDER_FLAG,
50
54
  CONTENT_TYPES_FILE,
51
55
  DATA_FLAG,
56
+ DATA_STORE_CHUNK_FILE_NAME_PATTERN,
57
+ DATA_STORE_CHUNK_VIRTUAL_ID_PREFIX,
52
58
  DATA_STORE_DIR,
53
59
  DATA_STORE_FILE,
54
60
  DATA_STORE_MANIFEST_FILE,
@@ -62,6 +68,8 @@ export {
62
68
  MODULES_MJS_VIRTUAL_ID,
63
69
  PROPAGATED_ASSET_FLAG,
64
70
  PROPAGATED_ASSET_QUERY_PARAM,
71
+ RESOLVED_DATA_STORE_CHUNK_VIRTUAL_ID_PREFIX,
72
+ RESOLVED_DATA_STORE_CHUNK_VIRTUAL_ID_SUFFIX,
65
73
  RESOLVED_DATA_STORE_VIRTUAL_ID,
66
74
  RESOLVED_VIRTUAL_MODULE_ID,
67
75
  STYLES_PLACEHOLDER,
@@ -196,7 +196,7 @@ ${contentConfig.error.message}`
196
196
  logger.info("Content config changed");
197
197
  shouldClear = true;
198
198
  }
199
- if (previousAstroVersion && previousAstroVersion !== "7.1.5") {
199
+ if (previousAstroVersion && previousAstroVersion !== "7.1.6") {
200
200
  logger.info("Astro version changed");
201
201
  shouldClear = true;
202
202
  }
@@ -204,8 +204,8 @@ ${contentConfig.error.message}`
204
204
  logger.info("Clearing content store");
205
205
  this.#store.clearAll();
206
206
  }
207
- if ("7.1.5") {
208
- this.#store.metaStore().set("astro-version", "7.1.5");
207
+ if ("7.1.6") {
208
+ this.#store.metaStore().set("astro-version", "7.1.6");
209
209
  }
210
210
  if (currentConfigDigest) {
211
211
  this.#store.metaStore().set("content-config-digest", currentConfigDigest);
@@ -62,6 +62,6 @@ export declare class FileWriter implements DataStoreWriter {
62
62
  */
63
63
  export declare class ChunkedWriter implements DataStoreWriter {
64
64
  #private;
65
- constructor(dir: URL);
65
+ constructor(dir: URL, chunkSize: number);
66
66
  write(collections: Map<string, Map<string, any>>): Promise<void>;
67
67
  }
@@ -3,7 +3,6 @@ import * as devalue from "devalue";
3
3
  import xxhash, {} from "xxhash-wasm";
4
4
  import { emptyDir } from "../core/fs/index.js";
5
5
  import { DATA_STORE_MANIFEST_FILE } from "./consts.js";
6
- const CHUNK_SIZE_LIMIT = 20 * 1024 * 1024;
7
6
  function sortCollections(collections) {
8
7
  return new Map(
9
8
  [...collections.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([key, collection]) => [
@@ -57,10 +56,12 @@ class FileWriter {
57
56
  class ChunkedWriter {
58
57
  #dir;
59
58
  #manifestFile;
59
+ #chunkSize;
60
60
  #hasher;
61
- constructor(dir) {
61
+ constructor(dir, chunkSize) {
62
62
  this.#dir = dir;
63
63
  this.#manifestFile = new URL(`./${DATA_STORE_MANIFEST_FILE}`, dir);
64
+ this.#chunkSize = chunkSize;
64
65
  }
65
66
  async write(collections) {
66
67
  if (!this.#hasher) {
@@ -72,7 +73,7 @@ class ChunkedWriter {
72
73
  for (const [collectionName, entries] of sortCollections(collections)) {
73
74
  const stringified = devalue.stringify(entries);
74
75
  const parts = [];
75
- for (const part of chunkString(stringified, CHUNK_SIZE_LIMIT)) {
76
+ for (const part of chunkString(stringified, this.#chunkSize)) {
76
77
  const fileName = `${h64ToString(part)}.txt`;
77
78
  await writeFileAtomic(new URL(`./${fileName}`, this.#dir), part);
78
79
  parts.push(fileName);
@@ -51,9 +51,9 @@ export declare class ImmutableDataStore {
51
51
  * names have already been swapped for their contents.
52
52
  *
53
53
  * Each collection maps to a list of parts. A part is either a raw string
54
- * (when the store is loaded from disk) or an ESM namespace from a `?raw`
55
- * import (`{ default: string }`, when emitted into the virtual module at
56
- * runtime). A collection's parts are concatenated back into the exact
54
+ * (when the store is loaded from disk) or an ESM namespace from a virtual
55
+ * chunk import (`{ default: string }`, when emitted at runtime). A collection's
56
+ * parts are concatenated back into the exact
57
57
  * serialized string, then parsed with devalue. This is the inverse of
58
58
  * {@link import('./data-store-writer.js').ChunkedWriter} and stays free of
59
59
  * Node built-ins so it can run at runtime.
@@ -38,9 +38,9 @@ class ImmutableDataStore {
38
38
  * names have already been swapped for their contents.
39
39
  *
40
40
  * Each collection maps to a list of parts. A part is either a raw string
41
- * (when the store is loaded from disk) or an ESM namespace from a `?raw`
42
- * import (`{ default: string }`, when emitted into the virtual module at
43
- * runtime). A collection's parts are concatenated back into the exact
41
+ * (when the store is loaded from disk) or an ESM namespace from a virtual
42
+ * chunk import (`{ default: string }`, when emitted at runtime). A collection's
43
+ * parts are concatenated back into the exact
44
44
  * serialized string, then parsed with devalue. This is the inverse of
45
45
  * {@link import('./data-store-writer.js').ChunkedWriter} and stays free of
46
46
  * Node built-ins so it can run at runtime.
@@ -36,13 +36,13 @@ export declare class MutableDataStore extends ImmutableDataStore {
36
36
  static fromString(data: string): Promise<MutableDataStore>;
37
37
  static fromFile(filePath: string | URL): Promise<MutableDataStore>;
38
38
  /**
39
- * Loads a MutableDataStore from a chunked store directory (experimental
40
- * `collectionStorage: 'chunked'`), reading the manifest and its referenced parts.
39
+ * Loads a MutableDataStore from a chunked store directory, reading the manifest
40
+ * and its referenced parts.
41
41
  * If the directory has no manifest yet (fresh build) it starts empty. If the
42
42
  * manifest exists but can't be read (corrupt cache), it warns and starts
43
43
  * empty so loaders rebuild it, rather than failing the sync.
44
44
  */
45
- static fromDir(dirPath: URL): Promise<MutableDataStore>;
45
+ static fromDir(dirPath: URL, chunkSize: number): Promise<MutableDataStore>;
46
46
  }
47
47
  export interface DataStore {
48
48
  get: <TData extends Record<string, unknown> = Record<string, unknown>>(key: string) => DataEntry<TData> | undefined;
@@ -412,13 +412,13 @@ ${lines.join(",\n")}]);
412
412
  return store;
413
413
  }
414
414
  /**
415
- * Loads a MutableDataStore from a chunked store directory (experimental
416
- * `collectionStorage: 'chunked'`), reading the manifest and its referenced parts.
415
+ * Loads a MutableDataStore from a chunked store directory, reading the manifest
416
+ * and its referenced parts.
417
417
  * If the directory has no manifest yet (fresh build) it starts empty. If the
418
418
  * manifest exists but can't be read (corrupt cache), it warns and starts
419
419
  * empty so loaders rebuild it, rather than failing the sync.
420
420
  */
421
- static async fromDir(dirPath) {
421
+ static async fromDir(dirPath, chunkSize) {
422
422
  const manifestFile = new URL(`./${DATA_STORE_MANIFEST_FILE}`, dirPath);
423
423
  if (existsSync(manifestFile)) {
424
424
  try {
@@ -434,7 +434,7 @@ ${lines.join(",\n")}]);
434
434
  }
435
435
  const map = ImmutableDataStore.manifestToMap(expanded);
436
436
  const store2 = await MutableDataStore.fromMap(map);
437
- store2.#writer = new ChunkedWriter(dirPath);
437
+ store2.#writer = new ChunkedWriter(dirPath, chunkSize);
438
438
  return store2;
439
439
  } catch (err) {
440
440
  console.warn(
@@ -445,7 +445,7 @@ ${lines.join(",\n")}]);
445
445
  }
446
446
  await fs.mkdir(dirPath, { recursive: true });
447
447
  const store = new MutableDataStore();
448
- store.#writer = new ChunkedWriter(dirPath);
448
+ store.#writer = new ChunkedWriter(dirPath, chunkSize);
449
449
  return store;
450
450
  }
451
451
  }
@@ -5,9 +5,10 @@ import type { AstroSettings } from '../types/astro.js';
5
5
  * In production, it's in the cache directory so that it's preserved between builds.
6
6
  */
7
7
  export declare function getDataStoreFile(settings: AstroSettings, isDev: boolean): URL;
8
+ export declare function getDataStoreChunkSize(settings: AstroSettings): number | undefined;
8
9
  /**
9
10
  * Get the path to the data store directory, used when the store is split across
10
- * multiple files (experimental `collectionStorage: 'chunked'`).
11
+ * multiple files.
11
12
  * During development, this is in the `.astro` directory so that the Vite watcher can see it.
12
13
  * In production, it's in the cache directory so that it's preserved between builds.
13
14
  */
@@ -2,10 +2,21 @@ import { DATA_STORE_DIR, DATA_STORE_FILE } from "./consts.js";
2
2
  function getDataStoreFile(settings, isDev) {
3
3
  return new URL(DATA_STORE_FILE, isDev ? settings.dotAstroDir : settings.config.cacheDir);
4
4
  }
5
+ function getDataStoreChunkSize(settings) {
6
+ const storage = settings.config.experimental.collectionStorage;
7
+ if (storage === void 0 || storage === "single-file") {
8
+ return void 0;
9
+ }
10
+ if (storage === "chunked") {
11
+ return 20 * 1024 * 1024;
12
+ }
13
+ return storage.chunkSize;
14
+ }
5
15
  function getDataStoreDir(settings, isDev) {
6
16
  return new URL(DATA_STORE_DIR, isDev ? settings.dotAstroDir : settings.config.cacheDir);
7
17
  }
8
18
  export {
19
+ getDataStoreChunkSize,
9
20
  getDataStoreDir,
10
21
  getDataStoreFile
11
22
  };
@@ -13,16 +13,20 @@ import {
13
13
  ASSET_IMPORTS_VIRTUAL_ID,
14
14
  CONTENT_MODULE_FLAG,
15
15
  CONTENT_RENDER_FLAG,
16
+ DATA_STORE_CHUNK_FILE_NAME_PATTERN,
17
+ DATA_STORE_CHUNK_VIRTUAL_ID_PREFIX,
16
18
  DATA_STORE_MANIFEST_FILE,
17
19
  DATA_STORE_VIRTUAL_ID,
18
20
  MODULES_IMPORTS_FILE,
19
21
  MODULES_MJS_ID,
20
22
  MODULES_MJS_VIRTUAL_ID,
23
+ RESOLVED_DATA_STORE_CHUNK_VIRTUAL_ID_PREFIX,
24
+ RESOLVED_DATA_STORE_CHUNK_VIRTUAL_ID_SUFFIX,
21
25
  RESOLVED_DATA_STORE_VIRTUAL_ID,
22
26
  RESOLVED_VIRTUAL_MODULE_ID,
23
27
  VIRTUAL_MODULE_ID
24
28
  } from "./consts.js";
25
- import { getDataStoreDir, getDataStoreFile } from "./paths.js";
29
+ import { getDataStoreChunkSize, getDataStoreDir, getDataStoreFile } from "./paths.js";
26
30
  import { getContentPaths, isDeferredModule } from "./utils.js";
27
31
  const LARGE_DATA_STORE_THRESHOLD = 5 * 1024 * 1024;
28
32
  function invalidateAssetImports(viteServer, filePath) {
@@ -81,11 +85,11 @@ function astroContentVirtualModPlugin({
81
85
  enforce: "pre",
82
86
  config(_, env) {
83
87
  isDev = env.command === "serve";
84
- if (settings.config.experimental.collectionStorage === "chunked") {
85
- dataStoreDir = getDataStoreDir(settings, env.command === "serve");
88
+ dataStoreDir = getDataStoreDir(settings, isDev);
89
+ if (getDataStoreChunkSize(settings) !== void 0) {
86
90
  dataStoreFile = new URL(DATA_STORE_MANIFEST_FILE, dataStoreDir);
87
91
  } else {
88
- dataStoreFile = getDataStoreFile(settings, env.command === "serve");
92
+ dataStoreFile = getDataStoreFile(settings, isDev);
89
93
  }
90
94
  const contentPaths = getContentPaths(
91
95
  settings.config,
@@ -108,7 +112,7 @@ function astroContentVirtualModPlugin({
108
112
  resolveId: {
109
113
  filter: {
110
114
  id: new RegExp(
111
- `^(${VIRTUAL_MODULE_ID}|${DATA_STORE_VIRTUAL_ID}|${MODULES_MJS_ID}|${ASSET_IMPORTS_VIRTUAL_ID})$|(?:\\?|&)${CONTENT_MODULE_FLAG}(?:&|=|$)`
115
+ `^(${VIRTUAL_MODULE_ID}|${DATA_STORE_VIRTUAL_ID}|${MODULES_MJS_ID}|${ASSET_IMPORTS_VIRTUAL_ID})$|^${DATA_STORE_CHUNK_VIRTUAL_ID_PREFIX}|(?:\\?|&)${CONTENT_MODULE_FLAG}(?:&|=|$)`
112
116
  )
113
117
  },
114
118
  async handler(id, importer) {
@@ -123,6 +127,13 @@ function astroContentVirtualModPlugin({
123
127
  if (id === DATA_STORE_VIRTUAL_ID) {
124
128
  return RESOLVED_DATA_STORE_VIRTUAL_ID;
125
129
  }
130
+ if (id.startsWith(DATA_STORE_CHUNK_VIRTUAL_ID_PREFIX)) {
131
+ const fileName = id.slice(DATA_STORE_CHUNK_VIRTUAL_ID_PREFIX.length);
132
+ if (!DATA_STORE_CHUNK_FILE_NAME_PATTERN.test(fileName)) {
133
+ this.error(`Invalid data-store chunk: ${fileName}`);
134
+ }
135
+ return `${RESOLVED_DATA_STORE_CHUNK_VIRTUAL_ID_PREFIX}${fileName}${RESOLVED_DATA_STORE_CHUNK_VIRTUAL_ID_SUFFIX}`;
136
+ }
126
137
  if (isDeferredModule(id)) {
127
138
  const [, query] = id.split("?");
128
139
  const params = new URLSearchParams(query);
@@ -154,10 +165,31 @@ function astroContentVirtualModPlugin({
154
165
  load: {
155
166
  filter: {
156
167
  id: new RegExp(
157
- `^(${RESOLVED_VIRTUAL_MODULE_ID}|${RESOLVED_DATA_STORE_VIRTUAL_ID}|${ASSET_IMPORTS_RESOLVED_STUB_ID}|${MODULES_MJS_VIRTUAL_ID})$`
168
+ `^(${RESOLVED_VIRTUAL_MODULE_ID}|${RESOLVED_DATA_STORE_VIRTUAL_ID}|${ASSET_IMPORTS_RESOLVED_STUB_ID}|${MODULES_MJS_VIRTUAL_ID})$|^${RESOLVED_DATA_STORE_CHUNK_VIRTUAL_ID_PREFIX}`
158
169
  )
159
170
  },
160
171
  async handler(id) {
172
+ if (id.startsWith(RESOLVED_DATA_STORE_CHUNK_VIRTUAL_ID_PREFIX)) {
173
+ const resolvedFileName = id.slice(RESOLVED_DATA_STORE_CHUNK_VIRTUAL_ID_PREFIX.length);
174
+ if (!resolvedFileName.endsWith(RESOLVED_DATA_STORE_CHUNK_VIRTUAL_ID_SUFFIX)) {
175
+ this.error(`Invalid data-store chunk: ${resolvedFileName}`);
176
+ }
177
+ const fileName = resolvedFileName.slice(
178
+ 0,
179
+ -RESOLVED_DATA_STORE_CHUNK_VIRTUAL_ID_SUFFIX.length
180
+ );
181
+ if (!DATA_STORE_CHUNK_FILE_NAME_PATTERN.test(fileName)) {
182
+ this.error(`Invalid data-store chunk: ${fileName}`);
183
+ }
184
+ const contents = await fs.promises.readFile(
185
+ new URL(`./${fileName}`, dataStoreDir),
186
+ "utf-8"
187
+ );
188
+ return {
189
+ code: `export default ${JSON.stringify(contents)}`,
190
+ map: { mappings: "" }
191
+ };
192
+ }
161
193
  if (id === RESOLVED_VIRTUAL_MODULE_ID) {
162
194
  const isClient = isAstroClientEnvironment(this.environment);
163
195
  const code = await generateContentEntryFile({
@@ -179,18 +211,12 @@ function astroContentVirtualModPlugin({
179
211
  return { code: "export default new Map()" };
180
212
  }
181
213
  const jsonData = await fs.promises.readFile(dataStoreFile, "utf-8");
182
- if (settings.config.experimental.collectionStorage === "chunked") {
214
+ if (getDataStoreChunkSize(settings) !== void 0) {
183
215
  try {
184
216
  const manifest = JSON.parse(jsonData);
185
- const rawImport = (fileName) => {
186
- const path = rootRelativePath(
187
- settings.config.root,
188
- new URL(`./${fileName}`, dataStoreDir)
189
- );
190
- return `(await import(${JSON.stringify(`${path}?raw`)}))`;
191
- };
217
+ const chunkImport = (fileName) => `(await import(${JSON.stringify(`${DATA_STORE_CHUNK_VIRTUAL_ID_PREFIX}${fileName}`)}))`;
192
218
  const entries = Object.entries(manifest).map(
193
- ([collection, parts]) => `${JSON.stringify(collection)}:[${parts.map(rawImport).join(",")}]`
219
+ ([collection, parts]) => `${JSON.stringify(collection)}:[${parts.map(chunkImport).join(",")}]`
194
220
  );
195
221
  const code = `export default{${entries.join(",")}}`;
196
222
  return { code, map: { mappings: "" } };
@@ -69,7 +69,10 @@ ${colors.bgGreen(colors.black(` ${verb} static routes `))}`);
69
69
  const hasI18nDomains = ssr && options.settings.config.i18n?.domains && Object.keys(options.settings.config.i18n.domains).length > 0;
70
70
  const { config } = options.settings;
71
71
  const builtPaths = /* @__PURE__ */ new Set();
72
- const filteredPaths = pathsWithRoutes.filter(({ pathname, route }) => {
72
+ const filteredPaths = [];
73
+ const fallbackPaths = [];
74
+ for (const pathWithRoute of pathsWithRoutes) {
75
+ const { pathname, route } = pathWithRoute;
73
76
  if (hasI18nDomains && route.prerender) {
74
77
  throw new AstroError({
75
78
  ...AstroErrorData.NoPrerenderedRoutesWithDomains,
@@ -79,44 +82,44 @@ ${colors.bgGreen(colors.black(` ${verb} static routes `))}`);
79
82
  const normalized = removeTrailingForwardSlash(pathname);
80
83
  if (!builtPaths.has(normalized)) {
81
84
  builtPaths.add(normalized);
82
- return true;
83
- }
84
- const matchedRoute = matchRoute(decodeURI(pathname), options.routesList);
85
- if (!matchedRoute) {
86
- return false;
87
- }
88
- if (matchedRoute === route) {
89
- return true;
90
- }
91
- if (config.prerenderConflictBehavior === "error") {
92
- throw new AstroError({
93
- ...AstroErrorData.PrerenderRouteConflict,
94
- message: AstroErrorData.PrerenderRouteConflict.message(
95
- matchedRoute.route,
96
- route.route,
97
- normalized
98
- ),
99
- hint: AstroErrorData.PrerenderRouteConflict.hint(matchedRoute.route, route.route)
100
- });
101
- } else if (config.prerenderConflictBehavior === "warn") {
102
- const msg = AstroErrorData.PrerenderRouteConflict.message(
103
- matchedRoute.route,
104
- route.route,
105
- normalized
106
- );
107
- logger.warn("build", msg);
85
+ } else {
86
+ const matchedRoute = matchRoute(decodeURI(pathname), options.routesList);
87
+ if (!matchedRoute) {
88
+ continue;
89
+ }
90
+ if (matchedRoute !== route) {
91
+ if (config.prerenderConflictBehavior === "error") {
92
+ throw new AstroError({
93
+ ...AstroErrorData.PrerenderRouteConflict,
94
+ message: AstroErrorData.PrerenderRouteConflict.message(
95
+ matchedRoute.route,
96
+ route.route,
97
+ normalized
98
+ ),
99
+ hint: AstroErrorData.PrerenderRouteConflict.hint(matchedRoute.route, route.route)
100
+ });
101
+ } else if (config.prerenderConflictBehavior === "warn") {
102
+ const msg = AstroErrorData.PrerenderRouteConflict.message(
103
+ matchedRoute.route,
104
+ route.route,
105
+ normalized
106
+ );
107
+ logger.warn("build", msg);
108
+ }
109
+ continue;
110
+ }
108
111
  }
109
- return false;
110
- });
112
+ const paths = route.type === "fallback" ? fallbackPaths : filteredPaths;
113
+ paths.push(pathWithRoute);
114
+ }
115
+ const generationPhases = [filteredPaths, fallbackPaths];
111
116
  if (config.build.concurrency > 1) {
112
117
  const limit = PLimit(config.build.concurrency);
113
118
  const BATCH_SIZE = 1e5;
114
- for (let i = 0; i < filteredPaths.length; i += BATCH_SIZE) {
115
- const batch = filteredPaths.slice(i, i + BATCH_SIZE);
116
- const promises = [];
117
- for (const { pathname, route } of batch) {
118
- promises.push(
119
- limit(
119
+ for (const paths of generationPhases) {
120
+ for (let i = 0; i < paths.length; i += BATCH_SIZE) {
121
+ const promises = paths.slice(i, i + BATCH_SIZE).map(
122
+ ({ pathname, route }) => limit(
120
123
  () => generatePathWithPrerenderer(
121
124
  prerenderer,
122
125
  pathname,
@@ -127,27 +130,31 @@ ${colors.bgGreen(colors.black(` ${verb} static routes `))}`);
127
130
  )
128
131
  )
129
132
  );
133
+ await Promise.all(promises);
130
134
  }
131
- await Promise.all(promises);
132
135
  }
133
136
  } else {
134
- for (const { pathname, route } of filteredPaths) {
135
- await generatePathWithPrerenderer(
136
- prerenderer,
137
- pathname,
138
- route,
139
- options,
140
- routeToHeaders,
141
- logger
142
- );
137
+ for (const paths of generationPhases) {
138
+ for (const { pathname, route } of paths) {
139
+ await generatePathWithPrerenderer(
140
+ prerenderer,
141
+ pathname,
142
+ route,
143
+ options,
144
+ routeToHeaders,
145
+ logger
146
+ );
147
+ }
143
148
  }
144
149
  }
145
- for (const { route: generatedRoute } of filteredPaths) {
146
- if (generatedRoute.distURL && generatedRoute.distURL.length > 0) {
147
- for (const pageData of Object.values(options.allPages)) {
148
- if (pageData.route.route === generatedRoute.route && pageData.route.component === generatedRoute.component) {
149
- pageData.route.distURL = generatedRoute.distURL;
150
- break;
150
+ for (const paths of generationPhases) {
151
+ for (const { route: generatedRoute } of paths) {
152
+ if (generatedRoute.distURL && generatedRoute.distURL.length > 0) {
153
+ for (const pageData of Object.values(options.allPages)) {
154
+ if (pageData.route.route === generatedRoute.route && pageData.route.component === generatedRoute.component) {
155
+ pageData.route.distURL = generatedRoute.distURL;
156
+ break;
157
+ }
151
158
  }
152
159
  }
153
160
  }
@@ -488,10 +488,10 @@ export declare const AstroConfigSchema: z.ZodObject<{
488
488
  name: z.ZodString;
489
489
  optimize: z.ZodCustom<(contents: string) => string | Promise<string>, (contents: string) => string | Promise<string>>;
490
490
  }, z.core.$strip>>;
491
- collectionStorage: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
492
- "single-file": "single-file";
493
- chunked: "chunked";
494
- }>>>;
491
+ collectionStorage: z.ZodDefault<z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"single-file">, z.ZodLiteral<"chunked">, z.ZodObject<{
492
+ type: z.ZodLiteral<"chunked">;
493
+ chunkSize: z.ZodNumber;
494
+ }, z.core.$strict>]>>>;
495
495
  }, z.core.$strict>>;
496
496
  legacy: z.ZodPrefault<z.ZodObject<{
497
497
  collectionsBackwardsCompat: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
@@ -277,7 +277,14 @@ const AstroConfigSchema = z.object({
277
277
  contentIntellisense: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.contentIntellisense),
278
278
  chromeDevtoolsWorkspace: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.chromeDevtoolsWorkspace),
279
279
  svgOptimizer: SvgOptimizerSchema.optional(),
280
- collectionStorage: z.enum(["single-file", "chunked"]).optional().default(ASTRO_CONFIG_DEFAULTS.experimental.collectionStorage)
280
+ collectionStorage: z.union([
281
+ z.literal("single-file"),
282
+ z.literal("chunked"),
283
+ z.strictObject({
284
+ type: z.literal("chunked"),
285
+ chunkSize: z.number().int().positive()
286
+ })
287
+ ]).optional().default(ASTRO_CONFIG_DEFAULTS.experimental.collectionStorage)
281
288
  }).prefault({}),
282
289
  legacy: z.object({
283
290
  collectionsBackwardsCompat: z.boolean().optional().default(false)
@@ -440,10 +440,10 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
440
440
  name: z.ZodString;
441
441
  optimize: z.ZodCustom<(contents: string) => string | Promise<string>, (contents: string) => string | Promise<string>>;
442
442
  }, z.core.$strip>>;
443
- collectionStorage: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
444
- "single-file": "single-file";
445
- chunked: "chunked";
446
- }>>>;
443
+ collectionStorage: z.ZodDefault<z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"single-file">, z.ZodLiteral<"chunked">, z.ZodObject<{
444
+ type: z.ZodLiteral<"chunked">;
445
+ chunkSize: z.ZodNumber;
446
+ }, z.core.$strict>]>>>;
447
447
  }, z.core.$strict>>;
448
448
  legacy: z.ZodPrefault<z.ZodObject<{
449
449
  collectionsBackwardsCompat: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
@@ -630,7 +630,10 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
630
630
  clientPrerender: boolean;
631
631
  contentIntellisense: boolean;
632
632
  chromeDevtoolsWorkspace: boolean;
633
- collectionStorage: "single-file" | "chunked";
633
+ collectionStorage: "single-file" | "chunked" | {
634
+ type: "chunked";
635
+ chunkSize: number;
636
+ };
634
637
  svgOptimizer?: {
635
638
  name: string;
636
639
  optimize: (contents: string) => string | Promise<string>;
@@ -893,7 +896,10 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
893
896
  clientPrerender: boolean;
894
897
  contentIntellisense: boolean;
895
898
  chromeDevtoolsWorkspace: boolean;
896
- collectionStorage: "single-file" | "chunked";
899
+ collectionStorage: "single-file" | "chunked" | {
900
+ type: "chunked";
901
+ chunkSize: number;
902
+ };
897
903
  svgOptimizer?: {
898
904
  name: string;
899
905
  optimize: (contents: string) => string | Promise<string>;
@@ -1,4 +1,4 @@
1
- const ASTRO_VERSION = "7.1.5";
1
+ const ASTRO_VERSION = "7.1.6";
2
2
  const ASTRO_GENERATOR = `Astro v${ASTRO_VERSION}`;
3
3
  const ASTRO_ERROR_HEADER = "X-Astro-Error";
4
4
  const DEFAULT_404_COMPONENT = "astro-default-404.astro";
@@ -2,7 +2,7 @@ import fs from "node:fs";
2
2
  import { performance } from "node:perf_hooks";
3
3
  import colors from "piccolore";
4
4
  import { gt, major, minor, patch } from "semver";
5
- import { getDataStoreDir, getDataStoreFile } from "../../content/paths.js";
5
+ import { getDataStoreChunkSize, getDataStoreDir, getDataStoreFile } from "../../content/paths.js";
6
6
  import { globalContentLayer } from "../../content/instance.js";
7
7
  import { attachContentServerListeners } from "../../content/index.js";
8
8
  import { MutableDataStore } from "../../content/mutable-data-store.js";
@@ -26,7 +26,7 @@ async function dev(inlineConfig) {
26
26
  await telemetry.record([]);
27
27
  const restart = await createContainerWithAutomaticRestart({ inlineConfig, fs });
28
28
  const logger = restart.container.logger;
29
- const currentVersion = "7.1.5";
29
+ const currentVersion = "7.1.6";
30
30
  const isPrerelease = currentVersion.includes("-");
31
31
  if (!isPrerelease) {
32
32
  try {
@@ -56,9 +56,10 @@ async function dev(inlineConfig) {
56
56
  }
57
57
  let store;
58
58
  try {
59
- if (restart.container.settings.config.experimental.collectionStorage === "chunked") {
59
+ const chunkSize = getDataStoreChunkSize(restart.container.settings);
60
+ if (chunkSize !== void 0) {
60
61
  const dataStoreDir = getDataStoreDir(restart.container.settings, true);
61
- store = await MutableDataStore.fromDir(dataStoreDir);
62
+ store = await MutableDataStore.fromDir(dataStoreDir, chunkSize);
62
63
  } else {
63
64
  const dataStoreFile = getDataStoreFile(restart.container.settings, true);
64
65
  store = await MutableDataStore.fromFile(dataStoreFile);
@@ -270,7 +270,7 @@ function printHelp({
270
270
  message.push(
271
271
  linebreak(),
272
272
  ` ${bgGreen(black(` ${commandName} `))} ${green(
273
- `v${"7.1.5"}`
273
+ `v${"7.1.6"}`
274
274
  )} ${headline}`
275
275
  );
276
276
  }
@@ -6,7 +6,7 @@ import colors from "piccolore";
6
6
  import { createServer } from "vite";
7
7
  import { syncFonts } from "../../assets/fonts/sync.js";
8
8
  import { CONTENT_TYPES_FILE } from "../../content/consts.js";
9
- import { getDataStoreDir, getDataStoreFile } from "../../content/paths.js";
9
+ import { getDataStoreChunkSize, getDataStoreDir, getDataStoreFile } from "../../content/paths.js";
10
10
  import { globalContentLayer } from "../../content/instance.js";
11
11
  import { createContentTypesGenerator } from "../../content/index.js";
12
12
  import { MutableDataStore } from "../../content/mutable-data-store.js";
@@ -59,7 +59,7 @@ async function clearContentLayerCache({
59
59
  fs = fsMod,
60
60
  isDev
61
61
  }) {
62
- if (settings.config.experimental.collectionStorage === "chunked") {
62
+ if (getDataStoreChunkSize(settings) !== void 0) {
63
63
  const dataStore = getDataStoreDir(settings, isDev);
64
64
  if (fs.existsSync(dataStore)) {
65
65
  logger.debug("content", "clearing data store");
@@ -98,9 +98,10 @@ async function syncInternal({
98
98
  settings.timer.start("Sync content layer");
99
99
  let store;
100
100
  try {
101
- if (settings.config.experimental.collectionStorage === "chunked") {
101
+ const chunkSize = getDataStoreChunkSize(settings);
102
+ if (chunkSize !== void 0) {
102
103
  const dataStoreDir = getDataStoreDir(settings, isDev);
103
- store = await MutableDataStore.fromDir(dataStoreDir);
104
+ store = await MutableDataStore.fromDir(dataStoreDir, chunkSize);
104
105
  } else {
105
106
  const dataStoreFile = getDataStoreFile(settings, isDev);
106
107
  store = await MutableDataStore.fromFile(dataStoreFile);
@@ -3128,7 +3128,7 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
3128
3128
  svgOptimizer?: SvgOptimizer;
3129
3129
  /**
3130
3130
  * @name experimental.collectionStorage
3131
- * @type {'single-file' | 'chunked'}
3131
+ * @type {'single-file' | 'chunked' | { type: 'chunked', chunkSize: number }}
3132
3132
  * @default `'single-file'`
3133
3133
  * @version 7.1.0
3134
3134
  * @description
@@ -3139,22 +3139,31 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
3139
3139
  * file. For very large content collections, this file can grow large
3140
3140
  * enough to hit platform file-size limits.
3141
3141
  *
3142
- * When set to `'chunked'`, the store is split across many smaller,
3143
- * content-addressed files so that no single file grows unbounded.
3142
+ * When set to `'chunked'`, the store is split into files
3143
+ * with a maximum size of 20 MiB each. To customize the maximum size,
3144
+ * pass an object with `type: 'chunked'` and `chunkSize`. A 1 MiB limit
3145
+ * is recommended for broad adapter compatibility.
3144
3146
  *
3145
3147
  * ```js
3146
3148
  * import { defineConfig } from 'astro/config';
3147
3149
  *
3148
3150
  * export default defineConfig({
3149
3151
  * experimental: {
3150
- * collectionStorage: 'chunked',
3152
+ * collectionStorage: {
3153
+ * type: 'chunked',
3154
+ * chunkSize: 1024 * 1024,
3155
+ * },
3151
3156
  * },
3152
3157
  * });
3153
3158
  * ```
3154
3159
  *
3155
3160
  * See the [experimental data store chunking documentation](https://docs.astro.build/en/reference/experimental-flags/collection-storage/) for more information.
3156
3161
  */
3157
- collectionStorage?: 'single-file' | 'chunked';
3162
+ collectionStorage?: 'single-file' | 'chunked' | {
3163
+ type: 'chunked';
3164
+ /** Maximum UTF-8 byte size of each data store chunk. */
3165
+ chunkSize: number;
3166
+ };
3158
3167
  };
3159
3168
  }
3160
3169
  /**
@@ -3,15 +3,38 @@ import { VIRTUAL_PAGE_RESOLVED_MODULE_ID } from "../vite-plugin-pages/const.js";
3
3
  import { RESOLVED_MODULE_DEV_CSS_PREFIX } from "../vite-plugin-css/const.js";
4
4
  import { getDevCssModuleNameFromPageVirtualModuleName } from "../vite-plugin-css/util.js";
5
5
  import { isAstroServerEnvironment } from "../environments.js";
6
- const STYLE_EXT_REGEX = /\.(?:css|scss|sass|less|styl|pcss)$/i;
6
+ const STYLE_EXT_REGEX = /\.(?:css|less|sass|scss|styl|stylus|pcss|postcss|sss)$/i;
7
+ const STYLE_COMPONENT_EXT_REGEX = /\.(astro|svelte|vue)$/i;
7
8
  const RAW_QUERY_REGEX = /(?:\?|&)raw(?:&|$)/;
8
9
  function hasStyleExtension(id) {
9
10
  return STYLE_EXT_REGEX.test(id.split("?")[0]);
10
11
  }
11
- function isStyleModule(mod) {
12
- if (mod.id && RAW_QUERY_REGEX.test(mod.id) && hasStyleExtension(mod.id)) return false;
13
- if (mod.file && hasStyleExtension(mod.file)) return true;
14
- return mod.id ? hasStyleExtension(mod.id) : false;
12
+ function isComponentStyleModule(id) {
13
+ const [filename, rawQuery] = id.split("?", 2);
14
+ const extensionMatch = STYLE_COMPONENT_EXT_REGEX.exec(filename);
15
+ if (!rawQuery || !extensionMatch) return false;
16
+ const query = new URLSearchParams(rawQuery);
17
+ return query.get("type") === "style" && query.has(extensionMatch[1].toLowerCase());
18
+ }
19
+ function getStyleModuleType(mod) {
20
+ if (mod.id && RAW_QUERY_REGEX.test(mod.id) && hasStyleExtension(mod.id)) return;
21
+ if (mod.id && isComponentStyleModule(mod.id)) return "component";
22
+ if (mod.file && hasStyleExtension(mod.file)) return "file";
23
+ return mod.id && hasStyleExtension(mod.id) ? "file" : void 0;
24
+ }
25
+ function hasClientStyleModuleByFile(moduleGraph, mod, styleType) {
26
+ if (mod.file == null || moduleGraph.getModulesByFile == null) return false;
27
+ const fileModules = moduleGraph.getModulesByFile(mod.file);
28
+ if (fileModules == null) return false;
29
+ for (const fileMod of fileModules) {
30
+ if (fileMod.id == null) continue;
31
+ if (styleType === "component") {
32
+ if (isComponentStyleModule(fileMod.id)) return true;
33
+ } else if (!RAW_QUERY_REGEX.test(fileMod.id) && hasStyleExtension(fileMod.id)) {
34
+ return true;
35
+ }
36
+ }
37
+ return false;
15
38
  }
16
39
  function hmrReload() {
17
40
  return {
@@ -22,19 +45,45 @@ function hmrReload() {
22
45
  handler({ modules, server, timestamp, file }) {
23
46
  if (!isAstroServerEnvironment(this.environment)) return;
24
47
  let hasSsrOnlyModules = false;
48
+ let hasStyleModules = false;
25
49
  let hasSkippedStyleModules = false;
26
50
  const invalidatedModules = /* @__PURE__ */ new Set();
27
51
  for (const mod of modules) {
28
52
  if (mod.id == null) continue;
29
- if (isStyleModule(mod)) {
53
+ const styleType = getStyleModuleType(mod);
54
+ const clientModule = server.environments.client.moduleGraph.getModuleById(mod.id);
55
+ if (styleType) {
56
+ hasStyleModules = true;
57
+ if (clientModule == null && !hasClientStyleModuleByFile(server.environments.client.moduleGraph, mod, styleType)) {
58
+ this.environment.moduleGraph.invalidateModule(
59
+ mod,
60
+ invalidatedModules,
61
+ timestamp,
62
+ true
63
+ );
64
+ hasSsrOnlyModules = true;
65
+ continue;
66
+ }
30
67
  hasSkippedStyleModules = true;
31
68
  continue;
32
69
  }
33
- const clientModule = server.environments.client.moduleGraph.getModuleById(mod.id);
34
70
  if (clientModule != null) continue;
35
71
  this.environment.moduleGraph.invalidateModule(mod, invalidatedModules, timestamp, true);
36
72
  hasSsrOnlyModules = true;
37
73
  }
74
+ const invalidateDevCssModules = () => {
75
+ for (const [id, mod] of this.environment.moduleGraph.idToModuleMap) {
76
+ if (id.startsWith(RESOLVED_MODULE_DEV_CSS_PREFIX)) {
77
+ this.environment.moduleGraph.invalidateModule(mod, void 0, timestamp, true);
78
+ if (isRunnableDevEnvironment(this.environment)) {
79
+ const runnerMod = this.environment.runner.evaluatedModules.getModuleById(id);
80
+ if (runnerMod) {
81
+ this.environment.runner.evaluatedModules.invalidateModule(runnerMod);
82
+ }
83
+ }
84
+ }
85
+ }
86
+ };
38
87
  for (const invalidatedModule of invalidatedModules) {
39
88
  if (invalidatedModule.id?.startsWith(VIRTUAL_PAGE_RESOLVED_MODULE_ID)) {
40
89
  const cssMod = this.environment.moduleGraph.getModuleById(
@@ -45,6 +94,9 @@ function hmrReload() {
45
94
  }
46
95
  }
47
96
  if (hasSsrOnlyModules) {
97
+ if (hasStyleModules) {
98
+ invalidateDevCssModules();
99
+ }
48
100
  if (isRunnableDevEnvironment(this.environment)) {
49
101
  for (const invalidated of invalidatedModules) {
50
102
  if (invalidated.id == null) continue;
@@ -67,17 +119,7 @@ function hmrReload() {
67
119
  return [];
68
120
  }
69
121
  if (hasSkippedStyleModules) {
70
- for (const [id, mod] of this.environment.moduleGraph.idToModuleMap) {
71
- if (id.startsWith(RESOLVED_MODULE_DEV_CSS_PREFIX)) {
72
- this.environment.moduleGraph.invalidateModule(mod, void 0, timestamp, true);
73
- if (isRunnableDevEnvironment(this.environment)) {
74
- const runnerMod = this.environment.runner.evaluatedModules.getModuleById(id);
75
- if (runnerMod) {
76
- this.environment.runner.evaluatedModules.invalidateModule(runnerMod);
77
- }
78
- }
79
- }
80
- }
122
+ invalidateDevCssModules();
81
123
  return [];
82
124
  }
83
125
  if (modules.length > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "7.1.5",
3
+ "version": "7.1.6",
4
4
  "description": "Astro is a modern site builder with web best practices, performance, and DX front-of-mind.",
5
5
  "type": "module",
6
6
  "author": "withastro",
@@ -96,7 +96,7 @@
96
96
  "README.md"
97
97
  ],
98
98
  "dependencies": {
99
- "@astrojs/compiler-rs": "^0.3.1",
99
+ "@astrojs/compiler-rs": "^0.3.2",
100
100
  "@capsizecss/unpack": "^4.0.0",
101
101
  "@clack/prompts": "^1.1.0",
102
102
  "@oslojs/encoding": "^1.1.0",