astro 7.1.6 → 7.2.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 (122) hide show
  1. package/dist/assets/build/generate.d.ts +8 -1
  2. package/dist/assets/build/generate.js +21 -1
  3. package/dist/assets/fonts/providers/index.js +12 -2
  4. package/dist/assets/types.d.ts +13 -0
  5. package/dist/assets/utils/resolveImports.d.ts +0 -1
  6. package/dist/assets/utils/resolveImports.js +1 -4
  7. package/dist/assets/vite-plugin-assets.js +8 -0
  8. package/dist/cli/add/index.js +1 -1
  9. package/dist/cli/agent.d.ts +1 -0
  10. package/dist/cli/agent.js +11 -0
  11. package/dist/cli/dev/background.d.ts +3 -16
  12. package/dist/cli/dev/background.js +7 -93
  13. package/dist/cli/dev/index.d.ts +12 -0
  14. package/dist/cli/dev/index.js +30 -23
  15. package/dist/cli/dev/logs.js +2 -64
  16. package/dist/cli/dev/status.d.ts +2 -9
  17. package/dist/cli/dev/status.js +6 -24
  18. package/dist/cli/dev/stop.d.ts +2 -6
  19. package/dist/cli/dev/stop.js +6 -14
  20. package/dist/cli/infra/build-time-astro-version-provider.js +1 -1
  21. package/dist/cli/install-package.js +1 -1
  22. package/dist/cli/preview/index.js +77 -3
  23. package/dist/cli/server.d.ts +60 -0
  24. package/dist/cli/server.js +274 -0
  25. package/dist/content/content-layer.js +63 -3
  26. package/dist/content/loaders/file.js +1 -1
  27. package/dist/content/loaders/glob.js +3 -2
  28. package/dist/content/mutable-data-store.js +3 -3
  29. package/dist/content/runtime.d.ts +3 -0
  30. package/dist/content/runtime.js +8 -2
  31. package/dist/content/types-generator.js +1 -0
  32. package/dist/content/vite-plugin-content-assets.js +6 -1
  33. package/dist/content/vite-plugin-content-imports.js +12 -3
  34. package/dist/content/vite-plugin-content-virtual-mod.js +28 -16
  35. package/dist/core/app/entrypoints/index.d.ts +2 -0
  36. package/dist/core/app/entrypoints/index.js +14 -0
  37. package/dist/core/app/types.d.ts +2 -0
  38. package/dist/core/build/config-hash/index.d.ts +8 -0
  39. package/dist/core/build/config-hash/index.js +23 -0
  40. package/dist/core/build/config-hash/input.d.ts +199 -0
  41. package/dist/core/build/config-hash/input.js +43 -0
  42. package/dist/core/build/generate.d.ts +3 -1
  43. package/dist/core/build/generate.js +149 -14
  44. package/dist/core/build/incremental-content-collector.d.ts +10 -0
  45. package/dist/core/build/incremental-content-collector.js +33 -0
  46. package/dist/core/build/incremental-image-collector.d.ts +11 -0
  47. package/dist/core/build/incremental-image-collector.js +32 -0
  48. package/dist/core/build/incremental-metadata.d.ts +9 -0
  49. package/dist/core/build/incremental-metadata.js +17 -0
  50. package/dist/core/build/incremental.d.ts +123 -0
  51. package/dist/core/build/incremental.js +178 -0
  52. package/dist/core/build/index.d.ts +3 -0
  53. package/dist/core/build/index.js +6 -2
  54. package/dist/core/build/internal.d.ts +19 -0
  55. package/dist/core/build/lockfile/finder.d.ts +22 -0
  56. package/dist/core/build/lockfile/finder.js +45 -0
  57. package/dist/core/build/lockfile/hasher.d.ts +16 -0
  58. package/dist/core/build/lockfile/hasher.js +46 -0
  59. package/dist/core/build/lockfile/index.d.ts +9 -0
  60. package/dist/core/build/lockfile/index.js +16 -0
  61. package/dist/core/build/plugins/index.js +3 -1
  62. package/dist/core/build/plugins/plugin-incremental.d.ts +14 -0
  63. package/dist/core/build/plugins/plugin-incremental.js +152 -0
  64. package/dist/core/build/plugins/plugin-manifest.js +11 -2
  65. package/dist/core/build/types.d.ts +2 -0
  66. package/dist/core/config/schemas/base.d.ts +3 -2
  67. package/dist/core/config/schemas/base.js +1 -0
  68. package/dist/core/config/schemas/defaults.d.ts +1 -0
  69. package/dist/core/config/schemas/defaults.js +1 -0
  70. package/dist/core/config/schemas/relative.d.ts +7 -4
  71. package/dist/core/config/settings.js +1 -1
  72. package/dist/core/constants.js +1 -1
  73. package/dist/core/create-vite.js +2 -1
  74. package/dist/core/dev/dev.js +1 -1
  75. package/dist/core/dev/lockfile.d.ts +8 -7
  76. package/dist/core/dev/lockfile.js +13 -13
  77. package/dist/core/encryption.d.ts +6 -0
  78. package/dist/core/encryption.js +14 -2
  79. package/dist/core/errors/default-handler.js +4 -5
  80. package/dist/core/fetch/fetch-state.js +1 -0
  81. package/dist/core/fetch/index.js +1 -1
  82. package/dist/core/logger/config.d.ts +1 -1
  83. package/dist/core/logger/load.d.ts +3 -1
  84. package/dist/core/logger/load.js +3 -3
  85. package/dist/core/logger/utils.d.ts +6 -4
  86. package/dist/core/logger/utils.js +13 -5
  87. package/dist/core/logger/vite-plugin.js +1 -1
  88. package/dist/core/messages/runtime.js +1 -1
  89. package/dist/core/middleware/vite-plugin.d.ts +1 -1
  90. package/dist/core/middleware/vite-plugin.js +11 -24
  91. package/dist/core/preview/static-preview-server.d.ts +2 -0
  92. package/dist/core/preview/static-preview-server.js +1 -0
  93. package/dist/core/routing/dev.d.ts +3 -1
  94. package/dist/core/routing/dev.js +10 -2
  95. package/dist/core/routing/handler.js +1 -1
  96. package/dist/core/session/config.d.ts +2 -2
  97. package/dist/core/session/config.js +2 -1
  98. package/dist/core/session/provider-disabled.d.ts +2 -0
  99. package/dist/core/session/provider-disabled.js +7 -0
  100. package/dist/core/session/provider.d.ts +1 -0
  101. package/dist/core/session/provider.js +4 -0
  102. package/dist/core/session/utils.js +3 -0
  103. package/dist/core/session/vite-plugin.d.ts +3 -0
  104. package/dist/core/session/vite-plugin.js +41 -6
  105. package/dist/integrations/hooks.js +3 -1
  106. package/dist/prefetch/index.js +18 -1
  107. package/dist/prefetch/speculation-rules.d.ts +6 -0
  108. package/dist/prefetch/speculation-rules.js +22 -0
  109. package/dist/runtime/prerender/static-paths.js +5 -1
  110. package/dist/runtime/server/render/head.js +10 -0
  111. package/dist/runtime/server/render/util.js +3 -0
  112. package/dist/transitions/swap-functions.js +49 -4
  113. package/dist/types/public/common.d.ts +1 -0
  114. package/dist/types/public/config.d.ts +81 -9
  115. package/dist/types/public/integrations.d.ts +29 -2
  116. package/dist/types/public/internal.d.ts +2 -0
  117. package/dist/types/public/preview.d.ts +2 -0
  118. package/dist/vite-plugin-app/app.d.ts +3 -1
  119. package/dist/vite-plugin-app/app.js +4 -3
  120. package/dist/vite-plugin-config-alias/index.js +1 -0
  121. package/dist/vite-plugin-environment/index.js +4 -1
  122. package/package.json +1 -2
