@lightningjs/blits 2.0.0-beta.1 → 2.0.0-beta.2
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 +57 -0
- package/README.md +2 -0
- package/element-test-full.txt +0 -0
- package/index.d.ts +71 -7
- package/package.json +23 -3
- package/scripts/prepublishOnly.js +11 -2
- package/scripts/prepush.js +21 -1
- package/src/announcer/announcer.js +50 -4
- package/src/announcer/speechSynthesis.js +156 -37
- package/src/component/base/methods.js +4 -0
- package/src/component/base/router.js +6 -0
- package/src/component/base/timeouts_intervals.js +56 -0
- package/src/component/setup/routes.js +5 -0
- package/src/component.js +7 -1
- package/src/components/Circle.js +1 -0
- package/src/components/FPScounter.js +1 -0
- package/src/components/RouterView.js +139 -123
- package/src/components/Sprite.js +13 -7
- package/src/engines/L3/element.js +92 -7
- package/src/lib/codegenerator/generator.js +40 -8
- package/src/lib/lifecycle.js +29 -3
- package/src/lib/precompiler/precompiler.js +23 -14
- package/src/lib/reactivity/effect.js +3 -2
- package/src/lib/reactivityguard/computedprops.js +85 -14
- package/src/lib/shaders/shaders.js +2 -1
- package/src/lib/symbols.js +2 -0
- package/src/plugins/appstate.d.ts +26 -0
- package/src/plugins/index.d.ts +28 -0
- package/src/plugins/language.d.ts +37 -0
- package/src/plugins/storage/storage.d.ts +31 -0
- package/src/plugins/theme.d.ts +43 -0
- package/src/plugins/theme.js +30 -3
- package/src/router/router.js +36 -3
- package/test-output.txt +0 -0
- package/vite/preCompiler.js +15 -5
package/src/components/Sprite.js
CHANGED
|
@@ -80,6 +80,7 @@ export default () =>
|
|
|
80
80
|
let slotChildCounter = 0
|
|
81
81
|
let cmps = []
|
|
82
82
|
elementConfigs[0] = {}
|
|
83
|
+
elementConfigs[0]['___wrapper'] = true
|
|
83
84
|
|
|
84
85
|
elms[0] = this.element(
|
|
85
86
|
{ parent: parent || 'root' },
|
|
@@ -167,15 +168,20 @@ export default () =>
|
|
|
167
168
|
// Resolve frame data from sprite map
|
|
168
169
|
let options = null
|
|
169
170
|
if (
|
|
170
|
-
this.map !== undefined &&
|
|
171
171
|
this.map !== null &&
|
|
172
|
-
this.
|
|
173
|
-
this.frame !== null
|
|
172
|
+
this.map !== undefined &&
|
|
173
|
+
this.frame !== null &&
|
|
174
|
+
this.frame !== undefined
|
|
174
175
|
) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
176
|
+
if (
|
|
177
|
+
this.map.frames !== null &&
|
|
178
|
+
this.map.frames !== undefined &&
|
|
179
|
+
this.frame in this.map.frames
|
|
180
|
+
) {
|
|
181
|
+
options = Object.assign({}, this.map.defaults || {}, this.map.frames[this.frame])
|
|
182
|
+
} else if (this.frame in this.map) {
|
|
183
|
+
options = this.map[this.frame]
|
|
184
|
+
}
|
|
179
185
|
}
|
|
180
186
|
|
|
181
187
|
// If no map but frame is object (manual subtexture)
|
|
@@ -210,15 +210,29 @@ const propsTransformer = {
|
|
|
210
210
|
this.props['rotation'] = v * (Math.PI / 180)
|
|
211
211
|
},
|
|
212
212
|
set w(v) {
|
|
213
|
-
|
|
214
|
-
this.props['w'] =
|
|
213
|
+
const parsed = parsePercentage.call(this, v, 'w')
|
|
214
|
+
this.props['w'] = parsed
|
|
215
|
+
if (
|
|
216
|
+
this.___wrapper === true &&
|
|
217
|
+
this.element.component.eol !== true &&
|
|
218
|
+
this.element.component[symbols.holder] !== undefined
|
|
219
|
+
) {
|
|
220
|
+
this.element.component[symbols.holder].set('w', parsed)
|
|
221
|
+
}
|
|
215
222
|
},
|
|
216
223
|
set width(v) {
|
|
217
224
|
this.w = v
|
|
218
225
|
},
|
|
219
226
|
set h(v) {
|
|
220
|
-
|
|
221
|
-
this.props['h'] =
|
|
227
|
+
const parsed = parsePercentage.call(this, v, 'h')
|
|
228
|
+
this.props['h'] = parsed
|
|
229
|
+
if (
|
|
230
|
+
this.___wrapper === true &&
|
|
231
|
+
this.element.component.eol !== true &&
|
|
232
|
+
this.element.component[symbols.holder] !== undefined
|
|
233
|
+
) {
|
|
234
|
+
this.element.component[symbols.holder].set('h', parsed)
|
|
235
|
+
}
|
|
222
236
|
},
|
|
223
237
|
set height(v) {
|
|
224
238
|
this.h = v
|
|
@@ -587,6 +601,39 @@ const Element = {
|
|
|
587
601
|
})
|
|
588
602
|
}
|
|
589
603
|
},
|
|
604
|
+
/**
|
|
605
|
+
* Sets framework-provided inspector metadata
|
|
606
|
+
* Only sets if inspector is enabled and in dev mode
|
|
607
|
+
* @param {Object} data - Framework inspector metadata to merge
|
|
608
|
+
*/
|
|
609
|
+
setInspectorMetadata(data) {
|
|
610
|
+
// Early return if inspector not enabled (performance optimization)
|
|
611
|
+
if (inspectorEnabled !== true) {
|
|
612
|
+
return
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// Early return if element is destroyed (props.props is null)
|
|
616
|
+
if (this.props.props === undefined || this.props.props === null) {
|
|
617
|
+
return
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// Initialize data object if it doesn't exist
|
|
621
|
+
if (this.props['data'] === undefined) {
|
|
622
|
+
this.props['data'] = {}
|
|
623
|
+
}
|
|
624
|
+
if (this.props.props['data'] === undefined) {
|
|
625
|
+
this.props.props['data'] = {}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// Merge framework data (blits-* keys data attributes)
|
|
629
|
+
Object.assign(this.props['data'], data)
|
|
630
|
+
Object.assign(this.props.props['data'], data)
|
|
631
|
+
|
|
632
|
+
// Sync to renderer node so inspector can see it
|
|
633
|
+
if (this.node !== undefined && this.node !== null) {
|
|
634
|
+
this.node.data = { ...this.props.props['data'] }
|
|
635
|
+
}
|
|
636
|
+
},
|
|
590
637
|
/**
|
|
591
638
|
* Set an individual property on the node
|
|
592
639
|
*
|
|
@@ -632,13 +679,31 @@ const Element = {
|
|
|
632
679
|
}
|
|
633
680
|
},
|
|
634
681
|
animate(prop, value, transition) {
|
|
682
|
+
// Clear any existing debounce timeout for this property
|
|
683
|
+
if (this.debounceTimeouts[prop] !== undefined) {
|
|
684
|
+
clearTimeout(this.debounceTimeouts[prop])
|
|
685
|
+
Log.debug(`Cleared debounce timeout for property "${prop}"`)
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// Debounce the animation execution
|
|
689
|
+
this.debounceTimeouts[prop] = setTimeout(() => {
|
|
690
|
+
delete this.debounceTimeouts[prop]
|
|
691
|
+
this._executeAnimation(prop, value, transition)
|
|
692
|
+
}, 0)
|
|
693
|
+
},
|
|
694
|
+
_executeAnimation(prop, value, transition) {
|
|
635
695
|
// check if a transition is already scheduled to run on the same prop
|
|
636
696
|
// and cancels it if it does
|
|
697
|
+
const stateOfAnimation =
|
|
698
|
+
this.scheduledTransitions[prop] !== undefined
|
|
699
|
+
? this.scheduledTransitions[prop].f.state
|
|
700
|
+
: undefined
|
|
701
|
+
|
|
637
702
|
if (
|
|
638
|
-
|
|
639
|
-
|
|
703
|
+
stateOfAnimation !== undefined &&
|
|
704
|
+
(stateOfAnimation === 'scheduled' || stateOfAnimation === 'running')
|
|
640
705
|
) {
|
|
641
|
-
this.scheduledTransitions[prop].f.stop()
|
|
706
|
+
this.scheduledTransitions[prop].f.stop(stateOfAnimation === 'running' ? false : true)
|
|
642
707
|
}
|
|
643
708
|
|
|
644
709
|
// if current value is the same as the value to animate to, instantly resolve
|
|
@@ -674,6 +739,11 @@ const Element = {
|
|
|
674
739
|
f,
|
|
675
740
|
}
|
|
676
741
|
|
|
742
|
+
// Update inspector metadata when transition starts
|
|
743
|
+
if (inspectorEnabled === true) {
|
|
744
|
+
this.setInspectorMetadata({ 'blits-isTransitioning': true })
|
|
745
|
+
}
|
|
746
|
+
|
|
677
747
|
if (transition.start !== undefined && typeof transition.start === 'function') {
|
|
678
748
|
// fire transition start callback when animation really starts (depending on specified delay)
|
|
679
749
|
f.once('animating', () => {
|
|
@@ -708,6 +778,12 @@ const Element = {
|
|
|
708
778
|
}
|
|
709
779
|
// remove the prop from scheduled transitions
|
|
710
780
|
delete this.scheduledTransitions[prop]
|
|
781
|
+
// Update inspector metadata when transition ends
|
|
782
|
+
if (inspectorEnabled === true) {
|
|
783
|
+
this.setInspectorMetadata({
|
|
784
|
+
'blits-isTransitioning': Object.keys(this.scheduledTransitions).length > 0,
|
|
785
|
+
})
|
|
786
|
+
}
|
|
711
787
|
})
|
|
712
788
|
|
|
713
789
|
// start animation
|
|
@@ -717,6 +793,14 @@ const Element = {
|
|
|
717
793
|
if (this.node === null) return
|
|
718
794
|
|
|
719
795
|
Log.debug('Deleting Node', this.nodeId)
|
|
796
|
+
|
|
797
|
+
// Clear all pending debounce timeouts
|
|
798
|
+
const debounceProps = Object.keys(this.debounceTimeouts)
|
|
799
|
+
for (let i = 0; i < debounceProps.length; i++) {
|
|
800
|
+
clearTimeout(this.debounceTimeouts[debounceProps[i]])
|
|
801
|
+
}
|
|
802
|
+
this.debounceTimeouts = null
|
|
803
|
+
|
|
720
804
|
// Clearing transition end callback functions
|
|
721
805
|
const transitionProps = Object.keys(this.scheduledTransitions)
|
|
722
806
|
for (let i = 0; i < transitionProps.length; i++) {
|
|
@@ -799,6 +883,7 @@ export default (config, component) => {
|
|
|
799
883
|
return Object.assign(Object.create(Element), {
|
|
800
884
|
props: Object.assign(Object.create(propsTransformer), { props: {} }),
|
|
801
885
|
scheduledTransitions: {},
|
|
886
|
+
debounceTimeouts: {},
|
|
802
887
|
config,
|
|
803
888
|
component,
|
|
804
889
|
})
|
|
@@ -163,6 +163,10 @@ const generateElementCode = function (
|
|
|
163
163
|
|
|
164
164
|
renderCode.push(`elementConfigs[${counter}] = {}`)
|
|
165
165
|
|
|
166
|
+
if (counter === 0) {
|
|
167
|
+
renderCode.push(`elementConfigs[${counter}]['___wrapper'] = true `)
|
|
168
|
+
}
|
|
169
|
+
|
|
166
170
|
if (options.forloop) {
|
|
167
171
|
renderCode.push(`if(${elm} === undefined) {`)
|
|
168
172
|
}
|
|
@@ -325,9 +329,11 @@ const generateComponentCode = function (
|
|
|
325
329
|
|
|
326
330
|
const children = templateObject.children
|
|
327
331
|
delete templateObject.children
|
|
332
|
+
// Capture holder counter before generating element code (which may process children and increment counter)
|
|
333
|
+
const holderCounter = counter
|
|
328
334
|
generateElementCode.call(this, templateObject, parent, { ...options, ...{ holder: true } })
|
|
329
335
|
|
|
330
|
-
parent = options.key ? `elms[${
|
|
336
|
+
parent = options.key ? `elms[${holderCounter}][${options.key}]` : `elms[${holderCounter}]`
|
|
331
337
|
|
|
332
338
|
counter++
|
|
333
339
|
|
|
@@ -396,6 +402,21 @@ const generateComponentCode = function (
|
|
|
396
402
|
}
|
|
397
403
|
`)
|
|
398
404
|
|
|
405
|
+
if (isDev === true) {
|
|
406
|
+
const templateTagName = templateObject[Symbol.for('componentType')]
|
|
407
|
+
const holderElm = options.key
|
|
408
|
+
? `elms[${holderCounter}][${options.key}]`
|
|
409
|
+
: `elms[${holderCounter}]`
|
|
410
|
+
const componentDisplayName = `typeof componentType === 'string'
|
|
411
|
+
? componentType
|
|
412
|
+
: (componentType?.[Symbol.for('componentType')] || '${templateTagName}')`
|
|
413
|
+
renderCode.push(`
|
|
414
|
+
if (${holderElm} !== undefined && typeof ${holderElm}.setInspectorMetadata === 'function') {
|
|
415
|
+
${holderElm}.setInspectorMetadata({ 'blits-componentType': ${componentDisplayName} })
|
|
416
|
+
}
|
|
417
|
+
`)
|
|
418
|
+
}
|
|
419
|
+
|
|
399
420
|
this.cleanupCode.push(`components[${counter}] = null`)
|
|
400
421
|
|
|
401
422
|
if (options.forloop) {
|
|
@@ -451,7 +472,7 @@ const generateForLoopCode = function (templateObject, parent) {
|
|
|
451
472
|
.replace(')', '')
|
|
452
473
|
.split(/\s*,\s*/)
|
|
453
474
|
|
|
454
|
-
const scopeRegex = new RegExp(`(scope\\.(?!${item}\\.|${index}|key)(
|
|
475
|
+
const scopeRegex = new RegExp(`(scope\\.(?!${item}\\.|${index}|key)([\\w$]+))`, 'gi')
|
|
455
476
|
|
|
456
477
|
// local context
|
|
457
478
|
const ctx = {
|
|
@@ -524,10 +545,16 @@ const generateForLoopCode = function (templateObject, parent) {
|
|
|
524
545
|
|
|
525
546
|
component !== null && component[Symbol.for('removeGlobalEffects')](effects[${forStartCounter}])
|
|
526
547
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
const
|
|
530
|
-
|
|
548
|
+
const effectsToRemove = new Set(effects[${forStartCounter}].slice(0))
|
|
549
|
+
if (effectsToRemove.size > 0) {
|
|
550
|
+
const componentEffects = component?.[Symbol.for('effects')] || []
|
|
551
|
+
let writeIndex = 0
|
|
552
|
+
for (let readIndex = 0; readIndex < componentEffects.length; readIndex++) {
|
|
553
|
+
if (!effectsToRemove.has(componentEffects[readIndex])) {
|
|
554
|
+
componentEffects[writeIndex++] = componentEffects[readIndex]
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
componentEffects.length = writeIndex
|
|
531
558
|
}
|
|
532
559
|
|
|
533
560
|
effects[${forStartCounter}].length = 0
|
|
@@ -825,8 +852,13 @@ const cast = (val = '', key = false, component = 'component.') => {
|
|
|
825
852
|
}
|
|
826
853
|
// @-listener
|
|
827
854
|
else if (key.startsWith('@') && val) {
|
|
828
|
-
const
|
|
829
|
-
|
|
855
|
+
const trimmed = val.trim()
|
|
856
|
+
if (/^\$?\w+$/.test(trimmed)) {
|
|
857
|
+
const c = component.slice(0, -1)
|
|
858
|
+
castedValue = `${c}['${trimmed.replace('$', '')}'] && ${c}['${trimmed.replace('$', '')}'].bind(${c})`
|
|
859
|
+
} else {
|
|
860
|
+
castedValue = interpolate(trimmed, component)
|
|
861
|
+
}
|
|
830
862
|
}
|
|
831
863
|
// dynamic value
|
|
832
864
|
else if (val.startsWith('$')) {
|
package/src/lib/lifecycle.js
CHANGED
|
@@ -18,6 +18,9 @@
|
|
|
18
18
|
import { Log } from './log.js'
|
|
19
19
|
import { emit, privateEmit } from './hooks.js'
|
|
20
20
|
import symbols from './symbols.js'
|
|
21
|
+
import Settings from '../settings.js'
|
|
22
|
+
|
|
23
|
+
let inspectorEnabled = null
|
|
21
24
|
|
|
22
25
|
/**
|
|
23
26
|
* List of valid lifecycle states for a component.
|
|
@@ -81,9 +84,32 @@ export default {
|
|
|
81
84
|
privateEmit(v, this.component[symbols.identifier], this.component)
|
|
82
85
|
// emit 'public' hook
|
|
83
86
|
emit(v, this.component[symbols.identifier], this.component)
|
|
84
|
-
// update the built-in hasFocus state variable
|
|
85
|
-
if (v === 'focus'
|
|
86
|
-
|
|
87
|
+
// update the built-in $hasFocus state variable
|
|
88
|
+
if (v === 'focus' || v === 'unfocus') {
|
|
89
|
+
if (inspectorEnabled === null) {
|
|
90
|
+
inspectorEnabled = Settings.get('inspector', false)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (v === 'focus') {
|
|
94
|
+
this.component[symbols.state].$hasFocus = true
|
|
95
|
+
if (
|
|
96
|
+
inspectorEnabled === true &&
|
|
97
|
+
this.component[symbols.holder] &&
|
|
98
|
+
typeof this.component[symbols.holder].setInspectorMetadata === 'function'
|
|
99
|
+
) {
|
|
100
|
+
this.component[symbols.holder].setInspectorMetadata({ 'blits-hasFocus': true })
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (v === 'unfocus') {
|
|
104
|
+
this.component[symbols.state].$hasFocus = false
|
|
105
|
+
if (
|
|
106
|
+
inspectorEnabled === true &&
|
|
107
|
+
this.component[symbols.holder] &&
|
|
108
|
+
typeof this.component[symbols.holder].setInspectorMetadata === 'function'
|
|
109
|
+
) {
|
|
110
|
+
this.component[symbols.holder].setInspectorMetadata({ 'blits-hasFocus': false })
|
|
111
|
+
}
|
|
112
|
+
}
|
|
87
113
|
}
|
|
88
114
|
},
|
|
89
115
|
}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import parser from '../templateparser/parser.js'
|
|
19
19
|
import generator from '../codegenerator/generator.js'
|
|
20
|
+
import MagicString from 'magic-string'
|
|
20
21
|
|
|
21
22
|
export default (source, filePath, mode) => {
|
|
22
23
|
if (
|
|
@@ -26,12 +27,9 @@ export default (source, filePath, mode) => {
|
|
|
26
27
|
/\{.*?template\s*:\s*(['"`])((?:\\?.)*?)\1.*?\}/s.test(source) // object with template key
|
|
27
28
|
) {
|
|
28
29
|
const templates = source.matchAll(/(?<!\/\/\s*)template\s*:\s*(['"`])((?:\\?.)*?)\1/gs)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
track of the offset caused by the previous replacements
|
|
33
|
-
*/
|
|
34
|
-
let offset = 0
|
|
30
|
+
|
|
31
|
+
// Use MagicString to track changes
|
|
32
|
+
const s = new MagicString(source)
|
|
35
33
|
|
|
36
34
|
for (const template of templates) {
|
|
37
35
|
if (template[2]) {
|
|
@@ -39,7 +37,8 @@ export default (source, filePath, mode) => {
|
|
|
39
37
|
|
|
40
38
|
// Only process if it looks like a Blits template
|
|
41
39
|
if (templateContent.match(/^\s*(<!--[\s\S]*?-->|<[A-Za-z][^>]*>)/s)) {
|
|
42
|
-
|
|
40
|
+
// Use original indices - MagicString handles position tracking automatically
|
|
41
|
+
const templateStartIndex = template.index
|
|
43
42
|
const templateEndIndex = templateStartIndex + template[0].length
|
|
44
43
|
|
|
45
44
|
// Parse the template
|
|
@@ -58,16 +57,26 @@ export default (source, filePath, mode) => {
|
|
|
58
57
|
(fn) => fn.toString()
|
|
59
58
|
)}], context: {}}`
|
|
60
59
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
newSource =
|
|
64
|
-
newSource.substring(0, templateStartIndex) +
|
|
65
|
-
replacement +
|
|
66
|
-
newSource.substring(templateEndIndex)
|
|
60
|
+
// MagicString tracks all changes automatically, no offset needed
|
|
61
|
+
s.overwrite(templateStartIndex, templateEndIndex, replacement)
|
|
67
62
|
}
|
|
68
63
|
}
|
|
69
64
|
}
|
|
70
|
-
|
|
65
|
+
|
|
66
|
+
const newSource = s.toString()
|
|
67
|
+
|
|
68
|
+
if (newSource !== source) {
|
|
69
|
+
const fileName = filePath.split(/[\\/]/).pop()
|
|
70
|
+
return {
|
|
71
|
+
code: newSource,
|
|
72
|
+
map: s.generateMap({
|
|
73
|
+
hires: true,
|
|
74
|
+
source: fileName,
|
|
75
|
+
includeContent: true,
|
|
76
|
+
file: fileName,
|
|
77
|
+
}),
|
|
78
|
+
}
|
|
79
|
+
}
|
|
71
80
|
}
|
|
72
81
|
return source
|
|
73
82
|
}
|
|
@@ -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()) {
|
|
@@ -35,10 +35,12 @@ const findMatchingBrace = (text, startPos) => {
|
|
|
35
35
|
// Skip comments when not in strings
|
|
36
36
|
else if (!inString && char === '/' && text[position + 1] === '/') {
|
|
37
37
|
// Skip line comment
|
|
38
|
-
|
|
38
|
+
const nextNewline = text.indexOf('\n', position + 2)
|
|
39
|
+
position = nextNewline === -1 ? text.length - 1 : nextNewline
|
|
39
40
|
} else if (!inString && char === '/' && text[position + 1] === '*') {
|
|
40
41
|
// Skip block comment
|
|
41
|
-
|
|
42
|
+
const end = text.indexOf('*/', position + 2)
|
|
43
|
+
position = end === -1 ? text.length - 1 : end + 1
|
|
42
44
|
}
|
|
43
45
|
// Count braces only outside strings and comments
|
|
44
46
|
else if (!inString) {
|
|
@@ -60,6 +62,44 @@ const findComputedFunctionEnd = (computedObj, openingBracePos) => {
|
|
|
60
62
|
return findMatchingBrace(computedObj, openingBracePos)
|
|
61
63
|
}
|
|
62
64
|
|
|
65
|
+
// Skip whitespace + commas + comments (line and block) starting from `pos`, return next code position.
|
|
66
|
+
const skipNonCode = (text, pos) => {
|
|
67
|
+
let p = pos
|
|
68
|
+
while (p < text.length) {
|
|
69
|
+
const c = text[p]
|
|
70
|
+
|
|
71
|
+
// whitespace
|
|
72
|
+
if (c === ' ' || c === '\t' || c === '\n' || c === '\r') {
|
|
73
|
+
p++
|
|
74
|
+
continue
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// separators between object members
|
|
78
|
+
if (c === ',') {
|
|
79
|
+
p++
|
|
80
|
+
continue
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// line comment
|
|
84
|
+
if (c === '/' && text[p + 1] === '/') {
|
|
85
|
+
const nextNewline = text.indexOf('\n', p + 2)
|
|
86
|
+
p = nextNewline === -1 ? text.length : nextNewline + 1
|
|
87
|
+
continue
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// block comment
|
|
91
|
+
if (c === '/' && text[p + 1] === '*') {
|
|
92
|
+
const end = text.indexOf('*/', p + 2)
|
|
93
|
+
p = end === -1 ? text.length : end + 2
|
|
94
|
+
continue
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return p
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return p
|
|
101
|
+
}
|
|
102
|
+
|
|
63
103
|
export default (code) => {
|
|
64
104
|
// Find component declarations
|
|
65
105
|
const componentRegex =
|
|
@@ -83,6 +123,8 @@ export default (code) => {
|
|
|
83
123
|
|
|
84
124
|
if (computedEnd !== -1) {
|
|
85
125
|
const computedObj = configObject.substring(computedStart, computedEnd)
|
|
126
|
+
const computedBlockStart = computedKeywordMatch.index
|
|
127
|
+
const computedBlockEnd = computedEnd
|
|
86
128
|
|
|
87
129
|
// Process each computed property
|
|
88
130
|
const computedProps = []
|
|
@@ -93,15 +135,20 @@ export default (code) => {
|
|
|
93
135
|
// using cursor-based parsing gives better accuracy
|
|
94
136
|
while (currentPos < computedObj.length - 1) {
|
|
95
137
|
// -1 to exclude the closing brace
|
|
96
|
-
//
|
|
97
|
-
|
|
138
|
+
// Skip whitespace/comments so we don't match commented-out computed functions
|
|
139
|
+
currentPos = skipNonCode(computedObj, currentPos)
|
|
140
|
+
if (currentPos >= computedObj.length - 1) break
|
|
141
|
+
|
|
142
|
+
// Find the next function name (computed properties are `name() { ... }`)
|
|
143
|
+
const functionNameMatch = /^(\w+)\s*\(\)\s*/.exec(computedObj.substring(currentPos))
|
|
98
144
|
if (!functionNameMatch) break
|
|
99
145
|
|
|
100
146
|
const funcName = functionNameMatch[1]
|
|
101
|
-
const funcNamePos = currentPos
|
|
147
|
+
const funcNamePos = currentPos
|
|
102
148
|
|
|
103
149
|
// Find opening brace for this function
|
|
104
|
-
const
|
|
150
|
+
const afterSignaturePos = funcNamePos + functionNameMatch[0].length
|
|
151
|
+
const openingBracePos = computedObj.indexOf('{', afterSignaturePos)
|
|
105
152
|
if (openingBracePos === -1) break
|
|
106
153
|
|
|
107
154
|
// Find closing brace for individual computed function
|
|
@@ -133,14 +180,15 @@ export default (code) => {
|
|
|
133
180
|
}
|
|
134
181
|
|
|
135
182
|
// Store the original computed object for later replacement
|
|
136
|
-
const originalComputedObj = configObject.substring(
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
)
|
|
183
|
+
const originalComputedObj = configObject.substring(computedBlockStart, computedBlockEnd)
|
|
184
|
+
const computedObjOffsetInOriginal = computedStart - computedBlockStart
|
|
185
|
+
const computedObjEndInOriginal = computedObjOffsetInOriginal + computedObj.length
|
|
140
186
|
|
|
141
187
|
let modifiedComputedObj = originalComputedObj
|
|
142
188
|
let hasChanges = false
|
|
143
189
|
|
|
190
|
+
const replacements = []
|
|
191
|
+
|
|
144
192
|
for (const prop of computedProps) {
|
|
145
193
|
const thisRefs = extractThisReferences(prop.body)
|
|
146
194
|
|
|
@@ -181,13 +229,31 @@ export default (code) => {
|
|
|
181
229
|
prop.fullText.substring(0, openBraceIndex).match(/^(\s*)/)[0] +
|
|
182
230
|
'}'
|
|
183
231
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
232
|
+
replacements.push({
|
|
233
|
+
start: prop.startPos,
|
|
234
|
+
end: prop.endPos,
|
|
235
|
+
text: modifiedPropText,
|
|
236
|
+
})
|
|
187
237
|
}
|
|
188
238
|
}
|
|
189
239
|
}
|
|
190
240
|
|
|
241
|
+
if (replacements.length > 0) {
|
|
242
|
+
let modifiedComputedInner = computedObj
|
|
243
|
+
|
|
244
|
+
replacements.sort((a, b) => b.start - a.start)
|
|
245
|
+
for (const r of replacements) {
|
|
246
|
+
modifiedComputedInner =
|
|
247
|
+
modifiedComputedInner.slice(0, r.start) + r.text + modifiedComputedInner.slice(r.end)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
modifiedComputedObj =
|
|
251
|
+
originalComputedObj.slice(0, computedObjOffsetInOriginal) +
|
|
252
|
+
modifiedComputedInner +
|
|
253
|
+
originalComputedObj.slice(computedObjEndInOriginal)
|
|
254
|
+
hasChanges = true
|
|
255
|
+
}
|
|
256
|
+
|
|
191
257
|
if (hasChanges) {
|
|
192
258
|
// Store the modification of the entire computed object
|
|
193
259
|
modifications.push({
|
|
@@ -213,7 +279,12 @@ export default (code) => {
|
|
|
213
279
|
modifiedCode = modifiedCode.replace(mod.original, mod.replacement)
|
|
214
280
|
}
|
|
215
281
|
|
|
216
|
-
|
|
282
|
+
// Returning code without a source map
|
|
283
|
+
// Vite will automatically chain this with the next plugin's source map (preCompiler)
|
|
284
|
+
return {
|
|
285
|
+
code: modifiedCode,
|
|
286
|
+
map: { mappings: '' },
|
|
287
|
+
}
|
|
217
288
|
}
|
|
218
289
|
|
|
219
290
|
return null
|
|
@@ -139,11 +139,12 @@ export default {
|
|
|
139
139
|
return v
|
|
140
140
|
}
|
|
141
141
|
if (typeof v === 'string') {
|
|
142
|
-
return colors.normalize(v)
|
|
142
|
+
return colors.normalize(v, v)
|
|
143
143
|
}
|
|
144
144
|
if (Array.isArray(v) === true) {
|
|
145
145
|
return v.map((i) => this.parseProp(i))
|
|
146
146
|
}
|
|
147
|
+
return v
|
|
147
148
|
},
|
|
148
149
|
parseProps(v) {
|
|
149
150
|
if (typeof v === 'string') {
|
package/src/lib/symbols.js
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* @property {symbol} inputEvents
|
|
18
18
|
* @property {symbol} internalEvent
|
|
19
19
|
* @property {symbol} intervals
|
|
20
|
+
* @property {symbol} debounces
|
|
20
21
|
* @property {symbol} isProxy
|
|
21
22
|
* @property {symbol} launched
|
|
22
23
|
* @property {symbol} level
|
|
@@ -73,6 +74,7 @@ export default {
|
|
|
73
74
|
inputEvents: Symbol('inputEvents'),
|
|
74
75
|
internalEvent: Symbol('internalEvent'),
|
|
75
76
|
intervals: Symbol('intervals'),
|
|
77
|
+
debounces: Symbol('debounces'),
|
|
76
78
|
isProxy: Symbol('isProxy'),
|
|
77
79
|
launched: Symbol('launched'),
|
|
78
80
|
level: Symbol('level'),
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 Comcast Cable Communications Management, LLC
|
|
3
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License.
|
|
5
|
+
* You may obtain a copy of the License at
|
|
6
|
+
*
|
|
7
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
*
|
|
9
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
* See the License for the specific language governing permissions and
|
|
13
|
+
* limitations under the License.
|
|
14
|
+
*
|
|
15
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export type AppStatePlugin<TState extends Record<string, unknown> = Record<string, unknown>> = TState
|
|
19
|
+
|
|
20
|
+
declare const appState: {
|
|
21
|
+
readonly name: 'appState'
|
|
22
|
+
plugin: <TState extends Record<string, unknown>>(state?: TState) => AppStatePlugin<TState>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default appState
|
|
26
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 Comcast Cable Communications Management, LLC
|
|
3
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License.
|
|
5
|
+
* You may obtain a copy of the License at
|
|
6
|
+
*
|
|
7
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
*
|
|
9
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
* See the License for the specific language governing permissions and
|
|
13
|
+
* limitations under the License.
|
|
14
|
+
*
|
|
15
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export { default as language } from './language.js'
|
|
19
|
+
export { default as theme } from './theme.js'
|
|
20
|
+
export { default as appState } from './appstate.js'
|
|
21
|
+
export { default as storage } from './storage/storage.js'
|
|
22
|
+
|
|
23
|
+
// Re-export plugin interfaces for convenience
|
|
24
|
+
export type { LanguagePlugin, LanguagePluginOptions } from './language.js'
|
|
25
|
+
export type { ThemePlugin, ThemePluginConfig } from './theme.js'
|
|
26
|
+
export type { StoragePlugin } from './storage/storage.js'
|
|
27
|
+
export type { AppStatePlugin } from './appstate.js'
|
|
28
|
+
|