diff --git a/History.md b/History.md index 09664f6c17..82e2edc4e6 100644 --- a/History.md +++ b/History.md @@ -358,6 +358,12 @@ [Feature #24](https://github.com/meteor/meteor-feature-requests/issues/24) [PR #9657](https://github.com/meteor/meteor/pull/9657) +## v1.6.1.3, 2018-06-16 + +* Node has been updated to version + [8.11.3](https://nodejs.org/en/blog/release/v8.11.3/), an important + [security release](https://nodejs.org/en/blog/vulnerability/june-2018-security-releases/). + ## v1.6.1.2, 2018-05-28 * Meteor 1.6.1.2 is a very small release intended to fix diff --git a/tools/isobuild/bundler.js b/tools/isobuild/bundler.js index e3c9b62d93..9cffbe8085 100644 --- a/tools/isobuild/bundler.js +++ b/tools/isobuild/bundler.js @@ -831,7 +831,10 @@ class Target { packages: packages || [] }); - const sourceBatches = this._runCompilerPlugins(); + const sourceBatches = this._runCompilerPlugins({ + minifiers, + minifyMode, + }); // Link JavaScript and set up this.js, etc. this._emitResources(sourceBatches); @@ -1041,16 +1044,58 @@ class Target { // Run all the compiler plugins on all source files in the project. Returns an // array of PackageSourceBatches which contain the results of this processing. - _runCompilerPlugins() { + _runCompilerPlugins({ + minifiers = [], + minifyMode = "development", + }) { buildmessage.assertInJob(); + + const minifiersByExt = Object.create(null); + if (this instanceof ClientTarget) { + ["js", "css"].forEach(ext => { + minifiers.some(minifier => { + if (_.contains(minifier.extensions, ext)) { + return minifiersByExt[ext] = minifier; + } + }); + }); + } + + const target = this; const processor = new compilerPluginModule.CompilerPluginProcessor({ unibuilds: this.unibuilds, arch: this.arch, sourceRoot: this.sourceRoot, isopackCache: this.isopackCache, - linkerCacheDir: - (this.bundlerCacheDir && files.pathJoin(this.bundlerCacheDir, 'linker')) + linkerCacheDir: this.bundlerCacheDir && + files.pathJoin(this.bundlerCacheDir, 'linker'), + + // Takes a CssOutputResource and returns a string of minified CSS, + // or null to indicate no minification occurred. + // TODO Cache result by resource hash? + minifyCssResource(resource) { + if (! minifiersByExt.css || + minifyMode === "development") { + // Indicates the caller should use the original resource.data + // without minification. + return null; + } + + const file = new File({ + info: 'resource ' + resource.servePath, + arch: target.arch, + data: resource.data, + }); + + file.setTargetPathFromRelPath( + stripLeadingSlash(resource.servePath)); + + return target.minifyCssFiles( + [file], minifiersByExt.css, minifyMode + ).map(file => file.contents("utf8")).join("\n"); + } }); + return processor.runCompilerPlugins(); } @@ -1520,12 +1565,14 @@ class ClientTarget extends Target { // Minify the CSS in this target minifyCss(minifierDef, minifyMode) { + this.css = this.minifyCssFiles(this.css, minifierDef, minifyMode); + } + + minifyCssFiles(files, minifierDef, minifyMode) { const { arch } = this; - const sources = this.css.map((file) => { - return new CssFile(file, { arch }); - }); - const minifier = minifierDef.userPlugin.processFilesForBundle.bind( - minifierDef.userPlugin); + const sources = files.map(file => new CssFile(file, { arch })); + const minifier = minifierDef.userPlugin.processFilesForBundle + .bind(minifierDef.userPlugin); buildmessage.enterJob('minifying app stylesheet', function () { try { @@ -1536,7 +1583,7 @@ class ClientTarget extends Target { } }); - this.css = _.flatten(sources.map((source) => { + return _.flatten(sources.map((source) => { return source._minifiedFiles.map((file) => { const newFile = new File({ info: 'minified css', diff --git a/tools/isobuild/compiler-plugin.js b/tools/isobuild/compiler-plugin.js index 0eb33129bb..4314d74014 100644 --- a/tools/isobuild/compiler-plugin.js +++ b/tools/isobuild/compiler-plugin.js @@ -110,17 +110,19 @@ export class CompilerPluginProcessor { sourceRoot, isopackCache, linkerCacheDir, + minifyCssResource, }) { - const self = this; + Object.assign(this, { + unibuilds, + arch, + sourceRoot, + isopackCache, + linkerCacheDir, + minifyCssResource, + }); - self.unibuilds = unibuilds; - self.arch = arch; - self.sourceRoot = sourceRoot; - self.isopackCache = isopackCache; - - self.linkerCacheDir = linkerCacheDir; - if (self.linkerCacheDir) { - files.mkdir_p(self.linkerCacheDir); + if (this.linkerCacheDir) { + files.mkdir_p(this.linkerCacheDir); } } @@ -509,15 +511,7 @@ class InputFile extends buildPluginModule.InputFile { * @instance */ addAsset(options, lazyFinalizer) { - if (typeof lazyFinalizer === "function") { - // For now, just call the lazyFinalizer function immediately. Since - // assets typically are not compiled, this immediate invocation is - // probably permanently appropriate for addAsset, whereas methods - // like addJavaScript benefit from waiting to call lazyFinalizer. - Object.assign(options, Promise.await(lazyFinalizer())); - } - - this._resourceSlot.addAsset(options); + this._resourceSlot.addAsset(options, lazyFinalizer); } /** @@ -721,21 +715,50 @@ class ResourceSlot { // default to being eager (non-lazy). options.lazy = this._isLazy(options, false); + const cssResource = new CssOutputResource({ + resourceSlot: this, + options, + lazyFinalizer, + }); + if (this.packageSourceBatch.useMeteorInstall && options.lazy) { // If the current packageSourceBatch supports modules, and this CSS // file is lazy, add it as a lazy JS module instead of adding it // unconditionally as a CSS resource, so that it can be imported // when needed. - this.addJavaScript(options, async () => { - const result = typeof lazyFinalizer === "function" - ? await lazyFinalizer() - : { data: options.data }; + const jsResource = this.addJavaScript(options, () => { + const result = {}; - if (result) { - result.data = Buffer.from(cssToCommonJS(result.data), "utf8"); + let css = this.packageSourceBatch.processor + .minifyCssResource(cssResource); + + if (! css && typeof css !== "string") { + // The minifier didn't do anything, so we should use the + // original contents of cssResource.data. + css = cssResource.data.toString("utf8"); + + if (cssResource.sourceMap) { + // Add the source map as an asset, and append a + // sourceMappingURL comment to the end of the CSS text that + // will be dynamically inserted when/if this JS module is + // evaluated at runtime. Note that this only happens when the + // minifier did not modify the CSS, and thus does not happen + // when we are building for production. + const { servePath } = this.addAsset({ + path: jsResource.targetPath + ".map.json", + data: JSON.stringify(cssResource.sourceMap) + }); + css += "\n//# sourceMappingURL=" + servePath + "\n"; + } } + result.data = Buffer.from(cssToCommonJS(css), "utf8"); + + // The JavaScript module that dynamically loads this CSS should + // not inherit the source map of the original CSS output. + result.sourceMap = null; + return result; }); @@ -755,11 +778,12 @@ class ResourceSlot { // stub, so setting .implicit marks the resource as disposable. }).implicit = true; - this.outputResources.push(new CssOutputResource({ - resourceSlot: this, - options, - lazyFinalizer, - })); + // If there was an error processing this file, cssResource.data will + // not be a Buffer, and accessing cssResource.data here should cause + // the error to be reported via inputFile.error. + if (Buffer.isBuffer(cssResource.data)) { + this.outputResources.push(cssResource); + } } } @@ -780,30 +804,20 @@ class ResourceSlot { return resource; } - addAsset(options) { - const self = this; - if (! self.sourceProcessor) { + addAsset(options, lazyFinalizer) { + if (! this.sourceProcessor) { throw Error("addAsset on non-source ResourceSlot?"); } - if (! (options.data instanceof Buffer)) { - if (_.isString(options.data)) { - options.data = Buffer.from(options.data); - } else { - throw new Error("'data' option to addAsset must be a Buffer or " + - "String: " + self.inputResource.path); - } - } - - self.outputResources.push({ - type: 'asset', - data: options.data, - path: options.path, - servePath: self.packageSourceBatch.unibuild.pkg._getServePath( - options.path), - hash: sha1(options.data), - lazy: self._isLazy(options, false), + const resource = new AssetOutputResource({ + resourceSlot: this, + options, + lazyFinalizer, }); + + this.outputResources.push(resource); + + return resource; } addHtml(options) { @@ -918,8 +932,8 @@ class OutputResource { switch (name) { case "data": - let { data } = this._initialOptions; - if (! Buffer.isBuffer(data)) { + let { data = null } = this._initialOptions; + if (typeof data === "string") { data = Buffer.from(data, "utf8"); } return this._set("data", data); @@ -957,18 +971,28 @@ class OutputResource { } class JsOutputResource extends OutputResource { - constructor(options) { - super({ ...options, type: "js" }); + constructor(params) { + super({ ...params, type: "js" }); } } class CssOutputResource extends OutputResource { - constructor(options) { - super({ ...options, type: "css" }); + constructor(params) { + super({ ...params, type: "css" }); this.refreshable = true; } } +class AssetOutputResource extends OutputResource { + constructor(params) { + super({ ...params, type: "asset" }); + // Asset paths must always be explicitly specified. + this.path = this._initialOptions.path; + // Eagerness/laziness should never matter for assets. + delete this.lazy; + } +} + export class PackageSourceBatch { constructor(unibuild, processor, { sourceRoot,