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

Add JSX support built on the JS backend #21206

Merged
merged 12 commits into from
Aug 7, 2024
Merged

Add JSX support built on the JS backend #21206

merged 12 commits into from
Aug 7, 2024

Conversation

tobni
Copy link
Contributor

@tobni tobni commented Jul 25, 2024

This is a small change for the JS backend, as the tree-sitter parser already supports JSX.

This is a fairly big decision w.r.t how the js backend will evolve with typescript and other javascript transpile formats. Since many tools that support js also supports other kinds of files, the JSRuntime... will be used to refer to the files that can be treated as a source code file for some node-ish runtime who either processes the AST or executes actual programmes.

Clarifiying examples:

  1. A linter that can process any js-esque file should accept JSRuntimeSourceField
  2. All test sources should use JSRuntimeDependencies
  3. Files that can cross import (post bundling) different transpile formats should use JSRuntimeDependencies and JSRuntimeSourceField. Think typescript, JSX.
  4. A checker that can only process typed vue-templates should not accept JSRuntimeSourceField.

This PR together with #21176 essentially gives pants "react" support.

@tobni tobni added backend: JavaScript JavaScript backend-related issues category:new feature labels Jul 25, 2024
@tobni
Copy link
Contributor Author

tobni commented Jul 25, 2024

The motivation for providing a separate target is that plugin authors can distinguish between the sources.

@tobni tobni self-assigned this Jul 25, 2024
@tobni tobni requested a review from sureshjoshi July 25, 2024 12:05
Copy link
Contributor

@huonw huonw left a comment

Choose a reason for hiding this comment

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

Thanks so much for continuing to plug away at this work! This is awesome

This PR together with #21176 essentially gives pants "react" support.

🎉

Files that can cross import (post bundling) different transpile formats should use JSRuntimeDependencies and JSRuntimeSourceField. Think typescript, JSX.

