2015-11-23 19:28:42 +00:00
/ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* Copyright ( c ) Microsoft Corporation . All rights reserved .
* Licensed under the MIT License . See License . txt in the project root for license information .
* -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- * /
2018-08-31 11:28:24 +00:00
import * as es from 'event-stream' ;
2016-10-20 12:16:57 +00:00
import * as gulp from 'gulp' ;
2018-08-31 11:28:24 +00:00
import * as concat from 'gulp-concat' ;
import * as filter from 'gulp-filter' ;
2019-02-05 21:21:05 +00:00
import * as fancyLog from 'fancy-log' ;
import * as ansiColors from 'ansi-colors' ;
2018-08-31 11:28:24 +00:00
import * as path from 'path' ;
import * as pump from 'pump' ;
2016-10-20 12:16:57 +00:00
import * as VinylFile from 'vinyl' ;
import * as bundle from './bundle' ;
2018-08-31 11:28:24 +00:00
import { Language , processNlsFiles } from './i18n' ;
import { createStatsStream } from './stats' ;
2016-10-20 12:16:57 +00:00
import * as util from './util' ;
2016-06-07 16:46:41 +00:00
2016-10-20 09:48:37 +00:00
const REPO_ROOT_PATH = path . join ( __dirname , '../..' ) ;
2017-03-28 11:01:51 +00:00
function log ( prefix : string , message : string ) : void {
2019-02-05 21:21:05 +00:00
fancyLog ( ansiColors . cyan ( '[' + prefix + ']' ) , message ) ;
2016-06-07 16:46:41 +00:00
}
2016-03-08 10:26:06 +00:00
2020-07-07 21:30:27 +00:00
export function loaderConfig() {
2018-10-03 05:10:41 +00:00
const result : any = {
2015-11-23 19:28:42 +00:00
paths : {
'vs' : 'out-build/vs' ,
2015-12-10 15:48:33 +00:00
'vscode' : 'empty:'
2015-11-23 19:28:42 +00:00
} ,
2020-07-07 21:30:27 +00:00
amdModulesPattern : /^vs\//
2015-11-23 19:28:42 +00:00
} ;
2016-07-10 10:28:11 +00:00
result [ 'vs/css' ] = { inlineResources : true } ;
2015-11-23 19:28:42 +00:00
return result ;
2016-10-20 12:16:57 +00:00
}
2015-11-23 19:28:42 +00:00
2016-08-09 07:54:28 +00:00
const IS_OUR_COPYRIGHT_REGEXP = /Copyright \(C\) Microsoft Corporation/i ;
2015-11-23 19:28:42 +00:00
2022-06-16 20:01:19 +00:00
function loaderPlugin ( src : string , base : string , amdModuleId : string | undefined ) : NodeJS . ReadWriteStream {
return (
gulp
. src ( src , { base } )
. pipe ( es . through ( function ( data : VinylFile ) {
if ( amdModuleId ) {
let contents = data . contents . toString ( 'utf8' ) ;
contents = contents . replace ( /^define\(/m , ` define(" ${ amdModuleId } ", ` ) ;
data . contents = Buffer . from ( contents ) ;
}
this . emit ( 'data' , data ) ;
} ) )
) ;
}
2023-03-09 14:20:15 +00:00
function loader ( src : string , bundledFileHeader : string , bundleLoader : boolean , externalLoaderInfo? : util.IExternalLoaderInfo ) : NodeJS . ReadWriteStream {
2022-06-16 20:01:19 +00:00
let loaderStream = gulp . src ( ` ${ src } /vs/loader.js ` , { base : ` ${ src } ` } ) ;
2016-09-18 20:05:42 +00:00
if ( bundleLoader ) {
2022-06-16 20:01:19 +00:00
loaderStream = es . merge (
loaderStream ,
loaderPlugin ( ` ${ src } /vs/css.js ` , ` ${ src } ` , 'vs/css' ) ,
loaderPlugin ( ` ${ src } /vs/nls.js ` , ` ${ src } ` , 'vs/nls' ) ,
) ;
2016-09-18 20:05:42 +00:00
}
2022-06-16 21:05:10 +00:00
const files : VinylFile [ ] = [ ] ;
const order = ( f : VinylFile ) = > {
if ( f . path . endsWith ( 'loader.js' ) ) {
return 0 ;
}
if ( f . path . endsWith ( 'css.js' ) ) {
return 1 ;
}
if ( f . path . endsWith ( 'nls.js' ) ) {
return 2 ;
}
return 3 ;
} ;
2016-09-18 20:05:42 +00:00
return (
2022-06-16 20:01:19 +00:00
loaderStream
2017-03-28 11:01:51 +00:00
. pipe ( es . through ( function ( data ) {
2022-06-16 21:05:10 +00:00
files . push ( data ) ;
2021-08-31 14:17:54 +00:00
} , function ( ) {
2022-06-16 21:05:10 +00:00
files . sort ( ( a , b ) = > {
return order ( a ) - order ( b ) ;
} ) ;
files . unshift ( new VinylFile ( {
path : 'fake' ,
base : '.' ,
contents : Buffer.from ( bundledFileHeader )
} ) ) ;
2021-08-31 14:17:54 +00:00
if ( externalLoaderInfo !== undefined ) {
2022-06-16 21:05:10 +00:00
files . push ( new VinylFile ( {
2021-08-31 14:17:54 +00:00
path : 'fake2' ,
base : '.' ,
2023-03-09 14:20:15 +00:00
contents : Buffer.from ( emitExternalLoaderInfo ( externalLoaderInfo ) )
2021-08-31 14:17:54 +00:00
} ) ) ;
}
2022-06-16 21:05:10 +00:00
for ( const file of files ) {
this . emit ( 'data' , file ) ;
}
2021-08-31 14:17:54 +00:00
this . emit ( 'end' ) ;
2017-03-28 11:01:51 +00:00
} ) )
. pipe ( concat ( 'vs/loader.js' ) )
2016-09-18 20:05:42 +00:00
) ;
2015-11-23 19:28:42 +00:00
}
2023-03-09 14:20:15 +00:00
function emitExternalLoaderInfo ( externalLoaderInfo : util.IExternalLoaderInfo ) : string {
const externalBaseUrl = externalLoaderInfo . baseUrl ;
externalLoaderInfo . baseUrl = '$BASE_URL' ;
// If defined, use the runtime configured baseUrl.
const code = `
( function ( ) {
const baseUrl = require . getConfig ( ) . baseUrl || $ { JSON . stringify ( externalBaseUrl ) } ;
require . config ( $ { JSON . stringify ( externalLoaderInfo , undefined , 2 ) } ) ;
} ) ( ) ; ` ;
return code . replace ( '"$BASE_URL"' , 'baseUrl' ) ;
}
2020-07-28 15:40:06 +00:00
function toConcatStream ( src : string , bundledFileHeader : string , sources : bundle.IFile [ ] , dest : string , fileContentMapper : ( contents : string , path : string ) = > string ) : NodeJS . ReadWriteStream {
2016-08-09 07:54:28 +00:00
const useSourcemaps = /\.js$/ . test ( dest ) && ! /\.nls\.js$/ . test ( dest ) ;
2015-11-23 19:28:42 +00:00
// If a bundle ends up including in any of the sources our copyright, then
// insert a fake source at the beginning of each bundle with our copyright
2016-08-09 07:54:28 +00:00
let containsOurCopyright = false ;
for ( let i = 0 , len = sources . length ; i < len ; i ++ ) {
const fileContents = sources [ i ] . contents ;
2015-11-23 19:28:42 +00:00
if ( IS_OUR_COPYRIGHT_REGEXP . test ( fileContents ) ) {
containsOurCopyright = true ;
break ;
}
}
if ( containsOurCopyright ) {
sources . unshift ( {
path : null ,
contents : bundledFileHeader
} ) ;
}
2017-03-28 11:01:51 +00:00
const treatedSources = sources . map ( function ( source ) {
2016-10-20 09:48:37 +00:00
const root = source . path ? REPO_ROOT_PATH . replace ( /\\/g , '/' ) : '' ;
2020-10-26 19:22:34 +00:00
const base = source . path ? root + ` / ${ src } ` : '.' ;
2020-07-28 15:40:06 +00:00
const path = source . path ? root + '/' + source . path . replace ( /\\/g , '/' ) : 'fake' ;
const contents = source . path ? fileContentMapper ( source . contents , path ) : source . contents ;
2015-11-23 19:28:42 +00:00
2016-10-20 09:48:37 +00:00
return new VinylFile ( {
2020-07-28 15:40:06 +00:00
path : path ,
2015-11-23 19:28:42 +00:00
base : base ,
2020-07-28 15:40:06 +00:00
contents : Buffer.from ( contents )
2015-11-23 19:28:42 +00:00
} ) ;
} ) ;
return es . readArray ( treatedSources )
. pipe ( useSourcemaps ? util . loadSourcemaps ( ) : es . through ( ) )
2018-08-31 11:28:24 +00:00
. pipe ( concat ( dest ) )
2018-09-03 09:51:26 +00:00
. pipe ( createStatsStream ( dest ) ) ;
2015-11-23 19:28:42 +00:00
}
2020-07-28 15:40:06 +00:00
function toBundleStream ( src : string , bundledFileHeader : string , bundles : bundle.IConcatFile [ ] , fileContentMapper : ( contents : string , path : string ) = > string ) : NodeJS . ReadWriteStream {
2017-03-28 11:01:51 +00:00
return es . merge ( bundles . map ( function ( bundle ) {
2020-07-28 15:40:06 +00:00
return toConcatStream ( src , bundledFileHeader , bundle . sources , bundle . dest , fileContentMapper ) ;
2015-11-23 19:28:42 +00:00
} ) ) ;
}
2022-09-21 07:38:44 +00:00
export interface IOptimizeAMDTaskOpts {
2018-07-20 08:38:56 +00:00
/ * *
* The folder to read files from .
* /
src : string ;
2016-10-20 12:16:57 +00:00
/ * *
* ( for AMD files , will get bundled and get Copyright treatment )
* /
entryPoints : bundle.IEntryPoint [ ] ;
/ * *
* ( svg , etc . )
* /
resources : string [ ] ;
loaderConfig : any ;
2021-10-16 09:43:26 +00:00
/ * *
* Additional info we append to the end of the loader
* /
2023-03-09 14:20:15 +00:00
externalLoaderInfo? : util.IExternalLoaderInfo ;
2016-10-20 12:16:57 +00:00
/ * *
* ( true by default - append css and nls to loader )
* /
bundleLoader? : boolean ;
/ * *
* ( basically the Copyright treatment )
* /
2019-07-14 11:12:54 +00:00
header? : string ;
2016-10-20 12:16:57 +00:00
/ * *
* ( emit bundleInfo . json file )
* /
bundleInfo : boolean ;
/ * *
2022-09-21 07:38:44 +00:00
* Language configuration .
2018-07-06 13:13:44 +00:00
* /
languages? : Language [ ] ;
2020-07-28 15:40:06 +00:00
/ * *
* File contents interceptor
2023-03-09 15:13:17 +00:00
* @param contents The contents of the file
2020-07-28 15:40:06 +00:00
* @param path The absolute file path , always using ` / ` , even on Windows
* /
fileContentMapper ? : ( contents : string , path : string ) = > string ;
2016-10-20 12:16:57 +00:00
}
2018-06-14 14:59:30 +00:00
2019-07-14 11:12:54 +00:00
const DEFAULT_FILE_HEADER = [
'/*!--------------------------------------------------------' ,
' * Copyright (C) Microsoft Corporation. All rights reserved.' ,
' *--------------------------------------------------------*/'
] . join ( '\n' ) ;
2022-09-21 07:38:44 +00:00
function optimizeAMDTask ( opts : IOptimizeAMDTaskOpts ) : NodeJS . ReadWriteStream {
2018-07-20 08:38:56 +00:00
const src = opts . src ;
2016-08-09 07:54:28 +00:00
const entryPoints = opts . entryPoints ;
const resources = opts . resources ;
const loaderConfig = opts . loaderConfig ;
2019-07-14 11:12:54 +00:00
const bundledFileHeader = opts . header || DEFAULT_FILE_HEADER ;
2020-07-28 15:40:06 +00:00
const fileContentMapper = opts . fileContentMapper || ( ( contents : string , _path : string ) = > contents ) ;
2015-11-23 19:28:42 +00:00
2022-09-21 07:38:44 +00:00
const sourcemaps = require ( 'gulp-sourcemaps' ) as typeof import ( 'gulp-sourcemaps' ) ;
2015-11-23 19:28:42 +00:00
2022-09-21 07:38:44 +00:00
const bundlesStream = es . through ( ) ; // this stream will contain the bundled files
const resourcesStream = es . through ( ) ; // this stream will contain the resources
const bundleInfoStream = es . through ( ) ; // this stream will contain bundleInfo.json
2015-11-23 19:28:42 +00:00
2022-09-21 07:38:44 +00:00
bundle . bundle ( entryPoints , loaderConfig , function ( err , result ) {
if ( err || ! result ) { return bundlesStream . emit ( 'error' , JSON . stringify ( err ) ) ; }
2016-06-07 16:46:41 +00:00
2022-09-21 07:38:44 +00:00
toBundleStream ( src , bundledFileHeader , result . files , fileContentMapper ) . pipe ( bundlesStream ) ;
2022-09-16 12:24:23 +00:00
2022-09-21 07:38:44 +00:00
// Remove css inlined resources
const filteredResources = resources . slice ( ) ;
result . cssInlinedResources . forEach ( function ( resource ) {
if ( process . env [ 'VSCODE_BUILD_VERBOSE' ] ) {
log ( 'optimizer' , 'excluding inlined: ' + resource ) ;
2022-09-16 16:31:44 +00:00
}
2022-09-21 07:38:44 +00:00
filteredResources . push ( '!' + resource ) ;
2022-09-16 16:31:44 +00:00
} ) ;
2022-09-21 07:38:44 +00:00
gulp . src ( filteredResources , { base : ` ${ src } ` , allowEmpty : true } ) . pipe ( resourcesStream ) ;
const bundleInfoArray : VinylFile [ ] = [ ] ;
if ( opts . bundleInfo ) {
bundleInfoArray . push ( new VinylFile ( {
path : 'bundleInfo.json' ,
base : '.' ,
contents : Buffer.from ( JSON . stringify ( result . bundleData , null , '\t' ) )
} ) ) ;
}
es . readArray ( bundleInfoArray ) . pipe ( bundleInfoStream ) ;
} ) ;
2022-09-16 12:24:23 +00:00
2022-09-21 07:38:44 +00:00
const result = es . merge (
loader ( src , bundledFileHeader , false , opts . externalLoaderInfo ) ,
bundlesStream ,
resourcesStream ,
bundleInfoStream
) ;
2022-09-16 12:24:23 +00:00
2022-09-21 07:38:44 +00:00
return result
. pipe ( sourcemaps . write ( './' , {
sourceRoot : undefined ,
addComment : true ,
includeContent : true
} ) )
. pipe ( opts . languages && opts . languages . length ? processNlsFiles ( {
fileHeader : bundledFileHeader ,
languages : opts.languages
} ) : es . through ( ) ) ;
}
export interface IOptimizeCommonJSTaskOpts {
/ * *
* The paths to consider for optimizing .
* /
entryPoints : string [ ] ;
/ * *
* The folder to read files from .
* /
src : string ;
/ * *
* ESBuild ` platform ` option : https : //esbuild.github.io/api/#platform
* /
platform : 'browser' | 'node' | 'neutral' ;
/ * *
* ESBuild ` external ` option : https : //esbuild.github.io/api/#external
* /
external : string [ ] ;
}
function optimizeCommonJSTask ( opts : IOptimizeCommonJSTaskOpts ) : NodeJS . ReadWriteStream {
const esbuild = require ( 'esbuild' ) as typeof import ( 'esbuild' ) ;
const src = opts . src ;
const entryPoints = opts . entryPoints ;
return gulp . src ( entryPoints , { base : ` ${ src } ` , allowEmpty : true } )
. pipe ( es . map ( ( f : any , cb ) = > {
esbuild . build ( {
entryPoints : [ f . path ] ,
bundle : true ,
platform : opts.platform ,
write : false ,
external : opts.external
} ) . then ( res = > {
const jsFile = res . outputFiles [ 0 ] ;
f . contents = Buffer . from ( jsFile . contents ) ;
cb ( undefined , f ) ;
} ) ;
} ) ) ;
}
export interface IOptimizeManualTaskOpts {
/ * *
* The paths to consider for concatenation . The entries
* will be concatenated in the order they are provided .
* /
src : string [ ] ;
/ * *
* Destination target to concatenate the entryPoints into .
* /
out : string ;
}
function optimizeManualTask ( options : IOptimizeManualTaskOpts [ ] ) : NodeJS . ReadWriteStream {
const concatenations = options . map ( opt = > {
return gulp
. src ( opt . src )
. pipe ( concat ( opt . out ) ) ;
} ) ;
return es . merge ( . . . concatenations ) ;
}
2023-03-09 14:20:15 +00:00
export function optimizeLoaderTask ( src : string , out : string , bundleLoader : boolean , bundledFileHeader = '' , externalLoaderInfo? : util.IExternalLoaderInfo ) : ( ) = > NodeJS . ReadWriteStream {
2022-09-21 07:38:44 +00:00
return ( ) = > loader ( src , bundledFileHeader , bundleLoader , externalLoaderInfo ) . pipe ( gulp . dest ( out ) ) ;
}
export interface IOptimizeTaskOpts {
/ * *
* Destination folder for the optimized files .
* /
out : string ;
/ * *
* Optimize AMD module s ( using our AMD loader ) .
* /
amd : IOptimizeAMDTaskOpts ;
/ * *
* Optimize CommonJS module s ( using esbuild ) .
* /
commonJS? : IOptimizeCommonJSTaskOpts ;
/ * *
* Optimize manually by concatenating files .
* /
manual? : IOptimizeManualTaskOpts [ ] ;
}
export function optimizeTask ( opts : IOptimizeTaskOpts ) : ( ) = > NodeJS . ReadWriteStream {
return function ( ) {
const optimizers = [ optimizeAMDTask ( opts . amd ) ] ;
if ( opts . commonJS ) {
optimizers . push ( optimizeCommonJSTask ( opts . commonJS ) ) ;
}
if ( opts . manual ) {
optimizers . push ( optimizeManualTask ( opts . manual ) ) ;
}
return es . merge ( . . . optimizers ) . pipe ( gulp . dest ( opts . out ) ) ;
2015-11-23 19:28:42 +00:00
} ;
2018-01-25 20:13:24 +00:00
}
2015-11-23 19:28:42 +00:00
2018-02-01 13:39:12 +00:00
export function minifyTask ( src : string , sourceMapBaseUrl? : string ) : ( cb : any ) = > void {
2021-01-06 13:19:19 +00:00
const esbuild = require ( 'esbuild' ) as typeof import ( 'esbuild' ) ;
2018-10-03 00:19:03 +00:00
const sourceMappingURL = sourceMapBaseUrl ? ( ( f : any ) = > ` ${ sourceMapBaseUrl } / ${ f . relative } .map ` ) : undefined ;
2016-09-13 07:41:47 +00:00
2016-09-19 08:22:57 +00:00
return cb = > {
2021-01-22 09:49:30 +00:00
const cssnano = require ( 'cssnano' ) as typeof import ( 'cssnano' ) ;
const postcss = require ( 'gulp-postcss' ) as typeof import ( 'gulp-postcss' ) ;
2021-01-04 14:54:50 +00:00
const sourcemaps = require ( 'gulp-sourcemaps' ) as typeof import ( 'gulp-sourcemaps' ) ;
2022-01-07 14:14:07 +00:00
const svgmin = require ( 'gulp-svgmin' ) as typeof import ( 'gulp-svgmin' ) ;
2020-12-22 18:55:56 +00:00
2016-08-09 07:54:28 +00:00
const jsFilter = filter ( '**/*.js' , { restore : true } ) ;
const cssFilter = filter ( '**/*.css' , { restore : true } ) ;
2022-01-07 14:14:07 +00:00
const svgFilter = filter ( '**/*.svg' , { restore : true } ) ;
2015-11-23 19:28:42 +00:00
2016-09-19 08:22:57 +00:00
pump (
gulp . src ( [ src + '/**' , '!' + src + '/**/*.map' ] ) ,
jsFilter ,
sourcemaps . init ( { loadMaps : true } ) ,
2021-01-06 13:19:19 +00:00
es . map ( ( f : any , cb ) = > {
esbuild . build ( {
entryPoints : [ f . path ] ,
minify : true ,
sourcemap : 'external' ,
outdir : '.' ,
platform : 'node' ,
2021-06-25 14:31:34 +00:00
target : [ 'esnext' ] ,
2021-01-06 13:19:19 +00:00
write : false
} ) . then ( res = > {
const jsFile = res . outputFiles . find ( f = > /\.js$/ . test ( f . path ) ) ! ;
const sourceMapFile = res . outputFiles . find ( f = > /\.js\.map$/ . test ( f . path ) ) ! ;
2023-03-30 07:26:11 +00:00
const contents = Buffer . from ( jsFile . contents ) ;
const unicodeMatch = contents . toString ( ) . match ( /[^\x00-\xFF]+/g ) ;
if ( unicodeMatch ) {
cb ( new Error ( ` Found non-ascii character ${ unicodeMatch [ 0 ] } in the minified output of ${ f . path } . Non-ASCII characters in the output can cause performance problems when loading. Please review if you have introduced a regular expression that esbuild is not automatically converting and convert it to using unicode escape sequences. ` ) ) ;
} else {
f . contents = contents ;
f . sourceMap = JSON . parse ( sourceMapFile . text ) ;
cb ( undefined , f ) ;
}
2021-01-06 13:19:19 +00:00
} , cb ) ;
} ) ,
2016-09-19 08:22:57 +00:00
jsFilter . restore ,
cssFilter ,
2021-01-22 09:49:30 +00:00
postcss ( [ cssnano ( { preset : 'default' } ) ] ) ,
2016-09-19 08:22:57 +00:00
cssFilter . restore ,
2022-01-07 14:14:07 +00:00
svgFilter ,
svgmin ( ) ,
svgFilter . restore ,
2019-02-20 02:05:48 +00:00
( < any > sourcemaps ) . mapSources ( ( sourcePath : string ) = > {
if ( sourcePath === 'bootstrap-fork.js' ) {
return 'bootstrap-fork.orig.js' ;
}
return sourcePath ;
} ) ,
2016-09-19 08:22:57 +00:00
sourcemaps . write ( './' , {
2016-09-13 07:41:47 +00:00
sourceMappingURL ,
2018-10-03 00:19:03 +00:00
sourceRoot : undefined ,
2015-11-23 19:28:42 +00:00
includeContent : true ,
2016-09-13 07:41:47 +00:00
addComment : true
2018-10-03 05:21:12 +00:00
} as any ) ,
2021-01-05 10:54:18 +00:00
gulp . dest ( src + '-min' ) ,
( err : any ) = > cb ( err ) ) ;
2015-11-23 19:28:42 +00:00
} ;
2018-01-25 20:13:24 +00:00
}