astro 4.4.15 → 4.5.0

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 (95) hide show
  1. package/components/Code.astro +15 -12
  2. package/dist/@types/astro.d.ts +95 -18
  3. package/dist/assets/utils/getAssetsPrefix.d.ts +2 -0
  4. package/dist/assets/utils/getAssetsPrefix.js +14 -0
  5. package/dist/assets/vite-plugin-assets.js +10 -3
  6. package/dist/content/types-generator.js +56 -7
  7. package/dist/content/vite-plugin-content-assets.js +11 -3
  8. package/dist/core/app/common.js +2 -0
  9. package/dist/core/app/index.js +10 -2
  10. package/dist/core/app/types.d.ts +7 -2
  11. package/dist/core/base-pipeline.d.ts +2 -1
  12. package/dist/core/base-pipeline.js +2 -1
  13. package/dist/core/build/generate.js +1 -0
  14. package/dist/core/build/internal.d.ts +6 -0
  15. package/dist/core/build/internal.js +1 -0
  16. package/dist/core/build/plugins/index.js +6 -1
  17. package/dist/core/build/plugins/plugin-analyzer.js +10 -98
  18. package/dist/core/build/plugins/plugin-css.js +27 -1
  19. package/dist/core/build/plugins/plugin-manifest.js +5 -2
  20. package/dist/core/build/plugins/plugin-scripts.d.ts +8 -0
  21. package/dist/core/build/plugins/plugin-scripts.js +34 -0
  22. package/dist/core/compile/compile.d.ts +1 -7
  23. package/dist/core/compile/compile.js +5 -4
  24. package/dist/core/compile/style.d.ts +4 -3
  25. package/dist/core/compile/style.js +5 -4
  26. package/dist/core/compile/types.d.ts +11 -0
  27. package/dist/core/config/schema.d.ts +177 -113
  28. package/dist/core/config/schema.js +42 -9
  29. package/dist/core/config/vite-load.js +1 -0
  30. package/dist/core/constants.d.ts +1 -0
  31. package/dist/core/constants.js +3 -1
  32. package/dist/core/create-vite.js +5 -3
  33. package/dist/core/dev/dev.js +1 -1
  34. package/dist/core/errors/dev/vite.js +1 -1
  35. package/dist/core/messages.js +2 -2
  36. package/dist/core/render/params-and-props.js +2 -1
  37. package/dist/core/render/ssr-element.d.ts +8 -8
  38. package/dist/core/render/ssr-element.js +4 -2
  39. package/dist/core/render-context.js +3 -1
  40. package/dist/core/routing/astro-designed-error-pages.d.ts +2 -0
  41. package/dist/core/routing/astro-designed-error-pages.js +21 -0
  42. package/dist/runtime/client/dev-toolbar/apps/audit/index.d.ts +8 -15
  43. package/dist/runtime/client/dev-toolbar/apps/audit/index.js +130 -249
  44. package/dist/runtime/client/dev-toolbar/apps/audit/{a11y.js → rules/a11y.js} +4 -2
  45. package/dist/runtime/client/dev-toolbar/apps/audit/rules/index.d.ts +35 -0
  46. package/dist/runtime/client/dev-toolbar/apps/audit/rules/index.js +40 -0
  47. package/dist/runtime/client/dev-toolbar/apps/audit/{perf.js → rules/perf.js} +2 -2
  48. package/dist/runtime/client/dev-toolbar/apps/audit/ui/audit-list-item.d.ts +7 -0
  49. package/dist/runtime/client/dev-toolbar/apps/audit/ui/audit-list-item.js +137 -0
  50. package/dist/runtime/client/dev-toolbar/apps/audit/ui/audit-list-window.d.ts +23 -0
  51. package/dist/runtime/client/dev-toolbar/apps/audit/ui/audit-list-window.js +384 -0
  52. package/dist/runtime/client/dev-toolbar/apps/audit/ui/audit-ui.d.ts +6 -0
  53. package/dist/runtime/client/dev-toolbar/apps/audit/ui/audit-ui.js +126 -0
  54. package/dist/runtime/client/dev-toolbar/apps/utils/window.d.ts +1 -1
  55. package/dist/runtime/client/dev-toolbar/apps/utils/window.js +3 -1
  56. package/dist/runtime/client/dev-toolbar/entrypoint.js +43 -15
  57. package/dist/runtime/client/dev-toolbar/settings.d.ts +3 -1
  58. package/dist/runtime/client/dev-toolbar/settings.js +8 -2
  59. package/dist/runtime/client/dev-toolbar/toolbar.d.ts +1 -0
  60. package/dist/runtime/client/dev-toolbar/toolbar.js +10 -8
  61. package/dist/runtime/client/dev-toolbar/ui-library/badge.d.ts +14 -4
  62. package/dist/runtime/client/dev-toolbar/ui-library/badge.js +72 -33
  63. package/dist/runtime/client/dev-toolbar/ui-library/button.d.ts +14 -4
  64. package/dist/runtime/client/dev-toolbar/ui-library/button.js +100 -47
  65. package/dist/runtime/client/dev-toolbar/ui-library/card.d.ts +9 -0
  66. package/dist/runtime/client/dev-toolbar/ui-library/card.js +57 -2
  67. package/dist/runtime/client/dev-toolbar/ui-library/highlight.d.ts +9 -0
  68. package/dist/runtime/client/dev-toolbar/ui-library/highlight.js +54 -2
  69. package/dist/runtime/client/dev-toolbar/ui-library/icons.d.ts +4 -0
  70. package/dist/runtime/client/dev-toolbar/ui-library/icons.js +5 -1
  71. package/dist/runtime/client/dev-toolbar/ui-library/toggle.d.ts +9 -0
  72. package/dist/runtime/client/dev-toolbar/ui-library/toggle.js +64 -5
  73. package/dist/runtime/compiler/index.d.ts +1 -1
  74. package/dist/runtime/compiler/index.js +2 -0
  75. package/dist/runtime/server/hydration.js +3 -2
  76. package/dist/runtime/server/index.d.ts +1 -1
  77. package/dist/runtime/server/index.js +2 -0
  78. package/dist/runtime/server/render/astro/factory.d.ts +1 -1
  79. package/dist/runtime/server/render/index.d.ts +1 -0
  80. package/dist/runtime/server/render/index.js +2 -0
  81. package/dist/runtime/server/render/script.d.ts +6 -0
  82. package/dist/runtime/server/render/script.js +15 -0
  83. package/dist/transitions/router.js +12 -3
  84. package/dist/vite-plugin-astro/index.d.ts +2 -2
  85. package/dist/vite-plugin-astro/index.js +12 -1
  86. package/dist/vite-plugin-astro/types.d.ts +21 -1
  87. package/dist/vite-plugin-astro-server/pipeline.js +6 -2
  88. package/dist/vite-plugin-astro-server/plugin.js +6 -2
  89. package/dist/vite-plugin-astro-server/response.d.ts +6 -0
  90. package/dist/vite-plugin-astro-server/response.js +13 -0
  91. package/dist/vite-plugin-astro-server/route.js +18 -2
  92. package/package.json +7 -8
  93. package/tsconfigs/base.json +3 -1
  94. /package/dist/runtime/client/dev-toolbar/apps/audit/{a11y.d.ts → rules/a11y.d.ts} +0 -0
  95. /package/dist/runtime/client/dev-toolbar/apps/audit/{perf.d.ts → rules/perf.d.ts} +0 -0
