diff --git a/dist/decompiler.js b/dist/decompiler.js index 50b490b..3cd47be 100644 --- a/dist/decompiler.js +++ b/dist/decompiler.js @@ -4,10 +4,14 @@ Object.defineProperty(exports, '__esModule', { value: true }); +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + var _reactAddonsTestUtils = require('react-addons-test-utils'); var _jsBeautify = require('js-beautify'); @@ -20,95 +24,165 @@ var _objectAssign = require('object-assign'); var _objectAssign2 = _interopRequireDefault(_objectAssign); -var getProps = function getProps(component) { - return (0, _objectAssign2['default'])((0, _objectAssign2['default'])(getAttribute('key', component), getAttribute('ref', component)), component.props); -}; - -var getAttribute = function getAttribute(attribute, component) { - return component[attribute] ? _defineProperty({}, attribute, component[attribute]) : {}; -}; - -var getChildren = function getChildren(component) { - return getProps(component).children; -}; - -var getPropsKeys = function getPropsKeys(component) { - return Object.keys(getProps(component)).filter(function (prop) { - return prop !== 'children'; - }); -}; - -var getComponentName = function getComponentName(component) { - return component.type.displayName || component.type.name; -}; - -var getComponentType = function getComponentType(component) { - return getComponentName(component) || component.type; -}; - -var getPropValue = function getPropValue(component, prop) { - return getProps(component)[prop]; -}; - -var getFormatedPropValue = function getFormatedPropValue(propValue) { - return typeof propValue === 'string' ? '"' + stringifyItem(propValue) + '"' : '{' + stringifyItem(propValue) + '}'; -}; - -var getComponentProp = function getComponentProp(component, prop) { - return getFormatedPropValue(getPropValue(component, prop)); -}; - -var appendStringifiedProp = function appendStringifiedProp(component) { - return function (accumulated, prop) { - return accumulated + ' ' + prop + '=' + getComponentProp(component, prop); - }; -}; - -var stringifyProps = function stringifyProps(component) { - return getPropsKeys(component).reduce(appendStringifiedProp(component), ''); -}; - -var stringifyComposedComponent = function stringifyComposedComponent(component) { - return '<' + getComponentType(component) + stringifyProps(component) + '>' + stringifyItems(getChildren(component)) + '' + getComponentType(component) + '>'; -}; - -var stringifySimpleComponent = function stringifySimpleComponent(component) { - return '<' + getComponentType(component) + stringifyProps(component) + ' />'; -}; +var Decompiler = (function () { + function Decompiler(options) { + _classCallCheck(this, Decompiler); -var stringifyComponent = function stringifyComponent(component) { - return getChildren(component) ? stringifyComposedComponent(component) : stringifySimpleComponent(component); -}; - -var stringifyFunction = function stringifyFunction(value) { - return value.toString().replace(/ {[\s\S]*/, '{ ... }'); -}; - -var stringifyValue = function stringifyValue(value) { - switch (typeof value) { - case 'function': - return stringifyFunction(value); - case 'object': - return (0, _stringifyObject2['default'])(value, { indent: ' ' }).replace(/\n| /g, ''); - case 'undefined': - return 'undefined'; - default: - return value.toString(); + this.skipPropsWithNonDefaultValues = !!options.skipPropsWithNonDefaultValues; } -}; -var stringifyItem = function stringifyItem(item) { - return (0, _reactAddonsTestUtils.isElement)(item) ? stringifyComponent(item) : stringifyValue(item); + _createClass(Decompiler, [{ + key: 'filteredProps', + value: function filteredProps(component) { + var _this = this; + + if (!this.skipPropsWithNonDefaultValues) { + return component.props; + } else { + var _ret = (function () { + var props = {}; + Object.keys(component.props).filter(function (key) { + return !_this.isDefaultValue(component, key); + }).forEach(function (key) { + props[key] = component.props[key]; + }); + return { + v: props + }; + })(); + + if (typeof _ret === 'object') return _ret.v; + } + } + }, { + key: 'isDefaultValue', + value: function isDefaultValue(component, prop) { + return component.type.defaultProps && component.props[prop] === component.type.defaultProps[prop]; + } + }, { + key: 'getProps', + value: function getProps(component) { + return (0, _objectAssign2['default'])((0, _objectAssign2['default'])(this.getAttribute('key', component), this.getAttribute('ref', component)), this.filteredProps(component)); + } + }, { + key: 'getAttribute', + value: function getAttribute(attribute, component) { + return component[attribute] ? _defineProperty({}, attribute, component[attribute]) : {}; + } + }, { + key: 'getChildren', + value: function getChildren(component) { + return this.getProps(component).children; + } + }, { + key: 'getPropsKeys', + value: function getPropsKeys(component) { + return Object.keys(this.getProps(component)).filter(function (prop) { + return prop !== 'children'; + }); + } + }, { + key: 'getComponentName', + value: function getComponentName(component) { + return component.type.displayName || component.type.name; + } + }, { + key: 'getComponentType', + value: function getComponentType(component) { + return this.getComponentName(component) || component.type; + } + }, { + key: 'getPropValue', + value: function getPropValue(component, prop) { + return this.getProps(component)[prop]; + } + }, { + key: 'getFormatedPropValue', + value: function getFormatedPropValue(propValue) { + return typeof propValue === 'string' ? '"' + this.stringifyItem(propValue) + '"' : '{' + this.stringifyItem(propValue) + '}'; + } + }, { + key: 'getComponentProp', + value: function getComponentProp(component, prop) { + return this.getFormatedPropValue(this.getPropValue(component, prop)); + } + }, { + key: 'appendStringifiedProp', + value: function appendStringifiedProp(component) { + var _this2 = this; + + return function (accumulated, prop) { + return accumulated + ' ' + prop + '=' + _this2.getComponentProp(component, prop); + }; + } + }, { + key: 'stringifyProps', + value: function stringifyProps(component) { + return this.getPropsKeys(component).reduce(this.appendStringifiedProp(component), ''); + } + }, { + key: 'stringifyComposedComponent', + value: function stringifyComposedComponent(component) { + return '<' + this.getComponentType(component) + this.stringifyProps(component) + '>' + this.stringifyItems(this.getChildren(component)) + '' + this.getComponentType(component) + '>'; + } + }, { + key: 'stringifySimpleComponent', + value: function stringifySimpleComponent(component) { + return '<' + this.getComponentType(component) + this.stringifyProps(component) + ' />'; + } + }, { + key: 'stringifyComponent', + value: function stringifyComponent(component) { + return this.getChildren(component) ? this.stringifyComposedComponent(component) : this.stringifySimpleComponent(component); + } + }, { + key: 'stringifyFunction', + value: function stringifyFunction(value) { + return value.toString().replace(/ {[\s\S]*/, '{ ... }'); + } + }, { + key: 'stringifyValue', + value: function stringifyValue(value) { + switch (typeof value) { + case 'function': + return this.stringifyFunction(value); + case 'object': + return (0, _stringifyObject2['default'])(value, { indent: ' ' }).replace(/\n| /g, ''); + case 'undefined': + return 'undefined'; + default: + return value.toString(); + } + } + }, { + key: 'stringifyItem', + value: function stringifyItem(item) { + return (0, _reactAddonsTestUtils.isElement)(item) ? this.stringifyComponent(item) : this.stringifyValue(item); + } + }, { + key: 'stringifyItems', + value: function stringifyItems(components) { + var _this3 = this; + + return [].concat(components).map(function (item) { + return _this3.stringifyItem(item); + }).join(''); + } + }]); + + return Decompiler; +})(); + +var decompile = function decompile(components) { + var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + + return new Decompiler(options).stringifyItems(components); }; -var stringifyItems = function stringifyItems(components) { - return [].concat(components).map(stringifyItem).join(''); -}; - -var decompile = stringifyItems; - exports.decompile = decompile; -var formatted = function formatted(items) { - return (0, _jsBeautify.html)(stringifyItems(items), { indent_size: 2 }); +var formatted = function formatted(components) { + var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + + return (0, _jsBeautify.html)(new Decompiler(options).stringifyItems(components), { indent_size: 2 }); }; exports.formatted = formatted; diff --git a/src/decompiler.js b/src/decompiler.js index 471a670..d23e128 100644 --- a/src/decompiler.js +++ b/src/decompiler.js @@ -3,71 +3,120 @@ import {html as htmlBeautify} from 'js-beautify'; import stringifyObject from './stringify-object'; import merge from 'object-assign'; -const getProps = component => - merge( - merge( - getAttribute('key', component), - getAttribute('ref', component) - ), - component.props - ); + +class Decompiler { + constructor(options) { + this.skipPropsWithNonDefaultValues = + !!options.skipPropsWithNonDefaultValues; + } + + filteredProps(component) { + if (!this.skipPropsWithNonDefaultValues) { + return component.props; + } else { + let props = {}; + Object.keys(component.props). + filter(key => !this.isDefaultValue(component, key)). + forEach(key => { props[key] = component.props[key]; }); + return props; + } + } -const getAttribute = (attribute, component) => - component[attribute] ? {[attribute]: component[attribute]} : {}; + isDefaultValue(component, prop) { + return component.type.defaultProps && + component.props[prop] === component.type.defaultProps[prop]; + } -const getChildren = component => getProps(component).children; + getProps(component) { + return merge( + merge( + this.getAttribute('key', component), + this.getAttribute('ref', component) + ), + this.filteredProps(component) + ) + } + + getAttribute(attribute, component) { + return component[attribute] ? {[attribute]: component[attribute]} : {}; + } + + getChildren(component) { + return this.getProps(component).children; + } + + getPropsKeys(component) { + return Object.keys(this.getProps(component)).filter(prop => prop !== 'children'); + } + + getComponentName(component) { + return component.type.displayName || component.type.name; + } -const getPropsKeys = component => - Object.keys(getProps(component)).filter(prop => prop !== 'children'); + getComponentType(component) { + return this.getComponentName(component) || component.type; + } -const getComponentName = component => - component.type.displayName || component.type.name; + getPropValue(component, prop) { + return this.getProps(component)[prop]; + } -const getComponentType = component => - getComponentName(component) || component.type; + getFormatedPropValue(propValue) { + return typeof propValue === 'string' ? `"${this.stringifyItem(propValue)}"` : `{${this.stringifyItem(propValue)}}`; + } -const getPropValue = (component, prop) => - getProps(component)[prop]; + getComponentProp(component, prop) { + return this.getFormatedPropValue(this.getPropValue(component, prop)); + } -const getFormatedPropValue = (propValue) => - typeof propValue === 'string' ? `"${stringifyItem(propValue)}"` : `{${stringifyItem(propValue)}}`; + appendStringifiedProp(component) { + return (accumulated, prop) => + `${accumulated} ${prop}=${this.getComponentProp(component, prop)}`; + } -const getComponentProp = (component, prop) => - getFormatedPropValue(getPropValue(component, prop)); + stringifyProps(component) { + return this.getPropsKeys(component).reduce(this.appendStringifiedProp(component), ''); + } -const appendStringifiedProp = component => (accumulated, prop) => - `${accumulated} ${prop}=${getComponentProp(component, prop)}`; + stringifyComposedComponent(component) { + return `<${this.getComponentType(component)}${this.stringifyProps(component)}>${this.stringifyItems(this.getChildren(component))}${this.getComponentType(component)}>`; + } -const stringifyProps = component => - getPropsKeys(component).reduce(appendStringifiedProp(component), ''); + stringifySimpleComponent(component) { + return `<${this.getComponentType(component)}${this.stringifyProps(component)} />`; + } -const stringifyComposedComponent = component => - `<${getComponentType(component)}${stringifyProps(component)}>${stringifyItems(getChildren(component))}${getComponentType(component)}>`; + stringifyComponent(component) { + return this.getChildren(component) ? this.stringifyComposedComponent(component) : this.stringifySimpleComponent(component); + } -const stringifySimpleComponent = component => - `<${getComponentType(component)}${stringifyProps(component)} />`; + stringifyFunction(value) { + return value.toString().replace(/ {[\s\S]*/, '{ ... }'); + } -const stringifyComponent = component => - getChildren(component) ? stringifyComposedComponent(component) : stringifySimpleComponent(component); + stringifyValue(value) { + switch (typeof value) { + case 'function': return this.stringifyFunction(value); + case 'object': return stringifyObject(value, {indent: ' '}).replace(/\n| /g, ''); + case 'undefined': return 'undefined'; + default: return value.toString(); + } + } -const stringifyFunction = value => - value.toString().replace(/ {[\s\S]*/, '{ ... }') + stringifyItem(item) { + return isReact(item) ? this.stringifyComponent(item) : this.stringifyValue(item); + } -const stringifyValue = value => { - switch (typeof value) { - case 'function': return stringifyFunction(value); - case 'object': return stringifyObject(value, {indent: ' '}).replace(/\n| /g, ''); - case 'undefined': return 'undefined'; - default: return value.toString(); + stringifyItems(components) { + return [].concat(components).map((item) => this.stringifyItem(item)).join(''); } } -const stringifyItem = item => - isReact(item) ? stringifyComponent(item) : stringifyValue(item); - -const stringifyItems = components => - [].concat(components).map(stringifyItem).join(''); +export const decompile = function(components, options={}) { + return (new Decompiler(options)).stringifyItems(components); +}; -export const decompile = stringifyItems; +export const formatted = function(components, options={}) { + return htmlBeautify((new Decompiler(options)).stringifyItems(components), { indent_size: 2 }); +}; -export const formatted = (items) => htmlBeautify(stringifyItems(items), { indent_size: 2 }); diff --git a/test/decompiler_spec.js b/test/decompiler_spec.js index 0bac6ba..4bee7a2 100644 --- a/test/decompiler_spec.js +++ b/test/decompiler_spec.js @@ -18,6 +18,35 @@ describe('decompiler', () => { } }; + const Bar = React.createClass({ + propTypes: { + baz: React.PropTypes.number + }, + + getDefaultProps() { + return { baz: 456 }; + }, + + render() { + return Bar; + } + }); + + const Baz = React.createClass({ + propTypes: { + name: React.PropTypes.string, + greeting: React.PropTypes.string + }, + + getDefaultProps() { + return { baz: 456, greeting: "hello" }; + }, + + render() { + return {this.props.name}; + } + }); + it('stringify a simple component', () => { let component =
; @@ -30,6 +59,14 @@ describe('decompiler', () => { expect(decompile(component)).toBe(''); }); + it('stringify should strip out props that have default values', () => { + const opts = {skipPropsWithNonDefaultValues: true}; + expect(decompile(