@iebh/reflib 2.5.6 → 2.5.8

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/lib/formats.js CHANGED
@@ -1,77 +1,77 @@
1
- /**
2
- * Lookup table of various citation file formats
3
- * @type {array<Object>} A collection of Reflib supported file formats
4
- * @property {string} title The long form title of the format
5
- * @property {string} titleShort Shorter title of the format
6
- * @property {string} input Input format required by parser
7
- * @property {string} output Output format required by formatter
8
- * @property {Array<String>} ext File extensions of this format, the first entry is generally used as the output default
9
- * @property {boolean} canRead Whether the format is supported when reading a citation library
10
- * @property {boolean} canWrite Whether the format is supported when writing a citation library
11
- */
12
- export let formats = {
13
- csv: {
14
- id: 'csv',
15
- title: 'Comma Seperated Values',
16
- titleShort: 'CSV',
17
- ext: ['.csv'],
18
- canRead: false,
19
- canWrite: false,
20
- },
21
- endnoteEnl: {
22
- id: 'endnoteEnl',
23
- title: 'EndNote ENL',
24
- titleShort: 'EndNote',
25
- ext: ['.enl'],
26
- canRead: true,
27
- canWrite: true,
28
- },
29
- endnoteEnlX: {
30
- id: 'endnoteEnlX',
31
- title: 'EndNote ENLX',
32
- titleShort: 'EndNote ENLX',
33
- ext: ['.enlx'],
34
- canRead: true,
35
- canWrite: false,
36
- },
37
- endnoteXml: {
38
- id: 'endnoteXml',
39
- title: 'EndNoteXML',
40
- titleShort: 'EndNoteXML',
41
- ext: ['.xml'],
42
- canRead: true,
43
- canWrite: true,
44
- },
45
- json: {
46
- id: 'json',
47
- title: 'JSON',
48
- titleShort: 'JSON',
49
- ext: ['.json'],
50
- canRead: true,
51
- canWrite: true,
52
- },
53
- medline: {
54
- id: 'medline',
55
- title: 'MEDLINE / PubMed',
56
- titleShort: 'MEDLINE',
57
- ext: ['.nbib'],
58
- canRead: true,
59
- canWrite: true,
60
- },
61
- ris: {
62
- id: 'ris',
63
- title: 'RIS',
64
- titleShort: 'RIS',
65
- ext: ['.ris','.txt','.cgi'],
66
- canRead: true,
67
- canWrite: true,
68
- },
69
- tsv: {
70
- id: 'tsv',
71
- title: 'Tab Seperated Values',
72
- titleShort: 'TSV',
73
- ext: ['.tsv'],
74
- canRead: false,
75
- canWrite: false,
76
- },
77
- }
1
+ /**
2
+ * Lookup table of various citation file formats
3
+ * @type {array<Object>} A collection of Reflib supported file formats
4
+ * @property {string} title The long form title of the format
5
+ * @property {string} titleShort Shorter title of the format
6
+ * @property {string} input Input format required by parser
7
+ * @property {string} output Output format required by formatter
8
+ * @property {Array<String>} ext File extensions of this format, the first entry is generally used as the output default
9
+ * @property {boolean} canRead Whether the format is supported when reading a citation library
10
+ * @property {boolean} canWrite Whether the format is supported when writing a citation library
11
+ */
12
+ export let formats = {
13
+ csv: {
14
+ id: 'csv',
15
+ title: 'Comma Seperated Values',
16
+ titleShort: 'CSV',
17
+ ext: ['.csv'],
18
+ canRead: false,
19
+ canWrite: false,
20
+ },
21
+ endnoteEnl: {
22
+ id: 'endnoteEnl',
23
+ title: 'EndNote ENL',
24
+ titleShort: 'EndNote',
25
+ ext: ['.enl'],
26
+ canRead: true,
27
+ canWrite: true,
28
+ },
29
+ endnoteEnlX: {
30
+ id: 'endnoteEnlX',
31
+ title: 'EndNote ENLX',
32
+ titleShort: 'EndNote ENLX',
33
+ ext: ['.enlx'],
34
+ canRead: true,
35
+ canWrite: false,
36
+ },
37
+ endnoteXml: {
38
+ id: 'endnoteXml',
39
+ title: 'EndNoteXML',
40
+ titleShort: 'EndNoteXML',
41
+ ext: ['.xml'],
42
+ canRead: true,
43
+ canWrite: true,
44
+ },
45
+ json: {
46
+ id: 'json',
47
+ title: 'JSON',
48
+ titleShort: 'JSON',
49
+ ext: ['.json'],
50
+ canRead: true,
51
+ canWrite: true,
52
+ },
53
+ medline: {
54
+ id: 'medline',
55
+ title: 'MEDLINE / PubMed',
56
+ titleShort: 'MEDLINE',
57
+ ext: ['.nbib'],
58
+ canRead: true,
59
+ canWrite: true,
60
+ },
61
+ ris: {
62
+ id: 'ris',
63
+ title: 'RIS',
64
+ titleShort: 'RIS',
65
+ ext: ['.ris','.txt','.cgi'],
66
+ canRead: true,
67
+ canWrite: true,
68
+ },
69
+ tsv: {
70
+ id: 'tsv',
71
+ title: 'Tab Seperated Values',
72
+ titleShort: 'TSV',
73
+ ext: ['.tsv'],
74
+ canRead: false,
75
+ canWrite: false,
76
+ },
77
+ }
package/lib/getModule.js CHANGED
@@ -1,39 +1,39 @@
1
- import * as modules from '../modules/default.js';
2
-
3
- let hasSetup = new Set(); // Modules we have already setup
4
-
5
- /**
6
- * Simple wrapper which loads the named module as a keyed lirary of functions
7
- *
8
- * @param {string} module The module ID as per `lib/formats.js`
9
- * @param {Object} [options] Additional options to use when fetching the module
10
- * @param {boolean} [options.setup=true] Call the `setup()` function on any module requested before use
11
- *
12
- * @returns {Object} The loaded module as an object of standardised functionality
13
- */
14
- export function getModule(module, options) {
15
- // Sanity checking
16
- if (!module) throw new Error('No module provided');
17
-
18
- // Argument mangling
19
- let settings = {
20
- setup: true,
21
- ...options,
22
- };
23
-
24
- // Try to find the module
25
- let mod = modules[module];
26
- if (!mod) throw new Error(`Unknown module "${module}"`);
27
-
28
- // Should setup and module exposes a setup function?
29
- if (
30
- mod.setup // We should set up...
31
- && settings.setup // AND the module has a function to do so...
32
- && !hasSetup.has(module) // AND we've not setup before
33
- ) {
34
- hasSetup.add(module);
35
- mod.setup();
36
- }
37
-
38
- return mod;
39
- }
1
+ import * as modules from '../modules/default.js';
2
+
3
+ let hasSetup = new Set(); // Modules we have already setup
4
+
5
+ /**
6
+ * Simple wrapper which loads the named module as a keyed lirary of functions
7
+ *
8
+ * @param {string} module The module ID as per `lib/formats.js`
9
+ * @param {Object} [options] Additional options to use when fetching the module
10
+ * @param {boolean} [options.setup=true] Call the `setup()` function on any module requested before use
11
+ *
12
+ * @returns {Object} The loaded module as an object of standardised functionality
13
+ */
14
+ export function getModule(module, options) {
15
+ // Sanity checking
16
+ if (!module) throw new Error('No module provided');
17
+
18
+ // Argument mangling
19
+ let settings = {
20
+ setup: true,
21
+ ...options,
22
+ };
23
+
24
+ // Try to find the module
25
+ let mod = modules[module];
26
+ if (!mod) throw new Error(`Unknown module "${module}"`);
27
+
28
+ // Should setup and module exposes a setup function?
29
+ if (
30
+ mod.setup // We should set up...
31
+ && settings.setup // AND the module has a function to do so...
32
+ && !hasSetup.has(module) // AND we've not setup before
33
+ ) {
34
+ hasSetup.add(module);
35
+ mod.setup();
36
+ }
37
+
38
+ return mod;
39
+ }
package/lib/getRefDoi.js CHANGED
@@ -1,16 +1,16 @@
1
- // @returns {Object} The modified ref object with the updated DOI
2
- /**
3
- * Identify DOI in the format(eg: 'https://dx.doi.org/10.1186/s40504-020-00106-2'), extract DOI number only(eg:'10.1186/s40504-020-00106-2')and return the ref object
4
- * @param {Object} ref The input object to identify and change DOI format
5
- *@returns {string|null} The modified DOI or null if no DOI is available
6
- */
7
- export function getRefDoi(ref) {
8
- if (ref.doi) {
9
- const doiPrefix = 'https://dx.doi.org/';
10
- if (ref.doi.startsWith(doiPrefix)) {
11
- ref.doi = ref.doi.slice(doiPrefix.length);
12
- }
13
- return ref.doi;
14
- }
15
- return null;
16
- }
1
+ // @returns {Object} The modified ref object with the updated DOI
2
+ /**
3
+ * Identify DOI in the format(eg: 'https://dx.doi.org/10.1186/s40504-020-00106-2'), extract DOI number only(eg:'10.1186/s40504-020-00106-2')and return the ref object
4
+ * @param {Object} ref The input object to identify and change DOI format
5
+ *@returns {string|null} The modified DOI or null if no DOI is available
6
+ */
7
+ export function getRefDoi(ref) {
8
+ if (ref.doi) {
9
+ const doiPrefix = 'https://dx.doi.org/';
10
+ if (ref.doi.startsWith(doiPrefix)) {
11
+ ref.doi = ref.doi.slice(doiPrefix.length);
12
+ }
13
+ return ref.doi;
14
+ }
15
+ return null;
16
+ }
@@ -1,13 +1,13 @@
1
- import {formats} from '../lib/formats.js';
2
-
3
- /**
4
- * Identify and return the Reflib format (from ./formats) to use for the given file name / path
5
- * @param {string} path The input path to identify
6
- * @returns {Object} A matching entry from ./formats or null if no matching format was found
7
- */
8
- export function identifyFormat(path) {
9
- let ext = /^.*(?<ext>\..+?)$/.exec(path)?.groups.ext.toLowerCase();
10
- if (!ext) return null;
11
-
12
- return Object.values(formats).find(format => format.ext.includes(ext));
13
- }
1
+ import {formats} from '../lib/formats.js';
2
+
3
+ /**
4
+ * Identify and return the Reflib format (from ./formats) to use for the given file name / path
5
+ * @param {string} path The input path to identify
6
+ * @returns {Object} A matching entry from ./formats or null if no matching format was found
7
+ */
8
+ export function identifyFormat(path) {
9
+ let ext = /^.*(?<ext>\..+?)$/.exec(path)?.groups.ext.toLowerCase();
10
+ if (!ext) return null;
11
+
12
+ return Object.values(formats).find(format => format.ext.includes(ext));
13
+ }
package/lib/readFile.js CHANGED
@@ -1,63 +1,63 @@
1
- import {createReadStream} from 'node:fs';
2
- import Emitter from '../shared/emitter.js';
3
- import {stat} from 'node:fs/promises';
4
- import {identifyFormat} from './identifyFormat.js';
5
- import {readStream} from './readStream.js';
6
-
7
- /**
8
- * Parse a file directly from a path
9
- * This function is a warpper around the readStream handler + some Promise magic
10
- * @param {string} path The file path to parse
11
- * @param {Object} [options] Additional options to pass to the parser
12
- * @param {string} [options.module] The module to use if overriding from the file path
13
- *
14
- * @returns {Promise<Array>} An eventual array of all references parsed from the file
15
- * @property {EventEmitter} emitter An event emitter which will fire the below events
16
- *
17
- * @emits progress Emitted as `({readBytes: Number, totalSize: Number, refsFound: Number})`
18
- * @emits end Emitted as `({refsFound: Number})` when the reading operation has completed
19
- */
20
- export function readFile(path, options) {
21
- let settings = {
22
- progressTotal: false,
23
- ...options,
24
- };
25
- let module = options?.module || identifyFormat(path)?.id;
26
- if (!module) throw new Error(`Unable to identify reference library format for file path "${path}"`);
27
-
28
- let promiseEmitter = Promise.resolve()
29
- .then(()=> stat(path))
30
- .then(stats => new Promise((resolve, reject) => {
31
- let refs = [];
32
-
33
- readStream(
34
- module,
35
- createReadStream(path),
36
- {
37
- ...settings,
38
- size: stats.size,
39
- },
40
- )
41
- .on('end', ()=> resolve(refs))
42
- .on('error', reject)
43
- .on('ref', ref => refs.push(ref))
44
- .on('progress', readBytes => promiseEmitter.emitter.emit('progress', {
45
- readBytes,
46
- totalSize: stats.size,
47
- refsFound: refs.length,
48
- }))
49
- }))
50
- .then(refs => {
51
- promiseEmitter.emitter.emit('end', {refsFound: refs.length})
52
- return refs;
53
- })
54
-
55
- // Extend our base promise with an emitter subkey
56
- return Object.defineProperties(promiseEmitter, {
57
- emitter: {
58
- value: Emitter(),
59
- enumerable: true,
60
- writable: false,
61
- },
62
- });
63
- }
1
+ import {createReadStream} from 'node:fs';
2
+ import Emitter from '../shared/emitter.js';
3
+ import {stat} from 'node:fs/promises';
4
+ import {identifyFormat} from './identifyFormat.js';
5
+ import {readStream} from './readStream.js';
6
+
7
+ /**
8
+ * Parse a file directly from a path
9
+ * This function is a warpper around the readStream handler + some Promise magic
10
+ * @param {string} path The file path to parse
11
+ * @param {Object} [options] Additional options to pass to the parser
12
+ * @param {string} [options.module] The module to use if overriding from the file path
13
+ *
14
+ * @returns {Promise<Array>} An eventual array of all references parsed from the file
15
+ * @property {EventEmitter} emitter An event emitter which will fire the below events
16
+ *
17
+ * @emits progress Emitted as `({readBytes: Number, totalSize: Number, refsFound: Number})`
18
+ * @emits end Emitted as `({refsFound: Number})` when the reading operation has completed
19
+ */
20
+ export function readFile(path, options) {
21
+ let settings = {
22
+ progressTotal: false,
23
+ ...options,
24
+ };
25
+ let module = options?.module || identifyFormat(path)?.id;
26
+ if (!module) throw new Error(`Unable to identify reference library format for file path "${path}"`);
27
+
28
+ let promiseEmitter = Promise.resolve()
29
+ .then(()=> stat(path))
30
+ .then(stats => new Promise((resolve, reject) => {
31
+ let refs = [];
32
+
33
+ readStream(
34
+ module,
35
+ createReadStream(path),
36
+ {
37
+ ...settings,
38
+ size: stats.size,
39
+ },
40
+ )
41
+ .on('end', ()=> resolve(refs))
42
+ .on('error', reject)
43
+ .on('ref', ref => refs.push(ref))
44
+ .on('progress', readBytes => promiseEmitter.emitter.emit('progress', {
45
+ readBytes,
46
+ totalSize: stats.size,
47
+ refsFound: refs.length,
48
+ }))
49
+ }))
50
+ .then(refs => {
51
+ promiseEmitter.emitter.emit('end', {refsFound: refs.length})
52
+ return refs;
53
+ })
54
+
55
+ // Extend our base promise with an emitter subkey
56
+ return Object.defineProperties(promiseEmitter, {
57
+ emitter: {
58
+ value: Emitter(),
59
+ enumerable: true,
60
+ writable: false,
61
+ },
62
+ });
63
+ }
package/lib/readStream.js CHANGED
@@ -1,21 +1,21 @@
1
- import {getModule} from './getModule.js';
2
-
3
- /**
4
- * Parse an input stream via a given format ID
5
- * This function is really just a multiplexor around each modules `readStream` export
6
- * @param {string} module The module ID as per `lib/formats.js`
7
- * @param {Stream.Readable} stream Input stream to parse
8
- * @param {Object} [options] Additional options to pass to the parser
9
- * @param {number} [options.size] Size of the input stream, if omitted `progress` events are not emitted
10
- * @returns {EventEmitter} An Event-Emitter compatible object which will fire various events while parsing
11
- *
12
- * @emits ref Emitted with an extracted reference object during parse
13
- * @emits end Emitted when the parsing has completed
14
- * @emits error Emitted with an Error object if any occured
15
- */
16
- export function readStream(module, stream, options) {
17
- if (!module) throw new Error('No module provided to parse with');
18
- if (!stream) throw new Error('No stream provided to parse');
19
-
20
- return getModule(module).readStream(stream, options);
21
- }
1
+ import {getModule} from './getModule.js';
2
+
3
+ /**
4
+ * Parse an input stream via a given format ID
5
+ * This function is really just a multiplexor around each modules `readStream` export
6
+ * @param {string} module The module ID as per `lib/formats.js`
7
+ * @param {Stream.Readable} stream Input stream to parse
8
+ * @param {Object} [options] Additional options to pass to the parser
9
+ * @param {number} [options.size] Size of the input stream, if omitted `progress` events are not emitted
10
+ * @returns {EventEmitter} An Event-Emitter compatible object which will fire various events while parsing
11
+ *
12
+ * @emits ref Emitted with an extracted reference object during parse
13
+ * @emits end Emitted when the parsing has completed
14
+ * @emits error Emitted with an Error object if any occured
15
+ */
16
+ export function readStream(module, stream, options) {
17
+ if (!module) throw new Error('No module provided to parse with');
18
+ if (!stream) throw new Error('No stream provided to parse');
19
+
20
+ return getModule(module).readStream(stream, options);
21
+ }
package/lib/uploadFile.js CHANGED
@@ -1,71 +1,71 @@
1
- import {formats} from './formats.js';
2
- import {identifyFormat} from './identifyFormat.js';
3
- import {readStream} from './readStream.js';
4
- import StreamEmitter from '../shared/streamEmitter.js';
5
-
6
- /**
7
- * Prompt the user for a file then read it as a Reflib event emitter
8
- * @param {Object} [options] Additional options when prompting the user
9
- * @param {File} [options.file] The File object to process, omitting this will prompt the user to select a file
10
- * @param {function} [options.onStart] Async function called as `(File)` when starting the read stage
11
- * @param {function} [options.onProgress] Function called as `(position, totalSize, refCount)` when processing the file
12
- * @param {function} [options.onEnd] Async function called as `()` when the read stage has completed
13
- * @param {...*} [options...] Additional settings to pass to `readStream()`
14
- * @returns {Promise} A promise which will resolve with an array of extracted citations
15
- */
16
- export function uploadFile(options) {
17
- let settings = {...options};
18
-
19
- if (!settings.file) { // No file provided - prompt the user via the DOM
20
- // Horrible kludge to create an upload element in the DOM + interact with it {{{
21
- return new Promise(resolve => {
22
- // Create hidden layer we will use to wrap the actual file upload input box
23
- let fileWrapper = document.createElement('div');
24
- fileWrapper.style.display = 'none';
25
- document.body.appendChild(fileWrapper);
26
-
27
- // Create upload input
28
- let uploader = document.createElement('input');
29
- uploader.type = 'file';
30
- uploader.accept = Object.values(formats) // Allow only uploading supported file extensions
31
- .flatMap(f => f.ext)
32
- .join(',');
33
-
34
- uploader.addEventListener('change', e => {
35
- document.body.removeChild(fileWrapper);
36
- resolve(uploadFile({
37
- file: e.target.files[0],
38
- ...options,
39
- }));
40
- });
41
- fileWrapper.appendChild(uploader);
42
- uploader.dispatchEvent(new MouseEvent('click'));
43
- });
44
- // }}}
45
- } else { // Read the File object and return an emitter
46
- if (!(settings.file instanceof File)) throw new Error('Expected "file" setting to uploadFile() to be a File type');
47
- let identifiedType = identifyFormat(settings.file.name);
48
- if (!identifiedType) throw new Error(`Unidenfified file format from filename "${settings.file.name}"`);
49
-
50
- let refs = [];
51
- return Promise.resolve()
52
- .then(()=> settings.onStart && settings.onStart(settings.file))
53
- .then(()=> new Promise((resolve, reject) => {
54
- let streamer = readStream(
55
- identifiedType.id,
56
- StreamEmitter(settings.file.stream()),
57
- {
58
- ...settings,
59
- size: settings.file.size,
60
- },
61
- )
62
- .on('end', ()=> resolve(refs))
63
- .on('error', e => reject(e))
64
- .on('ref', ref => refs.push(ref))
65
-
66
- if (settings.onProgress) streamer.on('progress', settings.onProgress);
67
- }))
68
- .then(()=> settings.onEnd && settings.onEnd())
69
- .then(()=> refs)
70
- }
71
- }
1
+ import {formats} from './formats.js';
2
+ import {identifyFormat} from './identifyFormat.js';
3
+ import {readStream} from './readStream.js';
4
+ import StreamEmitter from '../shared/streamEmitter.js';
5
+
6
+ /**
7
+ * Prompt the user for a file then read it as a Reflib event emitter
8
+ * @param {Object} [options] Additional options when prompting the user
9
+ * @param {File} [options.file] The File object to process, omitting this will prompt the user to select a file
10
+ * @param {function} [options.onStart] Async function called as `(File)` when starting the read stage
11
+ * @param {function} [options.onProgress] Function called as `(position, totalSize, refCount)` when processing the file
12
+ * @param {function} [options.onEnd] Async function called as `()` when the read stage has completed
13
+ * @param {...*} [options...] Additional settings to pass to `readStream()`
14
+ * @returns {Promise} A promise which will resolve with an array of extracted citations
15
+ */
16
+ export function uploadFile(options) {
17
+ let settings = {...options};
18
+
19
+ if (!settings.file) { // No file provided - prompt the user via the DOM
20
+ // Horrible kludge to create an upload element in the DOM + interact with it {{{
21
+ return new Promise(resolve => {
22
+ // Create hidden layer we will use to wrap the actual file upload input box
23
+ let fileWrapper = document.createElement('div');
24
+ fileWrapper.style.display = 'none';
25
+ document.body.appendChild(fileWrapper);
26
+
27
+ // Create upload input
28
+ let uploader = document.createElement('input');
29
+ uploader.type = 'file';
30
+ uploader.accept = Object.values(formats) // Allow only uploading supported file extensions
31
+ .flatMap(f => f.ext)
32
+ .join(',');
33
+
34
+ uploader.addEventListener('change', e => {
35
+ document.body.removeChild(fileWrapper);
36
+ resolve(uploadFile({
37
+ file: e.target.files[0],
38
+ ...options,
39
+ }));
40
+ });
41
+ fileWrapper.appendChild(uploader);
42
+ uploader.dispatchEvent(new MouseEvent('click'));
43
+ });
44
+ // }}}
45
+ } else { // Read the File object and return an emitter
46
+ if (!(settings.file instanceof File)) throw new Error('Expected "file" setting to uploadFile() to be a File type');
47
+ let identifiedType = identifyFormat(settings.file.name);
48
+ if (!identifiedType) throw new Error(`Unidenfified file format from filename "${settings.file.name}"`);
49
+
50
+ let refs = [];
51
+ return Promise.resolve()
52
+ .then(()=> settings.onStart && settings.onStart(settings.file))
53
+ .then(()=> new Promise((resolve, reject) => {
54
+ let streamer = readStream(
55
+ identifiedType.id,
56
+ StreamEmitter(settings.file.stream()),
57
+ {
58
+ ...settings,
59
+ size: settings.file.size,
60
+ },
61
+ )
62
+ .on('end', ()=> resolve(refs))
63
+ .on('error', e => reject(e))
64
+ .on('ref', ref => refs.push(ref))
65
+
66
+ if (settings.onProgress) streamer.on('progress', settings.onProgress);
67
+ }))
68
+ .then(()=> settings.onEnd && settings.onEnd())
69
+ .then(()=> refs)
70
+ }
71
+ }