@lightningjs/blits 1.8.0 → 1.8.2

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 CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## v1.8.2
4
+
5
+ _30 oct 2024_
6
+
7
+ - Fixed edgecase issue with array based props not triggering reactivity
8
+
9
+
10
+ ## v1.8.1
11
+
12
+ _29 oct 2024_
13
+
14
+ - Fixed issue with array based props not triggering reactivity
15
+ - Upgraded renderer to 2.5.1
16
+
17
+
3
18
  ## v1.8.0
4
19
 
5
20
  _22 oct 2024_
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightningjs/blits",
3
- "version": "1.8.0",
3
+ "version": "1.8.2",
4
4
  "description": "Blits: The Lightning 3 App Development Framework",
5
5
  "bin": "bin/index.js",
6
6
  "exports": {
@@ -13,7 +13,7 @@
13
13
  },
14
14
  "scripts": {
15
15
  "test": "c8 npm run test:run",
16
- "test:run": "node -r global-jsdom/register ./node_modules/.bin/tape '{,!(node_modules)/**/}*.test.js' | tap-diff",
16
+ "test:run": "node -r global-jsdom/register ./node_modules/.bin/tape '{,!(node_modules|packages)/**/}*.test.js' | tap-diff",
17
17
  "lint": "eslint '**/*.js'",
18
18
  "lint:fix": "eslint '**/*.js' --fix",
19
19
  "prepublishOnly": "node scripts/prepublishOnly.js",
@@ -49,7 +49,7 @@
49
49
  },
50
50
  "dependencies": {
51
51
  "@lightningjs/msdf-generator": "^1.1.0",
52
- "@lightningjs/renderer": "^2.4.0"
52
+ "@lightningjs/renderer": "^2.5.1"
53
53
  },
