-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
support to output declaration files or .dts on build or service.transform #95
Comments
This is not something that I'm planning to support. The parser in esbuild skips over type annotations as if they were whitespace, so the AST doesn't contain any type annotations. This means esbuild doesn't have the information necessary to generate a .d.ts file. |
@evanw thanks for the reply, we can close this or reopen if soon its in your roadmap. |
@evanw I was just speaking with client at [redacted] (2nd or 3rd largest user of TS in the world). They believe you could potentially solve this by parsing files and stripping away implementation, in a second pass, but only keeping annotations and signatures. This isn't how the actual TS compiler does it, it's much more complex. However, according to [redacted], the above solution would work and be extremely fast. Another reason you might want to take up this issue is that esbuild would become THE go to way of compiling all things TS, not just apps. Food for thought. |
@evanw I would as well like to get support to generate .d.ts - I'm not Xnd largest user of TS, but still like to have that option. Maybe you could point into place where such support might be placed and I would try myself to add it? Of course if your vision for this tool is that it will not be supported, then of course will stop complaining about that. |
Also consider library authors that want to use esbuild to compile their TS library but still include *.d.ts files for users. Right now, there is an additional burden on the package author to complicate their build process by requiring multiple steps (esbuild + tsc), and reduces the overall value of esbuild by increasing the time to compile. I have experimented with creating a plugin that emits declaration files, but the typescript api is not well suited for parsing a single file at a time and the plugin api doesn't offer a way to do work after all files have been processed -- thus creating a bottleneck. @evanw Do you have any thoughts for library maintainers on this? Is your guidance to use a mutli-stage build? If I'm still forced to use the typescript compiler, is there still a compelling reason to use esbuild? |
+1 |
I think a reason is the bundled speed faster than other tools.
|
@ycjcl868 Have you found a way to speed up the declarations step? It seems silly that |
@mattrossman This is really a good point and a real struggle. You bundle the whole project in less than a second then you have to wait to emit types. Feels like I am doing something wrong. Another point when bundling external dependency, the code is already bundled successfully but I have to find a way to include the external types. |
Another fast TypeScript compiler ( |
I agree on this Issue - Webpack has a neat plugin to bundle declaration files neatly into a single-file declaration. I want to advocate esbuild as much as possible, and this seems like a really common sense feature to focus on. |
I really want ESBuild to output typings. I'd be able to ditch tsc completely and save several seconds on every build while watching in dev mode. |
I made a small plugin for doing this - esbuild-plugin-d.ts |
I found this while closing outdated issues. I'm writing a comment because I made a mistake.
It's not possible without implementing a full typescript type system. Btw, this is sort of extremely hard stuff and I'm not sure if it's a viable option for an open-source project. I used an enormous amount of time, but still not done. |
Hi guys, I am ok with this feature not being supported out of the box, but could it please be somehow documented, even just 1 sentence leading to the right documentation? As far as I understand this thread, I am supposed to fallback to |
Good point. I added this to the documentation: https://esbuild.github.io/content-types/#no-type-system. |
For the record I've done this eventually: "build": "yarn run build:common && yarn run build:types",
"build:common": "esbuild index.ts --outdir=dist --sourcemap --bundle --minify",
"build:types": "tsc --emitDeclarationOnly --declaration --project tsconfig.build.json" I am just discovering Esbuild, but it seems to work ok so far! Building the types is taking most of the build time. But since the Esbuild time is negligible, it still means a 40% cutdown on the build time, eg from 15 seconds with webpack to 0.24 seconds with Esbuild + 8 seconds of building the types. Edit: actually I had to dig a bit more for a neutral build : But that's unrelated to .d.ts, using |
I have created an independent repository to demo building full-stack, typed packages, with Esbuild and other tools: https://github.com/VulcanJS/npm-the-right-way I've used tsc directly for the typings with success, however at the moment the Esbuild version doesn't work in Next.js, somehow the imports are not reckognized. I'd be glad to get some help 🙏 , so we can figure a setup that works and use it as canonical example |
A couple of suggestions from my side: Use the
|
So I suppose rewriting the TS compiler is out of the question. Somebody on the swc thread mentioned
Personally, we're using the same constraint on our code as well, and I think it would be quite helpful even with that restriction. Unfortunately, that restriction is not enough. The ESLint rule only applied to exported symbols. It doesn't require explicit types for internal constants and functions. TS has a function bar<T>(arr: T[]) {
for (const a of arr) { if (typeof a === 'string') return a }
return x;
}
export function foo(x: string): ReturnType<typeof bar> {
return undefined as any;
} So we'd either have to ban the Would you even consider implementing a limited feature that imposes some restriction on the TS input files? |
Looks like the creator of SWC (@kdy1) is actively working on a fast type checker called STC. It's apparently not ready for primetime yet, but I'm mentioning it here for tracking purposes, and in hopes that some people reading this thread might want to pitch in and help get it over the finish line, as it would likely help esbuild offer this feature as well as other more full-featured TS support. |
It's not possible, even with explicitly specified with "normal" types, because you still have to track down where those types came from and determine if they're valid. A tool that is capable of doing that would effectively be a complete type checker. type BarStuff = ReturnType<typeof bar>
type OtherType = { foo: string; bar: BarStuff }
type ExplicitType = OtherType['bar']
export function foo(x: string): ExplicitType {
// ...
} It's really important to note, however, that even if
export const value: number = 'oh no!' into "use strict";
export const value = "oh no!"; And if it could emit type definitions like suggested above, it would then happily emit: export declare const value: number; // WRONG! There's no point in using TypeScript if nothing between you and production checks the types.
The first one is the time consuming part. Emitting the files is relatively quick: $ time ./node_modules/.bin/tsc -p tsconfig.build.json --emitDeclarationOnly
real 0m8.901s
user 0m14.255s
sys 0m0.590s
$ time ./node_modules/.bin/tsc -p tsconfig.build.json --noEmit
real 0m8.723s
user 0m13.855s
sys 0m0.637s So you wouldn't be saving 8.9s, only a maximum of 0.178s. Not trying to downplay the significance of |
I believe esbuild could support declaration bundles without too much hassle by stripping non-exported statements, exported function bodies, and exported variable initializers. If an exported function/variable isn't explicitly typed, assume |
@aleclarson You'll be excited to learn that there's an effort to officially support exactly this within the TypeScript project itself: microsoft/TypeScript#47947. The proposal is a flag called |
@aleclarson What's the point? It still doesn't absolve you of having to run
Other projects would import those types. If those use The flag @evanw mentions would at least enforce the new "rules", but... it would be done by using |
I can't speak for others, but for us, this could be useful when we have multiple projects where one project depends on another and we want to do quick dev / snapshot builds. Although I agree the risk of generating invalid type declarations that break things down the pipleline is high and hard to debug for other people without intimate knowledge of the build process, so this might not be worth it. I guess we really want a faster |
@ehaynes99 I run both |
I think esbuild generating .d.ts files would be great for monorepos, where:
In this case you can still be confident without real type checking, but still have the types locally in the consumers of other monorepo packages. |
<!-- Please make sure you have read the submission guidelines before posting an PR --> <!-- https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr --> <!-- Please make sure that your commit message follows our format --> <!-- Example: `fix(nx): must begin with lowercase` --> ## Current Behavior esbuild doesn't support the creation of declaration files (*.d.ts) and probably never will (see evanw/esbuild#95). Since declaration files are essential for published libraries, it would be great if @nx/esbuild:esbuild would provide an option to output them. ## Expected Behavior - Introduced a new boolean valued `declaration` option for the `esbuild` executor - If `declaration` or the tsconfig option [declaration](https://www.typescriptlang.org/tsconfig#declaration) is true, then the TypeScript compiler is used before esbuild to generate the declarations - The output directory structure of the declarations can be influenced by setting the TypeScript rootDir via the `declarationRootDir` option ## Related Issue(s) #20688 ### Additional Comment Please note that the generated declaration files directory structure is affected by the tsconfig `rootDir` property. For a library that doesn't reference other libraries inside the monorepo, the `rootDir` property can be changed freely. If a library inside the monorepo is referenced the `rootDir` property must be set to the workspace root. The `tsc` executor has a sophisticated check that automatically sets the `rootDir` to the workspace root if a library is referenced. https://github.com/nrwl/nx/blob/master/packages/js/src/executors/tsc/tsc.impl.ts#L104 This check is quite complex and specific to the `tsc` executor options. Therefore, it hasn't been included inside the esbuild implementation. The current implementation leaves it to the user to solve the edge case by removing the `declarationRootDir` option or by setting the `declarationRootDir` to `.`. In the future, it might make sense to generalize and use the `tsc` executor check.
<!-- Please make sure you have read the submission guidelines before posting an PR --> <!-- https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr --> <!-- Please make sure that your commit message follows our format --> <!-- Example: `fix(nx): must begin with lowercase` --> ## Current Behavior esbuild doesn't support the creation of declaration files (*.d.ts) and probably never will (see evanw/esbuild#95). Since declaration files are essential for published libraries, it would be great if @nx/esbuild:esbuild would provide an option to output them. ## Expected Behavior - Introduced a new boolean valued `declaration` option for the `esbuild` executor - If `declaration` or the tsconfig option [declaration](https://www.typescriptlang.org/tsconfig#declaration) is true, then the TypeScript compiler is used before esbuild to generate the declarations - The output directory structure of the declarations can be influenced by setting the TypeScript rootDir via the `declarationRootDir` option ## Related Issue(s) #20688 ### Additional Comment Please note that the generated declaration files directory structure is affected by the tsconfig `rootDir` property. For a library that doesn't reference other libraries inside the monorepo, the `rootDir` property can be changed freely. If a library inside the monorepo is referenced the `rootDir` property must be set to the workspace root. The `tsc` executor has a sophisticated check that automatically sets the `rootDir` to the workspace root if a library is referenced. https://github.com/nrwl/nx/blob/master/packages/js/src/executors/tsc/tsc.impl.ts#L104 This check is quite complex and specific to the `tsc` executor options. Therefore, it hasn't been included inside the esbuild implementation. The current implementation leaves it to the user to solve the edge case by removing the `declarationRootDir` option or by setting the `declarationRootDir` to `.`. In the future, it might make sense to generalize and use the `tsc` executor check. (cherry picked from commit 7f32d86)
Hi! Sharing my build & watch compatible setup, combined from some of the solutions presented above. {
"scripts": {
"start": "node dist/index.js",
"watch": "npm-run-all --parallel 'build:js --watch' 'build:types -w'",
"build": "npm-run-all --serial build:js build:types",
"build:js": "esbuild index.ts --bundle --outfile=dist/index.js --platform=node",
"build:types": "tsc --declaration index.ts --emitDeclarationOnly --outDir ./dist",
},
"devDependencies": {
"esbuild": "^0.24.0",
"npm-run-all": "^4.1.5",
"typescript": "^5.7.2"
},
} Would be nice if more official/built-in support comes for this down the line, but this setup works cross-platform and has minimal setup! |
in typescript you can add on tsconfig.json "declaration": true, it can output or produce dts files. hope you can support on this :)
by the way nice work.
The text was updated successfully, but these errors were encountered: