storyloco 2.8.0 → 2.8.1

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/README.md CHANGED
@@ -394,7 +394,7 @@ The plugin will:
394
394
 
395
395
  - Fetch redirect entries from your Storyblok datasource
396
396
  - Bake them into the build as a fallback
397
- - Support exact matches and wildcard patterns
397
+ - Support exact matches and wildcard patterns, with or without a trailing slash (SvelteKit strips it before hooks run, so `/old/` matches a request for `/old`)
398
398
  - Handle both internal and external redirects
399
399
 
400
400
  Redirects stay live without a rebuild: the handle fetches the datasource on a server's first request, then serves from memory and refreshes in the background once the list is a minute old. If Storyblok can't be reached, it keeps the last list it had, or the one baked in at build.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "storyloco",
3
- "version": "2.8.0",
3
+ "version": "2.8.1",
4
4
  "description": "A collection of slick Storyblok field plugins built with Svelte 5, TypeScript, and Tailwind CSS",
5
5
  "keywords": [
6
6
  "storyblok",
@@ -9,6 +9,15 @@ function normalize_path(pathname) {
9
9
  return "/";
10
10
  return pathname.startsWith("/") ? pathname : `/${pathname}`;
11
11
  }
12
+ /**
13
+ * How paths are compared: leading slash, no trailing slash, query kept. SvelteKit strips a trailing
14
+ * slash before hooks run, so `/old/` has to match a request for `/old`.
15
+ */
16
+ function match_key(path) {
17
+ const i = path.indexOf("?");
18
+ const [pathname, query] = i < 0 ? [path, ""] : [path.slice(0, i), path.slice(i)];
19
+ return normalize_path(pathname).replace(/(.)\/+$/, "$1") + query;
20
+ }
12
21
  /** Absolute URL check (e.g. https://..., http://..., //cdn...) */
13
22
  function is_absolute_url(value) {
14
23
  return /^https?:\/\//i.test(value) || value.startsWith("//");
@@ -30,7 +39,7 @@ function build_rules(redirects) {
30
39
  if (key.includes("*")) {
31
40
  try {
32
41
  rules.wildcards.push({
33
- matcher: match(to_src_pattern(key), { decode: decodeURIComponent, end: true }),
42
+ matcher: match(to_src_pattern(match_key(key)), { decode: decodeURIComponent, end: true }),
34
43
  builder: compile(to_dst_pattern(value), { encode: (x) => x }),
35
44
  });
36
45
  }
@@ -39,8 +48,10 @@ function build_rules(redirects) {
39
48
  }
40
49
  }
41
50
  else {
42
- rules.exact.set(normalize_path(key), value);
43
- rules.exact.set(key.startsWith("/") ? key.slice(1) : key, value);
51
+ const existing = rules.exact.get(match_key(key));
52
+ if (existing !== undefined && existing !== value)
53
+ logger.warn(`${key} -> ${value} replaces ${existing}: keys match with or without a trailing slash`);
54
+ rules.exact.set(match_key(key), value);
44
55
  }
45
56
  }
46
57
  return rules;
@@ -90,11 +101,10 @@ function warn(err) {
90
101
  async function resolve_redirect(url) {
91
102
  const { exact, wildcards } = await current_rules();
92
103
  try {
93
- const source = normalize_path(url.pathname + (url.search || ""));
94
- const source_plain = source.includes("?") ? source.split("?")[0] : source;
95
- const lookup = (key) => exact.get(key) ?? exact.get(key.startsWith("/") ? key.slice(1) : key);
104
+ const source = match_key(url.pathname + url.search);
105
+ const source_plain = match_key(url.pathname);
96
106
  // Prefer exact including query, then plain path
97
- let target = lookup(source) ?? lookup(source_plain) ?? null;
107
+ let target = exact.get(source) ?? exact.get(source_plain) ?? null;
98
108
  if (!target) {
99
109
  for (const { matcher, builder } of wildcards) {
100
110
  const m = matcher(source_plain);
@@ -109,7 +119,8 @@ async function resolve_redirect(url) {
109
119
  const result = is_absolute_url(target) ? target : normalize_path(target);
110
120
  // If source included a query and the target is a relative path without its own query,
111
121
  // we do NOT automatically forward the original query. The map should specify it.
112
- if (result !== source && result !== source_plain)
122
+ const result_key = match_key(result);
123
+ if (result_key !== source && result_key !== source_plain)
113
124
  return result;
114
125
  }
115
126
  return null;