jats-utils 1.0.7

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.
@@ -0,0 +1,3 @@
1
+ export * from './utils.js';
2
+ export * from './serialize.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from './utils.js';
2
+ export * from './serialize.js';
@@ -0,0 +1,12 @@
1
+ import { type Element } from 'xml-js';
2
+ export type SerializationOptions = {
3
+ /**
4
+ * When 'pretty', the default, the xml be formatted in a custom, opinionated way
5
+ * When 'flat', the xml will be on a single line
6
+ * When `0`, the XML will be on different lines with 0 spaces.
7
+ * When any other value (e.g. `2` or `\t`) the XML will be indented at the start of the line by that amount.
8
+ */
9
+ format?: number | 'flat' | 'pretty' | '\t';
10
+ };
11
+ export declare function serializeJatsXml(element: Element, opts?: SerializationOptions): string;
12
+ //# sourceMappingURL=serialize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,OAAO,EAAE,MAAM,QAAQ,CAAC;AAG9C,MAAM,MAAM,oBAAoB,GAAG;IACjC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC;CAC5C,CAAC;AA+EF,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,oBAAoB,UAgB7E"}
@@ -0,0 +1,95 @@
1
+ import { js2xml } from 'xml-js';
2
+ import { escapeForXML } from './utils.js';
3
+ const both = [
4
+ '\\?xml',
5
+ 'article',
6
+ 'sub-article',
7
+ 'front',
8
+ 'front-stub',
9
+ 'journal-meta',
10
+ 'journal-title-group',
11
+ 'publisher',
12
+ 'article-meta',
13
+ 'article-categories',
14
+ 'title-group',
15
+ 'contrib-group',
16
+ 'contrib',
17
+ 'institution-wrap',
18
+ 'aff',
19
+ 'permissions',
20
+ 'license',
21
+ 'kwd-group',
22
+ 'history',
23
+ 'self-uri',
24
+ 'funding-group',
25
+ 'award-group',
26
+ 'principal-award-recipient',
27
+ 'custom-meta-group',
28
+ 'date',
29
+ 'pub-date',
30
+ 'abstract',
31
+ 'counts',
32
+ 'body',
33
+ 'sec',
34
+ 'fig',
35
+ 'disp-formula',
36
+ 'table-wrap',
37
+ 'caption',
38
+ 'table',
39
+ 'thead',
40
+ 'ref-list',
41
+ 'ref',
42
+ 'back',
43
+ ];
44
+ const first = [
45
+ 'journal-id',
46
+ 'journal-title',
47
+ 'issn',
48
+ 'publisher-name',
49
+ 'publisher-loc',
50
+ 'article-id',
51
+ 'article-title',
52
+ 'alt-title',
53
+ 'subtitle',
54
+ 'kwd',
55
+ 'name',
56
+ 'email',
57
+ 'contrib-id',
58
+ 'role',
59
+ 'institution',
60
+ 'institution-id',
61
+ 'award-id',
62
+ 'meta-name',
63
+ 'meta-value',
64
+ 'title',
65
+ 'p',
66
+ 'tr',
67
+ 'label',
68
+ 'graphic',
69
+ 'mixed-citation',
70
+ ];
71
+ function indentXML(xml) {
72
+ return xml
73
+ .replace(RegExp(`<(\\/)?(${both.join('|')})( [^>]*)?>`, 'g'), '<$1$2$3>\n')
74
+ .replace(RegExp(`([^\n])<(\\/)?(${both.join('|')})( [^>]*)?>`, 'g'), '$1\n<$2$3$4>')
75
+ .replace(RegExp(`([^\n])<(${first.join('|')})( [^>]*)?>`, 'g'), '$1\n<$2$3>')
76
+ .replace(RegExp(`<\\/(${first.join('|')})(\\s*)>([^\n])`, 'g'), '</$1>\n$3');
77
+ }
78
+ export function serializeJatsXml(element, opts) {
79
+ const { format } = { format: 'pretty', ...opts };
80
+ const xml = js2xml(element, {
81
+ compact: false,
82
+ // No way to write XML with new lines, but no indentation with js2xml.
83
+ // If you use 0 or '', you get a single line.
84
+ spaces: format === 'flat' || format === 'pretty' ? 0 : format || 1,
85
+ attributeValueFn: escapeForXML,
86
+ });
87
+ if (format === 0) {
88
+ // either `0` or `''`
89
+ return xml.replace(/\n(\s*)</g, '\n<');
90
+ }
91
+ else if (format === 'pretty') {
92
+ return indentXML(xml);
93
+ }
94
+ return xml;
95
+ }
@@ -0,0 +1,12 @@
1
+ import type { GenericNode, GenericParent } from 'myst-common';
2
+ import type { Element } from 'xml-js';
3
+ import type { Contributor } from 'myst-frontmatter';
4
+ export declare function convertToUnist(node: Element): GenericNode | GenericParent | undefined;
5
+ export declare function convertToXml(node: GenericNode): Element;
6
+ export declare function escapeForXML(text: string): string;
7
+ export declare function toDate(date?: GenericParent): Date | undefined;
8
+ export declare function formatDate(date?: Date): string | undefined;
9
+ export type PubIdTypes = 'doi' | 'pmc' | 'pmid' | 'publisher-id' | string;
10
+ export declare function findArticleId(node: GenericParent | undefined, pubIdType?: PubIdTypes): string | undefined;
11
+ export declare function authorAndAffiliation(node: GenericParent, article: GenericParent): Contributor;
12
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE9D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAKtC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,WAAW,GAAG,aAAa,GAAG,SAAS,CA2CrF;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAwBvD;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,UAExC;AA6BD,wBAAgB,MAAM,CAAC,IAAI,CAAC,EAAE,aAAa,GAAG,IAAI,GAAG,SAAS,CAa7D;AAED,wBAAgB,UAAU,CAAC,IAAI,CAAC,EAAE,IAAI,sBAGrC;AAED,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,cAAc,GAAG,MAAM,CAAC;AAE1E,wBAAgB,aAAa,CAC3B,IAAI,EAAE,aAAa,GAAG,SAAS,EAC/B,SAAS,GAAE,UAAkB,GAC5B,MAAM,GAAG,SAAS,CAQpB;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,aAAa,GAAG,WAAW,CAyC7F"}
package/dist/utils.js ADDED
@@ -0,0 +1,179 @@
1
+ import { toText } from 'myst-common';
2
+ import { doi } from 'doi-utils';
3
+ import { select, selectAll } from 'unist-util-select';
4
+ import { Tags } from 'jats-tags';
5
+ export function convertToUnist(node) {
6
+ switch (node.type) {
7
+ case 'element': {
8
+ const { name, attributes, elements } = node;
9
+ const children = elements === null || elements === void 0 ? void 0 : elements.map(convertToUnist).filter((n) => !!n);
10
+ const { type, ...attrs } = attributes !== null && attributes !== void 0 ? attributes : {};
11
+ if (type !== undefined)
12
+ attrs._type = type;
13
+ const next = { type: name !== null && name !== void 0 ? name : 'unknown', ...attrs };
14
+ if (name === 'code') {
15
+ next.value = elements === null || elements === void 0 ? void 0 : elements[0].text;
16
+ }
17
+ else if (children)
18
+ next.children = children;
19
+ return next;
20
+ }
21
+ case 'text': {
22
+ const { attributes, text } = node;
23
+ return {
24
+ type: 'text',
25
+ ...attributes,
26
+ value: String(text).replace(/\n(\s+)$/, ''),
27
+ };
28
+ }
29
+ case 'cdata': {
30
+ const { attributes, cdata } = node;
31
+ return {
32
+ type: 'cdata',
33
+ ...attributes,
34
+ cdata: String(cdata).trim(),
35
+ };
36
+ }
37
+ case 'comment': {
38
+ const { comment } = node;
39
+ return { type: 'comment', value: String(comment) };
40
+ }
41
+ case 'instruction': {
42
+ // For example:
43
+ // <?properties manuscript?> becomes:
44
+ // { type: 'instruction', name: 'properties', instruction: 'manuscript' }
45
+ return undefined;
46
+ }
47
+ default:
48
+ console.log(node);
49
+ throw new Error(`found ${node.type} ${node.name}`);
50
+ }
51
+ }
52
+ export function convertToXml(node) {
53
+ const { type, ...rest } = node;
54
+ switch (type) {
55
+ case 'text': {
56
+ const { value, ...attributes } = rest;
57
+ return { type: 'text', attributes, text: value };
58
+ }
59
+ case 'code': {
60
+ const { value, ...attributes } = rest;
61
+ return { type: 'element', name: type, attributes, elements: [{ type: 'text', text: value }] };
62
+ }
63
+ case 'comment': {
64
+ return { type: 'comment', comment: rest.value };
65
+ }
66
+ case 'cdata': {
67
+ const { cdata, ...attributes } = rest;
68
+ return { type: 'cdata', attributes, cdata };
69
+ }
70
+ default: {
71
+ const { children, _type, ...attributes } = rest;
72
+ if (_type !== undefined)
73
+ attributes.type = _type;
74
+ return { type: 'element', name: type, attributes, elements: children === null || children === void 0 ? void 0 : children.map(convertToXml) };
75
+ }
76
+ }
77
+ }
78
+ export function escapeForXML(text) {
79
+ return text.replace(/&(?!amp;)/g, '&amp;').replace(/</g, '&lt;');
80
+ }
81
+ const MonthLookup = {
82
+ jan: 0,
83
+ january: 0,
84
+ feb: 1,
85
+ february: 1,
86
+ mar: 2,
87
+ march: 2,
88
+ apr: 3,
89
+ april: 3,
90
+ may: 4,
91
+ jun: 5,
92
+ june: 5,
93
+ jul: 6,
94
+ july: 6,
95
+ aug: 7,
96
+ august: 7,
97
+ sep: 8,
98
+ sept: 8,
99
+ september: 8,
100
+ oct: 9,
101
+ october: 9,
102
+ nov: 10,
103
+ november: 10,
104
+ dec: 11,
105
+ december: 11,
106
+ };
107
+ export function toDate(date) {
108
+ if (!date)
109
+ return;
110
+ const isoDate = date['iso-8601-date'];
111
+ if (isoDate)
112
+ return new Date(isoDate);
113
+ const year = Number(toText(select('year', date)));
114
+ if (!year || Number.isNaN(year))
115
+ return;
116
+ const monthText = toText(select('month', date));
117
+ const monthTextNumber = Number(monthText);
118
+ const month = Number.isNaN(monthTextNumber) ? MonthLookup[monthText] : monthTextNumber - 1;
119
+ if (month == null)
120
+ return new Date(Date.UTC(year, 0));
121
+ const day = Number(toText(select('day', date)));
122
+ if (!day || Number.isNaN(day))
123
+ return new Date(Date.UTC(year, month));
124
+ return new Date(Date.UTC(year, month, day));
125
+ }
126
+ export function formatDate(date) {
127
+ if (!date)
128
+ return;
129
+ return new Intl.DateTimeFormat('en-US', { dateStyle: 'long', timeZone: 'UTC' }).format(date);
130
+ }
131
+ export function findArticleId(node, pubIdType = 'doi') {
132
+ if (!node)
133
+ return undefined;
134
+ const id = select(`[pub-id-type=${pubIdType}]`, node);
135
+ if (id && toText(id))
136
+ return toText(id);
137
+ const doiTag = selectAll(`${Tags.articleId},${Tags.pubId}`, node).find((t) => doi.validate(toText(t)));
138
+ return toText(doiTag) || undefined;
139
+ }
140
+ export function authorAndAffiliation(node, article) {
141
+ const author = {
142
+ name: `${toText(select(Tags.givenNames, node))} ${toText(select(Tags.surname, node))}`,
143
+ };
144
+ const orcid = select('[contrib-id-type=orcid]', node);
145
+ if (orcid) {
146
+ author.orcid = toText(orcid).replace(/(https?:\/\/)?orcid\.org\//, '');
147
+ }
148
+ //
149
+ /**
150
+ * For example:
151
+ *
152
+ * ```xml
153
+ * <aff id="aff2">
154
+ * <label>2</label>
155
+ * <institution-wrap>
156
+ * <institution-id institution-id-type="ror">https://ror.org/00t9vx427</institution-id>
157
+ * <institution>Department of Biochemistry, University of Texas Southwestern Medical Center</institution>
158
+ * </institution-wrap>
159
+ * <addr-line>
160
+ * <named-content content-type="city">Dallas</named-content>
161
+ * </addr-line>
162
+ * <country>United States</country>
163
+ * </aff>
164
+ * ```
165
+ */
166
+ const affiliationRefs = selectAll('xref[ref-type=aff]', node);
167
+ const affiliations = affiliationRefs.map((xref) => select(`[id=${xref.rid}]`, article));
168
+ const affiliationText = affiliations
169
+ .map((aff) => {
170
+ // TODO: handle rors!
171
+ const ror = select(`[institution-id-type=ror]`, aff);
172
+ return toText(select('institution', aff));
173
+ })
174
+ .filter((t) => !!t);
175
+ if (affiliationText.length > 0) {
176
+ author.affiliations = affiliationText;
177
+ }
178
+ return author;
179
+ }
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "jats-utils",
3
+ "version": "1.0.7",
4
+ "description": "Utility functions for working with JATS in Typescript",
5
+ "author": "Rowan Cockett <rowan@curvenote.com>",
6
+ "homepage": "https://github.com/curvenote/jats",
7
+ "license": "MIT",
8
+ "sideEffects": false,
9
+ "type": "module",
10
+ "exports": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "files": [
13
+ "dist"
14
+ ],
15
+ "keywords": [
16
+ "jats",
17
+ "open-science",
18
+ "publishing"
19
+ ],
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://github.com/curvenote/jats.git"
26
+ },
27
+ "scripts": {
28
+ "clean": "rm -rf dist",
29
+ "lint": "eslint \"src/**/*.ts*\" -c ./.eslintrc.cjs",
30
+ "lint:format": "prettier --check \"src/**/*.{ts,tsx,md}\"",
31
+ "build:esm": "tsc",
32
+ "build": "npm-run-all -l clean -p build:esm"
33
+ },
34
+ "bugs": {
35
+ "url": "https://github.com/curvenote/jats/issues"
36
+ },
37
+ "dependencies": {},
38
+ "peerDependencies": {
39
+ "xml-js": "^1",
40
+ "jats-tags": "^1"
41
+ },
42
+ "devDependencies": {
43
+ "myst-common": "^1.0.0",
44
+ "jats-tags": "^1"
45
+ }
46
+ }