From f786fd6fa50c29b80b78b2ab0258f709fa9aa033 Mon Sep 17 00:00:00 2001 From: David Glasser Date: Tue, 9 Jul 2013 10:42:25 -0700 Subject: [PATCH] Write (but don't use) sourceMaps (but not sources) for client programs. Add builder.writeToGeneratedFilename helper and use it a lot. --- tools/builder.js | 26 ++++++++++++-- tools/bundler.js | 92 ++++++++++++++++++++++++++++++----------------- tools/packages.js | 39 +++++++------------- 3 files changed, 96 insertions(+), 61 deletions(-) diff --git a/tools/builder.js b/tools/builder.js index a462332fc3..d8febca434 100644 --- a/tools/builder.js +++ b/tools/builder.js @@ -272,6 +272,19 @@ _.extend(Builder.prototype, { return relPath; }, + // Convenience wrapper around generateFilename and write. + // + // (Note that in the object returned by builder.enter, this method + // is patched through directly rather than rewriting its inputs and + // outputs. This is only valid because it does nothing with its inputs + // and outputs other than send pass them to other methods.) + writeToGeneratedFilename: function (relPath, writeOptions) { + var self = this; + var generated = self.generateFilename(relPath); + self.write(generated, writeOptions); + return generated; + }, + // Recursively copy a directory and all of its contents into the // bundle. But if the symlink option was passed to the Builder // constructor, then make a symlink instead, if possible. @@ -377,11 +390,11 @@ _.extend(Builder.prototype, { var self = this; var methods = ["write", "writeJson", "reserve", "generateFilename", "copyDirectory", "enter"]; - var ret = {}; + var subBuilder = {}; var relPathWithSep = relPath + path.sep; _.each(methods, function (method) { - ret[method] = function (/* arguments */) { + subBuilder[method] = function (/* arguments */) { var args = _.toArray(arguments); if (method !== "copyDirectory") { @@ -412,7 +425,14 @@ _.extend(Builder.prototype, { }; }); - return ret; + // Methods that don't have to fix up arguments or return values, because + // they are implemented purely in terms of other methods which do. + var passThroughMethods = ["writeToGeneratedFilename"]; + _.each(passThroughMethods, function (method) { + subBuilder[method] = self[method]; + }); + + return subBuilder; }, // Move the completed bundle into its final location (outputPath) diff --git a/tools/bundler.js b/tools/bundler.js index c7d5fcf439..1e862c7fa9 100644 --- a/tools/bundler.js +++ b/tools/bundler.js @@ -70,6 +70,8 @@ // parameter when used // - size: size of file in bytes // - hash: sha1 hash of the file contents +// - sourceMap: optional path to source map file (relative to program.json) +// - sources: same as in native format (see below) // Additionally there will be an entry with where equal to // "internal", path equal to page (above), and hash equal to the // sha1 of page (before replacements.) Currently this is used to @@ -200,6 +202,13 @@ var rejectBadPath = function (p) { throw new Error("bad path: " + p); }; +var stripLeadingSlash = function (p) { + if (p.charAt(0) !== '/') + throw new Error("bad path: " + p); + return p.slice(1); +}; + + /////////////////////////////////////////////////////////////////////////////// // NodeModulesDirectory /////////////////////////////////////////////////////////////////////////////// @@ -643,9 +652,7 @@ _.extend(Target.prototype, { if (resource.type === "static") relPath = path.join("static", resource.servePath); else { - if (resource.servePath.charAt(0) !== '/') - throw new Error("bad servePath: " + resource.servePath); - relPath = resource.servePath.slice(1); + relPath = stripLeadingSlash(resource.servePath); } f.setTargetPathFromRelPath(relPath); if (resource.type === "js") @@ -868,26 +875,50 @@ _.extend(ClientTarget.prototype, { // the target write: function (builder) { var self = this; - var manifest = []; builder.reserve("program.json"); + builder.reserve("app.html"); - // Resources served via HTTP - _.each(["js", "css", "static"], function (type) { - _.each(self[type], function (file) { - - writeFile(file, builder); - - manifest.push({ - path: file.targetPath, - where: "client", - type: type, - cacheable: file.cacheable, - url: file.url, - size: file.size(), - hash: file.hash() + // Helper to iterate over all resources that we serve over HTTP. + var eachResource = function (f) { + _.each(["js", "css", "static"], function (type) { + _.each(self[type], function (file) { + f(file, type); }); }); + }; + + // Reserve all file names from the manifest, so that interleaved + // generateFilename calls don't overlap with them. + eachResource(function (file, type) { + builder.reserve(file.targetPath); + }); + + // Build up a manifest of all resources served via HTTP. + var manifest = []; + eachResource(function (file, type) { + writeFile(file, builder); + + var manifestItem = { + path: file.targetPath, + where: "client", + type: type, + cacheable: file.cacheable, + url: file.url, + size: file.size(), + hash: file.hash() + }; + + if (file.sourceMap) { + manifestItem.sourceMap = builder.writeToGeneratedFilename( + stripLeadingSlash(file.targetPath + '.map'), { + data: new Buffer(file.sourceMap.toString(), 'utf8') + }); + + // XXX write sources too + } + + manifest.push(manifestItem); }); // HTML boilerplate (the HTML served to make the client load the @@ -1078,8 +1109,9 @@ _.extend(JsImage.prototype, { if (! item.targetPath) throw new Error("No targetPath?"); - var loadPath = builder.generateFilename(item.targetPath); - builder.write(loadPath, { data: new Buffer(item.source, 'utf8') }); + var loadPath = builder.writeToGeneratedFilename( + item.targetPath, + { data: new Buffer(item.source, 'utf8') }); var loadItem = { path: loadPath, node_modules: item.nodeModulesDirectory ? @@ -1089,26 +1121,22 @@ _.extend(JsImage.prototype, { }; if (item.sourceMap) { - // XXX this code is very similar to saveAsUnipackage. // Write the source map. - var mapFilename = builder.generateFilename(item.targetPath + '.map'); - loadItem.sourceMap = mapFilename; - builder.write(mapFilename, { - data: new Buffer(item.sourceMap.toString(), 'utf8') - }); + // XXX this code is very similar to saveAsUnipackage. + loadItem.sourceMap = builder.writeToGeneratedFilename( + item.targetPath + '.map', + { data: new Buffer(item.sourceMap.toString(), 'utf8') } + ); // Now write the sources themselves. loadItem.sources = {}; _.each(item.sources, function (x, pathForSourceMap) { - var savedFilename = builder.generateFilename( - path.join('sources', pathForSourceMap)); - builder.write(savedFilename, { - data: x.source - }); loadItem.sources[pathForSourceMap] = { package: x.package, sourcePath: x.sourcePath, - source: savedFilename + source: builder.writeToGeneratedFilename( + path.join('sources', pathForSourceMap), + { data: x.source }) }; }); } diff --git a/tools/packages.js b/tools/packages.js index 4a1845cebb..eb8d3cd1c4 100644 --- a/tools/packages.js +++ b/tools/packages.js @@ -2065,13 +2065,11 @@ _.extend(Package.prototype, { if (_.contains(["head", "body"], resource.type)) return; // already did this one - var resourcePath = builder.generateFilename( - path.join(sliceDir, resource.servePath)); - - builder.write(resourcePath, { data: resource.data }); sliceJson.resources.push({ type: resource.type, - file: resourcePath, + file: builder.writeToGeneratedFilename( + path.join(sliceDir, resource.servePath), + { data: resource.data }), length: resource.data.length, offset: 0, servePath: resource.servePath || undefined @@ -2080,17 +2078,11 @@ _.extend(Package.prototype, { // Output prelink resources _.each(slice.prelinkFiles, function (file) { - var resourcePath = builder.generateFilename( - path.join(sliceDir, file.servePath)); - var data = new Buffer(file.source, 'utf8'); - - builder.write(resourcePath, { - data: data - }); - var resource = { type: 'prelink', - file: resourcePath, + file: builder.writeToGeneratedFilename( + path.join(sliceDir, file.servePath), + { data: new Buffer(file.source, 'utf8') }), length: data.length, offset: 0, servePath: file.servePath || undefined @@ -2098,25 +2090,20 @@ _.extend(Package.prototype, { if (file.sourceMap) { // Write the source map. - var mapFilename = builder.generateFilename( - path.join(sliceDir, file.servePath + '.map')); - resource.sourceMap = mapFilename; - builder.write(mapFilename, { - data: new Buffer(file.sourceMap.toString(), 'utf8') - }); + resource.sourceMap = builder.writeToGeneratedFilename( + path.join(sliceDir, file.servePath + '.map'), + { data: new Buffer(file.sourceMap.toString(), 'utf8') } + ); // Now write the sources themselves. resource.sources = {}; _.each(file.sources, function (x, pathForSourceMap) { - var savedFilename = builder.generateFilename( - path.join(sliceDir, 'sources', x.sourcePath)); - builder.write(savedFilename, { - data: x.source - }); resource.sources[pathForSourceMap] = { package: x.package, sourcePath: x.sourcePath, - source: savedFilename + source: builder.writeToGeneratedFilename( + path.join(sliceDir, 'sources', x.sourcePath), + { data: x.source }) }; }); }