mirror of
https://github.com/Microsoft/vscode
synced 2024-10-02 17:32:41 +00:00
Reapply building of web server (#166391)
* Reapply building of web server
Revert 8b4642ab23
* Fix critical dep error
This commit is contained in:
parent
ada0a06f37
commit
d0d28a6295
|
@ -64,6 +64,7 @@ const compilations = [
|
||||||
'references-view/tsconfig.json',
|
'references-view/tsconfig.json',
|
||||||
'simple-browser/tsconfig.json',
|
'simple-browser/tsconfig.json',
|
||||||
'typescript-language-features/test-workspace/tsconfig.json',
|
'typescript-language-features/test-workspace/tsconfig.json',
|
||||||
|
'typescript-language-features/web/tsconfig.json',
|
||||||
'typescript-language-features/tsconfig.json',
|
'typescript-language-features/tsconfig.json',
|
||||||
'vscode-api-tests/tsconfig.json',
|
'vscode-api-tests/tsconfig.json',
|
||||||
'vscode-colorize-tests/tsconfig.json',
|
'vscode-colorize-tests/tsconfig.json',
|
||||||
|
|
|
@ -104,7 +104,7 @@ function fromLocalWebpack(extensionPath, webpackConfigFileName) {
|
||||||
// check for a webpack configuration files, then invoke webpack
|
// check for a webpack configuration files, then invoke webpack
|
||||||
// and merge its output with the files stream.
|
// and merge its output with the files stream.
|
||||||
const webpackConfigLocations = glob.sync(path.join(extensionPath, '**', webpackConfigFileName), { ignore: ['**/node_modules'] });
|
const webpackConfigLocations = glob.sync(path.join(extensionPath, '**', webpackConfigFileName), { ignore: ['**/node_modules'] });
|
||||||
const webpackStreams = webpackConfigLocations.map(webpackConfigPath => {
|
const webpackStreams = webpackConfigLocations.flatMap(webpackConfigPath => {
|
||||||
const webpackDone = (err, stats) => {
|
const webpackDone = (err, stats) => {
|
||||||
fancyLog(`Bundled extension: ${ansiColors.yellow(path.join(path.basename(extensionPath), path.relative(extensionPath, webpackConfigPath)))}...`);
|
fancyLog(`Bundled extension: ${ansiColors.yellow(path.join(path.basename(extensionPath), path.relative(extensionPath, webpackConfigPath)))}...`);
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -118,27 +118,30 @@ function fromLocalWebpack(extensionPath, webpackConfigFileName) {
|
||||||
result.emit('error', compilation.warnings.join('\n'));
|
result.emit('error', compilation.warnings.join('\n'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const webpackConfig = {
|
const exportedConfig = require(webpackConfigPath);
|
||||||
...require(webpackConfigPath),
|
return (Array.isArray(exportedConfig) ? exportedConfig : [exportedConfig]).map(config => {
|
||||||
...{ mode: 'production' }
|
const webpackConfig = {
|
||||||
};
|
...config,
|
||||||
const relativeOutputPath = path.relative(extensionPath, webpackConfig.output.path);
|
...{ mode: 'production' }
|
||||||
return webpackGulp(webpackConfig, webpack, webpackDone)
|
};
|
||||||
.pipe(es.through(function (data) {
|
const relativeOutputPath = path.relative(extensionPath, webpackConfig.output.path);
|
||||||
data.stat = data.stat || {};
|
return webpackGulp(webpackConfig, webpack, webpackDone)
|
||||||
data.base = extensionPath;
|
.pipe(es.through(function (data) {
|
||||||
this.emit('data', data);
|
data.stat = data.stat || {};
|
||||||
}))
|
data.base = extensionPath;
|
||||||
.pipe(es.through(function (data) {
|
this.emit('data', data);
|
||||||
// source map handling:
|
}))
|
||||||
// * rewrite sourceMappingURL
|
.pipe(es.through(function (data) {
|
||||||
// * save to disk so that upload-task picks this up
|
// source map handling:
|
||||||
const contents = data.contents.toString('utf8');
|
// * rewrite sourceMappingURL
|
||||||
data.contents = Buffer.from(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, function (_m, g1) {
|
// * save to disk so that upload-task picks this up
|
||||||
return `\n//# sourceMappingURL=${sourceMappingURLBase}/extensions/${path.basename(extensionPath)}/${relativeOutputPath}/${g1}`;
|
const contents = data.contents.toString('utf8');
|
||||||
}), 'utf8');
|
data.contents = Buffer.from(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, function (_m, g1) {
|
||||||
this.emit('data', data);
|
return `\n//# sourceMappingURL=${sourceMappingURLBase}/extensions/${path.basename(extensionPath)}/${relativeOutputPath}/${g1}`;
|
||||||
}));
|
}), 'utf8');
|
||||||
|
this.emit('data', data);
|
||||||
|
}));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
es.merge(...webpackStreams, es.readArray(files))
|
es.merge(...webpackStreams, es.readArray(files))
|
||||||
// .pipe(es.through(function (data) {
|
// .pipe(es.through(function (data) {
|
||||||
|
@ -414,19 +417,14 @@ async function webpackExtensions(taskName, isWatch, webpackConfigLocations) {
|
||||||
const webpackConfigs = [];
|
const webpackConfigs = [];
|
||||||
for (const { configPath, outputRoot } of webpackConfigLocations) {
|
for (const { configPath, outputRoot } of webpackConfigLocations) {
|
||||||
const configOrFnOrArray = require(configPath);
|
const configOrFnOrArray = require(configPath);
|
||||||
function addConfig(configOrFn) {
|
function addConfig(configOrFnOrArray) {
|
||||||
let config;
|
for (const configOrFn of Array.isArray(configOrFnOrArray) ? configOrFnOrArray : [configOrFnOrArray]) {
|
||||||
if (typeof configOrFn === 'function') {
|
const config = typeof configOrFn === 'function' ? configOrFn({}, {}) : configOrFn;
|
||||||
config = configOrFn({}, {});
|
if (outputRoot) {
|
||||||
|
config.output.path = path.join(outputRoot, path.relative(path.dirname(configPath), config.output.path));
|
||||||
|
}
|
||||||
webpackConfigs.push(config);
|
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);
|
addConfig(configOrFnOrArray);
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,7 +121,7 @@ function fromLocalWebpack(extensionPath: string, webpackConfigFileName: string):
|
||||||
{ ignore: ['**/node_modules'] }
|
{ ignore: ['**/node_modules'] }
|
||||||
));
|
));
|
||||||
|
|
||||||
const webpackStreams = webpackConfigLocations.map(webpackConfigPath => {
|
const webpackStreams = webpackConfigLocations.flatMap(webpackConfigPath => {
|
||||||
|
|
||||||
const webpackDone = (err: any, stats: any) => {
|
const webpackDone = (err: any, stats: any) => {
|
||||||
fancyLog(`Bundled extension: ${ansiColors.yellow(path.join(path.basename(extensionPath), path.relative(extensionPath, webpackConfigPath)))}...`);
|
fancyLog(`Bundled extension: ${ansiColors.yellow(path.join(path.basename(extensionPath), path.relative(extensionPath, webpackConfigPath)))}...`);
|
||||||
|
@ -137,29 +137,32 @@ function fromLocalWebpack(extensionPath: string, webpackConfigFileName: string):
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const webpackConfig = {
|
const exportedConfig = require(webpackConfigPath);
|
||||||
...require(webpackConfigPath),
|
return (Array.isArray(exportedConfig) ? exportedConfig : [exportedConfig]).map(config => {
|
||||||
...{ mode: 'production' }
|
const webpackConfig = {
|
||||||
};
|
...config,
|
||||||
const relativeOutputPath = path.relative(extensionPath, webpackConfig.output.path);
|
...{ mode: 'production' }
|
||||||
|
};
|
||||||
|
const relativeOutputPath = path.relative(extensionPath, webpackConfig.output.path);
|
||||||
|
|
||||||
return webpackGulp(webpackConfig, webpack, webpackDone)
|
return webpackGulp(webpackConfig, webpack, webpackDone)
|
||||||
.pipe(es.through(function (data) {
|
.pipe(es.through(function (data) {
|
||||||
data.stat = data.stat || {};
|
data.stat = data.stat || {};
|
||||||
data.base = extensionPath;
|
data.base = extensionPath;
|
||||||
this.emit('data', data);
|
this.emit('data', data);
|
||||||
}))
|
}))
|
||||||
.pipe(es.through(function (data: File) {
|
.pipe(es.through(function (data: File) {
|
||||||
// source map handling:
|
// source map handling:
|
||||||
// * rewrite sourceMappingURL
|
// * rewrite sourceMappingURL
|
||||||
// * save to disk so that upload-task picks this up
|
// * save to disk so that upload-task picks this up
|
||||||
const contents = (<Buffer>data.contents).toString('utf8');
|
const contents = (<Buffer>data.contents).toString('utf8');
|
||||||
data.contents = Buffer.from(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, function (_m, g1) {
|
data.contents = Buffer.from(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, function (_m, g1) {
|
||||||
return `\n//# sourceMappingURL=${sourceMappingURLBase}/extensions/${path.basename(extensionPath)}/${relativeOutputPath}/${g1}`;
|
return `\n//# sourceMappingURL=${sourceMappingURLBase}/extensions/${path.basename(extensionPath)}/${relativeOutputPath}/${g1}`;
|
||||||
}), 'utf8');
|
}), 'utf8');
|
||||||
|
|
||||||
this.emit('data', data);
|
this.emit('data', data);
|
||||||
}));
|
}));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
es.merge(...webpackStreams, es.readArray(files))
|
es.merge(...webpackStreams, es.readArray(files))
|
||||||
|
@ -506,20 +509,14 @@ export async function webpackExtensions(taskName: string, isWatch: boolean, webp
|
||||||
|
|
||||||
for (const { configPath, outputRoot } of webpackConfigLocations) {
|
for (const { configPath, outputRoot } of webpackConfigLocations) {
|
||||||
const configOrFnOrArray = require(configPath);
|
const configOrFnOrArray = require(configPath);
|
||||||
function addConfig(configOrFn: webpack.Configuration | Function) {
|
function addConfig(configOrFnOrArray: webpack.Configuration | ((env: unknown, args: unknown) => webpack.Configuration) | webpack.Configuration[]) {
|
||||||
let config;
|
for (const configOrFn of Array.isArray(configOrFnOrArray) ? configOrFnOrArray : [configOrFnOrArray]) {
|
||||||
if (typeof configOrFn === 'function') {
|
const config = typeof configOrFn === 'function' ? configOrFn({}, {}) : configOrFn;
|
||||||
config = (configOrFn as Function)({}, {});
|
if (outputRoot) {
|
||||||
|
config.output!.path = path.join(outputRoot, path.relative(path.dirname(configPath), config.output!.path!));
|
||||||
|
}
|
||||||
webpackConfigs.push(config);
|
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);
|
addConfig(configOrFnOrArray);
|
||||||
}
|
}
|
||||||
|
|
3
extensions/.vscodeignore
Normal file
3
extensions/.vscodeignore
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
node_modules/typescript/lib/tsc.js
|
||||||
|
node_modules/typescript/lib/typescriptServices.js
|
||||||
|
node_modules/typescript/lib/tsserverlibrary.js
|
|
@ -26,7 +26,6 @@ function processRoot() {
|
||||||
function processLib() {
|
function processLib() {
|
||||||
const toDelete = new Set([
|
const toDelete = new Set([
|
||||||
'tsc.js',
|
'tsc.js',
|
||||||
'tsserverlibrary.js',
|
|
||||||
'typescriptServices.js',
|
'typescriptServices.js',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
build/**
|
build/**
|
||||||
src/**
|
src/**
|
||||||
|
web/**
|
||||||
test/**
|
test/**
|
||||||
test-workspace/**
|
test-workspace/**
|
||||||
out/**
|
out/**
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
const CopyPlugin = require('copy-webpack-plugin');
|
const CopyPlugin = require('copy-webpack-plugin');
|
||||||
const Terser = require('terser');
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const defaultConfig = require('../shared.webpack.config');
|
const defaultConfig = require('../shared.webpack.config');
|
||||||
|
@ -30,8 +28,7 @@ const languages = [
|
||||||
'tr',
|
'tr',
|
||||||
'zh-cn',
|
'zh-cn',
|
||||||
];
|
];
|
||||||
|
module.exports = [withBrowserDefaults({
|
||||||
module.exports = withBrowserDefaults({
|
|
||||||
context: __dirname,
|
context: __dirname,
|
||||||
entry: {
|
entry: {
|
||||||
extension: './src/extension.browser.ts',
|
extension: './src/extension.browser.ts',
|
||||||
|
@ -60,30 +57,24 @@ module.exports = withBrowserDefaults({
|
||||||
}))
|
}))
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
// @ts-ignore
|
|
||||||
new CopyPlugin({
|
|
||||||
patterns: [
|
|
||||||
{
|
|
||||||
from: '../node_modules/typescript/lib/tsserver.js',
|
|
||||||
to: 'typescript/tsserver.web.js',
|
|
||||||
transform: async (content) => {
|
|
||||||
const dynamicImportCompatPath = path.join(__dirname, '..', 'node_modules', 'typescript', 'lib', 'dynamicImportCompat.js');
|
|
||||||
const prefix = fs.existsSync(dynamicImportCompatPath) ? fs.readFileSync(dynamicImportCompatPath) : undefined;
|
|
||||||
const output = await Terser.minify(content.toString());
|
|
||||||
if (!output.code) {
|
|
||||||
throw new Error('Terser returned undefined code');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prefix) {
|
|
||||||
return prefix.toString() + '\n' + output.code;
|
|
||||||
}
|
|
||||||
return output.code;
|
|
||||||
},
|
|
||||||
transformPath: (targetPath) => {
|
|
||||||
return targetPath.replace('tsserver.js', 'tsserver.web.js');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
],
|
],
|
||||||
});
|
}), withBrowserDefaults({
|
||||||
|
context: __dirname,
|
||||||
|
entry: {
|
||||||
|
'typescript/tsserver.web': './web/webServer.ts'
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
exprContextCritical: false,
|
||||||
|
},
|
||||||
|
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
|
||||||
|
filename: '[name].js',
|
||||||
|
path: path.join(__dirname, 'dist', 'browser'),
|
||||||
|
libraryTarget: undefined,
|
||||||
|
},
|
||||||
|
externals: {
|
||||||
|
'perf_hooks': 'commonjs perf_hooks',
|
||||||
|
}
|
||||||
|
})];
|
||||||
|
|
15
extensions/typescript-language-features/web/tsconfig.json
Normal file
15
extensions/typescript-language-features/web/tsconfig.json
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../out",
|
||||||
|
"module": "nodenext",
|
||||||
|
"moduleDetection": "legacy",
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"types": [
|
||||||
|
"node"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"webServer.ts"
|
||||||
|
]
|
||||||
|
}
|
528
extensions/typescript-language-features/web/webServer.ts
Normal file
528
extensions/typescript-language-features/web/webServer.ts
Normal file
|
@ -0,0 +1,528 @@
|
||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
/// <reference lib='webworker.importscripts' />
|
||||||
|
/// <reference lib='dom' />
|
||||||
|
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;
|
||||||
|
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: <T>(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;
|
||||||
|
const noopFileWatcher: ts.FileWatcher = { close: noop };
|
||||||
|
const returnNoopFileWatcher = () => noopFileWatcher;
|
||||||
|
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;
|
||||||
|
const identity: <T>(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function serverCreateWebSystem(host: WebHost, args: string[], getExecutingFilePath: () => string):
|
||||||
|
ts.server.ServerHost & {
|
||||||
|
importPlugin?(root: string, moduleName: string): Promise<ts.server.ModuleImportResult>;
|
||||||
|
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;
|
||||||
|
|
||||||
|
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<ts.server.ModuleImportResult> => {
|
||||||
|
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 import(/* webpackIgnore: true */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(
|
||||||
|
ts.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: ${ts.sys.useCaseSensitiveFileNames}`);
|
||||||
|
logger.info(`ServerMode: ${serverMode} syntaxOnly: ${syntaxOnly} hasUnknownServerMode: ${unknownServerMode}`);
|
||||||
|
|
||||||
|
setStackTraceLimit();
|
||||||
|
|
||||||
|
if ((ts as any).Debug.isDebugging) {
|
||||||
|
(ts as any).Debug.enableDebugInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
// 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
|
Loading…
Reference in a new issue