@herb-tools/linter 0.8.0 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -2
- package/dist/herb-lint.js +88 -17
- package/dist/herb-lint.js.map +1 -1
- package/dist/index.cjs +58 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +59 -1
- package/dist/index.js.map +1 -1
- package/dist/loader.cjs +70 -8
- package/dist/loader.cjs.map +1 -1
- package/dist/loader.js +70 -8
- package/dist/loader.js.map +1 -1
- package/dist/package.json +7 -7
- package/dist/src/herb-disable-comment-utils.js.map +1 -1
- package/dist/src/linter-ignore.js +42 -0
- package/dist/src/linter-ignore.js.map +1 -0
- package/dist/src/linter.js +12 -0
- package/dist/src/linter.js.map +1 -1
- package/dist/src/rules/html-head-only-elements.js +6 -1
- package/dist/src/rules/html-head-only-elements.js.map +1 -1
- package/dist/src/rules/rule-utils.js +2 -0
- package/dist/src/rules/rule-utils.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/linter-ignore.d.ts +12 -0
- package/dist/types/rules/rule-utils.d.ts +1 -1
- package/dist/types/src/linter-ignore.d.ts +12 -0
- package/dist/types/src/rules/rule-utils.d.ts +1 -1
- package/docs/rules/html-head-only-elements.md +24 -2
- package/package.json +7 -7
- package/src/herb-disable-comment-utils.ts +3 -0
- package/src/linter-ignore.ts +50 -0
- package/src/linter.ts +14 -0
- package/src/rules/html-head-only-elements.ts +6 -1
- package/src/rules/rule-utils.ts +3 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getNodesBeforePosition, getNodesAfterPosition, filterNodes, ERBContentNode, isERBOutputNode, Visitor, isToken, isParseResult, getStaticAttributeName, hasDynamicAttributeName as hasDynamicAttributeName$1, getCombinedAttributeName, hasStaticContent, getStaticContentFromNodes, Location, hasERBOutput, isEffectivelyStatic, getValidatableStaticContent, isERBNode, isWhitespaceNode, isCommentNode, isLiteralNode, isHTMLTextNode, Position, filterERBContentNodes, isNode, LiteralNode, didyoumean, filterLiteralNodes, Token, getTagName as getTagName$1, isHTMLElementNode, HTMLCloseTagNode, isHTMLOpenTagNode, WhitespaceNode, filterWhitespaceNodes, HTMLOpenTagNode } from '@herb-tools/core';
|
|
1
|
+
import { getNodesBeforePosition, getNodesAfterPosition, filterNodes, ERBContentNode, isERBOutputNode, Visitor, isToken, isParseResult, getStaticAttributeName, hasDynamicAttributeName as hasDynamicAttributeName$1, getCombinedAttributeName, hasStaticContent, getStaticContentFromNodes, Location, hasERBOutput, isEffectivelyStatic, getValidatableStaticContent, isERBNode, isWhitespaceNode, isCommentNode, isLiteralNode, isHTMLTextNode, Position, filterERBContentNodes, isNode, LiteralNode, didyoumean, filterLiteralNodes, Token, getTagName as getTagName$1, isHTMLElementNode, HTMLCloseTagNode, isHTMLOpenTagNode, WhitespaceNode, filterWhitespaceNodes, HTMLOpenTagNode, isERBCommentNode } from '@herb-tools/core';
|
|
2
2
|
|
|
3
3
|
class PrintContext {
|
|
4
4
|
output = "";
|
|
@@ -3053,6 +3053,8 @@ function findAttributeByName(attributes, attributeName) {
|
|
|
3053
3053
|
* Checks if a tag has a specific attribute
|
|
3054
3054
|
*/
|
|
3055
3055
|
function hasAttribute(node, attributeName) {
|
|
3056
|
+
if (!node)
|
|
3057
|
+
return false;
|
|
3056
3058
|
return getAttribute(node, attributeName) !== null;
|
|
3057
3059
|
}
|
|
3058
3060
|
/**
|
|
@@ -5152,8 +5154,13 @@ class HeadOnlyElementsVisitor extends BaseRuleVisitor {
|
|
|
5152
5154
|
return;
|
|
5153
5155
|
if (tagName === "title" && this.insideSVG)
|
|
5154
5156
|
return;
|
|
5157
|
+
if (tagName === "meta" && this.hasItempropAttribute(node))
|
|
5158
|
+
return;
|
|
5155
5159
|
this.addOffense(`Element \`<${tagName}>\` must be placed inside the \`<head>\` tag.`, node.location);
|
|
5156
5160
|
}
|
|
5161
|
+
hasItempropAttribute(node) {
|
|
5162
|
+
return hasAttribute(node.open_tag, "itemprop");
|
|
5163
|
+
}
|
|
5157
5164
|
get insideHead() {
|
|
5158
5165
|
return this.elementStack.includes("head");
|
|
5159
5166
|
}
|
|
@@ -6663,6 +6670,46 @@ const rules = [
|
|
|
6663
6670
|
ParserNoErrorsRule,
|
|
6664
6671
|
];
|
|
6665
6672
|
|
|
6673
|
+
const HERB_LINTER_PREFIX = "herb:linter";
|
|
6674
|
+
const HERB_LINTER_IGNORE_PREFIX = `${HERB_LINTER_PREFIX} ignore`;
|
|
6675
|
+
/**
|
|
6676
|
+
* Check if an ERB content node is a herb:linter ignore comment.
|
|
6677
|
+
*
|
|
6678
|
+
* @param node - The ERB content node to check
|
|
6679
|
+
* @returns true if this is a linter ignore directive
|
|
6680
|
+
*/
|
|
6681
|
+
function isHerbLinterIgnoreComment(node) {
|
|
6682
|
+
if (!isERBCommentNode(node))
|
|
6683
|
+
return false;
|
|
6684
|
+
const content = node?.content?.value || "";
|
|
6685
|
+
return content.trim() === HERB_LINTER_IGNORE_PREFIX;
|
|
6686
|
+
}
|
|
6687
|
+
/**
|
|
6688
|
+
* Check if the document contains a herb:linter ignore directive anywhere.
|
|
6689
|
+
*/
|
|
6690
|
+
function hasLinterIgnoreDirective(parseResult) {
|
|
6691
|
+
if (parseResult.failed)
|
|
6692
|
+
return false;
|
|
6693
|
+
const detector = new LinterIgnoreDetector();
|
|
6694
|
+
detector.visit(parseResult.value);
|
|
6695
|
+
return detector.hasIgnoreDirective;
|
|
6696
|
+
}
|
|
6697
|
+
/**
|
|
6698
|
+
* Visitor that detects if the AST contains a herb:linter ignore directive.
|
|
6699
|
+
*/
|
|
6700
|
+
class LinterIgnoreDetector extends Visitor {
|
|
6701
|
+
hasIgnoreDirective = false;
|
|
6702
|
+
visitERBContentNode(node) {
|
|
6703
|
+
if (isHerbLinterIgnoreComment(node)) {
|
|
6704
|
+
this.hasIgnoreDirective = true;
|
|
6705
|
+
return;
|
|
6706
|
+
}
|
|
6707
|
+
if (this.hasIgnoreDirective)
|
|
6708
|
+
return;
|
|
6709
|
+
this.visitChildNodes(node);
|
|
6710
|
+
}
|
|
6711
|
+
}
|
|
6712
|
+
|
|
6666
6713
|
class Linter {
|
|
6667
6714
|
rules;
|
|
6668
6715
|
allAvailableRules;
|
|
@@ -6873,6 +6920,17 @@ class Linter {
|
|
|
6873
6920
|
let ignoredCount = 0;
|
|
6874
6921
|
let wouldBeIgnoredCount = 0;
|
|
6875
6922
|
const parseResult = this.herb.parse(source, { track_whitespace: true });
|
|
6923
|
+
// Check for file-level ignore directive using visitor
|
|
6924
|
+
if (hasLinterIgnoreDirective(parseResult)) {
|
|
6925
|
+
return {
|
|
6926
|
+
offenses: [],
|
|
6927
|
+
errors: 0,
|
|
6928
|
+
warnings: 0,
|
|
6929
|
+
info: 0,
|
|
6930
|
+
hints: 0,
|
|
6931
|
+
ignored: 0
|
|
6932
|
+
};
|
|
6933
|
+
}
|
|
6876
6934
|
const lexResult = this.herb.lex(source);
|
|
6877
6935
|
const hasParserErrors = parseResult.recursiveErrors().length > 0;
|
|
6878
6936
|
const sourceLines = source.split("\n");
|