Add suggest package for improved error messages
MODIFY
packages/@romejs/consume/Consumer.ts
packages/@romejs/consume/Consumer.ts
@@ -13,7 +13,9 @@ import {
DiagnosticPointer,
getDiagnosticsFromError,
DiagnosticCategory,
+ PartialDiagnosticAdviceItem,
} from '@romejs/diagnostics';
+import {suggestClosest} from '@romejs/suggest';
import {UnknownObject} from '@romejs/typescript-helpers';
import {
JSONValue,
@@ -530,10 +532,25 @@ export default class Consumer {
for (const [key, value] of this.asMap(false, false)) {
if (!this.usedNames.has(key)) {
+ const advice: Array<PartialDiagnosticAdviceItem> = [];
+ const suggestion = suggestClosest(
+ key,
+ Array.from(this.usedNames.keys()),
+ );
+
+ if (suggestion) {
+ advice.push({
+ type: 'log',
+ category: 'info',
+ message: `Did you mean <emphasis>${suggestion}</emphasis>?`,
+ });
+ }
+
value.unexpected(`Unknown <emphasis>${key}</emphasis> ${type}`, {
target: 'key',
at: 'suffix',
atParent: true,
+ advice,
});
}

ADD
packages/@romejs/suggest/index.test.ts
+import {suggestClosest} from '@romejs/suggest';
+import test from '@romejs/test';
+
+test('should suggest test', t => {
+ const result = suggestClosest('tst', ['lint', 'ci', 'test', 'bundle']);
+
+ t.is(result, 'test');
+});
+
+test('should suggest nothing', t => {
+ const result = suggestClosest('not even close', [
+ 'lint',
+ 'ci',
+ 'test',
+ 'bundle',
+ ]);
+
+ t.is(result, undefined);
+});

ADD
packages/@romejs/suggest/index.ts
+import leven from './leven';
+
+// Arbitrary threshold all suggestions must hit
+// A lower number means higher quality suggestions but
+// will be less likley to find a suggestion
+const threshold = 2;
+
+export function suggestClosest(
+ text: string,
+ suggestions: Array<string>,
+): string | undefined {
+ let current;
+ let currentScore = threshold;
+
+ for (const suggestion of suggestions) {
+ const score = leven(text, suggestion);
+
+ if (score <= currentScore) {
+ currentScore = currentScore;
+ current = suggestion;
+ }
+ }
+
+ return current;
+}

ADD
packages/@romejs/suggest/leven.ts
+// Original source: https://github.com/sindresorhus/leven/blob/master/index.js
+
+const charCodeCache: Array<number> = [];
+const array: Array<number> = [];
+
+export default (left: string, right: string): number => {
+ if (left === right) {
+ return 0;
+ }
+
+ const swap = left;
+
+ // Swapping the strings if `a` is longer than `b` so we know which one is the
+ // shortest & which one is the longest
+ if (left.length > right.length) {
+ left = right;
+ right = swap;
+ }
+
+ let leftLength = left.length;
+ let rightLength = right.length;
+
+ // Performing suffix trimming:
+ // We can linearly drop suffix common to both strings since they
+ // don't increase distance at all
+ // Note: `~-` is the bitwise way to perform a `- 1` operation
+ while (
+ leftLength > 0 &&
+ left.charCodeAt(~-leftLength) === right.charCodeAt(~-rightLength)
+ ) {
+ leftLength--;
+ rightLength--;
+ }
+
+ // Performing prefix trimming
+ // We can linearly drop prefix common to both strings since they
+ // don't increase distance at all
+ let start = 0;
+
+ while (
+ start < leftLength &&
+ left.charCodeAt(start) === right.charCodeAt(start)
+ ) {
+ start++;
+ }
+
+ leftLength -= start;
+ rightLength -= start;
+
+ if (leftLength === 0) {
+ return rightLength;
+ }
+
+ let bCharCode;
+ let result;
+ let temp;
+ let temp2;
+ let i = 0;
+ let j = 0;
+
+ while (i < leftLength) {
+ charCodeCache[i] = left.charCodeAt(start + i);
+ array[i] = ++i;
+ }
+
+ while (j < rightLength) {
+ bCharCode = right.charCodeAt(start + j);
+ temp = j++;
+ result = j;
+
+ for (i = 0; i < leftLength; i++) {
+ temp2 = bCharCode === charCodeCache[i] ? temp : temp + 1;
+ temp = array[i];
+
+ result = array[i] =
+ temp > result
+ ? temp2 > result
+ ? result + 1
+ : temp2
+ : temp2 > temp
+ ? temp + 1
+ : temp2;
+ }
+ }
+
+ return result as number;
+};

ADD
packages/@romejs/suggest/package.json
+{
+ "name": "@romejs/suggest",
+ "private": true,
+ "type": "module",
+ "main": "index.ts",
+ "version": "0.0.0"
+}

Add suggest package for improved error messages