astro 2.3.3 → 2.4.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 (76) hide show
  1. package/client-base.d.ts +26 -1
  2. package/components/Code.astro +46 -21
  3. package/components/shiki-languages.js +172 -2016
  4. package/components/shiki-themes.js +41 -31
  5. package/dist/@types/app.d.js +0 -0
  6. package/dist/@types/astro.d.ts +108 -1
  7. package/dist/assets/internal.d.ts +6 -17
  8. package/dist/assets/internal.js +37 -4
  9. package/dist/content/runtime.js +8 -3
  10. package/dist/content/vite-plugin-content-assets.js +14 -4
  11. package/dist/content/vite-plugin-content-imports.js +1 -1
  12. package/dist/core/app/index.js +50 -10
  13. package/dist/core/app/types.d.ts +10 -1
  14. package/dist/core/build/generate.js +65 -30
  15. package/dist/core/build/internal.d.ts +10 -6
  16. package/dist/core/build/internal.js +31 -39
  17. package/dist/core/build/page-data.js +2 -2
  18. package/dist/core/build/plugins/plugin-component-entry.d.ts +1 -0
  19. package/dist/core/build/plugins/plugin-component-entry.js +1 -0
  20. package/dist/core/build/plugins/plugin-css.d.ts +1 -8
  21. package/dist/core/build/plugins/plugin-css.js +185 -150
  22. package/dist/core/build/plugins/plugin-pages.d.ts +1 -1
  23. package/dist/core/build/plugins/plugin-pages.js +13 -2
  24. package/dist/core/build/plugins/plugin-ssr.d.ts +2 -2
  25. package/dist/core/build/plugins/plugin-ssr.js +20 -7
  26. package/dist/core/build/static-build.js +4 -3
  27. package/dist/core/build/types.d.ts +15 -6
  28. package/dist/core/compile/compile.js +1 -0
  29. package/dist/core/config/config.js +5 -1
  30. package/dist/core/config/schema.d.ts +40 -0
  31. package/dist/core/config/schema.js +10 -2
  32. package/dist/core/constants.d.ts +1 -0
  33. package/dist/core/constants.js +3 -1
  34. package/dist/core/dev/dev.js +1 -1
  35. package/dist/core/endpoint/dev/index.js +7 -4
  36. package/dist/core/endpoint/index.d.ts +9 -2
  37. package/dist/core/endpoint/index.js +42 -24
  38. package/dist/core/errors/errors-data.d.ts +81 -0
  39. package/dist/core/errors/errors-data.js +84 -0
  40. package/dist/core/messages.js +2 -2
  41. package/dist/core/middleware/callMiddleware.d.ts +36 -0
  42. package/dist/core/middleware/callMiddleware.js +38 -0
  43. package/dist/core/middleware/index.d.ts +4 -0
  44. package/dist/core/middleware/index.js +8 -0
  45. package/dist/core/middleware/loadMiddleware.d.ts +8 -0
  46. package/dist/core/middleware/loadMiddleware.js +13 -0
  47. package/dist/core/middleware/sequence.d.ts +6 -0
  48. package/dist/core/middleware/sequence.js +27 -0
  49. package/dist/core/path.js +9 -1
  50. package/dist/core/render/context.d.ts +7 -2
  51. package/dist/core/render/context.js +13 -2
  52. package/dist/core/render/core.d.ts +22 -2
  53. package/dist/core/render/core.js +68 -32
  54. package/dist/core/render/dev/index.d.ts +5 -1
  55. package/dist/core/render/dev/index.js +23 -3
  56. package/dist/core/render/index.d.ts +1 -1
  57. package/dist/core/render/index.js +7 -1
  58. package/dist/core/render/result.d.ts +1 -0
  59. package/dist/core/render/result.js +2 -1
  60. package/dist/core/render/ssr-element.d.ts +3 -2
  61. package/dist/core/render/ssr-element.js +22 -15
  62. package/dist/core/request.js +2 -0
  63. package/dist/integrations/index.js +1 -1
  64. package/dist/runtime/server/endpoint.js +1 -1
  65. package/dist/runtime/server/index.d.ts +1 -1
  66. package/dist/runtime/server/index.js +0 -2
  67. package/dist/runtime/server/render/head.js +3 -1
  68. package/dist/runtime/server/render/index.d.ts +1 -1
  69. package/dist/runtime/server/render/index.js +1 -2
  70. package/dist/runtime/server/render/tags.d.ts +2 -7
  71. package/dist/runtime/server/render/tags.js +9 -27
  72. package/dist/vite-plugin-astro-server/response.js +3 -3
  73. package/dist/vite-plugin-astro-server/route.js +7 -0
  74. package/dist/vite-plugin-env/index.js +6 -1
  75. package/dist/vite-plugin-jsx/index.js +2 -1
  76. package/package.json +12 -9
