2024-01-01 14:58:21 -05:00
// Copyright 2018-2024 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" ,
"umask" ,
] ) ;
const unstableMsgSuggestion =
"If not, try changing the 'lib' compiler option to include 'deno.unstable' " +
2023-08-01 11:27:25 -04:00
"or add a triple-slash directive to the top of your entrypoint (main file): " +
'/// <reference lib="deno.unstable" />' ;
2023-07-13 19:29:51 -04:00
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 ,
2023-12-06 18:49:34 -05:00
// in the lsp we want to be able to show documentation
jsDocParsingMode : ts . JSDocParsingMode . ParseAll ,
2022-10-21 11:20:18 -04:00
} ,
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' " +
2023-08-01 11:27:25 -04:00
"or add a triple-slash directive to the top of your entrypoint " +
'(main file): /// <reference lib="deno.ns" />' ;
2023-07-13 19:29:51 -04:00
}
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 {
}
/ * *
2023-11-30 10:43:35 -05:00
* This implementation calls into Rust to check if Tokio ' s cancellation token
* has already been canceled .
2022-02-02 09:25:22 -05:00
* @ implements { ts . CancellationToken }
* /
2023-11-30 10:43:35 -05:00
class CancellationToken {
2022-02-02 09:25:22 -05:00
isCancellationRequested ( ) {
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.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-12-11 10:59:09 -05:00
// TODO(bartlomieju): is this assumption still valid?
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 } ") ` ) ;
}
2023-12-12 05:26:27 -05: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
2023-11-30 10:43:35 -05:00
return new CancellationToken ( ) ;
2022-02-02 09:25:22 -05:00
} ,
2023-12-27 19:13:57 -05:00
getProjectVersion ( ) {
return ops . op _project _version ( ) ;
} ,
2024-01-08 12:05:05 -05:00
// @ts-ignore Undocumented method.
getModuleSpecifierCache ( ) {
return moduleSpecifierCache ;
} ,
// @ts-ignore Undocumented method.
getCachedExportInfoMap ( ) {
return exportMapCache ;
} ,
2024-01-10 08:16:30 -05:00
getGlobalTypingsCacheLocation ( ) {
return undefined ;
} ,
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-11-23 13:34:44 -05:00
if ( logDebug ) {
debug (
` host.getSourceFile(" ${ specifier } ", ${
2023-12-06 18:49:34 -05:00
ts . ScriptTarget [
getCreateSourceFileOptions ( languageVersion ) . languageVersion
]
2022-11-23 13:34:44 -05:00
} ) ` ,
) ;
}
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; }} */
2023-12-02 21:07:04 -05:00
const fileInfo = ops . op _load ( specifier ) ;
2023-12-02 18:28:46 -05:00
if ( ! fileInfo ) {
return undefined ;
}
const { data , scriptKind , version } = fileInfo ;
2020-11-02 14:41:20 -05:00
assert (
data != null ,
` "data" is unexpectedly null for " ${ specifier } ". ` ,
) ;
sourceFile = ts . createSourceFile (
specifier ,
data ,
2022-10-21 11:20:18 -04:00
{
2023-12-06 18:49:34 -05:00
... getCreateSourceFileOptions ( languageVersion ) ,
2022-10-21 11:20:18 -04:00
impliedNodeFormat : isCjsCache . has ( specifier )
? ts . ModuleKind . CommonJS
: ts . ModuleKind . ESNext ,
2023-12-06 18:49:34 -05:00
// no need to parse docs for `deno check`
jsDocParsingMode : ts . JSDocParsingMode . ParseForTypeErrors ,
2022-10-21 11:20:18 -04:00
} ,
2020-11-02 14:41:20 -05:00
false ,
scriptKind ,
) ;
sourceFile . moduleName = specifier ;
2022-05-15 14:41:37 -04:00
sourceFile . version = version ;
2023-12-27 19:13:57 -05:00
if ( specifier . startsWith ( ASSETS _URL _PREFIX ) ) {
sourceFile . version = "1" ;
}
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()" ) ;
}
2024-03-26 11:52:20 -04:00
const tsConfig = normalizeConfig ( ops . op _ts _config ( ) ) ;
const { options , errors } = ts
. convertCompilerOptionsFromJson ( tsConfig , "" ) ;
Object . assign ( options , {
allowNonTsExtensions : true ,
allowImportingTsExtensions : true ,
} ) ;
if ( errors . length > 0 && logDebug ) {
debug ( ts . formatDiagnostics ( errors , host ) ) ;
}
return options ;
2020-12-07 05:46:39 -05:00
} ,
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 } ") ` ) ;
}
2023-12-27 19:13:57 -05:00
if ( specifier . startsWith ( ASSETS _URL _PREFIX ) ) {
return "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 ) ;
}
2023-12-01 16:57:52 -05: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 } ") ` ) ;
}
2023-12-02 18:28:46 -05:00
let sourceFile = sourceFileCache . get ( specifier ) ;
if (
! specifier . startsWith ( ASSETS _URL _PREFIX ) &&
sourceFile ? . version != this . getScriptVersion ( specifier )
) {
sourceFileCache . delete ( specifier ) ;
sourceFile = undefined ;
2020-12-07 05:46:39 -05:00
}
2023-12-02 18:28:46 -05:00
if ( ! sourceFile ) {
sourceFile = this . getSourceFile (
specifier ,
specifier . endsWith ( ".json" )
? ts . ScriptTarget . JSON
: ts . ScriptTarget . ESNext ,
) ;
}
if ( sourceFile ) {
return ts . ScriptSnapshot . fromString ( sourceFile . text ) ;
2020-12-07 05:46:39 -05:00
}
2023-12-02 18:28:46 -05:00
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
2024-01-08 12:05:05 -05:00
// @ts-ignore Undocumented function.
const moduleSpecifierCache = ts . server . createModuleSpecifierCache ( host ) ;
// @ts-ignore Undocumented function.
const exportMapCache = ts . createCacheableExportInfoMap ( host ) ;
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 } ` ,
) ;
}
}
2023-11-01 16:30:23 -04:00
/** @param {Record<string, string>} config */
function normalizeConfig ( config ) {
// the typescript compiler doesn't know about the precompile
// transform at the moment, so just tell it we're using react-jsx
if ( config . jsx === "precompile" ) {
config . jsx = "react-jsx" ;
}
return config ;
}
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 ( ) ;
2023-11-01 16:30:23 -04:00
config = normalizeConfig ( config ) ;
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
* /
2023-12-11 10:59:09 -05:00
// TODO(bartlomieju): this feels needlessly generic, both type chcking
// and language server use it with inefficient serialization. Id is not used
// anyway...
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
}
2023-10-02 02:32:05 -04:00
function serverRequest ( id , method , args ) {
2022-11-23 13:34:44 -05:00
if ( logDebug ) {
2023-10-02 02:32:05 -04:00
debug ( ` serverRequest() ` , id , method , args ) ;
2022-11-23 13:34:44 -05:00
}
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 ( ) ;
2023-10-02 02:32:05 -04:00
switch ( method ) {
case "$restart" : {
2022-06-27 13:43:43 -04:00
serverRestart ( ) ;
return respond ( id , true ) ;
}
2023-10-02 02:32:05 -04:00
case "$getSupportedCodeFixes" : {
2021-01-31 22:30:41 -05:00
return respond (
id ,
2023-10-02 02:32:05 -04:00
ts . getSupportedCodeFixes ( ) ,
2021-01-31 22:30:41 -05:00
) ;
}
2023-10-02 02:32:05 -04:00
case "$getAssets" : {
2023-01-14 09:36:19 -05:00
return respond ( id , getAssets ( ) ) ;
2020-12-15 14:34:39 -05:00
}
2023-10-02 02:32:05 -04:00
case "$getDiagnostics" : {
2020-12-29 20:46:58 -05:00
try {
2021-01-22 05:03:16 -05:00
/** @type {Record<string, any[]>} */
const diagnosticMap = { } ;
2023-10-02 02:32:05 -04:00
for ( const specifier of args [ 0 ] ) {
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
}
default :
2023-10-02 02:32:05 -04:00
if ( typeof languageService [ method ] === "function" ) {
// The `getCompletionEntryDetails()` method returns null if the
// `source` is `null` for whatever reason. It must be `undefined`.
if ( method == "getCompletionEntryDetails" ) {
args [ 4 ] ? ? = undefined ;
}
return respond ( id , languageService [ method ] ( ... args ) ) ;
}
2020-12-07 05:46:39 -05:00
throw new TypeError (
// @ts-ignore exhausted case statement sets type to never
2023-10-02 02:32:05 -04:00
` Invalid request method for request: " ${ method } " ( ${ id } ) ` ,
2020-12-07 05:46:39 -05:00
) ;
}
}
2023-12-11 10:59:09 -05:00
let hasStarted = false ;
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-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 ,
) ,
2023-11-01 15:26:12 -04:00
` failed to load ' ${ ASSETS _URL _PREFIX } ${ specifier } ' ` ,
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-11-01 15:26:12 -04:00
assert (
ts . getPreEmitDiagnostics ( TS _SNAPSHOT _PROGRAM ) . length === 0 ,
"lib.d.ts files have errors" ,
) ;
2023-01-14 09:36:19 -05:00
// 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 . 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 ) ;