diff --git a/lib/commands/generate.js b/lib/commands/generate.js index 6d488abe52e..93d32784a0b 100644 --- a/lib/commands/generate.js +++ b/lib/commands/generate.js @@ -9,6 +9,11 @@ const _ = require('ember-cli-lodash-subset'); const EOL = require('os').EOL; const SilentError = require('silent-error'); +const UNKNOWN_BLUEPRINT_ERROR = + 'The `ember generate` command requires a ' + + 'blueprint name or path (via --blueprint) to be specified. ' + + 'For more details, use `ember help`.'; + module.exports = Command.extend({ name: 'generate', description: 'Generates new code from blueprints.', @@ -22,6 +27,13 @@ module.exports = Command.extend({ { name: 'classic', type: Boolean, default: false, aliases: ['c'] }, { name: 'dummy', type: Boolean, default: false, aliases: ['dum', 'id'] }, { name: 'in-repo-addon', type: String, default: null, aliases: ['in-repo', 'ir'] }, + { + name: 'blueprint', + type: String, + default: null, + description: + 'Specifies a path to a local blueprint to use instead of using a resolved blueprint matching the name', + }, { name: 'in', type: String, @@ -37,15 +49,10 @@ module.exports = Command.extend({ run(commandOptions, rawArgs) { let blueprintName = rawArgs[0]; + let blueprintPath = commandOptions.blueprint; - if (!blueprintName) { - return Promise.reject( - new SilentError( - 'The `ember generate` command requires a ' + - 'blueprint name to be specified. ' + - 'For more details, use `ember help`.' - ) - ); + if (!blueprintName && !blueprintPath) { + return Promise.reject(new SilentError(UNKNOWN_BLUEPRINT_ERROR)); } let taskArgs = { @@ -165,3 +172,7 @@ module.exports = Command.extend({ } }, }); + +module.exports.ERRORS = { + UNKNOWN_BLUEPRINT_ERROR, +}; diff --git a/lib/models/blueprint.js b/lib/models/blueprint.js index 0736c9753f9..35858475f5c 100644 --- a/lib/models/blueprint.js +++ b/lib/models/blueprint.js @@ -1279,6 +1279,20 @@ let Blueprint = CoreObject.extend({ Blueprint.lookup = function (name, options) { options = options || {}; + if (options.blueprintPath) { + let blueprintPath = path.resolve(options.blueprintPath); + + if (Blueprint._existsSync(blueprintPath)) { + return Blueprint.load(blueprintPath); + } + + if (!options.ignoreMissing) { + throw new SilentError(`Unknown blueprint: ${name}`); + } + + return; + } + let lookupPaths = generateLookupPaths(options.paths); let lookupPath; diff --git a/lib/tasks/generate-from-blueprint.js b/lib/tasks/generate-from-blueprint.js index 45ab47d74af..0ff60d92bb1 100644 --- a/lib/tasks/generate-from-blueprint.js +++ b/lib/tasks/generate-from-blueprint.js @@ -13,20 +13,21 @@ class GenerateTask extends Task { async run(options) { let name = options.args[0]; + let blueprintPath = options.blueprint; let noAddonBlueprint = ['mixin', 'blueprint-test']; - let mainBlueprint = this.lookupBlueprint(name, options.ignoreMissingMain); - let testBlueprint = this.lookupBlueprint(`${name}-test`, true); + let mainBlueprint = this.lookupBlueprint(name, options.ignoreMissingMain, blueprintPath); + let testBlueprint = this.lookupBlueprint(`${name}-test`, true, `${blueprintPath}-test`); // lookup custom addon blueprint - let addonBlueprint = this.lookupBlueprint(`${name}-addon`, true); + let addonBlueprint = this.lookupBlueprint(`${name}-addon`, true, `${blueprintPath}-addon`); // otherwise, use default addon-import if (noAddonBlueprint.indexOf(name) < 0 && !addonBlueprint && options.args[1]) { let mainBlueprintSupportsAddon = mainBlueprint && mainBlueprint.supportsAddon(); if (mainBlueprintSupportsAddon) { - addonBlueprint = this.lookupBlueprint('addon-import', true); + addonBlueprint = this.lookupBlueprint('addon-import', true, `${blueprintPath}-addon-import`); } } @@ -91,10 +92,11 @@ class GenerateTask extends Task { return addonBlueprint[this.blueprintFunction](addonBlueprintOptions); } - lookupBlueprint(name, ignoreMissing) { + lookupBlueprint(name, ignoreMissing, blueprintPath) { return Blueprint.lookup(name, { paths: this.project.blueprintLookupPaths(), ignoreMissing, + blueprintPath, }); } } diff --git a/tests/acceptance/generate-test.js b/tests/acceptance/generate-test.js index bbf16bb516d..027fe071d1f 100755 --- a/tests/acceptance/generate-test.js +++ b/tests/acceptance/generate-test.js @@ -268,6 +268,22 @@ describe('Acceptance: ember generate', function () { expect(file('app/controllers/foo.js')).to.contain('custom: true'); }); + it('allows a path to be specified to a blueprint', async function () { + await outputFile( + 'blueprints/http-proxy/files/server/proxies/__name__.js', + "import Ember from 'ember';\n" + 'export default Ember.Object.extend({ foo: true });\n' + ); + await generate([ + 'http-proxy', + 'foo', + 'http://localhost:5000', + '--blueprint', + path.resolve(`${__dirname}/../../blueprints/http-proxy`), + ]); + + expect(file('server/index.js')).to.not.contain('foo: true', 'the local blueprint is not used'); + }); + it('passes custom cli arguments to blueprint options', async function () { await initApp(); diff --git a/tests/unit/commands/generate-test.js b/tests/unit/commands/generate-test.js index 6a978151c85..1c131633781 100644 --- a/tests/unit/commands/generate-test.js +++ b/tests/unit/commands/generate-test.js @@ -11,6 +11,7 @@ const GenerateCommand = require('../../../lib/commands/generate'); const td = require('testdouble'); const ROOT = process.cwd(); const { createTempDir } = require('broccoli-test-helper'); +const { ERRORS } = GenerateCommand; describe('generate command', function () { let input, options, command; @@ -98,11 +99,7 @@ describe('generate command', function () { it('complains if no blueprint name is given', function () { return expect(command.validateAndRun([])).to.be.rejected.then((error) => { - expect(error.message).to.equal( - 'The `ember generate` command requires a ' + - 'blueprint name to be specified. ' + - 'For more details, use `ember help`.' - ); + expect(error.message).to.equal(ERRORS.UNKNOWN_BLUEPRINT_ERROR); }); });