@inlang/paraglide-js 2.24.1 → 2.25.0

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 (85) hide show
  1. package/dist/bundler-plugins/esbuild.d.ts +1 -1
  2. package/dist/bundler-plugins/per-locale-build/index.d.ts +3 -3
  3. package/dist/bundler-plugins/per-locale-build/index.js +59 -15
  4. package/dist/bundler-plugins/per-locale-build/index.js.map +1 -1
  5. package/dist/bundler-plugins/rolldown.d.ts +1 -1
  6. package/dist/bundler-plugins/rollup.d.ts +1 -1
  7. package/dist/bundler-plugins/rspack.d.ts +1 -1
  8. package/dist/bundler-plugins/unplugin.d.ts +35 -1
  9. package/dist/bundler-plugins/unplugin.js +404 -123
  10. package/dist/bundler-plugins/unplugin.js.map +1 -1
  11. package/dist/bundler-plugins/unplugin.test.js +357 -202
  12. package/dist/bundler-plugins/unplugin.test.js.map +1 -1
  13. package/dist/bundler-plugins/vite.d.ts +2 -2
  14. package/dist/bundler-plugins/vite.js.map +1 -1
  15. package/dist/bundler-plugins/vite.test.js +2 -1
  16. package/dist/bundler-plugins/vite.test.js.map +1 -1
  17. package/dist/bundler-plugins/webpack.d.ts +1 -1
  18. package/dist/cli/commands/compile/command.js +156 -36
  19. package/dist/cli/commands/compile/command.js.map +1 -1
  20. package/dist/cli/commands/compile/command.test.js +311 -7
  21. package/dist/cli/commands/compile/command.test.js.map +1 -1
  22. package/dist/cli/defaults.d.ts +0 -2
  23. package/dist/cli/defaults.js +0 -2
  24. package/dist/cli/defaults.js.map +1 -1
  25. package/dist/cli/steps/initialize-inlang-project.js +2 -1
  26. package/dist/cli/steps/initialize-inlang-project.js.map +1 -1
  27. package/dist/cli/steps/prompt-for-outdir.js +1 -1
  28. package/dist/cli/steps/prompt-for-outdir.js.map +1 -1
  29. package/dist/compiler/compile-annotation.d.ts +2 -2
  30. package/dist/compiler/compile-annotation.js +9 -9
  31. package/dist/compiler/compile-annotation.js.map +1 -1
  32. package/dist/compiler/compile-local-variable.d.ts +2 -1
  33. package/dist/compiler/compile-local-variable.js +4 -4
  34. package/dist/compiler/compile-local-variable.js.map +1 -1
  35. package/dist/compiler/compile-local-variable.test.js +82 -0
  36. package/dist/compiler/compile-local-variable.test.js.map +1 -1
  37. package/dist/compiler/compile-message.js +10 -2
  38. package/dist/compiler/compile-message.js.map +1 -1
  39. package/dist/compiler/compile-message.test.js +132 -2
  40. package/dist/compiler/compile-message.test.js.map +1 -1
  41. package/dist/compiler/compile-pattern.js +4 -2
  42. package/dist/compiler/compile-pattern.js.map +1 -1
  43. package/dist/compiler/compile-pattern.test.js +31 -0
  44. package/dist/compiler/compile-pattern.test.js.map +1 -1
  45. package/dist/compiler/compiler-options.d.ts +1 -1
  46. package/dist/compiler/compiler-options.js.map +1 -1
  47. package/dist/compiler/server/create-server-file.js +18 -4
  48. package/dist/compiler/server/create-server-file.js.map +1 -1
  49. package/dist/compiler/server/create-server-file.test.js +76 -0
  50. package/dist/compiler/server/create-server-file.test.js.map +1 -1
  51. package/dist/compiler/variable-access.d.ts +2 -0
  52. package/dist/compiler/variable-access.js +6 -0
  53. package/dist/compiler/variable-access.js.map +1 -1
  54. package/dist/index.d.ts +1 -0
  55. package/dist/index.js +1 -0
  56. package/dist/index.js.map +1 -1
  57. package/dist/services/config/config-schema.d.ts +69 -0
  58. package/dist/services/config/config-schema.js +78 -0
  59. package/dist/services/config/config-schema.js.map +1 -0
  60. package/dist/services/config/defaults.d.ts +11 -0
  61. package/dist/services/config/defaults.js +12 -0
  62. package/dist/services/config/defaults.js.map +1 -0
  63. package/dist/services/config/discover-config-file.d.ts +19 -0
  64. package/dist/services/config/discover-config-file.js +39 -0
  65. package/dist/services/config/discover-config-file.js.map +1 -0
  66. package/dist/services/config/index.d.ts +24 -0
  67. package/dist/services/config/index.js +25 -0
  68. package/dist/services/config/index.js.map +1 -0
  69. package/dist/services/config/load-paraglide-config.d.ts +44 -0
  70. package/dist/services/config/load-paraglide-config.js +109 -0
  71. package/dist/services/config/load-paraglide-config.js.map +1 -0
  72. package/dist/services/config/load-paraglide-config.test.d.ts +1 -0
  73. package/dist/services/config/load-paraglide-config.test.js +201 -0
  74. package/dist/services/config/load-paraglide-config.test.js.map +1 -0
  75. package/dist/services/config/resolve-compiler-options.d.ts +24 -0
  76. package/dist/services/config/resolve-compiler-options.js +51 -0
  77. package/dist/services/config/resolve-compiler-options.js.map +1 -0
  78. package/dist/services/config/resolve-compiler-options.test.d.ts +1 -0
  79. package/dist/services/config/resolve-compiler-options.test.js +60 -0
  80. package/dist/services/config/resolve-compiler-options.test.js.map +1 -0
  81. package/dist/services/env-variables/index.js +1 -1
  82. package/dist/services/env-variables/index.js.map +1 -1
  83. package/dist/services/logger/index.js +2 -0
  84. package/dist/services/logger/index.js.map +1 -1
  85. package/package.json +4 -2
@@ -146,6 +146,37 @@ test("compiles pattern expression annotation options like local variable annotat
146
146
  });
147
147
  expect(code).toBe('`${registry.number("de", i?.price, { minimumFractionDigits: 2, style: "percent" })}`');
148
148
  });
