11import fs from 'fs'
22import path from 'path'
33
4+ import { Command , Option } from 'commander'
5+
46import { loading } from '@jscad/core'
57
68import { supportedInputExtensions , supportedOutputExtensions , supportedOutputFormats } from '@jscad/io'
@@ -9,113 +11,105 @@ import { env } from './env.js'
911
1012const { getDesignEntryPoint } = loading
1113
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 ( )
2659 }
2760
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 )
4174 }
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
4383 }
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' )
9095 process . exit ( 1 )
9196 }
97+ filepaths [ 0 ] = filepath // use the entry point for conversion
9298 }
9399 }
94- // exit if a input file was not provided
95- if ( ! inputFile ) process . exit ( 1 )
96100
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+ } )
109108
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
121115}
0 commit comments