UNPKG

meca

Version:
196 lines (195 loc) 9.2 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import fs from 'node:fs'; import path from 'node:path'; import { js2xml, xml2js } from 'xml-js'; import { convertToUnist } from 'jats-utils'; import { xmllintValidate } from 'jats-xml'; import { tic } from 'myst-cli-utils'; import { createTempFolder, elementWithText, removeTempFolder, select, selectAll } from './utils.js'; export const MANIFEST = 'manifest.xml'; export const MANIFEST_DTD = 'manifest-1.0.dtd'; export var ItemTypes; (function (ItemTypes) { ItemTypes["articleMetadata"] = "article-metadata"; ItemTypes["articleSupportingFile"] = "article-supporting-file"; ItemTypes["manuscript"] = "manuscript"; ItemTypes["manuscriptSupportingFile"] = "manuscript-supporting-file"; ItemTypes["articleSource"] = "article-source"; ItemTypes["articleSourceEnvironment"] = "article-source-environment"; ItemTypes["articleSourceDirectory"] = "article-source-directory"; ItemTypes["transferMetadata"] = "transfer-metadata"; })(ItemTypes || (ItemTypes = {})); export class ManifestXml { constructor(data, opts) { var _a, _b; const toc = tic(); this.log = (_a = opts === null || opts === void 0 ? void 0 : opts.log) !== null && _a !== void 0 ? _a : console; this.rawXML = data; if (opts === null || opts === void 0 ? void 0 : opts.source) this.source = opts.source; try { this.raw = xml2js(data, { compact: false }); } catch (error) { throw new Error('Problem parsing the TransferXML document, please ensure it is XML'); } const { declaration, elements } = this.raw; this.declaration = declaration === null || declaration === void 0 ? void 0 : declaration.attributes; if (!((elements === null || elements === void 0 ? void 0 : elements.length) === 2 && elements[0].type === 'doctype' && elements[1].name === 'manifest')) { throw new Error('Element <manifest> is not the only element of the manifest.xml'); } this.doctype = elements[0].doctype; const converted = convertToUnist(elements[1]); this.tree = select('manifest', converted); (_b = this.log) === null || _b === void 0 ? void 0 : _b.debug(toc('Parsed and converted manifest.xml to unist tree in %s')); } get localDtd() { // This works both compiled and in tests const dtd = fs.existsSync(path.join(__dirname, MANIFEST_DTD)) ? path.join(__dirname, MANIFEST_DTD) : path.join(__dirname, '..', 'static', MANIFEST_DTD); if (fs.existsSync(dtd)) return dtd; throw new Error(`Unable to locate manifest DTD file ${MANIFEST_DTD} in meca lib distribution`); } validateXml(remoteDtd) { return __awaiter(this, void 0, void 0, function* () { const tempFolder = createTempFolder(); fs.writeFileSync(path.join(tempFolder, MANIFEST), this.rawXML); let dtdFile = this.localDtd; if (remoteDtd) { const data = yield (yield fetch(remoteDtd)).text(); dtdFile = path.join(tempFolder, MANIFEST_DTD); fs.writeFileSync(dtdFile, data); } const manifestIsValid = yield xmllintValidate(this, path.join(tempFolder, MANIFEST), dtdFile).catch(() => { this.log.error(`${MANIFEST} DTD validation failed`); return false; }); removeTempFolder(tempFolder); return manifestIsValid; }); } get version() { return this.tree['manifest-version'] || this.tree.version; } get items() { const items = selectAll(`item`, this.tree) .map((item) => { var _a, _b, _c, _d, _e, _f, _g, _h; const instances = selectAll(`instance`, item); if (instances.length === 0) { this.log.warn('Item without an instance'); return undefined; } if (instances.length > 1) { this.log.warn('Item has multiple instances, only the first is used.'); } const instance = instances[0]; const title = (_b = (_a = select(`item-title`, item)) === null || _a === void 0 ? void 0 : _a.children) === null || _b === void 0 ? void 0 : _b[0].value; const description = (_d = (_c = select(`item-description`, item)) === null || _c === void 0 ? void 0 : _c.children) === null || _d === void 0 ? void 0 : _d[0].value; const fileOrder = (_f = (_e = select(`file-order`, item)) === null || _e === void 0 ? void 0 : _e.children) === null || _f === void 0 ? void 0 : _f[0].value; const metadata = Object.fromEntries((_h = (_g = selectAll('metadata', item)) === null || _g === void 0 ? void 0 : _g.map((n) => { var _a; return [ n['metadata-name'] || n.name, (_a = n === null || n === void 0 ? void 0 : n.children) === null || _a === void 0 ? void 0 : _a[0].value, ]; })) !== null && _h !== void 0 ? _h : []); return { id: item.id, itemType: item['item-type'], version: item['item-version'] || item['version'], title, description, href: instance['xlink:href'] || instance.href, mediaType: instance['media-type'], fileOrder, metadata, }; }) .filter((item) => !!item); return items; } get itemTypes() { const itemTypes = new Set(); this.items.forEach((item) => { if (item.itemType) itemTypes.add(item.itemType); }); return [...itemTypes]; } get articleMetadata() { const items = this.items.filter((item) => item.itemType === ItemTypes.articleMetadata); if (items.length > 1) this.log.warn('More than 1 article metadata found'); return items[0]; } get transferMetadata() { return this.items.filter((item) => item.itemType === ItemTypes.transferMetadata); } } function writeManifestItem(item, opts) { const { id, version, href, itemType, mediaType, title, description, fileOrder, metadata } = item; return { type: 'element', name: 'item', attributes: { id, 'item-type': itemType, 'item-version': version, }, elements: [ title ? elementWithText('item-title', title) : undefined, description ? elementWithText('item-description', description) : undefined, fileOrder ? elementWithText('file-order', fileOrder) : undefined, metadata && Object.keys(metadata !== null && metadata !== void 0 ? metadata : {}).length > 0 ? { type: 'element', name: 'item-metadata', elements: Object.entries(metadata).map(([k, v]) => elementWithText('metadata', v, { 'metadata-name': k })), } : undefined, { type: 'element', name: 'instance', attributes: { 'media-type': mediaType, [(opts === null || opts === void 0 ? void 0 : opts.noXLink) ? 'href' : 'xlink:href']: href, }, }, ].filter((e) => !!e), }; } export function createManifestXml(manifestItems, opts) { var _a; const element = { type: 'element', elements: [ { type: 'doctype', doctype: `manifest PUBLIC "-//MECA//DTD Manifest v1.0//en" "${(_a = opts === null || opts === void 0 ? void 0 : opts.dtdUrl) !== null && _a !== void 0 ? _a : 'https://meca.zip/manifest-1.0.dtd'}"`, }, { type: 'element', name: 'manifest', attributes: Object.assign({ 'manifest-version': '1', xmlns: 'https://manuscriptexchange.org/schema/manifest' }, ((opts === null || opts === void 0 ? void 0 : opts.noXLink) ? {} : { 'xmlns:xlink': 'http://www.w3.org/1999/xlink' })), elements: manifestItems.map((item) => writeManifestItem(item, opts)), }, ], declaration: { attributes: { version: '1.0', encoding: 'UTF-8' } }, }; const manifest = js2xml(element, { compact: false, spaces: 2, }); return manifest; }