eslint-plugin-harlanzw 0.12.3 → 0.14.0

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 +225 -41
  2. package/package.json +3 -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.12.3";
7
+ const version = "0.14.0";
8
8
 
9
9
  const STRENGTH_PATTERNS = {
10
10
  strong: ["never", "must", "always", "under no circumstances", "absolutely", "required", "mandatory", "forbidden", "prohibited"],
@@ -3780,9 +3780,9 @@ function createReactivityChecker(vueImports, nonVueImports) {
3780
3780
 
3781
3781
  const REGEX_2$4 = /[^\u0020-\u007F]/;
3782
3782
  const REGEX_1$6 = /[^\u0020-\u007F]/;
3783
- const RULE_NAME$s = "link-ascii-only";
3783
+ const RULE_NAME$t = "link-ascii-only";
3784
3784
  const linkAsciiOnly = createEslintRule({
3785
- name: RULE_NAME$s,
3785
+ name: RULE_NAME$t,
3786
3786
  meta: {
3787
3787
  type: "suggestion",
3788
3788
  docs: {
@@ -3858,9 +3858,9 @@ const REGEX_4$1 = /[A-Z]/;
3858
3858
  const REGEX_3$2 = /%[0-9A-F]{2}/i;
3859
3859
  const REGEX_2$3 = /[A-Z]/;
3860
3860
  const REGEX_1$5 = /%[0-9A-F]{2}/i;
3861
- const RULE_NAME$r = "link-lowercase";
3861
+ const RULE_NAME$s = "link-lowercase";
3862
3862
  const linkLowercase = createEslintRule({
3863
- name: RULE_NAME$r,
3863
+ name: RULE_NAME$s,
3864
3864
  meta: {
3865
3865
  type: "suggestion",
3866
3866
  docs: {
@@ -3939,7 +3939,7 @@ const linkLowercase = createEslintRule({
3939
3939
  const REGEX_3$1 = /\/+/g;
3940
3940
  const REGEX_2$2 = /\/{2,}/;
3941
3941
  const REGEX_1$4 = /\/{2,}/;
3942
- const RULE_NAME$q = "link-no-double-slashes";
3942
+ const RULE_NAME$r = "link-no-double-slashes";
3943
3943
  function fixDoubleSlashesInUrl(url) {
3944
3944
  if (url.startsWith("//") || url.includes("://"))
3945
3945
  return url;
@@ -3959,7 +3959,7 @@ function fixDoubleSlashesInUrl(url) {
3959
3959
  return `${path.replace(REGEX_3$1, "/")}${search}${hash}`;
3960
3960
  }
3961
3961
  const linkNoDoubleSlashes = createEslintRule({
3962
- name: RULE_NAME$q,
3962
+ name: RULE_NAME$r,
3963
3963
  meta: {
3964
3964
  type: "problem",
3965
3965
  docs: {
@@ -4035,9 +4035,9 @@ const linkNoDoubleSlashes = createEslintRule({
4035
4035
  }
4036
4036
  });
4037
4037
 
4038
- const RULE_NAME$p = "link-no-underscores";
4038
+ const RULE_NAME$q = "link-no-underscores";
4039
4039
  const linkNoUnderscores = createEslintRule({
4040
- name: RULE_NAME$p,
4040
+ name: RULE_NAME$q,
4041
4041
  meta: {
4042
4042
  type: "suggestion",
4043
4043
  docs: {
@@ -4115,9 +4115,9 @@ const REGEX_4 = /\s/;
4115
4115
  const REGEX_3 = /\s/g;
4116
4116
  const REGEX_2$1 = /\s/;
4117
4117
  const REGEX_1$3 = /\s/g;
4118
- const RULE_NAME$o = "link-no-whitespace";
4118
+ const RULE_NAME$p = "link-no-whitespace";
4119
4119
  const linkNoWhitespace = createEslintRule({
4120
- name: RULE_NAME$o,
4120
+ name: RULE_NAME$p,
4121
4121
  meta: {
4122
4122
  type: "suggestion",
4123
4123
  docs: {
@@ -4191,7 +4191,7 @@ const linkNoWhitespace = createEslintRule({
4191
4191
  }
4192
4192
  });
4193
4193
 
4194
- const RULE_NAME$n = "link-require-descriptive-text";
4194
+ const RULE_NAME$o = "link-require-descriptive-text";
4195
4195
  const BAD_LINK_TEXTS = /* @__PURE__ */ new Set([
4196
4196
  "click here",
4197
4197
  "click this",
@@ -4237,7 +4237,7 @@ function getVueLinkUrl(node) {
4237
4237
  return null;
4238
4238
  }
4239
4239
  const linkRequireDescriptiveText = createEslintRule({
4240
- name: RULE_NAME$n,
4240
+ name: RULE_NAME$o,
4241
4241
  meta: {
4242
4242
  type: "suggestion",
4243
4243
  docs: {
@@ -4313,9 +4313,9 @@ const linkRequireDescriptiveText = createEslintRule({
4313
4313
  }
4314
4314
  });
4315
4315
 
4316
- const RULE_NAME$m = "link-require-href";
4316
+ const RULE_NAME$n = "link-require-href";
4317
4317
  const linkRequireHref = createEslintRule({
4318
- name: RULE_NAME$m,
4318
+ name: RULE_NAME$n,
4319
4319
  meta: {
4320
4320
  type: "problem",
4321
4321
  docs: {
@@ -4383,12 +4383,12 @@ const linkRequireHref = createEslintRule({
4383
4383
  }
4384
4384
  });
4385
4385
 
4386
- const RULE_NAME$l = "link-trailing-slash";
4386
+ const RULE_NAME$m = "link-trailing-slash";
4387
4387
  function shouldSkipUrl(url) {
4388
4388
  return url.startsWith("#") || url.includes(":") || url === "/" || url === "";
4389
4389
  }
4390
4390
  const linkTrailingSlash = createEslintRule({
4391
- name: RULE_NAME$l,
4391
+ name: RULE_NAME$m,
4392
4392
  meta: {
4393
4393
  type: "suggestion",
4394
4394
  docs: {
@@ -4503,7 +4503,7 @@ const linkTrailingSlash = createEslintRule({
4503
4503
  }
4504
4504
  });
4505
4505
 
4506
- const RULE_NAME$k = "no-silent-catch";
4506
+ const RULE_NAME$l = "no-silent-catch";
4507
4507
  function isEmptyOrVoid(node) {
4508
4508
  if (node.type === "ArrowFunctionExpression") {
4509
4509
  if (node.body.type === "BlockStatement" && node.body.body.length === 0)
@@ -4523,7 +4523,7 @@ function hasOnlyComments(node, sourceCode) {
4523
4523
  return node.body.length === 0 && sourceCode.getCommentsInside(node).length > 0;
4524
4524
  }
4525
4525
  const noSilentCatch = createEslintRule({
4526
- name: RULE_NAME$k,
4526
+ name: RULE_NAME$l,
4527
4527
  meta: {
4528
4528
  type: "problem",
4529
4529
  docs: {
@@ -4561,9 +4561,9 @@ const noSilentCatch = createEslintRule({
4561
4561
  }
4562
4562
  });
4563
4563
 
4564
- const RULE_NAME$j = "nuxt-await-navigate-to";
4564
+ const RULE_NAME$k = "nuxt-await-navigate-to";
4565
4565
  const nuxtAwaitNavigateTo = createEslintRule({
4566
- name: RULE_NAME$j,
4566
+ name: RULE_NAME$k,
4567
4567
  meta: {
4568
4568
  type: "problem",
4569
4569
  docs: {
@@ -4719,7 +4719,7 @@ function isInConsequentBranch(node, ternary) {
4719
4719
  return false;
4720
4720
  }
4721
4721
 
4722
- const RULE_NAME$i = "nuxt-no-random";
4722
+ const RULE_NAME$j = "nuxt-no-random";
4723
4723
  function isMathRandomCall(node) {
4724
4724
  return node.callee.type === "MemberExpression" && node.callee.object.type === "Identifier" && node.callee.object.name === "Math" && node.callee.property.type === "Identifier" && node.callee.property.name === "random";
4725
4725
  }
@@ -4750,7 +4750,7 @@ function reportRandom(context, node, template) {
4750
4750
  }
4751
4751
  }
4752
4752
  const nuxtNoRandom = createEslintRule({
4753
- name: RULE_NAME$i,
4753
+ name: RULE_NAME$j,
4754
4754
  meta: {
4755
4755
  type: "problem",
4756
4756
  docs: {
@@ -4789,9 +4789,9 @@ const nuxtNoRandom = createEslintRule({
4789
4789
  }
4790
4790
  });
4791
4791
 
4792
- const RULE_NAME$h = "nuxt-no-redundant-import-meta";
4792
+ const RULE_NAME$i = "nuxt-no-redundant-import-meta";
4793
4793
  const nuxtNoRedundantImportMeta = createEslintRule({
4794
- name: RULE_NAME$h,
4794
+ name: RULE_NAME$i,
4795
4795
  meta: {
4796
4796
  type: "problem",
4797
4797
  docs: {
@@ -4829,7 +4829,7 @@ const nuxtNoRedundantImportMeta = createEslintRule({
4829
4829
  });
4830
4830
 
4831
4831
  const REGEX_1$2 = /\n\s*\n\s*\n/g;
4832
- const RULE_NAME$g = "nuxt-no-side-effects-in-async-data-handler";
4832
+ const RULE_NAME$h = "nuxt-no-side-effects-in-async-data-handler";
4833
4833
  const SIDE_EFFECT_PATTERNS = /* @__PURE__ */ new Set([
4834
4834
  // Store/State mutations ($ prefix makes these unambiguous)
4835
4835
  "$patch",
@@ -4939,7 +4939,7 @@ function findSideEffectsInFunction(functionNode) {
4939
4939
  return sideEffects;
4940
4940
  }
4941
4941
  const nuxtNoSideEffectsInAsyncDataHandler = createEslintRule({
4942
- name: RULE_NAME$g,
4942
+ name: RULE_NAME$h,
4943
4943
  meta: {
4944
4944
  type: "problem",
4945
4945
  docs: {
@@ -5030,9 +5030,9 @@ ${indent}${callOnceBlock}`)
5030
5030
  }
5031
5031
  });
5032
5032
 
5033
- const RULE_NAME$f = "nuxt-no-side-effects-in-setup";
5033
+ const RULE_NAME$g = "nuxt-no-side-effects-in-setup";
5034
5034
  const nuxtNoSideEffectsInSetup = createEslintRule({
5035
- name: RULE_NAME$f,
5035
+ name: RULE_NAME$g,
5036
5036
  meta: {
5037
5037
  type: "problem",
5038
5038
  docs: {
@@ -5123,7 +5123,7 @@ ${indent}})`;
5123
5123
  }
5124
5124
  });
5125
5125
 
5126
- const RULE_NAME$e = "nuxt-no-unsafe-date";
5126
+ const RULE_NAME$f = "nuxt-no-unsafe-date";
5127
5127
  function isDateNowCall(node) {
5128
5128
  return node.callee.type === "MemberExpression" && node.callee.object.type === "Identifier" && node.callee.object.name === "Date" && node.callee.property.type === "Identifier" && node.callee.property.name === "now";
5129
5129
  }
@@ -5154,7 +5154,7 @@ function isChainedWithStableMethod(node) {
5154
5154
  return grandparent?.type === "CallExpression" && grandparent.callee === parent;
5155
5155
  }
5156
5156
  const nuxtNoUnsafeDate = createEslintRule({
5157
- name: RULE_NAME$e,
5157
+ name: RULE_NAME$f,
5158
5158
  meta: {
5159
5159
  type: "problem",
5160
5160
  docs: {
@@ -5218,9 +5218,9 @@ const nuxtNoUnsafeDate = createEslintRule({
5218
5218
  }
5219
5219
  });
5220
5220
 
5221
- const RULE_NAME$d = "nuxt-prefer-navigate-to-over-router-push-replace";
5221
+ const RULE_NAME$e = "nuxt-prefer-navigate-to-over-router-push-replace";
5222
5222
  const nuxtPreferNavigateToOverRouterPushReplace = createEslintRule({
5223
- name: RULE_NAME$d,
5223
+ name: RULE_NAME$e,
5224
5224
  meta: {
5225
5225
  type: "suggestion",
5226
5226
  docs: {
@@ -5285,9 +5285,9 @@ const nuxtPreferNavigateToOverRouterPushReplace = createEslintRule({
5285
5285
 
5286
5286
  const REGEX_2 = /router-link|RouterLink/gi;
5287
5287
  const REGEX_1$1 = /router-link|RouterLink/gi;
5288
- const RULE_NAME$c = "nuxt-prefer-nuxt-link-over-router-link";
5288
+ const RULE_NAME$d = "nuxt-prefer-nuxt-link-over-router-link";
5289
5289
  const nuxtPreferNuxtLinkOverRouterLink = createEslintRule({
5290
- name: RULE_NAME$c,
5290
+ name: RULE_NAME$d,
5291
5291
  meta: {
5292
5292
  type: "suggestion",
5293
5293
  docs: {
@@ -5358,7 +5358,7 @@ const nuxtPreferNuxtLinkOverRouterLink = createEslintRule({
5358
5358
  }
5359
5359
  });
5360
5360
 
5361
- const RULE_NAME$b = "nuxt-ui-prefer-shorthand-css";
5361
+ const RULE_NAME$c = "nuxt-ui-prefer-shorthand-css";
5362
5362
  const REPLACEMENTS = {
5363
5363
  // text
5364
5364
  "text-[var(--ui-text)]": "text-default",
@@ -5415,7 +5415,7 @@ function findVerboseClasses(value) {
5415
5415
  return results;
5416
5416
  }
5417
5417
  const nuxtUiPreferShorthandCss = createEslintRule({
5418
- name: RULE_NAME$b,
5418
+ name: RULE_NAME$c,
5419
5419
  meta: {
5420
5420
  type: "suggestion",
5421
5421
  docs: {
@@ -5564,6 +5564,189 @@ const nuxtUiPreferShorthandCss = createEslintRule({
5564
5564
  }
5565
5565
  });
5566
5566
 
5567
+ const RULE_NAME$b = "prefer-node-style-text";
5568
+ const ESC_ALT = String.raw`(?:\\x1[bB]|\\u001[bB]|\\033|\x1B)`;
5569
+ const ANSI_ANYWHERE_RE = new RegExp(`${ESC_ALT}\\[[\\d;]*m`);
5570
+ const FULL_WRAP_RE = new RegExp(`^${ESC_ALT}\\[([\\d;]+)m([\\s\\S]*?)${ESC_ALT}\\[0m$`);
5571
+ const ANY_ANSI_RE = new RegExp(`${ESC_ALT}\\[`);
5572
+ const SGR_NAMES = {
5573
+ 0: "reset",
5574
+ 1: "bold",
5575
+ 2: "dim",
5576
+ 3: "italic",
5577
+ 4: "underline",
5578
+ 7: "inverse",
5579
+ 8: "hidden",
5580
+ 9: "strikethrough",
5581
+ 30: "black",
5582
+ 31: "red",
5583
+ 32: "green",
5584
+ 33: "yellow",
5585
+ 34: "blue",
5586
+ 35: "magenta",
5587
+ 36: "cyan",
5588
+ 37: "white",
5589
+ 90: "gray",
5590
+ 91: "redBright",
5591
+ 92: "greenBright",
5592
+ 93: "yellowBright",
5593
+ 94: "blueBright",
5594
+ 95: "magentaBright",
5595
+ 96: "cyanBright",
5596
+ 97: "whiteBright",
5597
+ 40: "bgBlack",
5598
+ 41: "bgRed",
5599
+ 42: "bgGreen",
5600
+ 43: "bgYellow",
5601
+ 44: "bgBlue",
5602
+ 45: "bgMagenta",
5603
+ 46: "bgCyan",
5604
+ 47: "bgWhite"
5605
+ };
5606
+ function paramsToNames(params) {
5607
+ const parts = params.split(";").filter(Boolean);
5608
+ const names = parts.map((p) => SGR_NAMES[p]);
5609
+ if (names.some((n) => !n || n === "reset"))
5610
+ return null;
5611
+ return names;
5612
+ }
5613
+ function describeCodes(raw) {
5614
+ const m = raw.match(new RegExp(`${ESC_ALT}\\[([\\d;]*)m`));
5615
+ if (!m)
5616
+ return "ANSI";
5617
+ const names = m[1].split(";").filter(Boolean).map((p) => SGR_NAMES[p] || p);
5618
+ return names.join(", ") || "ANSI";
5619
+ }
5620
+ function formatCodes(names) {
5621
+ if (names.length === 1)
5622
+ return `'${names[0]}'`;
5623
+ return `[${names.map((n) => `'${n}'`).join(", ")}]`;
5624
+ }
5625
+ const preferNodeStyleText = createEslintRule({
5626
+ name: RULE_NAME$b,
5627
+ meta: {
5628
+ type: "suggestion",
5629
+ fixable: "code",
5630
+ docs: {
5631
+ description: "prefer `styleText` from `node:util` over raw ANSI escape sequences"
5632
+ },
5633
+ schema: [],
5634
+ messages: {
5635
+ preferStyleText: "Avoid raw ANSI escape sequences ({{codes}}). Use `styleText` from `node:util` instead."
5636
+ }
5637
+ },
5638
+ defaultOptions: [],
5639
+ create: (context) => {
5640
+ const sourceCode = context.sourceCode ?? context.getSourceCode();
5641
+ let program = null;
5642
+ let nodeUtilImport = null;
5643
+ let hasStyleText = false;
5644
+ function ensureStyleTextImportFix(fixer) {
5645
+ const fixes = [];
5646
+ if (hasStyleText)
5647
+ return fixes;
5648
+ if (nodeUtilImport) {
5649
+ const specifiers = nodeUtilImport.specifiers.filter(
5650
+ (s) => s.type === "ImportSpecifier"
5651
+ );
5652
+ if (specifiers.length > 0) {
5653
+ const last = specifiers[specifiers.length - 1];
5654
+ fixes.push(fixer.insertTextAfter(last, ", styleText"));
5655
+ } else {
5656
+ const defaultSpec = nodeUtilImport.specifiers[0];
5657
+ if (defaultSpec)
5658
+ fixes.push(fixer.insertTextAfter(defaultSpec, ", { styleText }"));
5659
+ }
5660
+ } else if (program) {
5661
+ fixes.push(fixer.insertTextBefore(program.body[0], `import { styleText } from 'node:util'
5662
+ `));
5663
+ }
5664
+ return fixes;
5665
+ }
5666
+ function buildReplacement(node) {
5667
+ const text = sourceCode.getText(node);
5668
+ if (node.type === "Literal") {
5669
+ if (typeof node.value !== "string")
5670
+ return null;
5671
+ const quote = text[0];
5672
+ if (quote !== '"' && quote !== "'")
5673
+ return null;
5674
+ const inner2 = text.slice(1, -1);
5675
+ const m2 = inner2.match(FULL_WRAP_RE);
5676
+ if (!m2)
5677
+ return null;
5678
+ const names2 = paramsToNames(m2[1]);
5679
+ if (!names2)
5680
+ return null;
5681
+ const body2 = m2[2];
5682
+ if (ANY_ANSI_RE.test(body2))
5683
+ return null;
5684
+ return {
5685
+ codes: names2.join(", "),
5686
+ replacement: `styleText(${formatCodes(names2)}, ${quote}${body2}${quote})`
5687
+ };
5688
+ }
5689
+ if (node.parent?.type === "TaggedTemplateExpression")
5690
+ return null;
5691
+ const inner = text.slice(1, -1);
5692
+ const m = inner.match(FULL_WRAP_RE);
5693
+ if (!m)
5694
+ return null;
5695
+ const names = paramsToNames(m[1]);
5696
+ if (!names)
5697
+ return null;
5698
+ const body = m[2];
5699
+ if (ANY_ANSI_RE.test(body))
5700
+ return null;
5701
+ return {
5702
+ codes: names.join(", "),
5703
+ replacement: `styleText(${formatCodes(names)}, \`${body}\`)`
5704
+ };
5705
+ }
5706
+ function check(node, raw) {
5707
+ if (!ANSI_ANYWHERE_RE.test(raw))
5708
+ return;
5709
+ const codes = describeCodes(raw);
5710
+ const built = buildReplacement(node);
5711
+ context.report({
5712
+ node,
5713
+ messageId: "preferStyleText",
5714
+ data: { codes },
5715
+ fix: built ? (fixer) => {
5716
+ const fixes = [fixer.replaceText(node, built.replacement), ...ensureStyleTextImportFix(fixer)];
5717
+ return fixes;
5718
+ } : null
5719
+ });
5720
+ }
5721
+ return {
5722
+ Program(node) {
5723
+ program = node;
5724
+ for (const stmt of node.body) {
5725
+ if (stmt.type !== "ImportDeclaration")
5726
+ continue;
5727
+ if (stmt.source.value !== "node:util" && stmt.source.value !== "util")
5728
+ continue;
5729
+ nodeUtilImport = stmt;
5730
+ for (const spec of stmt.specifiers) {
5731
+ if (spec.type === "ImportSpecifier" && spec.imported.type === "Identifier" && spec.imported.name === "styleText") {
5732
+ hasStyleText = true;
5733
+ }
5734
+ }
5735
+ }
5736
+ },
5737
+ Literal(node) {
5738
+ if (typeof node.value !== "string")
5739
+ return;
5740
+ check(node, sourceCode.getText(node));
5741
+ },
5742
+ TemplateLiteral(node) {
5743
+ const text = sourceCode.getText(node);
5744
+ check(node, text);
5745
+ }
5746
+ };
5747
+ }
5748
+ });
5749
+
5567
5750
  const RULE_NAME$a = "vue-no-async-lifecycle-hook";
5568
5751
  const LIFECYCLE_HOOKS = /* @__PURE__ */ new Set([
5569
5752
  "onBeforeMount",
@@ -6404,8 +6587,6 @@ function hasUnresolvableType(node) {
6404
6587
  return node.typeArguments.params.some((t) => hasUnresolvableType(t));
6405
6588
  }
6406
6589
  return false;
6407
- case "TSParenthesizedType":
6408
- return hasUnresolvableType(node.typeAnnotation);
6409
6590
  default:
6410
6591
  return false;
6411
6592
  }
@@ -6672,6 +6853,7 @@ const plugin = {
6672
6853
  "nuxt-prefer-nuxt-link-over-router-link": nuxtPreferNuxtLinkOverRouterLink,
6673
6854
  "nuxt-ui-prefer-shorthand-css": nuxtUiPreferShorthandCss,
6674
6855
  "pnpm-require-trust-policy": pnpmRequireTrustPolicy,
6856
+ "prefer-node-style-text": preferNodeStyleText,
6675
6857
  "prompt-ambiguous-quantifier": promptAmbiguousQuantifier,
6676
6858
  "prompt-duplicate-heading": promptDuplicateHeading,
6677
6859
  "prompt-empty-section": promptEmptySection,
@@ -6845,7 +7027,8 @@ plugin.configs.nuxt = [
6845
7027
  "harlanzw/nuxt-no-unsafe-date": "warn",
6846
7028
  "harlanzw/nuxt-prefer-navigate-to-over-router-push-replace": "warn",
6847
7029
  "harlanzw/nuxt-prefer-nuxt-link-over-router-link": "warn",
6848
- "harlanzw/nuxt-ui-prefer-shorthand-css": "warn"
7030
+ "harlanzw/nuxt-ui-prefer-shorthand-css": "warn",
7031
+ "harlanzw/no-silent-catch": "error"
6849
7032
  }
6850
7033
  }
6851
7034
  ];
@@ -6867,7 +7050,8 @@ plugin.configs.vue = [
6867
7050
  "harlanzw/vue-no-torefs-on-props": "warn",
6868
7051
  "harlanzw/vue-no-unresolvable-define-emits": "error",
6869
7052
  "harlanzw/vue-prefer-define-emits-object-syntax": "warn",
6870
- "harlanzw/vue-require-composable-prefix": "warn"
7053
+ "harlanzw/vue-require-composable-prefix": "warn",
7054
+ "harlanzw/no-silent-catch": "error"
6871
7055
  }
6872
7056
  },
6873
7057
  {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "eslint-plugin-harlanzw",
3
3
  "type": "module",
4
- "version": "0.12.3",
4
+ "version": "0.14.0",
5
5
  "description": "Harlan's opinionated ESLint rules",
6
6
  "author": "Harlan Wilton <harlan@harlanzw.com>",
7
7
  "license": "MIT",
@@ -37,6 +37,7 @@
37
37
  "@antfu/utils": "^9.3.0",
38
38
  "@types/eslint": "^9.6.1",
39
39
  "@types/node": "^25.7.0",
40
+ "@typescript-eslint/parser": "^8.59.3",
40
41
  "@typescript-eslint/typescript-estree": "^8.59.3",
41
42
  "@typescript-eslint/utils": "^8.59.3",
42
43
  "bumpp": "^11.1.0",
@@ -50,6 +51,7 @@
50
51
  "vite": "^8.0.12",
51
52
  "vitest": "^4.1.6",
52
53
  "vue": "^3.5.34",
54
+ "vue-eslint-parser": "^10.4.0",
53
55
  "yaml-eslint-parser": "^2.0.0"
54
56
  },
55
57
  "resolutions": {