summaryrefslogtreecommitdiff
path: root/tests/testdata/lsp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/testdata/lsp')
-rw-r--r--tests/testdata/lsp/deno.import_map.jsonc3
-rw-r--r--tests/testdata/lsp/deno.lint.exclude.jsonc16
-rw-r--r--tests/testdata/lsp/diagnostics_deno_types.json101
-rw-r--r--tests/testdata/lsp/import-map.json5
-rw-r--r--tests/testdata/lsp/large_file.txt676
-rw-r--r--tests/testdata/lsp/registries/a_latest_.json4
-rw-r--r--tests/testdata/lsp/registries/a_v1.0.0_.json4
-rw-r--r--tests/testdata/lsp/registries/a_v1.0.0_b.json3
-rw-r--r--tests/testdata/lsp/registries/a_v1.0.1_.json4
-rw-r--r--tests/testdata/lsp/registries/a_v2.0.0_.json4
-rw-r--r--tests/testdata/lsp/registries/a_versions_.json5
-rw-r--r--tests/testdata/lsp/registries/a_versions_v1..json4
-rw-r--r--tests/testdata/lsp/registries/b_latest_.json4
-rw-r--r--tests/testdata/lsp/registries/b_v0.0.1_.json4
-rw-r--r--tests/testdata/lsp/registries/b_v0.0.2_.json4
-rw-r--r--tests/testdata/lsp/registries/b_v0.0.3_.json4
-rw-r--r--tests/testdata/lsp/registries/b_versions_.json5
-rw-r--r--tests/testdata/lsp/registries/cde_tags.json4
-rw-r--r--tests/testdata/lsp/registries/cdef_tags.json4
-rw-r--r--tests/testdata/lsp/registries/complex.json5
-rw-r--r--tests/testdata/lsp/registries/complex_efg.json6
-rw-r--r--tests/testdata/lsp/registries/complex_efg_0.2.0.json6
-rw-r--r--tests/testdata/lsp/registries/def_tags.json3
-rw-r--r--tests/testdata/lsp/registries/deno-import-intellisense-complex.json22
-rw-r--r--tests/testdata/lsp/registries/deno-import-intellisense-key-first.json18
-rw-r--r--tests/testdata/lsp/registries/deno-import-intellisense.json62
-rw-r--r--tests/testdata/lsp/registries/doc_a.json4
-rw-r--r--tests/testdata/lsp/registries/doc_a_latest_mod.ts.json4
-rw-r--r--tests/testdata/lsp/registries/key_first.json5
-rw-r--r--tests/testdata/lsp/registries/modules_.json8
-rw-r--r--tests/testdata/lsp/registries/modules_a.json10
-rw-r--r--tests/testdata/lsp/types.tsconfig.json12
-rw-r--r--tests/testdata/lsp/x_deno_warning_redirect.js1
33 files changed, 1024 insertions, 0 deletions
diff --git a/tests/testdata/lsp/deno.import_map.jsonc b/tests/testdata/lsp/deno.import_map.jsonc
new file mode 100644
index 000000000..1682ff6b5
--- /dev/null
+++ b/tests/testdata/lsp/deno.import_map.jsonc
@@ -0,0 +1,3 @@
+{
+ "importMap": "import-map.json"
+}
diff --git a/tests/testdata/lsp/deno.lint.exclude.jsonc b/tests/testdata/lsp/deno.lint.exclude.jsonc
new file mode 100644
index 000000000..9d4ba52ad
--- /dev/null
+++ b/tests/testdata/lsp/deno.lint.exclude.jsonc
@@ -0,0 +1,16 @@
+{
+ "lint": {
+ "exclude": [
+ "ignored.ts"
+ ],
+ "rules": {
+ "exclude": [
+ "camelcase"
+ ],
+ "include": [
+ "ban-untagged-todo"
+ ],
+ "tags": []
+ }
+ }
+}
diff --git a/tests/testdata/lsp/diagnostics_deno_types.json b/tests/testdata/lsp/diagnostics_deno_types.json
new file mode 100644
index 000000000..6ab153093
--- /dev/null
+++ b/tests/testdata/lsp/diagnostics_deno_types.json
@@ -0,0 +1,101 @@
+{
+ "uri": "file:///a/file.ts",
+ "diagnostics": [
+ {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 21
+ },
+ "end": {
+ "line": 0,
+ "character": 51
+ }
+ },
+ "severity": 1,
+ "code": "no-cache",
+ "source": "deno",
+ "message": "Uncached or missing remote URL: https://example.com/a/b.d.ts",
+ "data": {
+ "specifier": "https://example.com/a/b.d.ts"
+ }
+ },
+ {
+ "range": {
+ "start": {
+ "line": 7,
+ "character": 19
+ },
+ "end": {
+ "line": 7,
+ "character": 47
+ }
+ },
+ "severity": 1,
+ "code": "no-cache",
+ "source": "deno",
+ "message": "Uncached or missing remote URL: https://example.com/a/e.js",
+ "data": {
+ "specifier": "https://example.com/a/e.js"
+ }
+ },
+ {
+ "range": {
+ "start": {
+ "line": 6,
+ "character": 16
+ },
+ "end": {
+ "line": 6,
+ "character": 44
+ }
+ },
+ "severity": 1,
+ "code": "no-cache",
+ "source": "deno",
+ "message": "Uncached or missing remote URL: https://example.com/a/e.d.ts",
+ "data": {
+ "specifier": "https://example.com/a/e.d.ts"
+ }
+ },
+ {
+ "range": {
+ "start": {
+ "line": 4,
+ "character": 19
+ },
+ "end": {
+ "line": 4,
+ "character": 47
+ }
+ },
+ "severity": 1,
+ "code": "no-cache",
+ "source": "deno",
+ "message": "Uncached or missing remote URL: https://example.com/a/d.js",
+ "data": {
+ "specifier": "https://example.com/a/d.js"
+ }
+ },
+ {
+ "range": {
+ "start": {
+ "line": 3,
+ "character": 15
+ },
+ "end": {
+ "line": 3,
+ "character": 43
+ }
+ },
+ "severity": 1,
+ "code": "no-cache",
+ "source": "deno",
+ "message": "Uncached or missing remote URL: https://example.com/a/d.d.ts",
+ "data": {
+ "specifier": "https://example.com/a/d.d.ts"
+ }
+ }
+ ],
+ "version": 1
+}
diff --git a/tests/testdata/lsp/import-map.json b/tests/testdata/lsp/import-map.json
new file mode 100644
index 000000000..75d5d0849
--- /dev/null
+++ b/tests/testdata/lsp/import-map.json
@@ -0,0 +1,5 @@
+{
+ "imports": {
+ "/~/": "./lib/"
+ }
+}
diff --git a/tests/testdata/lsp/large_file.txt b/tests/testdata/lsp/large_file.txt
new file mode 100644
index 000000000..f1ca0481c
--- /dev/null
+++ b/tests/testdata/lsp/large_file.txt
@@ -0,0 +1,676 @@
+// Copyright 2018-2023 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. This intentionally makes
+// Deno non compliant with ECMA-262 Annex B.2.2.1
+delete Object.prototype.__proto__;
+
+((window) => {
+ /** @type {DenoCore} */
+ const core = window.Deno.core;
+
+ let logDebug = false;
+ let logSource = "JS";
+
+ function setLogDebug(debug, source) {
+ logDebug = debug;
+ if (source) {
+ logSource = source;
+ }
+ }
+
+ function debug(...args) {
+ if (logDebug) {
+ const stringifiedArgs = args.map((arg) =>
+ typeof arg === "string" ? arg : JSON.stringify(arg)
+ ).join(" ");
+ // adding a non-zero integer value to the end of the debug string causes
+ // the message to be printed to stderr instead of stdout, which is better
+ // aligned to the behaviour of debug messages
+ core.print(`DEBUG ${logSource} - ${stringifiedArgs}\n`, 1);
+ }
+ }
+
+ function error(...args) {
+ const stringifiedArgs = args.map((arg) =>
+ typeof arg === "string" || arg instanceof Error
+ ? String(arg)
+ : JSON.stringify(arg)
+ ).join(" ");
+ core.print(`ERROR ${logSource} = ${stringifiedArgs}\n`, 1);
+ }
+
+ class AssertionError extends Error {
+ constructor(msg) {
+ super(msg);
+ this.name = "AssertionError";
+ }
+ }
+
+ function assert(cond, msg = "Assertion failed.") {
+ if (!cond) {
+ throw new AssertionError(msg);
+ }
+ }
+
+ /** @type {Map<string, ts.SourceFile>} */
+ const sourceFileCache = new Map();
+
+ /** @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:///";
+ const CACHE = "cache:///";
+
+ /** Diagnostics that are intentionally ignored when compiling TypeScript in
+ * Deno, as they provide misleading or incorrect information. */
+ const IGNORED_DIAGNOSTICS = [
+ // TS1208: All files must be modules when the '--isolatedModules' flag is
+ // provided. We can ignore because we guarantee that all files are
+ // modules.
+ 1208,
+ // TS1375: 'await' expressions are only allowed at the top level of a file
+ // when that file is a module, but this file has no imports or exports.
+ // Consider adding an empty 'export {}' to make this file a module.
+ 1375,
+ // TS1103: 'for-await-of' statement is only allowed within an async function
+ // or async generator.
+ 1103,
+ // TS2306: File 'file:///Users/rld/src/deno/subdir/amd_like.js' is
+ // not a module.
+ 2306,
+ // 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,
+ };
+
+ class ScriptSnapshot {
+ /** @type {string} */
+ specifier;
+ /** @type {string} */
+ version;
+ /**
+ * @param {string} specifier
+ * @param {string} version
+ */
+ constructor(specifier, version) {
+ this.specifier = specifier;
+ this.version = version;
+ }
+ /**
+ * @param {number} start
+ * @param {number} end
+ * @returns {string}
+ */
+ getText(start, end) {
+ const { specifier, version } = this;
+ debug(
+ `snapshot.getText(${start}, ${end}) specifier: ${specifier} version: ${version}`,
+ );
+ return core.jsonOpSync("op_get_text", { specifier, version, start, end });
+ }
+ /**
+ * @returns {number}
+ */
+ getLength() {
+ const { specifier, version } = this;
+ debug(`snapshot.getLength() specifier: ${specifier} version: ${version}`);
+ return core.jsonOpSync("op_get_length", { specifier, version });
+ }
+ /**
+ * @param {ScriptSnapshot} oldSnapshot
+ * @returns {ts.TextChangeRange | undefined}
+ */
+ getChangeRange(oldSnapshot) {
+ const { specifier, version } = this;
+ const { version: oldVersion } = oldSnapshot;
+ const oldLength = oldSnapshot.getLength();
+ debug(
+ `snapshot.getLength() specifier: ${specifier} oldVersion: ${oldVersion} version: ${version}`,
+ );
+ return core.jsonOpSync(
+ "op_get_change_range",
+ { specifier, oldLength, oldVersion, version },
+ );
+ }
+ dispose() {
+ const { specifier, version } = this;
+ debug(`snapshot.dispose() specifier: ${specifier} version: ${version}`);
+ core.jsonOpSync("op_dispose", { specifier, version });
+ }
+ }
+
+ /** @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(fileName) {
+ debug(`host.fileExists("${fileName}")`);
+ return false;
+ },
+ readFile(specifier) {
+ debug(`host.readFile("${specifier}")`);
+ return core.jsonOpSync("op_load", { specifier }).data;
+ },
+ getSourceFile(
+ specifier,
+ languageVersion,
+ _onError,
+ _shouldCreateNewSourceFile,
+ ) {
+ debug(
+ `host.getSourceFile("${specifier}", ${
+ ts.ScriptTarget[languageVersion]
+ })`,
+ );
+ let sourceFile = sourceFileCache.get(specifier);
+ if (sourceFile) {
+ return sourceFile;
+ }
+
+ /** @type {{ data: string; hash?: string; scriptKind: ts.ScriptKind }} */
+ const { data, hash, scriptKind } = core.jsonOpSync(
+ "op_load",
+ { specifier },
+ );
+ assert(
+ data != null,
+ `"data" is unexpectedly null for "${specifier}".`,
+ );
+ sourceFile = ts.createSourceFile(
+ specifier,
+ data,
+ languageVersion,
+ false,
+ scriptKind,
+ );
+ sourceFile.moduleName = specifier;
+ sourceFile.version = hash;
+ sourceFileCache.set(specifier, sourceFile);
+ return sourceFile;
+ },
+ getDefaultLibFileName() {
+ return `${ASSETS}/lib.esnext.d.ts`;
+ },
+ getDefaultLibLocation() {
+ return ASSETS;
+ },
+ writeFile(fileName, data, _writeByteOrderMark, _onError, sourceFiles) {
+ debug(`host.writeFile("${fileName}")`);
+ let maybeSpecifiers;
+ if (sourceFiles) {
+ maybeSpecifiers = sourceFiles.map((sf) => sf.moduleName);
+ }
+ return core.jsonOpSync(
+ "op_emit",
+ { maybeSpecifiers, fileName, data },
+ );
+ },
+ getCurrentDirectory() {
+ return CACHE;
+ },
+ getCanonicalFileName(fileName) {
+ return fileName;
+ },
+ useCaseSensitiveFileNames() {
+ return true;
+ },
+ getNewLine() {
+ return "\n";
+ },
+ resolveModuleNames(specifiers, base) {
+ debug(`host.resolveModuleNames()`);
+ debug(` base: ${base}`);
+ debug(` specifiers: ${specifiers.join(", ")}`);
+ /** @type {Array<[string, ts.Extension] | undefined>} */
+ const resolved = core.jsonOpSync("op_resolve", {
+ specifiers,
+ base,
+ });
+ if (resolved) {
+ const result = resolved.map((item) => {
+ if (item) {
+ const [resolvedFileName, extension] = item;
+ return {
+ resolvedFileName,
+ extension,
+ isExternalLibraryImport: false,
+ };
+ }
+ return undefined;
+ });
+ result.length = specifiers.length;
+ return result;
+ } else {
+ return new Array(specifiers.length);
+ }
+ },
+ createHash(data) {
+ return core.jsonOpSync("op_create_hash", { data }).hash;
+ },
+
+ // LanguageServiceHost
+ getCompilationSettings() {
+ debug("host.getCompilationSettings()");
+ return compilationSettings;
+ },
+ getScriptFileNames() {
+ debug("host.getScriptFileNames()");
+ return core.jsonOpSync("op_script_names", undefined);
+ },
+ getScriptVersion(specifier) {
+ debug(`host.getScriptVersion("${specifier}")`);
+ const sourceFile = sourceFileCache.get(specifier);
+ if (sourceFile) {
+ return sourceFile.version ?? "1";
+ }
+ return core.jsonOpSync("op_script_version", { specifier });
+ },
+ getScriptSnapshot(specifier) {
+ 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;
+ },
+ };
+ }
+ /** @type {string | undefined} */
+ const version = core.jsonOpSync("op_script_version", { specifier });
+ if (version != null) {
+ return new ScriptSnapshot(specifier, version);
+ }
+ return undefined;
+ },
+ };
+
+ /** @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
+ */
+
+ /** The API that is called by Rust when executing a request.
+ * @param {Request} request
+ */
+ function exec({ config, debug: debugFlag, rootNames }) {
+ setLogDebug(debugFlag, "TS");
+ performanceStart();
+ debug(">>> exec start", { rootNames });
+ debug(config);
+
+ 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: emitDiagnostics } = program.emit();
+
+ const diagnostics = [
+ ...program.getConfigFileParsingDiagnostics(),
+ ...program.getSyntacticDiagnostics(),
+ ...program.getOptionsDiagnostics(),
+ ...program.getGlobalDiagnostics(),
+ ...program.getSemanticDiagnostics(),
+ ...emitDiagnostics,
+ ].filter(({ code }) => !IGNORED_DIAGNOSTICS.includes(code));
+ performanceProgram({ program });
+
+ core.jsonOpSync("op_respond", {
+ diagnostics: fromTypeScriptDiagnostic(diagnostics),
+ stats: performanceEnd(),
+ });
+ debug("<<< exec stop");
+ }
+
+ /**
+ * @param {number} id
+ * @param {any} data
+ */
+ function respond(id, data = null) {
+ core.jsonOpSync("op_respond", { id, data });
+ }
+
+ /**
+ * @param {LanguageServerRequest} request
+ */
+ function serverRequest({ id, ...request }) {
+ debug(`serverRequest()`, { id, ...request });
+ switch (request.method) {
+ 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 "getAsset": {
+ const sourceFile = host.getSourceFile(
+ request.specifier,
+ ts.ScriptTarget.ESNext,
+ );
+ return respond(id, sourceFile && sourceFile.text);
+ }
+ 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 ("stack" in e) {
+ error(e.stack);
+ } else {
+ error(e);
+ }
+ return respond(id, {});
+ }
+ }
+ case "getQuickInfo": {
+ return respond(
+ id,
+ languageService.getQuickInfoAtPosition(
+ request.specifier,
+ request.position,
+ ),
+ );
+ }
+ case "getCompletions": {
+ return respond(
+ id,
+ languageService.getCompletionsAtPosition(
+ request.specifier,
+ request.position,
+ request.preferences,
+ ),
+ );
+ }
+ case "getDocumentHighlights": {
+ return respond(
+ id,
+ languageService.getDocumentHighlights(
+ request.specifier,
+ request.position,
+ request.filesToSearch,
+ ),
+ );
+ }
+ case "getReferences": {
+ return respond(
+ id,
+ languageService.getReferencesAtPosition(
+ request.specifier,
+ request.position,
+ ),
+ );
+ }
+ case "getDefinition": {
+ return respond(
+ id,
+ languageService.getDefinitionAndBoundSpan(
+ request.specifier,
+ request.position,
+ ),
+ );
+ }
+ case "getImplementation": {
+ return respond(
+ id,
+ languageService.getImplementationAtPosition(
+ request.specifier,
+ request.position,
+ ),
+ );
+ }
+ case "findRenameLocations": {
+ return respond(
+ id,
+ languageService.findRenameLocations(
+ request.specifier,
+ request.position,
+ request.findInStrings,
+ request.findInComments,
+ request.providePrefixAndSuffixTextForRename,
+ ),
+ );
+ }
+ default:
+ throw new TypeError(
+ // @ts-ignore exhausted case statement sets type to never
+ `Invalid request method for request: "${request.method}" (${id})`,
+ );
+ }
+ }
+
+ /** @param {{ debug: boolean; }} init */
+ function serverInit({ debug: debugFlag }) {
+ if (hasStarted) {
+ throw new Error("The language server has already been initialized.");
+ }
+ hasStarted = true;
+ languageService = ts.createLanguageService(host);
+ core.ops();
+ setLogDebug(debugFlag, "TSLS");
+ debug("serverInit()");
+ }
+
+ 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;
+ core.ops();
+ setLogDebug(!!debugFlag, "TS");
+ }
+
+ // Setup the compiler runtime during the build process.
+ core.ops();
+ core.registerErrorClass("Error", Error);
+
+ // 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 } = core.jsonOpSync("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);
diff --git a/tests/testdata/lsp/registries/a_latest_.json b/tests/testdata/lsp/registries/a_latest_.json
new file mode 100644
index 000000000..f9f9d111e
--- /dev/null
+++ b/tests/testdata/lsp/registries/a_latest_.json
@@ -0,0 +1,4 @@
+[
+ "b/c.ts",
+ "d/e.js"
+]
diff --git a/tests/testdata/lsp/registries/a_v1.0.0_.json b/tests/testdata/lsp/registries/a_v1.0.0_.json
new file mode 100644
index 000000000..f9f9d111e
--- /dev/null
+++ b/tests/testdata/lsp/registries/a_v1.0.0_.json
@@ -0,0 +1,4 @@
+[
+ "b/c.ts",
+ "d/e.js"
+]
diff --git a/tests/testdata/lsp/registries/a_v1.0.0_b.json b/tests/testdata/lsp/registries/a_v1.0.0_b.json
new file mode 100644
index 000000000..20ec4ad90
--- /dev/null
+++ b/tests/testdata/lsp/registries/a_v1.0.0_b.json
@@ -0,0 +1,3 @@
+[
+ "b/c.ts"
+]
diff --git a/tests/testdata/lsp/registries/a_v1.0.1_.json b/tests/testdata/lsp/registries/a_v1.0.1_.json
new file mode 100644
index 000000000..f9f9d111e
--- /dev/null
+++ b/tests/testdata/lsp/registries/a_v1.0.1_.json
@@ -0,0 +1,4 @@
+[
+ "b/c.ts",
+ "d/e.js"
+]
diff --git a/tests/testdata/lsp/registries/a_v2.0.0_.json b/tests/testdata/lsp/registries/a_v2.0.0_.json
new file mode 100644
index 000000000..f9f9d111e
--- /dev/null
+++ b/tests/testdata/lsp/registries/a_v2.0.0_.json
@@ -0,0 +1,4 @@
+[
+ "b/c.ts",
+ "d/e.js"
+]
diff --git a/tests/testdata/lsp/registries/a_versions_.json b/tests/testdata/lsp/registries/a_versions_.json
new file mode 100644
index 000000000..930e38323
--- /dev/null
+++ b/tests/testdata/lsp/registries/a_versions_.json
@@ -0,0 +1,5 @@
+[
+ "v1.0.0",
+ "v1.0.1",
+ "v2.0.0"
+]
diff --git a/tests/testdata/lsp/registries/a_versions_v1..json b/tests/testdata/lsp/registries/a_versions_v1..json
new file mode 100644
index 000000000..1d8a865c1
--- /dev/null
+++ b/tests/testdata/lsp/registries/a_versions_v1..json
@@ -0,0 +1,4 @@
+[
+ "v1.0.0",
+ "v1.0.1"
+]
diff --git a/tests/testdata/lsp/registries/b_latest_.json b/tests/testdata/lsp/registries/b_latest_.json
new file mode 100644
index 000000000..f9f9d111e
--- /dev/null
+++ b/tests/testdata/lsp/registries/b_latest_.json
@@ -0,0 +1,4 @@
+[
+ "b/c.ts",
+ "d/e.js"
+]
diff --git a/tests/testdata/lsp/registries/b_v0.0.1_.json b/tests/testdata/lsp/registries/b_v0.0.1_.json
new file mode 100644
index 000000000..f9f9d111e
--- /dev/null
+++ b/tests/testdata/lsp/registries/b_v0.0.1_.json
@@ -0,0 +1,4 @@
+[
+ "b/c.ts",
+ "d/e.js"
+]
diff --git a/tests/testdata/lsp/registries/b_v0.0.2_.json b/tests/testdata/lsp/registries/b_v0.0.2_.json
new file mode 100644
index 000000000..f9f9d111e
--- /dev/null
+++ b/tests/testdata/lsp/registries/b_v0.0.2_.json
@@ -0,0 +1,4 @@
+[
+ "b/c.ts",
+ "d/e.js"
+]
diff --git a/tests/testdata/lsp/registries/b_v0.0.3_.json b/tests/testdata/lsp/registries/b_v0.0.3_.json
new file mode 100644
index 000000000..f9f9d111e
--- /dev/null
+++ b/tests/testdata/lsp/registries/b_v0.0.3_.json
@@ -0,0 +1,4 @@
+[
+ "b/c.ts",
+ "d/e.js"
+]
diff --git a/tests/testdata/lsp/registries/b_versions_.json b/tests/testdata/lsp/registries/b_versions_.json
new file mode 100644
index 000000000..9532fbb85
--- /dev/null
+++ b/tests/testdata/lsp/registries/b_versions_.json
@@ -0,0 +1,5 @@
+[
+ "v0.0.1",
+ "v0.0.2",
+ "v0.0.3"
+]
diff --git a/tests/testdata/lsp/registries/cde_tags.json b/tests/testdata/lsp/registries/cde_tags.json
new file mode 100644
index 000000000..24aeba56a
--- /dev/null
+++ b/tests/testdata/lsp/registries/cde_tags.json
@@ -0,0 +1,4 @@
+[
+ "1.0.0",
+ "1.0.1"
+]
diff --git a/tests/testdata/lsp/registries/cdef_tags.json b/tests/testdata/lsp/registries/cdef_tags.json
new file mode 100644
index 000000000..a69cb1c55
--- /dev/null
+++ b/tests/testdata/lsp/registries/cdef_tags.json
@@ -0,0 +1,4 @@
+[
+ "2.0.0",
+ "2.0.1"
+]
diff --git a/tests/testdata/lsp/registries/complex.json b/tests/testdata/lsp/registries/complex.json
new file mode 100644
index 000000000..b6e28649a
--- /dev/null
+++ b/tests/testdata/lsp/registries/complex.json
@@ -0,0 +1,5 @@
+[
+ "efg",
+ "efgh",
+ "fg"
+]
diff --git a/tests/testdata/lsp/registries/complex_efg.json b/tests/testdata/lsp/registries/complex_efg.json
new file mode 100644
index 000000000..cd170d1d8
--- /dev/null
+++ b/tests/testdata/lsp/registries/complex_efg.json
@@ -0,0 +1,6 @@
+[
+ "0.2.2",
+ "0.2.1",
+ "0.2.0",
+ "0.1.0"
+]
diff --git a/tests/testdata/lsp/registries/complex_efg_0.2.0.json b/tests/testdata/lsp/registries/complex_efg_0.2.0.json
new file mode 100644
index 000000000..d333b9e28
--- /dev/null
+++ b/tests/testdata/lsp/registries/complex_efg_0.2.0.json
@@ -0,0 +1,6 @@
+[
+ "mod.ts",
+ "example/mod.ts",
+ "CHANGELOG.md",
+ "deps.ts"
+]
diff --git a/tests/testdata/lsp/registries/def_tags.json b/tests/testdata/lsp/registries/def_tags.json
new file mode 100644
index 000000000..5a33204f2
--- /dev/null
+++ b/tests/testdata/lsp/registries/def_tags.json
@@ -0,0 +1,3 @@
+[
+ "3.0.0"
+]
diff --git a/tests/testdata/lsp/registries/deno-import-intellisense-complex.json b/tests/testdata/lsp/registries/deno-import-intellisense-complex.json
new file mode 100644
index 000000000..98e913bdb
--- /dev/null
+++ b/tests/testdata/lsp/registries/deno-import-intellisense-complex.json
@@ -0,0 +1,22 @@
+{
+ "version": 1,
+ "registries": [
+ {
+ "schema": "/:module([a-zA-Z0-9_]*)@:version/:path*",
+ "variables": [
+ {
+ "key": "module",
+ "url": "http://localhost:4545/lsp/registries/complex.json"
+ },
+ {
+ "key": "version",
+ "url": "http://localhost:4545/lsp/registries/complex_${module}.json"
+ },
+ {
+ "key": "path",
+ "url": "http://localhost:4545/lsp/registries/complex_${module}_${version}.json"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/testdata/lsp/registries/deno-import-intellisense-key-first.json b/tests/testdata/lsp/registries/deno-import-intellisense-key-first.json
new file mode 100644
index 000000000..9aa33ecd3
--- /dev/null
+++ b/tests/testdata/lsp/registries/deno-import-intellisense-key-first.json
@@ -0,0 +1,18 @@
+{
+ "version": 1,
+ "registries": [
+ {
+ "schema": "/:module([a-zA-Z0-9-_]+)@:tag([a-zA-Z0-9-_\\.]+)",
+ "variables": [
+ {
+ "key": "module",
+ "url": "http://localhost:4545/lsp/registries/key_first.json"
+ },
+ {
+ "key": "tag",
+ "url": "http://localhost:4545/lsp/registries/${module}_tags.json"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/testdata/lsp/registries/deno-import-intellisense.json b/tests/testdata/lsp/registries/deno-import-intellisense.json
new file mode 100644
index 000000000..5fd87085e
--- /dev/null
+++ b/tests/testdata/lsp/registries/deno-import-intellisense.json
@@ -0,0 +1,62 @@
+{
+ "version": 2,
+ "registries": [
+ {
+ "schema": "/x/:module([a-z0-9_]*)@:version?/:path*",
+ "variables": [
+ {
+ "key": "module",
+ "documentation": "/lsp/registries/doc_${module}.json",
+ "url": "/lsp/registries/modules_${module}.json"
+ },
+ {
+ "key": "version",
+ "documentation": "/lsp/registries/doc_${module}_${{version}}.json",
+ "url": "/lsp/registries/${module}_versions_${{version}}.json"
+ },
+ {
+ "key": "path",
+ "documentation": "/lsp/registries/doc_${module}_${{version}}_${path}.json",
+ "url": "/lsp/registries/${module}_${{version}}_${path}.json"
+ }
+ ]
+ },
+ {
+ "schema": "/x/:module([a-z0-9_]*)/:path*",
+ "variables": [
+ {
+ "key": "module",
+ "documentation": "/lsp/registries/doc_${module}.json",
+ "url": "/lsp/registries/modules_${module}.json"
+ },
+ {
+ "key": "path",
+ "documentation": "/lsp/registries/doc_${module}_latest_${path}.json",
+ "url": "/lsp/registries/${module}_latest_${path}.json"
+ }
+ ]
+ },
+ {
+ "schema": "/std@:version?/:path*",
+ "variables": [
+ {
+ "key": "version",
+ "url": "/lsp/registries/std_${{version}}.json"
+ },
+ {
+ "key": "path",
+ "url": "/lsp/registries/std_${{version}}_${path}.json"
+ }
+ ]
+ },
+ {
+ "schema": "/std/:path*",
+ "variables": [
+ {
+ "key": "path",
+ "url": "/lsp/registries/std_latest_${path}.json"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/testdata/lsp/registries/doc_a.json b/tests/testdata/lsp/registries/doc_a.json
new file mode 100644
index 000000000..ecddb9144
--- /dev/null
+++ b/tests/testdata/lsp/registries/doc_a.json
@@ -0,0 +1,4 @@
+{
+ "kind": "markdown",
+ "value": "**a**"
+}
diff --git a/tests/testdata/lsp/registries/doc_a_latest_mod.ts.json b/tests/testdata/lsp/registries/doc_a_latest_mod.ts.json
new file mode 100644
index 000000000..522f5b271
--- /dev/null
+++ b/tests/testdata/lsp/registries/doc_a_latest_mod.ts.json
@@ -0,0 +1,4 @@
+{
+ "kind": "markdown",
+ "value": "**a**\n\nmod.ts"
+}
diff --git a/tests/testdata/lsp/registries/key_first.json b/tests/testdata/lsp/registries/key_first.json
new file mode 100644
index 000000000..c95261b25
--- /dev/null
+++ b/tests/testdata/lsp/registries/key_first.json
@@ -0,0 +1,5 @@
+[
+ "cde",
+ "cdef",
+ "def"
+]
diff --git a/tests/testdata/lsp/registries/modules_.json b/tests/testdata/lsp/registries/modules_.json
new file mode 100644
index 000000000..fae3b40a3
--- /dev/null
+++ b/tests/testdata/lsp/registries/modules_.json
@@ -0,0 +1,8 @@
+{
+ "items": [
+ "a",
+ "b"
+ ],
+ "isIncomplete": true,
+ "preselect": "a"
+}
diff --git a/tests/testdata/lsp/registries/modules_a.json b/tests/testdata/lsp/registries/modules_a.json
new file mode 100644
index 000000000..0163f18a4
--- /dev/null
+++ b/tests/testdata/lsp/registries/modules_a.json
@@ -0,0 +1,10 @@
+{
+ "items": [
+ "a",
+ "aa",
+ "ab",
+ "aba"
+ ],
+ "isIncomplete": false,
+ "preselect": "a"
+}
diff --git a/tests/testdata/lsp/types.tsconfig.json b/tests/testdata/lsp/types.tsconfig.json
new file mode 100644
index 000000000..50de6939c
--- /dev/null
+++ b/tests/testdata/lsp/types.tsconfig.json
@@ -0,0 +1,12 @@
+{
+ "compilerOptions": {
+ "types": [
+ "./a.d.ts"
+ ]
+ },
+ "lint": {
+ "rules": {
+ "tags": []
+ }
+ }
+}
diff --git a/tests/testdata/lsp/x_deno_warning_redirect.js b/tests/testdata/lsp/x_deno_warning_redirect.js
new file mode 100644
index 000000000..34b950566
--- /dev/null
+++ b/tests/testdata/lsp/x_deno_warning_redirect.js
@@ -0,0 +1 @@
+console.log("testing x-deno-warning header");