astro 4.0.6 → 4.0.8

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 (39) hide show
  1. package/README.md +1 -1
  2. package/astro-jsx.d.ts +1 -0
  3. package/components/ViewTransitions.astro +1 -1
  4. package/content-types.template.d.ts +2 -2
  5. package/dist/@types/astro.d.ts +5 -3
  6. package/dist/assets/build/generate.js +21 -8
  7. package/dist/assets/endpoint/generic.js +1 -0
  8. package/dist/assets/endpoint/node.js +1 -0
  9. package/dist/cli/add/index.js +1 -1
  10. package/dist/content/vite-plugin-content-assets.js +1 -5
  11. package/dist/content/vite-plugin-content-virtual-mod.js +6 -1
  12. package/dist/core/build/generate.js +5 -4
  13. package/dist/core/build/plugins/plugin-manifest.js +2 -1
  14. package/dist/core/build/static-build.js +5 -2
  15. package/dist/core/constants.js +1 -1
  16. package/dist/core/dev/dev.js +1 -1
  17. package/dist/core/errors/errors-data.d.ts +35 -1
  18. package/dist/core/errors/errors-data.js +19 -2
  19. package/dist/core/errors/errors.d.ts +3 -3
  20. package/dist/core/errors/errors.js +6 -6
  21. package/dist/core/logger/core.d.ts +8 -7
  22. package/dist/core/logger/core.js +15 -14
  23. package/dist/core/logger/node.js +4 -3
  24. package/dist/core/messages.js +2 -2
  25. package/dist/core/middleware/loadMiddleware.d.ts +1 -1
  26. package/dist/core/middleware/loadMiddleware.js +6 -4
  27. package/dist/core/redirects/helpers.js +7 -1
  28. package/dist/core/routing/params.js +2 -2
  29. package/dist/runtime/client/dev-overlay/plugins/audit/a11y.js +14 -8
  30. package/dist/runtime/client/dev-overlay/plugins/utils/highlight.js +3 -0
  31. package/dist/runtime/server/endpoint.js +15 -27
  32. package/dist/transitions/router.js +4 -2
  33. package/dist/vite-plugin-astro-server/css.d.ts +1 -2
  34. package/dist/vite-plugin-astro-server/css.js +21 -15
  35. package/dist/vite-plugin-astro-server/request.js +1 -1
  36. package/dist/vite-plugin-astro-server/route.js +1 -5
  37. package/dist/vite-plugin-dev-overlay/vite-plugin-dev-overlay.js +1 -1
  38. package/jsx-runtime.d.ts +6 -0
  39. package/package.json +9 -7
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <br/>
2
2
  <p align="center">
3
- <img src="../../.github/assets/banner.png" alt="Build the web you want">
3
+ <img src="../../.github/assets/banner.jpg" alt="Build the web you want">
4
4
  <br/><br/>
5
5
  <a href="https://astro.build">Astro</a> is the all-in-one web framework designed for speed.
6
6
  <br/>
package/astro-jsx.d.ts CHANGED
@@ -627,6 +627,7 @@ declare namespace astroHTML.JSX {
627
627
  }
628
628
 
