astro 4.1.1 → 4.1.2

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.
@@ -5,7 +5,6 @@ import { getProxyCode } from "../assets/utils/proxy.js";
5
5
  import { AstroError } from "../core/errors/errors.js";
6
6
  import { AstroErrorData } from "../core/errors/index.js";
7
7
  import { isServerLikeOutput } from "../prerender/utils.js";
8
- import { escapeViteEnvReferences } from "../vite-plugin-utils/index.js";
9
8
  import { CONTENT_FLAG, DATA_FLAG } from "./consts.js";
10
9
  import {
11
10
  getContentEntryExts,
@@ -59,7 +58,7 @@ function astroContentImportPlugin({
59
58
  fs,
60
59
  pluginContext: this
61
60
  });
62
- const code = escapeViteEnvReferences(`
61
+ const code = `
63
62
  export const id = ${JSON.stringify(id)};
64
63
  export const collection = ${JSON.stringify(collection)};
65
64
  export const data = ${stringifyEntryData(data, isServerLikeOutput(settings.config))};
@@ -68,7 +67,7 @@ export const _internal = {
68
67
  filePath: ${JSON.stringify(_internal.filePath)},
69
68
  rawData: ${JSON.stringify(_internal.rawData)},
70
69
  };
71
- `);
70
+ `;
72
71
  return code;
73
72
  } else if (hasContentFlag(viteId, CONTENT_FLAG)) {
74
73
  const fileId = viteId.split("?")[0];
@@ -80,7 +79,7 @@ export const _internal = {
80
79
  fs,
81
80
  pluginContext: this
82
81
  });
83
- const code = escapeViteEnvReferences(`
82
+ const code = `
84
83
  export const id = ${JSON.stringify(id)};
85
84
  export const collection = ${JSON.stringify(collection)};
86
85
  export const slug = ${JSON.stringify(slug)};
@@ -90,7 +89,7 @@ export const _internal = {
90
89
  type: 'content',
91
90
  filePath: ${JSON.stringify(_internal.filePath)},
92
91
  rawData: ${JSON.stringify(_internal.rawData)},
93
- };`);
92
+ };`;
94
93
  return { code, map: { mappings: "" } };
95
94
  }
96
95
  },
