ace-linters
Version:
Ace linters is lsp client for Ace editor. It comes with large number of preconfigured easy to use in browser servers.
1,989 lines (1,675 loc) • 1.5 MB
JavaScript
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else {
var a = factory();
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
}
})(this, () => {
return /******/ (() => { // webpackBootstrap
/******/ var __webpack_modules__ = ({
/***/ 1542:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const { buildAst } = __webpack_require__(7523);
const { accept } = __webpack_require__(24);
const { DEFAULT_NS } = __webpack_require__(8319);
module.exports = {
buildAst: buildAst,
accept: accept,
DEFAULT_NS: DEFAULT_NS,
};
/***/ }),
/***/ 7523:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const { BaseXmlCstVisitor } = __webpack_require__(7882);
const {
last,
forEach,
reduce,
map,
pick,
sortBy,
isEmpty,
isArray,
assign,
} = __webpack_require__(5250);
const {
findNextTextualToken,
isXMLNamespaceKey,
getXMLNamespaceKeyPrefix,
} = __webpack_require__(1875);
const { getAstChildrenReflective } = __webpack_require__(8831);
const { DEFAULT_NS } = __webpack_require__(8319);
/**
* @param {DocumentCstNode} docCst
* @param {IToken[]} tokenVector
* @returns {XMLDocument}
*/
function buildAst(docCst, tokenVector) {
AstBuilder.setState({ tokenVector });
const xmlDocAst = AstBuilder.visit(docCst);
if (xmlDocAst.rootElement !== invalidSyntax) {
updateNamespaces(xmlDocAst.rootElement);
}
return xmlDocAst;
}
/* eslint-disable no-unused-vars -- consistent signatures in visitor methods even if they are empty placeholders */
class CstToAstVisitor extends BaseXmlCstVisitor {
constructor() {
super();
}
setState({ tokenVector }) {
this.tokenVector = tokenVector;
}
visit(cstNode, params = {}) {
return super.visit(cstNode, { location: cstNode.location, ...params });
}
/**
* @param ctx {DocumentCtx}
* @param opts {Object}
* @param opts.location {SourcePosition}
*
* @returns {XMLDocument}
*/
document(ctx, { location }) {
const astNode = {
type: "XMLDocument",
rootElement: invalidSyntax,
position: location,
};
if (ctx.prolog !== undefined) {
astNode.prolog = this.visit(ctx.prolog[0]);
}
if (
ctx.element !== undefined &&
isEmpty(ctx.element[0].children) === false
) {
astNode.rootElement = this.visit(ctx.element[0]);
}
setChildrenParent(astNode);
return astNode;
}
/**
* @param ctx {PrologCtx}
* @param opts {Object}
* @param opts.location {SourcePosition}
*/
prolog(ctx, { location }) {
const astNode = {
type: "XMLProlog",
attributes: [],
position: location,
};
if (ctx.attribute !== undefined) {
astNode.attributes = map(ctx.attribute, (_) =>
this.visit(_, { isPrologParent: true })
);
}
setChildrenParent(astNode);
return astNode;
}
/**
* @param {docTypeDeclCtx} ctx
*/
/* istanbul ignore next - place holder*/
docTypeDecl(ctx, astNode) {}
/**
* @param {ExternalIDCtx} ctx
*/
/* istanbul ignore next - place holder*/
externalID(ctx, astNode) {}
/**
* @param ctx {ContentCtx}
* @param opts {Object}
* @param opts.location {SourcePosition}
*
* @return {{elements, textContents}}
*/
content(ctx, { location }) {
let elements = [];
let textContents = [];
if (ctx.element !== undefined) {
elements = map(ctx.element, this.visit.bind(this));
}
if (ctx.chardata !== undefined) {
textContents = map(ctx.chardata, this.visit.bind(this));
}
return { elements, textContents };
}
/**
* @param ctx {ElementCtx}
* @param opts {Object}
* @param opts.location {SourcePosition}
*/
element(ctx, { location }) {
const astNode = {
type: "XMLElement",
// Avoid Accidental Keys in this map
namespaces: Object.create(null),
name: invalidSyntax,
attributes: [],
subElements: [],
textContents: [],
position: location,
syntax: {},
};
if (ctx.attribute !== undefined) {
astNode.attributes = map(ctx.attribute, this.visit.bind(this));
}
if (ctx.content !== undefined) {
const { elements, textContents } = this.visit(ctx.content[0]);
astNode.subElements = elements;
astNode.textContents = textContents;
}
handleElementOpenCloseNameRanges(astNode, ctx);
handleElementOpenCloseBodyRanges(astNode, ctx);
handleElementAttributeRanges(astNode, ctx, this.tokenVector);
setChildrenParent(astNode);
return astNode;
}
/**
* @param ctx {ReferenceCtx}
* @param opts {Object}
* @param opts.location {SourcePosition}
*/
/* istanbul ignore next - place holder*/
reference(ctx, { location }) {
// Irrelevant for the AST at this time
}
/**
* @param ctx {AttributeCtx}
* @param opts {Object}
* @param opts.location {SourcePosition}
* @param opts.isPrologParent {boolean}
*/
attribute(ctx, { location, isPrologParent }) {
const astNode = {
type: isPrologParent ? "XMLPrologAttribute" : "XMLAttribute",
position: location,
key: invalidSyntax,
value: invalidSyntax,
syntax: {},
};
/* istanbul ignore else - Defensive Coding, not actually possible else branch */
if (ctx.Name !== undefined && ctx.Name[0].isInsertedInRecovery !== true) {
const keyToken = ctx.Name[0];
astNode.key = keyToken.image;
astNode.syntax.key = toXMLToken(keyToken);
}
if (
ctx.STRING !== undefined &&
ctx.STRING[0].isInsertedInRecovery !== true
) {
const valueToken = ctx.STRING[0];
astNode.value = stripQuotes(valueToken.image);
astNode.syntax.value = toXMLToken(valueToken);
}
setChildrenParent(astNode);
return astNode;
}
/**
* @param ctx {ChardataCtx}
* @param opts {Object}
* @param opts.location {SourcePosition}
*/
chardata(ctx, { location }) {
const astNode = {
type: "XMLTextContent",
position: location,
text: invalidSyntax,
};
let allTokens = [];
if (ctx.SEA_WS !== undefined) {
allTokens = allTokens.concat(ctx.SEA_WS);
}
if (ctx.TEXT !== undefined) {
allTokens = allTokens.concat(ctx.TEXT);
}
const sortedTokens = sortBy(allTokens, ["startOffset"]);
const fullText = map(sortedTokens, "image").join("");
astNode.text = fullText;
return astNode;
}
/**
* @param ctx {MiscCtx}
* @param opts {Object}
* @param opts.location {SourcePosition}
*/
/* istanbul ignore next - place holder*/
misc(ctx, { location }) {
// Irrelevant for the AST at this time
}
}
/* eslint-enable no-unused-vars -- see matching disable comment */
const AstBuilder = new CstToAstVisitor();
function setChildrenParent(astParent) {
const astChildren = getAstChildrenReflective(astParent);
forEach(astChildren, (child) => (child.parent = astParent));
}
/**
* @param {XMLElement} element
* @param {Record<Prefix, Uri>} prevNamespaces
*/
function updateNamespaces(element, prevNamespaces = []) {
const currElemNamespaces = reduce(
element.attributes,
(result, attrib) => {
/* istanbul ignore else - Defensive Coding, not actually possible branch */
if (attrib.key !== invalidSyntax) {
if (
isXMLNamespaceKey({ key: attrib.key, includeEmptyPrefix: false }) ===
true
) {
const prefix = getXMLNamespaceKeyPrefix(attrib.key);
// TODO: Support un-defining namespaces (including the default one)
if (attrib.value) {
const uri = attrib.value;
if (prefix !== "") {
result[prefix] = uri;
} else {
// default namespace
result[DEFAULT_NS] = uri;
}
}
}
}
return result;
},
{}
);
const emptyMap = Object.create(null);
// "newer" (closer scope) namespaces definitions will overwrite "older" ones.
element.namespaces = assign(emptyMap, prevNamespaces, currElemNamespaces);
forEach(element.subElements, (subElem) =>
updateNamespaces(subElem, element.namespaces)
);
}
/**
* @param {chevrotain.IToken} token
*/
function toXMLToken(token) {
return pick(token, [
"image",
"startOffset",
"endOffset",
"startLine",
"endLine",
"startColumn",
"endColumn",
]);
}
function startOfXMLToken(token) {
return pick(token, ["startOffset", "startLine", "startColumn"]);
}
function endOfXMLToken(token) {
return pick(token, ["endOffset", "endLine", "endColumn"]);
}
function exists(tokArr) {
return (
isArray(tokArr) &&
tokArr.length === 1 &&
tokArr[0].isInsertedInRecovery !== true
);
}
function stripQuotes(quotedText) {
return quotedText.substring(1, quotedText.length - 1);
}
/**
* @param {string} text
*/
function nsToParts(text) {
const matchResult = /^([^:]+):([^:]+)$/.exec(text);
if (matchResult === null) {
return null;
}
const ns = matchResult[1];
const name = matchResult[2];
return { ns, name };
}
/**
* @type {InvalidSyntax}
*/
const invalidSyntax = null;
/**
* @param {XMLElement} astNode
* @param {ElementCtx} ctx
*/
function handleElementOpenCloseNameRanges(astNode, ctx) {
if (ctx.Name !== undefined && ctx.Name[0].isInsertedInRecovery !== true) {
const openNameToken = ctx.Name[0];
astNode.syntax.openName = toXMLToken(openNameToken);
const nsParts = nsToParts(openNameToken.image);
if (nsParts !== null) {
astNode.ns = nsParts.ns;
astNode.name = nsParts.name;
} else {
astNode.name = openNameToken.image;
}
}
if (
ctx.END_NAME !== undefined &&
ctx.END_NAME[0].isInsertedInRecovery !== true
) {
astNode.syntax.closeName = toXMLToken(ctx.END_NAME[0]);
}
}
/**
* @param {XMLElement} astNode
* @param {ElementCtx} ctx
*/
function handleElementOpenCloseBodyRanges(astNode, ctx) {
/* istanbul ignore else - Defensive Coding */
if (exists(ctx.OPEN)) {
let openBodyCloseTok = undefined;
/* istanbul ignore else - Defensive Coding */
if (exists(ctx.START_CLOSE)) {
openBodyCloseTok = ctx.START_CLOSE[0];
astNode.syntax.isSelfClosing = false;
} else if (exists(ctx.SLASH_CLOSE)) {
openBodyCloseTok = ctx.SLASH_CLOSE[0];
astNode.syntax.isSelfClosing = true;
}
if (openBodyCloseTok !== undefined) {
astNode.syntax.openBody = {
...startOfXMLToken(ctx.OPEN[0]),
...endOfXMLToken(openBodyCloseTok),
};
}
if (exists(ctx.SLASH_OPEN) && exists(ctx.END)) {
astNode.syntax.closeBody = {
...startOfXMLToken(ctx.SLASH_OPEN[0]),
...endOfXMLToken(ctx.END[0]),
};
}
}
}
/**
* @param {XMLElement} astNode
* @param {ElementCtx} ctx
* @param {IToken[]} tokenVector
*/
function handleElementAttributeRanges(astNode, ctx, tokenVector) {
if (exists(ctx.Name)) {
const startOffset = ctx.Name[0].endOffset + 2;
// Valid `attributesRange` exists
if (exists(ctx.START_CLOSE) || exists(ctx.SLASH_CLOSE)) {
const endOffset =
(exists(ctx.START_CLOSE)
? ctx.START_CLOSE[0].startOffset
: ctx.SLASH_CLOSE[0].startOffset) - 1;
astNode.syntax.attributesRange = { startOffset, endOffset };
}
// Have to scan-ahead and guess where the attributes range ends
else {
const hasAttributes = isArray(ctx.attribute);
const lastKnownAttribRangeTokenEnd = hasAttributes
? last(ctx.attribute).location.endOffset
: ctx.Name[0].endOffset;
const nextTextualToken = findNextTextualToken(
tokenVector,
lastKnownAttribRangeTokenEnd
);
if (nextTextualToken !== null) {
astNode.syntax.guessedAttributesRange = {
startOffset,
endOffset: nextTextualToken.endOffset - 1,
};
}
}
}
}
module.exports = {
buildAst: buildAst,
};
/***/ }),
/***/ 8319:
/***/ ((module) => {
module.exports = {
DEFAULT_NS: "::DEFAULT",
};
/***/ }),
/***/ 8831:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const { reduce, has, isArray } = __webpack_require__(5250);
function getAstChildrenReflective(astParent) {
const astChildren = reduce(
astParent,
(result, prop, name) => {
if (name === "parent") {
// parent property is never a child...
} else if (has(prop, "type")) {
result.push(prop);
} else if (isArray(prop) && prop.length > 0 && has(prop[0], "type")) {
result = result.concat(prop);
}
return result;
},
[]
);
return astChildren;
}
module.exports = {
getAstChildrenReflective: getAstChildrenReflective,
};
/***/ }),
/***/ 24:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const { forEach, isFunction } = __webpack_require__(5250);
const { getAstChildrenReflective } = __webpack_require__(8831);
/**
* @param {XMLAstNode} node
* @param {XMLAstVisitor} visitor
*
* @returns {void}
*/
function accept(node, visitor) {
switch (node.type) {
case "XMLDocument": {
if (isFunction(visitor.visitXMLDocument)) {
visitor.visitXMLDocument(node);
}
break;
}
case "XMLProlog": {
if (isFunction(visitor.visitXMLProlog)) {
visitor.visitXMLProlog(node);
}
break;
}
case "XMLPrologAttribute": {
if (isFunction(visitor.visitXMLPrologAttribute)) {
visitor.visitXMLPrologAttribute(node);
}
break;
}
case "XMLElement": {
if (isFunction(visitor.visitXMLElement)) {
visitor.visitXMLElement(node);
}
break;
}
case "XMLAttribute": {
if (isFunction(visitor.visitXMLAttribute)) {
visitor.visitXMLAttribute(node);
}
break;
}
case "XMLTextContent": {
if (isFunction(visitor.visitXMLTextContent)) {
visitor.visitXMLTextContent(node);
}
break;
}
/* istanbul ignore next defensive programming */
default:
throw Error("None Exhaustive Match");
}
const astChildren = getAstChildrenReflective(node);
forEach(astChildren, (childNode) => {
accept(childNode, visitor);
});
}
module.exports = {
accept: accept,
};
/***/ }),
/***/ 1875:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const { findNextTextualToken } = __webpack_require__(2266);
const {
isXMLNamespaceKey,
getXMLNamespaceKeyPrefix,
} = __webpack_require__(2370);
module.exports = {
findNextTextualToken: findNextTextualToken,
isXMLNamespaceKey: isXMLNamespaceKey,
getXMLNamespaceKeyPrefix: getXMLNamespaceKeyPrefix,
};
/***/ }),
/***/ 2266:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const { findIndex } = __webpack_require__(5250);
function findNextTextualToken(tokenVector, prevTokenEndOffset) {
// The TokenVector is sorted, so we could use a BinarySearch to optimize performance
const prevTokenIdx = findIndex(
tokenVector,
(tok) => tok.endOffset === prevTokenEndOffset
);
let nextTokenIdx = prevTokenIdx;
let found = false;
while (found === false) {
nextTokenIdx++;
const nextPossibleToken = tokenVector[nextTokenIdx];
// No Next textualToken
if (nextPossibleToken === undefined) {
return null;
}
/* istanbul ignore next
* I don't think this scenario can be created, however defensive coding never killed anyone...
* Basically SEA_WS can only only appear in "OUTSIDE" mode, and we need a CLOSE/SLASH_CLOSE to get back to outside
* mode, however if we had those this function would never have been called...
*/
if (nextPossibleToken.tokenType.name === "SEA_WS") {
// skip pure WS tokens as they do not contain any actual text
} else {
return nextPossibleToken;
}
}
}
module.exports = {
findNextTextualToken: findNextTextualToken,
};
/***/ }),
/***/ 2370:
/***/ ((module) => {
// The xml parser takes care of validating the attribute name.
// If the user started the attribute name with "xmlns:" we can assume that
// they meant for it to be an xml namespace attribute.
// xmlns attributes explicitly can't contain ":" after the "xmlns:" part.
const namespaceRegex = /^xmlns(?<prefixWithColon>:(?<prefix>[^:]*))?$/;
/**
* See comment in api.d.ts.
*
* @param {string} key
* @param {boolean} includeEmptyPrefix
* @returns {boolean}
*/
function isXMLNamespaceKey({ key, includeEmptyPrefix }) {
if (typeof key !== "string") {
return false;
}
const matchArr = key.match(namespaceRegex);
// No match - this is not an xmlns key
if (matchArr === null) {
return false;
}
return !!(
includeEmptyPrefix === true ||
// "xmlns" case
!matchArr.groups.prefixWithColon ||
// "xmlns:<prefix>" case
matchArr.groups.prefix
);
}
/**
* See comment in api.d.ts.
*
* @param {string} key
* @returns {string|undefined}
*/
function getXMLNamespaceKeyPrefix(key) {
if (typeof key !== "string") {
return undefined;
}
const matchArr = key.match(namespaceRegex);
if (matchArr === null) {
return undefined;
}
return (matchArr.groups && matchArr.groups.prefix) || "";
}
module.exports = {
isXMLNamespaceKey: isXMLNamespaceKey,
getXMLNamespaceKeyPrefix: getXMLNamespaceKeyPrefix,
};
/***/ }),
/***/ 30:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const { validate } = __webpack_require__(8737);
const {
validateUniqueAttributeKeys,
} = __webpack_require__(4232);
const {
validateTagClosingNameMatch,
} = __webpack_require__(8403);
/**
* @param {XMLDocument} ast
* @returns {ValidationIssue[]}
*/
function checkConstraints(ast) {
const constraintIssues = validate({
doc: ast,
validators: {
element: [validateTagClosingNameMatch, validateUniqueAttributeKeys],
},
});
return constraintIssues;
}
module.exports = {
checkConstraints,
};
/***/ }),
/***/ 8403:
/***/ ((module) => {
/**
* @param {XMLElement} elem
* @returns {ValidationIssue[]}
*/
function validateTagClosingNameMatch(elem) {
const openTagToken = elem.syntax.openName;
const closeTagToken = elem.syntax.closeName;
// The element tag must have **both** the opening and closing tokens
// to be able to validate.
if (!openTagToken || !closeTagToken) {
return [];
}
// alles gut
if (openTagToken.image === closeTagToken.image) {
return [];
} else {
return [
{
msg: `tags mismatch: "${openTagToken.image}" must match closing tag: "${closeTagToken.image}"`,
node: elem,
severity: "error",
position: {
startOffset: openTagToken.startOffset,
endOffset: openTagToken.endOffset,
},
},
{
msg: `tags mismatch: "${closeTagToken.image}" must match opening tag: "${openTagToken.image}"`,
node: elem,
severity: "error",
position: {
startOffset: closeTagToken.startOffset,
endOffset: closeTagToken.endOffset,
},
},
];
}
}
module.exports = {
validateTagClosingNameMatch,
};
/***/ }),
/***/ 4232:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const { groupBy, pickBy, reduce, map, filter } = __webpack_require__(5250);
/**
* @param {XMLElement} elem
* @returns {ValidationIssue[]}
*/
function validateUniqueAttributeKeys(elem) {
const attributesWithKeys = filter(elem.attributes, (_) => _.key !== null);
const attribByKey = groupBy(attributesWithKeys, "key");
const nonUniqueAttribsGroups = pickBy(attribByKey, (_) => _.length > 1);
const nonUniqueAttribs = reduce(
nonUniqueAttribsGroups,
(result, attribsGroup) => result.concat(attribsGroup),
[]
);
const validationIssues = map(nonUniqueAttribs, (_) => {
// the `key` is guaranteed to exist because we have filtered above
// for attributes with valid keys
const keyToken = _.syntax.key;
return {
msg: `duplicate attribute: "${_.key}"`,
node: _,
severity: "error",
position: {
startOffset: keyToken.startOffset,
endOffset: keyToken.endOffset,
},
};
});
return validationIssues;
}
module.exports = {
validateUniqueAttributeKeys,
};
/***/ }),
/***/ 7882:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const { xmlLexer } = __webpack_require__(3799);
const { xmlParser } = __webpack_require__(8322);
module.exports = {
parse: function parse(text) {
const lexResult = xmlLexer.tokenize(text);
// setting a new input will RESET the parser instance's state.
xmlParser.input = lexResult.tokens;
// any top level rule may be used as an entry point
const cst = xmlParser.document();
return {
cst: cst,
tokenVector: lexResult.tokens,
lexErrors: lexResult.errors,
parseErrors: xmlParser.errors,
};
},
BaseXmlCstVisitor: xmlParser.getBaseCstVisitorConstructor(),
};
/***/ }),
/***/ 3799:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const { createToken: createTokenOrg, Lexer } = __webpack_require__(397);
// A little mini DSL for easier lexer definition.
const fragments = {};
const f = fragments;
function FRAGMENT(name, def) {
fragments[name] = typeof def === "string" ? def : def.source;
}
function makePattern(strings, ...args) {
let combined = "";
for (let i = 0; i < strings.length; i++) {
combined += strings[i];
if (i < args.length) {
let pattern = args[i];
// By wrapping in a RegExp (none) capturing group
// We enabled the safe usage of qualifiers and assertions.
combined += `(?:${pattern})`;
}
}
return new RegExp(combined);
}
const tokensArray = [];
const tokensDictionary = {};
function createToken(options) {
const newTokenType = createTokenOrg(options);
tokensArray.push(newTokenType);
tokensDictionary[options.name] = newTokenType;
return newTokenType;
}
FRAGMENT(
"NameStartChar",
"(:|[a-zA-Z]|_|\\u2070-\\u218F|\\u2C00-\\u2FEF|\\u3001-\\uD7FF|\\uF900-\\uFDCF|\\uFDF0-\\uFFFD)"
);
FRAGMENT(
"NameChar",
makePattern`${f.NameStartChar}|-|\\.|\\d|\\u00B7||[\\u0300-\\u036F]|[\\u203F-\\u2040]`
);
FRAGMENT("Name", makePattern`${f.NameStartChar}(${f.NameChar})*`);
const Comment = createToken({
name: "Comment",
pattern: /<!--(.|\r?\n)*?-->/,
// A Comment may span multiple lines.
line_breaks: true,
});
const CData = createToken({
name: "CData",
pattern: /<!\[CDATA\[(.|\r?\n)*?]]>/,
line_breaks: true,
});
const DocType = createToken({
name: "DocType",
pattern: /<!DOCTYPE/,
push_mode: "INSIDE",
});
const IgnoredDTD = createToken({
name: "DTD",
pattern: /<!.*?>/,
group: Lexer.SKIPPED,
});
const EntityRef = createToken({
name: "EntityRef",
pattern: makePattern`&${f.Name};`,
});
const CharRef = createToken({
name: "CharRef",
pattern: /&#\d+;|&#x[a-fA-F0-9]/,
});
const SEA_WS = createToken({
name: "SEA_WS",
pattern: /( |\t|\n|\r\n)+/,
});
const XMLDeclOpen = createToken({
name: "XMLDeclOpen",
pattern: /<\?xml[ \t\r\n]/,
push_mode: "INSIDE",
});
const SLASH_OPEN = createToken({
name: "SLASH_OPEN",
pattern: /<\//,
push_mode: "INSIDE",
});
const INVALID_SLASH_OPEN = createToken({
name: "INVALID_SLASH_OPEN",
pattern: /<\//,
categories: [SLASH_OPEN],
});
const PROCESSING_INSTRUCTION = createToken({
name: "PROCESSING_INSTRUCTION",
pattern: makePattern`<\\?${f.Name}.*\\?>`,
});
const OPEN = createToken({ name: "OPEN", pattern: /</, push_mode: "INSIDE" });
// Meant to avoid skipping '<' token in a partial sequence of elements.
// Example of the problem this solves:
// <
// <from>john</from>
// - The second '<' will be skipped because in the mode "INSIDE" '<' is not recognized.
// - This means the AST will include only a single element instead of two
const INVALID_OPEN_INSIDE = createToken({
name: "INVALID_OPEN_INSIDE",
pattern: /</,
categories: [OPEN],
});
const TEXT = createToken({ name: "TEXT", pattern: /[^<&]+/ });
const CLOSE = createToken({ name: "CLOSE", pattern: />/, pop_mode: true });
const SPECIAL_CLOSE = createToken({
name: "SPECIAL_CLOSE",
pattern: /\?>/,
pop_mode: true,
});
const SLASH_CLOSE = createToken({
name: "SLASH_CLOSE",
pattern: /\/>/,
pop_mode: true,
});
const SLASH = createToken({ name: "SLASH", pattern: /\// });
const STRING = createToken({
name: "STRING",
pattern: /"[^<"]*"|'[^<']*'/,
});
const EQUALS = createToken({ name: "EQUALS", pattern: /=/ });
const Name = createToken({ name: "Name", pattern: makePattern`${f.Name}` });
const S = createToken({
name: "S",
pattern: /[ \t\r\n]/,
group: Lexer.SKIPPED,
});
const xmlLexerDefinition = {
defaultMode: "OUTSIDE",
modes: {
OUTSIDE: [
Comment,
CData,
DocType,
IgnoredDTD,
EntityRef,
CharRef,
SEA_WS,
XMLDeclOpen,
SLASH_OPEN,
PROCESSING_INSTRUCTION,
OPEN,
TEXT,
],
INSIDE: [
// Tokens from `OUTSIDE` to improve error recovery behavior
Comment,
INVALID_SLASH_OPEN,
INVALID_OPEN_INSIDE,
// "Real" `INSIDE` tokens
CLOSE,
SPECIAL_CLOSE,
SLASH_CLOSE,
SLASH,
EQUALS,
STRING,
Name,
S,
],
},
};
const xmlLexer = new Lexer(xmlLexerDefinition, {
// Reducing the amount of position tracking can provide a small performance boost (<10%)
// Likely best to keep the full info for better error position reporting and
// to expose "fuller" ITokens from the Lexer.
positionTracking: "full",
ensureOptimizations: false,
// TODO: inspect definitions for XML line terminators
lineTerminatorCharacters: ["\n"],
lineTerminatorsPattern: /\n|\r\n/g,
});
module.exports = {
xmlLexer,
tokensDictionary,
};
/***/ }),
/***/ 8322:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const { CstParser, tokenMatcher } = __webpack_require__(397);
const { tokensDictionary: t } = __webpack_require__(3799);
class Parser extends CstParser {
constructor() {
super(t, {
maxLookahead: 1,
recoveryEnabled: true,
nodeLocationTracking: "full",
});
this.deletionRecoveryEnabled = true;
const $ = this;
$.RULE("document", () => {
$.OPTION(() => {
$.SUBRULE($.prolog);
});
$.MANY(() => {
$.SUBRULE($.misc);
});
$.OPTION2(() => {
$.SUBRULE($.docTypeDecl);
});
$.MANY2(() => {
$.SUBRULE2($.misc);
});
$.SUBRULE($.element);
$.MANY3(() => {
$.SUBRULE3($.misc);
});
});
$.RULE("prolog", () => {
$.CONSUME(t.XMLDeclOpen);
$.MANY(() => {
$.SUBRULE($.attribute);
});
$.CONSUME(t.SPECIAL_CLOSE);
});
// https://www.w3.org/TR/xml/#NT-doctypedecl
$.RULE("docTypeDecl", () => {
$.CONSUME(t.DocType);
$.CONSUME(t.Name);
$.OPTION(() => {
$.SUBRULE($.externalID);
});
// The internal subSet part is intentionally not implemented because we do not at this
// time wish to implement a full DTD Parser as part of this project...
// https://www.w3.org/TR/xml/#NT-intSubset
$.CONSUME(t.CLOSE);
});
$.RULE("externalID", () => {
// Using gates to assert the value of the "Name" Identifiers.
// We could use Categories to model un-reserved keywords, however I am not sure
// The added complexity is needed at this time...
$.OR([
{
GATE: () => $.LA(1).image === "SYSTEM",
ALT: () => {
$.CONSUME2(t.Name, { LABEL: "System" });
$.CONSUME(t.STRING, { LABEL: "SystemLiteral" });
},
},
{
GATE: () => $.LA(1).image === "PUBLIC",
ALT: () => {
$.CONSUME3(t.Name, { LABEL: "Public" });
$.CONSUME2(t.STRING, { LABEL: "PubIDLiteral" });
$.CONSUME3(t.STRING, { LABEL: "SystemLiteral" });
},
},
]);
});
$.RULE("content", () => {
$.MANY(() => {
$.OR([
{ ALT: () => $.SUBRULE($.element) },
{ ALT: () => $.SUBRULE($.chardata) },
{ ALT: () => $.SUBRULE($.reference) },
{ ALT: () => $.CONSUME(t.CData) },
{ ALT: () => $.CONSUME(t.PROCESSING_INSTRUCTION) },
{ ALT: () => $.CONSUME(t.Comment) },
]);
});
});
$.RULE("element", () => {
$.CONSUME(t.OPEN);
try {
this.deletionRecoveryEnabled = false;
// disabling single token deletion here
// because `<
// </note>`
// will be parsed as: `<note>`
// and the next element will be lost
$.CONSUME(t.Name);
} finally {
this.deletionRecoveryEnabled = true;
}
$.MANY(() => {
$.SUBRULE($.attribute);
});
$.OR([
{
ALT: () => {
$.CONSUME(t.CLOSE, { LABEL: "START_CLOSE" });
$.SUBRULE($.content);
$.CONSUME(t.SLASH_OPEN);
$.CONSUME2(t.Name, { LABEL: "END_NAME" });
$.CONSUME2(t.CLOSE, { LABEL: "END" });
},
},
{
ALT: () => {
$.CONSUME(t.SLASH_CLOSE);
},
},
]);
});
$.RULE("reference", () => {
$.OR([
{ ALT: () => $.CONSUME(t.EntityRef) },
{ ALT: () => $.CONSUME(t.CharRef) },
]);
});
$.RULE("attribute", () => {
$.CONSUME(t.Name);
try {
this.deletionRecoveryEnabled = false;
// disabling single token deletion here
// because `attrib1 attrib2="666`
// will be parsed as: `attrib1="666`
$.CONSUME(t.EQUALS);
// disabling single token deletion here
// to avoid new elementName being
$.CONSUME(t.STRING);
} finally {
this.deletionRecoveryEnabled = true;
}
});
$.RULE("chardata", () => {
$.OR([
{ ALT: () => $.CONSUME(t.TEXT) },
{ ALT: () => $.CONSUME(t.SEA_WS) },
]);
});
$.RULE("misc", () => {
$.OR([
{ ALT: () => $.CONSUME(t.Comment) },
{ ALT: () => $.CONSUME(t.PROCESSING_INSTRUCTION) },
{ ALT: () => $.CONSUME(t.SEA_WS) },
]);
});
this.performSelfAnalysis();
}
canRecoverWithSingleTokenDeletion(expectedTokType) {
if (this.deletionRecoveryEnabled === false) {
return false;
}
return super.canRecoverWithSingleTokenDeletion(expectedTokType);
}
// TODO: provide this fix upstream to chevrotain
// https://github.com/SAP/chevrotain/issues/1055
/* istanbul ignore next - should be tested as part of Chevrotain */
findReSyncTokenType() {
const allPossibleReSyncTokTypes = this.flattenFollowSet();
// this loop will always terminate as EOF is always in the follow stack and also always (virtually) in the input
let nextToken = this.LA(1);
let k = 2;
/* eslint-disable-next-line no-constant-condition -- see above comment */
while (true) {
const foundMatch = allPossibleReSyncTokTypes.find((resyncTokType) => {
const canMatch = tokenMatcher(nextToken, resyncTokType);
return canMatch;
});
if (foundMatch !== undefined) {
return foundMatch;
}
nextToken = this.LA(k);
k++;
}
}
}
// Re-use the same parser instance
const xmlParser = new Parser();
module.exports = {
xmlParser,
};
/***/ }),
/***/ 7728:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const { getSchemaValidators } = __webpack_require__(2258);
const { getSchemaSuggestionsProviders } = __webpack_require__(4638);
module.exports = {
getSchemaValidators: getSchemaValidators,
getSchemaSuggestionsProviders: getSchemaSuggestionsProviders,
};
/***/ }),
/***/ 1851:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const { difference, map } = __webpack_require__(5250);
/**
* @param {XMLElement} elementNode
* @param {XSSElement} xssElement
*
* @returns {CompletionSuggestion[]}
*/
function attributeNameCompletion(elementNode, xssElement) {
const possibleSuggestions = map(xssElement.attributes, (_) => _.key);
const existingAttribNames = map(elementNode.attributes, (_) => _.key);
const possibleNewSuggestions = difference(
possibleSuggestions,
existingAttribNames
);
const suggestions = map(possibleNewSuggestions, (_) => {
return {
text: _,
label: _,
commitCharacter: "=",
};
});
return suggestions;
}
module.exports = {
attributeNameCompletion: attributeNameCompletion,
};
/***/ }),
/***/ 2650:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const { has, isRegExp, forEach, isArray } = __webpack_require__(5250);
/**
* @param {XMLAttribute} attributeNode
* @param {XSSAttribute} xssAttribute
* @param {string} prefix
*
* @returns {CompletionSuggestion[]}
*/
function attributeValueCompletion(attributeNode, xssAttribute, prefix = "") {
// An XSS Attribute definition may not specify any constraints on a value
if (has(xssAttribute, "value") === false) {
return [];
}
const suggestions = [];
const valueDef = xssAttribute.value;
/* istanbul ignore else - defensive programming */
if (isRegExp(valueDef)) {
// No suggestions for regExp value definitions...
} else if (isArray(valueDef)) {
forEach(valueDef, (enumVal) => {
if (enumVal.startsWith(prefix)) {
suggestions.push({
text: enumVal.substring(prefix.length),
label: enumVal,
});
}
});
} else {
/* istanbul ignore next defensive programming */
throw Error("None Exhaustive Match");
}
return suggestions;
}
module.exports = {
attributeValueCompletion: attributeValueCompletion,
};
/***/ }),
/***/ 2548:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const { difference, map, filter, has, pickBy } = __webpack_require__(5250);
const { DEFAULT_NS } = __webpack_require__(1542);
// https://www.w3.org/TR/2009/REC-xml-names-20091208/#NT-PrefixedName
const NAMESPACE_PATTERN = /^(?:([^:]*):)?([^:]*)$/;
/**
*
* Note that the Element (XML/XSS) are of the parent node of the element
* in which content assist was requested.
*
* @param {XMLElement} elementNode
* @param {XSSElement} xssElement
*
* @returns {CompletionSuggestion[]}
*/
function elementNameCompletion(elementNode, xssElement, prefix = "") {
const match = prefix.match(NAMESPACE_PATTERN);
if (match === null) {
return [];
}
// If there is no prefix, use the default namespace prefix
const namespacePrefix = match[1] ? match[1] : DEFAULT_NS;
const elementNamespaceUri = elementNode.namespaces[namespacePrefix];
const possibleElements = filter(
xssElement.elements,
(_) =>
has(_, "namespace") === false ||
(_.namespace && _.namespace === elementNamespaceUri)
);
const possibleSuggestionsWithoutExistingSingular = applicableElements(
xssElement.elements,
elementNode.subElements,
possibleElements
);
const suggestions = map(possibleSuggestionsWithoutExistingSingular, (_) => {
return {
text: _,
label: _,
};
});
if (namespacePrefix === undefined || namespacePrefix === DEFAULT_NS) {
// Can't really suggest anything for the `implicit` default namespace...
const namespacesWithoutDefault = pickBy(
elementNode.namespaces,
(uri, prefix) => prefix !== DEFAULT_NS
);
const applicableNamespaces = pickBy(namespacesWithoutDefault, (uri) => {
const possibleElements = filter(
xssElement.elements,
(element) =>
has(element, "namespace") === true && element.namespace === uri
);
const possibleSuggestionsWithoutExistingSingular = applicableElements(
xssElement.elements,
elementNode.subElements,
possibleElements
);
const namespaceHasApplicableElements =
possibleSuggestionsWithoutExistingSingular.length > 0;
return namespaceHasApplicableElements;
});
const namespaceSuggestions = map(applicableNamespaces, (uri, prefix) => ({
text: prefix,
label: prefix,
commitCharacter: ":",
isNamespace: true,
}));
return [...namespaceSuggestions, ...suggestions];
}
return suggestions;
}
function applicableElements(xssElements, subElements, possibleElements) {
const allPossibleSuggestions = map(
possibleElements,
(element) => element.name
);
const notSingularElem = filter(
xssElements,
(element) => element.cardinality === "many"
);
const notSingularElemNames = map(notSingularElem, (element) => element.name);
const existingElemNames = map(subElements, (element) => element.name);
const existingSingular = difference(existingElemNames, notSingularElemNames);
const possibleSuggestionsWithoutExistingSingular = difference(
allPossibleSuggestions,
existingSingular
);
return possibleSuggestionsWithoutExistingSingular;
}
module.exports = {
elementNameCompletion: elementNameCompletion,
};
/***/ }),
/***/ 4638:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const { attributeNameCompletion } = __webpack_require__(1851);
const {
attributeValueCompletion,
} = __webpack_require__(2650);
const { elementNameCompletion } = __webpack_require__(2548);
const { findElementXssDef, findAttributeXssDef } = __webpack_require__(8317);
function getSchemaSuggestionsProviders(schema) {
const attributeNameProvider = buildAttributeNameProvider(schema);
const attributeValueProvider = buildAttributeValueProvider(schema);
const elementNameProvider = buildElementNameProvider(schema);
return {
schemaElementNameCompletion: elementNameProvider,
schemaAttributeNameCompletion: attributeNameProvider,
schemaAttributeValueCompletion: attributeValueProvider,
};
}
/**
* @param {SimpleSchema} schema
*/
function buildAttributeNameProvider(schema) {
return ({ element, prefix }) => {
const xssElementDef = findElementXssDef(element, schema);
if (xssElementDef !== undefined) {
return attributeNameCompletion(element, xssElementDef, prefix);
} else {
return [];
}
};
}
/**
* @param {SimpleSchema} schema
*/
function buildElementNameProvider(schema) {
return ({ element, prefix }) => {
// Note we are finding the definition for the element's parent
// Because the information on possible sibling elements exists there...
const xssElementDef = findElementXssDef(element.parent, schema);
if (xssElementDef !== undefined) {
return elementNameCompletion(element.parent, xssElementDef, prefix);
} else {
return [];
}
};
}
/**
* @param {SimpleSchema} schema
*/
function buildAttributeValueProvider(schema) {
return ({ attribute, prefix }) => {
const attributeXssDef = findAttributeXssDef(attribute, schema);
return attributeValueCompletion(attribute, attributeXssDef, prefix);
};
}
module.exports = {
getSchemaSuggestionsProviders: getSchemaSuggestionsProviders,
};
/***/ }),
/***/ 2258:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const { validateAttributeValue } = __webpack_require__(7578);
const {
validateDuplicateSubElements,
} = __webpack_require__(1688);
const {
validateRequiredAttributes,
} = __webpack_require__(5587);
const {
validateRequiredSubElements,
} = __webpack_require__(6762);
const {
validateUnknownAttributes,
} = __webpack_require__(6336);
const {
validateUnknownSubElements,
} = __webpack_require__(1573);
const { findAttributeXssDef, findElementXssDef } = __webpack_require__(8317);
function getSchemaValidators(schema) {
const attributeValidator = buildAttributeValidator(schema);
const elementValidator = buildElementValidator(schema);
return {
attribute: attributeValidator,
element: elementValidator,
};
}
/**
* @param {SimpleSchema} schema
*/
function buildAttributeValidator(schema) {
return (attributeNode) => {
let issues = [];
const xssAttributeDef = findAttributeXssDef(attributeNode, schema);
if (xssAttributeDef !== undefined) {
const attributeValueIssues = validateAttributeValue(
attributeNode,
xssAttributeDef
);
issues = issues.concat(attributeValueIssues);
}
return issues;
};
}
/**
* @param {SimpleSchema} schema
*/
function buildElementValidator(schema) {
return (elementNode) => {
let issues = [];
const xssElementDef = findElementXssDef(elementNode, schema);
if (xssElementDef !== undefined) {
const duplicateElementsIssues = validateDuplicateSubElements(
elementNode,
xssElementDef
);
const requiredAttributesIssues = validateRequiredAttributes(
elementNode,
xssElementDef
);
const requiredSubElementsIssues = validateRequiredSubElements(
elementNode,
xssElementDef
);
const unknownAttributesIssues = validateUnknownAttributes(
elementNode,
xssElementDef
);
const unknownSubElementsIssues = validateUnknownSubElements(
elementNode,
xssElementDef
);
issues = issues.concat(
duplicateElementsIssues,
requiredAttributesIssues,
requiredSubElementsIssues,
unknownAttributesIssues,
unknownSubElementsIssues
);
}
return issues;
};
}
module.exports = {
getSchemaValidators: getSchemaValidators,
};
/***/ }),
/***/ 8317:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const { drop, map, forEach, first } = __webpack_require__(5250);
/**
* @param {XMLAttribute} attribNode
* @param {SimpleSchema} schema
*/
function findAttributeXssDef(attribNode, schema) {
const xssElement = findElementXssDef(attribNode.parent, schema);
let xssAttribute = undefined;
if (xssElement !== undefined) {
const attributeName = attribNode.key;
xssAttribute = xssElement.attributes[attributeName];
}
return xssAttribute;
}
/**
* @param {XMLElement} node
* @param {SimpleSchema} schema
*/
function findElementXssDef(node, schema) {
const ancestors = getAstNodeAncestors(node);
const elementsPath = map(ancestors, "name");
const rootElement = first(elementsPath);
// Root Element mis-match, The Schema cannot provide any attribute validations for this XML AST.
if (rootElement !== schema.name) {
return undefined;
}
let xssElement = schema;
forEach(drop(elementsPath), (elemName) => {
// traverse subElements
xssElement = xssElement.elements[elemName];
if (xssElement === undefined) {
return false;
}
});
return xssElement;
}
/**
* @param {XMLAstNode} node
*
* @returns {XMLAstNode[]} - The Ancestors do not include the XMLDocument
*/
function getAstNodeAncestors(node) {
const ancestors = [];
ancestors.push(node);
let currAncestor = node.parent;
while (
currAncestor !== undefined &&
// The Simple Schema only starts at the root Element (not the Root Document).
currAncestor.type !== "XMLDocument"
) {
ancestors.push(currAncestor);
currAncestor = currAncestor.parent;
}
ancestors.reverse();
return ancestors;
}
module.exports = {
findAttributeXssDef: findAttributeXssDef,
findElementXssDef: findElementXssDef,
};
/***/ }),
/***/ 7578:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const { isRegExp, isArray, includes, has } = __webpack_require__(5250);
const { tokenToOffsetPosition } = __webpack_require__(4663);
/**
* @param {XMLAttribute} attributeNode
* @param {XSSAttribute }xssAttribute
*
* @returns {ValidationIssue[]}
*/
function validateAttributeValue(attributeNode, xssAttribute) {
const issues = [];
const valueDef = xssAttribute.value;
// An XSS Attribute definition may not specify any constraints on a value
if (has(xssAttribute, "value") === false) {
return issues;
}
const actualValue = attributeNode.value;
if (actualValue === null) {
// we cannot validate a partial attribute AST without an actual value...
return issues;
}
// This is always safe because at this point we know the attribute has a value
const errPosition = tokenToOffsetPosition(attributeNode.syntax.value);
/* istanbul ignore else defensive programming */
if (isRegExp(valueDef)) {
if (valueDef.test(actualValue) === false) {
issues.push({
msg: `Expecting Value matching <${valueDef.toString()}> but found <${actualValue}>`,
node: attributeNode,
severity: "error",
position: errPosition,
});
}
} else if (isArray(valueDef)) {
if (includes(valueDef, actualValue) === false) {
issues.push({
msg: `Expecting one of <${valueDef.toString()}> but found <${actualValue}>`,
node: attributeNode,
severity: "error",
position: errPosition,
});
}
} else {
/* istanbul ignore next defensive programming */
throw Error("None Exhaustive Match");
}
return issues;
}
module.exports = {
validateAttributeValue: validateAttributeValue,
};
/***/ }),
/***/ 1688:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const { map, forEach, includes, filter, groupBy } = __webpack_require__(5250);
const { tokenToOffsetPosition } = __webpack_require__(4663);
/**
* @param {XMLElement} elem
* @param {XSSElement} schema
*
* @returns {ValidationIssue[]}
*/
function validateDuplicateSubElements(elem, schema) {
const allowedDupElem = filter(
schema.elements,
(_) => _.cardinality === "many"
);
const allowedDupElemNames = map(allowedDupElem, (_) => _.name);
const actualSubElemByName = groupBy(elem.subElements, (_) => _.name);
const issues = [];
forEach(actualSubElemByName, (dupElements, dupElementsName) => {
const allowedDup = includes(allowedDupElemNames, dupElementsName);
const hasConfiguration = schema.elements[dupElementsName] !== undefined;
const hasDuplicates = dupElements.length > 1;
if (allowedDup === false && hasDuplicates && hasConfiguration) {
forEach(dupElements, (dupElem) => {
issues.push({
msg: `Duplicate Sub-Element: <${dupElem.name}> only a single occurrence of this Sub-Element is allowed here.`,
node: dupElem,
severity: "error",
// safe assumption that we have an `openName` (see above condition)
position: tokenToOffsetPosition(dupElem.syntax.openName),
});
});
}
});
return issues;
}
module.exports = {
validateDuplicateSubElements: validateDuplicateSubElements,
};
/***/ }),
/***/ 5587:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const { map, filter, difference } = __webpack_require__(5250);
const { tokenToOffsetPosition } = __webpack_require__(4663);
/**
* @param {XMLElement} elem
* @param {XSSElement} schema
*
* @returns {ValidationIssue[]}
*/
function validateRequiredAttributes(elem, schema) {
const requiredAttribsDef = filter(
schema.attributes,
(_) => _.required === true
);
const requiredAttribNames = map(requiredAttribsDef, (_) => _.key);
const actualAttribNames = map(elem.attributes, (_) => _.key);
const missingAttributesNames = difference(
requiredAttribNames,
actualAttribNames
);
// This elementName must always exist, otherwise we could not locate the relevant schema definition
// so this validation could have never executed...
const errPosition = tokenToOffsetPosition(elem.syntax.openName);
const issues = map(missingAttributesNames, (_) => {
return {
msg: `Missing Required Attribute: <${_}>`,
node: elem,
severity: "error",
position: errPosition,
};
});
return issues;
}
module.exports = {
validateRequiredAttributes: validateRequiredAttributes,
};
/***/ }),
/***/ 6762:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const { map, filter, difference } = __webpack_require__(5250);
const { tokenToOffsetPosition } = __webpack_require__(4663);
/**
* @param {XMLElement} elem
* @param {XSSElement} schema
*
* @returns {ValidationIssue[]}
*/
function validateRequiredSubElements(elem, schema) {
const requiredSubElemsDef = filter(
schema.elements,
(_) => _.required === true
);
const requiredElemNames = map(requiredSubElemsDef, (_) => _.name);
const actualSubElemNameNames = map(elem.subElements, (_) => _.name);
const missingSubElemNames = difference(
requiredElemNames,
actualSubElemNameNames
);
// This elementName must always exist, otherwise we could not locate the relevant schema definition
// so this validation could have never executed...
const errPosition = tokenToOffsetPosition(elem.syntax.openName);
const issues = map(missingSubElemNames, (_) => {
return {
msg: `Missing Required Sub-Element: <${_}>`,
node: elem,
severity: "error",
position: errPosition,
};
});
return issues;
}
module.exports = {
validateRequiredSubElements: validateRequiredSubElements,
};
/***/ }),
/***/ 6336:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const { map, includes, forEach } = __webpack_require__(5250);
const { isXMLNamespaceKey } = __webpack_require__(1875);
const { tokenToOffsetPosition } = __webpack_require__(4663);
/**
* @param {XMLElement} elem
* @param {XSSElement} schema
*
* @returns {ValidationIssue[]}
*/
function validateUnknownAttributes(elem, schema) {
// This validation is only relevant if the Schema disallows unknown attributes.
if (schema.attributesType !== "closed") {
return [];
}
const allowedAttribNames = map(schema.attributes, (_) => _.key);
const issues = [];
forEach(elem.attributes, (attrib) => {
/* istanbul ignore else - Defensive progra