forked from ergogen/ergogen
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcli.js
executable file
·197 lines (162 loc) · 5.14 KB
/
cli.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#!/usr/bin/env node
const fs = require('fs-extra')
const fsp = require('fs/promises')
const path = require('path')
const yaml = require('js-yaml')
const yargs = require('yargs')
const jszip = require('jszip')
const io = require('./io')
const pkg = require('../package.json')
const ergogen = require('./ergogen')
;(async () => {
// command line args
const args = yargs
.option('output', {
alias: 'o',
default: path.resolve('output'),
describe: 'Output folder',
type: 'string'
})
.option('debug', {
alias: 'd',
default: false,
describe: 'Debug mode',
type: 'boolean'
})
.option('clean', {
default: false,
describe: 'Clean output dir before parsing',
type: 'boolean'
})
.argv
// greetings
const title_suffix = args.debug ? ' (Debug Mode)' : ''
console.log(`Ergogen v${pkg.version} CLI${title_suffix}`)
console.log()
// input helpers
// zip handling is baked in at the io level, so that both the cli and the webui can use it
// if, for local development, we want to use a folder as input, we temporarily zip it in
// memory so that it can be handled the exact same way
// functions shamelessly repurposed from https://github.com/Stuk/jszip/issues/386
// return a flat array of absolute paths of all files recursively contained in the dir
const list_files_in_dir = async (dir) => {
const list = await fsp.readdir(dir)
const statPromises = list.map(async (file) => {
const fullPath = path.resolve(dir, file)
const stat = await fsp.stat(fullPath)
if (stat && stat.isDirectory()) {
return list_files_in_dir(fullPath)
}
return fullPath
})
return (await Promise.all(statPromises)).flat(Infinity)
}
// create an in-memory zip stream from a folder in the file system
const zip_from_dir = async (dir) => {
const absRoot = path.resolve(dir)
const filePaths = await list_files_in_dir(dir)
return filePaths.reduce((z, filePath) => {
const relative = filePath.replace(absRoot, '')
// create folder trees manually :(
const zipFolder = path
.dirname(relative)
.split(path.sep)
.reduce((zf, dirName) => zf.folder(dirName), z)
zipFolder.file(path.basename(filePath), fs.createReadStream(filePath))
return z
}, new jszip())
}
// input reading
const config_file = args._[0]
if (!config_file) {
console.error('Usage: ergogen <config_file> [options]')
process.exit(1)
}
if (!fs.existsSync(config_file)) {
console.error(`Could not read config file "${config_file}": File does not exist!`)
process.exit(2)
}
let config_text = ''
let injections = []
try {
if (config_file.endsWith('.zip') || config_file.endsWith('.ekb')) {
console.log('Analyzing bundle...');
[config_text, injections] = await io.unpack(
await (new jszip()).loadAsync(fs.readFileSync(config_file))
)
} else if (fs.statSync(config_file).isDirectory()) {
console.log('Analyzing folder...');
[config_text, injections] = await io.unpack(
await zip_from_dir(config_file)
)
} else {
config_text = fs.readFileSync(config_file).toString()
// no injections...
}
for (const [type, name, value] of injections) {
ergogen.inject(type, name, value)
}
} catch (err) {
console.error(`Could not read config file "${config_file}"!`)
console.error(err)
process.exit(2)
}
// processing
let results
try {
results = await ergogen.process(config_text, args.debug, s => console.log(s))
} catch (err) {
console.error(err)
process.exit(3)
}
// output helpers
const yamldump = data => yaml.dump(data, {indent: 4, noRefs: true})
const single = (data, rel) => {
if (!data) return
const abs = path.join(args.o, rel)
fs.mkdirpSync(path.dirname(abs))
if (abs.endsWith('.yaml')) {
fs.writeFileSync(abs, yamldump(data))
} else {
fs.writeFileSync(abs, data)
}
}
const composite = (data, rel) => {
if (!data) return
const abs = path.join(args.o, rel)
if (data.yaml) {
fs.mkdirpSync(path.dirname(abs))
fs.writeFileSync(abs + '.yaml', yamldump(data.yaml))
}
for (const format of ['svg', 'dxf', 'jscad']) {
if (data[format]) {
fs.mkdirpSync(path.dirname(abs))
fs.writeFileSync(abs + '.' + format, data[format])
}
}
}
// output generation
if (args.clean) {
console.log('Cleaning output folder...')
fs.removeSync(args.o)
}
console.log('Writing output to disk...')
fs.mkdirpSync(args.o)
single(results.raw, 'source/raw.txt')
single(results.canonical, 'source/canonical.yaml')
single(results.units, 'points/units.yaml')
single(results.points, 'points/points.yaml')
composite(results.demo, 'points/demo')
for (const [name, outline] of Object.entries(results.outlines)) {
composite(outline, `outlines/${name}`)
}
for (const [name, _case] of Object.entries(results.cases)) {
composite(_case, `cases/${name}`)
}
for (const [name, pcb] of Object.entries(results.pcbs)) {
single(pcb, `pcbs/${name}.kicad_pcb`)
}
// goodbye
console.log('Done.')
console.log()
})()