diff --git a/.eslintrc.js b/.eslintrc.js
index 008c09a6..20afbd86 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -1,3 +1,4 @@
+/* eslint-env node */
 module.exports = {
   root: true,
   parserOptions: {
diff --git a/blueprints/-addon-import.js b/blueprints/-addon-import.js
new file mode 100644
index 00000000..27cf4f7f
--- /dev/null
+++ b/blueprints/-addon-import.js
@@ -0,0 +1,48 @@
+'use strict';
+
+const stringUtil = require('ember-cli-string-utils');
+const path = require('path');
+const inflector = require('inflection');
+
+module.exports = {
+  description: 'Generates an import wrapper.',
+
+  fileMapTokens: function () {
+    return {
+      __name__: function (options) {
+        return options.dasherizedModuleName;
+      },
+      __path__: function (options) {
+        return inflector.pluralize(options.locals.blueprintName);
+      },
+      __root__: function (options) {
+        if (options.inRepoAddon) {
+          return path.join('lib', options.inRepoAddon, 'app');
+        }
+        return 'app';
+      },
+    };
+  },
+
+  locals: function (options) {
+    let addonRawName = options.inRepoAddon ? options.inRepoAddon : options.project.name();
+    let addonName = stringUtil.dasherize(addonRawName);
+    let fileName = stringUtil.dasherize(options.entity.name);
+    let blueprintName = options.originBlueprintName;
+    let modulePathSegments = [
+      addonName,
+      inflector.pluralize(options.originBlueprintName),
+      fileName,
+    ];
+
+    if (blueprintName.match(/-addon/)) {
+      blueprintName = blueprintName.substr(0, blueprintName.indexOf('-addon'));
+      modulePathSegments = [addonName, inflector.pluralize(blueprintName), fileName];
+    }
+
+    return {
+      modulePath: modulePathSegments.join('/'),
+      blueprintName: blueprintName,
+    };
+  },
+};
diff --git a/blueprints/component-addon/files/__root__/__path__/__name__.js b/blueprints/component-addon/files/__root__/__path__/__name__.js
new file mode 100644
index 00000000..71a8b71c
--- /dev/null
+++ b/blueprints/component-addon/files/__root__/__path__/__name__.js
@@ -0,0 +1 @@
+export { default } from '<%= modulePath %>';
\ No newline at end of file
diff --git a/blueprints/component-addon/index.js b/blueprints/component-addon/index.js
new file mode 100644
index 00000000..a3f30e40
--- /dev/null
+++ b/blueprints/component-addon/index.js
@@ -0,0 +1,76 @@
+'use strict';
+
+const path = require('path');
+const stringUtil = require('ember-cli-string-utils');
+const getPathOption = require('ember-cli-get-component-path-option');
+const normalizeEntityName = require('ember-cli-normalize-entity-name');
+const useEditionDetector = require('../edition-detector');
+
+module.exports = useEditionDetector({
+  description: 'Generates a component.',
+
+  fileMapTokens: function () {
+    return {
+      __path__: function (options) {
+        if (options.pod) {
+          return path.join(options.podPath, options.locals.path, options.dasherizedModuleName);
+        }
+        return 'components';
+      },
+      __name__: function (options) {
+        if (options.pod) {
+          return 'component';
+        }
+        return options.dasherizedModuleName;
+      },
+      __root__: function (options) {
+        if (options.inRepoAddon) {
+          return path.join('lib', options.inRepoAddon, 'app');
+        }
+        return 'app';
+      },
+      __templatepath__: function (options) {
+        if (options.pod) {
+          return path.join(options.podPath, options.locals.path, options.dasherizedModuleName);
+        }
+        return 'templates/components';
+      },
+      __templatename__: function (options) {
+        if (options.pod) {
+          return 'template';
+        }
+        return options.dasherizedModuleName;
+      },
+    };
+  },
+
+  normalizeEntityName: function (entityName) {
+    return normalizeEntityName(entityName);
+  },
+
+  locals: function (options) {
+    let addonRawName = options.inRepoAddon ? options.inRepoAddon : options.project.name();
+    let addonName = stringUtil.dasherize(addonRawName);
+    let fileName = stringUtil.dasherize(options.entity.name);
+    let importPathName = [addonName, 'components', fileName].join('/');
+    let templatePath = '';
+
+    if (options.pod) {
+      importPathName = [addonName, 'components', fileName, 'component'].join('/');
+    }
+
+    if (this.project.isEmberCLIAddon() || (options.inRepoAddon && !options.inDummy)) {
+      if (options.pod) {
+        templatePath = './template';
+      } else {
+        templatePath = [addonName, 'templates/components', fileName].join('/');
+      }
+    }
+
+    return {
+      modulePath: importPathName,
+      templatePath,
+      path: getPathOption(options),
+    };
+  },
+});
diff --git a/blueprints/component-addon/native-files/__root__/__path__/__name__.js b/blueprints/component-addon/native-files/__root__/__path__/__name__.js
new file mode 100644
index 00000000..71a8b71c
--- /dev/null
+++ b/blueprints/component-addon/native-files/__root__/__path__/__name__.js
@@ -0,0 +1 @@
+export { default } from '<%= modulePath %>';
\ No newline at end of file
diff --git a/blueprints/component-class-addon/files/__root__/__path__/__name__.js b/blueprints/component-class-addon/files/__root__/__path__/__name__.js
new file mode 100644
index 00000000..71a8b71c
--- /dev/null
+++ b/blueprints/component-class-addon/files/__root__/__path__/__name__.js
@@ -0,0 +1 @@
+export { default } from '<%= modulePath %>';
\ No newline at end of file
diff --git a/blueprints/component-class-addon/index.js b/blueprints/component-class-addon/index.js
new file mode 100644
index 00000000..19272db5
--- /dev/null
+++ b/blueprints/component-class-addon/index.js
@@ -0,0 +1,54 @@
+'use strict';
+
+const path = require('path');
+const stringUtil = require('ember-cli-string-utils');
+const getPathOption = require('ember-cli-get-component-path-option');
+const normalizeEntityName = require('ember-cli-normalize-entity-name');
+const useEditionDetector = require('../edition-detector');
+
+module.exports = useEditionDetector({
+  description: 'Generates a component class.',
+
+  fileMapTokens: function () {
+    return {
+      __path__: function (options) {
+        if (options.pod) {
+          return path.join(options.podPath, options.locals.path, options.dasherizedModuleName);
+        }
+        return 'components';
+      },
+      __name__: function (options) {
+        if (options.pod) {
+          return 'component';
+        }
+        return options.dasherizedModuleName;
+      },
+      __root__: function (options) {
+        if (options.inRepoAddon) {
+          return path.join('lib', options.inRepoAddon, 'app');
+        }
+        return 'app';
+      },
+    };
+  },
+
+  normalizeEntityName: function (entityName) {
+    return normalizeEntityName(entityName);
+  },
+
+  locals: function (options) {
+    let addonRawName = options.inRepoAddon ? options.inRepoAddon : options.project.name();
+    let addonName = stringUtil.dasherize(addonRawName);
+    let fileName = stringUtil.dasherize(options.entity.name);
+    let importPathName = [addonName, 'components', fileName].join('/');
+
+    if (options.pod) {
+      importPathName = [addonName, 'components', fileName, 'component'].join('/');
+    }
+
+    return {
+      modulePath: importPathName,
+      path: getPathOption(options),
+    };
+  },
+});
diff --git a/blueprints/component-class-addon/native-files/__root__/__path__/__name__.js b/blueprints/component-class-addon/native-files/__root__/__path__/__name__.js
new file mode 100644
index 00000000..71a8b71c
--- /dev/null
+++ b/blueprints/component-class-addon/native-files/__root__/__path__/__name__.js
@@ -0,0 +1 @@
+export { default } from '<%= modulePath %>';
\ No newline at end of file
diff --git a/blueprints/component-class/files/__root__/__path__/__name__.js b/blueprints/component-class/files/__root__/__path__/__name__.js
new file mode 100644
index 00000000..777e6155
--- /dev/null
+++ b/blueprints/component-class/files/__root__/__path__/__name__.js
@@ -0,0 +1,3 @@
+<%= importComponent %>
+<%= importTemplate %>
+export default <%= defaultExport %>
diff --git a/blueprints/component-class/index.js b/blueprints/component-class/index.js
new file mode 100644
index 00000000..697d5b0b
--- /dev/null
+++ b/blueprints/component-class/index.js
@@ -0,0 +1,175 @@
+'use strict';
+
+const path = require('path');
+const SilentError = require('silent-error');
+const stringUtil = require('ember-cli-string-utils');
+const pathUtil = require('ember-cli-path-utils');
+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 OCTANE = has('octane');
+
+// TODO: this should be reading from the @ember/canary-features module
+// need to refactor broccoli/features.js to be able to work more similarly
+// to https://github.com/emberjs/data/pull/6231
+const EMBER_GLIMMER_SET_COMPONENT_TEMPLATE = true;
+
+// intentionally avoiding use-edition-detector
+module.exports = {
+  description: 'Generates a component class.',
+
+  availableOptions: [
+    {
+      name: 'path',
+      type: String,
+      default: 'components',
+      aliases: [{ 'no-path': '' }],
+    },
+    {
+      name: 'component-class',
+      type: ['@ember/component', '@glimmer/component', '@ember/component/template-only'],
+      default: OCTANE ? '@glimmer/component' : '@ember/component',
+      aliases: [
+        { cc: '@ember/component' },
+        { gc: '@glimmer/component' },
+        { tc: '@ember/component/template-only' },
+      ],
+    },
+    {
+      name: 'component-structure',
+      type: OCTANE ? ['flat', 'nested', 'classic'] : ['classic'],
+      default: OCTANE ? 'flat' : 'classic',
+      aliases: OCTANE ? [{ fs: 'flat' }, { ns: 'nested' }, { cs: 'classic' }] : [{ cs: 'classic' }],
+    },
+  ],
+
+  init() {
+    this._super && this._super.init.apply(this, arguments);
+    let isOctane = has('octane');
+
+    this.availableOptions.forEach((option) => {
+      if (option.name === 'component-class') {
+        if (isOctane) {
+          option.default = '@glimmer/component';
+        } else {
+          option.default = '@ember/component';
+        }
+      } else if (option.name === 'component-structure') {
+        if (isOctane) {
+          option.type = ['flat', 'nested', 'classic'];
+          option.default = 'flat';
+          option.aliases = [{ fs: 'flat' }, { ns: 'nested' }, { cs: 'classic' }];
+        } else {
+          option.type = ['classic'];
+          option.default = 'classic';
+          option.aliases = [{ cs: 'classic' }];
+        }
+      }
+    });
+
+    this.EMBER_GLIMMER_SET_COMPONENT_TEMPLATE = EMBER_GLIMMER_SET_COMPONENT_TEMPLATE || isOctane;
+  },
+
+  install() {
+    if (!this.EMBER_GLIMMER_SET_COMPONENT_TEMPLATE) {
+      throw new SilentError(
+        'Usage of `ember generate component-class` is only available on canary'
+      );
+    }
+
+    return this._super.install.apply(this, arguments);
+  },
+
+  fileMapTokens(options) {
+    let commandOptions = this.options;
+
+    if (commandOptions.pod) {
+      return {
+        __path__() {
+          return path.join(options.podPath, options.locals.path, options.dasherizedModuleName);
+        },
+        __name__() {
+          return 'component';
+        },
+      };
+    } else if (
+      commandOptions.componentStructure === 'classic' ||
+      commandOptions.componentStructure === 'flat'
+    ) {
+      return {
+        __path__() {
+          return 'components';
+        },
+      };
+    } else if (commandOptions.componentStructure === 'nested') {
+      return {
+        __path__() {
+          return `components/${options.dasherizedModuleName}`;
+        },
+        __name__() {
+          return 'index';
+        },
+      };
+    }
+  },
+
+  normalizeEntityName(entityName) {
+    return normalizeEntityName(
+      entityName.replace(/\.js$/, '') //Prevent generation of ".js.js" files
+    );
+  },
+
+  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
+    if (options.project.isEmberCLIAddon() || (options.inRepoAddon && !options.inDummy)) {
+      if (options.pod) {
+        templatePath = './template';
+      } else {
+        templatePath =
+          pathUtil.getRelativeParentPath(options.entity.name) +
+          'templates/components/' +
+          stringUtil.dasherize(options.entity.name);
+      }
+    }
+
+    let componentClass = options.componentClass;
+
+    switch (componentClass) {
+      case '@ember/component':
+        importComponent = `import Component from '@ember/component';`;
+        if (templatePath) {
+          importTemplate = `import layout from '${templatePath}';${EOL}`;
+          defaultExport = `Component.extend({${EOL}  layout${EOL}});`;
+        } else {
+          defaultExport = `Component.extend({${EOL}});`;
+        }
+        break;
+      case '@glimmer/component':
+        importComponent = `import Component from '@glimmer/component';`;
+        defaultExport = `class ${classifiedModuleName}Component extends Component {\n}`;
+        break;
+      case '@ember/component/template-only':
+        importComponent = `import templateOnly from '@ember/component/template-only';`;
+        defaultExport = `templateOnly();`;
+        break;
+    }
+
+    return {
+      importTemplate,
+      importComponent,
+      defaultExport,
+      path: getPathOption(options),
+      componentClass,
+    };
+  },
+};
diff --git a/blueprints/component-test/index.js b/blueprints/component-test/index.js
new file mode 100644
index 00000000..dbd546ef
--- /dev/null
+++ b/blueprints/component-test/index.js
@@ -0,0 +1,81 @@
+'use strict';
+
+const path = require('path');
+const stringUtil = require('ember-cli-string-utils');
+const getPathOption = require('ember-cli-get-component-path-option');
+
+const useTestFrameworkDetector = require('../test-framework-detector');
+
+function invocationFor(options) {
+  let parts = options.entity.name.split('/');
+  return parts.map((p) => stringUtil.classify(p)).join('::');
+}
+
+module.exports = useTestFrameworkDetector({
+  description: 'Generates a component integration or unit test.',
+
+  availableOptions: [
+    {
+      name: 'test-type',
+      type: ['integration', 'unit'],
+      default: 'integration',
+      aliases: [
+        { i: 'integration' },
+        { u: 'unit' },
+        { integration: 'integration' },
+        { unit: 'unit' },
+      ],
+    },
+  ],
+
+  fileMapTokens: function () {
+    return {
+      __root__() {
+        return 'tests';
+      },
+      __testType__(options) {
+        return options.locals.testType || 'integration';
+      },
+      __path__(options) {
+        if (options.pod) {
+          return path.join(options.podPath, options.locals.path, options.dasherizedModuleName);
+        }
+        return 'components';
+      },
+    };
+  },
+
+  locals: function (options) {
+    let dasherizedModuleName = stringUtil.dasherize(options.entity.name);
+    let componentPathName = dasherizedModuleName;
+    let testType = options.testType || 'integration';
+
+    let friendlyTestDescription = [
+      testType === 'unit' ? 'Unit' : 'Integration',
+      'Component',
+      dasherizedModuleName,
+    ].join(' | ');
+
+    if (options.pod && options.path !== 'components' && options.path !== '') {
+      componentPathName = [options.path, dasherizedModuleName].filter(Boolean).join('/');
+    }
+
+    let templateInvocation = invocationFor(options);
+    let componentName = templateInvocation;
+    let openComponent = (descriptor) => `<${descriptor}>`;
+    let closeComponent = (descriptor) => `${descriptor}>`;
+    let selfCloseComponent = (descriptor) => `<${descriptor} />`;
+
+    return {
+      path: getPathOption(options),
+      testType: testType,
+      componentName,
+      componentPathName,
+      templateInvocation,
+      openComponent,
+      closeComponent,
+      selfCloseComponent,
+      friendlyTestDescription,
+    };
+  }
+});
\ No newline at end of file
diff --git a/blueprints/component-test/mocha-files/__root__/__testType__/__path__/__test__.ts b/blueprints/component-test/mocha-files/__root__/__testType__/__path__/__test__.ts
new file mode 100644
index 00000000..e2b6cd3f
--- /dev/null
+++ b/blueprints/component-test/mocha-files/__root__/__testType__/__path__/__test__.ts
@@ -0,0 +1,27 @@
+import { expect } from 'chai';
+import { describe, it } from 'mocha';
+import { setupRenderingTest } from 'ember-mocha';
+import { render } from '@ember/test-helpers';
+import { hbs } from 'ember-cli-htmlbars';
+
+describe('<%= friendlyTestDescription %>', function() {
+  setupRenderingTest();
+
+  it('renders', async function() {
+    // Set any properties with this.set('myProperty', 'value');
+    // Handle any actions with this.set('myAction', function(val) { ... });
+
+    await render(hbs`<%= selfCloseComponent(componentName) %>`);
+
+    expect(this.element.textContent.trim()).to.equal('');
+
+    // Template block usage:
+    await render(hbs`
+      <%= openComponent(componentName) %>
+        template block text
+      <%= closeComponent(componentName) %>
+    `);
+
+    expect(this.element.textContent.trim()).to.equal('template block text');
+  });
+});
\ No newline at end of file
diff --git a/blueprints/component-test/qunit-files/__root__/__testType__/__path__/__test__.ts b/blueprints/component-test/qunit-files/__root__/__testType__/__path__/__test__.ts
new file mode 100644
index 00000000..6fd18705
--- /dev/null
+++ b/blueprints/component-test/qunit-files/__root__/__testType__/__path__/__test__.ts
@@ -0,0 +1,26 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import { render } from '@ember/test-helpers';
+import { hbs } from 'ember-cli-htmlbars';
+
+module('Integration | Component | <%= dasherizedModuleName %>', function(hooks) {
+  setupRenderingTest(hooks);
+
+  test('it renders', async function(assert) {
+    // Set any properties with this.set('myProperty', 'value');
+    // Handle any actions with this.set('myAction', function(val) { ... });
+
+    await render(hbs`<<%= templateInvocation %> />`);
+
+    assert.equal(this.element.textContent.trim(), '');
+
+    // Template block usage:
+    await render(hbs`
+      <<%= templateInvocation %>>
+        template block text
+      <%= templateInvocation %>>
+    `);
+
+    assert.equal(this.element.textContent.trim(), 'template block text');
+  });
+});
diff --git a/blueprints/component/files/__root__/__path__/__name__.ts b/blueprints/component/files/__root__/__path__/__name__.ts
new file mode 100644
index 00000000..777e6155
--- /dev/null
+++ b/blueprints/component/files/__root__/__path__/__name__.ts
@@ -0,0 +1,3 @@
+<%= importComponent %>
+<%= importTemplate %>
+export default <%= defaultExport %>
diff --git a/blueprints/component/files/__root__/__templatepath__/__templatename__.hbs b/blueprints/component/files/__root__/__templatepath__/__templatename__.hbs
new file mode 100644
index 00000000..fb5c4b15
--- /dev/null
+++ b/blueprints/component/files/__root__/__templatepath__/__templatename__.hbs
@@ -0,0 +1 @@
+{{yield}}
\ No newline at end of file
diff --git a/blueprints/component/glimmer-files/__root__/__path__/__name__.hbs b/blueprints/component/glimmer-files/__root__/__path__/__name__.hbs
new file mode 100644
index 00000000..fb5c4b15
--- /dev/null
+++ b/blueprints/component/glimmer-files/__root__/__path__/__name__.hbs
@@ -0,0 +1 @@
+{{yield}}
\ No newline at end of file
diff --git a/blueprints/component/glimmer-files/__root__/__path__/__name__.ts b/blueprints/component/glimmer-files/__root__/__path__/__name__.ts
new file mode 100644
index 00000000..d5b3ea97
--- /dev/null
+++ b/blueprints/component/glimmer-files/__root__/__path__/__name__.ts
@@ -0,0 +1,7 @@
+import Component from '@glimmer/component';
+
+interface <%= componentName %>Args {  
+}
+
+export default class <%= componentName %> extends Component<<%= componentName %>Args> {
+};
\ No newline at end of file
diff --git a/blueprints/component/index.js b/blueprints/component/index.js
new file mode 100644
index 00000000..6b81f8d2
--- /dev/null
+++ b/blueprints/component/index.js
@@ -0,0 +1,97 @@
+'use strict';
+
+const path = require('path');
+const stringUtil = require('ember-cli-string-utils');
+const pathUtil = require('ember-cli-path-utils');
+const getPathOption = require('ember-cli-get-component-path-option');
+const normalizeEntityName = require('ember-cli-normalize-entity-name');
+const EOL = require('os').EOL;
+
+function componentNameFor(options) {
+  let parts = options.entity.name.split('/');
+  return parts.map((p) => stringUtil.classify(p)).join("");
+}
+
+module.exports = {
+  description: 'Generates a component.',
+
+  availableOptions: [
+    {
+      name: 'path',
+      type: String,
+      default: 'components',
+      aliases: [{ 'no-path': '' }],
+    },
+  ],
+
+  filesPath: function() {
+    let filesDirectory = 'files';
+    let dependencies = this.project.dependencies();
+
+    if ('@glimmer/component' in dependencies) {
+      filesDirectory = 'glimmer-files';
+    }
+
+    return path.join(this.path, filesDirectory);
+  },
+
+  fileMapTokens: function() {
+    return {
+      __path__: function(options) {
+        if (options.pod) {
+          return path.join(options.podPath, options.locals.path, options.dasherizedModuleName);
+        } else {
+          return 'components';
+        }
+      },
+      __templatepath__: function(options) {
+        if (options.pod) {
+          return path.join(options.podPath, options.locals.path, options.dasherizedModuleName);
+        }
+        return 'templates/components';
+      },
+      __templatename__: function(options) {
+        if (options.pod) {
+          return 'template';
+        }
+        return options.dasherizedModuleName;
+      },
+    };
+  },
+
+  normalizeEntityName: function(entityName) {
+    return normalizeEntityName(entityName);
+  },
+
+  locals: function(options) {
+    let templatePath = '';
+    let importTemplate = '';
+    let contents = '';
+
+    let classifiedModuleName = stringUtil.dasherize(options.entity.name);
+
+    let componentName = componentNameFor(options);
+    
+    // if we're in an addon, build import statement
+    if (options.project.isEmberCLIAddon() || (options.inRepoAddon && !options.inDummy)) {
+      if (options.pod) {
+        templatePath = './template';
+      } else {
+        templatePath =
+          pathUtil.getRelativeParentPath(options.entity.name) +
+          'templates/components/' +
+          classifiedModuleName;
+      }
+      importTemplate = '// @ts-ignore: Ignore import of compiled template' + EOL + 'import layout from \'' + templatePath + '\';' + EOL;
+      contents = EOL + '  layout = layout;';
+    }
+
+    return {
+      importTemplate: importTemplate,
+      contents: contents,
+      path: getPathOption(options),
+      classifiedModuleName,
+      componentName
+    };
+  },
+};
diff --git a/blueprints/edition-detector.js b/blueprints/edition-detector.js
new file mode 100644
index 00000000..4e37fcf1
--- /dev/null
+++ b/blueprints/edition-detector.js
@@ -0,0 +1,13 @@
+'use strict';
+
+const { has } = require('@ember/edition-utils');
+const path = require('path');
+
+module.exports = function (blueprint) {
+  blueprint.filesPath = function () {
+    let rootPath = has('octane') ? 'native-files' : 'files';
+    return path.join(this.path, rootPath);
+  };
+
+  return blueprint;
+};
diff --git a/blueprints/helper-test/mocha-files/tests/__testType__/helpers/__name__-test.ts b/blueprints/helper-test/mocha-files/tests/__testType__/helpers/__name__-test.ts
index 8a36c684..4088a058 100644
--- a/blueprints/helper-test/mocha-files/tests/__testType__/helpers/__name__-test.ts
+++ b/blueprints/helper-test/mocha-files/tests/__testType__/helpers/__name__-test.ts
@@ -1,7 +1,7 @@
 import { expect } from 'chai';
 <% if (testType == 'integration') { %>import { describe, it } from 'mocha';
 import { setupComponentTest } from 'ember-mocha';
-import hbs from 'htmlbars-inline-precompile';
+import { hbs } from 'ember-cli-htmlbars';
 
 describe('<%= friendlyTestName %>', function() {
   setupComponentTest('<%= dasherizedModuleName %>', {
@@ -21,7 +21,7 @@ describe('<%= friendlyTestName %>', function() {
 
     this.render(hbs`{{<%= dasherizedModuleName %> inputValue}}`);
 
-    expect(this.$().text().trim()).to.equal('1234');
+    expect(this.element.text().trim()).to.equal('1234');
   });
 });
 <% } else if (testType == 'unit') { %>import { describe, it } from 'mocha';
diff --git a/blueprints/helper-test/qunit-files/tests/__testType__/helpers/__name__-test.ts b/blueprints/helper-test/qunit-files/tests/__testType__/helpers/__name__-test.ts
index 28a173e3..d894396b 100644
--- a/blueprints/helper-test/qunit-files/tests/__testType__/helpers/__name__-test.ts
+++ b/blueprints/helper-test/qunit-files/tests/__testType__/helpers/__name__-test.ts
@@ -1,7 +1,7 @@
 <% if (testType === 'integration') { %>import { module, test } from 'qunit';
 import { setupRenderingTest } from 'ember-qunit';
 import { render } from '@ember/test-helpers';
-import hbs from 'htmlbars-inline-precompile';
+import { hbs } from 'ember-cli-htmlbars';
 
 module('<%= friendlyTestName %>', function(hooks) {
   setupRenderingTest(hooks);
diff --git a/jsconfig.json b/jsconfig.json
new file mode 100644
index 00000000..f408cac8
--- /dev/null
+++ b/jsconfig.json
@@ -0,0 +1 @@
+{"compilerOptions":{"target":"es6","experimentalDecorators":true},"exclude":["node_modules","bower_components","tmp","vendor",".git","dist"]}
\ No newline at end of file
diff --git a/lib/utilities/update-paths-for-addon.js b/lib/utilities/update-paths-for-addon.js
index 02ddcb05..8edf7e6d 100644
--- a/lib/utilities/update-paths-for-addon.js
+++ b/lib/utilities/update-paths-for-addon.js
@@ -14,10 +14,10 @@ module.exports = function(paths, addonName, appName, options) {
   appStarPaths = paths[appNameStar] = paths[appNameStar] || [];
 
   if (options.removePaths) {
-    if (paths.hasOwnProperty(addonName)) {
+    if (Object.prototype.hasOwnProperty.call(paths, addonName)) {
       delete paths[addonName];
     }
-    if (paths.hasOwnProperty(addonNameStar)) {
+    if (Object.prototype.hasOwnProperty.call(paths, addonNameStar)) {
       delete paths[addonNameStar]
     }
     let addonAppPathIndex = appStarPaths.indexOf([addonAppPath, '*'].join('/'));
@@ -26,16 +26,16 @@ module.exports = function(paths, addonName, appName, options) {
       paths[appNameStar] = appStarPaths;
     }
   } else {
-    if (!paths.hasOwnProperty(addonName)) {
+    if (!Object.prototype.hasOwnProperty.call(paths, addonName)) {
       paths[addonName] = [ addonAddonPath ];
     }
-    if (!paths.hasOwnProperty(addonNameStar)) {
+    if (!Object.prototype.hasOwnProperty.call(paths, addonNameStar)) {
       paths[addonNameStar] = [ [addonAddonPath, '*'].join('/') ];
     }
-    if (!paths.hasOwnProperty(addonTestSupportPath)) {
+    if (!Object.prototype.hasOwnProperty.call(paths, addonTestSupportPath)) {
       paths[addonTestSupportPath] = [ [addonPath, 'addon-test-support'].join('/') ];
     }
-    if (!paths.hasOwnProperty(addonTestSupportStarPath)) {
+    if (!Object.prototype.hasOwnProperty.call(paths, addonTestSupportStarPath)) {
       paths[addonTestSupportStarPath] = [ [addonPath, 'addon-test-support', '*'].join('/') ];
     }
     if (appStarPaths.indexOf(addonAppPath) === -1) {
diff --git a/node-tests/blueprints/component-addon-test.js b/node-tests/blueprints/component-addon-test.js
new file mode 100644
index 00000000..b2f898f3
--- /dev/null
+++ b/node-tests/blueprints/component-addon-test.js
@@ -0,0 +1,34 @@
+'use strict';
+
+const blueprintHelpers = require('ember-cli-blueprint-test-helpers/helpers');
+const setupTestHooks = blueprintHelpers.setupTestHooks;
+const emberNew = blueprintHelpers.emberNew;
+const emberGenerateDestroy = blueprintHelpers.emberGenerateDestroy;
+
+const chai = require('ember-cli-blueprint-test-helpers/chai');
+const expect = chai.expect;
+
+describe('Blueprint: component-addon', function () {
+  setupTestHooks(this);
+
+  describe('in addon', function () {
+    beforeEach(function () {
+      return emberNew({ target: 'addon' });
+    });
+
+    it('component-addon foo', function () {
+      return emberGenerateDestroy(['component-addon', 'foo'], (_file) => {
+        expect(_file('app/components/foo.js')).to.contain(
+          "export { default } from 'my-addon/components/foo';"
+        );
+      });
+    });
+    it('component-addon foo-bar', function () {
+      return emberGenerateDestroy(['component-addon', 'foo-bar'], (_file) => {
+        expect(_file('app/components/foo-bar.js')).to.contain(
+          "export { default } from 'my-addon/components/foo-bar';"
+        );
+      });
+    });
+  });
+});
diff --git a/node-tests/blueprints/component-class-addon-test.js b/node-tests/blueprints/component-class-addon-test.js
new file mode 100644
index 00000000..1dcdd80c
--- /dev/null
+++ b/node-tests/blueprints/component-class-addon-test.js
@@ -0,0 +1,34 @@
+'use strict';
+
+const blueprintHelpers = require('ember-cli-blueprint-test-helpers/helpers');
+const setupTestHooks = blueprintHelpers.setupTestHooks;
+const emberNew = blueprintHelpers.emberNew;
+const emberGenerateDestroy = blueprintHelpers.emberGenerateDestroy;
+
+const chai = require('ember-cli-blueprint-test-helpers/chai');
+const expect = chai.expect;
+
+describe('Blueprint: component-class-addon', function () {
+  setupTestHooks(this);
+
+  describe('in addon', function () {
+    beforeEach(function () {
+      return emberNew({ target: 'addon' });
+    });
+
+    it('component-addon foo', function () {
+      return emberGenerateDestroy(['component-class-addon', 'foo'], (_file) => {
+        expect(_file('app/components/foo.js')).to.contain(
+          "export { default } from 'my-addon/components/foo';"
+        );
+      });
+    });
+    it('component-addon foo-bar', function () {
+      return emberGenerateDestroy(['component-class-addon', 'foo-bar'], (_file) => {
+        expect(_file('app/components/foo-bar.js')).to.contain(
+          "export { default } from 'my-addon/components/foo-bar';"
+        );
+      });
+    });
+  });
+});
diff --git a/node-tests/blueprints/component-class-test.js b/node-tests/blueprints/component-class-test.js
new file mode 100644
index 00000000..72d588e4
--- /dev/null
+++ b/node-tests/blueprints/component-class-test.js
@@ -0,0 +1,730 @@
+'use strict';
+
+const blueprintHelpers = require('ember-cli-blueprint-test-helpers/helpers');
+const setupTestHooks = blueprintHelpers.setupTestHooks;
+const emberNew = blueprintHelpers.emberNew;
+const emberGenerateDestroy = blueprintHelpers.emberGenerateDestroy;
+const setupPodConfig = blueprintHelpers.setupPodConfig;
+const modifyPackages = blueprintHelpers.modifyPackages;
+
+const chai = require('ember-cli-blueprint-test-helpers/chai');
+const expect = chai.expect;
+
+const generateFakePackageManifest = require('../helpers/generate-fake-package-manifest');
+const fixture = require('../helpers/fixture');
+
+const setupTestEnvironment = require('../helpers/setup-test-environment');
+const enableOctane = setupTestEnvironment.enableOctane;
+
+const { EMBER_SET_COMPONENT_TEMPLATE } = require('../../blueprints/component');
+
+const glimmerComponentContents = `import Component from '@glimmer/component';
+
+export default class FooComponent extends Component {
+}
+`;
+
+const emberComponentContents = `import Component from '@ember/component';
+
+export default Component.extend({
+});
+`;
+
+const templateOnlyContents = `import templateOnly from '@ember/component/template-only';
+
+export default templateOnly();
+`;
+
+describe('Blueprint: component-class', function () {
+  setupTestHooks(this);
+
+  describe('in app', function () {
+    beforeEach(function () {
+      return emberNew()
+        .then(() =>
+          modifyPackages([
+            { name: 'ember-qunit', delete: true },
+            { name: 'ember-cli-qunit', dev: true },
+          ])
+        )
+        .then(() => generateFakePackageManifest('ember-cli-qunit', '4.1.0'));
+    });
+
+    it('component-class foo', function () {
+      return emberGenerateDestroy(['component-class', 'foo'], (_file) => {
+        expect(_file('app/components/foo.js')).to.equal(emberComponentContents);
+      });
+    });
+
+    if (EMBER_SET_COMPONENT_TEMPLATE) {
+      // classic default
+      it('component-class foo --component-structure=classic --component-class=@ember/component', function () {
+        return emberGenerateDestroy(
+          [
+            'component-class',
+            'foo',
+            '--component-structure',
+            'classic',
+            '--component-class',
+            '@ember/component',
+          ],
+          (_file) => {
+            expect(_file('app/components/foo.js')).to.equal(emberComponentContents);
+          }
+        );
+      });
+
+      // Octane default
+      it('component-class foo --component-structure=flat --component-class=@glimmer/component', function () {
+        return emberGenerateDestroy(
+          [
+            'component-class',
+            '--component-structure',
+            'flat',
+            '--component-class',
+            '@glimmer/component',
+            'foo',
+          ],
+          (_file) => {
+            expect(_file('app/components/foo.js')).to.equal(glimmerComponentContents);
+          }
+        );
+      });
+
+      it('component-class foo --component-structure=flat', function () {
+        return emberGenerateDestroy(
+          ['component-class', '--component-structure', 'flat', 'foo'],
+          (_file) => {
+            expect(_file('app/components/foo.js')).to.equal(emberComponentContents);
+          }
+        );
+      });
+
+      it('component-class foo --component-structure=nested', function () {
+        return emberGenerateDestroy(
+          ['component-class', '--component-structure', 'nested', 'foo'],
+          (_file) => {
+            expect(_file('app/components/foo/index.js')).to.equal(emberComponentContents);
+          }
+        );
+      });
+
+      it('component-class foo --component-structure=classic', function () {
+        return emberGenerateDestroy(
+          ['component-class', '--component-structure', 'classic', 'foo'],
+          (_file) => {
+            expect(_file('app/components/foo.js')).to.equal(emberComponentContents);
+          }
+        );
+      });
+
+      it('component-class foo --component-class=@ember/component', function () {
+        return emberGenerateDestroy(
+          ['component-class', '--component-class', '@ember/component', 'foo'],
+          (_file) => {
+            expect(_file('app/components/foo.js')).to.equal(emberComponentContents);
+          }
+        );
+      });
+
+      it('component-class foo --component-class=@glimmer/component', function () {
+        return emberGenerateDestroy(
+          ['component-class', '--component-class', '@glimmer/component', 'foo'],
+          (_file) => {
+            expect(_file('app/components/foo.js')).to.equal(glimmerComponentContents);
+          }
+        );
+      });
+
+      it('component-class foo --component-class=@ember/component/template-only', function () {
+        return emberGenerateDestroy(
+          ['component-class', '--component-class', '@ember/component/template-only', 'foo'],
+          (_file) => {
+            expect(_file('app/components/foo.js')).to.equal(templateOnlyContents);
+          }
+        );
+      });
+    }
+
+    it('component-class x-foo', function () {
+      return emberGenerateDestroy(['component-class', 'x-foo'], (_file) => {
+        expect(_file('app/components/x-foo.js')).to.equal(fixture('component/component-dash.js'));
+      });
+    });
+
+    it('component-class foo/x-foo', function () {
+      return emberGenerateDestroy(['component-class', 'foo/x-foo'], (_file) => {
+        expect(_file('app/components/foo/x-foo.js')).to.equal(
+          fixture('component/component-nested.js')
+        );
+      });
+    });
+
+    it('component-class x-foo --path foo', function () {
+      return emberGenerateDestroy(['component-class', 'x-foo', '--path', 'foo'], (_file) => {
+        expect(_file('app/components/x-foo.js')).to.equal(fixture('component/component-dash.js'));
+      });
+    });
+
+    it('component-class foo.js', function () {
+      return emberGenerateDestroy(['component-class', 'foo.js'], (_file) => {
+        expect(_file('app/components/foo.js.js')).to.not.exist;
+        expect(_file('app/components/foo.js')).to.equal(fixture('component/component.js'));
+      });
+    });
+
+    it('component-class x-foo --pod', function () {
+      return emberGenerateDestroy(['component-class', 'x-foo', '--pod'], (_file) => {
+        expect(_file('app/components/x-foo/component.js')).to.equal(
+          fixture('component/component-dash.js')
+        );
+      });
+    });
+
+    it('component-class foo/x-foo --pod', function () {
+      return emberGenerateDestroy(['component-class', 'foo/x-foo', '--pod'], (_file) => {
+        expect(_file('app/components/foo/x-foo/component.js')).to.equal(
+          fixture('component/component-nested.js')
+        );
+      });
+    });
+
+    it('component-class x-foo --pod --path foo', function () {
+      return emberGenerateDestroy(
+        ['component-class', 'x-foo', '--pod', '--path', 'foo'],
+        (_file) => {
+          expect(_file('app/foo/x-foo/component.js')).to.equal(
+            fixture('component/component-dash.js')
+          );
+        }
+      );
+    });
+
+    it('component-class foo/x-foo --pod --path bar', function () {
+      return emberGenerateDestroy(
+        ['component-class', 'foo/x-foo', '--pod', '--path', 'bar'],
+        (_file) => {
+          expect(_file('app/bar/foo/x-foo/component.js')).to.equal(
+            fixture('component/component-nested.js')
+          );
+        }
+      );
+    });
+
+    it('component-class x-foo --pod --path bar/foo', function () {
+      return emberGenerateDestroy(
+        ['component-class', 'x-foo', '--pod', '--path', 'bar/foo'],
+        (_file) => {
+          expect(_file('app/bar/foo/x-foo/component.js')).to.equal(
+            fixture('component/component-dash.js')
+          );
+        }
+      );
+    });
+
+    it('component-class foo/x-foo --pod --path bar/baz', function () {
+      return emberGenerateDestroy(
+        ['component-class', 'foo/x-foo', '--pod', '--path', 'bar/baz'],
+        (_file) => {
+          expect(_file('app/bar/baz/foo/x-foo/component.js')).to.equal(
+            fixture('component/component-nested.js')
+          );
+        }
+      );
+    });
+
+    it('component-class x-foo --pod -no-path', function () {
+      return emberGenerateDestroy(['component-class', 'x-foo', '--pod', '-no-path'], (_file) => {
+        expect(_file('app/x-foo/component.js')).to.equal(fixture('component/component-dash.js'));
+      });
+    });
+
+    it('component-class foo/x-foo --pod -no-path', function () {
+      return emberGenerateDestroy(
+        ['component-class', 'foo/x-foo', '--pod', '-no-path'],
+        (_file) => {
+          expect(_file('app/foo/x-foo/component.js')).to.equal(
+            fixture('component/component-nested.js')
+          );
+        }
+      );
+    });
+
+    it('component-class x-foo.js --pod', function () {
+      return emberGenerateDestroy(['component-class', 'x-foo.js', '--pod'], (_file) => {
+        expect(_file('app/components/x-foo.js/component.js')).to.not.exist;
+        expect(_file('app/components/x-foo/component.js')).to.equal(
+          fixture('component/component-dash.js')
+        );
+      });
+    });
+
+    describe('with podModulePrefix', function () {
+      beforeEach(function () {
+        setupPodConfig({ podModulePrefix: true });
+      });
+
+      it('component-class foo --pod', function () {
+        return emberGenerateDestroy(['component-class', 'foo', '--pod'], (_file) => {
+          expect(_file('app/pods/components/foo/component.js')).to.equal(
+            fixture('component/component.js')
+          );
+        });
+      });
+
+      it('component-class x-foo --pod', function () {
+        return emberGenerateDestroy(['component-class', 'x-foo', '--pod'], (_file) => {
+          expect(_file('app/pods/components/x-foo/component.js')).to.equal(
+            fixture('component/component-dash.js')
+          );
+        });
+      });
+
+      it('component-class foo/x-foo --pod', function () {
+        return emberGenerateDestroy(['component-class', 'foo/x-foo', '--pod'], (_file) => {
+          expect(_file('app/pods/components/foo/x-foo/component.js')).to.equal(
+            fixture('component/component-nested.js')
+          );
+        });
+      });
+
+      it('component-class x-foo --pod --path foo', function () {
+        return emberGenerateDestroy(
+          ['component-class', 'x-foo', '--pod', '--path', 'foo'],
+          (_file) => {
+            expect(_file('app/pods/foo/x-foo/component.js')).to.equal(
+              fixture('component/component-dash.js')
+            );
+          }
+        );
+      });
+
+      it('component-class foo/x-foo --pod --path bar', function () {
+        return emberGenerateDestroy(
+          ['component-class', 'foo/x-foo', '--pod', '--path', 'bar'],
+          (_file) => {
+            expect(_file('app/pods/bar/foo/x-foo/component.js')).to.equal(
+              fixture('component/component-nested.js')
+            );
+          }
+        );
+      });
+
+      it('component-class x-foo --pod --path bar/foo', function () {
+        return emberGenerateDestroy(
+          ['component-class', 'x-foo', '--pod', '--path', 'bar/foo'],
+          (_file) => {
+            expect(_file('app/pods/bar/foo/x-foo/component.js')).to.equal(
+              fixture('component/component-dash.js')
+            );
+          }
+        );
+      });
+
+      it('component-class foo/x-foo --pod --path bar/baz', function () {
+        return emberGenerateDestroy(
+          ['component-class', 'foo/x-foo', '--pod', '--path', 'bar/baz'],
+          (_file) => {
+            expect(_file('app/pods/bar/baz/foo/x-foo/component.js')).to.equal(
+              fixture('component/component-nested.js')
+            );
+          }
+        );
+      });
+
+      it('component-class x-foo --pod -no-path', function () {
+        return emberGenerateDestroy(['component-class', 'x-foo', '--pod', '-no-path'], (_file) => {
+          expect(_file('app/pods/x-foo/component.js')).to.equal(
+            fixture('component/component-dash.js')
+          );
+        });
+      });
+
+      it('component-class foo/x-foo --pod -no-path', function () {
+        return emberGenerateDestroy(
+          ['component-class', 'foo/x-foo', '--pod', '-no-path'],
+          (_file) => {
+            expect(_file('app/pods/foo/x-foo/component.js')).to.equal(
+              fixture('component/component-nested.js')
+            );
+          }
+        );
+      });
+    });
+  });
+
+  describe('in app - octane', function () {
+    enableOctane();
+
+    beforeEach(function () {
+      return emberNew()
+        .then(() =>
+          modifyPackages([
+            { name: 'ember-qunit', delete: true },
+            { name: 'ember-cli-qunit', dev: true },
+          ])
+        )
+        .then(() => generateFakePackageManifest('ember-cli-qunit', '4.1.0'));
+    });
+
+    it('component-class foo', function () {
+      return emberGenerateDestroy(['component-class', 'foo'], (_file) => {
+        expect(_file('app/components/foo.js')).to.equal(glimmerComponentContents);
+      });
+    });
+
+    it('component-class x-foo', function () {
+      return emberGenerateDestroy(['component-class', 'x-foo'], (_file) => {
+        expect(_file('app/components/x-foo.js')).to.equal(
+          glimmerComponentContents.replace('FooComponent', 'XFooComponent')
+        );
+      });
+    });
+
+    it('component-class x-foo.js', function () {
+      return emberGenerateDestroy(['component-class', 'x-foo.js'], (_file) => {
+        expect(_file('app/components/x-foo.js.js')).to.not.exist;
+        expect(_file('app/components/x-foo.js')).to.equal(
+          glimmerComponentContents.replace('FooComponent', 'XFooComponent')
+        );
+      });
+    });
+
+    it('component-class foo/x-foo', function () {
+      return emberGenerateDestroy(['component-class', 'foo/x-foo'], (_file) => {
+        expect(_file('app/components/foo/x-foo.js')).to.equal(
+          glimmerComponentContents.replace('FooComponent', 'FooXFooComponent')
+        );
+      });
+    });
+
+    it('component-class foo/x-foo --component-class="@glimmer/component"', function () {
+      return emberGenerateDestroy(
+        ['component-class', 'foo/x-foo', '--component-class', '@glimmer/component'],
+        (_file) => {
+          expect(_file('app/components/foo/x-foo.js')).to.equal(
+            glimmerComponentContents.replace('FooComponent', 'FooXFooComponent')
+          );
+        }
+      );
+    });
+  });
+
+  describe('in addon', function () {
+    beforeEach(function () {
+      return emberNew({ target: 'addon' })
+        .then(() =>
+          modifyPackages([
+            { name: 'ember-qunit', delete: true },
+            { name: 'ember-cli-qunit', dev: true },
+          ])
+        )
+        .then(() => generateFakePackageManifest('ember-cli-qunit', '4.1.0'));
+    });
+
+    it('component-class foo', function () {
+      return emberGenerateDestroy(['component-class', 'foo'], (_file) => {
+        expect(_file('addon/components/foo.js')).to.equal(fixture('component/component-addon.js'));
+        expect(_file('app/components/foo.js')).to.contain(
+          "export { default } from 'my-addon/components/foo';"
+        );
+      });
+    });
+
+    it('component-class x-foo', function () {
+      return emberGenerateDestroy(['component-class', 'x-foo'], (_file) => {
+        expect(_file('addon/components/x-foo.js')).to.equal(
+          fixture('component/component-addon-dash.js')
+        );
+        expect(_file('app/components/x-foo.js')).to.contain(
+          "export { default } from 'my-addon/components/x-foo';"
+        );
+      });
+    });
+
+    it('component-class x-foo.js', function () {
+      return emberGenerateDestroy(['component-class', 'x-foo.js'], (_file) => {
+        expect(_file('addon/components/x-foo.js.js')).to.not.exist;
+        expect(_file('app/components/x-foo.js.js')).to.not.exist;
+        expect(_file('addon/components/x-foo.js')).to.equal(
+          fixture('component/component-addon-dash.js')
+        );
+        expect(_file('app/components/x-foo.js')).to.contain(
+          "export { default } from 'my-addon/components/x-foo';"
+        );
+      });
+    });
+
+    it('component-class foo/x-foo', function () {
+      return emberGenerateDestroy(['component-class', 'foo/x-foo'], (_file) => {
+        expect(_file('addon/components/foo/x-foo.js')).to.equal(
+          fixture('component/component-addon-nested.js')
+        );
+        expect(_file('app/components/foo/x-foo.js')).to.contain(
+          "export { default } from 'my-addon/components/foo/x-foo';"
+        );
+      });
+    });
+
+    it('component-class x-foo --dummy', function () {
+      return emberGenerateDestroy(['component-class', 'x-foo', '--dummy'], (_file) => {
+        expect(_file('tests/dummy/app/components/x-foo.js')).to.equal(
+          fixture('component/component-addon-dash.js')
+        );
+        expect(_file('app/components/x-foo.js')).to.not.exist;
+      });
+    });
+
+    it('component-class foo/x-foo --dummy', function () {
+      return emberGenerateDestroy(['component-class', 'foo/x-foo', '--dummy'], (_file) => {
+        expect(_file('tests/dummy/app/components/foo/x-foo.js')).to.equal(
+          fixture('component/component-addon-nested.js')
+        );
+        expect(_file('app/components/foo/x-foo.js')).to.not.exist;
+      });
+    });
+
+    it('component-class x-foo.js --dummy', function () {
+      return emberGenerateDestroy(['component-class', 'x-foo.js', '--dummy'], (_file) => {
+        expect(_file('tests/dummy/app/components/x-foo.js.js')).to.not.exist;
+        expect(_file('app/components/x-foo.js.js')).to.not.exist;
+        expect(_file('tests/dummy/app/components/x-foo.js')).to.equal(
+          fixture('component/component-addon-dash.js')
+        );
+        expect(_file('app/components/x-foo.js')).to.not.exist;
+      });
+    });
+
+    it('component-class x-foo --pod', function () {
+      return emberGenerateDestroy(['component-class', 'x-foo', '--pod'], (_file) => {
+        expect(_file('addon/components/x-foo/component.js')).to.equal(
+          fixture('component/component-addon-dash-pod.ts')
+        );
+        expect(_file('app/components/x-foo/component.js')).to.contain(
+          "export { default } from 'my-addon/components/x-foo/component';"
+        );
+      });
+    });
+  });
+
+  describe('in addon - octane', function () {
+    enableOctane();
+
+    beforeEach(function () {
+      return emberNew({ target: 'addon' })
+        .then(() =>
+          modifyPackages([
+            { name: 'ember-qunit', delete: true },
+            { name: 'ember-cli-qunit', dev: true },
+          ])
+        )
+        .then(() => generateFakePackageManifest('ember-cli-qunit', '4.1.0'));
+    });
+
+    it('component-class foo', function () {
+      return emberGenerateDestroy(['component-class', 'foo'], (_file) => {
+        expect(_file('addon/components/foo.js')).to.equal(glimmerComponentContents);
+        expect(_file('app/components/foo.js')).to.contain(
+          "export { default } from 'my-addon/components/foo';"
+        );
+      });
+    });
+
+    it('component-class x-foo', function () {
+      return emberGenerateDestroy(['component-class', 'x-foo'], (_file) => {
+        expect(_file('addon/components/x-foo.js')).to.equal(
+          glimmerComponentContents.replace('FooComponent', 'XFooComponent')
+        );
+        expect(_file('app/components/x-foo.js')).to.contain(
+          "export { default } from 'my-addon/components/x-foo';"
+        );
+      });
+    });
+
+    it('component-class foo/x-foo', function () {
+      return emberGenerateDestroy(['component-class', 'foo/x-foo'], (_file) => {
+        expect(_file('addon/components/foo/x-foo.js')).to.equal(
+          glimmerComponentContents.replace('FooComponent', 'FooXFooComponent')
+        );
+        expect(_file('app/components/foo/x-foo.js')).to.contain(
+          "export { default } from 'my-addon/components/foo/x-foo';"
+        );
+      });
+    });
+
+    it('component-class x-foo --dummy', function () {
+      return emberGenerateDestroy(['component-class', 'x-foo', '--dummy'], (_file) => {
+        expect(_file('tests/dummy/app/components/x-foo.js')).equal(
+          glimmerComponentContents.replace('FooComponent', 'XFooComponent')
+        );
+        expect(_file('app/components/x-foo.js')).to.not.exist;
+      });
+    });
+
+    it('component-class foo/x-foo --dummy', function () {
+      return emberGenerateDestroy(['component-class', 'foo/x-foo', '--dummy'], (_file) => {
+        expect(_file('tests/dummy/app/components/foo/x-foo.js')).to.equal(
+          glimmerComponentContents.replace('FooComponent', 'FooXFooComponent')
+        );
+        expect(_file('app/components/foo/x-foo.hbs')).to.not.exist;
+      });
+    });
+  });
+
+  describe('in in-repo-addon', function () {
+    beforeEach(function () {
+      return emberNew({ target: 'in-repo-addon' })
+        .then(() =>
+          modifyPackages([
+            { name: 'ember-qunit', delete: true },
+            { name: 'ember-cli-qunit', dev: true },
+          ])
+        )
+        .then(() => generateFakePackageManifest('ember-cli-qunit', '4.1.0'));
+    });
+
+    it('component-class foo --in-repo-addon=my-addon', function () {
+      return emberGenerateDestroy(
+        ['component-class', 'foo', '--in-repo-addon=my-addon'],
+        (_file) => {
+          expect(_file('lib/my-addon/addon/components/foo.js')).to.equal(
+            fixture('component/component-addon.js')
+          );
+          expect(_file('lib/my-addon/app/components/foo.js')).to.contain(
+            "export { default } from 'my-addon/components/foo';"
+          );
+        }
+      );
+    });
+
+    it('component-class x-foo --in-repo-addon=my-addon', function () {
+      return emberGenerateDestroy(
+        ['component-class', 'x-foo', '--in-repo-addon=my-addon'],
+        (_file) => {
+          expect(_file('lib/my-addon/addon/components/x-foo.js')).to.equal(
+            fixture('component/component-addon-dash.js')
+          );
+          expect(_file('lib/my-addon/app/components/x-foo.js')).to.contain(
+            "export { default } from 'my-addon/components/x-foo';"
+          );
+        }
+      );
+    });
+
+    it('component-class x-foo.js --in-repo-addon=my-addon', function () {
+      return emberGenerateDestroy(
+        ['component-class', 'x-foo.js', '--in-repo-addon=my-addon'],
+        (_file) => {
+          expect(_file('lib/my-addon/addon/components/x-foo.js.js')).to.not.exist;
+          expect(_file('lib/my-addon/app/components/x-foo.js.js')).to.not.exist;
+
+          expect(_file('lib/my-addon/addon/components/x-foo.js')).to.equal(
+            fixture('component/component-addon-dash.js')
+          );
+          expect(_file('lib/my-addon/app/components/x-foo.js')).to.contain(
+            "export { default } from 'my-addon/components/x-foo';"
+          );
+        }
+      );
+    });
+
+    it('component-class foo/x-foo --in-repo-addon=my-addon', function () {
+      return emberGenerateDestroy(
+        ['component-class', 'foo/x-foo', '--in-repo-addon=my-addon'],
+        (_file) => {
+          expect(_file('lib/my-addon/addon/components/foo/x-foo.js')).to.equal(
+            fixture('component/component-addon-nested.js')
+          );
+          expect(_file('lib/my-addon/app/components/foo/x-foo.js')).to.contain(
+            "export { default } from 'my-addon/components/foo/x-foo';"
+          );
+        }
+      );
+    });
+
+    it('component-class x-foo --in-repo-addon=my-addon --pod', function () {
+      return emberGenerateDestroy(
+        ['component-class', 'x-foo', '--in-repo-addon=my-addon', '--pod'],
+        (_file) => {
+          expect(_file('lib/my-addon/addon/components/x-foo/component.js')).to.equal(
+            fixture('component/component-addon-dash-pod.ts')
+          );
+          expect(_file('lib/my-addon/app/components/x-foo/component.js')).to.contain(
+            "export { default } from 'my-addon/components/x-foo/component';"
+          );
+        }
+      );
+    });
+
+    it('component-class x-foo.js --in-repo-addon=my-addon --pod', function () {
+      return emberGenerateDestroy(
+        ['component-class', 'x-foo.js', '--in-repo-addon=my-addon', '--pod'],
+        (_file) => {
+          expect(_file('lib/my-addon/addon/components/x-foo/component.js.js')).to.not.exist;
+          expect(_file('lib/my-addon/app/components/x-foo/component.js.js')).to.not.exist;
+          expect(_file('lib/my-addon/addon/components/x-foo/component.js')).to.equal(
+            fixture('component/component-addon-dash-pod.ts')
+          );
+          expect(_file('lib/my-addon/app/components/x-foo/component.js')).to.contain(
+            "export { default } from 'my-addon/components/x-foo/component';"
+          );
+        }
+      );
+    });
+
+    it('component-class foo/x-foo --in-repo-addon=my-addon --pod', function () {
+      return emberGenerateDestroy(
+        ['component-class', 'foo/x-foo', '--in-repo-addon=my-addon', '--pod'],
+        (_file) => {
+          expect(_file('lib/my-addon/addon/components/foo/x-foo/component.js')).to.equal(
+            fixture('component/component-addon-nested-pod.ts')
+          );
+          expect(_file('lib/my-addon/app/components/foo/x-foo/component.js')).to.contain(
+            "export { default } from 'my-addon/components/foo/x-foo/component';"
+          );
+        }
+      );
+    });
+  });
+
+  describe('in in-repo-addon - octane', function () {
+    enableOctane();
+
+    beforeEach(function () {
+      return emberNew({ target: 'in-repo-addon' })
+        .then(() =>
+          modifyPackages([
+            { name: 'ember-qunit', delete: true },
+            { name: 'ember-cli-qunit', dev: true },
+          ])
+        )
+        .then(() => generateFakePackageManifest('ember-cli-qunit', '4.1.0'));
+    });
+
+    it('component-class foo --in-repo-addon=my-addon', function () {
+      return emberGenerateDestroy(
+        ['component-class', 'foo', '--in-repo-addon=my-addon'],
+        (_file) => {
+          expect(_file('lib/my-addon/addon/components/foo.js')).to.equal(glimmerComponentContents);
+          expect(_file('lib/my-addon/app/components/foo.js')).to.contain(
+            "export { default } from 'my-addon/components/foo';"
+          );
+        }
+      );
+    });
+
+    it('component-class x-foo --in-repo-addon=my-addon', function () {
+      return emberGenerateDestroy(
+        ['component-class', 'x-foo', '--in-repo-addon=my-addon'],
+        (_file) => {
+          expect(_file('lib/my-addon/addon/components/x-foo.js')).to.equal(
+            glimmerComponentContents.replace('FooComponent', 'XFooComponent')
+          );
+          expect(_file('lib/my-addon/app/components/x-foo.js')).to.contain(
+            "export { default } from 'my-addon/components/x-foo';"
+          );
+        }
+      );
+    });
+  });
+});
diff --git a/node-tests/blueprints/component-test-test.js b/node-tests/blueprints/component-test-test.js
new file mode 100644
index 00000000..dcfc7fac
--- /dev/null
+++ b/node-tests/blueprints/component-test-test.js
@@ -0,0 +1,103 @@
+'use strict';
+
+const blueprintHelpers = require('ember-cli-blueprint-test-helpers/helpers');
+const setupTestHooks = blueprintHelpers.setupTestHooks;
+const emberNew = blueprintHelpers.emberNew;
+const emberGenerateDestroy = blueprintHelpers.emberGenerateDestroy;
+const modifyPackages = blueprintHelpers.modifyPackages;
+
+const chai = require('ember-cli-blueprint-test-helpers/chai');
+const expect = chai.expect;
+
+const generateFakePackageManifest = require('../helpers/generate-fake-package-manifest');
+const fixture = require('../helpers/fixture');
+
+describe('Blueprint: component-test', function() {
+  setupTestHooks(this);
+
+  describe('in app', function() {
+    beforeEach(function() {
+      return emberNew();
+    });
+
+    describe('with ember-cli-qunit@4.6.0', function() {
+      beforeEach(function() {
+        generateFakePackageManifest('ember-cli-qunit', '4.2.0');
+      });
+
+      it('component-test x-foo', function() {
+        return emberGenerateDestroy(['component-test', 'x-foo'], _file => {
+          expect(_file('tests/integration/components/x-foo-test.ts')).to.equal(
+            fixture('component-test/qunit.ts')
+          );
+        });
+      });
+
+    });
+
+    describe('with ember-mocha@0.16.0', function() {
+      beforeEach(function() {
+        modifyPackages([
+          { name: 'ember-cli-qunit', delete: true },
+          { name: 'ember-mocha', dev: true },
+        ]);
+        generateFakePackageManifest('ember-mocha', '0.16.0');
+      });
+
+      it('component-test x-foo', function() {
+        return emberGenerateDestroy(['component-test', 'x-foo'], _file => {
+          expect(_file('tests/integration/components/x-foo-test.ts')).to.equal(
+            fixture('component-test/mocha.ts')
+          );
+        });
+      });
+    });
+  });
+
+  describe('in addon', function() {
+    beforeEach(function() {
+      return emberNew({ target: 'addon' }).then(() =>
+        generateFakePackageManifest('ember-qunit', '4.6.0')
+      );
+    });
+
+    it('component-test x-foo', function() {
+      return emberGenerateDestroy(['component-test', 'x-foo'], _file => {
+        expect(_file('tests/integration/components/x-foo-test.ts')).to.equal(
+          fixture('component-test/qunit.ts')
+        );
+
+        expect(_file('app/component-test/x-foo.ts')).to.not.exist;
+      });
+    });
+
+    it('component-test x-foo --dummy', function() {
+      return emberGenerateDestroy(['component-test', 'x-foo', '--dummy'], _file => {
+        expect(_file('tests/integration/components/x-foo-test.ts')).to.equal(
+          fixture('component-test/qunit.ts')
+        );
+
+        expect(_file('app/component-test/x-foo.ts')).to.not.exist;
+      });
+    });
+  });
+
+  describe('in in-repo-addon', function() {
+    beforeEach(function() {
+      return emberNew({ target: 'in-repo-addon' }).then(() =>
+        generateFakePackageManifest('ember-qunit', '4.6.0')
+      );
+    });
+
+    it('component-test x-foo --in-repo-addon=my-addon', function() {
+      return emberGenerateDestroy(
+        ['component-test', 'x-foo', '--in-repo-addon=my-addon'],
+        _file => {
+          expect(_file('tests/integration/components/x-foo-test.ts')).to.equal(
+            fixture('component-test/qunit.ts')
+          );
+        }
+      );
+    });
+  });
+});
diff --git a/node-tests/blueprints/component-test.js b/node-tests/blueprints/component-test.js
new file mode 100644
index 00000000..f1da2e5a
--- /dev/null
+++ b/node-tests/blueprints/component-test.js
@@ -0,0 +1,1369 @@
+'use strict';
+
+const blueprintHelpers = require('ember-cli-blueprint-test-helpers/helpers');
+const setupTestHooks = blueprintHelpers.setupTestHooks;
+const emberNew = blueprintHelpers.emberNew;
+const emberGenerateDestroy = blueprintHelpers.emberGenerateDestroy;
+const setupPodConfig = blueprintHelpers.setupPodConfig;
+const modifyPackages = blueprintHelpers.modifyPackages;
+
+const chai = require('ember-cli-blueprint-test-helpers/chai');
+const expect = chai.expect;
+
+const generateFakePackageManifest = require('../helpers/generate-fake-package-manifest');
+const fixture = require('../helpers/fixture');
+
+const setupTestEnvironment = require('../helpers/setup-test-environment');
+const enableOctane = setupTestEnvironment.enableOctane;
+
+const { EMBER_SET_COMPONENT_TEMPLATE } = require('../../blueprints/component');
+
+const glimmerComponentContents = `import Component from '@glimmer/component';
+
+export default class FooComponent extends Component {
+}
+`;
+
+const emberComponentContents = `import Component from '@ember/component';
+
+export default Component.extend({
+});
+`;
+
+const templateOnlyContents = `import templateOnly from '@ember/component/template-only';
+
+export default templateOnly();
+`;
+
+describe('Blueprint: component', function () {
+  setupTestHooks(this);
+
+  describe('in app', function () {
+    beforeEach(function () {
+      return emberNew()
+        .then(() =>
+          modifyPackages([
+            { name: 'ember-qunit', delete: true },
+            { name: 'ember-cli-qunit', dev: true },
+          ])
+        )
+        .then(() => generateFakePackageManifest('ember-cli-qunit', '4.1.0'));
+    });
+
+    it('component foo', function () {
+      return emberGenerateDestroy(['component', 'foo'], (_file) => {
+        expect(_file('app/components/foo.ts')).to.equal(emberComponentContents);
+
+        expect(_file('app/templates/components/foo.hbs')).to.equal('{{yield}}');
+
+        expect(_file('tests/integration/components/foo-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'foo',
+              componentInvocation: 'Foo',
+            },
+          })
+        );
+      });
+    });
+
+    if (EMBER_SET_COMPONENT_TEMPLATE) {
+      // classic default
+      it('component foo --component-structure=classic --component-class=@ember/component', function () {
+        return emberGenerateDestroy(
+          [
+            'component',
+            'foo',
+            '--component-structure',
+            'classic',
+            '--component-class',
+            '@ember/component',
+          ],
+          (_file) => {
+            expect(_file('app/components/foo.ts')).to.equal(emberComponentContents);
+
+            expect(_file('app/templates/components/foo.hbs')).to.equal('{{yield}}');
+
+            expect(_file('tests/integration/components/foo-test.ts')).to.equal(
+              fixture('component-test/default-template.ts', {
+                replace: {
+                  component: 'foo',
+                  componentInvocation: 'Foo',
+                },
+              })
+            );
+          }
+        );
+      });
+
+      // Octane default
+      it('component foo --component-structure=flat --component-class=@glimmer/component', function () {
+        return emberGenerateDestroy(
+          [
+            'component',
+            '--component-structure',
+            'flat',
+            '--component-class',
+            '@glimmer/component',
+            'foo',
+          ],
+          (_file) => {
+            expect(_file('app/components/foo.ts')).to.equal(glimmerComponentContents);
+
+            expect(_file('app/components/foo.hbs')).to.equal('{{yield}}');
+
+            expect(_file('tests/integration/components/foo-test.ts')).to.equal(
+              fixture('component-test/default-template.ts', {
+                replace: {
+                  component: 'foo',
+                  componentInvocation: 'Foo',
+                },
+              })
+            );
+          }
+        );
+      });
+
+      it('component foo --component-structure=flat', function () {
+        return emberGenerateDestroy(
+          ['component', '--component-structure', 'flat', 'foo'],
+          (_file) => {
+            expect(_file('app/components/foo.ts')).to.equal(emberComponentContents);
+
+            expect(_file('app/components/foo.hbs')).to.equal('{{yield}}');
+
+            expect(_file('tests/integration/components/foo-test.ts')).to.equal(
+              fixture('component-test/default-template.ts', {
+                replace: {
+                  component: 'foo',
+                  componentInvocation: 'Foo',
+                },
+              })
+            );
+          }
+        );
+      });
+
+      it('component foo --component-structure=nested', function () {
+        return emberGenerateDestroy(
+          ['component', '--component-structure', 'nested', 'foo'],
+          (_file) => {
+            expect(_file('app/components/foo/index.ts')).to.equal(emberComponentContents);
+
+            expect(_file('app/components/foo/index.hbs')).to.equal('{{yield}}');
+
+            expect(_file('tests/integration/components/foo-test.ts')).to.equal(
+              fixture('component-test/default-template.ts', {
+                replace: {
+                  component: 'foo',
+                  componentInvocation: 'Foo',
+                },
+              })
+            );
+          }
+        );
+      });
+
+      it('component foo --component-structure=classic', function () {
+        return emberGenerateDestroy(
+          ['component', '--component-structure', 'classic', 'foo'],
+          (_file) => {
+            expect(_file('app/components/foo.ts')).to.equal(emberComponentContents);
+
+            expect(_file('app/templates/components/foo.hbs')).to.equal('{{yield}}');
+
+            expect(_file('tests/integration/components/foo-test.ts')).to.equal(
+              fixture('component-test/default-template.ts', {
+                replace: {
+                  component: 'foo',
+                  componentInvocation: 'Foo',
+                },
+              })
+            );
+          }
+        );
+      });
+
+      it('component foo --component-class=@ember/component', function () {
+        return emberGenerateDestroy(
+          ['component', '--component-class', '@ember/component', 'foo'],
+          (_file) => {
+            expect(_file('app/components/foo.ts')).to.equal(emberComponentContents);
+
+            expect(_file('app/templates/components/foo.hbs')).to.equal('{{yield}}');
+
+            expect(_file('tests/integration/components/foo-test.ts')).to.equal(
+              fixture('component-test/default-template.ts', {
+                replace: {
+                  component: 'foo',
+                  componentInvocation: 'Foo',
+                },
+              })
+            );
+          }
+        );
+      });
+
+      it('component foo --component-class=@glimmer/component', function () {
+        return emberGenerateDestroy(
+          ['component', '--component-class', '@glimmer/component', 'foo'],
+          (_file) => {
+            expect(_file('app/components/foo.ts')).to.equal(glimmerComponentContents);
+
+            expect(_file('app/templates/components/foo.hbs')).to.equal('{{yield}}');
+
+            expect(_file('tests/integration/components/foo-test.ts')).to.equal(
+              fixture('component-test/default-template.ts', {
+                replace: {
+                  component: 'foo',
+                  componentInvocation: 'Foo',
+                },
+              })
+            );
+          }
+        );
+      });
+
+      it('component foo --component-class=@ember/component/template-only', function () {
+        return emberGenerateDestroy(
+          ['component', '--component-class', '@ember/component/template-only', 'foo'],
+          (_file) => {
+            expect(_file('app/components/foo.ts')).to.equal(templateOnlyContents);
+
+            expect(_file('app/templates/components/foo.hbs')).to.equal('{{yield}}');
+
+            expect(_file('tests/integration/components/foo-test.ts')).to.equal(
+              fixture('component-test/default-template.ts', {
+                replace: {
+                  component: 'foo',
+                  componentInvocation: 'Foo',
+                },
+              })
+            );
+          }
+        );
+      });
+
+      it('component foo --no-component-class', function () {
+        return emberGenerateDestroy(['component', '--no-component-class', 'foo'], (_file) => {
+          expect(_file('app/components/foo.ts')).to.not.exist;
+
+          expect(_file('app/templates/components/foo.hbs')).to.equal('{{yield}}');
+
+          expect(_file('tests/integration/components/foo-test.ts')).to.equal(
+            fixture('component-test/default-template.ts', {
+              replace: {
+                component: 'foo',
+                componentInvocation: 'Foo',
+              },
+            })
+          );
+        });
+      });
+    }
+    it('component x-foo', function () {
+      return emberGenerateDestroy(['component', 'x-foo'], (_file) => {
+        expect(_file('app/components/x-foo.ts')).to.equal(fixture('component/component-dash.ts'));
+
+        expect(_file('app/templates/components/x-foo.hbs')).to.equal('{{yield}}');
+
+        expect(_file('tests/integration/components/x-foo-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'x-foo',
+              componentInvocation: 'XFoo',
+            },
+          })
+        );
+      });
+    });
+
+    it('component foo/x-foo', function () {
+      return emberGenerateDestroy(['component', 'foo/x-foo'], (_file) => {
+        expect(_file('app/components/foo/x-foo.ts')).to.equal(
+          fixture('component/component-nested.ts')
+        );
+
+        expect(_file('app/templates/components/foo/x-foo.hbs')).to.equal('{{yield}}');
+
+        expect(_file('tests/integration/components/foo/x-foo-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'foo/x-foo',
+              componentInvocation: 'Foo::XFoo',
+            },
+          })
+        );
+      });
+    });
+
+    it('component x-foo --path foo', function () {
+      return emberGenerateDestroy(['component', 'x-foo', '--path', 'foo'], (_file) => {
+        expect(_file('app/components/x-foo.ts')).to.equal(fixture('component/component-dash.ts'));
+
+        expect(_file('app/templates/components/x-foo.hbs')).to.equal('{{yield}}');
+
+        expect(_file('tests/integration/components/x-foo-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'x-foo',
+              componentInvocation: 'XFoo',
+            },
+          })
+        );
+      });
+    });
+
+    it('component foo.ts', function () {
+      return emberGenerateDestroy(['component', 'foo.ts'], (_file) => {
+        expect(_file('app/components/foo.js.ts')).to.not.exist;
+        expect(_file('app/templates/components/foo.js.hbs')).to.not.exist;
+        expect(_file('tests/integration/components/foo.js-test.ts')).to.not.exist;
+
+        expect(_file('app/components/foo.ts')).to.equal(fixture('component/component.ts'));
+
+        expect(_file('app/templates/components/foo.hbs')).to.equal('{{yield}}');
+
+        expect(_file('tests/integration/components/foo-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'foo',
+              componentInvocation: 'Foo',
+            },
+          })
+        );
+      });
+    });
+
+    it('component x-foo --pod', function () {
+      return emberGenerateDestroy(['component', 'x-foo', '--pod'], (_file) => {
+        expect(_file('app/components/x-foo/component.ts')).to.equal(
+          fixture('component/component-dash.ts')
+        );
+
+        expect(_file('app/components/x-foo/template.hbs')).to.equal('{{yield}}');
+
+        expect(_file('tests/integration/components/x-foo/component-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'x-foo',
+              componentInvocation: 'XFoo',
+            },
+          })
+        );
+      });
+    });
+
+    it('component foo/x-foo --pod', function () {
+      return emberGenerateDestroy(['component', 'foo/x-foo', '--pod'], (_file) => {
+        expect(_file('app/components/foo/x-foo/component.ts')).to.equal(
+          fixture('component/component-nested.ts')
+        );
+
+        expect(_file('app/components/foo/x-foo/template.hbs')).to.equal('{{yield}}');
+
+        expect(_file('tests/integration/components/foo/x-foo/component-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'foo/x-foo',
+              componentInvocation: 'Foo::XFoo',
+            },
+          })
+        );
+      });
+    });
+
+    it('component x-foo --pod --path foo', function () {
+      return emberGenerateDestroy(['component', 'x-foo', '--pod', '--path', 'foo'], (_file) => {
+        expect(_file('app/foo/x-foo/component.ts')).to.equal(
+          fixture('component/component-dash.ts')
+        );
+
+        expect(_file('app/foo/x-foo/template.hbs')).to.equal('{{yield}}');
+
+        expect(_file('tests/integration/foo/x-foo/component-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'x-foo',
+              componentInvocation: 'XFoo',
+              path: 'foo/',
+            },
+          })
+        );
+      });
+    });
+
+    it('component foo/x-foo --pod --path bar', function () {
+      return emberGenerateDestroy(['component', 'foo/x-foo', '--pod', '--path', 'bar'], (_file) => {
+        expect(_file('app/bar/foo/x-foo/component.ts')).to.equal(
+          fixture('component/component-nested.ts')
+        );
+
+        expect(_file('app/bar/foo/x-foo/template.hbs')).to.equal('{{yield}}');
+
+        expect(_file('tests/integration/bar/foo/x-foo/component-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'foo/x-foo',
+              componentInvocation: 'Foo::XFoo',
+              path: 'bar/',
+            },
+          })
+        );
+      });
+    });
+
+    it('component x-foo --pod --path bar/foo', function () {
+      return emberGenerateDestroy(['component', 'x-foo', '--pod', '--path', 'bar/foo'], (_file) => {
+        expect(_file('app/bar/foo/x-foo/component.ts')).to.equal(
+          fixture('component/component-dash.ts')
+        );
+
+        expect(_file('app/bar/foo/x-foo/template.hbs')).to.equal('{{yield}}');
+
+        expect(_file('tests/integration/bar/foo/x-foo/component-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'x-foo',
+              componentInvocation: 'XFoo',
+              path: 'bar/foo/',
+            },
+          })
+        );
+      });
+    });
+
+    it('component foo/x-foo --pod --path bar/baz', function () {
+      return emberGenerateDestroy(
+        ['component', 'foo/x-foo', '--pod', '--path', 'bar/baz'],
+        (_file) => {
+          expect(_file('app/bar/baz/foo/x-foo/component.ts')).to.equal(
+            fixture('component/component-nested.ts')
+          );
+
+          expect(_file('app/bar/baz/foo/x-foo/template.hbs')).to.equal('{{yield}}');
+
+          expect(_file('tests/integration/bar/baz/foo/x-foo/component-test.ts')).to.equal(
+            fixture('component-test/default-template.ts', {
+              replace: {
+                component: 'foo/x-foo',
+                componentInvocation: 'Foo::XFoo',
+                path: 'bar/baz/',
+              },
+            })
+          );
+        }
+      );
+    });
+
+    it('component x-foo --pod -no-path', function () {
+      return emberGenerateDestroy(['component', 'x-foo', '--pod', '-no-path'], (_file) => {
+        expect(_file('app/x-foo/component.ts')).to.equal(fixture('component/component-dash.ts'));
+
+        expect(_file('app/x-foo/template.hbs')).to.equal('{{yield}}');
+
+        expect(_file('tests/integration/x-foo/component-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'x-foo',
+              componentInvocation: 'XFoo',
+            },
+          })
+        );
+      });
+    });
+
+    it('component foo/x-foo --pod -no-path', function () {
+      return emberGenerateDestroy(['component', 'foo/x-foo', '--pod', '-no-path'], (_file) => {
+        expect(_file('app/foo/x-foo/component.ts')).to.equal(
+          fixture('component/component-nested.ts')
+        );
+
+        expect(_file('app/foo/x-foo/template.hbs')).to.equal('{{yield}}');
+
+        expect(_file('tests/integration/foo/x-foo/component-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'foo/x-foo',
+              componentInvocation: 'Foo::XFoo',
+            },
+          })
+        );
+      });
+    });
+
+    it('component x-foo.js --pod', function () {
+      return emberGenerateDestroy(['component', 'x-foo.ts', '--pod'], (_file) => {
+        expect(_file('app/components/x-foo.js/component.ts')).to.not.exist;
+        expect(_file('app/components/x-foo.js/template.hbs')).to.not.exist;
+        expect(_file('tests/integration/components/x-foo.js/component-test.ts')).to.not.exist;
+
+        expect(_file('app/components/x-foo/component.ts')).to.equal(
+          fixture('component/component-dash.ts')
+        );
+
+        expect(_file('app/components/x-foo/template.hbs')).to.equal('{{yield}}');
+
+        expect(_file('tests/integration/components/x-foo/component-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'x-foo',
+              componentInvocation: 'XFoo',
+            },
+          })
+        );
+      });
+    });
+
+    describe('with podModulePrefix', function () {
+      beforeEach(function () {
+        setupPodConfig({ podModulePrefix: true });
+      });
+
+      it('component foo --pod', function () {
+        return emberGenerateDestroy(['component', 'foo', '--pod'], (_file) => {
+          expect(_file('app/pods/components/foo/component.ts')).to.equal(
+            fixture('component/component.ts')
+          );
+
+          expect(_file('app/pods/components/foo/template.hbs')).to.equal('{{yield}}');
+
+          expect(_file('tests/integration/pods/components/foo/component-test.ts')).to.equal(
+            fixture('component-test/default-template.ts', {
+              replace: {
+                component: 'foo',
+                componentInvocation: 'Foo',
+              },
+            })
+          );
+        });
+      });
+
+      it('component x-foo --pod', function () {
+        return emberGenerateDestroy(['component', 'x-foo', '--pod'], (_file) => {
+          expect(_file('app/pods/components/x-foo/component.ts')).to.equal(
+            fixture('component/component-dash.ts')
+          );
+
+          expect(_file('app/pods/components/x-foo/template.hbs')).to.equal('{{yield}}');
+
+          expect(_file('tests/integration/pods/components/x-foo/component-test.ts')).to.equal(
+            fixture('component-test/default-template.ts', {
+              replace: {
+                component: 'x-foo',
+                componentInvocation: 'XFoo',
+              },
+            })
+          );
+        });
+      });
+
+      it('component foo/x-foo --pod', function () {
+        return emberGenerateDestroy(['component', 'foo/x-foo', '--pod'], (_file) => {
+          expect(_file('app/pods/components/foo/x-foo/component.ts')).to.equal(
+            fixture('component/component-nested.ts')
+          );
+
+          expect(_file('app/pods/components/foo/x-foo/template.hbs')).to.equal('{{yield}}');
+
+          expect(_file('tests/integration/pods/components/foo/x-foo/component-test.ts')).to.equal(
+            fixture('component-test/default-template.ts', {
+              replace: {
+                component: 'foo/x-foo',
+                componentInvocation: 'Foo::XFoo',
+              },
+            })
+          );
+        });
+      });
+
+      it('component x-foo --pod --path foo', function () {
+        return emberGenerateDestroy(['component', 'x-foo', '--pod', '--path', 'foo'], (_file) => {
+          expect(_file('app/pods/foo/x-foo/component.ts')).to.equal(
+            fixture('component/component-dash.ts')
+          );
+
+          expect(_file('app/pods/foo/x-foo/template.hbs')).to.equal('{{yield}}');
+
+          expect(_file('tests/integration/pods/foo/x-foo/component-test.ts')).to.equal(
+            fixture('component-test/default-template.ts', {
+              replace: {
+                component: 'x-foo',
+                componentInvocation: 'XFoo',
+                path: 'foo/',
+              },
+            })
+          );
+        });
+      });
+
+      it('component foo/x-foo --pod --path bar', function () {
+        return emberGenerateDestroy(
+          ['component', 'foo/x-foo', '--pod', '--path', 'bar'],
+          (_file) => {
+            expect(_file('app/pods/bar/foo/x-foo/component.ts')).to.equal(
+              fixture('component/component-nested.ts')
+            );
+
+            expect(_file('app/pods/bar/foo/x-foo/template.hbs')).to.equal('{{yield}}');
+
+            expect(_file('tests/integration/pods/bar/foo/x-foo/component-test.ts')).to.equal(
+              fixture('component-test/default-template.ts', {
+                replace: {
+                  component: 'foo/x-foo',
+                  componentInvocation: 'Foo::XFoo',
+                  path: 'bar/',
+                },
+              })
+            );
+          }
+        );
+      });
+
+      it('component x-foo --pod --path bar/foo', function () {
+        return emberGenerateDestroy(
+          ['component', 'x-foo', '--pod', '--path', 'bar/foo'],
+          (_file) => {
+            expect(_file('app/pods/bar/foo/x-foo/component.ts')).to.equal(
+              fixture('component/component-dash.ts')
+            );
+
+            expect(_file('app/pods/bar/foo/x-foo/template.hbs')).to.equal('{{yield}}');
+            expect(_file('tests/integration/pods/bar/foo/x-foo/component-test.ts')).to.equal(
+              fixture('component-test/default-template.ts', {
+                replace: {
+                  component: 'x-foo',
+                  componentInvocation: 'XFoo',
+                  path: 'bar/foo/',
+                },
+              })
+            );
+          }
+        );
+      });
+
+      it('component foo/x-foo --pod --path bar/baz', function () {
+        return emberGenerateDestroy(
+          ['component', 'foo/x-foo', '--pod', '--path', 'bar/baz'],
+          (_file) => {
+            expect(_file('app/pods/bar/baz/foo/x-foo/component.ts')).to.equal(
+              fixture('component/component-nested.ts')
+            );
+
+            expect(_file('app/pods/bar/baz/foo/x-foo/template.hbs')).to.equal('{{yield}}');
+
+            expect(_file('tests/integration/pods/bar/baz/foo/x-foo/component-test.ts')).to.equal(
+              fixture('component-test/default-template.ts', {
+                replace: {
+                  component: 'foo/x-foo',
+                  componentInvocation: 'Foo::XFoo',
+                  path: 'bar/baz/',
+                },
+              })
+            );
+          }
+        );
+      });
+
+      it('component x-foo --pod -no-path', function () {
+        return emberGenerateDestroy(['component', 'x-foo', '--pod', '-no-path'], (_file) => {
+          expect(_file('app/pods/x-foo/component.ts')).to.equal(
+            fixture('component/component-dash.ts')
+          );
+
+          expect(_file('app/pods/x-foo/template.hbs')).to.equal('{{yield}}');
+
+          expect(_file('tests/integration/pods/x-foo/component-test.ts')).to.equal(
+            fixture('component-test/default-template.ts', {
+              replace: {
+                component: 'x-foo',
+                componentInvocation: 'XFoo',
+              },
+            })
+          );
+        });
+      });
+
+      it('component foo/x-foo --pod -no-path', function () {
+        return emberGenerateDestroy(['component', 'foo/x-foo', '--pod', '-no-path'], (_file) => {
+          expect(_file('app/pods/foo/x-foo/component.ts')).to.equal(
+            fixture('component/component-nested.ts')
+          );
+
+          expect(_file('app/pods/foo/x-foo/template.hbs')).to.equal('{{yield}}');
+
+          expect(_file('tests/integration/pods/foo/x-foo/component-test.ts')).to.equal(
+            fixture('component-test/default-template.ts', {
+              replace: {
+                component: 'foo/x-foo',
+                componentInvocation: 'Foo::XFoo',
+              },
+            })
+          );
+        });
+      });
+    });
+  });
+
+  describe('in app - octane', function () {
+    enableOctane();
+
+    beforeEach(function () {
+      return emberNew()
+        .then(() =>
+          modifyPackages([
+            { name: 'ember-qunit', delete: true },
+            { name: 'ember-cli-qunit', dev: true },
+          ])
+        )
+        .then(() => generateFakePackageManifest('ember-cli-qunit', '4.1.0'));
+    });
+
+    it('component foo', function () {
+      return emberGenerateDestroy(['component', 'foo'], (_file) => {
+        expect(_file('app/components/foo.ts')).to.not.exist;
+        expect(_file('app/components/foo.hbs')).to.equal('{{yield}}');
+
+        expect(_file('tests/integration/components/foo-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'foo',
+              componentInvocation: 'Foo',
+            },
+          })
+        );
+      });
+    });
+
+    it('component x-foo', function () {
+      return emberGenerateDestroy(['component', 'x-foo'], (_file) => {
+        expect(_file('app/components/x-foo.ts')).to.not.exist;
+        expect(_file('app/components/x-foo.hbs')).to.equal('{{yield}}');
+
+        expect(_file('tests/integration/components/x-foo-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'x-foo',
+              componentInvocation: 'XFoo',
+            },
+          })
+        );
+      });
+    });
+
+    it('component x-foo.ts', function () {
+      return emberGenerateDestroy(['component', 'x-foo.ts'], (_file) => {
+        expect(_file('app/components/x-foo.ts')).to.not.exist;
+        expect(_file('app/components/x-foo.js.ts')).to.not.exist;
+        expect(_file('app/templates/components/x-foo.js.hbs')).to.not.exist;
+        expect(_file('tests/integration/components/x-foo-test.ts')).to.not.exist;
+
+        expect(_file('app/components/x-foo.hbs')).to.equal('{{yield}}');
+
+        expect(_file('tests/integration/components/x-foo-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'x-foo',
+              componentInvocation: 'XFoo',
+            },
+          })
+        );
+      });
+    });
+
+    it('component foo/x-foo', function () {
+      return emberGenerateDestroy(['component', 'foo/x-foo'], (_file) => {
+        expect(_file('app/components/foo/x-foo.ts')).to.not.exist;
+        expect(_file('app/components/foo/x-foo.hbs')).to.equal('{{yield}}');
+
+        expect(_file('tests/integration/components/foo/x-foo-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'foo/x-foo',
+              componentInvocation: 'Foo::XFoo',
+            },
+          })
+        );
+      });
+    });
+
+    it('component foo/x-foo --component-class="@glimmer/component"', function () {
+      return emberGenerateDestroy(
+        ['component', 'foo/x-foo', '--component-class', '@glimmer/component'],
+        (_file) => {
+          expect(_file('app/components/foo/x-foo.ts')).to.equal(
+            glimmerComponentContents.replace('FooComponent', 'FooXFooComponent')
+          );
+          expect(_file('app/components/foo/x-foo.hbs')).to.equal('{{yield}}');
+
+          expect(_file('tests/integration/components/foo/x-foo-test.ts')).to.equal(
+            fixture('component-test/default-template.ts', {
+              replace: {
+                component: 'foo/x-foo',
+                componentInvocation: 'Foo::XFoo',
+              },
+            })
+          );
+        }
+      );
+    });
+  });
+
+  describe('in addon', function () {
+    beforeEach(function () {
+      return emberNew({ target: 'addon' })
+        .then(() =>
+          modifyPackages([
+            { name: 'ember-qunit', delete: true },
+            { name: 'ember-cli-qunit', dev: true },
+          ])
+        )
+        .then(() => generateFakePackageManifest('ember-cli-qunit', '4.1.0'));
+    });
+
+    it('component foo', function () {
+      return emberGenerateDestroy(['component', 'foo'], (_file) => {
+        expect(_file('addon/components/foo.ts')).to.equal(fixture('component/component-addon.ts'));
+
+        expect(_file('addon/templates/components/foo.hbs')).to.equal('{{yield}}');
+
+        expect(_file('app/components/foo.ts')).to.contain(
+          "export { default } from 'my-addon/components/foo';"
+        );
+
+        expect(_file('tests/integration/components/foo-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'foo',
+              componentInvocation: 'Foo',
+            },
+          })
+        );
+      });
+    });
+
+    it('component x-foo', function () {
+      return emberGenerateDestroy(['component', 'x-foo'], (_file) => {
+        expect(_file('addon/components/x-foo.ts')).to.equal(
+          fixture('component/component-addon-dash.ts')
+        );
+
+        expect(_file('addon/templates/components/x-foo.hbs')).to.equal('{{yield}}');
+
+        expect(_file('app/components/x-foo.ts')).to.contain(
+          "export { default } from 'my-addon/components/x-foo';"
+        );
+
+        expect(_file('tests/integration/components/x-foo-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'x-foo',
+              componentInvocation: 'XFoo',
+            },
+          })
+        );
+      });
+    });
+
+    it('component x-foo.ts', function () {
+      return emberGenerateDestroy(['component', 'x-foo.ts'], (_file) => {
+        expect(_file('addon/components/x-foo.js.ts')).to.not.exist;
+        expect(_file('addon/templates/components/x-foo.js.hbs')).to.not.exist;
+        expect(_file('app/components/x-foo.js.ts')).to.not.exist;
+        expect(_file('tests/integration/components/x-foo.js-test.ts')).to.not.exist;
+
+        expect(_file('addon/components/x-foo.ts')).to.equal(
+          fixture('component/component-addon-dash.ts')
+        );
+
+        expect(_file('addon/templates/components/x-foo.hbs')).to.equal('{{yield}}');
+
+        expect(_file('app/components/x-foo.ts')).to.contain(
+          "export { default } from 'my-addon/components/x-foo';"
+        );
+
+        expect(_file('tests/integration/components/x-foo-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'x-foo',
+              componentInvocation: 'XFoo',
+            },
+          })
+        );
+      });
+    });
+
+    it('component foo/x-foo', function () {
+      return emberGenerateDestroy(['component', 'foo/x-foo'], (_file) => {
+        expect(_file('addon/components/foo/x-foo.ts')).to.equal(
+          fixture('component/component-addon-nested.ts')
+        );
+
+        expect(_file('addon/templates/components/foo/x-foo.hbs')).to.equal('{{yield}}');
+
+        expect(_file('app/components/foo/x-foo.ts')).to.contain(
+          "export { default } from 'my-addon/components/foo/x-foo';"
+        );
+
+        expect(_file('tests/integration/components/foo/x-foo-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'foo/x-foo',
+              componentInvocation: 'Foo::XFoo',
+            },
+          })
+        );
+      });
+    });
+
+    it('component x-foo --dummy', function () {
+      return emberGenerateDestroy(['component', 'x-foo', '--dummy'], (_file) => {
+        expect(_file('tests/dummy/app/components/x-foo.ts')).to.equal(
+          fixture('component/component-addon-dash.ts')
+        );
+
+        expect(_file('tests/dummy/app/templates/components/x-foo.hbs')).to.equal('{{yield}}');
+
+        expect(_file('app/components/x-foo.ts')).to.not.exist;
+
+        expect(_file('tests/unit/components/x-foo-test.ts')).to.not.exist;
+      });
+    });
+
+    it('component foo/x-foo --dummy', function () {
+      return emberGenerateDestroy(['component', 'foo/x-foo', '--dummy'], (_file) => {
+        expect(_file('tests/dummy/app/components/foo/x-foo.ts')).to.equal(
+          fixture('component/component-addon-nested.ts')
+        );
+
+        expect(_file('tests/dummy/app/templates/components/foo/x-foo.hbs')).to.equal('{{yield}}');
+
+        expect(_file('app/components/foo/x-foo.ts')).to.not.exist;
+
+        expect(_file('tests/unit/components/foo/x-foo-test.ts')).to.not.exist;
+      });
+    });
+
+    it('component x-foo.js --dummy', function () {
+      return emberGenerateDestroy(['component', 'x-foo.ts', '--dummy'], (_file) => {
+        expect(_file('tests/dummy/app/components/x-foo.js.ts')).to.not.exist;
+        expect(_file('tests/dummy/app/templates/components/x-foo.js.hbs')).to.not.exist;
+        expect(_file('app/components/x-foo.js.ts')).to.not.exist;
+        expect(_file('tests/unit/components/x-foo.js-test.ts')).to.not.exist;
+
+        expect(_file('tests/dummy/app/components/x-foo.ts')).to.equal(
+          fixture('component/component-addon-dash.ts')
+        );
+
+        expect(_file('tests/dummy/app/templates/components/x-foo.hbs')).to.equal('{{yield}}');
+
+        expect(_file('app/components/x-foo.ts')).to.not.exist;
+
+        expect(_file('tests/unit/components/x-foo-test.ts')).to.not.exist;
+      });
+    });
+
+    it('component x-foo --pod', function () {
+      return emberGenerateDestroy(['component', 'x-foo', '--pod'], (_file) => {
+        expect(_file('addon/components/x-foo/component.ts')).to.equal(
+          fixture('component/component-addon-dash-pod.ts')
+        );
+
+        expect(_file('addon/components/x-foo/template.hbs')).to.equal('{{yield}}');
+
+        expect(_file('app/components/x-foo/component.ts')).to.contain(
+          "export { default } from 'my-addon/components/x-foo/component';"
+        );
+
+        expect(_file('tests/integration/components/x-foo/component-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'x-foo',
+              componentInvocation: 'XFoo',
+            },
+          })
+        );
+      });
+    });
+  });
+
+  describe('in addon - octane', function () {
+    enableOctane();
+
+    beforeEach(function () {
+      return emberNew({ target: 'addon' })
+        .then(() =>
+          modifyPackages([
+            { name: 'ember-qunit', delete: true },
+            { name: 'ember-cli-qunit', dev: true },
+          ])
+        )
+        .then(() => generateFakePackageManifest('ember-cli-qunit', '4.1.0'));
+    });
+
+    it('component foo', function () {
+      return emberGenerateDestroy(['component', 'foo'], (_file) => {
+        expect(_file('addon/components/foo.ts')).to.not.exist;
+
+        expect(_file('addon/components/foo.hbs')).to.equal('{{yield}}');
+
+        expect(_file('app/components/foo.ts')).to.contain(
+          "export { default } from 'my-addon/components/foo';"
+        );
+
+        expect(_file('app/templates/components/foo.ts')).to.not.exist;
+
+        expect(_file('tests/integration/components/foo-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'foo',
+              componentInvocation: 'Foo',
+            },
+          })
+        );
+      });
+    });
+
+    it('component x-foo', function () {
+      return emberGenerateDestroy(['component', 'x-foo'], (_file) => {
+        expect(_file('addon/components/x-foo.ts')).to.not.exist;
+
+        expect(_file('addon/components/x-foo.hbs')).to.equal('{{yield}}');
+
+        expect(_file('app/components/x-foo.ts')).to.contain(
+          "export { default } from 'my-addon/components/x-foo';"
+        );
+
+        expect(_file('app/templates/components/x-foo.ts')).to.not.exist;
+        expect(_file('app/components/x-foo.hbs')).to.not.exist;
+
+        expect(_file('tests/integration/components/x-foo-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'x-foo',
+              componentInvocation: 'XFoo',
+            },
+          })
+        );
+      });
+    });
+
+    it('component foo/x-foo', function () {
+      return emberGenerateDestroy(['component', 'foo/x-foo'], (_file) => {
+        expect(_file('addon/components/foo/x-foo.ts')).to.not.exist;
+
+        expect(_file('addon/components/foo/x-foo.hbs')).to.equal('{{yield}}');
+
+        expect(_file('app/components/foo/x-foo.ts')).to.contain(
+          "export { default } from 'my-addon/components/foo/x-foo';"
+        );
+
+        expect(_file('app/templates/components/foo/x-foo.ts')).to.not.exist;
+
+        expect(_file('tests/integration/components/foo/x-foo-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'foo/x-foo',
+              componentInvocation: 'Foo::XFoo',
+            },
+          })
+        );
+      });
+    });
+
+    it('component x-foo --dummy', function () {
+      return emberGenerateDestroy(['component', 'x-foo', '--dummy'], (_file) => {
+        expect(_file('tests/dummy/app/components/x-foo.ts')).to.not.exist;
+
+        expect(_file('tests/dummy/app/components/x-foo.hbs')).to.equal('{{yield}}');
+
+        expect(_file('app/components/x-foo.ts')).to.not.exist;
+        expect(_file('app/components/x-foo.hbs')).to.not.exist;
+        expect(_file('app/templates/components/x-foo.ts')).to.not.exist;
+
+        expect(_file('tests/integration/components/x-foo-test.ts')).to.not.exist;
+      });
+    });
+
+    it('component foo/x-foo --dummy', function () {
+      return emberGenerateDestroy(['component', 'foo/x-foo', '--dummy'], (_file) => {
+        expect(_file('tests/dummy/app/components/foo/x-foo.ts')).to.not.exist;
+
+        expect(_file('tests/dummy/app/components/foo/x-foo.hbs')).to.equal('{{yield}}');
+        expect(_file('tests/dummy/app/templates/components/foo/x-foo.hbs')).to.not.exist;
+
+        expect(_file('app/components/foo/x-foo.ts')).to.not.exist;
+        expect(_file('app/components/foo/x-foo.hbs')).to.not.exist;
+        expect(_file('app/templates/components/foo/x-foo.ts')).to.not.exist;
+
+        expect(_file('tests/integration/components/foo/x-foo-test.ts')).to.not.exist;
+      });
+    });
+  });
+
+  describe('in in-repo-addon', function () {
+    beforeEach(function () {
+      return emberNew({ target: 'in-repo-addon' })
+        .then(() =>
+          modifyPackages([
+            { name: 'ember-qunit', delete: true },
+            { name: 'ember-cli-qunit', dev: true },
+          ])
+        )
+        .then(() => generateFakePackageManifest('ember-cli-qunit', '4.1.0'));
+    });
+
+    it('component foo --in-repo-addon=my-addon', function () {
+      return emberGenerateDestroy(['component', 'foo', '--in-repo-addon=my-addon'], (_file) => {
+        expect(_file('lib/my-addon/addon/components/foo.ts')).to.equal(
+          fixture('component/component-addon.ts')
+        );
+
+        expect(_file('lib/my-addon/addon/templates/components/foo.hbs')).to.equal('{{yield}}');
+
+        expect(_file('lib/my-addon/app/components/foo.ts')).to.contain(
+          "export { default } from 'my-addon/components/foo';"
+        );
+
+        expect(_file('tests/integration/components/foo-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'foo',
+              componentInvocation: 'Foo',
+            },
+          })
+        );
+      });
+    });
+
+    it('component x-foo --in-repo-addon=my-addon', function () {
+      return emberGenerateDestroy(['component', 'x-foo', '--in-repo-addon=my-addon'], (_file) => {
+        expect(_file('lib/my-addon/addon/components/x-foo.ts')).to.equal(
+          fixture('component/component-addon-dash.ts')
+        );
+
+        expect(_file('lib/my-addon/addon/templates/components/x-foo.hbs')).to.equal('{{yield}}');
+
+        expect(_file('lib/my-addon/app/components/x-foo.ts')).to.contain(
+          "export { default } from 'my-addon/components/x-foo';"
+        );
+
+        expect(_file('tests/integration/components/x-foo-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'x-foo',
+              componentInvocation: 'XFoo',
+            },
+          })
+        );
+      });
+    });
+
+    it('component x-foo.js --in-repo-addon=my-addon', function () {
+      return emberGenerateDestroy(
+        ['component', 'x-foo.ts', '--in-repo-addon=my-addon'],
+        (_file) => {
+          expect(_file('lib/my-addon/addon/components/x-foo.js.ts')).to.not.exist;
+          expect(_file('lib/my-addon/addon/templates/components/x-foo.js.hbs')).to.not.exist;
+          expect(_file('lib/my-addon/app/components/x-foo.js.ts')).to.not.exist;
+          expect(_file('tests/integration/components/x-foo-test.ts')).to.not.exist;
+
+          expect(_file('lib/my-addon/addon/components/x-foo.ts')).to.equal(
+            fixture('component/component-addon-dash.ts')
+          );
+
+          expect(_file('lib/my-addon/addon/templates/components/x-foo.hbs')).to.equal('{{yield}}');
+
+          expect(_file('lib/my-addon/app/components/x-foo.ts')).to.contain(
+            "export { default } from 'my-addon/components/x-foo';"
+          );
+
+          expect(_file('tests/integration/components/x-foo-test.ts')).to.equal(
+            fixture('component-test/default-template.ts', {
+              replace: {
+                component: 'x-foo',
+                componentInvocation: 'XFoo',
+              },
+            })
+          );
+        }
+      );
+    });
+
+    it('component foo/x-foo --in-repo-addon=my-addon', function () {
+      return emberGenerateDestroy(
+        ['component', 'foo/x-foo', '--in-repo-addon=my-addon'],
+        (_file) => {
+          expect(_file('lib/my-addon/addon/components/foo/x-foo.ts')).to.equal(
+            fixture('component/component-addon-nested.ts')
+          );
+
+          expect(_file('lib/my-addon/addon/templates/components/foo/x-foo.hbs')).to.equal(
+            '{{yield}}'
+          );
+
+          expect(_file('lib/my-addon/app/components/foo/x-foo.ts')).to.contain(
+            "export { default } from 'my-addon/components/foo/x-foo';"
+          );
+
+          expect(_file('tests/integration/components/foo/x-foo-test.ts')).to.equal(
+            fixture('component-test/default-template.ts', {
+              replace: {
+                component: 'foo/x-foo',
+                componentInvocation: 'Foo::XFoo',
+              },
+            })
+          );
+        }
+      );
+    });
+
+    it('component x-foo --in-repo-addon=my-addon --pod', function () {
+      return emberGenerateDestroy(
+        ['component', 'x-foo', '--in-repo-addon=my-addon', '--pod'],
+        (_file) => {
+          expect(_file('lib/my-addon/addon/components/x-foo/component.ts')).to.equal(
+            fixture('component/component-addon-dash-pod.ts')
+          );
+
+          expect(_file('lib/my-addon/addon/components/x-foo/template.hbs')).to.equal('{{yield}}');
+
+          expect(_file('lib/my-addon/app/components/x-foo/component.ts')).to.contain(
+            "export { default } from 'my-addon/components/x-foo/component';"
+          );
+
+          expect(_file('tests/integration/components/x-foo/component-test.ts')).to.equal(
+            fixture('component-test/default-template.ts', {
+              replace: {
+                component: 'x-foo',
+                componentInvocation: 'XFoo',
+              },
+            })
+          );
+        }
+      );
+    });
+
+    it('component x-foo.js --in-repo-addon=my-addon --pod', function () {
+      return emberGenerateDestroy(
+        ['component', 'x-foo.ts', '--in-repo-addon=my-addon', '--pod'],
+        (_file) => {
+          expect(_file('lib/my-addon/addon/components/x-foo/component.js.ts')).to.not.exist;
+          expect(_file('lib/my-addon/addon/components/x-foo/template.js.hbs')).to.not.exist;
+          expect(_file('lib/my-addon/app/components/x-foo/component.js.ts')).to.not.exist;
+          expect(_file('tests/integration/components/x-foo/component-test.ts')).to.not.exist;
+
+          expect(_file('lib/my-addon/addon/components/x-foo/component.ts')).to.equal(
+            fixture('component/component-addon-dash-pod.ts')
+          );
+
+          expect(_file('lib/my-addon/addon/components/x-foo/template.hbs')).to.equal('{{yield}}');
+
+          expect(_file('lib/my-addon/app/components/x-foo/component.ts')).to.contain(
+            "export { default } from 'my-addon/components/x-foo/component';"
+          );
+
+          expect(_file('tests/integration/components/x-foo/component-test.ts')).to.equal(
+            fixture('component-test/default-template.ts', {
+              replace: {
+                component: 'x-foo',
+                componentInvocation: 'XFoo',
+              },
+            })
+          );
+        }
+      );
+    });
+
+    it('component foo/x-foo --in-repo-addon=my-addon --pod', function () {
+      return emberGenerateDestroy(
+        ['component', 'foo/x-foo', '--in-repo-addon=my-addon', '--pod'],
+        (_file) => {
+          expect(_file('lib/my-addon/addon/components/foo/x-foo/component.ts')).to.equal(
+            fixture('component/component-addon-nested-pod.ts')
+          );
+
+          expect(_file('lib/my-addon/addon/components/foo/x-foo/template.hbs')).to.equal(
+            '{{yield}}'
+          );
+
+          expect(_file('lib/my-addon/app/components/foo/x-foo/component.ts')).to.contain(
+            "export { default } from 'my-addon/components/foo/x-foo/component';"
+          );
+
+          expect(_file('tests/integration/components/foo/x-foo/component-test.ts')).to.equal(
+            fixture('component-test/default-template.ts', {
+              replace: {
+                component: 'foo/x-foo',
+                componentInvocation: 'Foo::XFoo',
+              },
+            })
+          );
+        }
+      );
+    });
+  });
+
+  describe('in in-repo-addon - octane', function () {
+    enableOctane();
+
+    beforeEach(function () {
+      return emberNew({ target: 'in-repo-addon' })
+        .then(() =>
+          modifyPackages([
+            { name: 'ember-qunit', delete: true },
+            { name: 'ember-cli-qunit', dev: true },
+          ])
+        )
+        .then(() => generateFakePackageManifest('ember-cli-qunit', '4.1.0'));
+    });
+
+    it('component foo --in-repo-addon=my-addon', function () {
+      return emberGenerateDestroy(['component', 'foo', '--in-repo-addon=my-addon'], (_file) => {
+        expect(_file('lib/my-addon/addon/components/foo.ts')).to.not.exist;
+        expect(_file('lib/my-addon/addon/components/foo.hbs')).to.equal('{{yield}}');
+        expect(_file('lib/my-addon/addon/templates/components/foo.hbs')).to.not.exist;
+
+        expect(_file('lib/my-addon/app/components/foo.ts')).to.contain(
+          "export { default } from 'my-addon/components/foo';"
+        );
+
+        expect(_file('lib/my-addon/app/templates/components/foo.ts')).to.not.exist;
+        expect(_file('lib/my-addon/app/components/foo.hbs')).to.not.exist;
+
+        expect(_file('tests/integration/components/foo-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'foo',
+              componentInvocation: 'Foo',
+            },
+          })
+        );
+      });
+    });
+
+    it('component x-foo --in-repo-addon=my-addon', function () {
+      return emberGenerateDestroy(['component', 'x-foo', '--in-repo-addon=my-addon'], (_file) => {
+        expect(_file('lib/my-addon/addon/components/x-foo.ts')).to.not.exist;
+        expect(_file('lib/my-addon/addon/components/x-foo.hbs')).to.equal('{{yield}}');
+        expect(_file('lib/my-addon/addon/templates/components/x-foo.hbs')).to.not.exist;
+
+        expect(_file('lib/my-addon/app/components/x-foo.ts')).to.contain(
+          "export { default } from 'my-addon/components/x-foo';"
+        );
+
+        expect(_file('lib/my-addon/app/templates/components/x-foo.ts')).to.not.exist;
+        expect(_file('lib/my-addon/app/components/x-foo.hbs')).to.not.exist;
+
+        expect(_file('tests/integration/components/x-foo-test.ts')).to.equal(
+          fixture('component-test/default-template.ts', {
+            replace: {
+              component: 'x-foo',
+              componentInvocation: 'XFoo',
+            },
+          })
+        );
+      });
+    });
+  });
+});
diff --git a/node-tests/fixtures/component-test/default-template.ts b/node-tests/fixtures/component-test/default-template.ts
new file mode 100644
index 00000000..72df2c7f
--- /dev/null
+++ b/node-tests/fixtures/component-test/default-template.ts
@@ -0,0 +1,24 @@
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+
+moduleForComponent('<%= path =%><%= component =%>', 'Integration | Component | <%= component =%>', {
+  integration: true
+});
+
+test('it renders', function(assert) {
+  // Set any properties with this.set('myProperty', 'value');
+  // Handle any actions with this.on('myAction', function(val) { ... });
+
+  this.render(hbs`<<%= componentInvocation =%> />`);
+
+  assert.equal(this.$().text().trim(), '');
+
+  // Template block usage:
+  this.render(hbs`
+    <<%= componentInvocation =%>>
+      template block text
+    <%= componentInvocation =%>>
+  `);
+
+  assert.equal(this.$().text().trim(), 'template block text');
+});
diff --git a/node-tests/fixtures/component-test/default.ts b/node-tests/fixtures/component-test/default.ts
new file mode 100644
index 00000000..fc4c11ce
--- /dev/null
+++ b/node-tests/fixtures/component-test/default.ts
@@ -0,0 +1,24 @@
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+
+moduleForComponent('x-foo', 'Integration | Component | x-foo', {
+  integration: true
+});
+
+test('it renders', function(assert) {
+  // Set any properties with this.set('myProperty', 'value');
+  // Handle any actions with this.on('myAction', function(val) { ... });
+
+  this.render(hbs``);
+
+  assert.equal(this.$().text().trim(), '');
+
+  // Template block usage:
+  this.render(hbs`
+    
+      template block text
+    
+  `);
+
+  assert.equal(this.$().text().trim(), 'template block text');
+});
diff --git a/node-tests/fixtures/component-test/mocha.ts b/node-tests/fixtures/component-test/mocha.ts
new file mode 100644
index 00000000..87c62720
--- /dev/null
+++ b/node-tests/fixtures/component-test/mocha.ts
@@ -0,0 +1,27 @@
+import { expect } from 'chai';
+import { describe, it } from 'mocha';
+import { setupRenderingTest } from 'ember-mocha';
+import { render } from '@ember/test-helpers';
+import { hbs } from 'ember-cli-htmlbars';
+
+describe('x-foo', 'Integration | Component | x-foo', function() {
+  setupRenderingTest();
+
+  it('renders', async function() {
+    // Set any properties with this.set('myProperty', 'value');
+    // Handle any actions with this.set('myAction', function(val) { ... });
+
+    await render(hbs``);
+
+    expect(this.element.textContent.trim()).to.equal('');
+
+    // Template block usage:
+    await render(hbs`
+      
+        template block text
+      
+    `);
+
+    expect(this.element.textContent.trim()).to.equal('template block text');
+  });
+});
diff --git a/node-tests/fixtures/component-test/qunit.ts b/node-tests/fixtures/component-test/qunit.ts
new file mode 100644
index 00000000..dbebc793
--- /dev/null
+++ b/node-tests/fixtures/component-test/qunit.ts
@@ -0,0 +1,26 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import { render } from '@ember/test-helpers';
+import { hbs } from 'ember-cli-htmlbars';
+
+module('Integration | Component | x-foo', function(hooks) {
+  setupRenderingTest(hooks);
+
+  test('it renders', async function(assert) {
+    // Set any properties with this.set('myProperty', 'value');
+    // Handle any actions with this.set('myAction', function(val) { ... });
+
+    await render(hbs``);
+
+    assert.equal(this.element.textContent.trim(), '');
+
+    // Template block usage:
+    await render(hbs`
+      
+        template block text
+      
+    `);
+
+    assert.equal(this.element.textContent.trim(), 'template block text');
+  });
+});
diff --git a/node-tests/fixtures/component/component-addon-dash-pod.ts b/node-tests/fixtures/component/component-addon-dash-pod.ts
new file mode 100644
index 00000000..6dfbdd55
--- /dev/null
+++ b/node-tests/fixtures/component/component-addon-dash-pod.ts
@@ -0,0 +1,7 @@
+import Component from '@ember/component';
+// @ts-ignore: Ignore import of compiled template
+import layout from './template';
+
+export default class XFoo extends Component {
+  layout = layout;
+};
diff --git a/node-tests/fixtures/component/component-addon-dash.ts b/node-tests/fixtures/component/component-addon-dash.ts
new file mode 100644
index 00000000..7fd9a3b5
--- /dev/null
+++ b/node-tests/fixtures/component/component-addon-dash.ts
@@ -0,0 +1,7 @@
+import Component from '@ember/component';
+// @ts-ignore: Ignore import of compiled template
+import layout from '../templates/components/x-foo';
+
+export default class XFoo extends Component {
+  layout = layout;
+};
diff --git a/node-tests/fixtures/component/component-addon-nested-pod.ts b/node-tests/fixtures/component/component-addon-nested-pod.ts
new file mode 100644
index 00000000..b84869b7
--- /dev/null
+++ b/node-tests/fixtures/component/component-addon-nested-pod.ts
@@ -0,0 +1,10 @@
+import Component from '@ember/component';
+// @ts-ignore: Ignore import of compiled template
+import layout from './template';
+
+export default class FooXFoo extends Component.extend({
+  // anything which *must* be merged to prototype here
+}) {
+  layout = layout;
+  // normal class body definition here
+};
diff --git a/node-tests/fixtures/component/component-addon-nested.ts b/node-tests/fixtures/component/component-addon-nested.ts
new file mode 100644
index 00000000..b8d2c8a9
--- /dev/null
+++ b/node-tests/fixtures/component/component-addon-nested.ts
@@ -0,0 +1,7 @@
+import Component from '@ember/component';
+// @ts-ignore: Ignore import of compiled template
+import layout from '../../templates/components/foo/x-foo';
+
+export default class FooXFoo extends Component {
+  layout = layout;
+}
diff --git a/node-tests/fixtures/component/component-addon.ts b/node-tests/fixtures/component/component-addon.ts
new file mode 100644
index 00000000..8cc7412f
--- /dev/null
+++ b/node-tests/fixtures/component/component-addon.ts
@@ -0,0 +1,7 @@
+import Component from '@ember/component';
+// @ts-ignore: Ignore import of compiled template
+import layout from '../templates/components/foo';
+
+export default class Foo extends Component {
+  layout = layout;
+}
diff --git a/node-tests/fixtures/component/component-dash.ts b/node-tests/fixtures/component/component-dash.ts
new file mode 100644
index 00000000..c978f988
--- /dev/null
+++ b/node-tests/fixtures/component/component-dash.ts
@@ -0,0 +1,4 @@
+import Component from '@ember/component';
+
+export default class XFoo extends Component {
+}
diff --git a/node-tests/fixtures/component/component-nested.ts b/node-tests/fixtures/component/component-nested.ts
new file mode 100644
index 00000000..760840ce
--- /dev/null
+++ b/node-tests/fixtures/component/component-nested.ts
@@ -0,0 +1,4 @@
+import Component from '@ember/component';
+
+export default class FooXFoo extends Component {
+}
diff --git a/node-tests/fixtures/component/component.ts b/node-tests/fixtures/component/component.ts
new file mode 100644
index 00000000..9ffdf650
--- /dev/null
+++ b/node-tests/fixtures/component/component.ts
@@ -0,0 +1,4 @@
+import Component from '@ember/component';
+
+export default class Foo extends Component {
+};
diff --git a/node-tests/fixtures/component/glimmer-component-dash.ts b/node-tests/fixtures/component/glimmer-component-dash.ts
new file mode 100644
index 00000000..312a6b5f
--- /dev/null
+++ b/node-tests/fixtures/component/glimmer-component-dash.ts
@@ -0,0 +1,7 @@
+import Component from '@glimmer/component';
+
+interface XFooArgs {}
+
+export default class XFoo extends Component {
+  
+}
diff --git a/node-tests/fixtures/component/glimmer-component-nested.ts b/node-tests/fixtures/component/glimmer-component-nested.ts
new file mode 100644
index 00000000..1bd26c76
--- /dev/null
+++ b/node-tests/fixtures/component/glimmer-component-nested.ts
@@ -0,0 +1,5 @@
+import Component from '@glimmer/component';
+
+interface FooXFooArgs {}
+
+export default class FooXFoo extends Component {}
diff --git a/node-tests/fixtures/component/glimmer-component.ts b/node-tests/fixtures/component/glimmer-component.ts
new file mode 100644
index 00000000..b77f74d9
--- /dev/null
+++ b/node-tests/fixtures/component/glimmer-component.ts
@@ -0,0 +1,5 @@
+import Component from '@glimmer/component';
+
+interface FooArgs {}
+
+export default class Foo extends Component {}
diff --git a/node-tests/fixtures/helper-test/integration.ts b/node-tests/fixtures/helper-test/integration.ts
index 8ce8201b..f9b2db03 100644
--- a/node-tests/fixtures/helper-test/integration.ts
+++ b/node-tests/fixtures/helper-test/integration.ts
@@ -1,7 +1,7 @@
 import { module, test } from 'qunit';
 import { setupRenderingTest } from 'ember-qunit';
 import { render } from '@ember/test-helpers';
