Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F101980434
SideEffectsFlagPlugin.js
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Sat, Feb 15, 19:56
Size
9 KB
Mime Type
text/x-c++
Expires
Mon, Feb 17, 19:56 (2 d)
Engine
blob
Format
Raw Data
Handle
24231244
Attached To
rOACCT Open Access Compliance Check Tool (OACCT)
SideEffectsFlagPlugin.js
View Options
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict"
;
const
glob2regexp
=
require
(
"glob-to-regexp"
);
const
{
STAGE_DEFAULT
}
=
require
(
"../OptimizationStages"
);
const
HarmonyExportImportedSpecifierDependency
=
require
(
"../dependencies/HarmonyExportImportedSpecifierDependency"
);
const
HarmonyImportSpecifierDependency
=
require
(
"../dependencies/HarmonyImportSpecifierDependency"
);
const
formatLocation
=
require
(
"../formatLocation"
);
/** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("../Dependency")} Dependency */
/** @typedef {import("../Module")} Module */
/** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
/**
* @typedef {Object} ExportInModule
* @property {Module} module the module
* @property {string} exportName the name of the export
* @property {boolean} checked if the export is conditional
*/
/**
* @typedef {Object} ReexportInfo
* @property {Map<string, ExportInModule[]>} static
* @property {Map<Module, Set<string>>} dynamic
*/
/** @type {WeakMap<any, Map<string, RegExp>>} */
const
globToRegexpCache
=
new
WeakMap
();
/**
* @param {string} glob the pattern
* @param {Map<string, RegExp>} cache the glob to RegExp cache
* @returns {RegExp} a regular expression
*/
const
globToRegexp
=
(
glob
,
cache
)
=>
{
const
cacheEntry
=
cache
.
get
(
glob
);
if
(
cacheEntry
!==
undefined
)
return
cacheEntry
;
if
(
!
glob
.
includes
(
"/"
))
{
glob
=
`
**
/${glob}`;
}
const
baseRegexp
=
glob2regexp
(
glob
,
{
globstar
:
true
,
extended
:
true
});
const
regexpSource
=
baseRegexp
.
source
;
const
regexp
=
new
RegExp
(
"^(\\./)?"
+
regexpSource
.
slice
(
1
));
cache
.
set
(
glob
,
regexp
);
return
regexp
;
};
class
SideEffectsFlagPlugin
{
/**
* @param {boolean} analyseSource analyse source code for side effects
*/
constructor
(
analyseSource
=
true
)
{
this
.
_analyseSource
=
analyseSource
;
}
/**
* Apply the plugin
* @param {Compiler} compiler the compiler instance
* @returns {void}
*/
apply
(
compiler
)
{
let
cache
=
globToRegexpCache
.
get
(
compiler
.
root
);
if
(
cache
===
undefined
)
{
cache
=
new
Map
();
globToRegexpCache
.
set
(
compiler
.
root
,
cache
);
}
compiler
.
hooks
.
compilation
.
tap
(
"SideEffectsFlagPlugin"
,
(
compilation
,
{
normalModuleFactory
})
=>
{
const
moduleGraph
=
compilation
.
moduleGraph
;
normalModuleFactory
.
hooks
.
module
.
tap
(
"SideEffectsFlagPlugin"
,
(
module
,
data
)
=>
{
const
resolveData
=
data
.
resourceResolveData
;
if
(
resolveData
&&
resolveData
.
descriptionFileData
&&
resolveData
.
relativePath
)
{
const
sideEffects
=
resolveData
.
descriptionFileData
.
sideEffects
;
if
(
sideEffects
!==
undefined
)
{
if
(
module
.
factoryMeta
===
undefined
)
{
module
.
factoryMeta
=
{};
}
const
hasSideEffects
=
SideEffectsFlagPlugin
.
moduleHasSideEffects
(
resolveData
.
relativePath
,
sideEffects
,
cache
);
module
.
factoryMeta
.
sideEffectFree
=
!
hasSideEffects
;
}
}
return
module
;
}
);
normalModuleFactory
.
hooks
.
module
.
tap
(
"SideEffectsFlagPlugin"
,
(
module
,
data
)
=>
{
if
(
typeof
data
.
settings
.
sideEffects
===
"boolean"
)
{
if
(
module
.
factoryMeta
===
undefined
)
{
module
.
factoryMeta
=
{};
}
module
.
factoryMeta
.
sideEffectFree
=
!
data
.
settings
.
sideEffects
;
}
return
module
;
}
);
if
(
this
.
_analyseSource
)
{
/**
* @param {JavascriptParser} parser the parser
* @returns {void}
*/
const
parserHandler
=
parser
=>
{
let
sideEffectsStatement
;
parser
.
hooks
.
program
.
tap
(
"SideEffectsFlagPlugin"
,
()
=>
{
sideEffectsStatement
=
undefined
;
});
parser
.
hooks
.
statement
.
tap
(
{
name
:
"SideEffectsFlagPlugin"
,
stage
:
-
100
},
statement
=>
{
if
(
sideEffectsStatement
)
return
;
if
(
parser
.
scope
.
topLevelScope
!==
true
)
return
;
switch
(
statement
.
type
)
{
case
"ExpressionStatement"
:
if
(
!
parser
.
isPure
(
statement
.
expression
,
statement
.
range
[
0
])
)
{
sideEffectsStatement
=
statement
;
}
break
;
case
"IfStatement"
:
case
"WhileStatement"
:
case
"DoWhileStatement"
:
if
(
!
parser
.
isPure
(
statement
.
test
,
statement
.
range
[
0
]))
{
sideEffectsStatement
=
statement
;
}
// statement hook will be called for child statements too
break
;
case
"ForStatement"
:
if
(
!
parser
.
isPure
(
statement
.
init
,
statement
.
range
[
0
])
||
!
parser
.
isPure
(
statement
.
test
,
statement
.
init
?
statement
.
init
.
range
[
1
]
:
statement
.
range
[
0
]
)
||
!
parser
.
isPure
(
statement
.
update
,
statement
.
test
?
statement
.
test
.
range
[
1
]
:
statement
.
init
?
statement
.
init
.
range
[
1
]
:
statement
.
range
[
0
]
)
)
{
sideEffectsStatement
=
statement
;
}
// statement hook will be called for child statements too
break
;
case
"SwitchStatement"
:
if
(
!
parser
.
isPure
(
statement
.
discriminant
,
statement
.
range
[
0
])
)
{
sideEffectsStatement
=
statement
;
}
// statement hook will be called for child statements too
break
;
case
"VariableDeclaration"
:
case
"ClassDeclaration"
:
case
"FunctionDeclaration"
:
if
(
!
parser
.
isPure
(
statement
,
statement
.
range
[
0
]))
{
sideEffectsStatement
=
statement
;
}
break
;
case
"ExportNamedDeclaration"
:
case
"ExportDefaultDeclaration"
:
if
(
!
parser
.
isPure
(
statement
.
declaration
,
statement
.
range
[
0
])
)
{
sideEffectsStatement
=
statement
;
}
break
;
case
"LabeledStatement"
:
case
"BlockStatement"
:
// statement hook will be called for child statements too
break
;
case
"EmptyStatement"
:
break
;
case
"ExportAllDeclaration"
:
case
"ImportDeclaration"
:
// imports will be handled by the dependencies
break
;
default
:
sideEffectsStatement
=
statement
;
break
;
}
}
);
parser
.
hooks
.
finish
.
tap
(
"SideEffectsFlagPlugin"
,
()
=>
{
if
(
sideEffectsStatement
===
undefined
)
{
parser
.
state
.
module
.
buildMeta
.
sideEffectFree
=
true
;
}
else
{
const
{
loc
,
type
}
=
sideEffectsStatement
;
moduleGraph
.
getOptimizationBailout
(
parser
.
state
.
module
)
.
push
(
()
=>
`
Statement
(
$
{
type
})
with
side
effects
in
source
code
at
$
{
formatLocation
(
loc
)}
`
);
}
});
};
for
(
const
key
of
[
"javascript/auto"
,
"javascript/esm"
,
"javascript/dynamic"
])
{
normalModuleFactory
.
hooks
.
parser
.
for
(
key
)
.
tap
(
"SideEffectsFlagPlugin"
,
parserHandler
);
}
}
compilation
.
hooks
.
optimizeDependencies
.
tap
(
{
name
:
"SideEffectsFlagPlugin"
,
stage
:
STAGE_DEFAULT
},
modules
=>
{
const
logger
=
compilation
.
getLogger
(
"webpack.SideEffectsFlagPlugin"
);
logger
.
time
(
"update dependencies"
);
for
(
const
module
of
modules
)
{
if
(
module
.
getSideEffectsConnectionState
(
moduleGraph
)
===
false
)
{
const
exportsInfo
=
moduleGraph
.
getExportsInfo
(
module
);
for
(
const
connection
of
moduleGraph
.
getIncomingConnections
(
module
))
{
const
dep
=
connection
.
dependency
;
let
isReexport
;
if
(
(
isReexport
=
dep
instanceof
HarmonyExportImportedSpecifierDependency
)
||
(
dep
instanceof
HarmonyImportSpecifierDependency
&&
!
dep
.
namespaceObjectAsContext
)
)
{
// TODO improve for export *
if
(
isReexport
&&
dep
.
name
)
{
const
exportInfo
=
moduleGraph
.
getExportInfo
(
connection
.
originModule
,
dep
.
name
);
exportInfo
.
moveTarget
(
moduleGraph
,
({
module
})
=>
module
.
getSideEffectsConnectionState
(
moduleGraph
)
===
false
);
}
// TODO improve for nested imports
const
ids
=
dep
.
getIds
(
moduleGraph
);
if
(
ids
.
length
>
0
)
{
const
exportInfo
=
exportsInfo
.
getExportInfo
(
ids
[
0
]);
const
target
=
exportInfo
.
getTarget
(
moduleGraph
,
({
module
})
=>
module
.
getSideEffectsConnectionState
(
moduleGraph
)
===
false
);
if
(
!
target
)
continue
;
moduleGraph
.
updateModule
(
dep
,
target
.
module
);
moduleGraph
.
addExplanation
(
dep
,
"(skipped side-effect-free modules)"
);
dep
.
setIds
(
moduleGraph
,
target
.
export
?
[...
target
.
export
,
...
ids
.
slice
(
1
)]
:
ids
.
slice
(
1
)
);
}
}
}
}
}
logger
.
timeEnd
(
"update dependencies"
);
}
);
}
);
}
static
moduleHasSideEffects
(
moduleName
,
flagValue
,
cache
)
{
switch
(
typeof
flagValue
)
{
case
"undefined"
:
return
true
;
case
"boolean"
:
return
flagValue
;
case
"string"
:
return
globToRegexp
(
flagValue
,
cache
).
test
(
moduleName
);
case
"object"
:
return
flagValue
.
some
(
glob
=>
SideEffectsFlagPlugin
.
moduleHasSideEffects
(
moduleName
,
glob
,
cache
)
);
}
}
}
module
.
exports
=
SideEffectsFlagPlugin
;
Event Timeline
Log In to Comment