Skip to content

Commit 41c7524

Browse files
committed
Add Glint to the project
This adds basic [Glint]-powered capabilities to the project for anyone using a Glint Language Server client (VS Code, NeoVim, IntelliJ, etc.). Setup ----- The setup to make that work involves adding some `devDependencies`: - `@glint/core` to enable Glint and `@glint/environment-ember-loose` to tell Glint how to interpret Ember's "loose-mode" templates, i.e. "normal" - `typescript`, which Glint requires to work at all. **This is _not_ adding TypeScript to the project.** It just exists to enable Glint to run. (Glint doesn't ship its own copy because TypeScript is large and it is important that Glint use the same version of TypeScript that an app uses, or else it would give conflicting reports.) - A number of `@types` packages, which are required so that Glint knows what a `Controller` or Ember `Component` are, and so on. These will all be able to go away as packages start shipping their own types, including when this app is able to upgrade to at least Ember 4.8 and start using the types that Ember ships itself. It also configures Glint via tweaks to the existing `jsconfig.json` file, with good defaults so that it never shows *errors*, but does progressively enhance the experience for JS users. Other changes ------------- With the foundations in place, add some JSDoc comments and a single TS file to the project. - The JSDoc comments show how to make TS-powered tooling (like the various Glint language servers) understand components in the app. These comments, which build on the spec for both types and comments defined in [Ember RFC 0748][rfc], work well to enhance the experience of authoring JS backing classes even *without* Glint. With Glint enabled, though, it also makes all of that information available in templates. - The TS file shows how to make Glint understand component references. It is basically a hand-coded version of what the Ember resolver does at runtime: translating string names like `MyComponent` into references to the `MyComponent` class. There are limitations to how much this can do in a purely JS project with loose mode templates, but in this app it can cover *most* of the territory (and a move to strict mode templates using `<template>` in the future will close *all* of those gaps). Both of these show the "progressive enhancement" model at work: out of the box, you get a *very* minimal set of go-to-definition, refactoring, etc. capabilities. Not none! But minimal. The more fino you put into your JSDoc comments, though, and the more items you add to the template registry, the more useful Glint becomes. [rfc]: https://rfcs.emberjs.com/id/0748-glimmer-component-signature Caveats ------- This does not yet provide *all* the benefits that Glint can provide to JS-only apps. For example, you won't be able to go-to-definition for the `this.model` of route/controller templates unless you add a `model` field on the `Controller` class with a JSDoc annotation. Similarly, you will not be able to go-to-definition or see hover docs for a lot of the data used in the application just yet: the app will need to add some JSDoc annotations to tell the language server "this field here is that data model class from over there". However, it *does* already provide significant benefits for simple things like being able to go to definition or rename items in component files, and as the one JSDoc annotation added in this shows, you can get more of these benefits with a very low level of effort. [Glint]: https://github.com/typed-ember/glint
1 parent db828db commit 41c7524

File tree

8 files changed

+441
-15
lines changed

8 files changed

+441
-15
lines changed

app/components/api-index-filter.js

+14
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,20 @@ import sortBy from 'lodash.sortby';
77
const filterDataComputedParams =
88
'filterData.{showInherited,showProtected,showPrivate,showDeprecated}';
99

