"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createProject = void 0; const language_service_1 = require("@volar/language-service"); const path = require("path-browserify"); const vscode = require("vscode-languageserver"); const serverConfig_1 = require("./utils/serverConfig"); const uriMap_1 = require("./utils/uriMap"); const typescript_1 = require("@volar/typescript"); const globalSnapshots = new WeakMap(); async function createProject(context) { let projectVersion = 0; let token = context.server.runtimeEnv.getCancellationToken(); let languageService; const tsToken = { isCancellationRequested() { return token.isCancellationRequested; }, throwIfCancellationRequested() { }, }; const { uriToFileName, fileNameToUri, fs } = context.server.runtimeEnv; const env = { uriToFileName, fileNameToUri, fs, console: context.server.runtimeEnv.console, locale: context.workspaces.initParams.locale, workspaceUri: context.project.workspaceUri, rootUri: context.project.rootUri, clientCapabilities: context.workspaces.initParams.capabilities, getConfiguration: context.server.configurationHost?.getConfiguration, onDidChangeConfiguration: context.server.configurationHost?.onDidChangeConfiguration, onDidChangeWatchedFiles: context.server.onDidChangeWatchedFiles, }; if (!globalSnapshots.has(fs)) { globalSnapshots.set(fs, (0, uriMap_1.createUriMap)(fileNameToUri)); } const askedFiles = (0, uriMap_1.createUriMap)(fileNameToUri); const languageHost = { workspacePath: uriToFileName(context.project.workspaceUri.toString()), rootPath: uriToFileName(context.project.rootUri.toString()), getProjectVersion: () => projectVersion.toString(), getScriptFileNames: () => parsedCommandLine.fileNames, getScriptSnapshot: (fileName) => { askedFiles.pathSet(fileName, true); const doc = context.workspaces.documents.data.pathGet(fileName); if (doc) { return doc.getSnapshot(); } const fsSnapshot = globalSnapshots.get(fs).pathGet(fileName); if (fsSnapshot) { return fsSnapshot; } }, getLanguageId: (fileName) => context.workspaces.documents.data.pathGet(fileName)?.languageId, getCancellationToken: () => tsToken, getCompilationSettings: () => parsedCommandLine.options, getLocalizedDiagnosticMessages: context.workspaces.tsLocalized ? () => context.workspaces.tsLocalized : undefined, getProjectReferences: () => parsedCommandLine.projectReferences, }; const docChangeWatcher = context.workspaces.documents.onDidChangeContent(() => { projectVersion++; token = context.server.runtimeEnv.getCancellationToken(); }); const fileWatch = env.onDidChangeWatchedFiles?.(params => { onWorkspaceFilesChanged(params.changes); }); let existingOptions; for (const plugin of context.workspaces.plugins) { if (plugin.resolveExistingOptions) { existingOptions = plugin.resolveExistingOptions(existingOptions); } } let parsedCommandLine = await createParsedCommandLine(context.workspaces.ts, env, uriToFileName(context.project.rootUri.toString()), context.project.tsConfig, context.workspaces.plugins, existingOptions); let config = (context.project.workspaceUri.scheme === 'file' ? (0, serverConfig_1.loadConfig)(context.server.runtimeEnv.console, context.server.runtimeEnv.uriToFileName(context.project.workspaceUri.toString()), context.workspaces.initOptions.configFilePath) : {}) ?? {}; for (const plugin of context.workspaces.plugins) { if (plugin.resolveConfig) { config = await plugin.resolveConfig(config, { ...context, env, host: languageHost, }); } } await syncRootScriptSnapshots(); return { context, tsConfig: context.project.tsConfig, languageHost, getLanguageService, getLanguageServiceDontCreate: () => languageService, getParsedCommandLine: () => parsedCommandLine, tryAddFile: (fileName) => { if (!parsedCommandLine.fileNames.includes(fileName)) { parsedCommandLine.fileNames.push(fileName); projectVersion++; token = context.server.runtimeEnv.getCancellationToken(); } }, askedFiles, dispose, }; function getLanguageService() { if (!languageService) { languageService = (0, language_service_1.createLanguageService)({ typescript: context.workspaces.ts }, env, config, languageHost); } return languageService; } async function syncRootScriptSnapshots() { const promises = []; let dirty = false; for (const fileName of parsedCommandLine.fileNames) { const uri = fileNameToUri(fileName); if (!globalSnapshots.get(fs).uriGet(uri)) { dirty = true; promises.push(updateRootScriptSnapshot(uri)); } } await Promise.all(promises); return dirty; } async function updateRootScriptSnapshot(uri) { const text = await context.server.runtimeEnv.fs.readFile(uri); globalSnapshots.get(fs).uriSet(uri, text !== undefined ? { getText: (start, end) => text.substring(start, end), getLength: () => text.length, getChangeRange: () => undefined, } : undefined); } async function onWorkspaceFilesChanged(changes) { const oldProjectVersion = projectVersion; const creates = changes.filter(change => change.type === vscode.FileChangeType.Created); if (creates.length) { parsedCommandLine = await createParsedCommandLine(context.workspaces.ts, env, uriToFileName(context.project.rootUri.toString()), context.project.tsConfig, context.workspaces.plugins, existingOptions); if (await syncRootScriptSnapshots()) { projectVersion++; } } await Promise.all(changes.map(async (change) => { if (askedFiles.uriGet(change.uri) && globalSnapshots.get(fs).uriGet(change.uri)) { if (change.type === vscode.FileChangeType.Changed) { await updateRootScriptSnapshot(change.uri); } else if (change.type === vscode.FileChangeType.Deleted) { globalSnapshots.get(fs).uriSet(change.uri, undefined); } projectVersion++; } })); if (oldProjectVersion !== projectVersion) { token = context.server.runtimeEnv.getCancellationToken(); } } function dispose() { languageService?.dispose(); fileWatch?.dispose(); docChangeWatcher.dispose(); } } exports.createProject = createProject; async function createParsedCommandLine(ts, env, rootPath, tsConfig, plugins, existingOptions) { const extraFileExtensions = plugins.map(plugin => plugin.extraFileExtensions ?? []).flat(); if (ts) { const sys = (0, typescript_1.createSys)(ts, { ...env, onDidChangeWatchedFiles: undefined, }); let content; let sysVersion; let newSysVersion = await sys.sync(); while (sysVersion !== newSysVersion) { sysVersion = newSysVersion; try { if (typeof tsConfig === 'string') { const config = ts.readJsonConfigFile(tsConfig, sys.readFile); content = ts.parseJsonSourceFileConfigFileContent(config, sys, path.dirname(tsConfig), existingOptions, tsConfig, undefined, extraFileExtensions); } else { content = ts.parseJsonConfigFileContent({ files: [] }, sys, rootPath, { ...tsConfig, ...existingOptions }, rootPath + '/jsconfig.json', undefined, extraFileExtensions); } // fix https://github.com/johnsoncodehk/volar/issues/1786 // https://github.com/microsoft/TypeScript/issues/30457 // patching ts server broke with outDir + rootDir + composite/incremental content.options.outDir = undefined; content.fileNames = content.fileNames.map(fileName => fileName.replace(/\\/g, '/')); } catch { // will be failed if web fs host first result not ready } newSysVersion = await sys.sync(); } if (content) { return content; } } return { errors: [], fileNames: [], options: {}, }; } //# sourceMappingURL=project.js.map