2015-11-13 13:39:38 +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-10-03 18:00:38 +00:00
import * as fs from 'fs' ;
import * as path from 'path' ;
import * as vm from 'vm' ;
2015-11-13 13:39:38 +00:00
interface IPosition {
line : number ;
col : number ;
}
interface IBuildModuleInfo {
id : string ;
path : string ;
2022-06-15 12:51:27 +00:00
defineLocation : IPosition | null ;
2015-11-13 13:39:38 +00:00
dependencies : string [ ] ;
shim : string ;
exports : any ;
}
interface IBuildModuleInfoMap {
2017-03-06 15:32:19 +00:00
[ module Id : string ] : IBuildModuleInfo ;
2015-11-13 13:39:38 +00:00
}
interface ILoaderPlugin {
2017-03-06 15:32:19 +00:00
write ( pluginName : string , module Name : string , write : ILoaderPluginWriteFunc ) : void ;
writeFile ( pluginName : string , entryPoint : string , req : ILoaderPluginReqFunc , write : ( filename : string , contents : string ) = > void , config : any ) : void ;
finishBuild ( write : ( filename : string , contents : string ) = > void ) : void ;
2015-11-13 13:39:38 +00:00
}
interface ILoaderPluginWriteFunc {
2017-03-06 15:32:19 +00:00
( something : string ) : void ;
2015-11-13 13:39:38 +00:00
getEntryPoint ( ) : string ;
2017-03-06 15:32:19 +00:00
asModule ( module Id : string , code : string ) : void ;
2015-11-13 13:39:38 +00:00
}
interface ILoaderPluginReqFunc {
2017-03-06 15:32:19 +00:00
( something : string ) : void ;
toUrl ( something : string ) : string ;
2015-11-13 13:39:38 +00:00
}
2022-06-16 20:01:19 +00:00
export interface IExtraFile {
path : string ;
amdModuleId? : string ;
}
2015-11-13 13:39:38 +00:00
export interface IEntryPoint {
name : string ;
2018-02-01 13:39:12 +00:00
include? : string [ ] ;
exclude? : string [ ] ;
2022-06-16 20:01:19 +00:00
prepend? : IExtraFile [ ] ;
append? : IExtraFile [ ] ;
2018-02-01 13:39:12 +00:00
dest? : string ;
2015-11-13 13:39:38 +00:00
}
interface IEntryPointMap {
2017-03-06 15:32:19 +00:00
[ module Id : string ] : IEntryPoint ;
2015-11-13 13:39:38 +00:00
}
2016-07-19 09:39:02 +00:00
export interface IGraph {
2017-03-06 15:32:19 +00:00
[ node : string ] : string [ ] ;
2015-11-13 13:39:38 +00:00
}
interface INodeSet {
2017-03-06 15:32:19 +00:00
[ node : string ] : boolean ;
2015-11-13 13:39:38 +00:00
}
export interface IFile {
2018-10-02 23:38:00 +00:00
path : string | null ;
2015-11-13 13:39:38 +00:00
contents : string ;
}
export interface IConcatFile {
dest : string ;
sources : IFile [ ] ;
}
2016-06-08 12:54:13 +00:00
export interface IBundleData {
graph : IGraph ;
2022-02-02 13:34:41 +00:00
bundles : { [ module Id : string ] : string [ ] } ;
2016-06-08 12:54:13 +00:00
}
2016-06-07 16:46:41 +00:00
export interface IBundleResult {
files : IConcatFile [ ] ;
cssInlinedResources : string [ ] ;
2016-06-08 12:54:13 +00:00
bundleData : IBundleData ;
}
interface IPartialBundleResult {
files : IConcatFile [ ] ;
bundleData : IBundleData ;
2016-06-07 16:46:41 +00:00
}
2015-11-13 13:39:38 +00:00
export interface ILoaderConfig {
isBuild? : boolean ;
2022-02-02 13:34:41 +00:00
paths ? : { [ path : string ] : any } ;
2022-06-16 20:01:19 +00:00
/ *
* Normally , during a build , no module factories are invoked . This can be used
* to forcefully execute a module ' s factory .
* /
buildForceInvokeFactory : {
[ module Id : string ] : boolean ;
} ;
2015-11-13 13:39:38 +00:00
}
/ * *
* Bundle ` entryPoints ` given config ` config ` .
* /
2018-10-02 23:38:00 +00:00
export function bundle ( entryPoints : IEntryPoint [ ] , config : ILoaderConfig , callback : ( err : any , result : IBundleResult | null ) = > void ) : void {
2018-10-04 21:04:23 +00:00
const entryPointsMap : IEntryPointMap = { } ;
2017-03-06 15:32:19 +00:00
entryPoints . forEach ( ( module : IEntryPoint ) = > {
2021-11-09 22:41:02 +00:00
if ( entryPointsMap [ module .name ] ) {
throw new Error ( ` Cannot have two entry points with the same name ' ${ module .name } ' ` ) ;
}
2015-11-13 13:39:38 +00:00
entryPointsMap [ module .name ] = module ;
} ) ;
2022-02-02 13:34:41 +00:00
const allMentionedModulesMap : { [ module s : string ] : boolean } = { } ;
2017-03-06 15:32:19 +00:00
entryPoints . forEach ( ( module : IEntryPoint ) = > {
2016-06-08 22:22:59 +00:00
allMentionedModulesMap [ module .name ] = true ;
2022-02-28 21:52:59 +00:00
module .include?.forEach ( function ( includedModule ) {
2016-06-08 22:22:59 +00:00
allMentionedModulesMap [ includedModule ] = true ;
} ) ;
2022-02-28 21:52:59 +00:00
module .exclude?.forEach ( function ( excludedModule ) {
2016-06-08 22:22:59 +00:00
allMentionedModulesMap [ excludedModule ] = true ;
} ) ;
} ) ;
2015-11-13 13:39:38 +00:00
2018-10-04 20:55:56 +00:00
const code = require ( 'fs' ) . readFileSync ( path . join ( __dirname , '../../src/vs/loader.js' ) ) ;
const r : Function = < any > vm . runInThisContext ( '(function(require, module, exports) { ' + code + '\n});' ) ;
const loaderModule = { exports : { } } ;
2015-11-13 13:39:38 +00:00
r . call ( { } , require , loaderModule , loaderModule . exports ) ;
2018-10-04 20:55:56 +00:00
const loader : any = loaderModule . exports ;
2015-11-13 13:39:38 +00:00
config . isBuild = true ;
2017-06-23 14:31:32 +00:00
config . paths = config . paths || { } ;
2018-10-10 09:02:10 +00:00
if ( ! config . paths [ 'vs/nls' ] ) {
config . paths [ 'vs/nls' ] = 'out-build/vs/nls.build' ;
}
if ( ! config . paths [ 'vs/css' ] ) {
config . paths [ 'vs/css' ] = 'out-build/vs/css.build' ;
}
2022-06-16 20:01:19 +00:00
config . buildForceInvokeFactory = config . buildForceInvokeFactory || { } ;
config . buildForceInvokeFactory [ 'vs/nls' ] = true ;
config . buildForceInvokeFactory [ 'vs/css' ] = true ;
2015-11-13 13:39:38 +00:00
loader . config ( config ) ;
2018-10-03 05:10:41 +00:00
loader ( [ 'require' ] , ( localRequire : any ) = > {
2022-06-16 20:01:19 +00:00
const resolvePath = ( entry : IExtraFile ) = > {
2022-06-16 21:05:10 +00:00
let r = localRequire . toUrl ( entry . path ) ;
if ( ! r . endsWith ( '.js' ) ) {
r += '.js' ;
2016-09-18 20:05:42 +00:00
}
2022-06-16 21:05:10 +00:00
// avoid packaging the build version of plugins:
r = r . replace ( 'vs/nls.build.js' , 'vs/nls.js' ) ;
r = r . replace ( 'vs/css.build.js' , 'vs/css.js' ) ;
2022-06-16 20:01:19 +00:00
return { path : r , amdModuleId : entry.amdModuleId } ;
2016-09-18 20:05:42 +00:00
} ;
2018-10-04 21:04:23 +00:00
for ( const module Id in entryPointsMap ) {
const entryPoint = entryPointsMap [ module Id ] ;
2016-09-18 20:05:42 +00:00
if ( entryPoint . append ) {
entryPoint . append = entryPoint . append . map ( resolvePath ) ;
}
if ( entryPoint . prepend ) {
entryPoint . prepend = entryPoint . prepend . map ( resolvePath ) ;
}
}
} ) ;
2016-06-08 22:22:59 +00:00
loader ( Object . keys ( allMentionedModulesMap ) , ( ) = > {
2018-10-04 21:04:23 +00:00
const module s = < IBuildModuleInfo [ ] > loader . getBuildInfo ( ) ;
const partialResult = emitEntryPoints ( module s , entryPointsMap ) ;
const cssInlinedResources = loader ( 'vs/css' ) . getInlinedResources ( ) ;
2016-06-07 16:46:41 +00:00
callback ( null , {
2016-06-08 12:54:13 +00:00
files : partialResult.files ,
cssInlinedResources : cssInlinedResources ,
bundleData : partialResult.bundleData
2016-06-07 16:46:41 +00:00
} ) ;
2018-10-03 05:10:41 +00:00
} , ( err : any ) = > callback ( err , null ) ) ;
2015-11-13 13:39:38 +00:00
}
2017-03-06 15:32:19 +00:00
function emitEntryPoints ( module s : IBuildModuleInfo [ ] , entryPoints : IEntryPointMap ) : IPartialBundleResult {
2018-10-04 21:04:23 +00:00
const module sMap : IBuildModuleInfoMap = { } ;
2017-03-06 15:32:19 +00:00
module s.forEach ( ( m : IBuildModuleInfo ) = > {
2015-11-13 13:39:38 +00:00
module sMap [ m . id ] = m ;
} ) ;
2018-10-04 21:04:23 +00:00
const module sGraph : IGraph = { } ;
2017-03-06 15:32:19 +00:00
module s.forEach ( ( m : IBuildModuleInfo ) = > {
2015-11-13 13:39:38 +00:00
module sGraph [ m . id ] = m . dependencies ;
} ) ;
2018-10-04 21:04:23 +00:00
const sortedModules = topologicalSort ( module sGraph ) ;
2015-11-13 13:39:38 +00:00
let result : IConcatFile [ ] = [ ] ;
2018-10-04 21:04:23 +00:00
const usedPlugins : IPluginMap = { } ;
const bundleData : IBundleData = {
2016-06-08 12:54:13 +00:00
graph : modulesGraph ,
bundles : { }
} ;
2015-11-13 13:39:38 +00:00
2017-03-06 15:32:19 +00:00
Object . keys ( entryPoints ) . forEach ( ( module ToBundle : string ) = > {
2018-10-04 21:04:23 +00:00
const info = entryPoints [ module ToBundle ] ;
const rootNodes = [ module ToBundle ] . concat ( info . include || [ ] ) ;
const allDependencies = visit ( rootNodes , module sGraph ) ;
const excludes : string [ ] = [ 'require' , 'exports' , 'module' ] . concat ( info . exclude || [ ] ) ;
2015-11-13 13:39:38 +00:00
2017-03-06 15:32:19 +00:00
excludes . forEach ( ( excludeRoot : string ) = > {
2018-10-04 21:04:23 +00:00
const allExcludes = visit ( [ excludeRoot ] , module sGraph ) ;
2017-03-06 15:32:19 +00:00
Object . keys ( allExcludes ) . forEach ( ( exclude : string ) = > {
2015-11-13 13:39:38 +00:00
delete allDependencies [ exclude ] ;
} ) ;
} ) ;
2018-10-04 21:04:23 +00:00
const includedModules = sortedModules . filter ( ( module : string ) = > {
2015-11-13 13:39:38 +00:00
return allDependencies [ module ] ;
} ) ;
2016-06-08 12:54:13 +00:00
bundleData . bundles [ module ToBundle ] = includedModules ;
2018-10-04 21:04:23 +00:00
const res = emitEntryPoint (
2016-09-18 20:05:42 +00:00
module sMap ,
module sGraph ,
module ToBundle ,
includedModules ,
2018-10-02 23:38:00 +00:00
info . prepend || [ ] ,
info . append || [ ] ,
2016-09-18 20:05:42 +00:00
info . dest
) ;
2015-11-13 13:39:38 +00:00
result = result . concat ( res . files ) ;
2018-10-04 21:04:23 +00:00
for ( const pluginName in res . usedPlugins ) {
2015-11-13 13:39:38 +00:00
usedPlugins [ pluginName ] = usedPlugins [ pluginName ] || res . usedPlugins [ pluginName ] ;
}
} ) ;
2017-03-06 15:32:19 +00:00
Object . keys ( usedPlugins ) . forEach ( ( pluginName : string ) = > {
2018-10-04 21:04:23 +00:00
const plugin = usedPlugins [ pluginName ] ;
2015-11-13 13:39:38 +00:00
if ( typeof plugin . finishBuild === 'function' ) {
2018-10-04 21:04:23 +00:00
const write = ( filename : string , contents : string ) = > {
2015-11-13 13:39:38 +00:00
result . push ( {
dest : filename ,
sources : [ {
path : null ,
contents : contents
} ]
} ) ;
} ;
plugin . finishBuild ( write ) ;
}
} ) ;
2016-06-08 12:54:13 +00:00
return {
2017-04-14 12:18:46 +00:00
// TODO@TS 2.1.2
2017-04-28 09:02:17 +00:00
files : extractStrings ( removeDuplicateTSBoilerplate ( result ) ) ,
2016-06-08 12:54:13 +00:00
bundleData : bundleData
} ;
}
2017-03-06 15:32:19 +00:00
function extractStrings ( destFiles : IConcatFile [ ] ) : IConcatFile [ ] {
2018-10-04 21:04:23 +00:00
const parseDefineCall = ( module Match : string , depsMatch : string ) = > {
const module = module Match.replace ( /^"|"$/g , '' ) ;
2016-06-08 16:08:31 +00:00
let deps = depsMatch . split ( ',' ) ;
deps = deps . map ( ( dep ) = > {
dep = dep . trim ( ) ;
dep = dep . replace ( /^"|"$/g , '' ) ;
dep = dep . replace ( /^'|'$/g , '' ) ;
2018-10-02 23:38:00 +00:00
let prefix : string | null = null ;
let _path : string | null = null ;
2018-10-04 21:04:23 +00:00
const pieces = dep . split ( '!' ) ;
2016-06-08 16:08:31 +00:00
if ( pieces . length > 1 ) {
prefix = pieces [ 0 ] + '!' ;
_path = pieces [ 1 ] ;
} else {
prefix = '' ;
_path = pieces [ 0 ] ;
}
if ( /^\.\// . test ( _path ) || /^\.\.\// . test ( _path ) ) {
2018-10-04 21:04:23 +00:00
const res = path . join ( path . dirname ( module ) , _path ) . replace ( /\\/g , '/' ) ;
2016-06-08 16:08:31 +00:00
return prefix + res ;
}
return prefix + _path ;
} ) ;
return {
module : module ,
deps : deps
} ;
} ;
2018-10-03 22:52:36 +00:00
destFiles . forEach ( ( destFile ) = > {
2016-06-08 16:08:31 +00:00
if ( ! /\.js$/ . test ( destFile . dest ) ) {
return ;
}
if ( /\.nls\.js$/ . test ( destFile . dest ) ) {
return ;
}
// Do one pass to record the usage counts for each module id
2022-02-02 13:34:41 +00:00
const useCounts : { [ module Id : string ] : number } = { } ;
2016-06-08 16:08:31 +00:00
destFile . sources . forEach ( ( source ) = > {
2018-10-04 21:04:23 +00:00
const matches = source . contents . match ( /define\(("[^"]+"),\s*\[(((, )?("|')[^"']+("|'))+)\]/ ) ;
2016-06-08 16:08:31 +00:00
if ( ! matches ) {
return ;
}
2018-10-04 21:04:23 +00:00
const defineCall = parseDefineCall ( matches [ 1 ] , matches [ 2 ] ) ;
2016-06-08 16:08:31 +00:00
useCounts [ defineCall . module ] = ( useCounts [ defineCall . module ] || 0 ) + 1 ;
defineCall . deps . forEach ( ( dep ) = > {
useCounts [ dep ] = ( useCounts [ dep ] || 0 ) + 1 ;
} ) ;
} ) ;
2018-10-04 21:04:23 +00:00
const sortedByUseModules = Object . keys ( useCounts ) ;
2016-06-08 16:08:31 +00:00
sortedByUseModules . sort ( ( a , b ) = > {
return useCounts [ b ] - useCounts [ a ] ;
} ) ;
2022-02-02 13:34:41 +00:00
const replacementMap : { [ module Id : string ] : number } = { } ;
2016-06-08 16:08:31 +00:00
sortedByUseModules . forEach ( ( module , index ) = > {
replacementMap [ module ] = index ;
} ) ;
destFile . sources . forEach ( ( source ) = > {
source . contents = source . contents . replace ( /define\(("[^"]+"),\s*\[(((, )?("|')[^"']+("|'))+)\]/ , ( _ , module Match , depsMatch ) = > {
2018-10-04 21:04:23 +00:00
const defineCall = parseDefineCall ( module Match , depsMatch ) ;
2016-09-07 15:25:21 +00:00
return ` define(__m[ ${ replacementMap [ defineCall . module ] } /* ${ defineCall . module } */], __M([ ${ defineCall . deps . map ( dep = > replacementMap [ dep ] + '/*' + dep + '*/' ) . join ( ',' ) } ]) ` ;
2016-06-08 16:08:31 +00:00
} ) ;
} ) ;
destFile . sources . unshift ( {
path : null ,
contents : [
'(function() {' ,
` var __m = ${ JSON . stringify ( sortedByUseModules ) } ; ` ,
` var __M = function(deps) { ` ,
` var result = []; ` ,
` for (var i = 0, len = deps.length; i < len; i++) { ` ,
` result[i] = __m[deps[i]]; ` ,
` } ` ,
` return result; ` ,
` }; `
] . join ( '\n' )
} ) ;
destFile . sources . push ( {
path : null ,
contents : '}).call(this);'
} ) ;
} ) ;
return destFiles ;
}
2017-03-06 15:32:19 +00:00
function removeDuplicateTSBoilerplate ( destFiles : IConcatFile [ ] ) : IConcatFile [ ] {
2016-06-08 12:54:13 +00:00
// Taken from typescript compiler => emitFiles
2018-10-04 21:04:23 +00:00
const BOILERPLATE = [
2017-04-28 09:02:17 +00:00
{ start : /^var __extends/ , end : /^}\)\(\);$/ } ,
2016-06-08 12:54:13 +00:00
{ start : /^var __assign/ , end : /^};$/ } ,
{ start : /^var __decorate/ , end : /^};$/ } ,
{ start : /^var __metadata/ , end : /^};$/ } ,
{ start : /^var __param/ , end : /^};$/ } ,
{ start : /^var __awaiter/ , end : /^};$/ } ,
2018-02-15 11:58:35 +00:00
{ start : /^var __generator/ , end : /^};$/ } ,
2016-06-08 12:54:13 +00:00
] ;
destFiles . forEach ( ( destFile ) = > {
2018-10-04 21:04:23 +00:00
const SEEN_BOILERPLATE : boolean [ ] = [ ] ;
2016-06-08 12:54:13 +00:00
destFile . sources . forEach ( ( source ) = > {
2018-10-04 21:04:23 +00:00
const lines = source . contents . split ( /\r\n|\n|\r/ ) ;
const newLines : string [ ] = [ ] ;
2016-06-08 12:54:13 +00:00
let IS_REMOVING_BOILERPLATE = false , END_BOILERPLATE : RegExp ;
for ( let i = 0 ; i < lines . length ; i ++ ) {
2018-10-04 21:04:23 +00:00
const line = lines [ i ] ;
2016-06-08 12:54:13 +00:00
if ( IS_REMOVING_BOILERPLATE ) {
newLines . push ( '' ) ;
2018-10-02 23:38:00 +00:00
if ( END_BOILERPLATE ! . test ( line ) ) {
2016-06-08 12:54:13 +00:00
IS_REMOVING_BOILERPLATE = false ;
}
} else {
for ( let j = 0 ; j < BOILERPLATE . length ; j ++ ) {
2018-10-04 21:04:23 +00:00
const boilerplate = BOILERPLATE [ j ] ;
2016-06-08 12:54:13 +00:00
if ( boilerplate . start . test ( line ) ) {
if ( SEEN_BOILERPLATE [ j ] ) {
IS_REMOVING_BOILERPLATE = true ;
END_BOILERPLATE = boilerplate . end ;
} else {
SEEN_BOILERPLATE [ j ] = true ;
}
}
}
if ( IS_REMOVING_BOILERPLATE ) {
newLines . push ( '' ) ;
} else {
newLines . push ( line ) ;
}
}
}
source . contents = newLines . join ( '\n' ) ;
} ) ;
} ) ;
return destFiles ;
2015-11-13 13:39:38 +00:00
}
interface IPluginMap {
2017-03-06 15:32:19 +00:00
[ module Id : string ] : ILoaderPlugin ;
2015-11-13 13:39:38 +00:00
}
interface IEmitEntryPointResult {
files : IConcatFile [ ] ;
usedPlugins : IPluginMap ;
}
2016-09-18 20:05:42 +00:00
function emitEntryPoint (
2017-03-06 15:32:19 +00:00
module sMap : IBuildModuleInfoMap ,
deps : IGraph ,
entryPoint : string ,
includedModules : string [ ] ,
2022-06-16 20:01:19 +00:00
prepend : IExtraFile [ ] ,
append : IExtraFile [ ] ,
2018-10-02 23:38:00 +00:00
dest : string | undefined
2016-09-18 20:05:42 +00:00
) : IEmitEntryPointResult {
if ( ! dest ) {
dest = entryPoint + '.js' ;
}
2018-10-04 21:04:23 +00:00
const mainResult : IConcatFile = {
2017-03-06 15:32:19 +00:00
sources : [ ] ,
dest : dest
} ,
2015-11-13 13:39:38 +00:00
results : IConcatFile [ ] = [ mainResult ] ;
2018-10-04 21:04:23 +00:00
const usedPlugins : IPluginMap = { } ;
const getLoaderPlugin = ( pluginName : string ) : ILoaderPlugin = > {
2015-11-13 13:39:38 +00:00
if ( ! usedPlugins [ pluginName ] ) {
usedPlugins [ pluginName ] = module sMap [ pluginName ] . exports ;
}
return usedPlugins [ pluginName ] ;
2016-03-07 11:52:53 +00:00
} ;
2015-11-13 13:39:38 +00:00
2017-03-06 15:32:19 +00:00
includedModules . forEach ( ( c : string ) = > {
2018-10-04 21:04:23 +00:00
const bangIndex = c . indexOf ( '!' ) ;
2015-11-13 13:39:38 +00:00
if ( bangIndex >= 0 ) {
2018-10-04 21:04:23 +00:00
const pluginName = c . substr ( 0 , bangIndex ) ;
const plugin = getLoaderPlugin ( pluginName ) ;
2015-11-13 13:39:38 +00:00
mainResult . sources . push ( emitPlugin ( entryPoint , plugin , pluginName , c . substr ( bangIndex + 1 ) ) ) ;
return ;
}
2018-10-04 21:04:23 +00:00
const module = module sMap [ c ] ;
2015-11-13 13:39:38 +00:00
if ( module .path === 'empty:' ) {
return ;
}
2018-10-04 21:04:23 +00:00
const contents = readFileAndRemoveBOM ( module .path ) ;
2015-11-13 13:39:38 +00:00
if ( module .shim ) {
mainResult . sources . push ( emitShimmedModule ( c , deps [ c ] , module .shim , module .path , contents ) ) ;
2022-06-15 12:51:27 +00:00
} else if ( module .defineLocation ) {
2018-10-03 22:52:36 +00:00
mainResult . sources . push ( emitNamedModule ( c , module .defineLocation , module .path , contents ) ) ;
2022-06-15 12:51:27 +00:00
} else {
const module Copy = {
id : module.id ,
path : module.path ,
defineLocation : module.defineLocation ,
dependencies : module.dependencies
} ;
throw new Error ( ` Cannot bundle module ' ${ module .id } ' for entry point ' ${ entryPoint } ' because it has no shim and it lacks a defineLocation: ${ JSON . stringify ( module Copy ) } ` ) ;
2015-11-13 13:39:38 +00:00
}
} ) ;
2017-03-06 15:32:19 +00:00
Object . keys ( usedPlugins ) . forEach ( ( pluginName : string ) = > {
2018-10-04 21:04:23 +00:00
const plugin = usedPlugins [ pluginName ] ;
2015-11-13 13:39:38 +00:00
if ( typeof plugin . writeFile === 'function' ) {
2018-10-04 21:04:23 +00:00
const req : ILoaderPluginReqFunc = < any > ( ( ) = > {
2015-11-13 13:39:38 +00:00
throw new Error ( 'no-no!' ) ;
} ) ;
req . toUrl = something = > something ;
2018-10-04 21:04:23 +00:00
const write = ( filename : string , contents : string ) = > {
2015-11-13 13:39:38 +00:00
results . push ( {
dest : filename ,
sources : [ {
path : null ,
contents : contents
} ]
} ) ;
} ;
plugin . writeFile ( pluginName , entryPoint , req , write , { } ) ;
}
} ) ;
2022-06-16 20:01:19 +00:00
const toIFile = ( entry : IExtraFile ) : IFile = > {
let contents = readFileAndRemoveBOM ( entry . path ) ;
if ( entry . amdModuleId ) {
contents = contents . replace ( /^define\(/m , ` define(" ${ entry . amdModuleId } ", ` ) ;
}
2016-09-18 20:05:42 +00:00
return {
2022-06-16 20:01:19 +00:00
path : entry.path ,
2016-09-18 20:05:42 +00:00
contents : contents
} ;
} ;
2018-10-04 21:04:23 +00:00
const toPrepend = ( prepend || [ ] ) . map ( toIFile ) ;
const toAppend = ( append || [ ] ) . map ( toIFile ) ;
2016-09-18 20:05:42 +00:00
mainResult . sources = toPrepend . concat ( mainResult . sources ) . concat ( toAppend ) ;
2015-11-13 13:39:38 +00:00
return {
files : results ,
usedPlugins : usedPlugins
} ;
}
2017-03-06 15:32:19 +00:00
function readFileAndRemoveBOM ( path : string ) : string {
2018-10-04 20:55:56 +00:00
const BOM_CHAR_CODE = 65279 ;
let contents = fs . readFileSync ( path , 'utf8' ) ;
2015-11-13 13:39:38 +00:00
// Remove BOM
if ( contents . charCodeAt ( 0 ) === BOM_CHAR_CODE ) {
contents = contents . substring ( 1 ) ;
}
return contents ;
}
2017-03-06 15:32:19 +00:00
function emitPlugin ( entryPoint : string , plugin : ILoaderPlugin , pluginName : string , module Name : string ) : IFile {
2015-11-13 13:39:38 +00:00
let result = '' ;
if ( typeof plugin . write === 'function' ) {
2018-10-04 21:04:23 +00:00
const write : ILoaderPluginWriteFunc = < any > ( ( what : string ) = > {
2015-11-13 13:39:38 +00:00
result += what ;
} ) ;
write . getEntryPoint = ( ) = > {
return entryPoint ;
} ;
2017-03-06 15:32:19 +00:00
write . asModule = ( module Id : string , code : string ) = > {
code = code . replace ( /^define\(/ , 'define("' + module Id + '",' ) ;
2015-11-13 13:39:38 +00:00
result += code ;
} ;
plugin . write ( pluginName , module Name , write ) ;
}
return {
path : null ,
contents : result
} ;
}
2018-10-03 22:52:36 +00:00
function emitNamedModule ( module Id : string , defineCallPosition : IPosition , path : string , contents : string ) : IFile {
2015-11-13 13:39:38 +00:00
// `defineCallPosition` is the position in code: |define()
2018-10-04 21:04:23 +00:00
const defineCallOffset = positionToOffset ( contents , defineCallPosition . line , defineCallPosition . col ) ;
2015-11-13 13:39:38 +00:00
// `parensOffset` is the position in code: define|()
2018-10-04 21:04:23 +00:00
const parensOffset = contents . indexOf ( '(' , defineCallOffset ) ;
2015-11-13 13:39:38 +00:00
2018-10-04 21:04:23 +00:00
const insertStr = '"' + module Id + '", ' ;
2015-11-13 13:39:38 +00:00
return {
path : path ,
contents : contents.substr ( 0 , parensOffset + 1 ) + insertStr + contents . substr ( parensOffset + 1 )
} ;
}
2017-03-06 15:32:19 +00:00
function emitShimmedModule ( module Id : string , myDeps : string [ ] , factory : string , path : string , contents : string ) : IFile {
2018-10-04 21:04:23 +00:00
const strDeps = ( myDeps . length > 0 ? '"' + myDeps . join ( '", "' ) + '"' : '' ) ;
const strDefine = 'define("' + module Id + '", [' + strDeps + '], ' + factory + ');' ;
2015-11-13 13:39:38 +00:00
return {
path : path ,
contents : contents + '\n;\n' + strDefine
} ;
}
/ * *
* Convert a position ( line :col ) to ( offset ) in string ` str `
* /
2017-03-06 15:32:19 +00:00
function positionToOffset ( str : string , desiredLine : number , desiredCol : number ) : number {
2015-11-13 13:39:38 +00:00
if ( desiredLine === 1 ) {
return desiredCol - 1 ;
}
2018-10-04 21:04:23 +00:00
let line = 1 ;
let lastNewLineOffset = - 1 ;
2015-11-13 13:39:38 +00:00
do {
if ( desiredLine === line ) {
return lastNewLineOffset + 1 + desiredCol - 1 ;
}
lastNewLineOffset = str . indexOf ( '\n' , lastNewLineOffset + 1 ) ;
line ++ ;
} while ( lastNewLineOffset >= 0 ) ;
return - 1 ;
}
/ * *
* Return a set of reachable nodes in ` graph ` starting from ` rootNodes `
* /
2017-03-06 15:32:19 +00:00
function visit ( rootNodes : string [ ] , graph : IGraph ) : INodeSet {
2018-10-04 21:04:23 +00:00
const result : INodeSet = { } ;
const queue = rootNodes ;
2015-11-13 13:39:38 +00:00
rootNodes . forEach ( ( node ) = > {
result [ node ] = true ;
} ) ;
while ( queue . length > 0 ) {
2018-10-04 21:04:23 +00:00
const el = queue . shift ( ) ;
const myEdges = graph [ el ! ] || [ ] ;
2015-11-13 13:39:38 +00:00
myEdges . forEach ( ( toNode ) = > {
if ( ! result [ toNode ] ) {
result [ toNode ] = true ;
queue . push ( toNode ) ;
}
} ) ;
}
return result ;
}
/ * *
* Perform a topological sort on ` graph `
* /
2017-03-06 15:32:19 +00:00
function topologicalSort ( graph : IGraph ) : string [ ] {
2015-11-13 13:39:38 +00:00
2018-10-04 21:04:23 +00:00
const allNodes : INodeSet = { } ,
2022-02-02 13:34:41 +00:00
outgoingEdgeCount : { [ node : string ] : number } = { } ,
2017-03-06 15:32:19 +00:00
inverseEdges : IGraph = { } ;
2015-11-13 13:39:38 +00:00
2017-03-06 15:32:19 +00:00
Object . keys ( graph ) . forEach ( ( fromNode : string ) = > {
2015-11-13 13:39:38 +00:00
allNodes [ fromNode ] = true ;
outgoingEdgeCount [ fromNode ] = graph [ fromNode ] . length ;
graph [ fromNode ] . forEach ( ( toNode ) = > {
allNodes [ toNode ] = true ;
outgoingEdgeCount [ toNode ] = outgoingEdgeCount [ toNode ] || 0 ;
inverseEdges [ toNode ] = inverseEdges [ toNode ] || [ ] ;
inverseEdges [ toNode ] . push ( fromNode ) ;
} ) ;
} ) ;
// https://en.wikipedia.org/wiki/Topological_sorting
2018-10-04 21:04:23 +00:00
const S : string [ ] = [ ] ,
2015-11-13 13:39:38 +00:00
L : string [ ] = [ ] ;
2017-03-06 15:32:19 +00:00
Object . keys ( allNodes ) . forEach ( ( node : string ) = > {
2015-11-13 13:39:38 +00:00
if ( outgoingEdgeCount [ node ] === 0 ) {
delete outgoingEdgeCount [ node ] ;
S . push ( node ) ;
}
} ) ;
while ( S . length > 0 ) {
// Ensure the exact same order all the time with the same inputs
S . sort ( ) ;
2018-10-04 21:04:23 +00:00
const n : string = S . shift ( ) ! ;
2015-11-13 13:39:38 +00:00
L . push ( n ) ;
2018-10-04 21:04:23 +00:00
const myInverseEdges = inverseEdges [ n ] || [ ] ;
2017-03-06 15:32:19 +00:00
myInverseEdges . forEach ( ( m : string ) = > {
2015-11-13 13:39:38 +00:00
outgoingEdgeCount [ m ] -- ;
if ( outgoingEdgeCount [ m ] === 0 ) {
delete outgoingEdgeCount [ m ] ;
S . push ( m ) ;
}
} ) ;
}
if ( Object . keys ( outgoingEdgeCount ) . length > 0 ) {
throw new Error ( 'Cannot do topological sort on cyclic graph, remaining nodes: ' + Object . keys ( outgoingEdgeCount ) ) ;
}
return L ;
}