@inlang/paraglide-js 2.25.3 → 2.26.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.
- package/dist/compiler/runtime/create-runtime.js +24 -1
- package/dist/compiler/runtime/create-runtime.js.map +1 -1
- package/dist/compiler/runtime/extract-locale-from-url.js +11 -4
- package/dist/compiler/runtime/extract-locale-from-url.js.map +1 -1
- package/dist/compiler/runtime/generate-static-localized-urls.js +5 -3
- package/dist/compiler/runtime/generate-static-localized-urls.js.map +1 -1
- package/dist/compiler/runtime/localize-href.test.js +37 -0
- package/dist/compiler/runtime/localize-href.test.js.map +1 -1
- package/dist/compiler/runtime/localize-url.js +29 -56
- package/dist/compiler/runtime/localize-url.js.map +1 -1
- package/dist/compiler/runtime/localize-url.test.js +15 -0
- package/dist/compiler/runtime/localize-url.test.js.map +1 -1
- package/dist/compiler/runtime/route-strategy.js +9 -3
- package/dist/compiler/runtime/route-strategy.js.map +1 -1
- package/dist/compiler/runtime/type.test.js +9 -0
- package/dist/compiler/runtime/type.test.js.map +1 -1
- package/dist/compiler/runtime/url-pattern-cache.d.ts +11 -0
- package/dist/compiler/runtime/url-pattern-cache.js +42 -0
- package/dist/compiler/runtime/url-pattern-cache.js.map +1 -0
- package/dist/compiler/runtime/url-pattern-index.d.ts +24 -0
- package/dist/compiler/runtime/url-pattern-index.differential.test.d.ts +1 -0
- package/dist/compiler/runtime/url-pattern-index.differential.test.js +152 -0
- package/dist/compiler/runtime/url-pattern-index.differential.test.js.map +1 -0
- package/dist/compiler/runtime/url-pattern-index.js +168 -0
- package/dist/compiler/runtime/url-pattern-index.js.map +1 -0
- package/dist/compiler/runtime/url-pattern-index.test.d.ts +1 -0
- package/dist/compiler/runtime/url-pattern-index.test.js +262 -0
- package/dist/compiler/runtime/url-pattern-index.test.js.map +1 -0
- package/dist/compiler/server/create-server-file.js +1 -1
- package/dist/compiler/server/create-server-file.js.map +1 -1
- package/dist/compiler/server/create-server-file.test.js +12 -0
- package/dist/compiler/server/create-server-file.test.js.map +1 -1
- package/dist/services/env-variables/index.js +1 -1
- package/dist/services/env-variables/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { expect, test } from "vitest";
|
|
2
|
+
import { URLPattern as PolyfillURLPattern } from "urlpattern-polyfill";
|
|
3
|
+
import { createParaglide } from "../create-paraglide.js";
|
|
4
|
+
import { newProject } from "@inlang/sdk";
|
|
5
|
+
const nativeURLPattern = Reflect.get(globalThis, "URLPattern");
|
|
6
|
+
const engines = [];
|
|
7
|
+
let runtimeModuleNonce = 0;
|
|
8
|
+
if (typeof nativeURLPattern === "function" &&
|
|
9
|
+
nativeURLPattern !== PolyfillURLPattern) {
|
|
10
|
+
engines.push(["native", nativeURLPattern]);
|
|
11
|
+
}
|
|
12
|
+
engines.push(["polyfill", PolyfillURLPattern]);
|
|
13
|
+
async function withURLPattern(ctor, fn) {
|
|
14
|
+
const descriptor = Object.getOwnPropertyDescriptor(globalThis, "URLPattern");
|
|
15
|
+
Object.defineProperty(globalThis, "URLPattern", {
|
|
16
|
+
configurable: true,
|
|
17
|
+
writable: true,
|
|
18
|
+
value: ctor,
|
|
19
|
+
});
|
|
20
|
+
try {
|
|
21
|
+
return await fn();
|
|
22
|
+
}
|
|
23
|
+
finally {
|
|
24
|
+
if (descriptor) {
|
|
25
|
+
Object.defineProperty(globalThis, "URLPattern", descriptor);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
Reflect.deleteProperty(globalThis, "URLPattern");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async function withRuntime(options, ctor, fn) {
|
|
33
|
+
return withURLPattern(ctor, async () => fn(await createParaglide({
|
|
34
|
+
blob: await newProject({
|
|
35
|
+
settings: { baseLocale: "en", locales: ["en", "de"] },
|
|
36
|
+
}),
|
|
37
|
+
cookieName: `pattern-index-test-${runtimeModuleNonce++}`,
|
|
38
|
+
strategy: ["url", "baseLocale"],
|
|
39
|
+
...options,
|
|
40
|
+
})));
|
|
41
|
+
}
|
|
42
|
+
for (const [engineName, ctor] of engines) {
|
|
43
|
+
test(`${engineName} URLPattern keeps ordered routing and URL parts`, async () => {
|
|
44
|
+
await withRuntime({
|
|
45
|
+
urlPatterns: [
|
|
46
|
+
{
|
|
47
|
+
pattern: "/specific-path",
|
|
48
|
+
localized: [
|
|
49
|
+
["en", "/specific-path"],
|
|
50
|
+
["de", "/de/404"],
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
pattern: "/:path(.*)?",
|
|
55
|
+
localized: [
|
|
56
|
+
["en", "/:path(.*)?"],
|
|
57
|
+
["de", "/de/:path(.*)?"],
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
routeStrategies: [
|
|
62
|
+
{ match: "/specific-path", exclude: true },
|
|
63
|
+
{ match: "/:path(.*)?", strategy: ["url"] },
|
|
64
|
+
],
|
|
65
|
+
}, ctor, async (runtime) => {
|
|
66
|
+
const input = "https://alice:secret@example.com/specific-path?redirect=%2Faccount#section";
|
|
67
|
+
expect(runtime.localizeUrl(input, { locale: "de" }).href).toBe("https://alice:secret@example.com/de/404?redirect=%2Faccount#section");
|
|
68
|
+
expect(runtime.deLocalizeUrl("https://alice:secret@example.com/de/404?redirect=%2Faccount#section").href).toBe("https://alice:secret@example.com/specific-path?redirect=%2Faccount#section");
|
|
69
|
+
expect(runtime.extractLocaleFromUrl(input)).toBe("en");
|
|
70
|
+
expect(runtime.isExcludedByRouteStrategy("https://example.com/specific-path")).toBe(true);
|
|
71
|
+
expect(runtime.getStrategyForUrl("https://example.com/other")).toEqual([
|
|
72
|
+
"url",
|
|
73
|
+
]);
|
|
74
|
+
expect(runtime
|
|
75
|
+
.generateStaticLocalizedUrls(["https://example.com/specific-path"])
|
|
76
|
+
.map((url) => url.href)).toEqual([
|
|
77
|
+
"https://example.com/specific-path",
|
|
78
|
+
"https://example.com/de/404",
|
|
79
|
+
]);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
test(`${engineName} URLPattern refreshes ordered indexes and same-URL caches after edits`, async () => {
|
|
83
|
+
await withRuntime({
|
|
84
|
+
urlPatterns: [
|
|
85
|
+
{
|
|
86
|
+
pattern: "/:path(.*)?",
|
|
87
|
+
localized: [
|
|
88
|
+
["en", "/:path(.*)?"],
|
|
89
|
+
["de", "/de/:path(.*)?"],
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
routeStrategies: [{ match: "/old/:slug", exclude: true }],
|
|
94
|
+
}, ctor, async (runtime) => {
|
|
95
|
+
const url = "https://example.com/old/story";
|
|
96
|
+
expect(runtime.extractLocaleFromUrl(url)).toBe("en");
|
|
97
|
+
expect(runtime.localizeUrl(url, { locale: "de" }).pathname).toBe("/de/old/story");
|
|
98
|
+
expect(runtime.isExcludedByRouteStrategy(url)).toBe(true);
|
|
99
|
+
const pattern = runtime.urlPatterns[0];
|
|
100
|
+
pattern.pattern = "/new/:path(.*)?";
|
|
101
|
+
pattern.localized[0][1] = "/new/:path(.*)?";
|
|
102
|
+
pattern.localized[1][1] = "/de/new/:path(.*)?";
|
|
103
|
+
runtime.routeStrategies[0].match = "/new/:slug";
|
|
104
|
+
// Reuse the exact href so both one-entry result caches must notice
|
|
105
|
+
// the config version change and the fast-path parse must be refreshed.
|
|
106
|
+
expect(runtime.extractLocaleFromUrl(url)).toBe(undefined);
|
|
107
|
+
expect(runtime.localizeUrl(url, { locale: "de" }).href).toBe(url);
|
|
108
|
+
expect(runtime.isExcludedByRouteStrategy(url)).toBe(false);
|
|
109
|
+
expect(runtime.localizeUrl("https://example.com/new/story", { locale: "de" })
|
|
110
|
+
.pathname).toBe("/de/new/story");
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
test(`${engineName} URLPattern refreshes warmed winners after collection and reference edits`, async () => {
|
|
114
|
+
await withRuntime({
|
|
115
|
+
urlPatterns: [
|
|
116
|
+
{
|
|
117
|
+
pattern: "/same/:slug",
|
|
118
|
+
localized: [["de", "/same/:slug"]],
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
routeStrategies: [{ match: "/same/:slug", strategy: ["url"] }],
|
|
122
|
+
}, ctor, async (runtime) => {
|
|
123
|
+
const url = "https://example.com/same/story";
|
|
124
|
+
expect(runtime.extractLocaleFromUrl(url)).toBe("de");
|
|
125
|
+
expect(runtime.getStrategyForUrl(url)).toEqual(["url"]);
|
|
126
|
+
// A pushed earlier pattern changes the extraction winner. Splicing it
|
|
127
|
+
// back out must restore the original winner on this same cached URL.
|
|
128
|
+
runtime.urlPatterns.unshift({
|
|
129
|
+
pattern: "/same/:slug",
|
|
130
|
+
localized: [["en", "/same/:slug"]],
|
|
131
|
+
});
|
|
132
|
+
expect(runtime.extractLocaleFromUrl(url)).toBe("en");
|
|
133
|
+
runtime.urlPatterns.splice(0, 1);
|
|
134
|
+
expect(runtime.extractLocaleFromUrl(url)).toBe("de");
|
|
135
|
+
// Replacing the localized array and its locale string changes the
|
|
136
|
+
// first matching locale even though its pattern text is unchanged.
|
|
137
|
+
const entry = runtime.urlPatterns[0];
|
|
138
|
+
entry.localized = [["en", "/same/:slug"]];
|
|
139
|
+
expect(runtime.extractLocaleFromUrl(url)).toBe("en");
|
|
140
|
+
// Replacing a strategy object with the same match string must refresh
|
|
141
|
+
// the warmed rule result as well as the candidate index snapshot.
|
|
142
|
+
runtime.routeStrategies[0] = {
|
|
143
|
+
match: "/same/:slug",
|
|
144
|
+
strategy: ["baseLocale"],
|
|
145
|
+
};
|
|
146
|
+
expect(runtime.getStrategyForUrl(url)).toEqual(["baseLocale"]);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
test(`${engineName} URLPattern keeps invalid-pattern errors ahead of later matches`, async () => {
|
|
150
|
+
await withRuntime({
|
|
151
|
+
urlPatterns: [
|
|
152
|
+
{ pattern: "/broken/:id(", localized: [] },
|
|
153
|
+
{
|
|
154
|
+
pattern: "/valid/:id",
|
|
155
|
+
localized: [
|
|
156
|
+
["en", "/valid/:id"],
|
|
157
|
+
["de", "/de/valid/:id"],
|
|
158
|
+
],
|
|
159
|
+
},
|
|
160
|
+
],
|
|
161
|
+
}, ctor, async (runtime) => {
|
|
162
|
+
expect(() => runtime.localizeUrl("https://example.com/valid/7", { locale: "de" })).toThrow();
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
test(`${engineName} URLPattern keeps malformed localized entries unconditional`, async () => {
|
|
166
|
+
await withRuntime({ urlPatterns: [] }, ctor, async (runtime) => {
|
|
167
|
+
runtime.urlPatterns.push({
|
|
168
|
+
pattern: "/unrelated/:slug",
|
|
169
|
+
localized: [null],
|
|
170
|
+
});
|
|
171
|
+
// This route cannot be pruned by its path prefix: the original generic
|
|
172
|
+
// scan would encounter the malformed localized entry and throw.
|
|
173
|
+
expect(() => runtime.localizeUrl("https://example.com/elsewhere", { locale: "de" })).toThrow();
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
test(`${engineName} URLPattern preserves trailing-slash alias captures`, async () => {
|
|
177
|
+
await withRuntime({
|
|
178
|
+
trailingSlash: "always",
|
|
179
|
+
urlPatterns: [
|
|
180
|
+
{
|
|
181
|
+
pattern: "/:path(.*)",
|
|
182
|
+
localized: [
|
|
183
|
+
["en", "/:path(.*)"],
|
|
184
|
+
["de", "/de/:path(.*)/details"],
|
|
185
|
+
],
|
|
186
|
+
},
|
|
187
|
+
],
|
|
188
|
+
}, ctor, async (runtime) => {
|
|
189
|
+
expect(runtime.localizeUrl("https://example.com/foo", { locale: "de" }).href).toBe("https://example.com/de/foo/details/");
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
test(`${engineName} URLPattern keeps file-host bases separate`, async () => {
|
|
193
|
+
await withRuntime({
|
|
194
|
+
urlPatterns: [
|
|
195
|
+
{
|
|
196
|
+
pattern: "/section/:slug",
|
|
197
|
+
localized: [
|
|
198
|
+
["en", "/section/:slug"],
|
|
199
|
+
["de", "/de/section/:slug"],
|
|
200
|
+
],
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
routeStrategies: [{ match: "/section/:slug", exclude: true }],
|
|
204
|
+
}, ctor, async (runtime) => {
|
|
205
|
+
const first = new URL("file://server-one/section/story");
|
|
206
|
+
const second = new URL("file://server-two/section/story");
|
|
207
|
+
expect(runtime.extractLocaleFromUrl(first)).toBe("en");
|
|
208
|
+
expect(runtime.extractLocaleFromUrl(second)).toBe("en");
|
|
209
|
+
// Policy matching uses url.origin ("null" for both file hosts) as a
|
|
210
|
+
// URLPattern base and therefore retains the preexisting Invalid URL
|
|
211
|
+
// error. The locale extraction above exercises cache isolation safely.
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
for (const [engineName, ctor] of engines) {
|
|
216
|
+
test(`${engineName} URLPattern avoids unrelated late-route scans across APIs`, async () => {
|
|
217
|
+
let constructions = 0;
|
|
218
|
+
let executions = 0;
|
|
219
|
+
class CountingURLPattern extends ctor {
|
|
220
|
+
constructor(pattern, baseURL) {
|
|
221
|
+
if (baseURL === undefined)
|
|
222
|
+
super(pattern);
|
|
223
|
+
else
|
|
224
|
+
super(pattern, String(baseURL));
|
|
225
|
+
constructions++;
|
|
226
|
+
}
|
|
227
|
+
exec(input, baseURL) {
|
|
228
|
+
executions++;
|
|
229
|
+
return baseURL === undefined
|
|
230
|
+
? super.exec(input)
|
|
231
|
+
: super.exec(input, baseURL);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
const urlPatterns = Array.from({ length: 67 }, (_, index) => ({
|
|
235
|
+
pattern: `/route${index}/:slug`,
|
|
236
|
+
localized: [
|
|
237
|
+
["en", `/route${index}/:slug`],
|
|
238
|
+
["de", `/de/route${index}/:slug`],
|
|
239
|
+
],
|
|
240
|
+
}));
|
|
241
|
+
const routeStrategies = Array.from({ length: 67 }, (_, index) => ({
|
|
242
|
+
match: `/route${index}/:slug`,
|
|
243
|
+
exclude: true,
|
|
244
|
+
}));
|
|
245
|
+
await withRuntime({ urlPatterns, routeStrategies }, CountingURLPattern, async (runtime) => {
|
|
246
|
+
expect(runtime.localizeUrl("https://example.com/route66/story", {
|
|
247
|
+
locale: "de",
|
|
248
|
+
}).pathname).toBe("/de/route66/story");
|
|
249
|
+
expect(runtime.deLocalizeUrl("https://example.com/de/route66/story").pathname).toBe("/route66/story");
|
|
250
|
+
expect(runtime.extractLocaleFromUrl("https://example.com/de/route66/story")).toBe("de");
|
|
251
|
+
expect(runtime.isExcludedByRouteStrategy("https://example.com/route66/story")).toBe(true);
|
|
252
|
+
expect(runtime
|
|
253
|
+
.generateStaticLocalizedUrls(["https://example.com/route66/story"])
|
|
254
|
+
.map((url) => url.pathname)).toEqual(["/route66/story", "/de/route66/story"]);
|
|
255
|
+
// Includes createParaglide's capability probe. URLPattern work across
|
|
256
|
+
// five APIs stays proportional to the few matching patterns, not 67.
|
|
257
|
+
expect(constructions).toBeLessThanOrEqual(4);
|
|
258
|
+
expect(executions).toBeLessThanOrEqual(20);
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
//# sourceMappingURL=url-pattern-index.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url-pattern-index.test.js","sourceRoot":"","sources":["../../../src/compiler/runtime/url-pattern-index.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, test } from \"vitest\";\nimport { URLPattern as PolyfillURLPattern } from \"urlpattern-polyfill\";\nimport { createParaglide } from \"../create-paraglide.js\";\nimport type { CompilerOptions } from \"../compiler-options.js\";\nimport { newProject } from \"@inlang/sdk\";\n\ntype PatternCtor = typeof URLPattern;\n\nconst nativeURLPattern = Reflect.get(globalThis, \"URLPattern\") as\n\t| PatternCtor\n\t| undefined;\nconst engines: Array<[string, PatternCtor]> = [];\nlet runtimeModuleNonce = 0;\nif (\n\ttypeof nativeURLPattern === \"function\" &&\n\tnativeURLPattern !== PolyfillURLPattern\n) {\n\tengines.push([\"native\", nativeURLPattern]);\n}\nengines.push([\"polyfill\", PolyfillURLPattern as unknown as PatternCtor]);\n\nasync function withURLPattern<T>(ctor: PatternCtor, fn: () => Promise<T>) {\n\tconst descriptor = Object.getOwnPropertyDescriptor(globalThis, \"URLPattern\");\n\tObject.defineProperty(globalThis, \"URLPattern\", {\n\t\tconfigurable: true,\n\t\twritable: true,\n\t\tvalue: ctor,\n\t});\n\ttry {\n\t\treturn await fn();\n\t} finally {\n\t\tif (descriptor) {\n\t\t\tObject.defineProperty(globalThis, \"URLPattern\", descriptor);\n\t\t} else {\n\t\t\tReflect.deleteProperty(globalThis, \"URLPattern\");\n\t\t}\n\t}\n}\n\nasync function withRuntime(\n\toptions: {\n\t\turlPatterns: Array<{\n\t\t\tpattern: string;\n\t\t\tlocalized: Array<[string, string]>;\n\t\t}>;\n\t\trouteStrategies?: CompilerOptions[\"routeStrategies\"];\n\t\ttrailingSlash?: \"always\" | \"never\";\n\t},\n\tctor: PatternCtor,\n\tfn: (runtime: Awaited<ReturnType<typeof createParaglide>>) => Promise<void>\n) {\n\treturn withURLPattern(ctor, async () =>\n\t\tfn(\n\t\t\tawait createParaglide({\n\t\t\t\tblob: await newProject({\n\t\t\t\t\tsettings: { baseLocale: \"en\", locales: [\"en\", \"de\"] },\n\t\t\t\t}),\n\t\t\t\tcookieName: `pattern-index-test-${runtimeModuleNonce++}`,\n\t\t\t\tstrategy: [\"url\", \"baseLocale\"],\n\t\t\t\t...options,\n\t\t\t})\n\t\t)\n\t);\n}\n\nfor (const [engineName, ctor] of engines) {\n\ttest(`${engineName} URLPattern keeps ordered routing and URL parts`, async () => {\n\t\tawait withRuntime(\n\t\t\t{\n\t\t\t\turlPatterns: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpattern: \"/specific-path\",\n\t\t\t\t\t\tlocalized: [\n\t\t\t\t\t\t\t[\"en\", \"/specific-path\"],\n\t\t\t\t\t\t\t[\"de\", \"/de/404\"],\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tpattern: \"/:path(.*)?\",\n\t\t\t\t\t\tlocalized: [\n\t\t\t\t\t\t\t[\"en\", \"/:path(.*)?\"],\n\t\t\t\t\t\t\t[\"de\", \"/de/:path(.*)?\"],\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\trouteStrategies: [\n\t\t\t\t\t{ match: \"/specific-path\", exclude: true },\n\t\t\t\t\t{ match: \"/:path(.*)?\", strategy: [\"url\"] },\n\t\t\t\t],\n\t\t\t},\n\t\t\tctor,\n\t\t\tasync (runtime) => {\n\t\t\t\tconst input =\n\t\t\t\t\t\"https://alice:secret@example.com/specific-path?redirect=%2Faccount#section\";\n\t\t\t\texpect(runtime.localizeUrl(input, { locale: \"de\" }).href).toBe(\n\t\t\t\t\t\"https://alice:secret@example.com/de/404?redirect=%2Faccount#section\"\n\t\t\t\t);\n\t\t\t\texpect(\n\t\t\t\t\truntime.deLocalizeUrl(\n\t\t\t\t\t\t\"https://alice:secret@example.com/de/404?redirect=%2Faccount#section\"\n\t\t\t\t\t).href\n\t\t\t\t).toBe(\n\t\t\t\t\t\"https://alice:secret@example.com/specific-path?redirect=%2Faccount#section\"\n\t\t\t\t);\n\t\t\t\texpect(runtime.extractLocaleFromUrl(input)).toBe(\"en\");\n\t\t\t\texpect(\n\t\t\t\t\truntime.isExcludedByRouteStrategy(\"https://example.com/specific-path\")\n\t\t\t\t).toBe(true);\n\t\t\t\texpect(runtime.getStrategyForUrl(\"https://example.com/other\")).toEqual([\n\t\t\t\t\t\"url\",\n\t\t\t\t]);\n\t\t\t\texpect(\n\t\t\t\t\truntime\n\t\t\t\t\t\t.generateStaticLocalizedUrls([\"https://example.com/specific-path\"])\n\t\t\t\t\t\t.map((url) => url.href)\n\t\t\t\t).toEqual([\n\t\t\t\t\t\"https://example.com/specific-path\",\n\t\t\t\t\t\"https://example.com/de/404\",\n\t\t\t\t]);\n\t\t\t}\n\t\t);\n\t});\n\n\ttest(`${engineName} URLPattern refreshes ordered indexes and same-URL caches after edits`, async () => {\n\t\tawait withRuntime(\n\t\t\t{\n\t\t\t\turlPatterns: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpattern: \"/:path(.*)?\",\n\t\t\t\t\t\tlocalized: [\n\t\t\t\t\t\t\t[\"en\", \"/:path(.*)?\"],\n\t\t\t\t\t\t\t[\"de\", \"/de/:path(.*)?\"],\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\trouteStrategies: [{ match: \"/old/:slug\", exclude: true }],\n\t\t\t},\n\t\t\tctor,\n\t\t\tasync (runtime) => {\n\t\t\t\tconst url = \"https://example.com/old/story\";\n\t\t\t\texpect(runtime.extractLocaleFromUrl(url)).toBe(\"en\");\n\t\t\t\texpect(runtime.localizeUrl(url, { locale: \"de\" }).pathname).toBe(\n\t\t\t\t\t\"/de/old/story\"\n\t\t\t\t);\n\t\t\t\texpect(runtime.isExcludedByRouteStrategy(url)).toBe(true);\n\n\t\t\t\tconst pattern = runtime.urlPatterns[0]! as {\n\t\t\t\t\tpattern: string;\n\t\t\t\t\tlocalized: Array<[string, string]>;\n\t\t\t\t};\n\t\t\t\tpattern.pattern = \"/new/:path(.*)?\";\n\t\t\t\tpattern.localized[0]![1] = \"/new/:path(.*)?\";\n\t\t\t\tpattern.localized[1]![1] = \"/de/new/:path(.*)?\";\n\t\t\t\t(runtime.routeStrategies[0] as { match: string }).match = \"/new/:slug\";\n\n\t\t\t\t// Reuse the exact href so both one-entry result caches must notice\n\t\t\t\t// the config version change and the fast-path parse must be refreshed.\n\t\t\t\texpect(runtime.extractLocaleFromUrl(url)).toBe(undefined);\n\t\t\t\texpect(runtime.localizeUrl(url, { locale: \"de\" }).href).toBe(url);\n\t\t\t\texpect(runtime.isExcludedByRouteStrategy(url)).toBe(false);\n\t\t\t\texpect(\n\t\t\t\t\truntime.localizeUrl(\"https://example.com/new/story\", { locale: \"de\" })\n\t\t\t\t\t\t.pathname\n\t\t\t\t).toBe(\"/de/new/story\");\n\t\t\t}\n\t\t);\n\t});\n\n\ttest(`${engineName} URLPattern refreshes warmed winners after collection and reference edits`, async () => {\n\t\tawait withRuntime(\n\t\t\t{\n\t\t\t\turlPatterns: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpattern: \"/same/:slug\",\n\t\t\t\t\t\tlocalized: [[\"de\", \"/same/:slug\"]],\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\trouteStrategies: [{ match: \"/same/:slug\", strategy: [\"url\"] }],\n\t\t\t},\n\t\t\tctor,\n\t\t\tasync (runtime) => {\n\t\t\t\tconst url = \"https://example.com/same/story\";\n\t\t\t\texpect(runtime.extractLocaleFromUrl(url)).toBe(\"de\");\n\t\t\t\texpect(runtime.getStrategyForUrl(url)).toEqual([\"url\"]);\n\n\t\t\t\t// A pushed earlier pattern changes the extraction winner. Splicing it\n\t\t\t\t// back out must restore the original winner on this same cached URL.\n\t\t\t\t(\n\t\t\t\t\truntime.urlPatterns as Array<{\n\t\t\t\t\t\tpattern: string;\n\t\t\t\t\t\tlocalized: Array<[string, string]>;\n\t\t\t\t\t}>\n\t\t\t\t).unshift({\n\t\t\t\t\tpattern: \"/same/:slug\",\n\t\t\t\t\tlocalized: [[\"en\", \"/same/:slug\"]],\n\t\t\t\t});\n\t\t\t\texpect(runtime.extractLocaleFromUrl(url)).toBe(\"en\");\n\t\t\t\truntime.urlPatterns.splice(0, 1);\n\t\t\t\texpect(runtime.extractLocaleFromUrl(url)).toBe(\"de\");\n\n\t\t\t\t// Replacing the localized array and its locale string changes the\n\t\t\t\t// first matching locale even though its pattern text is unchanged.\n\t\t\t\tconst entry = runtime.urlPatterns[0]!;\n\t\t\t\tentry.localized = [[\"en\", \"/same/:slug\"]];\n\t\t\t\texpect(runtime.extractLocaleFromUrl(url)).toBe(\"en\");\n\n\t\t\t\t// Replacing a strategy object with the same match string must refresh\n\t\t\t\t// the warmed rule result as well as the candidate index snapshot.\n\t\t\t\truntime.routeStrategies[0] = {\n\t\t\t\t\tmatch: \"/same/:slug\",\n\t\t\t\t\tstrategy: [\"baseLocale\"],\n\t\t\t\t};\n\t\t\t\texpect(runtime.getStrategyForUrl(url)).toEqual([\"baseLocale\"]);\n\t\t\t}\n\t\t);\n\t});\n\n\ttest(`${engineName} URLPattern keeps invalid-pattern errors ahead of later matches`, async () => {\n\t\tawait withRuntime(\n\t\t\t{\n\t\t\t\turlPatterns: [\n\t\t\t\t\t{ pattern: \"/broken/:id(\", localized: [] },\n\t\t\t\t\t{\n\t\t\t\t\t\tpattern: \"/valid/:id\",\n\t\t\t\t\t\tlocalized: [\n\t\t\t\t\t\t\t[\"en\", \"/valid/:id\"],\n\t\t\t\t\t\t\t[\"de\", \"/de/valid/:id\"],\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\tctor,\n\t\t\tasync (runtime) => {\n\t\t\t\texpect(() =>\n\t\t\t\t\truntime.localizeUrl(\"https://example.com/valid/7\", { locale: \"de\" })\n\t\t\t\t).toThrow();\n\t\t\t}\n\t\t);\n\t});\n\n\ttest(`${engineName} URLPattern keeps malformed localized entries unconditional`, async () => {\n\t\tawait withRuntime({ urlPatterns: [] }, ctor, async (runtime) => {\n\t\t\t(\n\t\t\t\truntime.urlPatterns as unknown as Array<{\n\t\t\t\t\tpattern: string;\n\t\t\t\t\tlocalized: Array<[string, string]>;\n\t\t\t\t}>\n\t\t\t).push({\n\t\t\t\tpattern: \"/unrelated/:slug\",\n\t\t\t\tlocalized: [null as unknown as [string, string]],\n\t\t\t});\n\n\t\t\t// This route cannot be pruned by its path prefix: the original generic\n\t\t\t// scan would encounter the malformed localized entry and throw.\n\t\t\texpect(() =>\n\t\t\t\truntime.localizeUrl(\"https://example.com/elsewhere\", { locale: \"de\" })\n\t\t\t).toThrow();\n\t\t});\n\t});\n\n\ttest(`${engineName} URLPattern preserves trailing-slash alias captures`, async () => {\n\t\tawait withRuntime(\n\t\t\t{\n\t\t\t\ttrailingSlash: \"always\",\n\t\t\t\turlPatterns: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpattern: \"/:path(.*)\",\n\t\t\t\t\t\tlocalized: [\n\t\t\t\t\t\t\t[\"en\", \"/:path(.*)\"],\n\t\t\t\t\t\t\t[\"de\", \"/de/:path(.*)/details\"],\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\tctor,\n\t\t\tasync (runtime) => {\n\t\t\t\texpect(\n\t\t\t\t\truntime.localizeUrl(\"https://example.com/foo\", { locale: \"de\" }).href\n\t\t\t\t).toBe(\"https://example.com/de/foo/details/\");\n\t\t\t}\n\t\t);\n\t});\n\n\ttest(`${engineName} URLPattern keeps file-host bases separate`, async () => {\n\t\tawait withRuntime(\n\t\t\t{\n\t\t\t\turlPatterns: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpattern: \"/section/:slug\",\n\t\t\t\t\t\tlocalized: [\n\t\t\t\t\t\t\t[\"en\", \"/section/:slug\"],\n\t\t\t\t\t\t\t[\"de\", \"/de/section/:slug\"],\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\trouteStrategies: [{ match: \"/section/:slug\", exclude: true }],\n\t\t\t},\n\t\t\tctor,\n\t\t\tasync (runtime) => {\n\t\t\t\tconst first = new URL(\"file://server-one/section/story\");\n\t\t\t\tconst second = new URL(\"file://server-two/section/story\");\n\t\t\t\texpect(runtime.extractLocaleFromUrl(first)).toBe(\"en\");\n\t\t\t\texpect(runtime.extractLocaleFromUrl(second)).toBe(\"en\");\n\t\t\t\t// Policy matching uses url.origin (\"null\" for both file hosts) as a\n\t\t\t\t// URLPattern base and therefore retains the preexisting Invalid URL\n\t\t\t\t// error. The locale extraction above exercises cache isolation safely.\n\t\t\t}\n\t\t);\n\t});\n}\n\nfor (const [engineName, ctor] of engines) {\n\ttest(`${engineName} URLPattern avoids unrelated late-route scans across APIs`, async () => {\n\t\tlet constructions = 0;\n\t\tlet executions = 0;\n\t\tclass CountingURLPattern extends ctor {\n\t\t\tconstructor(pattern?: string | URLPatternInit, baseURL?: string | URL) {\n\t\t\t\tif (baseURL === undefined) super(pattern as string | URLPatternInit);\n\t\t\t\telse super(pattern as string | URLPatternInit, String(baseURL));\n\t\t\t\tconstructions++;\n\t\t\t}\n\n\t\t\toverride exec(input: string | URL, baseURL?: string) {\n\t\t\t\texecutions++;\n\t\t\t\treturn baseURL === undefined\n\t\t\t\t\t? super.exec(input)\n\t\t\t\t\t: super.exec(input, baseURL);\n\t\t\t}\n\t\t}\n\n\t\tconst urlPatterns = Array.from({ length: 67 }, (_, index) => ({\n\t\t\tpattern: `/route${index}/:slug`,\n\t\t\tlocalized: [\n\t\t\t\t[\"en\", `/route${index}/:slug`] as [string, string],\n\t\t\t\t[\"de\", `/de/route${index}/:slug`] as [string, string],\n\t\t\t],\n\t\t}));\n\t\tconst routeStrategies = Array.from({ length: 67 }, (_, index) => ({\n\t\t\tmatch: `/route${index}/:slug`,\n\t\t\texclude: true as const,\n\t\t}));\n\n\t\tawait withRuntime(\n\t\t\t{ urlPatterns, routeStrategies },\n\t\t\tCountingURLPattern as unknown as PatternCtor,\n\t\t\tasync (runtime) => {\n\t\t\t\texpect(\n\t\t\t\t\truntime.localizeUrl(\"https://example.com/route66/story\", {\n\t\t\t\t\t\tlocale: \"de\",\n\t\t\t\t\t}).pathname\n\t\t\t\t).toBe(\"/de/route66/story\");\n\t\t\t\texpect(\n\t\t\t\t\truntime.deLocalizeUrl(\"https://example.com/de/route66/story\").pathname\n\t\t\t\t).toBe(\"/route66/story\");\n\t\t\t\texpect(\n\t\t\t\t\truntime.extractLocaleFromUrl(\"https://example.com/de/route66/story\")\n\t\t\t\t).toBe(\"de\");\n\t\t\t\texpect(\n\t\t\t\t\truntime.isExcludedByRouteStrategy(\"https://example.com/route66/story\")\n\t\t\t\t).toBe(true);\n\t\t\t\texpect(\n\t\t\t\t\truntime\n\t\t\t\t\t\t.generateStaticLocalizedUrls([\"https://example.com/route66/story\"])\n\t\t\t\t\t\t.map((url) => url.pathname)\n\t\t\t\t).toEqual([\"/route66/story\", \"/de/route66/story\"]);\n\n\t\t\t\t// Includes createParaglide's capability probe. URLPattern work across\n\t\t\t\t// five APIs stays proportional to the few matching patterns, not 67.\n\t\t\t\texpect(constructions).toBeLessThanOrEqual(4);\n\t\t\t\texpect(executions).toBeLessThanOrEqual(20);\n\t\t\t}\n\t\t);\n\t});\n}\n"]}
|
|
@@ -93,7 +93,7 @@ function createCompiledMessagesObject(compiledBundles) {
|
|
|
93
93
|
function injectCode(path) {
|
|
94
94
|
const code = fs.readFileSync(new URL(path, import.meta.url), "utf-8");
|
|
95
95
|
// Regex to match single-line and multi-line imports
|
|
96
|
-
const importRegex =
|
|
96
|
+
const importRegex = /^\s*import\s+[\s\S]*?from\s+['"][^'"]+['"]\s*;?/gm;
|
|
97
97
|
const sourceMapRegex = /\/\/# sourceMappingURL=.*$/gm;
|
|
98
98
|
const blockSourceMapRegex = /\/\*# sourceMappingURL=.*?\*\//g;
|
|
99
99
|
return code
|
|
@@ -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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
|
|
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 = /^\\s*import\\s+[\\s\\S]*?from\\s+['\"][^'\"]+['\"]\\s*;?/gm;\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"]}
|
|
@@ -46,6 +46,18 @@ export const isExcludedByRouteStrategy = runtime.isExcludedByRouteStrategy;`);
|
|
|
46
46
|
expect(response.status).toBe(200);
|
|
47
47
|
expect(await response.text()).toBe("ready");
|
|
48
48
|
});
|
|
49
|
+
test("import statements in JSDoc examples survive in the generated server file", () => {
|
|
50
|
+
const serverCode = createServerFile({
|
|
51
|
+
compiledBundles: [],
|
|
52
|
+
compilerOptions: {
|
|
53
|
+
disableAsyncLocalStorage: false,
|
|
54
|
+
experimentalMiddlewareLocaleSplitting: false,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
expect(serverCode).toContain(` * import { paraglideMiddleware } from './paraglide/server.js'\n * import handler from '@tanstack/react-start/server-entry'\n *\n * export default {`);
|
|
58
|
+
// only the `import * as runtime` line that createServerFile itself prepends
|
|
59
|
+
expect(serverCode.match(/^\s*import\s/gm)).toHaveLength(1);
|
|
60
|
+
});
|
|
49
61
|
test("keys compiledBundles by the safe module id, not the raw bundle id (experimentalMiddlewareLocaleSplitting)", () => {
|
|
50
62
|
// A bundle id that changes under toSafeModuleId (case-folded + a numeric
|
|
51
63
|
// suffix appended for the uppercase letters). trackMessageCall(), the
|
|
@@ -1 +1 @@
|
|
|
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
|
+
{"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(\"import statements in JSDoc examples survive in the generated server file\", () => {\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});\n\n\texpect(serverCode).toContain(\n\t\t` * import { paraglideMiddleware } from './paraglide/server.js'\\n * import handler from '@tanstack/react-start/server-entry'\\n *\\n * export default {`\n\t);\n\t// only the `import * as runtime` line that createServerFile itself prepends\n\texpect(serverCode.match(/^\\s*import\\s/gm)).toHaveLength(1);\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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/services/env-variables/index.ts"],"names":[],"mappings":"AACA,MAAM,CAAC,MAAM,aAAa,GAAG;IAC5B,qBAAqB,EAAE,QAAQ;CAC/B,CAAA","sourcesContent":["\nexport const ENV_VARIABLES = {\n\tPARJS_PACKAGE_VERSION: \"2.
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/services/env-variables/index.ts"],"names":[],"mappings":"AACA,MAAM,CAAC,MAAM,aAAa,GAAG;IAC5B,qBAAqB,EAAE,QAAQ;CAC/B,CAAA","sourcesContent":["\nexport const ENV_VARIABLES = {\n\tPARJS_PACKAGE_VERSION: \"2.26.0\",\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inlang/paraglide-js",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.26.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@inlang/recommend-sherlock": "^0.2.1",
|
|
30
|
-
"@inlang/sdk": "^3.0
|
|
30
|
+
"@inlang/sdk": "^3.1.0",
|
|
31
31
|
"commander": "11.1.0",
|
|
32
32
|
"consola": "3.4.0",
|
|
33
33
|
"jiti": "^2.7.0",
|