Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: allow stdin to pick up settings from tsconfig.json #2543

Closed
Septh opened this issue Sep 13, 2022 · 8 comments
Closed

Feature request: allow stdin to pick up settings from tsconfig.json #2543

Septh opened this issue Sep 13, 2022 · 8 comments
Labels

Comments

@Septh
Copy link

Septh commented Sep 13, 2022

Hi,

Using esbuild v0.15.7 on Windows 10 x64 with Node 16.15.0, the following code:

import esbuild from 'esbuild'

try {
    const result = await esbuild.build({
        absWorkingDir: process.cwd(),
        stdin: {
            contents: [
                "import path from 'node:path'",
                "import esbuild from 'esbuild'",
                "import something from './somefile.js'",
            ].join('\n'),
            loader: 'js',
            resolveDir: process.cwd(),
        },
        bundle: true,
        platform: 'neutral',
        format: 'esm',
        target: 'esnext',
        treeShaking: false,
        minify: false,
        external: [ 'node:path', 'esbuild', './somefile.js' ],
        // logLevel: 'debug',
    })
    console.dir(result)
}
catch {}

works as expected:

node .
// <stdin>
import path from "node:path";
import esbuild from "esbuild";
import something from "./somefile.js";
{ errors: [], warnings: [] }

However, if I change loader to ts in stdin option, ie.: loader: 'ts', then the output is empty:

$ node .

{ errors: [], warnings: [] }

Trying other values for the platform, format, target, treeShaking and/or minify options has no effect.

@hyrious
Copy link

hyrious commented Sep 13, 2022

This is because in ts mode all import names coule be a type, esbuild will strip out these names if they are not being used in the code or they work like a type (e.g. import type { A } from ...). This matches the official typescript compiler in most of the cases since esbuild does not really have a type system.

If you want to keep unused names in ts mode, you can tweak tsconfig to include preserveValueImports: true and/or importsNotUsedAsValues: preserve.

@Septh
Copy link
Author

Septh commented Sep 13, 2022

Oh, I see. I assumed esbuild would only discard type imports (e.g., import type { ... } and import { type ... }), but that makes sens.

Closing this, then.

Thanks!

@Septh Septh closed this as completed Sep 13, 2022
@Septh Septh reopened this Sep 13, 2022
@Septh
Copy link
Author

Septh commented Sep 13, 2022

Reopening because actually, it still doesn't work. 🤣

Keeping the stdin option, I still get an empty result.

Using the entryPoints option, ts-loader seems to ignore treeShaking being set to false:

$ node .
// test.ts
import "node:path";
import "esbuild";
import "./somefile.js";
{ errors: [], warnings: [] }

Edit: tsconfig.json is

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "importsNotUsedAsValues": "preserve",
    "preserveValueImports": true
  }
}

@hyrious
Copy link

hyrious commented Sep 13, 2022

It seems that the filename <stdin> has some special treatments in esbuild, here's a simple reproduction:

import esbuild from "esbuild";

console.log("--- sourcefile: <stdin>");
await esbuild.build({
  stdin: {
    contents: `import a from 'a'`,
    loader: "ts",
  },
});

console.log("--- sourcefile: a.ts");
await esbuild.build({
  stdin: {
    contents: `import a from 'a'`,
    sourcefile: "a.ts",
  },
});

Outputs:

--- sourcefile: <stdin>

--- sourcefile: a.ts
import a from "a";

You can add bundle: true and external: ['a'] too, the result will be the same -- <stdin> seems to always miss some behaviors.

@evanw
Copy link
Owner

evanw commented Sep 13, 2022

The behavior of removing unused imports is just how TypeScript works. You can see that for yourself here, with the official TypeScript compiler:

https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBGAhjAFnAZlCI4CIB2EAJgKYBcSquA3AFCiSxwkDOARgK7AA2RGWOXK048iNeuGjwW2EqmD4A5v2x4AdAHoZIEuh4k1AKxY0gA

You have to set preserveValueImports to true if you want to preserve unused imports:

https://www.typescriptlang.org/play?preserveValueImports=true#code/JYWwDg9gTgLgBGAhjAFnAZlCI4CIB2EAJgKYBcSquA3AFCiSxwkDOARgK7AA2RGWOXK048iNeuGjwW2EqmD4A5v2x4AdAHoZIEuh4k1AKxY0gA

Again, this has nothing to do with esbuild. This is how the people who designed TypeScript have decided that TypeScript should work.

It seems that the filename <stdin> has some special treatments in esbuild, here's a simple reproduction:

I believe you are mistaken. Your first example sets loader: "ts" while your second example omits loader, which then defaults to "js". So you are just comparing TypeScript to JavaScript. The sourcefile parameter has no effect on the loader. It only affects the name used in error messages and in source maps (as documented here).

@Septh
Copy link
Author

Septh commented Sep 14, 2022

Hi @evanw, thanks for joining the party :)

The problem here, is that esbuild doesn't seem to look for tsconfig.json when input is <stdin>, even though the tsconfig option is correctly set.

And when using an actual file as entry point, preserveValueImports: true does preserve the imports, but not the values as shown in my previous comment They seem to have been treeshaken despite the treeShaking option being set to false.

@evanw
Copy link
Owner

evanw commented Sep 14, 2022

The problem here, is that esbuild doesn't seem to look for tsconfig.json when input is <stdin>, even though the tsconfig option is correctly set.

This is a feature request. I can rename this issue to be about this.

And when using an actual file as entry point, preserveValueImports: true does preserve the imports, but not the values as shown in my previous comment They seem to have been treeshaken despite the treeShaking option being set to false.

This is because you have bundling enabled. Bundling is an additional process that runs after transforming TypeScript to JavaScript. The preserveValueImports: true setting only affects transforming TypeScript to JavaScript, and has no effect on bundling. Bundling with esbuild only produces a whole module as output. It does not ever produce a partial module. So it doesn't matter whether or not unused import names are present or not in an import statement because they are not observable.

@evanw evanw changed the title ts-loader produces empty bundle Feature request: allow stdin to pick up settings from tsconfig.json Sep 14, 2022
@Septh
Copy link
Author

Septh commented Sep 15, 2022

This is a feature request. I can rename this issue to be about this.

Thank you.

It would be great too if the build/buildSync methods also honored the tsconfigRaw option as an alternative to tsconfig.

This is because you have bundling enabled.

I would argue that the behavior is different when the js loader is used -- see op :)

Unless I'm mistaken, enabling bundling is mandatory in my use case because otherwise esbuild does not fire the onResolve() plugin hooks (presumably for performance?).

I've not shown that plugin code here but what I'm trying to achieve in the first place is to transpile TS code and also rewrite the imports. So I guess having esbuild load tsconfig (or support tsconfigRaw 😃 ) when stdin input is used would be enough for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants