diff --git a/callback.html b/callback.html deleted file mode 100644 index 81f217e980b77..0000000000000 --- a/callback.html +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - - - - - - - Visual Studio Code - - - - - - - - Visual Studio Code - -
-
- You can close this page now. -
-
- - - diff --git a/code-web.js b/code-web.js deleted file mode 100755 index e4c28d750be89..0000000000000 --- a/code-web.js +++ /dev/null @@ -1,469 +0,0 @@ -#!/usr/bin/env node - -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -// @ts-check -/** @typedef {import('../src/vs/workbench/workbench.web.api').IWorkbenchConstructionOptions} WebConfiguration **/ - -const http = require('http'); -const url = require('url'); -const fs = require('fs'); -const path = require('path'); -const util = require('util'); -const glob = require('glob'); -const opn = require('opn'); -const minimist = require('minimist'); -const webpack = require('webpack'); - -const APP_ROOT = path.dirname(__dirname); -const EXTENSIONS_ROOT = path.join(APP_ROOT, 'extensions'); -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' - ], - string: [ - 'scheme', - 'host', - 'port', - 'local_port' - ], -}); - -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' + - ' --help\n' + - '[Example]\n' + - ' yarn web --scheme https --host example.com --port 8080 --local_port 30000' - ); - process.exit(0); -} - -const PORT = args.port || process.env.PORT || 8080; -const LOCAL_PORT = args.local_port || process.env.LOCAL_PORT || PORT; -const SCHEME = args.scheme || process.env.VSCODE_SCHEME || 'http'; -const HOST = args.host || 'localhost'; -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 CharCode_PC = '%'.charCodeAt(0); - -async function initialize() { - const extensionFolders = await util.promisify(fs.readdir)(EXTENSIONS_ROOT); - - const staticExtensions = []; - - const webpackConfigs = []; - - await Promise.all(extensionFolders.map(async extensionFolder => { - const packageJSONPath = path.join(EXTENSIONS_ROOT, extensionFolder, 'package.json'); - if (await exists(packageJSONPath)) { - try { - const packageJSON = JSON.parse((await readFile(packageJSONPath)).toString()); - if (packageJSON.main && !packageJSON.browser) { - return; // unsupported - } - - if (packageJSON.browser) { - packageJSON.main = packageJSON.browser; - - const webpackConfigLocations = glob.sync( - path.join(EXTENSIONS_ROOT, extensionFolder, '**', 'extension-browser.webpack.config.js'), - { ignore: ['**/node_modules'] } - ); - - for (const webpackConfigPath of webpackConfigLocations) { - const configOrFnOrArray = require(webpackConfigPath); - function addConfig(configOrFn) { - if (typeof configOrFn === 'function') { - webpackConfigs.push(configOrFn({}, {})); - } else { - webpackConfigs.push(configOrFn); - } - } - addConfig(configOrFnOrArray); - } - } - - const packageNlsPath = path.join(EXTENSIONS_ROOT, extensionFolder, 'package.nls.json'); - if (await exists(packageNlsPath)) { - const packageNls = JSON.parse((await readFile(packageNlsPath)).toString()); - const translate = (obj) => { - for (let key in obj) { - const val = obj[key]; - if (Array.isArray(val)) { - val.forEach(translate); - } else if (val && typeof val === 'object') { - translate(val); - } else if (typeof val === 'string' && val.charCodeAt(0) === CharCode_PC && val.charCodeAt(val.length - 1) === CharCode_PC) { - const translated = packageNls[val.substr(1, val.length - 2)]; - if (translated) { - obj[key] = translated; - } - } - } - }; - translate(packageJSON); - } - packageJSON.extensionKind = ['web']; // enable for Web - staticExtensions.push({ - packageJSON, - extensionLocation: { scheme: SCHEME, authority: AUTHORITY, path: `/static-extension/${extensionFolder}` } - }); - } catch (e) { - console.log(e); - } - } - })); - - return new Promise((resolve, reject) => { - if (args.watch) { - webpack(webpackConfigs).watch({}, (err, stats) => { - if (err) { - console.log(err); - reject(); - } else { - console.log(stats.toString()); - resolve(staticExtensions); - } - }); - } else { - webpack(webpackConfigs).run((err, stats) => { - if (err) { - console.log(err); - reject(); - } else { - console.log(stats.toString()); - resolve(staticExtensions); - } - }); - } - }); -} - -const staticExtensionsPromise = initialize(); - -const mapCallbackUriToRequestId = new Map(); - -const server = http.createServer((req, res) => { - const parsedUrl = url.parse(req.url, true); - const pathname = parsedUrl.pathname; - - try { - if (pathname === '/favicon.ico') { - // favicon - return serveFile(req, res, path.join(APP_ROOT, 'resources', 'win32', 'code.ico')); - } - if (pathname === '/manifest.json') { - // manifest - res.writeHead(200, { 'Content-Type': 'application/json' }); - return res.end(JSON.stringify({ - 'name': 'Code Web - OSS', - 'short_name': 'Code Web - OSS', - 'start_url': '/', - 'lang': 'en-US', - 'display': 'standalone' - })); - } - if (/^\/static\//.test(pathname)) { - // static requests - return handleStatic(req, res, parsedUrl); - } - if (/^\/static-extension\//.test(pathname)) { - // static extension requests - return handleStaticExtension(req, res, parsedUrl); - } - if (pathname === '/') { - // main web - return handleRoot(req, res); - } else if (pathname === '/callback') { - // callback support - return handleCallback(req, res, parsedUrl); - } else if (pathname === '/fetch-callback') { - // callback fetch support - return handleFetchCallback(req, res, parsedUrl); - } - - return serveError(req, res, 404, 'Not found.'); - } catch (error) { - console.error(error.toString()); - - return serveError(req, res, 500, 'Internal Server Error.'); - } -}); - -server.listen(LOCAL_PORT, () => { - if (LOCAL_PORT !== PORT) { - console.log(`Operating location at http://0.0.0.0:${LOCAL_PORT}`); - } - console.log(`Web UI available at ${SCHEME}://${AUTHORITY}`); -}); - -server.on('error', err => { - console.error(`Error occurred in server:`); - console.error(err); -}); - -/** - * @param {import('http').IncomingMessage} req - * @param {import('http').ServerResponse} res - * @param {import('url').UrlWithParsedQuery} parsedUrl - */ -function handleStatic(req, res, parsedUrl) { - - // Strip `/static/` from the path - const relativeFilePath = path.normalize(decodeURIComponent(parsedUrl.pathname.substr('/static/'.length))); - - return serveFile(req, res, path.join(APP_ROOT, relativeFilePath)); -} - -/** - * @param {import('http').IncomingMessage} req - * @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); - - return serveFile(req, res, filePath); -} - -/** - * @param {import('http').IncomingMessage} req - * @param {import('http').ServerResponse} res - */ -async function handleRoot(req, res) { - const match = req.url && req.url.match(/\?([^#]+)/); - let ghPath; - if (match) { - const qs = new URLSearchParams(match[1]); - ghPath = qs.get('gh'); - if (ghPath && !ghPath.startsWith('/')) { - ghPath = '/' + ghPath; - } - } - - const staticExtensions = await staticExtensionsPromise; - /** @type {WebConfiguration} */ - const webConfig = { - staticExtensions: staticExtensions, - }; - - const webConfigJSON = escapeAttribute(JSON.stringify({ - ...webConfig, - folderUri: ghPath - ? { scheme: 'github', authority: 'github.com', path: ghPath } - : { scheme: 'memfs', path: `/sample-folder` }, - })); - - const data = (await util.promisify(fs.readFile)(WEB_MAIN)).toString() - .replace('{{WORKBENCH_WEB_CONFIGURATION}}', () => webConfigJSON) // use a replace function to avoid that regexp replace patterns ($&, $0, ...) are applied - .replace('{{WEBVIEW_ENDPOINT}}', '') - .replace('{{REMOTE_USER_DATA_URI}}', ''); - - res.writeHead(200, { 'Content-Type': 'text/html' }); - return res.end(data); -} - -/** - * Handle HTTP requests for /callback - * @param {import('http').IncomingMessage} req - * @param {import('http').ServerResponse} res - * @param {import('url').UrlWithParsedQuery} parsedUrl -*/ -async function handleCallback(req, res, parsedUrl) { - const wellKnownKeys = ['vscode-requestId', 'vscode-scheme', 'vscode-authority', 'vscode-path', 'vscode-query', 'vscode-fragment']; - const [requestId, vscodeScheme, vscodeAuthority, vscodePath, vscodeQuery, vscodeFragment] = wellKnownKeys.map(key => { - const value = getFirstQueryValue(parsedUrl, key); - if (value) { - return decodeURIComponent(value); - } - - return value; - }); - - if (!requestId) { - res.writeHead(400, { 'Content-Type': 'text/plain' }); - return res.end(`Bad request.`); - } - - // merge over additional query values that we got - let query = vscodeQuery; - let index = 0; - getFirstQueryValues(parsedUrl, wellKnownKeys).forEach((value, key) => { - if (!query) { - query = ''; - } - - const prefix = (index++ === 0) ? '' : '&'; - query += `${prefix}${key}=${value}`; - }); - - - // add to map of known callbacks - mapCallbackUriToRequestId.set(requestId, JSON.stringify({ scheme: vscodeScheme || 'code-oss', authority: vscodeAuthority, path: vscodePath, query, fragment: vscodeFragment })); - return serveFile(req, res, path.join(APP_ROOT, 'scripts', 'callback.html'), { 'Content-Type': 'text/html' }); -} - -/** - * Handle HTTP requests for /fetch-callback - * @param {import('http').IncomingMessage} req - * @param {import('http').ServerResponse} res - * @param {import('url').UrlWithParsedQuery} parsedUrl -*/ -async function handleFetchCallback(req, res, parsedUrl) { - const requestId = getFirstQueryValue(parsedUrl, 'vscode-requestId'); - if (!requestId) { - res.writeHead(400, { 'Content-Type': 'text/plain' }); - return res.end(`Bad request.`); - } - - const knownCallbackUri = mapCallbackUriToRequestId.get(requestId); - if (knownCallbackUri) { - mapCallbackUriToRequestId.delete(requestId); - } - - res.writeHead(200, { 'Content-Type': 'text/json' }); - return res.end(knownCallbackUri); -} - -/** - * @param {import('url').UrlWithParsedQuery} parsedUrl - * @param {string} key - * @returns {string | undefined} -*/ -function getFirstQueryValue(parsedUrl, key) { - const result = parsedUrl.query[key]; - return Array.isArray(result) ? result[0] : result; -} - -/** - * @param {import('url').UrlWithParsedQuery} parsedUrl - * @param {string[] | undefined} ignoreKeys - * @returns {Map} -*/ -function getFirstQueryValues(parsedUrl, ignoreKeys) { - const queryValues = new Map(); - - for (const key in parsedUrl.query) { - if (ignoreKeys && ignoreKeys.indexOf(key) >= 0) { - continue; - } - - const value = getFirstQueryValue(parsedUrl, key); - if (typeof value === 'string') { - queryValues.set(key, value); - } - } - - return queryValues; -} - -/** - * @param {string} value - */ -function escapeAttribute(value) { - return value.replace(/"/g, '"'); -} - -/** - * @param {import('http').IncomingMessage} req - * @param {import('http').ServerResponse} res - * @param {string} errorMessage - */ -function serveError(req, res, errorCode, errorMessage) { - res.writeHead(errorCode, { 'Content-Type': 'text/plain' }); - res.end(errorMessage); -} - -const textMimeType = { - '.html': 'text/html', - '.js': 'text/javascript', - '.json': 'application/json', - '.css': 'text/css', - '.svg': 'image/svg+xml', -}; - -const mapExtToMediaMimes = { - '.bmp': 'image/bmp', - '.gif': 'image/gif', - '.ico': 'image/x-icon', - '.jpe': 'image/jpg', - '.jpeg': 'image/jpg', - '.jpg': 'image/jpg', - '.png': 'image/png', - '.tga': 'image/x-tga', - '.tif': 'image/tiff', - '.tiff': 'image/tiff', - '.woff': 'application/font-woff' -}; - -/** - * @param {string} forPath - */ -function getMediaMime(forPath) { - const ext = path.extname(forPath); - - return mapExtToMediaMimes[ext.toLowerCase()]; -} - -/** - * @param {import('http').IncomingMessage} req - * @param {import('http').ServerResponse} res - * @param {string} filePath - */ -async function serveFile(req, res, filePath, responseHeaders = Object.create(null)) { - try { - - // 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); - - // Check if file modified since - const etag = `W/"${[stat.ino, stat.size, stat.mtime.getTime()].join('-')}"`; // weak validator (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) - if (req.headers['if-none-match'] === etag) { - res.writeHead(304); - return res.end(); - } - - // Headers - responseHeaders['Content-Type'] = textMimeType[path.extname(filePath)] || getMediaMime(filePath) || 'text/plain'; - responseHeaders['Etag'] = etag; - - res.writeHead(200, responseHeaders); - - // Data - fs.createReadStream(filePath).pipe(res); - } catch (error) { - console.error(error.toString()); - res.writeHead(404, { 'Content-Type': 'text/plain' }); - return res.end('Not found'); - } -} - -if (args.launch !== false) { - opn(`${SCHEME}://${HOST}:${PORT}`); -}