diff --git a/docs/rules/prefer-import-tag.md b/docs/rules/prefer-import-tag.md index 161609596..695e10614 100644 --- a/docs/rules/prefer-import-tag.md +++ b/docs/rules/prefer-import-tag.md @@ -315,6 +315,15 @@ let foo; /** @type {import('foo').default} */ let foo; // Message: Inline `import()` found; prefer `@import` + +/** @type { import('@typescript-eslint/utils').TSESLint.FlatConfig.Config['rules'] } */ +// Message: Inline `import()` found; prefer `@import` + +/** @type { import('node:zlib').createGzip } */ +// Message: Inline `import()` found; prefer `@import` + +/** @type { import('./lib/someFile.js').someImport } */ +// Message: Inline `import()` found; prefer `@import` ```` diff --git a/package.json b/package.json index c7f326a58..201c4f83a 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "replace": "^1.2.2", "rimraf": "^6.0.1", "semantic-release": "^24.2.9", + "to-valid-identifier": "^0.1.1", "typescript": "5.9.3", "typescript-eslint": "^8.46.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 82f4a4f0f..92275e07b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -189,6 +189,9 @@ importers: semantic-release: specifier: ^24.2.9 version: 24.2.9(typescript@5.9.3) + to-valid-identifier: + specifier: ^0.1.1 + version: 0.1.1 typescript: specifier: 5.9.3 version: 5.9.3 @@ -1187,6 +1190,10 @@ packages: peerDependencies: semantic-release: '>=20.1.0' + '@sindresorhus/base62@0.1.0': + resolution: {integrity: sha512-BNjiImatCV+CUdvzSbFWzZIZqOddg6qn0Ag6/8Ty5G09oOtvxidZ5tkPQW2XHpzGVkB3eHfdYGwrWNy2nUFOwQ==} + engines: {node: '>=18'} + '@sindresorhus/is@4.6.0': resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} engines: {node: '>=10'} @@ -4354,6 +4361,10 @@ packages: resolution: {integrity: sha512-LBnkqsDE7BZKvqylbmn7lTIVdpx4K/QCduRATpO5R+wtPmky/a8pN1bO2D6wXppn1497AJF9mNjqAXr6bdl9jg==} engines: {node: '>=0.10.5'} + reserved-identifiers@1.0.0: + resolution: {integrity: sha512-h0bP2Katmvf3hv4Z3WtDl4+6xt/OglQ2Xa6TnhZ/Rm9/7IH1crXQqMwD4J2ngKBonVv+fB55zfGgNDAmsevLVQ==} + engines: {node: '>=18'} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -4787,6 +4798,10 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + to-valid-identifier@0.1.1: + resolution: {integrity: sha512-/m+BsP+oLrdYptSVuWdRanXBD0N1qiyx2GtAunpPz+TRPENw7IpEndqyPTwxe67wLiu16ZinauXESdOL1eDYBQ==} + engines: {node: '>=18'} + tough-cookie@2.5.0: resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==} engines: {node: '>=0.8'} @@ -6542,6 +6557,8 @@ snapshots: transitivePeerDependencies: - supports-color + '@sindresorhus/base62@0.1.0': {} + '@sindresorhus/is@4.6.0': {} '@sindresorhus/merge-streams@4.0.0': {} @@ -10032,6 +10049,8 @@ snapshots: requireindex@1.1.0: {} + reserved-identifiers@1.0.0: {} + resolve-from@4.0.0: {} resolve-from@5.0.0: {} @@ -10546,6 +10565,11 @@ snapshots: dependencies: is-number: 7.0.0 + to-valid-identifier@0.1.1: + dependencies: + '@sindresorhus/base62': 0.1.0 + reserved-identifiers: 1.0.0 + tough-cookie@2.5.0: dependencies: psl: 1.15.0 diff --git a/src/rules/preferImportTag.js b/src/rules/preferImportTag.js index 216e1f204..f5f3bf12f 100644 --- a/src/rules/preferImportTag.js +++ b/src/rules/preferImportTag.js @@ -13,6 +13,7 @@ import { import { parseImportsExports, } from 'parse-imports-exports'; +import toValidIdentifier from 'to-valid-identifier'; export default iterateJsdoc(({ context, @@ -133,10 +134,11 @@ export default iterateJsdoc(({ } /** - * @param {string} matchingName + * @param {string} name * @param {string[]} extrPathSegments */ - const getFixer = (matchingName, extrPathSegments) => { + const getFixer = (name, extrPathSegments) => { + const matchingName = toValidIdentifier(name); return () => { /** @type {import('jsdoc-type-pratt-parser').NamePathResult|undefined} */ let node = nodes.at(0); @@ -369,7 +371,7 @@ export default iterateJsdoc(({ return fixer.insertTextBefore( // @ts-expect-error Ok commentNodes[0] ?? programNode, - `/** @import * as ${element.value} from '${element.value}'; */${ + `/** @import * as ${toValidIdentifier(element.value)} from '${element.value}'; */${ commentNodes[0] ? '\n' + indent : '' }`, ); @@ -422,10 +424,13 @@ export default iterateJsdoc(({ // @ts-expect-error Ok commentNodes[0] ?? programNode, outputType === 'namespaced-import' ? - `/** @import * as ${element.value} from '${element.value}'; */${ + `/** @import * as ${toValidIdentifier(element.value)} from '${element.value}'; */${ commentNodes[0] ? '\n' + indent : '' }` : - `/** @import { ${pathSegments.at(-1)} } from '${element.value}'; */${ + `/** @import { ${toValidIdentifier( + /* c8 ignore next -- TS */ + pathSegments.at(-1) ?? '', + )} } from '${element.value}'; */${ commentNodes[0] ? '\n' + indent : '' }`, ); diff --git a/test/rules/assertions/preferImportTag.js b/test/rules/assertions/preferImportTag.js index 575cbfc1d..30a5d67f6 100644 --- a/test/rules/assertions/preferImportTag.js +++ b/test/rules/assertions/preferImportTag.js @@ -906,6 +906,48 @@ let foo; let foo; `, }, + { + code: ` + /** @type { import('@typescript-eslint/utils').TSESLint.FlatConfig.Config['rules'] } */ + `, + errors: [ + { + line: 2, + message: 'Inline `import()` found; prefer `@import`', + }, + ], + output: `/** @import * as $12$typescript$j$eslint$l$utils from '@typescript-eslint/utils'; */ + /** @type {$12$typescript$j$eslint$l$utils.TSESLint.FlatConfig.Config['rules']} */ + `, + }, + { + code: ` + /** @type { import('node:zlib').createGzip } */ + `, + errors: [ + { + line: 2, + message: 'Inline `import()` found; prefer `@import`', + }, + ], + output: `/** @import * as node$w$zlib from 'node:zlib'; */ + /** @type {node$w$zlib.createGzip} */ + `, + }, + { + code: ` + /** @type { import('./lib/someFile.js').someImport } */ + `, + errors: [ + { + line: 2, + message: 'Inline `import()` found; prefer `@import`', + }, + ], + output: `/** @import * as $k$$l$lib$l$someFile$k$js from './lib/someFile.js'; */ + /** @type {$k$$l$lib$l$someFile$k$js.someImport} */ + `, + }, ], valid: [ {