Skip to content

Commit ec6f79c

Browse files
committed
base func. working, usage text wip
1 parent 1cbe5b5 commit ec6f79c

7 files changed

Lines changed: 350 additions & 0 deletions

File tree

.editorconfig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[*]
2+
tab_width = 2
3+
indent_style = space
4+
insert_final_newline = true
5+
trim_trailing_whitespace = true

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,6 @@ dist
102102

103103
# TernJS port file
104104
.tern-port
105+
106+
# Build
107+
build/

.prettierrc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"semi": false,
3+
"printWidth": 120,
4+
"singleQuote": false
5+
}

package.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "massarg",
3+
"version": "0.1.0",
4+
"description": "Flexible, powerful, and simple command/argument parser for JS",
5+
"main": "index.js",
6+
"repository": "https://github.com/chenasraf/massarg.git",
7+
"author": "Chen Asraf <chenasrafil@gmail.com>",
8+
"license": "Apache 2.0",
9+
"devDependencies": {
10+
"@types/chalk": "^2.2.0",
11+
"@types/lodash": "^4.14.171",
12+
"@types/node": "^16.3.2"
13+
},
14+
"dependencies": {
15+
"chalk": "^4.1.1",
16+
"lodash": "^4.17.21"
17+
}
18+
}

src/index.ts

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
import chalk from "chalk"
2+
import merge from "lodash/merge"
3+
import path from "path"
4+
5+
export function massarg() {
6+
return new Massarg()
7+
}
8+
9+
export type OptionsBase = {
10+
help: boolean
11+
}
12+
13+
export type MainDef<Options> = (options: Options) => void
14+
15+
export interface OptionDef<Options, Value> {
16+
name: string
17+
aliases?: string[]
18+
description?: string
19+
defaultValue?: Value
20+
boolean?: boolean
21+
parse?(value: string, options: Options): Value
22+
}
23+
24+
export interface CommandDef<T> {
25+
name: string
26+
aliases?: string[]
27+
description?: string
28+
run(options: T): void
29+
}
30+
31+
export interface HelpDef {
32+
printWidth?: number
33+
binName?: string
34+
normalColor?: keyof typeof chalk
35+
highlightColor?: keyof typeof chalk
36+
titleColor?: keyof typeof chalk
37+
}
38+
39+
export class Massarg<Options extends OptionsBase = OptionsBase> {
40+
private _main?: MainDef<Options>
41+
private _options: OptionDef<Options, any>[] = []
42+
private _commands: CommandDef<Options>[] = []
43+
private _runCommand?: CommandDef<Options>
44+
public data: Options = { help: false } as Options
45+
46+
private _help: Required<HelpDef> = {
47+
binName: undefined as any,
48+
normalColor: "dim",
49+
highlightColor: "yellow",
50+
titleColor: "white",
51+
printWidth: 80,
52+
}
53+
54+
constructor() {
55+
this._options.push({
56+
name: "help",
57+
aliases: ["h"],
58+
defaultValue: false,
59+
parse: Boolean,
60+
})
61+
}
62+
63+
public main(options: MainDef<Options>): Massarg<Options> {
64+
this._main = options
65+
return this
66+
}
67+
68+
public option<Value>(options: OptionDef<Options, Value>): Massarg<Options> {
69+
this._options.push(options)
70+
return this
71+
}
72+
73+
public command(options: CommandDef<Options>): Massarg<Options> {
74+
this._commands.push(options)
75+
return this
76+
}
77+
78+
public help(options: HelpDef): Massarg<Options> {
79+
this._help = merge(this._help, options)
80+
return this
81+
}
82+
83+
public printHelp(): void {
84+
const { highlightColor, normalColor, titleColor, binName } = this._help
85+
console.log(
86+
this._color(titleColor, chalk.bold`Usage:`),
87+
this._color(highlightColor, binName ?? path.basename(process.argv[1])),
88+
this._color(normalColor, "[command] [options]")
89+
)
90+
}
91+
92+
private _color(color: keyof typeof chalk, ...text: any[]): string {
93+
return (chalk[color] as typeof chalk.dim)(...text)
94+
}
95+
96+
public parseArgs(args = process.argv): Options {
97+
for (const option of this._options) {
98+
if (option.defaultValue !== undefined) {
99+
this._addOptionToData(option, option.defaultValue)
100+
}
101+
}
102+
103+
for (let i = 0; i < args.length; i++) {
104+
const arg = args[i]
105+
const option = this._options.find((o) => `--${o.name}` === arg || o.aliases?.map((a) => `-${a}`).includes(arg))
106+
107+
if (option) {
108+
// detect boolean values
109+
option.boolean ??= option.parse === Boolean || [true, false].includes(option.defaultValue)
110+
111+
let tempValue: any
112+
const hasNextToken = args.length > i + 1
113+
const nextTokenIsValue = hasNextToken && !args[i + 1].startsWith("-")
114+
115+
// parse boolean args
116+
if (option.boolean) {
117+
tempValue = true
118+
} else if (!hasNextToken || !nextTokenIsValue) {
119+
throw new TypeError(`Missing value for: ${option.name}`)
120+
} else {
121+
tempValue = args[i + 1]
122+
args.shift()
123+
}
124+
const parse: NonNullable<OptionDef<Options, unknown>["parse"]> = option.parse ?? ((a) => a)
125+
const value = parse(tempValue, this.data)
126+
this._addOptionToData(option, value)
127+
128+
continue
129+
}
130+
131+
const command = this._commands.find((o) => o.name === arg || o.aliases?.includes(arg))
132+
133+
if (command) {
134+
if (this._runCommand) {
135+
// TODO add to extras?
136+
continue
137+
}
138+
this._runCommand = command
139+
}
140+
}
141+
return this.data
142+
}
143+
144+
public parse(args = process.argv): void {
145+
this.parseArgs(args)
146+
147+
if (this.data.help) {
148+
this.printHelp()
149+
return
150+
}
151+
152+
console.log("data", this.data)
153+
154+
if (this._runCommand) {
155+
console.log("Running command", this._runCommand)
156+
this._runCommand.run(this.data)
157+
} else if (this._main) {
158+
console.log("Running main", this._main)
159+
this._main(this.data)
160+
}
161+
}
162+
163+
private _addOptionToData(option: OptionDef<Options, any>, value: any) {
164+
const _d: Record<string, any> = this.data
165+
_d[option.name] = value
166+
option.aliases?.forEach((a) => (_d[a] = value))
167+
}
168+
}
169+
170+
massarg()
171+
.help({
172+
binName: "my-cmd",
173+
})
174+
.option({
175+
name: "bool",
176+
aliases: ["b"],
177+
defaultValue: false,
178+
parse: Boolean,
179+
})
180+
.option({
181+
name: "number",
182+
aliases: ["n"],
183+
defaultValue: 0,
184+
parse: (v) => parseInt(v),
185+
})
186+
.command({ name: "do", run: console.log.bind(undefined, "do") })
187+
.main(console.log.bind(undefined, "main"))
188+
.parse()
189+
190+
export default massarg

