@jscad/cli 3.0.2-alpha.0 → 3.0.3-alpha.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 CHANGED
@@ -3,6 +3,12 @@
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
+ ## [3.0.3-alpha.0](https://github.com/jscad/OpenJSCAD/compare/@jscad/cli@3.0.2-alpha.0...@jscad/cli@3.0.3-alpha.0) (2026-01-18)
7
+
8
+ ### Features
9
+
10
+ * **cli:** reworked command line parameter parsing to use commander ([225b541](https://github.com/jscad/OpenJSCAD/commit/225b5412cb92e0faa50c3c67a50b6dc5c24f424e))
11
+
6
12
  ## [3.0.2-alpha.0](https://github.com/jscad/OpenJSCAD.org/compare/@jscad/cli@3.0.1-alpha.0...@jscad/cli@3.0.2-alpha.0) (2025-09-06)
7
13
 
8
14
  **Note:** Version bump only for package @jscad/cli
package/README.md CHANGED
@@ -40,8 +40,7 @@ npm install -g @jscad/cli
40
40
  ```
41
41
  Once installed, the CLI can be invoked using
42
42
  ```
43
- jscad -v
44
- jscad
43
+ jscad --help
45
44
  ```
46
45
 
47
46
  ### Install the CLI as Part of a Project
@@ -54,45 +53,48 @@ npm install -D @jscad/cli
54
53
 
55
54
  This will add the CLI package as a development dependency. The CLI can be invoked using
56
55
  ```
57
- npx jscad -v
58
- npx jscad
56
+ npx jscad --help
59
57
  ```
60
- These two commands will show the version of the CLI, and a general help message.
58
+ These two commands will show the various options of the CLI, and a general usage example.
61
59
 
62
60
  ### Using the CLI
63
61
 
64
62
  Simply invoke 'jscad' using various options. Here are some examples.
65
63
 
64
+ There is also an exmaple JSCAD design in the 'example' directory.
65
+
66
66
  Examples:
67
67
 
68
68
  ```jscad mydesign.js # -- convert mydesign.js to mydesign.stl as default```
69
69
 
70
- ```jscad mydesign.js -o test.stl # -- convert mydesign.js to test.stl```
70
+ ```jscad -o test.stl mydesign.js # -- convert mydesign.js to test.stl```
71
71
 
72
72
  ```jscad frog.stl -o test.js # -- convert frog.stl to test.js```
73
73
 
74
- ```jscad mydesign.js -of amf # -- convert mydesign.js into mydesign.amf```
74
+ ```jscad -f 3mf mydesign.js # -- convert mydesign.js into 3MF, i.e. mydesign.3mf```
75
75
 
76
76
  For multi-part models, you can pass the `generateParts` flag `-gp` to output each part as a separate, numbered file:
77
77
 
78
- ```jscad mydesign.js -gp # -- convert mydesign.js into mydesign-part-1-of-2.stl and mydesign-part-2-of-2.stl```
78
+ ```jscad -p mydesign.js # -- convert mydesign.js into mydesign-part-1-of-2.stl and mydesign-part-2-of-2.stl```
79
79
 
80
80
  You may also pass the `zip` flag `-z` to zip generated files into one .zip file:
81
81
 
82
- ```jscad mydesign.js -z # -- convert mydesign.js into mydesign.zip which contains: mydesign.stl```
82
+ ```jscad -z mydesign.js # -- convert mydesign.js into mydesign.zip which contains: mydesign.stl```
83
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```
84
+ ```jscad -p -z mydesign.js # -- convert mydesign.js into mydesign.zip which contains: mydesign-part-1-of-2.stl and mydesign-part-2-of-2.stl```
85
85
 
86
86
  The '-o' option can be used to control where the output will be placed.
87
- While, the '-of' option can be used to control the format of the output.
87
+ While, the '-f' option can be used to control the format of the output.
88
+
89
+ You can also provide the parameters to a JSCAD design by passing --<paramName> <value> to the CLI.
88
90
 
89
- You can also provide the parameters to a design by passing --<paramName> <value> to the CLI.
91
+ ```jscad -o ouptput.stl mydesign.js -- --name "Just Me" --title "Geek"```
90
92
 
91
- ```jscad mydesign.js --name "Just Me" --title "Geek" -o output.stl```
93
+ NOTE: Be sure to seperate the JSCAD design parameters with '--'
92
94
 
93
95
  Also, design projects (directories) can be used as the input to the CLI.
94
96
 
95
- ```jscad myproject/ -o ./test.stl # -- convert the project mydesign to test.stl```
97
+ ```jscad -o test.stl example/ # -- convert the project 'example' to test.stl```
96
98
 
97
99
  > Note: The CLI will search for the design entry point just like NPM.
98
100
  > - if there is a package.json file in the project, then try to load the 'main' property
@@ -118,7 +120,7 @@ ln -s node_modules/@jscad/examples ./examples
118
120
  The examples are just single file designs, or multiple file projects.
119
121
  ```
120
122
  npx jscad examples/core/booleans/basicBooleans.js -o ./test.stl
121
- npx jscad examples/module-design/ -of dxf
123
+ npx jscad examples/module-design/ -f dxf
122
124
  ```
123
125
 
124
126
  ## Documentation
@@ -96,7 +96,7 @@ test('cli (conversions STL)', (t) => {
96
96
 
97
97
  t.context.file3Path = file3Path
98
98
 
99
- cmd = `node ${cliPath} ${file2Path} -o ${file3Path} -v -add-metadata false`
99
+ cmd = `node ${cliPath} ${file2Path} -o ${file3Path} -v --add-metadata`
100
100
  execSync(cmd, { stdio: [0, 1, 2] })
101
101
  t.true(fs.existsSync(file3Path))
102
102
  })
@@ -129,7 +129,7 @@ test('cli (conversions DXF)', (t) => {
129
129
 
130
130
  t.context.file3Path = file3Path
131
131
 
132
- cmd = `node ${cliPath} ${file2Path} -of js`
132
+ cmd = `node ${cliPath} ${file2Path} -f js`
133
133
  execSync(cmd, { stdio: [0, 1, 2] })
134
134
  t.true(fs.existsSync(file3Path))
135
135
  })
@@ -162,7 +162,40 @@ test('cli (conversions JSON)', (t) => {
162
162
 
163
163
  t.context.file3Path = file3Path
164
164
 
165
- cmd = `node ${cliPath} ${file2Path} -of js`
165
+ cmd = `node ${cliPath} ${file2Path} -f js`
166
+ execSync(cmd, { stdio: [0, 1, 2] })
167
+ t.true(fs.existsSync(file3Path))
168
+ })
169
+
170
+ test('cli (conversions OBJ)', (t) => {
171
+ const testID = 17
172
+
173
+ // convert from JSCAD to OBJ
174
+ const file1Path = createJscad(testID)
175
+ t.true(fs.existsSync(file1Path))
176
+
177
+ t.context.file1Path = file1Path
178
+
179
+ const file2Name = `./test${testID}.obj`
180
+ const file2Path = path.resolve(cwd(), file2Name)
181
+ t.false(fs.existsSync(file2Path))
182
+
183
+ t.context.file2Path = file2Path
184
+
185
+ const cliPath = t.context.cliPath
186
+
187
+ let cmd = `node ${cliPath} ${file1Path} -o ${file2Path}`
188
+ execSync(cmd, { stdio: [0, 1, 2] })
189
+ t.true(fs.existsSync(file2Path))
190
+
191
+ // convert from OBJ to JS
192
+ const file3Name = `./test${testID}.js`
193
+ const file3Path = path.resolve(cwd(), file3Name)
194
+ t.false(fs.existsSync(file3Path))
195
+
196
+ t.context.file3Path = file3Path
197
+
198
+ cmd = `node ${cliPath} ${file2Path} -f js`
166
199
  execSync(cmd, { stdio: [0, 1, 2] })
167
200
  t.true(fs.existsSync(file3Path))
168
201
  })
@@ -195,7 +228,7 @@ test('cli (conversions SVG)', (t) => {
195
228
 
196
229
  t.context.file3Path = file3Path
197
230
 
198
- cmd = `node ${cliPath} ${file2Path} -of js`
231
+ cmd = `node ${cliPath} ${file2Path} -f js`
199
232
  execSync(cmd, { stdio: [0, 1, 2] })
200
233
  t.true(fs.existsSync(file3Path))
201
234
  })
@@ -228,7 +261,40 @@ test('cli (conversions X3D)', (t) => {
228
261
 
229
262
  t.context.file3Path = file3Path
230
263
 
231
- cmd = `node ${cliPath} ${file2Path} -of js`
264
+ cmd = `node ${cliPath} ${file2Path} -f js`
265
+ execSync(cmd, { stdio: [0, 1, 2] })
266
+ t.true(fs.existsSync(file3Path))
267
+ })
268
+
269
+ test('cli (conversions 3MF)', (t) => {
270
+ const testID = 16
271
+
272
+ // convert from JSCAD to 3MF
273
+ const file1Path = createJscad(testID)
274
+ t.true(fs.existsSync(file1Path))
275
+
276
+ t.context.file1Path = file1Path
277
+
278
+ const file2Name = `./test${testID}.3mf`
279
+ const file2Path = path.resolve(cwd(), file2Name)
280
+ t.false(fs.existsSync(file2Path))
281
+
282
+ t.context.file2Path = file2Path
283
+
284
+ const cliPath = t.context.cliPath
285
+
286
+ let cmd = `node ${cliPath} ${file1Path} -o ${file2Path}`
287
+ execSync(cmd, { stdio: [0, 1, 2] })
288
+ t.true(fs.existsSync(file2Path))
289
+
290
+ // convert from 3MF to JS
291
+ const file3Name = `./test${testID}.js`
292
+ const file3Path = path.resolve(cwd(), file3Name)
293
+ t.false(fs.existsSync(file3Path))
294
+
295
+ t.context.file3Path = file3Path
296
+
297
+ cmd = `node ${cliPath} ${file2Path} -f js`
232
298
  execSync(cmd, { stdio: [0, 1, 2] })
233
299
  t.true(fs.existsSync(file3Path))
234
300
  })
package/cli.js CHANGED
@@ -1,46 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
  // --log_all
3
3
 
4
- // NOTE: this will only run on Node > 6 or needs to be transpiled
5
-
6
- // == JSCAD CLI interface, written by Rene K. Mueller <spiritdude@gmail.com>, Licensed under MIT License
7
- //
8
- // Description:
9
- // jscad <file> [-of <format>] [-o <output>]
10
- // e.g.
11
- // jscad test.jscad
12
- // jscad test.jscad -o test.stl
13
- // jscad test.jscad -o test.amf
14
- // jscad test.jscad -o test.dxf
15
- // jscad test.scad -o testFromSCAD.jscad
16
- // jscad test.scad -o test.stl
17
- // jscad test.stl -o test2.stl # reprocessed: stl -> jscad -> stl
18
- // jscad test.amf -o test2.jscad
19
- // jscad test.jscad -of amf
20
- // jscad test.jscad -of dxf
21
- // jscad test.jscad -of stl
22
- // jscad name_plate.jscad --name "Just Me" --title "CEO" -o amf test.amf
23
- //
4
+ /*
5
+ * Command Line Interface (CLI) for converting JSCAD designs to differnt external formats.
6
+ *
7
+ * Example:
8
+ * node ./cli.js --help
9
+ */
24
10
  import fs from 'fs'
11
+ import path from 'path'
12
+
25
13
  import JSZip from 'jszip'
26
14
 
27
15
  import { supportedFormats } from '@jscad/io'
28
16
 
17
+ import { parseArgs } from './src/parseArgs.js'
29
18
  import { generateOutputData } from './src/generateOutputData.js'
30
- import { determineOutputNameAndFormat } from './src/determineOutputNameAndFormat.js'
31
19
  import { writeOutput } from './src/writeOutput.js'
32
- import { parseArgs } from './src/parseArgs.js'
33
-
34
- const version = '[VI]{version}[/VI]' // version is injected by rollup
35
-
36
- // handle arguments (inputs, outputs, etc)
37
- const args = process.argv.splice(2)
38
- let { inputFile, inputFormat, outputFile, outputFormat, generateParts, zip, params, addMetaData, inputIsDirectory } = parseArgs(args)
39
-
40
- // outputs
41
- const output = determineOutputNameAndFormat(outputFormat, outputFile, inputFile)
42
- outputFormat = output.outputFormat
43
- outputFile = output.outputFile
44
20
 
45
21
  const clicolors = {
46
22
  red: '\u{1b}[31m',
@@ -50,10 +26,30 @@ const clicolors = {
50
26
  black: '\u{1b}[0m'
51
27
  }
52
28
 
53
- const logFileOutput = (outputFile) => {
29
+ const version = '[VI]{version}[/VI]' // version is injected by rollup
30
+
31
+ // handle arguments (inputs, outputs, etc)
32
+ let { filepaths, outputFile, outputFormat, generateParts, zip, params, addMetaData, inputIsDirectory } = parseArgs()
33
+
34
+ // FIXME handle N input files
35
+ const inputFile = filepaths[0]
36
+ const inputFormat = path.extname(inputFile).substring(1)
37
+
38
+ // outputs
39
+ if (!outputFile) {
40
+ // create a base name from the input file
41
+ const fileElements = path.parse(inputFile)
42
+ fileElements.ext = '.' + outputFormat
43
+ if (fileElements.ext === '.stla') fileElements.ext = '.stl'
44
+ if (fileElements.ext === '.stlb') fileElements.ext = '.stl'
45
+ fileElements.base = undefined
46
+ outputFile = path.format(fileElements)
47
+ }
48
+
49
+ const logFileOutput = (inputPath, outputPath) => {
54
50
  console.log(`${clicolors.blue}JSCAD: generating output ${clicolors.red}
55
- from: ${clicolors.green} ${inputFile} ${clicolors.red}
56
- to: ${clicolors.green} ${outputFile} ${clicolors.yellow}(${supportedFormats[outputFormat].description}) ${clicolors.black}
51
+ from: ${clicolors.green} ${inputPath} ${clicolors.red}
52
+ to: ${clicolors.green} ${outputPath} ${clicolors.yellow}(${supportedFormats[outputFormat].description}) ${clicolors.black}
57
53
  `)
58
54
  }
59
55
 
@@ -81,14 +77,14 @@ generateOutputData(src, params, { outputFile, outputFormat, inputFile, inputForm
81
77
  if (err) {
82
78
  console.error(err)
83
79
  } else {
84
- logFileOutput(zipFilename)
80
+ logFileOutput(inputFile, zipFilename)
85
81
  }
86
82
  })
87
83
  })
88
84
  } else {
89
85
  for (let i = 0; i < outputData.length; i++) {
90
86
  const filename = outputFile.replace(/\.(\w+)$/, `-part-${i + 1}-of-${outputData.length}.$1`)
91
- logFileOutput(filename)
87
+ logFileOutput(inputFile, filename)
92
88
  writeOutput(filename, outputData[i])
93
89
  }
94
90
  }
@@ -102,12 +98,12 @@ generateOutputData(src, params, { outputFile, outputFormat, inputFile, inputForm
102
98
  if (err) {
103
99
  console.error(err)
104
100
  } else {
105
- logFileOutput(zipFilename)
101
+ logFileOutput(inputFile, zipFilename)
106
102
  }
107
103
  })
108
104
  })
