args-json
Version:
Zero-dependency typed command-line argument parser
224 lines (222 loc) • 5.95 kB
JavaScript
// 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");