mirror of
https://github.com/coder/code-server.git
synced 2025-07-30 05:22:04 +08:00
chore(vscode): update to 1.54.2
This commit is contained in:
@ -7,7 +7,6 @@ import * as fs from 'fs';
|
||||
import { rtrim } from 'vs/base/common/strings';
|
||||
import { sep, join, normalize, dirname, basename } from 'vs/base/common/path';
|
||||
import { readdirSync } from 'vs/base/node/pfs';
|
||||
import { promisify } from 'util';
|
||||
|
||||
/**
|
||||
* Copied from: https://github.com/microsoft/vscode-node-debug/blob/master/src/node/pathUtilities.ts#L83
|
||||
@ -53,7 +52,7 @@ export function realcaseSync(path: string): string | null {
|
||||
|
||||
export async function realpath(path: string): Promise<string> {
|
||||
try {
|
||||
return await promisify(fs.realpath)(path);
|
||||
return await fs.promises.realpath(path);
|
||||
} catch (error) {
|
||||
|
||||
// We hit an error calling fs.realpath(). Since fs.realpath() is doing some path normalization
|
||||
@ -63,7 +62,7 @@ export async function realpath(path: string): Promise<string> {
|
||||
// to not resolve links but to simply see if the path is read accessible or not.
|
||||
const normalizedPath = normalizePath(path);
|
||||
|
||||
await promisify(fs.access)(normalizedPath, fs.constants.R_OK);
|
||||
await fs.promises.access(normalizedPath, fs.constants.R_OK);
|
||||
|
||||
return normalizedPath;
|
||||
}
|
||||
|
@ -2,87 +2,83 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
/// <reference path="../../../typings/require.d.ts" />
|
||||
|
||||
//@ts-check
|
||||
|
||||
/**
|
||||
* @param {NodeRequire} nodeRequire
|
||||
* @param {typeof import('path')} path
|
||||
* @param {typeof import('fs')} fs
|
||||
* @param {typeof import('../common/performance')} perf
|
||||
*/
|
||||
function factory(nodeRequire, path, fs, perf) {
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @param {string} file
|
||||
* @returns {Promise<boolean>}
|
||||
* @param {NodeRequire} nodeRequire
|
||||
* @param {typeof import('path')} path
|
||||
* @param {typeof import('fs')} fs
|
||||
* @param {typeof import('../common/performance')} perf
|
||||
*/
|
||||
function exists(file) {
|
||||
return new Promise(c => fs.exists(file, c));
|
||||
}
|
||||
function factory(nodeRequire, path, fs, perf) {
|
||||
|
||||
/**
|
||||
* @param {string} file
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
function touch(file) {
|
||||
return new Promise((c, e) => { const d = new Date(); fs.utimes(file, d, d, err => err ? e(err) : c()); });
|
||||
}
|
||||
/**
|
||||
* @param {string} file
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
function exists(file) {
|
||||
return new Promise(c => fs.exists(file, c));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} file
|
||||
* @returns {Promise<object>}
|
||||
*/
|
||||
function lstat(file) {
|
||||
return new Promise((c, e) => fs.lstat(file, (err, stats) => err ? e(err) : c(stats)));
|
||||
}
|
||||
/**
|
||||
* @param {string} file
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
function touch(file) {
|
||||
return new Promise((c, e) => { const d = new Date(); fs.utimes(file, d, d, err => err ? e(err) : c()); });
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} dir
|
||||
* @returns {Promise<string[]>}
|
||||
*/
|
||||
function readdir(dir) {
|
||||
return new Promise((c, e) => fs.readdir(dir, (err, files) => err ? e(err) : c(files)));
|
||||
}
|
||||
/**
|
||||
* @param {string} dir
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
function mkdirp(dir) {
|
||||
return new Promise((c, e) => fs.mkdir(dir, { recursive: true }, err => (err && err.code !== 'EEXIST') ? e(err) : c(dir)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} dir
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
function mkdirp(dir) {
|
||||
return new Promise((c, e) => fs.mkdir(dir, { recursive: true }, err => (err && err.code !== 'EEXIST') ? e(err) : c(dir)));
|
||||
}
|
||||
/**
|
||||
* @param {string} location
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
function rimraf(location) {
|
||||
return new Promise((c, e) => fs.rmdir(location, { recursive: true }, err => (err && err.code !== 'ENOENT') ? e(err) : c()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} dir
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
function rmdir(dir) {
|
||||
return new Promise((c, e) => fs.rmdir(dir, err => err ? e(err) : c(undefined)));
|
||||
}
|
||||
/**
|
||||
* @param {string} file
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
function readFile(file) {
|
||||
return new Promise((c, e) => fs.readFile(file, 'utf8', (err, data) => err ? e(err) : c(data)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} file
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
function unlink(file) {
|
||||
return new Promise((c, e) => fs.unlink(file, err => err ? e(err) : c(undefined)));
|
||||
}
|
||||
/**
|
||||
* @param {string} file
|
||||
* @param {string} content
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
function writeFile(file, content) {
|
||||
return new Promise((c, e) => fs.writeFile(file, content, 'utf8', err => err ? e(err) : c()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} location
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
function rimraf(location) {
|
||||
return lstat(location).then(stat => {
|
||||
if (stat.isDirectory() && !stat.isSymbolicLink()) {
|
||||
return readdir(location)
|
||||
.then(children => Promise.all(children.map(child => rimraf(path.join(location, child)))))
|
||||
.then(() => rmdir(location));
|
||||
} else {
|
||||
return unlink(location);
|
||||
/**
|
||||
* @param {string} userDataPath
|
||||
* @returns {object}
|
||||
*/
|
||||
function getLanguagePackConfigurations(userDataPath) {
|
||||
const configFile = path.join(userDataPath, 'languagepacks.json');
|
||||
try {
|
||||
return nodeRequire(configFile);
|
||||
} catch (err) {
|
||||
// Do nothing. If we can't read the file we have no
|
||||
// language pack config.
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
}, err => {
|
||||
if (err.code === 'ENOENT') {
|
||||
return undefined;
|
||||
@ -135,183 +131,184 @@ function factory(nodeRequire, path, fs, perf) {
|
||||
} catch (err) {
|
||||
// Do nothing. If we can't read the file we have no
|
||||
// language pack config.
|
||||
=======
|
||||
return undefined;
|
||||
>>>>>>> e8cd17a97d8c58fffcbac05394b3ee2b3c72d384
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} config
|
||||
* @param {string} locale
|
||||
*/
|
||||
function resolveLanguagePackLocale(config, locale) {
|
||||
try {
|
||||
while (locale) {
|
||||
if (config[locale]) {
|
||||
return locale;
|
||||
} else {
|
||||
const index = locale.lastIndexOf('-');
|
||||
if (index > 0) {
|
||||
locale = locale.substring(0, index);
|
||||
/**
|
||||
* @param {object} config
|
||||
* @param {string} locale
|
||||
*/
|
||||
function resolveLanguagePackLocale(config, locale) {
|
||||
try {
|
||||
while (locale) {
|
||||
if (config[locale]) {
|
||||
return locale;
|
||||
} else {
|
||||
return undefined;
|
||||
const index = locale.lastIndexOf('-');
|
||||
if (index > 0) {
|
||||
locale = locale.substring(0, index);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Resolving language pack configuration failed.', err);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Resolving language pack configuration failed.', err);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} commit
|
||||
* @param {string} userDataPath
|
||||
* @param {string} metaDataFile
|
||||
* @param {string} locale
|
||||
*/
|
||||
function getNLSConfiguration(commit, userDataPath, metaDataFile, locale) {
|
||||
if (locale === 'pseudo') {
|
||||
return Promise.resolve({ locale: locale, availableLanguages: {}, pseudo: true });
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (process.env['VSCODE_DEV']) {
|
||||
return Promise.resolve({ locale: locale, availableLanguages: {} });
|
||||
}
|
||||
|
||||
// We have a built version so we have extracted nls file. Try to find
|
||||
// the right file to use.
|
||||
|
||||
// Check if we have an English or English US locale. If so fall to default since that is our
|
||||
// English translation (we don't ship *.nls.en.json files)
|
||||
if (locale && (locale === 'en' || locale === 'en-us')) {
|
||||
return Promise.resolve({ locale: locale, availableLanguages: {} });
|
||||
}
|
||||
|
||||
const initialLocale = locale;
|
||||
|
||||
perf.mark('code/willGenerateNls');
|
||||
|
||||
const defaultResult = function (locale) {
|
||||
perf.mark('code/didGenerateNls');
|
||||
return Promise.resolve({ locale: locale, availableLanguages: {} });
|
||||
};
|
||||
try {
|
||||
if (!commit) {
|
||||
return defaultResult(initialLocale);
|
||||
/**
|
||||
* @param {string} commit
|
||||
* @param {string} userDataPath
|
||||
* @param {string} metaDataFile
|
||||
* @param {string} locale
|
||||
*/
|
||||
function getNLSConfiguration(commit, userDataPath, metaDataFile, locale) {
|
||||
if (locale === 'pseudo') {
|
||||
return Promise.resolve({ locale: locale, availableLanguages: {}, pseudo: true });
|
||||
}
|
||||
const configs = getLanguagePackConfigurations(userDataPath);
|
||||
if (!configs) {
|
||||
return defaultResult(initialLocale);
|
||||
|
||||
if (process.env['VSCODE_DEV']) {
|
||||
return Promise.resolve({ locale: locale, availableLanguages: {} });
|
||||
}
|
||||
locale = resolveLanguagePackLocale(configs, locale);
|
||||
if (!locale) {
|
||||
return defaultResult(initialLocale);
|
||||
|
||||
// We have a built version so we have extracted nls file. Try to find
|
||||
// the right file to use.
|
||||
|
||||
// Check if we have an English or English US locale. If so fall to default since that is our
|
||||
// English translation (we don't ship *.nls.en.json files)
|
||||
if (locale && (locale === 'en' || locale === 'en-us')) {
|
||||
return Promise.resolve({ locale: locale, availableLanguages: {} });
|
||||
}
|
||||
const packConfig = configs[locale];
|
||||
let mainPack;
|
||||
if (!packConfig || typeof packConfig.hash !== 'string' || !packConfig.translations || typeof (mainPack = packConfig.translations['vscode']) !== 'string') {
|
||||
return defaultResult(initialLocale);
|
||||
}
|
||||
return exists(mainPack).then(fileExists => {
|
||||
if (!fileExists) {
|
||||
|
||||
const initialLocale = locale;
|
||||
|
||||
perf.mark('code/willGenerateNls');
|
||||
|
||||
const defaultResult = function (locale) {
|
||||
perf.mark('code/didGenerateNls');
|
||||
return Promise.resolve({ locale: locale, availableLanguages: {} });
|
||||
};
|
||||
try {
|
||||
if (!commit) {
|
||||
return defaultResult(initialLocale);
|
||||
}
|
||||
const packId = packConfig.hash + '.' + locale;
|
||||
const cacheRoot = path.join(userDataPath, 'clp', packId);
|
||||
const coreLocation = path.join(cacheRoot, commit);
|
||||
const translationsConfigFile = path.join(cacheRoot, 'tcf.json');
|
||||
const corruptedFile = path.join(cacheRoot, 'corrupted.info');
|
||||
const result = {
|
||||
locale: initialLocale,
|
||||
availableLanguages: { '*': locale },
|
||||
_languagePackId: packId,
|
||||
_translationsConfigFile: translationsConfigFile,
|
||||
_cacheRoot: cacheRoot,
|
||||
_resolvedLanguagePackCoreLocation: coreLocation,
|
||||
_corruptedFile: corruptedFile
|
||||
};
|
||||
return exists(corruptedFile).then(corrupted => {
|
||||
// The nls cache directory is corrupted.
|
||||
let toDelete;
|
||||
if (corrupted) {
|
||||
toDelete = rimraf(cacheRoot);
|
||||
} else {
|
||||
toDelete = Promise.resolve(undefined);
|
||||
const configs = getLanguagePackConfigurations(userDataPath);
|
||||
if (!configs) {
|
||||
return defaultResult(initialLocale);
|
||||
}
|
||||
locale = resolveLanguagePackLocale(configs, locale);
|
||||
if (!locale) {
|
||||
return defaultResult(initialLocale);
|
||||
}
|
||||
const packConfig = configs[locale];
|
||||
let mainPack;
|
||||
if (!packConfig || typeof packConfig.hash !== 'string' || !packConfig.translations || typeof (mainPack = packConfig.translations['vscode']) !== 'string') {
|
||||
return defaultResult(initialLocale);
|
||||
}
|
||||
return exists(mainPack).then(fileExists => {
|
||||
if (!fileExists) {
|
||||
return defaultResult(initialLocale);
|
||||
}
|
||||
return toDelete.then(() => {
|
||||
return exists(coreLocation).then(fileExists => {
|
||||
if (fileExists) {
|
||||
// We don't wait for this. No big harm if we can't touch
|
||||
touch(coreLocation).catch(() => { });
|
||||
perf.mark('code/didGenerateNls');
|
||||
return result;
|
||||
}
|
||||
return mkdirp(coreLocation).then(() => {
|
||||
return Promise.all([readFile(metaDataFile), readFile(mainPack)]);
|
||||
}).then(values => {
|
||||
const metadata = JSON.parse(values[0]);
|
||||
const packData = JSON.parse(values[1]).contents;
|
||||
const bundles = Object.keys(metadata.bundles);
|
||||
const writes = [];
|
||||
for (const bundle of bundles) {
|
||||
const modules = metadata.bundles[bundle];
|
||||
const target = Object.create(null);
|
||||
for (const module of modules) {
|
||||
const keys = metadata.keys[module];
|
||||
const defaultMessages = metadata.messages[module];
|
||||
const translations = packData[module];
|
||||
let targetStrings;
|
||||
if (translations) {
|
||||
targetStrings = [];
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const elem = keys[i];
|
||||
const key = typeof elem === 'string' ? elem : elem.key;
|
||||
let translatedMessage = translations[key];
|
||||
if (translatedMessage === undefined) {
|
||||
translatedMessage = defaultMessages[i];
|
||||
}
|
||||
targetStrings.push(translatedMessage);
|
||||
}
|
||||
} else {
|
||||
targetStrings = defaultMessages;
|
||||
}
|
||||
target[module] = targetStrings;
|
||||
}
|
||||
writes.push(writeFile(path.join(coreLocation, bundle.replace(/\//g, '!') + '.nls.json'), JSON.stringify(target)));
|
||||
const packId = packConfig.hash + '.' + locale;
|
||||
const cacheRoot = path.join(userDataPath, 'clp', packId);
|
||||
const coreLocation = path.join(cacheRoot, commit);
|
||||
const translationsConfigFile = path.join(cacheRoot, 'tcf.json');
|
||||
const corruptedFile = path.join(cacheRoot, 'corrupted.info');
|
||||
const result = {
|
||||
locale: initialLocale,
|
||||
availableLanguages: { '*': locale },
|
||||
_languagePackId: packId,
|
||||
_translationsConfigFile: translationsConfigFile,
|
||||
_cacheRoot: cacheRoot,
|
||||
_resolvedLanguagePackCoreLocation: coreLocation,
|
||||
_corruptedFile: corruptedFile
|
||||
};
|
||||
return exists(corruptedFile).then(corrupted => {
|
||||
// The nls cache directory is corrupted.
|
||||
let toDelete;
|
||||
if (corrupted) {
|
||||
toDelete = rimraf(cacheRoot);
|
||||
} else {
|
||||
toDelete = Promise.resolve(undefined);
|
||||
}
|
||||
return toDelete.then(() => {
|
||||
return exists(coreLocation).then(fileExists => {
|
||||
if (fileExists) {
|
||||
// We don't wait for this. No big harm if we can't touch
|
||||
touch(coreLocation).catch(() => { });
|
||||
perf.mark('code/didGenerateNls');
|
||||
return result;
|
||||
}
|
||||
writes.push(writeFile(translationsConfigFile, JSON.stringify(packConfig.translations)));
|
||||
return Promise.all(writes);
|
||||
}).then(() => {
|
||||
perf.mark('code/didGenerateNls');
|
||||
return result;
|
||||
}).catch(err => {
|
||||
console.error('Generating translation files failed.', err);
|
||||
return defaultResult(locale);
|
||||
return mkdirp(coreLocation).then(() => {
|
||||
return Promise.all([readFile(metaDataFile), readFile(mainPack)]);
|
||||
}).then(values => {
|
||||
const metadata = JSON.parse(values[0]);
|
||||
const packData = JSON.parse(values[1]).contents;
|
||||
const bundles = Object.keys(metadata.bundles);
|
||||
const writes = [];
|
||||
for (const bundle of bundles) {
|
||||
const modules = metadata.bundles[bundle];
|
||||
const target = Object.create(null);
|
||||
for (const module of modules) {
|
||||
const keys = metadata.keys[module];
|
||||
const defaultMessages = metadata.messages[module];
|
||||
const translations = packData[module];
|
||||
let targetStrings;
|
||||
if (translations) {
|
||||
targetStrings = [];
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const elem = keys[i];
|
||||
const key = typeof elem === 'string' ? elem : elem.key;
|
||||
let translatedMessage = translations[key];
|
||||
if (translatedMessage === undefined) {
|
||||
translatedMessage = defaultMessages[i];
|
||||
}
|
||||
targetStrings.push(translatedMessage);
|
||||
}
|
||||
} else {
|
||||
targetStrings = defaultMessages;
|
||||
}
|
||||
target[module] = targetStrings;
|
||||
}
|
||||
writes.push(writeFile(path.join(coreLocation, bundle.replace(/\//g, '!') + '.nls.json'), JSON.stringify(target)));
|
||||
}
|
||||
writes.push(writeFile(translationsConfigFile, JSON.stringify(packConfig.translations)));
|
||||
return Promise.all(writes);
|
||||
}).then(() => {
|
||||
perf.mark('code/didGenerateNls');
|
||||
return result;
|
||||
}).catch(err => {
|
||||
console.error('Generating translation files failed.', err);
|
||||
return defaultResult(locale);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Generating translation files failed.', err);
|
||||
return defaultResult(locale);
|
||||
} catch (err) {
|
||||
console.error('Generating translation files failed.', err);
|
||||
return defaultResult(locale);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
getNLSConfiguration
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
getNLSConfiguration
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
if (typeof define === 'function') {
|
||||
// amd
|
||||
define(['path', 'fs', 'vs/base/common/performance'], function (path, fs, perf) { return factory(require.__$__nodeRequire, path, fs, perf); });
|
||||
} else if (typeof module === 'object' && typeof module.exports === 'object') {
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const perf = require('../common/performance');
|
||||
module.exports = factory(require, path, fs, perf);
|
||||
} else {
|
||||
throw new Error('Unknown context');
|
||||
}
|
||||
if (typeof define === 'function') {
|
||||
// amd
|
||||
define(['require', 'path', 'fs', 'vs/base/common/performance'], function (require, /** @type {typeof import('path')} */ path, /** @type {typeof import('fs')} */ fs, /** @type {typeof import('../common/performance')} */ perf) { return factory(require.__$__nodeRequire, path, fs, perf); });
|
||||
} else if (typeof module === 'object' && typeof module.exports === 'object') {
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const perf = require('../common/performance');
|
||||
module.exports = factory(require, path, fs, perf);
|
||||
} else {
|
||||
throw new Error('Unknown context');
|
||||
}
|
||||
}());
|
||||
|
@ -7,13 +7,14 @@ import * as fs from 'fs';
|
||||
import { tmpdir } from 'os';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { Queue } from 'vs/base/common/async';
|
||||
import { isMacintosh, isWindows } from 'vs/base/common/platform';
|
||||
import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { promisify } from 'util';
|
||||
import { isRootOrDriveLetter } from 'vs/base/common/extpath';
|
||||
import { isEqualOrParent, isRootOrDriveLetter } from 'vs/base/common/extpath';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { normalizeNFC } from 'vs/base/common/normalization';
|
||||
|
||||
//#region Constants
|
||||
|
||||
// See https://github.com/microsoft/vscode/issues/30180
|
||||
const WIN32_MAX_FILE_SIZE = 300 * 1024 * 1024; // 300 MB
|
||||
const GENERAL_MAX_FILE_SIZE = 16 * 1024 * 1024 * 1024; // 16 GB
|
||||
@ -25,6 +26,10 @@ const GENERAL_MAX_HEAP_SIZE = 700 * 2 * 1024 * 1024; // 1400 MB
|
||||
export const MAX_FILE_SIZE = process.arch === 'ia32' ? WIN32_MAX_FILE_SIZE : GENERAL_MAX_FILE_SIZE;
|
||||
export const MAX_HEAP_SIZE = process.arch === 'ia32' ? WIN32_MAX_HEAP_SIZE : GENERAL_MAX_HEAP_SIZE;
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region rimraf
|
||||
|
||||
export enum RimRafMode {
|
||||
|
||||
/**
|
||||
@ -40,12 +45,19 @@ export enum RimRafMode {
|
||||
MOVE
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to delete the provied path (either file or folder) recursively
|
||||
* with the options:
|
||||
* - `UNLINK`: direct removal from disk
|
||||
* - `MOVE`: faster variant that first moves the target to temp dir and then
|
||||
* deletes it in the background without waiting for that to finish.
|
||||
*/
|
||||
export async function rimraf(path: string, mode = RimRafMode.UNLINK): Promise<void> {
|
||||
if (isRootOrDriveLetter(path)) {
|
||||
throw new Error('rimraf - will refuse to recursively delete root');
|
||||
}
|
||||
|
||||
// delete: via unlink
|
||||
// delete: via rmDir
|
||||
if (mode === RimRafMode.UNLINK) {
|
||||
return rimrafUnlink(path);
|
||||
}
|
||||
@ -54,32 +66,17 @@ export async function rimraf(path: string, mode = RimRafMode.UNLINK): Promise<vo
|
||||
return rimrafMove(path);
|
||||
}
|
||||
|
||||
async function rimrafUnlink(path: string): Promise<void> {
|
||||
async function rimrafMove(path: string): Promise<void> {
|
||||
try {
|
||||
const stat = await lstat(path);
|
||||
|
||||
// Folder delete (recursive) - NOT for symbolic links though!
|
||||
if (stat.isDirectory() && !stat.isSymbolicLink()) {
|
||||
|
||||
// Children
|
||||
const children = await readdir(path);
|
||||
await Promise.all(children.map(child => rimrafUnlink(join(path, child))));
|
||||
|
||||
// Folder
|
||||
await promisify(fs.rmdir)(path);
|
||||
const pathInTemp = join(tmpdir(), generateUuid());
|
||||
try {
|
||||
await fs.promises.rename(path, pathInTemp);
|
||||
} catch (error) {
|
||||
return rimrafUnlink(path); // if rename fails, delete without tmp dir
|
||||
}
|
||||
|
||||
// Single file delete
|
||||
else {
|
||||
|
||||
// chmod as needed to allow for unlink
|
||||
const mode = stat.mode;
|
||||
if (!(mode & fs.constants.S_IWUSR)) {
|
||||
await chmod(path, mode | fs.constants.S_IWUSR);
|
||||
}
|
||||
|
||||
return unlink(path);
|
||||
}
|
||||
// Delete but do not return as promise
|
||||
rimrafUnlink(pathInTemp).catch(error => {/* ignore */ });
|
||||
} catch (error) {
|
||||
if (error.code !== 'ENOENT') {
|
||||
throw error;
|
||||
@ -87,22 +84,8 @@ async function rimrafUnlink(path: string): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
async function rimrafMove(path: string): Promise<void> {
|
||||
try {
|
||||
const pathInTemp = join(tmpdir(), generateUuid());
|
||||
try {
|
||||
await rename(path, pathInTemp);
|
||||
} catch (error) {
|
||||
return rimrafUnlink(path); // if rename fails, delete without tmp dir
|
||||
}
|
||||
|
||||
// Delete but do not return as promise
|
||||
rimrafUnlink(pathInTemp);
|
||||
} catch (error) {
|
||||
if (error.code !== 'ENOENT') {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
async function rimrafUnlink(path: string): Promise<void> {
|
||||
return fs.promises.rmdir(path, { recursive: true, maxRetries: 3 });
|
||||
}
|
||||
|
||||
export function rimrafSync(path: string): void {
|
||||
@ -110,193 +93,281 @@ export function rimrafSync(path: string): void {
|
||||
throw new Error('rimraf - will refuse to recursively delete root');
|
||||
}
|
||||
|
||||
fs.rmdirSync(path, { recursive: true });
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region readdir with NFC support (macos)
|
||||
|
||||
export interface IDirent {
|
||||
name: string;
|
||||
|
||||
isFile(): boolean;
|
||||
isDirectory(): boolean;
|
||||
isSymbolicLink(): boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop-in replacement of `fs.readdir` with support
|
||||
* for converting from macOS NFD unicon form to NFC
|
||||
* (https://github.com/nodejs/node/issues/2165)
|
||||
*/
|
||||
export async function readdir(path: string): Promise<string[]>;
|
||||
export async function readdir(path: string, options: { withFileTypes: true }): Promise<IDirent[]>;
|
||||
export async function readdir(path: string, options?: { withFileTypes: true }): Promise<(string | IDirent)[]> {
|
||||
return handleDirectoryChildren(await (options ? safeReaddirWithFileTypes(path) : fs.promises.readdir(path)));
|
||||
}
|
||||
|
||||
async function safeReaddirWithFileTypes(path: string): Promise<IDirent[]> {
|
||||
try {
|
||||
const stat = fs.lstatSync(path);
|
||||
|
||||
// Folder delete (recursive) - NOT for symbolic links though!
|
||||
if (stat.isDirectory() && !stat.isSymbolicLink()) {
|
||||
|
||||
// Children
|
||||
const children = readdirSync(path);
|
||||
children.map(child => rimrafSync(join(path, child)));
|
||||
|
||||
// Folder
|
||||
fs.rmdirSync(path);
|
||||
}
|
||||
|
||||
// Single file delete
|
||||
else {
|
||||
|
||||
// chmod as needed to allow for unlink
|
||||
const mode = stat.mode;
|
||||
if (!(mode & fs.constants.S_IWUSR)) {
|
||||
fs.chmodSync(path, mode | fs.constants.S_IWUSR);
|
||||
}
|
||||
|
||||
return fs.unlinkSync(path);
|
||||
}
|
||||
return await fs.promises.readdir(path, { withFileTypes: true });
|
||||
} catch (error) {
|
||||
if (error.code !== 'ENOENT') {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function readdir(path: string): Promise<string[]> {
|
||||
return handleDirectoryChildren(await promisify(fs.readdir)(path));
|
||||
}
|
||||
|
||||
export async function readdirWithFileTypes(path: string): Promise<fs.Dirent[]> {
|
||||
const children = await promisify(fs.readdir)(path, { withFileTypes: true });
|
||||
|
||||
// Mac: uses NFD unicode form on disk, but we want NFC
|
||||
// See also https://github.com/nodejs/node/issues/2165
|
||||
if (isMacintosh) {
|
||||
for (const child of children) {
|
||||
child.name = normalizeNFC(child.name);
|
||||
}
|
||||
console.warn('[node.js fs] readdir with filetypes failed with error: ', error);
|
||||
}
|
||||
|
||||
return children;
|
||||
// Fallback to manually reading and resolving each
|
||||
// children of the folder in case we hit an error
|
||||
// previously.
|
||||
// This can only really happen on exotic file systems
|
||||
// such as explained in #115645 where we get entries
|
||||
// from `readdir` that we can later not `lstat`.
|
||||
const result: IDirent[] = [];
|
||||
const children = await readdir(path);
|
||||
for (const child of children) {
|
||||
let isFile = false;
|
||||
let isDirectory = false;
|
||||
let isSymbolicLink = false;
|
||||
|
||||
try {
|
||||
const lstat = await fs.promises.lstat(join(path, child));
|
||||
|
||||
isFile = lstat.isFile();
|
||||
isDirectory = lstat.isDirectory();
|
||||
isSymbolicLink = lstat.isSymbolicLink();
|
||||
} catch (error) {
|
||||
console.warn('[node.js fs] unexpected error from lstat after readdir: ', error);
|
||||
}
|
||||
|
||||
result.push({
|
||||
name: child,
|
||||
isFile: () => isFile,
|
||||
isDirectory: () => isDirectory,
|
||||
isSymbolicLink: () => isSymbolicLink
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop-in replacement of `fs.readdirSync` with support
|
||||
* for converting from macOS NFD unicon form to NFC
|
||||
* (https://github.com/nodejs/node/issues/2165)
|
||||
*/
|
||||
export function readdirSync(path: string): string[] {
|
||||
return handleDirectoryChildren(fs.readdirSync(path));
|
||||
}
|
||||
|
||||
function handleDirectoryChildren(children: string[]): string[] {
|
||||
// Mac: uses NFD unicode form on disk, but we want NFC
|
||||
// See also https://github.com/nodejs/node/issues/2165
|
||||
if (isMacintosh) {
|
||||
return children.map(child => normalizeNFC(child));
|
||||
}
|
||||
function handleDirectoryChildren(children: string[]): string[];
|
||||
function handleDirectoryChildren(children: IDirent[]): IDirent[];
|
||||
function handleDirectoryChildren(children: (string | IDirent)[]): (string | IDirent)[];
|
||||
function handleDirectoryChildren(children: (string | IDirent)[]): (string | IDirent)[] {
|
||||
return children.map(child => {
|
||||
|
||||
return children;
|
||||
}
|
||||
// Mac: uses NFD unicode form on disk, but we want NFC
|
||||
// See also https://github.com/nodejs/node/issues/2165
|
||||
|
||||
export function exists(path: string): Promise<boolean> {
|
||||
return promisify(fs.exists)(path);
|
||||
}
|
||||
|
||||
export function chmod(path: string, mode: number): Promise<void> {
|
||||
return promisify(fs.chmod)(path, mode);
|
||||
}
|
||||
|
||||
export function stat(path: string): Promise<fs.Stats> {
|
||||
return promisify(fs.stat)(path);
|
||||
}
|
||||
|
||||
export interface IStatAndLink {
|
||||
|
||||
// The stats of the file. If the file is a symbolic
|
||||
// link, the stats will be of that target file and
|
||||
// not the link itself.
|
||||
// If the file is a symbolic link pointing to a non
|
||||
// existing file, the stat will be of the link and
|
||||
// the `dangling` flag will indicate this.
|
||||
stat: fs.Stats;
|
||||
|
||||
// Will be provided if the resource is a symbolic link
|
||||
// on disk. Use the `dangling` flag to find out if it
|
||||
// points to a resource that does not exist on disk.
|
||||
symbolicLink?: { dangling: boolean };
|
||||
}
|
||||
|
||||
export async function statLink(path: string): Promise<IStatAndLink> {
|
||||
|
||||
// First stat the link
|
||||
let lstats: fs.Stats | undefined;
|
||||
try {
|
||||
lstats = await lstat(path);
|
||||
|
||||
// Return early if the stat is not a symbolic link at all
|
||||
if (!lstats.isSymbolicLink()) {
|
||||
return { stat: lstats };
|
||||
}
|
||||
} catch (error) {
|
||||
/* ignore - use stat() instead */
|
||||
}
|
||||
|
||||
// If the stat is a symbolic link or failed to stat, use fs.stat()
|
||||
// which for symbolic links will stat the target they point to
|
||||
try {
|
||||
const stats = await stat(path);
|
||||
|
||||
return { stat: stats, symbolicLink: lstats?.isSymbolicLink() ? { dangling: false } : undefined };
|
||||
} catch (error) {
|
||||
|
||||
// If the link points to a non-existing file we still want
|
||||
// to return it as result while setting dangling: true flag
|
||||
if (error.code === 'ENOENT' && lstats) {
|
||||
return { stat: lstats, symbolicLink: { dangling: true } };
|
||||
if (typeof child === 'string') {
|
||||
return isMacintosh ? normalizeNFC(child) : child;
|
||||
}
|
||||
|
||||
// Windows: workaround a node.js bug where reparse points
|
||||
// are not supported (https://github.com/nodejs/node/issues/36790)
|
||||
if (isWindows && error.code === 'EACCES' && lstats) {
|
||||
try {
|
||||
const stats = await stat(await readlink(path));
|
||||
child.name = isMacintosh ? normalizeNFC(child.name) : child.name;
|
||||
|
||||
return { stat: stats, symbolicLink: lstats.isSymbolicLink() ? { dangling: false } : undefined };
|
||||
} catch (error) {
|
||||
return child;
|
||||
});
|
||||
}
|
||||
|
||||
// If the link points to a non-existing file we still want
|
||||
// to return it as result while setting dangling: true flag
|
||||
if (error.code === 'ENOENT') {
|
||||
return { stat: lstats, symbolicLink: { dangling: true } };
|
||||
}
|
||||
/**
|
||||
* A convinience method to read all children of a path that
|
||||
* are directories.
|
||||
*/
|
||||
export async function readDirsInDir(dirPath: string): Promise<string[]> {
|
||||
const children = await readdir(dirPath);
|
||||
const directories: string[] = [];
|
||||
|
||||
throw error;
|
||||
for (const child of children) {
|
||||
if (await SymlinkSupport.existsDirectory(join(dirPath, child))) {
|
||||
directories.push(child);
|
||||
}
|
||||
}
|
||||
|
||||
return directories;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region whenDeleted()
|
||||
|
||||
/**
|
||||
* A `Promise` that resolves when the provided `path`
|
||||
* is deleted from disk.
|
||||
*/
|
||||
export function whenDeleted(path: string, intervalMs = 1000): Promise<void> {
|
||||
return new Promise<void>(resolve => {
|
||||
let running = false;
|
||||
const interval = setInterval(() => {
|
||||
if (!running) {
|
||||
running = true;
|
||||
fs.access(path, err => {
|
||||
running = false;
|
||||
|
||||
if (err) {
|
||||
clearInterval(interval);
|
||||
resolve(undefined);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, intervalMs);
|
||||
});
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Methods with symbolic links support
|
||||
|
||||
export namespace SymlinkSupport {
|
||||
|
||||
export interface IStats {
|
||||
|
||||
// The stats of the file. If the file is a symbolic
|
||||
// link, the stats will be of that target file and
|
||||
// not the link itself.
|
||||
// If the file is a symbolic link pointing to a non
|
||||
// existing file, the stat will be of the link and
|
||||
// the `dangling` flag will indicate this.
|
||||
stat: fs.Stats;
|
||||
|
||||
// Will be provided if the resource is a symbolic link
|
||||
// on disk. Use the `dangling` flag to find out if it
|
||||
// points to a resource that does not exist on disk.
|
||||
symbolicLink?: { dangling: boolean };
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the `fs.Stats` of the provided path. If the path is a
|
||||
* symbolic link, the `fs.Stats` will be from the target it points
|
||||
* to. If the target does not exist, `dangling: true` will be returned
|
||||
* as `symbolicLink` value.
|
||||
*/
|
||||
export async function stat(path: string): Promise<IStats> {
|
||||
|
||||
// First stat the link
|
||||
let lstats: fs.Stats | undefined;
|
||||
try {
|
||||
lstats = await fs.promises.lstat(path);
|
||||
|
||||
// Return early if the stat is not a symbolic link at all
|
||||
if (!lstats.isSymbolicLink()) {
|
||||
return { stat: lstats };
|
||||
}
|
||||
} catch (error) {
|
||||
/* ignore - use stat() instead */
|
||||
}
|
||||
|
||||
throw error;
|
||||
// If the stat is a symbolic link or failed to stat, use fs.stat()
|
||||
// which for symbolic links will stat the target they point to
|
||||
try {
|
||||
const stats = await fs.promises.stat(path);
|
||||
|
||||
return { stat: stats, symbolicLink: lstats?.isSymbolicLink() ? { dangling: false } : undefined };
|
||||
} catch (error) {
|
||||
|
||||
// If the link points to a non-existing file we still want
|
||||
// to return it as result while setting dangling: true flag
|
||||
if (error.code === 'ENOENT' && lstats) {
|
||||
return { stat: lstats, symbolicLink: { dangling: true } };
|
||||
}
|
||||
|
||||
// Windows: workaround a node.js bug where reparse points
|
||||
// are not supported (https://github.com/nodejs/node/issues/36790)
|
||||
if (isWindows && error.code === 'EACCES') {
|
||||
try {
|
||||
const stats = await fs.promises.stat(await fs.promises.readlink(path));
|
||||
|
||||
return { stat: stats, symbolicLink: { dangling: false } };
|
||||
} catch (error) {
|
||||
|
||||
// If the link points to a non-existing file we still want
|
||||
// to return it as result while setting dangling: true flag
|
||||
if (error.code === 'ENOENT' && lstats) {
|
||||
return { stat: lstats, symbolicLink: { dangling: true } };
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Figures out if the `path` exists and is a file with support
|
||||
* for symlinks.
|
||||
*
|
||||
* Note: this will return `false` for a symlink that exists on
|
||||
* disk but is dangling (pointing to a non-existing path).
|
||||
*
|
||||
* Use `exists` if you only care about the path existing on disk
|
||||
* or not without support for symbolic links.
|
||||
*/
|
||||
export async function existsFile(path: string): Promise<boolean> {
|
||||
try {
|
||||
const { stat, symbolicLink } = await SymlinkSupport.stat(path);
|
||||
|
||||
return stat.isFile() && symbolicLink?.dangling !== true;
|
||||
} catch (error) {
|
||||
// Ignore, path might not exist
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Figures out if the `path` exists and is a directory with support for
|
||||
* symlinks.
|
||||
*
|
||||
* Note: this will return `false` for a symlink that exists on
|
||||
* disk but is dangling (pointing to a non-existing path).
|
||||
*
|
||||
* Use `exists` if you only care about the path existing on disk
|
||||
* or not without support for symbolic links.
|
||||
*/
|
||||
export async function existsDirectory(path: string): Promise<boolean> {
|
||||
try {
|
||||
const { stat, symbolicLink } = await SymlinkSupport.stat(path);
|
||||
|
||||
return stat.isDirectory() && symbolicLink?.dangling !== true;
|
||||
} catch (error) {
|
||||
// Ignore, path might not exist
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function lstat(path: string): Promise<fs.Stats> {
|
||||
return promisify(fs.lstat)(path);
|
||||
}
|
||||
//#endregion
|
||||
|
||||
export function rename(oldPath: string, newPath: string): Promise<void> {
|
||||
return promisify(fs.rename)(oldPath, newPath);
|
||||
}
|
||||
|
||||
export function renameIgnoreError(oldPath: string, newPath: string): Promise<void> {
|
||||
return new Promise(resolve => fs.rename(oldPath, newPath, () => resolve()));
|
||||
}
|
||||
|
||||
export function readlink(path: string): Promise<string> {
|
||||
return promisify(fs.readlink)(path);
|
||||
}
|
||||
|
||||
export function unlink(path: string): Promise<void> {
|
||||
return promisify(fs.unlink)(path);
|
||||
}
|
||||
|
||||
export function symlink(target: string, path: string, type?: string): Promise<void> {
|
||||
return promisify(fs.symlink)(target, path, type);
|
||||
}
|
||||
|
||||
export function truncate(path: string, len: number): Promise<void> {
|
||||
return promisify(fs.truncate)(path, len);
|
||||
}
|
||||
|
||||
export function readFile(path: string): Promise<Buffer>;
|
||||
export function readFile(path: string, encoding: string): Promise<string>;
|
||||
export function readFile(path: string, encoding?: string): Promise<Buffer | string> {
|
||||
return promisify(fs.readFile)(path, encoding);
|
||||
}
|
||||
|
||||
export async function mkdirp(path: string, mode?: number): Promise<void> {
|
||||
return promisify(fs.mkdir)(path, { mode, recursive: true });
|
||||
}
|
||||
|
||||
// According to node.js docs (https://nodejs.org/docs/v6.5.0/api/fs.html#fs_fs_writefile_file_data_options_callback)
|
||||
// it is not safe to call writeFile() on the same path multiple times without waiting for the callback to return.
|
||||
// Therefor we use a Queue on the path that is given to us to sequentialize calls to the same path properly.
|
||||
const writeFilePathQueues: Map<string, Queue<void>> = new Map();
|
||||
//#region Write File
|
||||
|
||||
/**
|
||||
* Same as `fs.writeFile` but with an additional call to
|
||||
* `fs.fdatasync` after writing to ensure changes are
|
||||
* flushed to disk.
|
||||
*
|
||||
* In addition, multiple writes to the same path are queued.
|
||||
*/
|
||||
export function writeFile(path: string, data: string, options?: IWriteFileOptions): Promise<void>;
|
||||
export function writeFile(path: string, data: Buffer, options?: IWriteFileOptions): Promise<void>;
|
||||
export function writeFile(path: string, data: Uint8Array, options?: IWriteFileOptions): Promise<void>;
|
||||
@ -311,6 +382,11 @@ export function writeFile(path: string, data: string | Buffer | Uint8Array, opti
|
||||
});
|
||||
}
|
||||
|
||||
// According to node.js docs (https://nodejs.org/docs/v6.5.0/api/fs.html#fs_fs_writefile_file_data_options_callback)
|
||||
// it is not safe to call writeFile() on the same path multiple times without waiting for the callback to return.
|
||||
// Therefor we use a Queue on the path that is given to us to sequentialize calls to the same path properly.
|
||||
const writeFilePathQueues: Map<string, Queue<void>> = new Map();
|
||||
|
||||
function toQueueKey(path: string): string {
|
||||
let queueKey = path;
|
||||
if (isWindows || isMacintosh) {
|
||||
@ -388,6 +464,11 @@ function doWriteFileAndFlush(path: string, data: string | Buffer | Uint8Array, o
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `fs.writeFileSync` but with an additional call to
|
||||
* `fs.fdatasyncSync` after writing to ensure changes are
|
||||
* flushed to disk.
|
||||
*/
|
||||
export function writeFileSync(path: string, data: string | Buffer, options?: IWriteFileOptions): void {
|
||||
const ensuredOptions = ensureWriteOptions(options);
|
||||
|
||||
@ -426,87 +507,48 @@ function ensureWriteOptions(options?: IWriteFileOptions): IEnsuredWriteFileOptio
|
||||
};
|
||||
}
|
||||
|
||||
export async function readDirsInDir(dirPath: string): Promise<string[]> {
|
||||
const children = await readdir(dirPath);
|
||||
const directories: string[] = [];
|
||||
//#endregion
|
||||
|
||||
for (const child of children) {
|
||||
if (await dirExists(join(dirPath, child))) {
|
||||
directories.push(child);
|
||||
}
|
||||
}
|
||||
|
||||
return directories;
|
||||
}
|
||||
|
||||
export async function dirExists(path: string): Promise<boolean> {
|
||||
try {
|
||||
const { stat, symbolicLink } = await statLink(path);
|
||||
|
||||
return stat.isDirectory() && symbolicLink?.dangling !== true;
|
||||
} catch (error) {
|
||||
// Ignore, path might not exist
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export async function fileExists(path: string): Promise<boolean> {
|
||||
try {
|
||||
const { stat, symbolicLink } = await statLink(path);
|
||||
|
||||
return stat.isFile() && symbolicLink?.dangling !== true;
|
||||
} catch (error) {
|
||||
// Ignore, path might not exist
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function whenDeleted(path: string): Promise<void> {
|
||||
|
||||
// Complete when wait marker file is deleted
|
||||
return new Promise<void>(resolve => {
|
||||
let running = false;
|
||||
const interval = setInterval(() => {
|
||||
if (!running) {
|
||||
running = true;
|
||||
fs.exists(path, exists => {
|
||||
running = false;
|
||||
|
||||
if (!exists) {
|
||||
clearInterval(interval);
|
||||
resolve(undefined);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
//#region Move / Copy
|
||||
|
||||
/**
|
||||
* A drop-in replacement for `fs.rename` that:
|
||||
* - updates the `mtime` of the `source` after the operation
|
||||
* - allows to move across multiple disks
|
||||
*/
|
||||
export async function move(source: string, target: string): Promise<void> {
|
||||
if (source === target) {
|
||||
return;
|
||||
return; // simulate node.js behaviour here and do a no-op if paths match
|
||||
}
|
||||
|
||||
// We have been updating `mtime` for move operations for files since the
|
||||
// beginning for reasons that are no longer quite clear, but changing
|
||||
// this could be risky as well. As such, trying to reason about it:
|
||||
// It is very common as developer to have file watchers enabled that watch
|
||||
// the current workspace for changes. Updating the `mtime` might make it
|
||||
// easier for these watchers to recognize an actual change. Since changing
|
||||
// a source code file also updates the `mtime`, moving a file should do so
|
||||
// as well because conceptually it is a change of a similar category.
|
||||
async function updateMtime(path: string): Promise<void> {
|
||||
const stat = await lstat(path);
|
||||
if (stat.isDirectory() || stat.isSymbolicLink()) {
|
||||
return; // only for files
|
||||
}
|
||||
|
||||
const fd = await promisify(fs.open)(path, 'a');
|
||||
try {
|
||||
await promisify(fs.futimes)(fd, stat.atime, new Date());
|
||||
} catch (error) {
|
||||
//ignore
|
||||
}
|
||||
const stat = await fs.promises.lstat(path);
|
||||
if (stat.isDirectory() || stat.isSymbolicLink()) {
|
||||
return; // only for files
|
||||
}
|
||||
|
||||
return promisify(fs.close)(fd);
|
||||
const fh = await fs.promises.open(path, 'a');
|
||||
try {
|
||||
await fh.utimes(stat.atime, new Date());
|
||||
} finally {
|
||||
await fh.close();
|
||||
}
|
||||
} catch (error) {
|
||||
// Ignore any error
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await rename(source, target);
|
||||
await fs.promises.rename(source, target);
|
||||
await updateMtime(target);
|
||||
} catch (error) {
|
||||
|
||||
@ -519,7 +561,7 @@ export async function move(source: string, target: string): Promise<void> {
|
||||
// 2.) The user tries to rename a file/folder that ends with a dot. This is not
|
||||
// really possible to move then, at least on UNC devices.
|
||||
if (source.toLowerCase() !== target.toLowerCase() && error.code === 'EXDEV' || source.endsWith('.')) {
|
||||
await copy(source, target);
|
||||
await copy(source, target, { preserveSymlinks: false /* copying to another device */ });
|
||||
await rimraf(source, RimRafMode.MOVE);
|
||||
await updateMtime(target);
|
||||
} else {
|
||||
@ -528,74 +570,119 @@ export async function move(source: string, target: string): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
interface ICopyPayload {
|
||||
readonly root: { source: string, target: string };
|
||||
readonly options: { preserveSymlinks: boolean };
|
||||
readonly handledSourcePaths: Set<string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively copies all of `source` to `target`.
|
||||
*
|
||||
* The options `preserveSymlinks` configures how symbolic
|
||||
* links should be handled when encountered. Set to
|
||||
* `false` to not preserve them and `true` otherwise.
|
||||
*/
|
||||
export async function copy(source: string, target: string, options: { preserveSymlinks: boolean }): Promise<void> {
|
||||
return doCopy(source, target, { root: { source, target }, options, handledSourcePaths: new Set<string>() });
|
||||
}
|
||||
|
||||
// When copying a file or folder, we want to preserve the mode
|
||||
// it had and as such provide it when creating. However, modes
|
||||
// can go beyond what we expect (see link below), so we mask it.
|
||||
// (https://github.com/nodejs/node-v0.x-archive/issues/3045#issuecomment-4862588)
|
||||
//
|
||||
// The `copy` method is very old so we should probably revisit
|
||||
// it's implementation and check wether this mask is still needed.
|
||||
const COPY_MODE_MASK = 0o777;
|
||||
|
||||
export async function copy(source: string, target: string, handledSourcesIn?: { [path: string]: boolean }): Promise<void> {
|
||||
async function doCopy(source: string, target: string, payload: ICopyPayload): Promise<void> {
|
||||
|
||||
// Keep track of paths already copied to prevent
|
||||
// cycles from symbolic links to cause issues
|
||||
const handledSources = handledSourcesIn ?? Object.create(null);
|
||||
if (handledSources[source]) {
|
||||
if (payload.handledSourcePaths.has(source)) {
|
||||
return;
|
||||
} else {
|
||||
handledSources[source] = true;
|
||||
payload.handledSourcePaths.add(source);
|
||||
}
|
||||
|
||||
const { stat, symbolicLink } = await statLink(source);
|
||||
if (symbolicLink?.dangling) {
|
||||
return; // skip over dangling symbolic links (https://github.com/microsoft/vscode/issues/111621)
|
||||
const { stat, symbolicLink } = await SymlinkSupport.stat(source);
|
||||
|
||||
// Symlink
|
||||
if (symbolicLink) {
|
||||
if (symbolicLink.dangling) {
|
||||
return; // do not copy dangling symbolic links (https://github.com/microsoft/vscode/issues/111621)
|
||||
}
|
||||
|
||||
// Try to re-create the symlink unless `preserveSymlinks: false`
|
||||
if (payload.options.preserveSymlinks) {
|
||||
try {
|
||||
return await doCopySymlink(source, target, payload);
|
||||
} catch (error) {
|
||||
// in any case of an error fallback to normal copy via dereferencing
|
||||
console.warn('[node.js fs] copy of symlink failed: ', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!stat.isDirectory()) {
|
||||
// Folder
|
||||
if (stat.isDirectory()) {
|
||||
return doCopyDirectory(source, target, stat.mode & COPY_MODE_MASK, payload);
|
||||
}
|
||||
|
||||
// File or file-like
|
||||
else {
|
||||
return doCopyFile(source, target, stat.mode & COPY_MODE_MASK);
|
||||
}
|
||||
}
|
||||
|
||||
async function doCopyDirectory(source: string, target: string, mode: number, payload: ICopyPayload): Promise<void> {
|
||||
|
||||
// Create folder
|
||||
await mkdirp(target, stat.mode & COPY_MODE_MASK);
|
||||
await fs.promises.mkdir(target, { recursive: true, mode });
|
||||
|
||||
// Copy each file recursively
|
||||
const files = await readdir(source);
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
await copy(join(source, file), join(target, file), handledSources);
|
||||
for (const file of files) {
|
||||
await doCopy(join(source, file), join(target, file), payload);
|
||||
}
|
||||
}
|
||||
|
||||
async function doCopyFile(source: string, target: string, mode: number): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = fs.createReadStream(source);
|
||||
const writer = fs.createWriteStream(target, { mode });
|
||||
|
||||
let finished = false;
|
||||
const finish = (error?: Error) => {
|
||||
if (!finished) {
|
||||
finished = true;
|
||||
// Copy file
|
||||
await fs.promises.copyFile(source, target);
|
||||
|
||||
// in error cases, pass to callback
|
||||
if (error) {
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
// we need to explicitly chmod because of https://github.com/nodejs/node/issues/1104
|
||||
fs.chmod(target, mode, error => error ? reject(error) : resolve());
|
||||
}
|
||||
};
|
||||
|
||||
// handle errors properly
|
||||
reader.once('error', error => finish(error));
|
||||
writer.once('error', error => finish(error));
|
||||
|
||||
// we are done (underlying fd has been closed)
|
||||
writer.once('close', () => finish());
|
||||
|
||||
// start piping
|
||||
reader.pipe(writer);
|
||||
});
|
||||
// restore mode (https://github.com/nodejs/node/issues/1104)
|
||||
await fs.promises.chmod(target, mode);
|
||||
}
|
||||
|
||||
async function doCopySymlink(source: string, target: string, payload: ICopyPayload): Promise<void> {
|
||||
|
||||
// Figure out link target
|
||||
let linkTarget = await fs.promises.readlink(source);
|
||||
|
||||
// Special case: the symlink points to a target that is
|
||||
// actually within the path that is being copied. In that
|
||||
// case we want the symlink to point to the target and
|
||||
// not the source
|
||||
if (isEqualOrParent(linkTarget, payload.root.source, !isLinux)) {
|
||||
linkTarget = join(payload.root.target, linkTarget.substr(payload.root.source.length + 1));
|
||||
}
|
||||
|
||||
// Create symlink
|
||||
await fs.promises.symlink(linkTarget, target);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Async FS Methods
|
||||
|
||||
export async function exists(path: string): Promise<boolean> {
|
||||
try {
|
||||
await fs.promises.access(path);
|
||||
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
@ -8,18 +8,72 @@ import * as os from 'os';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import { env } from 'vs/base/common/process';
|
||||
|
||||
const WindowsPowerShell64BitLabel = 'Windows PowerShell';
|
||||
const WindowsPowerShell32BitLabel = 'Windows PowerShell (x86)';
|
||||
|
||||
// This is required, since parseInt("7-preview") will return 7.
|
||||
const IntRegex: RegExp = /^\d+$/;
|
||||
|
||||
const PwshMsixRegex: RegExp = /^Microsoft.PowerShell_.*/;
|
||||
const PwshPreviewMsixRegex: RegExp = /^Microsoft.PowerShellPreview_.*/;
|
||||
|
||||
// The platform details descriptor for the platform we're on
|
||||
const isProcess64Bit: boolean = process.arch === 'x64';
|
||||
const isOS64Bit: boolean = isProcess64Bit || os.arch() === 'x64';
|
||||
const enum Arch {
|
||||
x64,
|
||||
x86,
|
||||
ARM
|
||||
}
|
||||
|
||||
let processArch: Arch;
|
||||
switch (process.arch) {
|
||||
case 'ia32':
|
||||
case 'x32':
|
||||
processArch = Arch.x86;
|
||||
break;
|
||||
case 'arm':
|
||||
case 'arm64':
|
||||
processArch = Arch.ARM;
|
||||
break;
|
||||
default:
|
||||
processArch = Arch.x64;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
Currently, here are the values for these environment variables on their respective archs:
|
||||
|
||||
On x86 process on x86:
|
||||
PROCESSOR_ARCHITECTURE is X86
|
||||
PROCESSOR_ARCHITEW6432 is undefined
|
||||
|
||||
On x86 process on x64:
|
||||
PROCESSOR_ARCHITECTURE is X86
|
||||
PROCESSOR_ARCHITEW6432 is AMD64
|
||||
|
||||
On x64 process on x64:
|
||||
PROCESSOR_ARCHITECTURE is AMD64
|
||||
PROCESSOR_ARCHITEW6432 is undefined
|
||||
|
||||
On ARM process on ARM:
|
||||
PROCESSOR_ARCHITECTURE is ARM64
|
||||
PROCESSOR_ARCHITEW6432 is undefined
|
||||
|
||||
On x86 process on ARM:
|
||||
PROCESSOR_ARCHITECTURE is X86
|
||||
PROCESSOR_ARCHITEW6432 is ARM64
|
||||
|
||||
On x64 process on ARM:
|
||||
PROCESSOR_ARCHITECTURE is ARM64
|
||||
PROCESSOR_ARCHITEW6432 is undefined
|
||||
*/
|
||||
let osArch: Arch;
|
||||
if (process.env['PROCESSOR_ARCHITEW6432']) {
|
||||
osArch = process.env['PROCESSOR_ARCHITEW6432'] === 'ARM64'
|
||||
? Arch.ARM
|
||||
: Arch.x64;
|
||||
} else if (process.env['PROCESSOR_ARCHITECTURE'] === 'ARM64') {
|
||||
osArch = Arch.ARM;
|
||||
} else if (process.env['PROCESSOR_ARCHITECTURE'] === 'X86') {
|
||||
osArch = Arch.x86;
|
||||
} else {
|
||||
osArch = Arch.x64;
|
||||
}
|
||||
|
||||
export interface IPowerShellExeDetails {
|
||||
readonly displayName: string;
|
||||
@ -38,7 +92,7 @@ class PossiblePowerShellExe implements IPossiblePowerShellExe {
|
||||
|
||||
public async exists(): Promise<boolean> {
|
||||
if (this.knownToExist === undefined) {
|
||||
this.knownToExist = await pfs.fileExists(this.exePath);
|
||||
this.knownToExist = await pfs.SymlinkSupport.existsFile(this.exePath);
|
||||
}
|
||||
return this.knownToExist;
|
||||
}
|
||||
@ -53,12 +107,12 @@ function getProgramFilesPath(
|
||||
}
|
||||
|
||||
// We might be a 64-bit process looking for 32-bit program files
|
||||
if (isProcess64Bit) {
|
||||
if (processArch === Arch.x64) {
|
||||
return env['ProgramFiles(x86)'] || null;
|
||||
}
|
||||
|
||||
// We might be a 32-bit process looking for 64-bit program files
|
||||
if (isOS64Bit) {
|
||||
if (osArch === Arch.x64) {
|
||||
return env.ProgramW6432 || null;
|
||||
}
|
||||
|
||||
@ -66,28 +120,6 @@ function getProgramFilesPath(
|
||||
return null;
|
||||
}
|
||||
|
||||
function getSystem32Path({ useAlternateBitness = false }: { useAlternateBitness?: boolean } = {}): string {
|
||||
const windir: string = env.windir!;
|
||||
|
||||
if (!useAlternateBitness) {
|
||||
// Just use the native system bitness
|
||||
return path.join(windir, 'System32');
|
||||
}
|
||||
|
||||
// We might be a 64-bit process looking for 32-bit system32
|
||||
if (isProcess64Bit) {
|
||||
return path.join(windir, 'SysWOW64');
|
||||
}
|
||||
|
||||
// We might be a 32-bit process looking for 64-bit system32
|
||||
if (isOS64Bit) {
|
||||
return path.join(windir, 'Sysnative');
|
||||
}
|
||||
|
||||
// We're on a 32-bit Windows, so no alternate bitness
|
||||
return path.join(windir, 'System32');
|
||||
}
|
||||
|
||||
async function findPSCoreWindowsInstallation(
|
||||
{ useAlternateBitness = false, findPreview = false }:
|
||||
{ useAlternateBitness?: boolean; findPreview?: boolean } = {}): Promise<IPossiblePowerShellExe | null> {
|
||||
@ -100,7 +132,7 @@ async function findPSCoreWindowsInstallation(
|
||||
const powerShellInstallBaseDir = path.join(programFilesPath, 'PowerShell');
|
||||
|
||||
// Ensure the base directory exists
|
||||
if (!await pfs.dirExists(powerShellInstallBaseDir)) {
|
||||
if (!await pfs.SymlinkSupport.existsDirectory(powerShellInstallBaseDir)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -142,7 +174,7 @@ async function findPSCoreWindowsInstallation(
|
||||
|
||||
// Now look for the file
|
||||
const exePath = path.join(powerShellInstallBaseDir, item, 'pwsh.exe');
|
||||
if (!await pfs.fileExists(exePath)) {
|
||||
if (!await pfs.SymlinkSupport.existsFile(exePath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -169,7 +201,7 @@ async function findPSCoreMsix({ findPreview }: { findPreview?: boolean } = {}):
|
||||
// Find the base directory for MSIX application exe shortcuts
|
||||
const msixAppDir = path.join(env.LOCALAPPDATA, 'Microsoft', 'WindowsApps');
|
||||
|
||||
if (!await pfs.dirExists(msixAppDir)) {
|
||||
if (!await pfs.SymlinkSupport.existsDirectory(msixAppDir)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -179,11 +211,15 @@ async function findPSCoreMsix({ findPreview }: { findPreview?: boolean } = {}):
|
||||
: { pwshMsixDirRegex: PwshMsixRegex, pwshMsixName: 'PowerShell (Store)' };
|
||||
|
||||
// We should find only one such application, so return on the first one
|
||||
for (const subdir of await pfs.readdir(msixAppDir)) {
|
||||
if (pwshMsixDirRegex.test(subdir)) {
|
||||
const pwshMsixPath = path.join(msixAppDir, subdir, 'pwsh.exe');
|
||||
return new PossiblePowerShellExe(pwshMsixPath, pwshMsixName);
|
||||
try {
|
||||
for (const subdir of await pfs.readdir(msixAppDir)) {
|
||||
if (pwshMsixDirRegex.test(subdir)) {
|
||||
const pwshMsixPath = path.join(msixAppDir, subdir, 'pwsh.exe');
|
||||
return new PossiblePowerShellExe(pwshMsixPath, pwshMsixName);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(`Unable to read MSIX directory (${msixAppDir}) because of the following error: ${err}`);
|
||||
}
|
||||
|
||||
// If we find nothing, return null
|
||||
@ -196,33 +232,13 @@ function findPSCoreDotnetGlobalTool(): IPossiblePowerShellExe {
|
||||
return new PossiblePowerShellExe(dotnetGlobalToolExePath, '.NET Core PowerShell Global Tool');
|
||||
}
|
||||
|
||||
function findWinPS({ useAlternateBitness = false }: { useAlternateBitness?: boolean } = {}): IPossiblePowerShellExe | null {
|
||||
function findWinPS(): IPossiblePowerShellExe | null {
|
||||
const winPSPath = path.join(
|
||||
env.windir!,
|
||||
processArch === Arch.x86 && osArch !== Arch.x86 ? 'SysNative' : 'System32',
|
||||
'WindowsPowerShell', 'v1.0', 'powershell.exe');
|
||||
|
||||
// x86 and ARM only have one WinPS on them
|
||||
if (!isOS64Bit && useAlternateBitness) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const systemFolderPath = getSystem32Path({ useAlternateBitness });
|
||||
|
||||
const winPSPath = path.join(systemFolderPath, 'WindowsPowerShell', 'v1.0', 'powershell.exe');
|
||||
|
||||
let displayName: string;
|
||||
if (isProcess64Bit) {
|
||||
displayName = useAlternateBitness
|
||||
? WindowsPowerShell32BitLabel
|
||||
: WindowsPowerShell64BitLabel;
|
||||
} else if (isOS64Bit) {
|
||||
displayName = useAlternateBitness
|
||||
? WindowsPowerShell64BitLabel
|
||||
: WindowsPowerShell32BitLabel;
|
||||
} else {
|
||||
// NOTE: ARM Windows devices also have Windows PowerShell x86 on them. There is no
|
||||
// "ARM Windows PowerShell".
|
||||
displayName = WindowsPowerShell32BitLabel;
|
||||
}
|
||||
|
||||
return new PossiblePowerShellExe(winPSPath, displayName, true);
|
||||
return new PossiblePowerShellExe(winPSPath, 'Windows PowerShell', true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -276,18 +292,10 @@ async function* enumerateDefaultPowerShellInstallations(): AsyncIterable<IPossib
|
||||
}
|
||||
|
||||
// Finally, get Windows PowerShell
|
||||
|
||||
// Get the natural Windows PowerShell for the process bitness
|
||||
pwshExe = findWinPS();
|
||||
if (pwshExe) {
|
||||
yield pwshExe;
|
||||
}
|
||||
|
||||
// Get the alternate bitness Windows PowerShell
|
||||
pwshExe = findWinPS({ useAlternateBitness: true });
|
||||
if (pwshExe) {
|
||||
yield pwshExe;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
import * as path from 'vs/base/common/path';
|
||||
import * as fs from 'fs';
|
||||
import { promisify } from 'util';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import * as cp from 'child_process';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as Types from 'vs/base/common/types';
|
||||
@ -456,8 +456,8 @@ export namespace win32 {
|
||||
}
|
||||
|
||||
async function fileExists(path: string): Promise<boolean> {
|
||||
if (await promisify(fs.exists)(path)) {
|
||||
return !((await promisify(fs.stat)(path)).isDirectory());
|
||||
if (await pfs.exists(path)) {
|
||||
return !((await fs.promises.stat(path)).isDirectory());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -3,9 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { FileAccess } from 'vs/base/common/network';
|
||||
|
||||
const pathsPath = FileAccess.asFileUri('paths', require).fsPath;
|
||||
const paths = require.__$__nodeRequire<{ getDefaultUserDataPath(): string }>(pathsPath);
|
||||
|
||||
export const getDefaultUserDataPath = paths.getDefaultUserDataPath;
|
||||
/**
|
||||
* Returns the user data path to use.
|
||||
*/
|
||||
export function getDefaultUserDataPath(): string;
|
72
lib/vscode/src/vs/base/node/userDataPath.js
Normal file
72
lib/vscode/src/vs/base/node/userDataPath.js
Normal file
@ -0,0 +1,72 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/// <reference path="../../../typings/require.d.ts" />
|
||||
|
||||
//@ts-check
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @param {typeof import('path')} path
|
||||
* @param {typeof import('os')} os
|
||||
* @param {string} productName
|
||||
*/
|
||||
function factory(path, os, productName) {
|
||||
|
||||
function getDefaultUserDataPath() {
|
||||
|
||||
// Support global VSCODE_APPDATA environment variable
|
||||
let appDataPath = process.env['VSCODE_APPDATA'];
|
||||
|
||||
// Otherwise check per platform
|
||||
if (!appDataPath) {
|
||||
switch (process.platform) {
|
||||
case 'win32':
|
||||
appDataPath = process.env['APPDATA'];
|
||||
if (!appDataPath) {
|
||||
const userProfile = process.env['USERPROFILE'];
|
||||
if (typeof userProfile !== 'string') {
|
||||
throw new Error('Windows: Unexpected undefined %USERPROFILE% environment variable');
|
||||
}
|
||||
appDataPath = path.join(userProfile, 'AppData', 'Roaming');
|
||||
}
|
||||
break;
|
||||
case 'darwin':
|
||||
appDataPath = path.join(os.homedir(), 'Library', 'Application Support');
|
||||
break;
|
||||
case 'linux':
|
||||
appDataPath = process.env['XDG_CONFIG_HOME'] || path.join(os.homedir(), '.config');
|
||||
break;
|
||||
default:
|
||||
throw new Error('Platform not supported');
|
||||
}
|
||||
}
|
||||
|
||||
return path.join(appDataPath, productName);
|
||||
}
|
||||
|
||||
return {
|
||||
getDefaultUserDataPath
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof define === 'function') {
|
||||
define(['require', 'path', 'os', 'vs/base/common/network', 'vs/base/common/resources'], function (require, /** @type {typeof import('path')} */ path, /** @type {typeof import('os')} */ os, /** @type {typeof import('../common/network')} */ network, /** @type {typeof import("../common/resources")} */ resources) {
|
||||
const rootPath = resources.dirname(network.FileAccess.asFileUri('', require));
|
||||
const pkg = require.__$__nodeRequire(resources.joinPath(rootPath, 'package.json').fsPath);
|
||||
|
||||
return factory(path, os, pkg.name);
|
||||
}); // amd
|
||||
} else if (typeof module === 'object' && typeof module.exports === 'object') {
|
||||
const pkg = require('../../../../package.json');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
|
||||
module.exports = factory(path, os, pkg.name); // commonjs
|
||||
} else {
|
||||
throw new Error('Unknown context');
|
||||
}
|
||||
}());
|
@ -5,10 +5,10 @@
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import { createWriteStream, WriteStream } from 'fs';
|
||||
import { promises, createWriteStream, WriteStream } from 'fs';
|
||||
import { Readable } from 'stream';
|
||||
import { Sequencer, createCancelablePromise } from 'vs/base/common/async';
|
||||
import { mkdirp, rimraf } from 'vs/base/node/pfs';
|
||||
import { rimraf } from 'vs/base/node/pfs';
|
||||
import { open as _openZip, Entry, ZipFile } from 'yauzl';
|
||||
import * as yazl from 'yazl';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
@ -86,7 +86,7 @@ function extractEntry(stream: Readable, fileName: string, mode: number, targetPa
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.resolve(mkdirp(targetDirName)).then(() => new Promise<void>((c, e) => {
|
||||
return Promise.resolve(promises.mkdir(targetDirName, { recursive: true })).then(() => new Promise<void>((c, e) => {
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
@ -149,7 +149,7 @@ function extractZip(zipfile: ZipFile, targetPath: string, options: IOptions, tok
|
||||
// directory file names end with '/'
|
||||
if (/\/$/.test(fileName)) {
|
||||
const targetFileName = path.join(targetPath, fileName);
|
||||
last = createCancelablePromise(token => mkdirp(targetFileName).then(() => readNextEntry(token)).then(undefined, e));
|
||||
last = createCancelablePromise(token => promises.mkdir(targetFileName, { recursive: true }).then(() => readNextEntry(token)).then(undefined, e));
|
||||
return;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user