astro 3.4.3 → 3.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 (158) hide show
  1. package/client.d.ts +86 -0
  2. package/components/Code.astro +37 -4
  3. package/components/Image.astro +2 -2
  4. package/components/Picture.astro +19 -5
  5. package/components/ViewTransitions.astro +39 -38
  6. package/content-module.template.mjs +4 -14
  7. package/dist/@types/astro.d.ts +223 -5
  8. package/dist/assets/build/generate.d.ts +2 -1
  9. package/dist/assets/build/generate.js +16 -5
  10. package/dist/assets/consts.d.ts +1 -0
  11. package/dist/assets/consts.js +2 -0
  12. package/dist/assets/endpoint/generic.js +6 -2
  13. package/dist/assets/internal.js +11 -2
  14. package/dist/assets/services/service.d.ts +10 -10
  15. package/dist/assets/services/service.js +2 -1
  16. package/dist/assets/services/vendor/squoosh/image-pool.d.ts +1 -2
  17. package/dist/assets/types.d.ts +19 -9
  18. package/dist/assets/utils/emitAsset.js +5 -0
  19. package/dist/assets/utils/metadata.d.ts +1 -2
  20. package/dist/assets/utils/proxy.d.ts +1 -0
  21. package/dist/assets/utils/proxy.js +16 -0
  22. package/dist/assets/utils/queryParams.d.ts +1 -1
  23. package/dist/assets/utils/transformToPath.d.ts +1 -1
  24. package/dist/assets/utils/transformToPath.js +8 -3
  25. package/dist/assets/vite-plugin-assets.js +26 -11
  26. package/dist/cli/build/index.js +1 -1
  27. package/dist/content/consts.d.ts +1 -0
  28. package/dist/content/consts.js +2 -0
  29. package/dist/content/runtime-assets.d.ts +9 -1
  30. package/dist/content/runtime-assets.js +1 -1
  31. package/dist/content/runtime.d.ts +1 -0
  32. package/dist/content/runtime.js +8 -2
  33. package/dist/content/utils.d.ts +1 -0
  34. package/dist/content/utils.js +9 -0
  35. package/dist/content/vite-plugin-content-assets.js +49 -23
  36. package/dist/content/vite-plugin-content-imports.js +9 -3
  37. package/dist/content/vite-plugin-content-virtual-mod.d.ts +17 -12
  38. package/dist/content/vite-plugin-content-virtual-mod.js +136 -57
  39. package/dist/core/app/index.js +19 -4
  40. package/dist/core/app/types.d.ts +7 -1
  41. package/dist/core/build/buildPipeline.js +17 -4
  42. package/dist/core/build/common.js +2 -0
  43. package/dist/core/build/generate.js +64 -34
  44. package/dist/core/build/index.d.ts +0 -8
  45. package/dist/core/build/index.js +9 -2
  46. package/dist/core/build/internal.d.ts +11 -1
  47. package/dist/core/build/internal.js +23 -1
  48. package/dist/core/build/page-data.js +46 -18
  49. package/dist/core/build/plugin.d.ts +12 -10
  50. package/dist/core/build/plugin.js +14 -22
  51. package/dist/core/build/plugins/index.js +4 -0
  52. package/dist/core/build/plugins/plugin-alias-resolve.js +1 -1
  53. package/dist/core/build/plugins/plugin-analyzer.js +1 -1
  54. package/dist/core/build/plugins/plugin-chunks.d.ts +4 -0
  55. package/dist/core/build/plugins/plugin-chunks.js +31 -0
  56. package/dist/core/build/plugins/plugin-component-entry.js +1 -1
  57. package/dist/core/build/plugins/plugin-content.d.ts +4 -0
  58. package/dist/core/build/plugins/plugin-content.js +273 -0
  59. package/dist/core/build/plugins/plugin-css.js +9 -4
  60. package/dist/core/build/plugins/plugin-hoisted-scripts.js +1 -1
  61. package/dist/core/build/plugins/plugin-internals.js +1 -1
  62. package/dist/core/build/plugins/plugin-manifest.js +14 -5
  63. package/dist/core/build/plugins/plugin-middleware.d.ts +1 -3
  64. package/dist/core/build/plugins/plugin-middleware.js +5 -57
  65. package/dist/core/build/plugins/plugin-pages.js +3 -3
  66. package/dist/core/build/plugins/plugin-prerender.js +2 -5
  67. package/dist/core/build/plugins/plugin-renderers.js +1 -1
  68. package/dist/core/build/plugins/plugin-ssr.js +6 -5
  69. package/dist/core/build/plugins/util.d.ts +3 -3
  70. package/dist/core/build/static-build.d.ts +2 -1
  71. package/dist/core/build/static-build.js +52 -28
  72. package/dist/core/build/types.d.ts +1 -1
  73. package/dist/core/build/util.d.ts +7 -0
  74. package/dist/core/build/util.js +37 -1
  75. package/dist/core/compile/compile.js +1 -0
  76. package/dist/core/config/config.js +3 -0
  77. package/dist/core/config/schema.d.ts +208 -0
  78. package/dist/core/config/schema.js +72 -4
  79. package/dist/core/config/settings.js +1 -0
  80. package/dist/core/constants.js +1 -1
  81. package/dist/core/create-vite.js +9 -3
  82. package/dist/core/dev/dev.js +1 -1
  83. package/dist/core/endpoint/index.d.ts +4 -3
  84. package/dist/core/endpoint/index.js +29 -3
  85. package/dist/core/errors/errors-data.d.ts +11 -0
  86. package/dist/core/errors/errors-data.js +17 -0
  87. package/dist/core/messages.js +2 -2
  88. package/dist/core/middleware/index.d.ts +7 -3
  89. package/dist/core/middleware/index.js +3 -2
  90. package/dist/core/middleware/loadMiddleware.d.ts +1 -2
  91. package/dist/core/middleware/loadMiddleware.js +3 -4
  92. package/dist/core/middleware/sequence.d.ts +2 -2
  93. package/dist/core/middleware/sequence.js +3 -2
  94. package/dist/core/middleware/vite-plugin.d.ts +9 -0
  95. package/dist/core/middleware/vite-plugin.js +101 -0
  96. package/dist/core/pipeline.d.ts +1 -1
  97. package/dist/core/pipeline.js +6 -4
  98. package/dist/core/preview/static-preview-server.js +3 -1
  99. package/dist/core/preview/vite-plugin-astro-preview.js +13 -2
  100. package/dist/core/redirects/helpers.d.ts +1 -0
  101. package/dist/core/redirects/helpers.js +4 -0
  102. package/dist/core/render/context.d.ts +24 -1
  103. package/dist/core/render/context.js +96 -2
  104. package/dist/core/render/core.d.ts +2 -14
  105. package/dist/core/render/core.js +12 -52
  106. package/dist/core/render/index.d.ts +2 -3
  107. package/dist/core/render/index.js +3 -4
  108. package/dist/core/render/params-and-props.d.ts +1 -1
  109. package/dist/core/render/params-and-props.js +5 -2
  110. package/dist/core/render/result.d.ts +1 -0
  111. package/dist/core/render/result.js +23 -0
  112. package/dist/core/render/route-cache.d.ts +1 -1
  113. package/dist/core/render/route-cache.js +17 -11
  114. package/dist/core/routing/manifest/create.js +118 -4
  115. package/dist/core/sync/index.d.ts +2 -24
  116. package/dist/i18n/index.d.ts +54 -0
  117. package/dist/i18n/index.js +91 -0
  118. package/dist/i18n/middleware.d.ts +2 -0
  119. package/dist/i18n/middleware.js +62 -0
  120. package/dist/i18n/vite-plugin-i18n.d.ts +7 -0
  121. package/dist/i18n/vite-plugin-i18n.js +62 -0
  122. package/dist/integrations/astroFeaturesValidation.js +4 -1
  123. package/dist/integrations/index.js +12 -0
  124. package/dist/prefetch/index.d.ts +31 -0
  125. package/dist/prefetch/index.js +176 -0
  126. package/dist/prefetch/vite-plugin-prefetch.d.ts +5 -0
  127. package/dist/prefetch/vite-plugin-prefetch.js +43 -0
  128. package/dist/runtime/client/dev-overlay/entrypoint.js +2 -2
  129. package/dist/runtime/client/dev-overlay/overlay.d.ts +10 -2
  130. package/dist/runtime/client/dev-overlay/overlay.js +47 -28
  131. package/dist/runtime/client/dev-overlay/plugins/astro.d.ts +1 -0
  132. package/dist/runtime/client/dev-overlay/plugins/astro.js +44 -9
  133. package/dist/runtime/client/dev-overlay/plugins/audit.d.ts +2 -1
  134. package/dist/runtime/client/dev-overlay/plugins/audit.js +101 -24
  135. package/dist/runtime/client/dev-overlay/plugins/xray.d.ts +1 -0
  136. package/dist/runtime/client/dev-overlay/plugins/xray.js +22 -0
  137. package/dist/runtime/client/dev-overlay/ui-library/card.js +2 -0
  138. package/dist/runtime/client/dev-overlay/ui-library/icons.d.ts +3 -0
  139. package/dist/runtime/client/dev-overlay/ui-library/icons.js +4 -1
  140. package/dist/runtime/client/dev-overlay/ui-library/tooltip.js +1 -0
  141. package/dist/runtime/client/dev-overlay/ui-library/window.js +1 -0
  142. package/dist/runtime/server/index.d.ts +0 -2
  143. package/dist/runtime/server/render/component.js +3 -5
  144. package/dist/transitions/router.d.ts +1 -0
  145. package/dist/transitions/router.js +10 -5
  146. package/dist/transitions/vite-plugin-transitions.d.ts +4 -1
  147. package/dist/transitions/vite-plugin-transitions.js +7 -1
  148. package/dist/vite-plugin-astro-server/devPipeline.d.ts +1 -0
  149. package/dist/vite-plugin-astro-server/devPipeline.js +2 -0
  150. package/dist/vite-plugin-astro-server/plugin.js +11 -1
  151. package/dist/vite-plugin-astro-server/route.js +113 -51
  152. package/dist/vite-plugin-head/index.js +1 -1
  153. package/dist/vite-plugin-markdown/index.js +1 -0
  154. package/dist/vite-plugin-scripts/index.js +2 -1
  155. package/package.json +7 -5
  156. package/tsconfigs/base.json +1 -1
  157. package/dist/core/endpoint/dev/index.d.ts +0 -2
  158. package/dist/core/endpoint/dev/index.js +0 -17
