From de75f3a0c02a8df348836f400ccb4d151c9fa71b Mon Sep 17 00:00:00 2001 From: Dan Freeman Date: Tue, 25 Apr 2023 16:49:24 +0200 Subject: [PATCH 1/7] Note changes to `{{component}}`'s return type --- docs/migrating.md | 63 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/docs/migrating.md b/docs/migrating.md index 9108d384f..7cd6593e6 100644 --- a/docs/migrating.md +++ b/docs/migrating.md @@ -36,6 +36,69 @@ alongside `@glint/core` and your environment package(s). Note also that if you publish an addon that depends on types from `@glint/template`, that addon should likewise declare a `peerDependency` on `@glint/template` and **not** a regular `dependency`. +### `{{component}}` Typing + +In `@glint/environment-ember-loose`, if you used the `{{component}}` helper to expose a "contextual +component" to your consumers without binding any additional arguments, e.g.: + +```handlebars +{{yield (hash Child=(component "some-child-component"))}} +``` + +In 0.9.x, Glint would allow you to type that yielded component as something like: + +```typescript +export interface MyComponentSignature { + Blocks: { + default: [{ Child: typeof SomeChildComponent }]; + }; +} +``` + +However, this was never quite correct: when you use the `{{component}}` helper, the value it returns +is not the actual component class itself. If `SomeChildComponent` had static members, for instance, +then according to `Child: typeof SomeChildComponent`, consumers should be able to access those +properties from the yielded `Child` value, but they wouldn't be there in the value returned from +`{{component}}`. + +The only thing you are guaranteed to be able to do with the result of the `{{component}}` helper is +invoke it as a component in a template. + +Accordingly, in Glint 1.0 the above combination will no longer typecheck. To fix the situation, you +can either update the signature to match the template: + +```typescript +export interface MyComponentSignature { + Blocks: { + default: [{ Child: ComponentLike }]; + }; +} +``` + +Or update the template to match the signature: + + +{% tabs %} +{% tab title="Template" %} + +```handlebars +{{yield (hash Child=this.SomeChildComponent)}} +``` + +{% endtab %} +{% tab title="Backing Class" %} + +```typescript +import SomeChildComponent from './some-child-component'; + +export default class MyComponent /* ... */ { + SomeChildComponent = SomeChildComponent; +} +``` + +{% endtab %} +{% endtabs %} + ### `include`/`exclude` Configuration Glint 1.0 drops support for the `transform` configuration key, which is where `include` and From c23335cc93f2e05f6b62582d274f908ecb334409 Mon Sep 17 00:00:00 2001 From: Dan Freeman Date: Tue, 25 Apr 2023 17:08:18 +0200 Subject: [PATCH 2/7] Restructure docs ToC --- docs/contents.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/contents.md b/docs/contents.md index eadf3620d..bb7c83584 100644 --- a/docs/contents.md +++ b/docs/contents.md @@ -6,7 +6,13 @@ ## Using Glint * [Configuration](configuration/\_index.md) - * [Project References](configuration/project-references.md) +* [Glint Types](glint-types.md) +* [@glint Directives](directives.md) +* [Glint with JavaScript](with-js.md) +* [Project References](configuration/project-references.md) + +## Environments + * [Ember](using-glint/ember/README.md) * [Installation](ember/installation.md) * [Component Signatures](ember/component-signatures.md) @@ -21,9 +27,8 @@ * [Installation](glimmerx/installation.md) * [Component Signatures](glimmerx/component-signatures.md) * [Template Components](glimmerx/template-components.md) -* [Glint with JavaScript](with-js.md) -* [@glint Directives](directives.md) -* [Glint Types](glint-types.md) -* [Migrating](migrating.md) -* [Diagnosing Common Error Messages](diagnosing-common-error-messages.md) + +## Troubleshooting +* [Migration Notes](migrating.md) +* [Common Error Messages](diagnosing-common-error-messages.md) * [Known Limitations](known-limitations.md) From db36ecb8a9e2e34a35d1506a0945fd53b851f565 Mon Sep 17 00:00:00 2001 From: Dan Freeman Date: Tue, 25 Apr 2023 17:17:56 +0200 Subject: [PATCH 3/7] Add a few notes around `@glint-` directives --- docs/contents.md | 2 +- docs/directives.md | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/docs/contents.md b/docs/contents.md index bb7c83584..654687fe2 100644 --- a/docs/contents.md +++ b/docs/contents.md @@ -7,7 +7,7 @@ * [Configuration](configuration/\_index.md) * [Glint Types](glint-types.md) -* [@glint Directives](directives.md) +* [`@glint` Directives](directives.md) * [Glint with JavaScript](with-js.md) * [Project References](configuration/project-references.md) diff --git a/docs/directives.md b/docs/directives.md index 6b0a1dc5d..da59cab38 100644 --- a/docs/directives.md +++ b/docs/directives.md @@ -5,6 +5,9 @@ Glint's behavior. Additional comment text can follow directives to document their purpose. These directives correspond to the similarly-named directives in TypeScript. +`@glint` directives may _only_ be applied in template comments, not in +TypeScript outside of templates. + ## `@glint-expect-error` The `@glint-expect-error` directive operates similarly to `@glint-ignore` in @@ -48,7 +51,7 @@ migration process. Example: ```hbs -{{! @glint-nocheck: this whole teplate needs work }} +{{! @glint-nocheck: this whole template needs work }} @@ -56,3 +59,11 @@ Example: {{two-arg-helper 'bar'}} ``` + +**Note**: the [`auto-glint-nocheck`] script in the `@glint/scripts` package +can automate the process of adding `@glint-nocheck` directives at the top +of every template with type errors in your project. This allows you to adopt +Glint in a project immediately for all new templates while incrementally +migrating your existing ones to make them typesafe over time. + +[`auto-glint-nocheck`]: https://github.com/typed-ember/glint/tree/main/packages/scripts#auto-glint-nocheck From 4bbbcedc3100527c21959b93654bea89aeced9e5 Mon Sep 17 00:00:00 2001 From: Dan Freeman Date: Tue, 25 Apr 2023 18:16:35 +0200 Subject: [PATCH 4/7] Add some notes on more advanced type manipulation --- docs/glint-types.md | 94 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/docs/glint-types.md b/docs/glint-types.md index e27e794d5..f4dc541de 100644 --- a/docs/glint-types.md +++ b/docs/glint-types.md @@ -79,3 +79,97 @@ interface MyComponentSignature { ``` Where `WithBoundArgs` accepts the names of the pre-bound arguments, `WithBoundPositionals` accepts the number of positional arguments that are pre-bound, since binding a positional argument with `{{component}}`/`{{modifier}}`/`{{helper}}` sets that argument in a way that downstream users can't override. + +## Advanced Types Usage + +From Glint's perspective, what _makes_ a value usable as a component is being typed as a constructor +for a value type that matches the instance type of `ComponentLike`. The same is true of helpers with +`HelperLike` and modifiers with `ModifierLike`. + +While this may seem like a negligible detail, making use of this fact can allow authors with a good +handle on TypeScript's type system to pull of some very flexible "tricks" when working with Glint. + +### Custom Glint Entities + +Ember (and the underlying Glimmer VM) has a notion of _managers_ that allow authors to define custom +values that act as components, helpers or modifiers when used in a template. Glint can't know how +these custom entities will work, but by using `ComponentLike`/`HelperLike`/`ModifierLike`, you can +explain to the typechecker how they function in a template. + +For example, if you had a custom DOM-less "fetcher component" base class, you could use TypeScript +[declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html) to +tell Glint that its instance type extended `InstanceType>`, where `S` is an +appropriate component signature based on how your custom component works. + +```typescript +// Define the custom component base class +class FetcherComponent { + // ... +} + +// Set its manager and, if necessary, template +setComponentManager(/*...*/, FetcherComponent); +setComponentTemplate(/*...*/, FetcherComponent); + +// Use declaration merging to declare that the base class acts, from Glint's perspective, +// like a component with the given signature when used in a template. +interface FetcherComponent extends InstanceType< + ComponentLike<{ + Args: { params: Params }; + Blocks: { + loading: []; + error: [message: string]; + ready: [payload: Payload]; + }; + } +>> {} +``` + +This is a fairly contrived example, and in most circumstances it would be simpler to use a standard +base class like `@glimmer/component`, but nevertheless the option exists. + +**Note**: this declaration merging technique using `InstanceType>` is _exactly_ +how Glint's own 1st-party environment packages like `@glint/environment-ember-loose` set up the +template-aware types for `@glimmer/component`, `@ember/component/helper`, etc. + +### Type Parameters + +When defining a class-based component, modifier or helper, you have a natural place to introduce +any type parameters you may need. For example: + +```typescript +export interface MyEachSignature { + Args: { items: Array }; + Blocks: { + default: [item: T, index: number]; + }; +} + +export class MyEach extends Component> { + // ... +} +``` + +However, if you aren't working with a concrete base type and can only say that your value is, +for instance, some kind of `ComponentLike`, then TypeScript no longer offers you a place to +introduce a type parameter into scope: + +```typescript +// 💥 Syntax error +declare const MyEach: ComponentLike>; + +// 💥 Cannot find name 'T'. ts(2304) +declare const MyEach: ComponentLike>; +``` + +Since what matters is the _instance_ type, however, it is possible to define `MyEach` using just +`ComponentLike` and slightly more type machinery: + +```typescript +declare const MyEach: abstract new () => InstanceType< + ComponentLike> +>; +``` + +This shouldn't be a tool you frequently find the need to reach for, but it can be useful on +occasion when working with complex declarations. From 2a3ff06462b7f66fa6d19edf5b6b41f2779d2551 Mon Sep 17 00:00:00 2001 From: Dan Freeman Date: Tue, 25 Apr 2023 19:04:32 +0200 Subject: [PATCH 5/7] Be more explicit about requiring a side-effect env import for vanilla TS --- docs/diagnosing-common-error-messages.md | 16 ++++++++++++++++ docs/ember/installation.md | 17 +++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/docs/diagnosing-common-error-messages.md b/docs/diagnosing-common-error-messages.md index 82511000a..43a11dc7e 100644 --- a/docs/diagnosing-common-error-messages.md +++ b/docs/diagnosing-common-error-messages.md @@ -55,6 +55,22 @@ declare module '@glint/environment-ember-loose/registry' { } ``` +## Invalid module name in augmentation + +``` +Invalid module name in augmentation: module '@glint/environment-ember-loose/registry' cannot be found.` +``` + +TypeScript will only allow you to add declarations for a module if it's already seen the original. In other words, +if you've never directly or transitively imported `@glint/environment-ember-loose/registry` anywhere in your project +that TypeScript can see then it won't allow you to add a registry entry. + +To fix this, [add `import '@glint/environment-ember-loose'` somewhere in your project][env-import]. This will ensure that the +registry, as well as other important type information like template-aware declarations, are visible to vanilla +`tsc` and `tsserver`. + +[env-import]: ./ember/installation.md + ## Does not satisfy the constraint 'Invokable' ``` diff --git a/docs/ember/installation.md b/docs/ember/installation.md index 22b9a9a78..7a95dd86b 100644 --- a/docs/ember/installation.md +++ b/docs/ember/installation.md @@ -1,4 +1,9 @@ -To use Glint with [Ember](https://github.com/emberjs/ember.js) v3.24 or higher, you'll add the `@glint/core`, `@glint/template` and `@glint/environment-ember-loose` packages to your project's `devDependencies`, then add a `"glint"` key to your project's `tsconfig.json`. +To use Glint with [Ember](https://github.com/emberjs/ember.js) v3.24 or higher, you'll need to: + 1. add the `@glint/core`, `@glint/template` and `@glint/environment-ember-loose` packages to your project's `devDependencies` + 2. add a `"glint"` key with appropriate config to your project's `tsconfig.json` + 3. add `import '@glint/environment-ember-loose';` somewhere in your project + +Read on for a more detailed explanation of each of these steps. {% tabs %} {% tab title="Yarn" %} @@ -33,9 +38,17 @@ If you are using `ember-template-imports` in your project, also install the `@gl Note that, by default, Glint will assume you want it to analyze all templates in the codebase that are covered by your `tsconfig.json`. To ignore any type errors up front so that you can incrementally migrate your project to typesafe templates, consider using [the `auto-glint-nocheck` script](https://github.com/typed-ember/glint/tree/main/packages/scripts#auto-glint-nocheck) to add [`@glint-nocheck` comments](../directives.md#glint-nocheck) to your existing templates that would produce errors. +Finally, ensure you've added the following statement somewhere in your project's source files or ambient type declarations: + +```typescript +import '@glint/environment-ember-loose'; +``` + +You may also choose to disable TypeScript's "unused symbol" warnings in your editor, since Glint will flag any symbols that are actually unused, while `tsserver` won't understand any symbol usage that only occurs in templates. + {% hint style="info" %} -To minimize spurious errors when typechecking with vanilla `tsc` or your editor's TypeScript integration, you should add `import '@glint/environment-ember-loose';` somewhere in your project's source or type declarations. You may also choose to disable TypeScript's "unused symbol" warnings in your editor, since `tsserver` won't understand that templates actually are using them. +When typechecking with vanilla `tsc` or your editor's `tsserver` integration, adding this side-effect `import` statement ensures that TypeScript is aware of the Glint-specific types provided by the environment package. Without this line, you may find that vanilla TypeScript produces spurious errors. {% endhint %} From 34346b153355c4b3e1c829960c4848aef7fa214b Mon Sep 17 00:00:00 2001 From: Dan Freeman Date: Tue, 25 Apr 2023 19:05:19 +0200 Subject: [PATCH 6/7] Remove stability warnings --- README.md | 2 -- docs/ember/authoring-addons.md | 6 ------ docs/known-limitations.md | 2 -- docs/migrating.md | 8 -------- docs/overview.md | 6 ------ 5 files changed, 24 deletions(-) diff --git a/README.md b/README.md index d1e38542c..e7c5cfb63 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,6 @@ TypeScript-powered tooling for Glimmer templates. [Glint] is a set of tools to aid in developing code that uses the Glimmer VM for rendering, such as [Ember.js] v3.24+ and [GlimmerX] projects. Similar to [Vetur] for Vue projects or [Svelte Language Tools], Glint consists of a CLI and a language server to provide feedback and enforce correctness both locally during editing and project-wide in CI. -⚠️ Note: **Glint is still under active development!** Please bear with us and expect breaking changes and rough edges as we work toward a stable release. - [glint]: https://typed-ember.gitbook.io/glint [ember.js]: https://www.emberjs.com [glimmerx]: https://github.com/glimmerjs/glimmer-experimental diff --git a/docs/ember/authoring-addons.md b/docs/ember/authoring-addons.md index cfd283f08..830d588ae 100644 --- a/docs/ember/authoring-addons.md +++ b/docs/ember/authoring-addons.md @@ -92,12 +92,6 @@ export default interface AwesomeAddonRegistry { 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 - -{% hint style="warning" %} -Note: **Glint is still under active development!** Please bear with us and expect breaking changes and rough edges as we work toward a stable release. This could also affect the users of your addon, so you might want to see your addon's Glint support as experimental for now! -{% endhint %} - [strict mode]: http://emberjs.github.io/rfcs/0496-handlebars-strict-mode.html [first class component templates]: http://emberjs.github.io/rfcs/0779-first-class-component-templates.html [template registry]: template-registry.md diff --git a/docs/known-limitations.md b/docs/known-limitations.md index f0c418d6c..ebb1301a4 100644 --- a/docs/known-limitations.md +++ b/docs/known-limitations.md @@ -1,5 +1,3 @@ -**Glint is not yet stable** and is still under active development. As such, there are currently several known limitations to be aware of. - ### Ember-Specific Glint is not currently integrated with `ember-cli-typescript`, so typechecking performed during an `ember-cli` build will not take templates into account. diff --git a/docs/migrating.md b/docs/migrating.md index 7cd6593e6..e51520d76 100644 --- a/docs/migrating.md +++ b/docs/migrating.md @@ -2,14 +2,6 @@ ## Glint 0.9.x to 1.0 -{% hint style="warning" %} - -Glint 1.0 is currently in a beta period. We encourage you to try out the beta and report any -issues you run into. While we don't anticipate landing any further breaking changes during the beta -cycle, be aware that it's still possible we'll do so in response to bugs or other early feedback. - -{% endhint %} - Most of the changes in Glint 1.0 should appear as bugfixes and improvements to the majority of users migrating from 0.9.x. diff --git a/docs/overview.md b/docs/overview.md index a6e516c30..6f49f0555 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -18,12 +18,6 @@ The Glint language server implements the standardized [Language Server Protocol] ![Suggesting component arguments in typeahead with type information and documentation](https://user-images.githubusercontent.com/108688/111070948-3f9b2f00-84d4-11eb-9eaa-077cadf6f380.png) -## Stability Note - -{% hint style="warning" %} -Note: **Glint is still under active development!** Please bear with us and expect breaking changes and rough edges as we work toward a stable release. Also note that Glint is currently only compatible with TypeScript projects, but our aim is ultimately to support JavaScript as well, as TypeScript's tooling can provide best-in-class support for both TS and JS projects. -{% endhint %} - [ember.js]: https://www.emberjs.com [glimmerx]: https://github.com/glimmerjs/glimmer-experimental [vetur]: https://github.com/vuejs/vetur From e10b12fba00a5e0b0b4ce28e3209a64a3a5d36b2 Mon Sep 17 00:00:00 2001 From: Dan Freeman Date: Wed, 26 Apr 2023 10:57:39 +0200 Subject: [PATCH 7/7] Fix typo in Glint Types page Co-authored-by: Chris Krycho --- docs/glint-types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/glint-types.md b/docs/glint-types.md index f4dc541de..1a3b78210 100644 --- a/docs/glint-types.md +++ b/docs/glint-types.md @@ -87,7 +87,7 @@ for a value type that matches the instance type of `ComponentLike`. The same is `HelperLike` and modifiers with `ModifierLike`. While this may seem like a negligible detail, making use of this fact can allow authors with a good -handle on TypeScript's type system to pull of some very flexible "tricks" when working with Glint. +handle on TypeScript's type system to pull off some very flexible "tricks" when working with Glint. ### Custom Glint Entities