astro 7.2.9 → 7.2.10

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.
@@ -1,6 +1,6 @@
1
1
  class BuildTimeAstroVersionProvider {
2
2
  // Injected during the build through esbuild define
3
- version = "7.2.9";
3
+ version = "7.2.10";
4
4
  }
5
5
  export {
6
6
  BuildTimeAstroVersionProvider
@@ -196,7 +196,7 @@ ${contentConfig.error.message}`
196
196
  logger.info("Content config changed");
197
197
  shouldClear = true;
198
198
  }
199
- if (previousAstroVersion && previousAstroVersion !== "7.2.9") {
199
+ if (previousAstroVersion && previousAstroVersion !== "7.2.10") {
200
200
  logger.info("Astro version changed");
201
201
  shouldClear = true;
202
202
  }
@@ -204,8 +204,8 @@ ${contentConfig.error.message}`
204
204
  logger.info("Clearing content store");
205
205
  this.#store.clearAll();
206
206
  }
207
- if ("7.2.9") {
208
- this.#store.metaStore().set("astro-version", "7.2.9");
207
+ if ("7.2.10") {
208
+ this.#store.metaStore().set("astro-version", "7.2.10");
209
209
  }
210
210
  if (currentConfigDigest) {
211
211
  this.#store.metaStore().set("content-config-digest", currentConfigDigest);
@@ -8,8 +8,17 @@ export type DataStoreManifest = Record<string, string[]>;
8
8
  * (build/dev) and are never imported at runtime.
9
9
  */
10
10
  export interface DataStoreWriter {
11
- /** Serialize and persist the given collections. */
12
- write(collections: Map<string, Map<string, any>>): Promise<void>;
11
+ /**
12
+ * Serialize and persist the given collections.
13
+ * Resolves to `true` if the data on disk changed, or `false` if the write
14
+ * was skipped because the persisted data was already identical.
15
+ */
16
+ write(collections: Map<string, Map<string, any>>): Promise<boolean>;
17
+ /**
18
+ * The file whose write commits a store update: the store file itself, or
19
+ * the manifest for chunked stores.
20
+ */
21
+ readonly target: PathLike;
13
22
  }
14
23
  /**
15
24
  * Serialize collections to a deterministic devalue string.
@@ -35,15 +44,18 @@ export declare function chunkString(str: string, maxBytes: number): string[];
35
44
  * partial reads. If the file already contains identical data, the write is
36
45
  * skipped. Callers are responsible for serializing concurrent writes to the
37
46
  * same file.
47
+ *
48
+ * Returns `true` if the file was written, or `false` if the write was skipped.
38
49
  */
39
- export declare function writeFileAtomic(file: PathLike, data: string): Promise<void>;
50
+ export declare function writeFileAtomic(file: PathLike, data: string): Promise<boolean>;
40
51
  /**
41
52
  * A {@link DataStoreWriter} that serializes the whole store to a single file.
42
53
  */
43
54
  export declare class FileWriter implements DataStoreWriter {
44
55
  #private;
45
56
  constructor(file: PathLike);
46
- write(collections: Map<string, Map<string, any>>): Promise<void>;
57
+ get target(): PathLike;
58
+ write(collections: Map<string, Map<string, any>>): Promise<boolean>;
47
59
  }
48
60
  /**
49
61
  * A {@link DataStoreWriter} that splits the store across many content-addressed
@@ -60,5 +72,6 @@ export declare class FileWriter implements DataStoreWriter {
60
72
  export declare class ChunkedWriter implements DataStoreWriter {
61
73
  #private;
62
74
  constructor(dir: URL, chunkSize: number);
63
- write(collections: Map<string, Map<string, any>>): Promise<void>;
75
+ get target(): PathLike;
76
+ write(collections: Map<string, Map<string, any>>): Promise<boolean>;
64
77
  }
@@ -47,18 +47,22 @@ async function writeFileAtomic(file, data) {
47
47
  const tempFile = file instanceof URL ? new URL(`${file.href}.tmp`) : `${file}.tmp`;
48
48
  const oldData = await fs.readFile(file, "utf-8").catch(() => "");
49
49
  if (oldData === data) {
50
- return;
50
+ return false;
51
51
  }
52
52
  await fs.writeFile(tempFile, data);
53
53
  await fs.rename(tempFile, file);
54
+ return true;
54
55
  }
55
56
  class FileWriter {
56
57
  #file;
57
58
  constructor(file) {
58
59
  this.#file = file;
59
60
  }
61
+ get target() {
62
+ return this.#file;
63
+ }
60
64
  async write(collections) {
61
- await writeFileAtomic(this.#file, serializeDataStore(collections));
65
+ return await writeFileAtomic(this.#file, serializeDataStore(collections));
62
66
  }
63
67
  }
64
68
  class ChunkedWriter {
@@ -72,6 +76,9 @@ class ChunkedWriter {
72
76
  this.#manifestFile = new URL(`./${DATA_STORE_MANIFEST_FILE}`, dir);
73
77
  this.#chunkSize = chunkSize;
74
78
  }
79
+ get target() {
80
+ return this.#manifestFile;
81
+ }
75
82
  async write(collections) {
76
83
  if (!this.#hasher) {
77
84
  this.#hasher = await xxhash();
@@ -81,9 +88,10 @@ class ChunkedWriter {
81
88
  for (const [collectionName, entries] of sortCollections(collections)) {
82
89
  manifest[collectionName] = await this.#writeCollection(entries);
83
90
  }
84
- await writeFileAtomic(this.#manifestFile, JSON.stringify(manifest));
91
+ const didWrite = await writeFileAtomic(this.#manifestFile, JSON.stringify(manifest));
85
92
  this.#writtenFiles.add(DATA_STORE_MANIFEST_FILE);
86
93
  emptyDir(this.#dir, this.#writtenFiles);
94
+ return didWrite;
87
95
  }
88
96
  async #writeCollection(entries) {
89
97
  const parts = [];
@@ -3,4 +3,4 @@ export { createContentTypesGenerator } from './types-generator.js';
3
3
  export { getContentPaths } from './utils.js';
4
4
  export { astroContentAssetPropagationPlugin } from './vite-plugin-content-assets.js';
5
5
  export { astroContentImportPlugin } from './vite-plugin-content-imports.js';
6
- export { astroContentVirtualModPlugin } from './vite-plugin-content-virtual-mod.js';
6
+ export { astroContentVirtualModPlugin, attachDataStoreInvalidation, } from './vite-plugin-content-virtual-mod.js';
@@ -3,12 +3,16 @@ import { createContentTypesGenerator } from "./types-generator.js";
3
3
  import { getContentPaths } from "./utils.js";
4
4
  import { astroContentAssetPropagationPlugin } from "./vite-plugin-content-assets.js";
5
5
  import { astroContentImportPlugin } from "./vite-plugin-content-imports.js";
6
- import { astroContentVirtualModPlugin } from "./vite-plugin-content-virtual-mod.js";
6
+ import {
7
+ astroContentVirtualModPlugin,
8
+ attachDataStoreInvalidation
9
+ } from "./vite-plugin-content-virtual-mod.js";
7
10
  export {
8
11
  astroContentAssetPropagationPlugin,
9
12
  astroContentImportPlugin,
10
13
  astroContentVirtualModPlugin,
11
14
  attachContentServerListeners,
15
+ attachDataStoreInvalidation,
12
16
  createContentTypesGenerator,
13
17
  getContentPaths
14
18
  };
@@ -6,6 +6,17 @@ import { type DataEntry, ImmutableDataStore } from './data-store.js';
6
6
  */
7
7
  export declare class MutableDataStore extends ImmutableDataStore {
8
8
  #private;
9
+ /**
10
+ * Registers a listener called with the file path whenever this store writes a
11
+ * file to disk (the data store itself, or the asset/module import files).
12
+ * Writes that are skipped because the data on disk is already identical do
13
+ * not notify. The dev server uses this to invalidate the content virtual
14
+ * modules deterministically, instead of relying on the file watcher to
15
+ * observe the write — on some platforms (notably Windows) the watcher can
16
+ * miss the atomic rename that commits it.
17
+ * Returns a function that removes the listener.
18
+ */
19
+ onFileWritten(listener: (path: string) => void): () => void;
9
20
  set(collectionName: string, key: string, value: unknown): void;
10
21
  delete(collectionName: string, key: string): void;
11
22
  clear(collectionName: string): void;
@@ -30,6 +30,32 @@ class MutableDataStore extends ImmutableDataStore {
30
30
  #moduleImports = /* @__PURE__ */ new Map();
31
31
  #writeInProgress = false;
32
32
  #writeQueued = false;
33
+ #fileWrittenListeners = /* @__PURE__ */ new Set();
34
+ /**
35
+ * Registers a listener called with the file path whenever this store writes a
36
+ * file to disk (the data store itself, or the asset/module import files).
37
+ * Writes that are skipped because the data on disk is already identical do
38
+ * not notify. The dev server uses this to invalidate the content virtual
39
+ * modules deterministically, instead of relying on the file watcher to
40
+ * observe the write — on some platforms (notably Windows) the watcher can
41
+ * miss the atomic rename that commits it.
42
+ * Returns a function that removes the listener.
43
+ */
44
+ onFileWritten(listener) {
45
+ this.#fileWrittenListeners.add(listener);
46
+ return () => {
47
+ this.#fileWrittenListeners.delete(listener);
48
+ };
49
+ }
50
+ #notifyFileWritten(path) {
51
+ if (this.#fileWrittenListeners.size === 0) {
52
+ return;
53
+ }
54
+ const normalized = path instanceof URL ? fileURLToPath(path) : path.toString();
55
+ for (const listener of this.#fileWrittenListeners) {
56
+ listener(normalized);
57
+ }
58
+ }
33
59
  set(collectionName, key, value) {
34
60
  const collection = this._collections.get(collectionName) ?? /* @__PURE__ */ new Map();
35
61
  collection.set(String(key), value);
@@ -275,6 +301,7 @@ ${lines.join(",\n")}]);
275
301
  }
276
302
  await fs.writeFile(tempFile, data);
277
303
  await fs.rename(tempFile, filePath);
304
+ this.#notifyFileWritten(filePath);
278
305
  } finally {
279
306
  this.#writing.delete(fileKey);
280
307
  if (this.#pending.has(fileKey)) {
@@ -415,7 +442,10 @@ ${lines.join(",\n")}]);
415
442
  try {
416
443
  this.#dirty = false;
417
444
  this.#writeInProgress = true;
418
- await this.#writer.write(this._collections);
445
+ const didWrite = await this.#writer.write(this._collections);
446
+ if (didWrite) {
447
+ this.#notifyFileWritten(this.#writer.target);
448
+ }
419
449
  } catch (err) {
420
450
  throw new AstroError(AstroErrorData.UnknownFilesystemError, { cause: err });
421
451
  } finally {
@@ -349,7 +349,9 @@ async function updateImageReferencesInBody(html, fileName) {
349
349
  return Object.entries({
350
350
  ...attributes,
351
351
  src: image.src,
352
- srcset: image.srcSet.attribute,
352
+ // An empty `srcset` is invalid HTML, so only emit it when there are
353
+ // actual candidates. This matches `vite-plugin-markdown/images.ts`.
354
+ ...image.srcSet.values.length > 0 ? { srcset: image.srcSet.attribute } : {},
353
355
  // This attribute is used by the toolbar audit
354
356
  ...import.meta.env.DEV ? { "data-image-component": "true" } : {}
355
357
  }).filter(([, value]) => value != null).map(([key, value]) => value === "" ? `${key}=""` : `${key}="${escape(String(value))}"`).join(" ");
@@ -1,9 +1,19 @@
1
1
  import nodeFs from 'node:fs';
2
- import { type Plugin } from 'vite';
2
+ import { type Plugin, type ViteDevServer } from 'vite';
3
3
  import type { AstroSettings } from '../types/astro.js';
4
+ import type { MutableDataStore } from './mutable-data-store.js';
4
5
  interface AstroContentVirtualModPluginParams {
5
6
  settings: AstroSettings;
6
7
  fs: typeof nodeFs;
7
8
  }
9
+ /**
10
+ * Invalidates the content virtual modules directly whenever the given store
11
+ * writes to disk. The watcher listeners in `configureServer` cover writes from
12
+ * other processes, but the watcher can miss the atomic rename that commits a
13
+ * write on some platforms (notably Windows, see #17335), leaving dev serving
14
+ * stale content until a restart. Subscribing to the store's own write
15
+ * notifications makes invalidation of this process's writes deterministic.
16
+ */
17
+ export declare function attachDataStoreInvalidation(store: MutableDataStore, server: ViteDevServer, settings: AstroSettings): void;
8
18
  export declare function astroContentVirtualModPlugin({ settings, fs, }: AstroContentVirtualModPluginParams): Plugin;
9
19
  export {};
@@ -70,6 +70,35 @@ function invalidateDataStore(viteServer, { notifyClient = true } = {}) {
70
70
  });
71
71
  }
