typescript-eslint-language-service
Version:
TypeScript language service plugin for ESLint
189 lines • 7.97 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ESLintAdapter = exports.translateToCodeFixesFromESLintResult = exports.isIntersect = exports.translateToDiagnosticsFromESLintResult = void 0;
const path_1 = __importDefault(require("path"));
const typescript_1 = __importDefault(require("typescript"));
const eslint_1 = require("eslint");
const parser_1 = require("@typescript-eslint/parser");
const errors_1 = require("./errors");
const consts_1 = require("./consts");
function translateToDiagnosticsFromESLintResult(result, sourceFile) {
return result.map(({ message, severity, ruleId, line, column, endLine, endColumn }) => {
const messageText = `[${ruleId}] ${message}`;
const category = severity === 2
? typescript_1.default.DiagnosticCategory.Error
: severity === 1
? typescript_1.default.DiagnosticCategory.Warning
: typescript_1.default.DiagnosticCategory.Suggestion;
/**
* ESLint uses 1-started index. On the other hand, TypeScript 0-started index.
* So we should minus from ESLint result's row/column to create TypeScript position.
*/
const start = typescript_1.default.getPositionOfLineAndCharacter(sourceFile, line - 1, column - 1);
const end = endLine && endColumn ? typescript_1.default.getPositionOfLineAndCharacter(sourceFile, endLine - 1, endColumn - 1) : start;
const length = Math.max(0, end - start);
const diagnostic = {
file: sourceFile,
category,
code: consts_1.TS_LANGSERVICE_ESLINT_DIAGNOSTIC_ERROR_CODE,
messageText,
start,
length,
};
return diagnostic;
});
}
exports.translateToDiagnosticsFromESLintResult = translateToDiagnosticsFromESLintResult;
function isIntersect(message, range, sourceFile) {
const { line, column, endLine, endColumn } = message;
const mStart = typescript_1.default.getPositionOfLineAndCharacter(sourceFile, line - 1, column - 1);
const mEnd = endLine && endColumn ? typescript_1.default.getPositionOfLineAndCharacter(sourceFile, endLine - 1, endColumn - 1) : mStart;
return !(mEnd < range.start || mStart > range.end);
}
exports.isIntersect = isIntersect;
function translateToCodeFixesFromESLintResult(result, fileName) {
return result.reduce((acc, { message, ruleId, fix }) => {
if (!fix) {
return acc;
}
const rid = ruleId || "eslint";
const rangeStart = fix.range[0];
const rangeLength = fix.range[1] ? fix.range[1] - fix.range[0] : 0;
const codeFixAction = {
description: `Fix: ${message}`,
fixId: rid,
fixName: rid,
changes: [
{
fileName,
isNewFile: false,
textChanges: [
{
span: {
start: rangeStart,
length: rangeLength,
},
newText: fix.text,
},
],
},
],
};
return [...acc, codeFixAction];
}, []);
}
exports.translateToCodeFixesFromESLintResult = translateToCodeFixesFromESLintResult;
class ESLintAdapter {
constructor({ logger, configProvider, getSourceFile }) {
this.linter = new eslint_1.Linter();
this.logger = logger;
this.configProvider = configProvider;
this.getSourceFile = getSourceFile;
this.ignoredFilepathMap = new Map();
}
convertToESLintSourceCode(src, filename, options) {
const code = src.getFullText();
const originalOpt = options !== null && options !== void 0 ? options : {};
const { ast, scopeManager, services, visitorKeys } = (0, parser_1.parseForESLint)(code, {
...originalOpt,
filePath: filename,
comment: true,
loc: true,
range: true,
tokens: true,
warnOnUnsupportedTypeScriptVersion: false,
});
const source = new eslint_1.SourceCode({
text: code,
ast,
scopeManager,
parserServices: services,
visitorKeys,
});
return source;
}
getESLintResult(fileName, sourceFile) {
if (this.ignoredFilepathMap.get(fileName) === true)
return [];
const configArray = this.configProvider.getConfigArrayForFile(fileName);
const configFileContent = configArray.extractConfig(fileName).toCompatibleObjectAsConfigFileContent();
if (!isParserModuleNameValid(configFileContent.parser, path_1.default.join("@typescript-eslint", "parser"))) {
throw new errors_1.InvalidParserError();
}
const parserOptions = (configFileContent.parserOptions ? configFileContent.parserOptions : {});
const sourceCode = this.convertToESLintSourceCode(sourceFile, fileName, parserOptions);
// See https://github.com/eslint/eslint/blob/v6.1.0/lib/linter/linter.js#L1130
return this.linter.verify(sourceCode, configArray, { filename: fileName });
}
checkFileToBeIgnored(fileName) {
if (fileName.indexOf("node_modules" + path_1.default.sep) !== -1)
return;
if (!fileName.endsWith(".ts") && !fileName.endsWith(".tsx"))
return;
Promise.resolve()
.then(() => new eslint_1.ESLint())
.then(eslint => eslint.isPathIgnored(fileName))
.then(result => this.ignoredFilepathMap.set(fileName, result));
}
getSemanticDiagnostics(delegate, fileName) {
const original = delegate(fileName);
try {
const sourceFile = this.getSourceFile(fileName);
if (!sourceFile) {
return original;
}
const eslintResult = this.getESLintResult(fileName, sourceFile);
return [...original, ...translateToDiagnosticsFromESLintResult(eslintResult, sourceFile)];
}
catch (error) {
if (error instanceof Error) {
this.logger(error.message);
if (error.stack) {
this.logger(error.stack);
}
}
else {
this.logger(`${error}`);
}
return original;
}
}
getCodeFixesAtPosition(delegate, fileName, start, end, errorCodes, formatOptions, preferences) {
const original = delegate(fileName, start, end, errorCodes, formatOptions, preferences);
try {
if (!errorCodes.includes(consts_1.TS_LANGSERVICE_ESLINT_DIAGNOSTIC_ERROR_CODE)) {
return original;
}
const sourceFile = this.getSourceFile(fileName);
if (!sourceFile) {
return original;
}
const eslintResult = this.getESLintResult(fileName, sourceFile);
return [
...original,
...translateToCodeFixesFromESLintResult(eslintResult.filter(r => isIntersect(r, { start, end }, sourceFile)), fileName),
];
}
catch (error) {
this.logger(error.message ? error.message : "unknow error");
return original;
}
return original;
}
}
exports.ESLintAdapter = ESLintAdapter;
function isParserModuleNameValid(parserModuleSpecifier, parserModuleName) {
if (!parserModuleSpecifier)
return false;
try {
const p = require.resolve(parserModuleSpecifier);
return p.indexOf(parserModuleName) !== -1;
}
catch (e) {
return false;
}
}
//# sourceMappingURL=eslint-adapter.js.map