tsconfig.json

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{
2+
"compilerOptions": {
3+
/* Visit https://aka.ms/tsconfig.json to read more about this file */
4+
/* Basic Options */
5+
// "incremental": true, /* Enable incremental compilation */
6+
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
7+
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
8+
"lib": [
9+
"ES2020"
10+
], /* Specify library files to be included in the compilation. */
11+
// "allowJs": true, /* Allow javascript files to be compiled. */
12+
// "checkJs": true, /* Report errors in .js files. */
13+
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
14+
"declaration": true, // /* Generates corresponding '.d.ts' file. */
15+
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
16+
"sourceMap": true, // /* Generates corresponding '.map' file. */
17+
// "outFile": "./", /* Concatenate and emit output to single file. */
18+
"outDir": "./build", // /* Redirect output structure to the directory. */
19+
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
20+
// "composite": true, /* Enable project compilation */
21+
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
22+
// "removeComments": true, /* Do not emit comments to output. */
23+
// "noEmit": true, /* Do not emit outputs. */
24+
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
25+
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
26+
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
27+
/* Strict Type-Checking Options */
28+
"strict": true, // /* Enable all strict type-checking options. */
29+
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
30+
// "strictNullChecks": true, /* Enable strict null checks. */
31+
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
32+
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
33+
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
34+
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
35+
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
36+
/* Additional Checks */
37+
// "noUnusedLocals": true, /* Report errors on unused locals. */
38+
// "noUnusedParameters": true, /* Report errors on unused parameters. */
39+
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
40+
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
41+
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
42+
/* Module Resolution Options */
43+
"moduleResolution": "node", // /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
44+
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
45+
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
46+
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
47+
// "typeRoots": [], /* List of folders to include type definitions from. */
48+
// "types": [], /* Type declaration files to be included in compilation. */
49+
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
50+
"esModuleInterop": true, // /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
51+
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
52+
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
53+
/* Source Map Options */
54+
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
55+
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
56+
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
57+
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
58+
/* Experimental Options */
59+
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
60+
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
61+
/* Advanced Options */
62+
"skipLibCheck": true, // /* Skip type checking of declaration files. */
63+
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
64+
}
65+
}