109
105
  } else {
110
- logFileOutput(outputFile)
106
+ logFileOutput(inputFile, outputFile)
111
107
  writeOutput(outputFile, outputData)
112
108
  }
113
109
  }
@@ -111,7 +111,7 @@ test('cli (single input file, output format)', (t) => {
111
111
 
112
112
  const cliPath = t.context.cliPath
113
113
 
114
- const cmd = `node ${cliPath} ${inputPath} -of dxf`
114
+ const cmd = `node ${cliPath} ${inputPath} -f dxf`
115
115
  execSync(cmd, { stdio: [0, 1, 2] })
116
116
  t.true(fs.existsSync(outputPath))
117
117
  })
@@ -167,7 +167,7 @@ test('cli (folder, output format)', (t) => {
167
167
 
168
168
  const cliPath = t.context.cliPath
169
169
 
170
- const cmd = `node ${cliPath} ${folderPath} -of dxf`
170
+ const cmd = `node ${cliPath} ${folderPath} -f dxf`
171
171
  execSync(cmd, { stdio: [0, 1, 2] })
172
172
  t.true(fs.existsSync(outputPath))
173
173
  })
@@ -188,7 +188,7 @@ test('cli (single input file, parameters)', (t) => {
188
188
 
189
189
  const cliPath = t.context.cliPath
190
190
 
191
- const cmd = `node ${cliPath} ${inputPath} --segments 32 --nothing "Yes"`
191
+ const cmd = `node ${cliPath} ${inputPath} -- --segments 32 --nothing "Yes"`
192
192
  execSync(cmd, { stdio: [0, 1, 2] })
193
193
  t.true(fs.existsSync(outputPath))
194
194
  })
