eslint-plugin-harlanzw 0.14.2 → 0.15.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.
package/README.md CHANGED
@@ -50,6 +50,7 @@ The rules are organized into the following categories:
50
50
  | [`nuxt-no-redundant-import-meta`](./src/rules/nuxt-no-redundant-import-meta.md) | disallow redundant `import.meta.server` or `import.meta.client` checks in scoped components |
51
51
  | [`nuxt-no-side-effects-in-async-data-handler`](./src/rules/nuxt-no-side-effects-in-async-data-handler.md) | disallow side effects in async data handlers |
52
52
  | [`nuxt-no-side-effects-in-setup`](./src/rules/nuxt-no-side-effects-in-setup.md) | disallow side effects in setup functions |
53
+ | [`nuxt-prefer-layer-alias`](./src/rules/nuxt-prefer-layer-alias.ts) | prefer `#layers/<name>` alias over `~~/layers/<name>` paths |
53
54
  | [`nuxt-prefer-navigate-to-over-router-push-replace`](./src/rules/nuxt-prefer-navigate-to-over-router-push-replace.md) | prefer `navigateTo()` over `router.push()` or `router.replace()` |
54
55
  | [`nuxt-prefer-nuxt-link-over-router-link`](./src/rules/nuxt-prefer-nuxt-link-over-router-link.md) | prefer `NuxtLink` over `RouterLink` |
55
56
  | [`nuxt-ui-prefer-shorthand-css`](./src/rules/nuxt-ui-prefer-shorthand-css.ts) | prefer Nuxt UI shorthand CSS classes over verbose `var()` syntax |
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.14.2";
7
+ const version = "0.15.0";
8
8
 