@@ -1,33 +1,43 @@
1
+ /**
2
+ * This file is prebuilt from packages/astro/scripts/shiki-gen-themes.mjs
3
+ * Do not edit this directly, but instead edit that file and rerun it to generate this file.
4
+ */
5
+
6
+ // prettier-ignore
1
7
  export const themes = {
2
- 'css-variables': () => import('shiki/themes/css-variables.json').then((mod) => mod.default),
3
- 'dark-plus': () => import('shiki/themes/dark-plus.json').then((mod) => mod.default),
4
- 'dracula-soft': () => import('shiki/themes/dracula-soft.json').then((mod) => mod.default),
5
- dracula: () => import('shiki/themes/dracula.json').then((mod) => mod.default),
6
- 'github-dark-dimmed': () =>
7
- import('shiki/themes/github-dark-dimmed.json').then((mod) => mod.default),
8
- 'github-dark': () => import('shiki/themes/github-dark.json').then((mod) => mod.default),
9
- 'github-light': () => import('shiki/themes/github-light.json').then((mod) => mod.default),
10
- hc_light: () => import('shiki/themes/hc_light.json').then((mod) => mod.default),
11
- 'light-plus': () => import('shiki/themes/light-plus.json').then((mod) => mod.default),
12
- 'material-darker': () => import('shiki/themes/material-darker.json').then((mod) => mod.default),
13
- 'material-default': () => import('shiki/themes/material-default.json').then((mod) => mod.default),
14
- 'material-lighter': () => import('shiki/themes/material-lighter.json').then((mod) => mod.default),
15
- 'material-ocean': () => import('shiki/themes/material-ocean.json').then((mod) => mod.default),
16
- 'material-palenight': () =>
17
- import('shiki/themes/material-palenight.json').then((mod) => mod.default),
18
- 'min-dark': () => import('shiki/themes/min-dark.json').then((mod) => mod.default),
19
- 'min-light': () => import('shiki/themes/min-light.json').then((mod) => mod.default),
20
- monokai: () => import('shiki/themes/monokai.json').then((mod) => mod.default),
21
- nord: () => import('shiki/themes/nord.json').then((mod) => mod.default),
22
- 'one-dark-pro': () => import('shiki/themes/one-dark-pro.json').then((mod) => mod.default),
23
- poimandres: () => import('shiki/themes/poimandres.json').then((mod) => mod.default),
24
- 'rose-pine-dawn': () => import('shiki/themes/rose-pine-dawn.json').then((mod) => mod.default),
25
- 'rose-pine-moon': () => import('shiki/themes/rose-pine-moon.json').then((mod) => mod.default),
26
- 'rose-pine': () => import('shiki/themes/rose-pine.json').then((mod) => mod.default),
27
- 'slack-dark': () => import('shiki/themes/slack-dark.json').then((mod) => mod.default),
28
- 'slack-ochin': () => import('shiki/themes/slack-ochin.json').then((mod) => mod.default),
29
- 'solarized-dark': () => import('shiki/themes/solarized-dark.json').then((mod) => mod.default),
30
- 'solarized-light': () => import('shiki/themes/solarized-light.json').then((mod) => mod.default),
31
- 'vitesse-dark': () => import('shiki/themes/vitesse-dark.json').then((mod) => mod.default),
32
- 'vitesse-light': () => import('shiki/themes/vitesse-light.json').then((mod) => mod.default),
8
+ 'css-variables': () => import('shiki/themes/css-variables.json').then(mod => mod.default),
9
+ 'dark-plus': () => import('shiki/themes/dark-plus.json').then(mod => mod.default),
10
+ 'dracula-soft': () => import('shiki/themes/dracula-soft.json').then(mod => mod.default),
11
+ 'dracula': () => import('shiki/themes/dracula.json').then(mod => mod.default),
12
+ 'github-dark-dimmed': () => import('shiki/themes/github-dark-dimmed.json').then(mod => mod.default),
13
+ 'github-dark': () => import('shiki/themes/github-dark.json').then(mod => mod.default),
14
+ 'github-light': () => import('shiki/themes/github-light.json').then(mod => mod.default),
15
+ 'hc_light': () => import('shiki/themes/hc_light.json').then(mod => mod.default),
16
+ 'light-plus': () => import('shiki/themes/light-plus.json').then(mod => mod.default),
17
+ 'material-theme-darker': () => import('shiki/themes/material-theme-darker.json').then(mod => mod.default),
18
+ 'material-theme-lighter': () => import('shiki/themes/material-theme-lighter.json').then(mod => mod.default),
19
+ 'material-theme-ocean': () => import('shiki/themes/material-theme-ocean.json').then(mod => mod.default),
20
+ 'material-theme-palenight': () => import('shiki/themes/material-theme-palenight.json').then(mod => mod.default),
21
+ 'material-theme': () => import('shiki/themes/material-theme.json').then(mod => mod.default),
22
+ 'min-dark': () => import('shiki/themes/min-dark.json').then(mod => mod.default),
23
+ 'min-light': () => import('shiki/themes/min-light.json').then(mod => mod.default),
24
+ 'monokai': () => import('shiki/themes/monokai.json').then(mod => mod.default),
25
+ 'nord': () => import('shiki/themes/nord.json').then(mod => mod.default),
26
+ 'one-dark-pro': () => import('shiki/themes/one-dark-pro.json').then(mod => mod.default),
27
+ 'poimandres': () => import('shiki/themes/poimandres.json').then(mod => mod.default),
28
+ 'rose-pine-dawn': () => import('shiki/themes/rose-pine-dawn.json').then(mod => mod.default),
29
+ 'rose-pine-moon': () => import('shiki/themes/rose-pine-moon.json').then(mod => mod.default),
30
+ 'rose-pine': () => import('shiki/themes/rose-pine.json').then(mod => mod.default),
31
+ 'slack-dark': () => import('shiki/themes/slack-dark.json').then(mod => mod.default),
32
+ 'slack-ochin': () => import('shiki/themes/slack-ochin.json').then(mod => mod.default),
33
+ 'solarized-dark': () => import('shiki/themes/solarized-dark.json').then(mod => mod.default),
34
+ 'solarized-light': () => import('shiki/themes/solarized-light.json').then(mod => mod.default),
35
+ 'vitesse-dark': () => import('shiki/themes/vitesse-dark.json').then(mod => mod.default),
36
+ 'vitesse-light': () => import('shiki/themes/vitesse-light.json').then(mod => mod.default),
37
+ // old theme names for compat
38
+ 'material-darker': () => import('shiki/themes/material-theme-darker').then(mod => mod.default),
39
+ 'material-default': () => import('shiki/themes/material-theme').then(mod => mod.default),
40
+ 'material-lighter': () => import('shiki/themes/material-theme-lighter').then(mod => mod.default),
41
+ 'material-ocean': () => import('shiki/themes/material-theme-ocean').then(mod => mod.default),
42
+ 'material-palenight': () => import('shiki/themes/material-theme-palenight').then(mod => mod.default),
33
43
  };
