UNPKG

8.4 kBJavaScriptView Raw
1"use strict";
2var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3 function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4 return new (P || (P = Promise))(function (resolve, reject) {
5 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7 function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8 step((generator = generator.apply(thisArg, _arguments || [])).next());
9 });
10};
11Object.defineProperty(exports, "__esModule", { value: true });
12exports.validateEffects = exports.attrValueLocation = exports.attrLocation = exports.offsetToLineAndColumn = exports.copyFile = exports.writeFile = exports.readFile = exports.shouldInline = exports.logWarning = exports.logVerbose = exports.replaceTextNode = exports.domWalkBackward = exports.htmlToDom = exports.emdTextNode = exports.wrapEmdFailure = exports.warnEmdFailure = void 0;
13const jsdom = require("jsdom");
14const chalk = require("chalk");
15const emd = require("ecmarkdown");
16const fs = require("fs");
17const path = require("path");
18const utils_1 = require("./lint/utils");
19function warnEmdFailure(report, node, e) {
20 if (typeof e.line === 'number' && typeof e.column === 'number') {
21 report({
22 type: 'contents',
23 ruleId: 'invalid-emd',
24 message: `ecmarkdown failed to parse: ${e.message}`,
25 node,
26 nodeRelativeLine: e.line,
27 nodeRelativeColumn: e.column,
28 });
29 }
30 else {
31 report({
32 type: 'node',
33 ruleId: 'invalid-emd',
34 message: `ecmarkdown failed to parse: ${e.message}`,
35 node,
36 });
37 }
38}
39exports.warnEmdFailure = warnEmdFailure;
40function wrapEmdFailure(src) {
41 return `#### ECMARKDOWN PARSE FAILED ####<pre>${src}</pre>`;
42}
43exports.wrapEmdFailure = wrapEmdFailure;
44/*@internal*/
45function emdTextNode(spec, node, namespace) {
46 const loc = spec.locate(node);
47 let c;
48 if ((loc === null || loc === void 0 ? void 0 : loc.endTag) == null) {
49 c = node.textContent.replace(/</g, '&lt;');
50 }
51 else {
52 const start = loc.startTag.endOffset;
53 const end = loc.endTag.startOffset;
54 c = loc.source.slice(start, end);
55 }
56 let processed;
57 try {
58 const parts = emd.parseFragment(c);
59 if (spec.opts.lintSpec && loc != null) {
60 const nonterminals = utils_1.collectNonterminalsFromEmd(parts).map(({ name, loc }) => ({
61 name,
62 loc,
63 node,
64 namespace,
65 }));
66 spec._ntStringRefs = spec._ntStringRefs.concat(nonterminals);
67 }
68 processed = emd.emit(parts);
69 }
70 catch (e) {
71 warnEmdFailure(spec.warn, node, e);
72 processed = wrapEmdFailure(c);
73 }
74 const template = spec.doc.createElement('template');
75 template.innerHTML = processed;
76 replaceTextNode(node, template.content);
77}
78exports.emdTextNode = emdTextNode;
79/*@internal*/
80function htmlToDom(html) {
81 return new jsdom.JSDOM(html, { includeNodeLocations: true });
82}
83exports.htmlToDom = htmlToDom;
84/*@internal*/
85function domWalkBackward(root, cb) {
86 const childNodes = root.childNodes;
87 const childLen = childNodes.length;
88 for (let i = childLen - 1; i >= 0; i--) {
89 const node = childNodes[i];
90 if (node.nodeType !== 1)
91 continue;
92 const cont = cb(node);
93 if (cont === false)
94 continue;
95 domWalkBackward(node, cb);
96 }
97}
98exports.domWalkBackward = domWalkBackward;
99/*@internal*/
100function replaceTextNode(node, frag) {
101 // Append all the nodes
102 const parent = node.parentNode;
103 if (!parent)
104 return [];
105 const newXrefNodes = Array.from(frag.querySelectorAll('EMU-XREF'));
106 const first = frag.childNodes[0];
107 if (first.nodeType === 3) {
108 node.textContent = first.textContent;
109 frag.removeChild(first);
110 }
111 else {
112 // set it to empty because we don't want to break iteration
113 // (I think it should work to delete it... investigate possible jsdom bug)
114 node.textContent = '';
115 }
116 parent.insertBefore(frag, node.nextSibling);
117 return newXrefNodes;
118}
119exports.replaceTextNode = replaceTextNode;
120/*@internal*/
121function logVerbose(str) {
122 const dateString = new Date().toISOString();
123 console.error(chalk.gray('[' + dateString + '] ') + str);
124}
125exports.logVerbose = logVerbose;
126/*@internal*/
127function logWarning(str) {
128 const dateString = new Date().toISOString();
129 console.error(chalk.gray('[' + dateString + '] ') + chalk.red(str));
130}
131exports.logWarning = logWarning;
132/*@internal*/
133function shouldInline(node) {
134 let parent = node.parentNode;
135 if (!parent)
136 return false;
137 while (parent &&
138 parent.parentNode &&
139 (parent.nodeName === 'EMU-GRAMMAR' ||
140 parent.nodeName === 'EMU-IMPORT' ||
141 parent.nodeName === 'INS' ||
142 parent.nodeName === 'DEL')) {
143 parent = parent.parentNode;
144 }
145 return (['EMU-ANNEX', 'EMU-CLAUSE', 'EMU-INTRO', 'EMU-NOTE', 'BODY'].indexOf(parent.nodeName) === -1);
146}
147exports.shouldInline = shouldInline;
148/*@internal*/
149function readFile(file) {
150 return new Promise((resolve, reject) => {
151 fs.readFile(file, 'utf8', (err, data) => (err ? reject(err) : resolve(data)));
152 });
153}
154exports.readFile = readFile;
155/*@internal*/
156function writeFile(file, content) {
157 return new Promise((resolve, reject) => {
158 // we could do this async, but it's not worth worrying about
159 fs.mkdirSync(path.dirname(file), { recursive: true });
160 fs.writeFile(file, content, { encoding: 'utf8' }, err => (err ? reject(err) : resolve()));
161 });
162}
163exports.writeFile = writeFile;
164/*@internal*/
165function copyFile(src, dest) {
166 return __awaiter(this, void 0, void 0, function* () {
167 const content = yield readFile(src);
168 yield writeFile(dest, content);
169 });
170}
171exports.copyFile = copyFile;
172function offsetToLineAndColumn(string, offset) {
173 const lines = string.split('\n');
174 let line = 0;
175 let seen = 0;
176 while (true) {
177 if (line >= lines.length) {
178 throw new Error(`offset ${offset} exceeded string ${JSON.stringify(string)}`);
179 }
180 if (seen + lines[line].length >= offset) {
181 break;
182 }
183 seen += lines[line].length + 1; // +1 for the '\n'
184 ++line;
185 }
186 const column = offset - seen;
187 return { line: line + 1, column: column + 1 };
188}
189exports.offsetToLineAndColumn = offsetToLineAndColumn;
190function attrLocation(source, loc, attr) {
191 const attrLoc = loc.startTag.attrs[attr];
192 if (attrLoc == null) {
193 return { line: loc.startTag.line, column: loc.startTag.col };
194 }
195 else {
196 return { line: attrLoc.line, column: attrLoc.col };
197 }
198}
199exports.attrLocation = attrLocation;
200function attrValueLocation(source, loc, attr) {
201 var _a, _b;
202 const attrLoc = loc.startTag.attrs[attr];
203 if (attrLoc == null || source == null) {
204 return { line: loc.startTag.line, column: loc.startTag.col };
205 }
206 else {
207 const tagText = source.slice(attrLoc.startOffset, attrLoc.endOffset);
208 // RegExp.escape when
209 const matcher = new RegExp(attr.replace(/[/\\^$*+?.()|[\]{}]/g, '\\$&') + '="?', 'i');
210 return { line: attrLoc.line, column: attrLoc.col + ((_b = (_a = tagText.match(matcher)) === null || _a === void 0 ? void 0 : _a[0].length) !== null && _b !== void 0 ? _b : 0) };
211 }
212}
213exports.attrValueLocation = attrValueLocation;
214const KNOWN_EFFECTS = ['user-code'];
215function validateEffects(spec, effectsRaw, node) {
216 const effects = [];
217 const unknownEffects = [];
218 for (const e of effectsRaw) {
219 if (KNOWN_EFFECTS.indexOf(e) !== -1) {
220 effects.push(e);
221 }
222 else {
223 unknownEffects.push(e);
224 }
225 }
226 if (unknownEffects.length !== 0) {
227 spec.warn({
228 type: 'node',
229 ruleId: 'unknown-effects',
230 message: `unknown effects: ${unknownEffects}`,
231 node,
232 });
233 }
234 return effects;
235}
236exports.validateEffects = validateEffects;