From 0d956e81f068c2eb98d954db7d72994192770bc2 Mon Sep 17 00:00:00 2001 From: Sean Ryan Date: Wed, 4 Jan 2023 20:04:46 +0100 Subject: [PATCH] Fix/issue support js index files fix 260 (#261) * Fix an ESM index.js/.mjs/.cjs import edge case In [1] a removeFileExtensionToAllowForJs() utility function has been added to help with normalizing import paths into extensionless ones in order to handle ESM imports (which have to have extensions, either .js, .mjs or .cjs). One thing was missed though as index files are handled in a special way. In the analyzer.ts: const processImports = (file: File, exportMap: ExportMap): void => { Object.keys(file.imports).forEach((key) => { let ex = exportMap[removeFileExtensionToAllowForJs(key)]?.exports; exportMap had keys with extensions and "index" removed (so that "xxx/index.ts" became "xxx" there) but paths in file.imports came from getFromText() which only handled extensionless index imports so xxx/index.js" stayed "xxx/index.js". The .js extension was then removed by removeFileExtensionToAllowForJs() but the damage was done already and "xxx" != "xxx/index". This change brings the removeFileExtensionToAllowForJs()-like ESM-compatible behavior to getFromText(). [1] 6e506ae05ef3 ("Add .js extension support with es6 example (#255)") * Fix test cleanup. Up changelog * Bump package.json and changelog Co-authored-by: Jakub Stasiak --- CHANGELOG.md | 6 +++++- features/index.feature | 18 ++++++++++++++++++ package.json | 2 +- src/parser/common.ts | 2 +- src/test.ts | 4 +++- 5 files changed, 28 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 869cdca2..f68c9a27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,8 @@ -### Added +## [9.0.1] - 4 Jan 2023 + +### Changed + +- Fix for index files that end with .js or .cjs or .mjs. ## [9.0.0] - 26 Dec 2022 diff --git a/features/index.feature b/features/index.feature index 58012fee..3911b531 100644 --- a/features/index.feature +++ b/features/index.feature @@ -18,6 +18,24 @@ Scenario: Import './index' When analyzing "tsconfig.json" Then the result is {} +Scenario: Import './index.js' + Given file "index.ts" is export const a = 1; + And file "a.ts" is import { a } from './index.js'; + When analyzing "tsconfig.json" + Then the result is {} + +Scenario: Import './index.mjs' + Given file "index.mts" is export const a = 1; + And file "a.ts" is import { a } from './index.mjs'; + When analyzing "tsconfig.json" + Then the result is {} + +Scenario: Import './index.cjs' + Given file "index.cts" is export const a = 1; + And file "a.ts" is import { a } from './index.cjs'; + When analyzing "tsconfig.json" + Then the result is {} + Scenario: Import './index' TSX Given file "index.tsx" is export const a = 1; And file "a.ts" is import { a } from './index'; diff --git a/package.json b/package.json index 49b1a96a..209e1e76 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ts-unused-exports", - "version": "9.0.0", + "version": "9.0.1", "description": "ts-unused-exports finds unused exported symbols in your Typescript project", "main": "lib/app.js", "repository": { diff --git a/src/parser/common.ts b/src/parser/common.ts index 6bc5ea00..f4fce03c 100644 --- a/src/parser/common.ts +++ b/src/parser/common.ts @@ -11,7 +11,7 @@ export interface FromWhat { const TRIM_QUOTES = /^['"](.*)['"]$/; export const getFromText = (moduleSpecifier: string): string => - moduleSpecifier.replace(TRIM_QUOTES, '$1').replace(/\/index$/, ''); + moduleSpecifier.replace(TRIM_QUOTES, '$1').replace(/\/index(.[mc]?js)?$/, ''); export const getFrom = (moduleSpecifier: ts.Expression): string => getFromText(moduleSpecifier.getText()); diff --git a/src/test.ts b/src/test.ts index b6d30061..3ea2e0d2 100644 --- a/src/test.ts +++ b/src/test.ts @@ -64,7 +64,9 @@ const setup: SetupFn = ({ const items = readdirSync(path, { encoding: 'utf8' }).filter( (f) => f[0] !== '.', ); - const files = items.filter((f) => !!f.match(/\.(json|ts|tsx|js|jsx)$/)); + const files = items.filter( + (f) => !!f.match(/\.(json|ts|tsx|js|jsx|mjs|cjs|mts|cts)$/), + ); files.forEach((f) => unlinkSync(join(path, f))); const dirs = items.filter((i) => !files.includes(i)); dirs.forEach((d) => removeDir(join(path, d)));