-
Notifications
You must be signed in to change notification settings - Fork 650
rfc: runtime API #3036
rfc: runtime API #3036
Conversation
Deploying with
|
Latest commit: |
f08d0e5
|
Status: | ✅ Deploy successful! |
Preview URL: | https://375ed51a.tools-8rn.pages.dev |
Branch Preview URL: | https://rfc-apis-rfc.tools-8rn.pages.dev |
Comments from my initial reading:
|
I left that out on purpose because I didn't know the specifics of the architecture. Is it something that you can confirm now or is it too soon? For example
Yeah, that's true, although providing a path for some content might seem less intuitive, especially for less experienced developers. Also, if we compare this decision against other tools ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thank you @ematipico for this awesome write-up.
I very much like this API, particularly splitting it into ..Files
, ..Content
methods.
I added a few inline questions with clarification questions. Two overarching that I have at the moment are:
- Exposing
lintFiles
andlintDirectories
requires that Rome is limited to run in environments where it's possible to read files. Is Rome's API different in environment that doesn't support IO (e.g. browser, if we're ignoring virtual filesystems) - It may be interesting to add a few example use cases for the different API's and how they would be used in that case. For example, how would a Jest pretty-printer integration look like? Are there any other immediate use cases that you are aware of that we could use to verify our API, by providing a few sample implementations?
rfcs/002-runtime-api.md
Outdated
|
||
const rome = new Rome(); | ||
const content = "function f() { return {}}"; | ||
const result = rome.formatContent(content, { fileType: FileType.Js }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I very much like the idea of having different methods to format a file, directory, or some content (to e.g. integrate into Jest).
You mentioned that Rome automatically infers the configuration. How does that work with the fileContent
API. Does it resolve the configuration relative to the cwd
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might be wrong, and this is only a theoretic guess: if the script is run from where the package.json is, then the configuration is automatically picked up, because that's how the CLI works too.
If the premise is correct, then yes, we will need of a cwd
as base directory, and from there we can pick the configuration. A script can be run from anywhere, even inside a Rome project but not from the working directory.
Thank you raising it, I completely missed this scenario.
import { Rome } from "rome"; | ||
|
||
const content = "function f() { return {} }"; | ||
const rome = new Rome({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You outline that Rome either starts a new process or runs using WebAssembly. What's your proposal for managing the lifetime of the webassembly module (GC?) or the spawned subprocess? Does the Rome
object need a shutdown
/stop
method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't an answer to this question, the design of the backend is still in the works and it might change. Maybe @leops can chime in, he might have some answer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As with async methods, the design of the underlying backend isn't entirely set down yet so I would include a shutdown method just in case. To give more detail on this:
- For WASM the
Module
andInstance
objects are managed by the runtime GC, but theWorkspace
bridge object is not: creating a workspace allocates native memory in the WASM instance that needs to be reclaimed by calling thefree
method automatically generated bywasm-bindgen
- For a native child process we'd either need to send a shutdown signal if the native process is running in "attached" mode, or cleanly close the IPC connection if connected to a daemon server
rfcs/002-runtime-api.md
Outdated
const ast = rome.parse(content); // not part of this paragraph | ||
const new_content = rome.format(content); // not part of this paragraph | ||
const diagnostics = rome.check(content); // not part of this paragraph |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this section outdated? It seems that the APIs are formatContent
or lintContent
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's outdated, yes. But it was mostly to show off how we can use multiple features/APIs in the same snippet.
import { Rome } from "rome"; | ||
|
||
const rome = new Rome(); | ||
const result = rome.lintFiles(["./path/to/file.js"]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's your reasoning behind naming these methods lint
vs. the CLIs terminology that uses check
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. Mostly two reasons:
- we're exploring untouched ground, even for Rome classic, there linter and formatter were tight together (and issues were rising up); if we want to increase adoption, we should be more flexible. A user should be able to use the tools separately if they want;
- I envision a
check
command (like Rome classic) that does multiple checks, and alint
command that does only linting. This command is inline with theformat
command
Hence, why the API. We can also have a .check
API, it's very straightforward.
rfcs/002-runtime-api.md
Outdated
import { Rome } from "rome"; | ||
|
||
const rome = new Rome(); | ||
const result = rome.parseFiles(["./path/to/file.js", ""]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are the use cases for parsing multiple files? How does a user get the CST for a specific file (I imagine this method must either return an array OR object). My recommendation would be to limit the API to parsing a specific file.
This raises another question. What is the API proposing to use as file identifiers. Paths alone may not be unique enough in case the user provided path is a symlink to another file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason was mainly to keep it in line with the other APIs. I don't see anything wrong limiting it to a single file. I didn't include the CST mainly because we promote the AST (see the VSCode extension for example). If we want to give access to both, we should align this choice with the VSCode extension too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used CST&AST as synonym. My main concern here was about what use cases there are for parsing multiple files and how the result structure would look like (one huge object where paths are keys?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it doesn't make much sense to parse multiple files. We can limit the API to a single file then.
How do you envision that caching works for the content methods. Is there no caching, or can users provide an optional explicit id/cache key (which could be a virtual path: virtual://a/b/c.js) |
These APIs are mostly for Node.js, but I can see the future problem. We have options here:
Note: I don't know if both options (especially the second) are doable
I am open to proposal, because I have no idea how to make a jest pretty-printer. An idea might be a plugin for webpack (like |
You may need to spend some time familiarizing yourself. Jest uses pretty-format under the hood. The documentation has a section on how to customize formatting in jest as well as on how to write a plugin The main challenge that I see is that many such plugin architectures don't support explicit lifetimes (beyond what a GC provides) EDIT: It seems that Jest only uses Prettier for inline snapshots and I don't think that is configurable except by providing a node module that mimics prettier's API Edit2: Other use cases |
I spent a little more time thinking about the API and have some more thoughts that may need additional exploration but can be deferred to later or we can decide that we don't want to pursue the idea.
|
I don't think a client should be aware of anything that regards the communication with Rust Workspace. I think that exposing this information would make the APIs more complex, especially for beginners. Our internal architecture should stay internal. We should decide which kind of communication we should have based on the API. For example, initially we decide that the API
Not part of this RFC, we can follow up here. I don't have an answer now.
Not part of this RFC, we can follow up here. Probably we will reimplement the same logic we had in Rome classic. |
I agree that we should for good defaults but I do believe that it should be possible for users to change the default. For example, it may be the most performant to spawn a new CLI for each This is why I believe we should expose some sort of control to the user and this may influence how our API looks like significantly which is why I think it's worth to explore the idea at least a bit. |
The only non-intrusive and friendly way I see using these suggestions is by introducing a different sets o APIs, different from the ones proposed in the RFC. An example: import { workflow } from "rome";
await workflow.withProcess(async romeInstance => {
romeInstance.formatContent("");
}).run()
await workflow.withDaemon(async romeInstance => {
romeInstance.formatContent("");
}).run()
await workflow.withWasm(async romeInstance => {
romeInstance.formatContent("");
}).run() We can explore this proposal in another RFC. |
Using closures has the downside that this isn't working well in situations where an API exposes An alternative could be
Or @leops proposed an API where it's possible to pass an optional
Or the deamon could be passed in the constructor?
One thing we may need to consider with async constructor methods is that not all plugin APIs allow for calling async methods. |
RFC updated:
Not yet confirmed but evaluated:
To evaluate and implement (they require changes in the Rust code):
|
Summary
Closes #3034
Rendered page
This is a follow up of closed PR: #3014
@MichaReiser raised some questions, which I am going to answer below
In a sense. It's actually the Rust Workspace that loads the configuration. The runtime might be able to override the defaults.
No, this is not an alternative API, it's a problem that is already been solved in the playground, for example. This will reflect a future command, where the CLI will read from
stdin
.Sure, thank you.
Yes, it's a good call. I used this feedback and applied it to the RFC.
Yes, done in the RFC.
Yes,
format
is too broad. I believe we should have specific APIs for each case. In this case aformatRange
API.I think it's preferred to have specific APIs for each specific usage. We don't have cursor functionality in the Workspace, so I didn't consider it for the proposal. I wouldn't make much sense the propose if I don't know if we're able to support it and if it helps adoption.
Prettier's API are not in sync with its CLI. With the CLI you can format files and folders. With the APIs the user is in charge reading the content of those files and pass the content the APIs.
I don't know the reasons of this design, but I believe I/O operation should be delegated to our workspace (at least give some API where Rome can do that), and I also believe that the APIs should resemble the CLI too.
Test Plan
No test involved