astro 4.2.1 → 4.2.3

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 (44) hide show
  1. package/astro-jsx.d.ts +2 -0
  2. package/dist/@types/astro.d.ts +8 -1
  3. package/dist/cli/db/index.d.ts +4 -0
  4. package/dist/cli/db/index.js +22 -0
  5. package/dist/cli/index.js +6 -0
  6. package/dist/cli/preferences/index.js +48 -15
  7. package/dist/core/app/index.js +2 -1
  8. package/dist/core/app/types.d.ts +1 -0
  9. package/dist/core/build/generate.js +4 -2
  10. package/dist/core/build/plugins/plugin-manifest.js +2 -1
  11. package/dist/core/build/static-build.js +1 -1
  12. package/dist/core/compile/index.d.ts +1 -1
  13. package/dist/core/compile/index.js +2 -10
  14. package/dist/core/config/schema.d.ts +10 -0
  15. package/dist/core/config/schema.js +1 -0
  16. package/dist/core/constants.js +1 -1
  17. package/dist/core/dev/dev.js +1 -1
  18. package/dist/core/logger/core.d.ts +1 -1
  19. package/dist/core/messages.js +2 -2
  20. package/dist/i18n/index.js +2 -2
  21. package/dist/i18n/middleware.d.ts +1 -1
  22. package/dist/i18n/middleware.js +3 -2
  23. package/dist/preferences/index.d.ts +1 -0
  24. package/dist/preferences/index.js +6 -0
  25. package/dist/runtime/client/dev-toolbar/apps/settings.js +2 -2
  26. package/dist/runtime/client/dev-toolbar/entrypoint.js +1 -1
  27. package/dist/runtime/client/dev-toolbar/settings.d.ts +4 -1
  28. package/dist/runtime/client/dev-toolbar/settings.js +8 -1
  29. package/dist/runtime/client/dev-toolbar/toolbar.d.ts +1 -0
  30. package/dist/runtime/client/dev-toolbar/toolbar.js +28 -6
  31. package/dist/runtime/server/index.js +4 -1
  32. package/dist/vite-plugin-astro/compile.d.ts +5 -3
  33. package/dist/vite-plugin-astro/compile.js +10 -4
  34. package/dist/vite-plugin-astro/hmr.d.ts +5 -6
  35. package/dist/vite-plugin-astro/hmr.js +28 -25
  36. package/dist/vite-plugin-astro/index.js +43 -57
  37. package/dist/vite-plugin-astro/types.d.ts +9 -1
  38. package/dist/vite-plugin-astro-server/plugin.js +1 -0
  39. package/dist/vite-plugin-astro-server/route.js +6 -1
  40. package/dist/vite-plugin-dev-toolbar/vite-plugin-dev-toolbar.d.ts +1 -1
  41. package/dist/vite-plugin-dev-toolbar/vite-plugin-dev-toolbar.js +39 -2
  42. package/package.json +5 -5
  43. package/dist/core/compile/cache.d.ts +0 -6
  44. package/dist/core/compile/cache.js +0 -38
package/astro-jsx.d.ts CHANGED
@@ -647,6 +647,7 @@ declare namespace astroHTML.JSX {
647
647
  name?: string | undefined | null;
648
648
  type?: 'submit' | 'reset' | 'button' | undefined | null;
649
649
  value?: string | string[] | number | undefined | null;
650
+ popovertarget?: string | undefined | null;
650
651
  }
651
652
 
