UNPKG

args-json

Version:

Zero-dependency typed command-line argument parser

224 lines (222 loc) 5.95 kB
// index.ts function toCamelCase(x) { let s = x.replace(/^[-_.\s~+]|[-_.\s~+]$/g, ""); if (!/[-_.\s~+]/.test(s)) return s.slice(0, 1).toLowerCase() + s.slice(1); return s.toLowerCase().replace(/[-_.\s~+](\S)/g, (_, match) => match.toUpperCase()); } function toKey(x) { if (x) { if (x.startsWith("--") && x.length > 2) return toCamelCase(x.slice(2)); if (x.startsWith("-") && x.length === 2) return toCamelCase(x.slice(1)); } } function split(x) { let words = [], word = ""; let hasOpenSingleQuote = false; let hasOpenDoubleQuote = false; for (let i = 0; i < x.length; i++) { let c = x[i]; if (/^\s/.test(c) && !hasOpenSingleQuote && !hasOpenDoubleQuote) { if (word) words.push(word); word = ""; continue; } if (c === "'" && x[i - 1] !== "\\") hasOpenSingleQuote = !hasOpenSingleQuote; if (c === '"' && x[i - 1] !== "\\") hasOpenDoubleQuote = !hasOpenDoubleQuote; word += c; } if (word) words.push(word); return words; } function getDefaultInput() { return typeof process === void 0 ? [] : process.argv; } function parseArgs(input, map) { let normalizedInput; let normalizedMap; if (input === void 0) normalizedInput = getDefaultInput(); else if (typeof input === "string") normalizedInput = split(input); else if (Array.isArray(input)) normalizedInput = input.map((x) => String(x)); else if (input !== null && typeof input === "object") { normalizedInput = getDefaultInput(); normalizedMap = input; } else normalizedInput = []; normalizedInput = normalizedInput.map((item) => { let normalizedItem = item.trim(); let k2 = normalizedItem.indexOf("="); if (k2 === -1) return normalizedItem; return [ normalizedItem.slice(0, k2), normalizedItem.slice(k2 + 1) ]; }).flat(); if (map) normalizedMap = map; let key = ""; let parsedArgs2 = {}; for (let rawValue of normalizedInput) { rawValue = rawValue.trim(); if (rawValue.startsWith('"') && rawValue.endsWith('"')) rawValue = rawValue.slice(1, -1); else if (rawValue.startsWith("'") && rawValue.endsWith("'")) rawValue = rawValue.slice(1, -1); let parsedKey = toKey(rawValue); if (parsedKey !== void 0) { let nextKey = normalizedMap?.[parsedKey] ?? parsedKey; if (key && nextKey !== key && parsedArgs2[key] === void 0) parsedArgs2[key] = true; key = nextKey; continue; } let parsedValue; if (rawValue) { try { parsedValue = JSON.parse(rawValue); } catch { parsedValue = rawValue; } } else parsedValue = true; let prevValue = parsedArgs2[key]; let value; if (prevValue === void 0) value = parsedValue; else if (Array.isArray(prevValue)) value = [...prevValue, parsedValue]; else value = [prevValue, parsedValue]; parsedArgs2[key] = value; } if (key && parsedArgs2[key] === void 0) parsedArgs2[key] = true; return parsedArgs2; } // tests.ts var k = 0; function test(actual, expected) { console.log(`00${++k}`.slice(-3)); if (JSON.stringify(actual) !== JSON.stringify(expected)) { console.log(`Expected: ${JSON.stringify(expected, null, 2)}`); console.log(`Actual: ${JSON.stringify(actual, null, 2)}`); throw new Error("Unexpected value"); } } function trim(args) { let { "": _unkeyedArgs, ...keyedArgs } = args; return keyedArgs; } test(trim(parseArgs()), { test: 1 }); test(trim(parseArgs({ test: "lorem" })), { lorem: 1 }); test(trim(parseArgs(process.argv)), { test: 1 }); test(trim(parseArgs(process.argv, { test: "ipsum" })), { ipsum: 1 }); test(parseArgs("--debug"), { debug: true }); test(parseArgs("-d", { d: "debug" }), { debug: true }); test(parseArgs("-debug"), { "": "-debug" }); test(parseArgs("--debug -v", { v: "version" }), { debug: true, version: true }); test(parseArgs("--debug -v 1.2.3", { v: "version" }), { debug: true, version: "1.2.3" }); test(parseArgs("--debug -v=1.2.3", { v: "version" }), { debug: true, version: "1.2.3" }); test(parseArgs("--debug --v=1.2.3", { v: "version" }), { debug: true, version: "1.2.3" }); test(parseArgs("--debug --version=1.2.3"), { debug: true, version: "1.2.3" }); test(parseArgs("--debug --parsed-args"), { debug: true, parsedArgs: true }); test(parseArgs("--config ./configs/default.json -d"), { config: "./configs/default.json", d: true }); test(parseArgs('--config "./configs/default.json" -d'), { config: "./configs/default.json", d: true }); test(parseArgs("--config=./configs/default.json -d"), { config: "./configs/default.json", d: true }); test(parseArgs('--config="./configs/default.json" -d'), { config: "./configs/default.json", d: true }); test(parseArgs("-c ./configs/default.json --ttl=1000 -d"), { c: "./configs/default.json", ttl: 1e3, d: true }); test(parseArgs('-c "./config.json" --ttl=1000 -v=1.0.0', { c: "config", v: "version" }), { config: "./config.json", ttl: 1e3, version: "1.0.0" }); test(parseArgs('-t "random word" -t test -t 10', { t: "tags" }), { tags: ["random word", "test", 10] }); test(parseArgs(`-d '{"data":{"id":3,"tags":["test","random"]}}' --debug`), { d: { data: { id: 3, tags: ["test", "random"] } }, debug: true }); var longLine = '-i -n=0 --test qwe -d "lorem ipsum" --config=./config.json -x true --debug'; test(parseArgs(longLine, { d: "description" }), { i: true, n: 0, test: "qwe", description: "lorem ipsum", config: "./config.json", x: true, debug: true }); test(parseArgs(`-d '{"x":10}' -i 0 -n=3 -c ./config.json`), { d: { x: 10 }, i: 0, n: 3, c: "./config.json" }); var parsedArgs = parseArgs(["--config", "./configs/default.json", "--debug"]); test(parsedArgs, { config: "./configs/default.json", debug: true }); test(parsedArgs.config, "./configs/default.json"); console.log("PASSED");