From b917427f8b900b9c94a5cf1d1127bbe8c0c8ed5d Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Wed, 1 Oct 2025 05:30:59 -0700 Subject: [PATCH] fix(`prefer-import-tag`): handle other cases of comments before structures early in the document; fixes #1549 --- docs/rules/prefer-import-tag.md | 13 ++++++ src/rules/preferImportTag.js | 25 ++++++++--- test/rules/assertions/preferImportTag.js | 57 +++++++++++++++++++++++- 3 files changed, 87 insertions(+), 8 deletions(-) diff --git a/docs/rules/prefer-import-tag.md b/docs/rules/prefer-import-tag.md index 6640f4434..161609596 100644 --- a/docs/rules/prefer-import-tag.md +++ b/docs/rules/prefer-import-tag.md @@ -302,6 +302,19 @@ let foo; /** @type {import('foo')} */ let foo; // Message: Inline `import()` found; prefer `@import` + +/** @type {import('foo').bar} */ +let foo; +// Message: Inline `import()` found; prefer `@import` + +/** @type {import('foo').bar} */ +let foo; +// "jsdoc/prefer-import-tag": ["error"|"warn", {"outputType":"named-import"}] +// Message: Inline `import()` found; prefer `@import` + +/** @type {import('foo').default} */ +let foo; +// Message: Inline `import()` found; prefer `@import` ```` diff --git a/src/rules/preferImportTag.js b/src/rules/preferImportTag.js index f2c92f51a..132d43c51 100644 --- a/src/rules/preferImportTag.js +++ b/src/rules/preferImportTag.js @@ -386,10 +386,15 @@ export default iterateJsdoc(({ enableFixer ? (fixer) => { getFixer(element.value, [])(); - const programNode = sourceCode.getNodeByRangeIndex(0); + const programNode = sourceCode.ast; + const commentNodes = sourceCode.getCommentsBefore(programNode); + return fixer.insertTextBefore( - /** @type {import('estree').Program} */ (programNode), - `/** @import ${element.value} from '${element.value}'; */`, + // @ts-expect-error Ok + commentNodes[0] ?? programNode, + `/** @import ${element.value} from '${element.value}'; */${ + commentNodes[0] ? '\n' + indent : '' + }`, ); } : null, ); @@ -411,12 +416,18 @@ export default iterateJsdoc(({ )(); } - const programNode = sourceCode.getNodeByRangeIndex(0); + const programNode = sourceCode.ast; + const commentNodes = sourceCode.getCommentsBefore(programNode); return fixer.insertTextBefore( - /** @type {import('estree').Program} */ (programNode), + // @ts-expect-error Ok + commentNodes[0] ?? programNode, outputType === 'namespaced-import' ? - `/** @import * as ${element.value} from '${element.value}'; */` : - `/** @import { ${pathSegments.at(-1)} } from '${element.value}'; */`, + `/** @import * as ${element.value} from '${element.value}'; */${ + commentNodes[0] ? '\n' + indent : '' + }` : + `/** @import { ${pathSegments.at(-1)} } from '${element.value}'; */${ + commentNodes[0] ? '\n' + indent : '' + }`, ); } : null, ); diff --git a/test/rules/assertions/preferImportTag.js b/test/rules/assertions/preferImportTag.js index 288c3b9fe..575cbfc1d 100644 --- a/test/rules/assertions/preferImportTag.js +++ b/test/rules/assertions/preferImportTag.js @@ -234,7 +234,6 @@ export default { */ `, }, - { code: ` /** @@ -851,6 +850,62 @@ let foo; let foo; `, }, + { + code: ` + /** @type {import('foo').bar} */ + let foo; + `, + errors: [ + { + line: 2, + message: 'Inline `import()` found; prefer `@import`', + }, + ], + output: ` + /** @import * as foo from 'foo'; */ + /** @type {foo.bar} */ + let foo; + `, + }, + { + code: ` + /** @type {import('foo').bar} */ + let foo; + `, + errors: [ + { + line: 2, + message: 'Inline `import()` found; prefer `@import`', + }, + ], + options: [ + { + outputType: 'named-import', + }, + ], + output: ` + /** @import { bar } from 'foo'; */ + /** @type {bar} */ + let foo; + `, + }, + { + code: ` + /** @type {import('foo').default} */ + let foo; + `, + errors: [ + { + line: 2, + message: 'Inline `import()` found; prefer `@import`', + }, + ], + output: ` + /** @import foo from 'foo'; */ + /** @type {foo} */ + let foo; + `, + }, ], valid: [ {