From e2b051f0585bd6c6f9873415090254aa7e95d8e7 Mon Sep 17 00:00:00 2001 From: Mike Brocchi Date: Mon, 2 Jan 2017 21:33:39 -0500 Subject: [PATCH] feat(generate): add ability to specify module for import (#3811) Closes #3806 --- .../angular-cli/blueprints/component/index.js | 25 ++++++++--- .../angular-cli/blueprints/directive/index.js | 25 ++++++++--- packages/angular-cli/blueprints/pipe/index.js | 25 ++++++++--- .../angular-cli/blueprints/service/index.js | 43 +++++++++++++++++-- .../component/component-module-fail.ts | 9 ++++ .../generate/component/component-module.ts | 15 +++++++ .../generate/directive/directive-basic.ts | 14 ++++++ .../directive/directive-module-fail.ts | 8 ++++ .../generate/directive/directive-module.ts | 15 +++++++ .../generate/{pipe.ts => pipe/pipe-basic.ts} | 4 +- .../tests/generate/pipe/pipe-module-fail.ts | 9 ++++ tests/e2e/tests/generate/pipe/pipe-module.ts | 15 +++++++ .../{service.ts => service/service-basic.ts} | 4 +- .../generate/service/service-module-fail.ts | 9 ++++ .../tests/generate/service/service-module.ts | 15 +++++++ 15 files changed, 209 insertions(+), 26 deletions(-) create mode 100644 tests/e2e/tests/generate/component/component-module-fail.ts create mode 100644 tests/e2e/tests/generate/component/component-module.ts create mode 100644 tests/e2e/tests/generate/directive/directive-basic.ts create mode 100644 tests/e2e/tests/generate/directive/directive-module-fail.ts create mode 100644 tests/e2e/tests/generate/directive/directive-module.ts rename tests/e2e/tests/generate/{pipe.ts => pipe/pipe-basic.ts} (82%) create mode 100644 tests/e2e/tests/generate/pipe/pipe-module-fail.ts create mode 100644 tests/e2e/tests/generate/pipe/pipe-module.ts rename tests/e2e/tests/generate/{service.ts => service/service-basic.ts} (83%) create mode 100644 tests/e2e/tests/generate/service/service-module-fail.ts create mode 100644 tests/e2e/tests/generate/service/service-module.ts diff --git a/packages/angular-cli/blueprints/component/index.js b/packages/angular-cli/blueprints/component/index.js index b1c9c94999bd..f5f37d645d96 100644 --- a/packages/angular-cli/blueprints/component/index.js +++ b/packages/angular-cli/blueprints/component/index.js @@ -1,4 +1,5 @@ const path = require('path'); +const fs = require('fs'); const chalk = require('chalk'); const Blueprint = require('../../ember-cli/lib/models/blueprint'); const dynamicPathParser = require('../../utilities/dynamic-path-parser'); @@ -19,15 +20,27 @@ module.exports = { { name: 'spec', type: Boolean }, { name: 'view-encapsulation', type: String, aliases: ['ve'] }, { name: 'change-detection', type: String, aliases: ['cd'] }, - { name: 'skip-import', type: Boolean, default: false } + { name: 'skip-import', type: Boolean, default: false }, + { name: 'module', type: String, aliases: ['m'] } ], beforeInstall: function(options) { - try { - this.pathToModule = findParentModule(this.project, this.dynamicPath.dir); - } catch(e) { - if (!options.skipImport) { - throw `Error locating module for declaration\n\t${e}`; + if (options.module) { + // Resolve path to module + const modulePath = options.module.endsWith('.ts') ? options.module : `${options.module}.ts`; + const parsedPath = dynamicPathParser(this.project, modulePath); + this.pathToModule = path.join(this.project.root, parsedPath.dir, parsedPath.base); + + if (!fs.existsSync(this.pathToModule)) { + throw 'Module specified does not exist'; + } + } else { + try { + this.pathToModule = findParentModule(this.project, this.dynamicPath.dir); + } catch(e) { + if (!options.skipImport) { + throw `Error locating module for declaration\n\t${e}`; + } } } }, diff --git a/packages/angular-cli/blueprints/directive/index.js b/packages/angular-cli/blueprints/directive/index.js index 00765faf56ae..dd5731be0f3a 100644 --- a/packages/angular-cli/blueprints/directive/index.js +++ b/packages/angular-cli/blueprints/directive/index.js @@ -1,4 +1,5 @@ const path = require('path'); +const fs = require('fs'); const chalk = require('chalk'); const dynamicPathParser = require('../../utilities/dynamic-path-parser'); const stringUtils = require('ember-cli-string-utils'); @@ -15,15 +16,27 @@ module.exports = { { name: 'flat', type: Boolean, default: true }, { name: 'prefix', type: String, default: null }, { name: 'spec', type: Boolean }, - { name: 'skip-import', type: Boolean, default: false } + { name: 'skip-import', type: Boolean, default: false }, + { name: 'module', type: String, aliases: ['m'] } ], beforeInstall: function(options) { - try { - this.pathToModule = findParentModule(this.project, this.dynamicPath.dir); - } catch(e) { - if (!options.skipImport) { - throw `Error locating module for declaration\n\t${e}`; + if (options.module) { + // Resolve path to module + const modulePath = options.module.endsWith('.ts') ? options.module : `${options.module}.ts`; + const parsedPath = dynamicPathParser(this.project, modulePath); + this.pathToModule = path.join(this.project.root, parsedPath.dir, parsedPath.base); + + if (!fs.existsSync(this.pathToModule)) { + throw ' '; + } + } else { + try { + this.pathToModule = findParentModule(this.project, this.dynamicPath.dir); + } catch(e) { + if (!options.skipImport) { + throw `Error locating module for declaration\n\t${e}`; + } } } }, diff --git a/packages/angular-cli/blueprints/pipe/index.js b/packages/angular-cli/blueprints/pipe/index.js index d1a17144dc81..10bf9fb02d3b 100644 --- a/packages/angular-cli/blueprints/pipe/index.js +++ b/packages/angular-cli/blueprints/pipe/index.js @@ -1,4 +1,5 @@ const path = require('path'); +const fs = require('fs'); const chalk = require('chalk'); const dynamicPathParser = require('../../utilities/dynamic-path-parser'); const stringUtils = require('ember-cli-string-utils'); @@ -14,15 +15,27 @@ module.exports = { availableOptions: [ { name: 'flat', type: Boolean, default: true }, { name: 'spec', type: Boolean }, - { name: 'skip-import', type: Boolean, default: false } + { name: 'skip-import', type: Boolean, default: false }, + { name: 'module', type: String, aliases: ['m'] } ], beforeInstall: function(options) { - try { - this.pathToModule = findParentModule(this.project, this.dynamicPath.dir); - } catch(e) { - if (!options.skipImport) { - throw `Error locating module for declaration\n\t${e}`; + if (options.module) { + // Resolve path to module + const modulePath = options.module.endsWith('.ts') ? options.module : `${options.module}.ts`; + const parsedPath = dynamicPathParser(this.project, modulePath); + this.pathToModule = path.join(this.project.root, parsedPath.dir, parsedPath.base); + + if (!fs.existsSync(this.pathToModule)) { + throw 'Module specified does not exist'; + } + } else { + try { + this.pathToModule = findParentModule(this.project, this.dynamicPath.dir); + } catch(e) { + if (!options.skipImport) { + throw `Error locating module for declaration\n\t${e}`; + } } } }, diff --git a/packages/angular-cli/blueprints/service/index.js b/packages/angular-cli/blueprints/service/index.js index bcdf691a6372..2707fe1ddce1 100644 --- a/packages/angular-cli/blueprints/service/index.js +++ b/packages/angular-cli/blueprints/service/index.js @@ -1,7 +1,11 @@ const path = require('path'); +const fs = require('fs'); const chalk = require('chalk'); const dynamicPathParser = require('../../utilities/dynamic-path-parser'); const Blueprint = require('../../ember-cli/lib/models/blueprint'); +const NodeHost = require('@angular-cli/ast-tools').NodeHost; +const stringUtils = require('ember-cli-string-utils'); +const astUtils = require('../../utilities/ast-utils'); const getFiles = Blueprint.prototype.files; module.exports = { @@ -9,9 +13,23 @@ module.exports = { availableOptions: [ { name: 'flat', type: Boolean, default: true }, - { name: 'spec', type: Boolean } + { name: 'spec', type: Boolean }, + { name: 'module', type: String, aliases: ['m'] } ], + beforeInstall: function(options) { + if (options.module) { + // Resolve path to module + const modulePath = options.module.endsWith('.ts') ? options.module : `${options.module}.ts`; + const parsedPath = dynamicPathParser(this.project, modulePath); + this.pathToModule = path.join(this.project.root, parsedPath.dir, parsedPath.base); + + if (!fs.existsSync(this.pathToModule)) { + throw 'Module specified does not exist'; + } + } + }, + normalizeEntityName: function (entityName) { var parsedPath = dynamicPathParser(this.project, entityName); @@ -54,8 +72,25 @@ module.exports = { }; }, - afterInstall() { - const warningMessage = 'Service is generated but not provided, it must be provided to be used'; - this._writeStatusToUI(chalk.yellow, 'WARNING', warningMessage); + afterInstall(options) { + const returns = []; + + if (!this.pathToModule) { + const warningMessage = 'Service is generated but not provided, it must be provided to be used'; + this._writeStatusToUI(chalk.yellow, 'WARNING', warningMessage); + } else { + const className = stringUtils.classify(`${options.entity.name}Service`); + const fileName = stringUtils.dasherize(`${options.entity.name}.service`); + const fullGeneratePath = path.join(this.project.root, this.generatePath); + const moduleDir = path.parse(this.pathToModule).dir; + const relativeDir = path.relative(moduleDir, fullGeneratePath); + const importPath = relativeDir ? `./${relativeDir}/${fileName}` : `./${fileName}`; + returns.push( + astUtils.addProviderToModule(this.pathToModule, className, importPath) + .then(change => change.apply(NodeHost))); + this._writeStatusToUI(chalk.yellow, 'update', path.relative(this.project.root, this.pathToModule)); + } + + return Promise.all(returns); } }; diff --git a/tests/e2e/tests/generate/component/component-module-fail.ts b/tests/e2e/tests/generate/component/component-module-fail.ts new file mode 100644 index 000000000000..f4efd1eb8ff6 --- /dev/null +++ b/tests/e2e/tests/generate/component/component-module-fail.ts @@ -0,0 +1,9 @@ +import {ng} from '../../../utils/process'; +import {expectToFail} from '../../../utils/utils'; + + +export default function() { + return Promise.resolve() + .then(() => expectToFail(() => + ng('generate', 'component', 'test-component', '--module', 'app.moduleXXX.ts'))); +} diff --git a/tests/e2e/tests/generate/component/component-module.ts b/tests/e2e/tests/generate/component/component-module.ts new file mode 100644 index 000000000000..4cbda4b5d07e --- /dev/null +++ b/tests/e2e/tests/generate/component/component-module.ts @@ -0,0 +1,15 @@ +import {join} from 'path'; +import {ng} from '../../../utils/process'; +import {expectFileToMatch} from '../../../utils/fs'; + + +export default function() { + const modulePath = join('src', 'app', 'app.module.ts'); + + return ng('generate', 'component', 'test-component', '--module', 'app.module.ts') + .then(() => expectFileToMatch(modulePath, + /import { TestComponentComponent } from '.\/test-component\/test-component.component'/)) + + // Try to run the unit tests. + .then(() => ng('build')); +} diff --git a/tests/e2e/tests/generate/directive/directive-basic.ts b/tests/e2e/tests/generate/directive/directive-basic.ts new file mode 100644 index 000000000000..07ac720d0ba3 --- /dev/null +++ b/tests/e2e/tests/generate/directive/directive-basic.ts @@ -0,0 +1,14 @@ +import {ng} from '../../../utils/process'; +import {join} from 'path'; +import {expectFileToExist} from '../../../utils/fs'; + + +export default function() { + const directiveDir = join('src', 'app'); + return ng('generate', 'directive', 'test-directive') + .then(() => expectFileToExist(join(directiveDir, 'test-directive.directive.ts'))) + .then(() => expectFileToExist(join(directiveDir, 'test-directive.directive.spec.ts'))) + + // Try to run the unit tests. + .then(() => ng('test', '--single-run')); +} diff --git a/tests/e2e/tests/generate/directive/directive-module-fail.ts b/tests/e2e/tests/generate/directive/directive-module-fail.ts new file mode 100644 index 000000000000..05ff8d98f1c9 --- /dev/null +++ b/tests/e2e/tests/generate/directive/directive-module-fail.ts @@ -0,0 +1,8 @@ +import {ng} from '../../../utils/process'; +import {expectToFail} from '../../../utils/utils'; + +export default function() { + return Promise.resolve() + .then(() => expectToFail(() => + ng('generate', 'directive', 'test-directive', '--module', 'app.moduleXXX.ts'))); +} diff --git a/tests/e2e/tests/generate/directive/directive-module.ts b/tests/e2e/tests/generate/directive/directive-module.ts new file mode 100644 index 000000000000..c95637762896 --- /dev/null +++ b/tests/e2e/tests/generate/directive/directive-module.ts @@ -0,0 +1,15 @@ +import {join} from 'path'; +import {ng} from '../../../utils/process'; +import {expectFileToMatch} from '../../../utils/fs'; + + +export default function() { + const modulePath = join('src', 'app', 'app.module.ts'); + + return ng('generate', 'directive', 'test-directive', '--module', 'app.module.ts') + .then(() => expectFileToMatch(modulePath, + /import { TestDirectiveDirective } from '.\/test-directive.directive'/)) + + // Try to run the unit tests. + .then(() => ng('build')); +} diff --git a/tests/e2e/tests/generate/pipe.ts b/tests/e2e/tests/generate/pipe/pipe-basic.ts similarity index 82% rename from tests/e2e/tests/generate/pipe.ts rename to tests/e2e/tests/generate/pipe/pipe-basic.ts index 125d51fe9505..18a8d106125f 100644 --- a/tests/e2e/tests/generate/pipe.ts +++ b/tests/e2e/tests/generate/pipe/pipe-basic.ts @@ -1,7 +1,7 @@ import {join} from 'path'; -import {ng} from '../../utils/process'; -import {expectFileToExist} from '../../utils/fs'; +import {ng} from '../../../utils/process'; +import {expectFileToExist} from '../../../utils/fs'; export default function() { // Create the pipe in the same directory. diff --git a/tests/e2e/tests/generate/pipe/pipe-module-fail.ts b/tests/e2e/tests/generate/pipe/pipe-module-fail.ts new file mode 100644 index 000000000000..e18d5e73d038 --- /dev/null +++ b/tests/e2e/tests/generate/pipe/pipe-module-fail.ts @@ -0,0 +1,9 @@ +import {ng} from '../../../utils/process'; +import {expectToFail} from '../../../utils/utils'; + + +export default function() { + return Promise.resolve() + .then(() => expectToFail(() => + ng('generate', 'pipe', 'test-pipe', '--module', 'app.moduleXXX.ts'))); +} diff --git a/tests/e2e/tests/generate/pipe/pipe-module.ts b/tests/e2e/tests/generate/pipe/pipe-module.ts new file mode 100644 index 000000000000..65d938e0223a --- /dev/null +++ b/tests/e2e/tests/generate/pipe/pipe-module.ts @@ -0,0 +1,15 @@ +import {join} from 'path'; +import {ng} from '../../../utils/process'; +import {expectFileToMatch} from '../../../utils/fs'; + + +export default function() { + const modulePath = join('src', 'app', 'app.module.ts'); + + return ng('generate', 'pipe', 'test-pipe', '--module', 'app.module.ts') + .then(() => expectFileToMatch(modulePath, + /import { TestPipePipe } from '.\/test-pipe.pipe'/)) + + // Try to run the unit tests. + .then(() => ng('build')); +} diff --git a/tests/e2e/tests/generate/service.ts b/tests/e2e/tests/generate/service/service-basic.ts similarity index 83% rename from tests/e2e/tests/generate/service.ts rename to tests/e2e/tests/generate/service/service-basic.ts index ec25f3c95621..273bcfb48bfd 100644 --- a/tests/e2e/tests/generate/service.ts +++ b/tests/e2e/tests/generate/service/service-basic.ts @@ -1,6 +1,6 @@ import {join} from 'path'; -import {ng} from '../../utils/process'; -import {expectFileToExist} from '../../utils/fs'; +import {ng} from '../../../utils/process'; +import {expectFileToExist} from '../../../utils/fs'; export default function() { diff --git a/tests/e2e/tests/generate/service/service-module-fail.ts b/tests/e2e/tests/generate/service/service-module-fail.ts new file mode 100644 index 000000000000..18f16e100ec7 --- /dev/null +++ b/tests/e2e/tests/generate/service/service-module-fail.ts @@ -0,0 +1,9 @@ +import {ng} from '../../../utils/process'; +import {expectToFail} from '../../../utils/utils'; + + +export default function() { + return Promise.resolve() + .then(() => expectToFail(() => + ng('generate', 'service', 'test-service', '--module', 'app.moduleXXX.ts'))); +} diff --git a/tests/e2e/tests/generate/service/service-module.ts b/tests/e2e/tests/generate/service/service-module.ts new file mode 100644 index 000000000000..e13ea17f04e1 --- /dev/null +++ b/tests/e2e/tests/generate/service/service-module.ts @@ -0,0 +1,15 @@ +import {join} from 'path'; +import {ng} from '../../../utils/process'; +import {expectFileToMatch} from '../../../utils/fs'; + + +export default function() { + const modulePath = join('src', 'app', 'app.module.ts'); + + return ng('generate', 'service', 'test-service', '--module', 'app.module.ts') + .then(() => expectFileToMatch(modulePath, + /import { TestServiceService } from '.\/test-service.service'/)) + + // Try to run the unit tests. + .then(() => ng('build')); +}