@lightningjs/blits 0.6.10 → 0.6.12
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/CHANGELOG.md +16 -0
- package/bin/helpers.js +1 -1
- package/boilerplate/common/git/gitignore +5 -0
- package/package.json +12 -11
- package/src/component.d.ts +11 -1
- package/src/component.js +0 -6
- package/src/components/RouterView.js +2 -2
- package/src/components/RouterView.orig.js +2 -2
- package/src/lib/codegenerator/generator.js +23 -13
- package/src/lib/symbols.js +2 -0
- package/src/lib/templateparser/parser.js +8 -6
- package/src/router/router.js +72 -13
- package/src/router/router.test.js +238 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
# v0.6.12
|
|
4
|
+
|
|
5
|
+
_9 feb 2024_
|
|
6
|
+
|
|
7
|
+
- Added type hinting for `this.$clearInterval` and `this.$clearTimeout`
|
|
8
|
+
- Fixed issue with `.gitignore` in app create flow
|
|
9
|
+
- Added support for dynamic route parts in the router (i.e `/tv/:series/episodes/:episode`)
|
|
10
|
+
|
|
11
|
+
# v0.6.11
|
|
12
|
+
|
|
13
|
+
_7 feb 2024_
|
|
14
|
+
|
|
15
|
+
- Enabled the use of `type` as a component prop or state variable
|
|
16
|
+
- Removed setting focus to AppComponent on back key press in RouterView
|
|
17
|
+
- Fixed broken app create flow caused by missing dev dependencies in npx command (temporary fix)
|
|
18
|
+
|
|
3
19
|
# v0.6.10
|
|
4
20
|
|
|
5
21
|
_2 feb 2024_
|
package/bin/helpers.js
CHANGED
|
@@ -182,7 +182,7 @@ export const gitInit = (cwd, fixturesBase) => {
|
|
|
182
182
|
.then(({ stdout }) => (msg = stdout))
|
|
183
183
|
.then(() => {
|
|
184
184
|
return fs.copyFileSync(
|
|
185
|
-
path.join(fixturesBase, 'common/git
|
|
185
|
+
path.join(fixturesBase, 'common/git/gitignore'),
|
|
186
186
|
path.join(cwd, '.gitignore')
|
|
187
187
|
)
|
|
188
188
|
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightningjs/blits",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.12",
|
|
4
4
|
"description": "Blits: The Lightning 3 App Development Framework",
|
|
5
5
|
"bin": "bin/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
},
|
|
12
12
|
"scripts": {
|
|
13
13
|
"test": "c8 npm run test:run",
|
|
14
|
-
"test:run": "tape '{,!(node_modules)/**/}*.test.js' | tap-diff",
|
|
14
|
+
"test:run": "node -r 'global-jsdom/register' ./node_modules/.bin/tape '{,!(node_modules)/**/}*.test.js' | tap-diff",
|
|
15
15
|
"lint": "eslint '**/*.js'",
|
|
16
16
|
"lint:fix": "eslint '**/*.js' --fix",
|
|
17
17
|
"prepublishOnly": "node scripts/prepublishOnly.js",
|
|
@@ -36,22 +36,23 @@
|
|
|
36
36
|
"eslint": "^8.8.0",
|
|
37
37
|
"eslint-config-prettier": "^8.3.0",
|
|
38
38
|
"eslint-plugin-prettier": "^4.0.0",
|
|
39
|
-
"
|
|
40
|
-
"execa": "^8.0.1",
|
|
39
|
+
"global-jsdom": "24.0.0",
|
|
41
40
|
"husky": "^7.0.4",
|
|
42
|
-
"
|
|
41
|
+
"jsdom": "24.0.0",
|
|
43
42
|
"lint-staged": "^12.3.3",
|
|
44
|
-
"ora": "^7.0.1",
|
|
45
43
|
"prettier": "^2.5.1",
|
|
46
|
-
"prompts": "^2.4.2",
|
|
47
|
-
"replace-in-file": "^6.1.0",
|
|
48
44
|
"tap-diff": "^0.1.1",
|
|
49
|
-
"tape": "^5.5.0"
|
|
50
|
-
"validate-npm-package-name": "^5.0.0"
|
|
45
|
+
"tape": "^5.5.0"
|
|
51
46
|
},
|
|
52
47
|
"dependencies": {
|
|
53
48
|
"@lightningjs/renderer": "^0.7.1",
|
|
54
|
-
"@lightningjs/vite-plugin-import-chunk-url": "^0.3.0"
|
|
49
|
+
"@lightningjs/vite-plugin-import-chunk-url": "^0.3.0",
|
|
50
|
+
"execa": "^8.0.1",
|
|
51
|
+
"kolorist": "^1.8.0",
|
|
52
|
+
"ora": "^7.0.1",
|
|
53
|
+
"prompts": "^2.4.2",
|
|
54
|
+
"replace-in-file": "^6.1.0",
|
|
55
|
+
"validate-npm-package-name": "^5.0.0"
|
|
55
56
|
},
|
|
56
57
|
"repository": {
|
|
57
58
|
"type": "git",
|
package/src/component.d.ts
CHANGED
|
@@ -142,12 +142,22 @@ declare namespace Component {
|
|
|
142
142
|
* Set a timeout that is automatically cleaned upon component destroy
|
|
143
143
|
*/
|
|
144
144
|
$setTimeout: (callback: (args: any) => void, ms?: number | undefined) => ReturnType<typeof setTimeout>
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Clear a timeout
|
|
148
|
+
*/
|
|
149
|
+
$clearTimeout: (id: ReturnType<typeof setTimeout>) => void
|
|
145
150
|
|
|
146
151
|
/**
|
|
147
152
|
* Set an interval that is automatically cleaned upon component destroy
|
|
148
153
|
*/
|
|
149
154
|
$setInterval: (callback: (args: any) => void, ms?: number | undefined) => ReturnType<typeof setInterval>
|
|
150
|
-
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Clear a interval
|
|
158
|
+
*/
|
|
159
|
+
$clearInterval: (id: ReturnType<typeof setInterval>) => void
|
|
160
|
+
|
|
151
161
|
/**
|
|
152
162
|
* Log to the console with prettier output and configurable debug levels in Settings
|
|
153
163
|
*/
|
package/src/component.js
CHANGED
|
@@ -122,12 +122,6 @@ const Component = (name = required('name'), config = required('config')) => {
|
|
|
122
122
|
this[symbols.wrapper] = parentEl
|
|
123
123
|
|
|
124
124
|
Object.defineProperties(this, {
|
|
125
|
-
type: {
|
|
126
|
-
value: name,
|
|
127
|
-
writable: false,
|
|
128
|
-
enumerable: true,
|
|
129
|
-
configurable: false,
|
|
130
|
-
},
|
|
131
125
|
componentId: {
|
|
132
126
|
value: createHumanReadableId(name),
|
|
133
127
|
writable: false,
|
|
@@ -64,14 +64,11 @@ const generateElementCode = function (
|
|
|
64
64
|
const children = templateObject['children']
|
|
65
65
|
delete templateObject['children']
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
renderCode.push(`elementConfig${counter}[Symbol.for('isSlot')] = true`)
|
|
71
|
-
}
|
|
72
|
-
return
|
|
73
|
-
}
|
|
67
|
+
if (templateObject[Symbol.for('componentType')] === 'Slot') {
|
|
68
|
+
renderCode.push(`elementConfig${counter}[Symbol.for('isSlot')] = true`)
|
|
69
|
+
}
|
|
74
70
|
|
|
71
|
+
Object.keys(templateObject).forEach((key) => {
|
|
75
72
|
if (key === 'slot') {
|
|
76
73
|
renderCode.push(`
|
|
77
74
|
elementConfig${counter}['parent'] = component[Symbol.for('slots')].filter(slot => slot.ref === '${templateObject.slot}').shift() || component[Symbol.for('slots')][0] || parent
|
|
@@ -119,8 +116,10 @@ const generateComponentCode = function (
|
|
|
119
116
|
|
|
120
117
|
renderCode.push(`
|
|
121
118
|
const cmp${counter} =
|
|
122
|
-
(context.components && context.components['${
|
|
123
|
-
|
|
119
|
+
(context.components && context.components['${
|
|
120
|
+
templateObject[Symbol.for('componentType')]
|
|
121
|
+
}']) ||
|
|
122
|
+
component[Symbol.for('components')]['${templateObject[Symbol.for('componentType')]}']
|
|
124
123
|
`)
|
|
125
124
|
|
|
126
125
|
if ('key' in templateObject) {
|
|
@@ -149,7 +148,6 @@ const generateComponentCode = function (
|
|
|
149
148
|
|
|
150
149
|
renderCode.push(`const props${counter} = {}`)
|
|
151
150
|
Object.keys(templateObject).forEach((key) => {
|
|
152
|
-
if (key === 'type') return
|
|
153
151
|
if (isReactiveKey(key)) {
|
|
154
152
|
this.effectsCode.push(`
|
|
155
153
|
${elm}[Symbol.for('props')]['${key.substring(1)}'] = ${interpolate(
|
|
@@ -171,7 +169,13 @@ const generateComponentCode = function (
|
|
|
171
169
|
|
|
172
170
|
renderCode.push(`
|
|
173
171
|
if(!${elm}) {
|
|
174
|
-
${elm} = (context.components && context.components['${
|
|
172
|
+
${elm} = (context.components && context.components['${
|
|
173
|
+
templateObject[Symbol.for('componentType')]
|
|
174
|
+
}'] || component[Symbol.for('components')]['${
|
|
175
|
+
templateObject[Symbol.for('componentType')]
|
|
176
|
+
}'] || (() => { console.error('component ${
|
|
177
|
+
templateObject[Symbol.for('componentType')]
|
|
178
|
+
} not found')})).call(null, {props: props${counter}}, ${parent}, component)
|
|
175
179
|
if (${elm}[Symbol.for('slots')][0]) {
|
|
176
180
|
parent = ${elm}[Symbol.for('slots')][0]
|
|
177
181
|
component = ${elm}
|
|
@@ -245,7 +249,10 @@ const generateForLoopCode = function (templateObject, parent) {
|
|
|
245
249
|
`)
|
|
246
250
|
}
|
|
247
251
|
|
|
248
|
-
if (
|
|
252
|
+
if (
|
|
253
|
+
templateObject[Symbol.for('componentType')] === 'Element' ||
|
|
254
|
+
templateObject[Symbol.for('componentType')] === 'Slot'
|
|
255
|
+
) {
|
|
249
256
|
generateElementCode.call(ctx, templateObject, parent, {
|
|
250
257
|
key: 'scope.key',
|
|
251
258
|
component: 'scope.',
|
|
@@ -283,7 +290,10 @@ const generateCode = function (templateObject, parent = false, options = {}) {
|
|
|
283
290
|
if (Object.keys(childTemplateObject).indexOf(':for') > -1) {
|
|
284
291
|
generateForLoopCode.call(this, childTemplateObject, parent)
|
|
285
292
|
} else {
|
|
286
|
-
if (
|
|
293
|
+
if (
|
|
294
|
+
childTemplateObject[Symbol.for('componentType')] === 'Element' ||
|
|
295
|
+
childTemplateObject[Symbol.for('componentType')] === 'Slot'
|
|
296
|
+
) {
|
|
287
297
|
generateElementCode.call(this, childTemplateObject, parent, options)
|
|
288
298
|
} else {
|
|
289
299
|
generateComponentCode.call(this, childTemplateObject, parent, options)
|
package/src/lib/symbols.js
CHANGED
|
@@ -77,7 +77,7 @@ export default (template = '', componentName, parentComponent, filePath = null)
|
|
|
77
77
|
const match = moveCursorOnMatch(emptyTagStartRegex)
|
|
78
78
|
if (match) {
|
|
79
79
|
tags.push({
|
|
80
|
-
|
|
80
|
+
[Symbol.for('componentType')]: null,
|
|
81
81
|
[symbols.type]: 'opening',
|
|
82
82
|
[symbols.level]: currentLevel,
|
|
83
83
|
[symbols.cursorTagStart]: prevCursor,
|
|
@@ -94,7 +94,7 @@ export default (template = '', componentName, parentComponent, filePath = null)
|
|
|
94
94
|
if (match) {
|
|
95
95
|
currentLevel--
|
|
96
96
|
tags.push({
|
|
97
|
-
|
|
97
|
+
[Symbol.for('componentType')]: null,
|
|
98
98
|
[symbols.type]: 'closing',
|
|
99
99
|
[symbols.level]: currentLevel,
|
|
100
100
|
})
|
|
@@ -108,7 +108,7 @@ export default (template = '', componentName, parentComponent, filePath = null)
|
|
|
108
108
|
const match = moveCursorOnMatch(tagStartRegex)
|
|
109
109
|
if (match) {
|
|
110
110
|
currentTag = {
|
|
111
|
-
|
|
111
|
+
[Symbol.for('componentType')]: match[1],
|
|
112
112
|
[symbols.level]: currentLevel,
|
|
113
113
|
[symbols.cursorTagStart]: prevCursor,
|
|
114
114
|
}
|
|
@@ -221,7 +221,7 @@ export default (template = '', componentName, parentComponent, filePath = null)
|
|
|
221
221
|
[symbols.level]: element[symbols.level],
|
|
222
222
|
[symbols.type]: element[symbols.type],
|
|
223
223
|
[symbols.cursorTagStart]: element[symbols.cursorTagStart],
|
|
224
|
-
|
|
224
|
+
[Symbol.for('componentType')]: element[Symbol.for('componentType')],
|
|
225
225
|
parent: currentParent, // helps getting the previous parent when closing tag is encountered
|
|
226
226
|
})
|
|
227
227
|
} else if (element[symbols.type] === 'closing') {
|
|
@@ -230,7 +230,9 @@ export default (template = '', componentName, parentComponent, filePath = null)
|
|
|
230
230
|
let isTagMismatch = false
|
|
231
231
|
if (!isStackEmpty) {
|
|
232
232
|
isLevelMismatch = stack[stack.length - 1][symbols.level] !== element[symbols.level]
|
|
233
|
-
isTagMismatch =
|
|
233
|
+
isTagMismatch =
|
|
234
|
+
stack[stack.length - 1][Symbol.for('componentType')] !==
|
|
235
|
+
element[Symbol.for('componentType')]
|
|
234
236
|
}
|
|
235
237
|
|
|
236
238
|
if (isStackEmpty || isLevelMismatch || isTagMismatch) {
|
|
@@ -329,7 +331,7 @@ export default (template = '', componentName, parentComponent, filePath = null)
|
|
|
329
331
|
let currentParent = parentComponent
|
|
330
332
|
|
|
331
333
|
while (currentParent) {
|
|
332
|
-
hierarchy = `${currentParent.
|
|
334
|
+
hierarchy = `${currentParent[Symbol.for('componentType')]}/${hierarchy}`
|
|
333
335
|
currentParent = currentParent.parent
|
|
334
336
|
}
|
|
335
337
|
return hierarchy
|
package/src/router/router.js
CHANGED
|
@@ -36,17 +36,68 @@ export const getHash = () => {
|
|
|
36
36
|
return (document.location.hash || '/').replace(/^#/, '')
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
const normalizePath = (path) => {
|
|
40
|
+
return (
|
|
41
|
+
path
|
|
42
|
+
// remove leading and trailing slashes
|
|
43
|
+
.replace(/^\/+|\/+$/g, '')
|
|
44
|
+
.toLowerCase()
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
39
48
|
export const matchHash = (path, routes = []) => {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
route
|
|
47
|
-
|
|
49
|
+
// remove trailing slashes
|
|
50
|
+
const originalPath = path
|
|
51
|
+
path = normalizePath(path)
|
|
52
|
+
let matchingRoute = false
|
|
53
|
+
let i = 0
|
|
54
|
+
while (!matchingRoute && i < routes.length) {
|
|
55
|
+
const route = routes[i]
|
|
56
|
+
route.path = normalizePath(route.path)
|
|
57
|
+
if (route.path === path) {
|
|
58
|
+
matchingRoute = route
|
|
59
|
+
} else if (route.path.indexOf(':') > -1) {
|
|
60
|
+
// match dynamic route parts
|
|
61
|
+
const dynamicRouteParts = [...route.path.matchAll(/:([^\s/]+)/gi)]
|
|
62
|
+
|
|
63
|
+
// construct a regex for the route with dynamic parts
|
|
64
|
+
let dynamicRoutePartsRegex = route.path
|
|
65
|
+
dynamicRouteParts.reverse().forEach((part) => {
|
|
66
|
+
dynamicRoutePartsRegex =
|
|
67
|
+
dynamicRoutePartsRegex.substring(0, part.index) +
|
|
68
|
+
'([^\\s/]+)' +
|
|
69
|
+
dynamicRoutePartsRegex.substring(part.index + part[0].length)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
// test if the constructed regex matches the path
|
|
73
|
+
const match = originalPath.match(new RegExp(`${dynamicRoutePartsRegex}`, 'i'))
|
|
74
|
+
|
|
75
|
+
if (match) {
|
|
76
|
+
// map the route params to a params object
|
|
77
|
+
route.params = dynamicRouteParts.reverse().reduce((acc, part, index) => {
|
|
78
|
+
acc[part[1]] = match[index + 1]
|
|
79
|
+
return acc
|
|
80
|
+
}, {})
|
|
81
|
+
matchingRoute = route
|
|
82
|
+
}
|
|
83
|
+
} else if (route.path.endsWith('*')) {
|
|
84
|
+
const regex = new RegExp(route.path.replace(/\/?\*/, '/?([^\\s]*)'), 'i')
|
|
85
|
+
const match = path.match(regex)
|
|
86
|
+
|
|
87
|
+
if (match) {
|
|
88
|
+
if (match[1]) route.params = { path: match[1] }
|
|
89
|
+
matchingRoute = route
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
i++
|
|
48
93
|
}
|
|
49
|
-
|
|
94
|
+
|
|
95
|
+
if (matchingRoute) {
|
|
96
|
+
matchingRoute.options = { ...matchingRoute.options, ...overrideOptions }
|
|
97
|
+
currentRoute = matchingRoute
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return matchingRoute
|
|
50
101
|
}
|
|
51
102
|
|
|
52
103
|
export const navigate = async function () {
|
|
@@ -81,16 +132,18 @@ export const navigate = async function () {
|
|
|
81
132
|
holder.populate({})
|
|
82
133
|
holder.set('w', '100%')
|
|
83
134
|
holder.set('h', '100%')
|
|
84
|
-
|
|
135
|
+
// merge props with potential route params to be injected into the component instance
|
|
136
|
+
const props = { ...this[symbols.props], ...route.params }
|
|
137
|
+
view = await route.component({ props }, holder, this)
|
|
85
138
|
if (view[Symbol.toStringTag] === 'Module') {
|
|
86
139
|
if (view.default && typeof view.default === 'function') {
|
|
87
|
-
view = view.default(
|
|
140
|
+
view = view.default({ props }, holder, this)
|
|
88
141
|
} else {
|
|
89
142
|
Log.error("Dynamic import doesn't have a default export or default is not a function")
|
|
90
143
|
}
|
|
91
144
|
}
|
|
92
145
|
if (typeof view === 'function') {
|
|
93
|
-
view = view(
|
|
146
|
+
view = view({ props }, holder, this)
|
|
94
147
|
}
|
|
95
148
|
} else {
|
|
96
149
|
holder = view[symbols.wrapper]
|
|
@@ -207,7 +260,13 @@ export const back = () => {
|
|
|
207
260
|
if (route) {
|
|
208
261
|
// set indicator that we are navigating back (to prevent adding page to history stack)
|
|
209
262
|
navigatingBack = true
|
|
210
|
-
|
|
263
|
+
let targetRoutePath = route.path
|
|
264
|
+
if (targetRoutePath.indexOf(':') > -1) {
|
|
265
|
+
Object.keys(route.params).forEach((item) => {
|
|
266
|
+
targetRoutePath = targetRoutePath.replace(`:${item}`, route.params[item])
|
|
267
|
+
})
|
|
268
|
+
}
|
|
269
|
+
to(targetRoutePath)
|
|
211
270
|
return true
|
|
212
271
|
} else {
|
|
213
272
|
return false
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 Comcast Cable Communications Management, LLC
|
|
3
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License.
|
|
5
|
+
* You may obtain a copy of the License at
|
|
6
|
+
*
|
|
7
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
*
|
|
9
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
* See the License for the specific language governing permissions and
|
|
13
|
+
* limitations under the License.
|
|
14
|
+
*
|
|
15
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import test from 'tape'
|
|
19
|
+
import { matchHash } from './router.js'
|
|
20
|
+
|
|
21
|
+
const routes = [
|
|
22
|
+
{
|
|
23
|
+
path: '/',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
path: '/page1',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
path: '/page1/subpage1',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
path: '/tv/:show/seasons/:season',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
path: '/movies/:name',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
path: '/examples/*',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
path: '/route/with/trailing/slash/',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
path: '*',
|
|
45
|
+
},
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
test('Type of matchHash', (assert) => {
|
|
49
|
+
const expected = 'function'
|
|
50
|
+
const actual = typeof matchHash
|
|
51
|
+
|
|
52
|
+
assert.equal(actual, expected, 'matchhash should be a function')
|
|
53
|
+
assert.end()
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test('Match paths with static routes that have an exact match', (assert) => {
|
|
57
|
+
assert.equal(matchHash('/', routes), routes[0], 'Should return the correct route object')
|
|
58
|
+
assert.equal(matchHash('/page1', routes), routes[1], 'Should return the correct route object')
|
|
59
|
+
assert.equal(
|
|
60
|
+
matchHash('/page1/subpage1', routes),
|
|
61
|
+
routes[2],
|
|
62
|
+
'Should return the correct route object'
|
|
63
|
+
)
|
|
64
|
+
assert.end()
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
test('Match paths with dynamic route parts', (assert) => {
|
|
68
|
+
assert.equal(
|
|
69
|
+
matchHash('/tv/simpsons/seasons/first', routes),
|
|
70
|
+
routes[3],
|
|
71
|
+
'Should return the correct route object'
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
assert.equal(
|
|
75
|
+
matchHash('/movies/avengers-endgame', routes),
|
|
76
|
+
routes[4],
|
|
77
|
+
'Should return the correct route object'
|
|
78
|
+
)
|
|
79
|
+
assert.end()
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
test('Match paths with a wildcard asterix', (assert) => {
|
|
83
|
+
assert.equal(
|
|
84
|
+
matchHash('/examples/example1', routes),
|
|
85
|
+
routes[5],
|
|
86
|
+
'Should return the correct route object'
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
assert.equal(
|
|
90
|
+
matchHash('/examples/example1/subexample/and-another-page', routes),
|
|
91
|
+
routes[5],
|
|
92
|
+
'Should return the correct route object'
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
assert.equal(matchHash('/404', routes), routes[7], 'Should return the correct route object')
|
|
96
|
+
|
|
97
|
+
assert.equal(
|
|
98
|
+
matchHash('/page1/subpage1/i-dont-exist', routes),
|
|
99
|
+
routes[7],
|
|
100
|
+
'Should return the correct route object'
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
assert.end()
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
test('Add params to route for dynamic route matches', (assert) => {
|
|
107
|
+
const match1 = matchHash('/tv/simpsons/seasons/first', routes)
|
|
108
|
+
|
|
109
|
+
assert.deepEqual(
|
|
110
|
+
match1.params,
|
|
111
|
+
{
|
|
112
|
+
show: 'simpsons',
|
|
113
|
+
season: 'first',
|
|
114
|
+
},
|
|
115
|
+
'Should return the correct params object'
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
const match2 = matchHash('/movies/avengers-endgame', routes)
|
|
119
|
+
assert.deepEqual(
|
|
120
|
+
match2.params,
|
|
121
|
+
{
|
|
122
|
+
name: 'avengers-endgame',
|
|
123
|
+
},
|
|
124
|
+
'Should return the correct route object'
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
assert.end()
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
test('Add remaining path as param for wild card routes', (assert) => {
|
|
131
|
+
const match1 = matchHash('/examples/example1', routes)
|
|
132
|
+
assert.deepEqual(match1.params, { path: 'example1' }, 'Should return the correct route object')
|
|
133
|
+
|
|
134
|
+
const match2 = matchHash('/examples/example1/subexample/and-another-page', routes)
|
|
135
|
+
assert.deepEqual(
|
|
136
|
+
match2.params,
|
|
137
|
+
{ path: 'example1/subexample/and-another-page' },
|
|
138
|
+
'Should return the correct params object'
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
const match3 = matchHash('/404', routes)
|
|
142
|
+
assert.deepEqual(match3.params, { path: '404' }, 'Should return the correct params object')
|
|
143
|
+
|
|
144
|
+
const match4 = matchHash('/page1/subpage1/i-dont-exist', routes)
|
|
145
|
+
assert.deepEqual(
|
|
146
|
+
match4.params,
|
|
147
|
+
{ path: 'page1/subpage1/i-dont-exist' },
|
|
148
|
+
'Should return the correct params object'
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
assert.end()
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
test('Work with trailing slashes', (assert) => {
|
|
155
|
+
assert.equal(matchHash('/page1/', routes), routes[1], 'Should return the correct route object')
|
|
156
|
+
assert.equal(
|
|
157
|
+
matchHash('/page1//////', routes),
|
|
158
|
+
routes[1],
|
|
159
|
+
'Should return the correct route object'
|
|
160
|
+
)
|
|
161
|
+
assert.equal(
|
|
162
|
+
matchHash('/page1/subpage1/', routes),
|
|
163
|
+
routes[2],
|
|
164
|
+
'Should return the correct route object'
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
assert.equal(
|
|
168
|
+
matchHash('/route/with/trailing/slash', routes),
|
|
169
|
+
routes[6],
|
|
170
|
+
'Should return the correct route object'
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
assert.end()
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
test('Work with and without leading slashes', (assert) => {
|
|
177
|
+
assert.equal(matchHash('page1/', routes), routes[1], 'Should return the correct route object')
|
|
178
|
+
|
|
179
|
+
assert.equal(matchHash('////page1', routes), routes[1], 'Should return the correct route object')
|
|
180
|
+
assert.equal(
|
|
181
|
+
matchHash('page1/subpage1/', routes),
|
|
182
|
+
routes[2],
|
|
183
|
+
'Should return the correct route object'
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
assert.equal(
|
|
187
|
+
matchHash('route/with/trailing/slash', routes),
|
|
188
|
+
routes[6],
|
|
189
|
+
'Should return the correct route object'
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
assert.end()
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
test('Match routes case insensitive', (assert) => {
|
|
196
|
+
assert.equal(matchHash('/', routes), routes[0], 'Should return the correct route object')
|
|
197
|
+
assert.equal(matchHash('/Page1', routes), routes[1], 'Should return the correct route object')
|
|
198
|
+
assert.equal(
|
|
199
|
+
matchHash('/pagE1/SUBpage1', routes),
|
|
200
|
+
routes[2],
|
|
201
|
+
'Should return the correct route object'
|
|
202
|
+
)
|
|
203
|
+
assert.equal(
|
|
204
|
+
matchHash('/Tv/SimpsonS/Seasons/First', routes),
|
|
205
|
+
routes[3],
|
|
206
|
+
'Should return the correct route object'
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
assert.equal(
|
|
210
|
+
matchHash('/Movies/Avengers-Endgame', routes),
|
|
211
|
+
routes[4],
|
|
212
|
+
'Should return the correct route object'
|
|
213
|
+
)
|
|
214
|
+
assert.end()
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
test('Match routes case insensitive, but pass props with original casing', (assert) => {
|
|
218
|
+
const match1 = matchHash('/Tv/SimpsonS/Seasons/First', routes)
|
|
219
|
+
assert.deepEqual(
|
|
220
|
+
match1.params,
|
|
221
|
+
{
|
|
222
|
+
show: 'SimpsonS',
|
|
223
|
+
season: 'First',
|
|
224
|
+
},
|
|
225
|
+
'Should return the correct params object'
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
const match2 = matchHash('/Movies/Avengers-Endgame', routes)
|
|
229
|
+
assert.deepEqual(
|
|
230
|
+
match2.params,
|
|
231
|
+
{
|
|
232
|
+
name: 'Avengers-Endgame',
|
|
233
|
+
},
|
|
234
|
+
'Should return the correct params object'
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
assert.end()
|
|
238
|
+
})
|