eslint-plugin-harlanzw 0.5.2 → 0.5.4

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.
Files changed (2) hide show
  1. package/dist/index.mjs +97 -64
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -4,7 +4,7 @@ import process from 'node:process';
4
4
  import { TextSourceCodeBase, ConfigCommentParser, Directive, VisitNodeStep } from '@eslint/plugin-kit';
5
5
  import { AST_NODE_TYPES } from '@typescript-eslint/utils';
6
6
 
7
- const version = "0.5.2";
7
+ const version = "0.5.4";
8
8
 
9
9
  const STRENGTH_PATTERNS = {
10
10
  strong: ["never", "must", "always", "under no circumstances", "absolutely", "required", "mandatory", "forbidden", "prohibited"],
@@ -641,6 +641,10 @@ function parseFrontmatter(lines) {
641
641
  return result;
642
642
  }
643
643
 
644
+ const COMPILED$8 = AMBIGUOUS_QUANTIFIERS.map((quantifier) => {
645
+ const escaped = quantifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
646
+ return new RegExp(`\\b${escaped}\\b`, "gi");
647
+ });
644
648
  const promptAmbiguousQuantifier = {
645
649
  meta: {
646
650
  type: "suggestion",
@@ -662,8 +666,9 @@ const promptAmbiguousQuantifier = {
662
666
  if (shouldSkipLine(i, codeBlockLines, frontmatterEnd))
663
667
  continue;
664
668
  const line = lines[i];
665
- for (const quantifier of AMBIGUOUS_QUANTIFIERS) {
666
- const regex = new RegExp(`\\b${quantifier}\\b`, "gi");
669
+ for (let qi = 0; qi < COMPILED$8.length; qi++) {
670
+ const regex = COMPILED$8[qi];
671
+ regex.lastIndex = 0;
667
672
  let match;
668
673
  while ((match = regex.exec(line)) !== null) {
669
674
  const suggestion = QUANTIFIER_SUGGESTIONS[match[0].toLowerCase()] ?? "a specific value";
@@ -689,6 +694,9 @@ const promptAmbiguousQuantifier = {
689
694
  }
690
695
  };
691
696
 
697
+ const COMPILED$7 = UNNECESSARY_ADVERBS.map((adverb) => {
698
+ return { regex: new RegExp(`\\b${adverb}\\s+`, "gi"), adverb };
699
+ });
692
700
  const aiDeslopAdverbs = {
693
701
  meta: {
694
702
  type: "suggestion",
@@ -711,8 +719,8 @@ const aiDeslopAdverbs = {
711
719
  continue;
712
720
  const line = lines[i];
713
721
  const lineNode = node.children[i];
714
- for (const adverb of UNNECESSARY_ADVERBS) {
715
- const regex = new RegExp(`\\b${adverb}\\s+`, "gi");
722
+ for (const { regex, adverb } of COMPILED$7) {
723
+ regex.lastIndex = 0;
716
724
  let match;
717
725
  while ((match = regex.exec(line)) !== null) {
718
726
  const startOffset = lineNode.position.start.offset + match.index;
@@ -838,7 +846,7 @@ const DEDUPED_ENTRIES = SORTED_ENTRIES$1.filter(([name, url]) => {
838
846
  SEEN_URLS.set(url, name);
839
847
  return true;
840
848
  });
841
- const COMPILED$1 = DEDUPED_ENTRIES.map(([name, url]) => {
849
+ const COMPILED$6 = DEDUPED_ENTRIES.map(([name, url]) => {
842
850
  const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
843
851
  return { name, url, regex: new RegExp(`(?<!\\[)\\b${escaped}\\b(?!\\])(?!\\()`, "g") };
844
852
  });
@@ -867,7 +875,7 @@ const aiDeslopAutolink = {
867
875
  if (line.trimStart().startsWith("#"))
868
876
  continue;
869
877
  const lineNode = node.children[i];
870
- for (const { name, url, regex } of COMPILED$1) {
878
+ for (const { name, url, regex } of COMPILED$6) {
871
879
  if (linkedUrls.has(url))
872
880
  continue;
873
881
  regex.lastIndex = 0;
@@ -909,6 +917,10 @@ const aiDeslopAutolink = {
909
917
  };
910
918
 
911
919
  const SORTED_PHRASES = Object.entries(BUZZWORD_PHRASES).sort((a, b) => b[0].length - a[0].length);
920
+ const COMPILED$5 = SORTED_PHRASES.map(([phrase, replacement]) => {
921
+ const escaped = phrase.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
922
+ return { regex: new RegExp(`\\b${escaped}\\b`, "gi"), phrase, replacement };
923
+ });
912
924
  const aiDeslopBuzzwords = {
913
925
  meta: {
914
926
  type: "suggestion",
@@ -932,9 +944,8 @@ const aiDeslopBuzzwords = {
932
944
  const line = lines[i];
933
945
  const lineNode = node.children[i];
934
946
  const matched = [];
935
- for (const [phrase, replacement] of SORTED_PHRASES) {
936
- const escaped = phrase.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
937
- const regex = new RegExp(`\\b${escaped}\\b`, "gi");
947
+ for (const { regex, replacement } of COMPILED$5) {
948
+ regex.lastIndex = 0;
938
949
  let match;
939
950
  while ((match = regex.exec(line)) !== null) {
940
951
  const matchStart = match.index;
@@ -1294,7 +1305,7 @@ const CASING_DICTIONARY = {
1294
1305
  };
1295
1306
 
1296
1307
  const SORTED_ENTRIES = Object.entries(CASING_DICTIONARY).sort((a, b) => b[0].length - a[0].length);
1297
- const COMPILED = SORTED_ENTRIES.map(([key, correct]) => {
1308
+ const COMPILED$4 = SORTED_ENTRIES.map(([key, correct]) => {
1298
1309
  const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1299
1310
  return { regex: new RegExp(`\\b${escaped}\\b`, "gi"), correct };
1300
1311
  });
@@ -1321,7 +1332,7 @@ const aiDeslopCasing = {
1321
1332
  const line = lines[i];
1322
1333
  const lineNode = node.children[i];
1323
1334
  const matched = [];
1324
- for (const { regex, correct } of COMPILED) {
1335
+ for (const { regex, correct } of COMPILED$4) {
1325
1336
  regex.lastIndex = 0;
1326
1337
  let match;
1327
1338
  while ((match = regex.exec(line)) !== null) {
@@ -1353,6 +1364,10 @@ const aiDeslopCasing = {
1353
1364
  }
1354
1365
  };
1355
1366
 
1367
+ const COMPILED$3 = FILLER_PHRASES.map((phrase) => {
1368
+ const escaped = phrase.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1369
+ return { regex: new RegExp(`\\b${escaped}\\s*,?\\s*`, "gi"), phrase };
1370
+ });
1356
1371
  const aiDeslopFiller = {
1357
1372
  meta: {
1358
1373
  type: "suggestion",
@@ -1375,8 +1390,8 @@ const aiDeslopFiller = {
1375
1390
  continue;
1376
1391
  const line = lines[i];
1377
1392
  const lineNode = node.children[i];
1378
- for (const phrase of FILLER_PHRASES) {
1379
- const regex = new RegExp(`\\b${phrase}\\s*,?\\s*`, "gi");
1393
+ for (const { regex, phrase } of COMPILED$3) {
1394
+ regex.lastIndex = 0;
1380
1395
  let match;
1381
1396
  while ((match = regex.exec(line)) !== null) {
1382
1397
  const startOffset = lineNode.position.start.offset + match.index;
@@ -1412,6 +1427,10 @@ const aiDeslopFiller = {
1412
1427
  }
1413
1428
  };
1414
1429
 
1430
+ const COMPILED$2 = HEDGING_WORDS.map((word) => {
1431
+ const escaped = word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1432
+ return { regex: new RegExp(`\\b${escaped}\\s+`, "gi"), word };
1433
+ });
1415
1434
  const aiDeslopHedging = {
1416
1435
  meta: {
1417
1436
  type: "suggestion",
@@ -1434,9 +1453,8 @@ const aiDeslopHedging = {
1434
1453
  continue;
1435
1454
  const line = lines[i];
1436
1455
  const lineNode = node.children[i];
1437
- for (const word of HEDGING_WORDS) {
1438
- const escaped = word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1439
- const regex = new RegExp(`\\b${escaped}\\s+`, "gi");
1456
+ for (const { regex, word } of COMPILED$2) {
1457
+ regex.lastIndex = 0;
1440
1458
  let match;
1441
1459
  while ((match = regex.exec(line)) !== null) {
1442
1460
  const startOffset = lineNode.position.start.offset + match.index;
@@ -1902,6 +1920,10 @@ const poorlyTokenized = [
1902
1920
  /\w{20,}/g,
1903
1921
  /\d{10,}/g
1904
1922
  ];
1923
+ const COMPILED_PHRASES = Object.entries(INEFFICIENT_PHRASES).map(([phrase, replacement]) => {
1924
+ const escaped = phrase.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1925
+ return { regex: new RegExp(`\\b${escaped}\\b`, "gi"), replacement };
1926
+ });
1905
1927
  const promptInefficientToken = {
1906
1928
  meta: {
1907
1929
  type: "suggestion",
@@ -1927,9 +1949,9 @@ const promptInefficientToken = {
1927
1949
  const lineNode = node.children[i];
1928
1950
  const reported = /* @__PURE__ */ new Set();
1929
1951
  for (const pattern of poorlyTokenized) {
1930
- const regex = new RegExp(pattern.source, pattern.flags);
1952
+ pattern.lastIndex = 0;
1931
1953
  let match;
1932
- while ((match = regex.exec(line)) !== null) {
1954
+ while ((match = pattern.exec(line)) !== null) {
1933
1955
  const key = `${match.index}:${match[0].length}`;
1934
1956
  if (reported.has(key))
1935
1957
  continue;
@@ -1945,8 +1967,8 @@ const promptInefficientToken = {
1945
1967
  });
1946
1968
  }
1947
1969
  }
1948
- for (const [phrase, replacement] of Object.entries(INEFFICIENT_PHRASES)) {
1949
- const regex = new RegExp(`\\b${phrase}\\b`, "gi");
1970
+ for (const { regex, replacement } of COMPILED_PHRASES) {
1971
+ regex.lastIndex = 0;
1950
1972
  let match;
1951
1973
  while ((match = regex.exec(line)) !== null) {
1952
1974
  const startOffset = lineNode.position.start.offset + match.index;
@@ -2194,9 +2216,9 @@ const promptRedundantInstruction = {
2194
2216
  for (let i = 0; i < lines.length; i++) {
2195
2217
  if (shouldSkipLine(i, codeBlockLines, frontmatterEnd))
2196
2218
  continue;
2197
- const regex = new RegExp(instructionRegex.source, instructionRegex.flags);
2219
+ instructionRegex.lastIndex = 0;
2198
2220
  let match;
2199
- while ((match = regex.exec(lines[i])) !== null) {
2221
+ while ((match = instructionRegex.exec(lines[i])) !== null) {
2200
2222
  const normalized = match[1].toLowerCase().trim().replace(/\s+/g, " ");
2201
2223
  if (normalized.length > 10) {
2202
2224
  const existing = instructionPatterns.get(normalized) ?? [];
@@ -2568,9 +2590,9 @@ const promptUndefinedVariable = {
2568
2590
  continue;
2569
2591
  const line = lines[i];
2570
2592
  for (const defPattern of definitionPatterns) {
2571
- const regex = new RegExp(defPattern.source, defPattern.flags);
2593
+ defPattern.lastIndex = 0;
2572
2594
  let match2;
2573
- while ((match2 = regex.exec(line)) !== null)
2595
+ while ((match2 = defPattern.exec(line)) !== null)
2574
2596
  definedVars.add(match2[1].toLowerCase());
2575
2597
  }
2576
2598
  const varRegex = /\{\{(\w+)\}\}/g;
@@ -2629,9 +2651,9 @@ const promptUnresolvedReference = {
2629
2651
  continue;
2630
2652
  const line = lines[i];
2631
2653
  for (const pattern of unresolvedPatterns) {
2632
- const regex = new RegExp(pattern.source, pattern.flags);
2654
+ pattern.lastIndex = 0;
2633
2655
  let match;
2634
- while ((match = regex.exec(line)) !== null) {
2656
+ while ((match = pattern.exec(line)) !== null) {
2635
2657
  context.report({
2636
2658
  loc: {
2637
2659
  start: { line: i + 1, column: match.index + 1 },
@@ -2648,6 +2670,9 @@ const promptUnresolvedReference = {
2648
2670
  }
2649
2671
  };
2650
2672
 
2673
+ const COMPILED$1 = VAGUE_TERMS.map((term) => {
2674
+ return new RegExp(`\\bbe ${term}\\b|\\bin a ${term}\\b`, "gi");
2675
+ });
2651
2676
  const promptVagueTerm = {
2652
2677
  meta: {
2653
2678
  type: "suggestion",
@@ -2668,8 +2693,9 @@ const promptVagueTerm = {
2668
2693
  if (shouldSkipLine(i, codeBlockLines, frontmatterEnd))
2669
2694
  continue;
2670
2695
  const line = lines[i];
2671
- for (const term of VAGUE_TERMS) {
2672
- const regex = new RegExp(`\\bbe ${term}\\b|\\bin a ${term}\\b`, "gi");
2696
+ for (let vi = 0; vi < COMPILED$1.length; vi++) {
2697
+ const regex = COMPILED$1[vi];
2698
+ regex.lastIndex = 0;
2673
2699
  let match;
2674
2700
  while ((match = regex.exec(line)) !== null) {
2675
2701
  context.report({
@@ -2688,6 +2714,10 @@ const promptVagueTerm = {
2688
2714
  }
2689
2715
  };
2690
2716
 
2717
+ const COMPILED = STRENGTH_PATTERNS.weak.map((pattern) => {
2718
+ const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2719
+ return new RegExp(`\\b${escaped}\\b`, "gi");
2720
+ });
2691
2721
  const promptWeakInstruction = {
2692
2722
  meta: {
2693
2723
  type: "suggestion",
@@ -2709,8 +2739,9 @@ const promptWeakInstruction = {
2709
2739
  if (shouldSkipLine(i, codeBlockLines, frontmatterEnd))
2710
2740
  continue;
2711
2741
  const line = lines[i];
2712
- for (const pattern of STRENGTH_PATTERNS.weak) {
2713
- const regex = new RegExp(`\\b${pattern}\\b`, "gi");
2742
+ for (let wi = 0; wi < COMPILED.length; wi++) {
2743
+ const regex = COMPILED[wi];
2744
+ regex.lastIndex = 0;
2714
2745
  let match;
2715
2746
  while ((match = regex.exec(line)) !== null) {
2716
2747
  const suggestion = WEAK_TO_STRONG[match[0].toLowerCase()] ?? "Must";
@@ -2778,12 +2809,21 @@ function hasJsxExternalAttr(attrs) {
2778
2809
  (a) => a.type === "JSXAttribute" && a.name?.name === "external"
2779
2810
  );
2780
2811
  }
2812
+ const excludeCache = /* @__PURE__ */ new Map();
2813
+ function getExcludeRegex(pattern) {
2814
+ let regex = excludeCache.get(pattern);
2815
+ if (!regex) {
2816
+ regex = new RegExp(pattern);
2817
+ excludeCache.set(pattern, regex);
2818
+ }
2819
+ return regex;
2820
+ }
2781
2821
  function shouldSkipLink(url, node, options) {
2782
2822
  if (options.ignoreExternal && (isExternalUrl(url) || hasExternalAttr(node)))
2783
2823
  return true;
2784
2824
  if (options.exclude?.length) {
2785
2825
  for (const pattern of options.exclude) {
2786
- if (new RegExp(pattern).test(url))
2826
+ if (getExcludeRegex(pattern).test(url))
2787
2827
  return true;
2788
2828
  }
2789
2829
  }
@@ -2794,7 +2834,7 @@ function shouldSkipJsxLink(url, attrs, options) {
2794
2834
  return true;
2795
2835
  if (options.exclude?.length) {
2796
2836
  for (const pattern of options.exclude) {
2797
- if (new RegExp(pattern).test(url))
2837
+ if (getExcludeRegex(pattern).test(url))
2798
2838
  return true;
2799
2839
  }
2800
2840
  }
@@ -2883,16 +2923,6 @@ function isReturned(node) {
2883
2923
  }
2884
2924
  return false;
2885
2925
  }
2886
- function isInFunction(node) {
2887
- let parent = node.parent;
2888
- while (parent) {
2889
- if (parent.type === "FunctionDeclaration" || parent.type === "FunctionExpression" || parent.type === "ArrowFunctionExpression") {
2890
- return true;
2891
- }
2892
- parent = parent.parent;
2893
- }
2894
- return false;
2895
- }
2896
2926
  function isInAsyncFunction(node) {
2897
2927
  let parent = node.parent;
2898
2928
  while (parent) {
@@ -2906,18 +2936,18 @@ function isInAsyncFunction(node) {
2906
2936
  function isFunctionCall(node, functionName) {
2907
2937
  return node.callee.type === "Identifier" && node.callee.name === functionName;
2908
2938
  }
2909
- function isAtTopLevel(node) {
2939
+ function getNodeScope(node) {
2910
2940
  let parent = node.parent;
2911
2941
  while (parent) {
2912
- if (parent.type === "Program") {
2913
- return true;
2914
- }
2915
2942
  if (parent.type === "FunctionDeclaration" || parent.type === "FunctionExpression" || parent.type === "ArrowFunctionExpression") {
2916
- return false;
2943
+ return { atTopLevel: false, inFunction: true };
2944
+ }
2945
+ if (parent.type === "Program") {
2946
+ return { atTopLevel: true, inFunction: false };
2917
2947
  }
2918
2948
  parent = parent.parent;
2919
2949
  }
2920
- return false;
2950
+ return { atTopLevel: false, inFunction: false };
2921
2951
  }
2922
2952
  function isCallExpression(node) {
2923
2953
  return node.type === "CallExpression";
@@ -3109,17 +3139,15 @@ function isReactivityCall(node, vueImports) {
3109
3139
  }
3110
3140
  return false;
3111
3141
  }
3142
+ const COMPOSABLE_RE = /^use[A-Z_]/;
3112
3143
  function isComposableCall(node) {
3113
3144
  if (node.callee.type === "Identifier") {
3114
- return /^use[A-Z_]/.test(node.callee.name);
3145
+ return COMPOSABLE_RE.test(node.callee.name);
3115
3146
  }
3116
3147
  return false;
3117
3148
  }
3118
3149
  function isComposableName(name) {
3119
- return /^use[A-Z_]/.test(name);
3120
- }
3121
- function isInVueScriptSetup(node) {
3122
- return isAtTopLevel(node);
3150
+ return COMPOSABLE_RE.test(name);
3123
3151
  }
3124
3152
  function isDefinePropsCall(node) {
3125
3153
  return node.callee.type === "Identifier" && node.callee.name === "defineProps";
@@ -3963,9 +3991,8 @@ const nuxtAwaitNavigateTo = createEslintRule({
3963
3991
  if (!isFunctionCall(node, "navigateTo")) {
3964
3992
  return;
3965
3993
  }
3966
- const inFunction = isInFunction(node);
3967
- const inVueScriptSetup = isInVueScriptSetup(node);
3968
- if (inVueScriptSetup && !inFunction) {
3994
+ const { inFunction, atTopLevel: inVueScriptSetup } = getNodeScope(node);
3995
+ if (inVueScriptSetup) {
3969
3996
  if (!isAwaited(node)) {
3970
3997
  context.report({
3971
3998
  node,
@@ -4481,9 +4508,8 @@ ${indent}})`;
4481
4508
  if (!isSideEffectCall(node)) {
4482
4509
  return;
4483
4510
  }
4484
- const inVueScriptSetup = isInVueScriptSetup(node);
4485
- const inFunction = isInFunction(node);
4486
- if (inVueScriptSetup && !inFunction) {
4511
+ const { atTopLevel } = getNodeScope(node);
4512
+ if (atTopLevel) {
4487
4513
  const functionName = node.callee.type === "Identifier" ? node.callee.name : "unknown";
4488
4514
  reportSideEffect(node, functionName);
4489
4515
  }
@@ -4492,9 +4518,8 @@ ${indent}})`;
4492
4518
  if (node.callee.type !== "Identifier" || !SIDE_EFFECT_FUNCTIONS.has(node.callee.name)) {
4493
4519
  return;
4494
4520
  }
4495
- const inVueScriptSetup = isInVueScriptSetup(node);
4496
- const inFunction = isInFunction(node);
4497
- if (inVueScriptSetup && !inFunction) {
4521
+ const { atTopLevel } = getNodeScope(node);
4522
+ if (atTopLevel) {
4498
4523
  const functionName = node.callee.type === "Identifier" ? node.callee.name : "unknown";
4499
4524
  reportSideEffect(node, functionName);
4500
4525
  }
@@ -5317,6 +5342,8 @@ const vueNoTorefsOnProps = createEslintRule({
5317
5342
  });
5318
5343
 
5319
5344
  const RULE_NAME = "vue-require-composable-prefix";
5345
+ const DEFINE_RE = /^define[A-Z]/;
5346
+ const CREATE_RE = /^create[A-Z]/;
5320
5347
  const vueRequireComposablePrefix = createEslintRule({
5321
5348
  name: RULE_NAME,
5322
5349
  meta: {
@@ -5340,7 +5367,7 @@ const vueRequireComposablePrefix = createEslintRule({
5340
5367
  const candidateFunctions = /* @__PURE__ */ new Map();
5341
5368
  const { hasReactivityInStatement, hasReactivityInExpression } = createReactivityChecker(vueImports, nonVueImports);
5342
5369
  function isExcludedName(name) {
5343
- return /^define[A-Z]/.test(name) || /^create[A-Z]/.test(name) || name === "setup";
5370
+ return DEFINE_RE.test(name) || CREATE_RE.test(name) || name === "setup";
5344
5371
  }
5345
5372
  function checkFunctionForReactivity(functionNode, idNode, functionName) {
5346
5373
  if (!functionNode.body)
@@ -5531,6 +5558,7 @@ plugin.configs["prompt:skill"] = [
5531
5558
  }
5532
5559
  }
5533
5560
  ];
5561
+ const CODE_IGNORES = [".claude/**", ".cursor/**", ".github/copilot-instructions.md"];
5534
5562
  const deslopRules = {
5535
5563
  "harlanzw/ai-deslop-buzzwords": "error",
5536
5564
  "harlanzw/ai-deslop-casing": "error",
@@ -5546,6 +5574,7 @@ plugin.configs.content = [
5546
5574
  {
5547
5575
  name: "harlanzw/content",
5548
5576
  files: CONTENT_FILES,
5577
+ ignores: CODE_IGNORES,
5549
5578
  language: "harlanzw/prompt",
5550
5579
  plugins: { harlanzw: plugin },
5551
5580
  rules: deslopRules
@@ -5565,6 +5594,7 @@ plugin.configs.link = [
5565
5594
  {
5566
5595
  name: "harlanzw/link",
5567
5596
  files: LINK_FILES,
5597
+ ignores: CODE_IGNORES,
5568
5598
  plugins: { harlanzw: plugin },
5569
5599
  rules: {
5570
5600
  "harlanzw/link-ascii-only": "warn",
@@ -5583,6 +5613,7 @@ plugin.configs.nuxt = [
5583
5613
  {
5584
5614
  name: "harlanzw/nuxt",
5585
5615
  files: NUXT_VUE_FILES,
5616
+ ignores: CODE_IGNORES,
5586
5617
  plugins: { harlanzw: plugin },
5587
5618
  rules: {
5588
5619
  "harlanzw/nuxt-await-navigate-to": "error",
@@ -5600,6 +5631,7 @@ plugin.configs.vue = [
5600
5631
  {
5601
5632
  name: "harlanzw/vue",
5602
5633
  files: NUXT_VUE_FILES,
5634
+ ignores: CODE_IGNORES,
5603
5635
  plugins: { harlanzw: plugin },
5604
5636
  rules: {
5605
5637
  "harlanzw/vue-no-faux-composables": "error",
@@ -5650,6 +5682,7 @@ function harlanzw(options = {}, ...extraConfigs) {
5650
5682
  configs.push({
5651
5683
  name: "harlanzw/link",
5652
5684
  files: LINK_FILES,
5685
+ ignores: CODE_IGNORES,
5653
5686
  plugins: { harlanzw: plugin },
5654
5687
  rules: buildLinkRules(linkOpts)
5655
5688
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "eslint-plugin-harlanzw",
3
3
  "type": "module",
4
- "version": "0.5.2",
4
+ "version": "0.5.4",
5
5
  "description": "Harlan's opinionated ESLint rules",
6
6
  "author": "Harlan Wilton <harlan@harlanzw.com>",
7
7
  "license": "MIT",