diff --git a/docs/ember/authoring-addons.md b/docs/ember/authoring-addons.md index 1a44ec14d..cfd283f08 100644 --- a/docs/ember/authoring-addons.md +++ b/docs/ember/authoring-addons.md @@ -10,20 +10,20 @@ The first step is adding the proper Glint signature types to all the addon's com If all apps were already using [first class component templates] (i.e. [strict mode] templates), this would be all we need to do. But as this is certainly not the case for the time being, we will need to find a way to expose the required [Template Registry] entries to the consuming app, in an easy but also flexible way. -As described in the [Using Addons] guide, the _easy_ way for users is to import a file from the addon containing all the registry entries. This is for the most common case in which the app is consuming the addon provided components, helpers and modifiers as they are. The convention here is for the addon - when published on npm - to provide a `glint.d.ts` file, that the app will import as `import 'awesome-addon/glint'`. For a (v1) addon using [`ember-cli-typescript`][ect] this would be a `/addon/glint.ts` file, which needs to contain all the registry entries for all the public components (helpers, modifiers) the addon exposes to the app (in the addon's `/app` tree): +As described in the [Using Addons] guide, the _easy_ way for users is to import an interface from the addon containing all the registry entries. This is for the most common case in which the app is consuming the addon provided components, helpers and modifiers as they are. The convention here is for the addon - when published on npm - to provide a `template-registry.d.ts` file, that the app will import as `import AwesomeAddonRegistry from 'awesome-addon/template-registry'` and then use to extend its own global registry. -{% code title="/addon/glint.ts" %} +For a (v1) addon using [`ember-cli-typescript`][ect] this would be a `/addon/template-registry.ts` file, which needs to contain registry entries for all the public components (helpers, modifiers) the addon exposes to the app (in the addon's `/app` tree): + +{% code title="/addon/template-registry.ts" %} ```typescript import type AwesomeButton from 'awesome-addon/components/awesome-button'; import type AwesomeModal from 'awesome-addon/components/awesome-modal'; -declare module '@glint/environment-ember-loose/registry' { - export default interface Registry { - AwesomeButton: typeof AwesomeButton; - AwesomeModal: typeof AwesomeModal; - // ... - } +export default interface AwesomeAddonRegistry { + AwesomeButton: typeof AwesomeButton; + AwesomeModal: typeof AwesomeModal; + // ... } ``` @@ -33,13 +33,13 @@ While this easy way, which only requires the app to import this single file, is This is given by letting the app manage the registry entries on its own, thereby being able to pick what to import from the addon directly and what to replace with its own custom types. -Note: in apps, we generally define the registry entries right in the actual file itself so that it is nicely co-located with the signature and implementation. If an app imported such a module from an addon, though, TypeScript would see the registry declaration there and apply it, which is exactly what we want to prevent. For addons it is therefore recommended that all the registry entries are only added to the `/addon/glint.ts` file as described above, and _not_ to the file containing the actual class! +Note: in apps, we generally define the registry entries right in the actual file itself so that it is nicely co-located with the signature and implementation. If an app imported such a module from an addon, though, TypeScript would see the registry declaration there and apply it, which is exactly what we want to prevent. For addons it is therefore recommended that all the registry entries are exposed as an importable type in `/addon/template-registry.ts` as described above, and _not_ to the file containing the actual class! A real world example of this setup can be seen in [`ember-responsive-image`][eri] ## Adding Glint types to addons not written in TypeScript -Even if an addon author has choosen not to adopt TypeScript, the addon can still ship Glint types! The setup, however, will be slightly different. First, without [`ember-cli-typescript`][ect], types in `/addon/glint.ts` won't be emitted to `/glint.d.ts` on publish, so you'll need to do what you would have done in `/addon/glint.ts` in `/glint.d.ts` instead. Also, since the components, helpers, and modifiers are not written in TypeScript, we can't add type signatures to them directly. Instead we'll need to create declaration files for them. And these files will need to use the importable path directly from the root of the addon (not under `/addon/`). Here's an example: +Even if an addon author has choosen not to adopt TypeScript, the addon can still ship Glint types! The setup, however, will be slightly different. First, without [`ember-cli-typescript`][ect], types in `/addon/template-registry.ts` won't be emitted to `/template-registry.d.ts` on publish, so you'll need to do what you would have done in `/addon/template-registry.ts` in `/template-registry.d.ts` instead. Also, since the components, helpers, and modifiers are not written in TypeScript, we can't add type signatures to them directly. Instead we'll need to create declaration files for them. And these files will need to use the importable path directly from the root of the addon (not under `/addon/`). Here's an example: {% code title="/components/awesome-button.d.ts" %} @@ -75,24 +75,22 @@ export default class AwesomeSauce extends Helper {} {% endcode %} -{% code title="/glint.d.ts" %} +{% code title="/template-registry.d.ts" %} ```typescript import type AwesomeButton from './components/awesome-button'; import type AwesomeSauce from './helpers/awesome-sauce'; -declare module '@glint/environment-ember-loose/registry' { - export default interface Registry { - AwesomeButton: typeof AwesomeButton; - 'awesome-sauce': typeof AwesomeSauce; - // ... - } +export default interface AwesomeAddonRegistry { + AwesomeButton: typeof AwesomeButton; + 'awesome-sauce': typeof AwesomeSauce; + // ... } ``` {% endcode %} -By defining the component, helper, and modifier types in separate importable files (rather than just directly in `glint.d.ts`), consumers can import them individually and manually add to the registry if they so choose. +By defining the component, helper, and modifier types in separate importable files (rather than just directly in `template-registry.d.ts`), consumers using [first class component templates] can import them from the correct paths. ## Stability Note diff --git a/docs/ember/using-addons.md b/docs/ember/using-addons.md index 17b323719..e89b0a61a 100644 --- a/docs/ember/using-addons.md +++ b/docs/ember/using-addons.md @@ -51,33 +51,38 @@ If you are already using [strict mode] templates (via [first class component tem But more likely you are still using classic `.hbs` template files, for which Glint needs to know e.g. which component _name_ maps to which component _class_ and hence its type. This is managed by the [Template Registry], which needs to be extended for all the components, helpers and modifiers provided by the addon. -By convention according to the [Authoring Guide][authoring], the addon will ship a `glint.d.ts` -file that already contains all the necessary registry entries. You just need to import this file from somewhere in your app, e.g. where you already import `@glint/environment-ember-loose`: +By convention according to the [Authoring Guide][authoring], the addon will ship a `template-registry.d.ts` file that exports a type containing all the registry entries for components, helpers and modifiers it exposes. To use these entries, you can declare that the `@glint/environment-ember-loose` registry `extends` the addon registry in the same place you add your local declarations. {% code title="types/global.d.ts" %} ```typescript import '@glint/environment-ember-loose'; -import 'ember-responsive-image/glint'; + +import type ResponsiveImageRegistry from 'ember-responsive-image/template-registry'; + +declare module '@glint/environment-ember-loose/registry' { + export default interface Registry extends ResponsiveImageRegistry, /* other addon registries */ { + // local entries + } +} ``` {% endcode %} In the majority of cases this should be all you need! -However, if you have for example extended or overridden a component of that addon, by having an implementation with the same name in your _app_, but with a somewhat different shape (e.g. additional arguments), then you will need to add the registry entries by yourself, as the ones provided by the addon will not match the type of your replaced component. +However, if you have for example extended or overridden a component of that addon, by having an implementation with the same name in your _app_, but with a somewhat different shape (e.g. additional arguments), then you will need to tweak the registry entries you add, as the ones provided by the addon will not match the type of your replaced component. -Add the registry entry for the replaced component as you would for any other of your app, as described under [Template Registry]. -For any other component, helper or modifier that you use from the addon as-is, you can extend the registry as described under [Typing your dependencies](#typing-your-dependencies) above. You don't need to write your own types though, as the addon already ships those. Just import the types from the addon itself: +Add the registry entry for the replaced component as you would for any other of your app, as described under [Template Registry]. To include everything _except_ a specific component, you can use TypeScript's `Omit` utility type on the original registry to leave that entry out. {% code title="types/global.d.ts" %} ```typescript -import type ResponsiveImage from 'ember-responsive-image/components/responsive-image'; +import type ResponsiveImageRegistry from 'ember-responsive-image/template-registry'; declare module '@glint/environment-ember-loose/registry' { - export default interface Registry { - ResponsiveImage: typeof ResponsiveImage; + export default interface Registry extends Omit { + ResponsiveImage: typeof MyCustomOverriddenResponsiveImage; // ... } } @@ -85,6 +90,10 @@ declare module '@glint/environment-ember-loose/registry' { {% endcode %} +You can also ignore the registry entirely and instead import and add _only_ individual entries from an addon: + + + [`glint-template-types`]: https://github.com/Gavant/glint-template-types [authoring]: authoring-addons.md [strict mode]: http://emberjs.github.io/rfcs/0496-handlebars-strict-mode.html