@@ -0,0 +1,4 @@
1
+ import { provideSession } from "./handler.js";
2
+ export {
3
+ provideSession
4
+ };
@@ -31,6 +31,9 @@ function normalizeSessionDriverConfig(driver, options) {
31
31
  };
32
32
  }
33
33
  function sessionConfigToManifest(config) {
34
+ if (config === false) {
35
+ return void 0;
36
+ }
34
37
  const sessionDriver = config?.driver;
35
38
  if (!config || !sessionDriver) {
36
39
  return void 0;
@@ -4,3 +4,6 @@ export declare const VIRTUAL_SESSION_DRIVER_ID = "virtual:astro:session-driver";
4
4
  export declare function vitePluginSessionDriver({ settings }: {
5
5
  settings: AstroSettings;
6
6
  }): VitePlugin;
7
+ export declare function vitePluginSessionProvider({ settings }: {
8
+ settings: AstroSettings;
9
+ }): VitePlugin;
@@ -1,6 +1,8 @@
1
+ import { realpathSync } from "node:fs";
1
2
  import { fileURLToPath } from "node:url";
2
3
  import { SessionStorageInitError } from "../errors/errors-data.js";
3
4
  import { AstroError } from "../errors/index.js";
5
+ import { normalizePath } from "../viteUtils.js";
4
6
  import { normalizeSessionDriverConfig } from "./utils.js";
5
7
  const VIRTUAL_SESSION_DRIVER_ID = "virtual:astro:session-driver";
6
8
  const RESOLVED_VIRTUAL_SESSION_DRIVER_ID = "\0" + VIRTUAL_SESSION_DRIVER_ID;
@@ -21,13 +23,11 @@ function vitePluginSessionDriver({ settings }) {
21
23
  id: new RegExp(`^${RESOLVED_VIRTUAL_SESSION_DRIVER_ID}$`)
22
24
  },
23
25
  async handler() {
24
- if (!settings.config.session?.driver) {
26
+ const session = settings.config.session;
27
+ if (session === false || !session?.driver) {
25
28
  return { code: "export default null;" };
26
29
  }
27
- const driver = normalizeSessionDriverConfig(
28
- settings.config.session.driver,
29
- settings.config.session.options
30
- );
30
+ const driver = normalizeSessionDriverConfig(session.driver, session.options);
31
31
  const importerPath = fileURLToPath(import.meta.url);
32
32
  const resolved = await this.resolve(driver.entrypoint, importerPath);
33
33
  if (!resolved) {
@@ -48,7 +48,42 @@ export default _default;`
48
48
  }
49
49
  };
50
50
  }
51
+ const PROVIDER_FILENAME = "provider.js";
52
+ const DISABLED_PROVIDER_FILENAME = "provider-disabled.js";
53
+ function canonicalizePath(filePath) {
54
+ try {
55
+ return normalizePath(realpathSync(filePath));
56
+ } catch {
57
+ return normalizePath(filePath);
58
+ }
59
+ }
60
+ function vitePluginSessionProvider({ settings }) {
61
+ const providerPath = canonicalizePath(
62
+ fileURLToPath(new URL(`./${PROVIDER_FILENAME}`, import.meta.url))
63
+ );
64
+ const disabledProviderPath = canonicalizePath(
65
+ fileURLToPath(new URL(`./${DISABLED_PROVIDER_FILENAME}`, import.meta.url))
66
+ );
67
+ return {
68
+ name: "astro:session-provider",
69
+ enforce: "pre",
70
+ async resolveId(id, importer) {
71
+ const session = settings.config.session;
72
+ const hasSessionDriver = session !== false && !!session?.driver;
73
+ if (hasSessionDriver) return null;
74
+ if (!normalizePath(id).endsWith(`/session/${PROVIDER_FILENAME}`)) return null;
75
+ if (canonicalizePath(id) === providerPath) return disabledProviderPath;
76
+ if (!importer) return null;
77
+ const resolved = await this.resolve(id, importer, { skipSelf: true });
78
+ if (resolved && canonicalizePath(resolved.id) === providerPath) {
79
+ return disabledProviderPath;
80
+ }
81
+ return null;
82
+ }
83
+ };
84
+ }
51
85
  export {
52
86
  VIRTUAL_SESSION_DRIVER_ID,
53
- vitePluginSessionDriver
87
+ vitePluginSessionDriver,
88
+ vitePluginSessionProvider
54
89
  };
@@ -260,7 +260,9 @@ async function runHookConfigSetup({
260
260
  try {
261
261
  updatedConfig = await validateConfigRefined(updatedConfig);
262
262
  if (isLoggerUpdated) {
263
- logger.setDestination(await loadLoggerDestination(updatedConfig.logger));
263
+ logger.setDestination(
264
+ await loadLoggerDestination(updatedConfig.logger, updatedConfig.root)
265
+ );
264
266
  }
265
267
  } catch (error) {
266
268
  integrationLogger.error("An error occurred while updating the config");
@@ -129,7 +129,7 @@ function prefetch(url, opts) {
129
129
  const ignoreSlowConnection = opts?.ignoreSlowConnection ?? false;
130
130
  if (!canPrefetchUrl(url, ignoreSlowConnection)) return;
131
131
  prefetchedUrls.add(url);
132
- if (clientPrerender && HTMLScriptElement.supports?.("speculationrules")) {
132
+ if (clientPrerender && HTMLScriptElement.supports?.("speculationrules") && !hasStaticSpeculationRules()) {
133
133
  debug?.(`[astro] Prefetching ${url} with <script type="speculationrules">`);
134
134
  appendSpeculationRules(url, opts?.eagerness ?? "immediate");
135
135
  } else if (document.createElement("link").relList?.supports?.("prefetch")) {
@@ -203,6 +203,23 @@ function onPageLoad(cb) {
203
203
  }
204
204
  }).observe(document.body, { childList: true, subtree: true });
205
205
  }
206
+ let _hasStaticRules;
207
+ function hasStaticSpeculationRules() {
208
+ if (_hasStaticRules === void 0) {
209
+ _hasStaticRules = Array.from(document.querySelectorAll('script[type="speculationrules"]')).some(
210
+ (el) => {
211
+ try {
212
+ const rules = JSON.parse(el.textContent ?? "");
213
+ const entries = [...rules.prerender ?? [], ...rules.prefetch ?? []];
214
+ return entries.some((entry) => entry.source === "document");
215
+ } catch {
216
+ return false;
217
+ }
218
+ }
219
+ );
220
+ }
221
+ return _hasStaticRules;
222
+ }
206
223
  function appendSpeculationRules(url, eagerness) {
207
224
  const script = document.createElement("script");
208
225
  script.type = "speculationrules";
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Generates static speculation rules JSON using `"source": "document"` with CSS selector matching.
3
+ * This produces a deterministic payload that can be hashed at build time for CSP compatibility,
4
+ * unlike the dynamic per-URL `"source": "list"` approach in `appendSpeculationRules()`.
5
+ */
6
+ export declare function generateSpeculationRulesContent(prefetchAll: boolean): string;
@@ -0,0 +1,22 @@
1
+ function generateSpeculationRulesContent(prefetchAll) {
2
+ const selector = prefetchAll ? "a" : "a[data-astro-prefetch]";
3
+ return JSON.stringify({
4
+ prerender: [
5
+ {
6
+ source: "document",
7
+ where: { selector_matches: selector },
8
+ eagerness: "moderate"
9
+ }
10
+ ],
11
+ prefetch: [
12
+ {
13
+ source: "document",
14
+ where: { selector_matches: selector },
15
+ eagerness: "moderate"
16
+ }
17
+ ]
18
+ });
19
+ }
20
+ export {
21
+ generateSpeculationRulesContent
22
+ };
@@ -61,7 +61,11 @@ class StaticPaths {
61
61
  });
62
62
  for (const staticPath of staticPaths) {
63
63
  const pathname = stringifyParams(staticPath.params, route, manifest.trailingSlash);
64
- paths.push({ pathname, route });
64
+ paths.push({
65
+ pathname,
66
+ route,
67
+ cacheKey: staticPath.cacheKey === void 0 ? void 0 : String(staticPath.cacheKey)
68
+ });
65
69
  }
66
70
  return paths;
67
71
  }
@@ -53,6 +53,16 @@ function renderAllHeadContent(result) {
53
53
  );
54
54
  const sep = result.compressHTML === true || result.compressHTML === "jsx" ? "" : "\n";
55
55
  content += styles.join(sep) + links.join(sep) + scripts.join(sep);
56
+ if (result.speculationRulesContent) {
57
+ content += renderElement(
58
+ "script",
59
+ {
60
+ props: { type: "speculationrules" },
61
+ children: result.speculationRulesContent
62
+ },
63
+ false
64
+ );
65
+ }
56
66
  content += result._metadata.extraHead.join("");
57
67
  return markHTMLString(content);
58
68
  }
@@ -35,6 +35,9 @@ function isCustomElement(tagName) {
35
35
  return tagName.includes("-");
36
36
  }
37
37
  function handleBooleanAttribute(key, value, shouldEscape, tagName) {
38
+ if (key === "popover") {
39
+ return markHTMLString(value ? ` ${key}` : "");
40
+ }
38
41
  if (tagName && isCustomElement(tagName)) {
39
42
  return markHTMLString(` ${key}="${toAttributeString(value, shouldEscape)}"`);
40
43
  }
@@ -1,7 +1,30 @@
1
1
  import { SERVER_ISLAND_START } from "../runtime/server/render/server-islands-shared.js";
2
2
  const PERSIST_ATTR = "data-astro-transition-persist";
3
3
  const NON_OVERRIDABLE_ASTRO_ATTRS = ["data-astro-transition", "data-astro-transition-fallback"];
4
- const knownVueScopedStyles = /* @__PURE__ */ new Map();
4
+ const viteStyleState = import.meta.env.DEV ? /* @__PURE__ */ (() => {
5
+ const styles = /* @__PURE__ */ new Map();
6
+ let observer;
7
+ return {
8
+ styles,
9
+ observe() {
10
+ if (observer) return;
11
+ observer = new MutationObserver((records) => {
12
+ for (const record of records) {
13
+ for (const node of record.addedNodes) {
14
+ if (!(node instanceof HTMLStyleElement)) continue;
15
+ const viteDevId = node.dataset.viteDevId;
16
+ if (!viteDevId) continue;
17
+ const knownStyle = styles.get(viteDevId);
18
+ if (node === knownStyle) continue;
19
+ knownStyle?.remove();
20
+ styles.set(viteDevId, node);
21
+ }
22
+ }
23
+ });
24
+ observer.observe(document.head, { childList: true });
25
+ }
26
+ };
27
+ })() : void 0;
5
28
  const scriptsAlreadyRan = /* @__PURE__ */ new Set();
6
29
  function detectScriptExecuted(script) {
7
30
  const key = script.src ? new URL(script.src, location.href).href : script.textContent;
@@ -41,8 +64,8 @@ function swapHeadElements(doc) {
41
64
  newEl.remove();
42
65
  } else {
43
66
  if (import.meta.env.DEV && el instanceof HTMLStyleElement) {
44
- const viteDevId = vueScopedStyleId(el);
45
- viteDevId && knownVueScopedStyles.set(viteDevId, el);
67
+ const viteDevId = el.dataset.viteDevId;
68
+ viteDevId && viteStyleState?.styles.set(viteDevId, el);
46
69
  }
47
70
  el.remove();
48
71
  }
@@ -50,7 +73,18 @@ function swapHeadElements(doc) {
50
73
  relevantNodes(document.head, "commentsOnly").forEach((node) => node.remove());
51
74
  if (import.meta.env.DEV) {
52
75
  relevantNodes(doc.head).forEach((child) => {
53
- document.head.append(knownVueScopedStyles.get(child.dataset?.viteDevId) || child);
76
+ const viteDevId = child instanceof HTMLStyleElement && child.dataset.viteDevId;
77
+ const knownStyle = viteDevId && viteStyleState?.styles.get(viteDevId);
78
+ if (knownStyle) {
79
+ if (!vueScopedStyleId(knownStyle)) knownStyle.textContent = child.textContent;
80
+ document.head.append(knownStyle);
81
+ } else {
82
+ if (viteDevId) {
83
+ viteStyleState?.styles.set(viteDevId, child);
84
+ viteStyleState?.observe();
85
+ }
86
+ document.head.append(child);
87
+ }
54
88
  });
55
89
  } else {
56
90
  document.head.append(...relevantNodes(doc.head));
@@ -85,6 +119,17 @@ function swapBodyElement(newElement, oldElement) {
85
119
  }
86
120
  }
87
121
  attachShadowRoots(newElement);
122
+ reifyMediaElements(newElement);
123
+ }
124
+ function reifyMediaElements(root) {
125
+ for (const media of root.querySelectorAll("video, audio")) {
126
+ const fresh = document.createElement(media.localName);
127
+ for (const attr of media.attributes) {
128
+ fresh.setAttribute(attr.name, attr.value);
129
+ }
130
+ fresh.innerHTML = media.innerHTML;
131
+ media.replaceWith(fresh);
132
+ }
88
133
  }
89
134
  function attachShadowRoots(root) {
90
135
  root.querySelectorAll("template[shadowrootmode]").forEach((template) => {
@@ -13,6 +13,7 @@ export interface GetStaticPathsOptions {
13
13
  export type GetStaticPathsItem = {
14
14
  params: Params;
15
15
  props?: Props;
16
+ cacheKey?: string | number;
16
17
  };
17
18
  export type GetStaticPathsResult = GetStaticPathsItem[];
18
19
  export type GetStaticPathsResultKeyed = GetStaticPathsResult & {
@@ -1171,6 +1171,7 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
1171
1171
  * Setting `build.format` controls what `Astro.url` is set to during the build. When it is:
1172
1172
  * - `directory` - The `Astro.url.pathname` will include a trailing slash to mimic folder behavior. (e.g. `/foo/`)
1173
1173
  * - `file` - The `Astro.url.pathname` will include `.html`. (e.g. `/foo.html`)
1174
+ * - `preserve` - The `Astro.url.pathname` matches the generated file for each page: index pages include a trailing slash (e.g. `/foo/`), while non-index pages include `.html` (e.g. `/foo.html`).
1174
1175
  *
1175
1176
  * This means that when you create relative URLs using `new URL('./relative', Astro.url)`, you will get consistent behavior between dev and build.
1176
1177
  *
@@ -1535,7 +1536,8 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
1535
1536
  * @description
1536
1537
  *
1537
1538
  * The entrypoint for the [logger implementation](https://docs.astro.build/en/reference/logger-reference/#the-logger-implementation).
1538
- * This can be an npm package, or a `URL` pointing to a file in your project:
1539
+ * This can be an npm package, a path relative to your project root, or a `URL` pointing to a
1540
+ * file in your project:
1539
1541
  *
1540
1542
  * ```js title="astro.config.mjs"
1541
1543
  * import { defineConfig } from 'astro/config';
@@ -1546,6 +1548,9 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
1546
1548
  * }
1547
1549
  * });
1548
1550
  * ```
1551
+ *
1552
+ * The logger entrypoint must be a JavaScript file. TypeScript is not supported. If Astro fails to load
1553
+ * the implementation, it falls back to its default logger.
1549
1554
  */
1550
1555
  /**
1551
1556
  * @docs
@@ -1573,6 +1578,7 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
1573
1578
  /**
1574
1579
  * @docs
1575
1580
  * @kind heading
1581
+ * @type {object | false}
1576
1582
  * @version 5.7.0
1577
1583
  * @name Session Options
1578
1584
  * @description
@@ -1596,9 +1602,19 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
1596
1602
  *
1597
1603
  * Session drivers are configured at build time. This means environment variables used in the driver configuration are inlined. You must create your own driver entrypoint to [override the configuration at runtime](https://docs.astro.build/en/guides/sessions/#overriding-the-configuration-at-runtime).
1598
1604
  *
1605
+ * Since Astro v7.2.0, you can opt out of session support by setting the option to `false`. When sessions are disabled, the session runtime is excluded from the SSR bundle, and adapters skip wiring their default session driver. This is useful for serverless and edge runtimes where bundle parse time is sensitive.
1606
+ *
1607
+ * ```js title="astro.config.mjs"
1608
+ * import { defineConfig } from 'astro/config';
1609
+ *
1610
+ * export default defineConfig({
1611
+ * session: false,
1612
+ * });
1613
+ * ```
1614
+ *
1599
1615
  * See [the sessions guide](https://docs.astro.build/en/guides/sessions/) for more information.
1600
1616
  */
1601
- session?: SessionConfig<TDriver>;
1617
+ session?: SessionConfig<TDriver> | false;
1602
1618
  /**
1603
1619
  * @docs
1604
1620
  * @name session.driver
@@ -2230,7 +2246,9 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
2230
2246
  * @docs
2231
2247
  * @name markdown.remarkPlugins
2232
2248
  * @type {RemarkPlugins}
2233
- * @deprecated Pass `remarkPlugins` to `unified({ remarkPlugins })` from `@astrojs/markdown-remark` and set it as `markdown.processor` instead. Will be removed in a future major.
2249
+ * @deprecated This property is deprecated and will be removed in a future major version. Pass plugins to the configured [`markdown.processor`](https://docs.astro.build/en/reference/configuration-reference/#markdownprocessor) instead.
2250
+ *
2251
+ * Learn more about [setting up a Markdown processor](https://docs.astro.build/en/guides/markdown-content/#setting-up-a-markdown-processor) and [using plugins](https://docs.astro.build/en/guides/markdown-content/#markdown-processor-plugins) in the Markdown guide.
2234
2252
  * @description
2235
2253
  * Pass [remark plugins](https://github.com/remarkjs/remark) to customize how your Markdown is built. You can import and apply the plugin function (recommended), or pass the plugin name as a string.
2236
2254
  *
@@ -2248,7 +2266,9 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
2248
2266
  * @docs
2249
2267
  * @name markdown.rehypePlugins
2250
2268
  * @type {RehypePlugins}
2251
- * @deprecated Pass `rehypePlugins` to `unified({ rehypePlugins })` from `@astrojs/markdown-remark` and set it as `markdown.processor` instead. Will be removed in a future major.
2269
+ * @deprecated This property is deprecated and will be removed in a future major version. Pass plugins to the configured [`markdown.processor`](https://docs.astro.build/en/reference/configuration-reference/#markdownprocessor) instead.
2270
+ *
2271
+ * Learn more about [setting up a Markdown processor](https://docs.astro.build/en/guides/markdown-content/#setting-up-a-markdown-processor) and [using plugins](https://docs.astro.build/en/guides/markdown-content/#markdown-processor-plugins) in the Markdown guide.
2252
2272
  * @description
2253
2273
  * Pass [rehype plugins](https://github.com/remarkjs/remark-rehype) to customize how your Markdown's output HTML is processed. You can import and apply the plugin function (recommended), or pass the plugin name as a string.
2254
2274
  *
@@ -2268,7 +2288,9 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
2268
2288
  * @type {boolean}
2269
2289
  * @default `true`
2270
2290
  * @version 2.0.0
2271
- * @deprecated Pass `gfm` to your processor instead (e.g. `unified({ gfm: false })`). Will be removed in a future major.
2291
+ * @deprecated This property is deprecated and will be removed in a future major version. Pass `gfm` to the configured [`markdown.processor`](https://docs.astro.build/en/reference/configuration-reference/#markdownprocessor) instead.
2292
+ *
2293
+ * Learn more about [setting up a Markdown processor](https://docs.astro.build/en/guides/markdown-content/#setting-up-a-markdown-processor) and [using GitHub-flavored Markdown](https://docs.astro.build/en/guides/markdown-content/#github-flavored-markdown) in the Markdown guide.
2272
2294
  * @description
2273
2295
  * Astro uses [GitHub-flavored Markdown](https://github.com/remarkjs/remark-gfm) by default. To disable this, set the `gfm` flag to `false`:
2274
2296
  *
@@ -2287,7 +2309,9 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
2287
2309
  * @type {boolean | Smartypants}
2288
2310
  * @default `true`
2289
2311
  * @version 2.0.0
2290
- * @deprecated Pass `smartypants` to your processor instead (e.g. `unified({ smartypants: false })`). Will be removed in a future major.
2312
+ * @deprecated This property is deprecated and will be removed in a future major version. Pass it to the configured [`markdown.processor`](https://docs.astro.build/en/reference/configuration-reference/#markdownprocessor) instead. Use `smartypants` for `unified()` or `smartPunctuation` for `satteri()`.
2313
+ *
2314
+ * Learn more about [setting up a Markdown processor](https://docs.astro.build/en/guides/markdown-content/#setting-up-a-markdown-processor) and [using smart punctuation](https://docs.astro.build/en/guides/markdown-content/#smart-punctuation) in the Markdown guide.
2291
2315
  * @description
2292
2316
  * Whether to use the [SmartyPants formatter](https://daringfireball.net/projects/smartypants/) to transform straight quotes into smart quotes, dashes into en/em dashes, and triple dots into ellipses.
2293
2317
  *
@@ -2300,7 +2324,11 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
2300
2324
  * @docs
2301
2325
  * @name markdown.remarkRehype
2302
2326
  * @type {RemarkRehype}
2303
- * @deprecated Pass `remarkRehype` to `unified({ remarkRehype })` from `@astrojs/markdown-remark` and set it as `markdown.processor` instead. Will be removed in a future major.
2327
+ * @deprecated This property is deprecated and will be removed in a future major version.
2328
+ *
2329
+ * To configure footnotes, pass `remarkRehype` to the `unified()` processor or `gfm.footnotes` to the `satteri()` processor instead. Other `remark-rehype` options are only supported when using `unified()`.
2330
+ *
2331
+ * Learn more about [setting up a Markdown processor](https://docs.astro.build/en/guides/markdown-content/#setting-up-a-markdown-processor) and [using built-in features](https://docs.astro.build/en/guides/markdown-content/#built-in-features) in the Markdown guide.
2304
2332
  * @description
2305
2333
  * Pass options to [remark-rehype](https://github.com/remarkjs/remark-rehype#api).
2306
2334
  *
@@ -2320,8 +2348,9 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
2320
2348
  * @type {MarkdownProcessor}
2321
2349
  * @version 6.4.0
2322
2350
  * @description
2323
- * Configures the Markdown processor used to render `.md` files. Defaults to `satteri()` from
2324
- * `@astrojs/markdown-satteri`, Astro's native Markdown pipeline.
2351
+ * Configures the [Markdown processor](https://docs.astro.build/en/guides/markdown-content/#markdown-processors) used to render `.md` files.
2352
+ *
2353
+ * Sätteri, Astro’s native Markdown pipeline, is the default processor. To configure it, install `@astrojs/markdown-satteri` and pass options to `satteri()`:
2325
2354
  *
2326
2355
  * ```js
2327
2356
  * // astro.config.mjs
@@ -2353,6 +2382,8 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
2353
2382
  * },
2354
2383
  * });
