mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Add tsserver entrypoint for vfs
This commit is contained in:
@@ -757,8 +757,7 @@ namespace ts.server {
|
||||
readonly currentDirectory: NormalizedPath;
|
||||
readonly toCanonicalFileName: (f: string) => string;
|
||||
|
||||
public host: ServerHost;
|
||||
public fs: ts.TestFSWithWatch.VirtualServerHost | undefined;
|
||||
public readonly host: ServerHost;
|
||||
public readonly logger: Logger;
|
||||
public readonly cancellationToken: HostCancellationToken;
|
||||
public readonly useSingleInferredProject: boolean;
|
||||
@@ -3693,24 +3692,18 @@ namespace ts.server {
|
||||
/* @internal */
|
||||
updateFileSystem(createdFiles: Iterator<protocol.FileSystemRequestArgs> | undefined, updatedFiles?: Iterator<protocol.FileSystemRequestArgs>, deletedFiles?: string[]) {
|
||||
// TODO: Maybe it is somehow gauche or verboten to use protocol types but the translation in applyChangesInOpenFiles seems stupid
|
||||
// 1. set some internal tsserver state for mocked FS (if it hasn't already been set, this might not be the first message)
|
||||
if (!this.fs) {
|
||||
// -nervous laugh-
|
||||
this.fs = ts.TestFSWithWatch.createVirtualServerHost([], { withSafeList: false })
|
||||
;(this as any).host = this.fs
|
||||
;(this.session as any).host = this.fs
|
||||
}
|
||||
// 2. update vfs
|
||||
// I THINK that only vfs needs to update, because none of these files should be open.
|
||||
// (I guess files could update from the filesystem while they are still open, but that's something to solve at the end of prototyping I think)
|
||||
const fs = this.host as ts.TestFSWithWatch.VirtualServerHost;
|
||||
if (createdFiles) {
|
||||
let it
|
||||
while (!(it = createdFiles.next()).done) {
|
||||
const document = it.value
|
||||
if (document.fileContent) {
|
||||
if (!this.fs.directoryExists(ts.getDirectoryPath(document.file)))
|
||||
this.fs.createDirectory(ts.getDirectoryPath(document.file), /*recursive*/ true);
|
||||
this.fs.writeFile(document.file, document.fileContent);
|
||||
if (!fs.directoryExists(ts.getDirectoryPath(document.file)))
|
||||
fs.createDirectory(ts.getDirectoryPath(document.file), /*recursive*/ true);
|
||||
fs.writeFile(document.file, document.fileContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3719,12 +3712,12 @@ namespace ts.server {
|
||||
let it
|
||||
while (!(it = updatedFiles.next()).done) {
|
||||
if (it.value.fileContent) {
|
||||
if (this.fs.fileExists(it.value.file)) {
|
||||
this.fs.modifyFile(it.value.file, it.value.fileContent)
|
||||
if (fs.fileExists(it.value.file)) {
|
||||
fs.modifyFile(it.value.file, it.value.fileContent)
|
||||
}
|
||||
else {
|
||||
this.fs.createDirectory(ts.getDirectoryPath(it.value.file), /*recursive*/ true);
|
||||
this.fs.writeFile(it.value.file, it.value.fileContent);
|
||||
fs.createDirectory(ts.getDirectoryPath(it.value.file), /*recursive*/ true);
|
||||
fs.writeFile(it.value.file, it.value.fileContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3732,7 +3725,7 @@ namespace ts.server {
|
||||
if (deletedFiles) {
|
||||
// TODO: Probably want to delete empty parent folders while they are empty too
|
||||
for (const file of deletedFiles) {
|
||||
this.fs.deleteFile(file)
|
||||
fs.deleteFile(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ ${'content' in file ? file.content : file.fileContent}`;
|
||||
// 1. make sure that everything in files is there
|
||||
assert.isDefined(host)
|
||||
const fs = (host as any).fs as ESMap<string, ts.TestFSWithWatch.FSEntry>
|
||||
console.log(Array.from(fs.values() as any as Iterable<ts.TestFSWithWatch.FSEntry>).filter(ts.TestFSWithWatch.isFsFile))
|
||||
// console.log(Array.from(fs.values() as any as Iterable<ts.TestFSWithWatch.FSEntry>).filter(ts.TestFSWithWatch.isFsFile))
|
||||
assert.equal(Array.from(fs.values() as any as Iterable<ts.TestFSWithWatch.FSEntry>).filter(ts.TestFSWithWatch.isFsFile).length, files.length)
|
||||
for (const { file, fileContent } of files) {
|
||||
assert(host?.fileExists(file))
|
||||
@@ -54,11 +54,8 @@ ${'content' in file ? file.content : file.fileContent}`;
|
||||
// 2. make sure nothing else is
|
||||
}
|
||||
it("with updateFileSystem request", () => {
|
||||
// TODO: Create a virtual host and make sure it works with session etc
|
||||
// TODO: File watchers still seem wrong
|
||||
// 5. send updateFS request with a create/update/delete of a watched file, assert that file watchers fired
|
||||
// 6. probably some other watcher tests, not sure what
|
||||
const host = createServerHost([]); // old path goes into virtualFileSystemWithWatch.ts, so I guess it's getting the old host, not the replaced one
|
||||
// TODO: probably some other watcher tests, not sure what
|
||||
const host = ts.TestFSWithWatch.createVirtualServerHost([], { withSafeList: false });
|
||||
const session = createSession(host);
|
||||
const created = [app, file1, file2, file3, config, lib]
|
||||
session.executeCommandSeq<protocol.UpdateFileSystemRequest>({
|
||||
@@ -79,8 +76,8 @@ ${'content' in file ? file.content : file.fileContent}`;
|
||||
const fakehost = (session as any).host as ts.TestFSWithWatch.VirtualServerHost
|
||||
assert.isDefined(project);
|
||||
assert.equal(fakehost.fsWatches.size, 0)
|
||||
assert.equal(fakehost.fsWatchesRecursive.size, 0)
|
||||
assert.equal(fakehost.watchedFiles.size, 0)
|
||||
assert.equal(fakehost.fsWatchesRecursive.size, 2)
|
||||
assert.equal(fakehost.watchedFiles.size, 5)
|
||||
verifyProjectVersion(project, 1);
|
||||
session.executeCommandSeq<protocol.OpenRequest>({
|
||||
command: protocol.CommandTypes.Open,
|
||||
@@ -92,14 +89,14 @@ ${'content' in file ? file.content : file.fileContent}`;
|
||||
verifyProjectVersion(project, 2);
|
||||
|
||||
// Verify Texts
|
||||
verifyFileSystem(service.fs, created)
|
||||
verifyFileSystem(host, created)
|
||||
verifyText(service, file1.file, file1.fileContent!);
|
||||
verifyText(service, commonFile2.path, commonFile2.content);
|
||||
verifyText(service, app.file, app.fileContent!);
|
||||
verifyText(service, file3.file, fileContentWithComment(file3));
|
||||
assert.equal(fakehost.fsWatches.size, 0)
|
||||
assert.equal(fakehost.fsWatchesRecursive.size, 0)
|
||||
assert.equal(fakehost.watchedFiles.size, 0)
|
||||
assert.equal(fakehost.fsWatchesRecursive.size, 2)
|
||||
assert.equal(fakehost.watchedFiles.size, 4)
|
||||
|
||||
session.executeCommandSeq<protocol.UpdateFileSystemRequest>({
|
||||
command: protocol.CommandTypes.UpdateFileSystem,
|
||||
@@ -114,14 +111,14 @@ ${'content' in file ? file.content : file.fileContent}`;
|
||||
verifyProjectVersion(project, 2);
|
||||
|
||||
// Verify Texts
|
||||
verifyFileSystem(service.fs, created)
|
||||
verifyFileSystem(host, created)
|
||||
verifyText(service, file1.file, file1.fileContent!);
|
||||
verifyText(service, commonFile2.path, commonFile2.content);
|
||||
verifyText(service, app.file, app.fileContent!);
|
||||
verifyText(service, file3.file, fileContentWithComment(file3));
|
||||
assert.equal(fakehost.fsWatches.size, 0)
|
||||
assert.equal(fakehost.fsWatchesRecursive.size, 0)
|
||||
assert.equal(fakehost.watchedFiles.size, 0)
|
||||
assert.equal(fakehost.fsWatchesRecursive.size, 2)
|
||||
assert.equal(fakehost.watchedFiles.size, 4)
|
||||
|
||||
session.executeCommandSeq<protocol.UpdateFileSystemRequest>({
|
||||
command: protocol.CommandTypes.UpdateFileSystem,
|
||||
@@ -132,18 +129,16 @@ ${'content' in file ? file.content : file.fileContent}`;
|
||||
updated: [], //FileSystemRequestArgs[];
|
||||
}
|
||||
});
|
||||
// also no change when deleting a file (changes session version but not project version?)
|
||||
verifyProjectVersion(project, 2);
|
||||
verifyProjectVersion(project, 3);
|
||||
|
||||
// Verify Texts
|
||||
verifyFileSystem(service.fs, [app, file2, file3, config, lib])
|
||||
verifyText(service, file1.file, file1.fileContent!);
|
||||
verifyFileSystem(host, [app, file2, file3, config, lib])
|
||||
verifyText(service, commonFile2.path, commonFile2.content);
|
||||
verifyText(service, app.file, app.fileContent!);
|
||||
verifyText(service, file3.file, fileContentWithComment(file3));
|
||||
assert.equal(fakehost.fsWatches.size, 0)
|
||||
assert.equal(fakehost.fsWatchesRecursive.size, 0)
|
||||
assert.equal(fakehost.watchedFiles.size, 0)
|
||||
assert.equal(fakehost.fsWatchesRecursive.size, 2)
|
||||
assert.equal(fakehost.watchedFiles.size, 3)
|
||||
|
||||
session.executeCommandSeq<protocol.CloseRequest>({
|
||||
command: protocol.CommandTypes.Close,
|
||||
@@ -151,17 +146,16 @@ ${'content' in file ? file.content : file.fileContent}`;
|
||||
});
|
||||
|
||||
// also no change when closing a file??? (changes session version but not project version?)
|
||||
verifyProjectVersion(project, 2);
|
||||
verifyProjectVersion(project, 3);
|
||||
|
||||
// Verify Texts
|
||||
verifyFileSystem(service.fs, [app, file2, file3, config, lib])
|
||||
verifyText(service, file1.file, file1.fileContent!);
|
||||
verifyFileSystem(host, [app, file2, file3, config, lib])
|
||||
verifyText(service, commonFile2.path, commonFile2.content);
|
||||
verifyText(service, app.file, app.fileContent!);
|
||||
verifyText(service, file3.file, fileContentWithComment(file3));
|
||||
assert.equal(fakehost.fsWatches.size, 0)
|
||||
assert.equal(fakehost.fsWatchesRecursive.size, 0)
|
||||
assert.equal(fakehost.watchedFiles.size, 0)
|
||||
assert.equal(fakehost.fsWatchesRecursive.size, 2)
|
||||
assert.equal(fakehost.watchedFiles.size, 4)
|
||||
});
|
||||
|
||||
function verifyText(service: server.ProjectService, file: string, expected: string) {
|
||||
|
||||
@@ -79,6 +79,16 @@ namespace ts.server {
|
||||
if (typeof process !== "undefined") {
|
||||
start(initializeNodeSystem(), require("os").platform());
|
||||
}
|
||||
// TODO: Learn how to pass arguments to server
|
||||
else if (findArgument("vfs")) {
|
||||
// Get args from first message
|
||||
const listener = (e: any) => {
|
||||
removeEventListener("message", listener);
|
||||
const args = e.data;
|
||||
start(initializeVirtualFileSystem(args), "vfs");
|
||||
};
|
||||
addEventListener("message", listener);
|
||||
}
|
||||
else {
|
||||
// Get args from first message
|
||||
const listener = (e: any) => {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
{ "path": "../jsTyping", "prepend": true },
|
||||
{ "path": "../server", "prepend": true },
|
||||
{ "path": "../webServer", "prepend": true },
|
||||
{ "path": "../vfs", "prepend": true },
|
||||
{ "path": "../deprecatedCompat", "prepend": true }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -46,6 +46,18 @@ namespace ts.server {
|
||||
};
|
||||
}
|
||||
|
||||
export function initializeVirtualFileSystem(args: string[]): StartInput {
|
||||
createVirtualFileSystem();
|
||||
return {
|
||||
args,
|
||||
logger: createLogger(),
|
||||
cancellationToken: nullCancellationToken,
|
||||
// Only semantic mode right now
|
||||
serverMode: LanguageServiceMode.Semantic,
|
||||
startSession: startVirtualFileSystemSession
|
||||
};
|
||||
}
|
||||
|
||||
function createLogger() {
|
||||
const cmdLineVerbosity = getLogLevel(findArgument("--logVerbosity"));
|
||||
return cmdLineVerbosity !== undefined ? new MainProcessLogger(cmdLineVerbosity, { writeMessage }) : nullLogger;
|
||||
@@ -81,6 +93,19 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function createVirtualFileSystem() {
|
||||
Debug.assert(ts.sys === undefined);
|
||||
// TODO: I don't think I need the XMLHttpRequest code since vfs will get its files from messages sent by the owner
|
||||
// ...but this means I may not need the webSession-copied code in startVirtualFileSystemSession
|
||||
const vfshost = ts.TestFSWithWatch.createVirtualServerHost([])
|
||||
setSys(vfshost);
|
||||
const localeStr = findArgument("--locale");
|
||||
if (localeStr) {
|
||||
validateLocaleAndSetLanguage(localeStr, sys);
|
||||
}
|
||||
}
|
||||
|
||||
function hrtime(previous?: [number, number]) {
|
||||
const now = self.performance.now() * 1e-3;
|
||||
let seconds = Math.floor(now);
|
||||
@@ -120,4 +145,28 @@ namespace ts.server {
|
||||
// Start listening
|
||||
session.listen();
|
||||
}
|
||||
|
||||
function startVirtualFileSystemSession(options: StartSessionOptions, logger: Logger, cancellationToken: ServerCancellationToken) {
|
||||
class WorkerSession extends server.WorkerSession {
|
||||
constructor() {
|
||||
super(sys as ServerHost, { writeMessage }, options, logger, cancellationToken, hrtime);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -10496,7 +10496,7 @@ declare namespace ts.server {
|
||||
private ensureProjectForOpenFiles;
|
||||
/**
|
||||
* Open file whose contents is managed by the client
|
||||
* @param filename is absolute pathname
|
||||
* @param fileName is absolute pathname
|
||||
* @param fileContent is a known version of the file content that is more up to date than the one on disk
|
||||
*/
|
||||
openClientFile(fileName: string, fileContent?: string, scriptKind?: ScriptKind, projectRootPath?: string): OpenConfiguredProjectResult;
|
||||
@@ -10512,7 +10512,7 @@ declare namespace ts.server {
|
||||
private telemetryOnOpenFile;
|
||||
/**
|
||||
* Close file whose contents is managed by the client
|
||||
* @param filename is absolute pathname
|
||||
* @param uncheckedFileName is absolute pathname
|
||||
*/
|
||||
closeClientFile(uncheckedFileName: string): void;
|
||||
private collectChanges;
|
||||
|
||||
@@ -168,13 +168,13 @@ tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts(
|
||||
async function * explicitReturnType10(): IterableIterator<number> {
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
!!! error TS2741: Property '[Symbol.iterator]' is missing in type 'AsyncGenerator<number, any, undefined>' but required in type 'IterableIterator<number>'.
|
||||
!!! related TS2728 /.ts/lib.es2015.iterable.d.ts:55:5: '[Symbol.iterator]' is declared here.
|
||||
!!! related TS2728 /.ts/lib.es2015.iterable.d.ts:90:5: '[Symbol.iterator]' is declared here.
|
||||
yield 1;
|
||||
}
|
||||
async function * explicitReturnType11(): Iterable<number> {
|
||||
~~~~~~~~~~~~~~~~
|
||||
!!! error TS2741: Property '[Symbol.iterator]' is missing in type 'AsyncGenerator<any, any, any>' but required in type 'Iterable<number>'.
|
||||
!!! related TS2728 /.ts/lib.es2015.iterable.d.ts:51:5: '[Symbol.iterator]' is declared here.
|
||||
!!! related TS2728 /.ts/lib.es2015.iterable.d.ts:82:5: '[Symbol.iterator]' is declared here.
|
||||
yield 1;
|
||||
}
|
||||
async function * explicitReturnType12(): Iterator<number> {
|
||||
|
||||
Reference in New Issue
Block a user