@markuplint/rules
Advanced tools
Comparing version 1.0.0-alpha.12 to 1.0.0-alpha.13
declare const _default: (import("@markuplint/ml-core").MLRule<boolean, import("./attr-spacing").AttrSpasingOptions> | import("@markuplint/ml-core").MLRule<boolean, null> | import("@markuplint/ml-core").MLRule<import("./class-naming").Value, null> | import("@markuplint/ml-core").MLRule<"always", { | ||
denyObsolateType: boolean; | ||
}> | import("@markuplint/ml-core").MLRule<import("./indentation").Value, import("./indentation").IndentationOptions> | import("@markuplint/ml-core").MLRule<boolean, { | ||
tag: string; | ||
contents: { | ||
require: string; | ||
min?: number | undefined; | ||
max?: number | undefined; | ||
} | { | ||
optional: string; | ||
} | { | ||
oneOrMore: string; | ||
max?: number | undefined; | ||
} | { | ||
choice: string[]; | ||
}; | ||
}[]> | import("@markuplint/ml-core").MLRule<boolean, import("./required-h1").RequiredH1Options>)[]; | ||
}> | import("@markuplint/ml-core").MLRule<import("./indentation").Value, import("./indentation").IndentationOptions> | import("@markuplint/ml-core").MLRule<boolean, import("@markuplint/ml-spec").PermittedStructuresSchema[]> | import("@markuplint/ml-core").MLRule<boolean, import("./required-h1").RequiredH1Options>)[]; | ||
export default _default; |
@@ -1,27 +0,3 @@ | ||
declare type TagRule = { | ||
tag: string; | ||
contents: Contents[]; | ||
}; | ||
declare type Contents = Required | Optional | OneOrMore | ZeroOrMore | Choice; | ||
declare type Required = { | ||
require: string; | ||
min?: number; | ||
max?: number; | ||
}; | ||
declare type Optional = { | ||
optional: string; | ||
max?: number; | ||
}; | ||
declare type OneOrMore = { | ||
oneOrMore: string; | ||
max?: number; | ||
}; | ||
declare type ZeroOrMore = { | ||
zeroOrMore: string; | ||
max?: number; | ||
}; | ||
declare type Choice = { | ||
choice: Contents[]; | ||
}; | ||
declare const _default: import("@markuplint/ml-core").MLRule<boolean, TagRule[]>; | ||
import { PermittedStructuresSchema } from '@markuplint/ml-spec'; | ||
declare const _default: import("@markuplint/ml-core").MLRule<boolean, PermittedStructuresSchema[]>; | ||
export default _default; |
@@ -38,11 +38,10 @@ "use strict"; | ||
}; | ||
var __spreadArrays = (this && this.__spreadArrays) || function () { | ||
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; | ||
for (var r = Array(s), k = 0, i = 0; i < il; i++) | ||
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) | ||
r[k] = a[j]; | ||
return r; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var ml_core_1 = require("@markuplint/ml-core"); | ||
var permitted_content_spec_to_regexp_1 = __importDefault(require("./permitted-content.spec-to-regexp")); | ||
var html_spec_1 = __importDefault(require("./html-spec")); | ||
var unfold_content_models_to_tags_1 = __importDefault(require("./unfold-content-models-to-tags")); | ||
exports.default = ml_core_1.createRule({ | ||
@@ -54,3 +53,3 @@ name: 'permitted-contents', | ||
return __awaiter(this, void 0, void 0, function () { | ||
var reports; | ||
var reports, idCounter; | ||
var _this = this; | ||
@@ -61,63 +60,58 @@ return __generator(this, function (_a) { | ||
reports = []; | ||
idCounter = 0; | ||
return [4 /*yield*/, document.walkOn('Element', function (node) { return __awaiter(_this, void 0, void 0, function () { | ||
var _i, _a, rule, childElementsAndTextOrigin, childElementsAndText, _b, _c, ruleContent, permittedTag, min, max, numPrefix, childElementsAndTextNameList, count, _d, childElementsAndTextNameList_1, childElemName, el, name_1; | ||
return __generator(this, function (_e) { | ||
var nodes, spec, expGen, matched, _i, _a, conditional, parentExp, exp, conditionalResult, exp, specResult, _b, _c, rule, parentExp, exp, r; | ||
return __generator(this, function (_d) { | ||
if (!node.rule.value) { | ||
return [2 /*return*/]; | ||
} | ||
// console.log(node.rule.option); | ||
for (_i = 0, _a = node.rule.option; _i < _a.length; _i++) { | ||
rule = _a[_i]; | ||
if (rule.tag.toLowerCase() !== node.nodeName.toLowerCase()) { | ||
continue; | ||
nodes = node.getChildElementsAndTextNodeWithoutWhitespaces(); | ||
spec = html_spec_1.default(node.nodeName); | ||
expGen = new permitted_content_spec_to_regexp_1.default(idCounter++); | ||
if (spec) { | ||
if (spec.ancestor && !node.closest(spec.ancestor)) { | ||
reports.push({ | ||
severity: node.rule.severity, | ||
message: messages("Invalid structure: \"" + node.nodeName + "\" element must have an ancestor \"" + spec.ancestor + "\""), | ||
line: node.startLine, | ||
col: node.startCol, | ||
raw: node.raw, | ||
}); | ||
return [2 /*return*/]; | ||
} | ||
childElementsAndTextOrigin = Object.freeze(node.getChildElementsAndTextNodeWithoutWhitespaces()); | ||
childElementsAndText = __spreadArrays(childElementsAndTextOrigin); | ||
for (_b = 0, _c = rule.contents; _b < _c.length; _b++) { | ||
ruleContent = _c[_b]; | ||
permittedTag = null; | ||
min = 0; | ||
max = Infinity; | ||
numPrefix = ''; | ||
if (isRequiredContents(ruleContent)) { | ||
permittedTag = ruleContent.require; | ||
min = ruleContent.min || 1; | ||
max = ruleContent.max || 1; | ||
} | ||
else if (isOptionalContents(ruleContent)) { | ||
permittedTag = ruleContent.optional; | ||
min = 0; | ||
max = ruleContent.max || 1; | ||
} | ||
else if (isOneOrMoreContents(ruleContent)) { | ||
permittedTag = ruleContent.oneOrMore; | ||
min = 1; | ||
max = ruleContent.max || Infinity; | ||
numPrefix = 'one or more '; | ||
} | ||
else if (isZeroOrMoreContents(ruleContent)) { | ||
permittedTag = ruleContent.zeroOrMore; | ||
min = 0; | ||
max = ruleContent.max || Infinity; | ||
} | ||
if (!permittedTag) { | ||
continue; | ||
} | ||
childElementsAndTextNameList = childElementsAndText.map(function (node) { | ||
return node.type === 'Element' ? node.nodeName.toLowerCase() : '#text'; | ||
}); | ||
count = 0; | ||
for (_d = 0, childElementsAndTextNameList_1 = childElementsAndTextNameList; _d < childElementsAndTextNameList_1.length; _d++) { | ||
childElemName = childElementsAndTextNameList_1[_d]; | ||
if (childElemName === permittedTag) { | ||
count++; | ||
continue; | ||
matched = false; | ||
if (spec.conditional) { | ||
for (_i = 0, _a = spec.conditional; _i < _a.length; _i++) { | ||
conditional = _a[_i]; | ||
matched = | ||
('hasAttr' in conditional.condition && node.hasAttribute(conditional.condition.hasAttr)) || | ||
('parent' in conditional.condition && | ||
!!node.parentNode && | ||
node.parentNode.type === 'Element' && | ||
node.parentNode.matches(conditional.condition.parent)); | ||
// console.log({ ...conditional, matched }); | ||
if (matched) { | ||
parentExp = getRegExpFromParentNode(node, expGen); | ||
exp = expGen.specToRegExp(conditional.contents, parentExp); | ||
conditionalResult = match(exp, nodes); | ||
if (!conditionalResult) { | ||
reports.push({ | ||
severity: node.rule.severity, | ||
message: messages("Invalid content in \"" + node.nodeName + "\" element on the HTML spec"), | ||
line: node.startLine, | ||
col: node.startCol, | ||
raw: node.raw, | ||
}); | ||
break; | ||
} | ||
} | ||
break; | ||
} | ||
// console.log({ childElementsAndTextNameList, count }); | ||
if (count < min) { | ||
} | ||
if (!matched) { | ||
exp = getRegExpFromNode(node, expGen); | ||
specResult = match(exp, nodes); | ||
if (!specResult) { | ||
reports.push({ | ||
severity: node.rule.severity, | ||
message: messages("Invalid content, Require " + numPrefix + "\"" + permittedTag + "\" element" + (!numPrefix && 1 < min ? " at least " + min : '')), | ||
message: messages("Invalid content in \"" + node.nodeName + "\" element on the HTML spec"), | ||
line: node.startLine, | ||
@@ -128,23 +122,21 @@ col: node.startCol, | ||
} | ||
if (count > max) { | ||
reports.push({ | ||
severity: node.rule.severity, | ||
message: messages("Invalid content, The maximum length of \"" + permittedTag + "\" elements is " + max), | ||
line: node.startLine, | ||
col: node.startCol, | ||
raw: node.raw, | ||
}); | ||
} | ||
childElementsAndText.splice(0, count); | ||
} | ||
if (childElementsAndText[0]) { | ||
el = childElementsAndText[0]; | ||
name_1 = el.type === 'Element' ? el.nodeName : 'TextNode'; | ||
} | ||
for (_b = 0, _c = node.rule.option; _b < _c.length; _b++) { | ||
rule = _c[_b]; | ||
if (rule.tag.toLowerCase() !== node.nodeName.toLowerCase()) { | ||
continue; | ||
} | ||
parentExp = getRegExpFromParentNode(node, expGen); | ||
exp = expGen.specToRegExp(rule.contents, parentExp); | ||
r = match(exp, nodes); | ||
if (!r) { | ||
reports.push({ | ||
severity: node.rule.severity, | ||
message: messages("Invalid contents, \"" + node.nodeName + "\" is unpermitted to contain \"" + name_1 + "\""), | ||
line: el.startLine, | ||
col: el.startCol, | ||
raw: el.raw, | ||
message: messages("Invalid content in \"" + node.nodeName + "\" element on rule settings"), | ||
line: node.startLine, | ||
col: node.startCol, | ||
raw: node.raw, | ||
}); | ||
return [2 /*return*/]; | ||
} | ||
@@ -163,16 +155,107 @@ } | ||
}); | ||
function isRequiredContents(contents) { | ||
return 'require' in contents; | ||
function normalization(nodes) { | ||
return nodes.map(function (node) { return "<" + (node.type === 'Element' ? node.nodeName : '#text') + ">"; }).join(''); | ||
} | ||
function isOptionalContents(contents) { | ||
return 'optional' in contents; | ||
var expMapOnNodeId = new Map(); | ||
function getRegExpFromNode(node, expGen) { | ||
// console.log({ n: node.nodeName }); | ||
if (expMapOnNodeId.has(node.uuid)) { | ||
return expMapOnNodeId.get(node.uuid); | ||
} | ||
var parentExp = node.parentNode ? getRegExpFromNode(node.parentNode, expGen) : null; | ||
var spec = html_spec_1.default(node.nodeName); | ||
var contentRule = spec ? spec.contents : true; | ||
var exp = expGen.specToRegExp(contentRule, parentExp); | ||
expMapOnNodeId.set(node.uuid, exp); | ||
return exp; | ||
} | ||
function isOneOrMoreContents(contents) { | ||
return 'oneOrMore' in contents; | ||
function getRegExpFromParentNode(node, expGen) { | ||
// console.log({ p: node.nodeName }); | ||
var parentExp = node.parentNode ? getRegExpFromNode(node.parentNode, expGen) : null; | ||
return parentExp; | ||
} | ||
function isZeroOrMoreContents(contents) { | ||
return 'zeroOrMore' in contents; | ||
function match(exp, nodes) { | ||
var target = normalization(nodes); | ||
var result = exp.exec(target); | ||
if (!result) { | ||
return false; | ||
} | ||
var capGroups = result.groups; | ||
// console.log({ exp, target, capGroups }); | ||
if (capGroups) { | ||
var groupNames = Object.keys(capGroups); | ||
var _loop_1 = function (groupName) { | ||
var matched = capGroups[groupName]; | ||
if (!matched) { | ||
return "continue"; | ||
} | ||
var targetsMaybeIncludesNotAllowedDescendants = Array.from(new Set(matched.split(/><|<|>/g).filter(function (_) { return _; }))); | ||
var _a = groupName.split(/(?<=[a-z0-9])_/gi), type = _a[0], _selector = _a.slice(2); | ||
switch (type) { | ||
case 'NAD': { | ||
var contents_1 = new Set(); | ||
var transparentGroupName = groupNames.find(function (name) { return /^TRANSPARENT_[0-9]+$/.test(name); }); | ||
var inTransparent_1 = _selector.includes('__InTRANSPARENT') | ||
? transparentGroupName && capGroups[transparentGroupName] | ||
? capGroups[transparentGroupName].split(/><|<|>/g).filter(function (_) { return _; }) | ||
: null | ||
: null; | ||
_selector.forEach(function (content) { | ||
if (content[0] === '_') { | ||
unfold_content_models_to_tags_1.default(content.replace('_', '#')).forEach(function (tag) { return contents_1.add(tag); }); | ||
return; | ||
} | ||
contents_1.add(content); | ||
}); | ||
var selectors = Array.from(contents_1); | ||
targetsMaybeIncludesNotAllowedDescendants = targetsMaybeIncludesNotAllowedDescendants.filter(function (content) { return (inTransparent_1 ? inTransparent_1.includes(content) : true); }); | ||
// console.log({ | ||
// groupName, | ||
// matched, | ||
// _selector, | ||
// type, | ||
// selectors, | ||
// inTransparent, | ||
// targetsMaybeIncludesNotAllowedDescendants, | ||
// }); | ||
for (var _i = 0, nodes_1 = nodes; _i < nodes_1.length; _i++) { | ||
var node = nodes_1[_i]; | ||
for (var _b = 0, targetsMaybeIncludesNotAllowedDescendants_1 = targetsMaybeIncludesNotAllowedDescendants; _b < targetsMaybeIncludesNotAllowedDescendants_1.length; _b++) { | ||
var target_1 = targetsMaybeIncludesNotAllowedDescendants_1[_b]; | ||
if (node.type === 'Text') { | ||
if (selectors.includes('#text')) { | ||
return { value: false }; | ||
} | ||
continue; | ||
} | ||
if (node.nodeName.toLowerCase() !== target_1.toLowerCase()) { | ||
continue; | ||
} | ||
for (var _c = 0, selectors_1 = selectors; _c < selectors_1.length; _c++) { | ||
var selector = selectors_1[_c]; | ||
// console.log({ selector, target }); | ||
// Self | ||
if (node.matches(selector)) { | ||
return { value: false }; | ||
} | ||
// Descendants | ||
var nodeList = node.querySelectorAll(selector); | ||
if (nodeList.length) { | ||
return { value: false }; | ||
} | ||
} | ||
} | ||
} | ||
break; | ||
} | ||
} | ||
}; | ||
for (var _i = 0, groupNames_1 = groupNames; _i < groupNames_1.length; _i++) { | ||
var groupName = groupNames_1[_i]; | ||
var state_1 = _loop_1(groupName); | ||
if (typeof state_1 === "object") | ||
return state_1.value; | ||
} | ||
} | ||
return !!result; | ||
} | ||
// function isChoiceContents(contents: Contents): contents is Choice { | ||
// return 'choice' in contents; | ||
// } |
{ | ||
"name": "@markuplint/rules", | ||
"version": "1.0.0-alpha.12", | ||
"version": "1.0.0-alpha.13", | ||
"description": "Rules for markuplint", | ||
@@ -20,8 +20,8 @@ "repository": "git@github.com:markuplint/markuplint.git", | ||
"dependencies": { | ||
"@markuplint/ml-spec": "^1.0.0-alpha.3" | ||
"@markuplint/ml-spec": "^1.0.0-alpha.4" | ||
}, | ||
"devDependencies": { | ||
"@markuplint/ml-core": "^1.0.0-alpha.10" | ||
"@markuplint/ml-core": "^1.0.0-alpha.11" | ||
}, | ||
"gitHead": "daa0c35191fb6f794fe38850e3cd0ba38eafcfc9" | ||
"gitHead": "5966e1fa119ba9813e5a19d760a794abd88da08c" | ||
} |
Sorry, the diff of this file is not supported yet
522091
106
5131