@sveltejs/kit 1.0.0-next.43 → 1.0.0-next.430
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 +12 -9
- package/package.json +95 -63
- package/src/cli.js +112 -0
- package/src/core/adapt/builder.js +207 -0
- package/src/core/adapt/index.js +19 -0
- package/src/core/config/index.js +86 -0
- package/src/core/config/options.js +488 -0
- package/src/core/config/types.d.ts +1 -0
- package/src/core/constants.js +5 -0
- package/src/core/env.js +97 -0
- package/src/core/generate_manifest/index.js +99 -0
- package/src/core/prerender/crawl.js +194 -0
- package/src/core/prerender/prerender.js +378 -0
- package/src/core/prerender/queue.js +80 -0
- package/src/core/sync/create_manifest_data/index.js +506 -0
- package/src/core/sync/create_manifest_data/types.d.ts +40 -0
- package/src/core/sync/sync.js +59 -0
- package/src/core/sync/utils.js +44 -0
- package/src/core/sync/write_ambient.js +27 -0
- package/src/core/sync/write_client_manifest.js +82 -0
- package/src/core/sync/write_matchers.js +25 -0
- package/src/core/sync/write_root.js +91 -0
- package/src/core/sync/write_tsconfig.js +195 -0
- package/src/core/sync/write_types.js +775 -0
- package/src/core/utils.js +70 -0
- package/src/hooks.js +26 -0
- package/src/index/index.js +45 -0
- package/src/index/private.js +33 -0
- package/src/node/index.js +145 -0
- package/src/node/polyfills.js +40 -0
- package/src/runtime/app/env.js +11 -0
- package/src/runtime/app/navigation.js +22 -0
- package/src/runtime/app/paths.js +1 -0
- package/src/runtime/app/stores.js +102 -0
- package/src/runtime/client/ambient.d.ts +17 -0
- package/src/runtime/client/client.js +1289 -0
- package/src/runtime/client/fetcher.js +60 -0
- package/src/runtime/client/parse.js +36 -0
- package/src/runtime/client/singletons.js +21 -0
- package/src/runtime/client/start.js +46 -0
- package/src/runtime/client/types.d.ts +105 -0
- package/src/runtime/client/utils.js +113 -0
- package/src/runtime/components/error.svelte +16 -0
- package/{assets → src/runtime}/components/layout.svelte +0 -0
- package/src/runtime/env/dynamic/private.js +1 -0
- package/src/runtime/env/dynamic/public.js +1 -0
- package/src/runtime/env-private.js +7 -0
- package/src/runtime/env-public.js +7 -0
- package/src/runtime/env.js +6 -0
- package/src/runtime/hash.js +16 -0
- package/src/runtime/paths.js +11 -0
- package/src/runtime/server/endpoint.js +58 -0
- package/src/runtime/server/index.js +448 -0
- package/src/runtime/server/page/cookie.js +25 -0
- package/src/runtime/server/page/crypto.js +239 -0
- package/src/runtime/server/page/csp.js +249 -0
- package/src/runtime/server/page/fetch.js +266 -0
- package/src/runtime/server/page/index.js +416 -0
- package/src/runtime/server/page/load_data.js +135 -0
- package/src/runtime/server/page/render.js +362 -0
- package/src/runtime/server/page/respond_with_error.js +94 -0
- package/src/runtime/server/page/types.d.ts +44 -0
- package/src/runtime/server/utils.js +116 -0
- package/src/utils/error.js +22 -0
- package/src/utils/escape.js +104 -0
- package/src/utils/filesystem.js +108 -0
- package/src/utils/http.js +55 -0
- package/src/utils/misc.js +1 -0
- package/src/utils/routing.js +108 -0
- package/src/utils/url.js +97 -0
- package/src/vite/build/build_server.js +337 -0
- package/src/vite/build/build_service_worker.js +90 -0
- package/src/vite/build/utils.js +160 -0
- package/src/vite/dev/index.js +551 -0
- package/src/vite/index.js +574 -0
- package/src/vite/preview/index.js +186 -0
- package/src/vite/types.d.ts +3 -0
- package/src/vite/utils.js +345 -0
- package/svelte-kit.js +1 -1
- package/types/ambient.d.ts +357 -0
- package/types/index.d.ts +343 -0
- package/types/internal.d.ts +308 -0
- package/types/private.d.ts +209 -0
- package/CHANGELOG.md +0 -431
- package/assets/components/error.svelte +0 -13
- package/assets/runtime/app/env.js +0 -5
- package/assets/runtime/app/navigation.js +0 -41
- package/assets/runtime/app/paths.js +0 -1
- package/assets/runtime/app/stores.js +0 -93
- package/assets/runtime/chunks/utils.js +0 -19
- package/assets/runtime/internal/singletons.js +0 -23
- package/assets/runtime/internal/start.js +0 -770
- package/assets/runtime/paths.js +0 -12
- package/dist/.DS_Store +0 -0
- package/dist/chunks/index.js +0 -3521
- package/dist/chunks/index2.js +0 -587
- package/dist/chunks/index3.js +0 -246
- package/dist/chunks/index4.js +0 -538
- package/dist/chunks/index5.js +0 -761
- package/dist/chunks/index6.js +0 -322
- package/dist/chunks/standard.js +0 -99
- package/dist/chunks/utils.js +0 -83
- package/dist/cli.js +0 -546
- package/dist/ssr.js +0 -2581
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inside a script element, only `</script` and `<!--` hold special meaning to the HTML parser.
|
|
3
|
+
*
|
|
4
|
+
* The first closes the script element, so everything after is treated as raw HTML.
|
|
5
|
+
* The second disables further parsing until `-->`, so the script element might be unexpectedly
|
|
6
|
+
* kept open until until an unrelated HTML comment in the page.
|
|
7
|
+
*
|
|
8
|
+
* U+2028 LINE SEPARATOR and U+2029 PARAGRAPH SEPARATOR are escaped for the sake of pre-2018
|
|
9
|
+
* browsers.
|
|
10
|
+
*
|
|
11
|
+
* @see tests for unsafe parsing examples.
|
|
12
|
+
* @see https://html.spec.whatwg.org/multipage/scripting.html#restrictions-for-contents-of-script-elements
|
|
13
|
+
* @see https://html.spec.whatwg.org/multipage/syntax.html#cdata-rcdata-restrictions
|
|
14
|
+
* @see https://html.spec.whatwg.org/multipage/parsing.html#script-data-state
|
|
15
|
+
* @see https://html.spec.whatwg.org/multipage/parsing.html#script-data-double-escaped-state
|
|
16
|
+
* @see https://github.com/tc39/proposal-json-superset
|
|
17
|
+
* @type {Record<string, string>}
|
|
18
|
+
*/
|
|
19
|
+
const render_json_payload_script_dict = {
|
|
20
|
+
'<': '\\u003C',
|
|
21
|
+
'\u2028': '\\u2028',
|
|
22
|
+
'\u2029': '\\u2029'
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const render_json_payload_script_regex = new RegExp(
|
|
26
|
+
`[${Object.keys(render_json_payload_script_dict).join('')}]`,
|
|
27
|
+
'g'
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Generates a raw HTML string containing a safe script element carrying JSON data and associated attributes.
|
|
32
|
+
*
|
|
33
|
+
* It escapes all the special characters needed to guarantee the element is unbroken, but care must
|
|
34
|
+
* be taken to ensure it is inserted in the document at an acceptable position for a script element,
|
|
35
|
+
* and that the resulting string isn't further modified.
|
|
36
|
+
*
|
|
37
|
+
* Attribute names must be type-checked so we don't need to escape them.
|
|
38
|
+
*
|
|
39
|
+
* @param {import('types').PayloadScriptAttributes} attrs A list of attributes to be added to the element.
|
|
40
|
+
* @param {any} payload The data to be carried by the element. Must be serializable to JSON.
|
|
41
|
+
* @returns {string} The raw HTML of a script element carrying the JSON payload.
|
|
42
|
+
* @example const html = render_json_payload_script({ type: 'data', url: '/data.json' }, { foo: 'bar' });
|
|
43
|
+
*/
|
|
44
|
+
export function render_json_payload_script(attrs, payload) {
|
|
45
|
+
const safe_payload = JSON.stringify(payload).replace(
|
|
46
|
+
render_json_payload_script_regex,
|
|
47
|
+
(match) => render_json_payload_script_dict[match]
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
let safe_attrs = '';
|
|
51
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
52
|
+
if (value === undefined) continue;
|
|
53
|
+
safe_attrs += ` sveltekit:data-${key}=${escape_html_attr(value)}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return `<script type="application/json"${safe_attrs}>${safe_payload}</script>`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* When inside a double-quoted attribute value, only `&` and `"` hold special meaning.
|
|
61
|
+
* @see https://html.spec.whatwg.org/multipage/parsing.html#attribute-value-(double-quoted)-state
|
|
62
|
+
* @type {Record<string, string>}
|
|
63
|
+
*/
|
|
64
|
+
const escape_html_attr_dict = {
|
|
65
|
+
'&': '&',
|
|
66
|
+
'"': '"'
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const escape_html_attr_regex = new RegExp(
|
|
70
|
+
// special characters
|
|
71
|
+
`[${Object.keys(escape_html_attr_dict).join('')}]|` +
|
|
72
|
+
// high surrogate without paired low surrogate
|
|
73
|
+
'[\\ud800-\\udbff](?![\\udc00-\\udfff])|' +
|
|
74
|
+
// a valid surrogate pair, the only match with 2 code units
|
|
75
|
+
// we match it so that we can match unpaired low surrogates in the same pass
|
|
76
|
+
// TODO: use lookbehind assertions once they are widely supported: (?<![\ud800-udbff])[\udc00-\udfff]
|
|
77
|
+
'[\\ud800-\\udbff][\\udc00-\\udfff]|' +
|
|
78
|
+
// unpaired low surrogate (see previous match)
|
|
79
|
+
'[\\udc00-\\udfff]',
|
|
80
|
+
'g'
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Formats a string to be used as an attribute's value in raw HTML.
|
|
85
|
+
*
|
|
86
|
+
* It escapes unpaired surrogates (which are allowed in js strings but invalid in HTML), escapes
|
|
87
|
+
* characters that are special in attributes, and surrounds the whole string in double-quotes.
|
|
88
|
+
*
|
|
89
|
+
* @param {string} str
|
|
90
|
+
* @returns {string} Escaped string surrounded by double-quotes.
|
|
91
|
+
* @example const html = `<tag data-value=${escape_html_attr('value')}>...</tag>`;
|
|
92
|
+
*/
|
|
93
|
+
export function escape_html_attr(str) {
|
|
94
|
+
const escaped_str = str.replace(escape_html_attr_regex, (match) => {
|
|
95
|
+
if (match.length === 2) {
|
|
96
|
+
// valid surrogate pair
|
|
97
|
+
return match;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return escape_html_attr_dict[match] ?? `&#${match.charCodeAt(0)};`;
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
return `"${escaped_str}"`;
|
|
104
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
/** @param {string} dir */
|
|
5
|
+
export function mkdirp(dir) {
|
|
6
|
+
try {
|
|
7
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
8
|
+
} catch (/** @type {any} */ e) {
|
|
9
|
+
if (e.code === 'EEXIST') return;
|
|
10
|
+
throw e;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** @param {string} path */
|
|
15
|
+
export function rimraf(path) {
|
|
16
|
+
fs.rmSync(path, { force: true, recursive: true });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {string} source
|
|
21
|
+
* @param {string} target
|
|
22
|
+
* @param {{
|
|
23
|
+
* filter?: (basename: string) => boolean;
|
|
24
|
+
* replace?: Record<string, string>;
|
|
25
|
+
* }} opts
|
|
26
|
+
*/
|
|
27
|
+
export function copy(source, target, opts = {}) {
|
|
28
|
+
if (!fs.existsSync(source)) return [];
|
|
29
|
+
|
|
30
|
+
/** @type {string[]} */
|
|
31
|
+
const files = [];
|
|
32
|
+
|
|
33
|
+
const prefix = posixify(target) + '/';
|
|
34
|
+
|
|
35
|
+
const regex = opts.replace
|
|
36
|
+
? new RegExp(`\\b(${Object.keys(opts.replace).join('|')})\\b`, 'g')
|
|
37
|
+
: null;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {string} from
|
|
41
|
+
* @param {string} to
|
|
42
|
+
*/
|
|
43
|
+
function go(from, to) {
|
|
44
|
+
if (opts.filter && !opts.filter(path.basename(from))) return;
|
|
45
|
+
|
|
46
|
+
const stats = fs.statSync(from);
|
|
47
|
+
|
|
48
|
+
if (stats.isDirectory()) {
|
|
49
|
+
fs.readdirSync(from).forEach((file) => {
|
|
50
|
+
go(path.join(from, file), path.join(to, file));
|
|
51
|
+
});
|
|
52
|
+
} else {
|
|
53
|
+
mkdirp(path.dirname(to));
|
|
54
|
+
|
|
55
|
+
if (opts.replace) {
|
|
56
|
+
const data = fs.readFileSync(from, 'utf-8');
|
|
57
|
+
fs.writeFileSync(
|
|
58
|
+
to,
|
|
59
|
+
data.replace(
|
|
60
|
+
/** @type {RegExp} */ (regex),
|
|
61
|
+
(_match, key) => /** @type {Record<string, string>} */ (opts.replace)[key]
|
|
62
|
+
)
|
|
63
|
+
);
|
|
64
|
+
} else {
|
|
65
|
+
fs.copyFileSync(from, to);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
files.push(to === target ? posixify(path.basename(to)) : posixify(to).replace(prefix, ''));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
go(source, target);
|
|
73
|
+
|
|
74
|
+
return files;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Get a list of all files in a directory
|
|
79
|
+
* @param {string} cwd - the directory to walk
|
|
80
|
+
* @param {boolean} [dirs] - whether to include directories in the result
|
|
81
|
+
*/
|
|
82
|
+
export function walk(cwd, dirs = false) {
|
|
83
|
+
/** @type {string[]} */
|
|
84
|
+
const all_files = [];
|
|
85
|
+
|
|
86
|
+
/** @param {string} dir */
|
|
87
|
+
function walk_dir(dir) {
|
|
88
|
+
const files = fs.readdirSync(path.join(cwd, dir));
|
|
89
|
+
|
|
90
|
+
for (const file of files) {
|
|
91
|
+
const joined = path.join(dir, file);
|
|
92
|
+
const stats = fs.statSync(path.join(cwd, joined));
|
|
93
|
+
if (stats.isDirectory()) {
|
|
94
|
+
if (dirs) all_files.push(joined);
|
|
95
|
+
walk_dir(joined);
|
|
96
|
+
} else {
|
|
97
|
+
all_files.push(joined);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return walk_dir(''), all_files;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** @param {string} str */
|
|
106
|
+
export function posixify(str) {
|
|
107
|
+
return str.replace(/\\/g, '/');
|
|
108
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Given an Accept header and a list of possible content types, pick
|
|
3
|
+
* the most suitable one to respond with
|
|
4
|
+
* @param {string} accept
|
|
5
|
+
* @param {string[]} types
|
|
6
|
+
*/
|
|
7
|
+
export function negotiate(accept, types) {
|
|
8
|
+
/** @type {Array<{ type: string, subtype: string, q: number, i: number }>} */
|
|
9
|
+
const parts = [];
|
|
10
|
+
|
|
11
|
+
accept.split(',').forEach((str, i) => {
|
|
12
|
+
const match = /([^/]+)\/([^;]+)(?:;q=([0-9.]+))?/.exec(str);
|
|
13
|
+
|
|
14
|
+
// no match equals invalid header — ignore
|
|
15
|
+
if (match) {
|
|
16
|
+
const [, type, subtype, q = '1'] = match;
|
|
17
|
+
parts.push({ type, subtype, q: +q, i });
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
parts.sort((a, b) => {
|
|
22
|
+
if (a.q !== b.q) {
|
|
23
|
+
return b.q - a.q;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if ((a.subtype === '*') !== (b.subtype === '*')) {
|
|
27
|
+
return a.subtype === '*' ? 1 : -1;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if ((a.type === '*') !== (b.type === '*')) {
|
|
31
|
+
return a.type === '*' ? 1 : -1;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return a.i - b.i;
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
let accepted;
|
|
38
|
+
let min_priority = Infinity;
|
|
39
|
+
|
|
40
|
+
for (const mimetype of types) {
|
|
41
|
+
const [type, subtype] = mimetype.split('/');
|
|
42
|
+
const priority = parts.findIndex(
|
|
43
|
+
(part) =>
|
|
44
|
+
(part.type === type || part.type === '*') &&
|
|
45
|
+
(part.subtype === subtype || part.subtype === '*')
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
if (priority !== -1 && priority < min_priority) {
|
|
49
|
+
accepted = mimetype;
|
|
50
|
+
min_priority = priority;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return accepted;
|
|
55
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const s = JSON.stringify;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
const param_pattern = /^(\.\.\.)?(\w+)(?:=(\w+))?$/;
|
|
2
|
+
|
|
3
|
+
/** @param {string} id */
|
|
4
|
+
export function parse_route_id(id) {
|
|
5
|
+
/** @type {string[]} */
|
|
6
|
+
const names = [];
|
|
7
|
+
|
|
8
|
+
/** @type {string[]} */
|
|
9
|
+
const types = [];
|
|
10
|
+
|
|
11
|
+
// `/foo` should get an optional trailing slash, `/foo.json` should not
|
|
12
|
+
// const add_trailing_slash = !/\.[a-z]+$/.test(key);
|
|
13
|
+
let add_trailing_slash = true;
|
|
14
|
+
|
|
15
|
+
const pattern =
|
|
16
|
+
id === ''
|
|
17
|
+
? /^\/$/
|
|
18
|
+
: new RegExp(
|
|
19
|
+
`^${id
|
|
20
|
+
.split(/(?:@[a-zA-Z0-9_-]+)?(?:\/|$)/)
|
|
21
|
+
.map((segment, i, segments) => {
|
|
22
|
+
const decoded_segment = decodeURIComponent(segment);
|
|
23
|
+
// special case — /[...rest]/ could contain zero segments
|
|
24
|
+
const match = /^\[\.\.\.(\w+)(?:=(\w+))?\]$/.exec(decoded_segment);
|
|
25
|
+
if (match) {
|
|
26
|
+
names.push(match[1]);
|
|
27
|
+
types.push(match[2]);
|
|
28
|
+
return '(?:/(.*))?';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const is_last = i === segments.length - 1;
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
decoded_segment &&
|
|
35
|
+
'/' +
|
|
36
|
+
decoded_segment
|
|
37
|
+
.split(/\[(.+?)\]/)
|
|
38
|
+
.map((content, i) => {
|
|
39
|
+
if (i % 2) {
|
|
40
|
+
const match = param_pattern.exec(content);
|
|
41
|
+
if (!match) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
`Invalid param: ${content}. Params and matcher names can only have underscores and alphanumeric characters.`
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const [, rest, name, type] = match;
|
|
48
|
+
names.push(name);
|
|
49
|
+
types.push(type);
|
|
50
|
+
return rest ? '(.*?)' : '([^/]+?)';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (is_last && content.includes('.')) add_trailing_slash = false;
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
content // allow users to specify characters on the file system in an encoded manner
|
|
57
|
+
.normalize()
|
|
58
|
+
// We use [ and ] to denote parameters, so users must encode these on the file
|
|
59
|
+
// system to match against them. We don't decode all characters since others
|
|
60
|
+
// can already be epressed and so that '%' can be easily used directly in filenames
|
|
61
|
+
.replace(/%5[Bb]/g, '[')
|
|
62
|
+
.replace(/%5[Dd]/g, ']')
|
|
63
|
+
// '#', '/', and '?' can only appear in URL path segments in an encoded manner.
|
|
64
|
+
// They will not be touched by decodeURI so need to be encoded here, so
|
|
65
|
+
// that we can match against them.
|
|
66
|
+
// We skip '/' since you can't create a file with it on any OS
|
|
67
|
+
.replace(/#/g, '%23')
|
|
68
|
+
.replace(/\?/g, '%3F')
|
|
69
|
+
// escape characters that have special meaning in regex
|
|
70
|
+
.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
71
|
+
); // TODO handle encoding
|
|
72
|
+
})
|
|
73
|
+
.join('')
|
|
74
|
+
);
|
|
75
|
+
})
|
|
76
|
+
.join('')}${add_trailing_slash ? '/?' : ''}$`
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
return { pattern, names, types };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @param {RegExpMatchArray} match
|
|
84
|
+
* @param {string[]} names
|
|
85
|
+
* @param {string[]} types
|
|
86
|
+
* @param {Record<string, import('types').ParamMatcher>} matchers
|
|
87
|
+
*/
|
|
88
|
+
export function exec(match, names, types, matchers) {
|
|
89
|
+
/** @type {Record<string, string>} */
|
|
90
|
+
const params = {};
|
|
91
|
+
|
|
92
|
+
for (let i = 0; i < names.length; i += 1) {
|
|
93
|
+
const name = names[i];
|
|
94
|
+
const type = types[i];
|
|
95
|
+
const value = match[i + 1] || '';
|
|
96
|
+
|
|
97
|
+
if (type) {
|
|
98
|
+
const matcher = matchers[type];
|
|
99
|
+
if (!matcher) throw new Error(`Missing "${type}" param matcher`); // TODO do this ahead of time?
|
|
100
|
+
|
|
101
|
+
if (!matcher(value)) return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
params[name] = value;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return params;
|
|
108
|
+
}
|
package/src/utils/url.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
const absolute = /^([a-z]+:)?\/?\//;
|
|
2
|
+
const scheme = /^[a-z]+:/;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @param {string} base
|
|
6
|
+
* @param {string} path
|
|
7
|
+
*/
|
|
8
|
+
export function resolve(base, path) {
|
|
9
|
+
if (scheme.test(path)) return path;
|
|
10
|
+
|
|
11
|
+
const base_match = absolute.exec(base);
|
|
12
|
+
const path_match = absolute.exec(path);
|
|
13
|
+
|
|
14
|
+
if (!base_match) {
|
|
15
|
+
throw new Error(`bad base path: "${base}"`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const baseparts = path_match ? [] : base.slice(base_match[0].length).split('/');
|
|
19
|
+
const pathparts = path_match ? path.slice(path_match[0].length).split('/') : path.split('/');
|
|
20
|
+
|
|
21
|
+
baseparts.pop();
|
|
22
|
+
|
|
23
|
+
for (let i = 0; i < pathparts.length; i += 1) {
|
|
24
|
+
const part = pathparts[i];
|
|
25
|
+
if (part === '.') continue;
|
|
26
|
+
else if (part === '..') baseparts.pop();
|
|
27
|
+
else baseparts.push(part);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const prefix = (path_match && path_match[0]) || (base_match && base_match[0]) || '';
|
|
31
|
+
|
|
32
|
+
return `${prefix}${baseparts.join('/')}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** @param {string} path */
|
|
36
|
+
export function is_root_relative(path) {
|
|
37
|
+
return path[0] === '/' && path[1] !== '/';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @param {string} path
|
|
42
|
+
* @param {import('types').TrailingSlash} trailing_slash
|
|
43
|
+
*/
|
|
44
|
+
export function normalize_path(path, trailing_slash) {
|
|
45
|
+
if (path === '/' || trailing_slash === 'ignore') return path;
|
|
46
|
+
|
|
47
|
+
if (trailing_slash === 'never') {
|
|
48
|
+
return path.endsWith('/') ? path.slice(0, -1) : path;
|
|
49
|
+
} else if (trailing_slash === 'always' && !path.endsWith('/')) {
|
|
50
|
+
return path + '/';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return path;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** @param {Record<string, string>} params */
|
|
57
|
+
export function decode_params(params) {
|
|
58
|
+
for (const key in params) {
|
|
59
|
+
// input has already been decoded by decodeURI
|
|
60
|
+
// now handle the rest that decodeURIComponent would do
|
|
61
|
+
params[key] = params[key]
|
|
62
|
+
.replace(/%23/g, '#')
|
|
63
|
+
.replace(/%3[Bb]/g, ';')
|
|
64
|
+
.replace(/%2[Cc]/g, ',')
|
|
65
|
+
.replace(/%2[Ff]/g, '/')
|
|
66
|
+
.replace(/%3[Ff]/g, '?')
|
|
67
|
+
.replace(/%3[Aa]/g, ':')
|
|
68
|
+
.replace(/%40/g, '@')
|
|
69
|
+
.replace(/%26/g, '&')
|
|
70
|
+
.replace(/%3[Dd]/g, '=')
|
|
71
|
+
.replace(/%2[Bb]/g, '+')
|
|
72
|
+
.replace(/%24/g, '$');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return params;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export class LoadURL extends URL {
|
|
79
|
+
/** @returns {string} */
|
|
80
|
+
get hash() {
|
|
81
|
+
throw new Error(
|
|
82
|
+
'url.hash is inaccessible from load. Consider accessing hash from the page store within the script tag of your component.'
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export class PrerenderingURL extends URL {
|
|
88
|
+
/** @returns {string} */
|
|
89
|
+
get search() {
|
|
90
|
+
throw new Error('Cannot access url.search on a page with prerendering enabled');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** @returns {URLSearchParams} */
|
|
94
|
+
get searchParams() {
|
|
95
|
+
throw new Error('Cannot access url.searchParams on a page with prerendering enabled');
|
|
96
|
+
}
|
|
97
|
+
}
|