From f84f00014946b5c563a13a39ad6e91e10e100bd9 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Fri, 31 May 2013 12:22:40 +0200 Subject: [PATCH] records, typo --- README.md | 2 +- bin/config-optimist.js | 6 ++ bin/convert-argv.js | 12 ++++ lib/Compilation.js | 64 +++++++-------------- lib/Compiler.js | 72 +++++++++++++++++++---- lib/DependenciesBlock.js | 1 + lib/RecordIdsPlugin.js | 113 +++++++++++++++++++++++++++++++++++++ lib/WebpackOptionsApply.js | 8 ++- package.json | 2 +- 9 files changed, 221 insertions(+), 59 deletions(-) create mode 100644 lib/RecordIdsPlugin.js diff --git a/README.md b/README.md index 8206374ba..58a34de88 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ # Introduction -webpack is a bundler for modules. The main purpose is to bundle javascript files for useage in browser. +webpack is a bundler for modules. The main purpose is to bundle javascript files for usage in browser. **TL;DR** diff --git a/bin/config-optimist.js b/bin/config-optimist.js index 48d7e8e8a..417dfde60 100644 --- a/bin/config-optimist.js +++ b/bin/config-optimist.js @@ -31,6 +31,12 @@ module.exports = function(optimist) { .string("output-library-target").describe("output-library-target") + .string("records-input-path").describe("records-input-path") + + .string("records-output-path").describe("records-output-path") + + .string("records-path").describe("records-path") + .string("target").describe("target") .boolean("cache").describe("cache") diff --git a/bin/convert-argv.js b/bin/convert-argv.js index 0ecff3030..a1c5fcf65 100644 --- a/bin/convert-argv.js +++ b/bin/convert-argv.js @@ -184,6 +184,18 @@ module.exports = function(optimist, argv, convertOptions) { options.output.libraryTarget = value; }); + ifArg("records-input-path", function(value) { + options.recordsInputPath = path.resolve(value); + }); + + ifArg("records-output-path", function(value) { + options.recordsOutputPath = path.resolve(value); + }); + + ifArg("records-path", function(value) { + options.recordsPath = path.resolve(value); + }); + ifArg("target", function(value) { options.target = value; }); diff --git a/lib/Compilation.js b/lib/Compilation.js index 5e88ff66d..7f8f5fe95 100644 --- a/lib/Compilation.js +++ b/lib/Compilation.js @@ -35,6 +35,9 @@ function Compilation(compiler) { this.modules = []; this._modules = {}; this.cache = null; + this.records = null; + this.nextFreeModuleId = 1; + this.nextFreeChunkId = 1; this.assets = {}; this.errors = []; this.warnings = []; @@ -347,21 +350,32 @@ Compilation.prototype.seal = function seal(callback) { this.processDependenciesBlockForChunk(module, chunk); }, this); this.applyPlugins("optimize"); + this.applyPlugins("optimize-modules", this.modules); this.applyPlugins("after-optimize-modules", this.modules); + this.applyPlugins("optimize-chunks", this.chunks); this.applyPlugins("after-optimize-chunks", this.chunks); + + this.applyPlugins("revive-modules", this.modules, this.records); this.applyPlugins("optimize-module-order", this.modules); this.applyModuleIds(); - this.applyPlugins("optimize-chunk-order", this.chunks); - this.applyChunkIds(); this.applyPlugins("optimize-module-ids", this.modules); this.applyPlugins("after-optimize-module-ids", this.modules); + this.applyPlugins("record-modules", this.modules, this.records); + + this.applyPlugins("revive-chunks", this.chunks, this.records); + this.applyPlugins("optimize-chunk-order", this.chunks); + this.applyChunkIds(); this.applyPlugins("optimize-chunk-ids", this.chunks); this.applyPlugins("after-optimize-chunk-ids", this.chunks); + this.applyPlugins("record-chunks", this.chunks, this.records); + this.sortItems(); this.createChunkAssets(); this.summarizeDependencies(); + this.applyPlugins("record", this, this.records); + this.applyPluginsAsync("optimize-chunk-assets", this.chunks, function(err) { if(err) return callback(err); this.applyPlugins("after-optimize-chunk-assets", this.chunks); @@ -418,62 +432,22 @@ Compilation.prototype.processDependenciesBlockForChunk = function processDepende }; Compilation.prototype.applyModuleIds = function applyModuleIds() { - var i = this.cache && this.cache["nextModuleId"] || 1; - var usedIds = {0:true}; this.modules.forEach(function(module) { if(module.id === null) { - if(module.lastId > 0) { - if(!usedIds[module.lastId]) { - usedIds[module.lastId] = true; - module.id = module.lastId; - return; - } - } - module.id = i++; + module.id = this.nextFreeModuleId++; } - }); - if(this.cache) this.cache["nextModuleId"] = i; + }, this); }; Compilation.prototype.applyChunkIds = function applyChunkIds() { - var i = this.cache && this.cache["nextChunkId"] || 1; - var usedIds = {0:true}; - if(this.cache) { - if(!this.cache.chunks) - this.cache.chunks = {}; - var keys = Object.keys(this.cache.chunks).slice(); - var cacheChunks = this.cache.chunks; - this.cache.chunks = {}; - } this.chunks.forEach(function(chunk) { if(chunk.id === null) { - if(this.cache) { - for(var j = 0; j < keys.length; j++) { - var chunkId = keys[j]; - var cacheChunk = cacheChunks[chunkId]; - if(usedIds[cacheChunk.id]) continue; - if(chunk.blocks.some(function(block) { - return cacheChunk.blocks.indexOf(block) >= 0; - })) { - usedIds[cacheChunk.id] = true; - chunk.id = cacheChunk.id; - break; - } - } - } if(chunk.id === null) - chunk.id = i++; - if(this.cache) { - this.cache.chunks["c"+chunk.id] = { - id: chunk.id, - blocks: chunk.blocks - }; - } + chunk.id = this.nextFreeChunkId++; } if(!chunk.ids) chunk.ids = [chunk.id]; }, this); - if(this.cache) this.cache["nextChunkId"] = i; }; Compilation.prototype.sortItems = function sortItems() { diff --git a/lib/Compiler.js b/lib/Compiler.js index 71c0ee98b..58af74d71 100644 --- a/lib/Compiler.js +++ b/lib/Compiler.js @@ -23,14 +23,18 @@ var RequireEnsureItemDependency = require("./dependencies/RequireEnsureItemDepen function Watching(compiler, handler, watchDelay) { this.startTime = null; - this.running = false; this.invalid = false; this.error = null; this.stats = null; this.handler = handler; this.watchDelay = watchDelay; this.compiler = compiler; - this._go(); + this.running = true; + this.compiler.readRecords(function(err) { + if(err) return this._done(err); + + this._go(); + }.bind(this)); } Watching.prototype._go = function() { @@ -44,8 +48,13 @@ Watching.prototype._go = function() { this.compiler.emitAssets(compilation, function(err) { if(err) return this._done(err); + if(this.invalid) return this._done(); - return this._done(null, compilation); + this.compiler.emitRecords(function(err) { + if(err) return this._done(err); + + return this._done(null, compilation); + }.bind(this)); }.bind(this)); }.bind(this)); }.bind(this)); @@ -101,6 +110,10 @@ function Compiler() { this.inputFileSystem = null; this.separateExecutor = null; + this.recordsInputPath = null; + this.recordsOutputPath = null; + this.records = {}; + this.fileTimestamps = {}; this.contextTimestamps = {}; @@ -130,17 +143,25 @@ Compiler.prototype.run = function(callback) { this.applyPluginsAsync("run", this, function(err) { if(err) return callback(err); - this.compile(function(err, compilation) { + this.readRecords(function(err) { if(err) return callback(err); - this.emitAssets(compilation, function(err) { + this.compile(function(err, compilation) { if(err) return callback(err); - var stats = compilation.getStats(); - stats.startTime = startTime; - stats.endTime = new Date().getTime(); - this.applyPlugins("done", stats); - return callback(null, stats); + this.emitAssets(compilation, function(err) { + if(err) return callback(err); + + this.emitRecords(function(err) { + if(err) return callback(err); + + var stats = compilation.getStats(); + stats.startTime = startTime; + stats.endTime = new Date().getTime(); + this.applyPlugins("done", stats); + return callback(null, stats); + }.bind(this)); + }.bind(this)); }.bind(this)); }.bind(this)); }.bind(this)); @@ -210,6 +231,36 @@ Compiler.prototype.emitAssets = function(compilation, callback) { }; +Compiler.prototype.emitRecords = function emitRecords(callback) { + if(!this.recordsOutputPath) return callback(); + this.outputFileSystem.writeFile(this.recordsOutputPath, JSON.stringify(this.records, undefined, 2), callback); +}; + +Compiler.prototype.readRecords = function readRecords(callback) { + if(!this.recordsInputPath) { + this.records = {}; + return callback(); + } + this.inputFileSystem.stat(this.recordsInputPath, function(err) { + // It doesn't exist + // We can ignore this. + if(err) return callback(); + + this.inputFileSystem.readFile(this.recordsInputPath, function(err, content) { + if(err) return callback(err); + + try { + this.records = JSON.parse(content); + } catch(e) { + e.message = "Cannot parse records: " + e.message; + return callback(e); + } + + return callback(); + }.bind(this)); + }.bind(this)); +}; + Compiler.prototype.createChildCompiler = function(compilation, compilerName, outputOptions) { var childCompiler = new Compiler(); for(var name in this._plugins) { @@ -248,6 +299,7 @@ Compiler.prototype.newCompilation = function(params) { compilation.fileTimestamps = this.fileTimestamps; compilation.contextTimestamps = this.contextTimestamps; compilation.name = this.name; + compilation.records = this.records; this.applyPlugins("compilation", compilation, params); return compilation; }; diff --git a/lib/DependenciesBlock.js b/lib/DependenciesBlock.js index eb981b0cb..ef334ba7c 100644 --- a/lib/DependenciesBlock.js +++ b/lib/DependenciesBlock.js @@ -13,6 +13,7 @@ module.exports = DependenciesBlock; DependenciesBlock.prototype.addBlock = function(block) { this.blocks.push(block); + block.parent = this; } DependenciesBlock.prototype.addVariable = function(name, expression, dependencies) { diff --git a/lib/RecordIdsPlugin.js b/lib/RecordIdsPlugin.js new file mode 100644 index 000000000..8dc78e994 --- /dev/null +++ b/lib/RecordIdsPlugin.js @@ -0,0 +1,113 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ +function RecordIdsPlugin() { +} +module.exports = RecordIdsPlugin; +RecordIdsPlugin.prototype.apply = function(compiler) { + compiler.plugin("compilation", function(compilation) { + compilation.plugin("record-modules", function(modules, records) { + records.nextFreeModuleId = compilation.nextFreeModuleId; + if(!records.modules) records.modules = {}; + if(!records.modules.byIdentifier) records.modules.byIdentifier = {}; + modules.forEach(function(module) { + var identifier = module.identifier(); + records.modules.byIdentifier[identifier] = module.id; + }); + }); + compilation.plugin("revive-modules", function(modules, records) { + if(records.nextFreeModuleId) + compilation.nextFreeModuleId = records.nextFreeModuleId; + if(!records.modules || !records.modules.byIdentifier) return; + var usedIds = {0: true}; + modules.forEach(function(module) { + if(module.id !== null) return; + var identifier = module.identifier(); + var id = records.modules.byIdentifier[identifier]; + if(usedIds[id]) return; + usedIds[id] = true; + module.id = id; + }); + }); + + function getDepBlockIdent(block) { + var ident = []; + while(block.parent) { + var p = block.parent; + var idx = p.blocks.indexOf(block); + var l = p.blocks.length - 1; + ident.unshift(idx + "/" + l); + block = block.parent; + } + if(!block.identifier) return null; + ident.unshift(block.identifier()); + return ident.join(":"); + } + compilation.plugin("record-chunks", function(chunks, records) { + records.nextFreeChunkId = compilation.nextFreeChunkId; + if(!records.chunks) records.chunks = {}; + if(!records.chunks.byName) records.chunks.byName = {}; + if(!records.chunks.byBlocks) records.chunks.byBlocks = {}; + chunks.forEach(function(chunk) { + var name = chunk.name; + var blockIdents = chunk.blocks.map(getDepBlockIdent).filter(Boolean); + if(name) records.chunks.byName[name] = chunk.id; + blockIdents.forEach(function(blockIdent) { + records.chunks.byBlocks[blockIdent] = chunk.id; + }); + }); + }); + compilation.plugin("revive-chunks", function(chunks, records) { + if(records.nextFreeChunkId) + compilation.nextFreeChunkId = records.nextFreeChunkId; + if(!records.chunks) return; + var usedIds = {0: true}; + if(records.chunks.byName) { + chunks.forEach(function(chunk) { + if(chunk.id !== null) return; + if(!chunk.name) return; + var id = records.chunks.byName[chunk.name]; + if(usedIds[id]) return; + usedIds[id] = true; + chunk.id = id; + }); + } + if(records.chunks.byBlocks) { + var argumentedChunks = chunks.filter(function(chunk) { + return chunk.id === null + }).map(function(chunk) { + return { + chunk: chunk, + blockIdents: chunk.blocks.map(getDepBlockIdent).filter(Boolean) + } + }).filter(function(arg) { + return arg.blockIdents.length > 0; + }); + var blockIdentsCount = {}; + argumentedChunks.forEach(function(arg, idx) { + arg.blockIdents.forEach(function(blockIdent) { + var id = records.chunks.byBlocks[blockIdent] + if(!id) return; + var accessor = id + ":" + idx; + blockIdentsCount[accessor] = (blockIdentsCount[accessor] || 0) + 1; + }); + }); + blockIdentsCount = Object.keys(blockIdentsCount).map(function(accessor) { + return [blockIdentsCount[accessor]].concat(accessor.split(":").map(Number)); + }).sort(function(a, b) { + return b[0] - a[0]; + }) + blockIdentsCount.forEach(function(arg) { + var id = arg[1]; + if(usedIds[id]) return; + var idx = arg[2]; + var chunk = argumentedChunks[idx].chunk; + if(chunk.id !== null) return; + usedIds[id] = true; + chunk.id = id; + }); + } + }); + }); +}; \ No newline at end of file diff --git a/lib/WebpackOptionsApply.js b/lib/WebpackOptionsApply.js index 60b80db5e..c6009ea2f 100644 --- a/lib/WebpackOptionsApply.js +++ b/lib/WebpackOptionsApply.js @@ -16,8 +16,7 @@ var PrefetchPlugin = require("./PrefetchPlugin"); var SingleEntryPlugin = require("./SingleEntryPlugin"); var MultiEntryPlugin = require("./MultiEntryPlugin"); var CachePlugin = require("./CachePlugin"); - -var UglifyJsPlugin = require("./optimize/UglifyJsPlugin"); +var RecordIdsPlugin = require("./RecordIdsPlugin"); var APIPlugin = require("./APIPlugin"); var ConstPlugin = require("./ConstPlugin"); @@ -35,6 +34,7 @@ var RequireContextPlugin = require("./dependencies/RequireContextPlugin"); var RequireEnsurePlugin = require("./dependencies/RequireEnsurePlugin"); var RequireIncludePlugin = require("./dependencies/RequireIncludePlugin"); +var UglifyJsPlugin = require("./optimize/UglifyJsPlugin"); var OccurenceOrderPlugin = require("./optimize/OccurenceOrderPlugin"); var LimitChunkCountPlugin = require("./optimize/LimitChunkCountPlugin"); var MinChunkSizePlugin = require("./optimize/MinChunkSizePlugin"); @@ -66,6 +66,8 @@ WebpackOptionsApply.prototype.process = function(options, compiler) { compiler.apply.apply(compiler, options.plugins); } compiler.outputPath = options.output.path; + compiler.recordsInputPath = options.recordsInputPath || options.recordsPath; + compiler.recordsOutputPath = options.recordsOutputPath || options.recordsPath; switch(options.target) { case "web": compiler.apply( @@ -138,6 +140,8 @@ WebpackOptionsApply.prototype.process = function(options, compiler) { new FlagIncludedChunksPlugin() ); + compiler.apply(new RecordIdsPlugin()); + if(options.optimize && options.optimize.occurenceOrder) compiler.apply(new OccurenceOrderPlugin(options.optimize.occurenceOrderPreferEntry)); diff --git a/package.json b/package.json index 38214f39a..294cfc3d4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webpack", - "version": "0.10.0-beta17", + "version": "0.10.0-beta18", "author": "Tobias Koppers @sokra", "description": "Packs CommonJs/AMD/Labeled Modules for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand. Support loaders to preprocess files, i.e. json, jade, coffee, css, less, ... and your custom stuff.", "dependencies": {