File without changes
@@ -70,6 +70,7 @@ export interface CLIFlags {
70
70
  drafts?: boolean;
71
71
  open?: boolean;
72
72
  experimentalAssets?: boolean;
73
+ experimentalMiddleware?: boolean;
73
74
  }
74
75
  export interface BuildConfig {
75
76
  /**
@@ -376,6 +377,22 @@ export interface AstroUserConfig {
376
377
  * ```
377
378
  */
378
379
  outDir?: string;
380
+ /**
381
+ * @docs
382
+ * @name cacheDir
383
+ * @type {string}
384
+ * @default `"./node_modules/.astro"`
385
+ * @description Set the directory for caching build artifacts. Files in this directory will be used in subsequent builds to speed up the build time.
386
+ *
387
+ * The value can be either an absolute file system path or a path relative to the project root.
388
+ *
389
+ * ```js
390
+ * {
391
+ * cacheDir: './my-custom-cache-directory'
392
+ * }
393
+ * ```
394
+ */
395
+ cacheDir?: string;
379
396
  /**
380
397
  * @docs
381
398
  * @name site
@@ -438,6 +455,22 @@ export interface AstroUserConfig {
438
455
  * ```
439
456
  */
440
457
  trailingSlash?: 'always' | 'never' | 'ignore';
458
+ /**
459
+ * @docs
460
+ * @name scopedStyleStrategy
461
+ * @type {('where' | 'class')}
462
+ * @default `'where'`
463
+ * @description
464
+ * @version 2.4
465
+ *
466
+ * Specify the strategy used for scoping styles within Astro components. Choose from:
467
+ * - `'where'` - Use `:where` selectors, causing no specifity increase.
468
+ * - `'class'` - Use class-based selectors, causing a +1 specifity increase.
469
+ *
470
+ * Using `'class'` is helpful when you want to ensure that element selectors within an Astro component override global style defaults (e.g. from a global stylesheet).
471
+ * Using `'where'` gives you more control over specifity, but requires that you use higher-specifity selectors, layers, and other tools to control which selectors are applied.
472
+ */
473
+ scopedStyleStrategy?: 'where' | 'class';
441
474
  /**
442
475
  * @docs
443
476
  * @name adapter
@@ -945,6 +978,44 @@ export interface AstroUserConfig {
945
978
  * }
946
979
  */
947
980
  assets?: boolean;
981
+ /**
982
+ * @docs
983
+ * @name experimental.inlineStylesheets
984
+ * @type {('always' | 'auto' | 'never')}
985
+ * @default `never`
986
+ * @description
987
+ * Control whether styles are sent to the browser in a separate css file or inlined into <style> tags. Choose from the following options:
988
+ * - `'always'` - all styles are inlined into <style> tags
989
+ * - `'auto'` - only stylesheets smaller than `ViteConfig.build.assetsInlineLimit` (default: 4kb) are inlined. Otherwise, styles are sent in external stylesheets.
990
+ * - `'never'` - all styles are sent in external stylesheets
991
+ *
992
+ * ```js
993
+ * {
994
+ * experimental: {
995
+ * inlineStylesheets: `auto`,
996
+ * },
997
+ * }
998
+ */
999
+ inlineStylesheets?: 'always' | 'auto' | 'never';
1000
+ /**
1001
+ * @docs
1002
+ * @name experimental.middleware
1003
+ * @type {boolean}
1004
+ * @default `false`
1005
+ * @version 2.4.0
1006
+ * @description
1007
+ * Enable experimental support for Astro middleware.
1008
+ *
1009
+ * To enable this feature, set `experimental.middleware` to `true` in your Astro config:
1010
+ *
1011
+ * ```js
1012
+ * {
1013
+ * experimental: {
1014
+ * middleware: true,
1015
+ * },
1016
+ * }
1017
+ */
1018
+ middleware?: boolean;
948
1019
  };
