Skip to content

Commit 9c1a166

Browse files
committed
feat: add inject function
1 parent 5f601d6 commit 9c1a166

File tree

3 files changed

+98
-0
lines changed

3 files changed

+98
-0
lines changed

index.d.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,16 @@ export function parse(route: RegExp): {
77
keys: false;
88
pattern: RegExp;
99
}
10+
11+
export type RouteParams<T extends string> =
12+
T extends `${string}:${infer P}?/${infer Rest}`
13+
? { [K in P]?: string } & RouteParams<Rest>
14+
: T extends `${string}:${infer P}/${infer Rest}`
15+
? { [K in P]: string } & RouteParams<Rest>
16+
: T extends `${string}:${infer P}?`
17+
? { [K in P]?: string }
18+
: T extends `${string}:${infer P}`
19+
? { [K in P]: string }
20+
: {};
21+
22+
export function inject<T extends string>(route: T, values: RouteParams<T>): string;

src/index.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,13 @@ export function parse(str, loose) {
2424
pattern: new RegExp('^' + pattern + (loose ? '(?=$|\/)' : '\/?$'), 'i')
2525
};
2626
}
27+
28+
var RGX = /*#__PURE__*/ /(\/|^)([:*][^\/]*?)(\?)?(?=[\/.]|$)/g;
29+
30+
// error if key missing?
31+
export function inject(route, values) {
32+
return route.replace(RGX, (_, start, key, optional) => {
33+
if (_ = values[key.substring(1)]) return '/' + _;
34+
return optional ? '' : '/' + key;
35+
});
36+
}

test/inject.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { test } from 'uvu';
2+
import * as assert from 'uvu/assert';
3+
import { inject } from '../src';
4+
5+
/**
6+
* @param {string} pattern
7+
* @param {string} expected
8+
* @param {Record<string, any>} values
9+
*/
10+
function run(pattern, expected, values) {
11+
let keys = Object.keys(values);
12+
test(`inject "${pattern}" with [${keys}]`, () => {
13+
let output = inject(pattern, values);
14+
assert.type(output, 'string');
15+
assert.is(output, expected);
16+
});
17+
}
18+
19+
test('exports', () => {
20+
assert.type(inject, 'function');
21+
});
22+
23+
test('returns', () => {
24+
let output = inject('/', {});
25+
assert.type(output, 'string');
26+
});
27+
28+
test('throws', () => {
29+
try {
30+
inject('/:foo/:bar');
31+
assert.unreachable('should throw');
32+
} catch (err) {
33+
assert.instance(err, TypeError);
34+
assert.is(err.message, `Cannot read property 'foo' of undefined`);
35+
}
36+
});
37+
38+
// Test Outputs
39+
// ---
40+
41+
run('/foo/:id', '/foo/123', { id: 123 });
42+
run('/foo/:id/', '/foo/123/', { id: 123 });
43+
44+
run('/:a/:b/:c', '/1/2/3', { a: 1, b: 2, c: 3 });
45+
run('/:a/:b/:c/', '/1/2/3/', { a: 1, b: 2, c: 3 });
46+
47+
run('/assets/:video.mp4', '/assets/demo.mp4', { video: 'demo' });
48+
run('/assets/:video.mp4/extra', '/assets/demo.mp4/extra', { video: 'demo' });
49+
run('/assets/:video.mp4?foo=bar', '/assets/demo.mp4?foo=bar', { video: 'demo' });
50+
run('/assets/:video/.hidden', '/assets/demo/.hidden', { video: 'demo' });
51+
52+
run('/foo/:id/:bar?', '/foo/123', { id: 123 });
53+
run('/foo/:id/:bar?/', '/foo/123/', { id: 123 });
54+
55+
run('/foo/:id/:bar?', '/foo/123/xxx', { id: 123, bar: 'xxx' });
56+
run('/foo/:id/:bar?/', '/foo/123/xxx/', { id: 123, bar: 'xxx' });
57+
58+
run('/foo/:id/:bar?/extra', '/foo/123/extra', { id: 123 });
59+
run('/foo/:id/:bar?/extra', '/foo/123/xxx/extra', { id: 123, bar: 'xxx' });
60+
61+
run('/foo/:id/:a?/:b?/:bar?', '/foo/123', { id: 123 });
62+
run('/foo/:id/:a?/:b?/:bar?', '/foo/123/bb', { id: 123, b: 'bb' });
63+
run('/foo/:id/:a?/:b?/:bar?', '/foo/123/xxx', { id: 123, bar: 'xxx' });
64+
run('/foo/:id/:a?/:b?/:bar?', '/foo/123/aa/xxx', { id: 123, a: 'aa', bar: 'xxx' });
65+
66+
67+
// NOTE: Missing non-optional values
68+
// ---
69+
run('/foo/:id', '/foo/:id', { /* empty */ });
70+
run('/foo/:id/', '/foo/:id/', { /* empty */ });
71+
72+
run('/:a/:b/:c', '/1/:b/:c', { a: 1 });
73+
run('/:a/:b/:c', '/1/:b/3', { a: 1, c: 3 });
74+
75+
test.run();

0 commit comments

Comments
 (0)