2013-01-31 01:49:25 +08:00
/ *
MIT License http : //www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @ sokra
* /
2017-01-04 13:53:37 +08:00
"use strict" ;
2013-01-31 01:49:25 +08:00
2017-01-04 13:53:37 +08:00
const SourceMapConsumer = require ( "source-map" ) . SourceMapConsumer ;
const SourceMapSource = require ( "webpack-sources" ) . SourceMapSource ;
const RawSource = require ( "webpack-sources" ) . RawSource ;
UglifyJsPlugin: extract comments to separate file
License comments use up a lot of space, especially when using many small
libraries with large license blocks. With this addition, you can extract
all license comments to a separate file and remove them from the bundle
files. A small banner points to the file containing all license
information such that the user can find it if needed.
We add a new option extractComments to the UglifyJsPlugin.
It can be omitted, then the behavior does not change, or it can be:
- true: All comments that normally would be preserved by the comments
option will be moved to a separate file. If the original file is
named foo.js, then the comments will be stored to foo.js.LICENSE
- regular expression (given as RegExp or string) or a function
(astNode, comment) -> boolean: All comments that match the given
expression (resp. are evaluated to true by the function) will be
extracted to the separate file. The comments option specifies
whether the comment will be preserved, i.e. it is possible to
preserve some comments (e.g. annotations) while extracting others or
even preserving comments that have been extracted.
- an object consisting of the following keys, all optional:
- condition: regular expression or function (see previous point)
- file: The file where the extracted comments will be stored. Can be
either a string (filename) or function (string) -> string which
will be given the original filename. Default is to append the
suffix .LICENSE to the original filename.
- banner: The banner text that points to the extracted file and will
be added on top of the original file. will be added to the
original file. Can be false (no banner), a string, or a function
(string) -> string that will be called with the filename where
extracted comments have been stored. Will be wrapped into comment.
Default: /*! For license information please see foo.js.LICENSE */
2017-01-30 07:43:09 +08:00
const ConcatSource = require ( "webpack-sources" ) . ConcatSource ;
2017-01-04 13:53:37 +08:00
const RequestShortener = require ( "../RequestShortener" ) ;
const ModuleFilenameHelpers = require ( "../ModuleFilenameHelpers" ) ;
const uglify = require ( "uglify-js" ) ;
class UglifyJsPlugin {
constructor ( options ) {
if ( typeof options !== "object" || Array . isArray ( options ) ) options = { } ;
if ( typeof options . compressor !== "undefined" ) options . compress = options . compressor ;
this . options = options ;
2013-01-31 01:49:25 +08:00
}
2014-10-30 18:29:33 +08:00
2017-01-04 13:53:37 +08:00
apply ( compiler ) {
2017-02-05 07:54:17 +08:00
const options = this . options ;
2017-01-04 13:53:37 +08:00
options . test = options . test || /\.js($|\?)/i ;
2017-02-04 21:32:22 +08:00
const warningsFilter = options . warningsFilter || ( ( ) => true ) ;
2014-10-30 18:29:33 +08:00
2017-02-05 07:54:17 +08:00
const requestShortener = new RequestShortener ( compiler . context ) ;
2017-01-04 13:53:37 +08:00
compiler . plugin ( "compilation" , ( compilation ) => {
if ( options . sourceMap ) {
compilation . plugin ( "build-module" , ( module ) => {
// to get detailed location info about errors
module . useSourceMap = true ;
2013-01-31 01:49:25 +08:00
} ) ;
2017-01-04 13:53:37 +08:00
}
compilation . plugin ( "optimize-chunk-assets" , ( chunks , callback ) => {
2017-02-05 07:54:17 +08:00
const files = [ ] ;
2017-01-04 13:53:37 +08:00
chunks . forEach ( ( chunk ) => files . push . apply ( files , chunk . files ) ) ;
files . push . apply ( files , compilation . additionalChunkAssets ) ;
2017-02-05 07:52:17 +08:00
const filterdFiles = files . filter ( ModuleFilenameHelpers . matchObject . bind ( undefined , options ) ) ;
filterdFiles . forEach ( ( file ) => {
2017-02-05 07:54:17 +08:00
const oldWarnFunction = uglify . AST _Node . warn _function ;
const warnings = [ ] ;
2017-01-04 13:53:37 +08:00
let sourceMap ;
try {
2017-02-05 07:54:17 +08:00
const asset = compilation . assets [ file ] ;
2017-01-04 13:53:37 +08:00
if ( asset . _ _UglifyJsPlugin ) {
compilation . assets [ file ] = asset . _ _UglifyJsPlugin ;
return ;
}
let input ;
let inputSourceMap ;
if ( options . sourceMap ) {
if ( asset . sourceAndMap ) {
2017-02-05 07:54:17 +08:00
const sourceAndMap = asset . sourceAndMap ( ) ;
2017-01-04 13:53:37 +08:00
inputSourceMap = sourceAndMap . map ;
input = sourceAndMap . source ;
} else {
inputSourceMap = asset . map ( ) ;
input = asset . source ( ) ;
}
sourceMap = new SourceMapConsumer ( inputSourceMap ) ;
uglify . AST _Node . warn _function = ( warning ) => { // eslint-disable-line camelcase
2017-02-05 07:54:17 +08:00
const match = /\[.+:([0-9]+),([0-9]+)\]/ . exec ( warning ) ;
const line = + match [ 1 ] ;
const column = + match [ 2 ] ;
const original = sourceMap . originalPositionFor ( {
2017-01-04 13:53:37 +08:00
line : line ,
column : column
} ) ;
if ( ! original || ! original . source || original . source === file ) return ;
2017-02-04 21:32:22 +08:00
if ( ! warningsFilter ( original . source ) ) return ;
2017-01-04 13:53:37 +08:00
warnings . push ( warning . replace ( /\[.+:([0-9]+),([0-9]+)\]/ , "" ) +
"[" + requestShortener . shorten ( original . source ) + ":" + original . line + "," + original . column + "]" ) ;
} ;
2015-04-03 18:38:56 +08:00
} else {
2016-01-19 08:52:28 +08:00
input = asset . source ( ) ;
2017-01-04 13:53:37 +08:00
uglify . AST _Node . warn _function = ( warning ) => { // eslint-disable-line camelcase
warnings . push ( warning ) ;
} ;
}
uglify . base54 . reset ( ) ;
let ast = uglify . parse ( input , {
filename : file
} ) ;
if ( options . compress !== false ) {
ast . figure _out _scope ( ) ;
2017-02-05 07:54:17 +08:00
const compress = uglify . Compressor ( options . compress || {
2017-01-04 13:53:37 +08:00
warnings : false
} ) ; // eslint-disable-line new-cap
2017-03-05 10:31:39 +08:00
ast = compress . compress ( ast ) ;
2017-01-04 13:53:37 +08:00
}
if ( options . mangle !== false ) {
ast . figure _out _scope ( options . mangle || { } ) ;
ast . compute _char _frequency ( options . mangle || { } ) ;
ast . mangle _names ( options . mangle || { } ) ;
if ( options . mangle && options . mangle . props ) {
uglify . mangle _properties ( ast , options . mangle . props ) ;
}
2015-04-03 18:38:56 +08:00
}
2017-02-05 07:54:17 +08:00
const output = { } ;
2017-01-04 13:53:37 +08:00
output . comments = Object . prototype . hasOwnProperty . call ( options , "comments" ) ? options . comments : /^\**!|@preserve|@license/ ;
output . beautify = options . beautify ;
for ( let k in options . output ) {
output [ k ] = options . output [ k ] ;
}
UglifyJsPlugin: extract comments to separate file
License comments use up a lot of space, especially when using many small
libraries with large license blocks. With this addition, you can extract
all license comments to a separate file and remove them from the bundle
files. A small banner points to the file containing all license
information such that the user can find it if needed.
We add a new option extractComments to the UglifyJsPlugin.
It can be omitted, then the behavior does not change, or it can be:
- true: All comments that normally would be preserved by the comments
option will be moved to a separate file. If the original file is
named foo.js, then the comments will be stored to foo.js.LICENSE
- regular expression (given as RegExp or string) or a function
(astNode, comment) -> boolean: All comments that match the given
expression (resp. are evaluated to true by the function) will be
extracted to the separate file. The comments option specifies
whether the comment will be preserved, i.e. it is possible to
preserve some comments (e.g. annotations) while extracting others or
even preserving comments that have been extracted.
- an object consisting of the following keys, all optional:
- condition: regular expression or function (see previous point)
- file: The file where the extracted comments will be stored. Can be
either a string (filename) or function (string) -> string which
will be given the original filename. Default is to append the
suffix .LICENSE to the original filename.
- banner: The banner text that points to the extracted file and will
be added on top of the original file. will be added to the
original file. Can be false (no banner), a string, or a function
(string) -> string that will be called with the filename where
extracted comments have been stored. Will be wrapped into comment.
Default: /*! For license information please see foo.js.LICENSE */
2017-01-30 07:43:09 +08:00
const extractedComments = [ ] ;
if ( options . extractComments ) {
const condition = { } ;
if ( typeof options . extractComments === "string" || options . extractComments instanceof RegExp ) {
// extractComments specifies the extract condition and output.comments specifies the preserve condition
condition . preserve = output . comments ;
condition . extract = options . extractComments ;
} else if ( Object . prototype . hasOwnProperty . call ( options . extractComments , "condition" ) ) {
// Extract condition is given in extractComments.condition
condition . preserve = output . comments ;
condition . extract = options . extractComments . condition ;
} else {
// No extract condition is given. Extract comments that match output.comments instead of preserving them
condition . preserve = false ;
condition . extract = output . comments ;
}
// Ensure that both conditions are functions
[ "preserve" , "extract" ] . forEach ( key => {
switch ( typeof condition [ key ] ) {
case "boolean" :
var b = condition [ key ] ;
condition [ key ] = ( ) => b ;
2017-02-11 06:16:20 +08:00
break ;
case "function" :
UglifyJsPlugin: extract comments to separate file
License comments use up a lot of space, especially when using many small
libraries with large license blocks. With this addition, you can extract
all license comments to a separate file and remove them from the bundle
files. A small banner points to the file containing all license
information such that the user can find it if needed.
We add a new option extractComments to the UglifyJsPlugin.
It can be omitted, then the behavior does not change, or it can be:
- true: All comments that normally would be preserved by the comments
option will be moved to a separate file. If the original file is
named foo.js, then the comments will be stored to foo.js.LICENSE
- regular expression (given as RegExp or string) or a function
(astNode, comment) -> boolean: All comments that match the given
expression (resp. are evaluated to true by the function) will be
extracted to the separate file. The comments option specifies
whether the comment will be preserved, i.e. it is possible to
preserve some comments (e.g. annotations) while extracting others or
even preserving comments that have been extracted.
- an object consisting of the following keys, all optional:
- condition: regular expression or function (see previous point)
- file: The file where the extracted comments will be stored. Can be
either a string (filename) or function (string) -> string which
will be given the original filename. Default is to append the
suffix .LICENSE to the original filename.
- banner: The banner text that points to the extracted file and will
be added on top of the original file. will be added to the
original file. Can be false (no banner), a string, or a function
(string) -> string that will be called with the filename where
extracted comments have been stored. Will be wrapped into comment.
Default: /*! For license information please see foo.js.LICENSE */
2017-01-30 07:43:09 +08:00
break ;
case "string" :
if ( condition [ key ] === "all" ) {
condition [ key ] = ( ) => true ;
break ;
}
2017-02-11 06:16:20 +08:00
var regex = new RegExp ( condition [ key ] ) ;
condition [ key ] = ( astNode , comment ) => regex . test ( comment . value ) ;
break ;
default :
regex = condition [ key ] ;
UglifyJsPlugin: extract comments to separate file
License comments use up a lot of space, especially when using many small
libraries with large license blocks. With this addition, you can extract
all license comments to a separate file and remove them from the bundle
files. A small banner points to the file containing all license
information such that the user can find it if needed.
We add a new option extractComments to the UglifyJsPlugin.
It can be omitted, then the behavior does not change, or it can be:
- true: All comments that normally would be preserved by the comments
option will be moved to a separate file. If the original file is
named foo.js, then the comments will be stored to foo.js.LICENSE
- regular expression (given as RegExp or string) or a function
(astNode, comment) -> boolean: All comments that match the given
expression (resp. are evaluated to true by the function) will be
extracted to the separate file. The comments option specifies
whether the comment will be preserved, i.e. it is possible to
preserve some comments (e.g. annotations) while extracting others or
even preserving comments that have been extracted.
- an object consisting of the following keys, all optional:
- condition: regular expression or function (see previous point)
- file: The file where the extracted comments will be stored. Can be
either a string (filename) or function (string) -> string which
will be given the original filename. Default is to append the
suffix .LICENSE to the original filename.
- banner: The banner text that points to the extracted file and will
be added on top of the original file. will be added to the
original file. Can be false (no banner), a string, or a function
(string) -> string that will be called with the filename where
extracted comments have been stored. Will be wrapped into comment.
Default: /*! For license information please see foo.js.LICENSE */
2017-01-30 07:43:09 +08:00
condition [ key ] = ( astNode , comment ) => regex . test ( comment . value ) ;
}
} ) ;
// Redefine the comments function to extract and preserve
// comments according to the two conditions
output . comments = ( astNode , comment ) => {
if ( condition . extract ( astNode , comment ) ) {
extractedComments . push (
comment . type === "comment2" ? "/*" + comment . value + "*/" : "//" + comment . value
) ;
}
return condition . preserve ( astNode , comment ) ;
} ;
}
2017-01-04 13:53:37 +08:00
let map ;
if ( options . sourceMap ) {
map = uglify . SourceMap ( { // eslint-disable-line new-cap
file : file ,
root : ""
2014-10-31 19:46:18 +08:00
} ) ;
2017-01-04 13:53:37 +08:00
output . source _map = map ; // eslint-disable-line camelcase
2016-02-06 17:06:48 +08:00
}
2017-02-05 07:54:02 +08:00
const stream = uglify . OutputStream ( output ) ; // eslint-disable-line new-cap
2017-01-04 13:53:37 +08:00
ast . print ( stream ) ;
if ( map ) map = map + "" ;
2017-02-05 07:54:02 +08:00
const stringifiedStream = stream + "" ;
2017-02-11 06:16:20 +08:00
let outputSource = ( map ?
2017-02-05 07:54:02 +08:00
new SourceMapSource ( stringifiedStream , file , JSON . parse ( map ) , input , inputSourceMap ) :
new RawSource ( stringifiedStream ) ) ;
UglifyJsPlugin: extract comments to separate file
License comments use up a lot of space, especially when using many small
libraries with large license blocks. With this addition, you can extract
all license comments to a separate file and remove them from the bundle
files. A small banner points to the file containing all license
information such that the user can find it if needed.
We add a new option extractComments to the UglifyJsPlugin.
It can be omitted, then the behavior does not change, or it can be:
- true: All comments that normally would be preserved by the comments
option will be moved to a separate file. If the original file is
named foo.js, then the comments will be stored to foo.js.LICENSE
- regular expression (given as RegExp or string) or a function
(astNode, comment) -> boolean: All comments that match the given
expression (resp. are evaluated to true by the function) will be
extracted to the separate file. The comments option specifies
whether the comment will be preserved, i.e. it is possible to
preserve some comments (e.g. annotations) while extracting others or
even preserving comments that have been extracted.
- an object consisting of the following keys, all optional:
- condition: regular expression or function (see previous point)
- file: The file where the extracted comments will be stored. Can be
either a string (filename) or function (string) -> string which
will be given the original filename. Default is to append the
suffix .LICENSE to the original filename.
- banner: The banner text that points to the extracted file and will
be added on top of the original file. will be added to the
original file. Can be false (no banner), a string, or a function
(string) -> string that will be called with the filename where
extracted comments have been stored. Will be wrapped into comment.
Default: /*! For license information please see foo.js.LICENSE */
2017-01-30 07:43:09 +08:00
if ( extractedComments . length > 0 ) {
2017-02-11 06:16:20 +08:00
let commentsFile = options . extractComments . filename || file + ".LICENSE" ;
UglifyJsPlugin: extract comments to separate file
License comments use up a lot of space, especially when using many small
libraries with large license blocks. With this addition, you can extract
all license comments to a separate file and remove them from the bundle
files. A small banner points to the file containing all license
information such that the user can find it if needed.
We add a new option extractComments to the UglifyJsPlugin.
It can be omitted, then the behavior does not change, or it can be:
- true: All comments that normally would be preserved by the comments
option will be moved to a separate file. If the original file is
named foo.js, then the comments will be stored to foo.js.LICENSE
- regular expression (given as RegExp or string) or a function
(astNode, comment) -> boolean: All comments that match the given
expression (resp. are evaluated to true by the function) will be
extracted to the separate file. The comments option specifies
whether the comment will be preserved, i.e. it is possible to
preserve some comments (e.g. annotations) while extracting others or
even preserving comments that have been extracted.
- an object consisting of the following keys, all optional:
- condition: regular expression or function (see previous point)
- file: The file where the extracted comments will be stored. Can be
either a string (filename) or function (string) -> string which
will be given the original filename. Default is to append the
suffix .LICENSE to the original filename.
- banner: The banner text that points to the extracted file and will
be added on top of the original file. will be added to the
original file. Can be false (no banner), a string, or a function
(string) -> string that will be called with the filename where
extracted comments have been stored. Will be wrapped into comment.
Default: /*! For license information please see foo.js.LICENSE */
2017-01-30 07:43:09 +08:00
if ( typeof commentsFile === "function" ) {
commentsFile = commentsFile ( file ) ;
}
// Write extracted comments to commentsFile
const commentsSource = new RawSource ( extractedComments . join ( "\n\n" ) + "\n" ) ;
if ( commentsFile in compilation . assets ) {
// commentsFile already exists, append new comments...
if ( compilation . assets [ commentsFile ] instanceof ConcatSource ) {
compilation . assets [ commentsFile ] . add ( "\n" ) ;
compilation . assets [ commentsFile ] . add ( commentsSource ) ;
} else {
compilation . assets [ commentsFile ] = new ConcatSource (
compilation . assets [ commentsFile ] , "\n" , commentsSource
) ;
}
} else {
compilation . assets [ commentsFile ] = commentsSource ;
}
// Add a banner to the original file
if ( options . extractComments . banner !== false ) {
let banner = options . extractComments . banner || "For license information please see " + commentsFile ;
if ( typeof banner === "function" ) {
banner = banner ( commentsFile ) ;
}
if ( banner ) {
2017-02-11 06:16:20 +08:00
outputSource = new ConcatSource (
"/*! " + banner + " */\n" , outputSource
UglifyJsPlugin: extract comments to separate file
License comments use up a lot of space, especially when using many small
libraries with large license blocks. With this addition, you can extract
all license comments to a separate file and remove them from the bundle
files. A small banner points to the file containing all license
information such that the user can find it if needed.
We add a new option extractComments to the UglifyJsPlugin.
It can be omitted, then the behavior does not change, or it can be:
- true: All comments that normally would be preserved by the comments
option will be moved to a separate file. If the original file is
named foo.js, then the comments will be stored to foo.js.LICENSE
- regular expression (given as RegExp or string) or a function
(astNode, comment) -> boolean: All comments that match the given
expression (resp. are evaluated to true by the function) will be
extracted to the separate file. The comments option specifies
whether the comment will be preserved, i.e. it is possible to
preserve some comments (e.g. annotations) while extracting others or
even preserving comments that have been extracted.
- an object consisting of the following keys, all optional:
- condition: regular expression or function (see previous point)
- file: The file where the extracted comments will be stored. Can be
either a string (filename) or function (string) -> string which
will be given the original filename. Default is to append the
suffix .LICENSE to the original filename.
- banner: The banner text that points to the extracted file and will
be added on top of the original file. will be added to the
original file. Can be false (no banner), a string, or a function
(string) -> string that will be called with the filename where
extracted comments have been stored. Will be wrapped into comment.
Default: /*! For license information please see foo.js.LICENSE */
2017-01-30 07:43:09 +08:00
) ;
}
}
}
2017-02-11 06:16:20 +08:00
asset . _ _UglifyJsPlugin = compilation . assets [ file ] = outputSource ;
2017-01-04 13:53:37 +08:00
if ( warnings . length > 0 ) {
compilation . warnings . push ( new Error ( file + " from UglifyJs\n" + warnings . join ( "\n" ) ) ) ;
2013-12-17 07:53:22 +08:00
}
2017-01-04 13:53:37 +08:00
} catch ( err ) {
if ( err . line ) {
2017-02-05 07:54:17 +08:00
const original = sourceMap && sourceMap . originalPositionFor ( {
2017-01-04 13:53:37 +08:00
line : err . line ,
column : err . col
} ) ;
if ( original && original . source ) {
compilation . errors . push ( new Error ( file + " from UglifyJs\n" + err . message + " [" + requestShortener . shorten ( original . source ) + ":" + original . line + "," + original . column + "][" + file + ":" + err . line + "," + err . col + "]" ) ) ;
} else {
compilation . errors . push ( new Error ( file + " from UglifyJs\n" + err . message + " [" + file + ":" + err . line + "," + err . col + "]" ) ) ;
}
} else if ( err . msg ) {
compilation . errors . push ( new Error ( file + " from UglifyJs\n" + err . msg ) ) ;
} else
compilation . errors . push ( new Error ( file + " from UglifyJs\n" + err . stack ) ) ;
} finally {
uglify . AST _Node . warn _function = oldWarnFunction ; // eslint-disable-line camelcase
}
} ) ;
callback ( ) ;
2013-01-31 01:49:25 +08:00
} ) ;
} ) ;
2017-01-04 13:53:37 +08:00
}
}
module . exports = UglifyJsPlugin ;