Merge from vscode 1b314ab317fbff7d799b21754326b7d849889ceb

This commit is contained in:
ADS Merger
2020-07-15 23:51:18 +00:00
parent aae013d498
commit 9d3f12d0b7
554 changed files with 15159 additions and 8223 deletions

View File

@@ -20,32 +20,35 @@ const ansiColors = require('ansi-colors');
const extensions = require('../../build/lib/extensions');
const APP_ROOT = path.join(__dirname, '..', '..');
const EXTENSIONS_ROOT = path.join(APP_ROOT, 'extensions');
const BUILTIN_EXTENSIONS_ROOT = path.join(APP_ROOT, 'extensions');
const BUILTIN_MARKETPLACE_EXTENSIONS_ROOT = path.join(APP_ROOT, '.build', 'builtInExtensions');
const WEB_MAIN = path.join(APP_ROOT, 'src', 'vs', 'code', 'browser', 'workbench', 'workbench-dev.html');
const args = minimist(process.argv, {
boolean: [
'watch',
'no-launch',
'help'
'help',
'verbose'
],
string: [
'scheme',
'host',
'port',
'local_port'
'local_port',
'extension'
],
});
if (args.help) {
console.log(
'yarn web [options]\n' +
' --watch Watch extensions that require browser specific builds\n' +
' --no-launch Do not open VSCode web in the browser\n' +
' --scheme Protocol (https or http)\n' +
' --host Remote host\n' +
' --port Remote/Local port\n' +
' --local_port Local port override\n' +
' --no-launch Do not open VSCode web in the browser\n' +
' --scheme Protocol (https or http)\n' +
' --host Remote host\n' +
' --port Remote/Local port\n' +
' --local_port Local port override\n' +
' --extension Path of an extension to include\n' +
' --verbose Print out more information\n' +
' --help\n' +
'[Example]\n' +
' yarn web --scheme https --host example.com --port 8080 --local_port 30000'
@@ -61,76 +64,124 @@ const AUTHORITY = process.env.VSCODE_AUTHORITY || `${HOST}:${PORT}`;
const exists = (path) => util.promisify(fs.exists)(path);
const readFile = (path) => util.promisify(fs.readFile)(path);
const readdir = (path) => util.promisify(fs.readdir)(path);
const readdirWithFileTypes = (path) => util.promisify(fs.readdir)(path, { withFileTypes: true });
let unbuiltExensions = [];
async function getBuiltInExtensionInfos() {
const extensions = [];
/** @type {Object.<string, string>} */
const locations = {};
async function initialize() {
const builtinExtensions = [];
const children = await util.promisify(fs.readdir)(EXTENSIONS_ROOT, { withFileTypes: true });
const folders = children.filter(c => !c.isFile());
await Promise.all(folders.map(async folder => {
const folderName = folder.name;
const extensionPath = path.join(EXTENSIONS_ROOT, folderName);
let children = [];
try {
children = await util.promisify(fs.readdir)(extensionPath);
} catch (error) {
console.log(error);
return;
}
const readme = children.filter(child => /^readme(\.txt|\.md|)$/i.test(child))[0];
const readmePath = readme ? path.join(extensionPath, readme) : undefined;
const changelog = children.filter(child => /^changelog(\.txt|\.md|)$/i.test(child))[0];
const changelogPath = changelog ? path.join(extensionPath, changelog) : undefined;
const packageJSONPath = path.join(EXTENSIONS_ROOT, folderName, 'package.json');
if (await exists(packageJSONPath)) {
try {
let packageJSON = JSON.parse((await readFile(packageJSONPath)).toString());
if (packageJSON.main && !packageJSON.browser) {
return; // unsupported
}
if (packageJSON.browser) {
packageJSON.main = packageJSON.browser;
let mainFilePath = path.join(EXTENSIONS_ROOT, folderName, packageJSON.browser);
if (path.extname(mainFilePath) !== '.js') {
mainFilePath += '.js';
}
if (!await exists(mainFilePath)) {
unbuiltExensions.push(path.relative(EXTENSIONS_ROOT, mainFilePath));
for (const extensionsRoot of [BUILTIN_EXTENSIONS_ROOT, BUILTIN_MARKETPLACE_EXTENSIONS_ROOT]) {
if (await exists(extensionsRoot)) {
const children = await readdirWithFileTypes(extensionsRoot);
await Promise.all(children.map(async child => {
if (child.isDirectory()) {
const extensionPath = path.join(extensionsRoot, child.name);
const info = await getBuiltInExtensionInfo(extensionPath);
if (info) {
extensions.push(info);
locations[path.basename(extensionPath)] = extensionPath;
}
}
packageJSON.extensionKind = ['web']; // enable for Web
const packageNLSPath = path.join(folderName, 'package.nls.json');
const packageNLSExists = await exists(path.join(EXTENSIONS_ROOT, packageNLSPath));
if (packageNLSExists) {
packageJSON = extensions.translatePackageJSON(packageJSON, path.join(EXTENSIONS_ROOT, packageNLSPath)); // temporary, until fixed in core
}
builtinExtensions.push({
extensionPath: folderName,
packageJSON,
packageNLSPath: packageNLSExists ? packageNLSPath : undefined,
readmePath,
changelogPath
});
} catch (e) {
console.log(e);
}
}));
}
}));
if (unbuiltExensions.length) {
fancyLog(`${ansiColors.yellow('Warning')}: Make sure to run ${ansiColors.cyan('yarn gulp watch-web')}\nCould not find the following browser main files: \n${unbuiltExensions.join('\n')}`);
}
return builtinExtensions;
return { extensions, locations };
}
const builtinExtensionsPromise = initialize();
async function getBuiltInExtensionInfo(extensionPath) {
const packageJSON = await getExtensionPackageJSON(extensionPath);
if (!packageJSON) {
return undefined;
}
const builtInExtensionPath = path.basename(extensionPath);
let children = [];
try {
children = await readdir(extensionPath);
} catch (error) {
console.log(`Can not read extension folder ${extensionPath}: ${error}`);
return;
}
const readme = children.find(child => /^readme(\.txt|\.md|)$/i.test(child));
const changelog = children.find(child => /^changelog(\.txt|\.md|)$/i.test(child));
const packageJSONNLS = children.find(child => /^package.nls.json$/i.test(child));
return {
extensionPath: builtInExtensionPath,
packageJSON,
packageNLSPath: packageJSONNLS ? `${builtInExtensionPath}/${packageJSONNLS}` : undefined,
readmePath: readme ? `${builtInExtensionPath}/${readme}` : undefined,
changelogPath: changelog ? `${builtInExtensionPath}/${changelog}` : undefined
};
}
async function getDefaultExtensionInfos() {
const extensions = [];
/** @type {Object.<string, string>} */
const locations = {};
let extensionArg = args['extension'];
if (!extensionArg) {
return { extensions, locations }
}
const extensionPaths = Array.isArray(extensionArg) ? extensionArg : [extensionArg];
await Promise.all(extensionPaths.map(async extensionPath => {
extensionPath = path.resolve(process.cwd(), extensionPath);
const packageJSON = await getExtensionPackageJSON(extensionPath);
if (packageJSON) {
const extensionId = `${packageJSON.publisher}.${packageJSON.name}`;
extensions.push({
packageJSON,
extensionLocation: { scheme: SCHEME, authority: AUTHORITY, path: `/extension/${extensionId}` }
});
locations[extensionId] = extensionPath;
}
}));
return { extensions, locations };
}
async function getExtensionPackageJSON(extensionPath) {
const packageJSONPath = path.join(extensionPath, 'package.json');
if (await exists(packageJSONPath)) {
try {
let packageJSON = JSON.parse((await readFile(packageJSONPath)).toString());
if (packageJSON.main && !packageJSON.browser) {
return; // unsupported
}
if (packageJSON.browser) {
packageJSON.main = packageJSON.browser;
let mainFilePath = path.join(extensionPath, packageJSON.browser);
if (path.extname(mainFilePath) !== '.js') {
mainFilePath += '.js';
}
if (!await exists(mainFilePath)) {
fancyLog(`${ansiColors.yellow('Warning')}: Could not find ${mainFilePath}. Use ${ansiColors.cyan('yarn gulp watch-web')} to build the built-in extensions.`);
}
}
packageJSON.extensionKind = ['web']; // enable for Web
const packageNLSPath = path.join(extensionPath, 'package.nls.json');
const packageNLSExists = await exists(packageNLSPath);
if (packageNLSExists) {
packageJSON = extensions.translatePackageJSON(packageJSON, packageNLSPath); // temporary, until fixed in core
}
return packageJSON;
} catch (e) {
console.log(e);
}
}
return undefined;
}
const builtInExtensionsPromise = getBuiltInExtensionInfos();
const defaultExtensionsPromise = getDefaultExtensionInfos();
const mapCallbackUriToRequestId = new Map();
@@ -158,9 +209,13 @@ const server = http.createServer((req, res) => {
// static requests
return handleStatic(req, res, parsedUrl);
}
if (/^\/static-extension\//.test(pathname)) {
// static extension requests
return handleStaticExtension(req, res, parsedUrl);
if (/^\/extension\//.test(pathname)) {
// default extension requests
return handleExtension(req, res, parsedUrl);
}
if (/^\/builtin-extension\//.test(pathname)) {
// built-in extension requests
return handleBuiltInExtension(req, res, parsedUrl);
}
if (pathname === '/') {
// main web
@@ -211,13 +266,28 @@ function handleStatic(req, res, parsedUrl) {
* @param {import('http').ServerResponse} res
* @param {import('url').UrlWithParsedQuery} parsedUrl
*/
function handleStaticExtension(req, res, parsedUrl) {
// Strip `/static-extension/` from the path
const relativeFilePath = path.normalize(decodeURIComponent(parsedUrl.pathname.substr('/static-extension/'.length)));
const filePath = path.join(EXTENSIONS_ROOT, relativeFilePath);
async function handleExtension(req, res, parsedUrl) {
// Strip `/extension/` from the path
const relativePath = decodeURIComponent(parsedUrl.pathname.substr('/extension/'.length));
const filePath = getExtensionFilePath(relativePath, (await defaultExtensionsPromise).locations);
if (!filePath) {
return serveError(req, res, 400, `Bad request.`);
}
return serveFile(req, res, filePath);
}
/**
* @param {import('http').IncomingMessage} req
* @param {import('http').ServerResponse} res
* @param {import('url').UrlWithParsedQuery} parsedUrl
*/
async function handleBuiltInExtension(req, res, parsedUrl) {
// Strip `/builtin-extension/` from the path
const relativePath = decodeURIComponent(parsedUrl.pathname.substr('/builtin-extension/'.length));
const filePath = getExtensionFilePath(relativePath, (await builtInExtensionsPromise).locations);
if (!filePath) {
return serveError(req, res, 400, `Bad request.`);
}
return serveFile(req, res, filePath);
}
@@ -232,34 +302,44 @@ async function handleRoot(req, res) {
if (match) {
const qs = new URLSearchParams(match[1]);
let ghPath = qs.get('gh');
if (ghPath) {
if (!ghPath.startsWith('/')) {
ghPath = '/' + ghPath;
let gh = qs.get('gh');
if (gh) {
if (gh.startsWith('/')) {
gh = gh.substr(1);
}
folderUri = { scheme: 'github', authority: 'HEAD', path: ghPath };
} else {
let csPath = qs.get('cs');
if (csPath) {
if (!csPath.startsWith('/')) {
csPath = '/' + csPath;
const [owner, repo, ...branch] = gh.split('/', 3);
folderUri = { scheme: 'github', authority: branch.join('/') || 'HEAD', path: `/${owner}/${repo}` };
} else {
let cs = qs.get('cs');
if (cs) {
if (cs.startsWith('/')) {
cs = cs.substr(1);
}
folderUri = { scheme: 'codespace', authority: 'HEAD', path: csPath };
const [owner, repo, ...branch] = cs.split('/');
folderUri = { scheme: 'codespace', authority: branch.join('/') || 'HEAD', path: `/${owner}/${repo}` };
}
}
}
const builtinExtensions = await builtinExtensionsPromise;
const { extensions: builtInExtensions } = await builtInExtensionsPromise;
const { extensions: staticExtensions } = await defaultExtensionsPromise;
if (args.verbose) {
fancyLog(`${ansiColors.magenta('BuiltIn extensions')}: ${builtInExtensions.map(e => path.basename(e.extensionPath)).join(', ')}`);
fancyLog(`${ansiColors.magenta('Additional extensions')}: ${staticExtensions.map(e => path.basename(e.extensionLocation.path)).join(', ') || 'None'}`);
}
const webConfigJSON = escapeAttribute(JSON.stringify({
folderUri: folderUri,
builtinExtensionsServiceUrl: `${SCHEME}://${AUTHORITY}/static-extension`
staticExtensions,
builtinExtensionsServiceUrl: `${SCHEME}://${AUTHORITY}/builtin-extension`
}));
const data = (await util.promisify(fs.readFile)(WEB_MAIN)).toString()
const data = (await readFile(WEB_MAIN)).toString()
.replace('{{WORKBENCH_WEB_CONFIGURATION}}', () => webConfigJSON) // use a replace function to avoid that regexp replace patterns ($&, $0, ...) are applied
.replace('{{WORKBENCH_BUILTIN_EXTENSIONS}}', () => escapeAttribute(JSON.stringify(builtinExtensions)))
.replace('{{WORKBENCH_BUILTIN_EXTENSIONS}}', () => escapeAttribute(JSON.stringify(builtInExtensions)))
.replace('{{WEBVIEW_ENDPOINT}}', '')
.replace('{{REMOTE_USER_DATA_URI}}', '');
@@ -368,6 +448,25 @@ function escapeAttribute(value) {
return value.replace(/"/g, '&quot;');
}
/**
* @param {string} relativePath
* @param {Object.<string, string>} locations
* @returns {string | undefined}
*/
function getExtensionFilePath(relativePath, locations) {
const firstSlash = relativePath.indexOf('/');
if (firstSlash === -1) {
return undefined;
}
const extensionId = relativePath.substr(0, firstSlash);
const extensionPath = locations[extensionId];
if (!extensionPath) {
return undefined;
}
return path.join(extensionPath, relativePath.substr(firstSlash + 1));
}
/**
* @param {import('http').IncomingMessage} req
* @param {import('http').ServerResponse} res
@@ -419,10 +518,6 @@ async function serveFile(req, res, filePath, responseHeaders = Object.create(nul
// Sanity checks
filePath = path.normalize(filePath); // ensure no "." and ".."
if (filePath.indexOf(`${APP_ROOT}${path.sep}`) !== 0) {
// invalid location outside of APP_ROOT
return serveError(req, res, 400, `Bad request.`);
}
const stat = await util.promisify(fs.stat)(filePath);