9
9
  const STRENGTH_PATTERNS = {
10
10
  strong: ["never", "must", "always", "under no circumstances", "absolutely", "required", "mandatory", "forbidden", "prohibited"],
@@ -3793,9 +3793,9 @@ function createReactivityChecker(vueImports, nonVueImports, options = {}) {
3793
3793
 
3794
3794
  const REGEX_2$4 = /[^\u0020-\u007F]/;
3795
3795
  const REGEX_1$6 = /[^\u0020-\u007F]/;
3796
- const RULE_NAME$t = "link-ascii-only";
3796
+ const RULE_NAME$u = "link-ascii-only";
3797
3797
  const linkAsciiOnly = createEslintRule({
3798
- name: RULE_NAME$t,
3798
+ name: RULE_NAME$u,
3799
3799
  meta: {
3800
3800
  type: "suggestion",
3801
3801
  docs: {
@@ -3871,9 +3871,9 @@ const REGEX_4$1 = /[A-Z]/;
3871
3871
  const REGEX_3$2 = /%[0-9A-F]{2}/i;
3872
3872
  const REGEX_2$3 = /[A-Z]/;
3873
3873
  const REGEX_1$5 = /%[0-9A-F]{2}/i;
3874
- const RULE_NAME$s = "link-lowercase";
3874
+ const RULE_NAME$t = "link-lowercase";
3875
3875
  const linkLowercase = createEslintRule({
3876
- name: RULE_NAME$s,
3876
+ name: RULE_NAME$t,
3877
3877
  meta: {
3878
3878
  type: "suggestion",
3879
3879
  docs: {
@@ -3952,7 +3952,7 @@ const linkLowercase = createEslintRule({
3952
3952
  const REGEX_3$1 = /\/+/g;
3953
3953
  const REGEX_2$2 = /\/{2,}/;
3954
3954
  const REGEX_1$4 = /\/{2,}/;
3955
- const RULE_NAME$r = "link-no-double-slashes";
3955
+ const RULE_NAME$s = "link-no-double-slashes";
3956
3956
  function fixDoubleSlashesInUrl(url) {
3957
3957
  if (url.startsWith("//") || url.includes("://"))
3958
3958
  return url;
@@ -3972,7 +3972,7 @@ function fixDoubleSlashesInUrl(url) {
3972
3972
  return `${path.replace(REGEX_3$1, "/")}${search}${hash}`;
3973
3973
  }
3974
3974
  const linkNoDoubleSlashes = createEslintRule({
3975
- name: RULE_NAME$r,
3975
+ name: RULE_NAME$s,
3976
3976
  meta: {
3977
3977
  type: "problem",
3978
3978
  docs: {
@@ -4048,9 +4048,9 @@ const linkNoDoubleSlashes = createEslintRule({
4048
4048
  }
4049
4049
  });
4050
4050
 
4051
- const RULE_NAME$q = "link-no-underscores";
4051
+ const RULE_NAME$r = "link-no-underscores";
4052
4052
  const linkNoUnderscores = createEslintRule({
4053
- name: RULE_NAME$q,
4053
+ name: RULE_NAME$r,
4054
4054
  meta: {
4055
4055
  type: "suggestion",
4056
4056
  docs: {
@@ -4128,9 +4128,9 @@ const REGEX_4 = /\s/;
4128
4128
  const REGEX_3 = /\s/g;
4129
4129
  const REGEX_2$1 = /\s/;
4130
4130
  const REGEX_1$3 = /\s/g;
4131
- const RULE_NAME$p = "link-no-whitespace";
4131
+ const RULE_NAME$q = "link-no-whitespace";
4132
4132
  const linkNoWhitespace = createEslintRule({
4133
- name: RULE_NAME$p,
4133
+ name: RULE_NAME$q,
4134
4134
  meta: {
4135
4135
  type: "suggestion",
4136
4136
  docs: {
@@ -4204,7 +4204,7 @@ const linkNoWhitespace = createEslintRule({
4204
4204
  }
4205
4205
  });
4206
4206
 
4207
- const RULE_NAME$o = "link-require-descriptive-text";
4207
+ const RULE_NAME$p = "link-require-descriptive-text";
4208
4208
  const BAD_LINK_TEXTS = /* @__PURE__ */ new Set([
4209
4209
  "click here",
4210
4210
  "click this",
@@ -4250,7 +4250,7 @@ function getVueLinkUrl(node) {
4250
4250
  return null;
4251
4251
  }
4252
4252
  const linkRequireDescriptiveText = createEslintRule({
4253
- name: RULE_NAME$o,
4253
+ name: RULE_NAME$p,
4254
4254
  meta: {
4255
4255
  type: "suggestion",
4256
4256
  docs: {
@@ -4326,9 +4326,9 @@ const linkRequireDescriptiveText = createEslintRule({
4326
4326
  }
4327
4327
  });
4328
4328
 
4329
- const RULE_NAME$n = "link-require-href";
4329
+ const RULE_NAME$o = "link-require-href";
4330
4330
  const linkRequireHref = createEslintRule({
4331
- name: RULE_NAME$n,
4331
+ name: RULE_NAME$o,
4332
4332
  meta: {
4333
4333
  type: "problem",
4334
4334
  docs: {
@@ -4396,12 +4396,12 @@ const linkRequireHref = createEslintRule({
4396
4396
  }
4397
4397
  });
4398
4398
 
4399
- const RULE_NAME$m = "link-trailing-slash";
4399
+ const RULE_NAME$n = "link-trailing-slash";
4400
4400
  function shouldSkipUrl(url) {
4401
4401
  return url.startsWith("#") || url.includes(":") || url === "/" || url === "";
4402
4402
  }
4403
4403
  const linkTrailingSlash = createEslintRule({
4404
- name: RULE_NAME$m,
4404
+ name: RULE_NAME$n,
4405
4405
  meta: {
4406
4406
  type: "suggestion",
4407
4407
  docs: {
@@ -4516,7 +4516,7 @@ const linkTrailingSlash = createEslintRule({
4516
4516
  }
4517
4517
  });
4518
4518
 
4519
- const RULE_NAME$l = "no-silent-catch";
4519
+ const RULE_NAME$m = "no-silent-catch";
4520
4520
  function isEmptyOrVoid(node) {
4521
4521
  if (node.type === "ArrowFunctionExpression") {
4522
4522
  if (node.body.type === "BlockStatement" && node.body.body.length === 0)
@@ -4536,7 +4536,7 @@ function hasOnlyComments(node, sourceCode) {
4536
4536
  return node.body.length === 0 && sourceCode.getCommentsInside(node).length > 0;
4537
4537
  }
4538
4538
  const noSilentCatch = createEslintRule({
4539
- name: RULE_NAME$l,
4539
+ name: RULE_NAME$m,
4540
4540
  meta: {
4541
4541
  type: "problem",
4542
4542
  docs: {
@@ -4574,9 +4574,9 @@ const noSilentCatch = createEslintRule({
4574
4574
  }
4575
4575
  });
4576
4576
 
4577
- const RULE_NAME$k = "nuxt-await-navigate-to";
4577
+ const RULE_NAME$l = "nuxt-await-navigate-to";
4578
4578
  const nuxtAwaitNavigateTo = createEslintRule({
4579
- name: RULE_NAME$k,
4579
+ name: RULE_NAME$l,
4580
4580
  meta: {
4581
4581
  type: "problem",
4582
4582
  docs: {
@@ -4732,7 +4732,7 @@ function isInConsequentBranch(node, ternary) {
4732
4732
  return false;
4733
4733
  }
4734
4734
 
4735
- const RULE_NAME$j = "nuxt-no-random";
4735
+ const RULE_NAME$k = "nuxt-no-random";
4736
4736
  function isMathRandomCall(node) {
4737
4737
  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";
4738
4738
  }
@@ -4763,7 +4763,7 @@ function reportRandom(context, node, template) {
4763
4763
  }
4764
4764
  }
4765
4765
  const nuxtNoRandom = createEslintRule({
4766
- name: RULE_NAME$j,
4766
+ name: RULE_NAME$k,
4767
4767
  meta: {
4768
4768
  type: "problem",
4769
4769
  docs: {
@@ -4802,9 +4802,9 @@ const nuxtNoRandom = createEslintRule({
4802
4802
  }
4803
4803
  });
4804
4804
 
4805
- const RULE_NAME$i = "nuxt-no-redundant-import-meta";
4805
+ const RULE_NAME$j = "nuxt-no-redundant-import-meta";
4806
4806
  const nuxtNoRedundantImportMeta = createEslintRule({
4807
- name: RULE_NAME$i,
4807
+ name: RULE_NAME$j,
4808
4808
  meta: {
4809
4809
  type: "problem",
4810
4810
  docs: {
@@ -4842,7 +4842,7 @@ const nuxtNoRedundantImportMeta = createEslintRule({
4842
4842
  });
4843
4843
 
4844
4844
  const REGEX_1$2 = /\n\s*\n\s*\n/g;
4845
- const RULE_NAME$h = "nuxt-no-side-effects-in-async-data-handler";
4845
+ const RULE_NAME$i = "nuxt-no-side-effects-in-async-data-handler";
4846
4846
  const SIDE_EFFECT_PATTERNS = /* @__PURE__ */ new Set([
4847
4847
  // Store/State mutations ($ prefix makes these unambiguous)
4848
4848
  "$patch",
@@ -4952,7 +4952,7 @@ function findSideEffectsInFunction(functionNode) {
4952
4952
  return sideEffects;
4953
4953
  }
4954
4954
  const nuxtNoSideEffectsInAsyncDataHandler = createEslintRule({
4955
- name: RULE_NAME$h,
4955
+ name: RULE_NAME$i,
4956
4956
  meta: {
4957
4957
  type: "problem",
4958
4958
  docs: {
@@ -5043,9 +5043,9 @@ ${indent}${callOnceBlock}`)
5043
5043
  }
5044
5044
  });
5045
5045
 
5046
- const RULE_NAME$g = "nuxt-no-side-effects-in-setup";
5046
+ const RULE_NAME$h = "nuxt-no-side-effects-in-setup";
5047
5047
  const nuxtNoSideEffectsInSetup = createEslintRule({
5048
- name: RULE_NAME$g,
5048
+ name: RULE_NAME$h,
5049
5049
  meta: {
5050
5050
  type: "problem",
5051
5051
  docs: {
@@ -5136,7 +5136,7 @@ ${indent}})`;
5136
5136
  }
5137
5137
  });
5138
5138
 
5139
- const RULE_NAME$f = "nuxt-no-unsafe-date";
5139
+ const RULE_NAME$g = "nuxt-no-unsafe-date";
5140
5140
  function isDateNowCall(node) {
5141
5141
  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";
5142
5142
  }
@@ -5167,7 +5167,7 @@ function isChainedWithStableMethod(node) {
5167
5167
  return grandparent?.type === "CallExpression" && grandparent.callee === parent;
5168
5168
  }
5169
5169
  const nuxtNoUnsafeDate = createEslintRule({
5170
- name: RULE_NAME$f,
5170
+ name: RULE_NAME$g,
5171
5171
  meta: {
5172
5172
  type: "problem",
5173
5173
  docs: {
@@ -5231,6 +5231,62 @@ const nuxtNoUnsafeDate = createEslintRule({
5231
5231
  }
5232
5232
  });
5233
5233
 
5234
+ const RULE_NAME$f = "nuxt-prefer-layer-alias";
5235
+ const LAYER_PATH_RE = /^(?:~~|@@|~|@)\/layers\/([^/]+)\/(.+)$/;
5236
+ const nuxtPreferLayerAlias = createEslintRule({
5237
+ name: RULE_NAME$f,
5238
+ meta: {
5239
+ type: "suggestion",
5240
+ docs: {
5241
+ description: "prefer Nuxt #layers/<name> alias over ~~/layers/<name> paths"
5242
+ },
5243
+ fixable: "code",
5244
+ schema: [],
5245
+ messages: {
5246
+ preferLayerAlias: "Prefer Nuxt layer alias '{{replacement}}' over '{{original}}'"
5247
+ }
5248
+ },
5249
+ defaultOptions: [],
5250
+ create: (context) => {
5251
+ function check(node, valueNode) {
5252
+ if (!valueNode || valueNode.type !== "Literal" || typeof valueNode.value !== "string")
5253
+ return;
5254
+ const original = valueNode.value;
5255
+ const match = original.match(LAYER_PATH_RE);
5256
+ if (!match)
5257
+ return;
5258
+ const [, layer, rest] = match;
5259
+ const replacement = `#layers/${layer}/${rest}`;
5260
+ context.report({
5261
+ node: valueNode,
5262
+ messageId: "preferLayerAlias",
5263
+ data: { original, replacement },
5264
+ fix(fixer) {
5265
+ const raw = valueNode.raw ?? `'${original}'`;
5266
+ const quote = raw[0] === '"' ? '"' : "'";
5267
+ return fixer.replaceText(valueNode, `${quote}${replacement}${quote}`);
5268
+ }
5269
+ });
5270
+ }
5271
+ return {
5272
+ ImportDeclaration(node) {
5273
+ check(node, node.source);
5274
+ },
5275
+ ExportNamedDeclaration(node) {
5276
+ if (node.source)
5277
+ check(node, node.source);
5278
+ },
5279
+ ExportAllDeclaration(node) {
5280
+ if (node.source)
5281
+ check(node, node.source);
5282
+ },
5283
+ ImportExpression(node) {
5284
+ check(node, node.source);
5285
+ }
5286
+ };
5287
+ }
5288
+ });
5289
+
5234
5290
  const RULE_NAME$e = "nuxt-prefer-navigate-to-over-router-push-replace";
5235
5291
  const nuxtPreferNavigateToOverRouterPushReplace = createEslintRule({
5236
5292
  name: RULE_NAME$e,
@@ -6867,6 +6923,7 @@ const plugin = {
6867
6923
  "nuxt-no-side-effects-in-async-data-handler": nuxtNoSideEffectsInAsyncDataHandler,
6868
6924
  "nuxt-no-side-effects-in-setup": nuxtNoSideEffectsInSetup,
6869
6925
  "nuxt-no-unsafe-date": nuxtNoUnsafeDate,
6926
+ "nuxt-prefer-layer-alias": nuxtPreferLayerAlias,
6870
6927
  "nuxt-prefer-navigate-to-over-router-push-replace": nuxtPreferNavigateToOverRouterPushReplace,
6871
6928
  "nuxt-prefer-nuxt-link-over-router-link": nuxtPreferNuxtLinkOverRouterLink,
6872
6929
  "nuxt-ui-prefer-shorthand-css": nuxtUiPreferShorthandCss,
@@ -7043,6 +7100,7 @@ plugin.configs.nuxt = [
7043
7100
  "harlanzw/nuxt-no-side-effects-in-async-data-handler": "error",
7044
7101
  "harlanzw/nuxt-no-side-effects-in-setup": "error",
7045
7102
  "harlanzw/nuxt-no-unsafe-date": "warn",
7103
+ "harlanzw/nuxt-prefer-layer-alias": "warn",
7046
7104
  "harlanzw/nuxt-prefer-navigate-to-over-router-push-replace": "warn",
7047
7105
  "harlanzw/nuxt-prefer-nuxt-link-over-router-link": "warn",
7048
7106
  "harlanzw/nuxt-ui-prefer-shorthand-css": "warn",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "eslint-plugin-harlanzw",
3
3
  "type": "module",
4
- "version": "0.14.2",
4
+ "version": "0.15.0",
5
5
  "description": "Harlan's opinionated ESLint rules",
6
6
  "author": "Harlan Wilton <harlan@harlanzw.com>",
7
7
  "license": "MIT",