diff --git a/packages/create-react-microservice/.bin/create-react-microservice b/packages/create-react-microservice/.bin/create-react-microservice index 22edb9606..09ff0f581 100755 --- a/packages/create-react-microservice/.bin/create-react-microservice +++ b/packages/create-react-microservice/.bin/create-react-microservice @@ -6,31 +6,30 @@ var path = require('path'); var pkg = require(path.join(__dirname, './../package.json')); require('check-engines')(pkg, function (err) { + var ora = require('ora'); + if (err) { - console.log(err); - require('ora')().fail(`System binaries do not meet constraints: ${JSON.stringify(pkg.engines)}.`); - process.exit(1); + ora().fail(err.message); + ora().fail('System binaries do not meet constraints: ' + JSON.stringify(pkg.engines)); - return; + return process.exit(1); } - const meow = require('meow'); - const ora = require('ora'); - const cli = meow({pkg, autoHelp: false}); - const commandsByName = { + var cli = require('meow')({pkg, autoHelp: false}); + var commandsByName = { default: require('./../dist/commands/default.js') }; - const commandName = cli.input.join(' '); - const Command = commandsByName[commandName] || commandsByName.default; - const instance = new Command({ + var commandName = cli.input.join(' '); + var Command = commandsByName[commandName] || commandsByName.default; + var instance = new Command({ pkg, input: cli.input, flags: cli.flags }); - instance.exec().catch(e => { + instance.exec().catch(function (e) { + ora().fail(e.message); console.error(e); - process.exit(1); }); }); diff --git a/packages/create-react-microservice/package.json b/packages/create-react-microservice/package.json index 082ae15b1..f65ea7c06 100644 --- a/packages/create-react-microservice/package.json +++ b/packages/create-react-microservice/package.json @@ -4,7 +4,8 @@ "private": false, "author": "Immonet dev team (https://immonet.de)", "license": "MIT", - "description": "Create highly scalable and universal React microservices/applications within seconds.", + "description": + "Create highly scalable and universal React microservices/applications within seconds.", "bin": { "create-react-microservice": "./.bin/create-react-microservice" }, @@ -19,14 +20,16 @@ "jest:watch": "yarn run jest -- --watch", "jest:coverage": "yarn run jest -- --coverage", "build:clean": "rimraf dist", - "build:babel": "NODE_ENV=production babel src --out-dir dist --ignore spec.js", + "build:babel": + "NODE_ENV=production babel src --out-dir dist --ignore spec.js", "build:flow": "flow-copy-source -v src dist --ignore='*.spec.js'", "build:watch": "yarn run build:babel -- -w", "prebuild": "yarn run build:clean", "build": "yarn run build:babel && yarn run build:flow", "flow": "flow", "flow-typed": "flow-typed", - "flow-typed-install": "flow-typed install --ignoreDeps=bundled peer --overwrite" + "flow-typed-install": + "flow-typed install --ignoreDeps=bundled peer --overwrite" }, "devDependencies": { "@immowelt/babel-preset-immowelt-node": "1.1.0", @@ -50,6 +53,7 @@ "create-react-microservice-scaffold": "0.0.0-development", "execa": "0.8.0", "find-node-modules": "1.0.4", + "latest-version": "^3.1.0", "meow": "4.0.0", "node-emoji": "1.8.1", "ora": "1.3.0", diff --git a/packages/create-react-microservice/src/commands/default.js b/packages/create-react-microservice/src/commands/default.js index 8cb2eaa6e..734cecc7d 100644 --- a/packages/create-react-microservice/src/commands/default.js +++ b/packages/create-react-microservice/src/commands/default.js @@ -25,6 +25,8 @@ class CreateReactMicroService extends Command { return this.printHelp(); } + await this.validateInstallation(); + const name = await this.resolveAppName(); const src = await this.resolveScaffold(); const dist = await this.resolveDistFolder(); diff --git a/packages/create-react-microservice/src/commands/default.spec.js b/packages/create-react-microservice/src/commands/default.spec.js index 4ffb59f58..b1cd4c338 100644 --- a/packages/create-react-microservice/src/commands/default.spec.js +++ b/packages/create-react-microservice/src/commands/default.spec.js @@ -21,6 +21,7 @@ describe('DefaultCommand', () => { describe('new Command().exec()', () => { let instance; + let validateInstallation; let processTemplateAndCreate; let resolveAndPromptTemplateArgs; let resolveDistFolder; @@ -32,6 +33,9 @@ describe('new Command().exec()', () => { beforeEach(() => { instance = new DefaultCommand({input: [], flags: {}, pkg: {}}); + validateInstallation = jest + .spyOn(instance, 'validateInstallation') + .mockImplementation(jest.fn()); processTemplateAndCreate = jest .spyOn(create, 'processTemplateAndCreate') .mockImplementation(jest.fn()); @@ -86,6 +90,7 @@ describe('new Command().exec()', () => { it('should resolve the dist folder, the app name and template args', async () => { await instance.exec(); + expect(validateInstallation).toHaveBeenCalledTimes(1); expect(resolveAppName).toHaveBeenCalledTimes(1); expect(resolveDistFolder).toHaveBeenCalledTimes(1); expect(resolveAndPromptTemplateArgs).toHaveBeenCalledTimes(1); diff --git a/packages/create-react-microservice/src/lib/command.js b/packages/create-react-microservice/src/lib/command.js index 1160c3816..2a2f0dc41 100644 --- a/packages/create-react-microservice/src/lib/command.js +++ b/packages/create-react-microservice/src/lib/command.js @@ -3,6 +3,7 @@ const exec = require('execa'); const chalk = require('chalk'); const ora = require('ora'); +const npm = require('./npm.js'); class Command { static exec = exec; @@ -22,6 +23,20 @@ class Command { this.flags = args.flags; } + /** + * Validates the global CLI installation so we can be sure that the newest version is always installed. + * + * @return {Promise} The Promise that resolves if everything is installed properly / rejects if something is wrong. + */ + async validateInstallation() { + const {name, version} = this.pkg; + const latestPublishedVersion = await npm.latestVersion(name); + + if (latestPublishedVersion !== version && version !== '0.0.0-development') { + throw new Error(`Oudated version of "${name}" found. Please update your global installation by executing "yarn global upgrade ${name}@${latestPublishedVersion}".`); + } + } + /** * Logs a message to the users console. * diff --git a/packages/create-react-microservice/src/lib/command.spec.js b/packages/create-react-microservice/src/lib/command.spec.js index a6998e2c5..64bc8c086 100644 --- a/packages/create-react-microservice/src/lib/command.spec.js +++ b/packages/create-react-microservice/src/lib/command.spec.js @@ -1,5 +1,8 @@ // @flow +jest.mock('./npm.js'); + +const npm: any = require('./npm.js'); const Command = require('./command.js'); describe('Command()', () => { @@ -8,6 +11,46 @@ describe('Command()', () => { }); }); +describe('new Command().validateInstallation()', () => { + let instance; + + beforeEach(() => { + instance = new Command({input: [], flags: {}, pkg: {name: 'foo-name', version: '1.0.0'}}); + }); + + afterEach(() => { + // $FlowFixMe: Ignore errors since the jest type-def is out of date. + jest.restoreAllMocks(); + jest.clearAllMocks(); + }); + + it('should be a function', () => { + expect(typeof instance.validateInstallation).toBe('function'); + }); + + it('should query the latest published version and should not throw an erorr if it matches the initialized version number in the package.json.', async () => { + npm.latestVersion.mockReturnValue('1.0.0'); + + await instance.validateInstallation(); + + expect(npm.latestVersion).toHaveBeenCalledWith('foo-name'); + }); + + it('should not throw an erorr if the initialized version number in the package.json matches "0.0.0-development".', async () => { + instance.pkg.version = '0.0.0-development'; + + npm.latestVersion.mockReturnValue('1.2.0'); + + await expect(instance.validateInstallation()).resolves; + }); + + it('should throw an erorr if the queried version does not match the initialized version number in the package.json.', async () => { + npm.latestVersion.mockReturnValue('1.2.0'); + + await expect(instance.validateInstallation()).rejects; + }); +}); + describe('new Command().log()', () => { let instance; let createLogMsg; diff --git a/packages/create-react-microservice/src/lib/npm.js b/packages/create-react-microservice/src/lib/npm.js new file mode 100644 index 000000000..7ad3dd3f9 --- /dev/null +++ b/packages/create-react-microservice/src/lib/npm.js @@ -0,0 +1,5 @@ +const latestVersion = require('latest-version'); + +module.exports = { + latestVersion +}; diff --git a/packages/create-react-microservice/src/lib/npm.spec.js b/packages/create-react-microservice/src/lib/npm.spec.js new file mode 100644 index 000000000..4491f7835 --- /dev/null +++ b/packages/create-react-microservice/src/lib/npm.spec.js @@ -0,0 +1,9 @@ +// @flow + +const npm = require('./npm.js'); + +describe('npm.latestVersion()', () => { + it('should be a function', () => { + expect(typeof npm.latestVersion).toBe('function'); + }); +}); diff --git a/yarn.lock b/yarn.lock index bc4c22be9..3347b8fbf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3581,6 +3581,12 @@ kind-of@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.1.tgz#4948e6263553ac3712fc44d305b77851d9e40ea4" +latest-version@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-3.1.0.tgz#a205383fea322b33b5ae3b18abee0dc2f356ee15" + dependencies: + package-json "^4.0.0" + lazy-cache@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" @@ -4508,7 +4514,7 @@ p-timeout@^1.1.1: dependencies: p-finally "^1.0.0" -package-json@^4.0.1: +package-json@^4.0.0, package-json@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/package-json/-/package-json-4.0.1.tgz#8869a0401253661c4c4ca3da6c2121ed555f5eed" dependencies: