Author Built-In Blueprints in TypeScript
Ember CLI's generators are a foundational part of Ember itself. They are one of the first elements of the developer experience that Ember prides itself on, and continue to be an essential part of the ecosystem by virtue of the fact that both apps and addons can generate their own custom blueprints. For many years, the
ember-cli-typescript addon has provided its own set of TypeScript-flavored built-in blueprints that essentially act as drop-in replacement for the built-in blueprints that provide with Ember today. However, these blueprints have proven very difficult to maintain as it involves keeping them all up to date with every single change made to Ember itself, so much so that the current TypeScript blueprints have diverged to the point there is currently not a TypeScript blueprint for components at all.
- Allow for multiple versions of TypeScript blueprints to exist (since they'd belong to each version of Ember), rather than today where there is no way to tie standalone TypeScript blueprints to individual versions of Ember (e.g. there's no LTS version of the TypeScript blueprints today).
- Enable TypeScript blueprint generation in any Ember app or addon right out of the box via a
tl;dr: Blueprints will be handled exactly the same as before, with the one exception that any blueprint file written in TypeScript will be run through Babel's
@babel/plugin-transform-typescript to strip out all the TypeScript syntax unless the user actually wants TypeScript output. Please read on for a more detailed explanation.
In the current generator system, new files are created from templates that reside in the
files directory of the blueprint that corresponds to the generator that was invoked. When a user runs
ember generate service foo, Ember CLI will locate the template at
blueprints/service/files/__root__/__path__/__name__.js, load it into memory, perform a text replacement on all of the EJS-style tags (e.g.
class <%= classifiedModuleName => extends Service becomes
class Foo extends Service), and then writes the entire string out to a file at the correct path inside the parent app or addon. Notably, there is no evaluation of the code itself, as the entire process of blueprint generation is simple text replacement.
In the new version of Ember's blueprint system, this template would instead be named
__name__.ts. The generator process would run identically (locate the template, read into memory, perform text replacement) up until it is actually time to write the file. Before that occurs, the (now fully populated) in-memory version of the newly generated file would be run through Babel's
Furthermore, blueprints would have to explicitly opt-in to this behavior by adding a
shouldTransformTypeScript: true flag to the blueprint's
index.js file. This will make the automatic TypeScript transform behavior purely opt-in, thereby preventing any conflicts with existing projects that may already have their own custom TypeScript blueprints.
In addition to the per-blueprint flag, there will also be a new
isTypeScriptProject flag added to
.ember-cli are automatically merged into the blueprint options when invoked, so this flag would be readily accessible to all blueprints. We'd also add a step to the
init hook for
ember-cli-typescript that would check for the presence of this flag in
.ember-cli and warn if it's not set. In the future, we'd be able to set this flag as part of a
ember new --typescript flag.
There are a few details worth calling out about this change to the generation process:
tsconfigrather than one we'd have to supply (and maintain).
- In the future, we DO also need the ability to test the actual TypeScript parts of the blueprints (i.e. do the blueprints output TS files that actually type-check?), but that functionality will be covered in a later RFC that is more concerned with enabling TypeScript support in Ember.
- By default, the generators would behave exactly as they did before. However, we would also add a
--typescriptflag to the
To sum up, the changes would be as follows:
- Rewrite Ember's existing blueprints in TypeScript.
- Add a
- Add a
shouldTransformTypeScriptflag to the
Blueprintbase class that opts the blueprint in to the down-leveling behavior.
- This defaults to
- This flag exists so that blueprint authors can choose whether they want the auto-transform behavior in case there are already apps/addons that have custom blueprints they expect to always result in TypeScript files.
- Add a
- This flag defaults to
- Users can override this default behavior by passing the
--typescriptflag (mentioned above), which will force the blueprint to output TypeScript as long as a TypeScript blueprint is available.
How we teach this
For most users, there isn't much to teach here since their default experience with Ember's blueprints won't change at all. However, we should document the typescript-related behaviors in the documentation for creating custom blueprints. More specifically, we'd need to mention the 3 new flags being added (
--typescript) and explain their usage.
That said, Ember (and most of the front-end world) already relies heavily on Babel, so this change would simply be extending a dependency that already exists. Furthermore, while there is a cost to running a Babel transform as part of a generator, it will be extremely trivial given that generators only ever create a handful of files at a time, and a newly-generated Ember app runs far more files through many more Babel transforms without issue. Finally, we'd be running the transform on the in-memory version of the file, so there'd be no additional I/O cost incurred.
We could continue to maintain a wholly independent set of blueprint files, a la
ember-cli-typescript-blueprints. However, this would leave us with the same challenges that exist today, and would also make it harder to provide official support for TypeScript in Ember itself.
Optional, but suggested for first drafts. What parts of the design are still TBD?