diff --git a/blueprints/-maybe-polyfill-typescript-blueprints.js b/blueprints/-maybe-polyfill-typescript-blueprints.js index 1af065e5a01..82d981beefe 100644 --- a/blueprints/-maybe-polyfill-typescript-blueprints.js +++ b/blueprints/-maybe-polyfill-typescript-blueprints.js @@ -8,10 +8,12 @@ function canEmitTypeScript() { } module.exports = function (context) { - if (canEmitTypeScript()) { + let canUseTypeScript = canEmitTypeScript(); + if (canUseTypeScript) { typescriptBlueprintPolyfill(context); } else { // Use the plain old JS templates from before context.path = context.path.replace('blueprints', 'blueprints-js'); } + return canUseTypeScript; }; diff --git a/blueprints/component/files/__root__/__path__/__name__.ts b/blueprints/component/files/__root__/__path__/__name__.ts index 777e6155290..3ddc45ea7ba 100644 --- a/blueprints/component/files/__root__/__path__/__name__.ts +++ b/blueprints/component/files/__root__/__path__/__name__.ts @@ -1,3 +1,4 @@ <%= importComponent %> <%= importTemplate %> +<%= componentSignature %> export default <%= defaultExport %> diff --git a/blueprints/component/index.js b/blueprints/component/index.js index 57f31654455..fe81e2e7653 100644 --- a/blueprints/component/index.js +++ b/blueprints/component/index.js @@ -53,9 +53,17 @@ module.exports = { }, ], + /** + Flag to let us correctly handle the case where we are running against a + version of Ember CLI which does not support TS-based emit, and where we + therefore *must* not emit a `defaultExport` local which includes a type + parameter in the exported function call or class definition. + */ + _isUsingTS: false, + init() { this._super && this._super.init.apply(this, arguments); - maybePolyfillTypeScriptBlueprints(this); + this._isUsingTS = maybePolyfillTypeScriptBlueprints(this); let isOctane = has('octane'); this.availableOptions.forEach((option) => { @@ -227,15 +235,8 @@ module.exports = { }, locals(options) { - let sanitizedModuleName = options.entity.name.replace(/\//g, '-'); - let classifiedModuleName = stringUtil.classify(sanitizedModuleName); - - let templatePath = ''; - let importComponent = ''; - let importTemplate = ''; - let defaultExport = ''; - // if we're in an addon, build import statement + let templatePath = ''; if (options.project.isEmberCLIAddon() || (options.inRepoAddon && !options.inDummy)) { if (options.pod) { templatePath = './template'; @@ -251,6 +252,14 @@ module.exports = { ? options.componentClass : '@ember/component'; + let sanitizedModuleName = options.entity.name.replace(/\//g, '-'); + let classifiedModuleName = stringUtil.classify(sanitizedModuleName); + + let importComponent = ''; + let importTemplate = ''; + let defaultExport = ''; + let componentSignature = ''; + switch (componentClass) { case '@ember/component': importComponent = `import Component from '@ember/component';`; @@ -263,20 +272,53 @@ module.exports = { break; case '@glimmer/component': importComponent = `import Component from '@glimmer/component';`; - defaultExport = `class ${classifiedModuleName}Component extends Component {}`; + if (this._isUsingTS) { + componentSignature = signatureFor(classifiedModuleName); + defaultExport = `class ${classifiedModuleName}Component extends Component<${classifiedModuleName}Signature> {}`; + } else { + defaultExport = `class ${classifiedModuleName}Component extends Component {}`; + } break; case '@ember/component/template-only': importComponent = `import templateOnly from '@ember/component/template-only';`; - defaultExport = `templateOnly();`; + if (this._isUsingTS) { + componentSignature = signatureFor(classifiedModuleName); + defaultExport = `templateOnly<${classifiedModuleName}Signature>();`; + } else { + defaultExport = `templateOnly();`; + } break; } return { importTemplate, importComponent, + componentSignature, defaultExport, path: getPathOption(options), componentClass: options.componentClass, }; }, }; + +function signatureFor(classifiedModuleName) { + let args = ` // The arguments accepted by the component${EOL} Args: {};`; + + let blocks = + ` // Any blocks yielded by the component${EOL}` + + ` Blocks: {${EOL}` + + ` default: []${EOL}` + + ` };`; + + let element = + ` // The element to which \`...attributes\` is applied in the component template${EOL}` + + ` Element: null;`; + + return ( + `interface ${classifiedModuleName}Signature {${EOL}` + + `${args}${EOL}` + + `${blocks}${EOL}` + + `${element}${EOL}` + + `}${EOL}` + ); +}