From 0cf7c9a329125e19c05c9f2617dc8c11c154ee47 Mon Sep 17 00:00:00 2001 From: horike37 Date: Sat, 16 Dec 2017 20:27:33 +0900 Subject: [PATCH 1/3] first commit --- lib/classes/CLI.js | 2 +- lib/classes/PluginManager.js | 32 ++++++++++++++++++++++++++++++++ package-lock.json | 3 +-- package.json | 1 + 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/lib/classes/CLI.js b/lib/classes/CLI.js index cb177428b83..0a87db2eef8 100644 --- a/lib/classes/CLI.js +++ b/lib/classes/CLI.js @@ -149,7 +149,7 @@ class CLI { this.consoleLog(chalk.dim('* Documentation: https://serverless.com/framework/docs/')); this.consoleLog(''); - +console.log(frameworkCommands) if (!_.isEmpty(frameworkCommands)) { _.forEach(frameworkCommands, (details, command) => { this.displayCommandUsage(details, command); diff --git a/lib/classes/PluginManager.js b/lib/classes/PluginManager.js index b2714300fc1..6c791b79cd5 100644 --- a/lib/classes/PluginManager.js +++ b/lib/classes/PluginManager.js @@ -8,6 +8,7 @@ const writeFile = require('../utils/fs/writeFile'); const getCacheFilePath = require('../utils/getCacheFilePath'); const getServerlessConfigFile = require('../utils/getServerlessConfigFile'); const crypto = require('crypto'); +const levenshtein = require('fast-levenshtein'); /** * @private @@ -324,6 +325,7 @@ class PluginManager { return current.commands[name]; } const commandName = commandOrAlias.slice(0, index + 1).join(' '); + this.getCommandSuggestion(commandName); const errorMessage = `Serverless command "${commandName}" not found Run "serverless help" for a list of all available commands.`; @@ -331,6 +333,36 @@ class PluginManager { }, { commands: this.commands }); } + getCommandSuggestion(inputCommand) { + let suggestion; + return this.getCollectCommandWord(this.serverless.cli.loadedCommands) + .then(commandWordsArray => { + let minValue = 0; + _.forEach(commandWordsArray, correctCommand => { + const distance = levenshtein.get(inputCommand, correctCommand); + minValue = minValue === 0 ? distance : minValue; + if (minValue > distance) { + suggestion = correctCommand; + minValue = distance; + } + }); + console.log(suggestion); + }); + } + + getCollectCommandWord(commandObject, commandWordsArray) { + const wordsArray = _.isArray(commandWordsArray) + && !_.isEmpty(commandWordsArray) ? commandWordsArray : []; + _.forEach(commandObject, (commandChildObject, commandChildName) => { + wordsArray.push(commandChildName); + if (commandChildObject.commands) { + return this.getCollectCommandWord(commandChildObject.commands, wordsArray); + } + return BbPromise.resolve(); + }); + return BbPromise.resolve(_.uniq(wordsArray)); + } + getEvents(command) { return _.flatMap(command.lifecycleEvents, (event) => [ `before:${command.key}:${event}`, diff --git a/package-lock.json b/package-lock.json index a3ae6ef8136..7598148ce2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2063,8 +2063,7 @@ "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" }, "fb-watchman": { "version": "1.9.2", diff --git a/package.json b/package.json index 007cd37b506..08f7b157f10 100644 --- a/package.json +++ b/package.json @@ -100,6 +100,7 @@ "chalk": "^2.0.0", "ci-info": "^1.1.1", "download": "^5.0.2", + "fast-levenshtein": "^2.0.6", "filesize": "^3.3.0", "fs-extra": "^0.26.7", "get-stdin": "^5.0.1", From fbce5ed4ae6ec447c000c0f716c0fd783d332833 Mon Sep 17 00:00:00 2001 From: horike37 Date: Sun, 17 Dec 2017 15:13:20 +0900 Subject: [PATCH 2/3] add suggestion feature --- lib/classes/CLI.js | 16 +++++---- lib/classes/PluginManager.js | 45 ++++++-------------------- lib/utils/getCommandSuggestion.js | 36 +++++++++++++++++++++ lib/utils/getCommandSuggestion.test.js | 33 +++++++++++++++++++ 4 files changed, 88 insertions(+), 42 deletions(-) create mode 100644 lib/utils/getCommandSuggestion.js create mode 100644 lib/utils/getCommandSuggestion.test.js diff --git a/lib/classes/CLI.js b/lib/classes/CLI.js index 0a87db2eef8..f445bd40357 100644 --- a/lib/classes/CLI.js +++ b/lib/classes/CLI.js @@ -5,6 +5,7 @@ const minimist = require('minimist'); const _ = require('lodash'); const os = require('os'); const chalk = require('chalk'); +const getCommandSuggestion = require('../utils/getCommandSuggestion'); class CLI { constructor(serverless, inputArray) { @@ -149,7 +150,6 @@ class CLI { this.consoleLog(chalk.dim('* Documentation: https://serverless.com/framework/docs/')); this.consoleLog(''); -console.log(frameworkCommands) if (!_.isEmpty(frameworkCommands)) { _.forEach(frameworkCommands, (details, command) => { this.displayCommandUsage(details, command); @@ -252,11 +252,14 @@ console.log(frameworkCommands) // Throw error if command not found. if (!command) { - const errorMessage = [ - `Serverless command "${commandName}" not found.`, - ' Run "serverless help" for a list of all available commands.', - ].join(''); - throw new this.serverless.classes.Error(errorMessage); + return getCommandSuggestion(commandName, this.serverless.cli.loadedCommands) + .then(suggestedCommand => { + const errorMessage = [ + `Serverless command "${commandName}" not found. Did you mean "${suggestedCommand}"?`, + ' Run "serverless help" for a list of all available commands.', + ].join(''); + throw new this.serverless.classes.Error(errorMessage); + }); } // print the name of the plugin @@ -266,6 +269,7 @@ console.log(frameworkCommands) this.displayCommandOptions(command); this.consoleLog(''); + return null; } getVersionNumber() { diff --git a/lib/classes/PluginManager.js b/lib/classes/PluginManager.js index 6c791b79cd5..9cc4ce16738 100644 --- a/lib/classes/PluginManager.js +++ b/lib/classes/PluginManager.js @@ -8,7 +8,7 @@ const writeFile = require('../utils/fs/writeFile'); const getCacheFilePath = require('../utils/getCacheFilePath'); const getServerlessConfigFile = require('../utils/getServerlessConfigFile'); const crypto = require('crypto'); -const levenshtein = require('fast-levenshtein'); +const getCommandSuggestion = require('../utils/getCommandSuggestion'); /** * @private @@ -325,44 +325,17 @@ class PluginManager { return current.commands[name]; } const commandName = commandOrAlias.slice(0, index + 1).join(' '); - this.getCommandSuggestion(commandName); - const errorMessage = `Serverless command "${commandName}" not found - - Run "serverless help" for a list of all available commands.`; - throw new this.serverless.classes.Error(errorMessage); + return getCommandSuggestion(commandName, this.serverless.cli.loadedCommands) + .then(suggestedCommand => { + const errorMessage = [ + `Serverless command "${commandName}" not found. Did you mean "${suggestedCommand}"?`, + ' Run "serverless help" for a list of all available commands.', + ].join(''); + throw new this.serverless.classes.Error(errorMessage); + }); }, { commands: this.commands }); } - getCommandSuggestion(inputCommand) { - let suggestion; - return this.getCollectCommandWord(this.serverless.cli.loadedCommands) - .then(commandWordsArray => { - let minValue = 0; - _.forEach(commandWordsArray, correctCommand => { - const distance = levenshtein.get(inputCommand, correctCommand); - minValue = minValue === 0 ? distance : minValue; - if (minValue > distance) { - suggestion = correctCommand; - minValue = distance; - } - }); - console.log(suggestion); - }); - } - - getCollectCommandWord(commandObject, commandWordsArray) { - const wordsArray = _.isArray(commandWordsArray) - && !_.isEmpty(commandWordsArray) ? commandWordsArray : []; - _.forEach(commandObject, (commandChildObject, commandChildName) => { - wordsArray.push(commandChildName); - if (commandChildObject.commands) { - return this.getCollectCommandWord(commandChildObject.commands, wordsArray); - } - return BbPromise.resolve(); - }); - return BbPromise.resolve(_.uniq(wordsArray)); - } - getEvents(command) { return _.flatMap(command.lifecycleEvents, (event) => [ `before:${command.key}:${event}`, diff --git a/lib/utils/getCommandSuggestion.js b/lib/utils/getCommandSuggestion.js new file mode 100644 index 00000000000..d115a792aae --- /dev/null +++ b/lib/utils/getCommandSuggestion.js @@ -0,0 +1,36 @@ +'use strict'; +const _ = require('lodash'); +const BbPromise = require('bluebird'); +const levenshtein = require('fast-levenshtein'); + +const getCollectCommandWords = (commandObject, commandWordsArray) => { + const wordsArray = _.isArray(commandWordsArray) + && !_.isEmpty(commandWordsArray) ? commandWordsArray : []; + _.forEach(commandObject, (commandChildObject, commandChildName) => { + wordsArray.push(commandChildName); + if (commandChildObject.commands) { + return getCollectCommandWords(commandChildObject.commands, wordsArray); + } + return BbPromise.resolve(); + }); + return BbPromise.resolve(_.uniq(wordsArray)); +}; + +const getCommandSuggestion = (inputCommand, allCommandsObject) => { + let suggestion; + return getCollectCommandWords(allCommandsObject) + .then(commandWordsArray => { + let minValue = 0; + _.forEach(commandWordsArray, correctCommand => { + const distance = levenshtein.get(inputCommand, correctCommand); + minValue = minValue === 0 ? distance : minValue; + if (minValue > distance) { + suggestion = correctCommand; + minValue = distance; + } + }); + return BbPromise.resolve(suggestion); + }); +}; + +module.exports = getCommandSuggestion; diff --git a/lib/utils/getCommandSuggestion.test.js b/lib/utils/getCommandSuggestion.test.js new file mode 100644 index 00000000000..d7e32ab040d --- /dev/null +++ b/lib/utils/getCommandSuggestion.test.js @@ -0,0 +1,33 @@ +'use strict'; + +const chai = require('chai'); +const getCommandSuggestion = require('./getCommandSuggestion'); +chai.use(require('chai-as-promised')); +const expect = require('chai').expect; +const Serverless = require('../../lib/Serverless'); + +const serverless = new Serverless(); +serverless.init(); + +describe('#getCommandSuggestion', () => { + it('should return "package" as a suggested command if you input "pekage"', () => + expect(getCommandSuggestion('pekage', serverless.cli.loadedCommands)) + .to.be.fulfilled.then(suggestedCommand => { + expect(suggestedCommand).to.be.equal('package'); + }) + ); + + it('should return "deploy" as a suggested command if you input "deploi"', () => + expect(getCommandSuggestion('deploi', serverless.cli.loadedCommands)) + .to.be.fulfilled.then(suggestedCommand => { + expect(suggestedCommand).to.be.equal('deploy'); + }) + ); + + it('should return "invoke" as a suggested command if you input "lnvoke"', () => + expect(getCommandSuggestion('lnvoke', serverless.cli.loadedCommands)) + .to.be.fulfilled.then(suggestedCommand => { + expect(suggestedCommand).to.be.equal('invoke'); + }) + ); +}); From 4a3e140dc2834a98d7a0e0b48802588f2e739226 Mon Sep 17 00:00:00 2001 From: horike37 Date: Tue, 19 Dec 2017 01:10:43 +0900 Subject: [PATCH 3/3] update code to synchronous --- lib/classes/CLI.js | 14 +++++------ lib/classes/PluginManager.js | 15 ++++++------ lib/utils/getCommandSuggestion.js | 34 +++++++++++++------------- lib/utils/getCommandSuggestion.test.js | 34 ++++++++++---------------- 4 files changed, 43 insertions(+), 54 deletions(-) diff --git a/lib/classes/CLI.js b/lib/classes/CLI.js index f445bd40357..3d7f4b5ab78 100644 --- a/lib/classes/CLI.js +++ b/lib/classes/CLI.js @@ -252,14 +252,12 @@ class CLI { // Throw error if command not found. if (!command) { - return getCommandSuggestion(commandName, this.serverless.cli.loadedCommands) - .then(suggestedCommand => { - const errorMessage = [ - `Serverless command "${commandName}" not found. Did you mean "${suggestedCommand}"?`, - ' Run "serverless help" for a list of all available commands.', - ].join(''); - throw new this.serverless.classes.Error(errorMessage); - }); + const suggestedCommand = getCommandSuggestion(commandName, allCommands); + const errorMessage = [ + `Serverless command "${commandName}" not found. Did you mean "${suggestedCommand}"?`, + ' Run "serverless help" for a list of all available commands.', + ].join(''); + throw new this.serverless.classes.Error(errorMessage); } // print the name of the plugin diff --git a/lib/classes/PluginManager.js b/lib/classes/PluginManager.js index 9cc4ce16738..1325ec9a91d 100644 --- a/lib/classes/PluginManager.js +++ b/lib/classes/PluginManager.js @@ -325,14 +325,13 @@ class PluginManager { return current.commands[name]; } const commandName = commandOrAlias.slice(0, index + 1).join(' '); - return getCommandSuggestion(commandName, this.serverless.cli.loadedCommands) - .then(suggestedCommand => { - const errorMessage = [ - `Serverless command "${commandName}" not found. Did you mean "${suggestedCommand}"?`, - ' Run "serverless help" for a list of all available commands.', - ].join(''); - throw new this.serverless.classes.Error(errorMessage); - }); + const suggestedCommand = getCommandSuggestion(commandName, + this.serverless.cli.loadedCommands); + const errorMessage = [ + `Serverless command "${commandName}" not found. Did you mean "${suggestedCommand}"?`, + ' Run "serverless help" for a list of all available commands.', + ].join(''); + throw new this.serverless.classes.Error(errorMessage); }, { commands: this.commands }); } diff --git a/lib/utils/getCommandSuggestion.js b/lib/utils/getCommandSuggestion.js index d115a792aae..3e4604ed84d 100644 --- a/lib/utils/getCommandSuggestion.js +++ b/lib/utils/getCommandSuggestion.js @@ -1,36 +1,36 @@ 'use strict'; const _ = require('lodash'); -const BbPromise = require('bluebird'); const levenshtein = require('fast-levenshtein'); const getCollectCommandWords = (commandObject, commandWordsArray) => { - const wordsArray = _.isArray(commandWordsArray) + let wordsArray = _.isArray(commandWordsArray) && !_.isEmpty(commandWordsArray) ? commandWordsArray : []; _.forEach(commandObject, (commandChildObject, commandChildName) => { wordsArray.push(commandChildName); if (commandChildObject.commands) { - return getCollectCommandWords(commandChildObject.commands, wordsArray); + wordsArray = getCollectCommandWords(commandChildObject.commands, wordsArray); } - return BbPromise.resolve(); }); - return BbPromise.resolve(_.uniq(wordsArray)); + return _.uniq(wordsArray); }; const getCommandSuggestion = (inputCommand, allCommandsObject) => { let suggestion; - return getCollectCommandWords(allCommandsObject) - .then(commandWordsArray => { - let minValue = 0; - _.forEach(commandWordsArray, correctCommand => { - const distance = levenshtein.get(inputCommand, correctCommand); - minValue = minValue === 0 ? distance : minValue; - if (minValue > distance) { - suggestion = correctCommand; - minValue = distance; - } - }); - return BbPromise.resolve(suggestion); + const commandWordsArray = getCollectCommandWords(allCommandsObject); + let minValue = 0; + _.forEach(commandWordsArray, correctCommand => { + const distance = levenshtein.get(inputCommand, correctCommand); + if (minValue === 0) { + suggestion = correctCommand; + minValue = distance; + } + + if (minValue > distance) { + suggestion = correctCommand; + minValue = distance; + } }); + return suggestion; }; module.exports = getCommandSuggestion; diff --git a/lib/utils/getCommandSuggestion.test.js b/lib/utils/getCommandSuggestion.test.js index d7e32ab040d..56ea4633781 100644 --- a/lib/utils/getCommandSuggestion.test.js +++ b/lib/utils/getCommandSuggestion.test.js @@ -1,33 +1,25 @@ 'use strict'; -const chai = require('chai'); -const getCommandSuggestion = require('./getCommandSuggestion'); -chai.use(require('chai-as-promised')); const expect = require('chai').expect; +const getCommandSuggestion = require('./getCommandSuggestion'); const Serverless = require('../../lib/Serverless'); const serverless = new Serverless(); serverless.init(); describe('#getCommandSuggestion', () => { - it('should return "package" as a suggested command if you input "pekage"', () => - expect(getCommandSuggestion('pekage', serverless.cli.loadedCommands)) - .to.be.fulfilled.then(suggestedCommand => { - expect(suggestedCommand).to.be.equal('package'); - }) - ); + it('should return "package" as a suggested command if you input "pekage"', () => { + const suggestedCommand = getCommandSuggestion('pekage', serverless.cli.loadedCommands); + expect(suggestedCommand).to.be.equal('package'); + }); - it('should return "deploy" as a suggested command if you input "deploi"', () => - expect(getCommandSuggestion('deploi', serverless.cli.loadedCommands)) - .to.be.fulfilled.then(suggestedCommand => { - expect(suggestedCommand).to.be.equal('deploy'); - }) - ); + it('should return "deploy" as a suggested command if you input "deploi"', () => { + const suggestedCommand = getCommandSuggestion('deploi', serverless.cli.loadedCommands); + expect(suggestedCommand).to.be.equal('deploy'); + }); - it('should return "invoke" as a suggested command if you input "lnvoke"', () => - expect(getCommandSuggestion('lnvoke', serverless.cli.loadedCommands)) - .to.be.fulfilled.then(suggestedCommand => { - expect(suggestedCommand).to.be.equal('invoke'); - }) - ); + it('should return "invoke" as a suggested command if you input "lnvoke"', () => { + const suggestedCommand = getCommandSuggestion('lnvoke', serverless.cli.loadedCommands); + expect(suggestedCommand).to.be.equal('invoke'); + }); });