From 8f89a9c44a1d79e1eca9bd3a540f368f3af3380d Mon Sep 17 00:00:00 2001 From: Zach Green Date: Sun, 21 Aug 2016 14:28:25 -0400 Subject: [PATCH 01/16] initial flow and eslint implementation --- .babelrc | 5 +- .eslintrc | 58 ++++++ .flowconfig | 0 index.js | 197 +++--------------- package.json | 24 ++- src/atRule.js | 22 ++ src/getChildRules.js | 49 +++++ src/getCriticalDestination.js | 16 ++ src/getCriticalRules.js | 54 +++++ src/index.js | 228 ++++----------------- src/matchChild.js | 13 ++ test/fixtures/atRule.critical.expected.css | 1 + test/fixtures/atRule.css | 16 ++ test/fixtures/atRule.expected.css | 14 ++ test/index.js | 10 + 15 files changed, 348 insertions(+), 359 deletions(-) create mode 100644 .eslintrc create mode 100644 .flowconfig create mode 100644 src/atRule.js create mode 100644 src/getChildRules.js create mode 100644 src/getCriticalDestination.js create mode 100644 src/getCriticalRules.js create mode 100644 src/matchChild.js create mode 100644 test/fixtures/atRule.critical.expected.css create mode 100644 test/fixtures/atRule.css create mode 100644 test/fixtures/atRule.expected.css diff --git a/.babelrc b/.babelrc index 9d8d516..99f141e 100644 --- a/.babelrc +++ b/.babelrc @@ -1 +1,4 @@ -{ "presets": ["es2015"] } +{ + "presets": ["es2015"], + "plugins": ["transform-flow-strip-types"] +} diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..dab1c9b --- /dev/null +++ b/.eslintrc @@ -0,0 +1,58 @@ +{ + "extends": "airbnb/base", + "ecmaFeatures": { + "arrowFunctions": true, + "blockBindings": true, + "classes": true, + "defaultParams": true, + "modules": true + }, + "parser": "babel-eslint", + "plugins": [ + "flowtype" + ], + "parserOptions": { + "ecmaVersion": 6, + "ecmaFeatures": { + "jsx": true + }, + "sourceType": "module" + }, + "env": { + "es6": true, + "browser": true, + "node": true, + "commonjs": true + }, + "rules": { + "flowtype/define-flow-type": 1, + "flowtype/require-parameter-type": 1, + "flowtype/require-return-type": [ + 1, + "always", + { + "annotateUndefined": "never" + } + ], + "flowtype/space-after-type-colon": [ + 1, + "always" + ], + "flowtype/space-before-type-colon": [ + 1, + "never" + ], + "flowtype/type-id-match": [ + 1, + "^([A-Z][a-z0-9]+)+Type$" + ], + "flowtype/use-flow-type": 1, + "flowtype/valid-syntax": 1, + "import/prefer-default-export": 0 + }, + "settings": { + "flowtype": { + "onlyFilesWithFlowAnnotation": true + } + } +} diff --git a/.flowconfig b/.flowconfig new file mode 100644 index 0000000..e69de29 diff --git a/index.js b/index.js index f32d650..8c08f19 100644 --- a/index.js +++ b/index.js @@ -1,161 +1,28 @@ 'use strict'; -var chalk = require('chalk'); -var postcss = require('postcss'); -var cssnano = require('cssnano'); -var fs = require('fs'); -var path = require('path'); +var _chalk = require('chalk'); -/** - * Throw a warning if a critical selector is used more than once. - * - * @param {array} Array of critical CSS rules. - * @param {selector} Selector to check for. - * @return {string} Console warning. - */ -function selectorReuseWarning(rules, selector) { - return rules.reduce(function (init, rule) { - if (rule.selector === selector) { - console.warn('Warning: Selector ' + selector + ' is used more than once.'); - } - return rules; - }, []); -} +var _chalk2 = _interopRequireDefault(_chalk); -/** - * Identify critical CSS selectors - * - * @param {obj} PostCSS CSS object. - * @return {object} Object containing critical rules, organized by output destination - */ -function getCriticalRules(css, preserve) { - var critical = {}; - - css.walkDecls('critical-selector', function (decl) { - var dest = getDest(decl.parent); +var _postcss = require('postcss'); - if ('undefined' === typeof critical[dest]) { - critical[dest] = []; - } +var _postcss2 = _interopRequireDefault(_postcss); - switch (decl.value) { - case 'scope': - var childRules = getChildRules(css, decl.parent, preserve); +var _cssnano = require('cssnano'); - selectorReuseWarning(critical[dest], decl.parent.selector); +var _cssnano2 = _interopRequireDefault(_cssnano); - // Make sure the parent selector contains declarations - if (decl.parent.nodes.length > 1) { - critical[dest].push(decl.parent); - } - - // Push all child rules - if (childRules !== null && childRules.length) { - childRules.forEach(function (rule) { - critical[dest].push(rule); - }); - } - break; +var _fs = require('fs'); - case 'this': - selectorReuseWarning(critical[dest], decl.parent.selector); - critical[dest].push(decl.parent); - break; +var _fs2 = _interopRequireDefault(_fs); - default: - var container = decl.parent; +var _path = require('path'); - container.selector = decl.value.replace(/['"]*/g, ''); - selectorReuseWarning(critical[dest], container.selector); - critical[dest].push(container); - break; - } +var _path2 = _interopRequireDefault(_path); - decl.remove(); - }); +var _getCriticalRules = require('./getCriticalRules'); - return critical; -} - -/** - * Get rules for selectors nested within parent node - * - * @param {obj} PostCSS CSS object - * @param {object} Parent rule for which children should be included - * @param {bool} Whether or not to keep the critical rule in the stylesheet - */ -function getChildRules(css, parent, preserve) { - var ruleList = []; - var selectorRegExp = new RegExp(parent.selector); - - // Walk all rules to mach child selectors - css.walkRules(selectorRegExp, function (rule) { - var childRule = matchChild(parent, rule); - - if (childRule) { - ruleList.push(rule); - } - }); - - // Walk all at-rules to match nested child selectors - css.walkAtRules(function (atRule) { - atRule.walkRules(selectorRegExp, function (rule) { - var childRule = matchChild(parent, rule); - - // Create new at-rule to append only necessary selector to critical - var criticalAtRule = postcss.atRule({ - name: atRule.name, - params: atRule.params - }); - - // Should append even if parent selector - if (rule.selector === parent.selector || childRule) { - criticalAtRule.append(rule); - ruleList.push(criticalAtRule); - - if (!preserve) { - rule.remove(); - } - } - }); - }); - - return ruleList; -} - -/** - * Get rules for selectors nested within parent node - * - * @param {obj} PostCSS CSS object - * @return {object} Parent rule for which children should be included - */ -function matchChild(parent, rule) { - var childRegExp = new RegExp('(, )?(' + parent.selector + ' [^,\s]*),?.*'); - var childMatch = rule.selector.match(childRegExp); - - if (rule.selector !== parent.selector && childMatch !== null) { - return true; - } - - return false; -} - -/** - * Identify critical CSS destinations. - * - * @param {object} PostCSS rule. - * @return {array} string corresponding to output destination. - */ -function getDest(selector) { - var dest = 'critical.css'; - - selector.walkDecls('critical-filename', function (decl) { - dest = decl.value.replace(/['"]*/g, ''); - decl.remove(); - }); - - return dest; -} +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * Primary plugin function. @@ -170,42 +37,36 @@ function buildCritical(options) { minify: true, dryRun: false }, options); - return function (css, result) { - var criticalOutput = getCriticalRules(css, args.preserve); - - var _loop = function _loop(fileName) { - var criticalCSS = postcss.parse(''); - var critical = ''; - var rules = []; - var plugins = args.minify ? [cssnano()] : []; + return function (css) { + var criticalOutput = (0, _getCriticalRules.getCriticalRules)(css, args.preserve); + criticalOutput = Object.keys(criticalOutput).reduce(function (init, cur) { + var criticalCSS = _postcss2.default.root(); - rules = criticalOutput[fileName].reduce(function (init, rule) { + Object.keys(criticalOutput[criticalOutput[cur].fileName]).forEach(function (key) { + var rule = criticalOutput[criticalOutput[cur].fileName][key]; rule.walkDecls('critical', function (decl) { decl.remove(); }); criticalCSS.append(rule); - if (rule.type === 'rule' && !args.preserve) { rule.remove(); } + }); - return criticalOutput[fileName]; - }, {}); - - postcss(plugins).process(criticalCSS).then(function (result) { + (0, _postcss2.default)(args.minify ? [(0, _cssnano2.default)()] : []).process(criticalCSS).then(function (result) { if (!args.dryRun) { - fs.writeFileSync(path.join(args.outputPath, fileName), result); + _fs2.default.writeFileSync(_path2.default.join(args.outputPath, criticalOutput[cur].fileName), result); } else { - console.log(chalk.green('\nCritical CSS result is:\n' + chalk.yellow(result.css))); - return '\nCritical CSS result is:\n' + result.css; + console.log( // eslint-disable-line no-console + _chalk2.default.green('\nCritical CSS result is:\n' + _chalk2.default.yellow(result.css))); } }); - }; - - for (var fileName in criticalOutput) { - _loop(fileName); - } + return criticalOutput; + }, {}); }; } +// import { getCriticalFromAtRule } from './atRule'; +// import { getCriticalDestination } from './getCriticalDestination'; + -module.exports = postcss.plugin('postcss-critical', buildCritical); +module.exports = _postcss2.default.plugin('postcss-critical', buildCritical); diff --git a/package.json b/package.json index a28f251..e108452 100644 --- a/package.json +++ b/package.json @@ -9,26 +9,42 @@ }, "keywords": [ "postcss-plugin", + "postcss plugin", "postcss", "critical-css", "critical", - "css" + "css", + "critical css" ], "author": "Zach Green", "license": "MIT", "devDependencies": { "babel": "^6.5.2", "babel-cli": "^6.11.4", + "babel-eslint": "^6.1.2", + "babel-plugin-transform-flow-strip-types": "^6.8.0", "babel-preset-es2015": "^6.13.2", "chalk": "^1.1.3", "cssnano": "^3.5.2", + "eslint": "^3.3.1", + "eslint-config-airbnb": "^10.0.1", + "eslint-plugin-flowtype": "^2.7.1", + "eslint-plugin-import": "^1.13.0", + "flow-bin": "^0.30.0", "postcss": "^5.0.5", "tape": "^4.6.0" }, "scripts": { - "build": "babel src/index.js --out-file index.js", - "start": "babel src/index.js --out-file index.js --watch", + "build": "eslint src/** && npm run flow && babel src/index.js --out-file index.js", + "flow": "flow; test $? -eq 0 -o $? -eq 2", + "eslint": "eslint src/**", + "start": "eslint src/** && npm run flow && babel src/index.js --out-file index.js --watch", "pretest": "./node_modules/.bin/babel-node test/preTest.js", - "test": "npm run pretest && tape test" + "test": "npm run flow && npm run pretest && tape test" + }, + "dependencies": { + "chalk": "^1.1.3", + "cssnano": "^3.7.4", + "postcss": "^5.1.2" } } diff --git a/src/atRule.js b/src/atRule.js new file mode 100644 index 0000000..f5e4126 --- /dev/null +++ b/src/atRule.js @@ -0,0 +1,22 @@ +// @flow + +/** + * Get critical CSS from an @ rule. + * + * @param {Object} args Function args. See flow type alias. + */ +type ArgsType = {filename: string, css: Object}; +export function getCriticalFromAtRule(args: ArgsType): Object { + let result: Object = {}; + const options = Object.assign({}, + { + filename: 'critical.css', + css: {}, + }, + args + ); + options.css.walkAtRules('critical', () => { + result = options.css; + }); + return result; +} diff --git a/src/getChildRules.js b/src/getChildRules.js new file mode 100644 index 0000000..43299bc --- /dev/null +++ b/src/getChildRules.js @@ -0,0 +1,49 @@ +// @flow + +import postcss from 'postcss'; +import { matchChild } from './matchChild'; + +/** + * Get rules for selectors nested within parent node + * + * @param {Object} PostCSS CSS object + * @param {Object} Parent rule for which children should be included + * @param {boolean} Whether or not to keep the critical rule in the stylesheet + */ +export function getChildRules( + css: Object, + parent: Object, + shouldPreserve: boolean): Array { + const result = []; + const selectorRegExp: Object = new RegExp(parent.selector); + + // Walk all rules to mach child selectors + css.walkRules(selectorRegExp, (rule: Object) => { + const childRule = matchChild(parent, rule); + if (childRule) { + result.push(rule); + } + }); + + // Walk all at-rules to match nested child selectors + css.walkAtRules((atRule: Object) => { + atRule.walkRules(selectorRegExp, (rule: Object) => { + const childRule = matchChild(parent, rule); + // Create new at-rule to append only necessary selector to critical + const criticalAtRule = postcss.atRule({ + name: atRule.name, + params: atRule.params, + }); + // Should append even if parent selector + if (rule.selector === parent.selector || childRule) { + criticalAtRule.append(rule); + result.push(criticalAtRule); + if (!shouldPreserve) { + rule.remove(); + } + } + }); + }); + + return result; +} diff --git a/src/getCriticalDestination.js b/src/getCriticalDestination.js new file mode 100644 index 0000000..132f4bc --- /dev/null +++ b/src/getCriticalDestination.js @@ -0,0 +1,16 @@ +// @flow + +/** + * Identify critical CSS destinations. + * + * @param {object} rule PostCSS rule. + * @return {string} String corresponding to output destination. + */ +export function getCriticalDestination(rule: Object): string { + let dest: string = 'critical.css'; + rule.walkDecls('critical-filename', (decl: Object) => { + dest = decl.value.replace(/['"]*/g, ''); + decl.remove(); + }); + return dest; +} diff --git a/src/getCriticalRules.js b/src/getCriticalRules.js new file mode 100644 index 0000000..efc5843 --- /dev/null +++ b/src/getCriticalRules.js @@ -0,0 +1,54 @@ +// @flow + +import { getChildRules } from './getChildRules'; +import { getCriticalDestination } from './getCriticalDestination'; + +/** + * Identify critical CSS selectors + * + * @param {obj} PostCSS CSS object. + * @return {object} Object containing critical rules, organized by output destination + */ +export function getCriticalRules(css: Object, shouldPreserve: boolean): Object { + const critical = {}; + + css.walkDecls('critical-selector', (decl: Object) => { + const dest = getCriticalDestination(decl.parent); + const container = decl.parent; + const childRules = decl.value === 'scope' ? + getChildRules(css, decl.parent, shouldPreserve) : + []; + if (typeof critical[dest] === 'undefined') { + critical[dest] = []; + } + + switch (decl.value) { + case 'scope': + // Make sure the parent selector contains declarations + if (decl.parent.nodes.length > 1) { + critical[dest].push(decl.parent); + } + + // Push all child rules + if (childRules !== null && childRules.length) { + childRules.forEach((rule: Object) => { + critical[dest].push(rule); + }); + } + break; + + case 'this': + critical[dest].push(decl.parent); + break; + + default: + container.selector = decl.value.replace(/['"]*/g, ''); + critical[dest].push(container); + break; + } + + decl.remove(); + }); + + return critical; +} diff --git a/src/index.js b/src/index.js index 4c534d9..ba442a9 100644 --- a/src/index.js +++ b/src/index.js @@ -1,161 +1,13 @@ -'use strict'; +// @flow -const chalk = require('chalk'); -var postcss = require('postcss'); -var cssnano = require('cssnano'); -var fs = require('fs'); -var path = require('path'); - -/** - * Throw a warning if a critical selector is used more than once. - * - * @param {array} Array of critical CSS rules. - * @param {selector} Selector to check for. - * @return {string} Console warning. - */ -function selectorReuseWarning(rules, selector) { - return rules.reduce((init, rule) => { - if (rule.selector === selector) { - console.warn(`Warning: Selector ${selector} is used more than once.`); - } - return rules; - }, []); -} - -/** - * Identify critical CSS selectors - * - * @param {obj} PostCSS CSS object. - * @return {object} Object containing critical rules, organized by output destination - */ -function getCriticalRules(css, preserve) { - let critical = {}; - - css.walkDecls('critical-selector', decl => { - let dest = getDest(decl.parent); - - if ('undefined' === typeof critical[dest]) { - critical[dest] = []; - } - - switch (decl.value) { - case 'scope': - let childRules = getChildRules(css, decl.parent, preserve); - - selectorReuseWarning(critical[dest], decl.parent.selector); - - // Make sure the parent selector contains declarations - if (decl.parent.nodes.length > 1) { - critical[dest].push(decl.parent); - } - - // Push all child rules - if (childRules !== null && childRules.length) { - childRules.forEach(rule => { - critical[dest].push(rule); - }); - } - break; - - case 'this': - selectorReuseWarning(critical[dest], decl.parent.selector); - critical[dest].push(decl.parent); - break; - - default: - const container = decl.parent; - - container.selector = decl.value.replace(/['"]*/g, ''); - selectorReuseWarning(critical[dest], container.selector); - critical[dest].push(container); - break; - } - - decl.remove(); - }); - - return critical; -} - -/** - * Get rules for selectors nested within parent node - * - * @param {obj} PostCSS CSS object - * @param {object} Parent rule for which children should be included - * @param {bool} Whether or not to keep the critical rule in the stylesheet - */ -function getChildRules(css, parent, preserve) { - let ruleList = []; - let selectorRegExp = new RegExp(parent.selector); - - // Walk all rules to mach child selectors - css.walkRules(selectorRegExp, rule => { - let childRule = matchChild(parent, rule); - - if (childRule) { - ruleList.push(rule); - } - }); - - // Walk all at-rules to match nested child selectors - css.walkAtRules(atRule => { - atRule.walkRules(selectorRegExp, rule => { - let childRule = matchChild(parent, rule); - - // Create new at-rule to append only necessary selector to critical - let criticalAtRule = postcss.atRule({ - name: atRule.name, - params: atRule.params - }); - - // Should append even if parent selector - if (rule.selector === parent.selector || childRule) { - criticalAtRule.append(rule); - ruleList.push(criticalAtRule); - - if (!preserve) { - rule.remove(); - } - } - }); - }); - - return ruleList; -} - -/** - * Get rules for selectors nested within parent node - * - * @param {obj} PostCSS CSS object - * @return {object} Parent rule for which children should be included - */ -function matchChild(parent, rule) { - let childRegExp = new RegExp('(, )?(' + parent.selector + ' [^,\s]*),?.*'); - let childMatch = rule.selector.match(childRegExp); - - if (rule.selector !== parent.selector && childMatch !== null) { - return true; - } - - return false; -} - -/** - * Identify critical CSS destinations. - * - * @param {object} PostCSS rule. - * @return {array} string corresponding to output destination. - */ -function getDest(selector) { - let dest = 'critical.css'; - - selector.walkDecls('critical-filename', decl => { - dest = decl.value.replace(/['"]*/g, ''); - decl.remove(); - }); - - return dest; -} +import chalk from 'chalk'; +import postcss from 'postcss'; +import cssnano from 'cssnano'; +import fs from 'fs'; +import path from 'path'; +// import { getCriticalFromAtRule } from './atRule'; +// import { getCriticalDestination } from './getCriticalDestination'; +import { getCriticalRules } from './getCriticalRules'; /** * Primary plugin function. @@ -163,54 +15,58 @@ function getDest(selector) { * @param {object} array of options. * @return {function} function for PostCSS plugin. */ -function buildCritical(options) { +type ArgsType = { + outputPath: string, + preserve: boolean, + minify: boolean, + dryRun: boolean +}; + +function buildCritical(options: ArgsType): Function { const args = Object.assign({}, - { - outputPath: process.cwd(), - preserve: true, - minify: true, - dryRun: false - }, options); - return (css, result) => { + { + outputPath: process.cwd(), + preserve: true, + minify: true, + dryRun: false, + }, + options + ); + return (css: Object) => { let criticalOutput = getCriticalRules(css, args.preserve); + criticalOutput = Object.keys(criticalOutput).reduce((init: Object, cur: string): Object => { + const criticalCSS = postcss.root(); - for (let fileName in criticalOutput) { - let criticalCSS = postcss.parse(''); - let critical = ''; - let rules = []; - let plugins = args.minify ? [cssnano()] : []; - - rules = criticalOutput[fileName].reduce((init, rule) => { - rule.walkDecls('critical', (decl) => { + Object.keys(criticalOutput[criticalOutput[cur].fileName]).forEach((key: string) => { + const rule = criticalOutput[criticalOutput[cur].fileName][key]; + rule.walkDecls('critical', (decl: Object) => { decl.remove(); }); criticalCSS.append(rule); - if (rule.type === 'rule' && !args.preserve) { rule.remove(); } + }); - return criticalOutput[fileName]; - }, {}); - - postcss(plugins) + postcss(args.minify ? [cssnano()] : []) .process(criticalCSS) - .then(result => { + .then((result: Object) => { if (!args.dryRun) { - fs.writeFileSync(path.join(args.outputPath, fileName), result); + fs.writeFileSync( + path.join(args.outputPath, criticalOutput[cur].fileName), + result + ); } else { - console.log( + console.log( // eslint-disable-line no-console chalk.green(` Critical CSS result is: ${chalk.yellow(result.css)}`) ); - return ` -Critical CSS result is: -${result.css}`; } }); - } - } + return criticalOutput; + }, {}); + }; } module.exports = postcss.plugin('postcss-critical', buildCritical); diff --git a/src/matchChild.js b/src/matchChild.js new file mode 100644 index 0000000..25defba --- /dev/null +++ b/src/matchChild.js @@ -0,0 +1,13 @@ +// @flow + +/** + * Get rules for selectors nested within parent node + * + * @param {obj} PostCSS CSS object + * @return {object} Parent rule for which children should be included + */ +export function matchChild(parent: Object, rule: Object): boolean { + const childRegExp = new RegExp(`(, )?(${parent.selector} [^,\s]*),?.*`); + return rule.selector !== parent.selector && + rule.selector.match(childRegExp) !== null; +} diff --git a/test/fixtures/atRule.critical.expected.css b/test/fixtures/atRule.critical.expected.css new file mode 100644 index 0000000..2a69746 --- /dev/null +++ b/test/fixtures/atRule.critical.expected.css @@ -0,0 +1 @@ +.foo{display:flex;width:calc(100% - 200px)}.bar{border:10px solid gold;height:100%}.baz:before{content:'test';position:fixed} diff --git a/test/fixtures/atRule.css b/test/fixtures/atRule.css new file mode 100644 index 0000000..d181a33 --- /dev/null +++ b/test/fixtures/atRule.css @@ -0,0 +1,16 @@ +@critical; + +.foo { + display: flex; + width: calc(100% - 200px); +} + +.bar { + border: 10px solid gold; + height: 100%; +} + +.baz::before { + content: 'test'; + position: fixed; +} diff --git a/test/fixtures/atRule.expected.css b/test/fixtures/atRule.expected.css new file mode 100644 index 0000000..3e8e855 --- /dev/null +++ b/test/fixtures/atRule.expected.css @@ -0,0 +1,14 @@ +.foo { + display: flex; + width: calc(100% - 200px); +} + +.bar { + border: 10px solid gold; + height: 100%; +} + +.baz::before { + content: 'test'; + position: fixed; +} diff --git a/test/index.js b/test/index.js index 6ef6da4..f676daf 100644 --- a/test/index.js +++ b/test/index.js @@ -52,3 +52,13 @@ test('Testing "this" non-critical result', function(t) { compareFixtures(t, 'this'); t.end(); }); + +test('Testing "this" critical result', function(t) { + compareCritical(t, 'atRule'); + t.end(); +}); + +test('Testing "this" non-critical result', function(t) { + compareFixtures(t, 'atRule'); + t.end(); +}); From a598c6e6abe0b4000d879671929143629b719906 Mon Sep 17 00:00:00 2001 From: Zach Green Date: Sun, 11 Dec 2016 10:53:08 -0500 Subject: [PATCH 02/16] use standard for eslint --- .eslintrc | 24 ++----------- package.json | 3 ++ src/atRule.js | 23 ++++++------- src/getChildRules.js | 34 +++++++++--------- src/getCriticalDestination.js | 12 +++---- src/getCriticalRules.js | 44 ++++++++++++------------ src/index.js | 65 +++++++++++++++++------------------ src/matchChild.js | 6 ++-- 8 files changed, 94 insertions(+), 117 deletions(-) diff --git a/.eslintrc b/.eslintrc index dab1c9b..c8ae6ec 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,29 +1,9 @@ { - "extends": "airbnb/base", - "ecmaFeatures": { - "arrowFunctions": true, - "blockBindings": true, - "classes": true, - "defaultParams": true, - "modules": true - }, - "parser": "babel-eslint", + "extends": "standard", "plugins": [ "flowtype" ], - "parserOptions": { - "ecmaVersion": 6, - "ecmaFeatures": { - "jsx": true - }, - "sourceType": "module" - }, - "env": { - "es6": true, - "browser": true, - "node": true, - "commonjs": true - }, + "parser": "babel-eslint", "rules": { "flowtype/define-flow-type": 1, "flowtype/require-parameter-type": 1, diff --git a/package.json b/package.json index e108452..5555971 100644 --- a/package.json +++ b/package.json @@ -28,8 +28,11 @@ "cssnano": "^3.5.2", "eslint": "^3.3.1", "eslint-config-airbnb": "^10.0.1", + "eslint-config-standard": "^6.2.1", "eslint-plugin-flowtype": "^2.7.1", "eslint-plugin-import": "^1.13.0", + "eslint-plugin-promise": "^3.4.0", + "eslint-plugin-standard": "^2.0.1", "flow-bin": "^0.30.0", "postcss": "^5.0.5", "tape": "^4.6.0" diff --git a/src/atRule.js b/src/atRule.js index f5e4126..5f24c23 100644 --- a/src/atRule.js +++ b/src/atRule.js @@ -5,18 +5,15 @@ * * @param {Object} args Function args. See flow type alias. */ -type ArgsType = {filename: string, css: Object}; -export function getCriticalFromAtRule(args: ArgsType): Object { - let result: Object = {}; - const options = Object.assign({}, - { - filename: 'critical.css', - css: {}, - }, - args - ); +type ArgsType = {filename: string, css: Object} +export function getCriticalFromAtRule (args: ArgsType): Object { + let result: Object = {} + const options = Object.assign({}, { + filename: 'critical.css', + css: {} + }, args) options.css.walkAtRules('critical', () => { - result = options.css; - }); - return result; + result = options.css + }) + return result } diff --git a/src/getChildRules.js b/src/getChildRules.js index 43299bc..71f199d 100644 --- a/src/getChildRules.js +++ b/src/getChildRules.js @@ -1,7 +1,7 @@ // @flow -import postcss from 'postcss'; -import { matchChild } from './matchChild'; +import postcss from 'postcss' +import { matchChild } from './matchChild' /** * Get rules for selectors nested within parent node @@ -10,40 +10,40 @@ import { matchChild } from './matchChild'; * @param {Object} Parent rule for which children should be included * @param {boolean} Whether or not to keep the critical rule in the stylesheet */ -export function getChildRules( +export function getChildRules ( css: Object, parent: Object, shouldPreserve: boolean): Array { - const result = []; - const selectorRegExp: Object = new RegExp(parent.selector); + const result = [] + const selectorRegExp: Object = new RegExp(parent.selector) // Walk all rules to mach child selectors css.walkRules(selectorRegExp, (rule: Object) => { - const childRule = matchChild(parent, rule); + const childRule = matchChild(parent, rule) if (childRule) { - result.push(rule); + result.push(rule) } - }); + }) // Walk all at-rules to match nested child selectors css.walkAtRules((atRule: Object) => { atRule.walkRules(selectorRegExp, (rule: Object) => { - const childRule = matchChild(parent, rule); + const childRule = matchChild(parent, rule) // Create new at-rule to append only necessary selector to critical const criticalAtRule = postcss.atRule({ name: atRule.name, - params: atRule.params, - }); + params: atRule.params + }) // Should append even if parent selector if (rule.selector === parent.selector || childRule) { - criticalAtRule.append(rule); - result.push(criticalAtRule); + criticalAtRule.append(rule) + result.push(criticalAtRule) if (!shouldPreserve) { - rule.remove(); + rule.remove() } } - }); - }); + }) + }) - return result; + return result } diff --git a/src/getCriticalDestination.js b/src/getCriticalDestination.js index 132f4bc..2c873ce 100644 --- a/src/getCriticalDestination.js +++ b/src/getCriticalDestination.js @@ -6,11 +6,11 @@ * @param {object} rule PostCSS rule. * @return {string} String corresponding to output destination. */ -export function getCriticalDestination(rule: Object): string { - let dest: string = 'critical.css'; +export function getCriticalDestination (rule: Object): string { + let dest: string = 'critical.css' rule.walkDecls('critical-filename', (decl: Object) => { - dest = decl.value.replace(/['"]*/g, ''); - decl.remove(); - }); - return dest; + dest = decl.value.replace(/['"]*/g, '') + decl.remove() + }) + return dest } diff --git a/src/getCriticalRules.js b/src/getCriticalRules.js index efc5843..78dcc16 100644 --- a/src/getCriticalRules.js +++ b/src/getCriticalRules.js @@ -1,7 +1,7 @@ // @flow -import { getChildRules } from './getChildRules'; -import { getCriticalDestination } from './getCriticalDestination'; +import { getChildRules } from './getChildRules' +import { getCriticalDestination } from './getCriticalDestination' /** * Identify critical CSS selectors @@ -9,46 +9,46 @@ import { getCriticalDestination } from './getCriticalDestination'; * @param {obj} PostCSS CSS object. * @return {object} Object containing critical rules, organized by output destination */ -export function getCriticalRules(css: Object, shouldPreserve: boolean): Object { - const critical = {}; +export function getCriticalRules (css: Object, shouldPreserve: boolean): Object { + const critical = {} css.walkDecls('critical-selector', (decl: Object) => { - const dest = getCriticalDestination(decl.parent); - const container = decl.parent; - const childRules = decl.value === 'scope' ? - getChildRules(css, decl.parent, shouldPreserve) : - []; + const dest = getCriticalDestination(decl.parent) + const container = decl.parent + const childRules = decl.value === 'scope' + ? getChildRules(css, decl.parent, shouldPreserve) + : [] if (typeof critical[dest] === 'undefined') { - critical[dest] = []; + critical[dest] = [] } switch (decl.value) { case 'scope': // Make sure the parent selector contains declarations if (decl.parent.nodes.length > 1) { - critical[dest].push(decl.parent); + critical[dest].push(decl.parent) } // Push all child rules if (childRules !== null && childRules.length) { childRules.forEach((rule: Object) => { - critical[dest].push(rule); - }); + critical[dest].push(rule) + }) } - break; + break case 'this': - critical[dest].push(decl.parent); - break; + critical[dest].push(decl.parent) + break default: - container.selector = decl.value.replace(/['"]*/g, ''); - critical[dest].push(container); - break; + container.selector = decl.value.replace(/['"]*/g, '') + critical[dest].push(container) + break } - decl.remove(); - }); + decl.remove() + }) - return critical; + return critical } diff --git a/src/index.js b/src/index.js index ba442a9..da0b6b7 100644 --- a/src/index.js +++ b/src/index.js @@ -1,13 +1,13 @@ // @flow -import chalk from 'chalk'; -import postcss from 'postcss'; -import cssnano from 'cssnano'; -import fs from 'fs'; -import path from 'path'; -// import { getCriticalFromAtRule } from './atRule'; -// import { getCriticalDestination } from './getCriticalDestination'; -import { getCriticalRules } from './getCriticalRules'; +import chalk from 'chalk' +import postcss from 'postcss' +import cssnano from 'cssnano' +import fs from 'fs' +import path from 'path' +// import { getCriticalFromAtRule } from './atRule' +// import { getCriticalDestination } from './getCriticalDestination' +import { getCriticalRules } from './getCriticalRules' /** * Primary plugin function. @@ -20,33 +20,30 @@ type ArgsType = { preserve: boolean, minify: boolean, dryRun: boolean -}; +} -function buildCritical(options: ArgsType): Function { - const args = Object.assign({}, - { - outputPath: process.cwd(), - preserve: true, - minify: true, - dryRun: false, - }, - options - ); +function buildCritical (options: ArgsType): Function { + const args = Object.assign({}, { + outputPath: process.cwd(), + preserve: true, + minify: true, + dryRun: false + }, options) return (css: Object) => { - let criticalOutput = getCriticalRules(css, args.preserve); + let criticalOutput = getCriticalRules(css, args.preserve) criticalOutput = Object.keys(criticalOutput).reduce((init: Object, cur: string): Object => { - const criticalCSS = postcss.root(); + const criticalCSS = postcss.root() Object.keys(criticalOutput[criticalOutput[cur].fileName]).forEach((key: string) => { - const rule = criticalOutput[criticalOutput[cur].fileName][key]; + const rule = criticalOutput[criticalOutput[cur].fileName][key] rule.walkDecls('critical', (decl: Object) => { - decl.remove(); - }); - criticalCSS.append(rule); + decl.remove() + }) + criticalCSS.append(rule) if (rule.type === 'rule' && !args.preserve) { - rule.remove(); + rule.remove() } - }); + }) postcss(args.minify ? [cssnano()] : []) .process(criticalCSS) @@ -55,18 +52,18 @@ function buildCritical(options: ArgsType): Function { fs.writeFileSync( path.join(args.outputPath, criticalOutput[cur].fileName), result - ); + ) } else { console.log( // eslint-disable-line no-console chalk.green(` Critical CSS result is: ${chalk.yellow(result.css)}`) - ); + ) } - }); - return criticalOutput; - }, {}); - }; + }) + return criticalOutput + }, {}) + } } -module.exports = postcss.plugin('postcss-critical', buildCritical); +module.exports = postcss.plugin('postcss-critical', buildCritical) diff --git a/src/matchChild.js b/src/matchChild.js index 25defba..1f8ae08 100644 --- a/src/matchChild.js +++ b/src/matchChild.js @@ -6,8 +6,8 @@ * @param {obj} PostCSS CSS object * @return {object} Parent rule for which children should be included */ -export function matchChild(parent: Object, rule: Object): boolean { - const childRegExp = new RegExp(`(, )?(${parent.selector} [^,\s]*),?.*`); +export function matchChild (parent: Object, rule: Object): boolean { + const childRegExp = new RegExp(`(, )?(${parent.selector} [^,\s]*),?.*`) // eslint-disable-line no-useless-escape return rule.selector !== parent.selector && - rule.selector.match(childRegExp) !== null; + rule.selector.match(childRegExp) !== null } From 67bf35c53393bec487394a9d5da6d3d08393da35 Mon Sep 17 00:00:00 2001 From: Zach Green Date: Sun, 11 Dec 2016 11:27:42 -0500 Subject: [PATCH 03/16] save progress on lib compile --- index.js | 73 +---------------------------------- lib/atRule.js | 23 +++++++++++ lib/getChildRules.js | 56 +++++++++++++++++++++++++++ lib/getCriticalDestination.js | 22 +++++++++++ lib/getCriticalRules.js | 58 ++++++++++++++++++++++++++++ lib/index.js | 73 +++++++++++++++++++++++++++++++++++ lib/matchChild.js | 18 +++++++++ package.json | 2 +- src/index.js | 1 + 9 files changed, 253 insertions(+), 73 deletions(-) create mode 100644 lib/atRule.js create mode 100644 lib/getChildRules.js create mode 100644 lib/getCriticalDestination.js create mode 100644 lib/getCriticalRules.js create mode 100644 lib/index.js create mode 100644 lib/matchChild.js diff --git a/index.js b/index.js index 8c08f19..bb0a047 100644 --- a/index.js +++ b/index.js @@ -1,72 +1 @@ -'use strict'; - -var _chalk = require('chalk'); - -var _chalk2 = _interopRequireDefault(_chalk); - -var _postcss = require('postcss'); - -var _postcss2 = _interopRequireDefault(_postcss); - -var _cssnano = require('cssnano'); - -var _cssnano2 = _interopRequireDefault(_cssnano); - -var _fs = require('fs'); - -var _fs2 = _interopRequireDefault(_fs); - -var _path = require('path'); - -var _path2 = _interopRequireDefault(_path); - -var _getCriticalRules = require('./getCriticalRules'); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -/** - * Primary plugin function. - * - * @param {object} array of options. - * @return {function} function for PostCSS plugin. - */ -function buildCritical(options) { - var args = Object.assign({}, { - outputPath: process.cwd(), - preserve: true, - minify: true, - dryRun: false - }, options); - return function (css) { - var criticalOutput = (0, _getCriticalRules.getCriticalRules)(css, args.preserve); - criticalOutput = Object.keys(criticalOutput).reduce(function (init, cur) { - var criticalCSS = _postcss2.default.root(); - - Object.keys(criticalOutput[criticalOutput[cur].fileName]).forEach(function (key) { - var rule = criticalOutput[criticalOutput[cur].fileName][key]; - rule.walkDecls('critical', function (decl) { - decl.remove(); - }); - criticalCSS.append(rule); - if (rule.type === 'rule' && !args.preserve) { - rule.remove(); - } - }); - - (0, _postcss2.default)(args.minify ? [(0, _cssnano2.default)()] : []).process(criticalCSS).then(function (result) { - if (!args.dryRun) { - _fs2.default.writeFileSync(_path2.default.join(args.outputPath, criticalOutput[cur].fileName), result); - } else { - console.log( // eslint-disable-line no-console - _chalk2.default.green('\nCritical CSS result is:\n' + _chalk2.default.yellow(result.css))); - } - }); - return criticalOutput; - }, {}); - }; -} -// import { getCriticalFromAtRule } from './atRule'; -// import { getCriticalDestination } from './getCriticalDestination'; - - -module.exports = _postcss2.default.plugin('postcss-critical', buildCritical); +module.exports = require('./lib'); diff --git a/lib/atRule.js b/lib/atRule.js new file mode 100644 index 0000000..220a0b3 --- /dev/null +++ b/lib/atRule.js @@ -0,0 +1,23 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getCriticalFromAtRule = getCriticalFromAtRule; +function getCriticalFromAtRule(args) { + var result = {}; + var options = Object.assign({}, { + filename: 'critical.css', + css: {} + }, args); + options.css.walkAtRules('critical', function () { + result = options.css; + }); + return result; +} + +/** + * Get critical CSS from an @ rule. + * + * @param {Object} args Function args. See flow type alias. + */ \ No newline at end of file diff --git a/lib/getChildRules.js b/lib/getChildRules.js new file mode 100644 index 0000000..6ef997a --- /dev/null +++ b/lib/getChildRules.js @@ -0,0 +1,56 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getChildRules = getChildRules; + +var _postcss = require('postcss'); + +var _postcss2 = _interopRequireDefault(_postcss); + +var _matchChild = require('./matchChild'); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Get rules for selectors nested within parent node + * + * @param {Object} PostCSS CSS object + * @param {Object} Parent rule for which children should be included + * @param {boolean} Whether or not to keep the critical rule in the stylesheet + */ +function getChildRules(css, parent, shouldPreserve) { + var result = []; + var selectorRegExp = new RegExp(parent.selector); + + // Walk all rules to mach child selectors + css.walkRules(selectorRegExp, function (rule) { + var childRule = (0, _matchChild.matchChild)(parent, rule); + if (childRule) { + result.push(rule); + } + }); + + // Walk all at-rules to match nested child selectors + css.walkAtRules(function (atRule) { + atRule.walkRules(selectorRegExp, function (rule) { + var childRule = (0, _matchChild.matchChild)(parent, rule); + // Create new at-rule to append only necessary selector to critical + var criticalAtRule = _postcss2.default.atRule({ + name: atRule.name, + params: atRule.params + }); + // Should append even if parent selector + if (rule.selector === parent.selector || childRule) { + criticalAtRule.append(rule); + result.push(criticalAtRule); + if (!shouldPreserve) { + rule.remove(); + } + } + }); + }); + + return result; +} \ No newline at end of file diff --git a/lib/getCriticalDestination.js b/lib/getCriticalDestination.js new file mode 100644 index 0000000..01159c6 --- /dev/null +++ b/lib/getCriticalDestination.js @@ -0,0 +1,22 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getCriticalDestination = getCriticalDestination; + + +/** + * Identify critical CSS destinations. + * + * @param {object} rule PostCSS rule. + * @return {string} String corresponding to output destination. + */ +function getCriticalDestination(rule) { + var dest = 'critical.css'; + rule.walkDecls('critical-filename', function (decl) { + dest = decl.value.replace(/['"]*/g, ''); + decl.remove(); + }); + return dest; +} \ No newline at end of file diff --git a/lib/getCriticalRules.js b/lib/getCriticalRules.js new file mode 100644 index 0000000..0bdbcfe --- /dev/null +++ b/lib/getCriticalRules.js @@ -0,0 +1,58 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getCriticalRules = getCriticalRules; + +var _getChildRules = require('./getChildRules'); + +var _getCriticalDestination = require('./getCriticalDestination'); + +/** + * Identify critical CSS selectors + * + * @param {obj} PostCSS CSS object. + * @return {object} Object containing critical rules, organized by output destination + */ +function getCriticalRules(css, shouldPreserve) { + var critical = {}; + + css.walkDecls('critical-selector', function (decl) { + var dest = (0, _getCriticalDestination.getCriticalDestination)(decl.parent); + var container = decl.parent; + var childRules = decl.value === 'scope' ? (0, _getChildRules.getChildRules)(css, decl.parent, shouldPreserve) : []; + if (typeof critical[dest] === 'undefined') { + critical[dest] = []; + } + + switch (decl.value) { + case 'scope': + // Make sure the parent selector contains declarations + if (decl.parent.nodes.length > 1) { + critical[dest].push(decl.parent); + } + + // Push all child rules + if (childRules !== null && childRules.length) { + childRules.forEach(function (rule) { + critical[dest].push(rule); + }); + } + break; + + case 'this': + critical[dest].push(decl.parent); + break; + + default: + container.selector = decl.value.replace(/['"]*/g, ''); + critical[dest].push(container); + break; + } + + decl.remove(); + }); + + return critical; +} \ No newline at end of file diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..e4a2e39 --- /dev/null +++ b/lib/index.js @@ -0,0 +1,73 @@ +'use strict'; + +var _chalk = require('chalk'); + +var _chalk2 = _interopRequireDefault(_chalk); + +var _postcss = require('postcss'); + +var _postcss2 = _interopRequireDefault(_postcss); + +var _cssnano = require('cssnano'); + +var _cssnano2 = _interopRequireDefault(_cssnano); + +var _fs = require('fs'); + +var _fs2 = _interopRequireDefault(_fs); + +var _path = require('path'); + +var _path2 = _interopRequireDefault(_path); + +var _getCriticalRules = require('./getCriticalRules'); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Primary plugin function. + * + * @param {object} array of options. + * @return {function} function for PostCSS plugin. + */ +function buildCritical(options) { + var args = Object.assign({}, { + outputPath: process.cwd(), + preserve: true, + minify: true, + dryRun: false + }, options); + console.log(args); + return function (css) { + var criticalOutput = (0, _getCriticalRules.getCriticalRules)(css, args.preserve); + criticalOutput = Object.keys(criticalOutput).reduce(function (init, cur) { + var criticalCSS = _postcss2.default.root(); + + Object.keys(criticalOutput[criticalOutput[cur].fileName]).forEach(function (key) { + var rule = criticalOutput[criticalOutput[cur].fileName][key]; + rule.walkDecls('critical', function (decl) { + decl.remove(); + }); + criticalCSS.append(rule); + if (rule.type === 'rule' && !args.preserve) { + rule.remove(); + } + }); + + (0, _postcss2.default)(args.minify ? [(0, _cssnano2.default)()] : []).process(criticalCSS).then(function (result) { + if (!args.dryRun) { + _fs2.default.writeFileSync(_path2.default.join(args.outputPath, criticalOutput[cur].fileName), result); + } else { + console.log( // eslint-disable-line no-console + _chalk2.default.green('\nCritical CSS result is:\n' + _chalk2.default.yellow(result.css))); + } + }); + return criticalOutput; + }, {}); + }; +} +// import { getCriticalFromAtRule } from './atRule' +// import { getCriticalDestination } from './getCriticalDestination' + + +module.exports = _postcss2.default.plugin('postcss-critical', buildCritical); \ No newline at end of file diff --git a/lib/matchChild.js b/lib/matchChild.js new file mode 100644 index 0000000..b14796d --- /dev/null +++ b/lib/matchChild.js @@ -0,0 +1,18 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.matchChild = matchChild; + + +/** + * Get rules for selectors nested within parent node + * + * @param {obj} PostCSS CSS object + * @return {object} Parent rule for which children should be included + */ +function matchChild(parent, rule) { + var childRegExp = new RegExp("(, )?(" + parent.selector + " [^,s]*),?.*"); // eslint-disable-line no-useless-escape + return rule.selector !== parent.selector && rule.selector.match(childRegExp) !== null; +} \ No newline at end of file diff --git a/package.json b/package.json index 5555971..d1fdc38 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "tape": "^4.6.0" }, "scripts": { - "build": "eslint src/** && npm run flow && babel src/index.js --out-file index.js", + "build": "eslint src/** && npm run flow && babel src --out-dir lib", "flow": "flow; test $? -eq 0 -o $? -eq 2", "eslint": "eslint src/**", "start": "eslint src/** && npm run flow && babel src/index.js --out-file index.js --watch", diff --git a/src/index.js b/src/index.js index da0b6b7..125a34e 100644 --- a/src/index.js +++ b/src/index.js @@ -29,6 +29,7 @@ function buildCritical (options: ArgsType): Function { minify: true, dryRun: false }, options) + console.log(args) return (css: Object) => { let criticalOutput = getCriticalRules(css, args.preserve) criticalOutput = Object.keys(criticalOutput).reduce((init: Object, cur: string): Object => { From dad39d886685b74fd2dde9a42674eb39c0866afd Mon Sep 17 00:00:00 2001 From: Zach Green Date: Sun, 11 Dec 2016 13:52:55 -0500 Subject: [PATCH 04/16] initial tests passing after refactor --- .babelrc | 2 +- lib/getCriticalRules.js | 3 +- lib/index.js | 38 ++++++------ package.json | 1 + src/getCriticalRules.js | 3 +- src/index.js | 39 ++++++------ ...ted.css => this.non-critical.expected.css} | 0 test/index.js | 60 ++++++------------- test/preTest.js | 30 +++++++--- 9 files changed, 85 insertions(+), 91 deletions(-) rename test/fixtures/{this.expected.css => this.non-critical.expected.css} (100%) diff --git a/.babelrc b/.babelrc index 99f141e..7592ae4 100644 --- a/.babelrc +++ b/.babelrc @@ -1,4 +1,4 @@ { "presets": ["es2015"], - "plugins": ["transform-flow-strip-types"] + "plugins": ["transform-flow-strip-types", "transform-object-rest-spread"] } diff --git a/lib/getCriticalRules.js b/lib/getCriticalRules.js index 0bdbcfe..6b3214f 100644 --- a/lib/getCriticalRules.js +++ b/lib/getCriticalRules.js @@ -12,7 +12,7 @@ var _getCriticalDestination = require('./getCriticalDestination'); /** * Identify critical CSS selectors * - * @param {obj} PostCSS CSS object. + * @param {object} PostCSS CSS object. * @return {object} Object containing critical rules, organized by output destination */ function getCriticalRules(css, shouldPreserve) { @@ -53,6 +53,5 @@ function getCriticalRules(css, shouldPreserve) { decl.remove(); }); - return critical; } \ No newline at end of file diff --git a/lib/index.js b/lib/index.js index e4a2e39..0686214 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,5 +1,10 @@ 'use strict'; +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; +// import { getCriticalFromAtRule } from './atRule' +// import { getCriticalDestination } from './getCriticalDestination' + + var _chalk = require('chalk'); var _chalk2 = _interopRequireDefault(_chalk); @@ -31,32 +36,32 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de * @return {function} function for PostCSS plugin. */ function buildCritical(options) { - var args = Object.assign({}, { + var args = _extends({ outputPath: process.cwd(), preserve: true, minify: true, dryRun: false }, options); - console.log(args); return function (css) { var criticalOutput = (0, _getCriticalRules.getCriticalRules)(css, args.preserve); - criticalOutput = Object.keys(criticalOutput).reduce(function (init, cur) { + return Object.keys(criticalOutput).reduce(function (init, cur) { var criticalCSS = _postcss2.default.root(); - - Object.keys(criticalOutput[criticalOutput[cur].fileName]).forEach(function (key) { - var rule = criticalOutput[criticalOutput[cur].fileName][key]; - rule.walkDecls('critical', function (decl) { - decl.remove(); - }); - criticalCSS.append(rule); - if (rule.type === 'rule' && !args.preserve) { - rule.remove(); - } - }); + criticalCSS.append(criticalOutput[cur]); + // criticalOutput[cur].forEach((rule) => { + // + // rule.walkDecls('critical', (decl: Object) => { + // console.log('walking critical') + // decl.remove() + // criticalCSS.append(rule) + // if (rule.type === 'rule' && !args.preserve) { + // rule.remove() + // } + // }) + // }) (0, _postcss2.default)(args.minify ? [(0, _cssnano2.default)()] : []).process(criticalCSS).then(function (result) { if (!args.dryRun) { - _fs2.default.writeFileSync(_path2.default.join(args.outputPath, criticalOutput[cur].fileName), result); + _fs2.default.writeFile(_path2.default.join(args.outputPath, cur), result.css); } else { console.log( // eslint-disable-line no-console _chalk2.default.green('\nCritical CSS result is:\n' + _chalk2.default.yellow(result.css))); @@ -66,8 +71,5 @@ function buildCritical(options) { }, {}); }; } -// import { getCriticalFromAtRule } from './atRule' -// import { getCriticalDestination } from './getCriticalDestination' - module.exports = _postcss2.default.plugin('postcss-critical', buildCritical); \ No newline at end of file diff --git a/package.json b/package.json index d1fdc38..d4e8508 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "babel-cli": "^6.11.4", "babel-eslint": "^6.1.2", "babel-plugin-transform-flow-strip-types": "^6.8.0", + "babel-plugin-transform-object-rest-spread": "^6.20.2", "babel-preset-es2015": "^6.13.2", "chalk": "^1.1.3", "cssnano": "^3.5.2", diff --git a/src/getCriticalRules.js b/src/getCriticalRules.js index 78dcc16..e5de773 100644 --- a/src/getCriticalRules.js +++ b/src/getCriticalRules.js @@ -6,7 +6,7 @@ import { getCriticalDestination } from './getCriticalDestination' /** * Identify critical CSS selectors * - * @param {obj} PostCSS CSS object. + * @param {object} PostCSS CSS object. * @return {object} Object containing critical rules, organized by output destination */ export function getCriticalRules (css: Object, shouldPreserve: boolean): Object { @@ -49,6 +49,5 @@ export function getCriticalRules (css: Object, shouldPreserve: boolean): Object decl.remove() }) - return critical } diff --git a/src/index.js b/src/index.js index 125a34e..fa5669c 100644 --- a/src/index.js +++ b/src/index.js @@ -23,36 +23,37 @@ type ArgsType = { } function buildCritical (options: ArgsType): Function { - const args = Object.assign({}, { + const args = { outputPath: process.cwd(), preserve: true, minify: true, - dryRun: false - }, options) - console.log(args) + dryRun: false, + ...options + } return (css: Object) => { let criticalOutput = getCriticalRules(css, args.preserve) - criticalOutput = Object.keys(criticalOutput).reduce((init: Object, cur: string): Object => { + return Object.keys(criticalOutput).reduce((init: Object, cur: string): Object => { const criticalCSS = postcss.root() - - Object.keys(criticalOutput[criticalOutput[cur].fileName]).forEach((key: string) => { - const rule = criticalOutput[criticalOutput[cur].fileName][key] - rule.walkDecls('critical', (decl: Object) => { - decl.remove() - }) - criticalCSS.append(rule) - if (rule.type === 'rule' && !args.preserve) { - rule.remove() - } - }) + criticalCSS.append(criticalOutput[cur]) + // criticalOutput[cur].forEach((rule) => { + // + // rule.walkDecls('critical', (decl: Object) => { + // console.log('walking critical') + // decl.remove() + // criticalCSS.append(rule) + // if (rule.type === 'rule' && !args.preserve) { + // rule.remove() + // } + // }) + // }) postcss(args.minify ? [cssnano()] : []) .process(criticalCSS) .then((result: Object) => { if (!args.dryRun) { - fs.writeFileSync( - path.join(args.outputPath, criticalOutput[cur].fileName), - result + fs.writeFile( + path.join(args.outputPath, cur), + result.css ) } else { console.log( // eslint-disable-line no-console diff --git a/test/fixtures/this.expected.css b/test/fixtures/this.non-critical.expected.css similarity index 100% rename from test/fixtures/this.expected.css rename to test/fixtures/this.non-critical.expected.css diff --git a/test/index.js b/test/index.js index f676daf..02422f6 100644 --- a/test/index.js +++ b/test/index.js @@ -7,38 +7,14 @@ var postcssCriticalCSS = require('..'); const basePath = `${process.cwd()}/test/fixtures`; const chalk = require('chalk'); -function fixturePath(name) { - return 'test/fixtures/' + name + '.css'; -} - -function fixture(name) { - return fs.readFileSync(fixturePath(name), 'utf8').trim(); -} - -function resolveFixture(name, options) { - return postcss(postcssCriticalCSS(options)) - .process(fixture(name), {from: fixturePath(name)}); -} - -function compareFixtures(t, name, options) { - var postcssResult = resolveFixture(name, options); - var actual = postcssResult.css.trim(); - - fs.writeFile(fixturePath(name + '.actual'), actual); - - var expected = fixture(name + '.expected'); - t.equal( - actual, expected, - 'processed fixture ' + chalk.bold(name) + ' should be equal to expected output' - ); - - return postcssResult; -} - -function compareCritical(t, name) { +function compareCritical(t, name, testNonCritical) { t.equal( - fs.readFileSync(`${basePath}/${name}.critical.actual.css`, 'utf8').trim(), - fs.readFileSync(`${basePath}/${name}.critical.expected.css`, 'utf8').trim(), + fs.readFileSync( +`${basePath}/${name}.${testNonCritical ? 'non-critical.actual' : 'critical.actual'}.css`, 'utf8' + ).trim(), + fs.readFileSync( +`${basePath}/${name}.${testNonCritical ? 'non-critical.expected' : 'critical.expected'}.css`, 'utf8' + ).trim(), `processed fixture ${chalk.bold(name)} should be equal to expected output` ); } @@ -49,16 +25,16 @@ test('Testing "this" critical result', function(t) { }); test('Testing "this" non-critical result', function(t) { - compareFixtures(t, 'this'); - t.end(); -}); - -test('Testing "this" critical result', function(t) { - compareCritical(t, 'atRule'); - t.end(); -}); - -test('Testing "this" non-critical result', function(t) { - compareFixtures(t, 'atRule'); + compareCritical(t, 'this'); t.end(); }); +// +// test('Testing "this" critical result', function(t) { +// compareCritical(t, 'atRule'); +// t.end(); +// }); +// +// test('Testing "this" non-critical result', function(t) { +// compareFixtures(t, 'atRule'); +// t.end(); +// }); diff --git a/test/preTest.js b/test/preTest.js index e7fbd0e..c64895a 100644 --- a/test/preTest.js +++ b/test/preTest.js @@ -5,11 +5,27 @@ var postcss = require('postcss') var postcssCriticalCSS = require('..'); const basePath = `${process.cwd()}/test/fixtures`; -const files = fs.readdirSync(basePath, 'utf8'); -files.forEach(function(file) { - if (file.indexOf('.expected') === -1 && file.indexOf('.actual') === -1) { - postcss(postcssCriticalCSS({outputPath: basePath})) - .process(fs.readFileSync(`${basePath}/${file}`, 'utf8') - .trim()); +function cb (files) { + function useFileData (data, file) { + postcss([postcssCriticalCSS({outputPath: basePath})]) + .process(data) + .then(result => fs.writeFile(`${basePath}/${file.split('.')[0]}.non-critical.actual.css`, result.css)) } -}); + files.forEach(function(file) { + if (file.indexOf('.expected') === -1 && file.indexOf('.actual') === -1) { + fs.readFile(`${basePath}/${file}`, 'utf8', (err, data) => { + if (err) { + throw new Error(err) + } + useFileData(data, file) + }) + } + }) +} + +fs.readdir(basePath, 'utf8', (err, files) => { + if (err) { + throw new Error(err) + } + cb(files) +}) From 09c10c9239c26bb9b00493ec32c2bda12972cd3f Mon Sep 17 00:00:00 2001 From: Zach Green Date: Sun, 11 Dec 2016 14:25:39 -0500 Subject: [PATCH 05/16] passing atRule test --- lib/atRule.js | 10 ++++++--- lib/getCriticalRules.js | 5 +++-- lib/index.js | 15 ------------- src/atRule.js | 18 +++++++++------ src/getCriticalRules.js | 4 ++-- src/index.js | 14 ------------ test/fixtures/atRule.css | 2 +- .../fixtures/atRule.non-critical.expected.css | 14 ++++++++++++ test/index.js | 22 +++++++++---------- 9 files changed, 49 insertions(+), 55 deletions(-) create mode 100644 test/fixtures/atRule.non-critical.expected.css diff --git a/lib/atRule.js b/lib/atRule.js index 220a0b3..f2116d1 100644 --- a/lib/atRule.js +++ b/lib/atRule.js @@ -3,15 +3,19 @@ Object.defineProperty(exports, "__esModule", { value: true }); + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + exports.getCriticalFromAtRule = getCriticalFromAtRule; function getCriticalFromAtRule(args) { var result = {}; - var options = Object.assign({}, { + var options = _extends({ filename: 'critical.css', css: {} }, args); - options.css.walkAtRules('critical', function () { - result = options.css; + options.css.walkAtRules('critical', function (rule) { + result[rule.params ? rule.params : options.filename] = rule.nodes.length ? rule.nodes : rule.parent; + rule.remove(); }); return result; } diff --git a/lib/getCriticalRules.js b/lib/getCriticalRules.js index 6b3214f..dd075ea 100644 --- a/lib/getCriticalRules.js +++ b/lib/getCriticalRules.js @@ -7,6 +7,8 @@ exports.getCriticalRules = getCriticalRules; var _getChildRules = require('./getChildRules'); +var _atRule = require('./atRule'); + var _getCriticalDestination = require('./getCriticalDestination'); /** @@ -16,8 +18,7 @@ var _getCriticalDestination = require('./getCriticalDestination'); * @return {object} Object containing critical rules, organized by output destination */ function getCriticalRules(css, shouldPreserve) { - var critical = {}; - + var critical = (0, _atRule.getCriticalFromAtRule)({ css: css }); css.walkDecls('critical-selector', function (decl) { var dest = (0, _getCriticalDestination.getCriticalDestination)(decl.parent); var container = decl.parent; diff --git a/lib/index.js b/lib/index.js index 0686214..ed5af24 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,9 +1,6 @@ 'use strict'; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; -// import { getCriticalFromAtRule } from './atRule' -// import { getCriticalDestination } from './getCriticalDestination' - var _chalk = require('chalk'); @@ -47,18 +44,6 @@ function buildCritical(options) { return Object.keys(criticalOutput).reduce(function (init, cur) { var criticalCSS = _postcss2.default.root(); criticalCSS.append(criticalOutput[cur]); - // criticalOutput[cur].forEach((rule) => { - // - // rule.walkDecls('critical', (decl: Object) => { - // console.log('walking critical') - // decl.remove() - // criticalCSS.append(rule) - // if (rule.type === 'rule' && !args.preserve) { - // rule.remove() - // } - // }) - // }) - (0, _postcss2.default)(args.minify ? [(0, _cssnano2.default)()] : []).process(criticalCSS).then(function (result) { if (!args.dryRun) { _fs2.default.writeFile(_path2.default.join(args.outputPath, cur), result.css); diff --git a/src/atRule.js b/src/atRule.js index 5f24c23..1a3aeb3 100644 --- a/src/atRule.js +++ b/src/atRule.js @@ -5,15 +5,19 @@ * * @param {Object} args Function args. See flow type alias. */ -type ArgsType = {filename: string, css: Object} +type ArgsType = {filename?: string, css?: Object} export function getCriticalFromAtRule (args: ArgsType): Object { - let result: Object = {} - const options = Object.assign({}, { + const result: Object = {} + const options = { filename: 'critical.css', - css: {} - }, args) - options.css.walkAtRules('critical', () => { - result = options.css + css: {}, + ...args + } + options.css.walkAtRules('critical', (rule: Object) => { + result[rule.params ? rule.params : options.filename] = rule.nodes.length + ? rule.nodes + : rule.parent + rule.remove() }) return result } diff --git a/src/getCriticalRules.js b/src/getCriticalRules.js index e5de773..7a7a2ac 100644 --- a/src/getCriticalRules.js +++ b/src/getCriticalRules.js @@ -1,6 +1,7 @@ // @flow import { getChildRules } from './getChildRules' +import { getCriticalFromAtRule } from './atRule' import { getCriticalDestination } from './getCriticalDestination' /** @@ -10,8 +11,7 @@ import { getCriticalDestination } from './getCriticalDestination' * @return {object} Object containing critical rules, organized by output destination */ export function getCriticalRules (css: Object, shouldPreserve: boolean): Object { - const critical = {} - + const critical = getCriticalFromAtRule({ css }) css.walkDecls('critical-selector', (decl: Object) => { const dest = getCriticalDestination(decl.parent) const container = decl.parent diff --git a/src/index.js b/src/index.js index fa5669c..fa1a897 100644 --- a/src/index.js +++ b/src/index.js @@ -5,8 +5,6 @@ import postcss from 'postcss' import cssnano from 'cssnano' import fs from 'fs' import path from 'path' -// import { getCriticalFromAtRule } from './atRule' -// import { getCriticalDestination } from './getCriticalDestination' import { getCriticalRules } from './getCriticalRules' /** @@ -35,18 +33,6 @@ function buildCritical (options: ArgsType): Function { return Object.keys(criticalOutput).reduce((init: Object, cur: string): Object => { const criticalCSS = postcss.root() criticalCSS.append(criticalOutput[cur]) - // criticalOutput[cur].forEach((rule) => { - // - // rule.walkDecls('critical', (decl: Object) => { - // console.log('walking critical') - // decl.remove() - // criticalCSS.append(rule) - // if (rule.type === 'rule' && !args.preserve) { - // rule.remove() - // } - // }) - // }) - postcss(args.minify ? [cssnano()] : []) .process(criticalCSS) .then((result: Object) => { diff --git a/test/fixtures/atRule.css b/test/fixtures/atRule.css index d181a33..64e9dcb 100644 --- a/test/fixtures/atRule.css +++ b/test/fixtures/atRule.css @@ -1,4 +1,4 @@ -@critical; +@critical atRule.critical.actual.css; .foo { display: flex; diff --git a/test/fixtures/atRule.non-critical.expected.css b/test/fixtures/atRule.non-critical.expected.css new file mode 100644 index 0000000..3e8e855 --- /dev/null +++ b/test/fixtures/atRule.non-critical.expected.css @@ -0,0 +1,14 @@ +.foo { + display: flex; + width: calc(100% - 200px); +} + +.bar { + border: 10px solid gold; + height: 100%; +} + +.baz::before { + content: 'test'; + position: fixed; +} diff --git a/test/index.js b/test/index.js index 02422f6..ca102ff 100644 --- a/test/index.js +++ b/test/index.js @@ -25,16 +25,16 @@ test('Testing "this" critical result', function(t) { }); test('Testing "this" non-critical result', function(t) { - compareCritical(t, 'this'); + compareCritical(t, 'this', true); + t.end(); +}); + +test('Testing "atRule" critical result', function(t) { + compareCritical(t, 'atRule'); + t.end(); +}); + +test('Testing "atRule" non-critical result', function(t) { + compareCritical(t, 'atRule', true); t.end(); }); -// -// test('Testing "this" critical result', function(t) { -// compareCritical(t, 'atRule'); -// t.end(); -// }); -// -// test('Testing "this" non-critical result', function(t) { -// compareFixtures(t, 'atRule'); -// t.end(); -// }); From 89ea5756f8f927aaa29fe83e911386ebea29d407 Mon Sep 17 00:00:00 2001 From: Zach Green Date: Sun, 11 Dec 2016 14:31:34 -0500 Subject: [PATCH 06/16] add failing @media test --- test/fixtures/media.critical.expected.css | 1 + test/fixtures/media.css | 8 ++++++++ test/fixtures/media.non-critical.expected.css | 6 ++++++ test/index.js | 10 ++++++++++ 4 files changed, 25 insertions(+) create mode 100644 test/fixtures/media.critical.expected.css create mode 100644 test/fixtures/media.css create mode 100644 test/fixtures/media.non-critical.expected.css diff --git a/test/fixtures/media.critical.expected.css b/test/fixtures/media.critical.expected.css new file mode 100644 index 0000000..3eec687 --- /dev/null +++ b/test/fixtures/media.critical.expected.css @@ -0,0 +1 @@ +@media (min-width:700px){.foo{color:red;width:100%}} diff --git a/test/fixtures/media.css b/test/fixtures/media.css new file mode 100644 index 0000000..4980df2 --- /dev/null +++ b/test/fixtures/media.css @@ -0,0 +1,8 @@ +@media (min-width: 700px) { + .foo { + color: red; + critical-selector: this; + width: 100%; + critical-filename: 'media.critical.actual.css'; + } +} diff --git a/test/fixtures/media.non-critical.expected.css b/test/fixtures/media.non-critical.expected.css new file mode 100644 index 0000000..4907ef7 --- /dev/null +++ b/test/fixtures/media.non-critical.expected.css @@ -0,0 +1,6 @@ +@media (min-width: 700px) { + .foo { + color: red; + width: 100%; + } +} diff --git a/test/index.js b/test/index.js index ca102ff..f590b6b 100644 --- a/test/index.js +++ b/test/index.js @@ -38,3 +38,13 @@ test('Testing "atRule" non-critical result', function(t) { compareCritical(t, 'atRule', true); t.end(); }); + +test('Testing "media" critical result', function(t) { + compareCritical(t, 'media'); + t.end(); +}); + +test('Testing "media" non-critical result', function(t) { + compareCritical(t, 'media', true); + t.end(); +}); From a20a68e3446149ec43ae109405efaa23c80d8e5f Mon Sep 17 00:00:00 2001 From: Zach Green Date: Sun, 11 Dec 2016 14:40:09 -0500 Subject: [PATCH 07/16] passing @media test --- lib/getCriticalRules.js | 6 +++--- src/getCriticalRules.js | 8 +++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/getCriticalRules.js b/lib/getCriticalRules.js index dd075ea..1b5d765 100644 --- a/lib/getCriticalRules.js +++ b/lib/getCriticalRules.js @@ -21,7 +21,7 @@ function getCriticalRules(css, shouldPreserve) { var critical = (0, _atRule.getCriticalFromAtRule)({ css: css }); css.walkDecls('critical-selector', function (decl) { var dest = (0, _getCriticalDestination.getCriticalDestination)(decl.parent); - var container = decl.parent; + var container = decl.parent.parent.type === 'atrule' ? decl.parent.parent : decl.parent; var childRules = decl.value === 'scope' ? (0, _getChildRules.getChildRules)(css, decl.parent, shouldPreserve) : []; if (typeof critical[dest] === 'undefined') { critical[dest] = []; @@ -31,7 +31,7 @@ function getCriticalRules(css, shouldPreserve) { case 'scope': // Make sure the parent selector contains declarations if (decl.parent.nodes.length > 1) { - critical[dest].push(decl.parent); + critical[dest].push(container); } // Push all child rules @@ -43,7 +43,7 @@ function getCriticalRules(css, shouldPreserve) { break; case 'this': - critical[dest].push(decl.parent); + critical[dest].push(container); break; default: diff --git a/src/getCriticalRules.js b/src/getCriticalRules.js index 7a7a2ac..85b7b68 100644 --- a/src/getCriticalRules.js +++ b/src/getCriticalRules.js @@ -14,7 +14,9 @@ export function getCriticalRules (css: Object, shouldPreserve: boolean): Object const critical = getCriticalFromAtRule({ css }) css.walkDecls('critical-selector', (decl: Object) => { const dest = getCriticalDestination(decl.parent) - const container = decl.parent + const container = decl.parent.parent.type === 'atrule' + ? decl.parent.parent + : decl.parent const childRules = decl.value === 'scope' ? getChildRules(css, decl.parent, shouldPreserve) : [] @@ -26,7 +28,7 @@ export function getCriticalRules (css: Object, shouldPreserve: boolean): Object case 'scope': // Make sure the parent selector contains declarations if (decl.parent.nodes.length > 1) { - critical[dest].push(decl.parent) + critical[dest].push(container) } // Push all child rules @@ -38,7 +40,7 @@ export function getCriticalRules (css: Object, shouldPreserve: boolean): Object break case 'this': - critical[dest].push(decl.parent) + critical[dest].push(container) break default: From e070b03b8a9e5a3b5c8e1fd72b8d64f6ad10fa95 Mon Sep 17 00:00:00 2001 From: Zach Green Date: Sun, 11 Dec 2016 15:47:15 -0500 Subject: [PATCH 08/16] fix some failing tests --- .gitignore | 1 - example/example.css | 16 +++++++++ example/example.js | 38 ++++++++++++++++++++ lib/atRule.js | 2 +- package.json | 2 ++ src/atRule.js | 2 +- test/fixtures/atRule.critical.actual.css | 1 + test/fixtures/atRule.non-critical.actual.css | 14 ++++++++ test/fixtures/media.critical.actual.css | 1 + test/fixtures/media.non-critical.actual.css | 6 ++++ test/fixtures/this.critical.actual.css | 1 + test/fixtures/this.non-critical.actual.css | 4 +++ test/preTest.js | 3 ++ 13 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 example/example.css create mode 100644 example/example.js create mode 100644 test/fixtures/atRule.critical.actual.css create mode 100644 test/fixtures/atRule.non-critical.actual.css create mode 100644 test/fixtures/media.critical.actual.css create mode 100644 test/fixtures/media.non-critical.actual.css create mode 100644 test/fixtures/this.critical.actual.css create mode 100644 test/fixtures/this.non-critical.actual.css diff --git a/.gitignore b/.gitignore index 604d737..153216e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ .DS_Store node_modules/ npm-debug.log -*.actual.css diff --git a/example/example.css b/example/example.css new file mode 100644 index 0000000..64e9dcb --- /dev/null +++ b/example/example.css @@ -0,0 +1,16 @@ +@critical atRule.critical.actual.css; + +.foo { + display: flex; + width: calc(100% - 200px); +} + +.bar { + border: 10px solid gold; + height: 100%; +} + +.baz::before { + content: 'test'; + position: fixed; +} diff --git a/example/example.js b/example/example.js new file mode 100644 index 0000000..17cd165 --- /dev/null +++ b/example/example.js @@ -0,0 +1,38 @@ +#!/usr/bin/env node +var fs = require('fs') +var test = require('tape') +var postcss = require('postcss') +var postcssCriticalCSS = require('..'); + +const basePath = `${process.cwd()}/example`; +function cb (files) { + function useFileData (data, file) { + postcss([postcssCriticalCSS({outputPath: basePath})]) + .process(data) + .then(result => fs.writeFile( + `${basePath}/${file.split('.')[0]}.non-critical.css`, + result.css, + (err) => { + if (err) { + console.log(err) + } + }) + } + files.forEach(function(file) { + if (file === 'example.css') { + fs.readFile(`${basePath}/${file}`, 'utf8', (err, data) => { + if (err) { + throw new Error(err) + } + useFileData(data, file) + }) + } + }) +} + +fs.readdir(basePath, 'utf8', (err, files) => { + if (err) { + throw new Error(err) + } + cb(files) +}) diff --git a/lib/atRule.js b/lib/atRule.js index f2116d1..2a4a565 100644 --- a/lib/atRule.js +++ b/lib/atRule.js @@ -14,7 +14,7 @@ function getCriticalFromAtRule(args) { css: {} }, args); options.css.walkAtRules('critical', function (rule) { - result[rule.params ? rule.params : options.filename] = rule.nodes.length ? rule.nodes : rule.parent; + result[rule.params ? rule.params : options.filename] = rule.nodes ? rule.nodes : rule.parent; rule.remove(); }); return result; diff --git a/package.json b/package.json index d4e8508..51b9dbe 100644 --- a/package.json +++ b/package.json @@ -36,10 +36,12 @@ "eslint-plugin-standard": "^2.0.1", "flow-bin": "^0.30.0", "postcss": "^5.0.5", + "postcss-cli": "^2.6.0", "tape": "^4.6.0" }, "scripts": { "build": "eslint src/** && npm run flow && babel src --out-dir lib", + "example": "./node_modules/.bin/babel-node example/example.js", "flow": "flow; test $? -eq 0 -o $? -eq 2", "eslint": "eslint src/**", "start": "eslint src/** && npm run flow && babel src/index.js --out-file index.js --watch", diff --git a/src/atRule.js b/src/atRule.js index 1a3aeb3..cb73c57 100644 --- a/src/atRule.js +++ b/src/atRule.js @@ -14,7 +14,7 @@ export function getCriticalFromAtRule (args: ArgsType): Object { ...args } options.css.walkAtRules('critical', (rule: Object) => { - result[rule.params ? rule.params : options.filename] = rule.nodes.length + result[rule.params ? rule.params : options.filename] = rule.nodes ? rule.nodes : rule.parent rule.remove() diff --git a/test/fixtures/atRule.critical.actual.css b/test/fixtures/atRule.critical.actual.css new file mode 100644 index 0000000..cffbee4 --- /dev/null +++ b/test/fixtures/atRule.critical.actual.css @@ -0,0 +1 @@ +.foo{display:flex;width:calc(100% - 200px)}.bar{border:10px solid gold;height:100%}.baz:before{content:'test';position:fixed} \ No newline at end of file diff --git a/test/fixtures/atRule.non-critical.actual.css b/test/fixtures/atRule.non-critical.actual.css new file mode 100644 index 0000000..3e8e855 --- /dev/null +++ b/test/fixtures/atRule.non-critical.actual.css @@ -0,0 +1,14 @@ +.foo { + display: flex; + width: calc(100% - 200px); +} + +.bar { + border: 10px solid gold; + height: 100%; +} + +.baz::before { + content: 'test'; + position: fixed; +} diff --git a/test/fixtures/media.critical.actual.css b/test/fixtures/media.critical.actual.css new file mode 100644 index 0000000..2e15cc8 --- /dev/null +++ b/test/fixtures/media.critical.actual.css @@ -0,0 +1 @@ +@media (min-width:700px){.foo{color:red;width:100%}} \ No newline at end of file diff --git a/test/fixtures/media.non-critical.actual.css b/test/fixtures/media.non-critical.actual.css new file mode 100644 index 0000000..4907ef7 --- /dev/null +++ b/test/fixtures/media.non-critical.actual.css @@ -0,0 +1,6 @@ +@media (min-width: 700px) { + .foo { + color: red; + width: 100%; + } +} diff --git a/test/fixtures/this.critical.actual.css b/test/fixtures/this.critical.actual.css new file mode 100644 index 0000000..d0f08b0 --- /dev/null +++ b/test/fixtures/this.critical.actual.css @@ -0,0 +1 @@ +.foo{color:red;width:100%} \ No newline at end of file diff --git a/test/fixtures/this.non-critical.actual.css b/test/fixtures/this.non-critical.actual.css new file mode 100644 index 0000000..25e8934 --- /dev/null +++ b/test/fixtures/this.non-critical.actual.css @@ -0,0 +1,4 @@ +.foo { + color: red; + width: 100%; +} diff --git a/test/preTest.js b/test/preTest.js index c64895a..e79e602 100644 --- a/test/preTest.js +++ b/test/preTest.js @@ -12,6 +12,9 @@ function cb (files) { .then(result => fs.writeFile(`${basePath}/${file.split('.')[0]}.non-critical.actual.css`, result.css)) } files.forEach(function(file) { + if (file.indexOf('.actual') !== -1) { + fs.unlink(`${basePath}/${file}`) + } if (file.indexOf('.expected') === -1 && file.indexOf('.actual') === -1) { fs.readFile(`${basePath}/${file}`, 'utf8', (err, data) => { if (err) { From f7dbe878af18ffcb33db07ab70daf45d26519f90 Mon Sep 17 00:00:00 2001 From: Zach Green Date: Sun, 11 Dec 2016 17:06:25 -0500 Subject: [PATCH 09/16] add wrapping at rule test --- test/fixtures/atRule-wrapping.critical.actual.css | 1 + test/fixtures/atRule-wrapping.critical.expected.css | 1 + test/fixtures/atRule-wrapping.css | 11 +++++++++++ test/fixtures/atRule-wrapping.non-critical.actual.css | 4 ++++ .../atRule-wrapping.non-critical.expected.css | 4 ++++ test/index.js | 10 ++++++++++ 6 files changed, 31 insertions(+) create mode 100644 test/fixtures/atRule-wrapping.critical.actual.css create mode 100644 test/fixtures/atRule-wrapping.critical.expected.css create mode 100644 test/fixtures/atRule-wrapping.css create mode 100644 test/fixtures/atRule-wrapping.non-critical.actual.css create mode 100644 test/fixtures/atRule-wrapping.non-critical.expected.css diff --git a/test/fixtures/atRule-wrapping.critical.actual.css b/test/fixtures/atRule-wrapping.critical.actual.css new file mode 100644 index 0000000..5baa7fc --- /dev/null +++ b/test/fixtures/atRule-wrapping.critical.actual.css @@ -0,0 +1 @@ +.bar{color:tomato;height:200px} \ No newline at end of file diff --git a/test/fixtures/atRule-wrapping.critical.expected.css b/test/fixtures/atRule-wrapping.critical.expected.css new file mode 100644 index 0000000..17c0170 --- /dev/null +++ b/test/fixtures/atRule-wrapping.critical.expected.css @@ -0,0 +1 @@ +.bar{color:tomato;height:200px} diff --git a/test/fixtures/atRule-wrapping.css b/test/fixtures/atRule-wrapping.css new file mode 100644 index 0000000..324b359 --- /dev/null +++ b/test/fixtures/atRule-wrapping.css @@ -0,0 +1,11 @@ +.foo { + color: gold; + height: 100px; +} + +@critical atRule-wrapping.critical.actual.css { + .bar { + color: tomato; + height: 200px; + } +} diff --git a/test/fixtures/atRule-wrapping.non-critical.actual.css b/test/fixtures/atRule-wrapping.non-critical.actual.css new file mode 100644 index 0000000..baa5561 --- /dev/null +++ b/test/fixtures/atRule-wrapping.non-critical.actual.css @@ -0,0 +1,4 @@ +.foo { + color: gold; + height: 100px; +} diff --git a/test/fixtures/atRule-wrapping.non-critical.expected.css b/test/fixtures/atRule-wrapping.non-critical.expected.css new file mode 100644 index 0000000..baa5561 --- /dev/null +++ b/test/fixtures/atRule-wrapping.non-critical.expected.css @@ -0,0 +1,4 @@ +.foo { + color: gold; + height: 100px; +} diff --git a/test/index.js b/test/index.js index f590b6b..da3a64d 100644 --- a/test/index.js +++ b/test/index.js @@ -39,6 +39,16 @@ test('Testing "atRule" non-critical result', function(t) { t.end(); }); +test(chalk.yellow(`Testing ${chalk.bold('atRule.wrapping')} critical result`), function(t) { + compareCritical(t, 'atRule-wrapping'); + t.end(); +}); + +test(chalk.yellow(`Testing ${chalk.bold('atRule.wrapping')} non-critical result`), function(t) { + compareCritical(t, 'atRule-wrapping', true); + t.end(); +}); + test('Testing "media" critical result', function(t) { compareCritical(t, 'media'); t.end(); From 6ed8798a19cdc1b2e1f0cdfd499701891a8a8942 Mon Sep 17 00:00:00 2001 From: Zach Green Date: Sun, 11 Dec 2016 21:21:43 -0500 Subject: [PATCH 10/16] fix a scope test, add a working example --- README.md | 28 ++++++++----------- example/critical.css | 1 + example/example.css | 2 +- example/example.js | 8 +----- example/example.non-critical.css | 14 ++++++++++ lib/getCriticalRules.js | 10 ++++--- package.json | 2 +- src/getCriticalRules.js | 9 +++--- src/index.js | 2 +- test/fixtures/scope.critical.actual.css | 1 + test/fixtures/scope.critical.expected.css | 1 + test/fixtures/scope.css | 9 ++++++ test/fixtures/scope.non-critical.actual.css | 7 +++++ test/fixtures/scope.non-critical.expected.css | 7 +++++ test/index.js | 10 +++++++ 15 files changed, 76 insertions(+), 35 deletions(-) create mode 100644 example/critical.css create mode 100644 example/example.non-critical.css create mode 100644 test/fixtures/scope.critical.actual.css create mode 100644 test/fixtures/scope.critical.expected.css create mode 100644 test/fixtures/scope.css create mode 100644 test/fixtures/scope.non-critical.actual.css create mode 100644 test/fixtures/scope.non-critical.expected.css diff --git a/README.md b/README.md index b1d8a0a..6f59e45 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,20 @@ # PostCSS Critical CSS -This plugin allows you to define and output critical CSS using custom CSS properties. +This plugin allows the user to define and output critical CSS using custom atRules, and/or custom CSS properties. Critical CSS may be output to one or more files, as defined within the plugin options or within the CSS. ## Install `npm install postcss-critical-css --save-dev` -## Example +## Examples + +A live example is available in this repo. See the `/example` directory, and use the command `npm run example` to test it out. ```css /* In foo.css */ +@critical; + .foo { - critical-selector: this; border: 3px solid gray; display: flex; padding: 1em; @@ -104,22 +107,13 @@ Will output: ## Options -**outputPath** -Path to which critical CSS should be output -Default: current working directory - -**preserve** -Whether or not to remove selectors from primary CSS document once they've been marked as critical. -This should prevent duplication of selectors across critical and non-critical CSS. -WARNING: this is a destructive option and may break styles relying on the cascade! -Default: true - -**minify** -Minify output CSS -Default: true +| Arg | Type | Description | Default | +| ------------ | --------- | ------------------------------------------- | ------------------------- | +| `outputPath` | `string` | Path to which critical CSS should be output | Current working directory | +| `preserve` | `boolean` | Whether or not to remove selectors from primary CSS document once they've been marked as critical. This should prevent duplication of selectors across critical and non-critical CSS. | `true` | +| `minify` | `boolean` | Minify output CSS? | `true` | ## To Dos -- Tests - More tests - More robust warnings diff --git a/example/critical.css b/example/critical.css new file mode 100644 index 0000000..cffbee4 --- /dev/null +++ b/example/critical.css @@ -0,0 +1 @@ +.foo{display:flex;width:calc(100% - 200px)}.bar{border:10px solid gold;height:100%}.baz:before{content:'test';position:fixed} \ No newline at end of file diff --git a/example/example.css b/example/example.css index 64e9dcb..d181a33 100644 --- a/example/example.css +++ b/example/example.css @@ -1,4 +1,4 @@ -@critical atRule.critical.actual.css; +@critical; .foo { display: flex; diff --git a/example/example.js b/example/example.js index 17cd165..efcfffc 100644 --- a/example/example.js +++ b/example/example.js @@ -10,13 +10,7 @@ function cb (files) { postcss([postcssCriticalCSS({outputPath: basePath})]) .process(data) .then(result => fs.writeFile( - `${basePath}/${file.split('.')[0]}.non-critical.css`, - result.css, - (err) => { - if (err) { - console.log(err) - } - }) + `${basePath}/${file.split('.')[0]}.non-critical.css`, result.css)) } files.forEach(function(file) { if (file === 'example.css') { diff --git a/example/example.non-critical.css b/example/example.non-critical.css new file mode 100644 index 0000000..3e8e855 --- /dev/null +++ b/example/example.non-critical.css @@ -0,0 +1,14 @@ +.foo { + display: flex; + width: calc(100% - 200px); +} + +.bar { + border: 10px solid gold; + height: 100%; +} + +.baz::before { + content: 'test'; + position: fixed; +} diff --git a/lib/getCriticalRules.js b/lib/getCriticalRules.js index 1b5d765..95d5058 100644 --- a/lib/getCriticalRules.js +++ b/lib/getCriticalRules.js @@ -34,12 +34,14 @@ function getCriticalRules(css, shouldPreserve) { critical[dest].push(container); } - // Push all child rules + // Add all child rules if (childRules !== null && childRules.length) { - childRules.forEach(function (rule) { - critical[dest].push(rule); - }); + critical[dest] = critical[dest].concat(childRules); } + // Ensure source ordering is correct. + critical[dest] = critical[dest].sort(function (a, b) { + return a.source.start.line - b.source.start.line; + }); break; case 'this': diff --git a/package.json b/package.json index 51b9dbe..51b6a75 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "eslint": "eslint src/**", "start": "eslint src/** && npm run flow && babel src/index.js --out-file index.js --watch", "pretest": "./node_modules/.bin/babel-node test/preTest.js", - "test": "npm run flow && npm run pretest && tape test" + "test": "npm run pretest && tape test" }, "dependencies": { "chalk": "^1.1.3", diff --git a/src/getCriticalRules.js b/src/getCriticalRules.js index 85b7b68..178d12f 100644 --- a/src/getCriticalRules.js +++ b/src/getCriticalRules.js @@ -31,12 +31,13 @@ export function getCriticalRules (css: Object, shouldPreserve: boolean): Object critical[dest].push(container) } - // Push all child rules + // Add all child rules if (childRules !== null && childRules.length) { - childRules.forEach((rule: Object) => { - critical[dest].push(rule) - }) + critical[dest] = critical[dest].concat(childRules) } + // Ensure source ordering is correct. + critical[dest] = critical[dest] + .sort((a: Object, b: Object) => a.source.start.line - b.source.start.line) break case 'this': diff --git a/src/index.js b/src/index.js index fa1a897..b5c18cb 100644 --- a/src/index.js +++ b/src/index.js @@ -28,7 +28,7 @@ function buildCritical (options: ArgsType): Function { dryRun: false, ...options } - return (css: Object) => { + return (css: Object): Object => { let criticalOutput = getCriticalRules(css, args.preserve) return Object.keys(criticalOutput).reduce((init: Object, cur: string): Object => { const criticalCSS = postcss.root() diff --git a/test/fixtures/scope.critical.actual.css b/test/fixtures/scope.critical.actual.css new file mode 100644 index 0000000..1c02631 --- /dev/null +++ b/test/fixtures/scope.critical.actual.css @@ -0,0 +1 @@ +.foo .bar{color:green}.foo{color:orange} \ No newline at end of file diff --git a/test/fixtures/scope.critical.expected.css b/test/fixtures/scope.critical.expected.css new file mode 100644 index 0000000..bbba33b --- /dev/null +++ b/test/fixtures/scope.critical.expected.css @@ -0,0 +1 @@ +.foo .bar{color:green}.foo{color:orange} diff --git a/test/fixtures/scope.css b/test/fixtures/scope.css new file mode 100644 index 0000000..68a87be --- /dev/null +++ b/test/fixtures/scope.css @@ -0,0 +1,9 @@ +.foo .bar { + color: green; +} + +.foo { + color: orange; + critical-selector: scope; + critical-filename: scope.critical.actual.css; +} diff --git a/test/fixtures/scope.non-critical.actual.css b/test/fixtures/scope.non-critical.actual.css new file mode 100644 index 0000000..9868f29 --- /dev/null +++ b/test/fixtures/scope.non-critical.actual.css @@ -0,0 +1,7 @@ +.foo .bar { + color: green; +} + +.foo { + color: orange; +} diff --git a/test/fixtures/scope.non-critical.expected.css b/test/fixtures/scope.non-critical.expected.css new file mode 100644 index 0000000..9868f29 --- /dev/null +++ b/test/fixtures/scope.non-critical.expected.css @@ -0,0 +1,7 @@ +.foo .bar { + color: green; +} + +.foo { + color: orange; +} diff --git a/test/index.js b/test/index.js index da3a64d..ab58bdb 100644 --- a/test/index.js +++ b/test/index.js @@ -58,3 +58,13 @@ test('Testing "media" non-critical result', function(t) { compareCritical(t, 'media', true); t.end(); }); + +test(chalk.yellow(`Testing ${chalk.bold('scope')} critical result`), function(t) { + compareCritical(t, 'scope'); + t.end(); +}); + +test(chalk.yellow(`Testing ${chalk.bold('scope')} non-critical result`), function(t) { + compareCritical(t, 'scope', true); + t.end(); +}); From 6b8a8fb34bb77c0023c114b51a755684e90a12cd Mon Sep 17 00:00:00 2001 From: Zach Green Date: Sun, 11 Dec 2016 21:41:08 -0500 Subject: [PATCH 11/16] update docs with examples --- README.md | 108 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 90 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 6f59e45..2c8e11a 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ This plugin allows the user to define and output critical CSS using custom atRul A live example is available in this repo. See the `/example` directory, and use the command `npm run example` to test it out. +### Using the `@critical` atRule + ```css /* In foo.css */ @critical; @@ -30,13 +32,13 @@ Will output: } ``` -Note that in the above example, the selector is rendered as it is written in the -module. This may not be desireable, so you can alternatively identify the -selector you'd like to use in your `critical.css`; +### Using the `@critical` atRule with a custom file path + ```css /* In foo.css */ +@critical bar; + .foo { - critical-selector: .custom-selector; border: 3px solid gray; display: flex; padding: 1em; @@ -44,27 +46,49 @@ selector you'd like to use in your `critical.css`; ``` Will output: ```css -/* In critical.css */ -.custom-selector { +/* In bar.css */ +.foo { border: 3px solid gray; display: flex; padding: 1em; } ``` -If you'd like to ouptut the entire scope of a module, including children, you can! +### Using the `@critical` atRule with a subset of styles + ```css -/* in foo.css */ +/* In foo.css */ .foo { - critical-selector: scope; border: 3px solid gray; display: flex; padding: 1em; } -.foo a { - color: blue; - text-decoration: none; +@critical { + .bar { + border: 10px solid gold; + color: gold; + } +} +``` +Will output: +```css +/* In bar.css */ +.bar { + border: 10px solid gold; + color: gold; +} +``` + +### Using the custom property, `critical-selector` + +```css +/* In foo.css */ +.foo { + critical-selector: this; + border: 3px solid gray; + display: flex; + padding: 1em; } ``` Will output: @@ -75,16 +99,31 @@ Will output: display: flex; padding: 1em; } +``` -.foo a { - color: blue; - text-decoration: none; +### Using the custom property, `critical-selector`, with a custom selector. + +```css +/* In foo.css */ +.foo { + critical-selector: .bar; + border: 3px solid gray; + display: flex; + padding: 1em; +} +``` +Will output: +```css +/* In critical.css */ +.bar { + border: 3px solid gray; + display: flex; + padding: 1em; } ``` -And what if you need to output multiple critical CSS files -(for example, if you have two different templates that do not share styles)? -You can do that as well. +### Using the custom property, `critical-filename` + ```css /* in foo.css */ .foo { @@ -105,6 +144,39 @@ Will output: } ``` +### Using the custom property, `critical-selector`, with value `scope` + +This allows the user to output the entire scope of a module, including children. + +```css +/* in foo.css */ +.foo { + critical-selector: scope; + border: 3px solid gray; + display: flex; + padding: 1em; +} + +.foo a { + color: blue; + text-decoration: none; +} +``` +Will output: +```css +/* In critical.css */ +.foo { + border: 3px solid gray; + display: flex; + padding: 1em; +} + +.foo a { + color: blue; + text-decoration: none; +} +``` + ## Options | Arg | Type | Description | Default | From 8ebf7eaf59c94799af6810a39533f4d596ceae94 Mon Sep 17 00:00:00 2001 From: Zach Green Date: Sun, 11 Dec 2016 21:43:55 -0500 Subject: [PATCH 12/16] update plugin options docs --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2c8e11a..86a5040 100644 --- a/README.md +++ b/README.md @@ -177,7 +177,9 @@ Will output: } ``` -## Options +## Plugin options + +The plugin takes a single object as its only parameter. The following properties are valid: | Arg | Type | Description | Default | | ------------ | --------- | ------------------------------------------- | ------------------------- | From 566fd160da2087d08ed9919722f2dfca31635c11 Mon Sep 17 00:00:00 2001 From: Zach Green Date: Sun, 11 Dec 2016 21:56:19 -0500 Subject: [PATCH 13/16] add .travis.yml --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..2197832 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,3 @@ +language: node_js +node_js: + - "node" From 950d4799e8fa6345c6362f3ca823ef8b521e2efd Mon Sep 17 00:00:00 2001 From: Zach Green Date: Sun, 11 Dec 2016 22:04:01 -0500 Subject: [PATCH 14/16] ver bump to 2.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 51b6a75..5894d46 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "postcss-critical-css", - "version": "1.0.3", + "version": "2.0.0", "description": "Generate critical CSS using PostCSS", "main": "index.js", "repository": { From fed3b8bd34788098ccedf60d17b119aa56ee9360 Mon Sep 17 00:00:00 2001 From: Zach Green Date: Sun, 11 Dec 2016 22:06:50 -0500 Subject: [PATCH 15/16] add MIT license --- LICENSE | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..56b734e --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +The MIT License (MIT) + +Copyright © 2016 Zachary Green + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. From ef5882291b55ff6022ea7753b1e27d3076fa2024 Mon Sep 17 00:00:00 2001 From: Zach Green Date: Mon, 12 Dec 2016 22:01:16 -0500 Subject: [PATCH 16/16] fix faulty test --- test/fixtures/atRule.critical.actual.css | 2 +- test/fixtures/atRule.critical.expected.css | 2 +- test/fixtures/atRule.css | 5 ----- test/fixtures/atRule.expected.css | 15 +-------------- test/fixtures/atRule.non-critical.actual.css | 5 ----- test/fixtures/atRule.non-critical.expected.css | 5 ----- 6 files changed, 3 insertions(+), 31 deletions(-) diff --git a/test/fixtures/atRule.critical.actual.css b/test/fixtures/atRule.critical.actual.css index cffbee4..ef3a245 100644 --- a/test/fixtures/atRule.critical.actual.css +++ b/test/fixtures/atRule.critical.actual.css @@ -1 +1 @@ -.foo{display:flex;width:calc(100% - 200px)}.bar{border:10px solid gold;height:100%}.baz:before{content:'test';position:fixed} \ No newline at end of file +.foo{display:flex;width:calc(100% - 200px)}.bar{border:10px solid gold;height:100%} \ No newline at end of file diff --git a/test/fixtures/atRule.critical.expected.css b/test/fixtures/atRule.critical.expected.css index 2a69746..8a820d7 100644 --- a/test/fixtures/atRule.critical.expected.css +++ b/test/fixtures/atRule.critical.expected.css @@ -1 +1 @@ -.foo{display:flex;width:calc(100% - 200px)}.bar{border:10px solid gold;height:100%}.baz:before{content:'test';position:fixed} +.foo{display:flex;width:calc(100% - 200px)}.bar{border:10px solid gold;height:100%} diff --git a/test/fixtures/atRule.css b/test/fixtures/atRule.css index 64e9dcb..d2d84e8 100644 --- a/test/fixtures/atRule.css +++ b/test/fixtures/atRule.css @@ -9,8 +9,3 @@ border: 10px solid gold; height: 100%; } - -.baz::before { - content: 'test'; - position: fixed; -} diff --git a/test/fixtures/atRule.expected.css b/test/fixtures/atRule.expected.css index 3e8e855..8a820d7 100644 --- a/test/fixtures/atRule.expected.css +++ b/test/fixtures/atRule.expected.css @@ -1,14 +1 @@ -.foo { - display: flex; - width: calc(100% - 200px); -} - -.bar { - border: 10px solid gold; - height: 100%; -} - -.baz::before { - content: 'test'; - position: fixed; -} +.foo{display:flex;width:calc(100% - 200px)}.bar{border:10px solid gold;height:100%} diff --git a/test/fixtures/atRule.non-critical.actual.css b/test/fixtures/atRule.non-critical.actual.css index 3e8e855..ac544dd 100644 --- a/test/fixtures/atRule.non-critical.actual.css +++ b/test/fixtures/atRule.non-critical.actual.css @@ -7,8 +7,3 @@ border: 10px solid gold; height: 100%; } - -.baz::before { - content: 'test'; - position: fixed; -} diff --git a/test/fixtures/atRule.non-critical.expected.css b/test/fixtures/atRule.non-critical.expected.css index 3e8e855..ac544dd 100644 --- a/test/fixtures/atRule.non-critical.expected.css +++ b/test/fixtures/atRule.non-critical.expected.css @@ -7,8 +7,3 @@ border: 10px solid gold; height: 100%; } - -.baz::before { - content: 'test'; - position: fixed; -}