149
+ test("compiles pattern annotation options that reference local variables", () => {
150
+ const pattern = [
151
+ {
152
+ type: "expression",
153
+ arg: { type: "variable-reference", name: "price" },
154
+ annotation: {
155
+ type: "function-reference",
156
+ name: "number",
157
+ options: [
158
+ {
159
+ name: "minimumFractionDigits",
160
+ value: { type: "variable-reference", name: "digits" },
161
+ },
162
+ ],
163
+ },
164
+ },
165
+ ];
166
+ const { code } = compilePattern({
167
+ pattern,
168
+ declarations: [
169
+ { type: "input-variable", name: "price" },
170
+ {
171
+ type: "local-variable",
172
+ name: "digits",
173
+ value: { type: "expression", arg: { type: "literal", value: "2" } },
174
+ },
175
+ ],
176
+ locale: "en",
177
+ });
178
+ expect(code).toBe('`${registry.number("en", i?.price, { minimumFractionDigits: digits })}`');
179
+ });
149
180
  test("parts mode compiles pattern expression annotations to registry calls", () => {
150
181
  const pattern = [
151
182
  {
@@ -1 +1 @@
1
- {"version":3,"file":"compile-pattern.test.js","sourceRoot":"","sources":["../../src/compiler/compile-pattern.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { test, expect } from \"vitest\";\nimport { compilePattern } from \"./compile-pattern.js\";\nimport type { Pattern } from \"@inlang/sdk\";\n\ntest(\"should compile a text only pattern\", () => {\n\tconst pattern: Pattern = [{ type: \"text\", value: \"Hello\" }];\n\tconst { code } = compilePattern({ pattern, declarations: [] });\n\texpect(code).toBe(\"`Hello`\");\n});\n\ntest(\"should compile a pattern with multiple VariableReference's\", () => {\n\tconst pattern: Pattern = [\n\t\t{ type: \"text\", value: \"Hello \" },\n\t\t{\n\t\t\ttype: \"expression\",\n\t\t\targ: {\n\t\t\t\ttype: \"variable-reference\",\n\t\t\t\tname: \"name\",\n\t\t\t},\n\t\t},\n\t\t{ type: \"text\", value: \"! You have \" },\n\t\t{ type: \"expression\", arg: { type: \"variable-reference\", name: \"count\" } },\n\t\t{ type: \"text\", value: \" messages.\" },\n\t];\n\n\tconst { code } = compilePattern({\n\t\tpattern,\n\t\tdeclarations: [\n\t\t\t{ type: \"input-variable\", name: \"name\" },\n\t\t\t{ type: \"input-variable\", name: \"count\" },\n\t\t],\n\t});\n\n\texpect(code).toBe(\"`Hello ${i?.name}! You have ${i?.count} messages.`\");\n});\n\ntest(\"uses bracket notation for input variables with non-identifier names\", () => {\n\tconst pattern: Pattern = [\n\t\t{ type: \"text\", value: \"Half \" },\n\t\t{\n\t\t\ttype: \"expression\",\n\t\t\targ: {\n\t\t\t\ttype: \"variable-reference\",\n\t\t\t\tname: \"half!\",\n\t\t\t},\n\t\t},\n\t];\n\n\tconst { code } = compilePattern({\n\t\tpattern,\n\t\tdeclarations: [{ type: \"input-variable\", name: \"half!\" }],\n\t});\n\n\texpect(code).toBe('`Half ${i?.[\"half!\"]}`');\n});\n\ntest(\"should escape backticks\", () => {\n\tconst pattern: Pattern = [{ type: \"text\", value: \"`Hello world`\" }];\n\tconst { code } = compilePattern({ pattern, declarations: [] });\n\texpect(code).toBe(\"`\\\\`Hello world\\\\``\");\n});\n\ntest(\"should escape backslashes\", () => {\n\tconst pattern: Pattern = [{ type: \"text\", value: \"\\\\Hello world\\\\\" }];\n\tconst { code } = compilePattern({ pattern, declarations: [] });\n\n\texpect(code).toBe(\"`\\\\\\\\Hello world\\\\\\\\`\");\n});\n\ntest(\"should escape escaped backticks\", () => {\n\tconst pattern: Pattern = [{ type: \"text\", value: \"\\\\`Hello world\\\\`\" }];\n\tconst { code } = compilePattern({ pattern, declarations: [] });\n\n\texpect(code).toBe(\"`\\\\\\\\\\\\`Hello world\\\\\\\\\\\\``\");\n});\n\ntest(\"should escape variable interpolation ( ${} )\", () => {\n\tconst pattern: Pattern = [{ type: \"text\", value: \"${name\" }];\n\tconst { code } = compilePattern({ pattern, declarations: [] });\n\n\texpect(code).toBe(\"`\\\\${name`\");\n});\n\ntest(\"it can reference local variables\", () => {\n\tconst { code } = compilePattern({\n\t\tpattern: [\n\t\t\t{ type: \"text\", value: \"Hello \" },\n\t\t\t{\n\t\t\t\ttype: \"expression\",\n\t\t\t\targ: {\n\t\t\t\t\ttype: \"variable-reference\",\n\t\t\t\t\tname: \"name\",\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\tdeclarations: [\n\t\t\t{\n\t\t\t\ttype: \"local-variable\",\n\t\t\t\tname: \"name\",\n\t\t\t\tvalue: {\n\t\t\t\t\ttype: \"expression\",\n\t\t\t\t\targ: { type: \"literal\", value: \"Peter\" },\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t});\n\n\texpect(code).toBe(\"`Hello ${name}`\");\n});\n\ntest(\"plain string mode strips markup wrappers\", () => {\n\tconst pattern: Pattern = [\n\t\t{ type: \"text\", value: \"Hello \" },\n\t\t{ type: \"markup-start\", name: \"b\" },\n\t\t{ type: \"expression\", arg: { type: \"variable-reference\", name: \"name\" } },\n\t\t{ type: \"markup-end\", name: \"b\" },\n\t\t{ type: \"text\", value: \"!\" },\n\t];\n\n\tconst { code } = compilePattern({\n\t\tpattern,\n\t\tdeclarations: [{ type: \"input-variable\", name: \"name\" }],\n\t});\n\n\texpect(code).toBe(\"`Hello ${i?.name}!`\");\n});\n\ntest(\"compiles a pattern expression annotation to a registry call\", () => {\n\t// https://github.com/opral/paraglide-js/issues/694\n\tconst pattern: Pattern = [\n\t\t{\n\t\t\ttype: \"expression\",\n\t\t\targ: { type: \"variable-reference\", name: \"count\" },\n\t\t\tannotation: { type: \"function-reference\", name: \"number\", options: [] },\n\t\t},\n\t\t{ type: \"text\", value: \" views\" },\n\t];\n\n\tconst { code } = compilePattern({\n\t\tpattern,\n\t\tdeclarations: [{ type: \"input-variable\", name: \"count\" }],\n\t\tlocale: \"en\",\n\t});\n\n\texpect(code).toBe('`${registry.number(\"en\", i?.count, {})} views`');\n});\n\ntest(\"compiles pattern expression annotation options like local variable annotations\", () => {\n\tconst pattern: Pattern = [\n\t\t{\n\t\t\ttype: \"expression\",\n\t\t\targ: { type: \"variable-reference\", name: \"price\" },\n\t\t\tannotation: {\n\t\t\t\ttype: \"function-reference\",\n\t\t\t\tname: \"number\",\n\t\t\t\toptions: [\n\t\t\t\t\t{\n\t\t\t\t\t\tname: \"minimumFractionDigits\",\n\t\t\t\t\t\tvalue: { type: \"literal\", value: \"2\" },\n\t\t\t\t\t},\n\t\t\t\t\t{ name: \"style\", value: { type: \"literal\", value: \"percent\" } },\n\t\t\t\t],\n\t\t\t},\n\t\t},\n\t];\n\n\tconst { code } = compilePattern({\n\t\tpattern,\n\t\tdeclarations: [{ type: \"input-variable\", name: \"price\" }],\n\t\tlocale: \"de\",\n\t});\n\n\texpect(code).toBe(\n\t\t'`${registry.number(\"de\", i?.price, { minimumFractionDigits: 2, style: \"percent\" })}`'\n\t);\n});\n\ntest(\"parts mode compiles pattern expression annotations to registry calls\", () => {\n\tconst pattern: Pattern = [\n\t\t{\n\t\t\ttype: \"expression\",\n\t\t\targ: { type: \"variable-reference\", name: \"count\" },\n\t\t\tannotation: { type: \"function-reference\", name: \"number\", options: [] },\n\t\t},\n\t\t{ type: \"text\", value: \" views\" },\n\t];\n\n\tconst { code } = compilePattern({\n\t\tmode: \"parts\",\n\t\tpattern,\n\t\tdeclarations: [{ type: \"input-variable\", name: \"count\" }],\n\t\tlocale: \"en\",\n\t});\n\n\texpect(code).toBe(\n\t\t'[{ type: \"text\", value: String(registry.number(\"en\", i?.count, {})) }, { type: \"text\", value: \" views\" }]'\n\t);\n});\n\ntest(\"falls back to plain interpolation for unknown formatters\", () => {\n\tconst pattern: Pattern = [\n\t\t{\n\t\t\ttype: \"expression\",\n\t\t\targ: { type: \"variable-reference\", name: \"name\" },\n\t\t\tannotation: {\n\t\t\t\ttype: \"function-reference\",\n\t\t\t\tname: \"uppercase\",\n\t\t\t\toptions: [],\n\t\t\t},\n\t\t},\n\t];\n\n\tconst { code } = compilePattern({\n\t\tpattern,\n\t\tdeclarations: [{ type: \"input-variable\", name: \"name\" }],\n\t\tlocale: \"en\",\n\t});\n\n\texpect(code).toBe(\"`${i?.name}`\");\n});\n\ntest(\"throws when a pattern annotation is compiled without a locale\", () => {\n\tconst pattern: Pattern = [\n\t\t{\n\t\t\ttype: \"expression\",\n\t\t\targ: { type: \"variable-reference\", name: \"count\" },\n\t\t\tannotation: { type: \"function-reference\", name: \"number\", options: [] },\n\t\t},\n\t];\n\n\texpect(() =>\n\t\tcompilePattern({\n\t\t\tpattern,\n\t\t\tdeclarations: [{ type: \"input-variable\", name: \"count\" }],\n\t\t})\n\t).toThrow('requires a locale to compile the formatter \"number\"');\n});\n\ntest(\"validates relativetime options on pattern annotations\", () => {\n\tconst pattern: Pattern = [\n\t\t{\n\t\t\ttype: \"expression\",\n\t\t\targ: { type: \"variable-reference\", name: \"days\" },\n\t\t\tannotation: {\n\t\t\t\ttype: \"function-reference\",\n\t\t\t\tname: \"relativetime\",\n\t\t\t\toptions: [],\n\t\t\t},\n\t\t},\n\t];\n\n\texpect(() =>\n\t\tcompilePattern({\n\t\t\tpattern,\n\t\t\tdeclarations: [{ type: \"input-variable\", name: \"days\" }],\n\t\t\tlocale: \"en\",\n\t\t})\n\t).toThrow('The \"relativetime\" formatter requires a \"unit\" option.');\n});\n\ntest(\"parts mode compiles text, markup, options and attributes\", () => {\n\tconst pattern: Pattern = [\n\t\t{ type: \"text\", value: \"Read \" },\n\t\t{\n\t\t\ttype: \"markup-start\",\n\t\t\tname: \"link\",\n\t\t\toptions: [\n\t\t\t\t{ name: \"to\", value: { type: \"literal\", value: \"/docs\" } },\n\t\t\t\t{\n\t\t\t\t\tname: \"rel\",\n\t\t\t\t\tvalue: { type: \"variable-reference\", name: \"relationship\" },\n\t\t\t\t},\n\t\t\t],\n\t\t\tattributes: [\n\t\t\t\t{ name: \"track\", value: true },\n\t\t\t\t{ name: \"variant\", value: { type: \"literal\", value: \"hero\" } },\n\t\t\t],\n\t\t},\n\t\t{ type: \"text\", value: \"docs\" },\n\t\t{\n\t\t\ttype: \"markup-end\",\n\t\t\tname: \"link\",\n\t\t\toptions: [{ name: \"to\", value: { type: \"literal\", value: \"/docs\" } }],\n\t\t\tattributes: [{ name: \"track\", value: true }],\n\t\t},\n\t\t{\n\t\t\ttype: \"markup-standalone\",\n\t\t\tname: \"icon\",\n\t\t\toptions: [{ name: \"name\", value: { type: \"literal\", value: \"arrow\" } }],\n\t\t\tattributes: [{ name: \"filled\", value: true }],\n\t\t},\n\t];\n\n\tconst { code } = compilePattern({\n\t\tmode: \"parts\",\n\t\tpattern,\n\t\tdeclarations: [{ type: \"input-variable\", name: \"relationship\" }],\n\t});\n\n\texpect(code).toBe(\n\t\t'[{ type: \"text\", value: \"Read \" }, { type: \"markup-start\", name: \"link\", options: { \"to\": \"/docs\", \"rel\": i?.relationship }, attributes: { \"track\": true, \"variant\": \"hero\" } }, { type: \"text\", value: \"docs\" }, { type: \"markup-end\", name: \"link\", options: { \"to\": \"/docs\" }, attributes: { \"track\": true } }, { type: \"markup-standalone\", name: \"icon\", options: { \"name\": \"arrow\" }, attributes: { \"filled\": true } }]'\n\t);\n});\n"]}
1
+ {"version":3,"file":"compile-pattern.test.js","sourceRoot":"","sources":["../../src/compiler/compile-pattern.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { test, expect } from \"vitest\";\nimport { compilePattern } from \"./compile-pattern.js\";\nimport type { Pattern } from \"@inlang/sdk\";\n\ntest(\"should compile a text only pattern\", () => {\n\tconst pattern: Pattern = [{ type: \"text\", value: \"Hello\" }];\n\tconst { code } = compilePattern({ pattern, declarations: [] });\n\texpect(code).toBe(\"`Hello`\");\n});\n\ntest(\"should compile a pattern with multiple VariableReference's\", () => {\n\tconst pattern: Pattern = [\n\t\t{ type: \"text\", value: \"Hello \" },\n\t\t{\n\t\t\ttype: \"expression\",\n\t\t\targ: {\n\t\t\t\ttype: \"variable-reference\",\n\t\t\t\tname: \"name\",\n\t\t\t},\n\t\t},\n\t\t{ type: \"text\", value: \"! You have \" },\n\t\t{ type: \"expression\", arg: { type: \"variable-reference\", name: \"count\" } },\n\t\t{ type: \"text\", value: \" messages.\" },\n\t];\n\n\tconst { code } = compilePattern({\n\t\tpattern,\n\t\tdeclarations: [\n\t\t\t{ type: \"input-variable\", name: \"name\" },\n\t\t\t{ type: \"input-variable\", name: \"count\" },\n\t\t],\n\t});\n\n\texpect(code).toBe(\"`Hello ${i?.name}! You have ${i?.count} messages.`\");\n});\n\ntest(\"uses bracket notation for input variables with non-identifier names\", () => {\n\tconst pattern: Pattern = [\n\t\t{ type: \"text\", value: \"Half \" },\n\t\t{\n\t\t\ttype: \"expression\",\n\t\t\targ: {\n\t\t\t\ttype: \"variable-reference\",\n\t\t\t\tname: \"half!\",\n\t\t\t},\n\t\t},\n\t];\n\n\tconst { code } = compilePattern({\n\t\tpattern,\n\t\tdeclarations: [{ type: \"input-variable\", name: \"half!\" }],\n\t});\n\n\texpect(code).toBe('`Half ${i?.[\"half!\"]}`');\n});\n\ntest(\"should escape backticks\", () => {\n\tconst pattern: Pattern = [{ type: \"text\", value: \"`Hello world`\" }];\n\tconst { code } = compilePattern({ pattern, declarations: [] });\n\texpect(code).toBe(\"`\\\\`Hello world\\\\``\");\n});\n\ntest(\"should escape backslashes\", () => {\n\tconst pattern: Pattern = [{ type: \"text\", value: \"\\\\Hello world\\\\\" }];\n\tconst { code } = compilePattern({ pattern, declarations: [] });\n\n\texpect(code).toBe(\"`\\\\\\\\Hello world\\\\\\\\`\");\n});\n\ntest(\"should escape escaped backticks\", () => {\n\tconst pattern: Pattern = [{ type: \"text\", value: \"\\\\`Hello world\\\\`\" }];\n\tconst { code } = compilePattern({ pattern, declarations: [] });\n\n\texpect(code).toBe(\"`\\\\\\\\\\\\`Hello world\\\\\\\\\\\\``\");\n});\n\ntest(\"should escape variable interpolation ( ${} )\", () => {\n\tconst pattern: Pattern = [{ type: \"text\", value: \"${name\" }];\n\tconst { code } = compilePattern({ pattern, declarations: [] });\n\n\texpect(code).toBe(\"`\\\\${name`\");\n});\n\ntest(\"it can reference local variables\", () => {\n\tconst { code } = compilePattern({\n\t\tpattern: [\n\t\t\t{ type: \"text\", value: \"Hello \" },\n\t\t\t{\n\t\t\t\ttype: \"expression\",\n\t\t\t\targ: {\n\t\t\t\t\ttype: \"variable-reference\",\n\t\t\t\t\tname: \"name\",\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\tdeclarations: [\n\t\t\t{\n\t\t\t\ttype: \"local-variable\",\n\t\t\t\tname: \"name\",\n\t\t\t\tvalue: {\n\t\t\t\t\ttype: \"expression\",\n\t\t\t\t\targ: { type: \"literal\", value: \"Peter\" },\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t});\n\n\texpect(code).toBe(\"`Hello ${name}`\");\n});\n\ntest(\"plain string mode strips markup wrappers\", () => {\n\tconst pattern: Pattern = [\n\t\t{ type: \"text\", value: \"Hello \" },\n\t\t{ type: \"markup-start\", name: \"b\" },\n\t\t{ type: \"expression\", arg: { type: \"variable-reference\", name: \"name\" } },\n\t\t{ type: \"markup-end\", name: \"b\" },\n\t\t{ type: \"text\", value: \"!\" },\n\t];\n\n\tconst { code } = compilePattern({\n\t\tpattern,\n\t\tdeclarations: [{ type: \"input-variable\", name: \"name\" }],\n\t});\n\n\texpect(code).toBe(\"`Hello ${i?.name}!`\");\n});\n\ntest(\"compiles a pattern expression annotation to a registry call\", () => {\n\t// https://github.com/opral/paraglide-js/issues/694\n\tconst pattern: Pattern = [\n\t\t{\n\t\t\ttype: \"expression\",\n\t\t\targ: { type: \"variable-reference\", name: \"count\" },\n\t\t\tannotation: { type: \"function-reference\", name: \"number\", options: [] },\n\t\t},\n\t\t{ type: \"text\", value: \" views\" },\n\t];\n\n\tconst { code } = compilePattern({\n\t\tpattern,\n\t\tdeclarations: [{ type: \"input-variable\", name: \"count\" }],\n\t\tlocale: \"en\",\n\t});\n\n\texpect(code).toBe('`${registry.number(\"en\", i?.count, {})} views`');\n});\n\ntest(\"compiles pattern expression annotation options like local variable annotations\", () => {\n\tconst pattern: Pattern = [\n\t\t{\n\t\t\ttype: \"expression\",\n\t\t\targ: { type: \"variable-reference\", name: \"price\" },\n\t\t\tannotation: {\n\t\t\t\ttype: \"function-reference\",\n\t\t\t\tname: \"number\",\n\t\t\t\toptions: [\n\t\t\t\t\t{\n\t\t\t\t\t\tname: \"minimumFractionDigits\",\n\t\t\t\t\t\tvalue: { type: \"literal\", value: \"2\" },\n\t\t\t\t\t},\n\t\t\t\t\t{ name: \"style\", value: { type: \"literal\", value: \"percent\" } },\n\t\t\t\t],\n\t\t\t},\n\t\t},\n\t];\n\n\tconst { code } = compilePattern({\n\t\tpattern,\n\t\tdeclarations: [{ type: \"input-variable\", name: \"price\" }],\n\t\tlocale: \"de\",\n\t});\n\n\texpect(code).toBe(\n\t\t'`${registry.number(\"de\", i?.price, { minimumFractionDigits: 2, style: \"percent\" })}`'\n\t);\n});\n\ntest(\"compiles pattern annotation options that reference local variables\", () => {\n\tconst pattern: Pattern = [\n\t\t{\n\t\t\ttype: \"expression\",\n\t\t\targ: { type: \"variable-reference\", name: \"price\" },\n\t\t\tannotation: {\n\t\t\t\ttype: \"function-reference\",\n\t\t\t\tname: \"number\",\n\t\t\t\toptions: [\n\t\t\t\t\t{\n\t\t\t\t\t\tname: \"minimumFractionDigits\",\n\t\t\t\t\t\tvalue: { type: \"variable-reference\", name: \"digits\" },\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t},\n\t];\n\n\tconst { code } = compilePattern({\n\t\tpattern,\n\t\tdeclarations: [\n\t\t\t{ type: \"input-variable\", name: \"price\" },\n\t\t\t{\n\t\t\t\ttype: \"local-variable\",\n\t\t\t\tname: \"digits\",\n\t\t\t\tvalue: { type: \"expression\", arg: { type: \"literal\", value: \"2\" } },\n\t\t\t},\n\t\t],\n\t\tlocale: \"en\",\n\t});\n\n\texpect(code).toBe(\n\t\t'`${registry.number(\"en\", i?.price, { minimumFractionDigits: digits })}`'\n\t);\n});\n\ntest(\"parts mode compiles pattern expression annotations to registry calls\", () => {\n\tconst pattern: Pattern = [\n\t\t{\n\t\t\ttype: \"expression\",\n\t\t\targ: { type: \"variable-reference\", name: \"count\" },\n\t\t\tannotation: { type: \"function-reference\", name: \"number\", options: [] },\n\t\t},\n\t\t{ type: \"text\", value: \" views\" },\n\t];\n\n\tconst { code } = compilePattern({\n\t\tmode: \"parts\",\n\t\tpattern,\n\t\tdeclarations: [{ type: \"input-variable\", name: \"count\" }],\n\t\tlocale: \"en\",\n\t});\n\n\texpect(code).toBe(\n\t\t'[{ type: \"text\", value: String(registry.number(\"en\", i?.count, {})) }, { type: \"text\", value: \" views\" }]'\n\t);\n});\n\ntest(\"falls back to plain interpolation for unknown formatters\", () => {\n\tconst pattern: Pattern = [\n\t\t{\n\t\t\ttype: \"expression\",\n\t\t\targ: { type: \"variable-reference\", name: \"name\" },\n\t\t\tannotation: {\n\t\t\t\ttype: \"function-reference\",\n\t\t\t\tname: \"uppercase\",\n\t\t\t\toptions: [],\n\t\t\t},\n\t\t},\n\t];\n\n\tconst { code } = compilePattern({\n\t\tpattern,\n\t\tdeclarations: [{ type: \"input-variable\", name: \"name\" }],\n\t\tlocale: \"en\",\n\t});\n\n\texpect(code).toBe(\"`${i?.name}`\");\n});\n\ntest(\"throws when a pattern annotation is compiled without a locale\", () => {\n\tconst pattern: Pattern = [\n\t\t{\n\t\t\ttype: \"expression\",\n\t\t\targ: { type: \"variable-reference\", name: \"count\" },\n\t\t\tannotation: { type: \"function-reference\", name: \"number\", options: [] },\n\t\t},\n\t];\n\n\texpect(() =>\n\t\tcompilePattern({\n\t\t\tpattern,\n\t\t\tdeclarations: [{ type: \"input-variable\", name: \"count\" }],\n\t\t})\n\t).toThrow('requires a locale to compile the formatter \"number\"');\n});\n\ntest(\"validates relativetime options on pattern annotations\", () => {\n\tconst pattern: Pattern = [\n\t\t{\n\t\t\ttype: \"expression\",\n\t\t\targ: { type: \"variable-reference\", name: \"days\" },\n\t\t\tannotation: {\n\t\t\t\ttype: \"function-reference\",\n\t\t\t\tname: \"relativetime\",\n\t\t\t\toptions: [],\n\t\t\t},\n\t\t},\n\t];\n\n\texpect(() =>\n\t\tcompilePattern({\n\t\t\tpattern,\n\t\t\tdeclarations: [{ type: \"input-variable\", name: \"days\" }],\n\t\t\tlocale: \"en\",\n\t\t})\n\t).toThrow('The \"relativetime\" formatter requires a \"unit\" option.');\n});\n\ntest(\"parts mode compiles text, markup, options and attributes\", () => {\n\tconst pattern: Pattern = [\n\t\t{ type: \"text\", value: \"Read \" },\n\t\t{\n\t\t\ttype: \"markup-start\",\n\t\t\tname: \"link\",\n\t\t\toptions: [\n\t\t\t\t{ name: \"to\", value: { type: \"literal\", value: \"/docs\" } },\n\t\t\t\t{\n\t\t\t\t\tname: \"rel\",\n\t\t\t\t\tvalue: { type: \"variable-reference\", name: \"relationship\" },\n\t\t\t\t},\n\t\t\t],\n\t\t\tattributes: [\n\t\t\t\t{ name: \"track\", value: true },\n\t\t\t\t{ name: \"variant\", value: { type: \"literal\", value: \"hero\" } },\n\t\t\t],\n\t\t},\n\t\t{ type: \"text\", value: \"docs\" },\n\t\t{\n\t\t\ttype: \"markup-end\",\n\t\t\tname: \"link\",\n\t\t\toptions: [{ name: \"to\", value: { type: \"literal\", value: \"/docs\" } }],\n\t\t\tattributes: [{ name: \"track\", value: true }],\n\t\t},\n\t\t{\n\t\t\ttype: \"markup-standalone\",\n\t\t\tname: \"icon\",\n\t\t\toptions: [{ name: \"name\", value: { type: \"literal\", value: \"arrow\" } }],\n\t\t\tattributes: [{ name: \"filled\", value: true }],\n\t\t},\n\t];\n\n\tconst { code } = compilePattern({\n\t\tmode: \"parts\",\n\t\tpattern,\n\t\tdeclarations: [{ type: \"input-variable\", name: \"relationship\" }],\n\t});\n\n\texpect(code).toBe(\n\t\t'[{ type: \"text\", value: \"Read \" }, { type: \"markup-start\", name: \"link\", options: { \"to\": \"/docs\", \"rel\": i?.relationship }, attributes: { \"track\": true, \"variant\": \"hero\" } }, { type: \"text\", value: \"docs\" }, { type: \"markup-end\", name: \"link\", options: { \"to\": \"/docs\" }, attributes: { \"track\": true } }, { type: \"markup-standalone\", name: \"icon\", options: { \"name\": \"arrow\" }, attributes: { \"filled\": true } }]'\n\t);\n});\n"]}
@@ -81,7 +81,7 @@ export type CompilerOptions = {
81
81
  * server-side takes the globalVariable (because cookie is unavailable),
82
82
  * whereas both fallback to the base locale if not available.
83
83
  *
84
- * Custom strategies with the pattern `custom-[A-Za-z0-9]+` are supported.
84
+ * Custom strategies with the pattern `custom-[A-Za-z0-9_-]+` are supported.
85
85
  *
86
86
  * @default ["cookie", "globalVariable", "baseLocale"]
87
87
  */
@@ -1 +1 @@
1
- {"version":3,"file":"compiler-options.js","sourceRoot":"","sources":["../../src/compiler/compiler-options.ts"],"names":[],"mappings":"AAgCA,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACrC,eAAe,EAAE,iBAAiB;IAClC,aAAa,EAAE,IAAI;IACnB,2BAA2B,EAAE,IAAI;IACjC,kBAAkB,EAAE,IAAI;IACxB,UAAU,EAAE,IAAI;IAChB,kBAAkB,EAAE,KAAK;IACzB,WAAW,EAAE,IAAI;IACjB,wBAAwB,EAAE,KAAK;IAC/B,qCAAqC,EAAE,KAAK;IAC5C,eAAe,EAAE,kBAAkB;IACnC,QAAQ,EAAE,+BAA+B;IACzC,QAAQ,EAAE,CAAC,QAAQ,EAAE,gBAAgB,EAAE,YAAY,CAAC;IACpD,UAAU,EAAE,kBAAkB;IAC9B,YAAY,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG;IAChC,YAAY,EAAE,EAAE;CAC4B,CAAC","sourcesContent":["import type { Runtime } from \"./runtime/type.js\";\n\nexport type RouteStrategy =\n\t| {\n\t\t\t/**\n\t\t\t * URLPattern-compatible matcher.\n\t\t\t */\n\t\t\tmatch: string;\n\t\t\t/**\n\t\t\t * Strategy override for matching requests.\n\t\t\t */\n\t\t\tstrategy: Runtime[\"strategy\"];\n\t\t\t/**\n\t\t\t * Prevent mutually exclusive configuration (`exclude` + `strategy`).\n\t\t\t */\n\t\t\texclude?: never;\n\t }\n\t| {\n\t\t\t/**\n\t\t\t * URLPattern-compatible matcher.\n\t\t\t */\n\t\t\tmatch: string;\n\t\t\t/**\n\t\t\t * Exclude matching requests from i18n middleware behavior.\n\t\t\t */\n\t\t\texclude: true;\n\t\t\t/**\n\t\t\t * Prevent mutually exclusive configuration (`exclude` + `strategy`).\n\t\t\t */\n\t\t\tstrategy?: never;\n\t };\n\nexport const defaultCompilerOptions = {\n\toutputStructure: \"message-modules\",\n\temitGitIgnore: true,\n\tincludeEslintDisableComment: true,\n\temitPrettierIgnore: true,\n\temitReadme: true,\n\temitTsDeclarations: false,\n\tcleanOutdir: true,\n\tdisableAsyncLocalStorage: false,\n\texperimentalMiddlewareLocaleSplitting: false,\n\tlocalStorageKey: \"PARAGLIDE_LOCALE\",\n\tisServer: \"typeof window === 'undefined'\",\n\tstrategy: [\"cookie\", \"globalVariable\", \"baseLocale\"],\n\tcookieName: \"PARAGLIDE_LOCALE\",\n\tcookieMaxAge: 60 * 60 * 24 * 400,\n\tcookieDomain: \"\",\n} as const satisfies Partial<CompilerOptions>;\n\nexport type CompilerOptions = {\n\t/**\n\t * The path to the inlang project.\n\t *\n\t * @example\n\t * ```diff\n\t * await compile({\n\t * + project: \"./project.inlang\",\n\t * outdir: \"./src/paraglide\"\n\t * })\n\t * ```\n\t */\n\tproject: string;\n\t/**\n\t * The path to the output directory.\n\t *\n\t * @example\n\t * ```diff\n\t * await compile({\n\t * project: \"./project.inlang\",\n\t * + outdir: \"./src/paraglide\"\n\t * })\n\t * ```\n\t */\n\toutdir: string;\n\t/**\n\t * The strategy to use for getting the locale.\n\t *\n\t * The order of the strategy defines the precedence of matches.\n\t *\n\t * For example, in `['url', 'cookie', 'baseLocale']`, the locale will be\n\t * first tried to be detected in the url, then in a cookie, and finally\n\t * fallback to the base locale.\n\t *\n\t * The default ensures that the browser takes a cookie approach,\n\t * server-side takes the globalVariable (because cookie is unavailable),\n\t * whereas both fallback to the base locale if not available.\n\t *\n\t * Custom strategies with the pattern `custom-[A-Za-z0-9]+` are supported.\n\t *\n\t * @default [\"cookie\", \"globalVariable\", \"baseLocale\"]\n\t */\n\tstrategy?: Runtime[\"strategy\"];\n\t/**\n\t * Route-level strategy overrides.\n\t *\n\t * Routes are matched in declaration order. The first matching rule wins.\n\t * `match` uses URLPattern syntax.\n\t *\n\t * - `strategy`: Override locale resolution for the matched route.\n\t * - `exclude: true`: Skip i18n middleware behavior for the matched route.\n\t *\n\t * @example\n\t * ```ts\n\t * routeStrategies: [\n\t * { match: \"/dashboard/:path(.*)?\", strategy: [\"cookie\", \"baseLocale\"] },\n\t * { match: \"/api/:path(.*)?\", exclude: true }\n\t * ]\n\t * ```\n\t */\n\trouteStrategies?: RouteStrategy[];\n\t/**\n\t * Whether or not to use experimental middleware locale splitting.\n\t *\n\t * ⚠️ This feature is experimental and only works in SSR/SSG environment\n\t * without client-side routing. Do not rely on this feature for production.\n\t *\n\t * This feature is part of the exploration of per locale splitting. The\n\t * issue is ongoing and can be followed here [#88](https://github.com/opral/inlang-paraglide-js/issues/88).\n\t *\n\t * - The client bundle will tree-shake all messages (have close to 0kb JS).\n\t * - The server middleware will inject the used messages into the HTML.\n\t * - The client will re-trieve the messages from the injected HTML.\n\t *\n\t * @default false\n\t */\n\texperimentalMiddlewareLocaleSplitting?: boolean;\n\t/**\n\t * The name of the localStorage key to use for the localStorage strategy.\n\t *\n\t * @default 'PARAGLIDE_LOCALE'\n\t */\n\tlocalStorageKey?: string;\n\t/**\n\t * Tree-shaking flag if the code is running on the server.\n\t *\n\t * Dependent on the bundler, this flag must be adapted to\n\t * enable tree-shaking.\n\t *\n\t * @example\n\t * // vite\n\t * isServer: \"import.meta.env.SSR\"\n\t *\n\t * @default typeof window === \"undefined\"\n\t */\n\tisServer?: string;\n\t/**\n\t * Compile-time locale constant used for per-locale tree-shaking.\n\t *\n\t * This is experimental and opt-in. It should be a JavaScript expression\n\t * (not a quoted literal) that resolves to a locale or `undefined` at build time.\n\t * You can pass a string literal (e.g. `\"de\"`) for programmatic builds, or an\n\t * injected env/define expression for bundler-driven builds.\n\t *\n\t * High-level flow for per-locale builds:\n\t *\n\t * client request (/)\n\t * |\n\t * v\n\t * +----------------------+\n\t * | detect locale |\n\t * | (cookie/Accept-L) |\n\t * | pick build prefix |\n\t * +----------------------+\n\t * | if \"de\"\n\t * v\n\t * serve dist/de/... <----> serve dist/en/...\n\t * (HTML + assets) (HTML + assets)\n\t *\n\t * @see https://github.com/opral/paraglide-js/issues/88#issuecomment-3634754638\n\t *\n\t * @example\n\t * // Vite define\n\t * experimentalStaticLocale: \"typeof __PARAGLIDE_STATIC_LOCALE__ === 'undefined' ? undefined : __PARAGLIDE_STATIC_LOCALE__\"\n\t *\n\t * @example\n\t * // Programmatic compile (literal)\n\t * experimentalStaticLocale: '\"de\"'\n\t */\n\texperimentalStaticLocale?: string;\n\t/**\n\t * The name of the cookie to use for the cookie strategy.\n\t *\n\t * @default 'PARAGLIDE_LOCALE'\n\t */\n\tcookieName?: string;\n\t/**\n\t * The max-age in seconds of the cookie until it expires.\n\t *\n\t * @default 60 * 60 * 24 * 400\n\t */\n\tcookieMaxAge?: number;\n\t/**\n\t * The host to which the cookie will be sent.\n\t * If undefined or empty, the domain attribute is omitted from the cookie, scoping it to the exact current domain only (no subdomains).\n\t * If specified, the cookie will be available to the specified domain and all its subdomains.\n\t *\n\t * Use this when you need cookies to be shared across subdomains (e.g., between `app.example.com` and `api.example.com`).\n\t * The default behavior (no domain) ensures better compatibility with server-side cookies that don't specify a domain attribute.\n\t *\n\t * @example\n\t * ```ts\n\t * // Default: exact domain only (compatible with server-side cookies)\n\t * cookieDomain: undefined // Cookie: \"PARAGLIDE_LOCALE=en; path=/; max-age=34560000\"\n\t *\n\t * // Subdomain sharing: available across all subdomains\n\t * cookieDomain: \"example.com\" // Cookie: \"PARAGLIDE_LOCALE=en; path=/; max-age=34560000; domain=example.com\"\n\t * ```\n\t *\n\t * @default \"\" (no domain attribute, exact domain only)\n\t */\n\tcookieDomain?: string;\n\t/**\n\t * The `additionalFiles` option is an array of paths to additional files that should be copied to the output directory.\n\t *\n\t * @example\n\t *\n\t * ```diff\n\t * await compile({\n\t * project: \"./project.inlang\",\n\t * outdir: \"./src/paraglide\",\n\t * additionalFiles: [\n\t * + \"my-file.js\": \"console.log('hello')\"\n\t * ]\n\t * })\n\t * ```\n\t *\n\t * The output will look like this:\n\t *\n\t * ```diff\n\t * - outdir/\n\t * - messages/\n\t * + - my-file.js\n\t * - messages.js\n\t * - runtime.js\n\t * ```\n\t */\n\tadditionalFiles?: Record<string, string>;\n\t/**\n\t * If `emitPrettierIgnore` is set to `true` a `.prettierignore` file will be emitted in the output directory. Defaults to `true`.\n\t *\n\t * ```diff\n\t * - outdir/\n\t * - messages/\n\t * + - .prettierignore\n\t * - messages.js\n\t * - runtime.js\n\t * ```\n\t *\n\t * @default true\n\t */\n\temitPrettierIgnore?: boolean;\n\t/**\n\t * If `emitReadme` is set to `true` a `README.md` file will be emitted in the output directory. Defaults to `true`.\n\t *\n\t * The README helps LLMs understand the compiled output.\n\t * See https://llmstxt.org/ for format guidelines.\n\t *\n\t * ```diff\n\t * - outdir/\n\t * - messages/\n\t * + - README.md\n\t * - messages.js\n\t * - runtime.js\n\t * ```\n\t *\n\t * @default true\n\t */\n\temitReadme?: boolean;\n\t/**\n\t * Emit `.d.ts` files for the generated output using the TypeScript compiler.\n\t *\n\t * Useful when `allowJs: true` cannot be set in your `tsconfig.json`\n\t * (e.g., due to project constraints or conflicting compiler options), or when\n\t * an editor language server caches stale JSDoc types for generated messages.\n\t *\n\t * Requires `typescript` to be resolvable in your toolchain.\n\t *\n\t * **Note:** Enabling this option reduces compiler speed because TypeScript\n\t * needs to generate declaration files for all output modules.\n\t *\n\t * **Note:** With TypeScript 5/6 the declarations are generated in-process.\n\t * TypeScript 7+ no longer provides the compiler API, so its `tsc` CLI is\n\t * invoked in a child process instead; the emitted declarations are\n\t * semantically equivalent but differ cosmetically between the two.\n\t *\n\t * @example\n\t * ```ts\n\t * await compile({\n\t * project: \"./project.inlang\",\n\t * outdir: \"./src/paraglide\",\n\t * emitTsDeclarations: true,\n\t * });\n\t * ```\n\t *\n\t * @default false\n\t */\n\temitTsDeclarations?: boolean;\n\t/**\n\t * https://paraglidejs.com/strategy#url\n\t */\n\turlPatterns?: Runtime[\"urlPatterns\"];\n\t/**\n\t * Controls trailing slash canonicalization for localized URLs.\n\t *\n\t * Canonicalization happens before URL pattern matching and after URL\n\t * localization. If omitted, Paraglide keeps its existing trailing slash\n\t * behavior.\n\t */\n\ttrailingSlash?: \"always\" | \"never\";\n\t/**\n\t * Whether to include an eslint-disable comment at the top of each .js file.\n\t *\n\t * @default true\n\t */\n\tincludeEslintDisableComment?: boolean;\n\t/**\n\t * Replaces AsyncLocalStorage with a synchronous implementation.\n\t *\n\t * Leave AsyncLocalStorage enabled by default. This option is a\n\t * compatibility fallback for runtimes that do not provide\n\t * AsyncLocalStorage or `node:async_hooks`.\n\t *\n\t * ⚠️ WARNING: Only use this option when your runtime also guarantees\n\t * request isolation. Disabling AsyncLocalStorage in multi-request\n\t * server environments risks cross-request pollution where state from\n\t * one request could leak into another concurrent request.\n\t */\n\tdisableAsyncLocalStorage?: boolean;\n\t/**\n\t * If `emitGitIgnore` is set to `true` a `.gitignore` file will be emitted in the output directory. Defaults to `true`.\n\t *\n\t * ```diff\n\t * - outdir/\n\t * - messages/\n\t * + - .gitignore\n\t * - messages.js\n\t * - runtime.js\n\t * ```\n\t *\n\t * @default true\n\t */\n\temitGitIgnore?: boolean;\n\t/**\n\t * The `outputStructure` defines how modules are structured in the output.\n\t *\n\t * - `message-modules` - Each module is a message. This is the default.\n\t * - `locale-modules` - Each module is a locale.\n\t *\n\t * It is recommended to use `locale-modules` for development and `message-modules` for production.\n\t * Bundlers speed up the dev mode by bypassing bundling which can lead to many http requests\n\t * during the dev mode with `message-modules`. See https://github.com/opral/inlang-paraglide-js/issues/486.\n\t *\n\t * **`message-modules`**\n\t *\n\t * Messages have their own module which eases tree-shaking for bundlers.\n\t *\n\t * ```diff\n\t * - outdir/\n\t * - messages/\n\t * + - blue_elephant_tree/\n\t * + - index.js\n\t * + - en.js\n\t * + - fr.js\n\t * + - sky_phone_bottle/\n\t * + - index.js\n\t * + - en.js\n\t * + - fr.js\n\t * - ...\n\t * - messages.js\n\t * - runtime.js\n\t * ```\n\t *\n\t * **`locale-modules`**\n\t *\n\t * Messages are bundled in a per locale module. Bundlers often struggle with tree-shaking this structure,\n\t * which can lead to more inefficient tree-shaking and larger bundle sizes compared to `message-modules`.\n\t *\n\t * The benefit are substantially fewer files which is needed in large projects.\n\t *\n\t * ```diff\n\t * - outdir/\n\t * - messages/\n\t * + - de.js\n\t * + - en.js\n\t * + - fr.js\n\t * - ...\n\t * - messages.js\n\t * - runtime.js\n\t * ```\n\t *\n\t * @default \"message-modules\"\n\t */\n\toutputStructure?: \"locale-modules\" | \"message-modules\";\n\t/**\n\t * Whether to clean the output directory before writing the new files.\n\t *\n\t * @default true\n\t */\n\tcleanOutdir?: boolean;\n\n\t/**\n\t * The file system to use. Defaults to `await import('node:fs')`.\n\t *\n\t * Useful for testing the paraglide compiler by mocking the fs.\n\t */\n\tfs?: any;\n};\n"]}
1
+ {"version":3,"file":"compiler-options.js","sourceRoot":"","sources":["../../src/compiler/compiler-options.ts"],"names":[],"mappings":"AAgCA,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACrC,eAAe,EAAE,iBAAiB;IAClC,aAAa,EAAE,IAAI;IACnB,2BAA2B,EAAE,IAAI;IACjC,kBAAkB,EAAE,IAAI;IACxB,UAAU,EAAE,IAAI;IAChB,kBAAkB,EAAE,KAAK;IACzB,WAAW,EAAE,IAAI;IACjB,wBAAwB,EAAE,KAAK;IAC/B,qCAAqC,EAAE,KAAK;IAC5C,eAAe,EAAE,kBAAkB;IACnC,QAAQ,EAAE,+BAA+B;IACzC,QAAQ,EAAE,CAAC,QAAQ,EAAE,gBAAgB,EAAE,YAAY,CAAC;IACpD,UAAU,EAAE,kBAAkB;IAC9B,YAAY,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG;IAChC,YAAY,EAAE,EAAE;CAC4B,CAAC","sourcesContent":["import type { Runtime } from \"./runtime/type.js\";\n\nexport type RouteStrategy =\n\t| {\n\t\t\t/**\n\t\t\t * URLPattern-compatible matcher.\n\t\t\t */\n\t\t\tmatch: string;\n\t\t\t/**\n\t\t\t * Strategy override for matching requests.\n\t\t\t */\n\t\t\tstrategy: Runtime[\"strategy\"];\n\t\t\t/**\n\t\t\t * Prevent mutually exclusive configuration (`exclude` + `strategy`).\n\t\t\t */\n\t\t\texclude?: never;\n\t }\n\t| {\n\t\t\t/**\n\t\t\t * URLPattern-compatible matcher.\n\t\t\t */\n\t\t\tmatch: string;\n\t\t\t/**\n\t\t\t * Exclude matching requests from i18n middleware behavior.\n\t\t\t */\n\t\t\texclude: true;\n\t\t\t/**\n\t\t\t * Prevent mutually exclusive configuration (`exclude` + `strategy`).\n\t\t\t */\n\t\t\tstrategy?: never;\n\t };\n\nexport const defaultCompilerOptions = {\n\toutputStructure: \"message-modules\",\n\temitGitIgnore: true,\n\tincludeEslintDisableComment: true,\n\temitPrettierIgnore: true,\n\temitReadme: true,\n\temitTsDeclarations: false,\n\tcleanOutdir: true,\n\tdisableAsyncLocalStorage: false,\n\texperimentalMiddlewareLocaleSplitting: false,\n\tlocalStorageKey: \"PARAGLIDE_LOCALE\",\n\tisServer: \"typeof window === 'undefined'\",\n\tstrategy: [\"cookie\", \"globalVariable\", \"baseLocale\"],\n\tcookieName: \"PARAGLIDE_LOCALE\",\n\tcookieMaxAge: 60 * 60 * 24 * 400,\n\tcookieDomain: \"\",\n} as const satisfies Partial<CompilerOptions>;\n\nexport type CompilerOptions = {\n\t/**\n\t * The path to the inlang project.\n\t *\n\t * @example\n\t * ```diff\n\t * await compile({\n\t * + project: \"./project.inlang\",\n\t * outdir: \"./src/paraglide\"\n\t * })\n\t * ```\n\t */\n\tproject: string;\n\t/**\n\t * The path to the output directory.\n\t *\n\t * @example\n\t * ```diff\n\t * await compile({\n\t * project: \"./project.inlang\",\n\t * + outdir: \"./src/paraglide\"\n\t * })\n\t * ```\n\t */\n\toutdir: string;\n\t/**\n\t * The strategy to use for getting the locale.\n\t *\n\t * The order of the strategy defines the precedence of matches.\n\t *\n\t * For example, in `['url', 'cookie', 'baseLocale']`, the locale will be\n\t * first tried to be detected in the url, then in a cookie, and finally\n\t * fallback to the base locale.\n\t *\n\t * The default ensures that the browser takes a cookie approach,\n\t * server-side takes the globalVariable (because cookie is unavailable),\n\t * whereas both fallback to the base locale if not available.\n\t *\n\t * Custom strategies with the pattern `custom-[A-Za-z0-9_-]+` are supported.\n\t *\n\t * @default [\"cookie\", \"globalVariable\", \"baseLocale\"]\n\t */\n\tstrategy?: Runtime[\"strategy\"];\n\t/**\n\t * Route-level strategy overrides.\n\t *\n\t * Routes are matched in declaration order. The first matching rule wins.\n\t * `match` uses URLPattern syntax.\n\t *\n\t * - `strategy`: Override locale resolution for the matched route.\n\t * - `exclude: true`: Skip i18n middleware behavior for the matched route.\n\t *\n\t * @example\n\t * ```ts\n\t * routeStrategies: [\n\t * { match: \"/dashboard/:path(.*)?\", strategy: [\"cookie\", \"baseLocale\"] },\n\t * { match: \"/api/:path(.*)?\", exclude: true }\n\t * ]\n\t * ```\n\t */\n\trouteStrategies?: RouteStrategy[];\n\t/**\n\t * Whether or not to use experimental middleware locale splitting.\n\t *\n\t * ⚠️ This feature is experimental and only works in SSR/SSG environment\n\t * without client-side routing. Do not rely on this feature for production.\n\t *\n\t * This feature is part of the exploration of per locale splitting. The\n\t * issue is ongoing and can be followed here [#88](https://github.com/opral/inlang-paraglide-js/issues/88).\n\t *\n\t * - The client bundle will tree-shake all messages (have close to 0kb JS).\n\t * - The server middleware will inject the used messages into the HTML.\n\t * - The client will re-trieve the messages from the injected HTML.\n\t *\n\t * @default false\n\t */\n\texperimentalMiddlewareLocaleSplitting?: boolean;\n\t/**\n\t * The name of the localStorage key to use for the localStorage strategy.\n\t *\n\t * @default 'PARAGLIDE_LOCALE'\n\t */\n\tlocalStorageKey?: string;\n\t/**\n\t * Tree-shaking flag if the code is running on the server.\n\t *\n\t * Dependent on the bundler, this flag must be adapted to\n\t * enable tree-shaking.\n\t *\n\t * @example\n\t * // vite\n\t * isServer: \"import.meta.env.SSR\"\n\t *\n\t * @default typeof window === \"undefined\"\n\t */\n\tisServer?: string;\n\t/**\n\t * Compile-time locale constant used for per-locale tree-shaking.\n\t *\n\t * This is experimental and opt-in. It should be a JavaScript expression\n\t * (not a quoted literal) that resolves to a locale or `undefined` at build time.\n\t * You can pass a string literal (e.g. `\"de\"`) for programmatic builds, or an\n\t * injected env/define expression for bundler-driven builds.\n\t *\n\t * High-level flow for per-locale builds:\n\t *\n\t * client request (/)\n\t * |\n\t * v\n\t * +----------------------+\n\t * | detect locale |\n\t * | (cookie/Accept-L) |\n\t * | pick build prefix |\n\t * +----------------------+\n\t * | if \"de\"\n\t * v\n\t * serve dist/de/... <----> serve dist/en/...\n\t * (HTML + assets) (HTML + assets)\n\t *\n\t * @see https://github.com/opral/paraglide-js/issues/88#issuecomment-3634754638\n\t *\n\t * @example\n\t * // Vite define\n\t * experimentalStaticLocale: \"typeof __PARAGLIDE_STATIC_LOCALE__ === 'undefined' ? undefined : __PARAGLIDE_STATIC_LOCALE__\"\n\t *\n\t * @example\n\t * // Programmatic compile (literal)\n\t * experimentalStaticLocale: '\"de\"'\n\t */\n\texperimentalStaticLocale?: string;\n\t/**\n\t * The name of the cookie to use for the cookie strategy.\n\t *\n\t * @default 'PARAGLIDE_LOCALE'\n\t */\n\tcookieName?: string;\n\t/**\n\t * The max-age in seconds of the cookie until it expires.\n\t *\n\t * @default 60 * 60 * 24 * 400\n\t */\n\tcookieMaxAge?: number;\n\t/**\n\t * The host to which the cookie will be sent.\n\t * If undefined or empty, the domain attribute is omitted from the cookie, scoping it to the exact current domain only (no subdomains).\n\t * If specified, the cookie will be available to the specified domain and all its subdomains.\n\t *\n\t * Use this when you need cookies to be shared across subdomains (e.g., between `app.example.com` and `api.example.com`).\n\t * The default behavior (no domain) ensures better compatibility with server-side cookies that don't specify a domain attribute.\n\t *\n\t * @example\n\t * ```ts\n\t * // Default: exact domain only (compatible with server-side cookies)\n\t * cookieDomain: undefined // Cookie: \"PARAGLIDE_LOCALE=en; path=/; max-age=34560000\"\n\t *\n\t * // Subdomain sharing: available across all subdomains\n\t * cookieDomain: \"example.com\" // Cookie: \"PARAGLIDE_LOCALE=en; path=/; max-age=34560000; domain=example.com\"\n\t * ```\n\t *\n\t * @default \"\" (no domain attribute, exact domain only)\n\t */\n\tcookieDomain?: string;\n\t/**\n\t * The `additionalFiles` option is an array of paths to additional files that should be copied to the output directory.\n\t *\n\t * @example\n\t *\n\t * ```diff\n\t * await compile({\n\t * project: \"./project.inlang\",\n\t * outdir: \"./src/paraglide\",\n\t * additionalFiles: [\n\t * + \"my-file.js\": \"console.log('hello')\"\n\t * ]\n\t * })\n\t * ```\n\t *\n\t * The output will look like this:\n\t *\n\t * ```diff\n\t * - outdir/\n\t * - messages/\n\t * + - my-file.js\n\t * - messages.js\n\t * - runtime.js\n\t * ```\n\t */\n\tadditionalFiles?: Record<string, string>;\n\t/**\n\t * If `emitPrettierIgnore` is set to `true` a `.prettierignore` file will be emitted in the output directory. Defaults to `true`.\n\t *\n\t * ```diff\n\t * - outdir/\n\t * - messages/\n\t * + - .prettierignore\n\t * - messages.js\n\t * - runtime.js\n\t * ```\n\t *\n\t * @default true\n\t */\n\temitPrettierIgnore?: boolean;\n\t/**\n\t * If `emitReadme` is set to `true` a `README.md` file will be emitted in the output directory. Defaults to `true`.\n\t *\n\t * The README helps LLMs understand the compiled output.\n\t * See https://llmstxt.org/ for format guidelines.\n\t *\n\t * ```diff\n\t * - outdir/\n\t * - messages/\n\t * + - README.md\n\t * - messages.js\n\t * - runtime.js\n\t * ```\n\t *\n\t * @default true\n\t */\n\temitReadme?: boolean;\n\t/**\n\t * Emit `.d.ts` files for the generated output using the TypeScript compiler.\n\t *\n\t * Useful when `allowJs: true` cannot be set in your `tsconfig.json`\n\t * (e.g., due to project constraints or conflicting compiler options), or when\n\t * an editor language server caches stale JSDoc types for generated messages.\n\t *\n\t * Requires `typescript` to be resolvable in your toolchain.\n\t *\n\t * **Note:** Enabling this option reduces compiler speed because TypeScript\n\t * needs to generate declaration files for all output modules.\n\t *\n\t * **Note:** With TypeScript 5/6 the declarations are generated in-process.\n\t * TypeScript 7+ no longer provides the compiler API, so its `tsc` CLI is\n\t * invoked in a child process instead; the emitted declarations are\n\t * semantically equivalent but differ cosmetically between the two.\n\t *\n\t * @example\n\t * ```ts\n\t * await compile({\n\t * project: \"./project.inlang\",\n\t * outdir: \"./src/paraglide\",\n\t * emitTsDeclarations: true,\n\t * });\n\t * ```\n\t *\n\t * @default false\n\t */\n\temitTsDeclarations?: boolean;\n\t/**\n\t * https://paraglidejs.com/strategy#url\n\t */\n\turlPatterns?: Runtime[\"urlPatterns\"];\n\t/**\n\t * Controls trailing slash canonicalization for localized URLs.\n\t *\n\t * Canonicalization happens before URL pattern matching and after URL\n\t * localization. If omitted, Paraglide keeps its existing trailing slash\n\t * behavior.\n\t */\n\ttrailingSlash?: \"always\" | \"never\";\n\t/**\n\t * Whether to include an eslint-disable comment at the top of each .js file.\n\t *\n\t * @default true\n\t */\n\tincludeEslintDisableComment?: boolean;\n\t/**\n\t * Replaces AsyncLocalStorage with a synchronous implementation.\n\t *\n\t * Leave AsyncLocalStorage enabled by default. This option is a\n\t * compatibility fallback for runtimes that do not provide\n\t * AsyncLocalStorage or `node:async_hooks`.\n\t *\n\t * ⚠️ WARNING: Only use this option when your runtime also guarantees\n\t * request isolation. Disabling AsyncLocalStorage in multi-request\n\t * server environments risks cross-request pollution where state from\n\t * one request could leak into another concurrent request.\n\t */\n\tdisableAsyncLocalStorage?: boolean;\n\t/**\n\t * If `emitGitIgnore` is set to `true` a `.gitignore` file will be emitted in the output directory. Defaults to `true`.\n\t *\n\t * ```diff\n\t * - outdir/\n\t * - messages/\n\t * + - .gitignore\n\t * - messages.js\n\t * - runtime.js\n\t * ```\n\t *\n\t * @default true\n\t */\n\temitGitIgnore?: boolean;\n\t/**\n\t * The `outputStructure` defines how modules are structured in the output.\n\t *\n\t * - `message-modules` - Each module is a message. This is the default.\n\t * - `locale-modules` - Each module is a locale.\n\t *\n\t * It is recommended to use `locale-modules` for development and `message-modules` for production.\n\t * Bundlers speed up the dev mode by bypassing bundling which can lead to many http requests\n\t * during the dev mode with `message-modules`. See https://github.com/opral/inlang-paraglide-js/issues/486.\n\t *\n\t * **`message-modules`**\n\t *\n\t * Messages have their own module which eases tree-shaking for bundlers.\n\t *\n\t * ```diff\n\t * - outdir/\n\t * - messages/\n\t * + - blue_elephant_tree/\n\t * + - index.js\n\t * + - en.js\n\t * + - fr.js\n\t * + - sky_phone_bottle/\n\t * + - index.js\n\t * + - en.js\n\t * + - fr.js\n\t * - ...\n\t * - messages.js\n\t * - runtime.js\n\t * ```\n\t *\n\t * **`locale-modules`**\n\t *\n\t * Messages are bundled in a per locale module. Bundlers often struggle with tree-shaking this structure,\n\t * which can lead to more inefficient tree-shaking and larger bundle sizes compared to `message-modules`.\n\t *\n\t * The benefit are substantially fewer files which is needed in large projects.\n\t *\n\t * ```diff\n\t * - outdir/\n\t * - messages/\n\t * + - de.js\n\t * + - en.js\n\t * + - fr.js\n\t * - ...\n\t * - messages.js\n\t * - runtime.js\n\t * ```\n\t *\n\t * @default \"message-modules\"\n\t */\n\toutputStructure?: \"locale-modules\" | \"message-modules\";\n\t/**\n\t * Whether to clean the output directory before writing the new files.\n\t *\n\t * @default true\n\t */\n\tcleanOutdir?: boolean;\n\n\t/**\n\t * The file system to use. Defaults to `await import('node:fs')`.\n\t *\n\t * Useful for testing the paraglide compiler by mocking the fs.\n\t */\n\tfs?: any;\n};\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
- code = code.replace("const compiledBundles = {};", `const compiledBundles = ${JSON.stringify(createCompiledMessagesObject(args.compiledBundles))};`);
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
- if (result[bundleId] === undefined) {
60
- result[bundleId] = {};
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[bundleId][locale] = compiledMessage.code
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":"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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(\"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\tcode = code.replace(\n\t\t\t\"const compiledBundles = {};\",\n\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\tif (result[bundleId] === undefined) {\n\t\t\tresult[bundleId] = {};\n\t\t}\n\t\tfor (const [locale, compiledMessage] of Object.entries(\n\t\t\tcompiledBundle.messages\n\t\t)) {\n\t\t\tresult[bundleId][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"]}
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(\"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;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,MAAM,oBAAoB,GAAa,EAAE,CAAC;AAE1C,SAAS,CAAC,KAAK,IAAI,EAAE;IACpB,MAAM,OAAO,CAAC,GAAG,CAChB,oBAAoB;SAClB,MAAM,CAAC,CAAC,CAAC;SACT,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CACrE,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,gGAAgG,EAAE,KAAK,IAAI,EAAE;IACjH,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,mBAAmB,CAAC,CAAC,CAAC;IACrE,oBAAoB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAErC,MAAM,UAAU,GAAG,gBAAgB,CAAC;QACnC,eAAe,EAAE,EAAE;QACnB,eAAe,EAAE;YAChB,wBAAwB,EAAE,KAAK;YAC/B,qCAAqC,EAAE,KAAK;SAC5C;KACD,CAAC,CAAC,OAAO,CAAC,gBAAgB,EAAE,yBAAyB,CAAC,CAAC;IAExD,MAAM,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,EAAE,UAAU,CAAC,CAAC;IAC3D,MAAM,SAAS,CACd,IAAI,CAAC,SAAS,EAAE,kBAAkB,CAAC,EACnC;;;;;;;;;;;EAWA,CACA,CAAC;IACF,MAAM,SAAS,CACd,IAAI,CAAC,SAAS,EAAE,qBAAqB,CAAC,EACtC;;;;;;4EAM0E,CAC1E,CAAC;IAEF,MAAM,MAAM,GAAG,MAAM,MAAM,CAC1B,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC,IAAI,CACjD,CAAC;IACF,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAChD,IAAI,OAAO,CAAC,+BAA+B,CAAC,EAC5C,GAAG,EAAE,CAAC,IAAI,QAAQ,CAAC,OAAO,CAAC,CAC3B,CAAC;IAEF,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAC7C,CAAC,CAAC,CAAC","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 { 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"]}
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"]}
@@ -1,3 +1,5 @@
1
+ import type { Declaration } from "@inlang/sdk";
1
2
  export declare function isValidIdentifier(name: string): boolean;
2
3
  export declare function quotePropertyKey(name: string): string;
3
4
  export declare function compileInputAccess(name: string): string;
5
+ export declare function compileVariableAccess(name: string, declarations?: Declaration[]): string;
@@ -12,4 +12,10 @@ export function compileInputAccess(name) {
12
12
  }
13
13
  return `i?.[${quotePropertyKey(name)}]`;
14
14
  }
15
+ export function compileVariableAccess(name, declarations) {
16
+ const declaration = declarations?.find((candidate) => candidate.name === name);
17
+ return declaration?.type === "local-variable"
18
+ ? name
19
+ : compileInputAccess(name);
20
+ }
15
21
  //# sourceMappingURL=variable-access.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"variable-access.js","sourceRoot":"","sources":["../../src/compiler/variable-access.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAE3E,MAAM,iBAAiB,GAAG,4BAA4B,CAAC;AAEvD,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC7C,OAAO,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC5C,OAAO,IAAI,0BAA0B,CAAC,IAAI,CAAC,GAAG,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC9C,IAAI,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7B,OAAO,MAAM,IAAI,EAAE,CAAC;IACrB,CAAC;IACD,OAAO,OAAO,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC;AACzC,CAAC","sourcesContent":["import { escapeForDoubleQuoteString } from \"../services/codegen/escape.js\";\n\nconst identifierPattern = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\n\nexport function isValidIdentifier(name: string): boolean {\n\treturn identifierPattern.test(name);\n}\n\nexport function quotePropertyKey(name: string): string {\n\treturn `\"${escapeForDoubleQuoteString(name)}\"`;\n}\n\nexport function compileInputAccess(name: string): string {\n\tif (isValidIdentifier(name)) {\n\t\treturn `i?.${name}`;\n\t}\n\treturn `i?.[${quotePropertyKey(name)}]`;\n}\n"]}
1
+ {"version":3,"file":"variable-access.js","sourceRoot":"","sources":["../../src/compiler/variable-access.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAE3E,MAAM,iBAAiB,GAAG,4BAA4B,CAAC;AAEvD,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC7C,OAAO,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC5C,OAAO,IAAI,0BAA0B,CAAC,IAAI,CAAC,GAAG,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC9C,IAAI,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7B,OAAO,MAAM,IAAI,EAAE,CAAC;IACrB,CAAC;IACD,OAAO,OAAO,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,qBAAqB,CACpC,IAAY,EACZ,YAA4B;IAE5B,MAAM,WAAW,GAAG,YAAY,EAAE,IAAI,CACrC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,IAAI,CACtC,CAAC;IACF,OAAO,WAAW,EAAE,IAAI,KAAK,gBAAgB;QAC5C,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC","sourcesContent":["import type { Declaration } from \"@inlang/sdk\";\nimport { escapeForDoubleQuoteString } from \"../services/codegen/escape.js\";\n\nconst identifierPattern = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\n\nexport function isValidIdentifier(name: string): boolean {\n\treturn identifierPattern.test(name);\n}\n\nexport function quotePropertyKey(name: string): string {\n\treturn `\"${escapeForDoubleQuoteString(name)}\"`;\n}\n\nexport function compileInputAccess(name: string): string {\n\tif (isValidIdentifier(name)) {\n\t\treturn `i?.${name}`;\n\t}\n\treturn `i?.[${quotePropertyKey(name)}]`;\n}\n\nexport function compileVariableAccess(\n\tname: string,\n\tdeclarations?: Declaration[]\n): string {\n\tconst declaration = declarations?.find(\n\t\t(candidate) => candidate.name === name\n\t);\n\treturn declaration?.type === \"local-variable\"\n\t\t? name\n\t\t: compileInputAccess(name);\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from "./bundler-plugins/index.js";
2
2
  export * from "./compiler/index.js";
3
+ export { defineConfig, type ParaglideConfig } from "./services/config/index.js";
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from "./bundler-plugins/index.js";
2
2
  export * from "./compiler/index.js";
3
+ export { defineConfig } from "./services/config/index.js";
3
4
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC","sourcesContent":["export * from \"./bundler-plugins/index.js\";\nexport * from \"./compiler/index.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,OAAO,EAAE,YAAY,EAAwB,MAAM,4BAA4B,CAAC","sourcesContent":["export * from \"./bundler-plugins/index.js\";\nexport * from \"./compiler/index.js\";\nexport { defineConfig, type ParaglideConfig } from \"./services/config/index.js\";\n"]}
@@ -0,0 +1,69 @@
1
+ import * as v from "valibot";
2
+ import type { CompilerOptions } from "../../compiler/compiler-options.js";
3
+ import type { Runtime } from "../../compiler/runtime/type.js";
4
+ /**
5
+ * The schema of a `paraglide.config.js/ts` file inside the project
6
+ * directory.
7
+ *
8
+ * The config cannot specify `project` — it lives inside the inlang project
9
+ * directory and therefore cannot name it. Values that are not provided
10
+ * fall back to the bundler plugin options / command line flags and
11
+ * ultimately to the built-in defaults.
12
+ */
13
+ export type ParaglideConfig = Partial<Omit<CompilerOptions, "fs" | "project">>;
14
+ /**
15
+ * Strategy names that are built into the runtime. Custom strategies are
16
+ * prefixed with `custom-` and are matched separately.
17
+ */
18
+ declare const BUILTIN_STRATEGY_NAMES: readonly ["cookie", "baseLocale", "globalVariable", "url", "preferredLanguage", "localStorage"];
19
+ type UncoveredStrategies = Exclude<Runtime["strategy"][number], (typeof BUILTIN_STRATEGY_NAMES)[number] | `custom-${string}`>;
20
+ export type AllStrategiesCovered = [UncoveredStrategies] extends [never] ? true : never;
21
+ export declare const allStrategiesCovered: AllStrategiesCovered;
22
+ export declare const strategyNameSchema: v.UnionSchema<[v.PicklistSchema<readonly ["cookie", "baseLocale", "globalVariable", "url", "preferredLanguage", "localStorage"], undefined>, v.BaseSchema<string, `custom-${string}`, v.RegexIssue<string> | v.StringIssue>], undefined>;
23
+ /**
24
+ * The valibot schema behind {@link ParaglideConfig}.
25
+ *
26
+ * Every option is optional — values that are not provided fall back to
27
+ * the bundler plugin options / command line flags and ultimately to
28
+ * `defaultCompilerOptions`.
29
+ */
30
+ export declare const paraglideConfigSchema: v.ObjectSchema<{
31
+ readonly outdir: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.NonEmptyAction<string, "\"outdir\" must not be empty">]>, undefined>;
32
+ readonly strategy: v.OptionalSchema<v.ArraySchema<v.UnionSchema<[v.PicklistSchema<readonly ["cookie", "baseLocale", "globalVariable", "url", "preferredLanguage", "localStorage"], undefined>, v.BaseSchema<string, `custom-${string}`, v.RegexIssue<string> | v.StringIssue>], undefined>, undefined>, undefined>;
33
+ readonly routeStrategies: v.OptionalSchema<v.ArraySchema<v.UnionSchema<[v.ObjectSchema<{
34
+ readonly match: v.StringSchema<undefined>;
35
+ readonly strategy: v.ArraySchema<v.UnionSchema<[v.PicklistSchema<readonly ["cookie", "baseLocale", "globalVariable", "url", "preferredLanguage", "localStorage"], undefined>, v.BaseSchema<string, `custom-${string}`, v.RegexIssue<string> | v.StringIssue>], undefined>, undefined>;
36
+ readonly exclude: v.OptionalSchema<v.NeverSchema<undefined>, undefined>;
37
+ }, undefined>, v.ObjectSchema<{
38
+ readonly match: v.StringSchema<undefined>;
39
+ readonly exclude: v.LiteralSchema<true, undefined>;
40
+ readonly strategy: v.OptionalSchema<v.NeverSchema<undefined>, undefined>;
41
+ }, undefined>], undefined>, undefined>, undefined>;
42
+ readonly experimentalMiddlewareLocaleSplitting: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
43
+ readonly localStorageKey: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
44
+ readonly isServer: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
45
+ readonly experimentalStaticLocale: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
46
+ readonly cookieName: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
47
+ readonly cookieMaxAge: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
48
+ readonly cookieDomain: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
49
+ readonly additionalFiles: v.OptionalSchema<v.RecordSchema<v.StringSchema<undefined>, v.StringSchema<undefined>, undefined>, undefined>;
50
+ readonly emitPrettierIgnore: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
51
+ readonly emitReadme: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
52
+ readonly emitTsDeclarations: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
53
+ readonly urlPatterns: v.OptionalSchema<v.ArraySchema<v.ObjectSchema<{
54
+ readonly pattern: v.StringSchema<undefined>;
55
+ readonly localized: v.ArraySchema<v.TupleSchema<[v.StringSchema<undefined>, v.StringSchema<undefined>], undefined>, undefined>;
56
+ }, undefined>, undefined>, undefined>;
57
+ readonly trailingSlash: v.OptionalSchema<v.PicklistSchema<["always", "never"], undefined>, undefined>;
58
+ readonly includeEslintDisableComment: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
59
+ readonly disableAsyncLocalStorage: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
60
+ readonly emitGitIgnore: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
61
+ readonly outputStructure: v.OptionalSchema<v.PicklistSchema<["message-modules", "locale-modules"], undefined>, undefined>;
62
+ readonly cleanOutdir: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
63
+ }, undefined>;
64
+ type SchemaOutput = v.InferOutput<typeof paraglideConfigSchema>;
65
+ export type SchemaMatchesCompilerOptions = [keyof ParaglideConfig] extends [
66
+ keyof SchemaOutput
67
+ ] ? [keyof SchemaOutput] extends [keyof ParaglideConfig] ? SchemaOutput extends ParaglideConfig ? ParaglideConfig extends SchemaOutput ? true : never : never : never : never;
68
+ export declare const schemaMatchesCompilerOptions: SchemaMatchesCompilerOptions;
69
+ export {};