@lightningjs/blits 1.6.1 → 1.7.0

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,6 +1,12 @@
1
1
  # Changelog
2
2
 
3
- ## v1.6.0
3
+ ## v1.7.0
4
+
5
+ _14 oct 2024_
6
+
7
+ - Added cleanup upon component destroy of effects generated by global reactivity
8
+
9
+ ## v1.6.0 / v1.6.1
4
10
 
5
11
  _9 oct 2024_
6
12
 
package/bin/index.js CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightningjs/blits",
3
- "version": "1.6.1",
3
+ "version": "1.7.0",
4
4
  "description": "Blits: The Lightning 3 App Development Framework",
5
5
  "bin": "bin/index.js",
6
6
  "exports": {
@@ -20,6 +20,7 @@ import Focus from '../../focus.js'
20
20
  import eventListeners from '../../lib/eventListeners.js'
21
21
  import { trigger } from '../../lib/reactivity/effect.js'
22
22
  import { Log } from '../../lib/log.js'
23
+ import { removeGlobalEffects } from '../../lib/reactivity/effect.js'
23
24
 
24
25
  export default {
25
26
  focus: {
@@ -56,6 +57,7 @@ export default {
56
57
  this.$clearIntervals()
57
58
  eventListeners.removeListeners(this)
58
59
  deleteChildren(this[symbols.children])
60
+ removeGlobalEffects(this[symbols.effects])
59
61
  Log.debug(`Destroyed component ${this.componentId}`)
60
62
  },
61
63
  writable: false,
@@ -32,6 +32,8 @@ let counter = 0
32
32
  export default function (component, config) {
33
33
  component[symbols.identifier] = ++counter
34
34
 
35
+ component[symbols.effects] = []
36
+
35
37
  // setup hooks
36
38
  registerHooks(config.hooks, component[symbols.identifier])
37
39
 
package/src/component.js CHANGED
@@ -161,9 +161,12 @@ const Component = (name = required('name'), config = required('config')) => {
161
161
  // reactive bindings define in the template
162
162
  const effects = config.code.effects
163
163
  for (let i = 0; i < effects.length; i++) {
164
- effect(() => {
164
+ const eff = () => {
165
165
  effects[i](this, this[symbols.children], config, globalComponents, rootComponent, effect)
166
- })
166
+ }
167
+ // store reference to the effect
168
+ this[symbols.effects].push(eff)
169
+ effect(eff)
167
170
  }
168
171
 
169
172
  // setup watchers if the components has watchers specified
@@ -392,13 +392,15 @@ const generateForLoopCode = function (templateObject, parent) {
392
392
  }
393
393
 
394
394
  // inner scope variables are part of the main forloop
395
- innerScopeEffects.forEach((effect) => {
395
+ innerScopeEffects.forEach((effect, index) => {
396
396
  const key = effect.indexOf(`scope.${index}`) > -1 ? `'${interpolate(result[2], '')}'` : null
397
397
  if (effect.indexOf("Symbol.for('props')") === -1) {
398
398
  ctx.renderCode.push(`
399
- effect(() => {
399
+ let eff${index} = () => {
400
400
  ${effect}
401
- }, ${key})
401
+ }
402
+ effect(eff${index}, ${key})
403
+ component[Symbol.for('effects')].push(eff${index})
402
404
  `)
403
405
  } else {
404
406
  // props shouldn't be wrapped in an effect, but simply passed on
@@ -29,8 +29,22 @@ export const resumeTracking = () => {
29
29
  }
30
30
 
31
31
  const objectMap = new WeakMap()
32
+ const globalEffectsMap = new Map()
32
33
 
33
- export const track = (target, key) => {
34
+ export const removeGlobalEffects = (effectsToRemove) => {
35
+ if (globalEffectsMap.size === 0) return
36
+ for (const [effect, target] of globalEffectsMap) {
37
+ if (effectsToRemove.indexOf(effect) === -1) continue
38
+ const effectsSet = objectMap.get(target)
39
+ if (effectsSet === undefined) continue
40
+ for (const set of effectsSet.values()) {
41
+ set.delete(effect)
42
+ globalEffectsMap.delete(effect)
43
+ }
44
+ }
45
+ }
46
+
47
+ export const track = (target, key, global = false) => {
34
48
  if (currentEffect) {
35
49
  if (paused) {
36
50
  return
@@ -49,6 +63,8 @@ export const track = (target, key) => {
49
63
  effectsMap.set(key, effects)
50
64
  }
51
65
  effects.add(currentEffect)
66
+
67
+ if (global === true) globalEffectsMap.set(currentEffect, target)
52
68
  }
53
69
  }
54
70
 
@@ -28,7 +28,7 @@ export const getRaw = (value) => {
28
28
  return raw ? getRaw(raw) : value
29
29
  }
30
30
 
31
- const reactiveProxy = (original, _parent = null, _key) => {
31
+ const reactiveProxy = (original, _parent = null, _key, global) => {
32
32
  // don't create a proxy when a Blits component or an Image Texture
33
33
  // is assigned to a state variable
34
34
  if (typeof original === 'object') {
@@ -53,7 +53,7 @@ const reactiveProxy = (original, _parent = null, _key) => {
53
53
  if (Array.isArray(target)) {
54
54
  if (typeof target[key] === 'object' && target[key] !== null) {
55
55
  if (Array.isArray(target[key])) {
56
- track(target, key)
56
+ track(target, key, global)
57
57
  }
58
58
  // create a new reactive proxy
59
59
  return reactiveProxy(getRaw(target[key]), target, key)
@@ -81,7 +81,7 @@ const reactiveProxy = (original, _parent = null, _key) => {
81
81
  // handling objects (but not null values, which have object type in JS)
82
82
  if (typeof target[key] === 'object' && target[key] !== null) {
83
83
  if (Array.isArray(target[key])) {
84
- track(target, key)
84
+ track(target, key, global)
85
85
  }
86
86
  // create a new reactive proxy
87
87
  return reactiveProxy(getRaw(target[key]), target, key)
@@ -89,7 +89,7 @@ const reactiveProxy = (original, _parent = null, _key) => {
89
89
 
90
90
  // handling all other types
91
91
  // track the key on the target
92
- track(target, key)
92
+ track(target, key, global)
93
93
  // return the reflected value
94
94
  return Reflect.get(target, key, receiver)
95
95
  },
@@ -119,7 +119,7 @@ const reactiveProxy = (original, _parent = null, _key) => {
119
119
  return proxy
120
120
  }
121
121
 
122
- const reactiveDefineProperty = (target) => {
122
+ const reactiveDefineProperty = (target, global) => {
123
123
  Object.keys(target).forEach((key) => {
124
124
  let internalValue = target[key]
125
125
 
@@ -140,7 +140,7 @@ const reactiveDefineProperty = (target) => {
140
140
  enumerable: true,
141
141
  configurable: true,
142
142
  get() {
143
- track(target, key)
143
+ track(target, key, global)
144
144
  return internalValue
145
145
  },
146
146
  set(newValue) {
@@ -157,8 +157,10 @@ const reactiveDefineProperty = (target) => {
157
157
  return target
158
158
  }
159
159
 
160
- export const reactive = (target, mode = 'Proxy') => {
161
- return mode === 'defineProperty' ? reactiveDefineProperty(target) : reactiveProxy(target)
160
+ export const reactive = (target, mode = 'Proxy', global = false) => {
161
+ return mode === 'defineProperty'
162
+ ? reactiveDefineProperty(target, global)
163
+ : reactiveProxy(target, undefined, undefined, global)
162
164
  }
163
165
 
164
166
  export const memo = (raw) => {
@@ -46,4 +46,6 @@ export default {
46
46
  componentType: Symbol.for('componentType'),
47
47
  // Symbol 'isComponent' utilized within generated code
48
48
  isComponent: Symbol.for('isComponent'),
49
+ // Symbol 'effects' utilized within generated code
50
+ effects: Symbol.for('effects'),
49
51
  }
@@ -21,6 +21,6 @@ import Settings from '../settings.js'
21
21
  export default {
22
22
  name: 'appState',
23
23
  plugin(state = {}) {
24
- return reactive(state, Settings.get('reactivityMode'))
24
+ return reactive(state, Settings.get('reactivityMode'), true)
25
25
  },
26
26
  }
@@ -32,7 +32,8 @@ export default {
32
32
  language: '',
33
33
  loaded: 0,
34
34
  },
35
- Settings.get('reactivityMode')
35
+ Settings.get('reactivityMode'),
36
+ true
36
37
  )
37
38
 
38
39
  const setLanguage = (language) => {
@@ -52,7 +52,8 @@ export default {
52
52
  {
53
53
  current: 'default',
54
54
  },
55
- Settings.get('reactivityMode')
55
+ Settings.get('reactivityMode'),
56
+ true
56
57
  )
57
58
 
58
59
  let themes = {}