54
54
  "repository": {
55
55
  "type": "git",
@@ -25,6 +25,7 @@ export default () =>
25
25
  const elms = []
26
26
  let componentType
27
27
  const rootComponent = component
28
+ let propData
28
29
  const elementConfig0 = {}
29
30
 
30
31
  elms[0] = this.element({ parent: parent || 'root' }, component)
@@ -27,6 +27,7 @@ export default () =>
27
27
  const elms = []
28
28
  let componentType
29
29
  const rootComponent = component
30
+ let propData
30
31
  const elementConfig0 = {}
31
32
 
32
33
  elms[0] = this.element({ parent: parent || 'root' }, component)
@@ -141,7 +142,12 @@ export default () =>
141
142
  props7['h'] = 20
142
143
  props7['w'] = 20
143
144
  props7['map'] = component.sprite
144
- props7['frame'] = component.fps[0]
145
+
146
+ propData = component.fps[0]
147
+ if (Array.isArray(propData) === true) {
148
+ propData = getRaw(propData).slice(0)
149
+ }
150
+ props7['frame'] = propData
145
151
 
146
152
  componentType = props7['is'] || 'Sprite'
147
153
 
@@ -198,7 +204,12 @@ export default () =>
198
204
  props9['h'] = 20
199
205
  props9['w'] = 20
200
206
  props9['map'] = component.sprite
201
- props9['frame'] = component.fps[1]
207
+
208
+ propData = component.fps[1]
209
+ if (Array.isArray(propData) === true) {
210
+ propData = getRaw(propData).slice(0)
211
+ }
212
+ props9['frame'] = propData
202
213
 
203
214
  componentType = props9['is'] || 'Sprite'
204
215
 
@@ -255,7 +266,12 @@ export default () =>
255
266
  props11['h'] = 20
256
267
  props11['w'] = 20
257
268
  props11['map'] = component.sprite
258
- props11['frame'] = component.fps[2]
269
+
270
+ propData = component.fps[2]
271
+ if (Array.isArray(propData) === true) {
272
+ propData = getRaw(propData).slice(0)
273
+ }
274
+ props11['frame'] = propData
259
275
 
260
276
  componentType = props11['is'] || 'Sprite'
261
277
 
@@ -386,7 +402,12 @@ export default () =>
386
402
  props17['h'] = 20
387
403
  props17['w'] = 20
388
404
  props17['map'] = component.sprite
389
- props17['frame'] = component.avgFps[0]
405
+
406
+ propData = component.avgFps[0]
407
+ if (Array.isArray(propData) === true) {
408
+ propData = getRaw(propData).slice(0)
409
+ }
410
+ props17['frame'] = propData
390
411
 
391
412
  componentType = props17['is'] || 'Sprite'
392
413
 
@@ -443,7 +464,12 @@ export default () =>
443
464
  props19['h'] = 20
444
465
  props19['w'] = 20
445
466
  props19['map'] = component.sprite
446
- props19['frame'] = component.avgFps[1]
467
+
468
+ propData = component.avgFps[1]
469
+ if (Array.isArray(propData) === true) {
470
+ propData = getRaw(propData).slice(0)
471
+ }
472
+ props19['frame'] = propData
447
473
 
448
474
  componentType = props19['is'] || 'Sprite'
449
475
 
@@ -500,7 +526,12 @@ export default () =>
500
526
  props21['h'] = 20
501
527
  props21['w'] = 20
502
528
  props21['map'] = component.sprite
503
- props21['frame'] = component.avgFps[2]
529
+
530
+ propData = component.avgFps[2]
531
+ if (Array.isArray(propData) === true) {
532
+ propData = getRaw(propData).slice(0)
533
+ }
534
+ props21['frame'] = propData
504
535
 
505
536
  componentType = props21['is'] || 'Sprite'
506
537
 
@@ -632,7 +663,12 @@ export default () =>
632
663
  props27['h'] = 20
633
664
  props27['w'] = 20
634
665
  props27['map'] = component.sprite
635
- props27['frame'] = component.minFps[0]
666
+
667
+ propData = component.minFps[0]
668
+ if (Array.isArray(propData) === true) {
669
+ propData = getRaw(propData).slice(0)
670
+ }
671
+ props27['frame'] = propData
636
672
 
637
673
  componentType = props27['is'] || 'Sprite'
638
674
 
@@ -689,7 +725,12 @@ export default () =>
689
725
  props29['h'] = 20
690
726
  props29['w'] = 20
691
727
  props29['map'] = component.sprite
692
- props29['frame'] = component.minFps[1]
728
+
729
+ propData = component.minFps[1]
730
+ if (Array.isArray(propData) === true) {
731
+ propData = getRaw(propData).slice(0)
732
+ }
733
+ props29['frame'] = propData
693
734
 
694
735
  componentType = props29['is'] || 'Sprite'
695
736
 
@@ -746,7 +787,12 @@ export default () =>
746
787
  props31['h'] = 20
747
788
  props31['w'] = 20
748
789
  props31['map'] = component.sprite
749
- props31['frame'] = component.minFps[2]
790
+
791
+ propData = component.minFps[2]
792
+ if (Array.isArray(propData) === true) {
793
+ propData = getRaw(propData).slice(0)
794
+ }
795
+ props31['frame'] = propData
750
796
 
751
797
  componentType = props31['is'] || 'Sprite'
752
798
 
@@ -876,7 +922,12 @@ export default () =>
876
922
  props37['h'] = 20
877
923
  props37['w'] = 20
878
924
  props37['map'] = component.sprite
879
- props37['frame'] = component.maxFps[0]
925
+
926
+ propData = component.maxFps[0]
927
+ if (Array.isArray(propData) === true) {
928
+ propData = getRaw(propData).slice(0)
929
+ }
930
+ props37['frame'] = propData
880
931
 
881
932
  componentType = props37['is'] || 'Sprite'
882
933
 
@@ -933,7 +984,12 @@ export default () =>
933
984
  props39['h'] = 20
934
985
  props39['w'] = 20
935
986
  props39['map'] = component.sprite
936
- props39['frame'] = component.maxFps[1]
987
+
988
+ propData = component.maxFps[1]
989
+ if (Array.isArray(propData) === true) {
990
+ propData = getRaw(propData).slice(0)
991
+ }
992
+ props39['frame'] = propData
937
993
 
938
994
  componentType = props39['is'] || 'Sprite'
939
995
 
@@ -990,7 +1046,12 @@ export default () =>
990
1046
  props41['h'] = 20
991
1047
  props41['w'] = 20
992
1048
  props41['map'] = component.sprite
993
- props41['frame'] = component.maxFps[2]
1049
+
1050
+ propData = component.maxFps[2]
1051
+ if (Array.isArray(propData) === true) {
1052
+ propData = getRaw(propData).slice(0)
1053
+ }
1054
+ props41['frame'] = propData
994
1055
 
995
1056
  componentType = props41['is'] || 'Sprite'
996
1057
 
@@ -8,6 +8,7 @@ export default () => {
8
8
  const elms = []
9
9
  let componentType
10
10
  const rootComponent = component
11
+ let propData
11
12
  const elementConfig0 = {}
12
13
 
13
14
  elms[0] = this.element({ parent: parent || 'root' }, component)
@@ -28,6 +28,7 @@ export default () =>
28
28
  const elms = []
29
29
  let componentType
30
30
  const rootComponent = component
31
+ let propData
31
32
  const elementConfig0 = {}
32
33
 
33
34
  elms[0] = this.element({ parent: parent || 'root' }, component)
@@ -27,6 +27,7 @@ export default () =>
27
27
  const elms = []
28
28
  let componentType
29
29
  const rootComponent = component
30
+ let propData
30
31
  const elementConfig0 = {}
31
32
 
32
33
  elms[0] = this.element({ parent: parent || 'root' }, component)
@@ -0,0 +1,50 @@
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
+ const deepEqualArray = (array1, array2) => {
19
+ if (array1 === array2) return true
20
+ if (array1.length !== array2.length) return false
21
+
22
+ let l = array1.length
23
+
24
+ while (l--) {
25
+ const value1 = array1[l]
26
+ const value2 = array2[l]
27
+
28
+ // values are arrays, deepEqual nested arrays
29
+ if (Array.isArray(value1) && Array.isArray(value2)) {
30
+ if (deepEqualArray(value1, value2) === false) return false
31
+ }
32
+ // values are objects, deepEqual nested objects
33
+ else if (
34
+ typeof value1 === 'object' &&
35
+ value1 !== null &&
36
+ typeof value2 === 'object' &&
37
+ value2 !== null
38
+ ) {
39
+ if (deepEqualArray(Object.entries(value1), Object.entries(value2)) === false) return false
40
+ }
41
+ // other type, do strict equal check
42
+ else if (value1 !== value2) {
43
+ return false
44
+ }
45
+ }
46
+
47
+ return true
48
+ }
49
+
50
+ export default deepEqualArray
@@ -0,0 +1,127 @@
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 deepEqual from './deepEqualArray.js'
20
+
21
+ test('Type', (assert) => {
22
+ const expected = 'function'
23
+ const actual = typeof deepEqual
24
+
25
+ assert.equal(actual, expected, 'deepEqual should be a function')
26
+ assert.end()
27
+ })
28
+
29
+ test('The same arrays', (assert) => {
30
+ const val1 = ['hello', 'world']
31
+
32
+ assert.true(
33
+ deepEqual(val1, val1),
34
+ 'deepEqual should return true if the values are the same reference'
35
+ )
36
+ assert.end()
37
+ })
38
+
39
+ test('Equal simple arrays', (assert) => {
40
+ const val1 = ['hello', 'world']
41
+ const val2 = ['hello', 'world']
42
+
43
+ assert.true(deepEqual(val1, val2), 'deepEqual should return true if simple arrays are equal')
44
+ assert.end()
45
+ })
46
+
47
+ test('Not equal simple arrays', (assert) => {
48
+ const val1 = ['hello', 'world']
49
+ const val2 = ['world', 'hello']
50
+
51
+ assert.false(
52
+ deepEqual(val1, val2),
53
+ 'deepEqual should return false if simple arrays are not equal'
54
+ )
55
+ assert.end()
56
+ })
57
+
58
+ test('Not Equal simple arrays (different length)', (assert) => {
59
+ const val1 = ['hello', 'world']
60
+ const val2 = ['hello', 'world', '!']
61
+
62
+ assert.false(deepEqual(val1, val2), 'deepEqual should return false if simple arrays are equal')
63
+ assert.end()
64
+ })
65
+
66
+ test('Equal nested arrays', (assert) => {
67
+ const val1 = ['hello', ['world', '!']]
68
+ const val2 = ['hello', ['world', '!']]
69
+
70
+ assert.true(deepEqual(val1, val2), 'deepEqual should return true if nested arrays are equal')
71
+ assert.end()
72
+ })
73
+
74
+ test('Not equal nested arrays', (assert) => {
75
+ const val1 = ['hello', ['world', '!']]
76
+ const val2 = ['hello', ['world', '!', '?']]
77
+
78
+ assert.false(
79
+ deepEqual(val1, val2),
80
+ 'deepEqual should return false if nested arrays are not equal'
81
+ )
82
+ assert.end()
83
+ })
84
+
85
+ test('Equal arrays with objects', (assert) => {
86
+ const val1 = [{ foo: 'bar', bar: 'foo' }, { hello: 'world' }]
87
+ const val2 = [{ foo: 'bar', bar: 'foo' }, { hello: 'world' }]
88
+
89
+ assert.true(
90
+ deepEqual(val1, val2),
91
+ 'deepEqual should return true if arrays with objects are equal'
92
+ )
93
+ assert.end()
94
+ })
95
+
96
+ test('Not equal arrays with objects (keys', (assert) => {
97
+ const val1 = [{ foo: 'bar', bar: 'foo' }, { test: 'bla' }]
98
+ const val2 = [{ foo2: 'bar', bar2: 'foo' }, { test: 'bla' }]
99
+
100
+ assert.false(
101
+ deepEqual(val1, val2),
102
+ 'deepEqual should return false if arrays with objects are not equal'
103
+ )
104
+ assert.end()
105
+ })
106
+
107
+ test('Equal arrays with mixed values', (assert) => {
108
+ const val1 = ['foo', { foo: 'bar', bar: 'foo' }, ['hello', 'world', ['this', 'is', { level: 2 }]]]
109
+ const val2 = ['foo', { foo: 'bar', bar: 'foo' }, ['hello', 'world', ['this', 'is', { level: 2 }]]]
110
+
111
+ assert.true(
112
+ deepEqual(val1, val2),
113
+ 'deepEqual should return true if arrays with mixed values are equal'
114
+ )
115
+ assert.end()
116
+ })
117
+
118
+ test('Not Equal arrays with mixed values', (assert) => {
119
+ const val1 = ['foo', { foo: 'bar', bar: 'foo' }, ['hello', 'world', ['this', 'is', { level: 2 }]]]
120
+ const val2 = ['foo', { foo: 'bar', bar: 'foo' }, ['hello', 'world', ['this', 'is', { level: 3 }]]]
121
+
122
+ assert.false(
123
+ deepEqual(val1, val2),
124
+ 'deepEqual should return true if arrays with mixed values are not equal'
125
+ )
126
+ assert.end()
127
+ })
@@ -19,7 +19,12 @@ let counter
19
19
 
20
20
  export default function (templateObject = { children: [] }) {
21
21
  const ctx = {
22
- renderCode: ['const elms = []', 'let componentType', 'const rootComponent = component'],
22
+ renderCode: [
23
+ 'const elms = []',
24
+ 'let componentType',
25
+ 'const rootComponent = component',
26
+ 'let propData',
27
+ ],
23
28
  effectsCode: [],
24
29
  context: { props: [], components: this.components },
25
30
  }
@@ -212,12 +217,12 @@ const generateComponentCode = function (
212
217
  templateObject[key],
213
218
  options.component
214
219
  )}`)
215
- renderCode.push(
216
- `props${counter}['${key.substring(1)}'] = ${interpolate(
217
- templateObject[key],
218
- options.component
219
- )}`
220
- )
220
+ renderCode.push(`
221
+ propData = ${interpolate(templateObject[key], options.component)}
222
+ if (Array.isArray(propData) === true) {
223
+ propData = getRaw(propData).slice(0)
224
+ }
225
+ props${counter}['${key.substring(1)}'] = propData`)
221
226
  } else {
222
227
  renderCode.push(
223
228
  `props${counter}['${key}'] = ${cast(templateObject[key], key, options.component)}`
@@ -18,6 +18,7 @@
18
18
  import { ImageTexture } from '@lightningjs/renderer'
19
19
  import { track, trigger, pauseTracking, resumeTracking } from './effect.js'
20
20
  import symbols from '../symbols.js'
21
+ import deepEqualArray from '../../helpers/deepEqualArray.js'
21
22
 
22
23
  const arrayPatchMethods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort']
23
24
 
@@ -38,7 +39,7 @@ const reactiveProxy = (original, _parent = null, _key, global) => {
38
39
 
39
40
  // if original object is already a proxy, don't create a new one but return the existing one instead
40
41
  const existingProxy = proxyMap.get(original)
41
- if (existingProxy) {
42
+ if (existingProxy !== undefined) {
42
43
  return existingProxy
43
44
  }
44
45
 
@@ -50,9 +51,9 @@ const reactiveProxy = (original, _parent = null, _key, global) => {
50
51
  }
51
52
 
52
53
  // handling arrays
53
- if (Array.isArray(target)) {
54
+ if (Array.isArray(target) === true) {
54
55
  if (typeof target[key] === 'object' && target[key] !== null) {
55
- if (Array.isArray(target[key])) {
56
+ if (Array.isArray(target[key]) === true) {
56
57
  track(target, key, global)
57
58
  }
58
59
  // create a new reactive proxy
@@ -80,7 +81,7 @@ const reactiveProxy = (original, _parent = null, _key, global) => {
80
81
 
81
82
  // handling objects (but not null values, which have object type in JS)
82
83
  if (typeof target[key] === 'object' && target[key] !== null) {
83
- if (Array.isArray(target[key])) {
84
+ if (Array.isArray(target[key]) === true) {
84
85
  track(target, key, global)
85
86
  }
86
87
  // create a new reactive proxy
@@ -99,13 +100,19 @@ const reactiveProxy = (original, _parent = null, _key, global) => {
99
100
  const rawValue = getRaw(value)
100
101
 
101
102
  let result = true
102
- if (oldRawValue !== rawValue) {
103
+ const isEqual =
104
+ Array.isArray(rawValue) === true
105
+ ? deepEqualArray(oldRawValue, rawValue)
106
+ : oldRawValue === rawValue
107
+
108
+ if (isEqual === false) {
109
+ if (Array.isArray(value) === true) value = getRaw(value).slice(0)
103
110
  result = Reflect.set(target, key, value, receiver)
104
111
  }
105
112
 
106
- if (result && oldRawValue !== rawValue) {
113
+ if (result === true && isEqual === false) {
107
114
  // if we're assigning an array key directly trigger reactivity on the parent key as well
108
- if (Array.isArray(target) && key in target) {
115
+ if (Array.isArray(target) === true && key in target === true) {
109
116
  trigger(_parent, _key, true)
110
117
  }
111
118
  trigger(target, key, true)
@@ -126,7 +133,7 @@ const reactiveDefineProperty = (target, global) => {
126
133
  if (target[key] !== null && typeof target[key] === 'object') {
127
134
  if (Object.getPrototypeOf(target[key]) === Object.prototype) {
128
135
  return reactiveDefineProperty(target[key])
129
- } else if (Array.isArray(target[key])) {
136
+ } else if (Array.isArray(target[key]) === true) {
130
137
  for (let i = 0; i < arrayPatchMethods.length - 1; i++) {
131
138
  target[key][arrayPatchMethods[i]] = function (v) {
132
139
  Array.prototype[arrayPatchMethods[i]].call(this, v)