72
72
  }
73
+ const directInvalidations = /* @__PURE__ */ new Map();
74
+ const DIRECT_INVALIDATION_ECHO_MS = 1e3;
75
+ function markDirectInvalidation(path) {
76
+ directInvalidations.set(path, Date.now());
77
+ }
78
+ function isDirectInvalidationEcho(path) {
79
+ const time = directInvalidations.get(path);
80
+ return time !== void 0 && Date.now() - time < DIRECT_INVALIDATION_ECHO_MS;
81
+ }
82
+ function getDevDataStoreFile(settings) {
83
+ if (getDataStoreChunkSize(settings) !== void 0) {
84
+ return new URL(DATA_STORE_MANIFEST_FILE, getDataStoreDir(settings, true));
85
+ }
86
+ return getDataStoreFile(settings, true);
87
+ }
88
+ function attachDataStoreInvalidation(store, server, settings) {
89
+ const dataStorePath = fileURLToPath(getDevDataStoreFile(settings));
90
+ const assetImportsPath = fileURLToPath(new URL(ASSET_IMPORTS_FILE, settings.dotAstroDir));
91
+ store.onFileWritten((path) => {
92
+ if (path === dataStorePath) {
93
+ markDirectInvalidation(dataStorePath);
94
+ invalidateDataStore(server);
95
+ invalidateAssetImports(server, assetImportsPath);
96
+ } else if (path === assetImportsPath) {
97
+ markDirectInvalidation(assetImportsPath);
98
+ invalidateAssetImports(server, assetImportsPath);
99
+ }
100
+ });
101
+ }
73
102
  function astroContentVirtualModPlugin({
74
103
  settings,
75
104
  fs
@@ -270,16 +299,19 @@ function astroContentVirtualModPlugin({
270
299
  const dataStorePath = fileURLToPath(dataStoreFile);
271
300
  const assetImportsPath = fileURLToPath(new URL(ASSET_IMPORTS_FILE, settings.dotAstroDir));
272
301
  server.watcher.on("add", (addedPath) => {
273
- if (addedPath === dataStorePath) {
302
+ if (addedPath === dataStorePath && !isDirectInvalidationEcho(dataStorePath)) {
274
303
  invalidateDataStore(server);
275
304
  invalidateAssetImports(server, assetImportsPath);
276
305
  }
277
306
  });
278
307
  server.watcher.on("change", (changedPath) => {
279
308
  if (changedPath === dataStorePath) {
309
+ if (isDirectInvalidationEcho(dataStorePath)) {
310
+ return;
311
+ }
280
312
  invalidateDataStore(server);
281
313
  invalidateAssetImports(server, assetImportsPath);
282
- } else if (changedPath === assetImportsPath) {
314
+ } else if (changedPath === assetImportsPath && !isDirectInvalidationEcho(assetImportsPath)) {
283
315
  invalidateAssetImports(server, assetImportsPath);
284
316
  }
285
317
  });
@@ -314,5 +346,6 @@ async function generateContentEntryFile({
314
346
  return virtualModContents;
315
347
  }
316
348
  export {
317
- astroContentVirtualModPlugin
349
+ astroContentVirtualModPlugin,
350
+ attachDataStoreInvalidation
318
351
  };
@@ -11,7 +11,7 @@ import { updateRouteTable } from "../../../routing/route-table.js";
11
11
  import { DevFacadeApp } from "../../dev-facade.js";
12
12
  let hmrWired = false;
13
13
  const createApp = ({ streaming } = {}) => {
14
- setLogger(manifest, createConsoleLogger(manifest.logLevel));
14
+ setLogger(manifest, createConsoleLogger({ level: manifest.logLevel }));
15
15
  setEnvironment(manifest, createNonRunnableEnvironment());
16
16
  const app = new DevFacadeApp(manifest, streaming);
17
17
  app.setFetchHandler(fetchable);
@@ -22,7 +22,7 @@ const createApp = ({ streaming } = {}) => {
22
22
  const { routes: newRoutes } = await import("virtual:astro:routes");
23
23
  updateRouteTable(
24
24
  manifest,
25
- newRoutes.map((r) => r.routeData)
25
+ newRoutes.map((route) => route.routeData)
26
26
  );
27
27
  } catch (e) {
28
28
  getLogger(manifest).error("router", `Failed to update routes via HMR:
@@ -26,7 +26,6 @@ import { getRedirectLocationOrThrow } from "../redirects/index.js";
26
26
  import { createRequest } from "../request.js";
27
27
  import { redirectTemplate } from "../routing/3xx.js";
28
28
  import { routeIsRedirect } from "../routing/helpers.js";
29
- import { matchRoute } from "../routing/match.js";
30
29
  import { getOutputFilename } from "../output-filename.js";
31
30
  import { getOutFile, getOutFolder } from "./common.js";
32
31
  import { createDefaultPrerenderer } from "./default-prerenderer.js";
@@ -102,7 +101,7 @@ ${colors.bgGreen(colors.black(` ${verb} static routes `))}`);
102
101
  const pathsWithRoutes = await prerenderer.getStaticPaths();
103
102
  const hasI18nDomains = ssr && options.settings.config.i18n?.domains && Object.keys(options.settings.config.i18n.domains).length > 0;
104
103
  const { config } = options.settings;
105
- const builtPaths = /* @__PURE__ */ new Set();
104
+ const builtPaths = /* @__PURE__ */ new Map();
106
105
  const filteredPaths = [];
107
106
  const fallbackPaths = [];
108
107
  for (const pathWithRoute of pathsWithRoutes) {
@@ -115,33 +114,28 @@ ${colors.bgGreen(colors.black(` ${verb} static routes `))}`);
115
114
  }
116
115
  const normalized = removeTrailingForwardSlash(pathname);
117
116
  if (!builtPaths.has(normalized)) {
118
- builtPaths.add(normalized);
117
+ builtPaths.set(normalized, route);
119
118
  } else {
120
- const matchedRoute = matchRoute(decodeURI(pathname), options.routesList);
121
- if (!matchedRoute) {
122
- continue;
123
- }
124
- if (matchedRoute !== route) {
125
- if (config.prerenderConflictBehavior === "error") {
126
- throw new AstroError({
127
- ...AstroErrorData.PrerenderRouteConflict,
128
- message: AstroErrorData.PrerenderRouteConflict.message(
129
- matchedRoute.route,
130
- route.route,
131
- normalized
132
- ),
133
- hint: AstroErrorData.PrerenderRouteConflict.hint(matchedRoute.route, route.route)
134
- });
135
- } else if (config.prerenderConflictBehavior === "warn") {
136
- const msg = AstroErrorData.PrerenderRouteConflict.message(
137
- matchedRoute.route,
119
+ const winningRoute = builtPaths.get(normalized);
120
+ if (config.prerenderConflictBehavior === "error") {
121
+ throw new AstroError({
122
+ ...AstroErrorData.PrerenderRouteConflict,
123
+ message: AstroErrorData.PrerenderRouteConflict.message(
124
+ winningRoute.route,
138
125
  route.route,
139
126
  normalized
140
- );
141
- logger.warn("build", msg);
142
- }
143
- continue;
127
+ ),
128
+ hint: AstroErrorData.PrerenderRouteConflict.hint(winningRoute.route, route.route)
129
+ });
130
+ } else if (config.prerenderConflictBehavior === "warn") {
131
+ const msg = AstroErrorData.PrerenderRouteConflict.message(
132
+ winningRoute.route,
133
+ route.route,
134
+ normalized
135
+ );
136
+ logger.warn("build", msg);
144
137
  }
138
+ continue;
145
139
  }
146
140
  const paths = route.type === "fallback" ? fallbackPaths : filteredPaths;
147
141
  paths.push(pathWithRoute);
@@ -31,7 +31,7 @@ import { makePageDataKey } from "./util.js";
31
31
  import { cacheConfigToManifest } from "../../cache/utils.js";
32
32
  import { sessionConfigToManifest } from "../../session/utils.js";
33
33
  const MANIFEST_REPLACE = "@@ASTRO_MANIFEST_REPLACE@@";
34
- const replaceExp = new RegExp(`['"]${MANIFEST_REPLACE}['"]`, "g");
34
+ const replaceExp = new RegExp(`['"\`]${MANIFEST_REPLACE}['"\`]`, "g");
35
35
  async function manifestBuildPostHook(options, internals, {
36
36
  chunks,
37
37
  mutate
@@ -75,6 +75,9 @@ class AstroCache {
75
75
  for (const [key, value] of headers) {
76
76
  response.headers.set(key, value);
77
77
  }
78
+ if (!response.headers.has("Cache-Control") && !response.headers.has("Expires") && (response.headers.has("Last-Modified") || response.headers.has("ETag"))) {
79
+ response.headers.set("Cache-Control", "no-cache");
80
+ }
78
81
  }
79
82
  /** @internal */
80
83
  get [IS_ACTIVE]() {
@@ -1,4 +1,4 @@
1
- const ASTRO_VERSION = "7.2.9";
1
+ const ASTRO_VERSION = "7.2.10";
2
2
  const ASTRO_GENERATOR = `Astro v${ASTRO_VERSION}`;
3
3
  const ASTRO_ERROR_HEADER = "X-Astro-Error";
4
4
  const DEFAULT_404_COMPONENT = "astro-default-404.astro";
@@ -4,7 +4,7 @@ import colors from "piccolore";
4
4
  import { gt, major, minor, patch } from "semver";
5
5
  import { getDataStoreChunkSize, getDataStoreDir, getDataStoreFile } from "../../content/paths.js";
6
6
  import { globalContentLayer } from "../../content/instance.js";
7
- import { attachContentServerListeners } from "../../content/index.js";
7
+ import { attachContentServerListeners, attachDataStoreInvalidation } from "../../content/index.js";
8
8
  import { MutableDataStore } from "../../content/mutable-data-store.js";
9
9
  import { globalContentConfigObserver } from "../../content/utils.js";
10
10
  import { telemetry } from "../../events/index.js";
@@ -26,7 +26,7 @@ async function dev(inlineConfig) {
26
26
  await telemetry.record([]);
27
27
  const restart = await createContainerWithAutomaticRestart({ inlineConfig, fs });
28
28
  const logger = restart.container.logger;
29
- const currentVersion = "7.2.9";
29
+ const currentVersion = "7.2.10";
30
30
  const isPrerelease = currentVersion.includes("-");
31
31
  if (!isPrerelease) {
32
32
  try {
@@ -69,6 +69,8 @@ async function dev(inlineConfig) {
69
69
  }
70
70
  if (!store) {
71
71
  logger.error("content", "Failed to create data store");
72
+ } else {
73
+ attachDataStoreInvalidation(store, restart.container.viteServer, restart.container.settings);
72
74
  }
73
75
  await attachContentServerListeners(restart.container);
74
76
  const config = globalContentConfigObserver.get();
@@ -270,7 +270,7 @@ function printHelp({
270
270
  message.push(
271
271
  linebreak(),
272
272
  ` ${bgGreen(black(` ${commandName} `))} ${green(
273
- `v${"7.2.9"}`
273
+ `v${"7.2.10"}`
274
274
  )} ${headline}`
275
275
  );
276
276
  }
@@ -5,8 +5,8 @@ const RESOLVED_SERVER_ISLAND_MANIFEST = "\0" + SERVER_ISLAND_MANIFEST;
5
5
  const serverIslandPlaceholderMap = "'$$server-islands-map$$'";
6
6
  const serverIslandPlaceholderNameMap = "'$$server-islands-name-map$$'";
7
7
  const SERVER_ISLAND_MAP_MARKER = "$$server-islands-map$$";
8
- const serverIslandMapReplaceExp = /['"]\$\$server-islands-map\$\$['"]/g;
9
- const serverIslandNameMapReplaceExp = /['"]\$\$server-islands-name-map\$\$['"]/g;
8
+ const serverIslandMapReplaceExp = /['"`]\$\$server-islands-map\$\$['"`]/g;
9
+ const serverIslandNameMapReplaceExp = /['"`]\$\$server-islands-name-map\$\$['"`]/g;
10
10
  function vitePluginServerIslands({
11
11
  settings,
12
12
  serverIslandsState
@@ -23,15 +23,6 @@ export declare function wrapId(id: string): string;
23
23
  export declare function resolvePages(config: AstroConfig): URL;
24
24
  export declare function isPage(file: URL, settings: AstroSettings): boolean;
25
25
  export declare function isEndpoint(file: URL, settings: AstroSettings): boolean;
26
- export declare function resolveJsToTs(filePath: string): string;
27
- /**
28
- * Resolve a path that doesn't name a file on disk (e.g. produced by an
29
- * extensionless import like `import { Counter } from './Counter'`) to the file
30
- * Vite would load, by probing Vite's default extension order and directory
31
- * `index` files. Returns the path unchanged when it already exists as a file
32
- * or when no candidate is found.
33
- */
34
- export declare function resolveExtensionlessPath(filePath: string): string;
35
26
  /**
36
27
  * Set a default NODE_ENV so Vite doesn't set an incorrect default when loading the Astro config
37
28
  */
package/dist/core/util.js CHANGED
@@ -1,4 +1,3 @@
1
- import fs from "node:fs";
2
1
  import { fileURLToPath } from "node:url";
3
2
  import { hasSpecialQueries } from "../vite-plugin-utils/index.js";
4
3
  import { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from "./constants.js";
@@ -85,37 +84,6 @@ function isEndpoint(file, settings) {
85
84
  if (!isPublicRoute(file, settings.config)) return false;
86
85
  return !endsWithPageExt(file, settings) && !file.toString().includes("?astro");
87
86
  }
88
- function resolveJsToTs(filePath) {
89
- if (filePath.endsWith(".jsx") && !fs.existsSync(filePath)) {
90
- const tryPath = filePath.slice(0, -4) + ".tsx";
91
- if (fs.existsSync(tryPath)) {
92
- return tryPath;
93
- }
94
- }
95
- return filePath;
96
- }
97
- const VITE_DEFAULT_RESOLVE_EXTENSIONS = [".mjs", ".js", ".mts", ".ts", ".jsx", ".tsx", ".json"];
98
- function resolveExtensionlessPath(filePath) {
99
- const stat = fs.statSync(filePath, { throwIfNoEntry: false });
100
- if (stat?.isFile()) {
101
- return filePath;
102
- }
103
- for (const ext of VITE_DEFAULT_RESOLVE_EXTENSIONS) {
104
- const tryPath = filePath + ext;
105
- if (fs.existsSync(tryPath)) {
106
- return tryPath;
107
- }
108
- }
109
- if (stat?.isDirectory()) {
110
- for (const ext of VITE_DEFAULT_RESOLVE_EXTENSIONS) {
111
- const tryPath = `${filePath}/index${ext}`;
112
- if (fs.existsSync(tryPath)) {
113
- return tryPath;
114
- }
115
- }
116
- }
117
- return filePath;
118
- }
119
87
  function ensureProcessNodeEnv(defaultNodeEnv) {
120
88
  if (!process.env.NODE_ENV) {
121
89
  process.env.NODE_ENV = defaultNodeEnv;
@@ -128,8 +96,6 @@ export {
128
96
  isMarkdownFile,
129
97
  isPage,
130
98
  parseNpmName,
131
- resolveExtensionlessPath,
132
- resolveJsToTs,
133
99
  resolvePages,
134
100
  unwrapId,
135
101
  viteID,
@@ -1,21 +1,9 @@
1
1
  import type { ModuleLoader } from './module-loader/index.js';
2
+ export { resolvePath } from '@astrojs/internal-helpers/mdx';
2
3
  /**
3
4
  * Re-implementation of Vite's normalizePath that can be used without Vite
4
5
  */
5
6
  export declare function normalizePath(id: string): string;
6
- /**
7
- * Resolve island component specifiers to stable paths for hydration metadata.
8
- *
9
- * Examples:
10
- * - `./components/Button.jsx` from `/app/src/pages/index.astro`
11
- * -> `/app/src/pages/components/Button.tsx` (when `.tsx` exists)
12
- * - `../components/Counter` from `/app/src/pages/index.astro`
13
- * -> `/app/src/components/Counter.tsx` (extensionless imports probe Vite's
14
- * default extension order, then directory `index` files)
15
- * - `#components/react/Counter.tsx`
16
- * -> `/app/src/components/react/Counter.tsx` via package `imports`
17
- */
18
- export declare function resolvePath(specifier: string, importer: string): string;
19
7
  export declare function rootRelativePath(root: URL, idOrUrl: URL | string, shouldPrependForwardSlash?: boolean): string;
20
8
  /**
21
9
  * Simulate Vite's resolve and import analysis so we can import the id as an URL
@@ -1,42 +1,12 @@
1
- import { createRequire } from "node:module";
2
1
  import path from "node:path";
3
- import { fileURLToPath, pathToFileURL } from "node:url";
2
+ import { fileURLToPath } from "node:url";
4
3
  import { prependForwardSlash, slash } from "../core/path.js";
5
- import {
6
- resolveExtensionlessPath,
7
- resolveJsToTs,
8
- unwrapId,
9
- VALID_ID_PREFIX,
10
- viteID
11
- } from "./util.js";
4
+ import { unwrapId, VALID_ID_PREFIX, viteID } from "./util.js";
5
+ import { resolvePath } from "@astrojs/internal-helpers/mdx";
12
6
  const isWindows = typeof process !== "undefined" && process.platform === "win32";
13
7
  function normalizePath(id) {
14
8
  return path.posix.normalize(isWindows ? slash(id) : id);
15
9
  }
16
- function resolvePath(specifier, importer) {
17
- if (specifier.startsWith(".")) {
18
- const absoluteSpecifier = path.resolve(path.dirname(importer), specifier);
19
- return resolveExtensionlessPath(resolveJsToTs(normalizePath(absoluteSpecifier)));
20
- } else if (specifier.startsWith("#")) {
21
- try {
22
- const resolved = createRequire(pathToFileURL(importer)).resolve(specifier);
23
- return resolveJsToTs(normalizePath(resolved));
24
- } catch {
25
- try {
26
- const importerURL = pathToFileURL(importer).toString();
27
- const resolved = import.meta.resolve(specifier, importerURL);
28
- const resolvedUrl = new URL(resolved);
29
- if (resolvedUrl.protocol === "file:") {
30
- return resolveJsToTs(normalizePath(fileURLToPath(resolvedUrl)));
31
- }
32
- } catch {
33
- }
34
- }
35
- return specifier;
36
- } else {
37
- return specifier;
38
- }
39
- }
40
10
  function rootRelativePath(root, idOrUrl, shouldPrependForwardSlash = true) {
41
11
  let id;
42
12
  if (typeof idOrUrl !== "string") {
@@ -47,7 +47,7 @@ async function createAstroServerApp(controller, settings, loader, logger) {
47
47
  const { routes: newRoutes } = await import("virtual:astro:routes");
48
48
  updateRouteTable(
49
49
  manifest,
50
- newRoutes.map((r) => r.routeData)
50
+ newRoutes.map((route) => route.routeData)
51
51
  );
52
52
  actualLogger.debug("router", "Routes updated via HMR");
53
53
  } catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "7.2.9",
3
+ "version": "7.2.10",
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",
@@ -153,15 +153,15 @@
153
153
  "xxhash-wasm": "^1.1.0",
154
154
  "yargs-parser": "^22.0.0",
155
155
  "zod": "^4.3.6",
156
- "@astrojs/markdown-satteri": "0.3.8",
157
- "@astrojs/internal-helpers": "0.10.4",
156
+ "@astrojs/internal-helpers": "0.11.0",
157
+ "@astrojs/markdown-satteri": "0.4.0",
158
158
  "@astrojs/telemetry": "3.3.3"
159
159
  },
160
160
  "optionalDependencies": {
161
161
  "sharp": "^0.35.4"
162
162
  },
163
163
  "peerDependencies": {
164
- "@astrojs/markdown-remark": "7.2.4"
164
+ "@astrojs/markdown-remark": "^7.3.0"
165
165
  },
166
166
  "peerDependenciesMeta": {
167
167
  "@astrojs/markdown-remark": {
@@ -193,8 +193,8 @@
193
193
  "typescript": "^6.0.3",
194
194
  "undici": "^7.22.0",
195
195
  "vitest": "^4.1.0",
196
- "@astrojs/markdown-remark": "7.2.4",
197
196
  "@astrojs/check": "0.9.10",
197
+ "@astrojs/markdown-remark": "7.3.0",
198
198
  "astro-scripts": "0.0.14"
199
199
  },
200
200
  "engines": {