astro 2.8.2 → 2.8.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.
@@ -3,7 +3,7 @@ import type * as shiki from 'shiki';
3
3
  import { renderToHtml } from 'shiki';
4
4
  import { getHighlighter } from './Shiki.js';
5
5
 
6
- export interface Props {
6
+ interface Props {
7
7
  /** The code to highlight. Required. */
8
8
  code: string;
9
9
  /**
@@ -2,8 +2,8 @@ export declare const VIRTUAL_MODULE_ID = "astro:assets";
2
2
  export declare const VIRTUAL_SERVICE_ID = "virtual:image-service";
3
3
  export declare const VALID_INPUT_FORMATS: readonly ["jpeg", "jpg", "png", "tiff", "webp", "gif", "svg"];
4
4
  /**
5
- * Valid formats for optimizations in our base services.
6
- * Certain formats can be imported (namely SVGs) but not processed, so they are excluded from this list.
5
+ * Valid formats that our base services support.
6
+ * Certain formats can be imported (namely SVGs) but will not be processed.
7
7
  */
8
- export declare const VALID_OPTIMIZABLE_FORMATS: readonly ["jpeg", "jpg", "png", "tiff", "webp", "gif"];
9
- export declare const VALID_OUTPUT_FORMATS: readonly ["avif", "png", "webp", "jpeg", "jpg"];
8
+ export declare const VALID_SUPPORTED_FORMATS: readonly ["jpeg", "jpg", "png", "tiff", "webp", "gif", "svg"];
9
+ export declare const VALID_OUTPUT_FORMATS: readonly ["avif", "png", "webp", "jpeg", "jpg", "svg"];
@@ -17,12 +17,20 @@ const VALID_INPUT_FORMATS = [
17
17
  "gif",
18
18
  "svg"
19
19
  ];
20
- const VALID_OPTIMIZABLE_FORMATS = ["jpeg", "jpg", "png", "tiff", "webp", "gif"];
21
- const VALID_OUTPUT_FORMATS = ["avif", "png", "webp", "jpeg", "jpg"];
20
+ const VALID_SUPPORTED_FORMATS = [
21
+ "jpeg",
22
+ "jpg",
23
+ "png",
24
+ "tiff",
25
+ "webp",
26
+ "gif",
27
+ "svg"
28
+ ];
29
+ const VALID_OUTPUT_FORMATS = ["avif", "png", "webp", "jpeg", "jpg", "svg"];
22
30
  export {
23
31
  VALID_INPUT_FORMATS,
24
- VALID_OPTIMIZABLE_FORMATS,
25
32
  VALID_OUTPUT_FORMATS,
33
+ VALID_SUPPORTED_FORMATS,
26
34
  VIRTUAL_MODULE_ID,
27
35
  VIRTUAL_SERVICE_ID
28
36
  };
@@ -31,7 +31,7 @@ interface SharedServiceProps {
31
31
  validateOptions?: (options: ImageTransform, serviceConfig: Record<string, any>) => ImageTransform;
32
32
  }
33
33
  export type ExternalImageService = SharedServiceProps;
34
- type LocalImageTransform = {
34
+ export type LocalImageTransform = {
35
35
  src: string;
36
36
  [key: string]: any;
37
37
  };
@@ -1,6 +1,6 @@
1
1
  import { AstroError, AstroErrorData } from "../../core/errors/index.js";
2
2
  import { joinPaths } from "../../core/path.js";
3
- import { VALID_OPTIMIZABLE_FORMATS } from "../consts.js";
3
+ import { VALID_SUPPORTED_FORMATS } from "../consts.js";
4
4
  import { isESMImportedImage } from "../internal.js";
5
5
  function isLocalService(service) {
6
6
  if (!service) {
@@ -45,16 +45,19 @@ const baseService = {
45
45
  });
46
46
  }
47
47
  } else {
48
- if (!VALID_OPTIMIZABLE_FORMATS.includes(options.src.format)) {
48
+ if (!VALID_SUPPORTED_FORMATS.includes(options.src.format)) {
49
49
  throw new AstroError({
50
50
  ...AstroErrorData.UnsupportedImageFormat,
51
51
  message: AstroErrorData.UnsupportedImageFormat.message(
52
52
  options.src.format,
53
53
  options.src.src,
54
- VALID_OPTIMIZABLE_FORMATS
54
+ VALID_SUPPORTED_FORMATS
55
55
  )
56
56
  });
57
57
  }
58
+ if (options.src.format === "svg") {
59
+ options.format = "svg";
60
+ }
58
61
  }
59
62
  if (!options.format) {
60
63
  options.format = "webp";
@@ -27,7 +27,10 @@ const sharpService = {
27
27
  if (!sharp)
28
28
  sharp = await loadSharp();
29
29
  const transform = transformOptions;
30
+ if (transform.format === "svg")
31
+ return { data: inputBuffer, format: "svg" };
30
32
  let result = sharp(inputBuffer, { failOnError: false, pages: -1 });
33
+ result.rotate();
31
34
  if (transform.height && !transform.width) {
32
35
  result.resize({ height: transform.height });
33
36
  } else if (transform.width) {
@@ -1,3 +1,4 @@
1
+ import { imageMetadata } from "../utils/metadata.js";
1
2
  import {
2
3
  baseService,
3
4
  parseQuality
@@ -17,6 +18,24 @@ const qualityTable = {
17
18
  webp: baseQuality
18
19
  // Squoosh's PNG encoder does not support a quality setting, so we can skip that here
19
20
  };
21
+ async function getRotationForEXIF(transform, inputBuffer) {
22
+ const filePath = transform.src.slice("/@fs".length);
23
+ const filePathURL = new URL("." + filePath, "file:");
24
+ const meta = await imageMetadata(filePathURL, inputBuffer);
25
+ if (!meta)
26
+ return void 0;
27
+ switch (meta.orientation) {
28
+ case 3:
29
+ case 4:
30
+ return { type: "rotate", numRotations: 2 };
31
+ case 5:
32
+ case 6:
33
+ return { type: "rotate", numRotations: 1 };
34
+ case 7:
35
+ case 8:
36
+ return { type: "rotate", numRotations: 3 };
37
+ }
38
+ }
20
39
  const service = {
21
40
  validateOptions: baseService.validateOptions,
22
41
  getURL: baseService.getURL,
@@ -25,7 +44,13 @@ const service = {
25
44
  async transform(inputBuffer, transformOptions) {
26
45
  const transform = transformOptions;
27
46
  let format = transform.format;
47
+ if (format === "svg")
48
+ return { data: inputBuffer, format: "svg" };
28
49
  const operations = [];
50
+ const rotation = await getRotationForEXIF(transform, inputBuffer);
51
+ if (rotation) {
52
+ operations.push(rotation);
53
+ }
29
54
  if (transform.height && !transform.width) {
30
55
  operations.push({
31
56
  type: "resize",
@@ -22,6 +22,7 @@ export interface ImageMetadata {
22
22
  width: number;
23
23
  height: number;
24
24
  format: ImageInputFormat;
25
+ orientation?: number;
25
26
  }
26
27
  /**
27
28
  * Options accepted by the image transformation service.
@@ -1,2 +1,2 @@
1
- import { type Metadata } from './metadata.js';
2
- export declare function emitESMImage(id: string | undefined, watchMode: boolean, fileEmitter: any): Promise<Metadata | undefined>;
1
+ import type { ImageMetadata } from '../types.js';
2
+ export declare function emitESMImage(id: string | undefined, watchMode: boolean, fileEmitter: any): Promise<ImageMetadata | undefined>;
@@ -1,6 +1,3 @@
1
1
  /// <reference types="node" />
2
2
  import type { ImageMetadata } from '../types.js';
3
- export interface Metadata extends ImageMetadata {
4
- orientation?: number;
5
- }
6
- export declare function imageMetadata(src: URL | string, data?: Buffer): Promise<Metadata | undefined>;
3
+ export declare function imageMetadata(src: URL | string, data?: Buffer): Promise<ImageMetadata | undefined>;
@@ -3,7 +3,6 @@ import { type LogOptions } from '../../core/logger/core.js';
3
3
  interface AddOptions {
4
4
  logging: LogOptions;
5
5
  flags: yargs.Arguments;
6
- cwd?: string;
7
6
  }
8
7
  interface IntegrationInfo {
9
8
  id: string;
@@ -11,6 +10,6 @@ interface IntegrationInfo {
11
10
  dependencies: [name: string, version: string][];
12
11
  type: 'integration' | 'adapter';
13
12
  }
14
- export declare function add(names: string[], { cwd, flags, logging }: AddOptions): Promise<void>;
13
+ export declare function add(names: string[], { flags, logging }: AddOptions): Promise<void>;
15
14
  export declare function validateIntegrations(integrations: string[]): Promise<IntegrationInfo[]>;
16
15
  export {};
@@ -20,6 +20,7 @@ import { printHelp } from "../../core/messages.js";
20
20
  import { appendForwardSlash } from "../../core/path.js";
21
21
  import { apply as applyPolyfill } from "../../core/polyfill.js";
22
22
  import { parseNpmName } from "../../core/util.js";
23
+ import { eventCliSession, telemetry } from "../../events/index.js";
23
24
  import { generate, parse, t, visit } from "./babel.js";
24
25
  import { ensureImport } from "./imports.js";
25
26
  import { wrapDefaultExport } from "./wrapper.js";
@@ -65,8 +66,9 @@ async function getRegistry() {
65
66
  return "https://registry.npmjs.org";
66
67
  }
67
68
  }
68
- async function add(names, { cwd, flags, logging }) {
69
+ async function add(names, { flags, logging }) {
69
70
  var _a;
71
+ telemetry.record(eventCliSession("add"));
70
72
  applyPolyfill();
71
73
  if (flags.help || names.length === 0) {
72
74
  printHelp({
@@ -106,6 +108,7 @@ async function add(names, { cwd, flags, logging }) {
106
108
  });
107
109
  return;
108
110
  }
111
+ const cwd = flags.root;
109
112
  const integrationNames = names.map((name) => ALIASES.has(name) ? ALIASES.get(name) : name);
110
113
  const integrations = await validateIntegrations(integrationNames);
111
114
  let installResult = await tryToInstallIntegrations({ integrations, cwd, flags, logging });
package/dist/cli/index.js CHANGED
@@ -1,14 +1,8 @@
1
1
  import * as colors from "kleur/colors";
2
2
  import yargs from "yargs-parser";
3
3
  import { ASTRO_VERSION } from "../core/constants.js";
4
- import { collectErrorMetadata } from "../core/errors/dev/index.js";
5
- import { createSafeError } from "../core/errors/index.js";
6
- import { debug } from "../core/logger/core.js";
7
- import { enableVerboseLogging, nodeLogDestination } from "../core/logger/node.js";
8
- import { formatErrorMessage, printHelp } from "../core/messages.js";
9
- import * as event from "../events/index.js";
10
- import { eventError, telemetry } from "../events/index.js";
11
- function printAstroHelp() {
4
+ async function printAstroHelp() {
5
+ const { printHelp } = await import("../core/messages.js");
12
6
  printHelp({
13
7
  commandName: "astro",
14
8
  usage: "[command] [...flags]",
@@ -64,21 +58,19 @@ function resolveCommand(flags) {
64
58
  }
65
59
  async function runCommand(cmd, flags) {
66
60
  var _a;
67
- const root = flags.root;
68
61
  switch (cmd) {
69
62
  case "help":
70
- printAstroHelp();
63
+ await printAstroHelp();
71
64
  return;
72
65
  case "version":
73
66
  printVersion();
74
67
  return;
75
68
  case "info": {
76
69
  const { printInfo } = await import("./info/index.js");
77
- await printInfo({ cwd: root, flags });
70
+ await printInfo({ flags });
78
71
  return;
79
72
  }
80
73
  case "docs": {
81
- telemetry.record(event.eventCliSession(cmd));
82
74
  const { docs } = await import("./docs/index.js");
83
75
  await docs({ flags });
84
76
  return;
@@ -90,6 +82,7 @@ async function runCommand(cmd, flags) {
90
82
  return;
91
83
  }
92
84
  }
85
+ const { enableVerboseLogging, nodeLogDestination } = await import("../core/logger/node.js");
93
86
  const logging = {
94
87
  dest: nodeLogDestination,
95
88
  level: "info"
@@ -105,10 +98,9 @@ async function runCommand(cmd, flags) {
105
98
  }
106
99
  switch (cmd) {
107
100
  case "add": {
108
- telemetry.record(event.eventCliSession(cmd));
109
101
  const { add } = await import("./add/index.js");
110
102
  const packages = flags._.slice(3);
111
- await add(packages, { cwd: root, flags, logging });
103
+ await add(packages, { flags, logging });
112
104
  return;
113
105
  }
114
106
  case "dev": {
@@ -142,7 +134,7 @@ async function runCommand(cmd, flags) {
142
134
  return await new Promise(() => {
143
135
  });
144
136
  } else {
145
- let checkResult = await checkServer.check();
137
+ const checkResult = await checkServer.check();
146
138
  return process.exit(checkResult);
147
139
  }
148
140
  }
@@ -162,22 +154,10 @@ async function cli(args) {
162
154
  try {
163
155
  await runCommand(cmd, flags);
164
156
  } catch (err) {
157
+ const { throwAndExit } = await import("./throw-and-exit.js");
165
158
  await throwAndExit(cmd, err);
166
159
  }
167
160
  }
168
- async function throwAndExit(cmd, err) {
169
- let telemetryPromise;
170
- let errorMessage;
171
- function exitWithErrorMessage() {
172
- console.error(errorMessage);
173
- process.exit(1);
174
- }
175
- const errorWithMetadata = collectErrorMetadata(createSafeError(err));
176
- telemetryPromise = telemetry.record(eventError({ cmd, err: errorWithMetadata, isFatal: true }));
177
- errorMessage = formatErrorMessage(errorWithMetadata);
178
- setTimeout(exitWithErrorMessage, 400);
179
- await telemetryPromise.catch((err2) => debug("telemetry", `record() error: ${err2.message}`)).then(exitWithErrorMessage);
180
- }
181
161
  export {
182
162
  cli
183
163
  };
@@ -1,7 +1,6 @@
1
1
  import type yargs from 'yargs-parser';
2
2
  interface InfoOptions {
3
- cwd?: string;
4
3
  flags: yargs.Arguments;
5
4
  }
6
- export declare function printInfo({ cwd, flags }: InfoOptions): Promise<void>;
5
+ export declare function printInfo({ flags }: InfoOptions): Promise<void>;
7
6
  export {};
@@ -3,7 +3,7 @@ import { arch, platform } from "node:os";
3
3
  import whichPm from "which-pm";
4
4
  import { openConfig } from "../../core/config/index.js";
5
5
  import { ASTRO_VERSION } from "../../core/constants.js";
6
- async function printInfo({ cwd, flags }) {
6
+ async function printInfo({ flags }) {
7
7
  var _a;
8
8
  const packageManager = await whichPm(process.cwd());
9
9
  let adapter = "Couldn't determine.";
@@ -15,7 +15,7 @@ async function printInfo({ cwd, flags }) {
15
15
  }
16
16
  try {
17
17
  const { userConfig } = await openConfig({
18
- cwd,
18
+ cwd: flags.root,
19
19
  flags,
20
20
  cmd: "info"
21
21
  });
@@ -0,0 +1,2 @@
1
+ /** Display error and exit */
2
+ export declare function throwAndExit(cmd: string, err: unknown): Promise<void>;
@@ -0,0 +1,21 @@
1
+ import { collectErrorMetadata } from "../core/errors/dev/index.js";
2
+ import { createSafeError } from "../core/errors/index.js";
3
+ import { debug } from "../core/logger/core.js";
4
+ import { formatErrorMessage } from "../core/messages.js";
5
+ import { eventError, telemetry } from "../events/index.js";
6
+ async function throwAndExit(cmd, err) {
7
+ let telemetryPromise;
8
+ let errorMessage;
9
+ function exitWithErrorMessage() {
10
+ console.error(errorMessage);
11
+ process.exit(1);
12
+ }
13
+ const errorWithMetadata = collectErrorMetadata(createSafeError(err));
14
+ telemetryPromise = telemetry.record(eventError({ cmd, err: errorWithMetadata, isFatal: true }));
15
+ errorMessage = formatErrorMessage(errorWithMetadata);
16
+ setTimeout(exitWithErrorMessage, 400);
17
+ await telemetryPromise.catch((err2) => debug("telemetry", `record() error: ${err2.message}`)).then(exitWithErrorMessage);
18
+ }
19
+ export {
20
+ throwAndExit
21
+ };
@@ -1,3 +1,3 @@
1
1
  import type { PluginContext } from 'rollup';
2
2
  import { z } from 'zod';
3
- export declare function createImage(pluginContext: PluginContext, entryFilePath: string): () => z.ZodEffects<z.ZodString, import("../assets/utils/metadata.js").Metadata | z.ZodNever, string>;
3
+ export declare function createImage(pluginContext: PluginContext, entryFilePath: string): () => z.ZodEffects<z.ZodString, import("../assets/types.js").ImageMetadata | z.ZodNever, string>;
@@ -461,9 +461,16 @@ async function generatePath(pathname, opts, gopts, manifest, onRequest) {
461
461
  return;
462
462
  }
463
463
  const location = getRedirectLocationOrThrow(response.headers);
464
+ const fromPath = new URL(renderContext.request.url).pathname;
465
+ const delay = response.status === 302 ? 2 : 0;
464
466
  body = `<!doctype html>
465
467
  <title>Redirecting to: ${location}</title>
466
- <meta http-equiv="refresh" content="0;url=${location}" />`;
468
+ <meta http-equiv="refresh" content="${delay};url=${location}">
469
+ <meta name="robots" content="noindex">
470
+ <link rel="canonical" href="${location}">
471
+ <body>
472
+ <a href="${location}">Redirecting from <code>${fromPath}</code> to <code>${location}</code></a>
473
+ </body>`;
467
474
  if (pageData.route.type !== "redirect") {
468
475
  pageData.route.redirect = location;
469
476
  }
@@ -19,6 +19,7 @@ import { isServerLikeOutput } from "../../prerender/utils.js";
19
19
  import { PAGE_SCRIPT_ID } from "../../vite-plugin-scripts/index.js";
20
20
  import { AstroError, AstroErrorData } from "../errors/index.js";
21
21
  import { info } from "../logger/core.js";
22
+ import { routeIsRedirect } from "../redirects/index.js";
22
23
  import { getOutDirWithinCwd } from "./common.js";
23
24
  import { generatePages } from "./generate.js";
24
25
  import { trackPageData } from "./internal.js";
@@ -43,8 +44,10 @@ async function viteBuild(opts) {
43
44
  const astroModuleURL = new URL("./" + component, settings.config.root);
44
45
  const astroModuleId = prependForwardSlash(component);
45
46
  trackPageData(internals, component, pageData, astroModuleId, astroModuleURL);
46
- pageInput.add(astroModuleId);
47
- facadeIdToPageDataMap.set(fileURLToPath(astroModuleURL), pageData);
47
+ if (!routeIsRedirect(pageData.route)) {
48
+ pageInput.add(astroModuleId);
49
+ facadeIdToPageDataMap.set(fileURLToPath(astroModuleURL), pageData);
50
+ }
48
51
  }
49
52
  if (((_c = (_b = (_a = settings.config) == null ? void 0 : _a.vite) == null ? void 0 : _b.build) == null ? void 0 : _c.emptyOutDir) !== false) {
50
53
  emptyDir(settings.config.outDir, new Set(".git"));
@@ -3,6 +3,7 @@ import * as colors from "kleur/colors";
3
3
  import path from "path";
4
4
  import { fileURLToPath, pathToFileURL } from "url";
5
5
  import { AstroError, AstroErrorData } from "../errors/index.js";
6
+ import { mergeConfig } from "./merge.js";
6
7
  import { createRelativeSchema } from "./schema.js";
7
8
  import { loadConfigWithVite } from "./vite-load.js";
8
9
  const LEGACY_ASTRO_CONFIG_KEYS = /* @__PURE__ */ new Set([
@@ -84,25 +85,18 @@ function resolveRoot(cwd) {
84
85
  return cwd ? path.resolve(cwd) : process.cwd();
85
86
  }
86
87
  function mergeCLIFlags(astroConfig, flags) {
87
- astroConfig.server = astroConfig.server || {};
88
- astroConfig.markdown = astroConfig.markdown || {};
89
- astroConfig.experimental = astroConfig.experimental || {};
90
- if (typeof flags.site === "string")
91
- astroConfig.site = flags.site;
92
- if (typeof flags.base === "string")
93
- astroConfig.base = flags.base;
94
- if (typeof flags.drafts === "boolean")
95
- astroConfig.markdown.drafts = flags.drafts;
96
- if (typeof flags.port === "number") {
97
- astroConfig.server.port = flags.port;
98
- }
99
- if (typeof flags.host === "string" || typeof flags.host === "boolean") {
100
- astroConfig.server.host = flags.host;
101
- }
102
- if (typeof flags.open === "boolean") {
103
- astroConfig.server.open = flags.open;
104
- }
105
- return astroConfig;
88
+ return mergeConfig(astroConfig, {
89
+ site: flags.site,
90
+ base: flags.base,
91
+ markdown: {
92
+ drafts: flags.drafts
93
+ },
94
+ server: {
95
+ port: flags.port,
96
+ host: flags.host,
97
+ open: flags.open
98
+ }
99
+ });
106
100
  }
107
101
  async function search(fsMod, root) {
108
102
  const paths = [
@@ -16,6 +16,16 @@ function mergeConfigRecursively(defaults, overrides, rootPath) {
16
16
  merged[key] = mergeViteConfig(existing, value);
17
17
  continue;
18
18
  }
19
+ if (key === "server" && rootPath === "") {
20
+ if (typeof existing === "function" || typeof value === "function") {
21
+ merged[key] = (...args) => {
22
+ const existingConfig = typeof existing === "function" ? existing(...args) : existing;
23
+ const valueConfig = typeof value === "function" ? value(...args) : value;
24
+ return mergeConfigRecursively(existingConfig, valueConfig, key);
25
+ };
26
+ continue;
27
+ }
28
+ }
19
29
  if (Array.isArray(existing) || Array.isArray(value)) {
20
30
  merged[key] = [...arraify(existing ?? []), ...arraify(value ?? [])];
21
31
  continue;
@@ -171,12 +171,7 @@ function createRelativeSchema(cmd, fileProtocolRoot) {
171
171
  // preprocess
172
172
  (val) => {
173
173
  if (typeof val === "function") {
174
- const result = val({ command: cmd === "dev" ? "dev" : "preview" });
175
- if (val.port)
176
- result.port = val.port;
177
- if (val.host)
178
- result.host = val.host;
179
- return result;
174
+ return val({ command: cmd === "dev" ? "dev" : "preview" });
180
175
  } else {
181
176
  return val;
182
177
  }
@@ -1,4 +1,4 @@
1
- const ASTRO_VERSION = "2.8.2";
1
+ const ASTRO_VERSION = "2.8.3";
2
2
  const SUPPORTED_MARKDOWN_FILE_EXTENSIONS = [
3
3
  ".markdown",
4
4
  ".mdown",
@@ -54,7 +54,7 @@ async function dev(settings, options) {
54
54
  isRestart: options.isRestart
55
55
  })
56
56
  );
57
- const currentVersion = "2.8.2";
57
+ const currentVersion = "2.8.3";
58
58
  if (currentVersion.includes("-")) {
59
59
  warn(options.logging, null, msg.prerelease({ currentVersion }));
60
60
  }
@@ -438,7 +438,7 @@ export declare const AstroErrorData: {
438
438
  readonly UnsupportedImageFormat: {
439
439
  readonly title: "Unsupported image format";
440
440
  readonly message: (format: string, imagePath: string, supportedFormats: readonly string[]) => string;
441
- readonly hint: "If you do not need optimization, using an `img` tag directly instead of the `Image` component might be what you're looking for.";
441
+ readonly hint: "Using an `img` tag directly instead of the `Image` component might be what you're looking for.";
442
442
  };
443
443
  /**
444
444
  * @docs
@@ -452,8 +452,8 @@ Expected \`${defaultExpectedValue}\` value but got \`${suffix}\`.`;
452
452
  title: "Unsupported image format",
453
453
  message: (format, imagePath, supportedFormats) => `Received unsupported format \`${format}\` from \`${imagePath}\`. Currently only ${supportedFormats.join(
454
454
  ", "
455
- )} are supported for optimization.`,
456
- hint: "If you do not need optimization, using an `img` tag directly instead of the `Image` component might be what you're looking for."
455
+ )} are supported by our image services.`,
456
+ hint: "Using an `img` tag directly instead of the `Image` component might be what you're looking for."
457
457
  },
458
458
  /**
459
459
  * @docs
@@ -47,7 +47,7 @@ function serverStart({
47
47
  base,
48
48
  isRestart = false
49
49
  }) {
50
- const version = "2.8.2";
50
+ const version = "2.8.3";
51
51
  const localPrefix = `${dim("\u2503")} Local `;
52
52
  const networkPrefix = `${dim("\u2503")} Network `;
53
53
  const emptyPrefix = " ".repeat(11);
@@ -233,7 +233,7 @@ function printHelp({
233
233
  message.push(
234
234
  linebreak(),
235
235
  ` ${bgGreen(black(` ${commandName} `))} ${green(
236
- `v${"2.8.2"}`
236
+ `v${"2.8.3"}`
237
237
  )} ${headline}`
238
238
  );
239
239
  }
@@ -313,6 +313,25 @@ This route collides with: "${collision.component}".`
313
313
  redirect: to,
314
314
  redirectRoute: routes.find((r) => r.route === to)
315
315
  };
316
+ const lastSegmentIsDynamic = (r) => {
317
+ var _a2, _b;
318
+ return !!((_b = (_a2 = r.segments.at(-1)) == null ? void 0 : _a2.at(-1)) == null ? void 0 : _b.dynamic);
319
+ };
320
+ const redirBase = path.posix.dirname(route);
321
+ const dynamicRedir = lastSegmentIsDynamic(routeData);
322
+ let i = 0;
323
+ for (const existingRoute of routes) {
324
+ if (existingRoute.route === route) {
325
+ routes.splice(i + 1, 0, routeData);
326
+ return;
327
+ }
328
+ const base = path.posix.dirname(existingRoute.route);
329
+ if (base === redirBase && !dynamicRedir && lastSegmentIsDynamic(existingRoute)) {
330
+ routes.splice(i, 0, routeData);
331
+ return;
332
+ }
333
+ i++;
334
+ }
316
335
  routes.push(routeData);
317
336
  });
318
337
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "2.8.2",
3
+ "version": "2.8.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",