Skip to content

Commit

Permalink
with Khan#82
Browse files Browse the repository at this point in the history
  • Loading branch information
mattkrick committed Oct 5, 2016
1 parent 2dc530e commit ecb7170
Show file tree
Hide file tree
Showing 6 changed files with 805 additions and 1 deletion.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
lib
node_modules
dist/*.map
.nyc_output
199 changes: 199 additions & 0 deletions lib/generate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
'use strict';

Object.defineProperty(exports, '__esModule', {
value: true
});

var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })();

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _inlineStylePrefixerStatic = require('inline-style-prefixer/static');

var _inlineStylePrefixerStatic2 = _interopRequireDefault(_inlineStylePrefixerStatic);

var _util = require('./util');

/**
* Generate CSS for a selector and some styles.
*
* This function handles the media queries, pseudo selectors, and descendant
* styles that can be used in aphrodite styles.
*
* @param {string} selector: A base CSS selector for the styles to be generated
* with.
* @param {Object} styleTypes: A list of properties of the return type of
* StyleSheet.create, e.g. [styles.red, styles.blue].
* @param stringHandlers: See `generateCSSRuleset`
* @param useImportant: See `generateCSSRuleset`
*
* To actually generate the CSS special-construct-less styles are passed to
* `generateCSSRuleset`.
*
* For instance, a call to
*
* generateCSSInner(".foo", {
* color: "red",
* "@media screen": {
* height: 20,
* ":hover": {
* backgroundColor: "black"
* }
* },
* ":active": {
* fontWeight: "bold",
* ">>bar": {
* _names: { "foo_bar": true },
* height: 10,
* }
* }
* });
*
* will make 5 calls to `generateCSSRuleset`:
*
* generateCSSRuleset(".foo", { color: "red" }, ...)
* generateCSSRuleset(".foo:active", { fontWeight: "bold" }, ...)
* generateCSSRuleset(".foo:active .foo_bar", { height: 10 }, ...)
* // These 2 will be wrapped in @media screen {}
* generateCSSRuleset(".foo", { height: 20 }, ...)
* generateCSSRuleset(".foo:hover", { backgroundColor: "black" }, ...)
*/
var generateCSS = function generateCSS(selector, styleTypes, stringHandlers, useImportant) {
var merged = styleTypes.reduce(_util.recursiveMerge);

var declarations = {};
var mediaQueries = {};
var pseudoStyles = {};

Object.keys(merged).forEach(function (key) {
if (key[0] === ':') {
pseudoStyles[key] = merged[key];
} else if (key[0] === '@') {
mediaQueries[key] = merged[key];
} else {
declarations[key] = merged[key];
}
});

return generateCSSRuleset(selector, declarations, stringHandlers, useImportant) + Object.keys(pseudoStyles).map(function (pseudoSelector) {
return generateCSSRuleset(selector + pseudoSelector, pseudoStyles[pseudoSelector], stringHandlers, useImportant);
}).join("") + Object.keys(mediaQueries).map(function (mediaQuery) {
var ruleset = generateCSS(selector, [mediaQueries[mediaQuery]], stringHandlers, useImportant);
return mediaQuery + '{' + ruleset + '}';
}).join("");
};

exports.generateCSS = generateCSS;
/**
* Helper method of generateCSSRuleset to facilitate custom handling of certain
* CSS properties. Used for e.g. font families.
*
* See generateCSSRuleset for usage and documentation of paramater types.
*/
var runStringHandlers = function runStringHandlers(declarations, stringHandlers) {
var result = {};

Object.keys(declarations).forEach(function (key) {
// If a handler exists for this particular key, let it interpret
// that value first before continuing
if (stringHandlers && stringHandlers.hasOwnProperty(key)) {
result[key] = stringHandlers[key](declarations[key]);
} else {
result[key] = declarations[key];
}
});

return result;
};

/**
* Generate a CSS ruleset with the selector and containing the declarations.
*
* This function assumes that the given declarations don't contain any special
* children (such as media queries, pseudo-selectors, or descendant styles).
*
* Note that this method does not deal with nesting used for e.g.
* psuedo-selectors or media queries. That responsibility is left to the
* `generateCSS` function.
*
* @param {string} selector: the selector associated with the ruleset
* @param {Object} declarations: a map from camelCased CSS property name to CSS
* property value.
* @param {Object.<string, function>} stringHandlers: a map from camelCased CSS
* property name to a function which will map the given value to the value
* that is output.
* @param {bool} useImportant: A boolean saying whether to append "!important"
* to each of the CSS declarations.
* @returns {string} A string of raw CSS.
*
* Examples:
*
* generateCSSRuleset(".blah", { color: "red" })
* -> ".blah{color: red !important;}"
* generateCSSRuleset(".blah", { color: "red" }, {}, false)
* -> ".blah{color: red}"
* generateCSSRuleset(".blah", { color: "red" }, {color: c => c.toUpperCase})
* -> ".blah{color: RED}"
* generateCSSRuleset(".blah:hover", { color: "red" })
* -> ".blah:hover{color: red}"
*/
var generateCSSRuleset = function generateCSSRuleset(selector, declarations, stringHandlers, useImportant) {
var handledDeclarations = runStringHandlers(declarations, stringHandlers);

var prefixedDeclarations = (0, _inlineStylePrefixerStatic2['default'])(handledDeclarations);

var prefixedRules = (0, _util.flatten)((0, _util.objectToPairs)(prefixedDeclarations).map(function (_ref) {
var _ref2 = _slicedToArray(_ref, 2);

var key = _ref2[0];
var value = _ref2[1];

if (Array.isArray(value)) {
var _ret = (function () {
// inline-style-prefix-all returns an array when there should be
// multiple rules, we will flatten to single rules

var prefixedValues = [];
var unprefixedValues = [];

value.forEach(function (v) {
if (v.indexOf('-') === 0) {
prefixedValues.push(v);
} else {
unprefixedValues.push(v);
}
});

prefixedValues.sort();
unprefixedValues.sort();

return {
v: prefixedValues.concat(unprefixedValues).map(function (v) {
return [key, v];
})
};
})();

if (typeof _ret === 'object') return _ret.v;
}
return [[key, value]];
}));

var rules = prefixedRules.map(function (_ref3) {
var _ref32 = _slicedToArray(_ref3, 2);

var key = _ref32[0];
var value = _ref32[1];

var stringValue = (0, _util.stringifyValue)(key, value);
var ret = (0, _util.kebabifyStyleName)(key) + ':' + stringValue + ';';
return useImportant === false ? ret : (0, _util.importantify)(ret);
}).join("");

if (rules) {
return selector + '{' + rules + '}';
} else {
return "";
}
};
exports.generateCSSRuleset = generateCSSRuleset;
115 changes: 115 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
'use strict';

Object.defineProperty(exports, '__esModule', {
value: true
});

var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })();

var _util = require('./util');

var _inject = require('./inject');

var StyleSheet = {
create: function create(sheetDefinition) {
return (0, _util.mapObj)(sheetDefinition, function (_ref) {
var _ref2 = _slicedToArray(_ref, 2);

var key = _ref2[0];
var val = _ref2[1];

return [key, {
// TODO(emily): Make a 'production' mode which doesn't prepend
// the class name here, to make the generated CSS smaller.
_name: key + '_' + (0, _util.hashObject)(val),
_definition: val
}];
});
},

rehydrate: function rehydrate() {
var renderedClassNames = arguments.length <= 0 || arguments[0] === undefined ? [] : arguments[0];

(0, _inject.addRenderedClassNames)(renderedClassNames);
}
};

/**
* Utilities for using Aphrodite server-side.
*/
var StyleSheetServer = {
renderStatic: function renderStatic(renderFunc) {
(0, _inject.reset)();
(0, _inject.startBuffering)();
var html = renderFunc();
var cssContent = (0, _inject.flushToString)();

return {
html: html,
css: {
content: cssContent,
renderedClassNames: (0, _inject.getRenderedClassNames)()
}
};
}
};

/**
* Utilities for using Aphrodite in tests.
*
* Not meant to be used in production.
*/
var StyleSheetTestUtils = {
/**
* Prevent styles from being injected into the DOM.
*
* This is useful in situations where you'd like to test rendering UI
* components which use Aphrodite without any of the side-effects of
* Aphrodite happening. Particularly useful for testing the output of
* components when you have no DOM, e.g. testing in Node without a fake DOM.
*
* Should be paired with a subsequent call to
* clearBufferAndResumeStyleInjection.
*/
suppressStyleInjection: function suppressStyleInjection() {
(0, _inject.reset)();
(0, _inject.startBuffering)();
},

/**
* Opposite method of preventStyleInject.
*/
clearBufferAndResumeStyleInjection: function clearBufferAndResumeStyleInjection() {
(0, _inject.reset)();
}
};

var css = function css() {
for (var _len = arguments.length, styleDefinitions = Array(_len), _key = 0; _key < _len; _key++) {
styleDefinitions[_key] = arguments[_key];
}

var useImportant = true; // Append !important to all style definitions
return (0, _inject.injectAndGetClassName)(useImportant, styleDefinitions);
};

var injectedGlobals = new WeakSet();
var cssGlobal = function cssGlobal(globalStyles) {
if (injectedGlobals.has(globalStyles)) return;
injectedGlobals.add(globalStyles);
var selectors = Object.keys(globalStyles);
for (var i = 0; i < selectors.length; i++) {
var _name = selectors[i];
var value = globalStyles[_name];
(0, _inject.injectStyleOnce)(_name, _name, [value], false);
}
};

exports['default'] = {
StyleSheet: StyleSheet,
StyleSheetServer: StyleSheetServer,
StyleSheetTestUtils: StyleSheetTestUtils,
css: css,
cssGlobal: cssGlobal
};
module.exports = exports['default'];
Loading

0 comments on commit ecb7170

Please sign in to comment.