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.
- package/dist/cmp/Content.d.ts +2 -0
- package/dist/cmp/Content.js +12 -0
- package/dist/cmp/Content.js.map +1 -0
- package/dist/cmp/Copy.d.ts +2 -0
- package/dist/cmp/Copy.js +13 -0
- package/dist/cmp/Copy.js.map +1 -0
- package/dist/cmp/File.d.ts +2 -0
- package/dist/cmp/File.js +13 -0
- package/dist/cmp/File.js.map +1 -0
- package/dist/cmp/Folder.d.ts +2 -0
- package/dist/cmp/Folder.js +12 -0
- package/dist/cmp/Folder.js.map +1 -0
- package/dist/cmp/Project.d.ts +2 -0
- package/dist/cmp/Project.js +13 -0
- package/dist/cmp/Project.js.map +1 -0
- package/dist/jostraca.d.ts +10 -34
- package/dist/jostraca.js +103 -294
- package/dist/jostraca.js.map +1 -1
- package/dist/op/ContentOp.d.ts +6 -0
- package/dist/op/ContentOp.js +12 -0
- package/dist/op/ContentOp.js.map +1 -0
- package/dist/op/CopyOp.d.ts +6 -0
- package/dist/op/CopyOp.js +188 -0
- package/dist/op/CopyOp.js.map +1 -0
- package/dist/op/FileOp.d.ts +6 -0
- package/dist/op/FileOp.js +43 -0
- package/dist/op/FileOp.js.map +1 -0
- package/dist/op/FolderOp.d.ts +6 -0
- package/dist/op/FolderOp.js +23 -0
- package/dist/op/FolderOp.js.map +1 -0
- package/dist/op/NoneOp.d.ts +6 -0
- package/dist/op/NoneOp.js +9 -0
- package/dist/op/NoneOp.js.map +1 -0
- package/dist/op/ProjectOp.d.ts +6 -0
- package/dist/op/ProjectOp.js +24 -0
- package/dist/op/ProjectOp.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/utility.d.ts +44 -0
- package/dist/utility.js +213 -0
- package/dist/utility.js.map +1 -0
- package/package.json +4 -4
- package/src/cmp/Content.ts +19 -0
- package/src/cmp/Copy.ts +21 -0
- package/src/cmp/File.ts +21 -0
- package/src/cmp/Folder.ts +21 -0
- package/src/cmp/Project.ts +22 -0
- package/src/jostraca.ts +118 -380
- package/src/op/ContentOp.ts +18 -0
- package/src/op/CopyOp.ts +229 -0
- package/src/op/FileOp.ts +58 -0
- package/src/op/FolderOp.ts +31 -0
- package/src/op/NoneOp.ts +14 -0
- package/src/op/ProjectOp.ts +33 -0
- package/src/utility.ts +306 -0
package/src/op/CopyOp.ts
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
|
|
2
|
+
import Path from 'node:path'
|
|
3
|
+
|
|
4
|
+
import type { Node } from '../jostraca'
|
|
5
|
+
|
|
6
|
+
import { getx } from '../jostraca'
|
|
7
|
+
|
|
8
|
+
import { FileOp } from './FileOp'
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
const CopyOp = {
|
|
12
|
+
|
|
13
|
+
before(node: Node, ctx$: any, buildctx: any) {
|
|
14
|
+
const fs = buildctx.fs
|
|
15
|
+
|
|
16
|
+
// TODO: do these need null checks here?
|
|
17
|
+
const name = node.name as string
|
|
18
|
+
const from = node.from as string
|
|
19
|
+
|
|
20
|
+
const fromStat = fs.statSync(from)
|
|
21
|
+
|
|
22
|
+
if (fromStat.isFile()) {
|
|
23
|
+
FileOp.before(node, ctx$, buildctx)
|
|
24
|
+
const topath = buildctx.current.file.path
|
|
25
|
+
const state = {
|
|
26
|
+
fileCount: 0,
|
|
27
|
+
folderCount: 0,
|
|
28
|
+
tmCount: 0,
|
|
29
|
+
ctx$,
|
|
30
|
+
buildctx,
|
|
31
|
+
}
|
|
32
|
+
const spec = { name, frompath: from, topath }
|
|
33
|
+
|
|
34
|
+
let content = processTemplate(state, fs.readFileSync(from).toString(), spec)
|
|
35
|
+
|
|
36
|
+
buildctx.current.file.content.push(content)
|
|
37
|
+
node.after = node.after || {}
|
|
38
|
+
node.after.kind = 'file'
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
else if (fromStat.isDirectory()) {
|
|
42
|
+
if (null != from && '' != from) {
|
|
43
|
+
node.after = node.after || {}
|
|
44
|
+
node.after.kind = 'copy'
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
else {
|
|
49
|
+
throw new Error('Unable to process file: ' + from)
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
after(node: Node, ctx$: any, buildctx: any) {
|
|
55
|
+
const info = buildctx.info
|
|
56
|
+
const kind = node.after.kind
|
|
57
|
+
|
|
58
|
+
const frompath = node.from as string
|
|
59
|
+
const topath = buildctx.current.folder.path.join('/')
|
|
60
|
+
|
|
61
|
+
let exclude = node.exclude
|
|
62
|
+
if (info && true === exclude) {
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if ('file' === kind) {
|
|
67
|
+
FileOp.after(node, ctx$, buildctx)
|
|
68
|
+
}
|
|
69
|
+
else if ('copy' === kind) {
|
|
70
|
+
const state = {
|
|
71
|
+
fileCount: 0,
|
|
72
|
+
folderCount: 0,
|
|
73
|
+
tmCount: 0,
|
|
74
|
+
ctx$,
|
|
75
|
+
buildctx,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// TODO: node.path is wrong
|
|
79
|
+
// change prop.name to prop.to and account for subfolders
|
|
80
|
+
|
|
81
|
+
walk(state, node.path, frompath, topath)
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
throw new Error('Unknown kind=' + kind + ' for file: ' + frompath)
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
function walk(state: any, nodepath: string[], from: string, to: string) {
|
|
92
|
+
const buildctx = state.buildctx
|
|
93
|
+
const fs = buildctx.fs
|
|
94
|
+
const entries = fs.readdirSync(from)
|
|
95
|
+
|
|
96
|
+
for (let name of entries) {
|
|
97
|
+
const frompath = Path.join(from, name)
|
|
98
|
+
const topath = Path.join(to, name)
|
|
99
|
+
const stat = fs.statSync(frompath)
|
|
100
|
+
|
|
101
|
+
if (stat.isDirectory()) {
|
|
102
|
+
state.folderCount++
|
|
103
|
+
walk(state, nodepath.concat(name), frompath, topath)
|
|
104
|
+
}
|
|
105
|
+
else if (isTemplate(name)) {
|
|
106
|
+
if (excludeFile(state, nodepath, name, topath)) { continue }
|
|
107
|
+
const src = fs.readFileSync(frompath, 'utf8')
|
|
108
|
+
const out = genTemplate(state, src, { name, frompath, topath })
|
|
109
|
+
writeFileSync(buildctx, topath, out)
|
|
110
|
+
state.fileCount++
|
|
111
|
+
state.tmCount++
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
if (excludeFile(state, nodepath, name, topath)) { continue }
|
|
115
|
+
copyFileSync(buildctx, frompath, topath)
|
|
116
|
+
state.fileCount++
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
function excludeFile(state: any, nodepath: string[], name: string, frompath: string) {
|
|
123
|
+
const { fs, info } = state.buildctx
|
|
124
|
+
let exclude = false
|
|
125
|
+
|
|
126
|
+
// NOT Path.sep - needs to be canonical
|
|
127
|
+
const rpath = nodepath.concat(name).join('/')
|
|
128
|
+
|
|
129
|
+
if (info) {
|
|
130
|
+
exclude = info.exclude.includes(rpath)
|
|
131
|
+
let stat, timedelta
|
|
132
|
+
if (!exclude) {
|
|
133
|
+
stat = fs.statSync(frompath, { throwIfNoEntry: false })
|
|
134
|
+
if (stat) {
|
|
135
|
+
timedelta = stat.mtimeMs - info.last
|
|
136
|
+
if (stat && (timedelta > 0 && timedelta < stat.mtimeMs)) {
|
|
137
|
+
exclude = true
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// if ('sdk.js' === name) {
|
|
143
|
+
// console.log('COPYSTAT', rpath, frompath,
|
|
144
|
+
// timedelta,
|
|
145
|
+
// info.exclude.includes(rpath),
|
|
146
|
+
// stat?.mtimeMs - info.last,
|
|
147
|
+
// stat?.mtimeMs, info.last, exclude)
|
|
148
|
+
// }
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (exclude && info && !info.exclude.includes(rpath)) {
|
|
152
|
+
// NOT Path.sep - has to be canonical
|
|
153
|
+
info.exclude.push(rpath)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// console.log('COPY-EXCLUDE', rpath, exclude)
|
|
157
|
+
return exclude
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
function writeFileSync(buildctx: any, path: string, content: string) {
|
|
162
|
+
// console.log('WF', path)
|
|
163
|
+
const fs = buildctx.fs
|
|
164
|
+
// TODO: check excludes
|
|
165
|
+
fs.mkdirSync(Path.dirname(path), { recursive: true })
|
|
166
|
+
fs.writeFileSync(path, content, 'utf8')
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
function copyFileSync(buildctx: any, frompath: string, topath: string) {
|
|
171
|
+
const fs = buildctx.fs
|
|
172
|
+
// TODO: check excludes
|
|
173
|
+
fs.mkdirSync(Path.dirname(topath), { recursive: true })
|
|
174
|
+
fs.copyFileSync(frompath, topath)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
function processTemplate(
|
|
179
|
+
state: any,
|
|
180
|
+
src: any,
|
|
181
|
+
spec: { name: string, frompath: string, topath: string }) {
|
|
182
|
+
if (isTemplate(spec.name)) {
|
|
183
|
+
return genTemplate(state, src, spec)
|
|
184
|
+
}
|
|
185
|
+
return src
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function isTemplate(name: string) {
|
|
189
|
+
// TODO: need a better way; alsp should be configurable
|
|
190
|
+
return !name.match(/\.(gif|jpe?g|png|ico|bmp|tiff)$/i)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// NOTE: $$foo.bar$$ format used as explicit start and end markers mean regex can be used
|
|
194
|
+
// unambiguously ($fooa would not match `foo`)
|
|
195
|
+
function genTemplate(
|
|
196
|
+
state: any,
|
|
197
|
+
src: any,
|
|
198
|
+
spec: { name: string, frompath: string, topath: string }) {
|
|
199
|
+
|
|
200
|
+
let model = state.ctx$.model // { foo: 'FOO', bar: 'BAR' }
|
|
201
|
+
let out = ''
|
|
202
|
+
let remain = src
|
|
203
|
+
let nextm = true
|
|
204
|
+
while (nextm) {
|
|
205
|
+
let m = remain.match(/\$\$([^$]+)\$\$/)
|
|
206
|
+
if (m) {
|
|
207
|
+
let ref = m[1]
|
|
208
|
+
out += remain.substring(0, m.index)
|
|
209
|
+
let insert = getx(model, ref)
|
|
210
|
+
if (null == insert) {
|
|
211
|
+
out += '$$' + ref + '$$'
|
|
212
|
+
remain = remain.substring(m.index + 4 + ref.length)
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
out += insert
|
|
216
|
+
remain = remain.substring(m.index + 4 + ref.length)
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
out += remain
|
|
221
|
+
nextm = false
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return out
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export {
|
|
228
|
+
CopyOp
|
|
229
|
+
}
|
package/src/op/FileOp.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
|
|
2
|
+
import Path from 'node:path'
|
|
3
|
+
|
|
4
|
+
import type { Node } from '../jostraca'
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
const FileOp = {
|
|
8
|
+
|
|
9
|
+
before(node: Node, _ctx$: any, buildctx: any) {
|
|
10
|
+
const cfile: any = buildctx.current.file = node
|
|
11
|
+
cfile.filepath = buildctx.current.folder.path.join('/') + '/' + node.name
|
|
12
|
+
cfile.content = []
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
after(node: Node, _ctx$: any, buildctx: any) {
|
|
17
|
+
// console.log('FB-INFO', buildctx.info)
|
|
18
|
+
|
|
19
|
+
const { fs, info, current } = buildctx
|
|
20
|
+
const cfile = current.file
|
|
21
|
+
const content = cfile.content.join('')
|
|
22
|
+
const rpath = cfile.path.join('/') // NOT Path.sep - needs to be canonical
|
|
23
|
+
let exclude = node.exclude
|
|
24
|
+
|
|
25
|
+
// console.log('FILE-a exclude', rpath, exclude, !!info)
|
|
26
|
+
|
|
27
|
+
if (info && null == exclude) {
|
|
28
|
+
exclude = info.exclude.includes(rpath)
|
|
29
|
+
if (!exclude) {
|
|
30
|
+
const stat = fs.statSync(cfile.filepath, { throwIfNoEntry: false })
|
|
31
|
+
if (stat) {
|
|
32
|
+
let timedelta = stat.mtimeMs - info.last
|
|
33
|
+
if ((timedelta > 0 && timedelta < stat.mtimeMs)) {
|
|
34
|
+
exclude = true
|
|
35
|
+
}
|
|
36
|
+
// console.log('STAT', rpath, timedelta, exclude, stat?.mtimeMs, info.last)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// console.log('FILE-a write', rpath, exclude) // , content.substring(0, 111))
|
|
42
|
+
|
|
43
|
+
if (!exclude) {
|
|
44
|
+
fs.writeFileSync(cfile.filepath, content)
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
if (!info.exclude.includes(rpath)) {
|
|
48
|
+
info.exclude.push(rpath)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
export {
|
|
57
|
+
FileOp
|
|
58
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
|
|
2
|
+
import Path from 'node:path'
|
|
3
|
+
|
|
4
|
+
import type { Node } from '../jostraca'
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
const FolderOp = {
|
|
8
|
+
|
|
9
|
+
before(node: Node, _ctx$: any, buildctx: any) {
|
|
10
|
+
const cfolder = buildctx.current.folder = (buildctx.current.folder || {})
|
|
11
|
+
|
|
12
|
+
cfolder.node = node
|
|
13
|
+
cfolder.path = (cfolder.path || [buildctx.current.folder.parent])
|
|
14
|
+
cfolder.path.push(node.name)
|
|
15
|
+
|
|
16
|
+
let fullpath = cfolder.path.join(Path.sep)
|
|
17
|
+
buildctx.fs.mkdirSync(fullpath, { recursive: true })
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
after(_node: Node, _ctx$: any, buildctx: any) {
|
|
22
|
+
const cfolder = buildctx.current.folder
|
|
23
|
+
cfolder.path.length = cfolder.path.length - 1
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
export {
|
|
30
|
+
FolderOp
|
|
31
|
+
}
|
package/src/op/NoneOp.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
|
|
2
|
+
import Path from 'node:path'
|
|
3
|
+
|
|
4
|
+
import type { Node } from '../jostraca'
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
const ProjectOp = {
|
|
8
|
+
|
|
9
|
+
before(node: Node, ctx$: any, buildctx: any) {
|
|
10
|
+
// console.log('PROJECT-B', node.folder, ctx$.folder)
|
|
11
|
+
|
|
12
|
+
node.folder = null == node.folder || '' === node.folder ? '.' : node.folder
|
|
13
|
+
node.folder = Path.isAbsolute(node.folder) ? node.folder : Path.join(ctx$.folder, node.folder)
|
|
14
|
+
|
|
15
|
+
// console.log('PROJECT-B folder', node.folder)
|
|
16
|
+
|
|
17
|
+
buildctx.current.project = { node }
|
|
18
|
+
buildctx.current.folder = {
|
|
19
|
+
node,
|
|
20
|
+
path: node.folder.split(Path.sep),
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
buildctx.fs.mkdirSync(node.folder, { recursive: true })
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
after(_node: Node, _ctx$: any, _buildctx: any) { },
|
|
27
|
+
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
export {
|
|
32
|
+
ProjectOp
|
|
33
|
+
}
|
package/src/utility.ts
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
/* Copyright (c) 2024 Richard Rodger, MIT License */
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
type JostracaOptions = {
|
|
5
|
+
folder?: string
|
|
6
|
+
fs?: any
|
|
7
|
+
meta?: any
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
type Node = {
|
|
12
|
+
kind: string
|
|
13
|
+
children?: Node[]
|
|
14
|
+
name?: string
|
|
15
|
+
path: string[]
|
|
16
|
+
from?: string
|
|
17
|
+
content?: any[]
|
|
18
|
+
folder?: string
|
|
19
|
+
after?: any
|
|
20
|
+
exclude?: boolean
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
type OpStep = (node: Node, ctx$: any, buildctx: any) => void
|
|
25
|
+
|
|
26
|
+
type OpDef = {
|
|
27
|
+
before: OpStep,
|
|
28
|
+
after: OpStep,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
type Component = (props: any, children?: any) => void
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
function each(subject?: any, apply?: any) {
|
|
38
|
+
if (null == apply) {
|
|
39
|
+
let out = []
|
|
40
|
+
if (Array.isArray(subject)) {
|
|
41
|
+
for (let fn of subject) {
|
|
42
|
+
out.push('function' === typeof fn ? fn() : fn)
|
|
43
|
+
}
|
|
44
|
+
return out.sort()
|
|
45
|
+
}
|
|
46
|
+
else if (null == subject || 'object' !== typeof subject) {
|
|
47
|
+
return []
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else if (Array.isArray(subject)) {
|
|
51
|
+
return subject.map(apply)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (null == subject || 'object' !== typeof subject) {
|
|
55
|
+
return []
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const entries: any = Object.entries(subject).map((n: any[], _: any) =>
|
|
59
|
+
(_ = typeof n[1],
|
|
60
|
+
(null != n[1] && 'object' === _) ? (n[1].key$ = n[0]) :
|
|
61
|
+
(n[1] = { name: n[0], key$: n[0], val$: n[1] }), n))
|
|
62
|
+
|
|
63
|
+
if (1 < entries.length) {
|
|
64
|
+
if (entries[0] && entries[0][1] && 'string' === typeof entries[0][1].name) {
|
|
65
|
+
entries.sort((a: any, b: any) =>
|
|
66
|
+
a[1].name < b[1].name ? -1 : b[1].name < a[1].name ? 1 : 0)
|
|
67
|
+
}
|
|
68
|
+
else if (entries[0] && entries[0][1] && 'string' === typeof entries[0][1].key$) {
|
|
69
|
+
entries.sort((a: any, b: any) =>
|
|
70
|
+
a[1].key$ < b[1].key$ ? -1 : b[1].key$ < a[1].key$ ? 1 : 0)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
apply = 'function' === typeof apply ? apply : (x: any) => x
|
|
75
|
+
|
|
76
|
+
return entries.map((n: any, ...args: any[]) =>
|
|
77
|
+
apply(n[1], n[0], ...args))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
function select(key: any, map: Record<string, Function>) {
|
|
83
|
+
const fn = map && map[key]
|
|
84
|
+
return fn ? fn() : undefined
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
function getx(root: any, path: string | string[]): any {
|
|
90
|
+
path = ('string' === typeof path ? path.split(/[.\s\r\n\t]/) : path).filter(part => '' != part)
|
|
91
|
+
let node = root
|
|
92
|
+
let parents = []
|
|
93
|
+
|
|
94
|
+
partloop:
|
|
95
|
+
for (let i = 0; i < path.length && null != node; i++) {
|
|
96
|
+
let part = String(path[i]).trim()
|
|
97
|
+
|
|
98
|
+
if ('*' === part) {
|
|
99
|
+
// console.log('STAR', each(node), path.slice(i + 1))
|
|
100
|
+
return getx(each(node), path.slice(i + 1))
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let m = part.match(/^([^<=>~^?!]*)([<=>~^?!]+)(.*)$/)
|
|
104
|
+
|
|
105
|
+
if (m) {
|
|
106
|
+
part = m[1]
|
|
107
|
+
let op = m[2]
|
|
108
|
+
let arg: any = m[3]
|
|
109
|
+
|
|
110
|
+
let val = '' === part ? node : node[part]
|
|
111
|
+
|
|
112
|
+
const argtype = typeof arg
|
|
113
|
+
arg =
|
|
114
|
+
'true' === arg ? true :
|
|
115
|
+
'false' === arg ? false :
|
|
116
|
+
'string' === argtype ?
|
|
117
|
+
(arg.match(/^"[^"]+"$/) ? arg.substring(1, arg.length - 1) : arg) : arg
|
|
118
|
+
|
|
119
|
+
// console.log('GETX-M', val, part, op, arg)
|
|
120
|
+
|
|
121
|
+
if ('=' === op && 'null' === arg) {
|
|
122
|
+
parents.push(node)
|
|
123
|
+
node = {} // virtual node so that ^ works consistently
|
|
124
|
+
continue partloop
|
|
125
|
+
}
|
|
126
|
+
else if ('^' === op && '' === part && '' !== arg) {
|
|
127
|
+
node = parents[parents.length - Number(arg)]
|
|
128
|
+
continue partloop
|
|
129
|
+
}
|
|
130
|
+
else if ('?' === op[0]) {
|
|
131
|
+
// no property name
|
|
132
|
+
arg = (1 < op.length ? op.substring(1) : '') + arg
|
|
133
|
+
|
|
134
|
+
node = Array.isArray(val) ?
|
|
135
|
+
each(val).filter((n: any) => (
|
|
136
|
+
null != getx(n, arg))) :
|
|
137
|
+
each(val).filter((n: any) => (
|
|
138
|
+
null != getx(n, arg)))
|
|
139
|
+
.reduce((a: any, n: any) => (a[n.key$] = n, delete n.key$, a), {})
|
|
140
|
+
|
|
141
|
+
continue partloop
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (null == val) return undefined
|
|
145
|
+
|
|
146
|
+
// const valtype = typeof val
|
|
147
|
+
|
|
148
|
+
val = Array.isArray(val) ? val.length :
|
|
149
|
+
'object' === typeof val ? Object.keys(val).filter(k => !k.includes('$')).length :
|
|
150
|
+
val
|
|
151
|
+
|
|
152
|
+
switch (op) {
|
|
153
|
+
case '<':
|
|
154
|
+
if (!(val < arg)) return undefined
|
|
155
|
+
break
|
|
156
|
+
case '<=':
|
|
157
|
+
if (!(val <= arg)) return undefined
|
|
158
|
+
break
|
|
159
|
+
case '>':
|
|
160
|
+
if (!(val > arg)) return undefined
|
|
161
|
+
break
|
|
162
|
+
case '>=':
|
|
163
|
+
if (!(val >= arg)) return undefined
|
|
164
|
+
break
|
|
165
|
+
case '=':
|
|
166
|
+
if (!(val == arg)) return undefined
|
|
167
|
+
break
|
|
168
|
+
case '==':
|
|
169
|
+
if (!(val === arg)) return undefined
|
|
170
|
+
break
|
|
171
|
+
case '!=':
|
|
172
|
+
if (!(val != arg)) return undefined
|
|
173
|
+
break
|
|
174
|
+
case '~':
|
|
175
|
+
if (!(String(val).match(RegExp(arg)))) return undefined
|
|
176
|
+
break
|
|
177
|
+
case '^':
|
|
178
|
+
node = parents[parents.length - Number(arg)]
|
|
179
|
+
continue partloop
|
|
180
|
+
default:
|
|
181
|
+
return undefined
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
parents.push(node)
|
|
186
|
+
node = '' === part ? node : node[part]
|
|
187
|
+
}
|
|
188
|
+
return node
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
function get(root: any, path: string | string[]): any {
|
|
193
|
+
path = 'string' === typeof path ? path.split('.') : path
|
|
194
|
+
let node = root
|
|
195
|
+
for (let i = 0; i < path.length && null != node; i++) {
|
|
196
|
+
node = node[path[i]]
|
|
197
|
+
}
|
|
198
|
+
return node
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
function camelify(input: any[] | string) {
|
|
203
|
+
let parts = 'string' == typeof input ? input.split('-') : input.map(n => '' + n)
|
|
204
|
+
return parts
|
|
205
|
+
.map((p: string) => ('' === p ? '' : (p[0].toUpperCase() + p.substring(1))))
|
|
206
|
+
.join('')
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
function kebabify(input: any[] | string) {
|
|
211
|
+
let parts = 'string' == typeof input ? input.split(/([A-Z])/) : input.map(n => '' + n)
|
|
212
|
+
return parts
|
|
213
|
+
.filter((p: string) => '' !== p)
|
|
214
|
+
.reduce((a: any[], n: string, i: number) =>
|
|
215
|
+
((0 === i % 2 ? a.push(n.toLowerCase()) : a[(i / 2) | 0] += n), a), [])
|
|
216
|
+
.join('-')
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
function snakify(input: any[] | string) {
|
|
221
|
+
let parts = 'string' == typeof input ? input.split(/([A-Z])/) : input.map(n => '' + n)
|
|
222
|
+
return parts
|
|
223
|
+
.filter((p: string) => '' !== p)
|
|
224
|
+
.reduce((a: any[], n: string, i: number) =>
|
|
225
|
+
((0 === i % 2 ? a.push(n.toLowerCase()) : a[(i / 2) | 0] += n), a), [])
|
|
226
|
+
.join('_')
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
function names(base: any, name: string, prop = 'name') {
|
|
231
|
+
base.name$ = name
|
|
232
|
+
base[prop.toLowerCase()] = name.toLowerCase()
|
|
233
|
+
base[camelify(prop)] = camelify(name)
|
|
234
|
+
base[snakify(prop)] = snakify(name)
|
|
235
|
+
base[kebabify(prop)] = kebabify(name)
|
|
236
|
+
base[prop.toUpperCase()] = name.toUpperCase()
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
// Map child objects to new child objects
|
|
242
|
+
function cmap(o: any, p: any) {
|
|
243
|
+
return Object
|
|
244
|
+
.entries(o)
|
|
245
|
+
.reduce((r: any, n: any, _: any) => (_ = Object
|
|
246
|
+
.entries(p)
|
|
247
|
+
.reduce((s: any, m: any) => (cmap.FILTER === s ? s : (s[m[0]] = (
|
|
248
|
+
// transfom(val,key,current,parentkey,parent)
|
|
249
|
+
'function' === typeof m[1] ? m[1](n[1][m[0]], {
|
|
250
|
+
skey: m[0], self: n[1], key: n[0], parent: o
|
|
251
|
+
}) : m[1]
|
|
252
|
+
), (cmap.FILTER === s[m[0]] ? cmap.FILTER : s))), {})
|
|
253
|
+
, (cmap.FILTER === _ ? 0 : r[n[0]] = _), r), {})
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
cmap.COPY = (x: any) => x
|
|
257
|
+
// keep self if x is truthy, or function returning truthy-new-value or [truthy,new-value]
|
|
258
|
+
cmap.FILTER = (x: any) => 'function' === typeof x ? ((y: any, p: any, _: any) =>
|
|
259
|
+
(_ = x(y, p), Array.isArray(_) ? !_[0] ? _[1] : cmap.FILTER : _)) : (x ? x : cmap.FILTER)
|
|
260
|
+
cmap.KEY = (_: any, p: any) => p.key
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
// Map child objects to a list of child objects
|
|
264
|
+
function vmap(o: any, p: any) {
|
|
265
|
+
return Object
|
|
266
|
+
.entries(o)
|
|
267
|
+
.reduce((r: any, n: any, _: any) => (_ = Object
|
|
268
|
+
.entries(p)
|
|
269
|
+
.reduce((s: any, m: any) => (vmap.FILTER === s ? s : (s[m[0]] = (
|
|
270
|
+
// transfom(val,key,current,parentkey,parent)
|
|
271
|
+
// 'function' === typeof m[1] ? m[1](n[1][m[0]], m[0], n[1], n[0], o) : m[1]
|
|
272
|
+
'function' === typeof m[1] ? m[1](n[1][m[0]], {
|
|
273
|
+
skey: m[0], self: n[1], key: n[0], parent: o
|
|
274
|
+
}) : m[1]
|
|
275
|
+
), (vmap.FILTER === s[m[0]] ? vmap.FILTER : s))), {})
|
|
276
|
+
, (vmap.FILTER === _ ? 0 : r.push(_)), r), [])
|
|
277
|
+
|
|
278
|
+
}
|
|
279
|
+
vmap.COPY = (x: any) => x
|
|
280
|
+
vmap.FILTER = (x: any) => 'function' === typeof x ? ((y: any, p: any, _: any) =>
|
|
281
|
+
(_ = x(y, p), Array.isArray(_) ? !_[0] ? _[1] : vmap.FILTER : _)) : (x ? x : vmap.FILTER)
|
|
282
|
+
vmap.KEY = (_: any, p: any) => p.key
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
export type {
|
|
287
|
+
JostracaOptions,
|
|
288
|
+
Node,
|
|
289
|
+
OpStep,
|
|
290
|
+
OpDef,
|
|
291
|
+
Component,
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
export {
|
|
296
|
+
each,
|
|
297
|
+
select,
|
|
298
|
+
get,
|
|
299
|
+
getx,
|
|
300
|
+
camelify,
|
|
301
|
+
snakify,
|
|
302
|
+
kebabify,
|
|
303
|
+
cmap,
|
|
304
|
+
vmap,
|
|
305
|
+
names,
|
|
306
|
+
}
|