@@ -464,6 +464,67 @@ export interface AstroUserConfig {
464
464
  * ```
465
465
  */
466
466
  redirects?: Record<string, RedirectConfig>;
467
+ /**
468
+ * @docs
469
+ * @name prefetch
470
+ * @type {boolean | object}
471
+ * @description
472
+ * Enable prefetching for links on your site to provide faster page transitions.
473
+ * (Enabled by default on pages using the `<ViewTransitions />` router. Set `prefetch: false` to opt out of this behaviour.)
474
+ *
475
+ * This configuration automatically adds a prefetch script to every page in the project
476
+ * giving you access to the `data-astro-prefetch` attribute.
477
+ * Add this attribute to any `<a />` link on your page to enable prefetching for that page.
478
+ *
479
+ * ```html
480
+ * <a href="/about" data-astro-prefetch>About</a>
481
+ * ```
482
+ * Further customize the default prefetching behavior using the [`prefetch.defaultStrategy`](#prefetchdefaultstrategy) and [`prefetch.prefetchAll`](#prefetchprefetchall) options.
483
+ *
484
+ * See the [Prefetch guide](https://docs.astro.build/en/guides/prefetch/) for more information.
485
+ */
486
+ prefetch?: boolean | {
487
+ /**
488
+ * @docs
489
+ * @name prefetch.prefetchAll
490
+ * @type {boolean}
491
+ * @description
492
+ * Enable prefetching for all links, including those without the `data-astro-prefetch` attribute.
493
+ * This value defaults to `true` when using the `<ViewTransitions />` router. Otherwise, the default value is `false`.
494
+ *
495
+ * ```js
496
+ * prefetch: {
497
+ * prefetchAll: true
498
+ * }
499
+ * ```
500
+ *
501
+ * When set to `true`, you can disable prefetching individually by setting `data-astro-prefetch="false"` on any individual links.
502
+ *
503
+ * ```html
504
+ * <a href="/about" data-astro-prefetch="false">About</a>
505
+ *```
506
+ */
507
+ prefetchAll?: boolean;
508
+ /**
509
+ * @docs
510
+ * @name prefetch.defaultStrategy
511
+ * @type {'tap' | 'hover' | 'viewport'}
512
+ * @default `'hover'`
513
+ * @description
514
+ * The default prefetch strategy to use when the `data-astro-prefetch` attribute is set on a link with no value.
515
+ *
516
+ * - `'tap'`: Prefetch just before you click on the link.
517
+ * - `'hover'`: Prefetch when you hover over or focus on the link. (default)
518
+ * - `'viewport'`: Prefetch as the links enter the viewport.
519
+ *
520
+ * You can override this default value and select a different strategy for any individual link by setting a value on the attribute.
521
+ *
522
+ * ```html
523
+ * <a href="/about" data-astro-prefetch="viewport">About</a>
524
+ * ```
525
+ */
526
+ defaultStrategy?: 'tap' | 'hover' | 'viewport';
527
+ };
467
528
  /**
468
529
  * @docs
469
530
  * @name site
@@ -1275,6 +1336,107 @@ export interface AstroUserConfig {
1275
1336
  * ```
1276
1337
  */
1277
1338
  devOverlay?: boolean;
1339
+ /**
1340
+ * @docs
1341
+ * @name experimental.i18n
1342
+ * @type {object}
1343
+ * @version 3.5.0
1344
+ * @type {object}
1345
+ * @description
1346
+ *
1347
+ * Configures experimental i18n routing and allows you to specify some customization options.
1348
+ */
1349
+ i18n?: {
1350
+ /**
1351
+ * @docs
1352
+ * @name experimental.i18n.defaultLocale
1353
+ * @type {string}
1354
+ * @version 3.5.0
1355
+ * @description
1356
+ *
1357
+ * The default locale of your website/application. This is a required field.
1358
+ */
1359
+ defaultLocale: string;
1360
+ /**
1361
+ * @docs
1362
+ * @name experimental.i18n.locales
1363
+ * @type {string[]}
1364
+ * @version 3.5.0
1365
+ * @description
1366
+ *
1367
+ * A list of all locales supported by the website (e.g. `['en', 'es', 'pt_BR']`). This list should also include the `defaultLocale`. This is a required field.
1368
+ *
1369
+ * No particular language format or syntax is enforced, but your folder structure must match exactly the locales in the list.
1370
+ */
1371
+ locales: string[];
1372
+ /**
1373
+ * @docs
1374
+ * @name experimental.i18n.fallback
1375
+ * @type {Record<string, string>}
1376
+ * @version 3.5.0
1377
+ * @description
1378
+ *
1379
+ * The fallback strategy when navigating to pages that do not exist (e.g. a translated page has not been created).
1380
+ *
1381
+ * Use this object to declare a fallback `locale` route for each language you support. If no fallback is specified, then unavailable pages will return a 404.
1382
+ *
1383
+ * #### Example
1384
+ *
1385
+ * The following example configures your content fallback strategy to redirect unavailable pages in `/pt/` to their `es` version, and unavailable pages in `/fr/` to their `en` version. Unavailable `/es/` pages will return a 404.
1386
+ *
1387
+ * ```js
1388
+ * export defualt defineConfig({
1389
+ * experimental: {
1390
+ * i18n: {
1391
+ * defaultLocale: "en",
1392
+ * locales: ["en", "fr", "pt", "es"],
1393
+ * fallback: {
1394
+ * pt: "es",
1395
+ * fr: "en"
1396
+ * }
1397
+ * }
1398
+ * }
1399
+ * })
1400
+ * ```
1401
+ */
1402
+ fallback?: Record<string, string>;
1403
+ /**
1404
+ * @docs
1405
+ * @name experimental.i18n.routingStrategy
1406
+ * @type {'prefix-always' | 'prefix-other-locales'}
1407
+ * @default 'prefix-other-locales'
1408
+ * @version 3.5.0
1409
+ * @description
1410
+ *
1411
+ * Controls the routing strategy to determine your site URLs.
1412
+ *
1413
+ * - `prefix-other-locales`(default): Only non-default languages will display a language prefix. The `defaultLocale` will not show a language prefix.
1414
+ * URLs will be of the form `example.com/[lang]/content/` for all non-default languages, but `example.com/content/` for the default locale.
1415
+ * - `prefix-always`: All URLs will display a language prefix.
1416
+ * URLs will be of the form `example.com/[lang]/content/` for every route, including the default language.
1417
+ *
1418
+ * Note: Astro requires all content to exist within a `/[lang]/` folder, even for the default language.
1419
+ */
1420
+ routingStrategy?: 'prefix-always' | 'prefix-other-locales';
1421
+ };
1422
+ /**
1423
+ * @docs
1424
+ * @name experimental.contentCollectionCache
1425
+ * @type {boolean}
1426
+ * @default `false`
1427
+ * @version 3.5.0
1428
+ * @description
1429
+ * Enables a persistent cache for content collections when building in static mode.
1430
+ *
1431
+ * ```js
1432
+ * {
1433
+ * experimental: {
1434
+ * contentCollectionCache: true,
1435
+ * },
1436
+ * }
1437
+ * ```
1438
+ */
1439
+ contentCollectionCache?: boolean;
1278
1440
  };
