astro 4.0.9 → 4.1.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 (38) hide show
  1. package/astro-jsx.d.ts +10 -1
  2. package/config.d.ts +2 -1
  3. package/config.mjs +2 -2
  4. package/dist/@types/astro.d.ts +58 -15
  5. package/dist/assets/services/sharp.d.ts +7 -1
  6. package/dist/assets/services/sharp.js +6 -2
  7. package/dist/cli/add/index.js +3 -2
  8. package/dist/cli/check/index.js +2 -0
  9. package/dist/cli/flags.js +1 -1
  10. package/dist/cli/index.js +0 -3
  11. package/dist/core/build/index.js +2 -0
  12. package/dist/core/config/config.js +2 -2
  13. package/dist/core/config/schema.d.ts +26 -26
  14. package/dist/core/config/schema.js +3 -3
  15. package/dist/core/constants.js +1 -1
  16. package/dist/core/cookies/cookies.d.ts +7 -3
  17. package/dist/core/cookies/cookies.js +8 -8
  18. package/dist/core/cookies/index.d.ts +1 -0
  19. package/dist/core/dev/container.js +2 -2
  20. package/dist/core/dev/dev.js +3 -1
  21. package/dist/core/messages.js +2 -2
  22. package/dist/core/preview/index.js +9 -0
  23. package/dist/core/render/result.js +1 -0
  24. package/dist/core/routing/manifest/create.js +1 -1
  25. package/dist/core/sync/index.js +2 -0
  26. package/dist/core/util.d.ts +4 -0
  27. package/dist/core/util.js +6 -0
  28. package/dist/prefetch/index.js +11 -1
  29. package/dist/runtime/client/dev-overlay/plugins/audit/a11y.js +126 -0
  30. package/dist/runtime/client/visible.js +6 -2
  31. package/dist/runtime/client/visible.prebuilt.d.ts +1 -1
  32. package/dist/runtime/client/visible.prebuilt.js +1 -1
  33. package/dist/runtime/server/render/common.js +9 -0
  34. package/dist/runtime/server/render/component.js +9 -0
  35. package/dist/runtime/server/render/instruction.d.ts +11 -1
  36. package/dist/transitions/router.js +9 -3
  37. package/dist/vite-plugin-dev-overlay/vite-plugin-dev-overlay.js +8 -0
  38. package/package.json +4 -1
