astro 4.1.3 → 4.2.1
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.
- package/dist/@types/astro.d.ts +101 -0
- package/dist/cli/preferences/index.js +1 -4
- package/dist/content/types-generator.js +0 -26
- package/dist/content/utils.d.ts +1 -1
- package/dist/content/utils.js +3 -10
- package/dist/core/app/createOutgoingHttpHeaders.d.ts +9 -0
- package/dist/core/app/createOutgoingHttpHeaders.js +19 -0
- package/dist/core/app/index.d.ts +38 -1
- package/dist/core/app/index.js +38 -8
- package/dist/core/app/node.d.ts +42 -10
- package/dist/core/app/node.js +87 -46
- package/dist/core/app/types.d.ts +2 -1
- package/dist/core/build/generate.js +1 -2
- package/dist/core/config/schema.d.ts +146 -66
- package/dist/core/config/schema.js +24 -6
- package/dist/core/config/vite-load.js +1 -1
- package/dist/core/constants.js +1 -1
- package/dist/core/dev/dev.js +1 -1
- package/dist/core/endpoint/index.d.ts +2 -1
- package/dist/core/errors/dev/vite.js +5 -11
- package/dist/core/errors/overlay.js +2 -2
- package/dist/core/logger/node.js +1 -1
- package/dist/core/logger/vite.d.ts +0 -1
- package/dist/core/logger/vite.js +1 -2
- package/dist/core/messages.js +2 -2
- package/dist/core/render/context.d.ts +3 -2
- package/dist/core/render/context.js +1 -1
- package/dist/core/render/result.d.ts +2 -1
- package/dist/core/routing/manifest/create.d.ts +1 -1
- package/dist/core/routing/manifest/create.js +166 -116
- package/dist/core/sync/index.js +1 -1
- package/dist/i18n/index.d.ts +3 -2
- package/dist/i18n/index.js +4 -4
- package/dist/i18n/middleware.js +35 -19
- package/dist/prefetch/index.js +17 -1
- package/dist/prefetch/vite-plugin-prefetch.js +4 -1
- package/dist/runtime/client/dev-toolbar/apps/audit/a11y.js +52 -4
- package/dist/runtime/server/render/util.js +1 -1
- package/dist/vite-plugin-astro/compile.js +0 -4
- package/dist/vite-plugin-astro/hmr.d.ts +3 -2
- package/dist/vite-plugin-astro/hmr.js +20 -41
- package/dist/vite-plugin-astro/index.js +24 -1
- package/dist/vite-plugin-astro/query.d.ts +0 -1
- package/dist/vite-plugin-astro/query.js +0 -5
- package/dist/vite-plugin-markdown/images.js +46 -19
- package/package.json +6 -5
|
@@ -70,10 +70,6 @@ function getTrailingSlashPattern(addTrailingSlash) {
|
|
|
70
70
|
}
|
|
71
71
|
return "\\/?$";
|
|
72
72
|
}
|
|
73
|
-
function isSpread(str) {
|
|
74
|
-
const spreadPattern = /\[\.{3}/g;
|
|
75
|
-
return spreadPattern.test(str);
|
|
76
|
-
}
|
|
77
73
|
function validateSegment(segment, file = "") {
|
|
78
74
|
if (!file)
|
|
79
75
|
file = segment;
|
|
@@ -87,59 +83,50 @@ function validateSegment(segment, file = "") {
|
|
|
87
83
|
throw new Error(`Invalid route ${file} \u2014 rest parameter must be a standalone segment`);
|
|
88
84
|
}
|
|
89
85
|
}
|
|
90
|
-
function
|
|
91
|
-
if (
|
|
92
|
-
|
|
93
|
-
return isSpread(a.file) ? 1 : -1;
|
|
94
|
-
return isSpread(b.file) ? -1 : 1;
|
|
86
|
+
function isSemanticallyEqualSegment(segmentA, segmentB) {
|
|
87
|
+
if (segmentA.length !== segmentB.length) {
|
|
88
|
+
return false;
|
|
95
89
|
}
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (!aSubPart)
|
|
101
|
-
return 1;
|
|
102
|
-
if (!bSubPart)
|
|
103
|
-
return -1;
|
|
104
|
-
if (aSubPart.spread && bSubPart.spread) {
|
|
105
|
-
return a.isIndex ? 1 : -1;
|
|
106
|
-
}
|
|
107
|
-
if (aSubPart.spread !== bSubPart.spread)
|
|
108
|
-
return aSubPart.spread ? 1 : -1;
|
|
109
|
-
if (aSubPart.dynamic !== bSubPart.dynamic) {
|
|
110
|
-
return aSubPart.dynamic ? 1 : -1;
|
|
90
|
+
for (const [index, partA] of segmentA.entries()) {
|
|
91
|
+
const partB = segmentB[index];
|
|
92
|
+
if (partA.dynamic !== partB.dynamic || partA.spread !== partB.spread) {
|
|
93
|
+
return false;
|
|
111
94
|
}
|
|
112
|
-
if (!
|
|
113
|
-
return
|
|
95
|
+
if (!partA.dynamic && partA.content !== partB.content) {
|
|
96
|
+
return false;
|
|
114
97
|
}
|
|
115
98
|
}
|
|
116
|
-
|
|
117
|
-
return a.isPage ? 1 : -1;
|
|
118
|
-
}
|
|
119
|
-
return a.file < b.file ? -1 : 1;
|
|
99
|
+
return true;
|
|
120
100
|
}
|
|
121
|
-
function
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
resolved = fileURLToPath(new URL(entrypoint, config.root));
|
|
101
|
+
function routeComparator(a, b) {
|
|
102
|
+
const aLength = a.isIndex ? a.segments.length + 1 : a.segments.length;
|
|
103
|
+
const bLength = b.isIndex ? b.segments.length + 1 : b.segments.length;
|
|
104
|
+
if (aLength !== bLength) {
|
|
105
|
+
return aLength > bLength ? -1 : 1;
|
|
127
106
|
}
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
107
|
+
const aIsStatic = a.segments.every(
|
|
108
|
+
(segment) => segment.every((part) => !part.dynamic && !part.spread)
|
|
109
|
+
);
|
|
110
|
+
const bIsStatic = b.segments.every(
|
|
111
|
+
(segment) => segment.every((part) => !part.dynamic && !part.spread)
|
|
112
|
+
);
|
|
113
|
+
if (aIsStatic !== bIsStatic) {
|
|
114
|
+
return aIsStatic ? -1 : 1;
|
|
115
|
+
}
|
|
116
|
+
const aHasSpread = a.segments.some((segment) => segment.some((part) => part.spread));
|
|
117
|
+
const bHasSpread = b.segments.some((segment) => segment.some((part) => part.spread));
|
|
118
|
+
if (aHasSpread !== bHasSpread) {
|
|
119
|
+
return aHasSpread ? 1 : -1;
|
|
120
|
+
}
|
|
121
|
+
if (a.prerender !== b.prerender) {
|
|
122
|
+
return a.prerender ? -1 : 1;
|
|
123
|
+
}
|
|
124
|
+
if (a.type === "endpoint" !== (b.type === "endpoint")) {
|
|
125
|
+
return a.type === "endpoint" ? -1 : 1;
|
|
126
|
+
}
|
|
127
|
+
return a.route.localeCompare(b.route);
|
|
141
128
|
}
|
|
142
|
-
function
|
|
129
|
+
function createFileBasedRoutes({ settings, cwd, fsMod }, logger) {
|
|
143
130
|
const components = [];
|
|
144
131
|
const routes = [];
|
|
145
132
|
const validPageExtensions = /* @__PURE__ */ new Set([
|
|
@@ -152,17 +139,18 @@ function createRouteManifest({ settings, cwd, fsMod }, logger) {
|
|
|
152
139
|
const prerender = getPrerenderDefault(settings.config);
|
|
153
140
|
function walk(fs, dir, parentSegments, parentParams) {
|
|
154
141
|
let items = [];
|
|
155
|
-
fs.readdirSync(dir)
|
|
142
|
+
const files = fs.readdirSync(dir);
|
|
143
|
+
for (const basename of files) {
|
|
156
144
|
const resolved = path.join(dir, basename);
|
|
157
145
|
const file = slash(path.relative(cwd || fileURLToPath(settings.config.root), resolved));
|
|
158
146
|
const isDir = fs.statSync(resolved).isDirectory();
|
|
159
147
|
const ext = path.extname(basename);
|
|
160
148
|
const name = ext ? basename.slice(0, -ext.length) : basename;
|
|
161
149
|
if (name[0] === "_") {
|
|
162
|
-
|
|
150
|
+
continue;
|
|
163
151
|
}
|
|
164
152
|
if (basename[0] === "." && basename !== ".well-known") {
|
|
165
|
-
|
|
153
|
+
continue;
|
|
166
154
|
}
|
|
167
155
|
if (!isDir && !validPageExtensions.has(ext) && !validEndpointExtensions.has(ext)) {
|
|
168
156
|
logger.warn(
|
|
@@ -171,7 +159,7 @@ function createRouteManifest({ settings, cwd, fsMod }, logger) {
|
|
|
171
159
|
resolved
|
|
172
160
|
)} found. Prefix filename with an underscore (\`_\`) to ignore.`
|
|
173
161
|
);
|
|
174
|
-
|
|
162
|
+
continue;
|
|
175
163
|
}
|
|
176
164
|
const segment = isDir ? basename : name;
|
|
177
165
|
validateSegment(segment, file);
|
|
@@ -189,9 +177,8 @@ function createRouteManifest({ settings, cwd, fsMod }, logger) {
|
|
|
189
177
|
isPage,
|
|
190
178
|
routeSuffix
|
|
191
179
|
});
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
items.forEach((item) => {
|
|
180
|
+
}
|
|
181
|
+
for (const item of items) {
|
|
195
182
|
const segments = parentSegments.slice();
|
|
196
183
|
if (item.isIndex) {
|
|
197
184
|
if (item.routeSuffix) {
|
|
@@ -233,6 +220,7 @@ function createRouteManifest({ settings, cwd, fsMod }, logger) {
|
|
|
233
220
|
const route = `/${segments.map(([{ dynamic, content }]) => dynamic ? `[${content}]` : content).join("/")}`.toLowerCase();
|
|
234
221
|
routes.push({
|
|
235
222
|
route,
|
|
223
|
+
isIndex: item.isIndex,
|
|
236
224
|
type: item.isPage ? "page" : "endpoint",
|
|
237
225
|
pattern,
|
|
238
226
|
segments,
|
|
@@ -244,7 +232,7 @@ function createRouteManifest({ settings, cwd, fsMod }, logger) {
|
|
|
244
232
|
fallbackRoutes: []
|
|
245
233
|
});
|
|
246
234
|
}
|
|
247
|
-
}
|
|
235
|
+
}
|
|
248
236
|
}
|
|
249
237
|
const { config } = settings;
|
|
250
238
|
const pages = resolvePages(config);
|
|
@@ -254,12 +242,18 @@ function createRouteManifest({ settings, cwd, fsMod }, logger) {
|
|
|
254
242
|
const pagesDirRootRelative = pages.href.slice(settings.config.root.href.length);
|
|
255
243
|
logger.warn(null, `Missing pages directory: ${pagesDirRootRelative}`);
|
|
256
244
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
245
|
+
return routes;
|
|
246
|
+
}
|
|
247
|
+
function createInjectedRoutes({ settings, cwd }) {
|
|
248
|
+
const { config } = settings;
|
|
249
|
+
const prerender = getPrerenderDefault(config);
|
|
250
|
+
const routes = {
|
|
251
|
+
normal: [],
|
|
252
|
+
legacy: []
|
|
253
|
+
};
|
|
254
|
+
const priority = computeRoutePriority(config);
|
|
255
|
+
for (const injectedRoute of settings.injectedRoutes) {
|
|
256
|
+
const { pattern: name, entrypoint, prerender: prerenderInjected } = injectedRoute;
|
|
263
257
|
let resolved;
|
|
264
258
|
try {
|
|
265
259
|
resolved = require2.resolve(entrypoint, { paths: [cwd || fileURLToPath(config.root)] });
|
|
@@ -279,15 +273,10 @@ function createRouteManifest({ settings, cwd, fsMod }, logger) {
|
|
|
279
273
|
const pathname = segments.every((segment) => segment.length === 1 && !segment[0].dynamic) ? `/${segments.map((segment) => segment[0].content).join("/")}` : null;
|
|
280
274
|
const params = segments.flat().filter((p) => p.dynamic).map((p) => p.content);
|
|
281
275
|
const route = `/${segments.map(([{ dynamic, content }]) => dynamic ? `[${content}]` : content).join("/")}`.toLowerCase();
|
|
282
|
-
|
|
283
|
-
if (collision) {
|
|
284
|
-
throw new Error(
|
|
285
|
-
`An integration attempted to inject a route that is already used in your project: "${route}" at "${component}".
|
|
286
|
-
This route collides with: "${collision.component}".`
|
|
287
|
-
);
|
|
288
|
-
}
|
|
289
|
-
routes.unshift({
|
|
276
|
+
routes[priority].push({
|
|
290
277
|
type,
|
|
278
|
+
// For backwards compatibility, an injected route is never considered an index route.
|
|
279
|
+
isIndex: false,
|
|
291
280
|
route,
|
|
292
281
|
pattern,
|
|
293
282
|
segments,
|
|
@@ -298,9 +287,18 @@ This route collides with: "${collision.component}".`
|
|
|
298
287
|
prerender: prerenderInjected ?? prerender,
|
|
299
288
|
fallbackRoutes: []
|
|
300
289
|
});
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
|
|
290
|
+
}
|
|
291
|
+
return routes;
|
|
292
|
+
}
|
|
293
|
+
function createRedirectRoutes({ settings }, routeMap, logger) {
|
|
294
|
+
const { config } = settings;
|
|
295
|
+
const trailingSlash = config.trailingSlash;
|
|
296
|
+
const routes = {
|
|
297
|
+
normal: [],
|
|
298
|
+
legacy: []
|
|
299
|
+
};
|
|
300
|
+
const priority = computeRoutePriority(settings.config);
|
|
301
|
+
for (const [from, to] of Object.entries(settings.config.redirects)) {
|
|
304
302
|
const segments = removeLeadingForwardSlash(from).split(path.posix.sep).filter(Boolean).map((s) => {
|
|
305
303
|
validateSegment(s);
|
|
306
304
|
return getParts(s, from);
|
|
@@ -310,22 +308,22 @@ This route collides with: "${collision.component}".`
|
|
|
310
308
|
const pathname = segments.every((segment) => segment.length === 1 && !segment[0].dynamic) ? `/${segments.map((segment) => segment[0].content).join("/")}` : null;
|
|
311
309
|
const params = segments.flat().filter((p) => p.dynamic).map((p) => p.content);
|
|
312
310
|
const route = `/${segments.map(([{ dynamic, content }]) => dynamic ? `[${content}]` : content).join("/")}`.toLowerCase();
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
);
|
|
325
|
-
}
|
|
311
|
+
let destination;
|
|
312
|
+
if (typeof to === "string") {
|
|
313
|
+
destination = to;
|
|
314
|
+
} else {
|
|
315
|
+
destination = to.destination;
|
|
316
|
+
}
|
|
317
|
+
if (/^https?:\/\//.test(destination)) {
|
|
318
|
+
logger.warn(
|
|
319
|
+
"redirects",
|
|
320
|
+
`Redirecting to an external URL is not officially supported: ${from} -> ${destination}`
|
|
321
|
+
);
|
|
326
322
|
}
|
|
327
|
-
|
|
323
|
+
routes[priority].push({
|
|
328
324
|
type: "redirect",
|
|
325
|
+
// For backwards compatibility, a redirect is never considered an index route.
|
|
326
|
+
isIndex: false,
|
|
329
327
|
route,
|
|
330
328
|
pattern,
|
|
331
329
|
segments,
|
|
@@ -335,36 +333,82 @@ This route collides with: "${collision.component}".`
|
|
|
335
333
|
pathname: pathname || void 0,
|
|
336
334
|
prerender: false,
|
|
337
335
|
redirect: to,
|
|
338
|
-
redirectRoute:
|
|
339
|
-
if (typeof to === "object") {
|
|
340
|
-
return r.route === to.destination;
|
|
341
|
-
} else {
|
|
342
|
-
return r.route === to;
|
|
343
|
-
}
|
|
344
|
-
}),
|
|
336
|
+
redirectRoute: routeMap.get(destination),
|
|
345
337
|
fallbackRoutes: []
|
|
346
|
-
};
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
return routes;
|
|
341
|
+
}
|
|
342
|
+
function isStaticSegment(segment) {
|
|
343
|
+
return segment.every((part) => !part.dynamic && !part.spread);
|
|
344
|
+
}
|
|
345
|
+
function detectRouteCollision(a, b, config, logger) {
|
|
346
|
+
if (a.type === "fallback" || b.type === "fallback") {
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
if (a.route === b.route && a.segments.every(isStaticSegment) && b.segments.every(isStaticSegment)) {
|
|
350
|
+
logger.warn(
|
|
351
|
+
"router",
|
|
352
|
+
`The route "${a.route}" is defined in both "${a.component}" and "${b.component}". A static route cannot be defined more than once.`
|
|
353
|
+
);
|
|
354
|
+
logger.warn(
|
|
355
|
+
"router",
|
|
356
|
+
"A collision will result in an hard error in following versions of Astro."
|
|
357
|
+
);
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
if (a.prerender || b.prerender) {
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
if (a.segments.length !== b.segments.length) {
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
const segmentCount = a.segments.length;
|
|
367
|
+
for (let index = 0; index < segmentCount; index++) {
|
|
368
|
+
const segmentA = a.segments[index];
|
|
369
|
+
const segmentB = b.segments[index];
|
|
370
|
+
if (!isSemanticallyEqualSegment(segmentA, segmentB)) {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
logger.warn(
|
|
375
|
+
"router",
|
|
376
|
+
`The route "${a.route}" is defined in both "${a.component}" and "${b.component}" using SSR mode. A dynamic SSR route cannot be defined more than once.`
|
|
377
|
+
);
|
|
378
|
+
logger.warn("router", "A collision will result in an hard error in following versions of Astro.");
|
|
379
|
+
}
|
|
380
|
+
function createRouteManifest(params, logger) {
|
|
381
|
+
const { settings } = params;
|
|
382
|
+
const { config } = settings;
|
|
383
|
+
const routeMap = /* @__PURE__ */ new Map();
|
|
384
|
+
const fileBasedRoutes = createFileBasedRoutes(params, logger);
|
|
385
|
+
for (const route of fileBasedRoutes) {
|
|
386
|
+
routeMap.set(route.route, route);
|
|
387
|
+
}
|
|
388
|
+
const injectedRoutes = createInjectedRoutes(params);
|
|
389
|
+
for (const [, routes2] of Object.entries(injectedRoutes)) {
|
|
390
|
+
for (const route of routes2) {
|
|
391
|
+
routeMap.set(route.route, route);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
const redirectRoutes = createRedirectRoutes(params, routeMap, logger);
|
|
395
|
+
const routes = [
|
|
396
|
+
...injectedRoutes["legacy"].sort(routeComparator),
|
|
397
|
+
...[...fileBasedRoutes, ...injectedRoutes["normal"], ...redirectRoutes["normal"]].sort(
|
|
398
|
+
routeComparator
|
|
399
|
+
),
|
|
400
|
+
...redirectRoutes["legacy"].sort(routeComparator)
|
|
401
|
+
];
|
|
402
|
+
if (config.experimental.globalRoutePriority) {
|
|
403
|
+
for (const [index, higherRoute] of routes.entries()) {
|
|
404
|
+
for (const lowerRoute of routes.slice(index + 1)) {
|
|
405
|
+
detectRouteCollision(higherRoute, lowerRoute, config, logger);
|
|
360
406
|
}
|
|
361
|
-
i++;
|
|
362
407
|
}
|
|
363
|
-
|
|
364
|
-
});
|
|
408
|
+
}
|
|
365
409
|
const i18n = settings.config.i18n;
|
|
366
410
|
if (i18n) {
|
|
367
|
-
if (i18n.routing === "prefix-always") {
|
|
411
|
+
if (i18n.routing === "pathname-prefix-always") {
|
|
368
412
|
let index = routes.find((route) => route.route === "/");
|
|
369
413
|
if (!index) {
|
|
370
414
|
let relativePath = path.relative(
|
|
@@ -415,7 +459,7 @@ This route collides with: "${collision.component}".`
|
|
|
415
459
|
}
|
|
416
460
|
setRoutes.delete(route);
|
|
417
461
|
}
|
|
418
|
-
if (i18n.routing === "prefix-always") {
|
|
462
|
+
if (i18n.routing === "pathname-prefix-always") {
|
|
419
463
|
const defaultLocaleRoutes = routesByLocale.get(i18n.defaultLocale);
|
|
420
464
|
if (defaultLocaleRoutes) {
|
|
421
465
|
const indexDefaultRoute = defaultLocaleRoutes.find((routeData) => {
|
|
@@ -465,7 +509,7 @@ This route collides with: "${collision.component}".`
|
|
|
465
509
|
if (!hasRoute) {
|
|
466
510
|
let pathname;
|
|
467
511
|
let route;
|
|
468
|
-
if (fallbackToLocale === i18n.defaultLocale && i18n.routing === "prefix-other-locales") {
|
|
512
|
+
if (fallbackToLocale === i18n.defaultLocale && i18n.routing === "pathname-prefix-other-locales") {
|
|
469
513
|
if (fallbackToRoute.pathname) {
|
|
470
514
|
pathname = `/${fallbackFromLocale}${fallbackToRoute.pathname}`;
|
|
471
515
|
}
|
|
@@ -504,6 +548,12 @@ This route collides with: "${collision.component}".`
|
|
|
504
548
|
routes
|
|
505
549
|
};
|
|
506
550
|
}
|
|
551
|
+
function computeRoutePriority(config) {
|
|
552
|
+
if (config.experimental.globalRoutePriority) {
|
|
553
|
+
return "normal";
|
|
554
|
+
}
|
|
555
|
+
return "legacy";
|
|
556
|
+
}
|
|
507
557
|
export {
|
|
508
558
|
createRouteManifest
|
|
509
559
|
};
|
package/dist/core/sync/index.js
CHANGED
|
@@ -35,7 +35,7 @@ async function syncInternal(settings, { logger, fs }) {
|
|
|
35
35
|
await createVite(
|
|
36
36
|
{
|
|
37
37
|
server: { middlewareMode: true, hmr: false, watch: null },
|
|
38
|
-
optimizeDeps: {
|
|
38
|
+
optimizeDeps: { noDiscovery: true },
|
|
39
39
|
ssr: { external: [] },
|
|
40
40
|
logLevel: "silent"
|
|
41
41
|
},
|
package/dist/i18n/index.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { AstroConfig, Locales } from '../@types/astro.js';
|
|
2
|
+
import type { RoutingStrategies } from '../core/config/schema.js';
|
|
2
3
|
type GetLocaleRelativeUrl = GetLocaleOptions & {
|
|
3
4
|
locale: string;
|
|
4
5
|
base: string;
|
|
5
6
|
locales: Locales;
|
|
6
7
|
trailingSlash: AstroConfig['trailingSlash'];
|
|
7
8
|
format: AstroConfig['build']['format'];
|
|
8
|
-
routing?:
|
|
9
|
+
routing?: RoutingStrategies;
|
|
9
10
|
defaultLocale: string;
|
|
10
11
|
};
|
|
11
12
|
export type GetLocaleOptions = {
|
|
@@ -39,7 +40,7 @@ type GetLocalesBaseUrl = GetLocaleOptions & {
|
|
|
39
40
|
locales: Locales;
|
|
40
41
|
trailingSlash: AstroConfig['trailingSlash'];
|
|
41
42
|
format: AstroConfig['build']['format'];
|
|
42
|
-
routing?:
|
|
43
|
+
routing?: RoutingStrategies;
|
|
43
44
|
defaultLocale: string;
|
|
44
45
|
};
|
|
45
46
|
export declare function getLocaleRelativeUrlList({ base, locales: _locales, trailingSlash, format, path, prependWith, normalizeLocale, routing, defaultLocale, }: GetLocalesBaseUrl): string[];
|
package/dist/i18n/index.js
CHANGED
|
@@ -11,7 +11,7 @@ function getLocaleRelativeUrl({
|
|
|
11
11
|
path,
|
|
12
12
|
prependWith,
|
|
13
13
|
normalizeLocale = true,
|
|
14
|
-
routing = "prefix-other-locales",
|
|
14
|
+
routing = "pathname-prefix-other-locales",
|
|
15
15
|
defaultLocale
|
|
16
16
|
}) {
|
|
17
17
|
const codeToUse = peekCodePathToUse(_locales, locale);
|
|
@@ -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 === "prefix-always") {
|
|
26
|
+
if (routing === "pathname-prefix-always") {
|
|
27
27
|
pathsToJoin.push(normalizedLocale);
|
|
28
28
|
} else if (locale !== defaultLocale) {
|
|
29
29
|
pathsToJoin.push(normalizedLocale);
|
|
@@ -51,14 +51,14 @@ function getLocaleRelativeUrlList({
|
|
|
51
51
|
path,
|
|
52
52
|
prependWith,
|
|
53
53
|
normalizeLocale = false,
|
|
54
|
-
routing = "prefix-other-locales",
|
|
54
|
+
routing = "pathname-prefix-other-locales",
|
|
55
55
|
defaultLocale
|
|
56
56
|
}) {
|
|
57
57
|
const locales = toPaths(_locales);
|
|
58
58
|
return locales.map((locale) => {
|
|
59
59
|
const pathsToJoin = [base, prependWith];
|
|
60
60
|
const normalizedLocale = normalizeLocale ? normalizeTheLocale(locale) : locale;
|
|
61
|
-
if (routing === "prefix-always") {
|
|
61
|
+
if (routing === "pathname-prefix-always") {
|
|
62
62
|
pathsToJoin.push(normalizedLocale);
|
|
63
63
|
} else if (locale !== defaultLocale) {
|
|
64
64
|
pathsToJoin.push(normalizedLocale);
|
package/dist/i18n/middleware.js
CHANGED
|
@@ -35,25 +35,41 @@ function createI18nMiddleware(i18n, base, trailingSlash) {
|
|
|
35
35
|
const response = await next();
|
|
36
36
|
if (response instanceof Response) {
|
|
37
37
|
const pathnameContainsDefaultLocale = url.pathname.includes(`/${defaultLocale}`);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
38
|
+
switch (i18n.routing) {
|
|
39
|
+
case "pathname-prefix-other-locales": {
|
|
40
|
+
if (pathnameContainsDefaultLocale) {
|
|
41
|
+
const newLocation = url.pathname.replace(`/${defaultLocale}`, "");
|
|
42
|
+
response.headers.set("Location", newLocation);
|
|
43
|
+
return new Response(null, {
|
|
44
|
+
status: 404,
|
|
45
|
+
headers: response.headers
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
case "pathname-prefix-always-no-redirect": {
|
|
51
|
+
const isRoot = url.pathname === base + "/" || url.pathname === base;
|
|
52
|
+
if (!(isRoot || pathnameHasLocale(url.pathname, i18n.locales))) {
|
|
53
|
+
return new Response(null, {
|
|
54
|
+
status: 404,
|
|
55
|
+
headers: response.headers
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
case "pathname-prefix-always": {
|
|
61
|
+
if (url.pathname === base + "/" || url.pathname === base) {
|
|
62
|
+
if (trailingSlash === "always") {
|
|
63
|
+
return context.redirect(`${appendForwardSlash(joinPaths(base, i18n.defaultLocale))}`);
|
|
64
|
+
} else {
|
|
65
|
+
return context.redirect(`${joinPaths(base, i18n.defaultLocale)}`);
|
|
66
|
+
}
|
|
67
|
+
} else if (!pathnameHasLocale(url.pathname, i18n.locales)) {
|
|
68
|
+
return new Response(null, {
|
|
69
|
+
status: 404,
|
|
70
|
+
headers: response.headers
|
|
71
|
+
});
|
|
51
72
|
}
|
|
52
|
-
} else if (!pathnameHasLocale(url.pathname, i18n.locales)) {
|
|
53
|
-
return new Response(null, {
|
|
54
|
-
status: 404,
|
|
55
|
-
headers: response.headers
|
|
56
|
-
});
|
|
57
73
|
}
|
|
58
74
|
}
|
|
59
75
|
if (response.status >= 300 && fallback) {
|
|
@@ -75,7 +91,7 @@ function createI18nMiddleware(i18n, base, trailingSlash) {
|
|
|
75
91
|
const fallbackLocale = fallback[urlLocale];
|
|
76
92
|
const pathFallbackLocale = getPathByLocale(fallbackLocale, locales);
|
|
77
93
|
let newPathname;
|
|
78
|
-
if (pathFallbackLocale === defaultLocale && routing === "prefix-other-locales") {
|
|
94
|
+
if (pathFallbackLocale === defaultLocale && routing === "pathname-prefix-other-locales") {
|
|
79
95
|
newPathname = url.pathname.replace(`/${urlLocale}`, ``);
|
|
80
96
|
} else {
|
|
81
97
|
newPathname = url.pathname.replace(`/${urlLocale}`, `/${pathFallbackLocale}`);
|
package/dist/prefetch/index.js
CHANGED
|
@@ -4,6 +4,7 @@ const prefetchedUrls = /* @__PURE__ */ new Set();
|
|
|
4
4
|
const listenedAnchors = /* @__PURE__ */ new WeakSet();
|
|
5
5
|
let prefetchAll = __PREFETCH_PREFETCH_ALL__;
|
|
6
6
|
let defaultStrategy = __PREFETCH_DEFAULT_STRATEGY__;
|
|
7
|
+
let clientPrerender = __EXPERIMENTAL_CLIENT_PRERENDER__;
|
|
7
8
|
let inited = false;
|
|
8
9
|
function init(defaultOpts) {
|
|
9
10
|
if (!inBrowser)
|
|
@@ -128,7 +129,9 @@ function prefetch(url, opts) {
|
|
|
128
129
|
prefetchedUrls.add(url);
|
|
129
130
|
const priority = opts?.with ?? "link";
|
|
130
131
|
debug?.(`[astro] Prefetching ${url} with ${priority}`);
|
|
131
|
-
if (
|
|
132
|
+
if (clientPrerender && HTMLScriptElement.supports && HTMLScriptElement.supports("speculationrules")) {
|
|
133
|
+
appendSpeculationRules(url);
|
|
134
|
+
} else if (priority === "link") {
|
|
132
135
|
const link = document.createElement("link");
|
|
133
136
|
link.rel = "prefetch";
|
|
134
137
|
link.setAttribute("href", url);
|
|
@@ -188,6 +191,19 @@ function onPageLoad(cb) {
|
|
|
188
191
|
cb();
|
|
189
192
|
});
|
|
190
193
|
}
|
|
194
|
+
function appendSpeculationRules(url) {
|
|
195
|
+
const script = document.createElement("script");
|
|
196
|
+
script.type = "speculationrules";
|
|
197
|
+
script.textContent = JSON.stringify({
|
|
198
|
+
prerender: [
|
|
199
|
+
{
|
|
200
|
+
source: "list",
|
|
201
|
+
urls: [url]
|
|
202
|
+
}
|
|
203
|
+
]
|
|
204
|
+
});
|
|
205
|
+
document.head.append(script);
|
|
206
|
+
}
|
|
191
207
|
export {
|
|
192
208
|
init,
|
|
193
209
|
prefetch
|
|
@@ -32,7 +32,10 @@ function astroPrefetch({ settings }) {
|
|
|
32
32
|
},
|
|
33
33
|
transform(code, id) {
|
|
34
34
|
if (id.includes(prefetchInternalModuleFsSubpath)) {
|
|
35
|
-
return code.replace("__PREFETCH_PREFETCH_ALL__", JSON.stringify(prefetch?.prefetchAll)).replace("__PREFETCH_DEFAULT_STRATEGY__", JSON.stringify(prefetch?.defaultStrategy))
|
|
35
|
+
return code.replace("__PREFETCH_PREFETCH_ALL__", JSON.stringify(prefetch?.prefetchAll)).replace("__PREFETCH_DEFAULT_STRATEGY__", JSON.stringify(prefetch?.defaultStrategy)).replace(
|
|
36
|
+
"__EXPERIMENTAL_CLIENT_PRERENDER__",
|
|
37
|
+
JSON.stringify(settings.config.experimental.clientPrerender)
|
|
38
|
+
);
|
|
36
39
|
}
|
|
37
40
|
}
|
|
38
41
|
};
|
|
@@ -330,13 +330,61 @@ const a11y = [
|
|
|
330
330
|
},
|
|
331
331
|
{
|
|
332
332
|
code: "a11y-missing-content",
|
|
333
|
-
title: "Missing content
|
|
334
|
-
message: "Headings and anchors must have
|
|
333
|
+
title: "Missing content",
|
|
334
|
+
message: "Headings and anchors must have an accessible name, which can come from: inner text, aria-label, aria-labelledby, an img with alt property, or an svg with a tag <title></title>.",
|
|
335
335
|
selector: a11y_required_content.join(","),
|
|
336
336
|
match(element) {
|
|
337
337
|
const innerText = element.innerText.trim();
|
|
338
|
-
if (innerText
|
|
339
|
-
return
|
|
338
|
+
if (innerText !== "")
|
|
339
|
+
return false;
|
|
340
|
+
const ariaLabel = element.getAttribute("aria-label")?.trim();
|
|
341
|
+
if (ariaLabel && ariaLabel !== "")
|
|
342
|
+
return false;
|
|
343
|
+
const ariaLabelledby = element.getAttribute("aria-labelledby")?.trim();
|
|
344
|
+
if (ariaLabelledby) {
|
|
345
|
+
const ids = ariaLabelledby.split(" ");
|
|
346
|
+
for (const id of ids) {
|
|
347
|
+
const referencedElement = document.getElementById(id);
|
|
348
|
+
if (referencedElement && referencedElement.innerText.trim() !== "")
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
const imgElements = element.querySelectorAll("img");
|
|
353
|
+
for (const img of imgElements) {
|
|
354
|
+
const altAttribute = img.getAttribute("alt");
|
|
355
|
+
if (altAttribute && altAttribute.trim() !== "")
|
|
356
|
+
return false;
|
|
357
|
+
}
|
|
358
|
+
const svgElements = element.querySelectorAll("svg");
|
|
359
|
+
for (const svg of svgElements) {
|
|
360
|
+
const titleText = svg.querySelector("title");
|
|
361
|
+
if (titleText && titleText.textContent && titleText.textContent.trim() !== "")
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
364
|
+
const inputElements = element.querySelectorAll("input");
|
|
365
|
+
for (const input of inputElements) {
|
|
366
|
+
if (input.type === "image") {
|
|
367
|
+
const altAttribute = input.getAttribute("alt");
|
|
368
|
+
if (altAttribute && altAttribute.trim() !== "")
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
const inputAriaLabel = input.getAttribute("aria-label")?.trim();
|
|
372
|
+
if (inputAriaLabel && inputAriaLabel !== "")
|
|
373
|
+
return false;
|
|
374
|
+
const inputAriaLabelledby = input.getAttribute("aria-labelledby")?.trim();
|
|
375
|
+
if (inputAriaLabelledby) {
|
|
376
|
+
const ids = inputAriaLabelledby.split(" ");
|
|
377
|
+
for (const id of ids) {
|
|
378
|
+
const referencedElement = document.getElementById(id);
|
|
379
|
+
if (referencedElement && referencedElement.innerText.trim() !== "")
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
const title = input.getAttribute("title")?.trim();
|
|
384
|
+
if (title && title !== "")
|
|
385
|
+
return false;
|
|
386
|
+
}
|
|
387
|
+
return true;
|
|
340
388
|
}
|
|
341
389
|
},
|
|
342
390
|
{
|