Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
214 changes: 151 additions & 63 deletions bin/asar.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -23,72 +23,160 @@
process.exit(1);
}

program.version('v' + packageJSON.version).description('Manipulate asar archive files');

program
.command('pack <dir> <output>')
.alias('p')
.description('create asar archive')
.option('--ordering <file path>', 'path to a text file for ordering contents')
.option('--unpack <expression>', 'do not pack files matching glob <expression>')
.option(
'--unpack-dir <expression>',
'do not pack dirs matching glob <expression> or starting with literal <expression>',
)
.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] <dir> <output>',
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 <expression>' },
'unpack-dir': {
type: 'string',
description:
'do not pack dirs matching glob <expression> or starting with literal <expression>',
},
'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] <archive>',
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) {

Check warning on line 68 in bin/asar.mjs

View workflow job for this annotation

GitHub Actions / Test (22.12.x, ubuntu-22.04)

typescript-eslint(no-for-in-array)

For-in loops over arrays skips holes, returns indices as strings, and may visit the prototype chain or other enumerable properties.

Check warning on line 68 in bin/asar.mjs

View workflow job for this annotation

GitHub Actions / Test (22.12.x, macos-latest)

typescript-eslint(no-for-in-array)

For-in loops over arrays skips holes, returns indices as strings, and may visit the prototype chain or other enumerable properties.

Check warning on line 68 in bin/asar.mjs

View workflow job for this annotation

GitHub Actions / Test (22.12.x, windows-latest)

typescript-eslint(no-for-in-array)

For-in loops over arrays skips holes, returns indices as strings, and may visit the prototype chain or other enumerable properties.
console.log(files[i]);
}
},
},
'extract-file': {
aliases: ['ef'],
usage: 'extract-file|ef <archive> <filename>',
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 <archive> <dest>',
description: 'extract archive',
args: ['archive', 'dest'],
options: {},
action: (positionals) => {
const [archive, dest] = positionals;
extractAll(archive, dest);
},
},
};

program
.command('list <archive>')
.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() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having to re-implement --help is a bit annoying but alas

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 <archive> <filename>')
.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' ? ' <value>' : '';
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 <archive> <dest>')
.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);
});
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
"prepare": "husky"
},
"dependencies": {
"commander": "^13.1.0",
"glob": "^13.0.2",
"minimatch": "^10.0.1"
},
Expand Down
8 changes: 0 additions & 8 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down
Loading