@lightningjs/blits 1.32.1-rc.0 → 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,37 +25,59 @@ 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) {
55
- if (elms[0] !== undefined) elms[0].set('texture', component.texture)
77
+ elms[0].set('texture', component.texture)
56
78
  },
57
79
  function anonymous(component, elms, context, components, rootComponent, effect) {
58
- if (elms[0] !== undefined) elms[0].set('color', component.color)
80
+ elms[0].set('color', component.color)
59
81
  },
60
82
  ],
61
83
  context: {},
@@ -546,7 +546,6 @@ const Element = {
546
546
  */
547
547
  set(prop, value) {
548
548
  if (value === undefined) return
549
- if (this.props === undefined) return
550
549
  // @ts-ignore
551
550
  if (this.props.raw[prop] === value) return
552
551
  // @ts-ignore
@@ -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,16 +184,13 @@ 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)
165
- if(${elm} !== undefined) ${elm}.set('${key.substring(1)}', ${interpolate(
166
- templateObject[key],
167
- options.component
168
- )})
187
+ if(typeof skips === 'undefined' || (typeof skips[${counter}] === 'undefined' ||
188
+ skips[${counter}].indexOf('${key.substring(1)}') === -1))
189
+ ${elm}.set('${key.substring(1)}', ${interpolate(templateObject[key], options.component)})
169
190
  `)
170
191
  } else {
171
192
  this.effectsCode.push(`
172
- if(${elm} !== undefined) ${elm}.set('${key.substring(1)}', ${interpolate(
193
+ ${elm}.set('${key.substring(1)}', ${interpolate(
173
194
  templateObject[key],
174
195
  options.component
175
196
  )})
@@ -180,23 +201,28 @@ const generateElementCode = function (
180
201
  verifyVariables(value, renderCode, 'reactive')
181
202
  }
182
203
  renderCode.push(
183
- `elementConfig${counter}['${key.substring(1)}'] = ${interpolate(value, options.component)}`
204
+ `elementConfigs[${counter}]['${key.substring(1)}'] = ${interpolate(
205
+ value,
206
+ options.component
207
+ )}`
184
208
  )
185
209
  } else {
186
210
  if (isDev === true && options.component !== 'scope.' && value.includes('$')) {
187
211
  verifyVariables(value, renderCode)
188
212
  }
189
- renderCode.push(`elementConfig${counter}['${key}'] = ${cast(value, key, options.component)}`)
213
+ renderCode.push(
214
+ `elementConfigs[${counter}]['${key}'] = ${cast(value, key, options.component)}`
215
+ )
190
216
  }
191
217
  })
192
218
 
193
219
  if (options.holder === true) {
194
220
  renderCode.push(`
195
- const skip${counter} = []
196
- if(typeof cmp${counter} !== 'undefined') {
197
- for(let key in cmp${counter}[Symbol.for('config')].props) {
198
- delete elementConfig${counter}[cmp${counter}[Symbol.for('config')].props[key]]
199
- 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])
200
226
  }
201
227
  }
202
228
  `)
@@ -206,7 +232,7 @@ const generateElementCode = function (
206
232
  renderCode.push(`if(${elm}.nodeId === undefined) {`)
207
233
  }
208
234
 
209
- renderCode.push(`${elm}.populate(elementConfig${counter})`)
235
+ renderCode.push(`${elm}.populate(elementConfigs[${counter}])`)
210
236
 
211
237
  renderCode.push(`
212
238
  if(inSlot === true) {
@@ -237,12 +263,16 @@ const generateComponentCode = function (
237
263
  const renderCode = options.forceEffect ? this.effectsCode : this.renderCode
238
264
 
239
265
  renderCode.push(`
240
- const cmp${counter} =
266
+ cmps[${counter}] =
241
267
  (context.components && context.components['${
242
268
  templateObject[Symbol.for('componentType')]
243
269
  }']) || components['${templateObject[Symbol.for('componentType')]}']
244
270
  `)
245
271
 
272
+ this.cleanupCode.push(`
273
+ cmps[${counter}] = null
274
+ `)
275
+
246
276
  if ('key' in templateObject) {
247
277
  options.key = interpolate(templateObject.key, options.component)
248
278
  }
@@ -266,10 +296,11 @@ const generateComponentCode = function (
266
296
  }
267
297
 
268
298
  if (parent) {
269
- renderCode.push(`parent = ${parent}`)
299
+ renderCode.push(`parent = ${parent};`)
270
300
  }
271
301
 
272
- renderCode.push(`const props${counter} = {}`)
302
+ renderCode.push(`props[${counter}] = {}`)
303
+ this.cleanupCode.push(`props[${counter}] = null`)
273
304
 
274
305
  if (options.forloop) {
275
306
  renderCode.push(`if(${elm} === undefined) {`)
@@ -287,28 +318,28 @@ const generateComponentCode = function (
287
318
  if (Array.isArray(propData) === true) {
288
319
  propData = getRaw(propData).slice(0)
289
320
  }
290
- props${counter}['${key.substring(1)}'] = propData`)
321
+ props[${counter}]['${key.substring(1)}'] = propData`)
291
322
  } else {
292
323
  renderCode.push(
293
- `props${counter}['${key}'] = ${cast(templateObject[key], key, options.component)}`
324
+ `props[${counter}]['${key}'] = ${cast(templateObject[key], key, options.component)}`
294
325
  )
295
326
  }
296
327
  })
297
328
 
298
329
  renderCode.push(`
299
- componentType = props${counter}['is'] || '${templateObject[Symbol.for('componentType')]}'
330
+ componentType = props[${counter}]['is'] || '${templateObject[Symbol.for('componentType')]}'
300
331
 
301
- let component${counter}
332
+ components[${counter}]
302
333
  if(typeof componentType === 'string') {
303
- component${counter} = context.components && context.components[componentType] || components[componentType]
304
- if(!component${counter}) {
334
+ components[${counter}] = context.components && context.components[componentType] || components[componentType]
335
+ if(!components[${counter}]) {
305
336
  throw new Error('Component "${templateObject[Symbol.for('componentType')]}" not found')
306
337
  }
307
338
  } else if(typeof componentType === 'function' && componentType[Symbol.for('isComponent')] === true) {
308
- component${counter} = componentType
339
+ components[${counter}] = componentType
309
340
  }
310
341
 
311
- ${elm} = component${counter}.call(null, {props: props${counter}}, ${parent}, component)
342
+ ${elm} = components[${counter}].call(null, {props: props[${counter}]}, ${parent}, component)
312
343
 
313
344
  if (${elm}[Symbol.for('slots')][0]) {
314
345
  parent = ${elm}[Symbol.for('slots')][0]
@@ -319,6 +350,8 @@ const generateComponentCode = function (
319
350
  }
320
351
  `)
321
352
 
353
+ this.cleanupCode.push(`components[${counter}] = null`)
354
+
322
355
  if (options.forloop) {
323
356
  renderCode.push('}')
324
357
  }
@@ -377,6 +410,7 @@ const generateForLoopCode = function (templateObject, parent) {
377
410
  const ctx = {
378
411
  renderCode: [],
379
412
  effectsCode: [],
413
+ cleanupCode: [],
380
414
  context: { props: [], components: this.components },
381
415
  }
382
416
 
@@ -398,12 +432,12 @@ const generateForLoopCode = function (templateObject, parent) {
398
432
  const forStartCounter = counter
399
433
 
400
434
  ctx.renderCode.push(`
401
- const created${forStartCounter} = []
435
+ created[${forStartCounter}] = []
402
436
 
403
437
  let from${forStartCounter}
404
438
  let to${forStartCounter}
405
439
 
406
- const forloop${forStartCounter} = (collection = [], elms, created) => {
440
+ forloops[${forStartCounter}] = (collection = [], elms, created) => {
407
441
  const rawCollection = getRaw(collection)
408
442
  const keys = new Set()
409
443
  let l = rawCollection.length
@@ -415,6 +449,12 @@ const generateForLoopCode = function (templateObject, parent) {
415
449
  while(l--) {
416
450
  const ${item} = rawCollection[l]
417
451
  `)
452
+
453
+ ctx.cleanupCode.push(`
454
+ created[${forStartCounter}].length = 0
455
+ forloops[${forStartCounter}] = null
456
+ `)
457
+
418
458
  // push reference of index variable
419
459
  if (index !== undefined) {
420
460
  ctx.renderCode.push(`
@@ -541,9 +581,10 @@ const generateForLoopCode = function (templateObject, parent) {
541
581
 
542
582
  for (let i = forStartCounter; i <= forEndCounter; i++) {
543
583
  destroyCode.push(`
544
- elms[${i}][key] && elms[${i}][key].destroy()
545
- delete elms[${i}][key]
546
- `)
584
+ elms[${i}][key] && elms[${i}][key].destroy()
585
+ elms[${i}][key] = null
586
+ delete elms[${i}][key]
587
+ `)
547
588
  }
548
589
  destroyCode.push(`
549
590
  }
@@ -565,14 +606,8 @@ const generateForLoopCode = function (templateObject, parent) {
565
606
  const effectKeys = [...range.matchAll(effectKeysRegex)].map((match) => `'${match[1]}'`)
566
607
 
567
608
  ctx.renderCode.push(`
568
- let forEffects${forStartCounter}
569
- const eff${forStartCounter} = () => {
570
- component[Symbol.for('removeGlobalEffects')](forEffects${forStartCounter})
571
- forEffects${forStartCounter} = null
572
- forEffects${forStartCounter} = forloop${forStartCounter}(${cast(
573
- result[2],
574
- ':for'
575
- )}, elms, created${forStartCounter})
609
+ let eff${forStartCounter} = () => {
610
+ forloops[${forStartCounter}](${cast(result[2], ':for')}, elms, created[${forStartCounter}])
576
611
  }
577
612
 
578
613
  component[Symbol.for('effects')].push(eff${forStartCounter})
@@ -580,6 +615,10 @@ const generateForLoopCode = function (templateObject, parent) {
580
615
  effect(eff${forStartCounter}, ['${effectKey}', ${effectKeys.join(',')}] )
581
616
  `)
582
617
 
618
+ ctx.cleanupCode.push(`
619
+ eff${forStartCounter} = null
620
+ `)
621
+
583
622
  outerScopeEffects.forEach((effect, outerScopeEffectsIndex) => {
584
623
  const matches = [...effect.matchAll(scopeRegex)]
585
624
 
@@ -597,7 +636,7 @@ const generateForLoopCode = function (templateObject, parent) {
597
636
  }
598
637
 
599
638
  ctx.renderCode.push(`
600
- const eff${forStartCounter}_${outerScopeEffectsIndex} = () => {
639
+ let eff${forStartCounter}_${outerScopeEffectsIndex} = () => {
601
640
  void ${refs.join(', ')}
602
641
  for(let __index = 0; __index < ${interpolate(result[2])}.length; __index++) {
603
642
  if(__index < from${forStartCounter} || __index >= to${forStartCounter}) continue
@@ -606,6 +645,7 @@ const generateForLoopCode = function (templateObject, parent) {
606
645
  scope['${item}'] = ${interpolate(result[2])}[__index]
607
646
  scope['key'] = ${forKey || '__index'}
608
647
  `)
648
+ ctx.cleanupCode.push(`eff${forStartCounter}_${outerScopeEffectsIndex} = null`)
609
649
 
610
650
  ctx.renderCode.push(`
611
651
  ${effect}
@@ -617,6 +657,7 @@ const generateForLoopCode = function (templateObject, parent) {
617
657
  })
618
658
 
619
659
  this.renderCode.push(ctx.renderCode.join('\n'))
660
+ this.cleanupCode.push(ctx.cleanupCode.join('\n'))
620
661
  }
621
662
 
622
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'),
@@ -132,7 +132,7 @@ export const matchHash = (path, routes = []) => {
132
132
  let matchingRoute = false
133
133
  let i = 0
134
134
  while (!matchingRoute && i < routes.length) {
135
- const route = routes[i]
135
+ const route = { ...routes[i] }
136
136
  route.path = normalizePath(route.path)
137
137
  if (route.path === path) {
138
138
  route.params = {}
@@ -206,7 +206,6 @@ export const navigate = async function () {
206
206
  let previousRoute = currentRoute //? Object.assign({}, currentRoute) : undefined
207
207
  const { hash, path, queryParams } = getHash()
208
208
  let route = matchHash(path, this.parent[symbols.routes])
209
-
210
209
  const queryParamsData = {}
211
210
  const queryParamsEntries = [...queryParams.entries()]
212
211
  for (let i = 0; i < queryParamsEntries.length; i++) {
@@ -214,8 +213,8 @@ export const navigate = async function () {
214
213
  }
215
214
 
216
215
  route.data = {
217
- ...navigationData,
218
216
  ...route.data,
217
+ ...navigationData,
219
218
  ...queryParamsData,
220
219
  }
221
220