-
-
Notifications
You must be signed in to change notification settings - Fork 66
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
[FR]: types_only()
#319
Comments
Refs #48. The renderer should not depend on `rules_prerender` directly, because that would pull in the JavaScript sources into `.../execroot/rules_prerender/node_modules/rules_prerender/` of the generated binary. The `rules_prerender` dependency should actually be linked against the user's implementation in their own workspace (`//:node_modules/rules_prerender`), not `@rules_prerender//:node_modules/rules_prerender`. However, we still want type checking of `rules_prerender` usage, even if the actual package gets injected via the main function parameter. `types_only()` accomplishes this by dropping all the JS sources and only propagating TypeScript declarations. This prevents the `rules_prerender` package from `@rules_prerender//:node_modules/rules_prerender` leaking into the renderer binary, so there is only one definition of it which comes from the user's workspace. `import type` also helps restrict that the dependency should _only_ ever be used as type. Unfortunately, `npm_link_package()` emits a `declarations` field which points to the NPM package's `TreeArtifact`, and that tree contains `*.js` files. That means TypeScript actually _does_ receive `*.js` files and is generally ok with value imports of `rules_prerender`. There's also no easy way of removing `*.js` files from that tree. This is an unfortunate foot-gun, but the most important thing is that the `*.js` files don't persist at runtime, even if the compiler may allow it. I filed a feature request to `@aspect_rules_ts` with this general idea and also explains more the `npm_link_package()` foot-gun: aspect-build/rules_ts#319.
We have a moderately large project with thousands of rules and tests. We auto generate all our dependencies and we use consistent-type-imports. The combination of all these things makes However, to ensure that we aren't over caching, we needed to make one change to the implementation provided above.
With that change, we get the appropriate caching behavior. A test (eg) will wait for a dependent ts_project (types_only) rule to build, but then if it only depends on |
This feature can also be useful for the cases where you have test targets that take It is important to note, however, that under Bazel the better pattern is to pre-transpile all .ts and .tsx files with |
Just discovered that Angular has an implementation of this called |
The proposed implementation here is no longer viable as of Instead I went with a different approach of using a regular dependency, created an NPM package, and then afterwards "pruned" the type-only dependency after the fact. It's not super elegant, but works well enough. dgp1130/rules_prerender@3893834...313314e My implementation is limited to pruning from An alternative approach is to just It would definitely be great for this to be a supported feature so we don't need these workarounds. |
What is the current behavior?
ts_project()
generally provides aJsInfo
with.d.ts
declarations and.js
sources, which is typically what you want in most situations. However, sometimes you want to type check against a dependency without including the implementation of that dependency. This can happen because you expect the implementation to be provided as an input or linked separately. In my particular situation, I would like to type check a tool against my ownnpm_package()
built at HEAD but not include it in the output tool's implementation, since it will be dynamically linked against the samenpm_package()
in the user's workspace. See dgp1130/rules_prerender#48 (comment) for more context.You can technically do this today with
emit_declaration_only
however this has two problems when applied to this use case:ts_project()
, notnpm_package()
.ts_project()
dependency, not the usage of that dependency. When packagefoo
is built, it should contain its full implementation. However a particular dependent of it may want to limit itself to just consuming the types offoo
, rather than the full*.js
.You can also do this with
import type
which makes TypeScript prevent value references to that import. However there is nothing stopping misguided developers from adding a value import in the future. Alsoimport type
is not known to the Bazel layer and does not prevent emitting the JavaScript. This will still pull JavaScript from the type-only dependency, even if the import is elided at runtime. This can complicate dependency resolution, particularly in cases of multiple workspaces. You'd need to pass through a bundler to fully tree shake the unused JS implementation, and that shouldn't be needed for something like this.Describe the feature
I propose a
types_only()
rule which effectively strips the JS implementation from theJsInfo
provider. You would use it like so:It implementation is pretty straightforward:
Since
npm_link_package()
returns aJsInfo
as well, this same implementation works there. However it has the caveat that NPM packages are implemented with aTreeArtifact
in thedeclarations
/transitive_declarations
property. ThatTreeArtifact
still maintains*.js
sources, even if all the other properties of the provider are dropped. That means that if you usetypes_only()
on annpm_link_package()
,tsc
will actually pass though the output won't include the*.js
files. Ideally the*.js
files would be removed from the NPM package and thepackage.json
would be limited to just.d.ts
, though I don't see an easily feasible way of doing that. Not sure what the best approach is there beyond accepting the foot gun thattsc
will allow value references for dependents oftypes_only()
of NPM packages.Fund our work
The text was updated successfully, but these errors were encountered: