pzavolinsky / ts-unused-exports

ts-unused-exports finds unused exported symbols in your Typescript project
MIT License
738 stars 46 forks source link

Support recognizing TypeScript files imported with `.js` suffix #214

Closed mon-jai closed 1 year ago

mon-jai commented 2 years ago

In TypeScript, it is valid to import TypeScript files with .js extension. TypeScript will match the coresponding .ts files if they exist. This behaviour is commonly used in Node.js projects as .js file extensions are required in Node.js ESM imports.

It would be great if ts-unused-exports also recognize variable imported with .js suffix.

Related: https://github.com/evanw/esbuild/issues/1343#issuecomment-854055814, https://github.com/microsoft/TypeScript/issues/16577#issuecomment-703190339, https://github.com/microsoft/TypeScript/issues/16577#issuecomment-754941937

Example

In this example project derived from example/simple, inc() from math.ts was imported with .js extension.

https://github.com/pzavolinsky/ts-unused-exports/blob/ad208c946855f9f5456d3d20435a4282f946c72b/example/simple/app.ts#L1

And TypeScript recognize it sucessfully.

56-18-22-235633

However, ts-unused-exports failed to recognize that the default import ofmath.ts was imported in app.ts.

Screenshot 2022-02-19 000527

mrseanryan commented 2 years ago

hm I'm not sure we will support imports of .js files in this way.

That link seems to be discussing ECMAScript (the spec for JavaScript).

However ts-unused-exports currently processes TypeScript.

Besides that, I don't really understand why this feature would be needed - isn't it possible use all TypeScript with Node.js ?

Perhaps if you can explain the use case a bit more, that would help ...

mon-jai commented 2 years ago

.js file extensions are required by Node.js ESM imports, and TypeScript won't add them for you by design.

Therefore, for any project that output target is ES6 or above, all import statements must be ended with .js extension (in TypeScript source files), so the compiled output will contain file extensions in import statements, and work on Node.js.

In fact, example/simple only works when the output format is set to commonjs. If I modify it to output ESM instead,

https://github.com/pzavolinsky/ts-unused-exports/blob/56939d1730404f57013d8e032ea9d5ba5b1b6f89/example/simple/tsconfig.json#L3-L4

the compiled output won't run.

$ tsc && node app.js
node:internal/process/esm_loader:94
    internalBinding('errors').triggerUncaughtException(
                              ^

Error [ERR_MODULE_NOT_FOUND]: Cannot find module 'D:\GitHub\ts-unused-exports\example\simple\math' imported from D:\GitHub\ts-unused-exports\example\simple\app.js
Did you mean to import ../math.js?
    at new NodeError (node:internal/errors:371:5)
    at finalizeResolution (node:internal/modules/esm/resolve:394:11)
    at moduleResolve (node:internal/modules/esm/resolve:944:10)
    at defaultResolve (node:internal/modules/esm/resolve:1041:11)
    at ESMLoader.resolve (node:internal/modules/esm/loader:530:30)
    at ESMLoader.getModuleJob (node:internal/modules/esm/loader:251:18)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:79:40)
    at link (node:internal/modules/esm/module_job:78:36) {
  code: 'ERR_MODULE_NOT_FOUND'
}

Node.js v17.4.0

So if I am developing a project which must output ESM code, ts-unused-exports won't work for it.

Edit: explain the issue more clearly.

ephys commented 2 years ago

Specifying the .js extension is the only way to load TypeScript files when using Node with native ES Modules, as ESM requires the extension to be specified and as @mon-jai said, TypeScript decided to not modify import paths at all (not replacing .ts with .js and not adding .js if the extension is unspecified)

bbeesley commented 2 years ago

Currently having to disable this tool in all our repos since migrating to node16/esm. There are plenty of details in the ts docs if you need to understand why we have to import like this now @mrseanryan .

mrseanryan commented 1 year ago

Hello - this is somewhat tricky to add support for...

Here is a PR that adds initial support:

https://github.com/pzavolinsky/ts-unused-exports/pull/255

If someone can try it out, that would be great ...

mrseanryan commented 1 year ago

Released as 9.0.0 - thank you

ThomasAribart commented 2 months ago

@mrseanryan Hello and thanks again for this wonderful library 🙌

It seems the issue has not been resolved when .js files are used in conjunction with tsconfig aliases. Here's the configuration on DynamoDB-Toolbox:

// tsconfig.json
{
  ...
  "compilerOptions": {
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "baseUrl": "src",
    "target": "es2019",
    "rootDir": "src",
    "outDir": "dist/types",
    "declaration": true,
    ...
    "paths": {
      "~/*": ["*"]
    }
  }
}

When I run ts-unused-exports I get a lot of false positives. I have tried:

But the conjunction of the two (from '~/someFolder/someFile.js') did not.

Do you confirm that's the current behavior? Is it the desired one? Can I do something to help?

Many thanks, Thomas