@lightningjs/blits 1.40.1 → 1.42.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 +20 -0
- package/package.json +2 -2
- package/src/component/base/timeouts_intervals.js +1 -1
- package/src/components/Sprite.js +5 -1
- package/src/engines/L3/element.js +20 -4
- package/src/lib/codegenerator/generator.js +3 -0
- package/src/lib/reactivity/reactive.js +3 -3
- package/src/lib/reactivityguard/computedprops.js +51 -22
- package/src/plugin.js +1 -1
- package/src/router/router.js +15 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v1.42.0
|
|
4
|
+
|
|
5
|
+
_24 Oct 2025_
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
- Added effects prop support to Sprite component
|
|
9
|
+
- Added support for redirecting in before hooks returning a route object (with updated path)
|
|
10
|
+
- Fixed meta options on routes functionality
|
|
11
|
+
- Enhanced router hook test coverage
|
|
12
|
+
- Fixed scale animation snapping issue when using object notation with transitions
|
|
13
|
+
|
|
14
|
+
## v1.41.0
|
|
15
|
+
|
|
16
|
+
_16 Oct 2025_
|
|
17
|
+
|
|
18
|
+
- Improved overall test coverage to 86%
|
|
19
|
+
- Filtered out inspector-data entirely in production
|
|
20
|
+
- Improved vite plugin that handles computed props to be more robust
|
|
21
|
+
- Fixed issue with global reactive object that are deeply nested
|
|
22
|
+
- Bumped MSDF generator dependency to address security issues in dependencies
|
|
3
23
|
|
|
4
24
|
## v1.40.1
|
|
5
25
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightningjs/blits",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.42.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.
|
|
57
|
+
"@lightningjs/msdf-generator": "^1.2.0",
|
|
58
58
|
"@lightningjs/renderer": "^2.18.2"
|
|
59
59
|
},
|
|
60
60
|
"repository": {
|
package/src/components/Sprite.js
CHANGED
|
@@ -49,6 +49,7 @@ export default () =>
|
|
|
49
49
|
elementConfigs[0]['h'] = parent.node.height * (100 / 100)
|
|
50
50
|
elementConfigs[0]['texture'] = component.texture
|
|
51
51
|
elementConfigs[0]['color'] = component.color
|
|
52
|
+
elementConfigs[0]['effects'] = component.effects
|
|
52
53
|
elms[0].populate(elementConfigs[0])
|
|
53
54
|
|
|
54
55
|
if (inSlot === true) {
|
|
@@ -80,10 +81,13 @@ export default () =>
|
|
|
80
81
|
function anonymous(component, elms, context, components, rootComponent, effect) {
|
|
81
82
|
elms[0].set('color', component.color)
|
|
82
83
|
},
|
|
84
|
+
function anonymous(component, elms, context, components, rootComponent, effect) {
|
|
85
|
+
elms[0].set('effects', component.effects)
|
|
86
|
+
},
|
|
83
87
|
],
|
|
84
88
|
context: {},
|
|
85
89
|
},
|
|
86
|
-
props: ['image', 'map', 'frame', 'color'],
|
|
90
|
+
props: ['image', 'map', 'frame', 'color', 'effects'],
|
|
87
91
|
state() {
|
|
88
92
|
return {
|
|
89
93
|
spriteTexture: null,
|
|
@@ -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
|
*
|
|
@@ -351,7 +357,8 @@ const propsTransformer = {
|
|
|
351
357
|
this.props['scaleY'] = v.y
|
|
352
358
|
}
|
|
353
359
|
} else {
|
|
354
|
-
this.props['
|
|
360
|
+
this.props['scaleX'] = v
|
|
361
|
+
this.props['scaleY'] = v
|
|
355
362
|
}
|
|
356
363
|
},
|
|
357
364
|
set show(v) {
|
|
@@ -465,6 +472,11 @@ const propsTransformer = {
|
|
|
465
472
|
}
|
|
466
473
|
},
|
|
467
474
|
set 'inspector-data'(v) {
|
|
475
|
+
// Skip processing if inspector is not enabled for performance optimization
|
|
476
|
+
if (inspectorEnabled === false) {
|
|
477
|
+
return
|
|
478
|
+
}
|
|
479
|
+
|
|
468
480
|
if (typeof v === 'object' || (isObjectString(v) === true && (v = parseToObject(v)))) {
|
|
469
481
|
this.props['data'] = v
|
|
470
482
|
}
|
|
@@ -567,10 +579,11 @@ const Element = {
|
|
|
567
579
|
for (let i = 0; i < propsKeys.length; i++) {
|
|
568
580
|
// todo: fix code duplication
|
|
569
581
|
if (isTransition(value) === true) {
|
|
570
|
-
|
|
582
|
+
this.animate(propsKeys[i], this.props.props[propsKeys[i]], value.transition)
|
|
583
|
+
} else {
|
|
584
|
+
// set the prop to the value on the node
|
|
585
|
+
this.node[propsKeys[i]] = this.props.props[propsKeys[i]]
|
|
571
586
|
}
|
|
572
|
-
// set the prop to the value on the node
|
|
573
|
-
this.node[propsKeys[i]] = this.props.props[propsKeys[i]]
|
|
574
587
|
}
|
|
575
588
|
}
|
|
576
589
|
|
|
@@ -740,6 +753,9 @@ export default (config, component) => {
|
|
|
740
753
|
fontFamily: Settings.get('defaultFont', 'sans-serif'),
|
|
741
754
|
}
|
|
742
755
|
}
|
|
756
|
+
if (inspectorEnabled === null) {
|
|
757
|
+
inspectorEnabled = Settings.get('inspector', false)
|
|
758
|
+
}
|
|
743
759
|
return Object.assign(Object.create(Element), {
|
|
744
760
|
props: Object.assign(Object.create(propsTransformer), { props: {} }),
|
|
745
761
|
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
|
-
//
|
|
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
|
|
37
|
-
|
|
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
|
-
|
|
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
|
|
71
|
-
|
|
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 (
|
|
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 }
|
package/src/router/router.js
CHANGED
|
@@ -220,6 +220,7 @@ const makeRouteObject = (route, overrides) => {
|
|
|
220
220
|
hooks: route.hooks || {},
|
|
221
221
|
data: { ...route.data, ...navigationData, ...overrides.queryParams },
|
|
222
222
|
params: overrides.params || {},
|
|
223
|
+
meta: route.meta || {},
|
|
223
224
|
}
|
|
224
225
|
|
|
225
226
|
return cleanRoute
|
|
@@ -249,21 +250,25 @@ export const navigate = async function () {
|
|
|
249
250
|
currentRoute = route
|
|
250
251
|
|
|
251
252
|
if (route) {
|
|
253
|
+
const currentPath = currentRoute.path
|
|
252
254
|
let beforeEachResult
|
|
253
255
|
if (this.parent[symbols.routerHooks]) {
|
|
254
256
|
const hooks = this.parent[symbols.routerHooks]
|
|
255
257
|
if (hooks.beforeEach) {
|
|
256
258
|
beforeEachResult = await hooks.beforeEach.call(this.parent, route, previousRoute)
|
|
257
259
|
if (isString(beforeEachResult)) {
|
|
260
|
+
currentRoute = previousRoute
|
|
258
261
|
to(beforeEachResult)
|
|
259
262
|
return
|
|
260
263
|
}
|
|
264
|
+
// If the resolved result is an object, redirect if the path in the object was changed
|
|
265
|
+
if (isObject(beforeEachResult) === true && beforeEachResult.path !== currentPath) {
|
|
266
|
+
currentRoute = previousRoute
|
|
267
|
+
to(beforeEachResult.path, beforeEachResult.data, beforeEachResult.options)
|
|
268
|
+
return
|
|
269
|
+
}
|
|
261
270
|
}
|
|
262
271
|
}
|
|
263
|
-
// If the resolved result is an object, assign it to the target route object
|
|
264
|
-
if (isObject(beforeEachResult) === true) {
|
|
265
|
-
route = beforeEachResult
|
|
266
|
-
}
|
|
267
272
|
|
|
268
273
|
let beforeHookOutput
|
|
269
274
|
if (route.hooks.before) {
|
|
@@ -273,10 +278,12 @@ export const navigate = async function () {
|
|
|
273
278
|
to(beforeHookOutput)
|
|
274
279
|
return
|
|
275
280
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
281
|
+
// If the resolved result is an object, redirect if the path in the object was changed
|
|
282
|
+
if (isObject(beforeHookOutput) === true && beforeHookOutput.path !== currentPath) {
|
|
283
|
+
currentRoute = previousRoute
|
|
284
|
+
to(beforeHookOutput.path, beforeHookOutput.data, beforeHookOutput.options)
|
|
285
|
+
return
|
|
286
|
+
}
|
|
280
287
|
}
|
|
281
288
|
// add the previous route (technically still the current route at this point)
|
|
282
289
|
// into the history stack when inHistory is true and we're not navigating back
|