@lightningjs/blits 1.51.0 → 2.0.0-beta.10

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.
Files changed (45) hide show
  1. package/element-test-full.txt +0 -0
  2. package/index.d.ts +35 -50
  3. package/package.json +6 -6
  4. package/scripts/lint.js +17 -0
  5. package/scripts/prepublishOnly.js +2 -2
  6. package/scripts/prepush.js +4 -6
  7. package/src/component/base/methods.js +16 -76
  8. package/src/component/base/utils.js +18 -4
  9. package/src/component/setup/props.js +37 -28
  10. package/src/component/setup/state.js +5 -5
  11. package/src/component.js +28 -19
  12. package/src/components/Circle.js +52 -9
  13. package/src/components/FPScounter.js +224 -88
  14. package/src/components/RouterView.js +58 -6
  15. package/src/components/Sprite.js +72 -8
  16. package/src/constants.js +17 -0
  17. package/src/engine.js +17 -0
  18. package/src/engines/L3/element.js +109 -86
  19. package/src/engines/L3/fontLoader.js +43 -31
  20. package/src/engines/L3/index.js +17 -0
  21. package/src/engines/L3/launch.js +1 -0
  22. package/src/engines/L3/shaderLoader.js +73 -6
  23. package/src/engines/index.js +17 -0
  24. package/src/focus/focus.js +7 -7
  25. package/src/focus/helpers.js +4 -2
  26. package/src/focus/hover.js +7 -6
  27. package/src/helpers/fetchJson.js +4 -6
  28. package/src/launch.js +1 -2
  29. package/src/lib/codegenerator/generator.js +127 -16
  30. package/src/lib/colors/colors.js +0 -1
  31. package/src/lib/lifecycle.js +8 -10
  32. package/src/lib/log.js +4 -4
  33. package/src/lib/reactivityguard/computedprops.js +27 -13
  34. package/src/lib/shaders/shaders.js +164 -0
  35. package/src/lib/symbols.js +25 -0
  36. package/src/lib/templateparser/parser.js +6 -2
  37. package/src/lib/utils.js +53 -0
  38. package/src/plugin.js +17 -0
  39. package/src/plugins/storage/localCookie.js +4 -6
  40. package/src/router/router.js +23 -15
  41. package/test-output.txt +0 -0
  42. package/vite/blitsTips.js +57 -0
  43. package/vite/inject-dev-config.js +17 -0
  44. package/vite/msdfGenerator.js +52 -6
  45. package/vscode/data/template-attributes.json +112 -0
