Skip to content
This repository has been archived by the owner on Dec 7, 2021. It is now read-only.

Commit

Permalink
Minify inline css (#514)
Browse files Browse the repository at this point in the history
* refactor optimize-streams, add better logging

* add temporary InlineCSSOptimizeStream to build process

Splitting and rejoining CSS styles is currently blocked in the
polymer-build library (see Polymer/polymer-build#40).
Until that issue is resolved and merged, the CLI can still optimize
inline styles via css-slam.

* add tests

* update CHANGELOG

* update test
  • Loading branch information
FredKSchott authored Dec 15, 2016
1 parent 0760712 commit c0edb07
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 37 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- Added an `install` command, which can install "variants" of Bower dependencies. TODO(justinfagnani): document variants.
- Added support for v7.x of Node.js, dropped support for v5.x. Please move to an [actively maintained version of Node.js](https://github.com/nodejs/LTS) for the best experience.
- Upgrade [web-component-tester 6.0](https://github.com/Polymer/web-component-tester/blob/master/CHANGELOG.md) which brings a number of breaking changes to the `test` command.
- `build`: Fix bug where CSS was not getting minified

## v0.17.0

Expand Down
8 changes: 7 additions & 1 deletion src/build/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {dest} from 'vinyl-fs';
import mergeStream = require('merge-stream');
import {PolymerProject, addServiceWorker, forkStream, SWConfig} from 'polymer-build';

import {JSOptimizeStream, CSSOptimizeStream, HTMLOptimizeStream} from './optimize-streams';
import {InlineCSSOptimizeStream, JSOptimizeStream, CSSOptimizeStream, HTMLOptimizeStream} from './optimize-streams';

import {ProjectConfig} from 'polymer-project-config';
import {PrefetchTransform} from './prefetch';
Expand Down Expand Up @@ -70,6 +70,9 @@ export async function build(
.pipe(polymerProject.splitHtml())
.pipe(gulpif(/\.js$/, new JSOptimizeStream(optimizeOptions.js)))
.pipe(gulpif(/\.css$/, new CSSOptimizeStream(optimizeOptions.css)))
// TODO(fks): Remove this InlineCSSOptimizeStream stream once CSS
// is properly being isolated by splitHtml() & rejoinHtml().
.pipe(gulpif(/\.html$/, new InlineCSSOptimizeStream(optimizeOptions.css)))
.pipe(gulpif(/\.html$/, new HTMLOptimizeStream(optimizeOptions.html)))
.pipe(polymerProject.rejoinHtml());

Expand All @@ -79,6 +82,9 @@ export async function build(
.pipe(polymerProject.splitHtml())
.pipe(gulpif(/\.js$/, new JSOptimizeStream(optimizeOptions.js)))
.pipe(gulpif(/\.css$/, new CSSOptimizeStream(optimizeOptions.css)))
// TODO(fks): Remove this InlineCSSOptimizeStream stream once CSS
// is properly being isolated by splitHtml() & rejoinHtml().
.pipe(gulpif(/\.html$/, new InlineCSSOptimizeStream(optimizeOptions.css)))
.pipe(gulpif(/\.html$/, new HTMLOptimizeStream(optimizeOptions.html)))
.pipe(polymerProject.rejoinHtml());

Expand Down
50 changes: 32 additions & 18 deletions src/build/optimize-streams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* http://polymer.github.io/PATENTS.txt
*/

import {css as cssSlam} from 'css-slam';
import * as cssSlam from 'css-slam';
import {minify as htmlMinify, Options as HTMLMinifierOptions} from 'html-minifier';
import * as logging from 'plylog';
import {Transform} from 'stream';
Expand All @@ -34,32 +34,34 @@ export type CSSOptimizeOptions = {
/**
* GenericOptimizeStream is a generic optimization stream. It can be extended
* to create a new kind of specific file-type optimizer, or it can be used
* directly to create an ad-hoc optimization stream for certain types of files.
* directly to create an ad-hoc optimization stream for different libraries.
* If the transform library throws an exception when run, the file will pass
* through unaffected.
*/
export class GenericOptimizeStream extends Transform {
validExtension: string;
optimizer: (content: string, options: any) => string;
optimizerName: string;
optimizerOptions: any;

constructor(
validExtension: string,
optimizerName: string,
optimizer: (content: string, optimizerOptions: any) => string,
optimizerOptions: any) {
super({objectMode: true});
this.optimizer = optimizer;
this.validExtension = validExtension;
this.optimizerName = optimizerName;
this.optimizerOptions = optimizerOptions || {};
}

_transform(file: File, _encoding: string, callback: FileCB): void {
if (file.contents && file.path.endsWith(`${this.validExtension}`)) {
if (file.contents) {
try {
let contents = file.contents.toString();
contents = this.optimizer(contents, this.optimizerOptions);
file.contents = new Buffer(contents);
} catch (error) {
logger.warn(
`Unable to optimize ${this.validExtension} file ${file.path}`,
`${this.optimizerName}: Unable to optimize ${file.path}`,
{err: error.message || error});
}
}
Expand All @@ -69,8 +71,6 @@ export class GenericOptimizeStream extends Transform {

/**
* JSOptimizeStream optimizes JS files that pass through it (via uglify).
* If a file is not a `.js` file or if uglify throws an exception when run,
* the file will pass through unaffected.
*/
export class JSOptimizeStream extends GenericOptimizeStream {
constructor(options: UglifyOptions) {
Expand All @@ -82,19 +82,16 @@ export class JSOptimizeStream extends GenericOptimizeStream {
};
// We automatically add the fromString option because it is required.
let uglifyOptions = Object.assign({fromString: true}, options);
super('.js', uglifyOptimizer, uglifyOptions);
super('uglify-js', uglifyOptimizer, uglifyOptions);
}
}


/**
* CSSOptimizeStream optimizes CSS files that pass through it (via css-slam).
* If a file is not a `.css` file or if css-slam throws an exception when run,
* the file will pass through unaffected.
* CSSOptimizeStream optimizes CSS that pass through it (via css-slam).
*/
export class CSSOptimizeStream extends GenericOptimizeStream {
constructor(options: CSSOptimizeOptions) {
super('.css', cssSlam, options);
super('css-slam', cssSlam.css, options);
}

_transform(file: File, encoding: string, callback: FileCB): void {
Expand All @@ -107,14 +104,31 @@ export class CSSOptimizeStream extends GenericOptimizeStream {
}
}

/**
* InlineCSSOptimizeStream optimizes inlined CSS (found in HTML files) that
* passes through it (via css-slam).
*/
export class InlineCSSOptimizeStream extends GenericOptimizeStream {
constructor(options: CSSOptimizeOptions) {
super('css-slam', cssSlam.html, options);
}

_transform(file: File, encoding: string, callback: FileCB): void {
// css-slam will only be run if the `stripWhitespace` option is true.
// Because css-slam itself doesn't accept any options, we handle the
// option here before transforming.
if (this.optimizerOptions.stripWhitespace) {
super._transform(file, encoding, callback);
}
}
}

/**
* HTMLOptimizeStream optimizes HTML files that pass through it
* (via html-minifier). If a file is not a `.html` file or if html-minifier
* throws an exception when run, the file will pass through unaffected.
* (via html-minifier).
*/
export class HTMLOptimizeStream extends GenericOptimizeStream {
constructor(options: HTMLMinifierOptions) {
super('.html', htmlMinify, options);
super('html-minify', htmlMinify, options);
}
}
67 changes: 49 additions & 18 deletions test/unit/build/optimize_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const optimizeStreams = require('../../../lib/build/optimize-streams');

const JSOptimizeStream = optimizeStreams.JSOptimizeStream;
const CSSOptimizeStream = optimizeStreams.CSSOptimizeStream;
const InlineCSSOptimizeStream = optimizeStreams.InlineCSSOptimizeStream;
const HTMLOptimizeStream = optimizeStreams.HTMLOptimizeStream;

suite('optimize-streams', () => {
Expand All @@ -27,24 +28,6 @@ suite('optimize-streams', () => {
stream.on('error', cb);
}

test('css', (done) => {
let stream = vfs.src([
{
path: 'foo.css',
contents: '/* comment */ selector { property: value; }',
},
]);
let op = stream.pipe(new CSSOptimizeStream({stripWhitespace: true}));
assert.notEqual(stream, op, 'stream should be wrapped');
testStream(op, (error, f) => {
if (error) {
return done(error);
}
assert.equal(f.contents.toString(), 'selector{property:value;}');
done();
});
});

test('js', (done) => {
let stream = vfs.src([
{
Expand Down Expand Up @@ -100,4 +83,52 @@ suite('optimize-streams', () => {
done();
});
});

test('css', (done) => {
let stream = vfs.src([
{
path: 'foo.css',
contents: '/* comment */ selector { property: value; }',
},
]);
let op = stream.pipe(new CSSOptimizeStream({stripWhitespace: true}));
testStream(op, (error, f) => {
if (error) {
return done(error);
}
assert.equal(f.contents.toString(), 'selector{property:value;}');
done();
});
});

test('inline css', (done) => {
let expected =`<style>foo{background:blue;}</style>`;
let stream = vfs.src([
{
path: 'foo.html',
contents: `
<!doctype html>
<html>
<head>
<style>
foo {
background: blue;
}
</style>
</head>
<body></body>
</html>
`,
},
], {cwdbase: true});
let op = stream.pipe(new InlineCSSOptimizeStream({stripWhitespace: true}));
testStream(op, (error, f) => {
if (error) {
return done(error);
}
assert.include(f.contents.toString(), expected);
done();
});
});

});

0 comments on commit c0edb07

Please sign in to comment.