diff --git a/.eslintrc.js b/.eslintrc.js index ed738f0..6a50fd5 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -62,6 +62,7 @@ module.exports = { ], '@typescript-eslint/require-await': 'error', '@typescript-eslint/no-floating-promises': 'error', + 'no-restricted-imports': ['error', 'typescript/lib/tsserverlibrary'], }, overrides: [ { @@ -71,10 +72,11 @@ module.exports = { }, }, { - files: ['**/*.test.ts', '**/*.spec.ts'], + files: ['**/*.test.ts', '**/*.spec.ts', '**/test-helpers*.ts'], rules: { '@typescript-eslint/no-non-null-assertion': 'off', '@typescript-eslint/explicit-function-return-type': 'off', + 'no-restricted-imports': 'off', }, }, ], diff --git a/package-lock.json b/package-lock.json index 349b722..e233d4d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5100,30 +5100,41 @@ "dev": true }, "cheerio": { - "version": "1.0.0-rc.3", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.3.tgz", - "integrity": "sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==", - "dev": true, - "requires": { - "css-select": "~1.2.0", - "dom-serializer": "~0.1.1", - "entities": "~1.1.1", - "htmlparser2": "^3.9.1", - "lodash": "^4.15.0", - "parse5": "^3.0.1" + "version": "1.0.0-rc.5", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.5.tgz", + "integrity": "sha512-yoqps/VCaZgN4pfXtenwHROTp8NG6/Hlt4Jpz2FEP0ZJQ+ZUkVDd0hAPDNKhj3nakpfPt/CNs57yEtxD1bXQiw==", + "dev": true, + "requires": { + "cheerio-select-tmp": "^0.1.0", + "dom-serializer": "~1.2.0", + "domhandler": "^4.0.0", + "entities": "~2.1.0", + "htmlparser2": "^6.0.0", + "parse5": "^6.0.0", + "parse5-htmlparser2-tree-adapter": "^6.0.0" }, "dependencies": { "parse5": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz", - "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==", - "dev": true, - "requires": { - "@types/node": "*" - } + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true } } }, + "cheerio-select-tmp": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/cheerio-select-tmp/-/cheerio-select-tmp-0.1.1.tgz", + "integrity": "sha512-YYs5JvbpU19VYJyj+F7oYrIE2BOll1/hRU7rEy/5+v9BzkSo3bK81iAeeQEMI92vRIxz677m72UmJUiVwwgjfQ==", + "dev": true, + "requires": { + "css-select": "^3.1.2", + "css-what": "^4.0.0", + "domelementtype": "^2.1.0", + "domhandler": "^4.0.0", + "domutils": "^2.4.4" + } + }, "chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", @@ -6095,21 +6106,22 @@ "dev": true }, "css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-3.1.2.tgz", + "integrity": "sha512-qmss1EihSuBNWNNhHjxzxSfJoFBM/lERB/Q4EnsJQQC62R2evJDW481091oAdOr9uh46/0n4nrg0It5cAnj1RA==", "dev": true, "requires": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" + "boolbase": "^1.0.0", + "css-what": "^4.0.0", + "domhandler": "^4.0.0", + "domutils": "^2.4.3", + "nth-check": "^2.0.0" } }, "css-what": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", - "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-4.0.0.tgz", + "integrity": "sha512-teijzG7kwYfNVsUh2H/YN62xW3KK9YhXEgSlbxMlcyjPNvdKJqFx5lrwlJgoFP1ZHlB89iGDlo/JyshKeRhv5A==", "dev": true }, "cssom": { @@ -6466,19 +6478,20 @@ } }, "dom-serializer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", - "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.2.0.tgz", + "integrity": "sha512-n6kZFH/KlCrqs/1GHMOd5i2fd/beQHuehKdWvNNffbGHTr/almdhuVvTVFb3V7fglz+nC50fFusu3lY33h12pA==", "dev": true, "requires": { - "domelementtype": "^1.3.0", - "entities": "^1.1.1" + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "entities": "^2.0.0" } }, "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz", + "integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==", "dev": true }, "domexception": { @@ -6499,22 +6512,23 @@ } }, "domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.0.0.tgz", + "integrity": "sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA==", "dev": true, "requires": { - "domelementtype": "1" + "domelementtype": "^2.1.0" } }, "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.4.4.tgz", + "integrity": "sha512-jBC0vOsECI4OMdD0GC9mGn7NXPLb+Qt6KW1YDQzeQYRUFKmNG8lh7mO5HiELfr+lLQE7loDVI4QcAxV80HS+RA==", "dev": true, "requires": { - "dom-serializer": "0", - "domelementtype": "1" + "dom-serializer": "^1.0.1", + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0" } }, "dot-prop": { @@ -6614,9 +6628,9 @@ } }, "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", "dev": true }, "env-ci": { @@ -8631,30 +8645,15 @@ "dev": true }, "htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", - "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.0.0.tgz", + "integrity": "sha512-numTQtDZMoh78zJpaNdJ9MXb2cv5G3jwUoe3dMQODubZvLoGvTE/Ofp6sHvH8OGKcN/8A47pGLi/k58xHP/Tfw==", "dev": true, "requires": { - "domelementtype": "^1.3.1", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.1.1" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.4.4", + "entities": "^2.0.0" } }, "http-cache-semantics": { @@ -16533,12 +16532,12 @@ } }, "nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz", + "integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==", "dev": true, "requires": { - "boolbase": "~1.0.0" + "boolbase": "^1.0.0" } }, "number-is-nan": { @@ -16941,6 +16940,23 @@ "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", "dev": true }, + "parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "requires": { + "parse5": "^6.0.1" + }, + "dependencies": { + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + } + } + }, "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", diff --git a/packages/extension/.vscodeignore b/packages/extension/.vscodeignore index 2efb11d..4037176 100644 --- a/packages/extension/.vscodeignore +++ b/packages/extension/.vscodeignore @@ -17,3 +17,6 @@ test-environment/** .nvmrc .releaserc prettier.config.js +node_modules +scripts/ +jest.config.js diff --git a/packages/extension/package-lock.json b/packages/extension/package-lock.json index d806a08..886f44c 100644 --- a/packages/extension/package-lock.json +++ b/packages/extension/package-lock.json @@ -1,6 +1,6 @@ { "name": "ts-quickfixes-extension", - "version": "1.4.0", + "version": "1.4.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/extension/package.json b/packages/extension/package.json index 8100c35..9d923e8 100644 --- a/packages/extension/package.json +++ b/packages/extension/package.json @@ -3,7 +3,7 @@ "private": true, "displayName": "TS QuickFixes", "description": "Quick fixes for typescript", - "version": "1.4.0", + "version": "1.4.1", "license": "MIT", "publisher": "tamj0rd2", "engines": { diff --git a/packages/plugin/src/code-fixes/fix.ts b/packages/plugin/src/code-fixes/fix.ts index 710f691..6275031 100644 --- a/packages/plugin/src/code-fixes/fix.ts +++ b/packages/plugin/src/code-fixes/fix.ts @@ -1,10 +1,12 @@ -import ts from 'typescript/lib/tsserverlibrary' import { Logger } from '../providers/provider' import { MissingVariableMembersArgs } from './missing-variable-members-fix' +export type ts = typeof import('typescript/lib/tsserverlibrary') + export interface CodeFixArgs { program: ts.Program logger: Logger + ts: ts } export abstract class CodeFix implements ts.CodeFixAction { @@ -12,12 +14,14 @@ export abstract class CodeFix implements ts.CodeFixAction { public abstract readonly description: string public abstract readonly changes: ts.FileTextChanges[] + protected readonly ts: ts protected readonly program: ts.Program protected readonly typeChecker: ts.TypeChecker protected readonly logger: Logger private readonly formattingOpts = { useSingleQuotes: true } constructor(args: CodeFixArgs) { + this.ts = args.ts this.program = args.program this.typeChecker = args.program.getTypeChecker() this.logger = args.logger @@ -87,10 +91,10 @@ export abstract class CodeFix implements ts.CodeFixAction { const expectedMembers: ts.Symbol[] = [] symbol.members?.forEach((member) => expectedMembers.push(member)) - if (ts.isInterfaceDeclaration(node)) { + if (this.ts.isInterfaceDeclaration(node)) { const inheritedMemberSymbols = node.heritageClauses ?.flatMap((clause) => clause.types.map((type) => type.expression)) - .filter(ts.isIdentifier) + .filter(this.ts.isIdentifier) .map(this.getTypeByIdentifier) .flatMap(this.getExpectedMemberSymbols) @@ -103,7 +107,7 @@ export abstract class CodeFix implements ts.CodeFixAction { } protected isObjectDeclarationLike = (node: ts.Node | undefined): node is ObjectDeclarationLike => { - return !!node && (ts.isTypeLiteralNode(node) || ts.isInterfaceDeclaration(node)) + return !!node && (this.ts.isTypeLiteralNode(node) || this.ts.isInterfaceDeclaration(node)) } protected getTypeByIdentifier = (identifier: ts.Identifier): ObjectDeclarationLike => { @@ -119,70 +123,73 @@ export abstract class CodeFix implements ts.CodeFixAction { protected createMemberForSymbol = (memberSymbol: ts.Symbol): ts.PropertyAssignment => { const createInitializer = (memberSymbol: ts.Symbol): ts.Expression => { const propertySignature = memberSymbol.valueDeclaration - if (!ts.isPropertySignature(propertySignature)) + if (!this.ts.isPropertySignature(propertySignature)) throw new Error('The given symbol is not a property signature') - if (propertySignature.type && ts.isLiteralTypeNode(propertySignature.type)) { - if (propertySignature.type.literal.kind === ts.SyntaxKind.TrueKeyword) { - return ts.factory.createTrue() + if (propertySignature.type && this.ts.isLiteralTypeNode(propertySignature.type)) { + if (propertySignature.type.literal.kind === this.ts.SyntaxKind.TrueKeyword) { + return this.ts.factory.createTrue() } - if (propertySignature.type.literal.kind === ts.SyntaxKind.FalseKeyword) { - return ts.factory.createFalse() + if (propertySignature.type.literal.kind === this.ts.SyntaxKind.FalseKeyword) { + return this.ts.factory.createFalse() } } - if (propertySignature.type && ts.isArrayTypeNode(propertySignature.type)) { - return ts.factory.createArrayLiteralExpression() + if (propertySignature.type && this.ts.isArrayTypeNode(propertySignature.type)) { + return this.ts.factory.createArrayLiteralExpression() } - if (propertySignature.type && ts.isArrayTypeNode(propertySignature.type)) { - return ts.factory.createArrayLiteralExpression() + if (propertySignature.type && this.ts.isArrayTypeNode(propertySignature.type)) { + return this.ts.factory.createArrayLiteralExpression() } const type = this.typeChecker.getTypeAtLocation(propertySignature) - if (type.flags & ts.TypeFlags.String) { - return ts.factory.createStringLiteral('todo', this.formattingOpts.useSingleQuotes) + if (type.flags & this.ts.TypeFlags.String) { + return this.ts.factory.createStringLiteral('todo', this.formattingOpts.useSingleQuotes) } - if (type.flags & ts.TypeFlags.Number) { - return ts.factory.createNumericLiteral(0) + if (type.flags & this.ts.TypeFlags.Number) { + return this.ts.factory.createNumericLiteral(0) } - if (type.flags & ts.TypeFlags.Boolean) { - return ts.factory.createFalse() + if (type.flags & this.ts.TypeFlags.Boolean) { + return this.ts.factory.createFalse() } - if (type.flags & ts.TypeFlags.EnumLiteral && type.isUnionOrIntersection() && type.aliasSymbol) { + if (type.flags & this.ts.TypeFlags.EnumLiteral && type.isUnionOrIntersection() && type.aliasSymbol) { const firstEnumMember = type.aliasSymbol.exports?.keys().next().value.toString() return firstEnumMember - ? ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier(type.aliasSymbol.name), - ts.factory.createIdentifier(firstEnumMember), + ? this.ts.factory.createPropertyAccessExpression( + this.ts.factory.createIdentifier(type.aliasSymbol.name), + this.ts.factory.createIdentifier(firstEnumMember), ) - : ts.factory.createNull() + : this.ts.factory.createNull() } const typeDeclaration = type.getSymbol()?.valueDeclaration ?? type.getSymbol()?.declarations[0] if ( typeDeclaration && - (ts.isTypeLiteralNode(typeDeclaration) || ts.isInterfaceDeclaration(typeDeclaration)) + (this.ts.isTypeLiteralNode(typeDeclaration) || this.ts.isInterfaceDeclaration(typeDeclaration)) ) { const memberSymbols = this.getExpectedMemberSymbols(typeDeclaration) - return ts.factory.createObjectLiteralExpression(memberSymbols.map(this.createMemberForSymbol), true) + return this.ts.factory.createObjectLiteralExpression( + memberSymbols.map(this.createMemberForSymbol), + true, + ) } if (type.getSymbol()?.name === 'Date') { - return ts.factory.createNewExpression(ts.factory.createIdentifier('Date'), undefined, []) + return this.ts.factory.createNewExpression(this.ts.factory.createIdentifier('Date'), undefined, []) } - return ts.factory.createNull() + return this.ts.factory.createNull() } - return ts.factory.createPropertyAssignment( - ts.factory.createIdentifier(memberSymbol.name), + return this.ts.factory.createPropertyAssignment( + this.ts.factory.createIdentifier(memberSymbol.name), createInitializer(memberSymbol), ) } diff --git a/packages/plugin/src/code-fixes/missing-argument-members-fix.spec.ts b/packages/plugin/src/code-fixes/missing-argument-members-fix.spec.ts index 292f330..262633f 100644 --- a/packages/plugin/src/code-fixes/missing-argument-members-fix.spec.ts +++ b/packages/plugin/src/code-fixes/missing-argument-members-fix.spec.ts @@ -1,3 +1,4 @@ +import ts from 'typescript/lib/tsserverlibrary' import { MissingArgumentMembersFix } from './missing-argument-members-fix' import { createDummyLogger, createTestProgram, FsMocker, getNodeRange } from '../test-helpers' @@ -16,6 +17,7 @@ describe('Fill Missing Argument Member', () => { const initializerLocation = getNodeRange(fileContent, argumentValue) const fix = new MissingArgumentMembersFix({ filePath, + ts, program: createTestProgram([filePath], MissingArgumentMembersFix.supportedErrorCodes), logger: createDummyLogger(), ...initializerLocation, @@ -47,6 +49,7 @@ describe('Fill Missing Argument Member', () => { const initializerLocation = getNodeRange(fileContent, argumentValue) const fix = new MissingArgumentMembersFix({ filePath, + ts, program: createTestProgram([filePath], MissingArgumentMembersFix.supportedErrorCodes), logger: createDummyLogger(), ...initializerLocation, @@ -82,6 +85,7 @@ describe('Fill Missing Argument Member', () => { const initializerLocation = getNodeRange(fileContent, argumentValue) const fix = new MissingArgumentMembersFix({ filePath, + ts, program: createTestProgram([filePath], MissingArgumentMembersFix.supportedErrorCodes), logger: createDummyLogger(), ...initializerLocation, diff --git a/packages/plugin/src/code-fixes/missing-argument-members-fix.ts b/packages/plugin/src/code-fixes/missing-argument-members-fix.ts index 7127c34..ebbfd30 100644 --- a/packages/plugin/src/code-fixes/missing-argument-members-fix.ts +++ b/packages/plugin/src/code-fixes/missing-argument-members-fix.ts @@ -1,4 +1,3 @@ -import ts from 'typescript/lib/tsserverlibrary' import { CodeFix, CodeFixArgs, ObjectDeclarationLike } from './fix' interface MissingArgumentMembersArgs extends CodeFixArgs { @@ -24,17 +23,17 @@ export class MissingArgumentMembersFix extends CodeFix { const { value, expectedType } = this.getArgumentInfo(sourceFile, args) const undeclaredMembers = this.getUndeclaredMemberSymbols(value, expectedType) - const replacedInitializer = ts.factory.createObjectLiteralExpression( + const replacedInitializer = this.ts.factory.createObjectLiteralExpression( [...value.properties, ...undeclaredMembers.map(this.createMemberForSymbol)], false, ) - const newText = ts + const newText = this.ts .createPrinter( - { newLine: ts.NewLineKind.LineFeed }, + { newLine: this.ts.NewLineKind.LineFeed }, { substituteNode: (_, node) => (node === value ? replacedInitializer : node) }, ) - .printNode(ts.EmitHint.Unspecified, value, sourceFile) + .printNode(this.ts.EmitHint.Unspecified, value, sourceFile) this.changes = [ { @@ -58,17 +57,20 @@ export class MissingArgumentMembersFix extends CodeFix { const argumentValue = this.findChildNode( sourceFile, (node): node is ts.ObjectLiteralExpression => - matchesPosition(node) && ts.isObjectLiteralExpression(node), + matchesPosition(node) && this.ts.isObjectLiteralExpression(node), ) - const callExpression = this.findParentNode(argumentValue, ts.isCallExpression) + const callExpression = this.findParentNode(argumentValue, this.ts.isCallExpression) const argumentIndex = callExpression.arguments.findIndex((arg) => arg === argumentValue) if (argumentIndex < 0) { TODO('Invalid argument index') } const { symbol: identifierSymbol } = this.typeChecker.getTypeAtLocation(callExpression.expression) - if (!identifierSymbol.valueDeclaration || !ts.isFunctionDeclaration(identifierSymbol.valueDeclaration)) { + if ( + !identifierSymbol.valueDeclaration || + !this.ts.isFunctionDeclaration(identifierSymbol.valueDeclaration) + ) { TODO('no value declaration') } @@ -83,7 +85,7 @@ export class MissingArgumentMembersFix extends CodeFix { return { value: argumentValue, expectedType } } - if (ts.isTypeReferenceNode(expectedType) && ts.isIdentifier(expectedType.typeName)) { + if (this.ts.isTypeReferenceNode(expectedType) && this.ts.isIdentifier(expectedType.typeName)) { return { value: argumentValue, expectedType: this.getTypeByIdentifier(expectedType.typeName) } } diff --git a/packages/plugin/src/code-fixes/missing-variable-members-fix.spec.ts b/packages/plugin/src/code-fixes/missing-variable-members-fix.spec.ts index 48aa2e2..8cd4d77 100644 --- a/packages/plugin/src/code-fixes/missing-variable-members-fix.spec.ts +++ b/packages/plugin/src/code-fixes/missing-variable-members-fix.spec.ts @@ -24,6 +24,7 @@ describe('missing variable members fix', () => { const errorLocation = getNodeRange(fileContent, 'targetVariable') const fix = new MissingVariableMembersFix({ + ts, filePath, program: createTestProgram([filePath], MissingVariableMembersFix.supportedErrorCodes), logger: createDummyLogger(), @@ -61,6 +62,7 @@ describe('missing variable members fix', () => { const errorLocation = getNodeRange(fileContent, 'targetVariable') const fix = new MissingVariableMembersFix({ + ts, filePath, program: createTestProgram([filePath], MissingVariableMembersFix.supportedErrorCodes), logger: createDummyLogger(), @@ -105,6 +107,7 @@ describe('missing variable members fix', () => { const errorLocation = getNodeRange(fileContent, 'targetVariable') const fix = new MissingVariableMembersFix({ + ts, filePath, program: createTestProgram( [filePath, inheritedFilePath], @@ -151,6 +154,7 @@ describe('missing variable members fix', () => { const errorLocation = getNodeRange(fileContent, 'targetVariable') const fix = new MissingVariableMembersFix({ + ts, filePath, program: createTestProgram([filePath], MissingVariableMembersFix.supportedErrorCodes), logger: createDummyLogger(), @@ -191,6 +195,7 @@ describe('missing variable members fix', () => { const errorLocation = getNodeRange(fileContent, 'targetVariable') const fix = new MissingVariableMembersFix({ + ts, filePath, program: createTestProgram([filePath], MissingVariableMembersFix.supportedErrorCodes), logger: createDummyLogger(), @@ -236,6 +241,7 @@ describe('missing variable members fix', () => { const errorLocation = getNodeRange(fileContent, 'targetVariable') const fix = new MissingVariableMembersFix({ + ts, filePath, program: createTestProgram([filePath], MissingVariableMembersFix.supportedErrorCodes), logger: createDummyLogger(), @@ -270,6 +276,7 @@ describe('missing variable members fix', () => { const errorLocation = getNodeRange(fileContent, 'targetVariable') const fix = new MissingVariableMembersFix({ + ts, filePath, program: createTestProgram([filePath], MissingVariableMembersFix.supportedErrorCodes), logger: createDummyLogger(), @@ -302,6 +309,7 @@ describe('missing variable members fix', () => { const errorLocation = getNodeRange(targetFileContent, 'targetVariable') const fix = new MissingVariableMembersFix({ + ts, filePath: targetFilePath, program: createTestProgram(FsMocker.fileNames, MissingVariableMembersFix.supportedErrorCodes), logger: createDummyLogger(), diff --git a/packages/plugin/src/code-fixes/missing-variable-members-fix.ts b/packages/plugin/src/code-fixes/missing-variable-members-fix.ts index 2ffc3ab..71f736d 100644 --- a/packages/plugin/src/code-fixes/missing-variable-members-fix.ts +++ b/packages/plugin/src/code-fixes/missing-variable-members-fix.ts @@ -1,4 +1,3 @@ -import ts from 'typescript/lib/tsserverlibrary' import { CodeFix, CodeFixArgs, NodeRange, ObjectDeclarationLike } from './fix' export interface MissingVariableMembersArgs extends CodeFixArgs { @@ -24,17 +23,17 @@ export class MissingVariableMembersFix extends CodeFix { const sourceFile = this.getSourceFile(args.filePath) const { initializer, type } = this.getVariableInfo(sourceFile, args) const undeclaredMembers = this.getUndeclaredMemberSymbols(initializer, type) - const replacedInitializer = ts.factory.createObjectLiteralExpression( + const replacedInitializer = this.ts.factory.createObjectLiteralExpression( [...initializer.properties, ...undeclaredMembers.map(this.createMemberForSymbol)], true, ) - const newText = ts + const newText = this.ts .createPrinter( - { newLine: ts.NewLineKind.LineFeed }, + { newLine: this.ts.NewLineKind.LineFeed }, { substituteNode: (_, node) => (node === initializer ? replacedInitializer : node) }, ) - .printNode(ts.EmitHint.Unspecified, initializer, sourceFile) + .printNode(this.ts.EmitHint.Unspecified, initializer, sourceFile) this.changes = [ { @@ -57,20 +56,23 @@ export class MissingVariableMembersFix extends CodeFix { const matchesPosition = this.curryMatchesPosition(sourceFile, { start, end }) const identifier = this.findChildNode( sourceFile, - (node): node is ts.Identifier => matchesPosition(node) && ts.isIdentifier(node), + (node): node is ts.Identifier => matchesPosition(node) && this.ts.isIdentifier(node), `Could not find a node at pos ${start}:${end}`, ) - const { initializer, type: typeReference } = this.findParentNode(identifier, ts.isVariableDeclaration) + const { initializer, type: typeReference } = this.findParentNode( + identifier, + this.ts.isVariableDeclaration, + ) - if (!initializer || !ts.isObjectLiteralExpression(initializer)) { + if (!initializer || !this.ts.isObjectLiteralExpression(initializer)) { throw new Error('No initializer for the given variable') } - if (!typeReference || !ts.isTypeReferenceNode(typeReference)) { + if (!typeReference || !this.ts.isTypeReferenceNode(typeReference)) { throw new Error('No type reference for the given variable') } - if (!ts.isIdentifier(typeReference.typeName)) { + if (!this.ts.isIdentifier(typeReference.typeName)) { throw new Error('Type reference does not have an identifier on it') } diff --git a/packages/plugin/src/index.ts b/packages/plugin/src/index.ts index cf1c6e3..651eaad 100644 --- a/packages/plugin/src/index.ts +++ b/packages/plugin/src/index.ts @@ -1,6 +1,5 @@ import { CodeFixProvider } from './providers' -// eslint-disable-next-line @typescript-eslint/no-unused-vars function init(modules: Modules): { create: CreateFn } { function create(info: ts.server.PluginCreateInfo): ts.LanguageService { const originalLanguageService = info.languageService @@ -38,7 +37,7 @@ function init(modules: Modules): { create: CreateFn } { return { ...originalLanguageService, getCodeFixesAtPosition: logOnError( - new CodeFixProvider(originalLanguageService, logger).getCodeFixesAtPosition, + new CodeFixProvider(originalLanguageService, logger, modules.typescript).getCodeFixesAtPosition, ), } } diff --git a/packages/plugin/src/members/member.ts b/packages/plugin/src/members/member.ts deleted file mode 100644 index 7c1f123..0000000 --- a/packages/plugin/src/members/member.ts +++ /dev/null @@ -1,22 +0,0 @@ -import ts from 'typescript/lib/tsserverlibrary' - -export const MemberType = { - String: 'todo', - Number: 0, - Union: null, - BuiltIn: null, - Boolean: false, - Never: 'never', - Array: '[]', -} as const -export type MemberType = typeof MemberType[keyof typeof MemberType] - -export abstract class Member { - constructor(public readonly name: string) {} - - public createPropertyAssignment(): ts.PropertyAssignment { - return ts.factory.createPropertyAssignment(this.name, this.createInitializer()) - } - - protected abstract createInitializer(): ts.Expression -} diff --git a/packages/plugin/src/members/object-literal-member.ts b/packages/plugin/src/members/object-literal-member.ts deleted file mode 100644 index 842baf2..0000000 --- a/packages/plugin/src/members/object-literal-member.ts +++ /dev/null @@ -1,30 +0,0 @@ -import ts from 'typescript/lib/tsserverlibrary' -import { Member } from './member' - -export class ObjectLiteralMember extends Member { - constructor(name: string, public readonly members: Member[] = []) { - super(name) - } - - public static createTopLevel(members?: Member[]): ObjectLiteralMember { - return new ObjectLiteralMember('topLevel', members) - } - - protected createInitializer(): ts.Expression { - return ts.factory.createObjectLiteralExpression( - this.toArray().map((member) => member.createPropertyAssignment()), - ) - } - - public addMember(member: Member): void { - this.members.push(member) - } - - public concat(objectLiteral: ObjectLiteralMember): ObjectLiteralMember { - return new ObjectLiteralMember(this.name, [...this.toArray(), ...objectLiteral.toArray()]) - } - - public toArray(): Member[] { - return Object.values(this.members) - } -} diff --git a/packages/plugin/src/members/simple-types.ts b/packages/plugin/src/members/simple-types.ts deleted file mode 100644 index 0203b1f..0000000 --- a/packages/plugin/src/members/simple-types.ts +++ /dev/null @@ -1,57 +0,0 @@ -import ts from 'typescript/lib/tsserverlibrary' -import { Member } from './member' - -export class EnumMember extends Member { - constructor(name: string, public readonly enumName: string, public readonly enumMember: string) { - super(name) - } - - protected createInitializer(): ts.Expression { - return ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier(this.enumName), - ts.factory.createIdentifier(this.enumMember), - ) - } -} - -export class StringMember extends Member { - public constructor(name: string, public readonly value = '') { - super(name) - } - - protected createInitializer(): ts.Expression { - return ts.factory.createStringLiteral(this.value) - } -} - -export class NumberMember extends Member { - public constructor(name: string, public readonly value = 0) { - super(name) - } - - protected createInitializer(): ts.Expression { - return ts.factory.createNumericLiteral(this.value) - } -} - -export class BooleanMember extends Member { - public constructor(name: string, public readonly value = false) { - super(name) - } - - protected createInitializer(): ts.Expression { - return this.value ? ts.factory.createTrue() : ts.factory.createFalse() - } -} - -export class ArrayMember extends Member { - protected createInitializer(): ts.Expression { - return ts.factory.createArrayLiteralExpression() - } -} - -export class UnimplementedMember extends Member { - protected createInitializer(): ts.Expression { - return ts.factory.createNull() - } -} diff --git a/packages/plugin/src/providers/code-fix-provider.ts b/packages/plugin/src/providers/code-fix-provider.ts index 5aa9dba..cc7aa13 100644 --- a/packages/plugin/src/providers/code-fix-provider.ts +++ b/packages/plugin/src/providers/code-fix-provider.ts @@ -26,6 +26,7 @@ export class CodeFixProvider extends BaseProvider { tryAddFixAction( () => new MissingVariableMembersFix({ + ts: this.ts, start, end, filePath: fileName, @@ -39,6 +40,7 @@ export class CodeFixProvider extends BaseProvider { tryAddFixAction( () => new MissingArgumentMembersFix({ + ts: this.ts, start, end, filePath: fileName, @@ -62,12 +64,4 @@ export class CodeFixProvider extends BaseProvider { ...customActions, ] } - - private tryAddFix(fixes: ts.CodeFixAction[], fixFactory: () => ts.CodeFixAction): void { - try { - fixes.push(fixFactory()) - } catch (err) { - this.logger.error(err) - } - } } diff --git a/packages/plugin/src/providers/provider.ts b/packages/plugin/src/providers/provider.ts index 130c5d7..c6fe248 100644 --- a/packages/plugin/src/providers/provider.ts +++ b/packages/plugin/src/providers/provider.ts @@ -2,6 +2,7 @@ export abstract class BaseProvider { constructor( protected readonly originalLanguageService: ts.LanguageService, protected readonly logger: Logger, + protected readonly ts: typeof import('typescript/lib/tsserverlibrary'), ) {} protected getProgram(): ts.Program {