@lightningjs/blits 1.7.1 → 1.8.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightningjs/blits",
3
- "version": "1.7.1",
3
+ "version": "1.8.0",
4
4
  "description": "Blits: The Lightning 3 App Development Framework",
5
5
  "bin": "bin/index.js",
6
6
  "exports": {
@@ -19,6 +19,7 @@
19
19
  "prepublishOnly": "node scripts/prepublishOnly.js",
20
20
  "postpublish": "node scripts/postpublish.js"
21
21
  },
22
+ "types": "./index.d.ts",
22
23
  "lint-staged": {
23
24
  "*.js": [
24
25
  "eslint --fix"
@@ -47,8 +48,8 @@
47
48
  "tape": "^5.5.0"
48
49
  },
49
50
  "dependencies": {
50
- "@lightningjs/msdf-generator": "^1.0.2",
51
- "@lightningjs/renderer": "^2.3.2"
51
+ "@lightningjs/msdf-generator": "^1.1.0",
52
+ "@lightningjs/renderer": "^2.4.0"
52
53
  },
53
54
  "repository": {
54
55
  "type": "git",
@@ -386,13 +386,21 @@ const Element = {
386
386
  f,
387
387
  }
388
388
 
389
- if (transition.start && typeof transition.start === 'function') {
389
+ if (transition.start !== undefined && typeof transition.start === 'function') {
390
390
  // fire transition start callback when animation really starts (depending on specified delay)
391
391
  f.once('animating', () => {
392
392
  transition.start.call(this.component, this, prop, startValue)
393
393
  })
394
394
  }
395
395
 
396
+ if (transition.progress !== undefined && typeof transition.progress === 'function') {
397
+ let prevProgress = 0
398
+ f.on('tick', (_node, { progress }) => {
399
+ transition.progress.call(this.component, this, prop, progress, prevProgress)
400
+ prevProgress = progress
401
+ })
402
+ }
403
+
396
404
  f.once('stopped', () => {
397
405
  // remove the prop from scheduled transitions
398
406
  this.scheduledTransitions[prop] = undefined
@@ -1,5 +1,6 @@
1
1
  import path from 'path'
2
2
  import * as fs from 'fs'
3
+ import { createHash } from 'crypto'
3
4
  import { genFont, setGeneratePaths } from '@lightningjs/msdf-generator'
4
5
  import { adjustFont } from '@lightningjs/msdf-generator/adjustFont'
5
6
 
@@ -21,6 +22,8 @@ class TaskQueue {
21
22
 
22
23
  const fontGenerationQueue = new TaskQueue()
23
24
  let config
25
+ let checksumPaths = []
26
+ let checksumData = []
24
27
 
25
28
  export default function () {
26
29
  let msdfOutputDir = ''
@@ -53,10 +56,12 @@ export default function () {
53
56
  // Attempt to find the font file with supported extensions
54
57
  const supportedExtensions = ['ttf', 'otf', 'woff']
55
58
  let fontFile = null
59
+ let fontType = null
56
60
  for (const fontExt of supportedExtensions) {
57
61
  const potentialPath = path.join(fontDir, `${fontName}.${fontExt}`)
58
62
  if (fs.existsSync(potentialPath)) {
59
63
  fontFile = potentialPath
64
+ fontType = '.' + fontExt
60
65
  break
61
66
  }
62
67
  }
@@ -70,10 +75,13 @@ export default function () {
70
75
  const mimeType = ext === 'png' ? 'image/png' : 'application/json'
71
76
 
72
77
  await fontGenerationQueue.enqueue(async () => {
73
- if (!fs.existsSync(generatedFontFile)) {
74
- // Check if generation is needed
78
+ if (isGenerationRequired(fontDir, targetDir, fontName, fontType)) {
79
+ const configFilePath = path.join(fontDir, `${fontName}.config.json`)
80
+
75
81
  console.log(`\nGenerating ${targetDir}/${fontName}.msdf.${ext}`)
76
- await generateSDF(fontFile, path.join(targetDir))
82
+ await generateSDF(fontFile, path.join(targetDir), configFilePath)
83
+
84
+ saveChecksum()
77
85
  }
78
86
  })
79
87
 
@@ -109,14 +117,17 @@ export default function () {
109
117
 
110
118
  for (const fontFile of fontFiles) {
111
119
  const relativePath = path.relative(publicDir, path.dirname(fontFile))
120
+ const fontExt = path.extname(fontFile)
112
121
  const baseName = path.basename(fontFile).replace(/\.(ttf|otf|woff)$/i, '')
113
- const msdfJsonPath = path.join(msdfOutputDir, relativePath, `${baseName}.msdf.json`)
114
- const msdfPngPath = path.join(msdfOutputDir, relativePath, `${baseName}.msdf.png`)
122
+ const targetDir = path.join(msdfOutputDir, relativePath)
115
123
 
116
- // Check if MSDF files are generated, if not, generate them
117
- if (!fs.existsSync(msdfJsonPath) || !fs.existsSync(msdfPngPath)) {
124
+ // Check MSDF generation is required
125
+ if (isGenerationRequired(path.dirname(fontFile), targetDir, baseName, fontExt)) {
126
+ const configFilePath = path.join(path.dirname(fontFile), `${baseName}.config.json`)
118
127
  console.log(`Generating missing MSDF files for ${fontFile}`)
119
- await generateSDF(fontFile, path.join(msdfOutputDir, relativePath))
128
+ await generateSDF(fontFile, path.join(msdfOutputDir, relativePath), configFilePath)
129
+
130
+ saveChecksum()
120
131
  }
121
132
  }
122
133
 
@@ -127,11 +138,11 @@ export default function () {
127
138
  }
128
139
  }
129
140
 
130
- const generateSDF = async (inputFilePath, outputDirPath) => {
141
+ const generateSDF = async (inputFilePath, outputDirPath, configFilePath) => {
131
142
  // Ensure the destination directory exists
132
143
  fs.mkdirSync(outputDirPath, { recursive: true })
133
144
 
134
- setGeneratePaths(path.dirname(inputFilePath), outputDirPath)
145
+ setGeneratePaths(path.dirname(inputFilePath), outputDirPath, configFilePath)
135
146
 
136
147
  let font = await genFont(path.basename(inputFilePath), 'msdf')
137
148
 
@@ -168,6 +179,74 @@ const copyDir = (src, dest) => {
168
179
  const srcPath = path.join(src, entry.name)
169
180
  const destPath = path.join(dest, entry.name)
170
181
 
182
+ // Should not copy .checksum files to dist directory
183
+ if (srcPath.indexOf('.checksum') !== -1) continue
184
+
171
185
  entry.isDirectory() ? copyDir(srcPath, destPath) : fs.copyFileSync(srcPath, destPath) // Copy files
172
186
  }
173
187
  }
188
+
189
+ // Check font config.json or font file modified since last generation
190
+ const isGenerationRequired = (fontDir, targetDir, fontName, fontExt) => {
191
+ const fontFilePath = path.resolve(fontDir, fontName + fontExt)
192
+ const fontChecksumPath = path.resolve(targetDir, fontName + fontExt + '.checksum')
193
+
194
+ const configJsonPath = path.resolve(fontDir, fontName + '.config.json')
195
+ const configChecksumPath = path.resolve(targetDir, fontName + '.config.checksum')
196
+
197
+ // If font file checksum not exists, should generate msdf
198
+ if (!fs.existsSync(fontChecksumPath)) {
199
+ checksumPaths.push(fontChecksumPath)
200
+ checksumData.push(generateHash(fontFilePath))
201
+
202
+ if (fs.existsSync(configJsonPath)) {
203
+ checksumPaths.push(configChecksumPath)
204
+ checksumData.push(generateHash(configJsonPath))
205
+ }
206
+ return true
207
+ }
208
+
209
+ // If config.json exists, can be newly added or modified since last generation of msdf
210
+ if (fs.existsSync(configJsonPath)) {
211
+ // If config.json checksum not exists, should generate msdf
212
+ if (!fs.existsSync(configChecksumPath)) {
213
+ checksumPaths.push(configChecksumPath)
214
+ checksumData.push(generateHash(configJsonPath))
215
+ return true
216
+ }
217
+ const isConfigModified = isInputFileModified(configJsonPath, configChecksumPath)
218
+
219
+ // If config.json is modified, should generate msdf
220
+ if (isConfigModified) return true
221
+ }
222
+
223
+ // Check font file modified, if modified, should generate msdf
224
+ return isInputFileModified(fontFilePath, fontChecksumPath)
225
+ }
226
+
227
+ const isInputFileModified = (inputFilePath, targetFilePath) => {
228
+ const inputChecksum = generateHash(inputFilePath)
229
+ const targetChecksum = fs.readFileSync(targetFilePath).toString()
230
+ if (inputChecksum !== targetChecksum) {
231
+ checksumPaths.push(targetFilePath)
232
+ checksumData.push(inputChecksum)
233
+ return true
234
+ }
235
+ return false
236
+ }
237
+
238
+ const generateHash = (filePath) => {
239
+ const buffer = fs.readFileSync(filePath)
240
+ return createHash('md5').update(buffer).digest('hex')
241
+ }
242
+
243
+ const saveChecksum = () => {
244
+ if (checksumPaths.length > 0 && checksumData.length > 0) {
245
+ for (let i = 0; i < checksumPaths.length; i++) {
246
+ // Save checksum
247
+ fs.writeFileSync(checksumPaths[i], checksumData[i])
248
+ }
249
+ checksumData.length = 0
250
+ checksumPaths.length = 0
251
+ }
252
+ }
@@ -1,174 +0,0 @@
1
-
2
- /*
3
- * Copyright 2023 Comcast Cable Communications Management, LLC
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- * http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- *
16
- * SPDX-License-Identifier: Apache-2.0
17
- */
18
-
19
- import {default as Component} from './component'
20
-
21
- declare namespace Application {
22
-
23
- interface Transition {
24
- /**
25
- * Name of the prop to transition (i.e. 'x', 'y', 'alpha', 'color')
26
- */
27
- prop: string,
28
- /**
29
- * Value the prop should transition to (i.e. 0, 100, '#223388')
30
- */
31
- value: any,
32
- /**
33
- * Duration of the transition in milliseconds, defaults to 300
34
- */
35
- duration?: number,
36
- /**
37
- * Easing function to apply to the transition
38
- */
39
- easing?: string,
40
- /**
41
- * Delay before the transition starts in milliseconds
42
- */
43
- delay?: number
44
- }
45
-
46
- interface Before {
47
- /**
48
- * Name of the prop to set before the transition starts
49
- */
50
- prop: string,
51
- /**
52
- * Value the prop to set before the transition starts
53
- */
54
- value: any,
55
- }
56
-
57
- interface RouteTransition {
58
- /**
59
- * Setting or Array of Settings before new view enters into the router view
60
- */
61
- before: Before | Before[],
62
- /**
63
- * Transition or Array of Transitions for new view to enters into the router view
64
- */
65
- in: Transition | Transition[],
66
- /**
67
- * Transition or Array of Transitions for old view to leave the router view
68
- */
69
- out: Transition | Transition[],
70
- }
71
-
72
- type RouteTransitionFunction = (previousRoute: Route, currentRoute: Route) => RequireAtLeastOne<RouteTransition>
73
-
74
- interface RouteAnnounce {
75
- /**
76
- * Message to be announced
77
- */
78
- message: String,
79
- /**
80
- * Politeness level
81
- *
82
- * Defaults to 'off'
83
- */
84
- politeness?: 'off' | 'polite' | 'assertive'
85
- }
86
-
87
- type RequireAtLeastOne<T> = {
88
- [K in keyof T]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<keyof T, K>>>
89
- }[keyof T]
90
-
91
- interface RouteHooks {
92
- before?: (to: Route, from: Route) => string | Route;
93
- }
94
-
95
- type Route = {
96
- /**
97
- * URI path for the route
98
- */
99
- path: string,
100
- /**
101
- * Component to load when activating the route
102
- */
103
- component: Component,
104
- /**
105
- * Transition configuration for the route
106
- */
107
- transition?: RequireAtLeastOne<RouteTransition> | RouteTransitionFunction,
108
- /**
109
- * Extra route options
110
- */
111
- options?: object // todo: specify which options are available,
112
- /**
113
- * Message to be announced when visiting the route (often used for accessibility purposes)
114
- *
115
- * Can be either a `String` with the message or an object that defines the message and the
116
- * politeness level
117
- */
118
- announce?: String | RouteAnnounce
119
- /**
120
- * Register hooks for the route
121
- */
122
- hooks?: RouteHooks
123
- /**
124
- * Route path parameters
125
- */
126
- readonly params?: {
127
- [key: string]: string | number
128
- }
129
- /**
130
- * Allows for attaching custom data to a route, either hardcoded in the
131
- * route definition or asigned to the route object in a before hook
132
- *
133
- * Will be merged with the route params and navigation data and passed as
134
- * props into the route component
135
- */
136
- data?: {
137
- [key: string]: any
138
- }
139
- }
140
-
141
- export interface ApplicationInstance extends Component.ComponentInstance {}
142
-
143
- export interface ApplicationConfig<Props extends string, S, M> extends Component.ComponentConfig<Props, S, M> {
144
- /**
145
- * Routes definition
146
- *
147
- * @example
148
- *
149
- * ```js
150
- * routes: [
151
- * { path: '/', component: Home },
152
- * { path: '/details', component: Details },
153
- * { path: '/account', component: Account },
154
- * ]
155
- * ```
156
- */
157
- routes?: Route[]
158
- }
159
- }
160
-
161
- /**
162
- * Blits.Application()
163
- *
164
- * Root App component
165
- */
166
- declare function Application<
167
- Props extends string,
168
- S extends Component.State,
169
- M extends Component.Methods
170
- >(
171
- config: Application.ApplicationConfig<Props, S, M>
172
- ) : Application.ApplicationInstance
173
-
174
- export default Application;