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