From cdfe67ca5d7890e14cbec7d3698dc56359ccdfef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Z=C3=B6rb?= Date: Wed, 27 Jan 2016 05:39:07 +0100 Subject: [PATCH 01/27] add testcase for #119 --- test/06-streams.js | 20 +++++ test/fixtures/folder/generate-default.html | 87 ++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 test/fixtures/folder/generate-default.html diff --git a/test/06-streams.js b/test/06-streams.js index 73d2b68e..72d36181 100644 --- a/test/06-streams.js +++ b/test/06-streams.js @@ -82,6 +82,26 @@ describe('Streams', function () { }); }); + it.skip('should work inside folders', function (done) { + var stream = critical.stream({ + base: path.join(__dirname, 'fixtures'), + inline: false, + minify: true, + inlineImages: true + }); + + var expected1 = read('expected/generate-default.css', true); + + getVinyl('folder/generate-default.html') + .pipe(stream) + .pipe(streamAssert.length(1)) + .pipe(streamAssert.nth(0, function (d) { + assert.strictEqual(path.extname(d.path), '.css'); + assert.strictEqual(nn(d.contents.toString('utf8')), expected1); + })) + .pipe(streamAssert.end(done)); + }); + it('should use "generateInline" if inline option is set', function (done) { var stream = critical.stream({base: path.join(__dirname, 'fixtures'), inline: true}); var expected = read('expected/generateInline.html'); diff --git a/test/fixtures/folder/generate-default.html b/test/fixtures/folder/generate-default.html new file mode 100644 index 00000000..d5a4a2fd --- /dev/null +++ b/test/fixtures/folder/generate-default.html @@ -0,0 +1,87 @@ + + + + + critical css test + + + + + + + + + + + + +
+
+ +

critical css test

+
+ +
+

'Allo, 'Allo!

+

Always a pleasure scaffolding your apps.

+

Splendid!

+
+ +
+
+

HTML5 Boilerplate

+

HTML5 Boilerplate is a professional front-end template for building fast, robust, and adaptable web apps or sites.

+ +

Bootstrap

+

Sleek, intuitive, and powerful mobile first front-end framework for faster and easier web development.

