From beec8f97cc87fc8b1cdc508b885b107ae2d0fd21 Mon Sep 17 00:00:00 2001 From: Bjarne Mogstad Date: Fri, 21 Mar 2025 10:59:57 +0100 Subject: [PATCH] Support implicit default export of template tags with satisfies keyword Resolves #824 --- .../core/__tests__/transform/rewrite.test.ts | 29 ++++- .../-private/environment/transform.ts | 15 ++- .../__tests__/transformation.test.ts | 102 ++++++++++++++++++ 3 files changed, 140 insertions(+), 6 deletions(-) diff --git a/packages/core/__tests__/transform/rewrite.test.ts b/packages/core/__tests__/transform/rewrite.test.ts index 6bbaee922..0cbfd7f74 100644 --- a/packages/core/__tests__/transform/rewrite.test.ts +++ b/packages/core/__tests__/transform/rewrite.test.ts @@ -417,7 +417,7 @@ describe('Transform: rewriteModule', () => { contents: stripIndent` import Component, { hbs } from 'special/component'; export default class MyComponent extends Component() { - + } `, }; @@ -429,7 +429,7 @@ describe('Transform: rewriteModule', () => { export default class MyComponent extends Component(({} as typeof import("@glint/environment-ember-template-imports/-private/dsl")).templateExpression(function(__glintRef__, __glintDSL__: typeof import("@glint/environment-ember-template-imports/-private/dsl")) { __glintRef__; __glintDSL__; })) { - + }" `); }); @@ -822,5 +822,30 @@ describe('Transform: rewriteModule', () => { |" `); }); + + test('with implicit export default and satisfies', () => { + let customEnv = GlintEnvironment.load(['ember-loose', 'ember-template-imports']); + let script = { + filename: 'test.gts', + contents: stripIndent` + import type { TOC } from '@ember/component/template-only'; + satisfies TOC<{ + Blocks: { default: [] } + }>; + `, + }; + + let transformedModule = rewriteModule(ts, { script }, customEnv); + + expect(transformedModule?.errors).toEqual([]); + expect(transformedModule?.transformedContents).toMatchInlineSnapshot(` + "import type { TOC } from '@ember/component/template-only'; + export default ({} as typeof import("@glint/environment-ember-template-imports/-private/dsl")).templateExpression(function(__glintRef__, __glintDSL__: typeof import("@glint/environment-ember-template-imports/-private/dsl")) { + __glintRef__; __glintDSL__; + }) satisfies TOC<{ + Blocks: { default: [] } + }>;" + `); + }); }); }); diff --git a/packages/environment-ember-template-imports/-private/environment/transform.ts b/packages/environment-ember-template-imports/-private/environment/transform.ts index 7274d54ed..8b9c2604e 100644 --- a/packages/environment-ember-template-imports/-private/environment/transform.ts +++ b/packages/environment-ember-template-imports/-private/environment/transform.ts @@ -111,12 +111,19 @@ type ETITemplateProperty = ts.PropertyDeclaration & { name: ts.ComputedPropertyName & { expression: ETITemplateLiteral }; }; -type ETIDefaultTemplate = ts.ExpressionStatement & { - expression: ETITemplateLiteral; -}; +type ETIDefaultTemplate = + | (ts.ExpressionStatement & { + expression: ETITemplateLiteral; + }) + | (ts.SatisfiesExpression & { + expression: ETITemplateLiteral; + }); function isETIDefaultTemplate(ts: TSLib, node: ts.Node): node is ETIDefaultTemplate { - return ts.isExpressionStatement(node) && isETITemplateLiteral(ts, node.expression); + return ( + (ts.isExpressionStatement(node) || ts.isSatisfiesExpression(node)) && + isETITemplateLiteral(ts, node.expression) + ); } function isETITemplateProperty(ts: TSLib, node: ts.Node): node is ETITemplateProperty { diff --git a/packages/environment-ember-template-imports/__tests__/transformation.test.ts b/packages/environment-ember-template-imports/__tests__/transformation.test.ts index a37190cd4..46441062c 100644 --- a/packages/environment-ember-template-imports/__tests__/transformation.test.ts +++ b/packages/environment-ember-template-imports/__tests__/transformation.test.ts @@ -126,6 +126,41 @@ describe('Environment: ETI', () => { ); }); + test('single template with satisfies', () => { + let source = stripIndent` + import type { TOC } from '@ember/component/template-only'; + satisfies TOC<{ + Blocks: { default: [] } + }>; + `; + + let { meta, sourceFile } = applyTransform(source); + let satisfiesExpression = (sourceFile.statements[2] as ts.ExpressionStatement) + .expression as ts.SatisfiesExpression; + let templateNode = satisfiesExpression.expression; + let start = source.indexOf(''.length; + + expect(meta).toEqual( + new Map([ + [ + templateNode, + { + prepend: 'export default ', + templateLocation: { + start, + contentStart, + contentEnd, + end, + }, + }, + ], + ]), + ); + }); + test('multiple templates', () => { let source = stripIndent`