Rome: A Linter for JavaScript and TypeScript
romefrontend.dev
romefrontend.dev
On the other, as someone who used to do some "JS platform" work at a tech company, I really don't want Rome to catch on. Yet Another Standard is really painful for the ecosystem, and while it's obnoxious that you need so many tools, at least we've finally settled (mostly) on good answers for each vertical. TS, ESlint, Prettier, Webpack, Babel.
Now, would it be nice if there were an underlying engine/server that all tools could use to share an AST without reparsing everything? Sure, I guess. Would it also be nice to have one tool that wraps the others, at least when you're getting started? Yes, though I think create-react-app has blazed that trail pretty well.
The thing is, all those tiny little details from Prettier and ESLint and Webpack really matter – Rome will take years to achieve the number of lints that ESLint has, and even more years for the community to agree on which ones matter. Similarly, every corner-case of prettifying will have to be considered anew, and every edge case of bundling rebuilt or reimagined.
I love to see a talented person take on something insanely ambitious, so I wish him luck and hope he proves me wrong. But if Rome succeeds, it'll be a big pain for a lot of people as they try to port everything over, and for the community as they grapple with dueling standards for how things should be done.
Rome being successful doesn't mean eliminating those tools, it's providing something valuable and giving people an option. If it's not for you then that's okay. Rome is early and is still evolving, including possible areas for extensibility.
I think a lot of people don't realize the sort of capabilities that they're missing out on by not having their tools work together, or sticking with old tooling that cannot innovate for legacy reasons (like Babel). I don't think it's harmful to the ecosystem to advocate for more consolidation, especially around tooling that not a lot of people either like to deal with, or have few maintainers in the first place.
I also think you have me (Sebastian McKenzie) confused with Sebastian Markbage from the React team.
Reading your comment, it sounds like you expect some people might use Rome for some things (say, linting and compilation) and preexisting tools for others (say, formatting and bundling). Is that your intent?
ie. For linting we also do dependency verification. So you might need to configure Rome if you put your dependencies on some weird non-standard place. Once we open bundling up, we'll already know how to resolve everything.
Each part should stand on it's own. It's not as if the linter we've released is worse, and the only selling point is that it's going to be part of a larger suite. It legitimately has features and separates itself from the alternatives. eg. extreme focus on useful error messages, powerful autofixes (that operate on an AST rather than insert strings like ESLint), proper caching for even better performance (ESLint doesn't offer a good solution here) etc.
Many are making the mistake in thinking that building Rome is just as time consuming and resource intensive as rewriting each of the tools it's meant to replace separately, it's not. Once we've validated the linter we've also validated the compiler (it's the same thing), our ability to watch files, analyze dependencies, integrate with your editor etc. Sharing so much fundamentally decreases the actual complexity of everything, not increases.
I think it's also important to note that I personally have written an extremely small amount of the implemented lint rules. Our API is just easier to use, and we've focused on setting up the tooling necessary to make writing them easy (although internally in our repo).
Check out https://github.com/romejs/rome/issues/341, https://github.com/romefrontend/rome/issues/20, and https://github.com/romefrontend/rome/issues/159, if you're interested in seeing how the progress was actually made in the implementation of those rules. The work was spread out over a really long time, and if given complete focus and proper coordination (I was not good at this and kind of let it be organized ad-hoc, which is actually how we got some amazing contributors), then it could have been completed in a fraction of the time.
There's been some overfocus on particular things like minimal configuration and the lack of extensibility but those are not hard requirements and will evolve over time, particularly as we get feedback and people demonstrate the requirements and restrictions they're under. The project understandably involves a lot of hubris, but I do believe that Rome isn't only valuable in aggregate and will have significant advantages even if you only decide to use one piece.
I've also done some work on Prettier in the past, so I'm curious about some details there too.
1. Do you plan to build a wadler-based formatter, similar to prettier? Will it be as opinionated as prettier, or based on a pluggable architecture? (I'll admit that I have yet to conceive of a viable way to make the latter work well).
2. Have you considered (or already built) an incremental parsing server for tools like the formatter? For example, when I add a statement at the bottom of the document, will it reparse the whole document, or be smart enough to notice that a change has been made that can use cached results for most of the AST? (I'm not sure if this is feasible for JS, especially for parsers that require line/col info).
In any case, I'll be following your progress with great interest!
- Prettier has to be integrated with ESLint to avoid conflicting formatting and for good devX
- TS config interferes with ESLint config ("this file is not part of any project" or conflicting unused var warnings)
- Webpack has to be integrated with TS to compile down to JS
- ESLint, TS and Webpack all need configuring to know about absolute imports
If you throw babel into the mix, complexity just explodes. Thankfully it's feasible to have a stable config that "just works", but it can be quite the time sink. I wouldn't be against something that takes this pain awayThe problem is, each little issue a developer has, often requires not just installing these, but then adding a whole bunch of plugins to make them work together. So whenever I start a project, my package.json is just already packed with a gazillion of devDependencies from @types/ to eslint and webpakc plugins.
The package has a bunch of automated tests, so Dependabot checks for me if dependency updates are still compatible with the way I expect them to behave (and if a dependency needs to be swapped by another, I only need to do it in one place).
As a developer, I haven't had any qualms with ESLint, so I wouldn't really have any reason to pick up Rome. Now if the full tool set offers lots of things, then I might consider replacing my current Vue template / create-react-app, but that's a big step.
All of this to say, no, it's not an appeal to authority. It's sounds like a "I'm not sure I can imagine this being worth switching to because we have something that works and is stable now, and we haven't had a great track record with tooling churn in JS world". It's a value add question.
If that's really the problem you have to perform an honest self-assessment: are you spending more time and energy on tooling and framework drama than building/maintaing original products?
Its an important question to ask yourself as it directly indicates whether you are delivering or consuming business value. Developers will twist themselves into knots to falsely correlate those two things, but customers and end users absolutely don't care.
Most people don't have jobs building/maintaining original products or delivering or consuming business value. Most people have jobs doing what someone else tells them. Business value just happens to be the byproduct of doing what is told. When this "someone else" finds this brand new tool, either they ignore it in favor of existing tooling which means you're never going to learn it which means you're left behind in a constantly changing ecosystem and then changing jobs becomes a massive pain because you're not familiar with said tool, or "someone else" loves it and you're forced to adopt it and your life is pain as you migrate ancient projects to try and please their whims
JS being in such constant flux means new companies making new products adopt the standards set by the current trends which means they will not hire you if your knowledge has gaps and if you're looking for new careers, you have to continually keep up. This is exhausting and a lot of people are, IMO rightfully so, frustrated
It’s that kind of nonsense that incompetent people use to qualify prior bad decisions formed from insecurity at expense to everything else. It’s less clever than a person might think.
When the web moved from jQuery to React, the details developers cared about changed. Opening modals got harder, but thats okay because React made so many other things better.
React is good enough to force us to adopt compilers for a language that doesn’t need them. When people complain about JavaScript on Hacker News, it’s usually about the build tools – and Rome has a shot at being an answer to that.
The web is more mature now, but that doesn’t mean the time for new tools is over.
Even if it somehow manages to succeed at its mission and become the defacto toolchain for all of frontend, I feel its improvements over the status quo will be short lived, and over the long run having a monolithic toolchain dominate will prove to be a net-negative for the frontend tooling ecosystem.
Having a single defacto monolithic toolchain greatly raises the barrier to entry for new competing tools, as they'd no longer be able to just compete on the merits of doing any individual task better than its direct competition to gain adoption.
Having different single purpose tools that can people can pick and choose from independently of each other is a crucial part of what has enabled rapid innovation in the JS tooling ecosystem up till now, since it keeps implementation/switching costs for any individual tool as low as it can reasonably be for that specific use-case.
This comes at the cost of making it harder for any individual to keep up with all the latest innovations (as critics of the JS tooling ecosystem are often quick to point out), but projects like create-react-app have been fairly successful at addressing this through sourcing collective wisdom from the community to create a collection of best-in-class tools configured to work effectively together out of the box.
I can also totally emphasize that there are many low-hanging fruits in terms of efficiencies to be gained through unifying tooling to avoid wasted work and maintenance overhead, but I'm of the opinion that the gains in efficiency isn't going to be worth the cost in potential for stifled innovation, and that those efficiencies are better achieved through shared specs and shared low-level libraries that individual tools can choose to adopt if they become compelling enough.
I'm personally still going to be rooting for single purpose tools + curated collections over any monolithic toolchain.
Specifically motivated by this popular issue: https://github.com/prettier/prettier/issues/5814
:)
My way of coping with the appearance of yet another new-and-hyped thing, is to not complain and resist, nor to try to immediately learn the thing, but rather the much easier task of learning the why of the thing. Equipped with this knowledge, I can grok the JS ecosystem as a whole, and make better choices when it's time to choose a tool.
I do agree it's super exciting to see Sebastian McKenzie launch out with something new. I think I understand the why of this, and wish him every success!
I cannot deal with these kind of statements and that's probably why I never use linters for my own projects. And yes, my code is good enough, a linter won't make it any better. It just blocks me time and time again and litters the codebase with: // eslint-disable-next-line
I never really understood why these tools are so popular and who really needs them. In the end it's all about the result of the code you've written, how easy it can be maintained and how good it works. No linter can help me with that.
Then you're perfect or your environment is perfect. Or you work alone?
Initially the practice is detestable, especially if you've already developed some hard-fought coding chops without them. But after some time the practice itself influences your ability to write better code to start with, and successive tests become terser and more useful.
I think the same is true for linting. I found being involved in projects that enforce linting rules on commit to be insufferable, so I installed Prettier and configured it to format on save. Over time the auto-formatter did less because of the habits I was picking up.
I did have to confront the idea of being less precious and attached to my own coding-style, but frankly I still have pretty strong opinions despite having an automaton do my thinking for me in some of the projects I work on.
Of course, neither tests nor linting are a panacea, but so long as you give a hoot to start with you won't be able to avoid writing better code after using them.
Indeed, making you more robot for "how things should be done".
Just one example: I like my code to be readable, and one of the things I apply for that is the use of white space. Between my functions/methods I have at least 3 lines of white space. It's a lie that this is not "how things should be done", and there is no way to tell prettier to accept that because it is meant to be opinionated. These tools have a place for some teams and junior devs, but I try to stay away from the ugly code it spits out.
On the point of being a robot, in what way are we not already "robots" by stubbornly sticking to what we think of as best? Are you not already a robot by limiting yourself to a narrow-range of what you consider "readable"?
I also find it annoying to occasionally lose white-space to Prettier. But I've learned my way around it: By either adding short comments or by decomposing single-lines into multiple ones, which ends up improving readability anyway.
sebmarkbage is one of the architects of React and a TC39 member. He's known for the Fiber architecture, among other things.
sebmck/kittens wrote 6to5 as a teenager in Australia, eventually landed a job doing JS infra at Facebook, and now works at Discord. He's a founder Babel, Lerna, Yarn, and now Rome.
Wanting a project to be successful because of the person who started it is a terrible reason and has had (unsurprisingly) terrible results wherever I've seen it in the OSS community.
I feel the exact opposite: things like Rome (and Deno) are exactly the kind of projects that are needed in order to sort out a horribly fractured eco-system. I don't care who built them as long as they work well to solve the problems that so clearly exist.
I somehow misinterpreted pipenv being an official python project. My usecase was pretty simple, I wanted a requirements.txt that only included the things I explicitly installed with perhaps a requirements-lock.txt with the actual list of things installed. :/
I didn’t even realize poetry existed until I talked to people who do python full time.
Have we? I have 2019 projects I have worked on that dont use TypeScript and while I dont mind TypeScript I have a YAGNA attitude about it still. My coworkers seem to hate it despite never having used it. I think of TS as almost irrelevant if you can do JSHint but thats just me coming from Python 3 where type hinting is valid and doesnt require additional tooling to make it work.
Sadly I understand why JS syntax doesnt expand too much over the years and you need something like Babel.
But on the other hand if we stopped allowing legacy web clients we could probably push modern JS to more mature stages including type hinting (optional of course) as was done in Python 3.
But if you want to? Each vertical has a de-facto community standard at this point. If you want typechecking, it's TS. If you want formatting, it's Prettier. Etc.
And of course, nobody's captured 100% of any of these verticals - there are plenty of people who use Flow o Reason for typechecking, or "StandardJS" for formatting.
But a new person setting up a JS project can just install the community standards at this point and be fine (albeit bothered at configuring things, if they don't use c-r-a).
If you want more configuration, build your own, and most people will do. But for the novice, people who do multiple new projects a year, or people who don't care about details, Rome will be great.
For homemade projects, I use CodeKit, and I think Rome will be the same without the friendly and refined GUI.
https://2019.stateofjs.com/javascript-flavors/#javascript_fl...
To me, (and with the conversation restricted to applications, not websites or anything that is primarily a document for consumption), we should be going in exactly the opposite direction; reducing the number and scope of the extra tools we need to use.
We should be making bundlers unnecessary (javascript modules plus smart http 2 servers are one way, service workers are another), we should write code that targets the js that modern browsers understand so that babel is unnecessary as part of our main workflow and is only run if you want old-browser compatibility, we should look at folding the key parts of react into the DOM (firefox used to have something really not all that different from JSX, and the core functionality of react is pretty small - the DOM is a virtual DOM). Linting and source code formatting should primarily be part of your IDE/Editor workflow. Testing needs vary, and I want to be able to use the right framework for testing for the project without having to bring along all the other cruft.
What I loved about the web when I first started building apps for it was that I could start with a single file in an editor and grow from there. Making a change, pressing F5 and seeing the change immediately was so liberating compared to long compile times and complex build systems. With your code carefully written so as to fail fast, you could often see failures in the live application faster than my Java IDE would pick up a type error in Java code (anyone remember the white menu bar of doom?).
I think it's a great pity that we've taken one of the web platforms major strengths - fast iteration times, and added so much tooling. We used to scoff at the fake stackoverflow question where someone asked 'how to do addition in jquery', but the sheer quantity of tooling that is normally used for projects has put us in a similar position.
- ESbuild (100x faster than webpack in part due to focus on doing a single parse (but also shipping a Go binary))
- Deno
- Rome
As we consolidate on the jobs to be done we expect out of modern tooling, it makes sense to do all these in a single pass with coherent tooling. It will not make sense for a large swathe of legacy setups, but once these tools are battle tested, they would be my clear choice for greenfield projects.
recommended related reads:
- https://medium.com/@Rich_Harris/small-modules-it-s-not-quite...
I just know that every time I revisit any JS front-end stuff, I end up punting and just manually adding script/stylesheet includes for Vue and Bootstrap or whatever so I don't need to deal with half that crap. As an outsider it's always so daunting, and I feel like whatever I learned 6-12 months ago last time I needed to do something doesn't necessarily apply anymore.
* create-react-app serves as a very basic frontend template for a React project. It eliminates worrying about Babel, webpack, polyfills, etc
* next.js / nuxt.js solve both front-end and backend for React / Vue. Likewise they package webpack, Babel, typescript, etc, and also help with bundle splitting, routing, and server-side rendering that used to take 30 different packages and 6 think-piece blog posts. It’s good stuff (I can speak for Next, dunno about Nuxt)
Until you need to do something that CRA doesn't do. Then you have to learn about babel/webpack/whatever AND how to do get CRA to play ball with it. I'm still on the fence on whether CRA and its leaky abstractions is better than a boilerplate with working config files exposed
Undeniably, it's technically ambitious to build all these pieces under a single umbrella. And then, convincing people who are honestly scared to touch their Webpack config to switch to a new tool might not be easier.
But if Rome booms, it'll truly benefit the community.
If someone can offer me a convincing alternative to webpack I will switch in a heartbeat.
Basically, Rome will most likely need somewhat of a Typescript moment. Where it's just transitioning to the new normal for (many) JS projects.
I really like Babel because of its plugins. My project typecheck.macro, could not exist without Babel plugins.
How will Rome support compile time transformations like graphql.macro or typecheck.macro?
Compile time plugins allow JavaScript to evolve super rapidly & make the creation of frameworks that act as compilers instead of traditional frameworks possible.
This is the first release and until there's some actual usage, there's no real way to realistically predict what sort of things people will feel is missing. You can always supplement your project with multiple linters if you feel like it is currently a blocker.
As someone who works on libraries that don't use JSX, and therefore have to rely on compiler and linter plugins to get support (tagged template literals in my case) I worry that React's dominance will mean that it gets a place in Rome, while other systems are locked out, which only increases React's dominance.
Would you consider an internal plugin system as a first step, where plugins have to be part of the codebase, but intentionally get a restricted API surface? This would allow you to try out and refactor the plugin API over time before committing to it publicly.
I specifically call out not allowing smaller communities to grow that don't have the advantage of their community size being forcing functions for support. It's the most compelling reason to me to even have any sort of plugin or custom rules system in the first place.
Although we didn't go through with that idea because they can just be enabled by default since the patterns they're linting for are unambiguous, I guess that applies to any additional ones, although so far the way we've approved these sort of these has been adhoc. There's a strong desire though to formalize some "approval process" for lint rules and more typical project decisions.
I, for one, welcome a future of build tools without plugins.
Constantinople and Moscow are waiting, as well as Venice and London. A plugin system that wants to incorporate everything will risk having to maintain Byzantine diplomatic relations. On the other hand, restricting the plugin system to its core will create a local powerhouse that will utterly fail to adapt once new ventures become available.
I am waiting for the TypeScript / JavaScript split.
- Only one tool to configure, instead of many
- Many tools revolve around parsing your code and generating an AST, then manipulating / processing that AST (Prettier, ESLint, Babel, TS, Webpack, ....). That's a lot of extra processing that has to be done. In theory, doing all that processing _once_, and reusing the AST, would potentially run a lot faster.
If you can do the same work in fewer passes, it will often be faster
So instead of every file change, webpack runs its own parser, then Babel runs its own parser, then ESLint, etc — Rome probably just runs the parser once and sends the AST to other plugins
And then if you invest in making the parsing stuff really good, that makes the linter better, the Babel-equivalent better, etc
(And it would be good to put something to that extent prominently on the website, if it isn't yet.)
They also just recently released Heft which is the rushstack repo build/lint/dev-server stack.
It’s a promising project
I get the single pass rationale but isn't it possible to integrate a bundler into TypeScript compiler ?
I'm just wondering because Microsoft has a decently sized team of paid engineers working on TS for years now - what are the odds an OSS project outperforms them at implementing their own language (which they also evolve with every release)
As an example of the sort of problems you might run into with the API in its current state, trying to parse and then pretty-print code can cause comments to vanish[2] under certain circumstances. This is fine for transpiling, but is a major issue when you’re trying to prettify and overwrite the original source file.
[1] https://github.com/Microsoft/TypeScript/wiki/Using-the-Compi... [2] https://github.com/microsoft/TypeScript/issues/39620
TS and Rome architectural goals are completely incompatible.
Writing a transpiler is not complicated at all, especially for someone who has written Babel in the past.
On the other hand, if you familiarise yourself with both TS's gigantic codebase and with Rome's goals, you'll quickly see that modifying TS would be much vaster undertaking than writing it from scratch.
Also if Rome were just an also-ran to replace ESLint/Webpack/Babel/etc, it would be ok to have a less-than-ideal starting point, but it's actually trying to do things differently: better error messages, recoverable parser errors, more fixable linter messages, being faster.
This is the exact case of something that should be written from scratch (or at least use something that makes it possible) rather than building on some another technology with completely incompatible goals.
Another example? It took several years for Webpack to achieve tree-shaking that was as good as Rollup first versions, even with more maintainers, more sponsorship and a lot of time. Why? Because Webpack's architecture was completely different.
Are you suggesting that TS team is so incompetent or legacy burdened that their codebase is impossible to extend. What use case of TS would you exclude to arrive at simpler code base ?
Writing a transpiler that ignores type checks is probably simple but also quite useless for dev workflow - if I need to run a separate type checker might as well run a separate linter and compiler, the tech is established.
I would argue editors already do a better job at linting and lint rules are mostly file local anyway so the "single pass lint on each build" is not that useful.
Having type checking as a part of the compile process is the best way to leverage typescript (you can fork the checking process so it doesn't block output for faster reload experience)
Maybe those things are not important for you, but they are for the Rome author and for many people in the community.
Also with brundolf's solution you can run the TSC typechecker (but not the transpiler/emitter) and Rome in parallel, making good use of modern multi-core processors. With Webpack+TSC you'll have to typecheck, transpile and bundle all in series. This might be very significant.
I'm not suggesting at all that they're incompetent, but you seem to be...
Typescript's goal was not extensibility in the way it would be needed for Rome to use it as its base. There is nothing wrong with that.
Also, it's not "incompetence" to write a codebase that is hard to extend or modify to do something in a completely different way. It's just a difference of priorities.
Of course it would be better to only have easily-modifiable codebases in the world, but this is not possible. Teams might have other conflicting goals.
Don't fool yourself into thinking that a good codebase has every single positive attribute in the world just because it's a good codebase, or because its authors are good coders.
Webpack was harder to modify than creating Rollup from scratch, GCC was harder to modify than it was to create LLVM from scratch, Linux is immeasurably harder to modify than Minix if you want it to do things it was not . But it doesn't mean that those codebases are worse or better.
--
> What use case of TS would you exclude to arrive at simpler code base ?
Why would I want to exclude any use case from Typescript? I actually mentioned Rome's raison d'être is to have more things than other transpilers.
It's a matter of architecture and early choices, of how the parser/ast/pretty-printer were implemented. Not really a matter of features we can remove.
If that existed, it would allow Typescript it to be embeddable, and Rome could use that.
People have been asking for it for half a decade.
Also it's pretty trivial to build a faster compiler if you don't have to worry about type checking. You can still have type checking with a bundler that ignores types by running the official TypeScript type checker in parallel with your bundler.
A lot more than you imagine. Esbuild can already transpile 100x faster than typescript.(Note: esbuild does not typecheck).
If they use similar technique as esbuild, they can easily surpass typescript compiler in all functional areas
It would be great to improve the performance of type checking, but that’s more complicated.
Some project are trying, the Deno maintainers have asserted that TypeScript should be rewritten in Rust for better performance. swc is trying to do that - https://github.com/swc-project/swc/issues/571
It seems to me Deno has built with browser-like environments in mind, thus the environment would be more unified.
Node.js is pretty much a different beast. If we want to have an ambitious project like Rome, I would love to have it based on an ambitious foundation.
This doesn't feel right. There are already ` for multi-line comments. Why not use them? Changing the meaning of " breaks the ability to copy paste an rjson file into regular sourcecode.
- does it handle images imports
- does it handle scss imports
- if not, does it require css-in-js (and if so which one)
- does it handle shaders imports (and other arbitrary dependencies that need custom processing to get embedded in the bundle)
- does it generate SRI hashes for stylesheets and scripts
- can it generate web workers
- can it generate node bundles or only browser bundles
- can it split chunks
- do you still get TS intellisense in VSCode
- can it be used with Rust and/or Webassembly
It already took forever for the ecosystem to get JSHint, Tslint and others to converge into Eslint.
[0]: https://raw.githubusercontent.com/romefrontend/rome/1d583050...
[1]: https://raw.githubusercontent.com/romefrontend/rome/main/ass...
Like many great Google projects it just never got fully realized, which is a shame :(
Let’s liberate React next?
https://romefrontend.dev/blog/2020/08/08/introducing-rome.ht...
It took forever to get eslint/prettier/webpack etc to work in sync and if there is one tool can do it all, I'm all for it.
I care a lot more about dev experience and speed than “one tool to do one job”. The “job” is to handle the code I’m working with in a way that isn’t as frustrating as our current tools.
Although "Borg" might be more accurate.
https://www.merriam-webster.com/dictionary/Rome
https://www.online-latin-dictionary.com/latin-english-dictio...