More questions to exploring the problem space:

  1. AIUI, bundlers allow importing other resources too, e.g. import './example.css' or even images (https://vitejs.dev/guide/assets), do we have thoughts on how those might be modelled? Maybe as an inferred JSRuntimeDependencies on a file or resource target? Or maybe something else?
  2. How does pants test path/to/some.test.jsx work? I see the test project has a test JSX-using test file in it, but it's not obvious to me how this actually executes? I'd be expecting more plumbing to make that work, so I'm surprised it's not here!

docs/notes/2.23.x.md Outdated Show resolved Hide resolved
src/python/pants/backend/jsx/target_types.py Show resolved Hide resolved
required_fields = (JSSourceField,)
required_fields = (JSRuntimeSourceField,)
Copy link
Contributor

Choose a reason for hiding this comment

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

On the target discussion, for prettier specifically, it supports things that are very non-JS-shaped, e.g. HTML, GraphQL, Markdown, JSON, Yaml.

Two questions:

  1. Specifically for prettier: do we currently have any thoughts on how to make Pants able to use Prettier to (optionally!) format non-JS files too?
  2. Generalising/another clarifying example: are there other tools in the JS ecosystem that might behave similarly and work with more file types? Do we need to think about how to handle them?

Copy link
Contributor Author

@tobni tobni Jul 27, 2024

Choose a reason for hiding this comment

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

  1. No. I have no use case myself but I suppose a contributor could open up the prettier plugin to more languages later by adding a union-layer? Alternatively prettier seems like the kind of tool one might want to run "target-less" and just glob?
  2. The typescript compiler can check other files than .ts. It can be run on .js, .jsx and .tsx as well Test runners have the same capability, so the test goal also applies. Those are the usecases I'm primarily intrested in, and I think the "JS-ish source" model works well there.

Copy link
Contributor Author

@tobni tobni Jul 27, 2024

Choose a reason for hiding this comment

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

I also think it is fun that you implied json is non-js shaped 😏 I get what you're saying though, you do not run a json file.

Copy link
Contributor

Choose a reason for hiding this comment

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

Alternatively prettier seems like the kind of tool one might want to run "target-less" and just glob?

Hm, maybe, yeah.

The typescript compiler can check other files than .ts. It can be run on .js, .jsx and .tsx as well Test runners have the same capability, so the test goal also applies. Those are the usecases I'm primarily intrested in, and I think the "JS-ish source" model works well there.

👍

Copy link
Member

Choose a reason for hiding this comment

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

I think I wrote the Prettier plugin a while back, and we did have the discussion about how it could be used target-less, but that (d)evolved into a much larger discussion about the needs for targets and target-less and so on and so forth.

There are a few tools where I think we just want to defer to whatever the tool can handle - and I don't know how we do that in the most pants-esque way

pass


class JSSourceField(SingleSourceField):
class JSRuntimeSourceField(SingleSourceField):
"""A source that is javascript at runtime."""
Copy link
Contributor

Choose a reason for hiding this comment

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

Feeding into the PR description. Thinking about this framing: AIUI, there's some tools that can run TypeScript natively (e.g. Bun; Node (experimentally) as of nodejs/node#53725), so a .ts file can be run without being/becoming JS.

Does that play into the broader consideration, or maybe it's not a big-picture issue (e.g. maybe just this doc string is (very slightly) inaccurate)?

Copy link
Contributor Author

@tobni tobni Jul 27, 2024

Choose a reason for hiding this comment

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

I suppose it is a point of semantics, but bun and node do not run typescript. They strip (node) or compile-down the (bun) types and run the resulting javascript.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, I think my key point is that there's tools for which the file run is the .ts one, without a bundling/packaging/compilation step to turn it into real JS first, so it's becomes harder to say that it "is Javascript" at runtime.

Brainstorming: I wonder if we could instead flip this around to be tool-focused using a union somehow (I'm not fully familiar with whether that's possible, though). E.g. register various source types as members of "can be passed to tsc ...", "can be passed to node ...", "prettier ...", "biome ...", etc.

Copy link
Contributor Author

@tobni tobni Jul 28, 2024

Choose a reason for hiding this comment

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

For specific tools were we can have a field set and union to target the tool, yes.

Not sure how that works out for node_test_script. The TestFieldSet is already a union that requires a SourcesField unique to each union member.

In other words, I think at some point we are required to "boil down" to a unique source field type to "end up" with a JSTestRequest. If it was generic on e.g SourceField I think the union is not specialized enough? But idk 🤷‍♂️

Copy link
Contributor

Choose a reason for hiding this comment

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

In other words, I think at some point we are required to "boil down" to a unique source field type to "end up" with a JSTestRequest. If it was generic on e.g SourceField I think the union is not specialized enough? But idk 🤷‍♂️

IDK either 🤷‍♂️

.gitignore Outdated Show resolved Hide resolved
testprojects/src/js/hello_react/src/test/index.test.js Outdated Show resolved Hide resolved
@tobni
Copy link
Contributor Author

tobni commented Jul 27, 2024

Thanks so much for continuing to plug away at this work! This is awesome

This PR together with #21176 essentially gives pants "react" support.

🎉

Files that can cross import (post bundling) different transpile formats should use JSRuntimeDependencies and JSRuntimeSourceField. Think typescript, JSX.

More questions to exploring the problem space:

  1. AIUI, bundlers allow importing other resources too, e.g. import './example.css' or even images (https://vitejs.dev/guide/assets), do we have thoughts on how those might be modelled? Maybe as an inferred JSRuntimeDependencies on a file or resource target? Or maybe something else?
  2. How does pants test path/to/some.test.jsx work? I see the test project has a test JSX-using test file in it, but it's not obvious to me how this actually executes? I'd be expecting more plumbing to make that work, so I'm surprised it's not here!
  1. Yes, that is true. For now I believe pants will pull in resources and files into js-backend-sandboxes as long as there is a dependency to them. That dependency is inferred OOTB with javascript: jsconfig (tsconfig) parsing for dependency inference #21176, because I didn't filter the target sources on any specific field (but you have to include extensions e.g .css as the extension substitution is only for js).
  2. I missed this because I've never seen jsx tests before (even though I just wrote one in this pr 🤪 ). The common case is to probe your transpiled JSX using some JS-framework, you rarely write markup in tests... But it is for sure a thing so test-targets should be included in this PR. The "plumbing" required should only be that the target in question inherits the yet to exist JSRuntimeTestSourceField analogous with the JSRuntimeSourceField, I guess.

@riisi
Copy link
Contributor

riisi commented Jul 30, 2024

Excited for this 🎉

suppose it is a point of semantics, but bun and node do not run typescript.

Just a heads up that it looks like Node will soon run typescript - nodejs/node#53725

Comment on lines +86 to +99
class JSXTestDependenciesField(JSXDependenciesField):
pass


class JSXTestSourceField(JSXSourceField, JSTestRuntimeSourceField):
expected_file_extensions = JSX_FILE_EXTENSIONS


class JSXTestTimeoutField(TestTimeoutField):
pass


class JSXTestExtraEnvVarsField(TestExtraEnvVarsField):
pass
Copy link
Contributor Author

@tobni tobni Jul 31, 2024

Choose a reason for hiding this comment

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

I'm honestly not sure why I declare subclasses for each field, but it seems prevalent in all backends. A lot of boilerplate, though.

@tobni tobni requested a review from huonw August 1, 2024 07:48
docs/notes/2.23.x.md Outdated Show resolved Hide resolved
Co-authored-by: riisi <rhysmadigan@gmail.com>
Copy link
Contributor

@huonw huonw left a comment

Choose a reason for hiding this comment

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

Looks good.

Approving to keep us moving forward, because it seems like the best way to explore the best option for how to model these targets is to starting doing something and work out if we like it or not!

pass


class JSSourceField(SingleSourceField):
class JSRuntimeSourceField(SingleSourceField):
"""A source that is javascript at runtime."""
Copy link
Contributor

Choose a reason for hiding this comment

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

In other words, I think at some point we are required to "boil down" to a unique source field type to "end up" with a JSTestRequest. If it was generic on e.g SourceField I think the union is not specialized enough? But idk 🤷‍♂️

IDK either 🤷‍♂️

@tobni tobni merged commit 6c41074 into pantsbuild:main Aug 7, 2024
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend: JavaScript JavaScript backend-related issues category:new feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants