Skip to content
This repository has been archived by the owner on Sep 6, 2018. It is now read-only.

Commit

Permalink
postprocessor => postprocessorFor()
Browse files Browse the repository at this point in the history
Don't use postcss options because they can't be passed along when using grunt-postcss.
  • Loading branch information
cvrebert committed Jan 19, 2015
1 parent 9d035dd commit 5b3cac9
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 58 deletions.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,13 @@ Unofficially supported:
## API
### Node.js module; CSS postprocessor
The npm module has the following properties:
* `postprocessor` - CSS postprocessor that transforms the source CSS as described above. A [PostCSS](https://github.com/postcss/postcss) processor object (that was returned from a call to the `postcss()` function). It requires that a `hoverSelectorPrefix` string option be provided; this string will be prepended to all selectors within `@media (hover: hover) {...}` blocks within the source CSS.
* `postprocessorFor`
* Arguments: an options object with one property:
* `hoverSelectorPrefix` - This string will be prepended to all selectors within `@media (hover: hover) {...}` blocks within the source CSS.
* Type: `string`
* Side-effects: none
* Return type: A [PostCSS](https://github.com/postcss/postcss) processor object (that was returned from a call to the `postcss()` function).
* Returns a CSS postprocessor that transforms the source CSS as described above.
* `featureDetector` - Each of this object's properties is a string filepath to a JavaScript file containing the browser-side feature detector in a particular JavaScript module format.
* `es6` - [ECMAScript 6 module](http://www.2ality.com/2014/09/es6-modules-final.html) format (this is the original from which the other versions are generated)
* `cjs` - [CommonJS](http://wiki.commonjs.org/wiki/Modules/1.1) module format
Expand Down Expand Up @@ -143,6 +149,7 @@ _Also, please don't edit files in the `dist` subdirectory as they are generated
## Release History
See the [GitHub Releases page](https://github.com/cvrebert/mq4-hover-hover-shim/releases) for detailed changelogs.
* (next release) - `master`
* 2015-01-18 - v0.0.4: Fix crash when CSS contains a media-type-only media query. Replaced `postprocessor` with `postprocessorFor()`.
* 2015-01-14 - v0.0.3: Add jspm metadata. Improve docs.
* 2015-01-09 - v0.0.2: Many improvements. jQuery is now a dependency on the client side.
* 2014-12-31 - v0.0.1: Initial release
Expand Down
2 changes: 1 addition & 1 deletion src/nodejs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ var CLIENT_SIDE_FEATURE_DETECTOR_FILENAME = 'mq4-hover-hover-shim.js';


module.exports = {
postprocessor: require('./postprocessor'),
postprocessorFor: require('./postprocessor'),
featureDetector: {
es6: path.join(__dirname, '../browser', CLIENT_SIDE_FEATURE_DETECTOR_FILENAME),
cjs: path.join(__dirname, '../../dist/cjs', CLIENT_SIDE_FEATURE_DETECTOR_FILENAME),
Expand Down
76 changes: 39 additions & 37 deletions src/nodejs/postprocessor.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,46 +68,48 @@ function prefixSelectorsWith(rule, selectorPrefix) {
}


module.exports = postcss(function process(css, opts) {
var hoverSelectorPrefix = opts.hoverSelectorPrefix;
if ((typeof hoverSelectorPrefix) !== 'string') {
throw new Error('hoverSelectorPrefix option must be a string');
}
module.exports = function (opts) {
return postcss(function process(css) {
var hoverSelectorPrefix = opts.hoverSelectorPrefix;
if ((typeof hoverSelectorPrefix) !== 'string') {
throw new Error('hoverSelectorPrefix option must be a string');
}

css.eachAtRule('media', function (atRule) {
var mediaType = mediaTypeIfSimpleHoverHover(atRule);
switch (mediaType) {
case 'all':
/* falls through */
case 'screen': {
atRule.eachRule(function (rule) {
prefixSelectorsWith(rule, hoverSelectorPrefix);
});
if (mediaType === 'screen') {
atRule.params = 'screen';
}
else {
// Remove tautological @media all {...} wrapper
replaceWithItsChildren(atRule);
css.eachAtRule('media', function (atRule) {
var mediaType = mediaTypeIfSimpleHoverHover(atRule);
switch (mediaType) {
case 'all':
/* falls through */
case 'screen': {
atRule.eachRule(function (rule) {
prefixSelectorsWith(rule, hoverSelectorPrefix);
});
if (mediaType === 'screen') {
atRule.params = 'screen';
}
else {
// Remove tautological @media all {...} wrapper
replaceWithItsChildren(atRule);
}
return;
}
return;
}

case 'print':
/* falls through */
case 'speech': {
// These media types never support hovering
// Delete always-false media query
atRule.removeSelf();
return;
}
case 'print':
/* falls through */
case 'speech': {
// These media types never support hovering
// Delete always-false media query
atRule.removeSelf();
return;
}

case undefined: {
return; // Media query irrelevant or too complicated
}
default: {
return; // Deprecated media type; take no action.
case undefined: {
return; // Media query irrelevant or too complicated
}
default: {
return; // Deprecated media type; take no action.
}
}
}
});
});
});
};
38 changes: 19 additions & 19 deletions test/postprocessor_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,111 +28,111 @@ exports.mq4HoverShim = {
'has no effect when there are no media queries': function (test) {
test.expect(1);
test.deepEqual(
postprocessor.process(".foobar { display: none; }", {hoverSelectorPrefix: 'PREFIX>'}).css,
postprocessor({hoverSelectorPrefix: 'PREFIX>'}).process(".foobar { display: none; }").css,
".foobar { display: none; }"
);
test.done();
},
'skips non-media at-rules': function (test) {
test.expect(1);
test.deepEqual(
postprocessor.process("@quux (hover: hover) { .foobar { display: none; } }", {hoverSelectorPrefix: 'PREFIX>'}).css,
postprocessor({hoverSelectorPrefix: 'PREFIX>'}).process("@quux (hover: hover) { .foobar { display: none; } }").css,
"@quux (hover: hover) { .foobar { display: none; } }"
);
test.done();
},
'skips media queries with only a media type': function (test) {
test.expect(1);
test.deepEqual(
postprocessor.process("@media screen { .foobar { display: none; } }", {hoverSelectorPrefix: 'PREFIX>'}).css,
postprocessor({hoverSelectorPrefix: 'PREFIX>'}).process("@media screen { .foobar { display: none; } }").css,
"@media screen { .foobar { display: none; } }"
);
test.done();
},
'skips media queries with ORs': function (test) {
test.expect(2);
test.deepEqual(
postprocessor.process("@media (hover: hover), (orientation: landscape) { .foobar { display: none; } }", {hoverSelectorPrefix: 'PREFIX>'}).css,
postprocessor({hoverSelectorPrefix: 'PREFIX>'}).process("@media (hover: hover), (orientation: landscape) { .foobar { display: none; } }").css,
"@media (hover: hover), (orientation: landscape) { .foobar { display: none; } }"
);
test.deepEqual(
postprocessor.process("@media screen (hover: hover), (orientation: landscape) { .foobar { display: none; } }", {hoverSelectorPrefix: 'PREFIX>'}).css,
postprocessor({hoverSelectorPrefix: 'PREFIX>'}).process("@media screen (hover: hover), (orientation: landscape) { .foobar { display: none; } }").css,
"@media screen (hover: hover), (orientation: landscape) { .foobar { display: none; } }"
);
test.done();
},
'skips media queries with ANDs': function (test) {
test.expect(2);
test.deepEqual(
postprocessor.process("@media (hover: hover) and (orientation: landscape) { .foobar { display: none; } }", {hoverSelectorPrefix: 'PREFIX>'}).css,
postprocessor({hoverSelectorPrefix: 'PREFIX>'}).process("@media (hover: hover) and (orientation: landscape) { .foobar { display: none; } }").css,
"@media (hover: hover) and (orientation: landscape) { .foobar { display: none; } }"
);
test.deepEqual(
postprocessor.process("@media screen (hover: hover) and (orientation: landscape) { .foobar { display: none; } }", {hoverSelectorPrefix: 'PREFIX>'}).css,
postprocessor({hoverSelectorPrefix: 'PREFIX>'}).process("@media screen (hover: hover) and (orientation: landscape) { .foobar { display: none; } }").css,
"@media screen (hover: hover) and (orientation: landscape) { .foobar { display: none; } }"
);
test.done();
},
'skips media queries that are not about the hover media feature': function (test) {
test.expect(1);
test.deepEqual(
postprocessor.process("@media (orientation: landscape) { .foobar { display: none; } }", {hoverSelectorPrefix: 'PREFIX>'}).css,
postprocessor({hoverSelectorPrefix: 'PREFIX>'}).process("@media (orientation: landscape) { .foobar { display: none; } }").css,
"@media (orientation: landscape) { .foobar { display: none; } }"
);
test.done();
},
'skips media queries about the hover media feature with a non-hover value': function (test) {
test.expect(2);
test.deepEqual(
postprocessor.process("@media (hover: none) { .foobar { display: none; } }", {hoverSelectorPrefix: 'PREFIX>'}).css,
postprocessor({hoverSelectorPrefix: 'PREFIX>'}).process("@media (hover: none) { .foobar { display: none; } }").css,
"@media (hover: none) { .foobar { display: none; } }"
);
test.deepEqual(
postprocessor.process("@media (hover: on-demand) { .foobar { display: none; } }", {hoverSelectorPrefix: 'PREFIX>'}).css,
postprocessor({hoverSelectorPrefix: 'PREFIX>'}).process("@media (hover: on-demand) { .foobar { display: none; } }").css,
"@media (hover: on-demand) { .foobar { display: none; } }"
);
test.done();
},
'works correctly on a representative example': function (test) {
test.expect(1);
test.deepEqual(
postprocessor.process("@media (hover: hover) { .foobar { color: white; background: red; } div .quux > input { color: blue; background: white; } }", {hoverSelectorPrefix: 'PREFIX>'}).css,
postprocessor({hoverSelectorPrefix: 'PREFIX>'}).process("@media (hover: hover) { .foobar { color: white; background: red; } div .quux > input { color: blue; background: white; } }").css,
"PREFIX>.foobar {\n color: white;\n background: red;\n}\nPREFIX>div .quux > input {\n color: blue;\n background: white;\n}"
);
test.done();
},
'handles nested at-rules': function (test) {
test.expect(4);
test.deepEqual(
postprocessor.process("@media (orientation: landscape) { @media (hover: hover) { .foobar { display: none; } } }", {hoverSelectorPrefix: 'PREFIX>'}).css,
postprocessor({hoverSelectorPrefix: 'PREFIX>'}).process("@media (orientation: landscape) { @media (hover: hover) { .foobar { display: none; } } }").css,
"@media (orientation: landscape) { PREFIX>.foobar { display: none; } }"
);
test.deepEqual(
postprocessor.process("@media screen (orientation: landscape) { @media (hover: hover) { .foobar { display: none; } } }", {hoverSelectorPrefix: 'PREFIX>'}).css,
postprocessor({hoverSelectorPrefix: 'PREFIX>'}).process("@media screen (orientation: landscape) { @media (hover: hover) { .foobar { display: none; } } }").css,
"@media screen (orientation: landscape) { PREFIX>.foobar { display: none; } }"
);
test.deepEqual(
postprocessor.process("@media (hover: hover) { @media (orientation: landscape) { .foobar { display: none; } } }", {hoverSelectorPrefix: 'PREFIX>'}).css,
postprocessor({hoverSelectorPrefix: 'PREFIX>'}).process("@media (hover: hover) { @media (orientation: landscape) { .foobar { display: none; } } }").css,
"@media (orientation: landscape) {\n PREFIX>.foobar {\n display: none;\n }\n}"
);
test.deepEqual(
postprocessor.process("@media (hover: hover) { @media screen (orientation: landscape) { .foobar { display: none; } } }", {hoverSelectorPrefix: 'PREFIX>'}).css,
postprocessor({hoverSelectorPrefix: 'PREFIX>'}).process("@media (hover: hover) { @media screen (orientation: landscape) { .foobar { display: none; } } }").css,
"@media screen (orientation: landscape) {\n PREFIX>.foobar {\n display: none;\n }\n}"
);
test.done();
},
'handles applicable media types': function (test) {
test.expect(1);
test.deepEqual(
postprocessor.process("@media screen (hover: hover) { .foobar { display: none; } }", {hoverSelectorPrefix: 'PREFIX>'}).css,
postprocessor({hoverSelectorPrefix: 'PREFIX>'}).process("@media screen (hover: hover) { .foobar { display: none; } }").css,
"@media screen { PREFIX>.foobar { display: none; } }"
);
test.done();
},
'handles non-applicable media types': function (test) {
test.expect(1);
test.deepEqual(
postprocessor.process("@media print (hover: hover) { .foobar { display: none; } }", {hoverSelectorPrefix: 'PREFIX>'}).css,
postprocessor({hoverSelectorPrefix: 'PREFIX>'}).process("@media print (hover: hover) { .foobar { display: none; } }").css,
""
);
test.done();
Expand All @@ -141,7 +141,7 @@ exports.mq4HoverShim = {
test.expect(1);
test.throws(function () {
/*eslint-disable no-unused-expressions */
postprocessor.process("@media (hover: hover) { .foobar { display: none; } }", {}).css;// jshint ignore:line
postprocessor({}).process("@media (hover: hover) { .foobar { display: none; } }").css;// jshint ignore:line
/*eslint-enable no-unused-expressions */
}, Error, 'hoverSelectorPrefix option must be a string');
test.done();
Expand All @@ -150,7 +150,7 @@ exports.mq4HoverShim = {
test.expect(1);
test.throws(function () {
/*eslint-disable no-unused-expressions */
postprocessor.process("@media (hover: hover) { .foobar { display: none; } }", {hoverSelectorPrefix: 42}).css;// jshint ignore:line
postprocessor({hoverSelectorPrefix: 42}).process("@media (hover: hover) { .foobar { display: none; } }").css;// jshint ignore:line
/*eslint-enable no-unused-expressions */
}, Error, 'hoverSelectorPrefix option must be a string');
test.done();
Expand Down

0 comments on commit 5b3cac9

Please sign in to comment.