@iebh/reflib 2.7.2 → 2.8.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.
- package/.ignore +1 -1
- package/LICENSE +20 -20
- package/README.md +297 -274
- package/app.js +87 -0
- package/lib/browser.js +30 -30
- package/lib/default.js +30 -30
- package/lib/downloadFile.js +94 -94
- package/lib/fields.js +158 -158
- package/lib/formats.js +85 -85
- package/lib/getModule.js +39 -39
- package/lib/getRefDoi.js +16 -16
- package/lib/identifyFormat.js +13 -13
- package/lib/readFile.js +63 -63
- package/lib/readStream.js +21 -21
- package/lib/uploadFile.js +71 -71
- package/lib/writeFile.js +32 -32
- package/lib/writeStream.js +16 -16
- package/modules/bibtex.js +401 -401
- package/modules/default.js +7 -7
- package/modules/endnoteEnl.js +237 -237
- package/modules/endnoteEnlX.js +85 -85
- package/modules/endnoteXml.js +410 -474
- package/modules/interface.js +47 -47
- package/modules/json.js +109 -79
- package/modules/medline.js +638 -638
- package/modules/ris.js +383 -383
- package/modules/shims/JSONStream-browser.js +43 -43
- package/modules/shims/WritableStream-browser.js +52 -52
- package/package.json +68 -66
- package/shared/camelCase.js +17 -17
- package/shared/emitter.js +23 -23
- package/shared/parseArgs.js +104 -0
- package/shared/streamEmitter.js +61 -61
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
import Emitter from '../../shared/emitter.js';
|
|
2
|
-
|
|
3
|
-
export default class BrowserJSONStream {
|
|
4
|
-
constructor() {
|
|
5
|
-
this.text = '';
|
|
6
|
-
this.emitter = Emitter();
|
|
7
|
-
this.recNumber = 1;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
write(data) {
|
|
11
|
-
// CF: TODO: Parse data as it comes in chunks for better memory efficiency
|
|
12
|
-
this.text += data;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
end() {
|
|
16
|
-
try {
|
|
17
|
-
// Parse this.text as JSON
|
|
18
|
-
const jsonArray = JSON.parse(this.text);
|
|
19
|
-
|
|
20
|
-
// Free memory
|
|
21
|
-
this.text = '';
|
|
22
|
-
|
|
23
|
-
// For each entry in the json array (as ref):
|
|
24
|
-
if (Array.isArray(jsonArray))
|
|
25
|
-
jsonArray.forEach(ref => {
|
|
26
|
-
this.emitter.emit('data', ref);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
// Finished
|
|
30
|
-
this.emitter.emit('end');
|
|
31
|
-
} catch (e) {
|
|
32
|
-
console.error('Error parsing final JSON:', e);
|
|
33
|
-
this.emitter.emit('error', e);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
on(event, listener) {
|
|
38
|
-
this.emitter.on(event, listener);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
static parse() {
|
|
42
|
-
return new BrowserJSONStream();
|
|
43
|
-
}
|
|
1
|
+
import Emitter from '../../shared/emitter.js';
|
|
2
|
+
|
|
3
|
+
export default class BrowserJSONStream {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.text = '';
|
|
6
|
+
this.emitter = Emitter();
|
|
7
|
+
this.recNumber = 1;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
write(data) {
|
|
11
|
+
// CF: TODO: Parse data as it comes in chunks for better memory efficiency
|
|
12
|
+
this.text += data;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
end() {
|
|
16
|
+
try {
|
|
17
|
+
// Parse this.text as JSON
|
|
18
|
+
const jsonArray = JSON.parse(this.text);
|
|
19
|
+
|
|
20
|
+
// Free memory
|
|
21
|
+
this.text = '';
|
|
22
|
+
|
|
23
|
+
// For each entry in the json array (as ref):
|
|
24
|
+
if (Array.isArray(jsonArray))
|
|
25
|
+
jsonArray.forEach(ref => {
|
|
26
|
+
this.emitter.emit('data', ref);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Finished
|
|
30
|
+
this.emitter.emit('end');
|
|
31
|
+
} catch (e) {
|
|
32
|
+
console.error('Error parsing final JSON:', e);
|
|
33
|
+
this.emitter.emit('error', e);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
on(event, listener) {
|
|
38
|
+
this.emitter.on(event, listener);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static parse() {
|
|
42
|
+
return new BrowserJSONStream();
|
|
43
|
+
}
|
|
44
44
|
}
|
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
import Emitter from '../../shared/emitter.js';
|
|
2
|
-
import CacxParser from '@iebh/cacx';
|
|
3
|
-
|
|
4
|
-
export class WritableStream {
|
|
5
|
-
constructor(passedParserOptions) {
|
|
6
|
-
this.emitter = Emitter();
|
|
7
|
-
this.parser = new CacxParser({
|
|
8
|
-
collect: false,
|
|
9
|
-
reAttrSegment: /(?<key>[a-zA-Z_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*(?<escape>["'])(?<val>.*?)\k<escape>)?/ig,
|
|
10
|
-
onTagOpen: (node) => {
|
|
11
|
-
const name = node.tag;
|
|
12
|
-
const attrs = node.attrs || {};
|
|
13
|
-
this.emitter.emit('opentag', name, attrs);
|
|
14
|
-
},
|
|
15
|
-
onTagClose: (node) => {
|
|
16
|
-
const name = node.tag;
|
|
17
|
-
// Emit text event before closing the tag
|
|
18
|
-
if (node.text && node.text.trim()) {
|
|
19
|
-
this.emitter.emit('text', node.text.trim());
|
|
20
|
-
}
|
|
21
|
-
this.emitter.emit('closetag', name);
|
|
22
|
-
},
|
|
23
|
-
flattenText: false,
|
|
24
|
-
keyText: 'text',
|
|
25
|
-
keyAttrs: 'attrs',
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
// Add event listeners
|
|
29
|
-
this.emitter.on('opentag', passedParserOptions.onopentag);
|
|
30
|
-
this.emitter.on('closetag', passedParserOptions.onclosetag);
|
|
31
|
-
this.emitter.on('text', passedParserOptions.ontext);
|
|
32
|
-
this.emitter.on('end', passedParserOptions.onend);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
write(data) {
|
|
36
|
-
this.parser.append(data).exec();
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
end() {
|
|
40
|
-
// Process any remaining data
|
|
41
|
-
this.parser.exec();
|
|
42
|
-
|
|
43
|
-
// Emit text event for the last node if it has text
|
|
44
|
-
const lastNode = this.parser.stack.at(-1);
|
|
45
|
-
if (lastNode && lastNode.text && lastNode.text.trim()) {
|
|
46
|
-
this.emitter.emit('text', lastNode.text.trim());
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Emit end event
|
|
50
|
-
this.emitter.emit('end');
|
|
51
|
-
}
|
|
52
|
-
}
|
|
1
|
+
import Emitter from '../../shared/emitter.js';
|
|
2
|
+
import CacxParser from '@iebh/cacx';
|
|
3
|
+
|
|
4
|
+
export class WritableStream {
|
|
5
|
+
constructor(passedParserOptions) {
|
|
6
|
+
this.emitter = Emitter();
|
|
7
|
+
this.parser = new CacxParser({
|
|
8
|
+
collect: false,
|
|
9
|
+
reAttrSegment: /(?<key>[a-zA-Z_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*(?<escape>["'])(?<val>.*?)\k<escape>)?/ig,
|
|
10
|
+
onTagOpen: (node) => {
|
|
11
|
+
const name = node.tag;
|
|
12
|
+
const attrs = node.attrs || {};
|
|
13
|
+
this.emitter.emit('opentag', name, attrs);
|
|
14
|
+
},
|
|
15
|
+
onTagClose: (node) => {
|
|
16
|
+
const name = node.tag;
|
|
17
|
+
// Emit text event before closing the tag
|
|
18
|
+
if (node.text && node.text.trim()) {
|
|
19
|
+
this.emitter.emit('text', node.text.trim());
|
|
20
|
+
}
|
|
21
|
+
this.emitter.emit('closetag', name);
|
|
22
|
+
},
|
|
23
|
+
flattenText: false,
|
|
24
|
+
keyText: 'text',
|
|
25
|
+
keyAttrs: 'attrs',
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Add event listeners
|
|
29
|
+
this.emitter.on('opentag', passedParserOptions.onopentag);
|
|
30
|
+
this.emitter.on('closetag', passedParserOptions.onclosetag);
|
|
31
|
+
this.emitter.on('text', passedParserOptions.ontext);
|
|
32
|
+
this.emitter.on('end', passedParserOptions.onend);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
write(data) {
|
|
36
|
+
this.parser.append(data).exec();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
end() {
|
|
40
|
+
// Process any remaining data
|
|
41
|
+
this.parser.exec();
|
|
42
|
+
|
|
43
|
+
// Emit text event for the last node if it has text
|
|
44
|
+
const lastNode = this.parser.stack.at(-1);
|
|
45
|
+
if (lastNode && lastNode.text && lastNode.text.trim()) {
|
|
46
|
+
this.emitter.emit('text', lastNode.text.trim());
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Emit end event
|
|
50
|
+
this.emitter.emit('end');
|
|
51
|
+
}
|
|
52
|
+
}
|
package/package.json
CHANGED
|
@@ -1,66 +1,68 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@iebh/reflib",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Reference / Citation reference library utilities",
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"@
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"mitt": "^3.0.1",
|
|
56
|
-
"sql.js": "^1.12.0"
|
|
57
|
-
},
|
|
58
|
-
"devDependencies": {
|
|
59
|
-
"@momsfriendlydevco/eslint-config": "^2.3.1",
|
|
60
|
-
"@momsfriendlydevco/testa": "^1.
|
|
61
|
-
"eslint": "^9.31.0",
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
|
|
66
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@iebh/reflib",
|
|
3
|
+
"version": "2.8.1",
|
|
4
|
+
"description": "Reference / Citation reference library utilities",
|
|
5
|
+
"bin": {
|
|
6
|
+
"reflib": "./app.js"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"lint": "eslint",
|
|
10
|
+
"test": "testa",
|
|
11
|
+
"test:browser": "cd test/browser && npm run dev",
|
|
12
|
+
"test:watch": "nodemon --exec npm run test"
|
|
13
|
+
},
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/IEBH/Reflib.git"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"reflib",
|
|
20
|
+
"references",
|
|
21
|
+
"citations",
|
|
22
|
+
"library"
|
|
23
|
+
],
|
|
24
|
+
"author": "Matt Carter <m@ttcarter.com> (https://github.com/hash-bang)",
|
|
25
|
+
"contributors": [
|
|
26
|
+
"Connor Forbes <cforbes.software@gmail.com> (https://github.com/connorf25)",
|
|
27
|
+
"Irene Priya Jose (https://github.com/s5333384)",
|
|
28
|
+
"Tian Liang (https://github.com/Octian)"
|
|
29
|
+
],
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/IEBH/Reflib/issues"
|
|
33
|
+
},
|
|
34
|
+
"homepage": "https://github.com/IEBH/Reflib",
|
|
35
|
+
"enginesStrict": true,
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=16.6.0"
|
|
38
|
+
},
|
|
39
|
+
"type": "module",
|
|
40
|
+
"exports": {
|
|
41
|
+
".": {
|
|
42
|
+
"browser": "./lib/browser.js",
|
|
43
|
+
"default": "./lib/default.js"
|
|
44
|
+
},
|
|
45
|
+
"./*": "./lib/*.js"
|
|
46
|
+
},
|
|
47
|
+
"browser": {
|
|
48
|
+
"JSONStream": "./modules/shims/JSONStream-browser.js"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@iebh/cacx": "^1.1.0",
|
|
52
|
+
"@momsfriendlydevco/sort-keys": "^1.0.0",
|
|
53
|
+
"@zip.js/zip.js": "^2.7.57",
|
|
54
|
+
"JSONStream": "^1.3.5",
|
|
55
|
+
"mitt": "^3.0.1",
|
|
56
|
+
"sql.js": "^1.12.0"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@momsfriendlydevco/eslint-config": "^2.3.1",
|
|
60
|
+
"@momsfriendlydevco/testa": "^1.2.0",
|
|
61
|
+
"eslint": "^9.31.0",
|
|
62
|
+
"execa": "^9.6.1",
|
|
63
|
+
"lodash-es": "^4.17.23",
|
|
64
|
+
"nodemon": "^3.1.9",
|
|
65
|
+
"temp": "^0.9.4",
|
|
66
|
+
"vite-plugin-replace": "^0.1.1"
|
|
67
|
+
}
|
|
68
|
+
}
|
package/shared/camelCase.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Camel case any input string
|
|
3
|
-
* This is functionally the same as Lodash's camelCase() function
|
|
4
|
-
*
|
|
5
|
-
* @param {string} input The input string to camelize
|
|
6
|
-
* @returns {string} The input string in camelCase format
|
|
7
|
-
* @url https://github.com/MomsFriendlyDevCo/Nodash
|
|
8
|
-
*/
|
|
9
|
-
export default function(input) {
|
|
10
|
-
return input
|
|
11
|
-
.split(/[\s-]/)
|
|
12
|
-
.map((word, offset) => offset == 0
|
|
13
|
-
? word.toLowerCase()
|
|
14
|
-
: word.substr(0, 1).toUpperCase() + word.substr(1).toLowerCase()
|
|
15
|
-
)
|
|
16
|
-
.join('')
|
|
17
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Camel case any input string
|
|
3
|
+
* This is functionally the same as Lodash's camelCase() function
|
|
4
|
+
*
|
|
5
|
+
* @param {string} input The input string to camelize
|
|
6
|
+
* @returns {string} The input string in camelCase format
|
|
7
|
+
* @url https://github.com/MomsFriendlyDevCo/Nodash
|
|
8
|
+
*/
|
|
9
|
+
export default function(input) {
|
|
10
|
+
return input
|
|
11
|
+
.split(/[\s-]/)
|
|
12
|
+
.map((word, offset) => offset == 0
|
|
13
|
+
? word.toLowerCase()
|
|
14
|
+
: word.substr(0, 1).toUpperCase() + word.substr(1).toLowerCase()
|
|
15
|
+
)
|
|
16
|
+
.join('')
|
|
17
|
+
}
|
package/shared/emitter.js
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import Mitt from 'mitt';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Generic wrapper for an event emitter
|
|
5
|
-
* This module returns a wrapped version of `mitt` stand-alone event emitter (+ support for method chaining)
|
|
6
|
-
*
|
|
7
|
-
* @returns {Object} A wrapped version of the NPM Mitt event emitter with chainable functionality
|
|
8
|
-
*/
|
|
9
|
-
export default function emitter() {
|
|
10
|
-
let emitter = Mitt();
|
|
11
|
-
|
|
12
|
-
// Add method chaining
|
|
13
|
-
emitter.mitt = {};
|
|
14
|
-
['on', 'off', 'emit'].forEach(f => {
|
|
15
|
-
emitter.mitt[f] = emitter[f]; // Backup old function into `.mitt.${F}`
|
|
16
|
-
emitter[f] = (...args) => {
|
|
17
|
-
emitter.mitt[f](...args);
|
|
18
|
-
return emitter;
|
|
19
|
-
};
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
return emitter;
|
|
23
|
-
}
|
|
1
|
+
import Mitt from 'mitt';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generic wrapper for an event emitter
|
|
5
|
+
* This module returns a wrapped version of `mitt` stand-alone event emitter (+ support for method chaining)
|
|
6
|
+
*
|
|
7
|
+
* @returns {Object} A wrapped version of the NPM Mitt event emitter with chainable functionality
|
|
8
|
+
*/
|
|
9
|
+
export default function emitter() {
|
|
10
|
+
let emitter = Mitt();
|
|
11
|
+
|
|
12
|
+
// Add method chaining
|
|
13
|
+
emitter.mitt = {};
|
|
14
|
+
['on', 'off', 'emit'].forEach(f => {
|
|
15
|
+
emitter.mitt[f] = emitter[f]; // Backup old function into `.mitt.${F}`
|
|
16
|
+
emitter[f] = (...args) => {
|
|
17
|
+
emitter.mitt[f](...args);
|
|
18
|
+
return emitter;
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
return emitter;
|
|
23
|
+
}
|
|
@@ -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
|
+
}
|
package/shared/streamEmitter.js
CHANGED
|
@@ -1,61 +1,61 @@
|
|
|
1
|
-
import Emitter from '../shared/emitter.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Wrapper for streams which transforms a given input into an emitter pattern
|
|
5
|
-
* This is designed to let regular `node:stream.Readable` objects pass through without alteration but browser based stream objects get wrapped
|
|
6
|
-
* @param {stream.Readable|ReadableStream} inStream The input stream to wrap
|
|
7
|
-
* @returns {stream.Readable|Emitter} Either the unedited node compatible stream or an event emitter with the same behaviour
|
|
8
|
-
*
|
|
9
|
-
* @emits data Emitted as `(chunk)` on each data chunk
|
|
10
|
-
* @emits end Emitted as `()` when the input stream has closed
|
|
11
|
-
* @emits error Emitted as `(Error)` on any read error
|
|
12
|
-
*/
|
|
13
|
-
export default function streamEmitter (inStream) {
|
|
14
|
-
if (inStream.getReader) { // Assume browser compatible ReadableStream
|
|
15
|
-
/**
|
|
16
|
-
* MC's tiny ReadableStream -> stream.Readable / Emitter pattern
|
|
17
|
-
* There is a non-zero chance that this is going to break at some future point
|
|
18
|
-
* This is really just a shiv for browser functionality to replicate Stream-a-like emitter pattern
|
|
19
|
-
* @date 2023-10-19
|
|
20
|
-
* @author Matt Carter <m@ttcarter.com>
|
|
21
|
-
*/
|
|
22
|
-
let reader = new Emitter();
|
|
23
|
-
Object.assign(reader, {
|
|
24
|
-
isBrowser: true, // Tells us we are in browser env
|
|
25
|
-
bytesRead: 0,
|
|
26
|
-
reader: inStream.getReader(),
|
|
27
|
-
textDecoder: new TextDecoder('utf-8'),
|
|
28
|
-
read() { // Read one chunk + trigger emitters
|
|
29
|
-
this.reader.read()
|
|
30
|
-
.then(({done, value}) => {
|
|
31
|
-
if (done) {
|
|
32
|
-
reader.emit('end');
|
|
33
|
-
|
|
34
|
-
if (this.pipeTarget)
|
|
35
|
-
this.pipeTarget.end();
|
|
36
|
-
} else if (value) {
|
|
37
|
-
let data = this.textDecoder.decode(value);
|
|
38
|
-
this.bytesRead += data.length;
|
|
39
|
-
reader.emit('data', data);
|
|
40
|
-
|
|
41
|
-
if (this.pipeTarget)
|
|
42
|
-
this.pipeTarget.write(data);
|
|
43
|
-
|
|
44
|
-
setTimeout(this.read.bind(this));
|
|
45
|
-
}
|
|
46
|
-
})
|
|
47
|
-
.catch(e => this.emit('error', e))
|
|
48
|
-
},
|
|
49
|
-
pipeTarget: null,
|
|
50
|
-
pipe(target) {
|
|
51
|
-
this.pipeTarget = target;
|
|
52
|
-
return this;
|
|
53
|
-
},
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
setTimeout(()=> reader.read());
|
|
57
|
-
return reader;
|
|
58
|
-
} else { // Assume Node native stream.Readable
|
|
59
|
-
return inStream;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
1
|
+
import Emitter from '../shared/emitter.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wrapper for streams which transforms a given input into an emitter pattern
|
|
5
|
+
* This is designed to let regular `node:stream.Readable` objects pass through without alteration but browser based stream objects get wrapped
|
|
6
|
+
* @param {stream.Readable|ReadableStream} inStream The input stream to wrap
|
|
7
|
+
* @returns {stream.Readable|Emitter} Either the unedited node compatible stream or an event emitter with the same behaviour
|
|
8
|
+
*
|
|
9
|
+
* @emits data Emitted as `(chunk)` on each data chunk
|
|
10
|
+
* @emits end Emitted as `()` when the input stream has closed
|
|
11
|
+
* @emits error Emitted as `(Error)` on any read error
|
|
12
|
+
*/
|
|
13
|
+
export default function streamEmitter (inStream) {
|
|
14
|
+
if (inStream.getReader) { // Assume browser compatible ReadableStream
|
|
15
|
+
/**
|
|
16
|
+
* MC's tiny ReadableStream -> stream.Readable / Emitter pattern
|
|
17
|
+
* There is a non-zero chance that this is going to break at some future point
|
|
18
|
+
* This is really just a shiv for browser functionality to replicate Stream-a-like emitter pattern
|
|
19
|
+
* @date 2023-10-19
|
|
20
|
+
* @author Matt Carter <m@ttcarter.com>
|
|
21
|
+
*/
|
|
22
|
+
let reader = new Emitter();
|
|
23
|
+
Object.assign(reader, {
|
|
24
|
+
isBrowser: true, // Tells us we are in browser env
|
|
25
|
+
bytesRead: 0,
|
|
26
|
+
reader: inStream.getReader(),
|
|
27
|
+
textDecoder: new TextDecoder('utf-8'),
|
|
28
|
+
read() { // Read one chunk + trigger emitters
|
|
29
|
+
this.reader.read()
|
|
30
|
+
.then(({done, value}) => {
|
|
31
|
+
if (done) {
|
|
32
|
+
reader.emit('end');
|
|
33
|
+
|
|
34
|
+
if (this.pipeTarget)
|
|
35
|
+
this.pipeTarget.end();
|
|
36
|
+
} else if (value) {
|
|
37
|
+
let data = this.textDecoder.decode(value);
|
|
38
|
+
this.bytesRead += data.length;
|
|
39
|
+
reader.emit('data', data);
|
|
40
|
+
|
|
41
|
+
if (this.pipeTarget)
|
|
42
|
+
this.pipeTarget.write(data);
|
|
43
|
+
|
|
44
|
+
setTimeout(this.read.bind(this));
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
.catch(e => this.emit('error', e))
|
|
48
|
+
},
|
|
49
|
+
pipeTarget: null,
|
|
50
|
+
pipe(target) {
|
|
51
|
+
this.pipeTarget = target;
|
|
52
|
+
return this;
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
setTimeout(()=> reader.read());
|
|
57
|
+
return reader;
|
|
58
|
+
} else { // Assume Node native stream.Readable
|
|
59
|
+
return inStream;
|
|
60
|
+
}
|
|
61
|
+
}
|