2023-01-02 16:00:42 -05:00
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
2020-07-19 13:49:44 -04:00
2020-12-07 05:46:39 -05:00
// @ts-check
/// <reference path="./compiler.d.ts" />
2020-11-03 10:19:29 -05:00
// deno-lint-ignore-file no-undef
2020-07-19 13:49:44 -04:00
// This module is the entry point for "compiler" isolate, ie. the one
2020-11-02 14:41:20 -05:00
// that is created when Deno needs to type check TypeScript, and in some
// instances convert TypeScript to JavaScript.
2020-07-19 13:49:44 -04:00
2022-08-03 23:09:16 -04:00
// Removes the `__proto__` for security reasons.
// https://tc39.es/ecma262/#sec-get-object.prototype.__proto__
2020-07-19 13:49:44 -04:00
delete Object . prototype . _ _proto _ _ ;
2023-03-24 10:35:44 -04:00
( ( /** @type {any} */ window ) => {
2020-12-07 05:46:39 -05:00
/** @type {DenoCore} */
2020-09-16 16:22:43 -04:00
const core = window . Deno . core ;
2022-08-11 09:56:56 -04:00
const ops = core . ops ;
2020-07-23 09:29:36 -04:00
2020-09-25 08:04:51 -04:00
let logDebug = false ;
let logSource = "JS" ;
2021-02-01 03:02:02 -05:00
// 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
2022-10-21 11:20:18 -04:00
/** @type {Map<string, string>} */
2021-02-01 03:02:02 -05:00
const normalizedToOriginalMap = new Map ( ) ;
2023-07-13 19:29:51 -04:00
/** @type {ReadonlySet<string>} */
const unstableDenoProps = new Set ( [
"AtomicOperation" ,
"CreateHttpClientOptions" ,
"DatagramConn" ,
"HttpClient" ,
"Kv" ,
"KvListIterator" ,
"KvU64" ,
"UnsafeCallback" ,
"UnsafePointer" ,
"UnsafePointerView" ,
"UnsafeFnPointer" ,
"UnixConnectOptions" ,
"UnixListenOptions" ,
"createHttpClient" ,
"dlopen" ,
"flock" ,
"flockSync" ,
"funlock" ,
"funlockSync" ,
"listen" ,
"listenDatagram" ,
"openKv" ,
"upgradeHttp" ,
"umask" ,
] ) ;
const unstableMsgSuggestion =
"If not, try changing the 'lib' compiler option to include 'deno.unstable' " +
'or add a triple-slash directive to your entrypoint: /// <reference lib="deno.unstable" />' ;
2022-05-31 20:19:18 -04:00
/ * *
* @ param { unknown } value
* @ returns { value is ts . CreateSourceFileOptions }
* /
function isCreateSourceFileOptions ( value ) {
return value != null && typeof value === "object" &&
"languageVersion" in value ;
}
2022-10-21 11:20:18 -04:00
/ * *
* @ param { ts . ScriptTarget | ts . CreateSourceFileOptions | undefined } versionOrOptions
* @ returns { ts . CreateSourceFileOptions }
* /
function getCreateSourceFileOptions ( versionOrOptions ) {
return isCreateSourceFileOptions ( versionOrOptions )
? versionOrOptions
: { languageVersion : versionOrOptions ? ? ts . ScriptTarget . ESNext } ;
}
2023-03-24 10:35:44 -04:00
/ * *
* @ param debug { boolean }
* @ param source { string }
* /
2020-09-25 08:04:51 -04:00
function setLogDebug ( debug , source ) {
logDebug = debug ;
if ( source ) {
logSource = source ;
}
}
2023-03-24 10:35:44 -04:00
/** @param msg {string} */
2021-05-02 19:30:03 -04:00
function printStderr ( msg ) {
core . print ( msg , true ) ;
}
2023-03-24 10:35:44 -04:00
/** @param args {any[]} */
2020-10-01 06:33:15 -04:00
function debug ( ... args ) {
2020-09-25 08:04:51 -04:00
if ( logDebug ) {
2020-12-07 05:46:39 -05:00
const stringifiedArgs = args . map ( ( arg ) =>
typeof arg === "string" ? arg : JSON . stringify ( arg )
) . join ( " " ) ;
2021-05-02 19:30:03 -04:00
printStderr ( ` DEBUG ${ logSource } - ${ stringifiedArgs } \n ` ) ;
2020-09-25 08:04:51 -04:00
}
}
2023-03-24 10:35:44 -04:00
/** @param args {any[]} */
2020-12-29 20:46:58 -05:00
function error ( ... args ) {
const stringifiedArgs = args . map ( ( arg ) =>
typeof arg === "string" || arg instanceof Error
? String ( arg )
: JSON . stringify ( arg )
) . join ( " " ) ;
2021-05-02 19:30:03 -04:00
printStderr ( ` ERROR ${ logSource } = ${ stringifiedArgs } \n ` ) ;
2020-12-29 20:46:58 -05:00
}
2020-09-25 08:04:51 -04:00
class AssertionError extends Error {
2023-03-24 10:35:44 -04:00
/** @param msg {string} */
2020-09-25 08:04:51 -04:00
constructor ( msg ) {
super ( msg ) ;
this . name = "AssertionError" ;
}
}
2023-03-24 10:35:44 -04:00
/** @param cond {boolean} */
2020-09-25 08:04:51 -04:00
function assert ( cond , msg = "Assertion failed." ) {
if ( ! cond ) {
throw new AssertionError ( msg ) ;
}
}
2022-10-21 11:20:18 -04:00
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 ) ;
}
}
2023-03-24 10:35:44 -04:00
/** @param specifier {string} */
2022-10-21 11:20:18 -04:00
has ( specifier ) {
return this . # cache . has ( specifier ) ;
}
}
// In the case of the LSP, this will only ever contain the assets.
2020-10-13 19:52:49 -04:00
/** @type {Map<string, ts.SourceFile>} */
const sourceFileCache = new Map ( ) ;
2021-07-26 17:40:12 -04:00
/** @type {string[]=} */
let scriptFileNamesCache ;
2021-05-17 18:51:35 -04:00
/** @type {Map<string, string>} */
const scriptVersionCache = new Map ( ) ;
2022-10-21 11:20:18 -04:00
/** @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
2023-03-21 11:46:40 -04:00
/** @type {Map<string, ts.SourceFile>} */
2022-10-21 11:20:18 -04:00
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 ,
2023-03-24 10:35:44 -04:00
scriptSnapshot . getChangeRange (
/** @type {ts.IScriptSnapshot} */ ( sourceFile . scriptSnapShot ) ,
) ,
2022-10-21 11:20:18 -04:00
) ;
}
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 ;
2022-10-22 15:36:07 -04:00
documentRegistrySourceFileCache . delete ( mapKey ) ;
2022-10-21 11:20:18 -04:00
} ,
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 ;
} ) ;
2023-07-13 19:29:51 -04:00
/ * *
* @ param msg { string }
* @ param code { number }
* /
function formatMessage ( msg , code ) {
switch ( code ) {
case 2304 : {
if ( msg === "Cannot find name 'Deno'." ) {
msg += " Do you need to change your target library? " +
"Try changing the 'lib' compiler option to include 'deno.ns' " +
'or add a triple-slash directive to your entrypoint: /// <reference lib="deno.ns" />' ;
}
return msg ;
}
case 2339 : {
const property = getProperty ( ) ;
if ( property && unstableDenoProps . has ( property ) ) {
return ` ${ msg } 'Deno. ${ property } ' is an unstable API. Did you forget to run with the '--unstable' flag? ${ unstableMsgSuggestion } ` ;
}
return msg ;
}
default : {
const property = getProperty ( ) ;
if ( property && unstableDenoProps . has ( property ) ) {
const suggestion = getMsgSuggestion ( ) ;
if ( suggestion ) {
return ` ${ msg } 'Deno. ${ property } ' is an unstable API. Did you forget to run with the '--unstable' flag, or did you mean ' ${ suggestion } '? ${ unstableMsgSuggestion } ` ;
}
}
return msg ;
}
}
function getProperty ( ) {
return /Property '([^']+)' does not exist on type 'typeof Deno'/
. exec ( msg ) ? . [ 1 ] ;
}
function getMsgSuggestion ( ) {
return / Did you mean '([^']+)'\?/ . exec ( msg ) ? . [ 1 ] ;
}
}
2020-11-02 14:41:20 -05:00
/** @param {ts.DiagnosticRelatedInformation} diagnostic */
2020-09-12 05:53:57 -04:00
function fromRelatedInformation ( {
start ,
length ,
file ,
messageText : msgText ,
... ri
} ) {
let messageText ;
let messageChain ;
if ( typeof msgText === "object" ) {
messageChain = msgText ;
} else {
2023-07-13 19:29:51 -04:00
messageText = formatMessage ( msgText , ri . code ) ;
2020-07-19 13:49:44 -04:00
}
2020-09-12 05:53:57 -04:00
if ( start !== undefined && length !== undefined && file ) {
const startPos = file . getLineAndCharacterOfPosition ( start ) ;
const sourceLine = file . getFullText ( ) . split ( "\n" ) [ startPos . line ] ;
const fileName = file . fileName ;
2020-07-19 13:49:44 -04:00
return {
2020-09-12 05:53:57 -04:00
start : startPos ,
end : file . getLineAndCharacterOfPosition ( start + length ) ,
fileName ,
messageChain ,
messageText ,
sourceLine ,
... ri ,
2020-07-19 13:49:44 -04:00
} ;
} else {
2020-09-12 05:53:57 -04:00
return {
messageChain ,
messageText ,
... ri ,
} ;
2020-07-19 13:49:44 -04:00
}
}
2023-03-21 18:19:42 -04:00
/** @param {readonly ts.Diagnostic[]} diagnostics */
2023-07-13 19:29:51 -04:00
function fromTypeScriptDiagnostics ( diagnostics ) {
2020-09-12 05:53:57 -04:00
return diagnostics . map ( ( { relatedInformation : ri , source , ... diag } ) => {
2020-12-07 05:46:39 -05:00
/** @type {any} */
2020-09-12 05:53:57 -04:00
const value = fromRelatedInformation ( diag ) ;
value . relatedInformation = ri
? ri . map ( fromRelatedInformation )
: undefined ;
value . source = source ;
return value ;
} ) ;
2020-07-19 13:49:44 -04:00
}
// 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.
2023-01-14 09:36:19 -05:00
const ASSETS _URL _PREFIX = "asset:///" ;
2023-03-11 11:43:45 -05:00
const CACHE _URL _PREFIX = "cache:///" ;
2020-07-19 13:49:44 -04:00
2020-11-02 14:41:20 -05:00
/ * * D i a g n o s t i c s t h a t a r e i n t e n t i o n a l l y i g n o r e d w h e n c o m p i l i n g T y p e S c r i p t i n
* Deno , as they provide misleading or incorrect information . * /
const IGNORED _DIAGNOSTICS = [
2022-10-21 11:20:18 -04:00
// 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 ,
2022-06-23 12:18:32 -04:00
// 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 ,
2021-05-18 00:09:33 -04:00
// TS2688: Cannot find type definition file for '...'.
// We ignore because type defintion files can end with '.ts'.
2688 ,
2020-12-24 05:53:03 -05:00
// TS2792: Cannot find module. Did you mean to set the 'moduleResolution'
// option to 'node', or to add aliases to the 'paths' option?
2792 ,
2020-11-02 14:41:20 -05:00
// TS5009: Cannot find the common subdirectory path for the input files.
5009 ,
// TS5055: Cannot write file
2021-08-11 10:20:47 -04:00
// 'http://localhost:4545/subdir/mt_application_x_javascript.j4.js'
2020-11-02 14:41:20 -05:00
// 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 = {
2020-07-19 13:49:44 -04:00
esModuleInterop : true ,
jsx : ts . JsxEmit . React ,
module : ts . ModuleKind . ESNext ,
2020-11-02 14:41:20 -05:00
noEmit : true ,
2020-07-19 13:49:44 -04:00
strict : true ,
target : ts . ScriptTarget . ESNext ,
2023-01-14 09:36:19 -05:00
lib : [ "lib.deno.window.d.ts" ] ,
2020-07-19 13:49:44 -04:00
} ;
2022-10-21 11:20:18 -04:00
// todo(dsherret): can we remove this and just use ts.OperationCanceledException?
2022-02-02 09:25:22 -05:00
/** Error thrown on cancellation. */
class OperationCanceledError extends Error {
}
2022-10-21 11:20:18 -04:00
// todo(dsherret): we should investigate if throttling is really necessary
2022-02-02 09:25:22 -05:00
/ * *
* 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 ;
2022-08-11 09:56:56 -04:00
return ops . op _is _cancelled ( ) ;
2022-02-02 09:25:22 -05:00
}
throwIfCancellationRequested ( ) {
if ( this . isCancellationRequested ( ) ) {
throw new OperationCanceledError ( ) ;
}
}
}
2020-12-07 05:46:39 -05:00
/** @type {ts.CompilerOptions} */
let compilationSettings = { } ;
/** @type {ts.LanguageService} */
let languageService ;
2020-11-02 14:41:20 -05:00
/ * * A n o b j e c t l i t e r a l o f t h e i n c r e m e n t a l c o m p i l e r h o s t , w h i c h p r o v i d e s t h e
* specific "bindings" to the Deno environment that tsc needs to work .
2020-11-22 18:20:32 -05:00
*
2020-12-07 05:46:39 -05:00
* @ type { ts . CompilerHost & ts . LanguageServiceHost } * /
2020-10-01 06:33:15 -04:00
const host = {
2021-06-21 17:18:32 -04:00
fileExists ( specifier ) {
2022-11-23 13:34:44 -05:00
if ( logDebug ) {
debug ( ` host.fileExists(" ${ specifier } ") ` ) ;
}
2023-03-11 11:43:45 -05:00
// this is used by typescript to find the libs path
// so we can completely ignore it
return false ;
2020-10-01 06:33:15 -04:00
} ,
2020-10-13 19:52:49 -04:00
readFile ( specifier ) {
2022-11-23 13:34:44 -05:00
if ( logDebug ) {
debug ( ` host.readFile(" ${ specifier } ") ` ) ;
}
2022-08-11 09:56:56 -04:00
return ops . op _load ( { specifier } ) . data ;
2020-10-01 06:33:15 -04:00
} ,
2022-02-02 09:25:22 -05:00
getCancellationToken ( ) {
// createLanguageService will call this immediately and cache it
return new ThrottledCancellationToken ( ) ;
} ,
2020-07-19 13:49:44 -04:00
getSourceFile (
2020-10-01 06:33:15 -04:00
specifier ,
2020-07-19 13:49:44 -04:00
languageVersion ,
2020-11-02 14:41:20 -05:00
_onError ,
_shouldCreateNewSourceFile ,
2020-07-19 13:49:44 -04:00
) {
2022-10-21 11:20:18 -04:00
const createOptions = getCreateSourceFileOptions ( languageVersion ) ;
2022-11-23 13:34:44 -05:00
if ( logDebug ) {
debug (
` host.getSourceFile(" ${ specifier } ", ${
ts . ScriptTarget [ createOptions . languageVersion ]
} ) ` ,
) ;
}
2022-05-15 14:41:37 -04:00
// Needs the original specifier
specifier = normalizedToOriginalMap . get ( specifier ) ? ? specifier ;
2020-11-02 14:41:20 -05:00
let sourceFile = sourceFileCache . get ( specifier ) ;
if ( sourceFile ) {
2020-10-13 19:52:49 -04:00
return sourceFile ;
2020-07-19 13:49:44 -04:00
}
2020-11-02 14:41:20 -05:00
2022-05-31 20:19:18 -04:00
/** @type {{ data: string; scriptKind: ts.ScriptKind; version: string; }} */
2022-08-11 09:56:56 -04:00
const { data , scriptKind , version } = ops . op _load (
2020-11-02 14:41:20 -05:00
{ specifier } ,
) ;
assert (
data != null ,
` "data" is unexpectedly null for " ${ specifier } ". ` ,
) ;
sourceFile = ts . createSourceFile (
specifier ,
data ,
2022-10-21 11:20:18 -04:00
{
... createOptions ,
impliedNodeFormat : isCjsCache . has ( specifier )
? ts . ModuleKind . CommonJS
: ts . ModuleKind . ESNext ,
} ,
2020-11-02 14:41:20 -05:00
false ,
scriptKind ,
) ;
sourceFile . moduleName = specifier ;
2022-05-15 14:41:37 -04:00
sourceFile . version = version ;
2020-11-02 14:41:20 -05:00
sourceFileCache . set ( specifier , sourceFile ) ;
2022-05-15 14:41:37 -04:00
scriptVersionCache . set ( specifier , version ) ;
2020-11-02 14:41:20 -05:00
return sourceFile ;
2020-10-01 06:33:15 -04:00
} ,
getDefaultLibFileName ( ) {
2023-01-14 09:36:19 -05:00
return ` ${ ASSETS _URL _PREFIX } lib.esnext.d.ts ` ;
2020-10-01 06:33:15 -04:00
} ,
getDefaultLibLocation ( ) {
2023-01-14 09:36:19 -05:00
return ASSETS _URL _PREFIX ;
2020-10-01 06:33:15 -04:00
} ,
2022-07-12 18:58:39 -04:00
writeFile ( fileName , data , _writeByteOrderMark , _onError , _sourceFiles ) {
2022-11-23 13:34:44 -05:00
if ( logDebug ) {
debug ( ` host.writeFile(" ${ fileName } ") ` ) ;
}
2022-08-11 09:56:56 -04:00
return ops . op _emit (
2022-07-12 18:58:39 -04:00
{ fileName , data } ,
2020-11-02 14:41:20 -05:00
) ;
2020-10-01 06:33:15 -04:00
} ,
getCurrentDirectory ( ) {
2022-11-23 13:34:44 -05:00
if ( logDebug ) {
debug ( ` host.getCurrentDirectory() ` ) ;
}
2023-03-11 11:43:45 -05:00
return CACHE _URL _PREFIX ;
2020-10-01 06:33:15 -04:00
} ,
getCanonicalFileName ( fileName ) {
return fileName ;
} ,
2020-07-19 13:49:44 -04:00
useCaseSensitiveFileNames ( ) {
return true ;
2020-10-01 06:33:15 -04:00
} ,
getNewLine ( ) {
return "\n" ;
} ,
2022-10-21 11:20:18 -04:00
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 ;
}
} ) ;
} ,
2020-10-01 06:33:15 -04:00
resolveModuleNames ( specifiers , base ) {
2022-11-23 13:34:44 -05:00
if ( logDebug ) {
debug ( ` host.resolveModuleNames() ` ) ;
debug ( ` base: ${ base } ` ) ;
debug ( ` specifiers: ${ specifiers . join ( ", " ) } ` ) ;
}
2020-12-07 05:46:39 -05:00
/** @type {Array<[string, ts.Extension] | undefined>} */
2022-08-11 09:56:56 -04:00
const resolved = ops . op _resolve ( {
2020-11-02 14:41:20 -05:00
specifiers ,
base ,
} ) ;
2020-12-07 05:46:39 -05:00
if ( resolved ) {
const result = resolved . map ( ( item ) => {
if ( item ) {
2022-10-21 11:20:18 -04:00
isCjsCache . add ( item ) ;
2020-12-07 05:46:39 -05:00
const [ resolvedFileName , extension ] = item ;
2022-10-21 11:20:18 -04:00
if ( resolvedFileName . startsWith ( "node:" ) ) {
// probably means the user doesn't have @types/node, so resolve to undefined
return undefined ;
}
2020-12-07 05:46:39 -05:00
return {
resolvedFileName ,
extension ,
isExternalLibraryImport : false ,
} ;
}
return undefined ;
} ) ;
result . length = specifiers . length ;
return result ;
} else {
return new Array ( specifiers . length ) ;
}
2020-10-01 06:33:15 -04:00
} ,
createHash ( data ) {
2023-04-01 10:12:40 -04:00
return ops . op _create _hash ( data ) ;
2020-10-01 06:33:15 -04:00
} ,
2020-12-07 05:46:39 -05:00
// LanguageServiceHost
getCompilationSettings ( ) {
2022-11-23 13:34:44 -05:00
if ( logDebug ) {
debug ( "host.getCompilationSettings()" ) ;
}
2020-12-07 05:46:39 -05:00
return compilationSettings ;
} ,
getScriptFileNames ( ) {
2022-11-23 13:34:44 -05:00
if ( logDebug ) {
debug ( "host.getScriptFileNames()" ) ;
}
2021-07-26 17:40:12 -04:00
// 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 ;
}
2022-08-11 09:56:56 -04:00
return scriptFileNamesCache = ops . op _script _names ( ) ;
2020-12-07 05:46:39 -05:00
} ,
getScriptVersion ( specifier ) {
2022-11-23 13:34:44 -05:00
if ( logDebug ) {
debug ( ` host.getScriptVersion(" ${ specifier } ") ` ) ;
}
2020-12-07 05:46:39 -05:00
const sourceFile = sourceFileCache . get ( specifier ) ;
if ( sourceFile ) {
return sourceFile . version ? ? "1" ;
}
2021-07-26 17:40:12 -04:00
// 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.
2021-05-17 18:51:35 -04:00
if ( scriptVersionCache . has ( specifier ) ) {
return scriptVersionCache . get ( specifier ) ;
}
2022-08-11 09:56:56 -04:00
const scriptVersion = ops . op _script _version ( { specifier } ) ;
2021-05-17 18:51:35 -04:00
scriptVersionCache . set ( specifier , scriptVersion ) ;
return scriptVersion ;
2020-12-07 05:46:39 -05:00
} ,
getScriptSnapshot ( specifier ) {
2022-11-23 13:34:44 -05:00
if ( logDebug ) {
debug ( ` host.getScriptSnapshot(" ${ specifier } ") ` ) ;
}
2020-12-07 05:46:39 -05:00
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 ;
} ,
} ;
}
2022-05-15 14:41:37 -04:00
2022-08-11 09:56:56 -04:00
const fileInfo = ops . op _load (
2022-05-15 14:41:37 -04:00
{ specifier } ,
) ;
if ( fileInfo ) {
scriptVersionCache . set ( specifier , fileInfo . version ) ;
return ts . ScriptSnapshot . fromString ( fileInfo . data ) ;
} else {
return undefined ;
2020-12-07 05:46:39 -05:00
}
} ,
2020-10-01 06:33:15 -04:00
} ;
2020-07-19 13:49:44 -04:00
2022-10-21 11:20:18 -04:00
// 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 ,
} ;
} ) ( ) ) ;
2020-11-02 14:41:20 -05:00
/** @type {Array<[string, number]>} */
2020-07-19 13:49:44 -04:00
const stats = [ ] ;
let statsStart = 0 ;
function performanceStart ( ) {
stats . length = 0 ;
2020-12-07 05:46:39 -05:00
statsStart = Date . now ( ) ;
2020-07-19 13:49:44 -04:00
ts . performance . enable ( ) ;
}
2020-12-07 05:46:39 -05:00
/ * *
2021-02-01 03:02:02 -05:00
* @ param { { program : ts . Program | ts . EmitAndSemanticDiagnosticsBuilderProgram , fileCount ? : number } } options
2020-12-07 05:46:39 -05:00
* /
2020-08-05 14:44:03 -04:00
function performanceProgram ( { program , fileCount } ) {
2020-07-19 13:49:44 -04:00
if ( program ) {
if ( "getProgram" in program ) {
program = program . getProgram ( ) ;
}
2020-11-02 14:41:20 -05:00
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 ( ) ] ) ;
2020-07-19 13:49:44 -04:00
} else if ( fileCount != null ) {
2020-11-02 14:41:20 -05:00
stats . push ( [ "Files" , fileCount ] ) ;
2020-07-19 13:49:44 -04:00
}
const programTime = ts . performance . getDuration ( "Program" ) ;
const bindTime = ts . performance . getDuration ( "Bind" ) ;
const checkTime = ts . performance . getDuration ( "Check" ) ;
const emitTime = ts . performance . getDuration ( "Emit" ) ;
2020-11-02 14:41:20 -05:00
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 ] ,
) ;
2020-07-19 13:49:44 -04:00
}
function performanceEnd ( ) {
2020-12-07 05:46:39 -05:00
const duration = Date . now ( ) - statsStart ;
2020-11-02 14:41:20 -05:00
stats . push ( [ "Compile time" , duration ] ) ;
2020-07-19 13:49:44 -04:00
return stats ;
}
2020-10-13 19:52:49 -04:00
/ * *
* @ typedef { object } Request
* @ property { Record < string , any > } config
* @ property { boolean } debug
* @ property { string [ ] } rootNames
2023-03-21 18:19:42 -04:00
* @ property { boolean } localOnly
2020-10-13 19:52:49 -04:00
* /
2020-07-23 09:29:36 -04:00
2021-02-01 03:02:02 -05:00
/ * *
* 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 } ` ,
) ;
}
}
2020-10-13 19:52:49 -04:00
/ * * T h e A P I t h a t i s c a l l e d b y R u s t w h e n e x e c u t i n g a r e q u e s t .
2020-11-22 18:20:32 -05:00
* @ param { Request } request
2020-10-13 19:52:49 -04:00
* /
2023-03-21 18:19:42 -04:00
function exec ( { config , debug : debugFlag , rootNames , localOnly } ) {
2020-10-13 19:52:49 -04:00
setLogDebug ( debugFlag , "TS" ) ;
performanceStart ( ) ;
2022-11-23 13:34:44 -05:00
if ( logDebug ) {
debug ( ">>> exec start" , { rootNames } ) ;
debug ( config ) ;
}
2020-10-13 19:52:49 -04:00
2021-02-01 03:02:02 -05:00
rootNames . forEach ( checkNormalizedPath ) ;
2020-10-13 19:52:49 -04:00
const { options , errors : configFileParsingDiagnostics } = ts
2020-12-07 05:46:39 -05:00
. convertCompilerOptionsFromJson ( config , "" ) ;
2020-11-22 18:20:32 -05:00
// 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 } ) ;
2020-10-13 19:52:49 -04:00
const program = ts . createIncrementalProgram ( {
rootNames ,
options ,
host ,
configFileParsingDiagnostics ,
} ) ;
2023-03-21 18:19:42 -04:00
const checkFiles = localOnly
? rootNames
. filter ( ( n ) => ! n . startsWith ( "http" ) )
. map ( ( checkName ) => {
const sourceFile = program . getSourceFile ( checkName ) ;
if ( sourceFile == null ) {
throw new Error ( "Could not find source file for: " + checkName ) ;
}
return sourceFile ;
} )
: undefined ;
2023-04-01 10:04:56 -04:00
if ( checkFiles != null ) {
// When calling program.getSemanticDiagnostics(...) with a source file, we
// need to call this code first in order to get it to invalidate cached
// diagnostics correctly. This is what program.getSemanticDiagnostics()
// does internally when calling without any arguments.
const checkFileNames = new Set ( checkFiles . map ( ( f ) => f . fileName ) ) ;
while (
program . getSemanticDiagnosticsOfNextAffectedFile (
undefined ,
/* ignoreSourceFile */ ( s ) => ! checkFileNames . has ( s . fileName ) ,
)
) {
// keep going until there are no more affected files
}
}
2020-10-13 19:52:49 -04:00
const diagnostics = [
... program . getConfigFileParsingDiagnostics ( ) ,
2023-03-21 18:19:42 -04:00
... ( checkFiles == null
? program . getSyntacticDiagnostics ( )
: ts . sortAndDeduplicateDiagnostics (
checkFiles . map ( ( s ) => program . getSyntacticDiagnostics ( s ) ) . flat ( ) ,
) ) ,
2020-10-13 19:52:49 -04:00
... program . getOptionsDiagnostics ( ) ,
... program . getGlobalDiagnostics ( ) ,
2023-03-21 18:19:42 -04:00
... ( checkFiles == null
? program . getSemanticDiagnostics ( )
: ts . sortAndDeduplicateDiagnostics (
checkFiles . map ( ( s ) => program . getSemanticDiagnostics ( s ) ) . flat ( ) ,
) ) ,
2023-01-24 09:05:54 -05:00
] . filter ( ( diagnostic ) => ! IGNORED _DIAGNOSTICS . includes ( diagnostic . code ) ) ;
2022-07-12 18:58:39 -04:00
// emit the tsbuildinfo file
// @ts-ignore: emitBuildInfo is not exposed (https://github.com/microsoft/TypeScript/issues/49871)
program . emitBuildInfo ( host . writeFile ) ;
2020-10-13 19:52:49 -04:00
performanceProgram ( { program } ) ;
2022-08-11 09:56:56 -04:00
ops . op _respond ( {
2023-07-13 19:29:51 -04:00
diagnostics : fromTypeScriptDiagnostics ( diagnostics ) ,
2020-11-02 14:41:20 -05:00
stats : performanceEnd ( ) ,
2020-10-13 19:52:49 -04:00
} ) ;
debug ( "<<< exec stop" ) ;
}
2023-01-14 09:36:19 -05:00
function getAssets ( ) {
/** @type {{ specifier: string; text: string; }[]} */
const assets = [ ] ;
for ( const sourceFile of sourceFileCache . values ( ) ) {
if ( sourceFile . fileName . startsWith ( ASSETS _URL _PREFIX ) ) {
assets . push ( {
specifier : sourceFile . fileName ,
text : sourceFile . text ,
} ) ;
}
}
return assets ;
}
2020-12-07 05:46:39 -05:00
/ * *
2021-02-01 03:02:02 -05:00
* @ param { number } id
* @ param { any } data
2020-12-07 05:46:39 -05:00
* /
function respond ( id , data = null ) {
2022-08-11 09:56:56 -04:00
ops . op _respond ( { id , data } ) ;
2020-12-07 05:46:39 -05:00
}
/ * *
2021-02-01 03:02:02 -05:00
* @ param { LanguageServerRequest } request
2020-12-07 05:46:39 -05:00
* /
function serverRequest ( { id , ... request } ) {
2022-11-23 13:34:44 -05:00
if ( logDebug ) {
debug ( ` serverRequest() ` , { id , ... request } ) ;
}
2022-06-27 13:43:43 -04:00
2021-07-26 17:40:12 -04:00
// reset all memoized source files names
scriptFileNamesCache = undefined ;
2021-05-17 18:51:35 -04:00
// evict all memoized source file versions
scriptVersionCache . clear ( ) ;
2020-12-07 05:46:39 -05:00
switch ( request . method ) {
2022-06-27 13:43:43 -04:00
case "restart" : {
serverRestart ( ) ;
return respond ( id , true ) ;
}
2020-12-07 05:46:39 -05:00
case "configure" : {
const { options , errors } = ts
. convertCompilerOptionsFromJson ( request . compilerOptions , "" ) ;
2023-03-21 11:46:40 -04:00
Object . assign ( options , {
allowNonTsExtensions : true ,
allowImportingTsExtensions : true ,
} ) ;
2023-03-21 18:19:42 -04:00
if ( errors . length > 0 && logDebug ) {
2020-12-07 05:46:39 -05:00
debug ( ts . formatDiagnostics ( errors , host ) ) ;
}
compilationSettings = options ;
return respond ( id , true ) ;
}
2021-01-31 22:30:41 -05:00
case "findRenameLocations" : {
return respond (
id ,
languageService . findRenameLocations (
request . specifier ,
request . position ,
request . findInStrings ,
request . findInComments ,
request . providePrefixAndSuffixTextForRename ,
) ,
) ;
}
2022-04-25 11:23:24 -04:00
case "getAssets" : {
2023-01-14 09:36:19 -05:00
return respond ( id , getAssets ( ) ) ;
2020-12-15 14:34:39 -05:00
}
2021-08-05 21:46:32 -04:00
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" ,
} ,
) ,
) ;
}
2021-02-04 13:53:02 -05:00
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" ,
} ,
) ,
) ;
}
2021-03-15 18:01:41 -04:00
case "getCompletionDetails" : {
2022-11-23 13:34:44 -05:00
if ( logDebug ) {
debug ( "request" , request ) ;
}
2021-03-15 18:01:41 -04:00
return respond (
id ,
languageService . getCompletionEntryDetails (
request . args . specifier ,
request . args . position ,
request . args . name ,
2022-07-11 19:35:18 -04:00
{ } ,
2021-03-15 18:01:41 -04:00
request . args . source ,
2022-07-11 19:35:18 -04:00
request . args . preferences ,
2021-03-15 18:01:41 -04:00
request . args . data ,
) ,
) ;
}
2021-01-31 22:30:41 -05:00
case "getCompletions" : {
return respond (
id ,
languageService . getCompletionsAtPosition (
request . specifier ,
request . position ,
request . preferences ,
) ,
) ;
}
case "getDefinition" : {
return respond (
id ,
languageService . getDefinitionAndBoundSpan (
request . specifier ,
request . position ,
) ,
) ;
}
2020-12-29 20:46:58 -05:00
case "getDiagnostics" : {
try {
2021-01-22 05:03:16 -05:00
/** @type {Record<string, any[]>} */
const diagnosticMap = { } ;
for ( const specifier of request . specifiers ) {
2023-07-13 19:29:51 -04:00
diagnosticMap [ specifier ] = fromTypeScriptDiagnostics ( [
2021-01-22 05:03:16 -05:00
... languageService . getSemanticDiagnostics ( specifier ) ,
... languageService . getSuggestionDiagnostics ( specifier ) ,
... languageService . getSyntacticDiagnostics ( specifier ) ,
] . filter ( ( { code } ) => ! IGNORED _DIAGNOSTICS . includes ( code ) ) ) ;
}
return respond ( id , diagnosticMap ) ;
2020-12-29 20:46:58 -05:00
} catch ( e ) {
2022-06-03 08:23:33 -04:00
if (
! ( e instanceof OperationCanceledError ||
e instanceof ts . OperationCanceledException )
) {
2022-02-02 09:25:22 -05:00
if ( "stack" in e ) {
error ( e . stack ) ;
} else {
error ( e ) ;
}
2021-01-22 05:03:16 -05:00
}
return respond ( id , { } ) ;
2020-12-29 20:46:58 -05:00
}
2020-12-07 05:46:39 -05:00
}
case "getDocumentHighlights" : {
return respond (
id ,
languageService . getDocumentHighlights (
request . specifier ,
request . position ,
request . filesToSearch ,
) ,
) ;
}
2021-04-19 21:26:36 -04:00
case "getEncodedSemanticClassifications" : {
return respond (
id ,
languageService . getEncodedSemanticClassifications (
request . specifier ,
request . span ,
ts . SemanticClassificationFormat . TwentyTwenty ,
) ,
) ;
}
2021-01-31 22:30:41 -05:00
case "getImplementation" : {
2020-12-07 05:46:39 -05:00
return respond (
id ,
2021-01-31 22:30:41 -05:00
languageService . getImplementationAtPosition (
2020-12-07 05:46:39 -05:00
request . specifier ,
request . position ,
) ,
) ;
}
2021-11-22 19:08:56 -05:00
case "getNavigateToItems" : {
return respond (
id ,
languageService . getNavigateToItems (
request . search ,
request . maxResultCount ,
request . fileName ,
) ,
) ;
}
2021-01-31 22:30:41 -05:00
case "getNavigationTree" : {
2020-12-07 05:46:39 -05:00
return respond (
id ,
2021-01-31 22:30:41 -05:00
languageService . getNavigationTree ( request . specifier ) ,
2020-12-07 05:46:39 -05:00
) ;
}
2021-04-02 02:21:07 -04:00
case "getOutliningSpans" : {
return respond (
id ,
languageService . getOutliningSpans (
request . specifier ,
) ,
) ;
}
2021-01-31 22:30:41 -05:00
case "getQuickInfo" : {
2021-01-12 16:53:27 -05:00
return respond (
id ,
2021-01-31 22:30:41 -05:00
languageService . getQuickInfoAtPosition (
2021-01-12 16:53:27 -05:00
request . specifier ,
request . position ,
) ,
) ;
}
2023-03-30 12:15:21 -04:00
case "findReferences" : {
2020-12-29 19:58:20 -05:00
return respond (
id ,
2023-03-30 12:15:21 -04:00
languageService . findReferences (
2020-12-29 19:58:20 -05:00
request . specifier ,
request . position ,
) ,
) ;
}
2021-02-15 21:34:09 -05:00
case "getSignatureHelpItems" : {
return respond (
id ,
languageService . getSignatureHelpItems (
request . specifier ,
request . position ,
request . options ,
) ,
) ;
}
2021-03-23 19:33:25 -04:00
case "getSmartSelectionRange" : {
return respond (
id ,
languageService . getSmartSelectionRange (
request . specifier ,
request . position ,
) ,
) ;
}
2021-02-04 13:53:02 -05:00
case "getSupportedCodeFixes" : {
return respond (
id ,
ts . getSupportedCodeFixes ( ) ,
) ;
}
2021-11-22 19:09:19 -05:00
case "getTypeDefinition" : {
return respond (
id ,
languageService . getTypeDefinitionAtPosition (
request . specifier ,
request . position ,
) ,
) ;
}
2021-04-19 01:11:26 -04:00
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 ,
) ,
) ;
}
2022-10-15 22:39:43 -04:00
case "provideInlayHints" :
return respond (
id ,
languageService . provideInlayHints (
request . specifier ,
request . span ,
request . preferences ,
) ,
) ;
2020-12-07 05:46:39 -05:00
default :
throw new TypeError (
// @ts-ignore exhausted case statement sets type to never
` Invalid request method for request: " ${ request . method } " ( ${ id } ) ` ,
) ;
}
}
2023-03-11 11:43:45 -05:00
/** @param {{ debug: boolean; }} init */
function serverInit ( { debug : debugFlag } ) {
2020-12-07 05:46:39 -05:00
if ( hasStarted ) {
throw new Error ( "The language server has already been initialized." ) ;
}
hasStarted = true ;
2022-10-21 11:20:18 -04:00
languageService = ts . createLanguageService ( host , documentRegistry ) ;
2020-12-07 05:46:39 -05:00
setLogDebug ( debugFlag , "TSLS" ) ;
debug ( "serverInit()" ) ;
}
2022-06-27 13:43:43 -04:00
function serverRestart ( ) {
2022-10-21 11:20:18 -04:00
languageService = ts . createLanguageService ( host , documentRegistry ) ;
isNodeSourceFileCache . clear ( ) ;
2022-06-27 13:43:43 -04:00
debug ( "serverRestart()" ) ;
}
2020-10-13 19:52:49 -04:00
let hasStarted = false ;
/ * * S t a r t u p t h e r u n t i m e e n v i r o n m e n t , s e t t i n g v a r i o u s f l a g s .
2020-11-22 18:20:32 -05:00
* @ param { { debugFlag ? : boolean ; legacyFlag ? : boolean ; } } msg
2020-10-13 19:52:49 -04:00
* /
2020-11-02 14:41:20 -05:00
function startup ( { debugFlag = false } ) {
2020-10-13 19:52:49 -04:00
if ( hasStarted ) {
throw new Error ( "The compiler runtime already started." ) ;
2020-07-23 09:29:36 -04:00
}
2020-10-13 19:52:49 -04:00
hasStarted = true ;
2020-09-26 10:33:25 -04:00
setLogDebug ( ! ! debugFlag , "TS" ) ;
2020-07-23 09:29:36 -04:00
}
2020-11-02 14:41:20 -05:00
// A build time only op that provides some setup information that is used to
// ensure the snapshot is setup properly.
2023-01-24 09:05:54 -05:00
/** @type {{ buildSpecifier: string; libs: string[]; nodeBuiltInModuleNames: string[] }} */
const { buildSpecifier , libs , nodeBuiltInModuleNames } = ops . op _build _info ( ) ;
ts . deno . setNodeBuiltInModuleNames ( nodeBuiltInModuleNames ) ;
2022-03-14 13:44:15 -04:00
2023-03-21 11:46:40 -04:00
// list of globals that should be kept in Node's globalThis
ts . deno . setNodeOnlyGlobalNames ( [
// when bumping the @types/node version we should check if
// anything needs to be updated here
"NodeRequire" ,
"RequireResolve" ,
"RequireResolve" ,
"process" ,
"console" ,
"__filename" ,
"__dirname" ,
"require" ,
"module" ,
"exports" ,
"gc" ,
"BufferEncoding" ,
"BufferConstructor" ,
"WithImplicitCoercion" ,
"Buffer" ,
"Console" ,
"ImportMeta" ,
"setTimeout" ,
"setInterval" ,
"setImmediate" ,
"Global" ,
"AbortController" ,
"AbortSignal" ,
"Blob" ,
"BroadcastChannel" ,
"MessageChannel" ,
"MessagePort" ,
"Event" ,
"EventTarget" ,
"performance" ,
"TextDecoder" ,
"TextEncoder" ,
"URL" ,
"URLSearchParams" ,
] ) ;
2020-11-02 14:41:20 -05:00
for ( const lib of libs ) {
2020-11-03 10:19:29 -05:00
const specifier = ` lib. ${ lib } .d.ts ` ;
2020-11-02 14:41:20 -05:00
// 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 (
2023-03-24 10:35:44 -04:00
! ! host . getSourceFile (
2023-01-14 09:36:19 -05:00
` ${ ASSETS _URL _PREFIX } ${ specifier } ` ,
ts . ScriptTarget . ESNext ,
) ,
2020-11-02 14:41:20 -05:00
) ;
}
// 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 ,
} ) ;
2023-01-14 09:36:19 -05:00
assert ( ts . getPreEmitDiagnostics ( TS _SNAPSHOT _PROGRAM ) . length === 0 ) ;
// remove this now that we don't need it anymore for warming up tsc
sourceFileCache . delete ( buildSpecifier ) ;
2020-11-02 14:41:20 -05:00
2023-03-24 10:35:44 -04:00
// exposes the functions that are called by `tsc::exec()` when type
2020-11-02 14:41:20 -05:00
// checking TypeScript.
2023-03-24 10:35:44 -04:00
/** @type {any} */
const global = globalThis ;
global . startup = startup ;
global . exec = exec ;
global . getAssets = getAssets ;
2020-12-07 05:46:39 -05:00
// exposes the functions that are called when the compiler is used as a
// language service.
2023-03-24 10:35:44 -04:00
global . serverInit = serverInit ;
global . serverRequest = serverRequest ;
2020-07-19 13:49:44 -04:00
} ) ( this ) ;