-import hbs from 'htmlbars-inline-precompile';
+import { hbs } from 'ember-cli-htmlbars';
 
 module('Integration | Helper | foo/bar-baz', function(hooks) {
   setupRenderingTest(hooks);
diff --git a/node-tests/fixtures/helper-test/mocha.ts b/node-tests/fixtures/helper-test/mocha.ts
index feca3336..ec2202a1 100644
--- a/node-tests/fixtures/helper-test/mocha.ts
+++ b/node-tests/fixtures/helper-test/mocha.ts
@@ -1,7 +1,7 @@
 import { expect } from 'chai';
 import { describe, it } from 'mocha';
 import { setupComponentTest } from 'ember-mocha';
-import hbs from 'htmlbars-inline-precompile';
+import { hbs } from 'ember-cli-htmlbars';
 
 describe('Integration | Helper | foo/bar-baz', function() {
   setupComponentTest('foo/bar-baz', {
@@ -21,6 +21,6 @@ describe('Integration | Helper | foo/bar-baz', function() {
 
     this.render(hbs`{{foo/bar-baz inputValue}}`);
 
-    expect(this.$().text().trim()).to.equal('1234');
+    expect(this.element.text().trim()).to.equal('1234');
   });
 });
diff --git a/node-tests/helpers/expect-error.js b/node-tests/helpers/expect-error.js
index c96aab1e..e9eda641 100644
--- a/node-tests/helpers/expect-error.js
+++ b/node-tests/helpers/expect-error.js
@@ -8,7 +8,7 @@ module.exports = function expectError(promise, expectedErrorText) {
     .then(() => {
       throw new Error('the command should raise an exception');
     })
-    .catch(error => {
-      expect(error.message || error).to.equal(expectedErrorText);
+    .catch((error) => {
+      expect(error.message).to.equal(expectedErrorText);
     });
 };
diff --git a/node-tests/helpers/fixture.js b/node-tests/helpers/fixture.js
index 4b9c83b1..b55cde25 100644
--- a/node-tests/helpers/fixture.js
+++ b/node-tests/helpers/fixture.js
@@ -4,12 +4,14 @@ const path = require('path');
 const file = require('ember-cli-blueprint-test-helpers/chai').file;
 const fs = require('fs');
 
-module.exports = function(filePath, options) {
+module.exports = function (filePath, options) {
   if (!options) {
     return file(path.join(__dirname, '../fixtures', filePath));
   }
 
-  let content = fs.readFileSync(path.join(__dirname, '../fixtures', filePath), { encoding: 'utf-8' });
+  let content = fs.readFileSync(path.join(__dirname, '../fixtures', filePath), {
+    encoding: 'utf-8',
+  });
   if (options.replace) {
     content = content.replace(/<%= (\w+) =%>/g, (_match, key) => options.replace[key] || '');
   }
diff --git a/node-tests/helpers/generate-fake-package-manifest.js b/node-tests/helpers/generate-fake-package-manifest.js
index 50a3c664..ad521a48 100644
--- a/node-tests/helpers/generate-fake-package-manifest.js
+++ b/node-tests/helpers/generate-fake-package-manifest.js
@@ -1,22 +1,16 @@
-// @ts-check
-const { writeFileSync } = require('fs');
-const { ensureDirSync } = require('fs-extra');
-const path = require('path');
+const fs = require('fs');
 
-/**
- * Create fake package manifests on the file system to use in ensuring that the
- * blueprint generator is well-behaved with different test environments (e.g.
- * qunit and mocha).
- *
- * @param {string} name
- * @param {string} version
- */
-function generateFakePackageManifest(name, version) {
-  const targetDir = path.join('node_modules', name);
-  ensureDirSync(targetDir);
-
-  const pkgFilePath = path.join(targetDir, 'package.json');
-  writeFileSync(pkgFilePath, JSON.stringify({ version }));
-}
-
-module.exports = generateFakePackageManifest;
+module.exports = function generateFakePackageManifest(name, version) {
+  if (!fs.existsSync('node_modules')) {
+    fs.mkdirSync('node_modules');
+  }
+  if (!fs.existsSync('node_modules/' + name)) {
+    fs.mkdirSync('node_modules/' + name);
+  }
+  fs.writeFileSync(
+    'node_modules/' + name + '/package.json',
+    JSON.stringify({
+      version: version,
+    })
+  );
+};
diff --git a/node-tests/helpers/setup-test-environment.js b/node-tests/helpers/setup-test-environment.js
new file mode 100644
index 00000000..25d9a60d
--- /dev/null
+++ b/node-tests/helpers/setup-test-environment.js
@@ -0,0 +1,15 @@
+const { setEdition, clearEdition } = require('@ember/edition-utils');
+
+function enableOctane() {
+  beforeEach(function () {
+    setEdition('octane');
+  });
+
+  afterEach(function () {
+    clearEdition();
+  });
+}
+
+module.exports = {
+  enableOctane,
+};
diff --git a/package.json b/package.json
index 4cbd1d5c..c30a8f5c 100644
--- a/package.json
+++ b/package.json
@@ -35,6 +35,7 @@
     "silent-error": "^1.1.0"
   },
   "devDependencies": {
+    "@babel/core": "^7.12.3",
     "@typed-ember/renovate-config": "1.2.1",
     "broccoli-asset-rev": "3.0.0",
     "ember-ajax": "5.0.0",
@@ -44,7 +45,6 @@
     "ember-cli-eslint": "5.1.0",
     "ember-cli-htmlbars": "5.3.1",
     "ember-cli-inject-live-reload": "2.0.2",
-    "ember-qunit": "4.6.0",
     "ember-cli-shims": "1.2.0",
     "ember-cli-sri": "2.1.1",
     "ember-cli-uglify": "3.0.0",
@@ -52,10 +52,12 @@
     "ember-export-application-global": "2.0.1",
     "ember-load-initializers": "2.1.2",
     "ember-maybe-import-regenerator": "0.1.6",
+    "ember-qunit": "4.6.0",
     "ember-resolver": "8.0.2",
     "ember-source": "3.23.0",
     "ember-source-channel-url": "3.0.0",
     "ember-try": "1.4.0",
+    "eslint": "^7.13.0",
     "eslint-plugin-ember": "9.6.0",
     "eslint-plugin-node": "11.1.0",
     "loader.js": "4.7.0",
diff --git a/yarn.lock b/yarn.lock
index 65c5c3fa..b3e5a9c6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -23,7 +23,7 @@
     invariant "^2.2.4"
     semver "^5.5.0"
 
-"@babel/core@^7.11.6", "@babel/core@^7.12.0", "@babel/core@^7.3.4", "@babel/core@^7.8.4":
+"@babel/core@^7.11.6", "@babel/core@^7.12.0", "@babel/core@^7.12.3", "@babel/core@^7.3.4", "@babel/core@^7.8.4":
   version "7.12.3"
   resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.3.tgz#1b436884e1e3bff6fb1328dc02b208759de92ad8"
   integrity sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==
@@ -1472,6 +1472,22 @@
     ember-cli-htmlbars-inline-precompile "^2.1.0"
     ember-test-waiters "^1.1.1"
 
+"@eslint/eslintrc@^0.2.1":
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.2.1.tgz#f72069c330461a06684d119384435e12a5d76e3c"
+  integrity sha512-XRUeBZ5zBWLYgSANMpThFddrZZkEbGHgUdt5UJjZfnlN9BGCiUBrf+nvbRupSjMvqzwnQN0qwCmOxITt1cfywA==
+  dependencies:
+    ajv "^6.12.4"
+    debug "^4.1.1"
+    espree "^7.3.0"
+    globals "^12.1.0"
+    ignore "^4.0.6"
+    import-fresh "^3.2.1"
+    js-yaml "^3.13.1"
+    lodash "^4.17.19"
+    minimatch "^3.0.4"
+    strip-json-comments "^3.1.1"
+
 "@nodelib/fs.scandir@2.1.3":
   version "2.1.3"
   resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b"
@@ -1640,16 +1656,36 @@ acorn-jsx@^5.0.0:
   resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.0.tgz#958584ddb60990c02c97c1bd9d521fce433bb101"
   integrity sha512-XkB50fn0MURDyww9+UYL3c1yLbOBz0ZFvrdYlGB8l+Ije1oSC75qAqrzSPjYQbdnQUzhlUGNKuesryAv0gxZOg==
 
+acorn-jsx@^5.2.0:
+  version "5.3.1"
+  resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b"
+  integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==
+
 acorn@^6.0.2:
   version "6.4.2"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6"
   integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==
 
+acorn@^7.4.0:
+  version "7.4.1"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
+  integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
+
 after@0.8.2:
   version "0.8.2"
   resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f"
   integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=
 
+ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.4:
+  version "6.12.6"
+  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
+  integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
+  dependencies:
+    fast-deep-equal "^3.1.1"
+    fast-json-stable-stringify "^2.0.0"
+    json-schema-traverse "^0.4.1"
+    uri-js "^4.2.2"
+
 ajv@^6.5.3:
   version "6.5.5"
   resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.5.tgz#cf97cdade71c6399a92c6d6c4177381291b781a1"
@@ -1680,7 +1716,7 @@ amdefine@>=0.0.4:
   resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
   integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=
 
-ansi-colors@4.1.1:
+ansi-colors@4.1.1, ansi-colors@^4.1.1:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
   integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
@@ -1710,6 +1746,11 @@ ansi-regex@^4.1.0:
   resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
   integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
 
+ansi-regex@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
+  integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==
+
 ansi-styles@^2.2.1:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
@@ -1868,6 +1909,11 @@ ast-types@0.13.1:
   resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.1.tgz#9461428a270c5a27fda44b738dd3bab2e9353003"
   integrity sha512-b+EeK0WlzrSmpMw5jktWvQGxblpWnvMrV+vOp69RLjzGiHwWV0vgq75DPKtUjppKni3yWwSW8WLGV3Ch/XIWcQ==
 
+astral-regex@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
+  integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==
+
 async-disk-cache@^1.2.1:
   version "1.3.3"
   resolved "https://registry.yarnpkg.com/async-disk-cache/-/async-disk-cache-1.3.3.tgz#6040486660b370e4051cd9fa9fee275e1fae3728"
@@ -3372,7 +3418,7 @@ callsites@^0.2.0:
   resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca"
   integrity sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=
 
-callsites@^3.1.0:
+callsites@^3.0.0, callsites@^3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
   integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
@@ -3894,7 +3940,7 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5:
     shebang-command "^1.2.0"
     which "^1.2.9"
 
-cross-spawn@^7.0.0:
+cross-spawn@^7.0.0, cross-spawn@^7.0.2:
   version "7.0.3"
   resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
   integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
@@ -3997,7 +4043,7 @@ deep-extend@^0.6.0:
   resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
   integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
 
-deep-is@~0.1.3:
+deep-is@^0.1.3, deep-is@~0.1.3:
   version "0.1.3"
   resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
   integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
@@ -4112,6 +4158,13 @@ doctrine@^2.1.0:
   dependencies:
     esutils "^2.0.2"
 
+doctrine@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
+  integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
+  dependencies:
+    esutils "^2.0.2"
+
 dot-case@^3.0.3:
   version "3.0.3"
   resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.3.tgz#21d3b52efaaba2ea5fda875bb1aa8124521cf4aa"
@@ -4829,6 +4882,13 @@ engine.io@~3.2.0:
     engine.io-parser "~2.1.0"
     ws "~3.3.1"
 
+enquirer@^2.3.5:
+  version "2.3.6"
+  resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d"
+  integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==
+  dependencies:
+    ansi-colors "^4.1.1"
+
 ensure-posix-path@^1.0.0, ensure-posix-path@^1.0.1, ensure-posix-path@^1.0.2, ensure-posix-path@^1.1.0, ensure-posix-path@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/ensure-posix-path/-/ensure-posix-path-1.1.1.tgz#3c62bdb19fa4681544289edb2b382adc029179ce"
@@ -4916,6 +4976,14 @@ eslint-scope@^4.0.0:
     esrecurse "^4.1.0"
     estraverse "^4.1.1"
 
+eslint-scope@^5.1.1:
+  version "5.1.1"
+  resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
+  integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
+  dependencies:
+    esrecurse "^4.3.0"
+    estraverse "^4.1.1"
+
 eslint-utils@^1.3.1:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.3.1.tgz#9a851ba89ee7c460346f97cf8939c7298827e512"
@@ -4928,11 +4996,28 @@ eslint-utils@^2.0.0:
   dependencies:
     eslint-visitor-keys "^1.1.0"
 
+eslint-utils@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27"
+  integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==
+  dependencies:
+    eslint-visitor-keys "^1.1.0"
+
 eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2"
   integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==
 
+eslint-visitor-keys@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e"
+  integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==
+
+eslint-visitor-keys@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8"
+  integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==
+
 eslint@^5.6.0:
   version "5.9.0"
   resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.9.0.tgz#b234b6d15ef84b5849c6de2af43195a2d59d408e"
@@ -4977,6 +5062,49 @@ eslint@^5.6.0:
     table "^5.0.2"
     text-table "^0.2.0"
 
+eslint@^7.13.0:
+  version "7.13.0"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.13.0.tgz#7f180126c0dcdef327bfb54b211d7802decc08da"
+  integrity sha512-uCORMuOO8tUzJmsdRtrvcGq5qposf7Rw0LwkTJkoDbOycVQtQjmnhZSuLQnozLE4TmAzlMVV45eCHmQ1OpDKUQ==
+  dependencies:
+    "@babel/code-frame" "^7.0.0"
+    "@eslint/eslintrc" "^0.2.1"
+    ajv "^6.10.0"
+    chalk "^4.0.0"
+    cross-spawn "^7.0.2"
+    debug "^4.0.1"
+    doctrine "^3.0.0"
+    enquirer "^2.3.5"
+    eslint-scope "^5.1.1"
+    eslint-utils "^2.1.0"
+    eslint-visitor-keys "^2.0.0"
+    espree "^7.3.0"
+    esquery "^1.2.0"
+    esutils "^2.0.2"
+    file-entry-cache "^5.0.1"
+    functional-red-black-tree "^1.0.1"
+    glob-parent "^5.0.0"
+    globals "^12.1.0"
+    ignore "^4.0.6"
+    import-fresh "^3.0.0"
+    imurmurhash "^0.1.4"
+    is-glob "^4.0.0"
+    js-yaml "^3.13.1"
+    json-stable-stringify-without-jsonify "^1.0.1"
+    levn "^0.4.1"
+    lodash "^4.17.19"
+    minimatch "^3.0.4"
+    natural-compare "^1.4.0"
+    optionator "^0.9.1"
+    progress "^2.0.0"
+    regexpp "^3.1.0"
+    semver "^7.2.1"
+    strip-ansi "^6.0.0"
+    strip-json-comments "^3.1.0"
+    table "^5.2.3"
+    text-table "^0.2.0"
+    v8-compile-cache "^2.0.3"
+
 esm@^3.2.4:
   version "3.2.25"
   resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10"
@@ -4991,6 +5119,15 @@ espree@^4.0.0:
     acorn-jsx "^5.0.0"
     eslint-visitor-keys "^1.0.0"
 
+espree@^7.3.0:
+  version "7.3.0"
+  resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.0.tgz#dc30437cf67947cf576121ebd780f15eeac72348"
+  integrity sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==
+  dependencies:
+    acorn "^7.4.0"
+    acorn-jsx "^5.2.0"
+    eslint-visitor-keys "^1.3.0"
+
 esprima@^4.0.0, esprima@~4.0.0:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
@@ -5008,6 +5145,13 @@ esquery@^1.0.1:
   dependencies:
     estraverse "^4.0.0"
 
+esquery@^1.2.0:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57"
+  integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==
+  dependencies:
+    estraverse "^5.1.0"
+
 esrecurse@^4.1.0:
   version "4.2.1"
   resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf"
@@ -5015,11 +5159,23 @@ esrecurse@^4.1.0:
   dependencies:
     estraverse "^4.1.0"
 
+esrecurse@^4.3.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
+  integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
+  dependencies:
+    estraverse "^5.2.0"
+
 estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1:
   version "4.2.0"
   resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13"
   integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=
 
+estraverse@^5.1.0, estraverse@^5.2.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880"
+  integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==
+
 esutils@^2.0.2:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
@@ -5202,6 +5358,11 @@ fast-deep-equal@^2.0.1:
   resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
   integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=
 
+fast-deep-equal@^3.1.1:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
+  integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+
 fast-glob@^3.0.3:
   version "3.2.4"
   resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3"
@@ -5219,7 +5380,7 @@ fast-json-stable-stringify@^2.0.0:
   resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
   integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I=
 
-fast-levenshtein@~2.0.4:
+fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.4:
   version "2.0.6"
   resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
   integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
@@ -5295,6 +5456,13 @@ file-entry-cache@^2.0.0:
     flat-cache "^1.2.1"
     object-assign "^4.0.1"
 
+file-entry-cache@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c"
+  integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==
+  dependencies:
+    flat-cache "^2.0.1"
+
 filesize@^6.1.0:
   version "6.1.0"
   resolved "https://registry.yarnpkg.com/filesize/-/filesize-6.1.0.tgz#e81bdaa780e2451d714d71c0d7a4f3238d37ad00"
@@ -5451,11 +5619,25 @@ flat-cache@^1.2.1:
     graceful-fs "^4.1.2"
     write "^0.2.1"
 
+flat-cache@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0"
+  integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==
+  dependencies:
+    flatted "^2.0.0"
+    rimraf "2.6.3"
+    write "1.0.3"
+
 flat@^5.0.2:
   version "5.0.2"
   resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241"
   integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==
 
+flatted@^2.0.0:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138"
+  integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==
+
 follow-redirects@^1.0.0:
   version "1.5.6"
   resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.6.tgz#44eb4fe1981dff25e2bd86b7d4033abcdb81e965"
@@ -5748,7 +5930,7 @@ git-write-pkt-line@0.1.0:
     bops "0.0.3"
     through "~2.2.7"
 
-glob-parent@^5.1.0, glob-parent@~5.1.0:
+glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0:
   version "5.1.1"
   resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
   integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==
@@ -5803,6 +5985,13 @@ globals@^11.1.0, globals@^11.7.0:
   resolved "https://registry.yarnpkg.com/globals/-/globals-11.9.0.tgz#bde236808e987f290768a93d065060d78e6ab249"
   integrity sha512-5cJVtyXWH8PiJPVLZzzoIizXx944O4OmRro5MWKx5fT4MgcN7OfaMutPeaTdJCCURwbWdhhcCWcKIffPnmTzBg==
 
+globals@^12.1.0:
+  version "12.4.0"
+  resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8"
+  integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==
+  dependencies:
+    type-fest "^0.8.1"
+
 globals@^9.18.0:
   version "9.18.0"
   resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
@@ -6160,6 +6349,14 @@ ignore@^5.1.1:
   resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.1.tgz#2fc6b8f518aff48fef65a7f348ed85632448e4a5"
   integrity sha512-DWjnQIFLenVrwyRCKZT+7a7/U4Cqgar4WG8V++K3hw+lrW1hc/SIwdiGmtxKCVACmHULTuGeBbHJmbwW7/sAvA==
 
+import-fresh@^3.0.0, import-fresh@^3.2.1:
+  version "3.2.2"
+  resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.2.tgz#fc129c160c5d68235507f4331a6baad186bdbc3e"
+  integrity sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==
+  dependencies:
+    parent-module "^1.0.0"
+    resolve-from "^4.0.0"
+
 imurmurhash@^0.1.4:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
@@ -6721,6 +6918,14 @@ levn@^0.3.0, levn@~0.3.0:
     prelude-ls "~1.1.2"
     type-check "~0.3.2"
 
+levn@^0.4.1:
+  version "0.4.1"
+  resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
+  integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==
+  dependencies:
+    prelude-ls "^1.2.1"
+    type-check "~0.4.0"
+
 line-stream@0.0.0:
   version "0.0.0"
   resolved "https://registry.yarnpkg.com/line-stream/-/line-stream-0.0.0.tgz#888b7cc7951c6a05ce4d696dd1e6b8262371bb45"
@@ -7798,6 +8003,18 @@ optionator@^0.8.2:
     type-check "~0.3.2"
     wordwrap "~1.0.0"
 
+optionator@^0.9.1:
+  version "0.9.1"
+  resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"
+  integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==
+  dependencies:
+    deep-is "^0.1.3"
+    fast-levenshtein "^2.0.6"
+    levn "^0.4.1"
+    prelude-ls "^1.2.1"
+    type-check "^0.4.0"
+    word-wrap "^1.2.3"
+
 ora@^3.4.0:
   version "3.4.0"
   resolved "https://registry.yarnpkg.com/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318"
@@ -7917,6 +8134,13 @@ package-json@^4.0.1:
     registry-url "^3.0.3"
     semver "^5.1.0"
 
+parent-module@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
+  integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
+  dependencies:
+    callsites "^3.0.0"
+
 parse-passwd@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6"
@@ -8073,6 +8297,11 @@ posix-character-classes@^0.1.0:
   resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
   integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
 
+prelude-ls@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
+  integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
+
 prelude-ls@~1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
@@ -8375,6 +8604,11 @@ regexpp@^3.0.0:
   resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.0.0.tgz#dd63982ee3300e67b41c1956f850aa680d9d330e"
   integrity sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g==
 
+regexpp@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2"
+  integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==
+
 regexpu-core@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240"
@@ -8523,6 +8757,11 @@ resolve-from@^1.0.0:
   resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226"
   integrity sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=
 
+resolve-from@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
+  integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+
 resolve-package-path@^1.0.11, resolve-package-path@^1.2.6:
   version "1.2.7"
   resolved "https://registry.yarnpkg.com/resolve-package-path/-/resolve-package-path-1.2.7.tgz#2a7bc37ad96865e239330e3102c31322847e652e"
@@ -8585,6 +8824,13 @@ reusify@^1.0.4:
   resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
   integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
 
+rimraf@2.6.3, rimraf@~2.6.2:
+  version "2.6.3"
+  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
+  integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
+  dependencies:
+    glob "^7.1.3"
+
 rimraf@^2.1.4, rimraf@^2.2.8, rimraf@^2.3.4, rimraf@^2.4.3, rimraf@^2.4.4, rimraf@^2.5.3, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3:
   version "2.7.1"
   resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
@@ -8599,13 +8845,6 @@ rimraf@^3.0.0, rimraf@^3.0.1, rimraf@^3.0.2:
   dependencies:
     glob "^7.1.3"
 
-rimraf@~2.6.2:
-  version "2.6.3"
-  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
-  integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
-  dependencies:
-    glob "^7.1.3"
-
 rsvp@^3.0.14, rsvp@^3.0.17, rsvp@^3.0.18, rsvp@^3.0.21, rsvp@^3.0.6, rsvp@^3.1.0:
   version "3.6.2"
   resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-3.6.2.tgz#2e96491599a96cde1b515d5674a8f7a91452926a"
@@ -8697,7 +8936,7 @@ semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.3.0:
   resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
   integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
 
-semver@^7.0.0, semver@^7.3.2:
+semver@^7.0.0, semver@^7.2.1, semver@^7.3.2:
   version "7.3.2"
   resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938"
   integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==
@@ -8831,6 +9070,15 @@ slice-ansi@1.0.0:
   dependencies:
     is-fullwidth-code-point "^2.0.0"
 
+slice-ansi@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636"
+  integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==
+  dependencies:
+    ansi-styles "^3.2.0"
+    astral-regex "^1.0.0"
+    is-fullwidth-code-point "^2.0.0"
+
 snake-case@^3.0.3:
   version "3.0.3"
   resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.3.tgz#c598b822ab443fcbb145ae8a82c5e43526d5bbee"
@@ -9140,6 +9388,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
   dependencies:
     ansi-regex "^4.1.0"
 
+strip-ansi@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532"
+  integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==
+  dependencies:
+    ansi-regex "^5.0.0"
+
 strip-bom@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878"
@@ -9155,7 +9410,7 @@ strip-final-newline@^2.0.0:
   resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
   integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
 
-strip-json-comments@3.1.1:
+strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
   version "3.1.1"
   resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
   integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
@@ -9240,6 +9495,16 @@ table@^5.0.2:
     slice-ansi "1.0.0"
     string-width "^2.1.1"
 
+table@^5.2.3:
+  version "5.4.6"
+  resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e"
+  integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==
+  dependencies:
+    ajv "^6.10.2"
+    lodash "^4.17.14"
+    slice-ansi "^2.1.0"
+    string-width "^3.0.0"
+
 tap-parser@^7.0.0:
   version "7.0.0"
   resolved "https://registry.yarnpkg.com/tap-parser/-/tap-parser-7.0.0.tgz#54db35302fda2c2ccc21954ad3be22b2cba42721"
@@ -9506,6 +9771,13 @@ tslib@^1.10.0, tslib@^1.9.0:
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
   integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
 
+type-check@^0.4.0, type-check@~0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
+  integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==
+  dependencies:
+    prelude-ls "^1.2.1"
+
 type-check@~0.3.2:
   version "0.3.2"
   resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
@@ -9533,6 +9805,11 @@ type-fest@^0.11.0:
   resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1"
   integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==
 
+type-fest@^0.8.1:
+  version "0.8.1"
+  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
+  integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
+
 type-is@~1.6.17, type-is@~1.6.18:
   version "1.6.18"
   resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
@@ -9720,6 +9997,11 @@ uuid@^8.3.0:
   resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.1.tgz#2ba2e6ca000da60fce5a196954ab241131e05a31"
   integrity sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==
 
+v8-compile-cache@^2.0.3:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz#9471efa3ef9128d2f7c6a7ca39c4dd6b5055b132"
+  integrity sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==
+
 validate-npm-package-name@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e"
@@ -9830,6 +10112,11 @@ wide-align@1.1.3, wide-align@^1.1.0:
   dependencies:
     string-width "^1.0.2 || 2"
 
+word-wrap@^1.2.3:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
+  integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
+
 wordwrap@^0.0.3:
   version "0.0.3"
   resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
@@ -9890,6 +10177,13 @@ write-file-atomic@^3.0.0:
     signal-exit "^3.0.2"
     typedarray-to-buffer "^3.1.5"
 
+write@1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3"
+  integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==
+  dependencies:
+    mkdirp "^0.5.1"
+
 write@^0.2.1:
   version "0.2.1"
   resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757"