astro 5.10.0 → 5.10.1

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.
@@ -2,7 +2,7 @@ import { AstroError, AstroErrorData, AstroUserError } from "../core/errors/index
2
2
  import { CONTENT_LAYER_TYPE, LIVE_CONTENT_TYPE } from "./consts.js";
3
3
  function getImporterFilename() {
4
4
  const stackLine = new Error().stack?.split("\n").find(
5
- (line) => !line.includes("defineCollection") && !line.includes("defineLiveCollection") && !line.includes("getImporterFilename") && line !== "Error"
5
+ (line) => !line.includes("defineCollection") && !line.includes("defineLiveCollection") && !line.includes("getImporterFilename") && !line.startsWith("Error")
6
6
  );
7
7
  if (!stackLine) {
8
8
  return void 0;
@@ -12,7 +12,7 @@ function getImporterFilename() {
12
12
  }
13
13
  function defineLiveCollection(config) {
14
14
  const importerFilename = getImporterFilename();
15
- if (!importerFilename?.includes("live.config")) {
15
+ if (importerFilename && !importerFilename.includes("live.config")) {
16
16
  throw new AstroError({
17
17
  ...AstroErrorData.LiveContentConfigError,
18
18
  message: AstroErrorData.LiveContentConfigError.message(
@@ -164,7 +164,7 @@ ${contentConfig.error.message}`);
164
164
  logger.info("Content config changed");
165
165
  shouldClear = true;
166
166
  }
167
- if (previousAstroVersion && previousAstroVersion !== "5.10.0") {
167
+ if (previousAstroVersion && previousAstroVersion !== "5.10.1") {
168
168
  logger.info("Astro version changed");
169
169
  shouldClear = true;
170
170
  }
@@ -172,8 +172,8 @@ ${contentConfig.error.message}`);
172
172
  logger.info("Clearing content store");
173
173
  this.#store.clearAll();
174
174
  }
175
- if ("5.10.0") {
176
- await this.#store.metaStore().set("astro-version", "5.10.0");
175
+ if ("5.10.1") {
176
+ await this.#store.metaStore().set("astro-version", "5.10.1");
177
177
  }
178
178
  if (currentConfigDigest) {
179
179
  await this.#store.metaStore().set("content-config-digest", currentConfigDigest);
@@ -41,7 +41,8 @@ function createCollectionToGlobResultMap({
41
41
  }
42
42
  const cacheHintSchema = z.object({
43
43
  tags: z.array(z.string()).optional(),
44
- maxAge: z.number().optional()
44
+ maxAge: z.number().optional(),
45
+ lastModified: z.date().optional()
45
46
  });
46
47
  async function parseLiveEntry(entry, schema, collection) {
47
48
  try {
@@ -381,24 +382,37 @@ function createGetLiveCollection({
381
382
  if (processedEntries.length > 0) {
382
383
  const entryTags = /* @__PURE__ */ new Set();
383
384
  let minMaxAge;
385
+ let latestModified;
384
386
  for (const entry of processedEntries) {
385
387
  if (entry.cacheHint) {
386
388
  if (entry.cacheHint.tags) {
387
389
  entry.cacheHint.tags.forEach((tag) => entryTags.add(tag));
388
390
  }
389
391
  if (typeof entry.cacheHint.maxAge === "number") {
390
- minMaxAge = minMaxAge === void 0 ? entry.cacheHint.maxAge : Math.min(minMaxAge, entry.cacheHint.maxAge);
392
+ if (minMaxAge === void 0 || entry.cacheHint.maxAge < minMaxAge) {
393
+ minMaxAge = entry.cacheHint.maxAge;
394
+ }
395
+ }
396
+ if (entry.cacheHint.lastModified instanceof Date) {
397
+ if (latestModified === void 0 || entry.cacheHint.lastModified > latestModified) {
398
+ latestModified = entry.cacheHint.lastModified;
399
+ }
391
400
  }
392
401
  }
393
402
  }
394
- if (entryTags.size > 0 || minMaxAge !== void 0 || cacheHint) {
403
+ if (entryTags.size > 0 || minMaxAge !== void 0 || latestModified || cacheHint) {
395
404
  const mergedCacheHint = {};
396
405
  if (cacheHint?.tags || entryTags.size > 0) {
397
- mergedCacheHint.tags = [...cacheHint?.tags || [], ...entryTags];
406
+ mergedCacheHint.tags = [.../* @__PURE__ */ new Set([...cacheHint?.tags || [], ...entryTags])];
398
407
  }
399
408
  if (cacheHint?.maxAge !== void 0 || minMaxAge !== void 0) {
400
409
  mergedCacheHint.maxAge = cacheHint?.maxAge !== void 0 && minMaxAge !== void 0 ? Math.min(cacheHint.maxAge, minMaxAge) : cacheHint?.maxAge ?? minMaxAge;
401
410
  }
411
+ if (cacheHint?.lastModified && latestModified) {
412
+ mergedCacheHint.lastModified = cacheHint.lastModified > latestModified ? cacheHint.lastModified : latestModified;
413
+ } else if (cacheHint?.lastModified || latestModified) {
414
+ mergedCacheHint.lastModified = cacheHint?.lastModified ?? latestModified;
415
+ }
402
416
  cacheHint = mergedCacheHint;
403
417
  }
404
418
  }
@@ -498,7 +512,9 @@ async function updateImageReferencesInBody(html, fileName) {
498
512
  return Object.entries({
499
513
  ...attributes,
500
514
  src: image.src,
501
- srcset: image.srcSet.attribute
515
+ srcset: image.srcSet.attribute,
516
+ // This attribute is used by the toolbar audit
517
+ ...import.meta.env.DEV ? { "data-image-component": "true" } : {}
502
518
  }).map(([key, value]) => value ? `${key}="${escape(value)}"` : "").join(" ");
503
519
  });
504
520
  }
@@ -1,4 +1,4 @@
1
- const ASTRO_VERSION = "5.10.0";
1
+ const ASTRO_VERSION = "5.10.1";
2
2
  const REROUTE_DIRECTIVE_HEADER = "X-Astro-Reroute";
3
3
  const REWRITE_DIRECTIVE_HEADER_KEY = "X-Astro-Rewrite";
4
4
  const REWRITE_DIRECTIVE_HEADER_VALUE = "yes";
@@ -22,7 +22,7 @@ async function dev(inlineConfig) {
22
22
  await telemetry.record([]);
23
23
  const restart = await createContainerWithAutomaticRestart({ inlineConfig, fs });
24
24
  const logger = restart.container.logger;
25
- const currentVersion = "5.10.0";
25
+ const currentVersion = "5.10.1";
26
26
  const isPrerelease = currentVersion.includes("-");
27
27
  if (!isPrerelease) {
28
28
  try {
@@ -51,6 +51,12 @@ async function restartContainer(container) {
51
51
  container.restartInFlight = true;
52
52
  try {
53
53
  const { astroConfig } = await resolveConfig(container.inlineConfig, "dev", container.fs);
54
+ if (astroConfig.experimental.csp) {
55
+ logger.warn(
56
+ "config",
57
+ "Astro's Content Security Policy (CSP) does not work in development mode. To verify your CSP implementation, build the project and run the preview server."
58
+ );
59
+ }
54
60
  const settings = await createSettings(astroConfig, fileURLToPath(existingSettings.config.root));
55
61
  await close();
56
62
  return await createRestartedContainer(container, settings);
@@ -80,9 +86,20 @@ async function createContainerWithAutomaticRestart({
80
86
  }) {
81
87
  const logger = createNodeLogger(inlineConfig ?? {});
82
88
  const { userConfig, astroConfig } = await resolveConfig(inlineConfig ?? {}, "dev", fs);
89
+ if (astroConfig.experimental.csp) {
90
+ logger.warn(
91
+ "config",
92
+ "Astro's Content Security Policy (CSP) does not work in development mode. To verify your CSP implementation, build the project and run the preview server."
93
+ );
94
+ }
83
95
  telemetry.record(eventCliSession("dev", userConfig));
84
96
  const settings = await createSettings(astroConfig, fileURLToPath(astroConfig.root));
85
- const initialContainer = await createContainer({ settings, logger, inlineConfig, fs });
97
+ const initialContainer = await createContainer({
98
+ settings,
99
+ logger,
100
+ inlineConfig,
101
+ fs
102
+ });
86
103
  let resolveRestart;
87
104
  let restartComplete = new Promise((resolve) => {
88
105
  resolveRestart = resolve;
@@ -1517,7 +1517,7 @@ export declare const ContentEntryDataError: {
1517
1517
  * @description
1518
1518
  * Error in live content config.
1519
1519
  * @see
1520
- * - [Experimental live content](https://astro.build/en/reference/experimental-flags/live-content-collections/)
1520
+ * - [Experimental live content](https://docs.astro.build/en/reference/experimental-flags/live-content-collections/)
1521
1521
  */
1522
1522
  export declare const LiveContentConfigError: {
1523
1523
  name: string;
@@ -37,7 +37,7 @@ function serverStart({
37
37
  host,
38
38
  base
39
39
  }) {
40
- const version = "5.10.0";
40
+ const version = "5.10.1";
41
41
  const localPrefix = `${dim("\u2503")} Local `;
42
42
  const networkPrefix = `${dim("\u2503")} Network `;
43
43
  const emptyPrefix = " ".repeat(11);
@@ -274,7 +274,7 @@ function printHelp({
274
274
  message.push(
275
275
  linebreak(),
276
276
  ` ${bgGreen(black(` ${commandName} `))} ${green(
277
- `v${"5.10.0"}`
277
+ `v${"5.10.1"}`
278
278
  )} ${headline}`
279
279
  );
280
280
  }
@@ -20,6 +20,7 @@ import {
20
20
  } from "./constants.js";
21
21
  import { AstroCookies, attachCookiesToResponse } from "./cookies/index.js";
22
22
  import { getCookiesFromResponse } from "./cookies/response.js";
23
+ import { generateCspDigest } from "./encryption.js";
23
24
  import { CspNotEnabled, ForbiddenRewrite } from "./errors/errors-data.js";
24
25
  import { AstroError, AstroErrorData } from "./errors/index.js";
25
26
  import { callMiddleware } from "./middleware/callMiddleware.js";
@@ -362,6 +363,18 @@ class RenderContext {
362
363
  const { cookies, pathname, pipeline, routeData, status } = this;
363
364
  const { clientDirectives, inlinedScripts, compressHTML, manifest, renderers, resolve } = pipeline;
364
365
  const { links, scripts, styles } = await pipeline.headElements(routeData);
366
+ const extraStyleHashes = [];
367
+ const extraScriptHashes = [];
368
+ const shouldInjectCspMetaTags = !!manifest.csp;
369
+ const cspAlgorithm = manifest.csp?.algorithm ?? "SHA-256";
370
+ if (shouldInjectCspMetaTags) {
371
+ for (const style of styles) {
372
+ extraStyleHashes.push(await generateCspDigest(style.children, cspAlgorithm));
373
+ }
374
+ for (const script of scripts) {
375
+ extraScriptHashes.push(await generateCspDigest(script.children, cspAlgorithm));
376
+ }
377
+ }
365
378
  const componentMetadata = await pipeline.componentMetadata(routeData) ?? manifest.componentMetadata;
366
379
  const headers = new Headers({ "Content-Type": "text/html" });
367
380
  const partial = typeof this.partial === "boolean" ? this.partial : Boolean(mod.partial);
@@ -411,13 +424,13 @@ class RenderContext {
411
424
  hasRenderedServerIslandRuntime: false,
412
425
  headInTree: false,
413
426
  extraHead: [],
414
- extraStyleHashes: [],
415
- extraScriptHashes: [],
427
+ extraStyleHashes,
428
+ extraScriptHashes,
416
429
  propagators: /* @__PURE__ */ new Set()
417
430
  },
418
431
  cspDestination: manifest.csp?.cspDestination ?? (routeData.prerender ? "meta" : "header"),
419
- shouldInjectCspMetaTags: !!manifest.csp,
420
- cspAlgorithm: manifest.csp?.algorithm ?? "SHA-256",
432
+ shouldInjectCspMetaTags,
433
+ cspAlgorithm,
421
434
  // The following arrays must be cloned, otherwise they become mutable across routes.
422
435
  scriptHashes: manifest.csp?.scriptHashes ? [...manifest.csp.scriptHashes] : [],
423
436
  scriptResources: manifest.csp?.scriptResources ? [...manifest.csp.scriptResources] : [],
@@ -67,7 +67,7 @@ function spreadAttributes(values = {}, _name, { class: scopedClassName } = {}) {
67
67
  }
68
68
  }
69
69
  for (const [key, value] of Object.entries(values)) {
70
- output += addAttribute2(value, key, true);
70
+ output += addAttribute2(value, key, true, _name);
71
71
  }
72
72
  return markHTMLString2(output);
73
73
  }
@@ -228,7 +228,9 @@ If you're still stuck, please open an issue on GitHub or join us at https://astr
228
228
  const Tag = sanitizeElementName(Component);
229
229
  const childSlots = Object.values(children).join("");
230
230
  const renderTemplateResult = renderTemplate`<${Tag}${internalSpreadAttributes(
231
- props
231
+ props,
232
+ true,
233
+ Tag
232
234
  )}${markHTMLString(
233
235
  childSlots === "" && voidElementNames.test(Tag) ? `/>` : `>${childSlots}</${Tag}>`
234
236
  )}`;
@@ -5,8 +5,8 @@ export declare const toAttributeString: (value: any, shouldEscape?: boolean) =>
5
5
  export declare const toStyleString: (obj: Record<string, any>) => string;
6
6
  export declare function defineScriptVars(vars: Record<any, any>): any;
7
7
  export declare function formatList(values: string[]): string;
8
- export declare function addAttribute(value: any, key: string, shouldEscape?: boolean): any;
9
- export declare function internalSpreadAttributes(values: Record<any, any>, shouldEscape?: boolean): any;
8
+ export declare function addAttribute(value: any, key: string, shouldEscape?: boolean, tagName?: string): any;
9
+ export declare function internalSpreadAttributes(values: Record<any, any>, shouldEscape: boolean | undefined, tagName: string): any;
10
10
  export declare function renderElement(name: string, { props: _props, children }: SSRElement, shouldEscape?: boolean): string;
11
11
  /**
12
12
  * Executes the `bufferRenderFunction` to prerender it into a buffer destination, and return a promise
@@ -33,7 +33,16 @@ function formatList(values) {
33
33
  }
34
34
  return `${values.slice(0, -1).join(", ")} or ${values[values.length - 1]}`;
35
35
  }
36
- function addAttribute(value, key, shouldEscape = true) {
36
+ function isCustomElement(tagName) {
37
+ return tagName.includes("-");
38
+ }
39
+ function handleBooleanAttribute(key, value, shouldEscape, tagName) {
40
+ if (tagName && isCustomElement(tagName)) {
41
+ return markHTMLString(` ${key}="${toAttributeString(value, shouldEscape)}"`);
42
+ }
43
+ return markHTMLString(value ? ` ${key}` : "");
44
+ }
45
+ function addAttribute(value, key, shouldEscape = true, tagName = "") {
37
46
  if (value == null) {
38
47
  return "";
39
48
  }
@@ -67,23 +76,23 @@ Make sure to use the static attribute syntax (\`${key}={value}\`) instead of the
67
76
  return markHTMLString(` ${key}="${toAttributeString(value, false)}"`);
68
77
  }
69
78
  if (htmlBooleanAttributes.test(key)) {
70
- return markHTMLString(value ? ` ${key}` : "");
79
+ return handleBooleanAttribute(key, value, shouldEscape, tagName);
71
80
  }
72
81
  if (value === "") {
73
82
  return markHTMLString(` ${key}`);
74
83
  }
75
84
  if (key === "popover" && typeof value === "boolean") {
76
- return markHTMLString(value ? ` popover` : "");
85
+ return handleBooleanAttribute(key, value, shouldEscape, tagName);
77
86
  }
78
87
  if (key === "download" && typeof value === "boolean") {
79
- return markHTMLString(value ? ` download` : "");
88
+ return handleBooleanAttribute(key, value, shouldEscape, tagName);
80
89
  }
81
90
  return markHTMLString(` ${key}="${toAttributeString(value, shouldEscape)}"`);
82
91
  }
83
- function internalSpreadAttributes(values, shouldEscape = true) {
92
+ function internalSpreadAttributes(values, shouldEscape = true, tagName) {
84
93
  let output = "";
85
94
  for (const [key, value] of Object.entries(values)) {
86
- output += addAttribute(value, key, shouldEscape);
95
+ output += addAttribute(value, key, shouldEscape, tagName);
87
96
  }
88
97
  return markHTMLString(output);
89
98
  }
@@ -100,9 +109,9 @@ function renderElement(name, { props: _props, children = "" }, shouldEscape = tr
100
109
  }
101
110
  }
102
111
  if ((children == null || children == "") && voidElementNames.test(name)) {
103
- return `<${name}${internalSpreadAttributes(props, shouldEscape)}>`;
112
+ return `<${name}${internalSpreadAttributes(props, shouldEscape, name)}>`;
104
113
  }
105
- return `<${name}${internalSpreadAttributes(props, shouldEscape)}>${children}</${name}>`;
114
+ return `<${name}${internalSpreadAttributes(props, shouldEscape, name)}>${children}</${name}>`;
106
115
  }
107
116
  const noop = () => {
108
117
  };
@@ -1240,6 +1240,20 @@ export interface ViteUserConfig extends OriginalViteUserConfig {
1240
1240
 
1241
1241
  */
1242
1242
  remotePatterns?: Partial<RemotePattern>[];
1243
+ /**
1244
+ * @docs
1245
+ * @name image.responsiveStyles
1246
+ * @type {boolean}
1247
+ * @default `false`
1248
+ * @version 5.10.0
1249
+ * @description
1250
+ * Whether to automatically add global styles for responsive images. You should enable this option unless you are styling the images yourself.
1251
+ *
1252
+ * This option is only used when `layout` is set to `constrained`, `full-width`, or `fixed` using the configuration or the `layout` prop on the image component.
1253
+ *
1254
+ * See [the images docs](https://docs.astro.build/en/guides/images/#responsive-image-styles) for more information.
1255
+ */
1256
+ responsiveStyles?: boolean;
1243
1257
  /**
1244
1258
  * @docs
1245
1259
  * @name image.layout
@@ -1262,7 +1276,7 @@ export interface ViteUserConfig extends OriginalViteUserConfig {
1262
1276
  * @default `"cover"`
1263
1277
  * @version 5.10.0
1264
1278
  * @description
1265
- * The default object-fit value for responsive images. Can be overridden by the `fit` prop on the image component.
1279
+ * The [`object-fit` CSS property value](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) for responsive images. Can be overridden by the `fit` prop on the image component.
1266
1280
  * Requires a value for `layout` to be set.
1267
1281
  *
1268
1282
  * See [the `fit` component property](https://docs.astro.build/en/reference/modules/astro-assets/#fit) for more details.
@@ -1275,7 +1289,7 @@ export interface ViteUserConfig extends OriginalViteUserConfig {
1275
1289
  * @default `"center"`
1276
1290
  * @version 5.10.0
1277
1291
  * @description
1278
- * The default object-position value for responsive images. Can be overridden by the `position` prop on the image component.
1292
+ * The default [`object-position` CSS property value](https://developer.mozilla.org/en-US/docs/Web/CSS/object-position) for responsive images. Can be overridden by the `position` prop on the image component.
1279
1293
  * Requires a value for `layout` to be set.
1280
1294
  *
1281
1295
  * See [the `position` component property](https://docs.astro.build/en/reference/modules/astro-assets/#position) for more details.
@@ -1293,19 +1307,6 @@ export interface ViteUserConfig extends OriginalViteUserConfig {
1293
1307
  * the more comprehensive list is used, because only the required sizes are generated. For local services, the list is shorter to reduce the number of images generated.
1294
1308
  */
1295
1309
  breakpoints?: number[];
1296
- /**
1297
- * @docs
1298
- * @name image.responsiveStyles
1299
- * @type {boolean}
1300
- * @default `false`
1301
- * @version 5.10.0
1302
- * @description
1303
- * Whether to automatically add global styles for responsive images. You should enable this option unless you are styling the images yourself.
1304
- * This option is only used when `layout` is set to `constrained`, `full-width`, or `fixed` using the configuration or the `layout` prop on the image component.
1305
- *
1306
- * See [the images docs](https://docs.astro.build/en/guides/images/#responsive-image-styles) for more information.
1307
- */
1308
- responsiveStyles?: boolean;
1309
1310
  };
1310
1311
  /**
1311
1312
  * @docs
@@ -114,6 +114,8 @@ export interface CacheHint {
114
114
  tags?: Array<string>;
115
115
  /** Maximum age of the response in seconds */
116
116
  maxAge?: number;
117
+ /** Last modified time of the content */
118
+ lastModified?: Date;
117
119
  }
118
120
  export interface LiveDataEntry<TData extends Record<string, any> = Record<string, unknown>> {
119
121
  /** The ID of the entry. Unique per collection. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "5.10.0",
3
+ "version": "5.10.1",
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",