mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-14 03:58:33 -05:00
Merge from vscode bead496a613e475819f89f08e9e882b841bc1fe8 (#14883)
* Merge from vscode bead496a613e475819f89f08e9e882b841bc1fe8 * Bump distro * Upgrade GCC to 4.9 due to yarn install errors * Update build image * Fix bootstrap base url * Bump distro * Fix build errors * Update source map file * Disable checkbox for blocking migration issues (#15131) * disable checkbox for blocking issues * wip * disable checkbox fixes * fix strings * Remove duplicate tsec command * Default to off for tab color if settings not present * re-skip failing tests * Fix mocha error * Bump sqlite version & fix notebooks search view * Turn off esbuild warnings * Update esbuild log level * Fix overflowactionbar tests * Fix ts-ignore in dropdown tests * cleanup/fixes * Fix hygiene * Bundle in entire zone.js module * Remove extra constructor param * bump distro for web compile break * bump distro for web compile break v2 * Undo log level change * New distro * Fix integration test scripts * remove the "no yarn.lock changes" workflow * fix scripts v2 * Update unit test scripts * Ensure ads-kerberos2 updates in .vscodeignore * Try fix unit tests * Upload crash reports * remove nogpu * always upload crashes * Use bash script * Consolidate data/ext dir names * Create in tmp directory Co-authored-by: chlafreniere <hichise@gmail.com> Co-authored-by: Christopher Suh <chsuh@microsoft.com> Co-authored-by: chgagnon <chgagnon@microsoft.com>
This commit is contained in:
@@ -3,14 +3,14 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { mkdir, open, close, read, write, fdatasync, Dirent, Stats } from 'fs';
|
||||
import { open, close, read, write, fdatasync, Stats, promises } from 'fs';
|
||||
import { promisify } from 'util';
|
||||
import { IDisposable, Disposable, toDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle';
|
||||
import { FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, FileType, FileDeleteOptions, FileOverwriteOptions, FileWriteOptions, FileOpenOptions, FileSystemProviderErrorCode, createFileSystemProviderError, FileSystemProviderError, IFileSystemProviderWithFileReadWriteCapability, IFileSystemProviderWithFileReadStreamCapability, IFileSystemProviderWithOpenReadWriteCloseCapability, FileReadStreamOptions, IFileSystemProviderWithFileFolderCopyCapability } from 'vs/platform/files/common/files';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { isLinux, isWindows } from 'vs/base/common/platform';
|
||||
import { statLink, unlink, move, copy, readFile, truncate, rimraf, RimRafMode, exists, readdirWithFileTypes } from 'vs/base/node/pfs';
|
||||
import { SymlinkSupport, move, copy, rimraf, RimRafMode, exists, readdir, IDirent } from 'vs/base/node/pfs';
|
||||
import { normalize, basename, dirname } from 'vs/base/common/path';
|
||||
import { joinPath } from 'vs/base/common/resources';
|
||||
import { isEqual } from 'vs/base/common/extpath';
|
||||
@@ -80,7 +80,7 @@ export class DiskFileSystemProvider extends Disposable implements
|
||||
|
||||
async stat(resource: URI): Promise<IStat> {
|
||||
try {
|
||||
const { stat, symbolicLink } = await statLink(this.toFilePath(resource)); // cannot use fs.stat() here to support links properly
|
||||
const { stat, symbolicLink } = await SymlinkSupport.stat(this.toFilePath(resource)); // cannot use fs.stat() here to support links properly
|
||||
|
||||
return {
|
||||
type: this.toType(stat, symbolicLink),
|
||||
@@ -95,7 +95,7 @@ export class DiskFileSystemProvider extends Disposable implements
|
||||
|
||||
async readdir(resource: URI): Promise<[string, FileType][]> {
|
||||
try {
|
||||
const children = await readdirWithFileTypes(this.toFilePath(resource));
|
||||
const children = await readdir(this.toFilePath(resource), { withFileTypes: true });
|
||||
|
||||
const result: [string, FileType][] = [];
|
||||
await Promise.all(children.map(async child => {
|
||||
@@ -119,7 +119,7 @@ export class DiskFileSystemProvider extends Disposable implements
|
||||
}
|
||||
}
|
||||
|
||||
private toType(entry: Stats | Dirent, symbolicLink?: { dangling: boolean }): FileType {
|
||||
private toType(entry: Stats | IDirent, symbolicLink?: { dangling: boolean }): FileType {
|
||||
|
||||
// Signal file type by checking for file / directory, except:
|
||||
// - symbolic links pointing to non-existing files are FileType.Unknown
|
||||
@@ -151,7 +151,7 @@ export class DiskFileSystemProvider extends Disposable implements
|
||||
try {
|
||||
const filePath = this.toFilePath(resource);
|
||||
|
||||
return await readFile(filePath);
|
||||
return await promises.readFile(filePath);
|
||||
} catch (error) {
|
||||
throw this.toFileSystemProviderError(error);
|
||||
}
|
||||
@@ -212,18 +212,20 @@ export class DiskFileSystemProvider extends Disposable implements
|
||||
|
||||
let flags: string | undefined = undefined;
|
||||
if (opts.create) {
|
||||
if (isWindows && await exists(filePath)) {
|
||||
if (isWindows) {
|
||||
try {
|
||||
// On Windows and if the file exists, we use a different strategy of saving the file
|
||||
// by first truncating the file and then writing with r+ flag. This helps to save hidden files on Windows
|
||||
// (see https://github.com/microsoft/vscode/issues/931) and prevent removing alternate data streams
|
||||
// (see https://github.com/microsoft/vscode/issues/6363)
|
||||
await truncate(filePath, 0);
|
||||
await promises.truncate(filePath, 0);
|
||||
|
||||
// After a successful truncate() the flag can be set to 'r+' which will not truncate.
|
||||
flags = 'r+';
|
||||
} catch (error) {
|
||||
this.logService.trace(error);
|
||||
if (error.code !== 'ENOENT') {
|
||||
this.logService.trace(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,7 +400,7 @@ export class DiskFileSystemProvider extends Disposable implements
|
||||
|
||||
async mkdir(resource: URI): Promise<void> {
|
||||
try {
|
||||
await promisify(mkdir)(this.toFilePath(resource));
|
||||
await promises.mkdir(this.toFilePath(resource));
|
||||
} catch (error) {
|
||||
throw this.toFileSystemProviderError(error);
|
||||
}
|
||||
@@ -418,7 +420,7 @@ export class DiskFileSystemProvider extends Disposable implements
|
||||
if (opts.recursive) {
|
||||
await rimraf(filePath, RimRafMode.MOVE);
|
||||
} else {
|
||||
await unlink(filePath);
|
||||
await promises.unlink(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,7 +465,7 @@ export class DiskFileSystemProvider extends Disposable implements
|
||||
await this.validateTargetDeleted(from, to, 'copy', opts.overwrite);
|
||||
|
||||
// Copy
|
||||
await copy(fromFilePath, toFilePath);
|
||||
await copy(fromFilePath, toFilePath, { preserveSymlinks: true });
|
||||
} catch (error) {
|
||||
|
||||
// rewrite some typical errors that can happen especially around symlinks
|
||||
@@ -522,7 +524,7 @@ export class DiskFileSystemProvider extends Disposable implements
|
||||
return this.watchRecursive(resource, opts.excludes);
|
||||
}
|
||||
|
||||
return this.watchNonRecursive(resource); // TODO@ben ideally the same watcher can be used in both cases
|
||||
return this.watchNonRecursive(resource); // TODO@bpasero ideally the same watcher can be used in both cases
|
||||
}
|
||||
|
||||
private watchRecursive(resource: URI, excludes: string[]): IDisposable {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { IDiskFileChange, normalizeFileChanges, ILogMessage } from 'vs/platform/files/node/watcher/watcher';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { statLink } from 'vs/base/node/pfs';
|
||||
import { SymlinkSupport } from 'vs/base/node/pfs';
|
||||
import { realpath } from 'vs/base/node/extpath';
|
||||
import { watchFolder, watchFile, CHANGE_BUFFER_DELAY } from 'vs/base/node/watcher';
|
||||
import { FileChangeType } from 'vs/platform/files/common/files';
|
||||
@@ -35,7 +35,7 @@ export class FileWatcher extends Disposable {
|
||||
|
||||
private async startWatching(): Promise<void> {
|
||||
try {
|
||||
const { stat, symbolicLink } = await statLink(this.path);
|
||||
const { stat, symbolicLink } = await SymlinkSupport.stat(this.path);
|
||||
|
||||
if (this.isDisposed) {
|
||||
return;
|
||||
@@ -47,6 +47,10 @@ export class FileWatcher extends Disposable {
|
||||
pathToWatch = await realpath(pathToWatch);
|
||||
} catch (error) {
|
||||
this.onError(error);
|
||||
|
||||
if (symbolicLink.dangling) {
|
||||
return; // give up if symbolic link is dangling
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +74,9 @@ export class FileWatcher extends Disposable {
|
||||
}, error => this.onError(error)));
|
||||
}
|
||||
} catch (error) {
|
||||
this.onError(error);
|
||||
if (error.code !== 'ENOENT') {
|
||||
this.onError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as glob from 'vs/base/common/glob';
|
||||
import * as extpath from 'vs/base/common/extpath';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { IDiskFileChange, normalizeFileChanges, ILogMessage } from 'vs/platform/files/node/watcher/watcher';
|
||||
import * as nsfw from 'vscode-nsfw';
|
||||
import * as glob from 'vs/base/common/glob';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { isEqualOrParent } from 'vs/base/common/extpath';
|
||||
import { IDiskFileChange, normalizeFileChanges, ILogMessage } from 'vs/platform/files/node/watcher/watcher';
|
||||
import { IWatcherService, IWatcherRequest } from 'vs/platform/files/node/watcher/nsfw/watcher';
|
||||
import { ThrottledDelayer } from 'vs/base/common/async';
|
||||
import { FileChangeType } from 'vs/platform/files/common/files';
|
||||
@@ -111,7 +111,7 @@ export class NsfwWatcherService extends Disposable implements IWatcherService {
|
||||
// We have to detect this case and massage the events to correct this.
|
||||
let realBasePathDiffers = false;
|
||||
let realBasePathLength = request.path.length;
|
||||
if (platform.isMacintosh) {
|
||||
if (isMacintosh) {
|
||||
try {
|
||||
|
||||
// First check for symbolic link
|
||||
@@ -141,7 +141,7 @@ export class NsfwWatcherService extends Disposable implements IWatcherService {
|
||||
for (const e of events) {
|
||||
// Logging
|
||||
if (this.verboseLogging) {
|
||||
const logPath = e.action === nsfw.actions.RENAMED ? path.join(e.directory, e.oldFile || '') + ' -> ' + e.newFile : path.join(e.directory, e.file || '');
|
||||
const logPath = e.action === nsfw.actions.RENAMED ? join(e.directory, e.oldFile || '') + ' -> ' + e.newFile : join(e.directory, e.file || '');
|
||||
this.log(`${e.action === nsfw.actions.CREATED ? '[CREATED]' : e.action === nsfw.actions.DELETED ? '[DELETED]' : e.action === nsfw.actions.MODIFIED ? '[CHANGED]' : '[RENAMED]'} ${logPath}`);
|
||||
}
|
||||
|
||||
@@ -149,20 +149,20 @@ export class NsfwWatcherService extends Disposable implements IWatcherService {
|
||||
let absolutePath: string;
|
||||
if (e.action === nsfw.actions.RENAMED) {
|
||||
// Rename fires when a file's name changes within a single directory
|
||||
absolutePath = path.join(e.directory, e.oldFile || '');
|
||||
absolutePath = join(e.directory, e.oldFile || '');
|
||||
if (!this.isPathIgnored(absolutePath, this.pathWatchers[request.path].ignored)) {
|
||||
undeliveredFileEvents.push({ type: FileChangeType.DELETED, path: absolutePath });
|
||||
} else if (this.verboseLogging) {
|
||||
this.log(` >> ignored ${absolutePath}`);
|
||||
}
|
||||
absolutePath = path.join(e.newDirectory || e.directory, e.newFile || '');
|
||||
absolutePath = join(e.newDirectory || e.directory, e.newFile || '');
|
||||
if (!this.isPathIgnored(absolutePath, this.pathWatchers[request.path].ignored)) {
|
||||
undeliveredFileEvents.push({ type: FileChangeType.ADDED, path: absolutePath });
|
||||
} else if (this.verboseLogging) {
|
||||
this.log(` >> ignored ${absolutePath}`);
|
||||
}
|
||||
} else {
|
||||
absolutePath = path.join(e.directory, e.file || '');
|
||||
absolutePath = join(e.directory, e.file || '');
|
||||
if (!this.isPathIgnored(absolutePath, this.pathWatchers[request.path].ignored)) {
|
||||
undeliveredFileEvents.push({
|
||||
type: nsfwActionToRawChangeType[e.action],
|
||||
@@ -179,7 +179,7 @@ export class NsfwWatcherService extends Disposable implements IWatcherService {
|
||||
const events = undeliveredFileEvents;
|
||||
undeliveredFileEvents = [];
|
||||
|
||||
if (platform.isMacintosh) {
|
||||
if (isMacintosh) {
|
||||
events.forEach(e => {
|
||||
|
||||
// Mac uses NFD unicode form on disk, but we want NFC
|
||||
@@ -230,7 +230,7 @@ export class NsfwWatcherService extends Disposable implements IWatcherService {
|
||||
// Normalizes a set of root paths by removing any root paths that are
|
||||
// sub-paths of other roots.
|
||||
return roots.filter(r => roots.every(other => {
|
||||
return !(r.path.length > other.path.length && extpath.isEqualOrParent(r.path, other.path));
|
||||
return !(r.path.length > other.path.length && isEqualOrParent(r.path, other.path));
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -30,28 +30,28 @@ suite('NSFW Watcher Service', async () => {
|
||||
test('should not impacts roots that don\'t overlap', () => {
|
||||
const service = new TestNsfwWatcherService();
|
||||
if (platform.isWindows) {
|
||||
assert.deepEqual(service.normalizeRoots(['C:\\a']), ['C:\\a']);
|
||||
assert.deepEqual(service.normalizeRoots(['C:\\a', 'C:\\b']), ['C:\\a', 'C:\\b']);
|
||||
assert.deepEqual(service.normalizeRoots(['C:\\a', 'C:\\b', 'C:\\c\\d\\e']), ['C:\\a', 'C:\\b', 'C:\\c\\d\\e']);
|
||||
assert.deepStrictEqual(service.normalizeRoots(['C:\\a']), ['C:\\a']);
|
||||
assert.deepStrictEqual(service.normalizeRoots(['C:\\a', 'C:\\b']), ['C:\\a', 'C:\\b']);
|
||||
assert.deepStrictEqual(service.normalizeRoots(['C:\\a', 'C:\\b', 'C:\\c\\d\\e']), ['C:\\a', 'C:\\b', 'C:\\c\\d\\e']);
|
||||
} else {
|
||||
assert.deepEqual(service.normalizeRoots(['/a']), ['/a']);
|
||||
assert.deepEqual(service.normalizeRoots(['/a', '/b']), ['/a', '/b']);
|
||||
assert.deepEqual(service.normalizeRoots(['/a', '/b', '/c/d/e']), ['/a', '/b', '/c/d/e']);
|
||||
assert.deepStrictEqual(service.normalizeRoots(['/a']), ['/a']);
|
||||
assert.deepStrictEqual(service.normalizeRoots(['/a', '/b']), ['/a', '/b']);
|
||||
assert.deepStrictEqual(service.normalizeRoots(['/a', '/b', '/c/d/e']), ['/a', '/b', '/c/d/e']);
|
||||
}
|
||||
});
|
||||
|
||||
test('should remove sub-folders of other roots', () => {
|
||||
const service = new TestNsfwWatcherService();
|
||||
if (platform.isWindows) {
|
||||
assert.deepEqual(service.normalizeRoots(['C:\\a', 'C:\\a\\b']), ['C:\\a']);
|
||||
assert.deepEqual(service.normalizeRoots(['C:\\a', 'C:\\b', 'C:\\a\\b']), ['C:\\a', 'C:\\b']);
|
||||
assert.deepEqual(service.normalizeRoots(['C:\\b\\a', 'C:\\a', 'C:\\b', 'C:\\a\\b']), ['C:\\a', 'C:\\b']);
|
||||
assert.deepEqual(service.normalizeRoots(['C:\\a', 'C:\\a\\b', 'C:\\a\\c\\d']), ['C:\\a']);
|
||||
assert.deepStrictEqual(service.normalizeRoots(['C:\\a', 'C:\\a\\b']), ['C:\\a']);
|
||||
assert.deepStrictEqual(service.normalizeRoots(['C:\\a', 'C:\\b', 'C:\\a\\b']), ['C:\\a', 'C:\\b']);
|
||||
assert.deepStrictEqual(service.normalizeRoots(['C:\\b\\a', 'C:\\a', 'C:\\b', 'C:\\a\\b']), ['C:\\a', 'C:\\b']);
|
||||
assert.deepStrictEqual(service.normalizeRoots(['C:\\a', 'C:\\a\\b', 'C:\\a\\c\\d']), ['C:\\a']);
|
||||
} else {
|
||||
assert.deepEqual(service.normalizeRoots(['/a', '/a/b']), ['/a']);
|
||||
assert.deepEqual(service.normalizeRoots(['/a', '/b', '/a/b']), ['/a', '/b']);
|
||||
assert.deepEqual(service.normalizeRoots(['/b/a', '/a', '/b', '/a/b']), ['/a', '/b']);
|
||||
assert.deepEqual(service.normalizeRoots(['/a', '/a/b', '/a/c/d']), ['/a']);
|
||||
assert.deepStrictEqual(service.normalizeRoots(['/a', '/a/b']), ['/a']);
|
||||
assert.deepStrictEqual(service.normalizeRoots(['/a', '/b', '/a/b']), ['/a', '/b']);
|
||||
assert.deepStrictEqual(service.normalizeRoots(['/b/a', '/a', '/b', '/a/b']), ['/a', '/b']);
|
||||
assert.deepStrictEqual(service.normalizeRoots(['/a', '/a/b', '/a/c/d']), ['/a']);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -39,9 +39,9 @@ export class FileWatcher extends Disposable {
|
||||
serverName: 'File Watcher (nsfw)',
|
||||
args: ['--type=watcherService'],
|
||||
env: {
|
||||
AMD_ENTRYPOINT: 'vs/platform/files/node/watcher/nsfw/watcherApp',
|
||||
PIPE_LOGGING: 'true',
|
||||
VERBOSE_LOGGING: 'true' // transmit console logs from server to client
|
||||
VSCODE_AMD_ENTRYPOINT: 'vs/platform/files/node/watcher/nsfw/watcherApp',
|
||||
VSCODE_PIPE_LOGGING: 'true',
|
||||
VSCODE_VERBOSE_LOGGING: 'true' // transmit console logs from server to client
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
@@ -6,9 +6,8 @@
|
||||
import * as chokidar from 'chokidar';
|
||||
import * as fs from 'fs';
|
||||
import * as gracefulFs from 'graceful-fs';
|
||||
gracefulFs.gracefulify(fs);
|
||||
import * as extpath from 'vs/base/common/extpath';
|
||||
import * as glob from 'vs/base/common/glob';
|
||||
import { isEqualOrParent } from 'vs/base/common/extpath';
|
||||
import { FileChangeType } from 'vs/platform/files/common/files';
|
||||
import { ThrottledDelayer } from 'vs/base/common/async';
|
||||
import { normalizeNFC } from 'vs/base/common/normalization';
|
||||
@@ -20,6 +19,8 @@ import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { equals } from 'vs/base/common/arrays';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
gracefulFs.gracefulify(fs); // enable gracefulFs
|
||||
|
||||
process.noAsar = true; // disable ASAR support in watcher process
|
||||
|
||||
interface IWatcher {
|
||||
@@ -311,7 +312,7 @@ function isIgnored(path: string, requests: ExtendedWatcherRequest[]): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (extpath.isEqualOrParent(path, request.path)) {
|
||||
if (isEqualOrParent(path, request.path)) {
|
||||
if (!request.parsedPattern) {
|
||||
if (request.excludes && request.excludes.length > 0) {
|
||||
const pattern = `{${request.excludes.join(',')}}`;
|
||||
@@ -343,7 +344,7 @@ export function normalizeRoots(requests: IWatcherRequest[]): { [basePath: string
|
||||
for (const request of requests) {
|
||||
const basePath = request.path;
|
||||
const ignored = (request.excludes || []).sort();
|
||||
if (prevRequest && (extpath.isEqualOrParent(basePath, prevRequest.path))) {
|
||||
if (prevRequest && (isEqualOrParent(basePath, prevRequest.path))) {
|
||||
if (!isEqualIgnore(ignored, prevRequest.excludes)) {
|
||||
result[prevRequest.path].push({ path: basePath, excludes: ignored });
|
||||
}
|
||||
|
||||
@@ -20,18 +20,18 @@ suite('Chokidar normalizeRoots', async () => {
|
||||
function assertNormalizedRootPath(inputPaths: string[], expectedPaths: string[]) {
|
||||
const requests = inputPaths.map(path => newRequest(path));
|
||||
const actual = normalizeRoots(requests);
|
||||
assert.deepEqual(Object.keys(actual).sort(), expectedPaths);
|
||||
assert.deepStrictEqual(Object.keys(actual).sort(), expectedPaths);
|
||||
}
|
||||
|
||||
function assertNormalizedRequests(inputRequests: IWatcherRequest[], expectedRequests: { [path: string]: IWatcherRequest[] }) {
|
||||
const actual = normalizeRoots(inputRequests);
|
||||
const actualPath = Object.keys(actual).sort();
|
||||
const expectedPaths = Object.keys(expectedRequests).sort();
|
||||
assert.deepEqual(actualPath, expectedPaths);
|
||||
assert.deepStrictEqual(actualPath, expectedPaths);
|
||||
for (let path of actualPath) {
|
||||
let a = expectedRequests[path].sort((r1, r2) => r1.path.localeCompare(r2.path));
|
||||
let e = expectedRequests[path].sort((r1, r2) => r1.path.localeCompare(r2.path));
|
||||
assert.deepEqual(a, e);
|
||||
assert.deepStrictEqual(a, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,9 +40,9 @@ export class FileWatcher extends Disposable {
|
||||
serverName: 'File Watcher (chokidar)',
|
||||
args: ['--type=watcherService'],
|
||||
env: {
|
||||
AMD_ENTRYPOINT: 'vs/platform/files/node/watcher/unix/watcherApp',
|
||||
PIPE_LOGGING: 'true',
|
||||
VERBOSE_LOGGING: 'true' // transmit console logs from server to client
|
||||
VSCODE_AMD_ENTRYPOINT: 'vs/platform/files/node/watcher/unix/watcherApp',
|
||||
VSCODE_PIPE_LOGGING: 'true',
|
||||
VSCODE_VERBOSE_LOGGING: 'true' // transmit console logs from server to client
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
Reference in New Issue
Block a user