@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.
Files changed (49) hide show
  1. package/bin/voxgig-model +152 -31
  2. package/dist/build.d.ts +21 -0
  3. package/dist/{lib/build.js → build.js} +30 -19
  4. package/dist/build.js.map +1 -0
  5. package/dist/builder/local.js +48 -0
  6. package/dist/builder/local.js.map +1 -0
  7. package/dist/{lib/builder → builder}/model.js +13 -13
  8. package/dist/builder/model.js.map +1 -0
  9. package/dist/config.d.ts +12 -0
  10. package/dist/{lib/config.js → config.js} +8 -6
  11. package/dist/config.js.map +1 -0
  12. package/dist/model.d.ts +8 -6
  13. package/dist/model.js +60 -36
  14. package/dist/model.js.map +1 -1
  15. package/dist/tsconfig.tsbuildinfo +1 -0
  16. package/dist/types.d.ts +83 -0
  17. package/dist/types.js +4 -0
  18. package/dist/types.js.map +1 -0
  19. package/dist/{lib/watch.d.ts → watch.d.ts} +6 -20
  20. package/dist/{lib/watch.js → watch.js} +68 -66
  21. package/dist/watch.js.map +1 -0
  22. package/package.json +19 -19
  23. package/dist/lib/build.d.ts +0 -20
  24. package/dist/lib/build.js.map +0 -1
  25. package/dist/lib/builder/local.js +0 -76
  26. package/dist/lib/builder/local.js.map +0 -1
  27. package/dist/lib/builder/model.js.map +0 -1
  28. package/dist/lib/config.d.ts +0 -11
  29. package/dist/lib/config.js.map +0 -1
  30. package/dist/lib/model-builder.d.ts +0 -0
  31. package/dist/lib/model-builder.js +0 -2
  32. package/dist/lib/model-builder.js.map +0 -1
  33. package/dist/lib/types.d.ts +0 -48
  34. package/dist/lib/types.js +0 -3
  35. package/dist/lib/types.js.map +0 -1
  36. package/dist/lib/util.d.ts +0 -7
  37. package/dist/lib/util.js +0 -94
  38. package/dist/lib/util.js.map +0 -1
  39. package/dist/lib/watch.js.map +0 -1
  40. package/dist/model.min.js +0 -94
  41. package/lib/build.ts +0 -138
  42. package/lib/builder/local.ts +0 -130
  43. package/lib/builder/model.ts +0 -37
  44. package/lib/config.ts +0 -50
  45. package/lib/types.ts +0 -67
  46. package/lib/watch.ts +0 -301
  47. package/model.ts +0 -147
  48. /package/dist/{lib/builder → builder}/local.d.ts +0 -0
  49. /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