@lightningjs/blits 1.44.0 → 1.45.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 CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## v1.45.0
4
+
5
+ _29 dec 2025_
6
+
7
+ - Added support for defining types for custom plugins on the Blits component definitions
8
+ - Added importable type definitions for built-in plugins
9
+ - Added support for JS sourcemaps
10
+ - Added reactivity for shader props (individual shaders, not effects via dynamic shader)
11
+ - Fixed issue with usage of Blits plugins not working in for loops
12
+ - Improved test coverage
13
+
3
14
  ## v1.44.0
4
15
 
5
16
  _20 Nov 2025_
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  # Blits - Lightning 3 App Development Framework
4
4
 
5
+ ![npm](https://img.shields.io/npm/v/@lightningjs/blits?label=Version&color=blue) ![License](https://img.shields.io/github/license/lightning-js/blits?label=License&color=blue) ![Tests](https://github.com/lightning-js/blits/workflows/Tests/badge.svg) ![Coverage](https://img.shields.io/codecov/c/github/lightning-js/blits/master?label=Coverage) ![npm](https://img.shields.io/npm/dm/@lightningjs/blits?label=Downloads&color=blue)
6
+
5
7
  With Blits, the App Development Framework for Lightning 3, it becomes a breeze to build great Lightning 3 applications.
6
8
 
7
9
  Blits is built on top of the Lightning 3 Rendererer and aims to provide a **great developer experience**, making it fun and easy to build your apps!
package/index.d.ts CHANGED
@@ -18,10 +18,10 @@
18
18
  // blits file type reference
19
19
  /// <reference path="./blits.d.ts" />
20
20
 
21
- import {type ShaderEffect as RendererShaderEffect, type WebGlCoreShader, type RendererMainSettings} from '@lightningjs/renderer'
22
-
23
21
  declare module '@lightningjs/blits' {
24
-
22
+ type RendererShaderEffect = import('@lightningjs/renderer').ShaderEffect
23
+ type WebGlCoreShader = import('@lightningjs/renderer').WebGlCoreShader
24
+ type RendererMainSettings = import('@lightningjs/renderer').RendererMainSettings
25
25
 
26
26
  export interface AnnouncerUtteranceOptions {
27
27
  /**
@@ -209,7 +209,7 @@ declare module '@lightningjs/blits' {
209
209
  }
210
210
 
211
211
  export interface Input {
212
- [key: string]: (event: KeyboardEvent) => void | undefined | unknown,
212
+ [key: string]: ((event: KeyboardEvent) => unknown) | undefined,
213
213
  /**
214
214
  * Catch all input function
215
215
  *
@@ -362,7 +362,39 @@ declare module '@lightningjs/blits' {
362
362
  }
363
363
  }
364
364
 
365
- export type ComponentBase = {
365
+ // Extension point for app- and plugin-specific fields on the component `this`.
366
+ // Add your own properties (e.g., `$telemetry`, `componentName`) via TypeScript
367
+ // module augmentation in your app, without changing core types.
368
+ // Note: `ComponentBase` extends this interface, so augmented fields appear in all
369
+ // hooks, methods, input, computed, and watch.
370
+ export interface CustomComponentProperties {
371
+ // Empty by design: extend in your app via TypeScript module augmentation.
372
+ }
373
+
374
+ export interface LanguagePlugin {
375
+ translate(key: string, ...replacements: any[]): string
376
+ readonly language: string
377
+ set(language: string): void
378
+ translations(translationsObject: Record<string, unknown>): void
379
+ load(file: string): Promise<void>
380
+ }
381
+
382
+ export interface ThemePlugin {
383
+ get<T = unknown>(key: string): T | undefined
384
+ get<T>(key: string, fallback: T): T
385
+ set(theme: string): void
386
+ }
387
+
388
+ export interface StoragePlugin {
389
+ get<T = unknown>(key: string): T | null
390
+ set(key: string, value: unknown): boolean
391
+ remove(key: string): void
392
+ clear(): void
393
+ }
394
+
395
+ export type AppStatePlugin<TState extends Record<string, unknown> = Record<string, unknown>> = TState
396
+
397
+ export interface ComponentBase extends CustomComponentProperties {
366
398
  /**
367
399
  * Indicates whether the component currently has focus
368
400
  *
@@ -624,7 +656,7 @@ declare module '@lightningjs/blits' {
624
656
  }
625
657
 
626
658
  export interface RouterHooks {
627
- init?: () => Promise<> | void;
659
+ init?: () => Promise<void> | void;
628
660
  beforeEach?: (to: Route, from: Route) => string | Route | Promise<string | Route> | void;
629
661
  error?: (err: string) => string | Route | Promise<string | Route> | void;
630
662
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightningjs/blits",
3
- "version": "1.44.0",
3
+ "version": "1.45.0",
4
4
  "description": "Blits: The Lightning 3 App Development Framework",
5
5
  "bin": "bin/index.js",
6
6
  "exports": {
@@ -8,7 +8,26 @@
8
8
  "./vite": "./vite/index.js",
9
9
  "./transitions": "./src/router/transitions/index.js",
10
10
  "./precompiler": "./src/lib/precompiler/precompiler.js",
11
- "./plugins": "./src/plugins/index.js",
11
+ "./plugins": {
12
+ "types": "./src/plugins/index.d.ts",
13
+ "default": "./src/plugins/index.js"
14
+ },
15
+ "./plugins/language": {
16
+ "types": "./src/plugins/language.d.ts",
17
+ "default": "./src/plugins/language.js"
18
+ },
19
+ "./plugins/theme": {
20
+ "types": "./src/plugins/theme.d.ts",
21
+ "default": "./src/plugins/theme.js"
22
+ },
23
+ "./plugins/appstate": {
24
+ "types": "./src/plugins/appstate.d.ts",
25
+ "default": "./src/plugins/appstate.js"
26
+ },
27
+ "./plugins/storage": {
28
+ "types": "./src/plugins/storage/storage.d.ts",
29
+ "default": "./src/plugins/storage/storage.js"
30
+ },
12
31
  "./symbols": "./src/lib/symbols.js",
13
32
  "./blitsFileConverter": "./src/lib/blitsfileconverter/blitsfileconverter.js"
14
33
  },
@@ -48,6 +67,7 @@
48
67
  "husky": "^9.1.7",
49
68
  "jsdom": "24.0.0",
50
69
  "lint-staged": "^15.5.0",
70
+ "magic-string": "^0.30.21",
51
71
  "prettier": "^3.6.2",
52
72
  "sinon": "^21.0.0",
53
73
  "tap-diff": "^0.1.1",
@@ -55,7 +55,10 @@ function processFile(filePath) {
55
55
  fs.copyFileSync(filePath, backupFilePath)
56
56
 
57
57
  const source = fs.readFileSync(filePath, 'utf-8')
58
- const newSource = compiler(source, filePath)
58
+ const result = compiler(source, filePath)
59
+
60
+ // Handle both string and object return types
61
+ const newSource = typeof result === 'object' && result.code ? result.code : result
59
62
  fs.writeFileSync(filePath, newSource)
60
63
 
61
64
  // only format the file if it was changed
@@ -121,15 +121,20 @@ export default () =>
121
121
  // Resolve frame data from sprite map
122
122
  let options = null
123
123
  if (
124
- this.map !== undefined &&
125
124
  this.map !== null &&
126
- this.frame !== undefined &&
127
- this.frame !== null
125
+ this.map !== undefined &&
126
+ this.frame !== null &&
127
+ this.frame !== undefined
128
128
  ) {
129
- options =
130
- 'frames' in this.map
131
- ? Object.assign({}, this.map.defaults || {}, this.map.frames[this.frame])
132
- : this.map[this.frame]
129
+ if (
130
+ this.map.frames !== null &&
131
+ this.map.frames !== undefined &&
132
+ this.frame in this.map.frames
133
+ ) {
134
+ options = Object.assign({}, this.map.defaults || {}, this.map.frames[this.frame])
135
+ } else if (this.frame in this.map) {
136
+ options = this.map[this.frame]
137
+ }
133
138
  }
134
139
 
135
140
  // If no map but frame is object (manual subtexture)
@@ -380,10 +380,21 @@ const propsTransformer = {
380
380
  }
381
381
  },
382
382
  set shader(v) {
383
- if (v !== null) {
384
- this.props['shader'] = renderer.createShader(v.type, v.props)
385
- } else {
386
- this.props['shader'] = renderer.createShader('DefaultShader')
383
+ const target = this.element.node !== undefined ? this.element.node : this.props
384
+
385
+ if (v === null) {
386
+ target['shader'] = null
387
+ return
388
+ }
389
+
390
+ if (typeof v === 'object' || (isObjectString(v) === true && (v = parseToObject(v)))) {
391
+ if (target.shader !== undefined && target.shader.type === v.type) {
392
+ for (const prop in v.props) {
393
+ target.shader.props[prop] = v.props[prop]
394
+ }
395
+ return
396
+ }
397
+ target['shader'] = renderer.createShader(v.type, v.props)
387
398
  }
388
399
  },
389
400
  set effects(v) {
@@ -409,7 +409,7 @@ const generateForLoopCode = function (templateObject, parent) {
409
409
  .replace(')', '')
410
410
  .split(/\s*,\s*/)
411
411
 
412
- const scopeRegex = new RegExp(`(scope\\.(?!${item}\\.|${index}|key)(\\w+))`, 'gi')
412
+ const scopeRegex = new RegExp(`(scope\\.(?!${item}\\.|${index}|key)([\\w$]+))`, 'gi')
413
413
 
414
414
  // local context
415
415
  const ctx = {
@@ -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
- let newSource = source
30
- /*
31
- if there are multiple templates in the file, we need to keep
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
- const templateStartIndex = template.index + offset
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
- offset += replacement.length - template[0].length
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
- return newSource
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
  }
@@ -213,7 +213,12 @@ export default (code) => {
213
213
  modifiedCode = modifiedCode.replace(mod.original, mod.replacement)
214
214
  }
215
215
 
216
- return { code: modifiedCode }
216
+ // Returning code without a source map
217
+ // Vite will automatically chain this with the next plugin's source map (preCompiler)
218
+ return {
219
+ code: modifiedCode,
220
+ map: { mappings: '' },
221
+ }
217
222
  }
218
223
 
219
224
  return null
@@ -0,0 +1,29 @@
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
+ import type { AppStatePlugin } from '@lightningjs/blits'
19
+
20
+ // Re-export AppStatePlugin for direct imports
21
+ export type { AppStatePlugin }
22
+
23
+ declare const appState: {
24
+ readonly name: 'appState'
25
+ plugin: <TState extends Record<string, unknown>>(state?: TState) => AppStatePlugin<TState>
26
+ }
27
+
28
+ export default appState
29
+
@@ -0,0 +1,29 @@
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, ThemePlugin, StoragePlugin, AppStatePlugin } from '@lightningjs/blits'
25
+
26
+ // Re-export option types
27
+ export type { LanguagePluginOptions } from './language.js'
28
+ export type { ThemePluginConfig } from './theme.js'
29
+
@@ -0,0 +1,34 @@
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
+ import type { LanguagePlugin } from '@lightningjs/blits'
19
+
20
+ export interface LanguagePluginOptions {
21
+ file?: string
22
+ language?: string
23
+ }
24
+
25
+ // Re-export LanguagePlugin for direct imports
26
+ export type { LanguagePlugin }
27
+
28
+ declare const language: {
29
+ readonly name: 'language'
30
+ plugin: (options?: LanguagePluginOptions) => LanguagePlugin
31
+ }
32
+
33
+ export default language
34
+
@@ -0,0 +1,29 @@
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
+ import type { StoragePlugin } from '@lightningjs/blits'
19
+
20
+ // Re-export StoragePlugin for direct imports
21
+ export type { StoragePlugin }
22
+
23
+ declare const storage: {
24
+ readonly name: 'storage'
25
+ plugin: () => StoragePlugin
26
+ }
27
+
28
+ export default storage
29
+
@@ -0,0 +1,35 @@
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
+ import type { ThemePlugin } from '@lightningjs/blits'
19
+
20
+ export interface ThemePluginConfig {
21
+ themes?: Record<string, Record<string, unknown>>
22
+ current?: string
23
+ base?: string
24
+ }
25
+
26
+ // Re-export ThemePlugin for direct imports
27
+ export type { ThemePlugin }
28
+
29
+ declare const theme: {
30
+ readonly name: 'theme'
31
+ plugin: (config?: ThemePluginConfig | Record<string, unknown>) => ThemePlugin
32
+ }
33
+
34
+ export default theme
35
+
@@ -32,12 +32,22 @@ export default function () {
32
32
 
33
33
  // we should only precompile .blits, .js and .ts files
34
34
  if (
35
- fileExtension === '.js' ||
36
- fileExtension === '.ts' ||
37
- fileExtension === '.blits' ||
38
- fileExtension === '.mjs'
35
+ filePath.indexOf('node_modules') === -1 &&
36
+ (fileExtension === '.js' ||
37
+ fileExtension === '.ts' ||
38
+ fileExtension === '.blits' ||
39
+ fileExtension === '.mjs')
39
40
  ) {
40
- return compiler(source, filePath, config.mode)
41
+ const result = compiler(source, filePath, config.mode)
42
+
43
+ if (typeof result === 'object') {
44
+ return {
45
+ code: result.code,
46
+ map: result.map,
47
+ }
48
+ }
49
+ // No transformation needed - return null so Vite knows no changes were made
50
+ return null
41
51
  }
42
52
 
43
53
  // vite expects null if there is no modification