@sebbro/paraglide-lite 0.1.0 → 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.
Files changed (126) hide show
  1. package/dist/bundler-plugins/per-locale-build/index.js +5 -3
  2. package/dist/bundler-plugins/per-locale-build/index.js.map +1 -1
  3. package/dist/bundler-plugins/per-locale-build/vite-environments.js +12 -7
  4. package/dist/bundler-plugins/per-locale-build/vite-environments.js.map +1 -1
  5. package/dist/bundler-plugins/plugin-core.d.ts +38 -2
  6. package/dist/bundler-plugins/plugin-core.js +198 -11
  7. package/dist/bundler-plugins/plugin-core.js.map +1 -1
  8. package/dist/bundler-plugins/rolldown.test.js +15 -6
  9. package/dist/bundler-plugins/rolldown.test.js.map +1 -1
  10. package/dist/bundler-plugins/unplugin.js +5 -0
  11. package/dist/bundler-plugins/unplugin.js.map +1 -1
  12. package/dist/bundler-plugins/unplugin.test.js +67 -0
  13. package/dist/bundler-plugins/unplugin.test.js.map +1 -1
  14. package/dist/bundler-plugins/virtual-modules.test.js +64 -1
  15. package/dist/bundler-plugins/virtual-modules.test.js.map +1 -1
  16. package/dist/cli/commands/compile/command.js +8 -1
  17. package/dist/cli/commands/compile/command.js.map +1 -1
  18. package/dist/cli/commands/compile/command.test.js +51 -0
  19. package/dist/cli/commands/compile/command.test.js.map +1 -1
  20. package/dist/compiler/compile-annotation.d.ts +2 -2
  21. package/dist/compiler/compile-annotation.js +9 -9
  22. package/dist/compiler/compile-annotation.js.map +1 -1
  23. package/dist/compiler/compile-local-variable.d.ts +2 -1
  24. package/dist/compiler/compile-local-variable.js +4 -4
  25. package/dist/compiler/compile-local-variable.js.map +1 -1
  26. package/dist/compiler/compile-local-variable.test.js +82 -0
  27. package/dist/compiler/compile-local-variable.test.js.map +1 -1
  28. package/dist/compiler/compile-message.js +10 -2
  29. package/dist/compiler/compile-message.js.map +1 -1
  30. package/dist/compiler/compile-message.test.js +132 -2
  31. package/dist/compiler/compile-message.test.js.map +1 -1
  32. package/dist/compiler/compile-pattern.js +4 -2
  33. package/dist/compiler/compile-pattern.js.map +1 -1
  34. package/dist/compiler/compile-pattern.test.js +31 -0
  35. package/dist/compiler/compile-pattern.test.js.map +1 -1
  36. package/dist/compiler/compile.bench.js +1 -1
  37. package/dist/compiler/compile.bench.js.map +1 -1
  38. package/dist/compiler/compile.test.js +14 -0
  39. package/dist/compiler/compile.test.js.map +1 -1
  40. package/dist/compiler/compiler-options.d.ts +9 -1
  41. package/dist/compiler/compiler-options.js.map +1 -1
  42. package/dist/compiler/emit-message-dts.d.ts +18 -0
  43. package/dist/compiler/emit-message-dts.js +166 -0
  44. package/dist/compiler/emit-message-dts.js.map +1 -0
  45. package/dist/compiler/emit-message-dts.test.d.ts +1 -0
  46. package/dist/compiler/emit-message-dts.test.js +283 -0
  47. package/dist/compiler/emit-message-dts.test.js.map +1 -0
  48. package/dist/compiler/runtime/create-runtime.d.ts +1 -0
  49. package/dist/compiler/runtime/create-runtime.js +11 -0
  50. package/dist/compiler/runtime/create-runtime.js.map +1 -1
  51. package/dist/compiler/runtime/exec-url-pattern.d.ts +9 -0
  52. package/dist/compiler/runtime/exec-url-pattern.js +31 -0
  53. package/dist/compiler/runtime/exec-url-pattern.js.map +1 -0
  54. package/dist/compiler/runtime/extract-locale-from-url.js +9 -4
  55. package/dist/compiler/runtime/extract-locale-from-url.js.map +1 -1
  56. package/dist/compiler/runtime/generate-static-localized-urls.js +13 -6
  57. package/dist/compiler/runtime/generate-static-localized-urls.js.map +1 -1
  58. package/dist/compiler/runtime/generate-static-localized-urls.test.js +38 -0
  59. package/dist/compiler/runtime/generate-static-localized-urls.test.js.map +1 -1
  60. package/dist/compiler/runtime/host-routing.d.ts +3 -3
  61. package/dist/compiler/runtime/host-routing.js +2 -2
  62. package/dist/compiler/runtime/host-routing.js.map +1 -1
  63. package/dist/compiler/runtime/host-routing.test.js +79 -0
  64. package/dist/compiler/runtime/host-routing.test.js.map +1 -1
  65. package/dist/compiler/runtime/localize-href.bench.d.ts +1 -0
  66. package/dist/compiler/runtime/localize-href.bench.js +68 -0
  67. package/dist/compiler/runtime/localize-href.bench.js.map +1 -0
  68. package/dist/compiler/runtime/localize-href.test.js +35 -0
  69. package/dist/compiler/runtime/localize-href.test.js.map +1 -1
  70. package/dist/compiler/runtime/localize-url.d.ts +14 -0
  71. package/dist/compiler/runtime/localize-url.js +338 -19
  72. package/dist/compiler/runtime/localize-url.js.map +1 -1
  73. package/dist/compiler/runtime/localize-url.test.js +139 -0
  74. package/dist/compiler/runtime/localize-url.test.js.map +1 -1
  75. package/dist/compiler/runtime/normalize-trailing-slash.d.ts +10 -0
  76. package/dist/compiler/runtime/normalize-trailing-slash.js +23 -0
  77. package/dist/compiler/runtime/normalize-trailing-slash.js.map +1 -0
  78. package/dist/compiler/runtime/route-strategy.js +4 -2
  79. package/dist/compiler/runtime/route-strategy.js.map +1 -1
  80. package/dist/compiler/runtime/should-redirect.js +4 -1
  81. package/dist/compiler/runtime/should-redirect.js.map +1 -1
  82. package/dist/compiler/runtime/should-redirect.test.js +26 -2
  83. package/dist/compiler/runtime/should-redirect.test.js.map +1 -1
  84. package/dist/compiler/runtime/type.d.ts +1 -0
  85. package/dist/compiler/runtime/type.js.map +1 -1
  86. package/dist/compiler/runtime/url-pattern.d.ts +11 -0
  87. package/dist/compiler/runtime/url-pattern.js +39 -1
  88. package/dist/compiler/runtime/url-pattern.js.map +1 -1
  89. package/dist/compiler/runtime/variables.d.ts +9 -3
  90. package/dist/compiler/runtime/variables.js +7 -1
  91. package/dist/compiler/runtime/variables.js.map +1 -1
  92. package/dist/compiler/server/create-server-file.js +18 -4
  93. package/dist/compiler/server/create-server-file.js.map +1 -1
  94. package/dist/compiler/server/create-server-file.test.js +76 -0
  95. package/dist/compiler/server/create-server-file.test.js.map +1 -1
  96. package/dist/compiler/server/middleware.js +6 -2
  97. package/dist/compiler/server/middleware.js.map +1 -1
  98. package/dist/compiler/server/middleware.test.js +30 -0
  99. package/dist/compiler/server/middleware.test.js.map +1 -1
  100. package/dist/compiler/strip-jsdoc.d.ts +9 -0
  101. package/dist/compiler/strip-jsdoc.js +19 -0
  102. package/dist/compiler/strip-jsdoc.js.map +1 -0
  103. package/dist/compiler/strip-jsdoc.test.d.ts +1 -0
  104. package/dist/compiler/strip-jsdoc.test.js +95 -0
  105. package/dist/compiler/strip-jsdoc.test.js.map +1 -0
  106. package/dist/compiler/variable-access.d.ts +2 -0
  107. package/dist/compiler/variable-access.js +6 -0
  108. package/dist/compiler/variable-access.js.map +1 -1
  109. package/dist/index.test.d.ts +1 -0
  110. package/dist/index.test.js +9 -0
  111. package/dist/index.test.js.map +1 -0
  112. package/dist/messages/load-messages.js +4 -5
  113. package/dist/messages/load-messages.js.map +1 -1
  114. package/dist/services/env-variables/index.js +1 -1
  115. package/dist/services/env-variables/index.js.map +1 -1
  116. package/dist/services/file-handling/write-output.bench.d.ts +1 -0
  117. package/dist/services/file-handling/write-output.bench.js +24 -0
  118. package/dist/services/file-handling/write-output.bench.js.map +1 -0
  119. package/dist/services/file-handling/write-output.d.ts +5 -0
  120. package/dist/services/file-handling/write-output.js +54 -9
  121. package/dist/services/file-handling/write-output.js.map +1 -1
  122. package/dist/services/file-handling/write-output.test.js +27 -0
  123. package/dist/services/file-handling/write-output.test.js.map +1 -1
  124. package/dist/services/logger/index.js +2 -0
  125. package/dist/services/logger/index.js.map +1 -1
  126. package/package.json +86 -86
