Socket
Socket
Sign inDemoInstall

@emotion/snapshot-serializer

Package Overview
Dependencies
Maintainers
2
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@emotion/snapshot-serializer - npm Package Compare versions

Comparing version 0.7.2 to 0.8.0

src/matchers.js

305

dist/snapshot-serializer.cjs.js
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var chalk = _interopDefault(require('chalk'));
var css = require('css');
function replacer(className, index) {
return "emotion-" + index;
}
var componentSelectorClassNamePattern = /^e[a-zA-Z0-9]+[0-9]+$/;
var replaceClassNames = function replaceClassNames(classNames, styles, code, keys) {
var index = 0;
var keyPattern = new RegExp("^(" + keys.join('|') + ")-");
return classNames.reduce(function (acc, className) {
if (keyPattern.test(className) || componentSelectorClassNamePattern.test(className)) {
var escapedRegex = new RegExp(className.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'), 'g');
return acc.replace(escapedRegex, replacer(className, index++));
}
return acc;
}, "" + styles + (styles ? '\n\n' : '') + code);
};
function getClassNames(selectors, classes) {
return classes ? selectors.concat(classes.split(' ')) : selectors;
}
function getClassNamesFromTestRenderer(selectors, _ref) {
var _ref$props = _ref.props,
props = _ref$props === void 0 ? {} : _ref$props;
return getClassNames(selectors, props.className || props.class);
}
function shouldDive(node) {
return typeof node.dive === 'function' && typeof node.type() !== 'string';
}
function isTagWithClassName(node) {
return node.prop('className') && typeof node.type() === 'string';
}
function getClassNamesFromEnzyme(selectors, node) {
// We need to dive if we have selected a styled child from a shallow render
var actualComponent = shouldDive(node) ? node.dive() : node; // Find the first node with a className prop
var components = actualComponent.findWhere(isTagWithClassName);
var classes = components.length && components.first().prop('className');
return getClassNames(selectors, classes);
}
function getClassNamesFromCheerio(selectors, node) {
var classes = node.attr('class');
return getClassNames(selectors, classes);
}
function getClassNamesFromDOMElement(selectors, node) {
return getClassNames(selectors, node.getAttribute('class'));
}
function isReactElement(val) {
return val.$$typeof === Symbol.for('react.test.json');
}
var domElementPattern = /^((HTML|SVG)\w*)?Element$/;
function isDOMElement(val) {
return val.nodeType === 1 && val.constructor && val.constructor.name && domElementPattern.test(val.constructor.name);
}
function isEnzymeElement(val) {
return typeof val.findWhere === 'function';
}
function isCheerioElement(val) {
return val.cheerio === '[cheerio object]';
}
function getClassNamesFromNodes(nodes) {
return nodes.reduce(function (selectors, node) {
if (isReactElement(node)) {
return getClassNamesFromTestRenderer(selectors, node);
} else if (isEnzymeElement(node)) {
return getClassNamesFromEnzyme(selectors, node);
} else if (isCheerioElement(node)) {
return getClassNamesFromCheerio(selectors, node);
}
return getClassNamesFromDOMElement(selectors, node);
}, []);
}
function getStylesFromClassNames(classNames, elements) {
if (!classNames.length) {
return '';
}
var keys = getKeys(elements);
if (!keys.length) {
return '';
}
var keyPatten = new RegExp("^(" + keys.join('|') + ")-");
var filteredClassNames = classNames.filter(function (className) {
return keyPatten.test(className);
});
if (!filteredClassNames.length) {
return '';
}
var selectorPattern = new RegExp('\\.(' + filteredClassNames.join('|') + ')');
var styles = '';
elements.forEach(function (element) {
var rule = element.textContent || '';
if (selectorPattern.test(rule)) {
styles += rule;
}
});
return styles;
}
function getStyleElements() {
var elements = Array.from(document.querySelectorAll('style[data-emotion]')); // $FlowFixMe
return elements;
}
var unique = function unique(arr) {
return Array.from(new Set(arr));
};
function getKeys(elements) {
var keys = unique(elements.map(function (element) {
return (// $FlowFixMe we know it exists since we query for elements with this attribute
element.getAttribute('data-emotion')
);
})).filter(Boolean);
return keys;
}
/*
* Taken from
* https://github.com/facebook/jest/blob/be4bec387d90ac8d6a7596be88bf8e4994bc3ed9/packages/expect/src/jasmine_utils.js#L234
*/
function isA(typeName, value) {
return Object.prototype.toString.apply(value) === "[object " + typeName + "]";
}
/*
* Taken from
* https://github.com/facebook/jest/blob/be4bec387d90ac8d6a7596be88bf8e4994bc3ed9/packages/expect/src/jasmine_utils.js#L36
*/
function isAsymmetric(obj) {
return obj && isA('Function', obj.asymmetricMatch);
}
function valueMatches(declaration, value) {
if (value instanceof RegExp) {
return value.test(declaration.value);
}
if (isAsymmetric(value)) {
return value.asymmetricMatch(declaration.value);
}
return value === declaration.value;
}
function toHaveStyleRule(received, property, value) {
var classNames = getClassNamesFromNodes([received]);
var cssString = getStylesFromClassNames(classNames, getStyleElements());
var styles = css.parse(cssString);
var declaration = styles.stylesheet.rules.reduce(function (decs, rule) {
return Object.assign([], decs, rule.declarations);
}, []).filter(function (dec) {
return dec.type === 'declaration' && dec.property === property;
}).pop();
if (!declaration) {
return {
pass: false,
message: function message() {
return "Property not found: " + property;
}
};
}
var pass = valueMatches(declaration, value);
var message = function message() {
return "Expected " + property + (pass ? ' not ' : ' ') + "to match:\n" + (" " + chalk.green(value) + "\n") + 'Received:\n' + (" " + chalk.red(declaration.value));
};
return {
pass: pass,
message: message
};
}
var matchers = {
toHaveStyleRule: toHaveStyleRule
};
function getNodes(node, nodes) {

@@ -11,12 +214,20 @@ if (nodes === void 0) {

if (node.children) {
node.children.forEach(function (child) {
return getNodes(child, nodes);
});
for (var _iterator = node.children, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
var _ref;
if (_isArray) {
if (_i >= _iterator.length) break;
_ref = _iterator[_i++];
} else {
_i = _iterator.next();
if (_i.done) break;
_ref = _i.value;
}
var child = _ref;
getNodes(child, nodes);
}
}
if (Array.isArray(node)) {
node.forEach(function (child) {
return getNodes(child, nodes);
});
} else if (typeof node === 'object') {
if (typeof node === 'object') {
nodes.push(node);

@@ -30,53 +241,41 @@ }

nodes.forEach(function (node) {
node.withNewStyles = true;
node.withEmotionNextStyles = true;
});
} // i know this looks hacky but it works pretty well
// and most importantly it doesn't mutate the object that gets passed in
}
function getPrettyStylesFromClassNames(classNames, elements) {
var styles = getStylesFromClassNames(classNames, elements);
var prettyStyles;
var re = /<style\n(\s+)dangerouslySetInnerHTML={\s+Object {\s+"__html": "(.*?)",\n\s+}\s+}\s+data-emotion-([\w-]+)="[^"]+"\s+\/>/g;
var serializer = {
test: function test(val) {
if (!val) {
return false;
}
try {
prettyStyles = css.stringify(css.parse(styles));
} catch (e) {
console.error(e);
throw new Error("There was an error parsing the following css: \"" + styles + "\"");
}
if (!val.withNewStyles && val.$$typeof === Symbol.for('react.test.json')) {
return true;
} else if (Array.isArray(val) && val[0] && !val[0].withNewStyles && val[0].$$typeof === Symbol.for('react.test.json')) {
return true;
}
return prettyStyles;
}
return false;
},
print: function print(val, printer) {
var nodes = getNodes(val);
markNodes(nodes);
var i = 0;
var classMap = {};
var printed = printer(val).replace(re, function (match, whiteSpace, cssString, key) {
return "<style>\n" + whiteSpace + css.stringify(css.parse( // for some reason the quotes seem to be escaped and that breaks the formatting
cssString.replace(/\\"/g, '"').replace(/\\'/g, "'"))).replace(new RegExp(key + "-([a-zA-Z0-9-]+)", 'g'), function (match) {
if (classMap[match] !== undefined) {
return classMap[match];
}
return classMap[match] = "emotion-" + i++;
}).split('\n').join('\n' + whiteSpace) + "\n" + whiteSpace.substring(2) + "</style>";
});
Object.keys(classMap).forEach(function (key) {
printed = printed.replace(new RegExp(key, 'g'), classMap[key]);
});
return printed;
} // clsPattern,
// (match, p1) => {
// if (classMap[p1] !== undefined) {
// return classMap[p1]
// }
// return match
// }
function print(val, printer) {
var nodes = getNodes(val);
markNodes(nodes);
var classNames = getClassNamesFromNodes(nodes);
var elements = getStyleElements();
var styles = getPrettyStylesFromClassNames(classNames, elements);
var printedVal = printer(val);
var keys = getKeys(elements);
return replaceClassNames(classNames, styles, printedVal, keys);
}
function test(val) {
return val && !val.withEmotionNextStyles && isReactElement(val) || isDOMElement(val);
}
var index = {
print: print,
test: test
};
module.exports = serializer;
//# sourceMappingURL=snapshot-serializer.cjs.js.map
exports.print = print;
exports.test = test;
exports.default = index;
exports.matchers = matchers;

4

package.json
{
"name": "@emotion/snapshot-serializer",
"version": "0.7.2",
"version": "0.8.0",
"description": "A snapshot serializer for jest and emotion",
"main": "dist/snapshot-serializer.cjs.js",
"module": "dist/snapshot-serializer.esm.js",
"license": "MIT",
"repository": "https://github.com/emotion-js/emotion/tree/master/next-packages/snapshot-serializer",
"dependencies": {
"chalk": "^2.4.1",
"css": "^2.2.1"

@@ -11,0 +11,0 @@ },

// @flow
import { parse, stringify } from 'css'
import * as css from 'css'
import { replaceClassNames } from './replace-class-names'
import {
getClassNamesFromNodes,
isReactElement,
isDOMElement,
getStylesFromClassNames,
getStyleElements,
getKeys
} from './utils'
// i know this package is called @emotion/snapshot-serializer and this is exporting a matcher but this is probably going to move to jest-emotion later
export { matchers } from './matchers'
function getNodes(node, nodes = []) {
if (node.children) {
node.children.forEach(child => getNodes(child, nodes))
for (let child of node.children) {
getNodes(child, nodes)
}
}
if (Array.isArray(node)) {
node.forEach(child => getNodes(child, nodes))
} else if (typeof node === 'object') {
if (typeof node === 'object') {
nodes.push(node)

@@ -19,67 +32,40 @@ }

nodes.forEach(node => {
node.withNewStyles = true
node.withEmotionNextStyles = true
})
}
// i know this looks hacky but it works pretty well
// and most importantly it doesn't mutate the object that gets passed in
let re = /<style\n(\s+)dangerouslySetInnerHTML={\s+Object {\s+"__html": "(.*?)",\n\s+}\s+}\s+data-emotion-([\w-]+)="[^"]+"\s+\/>/g
function getPrettyStylesFromClassNames(
classNames: Array<string>,
elements: Array<HTMLStyleElement>
) {
let styles = getStylesFromClassNames(classNames, elements)
const serializer = {
test: (val: any) => {
if (!val) {
return false
}
if (!val.withNewStyles && val.$$typeof === Symbol.for('react.test.json')) {
return true
} else if (
Array.isArray(val) &&
val[0] &&
!val[0].withNewStyles &&
val[0].$$typeof === Symbol.for('react.test.json')
) {
return true
}
return false
},
print: (val: any, printer: any => string) => {
const nodes = getNodes(val)
markNodes(nodes)
let i = 0
const classMap = {}
let printed = printer(val).replace(
re,
(match, whiteSpace, cssString, key) => {
return `<style>\n${whiteSpace}${stringify(
parse(
// for some reason the quotes seem to be escaped and that breaks the formatting
cssString.replace(/\\"/g, '"').replace(/\\'/g, "'")
)
)
.replace(new RegExp(`${key}-([a-zA-Z0-9-]+)`, 'g'), match => {
if (classMap[match] !== undefined) {
return classMap[match]
}
return (classMap[match] = `emotion-${i++}`)
})
.split('\n')
.join('\n' + whiteSpace)}\n${whiteSpace.substring(2)}</style>`
}
)
Object.keys(classMap).forEach(key => {
printed = printed.replace(new RegExp(key, 'g'), classMap[key])
})
return printed
let prettyStyles
try {
prettyStyles = css.stringify(css.parse(styles))
} catch (e) {
console.error(e)
throw new Error(`There was an error parsing the following css: "${styles}"`)
}
return prettyStyles
}
// clsPattern,
// (match, p1) => {
// if (classMap[p1] !== undefined) {
// return classMap[p1]
// }
// return match
// }
export function print(val: *, printer: Function) {
const nodes = getNodes(val)
markNodes(nodes)
const classNames = getClassNamesFromNodes(nodes)
let elements = getStyleElements()
const styles = getPrettyStylesFromClassNames(classNames, elements)
const printedVal = printer(val)
let keys = getKeys(elements)
return replaceClassNames(classNames, styles, printedVal, keys)
}
export default serializer
export function test(val: *) {
return (
(val && !val.withEmotionNextStyles && isReactElement(val)) ||
isDOMElement(val)
)
}
export default { print, test }
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc