jats-cli 0.0.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 +89 -0
- package/dist/convert.d.ts +3 -0
- package/dist/convert.d.ts.map +1 -0
- package/dist/convert.js +13 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/jats-test.d.ts +9 -0
- package/dist/jats-test.d.ts.map +1 -0
- package/dist/jats-test.js +106 -0
- package/dist/jats.cjs +26164 -0
- package/dist/parse.d.ts +7 -0
- package/dist/parse.d.ts.map +1 -0
- package/dist/parse.js +198 -0
- package/dist/validate.d.ts +3 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +24 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +2 -0
- package/package.json +70 -0
package/README.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# jats-cli
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/jats-cli)
|
|
4
|
+
[](https://github.com/curvenote/jats-cli/blob/main/LICENSE)
|
|
5
|
+
[](https://github.com/curvenote/jats-cli/actions)
|
|
6
|
+
|
|
7
|
+
Node CLI for working with JATS XML documents.
|
|
8
|
+
|
|
9
|
+
Read, write, and convert JATS XML and log summaries from the command line.
|
|
10
|
+
|
|
11
|
+
To use from the command line, use the `-g` to create a global install, which will provide a `jats` CLI:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
npm install -g jats-xml
|
|
15
|
+
jats -v
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## What is JATS?
|
|
19
|
+
|
|
20
|
+
JATS is a NISO standard for Journal Article Tags Schema, which is a way to define the XML structure of a scientific article semantically. This includes the `front`-matter (authors, funding, title, abstract, etc.), the `body` of the article (sections, figures, equations, tables, etc.), and `back`-matter (references, footnotes, etc.). The JATS can also contain `sub-articles`.
|
|
21
|
+
|
|
22
|
+
The standard documents are hosted by the NIH <https://jats.nlm.nih.gov/>. There are three flavours, this library currently uses in most cases the most precriptive tag set (for article authoring). Another helpful resource is <https://jats4r.org/>, which provides other examples and recomendations for JATS.
|
|
23
|
+
|
|
24
|
+
Note that most publishers do **not** provide the XML as a first class output - they should, it is an important part of open-science to have the content programatically accessible and interoperable. It is only [FAIR](https://www.go-fair.org/fair-principles/) 😉.
|
|
25
|
+
|
|
26
|
+
## From the command line
|
|
27
|
+
|
|
28
|
+
Commands available:
|
|
29
|
+
|
|
30
|
+
`download`: attempt to find the JATS file and download it locally.
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
jats download https://elifesciences.org/articles/81952 article.jats
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Note, currently this just downloads the XML, **not** the associated files.
|
|
37
|
+
|
|
38
|
+
`summary`: summarize the contents of the JATS, given a URL, DOI, or local file
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
jats summary https://elifesciences.org/articles/81952
|
|
42
|
+
jats summary 10.1371/journal.pclm.0000068
|
|
43
|
+
jats summary /local/article.jats
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
This will provide a summary, including a list of what the JATS file contains.
|
|
47
|
+
|
|
48
|
+

|
|
49
|
+
|
|
50
|
+
`validate`: validate local file against JATS Archive DTD schema. By default, this uses JATS 1.3.
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
jats validate article.jats --jats 1.2 --mathmml 2
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`test`: test a JATS file against a list of unit tests in YAML
|
|
57
|
+
|
|
58
|
+
The test cases are useful for known exports and expecting specific pieces of information in the XML.
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
jats test article.jats --cases tests.yml
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
```yaml
|
|
65
|
+
cases:
|
|
66
|
+
- title: Correct publisher ID (publisher-id)
|
|
67
|
+
select: 'front > journal-meta > journal-id[journal-id-type="publisher-id"] > *'
|
|
68
|
+
equals:
|
|
69
|
+
type: text
|
|
70
|
+
value: plos
|
|
71
|
+
- title: Every orcid is authenticated
|
|
72
|
+
selectAll: 'front > article-meta > contrib-group > contrib > contrib-id'
|
|
73
|
+
equals:
|
|
74
|
+
contrib-id-type: orcid
|
|
75
|
+
authenticated: 'true'
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
As of v1.0.0 this package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c).
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
<p style="text-align: center; color: #aaa; padding-top: 50px">
|
|
85
|
+
Made with love by
|
|
86
|
+
<a href="https://curvenote.com" target="_blank" style="color: #aaa">
|
|
87
|
+
<img src="https://curvenote.dev/images/icon.png" style="height: 1em" /> Curvenote
|
|
88
|
+
</a>
|
|
89
|
+
</p>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../src/convert.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAiB5C,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,QAE7C"}
|
package/dist/convert.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Command, Option } from 'commander';
|
|
2
|
+
import { jatsConvert } from 'jats-convert';
|
|
3
|
+
function makeConvertCLI(program) {
|
|
4
|
+
const command = new Command('convert')
|
|
5
|
+
.description('Convert JATS file to MyST mdast json')
|
|
6
|
+
.argument('<input>', 'The JATS file')
|
|
7
|
+
.addOption(new Option('--frontmatter <frontmatter>', 'Treat JATS frontmatter fields as page or project, or ignore if not specified').choices(['page', 'project']))
|
|
8
|
+
.action(jatsConvert);
|
|
9
|
+
return command;
|
|
10
|
+
}
|
|
11
|
+
export function addConvertCLI(program) {
|
|
12
|
+
program.addCommand(makeConvertCLI(program));
|
|
13
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import version from './version.js';
|
|
4
|
+
import { addDownloadCLI } from './parse.js';
|
|
5
|
+
import { addValidateCLI } from './validate.js';
|
|
6
|
+
import { addTestCLI } from './jats-test.js';
|
|
7
|
+
import { addConvertCLI } from './convert.js';
|
|
8
|
+
const program = new Command();
|
|
9
|
+
addDownloadCLI(program);
|
|
10
|
+
addValidateCLI(program);
|
|
11
|
+
addConvertCLI(program);
|
|
12
|
+
addTestCLI(program);
|
|
13
|
+
program.version(`v${version}`, '-v, --version', 'Print the current version of jats');
|
|
14
|
+
program.option('-d, --debug', 'Log out any errors to the console.');
|
|
15
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import type { ISession } from 'jats-xml';
|
|
3
|
+
type Options = {
|
|
4
|
+
cases: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function testJatsFile(session: ISession, file: string, opts: Options): Promise<boolean>;
|
|
7
|
+
export declare function addTestCLI(program: Command): void;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=jats-test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jats-test.d.ts","sourceRoot":"","sources":["../src/jats-test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAU5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEzC,KAAK,OAAO,GAAG;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAsBF,wBAAsB,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,oBAsFhF;AAWD,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,QAE1C"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { Command, Option } from 'commander';
|
|
2
|
+
import { clirun, tic } from 'myst-cli-utils';
|
|
3
|
+
import yaml from 'js-yaml';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import { select, selectAll } from 'unist-util-select';
|
|
6
|
+
import { is } from 'unist-util-is';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import { getSession } from 'jats-xml';
|
|
9
|
+
import { parseJats } from './parse.js';
|
|
10
|
+
const INDENT = ' ';
|
|
11
|
+
function printNodes(expected, received) {
|
|
12
|
+
return chalk.reset(`\n${INDENT}${chalk.greenBright('Expected node containing')}:\n${INDENT} ${yaml
|
|
13
|
+
.dump(expected)
|
|
14
|
+
.replace(/\n/g, `\n${INDENT} `)}\n${INDENT}${chalk.redBright('Received node')}:\n${INDENT} ${yaml.dump(received).replace(/\n/g, `\n${INDENT} `)}`);
|
|
15
|
+
}
|
|
16
|
+
export async function testJatsFile(session, file, opts) {
|
|
17
|
+
const toc = tic();
|
|
18
|
+
const jats = await parseJats(session, file);
|
|
19
|
+
const tests = yaml.load(fs.readFileSync(opts.cases).toString());
|
|
20
|
+
const results = tests.cases.map((testCase, index) => {
|
|
21
|
+
if (!testCase.title) {
|
|
22
|
+
return [`Test Case ${index}`, null, 'Test must include a title'];
|
|
23
|
+
}
|
|
24
|
+
if (testCase.equals === undefined) {
|
|
25
|
+
return [testCase.title, null, 'Test must have an equals statement'];
|
|
26
|
+
}
|
|
27
|
+
if (testCase.select) {
|
|
28
|
+
const node = select(testCase.select, jats.tree);
|
|
29
|
+
const pass = is(node, testCase.equals);
|
|
30
|
+
if (testCase.equals == null && node) {
|
|
31
|
+
return [testCase.title, false, 'Expected no node to be present'];
|
|
32
|
+
}
|
|
33
|
+
if (!node && testCase.equals == null)
|
|
34
|
+
return [testCase.title, true];
|
|
35
|
+
if (!node)
|
|
36
|
+
return [testCase.title, false];
|
|
37
|
+
let failed = false;
|
|
38
|
+
const messages = [];
|
|
39
|
+
if (!pass) {
|
|
40
|
+
failed = failed || true;
|
|
41
|
+
messages.push(`Failed to validate node\n${printNodes(testCase.equals, node)}`);
|
|
42
|
+
}
|
|
43
|
+
return [testCase.title, !failed, messages.join('\n')];
|
|
44
|
+
}
|
|
45
|
+
else if (testCase.selectAll) {
|
|
46
|
+
const testNodes = selectAll(testCase.selectAll, jats.tree);
|
|
47
|
+
if (!testNodes && testCase.equals == null)
|
|
48
|
+
return [testCase.title, true];
|
|
49
|
+
if (!testNodes)
|
|
50
|
+
return [testCase.title, false, 'Node not found'];
|
|
51
|
+
let equals = testCase.equals;
|
|
52
|
+
if (!Array.isArray(testCase.equals)) {
|
|
53
|
+
equals = Array(testNodes.length).fill(testCase.equals);
|
|
54
|
+
}
|
|
55
|
+
let failed = false;
|
|
56
|
+
const messages = [];
|
|
57
|
+
if (equals.length !== testNodes.length) {
|
|
58
|
+
failed = failed || true;
|
|
59
|
+
messages.push(`Expected ${equals.length} nodes, got ${testNodes.length}\n${printNodes(equals, testNodes)}`);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
equals.forEach((node, ii) => {
|
|
63
|
+
const pass = is(testNodes[ii], node);
|
|
64
|
+
if (!pass) {
|
|
65
|
+
failed = failed || true;
|
|
66
|
+
messages.push(`Failed to validate node ${ii}\n${printNodes(node, testNodes[ii])}`);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return [testCase.title, !failed, messages.join('\n')];
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
return [testCase.title, false, 'Test must have either `select` or `selectAll`'];
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
results.forEach((result) => {
|
|
77
|
+
const [title, pass, message] = result;
|
|
78
|
+
if (pass === null)
|
|
79
|
+
session.log.info(`${chalk.redBright.bold(`ERROR`)} - ${title}\n ${chalk.blueBright(message)}`);
|
|
80
|
+
else if (pass)
|
|
81
|
+
session.log.info(`${chalk.green(`PASS`)} - ${title}`);
|
|
82
|
+
else
|
|
83
|
+
session.log.info(`${chalk.red(`FAIL`)} - ${title}\n\n${INDENT}${chalk.blueBright(message)}\n`);
|
|
84
|
+
}, true);
|
|
85
|
+
const passed = results.reduce((num, [, pass]) => num + (pass ? 1 : 0), 0);
|
|
86
|
+
const failed = results.length - passed;
|
|
87
|
+
if (failed > 0 && passed === 0) {
|
|
88
|
+
throw new Error(toc(`${chalk.red(`Failed ${failed} tests in %s`)} 👎`));
|
|
89
|
+
}
|
|
90
|
+
if (failed > 0) {
|
|
91
|
+
throw new Error(toc(`${chalk.green(`Passed ${passed}/${results.length} tests in %s`)}\n${chalk.red(`Failed ${failed} tests`)} 👎`));
|
|
92
|
+
}
|
|
93
|
+
session.log.info(chalk.green(toc(`Passed ${passed} tests in %s 🚀`)));
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
function makeTestCLI(program) {
|
|
97
|
+
const command = new Command('test')
|
|
98
|
+
.description('Test JATS file against a list of cases')
|
|
99
|
+
.argument('<file>', 'JATS file to test')
|
|
100
|
+
.addOption(new Option('--cases <value>', 'The YAML file of unit tests to test against'))
|
|
101
|
+
.action(clirun(testJatsFile, { program, getSession }));
|
|
102
|
+
return command;
|
|
103
|
+
}
|
|
104
|
+
export function addTestCLI(program) {
|
|
105
|
+
program.addCommand(makeTestCLI(program));
|
|
106
|
+
}
|