├── .editorconfig
├── .eslintrc
├── .gitignore
├── .travis.yml
├── README.md
├── dist
├── react-stampit-with-addons.js
├── react-stampit-with-addons.min.js
├── react-stampit.js
└── react-stampit.min.js
├── docs
├── advanced.md
├── api.md
└── composition.md
├── package.json
├── src
├── addons.js
├── index.js
└── utils
│ ├── cache.js
│ ├── compose.js
│ ├── decorator.js
│ └── index.js
├── test
├── advanced.js
├── basics.js
├── compose.js
├── methods.js
├── state.js
├── statics.js
├── test.js
└── wrapMethods.js
└── webpack.config.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 |
4 | "env": {
5 | "node": true
6 | },
7 |
8 | "rules": {
9 | "brace-style": 2,
10 | "comma-dangle": [2, "always-multiline"],
11 | "consistent-return": 2,
12 | "curly": [2, "multi-line"],
13 | "dot-notation": 2,
14 | "eol-last": 2,
15 | "indent": [2, 2, {"SwitchCase": 1, "VariableDeclarator": 2}],
16 | "no-else-return": 2,
17 | "no-eq-null": 2,
18 | "no-floating-decimal": 2,
19 | "no-param-reassign": 2,
20 | "no-self-compare": 2,
21 | "no-shadow": 0,
22 | "no-throw-literal": 2,
23 | "no-underscore-dangle": 0,
24 | "object-shorthand": 2,
25 | "quotes": [2, "single"],
26 | "space-after-keywords": 2,
27 | "strict": 0,
28 | "vars-on-top": 2,
29 | "wrap-iife": 2,
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | lib
2 | node_modules
3 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | node_js:
4 | - "node"
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/stampit-org/react-stampit)
2 |
3 | # react-stampit
4 |
5 | > A specialized [stampit](https://github.com/stampit-org/stampit) factory for [React](https://github.com/facebook/react).
6 |
7 | Create React components in a way analogous to `React.createClass`, but powered by a [subset](docs/api.md#api-differences) of the [stampit](https://github.com/stampit-org/stampit) API.
8 |
9 | **This library has been replaced by [react-stamp](https://github.com/stampit-org/react-stamp).**
10 |
11 | ## Install
12 |
13 | react-stampit can be [installed via npm](https://www.npmjs.com/package/react-stampit)
14 |
15 | ```
16 | npm install react-stampit
17 | ```
18 |
19 | or by [downloading the latest release](https://github.com/stampit-org/react-stampit/releases).
20 |
21 | ## What is this
22 |
23 | This library is the result of wondering about what other ways a React component could be represented. [Stamps](https://github.com/stampit-org/stampit#what-is-a-stamp) are a cool concept, and more importantly have proven to be a great alternative to `React.createClass` and the ES2015 `class` due to their flexibility and use of multiple kinds of prototypal inheritance.
24 |
25 | react-stampit has an API similar to `React.createClass`. The factory accepts two parameters, the React library and a description object.
26 |
27 | ```js
28 | stampit(React, {
29 | init: [],
30 | state: {},
31 | statics: {},
32 |
33 | // convenience props for React statics
34 | contextTypes: {},
35 | childContextTypes: {}.
36 | propTypes: {},
37 | defaultProps: {},
38 |
39 | // ...methods
40 | });
41 | ```
42 |
43 | The best part about [stamps](https://github.com/stampit-org/stampit#what-is-a-stamp) is their composability. What this means is that `n` number of stamps can be combined into a new stamp which inherits each passed stamp's behavior. This is perfect for React, since `class` is being pushed as the new norm and does not provide an idiomatic way to use mixins. (classical inheritance :disappointed:). While stamp composability on the surface is not idiomatic, the conventions used underneath are; it is these conventions that provide a limitless way to extend any React component.
44 |
45 | __mixin1.jsx__
46 |
47 | ```js
48 | export default {
49 | componentWillMount() {
50 | this.state.mixin1 = true;
51 | },
52 | };
53 | ```
54 |
55 | __mixin2.jsx__
56 |
57 | ```js
58 | export default {
59 | componentWillMount() {
60 | this.state.mixin2 = true;
61 | },
62 | };
63 | ```
64 |
65 | __component.jsx__
66 |
67 | ```js
68 | import stampit from 'react-stampit';
69 | import * as cache from 'react-stampit/utils/cache';
70 |
71 | const id = cache.uniqueId();
72 |
73 | export default React => {
74 | return cache.find(id) || cache.save(
75 | stampit(React, {
76 | state: {
77 | comp: false,
78 | mixin1: false,
79 | mixin2: false,
80 | },
81 |
82 | _onClick() {
83 | return this.state;
84 | },
85 |
86 | componentWillMount() {
87 | this.state.comp = true;
88 | },
89 |
90 | render() {
91 | return this._onClick()} />;
92 | },
93 | }), id
94 | );
95 | };
96 | ```
97 |
98 | __app.jsx__
99 |
100 | ```js
101 | import React from 'react';
102 |
103 | import componentFactory from './component';
104 | import mixin1 from './mixin1';
105 | import mixin2 from './mixin2';
106 |
107 | const Component = componentFactory(React).compose(mixin1, mixin2);
108 |
109 | /**
110 | * Do things
111 | */
112 | ```
113 |
114 | ```js
115 | shallowRenderer.render();
116 | const button = shallowRenderer.getRenderOutput();
117 |
118 | assert.deepEqual(
119 | button.props.onClick(), { comp: true, mixin1: true, mixin2: true },
120 | 'should return component state with all truthy props'
121 | );
122 | >> ok
123 | ```
124 |
125 | You may have noticed several interesting behaviors.
126 |
127 | * component is a factory
128 |
129 | This design pattern is optional, but recommended. Component factories are react-stampit's solution for avoiding the often hard to debug problems created by multiple instances of React. Read more about that [here](https://medium.com/@dan_abramov/two-weird-tricks-that-fix-react-7cf9bbdef375). By injecting the React library, we are able to guarantee the version and instance of React that a component will receive.
130 |
131 | * caching
132 |
133 | This goes hand in hand with designing components as factories. Node.js's internal caching will not work as expected for component factories, react-stampit's cache utility can be used as a replacement.
134 |
135 | * no autobinding
136 |
137 | Event handlers require explicit binding. No magic. This can be done using `.bind` or through lexical binding with ES2015 [arrow functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) as shown in the example.
138 | * no `call super`
139 |
140 | React methods are wrapped during composition, providing functional inheritance. Sweet.
141 | * mixins are POJOs
142 |
143 | This is shorthand syntax for:
144 | ```js
145 | stampit(null, {
146 | // stuff
147 | });
148 | ```
149 |
150 | If you feel limited by `class`, or want a fresh take on `React.createClass`, maybe give react-stampit a try and learn more about what [stampit](https://github.com/stampit-org/stampit) is all about. And please report any issues you encounter!
151 |
152 | ## Docs
153 | * [API](docs/api.md)
154 | * [Composition logic](docs/composition.md)
155 | * [Advanced use cases](docs/advanced.md)
156 |
157 | ## Examples
158 | * [react-hello](https://github.com/stampit-org/react-hello)
159 |
160 | ## Pending Issues
161 | * [x] [childContextTypes](https://github.com/facebook/react/pull/3940)
162 | * [x] [component testing](https://github.com/facebook/react/pull/3941)
163 |
164 | ## License
165 | [](http://troutowicz.mit-license.org)
166 |
--------------------------------------------------------------------------------
/dist/react-stampit-with-addons.min.js:
--------------------------------------------------------------------------------
1 | !function(t,r){"object"==typeof exports&&"object"==typeof module?module.exports=r():"function"==typeof define&&define.amd?define(r):"object"==typeof exports?exports.stampit=r():t.stampit=r()}(this,function(){return function(t){function r(n){if(e[n])return e[n].exports;var o=e[n]={exports:{},id:n,loaded:!1};return t[n].call(o.exports,o,o.exports,r),o.loaded=!0,o.exports}var e={};return r.m=t,r.c=e,r.p="",r(0)}([function(t,r,e){t.exports=e(101)},function(t,r,e){"use strict";var n=e(12),o=e(7),u=e(3),i="[object Array]",c=Object.prototype,a=c.toString,s=n(Array,"isArray"),f=s||function(t){return u(t)&&o(t.length)&&a.call(t)==i};t.exports=f},function(t,r){"use strict";function e(t){var r=typeof t;return!!t&&("object"==r||"function"==r)}t.exports=e},function(t,r){"use strict";function e(t){return!!t&&"object"==typeof t}t.exports=e},function(t,r,e){"use strict";function n(t){return o(t)?t:Object(t)}var o=e(2);t.exports=n},function(t,r,e){"use strict";function n(t,r,e){if("function"!=typeof t)return o;if(void 0===r)return t;switch(e){case 1:return function(e){return t.call(r,e)};case 3:return function(e,n,o){return t.call(r,e,n,o)};case 4:return function(e,n,o,u){return t.call(r,e,n,o,u)};case 5:return function(e,n,o,u,i){return t.call(r,e,n,o,u,i)}}return function(){return t.apply(r,arguments)}}var o=e(42);t.exports=n},function(t,r,e){"use strict";function n(t){return null!=t&&u(o(t))}var o=e(36),u=e(7);t.exports=n},function(t,r){"use strict";function e(t){return"number"==typeof t&&t>-1&&t%1==0&&n>=t}var n=9007199254740991;t.exports=e},function(t,r,e){"use strict";function n(t){return u(t)&&o(t)&&c.call(t,"callee")&&!a.call(t,"callee")}var o=e(6),u=e(3),i=Object.prototype,c=i.hasOwnProperty,a=i.propertyIsEnumerable;t.exports=n},function(t,r,e){"use strict";var n=e(12),o=e(6),u=e(2),i=e(81),c=n(Object,"keys"),a=c?function(t){var r=null==t?void 0:t.constructor;return"function"==typeof r&&r.prototype===t||"function"!=typeof t&&o(t)?i(t):u(t)?c(t):[]}:i;t.exports=a},function(t,r,e){"use strict";function n(t){if(null==t)return[];a(t)||(t=Object(t));var r=t.length;r=r&&c(r)&&(u(t)||o(t))&&r||0;for(var e=t.constructor,n=-1,s="function"==typeof e&&e.prototype===t,l=Array(r),p=r>0;++n-1&&t%1==0&&r>t}var n=/^\d+$/,o=9007199254740991;t.exports=e},function(t,r,e){"use strict";function n(t){return o(t)&&c.call(t)==u}var o=e(2),u="[object Function]",i=Object.prototype,c=i.toString;t.exports=n},function(t,r,e){"use strict";function n(t){return t&&t.__esModule?t:{"default":t}}function o(t){return"function"==typeof t&&"function"==typeof t.compose&&"object"==typeof t.fixed}function u(t){return delete t.create,delete t.init,delete t.methods,delete t.state,delete t.refs,delete t.props,delete t.enclose,delete t["static"],t}Object.defineProperty(r,"__esModule",{value:!0}),r.isStamp=o,r.stripStamp=u;var i=e(98),c=n(i),a=e(99),s=n(a);r.compose=c["default"],r.stamp=s["default"]},function(t,r){"use strict";function e(t,r){if("function"!=typeof t)throw new TypeError(n);return r=o(void 0===r?t.length-1:+r||0,0),function(){for(var e=arguments,n=-1,u=o(e.length-r,0),i=Array(u);++nn;)t=t[r[n++]];return n&&n==u?t:void 0}}var o=e(4);t.exports=n},function(t,r,e){"use strict";function n(t,r){var e=typeof t;if("string"==e&&c.test(t)||"number"==e)return!0;if(o(t))return!1;var n=!i.test(t);return n||null!=r&&t in u(r)}var o=e(1),u=e(4),i=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\n\\]|\\.)*?\1)\]/,c=/^\w*$/;t.exports=n},function(t,r,e){"use strict";function n(t){if(u(t))return t;var r=[];return o(t).replace(i,function(t,e,n,o){r.push(n?o.replace(c,"$1"):e||t)}),r}var o=e(44),u=e(1),i=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g,c=/\\(\\)?/g;t.exports=n},function(t,r,e){"use strict";function n(t){return u(t)&&o(t.length)&&!!U[E.call(t)]}var o=e(7),u=e(3),i="[object Arguments]",c="[object Array]",a="[object Boolean]",s="[object Date]",f="[object Error]",l="[object Function]",p="[object Map]",d="[object Number]",v="[object Object]",y="[object RegExp]",x="[object Set]",m="[object String]",h="[object WeakMap]",g="[object ArrayBuffer]",b="[object Float32Array]",j="[object Float64Array]",_="[object Int8Array]",A="[object Int16Array]",O="[object Int32Array]",w="[object Uint8Array]",M="[object Uint8ClampedArray]",S="[object Uint16Array]",P="[object Uint32Array]",U={};U[b]=U[j]=U[_]=U[A]=U[O]=U[w]=U[M]=U[S]=U[P]=!0,U[i]=U[c]=U[g]=U[a]=U[s]=U[f]=U[l]=U[p]=U[d]=U[v]=U[y]=U[x]=U[m]=U[h]=!1;var C=Object.prototype,E=C.toString;t.exports=n},function(t,r,e){"use strict";var n=e(49),o=e(28),u=e(35),i=u(function(t,r,e){return e?n(t,r,e):o(t,r)});t.exports=i},function(t,r,e){"use strict";function n(t){return t&&t.__esModule?t:{"default":t}}function o(t){return t&&g["default"](t.then)}function u(){for(var t=[],r=arguments.length,e=Array(r),n=0;r>n;n++)e[n]=arguments[n];return g["default"](e[0])?m["default"](e,function(r){g["default"](r)&&t.push(r)}):j["default"](e[0])&&m["default"](e,function(r){m["default"](r,function(r){g["default"](r)&&t.push(r)})}),t}function i(t){for(var r=arguments.length,e=Array(r>1?r-1:0),n=1;r>n;n++)e[n-1]=arguments[n];return _.mixinFunctions.apply(void 0,[t.methods].concat(e))}function c(t){for(var r=arguments.length,e=Array(r>1?r-1:0),n=1;r>n;n++)e[n-1]=arguments[n];return t.refs=t.state=_.mixin.apply(void 0,[t.refs].concat(e)),t.refs}function a(t){for(var r=arguments.length,e=Array(r>1?r-1:0),n=1;r>n;n++)e[n-1]=arguments[n];var o=u.apply(void 0,e);return t.init=t.enclose=t.init.concat(o),t.init}function s(t){for(var r=arguments.length,e=Array(r>1?r-1:0),n=1;r>n;n++)e[n-1]=arguments[n];return _.merge.apply(void 0,[t.props].concat(e))}function f(t){for(var r=arguments.length,e=Array(r>1?r-1:0),n=1;r>n;n++)e[n-1]=arguments[n];return _.mixin.apply(void 0,[t["static"]].concat(e))}function l(t,r){for(var e=O(t),n=arguments.length,o=Array(n>2?n-2:0),u=2;n>u;u++)o[u-2]=arguments[u];return r.apply(void 0,[e.fixed].concat(o)),e}function p(){for(var t=O(),r=arguments.length,e=Array(r),n=0;r>n;n++)e[n]=arguments[n];return m["default"](e,function(r){r&&r.fixed&&(i(t.fixed,r.fixed.methods),c(t.fixed,r.fixed.refs||r.fixed.state),a(t.fixed,r.fixed.init||r.fixed.enclose),s(t.fixed,r.fixed.props),f(t.fixed,r.fixed["static"]))}),_.mixin(t,t.fixed["static"])}function d(t){return g["default"](t)&&g["default"](t.methods)&&(g["default"](t.refs)||g["default"](t.state))&&(g["default"](t.init)||g["default"](t.enclose))&&g["default"](t.props)&&g["default"](t["static"])&&j["default"](t.fixed)}function v(t){var r=O();return r.fixed.refs=r.fixed.state=_.mergeChainNonFunctions(r.fixed.refs,t.prototype),_.mixin(r,_.mixin(r.fixed["static"],t)),_.mixinChainFunctions(r.fixed.methods,t.prototype),a(r.fixed,function(r){var e=r.instance,n=r.args;return t.apply(e,n)}),r}function y(t){for(var r=O(),e=arguments.length,n=Array(e>1?e-1:0),o=1;e>o;o++)n[o-1]=arguments[o];return t.apply(void 0,[r.fixed].concat(n)),r}Object.defineProperty(r,"__esModule",{value:!0});var x=e(26),m=n(x),h=e(14),g=n(h),b=e(2),j=n(b),_=e(96),A=Object.create,O=function(t){var r={methods:{},refs:{},init:[],props:{},"static":{}};r.state=r.refs,r.enclose=r.init,t&&(i(r,t.methods),c(r,t.refs),a(r,t.init),s(r,t.props),f(r,t["static"]));var e=function(t){for(var n=arguments.length,u=Array(n>1?n-1:0),i=1;n>i;i++)u[i-1]=arguments[i];var c=_.mixin(A(r.methods),r.refs,t);_.mergeUnique(c,r.props);var a=null;return r.init.length>0&&m["default"](r.init,function(t){if(g["default"](t))if(a)a=a.then(function(r){c=r||c;var n=t.call(c,{args:u,instance:c,stamp:e});return n?o(n)?n:c=n:c});else{var r=t.call(c,{args:u,instance:c,stamp:e});if(!r)return;if(!o(r))return void(c=r);a=r}}),a?a.then(function(t){return t||c}):c},n=l.bind(null,r,c),u=l.bind(null,r,a);return _.mixin(e,{create:e,fixed:r,methods:l.bind(null,r,i),refs:n,state:n,init:u,enclose:u,props:l.bind(null,r,s),"static":function(){for(var t=arguments.length,r=Array(t),n=0;t>n;n++)r[n]=arguments[n];var o=l.apply(void 0,[e.fixed,f].concat(r));return _.mixin(o,o.fixed["static"])},compose:function(){for(var t=arguments.length,r=Array(t),n=0;t>n;n++)r[n]=arguments[n];return p.apply(void 0,[e].concat(r))}},r["static"])};r["default"]=_.mixin(O,{methods:y.bind(null,i),refs:y.bind(null,c),init:y.bind(null,a),props:y.bind(null,s),"static":function(){for(var t=arguments.length,r=Array(t),e=0;t>e;e++)r[e]=arguments[e];var n=y.apply(void 0,[f].concat(r));return _.mixin(n,n.fixed["static"])},compose:p,mixin:_.mixin,extend:_.mixin,mixIn:_.mixin,assign:_.mixin,isStamp:d,convertConstructor:v}),t.exports=r["default"]},function(t,r){"use strict";function e(t){var r=t?t.length:0;return r?t[r-1]:void 0}t.exports=e},function(t,r,e){"use strict";var n=e(17),o=e(53),u=e(68),i=u(n,o);t.exports=i},function(t,r){"use strict";function e(t,r){var e=-1,n=t.length;for(r||(r=Array(n));++er&&(r=-r>o?0:o+r),e=void 0===e||e>o?o:+e||0,0>e&&(e+=o),o=r>e?0:e-r>>>0,r>>>=0;for(var u=Array(o);++n2?e[i-2]:void 0,a=i>2?e[2]:void 0,s=i>1?e[i-1]:void 0;for("function"==typeof c?(c=o(c,s,5),i-=2):(c="function"==typeof s?s:void 0,i-=c?1:0),a&&u(e[0],e[1],a)&&(c=3>i?void 0:c,i=1);++n=0}),e=e.init(o).refs(n).methods(u)["static"](c),e.compose=m.compose,m.stripStamp(e))}Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=o;var u=e(23),i=n(u),c=e(40),a=n(c),s=e(83),f=n(s),l=e(41),p=n(l),d=e(94),v=n(d),y=e(24),x=n(y),m=e(15);t.exports=r["default"]},function(t,r){"use strict";function e(t){return null==t?"":t+""}t.exports=e},function(t,r,e){(function(r){"use strict";function n(t){var r=t?t.length:0;for(this.data={hash:c(null),set:new i};r--;)this.push(t[r])}var o=e(64),u=e(12),i=u(r,"Set"),c=u(Object,"create");n.prototype.push=o,t.exports=n}).call(r,function(){return this}())},function(t,r){"use strict";function e(t,r){for(var e=-1,n=t.length,o=Array(n);++e=c?i(r):null,p=r.length;l&&(s=u,f=!1,r=l);t:for(;++as))return!1;for(;++a1?n-1:0),u=1;n>u;u++)o[u-1]=arguments[u];if(v["default"](r)||!t.noOverwrite&&!p["default"](r))return o.length>1?t._innerMixer.apply(t,[{}].concat(o)):f["default"](o[0]);if(t.noOverwrite&&(!p["default"](r)||!p["default"](o[0])))return r;var c=t.chain?a["default"]:i["default"];return o.forEach(function(t){c(t,e)}),r}}Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=o;var u=e(90),i=n(u),c=e(89),a=n(c),s=e(82),f=n(s),l=e(2),p=n(l),d=e(87),v=n(d);t.exports=r["default"]},function(t,r,e){"use strict";function n(t){return t&&t.__esModule?t:{"default":t}}function o(t,r){var e=void 0;return e=v["default"](r,function(r,e){if("function"==typeof r)switch(A[e]){case"many":return function(){t[e]&&t[e].apply(this,arguments),r.apply(this,arguments)};case"many_merged_dupe":return function(){var n=t[e]&&t[e].apply(this,arguments),o=r.apply(this,arguments);return n?a["default"](n,o,_):o};case"once":default:if(!t[e])return r;throw new TypeError("Cannot mixin `"+e+"` because it has a unique constraint.")}}),a["default"]({},t,e)}function u(t,r){var e=a["default"]({},t);return f["default"](r,function(t,r){"many_merged_dupe"===A[r]?e[r]=a["default"]({},e[r],t,_):"many_merged"===A[r]?e[r]=a["default"]({},e[r],t):e[r]=t}),e}function i(){for(var t=h["default"](),r={state:{}},e=[],n={},c={},s=arguments.length,l=Array(s),d=0;s>d;d++)l[d]=arguments[d];return j.isStamp(this)&&l.push(this),f["default"](l,function(i){i=j.isStamp(i)?i:b["default"](null,i),e=e.concat(i.fixed.init),n=o(n,i.fixed.methods),c=u(c,x["default"](i,function(r,e){return p["default"](t,e)})),i.fixed.refs&&i.fixed.refs.state&&a["default"](r.state,i.fixed.refs.state,_)}),t=t.init(e).refs(r).methods(n)["static"](c),t.compose=i,j.stripStamp(t)}Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=i;var c=e(23),a=n(c),s=e(26),f=n(s),l=e(40),p=n(l),d=e(91),v=n(d),y=e(41),x=n(y),m=e(24),h=n(m),g=e(43),b=n(g),j=e(15),_=function(t,r,e,n){if(n[e])throw new TypeError("Cannot mixin key `"+e+"` because it is provided by multiple sources.");return r},A={propTypes:"many_merged_dupe",defaultProps:"many_merged_dupe",contextTypes:"many_merged",childContextTypes:"many_merged",getChildContext:"many_merged_dupe",render:"once",componentWillMount:"many",componentDidMount:"many",componentWillReceiveProps:"many",shouldComponentUpdate:"once",componentWillUpdate:"many",componentDidUpdate:"many",componentWillUnmount:"many"};t.exports=r["default"]},function(t,r,e){"use strict";function n(t){return t&&t.__esModule?t:{"default":t}}function o(t){var r=Object.getOwnPropertyNames(t),e=Object.keys(t),n={};return r.forEach(function(r){var o=e.indexOf(r);-1===o&&"constructor"!==r&&(n[r]=t[r])}),n}function u(t){var r=Object.keys(t),e={};return r.forEach(function(r){e[r]=t[r]}),e}function i(t){var r=function(){f["default"](this,new t)},e=a["default"]({},Object.getPrototypeOf(t).prototype,o(t.prototype)),n=p["default"].init(r).methods(e)["static"](u(t));return n.compose=d.compose,d.stripStamp(n)}Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=i;var c=e(23),a=n(c),s=e(92),f=n(s),l=e(24),p=n(l),d=e(15);t.exports=r["default"]},function(t,r,e){"use strict";function n(t){var r=++u;return o(t)+r}var o=e(44),u=0;t.exports=n},function(t,r,e){"use strict";function n(t){if(t&&t.__esModule)return t;var r={};if(null!=t)for(var e in t)Object.prototype.hasOwnProperty.call(t,e)&&(r[e]=t[e]);return r["default"]=t,r}function o(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(r,"__esModule",{value:!0});var u=e(43),i=o(u),c=e(102),a=n(c),s=e(15);i["default"].addons={cache:a,compose:s.compose,isStamp:s.isStamp,stamp:s.stamp},r["default"]=i["default"],t.exports=r["default"]},function(t,r,e){"use strict";function n(t){return t&&t.__esModule?t:{"default":t}}function o(t){return a[t]}function u(t,r){return r&&(a[r]=t),t}Object.defineProperty(r,"__esModule",{value:!0}),r.find=o,r.save=u;var i=e(100),c=n(i),a={};r.uniqueId=c["default"]}])});
--------------------------------------------------------------------------------
/dist/react-stampit.min.js:
--------------------------------------------------------------------------------
1 | !function(t,r){"object"==typeof exports&&"object"==typeof module?module.exports=r():"function"==typeof define&&define.amd?define(r):"object"==typeof exports?exports.stampit=r():t.stampit=r()}(this,function(){return function(t){function r(n){if(e[n])return e[n].exports;var o=e[n]={exports:{},id:n,loaded:!1};return t[n].call(o.exports,o,o.exports,r),o.loaded=!0,o.exports}var e={};return r.m=t,r.c=e,r.p="",r(0)}([function(t,r,e){t.exports=e(43)},function(t,r,e){"use strict";var n=e(12),o=e(7),u=e(3),i="[object Array]",c=Object.prototype,a=c.toString,s=n(Array,"isArray"),f=s||function(t){return u(t)&&o(t.length)&&a.call(t)==i};t.exports=f},function(t,r){"use strict";function e(t){var r=typeof t;return!!t&&("object"==r||"function"==r)}t.exports=e},function(t,r){"use strict";function e(t){return!!t&&"object"==typeof t}t.exports=e},function(t,r,e){"use strict";function n(t){return o(t)?t:Object(t)}var o=e(2);t.exports=n},function(t,r,e){"use strict";function n(t,r,e){if("function"!=typeof t)return o;if(void 0===r)return t;switch(e){case 1:return function(e){return t.call(r,e)};case 3:return function(e,n,o){return t.call(r,e,n,o)};case 4:return function(e,n,o,u){return t.call(r,e,n,o,u)};case 5:return function(e,n,o,u,i){return t.call(r,e,n,o,u,i)}}return function(){return t.apply(r,arguments)}}var o=e(42);t.exports=n},function(t,r,e){"use strict";function n(t){return null!=t&&u(o(t))}var o=e(36),u=e(7);t.exports=n},function(t,r){"use strict";function e(t){return"number"==typeof t&&t>-1&&t%1==0&&n>=t}var n=9007199254740991;t.exports=e},function(t,r,e){"use strict";function n(t){return u(t)&&o(t)&&c.call(t,"callee")&&!a.call(t,"callee")}var o=e(6),u=e(3),i=Object.prototype,c=i.hasOwnProperty,a=i.propertyIsEnumerable;t.exports=n},function(t,r,e){"use strict";var n=e(12),o=e(6),u=e(2),i=e(81),c=n(Object,"keys"),a=c?function(t){var r=null==t?void 0:t.constructor;return"function"==typeof r&&r.prototype===t||"function"!=typeof t&&o(t)?i(t):u(t)?c(t):[]}:i;t.exports=a},function(t,r,e){"use strict";function n(t){if(null==t)return[];a(t)||(t=Object(t));var r=t.length;r=r&&c(r)&&(u(t)||o(t))&&r||0;for(var e=t.constructor,n=-1,s="function"==typeof e&&e.prototype===t,l=Array(r),p=r>0;++n-1&&t%1==0&&r>t}var n=/^\d+$/,o=9007199254740991;t.exports=e},function(t,r,e){"use strict";function n(t){return o(t)&&c.call(t)==u}var o=e(2),u="[object Function]",i=Object.prototype,c=i.toString;t.exports=n},function(t,r,e){"use strict";function n(t){return t&&t.__esModule?t:{"default":t}}function o(t){return"function"==typeof t&&"function"==typeof t.compose&&"object"==typeof t.fixed}function u(t){return delete t.create,delete t.init,delete t.methods,delete t.state,delete t.refs,delete t.props,delete t.enclose,delete t["static"],t}Object.defineProperty(r,"__esModule",{value:!0}),r.isStamp=o,r.stripStamp=u;var i=e(98),c=n(i),a=e(99),s=n(a);r.compose=c["default"],r.stamp=s["default"]},function(t,r){"use strict";function e(t,r){if("function"!=typeof t)throw new TypeError(n);return r=o(void 0===r?t.length-1:+r||0,0),function(){for(var e=arguments,n=-1,u=o(e.length-r,0),i=Array(u);++nn;)t=t[r[n++]];return n&&n==u?t:void 0}}var o=e(4);t.exports=n},function(t,r,e){"use strict";function n(t,r){var e=typeof t;if("string"==e&&c.test(t)||"number"==e)return!0;if(o(t))return!1;var n=!i.test(t);return n||null!=r&&t in u(r)}var o=e(1),u=e(4),i=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\n\\]|\\.)*?\1)\]/,c=/^\w*$/;t.exports=n},function(t,r,e){"use strict";function n(t){if(u(t))return t;var r=[];return o(t).replace(i,function(t,e,n,o){r.push(n?o.replace(c,"$1"):e||t)}),r}var o=e(44),u=e(1),i=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g,c=/\\(\\)?/g;t.exports=n},function(t,r,e){"use strict";function n(t){return u(t)&&o(t.length)&&!!U[E.call(t)]}var o=e(7),u=e(3),i="[object Arguments]",c="[object Array]",a="[object Boolean]",s="[object Date]",f="[object Error]",l="[object Function]",p="[object Map]",d="[object Number]",v="[object Object]",y="[object RegExp]",x="[object Set]",m="[object String]",h="[object WeakMap]",g="[object ArrayBuffer]",b="[object Float32Array]",j="[object Float64Array]",A="[object Int8Array]",_="[object Int16Array]",O="[object Int32Array]",w="[object Uint8Array]",S="[object Uint8ClampedArray]",M="[object Uint16Array]",P="[object Uint32Array]",U={};U[b]=U[j]=U[A]=U[_]=U[O]=U[w]=U[S]=U[M]=U[P]=!0,U[i]=U[c]=U[g]=U[a]=U[s]=U[f]=U[l]=U[p]=U[d]=U[v]=U[y]=U[x]=U[m]=U[h]=!1;var C=Object.prototype,E=C.toString;t.exports=n},function(t,r,e){"use strict";var n=e(49),o=e(28),u=e(35),i=u(function(t,r,e){return e?n(t,r,e):o(t,r)});t.exports=i},function(t,r,e){"use strict";function n(t){return t&&t.__esModule?t:{"default":t}}function o(t){return t&&g["default"](t.then)}function u(){for(var t=[],r=arguments.length,e=Array(r),n=0;r>n;n++)e[n]=arguments[n];return g["default"](e[0])?m["default"](e,function(r){g["default"](r)&&t.push(r)}):j["default"](e[0])&&m["default"](e,function(r){m["default"](r,function(r){g["default"](r)&&t.push(r)})}),t}function i(t){for(var r=arguments.length,e=Array(r>1?r-1:0),n=1;r>n;n++)e[n-1]=arguments[n];return A.mixinFunctions.apply(void 0,[t.methods].concat(e))}function c(t){for(var r=arguments.length,e=Array(r>1?r-1:0),n=1;r>n;n++)e[n-1]=arguments[n];return t.refs=t.state=A.mixin.apply(void 0,[t.refs].concat(e)),t.refs}function a(t){for(var r=arguments.length,e=Array(r>1?r-1:0),n=1;r>n;n++)e[n-1]=arguments[n];var o=u.apply(void 0,e);return t.init=t.enclose=t.init.concat(o),t.init}function s(t){for(var r=arguments.length,e=Array(r>1?r-1:0),n=1;r>n;n++)e[n-1]=arguments[n];return A.merge.apply(void 0,[t.props].concat(e))}function f(t){for(var r=arguments.length,e=Array(r>1?r-1:0),n=1;r>n;n++)e[n-1]=arguments[n];return A.mixin.apply(void 0,[t["static"]].concat(e))}function l(t,r){for(var e=O(t),n=arguments.length,o=Array(n>2?n-2:0),u=2;n>u;u++)o[u-2]=arguments[u];return r.apply(void 0,[e.fixed].concat(o)),e}function p(){for(var t=O(),r=arguments.length,e=Array(r),n=0;r>n;n++)e[n]=arguments[n];return m["default"](e,function(r){r&&r.fixed&&(i(t.fixed,r.fixed.methods),c(t.fixed,r.fixed.refs||r.fixed.state),a(t.fixed,r.fixed.init||r.fixed.enclose),s(t.fixed,r.fixed.props),f(t.fixed,r.fixed["static"]))}),A.mixin(t,t.fixed["static"])}function d(t){return g["default"](t)&&g["default"](t.methods)&&(g["default"](t.refs)||g["default"](t.state))&&(g["default"](t.init)||g["default"](t.enclose))&&g["default"](t.props)&&g["default"](t["static"])&&j["default"](t.fixed)}function v(t){var r=O();return r.fixed.refs=r.fixed.state=A.mergeChainNonFunctions(r.fixed.refs,t.prototype),A.mixin(r,A.mixin(r.fixed["static"],t)),A.mixinChainFunctions(r.fixed.methods,t.prototype),a(r.fixed,function(r){var e=r.instance,n=r.args;return t.apply(e,n)}),r}function y(t){for(var r=O(),e=arguments.length,n=Array(e>1?e-1:0),o=1;e>o;o++)n[o-1]=arguments[o];return t.apply(void 0,[r.fixed].concat(n)),r}Object.defineProperty(r,"__esModule",{value:!0});var x=e(26),m=n(x),h=e(14),g=n(h),b=e(2),j=n(b),A=e(96),_=Object.create,O=function(t){var r={methods:{},refs:{},init:[],props:{},"static":{}};r.state=r.refs,r.enclose=r.init,t&&(i(r,t.methods),c(r,t.refs),a(r,t.init),s(r,t.props),f(r,t["static"]));var e=function(t){for(var n=arguments.length,u=Array(n>1?n-1:0),i=1;n>i;i++)u[i-1]=arguments[i];var c=A.mixin(_(r.methods),r.refs,t);A.mergeUnique(c,r.props);var a=null;return r.init.length>0&&m["default"](r.init,function(t){if(g["default"](t))if(a)a=a.then(function(r){c=r||c;var n=t.call(c,{args:u,instance:c,stamp:e});return n?o(n)?n:c=n:c});else{var r=t.call(c,{args:u,instance:c,stamp:e});if(!r)return;if(!o(r))return void(c=r);a=r}}),a?a.then(function(t){return t||c}):c},n=l.bind(null,r,c),u=l.bind(null,r,a);return A.mixin(e,{create:e,fixed:r,methods:l.bind(null,r,i),refs:n,state:n,init:u,enclose:u,props:l.bind(null,r,s),"static":function(){for(var t=arguments.length,r=Array(t),n=0;t>n;n++)r[n]=arguments[n];var o=l.apply(void 0,[e.fixed,f].concat(r));return A.mixin(o,o.fixed["static"])},compose:function(){for(var t=arguments.length,r=Array(t),n=0;t>n;n++)r[n]=arguments[n];return p.apply(void 0,[e].concat(r))}},r["static"])};r["default"]=A.mixin(O,{methods:y.bind(null,i),refs:y.bind(null,c),init:y.bind(null,a),props:y.bind(null,s),"static":function(){for(var t=arguments.length,r=Array(t),e=0;t>e;e++)r[e]=arguments[e];var n=y.apply(void 0,[f].concat(r));return A.mixin(n,n.fixed["static"])},compose:p,mixin:A.mixin,extend:A.mixin,mixIn:A.mixin,assign:A.mixin,isStamp:d,convertConstructor:v}),t.exports=r["default"]},function(t,r){"use strict";function e(t){var r=t?t.length:0;return r?t[r-1]:void 0}t.exports=e},function(t,r,e){"use strict";var n=e(17),o=e(53),u=e(68),i=u(n,o);t.exports=i},function(t,r){"use strict";function e(t,r){var e=-1,n=t.length;for(r||(r=Array(n));++er&&(r=-r>o?0:o+r),e=void 0===e||e>o?o:+e||0,0>e&&(e+=o),o=r>e?0:e-r>>>0,r>>>=0;for(var u=Array(o);++n2?e[i-2]:void 0,a=i>2?e[2]:void 0,s=i>1?e[i-1]:void 0;for("function"==typeof c?(c=o(c,s,5),i-=2):(c="function"==typeof s?s:void 0,i-=c?1:0),a&&u(e[0],e[1],a)&&(c=3>i?void 0:c,i=1);++n=0}),e=e.init(o).refs(n).methods(u)["static"](c),e.compose=m.compose,m.stripStamp(e))}Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=o;var u=e(23),i=n(u),c=e(40),a=n(c),s=e(83),f=n(s),l=e(41),p=n(l),d=e(94),v=n(d),y=e(24),x=n(y),m=e(15);t.exports=r["default"]},function(t,r){"use strict";function e(t){return null==t?"":t+""}t.exports=e},function(t,r,e){(function(r){"use strict";function n(t){var r=t?t.length:0;for(this.data={hash:c(null),set:new i};r--;)this.push(t[r])}var o=e(64),u=e(12),i=u(r,"Set"),c=u(Object,"create");n.prototype.push=o,t.exports=n}).call(r,function(){return this}())},function(t,r){"use strict";function e(t,r){for(var e=-1,n=t.length,o=Array(n);++e=c?i(r):null,p=r.length;l&&(s=u,f=!1,r=l);t:for(;++as))return!1;for(;++a1?n-1:0),u=1;n>u;u++)o[u-1]=arguments[u];if(v["default"](r)||!t.noOverwrite&&!p["default"](r))return o.length>1?t._innerMixer.apply(t,[{}].concat(o)):f["default"](o[0]);if(t.noOverwrite&&(!p["default"](r)||!p["default"](o[0])))return r;var c=t.chain?a["default"]:i["default"];return o.forEach(function(t){c(t,e)}),r}}Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=o;var u=e(90),i=n(u),c=e(89),a=n(c),s=e(82),f=n(s),l=e(2),p=n(l),d=e(87),v=n(d);t.exports=r["default"]},function(t,r,e){"use strict";function n(t){return t&&t.__esModule?t:{"default":t}}function o(t,r){var e=void 0;return e=v["default"](r,function(r,e){if("function"==typeof r)switch(_[e]){case"many":return function(){t[e]&&t[e].apply(this,arguments),r.apply(this,arguments)};case"many_merged_dupe":return function(){var n=t[e]&&t[e].apply(this,arguments),o=r.apply(this,arguments);return n?a["default"](n,o,A):o};case"once":default:if(!t[e])return r;throw new TypeError("Cannot mixin `"+e+"` because it has a unique constraint.")}}),a["default"]({},t,e)}function u(t,r){var e=a["default"]({},t);return f["default"](r,function(t,r){"many_merged_dupe"===_[r]?e[r]=a["default"]({},e[r],t,A):"many_merged"===_[r]?e[r]=a["default"]({},e[r],t):e[r]=t}),e}function i(){for(var t=h["default"](),r={state:{}},e=[],n={},c={},s=arguments.length,l=Array(s),d=0;s>d;d++)l[d]=arguments[d];return j.isStamp(this)&&l.push(this),f["default"](l,function(i){i=j.isStamp(i)?i:b["default"](null,i),e=e.concat(i.fixed.init),n=o(n,i.fixed.methods),c=u(c,x["default"](i,function(r,e){return p["default"](t,e)})),i.fixed.refs&&i.fixed.refs.state&&a["default"](r.state,i.fixed.refs.state,A)}),t=t.init(e).refs(r).methods(n)["static"](c),t.compose=i,j.stripStamp(t)}Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=i;var c=e(23),a=n(c),s=e(26),f=n(s),l=e(40),p=n(l),d=e(91),v=n(d),y=e(41),x=n(y),m=e(24),h=n(m),g=e(43),b=n(g),j=e(15),A=function(t,r,e,n){if(n[e])throw new TypeError("Cannot mixin key `"+e+"` because it is provided by multiple sources.");return r},_={propTypes:"many_merged_dupe",defaultProps:"many_merged_dupe",contextTypes:"many_merged",childContextTypes:"many_merged",getChildContext:"many_merged_dupe",render:"once",componentWillMount:"many",componentDidMount:"many",componentWillReceiveProps:"many",shouldComponentUpdate:"once",componentWillUpdate:"many",componentDidUpdate:"many",componentWillUnmount:"many"};t.exports=r["default"]},function(t,r,e){"use strict";function n(t){return t&&t.__esModule?t:{"default":t}}function o(t){var r=Object.getOwnPropertyNames(t),e=Object.keys(t),n={};return r.forEach(function(r){var o=e.indexOf(r);-1===o&&"constructor"!==r&&(n[r]=t[r])}),n}function u(t){var r=Object.keys(t),e={};return r.forEach(function(r){e[r]=t[r]}),e}function i(t){var r=function(){f["default"](this,new t)},e=a["default"]({},Object.getPrototypeOf(t).prototype,o(t.prototype)),n=p["default"].init(r).methods(e)["static"](u(t));return n.compose=d.compose,d.stripStamp(n)}Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=i;var c=e(23),a=n(c),s=e(92),f=n(s),l=e(24),p=n(l),d=e(15);t.exports=r["default"]}])});
--------------------------------------------------------------------------------
/docs/advanced.md:
--------------------------------------------------------------------------------
1 | ## Advanced use cases
2 |
3 | ### Class decorator
4 |
5 | For all those nice guys/gals that like `class` and just want some mixability. It is assumed that the component directly extends React.Component, anything else should be inherited via stamp composition.
6 |
7 | ```js
8 | import React from 'react/addons';
9 | import stamp from 'react-stampit/utils/decorator';
10 |
11 | @stamp
12 | class Component extends React.Component {
13 | constructor(props) {
14 | super(props);
15 |
16 | this.state = {
17 | foo: 'foo',
18 | };
19 | }
20 |
21 | render() {
22 | return null;
23 | }
24 | }
25 |
26 | Component.defaultProps = {
27 | foo: 'foo',
28 | };
29 |
30 | const mixin = {
31 | state: {
32 | bar: 'bar',
33 | },
34 |
35 | defaultProps: {
36 | bar: 'bar',
37 | },
38 | };
39 |
40 | Component = Component.compose(mixin);
41 | ```
42 |
43 | ```js
44 | assert.deepEqual(
45 | Component().state, { foo: 'foo', bar: 'bar' },
46 | 'should inherit mixin state'
47 | );
48 | >> ok
49 | assert.deepEqual(
50 | Component.defaultProps, { foo: 'foo', bar: 'bar' }
51 | 'should inherit `defaultProps` props'
52 | );
53 | >> ok
54 | ```
55 |
56 | __*Warning: Class decorators are a proposal for ES2016, they are not set in stone.*__
57 |
--------------------------------------------------------------------------------
/docs/api.md:
--------------------------------------------------------------------------------
1 | ## API
2 |
3 | ### stampit(React [,descObj])
4 |
5 | Return a factory function (called a stamp) that will produce new React components using the prototypes that are passed in or composed.
6 |
7 | * `@param {Object} React` The React library.
8 | * `@param {Object} descObj` A map of property names and values specialized for React.
9 | * `@return {Function} stamp` A factory to produce React components using the given properties.
10 | * `@return {Object} stamp.fixed` An object map containing the fixed prototypes.
11 | * `@return {Function} stamp.compose` Add mixin (stamp) to stamp. Chainable.
12 |
13 | ### stamp.compose([arg1] [,arg2] [,arg3...])
14 |
15 | Take one or more stamps produced from `react-stampit` or `stampit` and
16 | combine them with `this` to produce and return a new stamp.
17 |
18 | * `@param {...Function} stamp` One or more stamps.
19 | * `@return {Function}` A new stamp composed from `this` and arguments.
20 |
21 | ## Utility methods
22 |
23 | ### compose([arg1], [arg2] [,arg3...])
24 |
25 | Take two or more stamps produced from `react-stampit` or `stampit` and
26 | combine them to produce and return a new stamp.
27 |
28 | * `@param {...Function} stamp` Two or more stamps.
29 | * `@return {Function}` A new stamp composed from arguments.
30 |
31 | ### isStamp(obj)
32 |
33 | Take an object and return true if it's a stamp, false otherwise.
34 |
35 | ## API Differences
36 |
37 | react-stampit utitlizes a stamp description object made specifically for React components. Consider it a long lost relative of [stampit](https://github.com/stampit-org/stampit)'s stamp description object with nothing in common.
38 |
39 | react-stampit has also stripped all of stampit's static methods to enforce an API familiar to React users. Users are encouraged to utilize [React's lifecycle](https://facebook.github.io/react/docs/component-specs.html) and [component properties](#what-is-this) as replacements for these methods.
40 |
--------------------------------------------------------------------------------
/docs/composition.md:
--------------------------------------------------------------------------------
1 | ## Composition logic
2 |
3 | Stamp composition might be compared to `React.createClass`'s mixin feature. A significant difference is that you compose a stamp without referencing the stamps being inherited from inside the stamp's declaration.
4 |
5 |
6 | ### State
7 | Composed stamps inherit other stamps' state. State is merged, throwing on duplicate keys.
8 |
9 | ```js
10 | const mixin = {
11 | state: {
12 | foo: 'foo',
13 | bar: 'bar',
14 | },
15 | };
16 |
17 | const component = stampit(React).compose(mixin);
18 | ```
19 |
20 | ```js
21 | assert.deepEqual(
22 | component().state, { foo: 'foo', bar: 'bar },
23 | 'should inherit state'
24 | );
25 | >> ok
26 | ```
27 |
28 | __*`React.createClass` throws an Invariant Violation when duplicate keys are found within mixins. `react-stampit` will throw a TypeError.*__
29 |
30 | ### Statics
31 | Composed stamps inherit other stamps' statics. React statics are merged, throwing on duplicate keys for `propTypes` and `defaultProps`. Non-React statics override with last-in priority.
32 |
33 | ```js
34 | const mixin = {
35 | statics: {
36 | someStatic: {
37 | bar: 'bar',
38 | },
39 | },
40 |
41 | propTypes: {
42 | bar: React.PropTypes.string,
43 | },
44 | };
45 |
46 | const component = stampit(React, {
47 | statics: {
48 | someStatic: {
49 | foo: 'foo',
50 | },
51 | },
52 |
53 | propTypes: {
54 | foo: React.PropTypes.string,
55 | },
56 | }).compose(mixin);
57 | ```
58 |
59 | ```js
60 | assert.ok(
61 | component.propTypes.bar,
62 | 'should inherit bar propType'
63 | );
64 | >> ok
65 | assert.ok(
66 | component.propTypes.foo,
67 | 'should inherit foo propType'
68 | );
69 | >> ok
70 | assert.deepEqual(
71 | component.someStatic, { foo: 'foo' },
72 | 'should override non-React statics'
73 | );
74 | >> ok
75 | ```
76 |
77 | __*`React.createClass` throws an Invariant Violation when duplicate keys are found in `getDefaultProps` and `getInitialState`. `react-stampit` will throw a TypeError.*__
78 |
79 | ### Methods
80 | Composed stamps inherit other stamps' methods. A handful of React methods will be 'wrapped' executing with first-in priority. Any non-React methods or React methods with a unique constraint will throw on duplicates.
81 |
82 | __Wrapped__
83 |
84 | * componentWillMount
85 | * componentDidMount
86 | * componentWillReceiveProps
87 | * componentWillUpdate
88 | * componentDidUpdate
89 | * componentWillUnmount
90 | * getChildContext
91 |
92 | __Unique__
93 |
94 | * shouldComponentUpdate
95 | * render
96 |
97 | ```js
98 | const mixin = {
99 | componentDidMount() {
100 | this.state.mixin = true;
101 | },
102 | };
103 |
104 | const component = stampit(React, {
105 | state: {
106 | component: false,
107 | mixin: false,
108 | },
109 |
110 | componentDidMount() {
111 | this.state.component = true;
112 | }
113 | }).compose(mixin);
114 |
115 | const instance = component();
116 | instance.componentDidMount();
117 | ```
118 |
119 | ```js
120 | assert.deepEqual(
121 | instance.state, { component: true, mixin: true },
122 | 'should inherit functionality of mixable methods'
123 | );
124 | >> ok
125 | ```
126 | __*`React.createClass` throws an Invariant Violation when duplicate `shouldComponentUpdate` or `render` methods exist, `react-stampit` will throw a TypeError.*__
127 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-stampit",
3 | "version": "0.9.0",
4 | "description": "A specialized stampit factory for React.",
5 | "main": "lib",
6 | "files": [
7 | "dist",
8 | "lib"
9 | ],
10 | "scripts": {
11 | "build": "npm run clean && npm run transpile && npm run build-browser",
12 | "build-browser": "webpack && MINIFY=1 webpack -p",
13 | "clean": "rimraf lib",
14 | "lint": "eslint src test",
15 | "prepublish": "npm run test && npm run build",
16 | "test": "npm run lint && babel-node --stage 1 test/test.js",
17 | "transpile": "babel --stage 1 src --out-dir lib"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "https://github.com/stampit-org/react-stampit.git"
22 | },
23 | "keywords": [
24 | "composability",
25 | "mixins",
26 | "react",
27 | "stamp",
28 | "stampit"
29 | ],
30 | "author": "Tim Routowicz ",
31 | "license": "MIT",
32 | "bugs": {
33 | "url": "https://github.com/stampit-org/react-stampit/issues"
34 | },
35 | "homepage": "https://github.com/stampit-org/react-stampit",
36 | "dependencies": {
37 | "lodash": "^3.10.1",
38 | "stampit": "^2.1.0"
39 | },
40 | "devDependencies": {
41 | "babel": "^5.8.21",
42 | "babel-eslint": "^4.0.5",
43 | "babel-loader": "^5.3.2",
44 | "eslint": "^1.1.0",
45 | "node-libs-browser": "^0.5.2",
46 | "react": "^0.13.3",
47 | "rewire": "^2.3.4",
48 | "rimraf": "^2.4.2",
49 | "tape": "^4.1.0",
50 | "webpack": "^1.11.0"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/addons.js:
--------------------------------------------------------------------------------
1 | import stampit from './';
2 | import * as cache from './utils/cache';
3 | import { compose, isStamp, stamp } from './utils';
4 |
5 | stampit.addons = {
6 | cache,
7 | compose,
8 | isStamp,
9 | stamp,
10 | };
11 |
12 | export default stampit;
13 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import assign from 'lodash/object/assign';
2 | import has from 'lodash/object/has';
3 | import isEmpty from 'lodash/lang/isEmpty';
4 | import omit from 'lodash/object/omit';
5 | import pick from 'lodash/object/pick';
6 | import stampit from 'stampit';
7 |
8 | import { compose, stripStamp } from './utils';
9 |
10 | /**
11 | * Return a factory function (called a stamp) that will produce new
12 | * React components using the prototypes that are passed in or composed.
13 | *
14 | * @param {Object} React The React library.
15 | * @param {Object} [props] A map of property names and values specialized for React.
16 | * @return {Function} stamp A factory to produce React components using the given properties.
17 | * @return {Object} stamp.fixed An object map containing the fixed prototypes.
18 | */
19 | export default function rStampit(React, props) {
20 | let stamp = React ? stampit.convertConstructor(React.Component) : stampit();
21 | let refs, init, methods, statics;
22 |
23 | // Shortcut for converting React's class to a stamp.
24 | if (isEmpty(props)) {
25 | stamp.compose = compose;
26 | return stripStamp(stamp);
27 | }
28 |
29 | init = props.init || [];
30 | refs = props.state && { state: props.state } || {};
31 | statics = assign({},
32 | props.statics,
33 | pick(props, ['contextTypes', 'childContextTypes', 'propTypes', 'defaultProps']),
34 | { displayName: props.displayName || 'ReactStamp' }
35 | );
36 | methods = omit(props, (val, key) => has(statics, key) || ['init', 'state', 'statics'].indexOf(key) >= 0);
37 |
38 | stamp = stamp
39 | .init(init)
40 | .refs(refs)
41 | .methods(methods)
42 | .static(statics);
43 | stamp.compose = compose;
44 |
45 | return stripStamp(stamp);
46 | }
47 |
--------------------------------------------------------------------------------
/src/utils/cache.js:
--------------------------------------------------------------------------------
1 | import uniqueId from 'lodash/utility/uniqueId';
2 |
3 | let cache = {};
4 |
5 | export function find(id) {
6 | return cache[id];
7 | }
8 |
9 | export function save(val, id) {
10 | if (id) cache[id] = val;
11 |
12 | return val;
13 | }
14 |
15 | export { uniqueId };
16 |
--------------------------------------------------------------------------------
/src/utils/compose.js:
--------------------------------------------------------------------------------
1 | import assign from 'lodash/object/assign';
2 | import forEach from 'lodash/collection/forEach';
3 | import has from 'lodash/object/has';
4 | import mapValues from 'lodash/object/mapValues';
5 | import omit from 'lodash/object/omit';
6 | import stampit from 'stampit';
7 |
8 | import rStampit from '../';
9 | import { isStamp, stripStamp } from './';
10 |
11 | const dupeFilter = function (prev, next, key, targ) {
12 | if (targ[key]) {
13 | throw new TypeError('Cannot mixin key `' + key + '` because it is provided by multiple sources.');
14 | }
15 |
16 | return next;
17 | };
18 |
19 | /**
20 | * React specification for creating new components
21 | */
22 | const reactSpec = {
23 | propTypes: 'many_merged_dupe',
24 | defaultProps: 'many_merged_dupe',
25 | contextTypes: 'many_merged',
26 | childContextTypes: 'many_merged',
27 | getChildContext: 'many_merged_dupe',
28 | render: 'once',
29 | componentWillMount: 'many',
30 | componentDidMount: 'many',
31 | componentWillReceiveProps: 'many',
32 | shouldComponentUpdate: 'once',
33 | componentWillUpdate: 'many',
34 | componentDidUpdate: 'many',
35 | componentWillUnmount: 'many',
36 | };
37 |
38 | /**
39 | * Iterate through stamp methods, creating wrapper
40 | * functions for mixable React methods, starting
41 | * execution with first-in.
42 | *
43 | * @param {Object} targ Method destination
44 | * @param {Object} src New methods
45 | * @return {Object} An object of methods
46 | */
47 | function wrapMethods(targ, src) {
48 | let methods;
49 |
50 | methods = mapValues(src, (val, key) => {
51 | if (typeof val === 'function') {
52 | switch (reactSpec[key]) {
53 | case 'many':
54 | return function () {
55 | /* eslint-disable no-unused-expressions */
56 | targ[key] && targ[key].apply(this, arguments);
57 | val.apply(this, arguments);
58 | /* eslint-disable no-unused-expressions */
59 | };
60 | case 'many_merged_dupe':
61 | return function () {
62 | const res1 = targ[key] && targ[key].apply(this, arguments);
63 | const res2 = val.apply(this, arguments);
64 |
65 | return res1 ? assign(res1, res2, dupeFilter) : res2;
66 | };
67 | case 'once':
68 | default:
69 | if (!targ[key]) {
70 | return val;
71 | }
72 |
73 | throw new TypeError('Cannot mixin `' + key + '` because it has a unique constraint.');
74 | }
75 | }
76 | });
77 |
78 | return assign({}, targ, methods);
79 | }
80 |
81 | /**
82 | * Process the static properties of a stamp and
83 | * combine the result with the passed in statics object.
84 | *
85 | * @param {Object} stamp A stamp
86 | * @param {Object} prev An object of past static properties
87 | * @return {Object} A processed object of static properties
88 | */
89 | function extractStatics(targ, src) {
90 | let statics = assign({}, targ);
91 |
92 | forEach(src, (val, key) => {
93 | if (reactSpec[key] === 'many_merged_dupe') {
94 | statics[key] = assign({}, statics[key], val, dupeFilter);
95 | } else if (reactSpec[key] === 'many_merged') {
96 | statics[key] = assign({}, statics[key], val);
97 | } else {
98 | statics[key] = val;
99 | }
100 | });
101 |
102 | return statics;
103 | }
104 |
105 | /**
106 | * Take two or more stamps produced from react-stampit or
107 | * stampit and combine them to produce and return a new stamp.
108 | *
109 | * @param {...Function} stamp Two or more stamps.
110 | * @return {Function} A new stamp composed from arguments.
111 | */
112 | export default function compose(...stamps) {
113 | let result = stampit(),
114 | refs = { state: {} },
115 | init = [], methods = {}, statics = {};
116 |
117 | if (isStamp(this)) stamps.push(this);
118 |
119 | forEach(stamps, stamp => {
120 | stamp = !isStamp(stamp) ? rStampit(null, stamp) : stamp; // eslint-disable-line
121 |
122 | init = init.concat(stamp.fixed.init);
123 | methods = wrapMethods(methods, stamp.fixed.methods);
124 | statics = extractStatics(statics, omit(stamp, (val, key) => has(result, key)));
125 |
126 | if (stamp.fixed.refs && stamp.fixed.refs.state) {
127 | assign(refs.state, stamp.fixed.refs.state, dupeFilter);
128 | }
129 | });
130 |
131 | result = result
132 | .init(init)
133 | .refs(refs)
134 | .methods(methods)
135 | .static(statics);
136 | result.compose = compose;
137 |
138 | return stripStamp(result);
139 | }
140 |
--------------------------------------------------------------------------------
/src/utils/decorator.js:
--------------------------------------------------------------------------------
1 | import assign from 'lodash/object/assign';
2 | import merge from 'lodash/object/merge';
3 | import stampit from 'stampit';
4 |
5 | import { compose, stripStamp } from './';
6 |
7 | /**
8 | * Get object of non-enum properties
9 | */
10 | function getNonEnum(target) {
11 | const props = Object.getOwnPropertyNames(target);
12 | const enumOnly = Object.keys(target);
13 | let obj = {};
14 |
15 | props.forEach(function(key) {
16 | var indexInEnum = enumOnly.indexOf(key);
17 | if (indexInEnum === -1 && key !== 'constructor') {
18 | obj[key] = target[key];
19 | }
20 | });
21 |
22 | return obj;
23 | }
24 |
25 | /**
26 | * Get object of enum properties
27 | */
28 | function getEnum(target) {
29 | const props = Object.keys(target);
30 | let obj = {};
31 |
32 | props.forEach(function(key) {
33 | obj[key] = target[key];
34 | });
35 |
36 | return obj;
37 | }
38 |
39 | /**
40 | * ES2016 decorator for converting ES2015 class to stamp
41 | */
42 | export default function stamp(Class) {
43 | const constructor = function() {
44 | merge(this, new Class());
45 | };
46 | const methods = assign({},
47 | Object.getPrototypeOf(Class).prototype,
48 | getNonEnum(Class.prototype)
49 | );
50 |
51 | let stamp = stampit
52 | .init(constructor)
53 | .methods(methods)
54 | .static(getEnum(Class));
55 | stamp.compose = compose;
56 |
57 | return stripStamp(stamp);
58 | }
59 |
--------------------------------------------------------------------------------
/src/utils/index.js:
--------------------------------------------------------------------------------
1 | import compose from './compose';
2 | import stamp from './decorator';
3 |
4 | export function isStamp(obj) {
5 | return (
6 | typeof obj === 'function' &&
7 | typeof obj.compose === 'function' &&
8 | typeof obj.fixed === 'object'
9 | );
10 | }
11 |
12 | export function stripStamp(stamp) {
13 | delete stamp.create;
14 | delete stamp.init;
15 | delete stamp.methods;
16 | delete stamp.state;
17 | delete stamp.refs;
18 | delete stamp.props;
19 | delete stamp.enclose;
20 | delete stamp.static;
21 |
22 | return stamp;
23 | }
24 |
25 | export {
26 | compose,
27 | stamp,
28 | };
29 |
--------------------------------------------------------------------------------
/test/advanced.js:
--------------------------------------------------------------------------------
1 | import keys from 'lodash/object/keys';
2 | import React from 'react';
3 | import test from 'tape';
4 |
5 | import stampit from '../src';
6 | import { isStamp, stamp } from '../src/utils';
7 | import * as cache from '../src/utils/cache';
8 |
9 | test('stamp decorator', (t) => {
10 | t.plan(4);
11 |
12 | @stamp
13 | class Component extends React.Component {
14 | constructor(props) {
15 | super(props);
16 |
17 | this.state = {
18 | foo: 'foo',
19 | };
20 | }
21 |
22 | render() {
23 | return null;
24 | }
25 | }
26 |
27 | Component.defaultProps = {
28 | foo: 'foo',
29 | };
30 |
31 | const mixin = {
32 | state: {
33 | bar: 'bar',
34 | },
35 |
36 | defaultProps: {
37 | bar: 'bar',
38 | },
39 | };
40 |
41 | t.ok(isStamp(Component), 'converts class to stamp');
42 | /* eslint-disable new-cap */
43 | t.ok(Component().render, 'maps methods');
44 | t.deepEqual(
45 | keys(Component.compose(mixin)().state), ['bar', 'foo'],
46 | 'merges state'
47 | );
48 | t.deepEqual(
49 | keys(Component.compose(mixin).defaultProps), ['bar', 'foo'],
50 | 'merges statics'
51 | );
52 | /* eslint-enable new-cap */
53 | });
54 |
55 | test('stamp factory using `cacheStamp`', (t) => {
56 | t.plan(2);
57 |
58 | const id1 = cache.uniqueId();
59 | const id2 = cache.uniqueId();
60 |
61 | const stampFactory1 = React => {
62 | return cache.find(id1) || cache.save(
63 | stampit(React, {
64 | displayName: 'Component',
65 | }), id1
66 | );
67 | };
68 |
69 | const stampFactory2 = React => {
70 | return cache.find(id2) || cache.save(
71 | stampit(React)
72 | );
73 | };
74 |
75 | t.equal(
76 | stampFactory1(React), stampFactory1(React),
77 | 'is memoized if unique id is defined'
78 | );
79 |
80 | t.notEqual(
81 | stampFactory2(React), stampFactory2(React),
82 | 'is not memoized if unique id is undefined'
83 | );
84 | });
85 |
--------------------------------------------------------------------------------
/test/basics.js:
--------------------------------------------------------------------------------
1 | import React from 'react/addons';
2 | import test from 'tape';
3 |
4 | import stampit from '../src';
5 | import { isStamp } from '../src/utils';
6 |
7 | const TestUtils = React.addons.TestUtils;
8 |
9 | test('stampit()', (t) => {
10 | t.plan(1);
11 |
12 | t.ok(
13 | isStamp(stampit()),
14 | 'should return a stamp'
15 | );
16 | });
17 |
18 | test('stampit(React, props)', (t) => {
19 | t.plan(1);
20 |
21 | const stamp = stampit(React, {});
22 |
23 | t.ok(
24 | isStamp(stamp),
25 | 'should return a stamp'
26 | );
27 | });
28 |
29 | test('stampit(React)', (t) => {
30 | t.plan(1);
31 |
32 | const stamp = stampit(React);
33 |
34 | t.ok(
35 | isStamp(stamp),
36 | 'should return a stamp'
37 | );
38 | });
39 |
40 | test('stampit(null, props)', (t) => {
41 | t.plan(1);
42 |
43 | const stamp = stampit(null, {});
44 |
45 | t.ok(
46 | isStamp(stamp),
47 | 'should return a stamp'
48 | );
49 | });
50 |
51 | test('stampit(React, { render() })()', (t) => {
52 | t.plan(1);
53 |
54 | const stamp = stampit(React, {
55 | render() {},
56 | });
57 |
58 | t.ok(
59 | TestUtils.isCompositeComponent(stamp()),
60 | 'should return a React component'
61 | );
62 | });
63 |
64 | test('stampit(React, props).compose', (t) => {
65 | t.plan(1);
66 |
67 | t.equal(
68 | typeof stampit(React).compose, 'function',
69 | 'should be a function'
70 | );
71 | });
72 |
73 | test('stampit(React, props).create', (t) => {
74 | t.plan(1);
75 |
76 | t.equal(
77 | typeof stampit(React).create, 'undefined',
78 | 'should be undefined'
79 | );
80 | });
81 |
82 | test('stampit(React, props).init', (t) => {
83 | t.plan(1);
84 |
85 | t.equal(
86 | typeof stampit(React).init, 'undefined',
87 | 'should be undefined'
88 | );
89 | });
90 |
91 | test('stampit(React, props).methods', (t) => {
92 | t.plan(1);
93 |
94 | t.equal(
95 | typeof stampit(React).methods, 'undefined',
96 | 'should be undefined'
97 | );
98 | });
99 |
100 | test('stampit(React, props).state', (t) => {
101 | t.plan(1);
102 |
103 | t.equal(
104 | typeof stampit(React).state, 'undefined',
105 | 'should be undefined'
106 | );
107 | });
108 |
109 | test('stampit(React, props).refs', (t) => {
110 | t.plan(1);
111 |
112 | t.equal(
113 | typeof stampit(React).refs, 'undefined',
114 | 'should be undefined'
115 | );
116 | });
117 |
118 | test('stampit(React, props).props', (t) => {
119 | t.plan(1);
120 |
121 | t.equal(
122 | typeof stampit(React).props, 'undefined',
123 | 'should be undefined'
124 | );
125 | });
126 |
127 | test('stampit(React, props).enclose', (t) => {
128 | t.plan(1);
129 |
130 | t.equal(
131 | typeof stampit(React).enclose, 'undefined',
132 | 'should be undefined'
133 | );
134 | });
135 |
136 | test('stampit(React, props).static', (t) => {
137 | t.plan(1);
138 |
139 | t.equal(
140 | typeof stampit(React).static, 'undefined',
141 | 'should be undefined'
142 | );
143 | });
144 |
--------------------------------------------------------------------------------
/test/compose.js:
--------------------------------------------------------------------------------
1 | import keys from 'lodash/object/keys';
2 | import React from 'react';
3 | import test from 'tape';
4 |
5 | import stampit from '../src';
6 | import { compose } from '../src/utils';
7 |
8 | test('stampit(React, props).compose(stamp2)', (t) => {
9 | t.plan(1);
10 |
11 | const mixin = stampit(null, {
12 | method() {
13 | return 'mixin';
14 | },
15 | });
16 |
17 | const stamp = stampit(React).compose(mixin);
18 |
19 | t.equal(
20 | stamp().method(), 'mixin',
21 | 'should return a stamp composed of `this` and passed stamp'
22 | );
23 | });
24 |
25 | test('stampit(React, props).compose(pojo)', (t) => {
26 | t.plan(1);
27 |
28 | const mixin = {
29 | method() {
30 | return 'mixin';
31 | },
32 | };
33 |
34 | const stamp = stampit(React).compose(mixin);
35 |
36 | t.equal(
37 | stamp().method(), 'mixin',
38 | 'should return a stamp composed of `this` and passed pojo'
39 | );
40 | });
41 |
42 | test('stampit(React, props).compose(stamp2, stamp3)', (t) => {
43 | t.plan(2);
44 |
45 | const mixin1 = stampit(null, {
46 | method() {
47 | return this.state;
48 | },
49 | });
50 |
51 | const mixin2 = stampit(null, {
52 | statics: {
53 | util() {
54 | return 'static';
55 | },
56 | },
57 | });
58 |
59 | const stamp = stampit(React, {
60 | state: {
61 | foo: '',
62 | },
63 | }).compose(mixin1, mixin2);
64 |
65 | t.deepEqual(
66 | keys(stamp().method()), ['foo'],
67 | 'should expose `this` to inherited methods'
68 | );
69 |
70 | t.equal(
71 | stamp.util(), 'static',
72 | 'should inherit static methods'
73 | );
74 | });
75 |
76 | test('compose(stamp2, stamp1)', (t) => {
77 | t.plan(1);
78 |
79 | const mixin = stampit(null, {
80 | method() {
81 | return 'mixin';
82 | },
83 | });
84 |
85 | const stamp = stampit(React);
86 |
87 | t.equal(
88 | compose(mixin, stamp)().method(), 'mixin',
89 | 'should return a stamp composed of passed stamps'
90 | );
91 | });
92 |
93 | test('stamps composed of stamps with state', (t) => {
94 | t.plan(2);
95 |
96 | const mixin = stampit(null, {
97 | state: {
98 | foo: ' ',
99 | },
100 | });
101 |
102 | const stamp = stampit(React, {
103 | state: {
104 | bar: ' ',
105 | },
106 | }).compose(mixin);
107 |
108 | const failStamp = stampit(React, {
109 | state: {
110 | foo: ' ',
111 | },
112 | });
113 |
114 | t.deepEqual(
115 | stamp().state, { foo: ' ', bar: ' ' },
116 | 'should inherit state'
117 | );
118 |
119 | t.throws(
120 | () => failStamp.compose(mixin), TypeError,
121 | 'should throw on duplicate keys'
122 | );
123 | });
124 |
125 | test('stamps composed of stamps with React statics', (t) => {
126 | t.plan(8);
127 |
128 | const mixin = stampit(null, {
129 | contextTypes: {
130 | foo: React.PropTypes.string,
131 | },
132 | childContextTypes: {
133 | foo: React.PropTypes.string,
134 | },
135 | propTypes: {
136 | foo: React.PropTypes.string,
137 | },
138 | defaultProps: {
139 | foo: 'foo',
140 | },
141 | });
142 |
143 | const stamp = stampit(React, {
144 | contextTypes: {
145 | bar: React.PropTypes.string,
146 | },
147 | childContextTypes: {
148 | bar: React.PropTypes.string,
149 | },
150 | propTypes: {
151 | bar: React.PropTypes.string,
152 | },
153 | defaultProps: {
154 | bar: 'bar',
155 | },
156 | }).compose(mixin);
157 |
158 | const failStamp1 = stampit(React, {
159 | propTypes: {
160 | foo: React.PropTypes.string,
161 | },
162 | });
163 |
164 | const failStamp2 = stampit(React, {
165 | defaultProps: {
166 | foo: 'foo',
167 | },
168 | });
169 |
170 | const okStamp1 = stampit(React, {
171 | contextTypes: {
172 | foo: React.PropTypes.string,
173 | },
174 | });
175 |
176 | const okStamp2 = stampit(React, {
177 | childContextTypes: {
178 | foo: React.PropTypes.string,
179 | },
180 | });
181 |
182 | t.deepEqual(
183 | keys(stamp.contextTypes), ['foo', 'bar'],
184 | 'should inherit `contextTypes` props'
185 | );
186 |
187 | t.deepEqual(
188 | keys(stamp.childContextTypes), ['foo', 'bar'],
189 | 'should inherit `childContextTypes` props'
190 | );
191 |
192 | t.deepEqual(
193 | keys(stamp.propTypes), ['foo', 'bar'],
194 | 'should inherit `propTypes` props'
195 | );
196 |
197 | t.deepEqual(
198 | keys(stamp.defaultProps), ['foo', 'bar'],
199 | 'should inherit `defaultProps` props'
200 | );
201 |
202 | t.throws(
203 | () => failStamp1.compose(mixin), TypeError,
204 | 'should throw on duplicate keys in `propTypes`'
205 | );
206 |
207 | t.throws(
208 | () => failStamp2.compose(mixin), TypeError,
209 | 'should throw on duplicate keys in `defaultProps`'
210 | );
211 |
212 | t.doesNotThrow(
213 | () => okStamp1.compose(mixin),
214 | 'should not throw on duplicate keys in `contextTypes`'
215 | );
216 |
217 | t.doesNotThrow(
218 | () => okStamp2.compose(mixin),
219 | 'should not throw on duplicate keys in `childContextTypes`'
220 | );
221 | });
222 |
223 | test('stamps composed of stamps with non-React statics', (t) => {
224 | t.plan(2);
225 |
226 | const mixin = stampit(null, {
227 | statics: {
228 | obj: {
229 | foo: 'foo',
230 | bar: '',
231 | },
232 | method() {
233 | return 'foo';
234 | },
235 | },
236 | });
237 |
238 | const stamp = stampit(React, {
239 | statics: {
240 | obj: {
241 | bar: 'bar',
242 | },
243 | method() {
244 | return 'bar';
245 | },
246 | },
247 | }).compose(mixin);
248 |
249 | t.deepEqual(
250 | stamp.obj, { bar: 'bar' },
251 | 'should inherit static objects overriding with last-in priority'
252 | );
253 |
254 | t.equal(
255 | stamp.method(), 'bar',
256 | 'should inherit static methods overriding with last-in priority'
257 | );
258 | });
259 |
260 | test('stamps composed of stamps with mixable methods', (t) => {
261 | t.plan(2);
262 |
263 | const mixin1 = stampit(null, {
264 | getChildContext() {
265 | return {
266 | foo: '',
267 | };
268 | },
269 |
270 | componentDidMount() {
271 | this.state.mixin1 = true;
272 | },
273 | });
274 |
275 | const mixin2 = stampit(null, {
276 | componentDidMount() {
277 | this.state.mixin2 = true;
278 | },
279 | });
280 |
281 | const stamp = stampit(React, {
282 | state: {
283 | stamp: false,
284 | mixin1: false,
285 | mixin2: false,
286 | },
287 |
288 | getChildContext() {
289 | return {
290 | bar: '',
291 | };
292 | },
293 |
294 | componentDidMount() {
295 | this.state.stamp = true;
296 | },
297 | }).compose(mixin1, mixin2);
298 |
299 | const instance = stamp();
300 | instance.componentDidMount();
301 |
302 | t.deepEqual(
303 | instance.state, { stamp: true, mixin1: true, mixin2: true },
304 | 'should inherit functionality of mixable methods'
305 | );
306 |
307 | t.deepEqual(
308 | keys(instance.getChildContext()), ['foo', 'bar'],
309 | 'should inherit functionality of getChildContext'
310 | );
311 | });
312 |
313 | test('stamps composed of stamps with non-mixable methods', (t) => {
314 | t.plan(1);
315 |
316 | const mixin = stampit(null, {
317 | render() {},
318 | });
319 |
320 | const stamp = stampit(React, {
321 | render() {},
322 | });
323 |
324 | t.throws(
325 | () => stamp.compose(mixin), TypeError,
326 | 'should throw on duplicate methods'
327 | );
328 | });
329 |
330 |
--------------------------------------------------------------------------------
/test/methods.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import test from 'tape';
3 |
4 | import stampit from '../src';
5 |
6 | test('stampit(React, { method() {} })()', (t) => {
7 | t.plan(1);
8 |
9 | const stamp = stampit(React, {
10 | render() {},
11 | });
12 |
13 | t.equal(
14 | typeof Object.getPrototypeOf(stamp()).render, 'function',
15 | 'should return an instance with `method` as internal proto prop'
16 | );
17 | });
18 |
--------------------------------------------------------------------------------
/test/state.js:
--------------------------------------------------------------------------------
1 | import has from 'lodash/object/has';
2 | import React from 'react';
3 | import test from 'tape';
4 |
5 | import stampit from '../src';
6 |
7 | test('stampit(React, { state: obj })()', (t) => {
8 | t.plan(1);
9 |
10 | const stamp = stampit(React, {
11 | state: {
12 | foo: '',
13 | },
14 | });
15 |
16 | t.ok(
17 | has(stamp().state, 'foo'),
18 | 'should return an instance with `state` prop'
19 | );
20 | });
21 |
22 | test('stampit(React, { init() { ... } })()', (t) => {
23 | t.plan(1);
24 |
25 | const stamp = stampit(React, {
26 | init() {
27 | this.state = { foo: '' };
28 | },
29 | });
30 |
31 | t.ok(
32 | has(stamp().state, 'foo'),
33 | 'should return an instance with `state` prop'
34 | );
35 | });
36 |
--------------------------------------------------------------------------------
/test/statics.js:
--------------------------------------------------------------------------------
1 | import has from 'lodash/object/has';
2 | import React from 'react';
3 | import test from 'tape';
4 |
5 | import stampit from '../src';
6 |
7 | test('stampit(React, { statics: obj })', (t) => {
8 | t.plan(1);
9 |
10 | const stamp = stampit(React, {
11 | statics: {
12 | foo: '',
13 | },
14 | });
15 |
16 | t.ok(
17 | has(stamp, 'foo'),
18 | 'should return a stamp with `statics` props as props'
19 | );
20 | });
21 |
22 | test('stampit(React, { displayName: str })', (t) => {
23 | t.plan(1);
24 |
25 | const stamp = stampit(React, {
26 | displayName: 'stamp',
27 | });
28 |
29 | t.ok(
30 | has(stamp, 'displayName'),
31 | 'should return a stamp with `displayName` prop'
32 | );
33 | });
34 |
35 | test('stampit(React, { contextTypes: obj })', (t) => {
36 | t.plan(1);
37 |
38 | const stamp = stampit(React, {
39 | contextTypes: {},
40 | });
41 |
42 | t.ok(
43 | has(stamp, 'contextTypes'),
44 | 'should return a stamp with `contextTypes` prop'
45 | );
46 | });
47 |
48 | test('stampit(React, { childContextTypes: obj })', (t) => {
49 | t.plan(1);
50 |
51 | const stamp = stampit(React, {
52 | childContextTypes: {},
53 | });
54 |
55 | t.ok(
56 | has(stamp, 'childContextTypes'),
57 | 'should return a stamp with `childContextTypes` prop'
58 | );
59 | });
60 |
61 | test('stampit(React, { propTypes: obj })', (t) => {
62 | t.plan(1);
63 |
64 | const stamp = stampit(React, {
65 | propTypes: {},
66 | });
67 |
68 | t.ok(
69 | has(stamp, 'propTypes'),
70 | 'should return a stamp with `propTypes` prop'
71 | );
72 | });
73 |
74 | test('stampit(React, { defaultProps: obj })', (t) => {
75 | t.plan(1);
76 |
77 | const stamp = stampit(React, {
78 | defaultProps: {},
79 | });
80 |
81 | t.ok(
82 | has(stamp, 'defaultProps'),
83 | 'should return a stamp with `defaultProps` prop'
84 | );
85 | });
86 |
--------------------------------------------------------------------------------
/test/test.js:
--------------------------------------------------------------------------------
1 | require('./basics');
2 | require('./state');
3 | require('./statics');
4 | require('./methods');
5 | require('./compose');
6 | require('./wrapMethods');
7 | require('./advanced');
8 |
--------------------------------------------------------------------------------
/test/wrapMethods.js:
--------------------------------------------------------------------------------
1 | import rewire from 'rewire';
2 | import test from 'tape';
3 |
4 | let compose = rewire('../src/utils/compose');
5 | let wrapMethods = compose.__get__('wrapMethods');
6 |
7 | test('wrapMethods(targ, src)', (t) => {
8 | t.plan(7);
9 |
10 | /* eslint-disable brace-style */
11 | const targ = {
12 | componentWillMount() { this.wrapped = false; },
13 | componentDidMount() { this.wrapped = false; },
14 | componentWillReceiveProps() { this.wrapped = false; },
15 | componentWillUpdate() { this.wrapped = false; },
16 | componentDidUpdate() { this.wrapped = false; },
17 | componentWillUnmount() { this.wrapped = false; },
18 | method() {},
19 | };
20 |
21 | const src = {
22 | componentWillMount() { this.wrapped = 'WillMount'; },
23 | componentDidMount() { this.wrapped = 'DidMount'; },
24 | componentWillReceiveProps() { this.wrapped = 'WillReceiveProps'; },
25 | componentWillUpdate() { this.wrapped = 'WillUpdate'; },
26 | componentDidUpdate() { this.wrapped = 'DidUpdate'; },
27 | componentWillUnmount() { this.wrapped = 'WillUnmount'; },
28 | };
29 |
30 | const failObj = {
31 | method() {},
32 | };
33 | /* eslint-enable brace-style */
34 |
35 | const obj = wrapMethods(targ, src);
36 |
37 | obj.componentWillMount();
38 | t.equal(
39 | obj.wrapped, 'WillMount',
40 | 'should wrap `componentWillMount`'
41 | );
42 |
43 | obj.componentDidMount();
44 | t.equal(
45 | obj.wrapped, 'DidMount',
46 | 'should wrap `componentDidMount`'
47 | );
48 |
49 | obj.componentWillReceiveProps();
50 | t.equal(
51 | obj.wrapped, 'WillReceiveProps',
52 | 'should wrap `componentWillReceiveProps`'
53 | );
54 |
55 | obj.componentWillUpdate();
56 | t.equal(
57 | obj.wrapped, 'WillUpdate',
58 | 'should wrap `componentWillUpdate`'
59 | );
60 |
61 | obj.componentDidUpdate();
62 | t.equal(
63 | obj.wrapped, 'DidUpdate',
64 | 'should wrap `componentDidUpdate`'
65 | );
66 |
67 | obj.componentWillUnmount();
68 | t.ok(
69 | obj.wrapped, 'WillUnmount',
70 | 'should wrap `componentWillUnmount`'
71 | );
72 |
73 | t.throws(
74 | () => wrapMethods(targ, failObj), TypeError,
75 | 'should throw on dupe non-React method'
76 | );
77 | });
78 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var minify = process.env.MINIFY || false;
3 |
4 | module.exports = {
5 | entry: {
6 | 'react-stampit': ['./src/index'],
7 | 'react-stampit-with-addons': ['./src/addons'],
8 | },
9 |
10 | output: {
11 | path: path.resolve('./dist'),
12 | filename: minify ? '[name].min.js' : '[name].js',
13 | library: 'stampit',
14 | libraryTarget: 'umd',
15 | },
16 |
17 | module: {
18 | loaders: [
19 | { test: /\.js$/, loader: 'babel-loader' },
20 | ],
21 | },
22 | };
23 |
--------------------------------------------------------------------------------