2355
2384
  * ```
2385
+ *
2386
+ * Learn more about the [official Markdown processors and how to choose one](https://docs.astro.build/en/guides/markdown-content/#choosing-a-markdown-processor) in the Markdown guide.
2356
2387
  */
2357
2388
  processor?: MarkdownProcessor;
2358
2389
  };
@@ -3164,6 +3195,47 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
3164
3195
  /** Maximum UTF-8 byte size of each data store chunk. */
3165
3196
  chunkSize: number;
3166
3197
  };
3198
+ /**
3199
+ * @name experimental.incrementalBuild
3200
+ * @type {boolean}
3201
+ * @default `false`
3202
+ * @version 7.2
3203
+ * @description
3204
+ *
3205
+ * Enables incremental static builds. When enabled, pages with a `cacheKey` returned
3206
+ * from `getStaticPaths()` can be skipped during subsequent builds if neither
3207
+ * their data nor their dependencies have changed.
3208
+ *
3209
+ * On the first build, all pages are rendered normally and a cache manifest is saved.
3210
+ * On subsequent builds, each page is checked against the manifest: if the page's
3211
+ * `cacheKey` and the hash of its module dependency graph both match, the page is
3212
+ * skipped and the previous output is preserved.
3213
+ *
3214
+ * ```js
3215
+ * // astro.config.mjs
3216
+ * {
3217
+ * experimental: {
3218
+ * incrementalBuild: true,
3219
+ * },
3220
+ * }
3221
+ * ```
3222
+ *
3223
+ * In your dynamic routes, return a `cacheKey` from `getStaticPaths()`:
3224
+ *
3225
+ * ```js
3226
+ * // src/pages/blog/[slug].astro
3227
+ * export async function getStaticPaths() {
3228
+ * const posts = await fetchPosts();
3229
+ * return posts.map(post => ({
3230
+ * params: { slug: post.slug },
3231
+ * props: { post },
3232
+ * cacheKey: post.digest,
3233
+ * }));
3234
+ * }
3235
+ * ```
3236
+ * See the [experimental incremental static builds](https://docs.astro.build/en/reference/experimental-flags/incremental-build/) for more information.
3237
+ */
3238
+ incrementalBuild?: boolean;
3167
3239
  };
3168
3240
  }
3169
3241
  /**
@@ -1,7 +1,7 @@
1
1
  import type { AddressInfo } from 'node:net';
2
2
  import type { ViteDevServer, InlineConfig } from 'vite';
3
3
  import type { SerializedSSRManifest } from '../../core/app/types.js';
4
- import type { AssetsGlobalStaticImagesList } from '../../assets/types.js';
4
+ import type { AssetsGlobalStaticImagesList, SerializedStaticImage } from '../../assets/types.js';
5
5
  import type { PageBuildData } from '../../core/build/types.js';
6
6
  import type { AstroIntegrationLogger } from '../../core/logger/core.js';
7
7
  import type { AdapterFeatureStability } from '../../integrations/features-validation.js';
@@ -194,6 +194,28 @@ export type AstroAdapter = {
194
194
  export interface PathWithRoute {
195
195
  pathname: string;
196
196
  route: RouteData;
197
+ cacheKey?: string;
198
+ }
199
+ /**
200
+ * Incremental-build data an out-of-process prerenderer collects while rendering
201
+ * a page, reported back to the build process so skipped pages can be tracked and
202
+ * replayed without a re-render.
203
+ */
204
+ export interface PrerenderRenderMetadata {
205
+ /** Root-relative `filePath`s of the content entries the page rendered. */
206
+ contentEntryKeys?: string[];
207
+ /** Optimized-image transforms the page resolved. */
208
+ staticImages?: SerializedStaticImage[];
209
+ }
210
+ /**
211
+ * The richer result a prerenderer's `render()` may return instead of a bare
212
+ * `Response`, pairing the rendered response with the incremental-build metadata
213
+ * collected for that page. Used by out-of-process prerenderers, which collect
214
+ * the metadata in their own runtime rather than in the build's collectors.
215
+ */
216
+ export interface PrerenderResult {
217
+ response: Response;
218
+ metadata?: PrerenderRenderMetadata;
197
219
  }
198
220
  /**
199
221
  * Custom prerenderer that adapters can provide to control how pages are prerendered.
@@ -217,10 +239,15 @@ export interface AstroPrerenderer {
217
239
  * use the `pathname` from the `PathWithRoute` entry returned by `getStaticPaths`.
218
240
  * @param options - Render options
219
241
  * @param options.routeData - The matched route for this path
242
+ * @returns A `Response`, or a {@link PrerenderResult} pairing the response with
243
+ * the incremental-build metadata the page resolved. Out-of-process
244
+ * prerenderers return the latter to report metadata they collect in their own
245
+ * runtime; in-process prerenderers populate the build's collectors directly
246
+ * and return a bare `Response`.
220
247
  */
221
248
  render: (request: Request, options: {
222
249
  routeData: RouteData;
223
- }) => Promise<Response>;
250
+ }) => Promise<Response | PrerenderResult>;
224
251
  /**
225
252
  * Returns images collected in the adapter's runtime (e.g. workerd) to be merged
226
253
  * into the Node-side static image list. The default Sharp pipeline runs after.
@@ -231,6 +231,8 @@ export interface SSRResult {
231
231
  isStrictDynamic: SSRManifestCSP['isStrictDynamic'];
232
232
  scriptDirective: SSRManifestCSP['scriptDirective'];
233
233
  styleDirective: SSRManifestCSP['styleDirective'];
234
+ /** Static speculation rules JSON to inject in the head when CSP + clientPrerender are both enabled. */
235
+ speculationRulesContent?: string;
234
236
  internalFetchHeaders?: Record<string, string>;
235
237
  }
236
238
  /**
@@ -1,8 +1,10 @@
1
1
  import type { OutgoingHttpHeaders } from 'node:http';
2
+ import type { ResolvedServerUrls } from 'vite';
2
3
  import type { AstroIntegrationLogger } from '../../core/logger/core.js';
3
4
  export interface PreviewServer {
4
5
  host?: string;
5
6
  port: number;
7
+ urls?: ResolvedServerUrls;
6
8
  closed(): Promise<void>;
7
9
  stop(): Promise<void>;
8
10
  }
@@ -35,7 +35,9 @@ export declare class AstroServerApp extends BaseApp<RunnablePipeline> {
35
35
  * Called via HMR when action files change.
36
36
  */
37
37
  clearActions(): void;
38
- devMatch(pathname: string): Promise<DevMatch | undefined>;
38
+ devMatch(pathname: string, { prerenderOnly }?: {
39
+ prerenderOnly?: boolean;
40
+ }): Promise<DevMatch | undefined>;
39
41
  static create(manifest: SSRManifest, routesList: RoutesList, logger: AstroLogger, loader: ModuleLoader, settings: AstroSettings, getDebugInfo: () => Promise<string>): Promise<AstroServerApp>;
40
42
  createPipeline(_streaming: boolean, manifest: SSRManifest, settings: AstroSettings, logger: AstroLogger, loader: ModuleLoader, manifestData: RoutesList, getDebugInfo: () => Promise<string>): RunnablePipeline;
41
43
  /**
@@ -70,12 +70,13 @@ class AstroServerApp extends BaseApp {
70
70
  clearActions() {
71
71
  this.pipeline.clearActions();
72
72
  }
73
- async devMatch(pathname) {
73
+ async devMatch(pathname, { prerenderOnly } = {}) {
74
74
  const matchedRoute = await matchRoute(
75
75
  pathname,
76
76
  this.manifestData,
77
77
  this.pipeline,
78
- this.manifest
78
+ this.manifest,
79
+ { prerenderOnly }
79
80
  );
80
81
  if (!matchedRoute) {
81
82
  return void 0;
@@ -132,7 +133,7 @@ class AstroServerApp extends BaseApp {
132
133
  controller,
133
134
  pathname,
134
135
  async run() {
135
- const matchedRoute = await self.devMatch(pathname);
136
+ const matchedRoute = await self.devMatch(pathname, { prerenderOnly });
136
137
  if (!matchedRoute) {
137
138
  if (prerenderOnly) {
138
139
  handled = false;
@@ -68,6 +68,7 @@ function configAliasVitePlugin({
68
68
  let hasReplacement = false;
69
69
  const replaceAliases = (match, importId) => {
70
70
  if (!importId) return match;
71
+ if (importId.startsWith("data:")) return match;
71
72
  const resolved = resolveWithAlias(importId, configAlias);
72
73
  if (resolved) {
73
74
  hasReplacement = true;
@@ -16,7 +16,10 @@ const ALWAYS_NOEXTERNAL = [
16
16
  // Full explanation and related bug report: https://github.com/withastro/astro/pull/3667
17
17
  "@nanostores/preact",
18
18
  // fontsource packages are CSS that need to be processed
19
- "@fontsource/*"
19
+ "@fontsource/*",
20
+ // Must be bundled so the prerender output resolves Astro's own copy, not an
21
+ // older hoisted version from another dependency. See https://github.com/withastro/astro/issues/17508
22
+ "neotraverse"
20
23
  ];
21
24
  function vitePluginEnvironment({
22
25
  command,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "7.1.6",
3
+ "version": "7.2.1",
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",
@@ -100,7 +100,6 @@
100
100
  "@capsizecss/unpack": "^4.0.0",
101
101
  "@clack/prompts": "^1.1.0",
102
102
  "@oslojs/encoding": "^1.1.0",
103
- "@rollup/pluginutils": "^5.3.0",
104
103
  "am-i-vibing": "^0.4.0",
105
104
  "aria-query": "^5.3.2",
106
105
  "axobject-query": "^4.1.0",