652
653
  interface CanvasHTMLAttributes extends HTMLAttributes {
@@ -811,6 +812,7 @@ declare namespace astroHTML.JSX {
811
812
  type?: HTMLInputTypeAttribute | undefined | null;
812
813
  value?: string | string[] | number | undefined | null;
813
814
  width?: number | string | undefined | null;
815
+ popovertarget?: string | undefined | null;
814
816
  }
815
817
 
816
818
  interface KeygenHTMLAttributes extends HTMLAttributes {
@@ -1414,6 +1414,8 @@ export interface AstroUserConfig {
1414
1414
  strategy: 'pathname';
1415
1415
  };
1416
1416
  };
1417
+ /** ⚠️ WARNING: SUBJECT TO CHANGE */
1418
+ db?: Config.Database;
1417
1419
  /**
1418
1420
  * @docs
1419
1421
  * @kind heading
@@ -2399,7 +2401,7 @@ export interface ClientDirectiveConfig {
2399
2401
  export interface DevToolbarApp {
2400
2402
  id: string;
2401
2403
  name: string;
2402
- icon: Icon;
2404
+ icon?: Icon;
2403
2405
  init?(canvas: ShadowRoot, eventTarget: EventTarget): void | Promise<void>;
2404
2406
  beforeTogglingOff?(canvas: ShadowRoot): boolean | Promise<boolean>;
2405
2407
  }
@@ -2435,3 +2437,8 @@ declare global {
2435
2437
  'astro-dev-overlay-card': DevToolbarCard;
2436
2438
  }
2437
2439
  }
2440
+ declare global {
2441
+ namespace Config {
2442
+ type Database = Record<string, any>;
2443
+ }
2444
+ }
@@ -0,0 +1,4 @@
1
+ import type { Arguments } from 'yargs-parser';
2
+ export declare function db({ flags }: {
3
+ flags: Arguments;
4
+ }): Promise<void>;
@@ -0,0 +1,22 @@
1
+ import { createLoggerFromFlags, flagsToAstroInlineConfig } from "../flags.js";
2
+ import { getPackage } from "../install-package.js";
3
+ import { resolveConfig } from "../../core/config/config.js";
4
+ async function db({ flags }) {
5
+ const logger = createLoggerFromFlags(flags);
6
+ const getPackageOpts = { skipAsk: flags.yes || flags.y, cwd: flags.root };
7
+ const dbPackage = await getPackage("@astrojs/db", logger, getPackageOpts, []);
8
+ if (!dbPackage) {
9
+ logger.error(
10
+ "check",
11
+ "The `@astrojs/db` package is required for this command to work. Please manually install it in your project and try again."
12
+ );
13
+ return;
14
+ }
15
+ const { cli } = dbPackage;
16
+ const inlineConfig = flagsToAstroInlineConfig(flags);
17
+ const { astroConfig } = await resolveConfig(inlineConfig, "build");
18
+ await cli({ flags, config: astroConfig });
19
+ }
20
+ export {
21
+ db
22
+ };
package/dist/cli/index.js CHANGED
@@ -51,6 +51,7 @@ function resolveCommand(flags) {
51
51
  "preview",
52
52
  "check",
53
53
  "docs",
54
+ "db",
54
55
  "info"
55
56
  ]);
56
57
  if (supportedCommands.has(cmd)) {
@@ -107,6 +108,11 @@ async function runCommand(cmd, flags) {
107
108
  await add(packages, { flags });
108
109
  return;
109
110
  }
111
+ case "db": {
112
+ const { db } = await import("./db/index.js");
113
+ await db({ flags });
114
+ return;
115
+ }
110
116
  case "dev": {
111
117
  const { dev } = await import("./dev/index.js");
112
118
  const server = await dev({ flags });
@@ -1,4 +1,4 @@
1
- import { bgGreen, black, bold, dim } from "kleur/colors";
1
+ import { bgGreen, black, bold, dim, yellow } from "kleur/colors";
2
2
  import { fileURLToPath } from "node:url";
3
3
  import dlv from "dlv";
4
4
  import { resolveConfig } from "../../core/config/config.js";
@@ -156,23 +156,41 @@ async function resetPreference(settings, key, { location }) {
156
156
  }
157
157
  return 1;
158
158
  }
159
+ function annotate(flat, annotation) {
160
+ return Object.fromEntries(
161
+ Object.entries(flat).map(([key, value]) => [key, { annotation, value }])
162
+ );
163
+ }
164
+ function userValues(flatDefault, flatProject, flatGlobal) {
165
+ const result = {};
166
+ for (const key of Object.keys(flatDefault)) {
167
+ if (key in flatProject) {
168
+ result[key] = {
169
+ value: flatProject[key],
170
+ annotation: ""
171
+ };
172
+ if (key in flatGlobal) {
173
+ result[key].annotation += ` (also modified globally)`;
174
+ }
175
+ } else if (key in flatGlobal) {
176
+ result[key] = { value: flatGlobal[key], annotation: "(global)" };
177
+ }
178
+ }
179
+ return result;
180
+ }
159
181
  async function listPreferences(settings, { location, json }) {
160
182
  if (json) {
161
183
  const resolved = await settings.preferences.getAll();
162
184
  console.log(JSON.stringify(resolved, null, 2));
163
185
  return 0;
164
186
  }
165
- const { global, project, defaults } = await settings.preferences.list({ location });
187
+ const { global, project, fromAstroConfig, defaults } = await settings.preferences.list({
188
+ location
189
+ });
166
190
  const flatProject = flattie(project);
167
191
  const flatGlobal = flattie(global);
168
- const flatUser = Object.assign({}, flatGlobal, flatProject);
169
- for (let key of Object.keys(flatUser)) {
170
- if (!isValidKey(key)) {
171
- delete flatUser[key];
172
- continue;
173
- }
174
- }
175
192
  const flatDefault = flattie(defaults);
193
+ const flatUser = userValues(flatDefault, flatProject, flatGlobal);
176
194
  const userKeys = Object.keys(flatUser);
177
195
  if (userKeys.length > 0) {
178
196
  const badge = bgGreen(black(` Your Preferences `));
@@ -183,7 +201,7 @@ async function listPreferences(settings, { location, json }) {
183
201
  const message = dim("No preferences set");
184
202
  console.log(["", badge, "", message].join("\n"));
185
203
  }
186
- const flatUnset = Object.assign({}, flatDefault);
204
+ const flatUnset = annotate(Object.assign({}, flatDefault), "");
187
205
  for (const key of userKeys) {
188
206
  delete flatUnset[key];
189
207
  }
@@ -197,6 +215,13 @@ async function listPreferences(settings, { location, json }) {
197
215
  const message = dim("All preferences have been set");
198
216
  console.log(["", badge, "", message].join("\n"));
199
217
  }
218
+ if (fromAstroConfig.devToolbar?.enabled === false && flatUser["devToolbar.enabled"]?.value !== false) {
219
+ console.log(
220
+ yellow(
221
+ "The dev toolbar is currently disabled. To enable it, set devToolbar: {enabled: true} in your astroConfig file."
222
+ )
223
+ );
224
+ }
200
225
  return 0;
201
226
  }
202
227
  function prettyPrint(value) {
@@ -220,14 +245,22 @@ const chars = {
220
245
  bottomLeft: "\u2570",
221
246
  bottomRight: "\u256F"
222
247
  };
248
+ function annotatedFormat(mv) {
249
+ return mv.annotation ? `${mv.value} ${mv.annotation}` : mv.value.toString();
250
+ }
251
+ function formatAnnotated(mv, style = (v) => v.toString()) {
252
+ return mv.annotation ? `${style(mv.value)} ${dim(mv.annotation)}` : style(mv.value);
253
+ }
223
254
  function formatTable(object, columnLabels) {
224
255
  const [colA, colB] = columnLabels;
225
256
  const colALength = [colA, ...Object.keys(object)].reduce(longest, 0) + 3;
226
- const colBLength = [colB, ...Object.values(object)].reduce(longest, 0) + 3;
257
+ const colBLength = [colB, ...Object.values(object).map(annotatedFormat)].reduce(longest, 0) + 3;
227
258
  function formatRow(i2, a, b, style = (v) => v.toString()) {
228
- return `${dim(chars.v)} ${style(a)} ${space(colALength - a.length - 2)} ${dim(chars.v)} ${style(
229
- b
230
- )} ${space(colBLength - b.toString().length - 3)} ${dim(chars.v)}`;
259
+ return `${dim(chars.v)} ${style(a)} ${space(colALength - a.length - 2)} ${dim(
260
+ chars.v
261
+ )} ${formatAnnotated(b, style)} ${space(colBLength - annotatedFormat(b).length - 3)} ${dim(
262
+ chars.v
263
+ )}`;
231
264
  }
232
265
  const top = dim(
233
266
  `${chars.topLeft}${chars.h.repeat(colALength + 1)}${chars.hBottom}${chars.h.repeat(
@@ -242,7 +275,7 @@ function formatTable(object, columnLabels) {
242
275
  const divider = dim(
243
276
  `${chars.vRightThick}${chars.hThick.repeat(colALength + 1)}${chars.hThickCross}${chars.hThick.repeat(colBLength)}${chars.vLeftThick}`
244
277
  );
245
- const rows = [top, formatRow(-1, colA, colB, bold), divider];
278
+ const rows = [top, formatRow(-1, colA, { value: colB, annotation: "" }, bold), divider];
246
279
  let i = 0;
247
280
  for (const [key, value] of Object.entries(object)) {
248
281
  rows.push(formatRow(i, key, value, (v) => formatWithOptions({ colors: true }, v)));
@@ -170,7 +170,8 @@ class App {
170
170
  const i18nMiddleware = createI18nMiddleware(
171
171
  this.#manifest.i18n,
172
172
  this.#manifest.base,
173
- this.#manifest.trailingSlash
173
+ this.#manifest.trailingSlash,
174
+ this.#manifest.buildFormat
174
175
  );
175
176
  if (i18nMiddleware) {
176
177
  if (mod.onRequest) {
@@ -32,6 +32,7 @@ export type SSRManifest = {
32
32
  site?: string;
33
33
  base: string;
34
34
  trailingSlash: 'always' | 'never' | 'ignore';
35
+ buildFormat: 'file' | 'directory';
35
36
  compressHTML: boolean;
36
37
  assetsPrefix?: string;
37
38
  renderers: SSRLoadedRenderer[];
@@ -196,7 +196,8 @@ async function generatePage(pageData, ssrEntry, builtPaths, pipeline) {
196
196
  const i18nMiddleware = createI18nMiddleware(
197
197
  pipeline.getManifest().i18n,
198
198
  pipeline.getManifest().base,
199
- pipeline.getManifest().trailingSlash
199
+ pipeline.getManifest().trailingSlash,
200
+ pipeline.getManifest().buildFormat
200
201
  );
201
202
  if (config.i18n && i18nMiddleware) {
202
203
  if (onRequest) {
@@ -481,7 +482,8 @@ function createBuildManifest(settings, internals, renderers) {
481
482
  assetsPrefix: settings.config.build.assetsPrefix,
482
483
  site: settings.config.site ? new URL(settings.config.base, settings.config.site).toString() : settings.config.site,
483
484
  componentMetadata: internals.componentMetadata,
484
- i18n: i18nManifest
485
+ i18n: i18nManifest,
486
+ buildFormat: settings.config.build.format
485
487
  };
486
488
  }
487
489
  export {
@@ -202,7 +202,8 @@ function buildManifest(opts, internals, staticFiles) {
202
202
  clientDirectives: Array.from(settings.clientDirectives),
203
203
  entryModules,
204
204
  assets: staticFiles.map(prefixAssetPath),
205
- i18n: i18nManifest
205
+ i18n: i18nManifest,
206
+ buildFormat: settings.config.build.format
206
207
  };
207
208
  }
208
209
  export {
@@ -330,7 +330,7 @@ async function cleanServerOutput(opts, ssrOutputChunkNames, internals) {
330
330
  removeEmptyDirs(out);
331
331
  }
332
332
  if (out.toString() !== opts.settings.config.outDir.toString()) {
333
- await copyFiles(out, opts.settings.config.outDir);
333
+ await copyFiles(out, opts.settings.config.outDir, true);
334
334
  await fs.promises.rm(out, { recursive: true });
335
335
  return;
336
336
  }
@@ -1,3 +1,3 @@
1
- export { cachedCompilation, getCachedCompileResult, invalidateCompilation, isCached, } from './cache.js';
1
+ export { compile } from './compile.js';
2
2
  export type { CompileProps, CompileResult } from './compile.js';
3
3
  export type { TransformStyle } from './types.js';
@@ -1,12 +1,4 @@
1
- import {
2
- cachedCompilation,
3
- getCachedCompileResult,
4
- invalidateCompilation,
5
- isCached
6
- } from "./cache.js";
1
+ import { compile } from "./compile.js";
7
2
  export {
8
- cachedCompilation,
9
- getCachedCompileResult,
10
- invalidateCompilation,
11
- isCached
3
+ compile
12
4
  };
@@ -32,6 +32,7 @@ export declare const AstroConfigSchema: z.ZodObject<{
32
32
  name: string;
33
33
  hooks?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
34
34
  }>>;
35
+ db: z.ZodOptional<z.ZodDefault<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>>;
35
36
  integrations: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodObject<{
36
37
  name: z.ZodString;
37
38
  hooks: z.ZodDefault<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
@@ -417,6 +418,7 @@ export declare const AstroConfigSchema: z.ZodObject<{
417
418
  [k: string]: unknown;
418
419
  };
419
420
  } | undefined;
421
+ db?: z.objectOutputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
420
422
  prefetch?: boolean | {
421
423
  prefetchAll?: boolean | undefined;
422
424
  defaultStrategy?: "tap" | "hover" | "viewport" | "load" | undefined;
@@ -446,6 +448,7 @@ export declare const AstroConfigSchema: z.ZodObject<{
446
448
  name: string;
447
449
  hooks?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
448
450
  } | undefined;
451
+ db?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
449
452
  integrations?: unknown;
450
453
  build?: {
451
454
  format?: "file" | "directory" | undefined;
@@ -605,6 +608,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
605
608
  name: string;
606
609
  hooks?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
607
610
  }>>;
611
+ db: z.ZodOptional<z.ZodDefault<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>>;
608
612
  integrations: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodObject<{
609
613
  name: z.ZodString;
610
614
  hooks: z.ZodDefault<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
@@ -935,6 +939,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
935
939
  [k: string]: unknown;
936
940
  };
937
941
  } | undefined;
942
+ db?: z.objectOutputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
938
943
  prefetch?: boolean | {
939
944
  prefetchAll?: boolean | undefined;
940
945
  defaultStrategy?: "tap" | "hover" | "viewport" | "load" | undefined;
@@ -976,6 +981,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
976
981
  name: string;
977
982
  hooks?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
978
983
  } | undefined;
984
+ db?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
979
985
  integrations?: unknown;
980
986
  prefetch?: boolean | {
981
987
  prefetchAll?: boolean | undefined;
@@ -1122,6 +1128,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
1122
1128
  [k: string]: unknown;
1123
1129
  };
1124
1130
  } | undefined;
1131
+ db?: z.objectOutputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
1125
1132
  prefetch?: boolean | {
1126
1133
  prefetchAll?: boolean | undefined;
1127
1134
  defaultStrategy?: "tap" | "hover" | "viewport" | "load" | undefined;
@@ -1163,6 +1170,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
1163
1170
  name: string;
1164
1171
  hooks?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
1165
1172
  } | undefined;
1173
+ db?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
1166
1174
  integrations?: unknown;
1167
1175
  prefetch?: boolean | {
1168
1176
  prefetchAll?: boolean | undefined;
@@ -1309,6 +1317,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
1309
1317
  [k: string]: unknown;
1310
1318
  };
1311
1319
  } | undefined;
1320
+ db?: z.objectOutputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
1312
1321
  prefetch?: boolean | {
1313
1322
  prefetchAll?: boolean | undefined;
1314
1323
  defaultStrategy?: "tap" | "hover" | "viewport" | "load" | undefined;
@@ -1350,6 +1359,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
1350
1359
  name: string;
1351
1360
  hooks?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
1352
1361
  } | undefined;
1362
+ db?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
1353
1363
  integrations?: unknown;
1354
1364
  prefetch?: boolean | {
1355
1365
  prefetchAll?: boolean | undefined;
@@ -60,6 +60,7 @@ const AstroConfigSchema = z.object({
60
60
  output: z.union([z.literal("static"), z.literal("server"), z.literal("hybrid")]).optional().default("static"),
61
61
  scopedStyleStrategy: z.union([z.literal("where"), z.literal("class"), z.literal("attribute")]).optional().default("attribute"),
62
62
  adapter: z.object({ name: z.string(), hooks: z.object({}).passthrough().default({}) }).optional(),
63
+ db: z.object({}).passthrough().default({}).optional(),
63
64
  integrations: z.preprocess(
64
65
  // preprocess
65
66
  (val) => Array.isArray(val) ? val.flat(Infinity).filter(Boolean) : val,
@@ -1,4 +1,4 @@
1
- const ASTRO_VERSION = "4.2.1";
1
+ const ASTRO_VERSION = "4.2.3";
2
2
  const SUPPORTED_MARKDOWN_FILE_EXTENSIONS = [
3
3
  ".markdown",
4
4
  ".mdown",
@@ -23,7 +23,7 @@ async function dev(inlineConfig) {
23
23
  base: restart.container.settings.config.base
24
24
  })
25
25
  );
26
- const currentVersion = "4.2.1";
26
+ const currentVersion = "4.2.3";
27
27
  if (currentVersion.includes("-")) {
28
28
  logger.warn("SKIP_FORMAT", msg.prerelease({ currentVersion }));
29
29
  }
@@ -7,7 +7,7 @@ export type LoggerLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';
7
7
  * rather than specific to a single command, function, use, etc. The label will be
8
8
  * shown in the log message to the user, so it should be relevant.
9
9
  */
10
- export type LoggerLabel = 'add' | 'build' | 'check' | 'config' | 'content' | 'deprecated' | 'markdown' | 'router' | 'types' | 'vite' | 'watch' | 'middleware' | 'preferences' | 'redirects' | 'SKIP_FORMAT';
10
+ export type LoggerLabel = 'add' | 'build' | 'check' | 'config' | 'content' | 'deprecated' | 'markdown' | 'router' | 'types' | 'vite' | 'watch' | 'middleware' | 'preferences' | 'redirects' | 'toolbar' | 'SKIP_FORMAT';
11
11
  export interface LogOptions {
12
12
  dest: LogWritable<LogMessage>;
13
13
  level: LoggerLevel;
@@ -36,7 +36,7 @@ function serverStart({
36
36
  host,
37
37
  base
38
38
  }) {
39
- const version = "4.2.1";
39
+ const version = "4.2.3";
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.2.1"}`
261
+ `v${"4.2.3"}`
262
262
  )} ${headline}`
263
263
  );
264
264
  }
@@ -23,7 +23,7 @@ function getLocaleRelativeUrl({
23
23
  }
24
24
  const pathsToJoin = [base, prependWith];
25
25
  const normalizedLocale = normalizeLocale ? normalizeTheLocale(codeToUse) : codeToUse;
26
- if (routing === "pathname-prefix-always") {
26
+ if (routing === "pathname-prefix-always" || routing === "pathname-prefix-always-no-redirect") {
27
27
  pathsToJoin.push(normalizedLocale);
28
28
  } else if (locale !== defaultLocale) {
29
29
  pathsToJoin.push(normalizedLocale);
@@ -58,7 +58,7 @@ function getLocaleRelativeUrlList({
58
58
  return locales.map((locale) => {
59
59
  const pathsToJoin = [base, prependWith];
60
60
  const normalizedLocale = normalizeLocale ? normalizeTheLocale(locale) : locale;
61
- if (routing === "pathname-prefix-always") {
61
+ if (routing === "pathname-prefix-always" || routing === "pathname-prefix-always-no-redirect") {
62
62
  pathsToJoin.push(normalizedLocale);
63
63
  } else if (locale !== defaultLocale) {
64
64
  pathsToJoin.push(normalizedLocale);
@@ -1,6 +1,6 @@
1
1
  import type { MiddlewareHandler, SSRManifest } from '../@types/astro.js';
2
2
  import type { PipelineHookFunction } from '../core/pipeline.js';
3
- export declare function createI18nMiddleware(i18n: SSRManifest['i18n'], base: SSRManifest['base'], trailingSlash: SSRManifest['trailingSlash']): MiddlewareHandler | undefined;
3
+ export declare function createI18nMiddleware(i18n: SSRManifest['i18n'], base: SSRManifest['base'], trailingSlash: SSRManifest['trailingSlash'], buildFormat: SSRManifest['buildFormat']): MiddlewareHandler | undefined;
4
4
  /**
5
5
  * This pipeline hook attaches a `RouteData` object to the `Request`
6
6
  */
@@ -1,5 +1,6 @@
1
1
  import { appendForwardSlash, joinPaths } from "@astrojs/internal-helpers/path";
2
2
  import { getPathByLocale, normalizeTheLocale } from "./index.js";
3
+ import { shouldAppendForwardSlash } from "../core/build/util.js";
3
4
  const routeDataSymbol = Symbol.for("astro.routeData");
4
5
  function pathnameHasLocale(pathname, locales) {
5
6
  const segments = pathname.split("/");
@@ -16,7 +17,7 @@ function pathnameHasLocale(pathname, locales) {
16
17
  }
17
18
  return false;
18
19
  }
19
- function createI18nMiddleware(i18n, base, trailingSlash) {
20
+ function createI18nMiddleware(i18n, base, trailingSlash, buildFormat) {
20
21
  if (!i18n) {
21
22
  return void 0;
22
23
  }
@@ -59,7 +60,7 @@ function createI18nMiddleware(i18n, base, trailingSlash) {
59
60
  }
60
61
  case "pathname-prefix-always": {
61
62
  if (url.pathname === base + "/" || url.pathname === base) {
62
- if (trailingSlash === "always") {
63
+ if (shouldAppendForwardSlash(trailingSlash, buildFormat)) {
63
64
  return context.redirect(`${appendForwardSlash(joinPaths(base, i18n.defaultLocale))}`);
64
65
  } else {
65
66
  return context.redirect(`${joinPaths(base, i18n.defaultLocale)}`);
@@ -13,6 +13,7 @@ type DeepPartial<T> = T extends object ? {
13
13
  } : T;
14
14
  export type PreferenceKey = DotKeys<Preferences>;
15
15
  export interface PreferenceList extends Record<PreferenceLocation, DeepPartial<Preferences>> {
16
+ fromAstroConfig: DeepPartial<Preferences>;
16
17
  defaults: Preferences;
17
18
  }
18
19
  export interface AstroPreferences {
@@ -49,8 +49,14 @@ function createPreferences(config) {
49
49
  return {
50
50
  global: stores["global"].getAll(),
51
51
  project: stores["project"].getAll(),
52
+ fromAstroConfig: mapFrom(DEFAULT_PREFERENCES, config),
52
53
  defaults: DEFAULT_PREFERENCES
53
54
  };
55
+ function mapFrom(defaults, astroConfig) {
56
+ return Object.fromEntries(
57
+ Object.entries(defaults).map(([key, _]) => [key, astroConfig[key]])
58
+ );
59
+ }
54
60
  }
55
61
  };
56
62
  }
@@ -14,7 +14,7 @@ const settingsRows = [
14
14
  }
15
15
  settings.updateSetting("disableAppNotification", evt.currentTarget.checked);
16
16
  const action = evt.currentTarget.checked ? "disabled" : "enabled";
17
- settings.log(`App notification badges ${action}`);
17
+ settings.logger.verboseLog(`App notification badges ${action}`);
18
18
  }
19
19
  }
20
20
  },
@@ -27,7 +27,7 @@ const settingsRows = [
27
27
  if (evt.currentTarget instanceof HTMLInputElement) {
28
28
  settings.updateSetting("verbose", evt.currentTarget.checked);
29
29
  const action = evt.currentTarget.checked ? "enabled" : "disabled";
30
- settings.log(`Verbose logging ${action}`);
30
+ settings.logger.verboseLog(`Verbose logging ${action}`);
31
31
  }
32
32
  }
33
33
  }
@@ -173,7 +173,7 @@ document.addEventListener("DOMContentLoaded", async () => {
173
173
  button.setAttribute("data-app-id", app.id);
174
174
  const iconContainer = document.createElement("div");
175
175
  const iconElement = document.createElement("template");
176
- iconElement.innerHTML = getAppIcon(app.icon);
176
+ iconElement.innerHTML = app.icon ? getAppIcon(app.icon) : "?";
177
177
  iconContainer.append(iconElement.content.cloneNode(true));
178
178
  const notification = document.createElement("div");
179
179
  notification.classList.add("notification");
@@ -9,5 +9,8 @@ export declare const defaultSettings: {
9
9
  export declare const settings: {
10
10
  readonly config: Settings;
11
11
  updateSetting: (key: keyof Settings, value: boolean) => void;
12
- log: (message: string) => void;
12
+ logger: {
13
+ log: (message: string) => void;
14
+ verboseLog: (message: string) => void;
15
+ };
13
16
  };
@@ -30,7 +30,14 @@ function getSettings() {
30
30
  return _settings;
31
31
  },
32
32
  updateSetting,
33
- log
33
+ logger: {
34
+ log,
35
+ verboseLog: (message) => {
36
+ if (_settings.verbose) {
37
+ log(message);
38
+ }
39
+ }
40
+ }
34
41
  };
35
42
  }
36
43
  export {
@@ -28,6 +28,7 @@ export declare class AstroDevToolbar extends HTMLElement {
28
28
  getAppTemplate(app: DevToolbarApp): string;
29
29
  getAppById(id: string): DevToolbarApp | undefined;
30
30
  getAppCanvasById(id: string): HTMLElement | null;
31
+ getAppButtonById(id: string): HTMLElement | null;
31
32
  toggleAppStatus(app: DevToolbarApp): Promise<void>;
32
33
  setAppStatus(app: DevToolbarApp, newStatus: boolean): Promise<boolean>;
33
34
  isHidden(): boolean;
@@ -109,6 +109,11 @@ class AstroDevToolbar extends HTMLElement {
109
109
  outline-offset: -3px;
110
110
  }
111
111
 
112
+ #dev-bar #bar-container .item[data-app-error]:hover, #dev-bar #bar-container .item[data-app-error]:focus-visible {
113
+ cursor: not-allowed;
114
+ background: #ff252520;
115
+ }
116
+
112
117
  #dev-bar .item:first-of-type {
113
118
  border-top-left-radius: 9999px;
114
119
  border-bottom-left-radius: 9999px;
@@ -149,6 +154,10 @@ class AstroDevToolbar extends HTMLElement {
149
154
  border-top: 5px solid #343841;
150
155
  }
151
156
 
157
+ #dev-bar .item[data-app-error] .icon {
158
+ opacity: 0.35;
159
+ }
160
+
152
161
  #dev-bar .item:hover .item-tooltip, #dev-bar .item:not(.active):focus-visible .item-tooltip {
153
162
  transition: opacity 0.2s ease-in-out 200ms;
154
163
  opacity: 1;
@@ -229,8 +238,7 @@ class AstroDevToolbar extends HTMLElement {
229
238
  this.devToolbarContainer = this.shadowRoot.querySelector("#dev-toolbar-root");
230
239
  this.attachEvents();
231
240
  this.apps.forEach(async (app) => {
232
- if (settings.config.verbose)
233
- console.log(`Creating app canvas for ${app.id}`);
241
+ settings.logger.verboseLog(`Creating app canvas for ${app.id}`);
234
242
  const appCanvas = document.createElement("astro-dev-toolbar-app-canvas");
235
243
  appCanvas.dataset.appId = app.id;
236
244
  this.shadowRoot?.append(appCanvas);
@@ -311,8 +319,7 @@ class AstroDevToolbar extends HTMLElement {
311
319
  const shadowRoot = this.getAppCanvasById(app.id).shadowRoot;
312
320
  app.status = "loading";
313
321
  try {
314
- if (settings.config.verbose)
315
- console.info(`Initializing app ${app.id}`);
322
+ settings.logger.verboseLog(`Initializing app ${app.id}`);
316
323
  await app.init?.(shadowRoot, app.eventTarget);
317
324
  app.status = "ready";
318
325
  if (import.meta.hot) {
@@ -322,11 +329,23 @@ class AstroDevToolbar extends HTMLElement {
322
329
  } catch (e) {
323
330
  console.error(`Failed to init app ${app.id}, error: ${e}`);
324
331
  app.status = "error";
332
+ if (import.meta.hot) {
333
+ import.meta.hot.send("astro:devtoolbar:error:init", {
334
+ app,
335
+ error: e instanceof Error ? e.stack : e
336
+ });
337
+ }
338
+ const appButton = this.getAppButtonById(app.id);
339
+ const appTooltip = appButton?.querySelector(".item-tooltip");
340
+ if (appButton && appTooltip) {
341
+ appButton.toggleAttribute("data-app-error", true);
342
+ appTooltip.innerText = `Error initializing ${app.name}`;
343
+ }
325
344
  }
326
345
  }
327
346
  getAppTemplate(app) {
328
347
  return `<button class="item" data-app-id="${app.id}">
329
- <div class="icon">${getAppIcon(app.icon)}<div class="notification"></div></div>
348
+ <div class="icon">${app.icon ? getAppIcon(app.icon) : "?"}<div class="notification"></div></div>
330
349
  <span class="item-tooltip">${app.name}</span>
331
350
  </button>`;
332
351
  }
@@ -338,6 +357,9 @@ class AstroDevToolbar extends HTMLElement {
338
357
  `astro-dev-toolbar-app-canvas[data-app-id="${id}"]`
339
358
  );
340
359
  }
360
+ getAppButtonById(id) {
361
+ return this.shadowRoot.querySelector(`[data-app-id="${id}"]`);
362
+ }
341
363
  async toggleAppStatus(app) {
342
364
  const activeApp = this.getActiveApp();
343
365
  if (activeApp) {
@@ -361,7 +383,7 @@ class AstroDevToolbar extends HTMLElement {
361
383
  return false;
362
384
  }
363
385
  app.active = newStatus ?? !app.active;
364
- const mainBarButton = this.shadowRoot.querySelector(`[data-app-id="${app.id}"]`);
386
+ const mainBarButton = this.getAppButtonById(app.id);
365
387
  const moreBarButton = this.getAppCanvasById("astro:more")?.shadowRoot?.querySelector(
366
388
  `[data-app-id="${app.id}"]`
367
389
  );
@@ -35,9 +35,12 @@ import { markHTMLString as markHTMLString2 } from "./escape.js";
35
35
  import { addAttribute as addAttribute2, Renderer as Renderer2 } from "./render/index.js";
36
36
  function mergeSlots(...slotted) {
37
37
  const slots = {};
38
- for (const slot of slotted) {
38
+ for (let slot of slotted) {
39
39
  if (!slot)
40
40
  continue;
41
+ if (Array.isArray(slot)) {
42
+ slot = mergeSlots(...slot);
43
+ }
41
44
  if (typeof slot === "object") {
42
45
  Object.assign(slots, slot);
43
46
  } else if (typeof slot === "function") {
@@ -1,12 +1,14 @@
1
1
  import { type ESBuildTransformResult } from 'vite';
2
2
  import { type CompileProps, type CompileResult } from '../core/compile/index.js';
3
3
  import type { Logger } from '../core/logger/core.js';
4
- interface CachedFullCompilation {
4
+ import type { CompileMetadata } from './types.js';
5
+ interface CompileAstroOption {
5
6
  compileProps: CompileProps;
7
+ astroFileToCompileMetadata: Map<string, CompileMetadata>;
6
8
  logger: Logger;
7
9
  }
8
- interface FullCompileResult extends Omit<CompileResult, 'map'> {
10
+ export interface CompileAstroResult extends Omit<CompileResult, 'map'> {
9
11
  map: ESBuildTransformResult['map'];
10
12
  }
11
- export declare function cachedFullCompilation({ compileProps, logger, }: CachedFullCompilation): Promise<FullCompileResult>;
13
+ export declare function compileAstro({ compileProps, astroFileToCompileMetadata, logger, }: CompileAstroOption): Promise<CompileAstroResult>;
12
14
  export {};
@@ -1,15 +1,16 @@
1
1
  import { transformWithEsbuild } from "vite";
2
- import { cachedCompilation } from "../core/compile/index.js";
2
+ import { compile } from "../core/compile/index.js";
3
3
  import { getFileInfo } from "../vite-plugin-utils/index.js";
4
4
  const FRONTMATTER_PARSE_REGEXP = /^\-\-\-(.*)^\-\-\-/ms;
5
- async function cachedFullCompilation({
5
+ async function compileAstro({
6
6
  compileProps,
7
+ astroFileToCompileMetadata,
7
8
  logger
8
9
  }) {
9
10
  let transformResult;
10
11
  let esbuildResult;
11
12
  try {
12
- transformResult = await cachedCompilation(compileProps);
13
+ transformResult = await compile(compileProps);
13
14
  esbuildResult = await transformWithEsbuild(transformResult.code, compileProps.filename, {
14
15
  loader: "ts",
15
16
  target: "esnext",
@@ -50,6 +51,11 @@ const $$url = ${JSON.stringify(
50
51
  i++;
51
52
  }
52
53
  }
54
+ astroFileToCompileMetadata.set(compileProps.filename, {
55
+ originalCode: compileProps.source,
56
+ css: transformResult.css,
57
+ scripts: transformResult.scripts
58
+ });
53
59
  return {
54
60
  ...transformResult,
55
61
  code: esbuildResult.code + SUFFIX,
@@ -86,5 +92,5 @@ async function enhanceCompileError({
86
92
  throw err;
87
93
  }
88
94
  export {
89
- cachedFullCompilation
95
+ compileAstro
90
96
  };
@@ -1,12 +1,11 @@
1
1
  import type { HmrContext } from 'vite';
2
- import type { AstroConfig } from '../@types/astro.js';
3
- import type { cachedCompilation } from '../core/compile/index.js';
4
2
  import type { Logger } from '../core/logger/core.js';
3
+ import type { CompileAstroResult } from './compile.js';
4
+ import type { CompileMetadata } from './types.js';
5
5
  export interface HandleHotUpdateOptions {
6
- config: AstroConfig;
7
6
  logger: Logger;
7
+ compile: (code: string, filename: string) => Promise<CompileAstroResult>;
8
8
  astroFileToCssAstroDeps: Map<string, Set<string>>;
9
- compile: () => ReturnType<typeof cachedCompilation>;
10
- source: string;
9
+ astroFileToCompileMetadata: Map<string, CompileMetadata>;
11
10
  }
12
- export declare function handleHotUpdate(ctx: HmrContext, { config, logger, astroFileToCssAstroDeps, compile, source }: HandleHotUpdateOptions): Promise<import("vite").ModuleNode[] | undefined>;
11
+ export declare function handleHotUpdate(ctx: HmrContext, { logger, compile, astroFileToCssAstroDeps, astroFileToCompileMetadata }: HandleHotUpdateOptions): Promise<import("vite").ModuleNode[] | undefined>;
@@ -1,22 +1,11 @@
1
1
  import path from "node:path";
2
2
  import { appendForwardSlash } from "@astrojs/internal-helpers/path";
3
- import { invalidateCompilation, isCached } from "../core/compile/index.js";
4
- async function handleHotUpdate(ctx, { config, logger, astroFileToCssAstroDeps, compile, source }) {
5
- let isStyleOnlyChange = false;
6
- if (ctx.file.endsWith(".astro") && isCached(config, ctx.file)) {
7
- const oldResult = await compile();
8
- if (oldResult.source === source)
9
- return [];
10
- invalidateCompilation(config, ctx.file);
11
- const newResult = await compile();
12
- if (isStyleOnlyChanged(oldResult, newResult)) {
13
- isStyleOnlyChange = true;
14
- }
15
- } else {
16
- invalidateCompilation(config, ctx.file);
17
- }
18
- if (isStyleOnlyChange) {
3
+ async function handleHotUpdate(ctx, { logger, compile, astroFileToCssAstroDeps, astroFileToCompileMetadata }) {
4
+ const oldCode = astroFileToCompileMetadata.get(ctx.file)?.originalCode;
5
+ const newCode = await ctx.read();
6
+ if (oldCode && isStyleOnlyChanged(oldCode, newCode)) {
19
7
  logger.debug("watch", "style-only change");
8
+ await compile(newCode, ctx.file);
20
9
  return ctx.modules.filter((mod) => mod.id?.includes("astro&type=style"));
21
10
  }
22
11
  for (const [astroFile, cssAstroDeps] of astroFileToCssAstroDeps) {
@@ -32,15 +21,29 @@ async function handleHotUpdate(ctx, { config, logger, astroFileToCssAstroDeps, c
32
21
  }
33
22
  }
34
23
  }
35
- function isStyleOnlyChanged(oldResult, newResult) {
36
- return normalizeCode(oldResult.code) === normalizeCode(newResult.code) && // If style tags are added/removed, we need to regenerate the main Astro file
37
- // so that its CSS imports are also added/removed
38
- oldResult.css.length === newResult.css.length && !isArrayEqual(oldResult.css, newResult.css);
39
- }
40
- const astroStyleImportRE = /import\s*"[^"]+astro&type=style[^"]+";/g;
41
- const sourceMappingUrlRE = /\/\/# sourceMappingURL=[^ ]+$/gm;
42
- function normalizeCode(code) {
43
- return code.replace(astroStyleImportRE, "").replace(sourceMappingUrlRE, "").trim();
24
+ const frontmatterRE = /^\-\-\-.*?^\-\-\-/ms;
25
+ const scriptRE = /<script(?:\s.*?)?>.*?<\/script>/gs;
26
+ const styleRE = /<style(?:\s.*?)?>.*?<\/style>/gs;
27
+ function isStyleOnlyChanged(oldCode, newCode) {
28
+ if (oldCode === newCode)
29
+ return false;
30
+ let oldFrontmatter = "";
31
+ let newFrontmatter = "";
32
+ oldCode = oldCode.replace(frontmatterRE, (m) => (oldFrontmatter = m, ""));
33
+ newCode = newCode.replace(frontmatterRE, (m) => (newFrontmatter = m, ""));
34
+ if (oldFrontmatter !== newFrontmatter)
35
+ return false;
36
+ const oldScripts = [];
37
+ const newScripts = [];
38
+ oldCode = oldCode.replace(scriptRE, (m) => (oldScripts.push(m), ""));
39
+ newCode = newCode.replace(scriptRE, (m) => (newScripts.push(m), ""));
40
+ if (!isArrayEqual(oldScripts, newScripts))
41
+ return false;
42
+ const oldStyles = [];
43
+ const newStyles = [];
44
+ oldCode.match(styleRE)?.forEach((m) => oldStyles.push(m));
45
+ newCode.match(styleRE)?.forEach((m) => newStyles.push(m));
46
+ return oldStyles.length === newStyles.length && !isArrayEqual(oldStyles, newStyles);
44
47
  }
45
48
  function isArrayEqual(a, b) {
46
49
  if (a.length !== b.length) {
@@ -1,34 +1,51 @@
1
1
  import { normalizePath } from "vite";
2
- import {
3
- cachedCompilation,
4
- getCachedCompileResult,
5
- invalidateCompilation
6
- } from "../core/compile/index.js";
7
- import { isRelativePath } from "../core/path.js";
8
2
  import { normalizeFilename } from "../vite-plugin-utils/index.js";
9
- import { cachedFullCompilation } from "./compile.js";
3
+ import { compileAstro } from "./compile.js";
10
4
  import { handleHotUpdate } from "./hmr.js";
11
5
  import { parseAstroRequest } from "./query.js";
12
6
  import { getAstroMetadata } from "./metadata.js";
7
+ const astroFileToCompileMetadataWeakMap = /* @__PURE__ */ new WeakMap();
13
8
  function astro({ settings, logger }) {
14
9
  const { config } = settings;
15
- let resolvedConfig;
16
10
  let server;
11
+ let compile;
17
12
  let astroFileToCssAstroDeps = /* @__PURE__ */ new Map();
13
+ let astroFileToCompileMetadata = /* @__PURE__ */ new Map();
18
14
  const srcRootWeb = config.srcDir.pathname.slice(config.root.pathname.length - 1);
19
15
  const isBrowserPath = (path) => path.startsWith(srcRootWeb) && srcRootWeb !== "/";
20
16
  const prePlugin = {
21
17
  name: "astro:build",
22
18
  enforce: "pre",
23
19
  // run transforms before other plugins can
24
- configResolved(_resolvedConfig) {
25
- resolvedConfig = _resolvedConfig;
20
+ configResolved(viteConfig) {
21
+ compile = (code, filename) => {
22
+ return compileAstro({
23
+ compileProps: {
24
+ astroConfig: config,
25
+ viteConfig,
26
+ preferences: settings.preferences,
27
+ filename,
28
+ source: code
29
+ },
30
+ astroFileToCompileMetadata,
31
+ logger
32
+ });
33
+ };
26
34
  },
27
35
  configureServer(_server) {
28
36
  server = _server;
37
+ server.watcher.on("unlink", (filename) => {
38
+ astroFileToCompileMetadata.delete(filename);
39
+ });
29
40
  },
30
41
  buildStart() {
31
42
  astroFileToCssAstroDeps = /* @__PURE__ */ new Map();
43
+ astroFileToCompileMetadata = /* @__PURE__ */ new Map();
44
+ if (astroFileToCompileMetadataWeakMap.has(config)) {
45
+ astroFileToCompileMetadata = astroFileToCompileMetadataWeakMap.get(config);
46
+ } else {
47
+ astroFileToCompileMetadataWeakMap.set(config, astroFileToCompileMetadata);
48
+ }
32
49
  },
33
50
  async load(id, opts) {
34
51
  const parsedId = parseAstroRequest(id);
@@ -37,33 +54,22 @@ function astro({ settings, logger }) {
37
54
  return null;
38
55
  }
39
56
  const filename = normalizePath(normalizeFilename(parsedId.filename, config.root));
40
- let compileResult = getCachedCompileResult(config, filename);
41
- if (!compileResult) {
42
- if (server) {
43
- await server.transformRequest("/@fs" + filename);
44
- compileResult = getCachedCompileResult(config, filename);
45
- }
46
- if (!compileResult) {
47
- throw new Error("No cached compile result found for " + id);
48
- }
57
+ const compileMetadata = astroFileToCompileMetadata.get(filename);
58
+ if (!compileMetadata) {
59
+ throw new Error(
60
+ `No cached compile metadata found for "${id}". The main Astro module "${filename}" should have compiled and filled the metadata first, before its virtual modules can be requested.`
61
+ );
49
62
  }
50
63
  switch (query.type) {
51
64
  case "style": {
52
65
  if (typeof query.index === "undefined") {
53
66
  throw new Error(`Requests for Astro CSS must include an index.`);
54
67
  }
55
- const code = compileResult.css[query.index];
68
+ const code = compileMetadata.css[query.index];
56
69
  if (!code) {
57
70
  throw new Error(`No Astro CSS at index ${query.index}`);
58
71
  }
59
- return {
60
- code,
61
- meta: {
62
- vite: {
63
- isSelfAccepting: true
64
- }
65
- }
66
- };
72
+ return { code };
67
73
  }
68
74
  case "script": {
69
75
  if (typeof query.index === "undefined") {
@@ -74,7 +80,7 @@ function astro({ settings, logger }) {
74
80
  code: `/* client hoisted script, empty in SSR: ${id} */`
75
81
  };
76
82
  }
77
- const hoistedScript = compileResult.scripts[query.index];
83
+ const hoistedScript = compileMetadata.scripts[query.index];
78
84
  if (!hoistedScript) {
79
85
  throw new Error(`No hoisted script at index ${query.index}`);
80
86
  }
@@ -122,18 +128,8 @@ File: ${id}`
122
128
  if (!id.endsWith(".astro") || parsedId.query.astro) {
123
129
  return;
124
130
  }
125
- if (isRelativePath(parsedId.filename)) {
126
- return;
127
- }
128
- const compileProps = {
129
- astroConfig: config,
130
- viteConfig: resolvedConfig,
131
- preferences: settings.preferences,
132
- filename: normalizePath(parsedId.filename),
133
- source
134
- };
135
- invalidateCompilation(config, compileProps.filename);
136
- const transformResult = await cachedFullCompilation({ compileProps, logger });
131
+ const filename = normalizePath(parsedId.filename);
132
+ const transformResult = await compile(source, filename);
137
133
  const astroDeps = /* @__PURE__ */ new Set();
138
134
  for (const dep of transformResult.cssDeps) {
139
135
  if (dep.endsWith(".astro")) {
@@ -143,7 +139,7 @@ File: ${id}`
143
139
  }
144
140
  astroFileToCssAstroDeps.set(id, astroDeps);
145
141
  if (server) {
146
- const mods = server.moduleGraph.getModulesByFile(compileProps.filename);
142
+ const mods = server.moduleGraph.getModulesByFile(filename);
147
143
  if (mods) {
148
144
  const seen = new Set(mods);
149
145
  for (const mod of mods) {
@@ -174,24 +170,14 @@ File: ${id}`
174
170
  }
175
171
  };
176
172
  },
