feat(ember-tsc): support Ember 7.1 built-in keywords#1109
Merged
NullVoxPopuli merged 8 commits intoMay 14, 2026
Conversation
97b349b to
d9b940d
Compare
ijlee2
reviewed
May 13, 2026
Comment on lines
+64
to
+81
| // Ember 7.1+ built-in keywords (RFCs 562, 470, 997, 998, 999, 1000, 560, 561, 389). | ||
| // Their template-side signatures live in | ||
| // `@glint/ember-tsc/globals/ember-7.1` (opt-in), and they only resolve | ||
| // at runtime on `ember-source >= 7.1.0`. | ||
| 'on', | ||
| 'fn', | ||
| 'array', | ||
| 'hash', | ||
| 'and', | ||
| 'or', | ||
| 'not', | ||
| 'eq', | ||
| 'neq', | ||
| 'lt', | ||
| 'lte', | ||
| 'gt', | ||
| 'gte', | ||
| 'element', |
Contributor
There was a problem hiding this comment.
I noticed that the strings above (action to yield) are alphabetized. Can we alphabetize the list for Ember 7.1 similarly, and create variables so that it's easier to document and maintain the lists?
The code below is what I mean. (I sorted the RFC IDs, too. I wrote Ember7_1 but see that the name Ember71 is used in types/-private/dsl/globals.d.ts. Feel free to rename Ember7_1 to achieve consistency.)
export default function emberTemplateImportsEnvironment(
options: Record<string, unknown>,
): GlintEnvironmentConfig {
let additionalSpecialForms =
typeof options['additionalSpecialForms'] === 'object'
? (options['additionalSpecialForms'] as GlintSpecialFormConfig)
: {};
const additionalGlobalSpecialForms = additionalSpecialForms.globals ?? {};
const additionalGlobals = Array.isArray(options['additionalGlobals'])
? options['additionalGlobals']
: [];
const globalsForEmber = [
'action',
'component',
'debugger',
'each',
'each-in',
'has-block',
'has-block-params',
'helper',
'if',
'in-element',
'let',
'log',
'modifier',
'mount',
'mut',
'outlet',
'unbound',
'unless',
'with',
'yield',
];
/*
Ember 7.1+ built-in keywords (RFCs 389, 470, 560, 561, 562, 997, 998, 999, 1000).
Their template-side signatures live in `@glint/ember-tsc/globals/ember-7.1` (opt-in),
and they only resolve at runtime on `ember-source >= 7.1.0`.
*/
const globalsForEmber7_1 = [
'and',
'array',
'element',
'eq',
'fn',
'gt',
'gte',
'hash',
'lt',
'lte',
'neq',
'not',
'on',
'or',
];
return {
tags: {
'@glint/ember-tsc/environment-ember-template-imports/-private/tag': {
hbs: {
typesModule: '@glint/ember-tsc/-private/dsl',
specialForms: {
globals: {
if: 'if',
unless: 'if-not',
yield: 'yield',
component: 'bind-invokable',
modifier: 'bind-invokable',
helper: 'bind-invokable',
...additionalGlobalSpecialForms,
},
imports: {
'@ember/helper': {
array: 'array-literal',
hash: 'object-literal',
...additionalSpecialForms.imports?.['@ember/helper'],
},
...additionalSpecialForms.imports,
},
},
globals: [
...globalsForEmber,
...globalsForEmber7_1,
...Object.keys(additionalGlobalSpecialForms),
...additionalGlobals,
],
},
},
},
extensions: {
'.gts': {
kind: 'typed-script',
preprocess,
transform,
},
'.gjs': {
kind: 'untyped-script',
preprocess,
transform,
},
},
};
}
ijlee2
reviewed
May 13, 2026
Comment on lines
+228
to
+231
| // ---- Ember 7.1+ built-in keywords ---- | ||
| // These are gated on `HasEmber71BuiltIns` above. On `ember-source < 7.1.0` | ||
| // each one resolves to `never`, so un-imported usage in a template fails | ||
| // to type-check. |
Contributor
There was a problem hiding this comment.
suggestion
Similarly to https://github.com/typed-ember/glint/pull/1109/changes#r3231711703, maybe we can create temporary variables and sort keys to ease maintenance?
interface KeywordsForEmber {
/* ... */
}
interface KeywordsForEmber7_1 {
/* ... */
}
interface Keywords extends KeywordsForEmber, KeywordsForEmber7_1 {}349e11b to
9ad9fe0
Compare
Adds support for the new built-in template keywords introduced in
ember-source 7.1.0 (RFCs 562, 470, 997, 998, 999, 1000, 560, 561, 389):
on, fn, array, hash, and, or, not, eq, neq, lt, lte, gt, gte, element.
These can be referenced in .gjs/.gts templates without an explicit import
from @ember/helper or @ember/modifier, matching ember-source 7.1+
runtime behavior.
Detection is automatic — no consumer opt-in required:
- The Keywords interface in types/-private/dsl/globals.d.ts probes
'@ember/helper' for the EqHelper symbol (introduced in RFC 561,
shipped in ember-source 7.1.0). When present, each new keyword
resolves to a real HelperLike / ModifierLike / ComponentLike type.
When absent, each resolves to 'never', so un-imported usage in a
template fails to type-check on older Ember versions.
- src/environment-ember-template-imports/-private/environment/index.ts:
add the 14 keywords to the environment's globals list so the parser
recognizes them.
Behavior matrix:
ember-source >= 7.1.0 -> full types; un-imported keywords type-check
ember-source < 7.1.0 -> un-imported keywords resolve to 'never';
Glint reports a clear type error.
no @ember/helper -> keywords are enabled (fallback for non-Ember
Glimmer projects, gated by a @ts-ignore on
the probe import).
9ad9fe0 to
dc2aa0a
Compare
… fixture (#2) The Ember 7.1 detection probe in `globals.d.ts` reads `typeof import('@ember/helper')` and checks the resulting type for an `EqHelper` key. But `@ember/helper` exports `EqHelper` only as an `interface` — `typeof import(...)` surfaces only value exports, so `keyof` never includes `EqHelper`. The probe therefore always collapsed to `false`, `Ember71Only<T>` always resolved to `never`, and every new keyword (`on`, `fn`, `eq`, …) ended up untyped. Switch the probe to the matching `eq` value export added by RFC 561. The previous home for the new keyword tests was `ts-template-imports-app`, which pins `ember-source: ^6.2.0`. With the corrected probe those tests still resolved to `never` against a 6.x ember-source. Bumping that fixture to a 7.1 prerelease shifts the resolved peer set for `@glint/ember-tsc` and reformats unrelated `Modifier`-overload diagnostic snapshots in `package-test-core`. Move the keyword tests into a new `ts-gts-7-1-app` fixture pinned to `ember-source: ~7.1.0-beta.1`. The fixture stays minimal and exercises the real ember-source 7.1 types end-to-end via the probe, without disturbing the existing 6.x fixture's peer resolution. Co-authored-by: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
NullVoxPopuli
approved these changes
May 14, 2026
Merged
aklkv
added a commit
to aklkv/eslint-plugin-ember
that referenced
this pull request
May 14, 2026
…lobals ember-source 7.1.0 makes 14 helpers/modifiers (and, array, element, eq, fn, gt, gte, hash, lt, lte, neq, not, on, or — RFCs 389, 470, 560, 561, 562, 997, 998, 999, 1000) available in strict-mode templates without an explicit import. ESLint's no-undef rule was therefore flagging them as undefined when consumers turned off no-implicit-this etc. on .gjs/.gts files. Add the keywords to both the modern flat-config exports (lib/recommended.mjs's `gjs` and `gts` configs, used via `eslint-plugin-ember/recommended`) and the legacy-style `./configs/base` entry in lib/config/base.js so they are recognised as readonly globals on .gjs/.gts. Detection is gated on the consumer's installed ember-source version via a shared `getEmber71BuiltInKeywords()` helper (lib/utils/ember71-built- in-keywords.js) so pre-7.1 projects keep their existing behaviour. Mirrors Glint's KeywordsForEmber71 gate (typed-ember/glint#1109).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds support for the new built-in template keywords introduced in ember-source 7.1.0 (RFCs 562, 470, 997, 998, 999, 1000, 560, 561, 389): on, fn, array, hash, and, or, not, eq, neq, lt, lte, gt, gte, element.
These can be referenced in .gjs/.gts templates without an explicit import from @ember/helper or @ember/modifier, matching ember-source 7.1+ runtime behavior.
Detection is automatic — no consumer opt-in required:
The Keywords interface in types/-private/dsl/globals.d.ts probes '@ember/helper' for the EqHelper symbol (introduced in RFC 561, shipped in ember-source 7.1.0). When present, each new keyword resolves to a real HelperLike / ModifierLike / ComponentLike type. When absent, each resolves to 'never', so un-imported usage in a template fails to type-check on older Ember versions.
src/environment-ember-template-imports/-private/environment/index.ts: add the 14 keywords to the environment's globals list so the parser recognizes them.
Behavior matrix:
ember-source >= 7.1.0 -> full types; un-imported keywords type-check ember-source < 7.1.0 -> un-imported keywords resolve to 'never'; Glint reports a clear type error.
no @ember/helper -> keywords are enabled (fallback for non-Ember
Glimmer projects, gated by a @ts-ignore on
the probe import).