-
-
Notifications
You must be signed in to change notification settings - Fork 380
factor out shareable methods to a common class #140
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
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
This file contains hidden or 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,214 @@ | ||
var parseRange = require("range-parser"); | ||
var pathIsAbsolute = require("path-is-absolute"); | ||
var MemoryFileSystem = require("memory-fs"); | ||
var HASH_REGEXP = /[0-9a-f]{10,}/; | ||
|
||
module.exports = function Shared(context) { | ||
var share = { | ||
setOptions: function(options) { | ||
if(!options) options = {}; | ||
if(typeof options.watchOptions === "undefined") options.watchOptions = {}; | ||
if(typeof options.reporter !== "function") options.reporter = share.defaultReporter; | ||
if(typeof options.log !== "function") options.log = console.log.bind(console); | ||
if(typeof options.warn !== "function") options.warn = console.warn.bind(console); | ||
if(typeof options.watchDelay !== "undefined") { | ||
// TODO remove this in next major version | ||
options.warn("options.watchDelay is deprecated: Use 'options.watchOptions.aggregateTimeout' instead"); | ||
options.watchOptions.aggregateTimeout = options.watchDelay; | ||
} | ||
if(typeof options.watchOptions.aggregateTimeout === "undefined") options.watchOptions.aggregateTimeout = 200; | ||
if(typeof options.stats === "undefined") options.stats = {}; | ||
if(!options.stats.context) options.stats.context = process.cwd(); | ||
if(options.lazy) { | ||
if(typeof options.filename === "string") { | ||
var str = options.filename | ||
.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&") | ||
.replace(/\\\[[a-z]+\\\]/ig, ".+"); | ||
options.filename = new RegExp("^[\/]{0,1}" + str + "$"); | ||
} | ||
} | ||
context.options = options; | ||
}, | ||
defaultReporter: function(reporterOptions) { | ||
var state = reporterOptions.state; | ||
var stats = reporterOptions.stats; | ||
var options = reporterOptions.options; | ||
|
||
if(state) { | ||
var displayStats = (!options.quiet && options.stats !== false); | ||
if(displayStats && !(stats.hasErrors() || stats.hasWarnings()) && | ||
options.noInfo) | ||
displayStats = false; | ||
if(displayStats) { | ||
options.log(stats.toString(options.stats)); | ||
} | ||
if(!options.noInfo && !options.quiet) { | ||
options.log("webpack: bundle is now VALID."); | ||
} | ||
} else { | ||
options.log("webpack: bundle is now INVALID."); | ||
} | ||
}, | ||
handleRangeHeaders: function handleRangeHeaders(content, req, res) { | ||
//assumes express API. For other servers, need to add logic to access alternative header APIs | ||
res.setHeader("Accept-Ranges", "bytes"); | ||
if(req.headers.range) { | ||
var ranges = parseRange(content.length, req.headers.range); | ||
|
||
// unsatisfiable | ||
if(-1 == ranges) { | ||
res.setHeader("Content-Range", "bytes */" + content.length); | ||
res.statusCode = 416; | ||
} | ||
|
||
// valid (syntactically invalid/multiple ranges are treated as a regular response) | ||
if(-2 != ranges && ranges.length === 1) { | ||
// Content-Range | ||
res.statusCode = 206; | ||
var length = content.length; | ||
res.setHeader( | ||
"Content-Range", | ||
"bytes " + ranges[0].start + "-" + ranges[0].end + "/" + length | ||
); | ||
|
||
content = content.slice(ranges[0].start, ranges[0].end + 1); | ||
} | ||
} | ||
return content; | ||
}, | ||
setFs: function(compiler) { | ||
if(typeof compiler.outputPath === "string" && !pathIsAbsolute.posix(compiler.outputPath) && !pathIsAbsolute.win32(compiler.outputPath)) { | ||
throw new Error("`output.path` needs to be an absolute path or `/`."); | ||
} | ||
|
||
// store our files in memory | ||
var fs; | ||
var isMemoryFs = !compiler.compilers && compiler.outputFileSystem instanceof MemoryFileSystem; | ||
if(isMemoryFs) { | ||
fs = compiler.outputFileSystem; | ||
} else { | ||
fs = compiler.outputFileSystem = new MemoryFileSystem(); | ||
} | ||
context.fs = fs; | ||
}, | ||
compilerDone: function(stats) { | ||
// We are now on valid state | ||
context.state = true; | ||
context.webpackStats = stats; | ||
|
||
// Do the stuff in nextTick, because bundle may be invalidated | ||
// if a change happened while compiling | ||
process.nextTick(function() { | ||
// check if still in valid state | ||
if(!context.state) return; | ||
// print webpack output | ||
context.options.reporter({ | ||
state: true, | ||
stats: stats, | ||
options: context.options | ||
}); | ||
|
||
// execute callback that are delayed | ||
var cbs = context.callbacks; | ||
context.callbacks = []; | ||
cbs.forEach(function continueBecauseBundleAvailable(cb) { | ||
cb(stats); | ||
}); | ||
}); | ||
|
||
// In lazy mode, we may issue another rebuild | ||
if(context.forceRebuild) { | ||
context.forceRebuild = false; | ||
share.rebuild(); | ||
} | ||
}, | ||
compilerInvalid: function() { | ||
if(context.state && (!context.options.noInfo && !context.options.quiet)) | ||
context.options.reporter({ | ||
state: false, | ||
options: context.options | ||
}); | ||
|
||
// We are now in invalid state | ||
context.state = false; | ||
//resolve async | ||
if(arguments.length === 2 && typeof arguments[1] === "function") { | ||
var callback = arguments[1]; | ||
callback(); | ||
} | ||
}, | ||
ready: function ready(fn, req) { | ||
var options = context.options; | ||
if(context.state) return fn(context.webpackStats); | ||
if(!options.noInfo && !options.quiet) | ||
options.log("webpack: wait until bundle finished: " + (req.url || fn.name)); | ||
context.callbacks.push(fn); | ||
}, | ||
startWatch: function() { | ||
var options = context.options; | ||
var compiler = context.compiler; | ||
// start watching | ||
if(!options.lazy) { | ||
var watching = compiler.watch(options.watchOptions, function(err) { | ||
if(err) throw err; | ||
}); | ||
context.watching = watching; | ||
} else { | ||
context.state = true; | ||
} | ||
}, | ||
rebuild: function rebuild() { | ||
if(context.state) { | ||
context.state = false; | ||
context.compiler.run(function(err) { | ||
if(err) throw err; | ||
}); | ||
} else { | ||
context.forceRebuild = true; | ||
} | ||
}, | ||
handleRequest: function(filename, processRequest, req) { | ||
// in lazy mode, rebuild on bundle request | ||
if(context.options.lazy && (!context.options.filename || context.options.filename.test(filename))) | ||
share.rebuild(); | ||
if(HASH_REGEXP.test(filename)) { | ||
try { | ||
if(context.fs.statSync(filename).isFile()) { | ||
processRequest(); | ||
return; | ||
} | ||
} catch(e) { | ||
} | ||
} | ||
share.ready(processRequest, req); | ||
}, | ||
waitUntilValid: function(callback) { | ||
callback = callback || function() {}; | ||
share.ready(callback, {}); | ||
}, | ||
invalidate: function(callback) { | ||
callback = callback || function() {}; | ||
if(context.watching) { | ||
share.ready(callback, {}); | ||
context.watching.invalidate(); | ||
} else { | ||
callback(); | ||
} | ||
}, | ||
close: function(callback) { | ||
callback = callback || function() {}; | ||
if(context.watching) context.watching.close(callback); | ||
else callback(); | ||
} | ||
}; | ||
share.setOptions(context.options); | ||
share.setFs(context.compiler); | ||
|
||
context.compiler.plugin("done", share.compilerDone); | ||
context.compiler.plugin("invalid", share.compilerInvalid); | ||
context.compiler.plugin("watch-run", share.compilerInvalid); | ||
context.compiler.plugin("run", share.compilerInvalid); | ||
|
||
share.startWatch(); | ||
return share; | ||
}; |
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.
This method assumes that
res.setHeader
,req.headers.range
andres.statusCode
work, but we don't know what server will be used, so this is a bit of a leaky abstraction. Don't know the correct way to do this though.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.
I added a comment in the method, suggesting a way forward. E.g. conditional(s) based on an object check for various APIs. A new context flag could also be added to indicate what API is being used. But I believe that can be addressed in a future PR for support of additional server(s).
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.
All right, fair enough.