@sebbro/paraglide-lite 0.1.1 → 0.1.2
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/bundler-plugins/per-locale-build/index.js +5 -3
- package/dist/bundler-plugins/per-locale-build/index.js.map +1 -1
- package/dist/bundler-plugins/per-locale-build/vite-environments.js +12 -7
- package/dist/bundler-plugins/per-locale-build/vite-environments.js.map +1 -1
- package/dist/bundler-plugins/plugin-core.d.ts +38 -2
- package/dist/bundler-plugins/plugin-core.js +198 -11
- package/dist/bundler-plugins/plugin-core.js.map +1 -1
- package/dist/bundler-plugins/rolldown.test.js +15 -6
- package/dist/bundler-plugins/rolldown.test.js.map +1 -1
- package/dist/bundler-plugins/unplugin.js +5 -0
- package/dist/bundler-plugins/unplugin.js.map +1 -1
- package/dist/bundler-plugins/unplugin.test.js +67 -0
- package/dist/bundler-plugins/unplugin.test.js.map +1 -1
- package/dist/bundler-plugins/virtual-modules.test.js +64 -1
- package/dist/bundler-plugins/virtual-modules.test.js.map +1 -1
- package/dist/cli/commands/compile/command.js +8 -1
- package/dist/cli/commands/compile/command.js.map +1 -1
- package/dist/cli/commands/compile/command.test.js +51 -0
- package/dist/cli/commands/compile/command.test.js.map +1 -1
- package/dist/compiler/compile-annotation.d.ts +2 -2
- package/dist/compiler/compile-annotation.js +9 -9
- package/dist/compiler/compile-annotation.js.map +1 -1
- package/dist/compiler/compile-local-variable.d.ts +2 -1
- package/dist/compiler/compile-local-variable.js +4 -4
- package/dist/compiler/compile-local-variable.js.map +1 -1
- package/dist/compiler/compile-local-variable.test.js +82 -0
- package/dist/compiler/compile-local-variable.test.js.map +1 -1
- package/dist/compiler/compile-message.js +10 -2
- package/dist/compiler/compile-message.js.map +1 -1
- package/dist/compiler/compile-message.test.js +132 -2
- package/dist/compiler/compile-message.test.js.map +1 -1
- package/dist/compiler/compile-pattern.js +4 -2
- package/dist/compiler/compile-pattern.js.map +1 -1
- package/dist/compiler/compile-pattern.test.js +31 -0
- package/dist/compiler/compile-pattern.test.js.map +1 -1
- package/dist/compiler/compile.bench.js +1 -1
- package/dist/compiler/compile.bench.js.map +1 -1
- package/dist/compiler/compile.test.js +14 -0
- package/dist/compiler/compile.test.js.map +1 -1
- package/dist/compiler/compiler-options.d.ts +9 -1
- package/dist/compiler/compiler-options.js.map +1 -1
- package/dist/compiler/runtime/create-runtime.d.ts +1 -0
- package/dist/compiler/runtime/create-runtime.js +11 -0
- package/dist/compiler/runtime/create-runtime.js.map +1 -1
- package/dist/compiler/runtime/exec-url-pattern.d.ts +9 -0
- package/dist/compiler/runtime/exec-url-pattern.js +31 -0
- package/dist/compiler/runtime/exec-url-pattern.js.map +1 -0
- package/dist/compiler/runtime/extract-locale-from-url.js +9 -4
- package/dist/compiler/runtime/extract-locale-from-url.js.map +1 -1
- package/dist/compiler/runtime/generate-static-localized-urls.js +13 -6
- package/dist/compiler/runtime/generate-static-localized-urls.js.map +1 -1
- package/dist/compiler/runtime/generate-static-localized-urls.test.js +38 -0
- package/dist/compiler/runtime/generate-static-localized-urls.test.js.map +1 -1
- package/dist/compiler/runtime/localize-href.bench.d.ts +1 -0
- package/dist/compiler/runtime/localize-href.bench.js +68 -0
- package/dist/compiler/runtime/localize-href.bench.js.map +1 -0
- package/dist/compiler/runtime/localize-href.test.js +35 -0
- package/dist/compiler/runtime/localize-href.test.js.map +1 -1
- package/dist/compiler/runtime/localize-url.d.ts +14 -0
- package/dist/compiler/runtime/localize-url.js +338 -19
- package/dist/compiler/runtime/localize-url.js.map +1 -1
- package/dist/compiler/runtime/localize-url.test.js +139 -0
- package/dist/compiler/runtime/localize-url.test.js.map +1 -1
- package/dist/compiler/runtime/normalize-trailing-slash.d.ts +10 -0
- package/dist/compiler/runtime/normalize-trailing-slash.js +23 -0
- package/dist/compiler/runtime/normalize-trailing-slash.js.map +1 -0
- package/dist/compiler/runtime/route-strategy.js +4 -2
- package/dist/compiler/runtime/route-strategy.js.map +1 -1
- package/dist/compiler/runtime/should-redirect.js +4 -1
- package/dist/compiler/runtime/should-redirect.js.map +1 -1
- package/dist/compiler/runtime/should-redirect.test.js +26 -2
- package/dist/compiler/runtime/should-redirect.test.js.map +1 -1
- package/dist/compiler/runtime/type.d.ts +1 -0
- package/dist/compiler/runtime/type.js.map +1 -1
- package/dist/compiler/runtime/url-pattern.d.ts +11 -0
- package/dist/compiler/runtime/url-pattern.js +39 -1
- package/dist/compiler/runtime/url-pattern.js.map +1 -1
- package/dist/compiler/runtime/variables.d.ts +6 -0
- package/dist/compiler/runtime/variables.js +6 -0
- package/dist/compiler/runtime/variables.js.map +1 -1
- package/dist/compiler/server/create-server-file.js +18 -4
- package/dist/compiler/server/create-server-file.js.map +1 -1
- package/dist/compiler/server/create-server-file.test.js +76 -0
- package/dist/compiler/server/create-server-file.test.js.map +1 -1
- package/dist/compiler/server/middleware.js +6 -2
- package/dist/compiler/server/middleware.js.map +1 -1
- package/dist/compiler/server/middleware.test.js +30 -0
- package/dist/compiler/server/middleware.test.js.map +1 -1
- package/dist/compiler/variable-access.d.ts +2 -0
- package/dist/compiler/variable-access.js +6 -0
- package/dist/compiler/variable-access.js.map +1 -1
- package/dist/index.test.d.ts +1 -0
- package/dist/index.test.js +9 -0
- package/dist/index.test.js.map +1 -0
- package/dist/messages/load-messages.js +4 -5
- package/dist/messages/load-messages.js.map +1 -1
- package/dist/services/env-variables/index.js +1 -1
- package/dist/services/env-variables/index.js.map +1 -1
- package/dist/services/file-handling/write-output.bench.d.ts +1 -0
- package/dist/services/file-handling/write-output.bench.js +24 -0
- package/dist/services/file-handling/write-output.bench.js.map +1 -0
- package/dist/services/file-handling/write-output.d.ts +5 -0
- package/dist/services/file-handling/write-output.js +54 -9
- package/dist/services/file-handling/write-output.js.map +1 -1
- package/dist/services/file-handling/write-output.test.js +27 -0
- package/dist/services/file-handling/write-output.test.js.map +1 -1
- package/dist/services/logger/index.js +2 -0
- package/dist/services/logger/index.js.map +1 -1
- package/package.json +86 -86
|
@@ -26,6 +26,29 @@ test("shouldRedirect redirects to the strategy-preferred locale on the server",
|
|
|
26
26
|
expect(decision.redirectUrl?.href).toBe("https://example.com/fr/dashboard");
|
|
27
27
|
expect(decision.locale).toBe("fr");
|
|
28
28
|
});
|
|
29
|
+
test("shouldRedirect enforces the configured trailing slash policy", async () => {
|
|
30
|
+
const runtime = await createParaglide({
|
|
31
|
+
baseLocale: "en",
|
|
32
|
+
locales: ["en", "de"],
|
|
33
|
+
strategy: ["url", "baseLocale"],
|
|
34
|
+
trailingSlash: "never",
|
|
35
|
+
urlPatterns: [
|
|
36
|
+
{
|
|
37
|
+
pattern: "/about",
|
|
38
|
+
localized: [
|
|
39
|
+
["de", "/de/ueber"],
|
|
40
|
+
["en", "/about"],
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
});
|
|
45
|
+
const decision = await runtime.shouldRedirect({
|
|
46
|
+
request: new Request("https://example.com/de/ueber/"),
|
|
47
|
+
});
|
|
48
|
+
expect(decision.locale).toBe("de");
|
|
49
|
+
expect(decision.shouldRedirect).toBe(true);
|
|
50
|
+
expect(decision.redirectUrl?.href).toBe("https://example.com/de/ueber");
|
|
51
|
+
});
|
|
29
52
|
test("shouldRedirect uses the provided public url when the transport request url differs", async () => {
|
|
30
53
|
const runtime = await createParaglide({
|
|
31
54
|
baseLocale: "en",
|
|
@@ -188,7 +211,7 @@ test("shouldRedirect({ url }) resolves locale using target URL routeStrategies o
|
|
|
188
211
|
],
|
|
189
212
|
routeStrategies: [
|
|
190
213
|
{
|
|
191
|
-
match: "/dashboard
|
|
214
|
+
match: "/dashboard",
|
|
192
215
|
strategy: ["cookie", "baseLocale"],
|
|
193
216
|
},
|
|
194
217
|
],
|
|
@@ -250,6 +273,7 @@ test("shouldRedirect respects routeStrategies that disable url strategy per rout
|
|
|
250
273
|
locales: ["en", "fr"],
|
|
251
274
|
strategy: ["url", "cookie", "baseLocale"],
|
|
252
275
|
cookieName: "PARAGLIDE_LOCALE",
|
|
276
|
+
trailingSlash: "never",
|
|
253
277
|
routeStrategies: [
|
|
254
278
|
{
|
|
255
279
|
match: "/dashboard/:path(.*)?",
|
|
@@ -257,7 +281,7 @@ test("shouldRedirect respects routeStrategies that disable url strategy per rout
|
|
|
257
281
|
},
|
|
258
282
|
],
|
|
259
283
|
});
|
|
260
|
-
const request = new Request("https://example.com/dashboard", {
|
|
284
|
+
const request = new Request("https://example.com/dashboard/", {
|
|
261
285
|
headers: {
|
|
262
286
|
cookie: "PARAGLIDE_LOCALE=fr",
|
|
263
287
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"should-redirect.test.js","sourceRoot":"","sources":["../../../src/compiler/runtime/should-redirect.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,IAAI,CAAC,yEAAyE,EAAE,KAAK,IAAI,EAAE;IAC1F,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,UAAU,EAAE,IAAI;QAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC;QACrB,QAAQ,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC;QAC3B,UAAU,EAAE,kBAAkB;QAC9B,WAAW,EAAE;YACZ;gBACC,OAAO,EAAE,gCAAgC;gBACzC,SAAS,EAAE;oBACV,CAAC,IAAI,EAAE,mCAAmC,CAAC;oBAC3C,CAAC,IAAI,EAAE,mCAAmC,CAAC;iBAC3C;aACD;SACD;KACD,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,kCAAkC,EAAE;QAC/D,OAAO,EAAE;YACR,MAAM,EAAE,qBAAqB;SAC7B;KACD,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,cAAc,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IAE3D,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;IAC5E,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,oFAAoF,EAAE,KAAK,IAAI,EAAE;IACrG,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,UAAU,EAAE,IAAI;QAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC;QACrB,QAAQ,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC;QAC3B,UAAU,EAAE,kBAAkB;QAC9B,WAAW,EAAE;YACZ;gBACC,OAAO,EAAE,gCAAgC;gBACzC,SAAS,EAAE;oBACV,CAAC,IAAI,EAAE,mCAAmC,CAAC;oBAC3C,CAAC,IAAI,EAAE,mCAAmC,CAAC;iBAC3C;aACD;SACD;KACD,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,0CAA0C,EAAE;QACvE,OAAO,EAAE;YACR,MAAM,EAAE,qBAAqB;SAC7B;KACD,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,cAAc,CAAC;QAC7C,OAAO;QACP,mBAAmB,EAAE,kCAAkC;KACvD,CAAC,CAAC;IAEH,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;IAC5E,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,kFAAkF,EAAE,KAAK,IAAI,EAAE;IACnG,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,UAAU,EAAE,IAAI;QAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC;QACrB,QAAQ,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC;QAC3B,UAAU,EAAE,kBAAkB;QAC9B,WAAW,EAAE;YACZ;gBACC,OAAO,EAAE,gCAAgC;gBACzC,SAAS,EAAE;oBACV,CAAC,IAAI,EAAE,mCAAmC,CAAC;oBAC3C,CAAC,IAAI,EAAE,mCAAmC,CAAC;iBAC3C;aACD;SACD;KACD,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,kCAAkC,EAAE;QAC/D,OAAO,EAAE;YACR,MAAM,EAAE,qBAAqB;SAC7B;KACD,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,cAAc,CAAC;QAC7C,OAAO;QACP,mBAAmB,EAAE,eAAe;KACpC,CAAC,CAAC;IAEH,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;IAC5E,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,gEAAgE,EAAE,KAAK,IAAI,EAAE;IACjF,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,UAAU,EAAE,IAAI;QAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC;QACrB,QAAQ,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC;QAC3B,UAAU,EAAE,kBAAkB;QAC9B,WAAW,EAAE;YACZ;gBACC,OAAO,EAAE,gCAAgC;gBACzC,SAAS,EAAE;oBACV,CAAC,IAAI,EAAE,mCAAmC,CAAC;oBAC3C,CAAC,IAAI,EAAE,mCAAmC,CAAC;iBAC3C;aACD;SACD;KACD,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,kCAAkC,EAAE;QAC/D,OAAO,EAAE;YACR,MAAM,EAAE,qBAAqB;SAC7B;KACD,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,cAAc,CAAC;QAC7C,OAAO;QACP,GAAG,EAAE,YAAY;KACV,CAAC,CAAC;IAEV,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;IAC5E,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;IAC3E,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,UAAU,EAAE,IAAI;QAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC;QACrB,QAAQ,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC;QAC3B,UAAU,EAAE,kBAAkB;QAC9B,WAAW,EAAE;YACZ;gBACC,OAAO,EAAE,gCAAgC;gBACzC,SAAS,EAAE;oBACV,CAAC,IAAI,EAAE,mCAAmC,CAAC;oBAC3C,CAAC,IAAI,EAAE,mCAAmC,CAAC;iBAC3C;aACD;SACD;KACD,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,kCAAkC,EAAE;QAC/D,OAAO,EAAE;YACR,MAAM,EAAE,qBAAqB;SAC7B;KACD,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,cAAc,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IAE3D,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5C,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,aAAa,EAAE,CAAC;IAC7C,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,wEAAwE,EAAE,KAAK,IAAI,EAAE;IACzF,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,UAAU,EAAE,IAAI;QAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC;QACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,gBAAgB,CAAC;QACnC,QAAQ,EAAE,OAAO;QACjB,WAAW,EAAE,SAAS;KACtB,CAAC,CAAC;IAEH,MAAM,cAAc,GAAG,UAAU,CAAC,MAAM,CAAC;IACzC,IAAI,CAAC;QACJ,UAAU,CAAC,MAAM,GAAG;YACnB,QAAQ,EAAE;gBACT,IAAI,EAAE,gCAAgC;gBACtC,MAAM,EAAE,qBAAqB;aAC7B;SACM,CAAC;QAET,OAAO,CAAC,kBAAkB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QAEvC,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,cAAc,EAAE,CAAC;QAEhD,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;QAC1E,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;YAAS,CAAC;QACV,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;YAClC,OAAO,CAAC,cAAc,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC9C,CAAC;aAAM,CAAC;YACP,UAAU,CAAC,MAAM,GAAG,cAAc,CAAC;QACpC,CAAC;IACF,CAAC;AACF,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,oFAAoF,EAAE,KAAK,IAAI,EAAE;IACrG,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,UAAU,EAAE,IAAI;QAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC;QACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,YAAY,CAAC;QACzC,UAAU,EAAE,kBAAkB;QAC9B,QAAQ,EAAE,OAAO;QACjB,WAAW,EAAE;YACZ;gBACC,OAAO,EAAE,gCAAgC;gBACzC,SAAS,EAAE;oBACV,CAAC,IAAI,EAAE,mCAAmC,CAAC;oBAC3C,CAAC,IAAI,EAAE,mCAAmC,CAAC;iBAC3C;aACD;SACD;QACD,eAAe,EAAE;YAChB;gBACC,KAAK,EAAE,uBAAuB;gBAC9B,QAAQ,EAAE,CAAC,QAAQ,EAAE,YAAY,CAAC;aAClC;SACD;KACD,CAAC,CAAC;IAEH,MAAM,cAAc,GAAG,UAAU,CAAC,MAAM,CAAC;IACzC,MAAM,gBAAgB,GAAG,UAAU,CAAC,QAAQ,CAAC;IAE7C,IAAI,CAAC;QACJ,UAAU,CAAC,MAAM,GAAG;YACnB,QAAQ,EAAE;gBACT,IAAI,EAAE,+BAA+B;gBACrC,MAAM,EAAE,qBAAqB;aAC7B;SACM,CAAC;QACT,UAAU,CAAC,QAAQ,GAAG;YACrB,MAAM,EAAE,qBAAqB;SACtB,CAAC;QAET,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,cAAc,CAAC;YAC7C,GAAG,EAAE,gCAAgC;SACrC,CAAC,CAAC;QAEH,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC5C,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,aAAa,EAAE,CAAC;QAC7C,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;YAAS,CAAC;QACV,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;YAClC,OAAO,CAAC,cAAc,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC9C,CAAC;aAAM,CAAC;YACP,UAAU,CAAC,MAAM,GAAG,cAAc,CAAC;QACpC,CAAC;QAED,IAAI,gBAAgB,KAAK,SAAS,EAAE,CAAC;YACpC,OAAO,CAAC,cAAc,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;QAChD,CAAC;aAAM,CAAC;YACP,UAAU,CAAC,QAAQ,GAAG,gBAAgB,CAAC;QACxC,CAAC;IACF,CAAC;AACF,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;IACpF,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,UAAU,EAAE,IAAI;QAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC;QACrB,QAAQ,EAAE,CAAC,QAAQ,CAAC;QACpB,UAAU,EAAE,kBAAkB;KAC9B,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,kCAAkC,EAAE;QAC/D,OAAO,EAAE;YACR,MAAM,EAAE,qBAAqB;SAC7B;KACD,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,cAAc,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IAE3D,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5C,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,aAAa,EAAE,CAAC;IAC7C,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,6EAA6E,EAAE,KAAK,IAAI,EAAE;IAC9F,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,UAAU,EAAE,IAAI;QAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC;QACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,YAAY,CAAC;QACzC,UAAU,EAAE,kBAAkB;QAC9B,eAAe,EAAE;YAChB;gBACC,KAAK,EAAE,uBAAuB;gBAC9B,QAAQ,EAAE,CAAC,QAAQ,EAAE,YAAY,CAAC;aAClC;SACD;KACD,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,+BAA+B,EAAE;QAC5D,OAAO,EAAE;YACR,MAAM,EAAE,qBAAqB;SAC7B;KACD,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,cAAc,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IAE3D,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5C,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,aAAa,EAAE,CAAC;IAC7C,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpC,CAAC,CAAC,CAAC","sourcesContent":["import { expect, test } from \"vitest\";\nimport { createParaglide } from \"../create-paraglide.js\";\n\ntest(\"shouldRedirect redirects to the strategy-preferred locale on the server\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"fr\"],\n\t\tstrategy: [\"cookie\", \"url\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/en/:path(.*)?\"],\n\t\t\t\t\t[\"fr\", \"https://example.com/fr/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst request = new Request(\"https://example.com/en/dashboard\", {\n\t\theaders: {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t},\n\t});\n\n\tconst decision = await runtime.shouldRedirect({ request });\n\n\texpect(decision.shouldRedirect).toBe(true);\n\texpect(decision.redirectUrl?.href).toBe(\"https://example.com/fr/dashboard\");\n\texpect(decision.locale).toBe(\"fr\");\n});\n\ntest(\"shouldRedirect uses the provided public url when the transport request url differs\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"fr\"],\n\t\tstrategy: [\"cookie\", \"url\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/en/:path(.*)?\"],\n\t\t\t\t\t[\"fr\", \"https://example.com/fr/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst request = new Request(\"http://internal.example.com/en/dashboard\", {\n\t\theaders: {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t},\n\t});\n\n\tconst decision = await runtime.shouldRedirect({\n\t\trequest,\n\t\teffectiveRequestUrl: \"https://example.com/en/dashboard\",\n\t});\n\n\texpect(decision.shouldRedirect).toBe(true);\n\texpect(decision.redirectUrl?.href).toBe(\"https://example.com/fr/dashboard\");\n\texpect(decision.locale).toBe(\"fr\");\n});\n\ntest(\"shouldRedirect resolves relative effectiveRequestUrl strings against request.url\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"fr\"],\n\t\tstrategy: [\"cookie\", \"url\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/en/:path(.*)?\"],\n\t\t\t\t\t[\"fr\", \"https://example.com/fr/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst request = new Request(\"https://example.com/en/dashboard\", {\n\t\theaders: {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t},\n\t});\n\n\tconst decision = await runtime.shouldRedirect({\n\t\trequest,\n\t\teffectiveRequestUrl: \"/en/dashboard\",\n\t});\n\n\texpect(decision.shouldRedirect).toBe(true);\n\texpect(decision.redirectUrl?.href).toBe(\"https://example.com/fr/dashboard\");\n\texpect(decision.locale).toBe(\"fr\");\n});\n\ntest(\"shouldRedirect keeps request precedence over url on the server\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"fr\"],\n\t\tstrategy: [\"cookie\", \"url\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/en/:path(.*)?\"],\n\t\t\t\t\t[\"fr\", \"https://example.com/fr/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst request = new Request(\"https://example.com/en/dashboard\", {\n\t\theaders: {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t},\n\t});\n\n\tconst decision = await runtime.shouldRedirect({\n\t\trequest,\n\t\turl: \"/dashboard\",\n\t} as any);\n\n\texpect(decision.shouldRedirect).toBe(true);\n\texpect(decision.redirectUrl?.href).toBe(\"https://example.com/fr/dashboard\");\n\texpect(decision.locale).toBe(\"fr\");\n});\n\ntest(\"shouldRedirect does nothing when the URL already matches\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"fr\"],\n\t\tstrategy: [\"cookie\", \"url\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/en/:path(.*)?\"],\n\t\t\t\t\t[\"fr\", \"https://example.com/fr/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst request = new Request(\"https://example.com/fr/dashboard\", {\n\t\theaders: {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t},\n\t});\n\n\tconst decision = await runtime.shouldRedirect({ request });\n\n\texpect(decision.shouldRedirect).toBe(false);\n\texpect(decision.redirectUrl).toBeUndefined();\n\texpect(decision.locale).toBe(\"fr\");\n});\n\ntest(\"shouldRedirect falls back to the browser URL when no input is provided\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\"],\n\t\tstrategy: [\"url\", \"globalVariable\"],\n\t\tisServer: \"false\",\n\t\turlPatterns: undefined,\n\t});\n\n\tconst originalWindow = globalThis.window;\n\ttry {\n\t\tglobalThis.window = {\n\t\t\tlocation: {\n\t\t\t\thref: \"https://example.com/en/profile\",\n\t\t\t\torigin: \"https://example.com\",\n\t\t\t},\n\t\t} as any;\n\n\t\truntime.overwriteGetLocale(() => \"de\");\n\n\t\tconst decision = await runtime.shouldRedirect();\n\n\t\texpect(decision.shouldRedirect).toBe(true);\n\t\texpect(decision.redirectUrl?.href).toBe(\"https://example.com/de/profile\");\n\t\texpect(decision.locale).toBe(\"de\");\n\t} finally {\n\t\tif (originalWindow === undefined) {\n\t\t\tReflect.deleteProperty(globalThis, \"window\");\n\t\t} else {\n\t\t\tglobalThis.window = originalWindow;\n\t\t}\n\t}\n});\n\ntest(\"shouldRedirect({ url }) resolves locale using target URL routeStrategies on client\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"fr\"],\n\t\tstrategy: [\"url\", \"cookie\", \"baseLocale\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\tisServer: \"false\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/en/:path(.*)?\"],\n\t\t\t\t\t[\"fr\", \"https://example.com/fr/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t\trouteStrategies: [\n\t\t\t{\n\t\t\t\tmatch: \"/dashboard/:path(.*)?\",\n\t\t\t\tstrategy: [\"cookie\", \"baseLocale\"],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst originalWindow = globalThis.window;\n\tconst originalDocument = globalThis.document;\n\n\ttry {\n\t\tglobalThis.window = {\n\t\t\tlocation: {\n\t\t\t\thref: \"https://example.com/dashboard\",\n\t\t\t\torigin: \"https://example.com\",\n\t\t\t},\n\t\t} as any;\n\t\tglobalThis.document = {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t} as any;\n\n\t\tconst decision = await runtime.shouldRedirect({\n\t\t\turl: \"https://example.com/en/profile\",\n\t\t});\n\n\t\texpect(decision.shouldRedirect).toBe(false);\n\t\texpect(decision.redirectUrl).toBeUndefined();\n\t\texpect(decision.locale).toBe(\"en\");\n\t} finally {\n\t\tif (originalWindow === undefined) {\n\t\t\tReflect.deleteProperty(globalThis, \"window\");\n\t\t} else {\n\t\t\tglobalThis.window = originalWindow;\n\t\t}\n\n\t\tif (originalDocument === undefined) {\n\t\t\tReflect.deleteProperty(globalThis, \"document\");\n\t\t} else {\n\t\t\tglobalThis.document = originalDocument;\n\t\t}\n\t}\n});\n\ntest(\"shouldRedirect never suggests a redirect without the url strategy\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"fr\"],\n\t\tstrategy: [\"cookie\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t});\n\n\tconst request = new Request(\"https://example.com/en/dashboard\", {\n\t\theaders: {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t},\n\t});\n\n\tconst decision = await runtime.shouldRedirect({ request });\n\n\texpect(decision.shouldRedirect).toBe(false);\n\texpect(decision.redirectUrl).toBeUndefined();\n\texpect(decision.locale).toBe(\"fr\");\n});\n\ntest(\"shouldRedirect respects routeStrategies that disable url strategy per route\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"fr\"],\n\t\tstrategy: [\"url\", \"cookie\", \"baseLocale\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\trouteStrategies: [\n\t\t\t{\n\t\t\t\tmatch: \"/dashboard/:path(.*)?\",\n\t\t\t\tstrategy: [\"cookie\", \"baseLocale\"],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst request = new Request(\"https://example.com/dashboard\", {\n\t\theaders: {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t},\n\t});\n\n\tconst decision = await runtime.shouldRedirect({ request });\n\n\texpect(decision.shouldRedirect).toBe(false);\n\texpect(decision.redirectUrl).toBeUndefined();\n\texpect(decision.locale).toBe(\"fr\");\n});\n"]}
|
|
1
|
+
{"version":3,"file":"should-redirect.test.js","sourceRoot":"","sources":["../../../src/compiler/runtime/should-redirect.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, test } from \"vitest\";\nimport { createParaglide } from \"../create-paraglide.js\";\n\ntest(\"shouldRedirect redirects to the strategy-preferred locale on the server\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"fr\"],\n\t\tstrategy: [\"cookie\", \"url\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/en/:path(.*)?\"],\n\t\t\t\t\t[\"fr\", \"https://example.com/fr/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst request = new Request(\"https://example.com/en/dashboard\", {\n\t\theaders: {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t},\n\t});\n\n\tconst decision = await runtime.shouldRedirect({ request });\n\n\texpect(decision.shouldRedirect).toBe(true);\n\texpect(decision.redirectUrl?.href).toBe(\"https://example.com/fr/dashboard\");\n\texpect(decision.locale).toBe(\"fr\");\n});\n\ntest(\"shouldRedirect enforces the configured trailing slash policy\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\"],\n\t\tstrategy: [\"url\", \"baseLocale\"],\n\t\ttrailingSlash: \"never\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"/about\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"de\", \"/de/ueber\"],\n\t\t\t\t\t[\"en\", \"/about\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst decision = await runtime.shouldRedirect({\n\t\trequest: new Request(\"https://example.com/de/ueber/\"),\n\t});\n\n\texpect(decision.locale).toBe(\"de\");\n\texpect(decision.shouldRedirect).toBe(true);\n\texpect(decision.redirectUrl?.href).toBe(\"https://example.com/de/ueber\");\n});\n\ntest(\"shouldRedirect uses the provided public url when the transport request url differs\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"fr\"],\n\t\tstrategy: [\"cookie\", \"url\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/en/:path(.*)?\"],\n\t\t\t\t\t[\"fr\", \"https://example.com/fr/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst request = new Request(\"http://internal.example.com/en/dashboard\", {\n\t\theaders: {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t},\n\t});\n\n\tconst decision = await runtime.shouldRedirect({\n\t\trequest,\n\t\teffectiveRequestUrl: \"https://example.com/en/dashboard\",\n\t});\n\n\texpect(decision.shouldRedirect).toBe(true);\n\texpect(decision.redirectUrl?.href).toBe(\"https://example.com/fr/dashboard\");\n\texpect(decision.locale).toBe(\"fr\");\n});\n\ntest(\"shouldRedirect resolves relative effectiveRequestUrl strings against request.url\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"fr\"],\n\t\tstrategy: [\"cookie\", \"url\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/en/:path(.*)?\"],\n\t\t\t\t\t[\"fr\", \"https://example.com/fr/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst request = new Request(\"https://example.com/en/dashboard\", {\n\t\theaders: {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t},\n\t});\n\n\tconst decision = await runtime.shouldRedirect({\n\t\trequest,\n\t\teffectiveRequestUrl: \"/en/dashboard\",\n\t});\n\n\texpect(decision.shouldRedirect).toBe(true);\n\texpect(decision.redirectUrl?.href).toBe(\"https://example.com/fr/dashboard\");\n\texpect(decision.locale).toBe(\"fr\");\n});\n\ntest(\"shouldRedirect keeps request precedence over url on the server\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"fr\"],\n\t\tstrategy: [\"cookie\", \"url\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/en/:path(.*)?\"],\n\t\t\t\t\t[\"fr\", \"https://example.com/fr/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst request = new Request(\"https://example.com/en/dashboard\", {\n\t\theaders: {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t},\n\t});\n\n\tconst decision = await runtime.shouldRedirect({\n\t\trequest,\n\t\turl: \"/dashboard\",\n\t} as any);\n\n\texpect(decision.shouldRedirect).toBe(true);\n\texpect(decision.redirectUrl?.href).toBe(\"https://example.com/fr/dashboard\");\n\texpect(decision.locale).toBe(\"fr\");\n});\n\ntest(\"shouldRedirect does nothing when the URL already matches\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"fr\"],\n\t\tstrategy: [\"cookie\", \"url\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/en/:path(.*)?\"],\n\t\t\t\t\t[\"fr\", \"https://example.com/fr/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst request = new Request(\"https://example.com/fr/dashboard\", {\n\t\theaders: {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t},\n\t});\n\n\tconst decision = await runtime.shouldRedirect({ request });\n\n\texpect(decision.shouldRedirect).toBe(false);\n\texpect(decision.redirectUrl).toBeUndefined();\n\texpect(decision.locale).toBe(\"fr\");\n});\n\ntest(\"shouldRedirect falls back to the browser URL when no input is provided\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\"],\n\t\tstrategy: [\"url\", \"globalVariable\"],\n\t\tisServer: \"false\",\n\t\turlPatterns: undefined,\n\t});\n\n\tconst originalWindow = globalThis.window;\n\ttry {\n\t\tglobalThis.window = {\n\t\t\tlocation: {\n\t\t\t\thref: \"https://example.com/en/profile\",\n\t\t\t\torigin: \"https://example.com\",\n\t\t\t},\n\t\t} as any;\n\n\t\truntime.overwriteGetLocale(() => \"de\");\n\n\t\tconst decision = await runtime.shouldRedirect();\n\n\t\texpect(decision.shouldRedirect).toBe(true);\n\t\texpect(decision.redirectUrl?.href).toBe(\"https://example.com/de/profile\");\n\t\texpect(decision.locale).toBe(\"de\");\n\t} finally {\n\t\tif (originalWindow === undefined) {\n\t\t\tReflect.deleteProperty(globalThis, \"window\");\n\t\t} else {\n\t\t\tglobalThis.window = originalWindow;\n\t\t}\n\t}\n});\n\ntest(\"shouldRedirect({ url }) resolves locale using target URL routeStrategies on client\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"fr\"],\n\t\tstrategy: [\"url\", \"cookie\", \"baseLocale\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\tisServer: \"false\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/en/:path(.*)?\"],\n\t\t\t\t\t[\"fr\", \"https://example.com/fr/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t\trouteStrategies: [\n\t\t\t{\n\t\t\t\tmatch: \"/dashboard\",\n\t\t\t\tstrategy: [\"cookie\", \"baseLocale\"],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst originalWindow = globalThis.window;\n\tconst originalDocument = globalThis.document;\n\n\ttry {\n\t\tglobalThis.window = {\n\t\t\tlocation: {\n\t\t\t\thref: \"https://example.com/dashboard\",\n\t\t\t\torigin: \"https://example.com\",\n\t\t\t},\n\t\t} as any;\n\t\tglobalThis.document = {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t} as any;\n\n\t\tconst decision = await runtime.shouldRedirect({\n\t\t\turl: \"https://example.com/en/profile\",\n\t\t});\n\n\t\texpect(decision.shouldRedirect).toBe(false);\n\t\texpect(decision.redirectUrl).toBeUndefined();\n\t\texpect(decision.locale).toBe(\"en\");\n\t} finally {\n\t\tif (originalWindow === undefined) {\n\t\t\tReflect.deleteProperty(globalThis, \"window\");\n\t\t} else {\n\t\t\tglobalThis.window = originalWindow;\n\t\t}\n\n\t\tif (originalDocument === undefined) {\n\t\t\tReflect.deleteProperty(globalThis, \"document\");\n\t\t} else {\n\t\t\tglobalThis.document = originalDocument;\n\t\t}\n\t}\n});\n\ntest(\"shouldRedirect never suggests a redirect without the url strategy\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"fr\"],\n\t\tstrategy: [\"cookie\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t});\n\n\tconst request = new Request(\"https://example.com/en/dashboard\", {\n\t\theaders: {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t},\n\t});\n\n\tconst decision = await runtime.shouldRedirect({ request });\n\n\texpect(decision.shouldRedirect).toBe(false);\n\texpect(decision.redirectUrl).toBeUndefined();\n\texpect(decision.locale).toBe(\"fr\");\n});\n\ntest(\"shouldRedirect respects routeStrategies that disable url strategy per route\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"fr\"],\n\t\tstrategy: [\"url\", \"cookie\", \"baseLocale\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\ttrailingSlash: \"never\",\n\t\trouteStrategies: [\n\t\t\t{\n\t\t\t\tmatch: \"/dashboard/:path(.*)?\",\n\t\t\t\tstrategy: [\"cookie\", \"baseLocale\"],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst request = new Request(\"https://example.com/dashboard/\", {\n\t\theaders: {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t},\n\t});\n\n\tconst decision = await runtime.shouldRedirect({ request });\n\n\texpect(decision.shouldRedirect).toBe(false);\n\texpect(decision.redirectUrl).toBeUndefined();\n\texpect(decision.locale).toBe(\"fr\");\n});\n"]}
|
|
@@ -10,6 +10,7 @@ export type Runtime = {
|
|
|
10
10
|
cookieMaxAge: typeof import("./variables.js").cookieMaxAge;
|
|
11
11
|
urlPatterns: typeof import("./variables.js").urlPatterns;
|
|
12
12
|
routing: typeof import("./variables.js").routing;
|
|
13
|
+
trailingSlash: typeof import("./variables.js").trailingSlash;
|
|
13
14
|
disableAsyncLocalStorage: typeof import("./variables.js").disableAsyncLocalStorage;
|
|
14
15
|
serverAsyncLocalStorage: typeof import("./variables.js").serverAsyncLocalStorage;
|
|
15
16
|
getServerAsyncLocalStorage: typeof import("./variables.js").getServerAsyncLocalStorage;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"type.js","sourceRoot":"","sources":["../../../src/compiler/runtime/type.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * The Paraglide runtime API.\n */\nexport type Runtime = {\n\tbaseLocale: typeof import(\"./variables.js\").baseLocale;\n\tlocales: typeof import(\"./variables.js\").locales;\n\tstrategy: typeof import(\"./variables.js\").strategy;\n\trouteStrategies: typeof import(\"./variables.js\").routeStrategies;\n\tcookieName: typeof import(\"./variables.js\").cookieName;\n\tcookieMaxAge: typeof import(\"./variables.js\").cookieMaxAge;\n\turlPatterns: typeof import(\"./variables.js\").urlPatterns;\n\trouting: typeof import(\"./variables.js\").routing;\n\tdisableAsyncLocalStorage: typeof import(\"./variables.js\").disableAsyncLocalStorage;\n\tserverAsyncLocalStorage: typeof import(\"./variables.js\").serverAsyncLocalStorage;\n\tgetServerAsyncLocalStorage: typeof import(\"./variables.js\").getServerAsyncLocalStorage;\n\texperimentalMiddlewareLocaleSplitting: typeof import(\"./variables.js\").experimentalMiddlewareLocaleSplitting;\n\tisServer: typeof import(\"./variables.js\").isServer;\n\texperimentalStaticLocale: typeof import(\"./variables.js\").experimentalStaticLocale;\n\tgetLocale: typeof import(\"./get-locale.js\").getLocale;\n\tgetTextDirection: typeof import(\"./get-text-direction.js\").getTextDirection;\n\tsetLocale: typeof import(\"./set-locale.js\").setLocale;\n\tgetUrlOrigin: typeof import(\"./get-url-origin.js\").getUrlOrigin;\n\toverwriteGetLocale: typeof import(\"./get-locale.js\").overwriteGetLocale;\n\toverwriteSetLocale: typeof import(\"./set-locale.js\").overwriteSetLocale;\n\toverwriteGetUrlOrigin: typeof import(\"./get-url-origin.js\").overwriteGetUrlOrigin;\n\toverwriteServerAsyncLocalStorage: typeof import(\"./variables.js\").overwriteServerAsyncLocalStorage;\n\ttoLocale: typeof import(\"./check-locale.js\").toLocale;\n\tassertIsLocale: typeof import(\"./check-locale.js\").assertIsLocale;\n\tisLocale: typeof import(\"./check-locale.js\").isLocale;\n\tlocalizeHref: typeof import(\"./localize-href.js\").localizeHref;\n\tdeLocalizeHref: typeof import(\"./localize-href.js\").deLocalizeHref;\n\tlocalizeUrl: typeof import(\"./localize-url.js\").localizeUrl;\n\tdeLocalizeUrl: typeof import(\"./localize-url.js\").deLocalizeUrl;\n\tshouldRedirect: typeof import(\"./should-redirect.js\").shouldRedirect;\n\textractLocaleFromUrl: typeof import(\"./extract-locale-from-url.js\").extractLocaleFromUrl;\n\textractLocaleFromRequest: typeof import(\"./extract-locale-from-request.js\").extractLocaleFromRequest;\n\textractLocaleFromRequestAsync: typeof import(\"./extract-locale-from-request-async.js\").extractLocaleFromRequestAsync;\n\textractLocaleFromCookie: typeof import(\"./extract-locale-from-cookie.js\").extractLocaleFromCookie;\n\textractLocaleFromHeader: typeof import(\"./extract-locale-from-header.js\").extractLocaleFromHeader;\n\textractLocaleFromNavigator: typeof import(\"./extract-locale-from-navigator.js\").extractLocaleFromNavigator;\n\tgenerateStaticLocalizedUrls: typeof import(\"./generate-static-localized-urls.js\").generateStaticLocalizedUrls;\n\ttrackMessageCall: typeof import(\"./track-message-call.js\").trackMessageCall;\n\tgetStrategyForUrl: typeof import(\"./route-strategy.js\").getStrategyForUrl;\n\tisExcludedByRouteStrategy: typeof import(\"./route-strategy.js\").isExcludedByRouteStrategy;\n\tdefineCustomServerStrategy: typeof import(\"./strategy.js\").defineCustomServerStrategy;\n\tdefineCustomClientStrategy: typeof import(\"./strategy.js\").defineCustomClientStrategy;\n};\n"]}
|
|
1
|
+
{"version":3,"file":"type.js","sourceRoot":"","sources":["../../../src/compiler/runtime/type.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * The Paraglide runtime API.\n */\nexport type Runtime = {\n\tbaseLocale: typeof import(\"./variables.js\").baseLocale;\n\tlocales: typeof import(\"./variables.js\").locales;\n\tstrategy: typeof import(\"./variables.js\").strategy;\n\trouteStrategies: typeof import(\"./variables.js\").routeStrategies;\n\tcookieName: typeof import(\"./variables.js\").cookieName;\n\tcookieMaxAge: typeof import(\"./variables.js\").cookieMaxAge;\n\turlPatterns: typeof import(\"./variables.js\").urlPatterns;\n\trouting: typeof import(\"./variables.js\").routing;\n\ttrailingSlash: typeof import(\"./variables.js\").trailingSlash;\n\tdisableAsyncLocalStorage: typeof import(\"./variables.js\").disableAsyncLocalStorage;\n\tserverAsyncLocalStorage: typeof import(\"./variables.js\").serverAsyncLocalStorage;\n\tgetServerAsyncLocalStorage: typeof import(\"./variables.js\").getServerAsyncLocalStorage;\n\texperimentalMiddlewareLocaleSplitting: typeof import(\"./variables.js\").experimentalMiddlewareLocaleSplitting;\n\tisServer: typeof import(\"./variables.js\").isServer;\n\texperimentalStaticLocale: typeof import(\"./variables.js\").experimentalStaticLocale;\n\tgetLocale: typeof import(\"./get-locale.js\").getLocale;\n\tgetTextDirection: typeof import(\"./get-text-direction.js\").getTextDirection;\n\tsetLocale: typeof import(\"./set-locale.js\").setLocale;\n\tgetUrlOrigin: typeof import(\"./get-url-origin.js\").getUrlOrigin;\n\toverwriteGetLocale: typeof import(\"./get-locale.js\").overwriteGetLocale;\n\toverwriteSetLocale: typeof import(\"./set-locale.js\").overwriteSetLocale;\n\toverwriteGetUrlOrigin: typeof import(\"./get-url-origin.js\").overwriteGetUrlOrigin;\n\toverwriteServerAsyncLocalStorage: typeof import(\"./variables.js\").overwriteServerAsyncLocalStorage;\n\ttoLocale: typeof import(\"./check-locale.js\").toLocale;\n\tassertIsLocale: typeof import(\"./check-locale.js\").assertIsLocale;\n\tisLocale: typeof import(\"./check-locale.js\").isLocale;\n\tlocalizeHref: typeof import(\"./localize-href.js\").localizeHref;\n\tdeLocalizeHref: typeof import(\"./localize-href.js\").deLocalizeHref;\n\tlocalizeUrl: typeof import(\"./localize-url.js\").localizeUrl;\n\tdeLocalizeUrl: typeof import(\"./localize-url.js\").deLocalizeUrl;\n\tshouldRedirect: typeof import(\"./should-redirect.js\").shouldRedirect;\n\textractLocaleFromUrl: typeof import(\"./extract-locale-from-url.js\").extractLocaleFromUrl;\n\textractLocaleFromRequest: typeof import(\"./extract-locale-from-request.js\").extractLocaleFromRequest;\n\textractLocaleFromRequestAsync: typeof import(\"./extract-locale-from-request-async.js\").extractLocaleFromRequestAsync;\n\textractLocaleFromCookie: typeof import(\"./extract-locale-from-cookie.js\").extractLocaleFromCookie;\n\textractLocaleFromHeader: typeof import(\"./extract-locale-from-header.js\").extractLocaleFromHeader;\n\textractLocaleFromNavigator: typeof import(\"./extract-locale-from-navigator.js\").extractLocaleFromNavigator;\n\tgenerateStaticLocalizedUrls: typeof import(\"./generate-static-localized-urls.js\").generateStaticLocalizedUrls;\n\ttrackMessageCall: typeof import(\"./track-message-call.js\").trackMessageCall;\n\tgetStrategyForUrl: typeof import(\"./route-strategy.js\").getStrategyForUrl;\n\tisExcludedByRouteStrategy: typeof import(\"./route-strategy.js\").isExcludedByRouteStrategy;\n\tdefineCustomServerStrategy: typeof import(\"./strategy.js\").defineCustomServerStrategy;\n\tdefineCustomClientStrategy: typeof import(\"./strategy.js\").defineCustomClientStrategy;\n};\n"]}
|
|
@@ -10,3 +10,14 @@
|
|
|
10
10
|
* @returns {URLPattern}
|
|
11
11
|
*/
|
|
12
12
|
export declare function getCompiledUrlPattern(pattern: string): URLPattern;
|
|
13
|
+
/**
|
|
14
|
+
* URLPattern's base URL affects relative patterns. Absolute patterns only
|
|
15
|
+
* depend on the pattern itself, while root-relative patterns also depend on
|
|
16
|
+
* the URL origin. Other relative patterns are deliberately not cached because
|
|
17
|
+
* their semantics depend on the complete base URL.
|
|
18
|
+
*
|
|
19
|
+
* @param {string} pattern
|
|
20
|
+
* @param {URL} url
|
|
21
|
+
* @returns {URLPattern}
|
|
22
|
+
*/
|
|
23
|
+
export declare function getUrlPattern(pattern: string, url: URL): URLPattern;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/** @type {Map<string, URLPattern>} */
|
|
2
2
|
const urlPatternCache = new Map();
|
|
3
|
+
const URL_PATTERN_CACHE_LIMIT = 128;
|
|
4
|
+
const ABSOLUTE_URL_PATTERN = /^(?:[A-Za-z][A-Za-z\d+.-]*|:[A-Za-z][A-Za-z\d_-]*):\/\//;
|
|
3
5
|
/**
|
|
4
6
|
* URL patterns are generated at build time and remain constant for the lifetime
|
|
5
7
|
* of the process. Compiling them once avoids repeatedly constructing URLPattern
|
|
@@ -17,7 +19,7 @@ export function getCompiledUrlPattern(pattern) {
|
|
|
17
19
|
if (pattern.startsWith("/")) {
|
|
18
20
|
compiled = new URLPattern({ pathname: pattern });
|
|
19
21
|
}
|
|
20
|
-
else if (
|
|
22
|
+
else if (ABSOLUTE_URL_PATTERN.test(pattern) ||
|
|
21
23
|
pattern.startsWith(":") ||
|
|
22
24
|
pattern.startsWith("*")) {
|
|
23
25
|
compiled = new URLPattern(pattern);
|
|
@@ -32,4 +34,40 @@ export function getCompiledUrlPattern(pattern) {
|
|
|
32
34
|
}
|
|
33
35
|
return compiled;
|
|
34
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* URLPattern's base URL affects relative patterns. Absolute patterns only
|
|
39
|
+
* depend on the pattern itself, while root-relative patterns also depend on
|
|
40
|
+
* the URL origin. Other relative patterns are deliberately not cached because
|
|
41
|
+
* their semantics depend on the complete base URL.
|
|
42
|
+
*
|
|
43
|
+
* @param {string} pattern
|
|
44
|
+
* @param {URL} url
|
|
45
|
+
* @returns {URLPattern}
|
|
46
|
+
*/
|
|
47
|
+
export function getUrlPattern(pattern, url) {
|
|
48
|
+
const isAbsolutePattern = ABSOLUTE_URL_PATTERN.test(pattern);
|
|
49
|
+
const isRootRelativePattern = pattern.startsWith("/");
|
|
50
|
+
if (!isAbsolutePattern && !isRootRelativePattern) {
|
|
51
|
+
return new URLPattern(pattern, url.href);
|
|
52
|
+
}
|
|
53
|
+
const key = isAbsolutePattern
|
|
54
|
+
? pattern
|
|
55
|
+
: JSON.stringify([url.origin, pattern]);
|
|
56
|
+
const cached = urlPatternCache.get(key);
|
|
57
|
+
if (cached !== undefined) {
|
|
58
|
+
urlPatternCache.delete(key);
|
|
59
|
+
urlPatternCache.set(key, cached);
|
|
60
|
+
return cached;
|
|
61
|
+
}
|
|
62
|
+
const compiled = isRootRelativePattern
|
|
63
|
+
? getCompiledUrlPattern(pattern)
|
|
64
|
+
: new URLPattern(pattern, url.href);
|
|
65
|
+
if (urlPatternCache.size >= URL_PATTERN_CACHE_LIMIT) {
|
|
66
|
+
const oldestKey = urlPatternCache.keys().next().value;
|
|
67
|
+
if (oldestKey !== undefined)
|
|
68
|
+
urlPatternCache.delete(oldestKey);
|
|
69
|
+
}
|
|
70
|
+
urlPatternCache.set(key, compiled);
|
|
71
|
+
return compiled;
|
|
72
|
+
}
|
|
35
73
|
//# sourceMappingURL=url-pattern.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"url-pattern.js","sourceRoot":"","sources":["../../../src/compiler/runtime/url-pattern.js"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,MAAM,eAAe,GAAG,IAAI,GAAG,EAAE,CAAC;AAElC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAO;IAC5C,IAAI,QAAQ,GAAG,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC5C,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC5B,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7B,QAAQ,GAAG,IAAI,UAAU,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;QAClD,CAAC;aAAM,IACN,
|
|
1
|
+
{"version":3,"file":"url-pattern.js","sourceRoot":"","sources":["../../../src/compiler/runtime/url-pattern.js"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,MAAM,eAAe,GAAG,IAAI,GAAG,EAAE,CAAC;AAElC,MAAM,uBAAuB,GAAG,GAAG,CAAC;AACpC,MAAM,oBAAoB,GACzB,yDAAyD,CAAC;AAE3D;;;;;;;;;;GAUG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAO;IAC5C,IAAI,QAAQ,GAAG,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC5C,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC5B,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7B,QAAQ,GAAG,IAAI,UAAU,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;QAClD,CAAC;aAAM,IACN,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC;YAClC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YACvB,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EACtB,CAAC;YACF,QAAQ,GAAG,IAAI,UAAU,CAAC,OAAO,CAAC,CAAC;QACpC,CAAC;aAAM,CAAC;YACP,8DAA8D;YAC9D,8DAA8D;YAC9D,gDAAgD;YAChD,QAAQ,GAAG,IAAI,UAAU,CAAC,EAAE,QAAQ,EAAE,IAAI,OAAO,EAAE,EAAE,CAAC,CAAC;QACxD,CAAC;QACD,eAAe,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,QAAQ,CAAC;AACjB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAAC,OAAO,EAAE,GAAG;IACzC,MAAM,iBAAiB,GAAG,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC7D,MAAM,qBAAqB,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IACtD,IAAI,CAAC,iBAAiB,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAClD,OAAO,IAAI,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED,MAAM,GAAG,GAAG,iBAAiB;QAC5B,CAAC,CAAC,OAAO;QACT,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACzC,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACxC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QAC1B,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC5B,eAAe,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACjC,OAAO,MAAM,CAAC;IACf,CAAC;IAED,MAAM,QAAQ,GAAG,qBAAqB;QACrC,CAAC,CAAC,qBAAqB,CAAC,OAAO,CAAC;QAChC,CAAC,CAAC,IAAI,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,eAAe,CAAC,IAAI,IAAI,uBAAuB,EAAE,CAAC;QACrD,MAAM,SAAS,GAAG,eAAe,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;QACtD,IAAI,SAAS,KAAK,SAAS;YAAE,eAAe,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAChE,CAAC;IACD,eAAe,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACnC,OAAO,QAAQ,CAAC;AACjB,CAAC","sourcesContent":["/** @type {Map<string, URLPattern>} */\nconst urlPatternCache = new Map();\n\nconst URL_PATTERN_CACHE_LIMIT = 128;\nconst ABSOLUTE_URL_PATTERN =\n\t/^(?:[A-Za-z][A-Za-z\\d+.-]*|:[A-Za-z][A-Za-z\\d_-]*):\\/\\//;\n\n/**\n * URL patterns are generated at build time and remain constant for the lifetime\n * of the process. Compiling them once avoids repeatedly constructing URLPattern\n * instances for every localized link and incoming request.\n *\n * Path-only patterns need to be constructed as URLPatternInit objects so they\n * remain origin-agnostic without requiring a per-request base URL.\n *\n * @param {string} pattern\n * @returns {URLPattern}\n */\nexport function getCompiledUrlPattern(pattern) {\n\tlet compiled = urlPatternCache.get(pattern);\n\tif (compiled === undefined) {\n\t\tif (pattern.startsWith(\"/\")) {\n\t\t\tcompiled = new URLPattern({ pathname: pattern });\n\t\t} else if (\n\t\t\tABSOLUTE_URL_PATTERN.test(pattern) ||\n\t\t\tpattern.startsWith(\":\") ||\n\t\t\tpattern.startsWith(\"*\")\n\t\t) {\n\t\t\tcompiled = new URLPattern(pattern);\n\t\t} else {\n\t\t\t// Relative path segments (e.g. \"specific-path\") are not valid\n\t\t\t// URLPattern strings on their own. Treat them as pathnames so\n\t\t\t// matching stays origin-agnostic and cacheable.\n\t\t\tcompiled = new URLPattern({ pathname: `/${pattern}` });\n\t\t}\n\t\turlPatternCache.set(pattern, compiled);\n\t}\n\treturn compiled;\n}\n\n/**\n * URLPattern's base URL affects relative patterns. Absolute patterns only\n * depend on the pattern itself, while root-relative patterns also depend on\n * the URL origin. Other relative patterns are deliberately not cached because\n * their semantics depend on the complete base URL.\n *\n * @param {string} pattern\n * @param {URL} url\n * @returns {URLPattern}\n */\nexport function getUrlPattern(pattern, url) {\n\tconst isAbsolutePattern = ABSOLUTE_URL_PATTERN.test(pattern);\n\tconst isRootRelativePattern = pattern.startsWith(\"/\");\n\tif (!isAbsolutePattern && !isRootRelativePattern) {\n\t\treturn new URLPattern(pattern, url.href);\n\t}\n\n\tconst key = isAbsolutePattern\n\t\t? pattern\n\t\t: JSON.stringify([url.origin, pattern]);\n\tconst cached = urlPatternCache.get(key);\n\tif (cached !== undefined) {\n\t\turlPatternCache.delete(key);\n\t\turlPatternCache.set(key, cached);\n\t\treturn cached;\n\t}\n\n\tconst compiled = isRootRelativePattern\n\t\t? getCompiledUrlPattern(pattern)\n\t\t: new URLPattern(pattern, url.href);\n\tif (urlPatternCache.size >= URL_PATTERN_CACHE_LIMIT) {\n\t\tconst oldestKey = urlPatternCache.keys().next().value;\n\t\tif (oldestKey !== undefined) urlPatternCache.delete(oldestKey);\n\t}\n\turlPatternCache.set(key, compiled);\n\treturn compiled;\n}\n"]}
|
|
@@ -62,6 +62,12 @@ export declare const routing: Record<string, {
|
|
|
62
62
|
baseLocale: Locale;
|
|
63
63
|
locales: Locale[];
|
|
64
64
|
}>;
|
|
65
|
+
/**
|
|
66
|
+
* Controls trailing slash canonicalization for localized URLs.
|
|
67
|
+
*
|
|
68
|
+
* @type {"always" | "never" | undefined}
|
|
69
|
+
*/
|
|
70
|
+
export declare const trailingSlash: "always" | "never" | undefined;
|
|
65
71
|
export type ParaglideAsyncLocalStorage = {
|
|
66
72
|
getStore(): {
|
|
67
73
|
locale?: Locale;
|
|
@@ -52,6 +52,12 @@ export const urlPatterns = [];
|
|
|
52
52
|
* @type {Record<string, { baseLocale: Locale, locales: Locale[] }>}
|
|
53
53
|
*/
|
|
54
54
|
export const routing = {};
|
|
55
|
+
/**
|
|
56
|
+
* Controls trailing slash canonicalization for localized URLs.
|
|
57
|
+
*
|
|
58
|
+
* @type {"always" | "never" | undefined}
|
|
59
|
+
*/
|
|
60
|
+
export const trailingSlash = undefined;
|
|
55
61
|
/**
|
|
56
62
|
* @typedef {{
|
|
57
63
|
* getStore(): {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"variables.js","sourceRoot":"","sources":["../../../src/compiler/runtime/variables.js"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC;AAE/B;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,gCAAgC,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;AAEvE,qBAAqB;AACrB,MAAM,CAAC,MAAM,UAAU,GAAG,eAAe,CAAC;AAE1C,qBAAqB;AACrB,MAAM,CAAC,MAAM,YAAY,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC;AAE/C,qBAAqB;AACrB,MAAM,CAAC,MAAM,YAAY,GAAG,iBAAiB,CAAC;AAE9C,qBAAqB;AACrB,MAAM,CAAC,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAElD;;GAEG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,gBAAgB,CAAC,CAAC;AAE3C;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,CAAC;AAElC;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAE9B;;;;GAIG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,EAAE,CAAC;AAE1B;;;;;;;;;;GAUG;AAEH;;;;;;;GAOG;AACH,MAAM,CAAC,IAAI,uBAAuB,GAAG,SAAS,CAAC;AAE/C;;;;;;;GAOG;AACH,MAAM,UAAU,0BAA0B;IACzC,OAAO,uBAAuB,CAAC;AAChC,CAAC;AAED,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAK,CAAC;AAE9C,MAAM,CAAC,MAAM,qCAAqC,GAAG,KAAK,CAAC;AAE3D,MAAM,CAAC,MAAM,QAAQ,GAAG,OAAO,MAAM,KAAK,WAAW,CAAC;AAEtD,iCAAiC;AACjC,MAAM,CAAC,MAAM,wBAAwB,GAAG,SAAS,CAAC;AAElD;;;;;;;;;GASG;AACH,MAAM,UAAU,gCAAgC,CAAC,KAAK;IACrD,uBAAuB,GAAG,KAAK,CAAC;AACjC,CAAC;AAED,MAAM,CAAC,MAAM,+BAA+B,GAAG,KAAK,CAAC;AAErD,MAAM,CAAC,MAAM,4BAA4B,GAAG,KAAK,CAAC;AAElD,MAAM,CAAC,MAAM,wCAAwC,GAAG,KAAK,CAAC;AAE9D,MAAM,CAAC,MAAM,2CAA2C,GAAG,KAAK,CAAC;AAEjE,MAAM,CAAC,MAAM,mCAAmC,GAAG,KAAK,CAAC;AAEzD,MAAM,CAAC,MAAM,4BAA4B,GAAG,KAAK,CAAC;AAElD,MAAM,CAAC,MAAM,sCAAsC,GAAG,KAAK,CAAC","sourcesContent":["/**\n * The project's base locale.\n *\n * @example\n * if (locale === baseLocale) {\n * // do something\n * }\n */\nexport const baseLocale = \"en\";\n\n/**\n * The project's locales that have been specified in the settings.\n *\n * @example\n * if (locales.includes(userSelectedLocale) === false) {\n * throw new Error('Locale is not available');\n * }\n */\nexport const locales = /** @type {readonly string[]} */ ([\"en\", \"de\"]);\n\n/** @type {string} */\nexport const cookieName = \"<cookie-name>\";\n\n/** @type {number} */\nexport const cookieMaxAge = 60 * 60 * 24 * 400;\n\n/** @type {string} */\nexport const cookieDomain = \"<cookie-domain>\";\n\n/** @type {string} */\nexport const localStorageKey = \"PARAGLIDE_LOCALE\";\n\n/**\n * @type {Array<\"cookie\" | \"baseLocale\" | \"globalVariable\" | \"url\" | \"preferredLanguage\" | \"localStorage\" | `custom-${string}`>}\n */\nexport const strategy = [\"globalVariable\"];\n\n/**\n * Route-level strategy overrides.\n *\n * `match` uses URLPattern syntax.\n *\n * @type {Array<{\n * match: string;\n * strategy?: Array<\"cookie\" | \"baseLocale\" | \"globalVariable\" | \"url\" | \"preferredLanguage\" | \"localStorage\" | `custom-${string}`>;\n * exclude?: boolean;\n * }>}\n */\nexport const routeStrategies = [];\n\n/**\n * The used URL patterns.\n *\n * @type {Array<{ pattern: string, localized: Array<[Locale, string]> }>}\n */\nexport const urlPatterns = [];\n\n/**\n * Per-host routing table for the fast string-based URL strategy.\n *\n * @type {Record<string, { baseLocale: Locale, locales: Locale[] }>}\n */\nexport const routing = {};\n\n/**\n * @typedef {{\n * \t\tgetStore(): {\n * \t\tlocale?: Locale,\n * \t\t\torigin?: string,\n * \t\t\tmessageCalls?: Set<string>\n * \t} | undefined,\n * \t\trun: (store: { locale?: Locale, origin?: string, messageCalls?: Set<string>},\n * cb: any) => any\n * }} ParaglideAsyncLocalStorage\n */\n\n/**\n * Server side async local storage that is set by `serverMiddleware()`.\n *\n * The variable is used to retrieve the locale and origin in a server-side\n * rendering context without effecting other requests.\n *\n * @type {ParaglideAsyncLocalStorage | undefined}\n */\nexport let serverAsyncLocalStorage = undefined;\n\n/**\n * Returns the current server-side async local storage instance.\n *\n * Accessing the mutable value through a function keeps it observable when\n * module interceptors wrap exported bindings and snapshot their initial value.\n *\n * @returns {ParaglideAsyncLocalStorage | undefined}\n */\nexport function getServerAsyncLocalStorage() {\n\treturn serverAsyncLocalStorage;\n}\n\nexport const disableAsyncLocalStorage = false;\n\nexport const experimentalMiddlewareLocaleSplitting = false;\n\nexport const isServer = typeof window === \"undefined\";\n\n/** @type {Locale | undefined} */\nexport const experimentalStaticLocale = undefined;\n\n/**\n * Sets the server side async local storage.\n *\n * The function is needed because the `runtime.js` file\n * must define the `serverAsyncLocalStorage` variable to\n * avoid a circular import between `runtime.js` and\n * `server.js` files.\n *\n * @param {ParaglideAsyncLocalStorage | undefined} value\n */\nexport function overwriteServerAsyncLocalStorage(value) {\n\tserverAsyncLocalStorage = value;\n}\n\nexport const TREE_SHAKE_COOKIE_STRATEGY_USED = false;\n\nexport const TREE_SHAKE_URL_STRATEGY_USED = false;\n\nexport const TREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED = false;\n\nexport const TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED = false;\n\nexport const TREE_SHAKE_DEFAULT_URL_PATTERN_USED = false;\n\nexport const TREE_SHAKE_HOST_ROUTING_USED = false;\n\nexport const TREE_SHAKE_LOCAL_STORAGE_STRATEGY_USED = false;\n"]}
|
|
1
|
+
{"version":3,"file":"variables.js","sourceRoot":"","sources":["../../../src/compiler/runtime/variables.js"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC;AAE/B;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,gCAAgC,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;AAEvE,qBAAqB;AACrB,MAAM,CAAC,MAAM,UAAU,GAAG,eAAe,CAAC;AAE1C,qBAAqB;AACrB,MAAM,CAAC,MAAM,YAAY,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC;AAE/C,qBAAqB;AACrB,MAAM,CAAC,MAAM,YAAY,GAAG,iBAAiB,CAAC;AAE9C,qBAAqB;AACrB,MAAM,CAAC,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAElD;;GAEG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,gBAAgB,CAAC,CAAC;AAE3C;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,CAAC;AAElC;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAE9B;;;;GAIG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,EAAE,CAAC;AAE1B;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,SAAS,CAAC;AAEvC;;;;;;;;;;GAUG;AAEH;;;;;;;GAOG;AACH,MAAM,CAAC,IAAI,uBAAuB,GAAG,SAAS,CAAC;AAE/C;;;;;;;GAOG;AACH,MAAM,UAAU,0BAA0B;IACzC,OAAO,uBAAuB,CAAC;AAChC,CAAC;AAED,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAK,CAAC;AAE9C,MAAM,CAAC,MAAM,qCAAqC,GAAG,KAAK,CAAC;AAE3D,MAAM,CAAC,MAAM,QAAQ,GAAG,OAAO,MAAM,KAAK,WAAW,CAAC;AAEtD,iCAAiC;AACjC,MAAM,CAAC,MAAM,wBAAwB,GAAG,SAAS,CAAC;AAElD;;;;;;;;;GASG;AACH,MAAM,UAAU,gCAAgC,CAAC,KAAK;IACrD,uBAAuB,GAAG,KAAK,CAAC;AACjC,CAAC;AAED,MAAM,CAAC,MAAM,+BAA+B,GAAG,KAAK,CAAC;AAErD,MAAM,CAAC,MAAM,4BAA4B,GAAG,KAAK,CAAC;AAElD,MAAM,CAAC,MAAM,wCAAwC,GAAG,KAAK,CAAC;AAE9D,MAAM,CAAC,MAAM,2CAA2C,GAAG,KAAK,CAAC;AAEjE,MAAM,CAAC,MAAM,mCAAmC,GAAG,KAAK,CAAC;AAEzD,MAAM,CAAC,MAAM,4BAA4B,GAAG,KAAK,CAAC;AAElD,MAAM,CAAC,MAAM,sCAAsC,GAAG,KAAK,CAAC","sourcesContent":["/**\n * The project's base locale.\n *\n * @example\n * if (locale === baseLocale) {\n * // do something\n * }\n */\nexport const baseLocale = \"en\";\n\n/**\n * The project's locales that have been specified in the settings.\n *\n * @example\n * if (locales.includes(userSelectedLocale) === false) {\n * throw new Error('Locale is not available');\n * }\n */\nexport const locales = /** @type {readonly string[]} */ ([\"en\", \"de\"]);\n\n/** @type {string} */\nexport const cookieName = \"<cookie-name>\";\n\n/** @type {number} */\nexport const cookieMaxAge = 60 * 60 * 24 * 400;\n\n/** @type {string} */\nexport const cookieDomain = \"<cookie-domain>\";\n\n/** @type {string} */\nexport const localStorageKey = \"PARAGLIDE_LOCALE\";\n\n/**\n * @type {Array<\"cookie\" | \"baseLocale\" | \"globalVariable\" | \"url\" | \"preferredLanguage\" | \"localStorage\" | `custom-${string}`>}\n */\nexport const strategy = [\"globalVariable\"];\n\n/**\n * Route-level strategy overrides.\n *\n * `match` uses URLPattern syntax.\n *\n * @type {Array<{\n * match: string;\n * strategy?: Array<\"cookie\" | \"baseLocale\" | \"globalVariable\" | \"url\" | \"preferredLanguage\" | \"localStorage\" | `custom-${string}`>;\n * exclude?: boolean;\n * }>}\n */\nexport const routeStrategies = [];\n\n/**\n * The used URL patterns.\n *\n * @type {Array<{ pattern: string, localized: Array<[Locale, string]> }>}\n */\nexport const urlPatterns = [];\n\n/**\n * Per-host routing table for the fast string-based URL strategy.\n *\n * @type {Record<string, { baseLocale: Locale, locales: Locale[] }>}\n */\nexport const routing = {};\n\n/**\n * Controls trailing slash canonicalization for localized URLs.\n *\n * @type {\"always\" | \"never\" | undefined}\n */\nexport const trailingSlash = undefined;\n\n/**\n * @typedef {{\n * \t\tgetStore(): {\n * \t\tlocale?: Locale,\n * \t\t\torigin?: string,\n * \t\t\tmessageCalls?: Set<string>\n * \t} | undefined,\n * \t\trun: (store: { locale?: Locale, origin?: string, messageCalls?: Set<string>},\n * cb: any) => any\n * }} ParaglideAsyncLocalStorage\n */\n\n/**\n * Server side async local storage that is set by `serverMiddleware()`.\n *\n * The variable is used to retrieve the locale and origin in a server-side\n * rendering context without effecting other requests.\n *\n * @type {ParaglideAsyncLocalStorage | undefined}\n */\nexport let serverAsyncLocalStorage = undefined;\n\n/**\n * Returns the current server-side async local storage instance.\n *\n * Accessing the mutable value through a function keeps it observable when\n * module interceptors wrap exported bindings and snapshot their initial value.\n *\n * @returns {ParaglideAsyncLocalStorage | undefined}\n */\nexport function getServerAsyncLocalStorage() {\n\treturn serverAsyncLocalStorage;\n}\n\nexport const disableAsyncLocalStorage = false;\n\nexport const experimentalMiddlewareLocaleSplitting = false;\n\nexport const isServer = typeof window === \"undefined\";\n\n/** @type {Locale | undefined} */\nexport const experimentalStaticLocale = undefined;\n\n/**\n * Sets the server side async local storage.\n *\n * The function is needed because the `runtime.js` file\n * must define the `serverAsyncLocalStorage` variable to\n * avoid a circular import between `runtime.js` and\n * `server.js` files.\n *\n * @param {ParaglideAsyncLocalStorage | undefined} value\n */\nexport function overwriteServerAsyncLocalStorage(value) {\n\tserverAsyncLocalStorage = value;\n}\n\nexport const TREE_SHAKE_COOKIE_STRATEGY_USED = false;\n\nexport const TREE_SHAKE_URL_STRATEGY_USED = false;\n\nexport const TREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED = false;\n\nexport const TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED = false;\n\nexport const TREE_SHAKE_DEFAULT_URL_PATTERN_USED = false;\n\nexport const TREE_SHAKE_HOST_ROUTING_USED = false;\n\nexport const TREE_SHAKE_LOCAL_STORAGE_STRATEGY_USED = false;\n"]}
|
|
@@ -47,7 +47,16 @@ ${injectCode("./middleware.js")}
|
|
|
47
47
|
`${asyncLocalStorageBlock.join("\n")}\n` +
|
|
48
48
|
code.slice(endOfMarkerLine);
|
|
49
49
|
if (args.compilerOptions.experimentalMiddlewareLocaleSplitting) {
|
|
50
|
-
|
|
50
|
+
// Use a replacer function, not a replacement string. `String.replace()`
|
|
51
|
+
// treats a string replacement as a pattern where `$&`, `` $` ``, `$'`,
|
|
52
|
+
// `$$`, and `$<name>` have special meaning. A translated message value
|
|
53
|
+
// that contains a literal `$` (a price, a currency symbol) can produce
|
|
54
|
+
// one of these sequences inside the JSON payload, which `String.replace`
|
|
55
|
+
// then expands - e.g. `` $` `` is replaced with "everything before the
|
|
56
|
+
// match", splicing the file's own header into the string being built and
|
|
57
|
+
// corrupting the generated server file. A function's return value is
|
|
58
|
+
// used literally, with no pattern expansion.
|
|
59
|
+
code = code.replace("const compiledBundles = {};", () => `const compiledBundles = ${JSON.stringify(createCompiledMessagesObject(args.compiledBundles))};`);
|
|
51
60
|
}
|
|
52
61
|
return code.replace(/\t/g, " ");
|
|
53
62
|
}
|
|
@@ -56,11 +65,16 @@ function createCompiledMessagesObject(compiledBundles) {
|
|
|
56
65
|
for (const compiledBundle of compiledBundles) {
|
|
57
66
|
const bundleId = compiledBundle.bundle.node.id;
|
|
58
67
|
const safeModuleId = toSafeModuleId(bundleId);
|
|
59
|
-
|
|
60
|
-
|
|
68
|
+
// Key by the safe module id, not the raw bundle id: trackMessageCall()
|
|
69
|
+
// records calls under safeModuleId, the client reads
|
|
70
|
+
// globalThis.__paraglide.ssr[safeModuleId], and middleware.js looks up
|
|
71
|
+
// compiledBundles[id] with that same safe id. Keying by the raw
|
|
72
|
+
// bundleId here made every split-mode lookup miss.
|
|
73
|
+
if (result[safeModuleId] === undefined) {
|
|
74
|
+
result[safeModuleId] = {};
|
|
61
75
|
}
|
|
62
76
|
for (const [locale, compiledMessage] of Object.entries(compiledBundle.messages)) {
|
|
63
|
-
result[
|
|
77
|
+
result[safeModuleId][locale] = compiledMessage.code
|
|
64
78
|
.replace(`export const ${safeModuleId} = `, "")
|
|
65
79
|
.replace(/;$/, "");
|
|
66
80
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-server-file.js","sourceRoot":"","sources":["../../../src/compiler/server/create-server-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAGzB,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGtD;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAUhC;IACA,IAAI,IAAI,GAAG;;;EAGV,UAAU,CAAC,iBAAiB,CAAC;CAC9B,CAAC;IAED,MAAM,uBAAuB,GAAG,yBAAyB,CAAC;IAC1D,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAC1D,IAAI,WAAW,KAAK,CAAC,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACd,qEAAqE,CACrE,CAAC;IACH,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACvE,MAAM,WAAW,GAAG,GAAG,MAAM,IAAI,CAAC;IAClC,MAAM,sBAAsB,GAAG,IAAI,CAAC,eAAe,CAAC,wBAAwB;QAC3E,CAAC,CAAC;YACA,GAAG,MAAM,kEAAkE;YAC3E,GAAG,MAAM,kCAAkC;YAC3C,GAAG,WAAW,2DAA2D;YACzE,GAAG,WAAW,qEAAqE;YACnF,GAAG,MAAM,GAAG;SACZ;QACF,CAAC,CAAC;YACA,GAAG,MAAM,kEAAkE;YAC3E,GAAG,MAAM,uEAAuE;YAChF,GAAG,WAAW,iEAAiE;YAC/E,GAAG,WAAW,kEAAkE;YAChF,GAAG,WAAW,kCAAkC;YAChD,GAAG,WAAW,uDAAuD;YACrE,GAAG,WAAW,uEAAuE;YACrF,GAAG,WAAW,GAAG;YACjB,GAAG,MAAM,GAAG;YACZ,GAAG,MAAM,kCAAkC;YAC3C,GAAG,WAAW,2DAA2D;YACzE,GAAG,WAAW,qEAAqE;YACnF,GAAG,MAAM,GAAG;SACZ,CAAC;IAEJ,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAChD,MAAM,eAAe,GAAG,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC;IAEnE,IAAI;QACH,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC;YACxB,GAAG,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;YACxC,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IAE7B,IAAI,IAAI,CAAC,eAAe,CAAC,qCAAqC,EAAE,CAAC;QAChE,IAAI,GAAG,IAAI,CAAC,OAAO,CAClB,6BAA6B,EAC7B,2BAA2B,IAAI,CAAC,SAAS,CAAC,4BAA4B,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,GAAG,
|
|
1
|
+
{"version":3,"file":"create-server-file.js","sourceRoot":"","sources":["../../../src/compiler/server/create-server-file.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from \"node:fs\";\nimport type { CompiledBundleWithMessages } from \"../compile-bundle.js\";\nimport type { CompilerOptions } from \"../compiler-options.js\";\nimport { toSafeModuleId } from \"../safe-module-id.js\";\nimport type { Locale } from \"./runtime.d.ts\";\n\n/**\n * Returns the code for the `runtime.js` module\n */\nexport function createServerFile(args: {\n\tcompiledBundles: CompiledBundleWithMessages[];\n\tcompilerOptions: {\n\t\texperimentalMiddlewareLocaleSplitting: NonNullable<\n\t\t\tCompilerOptions[\"experimentalMiddlewareLocaleSplitting\"]\n\t\t>;\n\t\tdisableAsyncLocalStorage: NonNullable<\n\t\t\tCompilerOptions[\"disableAsyncLocalStorage\"]\n\t\t>;\n\t};\n}): string {\n\tlet code = `\nimport * as runtime from \"./runtime.js\";\n\n${injectCode(\"./middleware.js\")}\n`;\n\n\tconst asyncLocalStorageMarker = \"// %async-local-storage\";\n\tconst markerIndex = code.indexOf(asyncLocalStorageMarker);\n\tif (markerIndex === -1) {\n\t\tthrow new Error(\n\t\t\t\"Expected a single %async-local-storage marker in server middleware.\"\n\t\t);\n\t}\n\n\tconst lineStart = code.lastIndexOf(\"\\n\", markerIndex) + 1;\n\tconst indent = code.slice(lineStart, markerIndex).replace(/\\t/g, \" \");\n\tconst innerIndent = `${indent} `;\n\tconst asyncLocalStorageBlock = args.compilerOptions.disableAsyncLocalStorage\n\t\t? [\n\t\t\t\t`${indent}requestAsyncLocalStorage = runtime.getServerAsyncLocalStorage();`,\n\t\t\t\t`${indent}if (!requestAsyncLocalStorage) {`,\n\t\t\t\t`${innerIndent}requestAsyncLocalStorage = createMockAsyncLocalStorage();`,\n\t\t\t\t`${innerIndent}runtime.overwriteServerAsyncLocalStorage(requestAsyncLocalStorage);`,\n\t\t\t\t`${indent}}`,\n\t\t\t]\n\t\t: [\n\t\t\t\t`${indent}requestAsyncLocalStorage = runtime.getServerAsyncLocalStorage();`,\n\t\t\t\t`${indent}if (!runtime.disableAsyncLocalStorage && !requestAsyncLocalStorage) {`,\n\t\t\t\t`${innerIndent}const { AsyncLocalStorage } = await import(\"node:async_hooks\");`,\n\t\t\t\t`${innerIndent}requestAsyncLocalStorage = runtime.getServerAsyncLocalStorage();`,\n\t\t\t\t`${innerIndent}if (!requestAsyncLocalStorage) {`,\n\t\t\t\t`${innerIndent} requestAsyncLocalStorage = new AsyncLocalStorage();`,\n\t\t\t\t`${innerIndent} runtime.overwriteServerAsyncLocalStorage(requestAsyncLocalStorage);`,\n\t\t\t\t`${innerIndent}}`,\n\t\t\t\t`${indent}}`,\n\t\t\t\t`${indent}if (!requestAsyncLocalStorage) {`,\n\t\t\t\t`${innerIndent}requestAsyncLocalStorage = createMockAsyncLocalStorage();`,\n\t\t\t\t`${innerIndent}runtime.overwriteServerAsyncLocalStorage(requestAsyncLocalStorage);`,\n\t\t\t\t`${indent}}`,\n\t\t\t];\n\n\tconst lineEnd = code.indexOf(\"\\n\", markerIndex);\n\tconst endOfMarkerLine = lineEnd === -1 ? code.length : lineEnd + 1;\n\n\tcode =\n\t\tcode.slice(0, lineStart) +\n\t\t`${asyncLocalStorageBlock.join(\"\\n\")}\\n` +\n\t\tcode.slice(endOfMarkerLine);\n\n\tif (args.compilerOptions.experimentalMiddlewareLocaleSplitting) {\n\t\t// Use a replacer function, not a replacement string. `String.replace()`\n\t\t// treats a string replacement as a pattern where `$&`, `` $` ``, `$'`,\n\t\t// `$$`, and `$<name>` have special meaning. A translated message value\n\t\t// that contains a literal `$` (a price, a currency symbol) can produce\n\t\t// one of these sequences inside the JSON payload, which `String.replace`\n\t\t// then expands - e.g. `` $` `` is replaced with \"everything before the\n\t\t// match\", splicing the file's own header into the string being built and\n\t\t// corrupting the generated server file. A function's return value is\n\t\t// used literally, with no pattern expansion.\n\t\tcode = code.replace(\n\t\t\t\"const compiledBundles = {};\",\n\t\t\t() =>\n\t\t\t\t`const compiledBundles = ${JSON.stringify(createCompiledMessagesObject(args.compiledBundles))};`\n\t\t);\n\t}\n\n\treturn code.replace(/\\t/g, \" \");\n}\n\nfunction createCompiledMessagesObject(\n\tcompiledBundles: CompiledBundleWithMessages[]\n): Record<string, Record<Locale, string>> {\n\tconst result = {} as Record<string, Record<Locale, string>>;\n\n\tfor (const compiledBundle of compiledBundles) {\n\t\tconst bundleId = compiledBundle.bundle.node.id;\n\t\tconst safeModuleId = toSafeModuleId(bundleId);\n\t\t// Key by the safe module id, not the raw bundle id: trackMessageCall()\n\t\t// records calls under safeModuleId, the client reads\n\t\t// globalThis.__paraglide.ssr[safeModuleId], and middleware.js looks up\n\t\t// compiledBundles[id] with that same safe id. Keying by the raw\n\t\t// bundleId here made every split-mode lookup miss.\n\t\tif (result[safeModuleId] === undefined) {\n\t\t\tresult[safeModuleId] = {};\n\t\t}\n\t\tfor (const [locale, compiledMessage] of Object.entries(\n\t\t\tcompiledBundle.messages\n\t\t)) {\n\t\t\tresult[safeModuleId][locale] = compiledMessage.code\n\t\t\t\t.replace(`export const ${safeModuleId} = `, \"\")\n\t\t\t\t.replace(/;$/, \"\");\n\t\t}\n\t}\n\treturn result;\n}\n\n/**\n * Load a file from the current directory.\n *\n * Prunes the imports on top of the file as the runtime is\n * self-contained.\n *\n * @param {string} path\n * @returns {string}\n */\nfunction injectCode(path: string): string {\n\tconst code = fs.readFileSync(new URL(path, import.meta.url), \"utf-8\");\n\t// Regex to match single-line and multi-line imports\n\tconst importRegex = /import\\s+[\\s\\S]*?from\\s+['\"][^'\"]+['\"]\\s*;?/g;\n\tconst sourceMapRegex = /\\/\\/# sourceMappingURL=.*$/gm;\n\tconst blockSourceMapRegex = /\\/\\*# sourceMappingURL=.*?\\*\\//g;\n\treturn code\n\t\t.replace(importRegex, \"\")\n\t\t.replace(sourceMapRegex, \"\")\n\t\t.replace(blockSourceMapRegex, \"\")\n\t\t.trim();\n}\n"]}
|
|
@@ -3,6 +3,7 @@ import { tmpdir } from "node:os";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
import { afterEach, expect, test } from "vitest";
|
|
6
|
+
import { toSafeModuleId } from "../safe-module-id.js";
|
|
6
7
|
import { createServerFile } from "./create-server-file.js";
|
|
7
8
|
const temporaryDirectories = [];
|
|
8
9
|
afterEach(async () => {
|
|
@@ -45,4 +46,79 @@ export const isExcludedByRouteStrategy = runtime.isExcludedByRouteStrategy;`);
|
|
|
45
46
|
expect(response.status).toBe(200);
|
|
46
47
|
expect(await response.text()).toBe("ready");
|
|
47
48
|
});
|
|
49
|
+
test("keys compiledBundles by the safe module id, not the raw bundle id (experimentalMiddlewareLocaleSplitting)", () => {
|
|
50
|
+
// A bundle id that changes under toSafeModuleId (case-folded + a numeric
|
|
51
|
+
// suffix appended for the uppercase letters). trackMessageCall(), the
|
|
52
|
+
// client's SSR message guard, and middleware.js all look up
|
|
53
|
+
// compiledBundles by this safe id - only createCompiledMessagesObject
|
|
54
|
+
// (inside createServerFile) used the raw id instead.
|
|
55
|
+
const bundleId = "GREETING";
|
|
56
|
+
const safeModuleId = toSafeModuleId(bundleId);
|
|
57
|
+
expect(safeModuleId).not.toBe(bundleId);
|
|
58
|
+
const compiledBundles = [
|
|
59
|
+
{
|
|
60
|
+
bundle: { node: { id: bundleId } },
|
|
61
|
+
messages: {
|
|
62
|
+
en: { code: `export const ${safeModuleId} = () => "hello";` },
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
const serverCode = createServerFile({
|
|
67
|
+
compiledBundles,
|
|
68
|
+
compilerOptions: {
|
|
69
|
+
disableAsyncLocalStorage: false,
|
|
70
|
+
experimentalMiddlewareLocaleSplitting: true,
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
expect(serverCode).toContain(`"${safeModuleId}":{"en":`);
|
|
74
|
+
expect(serverCode).not.toContain(`"${bundleId}":{"en":`);
|
|
75
|
+
});
|
|
76
|
+
test("a literal '$' in a compiled message does not corrupt the generated file (experimentalMiddlewareLocaleSplitting)", async () => {
|
|
77
|
+
const directory = await mkdtemp(join(tmpdir(), "paraglide-server-dollar-"));
|
|
78
|
+
temporaryDirectories.push(directory);
|
|
79
|
+
// The compiled message source for locale "en" evaluates to the literal
|
|
80
|
+
// text "$", wrapped in a template literal by the compiler (i.e. it
|
|
81
|
+
// contains a backtick immediately followed by a `$` immediately followed
|
|
82
|
+
// by another backtick). Passed as a *string* to `code.replace()`, `` $` ``
|
|
83
|
+
// is a special replacement pattern ("insert everything before the
|
|
84
|
+
// match"), which splices the file's own header into the string being
|
|
85
|
+
// built and corrupts the generated server file.
|
|
86
|
+
const compiledBundles = [
|
|
87
|
+
{
|
|
88
|
+
bundle: { node: { id: "price_symbol" } },
|
|
89
|
+
messages: {
|
|
90
|
+
en: { code: "export const price_symbol = () => `$`;" },
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
];
|
|
94
|
+
const serverCode = createServerFile({
|
|
95
|
+
compiledBundles,
|
|
96
|
+
compilerOptions: {
|
|
97
|
+
disableAsyncLocalStorage: false,
|
|
98
|
+
experimentalMiddlewareLocaleSplitting: true,
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
// The message value must survive JSON serialization untouched.
|
|
102
|
+
expect(serverCode).toContain('"price_symbol":{"en":"() => `$`"}');
|
|
103
|
+
// The generated file must still be syntactically valid and importable
|
|
104
|
+
// (the bug produces an unterminated string / invalid JS).
|
|
105
|
+
await writeFile(join(directory, "runtime.js"), `export const serverAsyncLocalStorage = undefined;
|
|
106
|
+
export const disableAsyncLocalStorage = false;
|
|
107
|
+
export const experimentalMiddlewareLocaleSplitting = true;
|
|
108
|
+
export const baseLocale = "en";
|
|
109
|
+
export function isExcludedByRouteStrategy() {
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
export function getStrategyForUrl() {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
export async function shouldRedirect() {
|
|
116
|
+
return { locale: "en", shouldRedirect: false };
|
|
117
|
+
}
|
|
118
|
+
export function deLocalizeUrl(url) {
|
|
119
|
+
return url;
|
|
120
|
+
}`);
|
|
121
|
+
await writeFile(join(directory, "server.mjs"), serverCode);
|
|
122
|
+
await expect(import(pathToFileURL(join(directory, "server.mjs")).href)).resolves.toBeDefined();
|
|
123
|
+
});
|
|
48
124
|
//# sourceMappingURL=create-server-file.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-server-file.test.js","sourceRoot":"","sources":["../../../src/compiler/server/create-server-file.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"create-server-file.test.js","sourceRoot":"","sources":["../../../src/compiler/server/create-server-file.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdtemp, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { afterEach, expect, test } from \"vitest\";\nimport type { CompiledBundleWithMessages } from \"../compile-bundle.js\";\nimport { toSafeModuleId } from \"../safe-module-id.js\";\nimport { createServerFile } from \"./create-server-file.js\";\n\nconst temporaryDirectories: string[] = [];\n\nafterEach(async () => {\n\tawait Promise.all(\n\t\ttemporaryDirectories\n\t\t\t.splice(0)\n\t\t\t.map((directory) => rm(directory, { recursive: true, force: true }))\n\t);\n});\n\ntest(\"uses the initialized AsyncLocalStorage when an import interceptor snapshots the initial export\", async () => {\n\tconst directory = await mkdtemp(join(tmpdir(), \"paraglide-server-\"));\n\ttemporaryDirectories.push(directory);\n\n\tconst serverCode = createServerFile({\n\t\tcompiledBundles: [],\n\t\tcompilerOptions: {\n\t\t\tdisableAsyncLocalStorage: false,\n\t\t\texperimentalMiddlewareLocaleSplitting: false,\n\t\t},\n\t}).replace('\"./runtime.js\"', '\"./runtime-wrapper.mjs\"');\n\n\tawait writeFile(join(directory, \"server.mjs\"), serverCode);\n\tawait writeFile(\n\t\tjoin(directory, \"runtime-real.mjs\"),\n\t\t`export let serverAsyncLocalStorage;\nexport const disableAsyncLocalStorage = false;\nexport function overwriteServerAsyncLocalStorage(value) {\n serverAsyncLocalStorage = value;\n}\nexport function getServerAsyncLocalStorage() {\n return serverAsyncLocalStorage;\n}\nexport const baseLocale = \"en\";\nexport function isExcludedByRouteStrategy() {\n return true;\n}`\n\t);\n\tawait writeFile(\n\t\tjoin(directory, \"runtime-wrapper.mjs\"),\n\t\t`import * as runtime from \"./runtime-real.mjs\";\nexport const serverAsyncLocalStorage = runtime.serverAsyncLocalStorage;\nexport const disableAsyncLocalStorage = runtime.disableAsyncLocalStorage;\nexport const overwriteServerAsyncLocalStorage = runtime.overwriteServerAsyncLocalStorage;\nexport const getServerAsyncLocalStorage = runtime.getServerAsyncLocalStorage;\nexport const baseLocale = runtime.baseLocale;\nexport const isExcludedByRouteStrategy = runtime.isExcludedByRouteStrategy;`\n\t);\n\n\tconst server = await import(\n\t\tpathToFileURL(join(directory, \"server.mjs\")).href\n\t);\n\tconst response = await server.paraglideMiddleware(\n\t\tnew Request(\"https://example.com/api/ready\"),\n\t\t() => new Response(\"ready\")\n\t);\n\n\texpect(response.status).toBe(200);\n\texpect(await response.text()).toBe(\"ready\");\n});\n\ntest(\"keys compiledBundles by the safe module id, not the raw bundle id (experimentalMiddlewareLocaleSplitting)\", () => {\n\t// A bundle id that changes under toSafeModuleId (case-folded + a numeric\n\t// suffix appended for the uppercase letters). trackMessageCall(), the\n\t// client's SSR message guard, and middleware.js all look up\n\t// compiledBundles by this safe id - only createCompiledMessagesObject\n\t// (inside createServerFile) used the raw id instead.\n\tconst bundleId = \"GREETING\";\n\tconst safeModuleId = toSafeModuleId(bundleId);\n\texpect(safeModuleId).not.toBe(bundleId);\n\n\tconst compiledBundles = [\n\t\t{\n\t\t\tbundle: { node: { id: bundleId } },\n\t\t\tmessages: {\n\t\t\t\ten: { code: `export const ${safeModuleId} = () => \"hello\";` },\n\t\t\t},\n\t\t},\n\t] as unknown as CompiledBundleWithMessages[];\n\n\tconst serverCode = createServerFile({\n\t\tcompiledBundles,\n\t\tcompilerOptions: {\n\t\t\tdisableAsyncLocalStorage: false,\n\t\t\texperimentalMiddlewareLocaleSplitting: true,\n\t\t},\n\t});\n\n\texpect(serverCode).toContain(`\"${safeModuleId}\":{\"en\":`);\n\texpect(serverCode).not.toContain(`\"${bundleId}\":{\"en\":`);\n});\n\ntest(\"a literal '$' in a compiled message does not corrupt the generated file (experimentalMiddlewareLocaleSplitting)\", async () => {\n\tconst directory = await mkdtemp(join(tmpdir(), \"paraglide-server-dollar-\"));\n\ttemporaryDirectories.push(directory);\n\n\t// The compiled message source for locale \"en\" evaluates to the literal\n\t// text \"$\", wrapped in a template literal by the compiler (i.e. it\n\t// contains a backtick immediately followed by a `$` immediately followed\n\t// by another backtick). Passed as a *string* to `code.replace()`, `` $` ``\n\t// is a special replacement pattern (\"insert everything before the\n\t// match\"), which splices the file's own header into the string being\n\t// built and corrupts the generated server file.\n\tconst compiledBundles = [\n\t\t{\n\t\t\tbundle: { node: { id: \"price_symbol\" } },\n\t\t\tmessages: {\n\t\t\t\ten: { code: \"export const price_symbol = () => `$`;\" },\n\t\t\t},\n\t\t},\n\t] as unknown as CompiledBundleWithMessages[];\n\n\tconst serverCode = createServerFile({\n\t\tcompiledBundles,\n\t\tcompilerOptions: {\n\t\t\tdisableAsyncLocalStorage: false,\n\t\t\texperimentalMiddlewareLocaleSplitting: true,\n\t\t},\n\t});\n\n\t// The message value must survive JSON serialization untouched.\n\texpect(serverCode).toContain('\"price_symbol\":{\"en\":\"() => `$`\"}');\n\n\t// The generated file must still be syntactically valid and importable\n\t// (the bug produces an unterminated string / invalid JS).\n\tawait writeFile(\n\t\tjoin(directory, \"runtime.js\"),\n\t\t`export const serverAsyncLocalStorage = undefined;\nexport const disableAsyncLocalStorage = false;\nexport const experimentalMiddlewareLocaleSplitting = true;\nexport const baseLocale = \"en\";\nexport function isExcludedByRouteStrategy() {\n return true;\n}\nexport function getStrategyForUrl() {\n return [];\n}\nexport async function shouldRedirect() {\n return { locale: \"en\", shouldRedirect: false };\n}\nexport function deLocalizeUrl(url) {\n return url;\n}`\n\t);\n\tawait writeFile(join(directory, \"server.mjs\"), serverCode);\n\n\tawait expect(\n\t\timport(pathToFileURL(join(directory, \"server.mjs\")).href)\n\t).resolves.toBeDefined();\n});\n"]}
|
|
@@ -103,7 +103,10 @@ export async function paraglideMiddleware(request, resolve, options) {
|
|
|
103
103
|
return /** @type {Response} */ (await requestAsyncLocalStorage?.run({ locale, origin, messageCalls }, () => resolve({ locale, request: newRequest })));
|
|
104
104
|
}
|
|
105
105
|
const strategy = runtime.getStrategyForUrl(url.href);
|
|
106
|
-
const decision = await runtime.shouldRedirect({
|
|
106
|
+
const decision = await runtime.shouldRedirect({
|
|
107
|
+
request,
|
|
108
|
+
effectiveRequestUrl: url,
|
|
109
|
+
});
|
|
107
110
|
const locale = decision.locale;
|
|
108
111
|
// if the client makes a request to a URL that doesn't match
|
|
109
112
|
// the localizedUrl, redirect the client to the localized URL
|
|
@@ -188,7 +191,8 @@ function resolveMiddlewareUrl(request, effectiveRequestUrl) {
|
|
|
188
191
|
if (typeof effectiveRequestUrl === "function") {
|
|
189
192
|
return new URL(effectiveRequestUrl(request), request.url);
|
|
190
193
|
}
|
|
191
|
-
if (typeof effectiveRequestUrl === "string" ||
|
|
194
|
+
if (typeof effectiveRequestUrl === "string" ||
|
|
195
|
+
effectiveRequestUrl instanceof URL) {
|
|
192
196
|
return new URL(effectiveRequestUrl, request.url);
|
|
193
197
|
}
|
|
194
198
|
return new URL(request.url);
|