@@ -241,7 +241,7 @@ test('cli (single input file, multiple output files)', (t) => {
241
241
 
242
242
  const cliPath = t.context.cliPath
243
243
 
244
- const cmd = `node ${cliPath} ${inputPath} -gp`
244
+ const cmd = `node ${cliPath} -p ${inputPath}`
245
245
  execSync(cmd, { stdio: [0, 1, 2] })
246
246
  t.true(fs.existsSync(outputPath1))
247
247
  t.true(fs.existsSync(outputPath2))
@@ -265,7 +265,7 @@ test('cli (single multipart input file, zipped output file)', async (t) => {
265
265
 
266
266
  const cliPath = t.context.cliPath
267
267
 
268
- const cmd = `node ${cliPath} ${inputPath} -gp -z`
268
+ const cmd = `node ${cliPath} ${inputPath} -p -z`
269
269
  execSync(cmd, { stdio: [0, 1, 2] })
270
270
  t.true(fs.existsSync(outputPath))
271
271
 
@@ -0,0 +1,23 @@
1
+ // example JSCAD script
2
+ import { flatten } from '@jscad/array-utils'
3
+ import { arc, ellipse } from '@jscad/modeling'
4
+
5
+ import { createPart } from './part.js'
6
+
7
+ export const getParameterDefinitions = () => {
8
+ return flatten([
9
+ { name: 'segments', caption: 'Segments:', type: 'int', initial: 10, min: 5, max: 20, step: 1 }
10
+ ])
11
+ }
12
+
13
+ export const main = (params) => {
14
+ // parameters
15
+ const segments = params.segments || 16
16
+
17
+ // shapes
18
+ const apath2 = arc({ segments })
19
+ const ageom2 = ellipse({ segments, center: [5, 5] })
20
+ const ageom3 = createPart(segments)
21
+
22
+ return [apath2, ageom2, ageom3]
23
+ }
@@ -0,0 +1,7 @@
1
+ import { ellipsoid } from '@jscad/modeling'
2
+
3
+ export const createPart = (segments) => {
4
+ const center = [10, 10, 10]
5
+
6
+ return ellipsoid({ segments, center })
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jscad/cli",
3
- "version": "3.0.2-alpha.0",
3
+ "version": "3.0.3-alpha.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",
@@ -37,10 +37,11 @@
37
37
  "license": "MIT",
38
38
  "dependencies": {
39
39
  "@jscad/array-utils": "3.0.1-alpha.0",
40
- "@jscad/core": "3.0.2-alpha.0",
41
- "@jscad/io": "3.0.2-alpha.0",
42
- "@jscad/io-utils": "3.0.2-alpha.0",
43
- "@jscad/modeling": "3.0.2-alpha.0",
40
+ "@jscad/core": "3.0.3-alpha.0",
41
+ "@jscad/io": "3.0.3-alpha.0",
42
+ "@jscad/io-utils": "3.0.3-alpha.0",
43
+ "@jscad/modeling": "3.0.3-alpha.0",
44
+ "commander": "^14.0.0",
44
45
  "jszip": "^3.10.1"
45
46
  },
46
47
  "devDependencies": {
@@ -52,5 +53,5 @@
52
53
  "url": "https://opencollective.com/openjscad",
53
54
  "logo": "https://opencollective.com/openjscad/logo.txt"
54
55
  },
55
- "gitHead": "ac80d1a7cb0a8efb21aff9193d4a8bccb6e31f1c"
56
+ "gitHead": "b0a83db054ec02ccddd9e19e51f86bf30f72a69e"
56
57
  }
package/src/env.js CHANGED
@@ -1,9 +1,12 @@
1
1
  import os from 'os'
2
+ import process from 'process'
2
3
 
3
4
  const version = '[VI]{version}[/VI]' // version is injected by rollup
4
5
 
5
6
  export const env = () => {
6
- let env = 'JSCAD ' + version
7
+ const nodeVersion = process.version
8
+
9
+ let env = 'JSCAD CLI ' + version + ', Node.js ' + nodeVersion
7
10
  if (typeof document !== 'undefined') {
8
11
  const w = document.defaultView
9
12
  env = env + ' [' + w.navigator.userAgent + ']'
package/src/parseArgs.js CHANGED
@@ -1,121 +1,115 @@
1
1
  import fs from 'fs'
2
2
  import path from 'path'
3
3
 
4
+ import { Command, Option } from 'commander'
5
+
4
6
  import { loading } from '@jscad/core'
5
7
 
6
- import { supportedInputExtensions, supportedOutputExtensions, supportedOutputFormats } from '@jscad/io'
8
+ import { supportedInputExtensions, supportedOutputFormats } from '@jscad/io'
7
9
 
8
10
  import { env } from './env.js'
9
11
 
10
12
  const { getDesignEntryPoint } = loading
11
13
 
12
- export const parseArgs = (args) => {
13
- const inputExtensions = supportedInputExtensions()
14
- const outputExtensions = supportedOutputExtensions()
15
- const outputFormats = supportedOutputFormats()
16
-
17
- // hint: https://github.com/substack/node-optimist
18
- // https://github.com/visionmedia/commander.js
19
- if (args.length < 1) {
20
- console.log('USAGE:\n\njscad [-v]\n\n')
21
- console.log('jscad [-gp] [-z] <file> [-of <format>] [-o <output>]')
22
- console.log(`\t<file> :\tinput (Supported types: folder, .${inputExtensions.join(', .')})`)
23
- console.log(`\t<output>:\toutput (Supported types: folder, .${outputExtensions.join(', .')})`)
24
- console.log(`\t<format>:\t${outputFormats.join(', ')}`)
25
- process.exit(1)
14
+ const outputFormats = supportedOutputFormats()
15
+ const inputExtensions = supportedInputExtensions()
16
+
17
+ const isValidInputFileFormat = (input) => {
18
+ if (input === undefined || input === null || !(typeof input === 'string')) {
19
+ return false
20
+ }
21
+ return inputExtensions.reduce((acc, format) => input.toLowerCase().endsWith('.' + format) || acc, false)
22
+ }
23
+
24
+ export const parseArgs = () => {
25
+ const filepaths = [] // list of input file paths
26
+ const parameters = [] // parameter values for main(parameters) and serialize(parameters)
27
+
28
+ /*
29
+ * Setup command line arguments
30
+ */
31
+ const program = new Command()
32
+ program.name('cli.js')
33
+ program.usage('[options] <files...> -- parameter values')
34
+ program.argument('[files...]')
35
+ program.addOption(new Option('-f, --output-format <format>', 'output format').choices(outputFormats).default('stla'))
36
+ program.option('-p, --generate-parts', 'generate unique parts from the files', false)
37
+ program.option('-z, --zip', 'zip the output file contents', false)
38
+ program.option('-o, --output-file <filepath>', 'output file name (optional)')
39
+ program.option('-m, --add-metadata', 'add metadata to output format', false)
40
+ program.option('-v, --version', 'show version and environment information', false)
41
+ program.action((args) => {
42
+ // handle the provided arguments
43
+ args.forEach((arg) => {
44
+ try {
45
+ fs.statSync(arg)
46
+ filepaths.push(arg)
47
+ } catch (e) {
48
+ parameters.push(arg)
49
+ }
50
+ })
51
+ })
52
+ program.parse()
53
+
54
+ const options = program.opts()
55
+
56
+ // show the runtime environment if requested
57
+ if (options.version) {
58
+ env()
26
59
  }
27
60
 
28
- let inputFile
29
- let inputFormat
30
- let outputFile
31
- let outputFormat
32
- let generateParts = false
33
- let zip = false
34
- const params = {} // parameters to feed the script if applicable
35
- let addMetaData = false // wether to add metadata to outputs or not : ie version info, timestamp etc
36
- let inputIsDirectory = false // did we pass in a folder or a file ?
37
-
38
- const isValidInputFileFormat = (input) => {
39
- if (input === undefined || input === null || !(typeof input === 'string')) {
40
- return false
61
+ // console.log(options)
62
+ // console.log(filepaths)
63
+ // console.log(parameters)
64
+
65
+ if (filepaths.length === 0) process.exit(1)
66
+
67
+ if (options.outputFile) {
68
+ // check that the output file name implies a valid output format
69
+ let outputFormat = path.extname(options.outputFile).substring(1)
70
+ if (outputFormat === 'stl') outputFormat = 'stla'
71
+ if (!outputFormats.includes(outputFormat)) {
72
+ console.log('ERROR: invalid output file format <' + outputFormat + '>')
73
+ process.exit(1)
41
74
  }
42
- return inputExtensions.reduce((acc, format) => input.toLowerCase().endsWith('.' + format) || acc, false)
75
+ // check for toxic combinations
76
+ if (filepaths.length !== 1) {
77
+ console.log('ERROR: multiple inputs cannot be converted to a single output file.')
78
+ process.exit(1)
79
+ }
80
+ options.outputFormat = outputFormat
81
+ } else {
82
+ options.outputFile = undefined
43
83
  }
44
- const getFileExtensionFromString = (input) => (input.substring(input.lastIndexOf('.') + 1)).toLowerCase()
45
-
46
- const parseBool = (input) => input.toLowerCase() === 'true'
47
-
48
- for (let i = 0; i < args.length; i++) {
49
- if (args[i] === '-of') { // -of <format>
50
- outputFormat = args[++i]
51
- } else if (args[i] === '-gp') {
52
- generateParts = true
53
- } else if (args[i] === '-z') {
54
- zip = true
55
- } else if (args[i].match(/^-o(\S.+)/)) { // -o<output>
56
- outputFile = args[i]
57
- outputFile = outputFile.replace(/^-o(\S+)$/, '$1')
58
- } else if (args[i] === '-o') { // -o <output>
59
- outputFile = args[++i]
60
- } else if (args[i] === '-add-metadata') { // -metadata true/false
61
- addMetaData = parseBool(args[++i])
62
- } else if (args[i].match(/^--(\w+)=(.*)/)) { // params for main()
63
- params[RegExp.$1] = RegExp.$2
64
- } else if (args[i].match(/^--(\w+)$/)) { // params for main()
65
- params[RegExp.$1] = args[++i]
66
- } else if (isValidInputFileFormat(args[i])) {
67
- inputFile = args[i]
68
- inputFormat = getFileExtensionFromString(args[i])
69
- if (!fs.statSync(inputFile).isFile()) {
70
- console.log('ERROR: cannot open input file/directory <' + inputFile + '>')
71
- process.exit(1)
72
- }
73
- } else if (args[i].match(/^-v$/)) { // show the version and the environment information
74
- env()
75
- } else {
76
- inputFile = args[i]
77
- if (fs.statSync(inputFile).isDirectory()) {
78
- inputIsDirectory = true
79
- // get actual design entry point if applicable (if passed a folder as input etc)
80
- inputFile = getDesignEntryPoint(fs, inputFile)
81
- if (!inputFile) {
82
- console.log('ERROR: could not determine entry point of project.')
83
- console.log('Verify main or index exists')
84
- process.exit(1)
85
- }
86
- inputFormat = path.extname(inputFile).substring(1)
87
- } else {
88
- console.log('ERROR: invalid file name or argument <' + args[i] + '>')
89
- console.log("Type 'jscad' for a list of supported types")
84
+
85
+ // check for use of a directory, and determine the design entry point
86
+ options.inputIsDirectory = false
87
+ if (filepaths.length === 1) {
88
+ if (fs.statSync(filepaths[0]).isDirectory()) {
89
+ options.inputIsDirectory = true
90
+ // get actual design entry point
91
+ const filepath = getDesignEntryPoint(fs, filepaths[0])
92
+ if (!filepath) {
93
+ console.log('ERROR: could not determine entry point of project <' + filepaths[0] + '>')
94
+ console.log('Verify project main or index exists')
90
95
  process.exit(1)
91
96
  }
97
+ filepaths[0] = filepath // use the entry point for conversion
92
98
  }
93
99
  }
94
- // exit if a input file was not provided
95
- if (!inputFile) process.exit(1)
96
100
 
97
- if (!outputFormat && !outputFile) {
98
- outputFormat = 'stla'
99
- }
100
- if (!outputFormat && outputFile) {
101
- outputFormat = path.extname(outputFile).substring(1)
102
- if (outputFormat === 'stl') outputFormat = 'stla'
103
- }
104
- if (!outputFormats.includes(outputFormat)) {
105
- console.log('ERROR: invalid output format <' + outputFormat + '>')
106
- console.log("Type 'jscad' for a list of supported types")
107
- process.exit(1)
108
- }
101
+ // check that all input files are valid formats for conversion
102
+ filepaths.forEach((filepath) => {
103
+ if (!isValidInputFileFormat(filepath)) {
104
+ console.log('ERROR: invalid input file format <' + filepath + '>')
105
+ process.exit(1)
106
+ }
107
+ })
109
108
 
110
- return {
111
- inputFile,
112
- inputFormat,
113
- outputFile,
114
- outputFormat,
115
- generateParts,
116
- zip,
117
- params,
118
- addMetaData,
119
- inputIsDirectory
120
- }
109
+ options.filepaths = filepaths
110
+ options.params = parameters
111
+
112
+ // console.log('RETURN',options)
113
+
114
+ return options
121
115
  }
package/888.js DELETED
@@ -1,54 +0,0 @@
1
- //
2
- // producer: JSCAD SVG Deserializer [VI]{version}[/VI]
3
- // date: Sun Aug 24 2025 06:25:00 GMT+0900 (Japan Standard Time)
4
- // source: 888.svg
5
- //
6
- const { colors, geometries, primitives, transforms } = require('@jscad/modeling')
7
-
8
- function main(params) {
9
- let levels = {}
10
- let paths = {}
11
- let parts
12
- levels.l0 = []
13
- parts = [] // line 2,557
14
- paths.p01 = path2.fromPoints({}, [[1.515533214,-1.8626665199999997]])
15
- paths.p01 = path2.appendPoints([[1.515533214,-1.8852442959999998]], paths.p01)
16
- paths.p01 = path2.appendBezier({segments: 32, controlPoints: [[1.515533214,-2.1298368693333334], [1.464733218,-2.317985002666666], [1.363133226,-2.4496886959999995]]}, paths.p01)
17
- paths.p01 = path2.appendBezier({segments: 32, controlPoints: [[1.2615332339999998,-2.581392389333333], [1.1194813933333332,-2.647244236], [0.9369777039999999,-2.647244236]]}, paths.p01)
18
- paths.p01 = path2.appendPoints([[0.9369777039999999,-2.647244236]], paths.p01)
19
- paths.p01 = path2.appendBezier({segments: 32, controlPoints: [[0.7845777159999998,-2.647244236], [0.6622814293333332,-2.5870368333333333], [0.5700888439999999,-2.4666220279999997]]}, paths.p01)
20
- paths.p01 = path2.appendPoints([[0.5700888439999999,-2.4666220279999997]], paths.p01)
21
- paths.p01 = path2.appendPoints([[0.55033329,-2.6190220159999997]], paths.p01)
22
- paths.p01 = path2.appendPoints([[0.13264443399999998,-2.6190220159999997]], paths.p01)
23
- paths.p01 = path2.appendPoints([[0.13264443399999998,-0.502355516]], paths.p01)
24
- paths.p01 = path2.appendPoints([[0.598311064,-0.502355516]], paths.p01)
25
- paths.p01 = path2.appendPoints([[0.598311064,-1.2474221239999999]], paths.p01)
26
- paths.p01 = path2.appendBezier({segments: 32, controlPoints: [[0.6848592053333333,-1.1495850946666666], [0.7968073446666666,-1.10066658], [0.9341554819999999,-1.10066658]]}, paths.p01)
27
- paths.p01 = path2.appendPoints([[0.9341554819999999,-1.10066658]], paths.p01)
28
- paths.p01 = path2.appendBezier({segments: 32, controlPoints: [[1.1185406526666666,-1.10066658], [1.2615332339999998,-1.1665184266666664], [1.363133226,-1.2982221199999997]]}, paths.p01)
29
- paths.p01 = path2.appendBezier({segments: 32, controlPoints: [[1.464733218,-1.4318072946666665], [1.515533214,-1.619955428], [1.515533214,-1.8626665199999997]]}, paths.p01)
30
- paths.p01 = path2.appendPoints([[1.515533214,-1.8626665199999997]], paths.p01)
31
- paths.p01 = path2.close(paths.p01)
32
- parts.push(paths.p01)
33
- paths.p02 = path2.fromPoints({}, [[1.0498665839999999,-1.8570220759999998]])
34
- paths.p02 = path2.appendPoints([[1.0498665839999999,-1.8570220759999998]], paths.p02)
35
- paths.p02 = path2.appendBezier({segments: 32, controlPoints: [[1.0498665839999999,-1.7140294946666665], [1.0301110299999998,-1.6114887619999998], [0.9905999219999998,-1.549399878]]}, paths.p02)
36
- paths.p02 = path2.appendBezier({segments: 32, controlPoints: [[0.9529702953333332,-1.4891924753333332], [0.8946443739999999,-1.4590887739999998], [0.8156221579999999,-1.4590887739999998]]}, paths.p02)
37
- paths.p02 = path2.appendPoints([[0.8156221579999999,-1.4590887739999998]], paths.p02)
38
- paths.p02 = path2.appendBezier({segments: 32, controlPoints: [[0.7102592033333333,-1.4590887739999998], [0.6378221719999999,-1.4985998819999997], [0.598311064,-1.5776220979999998]]}, paths.p02)
39
- paths.p02 = path2.appendPoints([[0.598311064,-1.5776220979999998]], paths.p02)
40
- paths.p02 = path2.appendPoints([[0.598311064,-2.1702887179999997]], paths.p02)
41
- paths.p02 = path2.appendBezier({segments: 32, controlPoints: [[0.637822172,-2.2493109339999995], [0.711199944,-2.2888220419999996], [0.8184443799999999,-2.2888220419999996]]}, paths.p02)
42
- paths.p02 = path2.appendPoints([[0.8184443799999999,-2.2888220419999996]], paths.p02)
43
- paths.p02 = path2.appendBezier({segments: 32, controlPoints: [[0.9275702973333332,-2.2888220419999996], [0.9971851066666665,-2.235199824], [1.027288808,-2.1279553879999997]]}, paths.p02)
44
- paths.p02 = path2.appendPoints([[1.027288808,-2.1279553879999997]], paths.p02)
45
- paths.p02 = path2.appendBezier({segments: 32, controlPoints: [[1.0423406586666666,-2.0771553919999994], [1.0498665839999999,-1.9868442879999995], [1.0498665839999999,-1.8570220759999998]]}, paths.p02)
46
- paths.p02 = path2.close(paths.p02)
47
- parts.push(paths.p02)
48
- paths.p0 = parts
49
- levels.l0 = levels.l0.concat(paths.p0)
50
-
51
- return levels.l0
52
- }
53
-
54
- module.exports = { main }
package/888.svg DELETED
@@ -1 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="5.6298828125" height="11.71875"><path d="M5.37 6.60L5.37 6.68Q5.37 7.98 4.83 8.68Q4.29 9.38 3.32 9.38L3.32 9.38Q2.51 9.38 2.02 8.74L2.02 8.74L1.95 9.28L0.47 9.28L0.47 1.78L2.12 1.78L2.12 4.42Q2.58 3.90 3.31 3.90L3.31 3.90Q4.29 3.90 4.83 4.60Q5.37 5.31 5.37 6.60L5.37 6.60ZM3.72 6.58L3.72 6.58Q3.72 5.82 3.51 5.49Q3.31 5.17 2.89 5.17L2.89 5.17Q2.33 5.17 2.12 5.59L2.12 5.59L2.12 7.69Q2.33 8.11 2.90 8.11L2.90 8.11Q3.48 8.11 3.64 7.54L3.64 7.54Q3.72 7.27 3.72 6.58Z"/></svg>
package/a.svg DELETED
@@ -1,3 +0,0 @@
1
- <svg>
2
- <path d="m0 0v16h16v-16h-16zm4 4h8v8h-8v-8z" fill="#81e"/>
3
- </svg>
package/b.svg DELETED
@@ -1,7 +0,0 @@
1
- <svg>
2
- <path
3
- style="color:#000000;fill:#000000;-inkscape-stroke:none"
4
- d="m 30,24.390625 c -9.148585,0 -17.605395,4.881781 -22.1796875,12.804687 -4.5742924,7.922907 -4.5742924,17.686469 0,25.609375 C 12.394605,70.727594 20.851415,75.609375 30,75.609375 H 70 C 84.136772,75.609374 95.609375,64.136772 95.609375,50 95.609375,35.863227 84.136773,24.390625 70,24.390625 Z m 0,1.21875 H 70 C 83.477437,25.609375 94.390625,36.522563 94.390625,50 94.390625,63.477436 83.477436,74.390624 70,74.390625 H 30 c -8.714677,0 -16.765708,-4.648181 -21.1230469,-12.195313 -4.3573385,-7.547131 -4.3573385,-16.843493 0,-24.390625 C 13.234292,30.257556 21.285323,25.609375 30,25.609375 Z"
5
- id="path880"
6
- />
7
- </svg>
package/test.js DELETED
@@ -1,34 +0,0 @@
1
- import { extrudeLinear } from '@jscad/modeling'
2
- import { deserializers } from '@jscad/io'
3
-
4
- export const main = () => {
5
- let svg1087 = `
6
- <svg>
7
- <path
8
- style="color:#000000;fill:#000000;-inkscape-stroke:none"
9
- d="m 30,24.390625 c -9.148585,0 -17.605395,4.881781 -22.1796875,12.804687 -4.5742924,7.922907 -4.5742924,17.686469 0,25.609375 C 12.394605,70.727594 20.851415,75.609375 30,75.609375 H 70 C 84.136772,75.609374 95.609375,64.136772 95.609375,50 95.609375,35.863227 84.136773,24.390625 70,24.390625 Z m 0,1.21875 H 70 C 83.477437,25.609375 94.390625,36.522563 94.390625,50 94.390625,63.477436 83.477436,74.390624 70,74.390625 H 30 c -8.714677,0 -16.765708,-4.648181 -21.1230469,-12.195313 -4.3573385,-7.547131 -4.3573385,-16.843493 0,-24.390625 C 13.234292,30.257556 21.285323,25.609375 30,25.609375 Z"
10
- id="path880"
11
- />
12
- </svg>
13
- `
14
- let svg888 = `
15
- <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="5.6298828125" height="11.71875"><path d="M5.37 6.60L5.37 6.68Q5.37 7.98 4.83 8.68Q4.29 9.38 3.32 9.38L3.32 9.38Q2.51 9.38 2.02 8.74L2.02 8.74L1.95 9.28L0.47 9.28L0.47 1.78L2.12 1.78L2.12 4.42Q2.58 3.90 3.31 3.90L3.31 3.90Q4.29 3.90 4.83 4.60Q5.37 5.31 5.37 6.60L5.37 6.60ZM3.72 6.58L3.72 6.58Q3.72 5.82 3.51 5.49Q3.31 5.17 2.89 5.17L2.89 5.17Q2.33 5.17 2.12 5.59L2.12 5.59L2.12 7.69Q2.33 8.11 2.90 8.11L2.90 8.11Q3.48 8.11 3.64 7.54L3.64 7.54Q3.72 7.27 3.72 6.58Z"/></svg>
16
- `
17
- let sTEST = `
18
- <svg>
19
- <path d="m3 9h6v-6h-6v6zm1-5h4v4h-4v-4zm-4 8h12v-12h-12v12zm1-11h10v10h-10v-10" fill="#000"/>
20
- </svg>
21
- `
22
- let s_text = `
23
- <svg>
24
- <path id="Triangle_stroke_MLZ" fill="none" stroke="#000000" d=" M 62.00000 56.00000 L 113.96152 146.00000 L 10.03848 146.00000 L 62.00000 56.00000 Z M 62.00000 71.00000 L 100.97114 138.50000 L 23.02886 138.50000 L 62.00000 71.00000 Z "/>
25
- </svg>
26
- `
27
-
28
- let ds = deserializers['image/svg+xml']
29
- console.log(ds)
30
- let s = ds({ output: "geometry", target: "geom2"}, s_text)
31
- s = extrudeLinear({ height: 10 }, s)
32
- return s
33
- }
34
-
package/test.stl DELETED
Binary file