This repository has been archived by the owner on Dec 5, 2019. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 179
feat: add support for parallelization
&& caching
(options.parallel
)
#77
Merged
Merged
Changes from 27 commits
Commits
Show all changes
35 commits
Select commit
Hold shift + click to select a range
b9ff34b
refactor: minify()
aui 43208ec
add: Use multi-process and cache to speed up the build
aui 754037c
add: maxWorkers、cache、cacheDirectory
aui f3f0656
test: Disable caching
aui 04ea42b
Use multi-process and cache to speed up the build
aui 7e9c080
fix: cache bug
aui acd748b
test: Disable caching
aui aca16f4
test: uglify
aui 67ae3fb
merge: upstream/master
aui 8e50528
Keep the "WebPack-default" consistent
aui 744a435
refactor: options.parallel
aui 90ef4d4
refactor
aui 289f5d0
refactor: compute-cluster -> worker-farm
aui 2f63c88
fix: options.parallel
aui bd20b62
test: Disable caching
aui b2537a3
test: worker.js
aui 36f455e
test: coverage
aui 1a51733
test: fix
aui 24dcfb4
refactor
aui 44aff1c
refactor: options.parallel.workers
aui b944ffa
refactor: serialization
aui b7fe125
refactor: serialization
aui 48fee75
refactor: rename the internal variable
aui 5d1d3ec
test: options.parallel
aui 16a270e
refactor: remove `Date`
aui 68d6b58
refactor: serialization
aui fdea537
refactor: use eslint for serialization checks
aui 9ecb608
refactor: remove eslint
aui 50035c5
refactor: the default configuration does not use "parallel"
aui 29ec4b1
refactor: rename variable
aui 7051b8e
test: remove private API
aui cef8262
refactor: safeguard to not spawn more workers
aui 3a68612
add: options.parallel
aui 3aebef6
test: clean up
aui e53ae99
refactor: buildError
aui File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,23 +7,14 @@ import { SourceMapConsumer } from 'source-map'; | |
import { SourceMapSource, RawSource, ConcatSource } from 'webpack-sources'; | ||
import RequestShortener from 'webpack/lib/RequestShortener'; | ||
import ModuleFilenameHelpers from 'webpack/lib/ModuleFilenameHelpers'; | ||
import uglify from 'uglify-es'; | ||
import Uglify from './uglify'; | ||
|
||
/* eslint-disable | ||
no-param-reassign | ||
*/ | ||
|
||
const warningRegex = /\[.+:([0-9]+),([0-9]+)\]/; | ||
|
||
const defaultUglifyOptions = { | ||
output: { | ||
comments: /^\**!|@preserve|@license|@cc_on/, | ||
beautify: false, | ||
semicolons: true, | ||
shebang: true, | ||
}, | ||
}; | ||
|
||
class UglifyJsPlugin { | ||
constructor(options) { | ||
if (typeof options !== 'object' || Array.isArray(options)) { | ||
|
@@ -34,23 +25,8 @@ class UglifyJsPlugin { | |
|
||
this.options.test = this.options.test || /\.js($|\?)/i; | ||
this.options.warningsFilter = this.options.warningsFilter || (() => true); | ||
|
||
this.uglifyOptions = this.options.uglifyOptions || {}; | ||
} | ||
|
||
static buildDefaultUglifyOptions({ ecma, warnings, parse = {}, compress = {}, mangle, output, toplevel, ie8 }) { | ||
return { | ||
ecma, | ||
warnings, | ||
parse, | ||
compress, | ||
mangle: mangle == null ? true : mangle, | ||
// Ignoring sourcemap from options | ||
sourceMap: null, | ||
output: { ...defaultUglifyOptions.output, ...output }, | ||
toplevel, | ||
ie8, | ||
}; | ||
this.options.uglifyOptions = this.options.uglifyOptions || {}; | ||
this.options.parallel = this.options.parallel || {}; | ||
} | ||
|
||
static buildError(err, file, sourceMap, requestShortener) { | ||
|
@@ -64,8 +40,8 @@ class UglifyJsPlugin { | |
return new Error(`${file} from UglifyJs\n${err.message} [${requestShortener.shorten(original.source)}:${original.line},${original.column}][${file}:${err.line},${err.col}]`); | ||
} | ||
return new Error(`${file} from UglifyJs\n${err.message} [${file}:${err.line},${err.col}]`); | ||
} else if (err.msg) { | ||
return new Error(`${file} from UglifyJs\n${err.msg}`); | ||
} else if (err.message) { | ||
return new Error(`${file} from UglifyJs\n${err.message}`); | ||
} | ||
return new Error(`${file} from UglifyJs\n${err.stack}`); | ||
} | ||
|
@@ -91,71 +67,9 @@ class UglifyJsPlugin { | |
}, []); | ||
} | ||
|
||
static buildCommentsFunction(options, uglifyOptions, extractedComments) { | ||
const condition = {}; | ||
const commentsOpts = uglifyOptions.output.comments; | ||
if (typeof options.extractComments === 'string' || options.extractComments instanceof RegExp) { | ||
// extractComments specifies the extract condition and commentsOpts specifies the preserve condition | ||
condition.preserve = commentsOpts; | ||
condition.extract = options.extractComments; | ||
} else if (Object.prototype.hasOwnProperty.call(options.extractComments, 'condition')) { | ||
// Extract condition is given in extractComments.condition | ||
condition.preserve = commentsOpts; | ||
condition.extract = options.extractComments.condition; | ||
} else { | ||
// No extract condition is given. Extract comments that match commentsOpts instead of preserving them | ||
condition.preserve = false; | ||
condition.extract = commentsOpts; | ||
} | ||
|
||
// Ensure that both conditions are functions | ||
['preserve', 'extract'].forEach((key) => { | ||
let regexStr; | ||
let regex; | ||
switch (typeof (condition[key])) { | ||
case 'boolean': | ||
condition[key] = condition[key] ? () => true : () => false; | ||
break; | ||
case 'function': | ||
break; | ||
case 'string': | ||
if (condition[key] === 'all') { | ||
condition[key] = () => true; | ||
break; | ||
} | ||
if (condition[key] === 'some') { | ||
condition[key] = (astNode, comment) => comment.type === 'comment2' && /@preserve|@license|@cc_on/i.test(comment.value); | ||
break; | ||
} | ||
regexStr = condition[key]; | ||
condition[key] = (astNode, comment) => new RegExp(regexStr).test(comment.value); | ||
break; | ||
default: | ||
regex = condition[key]; | ||
condition[key] = (astNode, comment) => (regex.test(comment.value)); | ||
} | ||
}); | ||
|
||
// Redefine the comments function to extract and preserve | ||
// comments according to the two conditions | ||
return (astNode, comment) => { | ||
if (condition.extract(astNode, comment)) { | ||
extractedComments.push( | ||
comment.type === 'comment2' ? `/*${comment.value}*/` : `//${comment.value}`, | ||
); | ||
} | ||
return condition.preserve(astNode, comment); | ||
}; | ||
} | ||
|
||
apply(compiler) { | ||
const requestShortener = new RequestShortener(compiler.context); | ||
// Copy uglify options | ||
const uglifyOptions = UglifyJsPlugin.buildDefaultUglifyOptions(this.uglifyOptions); | ||
// Making sure output options exists if there is an extractComments options | ||
if (this.options.extractComments) { | ||
uglifyOptions.output = uglifyOptions.output || {}; | ||
} | ||
|
||
|
||
compiler.plugin('compilation', (compilation) => { | ||
if (this.options.sourceMap) { | ||
|
@@ -166,13 +80,13 @@ class UglifyJsPlugin { | |
} | ||
|
||
compilation.plugin('optimize-chunk-assets', (chunks, callback) => { | ||
const uglify = new Uglify(this.options.parallel); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Latest commit: |
||
const uglifiedAssets = new WeakSet(); | ||
const tasks = []; | ||
chunks.reduce((acc, chunk) => acc.concat(chunk.files || []), []) | ||
.concat(compilation.additionalChunkAssets || []) | ||
.filter(ModuleFilenameHelpers.matchObject.bind(null, this.options)) | ||
.forEach((file) => { | ||
// Reseting sourcemap to null | ||
uglifyOptions.sourceMap = null; | ||
let sourceMap; | ||
const asset = compilation.assets[file]; | ||
if (uglifiedAssets.has(asset)) { | ||
|
@@ -182,6 +96,7 @@ class UglifyJsPlugin { | |
try { | ||
let input; | ||
let inputSourceMap; | ||
const cacheKey = `${compiler.outputPath}/${file}`; | ||
if (this.options.sourceMap) { | ||
if (asset.sourceAndMap) { | ||
const sourceAndMap = asset.sourceAndMap(); | ||
|
@@ -192,89 +107,104 @@ class UglifyJsPlugin { | |
input = asset.source(); | ||
} | ||
sourceMap = new SourceMapConsumer(inputSourceMap); | ||
// Add source map data | ||
uglifyOptions.sourceMap = { | ||
content: inputSourceMap, | ||
}; | ||
} else { | ||
input = asset.source(); | ||
} | ||
|
||
// Handling comment extraction | ||
const extractedComments = []; | ||
let commentsFile = false; | ||
if (this.options.extractComments) { | ||
uglifyOptions.output.comments = UglifyJsPlugin.buildCommentsFunction(this.options, uglifyOptions, extractedComments); | ||
|
||
commentsFile = this.options.extractComments.filename || `${file}.LICENSE`; | ||
if (typeof commentsFile === 'function') { | ||
commentsFile = commentsFile(file); | ||
} | ||
} | ||
|
||
// Calling uglify | ||
const { error, map, code, warnings } = uglify.minify({ [file]: input }, uglifyOptions); | ||
tasks.push({ | ||
cacheKey, | ||
file, | ||
input, | ||
sourceMap, | ||
inputSourceMap, | ||
commentsFile, | ||
extractComments: this.options.extractComments, | ||
uglifyOptions: this.options.uglifyOptions, | ||
}); | ||
} catch (error) { | ||
compilation.errors.push(UglifyJsPlugin.buildError(error, file, sourceMap, compilation, requestShortener)); | ||
} | ||
}); | ||
|
||
// Handling results | ||
// Error case: add errors, and go to next file | ||
if (error) { | ||
compilation.errors.push(UglifyJsPlugin.buildError(error, file, sourceMap, compilation, requestShortener)); | ||
return; | ||
} | ||
uglify.runTasks(tasks, (tasksError, results) => { | ||
if (tasksError) { | ||
compilation.errors.push(tasksError); | ||
return; | ||
} | ||
|
||
let outputSource; | ||
if (map) { | ||
outputSource = new SourceMapSource(code, file, JSON.parse(map), input, inputSourceMap); | ||
} else { | ||
outputSource = new RawSource(code); | ||
} | ||
results.forEach((data, index) => { | ||
const { file, input, sourceMap, inputSourceMap, commentsFile } = tasks[index]; | ||
const { error, map, code, warnings, extractedComments } = data; | ||
|
||
// Handling results | ||
// Error case: add errors, and go to next file | ||
if (error) { | ||
compilation.errors.push(UglifyJsPlugin.buildError(error, file, sourceMap, compilation, requestShortener)); | ||
return; | ||
} | ||
|
||
let outputSource; | ||
if (map) { | ||
outputSource = new SourceMapSource(code, file, JSON.parse(map), input, inputSourceMap); | ||
} else { | ||
outputSource = new RawSource(code); | ||
} | ||
|
||
// Write extracted comments to commentsFile | ||
if (commentsFile && extractedComments.length > 0) { | ||
// Add a banner to the original file | ||
if (this.options.extractComments.banner !== false) { | ||
let banner = this.options.extractComments.banner || `For license information please see ${commentsFile}`; | ||
if (typeof banner === 'function') { | ||
banner = banner(commentsFile); | ||
} | ||
if (banner) { | ||
outputSource = new ConcatSource( | ||
`/*! ${banner} */\n`, outputSource, | ||
); | ||
} | ||
// Write extracted comments to commentsFile | ||
if (commentsFile && extractedComments.length > 0) { | ||
// Add a banner to the original file | ||
if (this.options.extractComments.banner !== false) { | ||
let banner = this.options.extractComments.banner || `For license information please see ${commentsFile}`; | ||
if (typeof banner === 'function') { | ||
banner = banner(commentsFile); | ||
} | ||
if (banner) { | ||
outputSource = new ConcatSource( | ||
`/*! ${banner} */\n`, outputSource, | ||
); | ||
} | ||
} | ||
|
||
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, | ||
); | ||
} | ||
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] = commentsSource; | ||
compilation.assets[commentsFile] = new ConcatSource( | ||
compilation.assets[commentsFile], '\n', commentsSource, | ||
); | ||
} | ||
} else { | ||
compilation.assets[commentsFile] = commentsSource; | ||
} | ||
} | ||
|
||
// Updating assets | ||
uglifiedAssets.add(compilation.assets[file] = outputSource); | ||
// Updating assets | ||
uglifiedAssets.add(compilation.assets[file] = outputSource); | ||
|
||
// Handling warnings | ||
if (warnings) { | ||
const warnArr = UglifyJsPlugin.buildWarnings(warnings, file, sourceMap, this.options.warningsFilter, requestShortener); | ||
if (warnArr.length > 0) { | ||
compilation.warnings.push(new Error(`${file} from UglifyJs\n${warnArr.join('\n')}`)); | ||
} | ||
// Handling warnings | ||
if (warnings) { | ||
const warnArr = UglifyJsPlugin.buildWarnings(warnings, file, sourceMap, this.options.warningsFilter, requestShortener); | ||
if (warnArr.length > 0) { | ||
compilation.warnings.push(new Error(`${file} from UglifyJs\n${warnArr.join('\n')}`)); | ||
} | ||
} catch (error) { | ||
compilation.errors.push(UglifyJsPlugin.buildError(error, file, sourceMap, compilation, requestShortener)); | ||
} | ||
}); | ||
callback(); | ||
|
||
uglify.exit(); | ||
callback(); | ||
}); | ||
}); | ||
}); | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import crypto from 'crypto'; | ||
import cacache from 'cacache'; | ||
|
||
const sha512 = 'sha512'; | ||
const getHash = data => `${sha512}-${ | ||
crypto.createHash(sha512).update(data).digest('base64') | ||
}`; | ||
|
||
export const get = (cacheDirectory, key, identifier) => cacache.get(cacheDirectory, key).then(({ data, metadata }) => { | ||
const hash = getHash(identifier); | ||
if (metadata.hash !== hash) { | ||
return Promise.reject(new Error('The cache has expired')); | ||
} | ||
return JSON.parse(data); | ||
}); | ||
|
||
export const put = (cacheDirectory, key, data, identifier) => { | ||
const hash = getHash(identifier); | ||
return cacache.put(cacheDirectory, key, JSON.stringify(data), { metadata: { hash } }); | ||
}; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Already included in #74 I will merge that PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#74 merged, this should also update some of the snapshots you updated aswell :)