Skip to content
Draft

v2 #167

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 1 addition & 10 deletions apps/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,6 @@
"configuration": {
"title": "Total TypeScript",
"properties": {
"totalTypeScript.showFullTranslation": {
"type": "boolean",
"default": false,
"description": "Show the full translation of the error"
},
"totalTypeScript.showTLDRTranslation": {
"type": "boolean",
"default": true,
"description": "Show the TL;DR translation of the error"
},
"totalTypeScript.hideAllTips": {
"type": "boolean",
"default": null
Expand Down Expand Up @@ -84,6 +74,7 @@
},
"devDependencies": {
"@total-typescript/error-translation-engine": "workspace:*",
"@total-typescript/error-hint-engine": "workspace:*",
"@types/glob": "^7.2.0",
"@types/mocha": "^9.1.0",
"@types/node": "14.x",
Expand Down
2 changes: 1 addition & 1 deletion apps/vscode/src/bundleErrors.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
},
"2322": {
"body": "You can assign variables types to give me hints about what kind of types you want to allow in that variable. For instance:\n\n```ts\nconst num: number = 0;\n```\n\nThis tells me that `num` will always be a `number`.\n\nBut sometimes, you'll break that pact you made with me. For instance:\n\n```ts\nconst num: number = 'some string';\n```\n\nIn your case, you said that this type was the only thing assignable to that variable:\n\n```\n{1}\n```\n\nAnd you passed me this instead:\n\n```\n{0}\n```\n",
"excerpt": "I was expecting a type matching A, but instead you passed B.",
"excerpt": "I was expecting a type matching {0}, but instead you passed {1}.",
"code": "2322"
},
"2324": {
Expand Down
2 changes: 0 additions & 2 deletions apps/vscode/src/defaultOptions.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { Options } from './types';

export const defaultOptions: Options = {
showFullTranslation: true,
showTLDRTranslation: true,
hideBasicTips: null,
hiddenTips: [],
hideAllTips: null,
Expand Down
90 changes: 9 additions & 81 deletions apps/vscode/src/humaniseDiagnostic.ts
Original file line number Diff line number Diff line change
@@ -1,98 +1,26 @@
import * as vscode from 'vscode';

import {
parseErrors,
fillBodyAndExcerptWithItems,
} from '@total-typescript/error-translation-engine';
import * as bundleErrors from './bundleErrors.json';
import { compressToEncodedURIComponent } from 'lz-string';
import { findHintsInError } from '@total-typescript/error-hint-engine';
import { Options } from './types';

const getMessageTemplate = (options: Options) => {
let messageTemplate:
| 'body-and-tldr'
| 'tldr-only'
| 'body-only'
| 'link-only';

if (options.showFullTranslation) {
if (options.showTLDRTranslation) {
messageTemplate = 'body-and-tldr';
} else {
messageTemplate = 'body-only';
}
} else {
if (options.showTLDRTranslation) {
messageTemplate = 'tldr-only';
} else {
messageTemplate = 'link-only';
}
}

return messageTemplate;
};

export const humaniseDiagnostic = (
diagnostic: vscode.Diagnostic,
options: Options,
): vscode.MarkdownString[] => {
if (diagnostic.source !== 'ts') {
return [];
}
const errors = parseErrors(diagnostic.message);

const markdownStrings: vscode.MarkdownString[] = [];

errors.forEach((error, index, array) => {
const errorBodies: string[] = [];

const fullError = (
bundleErrors as Record<string, { body: string; excerpt: string }>
)[error.code];

errorBodies.push(['```txt', error.parseInfo.rawError, '```'].join('\n'));

if (fullError) {
const { excerpt, body } = fillBodyAndExcerptWithItems(
fullError.body,
fullError.excerpt,
error.parseInfo.items,
);

const messageTemplate = getMessageTemplate(options);
const hints = findHintsInError(diagnostic.message);

const linkToTranslation = `[See full translation](https://ts-error-translator.vercel.app/?error=${compressToEncodedURIComponent(
diagnostic.message,
)})`;
const messageBodies: string[] = [];

switch (messageTemplate) {
case 'body-and-tldr':
{
errorBodies.push(
`**Translation**: ${excerpt}`,
body,
linkToTranslation,
);
}
break;
case 'body-only':
errorBodies.push(linkToTranslation, body);
break;
case 'link-only':
errorBodies.push(linkToTranslation);
break;
case 'tldr-only':
errorBodies.push(`**Translation**: ${excerpt}`, linkToTranslation);
break;
}
} else {
errorBodies.push(
`[Contribute a translation for \`#${error.code}\`](https://github.com/mattpocock/ts-error-translator/blob/main/CONTRIBUTING.md)`,
);
}
hints.forEach((hint, index, array) => {
const prepended = (str: string) => {
return `- ${str}`;
};

markdownStrings.push(new vscode.MarkdownString(errorBodies.join('\n\n')));
messageBodies.push(prepended(hint));
});

return markdownStrings;
return [new vscode.MarkdownString(messageBodies.join('\n'))];
};
2 changes: 0 additions & 2 deletions apps/vscode/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
export interface Options {
showFullTranslation: boolean;
showTLDRTranslation: boolean;
hideBasicTips: boolean | null;
hideAllTips: boolean | null;
hiddenTips: string[];
Expand Down
3 changes: 2 additions & 1 deletion apps/vscode/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"resolveJsonModule": true,
"skipLibCheck": true,
"skipDefaultLibCheck": true,
"esModuleInterop": true
"esModuleInterop": true,
"noUncheckedIndexedAccess": true
/* Additional Checks */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
Expand Down
2 changes: 1 addition & 1 deletion packages/engine/errors/2322.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
original: "Type '{0}' is not assignable to type '{1}'."
excerpt: 'I was expecting a type matching A, but instead you passed B.'
excerpt: 'I was expecting a type matching {0}, but instead you passed {1}.'
---

You can assign variables types to give me hints about what kind of types you want to allow in that variable. For instance:
Expand Down
9 changes: 2 additions & 7 deletions packages/engine/src/getImprovedMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,25 @@ export const getImprovedMessageFromMarkdown = (

const parseResult = fm<{ excerpt: string }>(fileResult);

let body = parseResult.body;
let excerpt = parseResult.attributes.excerpt;

return fillBodyAndExcerptWithItems(body, excerpt, items);
return fillExcerptWithItems(excerpt, items);
} catch (e) {
console.log(e);
return null;
}
};

export const fillBodyAndExcerptWithItems = (
body: string,
export const fillExcerptWithItems = (
excerpt: string,
items: (string | number)[],
) => {
items.forEach((item, index) => {
const bodyRegex = new RegExp(`\\\{${index}\\\}`, 'g');
body = body.replace(bodyRegex, item.toString());
const excerptRegex = new RegExp(`'?\\\{${index}\\\}'?`, 'g');
excerpt = excerpt.replace(excerptRegex, '`' + item + '`');
});

return {
body,
excerpt,
};
};
5 changes: 4 additions & 1 deletion packages/engine/src/parseErrors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,10 @@ export interface ErrorInfo extends ErrorInfoWithoutImprovedError {

export interface ParseErrorsOpts {}

export const parseErrorsWithDb = (db: TsErrorMessageDb, message: string) => {
export const parseErrorsWithDb = (
db: TsErrorMessageDb,
message: string,
): ErrorInfoWithoutImprovedError[] => {
const errorMessageByKey: Record<string, ErrorInfoWithoutImprovedError> = {};

const keys = Object.keys(db);
Expand Down
19 changes: 19 additions & 0 deletions packages/error-hint-engine/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# @total-typescript/error-translation-engine

## 1.0.3

### Patch Changes

- [#83](https://github.com/mattpocock/ts-error-translator/pull/83) [`6c29b64`](https://github.com/mattpocock/ts-error-translator/commit/6c29b640de019b59fe8c780a8cf139724a6c3efc) Thanks [@eddyw](https://github.com/eddyw)! - Correctly match duplicated matched parameters

## 1.0.2

### Patch Changes

- [#67](https://github.com/mattpocock/ts-error-translator/pull/67) [`dc3d052`](https://github.com/mattpocock/ts-error-translator/commit/dc3d0528b8fa7bd0af38d9f3603d4e836f09e7dd) Thanks [@eddyw](https://github.com/eddyw)! - Add translation for 2761

## 1.0.1

### Patch Changes

- [`d458eab`](https://github.com/mattpocock/ts-error-translator/commit/d458eabd0bd2481867eb69661163de2505411133) Thanks [@mattpocock](https://github.com/mattpocock)! - Tweak to 7006 error
21 changes: 21 additions & 0 deletions packages/error-hint-engine/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "@total-typescript/error-hint-engine",
"version": "1.0.3",
"license": "MIT",
"main": "./src/index.ts",
"types": "./src/index.ts",
"private": true,
"scripts": {
"dev": "vitest",
"test": "vitest run"
},
"dependencies": {
"front-matter": "^4.0.2"
},
"devDependencies": {
"typescript": "^4.5.3",
"vitest": "^0.10.0",
"esbuild": "^0.14.38",
"tsconfig": "workspace:*"
}
}
13 changes: 13 additions & 0 deletions packages/error-hint-engine/src/__tests__/engine.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { describe, expect, it } from 'vitest';
import { findHintsInError } from '../errorHintEngine';

it('REPL', () => {
const errors = findHintsInError(
`Type 'string' is not assignable to type 'number'.`,
);
expect(errors).toMatchInlineSnapshot(`
[
"[Not Assignable To](https://totaltypescript.com/concepts/assignability): I expected one thing, but you passed another.",
]
`);
});
112 changes: 112 additions & 0 deletions packages/error-hint-engine/src/errorHintEngine.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
export type HintInstruction =
| {
type: 'includes';
includes: string;
hint: string;
}
| {
type: 'regex';
regex: RegExp;
hint: string;
};

export type TransformedHintInstruction = {
type: 'regex';
regex: RegExp;
hint: string;
};

const hintNamespace = (() => {
const hintInstructions: HintInstruction[] = [
{
type: 'includes',
includes: ' not assignable to ',
hint: `[Not Assignable To](https://totaltypescript.com/concepts/assignability): I expected one thing, but you passed another.`,
},
{
type: 'includes',
includes: 'parameter',
hint: `[Parameters](https://totaltypescript.com/concepts/parameters) are the names you give to the variables that a function takes.`,
},
{
type: 'regex',
regex: /(?<!type )argument/gi,
hint: `[Arguments](https://totaltypescript.com/concepts/arguments) are the values you pass to a function when you call it.`,
},
{
type: 'includes',
includes: 'Type argument',
hint: `[Type Arguments](https://totaltypescript.com/concepts/type-arguments) are the types you pass to a generic type, function or class.`,
},
{
type: 'includes',
includes: ' used before its ',
hint: `[Used Before Its](https://totaltypescript.com/concepts/used-before-declared): You tried to use something before you declared it.`,
},
{
type: 'includes',
includes: ` implicitly has an 'any' type`,
hint: `[Implicit Any](https://totaltypescript.com/concepts/implicit-any): I couldn't figure out what type something is, so I defaulted it to \`any\`.`,
},
{
type: 'includes',
includes: `Unused '@ts-expect-error' directive`,
hint: `You used a \`@ts-expect-error\`, but I didn't find any errors on the following line.`,
},
{
type: 'includes',
includes: 'Cannot assign to',
hint: `You're trying to modify something that can't be modified.`,
},
{
type: 'includes',
includes: ' comparable to ',
hint: `You'll usually see this error when you're trying to use 'as' to cast between two types that aren't compatible.`,
},
{
type: 'includes',
includes: `'React' refers to a UMD global`,
hint: 'You either need to import React, or fix the `jsx` property of your `tsconfig.json`. [Full guide](https://www.totaltypescript.com/react-refers-to-a-umd-global).',
},
{
type: 'includes',
includes: `JSX.IntrinsicElements`,
hint: '[JSX.IntrinsicElements](https://www.totaltypescript.com/what-is-jsx-intrinsicelements) is a type that describes the valid HTML elements you can use in JSX.',
},
{
type: 'includes',
includes: `does not exist on type 'JSX.IntrinsicElements'`,
hint: `You're trying to use an HTML element that doesn't exist on React's types.`,
},
];

const transformedHintInstructions: TransformedHintInstruction[] =
hintInstructions.map((instruction) => {
if (instruction.type === 'includes') {
return {
hint: instruction.hint,
type: 'regex',
regex: new RegExp(instruction.includes, 'ig'),
};
}
return instruction;
});

return {
transformedHintInstructions,
};
})();

export const findHintsInError = (err: string): string[] => {
const hints: string[] = [];

hintNamespace.transformedHintInstructions.forEach((instruction) => {
if (instruction.type === 'regex') {
if (instruction.regex.test(err)) {
hints.push(instruction.hint);
}
}
});

return hints;
};
1 change: 1 addition & 0 deletions packages/error-hint-engine/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './errorHintEngine';
Loading