├── .eslintignore ├── .eslintrc ├── .gitattributes ├── .gitbook.yaml ├── .github └── workflows │ ├── ci.yml │ ├── plan-release.yml │ ├── publish-unstable.yml │ └── publish.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc ├── .vscode └── launch.json ├── ARCHITECTURE.md ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── GLINT_V2.md ├── LICENSE ├── README.md ├── RELEASE.md ├── bin ├── build-augmentations.mjs ├── build-elements.mjs ├── link-build.mjs ├── link-install.mjs └── packages.mjs ├── docs ├── configuration │ ├── _index.md │ └── project-references.md ├── contents.md ├── diagnosing-common-error-messages.md ├── directives.md ├── ember │ ├── authoring-addons.md │ ├── component-signatures.md │ ├── helper-and-modifier-signatures.md │ ├── installation.md │ ├── rendering-tests.md │ ├── routes-and-controllers.md │ ├── template-imports.md │ ├── template-only-components.md │ ├── template-registry.md │ └── using-addons.md ├── getting-started.md ├── glint-types.md ├── known-limitations.md ├── migrating.md ├── overview.md ├── using-glint │ └── ember │ │ └── README.md └── with-js.md ├── package.json ├── packages ├── core │ ├── .gitignore │ ├── README.md │ ├── bin │ │ ├── glint-language-server.js │ │ └── glint.js │ ├── package.json │ ├── src │ │ ├── cli │ │ │ └── run-volar-tsc.ts │ │ ├── config │ │ │ ├── config.ts │ │ │ ├── environment.ts │ │ │ ├── index.ts │ │ │ ├── loader.ts │ │ │ └── types.cts │ │ ├── index.ts │ │ ├── language-server │ │ │ └── util │ │ │ │ └── index.ts │ │ ├── plugins │ │ │ └── gts-gjs-plugin.ts │ │ ├── transform │ │ │ ├── diagnostics │ │ │ │ ├── augmentation.ts │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── template │ │ │ │ ├── code-features.ts │ │ │ │ ├── glimmer-ast-mapping-tree.ts │ │ │ │ ├── inlining │ │ │ │ │ ├── companion-file.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── tagged-strings.ts │ │ │ │ ├── map-template-contents.ts │ │ │ │ ├── rewrite-module.ts │ │ │ │ ├── scope-stack.ts │ │ │ │ ├── template-to-typescript.ts │ │ │ │ └── transformed-module.ts │ │ │ └── util.ts │ │ └── volar │ │ │ ├── ember-language-plugin.ts │ │ │ ├── gts-virtual-code.ts │ │ │ ├── language-server.ts │ │ │ ├── loose-mode-backing-component-class-virtual-code.ts │ │ │ └── script-snapshot.ts │ ├── tsconfig.json │ └── types │ │ └── silent-error.d.ts ├── environment-ember-loose │ ├── -private │ │ ├── dsl │ │ │ ├── globals.d.ts │ │ │ ├── index.d.ts │ │ │ └── integration-declarations.d.ts │ │ ├── environment │ │ │ └── index.ts │ │ ├── index.ts │ │ └── intrinsics │ │ │ ├── action.d.ts │ │ │ ├── concat.d.ts │ │ │ ├── each-in.d.ts │ │ │ ├── each.d.ts │ │ │ ├── fn.d.ts │ │ │ ├── get.d.ts │ │ │ ├── input.d.ts │ │ │ ├── link-to.d.ts │ │ │ ├── log.d.ts │ │ │ ├── mount.d.ts │ │ │ ├── mut.d.ts │ │ │ ├── on.d.ts │ │ │ ├── outlet.d.ts │ │ │ ├── textarea.d.ts │ │ │ ├── unbound.d.ts │ │ │ └── unique-id.d.ts │ ├── .eslintignore │ ├── .gitignore │ ├── README.md │ ├── __tests__ │ │ ├── environment.test.ts │ │ ├── tsconfig.json │ │ └── type-tests │ │ │ ├── ember-component.test.ts │ │ │ ├── glimmer-component.test.ts │ │ │ ├── helper.test.ts │ │ │ ├── intrinsics │ │ │ ├── action.test.ts │ │ │ ├── component.test.ts │ │ │ ├── concat.test.ts │ │ │ ├── each-in.test.ts │ │ │ ├── each.test.ts │ │ │ ├── fn.test.ts │ │ │ ├── get.test.ts │ │ │ ├── helper.test.ts │ │ │ ├── input.test.ts │ │ │ ├── link-to.test.ts │ │ │ ├── log.test.ts │ │ │ ├── modifier.test.ts │ │ │ ├── mount.test.ts │ │ │ ├── mut.test.ts │ │ │ ├── on.test.ts │ │ │ ├── outlet.test.ts │ │ │ ├── textarea.test.ts │ │ │ ├── unbound.test.ts │ │ │ └── unique-id.test.ts │ │ │ ├── modifier.test.ts │ │ │ ├── route-and-controller.test.ts │ │ │ ├── template-only.test.ts │ │ │ └── tsconfig.json │ ├── package.json │ ├── registry │ │ └── index.d.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── environment-ember-template-imports │ ├── -private │ │ ├── dsl │ │ │ ├── globals.d.ts │ │ │ ├── index.d.ts │ │ │ └── integration-declarations.d.ts │ │ ├── environment │ │ │ ├── common.ts │ │ │ ├── index.ts │ │ │ ├── preprocess.ts │ │ │ └── transform.ts │ │ └── index.ts │ ├── .eslintignore │ ├── .gitignore │ ├── README.md │ ├── __tests__ │ │ ├── transformation.test.ts │ │ └── tsconfig.json │ ├── globals │ │ └── index.d.ts │ ├── package.json │ ├── tsconfig.json │ └── vitest.config.ts ├── template │ ├── -private │ │ ├── dsl.d.ts │ │ ├── dsl │ │ │ ├── elements.d.ts │ │ │ ├── emit.d.ts │ │ │ ├── lib.dom.augmentation.d.ts │ │ │ ├── resolve.d.ts │ │ │ └── types.d.ts │ │ ├── index.d.ts │ │ ├── integration.d.ts │ │ ├── keywords.d.ts │ │ ├── keywords │ │ │ ├── -bind-invokable.d.ts │ │ │ ├── component.d.ts │ │ │ ├── debugger.d.ts │ │ │ ├── each.d.ts │ │ │ ├── has-block-params.d.ts │ │ │ ├── has-block.d.ts │ │ │ ├── helper.d.ts │ │ │ ├── in-element.d.ts │ │ │ ├── let.d.ts │ │ │ ├── modifier.d.ts │ │ │ └── with.d.ts │ │ └── signature.d.ts │ ├── .gitignore │ ├── README.md │ ├── __tests__ │ │ ├── attributes.test.ts │ │ ├── custom-invokable.test.ts │ │ ├── emit-component.test.ts │ │ ├── emit-content.test.ts │ │ ├── keywords │ │ │ ├── component.test.ts │ │ │ ├── debugger.test.ts │ │ │ ├── each.test.ts │ │ │ ├── in-element.test.ts │ │ │ ├── let.test.ts │ │ │ └── with.test.ts │ │ ├── resolution.test.ts │ │ ├── signature.test.ts │ │ ├── test-component.ts │ │ └── tsconfig.json │ ├── package.json │ └── tsconfig.json ├── tsserver-plugin │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ └── typescript-server-plugin.ts │ └── tsconfig.json ├── type-test │ ├── .gitignore │ ├── README.md │ ├── __tests__ │ │ ├── classic.test.ts │ │ ├── fccts.test.gts │ │ └── tsconfig.json │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json └── vscode │ ├── .gitignore │ ├── .vscodeignore │ ├── LICENSE │ ├── README.md │ ├── __fixtures__ │ ├── ember-app-loose-and-gts │ │ ├── app │ │ │ ├── app.ts │ │ │ ├── components │ │ │ │ ├── Greeting.gts │ │ │ │ ├── GreetingUntyped.gjs │ │ │ │ ├── Other.gts │ │ │ │ ├── OtherUntyped.gjs │ │ │ │ ├── colocated-folder │ │ │ │ │ └── index.gts │ │ │ │ ├── colocated-layout-with-errors.hbs │ │ │ │ ├── colocated-layout-with-errors.ts │ │ │ │ ├── colocated-layout.hbs │ │ │ │ ├── colocated-layout.ts │ │ │ │ ├── template-only.hbs │ │ │ │ └── vanilla.ts │ │ │ ├── controllers │ │ │ │ └── classic-controller-route.ts │ │ │ ├── pods │ │ │ │ ├── pod-controller-route │ │ │ │ │ ├── controller.ts │ │ │ │ │ ├── route.ts │ │ │ │ │ └── template.hbs │ │ │ │ └── pod-lone-route │ │ │ │ │ ├── route.ts │ │ │ │ │ └── template.hbs │ │ │ ├── routes │ │ │ │ ├── classic-controller-route.ts │ │ │ │ └── classic-lone-route.ts │ │ │ └── templates │ │ │ │ ├── classic-controller-route.hbs │ │ │ │ └── classic-lone-route.hbs │ │ ├── package.json │ │ ├── tests │ │ │ └── integration │ │ │ │ └── component-test.ts │ │ └── tsconfig.json │ ├── ember-app │ │ ├── app │ │ │ ├── app.ts │ │ │ ├── components │ │ │ │ ├── colocated-layout.hbs │ │ │ │ ├── colocated-layout.ts │ │ │ │ ├── pod-layout │ │ │ │ │ ├── component.ts │ │ │ │ │ └── template.hbs │ │ │ │ └── template-only.hbs │ │ │ ├── controllers │ │ │ │ └── classic-controller-route.ts │ │ │ ├── pods │ │ │ │ ├── pod-controller-route │ │ │ │ │ ├── controller.ts │ │ │ │ │ ├── route.ts │ │ │ │ │ └── template.hbs │ │ │ │ └── pod-lone-route │ │ │ │ │ ├── route.ts │ │ │ │ │ └── template.hbs │ │ │ ├── routes │ │ │ │ ├── classic-controller-route.ts │ │ │ │ └── classic-lone-route.ts │ │ │ └── templates │ │ │ │ ├── classic-controller-route.hbs │ │ │ │ └── classic-lone-route.hbs │ │ ├── package.json │ │ ├── tests │ │ │ └── integration │ │ │ │ └── component-test.ts │ │ └── tsconfig.json │ └── template-imports-app-ts-plugin │ │ ├── package.json │ │ ├── src │ │ ├── Greeting.gts │ │ ├── file.ts │ │ └── index.gts │ │ └── tsconfig.json │ ├── __tests__ │ ├── helpers │ │ └── async.ts │ ├── support │ │ ├── launch-from-cli.mts │ │ ├── vscode-runner-language-server.ts │ │ ├── vscode-runner-ts-plugin.ts │ │ └── vscode-runner.ts │ └── ts-plugin-tests │ │ ├── smoketest-ember-app-loose-and-gts.test.ts │ │ └── smoketest-template-imports-ts-plugin.test.ts │ ├── assets │ ├── glimmer-js-dark.svg │ ├── glimmer-js-light.svg │ ├── glimmer-ts-dark.svg │ ├── glimmer-ts-light.svg │ └── glint.png │ ├── package.json │ ├── schemas │ └── tsconfig.schema.json │ ├── scripts │ ├── build.mjs │ └── import-meta-url.js │ ├── src │ ├── config.ts │ ├── extension.ts │ ├── generated-meta.ts │ ├── hybrid-mode.ts │ └── language-client.ts │ └── tsconfig.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── test-packages ├── package-test-core │ ├── __tests__ │ │ ├── cli │ │ │ ├── build-watch.test.ts │ │ │ ├── build.test.ts │ │ │ ├── check-watch.test.ts │ │ │ ├── check.test.ts │ │ │ ├── declaration.test.ts │ │ │ └── incremental.test.ts │ │ ├── config │ │ │ ├── environment.test.ts │ │ │ ├── load-config.test.ts │ │ │ └── loader.test.ts │ │ ├── language-server │ │ │ ├── completions.test.ts │ │ │ ├── definitions.test.ts │ │ │ ├── diagnostic-augmentation.test.ts │ │ │ ├── diagnostics.test.ts │ │ │ ├── document-symbols.test.ts │ │ │ ├── hover.test.ts │ │ │ ├── imports.test.ts │ │ │ ├── references.test.ts │ │ │ └── rename.test.ts │ │ ├── support │ │ │ └── character-position-viewer.html │ │ └── transform │ │ │ ├── rewrite.test.ts │ │ │ └── template-to-typescript.test.ts │ ├── package.json │ ├── tsconfig.json │ └── vitest.config.ts ├── package-test-template │ ├── __tests__ │ │ ├── component-like.test.ts │ │ ├── helper-like.test.ts │ │ ├── modifier-like.test.ts │ │ └── test-component.ts │ ├── package.json │ └── tsconfig.json ├── test-utils │ ├── .gitignore │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── shared-test-workspace.ts │ └── tsconfig.json ├── ts-ember-app │ ├── .editorconfig │ ├── .ember-cli │ ├── .eslintrc.js │ ├── .gitignore │ ├── .template-lintrc.js │ ├── .watchmanconfig │ ├── README.md │ ├── app │ │ ├── app.js │ │ ├── components │ │ │ ├── .gitkeep │ │ │ ├── bar │ │ │ │ ├── index.hbs │ │ │ │ └── index.ts │ │ │ ├── ember-component.hbs │ │ │ ├── ember-component.ts │ │ │ ├── foo.hbs │ │ │ ├── foo.ts │ │ │ ├── js-component.hbs │ │ │ ├── js-component.js │ │ │ ├── no-backing-module.hbs │ │ │ ├── nocheck-me.hbs │ │ │ ├── template-only-module.hbs │ │ │ ├── template-only-module.ts │ │ │ ├── test-cases │ │ │ │ └── subclassing │ │ │ │ │ ├── child.hbs │ │ │ │ │ ├── child.ts │ │ │ │ │ ├── parent.hbs │ │ │ │ │ └── parent.ts │ │ │ ├── wrapper-component.hbs │ │ │ └── wrapper-component.ts │ │ ├── config │ │ │ └── environment.d.ts │ │ ├── controllers │ │ │ ├── .gitkeep │ │ │ └── classic-route.ts │ │ ├── helpers │ │ │ ├── .gitkeep │ │ │ ├── affix.ts │ │ │ └── repeat.ts │ │ ├── index.html │ │ ├── models │ │ │ └── .gitkeep │ │ ├── pods │ │ │ └── components │ │ │ │ └── baz │ │ │ │ ├── component.ts │ │ │ │ └── template.hbs │ │ ├── router.js │ │ ├── routes │ │ │ └── classic-route.ts │ │ ├── styles │ │ │ └── app.css │ │ └── templates │ │ │ ├── application.hbs │ │ │ └── classic-route.hbs │ ├── config │ │ ├── ember-cli-update.json │ │ ├── environment.js │ │ ├── optional-features.json │ │ └── targets.js │ ├── ember-cli-build.js │ ├── package.json │ ├── public │ │ └── robots.txt │ ├── testem.js │ ├── tests │ │ ├── helpers │ │ │ └── index.ts │ │ ├── index.html │ │ ├── integration │ │ │ ├── .gitkeep │ │ │ ├── components │ │ │ │ ├── bar-test.ts │ │ │ │ ├── baz-test.ts │ │ │ │ ├── ember-component-test.ts │ │ │ │ ├── render-test.ts │ │ │ │ └── template-only-module-test.ts │ │ │ ├── helpers │ │ │ │ └── repeat-test.ts │ │ │ └── types │ │ │ │ └── empty-signature-test.ts │ │ ├── test-helper.js │ │ └── unit │ │ │ └── .gitkeep │ ├── tsconfig.json │ ├── types │ │ ├── ember-data │ │ │ └── types │ │ │ │ └── registries │ │ │ │ └── model.d.ts │ │ └── template-registry.d.ts │ ├── vendor │ │ └── .gitkeep │ └── wat.ts ├── ts-ember-preview-types │ ├── .eslintignore │ ├── .eslintrc.js │ ├── README.md │ ├── app │ │ ├── app.js │ │ ├── components │ │ │ ├── .gitkeep │ │ │ ├── bar │ │ │ │ ├── index.hbs │ │ │ │ └── index.ts │ │ │ ├── ember-component.hbs │ │ │ ├── ember-component.ts │ │ │ ├── foo.hbs │ │ │ ├── foo.ts │ │ │ ├── js-component.hbs │ │ │ ├── js-component.js │ │ │ ├── nocheck-me.hbs │ │ │ ├── template-only-module.hbs │ │ │ ├── template-only-module.ts │ │ │ ├── template-only.hbs │ │ │ ├── test-cases │ │ │ │ └── subclassing │ │ │ │ │ ├── child.hbs │ │ │ │ │ ├── child.ts │ │ │ │ │ ├── parent.hbs │ │ │ │ │ └── parent.ts │ │ │ ├── wrapper-component.hbs │ │ │ └── wrapper-component.ts │ │ ├── config │ │ │ └── environment.d.ts │ │ ├── controllers │ │ │ ├── .gitkeep │ │ │ └── classic-route.ts │ │ ├── helpers │ │ │ ├── .gitkeep │ │ │ ├── affix.ts │ │ │ └── repeat.ts │ │ ├── models │ │ │ └── .gitkeep │ │ ├── no-dt.ts │ │ ├── pods │ │ │ └── components │ │ │ │ └── baz │ │ │ │ ├── component.ts │ │ │ │ └── template.hbs │ │ ├── router.js │ │ ├── routes │ │ │ └── classic-route.ts │ │ └── templates │ │ │ ├── application.hbs │ │ │ └── classic-route.hbs │ ├── package.json │ ├── tsconfig.json │ └── types │ │ ├── ember-data │ │ └── types │ │ │ └── registries │ │ │ └── model.d.ts │ │ └── template-registry.d.ts ├── ts-plugin-test-app │ ├── .vscode │ │ └── settings.json │ ├── package.json │ ├── src │ │ ├── ember-component.hbs │ │ ├── ember-component.ts │ │ ├── foo-glimmer.gts │ │ ├── glimmer.gts │ │ ├── test.ts │ │ └── test2.ts │ └── tsconfig.json ├── ts-template-imports-app │ ├── .gitignore │ ├── package.json │ ├── src │ │ ├── Greeting.gts │ │ ├── GreetingAutoImportTest.gts │ │ ├── GreetingUntyped.gjs │ │ ├── NoTemplate.gts │ │ ├── Playground.gts │ │ ├── colocated │ │ │ └── index.gts │ │ ├── empty-fixture-untyped.gjs │ │ ├── empty-fixture.gts │ │ ├── empty-fixture2.gts │ │ ├── index.gts │ │ ├── normal.ts │ │ └── other.ts │ ├── tsconfig.json │ └── types │ │ └── index.d.ts └── unstable-release │ ├── .eslintrc.cjs │ ├── README.md │ ├── package.json │ ├── publish.js │ ├── version.js │ └── workspaces.js ├── tsconfig.compileroptions.json └── tsconfig.json /.eslintignore: -------------------------------------------------------------------------------- 1 | # Compiled output 2 | /packages/*/lib 3 | /test-packages/*/lib 4 | packages/template/-private/dsl/elements.d.ts 5 | packages/template/-private/dsl/lib.dom.augmentation.d.ts 6 | 7 | dist/ 8 | __fixtures__ 9 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js text eol=lf 2 | *.ts text eol=lf 3 | *.css text eol=lf 4 | *.hbs text eol=lf 5 | *.json text eol=lf 6 | -------------------------------------------------------------------------------- /.gitbook.yaml: -------------------------------------------------------------------------------- 1 | root: docs 2 | 3 | structure: 4 | readme: overview.md 5 | summary: contents.md 6 | -------------------------------------------------------------------------------- /.github/workflows/publish-unstable.yml: -------------------------------------------------------------------------------- 1 | # For every push to the master branch, this publishes an NPM package to the 2 | # "unstable" NPM tag. 3 | 4 | name: Publish Unstable 5 | 6 | on: 7 | workflow_dispatch: 8 | push: 9 | branches: 10 | - main 11 | 12 | concurrency: 13 | group: publish-unstable-${{ github.head_ref || github.ref }} 14 | cancel-in-progress: true 15 | 16 | jobs: 17 | publish: 18 | name: "NPM Publish" 19 | runs-on: ubuntu-latest 20 | 21 | steps: 22 | - uses: actions/checkout@v4 23 | - uses: pnpm/action-setup@v2 24 | - uses: actions/setup-node@v4 25 | with: 26 | # This creates an .npmrc that reads the NODE_AUTH_TOKEN environment variable 27 | registry-url: 'https://registry.npmjs.org' 28 | cache: 'pnpm' 29 | 30 | - run: pnpm install 31 | 32 | # - name: Lint 33 | # run: pnpm lint 34 | - name: Build 35 | run: pnpm build 36 | 37 | 38 | - name: set versions 39 | run: node ./test-packages/unstable-release/version.js 40 | 41 | - name: npm publish 42 | run: node ./test-packages/unstable-release/publish.js 43 | env: 44 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 45 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | # For every push to the primary branch with .release-plan.json modified, 2 | # runs release-plan. 3 | 4 | name: Publish Stable 5 | 6 | on: 7 | workflow_dispatch: 8 | push: 9 | branches: 10 | - main 11 | - master 12 | paths: 13 | - '.release-plan.json' 14 | 15 | concurrency: 16 | group: publish-${{ github.head_ref || github.ref }} 17 | cancel-in-progress: true 18 | 19 | jobs: 20 | publish: 21 | name: "NPM Publish" 22 | runs-on: ubuntu-latest 23 | permissions: 24 | contents: write 25 | pull-requests: write 26 | id-token: write 27 | attestations: write 28 | 29 | steps: 30 | - uses: actions/checkout@v4 31 | - uses: pnpm/action-setup@v4 32 | - uses: actions/setup-node@v4 33 | with: 34 | node-version: 18 35 | # This creates an .npmrc that reads the NODE_AUTH_TOKEN environment variable 36 | registry-url: 'https://registry.npmjs.org' 37 | cache: pnpm 38 | - run: pnpm install --frozen-lockfile 39 | - name: Publish to NPM 40 | run: NPM_CONFIG_PROVENANCE=true pnpm release-plan publish 41 | env: 42 | GITHUB_AUTH: ${{ secrets.GITHUB_TOKEN }} 43 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | pnpm-debug.log 3 | .eslintcache 4 | *.tsbuildinfo 5 | *.js.map 6 | tsserver.log 7 | dist/ 8 | .log/ 9 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Compiled output 2 | /packages/*/lib 3 | /test-packages/*/lib 4 | dist/ 5 | *.yaml 6 | *.yml 7 | 8 | # Hidden directories 9 | .*/ 10 | 11 | # Templates (Prettier wreaks havoc with `@glint` directives) 12 | *.hbs 13 | 14 | # Environment packages use a weird structure because they're a mix 15 | # of generated and hand-rolled `.d.ts` files. 16 | /packages/environment*/**/*.js 17 | /packages/environment*/**/*.d.ts 18 | !/packages/environment*/-private/dsl/**/*.d.ts 19 | !/packages/environment*/-private/intrinsics/**/*.d.ts 20 | packages/template/-private/dsl/elements.d.ts 21 | packages/template/-private/dsl/lib.dom.augmentation.d.ts 22 | 23 | packages/core/__tests__/support/character-position-viewer.html 24 | test-packages/*/__tests__/support/*.html 25 | 26 | /packages/vscode/src/generated-meta.ts 27 | 28 | # Markdown files: the formatting Prettier uses by default *does. not. match.* 29 | # the formatting applied by Gitbook. Having both present is a recipe for ongoing 30 | # CI problems. 31 | *.md 32 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "useTabs": false, 4 | "printWidth": 100, 5 | "singleQuote": true 6 | } 7 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | // For this to work, make sure you're running `tsc --build --watch` at the root, AND 6 | // `pnpm bundle:watch` from within vscode directory. 7 | "name": "Debug Extension (TS Plugin, .gts)", 8 | "type": "extensionHost", 9 | "request": "launch", 10 | // "preLaunchTask": "npm: build", 11 | "autoAttachChildProcesses": true, 12 | "runtimeExecutable": "${execPath}", 13 | "outFiles": [ 14 | "${workspaceFolder}/**/*.js", 15 | "!**/node_modules/**" 16 | ], 17 | "args": [ 18 | "--extensionDevelopmentPath=${workspaceFolder}/packages/vscode", 19 | "--disable-extensions", // comment to activate your local extensions 20 | "${workspaceFolder}/test-packages/ts-plugin-test-app" 21 | ] 22 | }, 23 | { 24 | // For this to work, make sure you're running `tsc --build --watch` at the root, AND 25 | // `pnpm bundle:watch` from within vscode directory. 26 | "name": "Debug Extension (TS Plugin, ember-app-loose-and-gts)", 27 | "type": "extensionHost", 28 | "request": "launch", 29 | // "preLaunchTask": "npm: build", 30 | "autoAttachChildProcesses": true, 31 | "runtimeExecutable": "${execPath}", 32 | "outFiles": [ 33 | "${workspaceFolder}/**/*.js", 34 | "!**/node_modules/**" 35 | ], 36 | "args": [ 37 | "--extensionDevelopmentPath=${workspaceFolder}/packages/vscode", 38 | "--disable-extensions", // comment to activate your local extensions 39 | "${workspaceFolder}/packages/vscode/__fixtures__/ember-app-loose-and-gts/" 40 | ] 41 | }, 42 | { 43 | "name": "Attach to TS Server", 44 | "type": "node", 45 | "request": "attach", 46 | "port": 5667, 47 | "sourceMaps": true, 48 | "outFiles": [ 49 | "${workspaceFolder}/**/*.js", 50 | "!**/node_modules/**" 51 | ], 52 | } 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | The Ember team and community are committed to everyone having a safe and inclusive experience. 2 | 3 | **Our Community Guidelines / Code of Conduct can be found here**: 4 | 5 | https://emberjs.com/guidelines/ 6 | 7 | For a history of updates, see the page history here: 8 | 9 | https://github.com/emberjs/website/commits/master/source/guidelines.html.erb 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 The Ember TypeScript team and contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Glint [![CI](https://github.com/typed-ember/glint/actions/workflows/ci.yml/badge.svg)](https://github.com/typed-ember/glint/actions?query=workflow%3ACI) 2 | 3 | TypeScript-powered tooling for Glimmer templates. 4 | 5 | ## Glint 2 (Alpha) 6 | 7 | This branch contains the code for Glint V2, which is currently in an **alpha** state. Glint V2 is a major refactor of Glint V1 to run atop the [Volar.js language tooling framework], which powers the language tooling for Vue and many others. 8 | 9 | See [Glint V2](./GLINT_V2.md) for information on how to try out Glint V2. 10 | 11 | [Volar.js language tooling framework]: https://volarjs.dev/ 12 | 13 | ## Glint 1 Docs 14 | 15 | Please see [the documentation] for how to get set up with Glint V1. 16 | 17 | [the documentation]: https://typed-ember.gitbook.io/glint 18 | 19 | ## Debugging Glint 20 | 21 | See [CONTRIBUTING.md](./CONTRIBUTING.md) for instructions on how to run and debug the Glint language server and VSCode extension locally. 22 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Release Process 2 | 3 | Releases in this repo are mostly automated using [release-plan](https://github.com/embroider-build/release-plan/). Once you label all your PRs correctly (see below) you will have an automatically generated PR that updates your CHANGELOG.md file and a `.release-plan.json` that is used to prepare the release once the PR is merged. 4 | 5 | ## Preparation 6 | 7 | Since the majority of the actual release process is automated, the remaining tasks before releasing are: 8 | 9 | - correctly labeling **all** pull requests that have been merged since the last release 10 | - updating pull request titles so they make sense to our users 11 | 12 | Some great information on why this is important can be found at [keepachangelog.com](https://keepachangelog.com/en/1.1.0/), but the overall 13 | guiding principle here is that changelogs are for humans, not machines. 14 | 15 | When reviewing merged PR's the labels to be used are: 16 | 17 | - breaking - Used when the PR is considered a breaking change. 18 | - enhancement - Used when the PR adds a new feature or enhancement. 19 | - bug - Used when the PR fixes a bug included in a previous release. 20 | - documentation - Used when the PR adds or updates documentation. 21 | - internal - Internal changes or things that don't fit in any other category. 22 | 23 | **Note:** `release-plan` requires that **all** PRs are labeled. If a PR doesn't fit in a category it's fine to label it as `internal` 24 | 25 | ## Release 26 | 27 | Once the prep work is completed, the actual release is straight forward: you just need to merge the open [Plan Release](https://github.com/typed-ember/glint/pulls?q=is%3Apr+is%3Aopen+%22Prepare+Release%22+in%3Atitle) PR 28 | -------------------------------------------------------------------------------- /bin/link-build.mjs: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import { execa } from 'execa'; 3 | import { mkdirp } from 'mkdirp'; 4 | import { rimraf } from 'rimraf'; 5 | 6 | import { packages } from './packages.mjs'; 7 | 8 | const dist = new URL('../dist', import.meta.url).pathname; 9 | const pkgs = packages('@glint'); 10 | 11 | await mkdirp(dist); 12 | await rimraf(dist + '/*.tgz', { glob: true }); 13 | 14 | const pack = pkgs.map(async (pkg) => { 15 | try { 16 | await execa('pnpm', ['pack', '--pack-destination', dist], { 17 | cwd: pkg.path, 18 | }); 19 | 20 | console.log(chalk.green(`Successfully packed ${pkg.name}`)); 21 | } catch (error) { 22 | let message = `Failed to pack ${pkg.name}`; 23 | 24 | if (error instanceof Error) { 25 | message += `\n\n${error.stack}`; 26 | } 27 | 28 | throw new Error(message); 29 | } 30 | }); 31 | 32 | await Promise.all(pack); 33 | 34 | console.log( 35 | chalk.green(`Successfully packed all packages. Ready for linking in external project.`), 36 | ); 37 | -------------------------------------------------------------------------------- /bin/packages.mjs: -------------------------------------------------------------------------------- 1 | import { execSync } from 'node:child_process'; 2 | 3 | // export interface Package { 4 | // readonly name: string; 5 | // readonly version: string; 6 | // readonly path: string; 7 | // readonly private: boolean; 8 | // } 9 | 10 | export function packages(namespace) { 11 | return JSON.parse( 12 | execSync(`pnpm ls -r --depth -1 --filter "${namespace}/*" --json`, { 13 | encoding: 'utf-8', 14 | }), 15 | ).filter((x) => !x.private); 16 | } 17 | -------------------------------------------------------------------------------- /docs/configuration/_index.md: -------------------------------------------------------------------------------- 1 | Glint is configured by adding a `"glint"` key to your project's `tsconfig.json` or `jsconfig.json` file. 2 | 3 | {% code title="tsconfig.json" %} 4 | 5 | ```javascript 6 | { 7 | "compilerOptions": { /* ... */ }, 8 | "include": [ /* ... */ ], 9 | "glint": { 10 | "environment": "ember-loose" 11 | } 12 | } 13 | ``` 14 | 15 | {% endcode %} 16 | 17 | The general shape of the value of the `"glint"` key looks like this: 18 | 19 | ```typescript 20 | interface GlintConfigInput { 21 | environment: string | Array | Record; 22 | checkStandaloneTemplates?: boolean; 23 | } 24 | ``` 25 | 26 | Each key is summarized in further detail below. 27 | 28 | ## `environment` 29 | 30 | The `environment` key specifies what Glint environment(s) your project is operating in. For instance, in loose-mode Ember.js project where you have `@glint/environment-ember-loose` installed, you could specify `"environment": "ember-loose"`. You may also specify an array if your project operates in multiple environments. 31 | 32 | Some environments may accept user-specified configuration. To pass configuration into one or more environments, you can use an object literal mapping environment names to their config: 33 | 34 | ```javascript 35 | "glint" { 36 | "environment": { 37 | "ember-template-imports": { 38 | "additionalGlobals": ["my-special-template-macro"] 39 | } 40 | } 41 | } 42 | ``` 43 | 44 | ## `checkStandaloneTemplates` 45 | 46 | In environments like `ember-loose` that support templates in separate files from their backing class, Glint normally determines whether to typecheck a template based on whether its backing class is in a `.ts` or `.js` file. However, for a template-only component, there's no backing module to check. 47 | 48 | This flag defaults to `true`, and setting it to `false` means Glint will never produce type errors for templates that don't have a corresponding `.js` or `.ts` file. 49 | -------------------------------------------------------------------------------- /docs/contents.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | * [Overview](overview.md) 4 | * [Getting Started](getting-started.md) 5 | 6 | ## Using Glint 7 | 8 | * [Configuration](configuration/\_index.md) 9 | * [Glint Types](glint-types.md) 10 | * [`@glint` Directives](directives.md) 11 | * [Glint with JavaScript](with-js.md) 12 | * [Project References](configuration/project-references.md) 13 | 14 | ## Environments 15 | 16 | * [Ember](using-glint/ember/README.md) 17 | * [Installation](ember/installation.md) 18 | * [Component Signatures](ember/component-signatures.md) 19 | * [Helper and Modifier Signatures](ember/helper-and-modifier-signatures.md) 20 | * [Template Registry](ember/template-registry.md) 21 | * [Routes and Controllers](ember/routes-and-controllers.md) 22 | * [Template-Only Components](ember/template-only-components.md) 23 | * [Rendering Tests](ember/rendering-tests.md) 24 | * [Using Addons](ember/using-addons.md) 25 | * [Authoring Addons](ember/authoring-addons.md) 26 | * [Template Imports](ember/template-imports.md) 27 | 28 | ## Troubleshooting 29 | * [Migration Notes](migrating.md) 30 | * [Common Error Messages](diagnosing-common-error-messages.md) 31 | * [Known Limitations](known-limitations.md) 32 | -------------------------------------------------------------------------------- /docs/ember/rendering-tests.md: -------------------------------------------------------------------------------- 1 | Templates rendered in tests using `ember-cli-htmlbars`'s `hbs` tag will be checked the same way as standalone `hbs` files. 2 | 3 | ```typescript 4 | import { render } from '@ember/test-helpers'; 5 | import { hbs } from 'ember-cli-htmlbars'; 6 | 7 | test('MyComponent works', async function (assert) { 8 | // If `@arg` is declared to be a string, you'll get a squiggle here 9 | await render(hbs``); 10 | 11 | assert.dom().hasText('...'); 12 | }); 13 | ``` 14 | 15 | In some TypeScript codebases it's common practice to define per-module (or even per-test) context types that include additional properties. If you do this and need to access these properties in your template, you can include the context type as a parameter to `render`. 16 | 17 | ```typescript 18 | import { render } from '@ember/test-helpers'; 19 | import { hbs } from 'ember-cli-htmlbars'; 20 | import type { TestContext } from '@ember/test-helpers'; 21 | 22 | interface MyContext extends TestContext { 23 | message: string; 24 | } 25 | 26 | test('MyComponent works', async function (this: MyContext, assert) { 27 | this.message = 'hello'; 28 | 29 | await render(hbs` 30 | 31 | `); 32 | 33 | assert.dom().hasText('...'); 34 | }); 35 | ``` 36 | -------------------------------------------------------------------------------- /docs/ember/routes-and-controllers.md: -------------------------------------------------------------------------------- 1 | Templates associated with Ember routes and/or controllers will be typechecked against those backing classes without needing to import from Glint-specific paths. 2 | 3 | If a controller class exists, then `@model` in the corresponding template will have the type of the controller's declared `model` property, and `{{this}}` will be the type of the controller itself. 4 | 5 | ```typescript 6 | export default class MyController extends Controller { 7 | declare model: MyModelType; 8 | 9 | greeting = 'Hello, world!'; 10 | } 11 | ``` 12 | 13 | 14 | ```handlebars 15 | {{this}} {{! MyController }} 16 | {{this.greeting}} {{! string }} 17 | {{this.model}} {{! MyModelType }} 18 | {{@model}} {{! MyModelType }} 19 | ``` 20 | 21 | If no controller exists but a route does, then `{{@model}}` will be the return type of the route's `model()` hook (unwrapping any promise if necessary), and `{{this}}` will be the type of an empty controller with a `model` property of the same type as `@model`. 22 | 23 | ```typescript 24 | export default class MyRoute extends Route { 25 | async model(): Promise { 26 | // ... 27 | } 28 | } 29 | ``` 30 | 31 | 32 | ```handlebars 33 | {{this}} {{! Controller & { model: MyModelType } }} 34 | {{this.model}} {{! MyModelType }} 35 | {{@model}} {{! MyModelType }} 36 | ``` 37 | 38 | For `error` substate routes, the type of `{{@model}}` will not be automatically inferred. You will need to create a backing class for the route if you consume its model in the corresponding template: 39 | 40 | ```typescript 41 | export default class ErrorRoute extends Route { 42 | // ... 43 | } 44 | ``` 45 | 46 | 47 | ```handlebars 48 | {{@model}} {{! Error }} 49 | ``` -------------------------------------------------------------------------------- /docs/known-limitations.md: -------------------------------------------------------------------------------- 1 | ### Ember-Specific 2 | 3 | Glint is not currently integrated with `ember-cli-typescript`, so typechecking performed during an `ember-cli` build will not take templates into account. 4 | 5 | In addition, the [template registry](ember/template-registry.md) must currently be maintained by hand. A few possibilities for mitigating that pain have been discussed, but ultimately the best solution will be when [strict mode] comes to Ember and we no longer need to reckon with runtime resolution of template entities. 6 | 7 | [strict mode]: http://emberjs.github.io/rfcs/0496-handlebars-strict-mode.html 8 | 9 | ### Tooling 10 | 11 | In VS Code, you will see diagnostics from both TypeScript and Glint in many files, as well as false 'unused symbol' positives for things only referenced in templates. See [the VS Code extension README](../packages/vscode) for details on dealing with this. 12 | -------------------------------------------------------------------------------- /docs/overview.md: -------------------------------------------------------------------------------- 1 | Glint is a set of tools to aid in developing code that uses the Glimmer VM for rendering, such as [Ember.js] v3.24+. 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. 2 | 3 | ## Glint CLI 4 | 5 | Glint's CLI provides a template-aware tool for performing end-to-end TypeScript typechecking on your project. 6 | 7 | ![Command line reporting of template type errors from `glint`](https://user-images.githubusercontent.com/108688/111076577-1d61db00-84ed-11eb-876a-e5b504758d11.png) 8 | 9 | ## Glint Language Server 10 | 11 | The Glint language server implements the standardized [Language Server Protocol], allowing it to be easily incorporated into a variety of editors. The server enables Glimmer templates to participate in your editor's rich IDE features. 12 | 13 | ![Showing type information and documentation in templates on hover.](https://user-images.githubusercontent.com/108688/111069238-6eada280-84cc-11eb-9abb-c2d3af5e8976.png) 14 | 15 | ![Jumping to the definition of a component from where it's used in a template](https://user-images.githubusercontent.com/108688/111069304-b6ccc500-84cc-11eb-83b2-49681b248cbe.png) 16 | 17 | ![Locating all uses of a given component in a project](https://user-images.githubusercontent.com/108688/111070826-c6034100-84d3-11eb-9c12-e8e80e168940.png) 18 | 19 | ![Suggesting component arguments in typeahead with type information and documentation](https://user-images.githubusercontent.com/108688/111070948-3f9b2f00-84d4-11eb-9eaa-077cadf6f380.png) 20 | 21 | [ember.js]: https://www.emberjs.com 22 | [vetur]: https://github.com/vuejs/vetur 23 | [svelte language tools]: https://github.com/sveltejs/language-tools 24 | [language server protocol]: https://microsoft.github.io/language-server-protocol/ 25 | -------------------------------------------------------------------------------- /docs/using-glint/ember/README.md: -------------------------------------------------------------------------------- 1 | # Ember 2 | 3 | -------------------------------------------------------------------------------- /packages/core/.gitignore: -------------------------------------------------------------------------------- 1 | lib/ 2 | tsconfig.tsbuildinfo 3 | -------------------------------------------------------------------------------- /packages/core/README.md: -------------------------------------------------------------------------------- 1 | # `@glint/core` 2 | 3 | This package contains core functionality to power template-aware typechecking on a [glint] project. 4 | 5 | [glint]: https://github.com/typed-ember/glint 6 | 7 | ## CLI 8 | 9 | The `glint` CLI tool is a thin wrapper around `tsc` and hence all documentation / use cases / flags that apply to `tsc` also apply to `glint`. 10 | 11 | Because `glint` is only used for type-checking purposes (or generating declaration files), and not for producing compiled JS output, the emitting of JS should always be disabled by providing either the `--noEmit` or `--emitDeclarationOnly` flags, depending on your use case. 12 | 13 | ### Usage 14 | 15 | Gemeral Usage: 16 | 17 | ```sh 18 | glint --noEmit [--build] [--watch|-w] [--declaration|-d] [--emitDeclarationOnly] [--project path/to/tsconfig.json] 19 | ``` 20 | 21 | Type-checking: 22 | 23 | ```sh 24 | glint [--build] --noEmit 25 | ``` 26 | 27 | Type-checking in watch mode: 28 | 29 | ```sh 30 | glint [--build] --noEmit --watch 31 | ``` 32 | 33 | Build declaration files: 34 | 35 | ``` 36 | glint --build --declaration --emitDeclarationOnly 37 | ``` 38 | 39 | Build declaration files in watch mode: 40 | 41 | ``` 42 | glint --build --declaration --emitDeclarationOnly --watch 43 | ``` 44 | 45 | Please refer to `tsc` docs for other use cases and flags. 46 | -------------------------------------------------------------------------------- /packages/core/bin/glint-language-server.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import '../lib/volar/language-server.js'; 3 | -------------------------------------------------------------------------------- /packages/core/bin/glint.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // @ts-check 3 | import { run } from '../lib/cli/run-volar-tsc.js'; 4 | run(); 5 | -------------------------------------------------------------------------------- /packages/core/src/cli/run-volar-tsc.ts: -------------------------------------------------------------------------------- 1 | import { runTsc } from '@volar/typescript/lib/quickstart/runTsc.js'; 2 | import { createEmberLanguagePlugin } from '../volar/ember-language-plugin.js'; 3 | import { findConfig } from '../config/index.js'; 4 | 5 | import { createRequire } from 'node:module'; 6 | const require = createRequire(import.meta.url); 7 | 8 | export function run(): void { 9 | let cwd = process.cwd(); 10 | 11 | const options = { 12 | extraSupportedExtensions: ['.gjs', '.gts', '.hbs'], 13 | 14 | extraExtensionsToRemove: [], 15 | 16 | // With the above configuration, `{basename}.gts` will produce `{basename}.gts.d.ts`. 17 | // If we would prefer `{basename}.d.ts`, we could use the following configuration instead: 18 | // 19 | // extraExtensionsToRemove: ['.gts', '.gjs'], 20 | // 21 | // See discussion here: https://github.com/typed-ember/glint/issues/628 22 | }; 23 | 24 | const main = (): void => 25 | runTsc(require.resolve('typescript/lib/tsc'), options, (ts, options) => { 26 | const glintConfig = findConfig(cwd); 27 | 28 | // NOTE: this code used to assert in the failure of finding Glint config; I'm 29 | // not sure whether it's better to be lenient, but we were getting test failures 30 | // on environment-ember-loose's `yarn run test`. 31 | if (glintConfig) { 32 | const gtsLanguagePlugin = createEmberLanguagePlugin(glintConfig); 33 | return [gtsLanguagePlugin]; 34 | } else { 35 | return []; 36 | } 37 | }); 38 | main(); 39 | } 40 | -------------------------------------------------------------------------------- /packages/core/src/config/config.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'node:path'; 2 | import { GlintEnvironment } from './environment.js'; 3 | import { GlintConfigInput } from '@glint/core/config-types'; 4 | 5 | /** 6 | * This class represents parsed Glint configuration from a `tsconfig` or `jsconfig` file, 7 | * with methods for interrogating project configuration based on its contents. 8 | */ 9 | export class GlintConfig { 10 | declare public readonly ts: typeof import('typescript'); 11 | public readonly rootDir: string; 12 | public readonly configPath: string; 13 | public readonly environment: GlintEnvironment; 14 | 15 | public constructor( 16 | ts: typeof import('typescript'), 17 | configPath: string, 18 | config: GlintConfigInput, 19 | ) { 20 | Object.defineProperty(this, 'ts', { value: ts }); 21 | this.configPath = normalizePath(configPath); 22 | this.rootDir = path.dirname(configPath); 23 | this.environment = GlintEnvironment.load(config.environment, { rootDir: this.rootDir }); 24 | } 25 | } 26 | 27 | export function normalizePath(fileName: string): string { 28 | if (path.sep !== '/') { 29 | return fileName.split(path.sep).join('/'); 30 | } 31 | 32 | return fileName; 33 | } 34 | -------------------------------------------------------------------------------- /packages/core/src/config/index.ts: -------------------------------------------------------------------------------- 1 | import SilentError from 'silent-error'; 2 | import { GlintConfig } from './config.js'; 3 | import { ConfigLoader } from './loader.js'; 4 | 5 | export { GlintConfig } from './config.js'; 6 | export { GlintEnvironment } from './environment.js'; 7 | export { ConfigLoader, findTypeScript } from './loader.js'; 8 | 9 | /** 10 | * Loads glint configuration, starting from the given directory 11 | * and searching upwards and raising an error if no configuration 12 | * is found. 13 | */ 14 | export function loadConfig(from: string): GlintConfig { 15 | let config = findConfig(from); 16 | if (!config) { 17 | throw new SilentError(`Unable to find Glint configuration for ${from}`); 18 | } 19 | 20 | return config; 21 | } 22 | 23 | /** 24 | * Loads glint configuration, starting from the given directory 25 | * and searching upwards. Returns `null` if no configuration is 26 | * found. 27 | */ 28 | export function findConfig(from: string): GlintConfig | null { 29 | return new ConfigLoader().configForDirectory(from); 30 | } 31 | -------------------------------------------------------------------------------- /packages/core/src/index.ts: -------------------------------------------------------------------------------- 1 | import { GlintConfig, loadConfig, findConfig } from './config/index.js'; 2 | import * as utils from './language-server/util/index.js'; 3 | import { createEmberLanguagePlugin } from './volar/ember-language-plugin.js'; 4 | 5 | import { VirtualGtsCode } from './volar/gts-virtual-code.js'; 6 | import { LooseModeBackingComponentClassVirtualCode } from './volar/loose-mode-backing-component-class-virtual-code.js'; 7 | import { augmentDiagnostics } from './transform/diagnostics/augmentation.js'; 8 | 9 | /** @internal */ 10 | export const pathUtils = utils; 11 | 12 | export { 13 | loadConfig, 14 | findConfig, 15 | createEmberLanguagePlugin, 16 | VirtualGtsCode, 17 | LooseModeBackingComponentClassVirtualCode, 18 | augmentDiagnostics, 19 | }; 20 | 21 | export type { GlintConfig }; 22 | -------------------------------------------------------------------------------- /packages/core/src/language-server/util/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typed-ember/glint/d17c1f1b65531d010a7cef0675ce6bfac34bd938/packages/core/src/language-server/util/index.ts -------------------------------------------------------------------------------- /packages/core/src/transform/diagnostics/index.ts: -------------------------------------------------------------------------------- 1 | import type * as ts from 'typescript'; 2 | 3 | export type Diagnostic = ts.Diagnostic & { 4 | isContentTagError?: boolean; 5 | }; 6 | -------------------------------------------------------------------------------- /packages/core/src/transform/index.ts: -------------------------------------------------------------------------------- 1 | export type { Directive, default as TransformedModule } from './template/transformed-module.js'; 2 | export type { Diagnostic } from './diagnostics/index.js'; 3 | 4 | export { rewriteModule } from './template/rewrite-module.js'; 5 | -------------------------------------------------------------------------------- /packages/core/src/transform/template/inlining/index.ts: -------------------------------------------------------------------------------- 1 | import type ts from 'typescript'; 2 | import { CorrelatedSpan, Directive, TransformError } from '../transformed-module.js'; 3 | import { TSLib } from '../../util.js'; 4 | 5 | export type PartialCorrelatedSpan = Omit; 6 | 7 | export type CorrelatedSpansResult = { 8 | errors: Array; 9 | directives: Array; 10 | partialSpans: Array; 11 | }; 12 | 13 | /** 14 | * Given an AST node for an embedded template, determines whether it's embedded 15 | * within a class in such a way that that class should be treated as its backing 16 | * value. 17 | */ 18 | export function isEmbeddedInClass(ts: TSLib, node: ts.Node): boolean { 19 | let current: ts.Node | null = node; 20 | do { 21 | // TODO: this should likely actually filter on whether the template appears in a 22 | // static block or property definition, but just "am I in a class body" is the 23 | // current status quo and has been ok so far. 24 | if (ts.isHeritageClause(current)) { 25 | return false; 26 | } 27 | if (ts.isClassLike(current)) { 28 | return true; 29 | } 30 | } while ((current = current.parent)); 31 | 32 | return false; 33 | } 34 | -------------------------------------------------------------------------------- /packages/core/src/transform/template/scope-stack.ts: -------------------------------------------------------------------------------- 1 | import { assert } from '../util.js'; 2 | 3 | /** 4 | * A `ScopeStack` is used while traversing a template 5 | * to track what identifiers are currently in scope. 6 | */ 7 | export default class ScopeStack { 8 | private stack: Array>; 9 | 10 | public constructor(identifiers: string[]) { 11 | this.stack = [new Set(identifiers)]; 12 | } 13 | 14 | public push(identifiers: Array): void { 15 | let scope = new Set(this.top); 16 | for (let identifier of identifiers) { 17 | scope.add(identifier); 18 | } 19 | this.stack.unshift(scope); 20 | } 21 | 22 | public pop(): void { 23 | assert(this.stack.length > 1); 24 | this.stack.shift(); 25 | } 26 | 27 | public hasBinding(identifier: string): boolean { 28 | return this.top.has(identifier); 29 | } 30 | 31 | private get top(): Set { 32 | return this.stack[0]; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/core/src/transform/util.ts: -------------------------------------------------------------------------------- 1 | import type ts from 'typescript'; 2 | import { SourceFile } from './template/transformed-module.js'; 3 | 4 | export type TSLib = typeof ts; 5 | 6 | export function unreachable(value: never, message = 'unreachable code'): never { 7 | throw new Error(`[@glint/core] Internal error: ${message}`); 8 | } 9 | 10 | export function assert( 11 | test: unknown, 12 | message: string | (() => string) = 'Internal error', 13 | ): asserts test { 14 | if (test == null || test === false) { 15 | throw new Error(typeof message === 'string' ? message : message()); 16 | } 17 | } 18 | 19 | export function createSyntheticSourceFile(ts: TSLib, source: SourceFile): ts.SourceFile { 20 | return Object.assign(ts.createSourceFile(source.filename, '', ts.ScriptTarget.Latest), { 21 | text: source.contents, 22 | end: source.contents.length, 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /packages/core/src/volar/script-snapshot.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @typedef {import('typescript').IScriptSnapshot} IScriptSnapshot 3 | */ 4 | 5 | import { IScriptSnapshot, TextChangeRange } from 'typescript'; 6 | 7 | /** 8 | * A TypeScript compatible script snapshot that wraps a string of text. 9 | * 10 | * @implements {IScriptSnapshot} 11 | */ 12 | export class ScriptSnapshot implements IScriptSnapshot { 13 | constructor(public text: string) {} 14 | 15 | // Not Implemented 16 | getChangeRange(_oldSnapshot: IScriptSnapshot): TextChangeRange | undefined { 17 | return undefined; 18 | } 19 | 20 | getLength(): number { 21 | return this.text.length; 22 | } 23 | 24 | getText(start: number, end: number): string { 25 | return this.text.slice(start, end); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.compileroptions.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "outDir": "lib", 6 | "types": ["@types/node"] 7 | }, 8 | "include": ["src", "types"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/core/types/silent-error.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'silent-error' { 2 | class SilentError extends Error {} 3 | export default SilentError; 4 | } 5 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/-private/index.ts: -------------------------------------------------------------------------------- 1 | // Reference the scaffolding for the template registry and our merged declarations 2 | // for third party modules so that vanilla TS will see those as long as authors 3 | // have `import '@glint/environment-ember-loose'` somewhere in their project. 4 | 5 | /// 6 | /// 7 | export {}; 8 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/-private/intrinsics/action.d.ts: -------------------------------------------------------------------------------- 1 | import { DirectInvokable, NamedArgs } from '@glint/template/-private/integration'; 2 | 3 | export type ActionNamedArgs = NamedArgs<{ 4 | value?: keyof T; 5 | }>; 6 | 7 | export type ActionResult> = undefined extends Args['value'] 8 | ? T 9 | : Args['value'] extends keyof T 10 | ? T[Args['value']] 11 | : T; 12 | 13 | export type ActionKeyword = DirectInvokable<{ 14 | , Params extends unknown[]>( 15 | f: (...rest: Params) => Ret, 16 | args?: Args, 17 | ): (...rest: Params) => ActionResult; 18 | , Params extends unknown[]>( 19 | f: (a: A, ...rest: Params) => Ret, 20 | a: A, 21 | args?: Args, 22 | ): (...rest: Params) => ActionResult; 23 | , Params extends unknown[]>( 24 | f: (a: A, b: B, ...rest: Params) => Ret, 25 | a: A, 26 | b: B, 27 | args?: Args, 28 | ): (...rest: Params) => ActionResult; 29 | , Params extends unknown[]>( 30 | f: (a: A, b: B, c: C, ...rest: Params) => Ret, 31 | a: A, 32 | b: B, 33 | c: C, 34 | args?: Args, 35 | ): (...rest: Params) => ActionResult; 36 | , Params extends unknown[]>( 37 | f: (a: A, b: B, c: C, d: D, ...rest: Params) => Ret, 38 | a: A, 39 | b: B, 40 | c: C, 41 | d: D, 42 | args?: Args, 43 | ): (...rest: Params) => ActionResult; 44 | (action: string, ...rest: unknown[]): (...rest: unknown[]) => unknown; 45 | }>; 46 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/-private/intrinsics/concat.d.ts: -------------------------------------------------------------------------------- 1 | import { HelperLike } from '@glint/template'; 2 | 3 | export type ConcatHelper = HelperLike<{ 4 | Args: { Positional: unknown[] }; 5 | Return: string; 6 | }>; 7 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/-private/intrinsics/each-in.d.ts: -------------------------------------------------------------------------------- 1 | import { ComponentLike } from '@glint/template'; 2 | 3 | export type EachInKeyword = abstract new () => InstanceType< 4 | ComponentLike<{ 5 | Args: { 6 | Positional: [object: T]; 7 | Named: { key?: string }; 8 | }; 9 | Blocks: { 10 | default: EachInIteratorPair; 11 | else?: []; 12 | }; 13 | }> 14 | >; 15 | 16 | type EachInIteratorPair = 17 | T extends Iterable<[infer K, infer V]> 18 | ? [key: K, value: V] 19 | : [key: EachInKey, value: Exclude[EachInKey]]; 20 | 21 | // `{{each-in}}` internally uses `Object.keys`, so only string keys are included 22 | // TS, on the other hand, gives a wider result for `keyof` than many users expect 23 | // for record types: https://github.com/microsoft/TypeScript/issues/29249 24 | type EachInKey = Extract, string>; 25 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/-private/intrinsics/each.d.ts: -------------------------------------------------------------------------------- 1 | import EmberArray from '@ember/array'; 2 | import { ComponentLike } from '@glint/template'; 3 | 4 | type ArrayLike = ReadonlyArray | Iterable | EmberArray; 5 | 6 | export type EachKeyword = abstract new () => InstanceType< 7 | ComponentLike<{ 8 | Args: { 9 | Positional: [items: ArrayLike | null | undefined]; 10 | Named: { key?: string }; 11 | }; 12 | Blocks: { 13 | default: [T, number]; 14 | else: []; 15 | }; 16 | }> 17 | >; 18 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/-private/intrinsics/fn.d.ts: -------------------------------------------------------------------------------- 1 | import { DirectInvokable } from '@glint/template/-private/integration'; 2 | import { Mut } from './mut'; 3 | 4 | export type FnHelper = DirectInvokable<{ 5 | (update: Mut): (value: T) => void; 6 | (update: Mut, value: T): () => void; 7 | (f: (...rest: Args) => Ret): (...rest: Args) => Ret; 8 | (f: (a: A, ...rest: Args) => Ret, a: A): (...rest: Args) => Ret; 9 | ( 10 | f: (a: A, b: B, ...rest: Args) => Ret, 11 | a: A, 12 | b: B, 13 | ): (...rest: Args) => Ret; 14 | ( 15 | f: (a: A, b: B, c: C, ...rest: Args) => Ret, 16 | a: A, 17 | b: B, 18 | c: C, 19 | ): (...rest: Args) => Ret; 20 | ( 21 | f: (a: A, b: B, c: C, d: D, ...rest: Args) => Ret, 22 | a: A, 23 | b: B, 24 | c: C, 25 | d: D, 26 | ): (...rest: Args) => Ret; 27 | ( 28 | f: (a: A, b: B, c: C, d: D, e: E, ...rest: Args) => Ret, 29 | a: A, 30 | b: B, 31 | c: C, 32 | d: D, 33 | e: E, 34 | ): (...rest: Args) => Ret; 35 | ( 36 | f: (a: A, b: B, c: C, d: D, e: E, g: G, ...rest: Args) => Ret, 37 | a: A, 38 | b: B, 39 | c: C, 40 | d: D, 41 | e: E, 42 | g: G, 43 | ): (...rest: Args) => Ret; 44 | }>; 45 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/-private/intrinsics/get.d.ts: -------------------------------------------------------------------------------- 1 | import { DirectInvokable } from '@glint/template/-private/integration'; 2 | import ObjectProxy from '@ember/object/proxy'; 3 | import '@ember/object/-private/types'; 4 | 5 | // This hack lets us support both the stable/preview types from `ember-source` 6 | // and the classic types still live on DefinitelyTyped. If using the DT types, 7 | // this acts as a declaration merge for a module which is then integrated via 8 | // the rest of the DT types. If using the stable/preview types, this ends up 9 | // being a no-op. 10 | declare const GetSetMarker: unique symbol; 11 | declare module '@ember/object/-private/types' { 12 | interface ComputedPropertyMarker { 13 | [GetSetMarker]: [Get, Set]; 14 | } 15 | } 16 | 17 | type UnwrapComputedPropertyGetter = T extends { [GetSetMarker]: [infer U, any] } ? U : T; 18 | 19 | export type GetHelper = DirectInvokable<{ 20 | (obj: T, key: K): UnwrapComputedPropertyGetter; 21 | ( 22 | obj: T | null | undefined, 23 | key: K, 24 | ): UnwrapComputedPropertyGetter | undefined; 25 | ( 26 | obj: ObjectProxy | null | undefined, 27 | key: K, 28 | ): UnwrapComputedPropertyGetter | undefined; 29 | (obj: null | undefined, key: string): undefined; 30 | (obj: unknown, key: string): unknown; 31 | }>; 32 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/-private/intrinsics/input.d.ts: -------------------------------------------------------------------------------- 1 | import { ComponentLike } from '@glint/template'; 2 | 3 | export interface CheckboxInputArgs { 4 | type: 'checkbox'; 5 | checked?: boolean | undefined; 6 | } 7 | 8 | export interface TextInputArgs { 9 | type?: string | undefined; 10 | value?: string | null | undefined; 11 | enter?: ((value: string, event: KeyboardEvent) => void) | undefined; 12 | 'insert-newline'?: ((value: string, event: KeyboardEvent) => void) | undefined; 13 | 'escape-press'?: ((value: string, event: KeyboardEvent) => void) | undefined; 14 | 'focus-in'?: ((value: string, event: FocusEvent) => void) | undefined; 15 | 'focus-out'?: ((value: string, event: FocusEvent) => void) | undefined; 16 | 'key-down'?: ((value: string, event: KeyboardEvent) => void) | undefined; 17 | 'key-press'?: ((value: string, event: KeyboardEvent) => void) | undefined; 18 | 'key-up'?: ((value: string, event: KeyboardEvent) => void) | undefined; 19 | } 20 | 21 | export type InputComponent = ComponentLike<{ 22 | Args: { Named: CheckboxInputArgs | TextInputArgs }; 23 | Element: HTMLInputElement; 24 | }>; 25 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/-private/intrinsics/link-to.d.ts: -------------------------------------------------------------------------------- 1 | import { ComponentLike } from '@glint/template'; 2 | import { ComponentReturn, DirectInvokable, NamedArgs } from '@glint/template/-private/integration'; 3 | 4 | type RequireAtLeastOne = Pick> & 5 | { 6 | [K in Keys]-?: Required> & Partial>>; 7 | }[Keys]; 8 | 9 | type LinkToArgs = RequireAtLeastOne< 10 | { 11 | route?: string; 12 | model?: unknown; 13 | models?: unknown[]; 14 | query?: Record; 15 | disabled?: boolean; 16 | activeClass?: string; 17 | 'current-when'?: string | boolean; 18 | preventDefault?: boolean; 19 | replace?: boolean; 20 | tagName?: string; 21 | }, 22 | 'route' | 'model' | 'models' | 'query' 23 | >; 24 | 25 | type LinkToReturn = ComponentReturn<{ default: [] }, HTMLAnchorElement>; 26 | 27 | export type LinkToKeyword = DirectInvokable<{ 28 | // `{{link-to}}` classic invocation 29 | (route: string, ...params: Array): LinkToReturn; 30 | 31 | // `` invocation, but accessed via `'link-to'` e.g. with `{{component}}` 32 | (named: NamedArgs): LinkToReturn; 33 | }>; 34 | 35 | export type LinkToComponent = ComponentLike<{ 36 | Args: LinkToArgs; 37 | Blocks: { default: [] }; 38 | Element: HTMLAnchorElement; 39 | }>; 40 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/-private/intrinsics/log.d.ts: -------------------------------------------------------------------------------- 1 | import { HelperLike } from '@glint/template'; 2 | 3 | export type LogHelper = HelperLike<{ 4 | Args: { Positional: unknown[] }; 5 | Return: void; 6 | }>; 7 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/-private/intrinsics/mount.d.ts: -------------------------------------------------------------------------------- 1 | import { HelperLike } from '@glint/template'; 2 | 3 | export type MountKeyword = HelperLike<{ 4 | Args: { 5 | Positional: [engine: string]; 6 | Named: { model?: unknown }; 7 | }; 8 | Return: void; 9 | }>; 10 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/-private/intrinsics/mut.d.ts: -------------------------------------------------------------------------------- 1 | import { HelperLike } from '@glint/template'; 2 | 3 | declare const Mut: unique symbol; 4 | 5 | export type Mut = { [Mut]: (value: T) => void }; 6 | 7 | export type MutKeyword = abstract new () => InstanceType< 8 | HelperLike<{ 9 | Args: { 10 | Positional: [value: T]; 11 | }; 12 | Return: Mut; 13 | }> 14 | >; 15 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/-private/intrinsics/on.d.ts: -------------------------------------------------------------------------------- 1 | import { ModifierLike } from '@glint/template'; 2 | 3 | export interface OnModifierArgs { 4 | capture?: boolean; 5 | once?: boolean; 6 | passive?: boolean; 7 | } 8 | 9 | export type EventForName = Name extends keyof HTMLElementEventMap 10 | ? HTMLElementEventMap[Name] 11 | : Event; 12 | 13 | export type OnModifier = abstract new () => InstanceType< 14 | ModifierLike<{ 15 | Element: Element; 16 | Args: { 17 | Named: OnModifierArgs; 18 | Positional: [name: Name, callback: (event: EventForName) => void]; 19 | }; 20 | }> 21 | >; 22 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/-private/intrinsics/outlet.d.ts: -------------------------------------------------------------------------------- 1 | import { HelperLike } from '@glint/template'; 2 | 3 | export type OutletKeyword = HelperLike<{ 4 | Args: { 5 | Positional: [name?: string]; 6 | }; 7 | Return: void; 8 | }>; 9 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/-private/intrinsics/textarea.d.ts: -------------------------------------------------------------------------------- 1 | import { ComponentLike } from '@glint/template'; 2 | 3 | export interface TextareaArgs { 4 | value?: string | null | undefined; 5 | enter?: ((value: string, event: KeyboardEvent) => void) | undefined; 6 | 'insert-newline'?: ((value: string, event: KeyboardEvent) => void) | undefined; 7 | 'escape-press'?: ((value: string, event: KeyboardEvent) => void) | undefined; 8 | 'focus-in'?: ((value: string, event: FocusEvent) => void) | undefined; 9 | 'focus-out'?: ((value: string, event: FocusEvent) => void) | undefined; 10 | 'key-press'?: ((value: string, event: KeyboardEvent) => void) | undefined; 11 | } 12 | 13 | export type TextareaComponent = ComponentLike<{ 14 | Args: TextareaArgs; 15 | Element: HTMLTextAreaElement; 16 | }>; 17 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/-private/intrinsics/unbound.d.ts: -------------------------------------------------------------------------------- 1 | import { HelperLike } from '@glint/template'; 2 | 3 | export type UnboundKeyword = abstract new () => InstanceType< 4 | HelperLike<{ 5 | Args: { 6 | Positional: [value: T]; 7 | }; 8 | Return: T; 9 | }> 10 | >; 11 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/-private/intrinsics/unique-id.d.ts: -------------------------------------------------------------------------------- 1 | import { HelperLike } from '@glint/template'; 2 | 3 | export type UniqueIdHelper = HelperLike<{ 4 | Return: string; 5 | }>; 6 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/.eslintignore: -------------------------------------------------------------------------------- 1 | /*/*.js 2 | /*/*.d.ts 3 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/.gitignore: -------------------------------------------------------------------------------- 1 | *.js 2 | *.d.ts 3 | *.d.ts.map 4 | tsconfig.tsbuildinfo 5 | 6 | !registry/**/*.d.ts 7 | !-private/dsl/**/*.d.ts 8 | !-private/intrinsics/**/*.d.ts 9 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/README.md: -------------------------------------------------------------------------------- 1 | # `@glint/environment-ember-loose` 2 | 3 | This package contains the information necessary for glint to typecheck a standard (non-[strict-mode](http://emberjs.github.io/rfcs/0496-handlebars-strict-mode.html)) Ember.js project. 4 | 5 | See [the Glint documentation](https://typed-ember.gitbook.io/glint/using-glint/ember/installation) for more detailed instructions on working with Ember.js projects in Glint. 6 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/__tests__/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.compileroptions.json", 3 | "include": [".", "../-private"], 4 | "compilerOptions": { 5 | "rootDir": ".." 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/__tests__/type-tests/intrinsics/action.test.ts: -------------------------------------------------------------------------------- 1 | import { expectTypeOf } from 'expect-type'; 2 | import { Globals, NamedArgsMarker, resolve } from '@glint/environment-ember-loose/-private/dsl'; 3 | 4 | let action = resolve(Globals['action']); 5 | 6 | // Basic plumbing 7 | expectTypeOf(action(() => 'hi')).toEqualTypeOf<() => string>(); 8 | expectTypeOf(action((value: T) => value)).toEqualTypeOf<{ (value: T): T }>(); 9 | 10 | // Binding parameters 11 | expectTypeOf(action((x: string, y: number) => x.padStart(y), 'hello')).toEqualTypeOf< 12 | (y: number) => string 13 | >(); 14 | expectTypeOf(action((x: string, y: number) => x.padStart(y), 'hello', 123)).toEqualTypeOf< 15 | () => string 16 | >(); 17 | expectTypeOf(action((value: T) => value, 'hello')).toEqualTypeOf<() => string>(); 18 | 19 | // @ts-expect-error: invalid parameter type 20 | action((x: string) => x, 123); 21 | 22 | // Extracting a value from a particular key 23 | expectTypeOf(action(() => 'hello', { value: 'length', ...NamedArgsMarker })).toEqualTypeOf< 24 | () => number 25 | >(); 26 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/__tests__/type-tests/intrinsics/concat.test.ts: -------------------------------------------------------------------------------- 1 | import { expectTypeOf } from 'expect-type'; 2 | import { Globals, resolve } from '@glint/environment-ember-loose/-private/dsl'; 3 | 4 | let concat = resolve(Globals['concat']); 5 | 6 | // Basic plumbing 7 | expectTypeOf(concat()).toEqualTypeOf(); 8 | expectTypeOf(concat(1, true, 'three')).toEqualTypeOf(); 9 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/__tests__/type-tests/intrinsics/fn.test.ts: -------------------------------------------------------------------------------- 1 | import { expectTypeOf } from 'expect-type'; 2 | import { Globals, resolve } from '@glint/environment-ember-loose/-private/dsl'; 3 | 4 | let fn = resolve(Globals['fn']); 5 | 6 | // @ts-expect-error: invalid arg 7 | fn((t: string) => t, 123); 8 | 9 | expectTypeOf(fn(() => true)).toEqualTypeOf<() => boolean>(); 10 | expectTypeOf(fn((arg: string) => arg.length)).toEqualTypeOf<(arg: string) => number>(); 11 | expectTypeOf(fn((arg: string) => arg.length, 'hi')).toEqualTypeOf<() => number>(); 12 | 13 | let identity = (x: T): T => x; 14 | 15 | // Bound type parameters are reflected in the output 16 | expectTypeOf(fn(identity, 'hi')).toEqualTypeOf<() => string>(); 17 | 18 | // Unbound type parameters survive to the output 19 | expectTypeOf(fn(identity)).toEqualTypeOf<{ (x: T): T }>(); 20 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/__tests__/type-tests/intrinsics/log.test.ts: -------------------------------------------------------------------------------- 1 | import { Globals, resolve } from '@glint/environment-ember-loose/-private/dsl'; 2 | 3 | let log = resolve(Globals['log']); 4 | 5 | log('hello', 'world'); 6 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/__tests__/type-tests/intrinsics/mount.test.ts: -------------------------------------------------------------------------------- 1 | import { expectTypeOf } from 'expect-type'; 2 | import { Globals, NamedArgsMarker, resolve } from '@glint/environment-ember-loose/-private/dsl'; 3 | 4 | let mount = resolve(Globals['mount']); 5 | 6 | // Basic plumbing 7 | expectTypeOf(mount('engine-name')).toEqualTypeOf(); 8 | expectTypeOf(mount('engine-name', { model: {}, ...NamedArgsMarker })); 9 | 10 | // @ts-expect-error: missing engine name 11 | mount(); 12 | 13 | // @ts-expect-error: invalid named arg 14 | mount('engine-name', { hello: 'hi', ...NamedArgsMarker }); 15 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/__tests__/type-tests/intrinsics/mut.test.ts: -------------------------------------------------------------------------------- 1 | import { expectTypeOf } from 'expect-type'; 2 | import { Globals, NamedArgsMarker, resolve } from '@glint/environment-ember-loose/-private/dsl'; 3 | import { Mut } from '../../../-private/intrinsics/mut'; 4 | 5 | let fn = resolve(Globals['fn']); 6 | let mut = resolve(Globals['mut']); 7 | 8 | // Basic plumbing 9 | expectTypeOf(mut('hello')).toEqualTypeOf>(); 10 | 11 | // `{{fn (mut this.value)}}` returns an updater 12 | expectTypeOf(fn(mut('hello'))).toEqualTypeOf<(value: string) => void>(); 13 | 14 | // @ts-expect-error: missing value 15 | mut(); 16 | 17 | // @ts-expect-error: unexpected named args 18 | mut('hello', { 19 | hello: 'hi', 20 | ...NamedArgsMarker, 21 | }); 22 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/__tests__/type-tests/intrinsics/on.test.ts: -------------------------------------------------------------------------------- 1 | import { expectTypeOf } from 'expect-type'; 2 | import { 3 | Globals, 4 | applyModifier, 5 | resolve, 6 | NamedArgsMarker, 7 | } from '@glint/environment-ember-loose/-private/dsl'; 8 | 9 | const on = resolve(Globals['on']); 10 | const el = document.createElement('div'); 11 | 12 | on(el, 'click', () => {}, { 13 | // @ts-expect-error: extra named arg 14 | foo: 'bar', 15 | ...NamedArgsMarker, 16 | }); 17 | 18 | // @ts-expect-error: missing positional arg 19 | on(el, 'click'); 20 | 21 | // @ts-expect-error: extra positional arg 22 | on(el, 'click', () => {}, 'hello'); 23 | 24 | on(el, 'scroll', () => {}, { capture: true, once: true, passive: true, ...NamedArgsMarker }); 25 | 26 | on(el, 'unknown', (event) => { 27 | expectTypeOf(event).toEqualTypeOf(); 28 | }); 29 | 30 | on(el, 'click', (event) => { 31 | expectTypeOf(event).toEqualTypeOf(); 32 | }); 33 | 34 | on(el, 'keyup', (event) => { 35 | expectTypeOf(event).toEqualTypeOf(); 36 | }); 37 | 38 | applyModifier(on(el, 'click', () => {})); 39 | 40 | applyModifier(on(new SVGRectElement(), 'click', () => {})); 41 | 42 | applyModifier(on(new Element(), 'click', () => {})); 43 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/__tests__/type-tests/intrinsics/outlet.test.ts: -------------------------------------------------------------------------------- 1 | import { expectTypeOf } from 'expect-type'; 2 | import { Globals, NamedArgsMarker, resolve } from '@glint/environment-ember-loose/-private/dsl'; 3 | 4 | let outlet = resolve(Globals['outlet']); 5 | 6 | // Named outlet 7 | expectTypeOf(outlet('outlet-name')).toEqualTypeOf(); 8 | 9 | // Nameless main outlet 10 | outlet(); 11 | 12 | // @ts-expect-error: unexpected named args 13 | outlet('outlet-name', { 14 | hello: 'hi', 15 | ...NamedArgsMarker, 16 | }); 17 | -------------------------------------------------------------------------------- /packages/environment-ember-loose/__tests__/type-tests/intrinsics/textarea.test.ts: -------------------------------------------------------------------------------- 1 | import { expectTypeOf } from 'expect-type'; 2 | import { 3 | applySplattributes, 4 | emitComponent, 5 | Globals, 6 | NamedArgsMarker, 7 | resolve, 8 | } from '@glint/environment-ember-loose/-private/dsl'; 9 | 10 | let textarea = resolve(Globals['textarea']); 11 | let Textarea = resolve(Globals['Textarea']); 12 | 13 | // Both casings have the same signature 14 | expectTypeOf(textarea).toEqualTypeOf(Textarea); 15 | 16 | Textarea(); 17 | Textarea({ value: 'hello', ...NamedArgsMarker }); 18 | Textarea({ value: undefined, ...NamedArgsMarker }); 19 | Textarea({ value: null, ...NamedArgsMarker }); 20 | 21 | // Ensure we can apply