+
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From b43b81163f5ae42cd176ba03827b5620f08347fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Z=C3=B6rb?= Date: Wed, 27 Jan 2016 05:41:50 +0100 Subject: [PATCH 02/27] use vinyl --- index.js | 29 +++--- lib/core.js | 232 ++++++++++++++++++++++++++++------------------ lib/fileHelper.js | 55 +++++++---- 3 files changed, 194 insertions(+), 122 deletions(-) diff --git a/index.js b/index.js index 762df421..9c9570f1 100644 --- a/index.js +++ b/index.js @@ -10,7 +10,7 @@ var through2 = require('through2'); var PluginError = require('gulp-util').PluginError; var replaceExtension = require('gulp-util').replaceExtension; var core = require('./lib/core'); -var file = require('./lib/fileHelper'); +var FileHelper = require('./lib/fileHelper'); Promise.promisifyAll(fs); @@ -23,10 +23,10 @@ Promise.promisifyAll(fs); */ exports.generate = function (opts, cb) { - opts = _.defaults(opts || {},{ - base: file.guessBasePath(opts || {}), + opts = _.defaults(opts || {}, { + base: FileHelper.guessBasePath(opts || {}), dimensions: [{ - height: opts.height || 900, + height: opts.height || 900, width: opts.width || 1300 }] }); @@ -40,7 +40,7 @@ exports.generate = function (opts, cb) { corePromise.then(function (output) { var file = path.resolve(opts.styleTarget); var dir = path.dirname(file); - return fs.ensureDirAsync(dir).then(function(){ + return fs.ensureDirAsync(dir).then(function () { return fs.writeFileAsync(path.resolve(opts.styleTarget), output); }); }); @@ -49,10 +49,10 @@ exports.generate = function (opts, cb) { // inline if (opts.inline) { corePromise = Promise.props({ - html: file.getContentPromise(opts), + file: FileHelper.getVinylPromise(opts), css: corePromise }).then(function (result) { - return sourceInliner(result.html, result.css, { + return sourceInliner(result.file.contents.toString(), result.css, { minify: opts.minify || false, extract: opts.extract || false, basePath: opts.base || process.cwd() @@ -65,8 +65,8 @@ exports.generate = function (opts, cb) { corePromise = corePromise.then(function (output) { var file = path.resolve(opts.dest); var dir = path.dirname(file); - return fs.ensureDirAsync(dir).then(function(){ - return fs.writeFileAsync(path.resolve(opts.dest), output); + return fs.ensureDirAsync(dir).then(function () { + return fs.writeFileAsync(path.resolve(opts.dest), output); }).then(function () { return output; }); @@ -80,13 +80,13 @@ exports.generate = function (opts, cb) { throw new Promise.CancellationError(); }).then(function (output) { cb(null, output.toString()); - }).catch(Promise.CancellationError,function(){}).done(); + }).catch(Promise.CancellationError, function () { + }).done(); } else { return corePromise; } }; - /** * deprecated will be removed in the next version * @param opts @@ -113,7 +113,8 @@ exports.generateInline = function (opts, cb) { */ exports.inline = function (opts, cb) { opts = opts || {}; - cb = cb || function () {}; + cb = cb || function () { + }; if (!opts.src || !opts.base) { throw new Error('A valid source and base path are required.'); @@ -144,8 +145,6 @@ exports.inline = function (opts, cb) { }); }; - - /** * Streams wrapper for critical * @@ -165,7 +164,7 @@ exports.stream = function (opts) { } var options = _.assign(opts || {}, { - html: file.contents.toString() + src: file }); exports.generate(options, function (err, data) { diff --git a/lib/core.js b/lib/core.js index 4b86fc27..6573ba3b 100644 --- a/lib/core.js +++ b/lib/core.js @@ -13,7 +13,7 @@ var imageInliner = require('postcss-image-inliner'); /* jshint -W079 */ var Promise = require('bluebird'); var tempfile = require('tempfile'); -var file = require('./fileHelper'); +var FileHelper = require('./fileHelper'); var gc = require('./gc'); var getPort = require('get-port'); @@ -49,19 +49,146 @@ function combineCss(cssArray) { * @returns {Promise} */ function startServer(opts) { - var cb = serveStatic(opts.base); + var cb = serveStatic(opts.base); + + return getPort().then(function (port) { + var server = http.createServer(function (req, res) { + var done = finalhandler(req, res); + cb(req, res, done); + }).listen(port); + + return { + instance: server, + port: port + }; + }); +} + +/** + * Append stylesheets to result + * @param opts + * @returns {Function} + */ +function appendStylesheets(opts) { + return function (file) { + // consider opts.css and map to array if it's a string + if (opts.css) { + file.stylesheets = typeof opts.css === 'string' ? [opts.css] : opts.css; + return file; + } + + // Oust extracts a list of your stylesheets + var stylesheets = oust(file.contents.toString(), 'stylesheets').map(FileHelper.resourcePath(opts)); + debug('Stylesheets: ' + stylesheets); + return Promise.map(stylesheets, FileHelper.assertLocal(opts)).then(function (stylesheets) { + file.stylesheets = stylesheets; + return file; + }); + }; +} - return getPort().then(function(port){ - var server = http.createServer(function(req, res){ - var done = finalhandler(req, res); - cb(req, res, done); - }).listen(port); +function inlineImages(opts) { + return function _inlineImages(vinyl) { - return { - instance: server, - port: port + if (opts.inlineImages) { + var inlineOptions = { + assetPaths: _.uniq((opts.assetPaths || []).concat([vinyl.base, opts.base])), + maxFileSize: opts.maxImageFileSize || 10240 }; + debug('inlineImages', inlineOptions); + return postcss([imageInliner(inlineOptions)]) + .process(vinyl.contents.toString('utf8')) + .then(function (contents) { + vinyl.contents = new Buffer(contents.css); + return vinyl; + }); + } + + return vinyl; + }; +} + +function normalizePaths(file, opts) { + return function _normalizePaths(vinyl) { + + // normalize relative paths + var css = vinyl.contents.toString().replace(/url\(['"]?([^'"\)]+)['"]?\)/g, function (match, filePath) { + // do nothing for absolute paths, urls and data-uris + if (/^data\:/.test(filePath) || /(?:^\/)|(?:\:\/\/)/.test(filePath)) { + return match; + } + + // create path relative to opts.base + var relativeToBase = path.relative(path.resolve(file.base), path.resolve(path.join(vinyl.base, filePath))); + var pathPrefix = (typeof opts.pathPrefix === "undefined") ? "/" : opts.pathPrefix; + var result = FileHelper.normalizePath(match.replace(filePath, path.join(pathPrefix, relativeToBase))); + debug('normalizePaths', filePath, ' => ', result); + return result; }); + + vinyl.contents = new Buffer(css); + return vinyl; + }; +} + +function vinylize(opts) { + return function _vinylize(filepath) { + debug('vinylize', path.resolve(filepath)); + return FileHelper.getVinylPromise({ + src: path.resolve(filepath), + base: opts.base + }); + }; +} + +/** + * Read css source, inline images and normalize relative paths + * @param opts + * @returns {Function} + */ +function processStylesheets(opts) { + return function (file) { + debug('processStylesheets', file.stylesheets); + return Promise.map(file.stylesheets, vinylize(opts)) + .map(inlineImages(opts)) + .map(normalizePaths(file, opts)) + .reduce(function (total, stylesheet) { + return total + os.EOL + stylesheet.contents.toString('utf8'); + }, '') + .then(function (css) { + file.cssPath = tempfile('.css'); + // add file to garbage collector so it get's removed on exit + gc.addFile(file.cssPath); + + return fs.writeFileAsync(file.cssPath, css).then(function () { + return file; + }); + }); + }; +} + +/** + * Fire up a server as pentouse doesn't like filesystem paths on windows + * and let pentouse compute the critical css for us + * @param dimensions + * @param opts + * @returns {function} + */ +function computeCritical(dimensions, opts) { + return function _computeCritical(file) { + return startServer(opts).then(function (server) { + debug('Processing: ' + file.path + ' [' + dimensions.width + 'x' + dimensions.height + ']'); + return penthouseAsync({ + url: FileHelper.getPenthouseUrl(opts, file, server.port), + css: file.cssPath, + maxEmbeddedBase64Length: opts.maxImageFileSize || 10240, + width: dimensions.width, + height: dimensions.height + }).finally(function () { + server.instance.close(); + }); + }); + }; } /** @@ -88,90 +215,15 @@ function generate(opts) { return Promise.map(opts.dimensions, function (dimensions) { // use content to fetch used css files - return file.getContentPromise(opts).then(function (html) { - // consider opts.css and map to array if it's a string - if (opts.css) { - return typeof opts.css === 'string' ? [opts.css] : opts.css; - } - - // Oust extracts a list of your stylesheets - var stylesheets = oust(html.toString(), 'stylesheets').map(file.resourcePath(opts)); - debug('Stylesheets: ' + stylesheets); - return Promise.map(stylesheets, file.assertLocal(opts)); - // read files - }).map(function (fileName) { - return fs.readFileAsync(fileName, 'utf8').then(function (content) { - // get path to css file - var dir = path.dirname(fileName); - - if (opts.inlineImages) { - return postcss([imageInliner({ - assetPaths: _.uniq((opts.assetPaths || []).concat([dir, opts.base])), - maxFileSize: opts.maxImageFileSize || 10240 - })]).process(content).then(function (result) { - return { - dir: path.dirname(fileName), - content: result - }; - }); - } - - return { - dir: path.dirname(fileName), - content: content - }; - }).then(function (data) { - var content = data.content; - var dir = data.dir; - - // normalize relative paths - return content.toString().replace(/url\(['"]?([^'"\)]+)['"]?\)/g, function (match, filePath) { - // do nothing for absolute paths, urls and data-uris - if (/^data\:/.test(filePath) || /(?:^\/)|(?:\:\/\/)/.test(filePath)) { - return match; - } - - // create path relative to opts.base - var relativeToBase = path.relative(path.resolve(opts.base), path.resolve(path.join(dir, filePath))); - var pathPrefix = (typeof opts.pathPrefix === "undefined") ? "/" : opts.pathPrefix; - return file.normalizePath(match.replace(filePath, path.join(pathPrefix, relativeToBase))); - }); - }); - - // combine all css files to one bid stylesheet - }).reduce(function (total, contents) { - return total + os.EOL + contents; - - // write contents to tmp file - }, '').then(function (css) { - var csspath = tempfile('.css'); - // add file to garbage collector so it get's removed on exit - gc.addFile(csspath); - return fs.writeFileAsync(csspath, css).then(function () { - return csspath; - }); - // let penthouseAsync do the rest - }).then(function (csspath) { - - return startServer(opts).then(function(server){ - debug('Processing: ' + opts.url + ' [' + dimensions.width + 'x' + dimensions.height + ']'); - return penthouseAsync({ - url: file.getPenthouseUrl(opts,server.port), - css: csspath, - maxEmbeddedBase64Length: opts.maxImageFileSize || 10240, - // What viewports do you care about? - width: dimensions.width, // viewport width - height: dimensions.height // viewport height - }).finally(function(){ - server.instance.close(); - }); - }); - }); + return FileHelper.getVinylPromise(opts) + .then(appendStylesheets(opts)) + .then(processStylesheets(opts)) + .then(computeCritical(dimensions, opts)); }).then(function (criticalCSS) { criticalCSS = combineCss(criticalCSS); if (opts.ignore) { - debug('Applying filter',opts.ignore); + debug('Applying filter', opts.ignore); criticalCSS = filterCss(criticalCSS, opts.ignore, opts.ignoreOptions || {}); } diff --git a/lib/fileHelper.js b/lib/fileHelper.js index ba8e9356..cc8687ca 100644 --- a/lib/fileHelper.js +++ b/lib/fileHelper.js @@ -4,6 +4,7 @@ var _ = require('lodash'); var Promise = require('bluebird'); var request = require('request'); var mime = require('mime-types'); +var File = require('vinyl'); var slash = require('slash'); var path = require('path'); var url = require('url'); @@ -15,7 +16,6 @@ Promise.promisifyAll(tmp); Promise.promisifyAll(fs); tmp.setGracefulCleanup(); - /** * get first array entry * @param {array} data @@ -38,15 +38,14 @@ function normalizePath(str) { * @param opts * @returns {*} */ -function getPenthouseUrl(opts, port) { +function getPenthouseUrl(opts, file, port) { if (opts.src && isExternal(opts.src)) { return opts.src; } else { - return 'http://127.0.0.1:' + port + '/' + normalizePath( path.relative(path.resolve(opts.base), opts.url)); + return 'http://127.0.0.1:' + port + '/' + normalizePath(path.relative(path.resolve(file.base), file.path)); } } - /** * Check wether a resource is external or not * @param href @@ -121,7 +120,6 @@ function assertLocal(opts) { resolve(filePath); }); } - return requestAsync(filePath) .then(temp({ dir: opts.base @@ -150,34 +148,57 @@ function resourcePath(opts) { * Get content based on options * could either be a html string or a local file * @param opts - * @returns {{promise: *, tmpfiles: Array}} + * @returns {promise} resolves to vinyl object */ -function getContentPromise(opts) { +function getVinylPromise(opts) { if (!(opts.src || opts.html) || !opts.base) { return Promise.reject(new Error('A valid source and base path are required.')); } - // html passed in directly -> create tmp file and set opts.url - if (opts.html) { + var file; + + if (File.isVinyl(opts.src)) { + return new Promise(function (resolve) { + resolve(opts.src); + }); + + } else if (opts.html) { + // html passed in directly -> create tmp file return tmp.fileAsync({dir: opts.base, postfix: '.html'}) .then(getFirst) - .then(function (path) { - opts.url = path; - return fs.writeFileAsync(opts.url, opts.html).then(function () { - return opts.html; + .then(function (filepath) { + file = new File({ + cwd: opts.base, + base: path.dirname(filepath), + path: filepath, + contents: new Buffer(opts.html) + }); + + return fs.writeFileAsync(file.path, file.contents).then(function () { + return file; }); }); // use src file provided } else { + return assertLocal(opts)(opts.src) .then(function (data) { + file = new File({ + cwd: opts.base + }); + // src can either be absolute or relative to opts.base if (opts.src !== path.resolve(data) && !isExternal(opts.src)) { - opts.url = path.join(opts.base, opts.src); + file.path = path.join(opts.base, opts.src); } else { - opts.url = path.relative(process.cwd(), data); + file.path = path.relative(process.cwd(), data); } - return fs.readFileAsync(opts.url); + + file.base = path.dirname(file.path); + return fs.readFileAsync(file.path).then(function (contents) { + file.contents = contents; + return file; + }); }); } } @@ -188,4 +209,4 @@ exports.getPenthouseUrl = getPenthouseUrl; exports.guessBasePath = guessBasePath; exports.resourcePath = resourcePath; exports.assertLocal = assertLocal; -exports.getContentPromise = getContentPromise; +exports.getVinylPromise = getVinylPromise; From 33f7709d7805b00a82c64eb1a6656a9a9346b16c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Z=C3=B6rb?= Date: Wed, 27 Jan 2016 17:00:19 +0100 Subject: [PATCH 03/27] Keep relative images relative We now can campute the path prefix for asset references based on the location of the html file. This allows us to keep relative asset urls in stylesheets relative. Issues #57 and #95 --- lib/core.js | 22 +++++---- lib/fileHelper.js | 54 ++++++++++++++--------- test/02-generate.js | 51 +++++++++++++++++++++ test/expected/generate-image-big.css | 2 +- test/expected/generate-image-relative.css | 3 ++ test/expected/generate-image-skip.css | 2 +- test/fixtures/folder/generate-image.html | 15 +++++++ 7 files changed, 119 insertions(+), 30 deletions(-) create mode 100644 test/expected/generate-image-relative.css create mode 100644 test/fixtures/folder/generate-image.html diff --git a/lib/core.js b/lib/core.js index 6573ba3b..3ccca8c3 100644 --- a/lib/core.js +++ b/lib/core.js @@ -78,8 +78,9 @@ function appendStylesheets(opts) { } // Oust extracts a list of your stylesheets - var stylesheets = oust(file.contents.toString(), 'stylesheets').map(FileHelper.resourcePath(opts)); + var stylesheets = oust(file.contents.toString(), 'stylesheets'); debug('Stylesheets: ' + stylesheets); + stylesheets = stylesheets.map(FileHelper.resourcePath(file, opts)); return Promise.map(stylesheets, FileHelper.assertLocal(opts)).then(function (stylesheets) { file.stylesheets = stylesheets; return file; @@ -92,7 +93,7 @@ function inlineImages(opts) { if (opts.inlineImages) { var inlineOptions = { - assetPaths: _.uniq((opts.assetPaths || []).concat([vinyl.base, opts.base])), + assetPaths: _.uniq((opts.assetPaths || []).concat([path.dirname(vinyl.path), opts.base])), maxFileSize: opts.maxImageFileSize || 10240 }; debug('inlineImages', inlineOptions); @@ -118,12 +119,17 @@ function normalizePaths(file, opts) { return match; } - // create path relative to opts.base - var relativeToBase = path.relative(path.resolve(file.base), path.resolve(path.join(vinyl.base, filePath))); - var pathPrefix = (typeof opts.pathPrefix === "undefined") ? "/" : opts.pathPrefix; - var result = FileHelper.normalizePath(match.replace(filePath, path.join(pathPrefix, relativeToBase))); - debug('normalizePaths', filePath, ' => ', result); - return result; + // create asset path relative to opts.base + var cssDir = path.dirname(vinyl.path); + var assetRelative = path.relative(path.resolve(opts.base), path.resolve(path.join(cssDir, filePath))); + + // compute path prefix default relative to html + var htmlDir = path.resolve(path.dirname(file.path)); + var pathPrefixDefault = path.relative(htmlDir,opts.base); + // var pathPrefixDefault = '/'; + + var pathPrefix = (typeof opts.pathPrefix === "undefined") ? pathPrefixDefault : opts.pathPrefix; + return FileHelper.normalizePath(match.replace(filePath, path.join(pathPrefix, assetRelative))); }); vinyl.contents = new Buffer(css); diff --git a/lib/fileHelper.js b/lib/fileHelper.js index cc8687ca..42c6a7bc 100644 --- a/lib/fileHelper.js +++ b/lib/fileHelper.js @@ -3,6 +3,7 @@ var _ = require('lodash'); /* jshint -W079 */ var Promise = require('bluebird'); var request = require('request'); +var debug = require('debug')('critical:core'); var mime = require('mime-types'); var File = require('vinyl'); var slash = require('slash'); @@ -129,18 +130,32 @@ function assertLocal(opts) { /** * Resolve path to file + * @param htmlfile * @param opts * @returns {Function} */ -function resourcePath(opts) { - return function (file) { - if (isExternal(file)) { - return file; +function resourcePath(htmlfile, opts) { + return function (filepath) { + debug('resourcePath - htmlfile.history',htmlfile.history[0]); + debug('resourcePath - htmlfile.path',htmlfile.path); + debug('resourcePath - filepath',filepath); + + if (isExternal(filepath)) { + debug('resourcePath - external asset'); + return filepath; } - if (!isExternal(opts.src)) { - return path.join(opts.base, file.split('?')[0]); + if (isExternal(htmlfile.history[0])) { + debug('resourcePath - external src'); + return url.resolve(htmlfile.history[0], filepath); } - return url.resolve(opts.src, file); + + var folder = path.relative(opts.base,path.dirname(htmlfile.path)); + if (folder) { + debug('resourcePath - folder',folder); + } + return path.join(path.dirname(htmlfile.path), filepath.split('?')[0]); + + }; } @@ -155,24 +170,26 @@ function getVinylPromise(opts) { return Promise.reject(new Error('A valid source and base path are required.')); } - var file; - if (File.isVinyl(opts.src)) { return new Promise(function (resolve) { resolve(opts.src); }); + } + + var file = new File({ + base: opts.base, + path: opts.src + }); + + if (opts.html) { + // @todo try to compute filepath based on opts.base and paths referenced inside html - } else if (opts.html) { // html passed in directly -> create tmp file return tmp.fileAsync({dir: opts.base, postfix: '.html'}) .then(getFirst) .then(function (filepath) { - file = new File({ - cwd: opts.base, - base: path.dirname(filepath), - path: filepath, - contents: new Buffer(opts.html) - }); + file.path = filepath; + file.contents = new Buffer(opts.html); return fs.writeFileAsync(file.path, file.contents).then(function () { return file; @@ -183,9 +200,7 @@ function getVinylPromise(opts) { return assertLocal(opts)(opts.src) .then(function (data) { - file = new File({ - cwd: opts.base - }); + // src can either be absolute or relative to opts.base if (opts.src !== path.resolve(data) && !isExternal(opts.src)) { @@ -194,7 +209,6 @@ function getVinylPromise(opts) { file.path = path.relative(process.cwd(), data); } - file.base = path.dirname(file.path); return fs.readFileAsync(file.path).then(function (contents) { file.contents = contents; return file; diff --git a/test/02-generate.js b/test/02-generate.js index c64380d0..29434019 100644 --- a/test/02-generate.js +++ b/test/02-generate.js @@ -114,6 +114,57 @@ describe('Module - generate', function () { }, assertCritical(target, expected, done)); }); + it('should inline relative images from folder', function (done) { + var expected = read('expected/generate-image.css'); + var target = '.image-relative.css'; + + critical.generate({ + base: 'fixtures/', + src: 'folder/generate-image.html', + css: [ + 'fixtures/styles/image-relative.css' + ], + dest: target, + width: 1300, + height: 900, + inlineImages: true + }, assertCritical(target, expected, done)); + }); + + it('should rewrite relative images for html outside root', function (done) { + var expected = read('expected/generate-image-relative.css'); + var target = '.image-relative.css'; + + critical.generate({ + base: 'fixtures/', + src: 'folder/generate-image.html', + css: [ + 'fixtures/styles/image-relative.css' + ], + dest: target, + width: 1300, + height: 900, + inlineImages: false + }, assertCritical(target, expected, done)); + }); + + it('should rewrite relative images for html inside root', function (done) { + var expected = read('expected/generate-image-skip.css'); + var target = '.image-relative.css'; + + critical.generate({ + base: 'fixtures/', + src: 'generate-image.html', + css: [ + 'fixtures/styles/image-relative.css' + ], + dest: target, + width: 1300, + height: 900, + inlineImages: false + }, assertCritical(target, expected, done)); + }); + it('should inline absolute images', function (done) { var expected = read('expected/generate-image.css'); var target = '.image-absolute.css'; diff --git a/test/expected/generate-image-big.css b/test/expected/generate-image-big.css index f81998c3..64d912eb 100644 --- a/test/expected/generate-image-big.css +++ b/test/expected/generate-image-big.css @@ -1,3 +1,3 @@ .header { - background: transparent url('/images/critical-big.png'); + background: transparent url('images/critical-big.png'); } diff --git a/test/expected/generate-image-relative.css b/test/expected/generate-image-relative.css new file mode 100644 index 00000000..24e9df32 --- /dev/null +++ b/test/expected/generate-image-relative.css @@ -0,0 +1,3 @@ +.header { + background: transparent url('../images/critical.png'); +} diff --git a/test/expected/generate-image-skip.css b/test/expected/generate-image-skip.css index 9107d0d3..a3253ea3 100644 --- a/test/expected/generate-image-skip.css +++ b/test/expected/generate-image-skip.css @@ -1,3 +1,3 @@ .header { - background: transparent url('/images/critical.png'); + background: transparent url('images/critical.png'); } diff --git a/test/fixtures/folder/generate-image.html b/test/fixtures/folder/generate-image.html new file mode 100644 index 00000000..b8166b0c --- /dev/null +++ b/test/fixtures/folder/generate-image.html @@ -0,0 +1,15 @@ + + + + + critical css test + + + + + +
+
+
+ + From 67e06c4497d5c9ba9d592687ce2c290216844efc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Z=C3=B6rb?= Date: Wed, 27 Jan 2016 17:17:04 +0100 Subject: [PATCH 04/27] consider absolute stylesheet refs --- lib/fileHelper.js | 14 +- test/02-generate.js | 244 +++++++++--------- .../generate-image-relative-subfolder.css | 3 + .../subfolder/generate-image-absolute.html | 15 ++ 4 files changed, 155 insertions(+), 121 deletions(-) create mode 100644 test/expected/generate-image-relative-subfolder.css create mode 100644 test/fixtures/folder/subfolder/generate-image-absolute.html diff --git a/lib/fileHelper.js b/lib/fileHelper.js index 42c6a7bc..561a7dd0 100644 --- a/lib/fileHelper.js +++ b/lib/fileHelper.js @@ -149,13 +149,15 @@ function resourcePath(htmlfile, opts) { return url.resolve(htmlfile.history[0], filepath); } - var folder = path.relative(opts.base,path.dirname(htmlfile.path)); - if (folder) { - debug('resourcePath - folder',folder); + if (/(?:^\/)/.test(filepath)) { + return path.join(opts.base, filepath.split('?')[0]); + } else { + var folder = path.relative(opts.base,path.dirname(htmlfile.path)); + if (folder) { + debug('resourcePath - folder',folder); + } + return path.join(path.dirname(htmlfile.path), filepath.split('?')[0]); } - return path.join(path.dirname(htmlfile.path), filepath.split('?')[0]); - - }; } diff --git a/test/02-generate.js b/test/02-generate.js index 29434019..75c2c57b 100644 --- a/test/02-generate.js +++ b/test/02-generate.js @@ -21,126 +21,140 @@ describe('Module - generate', function () { after(function(){ process.emit('cleanup'); }); - it('should generate critical-path CSS', function (done) { - var expected = read('expected/generate-default.css'); - var target = '.critical.css'; - - critical.generate({ - base: 'fixtures/', - src: 'generate-default.html', - dest: target, - width: 1300, - height: 900 - }, assertCritical(target, expected, done)); - }); - - it('should generate critical-path CSS with query string in file name', function (done) { - var expected = read('expected/generate-default.css'); - var target = '.critical.css'; - - critical.generate({ - base: 'fixtures/', - src: 'generate-default-querystring.html', - dest: target, - width: 1300, - height: 900 - }, assertCritical(target, expected, done)); - }); - - it('should generate multi-dimension critical-path CSS', function (done) { - var expected = read('expected/generate-adaptive.css', 'utf8'); - var target = '.adaptive.css'; - - critical.generate({ - base: 'fixtures/', - src: 'generate-adaptive.html', - dest: target, - dimensions: [{ - width: 100, - height: 70 - }, { - width: 1000, - height: 70 - }] - }, assertCritical(target, expected, done)); - }); - - it('should generate minified critical-path CSS', function (done) { - var expected = read('expected/generate-default.css', true); - var target = '.critical.min.css'; - - critical.generate({ - base: 'fixtures/', - src: 'generate-default.html', - minify: true, - dest: target, - width: 1300, - height: 900 - }, assertCritical(target, expected, done)); - }); - - it('should generate minified critical-path CSS successfully with external css file configured', function (done) { - var expected = read('expected/generate-default.css', true); - var target = '.nostyle.css'; - - critical.generate({ - base: 'fixtures/', - src: 'generate-default-nostyle.html', - css: [ - 'fixtures/styles/main.css', - 'fixtures/styles/bootstrap.css' - ], - minify: true, - dest: target, - width: 1300, - height: 900 - }, assertCritical(target, expected, done)); - }); - - it('should inline relative images', function (done) { - var expected = read('expected/generate-image.css'); - var target = '.image-relative.css'; - - critical.generate({ - base: 'fixtures/', - src: 'generate-image.html', - css: [ - 'fixtures/styles/image-relative.css' - ], - dest: target, - width: 1300, - height: 900, - inlineImages: true - }, assertCritical(target, expected, done)); - }); - - it('should inline relative images from folder', function (done) { - var expected = read('expected/generate-image.css'); - var target = '.image-relative.css'; - - critical.generate({ - base: 'fixtures/', - src: 'folder/generate-image.html', - css: [ - 'fixtures/styles/image-relative.css' - ], - dest: target, - width: 1300, - height: 900, - inlineImages: true - }, assertCritical(target, expected, done)); - }); + //it('should generate critical-path CSS', function (done) { + // var expected = read('expected/generate-default.css'); + // var target = '.critical.css'; + // + // critical.generate({ + // base: 'fixtures/', + // src: 'generate-default.html', + // dest: target, + // width: 1300, + // height: 900 + // }, assertCritical(target, expected, done)); + //}); + // + //it('should generate critical-path CSS with query string in file name', function (done) { + // var expected = read('expected/generate-default.css'); + // var target = '.critical.css'; + // + // critical.generate({ + // base: 'fixtures/', + // src: 'generate-default-querystring.html', + // dest: target, + // width: 1300, + // height: 900 + // }, assertCritical(target, expected, done)); + //}); + // + //it('should generate multi-dimension critical-path CSS', function (done) { + // var expected = read('expected/generate-adaptive.css', 'utf8'); + // var target = '.adaptive.css'; + // + // critical.generate({ + // base: 'fixtures/', + // src: 'generate-adaptive.html', + // dest: target, + // dimensions: [{ + // width: 100, + // height: 70 + // }, { + // width: 1000, + // height: 70 + // }] + // }, assertCritical(target, expected, done)); + //}); + // + //it('should generate minified critical-path CSS', function (done) { + // var expected = read('expected/generate-default.css', true); + // var target = '.critical.min.css'; + // + // critical.generate({ + // base: 'fixtures/', + // src: 'generate-default.html', + // minify: true, + // dest: target, + // width: 1300, + // height: 900 + // }, assertCritical(target, expected, done)); + //}); + // + //it('should generate minified critical-path CSS successfully with external css file configured', function (done) { + // var expected = read('expected/generate-default.css', true); + // var target = '.nostyle.css'; + // + // critical.generate({ + // base: 'fixtures/', + // src: 'generate-default-nostyle.html', + // css: [ + // 'fixtures/styles/main.css', + // 'fixtures/styles/bootstrap.css' + // ], + // minify: true, + // dest: target, + // width: 1300, + // height: 900 + // }, assertCritical(target, expected, done)); + //}); + // + //it('should inline relative images', function (done) { + // var expected = read('expected/generate-image.css'); + // var target = '.image-relative.css'; + // + // critical.generate({ + // base: 'fixtures/', + // src: 'generate-image.html', + // css: [ + // 'fixtures/styles/image-relative.css' + // ], + // dest: target, + // width: 1300, + // height: 900, + // inlineImages: true + // }, assertCritical(target, expected, done)); + //}); + // + //it('should inline relative images from folder', function (done) { + // var expected = read('expected/generate-image.css'); + // var target = '.image-relative.css'; + // + // critical.generate({ + // base: 'fixtures/', + // src: 'folder/generate-image.html', + // css: [ + // 'fixtures/styles/image-relative.css' + // ], + // dest: target, + // width: 1300, + // height: 900, + // inlineImages: true + // }, assertCritical(target, expected, done)); + //}); + // + //it('should rewrite relative images for html outside root', function (done) { + // var expected = read('expected/generate-image-relative.css'); + // var target = '.image-relative.css'; + // + // critical.generate({ + // base: 'fixtures/', + // src: 'folder/generate-image.html', + // css: [ + // 'fixtures/styles/image-relative.css' + // ], + // dest: target, + // width: 1300, + // height: 900, + // inlineImages: false + // }, assertCritical(target, expected, done)); + //}); it('should rewrite relative images for html outside root', function (done) { - var expected = read('expected/generate-image-relative.css'); - var target = '.image-relative.css'; + var expected = read('expected/generate-image-relative-subfolder.css'); + var target = '.image-relative-subfolder.css'; critical.generate({ base: 'fixtures/', - src: 'folder/generate-image.html', - css: [ - 'fixtures/styles/image-relative.css' - ], + src: 'folder/subfolder/generate-image-absolute.html', dest: target, width: 1300, height: 900, diff --git a/test/expected/generate-image-relative-subfolder.css b/test/expected/generate-image-relative-subfolder.css new file mode 100644 index 00000000..b2b4de90 --- /dev/null +++ b/test/expected/generate-image-relative-subfolder.css @@ -0,0 +1,3 @@ +.header { + background: transparent url('../../images/critical.png'); +} diff --git a/test/fixtures/folder/subfolder/generate-image-absolute.html b/test/fixtures/folder/subfolder/generate-image-absolute.html new file mode 100644 index 00000000..095c960f --- /dev/null +++ b/test/fixtures/folder/subfolder/generate-image-absolute.html @@ -0,0 +1,15 @@ + + + + + critical css test + + + + + +
+
+
+ + From 81ab07e4c919be2d922e072f4e352240c34d71dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Z=C3=B6rb?= Date: Thu, 28 Jan 2016 08:07:45 +0100 Subject: [PATCH 05/27] Guess source path for piped html try to guess source path of the file by checking relative links inside the html source show warning to the user when no relative links are available --- cli.js | 4 +- lib/fileHelper.js | 76 +++++-- package.json | 1 + test/02-generate.js | 252 +++++++++++----------- test/05-cli.js | 47 +++- test/06-streams.js | 66 +++--- test/expected/generate-image-absolute.css | 3 + 7 files changed, 273 insertions(+), 176 deletions(-) create mode 100644 test/expected/generate-image-absolute.css diff --git a/cli.js b/cli.js index f3086d47..36bb9d55 100755 --- a/cli.js +++ b/cli.js @@ -2,6 +2,7 @@ 'use strict'; var os = require('os'); var path = require('path'); +var chalk = require('chalk'); var meow = require('meow'); var objectAssign = require('object-assign'); var indentString = require('indent-string'); @@ -109,13 +110,12 @@ cli.flags = _.reduce(cli.flags, function (res, val, key) { }, {}); function error(err) { - process.stderr.write(indentString(err.message || err, ' Error: ')); + process.stderr.write(indentString(err.message || err, chalk.red(' Error: '))); process.stderr.write(os.EOL); process.stderr.write(indentString(help.join(os.EOL), ' ')); process.exit(1); } - function run(data) { var opts = objectAssign({base: process.cwd()}, cli.flags); var command = opts.htmlTarget || opts.inline ? 'generateInline' : 'generate'; diff --git a/lib/fileHelper.js b/lib/fileHelper.js index 561a7dd0..932d05b0 100644 --- a/lib/fileHelper.js +++ b/lib/fileHelper.js @@ -3,15 +3,18 @@ var _ = require('lodash'); /* jshint -W079 */ var Promise = require('bluebird'); var request = require('request'); -var debug = require('debug')('critical:core'); +var debug = require('debug')('critical:file'); var mime = require('mime-types'); var File = require('vinyl'); var slash = require('slash'); +var oust = require('oust'); +var chalk = require('chalk'); var path = require('path'); var url = require('url'); var tmp = require('tmp'); var fs = require('fs'); var gc = require('./gc'); +var os = require('os'); Promise.promisifyAll(tmp); Promise.promisifyAll(fs); @@ -136,31 +139,77 @@ function assertLocal(opts) { */ function resourcePath(htmlfile, opts) { return function (filepath) { - debug('resourcePath - htmlfile.history',htmlfile.history[0]); - debug('resourcePath - htmlfile.path',htmlfile.path); - debug('resourcePath - filepath',filepath); - if (isExternal(filepath)) { - debug('resourcePath - external asset'); + debug('resourcePath - remote', filepath); return filepath; } if (isExternal(htmlfile.history[0])) { - debug('resourcePath - external src'); + debug('resourcePath - remote', htmlfile.history[0]); return url.resolve(htmlfile.history[0], filepath); } if (/(?:^\/)/.test(filepath)) { return path.join(opts.base, filepath.split('?')[0]); } else { - var folder = path.relative(opts.base,path.dirname(htmlfile.path)); + var folder = path.relative(opts.base, path.dirname(htmlfile.path)); if (folder) { - debug('resourcePath - folder',folder); + debug('resourcePath - folder', folder); } return path.join(path.dirname(htmlfile.path), filepath.split('?')[0]); } }; } +/** + * Compute a source path which fits to the directory structure + * so that relative links could be resolved + * @param opts + * @returns {Array} + */ +function generateSourcePath(opts) { + var html = opts.html; + var base = opts.base; + var pathPrefix = opts.pathPrefix; + + if (!pathPrefix) { + var links = _.flatten( + oust(html, 'stylesheets'), + oust(html, 'images'), + oust(html, 'images') + ); + + debug('generateSourcePath - links', links); + // we can only determine a valid path by checking relative links + var relative = _.chain(links).omitBy(function (link) { + return /^data\:/.test(link) || /(?:^\/)|(?:\:\/\/)/.test(link); + }).toArray().value(); + + debug('generateSourcePath - relative', relative); + + if (!relative.length) { + process.stderr.write([ + chalk.red('Warning:'), + 'Missing html source path. Consider \'pathPrefix\' option.', + 'https://goo.gl/PwvFVb', + os.EOL + ].join(' ')); + + opts.pathPrefix = '/'; + return base; + } + + var dots = _.map(relative, function (link) { + var match = /^(\.\.\/)+/.exec(link); + return _.first(match); + }); + + pathPrefix = _.chain(dots).sortBy('length').last().value() || ''; + debug('generateSourcePath', pathPrefix.replace(/\.\./g, '~')); + } + + return path.join(base, pathPrefix.replace(/\.\./g, '~')); +} + /** * Get content based on options * could either be a html string or a local file @@ -184,16 +233,19 @@ function getVinylPromise(opts) { }); if (opts.html) { - // @todo try to compute filepath based on opts.base and paths referenced inside html + var folder = generateSourcePath(opts); + debug('hacky source path folder', folder); // html passed in directly -> create tmp file return tmp.fileAsync({dir: opts.base, postfix: '.html'}) .then(getFirst) .then(function (filepath) { file.path = filepath; + file.path = path.join(folder, path.basename(filepath)); + file.base = folder; file.contents = new Buffer(opts.html); - return fs.writeFileAsync(file.path, file.contents).then(function () { + return fs.writeFileAsync(filepath, file.contents).then(function () { return file; }); }); @@ -202,8 +254,6 @@ function getVinylPromise(opts) { return assertLocal(opts)(opts.src) .then(function (data) { - - // src can either be absolute or relative to opts.base if (opts.src !== path.resolve(data) && !isExternal(opts.src)) { file.path = path.join(opts.base, opts.src); diff --git a/package.json b/package.json index 5b046703..f8ebb8df 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ }, "dependencies": { "bluebird": "^3.0.5", + "chalk": "^1.1.1", "cheerio": "^0.19.0", "clean-css": "^3.4.8", "cli": "^0.11.0", diff --git a/test/02-generate.js b/test/02-generate.js index 75c2c57b..7415e6c2 100644 --- a/test/02-generate.js +++ b/test/02-generate.js @@ -21,132 +21,132 @@ describe('Module - generate', function () { after(function(){ process.emit('cleanup'); }); - //it('should generate critical-path CSS', function (done) { - // var expected = read('expected/generate-default.css'); - // var target = '.critical.css'; - // - // critical.generate({ - // base: 'fixtures/', - // src: 'generate-default.html', - // dest: target, - // width: 1300, - // height: 900 - // }, assertCritical(target, expected, done)); - //}); - // - //it('should generate critical-path CSS with query string in file name', function (done) { - // var expected = read('expected/generate-default.css'); - // var target = '.critical.css'; - // - // critical.generate({ - // base: 'fixtures/', - // src: 'generate-default-querystring.html', - // dest: target, - // width: 1300, - // height: 900 - // }, assertCritical(target, expected, done)); - //}); - // - //it('should generate multi-dimension critical-path CSS', function (done) { - // var expected = read('expected/generate-adaptive.css', 'utf8'); - // var target = '.adaptive.css'; - // - // critical.generate({ - // base: 'fixtures/', - // src: 'generate-adaptive.html', - // dest: target, - // dimensions: [{ - // width: 100, - // height: 70 - // }, { - // width: 1000, - // height: 70 - // }] - // }, assertCritical(target, expected, done)); - //}); - // - //it('should generate minified critical-path CSS', function (done) { - // var expected = read('expected/generate-default.css', true); - // var target = '.critical.min.css'; - // - // critical.generate({ - // base: 'fixtures/', - // src: 'generate-default.html', - // minify: true, - // dest: target, - // width: 1300, - // height: 900 - // }, assertCritical(target, expected, done)); - //}); - // - //it('should generate minified critical-path CSS successfully with external css file configured', function (done) { - // var expected = read('expected/generate-default.css', true); - // var target = '.nostyle.css'; - // - // critical.generate({ - // base: 'fixtures/', - // src: 'generate-default-nostyle.html', - // css: [ - // 'fixtures/styles/main.css', - // 'fixtures/styles/bootstrap.css' - // ], - // minify: true, - // dest: target, - // width: 1300, - // height: 900 - // }, assertCritical(target, expected, done)); - //}); - // - //it('should inline relative images', function (done) { - // var expected = read('expected/generate-image.css'); - // var target = '.image-relative.css'; - // - // critical.generate({ - // base: 'fixtures/', - // src: 'generate-image.html', - // css: [ - // 'fixtures/styles/image-relative.css' - // ], - // dest: target, - // width: 1300, - // height: 900, - // inlineImages: true - // }, assertCritical(target, expected, done)); - //}); - // - //it('should inline relative images from folder', function (done) { - // var expected = read('expected/generate-image.css'); - // var target = '.image-relative.css'; - // - // critical.generate({ - // base: 'fixtures/', - // src: 'folder/generate-image.html', - // css: [ - // 'fixtures/styles/image-relative.css' - // ], - // dest: target, - // width: 1300, - // height: 900, - // inlineImages: true - // }, assertCritical(target, expected, done)); - //}); - // - //it('should rewrite relative images for html outside root', function (done) { - // var expected = read('expected/generate-image-relative.css'); - // var target = '.image-relative.css'; - // - // critical.generate({ - // base: 'fixtures/', - // src: 'folder/generate-image.html', - // css: [ - // 'fixtures/styles/image-relative.css' - // ], - // dest: target, - // width: 1300, - // height: 900, - // inlineImages: false - // }, assertCritical(target, expected, done)); - //}); + it('should generate critical-path CSS', function (done) { + var expected = read('expected/generate-default.css'); + var target = '.critical.css'; + + critical.generate({ + base: 'fixtures/', + src: 'generate-default.html', + dest: target, + width: 1300, + height: 900 + }, assertCritical(target, expected, done)); + }); + + it('should generate critical-path CSS with query string in file name', function (done) { + var expected = read('expected/generate-default.css'); + var target = '.critical.css'; + + critical.generate({ + base: 'fixtures/', + src: 'generate-default-querystring.html', + dest: target, + width: 1300, + height: 900 + }, assertCritical(target, expected, done)); + }); + + it('should generate multi-dimension critical-path CSS', function (done) { + var expected = read('expected/generate-adaptive.css', 'utf8'); + var target = '.adaptive.css'; + + critical.generate({ + base: 'fixtures/', + src: 'generate-adaptive.html', + dest: target, + dimensions: [{ + width: 100, + height: 70 + }, { + width: 1000, + height: 70 + }] + }, assertCritical(target, expected, done)); + }); + + it('should generate minified critical-path CSS', function (done) { + var expected = read('expected/generate-default.css', true); + var target = '.critical.min.css'; + + critical.generate({ + base: 'fixtures/', + src: 'generate-default.html', + minify: true, + dest: target, + width: 1300, + height: 900 + }, assertCritical(target, expected, done)); + }); + + it('should generate minified critical-path CSS successfully with external css file configured', function (done) { + var expected = read('expected/generate-default.css', true); + var target = '.nostyle.css'; + + critical.generate({ + base: 'fixtures/', + src: 'generate-default-nostyle.html', + css: [ + 'fixtures/styles/main.css', + 'fixtures/styles/bootstrap.css' + ], + minify: true, + dest: target, + width: 1300, + height: 900 + }, assertCritical(target, expected, done)); + }); + + it('should inline relative images', function (done) { + var expected = read('expected/generate-image.css'); + var target = '.image-relative.css'; + + critical.generate({ + base: 'fixtures/', + src: 'generate-image.html', + css: [ + 'fixtures/styles/image-relative.css' + ], + dest: target, + width: 1300, + height: 900, + inlineImages: true + }, assertCritical(target, expected, done)); + }); + + it('should inline relative images from folder', function (done) { + var expected = read('expected/generate-image.css'); + var target = '.image-relative.css'; + + critical.generate({ + base: 'fixtures/', + src: 'folder/generate-image.html', + css: [ + 'fixtures/styles/image-relative.css' + ], + dest: target, + width: 1300, + height: 900, + inlineImages: true + }, assertCritical(target, expected, done)); + }); + + it('should rewrite relative images for html outside root', function (done) { + var expected = read('expected/generate-image-relative.css'); + var target = '.image-relative.css'; + + critical.generate({ + base: 'fixtures/', + src: 'folder/generate-image.html', + css: [ + 'fixtures/styles/image-relative.css' + ], + dest: target, + width: 1300, + height: 900, + inlineImages: false + }, assertCritical(target, expected, done)); + }); it('should rewrite relative images for html outside root', function (done) { var expected = read('expected/generate-image-relative-subfolder.css'); diff --git a/test/05-cli.js b/test/05-cli.js index 2e59872e..4cbd6208 100644 --- a/test/05-cli.js +++ b/test/05-cli.js @@ -40,7 +40,7 @@ describe('CLI', function () { }.bind(this)); }); - it('should work well with the critical CSS file passed as an option', function (done) { + it('should work well with the html file passed as an option', function (done) { var cp = execFile('node', [ path.join(__dirname, '../', this.pkg.bin.critical), 'fixtures/generate-default.html', @@ -56,8 +56,24 @@ describe('CLI', function () { }); }); + it('should work well with the html file inside a folder passed as an option', function (done) { + var cp = execFile('node', [ + path.join(__dirname, '../', this.pkg.bin.critical), + 'fixtures/folder/generate-default.html', + '--base', 'fixtures', + '--width', '1300', + '--height', '900' + ]); + + var expected = fs.readFileSync(path.join(__dirname,'expected/generate-default.css'), 'utf8'); + cp.stdout.on('data', function (data) { + assert.strictEqual(nn(data), nn(expected)); + done(); + }); + }); + // pipes don't work on windows - skipWin('should work well with the critical CSS file piped to critical', function (done) { + skipWin('should work well with the html file piped to critical', function (done) { var cp = exec('cat fixtures/generate-default.html | node ' + path.join(__dirname, '../', this.pkg.bin.critical) + ' --base fixtures --width 1300 --height 900'); var expected = fs.readFileSync(path.join(__dirname,'expected/generate-default.css'), 'utf8'); @@ -67,6 +83,33 @@ describe('CLI', function () { }); }); + skipWin('should work well with the html file inside a folder piped to critical', function (done) { + var cmd = 'cat fixtures/folder/generate-default.html | node ' + path.join(__dirname, '../', this.pkg.bin.critical) + ' --base fixtures --width 1300 --height 900'; + var expected = fs.readFileSync(path.join(__dirname,'expected/generate-default.css'), 'utf8'); + + exec(cmd, function(error, stdout, stderr) { + assert.isNull(error); + assert.strictEqual(nn(stdout.toString('utf8')), nn(expected)); + done(); + }); + + + + }); + + skipWin('should work well with the html file inside a folder piped to critical', function (done) { + var cmd = 'cat fixtures/folder/subfolder/generate-image-absolute.html | node ' + path.join(__dirname, '../', this.pkg.bin.critical) + ' --base fixtures --width 1300 --height 900'; + var expected = fs.readFileSync(path.join(__dirname,'expected/generate-image-absolute.css'), 'utf8'); + + exec(cmd, function(error, stdout, stderr) { + assert.isNull(error); + assert.strictEqual(nn(stdout.toString('utf8')), nn(expected)); + assert.include(stderr.toString('utf8'), 'Missing html source path. Consider \'pathPrefix\' option.'); + done(); + }); + }); + + it('should exit with code 1 and show help', function (done) { execFile('node', [path.join(__dirname, '../', this.pkg.bin.critical), 'fixtures/not-exists.html'], function(err, stdout, stderr){ assert.typeOf(err,'Error'); diff --git a/test/06-streams.js b/test/06-streams.js index 72d36181..0cc784c9 100644 --- a/test/06-streams.js +++ b/test/06-streams.js @@ -50,39 +50,39 @@ describe('Streams', function () { process.emit('cleanup'); }); - it('should emit error on streamed file', function(done){ - var stream = critical.stream({base: path.join(__dirname,'fixtures')}); - var fakeFilePath = path.join(__dirname, 'fixtures','generate-default.html'); - - fs.createReadStream(fakeFilePath) - .pipe(vinylStream()) - .pipe(stream) - .on('data', function(data) { - assert.fail(null,data,'Should not emit data'); - }) - .on('error', function (err) { - err.message.should.eql('Streaming not supported'); - done(); - }); - }); - - - it('should support vinyl buffer streams', function(done){ - var stream = critical.stream({base: path.join(__dirname,'fixtures')}); - - getVinyl('generate-default.html') - .pipe(stream) - .on('data', function(data) { - assert.ok(data); - done(); - }) - .on('error', function (err) { - assert.fail(null,err,'Should not emit an error'); - done(); - }); - }); - - it.skip('should work inside folders', function (done) { + //it('should emit error on streamed file', function(done){ + // var stream = critical.stream({base: path.join(__dirname,'fixtures')}); + // var fakeFilePath = path.join(__dirname, 'fixtures','generate-default.html'); + // + // fs.createReadStream(fakeFilePath) + // .pipe(vinylStream()) + // .pipe(stream) + // .on('data', function(data) { + // assert.fail(null,data,'Should not emit data'); + // }) + // .on('error', function (err) { + // err.message.should.eql('Streaming not supported'); + // done(); + // }); + //}); + // + // + //it('should support vinyl buffer streams', function(done){ + // var stream = critical.stream({base: path.join(__dirname,'fixtures')}); + // + // getVinyl('generate-default.html') + // .pipe(stream) + // .on('data', function(data) { + // assert.ok(data); + // done(); + // }) + // .on('error', function (err) { + // assert.fail(null,err,'Should not emit an error'); + // done(); + // }); + //}); + + it('should work inside folders', function (done) { var stream = critical.stream({ base: path.join(__dirname, 'fixtures'), inline: false, diff --git a/test/expected/generate-image-absolute.css b/test/expected/generate-image-absolute.css new file mode 100644 index 00000000..9107d0d3 --- /dev/null +++ b/test/expected/generate-image-absolute.css @@ -0,0 +1,3 @@ +.header { + background: transparent url('/images/critical.png'); +} From 6949df1d72f9c61424d72edcac30ae8bb75ec7f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Z=C3=B6rb?= Date: Thu, 28 Jan 2016 08:17:13 +0100 Subject: [PATCH 06/27] tests for auto pathPrefix --- .travis.yml | 2 ++ package.json | 1 + test/02-generate.js | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/.travis.yml b/.travis.yml index aea12749..02b0612a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,3 +3,5 @@ language: node_js node_js: - 'stable' - '0.12' +before_install: + - npm cache clean diff --git a/package.json b/package.json index f8ebb8df..74991bed 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "request": "^2.61.0", "serve-static": "^1.10.0", "slash": "^1.0.0", + "supports-color": "^3.1.2", "tempfile": "^1.1.0", "through2": "^2.0.0", "tmp": "0.0.28" diff --git a/test/02-generate.js b/test/02-generate.js index 7415e6c2..9fef421c 100644 --- a/test/02-generate.js +++ b/test/02-generate.js @@ -264,6 +264,22 @@ describe('Module - generate', function () { }, assertCritical(target, expected, done)); }); + it('should detect pathPrefix', function (done) { + var expected = read('expected/path-prefix.css'); + var target = '.path-prefix.css'; + + critical.generate({ + base: 'fixtures/', + src: 'path-prefix.html', + css: [ + 'fixtures/styles/path-prefix.css' + ], + dest: target, + width: 1300, + height: 900 + }, assertCritical(target, expected, done)); + }); + it('should generate and inline, if "inline" option is set', function(done) { var expected = read('expected/generateInline.html'); var target = '.generateInline.html'; @@ -666,6 +682,22 @@ describe('Module - generate (remote)', function () { }, assertCritical(target, expected, done)); }); + it('should detect pathPrefix', function (done) { + var expected = read('expected/path-prefix.css'); + var target = '.path-prefix.css'; + + critical.generate({ + base: 'fixtures/', + src: 'http://localhost:3000/path-prefix.html', + css: [ + 'fixtures/styles/path-prefix.css' + ], + dest: target, + width: 1300, + height: 900 + }, assertCritical(target, expected, done)); + }); + it('should generate and inline, if "inline" option is set', function(done) { var expected = read('expected/generateInline.html'); var target = '.generateInline.html'; From 8029e736fd6ccb9f5803b4525c32132bca4e8c66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Z=C3=B6rb?= Date: Thu, 28 Jan 2016 09:53:38 +0100 Subject: [PATCH 07/27] travis tweaks some weird issue requires a second npm install to add all deps without this module minimist required by gulp-util is missing --- .travis.yml | 2 +- package.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 02b0612a..3863a6d0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,4 +4,4 @@ node_js: - 'stable' - '0.12' before_install: - - npm cache clean + - npm install diff --git a/package.json b/package.json index 74991bed..fd1bf189 100644 --- a/package.json +++ b/package.json @@ -52,10 +52,10 @@ "request": "^2.61.0", "serve-static": "^1.10.0", "slash": "^1.0.0", - "supports-color": "^3.1.2", "tempfile": "^1.1.0", "through2": "^2.0.0", - "tmp": "0.0.28" + "tmp": "0.0.28", + "vinyl": "^1.1.1" }, "devDependencies": { "async": "^1.2.1", From 121f0b8602d2975dcb540b1d55d5220aaf26083b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Z=C3=B6rb?= Date: Thu, 28 Jan 2016 16:43:54 +0100 Subject: [PATCH 08/27] eslint fixes --- index.js | 1 - lib/core.js | 7 ++----- lib/fileHelper.js | 44 +++++++++++++++++++++----------------------- test/05-cli.js | 14 +++++--------- 4 files changed, 28 insertions(+), 38 deletions(-) diff --git a/index.js b/index.js index a2161ceb..4791d4e3 100644 --- a/index.js +++ b/index.js @@ -22,7 +22,6 @@ Promise.promisifyAll(fs); * @return {Promise}|undefined */ exports.generate = function (opts, cb) { - opts = _.defaults(opts || {}, { base: FileHelper.guessBasePath(opts || {}), dimensions: [{ diff --git a/lib/core.js b/lib/core.js index 3a2c9a1b..a2461afd 100644 --- a/lib/core.js +++ b/lib/core.js @@ -90,7 +90,6 @@ function appendStylesheets(opts) { function inlineImages(opts) { return function _inlineImages(vinyl) { - if (opts.inlineImages) { var inlineOptions = { assetPaths: _.uniq((opts.assetPaths || []).concat([path.dirname(vinyl.path), opts.base])), @@ -111,7 +110,6 @@ function inlineImages(opts) { function normalizePaths(file, opts) { return function _normalizePaths(vinyl) { - // normalize relative paths var css = vinyl.contents.toString().replace(/url\(['"]?([^'"\)]+)['"]?\)/g, function (match, filePath) { // do nothing for absolute paths, urls and data-uris @@ -125,10 +123,9 @@ function normalizePaths(file, opts) { // compute path prefix default relative to html var htmlDir = path.resolve(path.dirname(file.path)); - var pathPrefixDefault = path.relative(htmlDir,opts.base); - // var pathPrefixDefault = '/'; + var pathPrefixDefault = path.relative(htmlDir, opts.base); - var pathPrefix = (typeof opts.pathPrefix === "undefined") ? pathPrefixDefault : opts.pathPrefix; + var pathPrefix = (typeof opts.pathPrefix === 'undefined') ? pathPrefixDefault : opts.pathPrefix; return FileHelper.normalizePath(match.replace(filePath, path.join(pathPrefix, assetRelative))); }); diff --git a/lib/fileHelper.js b/lib/fileHelper.js index a9a734e3..4350514c 100644 --- a/lib/fileHelper.js +++ b/lib/fileHelper.js @@ -45,9 +45,9 @@ function normalizePath(str) { function getPenthouseUrl(opts, file, port) { if (opts.src && isExternal(opts.src)) { return opts.src; - } else { - return 'http://127.0.0.1:' + port + '/' + normalizePath(path.relative(path.resolve(file.base), file.path)); } + + return 'http://127.0.0.1:' + port + '/' + normalizePath(path.relative(path.resolve(file.base), file.path)); } /** @@ -150,13 +150,12 @@ function resourcePath(htmlfile, opts) { if (/(?:^\/)/.test(filepath)) { return path.join(opts.base, filepath.split('?')[0]); - } else { - var folder = path.relative(opts.base, path.dirname(htmlfile.path)); - if (folder) { - debug('resourcePath - folder', folder); - } - return path.join(path.dirname(htmlfile.path), filepath.split('?')[0]); } + var folder = path.relative(opts.base, path.dirname(htmlfile.path)); + if (folder) { + debug('resourcePath - folder', folder); + } + return path.join(path.dirname(htmlfile.path), filepath.split('?')[0]); }; } @@ -249,24 +248,23 @@ function getVinylPromise(opts) { return file; }); }); - // use src file provided - } else { + } - return assertLocal(opts)(opts.src) - .then(function (data) { - // src can either be absolute or relative to opts.base - if (opts.src !== path.resolve(data) && !isExternal(opts.src)) { - file.path = path.join(opts.base, opts.src); - } else { - file.path = path.relative(process.cwd(), data); - } + // use src file provided + return assertLocal(opts)(opts.src) + .then(function (data) { + // src can either be absolute or relative to opts.base + if (opts.src !== path.resolve(data) && !isExternal(opts.src)) { + file.path = path.join(opts.base, opts.src); + } else { + file.path = path.relative(process.cwd(), data); + } - return fs.readFileAsync(file.path).then(function (contents) { - file.contents = contents; - return file; - }); + return fs.readFileAsync(file.path).then(function (contents) { + file.contents = contents; + return file; }); - } + }); } exports.isExternal = isExternal; diff --git a/test/05-cli.js b/test/05-cli.js index a17649b2..86aa4717 100644 --- a/test/05-cli.js +++ b/test/05-cli.js @@ -66,7 +66,7 @@ describe('CLI', function () { '--height', '900' ]); - var expected = fs.readFileSync(path.join(__dirname,'expected/generate-default.css'), 'utf8'); + var expected = fs.readFileSync(path.join(__dirname, 'expected/generate-default.css'), 'utf8'); cp.stdout.on('data', function (data) { assert.strictEqual(nn(data), nn(expected)); done(); @@ -86,23 +86,20 @@ describe('CLI', function () { skipWin('should work well with the html file inside a folder piped to critical', function (done) { var cmd = 'cat fixtures/folder/generate-default.html | node ' + path.join(__dirname, '../', this.pkg.bin.critical) + ' --base fixtures --width 1300 --height 900'; - var expected = fs.readFileSync(path.join(__dirname,'expected/generate-default.css'), 'utf8'); + var expected = fs.readFileSync(path.join(__dirname, 'expected/generate-default.css'), 'utf8'); - exec(cmd, function(error, stdout, stderr) { + exec(cmd, function (error, stdout) { assert.isNull(error); assert.strictEqual(nn(stdout.toString('utf8')), nn(expected)); done(); }); - - - }); skipWin('should work well with the html file inside a folder piped to critical', function (done) { var cmd = 'cat fixtures/folder/subfolder/generate-image-absolute.html | node ' + path.join(__dirname, '../', this.pkg.bin.critical) + ' --base fixtures --width 1300 --height 900'; - var expected = fs.readFileSync(path.join(__dirname,'expected/generate-image-absolute.css'), 'utf8'); + var expected = fs.readFileSync(path.join(__dirname, 'expected/generate-image-absolute.css'), 'utf8'); - exec(cmd, function(error, stdout, stderr) { + exec(cmd, function (error, stdout, stderr) { assert.isNull(error); assert.strictEqual(nn(stdout.toString('utf8')), nn(expected)); assert.include(stderr.toString('utf8'), 'Missing html source path. Consider \'pathPrefix\' option.'); @@ -110,7 +107,6 @@ describe('CLI', function () { }); }); - it('should exit with code 1 and show help', function (done) { execFile('node', [path.join(__dirname, '../', this.pkg.bin.critical), 'fixtures/not-exists.html'], function (err, stdout, stderr) { assert.typeOf(err, 'Error'); From b663cb8ae502e56b4e1f628e54c653db1c5b58fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Z=C3=B6rb?= Date: Thu, 28 Jan 2016 16:49:29 +0100 Subject: [PATCH 09/27] minor changes --- test/05-cli.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/05-cli.js b/test/05-cli.js index 86aa4717..f866bbdc 100644 --- a/test/05-cli.js +++ b/test/05-cli.js @@ -95,7 +95,7 @@ describe('CLI', function () { }); }); - skipWin('should work well with the html file inside a folder piped to critical', function (done) { + skipWin('should show warning on piped file without relative links and use "/"', function (done) { var cmd = 'cat fixtures/folder/subfolder/generate-image-absolute.html | node ' + path.join(__dirname, '../', this.pkg.bin.critical) + ' --base fixtures --width 1300 --height 900'; var expected = fs.readFileSync(path.join(__dirname, 'expected/generate-image-absolute.css'), 'utf8'); From 931936140b3c32f3576da8a71bfea3f1b1a638f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Z=C3=B6rb?= Date: Thu, 28 Jan 2016 17:12:47 +0100 Subject: [PATCH 10/27] doc blocks --- lib/core.js | 21 +++++++++++++++++++-- lib/fileHelper.js | 7 ++++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/lib/core.js b/lib/core.js index a2461afd..5d4e00cc 100644 --- a/lib/core.js +++ b/lib/core.js @@ -67,7 +67,7 @@ function startServer(opts) { /** * Append stylesheets to result * @param opts - * @returns {Function} + * @returns {function} */ function appendStylesheets(opts) { return function (file) { @@ -88,6 +88,11 @@ function appendStylesheets(opts) { }; } +/** + * Inline images using postcss-image-inliner + * @param opts + * @returns {function} + */ function inlineImages(opts) { return function _inlineImages(vinyl) { if (opts.inlineImages) { @@ -108,6 +113,13 @@ function inlineImages(opts) { }; } +/** + * Helper function to rebase the file paths relative to the stylesheet + * to be relative to the html file + * @param file + * @param opts + * @returns {function} + */ function normalizePaths(file, opts) { return function _normalizePaths(vinyl) { // normalize relative paths @@ -134,6 +146,11 @@ function normalizePaths(file, opts) { }; } +/** + * Helper function create vinyl objects + * @param opts + * @returns {function} + */ function vinylize(opts) { return function _vinylize(filepath) { debug('vinylize', path.resolve(filepath)); @@ -147,7 +164,7 @@ function vinylize(opts) { /** * Read css source, inline images and normalize relative paths * @param opts - * @returns {Function} + * @returns {function} */ function processStylesheets(opts) { return function (file) { diff --git a/lib/fileHelper.js b/lib/fileHelper.js index 4350514c..a3fc5bd3 100644 --- a/lib/fileHelper.js +++ b/lib/fileHelper.js @@ -210,8 +210,9 @@ function generateSourcePath(opts) { } /** - * Get content based on options - * could either be a html string or a local file + * Get vinyl object based on options + * could either be a html string or a local file. + * If opts.src already is a vinyl object it gets returnd without modifications * @param opts * @returns {promise} resolves to vinyl object */ @@ -250,7 +251,7 @@ function getVinylPromise(opts) { }); } - // use src file provided + // use src file provided, fetch content and return vinyl return assertLocal(opts)(opts.src) .then(function (data) { // src can either be absolute or relative to opts.base From 846c233e1bf26b129b002ea7fc8d1fa3429ee2d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Z=C3=B6rb?= Date: Fri, 29 Jan 2016 23:08:24 +0100 Subject: [PATCH 11/27] compatibility to vinyl < v0.5.3 --- index.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/index.js b/index.js index 4791d4e3..53bb5842 100644 --- a/index.js +++ b/index.js @@ -160,6 +160,13 @@ exports.stream = function (opts) { return this.emit('error', new PluginError('critical', 'Streaming not supported')); } + // we're checking for isVinyl inside + // this is only available for objects created by + // vinyl >= v0.5.3 + if (!file.hasOwnProperty('_isVinyl')) { + file._isVinyl = true; + } + var options = _.assign(opts || {}, { src: file }); From 883564e9ecf849ce8a20e2e688a7d13535f24d72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Z=C3=B6rb?= Date: Mon, 1 Feb 2016 06:04:44 +0100 Subject: [PATCH 12/27] Check vinyl --- index.js | 7 ------- lib/fileHelper.js | 14 +++++++++++++- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index 53bb5842..4791d4e3 100644 --- a/index.js +++ b/index.js @@ -160,13 +160,6 @@ exports.stream = function (opts) { return this.emit('error', new PluginError('critical', 'Streaming not supported')); } - // we're checking for isVinyl inside - // this is only available for objects created by - // vinyl >= v0.5.3 - if (!file.hasOwnProperty('_isVinyl')) { - file._isVinyl = true; - } - var options = _.assign(opts || {}, { src: file }); diff --git a/lib/fileHelper.js b/lib/fileHelper.js index a3fc5bd3..1ff0d25f 100644 --- a/lib/fileHelper.js +++ b/lib/fileHelper.js @@ -112,6 +112,17 @@ function guessBasePath(opts) { return process.cwd(); } +/** + * Wrapper for File.isVinyl to detect vinyl objects generated by gulp (vinyl < v0.5.6) + * @param file + * @returns {string} + */ +function isVinyl(file) { + return File.isVinyl(file) || + file instanceof File || + /function File\(/.test(file.constructor.toString()) && file.contents && file.path; +} + /** * Returns a promise to a local file * @param opts @@ -221,7 +232,7 @@ function getVinylPromise(opts) { return Promise.reject(new Error('A valid source and base path are required.')); } - if (File.isVinyl(opts.src)) { + if (isVinyl(opts.src)) { return new Promise(function (resolve) { resolve(opts.src); }); @@ -269,6 +280,7 @@ function getVinylPromise(opts) { } exports.isExternal = isExternal; +exports.isVinyl = isVinyl; exports.normalizePath = normalizePath; exports.getPenthouseUrl = getPenthouseUrl; exports.guessBasePath = guessBasePath; From 24f1870253838ee439bb7cd9360008e558c4c9e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Z=C3=B6rb?= Date: Mon, 1 Feb 2016 06:12:00 +0100 Subject: [PATCH 13/27] Check vinyl --- lib/fileHelper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fileHelper.js b/lib/fileHelper.js index 1ff0d25f..7b465be6 100644 --- a/lib/fileHelper.js +++ b/lib/fileHelper.js @@ -120,7 +120,7 @@ function guessBasePath(opts) { function isVinyl(file) { return File.isVinyl(file) || file instanceof File || - /function File\(/.test(file.constructor.toString()) && file.contents && file.path; + file && /function File\(/.test(file.constructor.toString()) && file.contents && file.path; } /** From 307cd1f1f958595f634a2f97c8fb28368f357305 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Z=C3=B6rb?= Date: Thu, 4 Feb 2016 17:12:15 +0100 Subject: [PATCH 14/27] remove unused setMaxListeners(0) --- test/02-generate.js | 1 - test/03-inline.js | 1 - test/04-generateInline.js | 1 - test/06-streams.js | 1 - 4 files changed, 4 deletions(-) diff --git a/test/02-generate.js b/test/02-generate.js index a9ae1523..65cf1c99 100644 --- a/test/02-generate.js +++ b/test/02-generate.js @@ -15,7 +15,6 @@ var http = require('http'); var serveStatic = require('serve-static'); process.chdir(path.resolve(__dirname)); -process.setMaxListeners(0); describe('Module - generate', function () { after(function () { diff --git a/test/03-inline.js b/test/03-inline.js index f6a3a0c5..40b20eca 100644 --- a/test/03-inline.js +++ b/test/03-inline.js @@ -8,7 +8,6 @@ var gc = require('../lib/gc'); gc.skipExceptions(); process.chdir(path.resolve(__dirname)); -process.setMaxListeners(0); describe('Module - inline (deprecated)', function () { it('inlines critical-path CSS successfully', function (done) { diff --git a/test/04-generateInline.js b/test/04-generateInline.js index 15e6a28a..31563983 100644 --- a/test/04-generateInline.js +++ b/test/04-generateInline.js @@ -12,7 +12,6 @@ var gc = require('../lib/gc'); gc.skipExceptions(); process.chdir(path.resolve(__dirname)); -process.setMaxListeners(0); describe('Module - generateInline (deprecated)', function () { after(function () { diff --git a/test/06-streams.js b/test/06-streams.js index ca5a74fe..f9ddc5ff 100644 --- a/test/06-streams.js +++ b/test/06-streams.js @@ -18,7 +18,6 @@ var gc = require('../lib/gc'); gc.skipExceptions(); process.chdir(path.resolve(__dirname)); -process.setMaxListeners(0); /** * Get vinyl file object From a87976b334fcb32019444501073f618168b0677c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Z=C3=B6rb?= Date: Sun, 7 Feb 2016 23:36:45 +0100 Subject: [PATCH 15/27] guess path to html source also consider script sources --- lib/fileHelper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fileHelper.js b/lib/fileHelper.js index 7b465be6..f28cca26 100644 --- a/lib/fileHelper.js +++ b/lib/fileHelper.js @@ -184,7 +184,7 @@ function generateSourcePath(opts) { if (!pathPrefix) { var links = _.flatten( oust(html, 'stylesheets'), - oust(html, 'images'), + oust(html, 'scripts'), oust(html, 'images') ); From ca32bda14f3cb446543a927364e4aee5463779d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Z=C3=B6rb?= Date: Sun, 7 Feb 2016 23:38:58 +0100 Subject: [PATCH 16/27] fixed jsdoc --- lib/fileHelper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fileHelper.js b/lib/fileHelper.js index f28cca26..a2351a91 100644 --- a/lib/fileHelper.js +++ b/lib/fileHelper.js @@ -174,7 +174,7 @@ function resourcePath(htmlfile, opts) { * Compute a source path which fits to the directory structure * so that relative links could be resolved * @param opts - * @returns {Array} + * @returns {string} */ function generateSourcePath(opts) { var html = opts.html; From dd0a5a68a112491d19bd9580fb767e145aa36cf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Z=C3=B6rb?= Date: Mon, 8 Feb 2016 06:31:31 +0100 Subject: [PATCH 17/27] add 'folder' option --- README.md | 1 + cli.js | 2 ++ lib/fileHelper.js | 28 ++++++++++++++++------------ test/05-cli.js | 6 +++++- 4 files changed, 24 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index c27b4b1f..f991b46f 100644 --- a/README.md +++ b/README.md @@ -219,6 +219,7 @@ critical.generate({ | inlineImages | `boolean` | `false` | Inline images | assetPaths | `array` | `[]` | List of directories/urls where the inliner should start looking for assets | maxImageFileSize | `integer` | `10240`| Sets a max file size (in bytes) for base64 inlined images +| folder | `string` | `''` | Subfolder, relative to base directory. Only relevant if no src raw html is provided | | pathPrefix | `string` | `/` | Path to prepend CSS assets with. You *must* make this path absolute if you are going to be using critical in multiple target files in disparate directory depths. (eg. targeting both `/index.html` and `/admin/index.html` would require this path to start with `/` or it wouldn't work.) | ignore | `array` | `[]` | Ignore css rules. See [`filter-css`](https://github.com/bezoerb/filter-css) for usage examples. | ignoreOptions | `object` | `{}` | Ignore options. See [`filter-css#options`](https://github.com/bezoerb/filter-css#options). diff --git a/cli.js b/cli.js index d68b5266..92a021cf 100755 --- a/cli.js +++ b/cli.js @@ -25,6 +25,7 @@ var help = [ ' -I, --ignore RegExp, @type or selector to ignore', ' -e, --extract Extract inlined styles from referenced stylesheets', ' -p, --pathPrefix Path to prepend CSS assets with (defaults to /) ', + ' -f, --folder HTML Subfolder (default: \'\')', ' --ii, --inlineImages Inline images', ' --maxFileSize Sets a max file size (in bytes) for base64 inlined images', ' --assetPaths Directories/Urls where the inliner should start looking for assets.', @@ -44,6 +45,7 @@ var cli = meow({ c: 'css', w: 'width', h: 'height', + f: 'folder', H: 'htmlTarget', i: 'inline', I: 'ignore', diff --git a/lib/fileHelper.js b/lib/fileHelper.js index a2351a91..a670493e 100644 --- a/lib/fileHelper.js +++ b/lib/fileHelper.js @@ -178,15 +178,19 @@ function resourcePath(htmlfile, opts) { */ function generateSourcePath(opts) { var html = opts.html; - var base = opts.base; - var pathPrefix = opts.pathPrefix; - if (!pathPrefix) { - var links = _.flatten( - oust(html, 'stylesheets'), - oust(html, 'scripts'), - oust(html, 'images') - ); + if (typeof opts.src !== 'undefined') { + return path.dirname(opts.src); + } + + if (typeof opts.folder !== 'undefined') { + var folder = path.isAbsolute(opts.folder) ? opts.folder : path.join(opts.base, opts.folder); + opts.pathPrefix = path.relative(opts.folder, opts.base); + return folder; + } + + if (!opts.pathPrefix) { + var links = oust(html, 'stylesheets'); debug('generateSourcePath - links', links); // we can only determine a valid path by checking relative links @@ -205,7 +209,7 @@ function generateSourcePath(opts) { ].join(' ')); opts.pathPrefix = '/'; - return base; + return opts.base; } var dots = _.map(relative, function (link) { @@ -213,11 +217,11 @@ function generateSourcePath(opts) { return _.first(match); }); - pathPrefix = _.chain(dots).sortBy('length').last().value() || ''; - debug('generateSourcePath', pathPrefix.replace(/\.\./g, '~')); + opts.pathPrefix = _.chain(dots).sortBy('length').last().value() || ''; + debug('generateSourcePath', opts.pathPrefix.replace(/\.\./g, '~')); } - return path.join(base, pathPrefix.replace(/\.\./g, '~')); + return path.join(opts.base, opts.pathPrefix.replace(/\.\./g, '~')); } /** diff --git a/test/05-cli.js b/test/05-cli.js index f866bbdc..10d266e5 100644 --- a/test/05-cli.js +++ b/test/05-cli.js @@ -102,7 +102,7 @@ describe('CLI', function () { exec(cmd, function (error, stdout, stderr) { assert.isNull(error); assert.strictEqual(nn(stdout.toString('utf8')), nn(expected)); - assert.include(stderr.toString('utf8'), 'Missing html source path. Consider \'pathPrefix\' option.'); + assert.include(stderr.toString('utf8'), 'Missing html source path. Consider \'folder\' option.'); done(); }); }); @@ -192,6 +192,7 @@ describe('CLI', function () { '-S', 'styleTarget', '-m', 'minify', '-e', 'extract', + '-f', 'folder', '-p', 'pathPrefix', '-I', '/ignore/', '-i' @@ -207,6 +208,7 @@ describe('CLI', function () { assert.strictEqual(this.mockOpts.minify, 'minify'); assert.strictEqual(this.mockOpts.extract, 'extract'); assert.strictEqual(this.mockOpts.pathPrefix, 'pathPrefix'); + assert.strictEqual(this.mockOpts.folder, 'folder'); assert.isArray(this.mockOpts.ignore); assert.instanceOf(this.mockOpts.ignore[0], RegExp); assert.strictEqual(this.mockOpts.inline, true); @@ -225,6 +227,7 @@ describe('CLI', function () { '--styleTarget', 'styleTarget', '--minify', 'minify', '--extract', 'extract', + '--folder', 'folder', '--pathPrefix', 'pathPrefix', '--inline', '--inlineImages', @@ -242,6 +245,7 @@ describe('CLI', function () { assert.strictEqual(this.mockOpts.styleTarget, 'styleTarget'); assert.strictEqual(this.mockOpts.minify, 'minify'); assert.strictEqual(this.mockOpts.extract, 'extract'); + assert.strictEqual(this.mockOpts.folder, 'folder'); assert.strictEqual(this.mockOpts.pathPrefix, 'pathPrefix'); assert.isArray(this.mockOpts.ignore); assert.include(this.mockOpts.ignore, 'ignore'); From 08777235769fbaf1b5844f3238d9a4dafe3e5b50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Z=C3=B6rb?= Date: Mon, 8 Feb 2016 06:40:06 +0100 Subject: [PATCH 18/27] updated jsdoc --- lib/core.js | 2 +- lib/fileHelper.js | 32 ++++++++++++++++++-------------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/lib/core.js b/lib/core.js index 5d4e00cc..121aaea7 100644 --- a/lib/core.js +++ b/lib/core.js @@ -191,7 +191,7 @@ function processStylesheets(opts) { * Fire up a server as pentouse doesn't like filesystem paths on windows * and let pentouse compute the critical css for us * @param dimensions - * @param opts + * @param {object} opts Options passed to critical * @returns {function} */ function computeCritical(dimensions, opts) { diff --git a/lib/fileHelper.js b/lib/fileHelper.js index a670493e..a35160ad 100644 --- a/lib/fileHelper.js +++ b/lib/fileHelper.js @@ -31,6 +31,8 @@ function getFirst(data) { /** * Fixup slashes in file paths for windows + * @param {string} str path + * @returns {string} */ function normalizePath(str) { return process.platform === 'win32' ? slash(str) : str; @@ -39,8 +41,10 @@ function normalizePath(str) { /** * Get html path for penthouse * Needs to be an absolute file:// url for local files to work on windows - * @param opts - * @returns {*} + * @param {object} opts Options passed to critical + * @param {File} file Vinyl file object of html file + * @param {string} port Server port + * @returns {string} */ function getPenthouseUrl(opts, file, port) { if (opts.src && isExternal(opts.src)) { @@ -52,7 +56,7 @@ function getPenthouseUrl(opts, file, port) { /** * Check wether a resource is external or not - * @param href + * @param {string} href * @returns {boolean} */ function isExternal(href) { @@ -61,8 +65,8 @@ function isExternal(href) { /** * Generate temp file from request response object - * @param opts - * @returns {Function} + * @param {object} opts Options passed to critical + * @returns {function} */ function temp(opts) { return function (resp) { @@ -101,7 +105,7 @@ function requestAsync(uri) { /** * Get default base path based on options - * @param opts + * @param {object} opts Options passed to critical * @returns {string} */ function guessBasePath(opts) { @@ -114,7 +118,7 @@ function guessBasePath(opts) { /** * Wrapper for File.isVinyl to detect vinyl objects generated by gulp (vinyl < v0.5.6) - * @param file + * @param {*} file * @returns {string} */ function isVinyl(file) { @@ -125,8 +129,8 @@ function isVinyl(file) { /** * Returns a promise to a local file - * @param opts - * @returns {Promise} + * @param {object} opts Options passed to critical + * @returns {function} */ function assertLocal(opts) { return function (filePath) { @@ -144,9 +148,9 @@ function assertLocal(opts) { /** * Resolve path to file - * @param htmlfile - * @param opts - * @returns {Function} + * @param {File} htmlfile Vinyl file object of html file + * @param {object} opts Options passed to critical + * @returns {function} */ function resourcePath(htmlfile, opts) { return function (filepath) { @@ -173,7 +177,7 @@ function resourcePath(htmlfile, opts) { /** * Compute a source path which fits to the directory structure * so that relative links could be resolved - * @param opts + * @param {object} opts Options passed to critical * @returns {string} */ function generateSourcePath(opts) { @@ -228,7 +232,7 @@ function generateSourcePath(opts) { * Get vinyl object based on options * could either be a html string or a local file. * If opts.src already is a vinyl object it gets returnd without modifications - * @param opts + * @param {object} opts Options passed to critical * @returns {promise} resolves to vinyl object */ function getVinylPromise(opts) { From 2571d78a5764d929d985acad046ee88d0a2ad00a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Z=C3=B6rb?= Date: Mon, 8 Feb 2016 06:42:08 +0100 Subject: [PATCH 19/27] text change --- lib/fileHelper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fileHelper.js b/lib/fileHelper.js index a35160ad..0a809309 100644 --- a/lib/fileHelper.js +++ b/lib/fileHelper.js @@ -207,7 +207,7 @@ function generateSourcePath(opts) { if (!relative.length) { process.stderr.write([ chalk.red('Warning:'), - 'Missing html source path. Consider \'pathPrefix\' option.', + 'Missing html source path. Consider \'folder\' option.', 'https://goo.gl/PwvFVb', os.EOL ].join(' ')); From 2c9cf362e19aba9f732afa794d68c94951de6d73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Z=C3=B6rb?= Date: Fri, 12 Aug 2016 22:42:37 +0200 Subject: [PATCH 20/27] WiP --- README.md | 2 +- index.js | 39 ++- lib/core.js | 35 +-- lib/fileHelper.js | 43 +++- test/00-lib.js | 116 +++++++++ test/02-generate.js | 78 ++++-- test/fixtures/.generateInline.html | 390 +++++++++++++++++++++++++++++ test/helper/testhelper.js | 23 +- 8 files changed, 650 insertions(+), 76 deletions(-) create mode 100644 test/00-lib.js create mode 100644 test/fixtures/.generateInline.html diff --git a/README.md b/README.md index fb2c1fb9..c9f9d4e9 100644 --- a/README.md +++ b/README.md @@ -219,7 +219,7 @@ critical.generate({ | inlineImages | `boolean` | `false` | Inline images | assetPaths | `array` | `[]` | List of directories/urls where the inliner should start looking for assets | maxImageFileSize | `integer` | `10240`| Sets a max file size (in bytes) for base64 inlined images -| folder | `string` | `''` | Subfolder, relative to base directory. Only relevant if no src raw html is provided | +| folder | `string` | `''` | Subfolder, relative to base directory. Only relevant without src if raw html is provided | | pathPrefix | `string` | `/` | Path to prepend CSS assets with. You *must* make this path absolute if you are going to be using critical in multiple target files in disparate directory depths. (eg. targeting both `/index.html` and `/admin/index.html` would require this path to start with `/` or it wouldn't work.) | include | `array` | `[]` | Force include css rules. See [`penthouse#usage`](https://github.com/pocketjoso/penthouse#usage-1). | ignore | `array` | `[]` | Ignore css rules. See [`filter-css`](https://github.com/bezoerb/filter-css) for usage examples. diff --git a/index.js b/index.js index cff1f85e..63e6bc55 100644 --- a/index.js +++ b/index.js @@ -16,6 +16,37 @@ var inliner = require('./lib/inline-styles'); Promise.promisifyAll(fs); +/** + * Normalize options + * + * @param opts + */ +function prepareOptions(opts) { + if (!opts) { + opts = {}; + } + + var options = _.defaults(opts, { + base: FileHelper.guessBasePath(opts), + dimensions: [{ + height: opts.height || 900, + width: opts.width || 1300 + }] + }); + + // set dest relative to base if isn't specivied absolute + if (options.dest && !path.isAbsolute(options.dest)) { + options.dest = path.join(options.base, options.dest); + } + + // set dest relative to base if isn't specivied absolute + if (options.destFolder && !path.isAbsolute(options.destFolder)) { + options.destFolder = path.join(options.base, options.destFolder); + } + + return options; +} + /** * Critical path CSS generation * @param {object} opts Options @@ -24,13 +55,7 @@ Promise.promisifyAll(fs); * @return {Promise}|undefined */ exports.generate = function (opts, cb) { - opts = _.defaults(opts || {}, { - base: FileHelper.guessBasePath(opts || {}), - dimensions: [{ - height: opts.height || 900, - width: opts.width || 1300 - }] - }); + opts = prepareOptions(opts); // generate critical css var corePromise = core.generate(opts); diff --git a/lib/core.js b/lib/core.js index 8785db72..19bc929f 100644 --- a/lib/core.js +++ b/lib/core.js @@ -111,39 +111,6 @@ function inlineImages(opts) { }; } -/** - * Helper function to rebase the file paths relative to the stylesheet - * to be relative to the html file - * @param file - * @param opts - * @returns {function} - */ -function normalizePaths(file, opts) { - return function _normalizePaths(vinyl) { - // normalize relative paths - var css = vinyl.contents.toString().replace(/url\(['"]?([^'"\)]+)['"]?\)/g, function (match, filePath) { - // do nothing for absolute paths, urls and data-uris - if (/^data:/.test(filePath) || /(?:^\/)|(?::\/\/)/.test(filePath)) { - return match; - } - - // create asset path relative to opts.base - var cssDir = path.dirname(vinyl.path); - var assetRelative = path.relative(path.resolve(opts.base), path.resolve(path.join(cssDir, filePath))); - - // compute path prefix default relative to html - var htmlDir = path.resolve(path.dirname(file.path)); - var pathPrefixDefault = path.relative(htmlDir, opts.base); - - var pathPrefix = (typeof opts.pathPrefix === 'undefined') ? pathPrefixDefault : opts.pathPrefix; - return FileHelper.normalizePath(match.replace(filePath, path.join(pathPrefix, assetRelative))); - }); - - vinyl.contents = new Buffer(css); - return vinyl; - }; -} - /** * Helper function create vinyl objects * @param opts @@ -169,7 +136,7 @@ function processStylesheets(opts) { debug('processStylesheets', file.stylesheets); return Promise.map(file.stylesheets, vinylize(opts)) .map(inlineImages(opts)) - .map(normalizePaths(file, opts)) + .map(FileHelper.replaceAssetPaths(file, opts)) .reduce(function (total, stylesheet) { return total + os.EOL + stylesheet.contents.toString('utf8'); }, '') diff --git a/lib/fileHelper.js b/lib/fileHelper.js index beb4ea1d..9fef6b6a 100644 --- a/lib/fileHelper.js +++ b/lib/fileHelper.js @@ -38,6 +38,47 @@ function normalizePath(str) { return process.platform === 'win32' ? slash(str) : str; } +/** + * Helper function to rewrite the file paths relative to the stylesheet + * to be relative to the html file + * @param {File} html + * @param opts + * @returns {function} + */ +function replaceAssetPaths(html, opts) { + // set dest path with fallback to html path + var destPath = opts.destFolder || (opts.dest && path.dirname(opts.dest)) || path.dirname(html.path); + var destPathResolved = path.resolve(destPath); + var baseResolved = path.resolve(opts.base); + + /** + * the resulting function should get passed an vinyl object with the css file + */ + return function _replaceAssetPaths(stylesheet) { + // normalize relative paths + var css = stylesheet.contents.toString().replace(/url\(['"]?([^'"\)]+)['"]?\)/g, function (match, assetPath) { + // skip absolute paths, urls and data-uris + if (/^data:/.test(assetPath) || /(?:^\/)|(?::\/\/)/.test(assetPath)) { + return match; + } + + // create asset path relative to opts.base + var stylesheetPath = path.dirname(stylesheet.path); + var assetRelative = path.relative(baseResolved, path.resolve(path.join(stylesheetPath, assetPath))); + + // compute path prefix default relative to html + var pathPrefixDefault = path.relative(destPathResolved, baseResolved); + + var pathPrefix = (typeof opts.pathPrefix === 'undefined') ? pathPrefixDefault : opts.pathPrefix; + + return normalizePath(match.replace(assetPath, path.join(pathPrefix, assetRelative))); + }); + + stylesheet.contents = new Buffer(css); + return stylesheet; + }; +} + /** * Get html path for penthouse * Needs to be an absolute file:// url for local files to work on windows @@ -289,7 +330,7 @@ function getVinylPromise(opts) { exports.isExternal = isExternal; exports.isVinyl = isVinyl; -exports.normalizePath = normalizePath; +exports.replaceAssetPaths = replaceAssetPaths; exports.getPenthouseUrl = getPenthouseUrl; exports.guessBasePath = guessBasePath; exports.resourcePath = resourcePath; diff --git a/test/00-lib.js b/test/00-lib.js new file mode 100644 index 00000000..4fa65342 --- /dev/null +++ b/test/00-lib.js @@ -0,0 +1,116 @@ +/* eslint-env node, mocha */ +'use strict'; +var path = require('path'); +var assert = require('chai').assert; +var File = require('vinyl'); +var FileHelper = require('../lib/fileHelper'); + +// unittests +describe('Lib', function () { + describe('FileHelper.guessBasePath', function () { + it('should return process.cwd() without base', function (done) { + var res = FileHelper.guessBasePath({}); + assert.strictEqual(res, process.cwd()); + done(); + }); + + it('should return base if base option is set', function (done) { + var res = FileHelper.guessBasePath({src: 'fixtures/folder/generate-default.html'}); + assert.strictEqual(res, 'fixtures/folder'); + done(); + }); + }); + + describe('FileHelper.resolveAssetPaths', function () { + function html(base, filepath) { + return new File({ + base: base || 'fixtures', + path: filepath || 'fixtures/generate-default.html' + }); + } + + it('should compute path based on file', function (done) { + var f = FileHelper.replaceAssetPaths(html(), { + base: 'fixtures' + }); + + function mock(p) { + return new File({ + path: 'fixtures/a/b/file.css', + contents: new Buffer('url(' + p + ')') + }); + } + + assert.strictEqual(f(mock('../../images/test.png')).contents.toString(), 'url(images/test.png)'); + assert.strictEqual(f(mock('../images/test.png')).contents.toString(), 'url(a/images/test.png)'); + assert.strictEqual(f(mock('../../../images/test.png')).contents.toString(), 'url(../images/test.png)'); + assert.strictEqual(f(mock('images/test.png')).contents.toString(), 'url(a/b/images/test.png)'); + + done(); + }); + + it('should compute path based on dest', function (done) { + var f = FileHelper.replaceAssetPaths(html(), { + base: 'fixtures', + dest: 'fixtures/1/2/3.html' + }); + + function mock(p) { + return new File({ + path: 'fixtures/a/b/file.css', + contents: new Buffer('url(' + p + ')') + }); + } + + assert.strictEqual(f(mock('../../images/test.png')).contents.toString(), 'url(../../images/test.png)'); + assert.strictEqual(f(mock('../images/test.png')).contents.toString(), 'url(../../a/images/test.png)'); + assert.strictEqual(f(mock('../../../images/test.png')).contents.toString(), 'url(../../../images/test.png)'); + assert.strictEqual(f(mock('images/test.png')).contents.toString(), 'url(../../a/b/images/test.png)'); + + done(); + }); + + it('should compute path based on destFolder', function (done) { + var f = FileHelper.replaceAssetPaths(html(), { + base: 'fixtures', + dest: '1/2/3.html', + destFolder: 'fixtures' + }); + + function mock(p) { + return new File({ + path: 'fixtures/a/b/file.css', + contents: new Buffer('url(' + p + ')') + }); + } + + assert.strictEqual(f(mock('../../images/test.png')).contents.toString(), 'url(images/test.png)'); + assert.strictEqual(f(mock('../images/test.png')).contents.toString(), 'url(a/images/test.png)'); + assert.strictEqual(f(mock('../../../images/test.png')).contents.toString(), 'url(../images/test.png)'); + assert.strictEqual(f(mock('images/test.png')).contents.toString(), 'url(a/b/images/test.png)'); + + done(); + }); + + it('should compute path based on dest with src outside base', function (done) { + var f = FileHelper.replaceAssetPaths(html('fixtures', path.resolve('expexted/generate-default.html')), { + base: 'fixtures', + dest: 'fixtures/1/2.html' + }); + + function mock(p) { + return new File({ + path: 'fixtures/a/file.css', + contents: new Buffer('url(' + p + ')') + }); + } + + assert.strictEqual(f(mock('../../images/test.png')).contents.toString(), 'url(../../images/test.png)'); + assert.strictEqual(f(mock('../images/test.png')).contents.toString(), 'url(../images/test.png)'); + assert.strictEqual(f(mock('../../../images/test.png')).contents.toString(), 'url(../../../images/test.png)'); + assert.strictEqual(f(mock('images/test.png')).contents.toString(), 'url(../a/images/test.png)'); + + done(); + }); + }); +}); diff --git a/test/02-generate.js b/test/02-generate.js index eb72a470..f71bb8d0 100644 --- a/test/02-generate.js +++ b/test/02-generate.js @@ -22,7 +22,7 @@ describe('Module - generate', function () { it('should generate critical-path CSS', function (done) { var expected = read('expected/generate-default.css'); - var target = '.critical.css'; + var target = path.resolve('.critical.css'); critical.generate({ base: 'fixtures/', @@ -35,7 +35,7 @@ describe('Module - generate', function () { it('should generate critical-path CSS with query string in file name', function (done) { var expected = read('expected/generate-default.css'); - var target = '.critical.css'; + var target = path.resolve('.critical.css'); critical.generate({ base: 'fixtures/', @@ -48,7 +48,7 @@ describe('Module - generate', function () { it('should generate multi-dimension critical-path CSS', function (done) { var expected = read('expected/generate-adaptive.css', 'utf8'); - var target = '.adaptive.css'; + var target = path.resolve('.adaptive.css'); critical.generate({ base: 'fixtures/', @@ -66,7 +66,7 @@ describe('Module - generate', function () { it('should generate minified critical-path CSS', function (done) { var expected = read('expected/generate-default.css', true); - var target = '.critical.min.css'; + var target = path.resolve('.critical.min.css'); critical.generate({ base: 'fixtures/', @@ -80,7 +80,7 @@ describe('Module - generate', function () { it('should generate minified critical-path CSS successfully with external css file configured', function (done) { var expected = read('expected/generate-default.css', true); - var target = '.nostyle.css'; + var target = path.resolve('.nostyle.css'); critical.generate({ base: 'fixtures/', @@ -98,7 +98,7 @@ describe('Module - generate', function () { it('should inline relative images', function (done) { var expected = read('expected/generate-image.css'); - var target = '.image-relative.css'; + var target = path.resolve('.image-relative.css'); critical.generate({ base: 'fixtures/', @@ -115,7 +115,7 @@ describe('Module - generate', function () { it('should inline relative images from folder', function (done) { var expected = read('expected/generate-image.css'); - var target = '.image-relative.css'; + var target = path.resolve('.image-relative.css'); critical.generate({ base: 'fixtures/', @@ -124,6 +124,7 @@ describe('Module - generate', function () { 'fixtures/styles/image-relative.css' ], dest: target, + destFolder: 'folder/', width: 1300, height: 900, inlineImages: true @@ -132,7 +133,7 @@ describe('Module - generate', function () { it('should rewrite relative images for html outside root', function (done) { var expected = read('expected/generate-image-relative.css'); - var target = '.image-relative.css'; + var target = path.resolve('fixtures/folder/.image-relative.css'); critical.generate({ base: 'fixtures/', @@ -147,9 +148,9 @@ describe('Module - generate', function () { }, assertCritical(target, expected, done)); }); - it('should rewrite relative images for html outside root', function (done) { + it('should rewrite relative images for html outside root with css file', function (done) { var expected = read('expected/generate-image-relative-subfolder.css'); - var target = '.image-relative-subfolder.css'; + var target = path.resolve('fixtures/folder/subfolder/.image-relative-subfolder.css'); critical.generate({ base: 'fixtures/', @@ -161,9 +162,24 @@ describe('Module - generate', function () { }, assertCritical(target, expected, done)); }); + it('should rewrite relative images for html outside root destFolder option', function (done) { + var expected = read('expected/generate-image-relative-subfolder.css'); + var target = path.resolve('.image-relative-subfolder.css'); + + critical.generate({ + base: 'fixtures/', + src: 'folder/subfolder/generate-image-absolute.html', + destFolder: 'folder/subfolder', + // dest: target, + width: 1300, + height: 900, + inlineImages: false + }, assertCritical(target, expected, done, true)); + }); + it('should rewrite relative images for html inside root', function (done) { var expected = read('expected/generate-image-skip.css'); - var target = '.image-relative.css'; + var target = path.resolve('.image-relative.css'); critical.generate({ base: 'fixtures/', @@ -172,6 +188,7 @@ describe('Module - generate', function () { 'fixtures/styles/image-relative.css' ], dest: target, + destFolder: '.', width: 1300, height: 900, inlineImages: false @@ -180,7 +197,7 @@ describe('Module - generate', function () { it('should inline absolute images', function (done) { var expected = read('expected/generate-image.css'); - var target = '.image-absolute.css'; + var target = path.resolve('.image-absolute.css'); critical.generate({ base: 'fixtures/', @@ -189,6 +206,7 @@ describe('Module - generate', function () { 'fixtures/styles/image-absolute.css' ], dest: target, + destFolder: '.', width: 1300, height: 900, inlineImages: true @@ -197,7 +215,7 @@ describe('Module - generate', function () { it('should skip to big images', function (done) { var expected = read('expected/generate-image-big.css'); - var target = '.image-big.css'; + var target = path.resolve('.image-big.css'); critical.generate({ base: 'fixtures/', @@ -206,6 +224,7 @@ describe('Module - generate', function () { 'fixtures/styles/image-big.css' ], dest: target, + destFolder: '.', width: 1300, height: 900, inlineImages: true @@ -214,7 +233,7 @@ describe('Module - generate', function () { it('considers "inlineImages" option', function (done) { var expected = read('expected/generate-image-skip.css'); - var target = '.image-skip.css'; + var target = path.resolve('.image-skip.css'); critical.generate({ base: 'fixtures/', @@ -223,6 +242,7 @@ describe('Module - generate', function () { 'fixtures/styles/image-relative.css' ], dest: target, + destFolder: '.', width: 1300, height: 900, inlineImages: false @@ -231,7 +251,7 @@ describe('Module - generate', function () { it('should not screw up win32 paths', function (done) { var expected = read('expected/generate-image.css'); - var target = '.image.css'; + var target = path.resolve('.image.css'); critical.generate({ base: 'fixtures/', @@ -248,7 +268,7 @@ describe('Module - generate', function () { it('should respect pathPrefix', function (done) { var expected = read('expected/path-prefix.css'); - var target = '.path-prefix.css'; + var target = path.resolve('.path-prefix.css'); critical.generate({ base: 'fixtures/', @@ -265,7 +285,7 @@ describe('Module - generate', function () { it('should detect pathPrefix', function (done) { var expected = read('expected/path-prefix.css'); - var target = '.path-prefix.css'; + var target = path.resolve('.path-prefix.css'); critical.generate({ base: 'fixtures/', @@ -274,21 +294,23 @@ describe('Module - generate', function () { 'fixtures/styles/path-prefix.css' ], dest: target, + destFolder: '.', width: 1300, height: 900 }, assertCritical(target, expected, done)); }); - it('should generate and inline, if "inline" option is set', function (done) { + it.only('should generate and inline, if "inline" option is set', function (done) { var expected = read('expected/generateInline.html'); var target = '.generateInline.html'; critical.generate({ base: 'fixtures/', src: 'generateInline.html', + destFolder: '.', dest: target, inline: true - }, assertCritical(target, expected, done)); + }, assertCritical(path.join('fixtures', target), expected, done)); }); it('should generate and inline critical-path CSS', function (done) { @@ -298,9 +320,10 @@ describe('Module - generate', function () { critical.generate({ base: 'fixtures/', src: 'generateInline.html', + destFolder: '.', dest: target, inline: true - }, assertCritical(target, expected, done)); + }, assertCritical(path.join('fixtures', target), expected, done)); }); it('should generate and inline minified critical-path CSS', function (done) { @@ -310,10 +333,11 @@ describe('Module - generate', function () { critical.generate({ base: 'fixtures/', src: 'generateInline.html', + destFolder: '.', minify: true, dest: target, inline: true - }, assertCritical(target, expected, done)); + }, assertCritical(path.join('fixtures', target), expected, done)); }); it('should handle multiple calls', function (done) { @@ -346,7 +370,7 @@ describe('Module - generate', function () { it('should inline critical-path CSS ignoring remote stylesheets', function (done) { var expected = read('expected/generateInline-external-minified.html'); - var target = '.generateInline-external.html'; + var target = path.resolve('.generateInline-external.html'); critical.generate({ base: 'fixtures/', @@ -360,7 +384,7 @@ describe('Module - generate', function () { it('should inline critical-path CSS with extract option ignoring remote stylesheets', function (done) { var expected = read('expected/generateInline-external-extract.html'); - var target = '.generateInline-external-extract.html'; + var target = path.resolve('.generateInline-external-extract.html'); critical.generate({ base: 'fixtures/', @@ -375,7 +399,7 @@ describe('Module - generate', function () { it('should inline critical-path CSS without screwing svg images ', function (done) { var expected = read('expected/generateInline-svg.html'); - var target = '.generateInline-svg.html'; + var target = path.resolve('.generateInline-svg.html'); critical.generate({ base: 'fixtures/', @@ -388,7 +412,7 @@ describe('Module - generate', function () { it('should inline and extract critical-path CSS', function (done) { var expected = read('expected/generateInline-extract.html'); - var target = '.generateInline-extract.html'; + var target = path.resolve('.generateInline-extract.html'); critical.generate({ base: 'fixtures/', @@ -402,7 +426,7 @@ describe('Module - generate', function () { it('should inline and extract critical-path CSS from html source', function (done) { var expected = read('expected/generateInline-extract.html'); - var target = '.generateInline-extract-src.html'; + var target = path.resolve('.generateInline-extract-src.html'); critical.generate({ base: 'fixtures/', @@ -416,7 +440,7 @@ describe('Module - generate', function () { it('should consider "ignore" option', function (done) { var expected = read('expected/generate-ignore.css'); - var target = '.ignore.css'; + var target = path.resolve('.ignore.css'); critical.generate({ base: 'fixtures/', diff --git a/test/fixtures/.generateInline.html b/test/fixtures/.generateInline.html new file mode 100644 index 00000000..9469b599 --- /dev/null +++ b/test/fixtures/.generateInline.html @@ -0,0 +1,390 @@ + + + + + critical css test + + + + + + + + + + + + + + + + + +
+
+ +

critical css test

+
+ +
+

'Allo, 'Allo!

+

Always a pleasure scaffolding your apps.

+

Splendid!

+
+ +
+
+

HTML5 Boilerplate

+

HTML5 Boilerplate is a professional front-end template for building fast, robust, and adaptable web apps or sites.

+ +

Bootstrap

+

Sleek, intuitive, and powerful mobile first front-end framework for faster and easier web development.

+
+
+ + +
+ + diff --git a/test/helper/testhelper.js b/test/helper/testhelper.js index 6fd8aa7a..b8948626 100644 --- a/test/helper/testhelper.js +++ b/test/helper/testhelper.js @@ -8,12 +8,22 @@ var nn = require('normalize-newline'); function readAndRemove(file, minify) { var content = read(file, minify); - fs.unlinkSync(path.join(__dirname, '..', file)); + // if (path.isAbsolute(file)) { + // fs.unlinkSync(file); + // } else { + // fs.unlinkSync(path.join(__dirname, '..', file)); + // } + return content; } function read(file, minify) { - var content = fs.readFileSync(path.join(__dirname, '..', file), 'utf8'); + var content = ''; + if (path.isAbsolute(file)) { + content = fs.readFileSync(file, 'utf8'); + } else { + content = fs.readFileSync(path.join(__dirname, '..', file), 'utf8'); + } return minify ? new CleanCSS().minify(content).styles : nn(content); } @@ -24,14 +34,15 @@ function read(file, minify) { * @param done * @returns {Function} */ -function assertCritical(target, expected, done) { +function assertCritical(target, expected, done, skipTarget) { return function (err, output) { assert.isNull(err, Boolean(err) && err); assert.isDefined(output, 'Should produce output'); - var dest = readAndRemove(target); - - assert.strictEqual(nn(dest), nn(expected)); + if (!skipTarget) { + var dest = readAndRemove(target); + assert.strictEqual(nn(dest), nn(expected)); + } assert.strictEqual(nn(output), nn(expected)); done(); From eedd20af98de5a07f551c44f175b0ba0f580d228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Z=C3=B6rb?= Date: Sun, 14 Aug 2016 23:23:13 +0200 Subject: [PATCH 21/27] fixed tests --- README.md | 4 +- cli.js | 4 +- lib/core.js | 1 + lib/file-helper.js | 4 +- test/02-generate.js | 8 +- test/05-cli.js | 2 +- test/fixtures/.include.css | 332 ------------------------------------- test/helper/testhelper.js | 13 +- 8 files changed, 21 insertions(+), 347 deletions(-) delete mode 100644 test/fixtures/.include.css diff --git a/README.md b/README.md index f31ea9b1..d91d8cc7 100644 --- a/README.md +++ b/README.md @@ -213,7 +213,8 @@ critical.generate({ | base | `string` | `path.dirname(src)` or `process.cwd()` | Base directory in which the source and destination are to be written | | html | `string` | | HTML source to be operated against. This option takes precedence over the `src` option | | src | `string` | | Location of the HTML source to be operated against | -| dest | `string` | | Location of where to save the output of an operation | +| dest | `string` | | Location of where to save the output of an operation (will be relative to base if no absolute path is set) | +| destFolder | `string` | `''` | Subfolder, relative to base directory. Only relevant without src (if raw html is provided) or if the destination is outside base | | width | `integer` | `900` | Width of the target viewport | | height | `integer` | `1300` | Height of the target viewport | | dimensions | `array` | `[]` | An array of objects containing height and width. Takes precedence over `width` and `height` if set @@ -222,7 +223,6 @@ critical.generate({ | inlineImages | `boolean` | `false` | Inline images | assetPaths | `array` | `[]` | List of directories/urls where the inliner should start looking for assets | maxImageFileSize | `integer` | `10240`| Sets a max file size (in bytes) for base64 inlined images -| folder | `string` | `''` | Subfolder, relative to base directory. Only relevant without src if raw html is provided | | timeout | `integer` | `30000`| Sets a maximum timeout for the operation | pathPrefix | `string` | `/` | Path to prepend CSS assets with. You *must* make this path absolute if you are going to be using critical in multiple target files in disparate directory depths. (eg. targeting both `/index.html` and `/admin/index.html` would require this path to start with `/` or it wouldn't work.) | include | `array` | `[]` | Force include css rules. See [`penthouse#usage`](https://github.com/pocketjoso/penthouse#usage-1). diff --git a/cli.js b/cli.js index 647f043c..8728df4e 100755 --- a/cli.js +++ b/cli.js @@ -119,9 +119,9 @@ cli.flags = _.reduce(cli.flags, function (res, val, key) { }, {}); function error(err) { - process.stderr.write(indentString(err.message || err, chalk.red(' Error: '))); + process.stderr.write(indentString((chalk.red('Error: ') + err.message || err), 3)); process.stderr.write(os.EOL); - process.stderr.write(indentString(help.join(os.EOL), 1, ' ')); + process.stderr.write(indentString(help.join(os.EOL), 3)); process.exit(1); } diff --git a/lib/core.js b/lib/core.js index b7df671f..1a56a6a1 100644 --- a/lib/core.js +++ b/lib/core.js @@ -166,6 +166,7 @@ function computeCritical(dimensions, opts) { url: file.getPenthouseUrl(opts, htmlfile, server.port), css: htmlfile.cssPath, forceInclude: opts.include || [], + timeout: opts.timeout, maxEmbeddedBase64Length: opts.maxImageFileSize || 10240, width: dimensions.width, height: dimensions.height diff --git a/lib/file-helper.js b/lib/file-helper.js index cfb0de27..09bc0c9d 100644 --- a/lib/file-helper.js +++ b/lib/file-helper.js @@ -156,8 +156,10 @@ function requestAsync(uri) { * @returns {string} */ function guessBasePath(opts) { - if (opts.src && !isExternal(opts.src)) { + if (opts.src && !isExternal(opts.src) && !isVinyl(opts.src)) { return path.dirname(opts.src); + } else if (opts.src && isVinyl(opts.src)) { + return opts.src.dirname; } return process.cwd(); diff --git a/test/02-generate.js b/test/02-generate.js index 399eed16..2779d11e 100644 --- a/test/02-generate.js +++ b/test/02-generate.js @@ -61,8 +61,8 @@ describe('Module - generate', function () { }); it('should ignore stylesheets blocked due to 403', function (done) { - var expected = ''; - var target = '.403.css'; + var expected = '\n'; + var target = path.resolve('.403.css'); critical.generate({ base: 'fixtures/', @@ -74,8 +74,8 @@ describe('Module - generate', function () { }); it('should ignore stylesheets blocked due to 404', function (done) { - var expected = ''; - var target = '.404.css'; + var expected = '\n'; + var target = path.resolve('.404.css'); critical.generate({ base: 'fixtures/', diff --git a/test/05-cli.js b/test/05-cli.js index 5c2d6d35..8805c8d9 100644 --- a/test/05-cli.js +++ b/test/05-cli.js @@ -101,7 +101,7 @@ describe('CLI', function () { }); }); - it('should exit with code 1 and show help', function (done) { + it.only('should exit with code 1 and show help', function (done) { execFile('node', [path.join(__dirname, '../', this.pkg.bin.critical), 'fixtures/not-exists.html'], function (err, stdout, stderr) { assert.typeOf(err, 'Error'); assert.strictEqual(err.code, 1); diff --git a/test/fixtures/.include.css b/test/fixtures/.include.css deleted file mode 100644 index aae9e23d..00000000 --- a/test/fixtures/.include.css +++ /dev/null @@ -1,332 +0,0 @@ -body { - padding-top: 20px; - padding-bottom: 20px; -} - -.header, -.marketing { - padding-left: 15px; - padding-right: 15px; -} - -.header { - border-bottom: 1px solid #e5e5e5; -} - -.header h3 { - margin-top: 0; - margin-bottom: 0; - line-height: 40px; - padding-bottom: 19px; -} - -.jumbotron { - text-align: center; - border-bottom: 1px solid #e5e5e5; -} - -.jumbotron .btn { - font-size: 21px; - padding: 14px 24px; -} - -.marketing { - margin: 40px 0; -} - -@media screen and (min-width: 768px) { - .container { - max-width: 730px; - } - - .header, - .marketing { - padding-left: 0; - padding-right: 0; - } - - .header { - margin-bottom: 30px; - } - - .jumbotron { - border-bottom: 0; - } -} - -html { - font-family: sans-serif; - -webkit-text-size-adjust: 100%; - -ms-text-size-adjust: 100%; -} - -body { - margin: 0; -} - -a { - background: transparent; -} - -h1 { - margin: .67em 0; - font-size: 2em; -} - -* { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} - -*:before, -*:after { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} - -html { - font-size: 62.5%; -} - -body { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - line-height: 1.42857143; - color: #333; - background-color: #fff; -} - -a { - color: #428bca; - text-decoration: none; -} - -h1, -h3, -h4 { - font-family: inherit; - font-weight: 500; - line-height: 1.1; - color: inherit; -} - -h1, -h3 { - margin-top: 20px; - margin-bottom: 10px; -} - -h4 { - margin-top: 10px; - margin-bottom: 10px; -} - -h1 { - font-size: 36px; -} - -h3 { - font-size: 24px; -} - -h4 { - font-size: 18px; -} - -p { - margin: 0 0 10px; -} - -.lead { - margin-bottom: 20px; - font-size: 16px; - font-weight: 200; - line-height: 1.4; -} - -@media (min-width: 768px) { - .lead { - font-size: 21px; - } -} - -.text-muted { - color: #999; -} - -ul { - margin-top: 0; - margin-bottom: 10px; -} - -.container { - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} - -@media (min-width: 768px) { - .container { - width: 750px; - } -} - -@media (min-width: 992px) { - .container { - width: 970px; - } -} - -@media (min-width: 1200px) { - .container { - width: 1170px; - } -} - -.row { - margin-right: -15px; - margin-left: -15px; -} - -.col-lg-6 { - position: relative; - min-height: 1px; - padding-right: 15px; - padding-left: 15px; -} - -@media (min-width: 1200px) { - .col-lg-6 { - float: left; - } - - .col-lg-6 { - width: 50%; - } -} - -.btn { - display: inline-block; - padding: 6px 12px; - margin-bottom: 0; - font-size: 14px; - font-weight: normal; - line-height: 1.42857143; - text-align: center; - white-space: nowrap; - vertical-align: middle; - background-image: none; - border: 1px solid transparent; - border-radius: 4px; -} - -.btn-success { - color: #fff; - background-color: #5cb85c; - border-color: #4cae4c; -} - -.btn-lg { - padding: 10px 16px; - font-size: 18px; - line-height: 1.33; - border-radius: 6px; -} - -.nav { - padding-left: 0; - margin-bottom: 0; - list-style: none; -} - -.nav > li { - position: relative; - display: block; -} - -.nav > li > a { - position: relative; - display: block; - padding: 10px 15px; -} - -.nav-pills > li { - float: left; -} - -.nav-pills > li > a { - border-radius: 4px; -} - -.nav-pills > li + li { - margin-left: 2px; -} - -.nav-pills > li.active > a { - color: #fff; - background-color: #428bca; -} - -.jumbotron { - padding: 30px; - margin-bottom: 30px; - color: inherit; - background-color: #eee; -} - -.jumbotron h1 { - color: inherit; -} - -.jumbotron p { - margin-bottom: 15px; - font-size: 21px; - font-weight: 200; -} - -.container .jumbotron { - border-radius: 6px; -} - -@media screen and (min-width: 768px) { - .jumbotron { - padding-top: 48px; - padding-bottom: 48px; - } - - .container .jumbotron { - padding-right: 60px; - padding-left: 60px; - } - - .jumbotron h1 { - font-size: 63px; - } -} - -.container:before, -.container:after, -.row:before, -.row:after, -.nav:before, -.nav:after { - display: table; - content: " "; -} - -.container:after, -.row:after, -.nav:after { - clear: both; -} - -.pull-right { - float: right !important; -} - -@-ms-viewport { - width: device-width; -} diff --git a/test/helper/testhelper.js b/test/helper/testhelper.js index 57943fe9..72b424f8 100644 --- a/test/helper/testhelper.js +++ b/test/helper/testhelper.js @@ -7,13 +7,16 @@ var CleanCSS = require('clean-css'); var nn = require('normalize-newline'); function readAndRemove(file, minify) { - var content = read(file, minify); - if (path.isAbsolute(file)) { - fs.unlinkSync(file); - } else { - fs.unlinkSync(path.join(__dirname, '..', file)); + var testBase = path.join(__dirname, '..'); + if (!path.isAbsolute(file) && fs.existsSync(path.join(testBase, file))) { + file = path.join(testBase, file); + } else if (!path.isAbsolute(file) && fs.existsSync(path.join(testBase, 'fixtures', file))) { + file = path.join(testBase, 'fixtures', file); } + var content = read(file, minify); + fs.unlinkSync(file); + return content; } From 16889d8a59bede85c072c606d81e84a7a56efb51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Z=C3=B6rb?= Date: Sun, 14 Aug 2016 23:23:37 +0200 Subject: [PATCH 22/27] run all tests --- test/05-cli.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/05-cli.js b/test/05-cli.js index 8805c8d9..5c2d6d35 100644 --- a/test/05-cli.js +++ b/test/05-cli.js @@ -101,7 +101,7 @@ describe('CLI', function () { }); }); - it.only('should exit with code 1 and show help', function (done) { + it('should exit with code 1 and show help', function (done) { execFile('node', [path.join(__dirname, '../', this.pkg.bin.critical), 'fixtures/not-exists.html'], function (err, stdout, stderr) { assert.typeOf(err, 'Error'); assert.strictEqual(err.code, 1); From 49df47f319115d2915d59d5794e48fbb566be999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Z=C3=B6rb?= Date: Sun, 14 Aug 2016 23:26:21 +0200 Subject: [PATCH 23/27] some text changes --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d91d8cc7..c2fc6904 100644 --- a/README.md +++ b/README.md @@ -214,7 +214,7 @@ critical.generate({ | html | `string` | | HTML source to be operated against. This option takes precedence over the `src` option | | src | `string` | | Location of the HTML source to be operated against | | dest | `string` | | Location of where to save the output of an operation (will be relative to base if no absolute path is set) | -| destFolder | `string` | `''` | Subfolder, relative to base directory. Only relevant without src (if raw html is provided) or if the destination is outside base | +| destFolder | `string` | `''` | Subfolder relative to base directory. Only relevant without src (if raw html is provided) or if the destination is outside base | | width | `integer` | `900` | Width of the target viewport | | height | `integer` | `1300` | Height of the target viewport | | dimensions | `array` | `[]` | An array of objects containing height and width. Takes precedence over `width` and `height` if set From 4b56cee8db6a64483c99fdb9fb4e57e16e3fb0f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Z=C3=B6rb?= Date: Tue, 16 Aug 2016 23:43:23 +0200 Subject: [PATCH 24/27] fixed tests --- test/02-generate.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/02-generate.js b/test/02-generate.js index 2779d11e..d0c4b6ca 100644 --- a/test/02-generate.js +++ b/test/02-generate.js @@ -61,7 +61,7 @@ describe('Module - generate', function () { }); it('should ignore stylesheets blocked due to 403', function (done) { - var expected = '\n'; + var expected = ''; var target = path.resolve('.403.css'); critical.generate({ @@ -74,7 +74,7 @@ describe('Module - generate', function () { }); it('should ignore stylesheets blocked due to 404', function (done) { - var expected = '\n'; + var expected = ''; var target = path.resolve('.404.css'); critical.generate({ From 1dcee2433a7c059e312a46dcbdd304a8a2dd4cb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Z=C3=B6rb?= Date: Wed, 17 Aug 2016 23:14:29 +0200 Subject: [PATCH 25/27] force re-run appveyor after penthouse fix --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ce26c9f7..9c189226 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "mime-types": "^2.1.6", "oust": "^0.3.0", "parseurl": "^1.3.0", - "penthouse": "^0.9.2", + "penthouse": "^0.9.12", "postcss": "^5.0.5", "postcss-image-inliner": "^0.3.0", "request": "^2.61.0", From 1f12d9588c0d50db3078e64c6c26fcddb65034b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Z=C3=B6rb?= Date: Wed, 28 Sep 2016 06:42:19 +0200 Subject: [PATCH 26/27] fixed tests --- test/expected/generateInline-external-extract.html | 2 +- test/expected/generateInline-external-extract2.html | 2 +- test/expected/generateInline-external-minified.html | 2 +- test/expected/generateInline-external-minified2.html | 2 +- test/expected/generateInline-extract.html | 2 +- test/expected/generateInline-minified.html | 2 +- test/expected/generateInline-svg.html | 2 +- test/expected/generateInline.html | 2 +- test/expected/streams-default.html | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test/expected/generateInline-external-extract.html b/test/expected/generateInline-external-extract.html index 4d2d0c4b..e307314a 100644 --- a/test/expected/generateInline-external-extract.html +++ b/test/expected/generateInline-external-extract.html @@ -17,7 +17,7 @@ - + diff --git a/test/expected/generateInline-external-extract2.html b/test/expected/generateInline-external-extract2.html index 5e7085b2..dc6ab149 100644 --- a/test/expected/generateInline-external-extract2.html +++ b/test/expected/generateInline-external-extract2.html @@ -17,7 +17,7 @@ - + diff --git a/test/expected/generateInline-external-minified.html b/test/expected/generateInline-external-minified.html index 5cc81cab..fe8c1e9c 100644 --- a/test/expected/generateInline-external-minified.html +++ b/test/expected/generateInline-external-minified.html @@ -17,7 +17,7 @@ - + diff --git a/test/expected/generateInline-external-minified2.html b/test/expected/generateInline-external-minified2.html index b2894edd..1512e70f 100644 --- a/test/expected/generateInline-external-minified2.html +++ b/test/expected/generateInline-external-minified2.html @@ -17,7 +17,7 @@ - + diff --git a/test/expected/generateInline-extract.html b/test/expected/generateInline-extract.html index 8c530485..7fe2b107 100644 --- a/test/expected/generateInline-extract.html +++ b/test/expected/generateInline-extract.html @@ -15,7 +15,7 @@ - + diff --git a/test/expected/generateInline-minified.html b/test/expected/generateInline-minified.html index d31f72bc..bfb34ba2 100644 --- a/test/expected/generateInline-minified.html +++ b/test/expected/generateInline-minified.html @@ -15,7 +15,7 @@ - + diff --git a/test/expected/generateInline-svg.html b/test/expected/generateInline-svg.html index 58736451..156c2dd7 100644 --- a/test/expected/generateInline-svg.html +++ b/test/expected/generateInline-svg.html @@ -12,7 +12,7 @@ - + diff --git a/test/expected/generateInline.html b/test/expected/generateInline.html index 9469b599..eeac33f0 100644 --- a/test/expected/generateInline.html +++ b/test/expected/generateInline.html @@ -347,7 +347,7 @@ - + diff --git a/test/expected/streams-default.html b/test/expected/streams-default.html index a4e88887..f0f2897c 100644 --- a/test/expected/streams-default.html +++ b/test/expected/streams-default.html @@ -70,7 +70,7 @@ - + From 2e902b30478f88fd7c366500ae78dc58c636d061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Z=C3=B6rb?= Date: Wed, 28 Sep 2016 06:56:31 +0200 Subject: [PATCH 27/27] fixed last test --- test/expected/generate-ignorefont.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/expected/generate-ignorefont.html b/test/expected/generate-ignorefont.html index 6ff304dd..68d1f2cf 100644 --- a/test/expected/generate-ignorefont.html +++ b/test/expected/generate-ignorefont.html @@ -10,7 +10,7 @@ - +

should be styled by @font-face