From be55639261d072353ab915bc00786d33d163872a Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Mon, 7 Nov 2022 15:02:44 -0800
Subject: [PATCH 1/9] Initial draft.
Not working.
Also not correctly formatted, I'll do that later.
---
extensions/postinstall.mjs | 1 -
.../extension-browser.webpack.config.js | 10 +-
.../src/tsServer/webhost.ts | 534 ++++++++++++++++++
.../src/utils/platform.ts | 1 -
4 files changed, 540 insertions(+), 6 deletions(-)
create mode 100644 extensions/typescript-language-features/src/tsServer/webhost.ts
diff --git a/extensions/postinstall.mjs b/extensions/postinstall.mjs
index 110b9b3b476c0..04e54dd6e2cab 100644
--- a/extensions/postinstall.mjs
+++ b/extensions/postinstall.mjs
@@ -26,7 +26,6 @@ function processRoot() {
function processLib() {
const toDelete = new Set([
'tsc.js',
- 'tsserverlibrary.js',
'typescriptServices.js',
]);
diff --git a/extensions/typescript-language-features/extension-browser.webpack.config.js b/extensions/typescript-language-features/extension-browser.webpack.config.js
index 7fb010d0448b6..1dc197a2e06a6 100644
--- a/extensions/typescript-language-features/extension-browser.webpack.config.js
+++ b/extensions/typescript-language-features/extension-browser.webpack.config.js
@@ -64,18 +64,20 @@ module.exports = withBrowserDefaults({
new CopyPlugin({
patterns: [
{
- from: '../node_modules/typescript/lib/tsserver.js',
+ from: '../node_modules/typescript/lib/tsserverlibrary.js',
to: 'typescript/tsserver.web.js',
transform: async (content) => {
const dynamicImportCompatPath = path.join(__dirname, '..', 'node_modules', 'typescript', 'lib', 'dynamicImportCompat.js');
+ const hostpath = path.join(__dirname, 'out', 'tsServer', 'webhost.js');
const prefix = fs.existsSync(dynamicImportCompatPath) ? fs.readFileSync(dynamicImportCompatPath) : undefined;
- const output = await Terser.minify(content.toString());
+ const host = fs.existsSync(hostpath) ? fs.readFileSync(hostpath) : undefined;
+ const output = {code: content.toString() }; // await Terser.minify(content.toString());
if (!output.code) {
throw new Error('Terser returned undefined code');
}
- if (prefix) {
- return prefix.toString() + '\n' + output.code;
+ if (prefix && host) {
+ return prefix.toString() + '\n' + output.code + '\n' + host;
}
return output.code;
},
diff --git a/extensions/typescript-language-features/src/tsServer/webhost.ts b/extensions/typescript-language-features/src/tsServer/webhost.ts
new file mode 100644
index 0000000000000..bea8d86a847d4
--- /dev/null
+++ b/extensions/typescript-language-features/src/tsServer/webhost.ts
@@ -0,0 +1,534 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+///
+///
+///
+// BEGIN misc internals
+// TODO: Might reference this as ts.sys or some other way, might rewrite stuff to not depend on it
+declare const sys: ts.System & {
+ tryEnableSourceMapsForHost?(): void;
+ getEnvironmentVariable(name: string): string;
+};
+const hasArgument: (argumentName: string) => boolean = (ts as any).server.hasArgument;
+const findArgument: (argumentName: string) => string | undefined = (ts as any).server.findArgument;
+const nowString: () => string = (ts as any).server.nowString;
+const noop = () => {};
+const perfLogger = {
+ logEvent: noop,
+ logErrEvent(_: any) {},
+ logPerfEvent(_: any) {},
+ logInfoEvent(_: any) {},
+ logStartCommand: noop,
+ logStopCommand: noop,
+ logStartUpdateProgram: noop,
+ logStopUpdateProgram: noop,
+ logStartUpdateGraph: noop,
+ logStopUpdateGraph: noop,
+ logStartResolveModule: noop,
+ logStopResolveModule: noop,
+ logStartParseSourceFile: noop,
+ logStopParseSourceFile: noop,
+ logStartReadFile: noop,
+ logStopReadFile: noop,
+ logStartBindFile: noop,
+ logStopBindFile: noop,
+ logStartScheduledOperation: noop,
+ logStopScheduledOperation: noop,
+};
+const assertNever: (member: never) => never = (ts as any).Debug.assertNever;
+const memoize: (callback: () => T) => () => T = (ts as any).memoize;
+const ensureTrailingDirectorySeparator: (path: string) => string = (ts as any).ensureTrailingDirectorySeparator;
+const getDirectoryPath: (path: string) => string = (ts as any).getDirectoryPath;
+const directorySeparator: string = (ts as any).directorySeparator;
+const combinePaths: (path: string, ...paths: (string | undefined)[]) => string = (ts as any).combinePaths;
+// NOTE: This relies on the current way that vscode injects dynamicImport into ts.server and will probably change
+const dynamicImport: ((id: string) => Promise) | undefined = (ts as any).server.dynamicImport;
+const noopFileWatcher: ts.FileWatcher = { close: noop };
+const returnNoopFileWatcher = () => noopFileWatcher;
+const getLogLevel: (level: string | undefined) => ts.server.LogLevel | undefined = (ts as any).server.getLogLevel;
+const notImplemented: () => never = (ts as any).notImplemented;
+const returnFalse: () => false = (ts as any).returnFalse;
+const returnUndefined: () => undefined = (ts as any).returnUndefined;
+const identity: (x: T) => T = (ts as any).identity;
+const indent: (str: string) => string = (ts as any).server.indent;
+const setSys: (s: ts.System) => void = (ts as any).setSys;
+const validateLocaleAndSetLanguage: (
+ locale: string,
+ sys: { getExecutingFilePath(): string; resolvePath(path: string): string; fileExists(fileName: string): boolean; readFile(fileName: string): string | undefined },
+) => void = (ts as any).validateLocaleAndSetLanguage;
+const setStackTraceLimit: () => void = (ts as any).setStackTraceLimit;
+
+// End misc internals
+// BEGIN webServer/webServer.ts
+interface HostWithWriteMessage {
+ writeMessage(s: any): void;
+}
+interface WebHost extends HostWithWriteMessage {
+ readFile(path: string): string | undefined;
+ fileExists(path: string): boolean;
+}
+
+class BaseLogger implements ts.server.Logger {
+ private seq = 0;
+ private inGroup = false;
+ private firstInGroup = true;
+ constructor(protected readonly level: ts.server.LogLevel) {
+ }
+ static padStringRight(str: string, padding: string) {
+ return (str + padding).slice(0, padding.length);
+ }
+ close() {
+ }
+ getLogFileName(): string | undefined {
+ return undefined;
+ }
+ perftrc(s: string) {
+ this.msg(s, ts.server.Msg.Perf);
+ }
+ info(s: string) {
+ this.msg(s, ts.server.Msg.Info);
+ }
+ err(s: string) {
+ this.msg(s, ts.server.Msg.Err);
+ }
+ startGroup() {
+ this.inGroup = true;
+ this.firstInGroup = true;
+ }
+ endGroup() {
+ this.inGroup = false;
+ }
+ loggingEnabled() {
+ return true;
+ }
+ hasLevel(level: ts.server.LogLevel) {
+ return this.loggingEnabled() && this.level >= level;
+ }
+ msg(s: string, type: ts.server.Msg = ts.server.Msg.Err) {
+ switch (type) {
+ case ts.server.Msg.Info:
+ perfLogger.logInfoEvent(s);
+ break;
+ case ts.server.Msg.Perf:
+ perfLogger.logPerfEvent(s);
+ break;
+ default: // Msg.Err
+ perfLogger.logErrEvent(s);
+ break;
+ }
+
+ if (!this.canWrite()) { return; }
+
+ s = `[${nowString()}] ${s}\n`;
+ if (!this.inGroup || this.firstInGroup) {
+ const prefix = BaseLogger.padStringRight(type + ' ' + this.seq.toString(), ' ');
+ s = prefix + s;
+ }
+ this.write(s, type);
+ if (!this.inGroup) {
+ this.seq++;
+ }
+ }
+ protected canWrite() {
+ return true;
+ }
+ protected write(_s: string, _type: ts.server.Msg) {
+ }
+}
+
+type MessageLogLevel = 'info' | 'perf' | 'error';
+interface LoggingMessage {
+ readonly type: 'log';
+ readonly level: MessageLogLevel;
+ readonly body: string;
+}
+class MainProcessLogger extends BaseLogger {
+ constructor(level: ts.server.LogLevel, private host: HostWithWriteMessage) {
+ super(level);
+ }
+ protected override write(body: string, type: ts.server.Msg) {
+ let level: MessageLogLevel;
+ switch (type) {
+ case ts.server.Msg.Info:
+ level = 'info';
+ break;
+ case ts.server.Msg.Perf:
+ level = 'perf';
+ break;
+ case ts.server.Msg.Err:
+ level = 'error';
+ break;
+ default:
+ assertNever(type);
+ }
+ this.host.writeMessage({
+ type: 'log',
+ level,
+ body,
+ } as LoggingMessage);
+ }
+}
+
+// TODO: rename one or the other web systems
+function serverCreateWebSystem(host: WebHost, args: string[], getExecutingFilePath: () => string):
+ ts.server.ServerHost & {
+ importPlugin?(root: string, moduleName: string): Promise;
+ getEnvironmentVariable(name: string): string;
+ } {
+ 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) => path.startsWith(directorySeparator) ? path.replace(directorySeparator, getExecutingDirectoryPath()) : undefined;
+
+ const dynamicImport = async (id: string): Promise => {
+ // Use syntactic dynamic import first, if available
+ if (dynamicImport) {
+ return dynamicImport(id);
+ }
+
+ throw new Error('Dynamic import not implemented');
+ };
+
+ return {
+ args,
+ newLine: '\r\n', // This can be configured by clients
+ useCaseSensitiveFileNames: false, // Use false as the default on web since that is the safest option
+ readFile: path => {
+ const webPath = getWebPath(path);
+ return webPath && host.readFile(webPath);
+ },
+ write: host.writeMessage.bind(host),
+ watchFile: returnNoopFileWatcher,
+ watchDirectory: returnNoopFileWatcher,
+
+ getExecutingFilePath: () => directorySeparator,
+ getCurrentDirectory: returnEmptyString, // For inferred project root if projectRoot path is not set, normalizing the paths
+
+ /* eslint-disable no-restricted-globals */
+ setTimeout: (cb, ms, ...args) => setTimeout(cb, ms, ...args),
+ clearTimeout: handle => clearTimeout(handle),
+ setImmediate: x => setTimeout(x, 0),
+ clearImmediate: handle => clearTimeout(handle),
+ /* eslint-enable no-restricted-globals */
+
+ importPlugin: async (initialDir: string, moduleName: string): Promise => {
+ const packageRoot = combinePaths(initialDir, moduleName);
+
+ let packageJson: any | undefined;
+ try {
+ const packageJsonResponse = await fetch(combinePaths(packageRoot, 'package.json'));
+ packageJson = await packageJsonResponse.json();
+ }
+ catch (e) {
+ return { module: undefined, error: new Error('Could not load plugin. Could not load "package.json".') };
+ }
+
+ const browser = packageJson.browser;
+ if (!browser) {
+ return { module: undefined, error: new Error('Could not load plugin. No "browser" field found in package.json.') };
+ }
+
+ const scriptPath = combinePaths(packageRoot, browser);
+ try {
+ const { default: module } = await dynamicImport(scriptPath);
+ return { module, error: undefined };
+ }
+ catch (e) {
+ return { module: undefined, error: e };
+ }
+ },
+ exit: notImplemented,
+
+ // Debugging related
+ getEnvironmentVariable: returnEmptyString, // TODO:: Used to enable debugging info
+ // tryEnableSourceMapsForHost?(): void;
+ // debugMode?: boolean;
+
+ // For semantic server mode
+ fileExists: path => {
+ const webPath = getWebPath(path);
+ return !!webPath && host.fileExists(webPath);
+ },
+ directoryExists: returnFalse, // Module resolution
+ readDirectory: notImplemented, // Configured project, typing installer
+ getDirectories: () => [], // For automatic type reference directives
+ createDirectory: notImplemented, // compile On save
+ writeFile: notImplemented, // compile on save
+ resolvePath: identity, // Plugins
+ // realpath? // Module resolution, symlinks
+ // getModifiedTime // File watching
+ // createSHA256Hash // telemetry of the project
+
+ // Logging related
+ // /*@internal*/ bufferFrom?(input: string, encoding?: string): Buffer;
+ // gc?(): void;
+ // getMemoryUsage?(): number;
+ };
+}
+
+interface StartSessionOptions {
+ globalPlugins: ts.server.SessionOptions['globalPlugins'];
+ pluginProbeLocations: ts.server.SessionOptions['pluginProbeLocations'];
+ allowLocalPluginLoads: ts.server.SessionOptions['allowLocalPluginLoads'];
+ useSingleInferredProject: ts.server.SessionOptions['useSingleInferredProject'];
+ useInferredProjectPerProjectRoot: ts.server.SessionOptions['useInferredProjectPerProjectRoot'];
+ suppressDiagnosticEvents: ts.server.SessionOptions['suppressDiagnosticEvents'];
+ noGetErrOnBackgroundUpdate: ts.server.SessionOptions['noGetErrOnBackgroundUpdate'];
+ syntaxOnly: ts.server.SessionOptions['syntaxOnly'];
+ serverMode: ts.server.SessionOptions['serverMode'];
+}
+class ServerWorkerSession extends ts.server.Session<{}> {
+ constructor(
+ host: ts.server.ServerHost,
+ private webHost: HostWithWriteMessage,
+ options: StartSessionOptions,
+ logger: ts.server.Logger,
+ cancellationToken: ts.server.ServerCancellationToken,
+ hrtime: ts.server.SessionOptions['hrtime']
+ ) {
+ super({
+ host,
+ cancellationToken,
+ ...options,
+ typingsInstaller: ts.server.nullTypingsInstaller,
+ byteLength: notImplemented, // Formats the message text in send of Session which is overriden in this class so not needed
+ hrtime,
+ logger,
+ canUseEvents: true,
+ });
+ }
+
+ public override send(msg: ts.server.protocol.Message) {
+ if (msg.type === 'event' && !this.canUseEvents) {
+ if (this.logger.hasLevel(ts.server.LogLevel.verbose)) {
+ this.logger.info(`Session does not support events: ignored event: ${JSON.stringify(msg)}`);
+ }
+ return;
+ }
+ if (this.logger.hasLevel(ts.server.LogLevel.verbose)) {
+ this.logger.info(`${msg.type}:${indent(JSON.stringify(msg))}`);
+ }
+ this.webHost.writeMessage(msg);
+ }
+
+ protected override parseMessage(message: {}): ts.server.protocol.Request {
+ return message as ts.server.protocol.Request;
+ }
+
+ protected override toStringMessage(message: {}) {
+ return JSON.stringify(message, undefined, 2);
+ }
+}
+// END webServer/webServer.ts
+// BEGIN tsserver/webServer.ts
+const nullLogger: ts.server.Logger = {
+ close: noop,
+ hasLevel: returnFalse,
+ loggingEnabled: returnFalse,
+ perftrc: noop,
+ info: noop,
+ msg: noop,
+ startGroup: noop,
+ endGroup: noop,
+ getLogFileName: returnUndefined,
+};
+
+function parseServerMode(): ts.LanguageServiceMode | string | undefined {
+ const mode = findArgument('--serverMode');
+ if (!mode) { return undefined; }
+ switch (mode.toLowerCase()) {
+ case 'partialsemantic':
+ return ts.LanguageServiceMode.PartialSemantic;
+ case 'syntactic':
+ return ts.LanguageServiceMode.Syntactic;
+ default:
+ return mode;
+ }
+}
+
+function initializeWebSystem(args: string[]): StartInput {
+ createWebSystem(args);
+ const modeOrUnknown = parseServerMode();
+ let serverMode: ts.LanguageServiceMode | undefined;
+ let unknownServerMode: string | undefined;
+ if (typeof modeOrUnknown === 'number') { serverMode = modeOrUnknown; }
+ else { unknownServerMode = modeOrUnknown; }
+ const logger = createLogger();
+
+ // enable deprecation logging
+ (ts as any).Debug.loggingHost = {
+ log(level: unknown, s: string) {
+ switch (level) {
+ case (ts as any).LogLevel.Error:
+ case (ts as any).LogLevel.Warning:
+ return logger.msg(s, ts.server.Msg.Err);
+ case (ts as any).LogLevel.Info:
+ case (ts as any).LogLevel.Verbose:
+ return logger.msg(s, ts.server.Msg.Info);
+ }
+ }
+ };
+
+ return {
+ args,
+ logger,
+ cancellationToken: ts.server.nullCancellationToken,
+ // Webserver defaults to partial semantic mode
+ serverMode: serverMode ?? ts.LanguageServiceMode.PartialSemantic,
+ unknownServerMode,
+ startSession: startWebSession
+ };
+}
+
+function createLogger() {
+ const cmdLineVerbosity = getLogLevel(findArgument('--logVerbosity'));
+ return cmdLineVerbosity !== undefined ? new MainProcessLogger(cmdLineVerbosity, { writeMessage }) : nullLogger;
+}
+
+function writeMessage(s: any) {
+ postMessage(s);
+}
+
+function createWebSystem(args: string[]) {
+ (ts as any).Debug.assert(ts.sys === undefined);
+ const webHost: WebHost = {
+ readFile: webPath => {
+ const request = new XMLHttpRequest();
+ request.open('GET', webPath, /* asynchronous */ false);
+ request.send();
+ return request.status === 200 ? request.responseText : undefined;
+ },
+ fileExists: webPath => {
+ const request = new XMLHttpRequest();
+ request.open('HEAD', webPath, /* asynchronous */ false);
+ request.send();
+ return request.status === 200;
+ },
+ writeMessage,
+ };
+ // Do this after sys has been set as findArguments is going to work only then
+ const sys = serverCreateWebSystem(webHost, args, () => findArgument('--executingFilePath') || location + '');
+ setSys(sys);
+ const localeStr = findArgument('--locale');
+ if (localeStr) {
+ validateLocaleAndSetLanguage(localeStr, sys);
+ }
+}
+
+function hrtime(previous?: number[]) {
+ const now = self.performance.now() * 1e-3;
+ let seconds = Math.floor(now);
+ let nanoseconds = Math.floor((now % 1) * 1e9);
+ // NOTE: This check is added probably because it's missed without strictFunctionTypes on
+ if (previous?.[0] !== undefined && previous?.[1] !== undefined) {
+ seconds = seconds - previous[0];
+ nanoseconds = nanoseconds - previous[1];
+ if (nanoseconds < 0) {
+ seconds--;
+ nanoseconds += 1e9;
+ }
+ }
+ return [seconds, nanoseconds];
+}
+
+function startWebSession(options: StartSessionOptions, logger: ts.server.Logger, cancellationToken: ts.server.ServerCancellationToken) {
+ class WorkerSession extends ServerWorkerSession {
+ constructor() {
+ super(
+ sys as ts.server.ServerHost & {tryEnableSourceMapsForHost?(): void; getEnvironmentVariable(name: string): string },
+ { writeMessage },
+ options,
+ logger,
+ cancellationToken,
+ hrtime);
+ }
+
+ override exit() {
+ this.logger.info('Exiting...');
+ this.projectService.closeLog();
+ close();
+ }
+
+ listen() {
+ addEventListener('message', (message: any) => {
+ this.onMessage(message.data);
+ });
+ }
+ }
+
+ const session = new WorkerSession();
+
+ // Start listening
+ session.listen();
+}
+// END tsserver/webServer.ts
+// BEGIN tsserver/server.ts
+function findArgumentStringArray(argName: string): readonly string[] {
+ const arg = findArgument(argName);
+ if (arg === undefined) {
+ return [];
+ }
+ return arg.split(',').filter(name => name !== '');
+}
+
+interface StartInput {
+ args: readonly string[];
+ logger: ts.server.Logger;
+ cancellationToken: ts.server.ServerCancellationToken;
+ serverMode: ts.LanguageServiceMode | undefined;
+ unknownServerMode?: string;
+ startSession: (option: StartSessionOptions, logger: ts.server.Logger, cancellationToken: ts.server.ServerCancellationToken) => void;
+}
+function start({ args, logger, cancellationToken, serverMode, unknownServerMode, startSession: startServer }: StartInput, platform: string) {
+ const syntaxOnly = hasArgument('--syntaxOnly');
+
+ logger.info(`Starting TS Server`);
+ logger.info(`Version: Moved from Typescript 5.0.0-dev`);
+ logger.info(`Arguments: ${args.join(' ')}`);
+ logger.info(`Platform: ${platform} NodeVersion: N/A CaseSensitive: ${sys.useCaseSensitiveFileNames}`);
+ logger.info(`ServerMode: ${serverMode} syntaxOnly: ${syntaxOnly} hasUnknownServerMode: ${unknownServerMode}`);
+
+ setStackTraceLimit();
+
+ if ((ts as any).Debug.isDebugging) {
+ (ts as any).Debug.enableDebugInfo();
+ }
+
+ if (sys.tryEnableSourceMapsForHost && /^development$/i.test(sys.getEnvironmentVariable('NODE_ENV'))) {
+ sys.tryEnableSourceMapsForHost();
+ }
+
+ // Overwrites the current console messages to instead write to
+ // the log. This is so that language service plugins which use
+ // console.log don't break the message passing between tsserver
+ // and the client
+ console.log = (...args) => logger.msg(args.length === 1 ? args[0] : args.join(', '), ts.server.Msg.Info);
+ console.warn = (...args) => logger.msg(args.length === 1 ? args[0] : args.join(', '), ts.server.Msg.Err);
+ console.error = (...args) => logger.msg(args.length === 1 ? args[0] : args.join(', '), ts.server.Msg.Err);
+
+ startServer(
+ {
+ globalPlugins: findArgumentStringArray('--globalPlugins'),
+ pluginProbeLocations: findArgumentStringArray('--pluginProbeLocations'),
+ allowLocalPluginLoads: hasArgument('--allowLocalPluginLoads'),
+ useSingleInferredProject: hasArgument('--useSingleInferredProject'),
+ useInferredProjectPerProjectRoot: hasArgument('--useInferredProjectPerProjectRoot'),
+ suppressDiagnosticEvents: hasArgument('--suppressDiagnosticEvents'),
+ noGetErrOnBackgroundUpdate: hasArgument('--noGetErrOnBackgroundUpdate'),
+ syntaxOnly,
+ serverMode
+ },
+ logger,
+ cancellationToken
+ );
+}
+// Get args from first message
+const listener = (e: any) => {
+ removeEventListener('message', listener);
+ const args = e.data;
+ start(initializeWebSystem(args), 'web');
+};
+addEventListener('message', listener);
+// END tsserver/server.ts
diff --git a/extensions/typescript-language-features/src/utils/platform.ts b/extensions/typescript-language-features/src/utils/platform.ts
index 2d754bf405471..7fa211c79699b 100644
--- a/extensions/typescript-language-features/src/utils/platform.ts
+++ b/extensions/typescript-language-features/src/utils/platform.ts
@@ -6,6 +6,5 @@
import * as vscode from 'vscode';
export function isWeb(): boolean {
- // @ts-expect-error
return typeof navigator !== 'undefined' && vscode.env.uiKind === vscode.UIKind.Web;
}
From 4b4ca3dc8505c261c7498f38a9c3947792ffbb2e Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Mon, 7 Nov 2022 16:20:50 -0800
Subject: [PATCH 2/9] Various fixes
It works now
---
.../extension-browser.webpack.config.js | 2 +-
.../src/tsServer/{webhost.ts => webServer.ts} | 34 +++++++++++--------
2 files changed, 21 insertions(+), 15 deletions(-)
rename extensions/typescript-language-features/src/tsServer/{webhost.ts => webServer.ts} (95%)
diff --git a/extensions/typescript-language-features/extension-browser.webpack.config.js b/extensions/typescript-language-features/extension-browser.webpack.config.js
index 1dc197a2e06a6..bc336a8eacab8 100644
--- a/extensions/typescript-language-features/extension-browser.webpack.config.js
+++ b/extensions/typescript-language-features/extension-browser.webpack.config.js
@@ -68,7 +68,7 @@ module.exports = withBrowserDefaults({
to: 'typescript/tsserver.web.js',
transform: async (content) => {
const dynamicImportCompatPath = path.join(__dirname, '..', 'node_modules', 'typescript', 'lib', 'dynamicImportCompat.js');
- const hostpath = path.join(__dirname, 'out', 'tsServer', 'webhost.js');
+ const hostpath = path.join(__dirname, 'out', 'tsServer', 'webServer.js');
const prefix = fs.existsSync(dynamicImportCompatPath) ? fs.readFileSync(dynamicImportCompatPath) : undefined;
const host = fs.existsSync(hostpath) ? fs.readFileSync(hostpath) : undefined;
const output = {code: content.toString() }; // await Terser.minify(content.toString());
diff --git a/extensions/typescript-language-features/src/tsServer/webhost.ts b/extensions/typescript-language-features/src/tsServer/webServer.ts
similarity index 95%
rename from extensions/typescript-language-features/src/tsServer/webhost.ts
rename to extensions/typescript-language-features/src/tsServer/webServer.ts
index bea8d86a847d4..3d7adbfa01316 100644
--- a/extensions/typescript-language-features/src/tsServer/webhost.ts
+++ b/extensions/typescript-language-features/src/tsServer/webServer.ts
@@ -6,20 +6,15 @@
///
///
// BEGIN misc internals
-// TODO: Might reference this as ts.sys or some other way, might rewrite stuff to not depend on it
-declare const sys: ts.System & {
- tryEnableSourceMapsForHost?(): void;
- getEnvironmentVariable(name: string): string;
-};
const hasArgument: (argumentName: string) => boolean = (ts as any).server.hasArgument;
const findArgument: (argumentName: string) => string | undefined = (ts as any).server.findArgument;
const nowString: () => string = (ts as any).server.nowString;
-const noop = () => {};
+const noop = () => { };
const perfLogger = {
logEvent: noop,
- logErrEvent(_: any) {},
- logPerfEvent(_: any) {},
- logInfoEvent(_: any) {},
+ logErrEvent(_: any) { },
+ logPerfEvent(_: any) { },
+ logInfoEvent(_: any) { },
logStartCommand: noop,
logStopCommand: noop,
logStartUpdateProgram: noop,
@@ -47,7 +42,18 @@ const combinePaths: (path: string, ...paths: (string | undefined)[]) => string =
const dynamicImport: ((id: string) => Promise) | undefined = (ts as any).server.dynamicImport;
const noopFileWatcher: ts.FileWatcher = { close: noop };
const returnNoopFileWatcher = () => noopFileWatcher;
-const getLogLevel: (level: string | undefined) => ts.server.LogLevel | undefined = (ts as any).server.getLogLevel;
+function getLogLevel(level: string | undefined) {
+ if (level) {
+ const l = level.toLowerCase();
+ for (const name in ts.server.LogLevel) {
+ if (isNaN(+name) && l === name.toLowerCase()) {
+ return ts.server.LogLevel[name] as any as ts.server.LogLevel;
+ }
+ }
+ }
+ return undefined;
+}
+
const notImplemented: () => never = (ts as any).notImplemented;
const returnFalse: () => false = (ts as any).returnFalse;
const returnUndefined: () => undefined = (ts as any).returnUndefined;
@@ -437,7 +443,7 @@ function startWebSession(options: StartSessionOptions, logger: ts.server.Logger,
class WorkerSession extends ServerWorkerSession {
constructor() {
super(
- sys as ts.server.ServerHost & {tryEnableSourceMapsForHost?(): void; getEnvironmentVariable(name: string): string },
+ ts.sys as ts.server.ServerHost & { tryEnableSourceMapsForHost?(): void; getEnvironmentVariable(name: string): string },
{ writeMessage },
options,
logger,
@@ -487,7 +493,7 @@ function start({ args, logger, cancellationToken, serverMode, unknownServerMode,
logger.info(`Starting TS Server`);
logger.info(`Version: Moved from Typescript 5.0.0-dev`);
logger.info(`Arguments: ${args.join(' ')}`);
- logger.info(`Platform: ${platform} NodeVersion: N/A CaseSensitive: ${sys.useCaseSensitiveFileNames}`);
+ logger.info(`Platform: ${platform} NodeVersion: N/A CaseSensitive: ${ts.sys.useCaseSensitiveFileNames}`);
logger.info(`ServerMode: ${serverMode} syntaxOnly: ${syntaxOnly} hasUnknownServerMode: ${unknownServerMode}`);
setStackTraceLimit();
@@ -496,8 +502,8 @@ function start({ args, logger, cancellationToken, serverMode, unknownServerMode,
(ts as any).Debug.enableDebugInfo();
}
- if (sys.tryEnableSourceMapsForHost && /^development$/i.test(sys.getEnvironmentVariable('NODE_ENV'))) {
- sys.tryEnableSourceMapsForHost();
+ if ((ts as any).sys.tryEnableSourceMapsForHost && /^development$/i.test((ts as any).sys.getEnvironmentVariable('NODE_ENV'))) {
+ (ts as any).sys.tryEnableSourceMapsForHost();
}
// Overwrites the current console messages to instead write to
From dc3c7172377b2a9f09dc20c0f6c4199820a8ec62 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Mon, 7 Nov 2022 16:32:32 -0800
Subject: [PATCH 3/9] A bit of cleanup
---
.../extension-browser.webpack.config.js | 2 +-
.../typescript-language-features/src/tsServer/webServer.ts | 1 -
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/extensions/typescript-language-features/extension-browser.webpack.config.js b/extensions/typescript-language-features/extension-browser.webpack.config.js
index bc336a8eacab8..2e6d1f87c47c2 100644
--- a/extensions/typescript-language-features/extension-browser.webpack.config.js
+++ b/extensions/typescript-language-features/extension-browser.webpack.config.js
@@ -71,7 +71,7 @@ module.exports = withBrowserDefaults({
const hostpath = path.join(__dirname, 'out', 'tsServer', 'webServer.js');
const prefix = fs.existsSync(dynamicImportCompatPath) ? fs.readFileSync(dynamicImportCompatPath) : undefined;
const host = fs.existsSync(hostpath) ? fs.readFileSync(hostpath) : undefined;
- const output = {code: content.toString() }; // await Terser.minify(content.toString());
+ const output = await Terser.minify(content.toString());
if (!output.code) {
throw new Error('Terser returned undefined code');
}
diff --git a/extensions/typescript-language-features/src/tsServer/webServer.ts b/extensions/typescript-language-features/src/tsServer/webServer.ts
index 3d7adbfa01316..299e6ffe7a22e 100644
--- a/extensions/typescript-language-features/src/tsServer/webServer.ts
+++ b/extensions/typescript-language-features/src/tsServer/webServer.ts
@@ -177,7 +177,6 @@ class MainProcessLogger extends BaseLogger {
}
}
-// TODO: rename one or the other web systems
function serverCreateWebSystem(host: WebHost, args: string[], getExecutingFilePath: () => string):
ts.server.ServerHost & {
importPlugin?(root: string, moduleName: string): Promise;
From a9ce12d02c40a95100e79d878c1b6723755d5107 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Thu, 10 Nov 2022 11:09:22 -0800
Subject: [PATCH 4/9] Move webServer to its own directory
And prepare for getting rid of dynamicImportCompat.js hack
---
build/gulpfile.extensions.js | 1 +
.../extension-browser.webpack.config.js | 2 +-
.../src/utils/platform.ts | 1 +
.../web/tsconfig.json | 15 +++++++++++++++
.../{src/tsServer => web}/webServer.ts | 0
5 files changed, 18 insertions(+), 1 deletion(-)
create mode 100644 extensions/typescript-language-features/web/tsconfig.json
rename extensions/typescript-language-features/{src/tsServer => web}/webServer.ts (100%)
diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js
index c0b79bc0f9d78..3c5f90f16d6f5 100644
--- a/build/gulpfile.extensions.js
+++ b/build/gulpfile.extensions.js
@@ -64,6 +64,7 @@ const compilations = [
'references-view/tsconfig.json',
'simple-browser/tsconfig.json',
'typescript-language-features/test-workspace/tsconfig.json',
+ 'typescript-language-features/web/tsconfig.json',
'typescript-language-features/tsconfig.json',
'vscode-api-tests/tsconfig.json',
'vscode-colorize-tests/tsconfig.json',
diff --git a/extensions/typescript-language-features/extension-browser.webpack.config.js b/extensions/typescript-language-features/extension-browser.webpack.config.js
index 2e6d1f87c47c2..e2ebe5af53209 100644
--- a/extensions/typescript-language-features/extension-browser.webpack.config.js
+++ b/extensions/typescript-language-features/extension-browser.webpack.config.js
@@ -68,7 +68,7 @@ module.exports = withBrowserDefaults({
to: 'typescript/tsserver.web.js',
transform: async (content) => {
const dynamicImportCompatPath = path.join(__dirname, '..', 'node_modules', 'typescript', 'lib', 'dynamicImportCompat.js');
- const hostpath = path.join(__dirname, 'out', 'tsServer', 'webServer.js');
+ const hostpath = path.join(__dirname, 'web', 'out', 'webServer.js');
const prefix = fs.existsSync(dynamicImportCompatPath) ? fs.readFileSync(dynamicImportCompatPath) : undefined;
const host = fs.existsSync(hostpath) ? fs.readFileSync(hostpath) : undefined;
const output = await Terser.minify(content.toString());
diff --git a/extensions/typescript-language-features/src/utils/platform.ts b/extensions/typescript-language-features/src/utils/platform.ts
index 7fa211c79699b..ccd72d47ad34e 100644
--- a/extensions/typescript-language-features/src/utils/platform.ts
+++ b/extensions/typescript-language-features/src/utils/platform.ts
@@ -5,6 +5,7 @@
import * as vscode from 'vscode';
+declare const navigator: unknown;
export function isWeb(): boolean {
return typeof navigator !== 'undefined' && vscode.env.uiKind === vscode.UIKind.Web;
}
diff --git a/extensions/typescript-language-features/web/tsconfig.json b/extensions/typescript-language-features/web/tsconfig.json
new file mode 100644
index 0000000000000..9944d5b63d8f9
--- /dev/null
+++ b/extensions/typescript-language-features/web/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "outDir": "../../out",
+ "module": "nodenext",
+ "moduleDetection": "legacy",
+ "experimentalDecorators": true,
+ "types": [
+ "node"
+ ]
+ },
+ "files": [
+ "webServer.ts"
+ ]
+}
diff --git a/extensions/typescript-language-features/src/tsServer/webServer.ts b/extensions/typescript-language-features/web/webServer.ts
similarity index 100%
rename from extensions/typescript-language-features/src/tsServer/webServer.ts
rename to extensions/typescript-language-features/web/webServer.ts
From c44d4c429dfa863de8650f8945c35a4cab20ffb4 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Thu, 10 Nov 2022 11:29:33 -0800
Subject: [PATCH 5/9] Remove dynamicImportCompat.js hack
---
.../extension-browser.webpack.config.js | 6 ++----
.../typescript-language-features/web/webServer.ts | 13 +------------
2 files changed, 3 insertions(+), 16 deletions(-)
diff --git a/extensions/typescript-language-features/extension-browser.webpack.config.js b/extensions/typescript-language-features/extension-browser.webpack.config.js
index e2ebe5af53209..d9106c1e017ea 100644
--- a/extensions/typescript-language-features/extension-browser.webpack.config.js
+++ b/extensions/typescript-language-features/extension-browser.webpack.config.js
@@ -67,17 +67,15 @@ module.exports = withBrowserDefaults({
from: '../node_modules/typescript/lib/tsserverlibrary.js',
to: 'typescript/tsserver.web.js',
transform: async (content) => {
- const dynamicImportCompatPath = path.join(__dirname, '..', 'node_modules', 'typescript', 'lib', 'dynamicImportCompat.js');
const hostpath = path.join(__dirname, 'web', 'out', 'webServer.js');
- const prefix = fs.existsSync(dynamicImportCompatPath) ? fs.readFileSync(dynamicImportCompatPath) : undefined;
const host = fs.existsSync(hostpath) ? fs.readFileSync(hostpath) : undefined;
const output = await Terser.minify(content.toString());
if (!output.code) {
throw new Error('Terser returned undefined code');
}
- if (prefix && host) {
- return prefix.toString() + '\n' + output.code + '\n' + host;
+ if (host) {
+ return output.code + '\n' + host;
}
return output.code;
},
diff --git a/extensions/typescript-language-features/web/webServer.ts b/extensions/typescript-language-features/web/webServer.ts
index 299e6ffe7a22e..b4461810accb1 100644
--- a/extensions/typescript-language-features/web/webServer.ts
+++ b/extensions/typescript-language-features/web/webServer.ts
@@ -38,8 +38,6 @@ const ensureTrailingDirectorySeparator: (path: string) => string = (ts as any).e
const getDirectoryPath: (path: string) => string = (ts as any).getDirectoryPath;
const directorySeparator: string = (ts as any).directorySeparator;
const combinePaths: (path: string, ...paths: (string | undefined)[]) => string = (ts as any).combinePaths;
-// NOTE: This relies on the current way that vscode injects dynamicImport into ts.server and will probably change
-const dynamicImport: ((id: string) => Promise) | undefined = (ts as any).server.dynamicImport;
const noopFileWatcher: ts.FileWatcher = { close: noop };
const returnNoopFileWatcher = () => noopFileWatcher;
function getLogLevel(level: string | undefined) {
@@ -187,15 +185,6 @@ function serverCreateWebSystem(host: WebHost, args: string[], getExecutingFilePa
// 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) => path.startsWith(directorySeparator) ? path.replace(directorySeparator, getExecutingDirectoryPath()) : undefined;
- const dynamicImport = async (id: string): Promise => {
- // Use syntactic dynamic import first, if available
- if (dynamicImport) {
- return dynamicImport(id);
- }
-
- throw new Error('Dynamic import not implemented');
- };
-
return {
args,
newLine: '\r\n', // This can be configured by clients
@@ -237,7 +226,7 @@ function serverCreateWebSystem(host: WebHost, args: string[], getExecutingFilePa
const scriptPath = combinePaths(packageRoot, browser);
try {
- const { default: module } = await dynamicImport(scriptPath);
+ const { default: module } = await import(scriptPath);
return { module, error: undefined };
}
catch (e) {
From 1094941799c37c35a4039fb8911faf0c89e0511d Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Thu, 10 Nov 2022 11:37:22 -0800
Subject: [PATCH 6/9] Revert unrelated change
---
extensions/typescript-language-features/src/utils/platform.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/extensions/typescript-language-features/src/utils/platform.ts b/extensions/typescript-language-features/src/utils/platform.ts
index ccd72d47ad34e..2d754bf405471 100644
--- a/extensions/typescript-language-features/src/utils/platform.ts
+++ b/extensions/typescript-language-features/src/utils/platform.ts
@@ -5,7 +5,7 @@
import * as vscode from 'vscode';
-declare const navigator: unknown;
export function isWeb(): boolean {
+ // @ts-expect-error
return typeof navigator !== 'undefined' && vscode.env.uiKind === vscode.UIKind.Web;
}
From d751d5c94f24c802b3ed76c1b6ac74943c046483 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Fri, 11 Nov 2022 10:41:01 -0800
Subject: [PATCH 7/9] Webpac tsserver.web.js with webServer.ts as entrypoint
Instead of using CopyPlugin.
1. Shipping multiple entrypoints in a single file required fixes to
build code.
2. There are a couple of warnings from `require` calls in
tsserverlibrary.js. Those are not relevant since they're in non-web
code, but I haven't figured how to turn them off; they are fully dynamic
so `externals` didn't work.
---
build/lib/extensions.js | 17 +++----
build/lib/extensions.ts | 18 +++----
.../extension-browser.webpack.config.js | 47 +++++++------------
.../web/webServer.ts | 4 +-
4 files changed, 31 insertions(+), 55 deletions(-)
diff --git a/build/lib/extensions.js b/build/lib/extensions.js
index f69b33220188b..bcf0f0344429d 100644
--- a/build/lib/extensions.js
+++ b/build/lib/extensions.js
@@ -414,19 +414,14 @@ async function webpackExtensions(taskName, isWatch, webpackConfigLocations) {
const webpackConfigs = [];
for (const { configPath, outputRoot } of webpackConfigLocations) {
const configOrFnOrArray = require(configPath);
- function addConfig(configOrFn) {
- let config;
- if (typeof configOrFn === 'function') {
- config = configOrFn({}, {});
+ function addConfig(configOrFnOrArray) {
+ for (const configOrFn of Array.isArray(configOrFnOrArray) ? configOrFnOrArray : [configOrFnOrArray]) {
+ const config = typeof configOrFn === 'function' ? configOrFn({}, {}) : configOrFn;
+ if (outputRoot) {
+ config.output.path = path.join(outputRoot, path.relative(path.dirname(configPath), config.output.path));
+ }
webpackConfigs.push(config);
}
- else {
- config = configOrFn;
- }
- if (outputRoot) {
- config.output.path = path.join(outputRoot, path.relative(path.dirname(configPath), config.output.path));
- }
- webpackConfigs.push(configOrFn);
}
addConfig(configOrFnOrArray);
}
diff --git a/build/lib/extensions.ts b/build/lib/extensions.ts
index 36f29fcf3a1e4..c46c29380fcfa 100644
--- a/build/lib/extensions.ts
+++ b/build/lib/extensions.ts
@@ -506,20 +506,14 @@ export async function webpackExtensions(taskName: string, isWatch: boolean, webp
for (const { configPath, outputRoot } of webpackConfigLocations) {
const configOrFnOrArray = require(configPath);
- function addConfig(configOrFn: webpack.Configuration | Function) {
- let config;
- if (typeof configOrFn === 'function') {
- config = (configOrFn as Function)({}, {});
+ function addConfig(configOrFnOrArray: webpack.Configuration | ((env: unknown,args: unknown) => webpack.Configuration) | webpack.Configuration[]) {
+ for (const configOrFn of Array.isArray(configOrFnOrArray) ? configOrFnOrArray : [configOrFnOrArray]) {
+ const config = typeof configOrFn === 'function' ? configOrFn({}, {}) : configOrFn;
+ if (outputRoot) {
+ config.output!.path = path.join(outputRoot, path.relative(path.dirname(configPath), config.output!.path!));
+ }
webpackConfigs.push(config);
- } else {
- config = configOrFn;
}
-
- if (outputRoot) {
- config.output.path = path.join(outputRoot, path.relative(path.dirname(configPath), config.output.path));
- }
-
- webpackConfigs.push(configOrFn);
}
addConfig(configOrFnOrArray);
}
diff --git a/extensions/typescript-language-features/extension-browser.webpack.config.js b/extensions/typescript-language-features/extension-browser.webpack.config.js
index d9106c1e017ea..298254f8d0e66 100644
--- a/extensions/typescript-language-features/extension-browser.webpack.config.js
+++ b/extensions/typescript-language-features/extension-browser.webpack.config.js
@@ -7,8 +7,6 @@
'use strict';
const CopyPlugin = require('copy-webpack-plugin');
-const Terser = require('terser');
-const fs = require('fs');
const path = require('path');
const defaultConfig = require('../shared.webpack.config');
@@ -30,8 +28,7 @@ const languages = [
'tr',
'zh-cn',
];
-
-module.exports = withBrowserDefaults({
+module.exports = [withBrowserDefaults({
context: __dirname,
entry: {
extension: './src/extension.browser.ts',
@@ -60,30 +57,20 @@ module.exports = withBrowserDefaults({
}))
],
}),
- // @ts-ignore
- new CopyPlugin({
- patterns: [
- {
- from: '../node_modules/typescript/lib/tsserverlibrary.js',
- to: 'typescript/tsserver.web.js',
- transform: async (content) => {
- const hostpath = path.join(__dirname, 'web', 'out', 'webServer.js');
- const host = fs.existsSync(hostpath) ? fs.readFileSync(hostpath) : undefined;
- const output = await Terser.minify(content.toString());
- if (!output.code) {
- throw new Error('Terser returned undefined code');
- }
-
- if (host) {
- return output.code + '\n' + host;
- }
- return output.code;
- },
- transformPath: (targetPath) => {
- return targetPath.replace('tsserver.js', 'tsserver.web.js');
- }
- }
- ],
- }),
],
-});
+}), withBrowserDefaults({
+ context: __dirname,
+ entry: {
+ 'typescript/tsserver.web': './web/webServer.ts'
+ },
+ output: {
+ // all output goes into `dist`.
+ // packaging depends on that and this must always be like it
+ filename: '[name].js',
+ path: path.join(__dirname, 'dist', 'browser'),
+ libraryTarget: undefined,
+ },
+ externals: {
+ 'perf_hooks': 'commonjs perf_hooks',
+ }
+})];
diff --git a/extensions/typescript-language-features/web/webServer.ts b/extensions/typescript-language-features/web/webServer.ts
index b4461810accb1..680228881fa2d 100644
--- a/extensions/typescript-language-features/web/webServer.ts
+++ b/extensions/typescript-language-features/web/webServer.ts
@@ -2,9 +2,9 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-///
///
///
+import * as ts from 'typescript/lib/tsserverlibrary';
// BEGIN misc internals
const hasArgument: (argumentName: string) => boolean = (ts as any).server.hasArgument;
const findArgument: (argumentName: string) => string | undefined = (ts as any).server.findArgument;
@@ -226,7 +226,7 @@ function serverCreateWebSystem(host: WebHost, args: string[], getExecutingFilePa
const scriptPath = combinePaths(packageRoot, browser);
try {
- const { default: module } = await import(scriptPath);
+ const { default: module } = await import(/* webpackIgnore: true */scriptPath);
return { module, error: undefined };
}
catch (e) {
From 8a2f514b6dbca6ae1ca3d8926ea93b70f047b8d2 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Fri, 11 Nov 2022 12:46:33 -0800
Subject: [PATCH 8/9] Ignore warnings from dynamic import in tsserver
---
.../extension-browser.webpack.config.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/extensions/typescript-language-features/extension-browser.webpack.config.js b/extensions/typescript-language-features/extension-browser.webpack.config.js
index 298254f8d0e66..9cbf690372538 100644
--- a/extensions/typescript-language-features/extension-browser.webpack.config.js
+++ b/extensions/typescript-language-features/extension-browser.webpack.config.js
@@ -63,6 +63,7 @@ module.exports = [withBrowserDefaults({
entry: {
'typescript/tsserver.web': './web/webServer.ts'
},
+ ignoreWarnings: [/Critical dependency: the request of a dependency is an expression/],
output: {
// all output goes into `dist`.
// packaging depends on that and this must always be like it
From 356d5a363be099bf67517c015a6cd9e6c68e5e4d Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Fri, 11 Nov 2022 13:04:01 -0800
Subject: [PATCH 9/9] Add to .vscodeignore files
---
extensions/.vscodeignore | 3 +++
extensions/typescript-language-features/.vscodeignore | 1 +
2 files changed, 4 insertions(+)
create mode 100644 extensions/.vscodeignore
diff --git a/extensions/.vscodeignore b/extensions/.vscodeignore
new file mode 100644
index 0000000000000..6e4d8fc1118d3
--- /dev/null
+++ b/extensions/.vscodeignore
@@ -0,0 +1,3 @@
+node_modules/typescript/lib/tsc.js
+node_modules/typescript/lib/typescriptServices.js
+node_modules/typescript/lib/tsserverlibrary.js
diff --git a/extensions/typescript-language-features/.vscodeignore b/extensions/typescript-language-features/.vscodeignore
index 079f06f08d931..76d64ae8237d7 100644
--- a/extensions/typescript-language-features/.vscodeignore
+++ b/extensions/typescript-language-features/.vscodeignore
@@ -1,5 +1,6 @@
build/**
src/**
+web/**
test/**
test-workspace/**
out/**