css-inliner
Advanced tools
Comparing version 1.0.0 to 1.1.0
@@ -24,4 +24,3 @@ // This stage adds CSS rules back into the document, by creating a <style> | ||
debug('%s: Adding <style> element to document', context.filename); | ||
const compress = context.compress; | ||
const styleElement = rulesToStyleElement(rules, compress); | ||
const styleElement = rulesToStyleElement(rules); | ||
insertStyleElement(dom, styleElement); | ||
@@ -36,4 +35,4 @@ } | ||
// Returns the <style> element with all remaining rules | ||
function rulesToStyleElement(rules, compress) { | ||
const css = stringifyRules(rules, compress); | ||
function rulesToStyleElement(rules) { | ||
const css = stringifyRules(rules); | ||
const textNode = { | ||
@@ -40,0 +39,0 @@ type: ElementType.Text, |
@@ -29,6 +29,4 @@ // Processing context for the pipeline. | ||
// List of CSS rules, updated (not mutated) between stages | ||
rules: null, | ||
// True to compress output | ||
compress: false | ||
rules: null | ||
}); | ||
@@ -41,3 +41,3 @@ // This stage converts dom back to HTML. | ||
if (isDoctype && isXHTML(node)) | ||
stack.get(0).isXML = true; | ||
stack.get(0).isXHTML = true; | ||
return stringifyDirective(node); | ||
@@ -125,12 +125,23 @@ } | ||
const isSVG = !!nextStack.find(element => element.name === 'svg'); | ||
const isXML = stack.get(0).isXML || isSVG; | ||
// In HTML5 SVG and MathML are "foreign elements", they have the same | ||
// production rules as XML, specifically empty elements can use self-closing | ||
// tags (smaller output) | ||
const isForeign = !!nextStack.find(element => element.name === 'svg'); | ||
// XML production rules apply to either XHTML documents or foreign elements | ||
// in HTML5 documents | ||
const isXML = stack.get(0).isXHTML || isForeign; | ||
const isVoidElement = VOID_ELEMENTS.has(name); | ||
if (isXML) { | ||
const isSelfClosing = !content; | ||
return isSelfClosing ? `<${openTag}/>` : `<${openTag}>${content}</${name}>`; | ||
} else { | ||
const isVoidElement = VOID_ELEMENTS.has(name); | ||
// In XHTML void elements have an empty content model, and so should use | ||
// self-closing tags (which also parses as HTML), but elements with a | ||
// content model must use open and close tags, even if they have no content | ||
// (e.g. parsers choke on <title />) | ||
// | ||
// Foreign elements (SVG/MathML in HTML5) can deal with self-closing tags | ||
const isSelfClosing = isVoidElement || (isForeign && !content); | ||
return isSelfClosing ? `<${openTag} />` : `<${openTag}>${content}</${name}>`; | ||
} else | ||
// In HTML void elements have no content, only use a single tag (implied close) | ||
return isVoidElement ? `<${openTag}>` : `<${openTag}>${content}</${name}>`; | ||
} | ||
} | ||
@@ -137,0 +148,0 @@ |
@@ -37,3 +37,2 @@ // The CSS inliner. | ||
// Supported options: | ||
// compress - True to compress output (default) | ||
// directory - Directory to load CSS resources from (defaults to cwd) | ||
@@ -48,5 +47,4 @@ // loadAsync - Load CSS from path name, resolves to Buffer/String | ||
const cache = new Cache(options); | ||
const compress = (options.compress) === false ? false : true; | ||
const template = options.template; | ||
this._context = new Context({ cache, compress, inliner: this, template }); | ||
this._context = new Context({ cache, inliner: this, template }); | ||
@@ -53,0 +51,0 @@ // Default handler for reporting any warnings |
// Convert PostCSS rules back into CSS text. | ||
// | ||
// PostCSS has a toString() method, but it preserves comments and whitespace, | ||
// and we have an option to compress the output by stripping unnecesssary junk. | ||
@@ -9,11 +6,4 @@ 'use strict'; | ||
// Call with a list of rules, and whether or not we want to compress the CSS. | ||
module.exports = function stringifyRules(rules, compress) { | ||
const css = compress ? | ||
stringifyAndCompress(rules) : | ||
stringifyDontCompress(rules); | ||
return css; | ||
}; | ||
module.exports = stringifyAllRules; | ||
// PostCSS rules -> string | ||
@@ -23,13 +13,8 @@ // | ||
// means of stringifyRule | ||
function stringifyAndCompress(rules) { | ||
return rules.map(stringifyRule).join(''); | ||
function stringifyAllRules(rules) { | ||
const css = rules.map(stringifyRule).join(''); | ||
return css; | ||
} | ||
// PostCSS rules -> string | ||
function stringifyDontCompress(rules) { | ||
return rules.map(rule => rule.toString()).join('\n'); | ||
} | ||
// PostCSS rule -> string | ||
@@ -42,5 +27,7 @@ // | ||
case 'atrule': { | ||
const childRules = rule.nodes; | ||
const atRuleBody = stringifyAndCompress(childRules); | ||
return `@${rule.name} ${rule.params}{${atRuleBody}}`; | ||
if (rule.nodes) { | ||
const atRuleBody = stringifyAllRules(rule.nodes); | ||
return `@${rule.name} ${rule.params}{${atRuleBody}}`; | ||
} else | ||
return `@${rule.name} ${rule.params};`; | ||
} | ||
@@ -47,0 +34,0 @@ case 'rule': { |
{ | ||
"name": "css-inliner", | ||
"version": "1.0.0", | ||
"version": "1.1.0", | ||
"description": "", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
@@ -6,2 +6,9 @@ # CSSInliner | ||
[![NPM](https://img.shields.io/npm/v/css-inliner.svg?style=flat-square&label=latest)](https://www.npmjs.com/package/css-inliner) | ||
[![Changelog](https://img.shields.io/badge/see-CHANGELOG-red.svg?style=flat-square)](https://github.com/broadly/css-inliner/blob/master/CHANGELOG.md) | ||
<img width="12" src="data:image/gif;base64,R0lGODlhAQABAPAAAP"> | ||
[![Travis.ci](https://img.shields.io/travis/broadly/css-inliner.svg?style=flat-square)](https://travis-ci.org/broadly/css-inliner) | ||
<img width="12" src="data:image/gif;base64,R0lGODlhAQABAPAAAP"> | ||
# Usage | ||
@@ -8,0 +15,0 @@ |
@@ -7,3 +7,2 @@ 'use strict'; | ||
const domToHTML = require('../lib/dom_to_html'); | ||
const DOMUtils = require('domutils'); | ||
const parseHTML = require('../lib/parse_html'); | ||
@@ -14,12 +13,3 @@ | ||
const css = | ||
`@media print { | ||
.footer { | ||
display: none; | ||
} | ||
} | ||
h1:hover, h1:before { | ||
color : red ; | ||
background: none ; | ||
}`; | ||
const css = `@media print{.footer{display:none}}h1:hover,h1:before{color:red;background:none}`; | ||
@@ -26,0 +16,0 @@ function parseAndAddRules(html) { |
@@ -128,3 +128,3 @@ 'use strict'; | ||
const html = '<svg autofocus></svg>'; | ||
const expected = '<svg autofocus/>'; | ||
const expected = '<svg autofocus />'; | ||
const actual = roundTrip(html); | ||
@@ -137,3 +137,3 @@ assert.equal(actual, expected); | ||
it('should produce element and contents', function() { | ||
const html = '<svg><rect/><path/></svg>'; | ||
const html = '<svg><rect /><path /></svg>'; | ||
const expected = html; | ||
@@ -189,9 +189,31 @@ const actual = roundTrip(html); | ||
describe('an XHTML document', function() { | ||
const xhtml = File.readFileSync(`${__dirname}/xhtml.html`, 'utf8'); | ||
const expected = xhtml; | ||
const actual = roundTrip(xhtml); | ||
assert.equal(actual, expected); | ||
it('should produce itself', function() { | ||
const xhtml = File.readFileSync(`${__dirname}/xhtml.html`, 'utf8'); | ||
const expected = xhtml; | ||
const actual = roundTrip(xhtml); | ||
assert.equal(actual, expected); | ||
}); | ||
}); | ||
describe('XHTML mode', function() { | ||
describe('a regular element with no content', function() { | ||
it('should not use the </> shorthand', function() { | ||
const html = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html><title></title></html>'; | ||
const expected = html; | ||
const actual = roundTrip(html); | ||
assert.equal(actual, expected); | ||
}); | ||
}); | ||
describe('an empty element with no content', function() { | ||
it('should use the </> shorthand', function() { | ||
const html = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html><br /></html>'; | ||
const expected = html; | ||
const actual = roundTrip(html); | ||
assert.equal(actual, expected); | ||
}); | ||
}); | ||
}); | ||
}); | ||
@@ -39,3 +39,3 @@ 'use strict'; | ||
const per = elapsed / count; | ||
assert(per < 5, `Expected to complete an iteration in < ~5ms, got ${per}ms`); | ||
assert(per < 10, `Expected to complete an iteration in < ~10ms, got ${per}ms`); | ||
}); | ||
@@ -42,0 +42,0 @@ }); |
@@ -10,2 +10,3 @@ 'use strict'; | ||
const source = ` | ||
@charset "UTF-8"; | ||
@@ -38,24 +39,9 @@ /* Media rules for printers */ | ||
describe('without compression', function() { | ||
it('should produce same CSS minus empty lines', function() { | ||
const expected = source.replace(/\n+/gm, '\n').trim(); | ||
const actual = stringifyRules(rules, false); | ||
assert.equal(actual, expected); | ||
}); | ||
it('should squeeze spaces and comments out of the CSS', function() { | ||
const expected = `@charset "UTF-8";@media print{.footer{display:none}}h1:hover,h1:before{color:red;background:none !important}`; | ||
const actual = stringifyRules(rules, true); | ||
assert.equal(actual, expected); | ||
}); | ||
describe('with compression', function() { | ||
it('should squeeze spaces and comments out of the CSS', function() { | ||
const expected = `@media print{.footer{display:none}}h1:hover,h1:before{color:red;background:none !important}`; | ||
const actual = stringifyRules(rules, true); | ||
assert.equal(actual, expected); | ||
}); | ||
}); | ||
}); | ||
Sorry, the diff of this file is not supported yet
121196
58
178
2926