@@ -0,0 +1,164 @@
1
+ /*
2
+ * Copyright 2023 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 { renderer } from '../../engines/L3/launch.js'
19
+ import { parseToObject, isObjectString, isArrayString } from '../utils.js'
20
+ import colors from '../colors/colors.js'
21
+
22
+ const parseBorder = (border, props = {}, prefixed = false) => {
23
+ const prefix = prefixed === true ? 'border-' : ''
24
+ if (isNaN(border) === false) {
25
+ props[prefix + 'w'] = border
26
+ return
27
+ }
28
+ if (Array.isArray(border) === true) {
29
+ props[prefix + 'w'] = border
30
+ return
31
+ }
32
+ if (isArrayString(border) === true) {
33
+ props[prefix + 'w'] = JSON.parse(border)
34
+ return
35
+ }
36
+ if (typeof border === 'object' || isObjectString(border) === true) {
37
+ border = typeof border === 'string' ? parseToObject(border) : border
38
+ for (const key in border) {
39
+ props[prefix + key] = border[key]
40
+ }
41
+ return
42
+ }
43
+ }
44
+
45
+ const parseShadow = (shadow, props = {}, prefixed = false) => {
46
+ const prefix = prefixed === true ? 'shadow-' : ''
47
+ if (Array.isArray(shadow) === true) {
48
+ props[prefix + 'projection'] = shadow
49
+ return
50
+ }
51
+ if (isArrayString(shadow) === true) {
52
+ props[prefix + 'projection'] = JSON.parse(shadow)
53
+ return
54
+ }
55
+ if (typeof shadow === 'object' || isObjectString(shadow) === true) {
56
+ shadow = typeof shadow === 'string' ? parseToObject(shadow) : shadow
57
+ for (const key in shadow) {
58
+ props[prefix + key] = shadow[key]
59
+ }
60
+ return
61
+ }
62
+ }
63
+
64
+ const parseRounded = (rounded, props = {}) => {
65
+ if (isNaN(rounded) === false) {
66
+ props.radius = rounded
67
+ return
68
+ }
69
+ if (Array.isArray(rounded) === true) {
70
+ props.radius = rounded
71
+ return
72
+ }
73
+ if (isArrayString(rounded) === true) {
74
+ props.radius = JSON.parse(rounded)
75
+ return
76
+ }
77
+ if (typeof rounded === 'object' || isObjectString(rounded) === true) {
78
+ rounded = typeof rounded === 'string' ? parseToObject(rounded) : rounded
79
+ for (const key in rounded) {
80
+ props[key] = rounded[key]
81
+ }
82
+ return
83
+ }
84
+ }
85
+
86
+ export default {
87
+ createElementProps(v) {
88
+ let { border, shadow, rounded } = v
89
+ const props = {}
90
+ const hasRounded = rounded !== undefined
91
+ const hasBorder = border !== undefined
92
+ const hasShadow = shadow !== undefined
93
+
94
+ if (border !== undefined) {
95
+ parseBorder(border, props, hasRounded || hasShadow)
96
+ }
97
+
98
+ if (shadow !== undefined) {
99
+ parseShadow(shadow, props, hasRounded || hasBorder)
100
+ }
101
+
102
+ if (hasRounded === true) {
103
+ parseRounded(rounded, props)
104
+ }
105
+ return props
106
+ },
107
+ createElementShader(v) {
108
+ let { border, shadow, rounded } = v
109
+ const props = this.createElementProps(v)
110
+ const hasShadow = shadow !== undefined
111
+ const hasBorder = border !== undefined
112
+ const hasRounded = rounded !== undefined
113
+
114
+ if (hasBorder === true && hasShadow === true) {
115
+ return renderer.createShader('roundedWithBorderAndShadow', props)
116
+ }
117
+
118
+ if (hasRounded === true) {
119
+ if (hasBorder === true) {
120
+ return renderer.createShader('roundedWithBorder', props)
121
+ }
122
+
123
+ if (hasShadow === true) {
124
+ return renderer.createShader('roundedWithShadow', props)
125
+ }
126
+
127
+ return renderer.createShader('rounded', props)
128
+ }
129
+
130
+ if (hasRounded === false && hasBorder === true) {
131
+ return renderer.createShader('border', props)
132
+ }
133
+
134
+ //only possible result left
135
+ return renderer.createShader('shadow', props)
136
+ },
137
+ parseProp(v) {
138
+ if (typeof v === 'number') {
139
+ return v
140
+ }
141
+ if (typeof v === 'string') {
142
+ return colors.normalize(v, v)
143
+ }
144
+ if (Array.isArray(v) === true) {
145
+ return v.map((i) => this.parseProp(i))
146
+ }
147
+ return v
148
+ },
149
+ parseProps(v) {
150
+ if (typeof v === 'string') {
151
+ v = parseToObject(v)
152
+ }
153
+ const keys = Object.keys(v)
154
+ const length = keys.length
155
+ for (let i = 0; i < length; i++) {
156
+ const key = keys[i]
157
+ if (key === 'type') {
158
+ continue
159
+ }
160
+ v[key] = this.parseProp(v[key])
161
+ }
162
+ return v
163
+ },
164
+ }
@@ -1,3 +1,20 @@
1
+ /*
2
+ * Copyright 2023 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
+
1
18
  /**
2
19
  * Symbol registry for internal framework properties and generated code.
3
20
  *
@@ -21,12 +38,14 @@
21
38
  * @property {symbol} isProxy
22
39
  * @property {symbol} launched
23
40
  * @property {symbol} level
41
+ * @property {symbol} lifecycle
24
42
  * @property {symbol} methodKeys
25
43
  * @property {symbol} originalState
26
44
  * @property {symbol} propKeys
27
45
  * @property {symbol} raw
28
46
  * @property {symbol} ready
29
47
  * @property {symbol} renderer
48
+ * @property {symbol} rootParent
30
49
  * @property {symbol} routes
31
50
  * @property {symbol} routerHooks
32
51
  * @property {symbol} settings
@@ -43,6 +62,7 @@
43
62
  * @property {symbol} config
44
63
  * @property {symbol} isSlot
45
64
  * @property {symbol} props
65
+ * @property {symbol} parent
46
66
  * @property {symbol} slots
47
67
  * @property {symbol} componentType
48
68
  * @property {symbol} isComponent
@@ -75,12 +95,15 @@ export default {
75
95
  isProxy: Symbol('isProxy'),
76
96
  launched: Symbol('launched'),
77
97
  level: Symbol('level'),
98
+ lifecycle: Symbol('lifecycle'),
78
99
  methodKeys: Symbol('methodKeys'),
79
100
  originalState: Symbol('originalState'),
101
+ parent: Symbol('parent'),
80
102
  propKeys: Symbol('propKeys'),
81
103
  raw: Symbol('raw'),
82
104
  ready: Symbol('ready'),
83
105
  renderer: Symbol('renderer'),
106
+ rootParent: Symbol('rootParent'),
84
107
  routes: Symbol('routes'),
85
108
  routerHooks: Symbol('routerHooks'),
86
109
  settings: Symbol('settings'),
@@ -115,4 +138,6 @@ export default {
115
138
  effects: Symbol.for('effects'),
116
139
  // Symbol 'removeGlobalEffects' utilized within generated code
117
140
  removeGlobalEffects: Symbol.for('removeGlobalEffects'),
141
+ // Symbol 'removeGlobalEffects' utilized within generated code
142
+ tagContent: Symbol.for('tagContent'),
118
143
  }
@@ -143,7 +143,7 @@ export default (template = '', componentName, parentComponent, filePath = null)
143
143
  if (currentTag[symbols.type] === 'opening') {
144
144
  const tagContent = template.slice(cursor, template.indexOf('<', cursor))
145
145
  if (tagContent) {
146
- currentTag.content = tagContent
146
+ currentTag[Symbol.for('tagContent')] = tagContent
147
147
  cursor += tagContent.length
148
148
  }
149
149
  }
@@ -189,7 +189,11 @@ export default (template = '', componentName, parentComponent, filePath = null)
189
189
  }
190
190
 
191
191
  // process color values
192
- if (['color', ':color', ':effects', 'effects'].includes(name)) {
192
+ if (
193
+ ['color', ':color', ':border', 'border', 'shadow', ':shadow', 'shader', ':shader'].includes(
194
+ name
195
+ )
196
+ ) {
193
197
  return processColors(name, value)
194
198
  }
195
199
  return { name, value }
package/src/lib/utils.js CHANGED
@@ -1,3 +1,20 @@
1
+ /*
2
+ * Copyright 2023 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
+
1
18
  /**
2
19
  * A mapping of common screen resolution names to their scale factors relative to 1080p (Full HD).
3
20
  *
@@ -25,3 +42,39 @@ export const screenResolutions = {
25
42
  '2160p': 2,
26
43
  2160: 2,
27
44
  }
45
+
46
+ /**
47
+ * Checks if a value is a transition object.
48
+ * @param {any} value - The value to check.
49
+ * @returns {boolean} True if the value is a transition object, false otherwise.
50
+ */
51
+ export const isTransition = (value) => {
52
+ return value !== null && typeof value === 'object' && 'transition' in value === true
53
+ }
54
+
55
+ /**
56
+ * Checks if a string is an object string (starts and ends with curly braces).
57
+ * @param {string} str - The string to check.
58
+ * @returns {boolean} True if the string is an object string, false otherwise.
59
+ */
60
+ export const isObjectString = (str) => {
61
+ return typeof str === 'string' && str.startsWith('{') && str.endsWith('}')
62
+ }
63
+
64
+ /**
65
+ * Checks if a string is an array string (starts and ends with brackets).
66
+ * @param {string} str - The string to check.
67
+ * @returns {boolean} True if the string is an object string, false otherwise.
68
+ */
69
+ export const isArrayString = (str) => {
70
+ return typeof str === 'string' && str.startsWith('[') && str.endsWith(']')
71
+ }
72
+
73
+ /**
74
+ * Parses a string into an object, converting single quotes to double and adding quotes to keys.
75
+ * @param {string} str - The string to parse.
76
+ * @returns {object} The parsed object.
77
+ */
78
+ export const parseToObject = (str) => {
79
+ return JSON.parse(str.replace(/'/g, '"').replace(/([\w-_]+)\s*:/g, '"$1":'))
80
+ }
package/src/plugin.js CHANGED
@@ -1,3 +1,20 @@
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
+
1
18
  import log from './plugins/log.js'
2
19
 
3
20
  export const plugins = {
@@ -1,10 +1,6 @@
1
1
  /*
2
- * If not stated otherwise in this file or this component's LICENSE file the
3
- * following copyright and licenses apply:
4
- *
5
- * Copyright 2019 Metrological
6
- *
7
- * Licensed under the Apache License, Version 2.0 (the License);
2
+ * Copyright 2025 Comcast Cable Communications Management, LLC
3
+ * Licensed under the Apache License, Version 2.0 (the "License");
8
4
  * you may not use this file except in compliance with the License.
9
5
  * You may obtain a copy of the License at
10
6
  *
@@ -15,6 +11,8 @@
15
11
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
12
  * See the License for the specific language governing permissions and
17
13
  * limitations under the License.
14
+ *
15
+ * SPDX-License-Identifier: Apache-2.0
18
16
  */
19
17
 
20
18
  // LocalStorage and Cookie storage library
@@ -254,20 +254,24 @@ export const navigate = async function () {
254
254
  Announcer.clear()
255
255
  state.navigating = true
256
256
  let reuse = false
257
- if (preventHashChangeNavigation === false && this.parent[symbols.routes]) {
257
+ if (preventHashChangeNavigation === false && this[symbols.parent][symbols.routes]) {
258
258
  let previousRoute = currentRoute //? Object.assign({}, currentRoute) : undefined
259
- let route = matchHash(getHash(document.location.hash), this.parent[symbols.routes])
259
+ let route = matchHash(getHash(location.hash), this[symbols.parent][symbols.routes])
260
260
 
261
261
  currentRoute = route
262
262
 
263
263
  if (route) {
264
264
  const currentPath = currentRoute.path
265
265
  let beforeEachResult
266
- if (this.parent[symbols.routerHooks]) {
267
- const hooks = this.parent[symbols.routerHooks]
266
+ if (this[symbols.parent][symbols.routerHooks]) {
267
+ const hooks = this[symbols.parent][symbols.routerHooks]
268
268
  if (hooks.beforeEach) {
269
269
  try {
270
- beforeEachResult = await hooks.beforeEach.call(this.parent, route, previousRoute)
270
+ beforeEachResult = await hooks.beforeEach.call(
271
+ this[symbols.parent],
272
+ route,
273
+ previousRoute
274
+ )
271
275
  if (isString(beforeEachResult)) {
272
276
  currentRoute = previousRoute
273
277
  to(beforeEachResult)
@@ -308,7 +312,11 @@ export const navigate = async function () {
308
312
  let beforeHookOutput
309
313
  if (route.hooks.before) {
310
314
  try {
311
- beforeHookOutput = await route.hooks.before.call(this.parent, route, previousRoute)
315
+ beforeHookOutput = await route.hooks.before.call(
316
+ this[symbols.parent],
317
+ route,
318
+ previousRoute
319
+ )
312
320
  if (isString(beforeHookOutput)) {
313
321
  currentRoute = previousRoute
314
322
  to(beforeHookOutput)
@@ -517,12 +525,12 @@ export const navigate = async function () {
517
525
  }
518
526
  }
519
527
 
520
- if (this.parent[symbols.routerHooks]) {
521
- const hooks = this.parent[symbols.routerHooks]
528
+ if (this[symbols.parent][symbols.routerHooks]) {
529
+ const hooks = this[symbols.parent][symbols.routerHooks]
522
530
  if (hooks.afterEach) {
523
531
  try {
524
532
  await hooks.afterEach.call(
525
- this.parent,
533
+ this[symbols.parent],
526
534
  route, // to
527
535
  previousRoute // from
528
536
  )
@@ -535,7 +543,7 @@ export const navigate = async function () {
535
543
  if (route.hooks.after) {
536
544
  try {
537
545
  await route.hooks.after.call(
538
- this.parent,
546
+ this[symbols.parent],
539
547
  route, // to
540
548
  previousRoute // from
541
549
  )
@@ -545,9 +553,9 @@ export const navigate = async function () {
545
553
  }
546
554
  } else {
547
555
  Log.error(`Route ${route.hash} not found`)
548
- const routerHooks = this.parent[symbols.routerHooks]
556
+ const routerHooks = this[symbols.parent][symbols.routerHooks]
549
557
  if (routerHooks && typeof routerHooks.error === 'function') {
550
- routerHooks.error.call(this.parent, `Route ${route.hash} not found`)
558
+ routerHooks.error.call(this[symbols.parent], `Route ${route.hash} not found`)
551
559
  }
552
560
  }
553
561
  }
@@ -641,11 +649,11 @@ const setOrAnimate = (node, transition, shouldAnimate = true) => {
641
649
  })
642
650
  }
643
651
 
644
- export const to = (location, data = {}, options = {}) => {
652
+ export const to = (path, data = {}, options = {}) => {
645
653
  navigationData = data
646
654
  overrideOptions = options
647
655
 
648
- window.location.hash = location
656
+ location.hash = path.replace(/^#/, '')
649
657
  }
650
658
 
651
659
  export const back = function () {
@@ -682,7 +690,7 @@ export const back = function () {
682
690
  }
683
691
  // Construct new path to backtrack to
684
692
  path = path.replace(hashEnd, '')
685
- const route = matchHash(getHash(path), this.parent[symbols.routes])
693
+ const route = matchHash(getHash(path), this[symbols.parent][symbols.routes])
686
694
 
687
695
  if (route && backtrack) {
688
696
  to(route.path, route.data, route.options)
Binary file
@@ -0,0 +1,57 @@
1
+ /*
2
+ * Copyright 2025 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
+ const tips = ['tip1', 'tip2', 'tip3']
19
+
20
+ let hmrCount = 0
21
+ let lastTipLines = 0
22
+ const HMR_THRESHOLD = 4
23
+
24
+ function showTip() {
25
+ const tip = tips[Math.floor(Math.random() * tips.length)]
26
+
27
+ // Clear previous tip (move up and erase lines)
28
+ if (lastTipLines > 0) {
29
+ process.stdout.write(`\x1B[${lastTipLines}A`)
30
+ for (let i = 0; i < lastTipLines; i++) {
31
+ process.stdout.write('\x1B[2K\x1B[1E') // clear line + move down
32
+ }
33
+ process.stdout.write(`\x1B[${lastTipLines}A`)
34
+ }
35
+
36
+ // Print new tip
37
+ const lines = [`💡 Blits Tip: ${tip}`, '']
38
+ lastTipLines = lines.length
39
+ process.stdout.write(lines.join('\n') + '\n')
40
+ }
41
+
42
+ export default function blitsTipsPlugin() {
43
+ return {
44
+ name: 'vite:blits-tips',
45
+ apply: 'serve',
46
+
47
+ configureServer(server) {
48
+ server.watcher.on('change', () => {
49
+ hmrCount++
50
+ if (hmrCount >= HMR_THRESHOLD) {
51
+ showTip()
52
+ hmrCount = 0
53
+ }
54
+ })
55
+ },
56
+ }
57
+ }
@@ -1,3 +1,20 @@
1
+ /*
2
+ * Copyright 2025 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
+
1
18
  export default function injectDevFlag() {
2
19
  return {
3
20
  name: 'inject-dev-config',
@@ -18,8 +18,7 @@
18
18
  import path from 'path'
19
19
  import * as fs from 'fs'
20
20
  import { createHash } from 'crypto'
21
- import { genFont, setGeneratePaths } from '@lightningjs/msdf-generator'
22
- import { adjustFont } from '@lightningjs/msdf-generator/adjustFont'
21
+ import { createRequire } from 'module'
23
22
 
24
23
  class TaskQueue {
25
24
  constructor() {
@@ -42,6 +41,48 @@ let config
42
41
  let checksumPaths = []
43
42
  let checksumData = []
44
43
 
44
+ let msdfLoaded = false
45
+ let genFont, setGeneratePaths, adjustFont
46
+ async function loadMsdfModules() {
47
+ // do not try to load it repeatedly
48
+ if (msdfLoaded) return true
49
+
50
+ try {
51
+ // Create a require function that resolves from the app's working directory
52
+ // This ensures the msdf-generator is resolved from the app's node_modules
53
+ const userRequire = createRequire(process.cwd() + '/')
54
+
55
+ const msdfGeneratorPath = userRequire.resolve('@lightningjs/msdf-generator')
56
+ const msdfGeneratorUrl = new URL(`file:///${msdfGeneratorPath.replace(/\\/g, '/')}`).href
57
+ const msdfGenerator = await import(msdfGeneratorUrl)
58
+ genFont = msdfGenerator.genFont
59
+ setGeneratePaths = msdfGenerator.setGeneratePaths
60
+
61
+ const adjustFontPath = userRequire.resolve('@lightningjs/msdf-generator/adjustFont')
62
+ const adjustFontUrl = new URL(`file:///${adjustFontPath.replace(/\\/g, '/')}`).href
63
+ const adjustFontModule = await import(adjustFontUrl)
64
+ adjustFont = adjustFontModule.adjustFont
65
+ return true
66
+ } catch {
67
+ return false
68
+ }
69
+ }
70
+
71
+ async function getMsdfReady() {
72
+ msdfLoaded = await loadMsdfModules()
73
+
74
+ if (!msdfLoaded) {
75
+ console.error('\n\x1b[31mError: @lightningjs/msdf-generator package is not installed.\x1b[0m')
76
+ console.error(
77
+ 'This package is required for generating MSDF fonts during development and build.\n'
78
+ )
79
+ console.error('\nPlease install it as a dev dependency in your project:\n')
80
+ console.error(' \x1b[33mnpm install --save-dev @lightningjs/msdf-generator\x1b[0m\n')
81
+
82
+ process.exit(1)
83
+ }
84
+ }
85
+
45
86
  export default function () {
46
87
  let msdfOutputDir = ''
47
88
  let buildOutputPath = ''
@@ -156,15 +197,20 @@ export default function () {
156
197
  }
157
198
 
158
199
  const generateSDF = async (inputFilePath, outputDirPath, configFilePath) => {
200
+ // plugin only required if there is a msdf conversion
201
+ // so it is better to check when generateSDF is called
202
+ await getMsdfReady()
203
+
159
204
  // Ensure the destination directory exists
160
205
  fs.mkdirSync(outputDirPath, { recursive: true })
161
-
162
206
  setGeneratePaths(path.dirname(inputFilePath), outputDirPath, configFilePath)
163
207
 
164
208
  let font = await genFont(path.basename(inputFilePath), 'msdf')
165
-
166
- if (font) await adjustFont(font)
167
- else console.error('Failed to generate MSDF file')
209
+ if (font) {
210
+ await adjustFont(font)
211
+ } else {
212
+ console.error('Failed to generate MSDF file')
213
+ }
168
214
  }
169
215
 
170
216
  // Finds all font files (.ttf, .otf, .woff) in a directory