1279
1441
  }
1280
1442
  /**
@@ -1332,10 +1494,6 @@ export interface AstroInlineOnlyConfig {
1332
1494
  * @default "info"
1333
1495
  */
1334
1496
  logLevel?: LoggerLevel;
1335
- /**
1336
- * @internal for testing only, use `logLevel` instead.
1337
- */
1338
- logger?: Logger;
1339
1497
  }
1340
1498
  export type ContentEntryModule = {
1341
1499
  id: string;
@@ -1426,6 +1584,10 @@ export interface AstroSettings {
1426
1584
  */
1427
1585
  clientDirectives: Map<string, string>;
1428
1586
  devOverlayPlugins: string[];
1587
+ middlewares: {
1588
+ pre: string[];
1589
+ post: string[];
1590
+ };
1429
1591
  tsConfig: TSConfig | undefined;
1430
1592
  tsConfigPath: string | undefined;
1431
1593
  watchFiles: string[];
@@ -1651,6 +1813,10 @@ export type AstroFeatureMap = {
1651
1813
  * The adapter can emit static assets
1652
1814
  */
1653
1815
  assets?: AstroAssetsFeature;
1816
+ /**
1817
+ * List of features that orbit around the i18n routing
1818
+ */
1819
+ i18n?: AstroInternationalizationFeature;
1654
1820
  };
1655
1821
  export interface AstroAssetsFeature {
1656
1822
  supportKind?: SupportsKind;
@@ -1663,6 +1829,12 @@ export interface AstroAssetsFeature {
1663
1829
  */
1664
1830
  isSquooshCompatible?: boolean;
1665
1831
  }
1832
+ export interface AstroInternationalizationFeature {
1833
+ /**
1834
+ * Whether the adapter is able to detect the language of the browser, usually using the `Accept-Language` header.
1835
+ */
1836
+ detectBrowserLanguage?: SupportsKind;
1837
+ }
1666
1838
  export interface AstroAdapter {
1667
1839
  name: string;
1668
1840
  serverEntrypoint?: string;
@@ -1712,6 +1884,14 @@ interface AstroSharedContext<Props extends Record<string, any> = Record<string,
1712
1884
  * Object accessed via Astro middleware
1713
1885
  */
1714
1886
  locals: App.Locals;
1887
+ /**
1888
+ * The current locale that is computed from the `Accept-Language` header of the browser (**SSR Only**).
1889
+ */
1890
+ preferredLocale: string | undefined;
1891
+ /**
1892
+ * The list of locales computed from the `Accept-Language` header of the browser, sorted by quality value (**SSR Only**).
1893
+ */
1894
+ preferredLocaleList: string[] | undefined;
1715
1895
  }
1716
1896
  export interface APIContext<Props extends Record<string, any> = Record<string, any>, APIParams extends Record<string, string | undefined> = Record<string, string | undefined>> extends AstroSharedContext<Props, Params> {
1717
1897
  site: URL | undefined;
@@ -1808,6 +1988,32 @@ export interface APIContext<Props extends Record<string, any> = Record<string, a
1808
1988
  */
1809
1989
  locals: App.Locals;
1810
1990
  ResponseWithEncoding: typeof ResponseWithEncoding;
1991
+ /**
1992
+ * Available only when `experimental.i18n` enabled and in SSR.
1993
+ *
1994
+ * It represents the preferred locale of the user. It's computed by checking the supported locales in `i18n.locales`
1995
+ * and locales supported by the users's browser via the header `Accept-Language`
1996
+ *
1997
+ * For example, given `i18n.locales` equals to `['fr', 'de']`, and the `Accept-Language` value equals to `en, de;q=0.2, fr;q=0.6`, the
1998
+ * `Astro.preferredLanguage` will be `fr` because `en` is not supported, its [quality value] is the highest.
1999
+ *
2000
+ * [quality value]: https://developer.mozilla.org/en-US/docs/Glossary/Quality_values
2001
+ */
2002
+ preferredLocale: string | undefined;
2003
+ /**
2004
+ * Available only when `experimental.i18n` enabled and in SSR.
2005
+ *
2006
+ * It represents the list of the preferred locales that are supported by the application. The list is sorted via [quality value].
2007
+ *
2008
+ * For example, given `i18n.locales` equals to `['fr', 'pt', 'de']`, and the `Accept-Language` value equals to `en, de;q=0.2, fr;q=0.6`, the
2009
+ * `Astro.preferredLocaleList` will be equal to `['fs', 'de']` because `en` isn't supported, and `pt` isn't part of the locales contained in the
2010
+ * header.
2011
+ *
2012
+ * When the `Accept-Header` is `*`, the original `i18n.locales` are returned. The value `*` means no preferences, so Astro returns all the supported locales.
2013
+ *
2014
+ * [quality value]: https://developer.mozilla.org/en-US/docs/Glossary/Quality_values
2015
+ */
2016
+ preferredLocaleList: string[] | undefined;
1811
2017
  }
1812
2018
  export type EndpointOutput = {
1813
2019
  body: Body;
@@ -1860,6 +2066,7 @@ export interface AstroIntegration {
1860
2066
  injectRoute: (injectRoute: InjectedRoute) => void;
1861
2067
  addClientDirective: (directive: ClientDirectiveConfig) => void;
1862
2068
  addDevOverlayPlugin: (entrypoint: string) => void;
2069
+ addMiddleware: (mid: AstroIntegrationMiddleware) => void;
1863
2070
  logger: AstroIntegrationLogger;
1864
2071
  }) => void | Promise<void>;
1865
2072
  'astro:config:done'?: (options: {
@@ -1923,11 +2130,21 @@ export type MiddlewareNextResponse = MiddlewareNext<Response>;
1923
2130
  export type AstroMiddlewareInstance<R> = {
1924
2131
  onRequest?: MiddlewareHandler<R>;
1925
2132
  };
2133
+ export type AstroIntegrationMiddleware = {
2134
+ order: 'pre' | 'post';
2135
+ entrypoint: string;
2136
+ };
1926
2137
  export interface AstroPluginOptions {
1927
2138
  settings: AstroSettings;
1928
2139
  logger: Logger;
1929
2140
  }
1930
- export type RouteType = 'page' | 'endpoint' | 'redirect';
2141
+ /**
2142
+ * - page: a route that lives in the file system, usually an Astro component
2143
+ * - endpoint: a route that lives in the file system, usually a JS file that exposes endpoints methods
2144
+ * - redirect: a route points to another route that lives in the file system
2145
+ * - fallback: a route that doesn't exist in the file system that needs to be handled with other means, usually the middleware
2146
+ */
2147
+ export type RouteType = 'page' | 'endpoint' | 'redirect' | 'fallback';
1931
2148
  export interface RoutePart {
1932
2149
  content: string;
1933
2150
  dynamic: boolean;
@@ -2058,6 +2275,7 @@ export interface DevOverlayPlugin {
2058
2275
  name: string;
2059
2276
  icon: Icon;
2060
2277
  init?(canvas: ShadowRoot, eventTarget: EventTarget): void | Promise<void>;
2278
+ beforeTogglingOff?(canvas: ShadowRoot): boolean | Promise<boolean>;
2061
2279
  }
2062
2280
  export type DevOverlayMetadata = Window & typeof globalThis & {
2063
2281
  __astro_dev_overlay__: {
@@ -6,6 +6,7 @@ import type { MapValue } from '../../type-utils.js';
6
6
  import type { AssetsGlobalStaticImagesList } from '../types.js';
7
7
  type AssetEnv = {
8
8
  logger: Logger;
9
+ isSSR: boolean;
9
10
  count: {
10
11
  total: number;
11
12
  current: number;
@@ -18,6 +19,6 @@ type AssetEnv = {
18
19
  assetsFolder: AstroConfig['build']['assets'];
19
20
  };
20
21
  export declare function prepareAssetsGenerationEnv(pipeline: BuildPipeline, totalCount: number): Promise<AssetEnv>;
21
- export declare function generateImagesForPath(originalFilePath: string, transforms: MapValue<AssetsGlobalStaticImagesList>, env: AssetEnv, queue: PQueue): Promise<void>;
22
+ export declare function generateImagesForPath(originalFilePath: string, transformsAndPath: MapValue<AssetsGlobalStaticImagesList>, env: AssetEnv, queue: PQueue): Promise<void>;
22
23
  export declare function getStaticImageList(): AssetsGlobalStaticImagesList;
23
24
  export {};
@@ -33,6 +33,7 @@ async function prepareAssetsGenerationEnv(pipeline, totalCount) {
33
33
  }
34
34
  return {
35
35
  logger,
36
+ isSSR: isServerLikeOutput(config),
36
37
  count,
37
38
  useCache,
38
39
  assetsCacheDir,
@@ -42,13 +43,25 @@ async function prepareAssetsGenerationEnv(pipeline, totalCount) {
42
43
  assetsFolder: config.build.assets
43
44
  };
44
45
  }
45
- async function generateImagesForPath(originalFilePath, transforms, env, queue) {
46
+ function getFullImagePath(originalFilePath, env) {
47
+ return new URL(
48
+ "." + prependForwardSlash(join(env.assetsFolder, basename(originalFilePath))),
49
+ env.serverRoot
50
+ );
51
+ }
52
+ async function generateImagesForPath(originalFilePath, transformsAndPath, env, queue) {
46
53
  const originalImageData = await loadImage(originalFilePath, env);
47
- for (const [_, transform] of transforms) {
54
+ for (const [_, transform] of transformsAndPath.transforms) {
48
55
  queue.add(
49
56
  async () => generateImage(originalImageData, transform.finalPath, transform.transform)
50
57
  );
51
58
  }
59
+ if (!env.isSSR && !isRemotePath(originalFilePath) && !globalThis.astroAsset.referencedImages?.has(transformsAndPath.originalSrcPath)) {
60
+ try {
61
+ await fs.promises.unlink(getFullImagePath(originalFilePath, env));
62
+ } catch (e) {
63
+ }
64
+ }
52
65
  async function generateImage(originalImage, filepath, options) {
53
66
  const timeStart = performance.now();
54
67
  const generationData = await generateImageInternal(originalImage, filepath, options);
@@ -156,9 +169,7 @@ async function loadImage(path, env) {
156
169
  };
157
170
  }
158
171
  return {
159
- data: await fs.promises.readFile(
160
- new URL("." + prependForwardSlash(join(env.assetsFolder, basename(path))), env.serverRoot)
161
- ),
172
+ data: await fs.promises.readFile(getFullImagePath(path, env)),
162
173
  expires: 0
163
174
  };
164
175
  }
@@ -8,3 +8,4 @@ export declare const VALID_INPUT_FORMATS: readonly ["jpeg", "jpg", "png", "tiff"
8
8
  export declare const VALID_SUPPORTED_FORMATS: readonly ["jpeg", "jpg", "png", "tiff", "webp", "gif", "svg", "avif"];
9
9
  export declare const DEFAULT_OUTPUT_FORMAT: "webp";
10
10
  export declare const VALID_OUTPUT_FORMATS: readonly ["avif", "png", "webp", "jpeg", "jpg", "svg"];
11
+ export declare const DEFAULT_HASH_PROPS: string[];
@@ -22,7 +22,9 @@ const VALID_SUPPORTED_FORMATS = [
22
22
  ];
23
23
  const DEFAULT_OUTPUT_FORMAT = "webp";
24
24
  const VALID_OUTPUT_FORMATS = ["avif", "png", "webp", "jpeg", "jpg", "svg"];
25
+ const DEFAULT_HASH_PROPS = ["src", "width", "height", "format", "quality"];
25
26
  export {
27
+ DEFAULT_HASH_PROPS,
26
28
  DEFAULT_OUTPUT_FORMAT,
27
29
  VALID_INPUT_FORMATS,
28
30
  VALID_OUTPUT_FORMATS,
@@ -9,7 +9,7 @@ async function loadRemoteImage(src) {
9
9
  if (!res.ok) {
10
10
  return void 0;
11
11
  }
12
- return Buffer.from(await res.arrayBuffer());
12
+ return await res.arrayBuffer();
13
13
  } catch (err) {
14
14
  return void 0;
15
15
  }
@@ -34,7 +34,11 @@ const GET = async ({ request }) => {
34
34
  if (!inputBuffer) {
35
35
  return new Response("Not Found", { status: 404 });
36
36
  }
37
- const { data, format } = await imageService.transform(inputBuffer, transform, imageConfig);
37
+ const { data, format } = await imageService.transform(
38
+ new Uint8Array(inputBuffer),
39
+ transform,
40
+ imageConfig
41
+ );
38
42
  return new Response(data, {
39
43
  status: 200,
40
44
  headers: {
@@ -1,5 +1,6 @@
1
1
  import { isRemotePath } from "@astrojs/internal-helpers/path";
2
2
  import { AstroError, AstroErrorData } from "../core/errors/index.js";
3
+ import { DEFAULT_HASH_PROPS } from "./consts.js";
3
4
  import { isLocalService } from "./services/service.js";
4
5
  import { matchHostname, matchPattern } from "./utils/remotePattern.js";
5
6
  function injectImageEndpoint(settings, mode) {
@@ -55,20 +56,28 @@ async function getImage(options, imageConfig) {
55
56
  ...options,
56
57
  src: typeof options.src === "object" && "then" in options.src ? (await options.src).default ?? await options.src : options.src
57
58
  };
59
+ const clonedSrc = isESMImportedImage(resolvedOptions.src) ? (
60
+ // @ts-expect-error - clone is a private, hidden prop
61
+ resolvedOptions.src.clone ?? resolvedOptions.src
62
+ ) : resolvedOptions.src;
63
+ resolvedOptions.src = clonedSrc;
58
64
  const validatedOptions = service.validateOptions ? await service.validateOptions(resolvedOptions, imageConfig) : resolvedOptions;
59
65
  const srcSetTransforms = service.getSrcSet ? await service.getSrcSet(validatedOptions, imageConfig) : [];
60
66
  let imageURL = await service.getURL(validatedOptions, imageConfig);
61
67
  let srcSets = await Promise.all(
62
68
  srcSetTransforms.map(async (srcSet) => ({
69
+ transform: srcSet.transform,
63
70
  url: await service.getURL(srcSet.transform, imageConfig),
64
71
  descriptor: srcSet.descriptor,
65
72
  attributes: srcSet.attributes
66
73
  }))
67
74
  );
68
75
  if (isLocalService(service) && globalThis.astroAsset.addStaticImage && !(isRemoteImage(validatedOptions.src) && imageURL === validatedOptions.src)) {
69
- imageURL = globalThis.astroAsset.addStaticImage(validatedOptions);
76
+ const propsToHash = service.propertiesToHash ?? DEFAULT_HASH_PROPS;
77
+ imageURL = globalThis.astroAsset.addStaticImage(validatedOptions, propsToHash);
70
78
  srcSets = srcSetTransforms.map((srcSet) => ({
71
- url: globalThis.astroAsset.addStaticImage(srcSet.transform),
79
+ transform: srcSet.transform,
80
+ url: globalThis.astroAsset.addStaticImage(srcSet.transform, propsToHash),
72
81
  descriptor: srcSet.descriptor,
73
82
  attributes: srcSet.attributes
74
83
  }));
@@ -1,6 +1,5 @@
1
- /// <reference types="node" resolution-mode="require"/>
2
1
  import type { AstroConfig } from '../../@types/astro.js';
3
- import type { ImageOutputFormat, ImageTransform } from '../types.js';
2
+ import type { ImageOutputFormat, ImageTransform, UnresolvedSrcSetValue } from '../types.js';
4
3
  export type ImageService = LocalImageService | ExternalImageService;
5
4
  export declare function isLocalService(service: ImageService | undefined): service is LocalImageService;
6
5
  export declare function parseQuality(quality: string): string | number;
@@ -10,11 +9,6 @@ type ImageConfig<T> = Omit<AstroConfig['image'], 'service'> & {
10
9
  config: T;
11
10
  };
12
11
  };
13
- type SrcSetValue = {
14
- transform: ImageTransform;
15
- descriptor?: string;
16
- attributes?: Record<string, any>;
17
- };
18
12
  interface SharedServiceProps<T extends Record<string, any> = Record<string, any>> {
19
13
  /**
20
14
  * Return the URL to the endpoint or URL your images are generated from.
@@ -31,7 +25,7 @@ interface SharedServiceProps<T extends Record<string, any> = Record<string, any>
31
25
  * While in most cases this is exclusively used for `srcset`, it can also be used in a more generic way to generate
32
26
  * multiple variants of the same image. For instance, you can use this to generate multiple aspect ratios or multiple formats.
33
27
  */
34
- getSrcSet?: (options: ImageTransform, imageConfig: ImageConfig<T>) => SrcSetValue[] | Promise<SrcSetValue[]>;
28
+ getSrcSet?: (options: ImageTransform, imageConfig: ImageConfig<T>) => UnresolvedSrcSetValue[] | Promise<UnresolvedSrcSetValue[]>;
35
29
  /**
36
30
  * Return any additional HTML attributes separate from `src` that your service requires to show the image properly.
37
31
  *
@@ -65,10 +59,16 @@ export interface LocalImageService<T extends Record<string, any> = Record<string
65
59
  * Performs the image transformations on the input image and returns both the binary data and
66
60
  * final image format of the optimized image.
67
61
  */
68
- transform: (inputBuffer: Buffer, transform: LocalImageTransform, imageConfig: ImageConfig<T>) => Promise<{
69
- data: Buffer;
62
+ transform: (inputBuffer: Uint8Array, transform: LocalImageTransform, imageConfig: ImageConfig<T>) => Promise<{
63
+ data: Uint8Array;
70
64
  format: ImageOutputFormat;
71
65
  }>;
66
+ /**
67
+ * A list of properties that should be used to generate the hash for the image.
68
+ *
69
+ * Generally, this should be all the properties that can change the result of the image. By default, this is `src`, `width`, `height`, `quality`, and `format`.
70
+ */
71
+ propertiesToHash?: string[];
72
72
  }
73
73
  export type BaseServiceTransform = {
74
74
  src: string;
@@ -1,6 +1,6 @@
1
1
  import { AstroError, AstroErrorData } from "../../core/errors/index.js";
2
2
  import { isRemotePath, joinPaths } from "../../core/path.js";
3
- import { DEFAULT_OUTPUT_FORMAT, VALID_SUPPORTED_FORMATS } from "../consts.js";
3
+ import { DEFAULT_HASH_PROPS, DEFAULT_OUTPUT_FORMAT, VALID_SUPPORTED_FORMATS } from "../consts.js";
4
4
  import { isESMImportedImage, isRemoteAllowed } from "../internal.js";
5
5
  function isLocalService(service) {
6
6
  if (!service) {
@@ -16,6 +16,7 @@ function parseQuality(quality) {
16
16
  return result;
17
17
  }
18
18
  const baseService = {
19
+ propertiesToHash: DEFAULT_HASH_PROPS,
19
20
  validateOptions(options) {
20
21
  if (!options.src || typeof options.src !== "string" && typeof options.src !== "object") {
21
22
  throw new AstroError({
@@ -1,4 +1,3 @@
1
- /// <reference types="node" resolution-mode="require"/>
2
1
  import type { ImageOutputFormat } from '../../../types.js';
3
2
  import type { Operation } from './image.js';
4
- export declare function processBuffer(buffer: Buffer, operations: Operation[], encoding: ImageOutputFormat, quality?: number): Promise<Uint8Array>;
3
+ export declare function processBuffer(buffer: Uint8Array, operations: Operation[], encoding: ImageOutputFormat, quality?: number): Promise<Uint8Array>;
@@ -5,15 +5,19 @@ export type ImageQualityPreset = 'low' | 'mid' | 'high' | 'max' | (string & {});
5
5
  export type ImageQuality = ImageQualityPreset | number;
6
6
  export type ImageInputFormat = (typeof VALID_INPUT_FORMATS)[number];
7
7
  export type ImageOutputFormat = (typeof VALID_OUTPUT_FORMATS)[number] | (string & {});
8
- export type AssetsGlobalStaticImagesList = Map<string, Map<string, {
9
- finalPath: string;
10
- transform: ImageTransform;
11
- }>>;
8
+ export type AssetsGlobalStaticImagesList = Map<string, {
9
+ originalSrcPath: string;
10
+ transforms: Map<string, {
11
+ finalPath: string;
12
+ transform: ImageTransform;
13
+ }>;
14
+ }>;
12
15
  declare global {
13
16
  var astroAsset: {
14
17
  imageService?: ImageService;
15
- addStaticImage?: ((options: ImageTransform) => string) | undefined;
18
+ addStaticImage?: ((options: ImageTransform, hashProperties: string[]) => string) | undefined;
16
19
  staticImages?: AssetsGlobalStaticImagesList;
20
+ referencedImages?: Set<string>;
17
21
  };
18
22
  }
19
23
  /**
@@ -26,11 +30,17 @@ export interface ImageMetadata {
26
30
  format: ImageInputFormat;
27
31
  orientation?: number;
28
32
  }
29
- export interface SrcSetValue {
30
- url: string;
33
+ /**
34
+ * A yet to be completed with an url `SrcSetValue`. Other hooks will only see a resolved value, where the URL of the image has been added.
35
+ */
36
+ export type UnresolvedSrcSetValue = {
37
+ transform: ImageTransform;
31
38
  descriptor?: string;
32
- attributes?: Record<string, string>;
33
- }
39
+ attributes?: Record<string, any>;
40
+ };
41
+ export type SrcSetValue = UnresolvedSrcSetValue & {
42
+ url: string;
43
+ };
34
44
  /**
35
45
  * A yet to be resolved image transform. Used by `getImage`
36
46
  */
@@ -19,6 +19,11 @@ async function emitESMImage(id, watchMode, fileEmitter) {
19
19
  src: "",
20
20
  ...fileMetadata
21
21
  };
22
+ Object.defineProperty(emittedImage, "fsPath", {
23
+ enumerable: false,
24
+ writable: false,
25
+ value: url
26
+ });
22
27
  if (!watchMode) {
23
28
  const pathname = decodeURI(url.pathname);
24
29
  const filename = path.basename(pathname, path.extname(pathname) + `.${fileMetadata.format}`);
@@ -1,3 +1,2 @@
1
- /// <reference types="node" resolution-mode="require"/>
2
1
  import type { ImageMetadata } from '../types.js';
3
- export declare function imageMetadata(data: Buffer, src?: string): Promise<Omit<ImageMetadata, 'src'>>;
2
+ export declare function imageMetadata(data: Uint8Array, src?: string): Promise<Omit<ImageMetadata, 'src' | 'fsPath'>>;
@@ -0,0 +1 @@
1
+ export declare function getProxyCode(options: Record<string, any>, isSSR: boolean): string;
@@ -0,0 +1,16 @@
1
+ function getProxyCode(options, isSSR) {
2
+ return `
3
+ new Proxy(${JSON.stringify(options)}, {
4
+ get(target, name, receiver) {
5
+ if (name === 'clone') {
6
+ return structuredClone(target);
7
+ }
8
+ ${!isSSR ? "globalThis.astroAsset.referencedImages.add(target.fsPath);" : ""}
9
+ return target[name];
10
+ }
11
+ })
12
+ `;
13
+ }
14
+ export {
15
+ getProxyCode
16
+ };
@@ -1,2 +1,2 @@
1
1
  import type { ImageMetadata } from '../types.js';
2
- export declare function getOrigQueryParams(params: URLSearchParams): Omit<ImageMetadata, 'src'> | undefined;
2
+ export declare function getOrigQueryParams(params: URLSearchParams): Pick<ImageMetadata, 'width' | 'height' | 'format'> | undefined;
@@ -1,3 +1,3 @@
1
1
  import type { ImageTransform } from '../types.js';
2
2
  export declare function propsToFilename(transform: ImageTransform, hash: string): string;
3
- export declare function hashTransform(transform: ImageTransform, imageService: string): string;
3
+ export declare function hashTransform(transform: ImageTransform, imageService: string, propertiesToHash: string[]): string;
@@ -12,9 +12,14 @@ function propsToFilename(transform, hash) {
12
12
  let outputExt = transform.format ? `.${transform.format}` : ext;
13
13
  return `/${filename}_${hash}${outputExt}`;
14
14
  }
15
- function hashTransform(transform, imageService) {
16
- const { alt, class: className, style, widths, densities, ...rest } = transform;
17
- const hashFields = { ...rest, imageService };
15
+ function hashTransform(transform, imageService, propertiesToHash) {
16
+ const hashFields = propertiesToHash.reduce(
17
+ (acc, prop) => {
18
+ acc[prop] = transform[prop];
19
+ return acc;
20
+ },
21
+ { imageService }
22
+ );
18
23
  return shorthash(deterministicString(hashFields));
19
24
  }
20
25
  export {