@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/dist/loader.js CHANGED
@@ -165,7 +165,7 @@ class Token {
165
165
  }
166
166
 
167
167
  // NOTE: This file is generated by the templates/template.rb script and should not
168
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.8.0/templates/javascript/packages/core/src/errors.ts.erb
168
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.8.2/templates/javascript/packages/core/src/errors.ts.erb
169
169
  class HerbError {
170
170
  type;
171
171
  message;
@@ -695,7 +695,7 @@ function convertToUTF8(string) {
695
695
  }
696
696
 
697
697
  // NOTE: This file is generated by the templates/template.rb script and should not
698
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.8.0/templates/javascript/packages/core/src/nodes.ts.erb
698
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.8.2/templates/javascript/packages/core/src/nodes.ts.erb
699
699
  class Node {
700
700
  type;
701
701
  location;
@@ -2929,7 +2929,7 @@ class ParseResult extends Result {
2929
2929
  }
2930
2930
 
2931
2931
  // NOTE: This file is generated by the templates/template.rb script and should not
2932
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.8.0/templates/javascript/packages/core/src/node-type-guards.ts.erb
2932
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.8.2/templates/javascript/packages/core/src/node-type-guards.ts.erb
2933
2933
  /**
2934
2934
  * Type guard functions for AST nodes.
2935
2935
  * These functions provide type checking by combining both instanceof
@@ -3319,10 +3319,14 @@ function isERBOutputNode(node) {
3319
3319
  return ["<%=", "<%=="].includes(node.tag_opening?.value);
3320
3320
  }
3321
3321
  /**
3322
- * Checks if a node is a non-output ERB node (control flow: <% %>)
3322
+ * Checks if a node is a ERB comment node (control flow: <%# %>)
3323
3323
  */
3324
- function isERBControlFlowNode(node) {
3325
- return isAnyOf(node, ERBIfNode, ERBUnlessNode, ERBBlockNode, ERBCaseNode, ERBCaseMatchNode, ERBWhileNode, ERBForNode, ERBBeginNode);
3324
+ function isERBCommentNode(node) {
3325
+ if (!isERBNode(node))
3326
+ return false;
3327
+ if (!node.tag_opening?.value)
3328
+ return false;
3329
+ return node.tag_opening?.value === "<%#" || (node.tag_opening?.value !== "<%#" && (node.content?.value || "").trimStart().startsWith("#"));
3326
3330
  }
3327
3331
  /**
3328
3332
  * Checks if an array of nodes contains any ERB content nodes
@@ -3441,7 +3445,7 @@ function getTagName$1(node) {
3441
3445
  * Check if a node is a comment (HTML comment or ERB comment)
3442
3446
  */
3443
3447
  function isCommentNode(node) {
3444
- return isNode(node, HTMLCommentNode) || (isERBNode(node) && !isERBControlFlowNode(node));
3448
+ return isHTMLCommentNode(node) || isERBCommentNode(node);
3445
3449
  }
3446
3450
  /**
3447
3451
  * Compares two positions to determine if the first comes before the second
@@ -3628,7 +3632,7 @@ function didyoumean(input, list, threshold) {
3628
3632
  }
3629
3633
 
3630
3634
  // NOTE: This file is generated by the templates/template.rb script and should not
3631
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.8.0/templates/javascript/packages/core/src/visitor.ts.erb
3635
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.8.2/templates/javascript/packages/core/src/visitor.ts.erb
3632
3636
  class Visitor {
3633
3637
  visit(node) {
3634
3638
  if (!node)
@@ -6843,6 +6847,8 @@ function findAttributeByName(attributes, attributeName) {
6843
6847
  * Checks if a tag has a specific attribute
6844
6848
  */
6845
6849
  function hasAttribute(node, attributeName) {
6850
+ if (!node)
6851
+ return false;
6846
6852
  return getAttribute(node, attributeName) !== null;
6847
6853
  }
6848
6854
  /**
@@ -8942,8 +8948,13 @@ class HeadOnlyElementsVisitor extends BaseRuleVisitor {
8942
8948
  return;
8943
8949
  if (tagName === "title" && this.insideSVG)
8944
8950
  return;
8951
+ if (tagName === "meta" && this.hasItempropAttribute(node))
8952
+ return;
8945
8953
  this.addOffense(`Element \`<${tagName}>\` must be placed inside the \`<head>\` tag.`, node.location);
8946
8954
  }
8955
+ hasItempropAttribute(node) {
8956
+ return hasAttribute(node.open_tag, "itemprop");
8957
+ }
8947
8958
  get insideHead() {
8948
8959
  return this.elementStack.includes("head");
8949
8960
  }
@@ -10453,6 +10464,46 @@ const rules = [
10453
10464
  ParserNoErrorsRule,
10454
10465
  ];
10455
10466
 
10467
+ const HERB_LINTER_PREFIX = "herb:linter";
10468
+ const HERB_LINTER_IGNORE_PREFIX = `${HERB_LINTER_PREFIX} ignore`;
10469
+ /**
10470
+ * Check if an ERB content node is a herb:linter ignore comment.
10471
+ *
10472
+ * @param node - The ERB content node to check
10473
+ * @returns true if this is a linter ignore directive
10474
+ */
10475
+ function isHerbLinterIgnoreComment(node) {
10476
+ if (!isERBCommentNode(node))
10477
+ return false;
10478
+ const content = node?.content?.value || "";
10479
+ return content.trim() === HERB_LINTER_IGNORE_PREFIX;
10480
+ }
10481
+ /**
10482
+ * Check if the document contains a herb:linter ignore directive anywhere.
10483
+ */
10484
+ function hasLinterIgnoreDirective(parseResult) {
10485
+ if (parseResult.failed)
10486
+ return false;
10487
+ const detector = new LinterIgnoreDetector();
10488
+ detector.visit(parseResult.value);
10489
+ return detector.hasIgnoreDirective;
10490
+ }
10491
+ /**
10492
+ * Visitor that detects if the AST contains a herb:linter ignore directive.
10493
+ */
10494
+ class LinterIgnoreDetector extends Visitor {
10495
+ hasIgnoreDirective = false;
10496
+ visitERBContentNode(node) {
10497
+ if (isHerbLinterIgnoreComment(node)) {
10498
+ this.hasIgnoreDirective = true;
10499
+ return;
10500
+ }
10501
+ if (this.hasIgnoreDirective)
10502
+ return;
10503
+ this.visitChildNodes(node);
10504
+ }
10505
+ }
10506
+
10456
10507
  class Linter {
10457
10508
  rules;
10458
10509
  allAvailableRules;
@@ -10663,6 +10714,17 @@ class Linter {
10663
10714
  let ignoredCount = 0;
10664
10715
  let wouldBeIgnoredCount = 0;
10665
10716
  const parseResult = this.herb.parse(source, { track_whitespace: true });
10717
+ // Check for file-level ignore directive using visitor
10718
+ if (hasLinterIgnoreDirective(parseResult)) {
10719
+ return {
10720
+ offenses: [],
10721
+ errors: 0,
10722
+ warnings: 0,
10723
+ info: 0,
10724
+ hints: 0,
10725
+ ignored: 0
10726
+ };
10727
+ }
10666
10728
  const lexResult = this.herb.lex(source);
10667
10729
  const hasParserErrors = parseResult.recursiveErrors().length > 0;
10668
10730
  const sourceLines = source.split("\n");