@inlang/paraglide-js 1.7.2 → 1.7.3

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.
@@ -1,5 +1,5 @@
1
1
  export { negotiateLanguagePreferences } from './negotiation/language.js';
2
2
  export { detectLanguageFromPath } from './routing/detectLanguage.js';
3
- export { bestMatch, resolveRoute, parseRouteDefinition, exec, type PathDefinitionTranslations, type ParamMatcher, type RouteParam, } from './routing/routeDefinitions.js';
3
+ export { bestMatch, resolveRoute, parseRouteDefinition, type PathDefinitionTranslations, type ParamMatcher, type RouteParam, } from './routing/routeDefinitions.js';
4
4
  export { validatePathTranslations, prettyPrintPathDefinitionIssues, } from './routing/validatePathTranslations.js';
5
5
  export { resolveUserPathDefinitions, type UserPathDefinitionTranslations, } from './routing/resolveUserPathDefinitions.js';
@@ -109,6 +109,116 @@ function detectLanguageFromPath({
109
109
  }
110
110
  return void 0;
111
111
  }
112
+ const EMPTY = { type: "static", content: "", matched: false };
113
+ function sort_routes(routes) {
114
+ const segment_cache = /* @__PURE__ */ new Map();
115
+ function split(id) {
116
+ const parts = [];
117
+ let i = 0;
118
+ while (i <= id.length) {
119
+ const start = id.indexOf("[", i);
120
+ if (start === -1) {
121
+ parts.push({ type: "static", content: id.slice(i), matched: false });
122
+ break;
123
+ }
124
+ parts.push({ type: "static", content: id.slice(i, start), matched: false });
125
+ const type = id[start + 1] === "[" ? "optional" : id[start + 1] === "." ? "rest" : "required";
126
+ const delimiter = type === "optional" ? "]]" : "]";
127
+ const end = id.indexOf(delimiter, start);
128
+ if (end === -1) {
129
+ throw new Error(`Invalid route ID ${id}`);
130
+ }
131
+ const content = id.slice(start, i = end + delimiter.length);
132
+ parts.push({
133
+ type,
134
+ content,
135
+ matched: content.includes("=")
136
+ });
137
+ }
138
+ return parts;
139
+ }
140
+ function get_parts(segment) {
141
+ if (!segment_cache.has(segment)) {
142
+ segment_cache.set(segment, split(segment));
143
+ }
144
+ return segment_cache.get(segment);
145
+ }
146
+ return routes.sort((route_a, route_b) => {
147
+ var _a, _b, _c, _d, _e, _f;
148
+ const segments_a = split_route_id(route_a).map(get_parts);
149
+ const segments_b = split_route_id(route_b).map(get_parts);
150
+ for (let i = 0; i < Math.max(segments_a.length, segments_b.length); i += 1) {
151
+ const segment_a = segments_a[i] ?? [EMPTY];
152
+ const segment_b = segments_b[i] ?? [EMPTY];
153
+ for (let j = 0; j < Math.max(segment_a.length, segment_b.length); j += 1) {
154
+ const a = segment_a[j];
155
+ const b = segment_b[j];
156
+ const dynamic = !!(j % 2);
157
+ if (dynamic) {
158
+ if (!a)
159
+ return -1;
160
+ if (!b)
161
+ return 1;
162
+ const next_a = ((_a = segment_a[j + 1]) == null ? void 0 : _a.content) || ((_c = (_b = segments_a[i + 1]) == null ? void 0 : _b[0]) == null ? void 0 : _c.content);
163
+ const next_b = ((_d = segment_b[j + 1]) == null ? void 0 : _d.content) || ((_f = (_e = segments_b[i + 1]) == null ? void 0 : _e[0]) == null ? void 0 : _f.content);
164
+ if (a.type === "rest" && b.type === "rest") {
165
+ if (next_a && next_b)
166
+ continue;
167
+ if (next_a)
168
+ return -1;
169
+ if (next_b)
170
+ return 1;
171
+ }
172
+ if (a.type === "rest") {
173
+ return next_a && !next_b ? -1 : 1;
174
+ }
175
+ if (b.type === "rest") {
176
+ return next_b && !next_a ? 1 : -1;
177
+ }
178
+ if (a.matched !== b.matched) {
179
+ return a.matched ? -1 : 1;
180
+ }
181
+ if (a.type !== b.type) {
182
+ if (a.type === "required")
183
+ return -1;
184
+ if (b.type === "required")
185
+ return 1;
186
+ }
187
+ } else if ((a == null ? void 0 : a.content) !== (b == null ? void 0 : b.content)) {
188
+ if (a === EMPTY)
189
+ return -1;
190
+ if (b === EMPTY)
191
+ return 1;
192
+ return sort_static(a.content, b.content);
193
+ }
194
+ }
195
+ }
196
+ return route_a < route_b ? 1 : -1;
197
+ });
198
+ }
199
+ function split_route_id(id) {
200
+ return get_route_segments(
201
+ id.replace(/\[\[[^\]]+\]\](?!$)/g, "")
202
+ ).filter(Boolean);
203
+ }
204
+ function sort_static(a, b) {
205
+ if (a === b)
206
+ return 0;
207
+ let i = 0;
208
+ while (a[i] || b[i]) {
209
+ const char_a = a[i];
210
+ const char_b = b[i];
211
+ if (char_a !== char_b) {
212
+ if (char_a === void 0)
213
+ return 1;
214
+ if (char_b === void 0)
215
+ return -1;
216
+ return char_a < char_b ? -1 : 1;
217
+ }
218
+ i++;
219
+ }
220
+ return 0;
221
+ }
112
222
  const param_pattern = /^(\[)?(\.\.\.)?(\w+)(?:=(\w+))?(\])?$/;
