UNPKG

to-userscript

Version:

Converts simple browser extensions to userscripts

214 lines (209 loc) 6.93 kB
#!/usr/bin/env node const yargs = require("yargs/yargs"); const { hideBin } = require("yargs/helpers"); const path = require("path"); const chalk = require("chalk"); const workflow = require("./workflow"); async function main() { try { await yargs(hideBin(process.argv)) .scriptName("to-userscript") .usage("$0 <command> [options]") .command( "convert <source>", "Convert an extension to userscript", (yargs) => { return yargs .positional("source", { describe: "Extension source (URL, archive file, or directory)", type: "string", demandOption: true, }) .option("output", { alias: "o", describe: "Output .user.js file path", type: "string", }) .option("minify", { describe: "Minify the JavaScript output using terser", type: "boolean", default: false, }) .option("beautify", { describe: "Beautify the JavaScript output using prettier", type: "boolean", default: false, }) .option("target", { alias: "t", describe: "Build target type", choices: ["userscript", "vanilla"], default: "userscript", }) .option("locale", { alias: "l", describe: "Preferred locale for extension name/description (e.g., 'en', 'fr', 'de')", type: "string", }) .option("ignore-assets", { describe: "Asset file extensions to ignore during inlining (comma-separated, e.g., 'mp4,webm,ttf')", type: "string", }) .option("force", { alias: "f", describe: "Overwrite output file if it exists", type: "boolean", default: false, }) .option("keep-temp", { describe: "Keep temporary files for debugging", type: "boolean", default: false, }) .option("temp-dir", { describe: "Custom temporary directory path", type: "string", }); }, async (argv) => { try { await workflow.run(argv); } catch (error) { console.error(chalk.red("Conversion failed:"), error.message); if (process.env.DEBUG) { console.error(error.stack); } process.exit(1); } } ) .command( "download <source>", "Download an extension archive from a URL", (yargs) => { return yargs .positional("source", { describe: "Extension source URL (Chrome/Firefox store or direct)", type: "string", demandOption: true, }) .option("output", { alias: "o", describe: "Output path for the downloaded file. If a directory is provided, a filename will be generated.", type: "string", }) .option("extract", { describe: "Extract the downloaded archive into a folder named after the extension", type: "boolean", default: false, }) .option("locale", { alias: "l", describe: "Preferred locale for the extracted folder name (e.g., 'en', 'fr')", type: "string", }) .option("force", { alias: "f", describe: "Overwrite output file or extracted directory if it exists", type: "boolean", default: false, }); }, async (argv) => { try { await workflow.runDownload(argv); } catch (error) { console.error(chalk.red("Download failed:"), error.message); if (process.env.DEBUG) { console.error(error.stack); } process.exit(1); } } ) .command( "require <userscript>", "Generate a metadata block with a @require pointing to the file", (yargs) => { return yargs.positional("userscript", { describe: "Path to the .user.js file to reference", type: "string", demandOption: true, normalize: true, }); }, async (argv) => { try { const { generateRequireBlock } = require("./require"); const requireBlock = await generateRequireBlock(argv.userscript); process.stdout.write(requireBlock); } catch (error) { console.error( chalk.red("Failed to generate require block:"), error.message ); if (process.env.DEBUG) { console.error(error.stack); } process.exit(1); } } ) .option("verbose", { alias: "v", describe: "Enable verbose logging", type: "boolean", global: true, }) .help() .alias("help", "h") .version() .alias("version", "V") .example([ [ '$0 convert "https://chrome.google.com/webstore/detail/..." -o material-design-fileicons.user.js --minify', "Convert from Chrome Web Store with minification", ], [ "$0 convert ./extension.xpi --target vanilla -o extension.js --locale fr", "Convert XPI to vanilla JS with French locale", ], [ "$0 convert ./my-extension/ -o my-script.user.js --ignore-assets mp4,webm,ttf", "Convert local directory ignoring video and font assets", ], [ "$0 convert ./extension/ --locale en --minify --keep-temp", "Convert with English locale, minification, and debug files", ], [ '$0 download "https://addons.mozilla.org/..." -o my-addon.xpi', "Download an extension from the Firefox store", ], [ '$0 download "https://chrome.google.com/webstore/detail/..." --extract --locale en', "Download and extract an extension, using English for the folder name", ], [ "$0 require ./path/to/my-script.user.js", "Outputs a metadata block that @requires my-script.user.js", ], ]) .demandCommand(1, "You must specify a command") .strict() .parse(); } catch (error) { console.error(chalk.red("CLI error:"), error.message); process.exit(1); } } if (require.main === module) { main(); } module.exports = { main };