949
1020
  /** @deprecated - Use "integrations" instead. Run Astro to learn more about migrating. */
950
1021
  renderers?: never;
@@ -1264,6 +1335,10 @@ interface AstroSharedContext<Props extends Record<string, any> = Record<string,
1264
1335
  * Redirect to another page (**SSR Only**).
1265
1336
  */
1266
1337
  redirect(path: string, status?: 301 | 302 | 303 | 307 | 308): Response;
1338
+ /**
1339
+ * Object accessed via Astro middleware
1340
+ */
1341
+ locals: App.Locals;
1267
1342
  }
1268
1343
  export interface APIContext<Props extends Record<string, any> = Record<string, any>> extends AstroSharedContext<Props> {
1269
1344
  site: URL | undefined;
@@ -1295,7 +1370,7 @@ export interface APIContext<Props extends Record<string, any> = Record<string, a
1295
1370
  * }
1296
1371
  * ```
1297
1372
  *
1298
- * [context reference](https://docs.astro.build/en/guides/api-reference/#contextparams)
1373
+ * [context reference](https://docs.astro.build/en/reference/api-reference/#contextparams)
1299
1374
  */
1300
1375
  params: AstroSharedContext['params'];
1301
1376
  /**
@@ -1335,6 +1410,30 @@ export interface APIContext<Props extends Record<string, any> = Record<string, a
1335
1410
  * [context reference](https://docs.astro.build/en/guides/api-reference/#contextredirect)
1336
1411
  */
1337
1412
  redirect: AstroSharedContext['redirect'];
1413
+ /**
1414
+ * Object accessed via Astro middleware.
1415
+ *
1416
+ * Example usage:
1417
+ *
1418
+ * ```ts
1419
+ * // src/middleware.ts
1420
+ * import {defineMiddleware} from "astro/middleware";
1421
+ *
1422
+ * export const onRequest = defineMiddleware((context, next) => {
1423
+ * context.locals.greeting = "Hello!";
1424
+ * next();
1425
+ * });
1426
+ * ```
1427
+ * Inside a `.astro` file:
1428
+ * ```astro
1429
+ * ---
1430
+ * // src/pages/index.astro
1431
+ * const greeting = Astro.locals.greeting;
1432
+ * ---
1433
+ * <h1>{greeting}</h1>
1434
+ * ```
1435
+ */
1436
+ locals: App.Locals;
1338
1437
  }
1339
1438
  export type Props = Record<string, unknown>;
1340
1439
  export interface EndpointOutput {
@@ -1415,6 +1514,14 @@ export interface AstroIntegration {
1415
1514
  }) => void | Promise<void>;
1416
1515
  };
1417
1516
  }
1517
+ export type MiddlewareNext<R> = () => Promise<R>;
1518
+ export type MiddlewareHandler<R> = (context: APIContext, next: MiddlewareNext<R>) => Promise<R> | Promise<void> | void;
1519
+ export type MiddlewareResponseHandler = MiddlewareHandler<Response>;
1520
+ export type MiddlewareEndpointHandler = MiddlewareHandler<Response | EndpointOutput>;
1521
+ export type MiddlewareNextResponse = MiddlewareNext<Response>;
1522
+ export type AstroMiddlewareInstance<R> = {
1523
+ onRequest?: MiddlewareHandler<R>;
1524
+ };
1418
1525
  export interface AstroPluginOptions {
1419
1526
  settings: AstroSettings;
1420
1527
  logging: LogOptions;
@@ -3,22 +3,6 @@ import { type ImageService } from './services/service.js';
3
3
  import type { GetImageResult, ImageMetadata, ImageTransform } from './types.js';
4
4
  export declare function isESMImportedImage(src: ImageMetadata | string): src is ImageMetadata;
5
5
  export declare function getConfiguredImageService(): Promise<ImageService>;
6
- /**
7
- * Get an optimized image and the necessary attributes to render it.
8
- *
9
- * **Example**
10
- * ```astro
11
- * ---
12
- * import { getImage } from 'astro:assets';
13
- * import originalImage from '../assets/image.png';
14
- *
15
- * const optimizedImage = await getImage({src: originalImage, width: 1280 });
16
- * ---
17
- * <img src={optimizedImage.src} {...optimizedImage.attributes} />
18
- * ```
19
- *
20
- * This is functionally equivalent to using the `<Image />` component, as the component calls this function internally.
21
- */
22
6
  export declare function getImage(options: ImageTransform, serviceConfig: Record<string, any>): Promise<GetImageResult>;
23
7
  export declare function getStaticImageList(): Iterable<[
24
8
  string,
@@ -27,11 +11,16 @@ export declare function getStaticImageList(): Iterable<[
27
11
  options: ImageTransform;
28
12
  }
29
13
  ]>;
30
- interface GenerationData {
14
+ interface GenerationDataUncached {
15
+ cached: false;
31
16
  weight: {
32
17
  before: number;
33
18
  after: number;
34
19
  };
35
20
  }
21
+ interface GenerationDataCached {
22
+ cached: true;
23
+ }
24
+ type GenerationData = GenerationDataUncached | GenerationDataCached;
36
25
  export declare function generateImage(buildOpts: StaticBuildOptions, options: ImageTransform, filepath: string): Promise<GenerationData | undefined>;
37
26
  export {};
@@ -55,7 +55,17 @@ async function generateImage(buildOpts, options, filepath) {
55
55
  if (!isESMImportedImage(options.src)) {
56
56
  return void 0;
57
57
  }
58
- const imageService = await getConfiguredImageService();
58
+ let useCache = true;
59
+ const assetsCacheDir = new URL("assets/", buildOpts.settings.config.cacheDir);
60
+ try {
61
+ await fs.promises.mkdir(assetsCacheDir, { recursive: true });
62
+ } catch (err) {
63
+ console.error(
64
+ "An error was encountered while creating the cache directory. Proceeding without caching. Error: ",
65
+ err
66
+ );
67
+ useCache = false;
68
+ }
59
69
  let serverRoot, clientRoot;
60
70
  if (buildOpts.settings.config.output === "server") {
61
71
  serverRoot = buildOpts.settings.config.build.server;
@@ -64,6 +74,16 @@ async function generateImage(buildOpts, options, filepath) {
64
74
  serverRoot = buildOpts.settings.config.outDir;
65
75
  clientRoot = buildOpts.settings.config.outDir;
66
76
  }
77
+ const finalFileURL = new URL("." + filepath, clientRoot);
78
+ const finalFolderURL = new URL("./", finalFileURL);
79
+ const cachedFileURL = new URL(basename(filepath), assetsCacheDir);
80
+ try {
81
+ await fs.promises.copyFile(cachedFileURL, finalFileURL);
82
+ return {
83
+ cached: true
84
+ };
85
+ } catch (e) {
86
+ }
67
87
  const originalImagePath = options.src.src;
68
88
  const fileData = await fs.promises.readFile(
69
89
  new URL(
@@ -73,16 +93,29 @@ async function generateImage(buildOpts, options, filepath) {
73
93
  serverRoot
74
94
  )
75
95
  );
96
+ const imageService = await getConfiguredImageService();
76
97
  const resultData = await imageService.transform(
77
98
  fileData,
78
99
  { ...options, src: originalImagePath },
79
100
  buildOpts.settings.config.image.service.config
80
101
  );
81
- const finalFileURL = new URL("." + filepath, clientRoot);
82
- const finalFolderURL = new URL("./", finalFileURL);
83
102
  await fs.promises.mkdir(finalFolderURL, { recursive: true });
84
- await fs.promises.writeFile(finalFileURL, resultData.data);
103
+ if (useCache) {
104
+ try {
105
+ await fs.promises.writeFile(cachedFileURL, resultData.data);
106
+ await fs.promises.copyFile(cachedFileURL, finalFileURL);
107
+ } catch (e) {
108
+ console.error(
109
+ `There was an error creating the cache entry for ${filepath}. Attempting to write directly to output directory. Error: `,
110
+ e
111
+ );
112
+ await fs.promises.writeFile(finalFileURL, resultData.data);
113
+ }
114
+ } else {
115
+ await fs.promises.writeFile(finalFileURL, resultData.data);
116
+ }
85
117
  return {
118
+ cached: false,
86
119
  weight: {
87
120
  before: Math.trunc(fileData.byteLength / 1024),
88
121
  after: Math.trunc(resultData.data.byteLength / 1024)
@@ -5,7 +5,6 @@ import {
5
5
  createHeadAndContent,
6
6
  renderComponent,
7
7
  renderScriptElement,
8
- renderStyleElement,
9
8
  renderTemplate,
10
9
  renderUniqueStylesheet,
11
10
  unescapeHTML
@@ -125,12 +124,18 @@ async function render({
125
124
  factory(result, baseProps, slots) {
126
125
  let styles = "", links = "", scripts = "";
127
126
  if (Array.isArray(collectedStyles)) {
128
- styles = collectedStyles.map((style) => renderStyleElement(style)).join("");
127
+ styles = collectedStyles.map((style) => {
128
+ return renderUniqueStylesheet(result, {
129
+ type: "inline",
130
+ content: style
131
+ });
132
+ }).join("");
129
133
  }
130
134
  if (Array.isArray(collectedLinks)) {
131
135
  links = collectedLinks.map((link) => {
132
136
  return renderUniqueStylesheet(result, {
133
- href: prependForwardSlash(link)
137
+ type: "external",
138
+ src: prependForwardSlash(link)
134
139
  });
135
140
  }).join("");
136
141
  }
@@ -98,7 +98,8 @@ function astroConfigBuildPlugin(options, internals) {
98
98
  };
99
99
  for (const chunk of outputs) {
100
100
  if (chunk.type === "chunk" && (chunk.code.includes(LINKS_PLACEHOLDER) || chunk.code.includes(SCRIPTS_PLACEHOLDER))) {
101
- let entryCSS = /* @__PURE__ */ new Set();
101
+ let entryStyles = /* @__PURE__ */ new Set();
102
+ let entryLinks = /* @__PURE__ */ new Set();
102
103
  let entryScripts = /* @__PURE__ */ new Set();
103
104
  for (const id of Object.keys(chunk.modules)) {
104
105
  for (const [pageInfo] of walkParentInfos(id, ssrPluginContext)) {
@@ -111,7 +112,10 @@ function astroConfigBuildPlugin(options, internals) {
111
112
  const _entryScripts = (_b = pageData.propagatedScripts) == null ? void 0 : _b.get(id);
112
113
  if (_entryCss) {
113
114
  for (const value of _entryCss) {
114
- entryCSS.add(value);
115
+ if (value.type === "inline")
116
+ entryStyles.add(value.content);
117
+ if (value.type === "external")
118
+ entryLinks.add(value.src);
115
119
  }
116
120
  }
117
121
  if (_entryScripts) {
@@ -123,10 +127,16 @@ function astroConfigBuildPlugin(options, internals) {
123
127
  }
124
128
  }
125
129
  let newCode = chunk.code;
126
- if (entryCSS.size) {
130
+ if (entryStyles.size) {
131
+ newCode = newCode.replace(
132
+ JSON.stringify(STYLES_PLACEHOLDER),
133
+ JSON.stringify(Array.from(entryStyles))
134
+ );
135
+ }
136
+ if (entryLinks.size) {
127
137
  newCode = newCode.replace(
128
138
  JSON.stringify(LINKS_PLACEHOLDER),
129
- JSON.stringify(Array.from(entryCSS).map(prependBase))
139
+ JSON.stringify(Array.from(entryLinks).map(prependBase))
130
140
  );
131
141
  }
132
142
  if (entryScripts.size) {
@@ -90,7 +90,7 @@ function astroContentImportPlugin({
90
90
  if (settings.contentEntryTypes.some((t) => t.getRenderModule)) {
91
91
  plugins.push({
92
92
  name: "astro:content-render-imports",
93
- async load(viteId) {
93
+ async transform(_, viteId) {
94
94
  const contentRenderer = getContentRendererByViteId(viteId, settings);
95
95
  if (!contentRenderer)
96
96
  return;
@@ -1,8 +1,9 @@
1
1
  import mime from "mime";
2
2
  import { attachToResponse, getSetCookiesFromResponse } from "../cookies/index.js";
3
- import { call as callEndpoint } from "../endpoint/index.js";
3
+ import { call as callEndpoint, createAPIContext } from "../endpoint/index.js";
4
4
  import { consoleLogDestination } from "../logger/console.js";
5
5
  import { error } from "../logger/core.js";
6
+ import { callMiddleware } from "../middleware/callMiddleware.js";
6
7
  import { removeTrailingForwardSlash } from "../path.js";
7
8
  import {
8
9
  createEnvironment,
@@ -12,11 +13,12 @@ import {
12
13
  import { RouteCache } from "../render/route-cache.js";
13
14
  import {
14
15
  createAssetLink,
15
- createLinkStylesheetElementSet,
16
- createModuleScriptElement
16
+ createModuleScriptElement,
17
+ createStylesheetElementSet
17
18
  } from "../render/ssr-element.js";
18
19
  import { matchRoute } from "../routing/match.js";
19
20
  import { deserializeManifest } from "./common.js";
21
+ const clientLocalsSymbol = Symbol.for("astro.locals");
20
22
  const pagesVirtualModuleId = "@astrojs-pages-virtual-entry";
21
23
  const resolvedPagesVirtualModuleId = "\0" + pagesVirtualModuleId;
22
24
  const responseSentSymbol = Symbol.for("astro.responseSent");
@@ -108,6 +110,7 @@ class App {
108
110
  });
109
111
  }
110
112
  }
113
+ Reflect.set(request, clientLocalsSymbol, {});
111
114
  if (routeData.route === "/404") {
112
115
  defaultStatus = 404;
113
116
  }
@@ -141,10 +144,12 @@ class App {
141
144
  return getSetCookiesFromResponse(response);
142
145
  }
143
146
  async #renderPage(request, routeData, mod, status = 200) {
147
+ var _a;
144
148
  const url = new URL(request.url);
145
149
  const pathname = "/" + this.removeBase(url.pathname);
146
150
  const info = this.#routeDataToRouteInfo.get(routeData);
147
- const links = createLinkStylesheetElementSet(info.links);
151
+ const links = /* @__PURE__ */ new Set();
152
+ const styles = createStylesheetElementSet(info.styles);
148
153
  let scripts = /* @__PURE__ */ new Set();
149
154
  for (const script of info.scripts) {
150
155
  if ("stage" in script) {
@@ -159,17 +164,44 @@ class App {
159
164
  }
160
165
  }
161
166
  try {
162
- const ctx = createRenderContext({
167
+ const renderContext = await createRenderContext({
163
168
  request,
164
169
  origin: url.origin,
165
170
  pathname,
166
171
  componentMetadata: this.#manifest.componentMetadata,
167
172
  scripts,
173
+ styles,
168
174
  links,
169
175
  route: routeData,
170
- status
176
+ status,
177
+ mod,
178
+ env: this.#env
171
179
  });
172
- const response = await renderPage(mod, ctx, this.#env);
180
+ const apiContext = createAPIContext({
181
+ request: renderContext.request,
182
+ params: renderContext.params,
183
+ props: renderContext.props,
184
+ site: this.#env.site,
185
+ adapterName: this.#env.adapterName
186
+ });
187
+ const onRequest = (_a = this.#manifest.middleware) == null ? void 0 : _a.onRequest;
188
+ let response;
189
+ if (onRequest) {
190
+ response = await callMiddleware(
191
+ onRequest,
192
+ apiContext,
193
+ () => {
194
+ return renderPage({ mod, renderContext, env: this.#env, apiContext });
195
+ }
196
+ );
197
+ } else {
198
+ response = await renderPage({
199
+ mod,
200
+ renderContext,
201
+ env: this.#env,
202
+ apiContext
203
+ });
204
+ }
173
205
  Reflect.set(request, responseSentSymbol, true);
174
206
  return response;
175
207
  } catch (err) {
@@ -184,14 +216,22 @@ class App {
184
216
  const url = new URL(request.url);
185
217
  const pathname = "/" + this.removeBase(url.pathname);
186
218
  const handler = mod;
187
- const ctx = createRenderContext({
219
+ const ctx = await createRenderContext({
188
220
  request,
189
221
  origin: url.origin,
190
222
  pathname,
191
223
  route: routeData,
192
- status
224
+ status,
225
+ env: this.#env,
226
+ mod: handler
193
227
  });
194
- const result = await callEndpoint(handler, this.#env, ctx, this.#logging);
228
+ const result = await callEndpoint(
229
+ handler,
230
+ this.#env,
231
+ ctx,
232
+ this.#logging,
233
+ this.#manifest.middleware
234
+ );
195
235
  if (result.type === "response") {
196
236
  if (result.response.headers.get("X-Astro-Response") === "Not-Found") {
197
237
  const fourOhFourRequest = new Request(new URL("/404", request.url));
@@ -1,6 +1,13 @@
1
1
  import type { MarkdownRenderingOptions } from '@astrojs/markdown-remark';
2
- import type { ComponentInstance, RouteData, SerializedRouteData, SSRComponentMetadata, SSRLoadedRenderer, SSRResult } from '../../@types/astro';
2
+ import type { AstroMiddlewareInstance, ComponentInstance, RouteData, SerializedRouteData, SSRComponentMetadata, SSRLoadedRenderer, SSRResult } from '../../@types/astro';
3
3
  export type ComponentPath = string;
4
+ export type StylesheetAsset = {
5
+ type: 'inline';
6
+ content: string;
7
+ } | {
8
+ type: 'external';
9
+ src: string;
10
+ };
4
11
  export interface RouteInfo {
5
12
  routeData: RouteData;
6
13
  file: string;
@@ -12,6 +19,7 @@ export interface RouteInfo {
12
19
  type: 'inline' | 'external';
13
20
  value: string;
14
21
  })[];
22
+ styles: StylesheetAsset[];
15
23
  }
16
24
  export type SerializedRouteInfo = Omit<RouteInfo, 'routeData'> & {
17
25
  routeData: SerializedRouteData;
@@ -28,6 +36,7 @@ export interface SSRManifest {
28
36
  entryModules: Record<string, string>;
29
37
  assets: Set<string>;
30
38
  componentMetadata: SSRResult['componentMetadata'];
39
+ middleware?: AstroMiddlewareInstance<unknown>;
31
40
  }
32
41
  export type SerializedSSRManifest = Omit<SSRManifest, 'routes' | 'assets' | 'componentMetadata'> & {
33
42
  routes: SerializedRouteInfo[];