177
- async handleHotUpdate(context) {
178
- if (context.server.config.isProduction)
173
+ async handleHotUpdate(ctx) {
174
+ if (!ctx.file.endsWith(".astro"))
179
175
  return;
180
- const filename = context.file;
181
- const source = await context.read();
182
- const compile = () => cachedCompilation({
183
- astroConfig: config,
184
- viteConfig: resolvedConfig,
185
- preferences: settings.preferences,
186
- filename,
187
- source
188
- });
189
- return handleHotUpdate(context, {
190
- config,
176
+ return handleHotUpdate(ctx, {
191
177
  logger,
192
- astroFileToCssAstroDeps,
193
178
  compile,
194
- source
179
+ astroFileToCssAstroDeps,
180
+ astroFileToCompileMetadata
195
181
  });
196
182
  }
197
183
  };
@@ -1,4 +1,4 @@
1
- import type { TransformResult } from '@astrojs/compiler';
1
+ import type { HoistedScript, TransformResult } from '@astrojs/compiler';
2
2
  import type { PropagationHint } from '../@types/astro.js';
3
3
  export interface PageOptions {
4
4
  prerender?: boolean;
@@ -13,3 +13,11 @@ export interface PluginMetadata {
13
13
  pageOptions: PageOptions;
14
14
  };
15
15
  }
16
+ export interface CompileMetadata {
17
+ /** Used for HMR to compare code changes */
18
+ originalCode: string;
19
+ /** For Astro CSS virtual module */
20
+ css: string[];
21
+ /** For Astro hoisted scripts virtual module */
22
+ scripts: HoistedScript[];
23
+ }
@@ -96,6 +96,7 @@ function createDevelopmentManifest(settings) {
96
96
  }
97
97
  return {
98
98
  trailingSlash: settings.config.trailingSlash,
99
+ buildFormat: settings.config.build.format,
99
100
  compressHTML: settings.config.compressHTML,
100
101
  assets: /* @__PURE__ */ new Set(),
101
102
  entryModules: {},
@@ -231,7 +231,12 @@ async function handleRoute({
231
231
  }
232
232
  const onRequest = middleware?.onRequest;
233
233
  if (config.i18n) {
234
- const i18Middleware = createI18nMiddleware(config.i18n, config.base, config.trailingSlash);
234
+ const i18Middleware = createI18nMiddleware(
235
+ config.i18n,
236
+ config.base,
237
+ config.trailingSlash,
238
+ config.build.format
239
+ );
235
240
  if (i18Middleware) {
236
241
  if (onRequest) {
237
242
  pipeline.setMiddlewareFunction(sequence(i18Middleware, onRequest));
@@ -1,3 +1,3 @@
1
1
  import type * as vite from 'vite';
2
2
  import type { AstroPluginOptions } from '../@types/astro.js';
3
- export default function astroDevToolbar({ settings }: AstroPluginOptions): vite.Plugin;
3
+ export default function astroDevToolbar({ settings, logger }: AstroPluginOptions): vite.Plugin;
@@ -1,6 +1,6 @@
1
1
  const VIRTUAL_MODULE_ID = "astro:dev-toolbar";
2
2
  const resolvedVirtualModuleId = "\0" + VIRTUAL_MODULE_ID;
3
- function astroDevToolbar({ settings }) {
3
+ function astroDevToolbar({ settings, logger }) {
4
4
  return {
5
5
  name: "astro:dev-toolbar",
6
6
  config() {
@@ -16,12 +16,49 @@ function astroDevToolbar({ settings }) {
16
16
  return resolvedVirtualModuleId;
17
17
  }
18
18
  },
19
+ configureServer(server) {
20
+ server.ws.on("astro:devtoolbar:error:load", (args) => {
21
+ logger.error(
22
+ "toolbar",
23
+ `Failed to load dev toolbar app from ${args.entrypoint}: ${args.error}`
24
+ );
25
+ });
26
+ server.ws.on("astro:devtoolbar:error:init", (args) => {
27
+ logger.error(
28
+ "toolbar",
29
+ `Failed to initialize dev toolbar app ${args.app.name} (${args.app.id}):
30
+ ${args.error}`
31
+ );
32
+ });
33
+ },
19
34
  async load(id) {
20
35
  if (id === resolvedVirtualModuleId) {
21
36
  return `
22
37
  export const loadDevToolbarApps = async () => {
23
- return [${settings.devToolbarApps.map((plugin) => `(await import(${JSON.stringify(plugin)})).default`).join(",")}];
38
+ return (await Promise.all([${settings.devToolbarApps.map((plugin) => `safeLoadPlugin(${JSON.stringify(plugin)})`).join(",")}])).filter(app => app);
24
39
  };
40
+
41
+ async function safeLoadPlugin(entrypoint) {
42
+ try {
43
+ const app = (await import(/* @vite-ignore */ entrypoint)).default;
44
+
45
+ if (typeof app !== 'object' || !app.id || !app.name) {
46
+ throw new Error("Apps must default export an object with an id, and a name.");
47
+ }
48
+
49
+ return app;
50
+ } catch (err) {
51
+ console.error(\`Failed to load dev toolbar app from \${entrypoint}: \${err.message}\`);
52
+
53
+ if (import.meta.hot) {
54
+ import.meta.hot.send('astro:devtoolbar:error:load', { entrypoint: entrypoint, error: err.message })
55
+ }
56
+
57
+ return undefined;
58
+ }
59
+
60
+ return undefined;
61
+ }
25
62
  `;
26
63
  }
27
64
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "4.2.1",
3
+ "version": "4.2.3",
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",
@@ -101,7 +101,7 @@
101
101
  "vendor"
102
102
  ],
103
103
  "dependencies": {
104
- "@astrojs/compiler": "^2.3.4",
104
+ "@astrojs/compiler": "^2.5.0",
105
105
  "@babel/core": "^7.23.3",
106
106
  "@babel/generator": "^7.23.3",
107
107
  "@babel/parser": "^7.23.3",
@@ -161,9 +161,9 @@
161
161
  "which-pm": "^2.1.1",
162
162
  "yargs-parser": "^21.1.1",
163
163
  "zod": "^3.22.4",
164
- "@astrojs/markdown-remark": "4.1.0",
165
- "@astrojs/telemetry": "3.0.4",
166
- "@astrojs/internal-helpers": "0.2.1"
164
+ "@astrojs/internal-helpers": "0.2.1",
165
+ "@astrojs/markdown-remark": "4.2.0",
166
+ "@astrojs/telemetry": "3.0.4"
167
167
  },
168
168
  "optionalDependencies": {
169
169
  "sharp": "^0.32.6"
@@ -1,6 +0,0 @@
1
- import type { AstroConfig } from '../../@types/astro.js';
2
- import { type CompileProps, type CompileResult } from './compile.js';
3
- export declare function isCached(config: AstroConfig, filename: string): boolean;
4
- export declare function getCachedCompileResult(config: AstroConfig, filename: string): CompileResult | null;
5
- export declare function invalidateCompilation(config: AstroConfig, filename: string): void;
6
- export declare function cachedCompilation(props: CompileProps): Promise<CompileResult>;
@@ -1,38 +0,0 @@
1
- import { compile } from "./compile.js";
2
- const configCache = /* @__PURE__ */ new WeakMap();
3
- function isCached(config, filename) {
4
- return configCache.has(config) && configCache.get(config).has(filename);
5
- }
6
- function getCachedCompileResult(config, filename) {
7
- if (!isCached(config, filename))
8
- return null;
9
- return configCache.get(config).get(filename);
10
- }
11
- function invalidateCompilation(config, filename) {
12
- if (configCache.has(config)) {
13
- const cache = configCache.get(config);
14
- cache.delete(filename);
15
- }
16
- }
17
- async function cachedCompilation(props) {
18
- const { astroConfig, filename } = props;
19
- let cache;
20
- if (!configCache.has(astroConfig)) {
21
- cache = /* @__PURE__ */ new Map();
22
- configCache.set(astroConfig, cache);
23
- } else {
24
- cache = configCache.get(astroConfig);
25
- }
26
- if (cache.has(filename)) {
27
- return cache.get(filename);
28
- }
29
- const compileResult = await compile(props);
30
- cache.set(filename, compileResult);
31
- return compileResult;
32
- }
33
- export {
34
- cachedCompilation,
35
- getCachedCompileResult,
36
- invalidateCompilation,
37
- isCached
38
- };