code-web: read built-in marketplace extensions

This commit is contained in:
Martin Aeschlimann 2020-07-15 16:34:47 +02:00
parent 135e5d9323
commit d706c7089c

View file

@ -20,13 +20,15 @@ const ansiColors = require('ansi-colors');
const extensions = require('../../build/lib/extensions'); const extensions = require('../../build/lib/extensions');
const APP_ROOT = path.join(__dirname, '..', '..'); 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 WEB_MAIN = path.join(APP_ROOT, 'src', 'vs', 'code', 'browser', 'workbench', 'workbench-dev.html');
const args = minimist(process.argv, { const args = minimist(process.argv, {
boolean: [ boolean: [
'no-launch', 'no-launch',
'help' 'help',
'verbose'
], ],
string: [ string: [
'scheme', 'scheme',
@ -46,6 +48,7 @@ if (args.help) {
' --port Remote/Local port\n' + ' --port Remote/Local port\n' +
' --local_port Local port override\n' + ' --local_port Local port override\n' +
' --extension Path of an extension to include\n' + ' --extension Path of an extension to include\n' +
' --verbose Print out more information\n' +
' --help\n' + ' --help\n' +
'[Example]\n' + '[Example]\n' +
' yarn web --scheme https --host example.com --port 8080 --local_port 30000' ' yarn web --scheme https --host example.com --port 8080 --local_port 30000'
@ -64,18 +67,27 @@ const readFile = (path) => util.promisify(fs.readFile)(path);
const readdir = (path) => util.promisify(fs.readdir)(path); const readdir = (path) => util.promisify(fs.readdir)(path);
const readdirWithFileTypes = (path) => util.promisify(fs.readdir)(path, { withFileTypes: true }); const readdirWithFileTypes = (path) => util.promisify(fs.readdir)(path, { withFileTypes: true });
async function getBuiltInExtensionInfos(extensionsRoot) { async function getBuiltInExtensionInfos() {
const builtinExtensions = []; const extensions = [];
const children = await readdirWithFileTypes(extensionsRoot); /** @type {Object.<string, string>} */
await Promise.all(children.map(async child => { const locations = {};
if (child.isDirectory()) {
const info = await getBuiltInExtensionInfo(path.join(extensionsRoot, child.name)); for (const extensionsRoot of [BUILTIN_EXTENSIONS_ROOT, BUILTIN_MARKETPLACE_EXTENSIONS_ROOT]) {
if (info) { if (await exists(extensionsRoot)) {
builtinExtensions.push(info); 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;
}
}
}));
} }
})); }
return builtinExtensions; return { extensions, locations };
} }
async function getBuiltInExtensionInfo(extensionPath) { async function getBuiltInExtensionInfo(extensionPath) {
@ -106,6 +118,8 @@ async function getBuiltInExtensionInfo(extensionPath) {
async function getDefaultExtensionInfos() { async function getDefaultExtensionInfos() {
const extensions = []; const extensions = [];
/** @type {Object.<string, string>} */
const locations = {}; const locations = {};
let extensionArg = args['extension']; let extensionArg = args['extension'];
@ -166,8 +180,7 @@ async function getExtensionPackageJSON(extensionPath) {
return undefined; return undefined;
} }
const builtInExtensionsPromise = getBuiltInExtensionInfos();
const builtinExtensionsPromise = getBuiltInExtensionInfos(EXTENSIONS_ROOT);
const defaultExtensionsPromise = getDefaultExtensionInfos(); const defaultExtensionsPromise = getDefaultExtensionInfos();
const mapCallbackUriToRequestId = new Map(); const mapCallbackUriToRequestId = new Map();
@ -201,8 +214,8 @@ const server = http.createServer((req, res) => {
return handleExtension(req, res, parsedUrl); return handleExtension(req, res, parsedUrl);
} }
if (/^\/builtin-extension\//.test(pathname)) { if (/^\/builtin-extension\//.test(pathname)) {
// builtin extension requests // built-in extension requests
return handleBuiltinExtension(req, res, parsedUrl); return handleBuiltInExtension(req, res, parsedUrl);
} }
if (pathname === '/') { if (pathname === '/') {
// main web // main web
@ -256,19 +269,10 @@ function handleStatic(req, res, parsedUrl) {
async function handleExtension(req, res, parsedUrl) { async function handleExtension(req, res, parsedUrl) {
// Strip `/extension/` from the path // Strip `/extension/` from the path
const relativePath = decodeURIComponent(parsedUrl.pathname.substr('/extension/'.length)); const relativePath = decodeURIComponent(parsedUrl.pathname.substr('/extension/'.length));
const firstSlash = relativePath.indexOf('/'); const filePath = getExtensionFilePath(relativePath, (await defaultExtensionsPromise).locations);
if (firstSlash === -1) { if (!filePath) {
return serveError(req, res, 400, `Bad request.`); return serveError(req, res, 400, `Bad request.`);
} }
const extensionId = relativePath.substr(0, firstSlash);
const { locations } = await defaultExtensionsPromise;
const extensionPath = locations[extensionId];
if (!extensionPath) {
return serveError(req, res, 400, `Bad request.`);
}
const filePath = path.join(extensionPath, relativePath.substr(firstSlash + 1));
return serveFile(req, res, filePath); return serveFile(req, res, filePath);
} }
@ -277,10 +281,13 @@ async function handleExtension(req, res, parsedUrl) {
* @param {import('http').ServerResponse} res * @param {import('http').ServerResponse} res
* @param {import('url').UrlWithParsedQuery} parsedUrl * @param {import('url').UrlWithParsedQuery} parsedUrl
*/ */
async function handleBuiltinExtension(req, res, parsedUrl) { async function handleBuiltInExtension(req, res, parsedUrl) {
// Strip `/builtin-extension/` from the path // Strip `/builtin-extension/` from the path
const relativePath = decodeURIComponent(parsedUrl.pathname.substr('/builtin-extension/'.length)); const relativePath = decodeURIComponent(parsedUrl.pathname.substr('/builtin-extension/'.length));
const filePath = path.join(EXTENSIONS_ROOT, relativePath); const filePath = getExtensionFilePath(relativePath, (await builtInExtensionsPromise).locations);
if (!filePath) {
return serveError(req, res, 400, `Bad request.`);
}
return serveFile(req, res, filePath); return serveFile(req, res, filePath);
} }
@ -316,18 +323,23 @@ async function handleRoot(req, res) {
} }
} }
const builtinExtensions = await builtinExtensionsPromise; const { extensions: builtInExtensions } = await builtInExtensionsPromise;
const { extensions } = await defaultExtensionsPromise; 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({ const webConfigJSON = escapeAttribute(JSON.stringify({
folderUri: folderUri, folderUri: folderUri,
staticExtensions: extensions, staticExtensions,
builtinExtensionsServiceUrl: `${SCHEME}://${AUTHORITY}/builtin-extension` builtinExtensionsServiceUrl: `${SCHEME}://${AUTHORITY}/builtin-extension`
})); }));
const data = (await 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_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('{{WEBVIEW_ENDPOINT}}', '')
.replace('{{REMOTE_USER_DATA_URI}}', ''); .replace('{{REMOTE_USER_DATA_URI}}', '');
@ -436,6 +448,25 @@ function escapeAttribute(value) {
return value.replace(/"/g, '&quot;'); 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').IncomingMessage} req
* @param {import('http').ServerResponse} res * @param {import('http').ServerResponse} res