@lightningjs/blits 1.32.1 → 1.33.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.
@@ -27,28 +27,50 @@ export default () =>
27
27
  code: {
28
28
  render: function anonymous(parent, component, context, components, effect, getRaw, Log) {
29
29
  const elms = []
30
+ const elementConfigs = []
31
+ const forloops = []
32
+ const props = []
33
+ const created = []
34
+ const skips = []
30
35
  let componentType
31
- const rootComponent = component
36
+ let rootComponent = component
32
37
  let propData
33
38
  let slotComponent
34
39
  let inSlot = false
35
40
  let slotChildCounter = 0
36
- const elementConfig0 = {}
41
+ let cmps = []
42
+ elementConfigs[0] = {}
37
43
 
38
44
  elms[0] = this.element(
39
45
  { parent: parent || 'root' },
40
46
  inSlot === true ? slotComponent : component
41
47
  )
42
48
 
43
- elementConfig0['w'] = parent.node.width * (100 / 100)
44
- elementConfig0['height'] = parent.node.height * (100 / 100)
45
- elms[0].populate(elementConfig0)
49
+ elementConfigs[0]['w'] = parent.node.width * (100 / 100)
50
+ elementConfigs[0]['height'] = parent.node.height * (100 / 100)
51
+ elms[0].populate(elementConfigs[0])
46
52
 
47
53
  if (inSlot === true) {
48
54
  slotChildCounter -= 1
49
55
  }
50
56
 
51
- return elms
57
+ return {
58
+ elms,
59
+ cleanup: () => {
60
+ rootComponent = null
61
+ propData = null
62
+ slotComponent = null
63
+ component = null
64
+ parent = null
65
+ cmps.length = 0
66
+ elms.length = 0
67
+ components.length = 0
68
+ elementConfigs.length = 0
69
+ forloops.length = 0
70
+ props.length = 0
71
+ skips.length = 0
72
+ },
73
+ }
52
74
  },
53
75
  effects: [],
54
76
  context: {},
@@ -25,30 +25,52 @@ export default () =>
25
25
  code: {
26
26
  render: function anonymous(parent, component, context, components, effect, getRaw, Log) {
27
27
  const elms = []
28
+ const elementConfigs = []
29
+ const forloops = []
30
+ const props = []
31
+ const created = []
32
+ const skips = []
28
33
  let componentType
29
- const rootComponent = component
34
+ let rootComponent = component
30
35
  let propData
31
36
  let slotComponent
32
37
  let inSlot = false
33
38
  let slotChildCounter = 0
34
- const elementConfig0 = {}
39
+ let cmps = []
40
+ elementConfigs[0] = {}
35
41
 
36
42
  elms[0] = this.element(
37
43
  { parent: parent || 'root' },
38
44
  inSlot === true ? slotComponent : component
39
45
  )
40
46
 
41
- elementConfig0['w'] = parent.node.width * (100 / 100)
42
- elementConfig0['h'] = parent.node.height * (100 / 100)
43
- elementConfig0['texture'] = component.texture
44
- elementConfig0['color'] = component.color
45
- elms[0].populate(elementConfig0)
47
+ elementConfigs[0]['w'] = parent.node.width * (100 / 100)
48
+ elementConfigs[0]['h'] = parent.node.height * (100 / 100)
49
+ elementConfigs[0]['texture'] = component.texture
50
+ elementConfigs[0]['color'] = component.color
51
+ elms[0].populate(elementConfigs[0])
46
52
 
47
53
  if (inSlot === true) {
48
54
  slotChildCounter -= 1
49
55
  }
50
56
 
51
- return elms
57
+ return {
58
+ elms,
59
+ cleanup: () => {
60
+ rootComponent = null
61
+ propData = null
62
+ slotComponent = null
63
+ component = null
64
+ parent = null
65
+ cmps.length = 0
66
+ elms.length = 0
67
+ components.length = 0
68
+ elementConfigs.length = 0
69
+ forloops.length = 0
70
+ props.length = 0
71
+ skips.length = 0
72
+ },
73
+ }
52
74
  },
53
75
  effects: [
54
76
  function anonymous(component, elms, context, components, rootComponent, effect) {
@@ -26,8 +26,8 @@ import colors from '../../lib/colors/colors.js'
26
26
  import fontLoader from './fontLoader.js'
27
27
  import shaderLoader from './shaderLoader.js'
28
28
 
29
- /** @type {RendererMain|null} */
30
- export let renderer = null
29
+ /** @type {RendererMain|{}} */
30
+ export let renderer = {}
31
31
 
32
32
  const renderEngine = (settings) => {
33
33
  const renderMode = 'renderMode' in settings ? settings.renderMode : 'webgl'
@@ -99,6 +99,7 @@ export default (App, target, settings = {}) => {
99
99
  textureProcessingTimeLimit: settings.textureProcessingTimeLimit,
100
100
  textureMemory: textureMemorySettings(settings),
101
101
  createImageBitmapSupport: 'auto',
102
+ targetFPS: 'maxFPS' in settings ? settings.maxFPS : 0,
102
103
  },
103
104
  ...(settings.advanced || {}),
104
105
  },
package/src/launch.js CHANGED
@@ -89,6 +89,7 @@ async function rendererVersion() {
89
89
  * @property {number} [textureProcessingTimeLimit] - Max time renderer can process textures in a frame
90
90
  * @property {Partial<import('@lightningjs/renderer').RendererMainSettings>} [advanced] - Advanced renderer settings
91
91
  * @property {boolean} [announcer] - Whether or not the announcer should be activated on initialization
92
+ * @property {number} [maxFPS] - Maximum FPS
92
93
  */
93
94
 
94
95
  /**
@@ -22,14 +22,27 @@ export default function (templateObject = { children: [] }, devMode = false) {
22
22
  const ctx = {
23
23
  renderCode: [
24
24
  'const elms = []',
25
+ 'const elementConfigs = []',
26
+ 'const forloops = []',
27
+ 'const props = []',
28
+ 'const created = []',
29
+ 'const skips = []',
25
30
  'let componentType',
26
- 'const rootComponent = component',
31
+ 'let rootComponent = component',
27
32
  'let propData',
28
33
  'let slotComponent',
29
34
  'let inSlot = false',
30
35
  'let slotChildCounter = 0',
36
+ 'let cmps = []',
31
37
  ],
32
38
  effectsCode: [],
39
+ cleanupCode: [
40
+ 'rootComponent = null',
41
+ 'propData = null',
42
+ 'slotComponent = null',
43
+ 'component = null',
44
+ 'parent = null',
45
+ ],
33
46
  context: { props: [], components: this.components },
34
47
  }
35
48
 
@@ -57,7 +70,18 @@ export default function (templateObject = { children: [] }, devMode = false) {
57
70
  `)
58
71
  }
59
72
  generateCode.call(ctx, templateObject)
60
- ctx.renderCode.push('return elms')
73
+ ctx.renderCode.push(`
74
+ return { elms, cleanup: () => {
75
+ ${ctx.cleanupCode.join('\n')}
76
+ cmps.length = 0
77
+ elms.length = 0
78
+ components.length = 0
79
+ elementConfigs.length = 0
80
+ forloops.length = 0
81
+ props.length = 0
82
+ skips.length = 0
83
+ }}
84
+ `)
61
85
 
62
86
  return {
63
87
  render: new Function(
@@ -124,7 +148,7 @@ const generateElementCode = function (
124
148
  `)
125
149
  }
126
150
 
127
- renderCode.push(`const elementConfig${counter} = {}`)
151
+ renderCode.push(`elementConfigs[${counter}] = {}`)
128
152
 
129
153
  if (options.forloop) {
130
154
  renderCode.push(`if(${elm} === undefined) {`)
@@ -142,13 +166,13 @@ const generateElementCode = function (
142
166
  delete templateObject['children']
143
167
 
144
168
  if (templateObject[Symbol.for('componentType')] === 'Slot') {
145
- renderCode.push(`elementConfig${counter}[Symbol.for('isSlot')] = true`)
169
+ renderCode.push(`elementConfigs[${counter}][Symbol.for('isSlot')] = true`)
146
170
  }
147
171
 
148
172
  Object.keys(templateObject).forEach((key) => {
149
173
  if (key === 'slot') {
150
174
  renderCode.push(`
151
- elementConfig${counter}['parent'] = slotComponent[Symbol.for('slots')] !== undefined && Array.isArray(slotComponent[Symbol.for('slots')]) === true && slotComponent[Symbol.for('slots')].filter(slot => slot.ref === '${templateObject.slot}').shift() || parent
175
+ elementConfigs[${counter}]['parent'] = slotComponent[Symbol.for('slots')] !== undefined && Array.isArray(slotComponent[Symbol.for('slots')]) === true && slotComponent[Symbol.for('slots')].filter(slot => slot.ref === '${templateObject.slot}').shift() || parent
152
176
  `)
153
177
  }
154
178
 
@@ -160,8 +184,8 @@ const generateElementCode = function (
160
184
  if (options.holder && key === ':color') return
161
185
  if (options.holder) {
162
186
  this.effectsCode.push(`
163
- if(typeof skip${counter} === 'undefined' ||
164
- skip${counter}.indexOf('${key.substring(1)}') === -1)
187
+ if(typeof skips === 'undefined' || (typeof skips[${counter}] === 'undefined' ||
188
+ skips[${counter}].indexOf('${key.substring(1)}') === -1))
165
189
  ${elm}.set('${key.substring(1)}', ${interpolate(templateObject[key], options.component)})
166
190
  `)
167
191
  } else {
@@ -177,23 +201,28 @@ const generateElementCode = function (
177
201
  verifyVariables(value, renderCode, 'reactive')
178
202
  }
179
203
  renderCode.push(
180
- `elementConfig${counter}['${key.substring(1)}'] = ${interpolate(value, options.component)}`
204
+ `elementConfigs[${counter}]['${key.substring(1)}'] = ${interpolate(
205
+ value,
206
+ options.component
207
+ )}`
181
208
  )
182
209
  } else {
183
210
  if (isDev === true && options.component !== 'scope.' && value.includes('$')) {
184
211
  verifyVariables(value, renderCode)
185
212
  }
186
- renderCode.push(`elementConfig${counter}['${key}'] = ${cast(value, key, options.component)}`)
213
+ renderCode.push(
214
+ `elementConfigs[${counter}]['${key}'] = ${cast(value, key, options.component)}`
215
+ )
187
216
  }
188
217
  })
189
218
 
190
219
  if (options.holder === true) {
191
220
  renderCode.push(`
192
- const skip${counter} = []
193
- if(typeof cmp${counter} !== 'undefined') {
194
- for(let key in cmp${counter}[Symbol.for('config')].props) {
195
- delete elementConfig${counter}[cmp${counter}[Symbol.for('config')].props[key]]
196
- skip${counter}.push(cmp${counter}[Symbol.for('config')].props[key])
221
+ skips[${counter}] = []
222
+ if(typeof cmps[${counter}] !== 'undefined') {
223
+ for(let key in cmps[${counter}][Symbol.for('config')].props) {
224
+ delete elementConfigs[${counter}][cmps[${counter}][Symbol.for('config')].props[key]]
225
+ skips[${counter}].push(cmps[${counter}][Symbol.for('config')].props[key])
197
226
  }
198
227
  }
199
228
  `)
@@ -203,7 +232,7 @@ const generateElementCode = function (
203
232
  renderCode.push(`if(${elm}.nodeId === undefined) {`)
204
233
  }
205
234
 
206
- renderCode.push(`${elm}.populate(elementConfig${counter})`)
235
+ renderCode.push(`${elm}.populate(elementConfigs[${counter}])`)
207
236
 
208
237
  renderCode.push(`
209
238
  if(inSlot === true) {
@@ -234,12 +263,16 @@ const generateComponentCode = function (
234
263
  const renderCode = options.forceEffect ? this.effectsCode : this.renderCode
235
264
 
236
265
  renderCode.push(`
237
- const cmp${counter} =
266
+ cmps[${counter}] =
238
267
  (context.components && context.components['${
239
268
  templateObject[Symbol.for('componentType')]
240
269
  }']) || components['${templateObject[Symbol.for('componentType')]}']
241
270
  `)
242
271
 
272
+ this.cleanupCode.push(`
273
+ cmps[${counter}] = null
274
+ `)
275
+
243
276
  if ('key' in templateObject) {
244
277
  options.key = interpolate(templateObject.key, options.component)
245
278
  }
@@ -263,10 +296,11 @@ const generateComponentCode = function (
263
296
  }
264
297
 
265
298
  if (parent) {
266
- renderCode.push(`parent = ${parent}`)
299
+ renderCode.push(`parent = ${parent};`)
267
300
  }
268
301
 
269
- renderCode.push(`const props${counter} = {}`)
302
+ renderCode.push(`props[${counter}] = {}`)
303
+ this.cleanupCode.push(`props[${counter}] = null`)
270
304
 
271
305
  if (options.forloop) {
272
306
  renderCode.push(`if(${elm} === undefined) {`)
@@ -284,28 +318,28 @@ const generateComponentCode = function (
284
318
  if (Array.isArray(propData) === true) {
285
319
  propData = getRaw(propData).slice(0)
286
320
  }
287
- props${counter}['${key.substring(1)}'] = propData`)
321
+ props[${counter}]['${key.substring(1)}'] = propData`)
288
322
  } else {
289
323
  renderCode.push(
290
- `props${counter}['${key}'] = ${cast(templateObject[key], key, options.component)}`
324
+ `props[${counter}]['${key}'] = ${cast(templateObject[key], key, options.component)}`
291
325
  )
292
326
  }
293
327
  })
294
328
 
295
329
  renderCode.push(`
296
- componentType = props${counter}['is'] || '${templateObject[Symbol.for('componentType')]}'
330
+ componentType = props[${counter}]['is'] || '${templateObject[Symbol.for('componentType')]}'
297
331
 
298
- let component${counter}
332
+ components[${counter}]
299
333
  if(typeof componentType === 'string') {
300
- component${counter} = context.components && context.components[componentType] || components[componentType]
301
- if(!component${counter}) {
334
+ components[${counter}] = context.components && context.components[componentType] || components[componentType]
335
+ if(!components[${counter}]) {
302
336
  throw new Error('Component "${templateObject[Symbol.for('componentType')]}" not found')
303
337
  }
304
338
  } else if(typeof componentType === 'function' && componentType[Symbol.for('isComponent')] === true) {
305
- component${counter} = componentType
339
+ components[${counter}] = componentType
306
340
  }
307
341
 
308
- ${elm} = component${counter}.call(null, {props: props${counter}}, ${parent}, component)
342
+ ${elm} = components[${counter}].call(null, {props: props[${counter}]}, ${parent}, component)
309
343
 
310
344
  if (${elm}[Symbol.for('slots')][0]) {
311
345
  parent = ${elm}[Symbol.for('slots')][0]
@@ -316,6 +350,8 @@ const generateComponentCode = function (
316
350
  }
317
351
  `)
318
352
 
353
+ this.cleanupCode.push(`components[${counter}] = null`)
354
+
319
355
  if (options.forloop) {
320
356
  renderCode.push('}')
321
357
  }
@@ -374,6 +410,7 @@ const generateForLoopCode = function (templateObject, parent) {
374
410
  const ctx = {
375
411
  renderCode: [],
376
412
  effectsCode: [],
413
+ cleanupCode: [],
377
414
  context: { props: [], components: this.components },
378
415
  }
379
416
 
@@ -395,12 +432,12 @@ const generateForLoopCode = function (templateObject, parent) {
395
432
  const forStartCounter = counter
396
433
 
397
434
  ctx.renderCode.push(`
398
- const created${forStartCounter} = []
435
+ created[${forStartCounter}] = []
399
436
 
400
437
  let from${forStartCounter}
401
438
  let to${forStartCounter}
402
439
 
403
- const forloop${forStartCounter} = (collection = [], elms, created) => {
440
+ forloops[${forStartCounter}] = (collection = [], elms, created) => {
404
441
  const rawCollection = getRaw(collection)
405
442
  const keys = new Set()
406
443
  let l = rawCollection.length
@@ -412,6 +449,12 @@ const generateForLoopCode = function (templateObject, parent) {
412
449
  while(l--) {
413
450
  const ${item} = rawCollection[l]
414
451
  `)
452
+
453
+ ctx.cleanupCode.push(`
454
+ created[${forStartCounter}].length = 0
455
+ forloops[${forStartCounter}] = null
456
+ `)
457
+
415
458
  // push reference of index variable
416
459
  if (index !== undefined) {
417
460
  ctx.renderCode.push(`
@@ -538,9 +581,10 @@ const generateForLoopCode = function (templateObject, parent) {
538
581
 
539
582
  for (let i = forStartCounter; i <= forEndCounter; i++) {
540
583
  destroyCode.push(`
541
- elms[${i}][key] && elms[${i}][key].destroy()
542
- delete elms[${i}][key]
543
- `)
584
+ elms[${i}][key] && elms[${i}][key].destroy()
585
+ elms[${i}][key] = null
586
+ delete elms[${i}][key]
587
+ `)
544
588
  }
545
589
  destroyCode.push(`
546
590
  }
@@ -562,14 +606,8 @@ const generateForLoopCode = function (templateObject, parent) {
562
606
  const effectKeys = [...range.matchAll(effectKeysRegex)].map((match) => `'${match[1]}'`)
563
607
 
564
608
  ctx.renderCode.push(`
565
- let forEffects${forStartCounter}
566
- const eff${forStartCounter} = () => {
567
- component[Symbol.for('removeGlobalEffects')](forEffects${forStartCounter})
568
- forEffects${forStartCounter} = null
569
- forEffects${forStartCounter} = forloop${forStartCounter}(${cast(
570
- result[2],
571
- ':for'
572
- )}, elms, created${forStartCounter})
609
+ let eff${forStartCounter} = () => {
610
+ forloops[${forStartCounter}](${cast(result[2], ':for')}, elms, created[${forStartCounter}])
573
611
  }
574
612
 
575
613
  component[Symbol.for('effects')].push(eff${forStartCounter})
@@ -577,6 +615,10 @@ const generateForLoopCode = function (templateObject, parent) {
577
615
  effect(eff${forStartCounter}, ['${effectKey}', ${effectKeys.join(',')}] )
578
616
  `)
579
617
 
618
+ ctx.cleanupCode.push(`
619
+ eff${forStartCounter} = null
620
+ `)
621
+
580
622
  outerScopeEffects.forEach((effect, outerScopeEffectsIndex) => {
581
623
  const matches = [...effect.matchAll(scopeRegex)]
582
624
 
@@ -594,7 +636,7 @@ const generateForLoopCode = function (templateObject, parent) {
594
636
  }
595
637
 
596
638
  ctx.renderCode.push(`
597
- const eff${forStartCounter}_${outerScopeEffectsIndex} = () => {
639
+ let eff${forStartCounter}_${outerScopeEffectsIndex} = () => {
598
640
  void ${refs.join(', ')}
599
641
  for(let __index = 0; __index < ${interpolate(result[2])}.length; __index++) {
600
642
  if(__index < from${forStartCounter} || __index >= to${forStartCounter}) continue
@@ -603,6 +645,7 @@ const generateForLoopCode = function (templateObject, parent) {
603
645
  scope['${item}'] = ${interpolate(result[2])}[__index]
604
646
  scope['key'] = ${forKey || '__index'}
605
647
  `)
648
+ ctx.cleanupCode.push(`eff${forStartCounter}_${outerScopeEffectsIndex} = null`)
606
649
 
607
650
  ctx.renderCode.push(`
608
651
  ${effect}
@@ -614,6 +657,7 @@ const generateForLoopCode = function (templateObject, parent) {
614
657
  })
615
658
 
616
659
  this.renderCode.push(ctx.renderCode.join('\n'))
660
+ this.cleanupCode.push(ctx.cleanupCode.join('\n'))
617
661
  }
618
662
 
619
663
  const generateCode = function (templateObject, parent = false, options = {}) {
package/src/lib/log.js CHANGED
@@ -19,16 +19,16 @@ import Settings from '../settings.js'
19
19
 
20
20
  const n = () => {}
21
21
 
22
+ const pad = (n) => String(n).padStart(2, '0')
23
+
22
24
  /**
23
25
  * Returns the current time as a locale time string (hh:mm:ss).
24
26
  * @returns {string} The current time string.
25
27
  */
26
- const time = () =>
27
- new Date().toLocaleTimeString([], {
28
- hour: '2-digit',
29
- minute: '2-digit',
30
- second: '2-digit',
31
- })
28
+ const time = () => {
29
+ const now = new Date()
30
+ return pad(now.getHours()) + ':' + pad(now.getMinutes()) + ':' + pad(now.getSeconds())
31
+ }
32
32
 
33
33
  /**
34
34
  * Returns a logger object for the given context, with info, warn, debug, and error methods.
@@ -2,6 +2,7 @@
2
2
  * Symbol registry for internal framework properties and generated code.
3
3
  *
4
4
  * @typedef {Object} BlitsSymbols
5
+ * @property {symbol} cleanup
5
6
  * @property {symbol} currentView
6
7
  * @property {symbol} cursorTagStart
7
8
  * @property {symbol} computedKeys
@@ -52,6 +53,7 @@
52
53
  * @type {BlitsSymbols}
53
54
  */
54
55
  export default {
56
+ cleanup: Symbol('cleanup'),
55
57
  currentView: Symbol('currentView'),
56
58
  cursorTagStart: Symbol('cursorTagStart'),
57
59
  computedKeys: Symbol('computedKeys'),