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

feat(typescript): add ts_project rule #1710

Merged
merged 6 commits into from
Mar 24, 2020

Conversation

alexeagle
Copy link
Collaborator

This is a very thin layer on top of vanilla tsc

examples/react_webpack/BUILD.bazel Outdated Show resolved Hide resolved
progress_message = "Compiling TypeScript project %s" % ctx.file.tsconfig.short_path,
)

runtime_files = depset(ctx.outputs.js_outs + ctx.outputs.map_outs)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this is important by if a src file is a .js dependency might it also need to be included as a runtime file?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you have .js inputs, does tsc write them to the our dir? or does it only emit diagnostics?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah this needs a test case for .js inputs...

Copy link

@Toxicable Toxicable left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excited to see this going in!
Also nice to see it can work as a stand alone tsc compiler - no project references


if srcs == None:
srcs = native.glob(["**/*.ts"])
tsconfig = name + ".json"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if im a fan of this.
Could we possibly change the logic to default to tsconfig = ctx.testonly ? 'tsconfig-test.json' : 'tsconfig.json'.
But override with the tsconfig speciffied by the user if exists.
Usually we'd have libs where their "primary target" is a ts_library and this will use the same name as the dir, making it easier to reference.
This here prevents using a name that's different from your tsconfig, right?
Is there another reason why this might be a good idea?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add a comment in the code explaining this.

Bazel's `name` property is arbitrarily controlled by the user. But here we constrain it to match the tsconfig file.
The reason for doing this is so that BUILD file generation can be trivial. If you know the path of a referenced tsconfig file, you know what label to use to include it in the deps of another rule.
If we allowed users to control it, then BUILD file generation would need heuristic semantics to do bazel query and try to figure out which label gives a target that is usable as a dep.

And on the rule docs

You can use a Bazel alias() to give an additional label for this typescript project compilation

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also note you could import the raw ts_project rule from the internal path, which I think we should leave undocumented, but it gives you the escape valve to avoid this macro logic.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yeah I see the constraint here, sounds good to me with the extra docs and code gen considered

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed with @gregmagolan - even if users can control tsconfig and name attributes independently, it's still pretty easy for the generator to print all ts_project rules in the referenced package to find the right one. So tsconfig is now independent (though we recommend naming it the basename of the tsconfig.json and that default is convenient)

# ts, tsx, js, jsx, json
"srcs": attr.label_list(allow_files = True),
"extends": attr.label_list(allow_files = [".json"]),
"tsc": attr.label(default = Label("@npm//typescript/bin:tsc"), executable = True, cfg = "host"),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'd love to see if this works by replacing this with ngtsc

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I played with it briefly and something didn't work, please give that a try if you have time :)

examples/react_webpack/BUILD.bazel Outdated Show resolved Hide resolved
@alexeagle alexeagle force-pushed the ts_project branch 2 times, most recently from 1b28486 to 91f43ab Compare March 17, 2020 01:44
Copy link

@Toxicable Toxicable left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@alexeagle
Copy link
Collaborator Author

/cc @tjgq @evmar

Copy link
Contributor

@evmar evmar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really have any context here. Why this in addition to a tsc rule that is also different than ts_library (?)

"--diagnostics",
"--extendedDiagnostics",
# Prefer to show gory details rather than save space in console
"--noErrorTruncation",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We recently discovered that tsc can OOM when you set this flag, because without error truncation error generation can infinite loop.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good tip, thanks. I only set this here from reading through the tsc docs and it sounded helpful, will remove

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wait they fixed it by actually limiting it with the flag, heh microsoft/TypeScript#37461

@alexeagle alexeagle force-pushed the ts_project branch 3 times, most recently from 68368c4 to b4eb2e0 Compare March 22, 2020 02:48
This is a very thin layer on top of vanilla tsc
@alexeagle alexeagle force-pushed the ts_project branch 2 times, most recently from cee6eb2 to d738f42 Compare March 23, 2020 04:34
this fixes a bug on Windows where Bazel deletes known outputs like .js
but then tsc doesn't write the .js file again on next action because
the tsbuildinfo file indicates it is up-to-date
# If you don't want to write //:tsconfig as the label for the TypeScript compilation,
# use an alias like this, so you can write //:compile_ts instead.
alias(
name = "compile_ts",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: use tsconfig attribute and use "compile" as ts_project name to avoid alias?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops, this BUILD file should have been updated for the change of declaration default to False too. will boil it down to the minimum


load("@build_bazel_rules_nodejs//:providers.bzl", "DeclarationInfo", "NpmPackageInfo", "run_node")

_ATTRS = {
Copy link
Collaborator

@gregmagolan gregmagolan Mar 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should add "args" in general to rules to allow users to pass their own arguments as

  1. config files do not always support all arguments. I recently added "args" to rollup for this reason so
    Angular could pass --silent which is command line only
  2. users may want to override config file settings with command line arguments in some cases
    General pattern is
    "args": attr.string_list(
        doc = """Command line arguments to pass to <tool name>. Can be used to override config file settings.

and

# See CLI documentation at https://rollupjs.org/guide/en/#command-line-reference
args = ctx.actions.args()

# Add user specified arguments *before* rule supplied arguments
args.add_all(ctx.attr.args)
where `args` is passed to `run_node`

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added args pass-through with a delightful test coverage

# that compiler might allow more sources than tsc does.
"srcs": attr.label_list(allow_files = True, mandatory = True),
"extends": attr.label_list(allow_files = [".json"]),
"tsc": attr.label(default = Label("@npm//typescript/bin:tsc"), executable = True, cfg = "host"),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: set DEFAULT_COMPILER = "@npm//typescript/bin:tsc" as the string is in two places

It controls writing the .tsbuildinfo output
For example, `tsc = "@my_deps//typescript/bin:tsc"`
Or you can pass a custom compiler binary instead.

declaration: if the `declaration` bit is set in the tsconfig.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: links to typescript docs for these 1:1 mappings for more info?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added above

Copy link
Collaborator

@gregmagolan gregmagolan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🌮 ^ 🌮

for dep in ctx.attr.deps
]),
),
DefaultInfo(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: short comment on rationale for only including runtime files in DefaultInfo?

Copy link
Collaborator Author

@alexeagle alexeagle Mar 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

    # DefaultInfo is what you see on the command-line for a built library,
    # and determines what files are used by a simple non-provider-aware
    # downstream library.
    # Only the JavaScript outputs are intended for use in non-TS-aware
    # dependents.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

# TODO: we could maybe filter these to be tsconfig.json or *.d.ts only
# we don't expect tsc wants to read any other files from npm packages.
deps_depsets.append(dep[NpmPackageInfo].sources)
elif DeclarationInfo in dep:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this just be an if? They seem orthogonal and should overlap anyway.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, I think this is true. Need to verify if we have coverage for it

outputs.append(ctx.outputs.buildinfo_out)

if len(outputs) == 0:
return []
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should still return transitive declarations & tsconfigs?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, took a while to add a test case to expose this...

@alexeagle alexeagle force-pushed the ts_project branch 3 times, most recently from af0229b to adb54d1 Compare March 24, 2020 22:10
Also add a test for an intermediate ts_project rule with no srcs
@alexeagle alexeagle merged commit 26f6698 into bazel-contrib:master Mar 24, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants