← Back to Home

Migrating to glint v2

Glint released a Volar-based v2 at the end of September. It's time to update, now that we have migrated to <template> tag. (It's okay if some hbs code remain. They just won't be type-checked.)

Currently, the official upgrade guide is minimal and assumes that you are familiar with the changes made in the Ember ecosystem. This post aims to fill in gaps and provide a guide that everyone can use.

1. Starting point

I'll assume the following about your dependencies to cover the 80% case.

  • Glint is on 1.5.2, i.e. the "core four" (@glint/core, @glint/environment-ember-loose, @glint/environment-ember-template-imports, and @glint/template) run on this version.
  • Your tsconfig.json extends @tsconfig/ember in apps and addons.
  • You consume native types from Ember, i.e. ember-source is on 5.1.0 or higher.

These conditions imply that your tsconfig.json can be as simple as the following for Ember apps:

tsconfig.json
{
  "extends": "@tsconfig/ember/tsconfig.json",
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "my-app/tests/*": ["tests/*"],
      "my-app/*": ["app/*"],
      "*": ["types/*"]
    },
    "types": ["ember-source/types"]
  },
  "glint": {
    "environment": ["ember-loose", "ember-template-imports"]
  },
  "include": ["app/**/*", "tests/**/*", "types/**/*"]
}

Not to worry, I'll describe alternatives if conditions 2 and 3 aren't met.

2. How to migrate apps

The following steps apply to both v1 (classic, Webpack) and v2 (Vite) apps. You can run lint:types to check your progress.

a. Update package.json

Replace the core four with @glint/ember-tsc and @glint/template. Optionally, add @glint/tsserver-plugin to support all IDEs (not just VS Code).

{
  "name": "my-app",
  "scripts: {
    "lint:types": "glint"
  },
  "devDependencies": {
-     "@glint/core": "^1.5.2",
-     "@glint/environment-ember-loose": "^1.5.2",
-     "@glint/environment-ember-template-imports": "^1.5.2",
-     "@glint/template": "^1.5.2",
+     "@glint/ember-tsc": "^1.0.8",
+     "@glint/template": "^1.7.3",
+     "@glint/tsserver-plugin": "^2.0.8",
    "@tsconfig/ember": "^3.0.12",
    "typescript": "^5.9.3"
  }
}

Then, replace the glint command with ember-tsc.

{
  "name": "my-app",
  "scripts: {
-     "lint:types": "glint"
+     "lint:types": "ember-tsc --noEmit"
  },
  "devDependencies": {
    "@glint/ember-tsc": "^1.0.8",
    "@glint/template": "^1.7.3",
    "@glint/tsserver-plugin": "^2.0.8",
    "@tsconfig/ember": "^3.0.12",
    "typescript": "^5.9.3"
  }
}

Finally, use your package manager to install the dependencies.

b. Update tsconfig.json

The simplest configuration from above becomes,

tsconfig.json
{
  "extends": "@tsconfig/ember/tsconfig.json",
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "my-app/tests/*": ["tests/*"],
      "my-app/*": ["app/*"],
      "*": ["types/*"]
    },
    "plugins": [
      {
        "name": "@glint/tsserver-plugin"
      }
    ],
    "types": ["ember-source/types", "@glint/ember-tsc/types"]
  },
  "include": ["app/**/*", "tests/**/*", "types/**/*"]
}

That is, remove the glint field, since @glint/environment-ember-loose and @glint/environment-ember-template-imports no longer exist. Then, update compilerOptions.{plugins,types} to account for @glint/tsserver-plugin and the ambient types from @glint/ember-tsc.

i. Use @ember/app-tsconfig?

Only the extends field should differ.

tsconfig.json
{
  "extends": "@ember/app-tsconfig"
}

ii. Use DefinitelyTyped packages?

These packages (e.g. @types/ember__component), whose types are compatible with ember-source@3.28 and 4.x, can continue to be used. That is, you can update glint to v2 if ember-source is at least 3.27 (the minimum version that ember-template-imports supports).

The types field excludes ember-source, since it doesn't provide types yet.

tsconfig.json
{
  "types": ["@glint/ember-tsc/types"]
}

iii. Use additionalGlobals or additionalSpecialForms?

If you used additionalGlobals (unlikely) or additionalSpecialForms (possible for ember-truth-helpers), re-introduce it in the glint field as a top-level key.

Here's how to configure ember-truth-helpers so that its helpers—whether named- or default-imported—can perform truthiness narrowing.

tsconfig.json
{
  "extends": "@tsconfig/ember/tsconfig.json",
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "my-app/tests/*": ["tests/*"],
      "my-app/*": ["app/*"],
      "*": ["types/*"]
    },
    "plugins": [
      {
        "name": "@glint/tsserver-plugin"
      }
    ],
    "types": ["ember-source/types", "@glint/ember-tsc/types"]
  },
  "include": ["app/**/*", "tests/**/*", "types/**/*"],
  "glint": {
    "additionalSpecialForms": {
      "imports": {
        "ember-truth-helpers": {
          "and": "&&",
          "eq": "===",
          "not": "!",
          "not-eq": "!==",
          "or": "||"
        },
        "ember-truth-helpers/helpers/and": {
          "default": "&&"
        },
        "ember-truth-helpers/helpers/eq": {
          "default": "==="
        },
        "ember-truth-helpers/helpers/not": {
          "default": "!"
        },
        "ember-truth-helpers/helpers/not-eq": {
          "default": "!=="
        },
        "ember-truth-helpers/helpers/or": {
          "default": "||"
        }
      }
    }
  }
}

c. Remove references to glint v1

Since @glint/environment-ember-loose and @glint/environment-ember-template-imports are gone, you can remove these dead code:

  • declare module blocks (look for declare module '@glint/environment-ember-loose/registry') in the app's components, helpers, modifiers, and types/global.d.ts (or a similar file).
  • TestContext passed to render() in rendering tests.
  • Imports of @glint/environment-ember-loose and @glint/environment-ember-template-imports, as well as of all template registries, in types/global.d.ts.

3. How to migrate v1 addons

ember-cli began to create v1 addons with typescript differently in 5.2.0, so there are two scenarios to consider.

As a rule of thumb, your addon came from the old blueprint if it depends on ember-cli-typescript (the prepack script shows ember ts:precompile). If from the new one, the addon will have 2 configuration files for TypeScript, and the prepack script will show tsc --project tsconfig.declarations.json.

For brevity, this guide only considers v1 addons generated from the old blueprint and won't mention @glint/tsserver-plugin again. If you had created a v1 addon from the newer blueprint, the idea remains the same: Update tsconfig.json and the scripts in package.json so that your addon is linted and built the same.

package.json
{
  "name": "my-v1-addon",
  "scripts": {
-     "lint:types": "glint",
+     "lint:types": "ember-tsc --noEmit",
    "prepack": "ember ts:precompile",
    "postpack": "ember ts:clean"
  },
  "dependencies": {
    "ember-cli-typescript": "^5.3.0",
    "ember-template-imports": "^4.3.0"
  },
  "devDependencies": {
-     "@glint/core": "^1.5.2",
-     "@glint/environment-ember-loose": "^1.5.2",
-     "@glint/environment-ember-template-imports": "^1.5.2",
-     "@glint/template": "^1.5.2",
+     "@glint/ember-tsc": "^1.0.8",
+     "@glint/template": "^1.7.3",
    "@tsconfig/ember": "^3.0.12",
    "typescript": "^5.9.3"
  }
}
tsconfig.json
{
  "extends": "@tsconfig/ember/tsconfig.json",
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "dummy/tests/*": ["tests/*"],
      "dummy/*": ["tests/dummy/app/*", "app/*"],
      "my-addon": ["addon"],
      "my-addon/*": ["addon/*"],
      "my-addon/test-support": ["addon-test-support"],
      "my-addon/test-support/*": ["addon-test-support/*"],
      "*": ["types/*"]
    },
+     "types": ["ember-source/types", "@glint/ember-tsc/types"]
  },
-   "glint": {
-     "environment": ["ember-loose", "ember-template-imports"]
-   },
  "include": ["addon/**/*", "addon-test-support/**/*", "tests/**/*", "types/**/*"]
}

Finally, remove any references to @glint/environment-ember-loose. The declare module blocks and template registry can be removed if you decide to not support consuming apps and addons with loose-mode templates.

4. How to migrate v2 addons

First, replace the dependencies for glint and update the lint:types script.

package.json
{
  "name": "my-v2-addon",
  "scripts": {
    "build": "rollup --config"
-     "lint:types": "glint",
+     "lint:types": "ember-tsc --noEmit"
  },
  "devDependencies": {
-     "@glint/core": "^1.5.2",
-     "@glint/environment-ember-loose": "^1.5.2",
-     "@glint/environment-ember-template-imports": "^1.5.2",
-     "@glint/template": "^1.5.2",
+     "@glint/ember-tsc": "^1.0.8",
+     "@glint/template": "^1.7.3",
    "@tsconfig/ember": "^3.0.12",
    "typescript": "^5.9.3"
  }
}

Next, we ensure that the package is linted and built the same.

rollup.config.mjs
import { Addon } from '@embroider/addon-dev/rollup';
import { babel } from '@rollup/plugin-babel';
 
const addon = new Addon({
  srcDir: 'src',
  destDir: 'dist',
});
 
export default {
  output: addon.output(),
  plugins: [
    addon.publicEntrypoints(['**/*.js', 'index.ts', 'template-registry.ts']),
    addon.appReexports([
      'components/**/*.js',
      'helpers/**/*.js',
      'modifiers/**/*.js',
      'services/**/*.js',
    ]),
    addon.dependencies(),
    babel({
      babelHelpers: 'bundled',
      configFile: './babel.config.mjs',
      extensions: ['.gjs', '.gts', '.js', '.ts'],
    }),
    addon.hbs(),
    addon.gjs(),
    // Emit .d.ts declaration files
-     addon.declarations('declarations'),
+     addon.declarations(
+       'declarations',
+       `pnpm ember-tsc --declaration --project tsconfig.json`,
+     ),
    addon.keepAssets(['**/*.css']),
    addon.clean(),
  ],
};
tsconfig.json
{
  "extends": "@tsconfig/ember/tsconfig.json",
  "compilerOptions": {
    "allowImportingTsExtensions": true,
+     "declaration": true,
    "declarationDir": "declarations",
+     "declarationMap": true,
+     "emitDeclarationOnly": true,
+     "noEmit": false,
    "rootDir": "./src",
-     "types": ["ember-source/types"]
+     "types": ["ember-source/types", "@glint/ember-tsc/types"]
  },
-   "glint": {
-     "environment": ["ember-loose", "ember-template-imports"]
-   },
  "include": ["src/**/*", "unpublished-development-types/**/*"]
}

Finally, remove references to @glint/environment-ember-loose. In particular, the file unpublished-development-types/index.d.ts can be cleared or removed. The template registry can be removed if you decide to not support consuming apps and addons with loose-mode templates.