halfcab 16.0.1 → 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.
Files changed (3) hide show
  1. package/halfcab.mjs +16 -72
  2. package/package.json +3 -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 rawState = {}
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
- rawState = (dataInitial && dataInitial.dataset.initial) && Object.assign({}, JSON.parse(b64DecodeUnicode(dataInitial.dataset.initial)))
42
+ state = (dataInitial && dataInitial.dataset.initial) && Object.assign({}, JSON.parse(b64DecodeUnicode(dataInitial.dataset.initial)))
43
43
 
44
- if (!rawState.router) {
45
- rawState.router = {}
44
+ if (!state.router) {
45
+ state.router = {}
46
46
  }
47
47
 
48
- if (!rawState.router.pathname) {
49
- Object.assign(rawState.router, {
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,46 +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
- return createState(value)
85
- }
86
- return value
87
- },
88
- set (obj, prop, value) {
89
- obj[prop] = value
90
- debounce(stateUpdated)
91
- return true
92
- },
93
- deleteProperty (obj, prop) {
94
- delete obj[prop]
95
- debounce(stateUpdated)
96
- return true
97
- }
98
- })
99
- proxyCache.set(target, proxy)
100
- return proxy
101
- }
102
-
103
- let state = createState(rawState)
104
-
105
65
  let geb = new eventEmitter({state})
106
66
 
107
67
  const stringsCache = new WeakMap()
@@ -356,30 +316,19 @@ function stateUpdated () {
356
316
  }
357
317
  }
358
318
 
359
- function mergeInPlace (target, source) {
360
- // Recursively merge source into target, mutating target in-place so that
361
- // existing object references (and their Proxy wrappers) are preserved.
362
- // Arrays are always replaced entirely — merging arrays is complex and error-prone.
363
- for (const key of Object.keys(source)) {
364
- const srcVal = source[key]
365
- const tgtVal = target[key]
366
- if (srcVal !== null && typeof srcVal === 'object' && !Array.isArray(srcVal) &&
367
- tgtVal !== null && typeof tgtVal === 'object' && !Array.isArray(tgtVal)) {
368
- mergeInPlace(tgtVal, srcVal)
369
- } else {
370
- target[key] = srcVal
371
- }
372
- }
373
- }
374
-
375
319
  function updateState (updateObject, options) {
376
320
  if (updateObject) {
377
321
  if (options && options.deepMerge === false) {
378
- Object.assign(rawState, updateObject)
322
+ Object.assign(state, updateObject)
379
323
  } else {
380
- // Merge in-place so existing nested object references stay intact,
381
- // keeping Proxy cache entries valid.
382
- mergeInPlace(rawState, updateObject, options)
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))
383
332
  }
384
333
  }
385
334
 
@@ -395,7 +344,7 @@ function updateState (updateObject, options) {
395
344
  console.log(updateObject)
396
345
  console.log(' ')
397
346
  console.log('------NEW STATE------')
398
- console.log(rawState)
347
+ console.log(state)
399
348
  console.log(' ')
400
349
  }
401
350
 
@@ -564,10 +513,6 @@ export default (config, {shiftyRouter = shiftyRouterModule, href = hrefModule, h
564
513
  })
565
514
  }
566
515
 
567
- /**
568
- * @deprecated The Proxy-based state now triggers rerenders automatically via set traps.
569
- * This function is kept for backwards compatibility but is no longer needed in most cases.
570
- */
571
516
  function rerender () {
572
517
  debounce(stateUpdated)
573
518
  }
@@ -584,7 +529,6 @@ export {
584
529
  html,
585
530
  defineRoute,
586
531
  updateState,
587
- createState,
588
532
  state,
589
533
  formField,
590
534
  gotoRoute,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "halfcab",
3
- "version": "16.0.1",
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 minor --no-git-tag-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.1.1"
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(`Throws an error when a route doesn't exist`, () => {
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(rootEl => {
389
- let routing = () => {
390
- gotoRoute('/thisIsAFakeRoute')
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',