@sveltejs/kit 1.0.0-next.545 → 1.0.0-next.546

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/kit",
3
- "version": "1.0.0-next.545",
3
+ "version": "1.0.0-next.546",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/sveltejs/kit",
@@ -111,11 +111,11 @@ const options = object(
111
111
  browser: object({
112
112
  hydrate: error(
113
113
  (keypath) =>
114
- `${keypath} has been removed. You can set it inside the top level +layout.js instead. See the PR for more information: https://github.com/sveltejs/kit/pull/6197`
114
+ `${keypath} has been removed. You can set \`export const csr = false\` inside the top level +layout.js instead. See the PR for more information: https://github.com/sveltejs/kit/pull/6197`
115
115
  ),
116
116
  router: error(
117
117
  (keypath) =>
118
- `${keypath} has been removed. You can set it inside the top level +layout.js instead. See the PR for more information: https://github.com/sveltejs/kit/pull/6197`
118
+ `${keypath} has been removed. You can set \`export const csr = false\` inside the top level +layout.js instead. See the PR for more information: https://github.com/sveltejs/kit/pull/6197`
119
119
  )
120
120
  }),
121
121
 
@@ -318,7 +318,10 @@ const options = object(
318
318
  ),
319
319
 
320
320
  // TODO remove for 1.0
321
- router: error((keypath) => `${keypath} has been moved to config.kit.browser.router`),
321
+ router: error(
322
+ (keypath) =>
323
+ `${keypath} has been removed. You can set \`export const csr = false\` inside the top level +layout.js instead. See the PR for more information: https://github.com/sveltejs/kit/pull/6197`
324
+ ),
322
325
 
323
326
  // TODO remove for 1.0
324
327
  routes: error(
@@ -102,7 +102,33 @@ function create_routes_and_nodes(cwd, config, fallback) {
102
102
  * @param {import('types').RouteData | null} parent
103
103
  */
104
104
  const walk = (depth, id, segment, parent) => {
105
- if (/\]\[/.test(id)) {
105
+ const unescaped = id.replace(/\[([ux])\+([^\]]+)\]/gi, (match, type, code) => {
106
+ if (match !== match.toLowerCase()) {
107
+ throw new Error(`Character escape sequence in ${id} must be lowercase`);
108
+ }
109
+
110
+ if (!/[0-9a-f]+/.test(code)) {
111
+ throw new Error(`Invalid character escape sequence in ${id}`);
112
+ }
113
+
114
+ if (type === 'x') {
115
+ if (code.length !== 2) {
116
+ throw new Error(`Hexadecimal escape sequence in ${id} must be two characters`);
117
+ }
118
+
119
+ return String.fromCharCode(parseInt(code, 16));
120
+ } else {
121
+ if (code.length < 4 || code.length > 6) {
122
+ throw new Error(
123
+ `Unicode escape sequence in ${id} must be between four and six characters`
124
+ );
125
+ }
126
+
127
+ return String.fromCharCode(parseInt(code, 16));
128
+ }
129
+ });
130
+
131
+ if (/\]\[/.test(unescaped)) {
106
132
  throw new Error(`Invalid route ${id} — parameters must be separated`);
107
133
  }
108
134
 
@@ -110,6 +136,11 @@ function create_routes_and_nodes(cwd, config, fallback) {
110
136
  throw new Error(`Invalid route ${id} — brackets are unbalanced`);
111
137
  }
112
138
 
139
+ if (/#/.test(segment)) {
140
+ // Vite will barf on files with # in them
141
+ throw new Error(`Route ${id} should be renamed to ${id.replace(/#/g, '[x+23]')}`);
142
+ }
143
+
113
144
  if (/\[\.\.\.\w+\]\/\[\[/.test(id)) {
114
145
  throw new Error(
115
146
  `Invalid route ${id} — an [[optional]] route segment cannot follow a [...rest] route segment`
@@ -469,6 +500,10 @@ function normalize_route_id(id) {
469
500
  // remove groups
470
501
  .replace(/(?<=^|\/)\(.+?\)(?=$|\/)/g, '')
471
502
 
503
+ .replace(/\[[ux]\+([0-9a-f]+)\]/g, (_, x) =>
504
+ String.fromCharCode(parseInt(x, 16)).replace(/\//g, '%2f')
505
+ )
506
+
472
507
  // replace `[param]` with `<*>`, `[param=x]` with `<x>`, and `[[param]]` with `<?*>`
473
508
  .replace(
474
509
  /\[(?:(\[)|(\.\.\.))?.+?(=.+?)?\]\]?/g,
@@ -1,6 +1,9 @@
1
1
  const param_pattern = /^(\[)?(\.\.\.)?(\w+)(?:=(\w+))?(\])?$/;
2
2
 
3
- /** @param {string} id */
3
+ /**
4
+ * Creates the regex pattern, extracts parameter names, and generates types for a route
5
+ * @param {string} id
6
+ */
4
7
  export function parse_route_id(id) {
5
8
  /** @type {string[]} */
6
9
  const names = [];
@@ -21,9 +24,8 @@ export function parse_route_id(id) {
21
24
  : new RegExp(
22
25
  `^${get_route_segments(id)
23
26
  .map((segment, i, segments) => {
24
- const decoded_segment = decodeURIComponent(segment);
25
27
  // special case — /[...rest]/ could contain zero segments
26
- const rest_match = /^\[\.\.\.(\w+)(?:=(\w+))?\]$/.exec(decoded_segment);
28
+ const rest_match = /^\[\.\.\.(\w+)(?:=(\w+))?\]$/.exec(segment);
27
29
  if (rest_match) {
28
30
  names.push(rest_match[1]);
29
31
  types.push(rest_match[2]);
@@ -31,7 +33,7 @@ export function parse_route_id(id) {
31
33
  return '(?:/(.*))?';
32
34
  }
33
35
  // special case — /[[optional]]/ could contain zero segments
34
- const optional_match = /^\[\[(\w+)(?:=(\w+))?\]\]$/.exec(decoded_segment);
36
+ const optional_match = /^\[\[(\w+)(?:=(\w+))?\]\]$/.exec(segment);
35
37
  if (optional_match) {
36
38
  names.push(optional_match[1]);
37
39
  types.push(optional_match[2]);
@@ -41,14 +43,29 @@ export function parse_route_id(id) {
41
43
 
42
44
  const is_last = i === segments.length - 1;
43
45
 
44
- if (!decoded_segment) {
46
+ if (!segment) {
45
47
  return;
46
48
  }
47
49
 
48
- const parts = decoded_segment.split(/\[(.+?)\](?!\])/);
50
+ const parts = segment.split(/\[(.+?)\](?!\])/);
49
51
  const result = parts
50
52
  .map((content, i) => {
51
53
  if (i % 2) {
54
+ if (content.startsWith('x+')) {
55
+ return escape(String.fromCharCode(parseInt(content.slice(2), 16)));
56
+ }
57
+
58
+ if (content.startsWith('u+')) {
59
+ return escape(
60
+ String.fromCharCode(
61
+ ...content
62
+ .slice(2)
63
+ .split('-')
64
+ .map((code) => parseInt(code, 16))
65
+ )
66
+ );
67
+ }
68
+
52
69
  const match = param_pattern.exec(content);
53
70
  if (!match) {
54
71
  throw new Error(
@@ -69,18 +86,7 @@ export function parse_route_id(id) {
69
86
 
70
87
  if (is_last && content.includes('.')) add_trailing_slash = false;
71
88
 
72
- return (
73
- content // allow users to specify characters on the file system in an encoded manner
74
- .normalize()
75
- // '#', '/', and '?' can only appear in URL path segments in an encoded manner.
76
- // They will not be touched by decodeURI so need to be encoded here, so
77
- // that we can match against them.
78
- // We skip '/' since you can't create a file with it on any OS
79
- .replace(/#/g, '%23')
80
- .replace(/\?/g, '%3F')
81
- // escape characters that have special meaning in regex
82
- .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
83
- ); // TODO handle encoding
89
+ return escape(content);
84
90
  })
85
91
  .join('');
86
92
 
@@ -143,3 +149,20 @@ export function exec(match, { names, types, optional }, matchers) {
143
149
 
144
150
  return params;
145
151
  }
152
+
153
+ /** @param {string} str */
154
+ function escape(str) {
155
+ return (
156
+ str
157
+ .normalize()
158
+ // escape [ and ] before escaping other characters, since they are used in the replacements
159
+ .replace(/[[\]]/g, '\\$&')
160
+ // replace %, /, ? and # with their encoded versions because decode_pathname leaves them untouched
161
+ .replace(/%/g, '%25')
162
+ .replace(/\//g, '%2[Ff]')
163
+ .replace(/\?/g, '%3[Ff]')
164
+ .replace(/#/g, '%23')
165
+ // escape characters that have special meaning in regex
166
+ .replace(/[.*+?^${}()|\\]/g, '\\$&')
167
+ );
168
+ }
@@ -260,7 +260,7 @@ declare module '$app/navigation' {
260
260
  * A navigation interceptor that triggers before we navigate to a new URL, whether by clicking a link, calling `goto(...)`, or using the browser back/forward controls.
261
261
  * Calling `cancel()` will prevent the navigation from completing.
262
262
  *
263
- * When a navigation isn't client side, `navigation.to.routeId` will be `null`.
263
+ * When a navigation isn't client side, `navigation.to.route.id` will be `null`.
264
264
  *
265
265
  * `beforeNavigate` must be called during a component initialization. It remains active as long as the component is mounted.
266
266
  */