Repository: jhipster/generator-jhipster
Pull request #8789 (2018-11-10 14:12:32)

Retrieve and save blueprint version + add blueprint module as dev dependency of generated application
Retrieve and save blueprint version in .yo-rc.json, just like jhipster version. This might be useful in future for jhipster upgrade since you can have a blueprint upgrade in the same time.

Add blueprint module as dev dependency in package.json, just like generator-jhipster, since it's required each time you invoke a jhipster subgenerator (ci-cd, entity, ...). Use 'latest' as fallback version if blueprint version can't be retrieved.

Also mention blueprint version in initial git commit message.

-   Please make sure the below checklist is followed for Pull Requests.

-   [x] [Travis tests](https://travis-ci.org/jhipster/generator-jhipster/pull_requests) are green
-   [x] Tests are added where necessary
-   [ ] Documentation is added/updated where necessary
-   [x] Coding Rules & Commit Guidelines as per our [CONTRIBUTING.md document](https://github.com/jhipster/generator-jhipster/blob/master/CONTRIBUTING.md) are followed

<!--
Please also reference the issue number in a commit message to [automatically close the related Github issue](https://help.github.com/articles/closing-issues-via-commit-messages/)

Note: It is also possible to add `[skip ci]` to your commit message to skip Travis tests
-->



Patch:
diff --git a/generators/app/index.js b/generators/app/index.js
index 4a39ce1e6f..73031b63a9 100644
--- a/generators/app/index.js
+++ b/generators/app/index.js
@@ -236,7 +236,13 @@ module.exports = class extends BaseGenerator {
                 if (this.jhipsterVersion === undefined) {
                     this.jhipsterVersion = this.config.get('jhipsterVersion');
                 }
-                this.otherModules = this.config.get('otherModules');
+                this.otherModules = this.config.get('otherModules') || [];
+                if (this.blueprint) {
+                    this.blueprintVersion = this.findBlueprintVersion(this.blueprint);
+                    // Remove potential previous value to avoid duplicates
+                    this.otherModules = this.otherModules.filter(module => module.name !== this.blueprint);
+                    this.otherModules.push({ name: this.blueprint, version: this.blueprintVersion || 'latest' });
+                }
                 this.testFrameworks = this.config.get('testFrameworks');
                 this.enableTranslation = this.config.get('enableTranslation');
                 this.nativeLanguage = this.config.get('nativeLanguage');
@@ -395,6 +401,7 @@ module.exports = class extends BaseGenerator {
                     config.languages = this.languages;
                 }
                 this.blueprint && (config.blueprint = this.blueprint);
+                this.blueprintVersion && (config.blueprintVersion = this.blueprintVersion);
                 this.reactive && (config.reactive = this.reactive);
                 this.skipClient && (config.skipClient = true);
                 this.skipServer && (config.skipServer = true);
@@ -476,6 +483,9 @@ module.exports = class extends BaseGenerator {
                                 let commitMsg = `Initial application generated by JHipster-${this.jhipsterVersion}`;
                                 if (this.blueprint) {
                                     commitMsg += ` with blueprint: ${this.blueprint.replace('generator-jhipster-', '')}`;
+                                    if (this.blueprintVersion) {
+                                        commitMsg += `-${this.blueprintVersion}`;
+                                    }
                                 }
                                 this.gitExec(`commit -am "${commitMsg}"`, { trace: false }, () => {
                                     this.log(chalk.green.bold('Application successfully committed to Git.'));
diff --git a/generators/app/prompts.js b/generators/app/prompts.js
index 5158ed6c75..534b9a4ab5 100644
--- a/generators/app/prompts.js
+++ b/generators/app/prompts.js
@@ -210,7 +210,6 @@ function askModulesToBeInstalled(done, generator) {
                         })
                         .then(prompt => {
                             // [ {name: [moduleName], version:[version]}, ...]
-                            generator.otherModules = [];
                             prompt.otherModules.forEach(module => {
                                 generator.otherModules.push({ name: module.name, version: module.version });
                             });
diff --git a/generators/generator-base-private.js b/generators/generator-base-private.js
index 1735c4235b..ed47e7024f 100644
--- a/generators/generator-base-private.js
+++ b/generators/generator-base-private.js
@@ -748,6 +748,28 @@ module.exports = class extends Generator {
         return false;
     }
 
+    /**
+     * Try to retrieve the version of the blueprint used.
+     * @param {string} blueprintPkgName - generator name
+     * @return {string} version - retrieved version or empty string if not found
+     */
+    findBlueprintVersion(blueprintPkgName) {
+        let packageJsonPath = path.join(process.cwd(), 'node_modules', blueprintPkgName, 'package.json');
+        try {
+            if (!fs.existsSync(packageJsonPath)) {
+                this.debug('using global module as local version could not be found in node_modules');
+                packageJsonPath = path.join(blueprintPkgName, 'package.json');
+            }
+            // eslint-disable-next-line global-require,import/no-dynamic-require
+            const packagejs = require(packageJsonPath);
+            return packagejs.version;
+        } catch (err) {
+            this.debug('ERROR:', err);
+            this.warning(`Could not retrieve version of blueprint '${blueprintPkgName}'`);
+            return '';
+        }
+    }
+
     /**
      * Check if the generator specified as blueprint is installed.
      * @param {string} blueprint - generator name
diff --git a/generators/server/templates/package.json.ejs b/generators/server/templates/package.json.ejs
index be8a34079f..22dddf092c 100644
--- a/generators/server/templates/package.json.ejs
+++ b/generators/server/templates/package.json.ejs
@@ -26,7 +26,7 @@
     "node_modules"
   ],
   "devDependencies": {
-    <%_ otherModules.forEach(function(module) { _%>
+    <%_ otherModules.forEach(module => { _%>
     "<%= module.name %>": "<%= module.version %>",
     <%_ }); _%>
     "generator-jhipster": "<%= jhipsterVersion %>"


Test patch:
diff --git a/test/blueprint/app-blueprint.spec.js b/test/blueprint/app-blueprint.spec.js
new file mode 100644
index 0000000000..dab7cd025f
--- /dev/null
+++ b/test/blueprint/app-blueprint.spec.js
@@ -0,0 +1,81 @@
+const path = require('path');
+const assert = require('yeoman-assert');
+const helpers = require('yeoman-test');
+const fse = require('fs-extra');
+const CommonGenerator = require('../../generators/common');
+const ServerGenerator = require('../../generators/server');
+const ClientGenerator = require('../../generators/client');
+const createBlueprintMockForSubgen = require('../utils/utils').createBlueprintMockForSubgen;
+const expectedFiles = require('../utils/expected-files');
+const getFilesForOptions = require('../utils/utils').getFilesForOptions;
+const angularFiles = require('../../generators/client/files-angular').files;
+
+describe('JHipster application generator with blueprint', () => {
+    describe('generate monolith application with blueprint', () => {
+        before(done => {
+            helpers
+                .run(path.join(__dirname, '../../generators/app'))
+                .inTmpDir(dir => {
+                    // Fake the presence of the blueprint in node_modules
+                    const packagejs = {
+                        name: 'generator-jhipster-myblueprint',
+                        version: '9.9.9'
+                    };
+                    const fakeBlueprintModuleDir = path.join(dir, 'node_modules/generator-jhipster-myblueprint');
+                    fse.ensureDirSync(fakeBlueprintModuleDir);
+                    fse.writeJsonSync(path.join(fakeBlueprintModuleDir, 'package.json'), packagejs);
+                })
+                .withOptions({
+                    'from-cli': true,
+                    skipInstall: true,
+                    skipChecks: true,
+                    blueprint: 'myblueprint'
+                })
+                .withPrompts({
+                    baseName: 'jhipster',
+                    clientFramework: 'angularX',
+                    packageName: 'com.mycompany.myapp',
+                    packageFolder: 'com/mycompany/myapp',
+                    serviceDiscoveryType: false,
+                    authenticationType: 'jwt',
+                    cacheProvider: 'ehcache',
+                    enableHibernateCache: true,
+                    databaseType: 'sql',
+                    devDatabaseType: 'h2Memory',
+                    prodDatabaseType: 'mysql',
+                    useSass: false,
+                    enableTranslation: true,
+                    nativeLanguage: 'en',
+                    languages: ['fr']
+                })
+                .withGenerators([
+                    [helpers.createDummyGenerator(), 'jhipster-myblueprint:'],
+                    [createBlueprintMockForSubgen(CommonGenerator), 'jhipster-myblueprint:common'],
+                    [createBlueprintMockForSubgen(ServerGenerator), 'jhipster-myblueprint:server'],
+                    [createBlueprintMockForSubgen(ClientGenerator), 'jhipster-myblueprint:client']
+                ])
+                .on('end', done);
+        });
+
+        it('creates expected default files for server and angularX', () => {
+            assert.file(expectedFiles.common);
+            assert.file(expectedFiles.server);
+            assert.file(
+                getFilesForOptions(angularFiles, {
+                    useSass: false,
+                    enableTranslation: true,
+                    serviceDiscoveryType: false,
+                    authenticationType: 'jwt',
+                    testFrameworks: []
+                })
+            );
+        });
+
+        it('blueprint version is saved in .yo-rc.json', () => {
+            assert.fileContent('.yo-rc.json', /"blueprintVersion": "9.9.9"/);
+        });
+        it('blueprint module and version are in package.json', () => {
+            assert.fileContent('package.json', /"generator-jhipster-myblueprint": "9.9.9"/);
+        });
+    });
+});