Skip to content

Conversation

@azat-io
Copy link
Owner

@azat-io azat-io commented Aug 20, 2025

🚀

Summary by CodeRabbit

  • Chores

    • Migrated package and build to ES module output; updated Node.js engine requirement to ^20 / >=22.
    • Bumped multiple dev dependencies and build tooling.
    • Replaced legacy Prettier and commitlint files with new TypeScript-based configs.
  • Refactor

    • Unified how source text is obtained across rules/utilities.
  • Documentation

    • Updated README wording and usage examples.
  • Tests

    • Removed or adjusted several unit tests to match the refactor.

@coderabbitai
Copy link

coderabbitai bot commented Aug 20, 2025

Walkthrough

Migrates the codebase to ESM and raises Node engine requirements; removes getSourceCode/getNodeContent helpers and related tests; updates rules and utils to call context.sourceCode.getText(...) directly; reconfigures Vite to build an ES module library and updates DTS handling and config files.

Changes

Cohort / File(s) Summary
Package & build config
package.json, vite.config.ts
Switch package "type" to "module"; bump engines.node to `^20.0.0
Removed helpers
utils/get-source-code.ts, utils/get-node-content.ts
Delete helper utilities that returned ESLint SourceCode and node text (removes fallback logic and wrapper function).
Rules — direct SourceCode access
rules/no-negated-conjunction.ts, rules/no-negated-disjunction.ts
Replace getNodeContent(node, context) with context.sourceCode.getText(node) and remove imports for deleted helpers; logic and fixer behavior unchanged.
Utils — direct SourceCode access
utils/has-negation-inside-parens.ts, utils/is-pure-group.ts, utils/toggle-negation.ts, utils/transform.ts
Replace calls to getSourceCode(...) / getNodeContent(...) with context.sourceCode and context.sourceCode.getText(...); remove unused imports; behavior unchanged.
Tests updated or removed
test/utils/is-pure-group.test.ts, test/utils/transform.test.ts, test/utils/get-node-content.test.ts, test/utils/get-source-code.test.ts
Update test mocks to provide sourceCode property instead of getSourceCode(); remove tests for deleted helpers.
Formatting & commitlint configs
.prettierrc (deleted), prettier.config.ts (added), .commitlintrc (deleted), commitlint.config.ts (added)
Replace JSON/.rc config files with TS-exported configs; .prettierrc and .commitlintrc removed, new prettier.config.ts and commitlint.config.ts added.
Exports / plugin surface
index.ts
Remove legacy ESLint config and createLegacyConfig; plugin configs now exposes only recommended: Linter.Config.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant ESLint as ESLint
  participant Rule as Rule (no-negated-*)
  participant Utils as Utils (transform/*)
  participant SC as context.sourceCode

  ESLint->>Rule: create rule with context
  Rule->>SC: context.sourceCode.getText(node)
  Rule->>Utils: transform(node, context)
  Utils->>SC: context.sourceCode.getText(subNode)
  Utils-->>Rule: transformed text / decision
  Rule-->>ESLint: report + fixer (uses replaced text)
  note right of SC#f8f5e6: Direct context.sourceCode usage replaces removed helpers
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

A rabbit hops in code so bright,
Helpers folded, rather light.
SourceCode speaks directly now,
Vite packs ES modules — bow.
Tests trimmed, configs set — hooray! 🥕🐇

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The PR description contains only a single emoji ("🚀") and does not follow the repository's required template; it is missing the "Description" explaining what the PR solves, "Additional context", the purpose checklist, and confirmations from the pre-submission checklist such as related issue references and test information. Please update the PR description to follow the template: add a clear "Description" summarizing the problem and the implemented solution, include any "Additional context" or reviewer guidance, mark the appropriate purpose checkbox, and confirm the pre-submission items (reference any related issue and note tests added or why none are needed).
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The PR title "release v2.0.0" is concise and correctly labels this as a release, but it is generic and does not communicate the primary technical changes (for example: migration to ESM, Node engine bump, dependency and config updates) that would help reviewers understand the scope at a glance.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests

✅ Unit Test PR creation complete.
✅ Unit Test PR creation complete.

  • Create PR with unit tests

  • Commit unit tests in branch next

  • Post copyable unit tests in a comment


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 67552e4 and 7b6a8c1.

📒 Files selected for processing (2)
  • index.ts (2 hunks)
  • readme.md (4 hunks)
✅ Files skipped from review due to trivial changes (1)
  • readme.md
🔇 Additional comments (2)
index.ts (2)

16-18: LGTM! Appropriate removal of legacy config support.

The removal of the legacy config and transition to a single recommended config aligns with ESLint's best practice guidance to "append older format configs with -legacy to make it clear that these configs will not be supported in the future". This change simplifies the plugin's API surface while maintaining support for modern flat config systems.


50-52: Clean consolidation to single config.

The export structure correctly exposes only the recommended configuration through createConfig(), which maintains consistency with the updated PluginConfig interface. This streamlined approach eliminates confusion between multiple configuration options.

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@azat-io azat-io changed the title release v5.0.0 release v2.0.0 Aug 20, 2025
@codecov
Copy link

codecov bot commented Aug 20, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (9017e2b) to head (7b6a8c1).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main        #9   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           25        23    -2     
  Lines          598       574   -24     
  Branches       130       125    -5     
=========================================
- Hits           598       574   -24     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (6)
rules/no-negated-disjunction.ts (1)

38-38: Prefer a typed, ESLint-8-compatible SourceCode access (avoid future breakage).

Using context.sourceCode.getText(node) assumes ESLint ≥9 at runtime. Your peerDep still allows ESLint 8, which will be undefined at runtime. Also, typing is looser here, which can trigger @typescript-eslint/no-unsafe-argument depending on your config. Use a typed fallback to getSourceCode() and cast the node to ESLint’s AST node type.

Apply this diff in-place:

-          let originalExpression = context.sourceCode.getText(node)
+          // Support ESLint v8 (getSourceCode) and v9 (sourceCode)
+          const sourceCode =
+            'sourceCode' in context && (context as any).sourceCode
+              ? (context as any).sourceCode
+              : context.getSourceCode()
+          // Cast to ESLint AST node to satisfy types
+          let originalExpression = sourceCode.getText(node as unknown as import('eslint').AST.Node)

Add the missing type import at the top (outside this hunk):

import type { AST } from 'eslint'

Verification ideas:

  • Run tests on ESLint 8 and 9 to ensure no runtime error: context.sourceCode is undefined on 8.
  • Ensure your TS config/lints don’t flag no-unsafe-argument after the cast.
package.json (1)

14-14: ESM switch: double-check Node-only scripts that use require(...).

With "type": "module", most Node REPL/node -p usages still allow require, but this can be environment-dependent. Your release scripts use:

  • node -p "require('./package.json').version"

If your CI shell happens to run the snippet under pure ESM semantics, this could fail. Consider a resilient alternative:

- "release:git:commit": "git commit -m \"build: publish v$(node -p \"require('./package.json').version\")\"",
- "release:git:push": "git push --follow-tags && git push origin v$(node -p \"require('./package.json').version\")",
- "release:git:tag": "git tag v$(node -p \"require('./package.json').version\")",
+ "release:git:commit": "git commit -m \"build: publish v$(node -p \"JSON.parse(require('node:fs').readFileSync('./package.json','utf8')).version\")\"",
+ "release:git:push": "git push --follow-tags && git push origin v$(node -p \"JSON.parse(require('node:fs').readFileSync('./package.json','utf8')).version\")",
+ "release:git:tag": "git tag v$(node -p \"JSON.parse(require('node:fs').readFileSync('./package.json','utf8')).version\")",

Or switch to jq if available in CI. Please verify in your pipeline.

utils/transform.ts (1)

99-103: Optional micro-optimization: avoid building RegExp when not needed.

In strictly formatted codepaths (no comments/extra spaces), you already skip transformWithFormatting. For the formatting branch, the RegExp rebuild is fine, but if perf ever matters, consider caching the escaped operator per run or using a tiny inlined replace for &&/||.

test/utils/is-pure-group.test.ts (1)

23-25: Future-proof the stub with a text field (optional).

Some utils (e.g., transformWithFormatting) require sourceCode.text. Consider adding it to the stub to reduce friction if tests expand.

 let fakeContext: Rule.RuleContext = {
   sourceCode: {
-    getText: () => currentCode,
+    getText: () => currentCode,
+    text: currentCode,
   },
 } as Rule.RuleContext
utils/is-pure-group.ts (1)

30-36: Type-safety and lint: avoid unsafe getText overload; slice from full source via range

Two improvements:

  • Declare the file text as string to avoid no-unsafe-assignment on Line 31.
  • Avoid passing an AST node to getText (which triggers no-unsafe-argument); instead, slice from the full program text using outermostNode.range.

Apply this diff:

-  let sourceCode = context.sourceCode.getText()
+  const programText: string = context.sourceCode.getText()
   let outermostNode = findOutermostParenthesizedNode(
     node as ParentedNode,
-    sourceCode,
+    programText,
   )
-  let fullCode = context.sourceCode.getText(outermostNode)
+  const [start, end] = outermostNode.range as [number, number]
+  const fullCode = programText.slice(start, end)

If you prefer not to rely on range, you can cast outermostNode to the parser’s node type expected by ESLint’s SourceCode#getText, but slicing avoids the cross-type friction entirely.

Run the linter locally to confirm the warnings are gone.

utils/toggle-negation.ts (1)

77-79: Reduce TS-ESLint “unsafe” diagnostics by slicing from SourceCode.text instead of getText(node)

Current calls to context.sourceCode.getText(nodeOrSubnode) are flagged with no-unsafe-argument/no-unsafe-member-access in several places. Use the full program text plus node ranges to construct substrings; behavior is the same and avoids typing friction across parser node types.

Apply this diff:

 function toggleBinaryExpression(
   node: BinaryExpression,
   context: Rule.RuleContext,
 ): string {
-  let left = context.sourceCode.getText(node.left).trim()
-  let right = context.sourceCode.getText(node.right).trim()
+  const programText: string = context.sourceCode.getText()
+  type Ranged<T> = T & { range: [number, number] }
+  const [lStart, lEnd] = (node.left as Ranged<Expression>).range
+  const [rStart, rEnd] = (node.right as Ranged<Expression>).range
+  let left = programText.slice(lStart, lEnd).trim()
+  let right = programText.slice(rStart, rEnd).trim()
 function toggleLogicalExpression(
   node: LogicalExpression,
   context: Rule.RuleContext,
 ): string {
-  let content = context.sourceCode.getText(node).trim()
+  const programText: string = context.sourceCode.getText()
+  type Ranged<T> = T & { range: [number, number] }
+  const [start, end] = (node as Ranged<LogicalExpression>).range
+  let content = programText.slice(start, end).trim()
   return toggleCode(parenthesize(content))
 }
 function toggleUnaryExpression(
   node: Expression,
   context: Rule.RuleContext,
 ): string {
-  let content = context.sourceCode.getText(node).trim()
+  const programText: string = context.sourceCode.getText()
+  type Ranged<T> = T & { range: [number, number] }
+  const [start, end] = (node as Ranged<Expression>).range
+  let content = programText.slice(start, end).trim()
   return toggleCode(content)
 }

Notes:

  • range is already assumed by other utilities in this repo; using it consistently here keeps behavior aligned.
  • This also prevents future breakage if the parser’s node type for getText diverges from raw estree types.

Please run the linter to confirm these warnings clear without behavior change in tests.

Also applies to: 119-121, 138-140

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9017e2b and cb6b0dd.

📒 Files selected for processing (14)
  • package.json (2 hunks)
  • rules/no-negated-conjunction.ts (1 hunks)
  • rules/no-negated-disjunction.ts (1 hunks)
  • test/utils/get-node-content.test.ts (0 hunks)
  • test/utils/get-source-code.test.ts (0 hunks)
  • test/utils/is-pure-group.test.ts (1 hunks)
  • test/utils/transform.test.ts (0 hunks)
  • utils/get-node-content.ts (0 hunks)
  • utils/get-source-code.ts (0 hunks)
  • utils/has-negation-inside-parens.ts (1 hunks)
  • utils/is-pure-group.ts (1 hunks)
  • utils/toggle-negation.ts (3 hunks)
  • utils/transform.ts (2 hunks)
  • vite.config.ts (1 hunks)
💤 Files with no reviewable changes (5)
  • utils/get-source-code.ts
  • test/utils/transform.test.ts
  • utils/get-node-content.ts
  • test/utils/get-node-content.test.ts
  • test/utils/get-source-code.test.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
utils/is-pure-group.ts (1)
utils/find-outermost-parenthesized-node.ts (1)
  • findOutermostParenthesizedNode (20-41)
🪛 ESLint
utils/is-pure-group.ts

[error] 31-34: Unsafe assignment of an error typed value.

(typescript/no-unsafe-assignment)


[error] 35-35: Unsafe argument of type error typed assigned to a parameter of type Node | undefined.

(typescript/no-unsafe-argument)

utils/transform.ts

[error] 88-88: Unsafe argument of type error typed assigned to a parameter of type Node | undefined.

(typescript/no-unsafe-argument)

rules/no-negated-conjunction.ts

[error] 40-40: Unsafe argument of type error typed assigned to a parameter of type Node | undefined.

(typescript/no-unsafe-argument)

utils/has-negation-inside-parens.ts

[error] 22-22: Unsafe argument of type error typed assigned to a parameter of type Node | undefined.

(typescript/no-unsafe-argument)

utils/toggle-negation.ts

[error] 77-77: Unsafe argument of type error typed assigned to a parameter of type Node | undefined.

(typescript/no-unsafe-argument)


[error] 77-77: Unsafe member access .left on an error typed value.

(typescript/no-unsafe-member-access)


[error] 78-78: Unsafe argument of type error typed assigned to a parameter of type Node | undefined.

(typescript/no-unsafe-argument)


[error] 78-78: Unsafe member access .right on an error typed value.

(typescript/no-unsafe-member-access)


[error] 119-119: Unsafe argument of type error typed assigned to a parameter of type Node | undefined.

(typescript/no-unsafe-argument)


[error] 138-138: Unsafe argument of type error typed assigned to a parameter of type Node | undefined.

(typescript/no-unsafe-argument)

vite.config.ts

[error] 23-23: Unsafe call of a(n) error type typed value.

(typescript/no-unsafe-call)

🔇 Additional comments (2)
test/utils/is-pure-group.test.ts (1)

23-25: LGTM. Minimal SourceCode stub aligns with new API.

The switch to fakeContext.sourceCode.getText() looks good and keeps tests focused.

utils/has-negation-inside-parens.ts (1)

22-24: Action Required: Verify full-file text is needed for correct parenthesis detection

Before merging, please confirm that findOutermostParenthesizedNode indeed relies on absolute character ranges within the entire source file—and that passing only the node’s sliced text will break its logic. Specifically:

  • Inspect utils/has-negation-inside-parens.ts and the implementation of findOutermostParenthesizedNode. Ensure it uses the node’s range indices against the full file text, not a substring.
  • Confirm that context.sourceCode.getText() (no arguments) returns the entire file’s text, whereas getText(node) returns only the node slice.

If verified, apply this change:

-  let sourceCode = context.sourceCode.getText(node)
-  let outermostNode = findOutermostParenthesizedNode(node, sourceCode)
+  const fileText: string = context.sourceCode.getText()
+  const outermostNode = findOutermostParenthesizedNode(node, fileText)

And add or retain tests covering cases where the negation isn’t at position 0 (e.g. const x = 0; if (!(a && !b)) {}) to validate absolute‐range detection.

},
"engines": {
"node": "^18.0.0 || >=20.0.0"
"node": "^20.0.0 || >=22.0.0"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Peer dep range is inconsistent with code requiring ESLint v9 APIs.

You now rely on context.sourceCode (ESLint ≥9). Declaring "eslint": ">=8.0.0" in peerDependencies will break users on ESLint 8 at runtime. Either:

  • Raise the peer range to >=9.0.0, or
  • Keep support for ESLint 8 by using a safe fallback to getSourceCode() everywhere (see my code suggestions in the rules and utils).

Proposed fix:

   "peerDependencies": {
-    "eslint": ">=8.0.0"
+    "eslint": ">=9.0.0"
   },

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In package.json around line 84, the peerDependencies entry for eslint ("eslint":
">=8.0.0") is incorrect because the code uses ESLint v9 APIs
(context.sourceCode); update the peer dependency to require ESLint v9 by
changing the range to ">=9.0.0" (or alternatively implement backward-compatible
fallbacks to getSourceCode() throughout the codebase if you must support ESLint
8) so consumers with ESLint 8 won't break at runtime.


if (fixedExpression) {
let originalExpression = getNodeContent(node, context)
let originalExpression = context.sourceCode.getText(node)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Fix the @typescript-eslint/no-unsafe-argument and keep ESLint 8/9 compatibility.

The static analysis hint points here. Root causes:

  • context.sourceCode is only present in ESLint ≥9.
  • Type mismatch between Estree nodes and ESLint’s AST.Node for getText.

Adopt a typed fallback and cast to ESLint’s AST node.

Apply this diff:

-          let originalExpression = context.sourceCode.getText(node)
+          // ESLint v9 has `context.sourceCode`; ESLint v8 relies on `getSourceCode()`
+          const sourceCode =
+            'sourceCode' in context && (context as any).sourceCode
+              ? (context as any).sourceCode
+              : context.getSourceCode()
+          let originalExpression = sourceCode.getText(node as unknown as import('eslint').AST.Node)

Add the missing type import at the top (outside this hunk):

import type { AST } from 'eslint'
🧰 Tools
🪛 ESLint

[error] 40-40: Unsafe argument of type error typed assigned to a parameter of type Node | undefined.

(typescript/no-unsafe-argument)

🤖 Prompt for AI Agents
In rules/no-negated-conjunction.ts around line 40, context.sourceCode usage
causes a @typescript-eslint/no-unsafe-argument and breaks ESLint 8/9
compatibility; add the missing type import "import type { AST } from 'eslint'"
at the top, obtain the sourceCode with a backwards-compatible fallback (use
context.getSourceCode() when available, otherwise context.sourceCode), and call
getText passing a properly cast node (e.g. node as unknown as AST.Node) so the
argument is correctly typed.

}

let originalText = getNodeContent(argument, context)
let originalText = context.sourceCode.getText(argument)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Harden SourceCode retrieval and satisfy TS linting (no-unsafe-argument).

Two spots:

  • Line 88: context.sourceCode.getText(argument) will fail on ESLint 8 and is flagged by TS lints.
  • Line 118: let { sourceCode } = context has the same ESLint 8 risk and loose typing.

Adopt a typed fallback and cast nodes to ESLint’s AST type.

Apply this diff for Line 88:

-  let originalText = context.sourceCode.getText(argument)
+  const sourceCode =
+    'sourceCode' in context && (context as any).sourceCode
+      ? (context as any).sourceCode
+      : context.getSourceCode()
+  let originalText = sourceCode.getText(argument as unknown as import('eslint').AST.Node)

Apply this diff for Line 118:

-  let { sourceCode } = context
+  const sourceCode =
+    'sourceCode' in context && (context as any).sourceCode
+      ? (context as any).sourceCode
+      : context.getSourceCode()

Also add the missing type import at the top (outside this hunk):

import type { AST } from 'eslint'

Also applies to: 118-118

🧰 Tools
🪛 ESLint

[error] 88-88: Unsafe argument of type error typed assigned to a parameter of type Node | undefined.

(typescript/no-unsafe-argument)

🤖 Prompt for AI Agents
In utils/transform.ts around line 88 and line 118, replace direct uses of
context.sourceCode/getText that cause unsafe-argument linting by obtaining
SourceCode via context.getSourceCode() and using explicit ESLint AST typing: add
"import type { AST } from 'eslint'" at the top, cast node arguments to AST.Node
when calling getText, and replace "let { sourceCode } = context" with a typed
retrieval such as "const sourceCode = context.getSourceCode()" (typed as
SourceCode/AST where appropriate) so handlers use a typed fallback and satisfy
no-unsafe-argument.

Comment on lines +9 to +13
entry: [path.resolve(import.meta.dirname, 'index.ts')],
fileName: (_format, entryName) => `${entryName}.js`,
name: 'eslint-plugin-de-morgan',
formats: ['es'],
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

ESM paths: import.meta.dirname is not standard in Node 20/22; use fileURLToPath

With the engines now ^20 || >=22, import.meta.dirname isn’t available in Node’s ESM. This can break the build when Vite reads the config. Use fileURLToPath(import.meta.url) to derive a directory path.

Apply this diff:

 import dts from 'vite-plugin-dts'
 import path from 'node:path'
+import { fileURLToPath } from 'node:url'

 export default defineConfig({
   build: {
     lib: {
-      entry: [path.resolve(import.meta.dirname, 'index.ts')],
+      constDir: undefined as unknown as string, // placeholder for explanation; see note below
+      entry: [path.resolve(path.dirname(fileURLToPath(import.meta.url)), 'index.ts')],
       fileName: (_format, entryName) => `${entryName}.js`,
       name: 'eslint-plugin-de-morgan',
       formats: ['es'],
     },
     dts({
       include: [
-        path.join(import.meta.dirname, 'index.ts'),
-        path.join(import.meta.dirname, 'rules'),
-        path.join(import.meta.dirname, 'utils'),
+        path.join(path.dirname(fileURLToPath(import.meta.url)), 'index.ts'),
+        path.join(path.dirname(fileURLToPath(import.meta.url)), 'rules'),
+        path.join(path.dirname(fileURLToPath(import.meta.url)), 'utils'),
       ],
       insertTypesEntry: true,
       strictOutput: true,
       copyDtsFiles: true,
     }),

Alternative (cleaner): declare once at the top:

const dirname = path.dirname(fileURLToPath(import.meta.url))

and then use dirname in the three places above. I kept the diff minimal across the changed lines; feel free to hoist to a dirname const.

Also consider marking eslint and parser-related packages as externals explicitly in rollupOptions.external if you run into bundling surprises during ESM-only builds.

Try node -e "console.log(import.meta.dirname)" on Node 20/22 to confirm the property is undefined; then run a local vite build after applying the fix.

Also applies to: 25-27


🏁 Script executed:

#!/bin/bash
# Test import.meta.dirname in Node ESM
node --input-type=module -e "console.log(import.meta.dirname)" || echo "Error: import.meta.dirname is not defined"

# Test fileURLToPath usage for import.meta.url
node --input-type=module -e "import { fileURLToPath } from 'node:url'; import path from 'node:path'; console.log(path.dirname(fileURLToPath(import.meta.url)))"

Length of output: 268


🏁 Script executed:

#!/bin/bash
# Check Node version in sandbox
node -v

Length of output: 17


Ensure ESM compatibility across all Node 20.x LTS versions by replacing import.meta.dirname

The properties import.meta.dirname and import.meta.filename were only introduced in Node.js v20.11.0 and became stable in v21.2.0; earlier 20.x installs (e.g. v20.0.0–v20.10.x) will see these as undefined and break your Vite build (sonarsource.com, nodejs.org). Instead, derive the directory via the well-supported fileURLToPath(import.meta.url) pattern (available in Node 10.12+), which works across all your declared engine ranges (stackoverflow.com).

Please update vite.config.ts as follows:

• At the top of the file, add the URL helper and define a stable __dirname constant:

import dts from 'vite-plugin-dts'
import path from 'node:path'
import { fileURLToPath } from 'node:url'

const __dirname = path.dirname(fileURLToPath(import.meta.url))

• Replace the entry path in build.lib:

- entry: [path.resolve(import.meta.dirname, 'index.ts')],
+ entry: [path.resolve(__dirname, 'index.ts')],

• Replace each import.meta.dirname in your dts({ include: […] }) array:

- path.join(import.meta.dirname, 'index.ts'),
- path.join(import.meta.dirname, 'rules'),
- path.join(import.meta.dirname, 'utils'),
+ path.join(__dirname, 'index.ts'),
+ path.join(__dirname, 'rules'),
+ path.join(__dirname, 'utils'),

• (Optional) If you encounter unexpected bundling of ESLint or parser dependencies during your ESM-only build, explicitly externalize them in build.rollupOptions:

rollupOptions: {
  external: [
    'eslint',
    'espree',
    '@typescript-eslint/parser'
  ]
}

These changes remove reliance on the still-experimental import.meta.dirname and ensure your library builds correctly on all Node 20.x and ≥22.x runtimes.

🤖 Prompt for AI Agents
In vite.config.ts around lines 9 to 13, replace uses of import.meta.dirname
(which is unavailable on older Node 20.x releases) by adding an ESM-safe
__dirname derived from fileURLToPath(import.meta.url): import fileURLToPath from
node:url and path from node:path, compute const __dirname =
path.dirname(fileURLToPath(import.meta.url)) at the top of the file, then update
build.lib.entry to use path.resolve(__dirname, 'index.ts') (or equivalent) and
replace any import.meta.dirname occurrences in the dts({ include: [...] })
entries with __dirname-based paths; optionally add rollupOptions.external to the
build config to externalize 'eslint', 'espree', and '@typescript-eslint/parser'
if you see them bundled.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
package.json (1)

85-85: Confirm Node engine bump is intentional.

You’re dropping Node 18 and 21 (^20 || >=22). If that’s deliberate for ESM/tooling parity, fine; otherwise consider >=18.18.0 to keep more users.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cb6b0dd and 67552e4.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (5)
  • .commitlintrc (0 hunks)
  • .prettierrc (0 hunks)
  • commitlint.config.ts (1 hunks)
  • package.json (3 hunks)
  • prettier.config.ts (1 hunks)
💤 Files with no reviewable changes (2)
  • .commitlintrc
  • .prettierrc
🔇 Additional comments (4)
package.json (2)

14-14: ESM switch looks good.

"type": "module" aligns with ESLint 9 being ESM-only and your exports map.


82-83: Peer dependency range must match ESLint v9 API usage.

Code relies on context.sourceCode (ESLint ≥9). Keeping "eslint": ">=8.0.0" will break users on ESLint 8.

Apply:

   "peerDependencies": {
-    "eslint": ">=8.0.0"
+    "eslint": ">=9.0.0"
   },
commitlint.config.ts (1)

1-5: Prefer commitlint.config.mjs — commitlint CLI doesn't load .ts configs without a TS loader

commitlint's CLI typically won't load a TypeScript config unless you wire a TS loader into the runtime; prefer commitlint.config.mjs for reliability.

Suggested commitlint.config.mjs:

/** @type {import('@commitlint/types').UserConfig} */
export default {
  extends: ['@commitlint/config-conventional'],
}

Verification attempted: echo "chore: test" | pnpm exec commitlint -V failed with "Command 'commitlint' not found" in this environment — re-run locally after adding commitlint as a devDependency (or provide access to install deps) to confirm.

prettier.config.ts (1)

1-15: Prettier TS config may not be discovered by the CLI — rename to .mjs or ensure Prettier CLI/TS loader is present

pnpm exec prettier failed: "Command 'prettier' not found" — couldn't verify; either install Prettier in the repo or rename prettier.config.ts → prettier.config.mjs (or add a TS loader).

Proposed prettier.config.mjs:

/** @type {import('prettier').Config} */
const config = {
  plugins: ['prettier-plugin-jsdoc'],
  quoteProps: 'as-needed',
  arrowParens: 'avoid',
  bracketSpacing: true,
  trailingComma: 'all',
  singleQuote: true,
  endOfLine: 'lf',
  printWidth: 80,
  useTabs: false,
  semi: false,
  tabWidth: 2,
}
export default config

Quick checks (run after installing Prettier):

pnpm exec prettier --find-config-path package.json
pnpm exec prettier --loglevel debug --check package.json 2>&1 | rg -n "loaded config|config file"

@azat-io azat-io merged commit 887b302 into main Sep 23, 2025
17 checks passed
@azat-io azat-io deleted the next branch September 23, 2025 15:10
@coderabbitai
Copy link

coderabbitai bot commented Oct 5, 2025

Note

Unit test generation is an Early Access feature. Expect some limitations and changes as we gather feedback and continue to improve it.


Generating unit tests... This may take up to 20 minutes.

@coderabbitai
Copy link

coderabbitai bot commented Oct 5, 2025

Caution

The MK3 agent failed during execution: Clone operation failed

@coderabbitai
Copy link

coderabbitai bot commented Oct 5, 2025

Note

Unit test generation is an Early Access feature. Expect some limitations and changes as we gather feedback and continue to improve it.


Generating unit tests... This may take up to 20 minutes.

@coderabbitai
Copy link

coderabbitai bot commented Oct 5, 2025

Caution

The MK3 agent failed during execution: Clone operation failed

@coderabbitai
Copy link

coderabbitai bot commented Oct 5, 2025

Note

Unit test generation is an Early Access feature. Expect some limitations and changes as we gather feedback and continue to improve it.


Generating unit tests... This may take up to 20 minutes.

@coderabbitai
Copy link

coderabbitai bot commented Oct 5, 2025

Caution

The MK3 agent failed during execution: Clone operation failed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants