jats-xml 1.1.3 → 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 +181 -41
- 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":"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;AAE7C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAKnC,OAAO,EAEL,KAAK,oBAAoB,EAI1B,MAAM,YAAY,CAAC;AAEpB,KAAK,OAAO,GAAG;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC;AAiBhE,KAAK,YAAY,GAAG,oBAAoB,GAAG;IACzC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAkCF,qBAAa,IAAI;IACf,WAAW,CAAC,EAAE,qBAAqB,CAAC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,OAAO,CAAC,2BAA2B,CAAS;gBAEhC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO;IAiExC,IAAI,WAAW,IAAI,eAAe,CAoEjC;IAED,IAAI,KAAK,IAAI,KAAK,GAAG,SAAS,CAE7B;IAED,IAAI,WAAW,IAAI,WAAW,GAAG,SAAS,CAEzC;IAED,IAAI,WAAW,IAAI,WAAW,GAAG,SAAS,CAEzC;IAED,IAAI,GAAG,IAAI,MAAM,GAAG,SAAS,CAE5B;IAED,IAAI,GAAG,IAAI,MAAM,GAAG,SAAS,CAE5B;IAED,IAAI,IAAI,IAAI,MAAM,GAAG,SAAS,CAE7B;IAED,IAAI,gBAAgB,IAAI,OAAO,EAAE,CAEhC;IAED,IAAI,eAAe,IAAI,OAAO,GAAG,SAAS,CAEzC;IAED;;;OAGG;IACH,IAAI,YAAY,IAAI,aAAa,EAAE,CAElC;IAED;;;OAGG;IACH,OAAO,CAAC,sBAAsB;IAkB9B,OAAO,CAAC,yBAAyB;IA6BjC,OAAO,CAAC,mBAAmB;IAsB3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,8BAA8B,CAMnD;IAEH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,0BAA0B,CAAmC;IAErF,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,UAAU;IAIlB,+EAA+E;IAC/E,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,gBAAgB;IAgBxB,OAAO,CAAC,qBAAqB;IAS7B,IAAI,mBAAmB,IAAI,aAAa,GAAG,SAAS,CAEnD;IAED,IAAI,sBAAsB,IAAI,aAAa,GAAG,SAAS,CAEtD;IAED,IAAI,mBAAmB,IAAI,aAAa,GAAG,SAAS,CAEnD;IAED;;;OAGG;IACH,IAAI,eAAe,IAAI,aAAa,EAAE,CAErC;IAED,OAAO,CAAC,wBAAwB;IAOhC,IAAI,iBAAiB,IAAI,aAAa,GAAG,SAAS,CAEjD;IAED,IAAI,sBAAsB,IAAI,aAAa,GAAG,SAAS,CAEtD;IAED,IAAI,OAAO,IAAI,OAAO,GAAG,SAAS,CAEjC;IAED,IAAI,YAAY,IAAI,YAAY,GAAG,SAAS,CAE3C;IAED,yBAAyB;IACzB,IAAI,QAAQ,IAAI,OAAO,EAAE,CAExB;IAED,IAAI,aAAa,IAAI,YAAY,EAAE,CAElC;IAED,IAAI,iBAAiB,IAAI,iBAAiB,GAAG,SAAS,CAErD;IAED,IAAI,UAAU,IAAI,UAAU,GAAG,SAAS,CAEvC;IAED,IAAI,YAAY,IAAI,YAAY,GAAG,SAAS,CAE3C;IAED,IAAI,eAAe,IAAI,QAAQ,GAAG,SAAS,CAE1C;IAED,IAAI,eAAe,IAAI,QAAQ,GAAG,SAAS,CAE1C;IAED,IAAI,QAAQ,IAAI,QAAQ,GAAG,SAAS,CAEnC;IAED,IAAI,SAAS,IAAI,QAAQ,EAAE,CAE1B;IAED,IAAI,YAAY,IAAI,YAAY,GAAG,SAAS,CAE3C;IAED,IAAI,aAAa,IAAI,YAAY,EAAE,CAElC;IAED,IAAI,cAAc,IAAI,OAAO,EAAE,CAU9B;IAED,IAAI,mBAAmB,IAAI,WAAW,EAAE,CAEvC;IAED,IAAI,IAAI,IAAI,IAAI,GAAG,SAAS,CAE3B;IAED,IAAI,IAAI,IAAI,IAAI,GAAG,SAAS,CAE3B;IAED,IAAI,WAAW,IAAI,UAAU,EAAE,CAE9B;IAED,uCAAuC;IACvC,IAAI,OAAO,IAAI,OAAO,GAAG,SAAS,CAEjC;IAED,+DAA+D;IAC/D,IAAI,QAAQ,IAAI,OAAO,EAAE,CAGxB;IAED,yEAAyE;IACzE,IAAI,UAAU,IAAI,SAAS,EAAE,CAE5B;IAED,IAAI;IAeJ,SAAS,CAAC,IAAI,CAAC,EAAE,YAAY,GAAG,MAAM;CAoBvC"}
|
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) {
|
|
@@ -37,15 +40,46 @@ function significantChildElements(elements) {
|
|
|
37
40
|
return true;
|
|
38
41
|
});
|
|
39
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Drop top-level processing instructions (e.g. xml-stylesheet) from the prolog.
|
|
45
|
+
* Malformed prologs such as <?version xml="1.0"?> are parsed this way by xml-js.
|
|
46
|
+
*/
|
|
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
|
+
});
|
|
55
|
+
}
|
|
40
56
|
export class Jats {
|
|
41
57
|
constructor(data, opts) {
|
|
42
|
-
var _a
|
|
58
|
+
var _a;
|
|
59
|
+
this.frontmatterMessagesRecorded = false;
|
|
43
60
|
const toc = tic();
|
|
44
61
|
this.log = opts === null || opts === void 0 ? void 0 : opts.log;
|
|
45
62
|
if (opts === null || opts === void 0 ? void 0 : opts.source)
|
|
46
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
|
+
}
|
|
47
81
|
try {
|
|
48
|
-
this.raw = xml2js(
|
|
82
|
+
this.raw = xml2js(parseInput, {
|
|
49
83
|
compact: false,
|
|
50
84
|
// Preserve whitespace-only text nodes between elements. This is usually unnecessary except inside <preformat>.
|
|
51
85
|
// convertToUnist drops these outside preformat so other content is processed independent of arbitrary xml whitespace.
|
|
@@ -57,9 +91,15 @@ export class Jats {
|
|
|
57
91
|
}
|
|
58
92
|
const { declaration, elements } = this.raw;
|
|
59
93
|
this.declaration = declaration === null || declaration === void 0 ? void 0 : declaration.attributes;
|
|
60
|
-
const filteredElements = significantChildElements(elements)
|
|
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
|
+
});
|
|
61
101
|
if ((filteredElements === null || filteredElements === void 0 ? void 0 : filteredElements.length) && filteredElements[0].type !== 'doctype') {
|
|
62
|
-
(
|
|
102
|
+
warnProlog('JATS is missing DOCTYPE declaration; inserted empty doctype');
|
|
63
103
|
filteredElements.unshift({ type: 'doctype' });
|
|
64
104
|
}
|
|
65
105
|
if (!((filteredElements === null || filteredElements === void 0 ? void 0 : filteredElements.length) === 2 &&
|
|
@@ -68,36 +108,32 @@ export class Jats {
|
|
|
68
108
|
throw new Error('JATS must be structured as <!DOCTYPE><article>...</article>');
|
|
69
109
|
}
|
|
70
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
|
+
}
|
|
71
114
|
const converted = convertToUnist(filteredElements[1]);
|
|
72
115
|
this.tree = select('article', converted);
|
|
73
|
-
(
|
|
116
|
+
(_a = this.log) === null || _a === void 0 ? void 0 : _a.debug(toc('Parsed and converted JATS to unist tree in %s'));
|
|
74
117
|
}
|
|
75
118
|
get frontmatter() {
|
|
76
|
-
var _a, _b, _c, _d, _e, _f, _g, _h
|
|
119
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
77
120
|
const title = this.articleTitle;
|
|
78
121
|
const subtitle = this.articleSubtitle;
|
|
79
122
|
const short_title = this.articleAltTitle;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const month = (d.getUTCMonth() + 1).toString().padStart(2, '0');
|
|
89
|
-
const day = d.getUTCDate().toString().padStart(2, '0');
|
|
90
|
-
date = `${year}-${month}-${day}`;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
const authors = (_d = this.articleAuthors) === null || _d === void 0 ? void 0 : _d.map((auth) => {
|
|
94
|
-
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,
|
|
95
131
|
});
|
|
96
|
-
const affiliations = (
|
|
132
|
+
const affiliations = (_b = this.articleAffiliations) === null || _b === void 0 ? void 0 : _b.map((aff) => {
|
|
97
133
|
return processAffiliation(aff);
|
|
98
134
|
});
|
|
99
|
-
const keywords = (
|
|
100
|
-
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;
|
|
101
137
|
const journalCollSubject = select(`${Tags.subjGroup}[subj-group-type="hwp-journal-coll"] ${Tags.subject}`, subjectScope);
|
|
102
138
|
const articleSubject = journalCollSubject !== null && journalCollSubject !== void 0 ? journalCollSubject : select(Tags.subject, subjectScope);
|
|
103
139
|
const journalTitle = select(Tags.journalTitle, this.front);
|
|
@@ -111,13 +147,13 @@ export class Jats {
|
|
|
111
147
|
}
|
|
112
148
|
else if (selectAll('ext-link', license).length === 1) {
|
|
113
149
|
// this should only happen if there is only one ext-link
|
|
114
|
-
licenseString = (
|
|
150
|
+
licenseString = (_f = select('ext-link', license)['xlink:href']) !== null && _f !== void 0 ? _f : null;
|
|
115
151
|
}
|
|
116
152
|
else if (license) {
|
|
117
153
|
licenseString = toText(license);
|
|
118
154
|
}
|
|
119
155
|
let openAccess;
|
|
120
|
-
const licenseType = (
|
|
156
|
+
const licenseType = (_g = license === null || license === void 0 ? void 0 : license['license-type']) === null || _g === void 0 ? void 0 : _g.toLowerCase();
|
|
121
157
|
if (licenseType && ['openaccess', 'open-access'].includes(licenseType)) {
|
|
122
158
|
openAccess = true;
|
|
123
159
|
}
|
|
@@ -131,10 +167,10 @@ export class Jats {
|
|
|
131
167
|
const pmc = this.pmc;
|
|
132
168
|
const identifiers = pmc ? { pmcid: `PMC${pmc}` } : undefined;
|
|
133
169
|
const frontmatter = validatePageFrontmatter({
|
|
134
|
-
title:
|
|
170
|
+
title: titleText || undefined,
|
|
135
171
|
subtitle: subtitle ? toText(subtitle) : undefined,
|
|
136
172
|
short_title: short_title ? toText(short_title) : undefined,
|
|
137
|
-
doi: (
|
|
173
|
+
doi: (_h = this.doi) !== null && _h !== void 0 ? _h : undefined,
|
|
138
174
|
identifiers,
|
|
139
175
|
date,
|
|
140
176
|
authors: authors.length ? authors : undefined,
|
|
@@ -182,16 +218,116 @@ export class Jats {
|
|
|
182
218
|
var _a;
|
|
183
219
|
return selectAll('history date', (_a = this.articleMeta) !== null && _a !== void 0 ? _a : this.front);
|
|
184
220
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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));
|
|
190
299
|
});
|
|
191
|
-
|
|
192
|
-
|
|
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;
|
|
193
320
|
return undefined;
|
|
194
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
|
+
}
|
|
195
331
|
/**
|
|
196
332
|
* Some sources store important dates under:
|
|
197
333
|
* `<article-meta><pub-history><event><date date-type="...">...</date></event></pub-history>`.
|
|
@@ -202,12 +338,8 @@ export class Jats {
|
|
|
202
338
|
}
|
|
203
339
|
findPubHistoryDateByType(types) {
|
|
204
340
|
const wanted = new Set(types.map((t) => t.toLowerCase()));
|
|
205
|
-
const found = this.pubHistoryDates.find((d) =>
|
|
206
|
-
|
|
207
|
-
const dt = String((_a = d['date-type']) !== null && _a !== void 0 ? _a : '').toLowerCase();
|
|
208
|
-
return wanted.has(dt);
|
|
209
|
-
});
|
|
210
|
-
if (found && select(Tags.day, found))
|
|
341
|
+
const found = this.pubHistoryDates.find((d) => wanted.has(this.jatsDateType(d)));
|
|
342
|
+
if (found && this.dateHasDay(found))
|
|
211
343
|
return found;
|
|
212
344
|
return undefined;
|
|
213
345
|
}
|
|
@@ -325,6 +457,14 @@ export class Jats {
|
|
|
325
457
|
return xml;
|
|
326
458
|
}
|
|
327
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']);
|
|
328
468
|
function hasSingleArticle(element) {
|
|
329
469
|
if (element.name === 'article') {
|
|
330
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
|
},
|