@voxgig/model 4.0.0 → 5.1.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/bin/voxgig-model +152 -31
- package/dist/build.d.ts +21 -0
- package/dist/{lib/build.js → build.js} +30 -19
- package/dist/build.js.map +1 -0
- package/dist/builder/local.js +48 -0
- package/dist/builder/local.js.map +1 -0
- package/dist/{lib/builder → builder}/model.js +13 -13
- package/dist/builder/model.js.map +1 -0
- package/dist/config.d.ts +12 -0
- package/dist/{lib/config.js → config.js} +8 -6
- package/dist/config.js.map +1 -0
- package/dist/model.d.ts +8 -6
- package/dist/model.js +60 -36
- package/dist/model.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types.d.ts +83 -0
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -0
- package/dist/{lib/watch.d.ts → watch.d.ts} +6 -20
- package/dist/{lib/watch.js → watch.js} +68 -66
- package/dist/watch.js.map +1 -0
- package/package.json +19 -19
- package/dist/lib/build.d.ts +0 -20
- package/dist/lib/build.js.map +0 -1
- package/dist/lib/builder/local.js +0 -76
- package/dist/lib/builder/local.js.map +0 -1
- package/dist/lib/builder/model.js.map +0 -1
- package/dist/lib/config.d.ts +0 -11
- package/dist/lib/config.js.map +0 -1
- package/dist/lib/model-builder.d.ts +0 -0
- package/dist/lib/model-builder.js +0 -2
- package/dist/lib/model-builder.js.map +0 -1
- package/dist/lib/types.d.ts +0 -48
- package/dist/lib/types.js +0 -3
- package/dist/lib/types.js.map +0 -1
- package/dist/lib/util.d.ts +0 -7
- package/dist/lib/util.js +0 -94
- package/dist/lib/util.js.map +0 -1
- package/dist/lib/watch.js.map +0 -1
- package/dist/model.min.js +0 -94
- package/lib/build.ts +0 -138
- package/lib/builder/local.ts +0 -130
- package/lib/builder/model.ts +0 -37
- package/lib/config.ts +0 -50
- package/lib/types.ts +0 -67
- package/lib/watch.ts +0 -301
- package/model.ts +0 -147
- /package/dist/{lib/builder → builder}/local.d.ts +0 -0
- /package/dist/{lib/builder → builder}/model.d.ts +0 -0
package/lib/watch.ts
DELETED
|
@@ -1,301 +0,0 @@
|
|
|
1
|
-
/* Copyright © 2021-2024 Voxgig Ltd, MIT License. */
|
|
2
|
-
|
|
3
|
-
import Path from 'node:path'
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import type { Build, BuildResult } from './types'
|
|
7
|
-
|
|
8
|
-
import { makeBuild } from './build'
|
|
9
|
-
import { FSWatcher } from 'chokidar'
|
|
10
|
-
|
|
11
|
-
import { readFile, stat } from 'fs/promises'
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const print = console.log
|
|
15
|
-
const warn = console.warn
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
type Run = {
|
|
19
|
-
canon: string
|
|
20
|
-
path: string
|
|
21
|
-
start: number
|
|
22
|
-
end: number
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
type Canon = {
|
|
26
|
-
path: string
|
|
27
|
-
isFolder: boolean
|
|
28
|
-
when: number
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
type ChangeItem = {
|
|
33
|
-
path: string
|
|
34
|
-
when: number
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
class Watch {
|
|
38
|
-
fsw: FSWatcher
|
|
39
|
-
spec: any
|
|
40
|
-
last?: BuildResult
|
|
41
|
-
lastChangeTime: number
|
|
42
|
-
build: Build | undefined
|
|
43
|
-
runq: Run[]
|
|
44
|
-
doneq: Run[]
|
|
45
|
-
canons: Canon[]
|
|
46
|
-
lastrun: Run | undefined
|
|
47
|
-
idle: number
|
|
48
|
-
startTime: number
|
|
49
|
-
running: boolean
|
|
50
|
-
lastChange: ChangeItem
|
|
51
|
-
lastTrigger: ChangeItem
|
|
52
|
-
|
|
53
|
-
constructor(spec: any) {
|
|
54
|
-
this.spec = spec
|
|
55
|
-
this.fsw = new FSWatcher()
|
|
56
|
-
this.lastChangeTime = 0
|
|
57
|
-
this.runq = []
|
|
58
|
-
this.doneq = []
|
|
59
|
-
this.canons = []
|
|
60
|
-
this.startTime = 0
|
|
61
|
-
this.lastChange = { path: '', when: 0 }
|
|
62
|
-
this.lastTrigger = { path: '', when: 0 }
|
|
63
|
-
this.running = false
|
|
64
|
-
this.lastrun = undefined
|
|
65
|
-
|
|
66
|
-
this.idle = spec.idle || 111
|
|
67
|
-
|
|
68
|
-
this.fsw.on('change', async (path: string) => {
|
|
69
|
-
// console.log('CHANGE', Date.now() - this.startTime, path)
|
|
70
|
-
this.handleChange(path)
|
|
71
|
-
})
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
// Returns first BuildResult
|
|
76
|
-
start() {
|
|
77
|
-
this.startTime = Date.now()
|
|
78
|
-
this.handleChange('<start>')
|
|
79
|
-
|
|
80
|
-
// Check if there have been no recent changes, if so, run build.
|
|
81
|
-
setInterval(() => {
|
|
82
|
-
const start = this.startTime
|
|
83
|
-
const now = Date.now()
|
|
84
|
-
const idleDuration = now - this.lastChange.when
|
|
85
|
-
|
|
86
|
-
// Only trigger a build if there was a actual change
|
|
87
|
-
const trigger = this.lastChange.when !== this.lastTrigger.when &&
|
|
88
|
-
this.lastChange.path !== this.lastTrigger.path
|
|
89
|
-
|
|
90
|
-
// console.log('CHECK-A', {
|
|
91
|
-
// tick: (now - start),
|
|
92
|
-
// idleDuration,
|
|
93
|
-
// trigger,
|
|
94
|
-
// running: this.running,
|
|
95
|
-
// path: this.lastChange.path
|
|
96
|
-
// })
|
|
97
|
-
|
|
98
|
-
if (trigger) {
|
|
99
|
-
|
|
100
|
-
// Only add to build queue if we've been idle.
|
|
101
|
-
// This allows external compilation outputting multiple files to complete fully.
|
|
102
|
-
// IMPORTANT: always trigger a new build if there were changes *inside* a build period
|
|
103
|
-
if (this.idle < idleDuration) {
|
|
104
|
-
this.lastTrigger.path = this.lastChange.path
|
|
105
|
-
this.lastTrigger.when = this.lastChange.when
|
|
106
|
-
|
|
107
|
-
const path = this.lastChange.path
|
|
108
|
-
const canon = this.canon(path)
|
|
109
|
-
|
|
110
|
-
this.runq.push({
|
|
111
|
-
canon,
|
|
112
|
-
path,
|
|
113
|
-
start: now,
|
|
114
|
-
end: -1,
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
// Defer builds to the event loop to keep idle checking separate.
|
|
118
|
-
setImmediate(this.drain.bind(this))
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
}, (this.idle * 1.1 / 2) | 0)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
// If path is inside a watched folder, return folder as canonical reference.
|
|
127
|
-
canon(path: string) {
|
|
128
|
-
for (const canon of this.canons) {
|
|
129
|
-
if (canon.isFolder && path.startsWith(canon.path)) {
|
|
130
|
-
return canon.path
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
return path
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
handleChange(path: string) {
|
|
138
|
-
// Record most recent (last) changed path and time
|
|
139
|
-
this.lastChange.path = path
|
|
140
|
-
this.lastChange.when = Date.now()
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
async drain() {
|
|
145
|
-
// If already running, all items in queue will be drained from this.runq in the while loop
|
|
146
|
-
if (this.running) {
|
|
147
|
-
return
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
this.running = true
|
|
151
|
-
let r: Run | undefined
|
|
152
|
-
|
|
153
|
-
// While there are queued runs, run them sequentially
|
|
154
|
-
while (r = this.runq.shift()) {
|
|
155
|
-
// console.log('===DRAIN-RUN', this.runq.length, new Date(), r.start, r.canon)
|
|
156
|
-
|
|
157
|
-
// TODO: collect results and errors!!!
|
|
158
|
-
let br = await this.run(false, r.canon)
|
|
159
|
-
// console.log('BR', br)
|
|
160
|
-
|
|
161
|
-
r.end = Date.now()
|
|
162
|
-
this.doneq.push(r)
|
|
163
|
-
this.lastrun = r
|
|
164
|
-
|
|
165
|
-
// console.log('===DRAIN-DONE', this.runq.length, new Date(), r.start, r.canon)
|
|
166
|
-
}
|
|
167
|
-
this.running = false
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
async add(path: string) {
|
|
172
|
-
if (!Path.isAbsolute(path)) {
|
|
173
|
-
path = Path.join(this.spec.require, path)
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Ignore if aleady added
|
|
177
|
-
if (this.canons.find((c: Canon) => c.path === path)) {
|
|
178
|
-
return
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const fileStat = await stat(path)
|
|
182
|
-
const canon: Canon = {
|
|
183
|
-
path: path,
|
|
184
|
-
isFolder: fileStat.isDirectory(),
|
|
185
|
-
when: Date.now()
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
this.canons.push(canon)
|
|
189
|
-
|
|
190
|
-
this.fsw.add(path)
|
|
191
|
-
|
|
192
|
-
// console.log('ADD', canon)
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
async update(br: BuildResult) {
|
|
197
|
-
let build = (br.build as Build)
|
|
198
|
-
|
|
199
|
-
let files: string[] =
|
|
200
|
-
Object.keys(build.root.deps).reduce((files: string[], target: any) => {
|
|
201
|
-
files = files.concat(Object.keys(build.root.deps[target]))
|
|
202
|
-
return files
|
|
203
|
-
}, [build.path])
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
// TODO: remove deleted files
|
|
207
|
-
files.forEach(async (file: string) => {
|
|
208
|
-
if ('string' === typeof (file) &&
|
|
209
|
-
'' !== file &&
|
|
210
|
-
build.opts.base !== file
|
|
211
|
-
) {
|
|
212
|
-
await this.add(file)
|
|
213
|
-
}
|
|
214
|
-
})
|
|
215
|
-
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
async run(once?: boolean, trigger?: string): Promise<BuildResult> {
|
|
221
|
-
// console.trace()
|
|
222
|
-
|
|
223
|
-
this.lastChangeTime = Date.now()
|
|
224
|
-
print('\n@voxgig/model =================', this.lastChangeTime, new Date(this.lastChangeTime))
|
|
225
|
-
print('TRIGGER:', trigger, '\n')
|
|
226
|
-
|
|
227
|
-
// TODO: build spec should not have src!
|
|
228
|
-
let src = (await readFile(this.spec.path)).toString()
|
|
229
|
-
this.spec.src = src
|
|
230
|
-
|
|
231
|
-
this.build = this.build || makeBuild(this.spec)
|
|
232
|
-
|
|
233
|
-
// TODO: better way to do this?
|
|
234
|
-
this.build.src = src
|
|
235
|
-
|
|
236
|
-
let br: BuildResult = await this.build.run()
|
|
237
|
-
|
|
238
|
-
print('\nFILES:\n' + this.descDeps((br as any).build.root.deps) + '\n')
|
|
239
|
-
|
|
240
|
-
if (br.ok) {
|
|
241
|
-
print('TOP:', Object.keys(br?.build?.model).join(', '), '\n')
|
|
242
|
-
|
|
243
|
-
if (!once) {
|
|
244
|
-
// There may be new files.
|
|
245
|
-
await this.update(br)
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
else {
|
|
249
|
-
warn('MODEL ERRORS: ' + br.err?.length)
|
|
250
|
-
this.handleErrors(br)
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
this.last = br
|
|
254
|
-
|
|
255
|
-
return br
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
async stop() {
|
|
260
|
-
await this.fsw.close()
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
handleErrors(br: BuildResult) {
|
|
265
|
-
if (br.err) {
|
|
266
|
-
for (let be of br.err) {
|
|
267
|
-
// TODO: print stack if not a model error
|
|
268
|
-
|
|
269
|
-
if (be.isVal && be.msg) {
|
|
270
|
-
warn(be.msg)
|
|
271
|
-
}
|
|
272
|
-
// else if (be.message) {
|
|
273
|
-
// warn(be.message)
|
|
274
|
-
// }
|
|
275
|
-
else {
|
|
276
|
-
warn(be)
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
descDeps(deps: Record<string, Record<string, { tar: string }>>) {
|
|
283
|
-
if (null == deps) {
|
|
284
|
-
return ''
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
let desc = []
|
|
288
|
-
for (let entryPath of Object.keys(deps)) {
|
|
289
|
-
desc.push(' ' + entryPath)
|
|
290
|
-
for (let depPath of Object.keys(deps[entryPath])) {
|
|
291
|
-
desc.push(' ' + depPath)
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
return desc.join('\n')
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
export {
|
|
300
|
-
Watch
|
|
301
|
-
}
|
package/model.ts
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
/* Copyright © 2021-2023 Voxgig Ltd, MIT License. */
|
|
2
|
-
|
|
3
|
-
// TODO: remove need for this
|
|
4
|
-
import Fs from 'fs'
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import type { Build, BuildResult, BuildAction, BuildContext, Spec } from './lib/types'
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
import { Config } from './lib/config'
|
|
11
|
-
import { Watch } from './lib/watch'
|
|
12
|
-
|
|
13
|
-
import { model_builder } from './lib/builder/model'
|
|
14
|
-
import { local_builder } from './lib/builder/local'
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class Model {
|
|
18
|
-
config: Config
|
|
19
|
-
build: any
|
|
20
|
-
watch: Watch
|
|
21
|
-
|
|
22
|
-
trigger_model = false
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
constructor(spec: Spec) {
|
|
26
|
-
const self = this
|
|
27
|
-
|
|
28
|
-
// Config is a special Watch to handle model config.
|
|
29
|
-
this.config = makeConfig(spec, {
|
|
30
|
-
path: '/',
|
|
31
|
-
build: async function trigger_model(build: Build, ctx: BuildContext) {
|
|
32
|
-
if ('post' !== ctx.step) {
|
|
33
|
-
return { ok: true }
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
let res
|
|
37
|
-
|
|
38
|
-
// console.log('TRIGGER', build.id, self.trigger_model, ctx)
|
|
39
|
-
// console.log(new Error().stack)
|
|
40
|
-
|
|
41
|
-
if (self.trigger_model) {
|
|
42
|
-
|
|
43
|
-
// TODO: fix!!!
|
|
44
|
-
self.build.use.config.watch.last.build = build
|
|
45
|
-
|
|
46
|
-
res = self.watch.run(true)
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
self.trigger_model = true
|
|
50
|
-
res = { ok: true }
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const watchmap = build.model?.sys?.model?.watch
|
|
54
|
-
|
|
55
|
-
if (watchmap) {
|
|
56
|
-
Object.keys(watchmap).map((file: string) => {
|
|
57
|
-
self.watch.add(file)
|
|
58
|
-
})
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return res
|
|
62
|
-
}
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
// The actual model.
|
|
67
|
-
this.build = {
|
|
68
|
-
src: spec.src,
|
|
69
|
-
path: spec.path,
|
|
70
|
-
base: spec.base,
|
|
71
|
-
use: { config: self.config },
|
|
72
|
-
res: [
|
|
73
|
-
{
|
|
74
|
-
path: '/',
|
|
75
|
-
build: model_builder
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
path: '/',
|
|
79
|
-
build: local_builder
|
|
80
|
-
}
|
|
81
|
-
],
|
|
82
|
-
require: spec.require
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
this.watch = new Watch(self.build)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
async run(): Promise<BuildResult> {
|
|
91
|
-
this.trigger_model = false
|
|
92
|
-
const br = await this.config.run()
|
|
93
|
-
return br.ok ? this.watch.run(true) : br
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
async start() {
|
|
98
|
-
this.trigger_model = false
|
|
99
|
-
|
|
100
|
-
const br = await this.config.run()
|
|
101
|
-
// console.log('MODEL CONFIG START', br.ok)
|
|
102
|
-
return br.ok ? this.watch.start() : br
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
async stop() {
|
|
107
|
-
return this.watch.stop()
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
function makeConfig(spec: Spec, trigger_model_build: BuildAction) {
|
|
113
|
-
let cbase = spec.base + '/.model-config'
|
|
114
|
-
let cpath = cbase + '/model-config.jsonic'
|
|
115
|
-
|
|
116
|
-
// Build should load file
|
|
117
|
-
let src = Fs.readFileSync(cpath).toString()
|
|
118
|
-
|
|
119
|
-
let cspec: Spec = {
|
|
120
|
-
src: src,
|
|
121
|
-
path: cpath,
|
|
122
|
-
base: cbase,
|
|
123
|
-
res: [
|
|
124
|
-
|
|
125
|
-
// Generate full config model and save as a file.
|
|
126
|
-
{
|
|
127
|
-
path: '/',
|
|
128
|
-
build: model_builder
|
|
129
|
-
},
|
|
130
|
-
|
|
131
|
-
// Trigger main model build.
|
|
132
|
-
trigger_model_build
|
|
133
|
-
],
|
|
134
|
-
require: spec.require
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return new Config(cspec)
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
export {
|
|
143
|
-
Model,
|
|
144
|
-
Spec,
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
|
|
File without changes
|
|
File without changes
|