astro 5.0.5 → 5.0.7

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 (36) hide show
  1. package/astro-jsx.d.ts +1 -0
  2. package/dist/actions/plugins.js +7 -0
  3. package/dist/actions/runtime/virtual/server.js +9 -1
  4. package/dist/actions/runtime/virtual/shared.d.ts +2 -0
  5. package/dist/actions/runtime/virtual/shared.js +3 -0
  6. package/dist/assets/endpoint/config.d.ts +0 -1
  7. package/dist/assets/endpoint/config.js +0 -6
  8. package/dist/cli/check/index.js +1 -5
  9. package/dist/content/content-layer.js +3 -3
  10. package/dist/content/mutable-data-store.js +30 -5
  11. package/dist/core/app/index.js +5 -4
  12. package/dist/core/build/index.js +3 -8
  13. package/dist/core/build/page-data.js +4 -0
  14. package/dist/core/build/plugins/plugin-manifest.js +13 -1
  15. package/dist/core/build/static-build.d.ts +1 -2
  16. package/dist/core/build/static-build.js +14 -12
  17. package/dist/core/constants.js +1 -1
  18. package/dist/core/dev/container.js +1 -5
  19. package/dist/core/dev/dev.js +1 -1
  20. package/dist/core/messages.js +2 -2
  21. package/dist/core/render/params-and-props.js +2 -1
  22. package/dist/core/routing/default.d.ts +2 -2
  23. package/dist/core/routing/default.js +5 -14
  24. package/dist/core/routing/manifest/create.js +10 -2
  25. package/dist/core/server-islands/endpoint.d.ts +1 -1
  26. package/dist/core/server-islands/endpoint.js +3 -6
  27. package/dist/i18n/index.d.ts +1 -1
  28. package/dist/i18n/index.js +4 -2
  29. package/dist/runtime/server/render/util.js +1 -1
  30. package/dist/virtual-modules/i18n.js +2 -2
  31. package/dist/vite-plugin-astro-server/plugin.js +2 -7
  32. package/package.json +1 -1
  33. package/templates/actions.mjs +15 -2
  34. package/templates/env.mjs +1 -0
  35. package/dist/core/routing/dev-default.d.ts +0 -3
  36. package/dist/core/routing/dev-default.js +0 -10
