Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/kind-bananas-poke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"eslint-plugin-import-x": minor
---

refactor: migrate deprecated `doctrine` to `comment-parser`
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,6 @@ lib
!.yarn/plugins
!.yarn/releases
!.yarn/sdks

# Local test
test.local.*
1 change: 1 addition & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export default config(
'.*.js',
'**/*.d.ts',
'**/.eslintrc.js',
'test.local.*',
'eslint.config.js',
'jest.config.ts',
],
Expand Down
37 changes: 18 additions & 19 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,23 +72,22 @@
},
"dependencies": {
"@pkgr/core": "^0.2.4",
"@types/doctrine": "^0.0.9",
"@typescript-eslint/utils": "^8.30.1",
"@typescript-eslint/utils": "^8.31.0",
"comment-parser": "^1.4.1",
"debug": "^4.4.0",
"doctrine": "^3.0.0",
"eslint-import-resolver-node": "^0.3.9",
"get-tsconfig": "^4.10.0",
"is-glob": "^4.0.3",
"minimatch": "^9.0.3 || ^10.0.1",
"semver": "^7.7.1",
"stable-hash": "^0.0.5",
"tslib": "^2.8.1",
"unrs-resolver": "^1.6.0"
"unrs-resolver": "^1.7.0"
},
"devDependencies": {
"@1stg/commitlint-config": "^5.0.6",
"@1stg/lint-staged": "^4.0.9",
"@1stg/prettier-config": "^5.1.3",
"@1stg/prettier-config": "^5.1.4",
"@1stg/remark-preset": "^3.1.1",
"@1stg/simple-git-hooks": "^2.0.1",
"@1stg/tsconfig": "^3.0.3",
Expand All @@ -106,10 +105,10 @@
"@changesets/cli": "^2.29.2",
"@commitlint/cli": "^19.8.0",
"@eslint/import-test-order-redirect-scoped": "link:./test/fixtures/order-redirect-scoped",
"@eslint/js": "^9.25.0",
"@eslint/js": "^9.25.1",
"@pkgr/rollup": "^6.0.3",
"@swc-node/jest": "^1.8.13",
"@swc/core": "^1.11.21",
"@swc/core": "^1.11.22",
"@swc/helpers": "^0.5.17",
"@test-scope/some-module": "link:./test/fixtures/symlinked-module",
"@total-typescript/ts-reset": "^0.6.1",
Expand All @@ -120,28 +119,28 @@
"@types/jest": "^29.5.14",
"@types/json-schema": "^7.0.15",
"@types/klaw-sync": "^6.0.5",
"@types/node": "^22.14.1",
"@types/node": "^22.15.2",
"@types/pnpapi": "^0.0.5",
"@typescript-eslint/eslint-plugin": "^8.30.1",
"@typescript-eslint/parser": "^8.30.1",
"@typescript-eslint/rule-tester": "^8.30.1",
"@typescript-eslint/eslint-plugin": "^8.31.0",
"@typescript-eslint/parser": "^8.31.0",
"@typescript-eslint/rule-tester": "^8.31.0",
"@unts/patch-package": "^8.1.1",
"clean-pkg-json": "^1.2.1",
"eslint": "^9.25.0",
"clean-pkg-json": "^1.3.0",
"eslint": "^9.25.1",
"eslint-config-prettier": "^10.1.2",
"eslint-doc-generator": "^2.1.2",
"eslint-import-resolver-typescript": "^4.3.3",
"eslint-import-resolver-typescript": "^4.3.4",
"eslint-import-resolver-webpack": "^0.13.10",
"eslint-import-test-order-redirect": "link:./test/fixtures/order-redirect",
"eslint-plugin-eslint-plugin": "^6.4.0",
"eslint-plugin-import-x": "link:.",
"eslint-plugin-jest": "^28.11.0",
"eslint-plugin-json": "^4.0.1",
"eslint-plugin-mdx": "^3.4.0",
"eslint-plugin-mdx": "^3.4.1",
"eslint-plugin-n": "^17.17.0",
"eslint-plugin-prettier": "^5.2.6",
"eslint-plugin-unicorn": "^58.0.0",
"eslint-plugin-yml": "^1.17.0",
"eslint-plugin-yml": "^1.18.0",
"eslint8.56": "npm:eslint@~8.56.0",
"eslint9": "npm:eslint@^9.24.0",
"globals": "^16.0.0",
Expand All @@ -150,16 +149,16 @@
"klaw-sync": "^7.0.0",
"nano-staged": "^0.8.0",
"npm-run-all2": "^7.0.2",
"path-serializer": "^0.3.4",
"path-serializer": "^0.4.0",
"prettier": "^3.5.3",
"redux": "^5.0.1",
"rimraf": "^6.0.1",
"simple-git-hooks": "^2.12.1",
"simple-git-hooks": "^2.13.0",
"tinyexec": "^1.0.1",
"ts-node": "^10.9.2",
"type-fest": "^4.40.0",
"typescript": "^5.8.3",
"typescript-eslint": "^8.30.1",
"typescript-eslint": "^8.31.0",
"yarn-berry-deduplicate": "^6.1.3",
"zod": "^3.24.3"
},
Expand Down
10 changes: 3 additions & 7 deletions src/meta.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import { createRequire } from 'node:module'

