@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.cjs CHANGED
@@ -186,7 +186,7 @@ class Token {
186
186
  }
187
187
 
188
188
  // NOTE: This file is generated by the templates/template.rb script and should not
189
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.8.0/templates/javascript/packages/core/src/errors.ts.erb
189
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.8.2/templates/javascript/packages/core/src/errors.ts.erb
190
190
  class HerbError {
191
191
  type;
192
192
  message;
@@ -716,7 +716,7 @@ function convertToUTF8(string) {
716
716
  }
717
717
 
718
718
  // NOTE: This file is generated by the templates/template.rb script and should not
719
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.8.0/templates/javascript/packages/core/src/nodes.ts.erb
719
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.8.2/templates/javascript/packages/core/src/nodes.ts.erb
720
720
  class Node {
721
721
  type;
722
722
  location;
@@ -2950,7 +2950,7 @@ class ParseResult extends Result {
2950
2950
  }
2951
2951
 
2952
2952
  // NOTE: This file is generated by the templates/template.rb script and should not
2953
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.8.0/templates/javascript/packages/core/src/node-type-guards.ts.erb
2953
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.8.2/templates/javascript/packages/core/src/node-type-guards.ts.erb
2954
2954
  /**
2955
2955
  * Type guard functions for AST nodes.
2956
2956
  * These functions provide type checking by combining both instanceof
@@ -3340,10 +3340,14 @@ function isERBOutputNode(node) {
3340
3340
  return ["<%=", "<%=="].includes(node.tag_opening?.value);
3341
3341
  }
3342
3342
  /**
3343
- * Checks if a node is a non-output ERB node (control flow: <% %>)
3343
+ * Checks if a node is a ERB comment node (control flow: <%# %>)
3344
3344
  */
3345
- function isERBControlFlowNode(node) {
3346
- return isAnyOf(node, ERBIfNode, ERBUnlessNode, ERBBlockNode, ERBCaseNode, ERBCaseMatchNode, ERBWhileNode, ERBForNode, ERBBeginNode);
3345
+ function isERBCommentNode(node) {
3346
+ if (!isERBNode(node))
3347
+ return false;
3348
+ if (!node.tag_opening?.value)
3349
+ return false;
3350
+ return node.tag_opening?.value === "<%#" || (node.tag_opening?.value !== "<%#" && (node.content?.value || "").trimStart().startsWith("#"));
3347
3351
  }
3348
3352
  /**
3349
3353
  * Checks if an array of nodes contains any ERB content nodes
@@ -3462,7 +3466,7 @@ function getTagName$1(node) {
3462
3466
  * Check if a node is a comment (HTML comment or ERB comment)
3463
3467
  */
3464
3468
  function isCommentNode(node) {
3465
- return isNode(node, HTMLCommentNode) || (isERBNode(node) && !isERBControlFlowNode(node));
3469
+ return isHTMLCommentNode(node) || isERBCommentNode(node);
3466
3470
  }
3467
3471
  /**
3468
3472
  * Compares two positions to determine if the first comes before the second
@@ -3649,7 +3653,7 @@ function didyoumean(input, list, threshold) {
3649
3653
  }
3650
3654
 
3651
3655
  // NOTE: This file is generated by the templates/template.rb script and should not
3652
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.8.0/templates/javascript/packages/core/src/visitor.ts.erb
3656
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.8.2/templates/javascript/packages/core/src/visitor.ts.erb
3653
3657
  class Visitor {
3654
3658
  visit(node) {
3655
3659
  if (!node)
@@ -6864,6 +6868,8 @@ function findAttributeByName(attributes, attributeName) {
6864
6868
  * Checks if a tag has a specific attribute
6865
6869
  */
6866
6870
  function hasAttribute(node, attributeName) {
6871
+ if (!node)
6872
+ return false;
6867
6873
  return getAttribute(node, attributeName) !== null;
6868
6874
  }
6869
6875
  /**
@@ -8963,8 +8969,13 @@ class HeadOnlyElementsVisitor extends BaseRuleVisitor {
8963
8969
  return;
8964
8970
  if (tagName === "title" && this.insideSVG)
8965
8971
  return;
8972
+ if (tagName === "meta" && this.hasItempropAttribute(node))
8973
+ return;
8966
8974
  this.addOffense(`Element \`<${tagName}>\` must be placed inside the \`<head>\` tag.`, node.location);
8967
8975
  }
8976
+ hasItempropAttribute(node) {
8977
+ return hasAttribute(node.open_tag, "itemprop");
8978
+ }
8968
8979
  get insideHead() {
8969
8980
  return this.elementStack.includes("head");
8970
8981
  }
@@ -10474,6 +10485,46 @@ const rules = [
10474
10485
  ParserNoErrorsRule,
10475
10486
  ];
10476
10487
 
10488
+ const HERB_LINTER_PREFIX = "herb:linter";
10489
+ const HERB_LINTER_IGNORE_PREFIX = `${HERB_LINTER_PREFIX} ignore`;
10490
+ /**
10491
+ * Check if an ERB content node is a herb:linter ignore comment.
10492
+ *
10493
+ * @param node - The ERB content node to check
10494
+ * @returns true if this is a linter ignore directive
10495
+ */
10496
+ function isHerbLinterIgnoreComment(node) {
10497
+ if (!isERBCommentNode(node))
10498
+ return false;
10499
+ const content = node?.content?.value || "";
10500
+ return content.trim() === HERB_LINTER_IGNORE_PREFIX;
10501
+ }
10502
+ /**
10503
+ * Check if the document contains a herb:linter ignore directive anywhere.
10504
+ */
10505
+ function hasLinterIgnoreDirective(parseResult) {
10506
+ if (parseResult.failed)
10507
+ return false;
10508
+ const detector = new LinterIgnoreDetector();
10509
+ detector.visit(parseResult.value);
10510
+ return detector.hasIgnoreDirective;
10511
+ }
10512
+ /**
10513
+ * Visitor that detects if the AST contains a herb:linter ignore directive.
10514
+ */
10515
+ class LinterIgnoreDetector extends Visitor {
10516
+ hasIgnoreDirective = false;
10517
+ visitERBContentNode(node) {
10518
+ if (isHerbLinterIgnoreComment(node)) {
10519
+ this.hasIgnoreDirective = true;
10520
+ return;
10521
+ }
10522
+ if (this.hasIgnoreDirective)
10523
+ return;
10524
+ this.visitChildNodes(node);
10525
+ }
10526
+ }
10527
+
10477
10528
  class Linter {
10478
10529
  rules;
10479
10530
  allAvailableRules;
@@ -10684,6 +10735,17 @@ class Linter {
10684
10735
  let ignoredCount = 0;
10685
10736
  let wouldBeIgnoredCount = 0;
10686
10737
  const parseResult = this.herb.parse(source, { track_whitespace: true });
10738
+ // Check for file-level ignore directive using visitor
10739
+ if (hasLinterIgnoreDirective(parseResult)) {
10740
+ return {
10741
+ offenses: [],
10742
+ errors: 0,
10743
+ warnings: 0,
10744
+ info: 0,
10745
+ hints: 0,
10746
+ ignored: 0
10747
+ };
10748
+ }
10687
10749
  const lexResult = this.herb.lex(source);
10688
10750
  const hasParserErrors = parseResult.recursiveErrors().length > 0;
10689
10751
  const sourceLines = source.split("\n");