@@ -6,34 +6,35 @@ import type {
6
6
  SpecialLanguage,
7
7
  ThemeRegistration,
8
8
  ThemeRegistrationRaw,
9
- } from 'shikiji';
10
- import { bundledLanguages } from 'shikiji/langs';
9
+ } from 'shiki';
10
+ import { bundledLanguages } from 'shiki/langs';
11
11
  import { getCachedHighlighter } from '../dist/core/shiki.js';
12
+ import type { HTMLAttributes } from '../types';
12
13
 
13
- interface Props {
14
+ interface Props extends Omit<HTMLAttributes<'pre'>, 'lang'> {
14
15
  /** The code to highlight. Required. */
15
16
  code: string;
16
17
  /**
17
18
  * The language of your code.
18
- * Supports all languages listed here: https://github.com/shikijs/shiki/blob/main/docs/languages.md#all-languages
19
- * Instructions for loading a custom language: https://github.com/shikijs/shiki/blob/main/docs/languages.md#supporting-your-own-languages-with-shiki
19
+ * Supports all languages listed here: https://shiki.style/languages
20
+ * Instructions for loading a custom language: https://shiki.style/guide/load-lang
20
21
  *
21
22
  * @default "plaintext"
22
23
  */
23
24
  lang?: BuiltinLanguage | SpecialLanguage | LanguageRegistration;
24
25
  /**
25
26
  * The styling theme.
26
- * Supports all themes listed here: https://github.com/shikijs/shiki/blob/main/docs/themes.md#all-themes
27
- * Instructions for loading a custom theme: https://github.com/shikijs/shiki/blob/main/docs/themes.md#loading-theme
27
+ * Supports all themes listed here: https://shiki.style/themes
28
+ * Instructions for loading a custom theme: https://shiki.style/guide/load-theme
28
29
  *
29
30
  * @default "github-dark"
30
31
  */
31
32
  theme?: ThemePresets | ThemeRegistration | ThemeRegistrationRaw;
32
33
  /**
33
34
  * Multiple themes to style with -- alternative to "theme" option.
34
- * Supports all themes found above; see https://github.com/antfu/shikiji#lightdark-dual-themes for more information.
35
+ * Supports all themes found above; see https://shiki.style/guide/dual-themes for more information.
35
36
  */
36
- experimentalThemes?: Record<string, ThemePresets | ThemeRegistration | ThemeRegistrationRaw>;
37
+ themes?: Record<string, ThemePresets | ThemeRegistration | ThemeRegistrationRaw>;
37
38
  /**
38
39
  * Enable word wrapping.
39
40
  * - true: enabled.
@@ -55,12 +56,13 @@ const {
55
56
  code,
56
57
  lang = 'plaintext',
57
58
  theme = 'github-dark',
58
- experimentalThemes = {},
59
+ themes = {},
59
60
  wrap = false,
60
61
  inline = false,
62
+ ...rest
61
63
  } = Astro.props;
62
64
 
63
- // shiki -> shikiji compat
65
+ // shiki 1.0 compat
64
66
  if (typeof lang === 'object') {
65
67
  // `id` renamed to `name` (always override)
66
68
  if ((lang as any).id) {
@@ -81,12 +83,13 @@ const highlighter = await getCachedHighlighter({
81
83
  : lang,
82
84
  ],
83
85
  theme,
84
- experimentalThemes,
86
+ themes,
85
87
  wrap,
86
88
  });
87
89
 
88
90
  const html = highlighter.highlight(code, typeof lang === 'string' ? lang : lang.name, {
89
91
  inline,
92
+ attributes: rest,
90
93
  });
91
94
  ---
92
95
 
@@ -7,7 +7,7 @@ import type * as babel from '@babel/core';
7
7
  import type * as rollup from 'rollup';
8
8
  import type * as vite from 'vite';
9
9
  import type { RemotePattern } from '../assets/utils/remotePattern.js';
10
- import type { SerializedSSRManifest } from '../core/app/types.js';
10
+ import type { AssetsPrefix, SerializedSSRManifest } from '../core/app/types.js';
11
11
  import type { PageBuildData } from '../core/build/types.js';
12
12
  import type { AstroConfigType } from '../core/config/index.js';
13
13
  import type { AstroTimer } from '../core/config/timer.js';
@@ -27,7 +27,7 @@ export type { MarkdownHeading, MarkdownMetadata, MarkdownRenderingResult, Rehype
27
27
  export type { ExternalImageService, ImageService, LocalImageService, } from '../assets/services/service.js';
28
28
  export type { GetImageResult, ImageInputFormat, ImageMetadata, ImageOutputFormat, ImageQuality, ImageQualityPreset, ImageTransform, UnresolvedImageTransform, } from '../assets/types.js';
29
29
  export type { RemotePattern } from '../assets/utils/remotePattern.js';
30
- export type { SSRManifest } from '../core/app/types.js';
30
+ export type { SSRManifest, AssetsPrefix } from '../core/app/types.js';
31
31
  export type { AstroCookieGetOptions, AstroCookieSetOptions, AstroCookies, } from '../core/cookies/index.js';
32
32
  export interface AstroBuiltinProps {
33
33
  'client:load'?: boolean;
@@ -779,17 +779,17 @@ export interface AstroUserConfig {
779
779
  /**
780
780
  * @docs
781
781
  * @name build.assetsPrefix
782
- * @type {string}
782
+ * @type {string | Record<string, string>}
783
783
  * @default `undefined`
784
784
  * @version 2.2.0
785
785
  * @description
786
786
  * Specifies the prefix for Astro-generated asset links. This can be used if assets are served from a different domain than the current site.
787
787
  *
788
- * For example, if this is set to `https://cdn.example.com`, assets will be fetched from `https://cdn.example.com/_astro/...` (regardless of the `base` option).
789
- * You would need to upload the files in `./dist/_astro/` to `https://cdn.example.com/_astro/` to serve the assets.
790
- * The process varies depending on how the third-party domain is hosted.
788
+ * This requires uploading the assets in your local `./dist/_astro` folder to a corresponding `/_astro/` folder on the remote domain.
791
789
  * To rename the `_astro` path, specify a new directory in `build.assets`.
792
790
  *
791
+ * To fetch all assets uploaded to the same domain (e.g. `https://cdn.example.com/_astro/...`), set `assetsPrefix` to the root domain as a string (regardless of your `base` configuration):
792
+ *
793
793
  * ```js
794
794
  * {
795
795
  * build: {
@@ -797,8 +797,27 @@ export interface AstroUserConfig {
797
797
  * }
798
798
  * }
799
799
  * ```
800
+ *
801
+ * **Added in:** `astro@4.5.0`
802
+ *
803
+ * You can also pass an object to `assetsPrefix` to specify a different domain for each file type.
804
+ * In this case, a `fallback` property is required and will be used by default for any other files.
805
+ *
806
+ * ```js
807
+ * {
808
+ * build: {
809
+ * assetsPrefix: {
810
+ * 'js': 'https://js.cdn.example.com',
811
+ * 'mjs': 'https://js.cdn.example.com',
812
+ * 'css': 'https://css.cdn.example.com',
813
+ * 'fallback': 'https://cdn.example.com'
814
+ * }
815
+ * }
816
+ * }
817
+ * ```
818
+ *
800
819
  */
801
- assetsPrefix?: string;
820
+ assetsPrefix?: AssetsPrefix;
802
821
  /**
803
822
  * @docs
804
823
  * @name build.serverEntry
@@ -1177,7 +1196,7 @@ export interface AstroUserConfig {
1177
1196
  * @default `shiki`
1178
1197
  * @description
1179
1198
  * Which syntax highlighter to use, if any.
1180
- * - `shiki` - use the [Shiki](https://github.com/shikijs/shiki) highlighter
1199
+ * - `shiki` - use the [Shiki](https://shiki.style) highlighter
1181
1200
  * - `prism` - use the [Prism](https://prismjs.com/) highlighter
1182
1201
  * - `false` - do not apply syntax highlighting.
1183
1202
  *
@@ -1478,25 +1497,30 @@ export interface AstroUserConfig {
1478
1497
  experimental?: {
1479
1498
  /**
1480
1499
  * @docs
1481
- * @name experimental.optimizeHoistedScript
1500
+ * @name experimental.directRenderScript
1482
1501
  * @type {boolean}
1483
1502
  * @default `false`
1484
- * @version 2.10.4
1503
+ * @version 4.5.0
1485
1504
  * @description
1486
- * Prevents unused components' scripts from being included in a page unexpectedly.
1487
- * The optimization is best-effort and may inversely miss including the used scripts. Make sure to double-check your built pages
1488
- * before publishing.
1489
- * Enable hoisted script analysis optimization by adding the experimental flag:
1505
+ * Enables a more reliable strategy to prevent scripts from being executed in pages where they are not used.
1506
+ *
1507
+ * Scripts will directly render as declared in Astro files (including existing features like TypeScript, importing `node_modules`,
1508
+ * and deduplicating scripts). You can also now conditionally render scripts in your Astro file.
1509
+
1510
+ * However, this means scripts are no longer hoisted to the `<head>` and multiple scripts on a page are no longer bundled together.
1511
+ * If you enable this option, you should check that all your `<script>` tags behave as expected.
1512
+ *
1513
+ * This option will be enabled by default in Astro 5.0.
1490
1514
  *
1491
1515
  * ```js
1492
1516
  * {
1493
- * experimental: {
1494
- * optimizeHoistedScript: true,
1495
- * },
1517
+ * experimental: {
1518
+ * directRenderScript: true,
1519
+ * },
1496
1520
  * }
1497
1521
  * ```
1498
1522
  */
1499
- optimizeHoistedScript?: boolean;
1523
+ directRenderScript?: boolean;
1500
1524
  /**
1501
1525
  * @docs
1502
1526
  * @name experimental.contentCollectionCache
@@ -1515,6 +1539,53 @@ export interface AstroUserConfig {
1515
1539
  * ```
1516
1540
  */
1517
1541
  contentCollectionCache?: boolean;
1542
+ /**
1543
+ * @docs
1544
+ * @name experimental.contentCollectionJsonSchema
1545
+ * @type {boolean}
1546
+ * @default `false`
1547
+ * @version 4.5.0
1548
+ * @description
1549
+ * This feature will auto-generate a JSON schema for content collections of `type: 'data'` which can be used as the `$schema` value for TypeScript-style autocompletion/hints in tools like VSCode.
1550
+ *
1551
+ * To enable this feature, add the experimental flag:
1552
+ *
1553
+ * ```diff
1554
+ * import { defineConfig } from 'astro/config';
1555
+
1556
+ * export default defineConfig({
1557
+ * experimental: {
1558
+ * + contentCollectionJsonSchema: true
1559
+ * }
1560
+ * });
1561
+ * ```
1562
+ *
1563
+ * This experimental implementation requires you to manually reference the schema in each data entry file of the collection:
1564
+ *
1565
+ * ```diff
1566
+ * // src/content/test/entry.json
1567
+ * {
1568
+ * + "$schema": "../../../.astro/collections/test.schema.json",
1569
+ * "test": "test"
1570
+ * }
1571
+ * ```
1572
+ *
1573
+ * Alternatively, you can set this in your [VSCode `json.schemas` settings](https://code.visualstudio.com/docs/languages/json#_json-schemas-and-settings):
1574
+ *
1575
+ * ```diff
1576
+ * "json.schemas": [
1577
+ * {
1578
+ * "fileMatch": [
1579
+ * "/src/content/test/**"
1580
+ * ],
1581
+ * "url": "../../../.astro/collections/test.schema.json"
1582
+ * }
1583
+ * ]
1584
+ * ```
1585
+ *
1586
+ * Note that this initial implementation uses a library with [known issues for advanced Zod schemas](https://github.com/StefanTerdell/zod-to-json-schema#known-issues), so you may wish to consult these limitations before enabling the experimental flag.
1587
+ */
1588
+ contentCollectionJsonSchema?: boolean;
1518
1589
  /**
1519
1590
  * @docs
1520
1591
  * @name experimental.clientPrerender
@@ -2411,6 +2482,7 @@ export interface SSRResult {
2411
2482
  scripts: Set<SSRElement>;
2412
2483
  links: Set<SSRElement>;
2413
2484
  componentMetadata: Map<string, SSRComponentMetadata>;
2485
+ inlinedScripts: Map<string, string>;
2414
2486
  createAstro(Astro: AstroGlobalPartial, props: Record<string, any>, slots: Record<string, any> | null): AstroGlobal;
2415
2487
  resolve: (s: string) => Promise<string>;
2416
2488
  response: AstroGlobal['response'];
@@ -2440,6 +2512,11 @@ export interface SSRMetadata {
2440
2512
  * script in the page HTML before the first Solid component.
2441
2513
  */
2442
2514
  rendererSpecificHydrationScripts: Set<string>;
2515
+ /**
2516
+ * Used by `renderScript` to track script ids that have been rendered,
2517
+ * so we only render each once.
2518
+ */
2519
+ renderedScripts: Set<string>;
2443
2520
  hasDirectives: Set<string>;
2444
2521
  hasRenderedHead: boolean;
2445
2522
  headInTree: boolean;
@@ -0,0 +1,2 @@
1
+ import type { AssetsPrefix } from '../../core/app/types.js';
2
+ export declare function getAssetsPrefix(fileExtension: string, assetsPrefix?: AssetsPrefix): string;
@@ -0,0 +1,14 @@
1
+ function getAssetsPrefix(fileExtension, assetsPrefix) {
2
+ if (!assetsPrefix)
3
+ return "";
4
+ if (typeof assetsPrefix === "string")
5
+ return assetsPrefix;
6
+ const dotLessFileExtension = fileExtension.slice(1);
7
+ if (assetsPrefix[dotLessFileExtension]) {
8
+ return assetsPrefix[dotLessFileExtension];
9
+ }
10
+ return assetsPrefix.fallback;
11
+ }
12
+ export {
13
+ getAssetsPrefix
14
+ };
@@ -1,3 +1,4 @@
1
+ import { extname } from "node:path";
1
2
  import MagicString from "magic-string";
2
3
  import { normalizePath } from "vite";
3
4
  import { extendManualChunks } from "../core/build/plugins/util.js";
@@ -11,6 +12,7 @@ import {
11
12
  import { isServerLikeOutput } from "../prerender/utils.js";
12
13
  import { VALID_INPUT_FORMATS, VIRTUAL_MODULE_ID, VIRTUAL_SERVICE_ID } from "./consts.js";
13
14
  import { emitESMImage } from "./utils/emitAsset.js";
15
+ import { getAssetsPrefix } from "./utils/getAssetsPrefix.js";
14
16
  import { isESMImportedImage } from "./utils/imageKind.js";
15
17
  import { getProxyCode } from "./utils/proxy.js";
16
18
  import { hashTransform, propsToFilename } from "./utils/transformToPath.js";
@@ -73,7 +75,10 @@ function assets({
73
75
  if (!globalThis.astroAsset.staticImages) {
74
76
  globalThis.astroAsset.staticImages = /* @__PURE__ */ new Map();
75
77
  }
76
- const finalOriginalImagePath = (isESMImportedImage(options.src) ? options.src.src : options.src).replace(settings.config.build.assetsPrefix || "", "");
78
+ const ESMImportedImageSrc = isESMImportedImage(options.src) ? options.src.src : options.src;
79
+ const fileExtension = extname(ESMImportedImageSrc);
80
+ const pf = getAssetsPrefix(fileExtension, settings.config.build.assetsPrefix);
81
+ const finalOriginalImagePath = ESMImportedImageSrc.replace(pf, "");
77
82
  const hash = hashTransform(
78
83
  options,
79
84
  settings.config.image.service.entrypoint,
@@ -101,7 +106,7 @@ function assets({
101
106
  });
102
107
  }
103
108
  if (settings.config.build.assetsPrefix) {
104
- return encodeURI(joinPaths(settings.config.build.assetsPrefix, finalFilePath));
109
+ return encodeURI(joinPaths(pf, finalFilePath));
105
110
  } else {
106
111
  return encodeURI(prependForwardSlash(joinPaths(settings.config.base, finalFilePath)));
107
112
  }
@@ -116,7 +121,9 @@ function assets({
116
121
  s = s || (s = new MagicString(code));
117
122
  const [full, hash, postfix = ""] = match;
118
123
  const file = this.getFileName(hash);
119
- const prefix = settings.config.build.assetsPrefix ? appendForwardSlash(settings.config.build.assetsPrefix) : resolvedConfig.base;
124
+ const fileExtension = extname(file);
125
+ const pf = getAssetsPrefix(fileExtension, settings.config.build.assetsPrefix);
126
+ const prefix = pf ? appendForwardSlash(pf) : resolvedConfig.base;
120
127
  const outputFilepath = prefix + normalizePath(file + postfix);
121
128
  s.overwrite(match.index, match.index + full.length, outputFilepath);
122
129
  }
@@ -3,6 +3,8 @@ import { fileURLToPath, pathToFileURL } from "node:url";
3
3
  import glob from "fast-glob";
4
4
  import { bold, cyan } from "kleur/colors";
5
5
  import { normalizePath } from "vite";
6
+ import { z } from "zod";
7
+ import { zodToJsonSchema } from "zod-to-json-schema";
6
8
  import { AstroError } from "../core/errors/errors.js";
7
9
  import { AstroErrorData } from "../core/errors/index.js";
8
10
  import { isRelativePath } from "../core/path.js";
@@ -72,7 +74,10 @@ async function createContentTypesGenerator({
72
74
  return { shouldGenerateTypes: false };
73
75
  switch (event.name) {
74
76
  case "addDir":
75
- collectionEntryMap[JSON.stringify(collection2)] = { type: "unknown", entries: {} };
77
+ collectionEntryMap[JSON.stringify(collection2)] = {
78
+ type: "unknown",
79
+ entries: {}
80
+ };
76
81
  logger.debug("content", `${cyan(collection2)} collection added`);
77
82
  break;
78
83
  case "unlinkDir":
@@ -150,7 +155,11 @@ async function createContentTypesGenerator({
150
155
  const contentEntryType = contentEntryConfigByExt.get(path.extname(event.entry.pathname));
151
156
  if (!contentEntryType)
152
157
  return { shouldGenerateTypes: false };
153
- const { id, slug: generatedSlug } = getContentEntryIdAndSlug({ entry, contentDir, collection });
158
+ const { id, slug: generatedSlug } = getContentEntryIdAndSlug({
159
+ entry,
160
+ contentDir,
161
+ collection
162
+ });
154
163
  const collectionKey = JSON.stringify(collection);
155
164
  if (!(collectionKey in collectionEntryMap)) {
156
165
  collectionEntryMap[collectionKey] = { type: "content", entries: {} };
@@ -181,7 +190,10 @@ async function createContentTypesGenerator({
181
190
  if (!(entryKey in collectionEntryMap[collectionKey].entries)) {
182
191
  collectionEntryMap[collectionKey] = {
183
192
  type: "content",
184
- entries: { ...collectionInfo.entries, [entryKey]: { slug: addedSlug } }
193
+ entries: {
194
+ ...collectionInfo.entries,
195
+ [entryKey]: { slug: addedSlug }
196
+ }
185
197
  };
186
198
  }
187
199
  return { shouldGenerateTypes: true };
@@ -244,7 +256,9 @@ async function createContentTypesGenerator({
244
256
  typeTemplateContent,
245
257
  contentConfig: observable.status === "loaded" ? observable.config : void 0,
246
258
  contentEntryTypes: settings.contentEntryTypes,
247
- viteServer
259
+ viteServer,
260
+ logger,
261
+ settings
248
262
  });
249
263
  invalidateVirtualMod(viteServer);
250
264
  }
@@ -269,12 +283,21 @@ async function writeContentFiles({
269
283
  typeTemplateContent,
270
284
  contentEntryTypes,
271
285
  contentConfig,
272
- viteServer
286
+ viteServer,
287
+ logger,
288
+ settings
273
289
  }) {
274
290
  let contentTypesStr = "";
275
291
  let dataTypesStr = "";
292
+ const collectionSchemasDir = new URL("./collections/", contentPaths.cacheDir);
293
+ if (settings.config.experimental.contentCollectionJsonSchema && !fs.existsSync(collectionSchemasDir)) {
294
+ fs.mkdirSync(collectionSchemasDir, { recursive: true });
295
+ }
276
296
  for (const [collection, config] of Object.entries(contentConfig?.collections ?? {})) {
277
- collectionEntryMap[JSON.stringify(collection)] ??= { type: config.type, entries: {} };
297
+ collectionEntryMap[JSON.stringify(collection)] ??= {
298
+ type: config.type,
299
+ entries: {}
300
+ };
278
301
  }
279
302
  for (const collectionKey of Object.keys(collectionEntryMap).sort()) {
280
303
  const collectionConfig = contentConfig?.collections[JSON.parse(collectionKey)];
@@ -292,7 +315,6 @@ async function writeContentFiles({
292
315
  hint: collection.type === "data" ? "Try adding `type: 'data'` to your collection config." : void 0,
293
316
  location: {
294
317
  file: ""
295
- /** required for error overlay `hot` messages */
296
318
  }
297
319
  })
298
320
  });
@@ -337,6 +359,33 @@ async function writeContentFiles({
337
359
  data: ${dataType}
338
360
  };
339
361
  `;
362
+ if (settings.config.experimental.contentCollectionJsonSchema && collectionConfig?.schema) {
363
+ let zodSchemaForJson = collectionConfig.schema;
364
+ if (zodSchemaForJson instanceof z.ZodObject) {
365
+ zodSchemaForJson = zodSchemaForJson.extend({
366
+ $schema: z.string().optional()
367
+ });
368
+ }
369
+ try {
370
+ await fs.promises.writeFile(
371
+ new URL(`./${collectionKey.replace(/"/g, "")}.schema.json`, collectionSchemasDir),
372
+ JSON.stringify(
373
+ zodToJsonSchema(zodSchemaForJson, {
374
+ name: collectionKey.replace(/"/g, ""),
375
+ markdownDescription: true,
376
+ errorMessages: true
377
+ }),
378
+ null,
379
+ 2
380
+ )
381
+ );
382
+ } catch (err) {
383
+ logger.warn(
384
+ "content",
385
+ `An error was encountered while creating the JSON schema. Proceeding without it. Error: ${err}`
386
+ );
387
+ }
388
+ }
340
389
  }
341
390
  dataTypesStr += `};
342
391
  `;
@@ -1,5 +1,6 @@
1
1
  import { extname } from "node:path";
2
2
  import { pathToFileURL } from "node:url";
3
+ import { getAssetsPrefix } from "../assets/utils/getAssetsPrefix.js";
3
4
  import { moduleIsTopLevelPage, walkParentInfos } from "../core/build/graph.js";
4
5
  import { getPageDataByViteID } from "../core/build/internal.js";
5
6
  import { createViteLoader } from "../core/module-loader/vite.js";
@@ -54,7 +55,11 @@ function astroContentAssetPropagationPlugin({
54
55
  urls,
55
56
  crawledFiles: styleCrawledFiles
56
57
  } = await getStylesForURL(pathToFileURL(basePath), devModuleLoader);
57
- const { scripts: hoistedScripts, crawledFiles: scriptCrawledFiles } = await getScriptsForURL(pathToFileURL(basePath), settings.config.root, devModuleLoader);
58
+ const { scripts: hoistedScripts, crawledFiles: scriptCrawledFiles } = settings.config.experimental.directRenderScript ? { scripts: /* @__PURE__ */ new Set(), crawledFiles: /* @__PURE__ */ new Set() } : await getScriptsForURL(
59
+ pathToFileURL(basePath),
60
+ settings.config.root,
61
+ devModuleLoader
62
+ );
58
63
  for (const file of styleCrawledFiles) {
59
64
  if (!file.includes("node_modules")) {
60
65
  this.addWatchFile(file);
@@ -108,8 +113,11 @@ function astroConfigBuildPlugin(options, internals) {
108
113
  "build:post": ({ ssrOutputs, clientOutputs, mutate }) => {
109
114
  const outputs = ssrOutputs.flatMap((o) => o.output);
110
115
  const prependBase = (src) => {
111
- if (options.settings.config.build.assetsPrefix) {
112
- return joinPaths(options.settings.config.build.assetsPrefix, src);
116
+ const { assetsPrefix } = options.settings.config.build;
117
+ if (assetsPrefix) {
118
+ const fileExtension = extname(src);
119
+ const pf = getAssetsPrefix(fileExtension, assetsPrefix);
120
+ return joinPaths(pf, src);
113
121
  } else {
114
122
  return prependForwardSlash(joinPaths(options.settings.config.base, src));
115
123
  }
@@ -11,6 +11,7 @@ function deserializeManifest(serializedManifest) {
11
11
  }
12
12
  const assets = new Set(serializedManifest.assets);
13
13
  const componentMetadata = new Map(serializedManifest.componentMetadata);
14
+ const inlinedScripts = new Map(serializedManifest.inlinedScripts);
14
15
  const clientDirectives = new Map(serializedManifest.clientDirectives);
15
16
  return {
16
17
  // in case user middleware exists, this no-op middleware will be reassigned (see plugin-ssr.ts)
@@ -20,6 +21,7 @@ function deserializeManifest(serializedManifest) {
20
21
  ...serializedManifest,
21
22
  assets,
22
23
  componentMetadata,
24
+ inlinedScripts,
23
25
  clientDirectives,
24
26
  routes
25
27
  };
@@ -1,5 +1,6 @@
1
1
  import { normalizeTheLocale } from "../../i18n/index.js";
2
2
  import {
3
+ DEFAULT_404_COMPONENT,
3
4
  REROUTABLE_STATUS_CODES,
4
5
  REROUTE_DIRECTIVE_HEADER,
5
6
  clientAddressSymbol,
@@ -20,6 +21,7 @@ import {
20
21
  import { RedirectSinglePageBuiltModule } from "../redirects/index.js";
21
22
  import { RenderContext } from "../render-context.js";
22
23
  import { createAssetLink } from "../render/ssr-element.js";
24
+ import { ensure404Route } from "../routing/astro-designed-error-pages.js";
23
25
  import { matchRoute } from "../routing/match.js";
24
26
  import { AppPipeline } from "./pipeline.js";
25
27
  import { deserializeManifest } from "./common.js";
@@ -36,9 +38,9 @@ class App {
36
38
  #renderOptionsDeprecationWarningShown = false;
37
39
  constructor(manifest, streaming = true) {
38
40
  this.#manifest = manifest;
39
- this.#manifestData = {
41
+ this.#manifestData = ensure404Route({
40
42
  routes: manifest.routes.map((route) => route.routeData)
41
- };
43
+ });
42
44
  this.#baseWithoutTrailingSlash = removeTrailingForwardSlash(this.#manifest.base);
43
45
  this.#pipeline = this.#createPipeline(streaming);
44
46
  this.#adapterLogger = new AstroIntegrationLogger(
@@ -348,6 +350,12 @@ class App {
348
350
  return 200;
349
351
  }
350
352
  async #getModuleForRoute(route) {
353
+ if (route.component === DEFAULT_404_COMPONENT) {
354
+ return {
355
+ page: async () => ({ default: () => new Response(null, { status: 404 }) }),
356
+ renderers: []
357
+ };
358
+ }
351
359
  if (route.type === "redirect") {
352
360
  return RedirectSinglePageBuiltModule;
353
361
  } else {
@@ -26,6 +26,9 @@ export type SerializedRouteInfo = Omit<RouteInfo, 'routeData'> & {
26
26
  routeData: SerializedRouteData;
27
27
  };
28
28
  export type ImportComponentInstance = () => Promise<SinglePageBuiltModule>;
29
+ export type AssetsPrefix = string | ({
30
+ fallback: string;
31
+ } & Record<string, string>) | undefined;
29
32
  export type SSRManifest = {
30
33
  adapterName: string;
31
34
  routes: RouteInfo[];
@@ -34,13 +37,14 @@ export type SSRManifest = {
34
37
  trailingSlash: 'always' | 'never' | 'ignore';
35
38
  buildFormat: 'file' | 'directory' | 'preserve';
36
39
  compressHTML: boolean;
37
- assetsPrefix?: string;
40
+ assetsPrefix?: AssetsPrefix;
38
41
  renderers: SSRLoadedRenderer[];
39
42
  /**
40
43
  * Map of directive name (e.g. `load`) to the directive script code
41
44
  */
42
45
  clientDirectives: Map<string, string>;
43
46
  entryModules: Record<string, string>;
47
+ inlinedScripts: Map<string, string>;
44
48
  assets: Set<string>;
45
49
  componentMetadata: SSRResult['componentMetadata'];
46
50
  pageModule?: SinglePageBuiltModule;
@@ -55,9 +59,10 @@ export type SSRManifestI18n = {
55
59
  defaultLocale: string;
56
60
  domainLookupTable: Record<string, string>;
57
61
  };
58
- export type SerializedSSRManifest = Omit<SSRManifest, 'middleware' | 'routes' | 'assets' | 'componentMetadata' | 'clientDirectives'> & {
62
+ export type SerializedSSRManifest = Omit<SSRManifest, 'middleware' | 'routes' | 'assets' | 'componentMetadata' | 'inlinedScripts' | 'clientDirectives'> & {
59
63
  routes: SerializedRouteInfo[];
60
64
  assets: string[];
61
65
  componentMetadata: [string, SSRComponentMetadata][];
66
+ inlinedScripts: [string, string][];
62
67
  clientDirectives: [string, string][];
63
68
  };
@@ -26,6 +26,7 @@ export declare abstract class Pipeline {
26
26
  */
27
27
  readonly adapterName: string;
28
28
  readonly clientDirectives: Map<string, string>;
29
+ readonly inlinedScripts: Map<string, string>;
29
30
  readonly compressHTML: boolean;
30
31
  readonly i18n: import("./app/types.js").SSRManifestI18n | undefined;
31
32
  readonly middleware: MiddlewareHandler;
@@ -47,7 +48,7 @@ export declare abstract class Pipeline {
47
48
  /**
48
49
  * Used to provide better error messages for `Astro.clientAddress`
49
50
  */
50
- adapterName?: string, clientDirectives?: Map<string, string>, compressHTML?: boolean, i18n?: import("./app/types.js").SSRManifestI18n | undefined, middleware?: MiddlewareHandler, routeCache?: RouteCache,
51
+ adapterName?: string, clientDirectives?: Map<string, string>, inlinedScripts?: Map<string, string>, compressHTML?: boolean, i18n?: import("./app/types.js").SSRManifestI18n | undefined, middleware?: MiddlewareHandler, routeCache?: RouteCache,
51
52
  /**
52
53
  * Used for `Astro.site`.
53
54
  */
@@ -1,7 +1,7 @@
1
1
  import { createI18nMiddleware } from "../i18n/middleware.js";
2
2
  import { RouteCache } from "./render/route-cache.js";
3
3
  class Pipeline {
4
- constructor(logger, manifest, mode, renderers, resolve, serverLike, streaming, adapterName = manifest.adapterName, clientDirectives = manifest.clientDirectives, compressHTML = manifest.compressHTML, i18n = manifest.i18n, middleware = manifest.middleware, routeCache = new RouteCache(logger, mode), site = manifest.site ? new URL(manifest.site) : void 0) {
4
+ constructor(logger, manifest, mode, renderers, resolve, serverLike, streaming, adapterName = manifest.adapterName, clientDirectives = manifest.clientDirectives, inlinedScripts = manifest.inlinedScripts, compressHTML = manifest.compressHTML, i18n = manifest.i18n, middleware = manifest.middleware, routeCache = new RouteCache(logger, mode), site = manifest.site ? new URL(manifest.site) : void 0) {
5
5
  this.logger = logger;
6
6
  this.manifest = manifest;
7
7
  this.mode = mode;
@@ -11,6 +11,7 @@ class Pipeline {
11
11
  this.streaming = streaming;
12
12
  this.adapterName = adapterName;
13
13
  this.clientDirectives = clientDirectives;
14
+ this.inlinedScripts = inlinedScripts;
14
15
  this.compressHTML = compressHTML;
15
16
  this.i18n = i18n;
16
17
  this.middleware = middleware;
@@ -421,6 +421,7 @@ function createBuildManifest(settings, internals, renderers, middleware) {
421
421
  trailingSlash: settings.config.trailingSlash,
422
422
  assets: /* @__PURE__ */ new Set(),
423
423
  entryModules: Object.fromEntries(internals.entrySpecifierToBundleMap.entries()),
424
+ inlinedScripts: internals.inlinedScripts,
424
425
  routes: [],
425
426
  adapterName: "",
426
427
  clientDirectives: settings.clientDirectives,
@@ -13,6 +13,12 @@ export interface BuildInternals {
13
13
  cssModuleToChunkIdMap: Map<string, string>;
14
14
  hoistedScriptIdToHoistedMap: Map<string, Set<string>>;
15
15
  hoistedScriptIdToPagesMap: Map<string, Set<string>>;
16
+ /**
17
+ * Used by the `directRenderScript` option. If script is inlined, its id and
18
+ * inlined code is mapped here. The resolved id is an URL like "/_astro/something.js"
19
+ * but will no longer exist as the content is now inlined in this map.
20
+ */
21
+ inlinedScripts: Map<string, string>;
16
22
  entrySpecifierToBundleMap: Map<string, string>;
17
23
  /**
18
24
  * A map to get a specific page's bundled output file.
@@ -14,6 +14,7 @@ function createBuildInternals() {
14
14
  cssModuleToChunkIdMap: /* @__PURE__ */ new Map(),
15
15
  hoistedScriptIdToHoistedMap,
16
16
  hoistedScriptIdToPagesMap,
17
+ inlinedScripts: /* @__PURE__ */ new Map(),
17
18
  entrySpecifierToBundleMap: /* @__PURE__ */ new Map(),
18
19
  pageToBundleMap: /* @__PURE__ */ new Map(),
19
20
  pagesByComponent: /* @__PURE__ */ new Map(),