Files
azuredatastudio/extensions/json-language-features/client/src/node/jsonClientMain.ts
Karl Burtram 8a3d08f0de Merge vscode 1.67 (#20883)
* Fix initial build breaks from 1.67 merge (#2514)

* Update yarn lock files

* Update build scripts

* Fix tsconfig

* Build breaks

* WIP

* Update yarn lock files

* Misc breaks

* Updates to package.json

* Breaks

* Update yarn

* Fix breaks

* Breaks

* Build breaks

* Breaks

* Breaks

* Breaks

* Breaks

* Breaks

* Missing file

* Breaks

* Breaks

* Breaks

* Breaks

* Breaks

* Fix several runtime breaks (#2515)

* Missing files

* Runtime breaks

* Fix proxy ordering issue

* Remove commented code

* Fix breaks with opening query editor

* Fix post merge break

* Updates related to setup build and other breaks (#2516)

* Fix bundle build issues

* Update distro

* Fix distro merge and update build JS files

* Disable pipeline steps

* Remove stats call

* Update license name

* Make new RPM dependencies a warning

* Fix extension manager version checks

* Update JS file

* Fix a few runtime breaks

* Fixes

* Fix runtime issues

* Fix build breaks

* Update notebook tests (part 1)

* Fix broken tests

* Linting errors

* Fix hygiene

* Disable lint rules

* Bump distro

* Turn off smoke tests

* Disable integration tests

* Remove failing "activate" test

* Remove failed test assertion

* Disable other broken test

* Disable query history tests

* Disable extension unit tests

* Disable failing tasks
2022-10-19 19:13:18 -07:00

188 lines
6.5 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ExtensionContext, OutputChannel, window, workspace } from 'vscode';
import { startClient, LanguageClientConstructor, SchemaRequestService, languageServerDescription } from '../jsonClient';
import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient } from 'vscode-languageclient/node';
import { promises as fs } from 'fs';
import * as path from 'path';
import { xhr, XHRResponse, getErrorStatusDescription, Headers } from 'request-light';
import TelemetryReporter from '@vscode/extension-telemetry';
import { JSONSchemaCache } from './schemaCache';
let telemetry: TelemetryReporter | undefined;
// this method is called when vs code is activated
export async function activate(context: ExtensionContext) {
const clientPackageJSON = await getPackageInfo(context);
telemetry = new TelemetryReporter(clientPackageJSON.name, clientPackageJSON.version, clientPackageJSON.aiKey);
const outputChannel = window.createOutputChannel(languageServerDescription);
const serverMain = `./server/${clientPackageJSON.main.indexOf('/dist/') !== -1 ? 'dist' : 'out'}/node/jsonServerMain`;
const serverModule = context.asAbsolutePath(serverMain);
// The debug options for the server
const debugOptions = { execArgv: ['--nolazy', '--inspect=' + (6000 + Math.round(Math.random() * 999))] };
// If the extension is launch in debug mode the debug server options are use
// Otherwise the run options are used
const serverOptions: ServerOptions = {
run: { module: serverModule, transport: TransportKind.ipc },
debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions }
};
const newLanguageClient: LanguageClientConstructor = (id: string, name: string, clientOptions: LanguageClientOptions) => {
clientOptions.outputChannel = outputChannel;
return new LanguageClient(id, name, serverOptions, clientOptions);
};
const log = getLog(outputChannel);
context.subscriptions.push(log);
const schemaRequests = await getSchemaRequestService(context, log);
startClient(context, newLanguageClient, { schemaRequests, telemetry });
}
export function deactivate(): Promise<any> {
return telemetry ? telemetry.dispose() : Promise.resolve(null);
}
interface IPackageInfo {
name: string;
version: string;
aiKey: string;
main: string;
}
async function getPackageInfo(context: ExtensionContext): Promise<IPackageInfo> {
const location = context.asAbsolutePath('./package.json');
try {
return JSON.parse((await fs.readFile(location)).toString());
} catch (e) {
console.log(`Problems reading ${location}: ${e}`);
return { name: '', version: '', aiKey: '', main: '' };
}
}
interface Log {
trace(message: string): void;
isTrace(): boolean;
dispose(): void;
}
const traceSetting = 'json.trace.server';
function getLog(outputChannel: OutputChannel): Log {
let trace = workspace.getConfiguration().get(traceSetting) === 'verbose';
const configListener = workspace.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(traceSetting)) {
trace = workspace.getConfiguration().get(traceSetting) === 'verbose';
}
});
return {
trace(message: string) {
if (trace) {
outputChannel.appendLine(message);
}
},
isTrace() {
return trace;
},
dispose: () => configListener.dispose()
};
}
const retryTimeoutInHours = 2 * 24; // 2 days
async function getSchemaRequestService(context: ExtensionContext, log: Log): Promise<SchemaRequestService> {
let cache: JSONSchemaCache | undefined = undefined;
const globalStorage = context.globalStorageUri;
let clearCache: (() => Promise<string[]>) | undefined;
if (globalStorage.scheme === 'file') {
const schemaCacheLocation = path.join(globalStorage.fsPath, 'json-schema-cache');
await fs.mkdir(schemaCacheLocation, { recursive: true });
const schemaCache = new JSONSchemaCache(schemaCacheLocation, context.globalState);
log.trace(`[json schema cache] initial state: ${JSON.stringify(schemaCache.getCacheInfo(), null, ' ')}`);
cache = schemaCache;
clearCache = async () => {
const cachedSchemas = await schemaCache.clearCache();
log.trace(`[json schema cache] cache cleared. Previously cached schemas: ${cachedSchemas.join(', ')}`);
return cachedSchemas;
};
}
const isXHRResponse = (error: any): error is XHRResponse => typeof error?.status === 'number';
const request = async (uri: string, etag?: string): Promise<string> => {
const headers: Headers = { 'Accept-Encoding': 'gzip, deflate' };
if (etag) {
headers['If-None-Match'] = etag;
}
try {
log.trace(`[json schema cache] Requesting schema ${uri} etag ${etag}...`);
const response = await xhr({ url: uri, followRedirects: 5, headers });
if (cache) {
const etag = response.headers['etag'];
if (typeof etag === 'string') {
log.trace(`[json schema cache] Storing schema ${uri} etag ${etag} in cache`);
await cache.putSchema(uri, etag, response.responseText);
} else {
log.trace(`[json schema cache] Response: schema ${uri} no etag`);
}
}
return response.responseText;
} catch (error: unknown) {
if (isXHRResponse(error)) {
if (error.status === 304 && etag && cache) {
log.trace(`[json schema cache] Response: schema ${uri} unchanged etag ${etag}`);
const content = await cache.getSchema(uri, etag, true);
if (content) {
log.trace(`[json schema cache] Get schema ${uri} etag ${etag} from cache`);
return content;
}
return request(uri);
}
let status = getErrorStatusDescription(error.status);
if (status && error.responseText) {
status = `${status}\n${error.responseText.substring(0, 200)}`;
}
if (!status) {
status = error.toString();
}
log.trace(`[json schema cache] Respond schema ${uri} error ${status}`);
throw status;
}
throw error;
}
};
return {
getContent: async (uri: string) => {
if (cache && /^https?:\/\/json\.schemastore\.org\//.test(uri)) {
const content = await cache.getSchemaIfUpdatedSince(uri, retryTimeoutInHours);
if (content) {
if (log.isTrace()) {
log.trace(`[json schema cache] Schema ${uri} from cache without request (last accessed ${cache.getLastUpdatedInHours(uri)} hours ago)`);
}
return content;
}
}
return request(uri, cache?.getETag(uri));
},
clearCache
};
}