import { EVAL_FILENAMES } from '@pkgr/core'
import type { CjsRequire } from '@pkgr/core'

const cjsRequire: CjsRequire =
typeof require === 'undefined' ||
// workaround for #296
EVAL_FILENAMES.has(__filename)
? createRequire(import.meta.url)
: /* istanbul ignore next */ require
const cjsRequire: CjsRequire = import.meta.url
? createRequire(import.meta.url)
: /* istanbul ignore next */ require

export const { name, version } = cjsRequire<{ name: string; version: string }>(
'../package.json',
Expand Down
10 changes: 5 additions & 5 deletions src/rules/no-deprecated.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { TSESTree } from '@typescript-eslint/utils'
import type { Tag } from 'doctrine'
import type { Spec } from 'comment-parser'

import type { ModuleNamespace } from '../utils/index.js'
import {
Expand All @@ -9,7 +9,7 @@ import {
getValue,
} from '../utils/index.js'

function message(deprecation: Tag) {
function message(deprecation: Spec) {
if (deprecation.description) {
return {
messageId: 'deprecatedDesc',
Expand All @@ -25,7 +25,7 @@ function getDeprecation(metadata?: ModuleNamespace | null) {
return
}

return metadata.doc.tags.find(t => t.title === 'deprecated')
return metadata.doc.tags.find(t => t.tag === 'deprecated')
}

export default createRule({
Expand All @@ -45,7 +45,7 @@ export default createRule({
},
defaultOptions: [],
create(context) {
const deprecated = new Map<string, Tag>()
const deprecated = new Map<string, Spec>()
const namespaces = new Map<string, ExportMap | null>()

return {
Expand All @@ -66,7 +66,7 @@ export default createRule({
}

const moduleDeprecation = imports.doc?.tags.find(
t => t.title === 'deprecated',
t => t.tag === 'deprecated',
)
if (moduleDeprecation) {
context.report({
Expand Down
2 changes: 1 addition & 1 deletion src/rules/no-duplicates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const isTypeScriptVersionSupportPreferInline = lazy(() => {
let typescriptPkg: PackageJson | undefined

try {
typescriptPkg = cjsRequire('typescript/package.json') as PackageJson
typescriptPkg = cjsRequire<PackageJson>('typescript/package.json')
} catch {
//
}
Expand Down
73 changes: 49 additions & 24 deletions src/utils/export-map.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import fs from 'node:fs'
import path from 'node:path'

import { cjsRequire } from '@pkgr/core'
import type { TSESLint, TSESTree } from '@typescript-eslint/utils'
import type * as commentParser from 'comment-parser'
import debug from 'debug'
import type { Annotation } from 'doctrine'
import * as doctrine from 'doctrine'
import type { AST } from 'eslint'
import { SourceCode } from 'eslint'
import type { TsConfigJsonResolved, TsConfigResult } from 'get-tsconfig'
Expand Down Expand Up @@ -36,7 +36,7 @@ const tsconfigCache = new Map<string, TsConfigJsonResolved | null | undefined>()

export type DocStyleParsers = Record<
DocStyle,
(comments: TSESTree.Comment[]) => Annotation | undefined
(comments: TSESTree.Comment[]) => commentParser.Block | undefined
>

export interface DeclarationMetadata {
Expand All @@ -47,7 +47,7 @@ export interface DeclarationMetadata {
}

export interface ModuleNamespace {
doc?: Annotation
doc?: commentParser.Block
namespace?: ExportMap | null
}

Expand All @@ -67,6 +67,29 @@ const declTypes = new Set([
'TSModuleDeclaration',
])

// https://github.com/syavorsky/comment-parser/issues/172
const fixup = new Set(['deprecated', 'module'])

let parseComment_: typeof commentParser.parse | undefined

const parseComment = (comment: string): commentParser.Block => {
parseComment_ ??= cjsRequire<typeof commentParser>('comment-parser').parse
const restored = `/**${comment.split('\n').reduce((acc, line) => {
line = line.trim()
return line && line !== '*' ? acc + '\n ' + line : acc
}, '')}
*/`
const [doc] = parseComment_(restored)
return {
...doc,
tags: doc.tags.map(t =>
t.name && fixup.has(t.tag)
? { ...t, description: `${t.name} ${t.description}` }
: t,
),
}
}

export class ExportMap {
static for(context: ChildContext) {
const filepath = context.path
Expand Down Expand Up @@ -663,20 +686,20 @@ export class ExportMap {

// attempt to collect module doc
defineLazyProperty(m, 'doc', () => {
if (ast.comments) {
for (let i = 0, len = ast.comments.length; i < len; i++) {
const c = ast.comments[i]
if (c.type !== 'Block') {
continue
}
try {
const doc = doctrine.parse(c.value, { unwrap: true })
if (doc.tags.some(t => t.title === 'module')) {
return doc
}
} catch {
/* ignore */
if (!ast.comments?.length) {
return
}
for (const c of ast.comments) {
if (c.type !== 'Block') {
continue
}
try {
const doc = parseComment(c.value)
if (doc.tags.some(t => t.tag === 'module')) {
return doc
}
} catch {
/* ignore */
}
}
})
Expand Down Expand Up @@ -727,7 +750,7 @@ export class ExportMap {

declare private mtime: number

declare doc: Annotation | undefined
declare doc: commentParser.Block | undefined

constructor(public path: string) {}

Expand Down Expand Up @@ -943,7 +966,7 @@ function captureDoc(
...nodes: Array<TSESTree.Node | undefined>
) {
const metadata: {
doc?: Annotation | undefined
doc?: commentParser.Block | undefined
} = {}

defineLazyProperty(metadata, 'doc', () => {
Expand Down Expand Up @@ -1002,15 +1025,17 @@ function captureJsDoc(comments: TSESTree.Comment[]) {
continue
}
try {
return doctrine.parse(comment.value, { unwrap: true })
return parseComment(comment.value)
} catch {
/* don't care, for now? maybe add to `errors?` */
}
}
}

/** Parse TomDoc section from comments */
function captureTomDoc(comments: TSESTree.Comment[]): Annotation | undefined {
function captureTomDoc(
comments: TSESTree.Comment[],
): commentParser.Block | undefined {
// collect lines up to first paragraph break
const lines = []
for (const comment of comments) {
Expand All @@ -1020,7 +1045,7 @@ function captureTomDoc(comments: TSESTree.Comment[]): Annotation | undefined {
lines.push(comment.value.trim())
}

// return doctrine-like object
// return comment-parser-like object
const statusMatch = lines
.join(' ')
.match(/^(Public|Internal|Deprecated):\s*(.+)/)
Expand All @@ -1029,11 +1054,11 @@ function captureTomDoc(comments: TSESTree.Comment[]): Annotation | undefined {
description: statusMatch[2],
tags: [
{
title: statusMatch[1].toLowerCase(),
tag: statusMatch[1].toLowerCase(),
description: statusMatch[2],
},
],
}
} as commentParser.Block
}
}

Expand Down
7 changes: 3 additions & 4 deletions src/utils/resolve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,9 @@ import {
} from './legacy-resolver-settings.js'
import { ModuleCache } from './module-cache.js'

const _filename =
typeof __filename === 'undefined'
? fileURLToPath(import.meta.url)
: /* istanbul ignore next */ __filename
const _filename = import.meta.url
? fileURLToPath(import.meta.url)
: /* istanbul ignore next */ __filename
const _dirname = path.dirname(_filename)

export const CASE_SENSITIVE_FS = !fs.existsSync(
Expand Down
2 changes: 1 addition & 1 deletion test/rules/no-extraneous-dependencies.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ ruleTester.run('no-extraneous-dependencies', rule, {
}),
tValid({ code: 'require(6)' }),
tValid({
code: 'import "doctrine"',
code: 'import "comment-parser"',
options: [{ packageDir: path.join(_dirname, '../../') }],
}),
tValid({
Expand Down
Loading