halfcab 16.0.2 → 16.0.3
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/halfcab.mjs +16 -76
- package/package.json +3 -3
- package/test.js +9 -104
package/halfcab.mjs
CHANGED
|
@@ -18,7 +18,7 @@ let cssTag = cssInject
|
|
|
18
18
|
let componentCSSString = ''
|
|
19
19
|
let routesArray = []
|
|
20
20
|
let externalRoutes = []
|
|
21
|
-
let
|
|
21
|
+
let state = {}
|
|
22
22
|
let router
|
|
23
23
|
let rootEl
|
|
24
24
|
let components
|
|
@@ -39,14 +39,14 @@ function b64DecodeUnicode (str) {
|
|
|
39
39
|
if (typeof window !== 'undefined') {
|
|
40
40
|
dataInitial = document.querySelector('[data-initial]')
|
|
41
41
|
if (!!dataInitial) {
|
|
42
|
-
|
|
42
|
+
state = (dataInitial && dataInitial.dataset.initial) && Object.assign({}, JSON.parse(b64DecodeUnicode(dataInitial.dataset.initial)))
|
|
43
43
|
|
|
44
|
-
if (!
|
|
45
|
-
|
|
44
|
+
if (!state.router) {
|
|
45
|
+
state.router = {}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
if (!
|
|
49
|
-
Object.assign(
|
|
48
|
+
if (!state.router.pathname) {
|
|
49
|
+
Object.assign(state.router, {
|
|
50
50
|
pathname: window.location.pathname,
|
|
51
51
|
hash: window.location.hash,
|
|
52
52
|
query: qs.parse(window.location.search)
|
|
@@ -62,50 +62,6 @@ if (typeof window !== 'undefined') {
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
const proxyCache = new WeakMap()
|
|
66
|
-
|
|
67
|
-
const ARRAY_MUTATING_METHODS = new Set(['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse', 'fill', 'copyWithin'])
|
|
68
|
-
|
|
69
|
-
function createState (target) {
|
|
70
|
-
if (proxyCache.has(target)) {
|
|
71
|
-
return proxyCache.get(target)
|
|
72
|
-
}
|
|
73
|
-
const proxy = new Proxy(target, {
|
|
74
|
-
get (obj, prop) {
|
|
75
|
-
const value = obj[prop]
|
|
76
|
-
if (Array.isArray(obj) && typeof value === 'function' && ARRAY_MUTATING_METHODS.has(prop)) {
|
|
77
|
-
return function (...args) {
|
|
78
|
-
const result = Array.prototype[prop].apply(obj, args)
|
|
79
|
-
debounce(stateUpdated)
|
|
80
|
-
return result
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
if (value !== null && typeof value === 'object') {
|
|
84
|
-
const descriptor = Object.getOwnPropertyDescriptor(obj, prop)
|
|
85
|
-
if (descriptor && descriptor.configurable === false && descriptor.writable === false) {
|
|
86
|
-
return value
|
|
87
|
-
}
|
|
88
|
-
return createState(value)
|
|
89
|
-
}
|
|
90
|
-
return value
|
|
91
|
-
},
|
|
92
|
-
set (obj, prop, value) {
|
|
93
|
-
obj[prop] = value
|
|
94
|
-
debounce(stateUpdated)
|
|
95
|
-
return true
|
|
96
|
-
},
|
|
97
|
-
deleteProperty (obj, prop) {
|
|
98
|
-
delete obj[prop]
|
|
99
|
-
debounce(stateUpdated)
|
|
100
|
-
return true
|
|
101
|
-
}
|
|
102
|
-
})
|
|
103
|
-
proxyCache.set(target, proxy)
|
|
104
|
-
return proxy
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
let state = createState(rawState)
|
|
108
|
-
|
|
109
65
|
let geb = new eventEmitter({state})
|
|
110
66
|
|
|
111
67
|
const stringsCache = new WeakMap()
|
|
@@ -360,30 +316,19 @@ function stateUpdated () {
|
|
|
360
316
|
}
|
|
361
317
|
}
|
|
362
318
|
|
|
363
|
-
function mergeInPlace (target, source) {
|
|
364
|
-
// Recursively merge source into target, mutating target in-place so that
|
|
365
|
-
// existing object references (and their Proxy wrappers) are preserved.
|
|
366
|
-
// Arrays are always replaced entirely — merging arrays is complex and error-prone.
|
|
367
|
-
for (const key of Object.keys(source)) {
|
|
368
|
-
const srcVal = source[key]
|
|
369
|
-
const tgtVal = target[key]
|
|
370
|
-
if (srcVal !== null && typeof srcVal === 'object' && !Array.isArray(srcVal) &&
|
|
371
|
-
tgtVal !== null && typeof tgtVal === 'object' && !Array.isArray(tgtVal)) {
|
|
372
|
-
mergeInPlace(tgtVal, srcVal)
|
|
373
|
-
} else {
|
|
374
|
-
target[key] = srcVal
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
|
|
379
319
|
function updateState (updateObject, options) {
|
|
380
320
|
if (updateObject) {
|
|
381
321
|
if (options && options.deepMerge === false) {
|
|
382
|
-
Object.assign(
|
|
322
|
+
Object.assign(state, updateObject)
|
|
383
323
|
} else {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
324
|
+
let deepMergeOptions = {clone: false}
|
|
325
|
+
if (options && options.arrayMerge === false) {
|
|
326
|
+
deepMergeOptions.arrayMerge = (destinationArray, sourceArray, options) => {
|
|
327
|
+
//don't merge arrays, just return the new one
|
|
328
|
+
return sourceArray
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
Object.assign(state, merge(state, updateObject, deepMergeOptions))
|
|
387
332
|
}
|
|
388
333
|
}
|
|
389
334
|
|
|
@@ -399,7 +344,7 @@ function updateState (updateObject, options) {
|
|
|
399
344
|
console.log(updateObject)
|
|
400
345
|
console.log(' ')
|
|
401
346
|
console.log('------NEW STATE------')
|
|
402
|
-
console.log(
|
|
347
|
+
console.log(state)
|
|
403
348
|
console.log(' ')
|
|
404
349
|
}
|
|
405
350
|
|
|
@@ -568,10 +513,6 @@ export default (config, {shiftyRouter = shiftyRouterModule, href = hrefModule, h
|
|
|
568
513
|
})
|
|
569
514
|
}
|
|
570
515
|
|
|
571
|
-
/**
|
|
572
|
-
* @deprecated The Proxy-based state now triggers rerenders automatically via set traps.
|
|
573
|
-
* This function is kept for backwards compatibility but is no longer needed in most cases.
|
|
574
|
-
*/
|
|
575
516
|
function rerender () {
|
|
576
517
|
debounce(stateUpdated)
|
|
577
518
|
}
|
|
@@ -588,7 +529,6 @@ export {
|
|
|
588
529
|
html,
|
|
589
530
|
defineRoute,
|
|
590
531
|
updateState,
|
|
591
|
-
createState,
|
|
592
532
|
state,
|
|
593
533
|
formField,
|
|
594
534
|
gotoRoute,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "halfcab",
|
|
3
|
-
"version": "16.0.
|
|
3
|
+
"version": "16.0.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A simple universal JavaScript framework focused on making use of es2015 template strings to build components.",
|
|
6
6
|
"main": "halfcab.mjs",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"test:coverage": "c8 --reporter=html --check-coverage --lines 75 --functions 75 --branches 75 npm test",
|
|
12
12
|
"test:coveralls": "c8 npm test && c8 report --reporter=text-lcov | coveralls",
|
|
13
13
|
"versionbump:fix": "npm version patch --no-git-tag-version",
|
|
14
|
-
"versionbump:feature": "npm version
|
|
14
|
+
"versionbump:feature": "npm version major --no-git-tag-version",
|
|
15
15
|
"versionbump:breakingchanges": "npm version major --no-git-tag-version",
|
|
16
16
|
"npm-publish": "npm publish --tag beta",
|
|
17
17
|
"start:example": "npx --yes serve . -l 5173"
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"lit": "^3.3.2",
|
|
61
61
|
"marked": "^0.7.0",
|
|
62
62
|
"qs": "^6.5.2",
|
|
63
|
-
"shifty-router": "^0.
|
|
63
|
+
"shifty-router": "^1.0.0"
|
|
64
64
|
},
|
|
65
65
|
"peerDependencies": {
|
|
66
66
|
"esm": "^3.2.22"
|
package/test.js
CHANGED
|
@@ -377,7 +377,12 @@ describe('halfcab', () => {
|
|
|
377
377
|
|
|
378
378
|
})
|
|
379
379
|
|
|
380
|
-
it(`
|
|
380
|
+
it(`Switches to the 404 route when a route doesn't exist`, () => {
|
|
381
|
+
defineRoute({
|
|
382
|
+
path: '/404',
|
|
383
|
+
title: 'Not Found',
|
|
384
|
+
component: '404Component'
|
|
385
|
+
})
|
|
381
386
|
|
|
382
387
|
return halfcab({
|
|
383
388
|
el: '#root',
|
|
@@ -385,11 +390,9 @@ describe('halfcab', () => {
|
|
|
385
390
|
return html `<div></div>`
|
|
386
391
|
}
|
|
387
392
|
})
|
|
388
|
-
.then(
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
}
|
|
392
|
-
expect(routing).to.throw()
|
|
393
|
+
.then(({state}) => {
|
|
394
|
+
gotoRoute('/thisIsAFakeRoute')
|
|
395
|
+
expect(state.router.component).to.equal('404Component')
|
|
393
396
|
})
|
|
394
397
|
|
|
395
398
|
})
|
|
@@ -403,104 +406,6 @@ describe('halfcab', () => {
|
|
|
403
406
|
.true()
|
|
404
407
|
})
|
|
405
408
|
|
|
406
|
-
it('Keeps sub-object references live after overwriting via updateState', (done) => {
|
|
407
|
-
halfcab({
|
|
408
|
-
el: '#root',
|
|
409
|
-
components () {
|
|
410
|
-
return html `<div></div>`
|
|
411
|
-
}
|
|
412
|
-
})
|
|
413
|
-
.then(({rootEl, state}) => {
|
|
414
|
-
updateState({
|
|
415
|
-
user: { name: 'Original Name', age: 25 }
|
|
416
|
-
})
|
|
417
|
-
|
|
418
|
-
nextTick(() => {
|
|
419
|
-
// Grab a reference to the sub-object proxy
|
|
420
|
-
const user = state.user
|
|
421
|
-
|
|
422
|
-
// Wipe out the user object entirely via a merge
|
|
423
|
-
updateState({ user: { name: 'New Name', age: 30 } })
|
|
424
|
-
|
|
425
|
-
nextTick(() => {
|
|
426
|
-
// The reference should reflect the new values
|
|
427
|
-
expect(user.name).to.equal('New Name')
|
|
428
|
-
expect(user.age).to.equal(30)
|
|
429
|
-
done()
|
|
430
|
-
})
|
|
431
|
-
})
|
|
432
|
-
})
|
|
433
|
-
})
|
|
434
|
-
|
|
435
|
-
it('Replaces arrays entirely instead of merging them', (done) => {
|
|
436
|
-
halfcab({
|
|
437
|
-
el: '#root',
|
|
438
|
-
components () {
|
|
439
|
-
return html `<div></div>`
|
|
440
|
-
}
|
|
441
|
-
})
|
|
442
|
-
.then(({rootEl, state}) => {
|
|
443
|
-
updateState({ list: [1, 2, 3] })
|
|
444
|
-
|
|
445
|
-
nextTick(() => {
|
|
446
|
-
updateState({ list: [4, 5] })
|
|
447
|
-
|
|
448
|
-
nextTick(() => {
|
|
449
|
-
expect(state.list).to.deep.equal([4, 5])
|
|
450
|
-
done()
|
|
451
|
-
})
|
|
452
|
-
})
|
|
453
|
-
})
|
|
454
|
-
})
|
|
455
|
-
|
|
456
|
-
it('Triggers rerender when a property is deleted from state', (done) => {
|
|
457
|
-
halfcab({
|
|
458
|
-
el: '#root',
|
|
459
|
-
components () {
|
|
460
|
-
return html `<div></div>`
|
|
461
|
-
}
|
|
462
|
-
})
|
|
463
|
-
.then(({rootEl, state}) => {
|
|
464
|
-
updateState({ toDelete: 'bye' })
|
|
465
|
-
|
|
466
|
-
nextTick(() => {
|
|
467
|
-
expect(state.toDelete).to.equal('bye')
|
|
468
|
-
delete state.toDelete
|
|
469
|
-
|
|
470
|
-
nextTick(() => {
|
|
471
|
-
expect(state.toDelete).to.be.undefined()
|
|
472
|
-
done()
|
|
473
|
-
})
|
|
474
|
-
})
|
|
475
|
-
})
|
|
476
|
-
})
|
|
477
|
-
|
|
478
|
-
it('Triggers rerender when array mutation methods are used on state', (done) => {
|
|
479
|
-
halfcab({
|
|
480
|
-
el: '#root',
|
|
481
|
-
components () {
|
|
482
|
-
return html `<div></div>`
|
|
483
|
-
}
|
|
484
|
-
})
|
|
485
|
-
.then(({rootEl, state}) => {
|
|
486
|
-
updateState({ items: [1, 2, 3] })
|
|
487
|
-
|
|
488
|
-
nextTick(() => {
|
|
489
|
-
state.items.push(4)
|
|
490
|
-
|
|
491
|
-
nextTick(() => {
|
|
492
|
-
expect(state.items).to.deep.equal([1, 2, 3, 4])
|
|
493
|
-
state.items.pop()
|
|
494
|
-
|
|
495
|
-
nextTick(() => {
|
|
496
|
-
expect(state.items).to.deep.equal([1, 2, 3])
|
|
497
|
-
done()
|
|
498
|
-
})
|
|
499
|
-
})
|
|
500
|
-
})
|
|
501
|
-
})
|
|
502
|
-
})
|
|
503
|
-
|
|
504
409
|
it(`Doesn't clone when merging`, (done) => {
|
|
505
410
|
halfcab({
|
|
506
411
|
el: '#root',
|