package/astro-jsx.d.ts CHANGED
@@ -679,6 +679,7 @@ declare namespace astroHTML.JSX {
679
679
 
680
680
  interface DialogHTMLAttributes extends HTMLAttributes {
681
681
  open?: boolean | string | undefined | null;
682
+ closedby?: 'none' | 'closerequest' | 'any' | undefined | null;
682
683
  }
683
684
 
684
685
  interface EmbedHTMLAttributes extends HTMLAttributes {
@@ -1,3 +1,4 @@
1
+ import { shouldAppendForwardSlash } from "../core/build/util.js";
1
2
  import {
2
3
  NOOP_ACTIONS,
3
4
  RESOLVED_VIRTUAL_INTERNAL_MODULE_ID,
@@ -69,6 +70,12 @@ export * from 'astro/actions/runtime/virtual/server.js';`;
69
70
  } else {
70
71
  code += `
71
72
  export * from 'astro/actions/runtime/virtual/client.js';`;
73
+ code = code.replace(
74
+ "'/** @TRAILING_SLASH@ **/'",
75
+ JSON.stringify(
76
+ shouldAppendForwardSlash(settings.config.trailingSlash, settings.config.build.format)
77
+ )
78
+ );
72
79
  }
73
80
  return code;
74
81
  }
@@ -1,6 +1,9 @@
1
1
  import { z } from "zod";
2
+ import { shouldAppendForwardSlash } from "../../../core/build/util.js";
2
3
  import { ActionCalledFromServerError } from "../../../core/errors/errors-data.js";
3
4
  import { AstroError } from "../../../core/errors/errors.js";
5
+ import { removeTrailingForwardSlash } from "../../../core/path.js";
6
+ import { apiContextRoutesSymbol } from "../../../core/render-context.js";
4
7
  import { ACTION_RPC_ROUTE_PATTERN } from "../../consts.js";
5
8
  import {
6
9
  ACTION_API_CONTEXT_SYMBOL,
@@ -143,7 +146,12 @@ function getActionContext(context) {
143
146
  calledFrom: callerInfo.from,
144
147
  name: callerInfo.name,
145
148
  handler: async () => {
146
- const baseAction = await getAction(callerInfo.name);
149
+ const pipeline = Reflect.get(context, apiContextRoutesSymbol);
150
+ const callerInfoName = shouldAppendForwardSlash(
151
+ pipeline.manifest.trailingSlash,
152
+ pipeline.manifest.buildFormat
153
+ ) ? removeTrailingForwardSlash(callerInfo.name) : callerInfo.name;
154
+ const baseAction = await getAction(callerInfoName);
147
155
  let input;
148
156
  try {
149
157
  input = await parseRequestBody(context.request);
@@ -1,10 +1,12 @@
1
1
  import type { z } from 'zod';
2
+ import { appendForwardSlash as _appendForwardSlash } from '../../../core/path.js';
2
3
  import type { ErrorInferenceObject, MaybePromise, ActionAPIContext as _ActionAPIContext } from '../utils.js';
3
4
  export type ActionAPIContext = _ActionAPIContext;
4
5
  export declare const ACTION_QUERY_PARAMS: {
5
6
  actionName: string;
6
7
  actionPayload: string;
7
8
  };
9
+ export declare const appendForwardSlash: typeof _appendForwardSlash;
8
10
  export declare const ACTION_ERROR_CODES: readonly ["BAD_REQUEST", "UNAUTHORIZED", "FORBIDDEN", "NOT_FOUND", "TIMEOUT", "CONFLICT", "PRECONDITION_FAILED", "PAYLOAD_TOO_LARGE", "UNSUPPORTED_MEDIA_TYPE", "UNPROCESSABLE_CONTENT", "TOO_MANY_REQUESTS", "CLIENT_CLOSED_REQUEST", "INTERNAL_SERVER_ERROR"];
9
11
  export type ActionErrorCode = (typeof ACTION_ERROR_CODES)[number];
10
12
  export declare class ActionError<_T extends ErrorInferenceObject = ErrorInferenceObject> extends Error {
@@ -2,8 +2,10 @@ import { parse as devalueParse, stringify as devalueStringify } from "devalue";
2
2
  import { REDIRECT_STATUS_CODES } from "../../../core/constants.js";
3
3
  import { ActionsReturnedInvalidDataError } from "../../../core/errors/errors-data.js";
4
4
  import { AstroError } from "../../../core/errors/errors.js";
5
+ import { appendForwardSlash as _appendForwardSlash } from "../../../core/path.js";
5
6
  import { ACTION_QUERY_PARAMS as _ACTION_QUERY_PARAMS } from "../../consts.js";
6
7
  const ACTION_QUERY_PARAMS = _ACTION_QUERY_PARAMS;
8
+ const appendForwardSlash = _appendForwardSlash;
7
9
  const ACTION_ERROR_CODES = [
8
10
  "BAD_REQUEST",
9
11
  "UNAUTHORIZED",
@@ -227,6 +229,7 @@ export {
227
229
  ACTION_QUERY_PARAMS,
228
230
  ActionError,
229
231
  ActionInputError,
232
+ appendForwardSlash,
230
233
  callSafely,
231
234
  deserializeActionResult,
232
235
  getActionQueryString,
@@ -1,3 +1,2 @@
1
1
  import type { AstroSettings, ManifestData } from '../../types/astro.js';
2
2
  export declare function injectImageEndpoint(settings: AstroSettings, manifest: ManifestData, mode: 'dev' | 'build', cwd?: string): void;
3
- export declare function ensureImageEndpointRoute(settings: AstroSettings, manifest: ManifestData, mode: 'dev' | 'build', cwd?: string): void;
@@ -7,11 +7,6 @@ import { getPattern } from "../../core/routing/manifest/pattern.js";
7
7
  function injectImageEndpoint(settings, manifest, mode, cwd) {
8
8
  manifest.routes.unshift(getImageEndpointData(settings, mode, cwd));
9
9
  }
10
- function ensureImageEndpointRoute(settings, manifest, mode, cwd) {
11
- if (!manifest.routes.some((route) => route.route === settings.config.image.endpoint.route)) {
12
- manifest.routes.unshift(getImageEndpointData(settings, mode, cwd));
13
- }
14
- }
15
10
  function getImageEndpointData(settings, mode, cwd) {
16
11
  const endpointEntrypoint = settings.config.image.endpoint.entrypoint === void 0 ? mode === "dev" ? "astro/assets/endpoint/node" : "astro/assets/endpoint/generic" : settings.config.image.endpoint.entrypoint;
17
12
  const segments = [
@@ -41,6 +36,5 @@ function getImageEndpointData(settings, mode, cwd) {
41
36
  };
42
37
  }
43
38
  export {
44
- ensureImageEndpointRoute,
45
39
  injectImageEndpoint
46
40
  };
@@ -25,11 +25,7 @@ async function check(flags) {
25
25
  }
26
26
  if (!flags.noSync && !flags.help) {
27
27
  const { default: sync } = await import("../../core/sync/index.js");
28
- try {
29
- await sync(flagsToAstroInlineConfig(flags));
30
- } catch (_) {
31
- return process.exit(1);
32
- }
28
+ await sync(flagsToAstroInlineConfig(flags));
33
29
  }
34
30
  const { check: checker, parseArgsAsCheckConfig } = checkPackage;
35
31
  const config = parseArgsAsCheckConfig(process.argv);
@@ -114,7 +114,7 @@ class ContentLayer {
114
114
  logger.info("Content config changed");
115
115
  shouldClear = true;
116
116
  }
117
- if (previousAstroVersion !== "5.0.5") {
117
+ if (previousAstroVersion !== "5.0.7") {
118
118
  logger.info("Astro version changed");
119
119
  shouldClear = true;
120
120
  }
@@ -122,8 +122,8 @@ class ContentLayer {
122
122
  logger.info("Clearing content store");
123
123
  this.#store.clearAll();
124
124
  }
125
- if ("5.0.5") {
126
- await this.#store.metaStore().set("astro-version", "5.0.5");
125
+ if ("5.0.7") {
126
+ await this.#store.metaStore().set("astro-version", "5.0.7");
127
127
  }
128
128
  if (currentConfigDigest) {
129
129
  await this.#store.metaStore().set("config-digest", currentConfigDigest);
@@ -7,6 +7,7 @@ import { IMAGE_IMPORT_PREFIX } from "./consts.js";
7
7
  import { ImmutableDataStore } from "./data-store.js";
8
8
  import { contentModuleToId } from "./utils.js";
9
9
  const SAVE_DEBOUNCE_MS = 500;
10
+ const MAX_DEPTH = 10;
10
11
  class MutableDataStore extends ImmutableDataStore {
11
12
  #file;
12
13
  #assetsFile;
@@ -61,7 +62,7 @@ class MutableDataStore extends ImmutableDataStore {
61
62
  this.#assetsFile = filePath;
62
63
  if (this.#assetImports.size === 0) {
63
64
  try {
64
- await fs.writeFile(filePath, "export default new Map();");
65
+ await this.#writeFileAtomic(filePath, "export default new Map();");
65
66
  } catch (err) {
66
67
  throw new AstroError(AstroErrorData.UnknownFilesystemError, { cause: err });
67
68
  }
@@ -84,7 +85,7 @@ export default new Map([${exports.join(", ")}]);
84
85
  `
85
86
  );
86
87
  try {
87
- await fs.writeFile(filePath, code);
88
+ await this.#writeFileAtomic(filePath, code);
88
89
  } catch (err) {
89
90
  throw new AstroError(AstroErrorData.UnknownFilesystemError, { cause: err });
90
91
  }
@@ -94,7 +95,7 @@ export default new Map([${exports.join(", ")}]);
94
95
  this.#modulesFile = filePath;
95
96
  if (this.#moduleImports.size === 0) {
96
97
  try {
97
- await fs.writeFile(filePath, "export default new Map();");
98
+ await this.#writeFileAtomic(filePath, "export default new Map();");
98
99
  } catch (err) {
99
100
  throw new AstroError(AstroErrorData.UnknownFilesystemError, { cause: err });
100
101
  }
@@ -111,7 +112,7 @@ export default new Map([
111
112
  ${lines.join(",\n")}]);
112
113
  `;
113
114
  try {
114
- await fs.writeFile(filePath, code);
115
+ await this.#writeFileAtomic(filePath, code);
115
116
  } catch (err) {
116
117
  throw new AstroError(AstroErrorData.UnknownFilesystemError, { cause: err });
117
118
  }
@@ -153,6 +154,30 @@ ${lines.join(",\n")}]);
153
154
  }, SAVE_DEBOUNCE_MS);
154
155
  }
155
156
  }
157
+ #writing = /* @__PURE__ */ new Set();
158
+ #pending = /* @__PURE__ */ new Set();
159
+ async #writeFileAtomic(filePath, data, depth = 0) {
160
+ if (depth > MAX_DEPTH) {
161
+ return;
162
+ }
163
+ const fileKey = filePath.toString();
164
+ if (this.#writing.has(fileKey)) {
165
+ this.#pending.add(fileKey);
166
+ return;
167
+ }
168
+ this.#writing.add(fileKey);
169
+ const tempFile = filePath instanceof URL ? new URL(`${filePath.href}.tmp`) : `${filePath}.tmp`;
170
+ try {
171
+ await fs.writeFile(tempFile, data);
172
+ await fs.rename(tempFile, filePath);
173
+ } finally {
174
+ this.#writing.delete(fileKey);
175
+ if (this.#pending.has(fileKey)) {
176
+ this.#pending.delete(fileKey);
177
+ await this.#writeFileAtomic(filePath, data, depth + 1);
178
+ }
179
+ }
180
+ }
156
181
  scopedStore(collectionName) {
157
182
  return {
158
183
  get: (key) => this.get(collectionName, key),
@@ -250,7 +275,7 @@ ${lines.join(",\n")}]);
250
275
  return;
251
276
  }
252
277
  try {
253
- await fs.writeFile(filePath, this.toString());
278
+ await this.#writeFileAtomic(filePath, this.toString());
254
279
  this.#file = filePath;
255
280
  this.#dirty = false;
256
281
  } catch (err) {
@@ -3,7 +3,6 @@ import {
3
3
  REROUTABLE_STATUS_CODES,
4
4
  REROUTE_DIRECTIVE_HEADER,
5
5
  clientAddressSymbol,
6
- clientLocalsSymbol,
7
6
  responseSentSymbol
8
7
  } from "../constants.js";
9
8
  import { getSetCookiesFromResponse } from "../cookies/index.js";
@@ -19,7 +18,8 @@ import {
19
18
  } from "../path.js";
20
19
  import { RenderContext } from "../render-context.js";
21
20
  import { createAssetLink } from "../render/ssr-element.js";
22
- import { createDefaultRoutes, injectDefaultRoutes } from "../routing/default.js";
21
+ import { ensure404Route } from "../routing/astro-designed-error-pages.js";
22
+ import { createDefaultRoutes } from "../routing/default.js";
23
23
  import { matchRoute } from "../routing/match.js";
24
24
  import { AppPipeline } from "./pipeline.js";
25
25
  import { deserializeManifest } from "./common.js";
@@ -36,9 +36,10 @@ class App {
36
36
  #renderOptionsDeprecationWarningShown = false;
37
37
  constructor(manifest, streaming = true) {
38
38
  this.#manifest = manifest;
39
- this.#manifestData = injectDefaultRoutes(manifest, {
39
+ this.#manifestData = {
40
40
  routes: manifest.routes.map((route) => route.routeData)
41
- });
41
+ };
42
+ ensure404Route(this.#manifestData);
42
43
  this.#baseWithoutTrailingSlash = removeTrailingForwardSlash(this.#manifest.base);
43
44
  this.#pipeline = this.#createPipeline(this.#manifestData, streaming);
44
45
  this.#adapterLogger = new AstroIntegrationLogger(
@@ -2,7 +2,6 @@ import fs from "node:fs";
2
2
  import { performance } from "node:perf_hooks";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { blue, bold, green } from "kleur/colors";
5
- import { injectImageEndpoint } from "../../assets/endpoint/config.js";
6
5
  import { telemetry } from "../../events/index.js";
7
6
  import { eventCliSession } from "../../events/session.js";
8
7
  import {
@@ -20,7 +19,6 @@ import { AstroError, AstroErrorData } from "../errors/index.js";
20
19
  import { levels, timerMessage } from "../logger/core.js";
21
20
  import { apply as applyPolyfill } from "../polyfill.js";
22
21
  import { createRouteManifest } from "../routing/index.js";
23
- import { getServerIslandRouteData } from "../server-islands/endpoint.js";
24
22
  import { clearContentLayerCache } from "../sync/index.js";
25
23
  import { ensureProcessNodeEnv } from "../util.js";
26
24
  import { collectPagesData } from "./page-data.js";
@@ -74,9 +72,6 @@ class AstroBuilder {
74
72
  logger
75
73
  });
76
74
  this.manifest = await createRouteManifest({ settings: this.settings }, this.logger);
77
- if (this.settings.buildOutput === "server") {
78
- injectImageEndpoint(this.settings, this.manifest, "build");
79
- }
80
75
  await runHookConfigDone({ settings: this.settings, logger, command: "build" });
81
76
  if (!this.settings.config.adapter && this.settings.buildOutput === "server") {
82
77
  throw new AstroError(AstroErrorData.NoAdapterInstalled);
@@ -144,12 +139,12 @@ class AstroBuilder {
144
139
  viteConfig,
145
140
  key: keyPromise
146
141
  };
147
- const { internals, ssrOutputChunkNames, ssrOutputAssetNames, contentFileNames } = await viteBuild(opts);
142
+ const { internals, ssrOutputChunkNames, contentFileNames } = await viteBuild(opts);
148
143
  const hasServerIslands = this.settings.serverIslandNameMap.size > 0;
149
144
  if (hasServerIslands && this.settings.buildOutput !== "server") {
150
145
  throw new AstroError(AstroErrorData.NoAdapterInstalledServerIslands);
151
146
  }
152
- await staticBuild(opts, internals, ssrOutputChunkNames, ssrOutputAssetNames, contentFileNames);
147
+ await staticBuild(opts, internals, ssrOutputChunkNames, contentFileNames);
153
148
  this.timer.assetsStart = performance.now();
154
149
  Object.keys(assets).map((k) => {
155
150
  if (!assets[k]) return;
@@ -162,7 +157,7 @@ class AstroBuilder {
162
157
  await runHookBuildDone({
163
158
  settings: this.settings,
164
159
  pages: pageNames,
165
- routes: Object.values(allPages).flat().map((pageData) => pageData.route).concat(hasServerIslands ? getServerIslandRouteData(this.settings.config) : []),
160
+ routes: Object.values(allPages).flat().map((pageData) => pageData.route),
166
161
  logging: this.logger
167
162
  });
168
163
  if (this.logger.level && levels[this.logger.level()] <= levels["info"]) {
@@ -1,11 +1,15 @@
1
1
  import * as colors from "kleur/colors";
2
2
  import { debug } from "../logger/core.js";
3
+ import { DEFAULT_COMPONENTS } from "../routing/default.js";
3
4
  import { makePageDataKey } from "./plugins/util.js";
4
5
  function collectPagesData(opts) {
5
6
  const { settings, manifest } = opts;
6
7
  const assets = {};
7
8
  const allPages = {};
8
9
  for (const route of manifest.routes) {
10
+ if (DEFAULT_COMPONENTS.some((component) => route.component === component)) {
11
+ continue;
12
+ }
9
13
  const key = makePageDataKey(route.route, route.component);
10
14
  if (route.pathname) {
11
15
  allPages[key] = {
@@ -3,11 +3,11 @@ import glob from "fast-glob";
3
3
  import { getAssetsPrefix } from "../../../assets/utils/getAssetsPrefix.js";
4
4
  import { normalizeTheLocale } from "../../../i18n/index.js";
5
5
  import { toFallbackType, toRoutingStrategy } from "../../../i18n/utils.js";
6
- import { unwrapSupportKind } from "../../../integrations/features-validation.js";
7
6
  import { runHookBuildSsr } from "../../../integrations/hooks.js";
8
7
  import { BEFORE_HYDRATION_SCRIPT_ID, PAGE_SCRIPT_ID } from "../../../vite-plugin-scripts/index.js";
9
8
  import { encodeKey } from "../../encryption.js";
10
9
  import { fileExtension, joinPaths, prependForwardSlash } from "../../path.js";
10
+ import { DEFAULT_COMPONENTS } from "../../routing/default.js";
11
11
  import { serializeRouteData } from "../../routing/index.js";
12
12
  import { addRollupInput } from "../add-rollup-input.js";
13
13
  import { getOutFile, getOutFolder } from "../common.js";
@@ -130,6 +130,18 @@ function buildManifest(opts, internals, staticFiles, encodedKey) {
130
130
  return prependForwardSlash(joinPaths(settings.config.base, pth));
131
131
  }
132
132
  };
133
+ for (const route of opts.manifest.routes) {
134
+ if (!DEFAULT_COMPONENTS.find((component) => route.component === component)) {
135
+ continue;
136
+ }
137
+ routes.push({
138
+ file: "",
139
+ links: [],
140
+ scripts: [],
141
+ styles: [],
142
+ routeData: serializeRouteData(route, settings.config.trailingSlash)
143
+ });
144
+ }
133
145
  for (const route of opts.manifest.routes) {
134
146
  if (!route.prerender) continue;
135
147
  if (!route.pathname) continue;
@@ -4,10 +4,9 @@ import type { StaticBuildOptions } from './types.js';
4
4
  export declare function viteBuild(opts: StaticBuildOptions): Promise<{
5
5
  internals: BuildInternals;
6
6
  ssrOutputChunkNames: string[];
7
- ssrOutputAssetNames: string[];
8
7
  contentFileNames: undefined;
9
8
  }>;
10
- export declare function staticBuild(opts: StaticBuildOptions, internals: BuildInternals, ssrOutputChunkNames: string[], ssrOutputAssetNames: string[], contentFileNames?: string[]): Promise<void>;
9
+ export declare function staticBuild(opts: StaticBuildOptions, internals: BuildInternals, ssrOutputChunkNames: string[], contentFileNames?: string[]): Promise<void>;
11
10
  export declare function copyFiles(fromFolder: URL, toFolder: URL, includeDotfiles?: boolean): Promise<void[] | undefined>;
12
11
  /**
13
12
  * This function takes the virtual module name of any page entrypoint and
@@ -69,20 +69,16 @@ async function viteBuild(opts) {
69
69
  teardown();
70
70
  }
71
71
  const ssrOutputChunkNames = [];
72
- const ssrOutputAssetNames = [];
73
72
  for (const output of ssrOutputs) {
74
73
  for (const chunk of output.output) {
75
74
  if (chunk.type === "chunk") {
76
75
  ssrOutputChunkNames.push(chunk.fileName);
77
76
  }
78
- if (chunk.type === "asset") {
79
- ssrOutputAssetNames.push(chunk.fileName);
80
- }
81
77
  }
82
78
  }
83
- return { internals, ssrOutputChunkNames, ssrOutputAssetNames, contentFileNames };
79
+ return { internals, ssrOutputChunkNames, contentFileNames };
84
80
  }
85
- async function staticBuild(opts, internals, ssrOutputChunkNames, ssrOutputAssetNames, contentFileNames) {
81
+ async function staticBuild(opts, internals, ssrOutputChunkNames, contentFileNames) {
86
82
  const { settings } = opts;
87
83
  if (settings.buildOutput === "static") {
88
84
  settings.timer.start("Static generate");
@@ -93,7 +89,7 @@ async function staticBuild(opts, internals, ssrOutputChunkNames, ssrOutputAssetN
93
89
  settings.timer.start("Server generate");
94
90
  await generatePages(opts, internals);
95
91
  await cleanStaticOutput(opts, internals);
96
- await ssrMoveAssets(opts, ssrOutputAssetNames);
92
+ await ssrMoveAssets(opts);
97
93
  settings.timer.end("Server generate");
98
94
  }
99
95
  }
@@ -309,15 +305,21 @@ async function copyFiles(fromFolder, toFolder, includeDotfiles = false) {
309
305
  })
310
306
  );
311
307
  }
312
- async function ssrMoveAssets(opts, ssrOutputAssetNames) {
308
+ async function ssrMoveAssets(opts) {
313
309
  opts.logger.info("build", "Rearranging server assets...");
314
310
  const serverRoot = opts.settings.buildOutput === "static" ? opts.settings.config.build.client : opts.settings.config.build.server;
315
311
  const clientRoot = opts.settings.config.build.client;
316
- if (ssrOutputAssetNames.length > 0) {
312
+ const assets = opts.settings.config.build.assets;
313
+ const serverAssets = new URL(`./${assets}/`, appendForwardSlash(serverRoot.toString()));
314
+ const clientAssets = new URL(`./${assets}/`, appendForwardSlash(clientRoot.toString()));
315
+ const files = await glob(`**/*`, {
316
+ cwd: fileURLToPath(serverAssets)
317
+ });
318
+ if (files.length > 0) {
317
319
  await Promise.all(
318
- ssrOutputAssetNames.map(async function moveAsset(filename) {
319
- const currentUrl = new URL(filename, appendForwardSlash(serverRoot.toString()));
320
- const clientUrl = new URL(filename, appendForwardSlash(clientRoot.toString()));
320
+ files.map(async function moveAsset(filename) {
321
+ const currentUrl = new URL(filename, appendForwardSlash(serverAssets.toString()));
322
+ const clientUrl = new URL(filename, appendForwardSlash(clientAssets.toString()));
321
323
  const dir = new URL(path.parse(clientUrl.href).dir);
322
324
  if (!fs.existsSync(dir)) await fs.promises.mkdir(dir, { recursive: true });
323
325
  return fs.promises.rename(currentUrl, clientUrl);
@@ -1,4 +1,4 @@
1
- const ASTRO_VERSION = "5.0.5";
1
+ const ASTRO_VERSION = "5.0.7";
2
2
  const REROUTE_DIRECTIVE_HEADER = "X-Astro-Reroute";
3
3
  const REWRITE_DIRECTIVE_HEADER_KEY = "X-Astro-Rewrite";
4
4
  const REWRITE_DIRECTIVE_HEADER_VALUE = "yes";
@@ -3,14 +3,12 @@ import * as vite from "vite";
3
3
  import {
4
4
  runHookConfigDone,
5
5
  runHookConfigSetup,
6
- runHookRoutesResolved,
7
6
  runHookServerDone,
8
7
  runHookServerStart
9
8
  } from "../../integrations/hooks.js";
10
9
  import { createDevelopmentManifest } from "../../vite-plugin-astro-server/plugin.js";
11
10
  import { createVite } from "../create-vite.js";
12
11
  import { apply as applyPolyfill } from "../polyfill.js";
13
- import { injectDefaultDevRoutes } from "../routing/dev-default.js";
14
12
  import { createRouteManifest } from "../routing/index.js";
15
13
  import { syncInternal } from "../sync/index.js";
16
14
  import { warnMissingAdapter } from "./adapter-validation.js";
@@ -36,10 +34,8 @@ async function createContainer({
36
34
  const isServerOpenBoolean = serverOpen && !isRestart;
37
35
  const open = isServerOpenURL ? serverOpen : isServerOpenBoolean ? base : false;
38
36
  const rendererClientEntries = settings.renderers.map((r) => r.clientEntrypoint).filter(Boolean);
39
- let manifest = await createRouteManifest({ settings, fsMod: fs }, logger, { dev: true });
37
+ const manifest = await createRouteManifest({ settings, fsMod: fs }, logger, { dev: true });
40
38
  const devSSRManifest = createDevelopmentManifest(settings);
41
- manifest = injectDefaultDevRoutes(settings, devSSRManifest, manifest);
42
- await runHookRoutesResolved({ settings, logger, routes: manifest.routes });
43
39
  await runHookConfigDone({ settings, logger, command: "dev" });
44
40
  warnMissingAdapter(logger, settings);
45
41
  const mode = inlineConfig?.mode ?? "development";
@@ -22,7 +22,7 @@ async function dev(inlineConfig) {
22
22
  await telemetry.record([]);
23
23
  const restart = await createContainerWithAutomaticRestart({ inlineConfig, fs });
24
24
  const logger = restart.container.logger;
25
- const currentVersion = "5.0.5";
25
+ const currentVersion = "5.0.7";
26
26
  const isPrerelease = currentVersion.includes("-");
27
27
  if (!isPrerelease) {
28
28
  try {
@@ -38,7 +38,7 @@ function serverStart({
38
38
  host,
39
39
  base
40
40
  }) {
41
- const version = "5.0.5";
41
+ const version = "5.0.7";
42
42
  const localPrefix = `${dim("\u2503")} Local `;
43
43
  const networkPrefix = `${dim("\u2503")} Network `;
44
44
  const emptyPrefix = " ".repeat(11);
@@ -276,7 +276,7 @@ function printHelp({
276
276
  message.push(
277
277
  linebreak(),
278
278
  ` ${bgGreen(black(` ${commandName} `))} ${green(
279
- `v${"5.0.5"}`
279
+ `v${"5.0.7"}`
280
280
  )} ${headline}`
281
281
  );
282
282
  }
@@ -19,6 +19,7 @@ async function getProps(opts) {
19
19
  ssr: serverLike,
20
20
  base
21
21
  });
22
+ if (!staticPaths.length) return {};
22
23
  const params = getParams(route, decodeURI(pathname));
23
24
  const matchedStaticPath = findPathItemByKey(staticPaths, params, route, logger);
24
25
  if (!matchedStaticPath && (serverLike ? route.prerender : true)) {
@@ -36,7 +37,7 @@ async function getProps(opts) {
36
37
  }
37
38
  function getParams(route, pathname) {
38
39
  if (!route.params.length) return {};
39
- const paramsMatch = route.pattern.exec(pathname);
40
+ const paramsMatch = route.pattern.exec(pathname) || route.fallbackRoutes.map((fallbackRoute) => fallbackRoute.pattern.exec(pathname)).find((x) => x);
40
41
  if (!paramsMatch) return {};
41
42
  const params = {};
42
43
  route.params.forEach((key, i) => {
@@ -1,11 +1,11 @@
1
- import type { ComponentInstance, ManifestData } from '../../types/astro.js';
1
+ import type { ComponentInstance } from '../../types/astro.js';
2
2
  import type { SSRManifest } from '../app/types.js';
3
- export declare function injectDefaultRoutes(ssrManifest: SSRManifest, routeManifest: ManifestData): ManifestData;
4
3
  type DefaultRouteParams = {
5
4
  instance: ComponentInstance;
6
5
  matchesComponent(filePath: URL): boolean;
7
6
  route: string;
8
7
  component: string;
9
8
  };
9
+ export declare const DEFAULT_COMPONENTS: string[];
10
10
  export declare function createDefaultRoutes(manifest: SSRManifest): DefaultRouteParams[];
11
11
  export {};
@@ -2,19 +2,10 @@ import { DEFAULT_404_COMPONENT } from "../constants.js";
2
2
  import {
3
3
  SERVER_ISLAND_COMPONENT,
4
4
  SERVER_ISLAND_ROUTE,
5
- createEndpoint as createServerIslandEndpoint,
6
- ensureServerIslandRoute
5
+ createEndpoint as createServerIslandEndpoint
7
6
  } from "../server-islands/endpoint.js";
8
- import {
9
- DEFAULT_404_ROUTE,
10
- default404Instance,
11
- ensure404Route
12
- } from "./astro-designed-error-pages.js";
13
- function injectDefaultRoutes(ssrManifest, routeManifest) {
14
- ensure404Route(routeManifest);
15
- ensureServerIslandRoute(ssrManifest, routeManifest);
16
- return routeManifest;
17
- }
7
+ import { DEFAULT_404_ROUTE, default404Instance } from "./astro-designed-error-pages.js";
8
+ const DEFAULT_COMPONENTS = [DEFAULT_404_COMPONENT, SERVER_ISLAND_COMPONENT];
18
9
  function createDefaultRoutes(manifest) {
19
10
  const root = new URL(manifest.hrefRoot);
20
11
  return [
@@ -33,6 +24,6 @@ function createDefaultRoutes(manifest) {
33
24
  ];
34
25
  }
35
26
  export {
36
- createDefaultRoutes,
37
- injectDefaultRoutes
27
+ DEFAULT_COMPONENTS,
28
+ createDefaultRoutes
38
29
  };
@@ -4,6 +4,7 @@ import path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { bold } from "kleur/colors";
6
6
  import pLimit from "p-limit";
7
+ import { injectImageEndpoint } from "../../../assets/endpoint/config.js";
7
8
  import { toRoutingStrategy } from "../../../i18n/utils.js";
8
9
  import { runHookRoutesResolved } from "../../../integrations/hooks.js";
9
10
  import { getPrerenderDefault } from "../../../prerender/utils.js";
@@ -11,7 +12,9 @@ import { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from "../../constants.js";
11
12
  import { MissingIndexForInternationalization } from "../../errors/errors-data.js";
12
13
  import { AstroError } from "../../errors/index.js";
13
14
  import { removeLeadingForwardSlash, slash } from "../../path.js";
15
+ import { injectServerIslandRoute } from "../../server-islands/endpoint.js";
14
16
  import { resolvePages } from "../../util.js";
17
+ import { ensure404Route } from "../astro-designed-error-pages.js";
15
18
  import { routeComparator } from "../priority.js";
16
19
  import { getRouteGenerator } from "./generator.js";
17
20
  import { getPattern } from "./pattern.js";
@@ -493,9 +496,14 @@ async function createRouteManifest(params, logger, { dev = false } = {}) {
493
496
  }
494
497
  }
495
498
  }
496
- if (!dev) {
497
- await runHookRoutesResolved({ routes, settings, logger });
499
+ if (dev) {
500
+ ensure404Route({ routes });
498
501
  }
502
+ if (dev || settings.buildOutput === "server") {
503
+ injectImageEndpoint(settings, { routes }, dev ? "dev" : "build");
504
+ injectServerIslandRoute(settings.config, { routes });
505
+ }
506
+ await runHookRoutesResolved({ routes, settings, logger });
499
507
  return {
500
508
  routes
501
509
  };
@@ -4,6 +4,6 @@ export declare const SERVER_ISLAND_ROUTE = "/_server-islands/[name]";
4
4
  export declare const SERVER_ISLAND_COMPONENT = "_server-islands.astro";
5
5
  type ConfigFields = Pick<SSRManifest, 'base' | 'trailingSlash'>;
6
6
  export declare function getServerIslandRouteData(config: ConfigFields): RouteData;
7
- export declare function ensureServerIslandRoute(config: ConfigFields, routeManifest: ManifestData): void;
7
+ export declare function injectServerIslandRoute(config: ConfigFields, routeManifest: ManifestData): void;
8
8
  export declare function createEndpoint(manifest: SSRManifest): ComponentInstance;
9
9
  export {};
@@ -28,10 +28,7 @@ function getServerIslandRouteData(config) {
28
28
  };
29
29
  return route;
30
30
  }
31
- function ensureServerIslandRoute(config, routeManifest) {
32
- if (routeManifest.routes.some((route) => route.route === "/_server-islands/[name]")) {
33
- return;
34
- }
31
+ function injectServerIslandRoute(config, routeManifest) {
35
32
  routeManifest.routes.unshift(getServerIslandRouteData(config));
36
33
  }
37
34
  function badRequest(reason) {
@@ -125,6 +122,6 @@ export {
125
122
  SERVER_ISLAND_COMPONENT,
126
123
  SERVER_ISLAND_ROUTE,
127
124
  createEndpoint,
128
- ensureServerIslandRoute,
129
- getServerIslandRouteData
125
+ getServerIslandRouteData,
126
+ injectServerIslandRoute
130
127
  };
@@ -97,7 +97,7 @@ export type MiddlewarePayload = {
97
97
  fallbackType: 'redirect' | 'rewrite';
98
98
  };
99
99
  export declare function redirectToDefaultLocale({ trailingSlash, format, base, defaultLocale, }: MiddlewarePayload): (context: APIContext, statusCode?: ValidRedirectStatus) => Response;
100
- export declare function notFound({ base, locales }: MiddlewarePayload): (context: APIContext, response?: Response) => Response | undefined;
100
+ export declare function notFound({ base, locales, fallback }: MiddlewarePayload): (context: APIContext, response?: Response) => Response | undefined;
101
101
  export type RedirectToFallback = (context: APIContext, response: Response) => Promise<Response>;
102
102
  export declare function redirectToFallback({ fallback, locales, defaultLocale, strategy, base, fallbackType, }: MiddlewarePayload): (context: APIContext, response: Response) => Promise<Response>;
103
103
  export declare function createMiddleware(i18nManifest: SSRManifest['i18n'], base: SSRManifest['base'], trailingSlash: SSRManifest['trailingSlash'], format: SSRManifest['buildFormat']): import("../types/public/common.js").MiddlewareHandler;
@@ -177,9 +177,11 @@ function redirectToDefaultLocale({
177
177
  }
178
178
  };
179
179
  }
180
- function notFound({ base, locales }) {
180
+ function notFound({ base, locales, fallback }) {
181
181
  return function(context, response) {
182
- if (response?.headers.get(REROUTE_DIRECTIVE_HEADER) === "no") return response;
182
+ if (response?.headers.get(REROUTE_DIRECTIVE_HEADER) === "no" && typeof fallback === "undefined") {
183
+ return response;
184
+ }
183
185
  const url = context.url;
184
186
  const isRoot = url.pathname === base + "/" || url.pathname === base;
185
187
  if (!(isRoot || pathHasLocale(url.pathname, locales))) {
@@ -1,7 +1,7 @@
1
1
  import { clsx } from "clsx";
2
2
  import { HTMLString, markHTMLString } from "../escape.js";
3
3
  const voidElementNames = /^(area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/i;
4
- const htmlBooleanAttributes = /^(?:allowfullscreen|async|autofocus|autoplay|checked|controls|default|defer|disabled|disablepictureinpicture|disableremoteplayback|formnovalidate|hidden|loop|nomodule|novalidate|open|playsinline|readonly|required|reversed|scoped|seamless|selected|itemscope)$/i;
4
+ const htmlBooleanAttributes = /^(?:allowfullscreen|async|autofocus|autoplay|checked|controls|default|defer|disabled|disablepictureinpicture|disableremoteplayback|formnovalidate|hidden|inert|loop|nomodule|novalidate|open|playsinline|readonly|required|reversed|scoped|seamless|selected|itemscope)$/i;
5
5
  const AMPERSAND_REGEX = /&/g;
6
6
  const DOUBLE_QUOTE_REGEX = /"/g;
7
7
  const STATIC_DIRECTIVES = /* @__PURE__ */ new Set(["set:html", "set:text"]);
@@ -131,10 +131,10 @@ if (i18n?.routing === "manual") {
131
131
  fallbackType = toFallbackType(customOptions);
132
132
  const manifest = {
133
133
  ...i18n,
134
- fallback: void 0,
135
134
  strategy,
136
135
  domainLookupTable: {},
137
- fallbackType
136
+ fallbackType,
137
+ fallback: i18n.fallback
138
138
  };
139
139
  return I18nInternals.createMiddleware(manifest, base, trailingSlash, format);
140
140
  };
@@ -9,7 +9,6 @@ import { AstroError, AstroErrorData } from "../core/errors/index.js";
9
9
  import { patchOverlay } from "../core/errors/overlay.js";
10
10
  import { NOOP_MIDDLEWARE_FN } from "../core/middleware/noop-middleware.js";
11
11
  import { createViteLoader } from "../core/module-loader/index.js";
12
- import { injectDefaultDevRoutes } from "../core/routing/dev-default.js";
13
12
  import { createRouteManifest } from "../core/routing/index.js";
14
13
  import { getRoutePrerenderOption } from "../core/routing/manifest/prerender.js";
15
14
  import { toFallbackType, toRoutingStrategy } from "../i18n/utils.js";
@@ -53,16 +52,12 @@ function createVitePluginAstroServer({
53
52
  try {
54
53
  const content = await fsMod.promises.readFile(routePath, "utf-8");
55
54
  await getRoutePrerenderOption(content, route, settings, logger);
55
+ await runHookRoutesResolved({ routes: routeManifest.routes, settings, logger });
56
56
  } catch (_) {
57
57
  }
58
58
  } else {
59
- routeManifest = injectDefaultDevRoutes(
60
- settings,
61
- devSSRManifest,
62
- await createRouteManifest({ settings, fsMod }, logger, { dev: true })
63
- );
59
+ routeManifest = await createRouteManifest({ settings, fsMod }, logger, { dev: true });
64
60
  }
65
- await runHookRoutesResolved({ routes: routeManifest.routes, settings, logger });
66
61
  warnMissingAdapter(logger, settings);
67
62
  pipeline.manifest.checkOrigin = settings.config.security.checkOrigin && settings.buildOutput === "server";
68
63
  pipeline.setManifestData(routeManifest);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "5.0.5",
3
+ "version": "5.0.7",
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",
@@ -1,4 +1,9 @@
1
- import { ActionError, deserializeActionResult, getActionQueryString } from 'astro:actions';
1
+ import {
2
+ ActionError,
3
+ appendForwardSlash,
4
+ deserializeActionResult,
5
+ getActionQueryString,
6
+ } from 'astro:actions';
2
7
 
3
8
  const ENCODED_DOT = '%2E';
4
9
 
@@ -83,7 +88,15 @@ async function handleAction(param, path, context) {
83
88
  headers.set('Content-Length', '0');
84
89
  }
85
90
  }
86
- const rawResult = await fetch(`${import.meta.env.BASE_URL.replace(/\/$/, '')}/_actions/${path}`, {
91
+
92
+ const shouldAppendTrailingSlash = '/** @TRAILING_SLASH@ **/';
93
+ let actionPath = import.meta.env.BASE_URL.replace(/\/$/, '') + '/_actions/' + path;
94
+
95
+ if (shouldAppendTrailingSlash) {
96
+ actionPath = appendForwardSlash(actionPath);
97
+ }
98
+
99
+ const rawResult = await fetch(actionPath, {
87
100
  method: 'POST',
88
101
  body,
89
102
  headers,
package/templates/env.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  // @ts-check
2
2
  import { schema } from 'virtual:astro:env/internal';
3
3
  import {
4
+ // biome-ignore lint/correctness/noUnusedImports: `_getEnv` is used by the generated code
4
5
  getEnv as _getEnv,
5
6
  createInvalidVariablesError,
6
7
  getEnvFieldType,
@@ -1,3 +0,0 @@
1
- import type { AstroSettings, ManifestData } from '../../types/astro.js';
2
- import type { SSRManifest } from '../app/types.js';
3
- export declare function injectDefaultDevRoutes(settings: AstroSettings, ssrManifest: SSRManifest, routeManifest: ManifestData): ManifestData;
@@ -1,10 +0,0 @@
1
- import { ensureImageEndpointRoute } from "../../assets/endpoint/config.js";
2
- import { injectDefaultRoutes } from "./default.js";
3
- function injectDefaultDevRoutes(settings, ssrManifest, routeManifest) {
4
- ensureImageEndpointRoute(settings, routeManifest, "dev");
5
- injectDefaultRoutes(ssrManifest, routeManifest);
6
- return routeManifest;
7
- }
8
- export {
9
- injectDefaultDevRoutes
10
- };