jostraca 0.4.0 → 0.6.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 (54) hide show
  1. package/dist/cmp/Content.d.ts +2 -0
  2. package/dist/cmp/Content.js +12 -0
  3. package/dist/cmp/Content.js.map +1 -0
  4. package/dist/cmp/Copy.d.ts +2 -0
  5. package/dist/cmp/Copy.js +13 -0
  6. package/dist/cmp/Copy.js.map +1 -0
  7. package/dist/cmp/File.d.ts +2 -0
  8. package/dist/cmp/File.js +13 -0
  9. package/dist/cmp/File.js.map +1 -0
  10. package/dist/cmp/Folder.d.ts +2 -0
  11. package/dist/cmp/Folder.js +12 -0
  12. package/dist/cmp/Folder.js.map +1 -0
  13. package/dist/cmp/Project.d.ts +2 -0
  14. package/dist/cmp/Project.js +13 -0
  15. package/dist/cmp/Project.js.map +1 -0
  16. package/dist/jostraca.d.ts +10 -34
  17. package/dist/jostraca.js +103 -294
  18. package/dist/jostraca.js.map +1 -1
  19. package/dist/op/ContentOp.d.ts +6 -0
  20. package/dist/op/ContentOp.js +12 -0
  21. package/dist/op/ContentOp.js.map +1 -0
  22. package/dist/op/CopyOp.d.ts +6 -0
  23. package/dist/op/CopyOp.js +188 -0
  24. package/dist/op/CopyOp.js.map +1 -0
  25. package/dist/op/FileOp.d.ts +6 -0
  26. package/dist/op/FileOp.js +43 -0
  27. package/dist/op/FileOp.js.map +1 -0
  28. package/dist/op/FolderOp.d.ts +6 -0
  29. package/dist/op/FolderOp.js +23 -0
  30. package/dist/op/FolderOp.js.map +1 -0
  31. package/dist/op/NoneOp.d.ts +6 -0
  32. package/dist/op/NoneOp.js +9 -0
  33. package/dist/op/NoneOp.js.map +1 -0
  34. package/dist/op/ProjectOp.d.ts +6 -0
  35. package/dist/op/ProjectOp.js +24 -0
  36. package/dist/op/ProjectOp.js.map +1 -0
  37. package/dist/tsconfig.tsbuildinfo +1 -0
  38. package/dist/utility.d.ts +44 -0
  39. package/dist/utility.js +213 -0
  40. package/dist/utility.js.map +1 -0
  41. package/package.json +4 -4
  42. package/src/cmp/Content.ts +19 -0
  43. package/src/cmp/Copy.ts +21 -0
  44. package/src/cmp/File.ts +21 -0
  45. package/src/cmp/Folder.ts +21 -0
  46. package/src/cmp/Project.ts +22 -0
  47. package/src/jostraca.ts +118 -380
  48. package/src/op/ContentOp.ts +18 -0
  49. package/src/op/CopyOp.ts +229 -0
  50. package/src/op/FileOp.ts +58 -0
  51. package/src/op/FolderOp.ts +31 -0
  52. package/src/op/NoneOp.ts +14 -0
  53. package/src/op/ProjectOp.ts +33 -0
  54. package/src/utility.ts +306 -0
package/src/jostraca.ts CHANGED
@@ -1,32 +1,46 @@
1
1
  /* Copyright (c) 2024 Richard Rodger, MIT License */
2
2
 
3
3
  import * as Fs from 'node:fs'
4
+ import Path from 'node:path'
4
5
 
5
6
  import { AsyncLocalStorage } from 'node:async_hooks'
6
7
 
7
8
 
8
- import { util as JostracaUtil } from '@jsonic/jsonic-next'
9
-
10
- const { deep } = JostracaUtil
9
+ import type {
10
+ JostracaOptions,
11
+ Node,
12
+ OpDef,
13
+ Component,
14
+ } from './utility'
11
15
 
12
16
 