629
629
  interface ButtonHTMLAttributes extends HTMLAttributes {
630
+ autocomplete?: string | undefined | null;
630
631
  disabled?: boolean | string | undefined | null;
631
632
  form?: string | undefined | null;
632
633
  formaction?: string | undefined | null;
@@ -94,7 +94,7 @@ const { fallback = 'animate' } = Astro.props;
94
94
 
95
95
  document.addEventListener('submit', (ev) => {
96
96
  let el = ev.target as HTMLElement;
97
- if (el.tagName !== 'FORM' || isReloadEl(el)) {
97
+ if (el.tagName !== 'FORM' || ev.defaultPrevented || isReloadEl(el)) {
98
98
  return;
99
99
  }
100
100
  const form = el as HTMLFormElement;
@@ -155,11 +155,11 @@ declare module 'astro:content' {
155
155
  ? {
156
156
  collection: C;
157
157
  slug: ValidContentEntrySlug<C>;
158
- }
158
+ }
159
159
  : {
160
160
  collection: C;
161
161
  id: keyof DataEntryMap[C];
162
- }
162
+ }
163
163
  >;
164
164
  // Allow generic `string` to avoid excessive type errors in the config
165
165
  // if `dev` is not running to update as you edit.
@@ -589,7 +589,7 @@ export interface AstroUserConfig {
589
589
  * [See our Server-side Rendering guide](https://docs.astro.build/en/guides/server-side-rendering/) for more on SSR, and [our deployment guides](https://docs.astro.build/en/guides/deploy/) for a complete list of hosts.
590
590
  *
591
591
  * ```js
592
- * import netlify from '@astrojs/netlify/functions';
592
+ * import netlify from '@astrojs/netlify';
593
593
  * {
594
594
  * // Example: Build for Netlify serverless deployment
595
595
  * adapter: netlify(),
@@ -632,7 +632,7 @@ export interface AstroUserConfig {
632
632
  * @typeraw {('file' | 'directory')}
633
633
  * @default `'directory'`
634
634
  * @description
635
- * Control the output file format of each page.
635
+ * Control the output file format of each page. This value may be set by an adapter for you.
636
636
  * - If `'file'`, Astro will generate an HTML file (ex: "/foo.html") for each page.
637
637
  * - If `'directory'`, Astro will generate a directory with a nested `index.html` file (ex: "/foo/index.html") for each page.
638
638
  *
@@ -645,6 +645,8 @@ export interface AstroUserConfig {
645
645
  * }
646
646
  * ```
647
647
  *
648
+ *
649
+ *
648
650
  * #### Effect on Astro.url
649
651
  * Setting `build.format` controls what `Astro.url` is set to during the build. When it is:
650
652
  * - `directory` - The `Astro.url.pathname` will include a trailing slash to mimic folder behavior; ie `/foo/`.
@@ -2000,7 +2002,7 @@ export interface APIContext<Props extends Record<string, any> = Record<string, a
2000
2002
  }
2001
2003
  export type APIRoute<Props extends Record<string, any> = Record<string, any>> = (context: APIContext<Props>) => Response | Promise<Response>;
2002
2004
  export interface EndpointHandler {
2003
- [method: string]: APIRoute | ((params: Params, request: Request) => Response);
2005
+ [method: string]: APIRoute;
2004
2006
  }
2005
2007
  export type Props = Record<string, unknown>;
2006
2008
  export interface AstroRenderer {
@@ -3,6 +3,8 @@ import fs, { readFileSync } from "node:fs";
3
3
  import { basename, join } from "node:path/posix";
4
4
  import { getOutDirWithinCwd } from "../../core/build/common.js";
5
5
  import { getTimeStat } from "../../core/build/util.js";
6
+ import { AstroError } from "../../core/errors/errors.js";
7
+ import { AstroErrorData } from "../../core/errors/index.js";
6
8
  import { isRemotePath, prependForwardSlash } from "../../core/path.js";
7
9
  import { isServerLikeOutput } from "../../prerender/utils.js";
8
10
  import { getConfiguredImageService } from "../internal.js";
@@ -52,9 +54,9 @@ function getFullImagePath(originalFilePath, env) {
52
54
  async function generateImagesForPath(originalFilePath, transformsAndPath, env, queue) {
53
55
  const originalImageData = await loadImage(originalFilePath, env);
54
56
  for (const [_, transform] of transformsAndPath.transforms) {
55
- queue.add(
56
- async () => generateImage(originalImageData, transform.finalPath, transform.transform)
57
- );
57
+ await queue.add(async () => generateImage(originalImageData, transform.finalPath, transform.transform)).catch((e) => {
58
+ throw e;
59
+ });
58
60
  }
59
61
  if (!env.isSSR && !isRemotePath(originalFilePath) && !globalThis.astroAsset.referencedImages?.has(transformsAndPath.originalSrcPath)) {
60
62
  try {
@@ -117,11 +119,22 @@ async function generateImagesForPath(originalFilePath, transformsAndPath, env, q
117
119
  expires: originalImage.expires
118
120
  };
119
121
  const imageService = await getConfiguredImageService();
120
- resultData.data = (await imageService.transform(
121
- originalImage.data,
122
- { ...options, src: originalImagePath },
123
- env.imageConfig
124
- )).data;
122
+ try {
123
+ resultData.data = (await imageService.transform(
124
+ originalImage.data,
125
+ { ...options, src: originalImagePath },
126
+ env.imageConfig
127
+ )).data;
128
+ } catch (e) {
129
+ const error = new AstroError(
130
+ {
131
+ ...AstroErrorData.CouldNotTransformImage,
132
+ message: AstroErrorData.CouldNotTransformImage.message(originalFilePath)
133
+ },
134
+ { cause: e }
135
+ );
136
+ throw error;
137
+ }
125
138
  try {
126
139
  if (env.useCache) {
127
140
  if (isLocalImage) {
@@ -50,6 +50,7 @@ const GET = async ({ request }) => {
50
50
  }
51
51
  });
52
52
  } catch (err) {
53
+ console.error("Could not process image request:", err);
53
54
  return new Response(`Server Error: ${err}`, { status: 500 });
54
55
  }
55
56
  };
@@ -65,6 +65,7 @@ const GET = async ({ request }) => {
65
65
  }
66
66
  });
67
67
  } catch (err) {
68
+ console.error("Could not process image request:", err);
68
69
  return new Response(`Server Error: ${err}`, { status: 500 });
69
70
  }
70
71
  };
@@ -56,7 +56,7 @@ const LIT_NPMRC_STUB = `# Lit libraries are required to be hoisted due to depend
56
56
  public-hoist-pattern[]=*lit*
57
57
  `;
58
58
  const OFFICIAL_ADAPTER_TO_IMPORT_MAP = {
59
- netlify: "@astrojs/netlify/functions",
59
+ netlify: "@astrojs/netlify",
60
60
  vercel: "@astrojs/vercel/serverless",
61
61
  cloudflare: "@astrojs/cloudflare",
62
62
  node: "@astrojs/node"
@@ -49,11 +49,7 @@ function astroContentAssetPropagationPlugin({
49
49
  if (!devModuleLoader.getModuleById(basePath)?.ssrModule) {
50
50
  await devModuleLoader.import(basePath);
51
51
  }
52
- const { styles, urls } = await getStylesForURL(
53
- pathToFileURL(basePath),
54
- devModuleLoader,
55
- "development"
56
- );
52
+ const { styles, urls } = await getStylesForURL(pathToFileURL(basePath), devModuleLoader);
57
53
  const hoistedScripts = await getScriptsForURL(
58
54
  pathToFileURL(basePath),
59
55
  settings.config.root,
@@ -209,7 +209,12 @@ async function generateLookupMap({
209
209
  if (lookupMap[collection]?.entries?.[slug]) {
210
210
  throw new AstroError({
211
211
  ...AstroErrorData.DuplicateContentEntrySlugError,
212
- message: AstroErrorData.DuplicateContentEntrySlugError.message(collection, slug),
212
+ message: AstroErrorData.DuplicateContentEntrySlugError.message(
213
+ collection,
214
+ slug,
215
+ lookupMap[collection].entries[slug],
216
+ rootRelativePath(root, filePath)
217
+ ),
213
218
  hint: slug !== generatedSlug ? `Check the \`slug\` frontmatter property in **${id}**.` : void 0
214
219
  });
215
220
  }
@@ -168,7 +168,7 @@ ${bgGreen(black(` ${verb} static routes `))}`);
168
168
  const totalCount = Array.from(staticImageList.values()).map((x) => x.transforms.size).reduce((a, b) => a + b, 0);
169
169
  const cpuCount = os.cpus().length;
170
170
  const assetsCreationEnvironment = await prepareAssetsGenerationEnv(pipeline, totalCount);
171
- const queue = new PQueue({ concurrency: cpuCount });
171
+ const queue = new PQueue({ concurrency: Math.max(cpuCount, 1) });
172
172
  const assetsTimer = performance.now();
173
173
  for (const [originalPath, transforms] of staticImageList) {
174
174
  await generateImagesForPath(originalPath, transforms, assetsCreationEnvironment, queue);
@@ -230,13 +230,14 @@ async function generatePage(pageData, ssrEntry, builtPaths, pipeline) {
230
230
  for (let i = 0; i < paths.length; i++) {
231
231
  const path = paths[i];
232
232
  pipeline.getEnvironment().logger.debug("build", `Generating: ${path}`);
233
+ const filePath = getOutputFilename(pipeline.getConfig(), path, pageData.route.type);
234
+ const lineIcon = i === paths.length - 1 ? "\u2514\u2500" : "\u251C\u2500";
235
+ logger.info(null, ` ${blue(lineIcon)} ${dim(filePath)}`, false);
233
236
  await generatePath(path, pipeline, generationOptions, route);
234
237
  const timeEnd = performance.now();
235
238
  const timeChange = getTimeStat(prevTimeEnd, timeEnd);
236
239
  const timeIncrease = `(+${timeChange})`;
237
- const filePath = getOutputFilename(pipeline.getConfig(), path, pageData.route.type);
238
- const lineIcon = i === paths.length - 1 ? "\u2514\u2500" : "\u251C\u2500";
239
- logger.info(null, ` ${blue(lineIcon)} ${dim(filePath)} ${dim(timeIncrease)}`);
240
+ logger.info("SKIP_FORMAT", ` ${dim(timeIncrease)}`);
240
241
  prevTimeEnd = timeEnd;
241
242
  }
242
243
  }
@@ -148,8 +148,9 @@ function buildManifest(opts, internals, staticFiles) {
148
148
  continue;
149
149
  const scripts = [];
150
150
  if (pageData.hoistedScript) {
151
+ const shouldPrefixAssetPath = pageData.hoistedScript.type === "external";
151
152
  const hoistedValue = pageData.hoistedScript.value;
152
- const value = hoistedValue.endsWith(".js") ? prefixAssetPath(hoistedValue) : hoistedValue;
153
+ const value = shouldPrefixAssetPath ? prefixAssetPath(hoistedValue) : hoistedValue;
153
154
  scripts.unshift(
154
155
  Object.assign({}, pageData.hoistedScript, {
155
156
  value
@@ -94,7 +94,7 @@ async function staticBuild(opts, internals, ssrOutputChunkNames) {
94
94
  case settings.config.output === "static": {
95
95
  settings.timer.start("Static generate");
96
96
  await generatePages(opts, internals);
97
- await cleanServerOutput(opts, ssrOutputChunkNames);
97
+ await cleanServerOutput(opts, ssrOutputChunkNames, internals);
98
98
  settings.timer.end("Static generate");
99
99
  return;
100
100
  }
@@ -314,9 +314,12 @@ export const ${e.n} = noop;`;
314
314
  removeEmptyDirs(out);
315
315
  }
316
316
  }
317
- async function cleanServerOutput(opts, ssrOutputChunkNames) {
317
+ async function cleanServerOutput(opts, ssrOutputChunkNames, internals) {
318
318
  const out = getOutDirWithinCwd(opts.settings.config.outDir);
319
319
  const files = ssrOutputChunkNames.filter((f) => f.endsWith(".mjs"));
320
+ if (internals.manifestFileName) {
321
+ files.push(internals.manifestFileName);
322
+ }
320
323
  if (files.length) {
321
324
  await Promise.all(
322
325
  files.map(async (filename) => {
@@ -1,4 +1,4 @@
1
- const ASTRO_VERSION = "4.0.6";
1
+ const ASTRO_VERSION = "4.0.8";
2
2
  const SUPPORTED_MARKDOWN_FILE_EXTENSIONS = [
3
3
  ".markdown",
4
4
  ".mdown",
@@ -21,7 +21,7 @@ async function dev(inlineConfig) {
21
21
  base: restart.container.settings.config.base
22
22
  })
23
23
  );
24
- const currentVersion = "4.0.6";
24
+ const currentVersion = "4.0.8";
25
25
  if (currentVersion.includes("-")) {
26
26
  logger.warn("SKIP_FORMAT", msg.prerelease({ currentVersion }));
27
27
  }
@@ -622,6 +622,21 @@ export declare const MarkdownImageNotFound: {
622
622
  message: (imagePath: string, fullImagePath: string | undefined) => string;
623
623
  hint: string;
624
624
  };
625
+ /**
626
+ * @docs
627
+ * @see
628
+ * - [Images](https://docs.astro.build/en/guides/images/)
629
+ * @description
630
+ * Astro could not transform one of your images. Often, this is caused by a corrupted or malformed image. Re-exporting the image from your image editor may fix this issue.
631
+ *
632
+ * Depending on the image service you are using, the stack trace may contain more information on the specific error encountered.
633
+ */
634
+ export declare const CouldNotTransformImage: {
635
+ name: string;
636
+ title: string;
637
+ message: (imagePath: string) => string;
638
+ hint: string;
639
+ };
625
640
  /**
626
641
  * @docs
627
642
  * @description
@@ -690,6 +705,25 @@ export declare const LocalsNotAnObject: {
690
705
  message: string;
691
706
  hint: string;
692
707
  };
708
+ /**
709
+ * @docs
710
+ * @description
711
+ * Thrown in development mode when middleware throws an error while attempting to loading it.
712
+ *
713
+ * For example:
714
+ * ```ts
715
+ * import {defineMiddleware} from "astro:middleware";
716
+ * throw new Error("Error thrown while loading the middleware.")
717
+ * export const onRequest = defineMiddleware(() => {
718
+ * return "string"
719
+ * });
720
+ * ```
721
+ */
722
+ export declare const MiddlewareCantBeLoaded: {
723
+ name: string;
724
+ title: string;
725
+ message: string;
726
+ };
693
727
  /**
694
728
  * @docs
695
729
  * @see
@@ -1125,7 +1159,7 @@ export declare const DataCollectionEntryParseError: {
1125
1159
  export declare const DuplicateContentEntrySlugError: {
1126
1160
  name: string;
1127
1161
  title: string;
1128
- message: (collection: string, slug: string) => string;
1162
+ message: (collection: string, slug: string, preExisting: string, alsoFound: string) => string;
1129
1163
  };
1130
1164
  /**
1131
1165
  * @docs
@@ -223,6 +223,12 @@ const MarkdownImageNotFound = {
223
223
  message: (imagePath, fullImagePath) => `Could not find requested image \`${imagePath}\`${fullImagePath ? ` at \`${fullImagePath}\`.` : "."}`,
224
224
  hint: "This is often caused by a typo in the image path. Please make sure the file exists, and is spelled correctly."
225
225
  };
226
+ const CouldNotTransformImage = {
227
+ name: "CouldNotTransformImage",
228
+ title: "Could not transform image.",
229
+ message: (imagePath) => `Could not transform image \`${imagePath}\`. See the stack trace for more information.`,
230
+ hint: "This is often caused by a corrupted or malformed image. Re-exporting the image from your image editor may fix this issue."
231
+ };
226
232
  const ResponseSentError = {
227
233
  name: "ResponseSentError",
228
234
  title: "Unable to set response.",
@@ -244,6 +250,11 @@ const LocalsNotAnObject = {
244
250
  message: "`locals` can only be assigned to an object. Other values like numbers, strings, etc. are not accepted.",
245
251
  hint: "If you tried to remove some information from the `locals` object, try to use `delete` or set the property to `undefined`."
246
252
  };
253
+ const MiddlewareCantBeLoaded = {
254
+ name: "MiddlewareCantBeLoaded",
255
+ title: "Can't load the middleware.",
256
+ message: "The middleware threw an error while Astro was trying to loading it."
257
+ };
247
258
  const LocalImageUsedWrongly = {
248
259
  name: "LocalImageUsedWrongly",
249
260
  title: "Local images must be imported.",
@@ -415,8 +426,12 @@ const DataCollectionEntryParseError = {
415
426
  const DuplicateContentEntrySlugError = {
416
427
  name: "DuplicateContentEntrySlugError",
417
428
  title: "Duplicate content entry slug.",
418
- message: (collection, slug) => {
419
- return `**${collection}** contains multiple entries with the same slug: \`${slug}\`. Slugs must be unique.`;
429
+ message: (collection, slug, preExisting, alsoFound) => {
430
+ return `**${collection}** contains multiple entries with the same slug: \`${slug}\`. Slugs must be unique.
431
+
432
+ Entries:
433
+ - ${preExisting}
434
+ - ${alsoFound}`;
420
435
  }
421
436
  };
422
437
  const UnsupportedConfigTransformError = {
@@ -460,6 +475,7 @@ export {
460
475
  ConfigNotFound,
461
476
  ContentCollectionTypeMismatchError,
462
477
  ContentSchemaContainsSlugError,
478
+ CouldNotTransformImage,
463
479
  DataCollectionEntryParseError,
464
480
  DuplicateContentEntrySlugError,
465
481
  ExpectedImage,
@@ -490,6 +506,7 @@ export {
490
506
  MarkdownFrontmatterParseError,
491
507
  MarkdownImageNotFound,
492
508
  MdxIntegrationMissingError,
509
+ MiddlewareCantBeLoaded,
493
510
  MiddlewareNoDataOrNextCalled,
494
511
  MiddlewareNotAResponse,
495
512
  MissingImageDimension,
@@ -21,7 +21,7 @@ export declare class AstroError extends Error {
21
21
  hint: string | undefined;
22
22
  frame: string | undefined;
23
23
  type: ErrorTypes;
24
- constructor(props: ErrorProperties, ...params: any);
24
+ constructor(props: ErrorProperties, options?: ErrorOptions);
25
25
  setLocation(location: ErrorLocation): void;
26
26
  setName(name: string): void;
27
27
  setMessage(message: string): void;
@@ -31,7 +31,7 @@ export declare class AstroError extends Error {
31
31
  }
32
32
  export declare class CompilerError extends AstroError {
33
33
  type: ErrorTypes;
34
- constructor(props: ErrorProperties, ...params: any);
34
+ constructor(props: ErrorProperties, options?: ErrorOptions);
35
35
  static is(err: unknown): err is CompilerError;
36
36
  }
37
37
  export declare class CSSError extends AstroError {
@@ -51,7 +51,7 @@ export declare class AggregateError extends AstroError {
51
51
  errors: AstroError[];
52
52
  constructor(props: ErrorProperties & {
53
53
  errors: AstroError[];
54
- }, ...params: any);
54
+ }, options?: ErrorOptions);
55
55
  static is(err: unknown): err is AggregateError;
56
56
  }
57
57
  /**
@@ -8,9 +8,9 @@ class AstroError extends Error {
8
8
  hint;
9
9
  frame;
10
10
  type = "AstroError";
11
- constructor(props, ...params) {
12
- super(...params);
11
+ constructor(props, options) {
13
12
  const { name, title, message, stack, location, hint, frame } = props;
13
+ super(message, options);
14
14
  this.title = title;
15
15
  this.name = name;
16
16
  if (message)
@@ -41,8 +41,8 @@ class AstroError extends Error {
41
41
  }
42
42
  class CompilerError extends AstroError {
43
43
  type = "CompilerError";
44
- constructor(props, ...params) {
45
- super(props, ...params);
44
+ constructor(props, options) {
45
+ super(props, options);
46
46
  }
47
47
  static is(err) {
48
48
  return err.type === "CompilerError";
@@ -71,8 +71,8 @@ class AggregateError extends AstroError {
71
71
  errors;
72
72
  // Despite being a collection of errors, AggregateError still needs to have a main error attached to it
73
73
  // This is because Vite expects every thrown errors handled during HMR to be, well, Error and have a message
74
- constructor(props, ...params) {
75
- super(props, ...params);
74
+ constructor(props, options) {
75
+ super(props, options);
76
76
  this.errors = props.errors;
77
77
  }
78
78
  static is(err) {
@@ -17,17 +17,18 @@ export interface LogMessage {
17
17
  label: string | null;
18
18
  level: LoggerLevel;
19
19
  message: string;
20
+ newLine: boolean;
20
21
  }
21
22
  export declare const levels: Record<LoggerLevel, number>;
22
23
  /** Full logging API */
23
- export declare function log(opts: LogOptions, level: LoggerLevel, label: string | null, message: string): void;
24
+ export declare function log(opts: LogOptions, level: LoggerLevel, label: string | null, message: string, newLine?: boolean): void;
24
25
  export declare function isLogLevelEnabled(configuredLogLevel: LoggerLevel, level: LoggerLevel): boolean;
25
26
  /** Emit a user-facing message. Useful for UI and other console messages. */
26
- export declare function info(opts: LogOptions, label: string | null, message: string): void;
27
+ export declare function info(opts: LogOptions, label: string | null, message: string, newLine?: boolean): void;
27
28
  /** Emit a warning message. Useful for high-priority messages that aren't necessarily errors. */
28
- export declare function warn(opts: LogOptions, label: string | null, message: string): void;
29
+ export declare function warn(opts: LogOptions, label: string | null, message: string, newLine?: boolean): void;
29
30
  /** Emit a error message, Useful when Astro can't recover from some error. */
30
- export declare function error(opts: LogOptions, label: string | null, message: string): void;
31
+ export declare function error(opts: LogOptions, label: string | null, message: string, newLine?: boolean): void;
31
32
  type LogFn = typeof info | typeof warn | typeof error;
32
33
  export declare function table(opts: LogOptions, columns: number[]): (logFn: LogFn, ...input: Array<any>) => void;
33
34
  export declare function debug(...args: any[]): void;
@@ -43,9 +44,9 @@ export declare function timerMessage(message: string, startTime?: number): strin
43
44
  export declare class Logger {
44
45
  options: LogOptions;
45
46
  constructor(options: LogOptions);
46
- info(label: LoggerLabel | null, message: string): void;
47
- warn(label: LoggerLabel | null, message: string): void;
48
- error(label: LoggerLabel | null, message: string): void;
47
+ info(label: LoggerLabel | null, message: string, newLine?: boolean): void;
48
+ warn(label: LoggerLabel | null, message: string, newLine?: boolean): void;
49
+ error(label: LoggerLabel | null, message: string, newLine?: boolean): void;
49
50
  debug(label: LoggerLabel, ...messages: any[]): void;
50
51
  level(): LoggerLevel;
51
52
  forkIntegrationLogger(label: string): AstroIntegrationLogger;
@@ -13,13 +13,14 @@ const levels = {
13
13
  error: 50,
14
14
  silent: 90
15
15
  };
16
- function log(opts, level, label, message) {
16
+ function log(opts, level, label, message, newLine = true) {
17
17
  const logLevel = opts.level;
18
18
  const dest = opts.dest;
19
19
  const event = {
20
20
  label,
21
21
  level,
22
- message
22
+ message,
23
+ newLine
23
24
  };
24
25
  if (!isLogLevelEnabled(logLevel, level)) {
25
26
  return;
@@ -29,14 +30,14 @@ function log(opts, level, label, message) {
29
30
  function isLogLevelEnabled(configuredLogLevel, level) {
30
31
  return levels[configuredLogLevel] <= levels[level];
31
32
  }
32
- function info(opts, label, message) {
33
- return log(opts, "info", label, message);
33
+ function info(opts, label, message, newLine = true) {
34
+ return log(opts, "info", label, message, newLine);
34
35
  }
35
- function warn(opts, label, message) {
36
- return log(opts, "warn", label, message);
36
+ function warn(opts, label, message, newLine = true) {
37
+ return log(opts, "warn", label, message, newLine);
37
38
  }
38
- function error(opts, label, message) {
39
- return log(opts, "error", label, message);
39
+ function error(opts, label, message, newLine = true) {
40
+ return log(opts, "error", label, message, newLine);
40
41
  }
41
42
  function table(opts, columns) {
42
43
  return function logTable(logFn, ...input) {
@@ -107,14 +108,14 @@ class Logger {
107
108
  constructor(options) {
108
109
  this.options = options;
109
110
  }
110
- info(label, message) {
111
- info(this.options, label, message);
111
+ info(label, message, newLine = true) {
112
+ info(this.options, label, message, newLine);
112
113
  }
113
- warn(label, message) {
114
- warn(this.options, label, message);
114
+ warn(label, message, newLine = true) {
115
+ warn(this.options, label, message, newLine);
115
116
  }
116
- error(label, message) {
117
- error(this.options, label, message);
117
+ error(label, message, newLine = true) {
118
+ error(this.options, label, message, newLine);
118
119
  }
119
120
  debug(label, ...messages) {
120
121
  debug(label, ...messages);
@@ -1,15 +1,16 @@
1
1
  import debugPackage from "debug";
2
2
  import { getEventPrefix, levels } from "./core.js";
3
3
  const nodeLogDestination = {
4
- write(event) {
4
+ write(event, newLine = true) {
5
5
  let dest = process.stderr;
6
6
  if (levels[event.level] < levels["error"]) {
7
7
  dest = process.stdout;
8
8
  }
9
+ let trailingLine = event.newLine ? "\n" : "";
9
10
  if (event.label === "SKIP_FORMAT") {
10
- dest.write(event.message + "\n");
11
+ dest.write(event.message + trailingLine);
11
12
  } else {
12
- dest.write(getEventPrefix(event) + " " + event.message + "\n");
13
+ dest.write(getEventPrefix(event) + " " + event.message + trailingLine);
13
14
  }
14
15
  return true;
15
16
  }
@@ -36,7 +36,7 @@ function serverStart({
36
36
  host,
37
37
  base
38
38
  }) {
39
- const version = "4.0.6";
39
+ const version = "4.0.8";
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.6"}`
261
+ `v${"4.0.8"}`
262
262
  )} ${headline}`
263
263
  );
264
264
  }
@@ -4,4 +4,4 @@ import type { ModuleLoader } from '../module-loader/index.js';
4
4
  *
5
5
  * If not middlewares were not set, the function returns an empty array.
6
6
  */
7
- export declare function loadMiddleware(moduleLoader: ModuleLoader): Promise<Record<string, any> | undefined>;
7
+ export declare function loadMiddleware(moduleLoader: ModuleLoader): Promise<Record<string, any>>;
@@ -1,10 +1,12 @@
1
1
  import { MIDDLEWARE_MODULE_ID } from "./vite-plugin.js";
2
+ import { MiddlewareCantBeLoaded } from "../errors/errors-data.js";
3
+ import { AstroError } from "../errors/index.js";
2
4
  async function loadMiddleware(moduleLoader) {
3
5
  try {
4
- const module = await moduleLoader.import(MIDDLEWARE_MODULE_ID);
5
- return module;
6
- } catch {
7
- return void 0;
6
+ return await moduleLoader.import(MIDDLEWARE_MODULE_ID);
7
+ } catch (error) {
8
+ const astroError = new AstroError(MiddlewareCantBeLoaded, { cause: error });
9
+ throw astroError;
8
10
  }
9
11
  }
10
12
  export {
@@ -10,7 +10,13 @@ function redirectRouteGenerate(redirectRoute, data) {
10
10
  if (typeof routeData !== "undefined") {
11
11
  return routeData?.generate(data) || routeData?.pathname || "/";
12
12
  } else if (typeof route === "string") {
13
- return route;
13
+ let target = route;
14
+ for (const param of Object.keys(data)) {
15
+ const paramValue = data[param];
16
+ target = target.replace(`[${param}]`, paramValue);
17
+ target = target.replace(`[...${param}]`, paramValue);
18
+ }
19
+ return target;
14
20
  } else if (typeof route === "undefined") {
15
21
  return "/";
16
22
  }
@@ -5,9 +5,9 @@ function getParams(array) {
5
5
  const params = {};
6
6
  array.forEach((key, i) => {
7
7
  if (key.startsWith("...")) {
8
- params[key.slice(3)] = match[i + 1] ? decodeURIComponent(match[i + 1]) : void 0;
8
+ params[key.slice(3)] = match[i + 1] ? match[i + 1] : void 0;
9
9
  } else {
10
- params[key] = decodeURIComponent(match[i + 1]);
10
+ params[key] = match[i + 1];
11
11
  }
12
12
  });
13
13
  return params;
@@ -33,6 +33,7 @@ const a11y_required_attributes = {
33
33
  object: ["title", "aria-label", "aria-labelledby"]
34
34
  };
35
35
  const interactiveElements = ["button", "details", "embed", "iframe", "label", "select", "textarea"];
36
+ const labellableElements = ["input", "meter", "output", "progress", "select", "textarea"];
36
37
  const aria_non_interactive_roles = [
37
38
  "alert",
38
39
  "alertdialog",
@@ -204,7 +205,7 @@ const a11y = [
204
205
  {
205
206
  code: "a11y-aria-activedescendant-has-tabindex",
206
207
  title: "Elements with attribute `aria-activedescendant` must be tabbable",
207
- message: "This element must either have an inherent `tabindex` or declare `tabindex` as an attribute.",
208
+ message: "Element with the `aria-activedescendant` attribute must either have an inherent `tabindex` or declare `tabindex` as an attribute.",
208
209
  selector: "[aria-activedescendant]",
209
210
  match(element) {
210
211
  if (!element.tabIndex && !element.hasAttribute("tabindex"))
@@ -268,13 +269,17 @@ const a11y = [
268
269
  selector: 'a[href]:is([href=""], [href="#"], [href^="javascript:" i])'
269
270
  },
270
271
  {
271
- code: "a11y-label-has-associated-control",
272
- title: "`label` tag should have an associated control and a text content.",
273
- message: "The `label` tag must be associated with a control using either `for` or having a nested input. Additionally, the `label` tag must have text content.",
274
- selector: "label:not([for])",
272
+ code: "a11y-invalid-label",
273
+ title: "`label` element should have an associated control and a text content.",
274
+ message: "The `label` element must be associated with a control either by using the `for` attribute or by containing a nested form element. Additionally, the `label` element must have text content.",
275
+ selector: "label",
275
276
  match(element) {
276
- const inputChild = element.querySelector("input");
277
- if (!inputChild?.textContent)
277
+ const hasFor = element.hasAttribute("for");
278
+ const nestedLabellableElement = element.querySelector(`${labellableElements.join(", ")}`);
279
+ if (!hasFor && !nestedLabellableElement)
280
+ return true;
281
+ const innerText = element.innerText.trim();
282
+ if (innerText === "")
278
283
  return true;
279
284
  }
280
285
  },
@@ -327,7 +332,8 @@ const a11y = [
327
332
  message: "Headings and anchors must have content to be accessible.",
328
333
  selector: a11y_required_content.join(","),
329
334
  match(element) {
330
- if (!element.textContent)
335
+ const innerText = element.innerText.trim();
336
+ if (innerText === "")
331
337
  return true;
332
338
  }
333
339
  },
@@ -44,6 +44,9 @@ function attachTooltipToHighlight(highlight, tooltip, originalElement) {
44
44
  } else {
45
45
  tooltip.style.top = `-${tooltip.offsetHeight}px`;
46
46
  }
47
+ if (dialogRect.right > document.documentElement.clientWidth) {
48
+ tooltip.style.right = "0px";
49
+ }
47
50
  });
48
51
  });
49
52
  ["mouseout", "blur"].forEach((event) => {
@@ -1,44 +1,32 @@
1
1
  import { bold } from "kleur/colors";
2
- function getHandlerFromModule(mod, method) {
3
- if (mod[method]) {
4
- return mod[method];
5
- }
6
- if (mod["ALL"]) {
7
- return mod["ALL"];
8
- }
9
- return void 0;
10
- }
11
2
  async function renderEndpoint(mod, context, ssr, logger) {
12
3
  const { request, url } = context;
13
- const chosenMethod = request.method?.toUpperCase();
14
- const handler = getHandlerFromModule(mod, chosenMethod);
15
- if (!ssr && ssr === false && chosenMethod && chosenMethod !== "GET") {
4
+ const method = request.method.toUpperCase();
5
+ const handler = mod[method] ?? mod["ALL"];
6
+ if (!ssr && ssr === false && method !== "GET") {
16
7
  logger.warn(
17
- null,
8
+ "router",
18
9
  `${url.pathname} ${bold(
19
- chosenMethod
10
+ method
20
11
  )} requests are not available for a static site. Update your config to \`output: 'server'\` or \`output: 'hybrid'\` to enable.`
21
12
  );
22
13
  }
23
- if (!handler || typeof handler !== "function") {
24
- let response = new Response(null, {
14
+ if (typeof handler !== "function") {
15
+ logger.warn(
16
+ "router",
17
+ `No API Route handler exists for the method "${method}" for the route ${url.pathname}.
18
+ Found handlers: ${Object.keys(mod).map((exp) => JSON.stringify(exp)).join(", ")}
19
+ ` + ("all" in mod ? `One of the exported handlers is "all" (lowercase), did you mean to export 'ALL'?
20
+ ` : "")
21
+ );
22
+ return new Response(null, {
25
23
  status: 404,
26
24
  headers: {
27
25
  "X-Astro-Response": "Not-Found"
28
26
  }
29
27
  });
30
- return response;
31
28
  }
32
- const proxy = new Proxy(context, {
33
- get(target, prop) {
34
- if (prop in target) {
35
- return Reflect.get(target, prop);
36
- } else {
37
- return void 0;
38
- }
39
- }
40
- });
41
- return handler.call(mod, proxy, request);
29
+ return handler.call(mod, context);
42
30
  }
43
31
  export {
44
32
  renderEndpoint
@@ -71,7 +71,8 @@ const throttle = (cb, delay) => {
71
71
  async function fetchHTML(href, init) {
72
72
  try {
73
73
  const res = await fetch(href, init);
74
- const mediaType = res.headers.get("content-type")?.replace(/;.*$/, "");
74
+ const contentType = res.headers.get("content-type") ?? "";
75
+ const mediaType = contentType.split(";", 1)[0].trim();
75
76
  if (mediaType !== "text/html" && mediaType !== "application/xhtml+xml") {
76
77
  return null;
77
78
  }
@@ -317,7 +318,8 @@ async function transition(direction, from, to, options, historyState) {
317
318
  const init = {};
318
319
  if (preparationEvent.formData) {
319
320
  init.method = "POST";
320
- init.body = preparationEvent.formData;
321
+ const form = preparationEvent.sourceElement instanceof HTMLFormElement ? preparationEvent.sourceElement : preparationEvent.sourceElement instanceof HTMLElement && "form" in preparationEvent.sourceElement ? preparationEvent.sourceElement.form : preparationEvent.sourceElement?.closest("form");
322
+ init.body = form?.attributes.getNamedItem("enctype")?.value === "application/x-www-form-urlencoded" ? new URLSearchParams(preparationEvent.formData) : preparationEvent.formData;
321
323
  }
322
324
  const response = await fetchHTML(href, init);
323
325
  if (response === null) {
@@ -1,4 +1,3 @@
1
- import type { RuntimeMode } from '../@types/astro.js';
2
1
  import type { ModuleLoader } from '../core/module-loader/index.js';
3
2
  interface ImportedStyle {
4
3
  id: string;
@@ -6,7 +5,7 @@ interface ImportedStyle {
6
5
  content: string;
7
6
  }
8
7
  /** Given a filePath URL, crawl Vite’s module graph to find all style imports. */
9
- export declare function getStylesForURL(filePath: URL, loader: ModuleLoader, mode: RuntimeMode): Promise<{
8
+ export declare function getStylesForURL(filePath: URL, loader: ModuleLoader): Promise<{
10
9
  urls: Set<string>;
11
10
  styles: ImportedStyle[];
12
11
  }>;
@@ -1,27 +1,33 @@
1
1
  import { viteID } from "../core/util.js";
2
2
  import { isBuildableCSSRequest } from "./util.js";
3
3
  import { crawlGraph } from "./vite.js";
4
- async function getStylesForURL(filePath, loader, mode) {
4
+ async function getStylesForURL(filePath, loader) {
5
5
  const importedCssUrls = /* @__PURE__ */ new Set();
6
6
  const importedStylesMap = /* @__PURE__ */ new Map();
7
7
  for await (const importedModule of crawlGraph(loader, viteID(filePath), true)) {
8
8
  if (isBuildableCSSRequest(importedModule.url)) {
9
- let ssrModule;
10
- try {
11
- ssrModule = importedModule.ssrModule ?? await loader.import(importedModule.url);
12
- } catch {
13
- continue;
14
- }
15
- if (mode === "development" && // only inline in development
16
- typeof ssrModule?.default === "string") {
17
- importedStylesMap.set(importedModule.url, {
18
- id: importedModule.id ?? importedModule.url,
19
- url: importedModule.url,
20
- content: ssrModule.default
21
- });
9
+ let css = "";
10
+ if (typeof importedModule.ssrModule?.default === "string") {
11
+ css = importedModule.ssrModule.default;
22
12
  } else {
23
- importedCssUrls.add(importedModule.url);
13
+ const url = new URL(importedModule.url, "http://localhost");
14
+ url.searchParams.set("inline", "");
15
+ const modId = `${decodeURI(url.pathname)}${url.search}`;
16
+ try {
17
+ const ssrModule = await loader.import(modId);
18
+ css = ssrModule.default;
19
+ } catch {
20
+ if (modId.includes(".module.")) {
21
+ importedCssUrls.add(importedModule.url);
22
+ }
23
+ continue;
24
+ }
24
25
  }
26
+ importedStylesMap.set(importedModule.url, {
27
+ id: importedModule.id ?? importedModule.url,
28
+ url: importedModule.url,
29
+ content: css
30
+ });
25
31
  }
26
32
  }
27
33
  return {
@@ -21,7 +21,7 @@ async function handleRequest({
21
21
  if (config.trailingSlash === "never" && !incomingRequest.url) {
22
22
  pathname = "";
23
23
  } else {
24
- pathname = decodeURI(url.pathname);
24
+ pathname = url.pathname;
25
25
  }
26
26
  url.pathname = removeTrailingForwardSlash(config.base) + url.pathname;
27
27
  if (!buildingToSSR && pathname !== "/_image") {
@@ -330,11 +330,7 @@ async function getScriptsAndStyles({ pipeline, filePath }) {
330
330
  });
331
331
  }
332
332
  }
333
- const { urls: styleUrls, styles: importedStyles } = await getStylesForURL(
334
- filePath,
335
- moduleLoader,
336
- mode
337
- );
333
+ const { urls: styleUrls, styles: importedStyles } = await getStylesForURL(filePath, moduleLoader);
338
334
  let links = /* @__PURE__ */ new Set();
339
335
  [...styleUrls].forEach((href) => {
340
336
  links.add({
@@ -12,7 +12,7 @@ function astroDevOverlay({ settings }) {
12
12
  if (id === resolvedVirtualModuleId) {
13
13
  return `
14
14
  export const loadDevOverlayPlugins = async () => {
15
- return [${settings.devToolbarApps.map((plugin) => `(await import('${plugin}')).default`).join(",")}];
15
+ return [${settings.devToolbarApps.map((plugin) => `(await import(${JSON.stringify(plugin)})).default`).join(",")}];
16
16
  };
17
17
  `;
18
18
  }
@@ -0,0 +1,6 @@
1
+ // Q: Why this file?
2
+ // A: Our language tooling needs to access the JSX types from `astro/jsx-runtime`, due to TS limitations, however we
3
+ // can't import `astro-jsx` types inside the actual `jsx-runtime/index.js` file due to circular dependency issues.
4
+ import './astro-jsx.js';
5
+ export * from './dist/jsx-runtime/index.js';
6
+ export import JSX = astroHTML.JSX;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "4.0.6",
3
+ "version": "4.0.8",
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",
@@ -34,12 +34,14 @@
34
34
  "./env": "./env.d.ts",
35
35
  "./types": "./types.d.ts",
36
36
  "./client": "./client.d.ts",
37
- "./import-meta": "./import-meta.d.ts",
38
37
  "./astro-jsx": "./astro-jsx.d.ts",
39
38
  "./tsconfigs/*.json": "./tsconfigs/*",
40
39
  "./tsconfigs/*": "./tsconfigs/*.json",
41
40
  "./jsx/*": "./dist/jsx/*",
42
- "./jsx-runtime": "./dist/jsx-runtime/index.js",
41
+ "./jsx-runtime": {
42
+ "types": "./jsx-runtime.d.ts",
43
+ "default": "./dist/jsx-runtime/index.js"
44
+ },
43
45
  "./compiler-runtime": "./dist/runtime/compiler/index.js",
44
46
  "./runtime/*": "./dist/runtime/*",
45
47
  "./config": {
@@ -90,9 +92,9 @@
90
92
  "zod.mjs",
91
93
  "env.d.ts",
92
94
  "client.d.ts",
95
+ "jsx-runtime.d.ts",
93
96
  "content-types.template.d.ts",
94
97
  "content-module.template.mjs",
95
- "import-meta.d.ts",
96
98
  "astro-jsx.d.ts",
97
99
  "types.d.ts",
98
100
  "README.md",
@@ -137,7 +139,7 @@
137
139
  "mime": "^3.0.0",
138
140
  "ora": "^7.0.1",
139
141
  "p-limit": "^5.0.0",
140
- "p-queue": "^7.4.1",
142
+ "p-queue": "^8.0.1",
141
143
  "path-to-regexp": "^6.2.1",
142
144
  "preferred-pm": "^3.1.2",
143
145
  "probe-image-size": "^7.2.3",
@@ -152,7 +154,7 @@
152
154
  "tsconfck": "^3.0.0",
153
155
  "unist-util-visit": "^5.0.0",
154
156
  "vfile": "^6.0.1",
155
- "vite": "^5.0.0",
157
+ "vite": "^5.0.10",
156
158
  "vitefu": "^0.2.5",
157
159
  "which-pm": "^2.1.1",
158
160
  "yargs-parser": "^21.1.1",
@@ -162,7 +164,7 @@
162
164
  "@astrojs/telemetry": "3.0.4"
163
165
  },
164
166
  "optionalDependencies": {
165
- "sharp": "^0.32.5"
167
+ "sharp": "^0.33.1"
166
168
  },
167
169
  "devDependencies": {
168
170
  "@astrojs/check": "^0.3.1",