UNPKG

salve-annos

Version:

A fork with support for documentation of Salve, a Javascript library which implements a validator able to validate an XML document on the basis of a subset of RelaxNG.

225 lines 9.3 kB
/** * A parser used for testing. * @author Louis-Dominique Dubeau * @license MPL 2.0 * @copyright Mangalam Research Center for Buddhist Languages */ "use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); 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()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.parse = parse; const file_url_1 = __importDefault(require("file-url")); const fs = __importStar(require("fs")); const path = __importStar(require("path")); const saxes_1 = require("saxes"); const validate_1 = require("./validate"); // tslint:disable no-console const parser = new saxes_1.SaxesParser({ xmlns: true }); function grammarFromSource(rngSource) { return __awaiter(this, void 0, void 0, function* () { if (rngSource instanceof validate_1.Grammar) { return rngSource; } const rngSourceContent = fs.readFileSync(path.resolve(rngSource), "utf8").toString(); // We try loading the tree as a JSON file. It may not work if the file is not // actually JSON. let obj; try { obj = JSON.parse(rngSourceContent); } // tslint:disable-next-line:no-empty catch (_a) { } if (obj !== undefined) { return (0, validate_1.readTreeFromJSON)(obj); } // Treat it as a Relax NG schema. const conversionResult = yield (0, validate_1.convertRNGToPattern)(new URL((0, file_url_1.default)(rngSource))); const schemaText = conversionResult.schemaText; console.log(schemaText); // I will then be able to pass this on to a schematron validator return conversionResult.pattern; }); } /** * Parse an XML file and validate it against a schema. This function is meant * for **illustration purposes** and is used in testing. It does cut * corners. You should not use this for production code. * * @param rngSource It may be a [[Grammar]] object pre-built from the Relax NG * schema you want to use. Or it can be a JSON string, which is the contents of * a file created with ``salve-convert``. Or it can be a path to a local file. * * @param xmlSource The XML contents to parse and validate. * * @param mute If true, don't report errors verbosely. * * @returns A promise resolving ``true`` if there were errors, ``false`` * otherwise. */ // tslint:disable-next-line: max-func-body-length function parse(rngSource, xmlSource, mute) { return __awaiter(this, void 0, void 0, function* () { // tslint:disable-next-line:no-parameter-reassignment mute = !!mute; const tree = yield grammarFromSource(rngSource); const nameResolver = new validate_1.DefaultNameResolver(); const walker = tree.newWalker(nameResolver); let error = false; function fireEvent(name, args) { const ret = walker.fireEvent(name, args); if (ret instanceof Array) { error = true; if (!mute) { for (const err of ret) { console.log(`on event ${name}, ${args.join(", ")}`); console.log(err.toString()); } } } } const tagStack = []; let textBuf = ""; function flushTextBuf() { if (textBuf !== "") { fireEvent("text", [textBuf]); textBuf = ""; } } parser.on("opentag", (node) => { flushTextBuf(); const names = Object.keys(node.attributes); const nsDefinitions = []; const attributeEvents = []; names.sort(); for (const name of names) { const attr = node.attributes[name]; if (name === "xmlns") { // xmlns="..." nsDefinitions.push(["", attr.value]); } else if (attr.prefix === "xmlns") { // xmlns:...=... nsDefinitions.push([attr.local, attr.value]); } else { attributeEvents.push(["attributeName", attr.uri, attr.local], ["attributeValue", attr.value]); } } if (nsDefinitions.length !== 0) { nameResolver.enterContext(); for (const definition of nsDefinitions) { nameResolver.definePrefix(definition[0], definition[1]); } } fireEvent("enterStartTag", [node.uri, node.local]); for (const event of attributeEvents) { fireEvent(event[0], event.slice(1)); } fireEvent("leaveStartTag", []); tagStack.push({ uri: node.uri || "", local: node.local || "", hasContext: nsDefinitions.length !== 0, }); }); parser.on("text", (text) => { textBuf += text; }); parser.on("closetag", () => { flushTextBuf(); const tagInfo = tagStack.pop(); if (tagInfo === undefined) { throw new Error("stack underflow"); } fireEvent("endTag", [tagInfo.uri, tagInfo.local]); if (tagInfo.hasContext) { nameResolver.leaveContext(); } }); const entityRe = /^<!ENTITY\s+([^\s]+)\s+(['"])(.*?)\2\s*>\s*/; parser.on("doctype", (doctype) => { // This is an extremely primitive way to handle ENTITY declarations in a // DOCTYPE. It is unlikely to support any kind of complicated construct. // If a reminder need be given then: THIS PARSER IS NOT MEANT TO BE A // GENERAL SOLUTION TO PARSING XML FILES!!! It supports just enough to // perform some testing. let cleaned = doctype .replace(/^.*?\[/, "") .replace(/].*?$/, "") .replace(/<!--(?:.|\n|\r)*?-->/g, "") .trim(); while (cleaned.length !== 0) { const match = entityRe.exec(cleaned); if (match !== null) { const name = match[1]; const value = match[3]; cleaned = cleaned.slice(match[0].length); if (parser.ENTITIES[name] !== undefined) { throw new Error(`redefining entity: ${name}`); } parser.ENTITIES[name] = value; } else { throw new Error(`unexpected construct in DOCTYPE: ${doctype}`); } } console.log(doctype); }); parser.on("end", () => { const result = walker.end(); if (result !== false) { error = true; if (!mute) { for (const err of result) { console.log(`on end`); console.log(err.toString()); } } } }); parser.write(xmlSource).close(); return error; }); } //# sourceMappingURL=parse.js.map