package/astro-jsx.d.ts CHANGED
@@ -499,6 +499,15 @@ declare namespace astroHTML.JSX {
499
499
  KebabCSSDOMProperties & DOMCSSProperties & AllCSSProperties
500
500
  >;
501
501
 
502
+ interface CSSProperties extends StyleObject {
503
+ /**
504
+ * Extend namespace to add properties or an index signature of your own.
505
+ *
506
+ * For more information, visit:
507
+ * https://docs.astro.build/en/guides/typescript/#built-in-html-attributes
508
+ */
509
+ }
510
+
502
511
  interface HTMLAttributes extends AriaAttributes, DOMAttributes, AstroBuiltinAttributes {
503
512
  // Standard HTML Attributes
504
513
  accesskey?: string | undefined | null;
@@ -547,7 +556,7 @@ declare namespace astroHTML.JSX {
547
556
  popover?: boolean | string | undefined | null;
548
557
  slot?: string | undefined | null;
549
558
  spellcheck?: 'true' | 'false' | boolean | undefined | null;
550
- style?: string | StyleObject | undefined | null;
559
+ style?: string | CSSProperties | undefined | null;
551
560
  tabindex?: number | string | undefined | null;
552
561
  title?: string | undefined | null;
553
562
  translate?: 'yes' | 'no' | '' | undefined | null;
package/config.d.ts CHANGED
@@ -2,6 +2,7 @@ type ViteUserConfig = import('vite').UserConfig;
2
2
  type ViteUserConfigFn = import('vite').UserConfigFn;
3
3
  type AstroUserConfig = import('./dist/@types/astro.js').AstroUserConfig;
4
4
  type ImageServiceConfig = import('./dist/@types/astro.js').ImageServiceConfig;
5
+ type SharpImageServiceConfig = import('./dist/assets/services/sharp.js').SharpImageServiceConfig;
5
6
 
6
7
  /**
7
8
  * See the full Astro Configuration API Documentation
@@ -17,7 +18,7 @@ export function getViteConfig(config: ViteUserConfig): ViteUserConfigFn;
17
18
  /**
18
19
  * Return the configuration needed to use the Sharp-based image service
19
20
  */
20
- export function sharpImageService(): ImageServiceConfig;
21
+ export function sharpImageService(config?: SharpImageServiceConfig): ImageServiceConfig;
21
22
 
22
23
  /**
23
24
  * Return the configuration needed to use the Squoosh-based image service
package/config.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  export { defineConfig, getViteConfig } from './dist/config/index.js';
2
2
 
3
- export function sharpImageService() {
3
+ export function sharpImageService(config = {}) {
4
4
  return {
5
5
  entrypoint: 'astro/assets/services/sharp',
6
- config: {},
6
+ config,
7
7
  };
8
8
  }
9
9
 
@@ -27,14 +27,15 @@ export type { ExternalImageService, ImageService, LocalImageService, } from '../
27
27
  export type { GetImageResult, ImageInputFormat, ImageMetadata, ImageOutputFormat, ImageQuality, ImageQualityPreset, ImageTransform, UnresolvedImageTransform, } from '../assets/types.js';
28
28
  export type { RemotePattern } from '../assets/utils/remotePattern.js';
29
29
  export type { SSRManifest } from '../core/app/types.js';
30
- export type { AstroCookies } from '../core/cookies/index.js';
30
+ export type { AstroCookieGetOptions, AstroCookieSetOptions, AstroCookies, } from '../core/cookies/index.js';
31
31
  export interface AstroBuiltinProps {
32
32
  'client:load'?: boolean;
33
33
  'client:idle'?: boolean;
34
34
  'client:media'?: string;
35
- 'client:visible'?: boolean;
35
+ 'client:visible'?: ClientVisibleOptions | boolean;
36
36
  'client:only'?: boolean | string;
37
37
  }
38
+ export type ClientVisibleOptions = Pick<IntersectionObserverInit, 'rootMargin'>;
38
39
  export interface TransitionAnimation {
39
40
  name: string;
40
41
  delay?: number | string;
@@ -95,7 +96,7 @@ export interface CLIFlags {
95
96
  host?: string | boolean;
96
97
  port?: number;
97
98
  config?: string;
98
- open?: boolean;
99
+ open?: string | boolean;
99
100
  }
100
101
  /**
101
102
  * Astro global available in all contexts in .astro files
@@ -299,19 +300,21 @@ type ServerConfig = {
299
300
  headers?: OutgoingHttpHeaders;
300
301
  /**
301
302
  * @name server.open
302
- * @type {boolean}
303
+ * @type {string | boolean}
303
304
  * @default `false`
304
- * @version 2.1.8
305
+ * @version 4.1.0
305
306
  * @description
306
- * Control whether the dev server should open in your browser window on startup.
307
+ * Controls whether the dev server should open in your browser window on startup.
308
+ *
309
+ * Pass a full URL string (e.g. "http://example.com") or a pathname (e.g. "/about") to specify the URL to open.
307
310
  *
308
311
  * ```js
309
312
  * {
310
- * server: { open: true }
313
+ * server: { open: "/about" }
311
314
  * }
312
315
  * ```
313
316
  */
314
- open?: boolean;
317
+ open?: string | boolean;
315
318
  };
316
319
  export interface ViteUserConfig extends vite.UserConfig {
317
320
  ssr?: vite.SSROptions;
@@ -853,7 +856,7 @@ export interface AstroUserConfig {
853
856
  /**
854
857
  * @docs
855
858
  * @name prefetch.defaultStrategy
856
- * @type {'tap' | 'hover' | 'viewport'}
859
+ * @type {'tap' | 'hover' | 'viewport' | 'load'}
857
860
  * @default `'hover'`
858
861
  * @description
859
862
  * The default prefetch strategy to use when the `data-astro-prefetch` attribute is set on a link with no value.
@@ -861,6 +864,7 @@ export interface AstroUserConfig {
861
864
  * - `'tap'`: Prefetch just before you click on the link.
862
865
  * - `'hover'`: Prefetch when you hover over or focus on the link. (default)
863
866
  * - `'viewport'`: Prefetch as the links enter the viewport.
867
+ * - `'load'`: Prefetch the link without any restrictions.
864
868
  *
865
869
  * You can override this default value and select a different strategy for any individual link by setting a value on the attribute.
866
870
  *
@@ -868,7 +872,7 @@ export interface AstroUserConfig {
868
872
  * <a href="/about" data-astro-prefetch="viewport">About</a>
869
873
  * ```
870
874
  */
871
- defaultStrategy?: 'tap' | 'hover' | 'viewport';
875
+ defaultStrategy?: 'tap' | 'hover' | 'viewport' | 'load';
872
876
  };
873
877
  /**
874
878
  * @docs
@@ -922,16 +926,19 @@ export interface AstroUserConfig {
922
926
  * ```
923
927
  */
924
928
  /**
929
+ * @docs
925
930
  * @name server.open
926
- * @type {boolean}
931
+ * @type {string | boolean}
927
932
  * @default `false`
928
933
  * @version 2.1.8
929
934
  * @description
930
- * Control whether the dev server should open in your browser window on startup.
935
+ * Controls whether the dev server should open in your browser window on startup.
936
+ *
937
+ * Pass a full URL string (e.g. "http://example.com") or a pathname (e.g. "/about") to specify the URL to open.
931
938
  *
932
939
  * ```js
933
940
  * {
934
- * server: { open: true }
941
+ * server: { open: "/about" }
935
942
  * }
936
943
  * ```
937
944
  */
@@ -990,13 +997,31 @@ export interface AstroUserConfig {
990
997
  * ```js
991
998
  * {
992
999
  * image: {
993
- * // Example: Enable the Sharp-based image service
994
- * service: { entrypoint: 'astro/assets/services/sharp' },
1000
+ * // Example: Enable the Sharp-based image service with a custom config
1001
+ * service: {
1002
+ * entrypoint: 'astro/assets/services/sharp',
1003
+ * config: {
1004
+ * limitInputPixels: false,
1005
+ * },
1006
+ * },
995
1007
  * },
996
1008
  * }
997
1009
  * ```
998
1010
  */
999
1011
  service?: ImageServiceConfig;
1012
+ /**
1013
+ * @docs
1014
+ * @name image.service.config.limitInputPixels
1015
+ * @kind h4
1016
+ * @type {boolean}
1017
+ * @default `true`
1018
+ * @version 4.1.0
1019
+ * @description
1020
+ *
1021
+ * Whether or not to limit the size of images that the Sharp image service will process.
1022
+ *
1023
+ * Set `false` to bypass the default image size limit for the Sharp image service and process large images.
1024
+ */
1000
1025
  /**
1001
1026
  * @docs
1002
1027
  * @name image.domains
@@ -2025,6 +2050,18 @@ export interface SSRLoadedRenderer extends AstroRenderer {
2025
2050
  attrs?: Record<string, string>;
2026
2051
  }>;
2027
2052
  supportsAstroStaticSlot?: boolean;
2053
+ /**
2054
+ * If provided, Astro will call this function and inject the returned
2055
+ * script in the HTML before the first component handled by this renderer.
2056
+ *
2057
+ * This feature is needed by some renderers (in particular, by Solid). The
2058
+ * Solid official hydration script sets up a page-level data structure.
2059
+ * It is mainly used to transfer data between the server side render phase
2060
+ * and the browser application state. Solid Components rendered later in
2061
+ * the HTML may inject tiny scripts into the HTML that call into this
2062
+ * page-level data structure.
2063
+ */
2064
+ renderHydrationScript?: () => string;
2028
2065
  };
2029
2066
  }
2030
2067
  export type HookParameters<Hook extends keyof AstroIntegration['hooks'], Fn = AstroIntegration['hooks'][Hook]> = Fn extends (...args: any) => any ? Parameters<Fn>[0] : never;
@@ -2209,6 +2246,12 @@ export interface SSRResult {
2209
2246
  */
2210
2247
  export interface SSRMetadata {
2211
2248
  hasHydrationScript: boolean;
2249
+ /**
2250
+ * Names of renderers that have injected their hydration scripts
2251
+ * into the current page. For example, Solid SSR needs a hydration
2252
+ * script in the page HTML before the first Solid component.
2253
+ */
2254
+ rendererSpecificHydrationScripts: Set<string>;
2212
2255
  hasDirectives: Set<string>;
2213
2256
  hasRenderedHead: boolean;
2214
2257
  headInTree: boolean;
@@ -1,3 +1,9 @@
1
1
  import { type LocalImageService } from './service.js';
2
- declare const sharpService: LocalImageService;
2
+ export interface SharpImageServiceConfig {
3
+ /**
4
+ * The `limitInputPixels` option passed to Sharp. See https://sharp.pixelplumbing.com/api-constructor for more information
5
+ */
6
+ limitInputPixels?: number;
7
+ }
8
+ declare const sharpService: LocalImageService<SharpImageServiceConfig>;
3
9
  export default sharpService;
@@ -25,13 +25,17 @@ const sharpService = {
25
25
  parseURL: baseService.parseURL,
26
26
  getHTMLAttributes: baseService.getHTMLAttributes,
27
27
  getSrcSet: baseService.getSrcSet,
28
- async transform(inputBuffer, transformOptions) {
28
+ async transform(inputBuffer, transformOptions, config) {
29
29
  if (!sharp)
30
30
  sharp = await loadSharp();
31
31
  const transform = transformOptions;
32
32
  if (transform.format === "svg")
33
33
  return { data: inputBuffer, format: "svg" };
34
- let result = sharp(inputBuffer, { failOnError: false, pages: -1 });
34
+ const result = sharp(inputBuffer, {
35
+ failOnError: false,
36
+ pages: -1,
37
+ limitInputPixels: config.service.config.limitInputPixels
38
+ });
35
39
  result.rotate();
36
40
  if (transform.height && !transform.width) {
37
41
  result.resize({ height: Math.round(transform.height) });
@@ -24,7 +24,7 @@ import * as msg from "../../core/messages.js";
24
24
  import { printHelp } from "../../core/messages.js";
25
25
  import { appendForwardSlash } from "../../core/path.js";
26
26
  import { apply as applyPolyfill } from "../../core/polyfill.js";
27
- import { parseNpmName } from "../../core/util.js";
27
+ import { ensureProcessNodeEnv, parseNpmName } from "../../core/util.js";
28
28
  import { eventCliSession, telemetry } from "../../events/index.js";
29
29
  import { createLoggerFromFlags, flagsToAstroInlineConfig } from "../flags.js";
30
30
  import { generate, parse, t, visit } from "./babel.js";
@@ -71,6 +71,7 @@ async function getRegistry() {
71
71
  }
72
72
  }
73
73
  async function add(names, { flags }) {
74
+ ensureProcessNodeEnv("production");
74
75
  const inlineConfig = flagsToAstroInlineConfig(flags);
75
76
  const { userConfig } = await resolveConfig(inlineConfig, "add");
76
77
  telemetry.record(eventCliSession("add", userConfig));
@@ -614,7 +615,7 @@ ${message}`
614
615
  } catch (err) {
615
616
  spinner.fail();
616
617
  logger.debug("add", "Error installing dependencies", err);
617
- console.error("\n", err.stdout, "\n");
618
+ console.error("\n", err.stdout || err.message, "\n");
618
619
  return 3 /* failure */;
619
620
  }
620
621
  } else {
@@ -1,7 +1,9 @@
1
1
  import path from "node:path";
2
2
  import { createLoggerFromFlags, flagsToAstroInlineConfig } from "../flags.js";
3
3
  import { getPackage } from "../install-package.js";
4
+ import { ensureProcessNodeEnv } from "../../core/util.js";
4
5
  async function check(flags) {
6
+ ensureProcessNodeEnv("production");
5
7
  const logger = createLoggerFromFlags(flags);
6
8
  const getPackageOpts = { skipAsk: flags.yes || flags.y, cwd: flags.root };
7
9
  const checkPackage = await getPackage(
package/dist/cli/flags.js CHANGED
@@ -14,7 +14,7 @@ function flagsToAstroInlineConfig(flags) {
14
14
  server: {
15
15
  port: typeof flags.port === "number" ? flags.port : void 0,
16
16
  host: typeof flags.host === "string" || typeof flags.host === "boolean" ? flags.host : void 0,
17
- open: typeof flags.open === "boolean" ? flags.open : void 0
17
+ open: typeof flags.open === "string" || typeof flags.open === "boolean" ? flags.open : void 0
18
18
  }
19
19
  };
20
20
  }
package/dist/cli/index.js CHANGED
@@ -98,9 +98,6 @@ async function runCommand(cmd, flags) {
98
98
  const { enableVerboseLogging } = await import("../core/logger/node.js");
99
99
  enableVerboseLogging();
100
100
  }
101
- if (!process.env.NODE_ENV) {
102
- process.env.NODE_ENV = cmd === "dev" ? "development" : "production";
103
- }
104
101
  const { notify } = await import("./telemetry/index.js");
105
102
  await notify();
106
103
  switch (cmd) {
@@ -23,7 +23,9 @@ import { createRouteManifest } from "../routing/index.js";
23
23
  import { collectPagesData } from "./page-data.js";
24
24
  import { staticBuild, viteBuild } from "./static-build.js";
25
25
  import { getTimeStat } from "./util.js";
26
+ import { ensureProcessNodeEnv } from "../util.js";
26
27
  async function build(inlineConfig, options = {}) {
28
+ ensureProcessNodeEnv("production");
27
29
  applyPolyfill();
28
30
  const logger = createNodeLogger(inlineConfig);
29
31
  const { userConfig, astroConfig } = await resolveConfig(inlineConfig, "build");
@@ -34,9 +34,9 @@ function resolveFlags(flags) {
34
34
  site: typeof flags.site === "string" ? flags.site : void 0,
35
35
  base: typeof flags.base === "string" ? flags.base : void 0,
36
36
  port: typeof flags.port === "number" ? flags.port : void 0,
37
- open: typeof flags.open === "boolean" ? flags.open : void 0,
38
37
  config: typeof flags.config === "string" ? flags.config : void 0,
39
- host: typeof flags.host === "string" || typeof flags.host === "boolean" ? flags.host : void 0
38
+ host: typeof flags.host === "string" || typeof flags.host === "boolean" ? flags.host : void 0,
39
+ open: typeof flags.open === "string" || typeof flags.open === "boolean" ? flags.open : void 0
40
40
  };
41
41
  }
42
42
  function resolveRoot(cwd) {
@@ -76,24 +76,24 @@ export declare const AstroConfigSchema: z.ZodObject<{
76
76
  inlineStylesheets?: "always" | "never" | "auto" | undefined;
77
77
  }>>;
78
78
  server: z.ZodEffects<z.ZodDefault<z.ZodObject<{
79
- open: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
79
+ open: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodBoolean]>>>;
80
80
  host: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodBoolean]>>>;
81
81
  port: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
82
82
  headers: z.ZodOptional<z.ZodType<OutgoingHttpHeaders, z.ZodTypeDef, OutgoingHttpHeaders>>;
83
83
  }, "strip", z.ZodTypeAny, {
84
84
  host: string | boolean;
85
85
  port: number;
86
- open: boolean;
86
+ open: string | boolean;
87
87
  headers?: OutgoingHttpHeaders | undefined;
88
88
  }, {
89
- open?: boolean | undefined;
89
+ open?: string | boolean | undefined;
90
90
  host?: string | boolean | undefined;
91
91
  port?: number | undefined;
92
92
  headers?: OutgoingHttpHeaders | undefined;
93
93
  }>>, {
94
94
  host: string | boolean;
95
95
  port: number;
96
- open: boolean;
96
+ open: string | boolean;
97
97
  headers?: OutgoingHttpHeaders | undefined;
98
98
  }, unknown>;
99
99
  redirects: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodObject<{
@@ -108,13 +108,13 @@ export declare const AstroConfigSchema: z.ZodObject<{
108
108
  }>]>>>;
109
109
  prefetch: z.ZodOptional<z.ZodUnion<[z.ZodBoolean, z.ZodObject<{
110
110
  prefetchAll: z.ZodOptional<z.ZodBoolean>;
111
- defaultStrategy: z.ZodOptional<z.ZodEnum<["tap", "hover", "viewport"]>>;
111
+ defaultStrategy: z.ZodOptional<z.ZodEnum<["tap", "hover", "viewport", "load"]>>;
112
112
  }, "strip", z.ZodTypeAny, {
113
113
  prefetchAll?: boolean | undefined;
114
- defaultStrategy?: "tap" | "hover" | "viewport" | undefined;
114
+ defaultStrategy?: "tap" | "hover" | "viewport" | "load" | undefined;
115
115
  }, {
116
116
  prefetchAll?: boolean | undefined;
117
- defaultStrategy?: "tap" | "hover" | "viewport" | undefined;
117
+ defaultStrategy?: "tap" | "hover" | "viewport" | "load" | undefined;
118
118
  }>]>>;
119
119
  image: z.ZodDefault<z.ZodObject<{
120
120
  endpoint: z.ZodOptional<z.ZodString>;
@@ -324,7 +324,7 @@ export declare const AstroConfigSchema: z.ZodObject<{
324
324
  server: {
325
325
  host: string | boolean;
326
326
  port: number;
327
- open: boolean;
327
+ open: string | boolean;
328
328
  headers?: OutgoingHttpHeaders | undefined;
329
329
  };
330
330
  redirects: Record<string, string | {
@@ -389,7 +389,7 @@ export declare const AstroConfigSchema: z.ZodObject<{
389
389
  } | undefined;
390
390
  prefetch?: boolean | {
391
391
  prefetchAll?: boolean | undefined;
392
- defaultStrategy?: "tap" | "hover" | "viewport" | undefined;
392
+ defaultStrategy?: "tap" | "hover" | "viewport" | "load" | undefined;
393
393
  } | undefined;
394
394
  i18n?: {
395
395
  defaultLocale: string;
@@ -434,7 +434,7 @@ export declare const AstroConfigSchema: z.ZodObject<{
434
434
  }> | undefined;
435
435
  prefetch?: boolean | {
436
436
  prefetchAll?: boolean | undefined;
437
- defaultStrategy?: "tap" | "hover" | "viewport" | undefined;
437
+ defaultStrategy?: "tap" | "hover" | "viewport" | "load" | undefined;
438
438
  } | undefined;
439
439
  image?: {
440
440
  endpoint?: string | undefined;
@@ -590,13 +590,13 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
590
590
  }[], unknown>;
591
591
  prefetch: z.ZodOptional<z.ZodUnion<[z.ZodBoolean, z.ZodObject<{
592
592
  prefetchAll: z.ZodOptional<z.ZodBoolean>;
593
- defaultStrategy: z.ZodOptional<z.ZodEnum<["tap", "hover", "viewport"]>>;
593
+ defaultStrategy: z.ZodOptional<z.ZodEnum<["tap", "hover", "viewport", "load"]>>;
594
594
  }, "strip", z.ZodTypeAny, {
595
595
  prefetchAll?: boolean | undefined;
596
- defaultStrategy?: "tap" | "hover" | "viewport" | undefined;
596
+ defaultStrategy?: "tap" | "hover" | "viewport" | "load" | undefined;
597
597
  }, {
598
598
  prefetchAll?: boolean | undefined;
599
- defaultStrategy?: "tap" | "hover" | "viewport" | undefined;
599
+ defaultStrategy?: "tap" | "hover" | "viewport" | "load" | undefined;
600
600
  }>]>>;
601
601
  devToolbar: z.ZodDefault<z.ZodObject<{
602
602
  enabled: z.ZodDefault<z.ZodBoolean>;
@@ -767,27 +767,27 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
767
767
  inlineStylesheets?: "always" | "never" | "auto" | undefined;
768
768
  }>>>;
769
769
  server: z.ZodEffects<z.ZodDefault<z.ZodOptional<z.ZodObject<{
770
+ open: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodBoolean]>>>;
770
771
  host: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodBoolean]>>>;
771
772
  port: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
772
- open: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
773
773
  headers: z.ZodOptional<z.ZodType<OutgoingHttpHeaders, z.ZodTypeDef, OutgoingHttpHeaders>>;
774
774
  streaming: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
775
775
  }, "strip", z.ZodTypeAny, {
776
776
  host: string | boolean;
777
777
  port: number;
778
- open: boolean;
778
+ open: string | boolean;
779
779
  streaming: boolean;
780
780
  headers?: OutgoingHttpHeaders | undefined;
781
781
  }, {
782
+ open?: string | boolean | undefined;
782
783
  host?: string | boolean | undefined;
783
784
  port?: number | undefined;
784
- open?: boolean | undefined;
785
785
  headers?: OutgoingHttpHeaders | undefined;
786
786
  streaming?: boolean | undefined;
787
787
  }>>>, {
788
788
  host: string | boolean;
789
789
  port: number;
790
- open: boolean;
790
+ open: string | boolean;
791
791
  streaming: boolean;
792
792
  headers?: OutgoingHttpHeaders | undefined;
793
793
  }, unknown>;
@@ -809,7 +809,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
809
809
  server: {
810
810
  host: string | boolean;
811
811
  port: number;
812
- open: boolean;
812
+ open: string | boolean;
813
813
  streaming: boolean;
814
814
  headers?: OutgoingHttpHeaders | undefined;
815
815
  };
@@ -875,7 +875,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
875
875
  } | undefined;
876
876
  prefetch?: boolean | {
877
877
  prefetchAll?: boolean | undefined;
878
- defaultStrategy?: "tap" | "hover" | "viewport" | undefined;
878
+ defaultStrategy?: "tap" | "hover" | "viewport" | "load" | undefined;
879
879
  } | undefined;
880
880
  i18n?: {
881
881
  defaultLocale: string;
@@ -917,7 +917,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
917
917
  integrations?: unknown;
918
918
  prefetch?: boolean | {
919
919
  prefetchAll?: boolean | undefined;
920
- defaultStrategy?: "tap" | "hover" | "viewport" | undefined;
920
+ defaultStrategy?: "tap" | "hover" | "viewport" | "load" | undefined;
921
921
  } | undefined;
922
922
  devToolbar?: {
923
923
  enabled?: boolean | undefined;
@@ -989,7 +989,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
989
989
  server: {
990
990
  host: string | boolean;
991
991
  port: number;
992
- open: boolean;
992
+ open: string | boolean;
993
993
  streaming: boolean;
994
994
  headers?: OutgoingHttpHeaders | undefined;
995
995
  };
@@ -1055,7 +1055,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
1055
1055
  } | undefined;
1056
1056
  prefetch?: boolean | {
1057
1057
  prefetchAll?: boolean | undefined;
1058
- defaultStrategy?: "tap" | "hover" | "viewport" | undefined;
1058
+ defaultStrategy?: "tap" | "hover" | "viewport" | "load" | undefined;
1059
1059
  } | undefined;
1060
1060
  i18n?: {
1061
1061
  defaultLocale: string;
@@ -1097,7 +1097,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
1097
1097
  integrations?: unknown;
1098
1098
  prefetch?: boolean | {
1099
1099
  prefetchAll?: boolean | undefined;
1100
- defaultStrategy?: "tap" | "hover" | "viewport" | undefined;
1100
+ defaultStrategy?: "tap" | "hover" | "viewport" | "load" | undefined;
1101
1101
  } | undefined;
1102
1102
  devToolbar?: {
1103
1103
  enabled?: boolean | undefined;
@@ -1169,7 +1169,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
1169
1169
  server: {
1170
1170
  host: string | boolean;
1171
1171
  port: number;
1172
- open: boolean;
1172
+ open: string | boolean;
1173
1173
  streaming: boolean;
1174
1174
  headers?: OutgoingHttpHeaders | undefined;
1175
1175
  };
@@ -1235,7 +1235,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
1235
1235
  } | undefined;
1236
1236
  prefetch?: boolean | {
1237
1237
  prefetchAll?: boolean | undefined;
1238
- defaultStrategy?: "tap" | "hover" | "viewport" | undefined;
1238
+ defaultStrategy?: "tap" | "hover" | "viewport" | "load" | undefined;
1239
1239
  } | undefined;
1240
1240
  i18n?: {
1241
1241
  defaultLocale: string;
@@ -1277,7 +1277,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
1277
1277
  integrations?: unknown;
1278
1278
  prefetch?: boolean | {
1279
1279
  prefetchAll?: boolean | undefined;
1280
- defaultStrategy?: "tap" | "hover" | "viewport" | undefined;
1280
+ defaultStrategy?: "tap" | "hover" | "viewport" | "load" | undefined;
1281
1281
  } | undefined;
1282
1282
  devToolbar?: {
1283
1283
  enabled?: boolean | undefined;
@@ -80,7 +80,7 @@ const AstroConfigSchema = z.object({
80
80
  (val) => typeof val === "function" ? val({ command: "error" }) : val,
81
81
  // validate
82
82
  z.object({
83
- open: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.server.open),
83
+ open: z.union([z.string(), z.boolean()]).optional().default(ASTRO_CONFIG_DEFAULTS.server.open),
84
84
  host: z.union([z.string(), z.boolean()]).optional().default(ASTRO_CONFIG_DEFAULTS.server.host),
85
85
  port: z.number().optional().default(ASTRO_CONFIG_DEFAULTS.server.port),
86
86
  headers: z.custom().optional()
@@ -108,7 +108,7 @@ const AstroConfigSchema = z.object({
108
108
  z.boolean(),
109
109
  z.object({
110
110
  prefetchAll: z.boolean().optional(),
111
- defaultStrategy: z.enum(["tap", "hover", "viewport"]).optional()
111
+ defaultStrategy: z.enum(["tap", "hover", "viewport", "load"]).optional()
112
112
  })
113
113
  ]).optional(),
114
114
  image: z.object({
@@ -289,9 +289,9 @@ function createRelativeSchema(cmd, fileProtocolRoot) {
289
289
  },
290
290
  // validate
291
291
  z.object({
292
+ open: z.union([z.string(), z.boolean()]).optional().default(ASTRO_CONFIG_DEFAULTS.server.open),
292
293
  host: z.union([z.string(), z.boolean()]).optional().default(ASTRO_CONFIG_DEFAULTS.server.host),
293
294
  port: z.number().optional().default(ASTRO_CONFIG_DEFAULTS.server.port),
294
- open: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.server.open),
295
295
  headers: z.custom().optional(),
296
296
  streaming: z.boolean().optional().default(true)
297
297
  }).optional().default({})
@@ -1,4 +1,4 @@
1
- const ASTRO_VERSION = "4.0.9";
1
+ const ASTRO_VERSION = "4.1.0";
2
2
  const SUPPORTED_MARKDOWN_FILE_EXTENSIONS = [
3
3
  ".markdown",
4
4
  ".mdown",
@@ -1,4 +1,4 @@
1
- interface AstroCookieSetOptions {
1
+ export interface AstroCookieSetOptions {
2
2
  domain?: string;
3
3
  expires?: Date;
4
4
  httpOnly?: boolean;
@@ -6,6 +6,10 @@ interface AstroCookieSetOptions {
6
6
  path?: string;
7
7
  sameSite?: boolean | 'lax' | 'none' | 'strict';
8
8
  secure?: boolean;
9
+ encode?: (value: string) => string;
10
+ }
11
+ export interface AstroCookieGetOptions {
12
+ decode?: (value: string) => string;
9
13
  }
10
14
  type AstroCookieDeleteOptions = Pick<AstroCookieSetOptions, 'domain' | 'path'>;
11
15
  interface AstroCookieInterface {
@@ -44,14 +48,14 @@ declare class AstroCookies implements AstroCookiesInterface {
44
48
  * @param key The cookie to get.
45
49
  * @returns An object containing the cookie value as well as convenience methods for converting its value.
46
50
  */
47
- get(key: string): AstroCookie | undefined;
51
+ get(key: string, options?: AstroCookieGetOptions | undefined): AstroCookie | undefined;
48
52
  /**
49
53
  * Astro.cookies.has(key) returns a boolean indicating whether this cookie is either
50
54
  * part of the initial request or set via Astro.cookies.set(key)
51
55
  * @param key The cookie to check for.
52
56
  * @returns
53
57
  */
54
- has(key: string): boolean;
58
+ has(key: string, options?: AstroCookieGetOptions | undefined): boolean;
55
59
  /**
56
60
  * Astro.cookies.set(key, value) is used to set a cookie's value. If provided
57
61
  * an object it will be stringified via JSON.stringify(value). Additionally you
@@ -62,7 +62,7 @@ class AstroCookies {
62
62
  * @param key The cookie to get.
63
63
  * @returns An object containing the cookie value as well as convenience methods for converting its value.
64
64
  */
65
- get(key) {
65
+ get(key, options = void 0) {
66
66
  if (this.#outgoing?.has(key)) {
67
67
  let [serializedValue, , isSetValue] = this.#outgoing.get(key);
68
68
  if (isSetValue) {
@@ -71,7 +71,7 @@ class AstroCookies {
71
71
  return void 0;
72
72
  }
73
73
  }
74
- const values = this.#ensureParsed();
74
+ const values = this.#ensureParsed(options);
75
75
  if (key in values) {
76
76
  const value = values[key];
77
77
  return new AstroCookie(value);
@@ -83,12 +83,12 @@ class AstroCookies {
83
83
  * @param key The cookie to check for.
84
84
  * @returns
85
85
  */
86
- has(key) {
86
+ has(key, options = void 0) {
87
87
  if (this.#outgoing?.has(key)) {
88
88
  let [, , isSetValue] = this.#outgoing.get(key);
89
89
  return isSetValue;
90
90
  }
91
- const values = this.#ensureParsed();
91
+ const values = this.#ensureParsed(options);
92
92
  return !!values[key];
93
93
  }
94
94
  /**
@@ -140,9 +140,9 @@ class AstroCookies {
140
140
  yield value[1];
141
141
  }
142
142
  }
143
- #ensureParsed() {
143
+ #ensureParsed(options = void 0) {
144
144
  if (!this.#requestValues) {
145
- this.#parse();
145
+ this.#parse(options);
146
146
  }
147
147
  if (!this.#requestValues) {
148
148
  this.#requestValues = {};
@@ -155,12 +155,12 @@ class AstroCookies {
155
155
  }
156
156
  return this.#outgoing;
157
157
  }
158
- #parse() {
158
+ #parse(options = void 0) {
159
159
  const raw = this.#request.headers.get("cookie");
160
160
  if (!raw) {
161
161
  return;
162
162
  }
163
- this.#requestValues = parse(raw);
163
+ this.#requestValues = parse(raw, options);
164
164
  }
165
165
  }
166
166
  export {
@@ -1,2 +1,3 @@
1
1
  export { AstroCookies } from './cookies.js';
2
2
  export { attachCookiesToResponse, getSetCookiesFromResponse, responseHasCookies, } from './response.js';
3
+ export type { AstroCookieSetOptions, AstroCookieGetOptions } from './cookies.js';
@@ -26,9 +26,9 @@ async function createContainer({
26
26
  settings = injectImageEndpoint(settings, "dev");
27
27
  const {
28
28
  base,
29
- server: { host, headers, open: shouldOpen }
29
+ server: { host, headers, open: serverOpen }
30
30
  } = settings.config;
31
- const open = shouldOpen ? base : false;
31
+ const open = typeof serverOpen == "string" ? serverOpen : serverOpen ? base : false;
32
32
  const rendererClientEntries = settings.renderers.map((r) => r.clientEntrypoint).filter(Boolean);
33
33
  const viteConfig = await createVite(
34
34
  {
@@ -6,7 +6,9 @@ import { telemetry } from "../../events/index.js";
6
6
  import * as msg from "../messages.js";
7
7
  import { startContainer } from "./container.js";
8
8
  import { createContainerWithAutomaticRestart } from "./restart.js";
9
+ import { ensureProcessNodeEnv } from "../util.js";
9
10
  async function dev(inlineConfig) {
11
+ ensureProcessNodeEnv("development");
10
12
  const devStart = performance.now();
11
13
  await telemetry.record([]);
12
14
  const restart = await createContainerWithAutomaticRestart({ inlineConfig, fs });
@@ -21,7 +23,7 @@ async function dev(inlineConfig) {
21
23
  base: restart.container.settings.config.base
22
24
  })
23
25
  );
24
- const currentVersion = "4.0.9";
26
+ const currentVersion = "4.1.0";
25
27
  if (currentVersion.includes("-")) {
26
28
  logger.warn("SKIP_FORMAT", msg.prerelease({ currentVersion }));
27
29
  }
@@ -36,7 +36,7 @@ function serverStart({
36
36
  host,
37
37
  base
38
38
  }) {
39
- const version = "4.0.9";
39
+ const version = "4.1.0";
40
40
  const localPrefix = `${dim("\u2503")} Local `;
41
41
  const networkPrefix = `${dim("\u2503")} Network `;
42
42
  const emptyPrefix = " ".repeat(11);
@@ -258,7 +258,7 @@ function printHelp({
258
258
  message.push(
259
259
  linebreak(),
260
260
  ` ${bgGreen(black(` ${commandName} `))} ${green(
261
- `v${"4.0.9"}`
261
+ `v${"4.1.0"}`
262
262
  )} ${headline}`
263
263
  );
264
264
  }
@@ -1,3 +1,4 @@
1
+ import fs from "node:fs";
1
2
  import { createRequire } from "node:module";
2
3
  import { fileURLToPath, pathToFileURL } from "node:url";
3
4
  import { AstroIntegrationLogger } from "../../core/logger/core.js";
@@ -9,7 +10,9 @@ import { createNodeLogger } from "../config/logging.js";
9
10
  import { createSettings } from "../config/settings.js";
10
11
  import createStaticPreviewServer from "./static-preview-server.js";
11
12
  import { getResolvedHostForHttpServer } from "./util.js";
13
+ import { ensureProcessNodeEnv } from "../util.js";
12
14
  async function preview(inlineConfig) {
15
+ ensureProcessNodeEnv("production");
13
16
  const logger = createNodeLogger(inlineConfig);
14
17
  const { userConfig, astroConfig } = await resolveConfig(inlineConfig ?? {}, "preview");
15
18
  telemetry.record(eventCliSession("preview", userConfig));
@@ -21,6 +24,12 @@ async function preview(inlineConfig) {
21
24
  });
22
25
  await runHookConfigDone({ settings, logger });
23
26
  if (settings.config.output === "static") {
27
+ if (!fs.existsSync(settings.config.outDir)) {
28
+ const outDirPath = fileURLToPath(settings.config.outDir);
29
+ throw new Error(
30
+ `[preview] The output directory ${outDirPath} does not exist. Did you run \`astro build\`?`
31
+ );
32
+ }
24
33
  const server2 = await createStaticPreviewServer(settings, logger);
25
34
  return server2;
26
35
  }
@@ -198,6 +198,7 @@ function createResult(args) {
198
198
  response,
199
199
  _metadata: {
200
200
  hasHydrationScript: false,
201
+ rendererSpecificHydrationScripts: /* @__PURE__ */ new Set(),
201
202
  hasRenderedHead: false,
202
203
  hasDirectives: /* @__PURE__ */ new Set(),
203
204
  headInTree: false,
@@ -224,7 +224,7 @@ function createRouteManifest({ settings, cwd, fsMod }, logger) {
224
224
  } else {
225
225
  components.push(item.file);
226
226
  const component = item.file;
227
- const trailingSlash = item.isPage ? settings.config.trailingSlash : "never";
227
+ const { trailingSlash } = settings.config;
228
228
  const pattern = getPattern(segments, settings.config, trailingSlash);
229
229
  const generate = getRouteGenerator(segments, trailingSlash);
230
230
  const pathname = segments.every((segment) => segment.length === 1 && !segment[0].dynamic) ? `/${segments.map((segment) => segment[0].content).join("/")}` : null;
@@ -15,7 +15,9 @@ import { createNodeLogger } from "../config/logging.js";
15
15
  import { createSettings } from "../config/settings.js";
16
16
  import { createVite } from "../create-vite.js";
17
17
  import { AstroError, AstroErrorData, createSafeError, isAstroError } from "../errors/index.js";
18
+ import { ensureProcessNodeEnv } from "../util.js";
18
19
  async function sync(inlineConfig, options) {
20
+ ensureProcessNodeEnv("production");
19
21
  const logger = createNodeLogger(inlineConfig);
20
22
  const { userConfig, astroConfig } = await resolveConfig(inlineConfig ?? {}, "sync");
21
23
  telemetry.record(eventCliSession("sync", userConfig));
@@ -50,3 +50,7 @@ export declare function resolveJsToTs(filePath: string): string;
50
50
  * Resolve the hydration paths so that it can be imported in the client
51
51
  */
52
52
  export declare function resolvePath(specifier: string, importer: string): string;
53
+ /**
54
+ * Set a default NODE_ENV so Vite doesn't set an incorrect default when loading the Astro config
55
+ */
56
+ export declare function ensureProcessNodeEnv(defaultNodeEnv: string): void;
package/dist/core/util.js CHANGED
@@ -174,11 +174,17 @@ function resolvePath(specifier, importer) {
174
174
  return specifier;
175
175
  }
176
176
  }
177
+ function ensureProcessNodeEnv(defaultNodeEnv) {
178
+ if (!process.env.NODE_ENV) {
179
+ process.env.NODE_ENV = defaultNodeEnv;
180
+ }
181
+ }
177
182
  export {
178
183
  NULL_BYTE_PLACEHOLDER,
179
184
  VALID_ID_PREFIX,
180
185
  arraify,
181
186
  emoji,
187
+ ensureProcessNodeEnv,
182
188
  getOutputFilename,
183
189
  isEndpoint,
184
190
  isMarkdownFile,
@@ -17,6 +17,7 @@ function init(defaultOpts) {
17
17
  initTapStrategy();
18
18
  initHoverStrategy();
19
19
  initViewportStrategy();
20
+ initLoadStrategy();
20
21
  }
21
22
  function initTapStrategy() {
22
23
  for (const event of ["touchstart", "mousedown"]) {
@@ -111,6 +112,15 @@ function createViewportIntersectionObserver() {
111
112
  }
112
113
  });
113
114
  }
115
+ function initLoadStrategy() {
116
+ onPageLoad(() => {
117
+ for (const anchor of document.getElementsByTagName("a")) {
118
+ if (elMatchesStrategy(anchor, "load")) {
119
+ prefetch(anchor.href, { with: "link" });
120
+ }
121
+ }
122
+ });
123
+ }
114
124
  function prefetch(url, opts) {
115
125
  const ignoreSlowConnection = opts?.ignoreSlowConnection ?? false;
116
126
  if (!canPrefetchUrl(url, ignoreSlowConnection))
@@ -163,7 +173,7 @@ function elMatchesStrategy(el, strategy) {
163
173
  function isSlowConnection() {
164
174
  if ("connection" in navigator) {
165
175
  const conn = navigator.connection;
166
- return conn.saveData || /(2|3)g/.test(conn.effectiveType);
176
+ return conn.saveData || /2g/.test(conn.effectiveType);
167
177
  }
168
178
  return false;
169
179
  }
@@ -22,6 +22,8 @@
22
22
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
23
  * SOFTWARE.
24
24
  */
25
+ import { aria, roles } from "aria-query";
26
+ import { AXObjectRoles, elementAXObjects } from "axobject-query";
25
27
  const a11y_required_attributes = {
26
28
  a: ["href"],
27
29
  area: ["alt", "aria-label", "aria-labelledby"],
@@ -413,6 +415,58 @@ const a11y = [
413
415
  message: "This will move elements out of the expected tab order, creating a confusing experience for keyboard users.",
414
416
  selector: '[tabindex]:not([tabindex="-1"]):not([tabindex="0"])'
415
417
  },
418
+ {
419
+ code: "a11y-role-has-required-aria-props",
420
+ title: "Missing attributes required for ARIA role",
421
+ message: (element) => {
422
+ const { __astro_role: role, __astro_missing_attributes: required } = element;
423
+ return `${element.localName} element is missing required attributes for its role (${role}): ${required.join(", ")}`;
424
+ },
425
+ selector: "*",
426
+ match(element) {
427
+ const role = getRole(element);
428
+ if (!role)
429
+ return false;
430
+ if (is_semantic_role_element(role, element.localName, getAttributeObject(element))) {
431
+ return;
432
+ }
433
+ const { requiredProps } = roles.get(role);
434
+ const required_role_props = Object.keys(requiredProps);
435
+ const missingProps = required_role_props.filter((prop) => !element.hasAttribute(prop));
436
+ if (missingProps.length > 0) {
437
+ element.__astro_role = role;
438
+ element.__astro_missing_attributes = missingProps;
439
+ return true;
440
+ }
441
+ }
442
+ },
443
+ {
444
+ code: "a11y-role-supports-aria-props",
445
+ title: "Unsupported ARIA attribute",
446
+ message: (element) => {
447
+ const { __astro_role: role, __astro_unsupported_attributes: unsupported } = element;
448
+ return `${element.localName} element has ARIA attributes that are not supported by its role (${role}): ${unsupported.join(
449
+ ", "
450
+ )}`;
451
+ },
452
+ selector: "*",
453
+ match(element) {
454
+ const role = getRole(element);
455
+ if (!role)
456
+ return false;
457
+ const { props } = roles.get(role);
458
+ const attributes = getAttributeObject(element);
459
+ const unsupportedAttributes = aria.keys().filter((attribute) => !(attribute in props));
460
+ const invalidAttributes = Object.keys(attributes).filter(
461
+ (key) => key.startsWith("aria-") && unsupportedAttributes.includes(key)
462
+ );
463
+ if (invalidAttributes.length > 0) {
464
+ element.__astro_role = role;
465
+ element.__astro_unsupported_attributes = invalidAttributes;
466
+ return true;
467
+ }
468
+ }
469
+ },
416
470
  {
417
471
  code: "a11y-structure",
418
472
  title: "Invalid DOM structure",
@@ -447,6 +501,16 @@ const a11y = [
447
501
  }
448
502
  }
449
503
  ];
504
+ const a11y_labelable = [
505
+ "button",
506
+ "input",
507
+ "keygen",
508
+ "meter",
509
+ "output",
510
+ "progress",
511
+ "select",
512
+ "textarea"
513
+ ];
450
514
  const a11y_non_interactive_element_to_interactive_role_exceptions = {
451
515
  ul: ["listbox", "menu", "menubar", "radiogroup", "tablist", "tree", "treegrid"],
452
516
  ol: ["listbox", "menu", "menubar", "radiogroup", "tablist", "tree", "treegrid"],
@@ -455,6 +519,68 @@ const a11y_non_interactive_element_to_interactive_role_exceptions = {
455
519
  td: ["gridcell"],
456
520
  fieldset: ["radiogroup", "presentation"]
457
521
  };
522
+ const combobox_if_list = ["email", "search", "tel", "text", "url"];
523
+ function input_implicit_role(attributes) {
524
+ if (!("type" in attributes))
525
+ return;
526
+ const { type, list } = attributes;
527
+ if (!type)
528
+ return;
529
+ if (list && combobox_if_list.includes(type)) {
530
+ return "combobox";
531
+ }
532
+ return input_type_to_implicit_role.get(type);
533
+ }
534
+ function menuitem_implicit_role(attributes) {
535
+ if (!("type" in attributes))
536
+ return;
537
+ const { type } = attributes;
538
+ if (!type)
539
+ return;
540
+ return menuitem_type_to_implicit_role.get(type);
541
+ }
542
+ function getRole(element) {
543
+ if (element.hasAttribute("role")) {
544
+ return element.getAttribute("role");
545
+ }
546
+ return getImplicitRole(element);
547
+ }
548
+ function getImplicitRole(element) {
549
+ const name = element.localName;
550
+ const attrs = getAttributeObject(element);
551
+ if (name === "menuitem") {
552
+ return menuitem_implicit_role(attrs);
553
+ } else if (name === "input") {
554
+ return input_implicit_role(attrs);
555
+ } else {
556
+ return a11y_implicit_semantics.get(name);
557
+ }
558
+ }
559
+ function getAttributeObject(element) {
560
+ let obj = {};
561
+ for (let i = 0; i < element.attributes.length; i++) {
562
+ const attribute = element.attributes.item(i);
563
+ obj[attribute.name] = attribute.value;
564
+ }
565
+ return obj;
566
+ }
567
+ function is_semantic_role_element(role, tag_name, attributes) {
568
+ for (const [schema, ax_object] of elementAXObjects.entries()) {
569
+ if (schema.name === tag_name && (!schema.attributes || schema.attributes.every((attr) => attributes[attr.name] === attr.value))) {
570
+ for (const name of ax_object) {
571
+ const axRoles = AXObjectRoles.get(name);
572
+ if (axRoles) {
573
+ for (const { name: _name } of axRoles) {
574
+ if (_name === role) {
575
+ return true;
576
+ }
577
+ }
578
+ }
579
+ }
580
+ }
581
+ }
582
+ return false;
583
+ }
458
584
  export {
459
585
  a11y
460
586
  };
@@ -1,8 +1,12 @@
1
- const visibleDirective = (load, _options, el) => {
1
+ const visibleDirective = (load, options, el) => {
2
2
  const cb = async () => {
3
3
  const hydrate = await load();
4
4
  await hydrate();
5
5
  };
6
+ const rawOptions = typeof options.value === "object" ? options.value : void 0;
7
+ const ioOptions = {
8
+ rootMargin: rawOptions?.rootMargin
9
+ };
6
10
  const io = new IntersectionObserver((entries) => {
7
11
  for (const entry of entries) {
8
12
  if (!entry.isIntersecting)
@@ -11,7 +15,7 @@ const visibleDirective = (load, _options, el) => {
11
15
  cb();
12
16
  break;
13
17
  }
14
- });
18
+ }, ioOptions);
15
19
  for (const child of el.children) {
16
20
  io.observe(child);
17
21
  }
@@ -3,5 +3,5 @@
3
3
  * Do not edit this directly, but instead edit that file and rerun the prebuild
4
4
  * to generate this file.
5
5
  */
6
- declare const _default: "(()=>{var r=(i,c,s)=>{let n=async()=>{await(await i())()},t=new IntersectionObserver(e=>{for(let o of e)if(o.isIntersecting){t.disconnect(),n();break}});for(let e of s.children)t.observe(e)};(self.Astro||(self.Astro={})).visible=r;window.dispatchEvent(new Event(\"astro:visible\"));})();";
6
+ declare const _default: "(()=>{var l=(s,i,o)=>{let r=async()=>{await(await s())()},t=typeof i.value==\"object\"?i.value:void 0,c={rootMargin:t==null?void 0:t.rootMargin},n=new IntersectionObserver(e=>{for(let a of e)if(a.isIntersecting){n.disconnect(),r();break}},c);for(let e of o.children)n.observe(e)};(self.Astro||(self.Astro={})).visible=l;window.dispatchEvent(new Event(\"astro:visible\"));})();";
7
7
  export default _default;
@@ -1,4 +1,4 @@
1
- var visible_prebuilt_default = `(()=>{var r=(i,c,s)=>{let n=async()=>{await(await i())()},t=new IntersectionObserver(e=>{for(let o of e)if(o.isIntersecting){t.disconnect(),n();break}});for(let e of s.children)t.observe(e)};(self.Astro||(self.Astro={})).visible=r;window.dispatchEvent(new Event("astro:visible"));})();`;
1
+ var visible_prebuilt_default = `(()=>{var l=(s,i,o)=>{let r=async()=>{await(await s())()},t=typeof i.value=="object"?i.value:void 0,c={rootMargin:t==null?void 0:t.rootMargin},n=new IntersectionObserver(e=>{for(let a of e)if(a.isIntersecting){n.disconnect(),r();break}},c);for(let e of o.children)n.observe(e)};(self.Astro||(self.Astro={})).visible=l;window.dispatchEvent(new Event("astro:visible"));})();`;
2
2
  export {
3
3
  visible_prebuilt_default as default
4
4
  };
@@ -39,6 +39,15 @@ function stringifyChunk(result, chunk) {
39
39
  }
40
40
  return renderAllHeadContent(result);
41
41
  }
42
+ case "renderer-hydration-script": {
43
+ const { rendererSpecificHydrationScripts } = result._metadata;
44
+ const { rendererName } = instruction;
45
+ if (!rendererSpecificHydrationScripts.has(rendererName)) {
46
+ rendererSpecificHydrationScripts.add(rendererName);
47
+ return instruction.render();
48
+ }
49
+ return "";
50
+ }
42
51
  default: {
43
52
  throw new Error(`Unknown chunk type: ${chunk.type}`);
44
53
  }
@@ -291,6 +291,15 @@ ${serializeProps(
291
291
  }
292
292
  }
293
293
  destination.write(createRenderInstruction({ type: "directive", hydration }));
294
+ if (hydration.directive !== "only" && renderer?.ssr.renderHydrationScript) {
295
+ destination.write(
296
+ createRenderInstruction({
297
+ type: "renderer-hydration-script",
298
+ rendererName: renderer.name,
299
+ render: renderer.ssr.renderHydrationScript
300
+ })
301
+ );
302
+ }
294
303
  destination.write(markHTMLString(renderElement("astro-island", island, false)));
295
304
  }
296
305
  };
@@ -6,11 +6,21 @@ export type RenderDirectiveInstruction = {
6
6
  export type RenderHeadInstruction = {
7
7
  type: 'head';
8
8
  };
9
+ /**
10
+ * Render a renderer-specific hydration script before the first component of that
11
+ * framework
12
+ */
13
+ export type RendererHydrationScriptInstruction = {
14
+ type: 'renderer-hydration-script';
15
+ rendererName: string;
16
+ render: () => string;
17
+ };
9
18
  export type MaybeRenderHeadInstruction = {
10
19
  type: 'maybe-head';
11
20
  };
12
- export type RenderInstruction = RenderDirectiveInstruction | RenderHeadInstruction | MaybeRenderHeadInstruction;
21
+ export type RenderInstruction = RenderDirectiveInstruction | RenderHeadInstruction | MaybeRenderHeadInstruction | RendererHydrationScriptInstruction;
13
22
  export declare function createRenderInstruction(instruction: RenderDirectiveInstruction): RenderDirectiveInstruction;
23
+ export declare function createRenderInstruction(instruction: RendererHydrationScriptInstruction): RendererHydrationScriptInstruction;
14
24
  export declare function createRenderInstruction(instruction: RenderHeadInstruction): RenderHeadInstruction;
15
25
  export declare function createRenderInstruction(instruction: MaybeRenderHeadInstruction): MaybeRenderHeadInstruction;
16
26
  export declare function isRenderInstruction(chunk: any): chunk is RenderInstruction;
@@ -114,8 +114,10 @@ function runScripts() {
114
114
  }
115
115
  return wait;
116
116
  }
117
- const moveToLocation = (to, from, options, historyState) => {
117
+ const moveToLocation = (to, from, options, pageTitleForBrowserHistory, historyState) => {
118
118
  const intraPage = samePage(from, to);
119
+ const targetPageTitle = document.title;
120
+ document.title = pageTitleForBrowserHistory;
119
121
  let scrolledToTop = false;
120
122
  if (to.href !== location.href && !historyState) {
121
123
  if (options.history === "replace") {
@@ -148,7 +150,9 @@ const moveToLocation = (to, from, options, historyState) => {
148
150
  } else {
149
151
  if (to.hash) {
150
152
  history.scrollRestoration = "auto";
153
+ const savedState = history.state;
151
154
  location.href = to.href;
155
+ history.state || replaceState(savedState, "");
152
156
  } else {
153
157
  if (!scrolledToTop) {
154
158
  scrollTo({ left: 0, top: 0, behavior: "instant" });
@@ -156,6 +160,7 @@ const moveToLocation = (to, from, options, historyState) => {
156
160
  }
157
161
  history.scrollRestoration = "manual";
158
162
  }
163
+ document.title = targetPageTitle;
159
164
  };
160
165
  function preloadStyleLinks(newDocument) {
161
166
  const links = [];
@@ -279,8 +284,9 @@ async function updateDOM(preparationEvent, options, historyState, fallback) {
279
284
  } else {
280
285
  throw new DOMException("Transition was skipped");
281
286
  }
287
+ const pageTitleForBrowserHistory = document.title;
282
288
  const swapEvent = await doSwap(preparationEvent, viewTransition, defaultSwap);
283
- moveToLocation(swapEvent.to, swapEvent.from, options, historyState);
289
+ moveToLocation(swapEvent.to, swapEvent.from, options, pageTitleForBrowserHistory, historyState);
284
290
  triggerEvent(TRANSITION_AFTER_SWAP);
285
291
  if (fallback === "animate" && !skipTransition) {
286
292
  animate("new").then(() => viewTransitionFinished());
@@ -296,7 +302,7 @@ async function transition(direction, from, to, options, historyState) {
296
302
  updateScrollPosition({ scrollX, scrollY });
297
303
  }
298
304
  if (samePage(from, to) && !!to.hash) {
299
- moveToLocation(to, from, options, historyState);
305
+ moveToLocation(to, from, options, document.title, historyState);
300
306
  return;
301
307
  }
302
308
  const prepEvent = await doPreparation(
@@ -3,6 +3,14 @@ const resolvedVirtualModuleId = "\0" + VIRTUAL_MODULE_ID;
3
3
  function astroDevOverlay({ settings }) {
4
4
  return {
5
5
  name: "astro:dev-overlay",
6
+ config() {
7
+ return {
8
+ optimizeDeps: {
9
+ // Optimize CJS dependencies used by the dev toolbar
10
+ include: ["astro > aria-query", "astro > axobject-query"]
11
+ }
12
+ };
13
+ },
6
14
  resolveId(id) {
7
15
  if (id === VIRTUAL_MODULE_ID) {
8
16
  return resolvedVirtualModuleId;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "4.0.9",
3
+ "version": "4.1.0",
4
4
  "description": "Astro is a modern site builder with web best practices, performance, and DX front-of-mind.",
5
5
  "type": "module",
6
6
  "author": "withastro",
@@ -110,6 +110,8 @@
110
110
  "@babel/types": "^7.23.3",
111
111
  "@types/babel__core": "^7.20.4",
112
112
  "acorn": "^8.11.2",
113
+ "aria-query": "^5.3.0",
114
+ "axobject-query": "^4.0.0",
113
115
  "boxen": "^7.1.1",
114
116
  "chokidar": "^3.5.3",
115
117
  "ci-info": "^4.0.0",
@@ -169,6 +171,7 @@
169
171
  "devDependencies": {
170
172
  "@astrojs/check": "^0.3.1",
171
173
  "@playwright/test": "1.40.0",
174
+ "@types/aria-query": "^5.0.4",
172
175
  "@types/babel__generator": "^7.6.7",
173
176
  "@types/babel__traverse": "^7.20.4",
174
177
  "@types/chai": "^4.3.10",