eslint-plugin-harlanzw 0.14.0 → 0.14.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/README.md CHANGED
@@ -76,10 +76,12 @@ The rules are organized into the following categories:
76
76
  | [`ai-deslop-false-dichotomy`](./src/prompt/rules/deslop-false-dichotomy.ts) | flag "it's not X, it's Y" contrast patterns common in AI writing |
77
77
  | [`ai-deslop-filler`](./src/prompt/rules/deslop-filler.ts) | remove AI-generated filler sentences and phrases (e.g. "it's worth noting that") |
78
78
  | [`ai-deslop-hedging`](./src/prompt/rules/deslop-hedging.ts) | remove hedging/qualifying words that weaken copy (e.g. "very", "really", "quite", "just") |
79
+ | [`ai-deslop-no-em-dash`](./src/prompt/rules/deslop-no-em-dash.ts) | replace em dashes in content prose |
79
80
  | [`ai-deslop-no-exclamation`](./src/prompt/rules/deslop-no-exclamation.ts) | remove exclamation marks from content prose |
80
81
  | [`ai-deslop-passive-voice`](./src/prompt/rules/deslop-passive-voice.ts) | flag passive voice constructions (e.g. "is generated" → rewrite in active voice) |
81
82
  | [`ai-deslop-weak-opener`](./src/prompt/rules/deslop-weak-opener.ts) | flag weak sentence openers like "There is" and "It is possible to" |
82
83
  | [`ai-deslop-frontmatter-spacing`](./src/prompt/rules/deslop-frontmatter-spacing.ts) | remove empty lines inside YAML frontmatter |
84
+ | [`ai-deslop-code-lang`](./src/prompt/rules/deslop-code-lang.ts) | require language hints on fenced code examples |
83
85
  | [`ai-deslop-vue-ts-lang`](./src/prompt/rules/deslop-vue-ts-lang.ts) | require `lang="ts"` on Vue `<script>` blocks in code examples |
84
86
  | **pnpm** | |
85
87
  | [`pnpm-require-trust-policy`](./src/prompt/rules/pnpm-require-trust-policy.ts) | require `trustPolicyIgnoreAfter: 262800` in `pnpm-workspace.yaml` |