113
223
  function parseRouteDefinition(id) {
114
224
  const params = [];
@@ -157,9 +267,7 @@ function parseRouteDefinition(id) {
157
267
  param_pattern.exec(content)
158
268
  );
159
269
  if (!match) {
160
- throw new Error(
161
- `Invalid param: ${content}. Params and matcher names can only have underscores and alphanumeric characters.`
162
- );
270
+ throw new Error(`Invalid param: ${content}`);
163
271
  }
164
272
  const [, is_optional, is_rest, name, matcher] = match;
165
273
  params.push({
@@ -181,7 +289,7 @@ function parseRouteDefinition(id) {
181
289
  function exec(match, params, matchers) {
182
290
  const result = {};
183
291
  const values = match.slice(1);
184
- const values_needing_match = values.filter((value) => value !== void 0);
292
+ const values_needing_match = values.filter((v) => v !== void 0);
185
293
  let buffered = 0;
186
294
  for (const [i, param] of params.entries()) {
187
295
  let value = values[i - buffered];
@@ -225,28 +333,24 @@ function escape(str) {
225
333
  }
226
334
  const basic_param_pattern = /\[(\[)?(\.\.\.)?(\w+?)(?:=(\w+))?\]\]?/g;
227
335
  function resolveRoute(id, params) {
228
- const segments = get_route_segments(id);
229
- return "/" + segments.map(
336
+ return "/" + get_route_segments(id).map(
230
337
  (segment) => segment.replace(basic_param_pattern, (_, optional, rest, name) => {
231
338
  const param_value = params[name];
232
339
  if (!param_value) {
233
- if (optional)
234
- return "";
235
- if (rest && param_value !== void 0)
340
+ if (optional || rest && param_value !== void 0)
236
341
  return "";
237
- throw new Error(`Missing parameter '${name}' in route ${id}`);
342
+ else
343
+ throw new Error(`Missing parameter '${name}' in route ${id}`);
238
344
  }
239
- if (param_value.startsWith("/") || param_value.endsWith("/"))
240
- throw new Error(
241
- `Parameter '${name}' in route ${id} cannot start or end with a slash -- this would cause an invalid route like foo//bar`
242
- );
345
+ if (param_value[0] == "/" || param_value.endsWith("/"))
346
+ throw new Error(`Parameter '${name}' in route ${id} cannot start or end with a slash`);
243
347
  return param_value;
244
348
  })
245
349
  ).filter(Boolean).join("/");
246
350
  }
247
351
  function bestMatch(canonicalPath, pathDefinitions, matchers) {
248
- let bestMatch2 = void 0;
249
- for (const pathDefinition of pathDefinitions) {
352
+ const sorted = sort_routes(pathDefinitions);
353
+ for (const pathDefinition of sorted) {
250
354
  const route = parseRouteDefinition(pathDefinition);
251
355
  const match = route.pattern.exec(removeTrailingSlash(canonicalPath));
252
356
  if (!match)
@@ -254,34 +358,14 @@ function bestMatch(canonicalPath, pathDefinitions, matchers) {
254
358
  const params = exec(match, route.params, matchers);
255
359
  if (!params)
256
360
  continue;
257
- if (!bestMatch2) {
258
- bestMatch2 = { params, route, id: pathDefinition };
259
- continue;
260
- }
261
- const bestMatchNumParams = Object.keys(bestMatch2.route.params).length;
262
- const currentMatchNumParams = Object.keys(route.params).length;
263
- if (bestMatchNumParams < currentMatchNumParams) {
264
- continue;
265
- }
266
- if (bestMatchNumParams > currentMatchNumParams) {
267
- bestMatch2 = { params, route, id: pathDefinition };
268
- continue;
269
- }
270
- if (bestMatchNumParams === currentMatchNumParams && route.pattern.source.length < bestMatch2.route.pattern.source.length) {
271
- bestMatch2 = { params, route, id: pathDefinition };
272
- }
361
+ return { params, id: pathDefinition };
273
362
  }
274
- return bestMatch2 ? {
275
- id: bestMatch2.id,
276
- params: bestMatch2.params
277
- } : void 0;
363
+ return void 0;
278
364
  }
279
365
  function removeTrailingSlash(path) {
280
366
  return path.endsWith("/") ? path.slice(0, -1) : path;
281
367
  }
282
- function get_route_segments(route) {
283
- return route.slice(1).split("/");
284
- }
368
+ const get_route_segments = (route) => route.slice(1).split("/");
285
369
  function validatePathTranslations(pathTranslations, availableLanguageTags, matchers) {
286
370
  const issues = [];
287
371
  const expectedLanguages = new Set(availableLanguageTags);
@@ -375,7 +459,6 @@ const fromMessage = (message, availableLanguageTags) => Object.fromEntries(
375
459
  export {
376
460
  bestMatch,
377
461
  detectLanguageFromPath,
378
- exec,
379
462
  negotiateLanguagePreferences,
380
463
  parseRouteDefinition,
381
464
  prettyPrintPathDefinitionIssues,
@@ -1,6 +1,10 @@
1
1
  export type PathDefinitionTranslations<T extends string = string> = {
2
2
  [canonicalPath: `/${string}`]: Record<T, `/${string}`>;
3
3
  };
4
+ type Route = {
5
+ params: RouteParam[];
6
+ pattern: RegExp;
7
+ };
4
8
  export type RouteParam = {
5
9
  name: string;
6
10
  matcher: string;
@@ -12,10 +16,7 @@ export type ParamMatcher = (segment: string) => boolean;
12
16
  /**
13
17
  * Creates the regex pattern, extracts parameter names, and generates types for a route
14
18
  */
15
- export declare function parseRouteDefinition(id: string): {
16
- params: RouteParam[];
17
- pattern: RegExp;
18
- };
19
+ export declare function parseRouteDefinition(id: string): Route;
19
20
  export declare function exec(match: RegExpMatchArray, params: RouteParam[], matchers: Record<string, ParamMatcher>): Record<string, string> | undefined;
20
21
  /**
21
22
  * Populate a route ID with params to resolve a pathname.
@@ -43,3 +44,12 @@ export declare function bestMatch(canonicalPath: string, pathDefinitions: string
43
44
  params: Record<string, string>;
44
45
  id: string;
45
46
  } | undefined;
47
+ /**
48
+ * Splits a route id into its segments, removing segments that
49
+ * don't affect the path (i.e. groups). The root route is represented by `/`
50
+ * and will be returned as `['']`.
51
+ * @param {string} route
52
+ * @returns string[]
53
+ */
54
+ export declare const get_route_segments: (route: string) => string[];
55
+ export {};
@@ -0,0 +1 @@
1
+ export declare function sort_routes(routes: string[]): string[];
package/dist/cli/index.js CHANGED
@@ -32234,7 +32234,7 @@ const addParaglideJsToDevDependencies = async (ctx) => {
32234
32234
  const ctx1 = await updatePackageJson({
32235
32235
  devDependencies: async (devDeps) => ({
32236
32236
  ...devDeps,
32237
- "@inlang/paraglide-js": "1.7.2"
32237
+ "@inlang/paraglide-js": "1.7.3"
32238
32238
  })
32239
32239
  })(ctx);
32240
32240
  ctx.logger.success("Added @inlang/paraglide-js to the devDependencies in package.json.");
@@ -32321,7 +32321,7 @@ const steps = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePropert
32321
32321
  runCompiler,
32322
32322
  updatePackageJson
32323
32323
  }, Symbol.toStringTag, { value: "Module" }));
32324
- const cli = new Command().name("paraglide-js").addCommand(compileCommand).addCommand(initCommand).showHelpAfterError().version("1.7.2");
32324
+ const cli = new Command().name("paraglide-js").addCommand(compileCommand).addCommand(initCommand).showHelpAfterError().version("1.7.3");
32325
32325
  export {
32326
32326
  defaults as Defaults,
32327
32327
  steps as Steps,