@lightningjs/blits 1.47.0 → 1.48.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 +14 -0
- package/package.json +2 -2
- package/scripts/prepush.js +21 -1
- package/src/component.js +3 -0
- package/src/engines/L3/element.js +33 -6
- package/src/lib/codegenerator/generator.js +22 -11
- package/src/lib/lifecycle.js +2 -2
- package/src/lib/reactivity/effect.js +3 -2
- package/src/router/router.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
|
|
4
|
+
## v1.48.0
|
|
5
|
+
|
|
6
|
+
_10 feb 2026_
|
|
7
|
+
|
|
8
|
+
- Improved cleanup of for-loop effects
|
|
9
|
+
- Fixed router issue with params not being reset
|
|
10
|
+
- Added next tick debouncing for transitions on multiple props
|
|
11
|
+
- Bumped renderer to 2.21.0
|
|
12
|
+
- Added support for using function-notation in `@loaded`, `@error` and `@updated` callbacks
|
|
13
|
+
- Added displaying of correct component type in inspector data when using `is`-attribute
|
|
14
|
+
- Replaced `$`-prefix for automatic inspector data with `blits-` prefix (fixes fatal error in Safari)
|
|
15
|
+
|
|
16
|
+
|
|
3
17
|
## v1.47.0
|
|
4
18
|
|
|
5
19
|
_26 jan 2026_
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightningjs/blits",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.48.0",
|
|
4
4
|
"description": "Blits: The Lightning 3 App Development Framework",
|
|
5
5
|
"bin": "bin/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
},
|
|
75
75
|
"dependencies": {
|
|
76
76
|
"@lightningjs/msdf-generator": "^1.2.0",
|
|
77
|
-
"@lightningjs/renderer": "^2.
|
|
77
|
+
"@lightningjs/renderer": "^2.21.0",
|
|
78
78
|
"magic-string": "^0.30.21"
|
|
79
79
|
},
|
|
80
80
|
"repository": {
|
package/scripts/prepush.js
CHANGED
|
@@ -69,7 +69,6 @@ try {
|
|
|
69
69
|
// Run ESLint on the changed files
|
|
70
70
|
execSync(`npx eslint ${fileList.join(' ')}`, { stdio: 'inherit' })
|
|
71
71
|
console.log('Linting passed successfully!\n')
|
|
72
|
-
process.exit(0)
|
|
73
72
|
} catch (error) {
|
|
74
73
|
console.log('\n==========================================================================')
|
|
75
74
|
console.log('ESLint found issues in your code. Please fix them before pushing.')
|
|
@@ -79,3 +78,24 @@ try {
|
|
|
79
78
|
console.log('==========================================================================\n')
|
|
80
79
|
process.exit(1)
|
|
81
80
|
}
|
|
81
|
+
|
|
82
|
+
// By default, always run tests unless RUN_TEST=skip
|
|
83
|
+
const runTestsFlag = process.env.RUN_TEST || ''
|
|
84
|
+
|
|
85
|
+
if (runTestsFlag.toLowerCase() === 'skip') {
|
|
86
|
+
console.log('Skipping tests due to RUN_TEST=skip. Proceeding with push.')
|
|
87
|
+
process.exit(0)
|
|
88
|
+
} else {
|
|
89
|
+
// For any other value (including empty or unknown), run tests
|
|
90
|
+
try {
|
|
91
|
+
console.log('Running tests...\n')
|
|
92
|
+
execSync('npm run test', { stdio: 'inherit' })
|
|
93
|
+
console.log('Tests passed successfully!\n')
|
|
94
|
+
process.exit(0)
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.log('\n==========================================================================')
|
|
97
|
+
console.log('Some tests failed. Please fix them before pushing.')
|
|
98
|
+
console.log('==========================================================================\n')
|
|
99
|
+
process.exit(1)
|
|
100
|
+
}
|
|
101
|
+
}
|
package/src/component.js
CHANGED
|
@@ -450,6 +450,9 @@ const Component = (name = required('name'), config = required('config')) => {
|
|
|
450
450
|
// during the code generation step
|
|
451
451
|
factory[Symbol.for('config')] = config
|
|
452
452
|
|
|
453
|
+
// Display name for inspector
|
|
454
|
+
factory[Symbol.for('componentType')] = name
|
|
455
|
+
|
|
453
456
|
// To determine whether dynamic component is actual Blits component or not
|
|
454
457
|
factory[symbols.isComponent] = true
|
|
455
458
|
|
|
@@ -602,7 +602,7 @@ const Element = {
|
|
|
602
602
|
this.props.props['data'] = {}
|
|
603
603
|
}
|
|
604
604
|
|
|
605
|
-
// Merge framework data (
|
|
605
|
+
// Merge framework data (blits-* keys data attributes)
|
|
606
606
|
Object.assign(this.props['data'], data)
|
|
607
607
|
Object.assign(this.props.props['data'], data)
|
|
608
608
|
|
|
@@ -656,13 +656,31 @@ const Element = {
|
|
|
656
656
|
}
|
|
657
657
|
},
|
|
658
658
|
animate(prop, value, transition) {
|
|
659
|
+
// Clear any existing debounce timeout for this property
|
|
660
|
+
if (this.debounceTimeouts[prop] !== undefined) {
|
|
661
|
+
clearTimeout(this.debounceTimeouts[prop])
|
|
662
|
+
Log.debug(`Cleared debounce timeout for property "${prop}"`)
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// Debounce the animation execution
|
|
666
|
+
this.debounceTimeouts[prop] = setTimeout(() => {
|
|
667
|
+
delete this.debounceTimeouts[prop]
|
|
668
|
+
this._executeAnimation(prop, value, transition)
|
|
669
|
+
}, 0)
|
|
670
|
+
},
|
|
671
|
+
_executeAnimation(prop, value, transition) {
|
|
659
672
|
// check if a transition is already scheduled to run on the same prop
|
|
660
673
|
// and cancels it if it does
|
|
674
|
+
const stateOfAnimation =
|
|
675
|
+
this.scheduledTransitions[prop] !== undefined
|
|
676
|
+
? this.scheduledTransitions[prop].f.state
|
|
677
|
+
: undefined
|
|
678
|
+
|
|
661
679
|
if (
|
|
662
|
-
|
|
663
|
-
|
|
680
|
+
stateOfAnimation !== undefined &&
|
|
681
|
+
(stateOfAnimation === 'scheduled' || stateOfAnimation === 'running')
|
|
664
682
|
) {
|
|
665
|
-
this.scheduledTransitions[prop].f.stop()
|
|
683
|
+
this.scheduledTransitions[prop].f.stop(stateOfAnimation === 'running' ? false : true)
|
|
666
684
|
}
|
|
667
685
|
|
|
668
686
|
// if current value is the same as the value to animate to, instantly resolve
|
|
@@ -700,7 +718,7 @@ const Element = {
|
|
|
700
718
|
|
|
701
719
|
// Update inspector metadata when transition starts
|
|
702
720
|
if (inspectorEnabled === true) {
|
|
703
|
-
this.setInspectorMetadata({
|
|
721
|
+
this.setInspectorMetadata({ 'blits-isTransitioning': true })
|
|
704
722
|
}
|
|
705
723
|
|
|
706
724
|
if (transition.start !== undefined && typeof transition.start === 'function') {
|
|
@@ -740,7 +758,7 @@ const Element = {
|
|
|
740
758
|
// Update inspector metadata when transition ends
|
|
741
759
|
if (inspectorEnabled === true) {
|
|
742
760
|
this.setInspectorMetadata({
|
|
743
|
-
|
|
761
|
+
'blits-isTransitioning': Object.keys(this.scheduledTransitions).length > 0,
|
|
744
762
|
})
|
|
745
763
|
}
|
|
746
764
|
})
|
|
@@ -752,6 +770,14 @@ const Element = {
|
|
|
752
770
|
if (this.node === null) return
|
|
753
771
|
|
|
754
772
|
Log.debug('Deleting Node', this.nodeId)
|
|
773
|
+
|
|
774
|
+
// Clear all pending debounce timeouts
|
|
775
|
+
const debounceProps = Object.keys(this.debounceTimeouts)
|
|
776
|
+
for (let i = 0; i < debounceProps.length; i++) {
|
|
777
|
+
clearTimeout(this.debounceTimeouts[debounceProps[i]])
|
|
778
|
+
}
|
|
779
|
+
this.debounceTimeouts = null
|
|
780
|
+
|
|
755
781
|
// Clearing transition end callback functions
|
|
756
782
|
const transitionProps = Object.keys(this.scheduledTransitions)
|
|
757
783
|
for (let i = 0; i < transitionProps.length; i++) {
|
|
@@ -834,6 +860,7 @@ export default (config, component) => {
|
|
|
834
860
|
return Object.assign(Object.create(Element), {
|
|
835
861
|
props: Object.assign(Object.create(propsTransformer), { props: {} }),
|
|
836
862
|
scheduledTransitions: {},
|
|
863
|
+
debounceTimeouts: {},
|
|
837
864
|
config,
|
|
838
865
|
component,
|
|
839
866
|
})
|
|
@@ -360,17 +360,17 @@ const generateComponentCode = function (
|
|
|
360
360
|
}
|
|
361
361
|
`)
|
|
362
362
|
|
|
363
|
-
// For forloops, this code runs per instance, setting metadata for each component instance
|
|
364
363
|
if (isDev === true) {
|
|
365
|
-
const
|
|
364
|
+
const templateTagName = templateObject[Symbol.for('componentType')]
|
|
366
365
|
const holderElm = options.key
|
|
367
366
|
? `elms[${holderCounter}][${options.key}]`
|
|
368
367
|
: `elms[${holderCounter}]`
|
|
368
|
+
const componentDisplayName = `typeof componentType === 'string'
|
|
369
|
+
? componentType
|
|
370
|
+
: (componentType?.[Symbol.for('componentType')] || '${templateTagName}')`
|
|
369
371
|
renderCode.push(`
|
|
370
372
|
if (${holderElm} !== undefined && typeof ${holderElm}.setInspectorMetadata === 'function') {
|
|
371
|
-
${holderElm}.setInspectorMetadata({
|
|
372
|
-
$componentType: '${componentType}'
|
|
373
|
-
})
|
|
373
|
+
${holderElm}.setInspectorMetadata({ 'blits-componentType': ${componentDisplayName} })
|
|
374
374
|
}
|
|
375
375
|
`)
|
|
376
376
|
}
|
|
@@ -503,10 +503,16 @@ const generateForLoopCode = function (templateObject, parent) {
|
|
|
503
503
|
|
|
504
504
|
component !== null && component[Symbol.for('removeGlobalEffects')](effects[${forStartCounter}])
|
|
505
505
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
const
|
|
509
|
-
|
|
506
|
+
const effectsToRemove = new Set(effects[${forStartCounter}].slice(0))
|
|
507
|
+
if (effectsToRemove.size > 0) {
|
|
508
|
+
const componentEffects = component?.[Symbol.for('effects')] || []
|
|
509
|
+
let writeIndex = 0
|
|
510
|
+
for (let readIndex = 0; readIndex < componentEffects.length; readIndex++) {
|
|
511
|
+
if (!effectsToRemove.has(componentEffects[readIndex])) {
|
|
512
|
+
componentEffects[writeIndex++] = componentEffects[readIndex]
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
componentEffects.length = writeIndex
|
|
510
516
|
}
|
|
511
517
|
|
|
512
518
|
effects[${forStartCounter}].length = 0
|
|
@@ -804,8 +810,13 @@ const cast = (val = '', key = false, component = 'component.') => {
|
|
|
804
810
|
}
|
|
805
811
|
// @-listener
|
|
806
812
|
else if (key.startsWith('@') && val) {
|
|
807
|
-
const
|
|
808
|
-
|
|
813
|
+
const trimmed = val.trim()
|
|
814
|
+
if (/^\$?\w+$/.test(trimmed)) {
|
|
815
|
+
const c = component.slice(0, -1)
|
|
816
|
+
castedValue = `${c}['${trimmed.replace('$', '')}'] && ${c}['${trimmed.replace('$', '')}'].bind(${c})`
|
|
817
|
+
} else {
|
|
818
|
+
castedValue = interpolate(trimmed, component)
|
|
819
|
+
}
|
|
809
820
|
}
|
|
810
821
|
// dynamic value
|
|
811
822
|
else if (val.startsWith('$')) {
|
package/src/lib/lifecycle.js
CHANGED
|
@@ -97,7 +97,7 @@ export default {
|
|
|
97
97
|
this.component[symbols.holder] &&
|
|
98
98
|
typeof this.component[symbols.holder].setInspectorMetadata === 'function'
|
|
99
99
|
) {
|
|
100
|
-
this.component[symbols.holder].setInspectorMetadata({
|
|
100
|
+
this.component[symbols.holder].setInspectorMetadata({ 'blits-hasFocus': true })
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
if (v === 'unfocus') {
|
|
@@ -107,7 +107,7 @@ export default {
|
|
|
107
107
|
this.component[symbols.holder] &&
|
|
108
108
|
typeof this.component[symbols.holder].setInspectorMetadata === 'function'
|
|
109
109
|
) {
|
|
110
|
-
this.component[symbols.holder].setInspectorMetadata({
|
|
110
|
+
this.component[symbols.holder].setInspectorMetadata({ 'blits-hasFocus': false })
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
}
|
|
@@ -32,9 +32,10 @@ const objectMap = new WeakMap()
|
|
|
32
32
|
const globalEffectsMap = new Map()
|
|
33
33
|
|
|
34
34
|
export const removeGlobalEffects = (effectsToRemove) => {
|
|
35
|
-
if (globalEffectsMap.size === 0) return
|
|
35
|
+
if (globalEffectsMap.size === 0 || effectsToRemove.length === 0) return
|
|
36
|
+
const effectsToRemoveSet = new Set(effectsToRemove)
|
|
36
37
|
for (const [effect, target] of globalEffectsMap) {
|
|
37
|
-
if (
|
|
38
|
+
if (!effectsToRemoveSet.has(effect)) continue
|
|
38
39
|
const effectsSet = objectMap.get(target)
|
|
39
40
|
if (effectsSet === undefined) continue
|
|
40
41
|
for (const set of effectsSet.values()) {
|
package/src/router/router.js
CHANGED
|
@@ -403,7 +403,7 @@ export const navigate = async function () {
|
|
|
403
403
|
// Update router state after announcements and final route resolution,
|
|
404
404
|
// right before initializing or restoring the view
|
|
405
405
|
state.path = route.path
|
|
406
|
-
state.params = route.params
|
|
406
|
+
state.params = Object.keys(route.params).length === 0 ? null : route.params
|
|
407
407
|
state.hash = route.hash
|
|
408
408
|
state.data = null
|
|
409
409
|
state.data = route.data || {}
|