@iebh/reflib 2.7.2 → 2.8.0

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.
package/README.md CHANGED
@@ -30,6 +30,29 @@ Compatibility
30
30
  * Medline seems to implement a totally different [publication type system](https://www.nlm.nih.gov/mesh/pubtypes.html) than others. Reflib will attempt to guess the best match, storing the original type in the `medlineType` key. Should the citation library be exported _back_ to Medline / `.nbib` files this key will take precedence to avoid data loss
31
31
 
32
32
 
33
+ Command Line Interface
34
+ ======================
35
+ This NPM ships with a very basic Command Line Interface (CLI) for very basic Reflib files manipulation.
36
+
37
+ ```
38
+ Usage: reflib -i INPUT_FILE [-f FORMAT] [-o -|OUTPUT_FILE]
39
+
40
+
41
+ -i, --input <file> Input file to process
42
+
43
+ -o, --output <file> Output file to save. Use '-' for STDOUT
44
+
45
+ -f, --format <reflib-format> Override or set the file output type (if omitted the outfile filename
46
+ is used to determine the format)
47
+
48
+ -v, --verbose Be verbose when processing
49
+
50
+ --version Print CLI version and exit
51
+
52
+ -h, --help This help screen
53
+ ```
54
+
55
+
33
56
  Reference Structure
34
57
  ===================
35
58
  Reflib creates a simple Plain-Old-JavaScript-Object (POJO) for each reference it parses, or writes to a file format when given a collection of the same.
package/app.js ADDED
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/node
2
+
3
+ import parseArgs from './shared/parseArgs.js';
4
+ import packageInfo from './package.json' with {type: 'json'};
5
+ import reflib from './lib/default.js';
6
+
7
+ let help = `
8
+
9
+ Usage: reflib -i INPUT_FILE [-f FORMAT] [-o -|OUTPUT_FILE]
10
+
11
+
12
+ -i, --input <file> Input file to process
13
+
14
+ -o, --output <file> Output file to save. Use '-' for STDOUT
15
+
16
+ -f, --format <reflib-format> Override or set the file output type (if omitted the outfile filename
17
+ is used to determine the format)
18
+
19
+ -v, --verbose Be verbose when processing
20
+
21
+ --version Print CLI version and exit
22
+
23
+ -h, --help This help screen
24
+ `;
25
+
26
+ // Parse args {{{
27
+ let args = parseArgs.parse();
28
+ args = parseArgs.expand(args, {
29
+ 'h': 'help',
30
+ 'i': 'input',
31
+ 'o': 'output',
32
+ 'f': 'format',
33
+ 'v': 'verbose',
34
+ });
35
+ // }}}
36
+
37
+ // Action: Version {{{
38
+ if (args.version) {
39
+ console.log(`Version: ${packageInfo.version}`);
40
+ process.exit(0);
41
+ }
42
+ // }}}
43
+ // Action: Convert (if --input) {{{
44
+ if (args.input) {
45
+ if (args.verbose) console.log('Reading', args.input);
46
+ let refs = await reflib.readFile(args.input);
47
+ if (args.verbose) console.log('Read', refs.length, 'refs');
48
+
49
+ if (!args.output) {
50
+ if (args.verbose) console.log('No output file specified - assuming STDOUT JSON');
51
+ console.log(JSON.stringify(refs, null, 2));
52
+ } else if (args.output == '-' || args.output === true) {
53
+ if (!args.format) {
54
+ if (args.verbose) console.log('No STDOUT format specified - assuming JSON');
55
+ console.log(JSON.stringify(refs, null, 2));
56
+ } else {
57
+ if (args.verbose) console.log(`Raw output to STDOUT using "${args.format}" format`);
58
+
59
+ let stream = reflib.writeStream(args.format, process.stdout);
60
+ await stream.start();
61
+ await Array.fromAsync(refs, ref =>
62
+ stream.write(ref)
63
+ );
64
+ await stream.end();
65
+ }
66
+ } else if (args.output) {
67
+ if (args.verbose) console.log('Writing', args.output);
68
+ await reflib.writeFile(args.output, refs);
69
+ }
70
+
71
+ if (args.verbose) console.log('All done');
72
+ process.exit(0);
73
+ }
74
+ // }}}
75
+ // Action: Help {{{
76
+ if (args.help) {
77
+ console.log(help);
78
+ process.exit(0);
79
+ }
80
+ // }}}
81
+ // Action: Unknown {{{
82
+ console.warn('Nothing to do');
83
+ console.log(help);
84
+ process.exit(1);
85
+ // }}}
package/package.json CHANGED
@@ -1,7 +1,10 @@
1
1
  {
2
2
  "name": "@iebh/reflib",
3
- "version": "2.7.2",
3
+ "version": "2.8.0",
4
4
  "description": "Reference / Citation reference library utilities",
5
+ "bin": {
6
+ "reflib": "./app.js"
7
+ },
5
8
  "scripts": {
6
9
  "lint": "eslint",
7
10
  "test": "testa",
@@ -59,6 +62,7 @@
59
62
  "@momsfriendlydevco/eslint-config": "^2.3.1",
60
63
  "@momsfriendlydevco/testa": "^1.1.2",
61
64
  "eslint": "^9.31.0",
65
+ "execa": "^9.6.1",
62
66
  "nodemon": "^3.1.9",
63
67
  "temp": "^0.9.4",
64
68
  "vite-plugin-replace": "^0.1.1"
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Attempt to parse an process.argv like array into extracted flags and their values
3
+ *
4
+ * Supported:
5
+ * `--flag` → `true`
6
+ * `--flag val` → `'val'`
7
+ * `--flag a b c` → `['a', 'b', 'c']` (variadic, stops at next flag)
8
+ * `-f` → `true`
9
+ * `-abc` → `{ a: true, b: true, c: true }`
10
+ * `-n 1 2 3` → `{ n: ['1', '2', '3'] }` (last short flag is variadic)
11
+ * `--` → remaining args go to `_`
12
+ * Bare positional args → `_` array
13
+ *
14
+ * @param {Array<String>} [argv=process.argv] `argv` like array to parse, If omitted `process.argv.slice(2)` is used
15
+ * @returns {Object} An object with extracted flags
16
+ *
17
+ * @example Basic argv example
18
+ * parseArgs([
19
+ * '--name', 'Alice', 'Bob', '--verbose', '-n', '42', '-xvf', 'file1', 'file2',
20
+ * ]) //= {
21
+ * _: [],
22
+ * name: ['Alice', 'Bob'],
23
+ * verbose: true,
24
+ * n: '42',
25
+ * x: true,
26
+ * v: true,
27
+ * f: ['file1', 'file2']
28
+ * }
29
+ */
30
+ export function parse(argv = process.argv.slice(2)) {
31
+ let result = { _: [] }
32
+ let i = 0
33
+
34
+ let collectValues = (args, start) => {
35
+ let values = []
36
+ let j = start
37
+ while (j < args.length && !args[j].startsWith('-')) {
38
+ values.push(args[j++])
39
+ }
40
+ return { values, next: j }
41
+ }
42
+
43
+ while (i < argv.length) {
44
+ let arg = argv[i]
45
+
46
+ if (arg.startsWith('--')) {
47
+ let key = arg.slice(2)
48
+ if (!key) { i++; break } // -- separator
49
+ let { values, next } = collectValues(argv, i + 1)
50
+ result[key] = values.length === 0 ? true
51
+ : values.length === 1 ? values[0]
52
+ : values
53
+ i = next
54
+ } else if (arg.startsWith('-')) {
55
+ let flags = arg.slice(1)
56
+ // Check if last char is the one collecting values
57
+ for (let f = 0; f < flags.length - 1; f++) {
58
+ result[flags[f]] = true
59
+ }
60
+ let lastFlag = flags.at(-1);
61
+ let { values, next } = collectValues(argv, i + 1);
62
+ result[lastFlag] = values.length === 0 ? true
63
+ : values.length === 1 ? values[0]
64
+ : values
65
+ i = next;
66
+ } else {
67
+ result._.push(arg)
68
+ i++
69
+ }
70
+ }
71
+
72
+ // Remaining after -- go to _
73
+ while (i < argv.length) result._.push(argv[i++])
74
+
75
+ return result
76
+ }
77
+
78
+
79
+ /**
80
+ * Accept a parsed args object and expand short-flags into long-flags
81
+ *
82
+ * @param {Object} args Parsed arg object to process
83
+ * @param {Object} argMap Object of `shortFlag:String => longFlag:String` translations to apply
84
+ *
85
+ * @returns {Object} The input `args` object with all short-flags translated to long-flags
86
+ */
87
+ export function expand(args, argMap) {
88
+ let outArgs = {...args}; // Shallow copy of incoming args object we are going to mutate
89
+
90
+ Object.entries(argMap)
91
+ .filter(([shortFlag]) => args[shortFlag])
92
+ .map(([shortFlag, longFlag]) => {
93
+ outArgs[longFlag] = outArgs[shortFlag];
94
+ delete outArgs[shortFlag];
95
+ });
96
+
97
+ return outArgs;
98
+ }
99
+
100
+
101
+ export default {
102
+ expand,
103
+ parse,
104
+ }