From 3f7f881de7342e4f3fe9fa390f4913061495dbc9 Mon Sep 17 00:00:00 2001
From: Samuel Plumppu <6125097+Greenheart@users.noreply.github.com>
Date: Mon, 9 Mar 2026 03:06:32 +0100
Subject: [PATCH 1/2] Improve CLI startup performance and remove 11 production
dependencies by switching from `yargs` to `commander`
- Improved startup performance and responsiveness when using the CLI
- Reduces risk for supply chain attacks by removing 11 production
dependencies
- Reduces the install size from `511 kB` to `208 kB`
---
bin/qrcode | 219 +++++++++++++++++++++++++---------------------
package-lock.json | 67 ++++++++++----
package.json | 4 +-
3 files changed, 169 insertions(+), 121 deletions(-)
diff --git a/bin/qrcode b/bin/qrcode
index dd990b63..e49e6d56 100755
--- a/bin/qrcode
+++ b/bin/qrcode
@@ -1,21 +1,118 @@
#!/usr/bin/env node
-var yargs = require('yargs')
-var qr = require('../lib')
+const { program, Option, InvalidArgumentError } = require('commander')
+const QRCode = require('../lib')
+const pkg = require('../package.json')
-function save (file, text, options) {
- qr.toFile(file, text, options, function (err, data) {
+const QR_VERSION_RANGE = [1, 40]
+const QR_MASK_RANGE = [0, 7]
+const ALL_EC_LEVELS = ['L', 'M', 'Q', 'H']
+const OUTPUT_OPTIONS = ['png', 'svg', 'utf8']
+
+/**
+ * Parse CLI arguments and return a CLI instance.
+ * @returns {import('commander').Command}
+ */
+function parseArgs() {
+ return (
+ program
+ .name('qrcode')
+ .usage('[options] ')
+ .optionsGroup('QR Code options:')
+ .option(
+ '-v, --qversion ',
+ `QR Code symbol version ${printRange(QR_VERSION_RANGE)}`,
+ integerBetween(...QR_VERSION_RANGE),
+ )
+ .addOption(new Option('-e, --error ', 'Error correction level').choices(ALL_EC_LEVELS))
+ .option(
+ '-m, --mask ',
+ `Mask pattern ${printRange(QR_MASK_RANGE)}`,
+ integerBetween(...QR_MASK_RANGE),
+ )
+ .optionsGroup('Renderer options:')
+ // IDEA: Maybe use a shared constant for valid renderers, defined by the renderers themselves
+ .addOption(new Option('-t, --type ', 'Output type').choices(OUTPUT_OPTIONS))
+ .option('-i, --inverse', 'Invert colors')
+ .option('-w, --width ', 'Image width (px)', integerBetween(0))
+ .option('-s, --scale ', 'Scale factor', integer)
+ .option('-q, --qzone ', 'Quiet zone size', integerBetween(0))
+ .option('-l, --lightcolor ', 'Light RGBA hex color')
+ .option('-d, --darkcolor ', 'Dark RGBA hex color')
+ .option('--small', 'Output smaller QR code to terminal')
+ .optionsGroup('Options:')
+ .option('-o, --output ', 'Output file')
+ .helpOption('-h, --help', 'Show help')
+ .addHelpText(
+ 'after',
+ `
+Examples:
+ qrcode "some text" Draw in terminal window
+ qrcode -o out.png "some text" Save as png image
+ qrcode -d F00 -o out.png "some text" Use red as foreground color
+`,
+ )
+ .showHelpAfterError()
+ .version(pkg.version, '--version', 'Show version number')
+ // The input string is a variadic argument that will include any remaining
+ // input after parsing options In commander, this is indicated by the `...`
+ // suffix.
+ // The wrapping [] makes the input string arguments optional.
+ // This allows executing the CLI and reading from stdin, useful in scripts.
+ // By making the arguments optional, the help can be printed by default.
+ .argument('[input string...]', undefined) // undefined to hide description
+ .parse()
+ )
+}
+
+/**
+ * @param {readonly [number, number])} range
+ */
+function printRange([min, max]) {
+ return `(${min} - ${max})`
+}
+
+/**
+ * @param {string} value
+ */
+function integer(value) {
+ const parsedValue = parseInt(value, 10)
+ if (Number.isInteger(parsedValue)) {
+ return parsedValue
+ }
+
+ throw new InvalidArgumentError('Must be an integer')
+}
+
+/**
+ * @param {number} min
+ * @param {number} [max]
+ */
+function integerBetween(min, max = Number.MAX_SAFE_INTEGER) {
+ return (value) => {
+ const n = integer(value)
+
+ if (min <= n && n <= max) {
+ return n
+ }
+
+ throw new InvalidArgumentError(`Must be between ${min} and ${max}`)
+ }
+}
+
+function save(file, text, options) {
+ QRCode.toFile(file, text, options, (err, _data) => {
if (err) {
console.error('Error:', err.message)
process.exit(1)
}
- console.log('saved qrcode to: ' + file + '\n')
+ console.log('Saved qrcode to: ' + file + '\n')
})
}
-function print (text, options) {
+function print(text, options) {
options.type = 'terminal'
- qr.toString(text, options, function (err, text) {
+ QRCode.toString(text, options, (err, text) => {
if (err) {
console.error('Error:', err.message)
process.exit(1)
@@ -25,7 +122,7 @@ function print (text, options) {
})
}
-function parseOptions (args) {
+function parseOptions(args) {
return {
version: args.qversion,
errorCorrectionLevel: args.error,
@@ -43,10 +140,10 @@ function parseOptions (args) {
}
}
-function processInputs (text, opts) {
+function processInput(text, opts) {
+ // Show help and exit when no input was passed
if (!text.length) {
- yargs.showHelp()
- process.exit(1)
+ program.help()
}
if (opts.output) {
@@ -56,104 +153,24 @@ function processInputs (text, opts) {
}
}
-var argv = yargs
- .detectLocale(false)
- .usage('Usage: $0 [options] ')
- .option('v', {
- alias: 'qversion',
- description: 'QR Code symbol version (1 - 40)',
- group: 'QR Code options:',
- type: 'number'
- })
- .option('e', {
- alias: 'error',
- description: 'Error correction level',
- choices: ['L', 'M', 'Q', 'H'],
- group: 'QR Code options:'
- })
- .option('m', {
- alias: 'mask',
- description: 'Mask pattern (0 - 7)',
- group: 'QR Code options:',
- type: 'number'
- })
- .option('t', {
- alias: 'type',
- description: 'Output type',
- choices: ['png', 'svg', 'utf8'],
- implies: 'output',
- group: 'Renderer options:'
- })
- .option('i', {
- alias: 'inverse',
- type: 'boolean',
- description: 'Invert colors',
- group: 'Renderer options:'
- })
- .option('w', {
- alias: 'width',
- description: 'Image width (px)',
- conflicts: 'scale',
- group: 'Renderer options:',
- type: 'number'
- })
- .option('s', {
- alias: 'scale',
- description: 'Scale factor',
- conflicts: 'width',
- group: 'Renderer options:',
- type: 'number'
- })
- .option('q', {
- alias: 'qzone',
- description: 'Quiet zone size',
- group: 'Renderer options:',
- type: 'number'
- })
- .option('l', {
- alias: 'lightcolor',
- description: 'Light RGBA hex color',
- group: 'Renderer options:'
- })
- .option('d', {
- alias: 'darkcolor',
- description: 'Dark RGBA hex color',
- group: 'Renderer options:'
- })
- .option('small', {
- type: 'boolean',
- description: 'Output smaller QR code to terminal',
- conflicts: 'type',
- group: 'Renderer options:'
- })
- .option('o', {
- alias: 'output',
- description: 'Output file'
- })
- .help('h')
- .alias('h', 'help')
- .version()
- .example('$0 "some text"', 'Draw in terminal window')
- .example('$0 -o out.png "some text"', 'Save as png image')
- .example('$0 -d F00 -o out.png "some text"', 'Use red as foreground color')
- .parserConfiguration({'parse-numbers': false})
- .argv
+const cli = parseArgs()
+const argv = cli.opts()
if (process.stdin.isTTY) {
- processInputs(argv._.join(' '), argv)
+ processInput(cli.args.join(' '), argv)
} else {
- var text = ''
+ let text = ''
process.stdin.setEncoding('utf8')
- process.stdin.on('readable', function () {
- var chunk = process.stdin.read()
+ process.stdin.on('readable', () => {
+ const chunk = process.stdin.read()
if (chunk !== null) {
text += chunk
}
})
- process.stdin.on('end', function () {
- // this process can be run as a command outside of a tty so if there was no
- // data on stdin read from argv
- processInputs(text.length?text:argv._.join(' '), argv)
+ process.stdin.on('end', () => {
+ // This process can be run as a command outside of a TTY
+ // so if there was no data on stdin, read the CLI args instead
+ processInput(text.length ? text : cli.args.join(' '), argv)
})
}
diff --git a/package-lock.json b/package-lock.json
index 99d439ba..bb84531e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1351,12 +1351,14 @@
"ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true
},
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
"requires": {
"color-convert": "^2.0.1"
}
@@ -1914,7 +1916,8 @@
"camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
- "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
},
"caniuse-lite": {
"version": "1.0.30001282",
@@ -2018,6 +2021,7 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+ "dev": true,
"requires": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
@@ -2028,6 +2032,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
"requires": {
"color-name": "~1.1.4"
}
@@ -2035,7 +2040,8 @@
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
},
"color-support": {
"version": "1.1.3",
@@ -2070,10 +2076,9 @@
}
},
"commander": {
- "version": "2.20.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
- "dev": true
+ "version": "14.0.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz",
+ "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="
},
"commondir": {
"version": "1.0.1",
@@ -2270,7 +2275,8 @@
"decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
- "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+ "dev": true
},
"decompress-response": {
"version": "4.2.1",
@@ -2527,7 +2533,8 @@
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
},
"encodeurl": {
"version": "1.0.2",
@@ -3293,6 +3300,7 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
"requires": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
@@ -3426,7 +3434,8 @@
"get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true
},
"get-intrinsic": {
"version": "1.1.1",
@@ -3842,7 +3851,8 @@
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
},
"is-glob": {
"version": "4.0.3",
@@ -4257,6 +4267,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
"requires": {
"p-locate": "^4.1.0"
}
@@ -4798,6 +4809,7 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
"requires": {
"p-try": "^2.0.0"
}
@@ -4806,6 +4818,7 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
"requires": {
"p-limit": "^2.2.0"
}
@@ -4822,7 +4835,8 @@
"p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
- "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true
},
"package-hash": {
"version": "4.0.0",
@@ -4898,7 +4912,8 @@
"path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true
},
"path-is-absolute": {
"version": "1.0.1",
@@ -5520,7 +5535,8 @@
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
- "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
+ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+ "dev": true
},
"require-from-string": {
"version": "2.0.2",
@@ -5531,7 +5547,8 @@
"require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
- "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
+ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+ "dev": true
},
"resolve": {
"version": "1.20.0",
@@ -5705,7 +5722,8 @@
"set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
- "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
+ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+ "dev": true
},
"setprototypeof": {
"version": "1.1.1",
@@ -6031,6 +6049,7 @@
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
@@ -6094,6 +6113,7 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
"requires": {
"ansi-regex": "^5.0.1"
}
@@ -7515,6 +7535,12 @@
"source-map-support": "~0.5.12"
},
"dependencies": {
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -7910,7 +7936,8 @@
"which-module": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
- "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
+ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
+ "dev": true
},
"wide-align": {
"version": "1.1.5",
@@ -7931,6 +7958,7 @@
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "dev": true,
"requires": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
@@ -7970,7 +7998,8 @@
"y18n": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
- "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="
+ "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
+ "dev": true
},
"yallist": {
"version": "4.0.0",
@@ -7988,6 +8017,7 @@
"version": "15.4.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
+ "dev": true,
"requires": {
"cliui": "^6.0.0",
"decamelize": "^1.2.0",
@@ -8006,6 +8036,7 @@
"version": "18.1.3",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+ "dev": true,
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
diff --git a/package.json b/package.json
index 6c9ab00d..1d1fe85b 100644
--- a/package.json
+++ b/package.json
@@ -38,9 +38,9 @@
"qrcode": "./bin/qrcode"
},
"dependencies": {
+ "commander": "^14.0.3",
"dijkstrajs": "^1.0.1",
- "pngjs": "^5.0.0",
- "yargs": "^15.3.1"
+ "pngjs": "^5.0.0"
},
"devDependencies": {
"@babel/core": "^7.9.0",
From 41c5101ea45ed74a4f5f1eb7ef4252d02b191f9f Mon Sep 17 00:00:00 2001
From: Samuel Plumppu <6125097+Greenheart@users.noreply.github.com>
Date: Mon, 9 Mar 2026 03:10:56 +0100
Subject: [PATCH 2/2] Add new CLI help to README
---
README.md | 33 +++++++++++++++++----------------
1 file changed, 17 insertions(+), 16 deletions(-)
diff --git a/README.md b/README.md
index 510e6b9f..81fb3b79 100644
--- a/README.md
+++ b/README.md
@@ -50,28 +50,29 @@ npm install -g qrcode
Usage: qrcode [options]
QR Code options:
- -v, --qversion QR Code symbol version (1 - 40) [number]
- -e, --error Error correction level [choices: "L", "M", "Q", "H"]
- -m, --mask Mask pattern (0 - 7) [number]
+ -v, --qversion QR Code symbol version (1 - 40)
+ -e, --error Error correction level (choices: "L", "M", "Q", "H")
+ -m, --mask Mask pattern (0 - 7)
Renderer options:
- -t, --type Output type [choices: "png", "svg", "utf8"]
- -w, --width Image width (px) [number]
- -s, --scale Scale factor [number]
- -q, --qzone Quiet zone size [number]
- -l, --lightcolor Light RGBA hex color
- -d, --darkcolor Dark RGBA hex color
- --small Output smaller QR code to terminal [boolean]
+ -t, --type Output type (choices: "png", "svg", "utf8")
+ -i, --inverse Invert colors
+ -w, --width Image width (px)
+ -s, --scale Scale factor
+ -q, --qzone Quiet zone size
+ -l, --lightcolor Light RGBA hex color
+ -d, --darkcolor Dark RGBA hex color
+ --small Output smaller QR code to terminal
Options:
- -o, --output Output file
- -h, --help Show help [boolean]
- --version Show version number [boolean]
+ -o, --output Output file
+ --version Show version number
+ -h, --help Show help
Examples:
- qrcode "some text" Draw in terminal window
- qrcode -o out.png "some text" Save as png image
- qrcode -d F00 -o out.png "some text" Use red as foreground color
+ qrcode "some text" Draw in terminal window
+ qrcode -o out.png "some text" Save as png image
+ qrcode -d F00 -o out.png "some text" Use red as foreground color
```
If not specified, output type is guessed from file extension.
Recognized extensions are `png`, `svg` and `txt`.