10+
/**
11+
* @typedef Args
12+
* @property {object} model
13+
* @property {object} filterData
14+
*/
15+
16+
/**
17+
* @typedef Blocks
18+
* @property {[ApiIndexFilter['filteredData']]} default
19+
*/
20+
21+
/**
22+
* @extends Component<{ Args: Args, Blocks: Blocks }>
23+
*/
1024
@classNames('api-index-filter')
1125
export default class ApiIndexFilter extends Component {
1226
@computed('model.methods.[]', filterDataComputedParams)

app/components/api-index.js

+24-7
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,46 @@
1-
/* eslint-disable ember/no-computed-properties-in-native-classes */
2-
import { computed } from '@ember/object';
3-
import Component from '@ember/component';
1+
import Component from '@glimmer/component';
42

3+
/**
4+
* @typedef ItemData
5+
* @property {Array<{ name: string }>} methods
6+
* @property {Array<{ name: string }>} properties
7+
* @property {Array<{ name: string }>} events
8+
*/
9+
10+
/**
11+
* @typedef Args
12+
* @property {ItemData} itemData
13+
*/
14+
15+
/**
16+
* @typedef Blocks
17+
* @property {[{ sections: ApiIndex['sections'] }]} default
18+
*/
19+
20+
/**
21+
* @extends Component<{ Args: Args, Blocks: Blocks }>
22+
*/
523
export default class ApiIndex extends Component {
6-
@computed('itemData.{methods,properties,events}')
724
get sections() {
825
return [
926
{
1027
title: 'Methods',
1128
tab: 'methods',
12-
items: this.itemData.methods,
29+
items: this.args.itemData.methods,
1330
class: 'spec-method-list',
1431
routeSuffix: '.methods.method',
1532
},
1633
{
1734
title: 'Properties',
1835
tab: 'properties',
19-
items: this.itemData.properties,
36+
items: this.args.itemData.properties,
2037
class: 'spec-property-list',
2138
routeSuffix: '.properties.property',
2239
},
2340
{
2441
title: 'Events',
2542
tab: 'events',
26-
items: this.itemData.events,
43+
items: this.args.itemData.events,
2744
class: 'spec-event-list',
2845
routeSuffix: '.events.event',
2946
},

app/registry.d.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import ApiIndex from './components/api-index';
2+
import ApiIndexFilter from './components/api-index-filter';
3+
4+
declare module '@glint/environment-ember-loose/registry' {
5+
export default interface Registry {
6+
ApiIndex: typeof ApiIndex;
7+
ApiIndexFilter: typeof ApiIndexFilter;
8+
}
9+
}

app/templates/class-index.hbs

+9-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,15 @@
66
<ul class="{{section.class}}">
77
{{#each section.items as |item|}}
88
<li data-test-item={{item.name}}>
9-
<LinkTo @route={{concat this.parentName section.routeSuffix }} @models={{array @model.project.id @model.projectVersion.compactVersion @model.name item.name}} @query={{hash anchor=item.name}}>
9+
<LinkTo
10+
@route="{{this.parentName}}{{section.routeSuffix}}"
11+
@models={{array
12+
@model.project.id
13+
@model.projectVersion.compactVersion
14+
@model.name item.name
15+
}}
16+
@query={{hash anchor=item.name}}
17+
>
1018
{{item.name}}
1119
</LinkTo>
1220
</li>
+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
<div>
12
{{yield (hash
23
sections=this.sections)
34
}}
5+
</div>

jsconfig.json

+14-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
{
2-
"compilerOptions": {
3-
"experimentalDecorators": true
4-
},
5-
}
2+
"extends": "@tsconfig/ember",
3+
"compilerOptions": {
4+
"baseUrl": ".",
5+
"paths": {
6+
"ember-api-docs/*": [
7+
"app/*"
8+
]
9+
}
10+
},
11+
"glint": {
12+
"environment": "ember-loose",
13+
"checkStandaloneTemplates": false
14+
}
15+
}

package.json

+20-1
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,26 @@
3030
"@ember/test-helpers": "^2.2.5",
3131
"@glimmer/component": "^1.0.0",
3232
"@glimmer/tracking": "^1.0.0",
33+
"@glint/core": "^0.9.7",
34+
"@glint/environment-ember-loose": "^0.9.7",
3335
"@percy/cli": "^1.0.0-beta.54",
3436
"@percy/ember": "^3.0.0",
37+
"@tsconfig/ember": "^1.0.1",
38+
"@types/ember": "^4.0.2",
39+
"@types/ember-qunit": "^5.0.2",
40+
"@types/ember-resolver": "^5.0.13",
41+
"@types/ember__application": "^4.0.4",
42+
"@types/ember__array": "^4.0.3",
43+
"@types/ember__component": "^4.0.11",
44+
"@types/ember__controller": "^4.0.3",
45+
"@types/ember__object": "^4.0.5",
46+
"@types/ember__polyfills": "^4.0.1",
47+
"@types/ember__routing": "^4.0.12",
48+
"@types/ember__runloop": "^4.0.2",
49+
"@types/ember__service": "^4.0.1",
50+
"@types/ember__string": "^3.0.10",
51+
"@types/ember__test-helpers": "^2.8.2",
52+
"@types/ember__utils": "^4.0.2",
3553
"algoliasearch": "^3.32.1",
3654
"babel-eslint": "^10.1.0",
3755
"bourbon": "5.1.0",
@@ -114,7 +132,8 @@
114132
"sass": "^1.54.4",
115133
"semver-compare": "^1.0.0",
116134
"spawndamnit": "2.0.0",
117-
"testem": "^2.14.0"
135+
"testem": "^2.14.0",
136+
"typescript": "^4.9.3"
118137
},
119138
"engines": {
120139
"node": "14.* || 16.* || 18.*",

0 commit comments

Comments
 (0)