diff --git a/bin/asar.mjs b/bin/asar.mjs index dca335e..26265b6 100755 --- a/bin/asar.mjs +++ b/bin/asar.mjs @@ -2,7 +2,7 @@ import packageJSON from '../package.json' with { type: 'json' }; import { createPackageWithOptions, listPackage, extractFile, extractAll } from '../lib/asar.js'; -import { program } from 'commander'; +import { parseArgs } from 'node:util'; import fs from 'node:fs'; import path from 'node:path'; @@ -23,72 +23,160 @@ if ( process.exit(1); } -program.version('v' + packageJSON.version).description('Manipulate asar archive files'); - -program - .command('pack ') - .alias('p') - .description('create asar archive') - .option('--ordering ', 'path to a text file for ordering contents') - .option('--unpack ', 'do not pack files matching glob ') - .option( - '--unpack-dir ', - 'do not pack dirs matching glob or starting with literal ', - ) - .option('--exclude-hidden', 'exclude hidden files') - .action(function (dir, output, options) { - options = { - unpack: options.unpack, - unpackDir: options.unpackDir, - ordering: options.ordering, - version: options.sv, - arch: options.sa, - builddir: options.sb, - dot: !options.excludeHidden, - }; - createPackageWithOptions(dir, output, options).catch((error) => { - console.error(error); - process.exit(1); - }); - }); +const commands = { + pack: { + aliases: ['p'], + usage: 'pack|p [options] ', + description: 'create asar archive', + args: ['dir', 'output'], + options: { + ordering: { type: 'string', description: 'path to a text file for ordering contents' }, + unpack: { type: 'string', description: 'do not pack files matching glob ' }, + 'unpack-dir': { + type: 'string', + description: + 'do not pack dirs matching glob or starting with literal ', + }, + 'exclude-hidden': { type: 'boolean', description: 'exclude hidden files' }, + }, + action: (positionals, values) => { + const [dir, output] = positionals; + const options = { + unpack: values.unpack, + unpackDir: values['unpack-dir'], + ordering: values.ordering, + dot: !values['exclude-hidden'], + }; + return createPackageWithOptions(dir, output, options); + }, + }, + list: { + aliases: ['l'], + usage: 'list|l [options] ', + description: 'list files of asar archive', + args: ['archive'], + options: { + 'is-pack': { + type: 'boolean', + short: 'i', + description: 'each file in the asar is pack or unpack', + }, + }, + action: (positionals, values) => { + const [archive] = positionals; + const files = listPackage(archive, { isPack: values['is-pack'] }); + for (const i in files) { + console.log(files[i]); + } + }, + }, + 'extract-file': { + aliases: ['ef'], + usage: 'extract-file|ef ', + description: 'extract one file from archive', + args: ['archive', 'filename'], + options: {}, + action: (positionals) => { + const [archive, filename] = positionals; + fs.writeFileSync(path.basename(filename), extractFile(archive, filename)); + }, + }, + extract: { + aliases: ['e'], + usage: 'extract|e ', + description: 'extract archive', + args: ['archive', 'dest'], + options: {}, + action: (positionals) => { + const [archive, dest] = positionals; + extractAll(archive, dest); + }, + }, +}; -program - .command('list ') - .alias('l') - .description('list files of asar archive') - .option('-i, --is-pack', 'each file in the asar is pack or unpack') - .action(function (archive, options) { - options = { - isPack: options.isPack, - }; - const files = listPackage(archive, options); - for (const i in files) { - console.log(files[i]); - } - }); +function printHelp() { + console.log('Usage: asar [options] [command]'); + console.log(); + console.log('Manipulate asar archive files'); + console.log(); + console.log('Options:'); + console.log(' -V, --version output the version number'); + console.log(' -h, --help display help for command'); + console.log(); + console.log('Commands:'); + for (const [name, cmd] of Object.entries(commands)) { + const label = `${name}|${cmd.aliases[0]}`; + console.log(` ${label.padEnd(32)} ${cmd.description}`); + } +} -program - .command('extract-file ') - .alias('ef') - .description('extract one file from archive') - .action(function (archive, filename) { - fs.writeFileSync(path.basename(filename), extractFile(archive, filename)); - }); +function printCommandHelp(cmd) { + console.log(`Usage: asar ${cmd.usage}`); + console.log(); + console.log(cmd.description); + console.log(); + console.log('Options:'); + for (const [opt, spec] of Object.entries(cmd.options)) { + const prefix = spec.short ? `-${spec.short}, ` : ''; + const suffix = spec.type === 'string' ? ' ' : ''; + const label = `${prefix}--${opt}${suffix}`; + console.log(` ${label.padEnd(32)} ${spec.description}`); + } + console.log(` ${'-h, --help'.padEnd(32)} display help for command`); +} -program - .command('extract ') - .alias('e') - .description('extract archive') - .action(function (archive, dest) { - extractAll(archive, dest); - }); +const args = process.argv.slice(2); -program.command('*', { hidden: true }).action(function (_cmd, args) { - console.log("asar: '%s' is not an asar command. See 'asar --help'.", args[0]); -}); +if (args.length === 0 || args[0] === '--help' || args[0] === '-h') { + printHelp(); + process.exit(0); +} + +if (args[0] === '--version' || args[0] === '-V') { + console.log('v' + packageJSON.version); + process.exit(0); +} -program.parse(process.argv); +const commandName = args[0]; +const commandArgs = args.slice(1); -if (program.args.length === 0) { - program.help(); +let command = commands[commandName]; +if (!command) { + command = Object.values(commands).find((cmd) => cmd.aliases.includes(commandName)); } + +if (!command) { + console.log("asar: '%s' is not an asar command. See 'asar --help'.", commandName); + process.exit(1); +} + +let values, positionals; +try { + ({ values, positionals } = parseArgs({ + args: commandArgs, + options: { + ...command.options, + help: { type: 'boolean', short: 'h' }, + }, + allowPositionals: true, + })); +} catch (error) { + console.error(`error: ${error.message}`); + process.exit(1); +} + +if (values.help) { + printCommandHelp(command); + process.exit(0); +} + +if (positionals.length < command.args.length) { + const missing = command.args[positionals.length]; + console.error(`error: missing required argument '${missing}'`); + process.exit(1); +} + +Promise.resolve(command.action(positionals, values)).catch((error) => { + console.error(error); + process.exit(1); +}); diff --git a/package.json b/package.json index 769a206..79f8db4 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,6 @@ "prepare": "husky" }, "dependencies": { - "commander": "^13.1.0", "glob": "^13.0.2", "minimatch": "^10.0.1" }, diff --git a/yarn.lock b/yarn.lock index 4e75d28..1f2b92e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11,7 +11,6 @@ __metadata: dependencies: "@tsconfig/node22": "npm:^22.0.1" "@types/node": "npm:^22.12.0" - commander: "npm:^13.1.0" electron: "npm:^39.8.4" glob: "npm:^13.0.2" husky: "npm:^9.1.7" @@ -1433,13 +1432,6 @@ __metadata: languageName: node linkType: hard -"commander@npm:^13.1.0": - version: 13.1.0 - resolution: "commander@npm:13.1.0" - checksum: 10c0/7b8c5544bba704fbe84b7cab2e043df8586d5c114a4c5b607f83ae5060708940ed0b5bd5838cf8ce27539cde265c1cbd59ce3c8c6b017ed3eec8943e3a415164 - languageName: node - linkType: hard - "commander@npm:^14.0.2": version: 14.0.3 resolution: "commander@npm:14.0.3"