@lightningjs/blits 1.40.1 → 1.41.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,14 @@
1
1
  # Changelog
2
2
 
3
+ ## v1.41.0
4
+
5
+ _16 Oct 2025_
6
+
7
+ - Improved overall test coverage to 86%
8
+ - Filtered out inspector-data entirely in production
9
+ - Improved vite plugin that handles computed props to be more robust
10
+ - Fixed issue with global reactive object that are deeply nested
11
+ - Bumped MSDF generator dependency to address security issues in dependencies
3
12
 
4
13
  ## v1.40.1
5
14
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightningjs/blits",
3
- "version": "1.40.1",
3
+ "version": "1.41.0",
4
4
  "description": "Blits: The Lightning 3 App Development Framework",
5
5
  "bin": "bin/index.js",
6
6
  "exports": {
@@ -54,7 +54,7 @@
54
54
  "tape": "^5.5.0"
55
55
  },
56
56
  "dependencies": {
57
- "@lightningjs/msdf-generator": "^1.1.1",
57
+ "@lightningjs/msdf-generator": "^1.2.0",
58
58
  "@lightningjs/renderer": "^2.18.2"
59
59
  },
60
60
  "repository": {
@@ -78,7 +78,7 @@ export default {
78
78
  const index = this[symbols.intervals].indexOf(intervalId)
79
79
  if (index > -1) {
80
80
  this[symbols.intervals].splice(index, 1)
81
- clearTimeout(intervalId)
81
+ clearInterval(intervalId)
82
82
  }
83
83
  },
84
84
  writable: false,
@@ -222,6 +222,12 @@ const colorMap = {
222
222
  */
223
223
  let textDefaults = null
224
224
 
225
+ /**
226
+ * Inspector enabled flag (initialized on first use).
227
+ * @type {boolean|null}
228
+ */
229
+ let inspectorEnabled = null
230
+
225
231
  /**
226
232
  * @typedef {import('../../component.js').BlitsElement} BlitsElement
227
233
  *
@@ -465,6 +471,11 @@ const propsTransformer = {
465
471
  }
466
472
  },
467
473
  set 'inspector-data'(v) {
474
+ // Skip processing if inspector is not enabled for performance optimization
475
+ if (inspectorEnabled === false) {
476
+ return
477
+ }
478
+
468
479
  if (typeof v === 'object' || (isObjectString(v) === true && (v = parseToObject(v)))) {
469
480
  this.props['data'] = v
470
481
  }
@@ -567,10 +578,11 @@ const Element = {
567
578
  for (let i = 0; i < propsKeys.length; i++) {
568
579
  // todo: fix code duplication
569
580
  if (isTransition(value) === true) {
570
- return this.animate(propsKeys[i], this.props.props[propsKeys[i]], value.transition)
581
+ this.animate(propsKeys[i], this.props.props[propsKeys[i]], value.transition)
582
+ } else {
583
+ // set the prop to the value on the node
584
+ this.node[propsKeys[i]] = this.props.props[propsKeys[i]]
571
585
  }
572
- // set the prop to the value on the node
573
- this.node[propsKeys[i]] = this.props.props[propsKeys[i]]
574
586
  }
575
587
  }
576
588
 
@@ -740,6 +752,9 @@ export default (config, component) => {
740
752
  fontFamily: Settings.get('defaultFont', 'sans-serif'),
741
753
  }
742
754
  }
755
+ if (inspectorEnabled === null) {
756
+ inspectorEnabled = Settings.get('inspector', false)
757
+ }
743
758
  return Object.assign(Object.create(Element), {
744
759
  props: Object.assign(Object.create(propsTransformer), { props: {} }),
745
760
  scheduledTransitions: {},
@@ -179,6 +179,9 @@ const generateElementCode = function (
179
179
 
180
180
  if (key === 'key') return
181
181
 
182
+ // Skip inspector-data in production builds for performance optimization
183
+ if (key === 'inspector-data' && !isDev) return
184
+
182
185
  const value = templateObject[key]
183
186
 
184
187
  if (isReactiveKey(key)) {
@@ -46,7 +46,7 @@ const reactiveProxy = (original, _parent = null, _key, global) => {
46
46
 
47
47
  const handler = {
48
48
  get(target, key, receiver) {
49
- // return the original object instead of the proxied
49
+ // indicate that this is a proxy object
50
50
  if (key === symbols.isProxy) {
51
51
  return true
52
52
  }
@@ -62,7 +62,7 @@ const reactiveProxy = (original, _parent = null, _key, global) => {
62
62
  track(target, key, global)
63
63
  }
64
64
  // create a new reactive proxy
65
- return reactiveProxy(getRaw(target[key]), target, key)
65
+ return reactiveProxy(getRaw(target[key]), target, key, global)
66
66
  }
67
67
  // augment array path methods (that change the length of the array)
68
68
  if (arrayPatchMethods.indexOf(key) !== -1) {
@@ -90,7 +90,7 @@ const reactiveProxy = (original, _parent = null, _key, global) => {
90
90
  track(target, key, global)
91
91
  }
92
92
  // create a new reactive proxy
93
- return reactiveProxy(getRaw(target[key]), target, key)
93
+ return reactiveProxy(getRaw(target[key]), target, key, global)
94
94
  }
95
95
 
96
96
  // handling all other types
@@ -15,6 +15,51 @@
15
15
  * SPDX-License-Identifier: Apache-2.0
16
16
  */
17
17
 
18
+ // Utility function: Character-wise brace matcher that handles strings and comments
19
+ const findMatchingBrace = (text, startPos) => {
20
+ let braceLevel = 1
21
+ let position = startPos + 1
22
+ let inString = false
23
+ let stringChar = ''
24
+
25
+ for (; position < text.length && braceLevel > 0; position++) {
26
+ const char = text[position]
27
+
28
+ // Handle string boundaries (ignore escaped quotes)
29
+ if (!inString && (char === '"' || char === "'" || char === '`')) {
30
+ inString = true
31
+ stringChar = char
32
+ } else if (inString && char === stringChar && text[position - 1] !== '\\') {
33
+ inString = false
34
+ }
35
+ // Skip comments when not in strings
36
+ else if (!inString && char === '/' && text[position + 1] === '/') {
37
+ // Skip line comment
38
+ position = text.indexOf('\n', position) || text.length - 1
39
+ } else if (!inString && char === '/' && text[position + 1] === '*') {
40
+ // Skip block comment
41
+ position = text.indexOf('*/', position + 2) || text.length - 1
42
+ }
43
+ // Count braces only outside strings and comments
44
+ else if (!inString) {
45
+ if (char === '{') braceLevel++
46
+ else if (char === '}') braceLevel--
47
+ }
48
+ }
49
+
50
+ return braceLevel === 0 ? position : -1
51
+ }
52
+
53
+ // Find computed object boundaries in component config
54
+ const findComputedObjectEnd = (configObject, computedStart) => {
55
+ return findMatchingBrace(configObject, computedStart)
56
+ }
57
+
58
+ // Find individual computed function boundaries
59
+ const findComputedFunctionEnd = (computedObj, openingBracePos) => {
60
+ return findMatchingBrace(computedObj, openingBracePos)
61
+ }
62
+
18
63
  export default (code) => {
19
64
  // Find component declarations
20
65
  const componentRegex =
@@ -33,18 +78,10 @@ export default (code) => {
33
78
  // Find start position of the computed block
34
79
  const computedStart = computedKeywordMatch.index + computedKeywordMatch[0].length - 1 // Position of the opening brace
35
80
 
36
- // Find the matching closing brace with balance tracking
37
- let braceLevel = 1
38
- let pos = computedStart + 1
81
+ // Find the matching closing brace for computed object in config
82
+ const computedEnd = findComputedObjectEnd(configObject, computedStart)
39
83
 
40
- while (braceLevel > 0 && pos < configObject.length) {
41
- if (configObject[pos] === '{') braceLevel++
42
- else if (configObject[pos] === '}') braceLevel--
43
- pos++
44
- }
45
-
46
- if (braceLevel === 0) {
47
- const computedEnd = pos
84
+ if (computedEnd !== -1) {
48
85
  const computedObj = configObject.substring(computedStart, computedEnd)
49
86
 
50
87
  // Process each computed property
@@ -67,18 +104,10 @@ export default (code) => {
67
104
  const openingBracePos = computedObj.indexOf('{', funcNamePos)
68
105
  if (openingBracePos === -1) break
69
106
 
70
- // Find closing brace with proper nesting
71
- let braceLevel = 1
72
- let closingBracePos = openingBracePos + 1
73
-
74
- while (braceLevel > 0 && closingBracePos < computedObj.length) {
75
- if (computedObj[closingBracePos] === '{') braceLevel++
76
- else if (computedObj[closingBracePos] === '}') braceLevel--
77
- closingBracePos++
78
- }
107
+ // Find closing brace for individual computed function
108
+ const closingBracePos = findComputedFunctionEnd(computedObj, openingBracePos)
79
109
 
80
- if (braceLevel !== 0) {
81
- // Could not find matching brace
110
+ if (closingBracePos === -1) {
82
111
  break
83
112
  }
84
113
 
package/src/plugin.js CHANGED
@@ -15,7 +15,7 @@ const registerPlugin = (plugin, nameOrOptions = '', options = {}) => {
15
15
  }
16
16
 
17
17
  if (typeof plugin === 'function') {
18
- if (name === undefined) {
18
+ if (name === undefined || name === '') {
19
19
  throw Error('Error registering plugin: name is required for plugin')
20
20
  }
21
21
  plugins[name] = { plugin, options }