diff --git a/blueprints/-utils.js b/blueprints/-utils.js index 37f7754f770..d681914f02a 100644 --- a/blueprints/-utils.js +++ b/blueprints/-utils.js @@ -1,9 +1,33 @@ const { dasherize } = require('ember-cli-string-utils'); +const { EOL } = require('os'); + +function generateComponentSignature(componentName) { + 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 ( + `export interface ${componentName}Signature {${EOL}` + + `${args}${EOL}` + + `${blocks}${EOL}` + + `${element}${EOL}` + + `}${EOL}` + ); +} function modulePrefixForProject(project) { return dasherize(project.config().modulePrefix); } module.exports = { + generateComponentSignature, modulePrefixForProject, }; diff --git a/blueprints/component-class/files/__root__/__path__/__name__.ts b/blueprints/component-class/files/__root__/__path__/__name__.ts index 777e6155290..3ddc45ea7ba 100644 --- a/blueprints/component-class/files/__root__/__path__/__name__.ts +++ b/blueprints/component-class/files/__root__/__path__/__name__.ts @@ -1,3 +1,4 @@ <%= importComponent %> <%= importTemplate %> +<%= componentSignature %> export default <%= defaultExport %> diff --git a/blueprints/component-class/index.js b/blueprints/component-class/index.js index 1c6f463b7cc..65cdc61442d 100644 --- a/blueprints/component-class/index.js +++ b/blueprints/component-class/index.js @@ -8,6 +8,7 @@ const getPathOption = require('ember-cli-get-component-path-option'); const normalizeEntityName = require('ember-cli-normalize-entity-name'); const { EOL } = require('os'); const { has } = require('@ember/edition-utils'); +const { generateComponentSignature } = require('../-utils'); const maybePolyfillTypeScriptBlueprints = require('../-maybe-polyfill-typescript-blueprints'); @@ -49,9 +50,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) => { @@ -134,6 +143,7 @@ module.exports = { let importComponent = ''; let importTemplate = ''; let defaultExport = ''; + let componentSignature = ''; // if we're in an addon, build import statement if (options.project.isEmberCLIAddon() || (options.inRepoAddon && !options.inDummy)) { @@ -161,17 +171,28 @@ module.exports = { break; case '@glimmer/component': importComponent = `import Component from '@glimmer/component';`; - defaultExport = `class ${classifiedModuleName}Component extends Component {}`; + if (this._isUsingTS) { + componentSignature = generateComponentSignature(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 = generateComponentSignature(classifiedModuleName); + defaultExport = `templateOnly<${classifiedModuleName}Signature>();`; + } else { + defaultExport = `templateOnly();`; + } break; } return { importTemplate, importComponent, + componentSignature, defaultExport, path: getPathOption(options), componentClass, diff --git a/blueprints/component/index.js b/blueprints/component/index.js index fe81e2e7653..90840c5bb42 100644 --- a/blueprints/component/index.js +++ b/blueprints/component/index.js @@ -9,6 +9,7 @@ const getPathOption = require('ember-cli-get-component-path-option'); const normalizeEntityName = require('ember-cli-normalize-entity-name'); const { EOL } = require('os'); const { has } = require('@ember/edition-utils'); +const { generateComponentSignature } = require('../-utils'); const maybePolyfillTypeScriptBlueprints = require('../-maybe-polyfill-typescript-blueprints'); @@ -273,7 +274,7 @@ module.exports = { case '@glimmer/component': importComponent = `import Component from '@glimmer/component';`; if (this._isUsingTS) { - componentSignature = signatureFor(classifiedModuleName); + componentSignature = generateComponentSignature(classifiedModuleName); defaultExport = `class ${classifiedModuleName}Component extends Component<${classifiedModuleName}Signature> {}`; } else { defaultExport = `class ${classifiedModuleName}Component extends Component {}`; @@ -282,7 +283,7 @@ module.exports = { case '@ember/component/template-only': importComponent = `import templateOnly from '@ember/component/template-only';`; if (this._isUsingTS) { - componentSignature = signatureFor(classifiedModuleName); + componentSignature = generateComponentSignature(classifiedModuleName); defaultExport = `templateOnly<${classifiedModuleName}Signature>();`; } else { defaultExport = `templateOnly();`; @@ -300,25 +301,3 @@ module.exports = { }; }, }; - -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}` - ); -}