@@ -180,7 +182,7 @@ export default [
180
182
 
181
183
  ### AI Deslop Rules
182
184
 
183
- 12 rules for cleaning AI-generated slop from your content markdown files (`content/**/*.md`). Most rules are auto-fixable.
185
+ 14 rules for cleaning AI-generated slop from your content markdown files (`content/**/*.md`). Most rules are auto-fixable.
184
186
 
185
187
  ```js
186
188
  // eslint.config.js
@@ -208,10 +210,12 @@ export default [
208
210
  | `ai-deslop-autolink` | Links first mention of tech terms to their canonical URLs ("Nuxt" → `[Nuxt](https://nuxt.com)`) |
209
211
  | `ai-deslop-false-dichotomy` | Flags "it's not X, it's Y" false contrast patterns |
210
212
  | `ai-deslop-hedging` | Strips hedging words that weaken copy ("very", "really", "quite", "just", "somewhat") |
213
+ | `ai-deslop-no-em-dash` | Replaces em dashes in content prose |
211
214
  | `ai-deslop-no-exclamation` | Replaces exclamation marks with periods in content prose |
212
215
  | `ai-deslop-passive-voice` | Flags passive voice ("is generated", "was created") for active rewriting |
213
216
  | `ai-deslop-weak-opener` | Flags weak expletive openers ("There is", "It is possible to") |
214
217
  | `ai-deslop-frontmatter-spacing` | Removes empty lines inside YAML frontmatter blocks |
218
+ | `ai-deslop-code-lang` | Adds language hints to fenced code blocks |
215
219
  | `ai-deslop-vue-ts-lang` | Adds `lang="ts"` to Vue `<script>` blocks in code examples |
216
220
 
217
221
  ### Prompt Rules
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.0";
7
+ const version = "0.14.2";
8
8
 
9
9
  const STRENGTH_PATTERNS = {
10
10
  strong: ["never", "must", "always", "under no circumstances", "absolutely", "required", "mandatory", "forbidden", "prohibited"],
@@ -3518,7 +3518,20 @@ const VUE_REACTIVITY_APIS = /* @__PURE__ */ new Set([
3518
3518
  // Dependency injection (composition-context only)
3519
3519
  "provide",
3520
3520
  "inject",
3521
- "hasInjectionContext"
3521
+ "hasInjectionContext",
3522
+ // Lifecycle hooks (composition-context only)
3523
+ "onBeforeMount",
3524
+ "onMounted",
3525
+ "onBeforeUpdate",
3526
+ "onUpdated",
3527
+ "onBeforeUnmount",
3528
+ "onUnmounted",
3529
+ "onActivated",
3530
+ "onDeactivated",
3531
+ "onErrorCaptured",
3532
+ "onRenderTracked",
3533
+ "onRenderTriggered",
3534
+ "onServerPrefetch"
3522
3535
  ]);
3523
3536
  const VUEUSE_REACTIVITY_APIS = /* @__PURE__ */ new Set([
3524
3537
  // Watch variants
@@ -3653,9 +3666,9 @@ const COMPOSABLE_RE = /^_?use[A-Z_]/;
3653
3666
  const NON_REACTIVE_COMPOSABLE_CALLS = /* @__PURE__ */ new Set([
3654
3667
  "useNuxtApp"
3655
3668
  ]);
3656
- function isComposableCall(node) {
3669
+ function isComposableCall(node, options = {}) {
3657
3670
  if (node.callee.type === "Identifier") {
3658
- return COMPOSABLE_RE.test(node.callee.name) && !NON_REACTIVE_COMPOSABLE_CALLS.has(node.callee.name);
3671
+ return COMPOSABLE_RE.test(node.callee.name) && (options.countNuxtAppAsComposable || !NON_REACTIVE_COMPOSABLE_CALLS.has(node.callee.name));
3659
3672
  }
3660
3673
  return false;
3661
3674
  }
@@ -3692,7 +3705,7 @@ function trackNonVueImports(node, nonVueImports) {
3692
3705
  }
3693
3706
  }
3694
3707
  }
3695
- function createReactivityChecker(vueImports, nonVueImports) {
3708
+ function createReactivityChecker(vueImports, nonVueImports, options = {}) {
3696
3709
  function isAutoImportedReactivityCall(node) {
3697
3710
  if (node.callee.type === "Identifier") {
3698
3711
  const name = node.callee.name;
@@ -3708,7 +3721,7 @@ function createReactivityChecker(vueImports, nonVueImports) {
3708
3721
  return false;
3709
3722
  switch (expr.type) {
3710
3723
  case "CallExpression":
3711
- if (isReactivityCall(expr, vueImports) || isAutoImportedReactivityCall(expr) || isComposableCall(expr) || isReactiveLifecycleCall(expr))
3724
+ if (isReactivityCall(expr, vueImports) || isAutoImportedReactivityCall(expr) || isComposableCall(expr, options) || isReactiveLifecycleCall(expr))
3712
3725
  return true;
3713
3726
  return expr.arguments.some((arg) => hasReactivityInArg(arg));
3714
3727
  case "NewExpression":
@@ -5799,6 +5812,7 @@ const vueNoAsyncLifecycleHook = createEslintRule({
5799
5812
  });
5800
5813
 
5801
5814
  const RULE_NAME$9 = "vue-no-faux-composables";
5815
+ const SERVER_RE = /(?:^|[/\\])server[/\\]/;
5802
5816
  const vueNoFauxComposables = createEslintRule({
5803
5817
  name: RULE_NAME$9,
5804
5818
  meta: {
@@ -5814,12 +5828,14 @@ const vueNoFauxComposables = createEslintRule({
5814
5828
  defaultOptions: [],
5815
5829
  create: (context) => {
5816
5830
  const filename = context.filename || context.getFilename();
5817
- if (filename.endsWith(".vue"))
5831
+ if (filename.endsWith(".vue") || SERVER_RE.test(filename))
5818
5832
  return {};
5819
5833
  const vueImports = /* @__PURE__ */ new Set();
5820
5834
  const nonVueImports = /* @__PURE__ */ new Set();
5821
5835
  const composableFunctions = /* @__PURE__ */ new Map();
5822
- const { hasReactivityInStatement, hasReactivityInExpression } = createReactivityChecker(vueImports, nonVueImports);
5836
+ const { hasReactivityInStatement, hasReactivityInExpression } = createReactivityChecker(vueImports, nonVueImports, {
5837
+ countNuxtAppAsComposable: true
5838
+ });
5823
5839
  function checkFunctionForReactivity(functionNode, functionName) {
5824
5840
  if (!functionNode.body)
5825
5841
  return;
@@ -6720,6 +6736,8 @@ const REGEX_1 = /[/\\]composables[/\\]/;
6720
6736
  const RULE_NAME = "vue-require-composable-prefix";
6721
6737
  const DEFINE_RE = /^define[A-Z]/;
6722
6738
  const CREATE_RE = /^create[A-Z]/;
6739
+ const PROVIDE_RE = /^provide[A-Z]/;
6740
+ const INJECT_RE = /^inject[A-Z]/;
6723
6741
  const vueRequireComposablePrefix = createEslintRule({
6724
6742
  name: RULE_NAME,
6725
6743
  meta: {
@@ -6743,7 +6761,7 @@ const vueRequireComposablePrefix = createEslintRule({
6743
6761
  const candidateFunctions = /* @__PURE__ */ new Map();
6744
6762
  const { hasReactivityInStatement, hasReactivityInExpression } = createReactivityChecker(vueImports, nonVueImports);
6745
6763
  function isExcludedName(name) {
6746
- return DEFINE_RE.test(name) || CREATE_RE.test(name) || name === "setup";
6764
+ return DEFINE_RE.test(name) || CREATE_RE.test(name) || PROVIDE_RE.test(name) || INJECT_RE.test(name) || name === "setup";
6747
6765
  }
6748
6766
  function checkFunctionForReactivity(functionNode, idNode, functionName) {
6749
6767
  if (!functionNode.body)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "eslint-plugin-harlanzw",
3
3
  "type": "module",
4
- "version": "0.14.0",
4
+ "version": "0.14.2",
5
5
  "description": "Harlan's opinionated ESLint rules",
6
6
  "author": "Harlan Wilton <harlan@harlanzw.com>",
7
7
  "license": "MIT",