UNPKG

5.08 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 });
12const Builder_1 = require("./Builder");
13const utils_1 = require("./lint/utils");
14const grammarkdown_1 = require("grammarkdown");
15const endTagRe = /<\/?(emu-\w+|h?\d|p|ul|table|pre|code)\b[^>]*>/i;
16const globalEndTagRe = /<\/?(emu-\w+|h?\d|p|ul|table|pre|code)\b[^>]*>/gi;
17/*@internal*/
18class Grammar extends Builder_1.default {
19 static enter({ spec, node, clauseStack }) {
20 return __awaiter(this, void 0, void 0, function* () {
21 if ('grammarkdownOut' in node) {
22 // i.e., we already parsed this during an earlier phase
23 // @ts-ignore
24 node.innerHTML = node.grammarkdownOut;
25 return;
26 }
27 // fetch the original source text and source DOM for the node.
28 // walk up the current DOM as this may come from an import.
29 const location = spec.locate(node);
30 let content;
31 let possiblyMalformed = true;
32 // If the source text is available, we should use it since `innerHTML` serializes the
33 // DOM tree beneath the node. This can result in odd behavior when the syntax is malformed
34 // in a way that parse5 does not understand, but grammarkdown could possibly recover.
35 if (location) {
36 if (location.startTag && location.endTag) {
37 // the parser was able to find a matching end tag.
38 const start = location.startTag.endOffset;
39 const end = location.endTag.startOffset;
40 content = location.source.slice(start, end);
41 }
42 else {
43 // TODO this is not reached
44 // the parser was *not* able to find a matching end tag. Try to recover by finding a
45 // possible end tag, otherwise read the rest of the source text.
46 const start = (globalEndTagRe.lastIndex = location.endOffset);
47 const match = globalEndTagRe.exec(location.source);
48 const end = match ? match.index : location.source.length;
49 content = location.source.slice(start, end);
50 // since we already tested for an end tag, no need to test again later.
51 possiblyMalformed = false;
52 globalEndTagRe.lastIndex = 0;
53 }
54 }
55 else {
56 // no source text, so read innerHTML as a fallback.
57 content = node.innerHTML.replace(/&gt;/g, '>');
58 }
59 if (possiblyMalformed) {
60 // check for a possible end-tag in the content. For now we only check for a few possible
61 // recovery cases, namely emu-* tags, and a few block-level elements.
62 const match = endTagRe.exec(content);
63 if (match) {
64 content = content.slice(0, match.index);
65 }
66 }
67 const options = {
68 format: grammarkdown_1.EmitFormat.ecmarkup,
69 noChecks: true,
70 };
71 const grammarHost = grammarkdown_1.CoreAsyncHost.forFile(content);
72 const grammar = new grammarkdown_1.Grammar([grammarHost.file], options, grammarHost);
73 yield grammar.parse();
74 if (spec.opts.lintSpec && spec.locate(node) != null && !node.hasAttribute('example')) {
75 // Collect referenced nonterminals to check definedness later
76 // The `'grammarkdownOut' in node` check at the top means we don't do this for nodes which have already been covered by a separate linting pass
77 const clause = clauseStack[clauseStack.length - 1];
78 const namespace = clause ? clause.namespace : spec.namespace;
79 const nonterminals = utils_1.collectNonterminalsFromGrammar(grammar).map(({ name, loc }) => ({
80 name,
81 loc,
82 node,
83 namespace,
84 }));
85 spec._ntStringRefs = spec._ntStringRefs.concat(nonterminals);
86 }
87 yield grammar.emit(undefined, (file, source) => {
88 node.innerHTML = source;
89 });
90 });
91 }
92}
93exports.default = Grammar;
94Grammar.elements = ['EMU-GRAMMAR'];