diff --git a/Gulpfile.js b/Gulpfile.js
index c9d3feff80b..a5a5536bda9 100644
--- a/Gulpfile.js
+++ b/Gulpfile.js
@@ -214,20 +214,29 @@ task("watch-services").flags = {
" --built": "Compile using the built version of the compiler."
};
-const buildServer = () => buildProject("src/tsserver", cmdLineOptions);
+const buildServerWeb = () => buildProject("src/tsserverWeb", cmdLineOptions);
+task("tsserverWeb", buildServerWeb);
+
+const buildServerMain = () => buildProject("src/tsserver", cmdLineOptions);
+const buildServer = series(buildServerWeb, buildServerMain);
+buildServer.displayName = "buildServer";
task("tsserver", series(preBuild, buildServer));
task("tsserver").description = "Builds the language server";
task("tsserver").flags = {
" --built": "Compile using the built version of the compiler."
};
-const cleanServer = () => cleanProject("src/tsserver");
+const cleanServerWeb = () => cleanProject("src/tsserverWeb");
+const cleanServerMain = () => cleanProject("src/tsserver");
+const cleanServer = series(cleanServerWeb, cleanServerMain);
+cleanServer.displayName = "cleanServer";
cleanTasks.push(cleanServer);
task("clean-tsserver", cleanServer);
task("clean-tsserver").description = "Cleans outputs for the language server";
+const watchServerWeb = () => watchProject("src/tsserverWeb", cmdLineOptions);
const watchServer = () => watchProject("src/tsserver", cmdLineOptions);
-task("watch-tsserver", series(preBuild, parallel(watchLib, watchDiagnostics, watchServer)));
+task("watch-tsserver", series(preBuild, parallel(watchLib, watchDiagnostics, watchServerWeb, watchServer)));
task("watch-tsserver").description = "Watch for changes and rebuild the language server only";
task("watch-tsserver").flags = {
" --built": "Compile using the built version of the compiler."
@@ -549,6 +558,7 @@ const produceLKG = async () => {
"built/local/typescriptServices.js",
"built/local/typescriptServices.d.ts",
"built/local/tsserver.js",
+ "built/local/tsserverWeb.js",
"built/local/typescript.js",
"built/local/typescript.d.ts",
"built/local/tsserverlibrary.js",
diff --git a/scripts/produceLKG.ts b/scripts/produceLKG.ts
index 89199df6125..45fd5bd17c7 100644
--- a/scripts/produceLKG.ts
+++ b/scripts/produceLKG.ts
@@ -62,6 +62,7 @@ async function copyScriptOutputs() {
await copyWithCopyright("cancellationToken.js");
await copyWithCopyright("tsc.release.js", "tsc.js");
await copyWithCopyright("tsserver.js");
+ await copyWithCopyright("tsserverWeb.js");
await copyFromBuiltLocal("tsserverlibrary.js"); // copyright added by build
await copyFromBuiltLocal("typescript.js"); // copyright added by build
await copyFromBuiltLocal("typescriptServices.js"); // copyright added by build
diff --git a/src/tsconfig.json b/src/tsconfig.json
index 8a0f4dbce0e..a23eca9ac34 100644
--- a/src/tsconfig.json
+++ b/src/tsconfig.json
@@ -9,6 +9,7 @@
{ "path": "./watchGuard" },
{ "path": "./debug" },
{ "path": "./cancellationToken" },
+ { "path": "./tsserverWeb" },
{ "path": "./testRunner" }
]
}
\ No newline at end of file
diff --git a/src/tsserverWeb/tsconfig.json b/src/tsserverWeb/tsconfig.json
new file mode 100644
index 00000000000..10d9cd19168
--- /dev/null
+++ b/src/tsserverWeb/tsconfig.json
@@ -0,0 +1,16 @@
+{
+ "extends": "../tsconfig-library-base",
+ "compilerOptions": {
+ "outDir": "../../built/local",
+ "rootDir": ".",
+ "target": "esnext",
+ "module": "esnext",
+ "lib": ["esnext"],
+ "declaration": false,
+ "sourceMap": true,
+ "tsBuildInfoFile": "../../built/local/tsserverWeb.tsbuildinfo"
+ },
+ "files": [
+ "tsserverWeb.ts",
+ ]
+}
diff --git a/src/tsserverWeb/tsserverWeb.ts b/src/tsserverWeb/tsserverWeb.ts
new file mode 100644
index 00000000000..aa21ba70a95
--- /dev/null
+++ b/src/tsserverWeb/tsserverWeb.ts
@@ -0,0 +1,3 @@
+namespace ts.server {
+ export const dynamicImport = (id: string) => import(id);
+}
\ No newline at end of file
diff --git a/src/webServer/webServer.ts b/src/webServer/webServer.ts
index cf24134da4b..fd252656575 100644
--- a/src/webServer/webServer.ts
+++ b/src/webServer/webServer.ts
@@ -1,5 +1,6 @@
/*@internal*/
///
+///
namespace ts.server {
export interface HostWithWriteMessage {
@@ -111,11 +112,35 @@ namespace ts.server {
}
}
+ export declare const dynamicImport: ((id: string) => Promise) | undefined;
+
+ // Attempt to load `dynamicImport`
+ if (typeof importScripts === "function") {
+ try {
+ // NOTE: importScripts is synchronous
+ importScripts("tsserverWeb.js");
+ }
+ catch {
+ // ignored
+ }
+ }
+
export function createWebSystem(host: WebHost, args: string[], getExecutingFilePath: () => string): ServerHost {
const returnEmptyString = () => "";
const getExecutingDirectoryPath = memoize(() => memoize(() => ensureTrailingDirectorySeparator(getDirectoryPath(getExecutingFilePath()))));
// Later we could map ^memfs:/ to do something special if we want to enable more functionality like module resolution or something like that
const getWebPath = (path: string) => startsWith(path, directorySeparator) ? path.replace(directorySeparator, getExecutingDirectoryPath()) : undefined;
+
+ const dynamicImport = async (id: string): Promise => {
+ // Use syntactic dynamic import first, if available
+ if (ts.server.dynamicImport) {
+ return ts.server.dynamicImport(id);
+ }
+
+ // Fall back to `eval` if dynamic import wasn't avaialble.
+ return eval(`import(${JSON.stringify(id)})`); // eslint-disable-line no-eval
+ };
+
return {
args,
newLine: "\r\n", // This can be configured by clients
@@ -157,11 +182,8 @@ namespace ts.server {
}
const scriptPath = combinePaths(packageRoot, browser);
-
- // TODO: TS rewrites `import(...)` to `require`. Use eval to bypass this
- // eslint-disable-next-line no-eval
try {
- const module = (await eval(`import(${JSON.stringify(scriptPath)})`)).default;
+ const { default: module } = await dynamicImport(scriptPath);
return { module, error: undefined };
}
catch (e) {