@@ -20,13 +20,15 @@ const ansiColors = require('ansi-colors');
2020const extensions = require ( '../../build/lib/extensions' ) ;
2121
2222const APP_ROOT = path . join ( __dirname , '..' , '..' ) ;
23- const EXTENSIONS_ROOT = path . join ( APP_ROOT , 'extensions' ) ;
23+ const BUILTIN_EXTENSIONS_ROOT = path . join ( APP_ROOT , 'extensions' ) ;
24+ const BUILTIN_MARKETPLACE_EXTENSIONS_ROOT = path . join ( APP_ROOT , '.build' , 'builtInExtensions' ) ;
2425const WEB_MAIN = path . join ( APP_ROOT , 'src' , 'vs' , 'code' , 'browser' , 'workbench' , 'workbench-dev.html' ) ;
2526
2627const args = minimist ( process . argv , {
2728 boolean : [
2829 'no-launch' ,
29- 'help'
30+ 'help' ,
31+ 'verbose'
3032 ] ,
3133 string : [
3234 'scheme' ,
@@ -46,6 +48,7 @@ if (args.help) {
4648 ' --port Remote/Local port\n' +
4749 ' --local_port Local port override\n' +
4850 ' --extension Path of an extension to include\n' +
51+ ' --verbose Print out more information\n' +
4952 ' --help\n' +
5053 '[Example]\n' +
5154 ' yarn web --scheme https --host example.com --port 8080 --local_port 30000'
@@ -64,18 +67,27 @@ const readFile = (path) => util.promisify(fs.readFile)(path);
6467const readdir = ( path ) => util . promisify ( fs . readdir ) ( path ) ;
6568const readdirWithFileTypes = ( path ) => util . promisify ( fs . readdir ) ( path , { withFileTypes : true } ) ;
6669
67- async function getBuiltInExtensionInfos ( extensionsRoot ) {
68- const builtinExtensions = [ ] ;
69- const children = await readdirWithFileTypes ( extensionsRoot ) ;
70- await Promise . all ( children . map ( async child => {
71- if ( child . isDirectory ( ) ) {
72- const info = await getBuiltInExtensionInfo ( path . join ( extensionsRoot , child . name ) ) ;
73- if ( info ) {
74- builtinExtensions . push ( info ) ;
75- }
70+ async function getBuiltInExtensionInfos ( ) {
71+ const extensions = [ ] ;
72+ /** @type {Object.<string, string> } */
73+ const locations = { } ;
74+
75+ for ( const extensionsRoot of [ BUILTIN_EXTENSIONS_ROOT , BUILTIN_MARKETPLACE_EXTENSIONS_ROOT ] ) {
76+ if ( await exists ( extensionsRoot ) ) {
77+ const children = await readdirWithFileTypes ( extensionsRoot ) ;
78+ await Promise . all ( children . map ( async child => {
79+ if ( child . isDirectory ( ) ) {
80+ const extensionPath = path . join ( extensionsRoot , child . name ) ;
81+ const info = await getBuiltInExtensionInfo ( extensionPath ) ;
82+ if ( info ) {
83+ extensions . push ( info ) ;
84+ locations [ path . basename ( extensionPath ) ] = extensionPath ;
85+ }
86+ }
87+ } ) ) ;
7688 }
77- } ) ) ;
78- return builtinExtensions ;
89+ }
90+ return { extensions , locations } ;
7991}
8092
8193async function getBuiltInExtensionInfo ( extensionPath ) {
@@ -106,6 +118,8 @@ async function getBuiltInExtensionInfo(extensionPath) {
106118
107119async function getDefaultExtensionInfos ( ) {
108120 const extensions = [ ] ;
121+
122+ /** @type {Object.<string, string> } */
109123 const locations = { } ;
110124
111125 let extensionArg = args [ 'extension' ] ;
@@ -166,8 +180,7 @@ async function getExtensionPackageJSON(extensionPath) {
166180 return undefined ;
167181}
168182
169-
170- const builtinExtensionsPromise = getBuiltInExtensionInfos ( EXTENSIONS_ROOT ) ;
183+ const builtInExtensionsPromise = getBuiltInExtensionInfos ( ) ;
171184const defaultExtensionsPromise = getDefaultExtensionInfos ( ) ;
172185
173186const mapCallbackUriToRequestId = new Map ( ) ;
@@ -201,8 +214,8 @@ const server = http.createServer((req, res) => {
201214 return handleExtension ( req , res , parsedUrl ) ;
202215 }
203216 if ( / ^ \/ b u i l t i n - e x t e n s i o n \/ / . test ( pathname ) ) {
204- // builtin extension requests
205- return handleBuiltinExtension ( req , res , parsedUrl ) ;
217+ // built-in extension requests
218+ return handleBuiltInExtension ( req , res , parsedUrl ) ;
206219 }
207220 if ( pathname === '/' ) {
208221 // main web
@@ -256,19 +269,10 @@ function handleStatic(req, res, parsedUrl) {
256269async function handleExtension ( req , res , parsedUrl ) {
257270 // Strip `/extension/` from the path
258271 const relativePath = decodeURIComponent ( parsedUrl . pathname . substr ( '/extension/' . length ) ) ;
259- const firstSlash = relativePath . indexOf ( '/' ) ;
260- if ( firstSlash === - 1 ) {
261- return serveError ( req , res , 400 , `Bad request.` ) ;
262- }
263- const extensionId = relativePath . substr ( 0 , firstSlash ) ;
264- const { locations } = await defaultExtensionsPromise ;
265-
266- const extensionPath = locations [ extensionId ] ;
267- if ( ! extensionPath ) {
272+ const filePath = getExtensionFilePath ( relativePath , ( await defaultExtensionsPromise ) . locations ) ;
273+ if ( ! filePath ) {
268274 return serveError ( req , res , 400 , `Bad request.` ) ;
269275 }
270-
271- const filePath = path . join ( extensionPath , relativePath . substr ( firstSlash + 1 ) ) ;
272276 return serveFile ( req , res , filePath ) ;
273277}
274278
@@ -277,10 +281,13 @@ async function handleExtension(req, res, parsedUrl) {
277281 * @param {import('http').ServerResponse } res
278282 * @param {import('url').UrlWithParsedQuery } parsedUrl
279283 */
280- async function handleBuiltinExtension ( req , res , parsedUrl ) {
284+ async function handleBuiltInExtension ( req , res , parsedUrl ) {
281285 // Strip `/builtin-extension/` from the path
282286 const relativePath = decodeURIComponent ( parsedUrl . pathname . substr ( '/builtin-extension/' . length ) ) ;
283- const filePath = path . join ( EXTENSIONS_ROOT , relativePath ) ;
287+ const filePath = getExtensionFilePath ( relativePath , ( await builtInExtensionsPromise ) . locations ) ;
288+ if ( ! filePath ) {
289+ return serveError ( req , res , 400 , `Bad request.` ) ;
290+ }
284291 return serveFile ( req , res , filePath ) ;
285292}
286293
@@ -316,18 +323,23 @@ async function handleRoot(req, res) {
316323 }
317324 }
318325
319- const builtinExtensions = await builtinExtensionsPromise ;
320- const { extensions } = await defaultExtensionsPromise ;
326+ const { extensions : builtInExtensions } = await builtInExtensionsPromise ;
327+ const { extensions : staticExtensions } = await defaultExtensionsPromise ;
328+
329+ if ( args . verbose ) {
330+ fancyLog ( `${ ansiColors . magenta ( 'BuiltIn extensions' ) } : ${ builtInExtensions . map ( e => path . basename ( e . extensionPath ) ) . join ( ', ' ) } ` ) ;
331+ fancyLog ( `${ ansiColors . magenta ( 'Additional extensions' ) } : ${ staticExtensions . map ( e => path . basename ( e . extensionLocation . path ) ) . join ( ', ' ) || 'None' } ` ) ;
332+ }
321333
322334 const webConfigJSON = escapeAttribute ( JSON . stringify ( {
323335 folderUri : folderUri ,
324- staticExtensions : extensions ,
336+ staticExtensions,
325337 builtinExtensionsServiceUrl : `${ SCHEME } ://${ AUTHORITY } /builtin-extension`
326338 } ) ) ;
327339
328340 const data = ( await readFile ( WEB_MAIN ) ) . toString ( )
329341 . replace ( '{{WORKBENCH_WEB_CONFIGURATION}}' , ( ) => webConfigJSON ) // use a replace function to avoid that regexp replace patterns ($&, $0, ...) are applied
330- . replace ( '{{WORKBENCH_BUILTIN_EXTENSIONS}}' , ( ) => escapeAttribute ( JSON . stringify ( builtinExtensions ) ) )
342+ . replace ( '{{WORKBENCH_BUILTIN_EXTENSIONS}}' , ( ) => escapeAttribute ( JSON . stringify ( builtInExtensions ) ) )
331343 . replace ( '{{WEBVIEW_ENDPOINT}}' , '' )
332344 . replace ( '{{REMOTE_USER_DATA_URI}}' , '' ) ;
333345
@@ -436,6 +448,25 @@ function escapeAttribute(value) {
436448 return value . replace ( / " / g, '"' ) ;
437449}
438450
451+ /**
452+ * @param {string } relativePath
453+ * @param {Object.<string, string> } locations
454+ * @returns {string | undefined }
455+ */
456+ function getExtensionFilePath ( relativePath , locations ) {
457+ const firstSlash = relativePath . indexOf ( '/' ) ;
458+ if ( firstSlash === - 1 ) {
459+ return undefined ;
460+ }
461+ const extensionId = relativePath . substr ( 0 , firstSlash ) ;
462+
463+ const extensionPath = locations [ extensionId ] ;
464+ if ( ! extensionPath ) {
465+ return undefined ;
466+ }
467+ return path . join ( extensionPath , relativePath . substr ( firstSlash + 1 ) ) ;
468+ }
469+
439470/**
440471 * @param {import('http').IncomingMessage } req
441472 * @param {import('http').ServerResponse } res
0 commit comments