Why the CLI has a core and plugins, and why Vue doesn't live in the core
- buildinpublic
- architecture
- typescript
- plugins
By the time the CLI reached version 1.0.0 on May 26, it could read TypeScript and JavaScript well. It also had Vue, Svelte and Astro support built in. That last part is what I changed next, and it is the architecture decision I would defend hardest.
The first idea: one tool that does everything
My first instinct was to make the CLI universal: one package with every framework built in. Install it, run it, done. I had done something similar with the SDK, which is one package that works everywhere, and that went well.
It stopped making sense once I thought about who uses the CLI. Not everyone has a Vue app. A team that writes a Node backend does not need a Vue parser in their tooling, and neither does a React team. With everything in one package, every user installs and loads code for frameworks they will never use, and every framework I add makes the core bigger and harder to change. The core's job is to understand TypeScript. A Vue component is not TypeScript.
So I split it: a small core that knows nothing about any framework, and plugins that teach it about one framework each.
The shape of the split
A plugin has one job: adapt a framework's files so the core can work with them. The core does not know what Vue is. There is no framework-specific branch anywhere in the analysis.
The plugin system arrived on June 25, in the 1.x line. More hooks followed on July 17, and the first real plugin, for Vue, on July 22. Svelte, Astro and Angular followed. The plugin list and setup steps are in the docs.
The version numbers mark the shape of it too. 1.0.0 shipped on May 26 and 2.0.0 on July 28. I did not plan a big 2.0 moment: 2.0.0 is the version I had been trying to build. By then the CLI was so different from 1.x that it needed a major number to say so honestly.
From regular expressions to real compilers
The first Vue support used two regular expressions. It covered the cases in my test fixtures, and real components went beyond them. The fix, which became the standard for every plugin after it, was to parse files with the framework's own compiler instead of something that looks similar. That is what makes the results match what the framework itself understands.
What I would tell someone designing something similar
Put the stable, general part in the core and push everything specific to one case outward. If a new framework forces a change in the core, the boundary is in the wrong place. Adding Svelte and Astro touched zero lines of the core's analysis, and I take that as the clearest sign the split is right.