@jscad/cli 2.2.26 → 2.3.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/CHANGELOG.md +11 -0
- package/README.md +10 -0
- package/cli.js +54 -7
- package/cli.parameters.test.js +95 -2
- package/package.json +6 -5
- package/src/generateOutputData.js +10 -2
- package/src/parseArgs.js +8 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [2.3.0](https://github.com/jscad/OpenJSCAD.org/compare/@jscad/cli@2.2.26...@jscad/cli@2.3.0) (2023-06-27)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **cli:** added options to output designs as individual parts, and create zip archive of parts ([a20b9cf](https://github.com/jscad/OpenJSCAD.org/commit/a20b9cf0edaaeca051427372d237af4c3522b04c))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
6
17
|
## [2.2.26](https://github.com/jscad/OpenJSCAD.org/compare/@jscad/cli@2.2.25...@jscad/cli@2.2.26) (2023-04-30)
|
|
7
18
|
|
|
8
19
|
|
package/README.md
CHANGED
|
@@ -73,6 +73,16 @@ Examples:
|
|
|
73
73
|
|
|
74
74
|
```jscad mydesign.js -of amf # -- convert mydesign.js into mydesign.amf```
|
|
75
75
|
|
|
76
|
+
For multi-part models, you can pass the `generateParts` flag `-gp` to output each part as a separate, numbered file:
|
|
77
|
+
|
|
78
|
+
```jscad mydesign.js -gp # -- convert mydesign.js into mydesign-part-1-of-2.stl and mydesign-part-2-of-2.stl```
|
|
79
|
+
|
|
80
|
+
You may also pass the `zip` flag `-z` to zip generated files into one .zip file:
|
|
81
|
+
|
|
82
|
+
```jscad mydesign.js -z # -- convert mydesign.js into mydesign.zip which contains: mydesign.stl```
|
|
83
|
+
|
|
84
|
+
```jscad mydesign.js -gp -z # -- convert mydesign.js into mydesign.zip which contains: mydesign-part-1-of-2.stl and mydesign-part-2-of-2.stl```
|
|
85
|
+
|
|
76
86
|
The '-o' option can be used to control where the output will be placed.
|
|
77
87
|
While, the '-of' option can be used to control the format of the output.
|
|
78
88
|
|
package/cli.js
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
// jscad name_plate.jscad --name "Just Me" --title "CEO" -o amf test.amf
|
|
23
23
|
//
|
|
24
24
|
const fs = require('fs')
|
|
25
|
+
const JSZip = require('jszip')
|
|
25
26
|
|
|
26
27
|
const { formats } = require('@jscad/io/formats')
|
|
27
28
|
|
|
@@ -33,7 +34,7 @@ const parseArgs = require('./src/parseArgs')
|
|
|
33
34
|
|
|
34
35
|
// handle arguments (inputs, outputs, etc)
|
|
35
36
|
const args = process.argv.splice(2)
|
|
36
|
-
let { inputFile, inputFormat, outputFile, outputFormat, params, addMetaData, inputIsDirectory } = parseArgs(args)
|
|
37
|
+
let { inputFile, inputFormat, outputFile, outputFormat, generateParts, zip, params, addMetaData, inputIsDirectory } = parseArgs(args)
|
|
37
38
|
|
|
38
39
|
// outputs
|
|
39
40
|
const output = determineOutputNameAndFormat(outputFormat, outputFile, inputFile)
|
|
@@ -48,18 +49,64 @@ const clicolors = {
|
|
|
48
49
|
black: '\u{1b}[0m'
|
|
49
50
|
}
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
const logFileOutput = (outputFile) => {
|
|
53
|
+
console.log(`${clicolors.blue}JSCAD: generating output ${clicolors.red}
|
|
54
|
+
from: ${clicolors.green} ${inputFile} ${clicolors.red}
|
|
55
|
+
to: ${clicolors.green} ${outputFile} ${clicolors.yellow}(${formats[outputFormat].description}) ${clicolors.black}
|
|
56
|
+
`)
|
|
57
|
+
}
|
|
55
58
|
|
|
56
59
|
// read input data
|
|
57
60
|
const src = fs.readFileSync(inputFile, inputFile.match(/\.stl$/i) ? 'binary' : 'UTF8')
|
|
58
61
|
|
|
59
62
|
// -- convert from JSCAD script into the desired output format
|
|
60
63
|
// -- and write it to disk
|
|
61
|
-
generateOutputData(src, params, { outputFile, outputFormat, inputFile, inputFormat, version, addMetaData, inputIsDirectory })
|
|
62
|
-
.then((outputData) =>
|
|
64
|
+
generateOutputData(src, params, { outputFile, outputFormat, inputFile, inputFormat, generateParts, version, addMetaData, inputIsDirectory })
|
|
65
|
+
.then((outputData) => {
|
|
66
|
+
if (outputData instanceof Array) {
|
|
67
|
+
if (zip) {
|
|
68
|
+
const zip = new JSZip()
|
|
69
|
+
for (let i = 0; i < outputData.length; i++) {
|
|
70
|
+
const filename = outputFile.replace(/\.(\w+)$/, `-part-${i + 1}-of-${outputData.length}.$1`)
|
|
71
|
+
zip.file(filename, outputData[i].asBuffer())
|
|
72
|
+
}
|
|
73
|
+
zip.generateAsync({ type: 'nodebuffer' }).then((content) => {
|
|
74
|
+
const zipFilename = outputFile.replace(/\.(\w+)$/, '.zip')
|
|
75
|
+
fs.writeFile(zipFilename, content, (err) => {
|
|
76
|
+
if (err) {
|
|
77
|
+
console.error(err)
|
|
78
|
+
} else {
|
|
79
|
+
logFileOutput(zipFilename)
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
})
|
|
83
|
+
} else {
|
|
84
|
+
for (let i = 0; i < outputData.length; i++) {
|
|
85
|
+
const filename = outputFile.replace(/\.(\w+)$/, `-part-${i + 1}-of-${outputData.length}.$1`)
|
|
86
|
+
logFileOutput(filename)
|
|
87
|
+
writeOutput(filename, outputData[i])
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
if (zip) {
|
|
92
|
+
const zip = new JSZip()
|
|
93
|
+
zip.file(outputFile, outputData.asBuffer())
|
|
94
|
+
zip.generateAsync({ type: 'nodebuffer' }).then((content) => {
|
|
95
|
+
const zipFilename = outputFile.replace(/\.(\w+)$/, '.zip')
|
|
96
|
+
fs.writeFile(zipFilename, content, (err) => {
|
|
97
|
+
if (err) {
|
|
98
|
+
console.error(err)
|
|
99
|
+
} else {
|
|
100
|
+
logFileOutput(zipFilename)
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
})
|
|
104
|
+
} else {
|
|
105
|
+
logFileOutput(outputFile)
|
|
106
|
+
writeOutput(outputFile, outputData)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
})
|
|
63
110
|
.catch((error) => {
|
|
64
111
|
console.error(error)
|
|
65
112
|
process.exit(1)
|
package/cli.parameters.test.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const test = require('ava')
|
|
2
|
+
const JSZip = require('jszip')
|
|
2
3
|
|
|
3
4
|
const path = require('path')
|
|
4
5
|
const { execSync } = require('child_process')
|
|
@@ -13,6 +14,12 @@ test.afterEach.always((t) => {
|
|
|
13
14
|
try {
|
|
14
15
|
if (t.context.outputPath) fs.unlinkSync(t.context.outputPath)
|
|
15
16
|
} catch (err) {}
|
|
17
|
+
try {
|
|
18
|
+
if (t.context.outputPath2) fs.unlinkSync(t.context.outputPath2)
|
|
19
|
+
} catch (err) {}
|
|
20
|
+
try {
|
|
21
|
+
if (t.context.outputPath3) fs.unlinkSync(t.context.outputPath3)
|
|
22
|
+
} catch (err) {}
|
|
16
23
|
|
|
17
24
|
try {
|
|
18
25
|
if (t.context.folderPath) fs.rmdirSync(t.context.folderPath, { recursive: false })
|
|
@@ -32,7 +39,7 @@ test.beforeEach((t) => {
|
|
|
32
39
|
|
|
33
40
|
// create a simple JSCAD script for input
|
|
34
41
|
// the script should produce ALL geometry types
|
|
35
|
-
const createJscad = (id) => {
|
|
42
|
+
const createJscad = (id, multipart = false) => {
|
|
36
43
|
const jscadScript = `// test script ${id}
|
|
37
44
|
const { primitives } = require('@jscad/modeling')
|
|
38
45
|
|
|
@@ -51,7 +58,7 @@ const main = (params) => {
|
|
|
51
58
|
let ageom2 = primitives.ellipse()
|
|
52
59
|
let ageom3 = primitives.ellipsoid()
|
|
53
60
|
|
|
54
|
-
return [apath2, ageom2, ageom3]
|
|
61
|
+
${multipart ? `return [ageom3, ageom3, ageom3]` : `return [apath2, ageom2, ageom3]`}
|
|
55
62
|
}
|
|
56
63
|
|
|
57
64
|
module.exports = { main, getParameterDefinitions }
|
|
@@ -207,3 +214,89 @@ test('cli (single input file, invalid jscad)', (t) => {
|
|
|
207
214
|
execSync(cmd, { stdio: [0, 1, 2] })
|
|
208
215
|
})
|
|
209
216
|
})
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
test('cli (single input file, multiple output files)', (t) => {
|
|
220
|
+
const testID = 7
|
|
221
|
+
|
|
222
|
+
const inputPath = createJscad(testID, true)
|
|
223
|
+
t.true(fs.existsSync(inputPath))
|
|
224
|
+
|
|
225
|
+
t.context.inputPath = inputPath
|
|
226
|
+
|
|
227
|
+
const outputName = (partNum) => `./test${testID}-part-${partNum}-of-3.stl`
|
|
228
|
+
const outputPath1 = path.resolve(__dirname, outputName(1))
|
|
229
|
+
const outputPath2 = path.resolve(__dirname, outputName(2))
|
|
230
|
+
const outputPath3 = path.resolve(__dirname, outputName(3))
|
|
231
|
+
t.false(fs.existsSync(outputPath1))
|
|
232
|
+
t.false(fs.existsSync(outputPath2))
|
|
233
|
+
t.false(fs.existsSync(outputPath3))
|
|
234
|
+
|
|
235
|
+
t.context.outputPath = outputPath1
|
|
236
|
+
t.context.outputPath2 = outputPath2
|
|
237
|
+
t.context.outputPath3 = outputPath3
|
|
238
|
+
|
|
239
|
+
const cliPath = t.context.cliPath
|
|
240
|
+
|
|
241
|
+
const cmd = `node ${cliPath} ${inputPath} -gp`
|
|
242
|
+
execSync(cmd, { stdio: [0,1,2] })
|
|
243
|
+
t.true(fs.existsSync(outputPath1))
|
|
244
|
+
t.true(fs.existsSync(outputPath2))
|
|
245
|
+
t.true(fs.existsSync(outputPath3))
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
test('cli (single multipart input file, zipped output file)', async (t) => {
|
|
249
|
+
const testID = 8
|
|
250
|
+
|
|
251
|
+
const inputPath = createJscad(testID, true)
|
|
252
|
+
t.true(fs.existsSync(inputPath))
|
|
253
|
+
|
|
254
|
+
t.context.inputPath = inputPath
|
|
255
|
+
|
|
256
|
+
const outputName = `./test${testID}.zip`
|
|
257
|
+
const outputPath = path.resolve(__dirname, outputName)
|
|
258
|
+
|
|
259
|
+
t.false(fs.existsSync(outputPath))
|
|
260
|
+
|
|
261
|
+
t.context.outputPath = outputPath
|
|
262
|
+
|
|
263
|
+
const cliPath = t.context.cliPath
|
|
264
|
+
|
|
265
|
+
const cmd = `node ${cliPath} ${inputPath} -gp -z`
|
|
266
|
+
execSync(cmd, { stdio: [0,1,2] })
|
|
267
|
+
t.true(fs.existsSync(outputPath))
|
|
268
|
+
|
|
269
|
+
// check contents of zip file
|
|
270
|
+
const data = await fs.promises.readFile(outputPath)
|
|
271
|
+
const content = await JSZip.loadAsync(data)
|
|
272
|
+
t.true(content.files[path.resolve(__dirname, `./test${testID}-part-1-of-3.stl`)] !== undefined)
|
|
273
|
+
t.true(content.files[path.resolve(__dirname, `./test${testID}-part-2-of-3.stl`)] !== undefined)
|
|
274
|
+
t.true(content.files[path.resolve(__dirname, `./test${testID}-part-3-of-3.stl`)] !== undefined)
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
test('cli (single input file, zipped output file)', async (t) => {
|
|
278
|
+
const testID = 9
|
|
279
|
+
|
|
280
|
+
const inputPath = createJscad(testID, true)
|
|
281
|
+
t.true(fs.existsSync(inputPath))
|
|
282
|
+
|
|
283
|
+
t.context.inputPath = inputPath
|
|
284
|
+
|
|
285
|
+
const outputName = `./test${testID}.zip`
|
|
286
|
+
const outputPath = path.resolve(__dirname, outputName)
|
|
287
|
+
|
|
288
|
+
t.false(fs.existsSync(outputPath))
|
|
289
|
+
|
|
290
|
+
t.context.outputPath = outputPath
|
|
291
|
+
|
|
292
|
+
const cliPath = t.context.cliPath
|
|
293
|
+
|
|
294
|
+
const cmd = `node ${cliPath} ${inputPath} -z`
|
|
295
|
+
execSync(cmd, { stdio: [0,1,2] })
|
|
296
|
+
t.true(fs.existsSync(outputPath))
|
|
297
|
+
|
|
298
|
+
// check contents of zip file
|
|
299
|
+
const data = await fs.promises.readFile(outputPath)
|
|
300
|
+
const content = await JSZip.loadAsync(data)
|
|
301
|
+
t.true(content.files[path.resolve(__dirname, `./test${testID}.stl`)] !== undefined)
|
|
302
|
+
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jscad/cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Command Line Interface (CLI) for JSCAD",
|
|
5
5
|
"homepage": "https://openjscad.xyz/",
|
|
6
6
|
"repository": "https://github.com/jscad/OpenJSCAD.org",
|
|
@@ -36,9 +36,10 @@
|
|
|
36
36
|
"license": "MIT",
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@jscad/array-utils": "2.1.4",
|
|
39
|
-
"@jscad/core": "2.6.
|
|
40
|
-
"@jscad/io": "2.4.
|
|
41
|
-
"@jscad/modeling": "2.
|
|
39
|
+
"@jscad/core": "2.6.7",
|
|
40
|
+
"@jscad/io": "2.4.6",
|
|
41
|
+
"@jscad/modeling": "2.12.0",
|
|
42
|
+
"jszip": "^3.10.1"
|
|
42
43
|
},
|
|
43
44
|
"devDependencies": {
|
|
44
45
|
"ava": "3.15.0",
|
|
@@ -49,5 +50,5 @@
|
|
|
49
50
|
"url": "https://opencollective.com/openjscad",
|
|
50
51
|
"logo": "https://opencollective.com/openjscad/logo.txt"
|
|
51
52
|
},
|
|
52
|
-
"gitHead": "
|
|
53
|
+
"gitHead": "e269f212db5a00cda740d2f7ad3e5206d1eb839f"
|
|
53
54
|
}
|
|
@@ -19,10 +19,11 @@ const generateOutputData = (source, params, options) => {
|
|
|
19
19
|
outputFormat: 'stl',
|
|
20
20
|
inputFile: '',
|
|
21
21
|
version: '',
|
|
22
|
-
addMetaData: true
|
|
22
|
+
addMetaData: true,
|
|
23
|
+
generateParts: false
|
|
23
24
|
}
|
|
24
25
|
options = Object.assign({}, defaults, options)
|
|
25
|
-
const { outputFormat, inputFile, inputFormat } = options
|
|
26
|
+
const { outputFormat, inputFile, inputFormat, generateParts } = options
|
|
26
27
|
|
|
27
28
|
options.filename = inputFile // for deserializers
|
|
28
29
|
|
|
@@ -62,6 +63,13 @@ const generateOutputData = (source, params, options) => {
|
|
|
62
63
|
})
|
|
63
64
|
.then((solids) => {
|
|
64
65
|
const serializerOptions = Object.assign({ format: outputFormat }, params)
|
|
66
|
+
if (generateParts) {
|
|
67
|
+
let blobs = []
|
|
68
|
+
for (let i = 0; i < solids.length; i++) {
|
|
69
|
+
blobs.push(solidsAsBlob(solids[i], serializerOptions))
|
|
70
|
+
}
|
|
71
|
+
return blobs
|
|
72
|
+
}
|
|
65
73
|
return solidsAsBlob(solids, serializerOptions)
|
|
66
74
|
})
|
|
67
75
|
}
|
package/src/parseArgs.js
CHANGED
|
@@ -24,6 +24,8 @@ const parseArgs = (args) => {
|
|
|
24
24
|
let inputFormat
|
|
25
25
|
let outputFile
|
|
26
26
|
let outputFormat
|
|
27
|
+
let generateParts = false
|
|
28
|
+
let zip = false
|
|
27
29
|
const params = {} // parameters to feed the script if applicable
|
|
28
30
|
let addMetaData = false // wether to add metadata to outputs or not : ie version info, timestamp etc
|
|
29
31
|
let inputIsDirectory = false // did we pass in a folder or a file ?
|
|
@@ -41,6 +43,10 @@ const parseArgs = (args) => {
|
|
|
41
43
|
for (let i = 0; i < args.length; i++) {
|
|
42
44
|
if (args[i] === '-of') { // -of <format>
|
|
43
45
|
outputFormat = args[++i]
|
|
46
|
+
} else if (args[i] === '-gp') {
|
|
47
|
+
generateParts = true
|
|
48
|
+
} else if (args[i] === '-z') {
|
|
49
|
+
zip = true
|
|
44
50
|
} else if (args[i].match(/^-o(\S.+)/)) { // -o<output>
|
|
45
51
|
outputFile = args[i]
|
|
46
52
|
outputFile = outputFile.replace(/^-o(\S+)$/, '$1')
|
|
@@ -92,6 +98,8 @@ const parseArgs = (args) => {
|
|
|
92
98
|
inputFormat,
|
|
93
99
|
outputFile,
|
|
94
100
|
outputFormat,
|
|
101
|
+
generateParts,
|
|
102
|
+
zip,
|
|
95
103
|
params,
|
|
96
104
|
addMetaData,
|
|
97
105
|
inputIsDirectory
|