13
- type JostracaOptions = {
14
- folder: string
15
- fs: any
16
- meta: any
17
- }
17
+ import {
18
+ each,
19
+ select,
20
+ get,
21
+ getx,
22
+ camelify,
23
+ snakify,
24
+ kebabify,
25
+ cmap,
26
+ vmap,
27
+ names,
28
+ } from './utility'
18
29
 
19
30
 
20
- type Node = {
21
- kind: string
22
- children?: Node[]
23
- name?: string
24
- path?: string
25
- content?: any[]
26
- }
31
+ import { Content } from './cmp/Content'
32
+ import { Copy } from './cmp/Copy'
33
+ import { File } from './cmp/File'
34
+ import { Folder } from './cmp/Folder'
35
+ import { Project } from './cmp/Project'
27
36
 
37
+ import { CopyOp } from './op/CopyOp'
38
+ import { ProjectOp } from './op/ProjectOp'
39
+ import { FolderOp } from './op/FolderOp'
40
+ import { FileOp } from './op/FileOp'
41
+ import { ContentOp } from './op/ContentOp'
42
+ import { NoneOp } from './op/NoneOp'
28
43
 
29
- type Component = (props: any, children?: any) => void
30
44
 
31
45
 
32
46
  const GLOBAL = (global as any)
