From 89c967552144b16ac52e45668ee7cf4d8f11e6bb Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Sun, 16 Aug 2020 19:30:57 -0400 Subject: [PATCH] New: Add a 'did you mean?' note when failing to find a task (#94) --- lib/helpers/normalizeArgs.js | 29 ++++++++++++++++++++++++++++- package.json | 3 ++- test/registry.js | 12 ++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/lib/helpers/normalizeArgs.js b/lib/helpers/normalizeArgs.js index 297601f..3f9e339 100644 --- a/lib/helpers/normalizeArgs.js +++ b/lib/helpers/normalizeArgs.js @@ -4,6 +4,7 @@ var assert = require('assert'); var map = require('arr-map'); var flatten = require('arr-flatten'); +var levenshtein = require('fast-levenshtein'); function normalizeArgs(registry, args) { function getFunction(task) { @@ -12,7 +13,14 @@ function normalizeArgs(registry, args) { } var fn = registry.get(task); - assert(fn, 'Task never defined: ' + task); + if (!fn) { + var similar = similarTasks(registry, task); + if (similar.length > 0) { + assert(false, 'Task never defined: ' + task + ' - did you mean? ' + similar.join(', ')); + } else { + assert(false, 'Task never defined: ' + task); + } + } return fn; } @@ -22,4 +30,23 @@ function normalizeArgs(registry, args) { return map(flattenArgs, getFunction); } +function similarTasks(registry, queryTask) { + if (typeof queryTask !== 'string') { + return []; + } + + var tasks = registry.tasks(); + var similarTasks = []; + for (var task in tasks) { + if (tasks.hasOwnProperty(task)) { + var distance = levenshtein.get(task, queryTask); + var allowedDistance = Math.floor(0.4 * task.length) + 1; + if (distance < allowedDistance) { + similarTasks.push(task); + } + } + } + return similarTasks; +} + module.exports = normalizeArgs; diff --git a/package.json b/package.json index 7858ef3..e91b63f 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,8 @@ "last-run": "^1.1.0", "object.defaults": "^1.0.0", "object.reduce": "^1.0.0", - "undertaker-registry": "^1.0.0" + "undertaker-registry": "^1.0.0", + "fast-levenshtein": "^1.0.0" }, "devDependencies": { "async-once": "^1.0.0", diff --git a/test/registry.js b/test/registry.js index 37c6a89..a3a1c21 100644 --- a/test/registry.js +++ b/test/registry.js @@ -194,4 +194,16 @@ describe('registry', function() { taker.series('test')(); }); + it('should fail and offer tasks which are close in name', function(done) { + var taker = new Undertaker(new CommonRegistry()); + var customRegistry = new DefaultRegistry(); + taker.registry(customRegistry); + + function fail() { + taker.series('clear'); + } + + expect(fail).toThrow(/Task never defined: clear - did you mean\? clean/); + done(); + }); });