@rehearsal/codefixes
Advanced tools
Comparing version 1.0.5-beta to 1.0.5
import { CodeFixAction } from 'typescript'; | ||
import { CodeFix, CodeFixCollection, DiagnosticWithContext } from '.'; | ||
import { CodeFix, CodeFixCollection, DiagnosticWithContext } from './index.js'; | ||
/** | ||
@@ -7,10 +7,8 @@ * Provides | ||
export declare class BaseCodeFixCollection implements CodeFixCollection { | ||
readonly list: { | ||
[key: number]: CodeFix; | ||
readonly fixes: { | ||
[key: number]: CodeFix[]; | ||
}; | ||
constructor(list: { | ||
[key: number]: CodeFix; | ||
}); | ||
constructor(codefixes: CodeFix[]); | ||
getFixesForDiagnostic(diagnostic: DiagnosticWithContext): CodeFixAction[]; | ||
} | ||
//# sourceMappingURL=base-codefix-collection.d.ts.map |
@@ -1,23 +0,23 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.BaseCodeFixCollection = void 0; | ||
/** | ||
* Provides | ||
*/ | ||
class BaseCodeFixCollection { | ||
constructor(list) { | ||
this.list = list; | ||
export class BaseCodeFixCollection { | ||
constructor(codefixes) { | ||
this.fixes = {}; | ||
for (const codefix of codefixes) { | ||
for (const error of codefix.getErrorCodes()) { | ||
this.fixes[error] ? this.fixes[error].push(codefix) : (this.fixes[error] = [codefix]); | ||
} | ||
} | ||
} | ||
getFixesForDiagnostic(diagnostic) { | ||
if (this.list[diagnostic.code] === undefined) { | ||
if (!this.fixes[diagnostic.code]) { | ||
return []; | ||
} | ||
const codeFixAction = this.list[diagnostic.code].getCodeAction(diagnostic); | ||
if (codeFixAction === undefined) { | ||
return []; | ||
} | ||
return [codeFixAction]; | ||
const codeFixActions = this.fixes[diagnostic.code] | ||
.map((fix) => fix.getCodeAction(diagnostic)) | ||
.filter((codefix) => codefix); | ||
return codeFixActions; | ||
} | ||
} | ||
exports.BaseCodeFixCollection = BaseCodeFixCollection; | ||
//# sourceMappingURL=base-codefix-collection.js.map |
import type { CodeFixAction } from 'typescript'; | ||
import type { CodeFixCollection, CodeFixCollectionFilter, DiagnosticWithContext } from './types'; | ||
import type { CodeFixCollection, CodeFixCollectionFilter, DiagnosticWithContext } from './types.js'; | ||
/** | ||
@@ -4,0 +4,0 @@ * Provides |
@@ -1,8 +0,5 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.CodeFixesProvider = void 0; | ||
/** | ||
* Provides | ||
*/ | ||
class CodeFixesProvider { | ||
export class CodeFixesProvider { | ||
constructor(collections) { | ||
@@ -22,3 +19,2 @@ this.collections = collections; | ||
} | ||
exports.CodeFixesProvider = CodeFixesProvider; | ||
//# sourceMappingURL=codefixes-provider.js.map |
@@ -1,3 +0,10 @@ | ||
import { CodeFixesProvider } from './codefixes-provider'; | ||
import { CodeFixesProvider } from './codefixes-provider.js'; | ||
import type { CodeFixAction } from 'typescript'; | ||
export declare const codefixes: CodeFixesProvider; | ||
export interface ContentDelegate { | ||
getText(filename: string): string; | ||
setText(filename: string, text: string): void; | ||
applyText?(newText: string): void; | ||
} | ||
export declare function applyCodeFix(fix: CodeFixAction, contentDelegate: ContentDelegate): void; | ||
//# sourceMappingURL=codefixes.d.ts.map |
@@ -1,19 +0,33 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.codefixes = void 0; | ||
const base_codefix_collection_1 = require("./base-codefix-collection"); | ||
const codefixes_provider_1 = require("./codefixes-provider"); | ||
const typescript_codefix_collection_1 = require("./typescript-codefix-collection"); | ||
const addErrorTypeGuard_1 = require("./fixes/addErrorTypeGuard"); | ||
const addMissingExport_1 = require("./fixes/addMissingExport"); | ||
const makeMemberOptional_1 = require("./fixes/makeMemberOptional"); | ||
exports.codefixes = new codefixes_provider_1.CodeFixesProvider([ | ||
new base_codefix_collection_1.BaseCodeFixCollection({ | ||
18046: new addErrorTypeGuard_1.AddErrorTypeGuardCodeFix(), | ||
2571: new addErrorTypeGuard_1.AddErrorTypeGuardCodeFix(), | ||
2790: new makeMemberOptional_1.MakeMemberOptionalCodeFix(), | ||
4082: new addMissingExport_1.AddMissingExportCodeFix(), | ||
}), | ||
new typescript_codefix_collection_1.TypescriptCodeFixCollection(), | ||
import { applyTextChange, normalizeTextChanges } from '@rehearsal/ts-utils'; | ||
import { BaseCodeFixCollection } from './base-codefix-collection.js'; | ||
import { CodeFixesProvider } from './codefixes-provider.js'; | ||
import { TypescriptCodeFixCollection } from './typescript-codefix-collection.js'; | ||
import { AddErrorTypeGuardCodeFix } from './fixes/addErrorTypeGuard.js'; | ||
import { AddMissingExportCodeFix } from './fixes/addMissingExport.js'; | ||
import { MakeMemberOptionalCodeFix } from './fixes/makeMemberOptional.js'; | ||
import { AddMissingTypesBasedOnInheritanceCodeFix } from './fixes/addMissingTypesBasedOnInheritance.js'; | ||
import { AddMissingTypesBasedOnInlayHintsCodeFix } from './fixes/addMissingTypesBasedOnInlayHints.js'; | ||
import { AnnotateWithStrictTypeFromJSDoc } from './fixes/annotateWithStrictTypeFromJSDoc.js'; | ||
export const codefixes = new CodeFixesProvider([ | ||
new BaseCodeFixCollection([ | ||
new AddErrorTypeGuardCodeFix(), | ||
new AddMissingExportCodeFix(), | ||
new AddMissingTypesBasedOnInheritanceCodeFix(), | ||
new AddMissingTypesBasedOnInlayHintsCodeFix(), | ||
new AnnotateWithStrictTypeFromJSDoc(), | ||
new MakeMemberOptionalCodeFix(), | ||
]), | ||
new TypescriptCodeFixCollection(), | ||
]); | ||
export function applyCodeFix(fix, contentDelegate) { | ||
for (const fileTextChange of fix.changes) { | ||
let text = contentDelegate.getText(fileTextChange.fileName); | ||
const textChanges = normalizeTextChanges([...fileTextChange.textChanges]); | ||
for (const textChange of textChanges) { | ||
text = applyTextChange(text, textChange); | ||
contentDelegate.applyText?.(text); | ||
} | ||
contentDelegate.setText(fileTextChange.fileName, text); | ||
} | ||
} | ||
//# sourceMappingURL=codefixes.js.map |
import { type CodeFixAction, type DiagnosticWithLocation, type FileTextChanges } from 'typescript'; | ||
import type { CodeFixCollection, CodeHintList, DiagnosticWithContext } from './types'; | ||
import type { CodeFixCollection, CodeHintList, DiagnosticWithContext } from './types.js'; | ||
/** | ||
@@ -4,0 +4,0 @@ * Don't actually fix the issue but adds a @ts-expect-error comments instead |
@@ -1,10 +0,8 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.createCodeFixAction = exports.HintCodeFixCollection = void 0; | ||
const typescript_1 = require("typescript"); | ||
const utils_1 = require("@rehearsal/utils"); | ||
import ts from 'typescript'; | ||
import { ChangesFactory } from '@rehearsal/ts-utils'; | ||
const { flattenDiagnosticMessageText } = ts; | ||
/** | ||
* Don't actually fix the issue but adds a @ts-expect-error comments instead | ||
*/ | ||
class HintCodeFixCollection { | ||
export class HintCodeFixCollection { | ||
constructor(list) { | ||
@@ -16,8 +14,10 @@ this.list = list; | ||
const comment = `@ts-expect-error @rehearsal TODO TS${diagnostic.code}: ${hint}`; | ||
const changes = utils_1.ChangesFactory.insertCommentAtLineBeforeNode(diagnostic.file, diagnostic.node, comment); | ||
if (diagnostic.node === undefined) { | ||
return []; | ||
} | ||
const changes = ChangesFactory.insertCommentAtLineBeforeNode(diagnostic.file, diagnostic.node, comment); | ||
return [createCodeFixAction('hint', [changes], 'Add hint comment to help solve the issue')]; | ||
} | ||
getHint(diagnostic) { | ||
var _a, _b; | ||
const conditionalHints = (_a = this.list[diagnostic.code]) === null || _a === void 0 ? void 0 : _a.hints; | ||
const conditionalHints = this.list[diagnostic.code]?.hints; | ||
if (conditionalHints !== undefined && diagnostic.node !== undefined) { | ||
@@ -30,9 +30,8 @@ for (const conditionalHint of conditionalHints) { | ||
} | ||
const defaultHint = ((_b = this.list[diagnostic.code]) === null || _b === void 0 ? void 0 : _b.hint) || (0, typescript_1.flattenDiagnosticMessageText)(diagnostic.messageText, '.'); | ||
const defaultHint = this.list[diagnostic.code]?.hint || flattenDiagnosticMessageText(diagnostic.messageText, '.'); | ||
return this.prepareHint(defaultHint, diagnostic); | ||
} | ||
getHelpUrl(diagnostic) { | ||
var _a; | ||
const defaultHelpUrl = `https://stackoverflow.com/search?tab=votes&q=ts${diagnostic.code}`; | ||
return ((_a = this.list[diagnostic.code]) === null || _a === void 0 ? void 0 : _a.helpUrl) || defaultHelpUrl; | ||
return this.list[diagnostic.code]?.helpUrl || defaultHelpUrl; | ||
} | ||
@@ -43,9 +42,10 @@ /** | ||
prepareHint(hint, diagnostic) { | ||
var _a; | ||
const message = (0, typescript_1.flattenDiagnosticMessageText)(diagnostic.messageText, '. '); | ||
const message = flattenDiagnosticMessageText(diagnostic.messageText, '. '); | ||
// Prepare a replacement dictionary | ||
// e.g. {'{0}': 'p', '{1}': 'any'} for message "Parameter 'p' implicitly has an 'any' type." | ||
const replacements = ((_a = message | ||
const replacements = message | ||
// return ["'p'", "'any'"] | ||
.match(/'[^']+'/gm)) === null || _a === void 0 ? void 0 : _a.reduce((a, v, i) => (Object.assign(Object.assign({}, a), { [`{${i}}`]: v.replace(/^\W+|\W+$/g, '') })), {})) || {}; | ||
.match(/'[^']+'/gm) | ||
// converts ["'p'", "'any'"] to {'{0}': 'p', '{1}': 'any'} | ||
?.reduce((a, v, i) => ({ ...a, [`{${i}}`]: v.replace(/^\W+|\W+$/g, '') }), {}) || {}; | ||
const node = diagnostic.node; | ||
@@ -63,4 +63,3 @@ // Node related replacements | ||
} | ||
exports.HintCodeFixCollection = HintCodeFixCollection; | ||
function createCodeFixAction(fixName, changes, description) { | ||
export function createCodeFixAction(fixName, changes, description) { | ||
return { | ||
@@ -72,3 +71,2 @@ fixName, | ||
} | ||
exports.createCodeFixAction = createCodeFixAction; | ||
//# sourceMappingURL=hints-codefix-collection.js.map |
import { type DiagnosticWithLocation } from 'typescript'; | ||
import type { CodeHintList, DiagnosticWithContext } from './types'; | ||
import type { CodeHintList, DiagnosticWithContext } from './types.js'; | ||
/** | ||
@@ -4,0 +4,0 @@ * Provides access to useful hints for Diagnostics |
@@ -1,9 +0,7 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.HintsProvider = void 0; | ||
const typescript_1 = require("typescript"); | ||
import ts from 'typescript'; | ||
const { flattenDiagnosticMessageText } = ts; | ||
/** | ||
* Provides access to useful hints for Diagnostics | ||
*/ | ||
class HintsProvider { | ||
export class HintsProvider { | ||
constructor(list) { | ||
@@ -13,4 +11,3 @@ this.list = list; | ||
getHint(diagnostic) { | ||
var _a, _b; | ||
const conditionalHints = (_a = this.list[diagnostic.code]) === null || _a === void 0 ? void 0 : _a.hints; | ||
const conditionalHints = this.list[diagnostic.code]?.hints; | ||
if (conditionalHints !== undefined && diagnostic.node !== undefined) { | ||
@@ -23,9 +20,8 @@ for (const conditionalHint of conditionalHints) { | ||
} | ||
const defaultHint = ((_b = this.list[diagnostic.code]) === null || _b === void 0 ? void 0 : _b.hint) || (0, typescript_1.flattenDiagnosticMessageText)(diagnostic.messageText, '.'); | ||
const defaultHint = this.list[diagnostic.code]?.hint || flattenDiagnosticMessageText(diagnostic.messageText, '.'); | ||
return this.prepareHint(defaultHint, diagnostic); | ||
} | ||
getHelpUrl(diagnostic) { | ||
var _a; | ||
const defaultHelpUrl = `https://stackoverflow.com/search?tab=votes&q=ts${diagnostic.code}}`; | ||
return ((_a = this.list[diagnostic.code]) === null || _a === void 0 ? void 0 : _a.helpUrl) || defaultHelpUrl; | ||
const defaultHelpUrl = `https://stackoverflow.com/search?tab=votes&q=ts${diagnostic.code}`; | ||
return this.list[diagnostic.code]?.helpUrl || defaultHelpUrl; | ||
} | ||
@@ -36,9 +32,10 @@ /** | ||
prepareHint(hint, diagnostic) { | ||
var _a; | ||
const message = (0, typescript_1.flattenDiagnosticMessageText)(diagnostic.messageText, '. '); | ||
const message = flattenDiagnosticMessageText(diagnostic.messageText, '. '); | ||
// Prepare a replacement dictionary | ||
// e.g. {'{0}': 'p', '{1}': 'any'} for message "Parameter 'p' implicitly has an 'any' type." | ||
const replacements = ((_a = message | ||
const replacements = message | ||
// return ["'p'", "'any'"] | ||
.match(/'[^']+'/gm)) === null || _a === void 0 ? void 0 : _a.reduce((a, v, i) => (Object.assign(Object.assign({}, a), { [`{${i}}`]: v.replace(/^\W+|\W+$/g, '') })), {})) || {}; | ||
.match(/'[^']+'/gm) | ||
// converts ["'p'", "'any'"] to {'{0}': 'p', '{1}': 'any'} | ||
?.reduce((a, v, i) => ({ ...a, [`{${i}}`]: v.replace(/^\W+|\W+$/g, '') }), {}) || {}; | ||
const node = diagnostic.node; | ||
@@ -53,6 +50,7 @@ // Node related replacements | ||
hint = hint.replace(/{[^}]+}/gm, (key) => replacements[key] || key); | ||
// Make sure the hint is a single line | ||
hint = hint.replace(/(\r\n|\n|\r)/gm, '. '); | ||
return hint; | ||
} | ||
} | ||
exports.HintsProvider = HintsProvider; | ||
//# sourceMappingURL=hints-provider.js.map |
@@ -1,3 +0,3 @@ | ||
import { HintsProvider } from './hints-provider'; | ||
import { HintsProvider } from './hints-provider.js'; | ||
export declare const hints: HintsProvider; | ||
//# sourceMappingURL=hints.d.ts.map |
@@ -1,8 +0,6 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.hints = void 0; | ||
const utils_1 = require("@rehearsal/utils"); | ||
const typescript_1 = require("typescript"); | ||
const hints_provider_1 = require("./hints-provider"); | ||
exports.hints = new hints_provider_1.HintsProvider({ | ||
import { getTypeNameFromVariable, isVariableOfCatchClause } from '@rehearsal/ts-utils'; | ||
import ts from 'typescript'; | ||
import { HintsProvider } from './hints-provider.js'; | ||
const { isIdentifier, isFunctionDeclaration, isParameter, isReturnStatement } = ts; | ||
export const hints = new HintsProvider({ | ||
2322: { | ||
@@ -13,7 +11,7 @@ hint: `Type '{0}' is being returned or assigned, but type '{1}' is expected. Please convert type '{0}' to type '{1}', or return or assign a variable of type '{1}'`, | ||
{ | ||
when: (n) => (0, typescript_1.isReturnStatement)(n), | ||
when: (n) => isReturnStatement(n), | ||
hint: `The function expects to return '{1}', but '{0}' is returned. Please convert '{0}' value to '{1}' or update the function's return type.`, | ||
}, | ||
{ | ||
when: (n) => (0, typescript_1.isIdentifier)(n), | ||
when: (n) => isIdentifier(n), | ||
hint: `The variable '{node.text}' has type '{1}', but '{0}' is assigned. Please convert '{0}' to '{1}' or change variable's type.`, | ||
@@ -27,7 +25,7 @@ }, | ||
{ | ||
when: (n, _, c) => (0, utils_1.getTypeNameFromVariable)(n, c) === 'unknown', | ||
when: (n, _, c) => getTypeNameFromVariable(n, c) === 'unknown', | ||
hint: `Argument of type '{0}' is not assignable to parameter of type '{1}'. Consider specifying type of argument to be '{1}', using type assertion: '({node.fullText} as {1})', or using type guard: 'if ({node.fullText} instanceof {1}) { ... }'.`, | ||
}, | ||
{ | ||
when: (n, _, c) => (0, utils_1.getTypeNameFromVariable)(n, c) !== 'unknown', | ||
when: (n, _, c) => getTypeNameFromVariable(n, c) !== 'unknown', | ||
hint: `Argument of type '{0}' is not assignable to parameter of type '{1}'. Consider verifying both types, using type assertion: '({node.fullText} as string)', or using type guard: 'if ({node.fullText} instanceof string) { ... }'.`, | ||
@@ -43,3 +41,3 @@ }, | ||
{ | ||
when: (n) => !(0, typescript_1.isIdentifier)(n) || !(0, utils_1.isVariableOfCatchClause)(n), | ||
when: (n) => !isIdentifier(n) || !isVariableOfCatchClause(n), | ||
hint: `Object is of type '{0}'. Specify a type of {node.text}, use type assertion: \`({node.text} as DesiredType)\` or type guard: \`if ({node.text} instanceof DesiredType) { ... }\``, | ||
@@ -62,11 +60,11 @@ }, | ||
{ | ||
when: (n) => (0, typescript_1.isIdentifier)(n) && (0, typescript_1.isFunctionDeclaration)(n.parent), | ||
when: (n) => isIdentifier(n) && isFunctionDeclaration(n.parent), | ||
hint: `The function '{0}' is never called. Remove the function or use it.`, | ||
}, | ||
{ | ||
when: (n) => (0, typescript_1.isIdentifier)(n) && (0, typescript_1.isParameter)(n.parent), | ||
when: (n) => isIdentifier(n) && isParameter(n.parent), | ||
hint: `The parameter '{0}' is never used. Remove the parameter from function definition or use it.`, | ||
}, | ||
{ | ||
when: (n) => (0, typescript_1.isIdentifier)(n), | ||
when: (n) => isIdentifier(n), | ||
hint: `The variable '{0}' is never read or used. Remove the variable or use it.`, | ||
@@ -80,3 +78,6 @@ }, | ||
}, | ||
7016: { | ||
hint: `Could not find a declaration file for module '{0}'. Try installing the missing type or add a new declaration (.d.ts) file containing \`declare module '{0}';\`.`, | ||
}, | ||
}); | ||
//# sourceMappingURL=hints.js.map |
@@ -1,4 +0,6 @@ | ||
export { codefixes } from './codefixes'; | ||
export { hints } from './hints'; | ||
export * from './types'; | ||
export { codefixes, applyCodeFix, ContentDelegate } from './codefixes.js'; | ||
export { isInstallPackageCommand, makeCodeFixStrict } from './typescript-codefix-collection.js'; | ||
export { hints } from './hints.js'; | ||
export { getDiagnosticOrder } from './get-diagnostics.js'; | ||
export * from './types.js'; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -1,23 +0,6 @@ | ||
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.hints = exports.codefixes = void 0; | ||
var codefixes_1 = require("./codefixes"); | ||
Object.defineProperty(exports, "codefixes", { enumerable: true, get: function () { return codefixes_1.codefixes; } }); | ||
var hints_1 = require("./hints"); | ||
Object.defineProperty(exports, "hints", { enumerable: true, get: function () { return hints_1.hints; } }); | ||
__exportStar(require("./types"), exports); | ||
export { codefixes, applyCodeFix } from './codefixes.js'; | ||
export { isInstallPackageCommand, makeCodeFixStrict } from './typescript-codefix-collection.js'; | ||
export { hints } from './hints.js'; | ||
export { getDiagnosticOrder } from './get-diagnostics.js'; | ||
export * from './types.js'; | ||
//# sourceMappingURL=index.js.map |
@@ -28,4 +28,5 @@ import type { CodeFixAction, DiagnosticWithLocation, LanguageService, Node, Program, TypeChecker } from 'typescript'; | ||
export interface CodeFix { | ||
getErrorCodes: () => number[]; | ||
getCodeAction: (diagnostic: DiagnosticWithContext) => CodeFixAction | undefined; | ||
} | ||
//# sourceMappingURL=types.d.ts.map |
@@ -1,3 +0,2 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
export {}; | ||
//# sourceMappingURL=types.js.map |
@@ -1,4 +0,3 @@ | ||
import { type CodeFixAction } from 'typescript'; | ||
import { CodeFixCollectionFilter } from './types'; | ||
import type { CodeFixCollection, DiagnosticWithContext } from './types'; | ||
import { type CodeActionCommand, type CodeFixAction } from 'typescript'; | ||
import type { CodeFixCollection, CodeFixCollectionFilter, DiagnosticWithContext } from './types.js'; | ||
import type { Options as PrettierOptions } from 'prettier'; | ||
@@ -12,17 +11,9 @@ /** | ||
prettierConfigs: PrettierOptions | undefined; | ||
safeCodeFixNames: string[]; | ||
getFixesForDiagnostic(diagnostic: DiagnosticWithContext, filter: CodeFixCollectionFilter): CodeFixAction[]; | ||
/** | ||
* Checks if the codefix is safe to apply | ||
* | ||
* Filtering out codefixes based on fixName, | ||
* because `fixId` is not exists when there is only one/last error of a certain type in the file | ||
*/ | ||
private isCodeFixSafe; | ||
/** | ||
* Remove text changes contains loose typing (like usage of `any` type) | ||
*/ | ||
private makeCodeFixStrict; | ||
private getFormatCodeSettingsForFile; | ||
} | ||
export declare function isInstallPackageCommand(fix: CodeFixAction): fix is CodeFixAction & { | ||
commands: CodeActionCommand; | ||
}; | ||
export declare function makeCodeFixStrict(fix: CodeFixAction): CodeFixAction | undefined; | ||
//# sourceMappingURL=typescript-codefix-collection.d.ts.map |
@@ -1,6 +0,23 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.TypescriptCodeFixCollection = void 0; | ||
const path_1 = require("path"); | ||
const typescript_1 = require("typescript"); | ||
import { dirname, resolve } from 'node:path'; | ||
import { fileURLToPath } from 'node:url'; | ||
import Module from 'node:module'; | ||
import debug from 'debug'; | ||
import ts from 'typescript'; | ||
import { isCodeFixSupported } from './safe-codefixes.js'; | ||
import { Diagnostics } from './diagnosticInformationMap.generated.js'; | ||
const DEBUG_CALLBACK = debug('rehearsal:codefixes:TypeScriptCodeFixCollection'); | ||
const __filename = fileURLToPath(import.meta.url); | ||
const __dirname = dirname(__filename); | ||
const require = Module.createRequire(import.meta.url); | ||
const { SemicolonPreference, getDefaultFormatCodeSettings } = ts; | ||
const SUPPRESSED_ERRORS = [ | ||
{ | ||
code: Diagnostics.TS2339.code, | ||
message: 'False expression: Token end is child end', | ||
}, | ||
{ | ||
code: Diagnostics.TS2345.code, | ||
message: `Cannot read properties of undefined (reading 'flags')`, | ||
}, | ||
]; | ||
/** | ||
@@ -10,44 +27,3 @@ * Provides code fixes based on the Typescript's codefix collection. | ||
*/ | ||
class TypescriptCodeFixCollection { | ||
constructor() { | ||
// List of safe to apply code fix names | ||
// @see available-fix-names.md for the list of available fixNames | ||
this.safeCodeFixNames = [ | ||
'addMissingAsync', | ||
'addMissingAwait', | ||
'addMissingAwaitToInitializer', | ||
'addMissingConst', | ||
'addMissingConstraint', | ||
'addMissingDeclareProperty', | ||
'addMissingInvocationForDecorator', | ||
'addMissingNewOperator', | ||
'addOptionalPropertyUndefined', | ||
'addVoidToPromise', | ||
'annotateWithTypeFromJSDoc', | ||
'constructorForDerivedNeedSuperCall', | ||
'convertFunctionToEs6Class', | ||
'convertToTypeOnlyExport', | ||
'convertToTypeOnlyImport', | ||
'deleteUnmatchedParameter', | ||
'disableJsDiagnostics', | ||
'extendsInterfaceBecomesImplements', | ||
'fixAwaitInSyncFunction', | ||
'fixEnableJsxFlag', | ||
'fixImportNonExportedMember', | ||
'fixMissingAttributes', | ||
'fixMissingMember', | ||
'fixMissingProperties', | ||
'fixOverrideModifier', | ||
'fixReturnTypeInAsyncFunction', | ||
'import', | ||
'inferFromUsage', | ||
'invalidImportSyntax', | ||
'jdocTypes', | ||
'removeUnnecessaryAwait', | ||
'requireInTs', | ||
'strictClassInitialization', | ||
'unusedIdentifier', | ||
'useDefaultImport', // useDefaultImport.ts | ||
]; | ||
} | ||
export class TypescriptCodeFixCollection { | ||
getFixesForDiagnostic(diagnostic, filter) { | ||
@@ -65,10 +41,25 @@ const languageService = diagnostic.service; | ||
}; | ||
const fixes = languageService.getCodeFixesAtPosition(diagnostic.file.fileName, diagnostic.start, diagnostic.start + diagnostic.length, [diagnostic.code], this.getFormatCodeSettingsForFile(diagnostic.file.fileName), userPreferences); | ||
let fixes = []; | ||
try { | ||
fixes = languageService.getCodeFixesAtPosition(diagnostic.file.fileName, diagnostic.start, diagnostic.start + diagnostic.length, [diagnostic.code], this.getFormatCodeSettingsForFile(diagnostic.file.fileName), userPreferences); | ||
} | ||
catch (e) { | ||
const code = diagnostic.code; | ||
const supressedFound = SUPPRESSED_ERRORS.find((d) => d.code == code); | ||
const supressError = !!supressedFound && e instanceof Error && e.message.includes(supressedFound?.message); | ||
DEBUG_CALLBACK('getCodeFixesAtPosition threw an exception: %s %s\n %s', diagnostic.code, diagnostic.file.fileName, e); | ||
if (!supressError) { | ||
throw new Error(`An unknown error occured when attemping to getCodeFixesAtPosition for file: ${diagnostic.file.fileName} due to TS${diagnostic.code} ${diagnostic.messageText}`); | ||
} | ||
} | ||
const filteredCodeFixes = []; | ||
for (let fix of fixes) { | ||
if (filter.safeFixes && !this.isCodeFixSafe(fix)) { | ||
if (filter.safeFixes && !isCodeFixSupported(fix.fixName)) { | ||
continue; | ||
} | ||
if (filter.strictTyping) { | ||
const strictCodeFix = this.makeCodeFixStrict(fix); | ||
let strictCodeFix = makeCodeFixStrict(fix); | ||
if (strictCodeFix === undefined && isInstallPackageCommand(fix)) { | ||
strictCodeFix = fix; | ||
} | ||
if (!strictCodeFix) { | ||
@@ -83,42 +74,3 @@ continue; | ||
} | ||
/** | ||
* Checks if the codefix is safe to apply | ||
* | ||
* Filtering out codefixes based on fixName, | ||
* because `fixId` is not exists when there is only one/last error of a certain type in the file | ||
*/ | ||
isCodeFixSafe(fix) { | ||
return this.safeCodeFixNames.includes(fix.fixName); | ||
} | ||
/** | ||
* Remove text changes contains loose typing (like usage of `any` type) | ||
*/ | ||
makeCodeFixStrict(fix) { | ||
// Filtering out all text changes contain `any` | ||
const safeChanges = []; | ||
for (const changes of fix.changes) { | ||
const safeTextChanges = []; | ||
for (const textChanges of changes.textChanges) { | ||
// Don't return dummy function declarations | ||
if (textChanges.newText.includes('throw new Error')) { | ||
continue; | ||
} | ||
// Covers: `: any`, `| any`, `<any`, `any>`, `any |`, and sane cases with `any[]` | ||
const anyTypeUsageRegex = /[:<|]\s*any|any(\[])*\s*[|>]/i; | ||
if (anyTypeUsageRegex.test(textChanges.newText)) { | ||
continue; | ||
} | ||
safeTextChanges.push(textChanges); | ||
} | ||
if (safeTextChanges.length) { | ||
safeChanges.push(Object.assign(Object.assign({}, changes), { textChanges: safeTextChanges })); | ||
} | ||
} | ||
if (safeChanges.length) { | ||
return Object.assign(Object.assign({}, fix), { changes: safeChanges }); | ||
} | ||
return undefined; | ||
} | ||
getFormatCodeSettingsForFile(filePath) { | ||
var _a, _b; | ||
if (this.hasPrettier === undefined) { | ||
@@ -139,19 +91,23 @@ let prettierConfig = null; | ||
} | ||
const tsFormatCodeOptions = (0, typescript_1.getDefaultFormatCodeSettings)(); | ||
const tsFormatCodeOptions = getDefaultFormatCodeSettings(); | ||
let useSemicolons = true; | ||
let indentSize = (_a = tsFormatCodeOptions.tabSize) !== null && _a !== void 0 ? _a : 2; | ||
let indentSize = tsFormatCodeOptions.tabSize ?? 2; | ||
let convertTabsToSpaces = true; | ||
if (this.prettierConfigs) { | ||
useSemicolons = this.prettierConfigs.semi !== false; | ||
indentSize = (_b = this.prettierConfigs.tabWidth) !== null && _b !== void 0 ? _b : indentSize; | ||
indentSize = this.prettierConfigs.tabWidth ?? indentSize; | ||
convertTabsToSpaces = this.prettierConfigs.useTabs !== true; | ||
} | ||
return Object.assign(Object.assign({}, tsFormatCodeOptions), { baseIndentSize: indentSize, convertTabsToSpaces, | ||
indentSize, semicolons: useSemicolons ? typescript_1.SemicolonPreference.Insert : typescript_1.SemicolonPreference.Remove }); | ||
return { | ||
...tsFormatCodeOptions, | ||
baseIndentSize: indentSize, | ||
convertTabsToSpaces, | ||
indentSize, | ||
semicolons: useSemicolons ? SemicolonPreference.Insert : SemicolonPreference.Remove, | ||
}; | ||
} | ||
} | ||
exports.TypescriptCodeFixCollection = TypescriptCodeFixCollection; | ||
function importPrettier(fromPath) { | ||
const pkg = getPackageInfo('prettier', fromPath); | ||
const main = (0, path_1.resolve)(pkg.path); | ||
const main = resolve(pkg.path); | ||
return require(main); | ||
@@ -166,5 +122,48 @@ } | ||
return { | ||
path: (0, path_1.dirname)(packageJSONPath), | ||
path: dirname(packageJSONPath), | ||
}; | ||
} | ||
export function isInstallPackageCommand(fix) { | ||
return fix.fixId === 'installTypesPackage' && !!fix.commands; | ||
} | ||
export function makeCodeFixStrict(fix) { | ||
// Filtering out all text changes contain `any` | ||
const safeChanges = []; | ||
for (const changes of fix.changes) { | ||
const safeTextChanges = []; | ||
for (const textChanges of changes.textChanges) { | ||
// Don't return dummy function declarations | ||
if (textChanges.newText.includes('throw new Error')) { | ||
continue; | ||
} | ||
const neverTypeUsageRegex = /(=>|[:<|])\s*never|never(\[])*\s*[|>]/i; | ||
if (neverTypeUsageRegex.test(textChanges.newText)) { | ||
continue; | ||
} | ||
// Covers: `: any`, `| any`, `<any`, `any>`, `any |`, `=> any`, and same cases with `any[]` | ||
const anyTypeUsageRegex = /(=>|[:<|])\s*any|any(\[])*\s*[|>]/i; | ||
if (anyTypeUsageRegex.test(textChanges.newText)) { | ||
continue; | ||
} | ||
// Covers: `: object`, `| object`, `<object`, `object>`, `object |`, `=> object`, and same cases with `object[]` | ||
const objectTypeUsageRegex = /(=>|[:<|])\s*object|object(\[])*\s*[|>]/i; | ||
if (objectTypeUsageRegex.test(textChanges.newText)) { | ||
continue; | ||
} | ||
// Covers cases with broken type signatures, like: `() =>` | ||
const brokenTypeSignatures = /\(\) =>\s*$/i; | ||
if (brokenTypeSignatures.test(textChanges.newText)) { | ||
continue; | ||
} | ||
safeTextChanges.push(textChanges); | ||
} | ||
if (safeTextChanges.length) { | ||
safeChanges.push({ ...changes, textChanges: safeTextChanges }); | ||
} | ||
} | ||
if (safeChanges.length) { | ||
return { ...fix, changes: safeChanges }; | ||
} | ||
return undefined; | ||
} | ||
//# sourceMappingURL=typescript-codefix-collection.js.map |
{ | ||
"name": "@rehearsal/codefixes", | ||
"version": "1.0.5-beta", | ||
"version": "1.0.5", | ||
"description": "Rehearsal Dependency Codefixes Collection", | ||
@@ -18,2 +18,10 @@ "keywords": [ | ||
"license": "BSD-2-Clause", | ||
"type": "module", | ||
"exports": { | ||
".": { | ||
"types": "./dist/src/index.d.ts", | ||
"import": "./dist/src/index.js" | ||
}, | ||
"./*": "./*" | ||
}, | ||
"main": "dist/src/index.js", | ||
@@ -25,13 +33,22 @@ "types": "dist/src/index.d.ts", | ||
"dependencies": { | ||
"@rehearsal/utils": "1.0.5-beta" | ||
"debug": "^4.3.4", | ||
"@rehearsal/ts-utils": "1.0.5" | ||
}, | ||
"devDependencies": { | ||
"@types/prettier": "^2.7.2", | ||
"prettier": "^2.8.1", | ||
"vitest": "^0.23.4" | ||
"@vitest/coverage-c8": "^0.30.1", | ||
"fixturify": "^3.0.0", | ||
"fixturify-project": "^5.2.0", | ||
"fs-extra": "^11.1.1", | ||
"prettier": "^2.8.7", | ||
"tmp": "^0.2.1", | ||
"vitest": "^0.30.0", | ||
"@rehearsal/migrate": "1.0.5", | ||
"@rehearsal/reporter": "1.0.5", | ||
"@rehearsal/service": "1.0.5" | ||
}, | ||
"peerDependencies": { | ||
"typescript": "^4.9 || ^5.0" | ||
"typescript": "^5.1" | ||
}, | ||
"packageManager": "pnpm@7.12.1", | ||
"packageManager": "pnpm@8.2.0", | ||
"engines": { | ||
@@ -46,6 +63,5 @@ "node": ">=14.16.0" | ||
"lint": "npm-run-all lint:*", | ||
"lint:eslint": "eslint --fix . --ext .ts", | ||
"lint:tsc-src": "tsc --noEmit", | ||
"lint:tsc-test": "tsc --noEmit --project test/tsconfig.json", | ||
"test": "vitest --run", | ||
"test": "vitest --run --config ./vitest.config.ts --coverage", | ||
"test:watch": "vitest --coverage --watch", | ||
@@ -52,0 +68,0 @@ "version": "pnpm version" |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
1400458
80
16891
1
Yes
3
11
+ Added@rehearsal/ts-utils@1.0.5
+ Addeddebug@^4.3.4
+ Added@rehearsal/ts-utils@1.0.5(transitive)
+ Addeddebug@4.4.0(transitive)
+ Addedms@2.1.3(transitive)
- Removed@rehearsal/utils@1.0.5-beta
- Removed@rehearsal/utils@1.0.5-beta(transitive)