@@ -1,4 +1,4 @@
1
- const ASTRO_VERSION = "4.1.1";
1
+ const ASTRO_VERSION = "4.1.2";
2
2
  const SUPPORTED_MARKDOWN_FILE_EXTENSIONS = [
3
3
  ".markdown",
4
4
  ".mdown",
@@ -73,5 +73,10 @@ declare class AstroCookies implements AstroCookiesInterface {
73
73
  * @returns
74
74
  */
75
75
  headers(): Generator<string, void, unknown>;
76
+ /**
77
+ * Behaves the same as AstroCookies.prototype.headers(),
78
+ * but allows a warning when cookies are set after the instance is consumed.
79
+ */
80
+ static consume(cookies: AstroCookies): Generator<string, void, unknown>;
76
81
  }
77
82
  export { AstroCookies };
@@ -28,10 +28,12 @@ class AstroCookies {
28
28
  #request;
29
29
  #requestValues;
30
30
  #outgoing;
31
+ #consumed;
31
32
  constructor(request) {
32
33
  this.#request = request;
33
34
  this.#requestValues = null;
34
35
  this.#outgoing = null;
36
+ this.#consumed = false;
35
37
  }
36
38
  /**
37
39
  * Astro.cookies.delete(key) is used to delete a cookie. Using this method will result
@@ -101,6 +103,13 @@ class AstroCookies {
101
103
  * @param options Options for the cookie, such as the path and security settings.
102
104
  */
103
105
  set(key, value, options) {
106
+ if (this.#consumed) {
107
+ const warning = new Error(
108
+ "Astro.cookies.set() was called after the cookies had already been sent to the browser.\nThis may have happened if this method was called in an imported component.\nPlease make sure that Astro.cookies.set() is only called in the frontmatter of the main page."
109
+ );
110
+ warning.name = "Warning";
111
+ console.warn(warning);
112
+ }
104
113
  let serializedValue;
105
114
  if (typeof value === "string") {
106
115
  serializedValue = value;
@@ -140,6 +149,14 @@ class AstroCookies {
140
149
  yield value[1];
141
150
  }
142
151
  }
152
+ /**
153
+ * Behaves the same as AstroCookies.prototype.headers(),
154
+ * but allows a warning when cookies are set after the instance is consumed.
155
+ */
156
+ static consume(cookies) {
157
+ cookies.#consumed = true;
158
+ return cookies.headers();
159
+ }
143
160
  #ensureParsed(options = void 0) {
144
161
  if (!this.#requestValues) {
145
162
  this.#parse(options);
@@ -1,4 +1,4 @@
1
- import type { AstroCookies } from './cookies.js';
1
+ import { AstroCookies } from './cookies.js';
2
2
  export declare function attachCookiesToResponse(response: Response, cookies: AstroCookies): void;
3
3
  export declare function responseHasCookies(response: Response): boolean;
4
4
  export declare function getSetCookiesFromResponse(response: Response): Generator<string, string[]>;
@@ -1,3 +1,4 @@
1
+ import { AstroCookies } from "./cookies.js";
1
2
  const astroCookiesSymbol = Symbol.for("astro.cookies");
2
3
  function attachCookiesToResponse(response, cookies) {
3
4
  Reflect.set(response, astroCookiesSymbol, cookies);
@@ -18,7 +19,7 @@ function* getSetCookiesFromResponse(response) {
18
19
  if (!cookies) {
19
20
  return [];
20
21
  }
21
- for (const headerValue of cookies.headers()) {
22
+ for (const headerValue of AstroCookies.consume(cookies)) {
22
23
  yield headerValue;
23
24
  }
24
25
  return [];
@@ -121,7 +121,9 @@ async function createVite(commandConfig, { settings, logger, mode, command, fs =
121
121
  root: fileURLToPath(settings.config.root),
122
122
  envPrefix: settings.config.vite?.envPrefix ?? "PUBLIC_",
123
123
  define: {
124
- "import.meta.env.SITE": settings.config.site ? JSON.stringify(settings.config.site) : "undefined"
124
+ "import.meta.env.SITE": stringifyForDefine(settings.config.site),
125
+ "import.meta.env.BASE_URL": stringifyForDefine(settings.config.base),
126
+ "import.meta.env.ASSETS_PREFIX": stringifyForDefine(settings.config.build.assetsPrefix)
125
127
  },
126
128
  server: {
127
129
  hmr: process.env.NODE_ENV === "test" || process.env.NODE_ENV === "production" ? false : void 0,
@@ -245,6 +247,9 @@ function isCommonNotAstro(dep) {
245
247
  // check prefix omitting @scope/
246
248
  );
247
249
  }
250
+ function stringifyForDefine(value) {
251
+ return typeof value === "string" ? JSON.stringify(value) : "undefined";
252
+ }
248
253
  export {
249
254
  createVite
250
255
  };
@@ -23,7 +23,7 @@ async function dev(inlineConfig) {
23
23
  base: restart.container.settings.config.base
24
24
  })
25
25
  );
26
- const currentVersion = "4.1.1";
26
+ const currentVersion = "4.1.2";
27
27
  if (currentVersion.includes("-")) {
28
28
  logger.warn("SKIP_FORMAT", msg.prerelease({ currentVersion }));
29
29
  }
@@ -133,7 +133,7 @@ function cleanErrorStack(stack) {
133
133
  return stack.split(/\n/g).map((l) => l.replace(/\/@fs\//g, "/")).join("\n");
134
134
  }
135
135
  function getDocsForError(err) {
136
- if (err.name in AstroErrorData) {
136
+ if (err.name !== "UnknownError" && err.name in AstroErrorData) {
137
137
  return `https://docs.astro.build/en/reference/errors/${getKebabErrorName(err.name)}/`;
138
138
  }
139
139
  return void 0;
@@ -389,7 +389,7 @@ export declare const NoMatchingImport: {
389
389
  export declare const InvalidPrerenderExport: {
390
390
  name: string;
391
391
  title: string;
392
- message: (prefix: string, suffix: string, isHydridOuput: boolean) => string;
392
+ message(prefix: string, suffix: string, isHydridOuput: boolean): string;
393
393
  hint: string;
394
394
  };
395
395
  /**
@@ -883,6 +883,38 @@ export declare const FailedToFindPageMapSSR: {
883
883
  title: string;
884
884
  message: string;
885
885
  };
886
+ /**
887
+ * @docs
888
+ * @description
889
+ * Astro can't find the requested locale. All supported locales must be configured in [i18n.locales](/en/reference/configuration-reference/#i18nlocales) and have corresponding directories within `src/pages/`.
890
+ */
891
+ export declare const MissingLocale: {
892
+ name: string;
893
+ title: string;
894
+ message: (locale: string) => string;
895
+ };
896
+ /**
897
+ * @docs
898
+ * @description
899
+ * Astro could not find an associated file with content while trying to render the route. This is an Astro error and not a user error. If restarting the dev server does not fix the problem, please file an issue.
900
+ */
901
+ export declare const CantRenderPage: {
902
+ name: string;
903
+ title: string;
904
+ message: string;
905
+ hint: string;
906
+ };
907
+ /**
908
+ * @docs
909
+ * @description
910
+ * Astro could not find any code to handle a rejected `Promise`. Make sure all your promises have an `await` or `.catch()` handler.
911
+ */
912
+ export declare const UnhandledRejection: {
913
+ name: string;
914
+ title: string;
915
+ message: (stack: string) => string;
916
+ hint: string;
917
+ };
886
918
  /**
887
919
  * @docs
888
920
  * @kind heading
@@ -1068,7 +1100,7 @@ export declare const UnknownContentCollectionError: {
1068
1100
  export declare const InvalidContentEntryFrontmatterError: {
1069
1101
  name: string;
1070
1102
  title: string;
1071
- message: (collection: string, entryId: string, error: ZodError) => string;
1103
+ message(collection: string, entryId: string, error: ZodError): string;
1072
1104
  hint: string;
1073
1105
  };
1074
1106
  /**
@@ -1082,7 +1114,7 @@ export declare const InvalidContentEntryFrontmatterError: {
1082
1114
  export declare const InvalidContentEntrySlugError: {
1083
1115
  name: string;
1084
1116
  title: string;
1085
- message: (collection: string, entryId: string) => string;
1117
+ message(collection: string, entryId: string): string;
1086
1118
  hint: string;
1087
1119
  };
1088
1120
  /**
@@ -1113,7 +1145,6 @@ export declare const CollectionDoesNotExistError: {
1113
1145
  };
1114
1146
  /**
1115
1147
  * @docs
1116
- * @message `COLLECTION_NAME` contains a mix of content and data entries. All entries must be of the same type.
1117
1148
  * @see
1118
1149
  * - [Defining content collections](https://docs.astro.build/en/guides/content-collections/#defining-collections)
1119
1150
  * @description
@@ -1122,12 +1153,11 @@ export declare const CollectionDoesNotExistError: {
1122
1153
  export declare const MixedContentDataCollectionError: {
1123
1154
  name: string;
1124
1155
  title: string;
1125
- message: (collection: string) => string;
1156
+ message: (collectionName: string) => string;
1126
1157
  hint: string;
1127
1158
  };
1128
1159
  /**
1129
1160
  * @docs
1130
- * @message `COLLECTION_NAME` contains entries of type `ACTUAL_TYPE`, but is configured as a `EXPECTED_TYPE` collection.
1131
1161
  * @see
1132
1162
  * - [Defining content collections](https://docs.astro.build/en/guides/content-collections/#defining-collections)
1133
1163
  * @description
@@ -1147,7 +1177,7 @@ export declare const ContentCollectionTypeMismatchError: {
1147
1177
  export declare const DataCollectionEntryParseError: {
1148
1178
  name: string;
1149
1179
  title: string;
1150
- message: (entryId: string, errorMessage: string) => string;
1180
+ message(entryId: string, errorMessage: string): string;
1151
1181
  hint: string;
1152
1182
  };
1153
1183
  /**
@@ -1159,7 +1189,7 @@ export declare const DataCollectionEntryParseError: {
1159
1189
  export declare const DuplicateContentEntrySlugError: {
1160
1190
  name: string;
1161
1191
  title: string;
1162
- message: (collection: string, slug: string, preExisting: string, alsoFound: string) => string;
1192
+ message(collection: string, slug: string, preExisting: string, alsoFound: string): string;
1163
1193
  };
1164
1194
  /**
1165
1195
  * @docs
@@ -1174,24 +1204,7 @@ export declare const UnsupportedConfigTransformError: {
1174
1204
  message: (parseError: string) => string;
1175
1205
  hint: string;
1176
1206
  };
1177
- export declare const MissingLocale: {
1178
- name: string;
1179
- title: string;
1180
- message: (locale: string) => string;
1181
- };
1182
- export declare const CantRenderPage: {
1183
- name: string;
1184
- title: string;
1185
- message: string;
1186
- hint: string;
1187
- };
1188
1207
  export declare const UnknownError: {
1189
1208
  name: string;
1190
1209
  title: string;
1191
1210
  };
1192
- export declare const UnhandledRejection: {
1193
- name: string;
1194
- title: string;
1195
- message: (stack: string) => string;
1196
- hint: string;
1197
- };
@@ -125,7 +125,7 @@ const NoMatchingImport = {
125
125
  const InvalidPrerenderExport = {
126
126
  name: "InvalidPrerenderExport",
127
127
  title: "Invalid prerender export.",
128
- message: (prefix, suffix, isHydridOuput) => {
128
+ message(prefix, suffix, isHydridOuput) {
129
129
  const defaultExpectedValue = isHydridOuput ? "false" : "true";
130
130
  let msg = `A \`prerender\` export has been detected, but its value cannot be statically analyzed.`;
131
131
  if (prefix !== "const")
@@ -308,6 +308,24 @@ const FailedToFindPageMapSSR = {
308
308
  title: "Astro couldn't find the correct page to render",
309
309
  message: "Astro couldn't find the correct page to render, probably because it wasn't correctly mapped for SSR usage. This is an internal error. Please file an issue."
310
310
  };
311
+ const MissingLocale = {
312
+ name: "MissingLocaleError",
313
+ title: "The provided locale does not exist.",
314
+ message: (locale) => `The locale/path \`${locale}\` does not exist in the configured \`i18n.locales\`.`
315
+ };
316
+ const CantRenderPage = {
317
+ name: "CantRenderPage",
318
+ title: "Astro can't render the route.",
319
+ message: "Astro cannot find any content to render for this route. There is no file or redirect associated with this route.",
320
+ hint: "If you expect to find a route here, this may be an Astro bug. Please file an issue/restart the dev server"
321
+ };
322
+ const UnhandledRejection = {
323
+ name: "UnhandledRejection",
324
+ title: "Unhandled rejection",
325
+ message: (stack) => `Astro detected an unhandled rejection. Here's the stack trace:
326
+ ${stack}`,
327
+ hint: "Make sure your promises all have an `await` or a `.catch()` handler."
328
+ };
311
329
  const UnknownCSSError = {
312
330
  name: "UnknownCSSError",
313
331
  title: "Unknown CSS Error."
@@ -368,7 +386,7 @@ const UnknownContentCollectionError = {
368
386
  const InvalidContentEntryFrontmatterError = {
369
387
  name: "InvalidContentEntryFrontmatterError",
370
388
  title: "Content entry frontmatter does not match schema.",
371
- message: (collection, entryId, error) => {
389
+ message(collection, entryId, error) {
372
390
  return [
373
391
  `**${String(collection)} \u2192 ${String(
374
392
  entryId
@@ -381,7 +399,7 @@ const InvalidContentEntryFrontmatterError = {
381
399
  const InvalidContentEntrySlugError = {
382
400
  name: "InvalidContentEntrySlugError",
383
401
  title: "Invalid content entry slug.",
384
- message: (collection, entryId) => {
402
+ message(collection, entryId) {
385
403
  return `${String(collection)} \u2192 ${String(
386
404
  entryId
387
405
  )} has an invalid slug. \`slug\` must be a string.`;
@@ -403,22 +421,18 @@ const CollectionDoesNotExistError = {
403
421
  const MixedContentDataCollectionError = {
404
422
  name: "MixedContentDataCollectionError",
405
423
  title: "Content and data cannot be in same collection.",
406
- message: (collection) => {
407
- return `**${collection}** contains a mix of content and data entries. All entries must be of the same type.`;
408
- },
424
+ message: (collectionName) => `**${collectionName}** contains a mix of content and data entries. All entries must be of the same type.`,
409
425
  hint: "Store data entries in a new collection separate from your content collection."
410
426
  };
411
427
  const ContentCollectionTypeMismatchError = {
412
428
  name: "ContentCollectionTypeMismatchError",
413
429
  title: "Collection contains entries of a different type.",
414
- message: (collection, expectedType, actualType) => {
415
- return `${collection} contains ${expectedType} entries, but is configured as a ${actualType} collection.`;
416
- }
430
+ message: (collection, expectedType, actualType) => `${collection} contains ${expectedType} entries, but is configured as a ${actualType} collection.`
417
431
  };
418
432
  const DataCollectionEntryParseError = {
419
433
  name: "DataCollectionEntryParseError",
420
434
  title: "Data collection entry failed to parse.",
421
- message: (entryId, errorMessage) => {
435
+ message(entryId, errorMessage) {
422
436
  return `**${entryId}** failed to parse: ${errorMessage}`;
423
437
  },
424
438
  hint: "Ensure your data entry is an object with valid JSON (for `.json` entries) or YAML (for `.yaml` entries)."
@@ -426,7 +440,7 @@ const DataCollectionEntryParseError = {
426
440
  const DuplicateContentEntrySlugError = {
427
441
  name: "DuplicateContentEntrySlugError",
428
442
  title: "Duplicate content entry slug.",
429
- message: (collection, slug, preExisting, alsoFound) => {
443
+ message(collection, slug, preExisting, alsoFound) {
430
444
  return `**${collection}** contains multiple entries with the same slug: \`${slug}\`. Slugs must be unique.
431
445
 
432
446
  Entries:
@@ -441,29 +455,7 @@ const UnsupportedConfigTransformError = {
441
455
  Full error: ${parseError}`,
442
456
  hint: "See the devalue library for all supported types: https://github.com/rich-harris/devalue"
443
457
  };
444
- const MissingLocale = {
445
- name: "MissingLocaleError",
446
- title: "The provided locale does not exist.",
447
- message: (locale) => {
448
- return `The locale/path \`${locale}\` does not exist in the configured \`i18n.locales\`.`;
449
- }
450
- };
451
- const CantRenderPage = {
452
- name: "CantRenderPage",
453
- title: "Astro can't render the route.",
454
- message: "Astro cannot find any content to render for this route. There is no file or redirect associated with this route.",
455
- hint: "If you expect to find a route here, this may be an Astro bug. Please file an issue/restart the dev server"
456
- };
457
458
  const UnknownError = { name: "UnknownError", title: "Unknown Error." };
458
- const UnhandledRejection = {
459
- name: "UnhandledRejection",
460
- title: "Unhandled rejection",
461
- message: (stack) => {
462
- return `Astro detected an unhandled rejection. Here's the stack trace:
463
- ${stack}`;
464
- },
465
- hint: "Make sure your promises all have an `await` or a `.catch()` handler."
466
- };
467
459
  export {
468
460
  AstroGlobNoMatch,
469
461
  AstroGlobUsedOutside,
@@ -36,7 +36,7 @@ function serverStart({
36
36
  host,
37
37
  base
38
38
  }) {
39
- const version = "4.1.1";
39
+ const version = "4.1.2";
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.1.1"}`
261
+ `v${"4.1.2"}`
262
262
  )} ${headline}`
263
263
  );
264
264
  }
@@ -24,8 +24,8 @@ function redirectRouteGenerate(redirectRoute, data) {
24
24
  }
25
25
  function redirectRouteStatus(redirectRoute, method = "GET") {
26
26
  const routeData = redirectRoute.redirectRoute;
27
- if (typeof routeData?.redirect === "object") {
28
- return routeData.redirect.status;
27
+ if (routeData && typeof redirectRoute.redirect === "object") {
28
+ return redirectRoute.redirect.status;
29
29
  } else if (method !== "GET") {
30
30
  return 308;
31
31
  }
@@ -333,7 +333,13 @@ This route collides with: "${collision.component}".`
333
333
  pathname: pathname || void 0,
334
334
  prerender: false,
335
335
  redirect: to,
336
- redirectRoute: routes.find((r) => r.route === to),
336
+ redirectRoute: routes.find((r) => {
337
+ if (typeof to === "object") {
338
+ return r.route === to.destination;
339
+ } else {
340
+ return r.route === to;
341
+ }
342
+ }),
337
343
  fallbackRoutes: []
338
344
  };
339
345
  const lastSegmentIsDynamic = (r) => !!r.segments.at(-1)?.at(-1)?.dynamic;
@@ -10,7 +10,7 @@ var astro_default = {
10
10
  async init(canvas, eventTarget) {
11
11
  createCanvas();
12
12
  document.addEventListener("astro:after-swap", createCanvas);
13
- eventTarget.addEventListener("plugin-toggled", async (event) => {
13
+ eventTarget.addEventListener("app-toggled", async (event) => {
14
14
  resetDebugButton();
15
15
  if (!(event instanceof CustomEvent))
16
16
  return;
@@ -44,7 +44,7 @@ var audit_default = {
44
44
  })
45
45
  );
46
46
  }
47
- eventTarget.addEventListener("plugin-toggled", (event) => {
47
+ eventTarget.addEventListener("app-toggled", (event) => {
48
48
  if (event.detail.state === true) {
49
49
  document.addEventListener("click", onPageClick, true);
50
50
  } else {
@@ -33,7 +33,7 @@ var xray_default = {
33
33
  })
34
34
  );
35
35
  }
36
- eventTarget.addEventListener("plugin-toggled", (event) => {
36
+ eventTarget.addEventListener("app-toggled", (event) => {
37
37
  if (event.detail.state === true) {
38
38
  document.addEventListener("click", onPageClick, true);
39
39
  } else {
@@ -98,12 +98,15 @@ function runScripts() {
98
98
  for (const script of Array.from(document.scripts)) {
99
99
  if (script.dataset.astroExec === "")
100
100
  continue;
101
+ const type = script.getAttribute("type");
102
+ if (type && type !== "module" && type !== "text/javascript")
103
+ continue;
101
104
  const newScript = document.createElement("script");
102
105
  newScript.innerHTML = script.innerHTML;
103
106
  for (const attr of script.attributes) {
104
107
  if (attr.name === "src") {
105
108
  const p = new Promise((r) => {
106
- newScript.onload = r;
109
+ newScript.onload = newScript.onerror = r;
107
110
  });
108
111
  wait = wait.then(() => p);
109
112
  }
@@ -284,7 +284,10 @@ async function handleRoute({
284
284
  return;
285
285
  }
286
286
  if (status && response.status !== status && (status === 404 || status === 500)) {
287
- response = new Response(response.body, { ...response, status });
287
+ response = new Response(response.body, {
288
+ status,
289
+ headers: response.headers
290
+ });
288
291
  }
289
292
  await writeSSRResult(request, response, incomingResponse);
290
293
  }
@@ -48,6 +48,9 @@ function configAliasVitePlugin({
48
48
  for (const alias of configAlias) {
49
49
  if (alias.find.test(id)) {
50
50
  const updatedId = id.replace(alias.find, alias.replacement);
51
+ if (updatedId.includes("*")) {
52
+ return updatedId;
53
+ }
51
54
  const resolved = await this.resolve(updatedId, importer, { skipSelf: true, ...options });
52
55
  if (resolved)
53
56
  return resolved;
@@ -3,5 +3,5 @@ import type { AstroSettings } from '../@types/astro.js';
3
3
  interface EnvPluginOptions {
4
4
  settings: AstroSettings;
5
5
  }
6
- export default function envVitePlugin({ settings }: EnvPluginOptions): vite.PluginOption;
6
+ export default function envVitePlugin({ settings }: EnvPluginOptions): vite.Plugin;
7
7
  export {};
@@ -1,6 +1,9 @@
1
- import MagicString from "magic-string";
2
1
  import { fileURLToPath } from "node:url";
3
2
  import { loadEnv } from "vite";
3
+ import { transform } from "esbuild";
4
+ import MagicString from "magic-string";
5
+ const importMetaEnvOnlyRe = /\bimport\.meta\.env\b(?!\.)/;
6
+ const isValidIdentifierRe = /^[_$a-zA-Z][_$a-zA-Z0-9]*$/;
4
7
  function getPrivateEnv(viteConfig, astroConfig) {
5
8
  let envPrefixes = ["PUBLIC_"];
6
9
  if (viteConfig.envPrefix) {
@@ -13,7 +16,7 @@ function getPrivateEnv(viteConfig, astroConfig) {
13
16
  );
14
17
  const privateEnv = {};
15
18
  for (const key in fullEnv) {
16
- if (envPrefixes.every((prefix) => !key.startsWith(prefix))) {
19
+ if (isValidIdentifierRe.test(key) && envPrefixes.every((prefix) => !key.startsWith(prefix))) {
17
20
  if (typeof process.env[key] !== "undefined") {
18
21
  let value = process.env[key];
19
22
  if (typeof value !== "string") {
@@ -29,10 +32,6 @@ function getPrivateEnv(viteConfig, astroConfig) {
29
32
  }
30
33
  }
31
34
  }
32
- privateEnv.SITE = astroConfig.site ? JSON.stringify(astroConfig.site) : "undefined";
33
- privateEnv.SSR = JSON.stringify(true);
34
- privateEnv.BASE_URL = astroConfig.base ? JSON.stringify(astroConfig.base) : "undefined";
35
- privateEnv.ASSETS_PREFIX = astroConfig.build.assetsPrefix ? JSON.stringify(astroConfig.build.assetsPrefix) : "undefined";
36
35
  return privateEnv;
37
36
  }
38
37
  function getReferencedPrivateKeys(source, privateEnv) {
@@ -44,63 +43,102 @@ function getReferencedPrivateKeys(source, privateEnv) {
44
43
  }
45
44
  return references;
46
45
  }
46
+ async function replaceDefine(code, id, define, config) {
47
+ const replacementMarkers = {};
48
+ const env = define["import.meta.env"];
49
+ if (env) {
50
+ const marker = `__astro_import_meta_env${"_".repeat(
51
+ env.length - 23
52
+ /* length of preceding string */
53
+ )}`;
54
+ replacementMarkers[marker] = env;
55
+ define = { ...define, "import.meta.env": marker };
56
+ }
57
+ const esbuildOptions = config.esbuild || {};
58
+ const result = await transform(code, {
59
+ loader: "js",
60
+ charset: esbuildOptions.charset ?? "utf8",
61
+ platform: "neutral",
62
+ define,
63
+ sourcefile: id,
64
+ sourcemap: config.command === "build" ? !!config.build.sourcemap : true
65
+ });
66
+ for (const marker in replacementMarkers) {
67
+ result.code = result.code.replaceAll(marker, replacementMarkers[marker]);
68
+ }
69
+ return {
70
+ code: result.code,
71
+ map: result.map || null
72
+ };
73
+ }
47
74
  function envVitePlugin({ settings }) {
48
75
  let privateEnv;
76
+ let defaultDefines;
77
+ let isDev;
78
+ let devImportMetaEnvPrepend;
49
79
  let viteConfig;
50
80
  const { config: astroConfig } = settings;
51
81
  return {
52
82
  name: "astro:vite-plugin-env",
53
- enforce: "pre",
54
- config() {
55
- return {
56
- define: {
57
- "import.meta.env.BASE_URL": astroConfig.base ? JSON.stringify(astroConfig.base) : "undefined",
58
- "import.meta.env.ASSETS_PREFIX": astroConfig.build.assetsPrefix ? JSON.stringify(astroConfig.build.assetsPrefix) : "undefined"
59
- }
60
- };
83
+ config(_, { command }) {
84
+ isDev = command !== "build";
61
85
  },
62
86
  configResolved(resolvedConfig) {
63
87
  viteConfig = resolvedConfig;
88
+ const viteDefinePluginIndex = resolvedConfig.plugins.findIndex(
89
+ (p) => p.name === "vite:define"
90
+ );
91
+ if (viteDefinePluginIndex !== -1) {
92
+ const myPluginIndex = resolvedConfig.plugins.findIndex(
93
+ (p) => p.name === "astro:vite-plugin-env"
94
+ );
95
+ if (myPluginIndex !== -1) {
96
+ const myPlugin = resolvedConfig.plugins[myPluginIndex];
97
+ resolvedConfig.plugins.splice(viteDefinePluginIndex, 0, myPlugin);
98
+ resolvedConfig.plugins.splice(myPluginIndex, 1);
99
+ }
100
+ }
64
101
  },
65
- async transform(source, id, options) {
102
+ transform(source, id, options) {
66
103
  if (!options?.ssr || !source.includes("import.meta.env")) {
67
104
  return;
68
105
  }
69
- let s;
70
- const pattern = new RegExp(
71
- // Do not allow preceding '.', but do allow preceding '...' for spread operations
72
- `(?<!(?<!\\.\\.)\\.)\\b(import\\.meta\\.env\\.(.+?)|import\\.meta\\.env)\\b(?!\\s*?=[^=])`,
73
- "g"
74
- );
75
- let references;
76
- let match;
77
- while (match = pattern.exec(source)) {
78
- let replacement;
79
- if (match[0] === "import.meta.env") {
80
- privateEnv ??= getPrivateEnv(viteConfig, astroConfig);
81
- references ??= getReferencedPrivateKeys(source, privateEnv);
82
- replacement = `(Object.assign(import.meta.env,{`;
83
- for (const key of references.values()) {
84
- replacement += `${key}:${privateEnv[key]},`;
106
+ privateEnv ??= getPrivateEnv(viteConfig, astroConfig);
107
+ if (isDev) {
108
+ const s = new MagicString(source);
109
+ if (!devImportMetaEnvPrepend) {
110
+ devImportMetaEnvPrepend = `Object.assign(import.meta.env,{`;
111
+ for (const key in privateEnv) {
112
+ devImportMetaEnvPrepend += `${key}:${privateEnv[key]},`;
85
113
  }
86
- replacement += "}))";
87
- } else if (match[2]) {
88
- privateEnv ??= getPrivateEnv(viteConfig, astroConfig);
89
- replacement = privateEnv[match[2]];
90
- }
91
- if (replacement) {
92
- const start = match.index;
93
- const end = start + match[0].length;
94
- s ??= new MagicString(source);
95
- s.overwrite(start, end, replacement);
114
+ devImportMetaEnvPrepend += "});";
96
115
  }
97
- }
98
- if (s) {
116
+ s.prepend(devImportMetaEnvPrepend);
99
117
  return {
100
118
  code: s.toString(),
101
119
  map: s.generateMap({ hires: "boundary" })
102
120
  };
103
121
  }
122
+ if (!defaultDefines) {
123
+ defaultDefines = {};
124
+ for (const key in privateEnv) {
125
+ defaultDefines[`import.meta.env.${key}`] = privateEnv[key];
126
+ }
127
+ }
128
+ let defines = defaultDefines;
129
+ if (importMetaEnvOnlyRe.test(source)) {
130
+ const references = getReferencedPrivateKeys(source, privateEnv);
131
+ let replacement = `(Object.assign(import.meta.env,{`;
132
+ for (const key of references.values()) {
133
+ replacement += `${key}:${privateEnv[key]},`;
134
+ }
135
+ replacement += "}))";
136
+ defines = {
137
+ ...defaultDefines,
138
+ "import.meta.env": replacement
139
+ };
140
+ }
141
+ return replaceDefine(source, id, defines, viteConfig);
104
142
  }
105
143
  };
106
144
  }
@@ -10,7 +10,7 @@ import { normalizePath } from "vite";
10
10
  import { AstroError, AstroErrorData, MarkdownError } from "../core/errors/index.js";
11
11
  import { isMarkdownFile } from "../core/util.js";
12
12
  import { shorthash } from "../runtime/server/shorthash.js";
13
- import { escapeViteEnvReferences, getFileInfo } from "../vite-plugin-utils/index.js";
13
+ import { getFileInfo } from "../vite-plugin-utils/index.js";
14
14
  import { getMarkdownCodeForImages } from "./images.js";
15
15
  function safeMatter(source, id) {
16
16
  try {
@@ -86,7 +86,7 @@ function markdown({ settings, logger }) {
86
86
  `[${id}] Astro now supports MDX! Support for components in ".md" (or alternative extensions like ".markdown") files using the "setup" frontmatter is no longer enabled by default. Migrate this file to MDX.`
87
87
  );
88
88
  }
89
- const code = escapeViteEnvReferences(`
89
+ const code = `
90
90
  import { unescapeHTML, spreadAttributes, createComponent, render, renderComponent, maybeRenderHead } from ${JSON.stringify(
91
91
  astroServerRuntimeModulePath
92
92
  )};
@@ -128,7 +128,7 @@ function markdown({ settings, logger }) {
128
128
  })}\`;` : `render\`\${maybeRenderHead(result)}\${unescapeHTML(html)}\`;`}
129
129
  });
130
130
  export default Content;
131
- `);
131
+ `;
132
132
  return {
133
133
  code,
134
134
  meta: {
@@ -1,10 +1,4 @@
1
1
  import type { AstroConfig } from '../@types/astro.js';
2
- /**
3
- * Converts the first dot in `import.meta.env` to its Unicode escape sequence,
4
- * which prevents Vite from replacing strings like `import.meta.env.SITE`
5
- * in our JS representation of modules like Markdown
6
- */
7
- export declare function escapeViteEnvReferences(code: string): string;
8
2
  export declare function getFileInfo(id: string, config: AstroConfig): {
9
3
  fileId: string;
10
4
  fileUrl: string | undefined;
@@ -6,9 +6,6 @@ import {
6
6
  removeLeadingForwardSlashWindows
7
7
  } from "../core/path.js";
8
8
  import { viteID } from "../core/util.js";
9
- function escapeViteEnvReferences(code) {
10
- return code.replace(/import\.meta\.env/g, "import\\u002Emeta.env").replace(/process\.env/g, "process\\u002Eenv");
11
- }
12
9
  function getFileInfo(id, config) {
13
10
  const sitePathname = appendForwardSlash(
14
11
  config.site ? new URL(config.base, config.site).pathname : config.base
@@ -38,7 +35,6 @@ function cleanUrl(url) {
38
35
  }
39
36
  export {
40
37
  cleanUrl,
41
- escapeViteEnvReferences,
42
38
  getFileInfo,
43
39
  normalizeFilename
44
40
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "4.1.1",
3
+ "version": "4.1.2",
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",
@@ -166,7 +166,7 @@
166
166
  "@astrojs/telemetry": "3.0.4"
167
167
  },
168
168
  "optionalDependencies": {
169
- "sharp": "^0.33.1"
169
+ "sharp": "^0.32.6"
170
170
  },
171
171
  "devDependencies": {
172
172
  "@astrojs/check": "^0.3.1",