UNPKG

@tracespace/cli

Version:

Render a PCB as SVGs from the command line

143 lines (113 loc) 4.32 kB
'use strict' const fs = require('fs') const path = require('path') const util = require('util') const common = require('common-prefix') const {get} = require('dot-prop') const makeDir = require('make-dir') const debug = require('debug')('@tracespace/cli') const gerberToSvg = require('gerber-to-svg') const pcbStackup = require('pcb-stackup') const whatsThatGerber = require('whats-that-gerber') const yargs = require('yargs') const {description, version} = require('../package.json') const examples = require('./examples') const options = require('./options') const {resolvePatterns} = require('./resolve') const writeFile = util.promisify(fs.writeFile) const stackup = util.promisify(pcbStackup) module.exports = function cli(processArgv, config) { const argv = yargs .usage('$0 [options] <files...>', `${description}\nv${version}`, yargs => { yargs.positional('files', { describe: "Filenames, directories, or globs to a PCB's Gerber/drill files", type: 'string', }) examples.forEach(e => yargs.example(e.cmd, e.desc)) yargs.epilog( `You may also specify options in the current working directory using a config file in (.tracespacerc, .tracespacerc.json, tracespace.config.js, etc.) or a "tracespace" key in package.json` ) }) .config(config) .options(options) .version() .help() .alias({help: 'h', version: 'v'}) .fail((error, message, yargs) => { throw new Error(error) }) .parserConfiguration({'boolean-negation': false}) .parse(processArgv) debug('argv', argv) const info = message => !argv.quiet && console.warn(message) if (config._configFile) info(`Config loaded from ${config._configFile}`) return resolvePatterns(argv.files) .then(renderFiles) .then(writeRenders) function renderFiles(filenames) { const typesByName = whatsThatGerber(filenames) const layers = filenames .map(filename => makeLayerFromFilename(filename, typesByName)) .filter(_ => _) if (!layers.length) throw new Error(`No valid Gerber or drill files found`) return stackup(layers, argv.board) } function makeLayerFromFilename(filename, typesByName) { const basename = path.basename(filename) const {type, side} = getType(basename, typesByName[filename]) if (type == null && !argv.force) { info(`Skipping ${basename} (unable to identify type)`) return null } info(`Rendering ${basename} as ${type} (${side})`) const gerber = fs.createReadStream(filename) const options = getOptions(basename, type) debug(filename, type, side, options) return {type, side, gerber, options, filename: basename} } function getType(basename, defaults) { return { side: get(argv.layer, `${basename}.side`, defaults.side), type: get(argv.layer, `${basename}.type`, defaults.type), } } function getOptions(basename, type) { const defaultOptions = type === 'drill' ? argv.drill : argv.gerber return get(argv.layer, `${basename}.options`, defaultOptions) } function writeRenders(stackup) { const name = inferBoardName(stackup) const ensureDir = argv.out !== options.out.STDOUT ? makeDir(argv.out) : Promise.resolve() return ensureDir.then(() => Promise.all([ !argv.noBoard && writeOutput(`${name}.top.svg`, stackup.top.svg), !argv.noBoard && writeOutput(`${name}.bottom.svg`, stackup.bottom.svg), ...stackup.layers .filter(_ => !argv.noLayer) .map(layer => { let filename = layer.filename if (layer.side) filename += `.${layer.side}` if (layer.type) filename += `.${layer.type}` return writeOutput( `${filename}.svg`, gerberToSvg.render(layer.converter, layer.options.attributes) ) }), ]) ) } function writeOutput(name, contents) { if (argv.out === options.out.STDOUT) return console.log(contents) const filename = path.join(argv.out, name) info(`Writing ${filename}`) return writeFile(filename, contents) } } function inferBoardName(stackup) { const names = stackup.layers.map(ly => path.basename(ly.filename, path.extname(ly.filename)) ) return common(names) || 'board' }