yarn.lock

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2+
# yarn lockfile v1
3+
4+
5+
"@types/chalk@^2.2.0":
6+
version "2.2.0"
7+
resolved "https://registry.yarnpkg.com/@types/chalk/-/chalk-2.2.0.tgz#b7f6e446f4511029ee8e3f43075fb5b73fbaa0ba"
8+
integrity sha512-1zzPV9FDe1I/WHhRkf9SNgqtRJWZqrBWgu7JGveuHmmyR9CnAPCie2N/x+iHrgnpYBIcCJWHBoMRv2TRWktsvw==
9+
dependencies:
10+
chalk "*"
11+
12+
"@types/lodash@^4.14.171":
13+
version "4.14.171"
14+
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.171.tgz#f01b3a5fe3499e34b622c362a46a609fdb23573b"
15+
integrity sha512-7eQ2xYLLI/LsicL2nejW9Wyko3lcpN6O/z0ZLHrEQsg280zIdCv1t/0m6UtBjUHokCGBQ3gYTbHzDkZ1xOBwwg==
16+
17+
"@types/node@^16.3.2":
18+
version "16.3.2"
19+
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.3.2.tgz#655432817f83b51ac869c2d51dd8305fb8342e16"
20+
integrity sha512-jJs9ErFLP403I+hMLGnqDRWT0RYKSvArxuBVh2veudHV7ifEC1WAmjJADacZ7mRbA2nWgHtn8xyECMAot0SkAw==
21+
22+
ansi-styles@^4.1.0:
23+
version "4.3.0"
24+
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
25+
integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
26+
dependencies:
27+
color-convert "^2.0.1"
28+
29+
chalk@*, chalk@^4.1.1:
30+
version "4.1.1"
31+
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad"
32+
integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==
33+
dependencies:
34+
ansi-styles "^4.1.0"
35+
supports-color "^7.1.0"
36+
37+
color-convert@^2.0.1:
38+
version "2.0.1"
39+
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
40+
integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
41+
dependencies:
42+
color-name "~1.1.4"
43+
44+
color-name@~1.1.4:
45+
version "1.1.4"
46+
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
47+
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
48+
49+
has-flag@^4.0.0:
50+
version "4.0.0"
51+
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
52+
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
53+
54+
lodash@^4.17.21:
55+
version "4.17.21"
56+
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
57+
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
58+
59+
supports-color@^7.1.0:
60+
version "7.2.0"
61+
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
62+
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
63+
dependencies:
64+
has-flag "^4.0.0"

0 commit comments

Comments
 (0)