@lightningjs/blits 2.10.0 → 2.11.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,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.11.0
4
+
5
+ _28 sep 2026_
6
+
7
+ - Improved `$select()` method by adding caching
8
+ - Upgraded several dependencies
9
+ - Bumped renderer to 3.5.1
10
+
3
11
  ## 2.10.0
4
12
 
5
13
  _21 sep 2026_
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightningjs/blits",
3
- "version": "2.10.0",
3
+ "version": "2.11.0",
4
4
  "description": "Blits: The Lightning 3 App Development Framework",
5
5
  "bin": "bin/index.js",
6
6
  "exports": {
@@ -78,25 +78,25 @@
78
78
  "@babel/plugin-syntax-import-assertions": "^7.28.6",
79
79
  "@eslint/eslintrc": "^3.3.4",
80
80
  "@eslint/js": "^9.39.3",
81
- "c8": "^10.1.3",
81
+ "c8": "^12.0.0",
82
82
  "eslint": "^9.39.3",
83
83
  "eslint-config-prettier": "^10.1.8",
84
84
  "eslint-plugin-prettier": "^5.5.4",
85
85
  "fast-glob": "^3.3.3",
86
- "global-jsdom": "^26.0.0",
87
- "globals": "^16.3.0",
86
+ "global-jsdom": "^30.0.0",
87
+ "globals": "^17.12.0",
88
88
  "husky": "^9.1.7",
89
- "jsdom": "^26.0.0",
89
+ "jsdom": "^30.1.0",
90
90
  "lint-staged": "^15.5.2",
91
91
  "prettier": "^3.6.2",
92
- "sinon": "^21.0.1",
92
+ "sinon": "^22.1.0",
93
93
  "tap-diff": "^0.1.1",
94
94
  "tap-filter": "^1.0.3",
95
95
  "tape": "^5.9.0"
96
96
  },
97
97
  "dependencies": {
98
- "@lightningjs/renderer": "3.5.0",
99
- "magic-string": "^0.30.21"
98
+ "@lightningjs/renderer": "3.5.1",
99
+ "magic-string": "^1.4.1"
100
100
  },
101
101
  "repository": {
102
102
  "type": "git",
@@ -30,6 +30,8 @@ import { renderer } from '../../launch.js'
30
30
  import { keyMap } from '../../application.js'
31
31
  import { platform } from '../../platform.js'
32
32
 
33
+ const selectCache = new WeakMap()
34
+
33
35
  export default {
34
36
  $focus: {
35
37
  /**
@@ -103,6 +105,7 @@ export default {
103
105
  */
104
106
  value: function () {
105
107
  this.eol = true
108
+ selectCache.delete(this)
106
109
  removeKeyUpCallbacks(this)
107
110
  this[symbols.lifecycle].state = 'destroy'
108
111
 
@@ -197,26 +200,60 @@ export default {
197
200
  // early exit when component is marked as end of life
198
201
  if (this.eol === true) return
199
202
 
200
- let selected = null
201
- this[symbols.children].forEach((child) => {
203
+ let cache = selectCache.get(this)
204
+ if (cache !== undefined && cache.has(ref) === true) {
205
+ const selected = cache.get(ref)
206
+ if (selected !== undefined && selected.eol !== true) return selected
207
+ cache.delete(ref)
208
+ }
209
+
210
+ for (const child of this[symbols.children]) {
211
+ if (child === undefined || child === null) continue
202
212
  if (Array.isArray(child)) {
203
- child.forEach((c) => {
204
- if (c['ref'] === ref) selected = c
205
- })
213
+ const selected = child.find((c) => c['ref'] === ref)
214
+ if (selected !== undefined) {
215
+ if (cache === undefined) {
216
+ cache = new Map()
217
+ selectCache.set(this, cache)
218
+ }
219
+ cache.set(ref, selected)
220
+ return selected
221
+ }
206
222
  } else if (Object.getPrototypeOf(child) === Object.prototype) {
207
- Object.keys(child).forEach((k) => {
208
- if (child[k]['ref'] === ref) selected = child[k]
209
- })
223
+ const selected = Object.values(child).find((c) => c['ref'] === ref)
224
+ if (selected !== undefined) {
225
+ if (cache === undefined) {
226
+ cache = new Map()
227
+ selectCache.set(this, cache)
228
+ }
229
+ cache.set(ref, selected)
230
+ return selected
231
+ }
210
232
  } else {
211
- if (child['ref'] === ref) selected = child
233
+ if (child['ref'] === ref) {
234
+ if (cache === undefined) {
235
+ cache = new Map()
236
+ selectCache.set(this, cache)
237
+ }
238
+ cache.set(ref, child)
239
+ return child
240
+ }
212
241
  }
213
- })
214
- return selected
242
+ }
243
+ return null
215
244
  },
216
245
  writable: false,
217
246
  enumerable: true,
218
247
  configurable: false,
219
248
  },
249
+ [symbols.invalidateSelectCache]: {
250
+ value: function () {
251
+ selectCache.delete(this)
252
+ },
253
+ writable: false,
254
+ enumerable: false,
255
+ configurable: false,
256
+ },
220
257
  $trigger: {
221
258
  /**
222
259
  * @this {import('../../component').BlitsComponent}
@@ -223,6 +223,9 @@ const generateElementCode = function (
223
223
 
224
224
  if (key === 'key') return
225
225
 
226
+ // a ref on a component belongs to the component itself, not to its holder element
227
+ if (options.holder === true && (key === 'ref' || key === ':ref')) return
228
+
226
229
  // Skip inspector-data in production builds for performance optimization
227
230
  if (key === 'inspector-data' && !isDev) return
228
231
 
@@ -524,6 +527,8 @@ const generateForLoopCode = function (templateObject, parent) {
524
527
  }
525
528
  }
526
529
 
530
+ const invalidateSelectCache = 'ref' in templateObject
531
+
527
532
  ctx.renderCode.push(`
528
533
  created[${forStartCounter}] = []
529
534
  effects[${forStartCounter}] = []
@@ -532,6 +537,7 @@ const generateForLoopCode = function (templateObject, parent) {
532
537
  let to${forStartCounter}
533
538
 
534
539
  forloops[${forStartCounter}] = (collection = [], elms, created) => {
540
+ ${invalidateSelectCache ? "rootComponent && rootComponent[Symbol.for('invalidateSelectCache')]()" : ''}
535
541
  const rawCollection = getRaw(collection)
536
542
  const keys = new Set()
537
543
  let l = rawCollection.length
@@ -144,4 +144,6 @@ export default {
144
144
  removeEffects: Symbol.for('removeEffects'),
145
145
  // Symbol 'tagContent' utilized within generated code
146
146
  tagContent: Symbol.for('tagContent'),
147
+ // Symbol used by generated loops to invalidate cached ref lookups
148
+ invalidateSelectCache: Symbol.for('invalidateSelectCache'),
147
149
  }