@@ -37,35 +51,40 @@ function Jostraca() {
37
51
  GLOBAL.jostraca = new AsyncLocalStorage()
38
52
 
39
53
 
40
- function generate(opts: JostracaOptions, root: Function) {
54
+ async function generate(opts: JostracaOptions, root: Function) {
41
55
  const fs = opts.fs || Fs
42
56
  const meta = opts.meta || {}
43
57
  const folder = opts.folder || '.'
44
58
 
45
59
  const ctx$ = {
46
- fs,
47
60
  folder,
48
61
  content: null,
49
62
  meta,
63
+ fs,
50
64
  }
51
65
 
52
- GLOBAL.jostraca.run(ctx$, () => {
66
+ return GLOBAL.jostraca.run(ctx$, async () => {
53
67
  try {
68
+ // Define phase
54
69
  root()
55
70
 
56
71
  const ctx$ = GLOBAL.jostraca.getStore()
57
- const node = ctx$.node
58
-
59
- build(
60
- node,
61
- {
62
- fs,
63
- current: {
64
- folder: {
65
- parent: folder
66
- }
72
+
73
+ // console.dir(ctx$.node, { depth: null })
74
+
75
+ // Build phase
76
+
77
+ const buildctx = {
78
+ fs,
79
+ folder,
80
+ current: {
81
+ folder: {
82
+ parent: folder
67
83
  }
68
- })
84
+ }
85
+ }
86
+ await build(ctx$, buildctx)
87
+ return buildctx
69
88
  }
70
89
  catch (err: any) {
71
90
  console.log('JOSTRACA ERROR:', err)
@@ -75,98 +94,80 @@ function Jostraca() {
75
94
  }
76
95
 
77
96
 
78
- function build(topnode: Node, ctx: any) {
79
- step(topnode, ctx)
80
- }
97
+ async function build(ctx$: any, buildctx: any) {
98
+ const topnode = ctx$.node
81
99
 
100
+ let info = { exclude: [], last: -1 }
101
+ const infopath = Path.join(buildctx.folder, '.jostraca', 'info.json')
82
102
 
83
- function step(node: Node, ctx: any) {
84
- const op = opmap[node.kind]
85
- if (null == op) {
86
- throw new Error('missing op: ' + node.kind)
103
+ try {
104
+ info = JSON.parse(ctx$.fs.readFileSync(
105
+ infopath, 'utf8'))
87
106
  }
88
-
89
- op.before(node, ctx)
90
-
91
- if (node.children) {
92
- for (let childnode of node.children) {
93
- step(childnode, ctx)
94
- }
107
+ catch (err: any) {
108
+ // console.log(err)
109
+ // TODO: file not foound ignored, handle others!
95
110
  }
96
111
 
97
- op.after(node, ctx)
98
- }
99
-
100
-
101
-
102
- const opmap: any = {
103
-
104
- project: {
105
- before(node: Node, ctx: any) {
106
- const cproject: any = ctx.current.project = (ctx.current.project || {})
107
- cproject.node = node
108
- },
109
-
110
- after(_node: Node, _ctx: any) {
111
-
112
- },
113
- },
114
-
112
+ buildctx.info = info
113
+ // console.log('B-INFO', buildctx.info)
115
114
 
116
- folder: {
117
- before(node: Node, ctx: any) {
118
- const cfolder = ctx.current.folder = (ctx.current.folder || {})
115
+ await step(topnode, ctx$, buildctx)
119
116
 
120
- cfolder.node = node
121
- cfolder.path = (cfolder.path || [ctx.current.folder.parent])
122
- cfolder.path.push(node.name)
123
-
124
- let fullpath = cfolder.path.join('/')
125
- ctx.fs.mkdirSync(fullpath, { recursive: true })
126
- },
127
-
128
-
129
- after(_node: Node, ctx: any) {
130
- const cfolder = ctx.current.folder
131
- cfolder.path.length = cfolder.path.length - 1
132
- },
133
- },
134
117
 
118
+ try {
119
+ const info = {
120
+ last: Date.now(),
121
+ exclude: buildctx.info.exclude,
122
+ }
123
+ ctx$.fs.mkdirSync(Path.dirname(infopath), { recursive: true })
124
+ ctx$.fs.writeFileSync(infopath, JSON.stringify(info, null, 2))
125
+ }
126
+ catch (err: any) {
127
+ console.log(err)
128
+ // TODO: file not foound ignored, handle others!
129
+ }
135
130
 
136
- file: {
137
- before(node: Node, ctx: any) {
138
- const cfile = ctx.current.file = node
139
- cfile.path = ctx.current.folder.path.join('/') + '/' + node.name
140
- cfile.content = []
141
- },
142
131
 
143
- after(_node: Node, ctx: any) {
144
- const cfile = ctx.current.file
145
- const content = cfile.content.join('')
146
- ctx.fs.writeFileSync(cfile.path, content)
147
- },
148
- },
132
+ return { node: topnode, ctx$, buildctx }
133
+ }
149
134
 
150
135
 
151
- content: {
152
- before(node: Node, ctx: any) {
153
- const content = ctx.current.content = node
154
- ctx.current.file.content.push(content.content)
155
- },
136
+ async function step(node: Node, ctx$: any, buildctx: any) {
137
+ try {
138
+ const op = opmap[node.kind]
139
+ if (null == op) {
140
+ throw new Error('missing op: ' + node.kind)
141
+ }
156
142
 
157
- after(_node: Node, _ctx: any) {
158
- },
159
- },
143
+ await op.before(node, ctx$, buildctx)
160
144
 
145
+ if (node.children) {
146
+ for (let childnode of node.children) {
147
+ await step(childnode, ctx$, buildctx)
148
+ }
149
+ }
161
150
 
162
- none: {
163
- before(_node: Node, _ctx: any) {
164
- },
151
+ await op.after(node, ctx$, buildctx)
152
+ }
153
+ catch (err: any) {
154
+ if (err.jostraca) {
155
+ throw err
156
+ }
157
+ err.jostraca = true
158
+ err.step = node.kind
159
+ throw err
160
+ }
161
+ }
165
162
 
166
- after(_node: Node, _ctx: any) {
167
- },
168
- },
169
163
 
164
+ const opmap: Record<string, OpDef> = {
165
+ project: ProjectOp,
166
+ folder: FolderOp,
167
+ file: FileOp,
168
+ content: ContentOp,
169
+ copy: CopyOp,
170
+ none: NoneOp,
170
171
  }
171
172
 
172
173
 
@@ -176,51 +177,6 @@ function Jostraca() {
176
177
  }
177
178
 
178
179
 
179
- const Code = cmp(function Code(props: any) {
180
- props.ctx$.node.kind = 'content'
181
- let src = props.arg
182
- props.ctx$.node.content = src
183
- })
184
-
185
-
186
- const File = cmp(function File(props: any, children: any) {
187
- props.ctx$.node.kind = 'file'
188
- props.ctx$.node.name = props.name
189
-
190
- // Code('// FILE START: ' + props.name + '\n')
191
-
192
- each(children)
193
-
194
- // Code('// FILE END: ' + props.name + '\n')
195
- })
196
-
197
-
198
- const Copy = cmp(function Copy(props: any, children: any) {
199
- props.ctx$.node.kind = 'file'
200
- props.ctx$.node.name = props.name
201
-
202
- const content = props.ctx$.fs.readFileSync(props.from).toString()
203
- Code(content)
204
- })
205
-
206
-
207
-
208
- const Project: Component = cmp(function Project(props: any, children: any) {
209
- props.ctx$.node.kind = 'project'
210
- props.ctx$.node.name = props.name
211
-
212
- each(children)
213
- })
214
-
215
-
216
- const Folder = cmp(function Folder(props: any, children: any) {
217
- props.ctx$.node.kind = 'folder'
218
- props.ctx$.node.name = props.name
219
-
220
- each(children)
221
- })
222
-
223
-
224
180
  function cmp(component: Function): Component {
225
181
  const cf = (props: any, children?: any) => {
226
182
  props = props || {}
@@ -230,9 +186,10 @@ function cmp(component: Function): Component {
230
186
  props.ctx$ = GLOBAL.jostraca.getStore()
231
187
  children = 'function' === typeof children ? [children] : children
232
188
 
233
- let node = {
189
+ let node: Node = {
234
190
  kind: 'none',
235
- children: []
191
+ children: [],
192
+ path: [],
236
193
  }
237
194
 
238
195
  const parent = props.ctx$.node = (props.ctx$.node || node)
@@ -242,6 +199,13 @@ function cmp(component: Function): Component {
242
199
  props.ctx$.children = node.children
243
200
  props.ctx$.node = node
244
201
 
202
+ node.path = parent.path.slice(0)
203
+ if ('string' === typeof props.name) {
204
+ node.path.push(props.name)
205
+ // console.log('CMP-PATH', component.name, node.path)
206
+ // console.trace()
207
+ }
208
+
245
209
  let out = component(props, children)
246
210
 
247
211
  props.ctx$.children = siblings
@@ -254,238 +218,12 @@ function cmp(component: Function): Component {
254
218
  }
255
219
 
256
220
 
257
- function each(subject?: any, apply?: any) {
258
- if (null == apply) {
259
- let out = []
260
- if (Array.isArray(subject)) {
261
- for (let fn of subject) {
262
- out.push('function' === typeof fn ? fn() : fn)
263
- }
264
- return out.sort()
265
- }
266
- else if (null == subject || 'object' !== typeof subject) {
267
- return []
268
- }
269
- }
270
- else if (Array.isArray(subject)) {
271
- return subject.map(apply)
272
- }
273
-
274
- if (null == subject || 'object' !== typeof subject) {
275
- return []
276
- }
277
-
278
- const entries: any = Object.entries(subject).map((n: any[], _: any) =>
279
- (_ = typeof n[1],
280
- (null != n[1] && 'object' === _) ? (n[1].key$ = n[0]) :
281
- (n[1] = { name: n[0], key$: n[0], val$: n[1] }), n))
282
-
283
- if (1 < entries.length) {
284
- if (entries[0] && entries[0][1] && 'string' === typeof entries[0][1].name) {
285
- entries.sort((a: any, b: any) =>
286
- a[1].name < b[1].name ? -1 : b[1].name < a[1].name ? 1 : 0)
287
- }
288
- else if (entries[0] && entries[0][1] && 'string' === typeof entries[0][1].key$) {
289
- entries.sort((a: any, b: any) =>
290
- a[1].key$ < b[1].key$ ? -1 : b[1].key$ < a[1].key$ ? 1 : 0)
291
- }
292
- }
293
-
294
- apply = 'function' === typeof apply ? apply : (x: any) => x
295
-
296
- return entries.map((n: any, ...args: any[]) =>
297
- apply(n[1], n[0], ...args))
298
- }
299
-
300
-
301
-
302
- function select(key: any, map: Record<string, Function>) {
303
- const fn = map && map[key]
304
- return fn ? fn() : undefined
305
- }
306
-
307
-
308
-
309
- function getx(root: any, path: string | string[]): any {
310
- path = ('string' === typeof path ? path.split(/[.\s\r\n\t]/) : path).filter(part => '' != part)
311
- let node = root
312
- let parents = []
313
-
314
- partloop:
315
- for (let i = 0; i < path.length && null != node; i++) {
316
- let part = String(path[i]).trim()
317
- let m = part.match(/^([^<=>~^?!]*)([<=>~^?!]+)(.*)$/)
318
-
319
- if (m) {
320
- part = m[1]
321
- let op = m[2]
322
- let arg = m[3]
323
-
324
- let val = '' === part ? node : node[part]
325
-
326
- if ('=' === op && 'null' === arg) {
327
- parents.push(node)
328
- node = {} // virtual node so that ^ works consistently
329
- continue partloop
330
- }
331
- else if ('^' === op && '' === part && '' !== arg) {
332
- node = parents[parents.length - Number(arg)]
333
- continue partloop
334
- }
335
- else if ('?' === op[0]) {
336
- arg = (1 < op.length ? op.substring(1) : '') + arg
337
- node = Array.isArray(val) ?
338
- each(val).filter((n: any) => (
339
- null != getx(n, arg))) :
340
- each(val).filter((n: any) => (
341
- null != getx(n, arg)))
342
- .reduce((a: any, n: any) => (a[n.key$] = n, delete n.key$, a), {})
343
-
344
- continue partloop
345
- }
346
-
347
- if (null == val) return undefined
348
-
349
- val = Array.isArray(val) ? val.length :
350
- 'object' === typeof val ? Object.keys(val).filter(k => !k.includes('$')).length :
351
- val
352
-
353
- switch (op) {
354
- case '<':
355
- if (!(val < arg)) return undefined
356
- break
357
- case '<=':
358
- if (!(val <= arg)) return undefined
359
- break
360
- case '>':
361
- if (!(val > arg)) return undefined
362
- break
363
- case '>=':
364
- if (!(val >= arg)) return undefined
365
- break
366
- case '=':
367
- if (!(val == arg)) return undefined
368
- break
369
- case '!=':
370
- if (!(val != arg)) return undefined
371
- break
372
- case '~':
373
- if (!(String(val).match(RegExp(arg)))) return undefined
374
- break
375
- case '^':
376
- node = parents[parents.length - Number(arg)]
377
- continue partloop
378
- default:
379
- return undefined
380
- }
381
- }
382
-
383
- parents.push(node)
384
- node = '' === part ? node : node[part]
385
- }
386
- return node
387
- }
388
-
389
-
390
- function get(root: any, path: string | string[]): any {
391
- path = 'string' === typeof path ? path.split('.') : path
392
- let node = root
393
- for (let i = 0; i < path.length && null != node; i++) {
394
- node = node[path[i]]
395
- }
396
- return node
397
- }
398
-
399
-
400
- function camelify(input: any[] | string) {
401
- let parts = 'string' == typeof input ? input.split('-') : input.map(n => '' + n)
402
- return parts
403
- .map((p: string) => ('' === p ? '' : (p[0].toUpperCase() + p.substring(1))))
404
- .join('')
405
- }
406
-
407
-
408
- function kebabify(input: any[] | string) {
409
- let parts = 'string' == typeof input ? input.split(/([A-Z])/) : input.map(n => '' + n)
410
- return parts
411
- .filter((p: string) => '' !== p)
412
- .reduce((a: any[], n: string, i: number) =>
413
- ((0 === i % 2 ? a.push(n.toLowerCase()) : a[(i / 2) | 0] += n), a), [])
414
- .join('-')
415
- }
416
-
417
-
418
- function snakify(input: any[] | string) {
419
- let parts = 'string' == typeof input ? input.split(/([A-Z])/) : input.map(n => '' + n)
420
- return parts
421
- .filter((p: string) => '' !== p)
422
- .reduce((a: any[], n: string, i: number) =>
423
- ((0 === i % 2 ? a.push(n.toLowerCase()) : a[(i / 2) | 0] += n), a), [])
424
- .join('_')
425
- }
426
-
427
-
428
- function names(base: any, name: string, prop = 'name') {
429
- base.name$ = name
430
- base[prop.toLowerCase()] = name.toLowerCase()
431
- base[camelify(prop)] = camelify(name)
432
- base[snakify(prop)] = snakify(name)
433
- base[kebabify(prop)] = kebabify(name)
434
- base[prop.toUpperCase()] = name.toUpperCase()
435
- }
436
-
437
-
438
-
439
- // Map child objects to new child objects
440
- function cmap(o: any, p: any) {
441
- return Object
442
- .entries(o)
443
- .reduce((r: any, n: any, _: any) => (_ = Object
444
- .entries(p)
445
- .reduce((s: any, m: any) => (cmap.FILTER === s ? s : (s[m[0]] = (
446
- // transfom(val,key,current,parentkey,parent)
447
- 'function' === typeof m[1] ? m[1](n[1][m[0]], {
448
- skey: m[0], self: n[1], key: n[0], parent: o
449
- }) : m[1]
450
- ), (cmap.FILTER === s[m[0]] ? cmap.FILTER : s))), {})
451
- , (cmap.FILTER === _ ? 0 : r[n[0]] = _), r), {})
452
- }
453
-
454
- cmap.COPY = (x: any) => x
455
- // keep self if x is truthy, or function returning truthy-new-value or [truthy,new-value]
456
- cmap.FILTER = (x: any) => 'function' === typeof x ? ((y: any, p: any, _: any) =>
457
- (_ = x(y, p), Array.isArray(_) ? !_[0] ? _[1] : cmap.FILTER : _)) : (x ? x : cmap.FILTER)
458
- cmap.KEY = (_: any, p: any) => p.key
459
-
460
-
461
- // Map child objects to a list of child objects
462
- function vmap(o: any, p: any) {
463
- return Object
464
- .entries(o)
465
- .reduce((r: any, n: any, _: any) => (_ = Object
466
- .entries(p)
467
- .reduce((s: any, m: any) => (vmap.FILTER === s ? s : (s[m[0]] = (
468
- // transfom(val,key,current,parentkey,parent)
469
- // 'function' === typeof m[1] ? m[1](n[1][m[0]], m[0], n[1], n[0], o) : m[1]
470
- 'function' === typeof m[1] ? m[1](n[1][m[0]], {
471
- skey: m[0], self: n[1], key: n[0], parent: o
472
- }) : m[1]
473
- ), (vmap.FILTER === s[m[0]] ? vmap.FILTER : s))), {})
474
- , (vmap.FILTER === _ ? 0 : r.push(_)), r), [])
475
-
476
- }
477
- vmap.COPY = (x: any) => x
478
- vmap.FILTER = (x: any) => 'function' === typeof x ? ((y: any, p: any, _: any) =>
479
- (_ = x(y, p), Array.isArray(_) ? !_[0] ? _[1] : vmap.FILTER : _)) : (x ? x : vmap.FILTER)
480
- vmap.KEY = (_: any, p: any) => p.key
481
-
482
-
483
-
484
221
 
485
222
 
486
223
  export type {
487
224
  JostracaOptions,
488
225
  Component,
226
+ Node,
489
227
  }
490
228
 
491
229
 
@@ -505,7 +243,7 @@ export {
505
243
  names,
506
244
 
507
245
  Project,
508
- Code,
246
+ Content,
509
247
  File,
510
248
  Folder,
511
249
  Copy,
@@ -0,0 +1,18 @@
1
+
2
+
3
+ import type { Node } from '../jostraca'
4
+
5
+
6
+ const ContentOp = {
7
+ before(node: Node, _ctx$: any, buildctx: any) {
8
+ const content = buildctx.current.content = node
9
+ buildctx.current.file.content.push(content.content)
10
+ },
11
+
12
+ after(_node: Node, _ctx$: any, buildctx: any) { },
13
+ }
14
+
15
+
16
+ export {
17
+ ContentOp
18
+ }