Files
ry.kazcloud.dev/node_modules/@volar/kit/lib/createChecker.js

309 lines
14 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createTypeScriptChecker = createTypeScriptChecker;
exports.createTypeScriptInferredChecker = createTypeScriptInferredChecker;
const language_service_1 = require("@volar/language-service");
const typescript_1 = require("@volar/typescript");
const path = require("typesafe-path/posix");
const ts = require("typescript");
const vscode_languageserver_textdocument_1 = require("vscode-languageserver-textdocument");
const vscode_uri_1 = require("vscode-uri");
const createServiceEnvironment_1 = require("./createServiceEnvironment");
const utils_1 = require("./utils");
function createTypeScriptChecker(languagePlugins, languageServicePlugins, tsconfig, includeProjectReference = false, setup) {
const tsconfigPath = (0, utils_1.asPosix)(tsconfig);
return createTypeScriptCheckerWorker(languagePlugins, languageServicePlugins, tsconfigPath, () => {
return ts.parseJsonSourceFileConfigFileContent(ts.readJsonConfigFile(tsconfigPath, ts.sys.readFile), ts.sys, path.dirname(tsconfigPath), undefined, tsconfigPath, undefined, languagePlugins.map(plugin => plugin.typescript?.extraFileExtensions ?? []).flat());
}, includeProjectReference, setup);
}
function createTypeScriptInferredChecker(languagePlugins, languageServicePlugins, getScriptFileNames, compilerOptions = utils_1.defaultCompilerOptions, setup) {
return createTypeScriptCheckerWorker(languagePlugins, languageServicePlugins, undefined, () => {
return {
options: compilerOptions,
fileNames: getScriptFileNames(),
errors: [],
};
}, false, setup);
}
const fsFileSnapshots = (0, language_service_1.createUriMap)();
function createTypeScriptCheckerWorker(languagePlugins, languageServicePlugins, configFileName, getCommandLine, includeProjectReference, setup) {
let settings = {};
const didChangeWatchedFilesCallbacks = new Set();
const env = (0, createServiceEnvironment_1.createServiceEnvironment)(() => settings);
env.onDidChangeWatchedFiles = cb => {
didChangeWatchedFilesCallbacks.add(cb);
return {
dispose: () => {
didChangeWatchedFilesCallbacks.delete(cb);
},
};
};
const language = (0, language_service_1.createLanguage)([
...languagePlugins,
{ getLanguageId: uri => (0, typescript_1.resolveFileLanguageId)(uri.path) },
], (0, language_service_1.createUriMap)(ts.sys.useCaseSensitiveFileNames), (uri, includeFsFiles) => {
if (!includeFsFiles) {
return;
}
const cache = fsFileSnapshots.get(uri);
const fileName = (0, utils_1.asFileName)(uri);
const modifiedTime = ts.sys.getModifiedTime?.(fileName)?.valueOf();
if (!cache || cache[0] !== modifiedTime) {
if (ts.sys.fileExists(fileName)) {
const text = ts.sys.readFile(fileName);
const snapshot = text !== undefined ? ts.ScriptSnapshot.fromString(text) : undefined;
fsFileSnapshots.set(uri, [modifiedTime, snapshot]);
}
else {
fsFileSnapshots.set(uri, [modifiedTime, undefined]);
}
}
const snapshot = fsFileSnapshots.get(uri)?.[1];
if (snapshot) {
language.scripts.set(uri, snapshot);
}
else {
language.scripts.delete(uri);
}
});
const [projectHost, languageService] = createTypeScriptCheckerLanguageService(env, language, languageServicePlugins, configFileName, getCommandLine, setup);
const projectReferenceLanguageServices = new Map();
if (includeProjectReference) {
const tsconfigs = new Set();
const tsLs = languageService.context.inject('typescript/languageService');
const projectReferences = tsLs.getProgram()?.getResolvedProjectReferences();
if (configFileName) {
tsconfigs.add((0, utils_1.asPosix)(configFileName));
}
projectReferences?.forEach(visit);
function visit(ref) {
if (ref && !tsconfigs.has(ref.sourceFile.fileName)) {
tsconfigs.add(ref.sourceFile.fileName);
const projectReferenceLanguageService = createTypeScriptCheckerLanguageService(env, language, languageServicePlugins, ref.sourceFile.fileName, () => ref.commandLine, setup);
projectReferenceLanguageServices.set(ref.sourceFile.fileName, projectReferenceLanguageService);
ref.references?.forEach(visit);
}
}
}
return {
// apis
check,
fixErrors,
printErrors,
getRootFileNames: () => {
const fileNames = projectHost.getScriptFileNames();
for (const [projectHost] of projectReferenceLanguageServices.values()) {
fileNames.push(...projectHost.getScriptFileNames());
}
return [...new Set(fileNames)];
},
language,
// settings
get settings() {
return settings;
},
set settings(v) {
settings = v;
},
// file events
fileCreated(fileName) {
fileEvent(fileName, 1);
},
fileUpdated(fileName) {
fileEvent(fileName, 2);
},
fileDeleted(fileName) {
fileEvent(fileName, 3);
},
};
function fileEvent(fileName, type) {
fileName = (0, utils_1.asPosix)(fileName);
for (const cb of didChangeWatchedFilesCallbacks) {
cb({ changes: [{ uri: (0, utils_1.asUri)(fileName).toString(), type }] });
}
}
function check(fileName) {
fileName = (0, utils_1.asPosix)(fileName);
const uri = (0, utils_1.asUri)(fileName);
const languageService = getLanguageServiceForFile(fileName);
return languageService.getDiagnostics(uri);
}
async function fixErrors(fileName, diagnostics, only, writeFile) {
fileName = (0, utils_1.asPosix)(fileName);
const uri = (0, utils_1.asUri)(fileName);
const languageService = getLanguageServiceForFile(fileName);
const sourceScript = languageService.context.language.scripts.get(uri);
if (sourceScript) {
const document = languageService.context.documents.get(uri, sourceScript.languageId, sourceScript.snapshot);
const range = { start: document.positionAt(0), end: document.positionAt(document.getText().length) };
const codeActions = await languageService.getCodeActions(uri, range, { diagnostics, only, triggerKind: 1 });
if (codeActions) {
for (let i = 0; i < codeActions.length; i++) {
codeActions[i] = await languageService.resolveCodeAction(codeActions[i]);
}
const edits = codeActions.map(codeAction => codeAction.edit).filter((edit) => !!edit);
if (edits.length) {
const rootEdit = edits[0];
(0, language_service_1.mergeWorkspaceEdits)(rootEdit, ...edits.slice(1));
for (const uri in rootEdit.changes ?? {}) {
const edits = rootEdit.changes[uri];
if (edits.length) {
const parsedUri = vscode_uri_1.URI.parse(uri);
const editFile = languageService.context.language.scripts.get(parsedUri);
if (editFile) {
const editDocument = languageService.context.documents.get(parsedUri, editFile.languageId, editFile.snapshot);
const newString = vscode_languageserver_textdocument_1.TextDocument.applyEdits(editDocument, edits);
await writeFile((0, utils_1.asFileName)(parsedUri), newString);
}
}
}
for (const change of rootEdit.documentChanges ?? []) {
if ('textDocument' in change) {
const changeUri = vscode_uri_1.URI.parse(change.textDocument.uri);
const editFile = languageService.context.language.scripts.get(changeUri);
if (editFile) {
const editDocument = languageService.context.documents.get(changeUri, editFile.languageId, editFile.snapshot);
const newString = vscode_languageserver_textdocument_1.TextDocument.applyEdits(editDocument, change.edits);
await writeFile((0, utils_1.asFileName)(changeUri), newString);
}
}
// TODO: CreateFile | RenameFile | DeleteFile
}
}
}
}
}
function printErrors(fileName, diagnostics, rootPath = process.cwd()) {
let text = formatErrors(fileName, diagnostics, rootPath);
for (const diagnostic of diagnostics) {
text = text.replace(`TS${diagnostic.code}`, (diagnostic.source ?? '') + (diagnostic.code ? `(${diagnostic.code})` : ''));
}
return text;
}
function formatErrors(fileName, diagnostics, rootPath) {
fileName = (0, utils_1.asPosix)(fileName);
const uri = (0, utils_1.asUri)(fileName);
const languageService = getLanguageServiceForFile(fileName);
const sourceScript = languageService.context.language.scripts.get(uri);
const document = languageService.context.documents.get(uri, sourceScript.languageId, sourceScript.snapshot);
const errors = diagnostics.map(diagnostic => ({
category: diagnostic.severity === 1 ? ts.DiagnosticCategory.Error : ts.DiagnosticCategory.Warning,
code: diagnostic.code,
file: ts.createSourceFile(fileName, document.getText(), ts.ScriptTarget.JSON),
start: document.offsetAt(diagnostic.range.start),
length: document.offsetAt(diagnostic.range.end) - document.offsetAt(diagnostic.range.start),
messageText: diagnostic.message,
}));
const text = ts.formatDiagnosticsWithColorAndContext(errors, {
getCurrentDirectory: () => rootPath,
getCanonicalFileName: fileName => ts.sys.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase(),
getNewLine: () => ts.sys.newLine,
});
return text;
}
function getLanguageServiceForFile(fileName) {
if (!includeProjectReference) {
return languageService;
}
fileName = (0, utils_1.asPosix)(fileName);
for (const [_1, languageService] of projectReferenceLanguageServices.values()) {
const tsLs = languageService.context.inject('typescript/languageService');
if (tsLs.getProgram()?.getSourceFile(fileName)) {
return languageService;
}
}
return languageService;
}
}
function createTypeScriptCheckerLanguageService(env, language, languageServicePlugins, configFileName, getCommandLine, setup) {
let commandLine = getCommandLine();
let projectVersion = 0;
let shouldCheckRootFiles = false;
const resolvedFileNameByCommandLine = new WeakMap();
const projectHost = {
getCurrentDirectory: () => env.workspaceFolders.length
? (0, utils_1.asFileName)(env.workspaceFolders[0])
: process.cwd(),
getCompilationSettings: () => {
return commandLine.options;
},
getProjectReferences: () => {
return commandLine.projectReferences;
},
getProjectVersion: () => {
checkRootFilesUpdate();
return projectVersion.toString();
},
getScriptFileNames: () => {
checkRootFilesUpdate();
let fileNames = resolvedFileNameByCommandLine.get(commandLine);
if (!fileNames) {
fileNames = commandLine.fileNames.map(utils_1.asPosix);
resolvedFileNameByCommandLine.set(commandLine, fileNames);
}
return fileNames;
},
};
const project = {
typescript: {
configFileName,
sys: ts.sys,
uriConverter: {
asFileName: utils_1.asFileName,
asUri: utils_1.asUri,
},
...(0, typescript_1.createLanguageServiceHost)(ts, ts.sys, language, utils_1.asUri, projectHost),
},
};
setup?.({ language, project });
const languageService = (0, language_service_1.createLanguageService)(language, languageServicePlugins, env, project);
env.onDidChangeWatchedFiles?.(({ changes }) => {
const tsLs = languageService.context.inject('typescript/languageService');
const program = tsLs.getProgram();
for (const change of changes) {
const changeUri = vscode_uri_1.URI.parse(change.uri);
const fileName = (0, utils_1.asFileName)(changeUri);
if (change.type === 2) {
if (program?.getSourceFile(fileName)) {
projectVersion++;
}
}
else if (change.type === 3) {
if (program?.getSourceFile(fileName)) {
projectVersion++;
shouldCheckRootFiles = true;
break;
}
}
else if (change.type === 1) {
shouldCheckRootFiles = true;
break;
}
}
});
return [projectHost, languageService];
function checkRootFilesUpdate() {
if (!shouldCheckRootFiles) {
return;
}
shouldCheckRootFiles = false;
const newCommandLine = getCommandLine();
if (!arrayItemsEqual(newCommandLine.fileNames, commandLine.fileNames)) {
commandLine.fileNames = newCommandLine.fileNames;
projectVersion++;
}
}
}
function arrayItemsEqual(a, b) {
if (a.length !== b.length) {
return false;
}
const set = new Set(a);
for (const file of b) {
if (!set.has(file)) {
return false;
}
}
return true;
}
//# sourceMappingURL=createChecker.js.map