diff --git a/packages/cli/package.json b/packages/cli/package.json index 608d4446d..5761c29c8 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -47,6 +47,7 @@ "chalk": "^2.3.0", "cli-table": "^0.3.1", "commander": "^2.17.1", + "fuzzaldrin": "^2.1.0", "gettext-parser": "^2.0.0", "glob": "^7.1.2", "inquirer": "^6.2.0", diff --git a/packages/cli/src/api/__snapshots__/utils.test.js.snap b/packages/cli/src/api/__snapshots__/utils.test.js.snap new file mode 100644 index 000000000..13998cd14 --- /dev/null +++ b/packages/cli/src/api/__snapshots__/utils.test.js.snap @@ -0,0 +1,16 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`fuzzValidateCommand should return command is invalid 1`] = ` +lingui: compilesss is unknown + Do you mean: compile ? +`; + +exports[`fuzzValidateCommand should return command is invalid if no commands are configured 1`] = ` +lingui: compile is unknown + +`; + +exports[`fuzzValidateCommand should return suggestion for the first command if user passes multiple commands 1`] = ` +lingui: add is unknown + Do you mean: add-strings, add-locales ? +`; diff --git a/packages/cli/src/api/utils.js b/packages/cli/src/api/utils.js index fb52976f6..af972ff37 100644 --- a/packages/cli/src/api/utils.js +++ b/packages/cli/src/api/utils.js @@ -1,6 +1,7 @@ import fs from "fs" import path from "path" import chalk from "chalk" +import { score } from "fuzzaldrin" export function removeDirectory(dir, keep = false) { if (!fs.existsSync(dir)) return @@ -32,3 +33,27 @@ export function prettyOrigin(origins) { return "" } } + +export function fuzzValidateCommand(commands = [], userCommands = []) { + const commandNames = commands.map(command => command.name()) + for (let userCommand of userCommands) { + if (!commandNames.includes(userCommand)) { + const commandScores = commandNames + .map(name => ({ + name: name, + score: score(name, userCommand.slice(0, name.length)) + })) + .filter(nameScore => nameScore.score > 0) + return `lingui: ${userCommand} is unknown + ${ + commandScores.length + ? `Do you mean: ${commandScores + .slice(0, 3) + .map(commandScore => chalk.inverse(commandScore.name)) + .join(", ")} ?` + : "" + }` + } + } + return "" +} diff --git a/packages/cli/src/api/utils.test.js b/packages/cli/src/api/utils.test.js new file mode 100644 index 000000000..62d274a98 --- /dev/null +++ b/packages/cli/src/api/utils.test.js @@ -0,0 +1,34 @@ +import { fuzzValidateCommand } from "./utils" + +describe("fuzzValidateCommand", function() { + function runFuzzValidateCommand(commandNames, userCommands) { + return fuzzValidateCommand( + commandNames.map(commandName => ({ name: () => commandName })), + userCommands + ) + } + + it("should return empty string if user command is valid", function() { + expect(runFuzzValidateCommand(["compile"], ["compile"])).toEqual("") + }) + + it("should return empty string if user passes no command", function() { + expect(runFuzzValidateCommand(["compile"], [])).toEqual("") + }) + + it("should return command is invalid if no commands are configured", function() { + expect(runFuzzValidateCommand([], ["compile"])).toMatchSnapshot() + }) + + it("should return command is invalid", function() { + expect( + runFuzzValidateCommand(["compile"], ["compilesss"]) + ).toMatchSnapshot() + }) + + it("should return suggestion for the first command if user passes multiple commands", function() { + expect( + runFuzzValidateCommand(["add-strings", "add-locales"], ["add", "com"]) + ).toMatchSnapshot() + }) +}) diff --git a/packages/cli/src/lingui.js b/packages/cli/src/lingui.js index fb53406fb..ee40ce9ff 100755 --- a/packages/cli/src/lingui.js +++ b/packages/cli/src/lingui.js @@ -1,5 +1,7 @@ #!/usr/bin/env node +import { fuzzValidateCommand } from "./api/utils" + const program = require("commander") let version @@ -19,3 +21,5 @@ program .command("extract [files...]", "Extracts messages from source files") .command("compile", "Compile message catalogs") .parse(process.argv) + +console.log(fuzzValidateCommand(program.commands, program.args)) diff --git a/yarn.lock b/yarn.lock index 6666e4842..1b8e67c5e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2725,6 +2725,10 @@ functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" +fuzzaldrin@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fuzzaldrin/-/fuzzaldrin-2.1.0.tgz#90204c3e2fdaa6941bb28d16645d418063a90e9b" + gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"