@clerc/parser 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.mjs +1 -302
  2. package/package.json +2 -2
package/dist/index.mjs CHANGED
@@ -1,302 +1 @@
1
- import { camelCase, looseIsArray, resolveValue } from "@clerc/utils";
2
-
3
- //#region src/errors.ts
4
- var InvalidSchemaError = class extends Error {
5
- constructor(message) {
6
- super(`Invalid schema: ${message}`);
7
- this.name = "InvalidSchemaError";
8
- }
9
- };
10
-
11
- //#endregion
12
- //#region src/iterator.ts
13
- const KNOWN_FLAG = "known-flag";
14
- const UNKNOWN_FLAG = "unknown-flag";
15
- const PARAMETER = "parameter";
16
- function iterateArgs(args, result, shouldProcessAsFlag, isKnownFlag, ignore, callback) {
17
- let index = 0;
18
- let stopped = false;
19
- const argsLength = args.length;
20
- const iterator = {
21
- current: "",
22
- index: 0,
23
- hasNext: false,
24
- next: "",
25
- shouldIgnore: (arg) => {
26
- if (ignore) return ignore(shouldProcessAsFlag(arg) ? isKnownFlag(arg) ? KNOWN_FLAG : UNKNOWN_FLAG : PARAMETER, arg);
27
- return false;
28
- },
29
- eat: () => {
30
- if (index + 1 >= argsLength) return;
31
- const nextArg = args[index + 1];
32
- if (iterator.shouldIgnore(nextArg)) {
33
- iterator.exit();
34
- return;
35
- }
36
- index++;
37
- next();
38
- return args[index];
39
- },
40
- exit: (push = true) => {
41
- if (!stopped) {
42
- if (push) result.ignored.push(...args.slice(index + 1));
43
- stopped = true;
44
- }
45
- }
46
- };
47
- function next() {
48
- iterator.current = args[index];
49
- iterator.index = index;
50
- iterator.hasNext = index + 1 < argsLength;
51
- iterator.next = iterator.hasNext ? args[index + 1] : "";
52
- }
53
- for (index = 0; index < argsLength; index++) {
54
- if (stopped) break;
55
- next();
56
- callback(iterator);
57
- }
58
- }
59
-
60
- //#endregion
61
- //#region src/config.ts
62
- const defaultParserOptions = { delimiters: ["=", ":"] };
63
- const resolveParserOptions = (options = {}) => ({
64
- ...defaultParserOptions,
65
- ...options
66
- });
67
- const normalizeConfig = (config) => typeof config === "function" || looseIsArray(config) ? { type: config } : config;
68
- const BUILDTIN_DELIMITERS_RE = /[\s.]/;
69
- function buildConfigsAndAliases(delimiters, flags) {
70
- const configs = /* @__PURE__ */ new Map();
71
- const aliases = /* @__PURE__ */ new Map();
72
- const isNameInvalid = (name) => delimiters.some((char) => name.includes(char)) || BUILDTIN_DELIMITERS_RE.test(name);
73
- function validateFlagOptions(name, options) {
74
- const prefix = `Flag "${name}"`;
75
- if (Array.isArray(options.type) && options.type.length > 1) throw new InvalidSchemaError(`${prefix} has an invalid type array. Only single-element arrays are allowed to denote multiple occurrences.`);
76
- if (name.length < 2) throw new InvalidSchemaError(`${prefix} name must be at least 2 characters long.`);
77
- const names = [name];
78
- if (options.short) {
79
- if (options.short.length !== 1) throw new InvalidSchemaError(`${prefix} short flag must be exactly 1 character long.`);
80
- names.push(options.short);
81
- }
82
- if (names.some(isNameInvalid)) throw new InvalidSchemaError(`${prefix} contains reserved characters, which are used as delimiters.`);
83
- if (options.required && options.default !== void 0) throw new InvalidSchemaError(`${prefix} cannot be both required and have a default value.`);
84
- }
85
- for (const [name, config] of Object.entries(flags)) {
86
- const normalized = normalizeConfig(config);
87
- validateFlagOptions(name, normalized);
88
- configs.set(name, normalized);
89
- aliases.set(name, name);
90
- aliases.set(camelCase(name), name);
91
- if (normalized.short) aliases.set(normalized.short, name);
92
- }
93
- return {
94
- configs,
95
- aliases
96
- };
97
- }
98
-
99
- //#endregion
100
- //#region src/utils.ts
101
- const isArrayOfType = (arr, type) => Array.isArray(arr) && arr[0] === type;
102
- /**
103
- * Check if it's a letter (a-z: 97-122, A-Z: 65-90)
104
- */
105
- const isLetter = (charCode) => charCode >= 65 && charCode <= 90 || charCode >= 97 && charCode <= 122;
106
- /**
107
- * Check if it's a digit (0-9: 48-57)
108
- */
109
- const isDigit = (charCode) => charCode >= 48 && charCode <= 57;
110
- function setValueByType(flags, key, value, config) {
111
- const { type } = config;
112
- if (looseIsArray(type)) if (isArrayOfType(type, Boolean)) flags[key] = (flags[key] ?? 0) + 1;
113
- else (flags[key] ??= []).push(type[0](value));
114
- else flags[key] = type(value);
115
- }
116
- function setDotValues(obj, path, value) {
117
- const keys = path.split(".");
118
- let current = obj;
119
- for (let i = 0; i < keys.length - 1; i++) {
120
- const key = keys[i];
121
- current[key] ??= {};
122
- current = current[key];
123
- }
124
- const lastKey = keys[keys.length - 1];
125
- if (value === "true" || value === "") current[lastKey] = true;
126
- else if (value === "false") current[lastKey] = false;
127
- else current[lastKey] = value;
128
- }
129
- function splitNameAndValue(arg, delimiters) {
130
- let sepIdx = -1;
131
- let delimiterLen = 0;
132
- for (const delimiter of delimiters) {
133
- const idx = arg.indexOf(delimiter);
134
- if (idx !== -1 && (sepIdx === -1 || idx < sepIdx)) {
135
- sepIdx = idx;
136
- delimiterLen = delimiter.length;
137
- }
138
- }
139
- if (!(sepIdx !== -1)) return {
140
- rawName: arg,
141
- rawValue: void 0,
142
- hasSep: false
143
- };
144
- return {
145
- rawName: arg.slice(0, sepIdx),
146
- rawValue: arg.slice(sepIdx + delimiterLen),
147
- hasSep: true
148
- };
149
- }
150
-
151
- //#endregion
152
- //#region src/parse.ts
153
- const DOUBLE_DASH = "--";
154
- function createParser(options = {}) {
155
- const { flags: flagsConfig = {}, delimiters, ignore } = resolveParserOptions(options);
156
- const { configs, aliases } = buildConfigsAndAliases(delimiters, flagsConfig);
157
- function resolve(name, isShortFlag = false) {
158
- const dotIdx = name.indexOf(".");
159
- const rootName = dotIdx === -1 ? name : name.slice(0, dotIdx);
160
- let key = aliases.get(rootName);
161
- if (!key) {
162
- key = aliases.get(camelCase(rootName));
163
- if (!key) return;
164
- }
165
- const config = configs.get(key);
166
- if (!isShortFlag && config.short === rootName) return;
167
- return {
168
- key,
169
- config,
170
- path: dotIdx === -1 ? void 0 : name.slice(dotIdx + 1)
171
- };
172
- }
173
- function resolveNegated(name) {
174
- if (!name.startsWith("no")) return;
175
- const possibleName = name[2] === "-" ? name.slice(3) : name.length > 2 && /[A-Z]/.test(name[2]) ? name[2].toLowerCase() + name.slice(3) : "";
176
- if (possibleName) {
177
- const possibleResolved = resolve(possibleName);
178
- if (possibleResolved?.config.type === Boolean && possibleResolved.config.negatable !== false) return possibleResolved;
179
- }
180
- }
181
- function shouldProcessAsFlag(arg) {
182
- if (arg.charCodeAt(0) !== 45) return false;
183
- const len = arg.length;
184
- if (len < 2) return false;
185
- const secondChar = arg.charCodeAt(1);
186
- if (isLetter(secondChar)) return true;
187
- if (isDigit(secondChar)) {
188
- const isShortFlag = secondChar !== 45;
189
- return !!resolve(isShortFlag ? arg[1] : arg.slice(2), isShortFlag);
190
- }
191
- if (secondChar === 45 && len > 2) return isLetter(arg.charCodeAt(2));
192
- return false;
193
- }
194
- function isKnownFlag(arg) {
195
- if (arg.charCodeAt(1) === 45) {
196
- const { rawName } = splitNameAndValue(arg.slice(2), delimiters);
197
- if (resolve(rawName, false)) return true;
198
- if (resolveNegated(rawName)) return true;
199
- return false;
200
- }
201
- const chars = arg.slice(1);
202
- for (const char of chars) if (!resolve(char, true)) return false;
203
- return true;
204
- }
205
- const parse$1 = (args) => {
206
- const result = {
207
- parameters: [],
208
- doubleDash: [],
209
- flags: {},
210
- raw: args,
211
- unknown: {},
212
- ignored: [],
213
- missingRequiredFlags: []
214
- };
215
- iterateArgs(args, result, shouldProcessAsFlag, isKnownFlag, ignore, ({ current, eat, exit, hasNext, index, next, shouldIgnore }) => {
216
- if (current === DOUBLE_DASH) {
217
- result.doubleDash.push(...args.slice(index + 1));
218
- exit(false);
219
- return;
220
- }
221
- if (shouldIgnore(current)) {
222
- result.ignored.push(current);
223
- exit();
224
- return;
225
- }
226
- if (!shouldProcessAsFlag(current)) {
227
- result.parameters.push(current);
228
- return;
229
- }
230
- const isShortFlag = !current.startsWith(DOUBLE_DASH);
231
- const chars = current.slice(isShortFlag ? 1 : 2);
232
- if (isShortFlag) {
233
- const charsLen = chars.length;
234
- for (let j = 0; j < charsLen; j++) {
235
- const char = chars[j];
236
- const resolved = resolve(char, true);
237
- if (!resolved) {
238
- result.unknown[char] = true;
239
- continue;
240
- }
241
- const { key, config } = resolved;
242
- const configType = config.type;
243
- if (configType === Boolean || isArrayOfType(configType, Boolean)) setValueByType(result.flags, key, "true", config);
244
- else if (j + 1 < charsLen) {
245
- setValueByType(result.flags, key, chars.slice(j + 1), config);
246
- break;
247
- } else {
248
- const value = hasNext && !shouldProcessAsFlag(next) ? eat() ?? "" : "";
249
- setValueByType(result.flags, key, value, config);
250
- }
251
- }
252
- } else {
253
- const { rawName, rawValue, hasSep } = splitNameAndValue(chars, delimiters);
254
- let resolved = resolve(rawName);
255
- let isNegated = false;
256
- if (!resolved) {
257
- const negated = resolveNegated(rawName);
258
- if (negated) {
259
- resolved = negated;
260
- isNegated = true;
261
- }
262
- }
263
- if (!resolved) {
264
- const key$1 = camelCase(rawName);
265
- if (hasSep) result.unknown[key$1] = rawValue;
266
- else if (hasNext && !shouldProcessAsFlag(next)) {
267
- const value = eat();
268
- result.unknown[key$1] = value ?? true;
269
- } else result.unknown[key$1] = true;
270
- return;
271
- }
272
- const { key, config, path } = resolved;
273
- if (path) {
274
- if (config.type === Object) {
275
- result.flags[key] ??= {};
276
- const value = hasSep ? rawValue : hasNext && !shouldProcessAsFlag(next) ? eat() ?? "" : "";
277
- setDotValues(result.flags[key], path, value);
278
- }
279
- } else if (config.type === Boolean) {
280
- const value = hasSep ? rawValue !== "false" : true;
281
- result.flags[key] = isNegated ? !value : value;
282
- } else {
283
- const value = hasSep ? rawValue : hasNext && !shouldProcessAsFlag(next) ? eat() ?? "" : "";
284
- setValueByType(result.flags, key, value, config);
285
- }
286
- }
287
- });
288
- for (const [key, config] of configs.entries()) if (result.flags[key] === void 0) {
289
- if (config.default !== void 0) result.flags[key] = resolveValue(config.default);
290
- else if (Array.isArray(config.type)) result.flags[key] = isArrayOfType(config.type, Boolean) ? 0 : [];
291
- else if (config.type === Object) result.flags[key] = {};
292
- else if (config.type === Boolean) result.flags[key] = false;
293
- else if (config.required) result.missingRequiredFlags.push(key);
294
- }
295
- return result;
296
- };
297
- return { parse: parse$1 };
298
- }
299
- const parse = (args, options = {}) => createParser(options).parse(args);
300
-
301
- //#endregion
302
- export { DOUBLE_DASH, InvalidSchemaError, KNOWN_FLAG, PARAMETER, UNKNOWN_FLAG, createParser, parse };
1
+ import{camelCase as e,looseIsArray as t,resolveValue as n}from"@clerc/utils";var r=class extends Error{constructor(e){super(`Invalid schema: ${e}`),this.name=`InvalidSchemaError`}};const i=`known-flag`,a=`unknown-flag`,o=`parameter`;function s(e,t,n,r,s,c){let l=0,u=!1,d=e.length,f={current:``,index:0,hasNext:!1,next:``,shouldIgnore:e=>s?s(n(e)?r(e)?i:a:o,e):!1,eat:()=>{if(l+1>=d)return;let t=e[l+1];if(f.shouldIgnore(t)){f.exit();return}return l++,p(),e[l]},exit:(n=!0)=>{u||=(n&&t.ignored.push(...e.slice(l+1)),!0)}};function p(){f.current=e[l],f.index=l,f.hasNext=l+1<d,f.next=f.hasNext?e[l+1]:``}for(l=0;l<d&&!u;l++)p(),c(f)}const c={delimiters:[`=`,`:`]},l=(e={})=>({...c,...e}),u=e=>typeof e==`function`||t(e)?{type:e}:e,d=/[\s.]/;function f(t,n){let i=new Map,a=new Map,o=e=>t.some(t=>e.includes(t))||d.test(e);function s(e,t){let n=`Flag "${e}"`;if(Array.isArray(t.type)&&t.type.length>1)throw new r(`${n} has an invalid type array. Only single-element arrays are allowed to denote multiple occurrences.`);if(e.length<2)throw new r(`${n} name must be at least 2 characters long.`);let i=[e];if(t.short){if(t.short.length!==1)throw new r(`${n} short flag must be exactly 1 character long.`);i.push(t.short)}if(i.some(o))throw new r(`${n} contains reserved characters, which are used as delimiters.`);if(t.required&&t.default!==void 0)throw new r(`${n} cannot be both required and have a default value.`)}for(let[t,r]of Object.entries(n)){let n=u(r);s(t,n),i.set(t,n),a.set(t,t),a.set(e(t),t),n.short&&a.set(n.short,t)}return{configs:i,aliases:a}}const p=(e,t)=>Array.isArray(e)&&e[0]===t,m=e=>e>=65&&e<=90||e>=97&&e<=122,h=e=>e>=48&&e<=57;function g(e,n,r,i){let{type:a}=i;t(a)?p(a,Boolean)?e[n]=(e[n]??0)+1:(e[n]??=[]).push(a[0](r)):e[n]=a(r)}function _(e,t,n){let r=t.split(`.`),i=e;for(let e=0;e<r.length-1;e++){let t=r[e];i[t]??={},i=i[t]}let a=r[r.length-1];n===`true`||n===``?i[a]=!0:n===`false`?i[a]=!1:i[a]=n}function v(e,t){let n=-1,r=0;for(let i of t){let t=e.indexOf(i);t!==-1&&(n===-1||t<n)&&(n=t,r=i.length)}return n===-1?{rawName:e,rawValue:void 0,hasSep:!1}:{rawName:e.slice(0,n),rawValue:e.slice(n+r),hasSep:!0}}const y=`--`;function b(t={}){let{flags:r={},delimiters:i,ignore:a}=l(t),{configs:o,aliases:c}=f(i,r);function u(t,n=!1){let r=t.indexOf(`.`),i=r===-1?t:t.slice(0,r),a=c.get(i);if(!a&&(a=c.get(e(i)),!a))return;let s=o.get(a);if(!(!n&&s.short===i))return{key:a,config:s,path:r===-1?void 0:t.slice(r+1)}}function d(e){if(!e.startsWith(`no`))return;let t=e[2]===`-`?e.slice(3):e.length>2&&/[A-Z]/.test(e[2])?e[2].toLowerCase()+e.slice(3):``;if(t){let e=u(t);if(e?.config.type===Boolean&&e.config.negatable!==!1)return e}}function y(e){if(e.charCodeAt(0)!==45)return!1;let t=e.length;if(t<2)return!1;let n=e.charCodeAt(1);if(m(n))return!0;if(h(n)){let t=n!==45;return!!u(t?e[1]:e.slice(2),t)}return n===45&&t>2?m(e.charCodeAt(2)):!1}function b(e){if(e.charCodeAt(1)===45){let{rawName:t}=v(e.slice(2),i);return!!(u(t,!1)||d(t))}let t=e.slice(1);for(let e of t)if(!u(e,!0))return!1;return!0}return{parse:t=>{let r={parameters:[],doubleDash:[],flags:{},raw:t,unknown:{},ignored:[],missingRequiredFlags:[]};s(t,r,y,b,a,({current:n,eat:a,exit:o,hasNext:s,index:c,next:l,shouldIgnore:f})=>{if(n===`--`){r.doubleDash.push(...t.slice(c+1)),o(!1);return}if(f(n)){r.ignored.push(n),o();return}if(!y(n)){r.parameters.push(n);return}let m=!n.startsWith(`--`),h=n.slice(m?1:2);if(m){let e=h.length;for(let t=0;t<e;t++){let n=h[t],i=u(n,!0);if(!i){r.unknown[n]=!0;continue}let{key:o,config:c}=i,d=c.type;if(d===Boolean||p(d,Boolean))g(r.flags,o,`true`,c);else if(t+1<e){g(r.flags,o,h.slice(t+1),c);break}else{let e=s&&!y(l)?a()??``:``;g(r.flags,o,e,c)}}}else{let{rawName:t,rawValue:n,hasSep:o}=v(h,i),c=u(t),f=!1;if(!c){let e=d(t);e&&(c=e,f=!0)}if(!c){let i=e(t);if(o)r.unknown[i]=n;else if(s&&!y(l)){let e=a();r.unknown[i]=e??!0}else r.unknown[i]=!0;return}let{key:p,config:m,path:b}=c;if(b){if(m.type===Object){r.flags[p]??={};let e=o?n:s&&!y(l)?a()??``:``;_(r.flags[p],b,e)}}else if(m.type===Boolean){let e=o?n!==`false`:!0;r.flags[p]=f?!e:e}else{let e=o?n:s&&!y(l)?a()??``:``;g(r.flags,p,e,m)}}});for(let[e,t]of o.entries())r.flags[e]===void 0&&(t.default===void 0?Array.isArray(t.type)?r.flags[e]=p(t.type,Boolean)?0:[]:t.type===Object?r.flags[e]={}:t.type===Boolean?r.flags[e]=!1:t.required&&r.missingRequiredFlags.push(e):r.flags[e]=n(t.default));return r}}}const x=(e,t={})=>b(t).parse(e);export{y as DOUBLE_DASH,r as InvalidSchemaError,i as KNOWN_FLAG,o as PARAMETER,a as UNKNOWN_FLAG,b as createParser,x as parse};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clerc/parser",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "author": "Ray <i@mk1.io> (https://github.com/so1ve)",
5
5
  "type": "module",
6
6
  "description": "Clerc parser",
@@ -40,7 +40,7 @@
40
40
  "access": "public"
41
41
  },
42
42
  "dependencies": {
43
- "@clerc/utils": "1.1.0"
43
+ "@clerc/utils": "1.1.1"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/minimist": "^1.2.5",