@@ -0,0 +1,283 @@
1
+ import { beforeEach, expect, test, vi } from "vitest";
2
+ import { createProject as typescriptProject, ts } from "@ts-morph/bootstrap";
3
+ import { createBundleNested } from "./test-helpers.js";
4
+ import { createRequire } from "node:module";
5
+ import path from "node:path";
6
+ import { compileProject } from "./compile-project.js";
7
+ import { emitMessageDeclarations } from "./emit-message-dts.js";
8
+ import { emitTsDeclarations } from "./emit-ts-declarations.js";
9
+ import { compileBundle } from "./compile-bundle.js";
10
+ import * as messageModules from "./output-structure/message-modules.js";
11
+ import * as localeModules from "./output-structure/locale-modules.js";
12
+ // Route infrastructure DTS through TypeScript 7 CLI (same as emit-ts-declarations.test.ts).
13
+ vi.mock("typescript", async () => {
14
+ const actual = await vi.importActual("typescript-go");
15
+ return {
16
+ ...actual,
17
+ createProgram: undefined,
18
+ createCompilerHost: undefined,
19
+ createSourceFile: undefined,
20
+ ScriptTarget: undefined,
21
+ ScriptKind: undefined,
22
+ ModuleKind: undefined,
23
+ ModuleResolutionKind: undefined,
24
+ };
25
+ });
26
+ const tscResolution = vi.hoisted(() => ({ tscJsPath: "" }));
27
+ vi.mock("./resolve-tsc-js-path.js", () => ({
28
+ resolveTscJsPath: () => tscResolution.tscJsPath,
29
+ }));
30
+ const typescriptGoTscJsPath = path.join(path.dirname(createRequire(import.meta.url).resolve("typescript-go/package.json")), "lib", "tsc.js");
31
+ beforeEach(() => {
32
+ tscResolution.tscJsPath = typescriptGoTscJsPath;
33
+ });
34
+ function testBundles() {
35
+ const quotedAliasBundle = createBundleNested({
36
+ id: "greeting.hello",
37
+ messages: [
38
+ {
39
+ locale: "en",
40
+ variants: [{ pattern: [{ type: "text", value: "Hello" }] }],
41
+ },
42
+ {
43
+ locale: "de",
44
+ variants: [{ pattern: [{ type: "text", value: "Hallo" }] }],
45
+ },
46
+ ],
47
+ });
48
+ const parameterizedBundle = createBundleNested({
49
+ id: "balance",
50
+ declarations: [{ type: "input-variable", name: "amount" }],
51
+ messages: [
52
+ {
53
+ locale: "en",
54
+ variants: [
55
+ {
56
+ pattern: [
57
+ { type: "text", value: "You have " },
58
+ {
59
+ type: "expression",
60
+ arg: { type: "variable-reference", name: "amount" },
61
+ },
62
+ { type: "text", value: " coins." },
63
+ ],
64
+ },
65
+ ],
66
+ },
67
+ {
68
+ locale: "de",
69
+ variants: [
70
+ {
71
+ pattern: [
72
+ { type: "text", value: "Du hast " },
73
+ {
74
+ type: "expression",
75
+ arg: { type: "variable-reference", name: "amount" },
76
+ },
77
+ { type: "text", value: " Münzen." },
78
+ ],
79
+ },
80
+ ],
81
+ },
82
+ ],
83
+ });
84
+ const plainBundle = createBundleNested({
85
+ id: "plain",
86
+ messages: [
87
+ {
88
+ locale: "en",
89
+ variants: [{ pattern: [{ type: "text", value: "Static" }] }],
90
+ },
91
+ ],
92
+ });
93
+ return {
94
+ bundles: [quotedAliasBundle, parameterizedBundle, plainBundle],
95
+ settings: { locales: ["en", "de"], baseLocale: "en" },
96
+ };
97
+ }
98
+ async function typecheckOutput(output, consumer) {
99
+ const tsProject = await typescriptProject({
100
+ useInMemoryFileSystem: true,
101
+ compilerOptions: {
102
+ module: ts.ModuleKind.Node16,
103
+ moduleResolution: ts.ModuleResolutionKind.Node16,
104
+ strict: true,
105
+ },
106
+ });
107
+ for (const [fileName, code] of Object.entries(output)) {
108
+ if (fileName.endsWith(".d.ts")) {
109
+ tsProject.createSourceFile(fileName, code);
110
+ }
111
+ }
112
+ tsProject.createSourceFile("test.ts", consumer);
113
+ const program = tsProject.createProgram();
114
+ const diagnostics = ts.getPreEmitDiagnostics(program).filter((d) => {
115
+ const text = d.messageText.toString();
116
+ return (!text.includes("Cannot find name 'URLPattern'") &&
117
+ !text.includes("Type 'string' is not assignable to type"));
118
+ });
119
+ return diagnostics;
120
+ }
121
+ for (const outputStructure of ["message-modules", "locale-modules"]) {
122
+ test(`hand-rolled message DTS typechecks (${outputStructure}) via compileProject + TS7 infra`, async () => {
123
+ const { bundles, settings } = testBundles();
124
+ const output = await compileProject({
125
+ bundles,
126
+ settings,
127
+ compilerOptions: {
128
+ emitTsDeclarations: true,
129
+ outputStructure,
130
+ emitGitIgnore: false,
131
+ emitPrettierIgnore: false,
132
+ emitReadme: false,
133
+ },
134
+ });
135
+ expect(output["messages.d.ts"]).toBeDefined();
136
+ expect(output["messages/_index.d.ts"]).toBeDefined();
137
+ expect(output["runtime.d.ts"]).toBeDefined();
138
+ if (outputStructure === "message-modules") {
139
+ expect(output["messages/balance.d.ts"]).toContain("BalanceInputs");
140
+ expect(output["messages/greeting_hello.d.ts"]).toContain(`as "greeting.hello"`);
141
+ }
142
+ else {
143
+ expect(output["messages/en.d.ts"]).toContain("export const balance");
144
+ expect(output["messages/_index.d.ts"]).toContain(`as "greeting.hello"`);
145
+ }
146
+ const diagnostics = await typecheckOutput(output, `
147
+ import { "greeting.hello" as greetingHello, balance, plain } from "./messages.js";
148
+
149
+ greetingHello() satisfies string;
150
+ plain() satisfies string;
151
+ balance({ amount: 5 }) satisfies string;
152
+ // @ts-expect-error amount is required
153
+ balance({});
154
+ `);
155
+ for (const diagnostic of diagnostics) {
156
+ console.error(diagnostic.messageText, diagnostic.file?.fileName);
157
+ }
158
+ expect(diagnostics.length).toEqual(0);
159
+ });
160
+ }
161
+ test("hand-rolled message DTS matches TS7 export surface (message-modules)", async () => {
162
+ const { bundles, settings } = testBundles();
163
+ const fallbackMap = Object.fromEntries(settings.locales.map((l) => [l, l === settings.baseLocale ? undefined : settings.baseLocale]));
164
+ // Fix fallback: base has undefined, others fall back to base
165
+ for (const locale of settings.locales) {
166
+ fallbackMap[locale] =
167
+ locale === settings.baseLocale ? undefined : settings.baseLocale;
168
+ }
169
+ const compiledBundles = bundles.map((bundle) => compileBundle({
170
+ bundle,
171
+ fallbackMap,
172
+ messageReferenceExpression: messageModules.messageReferenceExpression,
173
+ settings,
174
+ }));
175
+ const jsOutput = {
176
+ "messages.js": [
177
+ "export * from './messages/_index.js'",
178
+ "export * as m from './messages/_index.js'",
179
+ ].join("\n"),
180
+ ...messageModules.generateOutput(compiledBundles, settings, fallbackMap, false),
181
+ };
182
+ const handRolled = emitMessageDeclarations({
183
+ compiledBundles,
184
+ settings,
185
+ outputStructure: "message-modules",
186
+ });
187
+ const ts7 = await emitTsDeclarations(jsOutput);
188
+ // Every message .d.ts path hand-rolled must also exist from tsc
189
+ for (const fileName of Object.keys(handRolled)) {
190
+ expect(ts7[fileName], `TS7 missing ${fileName}`).toBeDefined();
191
+ }
192
+ // Key export shapes
193
+ expect(handRolled["messages/balance.d.ts"]).toMatch(/export const balance:/);
194
+ expect(handRolled["messages/balance.d.ts"]).toContain("amount:");
195
+ expect(handRolled["messages/greeting_hello.d.ts"]).toContain(`as "greeting.hello"`);
196
+ // TS7 includes JSDoc noise; hand-rolled is compact — both must typecheck
197
+ // the same consumer when paired with the same runtime.d.ts from a full compile.
198
+ const full = await compileProject({
199
+ bundles,
200
+ settings,
201
+ compilerOptions: {
202
+ emitTsDeclarations: true,
203
+ outputStructure: "message-modules",
204
+ emitGitIgnore: false,
205
+ emitPrettierIgnore: false,
206
+ emitReadme: false,
207
+ },
208
+ });
209
+ // Replace hand-rolled message dts with TS7 versions and typecheck both ways
210
+ const withTs7Messages = { ...full, ...pick(ts7, Object.keys(handRolled)) };
211
+ const withHandRolled = { ...full }; // already hand-rolled
212
+ const consumer = `
213
+ import { "greeting.hello" as greetingHello, balance, plain, m } from "./messages.js";
214
+ greetingHello() satisfies string;
215
+ balance({ amount: 1 }) satisfies string;
216
+ plain() satisfies string;
217
+ m.balance({ amount: 2 }) satisfies string;
218
+ `;
219
+ expect((await typecheckOutput(withHandRolled, consumer)).length).toBe(0);
220
+ expect((await typecheckOutput(withTs7Messages, consumer)).length).toBe(0);
221
+ });
222
+ test("hand-rolled message DTS matches TS7 export surface (locale-modules)", async () => {
223
+ const { bundles, settings } = testBundles();
224
+ const fallbackMap = {};
225
+ for (const locale of settings.locales) {
226
+ fallbackMap[locale] =
227
+ locale === settings.baseLocale ? undefined : settings.baseLocale;
228
+ }
229
+ const compiledBundles = bundles.map((bundle) => compileBundle({
230
+ bundle,
231
+ fallbackMap,
232
+ messageReferenceExpression: localeModules.messageReferenceExpression,
233
+ settings,
234
+ }));
235
+ const jsOutput = {
236
+ "messages.js": [
237
+ "export * from './messages/_index.js'",
238
+ "export * as m from './messages/_index.js'",
239
+ ].join("\n"),
240
+ ...localeModules.generateOutput(compiledBundles, settings, fallbackMap, false),
241
+ };
242
+ const handRolled = emitMessageDeclarations({
243
+ compiledBundles,
244
+ settings,
245
+ outputStructure: "locale-modules",
246
+ });
247
+ const ts7 = await emitTsDeclarations(jsOutput);
248
+ for (const fileName of Object.keys(handRolled)) {
249
+ expect(ts7[fileName], `TS7 missing ${fileName}`).toBeDefined();
250
+ }
251
+ expect(handRolled["messages/en.d.ts"]).toContain("export const balance");
252
+ expect(handRolled["messages/de.d.ts"]).toContain("export const plain");
253
+ expect(handRolled["messages/_index.d.ts"]).toContain("export const balance");
254
+ const full = await compileProject({
255
+ bundles,
256
+ settings,
257
+ compilerOptions: {
258
+ emitTsDeclarations: true,
259
+ outputStructure: "locale-modules",
260
+ emitGitIgnore: false,
261
+ emitPrettierIgnore: false,
262
+ emitReadme: false,
263
+ },
264
+ });
265
+ const consumer = `
266
+ import { "greeting.hello" as greetingHello, balance, plain } from "./messages.js";
267
+ greetingHello() satisfies string;
268
+ balance({ amount: 1 }) satisfies string;
269
+ plain() satisfies string;
270
+ `;
271
+ expect((await typecheckOutput(full, consumer)).length).toBe(0);
272
+ const withTs7Messages = { ...full, ...pick(ts7, Object.keys(handRolled)) };
273
+ expect((await typecheckOutput(withTs7Messages, consumer)).length).toBe(0);
274
+ });
275
+ function pick(obj, keys) {
276
+ const out = {};
277
+ for (const key of keys) {
278
+ if (obj[key] !== undefined)
279
+ out[key] = obj[key];
280
+ }
281
+ return out;
282
+ }
283
+ //# sourceMappingURL=emit-message-dts.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emit-message-dts.test.js","sourceRoot":"","sources":["../../src/compiler/emit-message-dts.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { beforeEach, expect, test, vi } from \"vitest\";\nimport { createProject as typescriptProject, ts } from \"@ts-morph/bootstrap\";\nimport type { BundleNested } from \"../messages/ast.js\";\nimport { createBundleNested } from \"./test-helpers.js\";\nimport { createRequire } from \"node:module\";\nimport path from \"node:path\";\nimport { compileProject } from \"./compile-project.js\";\nimport { emitMessageDeclarations } from \"./emit-message-dts.js\";\nimport { emitTsDeclarations } from \"./emit-ts-declarations.js\";\nimport { compileBundle } from \"./compile-bundle.js\";\nimport * as messageModules from \"./output-structure/message-modules.js\";\nimport * as localeModules from \"./output-structure/locale-modules.js\";\n\n// Route infrastructure DTS through TypeScript 7 CLI (same as emit-ts-declarations.test.ts).\nvi.mock(\"typescript\", async () => {\n\tconst actual =\n\t\tawait vi.importActual<typeof import(\"typescript-go\")>(\"typescript-go\");\n\treturn {\n\t\t...actual,\n\t\tcreateProgram: undefined,\n\t\tcreateCompilerHost: undefined,\n\t\tcreateSourceFile: undefined,\n\t\tScriptTarget: undefined,\n\t\tScriptKind: undefined,\n\t\tModuleKind: undefined,\n\t\tModuleResolutionKind: undefined,\n\t};\n});\n\nconst tscResolution = vi.hoisted(() => ({ tscJsPath: \"\" }));\n\nvi.mock(\"./resolve-tsc-js-path.js\", () => ({\n\tresolveTscJsPath: () => tscResolution.tscJsPath,\n}));\n\nconst typescriptGoTscJsPath = path.join(\n\tpath.dirname(\n\t\tcreateRequire(import.meta.url).resolve(\"typescript-go/package.json\")\n\t),\n\t\"lib\",\n\t\"tsc.js\"\n);\n\nbeforeEach(() => {\n\ttscResolution.tscJsPath = typescriptGoTscJsPath;\n});\n\nfunction testBundles(): {\n\tbundles: BundleNested[];\n\tsettings: { locales: string[]; baseLocale: string };\n} {\n\tconst quotedAliasBundle: BundleNested = createBundleNested({\n\t\tid: \"greeting.hello\",\n\t\tmessages: [\n\t\t\t{\n\t\t\t\tlocale: \"en\",\n\t\t\t\tvariants: [{ pattern: [{ type: \"text\", value: \"Hello\" }] }],\n\t\t\t},\n\t\t\t{\n\t\t\t\tlocale: \"de\",\n\t\t\t\tvariants: [{ pattern: [{ type: \"text\", value: \"Hallo\" }] }],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst parameterizedBundle: BundleNested = createBundleNested({\n\t\tid: \"balance\",\n\t\tdeclarations: [{ type: \"input-variable\", name: \"amount\" }],\n\t\tmessages: [\n\t\t\t{\n\t\t\t\tlocale: \"en\",\n\t\t\t\tvariants: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpattern: [\n\t\t\t\t\t\t\t{ type: \"text\", value: \"You have \" },\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: \"expression\",\n\t\t\t\t\t\t\t\targ: { type: \"variable-reference\", name: \"amount\" },\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{ type: \"text\", value: \" coins.\" },\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t\t{\n\t\t\t\tlocale: \"de\",\n\t\t\t\tvariants: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpattern: [\n\t\t\t\t\t\t\t{ type: \"text\", value: \"Du hast \" },\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: \"expression\",\n\t\t\t\t\t\t\t\targ: { type: \"variable-reference\", name: \"amount\" },\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{ type: \"text\", value: \" Münzen.\" },\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst plainBundle: BundleNested = createBundleNested({\n\t\tid: \"plain\",\n\t\tmessages: [\n\t\t\t{\n\t\t\t\tlocale: \"en\",\n\t\t\t\tvariants: [{ pattern: [{ type: \"text\", value: \"Static\" }] }],\n\t\t\t},\n\t\t],\n\t});\n\n\treturn {\n\t\tbundles: [quotedAliasBundle, parameterizedBundle, plainBundle],\n\t\tsettings: { locales: [\"en\", \"de\"], baseLocale: \"en\" },\n\t};\n}\n\nasync function typecheckOutput(output: Record<string, string>, consumer: string) {\n\tconst tsProject = await typescriptProject({\n\t\tuseInMemoryFileSystem: true,\n\t\tcompilerOptions: {\n\t\t\tmodule: ts.ModuleKind.Node16,\n\t\t\tmoduleResolution: ts.ModuleResolutionKind.Node16,\n\t\t\tstrict: true,\n\t\t},\n\t});\n\n\tfor (const [fileName, code] of Object.entries(output)) {\n\t\tif (fileName.endsWith(\".d.ts\")) {\n\t\t\ttsProject.createSourceFile(fileName, code);\n\t\t}\n\t}\n\n\ttsProject.createSourceFile(\"test.ts\", consumer);\n\n\tconst program = tsProject.createProgram();\n\tconst diagnostics = ts.getPreEmitDiagnostics(program).filter((d) => {\n\t\tconst text = d.messageText.toString();\n\t\treturn (\n\t\t\t!text.includes(\"Cannot find name 'URLPattern'\") &&\n\t\t\t!text.includes(\"Type 'string' is not assignable to type\")\n\t\t);\n\t});\n\treturn diagnostics;\n}\n\nfor (const outputStructure of [\"message-modules\", \"locale-modules\"] as const) {\n\ttest(`hand-rolled message DTS typechecks (${outputStructure}) via compileProject + TS7 infra`, async () => {\n\t\tconst { bundles, settings } = testBundles();\n\t\tconst output = await compileProject({\n\t\t\tbundles,\n\t\t\tsettings,\n\t\t\tcompilerOptions: {\n\t\t\t\temitTsDeclarations: true,\n\t\t\t\toutputStructure,\n\t\t\t\temitGitIgnore: false,\n\t\t\t\temitPrettierIgnore: false,\n\t\t\t\temitReadme: false,\n\t\t\t},\n\t\t});\n\n\t\texpect(output[\"messages.d.ts\"]).toBeDefined();\n\t\texpect(output[\"messages/_index.d.ts\"]).toBeDefined();\n\t\texpect(output[\"runtime.d.ts\"]).toBeDefined();\n\n\t\tif (outputStructure === \"message-modules\") {\n\t\t\texpect(output[\"messages/balance.d.ts\"]).toContain(\"BalanceInputs\");\n\t\t\texpect(output[\"messages/greeting_hello.d.ts\"]).toContain(\n\t\t\t\t`as \"greeting.hello\"`\n\t\t\t);\n\t\t} else {\n\t\t\texpect(output[\"messages/en.d.ts\"]).toContain(\"export const balance\");\n\t\t\texpect(output[\"messages/_index.d.ts\"]).toContain(\n\t\t\t\t`as \"greeting.hello\"`\n\t\t\t);\n\t\t}\n\n\t\tconst diagnostics = await typecheckOutput(\n\t\t\toutput,\n\t\t\t`\n\t\t\t\timport { \"greeting.hello\" as greetingHello, balance, plain } from \"./messages.js\";\n\n\t\t\t\tgreetingHello() satisfies string;\n\t\t\t\tplain() satisfies string;\n\t\t\t\tbalance({ amount: 5 }) satisfies string;\n\t\t\t\t// @ts-expect-error amount is required\n\t\t\t\tbalance({});\n\t\t\t`\n\t\t);\n\n\t\tfor (const diagnostic of diagnostics) {\n\t\t\tconsole.error(diagnostic.messageText, diagnostic.file?.fileName);\n\t\t}\n\t\texpect(diagnostics.length).toEqual(0);\n\t});\n}\n\ntest(\"hand-rolled message DTS matches TS7 export surface (message-modules)\", async () => {\n\tconst { bundles, settings } = testBundles();\n\tconst fallbackMap = Object.fromEntries(\n\t\tsettings.locales.map((l) => [l, l === settings.baseLocale ? undefined : settings.baseLocale])\n\t) as Record<string, string | undefined>;\n\t// Fix fallback: base has undefined, others fall back to base\n\tfor (const locale of settings.locales) {\n\t\tfallbackMap[locale] =\n\t\t\tlocale === settings.baseLocale ? undefined : settings.baseLocale;\n\t}\n\n\tconst compiledBundles = bundles.map((bundle) =>\n\t\tcompileBundle({\n\t\t\tbundle,\n\t\t\tfallbackMap,\n\t\t\tmessageReferenceExpression: messageModules.messageReferenceExpression,\n\t\t\tsettings,\n\t\t})\n\t);\n\n\tconst jsOutput = {\n\t\t\"messages.js\": [\n\t\t\t\"export * from './messages/_index.js'\",\n\t\t\t\"export * as m from './messages/_index.js'\",\n\t\t].join(\"\\n\"),\n\t\t...messageModules.generateOutput(\n\t\t\tcompiledBundles,\n\t\t\tsettings,\n\t\t\tfallbackMap,\n\t\t\tfalse\n\t\t),\n\t};\n\n\tconst handRolled = emitMessageDeclarations({\n\t\tcompiledBundles,\n\t\tsettings,\n\t\toutputStructure: \"message-modules\",\n\t});\n\n\tconst ts7 = await emitTsDeclarations(jsOutput);\n\n\t// Every message .d.ts path hand-rolled must also exist from tsc\n\tfor (const fileName of Object.keys(handRolled)) {\n\t\texpect(ts7[fileName], `TS7 missing ${fileName}`).toBeDefined();\n\t}\n\n\t// Key export shapes\n\texpect(handRolled[\"messages/balance.d.ts\"]).toMatch(/export const balance:/);\n\texpect(handRolled[\"messages/balance.d.ts\"]).toContain(\"amount:\");\n\texpect(handRolled[\"messages/greeting_hello.d.ts\"]).toContain(\n\t\t`as \"greeting.hello\"`\n\t);\n\n\t// TS7 includes JSDoc noise; hand-rolled is compact — both must typecheck\n\t// the same consumer when paired with the same runtime.d.ts from a full compile.\n\tconst full = await compileProject({\n\t\tbundles,\n\t\tsettings,\n\t\tcompilerOptions: {\n\t\t\temitTsDeclarations: true,\n\t\t\toutputStructure: \"message-modules\",\n\t\t\temitGitIgnore: false,\n\t\t\temitPrettierIgnore: false,\n\t\t\temitReadme: false,\n\t\t},\n\t});\n\n\t// Replace hand-rolled message dts with TS7 versions and typecheck both ways\n\tconst withTs7Messages = { ...full, ...pick(ts7, Object.keys(handRolled)) };\n\tconst withHandRolled = { ...full }; // already hand-rolled\n\n\tconst consumer = `\n\t\timport { \"greeting.hello\" as greetingHello, balance, plain, m } from \"./messages.js\";\n\t\tgreetingHello() satisfies string;\n\t\tbalance({ amount: 1 }) satisfies string;\n\t\tplain() satisfies string;\n\t\tm.balance({ amount: 2 }) satisfies string;\n\t`;\n\n\texpect((await typecheckOutput(withHandRolled, consumer)).length).toBe(0);\n\texpect((await typecheckOutput(withTs7Messages, consumer)).length).toBe(0);\n});\n\ntest(\"hand-rolled message DTS matches TS7 export surface (locale-modules)\", async () => {\n\tconst { bundles, settings } = testBundles();\n\tconst fallbackMap: Record<string, string | undefined> = {};\n\tfor (const locale of settings.locales) {\n\t\tfallbackMap[locale] =\n\t\t\tlocale === settings.baseLocale ? undefined : settings.baseLocale;\n\t}\n\n\tconst compiledBundles = bundles.map((bundle) =>\n\t\tcompileBundle({\n\t\t\tbundle,\n\t\t\tfallbackMap,\n\t\t\tmessageReferenceExpression: localeModules.messageReferenceExpression,\n\t\t\tsettings,\n\t\t})\n\t);\n\n\tconst jsOutput = {\n\t\t\"messages.js\": [\n\t\t\t\"export * from './messages/_index.js'\",\n\t\t\t\"export * as m from './messages/_index.js'\",\n\t\t].join(\"\\n\"),\n\t\t...localeModules.generateOutput(\n\t\t\tcompiledBundles,\n\t\t\tsettings,\n\t\t\tfallbackMap,\n\t\t\tfalse\n\t\t),\n\t};\n\n\tconst handRolled = emitMessageDeclarations({\n\t\tcompiledBundles,\n\t\tsettings,\n\t\toutputStructure: \"locale-modules\",\n\t});\n\tconst ts7 = await emitTsDeclarations(jsOutput);\n\n\tfor (const fileName of Object.keys(handRolled)) {\n\t\texpect(ts7[fileName], `TS7 missing ${fileName}`).toBeDefined();\n\t}\n\n\texpect(handRolled[\"messages/en.d.ts\"]).toContain(\"export const balance\");\n\texpect(handRolled[\"messages/de.d.ts\"]).toContain(\"export const plain\");\n\texpect(handRolled[\"messages/_index.d.ts\"]).toContain(\"export const balance\");\n\n\tconst full = await compileProject({\n\t\tbundles,\n\t\tsettings,\n\t\tcompilerOptions: {\n\t\t\temitTsDeclarations: true,\n\t\t\toutputStructure: \"locale-modules\",\n\t\t\temitGitIgnore: false,\n\t\t\temitPrettierIgnore: false,\n\t\t\temitReadme: false,\n\t\t},\n\t});\n\n\tconst consumer = `\n\t\timport { \"greeting.hello\" as greetingHello, balance, plain } from \"./messages.js\";\n\t\tgreetingHello() satisfies string;\n\t\tbalance({ amount: 1 }) satisfies string;\n\t\tplain() satisfies string;\n\t`;\n\n\texpect((await typecheckOutput(full, consumer)).length).toBe(0);\n\n\tconst withTs7Messages = { ...full, ...pick(ts7, Object.keys(handRolled)) };\n\texpect((await typecheckOutput(withTs7Messages, consumer)).length).toBe(0);\n});\n\nfunction pick<T extends Record<string, string>>(\n\tobj: T,\n\tkeys: string[]\n): Record<string, string> {\n\tconst out: Record<string, string> = {};\n\tfor (const key of keys) {\n\t\tif (obj[key] !== undefined) out[key] = obj[key]!;\n\t}\n\treturn out;\n}\n"]}
@@ -25,6 +25,7 @@ export declare function createRuntimeFile(args: {
25
25
  cookieDomain: CompilerOptions["cookieDomain"];
26
26
  urlPatterns?: CompilerOptions["urlPatterns"];
27
27
  routing?: CompilerOptions["routing"];
28
+ trailingSlash?: CompilerOptions["trailingSlash"];
28
29
  experimentalMiddlewareLocaleSplitting: CompilerOptions["experimentalMiddlewareLocaleSplitting"];
29
30
  isServer: CompilerOptions["isServer"];
30
31
  experimentalStaticLocale?: CompilerOptions["experimentalStaticLocale"];
@@ -102,6 +102,9 @@ ${injectCode("./variables.js")
102
102
  .replace(`export const TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED = false;`, `const TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED = ${allUsedStrategies.has("preferredLanguage")};`)
103
103
  .replace(`export const urlPatterns = [];`, `export const urlPatterns = ${JSON.stringify(urlPatterns, null, 2)};`)
104
104
  .replace(`export const routing = {};`, `export const routing = ${JSON.stringify(hostRouting ?? {}, null, 2)};`)
105
+ .replace(`export const trailingSlash = undefined;`, `export const trailingSlash = ${args.compilerOptions.trailingSlash === undefined
106
+ ? "undefined"
107
+ : JSON.stringify(args.compilerOptions.trailingSlash)};`)
105
108
  .replace(`export const disableAsyncLocalStorage = false;`, `export const disableAsyncLocalStorage = ${args.compilerOptions.disableAsyncLocalStorage};`)
106
109
  .replace(`export const TREE_SHAKE_DEFAULT_URL_PATTERN_USED = false;`, `const TREE_SHAKE_DEFAULT_URL_PATTERN_USED = ${defaultUrlPatternUsed};`)
107
110
  .replace(`export const TREE_SHAKE_HOST_ROUTING_USED = false;`, `const TREE_SHAKE_HOST_ROUTING_USED = ${hostRoutingUsed};`)
@@ -131,6 +134,14 @@ ${injectCode("./get-url-origin.js")}
131
134
 
132
135
  ${injectCode("./check-locale.js")}
133
136
 
137
+ ${injectCode("./normalize-trailing-slash.js", {
138
+ privateExports: ["normalizeTrailingSlash"],
139
+ })}
140
+
141
+ ${injectCode("./exec-url-pattern.js", {
142
+ privateExports: ["execUrlPattern"],
143
+ })}
144
+
134
145
  ${injectCode("./extract-locale-from-request.js")}
135
146
 
136
147
  ${injectCode("./extract-locale-from-request-async.js")}
@@ -1 +1 @@
1
- {"version":3,"file":"create-runtime.js","sourceRoot":"","sources":["../../../src/compiler/runtime/create-runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from \"node:fs\";\nimport type {\n\tCompilerOptions,\n\tRoutingConfig,\n} from \"../compiler-options.js\";\nimport { perLocaleBuildStaticLocaleExpression } from \"../per-locale-build.js\";\n\ntype ResolvedHostRouting = Record<\n\tstring,\n\t{ baseLocale: string; locales: string[] }\n>;\n\n/**\n * Normalize and validate host routing config for the generated runtime.\n */\nexport function normalizeRouting(args: {\n\trouting: RoutingConfig | undefined;\n\tlocales: string[];\n\tbaseLocale: string;\n}): ResolvedHostRouting | undefined {\n\tif (args.routing === undefined) {\n\t\treturn undefined;\n\t}\n\n\tconst resolved: ResolvedHostRouting = {};\n\n\tfor (const [hostname, rule] of Object.entries(args.routing)) {\n\t\tconst hostBaseLocale = rule.baseLocale ?? args.baseLocale;\n\t\tconst hostLocales = rule.locales ?? [...args.locales];\n\n\t\tif (!args.locales.includes(hostBaseLocale)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid baseLocale \"${hostBaseLocale}\" in routing[\"${hostname}\"]. It must be one of the locales defined in the \"locales\" array.`\n\t\t\t);\n\t\t}\n\n\t\tfor (const locale of hostLocales) {\n\t\t\tif (!args.locales.includes(locale)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid locale \"${locale}\" in routing[\"${hostname}\"].locales. It must be one of the locales defined in the \"locales\" array.`\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (!hostLocales.includes(hostBaseLocale)) {\n\t\t\tthrow new Error(\n\t\t\t\t`routing[\"${hostname}\"].baseLocale \"${hostBaseLocale}\" must be included in routing[\"${hostname}\"].locales.`\n\t\t\t);\n\t\t}\n\n\t\tresolved[hostname] = {\n\t\t\tbaseLocale: hostBaseLocale,\n\t\t\tlocales: hostLocales,\n\t\t};\n\t}\n\n\tif (resolved[\"*\"] === undefined) {\n\t\tresolved[\"*\"] = {\n\t\t\tbaseLocale: args.baseLocale,\n\t\t\tlocales: [...args.locales],\n\t\t};\n\t}\n\n\treturn resolved;\n}\n\n/**\n * Returns the code for the `runtime.js` module\n */\nexport function createRuntimeFile(args: {\n\tbaseLocale: string;\n\tlocales: string[];\n\tcompilerOptions: {\n\t\tstrategy: NonNullable<CompilerOptions[\"strategy\"]>;\n\t\trouteStrategies?: CompilerOptions[\"routeStrategies\"];\n\t\tcookieName: NonNullable<CompilerOptions[\"cookieName\"]>;\n\t\tcookieMaxAge: NonNullable<CompilerOptions[\"cookieMaxAge\"]>;\n\t\tcookieDomain: CompilerOptions[\"cookieDomain\"];\n\t\turlPatterns?: CompilerOptions[\"urlPatterns\"];\n\t\trouting?: CompilerOptions[\"routing\"];\n\t\texperimentalMiddlewareLocaleSplitting: CompilerOptions[\"experimentalMiddlewareLocaleSplitting\"];\n\t\tisServer: CompilerOptions[\"isServer\"];\n\t\texperimentalStaticLocale?: CompilerOptions[\"experimentalStaticLocale\"];\n\t\tlocalStorageKey: CompilerOptions[\"localStorageKey\"];\n\t\tdisableAsyncLocalStorage: NonNullable<\n\t\t\tCompilerOptions[\"disableAsyncLocalStorage\"]\n\t\t>;\n\t};\n}): string {\n\tif (\n\t\targs.compilerOptions.routing !== undefined &&\n\t\targs.compilerOptions.urlPatterns !== undefined\n\t) {\n\t\tthrow new Error(\n\t\t\t`Cannot set both \"routing\" and \"urlPatterns\". Use \"routing\" for per-host locale prefixes, or \"urlPatterns\" for custom URLPattern rules.`\n\t\t);\n\t}\n\n\tconst hostRouting = normalizeRouting({\n\t\trouting: args.compilerOptions.routing,\n\t\tlocales: args.locales,\n\t\tbaseLocale: args.baseLocale,\n\t});\n\tconst hostRoutingUsed = hostRouting !== undefined;\n\n\tconst urlPatterns = args.compilerOptions.urlPatterns ?? [];\n\tconst routeStrategies = args.compilerOptions.routeStrategies ?? [];\n\n\tlet defaultUrlPatternUsed = false;\n\n\t// Default string-prefix router when neither routing nor custom urlPatterns are set.\n\tif (\n\t\t!hostRoutingUsed &&\n\t\targs.compilerOptions.urlPatterns === undefined\n\t) {\n\t\tdefaultUrlPatternUsed = true;\n\t\turlPatterns.push({\n\t\t\tpattern: `:protocol://:domain(.*)::port?/:path(.*)?`,\n\t\t\tlocalized: [],\n\t\t});\n\t\tfor (const locale of args.locales) {\n\t\t\tif (locale === args.baseLocale) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\turlPatterns[0]?.localized.push([\n\t\t\t\tlocale,\n\t\t\t\t`:protocol://:domain(.*)::port?/${locale}/:path(.*)?`,\n\t\t\t]);\n\t\t}\n\t\turlPatterns[0]?.localized.push([\n\t\t\targs.baseLocale,\n\t\t\t`:protocol://:domain(.*)::port?/:path(.*)?`,\n\t\t]);\n\t}\n\tconst strategiesUsedByRoutes = routeStrategies.flatMap((routeStrategy) =>\n\t\t\"strategy\" in routeStrategy ? routeStrategy.strategy : []\n\t);\n\tconst allUsedStrategies = new Set([\n\t\t...args.compilerOptions.strategy,\n\t\t...strategiesUsedByRoutes,\n\t]);\n\t// verify that urlPatterns' locales are valid\n\tfor (const urlPattern of urlPatterns) {\n\t\tfor (const [locale] of urlPattern.localized) {\n\t\t\tif (!args.locales.includes(locale)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid locale \"${locale}\" in urlPatterns. It must be one of the locales defined in the \"locales\" array.`\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tconst code = `\n${injectCode(\"./variables.js\")\n\t.replace(\n\t\t`export const baseLocale = \"en\";`,\n\t\t`export const baseLocale = \"${args.baseLocale}\";`\n\t)\n\t.replace(\n\t\t`export const locales = /** @type {readonly string[]} */ ([\"en\", \"de\"]);`,\n\t\t`export const locales = /** @type {const} */ (${JSON.stringify(args.locales)});`\n\t)\n\t.replace(\n\t\t`export const strategy = [\"globalVariable\"];`,\n\t\t`export const strategy = ${JSON.stringify(args.compilerOptions.strategy, null, 2)};`\n\t)\n\t.replace(`<cookie-name>`, `${args.compilerOptions.cookieName}`)\n\t.replace(`60 * 60 * 24 * 400`, `${args.compilerOptions.cookieMaxAge}`)\n\t.replace(`<cookie-domain>`, `${args.compilerOptions.cookieDomain}`)\n\t.replace(\n\t\t`export const routeStrategies = [];`,\n\t\t`export const routeStrategies = ${JSON.stringify(routeStrategies, null, 2)};`\n\t)\n\t.replace(\n\t\t`export const TREE_SHAKE_COOKIE_STRATEGY_USED = false;`,\n\t\t`const TREE_SHAKE_COOKIE_STRATEGY_USED = ${allUsedStrategies.has(\"cookie\")};`\n\t)\n\t.replace(\n\t\t`export const TREE_SHAKE_URL_STRATEGY_USED = false;`,\n\t\t`const TREE_SHAKE_URL_STRATEGY_USED = ${allUsedStrategies.has(\"url\")};`\n\t)\n\t.replace(\n\t\t`export const TREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED = false;`,\n\t\t`const TREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED = ${allUsedStrategies.has(\"globalVariable\")};`\n\t)\n\t.replace(\n\t\t`export const TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED = false;`,\n\t\t`const TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED = ${allUsedStrategies.has(\"preferredLanguage\")};`\n\t)\n\t.replace(\n\t\t`export const urlPatterns = [];`,\n\t\t`export const urlPatterns = ${JSON.stringify(urlPatterns, null, 2)};`\n\t)\n\t.replace(\n\t\t`export const routing = {};`,\n\t\t`export const routing = ${JSON.stringify(hostRouting ?? {}, null, 2)};`\n\t)\n\t.replace(\n\t\t`export const disableAsyncLocalStorage = false;`,\n\t\t`export const disableAsyncLocalStorage = ${args.compilerOptions.disableAsyncLocalStorage};`\n\t)\n\t.replace(\n\t\t`export const TREE_SHAKE_DEFAULT_URL_PATTERN_USED = false;`,\n\t\t`const TREE_SHAKE_DEFAULT_URL_PATTERN_USED = ${defaultUrlPatternUsed};`\n\t)\n\t.replace(\n\t\t`export const TREE_SHAKE_HOST_ROUTING_USED = false;`,\n\t\t`const TREE_SHAKE_HOST_ROUTING_USED = ${hostRoutingUsed};`\n\t)\n\t.replace(\n\t\t`export const experimentalMiddlewareLocaleSplitting = false;`,\n\t\t`export const experimentalMiddlewareLocaleSplitting = ${args.compilerOptions.experimentalMiddlewareLocaleSplitting};`\n\t)\n\t.replace(\n\t\t`export const isServer = typeof window === \"undefined\";`,\n\t\t`export const isServer = ${args.compilerOptions.isServer};`\n\t)\n\t.replace(\n\t\t`export const experimentalStaticLocale = undefined;`,\n\t\targs.compilerOptions.experimentalStaticLocale\n\t\t\t? `export const experimentalStaticLocale = ${\n\t\t\t\t\targs.compilerOptions.experimentalStaticLocale ===\n\t\t\t\t\tperLocaleBuildStaticLocaleExpression\n\t\t\t\t\t\t? args.compilerOptions.experimentalStaticLocale\n\t\t\t\t\t\t: `assertIsLocale(${args.compilerOptions.experimentalStaticLocale})`\n\t\t\t\t};`\n\t\t\t: `export const experimentalStaticLocale = undefined;`\n\t)\n\t.replace(\n\t\t`export const localStorageKey = \"PARAGLIDE_LOCALE\";`,\n\t\t`export const localStorageKey = \"${args.compilerOptions.localStorageKey}\";`\n\t)\n\t.replace(\n\t\t`export const TREE_SHAKE_LOCAL_STORAGE_STRATEGY_USED = false;`,\n\t\t`const TREE_SHAKE_LOCAL_STORAGE_STRATEGY_USED = ${allUsedStrategies.has(\"localStorage\")};`\n\t)}\n\n/** @type {any} */ (globalThis).__paraglide =\n\t/** @type {any} */ (globalThis).__paraglide ?? {};\n/** @type {any} */ (globalThis).__paraglide.ssr =\n\t/** @type {any} */ (globalThis).__paraglide.ssr ?? {};\n\n${injectCode(\"./get-locale.js\")}\n\n${injectCode(\"./get-text-direction.js\")}\n\n${injectCode(\"./set-locale.js\")}\n\n${injectCode(\"./get-url-origin.js\")}\n\n${injectCode(\"./check-locale.js\")}\n\n${injectCode(\"./extract-locale-from-request.js\")}\n\n${injectCode(\"./extract-locale-from-request-async.js\")}\n\n${injectCode(\"./extract-locale-from-cookie.js\", {\n\tprivateExports: [\"clearLocaleCookieCache\"],\n})}\n\n${injectCode(\"./extract-locale-from-header.js\")}\n\n${injectCode(\"./extract-locale-from-navigator.js\")}\n\n${injectCode(\"./url-pattern.js\")}\n\n${injectCode(\"./host-routing.js\")}\n\n${injectCode(\"./extract-locale-from-url.js\")}\n\n${injectCode(\"./localize-url.js\")}\n\n${injectCode(\"./route-strategy.js\")}\n\n${injectCode(\"./should-redirect.js\")}\n\n${injectCode(\"./localize-href.js\")}\n\n${injectCode(\"./track-message-call.js\")}\n\n${injectCode(\"./generate-static-localized-urls.js\")}\n\n${injectCode(\"./strategy.js\")}\n\n${injectCode(\"./type-definitions.js\").replace(\n\t`@typedef {string} Locale`,\n\t`@typedef {typeof locales[number]} Locale`\n)}\n`;\n\n\treturn code;\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 * @param {{ privateExports?: string[] }} [options]\n * @returns {string}\n */\nfunction injectCode(\n\tpath: string,\n\toptions?: { privateExports?: string[] }\n): 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\tlet injected = code\n\t\t.replace(importRegex, \"\")\n\t\t.replace(sourceMapRegex, \"\")\n\t\t.replace(blockSourceMapRegex, \"\")\n\t\t.trim();\n\tfor (const exportName of options?.privateExports ?? []) {\n\t\tinjected = injected.replace(\n\t\t\tnew RegExp(`\\\\bexport\\\\s+function\\\\s+${exportName}\\\\b`, \"g\"),\n\t\t\t`function ${exportName}`\n\t\t);\n\t}\n\treturn injected;\n}\n"]}
1
+ {"version":3,"file":"create-runtime.js","sourceRoot":"","sources":["../../../src/compiler/runtime/create-runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from \"node:fs\";\nimport type {\n\tCompilerOptions,\n\tRoutingConfig,\n} from \"../compiler-options.js\";\nimport { perLocaleBuildStaticLocaleExpression } from \"../per-locale-build.js\";\n\ntype ResolvedHostRouting = Record<\n\tstring,\n\t{ baseLocale: string; locales: string[] }\n>;\n\n/**\n * Normalize and validate host routing config for the generated runtime.\n */\nexport function normalizeRouting(args: {\n\trouting: RoutingConfig | undefined;\n\tlocales: string[];\n\tbaseLocale: string;\n}): ResolvedHostRouting | undefined {\n\tif (args.routing === undefined) {\n\t\treturn undefined;\n\t}\n\n\tconst resolved: ResolvedHostRouting = {};\n\n\tfor (const [hostname, rule] of Object.entries(args.routing)) {\n\t\tconst hostBaseLocale = rule.baseLocale ?? args.baseLocale;\n\t\tconst hostLocales = rule.locales ?? [...args.locales];\n\n\t\tif (!args.locales.includes(hostBaseLocale)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid baseLocale \"${hostBaseLocale}\" in routing[\"${hostname}\"]. It must be one of the locales defined in the \"locales\" array.`\n\t\t\t);\n\t\t}\n\n\t\tfor (const locale of hostLocales) {\n\t\t\tif (!args.locales.includes(locale)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid locale \"${locale}\" in routing[\"${hostname}\"].locales. It must be one of the locales defined in the \"locales\" array.`\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (!hostLocales.includes(hostBaseLocale)) {\n\t\t\tthrow new Error(\n\t\t\t\t`routing[\"${hostname}\"].baseLocale \"${hostBaseLocale}\" must be included in routing[\"${hostname}\"].locales.`\n\t\t\t);\n\t\t}\n\n\t\tresolved[hostname] = {\n\t\t\tbaseLocale: hostBaseLocale,\n\t\t\tlocales: hostLocales,\n\t\t};\n\t}\n\n\tif (resolved[\"*\"] === undefined) {\n\t\tresolved[\"*\"] = {\n\t\t\tbaseLocale: args.baseLocale,\n\t\t\tlocales: [...args.locales],\n\t\t};\n\t}\n\n\treturn resolved;\n}\n\n/**\n * Returns the code for the `runtime.js` module\n */\nexport function createRuntimeFile(args: {\n\tbaseLocale: string;\n\tlocales: string[];\n\tcompilerOptions: {\n\t\tstrategy: NonNullable<CompilerOptions[\"strategy\"]>;\n\t\trouteStrategies?: CompilerOptions[\"routeStrategies\"];\n\t\tcookieName: NonNullable<CompilerOptions[\"cookieName\"]>;\n\t\tcookieMaxAge: NonNullable<CompilerOptions[\"cookieMaxAge\"]>;\n\t\tcookieDomain: CompilerOptions[\"cookieDomain\"];\n\t\turlPatterns?: CompilerOptions[\"urlPatterns\"];\n\t\trouting?: CompilerOptions[\"routing\"];\n\t\ttrailingSlash?: CompilerOptions[\"trailingSlash\"];\n\t\texperimentalMiddlewareLocaleSplitting: CompilerOptions[\"experimentalMiddlewareLocaleSplitting\"];\n\t\tisServer: CompilerOptions[\"isServer\"];\n\t\texperimentalStaticLocale?: CompilerOptions[\"experimentalStaticLocale\"];\n\t\tlocalStorageKey: CompilerOptions[\"localStorageKey\"];\n\t\tdisableAsyncLocalStorage: NonNullable<\n\t\t\tCompilerOptions[\"disableAsyncLocalStorage\"]\n\t\t>;\n\t};\n}): string {\n\tif (\n\t\targs.compilerOptions.routing !== undefined &&\n\t\targs.compilerOptions.urlPatterns !== undefined\n\t) {\n\t\tthrow new Error(\n\t\t\t`Cannot set both \"routing\" and \"urlPatterns\". Use \"routing\" for per-host locale prefixes, or \"urlPatterns\" for custom URLPattern rules.`\n\t\t);\n\t}\n\n\tconst hostRouting = normalizeRouting({\n\t\trouting: args.compilerOptions.routing,\n\t\tlocales: args.locales,\n\t\tbaseLocale: args.baseLocale,\n\t});\n\tconst hostRoutingUsed = hostRouting !== undefined;\n\n\tconst urlPatterns = args.compilerOptions.urlPatterns ?? [];\n\tconst routeStrategies = args.compilerOptions.routeStrategies ?? [];\n\n\tlet defaultUrlPatternUsed = false;\n\n\t// Default string-prefix router when neither routing nor custom urlPatterns are set.\n\tif (\n\t\t!hostRoutingUsed &&\n\t\targs.compilerOptions.urlPatterns === undefined\n\t) {\n\t\tdefaultUrlPatternUsed = true;\n\t\turlPatterns.push({\n\t\t\tpattern: `:protocol://:domain(.*)::port?/:path(.*)?`,\n\t\t\tlocalized: [],\n\t\t});\n\t\tfor (const locale of args.locales) {\n\t\t\tif (locale === args.baseLocale) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\turlPatterns[0]?.localized.push([\n\t\t\t\tlocale,\n\t\t\t\t`:protocol://:domain(.*)::port?/${locale}/:path(.*)?`,\n\t\t\t]);\n\t\t}\n\t\turlPatterns[0]?.localized.push([\n\t\t\targs.baseLocale,\n\t\t\t`:protocol://:domain(.*)::port?/:path(.*)?`,\n\t\t]);\n\t}\n\tconst strategiesUsedByRoutes = routeStrategies.flatMap((routeStrategy) =>\n\t\t\"strategy\" in routeStrategy ? routeStrategy.strategy : []\n\t);\n\tconst allUsedStrategies = new Set([\n\t\t...args.compilerOptions.strategy,\n\t\t...strategiesUsedByRoutes,\n\t]);\n\t// verify that urlPatterns' locales are valid\n\tfor (const urlPattern of urlPatterns) {\n\t\tfor (const [locale] of urlPattern.localized) {\n\t\t\tif (!args.locales.includes(locale)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid locale \"${locale}\" in urlPatterns. It must be one of the locales defined in the \"locales\" array.`\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tconst code = `\n${injectCode(\"./variables.js\")\n\t.replace(\n\t\t`export const baseLocale = \"en\";`,\n\t\t`export const baseLocale = \"${args.baseLocale}\";`\n\t)\n\t.replace(\n\t\t`export const locales = /** @type {readonly string[]} */ ([\"en\", \"de\"]);`,\n\t\t`export const locales = /** @type {const} */ (${JSON.stringify(args.locales)});`\n\t)\n\t.replace(\n\t\t`export const strategy = [\"globalVariable\"];`,\n\t\t`export const strategy = ${JSON.stringify(args.compilerOptions.strategy, null, 2)};`\n\t)\n\t.replace(`<cookie-name>`, `${args.compilerOptions.cookieName}`)\n\t.replace(`60 * 60 * 24 * 400`, `${args.compilerOptions.cookieMaxAge}`)\n\t.replace(`<cookie-domain>`, `${args.compilerOptions.cookieDomain}`)\n\t.replace(\n\t\t`export const routeStrategies = [];`,\n\t\t`export const routeStrategies = ${JSON.stringify(routeStrategies, null, 2)};`\n\t)\n\t.replace(\n\t\t`export const TREE_SHAKE_COOKIE_STRATEGY_USED = false;`,\n\t\t`const TREE_SHAKE_COOKIE_STRATEGY_USED = ${allUsedStrategies.has(\"cookie\")};`\n\t)\n\t.replace(\n\t\t`export const TREE_SHAKE_URL_STRATEGY_USED = false;`,\n\t\t`const TREE_SHAKE_URL_STRATEGY_USED = ${allUsedStrategies.has(\"url\")};`\n\t)\n\t.replace(\n\t\t`export const TREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED = false;`,\n\t\t`const TREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED = ${allUsedStrategies.has(\"globalVariable\")};`\n\t)\n\t.replace(\n\t\t`export const TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED = false;`,\n\t\t`const TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED = ${allUsedStrategies.has(\"preferredLanguage\")};`\n\t)\n\t.replace(\n\t\t`export const urlPatterns = [];`,\n\t\t`export const urlPatterns = ${JSON.stringify(urlPatterns, null, 2)};`\n\t)\n\t.replace(\n\t\t`export const routing = {};`,\n\t\t`export const routing = ${JSON.stringify(hostRouting ?? {}, null, 2)};`\n\t)\n\t.replace(\n\t\t`export const trailingSlash = undefined;`,\n\t\t`export const trailingSlash = ${\n\t\t\targs.compilerOptions.trailingSlash === undefined\n\t\t\t\t? \"undefined\"\n\t\t\t\t: JSON.stringify(args.compilerOptions.trailingSlash)\n\t\t};`\n\t)\n\t.replace(\n\t\t`export const disableAsyncLocalStorage = false;`,\n\t\t`export const disableAsyncLocalStorage = ${args.compilerOptions.disableAsyncLocalStorage};`\n\t)\n\t.replace(\n\t\t`export const TREE_SHAKE_DEFAULT_URL_PATTERN_USED = false;`,\n\t\t`const TREE_SHAKE_DEFAULT_URL_PATTERN_USED = ${defaultUrlPatternUsed};`\n\t)\n\t.replace(\n\t\t`export const TREE_SHAKE_HOST_ROUTING_USED = false;`,\n\t\t`const TREE_SHAKE_HOST_ROUTING_USED = ${hostRoutingUsed};`\n\t)\n\t.replace(\n\t\t`export const experimentalMiddlewareLocaleSplitting = false;`,\n\t\t`export const experimentalMiddlewareLocaleSplitting = ${args.compilerOptions.experimentalMiddlewareLocaleSplitting};`\n\t)\n\t.replace(\n\t\t`export const isServer = typeof window === \"undefined\";`,\n\t\t`export const isServer = ${args.compilerOptions.isServer};`\n\t)\n\t.replace(\n\t\t`export const experimentalStaticLocale = undefined;`,\n\t\targs.compilerOptions.experimentalStaticLocale\n\t\t\t? `export const experimentalStaticLocale = ${\n\t\t\t\t\targs.compilerOptions.experimentalStaticLocale ===\n\t\t\t\t\tperLocaleBuildStaticLocaleExpression\n\t\t\t\t\t\t? args.compilerOptions.experimentalStaticLocale\n\t\t\t\t\t\t: `assertIsLocale(${args.compilerOptions.experimentalStaticLocale})`\n\t\t\t\t};`\n\t\t\t: `export const experimentalStaticLocale = undefined;`\n\t)\n\t.replace(\n\t\t`export const localStorageKey = \"PARAGLIDE_LOCALE\";`,\n\t\t`export const localStorageKey = \"${args.compilerOptions.localStorageKey}\";`\n\t)\n\t.replace(\n\t\t`export const TREE_SHAKE_LOCAL_STORAGE_STRATEGY_USED = false;`,\n\t\t`const TREE_SHAKE_LOCAL_STORAGE_STRATEGY_USED = ${allUsedStrategies.has(\"localStorage\")};`\n\t)}\n\n/** @type {any} */ (globalThis).__paraglide =\n\t/** @type {any} */ (globalThis).__paraglide ?? {};\n/** @type {any} */ (globalThis).__paraglide.ssr =\n\t/** @type {any} */ (globalThis).__paraglide.ssr ?? {};\n\n${injectCode(\"./get-locale.js\")}\n\n${injectCode(\"./get-text-direction.js\")}\n\n${injectCode(\"./set-locale.js\")}\n\n${injectCode(\"./get-url-origin.js\")}\n\n${injectCode(\"./check-locale.js\")}\n\n${injectCode(\"./normalize-trailing-slash.js\", {\n\tprivateExports: [\"normalizeTrailingSlash\"],\n})}\n\n${injectCode(\"./exec-url-pattern.js\", {\n\tprivateExports: [\"execUrlPattern\"],\n})}\n\n${injectCode(\"./extract-locale-from-request.js\")}\n\n${injectCode(\"./extract-locale-from-request-async.js\")}\n\n${injectCode(\"./extract-locale-from-cookie.js\", {\n\tprivateExports: [\"clearLocaleCookieCache\"],\n})}\n\n${injectCode(\"./extract-locale-from-header.js\")}\n\n${injectCode(\"./extract-locale-from-navigator.js\")}\n\n${injectCode(\"./url-pattern.js\")}\n\n${injectCode(\"./host-routing.js\")}\n\n${injectCode(\"./extract-locale-from-url.js\")}\n\n${injectCode(\"./localize-url.js\")}\n\n${injectCode(\"./route-strategy.js\")}\n\n${injectCode(\"./should-redirect.js\")}\n\n${injectCode(\"./localize-href.js\")}\n\n${injectCode(\"./track-message-call.js\")}\n\n${injectCode(\"./generate-static-localized-urls.js\")}\n\n${injectCode(\"./strategy.js\")}\n\n${injectCode(\"./type-definitions.js\").replace(\n\t`@typedef {string} Locale`,\n\t`@typedef {typeof locales[number]} Locale`\n)}\n`;\n\n\treturn code;\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 * @param {{ privateExports?: string[] }} [options]\n * @returns {string}\n */\nfunction injectCode(\n\tpath: string,\n\toptions?: { privateExports?: string[] }\n): 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\tlet injected = code\n\t\t.replace(importRegex, \"\")\n\t\t.replace(sourceMapRegex, \"\")\n\t\t.replace(blockSourceMapRegex, \"\")\n\t\t.trim();\n\tfor (const exportName of options?.privateExports ?? []) {\n\t\tinjected = injected.replace(\n\t\t\tnew RegExp(`\\\\bexport\\\\s+function\\\\s+${exportName}\\\\b`, \"g\"),\n\t\t\t`function ${exportName}`\n\t\t);\n\t}\n\treturn injected;\n}\n"]}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Matches a canonical URL while allowing configured patterns to retain their
3
+ * existing trailing slash style.
4
+ *
5
+ * @param {URLPattern} pattern
6
+ * @param {URL} url
7
+ * @returns {any}
8
+ */
9
+ export declare function execUrlPattern(pattern: URLPattern, url: URL): any;
@@ -0,0 +1,31 @@
1
+ import { trailingSlash } from "./variables.js";
2
+ /**
3
+ * Matches a canonical URL while allowing configured patterns to retain their
4
+ * existing trailing slash style.
5
+ *
6
+ * @param {URLPattern} pattern
7
+ * @param {URL} url
8
+ * @returns {any}
9
+ */
10
+ export function execUrlPattern(pattern, url) {
11
+ if (trailingSlash === undefined || url.pathname === "/") {
12
+ return pattern.exec(url.href);
13
+ }
14
+ const alias = new URL(url);
15
+ if (trailingSlash === "always") {
16
+ alias.pathname = alias.pathname.replace(/\/+$/, "") || "/";
17
+ // Prefer the slashless alias so the canonical slash does not become part
18
+ // of a terminal wildcard capture.
19
+ return pattern.exec(alias.href) ?? pattern.exec(url.href);
20
+ }
21
+ else if (trailingSlash === "never") {
22
+ const match = pattern.exec(url.href);
23
+ if (match) {
24
+ return match;
25
+ }
26
+ alias.pathname = alias.pathname.replace(/\/+$/, "") + "/";
27
+ return pattern.exec(alias.href);
28
+ }
29
+ return pattern.exec(url.href);
30
+ }
31
+ //# sourceMappingURL=exec-url-pattern.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exec-url-pattern.js","sourceRoot":"","sources":["../../../src/compiler/runtime/exec-url-pattern.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,OAAO,EAAE,GAAG;IAC1C,IAAI,aAAa,KAAK,SAAS,IAAI,GAAG,CAAC,QAAQ,KAAK,GAAG,EAAE,CAAC;QACzD,OAAO,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IAC3B,IAAI,aAAa,KAAK,QAAQ,EAAE,CAAC;QAChC,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC;QAC3D,yEAAyE;QACzE,kCAAkC;QAClC,OAAO,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC3D,CAAC;SAAM,IAAI,aAAa,KAAK,OAAO,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,KAAK,EAAE,CAAC;YACX,OAAO,KAAK,CAAC;QACd,CAAC;QACD,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC;QAC1D,OAAO,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IACD,OAAO,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC","sourcesContent":["import { trailingSlash } from \"./variables.js\";\n\n/**\n * Matches a canonical URL while allowing configured patterns to retain their\n * existing trailing slash style.\n *\n * @param {URLPattern} pattern\n * @param {URL} url\n * @returns {any}\n */\nexport function execUrlPattern(pattern, url) {\n\tif (trailingSlash === undefined || url.pathname === \"/\") {\n\t\treturn pattern.exec(url.href);\n\t}\n\n\tconst alias = new URL(url);\n\tif (trailingSlash === \"always\") {\n\t\talias.pathname = alias.pathname.replace(/\\/+$/, \"\") || \"/\";\n\t\t// Prefer the slashless alias so the canonical slash does not become part\n\t\t// of a terminal wildcard capture.\n\t\treturn pattern.exec(alias.href) ?? pattern.exec(url.href);\n\t} else if (trailingSlash === \"never\") {\n\t\tconst match = pattern.exec(url.href);\n\t\tif (match) {\n\t\t\treturn match;\n\t\t}\n\t\talias.pathname = alias.pathname.replace(/\\/+$/, \"\") + \"/\";\n\t\treturn pattern.exec(alias.href);\n\t}\n\treturn pattern.exec(url.href);\n}\n"]}
@@ -1,7 +1,9 @@
1
1
  import { toLocale } from "./check-locale.js";
2
- import { getCompiledUrlPattern } from "./url-pattern.js";
2
+ import { getUrlPattern } from "./url-pattern.js";
3
3
  import { extractLocaleFromUrlHostRouting } from "./host-routing.js";
4
4
  import { baseLocale, TREE_SHAKE_DEFAULT_URL_PATTERN_USED, TREE_SHAKE_HOST_ROUTING_USED, urlPatterns, } from "./variables.js";
5
+ import { normalizeTrailingSlash } from "./normalize-trailing-slash.js";
6
+ import { execUrlPattern } from "./exec-url-pattern.js";
5
7
  /**
6
8
  * If extractLocaleFromUrl is called many times on the same page and the URL
7
9
  * hasn't changed, we don't need to recompute it every time which can get expensive.
@@ -33,14 +35,17 @@ export function extractLocaleFromUrl(url) {
33
35
  result = extractLocaleFromUrlHostRouting(url);
34
36
  }
35
37
  else if (TREE_SHAKE_DEFAULT_URL_PATTERN_USED) {
36
- result = defaultUrlPatternExtractLocale(url);
38
+ const urlObj = typeof url === "string"
39
+ ? new URL(url, "http://example.com")
40
+ : new URL(url);
41
+ result = defaultUrlPatternExtractLocale(normalizeTrailingSlash(urlObj));
37
42
  }
38
43
  else {
39
- const urlObj = typeof url === "string" ? new URL(url) : url;
44
+ const urlObj = normalizeTrailingSlash(typeof url === "string" ? new URL(url) : new URL(url));
40
45
  // Iterate over URL patterns
41
46
  for (const element of urlPatterns) {
42
47
  for (const [locale, localizedPattern] of element.localized) {
43
- const match = getCompiledUrlPattern(localizedPattern).exec(urlObj.href);
48
+ const match = execUrlPattern(getUrlPattern(localizedPattern, urlObj), urlObj);
44
49
  if (match) {
45
50
  result = locale;
46
51
  break;
@@ -1 +1 @@
1
- {"version":3,"file":"extract-locale-from-url.js","sourceRoot":"","sources":["../../../src/compiler/runtime/extract-locale-from-url.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,+BAA+B,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,EACN,UAAU,EACV,mCAAmC,EACnC,4BAA4B,EAC5B,WAAW,GACX,MAAM,gBAAgB,CAAC;AAExB;;;;;GAKG;AACH,+BAA+B;AAC/B,IAAI,SAAS,CAAC;AACd,+BAA+B;AAC/B,IAAI,YAAY,CAAC;AAEjB;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAG;IACvC,MAAM,SAAS,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;IAE3D,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,YAAY,CAAC;IACrB,CAAC;IAED,iCAAiC;IACjC,IAAI,MAAM,CAAC;IACX,IAAI,4BAA4B,EAAE,CAAC;QAClC,MAAM,GAAG,+BAA+B,CAAC,GAAG,CAAC,CAAC;IAC/C,CAAC;SAAM,IAAI,mCAAmC,EAAE,CAAC;QAChD,MAAM,GAAG,8BAA8B,CAAC,GAAG,CAAC,CAAC;IAC9C,CAAC;SAAM,CAAC;QACP,MAAM,MAAM,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QAE5D,4BAA4B;QAC5B,KAAK,MAAM,OAAO,IAAI,WAAW,EAAE,CAAC;YACnC,KAAK,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;gBAC5D,MAAM,KAAK,GAAG,qBAAqB,CAAC,gBAAgB,CAAC,CAAC,IAAI,CACzD,MAAM,CAAC,IAAI,CACX,CAAC;gBAEF,IAAI,KAAK,EAAE,CAAC;oBACX,MAAM,GAAG,MAAM,CAAC;oBAChB,MAAM;gBACP,CAAC;YACF,CAAC;YACD,IAAI,MAAM;gBAAE,MAAM;QACnB,CAAC;IACF,CAAC;IAED,SAAS,GAAG,SAAS,CAAC;IACtB,YAAY,GAAG,MAAM,CAAC;IACtB,OAAO,MAAM,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,SAAS,8BAA8B,CAAC,GAAG;IAC1C,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,EAAE,oBAAoB,CAAC,CAAC;IAClD,MAAM,YAAY,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAChE,OAAO,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC;AAChD,CAAC","sourcesContent":["import { toLocale } from \"./check-locale.js\";\nimport { getCompiledUrlPattern } from \"./url-pattern.js\";\nimport { extractLocaleFromUrlHostRouting } from \"./host-routing.js\";\nimport {\n\tbaseLocale,\n\tTREE_SHAKE_DEFAULT_URL_PATTERN_USED,\n\tTREE_SHAKE_HOST_ROUTING_USED,\n\turlPatterns,\n} from \"./variables.js\";\n\n/**\n * If extractLocaleFromUrl is called many times on the same page and the URL\n * hasn't changed, we don't need to recompute it every time which can get expensive.\n * We might use a LRU cache if needed, but for now storing only the last result is enough.\n * https://github.com/opral/monorepo/pull/3575#discussion_r2066731243\n */\n/** @type {string|undefined} */\nlet cachedUrl;\n/** @type {Locale|undefined} */\nlet cachedLocale;\n\n/**\n * Extracts the locale from a given URL using native URLPattern.\n *\n * The built-in default `/:locale/...` routing is case-insensitive because it\n * canonicalizes the first path segment with `toLocale()`. Custom `urlPatterns`\n * keep URLPattern's normal exact matching semantics for path segments.\n *\n * @param {URL|string} url - The full URL from which to extract the locale.\n * @returns {Locale|undefined} The extracted locale, or undefined if no locale is found.\n */\nexport function extractLocaleFromUrl(url) {\n\tconst urlString = typeof url === \"string\" ? url : url.href;\n\n\tif (cachedUrl === urlString) {\n\t\treturn cachedLocale;\n\t}\n\n\t/** @type {Locale | undefined} */\n\tlet result;\n\tif (TREE_SHAKE_HOST_ROUTING_USED) {\n\t\tresult = extractLocaleFromUrlHostRouting(url);\n\t} else if (TREE_SHAKE_DEFAULT_URL_PATTERN_USED) {\n\t\tresult = defaultUrlPatternExtractLocale(url);\n\t} else {\n\t\tconst urlObj = typeof url === \"string\" ? new URL(url) : url;\n\n\t\t// Iterate over URL patterns\n\t\tfor (const element of urlPatterns) {\n\t\t\tfor (const [locale, localizedPattern] of element.localized) {\n\t\t\t\tconst match = getCompiledUrlPattern(localizedPattern).exec(\n\t\t\t\t\turlObj.href\n\t\t\t\t);\n\n\t\t\t\tif (match) {\n\t\t\t\t\tresult = locale;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (result) break;\n\t\t}\n\t}\n\n\tcachedUrl = urlString;\n\tcachedLocale = result;\n\treturn result;\n}\n\n/**\n * https://github.com/opral/inlang-paraglide-js/issues/381\n *\n * @param {URL | string} url - The full URL from which to extract the locale.\n * @returns {Locale | undefined} The extracted locale, or undefined if no locale is found.\n */\nfunction defaultUrlPatternExtractLocale(url) {\n\tconst urlObj = new URL(url, \"http://example.com\");\n\tconst pathSegments = urlObj.pathname.split(\"/\").filter(Boolean);\n\treturn toLocale(pathSegments[0]) || baseLocale;\n}\n"]}
1
+ {"version":3,"file":"extract-locale-from-url.js","sourceRoot":"","sources":["../../../src/compiler/runtime/extract-locale-from-url.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,+BAA+B,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,EACN,UAAU,EACV,mCAAmC,EACnC,4BAA4B,EAC5B,WAAW,GACX,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD;;;;;GAKG;AACH,+BAA+B;AAC/B,IAAI,SAAS,CAAC;AACd,+BAA+B;AAC/B,IAAI,YAAY,CAAC;AAEjB;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAG;IACvC,MAAM,SAAS,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;IAE3D,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,YAAY,CAAC;IACrB,CAAC;IAED,iCAAiC;IACjC,IAAI,MAAM,CAAC;IACX,IAAI,4BAA4B,EAAE,CAAC;QAClC,MAAM,GAAG,+BAA+B,CAAC,GAAG,CAAC,CAAC;IAC/C,CAAC;SAAM,IAAI,mCAAmC,EAAE,CAAC;QAChD,MAAM,MAAM,GACX,OAAO,GAAG,KAAK,QAAQ;YACtB,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,EAAE,oBAAoB,CAAC;YACpC,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QACjB,MAAM,GAAG,8BAA8B,CAAC,sBAAsB,CAAC,MAAM,CAAC,CAAC,CAAC;IACzE,CAAC;SAAM,CAAC;QACP,MAAM,MAAM,GAAG,sBAAsB,CACpC,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CACrD,CAAC;QAEF,4BAA4B;QAC5B,KAAK,MAAM,OAAO,IAAI,WAAW,EAAE,CAAC;YACnC,KAAK,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;gBAC5D,MAAM,KAAK,GAAG,cAAc,CAC3B,aAAa,CAAC,gBAAgB,EAAE,MAAM,CAAC,EACvC,MAAM,CACN,CAAC;gBAEF,IAAI,KAAK,EAAE,CAAC;oBACX,MAAM,GAAG,MAAM,CAAC;oBAChB,MAAM;gBACP,CAAC;YACF,CAAC;YACD,IAAI,MAAM;gBAAE,MAAM;QACnB,CAAC;IACF,CAAC;IAED,SAAS,GAAG,SAAS,CAAC;IACtB,YAAY,GAAG,MAAM,CAAC;IACtB,OAAO,MAAM,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,SAAS,8BAA8B,CAAC,GAAG;IAC1C,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,EAAE,oBAAoB,CAAC,CAAC;IAClD,MAAM,YAAY,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAChE,OAAO,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC;AAChD,CAAC","sourcesContent":["import { toLocale } from \"./check-locale.js\";\nimport { getUrlPattern } from \"./url-pattern.js\";\nimport { extractLocaleFromUrlHostRouting } from \"./host-routing.js\";\nimport {\n\tbaseLocale,\n\tTREE_SHAKE_DEFAULT_URL_PATTERN_USED,\n\tTREE_SHAKE_HOST_ROUTING_USED,\n\turlPatterns,\n} from \"./variables.js\";\nimport { normalizeTrailingSlash } from \"./normalize-trailing-slash.js\";\nimport { execUrlPattern } from \"./exec-url-pattern.js\";\n\n/**\n * If extractLocaleFromUrl is called many times on the same page and the URL\n * hasn't changed, we don't need to recompute it every time which can get expensive.\n * We might use a LRU cache if needed, but for now storing only the last result is enough.\n * https://github.com/opral/monorepo/pull/3575#discussion_r2066731243\n */\n/** @type {string|undefined} */\nlet cachedUrl;\n/** @type {Locale|undefined} */\nlet cachedLocale;\n\n/**\n * Extracts the locale from a given URL using native URLPattern.\n *\n * The built-in default `/:locale/...` routing is case-insensitive because it\n * canonicalizes the first path segment with `toLocale()`. Custom `urlPatterns`\n * keep URLPattern's normal exact matching semantics for path segments.\n *\n * @param {URL|string} url - The full URL from which to extract the locale.\n * @returns {Locale|undefined} The extracted locale, or undefined if no locale is found.\n */\nexport function extractLocaleFromUrl(url) {\n\tconst urlString = typeof url === \"string\" ? url : url.href;\n\n\tif (cachedUrl === urlString) {\n\t\treturn cachedLocale;\n\t}\n\n\t/** @type {Locale | undefined} */\n\tlet result;\n\tif (TREE_SHAKE_HOST_ROUTING_USED) {\n\t\tresult = extractLocaleFromUrlHostRouting(url);\n\t} else if (TREE_SHAKE_DEFAULT_URL_PATTERN_USED) {\n\t\tconst urlObj =\n\t\t\ttypeof url === \"string\"\n\t\t\t\t? new URL(url, \"http://example.com\")\n\t\t\t\t: new URL(url);\n\t\tresult = defaultUrlPatternExtractLocale(normalizeTrailingSlash(urlObj));\n\t} else {\n\t\tconst urlObj = normalizeTrailingSlash(\n\t\t\ttypeof url === \"string\" ? new URL(url) : new URL(url)\n\t\t);\n\n\t\t// Iterate over URL patterns\n\t\tfor (const element of urlPatterns) {\n\t\t\tfor (const [locale, localizedPattern] of element.localized) {\n\t\t\t\tconst match = execUrlPattern(\n\t\t\t\t\tgetUrlPattern(localizedPattern, urlObj),\n\t\t\t\t\turlObj\n\t\t\t\t);\n\n\t\t\t\tif (match) {\n\t\t\t\t\tresult = locale;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (result) break;\n\t\t}\n\t}\n\n\tcachedUrl = urlString;\n\tcachedLocale = result;\n\treturn result;\n}\n\n/**\n * https://github.com/opral/inlang-paraglide-js/issues/381\n *\n * @param {URL | string} url - The full URL from which to extract the locale.\n * @returns {Locale | undefined} The extracted locale, or undefined if no locale is found.\n */\nfunction defaultUrlPatternExtractLocale(url) {\n\tconst urlObj = new URL(url, \"http://example.com\");\n\tconst pathSegments = urlObj.pathname.split(\"/\").filter(Boolean);\n\treturn toLocale(pathSegments[0]) || baseLocale;\n}\n"]}
@@ -1,5 +1,8 @@
1
1
  import { localizeUrl } from "./localize-url.js";
2
- import { locales, baseLocale, TREE_SHAKE_DEFAULT_URL_PATTERN_USED, TREE_SHAKE_HOST_ROUTING_USED, urlPatterns, } from "./variables.js";
2
+ import { normalizeTrailingSlash } from "./normalize-trailing-slash.js";
3
+ import { execUrlPattern } from "./exec-url-pattern.js";
4
+ import { getUrlPattern } from "./url-pattern.js";
5
+ import { locales, baseLocale, TREE_SHAKE_DEFAULT_URL_PATTERN_USED, TREE_SHAKE_HOST_ROUTING_USED, trailingSlash, urlPatterns, } from "./variables.js";
3
6
  import { getRoutingRule } from "./host-routing.js";
4
7
  /**
5
8
  * Generates localized URL variants for all provided URLs based on your configured locales and URL patterns.
@@ -50,9 +53,12 @@ export function generateStaticLocalizedUrls(urls) {
50
53
  // For default URL pattern, we can optimize the generation
51
54
  if (TREE_SHAKE_DEFAULT_URL_PATTERN_USED) {
52
55
  for (const urlInput of urls) {
53
- const url = urlInput instanceof URL
56
+ const originalUrl = urlInput instanceof URL
54
57
  ? urlInput
55
58
  : new URL(urlInput, "http://localhost");
59
+ const url = trailingSlash === undefined
60
+ ? originalUrl
61
+ : normalizeTrailingSlash(new URL(originalUrl));
56
62
  // Base locale doesn't get a prefix
57
63
  localizedUrls.add(url);
58
64
  // Other locales get their code as prefix
@@ -60,7 +66,7 @@ export function generateStaticLocalizedUrls(urls) {
60
66
  if (locale !== baseLocale) {
61
67
  const localizedPath = `/${locale}${url.pathname}${url.search}${url.hash}`;
62
68
  const localizedUrl = new URL(localizedPath, url.origin);
63
- localizedUrls.add(localizedUrl);
69
+ localizedUrls.add(normalizeTrailingSlash(localizedUrl));
64
70
  }
65
71
  }
66
72
  }
@@ -86,15 +92,16 @@ export function generateStaticLocalizedUrls(urls) {
86
92
  }
87
93
  // For custom URL patterns, we need to use localizeUrl for each URL and locale
88
94
  for (const urlInput of urls) {
89
- const url = urlInput instanceof URL
95
+ const originalUrl = urlInput instanceof URL
90
96
  ? urlInput
91
97
  : new URL(urlInput, "http://localhost");
98
+ const url = normalizeTrailingSlash(new URL(originalUrl));
92
99
  // Try each URL pattern to find one that matches
93
100
  let patternFound = false;
94
101
  for (const pattern of urlPatterns) {
95
102
  try {
96
103
  // Try to match the unlocalized pattern
97
- const unlocalizedMatch = new URLPattern(pattern.pattern, url.href).exec(url.href);
104
+ const unlocalizedMatch = execUrlPattern(getUrlPattern(pattern.pattern, url), url);
98
105
  if (!unlocalizedMatch)
99
106
  continue;
100
107
  patternFound = true;
@@ -125,7 +132,7 @@ export function generateStaticLocalizedUrls(urls) {
125
132
  }
126
133
  // If no pattern matched, use the URL as is
127
134
  if (!patternFound) {
128
- localizedUrls.add(url);
135
+ localizedUrls.add(originalUrl);
129
136
  }
130
137
  }
131
138
  return Array.from(localizedUrls);