deno.land / x / deno@v1.28.2 / cli / tsc / 99_main_compiler.js
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
// @ts-check/// <reference path="./compiler.d.ts" />// deno-lint-ignore-file no-undef
// This module is the entry point for "compiler" isolate, ie. the one// that is created when Deno needs to type check TypeScript, and in some// instances convert TypeScript to JavaScript.
// Removes the `__proto__` for security reasons.// https://tc39.es/ecma262/#sec-get-object.prototype.__proto__delete Object.prototype.__proto__;
((window) => { /** @type {DenoCore} */ const core = window.Deno.core; const ops = core.ops;
let logDebug = false; let logSource = "JS";
/** @type {string=} */ let cwd;
// The map from the normalized specifier to the original. // TypeScript normalizes the specifier in its internal processing, // but the original specifier is needed when looking up the source from the runtime. // This map stores that relationship, and the original can be restored by the // normalized specifier. // See: https://github.com/denoland/deno/issues/9277#issuecomment-769653834 /** @type {Map<string, string>} */ const normalizedToOriginalMap = new Map();
/** * @param {unknown} value * @returns {value is ts.CreateSourceFileOptions} */ function isCreateSourceFileOptions(value) { return value != null && typeof value === "object" && "languageVersion" in value; }
/** * @param {ts.ScriptTarget | ts.CreateSourceFileOptions | undefined} versionOrOptions * @returns {ts.CreateSourceFileOptions} */ function getCreateSourceFileOptions(versionOrOptions) { return isCreateSourceFileOptions(versionOrOptions) ? versionOrOptions : { languageVersion: versionOrOptions ?? ts.ScriptTarget.ESNext }; }
function setLogDebug(debug, source) { logDebug = debug; if (source) { logSource = source; } }
function printStderr(msg) { core.print(msg, true); }
function debug(...args) { if (logDebug) { const stringifiedArgs = args.map((arg) => typeof arg === "string" ? arg : JSON.stringify(arg) ).join(" "); printStderr(`DEBUG ${logSource} - ${stringifiedArgs}\n`); } }
function error(...args) { const stringifiedArgs = args.map((arg) => typeof arg === "string" || arg instanceof Error ? String(arg) : JSON.stringify(arg) ).join(" "); printStderr(`ERROR ${logSource} = ${stringifiedArgs}\n`); }
class AssertionError extends Error { constructor(msg) { super(msg); this.name = "AssertionError"; } }
function assert(cond, msg = "Assertion failed.") { if (!cond) { throw new AssertionError(msg); } }
// deno-fmt-ignore const base64abc = [ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "+", "/", ];
/** Taken from https://deno.land/std/encoding/base64.ts */ function convertToBase64(data) { const uint8 = core.encode(data); let result = "", i; const l = uint8.length; for (i = 2; i < l; i += 3) { result += base64abc[uint8[i - 2] >> 2]; result += base64abc[((uint8[i - 2] & 0x03) << 4) | (uint8[i - 1] >> 4)]; result += base64abc[((uint8[i - 1] & 0x0f) << 2) | (uint8[i] >> 6)]; result += base64abc[uint8[i] & 0x3f]; } if (i === l + 1) { // 1 octet yet to write result += base64abc[uint8[i - 2] >> 2]; result += base64abc[(uint8[i - 2] & 0x03) << 4]; result += "=="; } if (i === l) { // 2 octets yet to write result += base64abc[uint8[i - 2] >> 2]; result += base64abc[((uint8[i - 2] & 0x03) << 4) | (uint8[i - 1] >> 4)]; result += base64abc[(uint8[i - 1] & 0x0f) << 2]; result += "="; } return result; }
class SpecifierIsCjsCache { /** @type {Set<string>} */ #cache = new Set();
/** @param {[string, ts.Extension]} param */ add([specifier, ext]) { if (ext === ".cjs" || ext === ".d.cts" || ext === ".cts") { this.#cache.add(specifier); } }
has(specifier) { return this.#cache.has(specifier); } }
// In the case of the LSP, this will only ever contain the assets. /** @type {Map<string, ts.SourceFile>} */ const sourceFileCache = new Map();
/** @type {string[]=} */ let scriptFileNamesCache;
/** @type {Map<string, string>} */ const scriptVersionCache = new Map();
/** @type {Map<string, boolean>} */ const isNodeSourceFileCache = new Map();
const isCjsCache = new SpecifierIsCjsCache();
/** * @param {ts.CompilerOptions | ts.MinimalResolutionCacheHost} settingsOrHost * @returns {ts.CompilerOptions} */ function getCompilationSettings(settingsOrHost) { if (typeof settingsOrHost.getCompilationSettings === "function") { return settingsOrHost.getCompilationSettings(); } return /** @type {ts.CompilerOptions} */ (settingsOrHost); }
// We need to use a custom document registry in order to provide source files // with an impliedNodeFormat to the ts language service
/** @type {Map<string, ts.SourceFile} */ const documentRegistrySourceFileCache = new Map(); const { getKeyForCompilationSettings } = ts.createDocumentRegistry(); // reuse this code /** @type {ts.DocumentRegistry} */ const documentRegistry = { acquireDocument( fileName, compilationSettingsOrHost, scriptSnapshot, version, scriptKind, sourceFileOptions, ) { const key = getKeyForCompilationSettings( getCompilationSettings(compilationSettingsOrHost), ); return this.acquireDocumentWithKey( fileName, /** @type {ts.Path} */ (fileName), compilationSettingsOrHost, key, scriptSnapshot, version, scriptKind, sourceFileOptions, ); },
acquireDocumentWithKey( fileName, path, _compilationSettingsOrHost, key, scriptSnapshot, version, scriptKind, sourceFileOptions, ) { const mapKey = path + key; let sourceFile = documentRegistrySourceFileCache.get(mapKey); if (!sourceFile || sourceFile.version !== version) { sourceFile = ts.createLanguageServiceSourceFile( fileName, scriptSnapshot, { ...getCreateSourceFileOptions(sourceFileOptions), impliedNodeFormat: isCjsCache.has(fileName) ? ts.ModuleKind.CommonJS : ts.ModuleKind.ESNext, }, version, true, scriptKind, ); documentRegistrySourceFileCache.set(mapKey, sourceFile); } return sourceFile; },
updateDocument( fileName, compilationSettingsOrHost, scriptSnapshot, version, scriptKind, sourceFileOptions, ) { const key = getKeyForCompilationSettings( getCompilationSettings(compilationSettingsOrHost), ); return this.updateDocumentWithKey( fileName, /** @type {ts.Path} */ (fileName), compilationSettingsOrHost, key, scriptSnapshot, version, scriptKind, sourceFileOptions, ); },
updateDocumentWithKey( fileName, path, compilationSettingsOrHost, key, scriptSnapshot, version, scriptKind, sourceFileOptions, ) { const mapKey = path + key; let sourceFile = documentRegistrySourceFileCache.get(mapKey) ?? this.acquireDocumentWithKey( fileName, path, compilationSettingsOrHost, key, scriptSnapshot, version, scriptKind, sourceFileOptions, );
if (sourceFile.version !== version) { sourceFile = ts.updateLanguageServiceSourceFile( sourceFile, scriptSnapshot, version, scriptSnapshot.getChangeRange(sourceFile.scriptSnapShot), ); } return sourceFile; },
getKeyForCompilationSettings(settings) { return getKeyForCompilationSettings(settings); },
releaseDocument( fileName, compilationSettings, scriptKind, impliedNodeFormat, ) { const key = getKeyForCompilationSettings(compilationSettings); return this.releaseDocumentWithKey( /** @type {ts.Path} */ (fileName), key, scriptKind, impliedNodeFormat, ); },
releaseDocumentWithKey(path, key, _scriptKind, _impliedNodeFormat) { const mapKey = path + key; documentRegistrySourceFileCache.delete(mapKey); },
reportStats() { return "[]"; }, };
ts.deno.setIsNodeSourceFileCallback((sourceFile) => { const fileName = sourceFile.fileName; let isNodeSourceFile = isNodeSourceFileCache.get(fileName); if (isNodeSourceFile == null) { const result = ops.op_is_node_file(fileName); isNodeSourceFile = /** @type {boolean} */ (result); isNodeSourceFileCache.set(fileName, isNodeSourceFile); } return isNodeSourceFile; });
/** @param {ts.DiagnosticRelatedInformation} diagnostic */ function fromRelatedInformation({ start, length, file, messageText: msgText, ...ri }) { let messageText; let messageChain; if (typeof msgText === "object") { messageChain = msgText; } else { messageText = msgText; } if (start !== undefined && length !== undefined && file) { const startPos = file.getLineAndCharacterOfPosition(start); const sourceLine = file.getFullText().split("\n")[startPos.line]; const fileName = file.fileName; return { start: startPos, end: file.getLineAndCharacterOfPosition(start + length), fileName, messageChain, messageText, sourceLine, ...ri, }; } else { return { messageChain, messageText, ...ri, }; } }
/** @param {ts.Diagnostic[]} diagnostics */ function fromTypeScriptDiagnostic(diagnostics) { return diagnostics.map(({ relatedInformation: ri, source, ...diag }) => { /** @type {any} */ const value = fromRelatedInformation(diag); value.relatedInformation = ri ? ri.map(fromRelatedInformation) : undefined; value.source = source; return value; }); }
// Using incremental compile APIs requires that all // paths must be either relative or absolute. Since // analysis in Rust operates on fully resolved URLs, // it makes sense to use the same scheme here. const ASSETS = "asset:///";
/** Diagnostics that are intentionally ignored when compiling TypeScript in * Deno, as they provide misleading or incorrect information. */ const IGNORED_DIAGNOSTICS = [ // TS1452: 'resolution-mode' assertions are only supported when `moduleResolution` is `node16` or `nodenext`. // We specify the resolution mode to be CommonJS for some npm files and this // diagnostic gets generated even though we're using custom module resolution. 1452, // TS2306: File '.../index.d.ts' is not a module. // We get this for `x-typescript-types` declaration files which don't export // anything. We prefer to treat these as modules with no exports. 2306, // TS2688: Cannot find type definition file for '...'. // We ignore because type defintion files can end with '.ts'. 2688, // TS2691: An import path cannot end with a '.ts' extension. Consider // importing 'bad-module' instead. 2691, // TS2792: Cannot find module. Did you mean to set the 'moduleResolution' // option to 'node', or to add aliases to the 'paths' option? 2792, // TS5009: Cannot find the common subdirectory path for the input files. 5009, // TS5055: Cannot write file // 'http://localhost:4545/subdir/mt_application_x_javascript.j4.js' // because it would overwrite input file. 5055, // TypeScript is overly opinionated that only CommonJS modules kinds can // support JSON imports. Allegedly this was fixed in // Microsoft/TypeScript#26825 but that doesn't seem to be working here, // so we will ignore complaints about this compiler setting. 5070, // TS7016: Could not find a declaration file for module '...'. '...' // implicitly has an 'any' type. This is due to `allowJs` being off by // default but importing of a JavaScript module. 7016, ];
const SNAPSHOT_COMPILE_OPTIONS = { esModuleInterop: true, jsx: ts.JsxEmit.React, module: ts.ModuleKind.ESNext, noEmit: true, strict: true, target: ts.ScriptTarget.ESNext, };
// todo(dsherret): can we remove this and just use ts.OperationCanceledException? /** Error thrown on cancellation. */ class OperationCanceledError extends Error { }
// todo(dsherret): we should investigate if throttling is really necessary /** * Inspired by ThrottledCancellationToken in ts server. * * We don't want to continually call back into Rust and so * we throttle cancellation checks to only occur once * in a while. * @implements {ts.CancellationToken} */ class ThrottledCancellationToken { #lastCheckTimeMs = 0;
isCancellationRequested() { const timeMs = Date.now(); // TypeScript uses 20ms if ((timeMs - this.#lastCheckTimeMs) < 20) { return false; }
this.#lastCheckTimeMs = timeMs; return ops.op_is_cancelled(); }
throwIfCancellationRequested() { if (this.isCancellationRequested()) { throw new OperationCanceledError(); } } }
/** @type {ts.CompilerOptions} */ let compilationSettings = {};
/** @type {ts.LanguageService} */ let languageService;
/** An object literal of the incremental compiler host, which provides the * specific "bindings" to the Deno environment that tsc needs to work. * * @type {ts.CompilerHost & ts.LanguageServiceHost} */ const host = { fileExists(specifier) { if (logDebug) { debug(`host.fileExists("${specifier}")`); } specifier = normalizedToOriginalMap.get(specifier) ?? specifier; return ops.op_exists({ specifier }); }, readFile(specifier) { if (logDebug) { debug(`host.readFile("${specifier}")`); } return ops.op_load({ specifier }).data; }, getCancellationToken() { // createLanguageService will call this immediately and cache it return new ThrottledCancellationToken(); }, getSourceFile( specifier, languageVersion, _onError, _shouldCreateNewSourceFile, ) { const createOptions = getCreateSourceFileOptions(languageVersion); if (logDebug) { debug( `host.getSourceFile("${specifier}", ${ ts.ScriptTarget[createOptions.languageVersion] })`, ); }
// Needs the original specifier specifier = normalizedToOriginalMap.get(specifier) ?? specifier;
let sourceFile = sourceFileCache.get(specifier); if (sourceFile) { return sourceFile; }
/** @type {{ data: string; scriptKind: ts.ScriptKind; version: string; }} */ const { data, scriptKind, version } = ops.op_load( { specifier }, ); assert( data != null, `"data" is unexpectedly null for "${specifier}".`, ); sourceFile = ts.createSourceFile( specifier, data, { ...createOptions, impliedNodeFormat: isCjsCache.has(specifier) ? ts.ModuleKind.CommonJS : ts.ModuleKind.ESNext, }, false, scriptKind, ); sourceFile.moduleName = specifier; sourceFile.version = version; sourceFileCache.set(specifier, sourceFile); scriptVersionCache.set(specifier, version); return sourceFile; }, getDefaultLibFileName() { return `${ASSETS}/lib.esnext.d.ts`; }, getDefaultLibLocation() { return ASSETS; }, writeFile(fileName, data, _writeByteOrderMark, _onError, _sourceFiles) { if (logDebug) { debug(`host.writeFile("${fileName}")`); } return ops.op_emit( { fileName, data }, ); }, getCurrentDirectory() { if (logDebug) { debug(`host.getCurrentDirectory()`); } return cwd ?? ops.op_cwd(); }, getCanonicalFileName(fileName) { return fileName; }, useCaseSensitiveFileNames() { return true; }, getNewLine() { return "\n"; }, resolveTypeReferenceDirectives( typeDirectiveNames, containingFilePath, redirectedReference, options, containingFileMode, ) { return typeDirectiveNames.map((arg) => { /** @type {ts.FileReference} */ const fileReference = typeof arg === "string" ? { pos: -1, end: -1, fileName: arg, } : arg; if (fileReference.fileName.startsWith("npm:")) { /** @type {[string, ts.Extension] | undefined} */ const resolved = ops.op_resolve({ specifiers: [fileReference.fileName], base: containingFilePath, })?.[0]; if (resolved) { isCjsCache.add(resolved); return { primary: true, resolvedFileName: resolved[0], }; } else { return undefined; } } else { return ts.resolveTypeReferenceDirective( fileReference.fileName, containingFilePath, options, host, redirectedReference, undefined, containingFileMode ?? fileReference.resolutionMode, ).resolvedTypeReferenceDirective; } }); }, resolveModuleNames(specifiers, base) { if (logDebug) { debug(`host.resolveModuleNames()`); debug(` base: ${base}`); debug(` specifiers: ${specifiers.join(", ")}`); } /** @type {Array<[string, ts.Extension] | undefined>} */ const resolved = ops.op_resolve({ specifiers, base, }); if (resolved) { const result = resolved.map((item) => { if (item) { isCjsCache.add(item); const [resolvedFileName, extension] = item; if (resolvedFileName.startsWith("node:")) { // probably means the user doesn't have @types/node, so resolve to undefined return undefined; } return { resolvedFileName, extension, isExternalLibraryImport: false, }; } return undefined; }); result.length = specifiers.length; return result; } else { return new Array(specifiers.length); } }, createHash(data) { return ops.op_create_hash({ data }).hash; },
// LanguageServiceHost getCompilationSettings() { if (logDebug) { debug("host.getCompilationSettings()"); } return compilationSettings; }, getScriptFileNames() { if (logDebug) { debug("host.getScriptFileNames()"); } // tsc requests the script file names multiple times even though it can't // possibly have changed, so we will memoize it on a per request basis. if (scriptFileNamesCache) { return scriptFileNamesCache; } return scriptFileNamesCache = ops.op_script_names(); }, getScriptVersion(specifier) { if (logDebug) { debug(`host.getScriptVersion("${specifier}")`); } const sourceFile = sourceFileCache.get(specifier); if (sourceFile) { return sourceFile.version ?? "1"; } // tsc requests the script version multiple times even though it can't // possibly have changed, so we will memoize it on a per request basis. if (scriptVersionCache.has(specifier)) { return scriptVersionCache.get(specifier); } const scriptVersion = ops.op_script_version({ specifier }); scriptVersionCache.set(specifier, scriptVersion); return scriptVersion; }, getScriptSnapshot(specifier) { if (logDebug) { debug(`host.getScriptSnapshot("${specifier}")`); } const sourceFile = sourceFileCache.get(specifier); if (sourceFile) { return { getText(start, end) { return sourceFile.text.substring(start, end); }, getLength() { return sourceFile.text.length; }, getChangeRange() { return undefined; }, }; }
const fileInfo = ops.op_load( { specifier }, ); if (fileInfo) { scriptVersionCache.set(specifier, fileInfo.version); return ts.ScriptSnapshot.fromString(fileInfo.data); } else { return undefined; } }, };
// override the npm install @types package diagnostics to be deno specific ts.setLocalizedDiagnosticMessages((() => { const nodeMessage = "Cannot find name '{0}'."; // don't offer any suggestions const jqueryMessage = "Cannot find name '{0}'. Did you mean to import jQuery? Try adding `import $ from \"npm:jquery\";`."; return { "Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_node_Try_npm_i_save_dev_types_Slashno_2580": nodeMessage, "Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_node_Try_npm_i_save_dev_types_Slashno_2591": nodeMessage, "Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_jQuery_Try_npm_i_save_dev_types_Slash_2581": jqueryMessage, "Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_jQuery_Try_npm_i_save_dev_types_Slash_2592": jqueryMessage, }; })());
/** @type {Array<[string, number]>} */ const stats = []; let statsStart = 0;
function performanceStart() { stats.length = 0; statsStart = Date.now(); ts.performance.enable(); }
/** * @param {{ program: ts.Program | ts.EmitAndSemanticDiagnosticsBuilderProgram, fileCount?: number }} options */ function performanceProgram({ program, fileCount }) { if (program) { if ("getProgram" in program) { program = program.getProgram(); } stats.push(["Files", program.getSourceFiles().length]); stats.push(["Nodes", program.getNodeCount()]); stats.push(["Identifiers", program.getIdentifierCount()]); stats.push(["Symbols", program.getSymbolCount()]); stats.push(["Types", program.getTypeCount()]); stats.push(["Instantiations", program.getInstantiationCount()]); } else if (fileCount != null) { stats.push(["Files", fileCount]); } const programTime = ts.performance.getDuration("Program"); const bindTime = ts.performance.getDuration("Bind"); const checkTime = ts.performance.getDuration("Check"); const emitTime = ts.performance.getDuration("Emit"); stats.push(["Parse time", programTime]); stats.push(["Bind time", bindTime]); stats.push(["Check time", checkTime]); stats.push(["Emit time", emitTime]); stats.push( ["Total TS time", programTime + bindTime + checkTime + emitTime], ); }
function performanceEnd() { const duration = Date.now() - statsStart; stats.push(["Compile time", duration]); return stats; }
/** * @typedef {object} Request * @property {Record<string, any>} config * @property {boolean} debug * @property {string[]} rootNames */
/** * Checks the normalized version of the root name and stores it in * `normalizedToOriginalMap`. If the normalized specifier is already * registered for the different root name, it throws an AssertionError. * * @param {string} rootName */ function checkNormalizedPath(rootName) { const normalized = ts.normalizePath(rootName); const originalRootName = normalizedToOriginalMap.get(normalized); if (typeof originalRootName === "undefined") { normalizedToOriginalMap.set(normalized, rootName); } else if (originalRootName !== rootName) { // The different root names are normalizd to the same path. // This will cause problem when looking up the source for each. throw new AssertionError( `The different names for the same normalized specifier are specified: normalized=${normalized}, rootNames=${originalRootName},${rootName}`, ); } }
/** The API that is called by Rust when executing a request. * @param {Request} request */ function exec({ config, debug: debugFlag, rootNames }) { // https://github.com/microsoft/TypeScript/issues/49150 ts.base64encode = function (host, input) { if (host && host.base64encode) { return host.base64encode(input); } return convertToBase64(input); };
setLogDebug(debugFlag, "TS"); performanceStart(); if (logDebug) { debug(">>> exec start", { rootNames }); debug(config); }
rootNames.forEach(checkNormalizedPath);
const { options, errors: configFileParsingDiagnostics } = ts .convertCompilerOptionsFromJson(config, ""); // The `allowNonTsExtensions` is a "hidden" compiler option used in VSCode // which is not allowed to be passed in JSON, we need it to allow special // URLs which Deno supports. So we need to either ignore the diagnostic, or // inject it ourselves. Object.assign(options, { allowNonTsExtensions: true }); const program = ts.createIncrementalProgram({ rootNames, options, host, configFileParsingDiagnostics, });
const diagnostics = [ ...program.getConfigFileParsingDiagnostics(), ...program.getSyntacticDiagnostics(), ...program.getOptionsDiagnostics(), ...program.getGlobalDiagnostics(), ...program.getSemanticDiagnostics(), ].filter((diagnostic) => { if (IGNORED_DIAGNOSTICS.includes(diagnostic.code)) { return false; } else if ( diagnostic.code === 1259 && typeof diagnostic.messageText === "string" && diagnostic.messageText.startsWith( "Module '\"deno:///missing_dependency.d.ts\"' can only be default-imported using the 'allowSyntheticDefaultImports' flag", ) ) { // For now, ignore diagnostics like: // > TS1259 [ERROR]: Module '"deno:///missing_dependency.d.ts"' can only be default-imported using the 'allowSyntheticDefaultImports' flag // This diagnostic has surfaced due to supporting node cjs imports because this module does `export =`. // See discussion in https://github.com/microsoft/TypeScript/pull/51136 return false; } else { return true; } });
// emit the tsbuildinfo file // @ts-ignore: emitBuildInfo is not exposed (https://github.com/microsoft/TypeScript/issues/49871) program.emitBuildInfo(host.writeFile);
performanceProgram({ program });
ops.op_respond({ diagnostics: fromTypeScriptDiagnostic(diagnostics), stats: performanceEnd(), }); debug("<<< exec stop"); }
/** * @param {number} id * @param {any} data */ function respond(id, data = null) { ops.op_respond({ id, data }); }
/** * @param {LanguageServerRequest} request */ function serverRequest({ id, ...request }) { if (logDebug) { debug(`serverRequest()`, { id, ...request }); }
// reset all memoized source files names scriptFileNamesCache = undefined; // evict all memoized source file versions scriptVersionCache.clear(); switch (request.method) { case "restart": { serverRestart(); return respond(id, true); } case "configure": { const { options, errors } = ts .convertCompilerOptionsFromJson(request.compilerOptions, ""); Object.assign(options, { allowNonTsExtensions: true }); if (errors.length) { debug(ts.formatDiagnostics(errors, host)); } compilationSettings = options; return respond(id, true); } case "findRenameLocations": { return respond( id, languageService.findRenameLocations( request.specifier, request.position, request.findInStrings, request.findInComments, request.providePrefixAndSuffixTextForRename, ), ); } case "getAssets": { const assets = []; for (const sourceFile of sourceFileCache.values()) { if (sourceFile.fileName.startsWith(ASSETS)) { assets.push({ specifier: sourceFile.fileName, text: sourceFile.text, }); } } return respond(id, assets); } case "getApplicableRefactors": { return respond( id, languageService.getApplicableRefactors( request.specifier, request.range, { quotePreference: "double", allowTextChangesInNewFiles: true, provideRefactorNotApplicableReason: true, }, undefined, request.kind, ), ); } case "getEditsForRefactor": { return respond( id, languageService.getEditsForRefactor( request.specifier, { indentSize: 2, indentStyle: ts.IndentStyle.Smart, semicolons: ts.SemicolonPreference.Insert, convertTabsToSpaces: true, insertSpaceBeforeAndAfterBinaryOperators: true, insertSpaceAfterCommaDelimiter: true, }, request.range, request.refactorName, request.actionName, { quotePreference: "double", }, ), ); } case "getCodeFixes": { return respond( id, languageService.getCodeFixesAtPosition( request.specifier, request.startPosition, request.endPosition, request.errorCodes.map((v) => Number(v)), { indentSize: 2, indentStyle: ts.IndentStyle.Block, semicolons: ts.SemicolonPreference.Insert, }, { quotePreference: "double", }, ), ); } case "getCombinedCodeFix": { return respond( id, languageService.getCombinedCodeFix( { type: "file", fileName: request.specifier, }, request.fixId, { indentSize: 2, indentStyle: ts.IndentStyle.Block, semicolons: ts.SemicolonPreference.Insert, }, { quotePreference: "double", }, ), ); } case "getCompletionDetails": { if (logDebug) { debug("request", request); } return respond( id, languageService.getCompletionEntryDetails( request.args.specifier, request.args.position, request.args.name, {}, request.args.source, request.args.preferences, request.args.data, ), ); } case "getCompletions": { return respond( id, languageService.getCompletionsAtPosition( request.specifier, request.position, request.preferences, ), ); } case "getDefinition": { return respond( id, languageService.getDefinitionAndBoundSpan( request.specifier, request.position, ), ); } case "getDiagnostics": { try { /** @type {Record<string, any[]>} */ const diagnosticMap = {}; for (const specifier of request.specifiers) { diagnosticMap[specifier] = fromTypeScriptDiagnostic([ ...languageService.getSemanticDiagnostics(specifier), ...languageService.getSuggestionDiagnostics(specifier), ...languageService.getSyntacticDiagnostics(specifier), ].filter(({ code }) => !IGNORED_DIAGNOSTICS.includes(code))); } return respond(id, diagnosticMap); } catch (e) { if ( !(e instanceof OperationCanceledError || e instanceof ts.OperationCanceledException) ) { if ("stack" in e) { error(e.stack); } else { error(e); } } return respond(id, {}); } } case "getDocumentHighlights": { return respond( id, languageService.getDocumentHighlights( request.specifier, request.position, request.filesToSearch, ), ); } case "getEncodedSemanticClassifications": { return respond( id, languageService.getEncodedSemanticClassifications( request.specifier, request.span, ts.SemanticClassificationFormat.TwentyTwenty, ), ); } case "getImplementation": { return respond( id, languageService.getImplementationAtPosition( request.specifier, request.position, ), ); } case "getNavigateToItems": { return respond( id, languageService.getNavigateToItems( request.search, request.maxResultCount, request.fileName, ), ); } case "getNavigationTree": { return respond( id, languageService.getNavigationTree(request.specifier), ); } case "getOutliningSpans": { return respond( id, languageService.getOutliningSpans( request.specifier, ), ); } case "getQuickInfo": { return respond( id, languageService.getQuickInfoAtPosition( request.specifier, request.position, ), ); } case "getReferences": { return respond( id, languageService.getReferencesAtPosition( request.specifier, request.position, ), ); } case "getSignatureHelpItems": { return respond( id, languageService.getSignatureHelpItems( request.specifier, request.position, request.options, ), ); } case "getSmartSelectionRange": { return respond( id, languageService.getSmartSelectionRange( request.specifier, request.position, ), ); } case "getSupportedCodeFixes": { return respond( id, ts.getSupportedCodeFixes(), ); } case "getTypeDefinition": { return respond( id, languageService.getTypeDefinitionAtPosition( request.specifier, request.position, ), ); } case "prepareCallHierarchy": { return respond( id, languageService.prepareCallHierarchy( request.specifier, request.position, ), ); } case "provideCallHierarchyIncomingCalls": { return respond( id, languageService.provideCallHierarchyIncomingCalls( request.specifier, request.position, ), ); } case "provideCallHierarchyOutgoingCalls": { return respond( id, languageService.provideCallHierarchyOutgoingCalls( request.specifier, request.position, ), ); } case "provideInlayHints": return respond( id, languageService.provideInlayHints( request.specifier, request.span, request.preferences, ), ); default: throw new TypeError( // @ts-ignore exhausted case statement sets type to never `Invalid request method for request: "${request.method}" (${id})`, ); } }
/** @param {{ debug: boolean; rootUri?: string; }} init */ function serverInit({ debug: debugFlag, rootUri }) { if (hasStarted) { throw new Error("The language server has already been initialized."); } hasStarted = true; cwd = rootUri; languageService = ts.createLanguageService(host, documentRegistry); setLogDebug(debugFlag, "TSLS"); debug("serverInit()"); }
function serverRestart() { languageService = ts.createLanguageService(host, documentRegistry); isNodeSourceFileCache.clear(); debug("serverRestart()"); }
let hasStarted = false;
/** Startup the runtime environment, setting various flags. * @param {{ debugFlag?: boolean; legacyFlag?: boolean; }} msg */ function startup({ debugFlag = false }) { if (hasStarted) { throw new Error("The compiler runtime already started."); } hasStarted = true; setLogDebug(!!debugFlag, "TS"); }
// A build time only op that provides some setup information that is used to // ensure the snapshot is setup properly. /** @type {{ buildSpecifier: string; libs: string[] }} */
const { buildSpecifier, libs } = ops.op_build_info(); for (const lib of libs) { const specifier = `lib.${lib}.d.ts`; // we are using internal APIs here to "inject" our custom libraries into // tsc, so things like `"lib": [ "deno.ns" ]` are supported. if (!ts.libs.includes(lib)) { ts.libs.push(lib); ts.libMap.set(lib, `lib.${lib}.d.ts`); } // we are caching in memory common type libraries that will be re-used by // tsc on when the snapshot is restored assert( host.getSourceFile(`${ASSETS}${specifier}`, ts.ScriptTarget.ESNext), ); } // this helps ensure as much as possible is in memory that is re-usable // before the snapshotting is done, which helps unsure fast "startup" for // subsequent uses of tsc in Deno. const TS_SNAPSHOT_PROGRAM = ts.createProgram({ rootNames: [buildSpecifier], options: SNAPSHOT_COMPILE_OPTIONS, host, }); ts.getPreEmitDiagnostics(TS_SNAPSHOT_PROGRAM);
// exposes the two functions that are called by `tsc::exec()` when type // checking TypeScript. globalThis.startup = startup; globalThis.exec = exec;
// exposes the functions that are called when the compiler is used as a // language service. globalThis.serverInit = serverInit; globalThis.serverRequest = serverRequest;})(this);
Version Info