jats-xml 1.1.4 → 1.1.5
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/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/jats.d.ts +21 -0
- package/dist/jats.d.ts.map +1 -1
- package/dist/jats.js +177 -50
- package/dist/messages.d.ts +5 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +6 -0
- package/dist/repairKnownXmlDefects.d.ts +23 -0
- package/dist/repairKnownXmlDefects.d.ts.map +1 -0
- package/dist/repairKnownXmlDefects.js +26 -0
- package/dist/sanitizeXmlEntities.d.ts +14 -0
- package/dist/sanitizeXmlEntities.d.ts.map +1 -0
- package/dist/sanitizeXmlEntities.js +26 -0
- package/dist/utils.js +2 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +6 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export { default as version } from './version.js';
|
|
2
2
|
export { Jats } from './jats.js';
|
|
3
|
+
export { recordJatsMessage } from './messages.js';
|
|
4
|
+
export { knownXmlDefectRepairMessage, repairKnownXmlDefects, type AppliedXmlDefectRepair, type KnownXmlDefectRepair, type RepairKnownXmlDefectsResult, } from './repairKnownXmlDefects.js';
|
|
5
|
+
export { sanitizeXmlEntities, type SanitizeXmlEntitiesResult } from './sanitizeXmlEntities.js';
|
|
3
6
|
export * from './session.js';
|
|
4
7
|
export * from './types.js';
|
|
5
8
|
export * from './utils.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EACL,2BAA2B,EAC3B,qBAAqB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EACzB,KAAK,2BAA2B,GACjC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,KAAK,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAC/F,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,qBAAqB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export { default as version } from './version.js';
|
|
2
2
|
export { Jats } from './jats.js';
|
|
3
|
+
export { recordJatsMessage } from './messages.js';
|
|
4
|
+
export { knownXmlDefectRepairMessage, repairKnownXmlDefects, } from './repairKnownXmlDefects.js';
|
|
5
|
+
export { sanitizeXmlEntities } from './sanitizeXmlEntities.js';
|
|
3
6
|
export * from './session.js';
|
|
4
7
|
export * from './types.js';
|
|
5
8
|
export * from './utils.js';
|
package/dist/jats.d.ts
CHANGED
|
@@ -3,10 +3,12 @@ import type { Element, DeclarationAttributes } from 'xml-js';
|
|
|
3
3
|
import { type PageFrontmatter } from 'myst-frontmatter';
|
|
4
4
|
import type { Front, Body, Back, SubArticle, RefList, Reference, TitleGroup, ArticleTitle, Subtitle, Permissions, PubDate, License, Abstract, ContribGroup, Contrib, Affiliation, KeywordGroup, Keyword, ArticleCategories, ArticleMeta } from 'jats-tags';
|
|
5
5
|
import type { Logger } from 'myst-cli-utils';
|
|
6
|
+
import type { VFile } from 'vfile';
|
|
6
7
|
import { type SerializationOptions } from 'jats-utils';
|
|
7
8
|
type Options = {
|
|
8
9
|
log?: Logger;
|
|
9
10
|
source?: string;
|
|
11
|
+
vfile?: VFile;
|
|
10
12
|
};
|
|
11
13
|
type WriteOptions = SerializationOptions & {
|
|
12
14
|
bodyOnly?: boolean;
|
|
@@ -18,6 +20,8 @@ export declare class Jats {
|
|
|
18
20
|
log?: Logger;
|
|
19
21
|
tree: GenericParent;
|
|
20
22
|
source?: string;
|
|
23
|
+
vfile?: VFile;
|
|
24
|
+
private frontmatterMessagesRecorded;
|
|
21
25
|
constructor(data: string, opts?: Options);
|
|
22
26
|
get frontmatter(): PageFrontmatter;
|
|
23
27
|
get front(): Front | undefined;
|
|
@@ -33,7 +37,24 @@ export declare class Jats {
|
|
|
33
37
|
* often including an "accepted" date that is a better default for project/page `date`.
|
|
34
38
|
*/
|
|
35
39
|
get historyDates(): GenericParent[];
|
|
40
|
+
/**
|
|
41
|
+
* Prefer a fully-specified publication date (day/month/year).
|
|
42
|
+
* If publication date is incomplete (e.g. year-only), fall back through history dates.
|
|
43
|
+
*/
|
|
44
|
+
private resolvePublicationDate;
|
|
45
|
+
private recordFrontmatterMessages;
|
|
46
|
+
private pickPublicationDate;
|
|
47
|
+
private static readonly PUB_HISTORY_PRIMARY_DATE_TYPES;
|
|
48
|
+
private static readonly HISTORY_PRIMARY_DATE_TYPES;
|
|
49
|
+
private jatsDateType;
|
|
50
|
+
private dateHasDay;
|
|
51
|
+
/** Last `<date>` with a `<day>` whose `date-type` is not in `excludeTypes`. */
|
|
52
|
+
private lastDateWithDay;
|
|
53
|
+
private fallbackDatePick;
|
|
54
|
+
private findHistoryDateByType;
|
|
36
55
|
get historyAcceptedDate(): GenericParent | undefined;
|
|
56
|
+
get pubHistoryFallbackDate(): GenericParent | undefined;
|
|
57
|
+
get historyFallbackDate(): GenericParent | undefined;
|
|
37
58
|
/**
|
|
38
59
|
* Some sources store important dates under:
|
|
39
60
|
* `<article-meta><pub-history><event><date date-type="...">...</date></event></pub-history>`.
|
package/dist/jats.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jats.d.ts","sourceRoot":"","sources":["../src/jats.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,aAAa,EAAE,MAAM,aAAa,CAAC;AAI9D,OAAO,KAAK,EAAE,OAAO,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,EAA2B,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAIjF,OAAO,KAAK,EACV,KAAK,EACL,IAAI,EACJ,IAAI,EACJ,UAAU,EACV,OAAO,EACP,SAAS,EACT,UAAU,EACV,YAAY,EACZ,QAAQ,EACR,WAAW,EACX,OAAO,EACP,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,OAAO,EACP,WAAW,EACX,YAAY,EACZ,OAAO,EACP,iBAAiB,EACjB,WAAW,EAEZ,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"jats.d.ts","sourceRoot":"","sources":["../src/jats.ts"],"names":[],"mappings":"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"}
|
package/dist/jats.js
CHANGED
|
@@ -6,6 +6,9 @@ import { select as unistSelect, selectAll } from 'unist-util-select';
|
|
|
6
6
|
import { Tags } from 'jats-tags';
|
|
7
7
|
import { findArticleId, processAffiliation, processContributor } from './utils.js';
|
|
8
8
|
import { tic } from 'myst-cli-utils';
|
|
9
|
+
import { recordJatsMessage } from './messages.js';
|
|
10
|
+
import { knownXmlDefectRepairMessage, repairKnownXmlDefects } from './repairKnownXmlDefects.js';
|
|
11
|
+
import { sanitizeXmlEntities } from './sanitizeXmlEntities.js';
|
|
9
12
|
import { articleMetaOrder, tableWrapOrder } from './order.js';
|
|
10
13
|
import { serializeJatsXml, convertToUnist, convertToXml, toDate, } from 'jats-utils';
|
|
11
14
|
function select(selector, node) {
|
|
@@ -38,27 +41,45 @@ function significantChildElements(elements) {
|
|
|
38
41
|
});
|
|
39
42
|
}
|
|
40
43
|
/**
|
|
41
|
-
* Drop
|
|
44
|
+
* Drop top-level processing instructions (e.g. xml-stylesheet) from the prolog.
|
|
42
45
|
* Malformed prologs such as <?version xml="1.0"?> are parsed this way by xml-js.
|
|
43
46
|
*/
|
|
44
|
-
function
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
function dropTopLevelInstructions(elements, onInstruction) {
|
|
48
|
+
return elements === null || elements === void 0 ? void 0 : elements.filter((elem) => {
|
|
49
|
+
if (elem.type === 'instruction') {
|
|
50
|
+
onInstruction === null || onInstruction === void 0 ? void 0 : onInstruction(elem);
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
});
|
|
52
55
|
}
|
|
53
56
|
export class Jats {
|
|
54
57
|
constructor(data, opts) {
|
|
55
|
-
var _a
|
|
58
|
+
var _a;
|
|
59
|
+
this.frontmatterMessagesRecorded = false;
|
|
56
60
|
const toc = tic();
|
|
57
61
|
this.log = opts === null || opts === void 0 ? void 0 : opts.log;
|
|
58
62
|
if (opts === null || opts === void 0 ? void 0 : opts.source)
|
|
59
63
|
this.source = opts.source;
|
|
64
|
+
this.vfile = opts === null || opts === void 0 ? void 0 : opts.vfile;
|
|
65
|
+
const warnProlog = (reason, note) => {
|
|
66
|
+
recordJatsMessage(this.vfile, reason, { note });
|
|
67
|
+
};
|
|
68
|
+
const { xml: afterDefectRepairs, applied: defectRepairs } = repairKnownXmlDefects(data);
|
|
69
|
+
defectRepairs.forEach(({ repair, count }) => {
|
|
70
|
+
const reason = knownXmlDefectRepairMessage(repair.from, repair.to);
|
|
71
|
+
const note = count === 1 ? undefined : `${count} occurrences`;
|
|
72
|
+
warnProlog(reason, note);
|
|
73
|
+
});
|
|
74
|
+
const { xml: parseInput, escapedBareAmpersandCount } = sanitizeXmlEntities(afterDefectRepairs);
|
|
75
|
+
if (escapedBareAmpersandCount > 0) {
|
|
76
|
+
const note = escapedBareAmpersandCount === 1
|
|
77
|
+
? '1 bare & rewritten to &'
|
|
78
|
+
: `${escapedBareAmpersandCount} bare & rewritten to &`;
|
|
79
|
+
warnProlog('Escaped bare ampersand(s) before XML parse', note);
|
|
80
|
+
}
|
|
60
81
|
try {
|
|
61
|
-
this.raw = xml2js(
|
|
82
|
+
this.raw = xml2js(parseInput, {
|
|
62
83
|
compact: false,
|
|
63
84
|
// Preserve whitespace-only text nodes between elements. This is usually unnecessary except inside <preformat>.
|
|
64
85
|
// convertToUnist drops these outside preformat so other content is processed independent of arbitrary xml whitespace.
|
|
@@ -70,9 +91,15 @@ export class Jats {
|
|
|
70
91
|
}
|
|
71
92
|
const { declaration, elements } = this.raw;
|
|
72
93
|
this.declaration = declaration === null || declaration === void 0 ? void 0 : declaration.attributes;
|
|
73
|
-
const filteredElements =
|
|
94
|
+
const filteredElements = dropTopLevelInstructions(significantChildElements(elements), (instruction) => {
|
|
95
|
+
var _a, _b;
|
|
96
|
+
const name = (_a = instruction.name) !== null && _a !== void 0 ? _a : '(unnamed)';
|
|
97
|
+
const body = String((_b = instruction.instruction) !== null && _b !== void 0 ? _b : '').trim();
|
|
98
|
+
const note = body ? `name=${name} ${body}` : `name=${name}`;
|
|
99
|
+
warnProlog('Removed top-level XML processing instruction from prolog', note);
|
|
100
|
+
});
|
|
74
101
|
if ((filteredElements === null || filteredElements === void 0 ? void 0 : filteredElements.length) && filteredElements[0].type !== 'doctype') {
|
|
75
|
-
(
|
|
102
|
+
warnProlog('JATS is missing DOCTYPE declaration; inserted empty doctype');
|
|
76
103
|
filteredElements.unshift({ type: 'doctype' });
|
|
77
104
|
}
|
|
78
105
|
if (!((filteredElements === null || filteredElements === void 0 ? void 0 : filteredElements.length) === 2 &&
|
|
@@ -81,36 +108,32 @@ export class Jats {
|
|
|
81
108
|
throw new Error('JATS must be structured as <!DOCTYPE><article>...</article>');
|
|
82
109
|
}
|
|
83
110
|
this.doctype = filteredElements[0].doctype;
|
|
111
|
+
if (filteredElements[1].name === 'pmc-articleset') {
|
|
112
|
+
warnProlog('JATS root is pmc-articleset wrapper', 'Using nested article element');
|
|
113
|
+
}
|
|
84
114
|
const converted = convertToUnist(filteredElements[1]);
|
|
85
115
|
this.tree = select('article', converted);
|
|
86
|
-
(
|
|
116
|
+
(_a = this.log) === null || _a === void 0 ? void 0 : _a.debug(toc('Parsed and converted JATS to unist tree in %s'));
|
|
87
117
|
}
|
|
88
118
|
get frontmatter() {
|
|
89
|
-
var _a, _b, _c, _d, _e, _f, _g, _h
|
|
119
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
90
120
|
const title = this.articleTitle;
|
|
91
121
|
const subtitle = this.articleSubtitle;
|
|
92
122
|
const short_title = this.articleAltTitle;
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const month = (d.getUTCMonth() + 1).toString().padStart(2, '0');
|
|
102
|
-
const day = d.getUTCDate().toString().padStart(2, '0');
|
|
103
|
-
date = `${year}-${month}-${day}`;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
const authors = (_d = this.articleAuthors) === null || _d === void 0 ? void 0 : _d.map((auth) => {
|
|
107
|
-
return processContributor(auth);
|
|
123
|
+
const titleText = title ? toText(title).trim() : '';
|
|
124
|
+
const authors = ((_a = this.articleAuthors) !== null && _a !== void 0 ? _a : []).map((auth) => processContributor(auth));
|
|
125
|
+
const { date, datePick } = this.resolvePublicationDate();
|
|
126
|
+
this.recordFrontmatterMessages({
|
|
127
|
+
date,
|
|
128
|
+
datePick,
|
|
129
|
+
titleText,
|
|
130
|
+
authorCount: authors.length,
|
|
108
131
|
});
|
|
109
|
-
const affiliations = (
|
|
132
|
+
const affiliations = (_b = this.articleAffiliations) === null || _b === void 0 ? void 0 : _b.map((aff) => {
|
|
110
133
|
return processAffiliation(aff);
|
|
111
134
|
});
|
|
112
|
-
const keywords = (
|
|
113
|
-
const subjectScope = (
|
|
135
|
+
const keywords = (_d = (_c = this.keywords) === null || _c === void 0 ? void 0 : _c.map((k) => toText(k))) !== null && _d !== void 0 ? _d : [];
|
|
136
|
+
const subjectScope = (_e = this.articleCategories) !== null && _e !== void 0 ? _e : this.front;
|
|
114
137
|
const journalCollSubject = select(`${Tags.subjGroup}[subj-group-type="hwp-journal-coll"] ${Tags.subject}`, subjectScope);
|
|
115
138
|
const articleSubject = journalCollSubject !== null && journalCollSubject !== void 0 ? journalCollSubject : select(Tags.subject, subjectScope);
|
|
116
139
|
const journalTitle = select(Tags.journalTitle, this.front);
|
|
@@ -124,13 +147,13 @@ export class Jats {
|
|
|
124
147
|
}
|
|
125
148
|
else if (selectAll('ext-link', license).length === 1) {
|
|
126
149
|
// this should only happen if there is only one ext-link
|
|
127
|
-
licenseString = (
|
|
150
|
+
licenseString = (_f = select('ext-link', license)['xlink:href']) !== null && _f !== void 0 ? _f : null;
|
|
128
151
|
}
|
|
129
152
|
else if (license) {
|
|
130
153
|
licenseString = toText(license);
|
|
131
154
|
}
|
|
132
155
|
let openAccess;
|
|
133
|
-
const licenseType = (
|
|
156
|
+
const licenseType = (_g = license === null || license === void 0 ? void 0 : license['license-type']) === null || _g === void 0 ? void 0 : _g.toLowerCase();
|
|
134
157
|
if (licenseType && ['openaccess', 'open-access'].includes(licenseType)) {
|
|
135
158
|
openAccess = true;
|
|
136
159
|
}
|
|
@@ -144,10 +167,10 @@ export class Jats {
|
|
|
144
167
|
const pmc = this.pmc;
|
|
145
168
|
const identifiers = pmc ? { pmcid: `PMC${pmc}` } : undefined;
|
|
146
169
|
const frontmatter = validatePageFrontmatter({
|
|
147
|
-
title:
|
|
170
|
+
title: titleText || undefined,
|
|
148
171
|
subtitle: subtitle ? toText(subtitle) : undefined,
|
|
149
172
|
short_title: short_title ? toText(short_title) : undefined,
|
|
150
|
-
doi: (
|
|
173
|
+
doi: (_h = this.doi) !== null && _h !== void 0 ? _h : undefined,
|
|
151
174
|
identifiers,
|
|
152
175
|
date,
|
|
153
176
|
authors: authors.length ? authors : undefined,
|
|
@@ -195,16 +218,116 @@ export class Jats {
|
|
|
195
218
|
var _a;
|
|
196
219
|
return selectAll('history date', (_a = this.articleMeta) !== null && _a !== void 0 ? _a : this.front);
|
|
197
220
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
221
|
+
/**
|
|
222
|
+
* Prefer a fully-specified publication date (day/month/year).
|
|
223
|
+
* If publication date is incomplete (e.g. year-only), fall back through history dates.
|
|
224
|
+
*/
|
|
225
|
+
resolvePublicationDate() {
|
|
226
|
+
const datePick = this.pickPublicationDate();
|
|
227
|
+
let date;
|
|
228
|
+
if (datePick) {
|
|
229
|
+
const d = toDate(datePick.node);
|
|
230
|
+
if (d) {
|
|
231
|
+
const year = d.getUTCFullYear();
|
|
232
|
+
const month = (d.getUTCMonth() + 1).toString().padStart(2, '0');
|
|
233
|
+
const day = d.getUTCDate().toString().padStart(2, '0');
|
|
234
|
+
date = `${year}-${month}-${day}`;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return { date, datePick };
|
|
238
|
+
}
|
|
239
|
+
recordFrontmatterMessages(input) {
|
|
240
|
+
var _a;
|
|
241
|
+
if (this.frontmatterMessagesRecorded)
|
|
242
|
+
return;
|
|
243
|
+
this.frontmatterMessagesRecorded = true;
|
|
244
|
+
if ((_a = input.datePick) === null || _a === void 0 ? void 0 : _a.warn) {
|
|
245
|
+
recordJatsMessage(this.vfile, input.datePick.warn, { note: input.datePick.note });
|
|
246
|
+
}
|
|
247
|
+
if (!input.date) {
|
|
248
|
+
recordJatsMessage(this.vfile, 'No publication date found in JATS', {
|
|
249
|
+
note: 'article-meta/pub-date or history/pub-history dates',
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
if (!input.titleText) {
|
|
253
|
+
recordJatsMessage(this.vfile, 'No article title found in JATS', {
|
|
254
|
+
note: 'article-meta/title-group/article-title',
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
if (input.authorCount === 0) {
|
|
258
|
+
recordJatsMessage(this.vfile, 'No authors found in JATS', {
|
|
259
|
+
note: 'article-meta/contrib-group/contrib[@contrib-type="author"]',
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
pickPublicationDate() {
|
|
264
|
+
if (this.publicationDate) {
|
|
265
|
+
return { node: this.publicationDate };
|
|
266
|
+
}
|
|
267
|
+
if (this.pubHistoryPubDate) {
|
|
268
|
+
return { node: this.pubHistoryPubDate };
|
|
269
|
+
}
|
|
270
|
+
if (this.pubHistoryAcceptedDate) {
|
|
271
|
+
return { node: this.pubHistoryAcceptedDate };
|
|
272
|
+
}
|
|
273
|
+
if (this.historyAcceptedDate) {
|
|
274
|
+
return { node: this.historyAcceptedDate };
|
|
275
|
+
}
|
|
276
|
+
if (this.pubHistoryFallbackDate) {
|
|
277
|
+
return this.fallbackDatePick(this.pubHistoryFallbackDate, 'pub-history');
|
|
278
|
+
}
|
|
279
|
+
if (this.historyFallbackDate) {
|
|
280
|
+
return this.fallbackDatePick(this.historyFallbackDate, 'history');
|
|
281
|
+
}
|
|
282
|
+
return undefined;
|
|
283
|
+
}
|
|
284
|
+
jatsDateType(node) {
|
|
285
|
+
var _a;
|
|
286
|
+
return String((_a = node['date-type']) !== null && _a !== void 0 ? _a : '')
|
|
287
|
+
.trim()
|
|
288
|
+
.toLowerCase();
|
|
289
|
+
}
|
|
290
|
+
dateHasDay(node) {
|
|
291
|
+
return !!select(Tags.day, node);
|
|
292
|
+
}
|
|
293
|
+
/** Last `<date>` with a `<day>` whose `date-type` is not in `excludeTypes`. */
|
|
294
|
+
lastDateWithDay(dates, excludeTypes) {
|
|
295
|
+
const eligible = dates.filter((d) => {
|
|
296
|
+
if (!this.dateHasDay(d))
|
|
297
|
+
return false;
|
|
298
|
+
return !excludeTypes.has(this.jatsDateType(d));
|
|
203
299
|
});
|
|
204
|
-
|
|
205
|
-
|
|
300
|
+
return eligible.at(-1);
|
|
301
|
+
}
|
|
302
|
+
fallbackDatePick(node, source) {
|
|
303
|
+
const dateType = this.jatsDateType(node) || 'unknown';
|
|
304
|
+
const xpath = source === 'pub-history'
|
|
305
|
+
? `article-meta/pub-history/event/date[@date-type="${dateType}"]`
|
|
306
|
+
: `article-meta/history/date[@date-type="${dateType}"]`;
|
|
307
|
+
return {
|
|
308
|
+
node,
|
|
309
|
+
warn: `Using JATS ${source} date as publication date`,
|
|
310
|
+
note: xpath,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
findHistoryDateByType(types) {
|
|
314
|
+
const wanted = new Set(types.map((t) => t.toLowerCase()));
|
|
315
|
+
const found = this.historyDates.find((d) => {
|
|
316
|
+
return wanted.has(this.jatsDateType(d));
|
|
317
|
+
});
|
|
318
|
+
if (found && this.dateHasDay(found))
|
|
319
|
+
return found;
|
|
206
320
|
return undefined;
|
|
207
321
|
}
|
|
322
|
+
get historyAcceptedDate() {
|
|
323
|
+
return this.findHistoryDateByType(['accepted', 'accept']);
|
|
324
|
+
}
|
|
325
|
+
get pubHistoryFallbackDate() {
|
|
326
|
+
return this.lastDateWithDay(this.pubHistoryDates, Jats.PUB_HISTORY_PRIMARY_DATE_TYPES);
|
|
327
|
+
}
|
|
328
|
+
get historyFallbackDate() {
|
|
329
|
+
return this.lastDateWithDay(this.historyDates, Jats.HISTORY_PRIMARY_DATE_TYPES);
|
|
330
|
+
}
|
|
208
331
|
/**
|
|
209
332
|
* Some sources store important dates under:
|
|
210
333
|
* `<article-meta><pub-history><event><date date-type="...">...</date></event></pub-history>`.
|
|
@@ -215,12 +338,8 @@ export class Jats {
|
|
|
215
338
|
}
|
|
216
339
|
findPubHistoryDateByType(types) {
|
|
217
340
|
const wanted = new Set(types.map((t) => t.toLowerCase()));
|
|
218
|
-
const found = this.pubHistoryDates.find((d) =>
|
|
219
|
-
|
|
220
|
-
const dt = String((_a = d['date-type']) !== null && _a !== void 0 ? _a : '').toLowerCase();
|
|
221
|
-
return wanted.has(dt);
|
|
222
|
-
});
|
|
223
|
-
if (found && select(Tags.day, found))
|
|
341
|
+
const found = this.pubHistoryDates.find((d) => wanted.has(this.jatsDateType(d)));
|
|
342
|
+
if (found && this.dateHasDay(found))
|
|
224
343
|
return found;
|
|
225
344
|
return undefined;
|
|
226
345
|
}
|
|
@@ -338,6 +457,14 @@ export class Jats {
|
|
|
338
457
|
return xml;
|
|
339
458
|
}
|
|
340
459
|
}
|
|
460
|
+
Jats.PUB_HISTORY_PRIMARY_DATE_TYPES = new Set([
|
|
461
|
+
'pub',
|
|
462
|
+
'published',
|
|
463
|
+
'publication',
|
|
464
|
+
'accepted',
|
|
465
|
+
'accept',
|
|
466
|
+
]);
|
|
467
|
+
Jats.HISTORY_PRIMARY_DATE_TYPES = new Set(['accepted', 'accept']);
|
|
341
468
|
function hasSingleArticle(element) {
|
|
342
469
|
if (element.name === 'article') {
|
|
343
470
|
return true;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAEnC,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,KAAK,GAAG,SAAS,EACxB,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,QAIzB"}
|
package/dist/messages.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { fileWarn, RuleId } from 'myst-common';
|
|
2
|
+
export function recordJatsMessage(vfile, reason, opts) {
|
|
3
|
+
if (!vfile)
|
|
4
|
+
return;
|
|
5
|
+
fileWarn(vfile, reason, { source: 'jats-xml', ruleId: RuleId.jatsParses, note: opts === null || opts === void 0 ? void 0 : opts.note });
|
|
6
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Literal find/replace fixes for malformed JATS seen in the wild.
|
|
3
|
+
* Each entry emits at most one vfile message when it matches.
|
|
4
|
+
*/
|
|
5
|
+
export type KnownXmlDefectRepair = {
|
|
6
|
+
from: string;
|
|
7
|
+
to: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function knownXmlDefectRepairMessage(from: string, to: string): string;
|
|
10
|
+
export type AppliedXmlDefectRepair = {
|
|
11
|
+
repair: KnownXmlDefectRepair;
|
|
12
|
+
count: number;
|
|
13
|
+
};
|
|
14
|
+
export type RepairKnownXmlDefectsResult = {
|
|
15
|
+
xml: string;
|
|
16
|
+
applied: AppliedXmlDefectRepair[];
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Apply KNOWN_XML_DEFECT_REPAIRS in order. Each rule uses a single literal
|
|
20
|
+
* `from` → `to` substitution (all occurrences of `from`).
|
|
21
|
+
*/
|
|
22
|
+
export declare function repairKnownXmlDefects(xml: string): RepairKnownXmlDefectsResult;
|
|
23
|
+
//# sourceMappingURL=repairKnownXmlDefects.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repairKnownXmlDefects.d.ts","sourceRoot":"","sources":["../src/repairKnownXmlDefects.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AASF,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAE5E;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,sBAAsB,EAAE,CAAC;CACnC,CAAC;AAEF;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,2BAA2B,CAW9E"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const KNOWN_XML_DEFECT_REPAIRS = [
|
|
2
|
+
{
|
|
3
|
+
from: '<fn id="n1"fn-type="equal">',
|
|
4
|
+
to: '<fn id="n1" fn-type="equal">',
|
|
5
|
+
},
|
|
6
|
+
];
|
|
7
|
+
export function knownXmlDefectRepairMessage(from, to) {
|
|
8
|
+
return `replaced ${from} with ${to}`;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Apply KNOWN_XML_DEFECT_REPAIRS in order. Each rule uses a single literal
|
|
12
|
+
* `from` → `to` substitution (all occurrences of `from`).
|
|
13
|
+
*/
|
|
14
|
+
export function repairKnownXmlDefects(xml) {
|
|
15
|
+
const applied = [];
|
|
16
|
+
let next = xml;
|
|
17
|
+
for (const repair of KNOWN_XML_DEFECT_REPAIRS) {
|
|
18
|
+
if (!next.includes(repair.from))
|
|
19
|
+
continue;
|
|
20
|
+
const parts = next.split(repair.from);
|
|
21
|
+
const count = parts.length - 1;
|
|
22
|
+
next = parts.join(repair.to);
|
|
23
|
+
applied.push({ repair, count });
|
|
24
|
+
}
|
|
25
|
+
return { xml: next, applied };
|
|
26
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type SanitizeXmlEntitiesResult = {
|
|
2
|
+
xml: string;
|
|
3
|
+
/** How many bare `&` were rewritten to `&`. */
|
|
4
|
+
escapedBareAmpersandCount: number;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Escape bare ampersands so xml-js (and other XML parsers) do not fail with
|
|
8
|
+
* "Invalid character in entity name" / unterminated reference errors.
|
|
9
|
+
*
|
|
10
|
+
* Existing `&`, `–`, `{`, etc. are left unchanged; xml-js expands
|
|
11
|
+
* those when parsing.
|
|
12
|
+
*/
|
|
13
|
+
export declare function sanitizeXmlEntities(xml: string): SanitizeXmlEntitiesResult;
|
|
14
|
+
//# sourceMappingURL=sanitizeXmlEntities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitizeXmlEntities.d.ts","sourceRoot":"","sources":["../src/sanitizeXmlEntities.ts"],"names":[],"mappings":"AAmBA,MAAM,MAAM,yBAAyB,GAAG;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,mDAAmD;IACnD,yBAAyB,EAAE,MAAM,CAAC;CACnC,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,yBAAyB,CAO1E"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { characterEntities } from 'character-entities';
|
|
2
|
+
/**
|
|
3
|
+
* Names of HTML/XML character references recognized when deciding whether `&` starts
|
|
4
|
+
* a valid entity (e.g. `&`, `–`, `&`, `&`).
|
|
5
|
+
*/
|
|
6
|
+
const VALID_ENTITY_NAMES = Object.keys(characterEntities).map((name) => name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
|
|
7
|
+
/**
|
|
8
|
+
* `&` not followed by a known named entity or numeric reference — e.g. "Bill & Melinda".
|
|
9
|
+
* Matches the escape step from common JATS preprocessors before XML parse.
|
|
10
|
+
*/
|
|
11
|
+
const BARE_AMPERSAND_RE = new RegExp(`&(?!(?:${VALID_ENTITY_NAMES.join('|')}|#(?:x[0-9a-fA-F]+|\\d+));)`, 'g');
|
|
12
|
+
/**
|
|
13
|
+
* Escape bare ampersands so xml-js (and other XML parsers) do not fail with
|
|
14
|
+
* "Invalid character in entity name" / unterminated reference errors.
|
|
15
|
+
*
|
|
16
|
+
* Existing `&`, `–`, `{`, etc. are left unchanged; xml-js expands
|
|
17
|
+
* those when parsing.
|
|
18
|
+
*/
|
|
19
|
+
export function sanitizeXmlEntities(xml) {
|
|
20
|
+
let escapedBareAmpersandCount = 0;
|
|
21
|
+
const sanitized = xml.replace(BARE_AMPERSAND_RE, () => {
|
|
22
|
+
escapedBareAmpersandCount += 1;
|
|
23
|
+
return '&';
|
|
24
|
+
});
|
|
25
|
+
return { xml: sanitized, escapedBareAmpersandCount };
|
|
26
|
+
}
|
package/dist/utils.js
CHANGED
|
@@ -50,7 +50,7 @@ function markForDeletion(nodes) {
|
|
|
50
50
|
});
|
|
51
51
|
}
|
|
52
52
|
export function processAffiliation(aff) {
|
|
53
|
-
var _a, _b;
|
|
53
|
+
var _a, _b, _c, _d;
|
|
54
54
|
const id = aff.id;
|
|
55
55
|
let ror;
|
|
56
56
|
let isni;
|
|
@@ -84,7 +84,7 @@ export function processAffiliation(aff) {
|
|
|
84
84
|
countryNode,
|
|
85
85
|
]);
|
|
86
86
|
remove(aff, '__delete__');
|
|
87
|
-
const affChildren = aff.children.filter((child) => child.type !== 'label');
|
|
87
|
+
const affChildren = (_d = (_c = aff.children) === null || _c === void 0 ? void 0 : _c.filter((child) => child.type !== 'label')) !== null && _d !== void 0 ? _d : [];
|
|
88
88
|
let institution;
|
|
89
89
|
if (affChildren.filter((child) => ['text', 'institution-wrap', 'institution'].includes(child.type))
|
|
90
90
|
.length === affChildren.length) {
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const version = '1.1.
|
|
1
|
+
const version = '1.1.5';
|
|
2
2
|
export default version;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jats-xml",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
4
4
|
"description": "Types and utilities for working with JATS in Typescript",
|
|
5
5
|
"author": "Rowan Cockett <rowan@continuousfoundation.org>",
|
|
6
6
|
"homepage": "https://github.com/continuous-foundation/jats",
|
|
@@ -39,15 +39,17 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"adm-zip": "^0.5.10",
|
|
42
|
+
"character-entities": "^2.0.2",
|
|
42
43
|
"doi-utils": "^2.0.6",
|
|
43
|
-
"jats-tags": "^1.1.
|
|
44
|
-
"jats-utils": "^1.1.
|
|
44
|
+
"jats-tags": "^1.1.5",
|
|
45
|
+
"jats-utils": "^1.1.5",
|
|
45
46
|
"myst-cli-utils": "^2.0.11",
|
|
46
47
|
"myst-common": "^1.7.5",
|
|
47
48
|
"myst-frontmatter": "^1.7.5",
|
|
48
49
|
"node-fetch": "^3.3.1",
|
|
49
|
-
"unist-util-select": "^4.0.0",
|
|
50
50
|
"unist-util-remove": "4.0.0",
|
|
51
|
+
"unist-util-select": "^4.0.0",
|
|
52
|
+
"vfile": "^5.0.0",
|
|
51
53
|
"which": "^3.0.1",
|
|
52
54
|
"xml-js": "^1.6.11"
|
|
53
55
|
},
|