├── .eslintrc ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── examples ├── 01 - Basic │ ├── .eslintrc │ ├── README.md │ ├── components │ │ └── MyComponent.js │ ├── index.html │ ├── index.js │ ├── package.json │ └── scripts │ │ └── run ├── 02 - Side effects │ ├── .eslintrc │ ├── README.md │ ├── components │ │ ├── Dep.js │ │ ├── MyComponent.js │ │ ├── MyComponent2.js │ │ └── theme.js │ ├── index.html │ ├── index.js │ ├── package.json │ └── scripts │ │ └── run └── 03 - Basic auth │ ├── README.md │ ├── components │ ├── About.js │ ├── App.js │ ├── Dashboard.js │ ├── Login.js │ └── Logout.js │ ├── index.html │ ├── index.js │ ├── package.json │ ├── scripts │ └── run │ └── vendor │ ├── auth.js │ └── requireAuth.js ├── index.js ├── package.json └── src ├── Proxies.js ├── StaticModules.js ├── actions ├── applyPatch.js ├── bytesToKb.js ├── processError.js └── storeSources.js ├── injectWebSocket.js ├── overrideRequire.js └── transform.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "env": { 4 | "browser": true, 5 | "node": true, 6 | "jasmine": true 7 | }, 8 | "ecmaFeatures": { 9 | "arrowFunctions": true, 10 | "blockBindings": true, 11 | "classes": true, 12 | "defaultParams": true, 13 | "destructuring": true, 14 | "forOf": true, 15 | "generators": false, 16 | "modules": true, 17 | "objectLiteralComputedProperties": true, 18 | "objectLiteralDuplicateProperties": false, 19 | "objectLiteralShorthandMethods": true, 20 | "objectLiteralShorthandProperties": true, 21 | "spread": true, 22 | "superInFunctions": true, 23 | "templateStrings": true, 24 | "jsx": true 25 | }, 26 | "rules": { 27 | /** 28 | * Strict mode 29 | */ 30 | // babel inserts "use strict"; for us 31 | // http://eslint.org/docs/rules/strict 32 | "strict": [2, "never"], 33 | 34 | /** 35 | * ES6 36 | */ 37 | "no-var": 0, // http://eslint.org/docs/rules/no-var 38 | 39 | /** 40 | * Variables 41 | */ 42 | "no-shadow": 2, // http://eslint.org/docs/rules/no-shadow 43 | "no-shadow-restricted-names": 2, // http://eslint.org/docs/rules/no-shadow-restricted-names 44 | "no-unused-vars": [0, { // http://eslint.org/docs/rules/no-unused-vars 45 | "vars": "local", 46 | "args": "after-used" 47 | }], 48 | "no-use-before-define": 2, // http://eslint.org/docs/rules/no-use-before-define 49 | 50 | /** 51 | * Possible errors 52 | */ 53 | "comma-dangle": [2, "always"], // http://eslint.org/docs/rules/comma-dangle 54 | "no-cond-assign": [2, "always"], // http://eslint.org/docs/rules/no-cond-assign 55 | "no-console": 0, // http://eslint.org/docs/rules/no-console 56 | "no-debugger": 1, // http://eslint.org/docs/rules/no-debugger 57 | "no-alert": 1, // http://eslint.org/docs/rules/no-alert 58 | "no-constant-condition": 1, // http://eslint.org/docs/rules/no-constant-condition 59 | "no-dupe-keys": 2, // http://eslint.org/docs/rules/no-dupe-keys 60 | "no-duplicate-case": 2, // http://eslint.org/docs/rules/no-duplicate-case 61 | "no-empty": 2, // http://eslint.org/docs/rules/no-empty 62 | "no-ex-assign": 2, // http://eslint.org/docs/rules/no-ex-assign 63 | "no-extra-boolean-cast": 0, // http://eslint.org/docs/rules/no-extra-boolean-cast 64 | "no-extra-semi": 2, // http://eslint.org/docs/rules/no-extra-semi 65 | "no-func-assign": 2, // http://eslint.org/docs/rules/no-func-assign 66 | "no-inner-declarations": 2, // http://eslint.org/docs/rules/no-inner-declarations 67 | "no-invalid-regexp": 2, // http://eslint.org/docs/rules/no-invalid-regexp 68 | "no-irregular-whitespace": 2, // http://eslint.org/docs/rules/no-irregular-whitespace 69 | "no-obj-calls": 2, // http://eslint.org/docs/rules/no-obj-calls 70 | "no-reserved-keys": 0, // http://eslint.org/docs/rules/no-reserved-keys 71 | "no-sparse-arrays": 2, // http://eslint.org/docs/rules/no-sparse-arrays 72 | "no-unreachable": 2, // http://eslint.org/docs/rules/no-unreachable 73 | "use-isnan": 2, // http://eslint.org/docs/rules/use-isnan 74 | "block-scoped-var": 2, // http://eslint.org/docs/rules/block-scoped-var 75 | 76 | /** 77 | * Best practices 78 | */ 79 | "consistent-return": 2, // http://eslint.org/docs/rules/consistent-return 80 | "curly": [2, "multi-line"], // http://eslint.org/docs/rules/curly 81 | "default-case": 2, // http://eslint.org/docs/rules/default-case 82 | "dot-notation": [2, { // http://eslint.org/docs/rules/dot-notation 83 | "allowKeywords": true 84 | }], 85 | "eqeqeq": 2, // http://eslint.org/docs/rules/eqeqeq 86 | "guard-for-in": 2, // http://eslint.org/docs/rules/guard-for-in 87 | "no-caller": 2, // http://eslint.org/docs/rules/no-caller 88 | "no-else-return": 2, // http://eslint.org/docs/rules/no-else-return 89 | "no-eq-null": 2, // http://eslint.org/docs/rules/no-eq-null 90 | "no-eval": 2, // http://eslint.org/docs/rules/no-eval 91 | "no-extend-native": 2, // http://eslint.org/docs/rules/no-extend-native 92 | "no-extra-bind": 2, // http://eslint.org/docs/rules/no-extra-bind 93 | "no-fallthrough": 2, // http://eslint.org/docs/rules/no-fallthrough 94 | "no-floating-decimal": 2, // http://eslint.org/docs/rules/no-floating-decimal 95 | "no-implied-eval": 2, // http://eslint.org/docs/rules/no-implied-eval 96 | "no-lone-blocks": 2, // http://eslint.org/docs/rules/no-lone-blocks 97 | "no-loop-func": 2, // http://eslint.org/docs/rules/no-loop-func 98 | "no-multi-str": 2, // http://eslint.org/docs/rules/no-multi-str 99 | "no-native-reassign": 2, // http://eslint.org/docs/rules/no-native-reassign 100 | "no-new": 2, // http://eslint.org/docs/rules/no-new 101 | "no-new-func": 2, // http://eslint.org/docs/rules/no-new-func 102 | "no-new-wrappers": 2, // http://eslint.org/docs/rules/no-new-wrappers 103 | "no-octal": 2, // http://eslint.org/docs/rules/no-octal 104 | "no-octal-escape": 2, // http://eslint.org/docs/rules/no-octal-escape 105 | "no-param-reassign": 2, // http://eslint.org/docs/rules/no-param-reassign 106 | "no-proto": 2, // http://eslint.org/docs/rules/no-proto 107 | "no-redeclare": 2, // http://eslint.org/docs/rules/no-redeclare 108 | "no-return-assign": 2, // http://eslint.org/docs/rules/no-return-assign 109 | "no-script-url": 2, // http://eslint.org/docs/rules/no-script-url 110 | "no-self-compare": 2, // http://eslint.org/docs/rules/no-self-compare 111 | "no-sequences": 2, // http://eslint.org/docs/rules/no-sequences 112 | "no-throw-literal": 2, // http://eslint.org/docs/rules/no-throw-literal 113 | "no-with": 2, // http://eslint.org/docs/rules/no-with 114 | "radix": 2, // http://eslint.org/docs/rules/radix 115 | "vars-on-top": 0, // http://eslint.org/docs/rules/vars-on-top 116 | "wrap-iife": [2, "any"], // http://eslint.org/docs/rules/wrap-iife 117 | "yoda": 2, // http://eslint.org/docs/rules/yoda 118 | 119 | /** 120 | * Style 121 | */ 122 | "indent": [2, 2], // http://eslint.org/docs/rules/ 123 | "brace-style": [2, // http://eslint.org/docs/rules/brace-style 124 | "1tbs", { 125 | "allowSingleLine": true 126 | }], 127 | "quotes": [ 128 | 2, "single", "avoid-escape" // http://eslint.org/docs/rules/quotes 129 | ], 130 | "camelcase": [2, { // http://eslint.org/docs/rules/camelcase 131 | "properties": "never" 132 | }], 133 | "comma-spacing": [2, { // http://eslint.org/docs/rules/comma-spacing 134 | "before": false, 135 | "after": true 136 | }], 137 | "comma-style": [2, "last"], // http://eslint.org/docs/rules/comma-style 138 | "eol-last": 2, // http://eslint.org/docs/rules/eol-last 139 | "func-names": 1, // http://eslint.org/docs/rules/func-names 140 | "key-spacing": [2, { // http://eslint.org/docs/rules/key-spacing 141 | "beforeColon": false, 142 | "afterColon": true 143 | }], 144 | "new-cap": [2, { // http://eslint.org/docs/rules/new-cap 145 | "newIsCap": true 146 | }], 147 | "no-multiple-empty-lines": [2, { // http://eslint.org/docs/rules/no-multiple-empty-lines 148 | "max": 2 149 | }], 150 | "no-nested-ternary": 2, // http://eslint.org/docs/rules/no-nested-ternary 151 | "no-new-object": 2, // http://eslint.org/docs/rules/no-new-object 152 | "no-spaced-func": 2, // http://eslint.org/docs/rules/no-spaced-func 153 | "no-trailing-spaces": 2, // http://eslint.org/docs/rules/no-trailing-spaces 154 | "no-wrap-func": 2, // http://eslint.org/docs/rules/no-wrap-func 155 | "no-underscore-dangle": 0, // http://eslint.org/docs/rules/no-underscore-dangle 156 | "one-var": [2, "never"], // http://eslint.org/docs/rules/one-var 157 | "padded-blocks": 0, // http://eslint.org/docs/rules/padded-blocks 158 | "semi": [2, "always"], // http://eslint.org/docs/rules/semi 159 | "semi-spacing": [2, { // http://eslint.org/docs/rules/semi-spacing 160 | "before": false, 161 | "after": true 162 | }], 163 | "space-after-keywords": 2, // http://eslint.org/docs/rules/space-after-keywords 164 | "space-before-blocks": 2, // http://eslint.org/docs/rules/space-before-blocks 165 | "space-before-function-paren": [2, "never"], // http://eslint.org/docs/rules/space-before-function-paren 166 | "space-infix-ops": 2, // http://eslint.org/docs/rules/space-infix-ops 167 | "space-return-throw-case": 2, // http://eslint.org/docs/rules/space-return-throw-case 168 | "spaced-line-comment": 2 // http://eslint.org/docs/rules/spaced-line-comment 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | examples 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Alexey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Browserify React Live 2 | This is a [browserify](https://github.com/substack/node-browserify) transform which works similarly to [react-hot-loader](https://github.com/gaearon/react-hot-loader). 3 | Once you run you app in the browser, it monitors your JavaScript code and only updates the changed component, preserving the state of the application. 4 | 5 | 6 | 7 | ## Installing 8 | ```bash 9 | npm install browserify-react-live --save-dev 10 | ``` 11 | 12 | ## Quick example 13 | ```bash 14 | git clone https://github.com/Kureev/browserify-react-live.git 15 | cd browserify-react-live/examples/01\ -\ Basic 16 | npm i && npm start 17 | ``` 18 | 19 | Run [http://localhost:8080](http://localhost:8080) and try updating the component. 20 | 21 | ## Running 22 | 23 | Add transform to `package.json`: 24 | ```json 25 | "browserify": { 26 | "transform": [ 27 | "browserify-react-live" 28 | ] 29 | } 30 | ``` 31 | or run watchify with transform from the CLI: 32 | ```bash 33 | watchify -t browserify-react-live components/file.js -o bundles/file.js 34 | ``` 35 | 36 | Start `browserify-patch-server`: 37 | ```bash 38 | node_modules/.bin/browserify-patch-server components/* 39 | ``` 40 | 41 | 42 | ## Configuration 43 | ### Port number 44 | 45 | - Server 46 | ```bash 47 | node_modules/.bin/browserify-patch-server components/* -p 8888 # Default is 8081 48 | ``` 49 | 50 | - Transform 51 | ```bash 52 | watchify -t [ browserify-react-live -p 8888 ] components/file.js -o bundles/file.js # Default is 8080 53 | ``` 54 | 55 | ## How it works 56 | `browserify-react-live` works with `browserify-patch-server`: 57 | - `browserify-patch-server` 58 | This part is responsible for watching changes for specified path and compute/broadcast patch. Every time watched files changes, it automatically calculate patch and send it via websocket to client. 59 | - `browserify-react-live` transform. Patch browserify's `require` function to inject Dan Abramov's `react-hot-api` and websocket client which will wait for server broadcast and apply received patch. 60 | 61 | 62 | 63 | ## Migration 1.x -> 2.x 64 | - Now you need to watch files instead of bundle. E.g. `components/*` instead of `dist/bundle.js` 65 | 66 | ## Compatibility 67 | - `node` > 0.10 or `io.js` > 2.0 68 | 69 | ## License 70 | MIT 71 | -------------------------------------------------------------------------------- /examples/01 - Basic/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "env": { 4 | "browser": true, 5 | "node": true, 6 | "jasmine": true 7 | }, 8 | "ecmaFeatures": { 9 | "arrowFunctions": true, 10 | "blockBindings": true, 11 | "classes": true, 12 | "defaultParams": true, 13 | "destructuring": true, 14 | "forOf": true, 15 | "generators": false, 16 | "modules": true, 17 | "objectLiteralComputedProperties": true, 18 | "objectLiteralDuplicateProperties": false, 19 | "objectLiteralShorthandMethods": true, 20 | "objectLiteralShorthandProperties": true, 21 | "spread": true, 22 | "superInFunctions": true, 23 | "templateStrings": true, 24 | "jsx": true 25 | }, 26 | "rules": { 27 | /** 28 | * Strict mode 29 | */ 30 | // babel inserts "use strict"; for us 31 | // http://eslint.org/docs/rules/strict 32 | "strict": [2, "never"], 33 | 34 | /** 35 | * ES6 36 | */ 37 | "no-var": 0, // http://eslint.org/docs/rules/no-var 38 | 39 | /** 40 | * Variables 41 | */ 42 | "no-shadow": 2, // http://eslint.org/docs/rules/no-shadow 43 | "no-shadow-restricted-names": 2, // http://eslint.org/docs/rules/no-shadow-restricted-names 44 | "no-unused-vars": [0, { // http://eslint.org/docs/rules/no-unused-vars 45 | "vars": "local", 46 | "args": "after-used" 47 | }], 48 | "no-use-before-define": 2, // http://eslint.org/docs/rules/no-use-before-define 49 | 50 | /** 51 | * Possible errors 52 | */ 53 | "comma-dangle": [2, "always"], // http://eslint.org/docs/rules/comma-dangle 54 | "no-cond-assign": [2, "always"], // http://eslint.org/docs/rules/no-cond-assign 55 | "no-console": 0, // http://eslint.org/docs/rules/no-console 56 | "no-debugger": 1, // http://eslint.org/docs/rules/no-debugger 57 | "no-alert": 1, // http://eslint.org/docs/rules/no-alert 58 | "no-constant-condition": 1, // http://eslint.org/docs/rules/no-constant-condition 59 | "no-dupe-keys": 2, // http://eslint.org/docs/rules/no-dupe-keys 60 | "no-duplicate-case": 2, // http://eslint.org/docs/rules/no-duplicate-case 61 | "no-empty": 2, // http://eslint.org/docs/rules/no-empty 62 | "no-ex-assign": 2, // http://eslint.org/docs/rules/no-ex-assign 63 | "no-extra-boolean-cast": 0, // http://eslint.org/docs/rules/no-extra-boolean-cast 64 | "no-extra-semi": 2, // http://eslint.org/docs/rules/no-extra-semi 65 | "no-func-assign": 2, // http://eslint.org/docs/rules/no-func-assign 66 | "no-inner-declarations": 2, // http://eslint.org/docs/rules/no-inner-declarations 67 | "no-invalid-regexp": 2, // http://eslint.org/docs/rules/no-invalid-regexp 68 | "no-irregular-whitespace": 2, // http://eslint.org/docs/rules/no-irregular-whitespace 69 | "no-obj-calls": 2, // http://eslint.org/docs/rules/no-obj-calls 70 | "no-reserved-keys": 0, // http://eslint.org/docs/rules/no-reserved-keys 71 | "no-sparse-arrays": 2, // http://eslint.org/docs/rules/no-sparse-arrays 72 | "no-unreachable": 2, // http://eslint.org/docs/rules/no-unreachable 73 | "use-isnan": 2, // http://eslint.org/docs/rules/use-isnan 74 | "block-scoped-var": 2, // http://eslint.org/docs/rules/block-scoped-var 75 | 76 | /** 77 | * Best practices 78 | */ 79 | "consistent-return": 2, // http://eslint.org/docs/rules/consistent-return 80 | "curly": [2, "multi-line"], // http://eslint.org/docs/rules/curly 81 | "default-case": 2, // http://eslint.org/docs/rules/default-case 82 | "dot-notation": [2, { // http://eslint.org/docs/rules/dot-notation 83 | "allowKeywords": true 84 | }], 85 | "eqeqeq": 2, // http://eslint.org/docs/rules/eqeqeq 86 | "guard-for-in": 2, // http://eslint.org/docs/rules/guard-for-in 87 | "no-caller": 2, // http://eslint.org/docs/rules/no-caller 88 | "no-else-return": 2, // http://eslint.org/docs/rules/no-else-return 89 | "no-eq-null": 2, // http://eslint.org/docs/rules/no-eq-null 90 | "no-eval": 2, // http://eslint.org/docs/rules/no-eval 91 | "no-extend-native": 2, // http://eslint.org/docs/rules/no-extend-native 92 | "no-extra-bind": 2, // http://eslint.org/docs/rules/no-extra-bind 93 | "no-fallthrough": 2, // http://eslint.org/docs/rules/no-fallthrough 94 | "no-floating-decimal": 2, // http://eslint.org/docs/rules/no-floating-decimal 95 | "no-implied-eval": 2, // http://eslint.org/docs/rules/no-implied-eval 96 | "no-lone-blocks": 2, // http://eslint.org/docs/rules/no-lone-blocks 97 | "no-loop-func": 2, // http://eslint.org/docs/rules/no-loop-func 98 | "no-multi-str": 2, // http://eslint.org/docs/rules/no-multi-str 99 | "no-native-reassign": 2, // http://eslint.org/docs/rules/no-native-reassign 100 | "no-new": 2, // http://eslint.org/docs/rules/no-new 101 | "no-new-func": 2, // http://eslint.org/docs/rules/no-new-func 102 | "no-new-wrappers": 2, // http://eslint.org/docs/rules/no-new-wrappers 103 | "no-octal": 2, // http://eslint.org/docs/rules/no-octal 104 | "no-octal-escape": 2, // http://eslint.org/docs/rules/no-octal-escape 105 | "no-param-reassign": 2, // http://eslint.org/docs/rules/no-param-reassign 106 | "no-proto": 2, // http://eslint.org/docs/rules/no-proto 107 | "no-redeclare": 2, // http://eslint.org/docs/rules/no-redeclare 108 | "no-return-assign": 2, // http://eslint.org/docs/rules/no-return-assign 109 | "no-script-url": 2, // http://eslint.org/docs/rules/no-script-url 110 | "no-self-compare": 2, // http://eslint.org/docs/rules/no-self-compare 111 | "no-sequences": 2, // http://eslint.org/docs/rules/no-sequences 112 | "no-throw-literal": 2, // http://eslint.org/docs/rules/no-throw-literal 113 | "no-with": 2, // http://eslint.org/docs/rules/no-with 114 | "radix": 2, // http://eslint.org/docs/rules/radix 115 | "vars-on-top": 2, // http://eslint.org/docs/rules/vars-on-top 116 | "wrap-iife": [2, "any"], // http://eslint.org/docs/rules/wrap-iife 117 | "yoda": 2, // http://eslint.org/docs/rules/yoda 118 | 119 | /** 120 | * Style 121 | */ 122 | "indent": [2, 2], // http://eslint.org/docs/rules/ 123 | "brace-style": [2, // http://eslint.org/docs/rules/brace-style 124 | "1tbs", { 125 | "allowSingleLine": true 126 | }], 127 | "quotes": [ 128 | 2, "single", "avoid-escape" // http://eslint.org/docs/rules/quotes 129 | ], 130 | "camelcase": [2, { // http://eslint.org/docs/rules/camelcase 131 | "properties": "never" 132 | }], 133 | "comma-spacing": [2, { // http://eslint.org/docs/rules/comma-spacing 134 | "before": false, 135 | "after": true 136 | }], 137 | "comma-style": [2, "last"], // http://eslint.org/docs/rules/comma-style 138 | "eol-last": 2, // http://eslint.org/docs/rules/eol-last 139 | "func-names": 1, // http://eslint.org/docs/rules/func-names 140 | "key-spacing": [2, { // http://eslint.org/docs/rules/key-spacing 141 | "beforeColon": false, 142 | "afterColon": true 143 | }], 144 | "new-cap": [2, { // http://eslint.org/docs/rules/new-cap 145 | "newIsCap": true 146 | }], 147 | "no-multiple-empty-lines": [2, { // http://eslint.org/docs/rules/no-multiple-empty-lines 148 | "max": 2 149 | }], 150 | "no-nested-ternary": 2, // http://eslint.org/docs/rules/no-nested-ternary 151 | "no-new-object": 2, // http://eslint.org/docs/rules/no-new-object 152 | "no-spaced-func": 2, // http://eslint.org/docs/rules/no-spaced-func 153 | "no-trailing-spaces": 2, // http://eslint.org/docs/rules/no-trailing-spaces 154 | "no-wrap-func": 2, // http://eslint.org/docs/rules/no-wrap-func 155 | "no-underscore-dangle": 0, // http://eslint.org/docs/rules/no-underscore-dangle 156 | "one-var": [2, "never"], // http://eslint.org/docs/rules/one-var 157 | "padded-blocks": 0, // http://eslint.org/docs/rules/padded-blocks 158 | "semi": [2, "always"], // http://eslint.org/docs/rules/semi 159 | "semi-spacing": [2, { // http://eslint.org/docs/rules/semi-spacing 160 | "before": false, 161 | "after": true 162 | }], 163 | "space-after-keywords": 2, // http://eslint.org/docs/rules/space-after-keywords 164 | "space-before-blocks": 2, // http://eslint.org/docs/rules/space-before-blocks 165 | "space-before-function-paren": [2, "never"], // http://eslint.org/docs/rules/space-before-function-paren 166 | "space-infix-ops": 2, // http://eslint.org/docs/rules/space-infix-ops 167 | "space-return-throw-case": 2, // http://eslint.org/docs/rules/space-return-throw-case 168 | "spaced-line-comment": 2 // http://eslint.org/docs/rules/spaced-line-comment 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /examples/01 - Basic/README.md: -------------------------------------------------------------------------------- 1 | ## Example 01 - Basic 2 | 3 | #### About 4 | Demonstrates the most basic flow with `browserify-react-live` 5 | 6 | #### Getting started 7 | ``` 8 | npm install 9 | npm start 10 | ``` 11 | 12 | By default your server is ready at [http://localhost:8080](http://localhost:8080). 13 | 14 | You can now change anything, in, say `components` folder and your changes will propagate! 15 | 16 | See [browserify-react-live](https://github.com/Kureev/browserify-react-live) for more details. 17 | -------------------------------------------------------------------------------- /examples/01 - Basic/components/MyComponent.js: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | 3 | const styles = { 4 | button: { 5 | padding: 10, 6 | display: 'block', 7 | }, 8 | }; 9 | 10 | module.exports = class MyComponent extends React.Component { 11 | state = { 12 | counter: 0, 13 | } 14 | 15 | onClick() { 16 | this.setState({ counter: this.state.counter + 1, }); 17 | } 18 | 19 | render() { 20 | return ( 21 |
22 | Hello, world (said {this.state.counter} times) 23 | 26 |
27 | ); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /examples/01 - Basic/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Test for hot module replacement 5 | 6 | 7 |
8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/01 - Basic/index.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var MyComponent = require('./components/MyComponent'); 3 | 4 | React.render(( 5 |
6 | 7 |
8 | ), document.getElementById('playground')); 9 | -------------------------------------------------------------------------------- /examples/01 - Basic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "./scripts/run", 8 | "postinstall": "mkdir -p dist && ./node_modules/.bin/browserify index.js -o dist/bundle.js", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "Alexey Kureev (https://github.com/Kureev)", 12 | "license": "MIT", 13 | "browserify": { 14 | "transform": [ 15 | [ 16 | "babelify", 17 | { 18 | "stage": 0 19 | } 20 | ], 21 | "browserify-react-live" 22 | ] 23 | }, 24 | "devDependencies": { 25 | "babelify": "^6.1.3", 26 | "browserify": "^11.0.0", 27 | "browserify-patch-server": "^0.4", 28 | "browserify-react-live": "^2", 29 | "http-server": "^0.8.0", 30 | "watchify": "^3.3.0" 31 | }, 32 | "dependencies": { 33 | "react": "^0.13.3" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /examples/01 - Basic/scripts/run: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | node_modules/.bin/watchify index.js -o dist/bundle.js & 3 | node_modules/.bin/bfps components/* & 4 | node_modules/.bin/http-server . & 5 | wait 6 | -------------------------------------------------------------------------------- /examples/02 - Side effects/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "env": { 4 | "browser": true, 5 | "node": true, 6 | "jasmine": true 7 | }, 8 | "ecmaFeatures": { 9 | "arrowFunctions": true, 10 | "blockBindings": true, 11 | "classes": true, 12 | "defaultParams": true, 13 | "destructuring": true, 14 | "forOf": true, 15 | "generators": false, 16 | "modules": true, 17 | "objectLiteralComputedProperties": true, 18 | "objectLiteralDuplicateProperties": false, 19 | "objectLiteralShorthandMethods": true, 20 | "objectLiteralShorthandProperties": true, 21 | "spread": true, 22 | "superInFunctions": true, 23 | "templateStrings": true, 24 | "jsx": true 25 | }, 26 | "rules": { 27 | /** 28 | * Strict mode 29 | */ 30 | // babel inserts "use strict"; for us 31 | // http://eslint.org/docs/rules/strict 32 | "strict": [2, "never"], 33 | 34 | /** 35 | * ES6 36 | */ 37 | "no-var": 0, // http://eslint.org/docs/rules/no-var 38 | 39 | /** 40 | * Variables 41 | */ 42 | "no-shadow": 2, // http://eslint.org/docs/rules/no-shadow 43 | "no-shadow-restricted-names": 2, // http://eslint.org/docs/rules/no-shadow-restricted-names 44 | "no-unused-vars": [0, { // http://eslint.org/docs/rules/no-unused-vars 45 | "vars": "local", 46 | "args": "after-used" 47 | }], 48 | "no-use-before-define": 2, // http://eslint.org/docs/rules/no-use-before-define 49 | 50 | /** 51 | * Possible errors 52 | */ 53 | "comma-dangle": [2, "always"], // http://eslint.org/docs/rules/comma-dangle 54 | "no-cond-assign": [2, "always"], // http://eslint.org/docs/rules/no-cond-assign 55 | "no-console": 0, // http://eslint.org/docs/rules/no-console 56 | "no-debugger": 1, // http://eslint.org/docs/rules/no-debugger 57 | "no-alert": 1, // http://eslint.org/docs/rules/no-alert 58 | "no-constant-condition": 1, // http://eslint.org/docs/rules/no-constant-condition 59 | "no-dupe-keys": 2, // http://eslint.org/docs/rules/no-dupe-keys 60 | "no-duplicate-case": 2, // http://eslint.org/docs/rules/no-duplicate-case 61 | "no-empty": 2, // http://eslint.org/docs/rules/no-empty 62 | "no-ex-assign": 2, // http://eslint.org/docs/rules/no-ex-assign 63 | "no-extra-boolean-cast": 0, // http://eslint.org/docs/rules/no-extra-boolean-cast 64 | "no-extra-semi": 2, // http://eslint.org/docs/rules/no-extra-semi 65 | "no-func-assign": 2, // http://eslint.org/docs/rules/no-func-assign 66 | "no-inner-declarations": 2, // http://eslint.org/docs/rules/no-inner-declarations 67 | "no-invalid-regexp": 2, // http://eslint.org/docs/rules/no-invalid-regexp 68 | "no-irregular-whitespace": 2, // http://eslint.org/docs/rules/no-irregular-whitespace 69 | "no-obj-calls": 2, // http://eslint.org/docs/rules/no-obj-calls 70 | "no-reserved-keys": 0, // http://eslint.org/docs/rules/no-reserved-keys 71 | "no-sparse-arrays": 2, // http://eslint.org/docs/rules/no-sparse-arrays 72 | "no-unreachable": 2, // http://eslint.org/docs/rules/no-unreachable 73 | "use-isnan": 2, // http://eslint.org/docs/rules/use-isnan 74 | "block-scoped-var": 2, // http://eslint.org/docs/rules/block-scoped-var 75 | 76 | /** 77 | * Best practices 78 | */ 79 | "consistent-return": 2, // http://eslint.org/docs/rules/consistent-return 80 | "curly": [2, "multi-line"], // http://eslint.org/docs/rules/curly 81 | "default-case": 2, // http://eslint.org/docs/rules/default-case 82 | "dot-notation": [2, { // http://eslint.org/docs/rules/dot-notation 83 | "allowKeywords": true 84 | }], 85 | "eqeqeq": 2, // http://eslint.org/docs/rules/eqeqeq 86 | "guard-for-in": 2, // http://eslint.org/docs/rules/guard-for-in 87 | "no-caller": 2, // http://eslint.org/docs/rules/no-caller 88 | "no-else-return": 2, // http://eslint.org/docs/rules/no-else-return 89 | "no-eq-null": 2, // http://eslint.org/docs/rules/no-eq-null 90 | "no-eval": 2, // http://eslint.org/docs/rules/no-eval 91 | "no-extend-native": 2, // http://eslint.org/docs/rules/no-extend-native 92 | "no-extra-bind": 2, // http://eslint.org/docs/rules/no-extra-bind 93 | "no-fallthrough": 2, // http://eslint.org/docs/rules/no-fallthrough 94 | "no-floating-decimal": 2, // http://eslint.org/docs/rules/no-floating-decimal 95 | "no-implied-eval": 2, // http://eslint.org/docs/rules/no-implied-eval 96 | "no-lone-blocks": 2, // http://eslint.org/docs/rules/no-lone-blocks 97 | "no-loop-func": 2, // http://eslint.org/docs/rules/no-loop-func 98 | "no-multi-str": 2, // http://eslint.org/docs/rules/no-multi-str 99 | "no-native-reassign": 2, // http://eslint.org/docs/rules/no-native-reassign 100 | "no-new": 2, // http://eslint.org/docs/rules/no-new 101 | "no-new-func": 2, // http://eslint.org/docs/rules/no-new-func 102 | "no-new-wrappers": 2, // http://eslint.org/docs/rules/no-new-wrappers 103 | "no-octal": 2, // http://eslint.org/docs/rules/no-octal 104 | "no-octal-escape": 2, // http://eslint.org/docs/rules/no-octal-escape 105 | "no-param-reassign": 2, // http://eslint.org/docs/rules/no-param-reassign 106 | "no-proto": 2, // http://eslint.org/docs/rules/no-proto 107 | "no-redeclare": 2, // http://eslint.org/docs/rules/no-redeclare 108 | "no-return-assign": 2, // http://eslint.org/docs/rules/no-return-assign 109 | "no-script-url": 2, // http://eslint.org/docs/rules/no-script-url 110 | "no-self-compare": 2, // http://eslint.org/docs/rules/no-self-compare 111 | "no-sequences": 2, // http://eslint.org/docs/rules/no-sequences 112 | "no-throw-literal": 2, // http://eslint.org/docs/rules/no-throw-literal 113 | "no-with": 2, // http://eslint.org/docs/rules/no-with 114 | "radix": 2, // http://eslint.org/docs/rules/radix 115 | "vars-on-top": 2, // http://eslint.org/docs/rules/vars-on-top 116 | "wrap-iife": [2, "any"], // http://eslint.org/docs/rules/wrap-iife 117 | "yoda": 2, // http://eslint.org/docs/rules/yoda 118 | 119 | /** 120 | * Style 121 | */ 122 | "indent": [2, 2], // http://eslint.org/docs/rules/ 123 | "brace-style": [2, // http://eslint.org/docs/rules/brace-style 124 | "1tbs", { 125 | "allowSingleLine": true 126 | }], 127 | "quotes": [ 128 | 2, "single", "avoid-escape" // http://eslint.org/docs/rules/quotes 129 | ], 130 | "camelcase": [2, { // http://eslint.org/docs/rules/camelcase 131 | "properties": "never" 132 | }], 133 | "comma-spacing": [2, { // http://eslint.org/docs/rules/comma-spacing 134 | "before": false, 135 | "after": true 136 | }], 137 | "comma-style": [2, "last"], // http://eslint.org/docs/rules/comma-style 138 | "eol-last": 2, // http://eslint.org/docs/rules/eol-last 139 | "func-names": 1, // http://eslint.org/docs/rules/func-names 140 | "key-spacing": [2, { // http://eslint.org/docs/rules/key-spacing 141 | "beforeColon": false, 142 | "afterColon": true 143 | }], 144 | "new-cap": [2, { // http://eslint.org/docs/rules/new-cap 145 | "newIsCap": true 146 | }], 147 | "no-multiple-empty-lines": [2, { // http://eslint.org/docs/rules/no-multiple-empty-lines 148 | "max": 2 149 | }], 150 | "no-nested-ternary": 2, // http://eslint.org/docs/rules/no-nested-ternary 151 | "no-new-object": 2, // http://eslint.org/docs/rules/no-new-object 152 | "no-spaced-func": 2, // http://eslint.org/docs/rules/no-spaced-func 153 | "no-trailing-spaces": 2, // http://eslint.org/docs/rules/no-trailing-spaces 154 | "no-wrap-func": 2, // http://eslint.org/docs/rules/no-wrap-func 155 | "no-underscore-dangle": 0, // http://eslint.org/docs/rules/no-underscore-dangle 156 | "one-var": [2, "never"], // http://eslint.org/docs/rules/one-var 157 | "padded-blocks": 0, // http://eslint.org/docs/rules/padded-blocks 158 | "semi": [2, "always"], // http://eslint.org/docs/rules/semi 159 | "semi-spacing": [2, { // http://eslint.org/docs/rules/semi-spacing 160 | "before": false, 161 | "after": true 162 | }], 163 | "space-after-keywords": 2, // http://eslint.org/docs/rules/space-after-keywords 164 | "space-before-blocks": 2, // http://eslint.org/docs/rules/space-before-blocks 165 | "space-before-function-paren": [2, "never"], // http://eslint.org/docs/rules/space-before-function-paren 166 | "space-infix-ops": 2, // http://eslint.org/docs/rules/space-infix-ops 167 | "space-return-throw-case": 2, // http://eslint.org/docs/rules/space-return-throw-case 168 | "spaced-line-comment": 2 // http://eslint.org/docs/rules/spaced-line-comment 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /examples/02 - Side effects/README.md: -------------------------------------------------------------------------------- 1 | ## Example 02 - Side effects 2 | 3 | #### About 4 | Demonstrates partial bundle update with `browserify-react-live`. Especially handy when some parts of your bundle has side-effects you don't want to occur. 5 | 6 | #### Getting started 7 | ``` 8 | npm install 9 | npm start 10 | ``` 11 | 12 | By default your server is ready at [http://localhost:8080](http://localhost:8080). 13 | 14 | You can now change anything, in, say `components` folder and your changes will propagate! 15 | 16 | See [browserify-react-live](https://github.com/Kureev/browserify-react-live) for more details. 17 | -------------------------------------------------------------------------------- /examples/02 - Side effects/components/Dep.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | export default class Test extends Component { 4 | render() { 5 | return ( 6 |

Hi, I'm MyComponent2 dependency

7 | ); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/02 - Side effects/components/MyComponent.js: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | const theme = require('./theme'); 3 | 4 | alert('Hi, I\'m a side effect of MyComponent'); 5 | 6 | module.exports = class MyComponent extends React.Component { 7 | state = { 8 | counter: 0, 9 | } 10 | 11 | onClick() { 12 | this.setState({ counter: this.state.counter + 1, }); 13 | } 14 | 15 | render() { 16 | return ( 17 |
18 | Hello, world (said {this.state.counter} times) 19 | 22 |
23 | ); 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /examples/02 - Side effects/components/MyComponent2.js: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | const TestDependency = require('./Dep'); 3 | const theme = require('./theme'); 4 | 5 | module.exports = class MyComponent2 extends React.Component { 6 | state = { 7 | counter: 100, 8 | } 9 | 10 | onClick() { 11 | this.setState({ counter: this.state.counter - 1, }); 12 | } 13 | 14 | render() { 15 | return ( 16 |
17 | Counter is {this.state.counter} 18 | 21 | 22 |
23 | ); 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /examples/02 - Side effects/components/theme.js: -------------------------------------------------------------------------------- 1 | export default { 2 | button: { 3 | padding: 10, 4 | display: 'block', 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /examples/02 - Side effects/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Test for hot module replacement 5 | 6 | 7 |
8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/02 - Side effects/index.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var MyComponent = require('./components/MyComponent'); 3 | var MyComponent2 = require('./components/MyComponent2'); 4 | 5 | React.render(( 6 |
7 | 8 | 9 |
10 | ), document.getElementById('playground')); 11 | -------------------------------------------------------------------------------- /examples/02 - Side effects/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "./scripts/run", 8 | "postinstall": "mkdir -p dist && ./node_modules/.bin/browserify index.js -o dist/bundle.js", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "Alexey Kureev (https://github.com/Kureev)", 12 | "license": "MIT", 13 | "browserify": { 14 | "transform": [ 15 | [ 16 | "babelify", 17 | { 18 | "stage": 0 19 | } 20 | ], 21 | "browserify-react-live" 22 | ] 23 | }, 24 | "devDependencies": { 25 | "babelify": "^6.1.3", 26 | "browserify": "^11.0.0", 27 | "browserify-patch-server": "^0.4", 28 | "browserify-react-live": "^2", 29 | "http-server": "^0.8.0", 30 | "watchify": "^3.3.0" 31 | }, 32 | "dependencies": { 33 | "react": "^0.13.3" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /examples/02 - Side effects/scripts/run: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | node_modules/.bin/watchify index.js -o dist/bundle.js & 3 | node_modules/.bin/bfps components/* & 4 | node_modules/.bin/http-server . & 5 | wait 6 | -------------------------------------------------------------------------------- /examples/03 - Basic auth/README.md: -------------------------------------------------------------------------------- 1 | ## Example 03 - Basic auth 2 | 3 | #### About 4 | Demonstrates usage `browserify-react-live` with `react-router` and basic auth scenario. 5 | 6 | #### Getting started 7 | ``` 8 | npm install 9 | npm start 10 | ``` 11 | 12 | By default your server is ready at [http://localhost:8080](http://localhost:8080). 13 | 14 | You can now change anything, in, say `components` folder and your changes will propagate! 15 | 16 | See [browserify-react-live](https://github.com/Kureev/browserify-react-live) for more details. 17 | -------------------------------------------------------------------------------- /examples/03 - Basic auth/components/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class About extends React.Component { 4 | render() { 5 | return

About

; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /examples/03 - Basic auth/components/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import auth from '../vendor/auth'; 3 | import Router from 'react-router'; 4 | 5 | var { RouteHandler, Link } = Router; 6 | 7 | export default class App extends React.Component { 8 | state = { 9 | loggedIn: auth.loggedIn(), 10 | } 11 | 12 | setStateOnAuth(loggedIn) { 13 | this.setState({ 14 | loggedIn: loggedIn, 15 | }); 16 | } 17 | 18 | componentWillMount() { 19 | auth.onChange = this.setStateOnAuth.bind(this); 20 | auth.login(); 21 | } 22 | 23 | render() { 24 | return ( 25 |
26 |
    27 |
  • 28 | {this.state.loggedIn ? ( 29 | Log out 30 | ) : ( 31 | Sign in 32 | )} 33 |
  • 34 |
  • About
  • 35 |
  • Dashboard (authenticated)
  • 36 |
37 | 38 |
39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/03 - Basic auth/components/Dashboard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import auth from '../vendor/auth'; 3 | 4 | export default class Dashboard extends React.Component { 5 | render() { 6 | var token = auth.getToken(); 7 | return ( 8 |
9 |

Dashboard

10 |

You made it!

11 |

{token}

12 |
13 | ); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/03 - Basic auth/components/Login.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import auth from '../vendor/auth'; 3 | 4 | export default class Login extends React.Component { 5 | constructor() { 6 | super(); 7 | this.handleSubmit = this.handleSubmit.bind(this); 8 | this.state = { 9 | error: false, 10 | }; 11 | } 12 | 13 | handleSubmit(event) { 14 | event.preventDefault(); 15 | var { router } = this.context; 16 | var nextPath = router.getCurrentQuery().nextPath; 17 | var email = this.refs.email.getDOMNode().value; 18 | var pass = this.refs.pass.getDOMNode().value; 19 | auth.login(email, pass, (loggedIn) => { 20 | if (!loggedIn) { 21 | return this.setState({ error: true, }); 22 | } 23 | 24 | if (nextPath) { 25 | router.replaceWith(nextPath); 26 | } else { 27 | router.replaceWith('/about'); 28 | } 29 | }); 30 | } 31 | 32 | render() { 33 | return ( 34 |
35 | 36 | (hint: password1)
37 | 38 | {this.state.error && ( 39 |

Bad login information

40 | )} 41 |
42 | ); 43 | } 44 | 45 | static contextTypes = { 46 | router: React.PropTypes.func, 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /examples/03 - Basic auth/components/Logout.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import auth from '../vendor/auth'; 3 | 4 | export default class Logout extends React.Component { 5 | componentDidMount() { 6 | auth.logout(); 7 | } 8 | 9 | render() { 10 | return

You are now logged out

; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/03 - Basic auth/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Test for hot module replacement 5 | 6 | 7 |
8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/03 - Basic auth/index.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var Router = require('react-router'); 3 | var auth = require('./vendor/auth'); 4 | var Login = require('./components/Login'); 5 | var Dashboard = require('./components/Dashboard'); 6 | var Logout = require('./components/Logout'); 7 | var About = require('./components/About'); 8 | var App = require('./components/App'); 9 | var requireAuth = require('./vendor/requireAuth'); 10 | var { Route } = Router; 11 | 12 | var Dashboard = requireAuth(Dashboard); 13 | 14 | var routes = ( 15 | 16 | 17 | 18 | 19 | 20 | 21 | ); 22 | 23 | Router.run(routes, function(Handler) { 24 | React.render(, document.getElementById('playground')); 25 | }); 26 | -------------------------------------------------------------------------------- /examples/03 - Basic auth/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "./scripts/run", 8 | "postinstall": "mkdir -p dist && ./node_modules/.bin/browserify index.js -o dist/bundle.js", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "Alexey Kureev (https://github.com/Kureev)", 12 | "license": "MIT", 13 | "browserify": { 14 | "transform": [ 15 | [ 16 | "babelify", 17 | { 18 | "stage": 0 19 | } 20 | ], 21 | "browserify-react-live" 22 | ] 23 | }, 24 | "devDependencies": { 25 | "babelify": "^6.1.3", 26 | "browserify": "^11.0.0", 27 | "browserify-patch-server": "^0.4", 28 | "browserify-react-live": "^2", 29 | "http-server": "^0.8.0", 30 | "watchify": "^3.3.0" 31 | }, 32 | "dependencies": { 33 | "react": "^0.13.3", 34 | "react-router": "^0.13.3" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /examples/03 - Basic auth/scripts/run: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | node_modules/.bin/watchify index.js -o dist/bundle.js & 3 | node_modules/.bin/bfps components/* & 4 | node_modules/.bin/http-server . & 5 | wait 6 | -------------------------------------------------------------------------------- /examples/03 - Basic auth/vendor/auth.js: -------------------------------------------------------------------------------- 1 | function pretendRequest(email, pass, cb) { 2 | setTimeout(() => { 3 | if (email === 'joe@example.com' && pass === 'password1') { 4 | cb({ 5 | authenticated: true, 6 | token: Math.random().toString(36).substring(7) 7 | }); 8 | } else { 9 | cb({authenticated: false}); 10 | } 11 | }, 0); 12 | } 13 | 14 | export default { 15 | login (email, pass, cb) { 16 | cb = arguments[arguments.length - 1]; 17 | if (localStorage.token) { 18 | if (cb) cb(true); 19 | this.onChange(true); 20 | return; 21 | } 22 | pretendRequest(email, pass, (res) => { 23 | if (res.authenticated) { 24 | localStorage.token = res.token; 25 | if (cb) cb(true); 26 | this.onChange(true); 27 | } else { 28 | if (cb) cb(false); 29 | this.onChange(false); 30 | } 31 | }); 32 | }, 33 | 34 | getToken: function () { 35 | return localStorage.token; 36 | }, 37 | 38 | logout: function (cb) { 39 | delete localStorage.token; 40 | if (cb) cb(); 41 | this.onChange(false); 42 | }, 43 | 44 | loggedIn: function () { 45 | return !!localStorage.token; 46 | }, 47 | 48 | onChange: function () {} 49 | }; 50 | -------------------------------------------------------------------------------- /examples/03 - Basic auth/vendor/requireAuth.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import auth from './auth'; 3 | 4 | export default (Component) => { 5 | return class Authenticated extends React.Component { 6 | static willTransitionTo(transition) { 7 | if (!auth.loggedIn()) { 8 | transition.redirect('/login', {}, { 9 | 'nextPath': transition.path, 10 | }); 11 | } 12 | } 13 | render() { 14 | return ; 15 | } 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./src/transform'); 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "browserify-react-live", 3 | "version": "3.1.2", 4 | "description": "React live module reload for browserify", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Alexey Kureev (https://github.com/Kureev)", 10 | "license": "MIT", 11 | "dependencies": { 12 | "diff": "^1.4.0", 13 | "logdown": "^1.2.4", 14 | "minimatch": "^2.0.10", 15 | "moment": "^2.10.3", 16 | "react-proxy": "^0.6", 17 | "strip-ansi": "^3.0.0", 18 | "through2": "^2.0.0", 19 | "ws": "^0.7.2" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Proxies.js: -------------------------------------------------------------------------------- 1 | var ReactProxy = require('react-proxy'); 2 | 3 | function isWindows() { 4 | return navigator.platform.indexOf('Win') > -1; 5 | } 6 | 7 | function isObject(toTest) { 8 | return Object.prototype.toString.apply(toTest) === '[object Object]'; 9 | } 10 | 11 | function isFunction(toTest) { 12 | return Object.prototype.toString.apply(toTest) === '[object Function]'; 13 | } 14 | 15 | function isReactComponent(component) { 16 | return isFunction(component) && 17 | isFunction(component.prototype.render); 18 | } 19 | 20 | function Proxies() { 21 | this.proxies = {}; 22 | } 23 | 24 | Proxies.prototype = { 25 | get: function get(name) { 26 | // Preprocess name to shrink extension if supplied 27 | var proxyName = name.replace(/\.(js|jsx)$/, ''); 28 | var proxy = this.proxies[proxyName]; 29 | 30 | // If we don't have an exact match, 31 | // try to find by part of the name 32 | if (!proxy) { 33 | proxyName = Object.keys(this.proxies).filter(function iterateProxies(p) { 34 | if (isWindows()) { 35 | return proxyName.slice(-p.length) === p; 36 | } 37 | 38 | return p.slice(-proxyName.length) === proxyName; 39 | })[0]; 40 | 41 | proxy = this.proxies[proxyName]; 42 | } 43 | 44 | return proxy; 45 | }, 46 | 47 | add: function add(name, component) { 48 | if (!name || !component) { 49 | return false; 50 | } 51 | 52 | var proxy; 53 | 54 | // If we already have a proxy with 55 | // this name, just return it 56 | if (proxy = this.get(name)) { 57 | return proxy; 58 | } 59 | 60 | // We can proxy only React Classes 61 | if (isReactComponent(component)) { 62 | proxy = ReactProxy.createProxy(component); 63 | this.proxies[name] = proxy; 64 | 65 | return proxy; 66 | } 67 | 68 | return false; 69 | }, 70 | 71 | update: function update(name, newComponent) { 72 | var proxy = this.get(name); 73 | if (proxy) { 74 | return proxy.update(newComponent); 75 | } 76 | 77 | return false; 78 | }, 79 | }; 80 | 81 | module.exports = Proxies; 82 | -------------------------------------------------------------------------------- /src/StaticModules.js: -------------------------------------------------------------------------------- 1 | function StaticModules() { 2 | this.modules = {}; 3 | } 4 | 5 | function isWindows() { 6 | return navigator.platform.indexOf('Win') > -1; 7 | } 8 | 9 | StaticModules.prototype = { 10 | get: function get(name) { 11 | var moduleName = name 12 | .replace(/\.(js|jsx)$/, '') 13 | .replace(/^\//, ''); 14 | 15 | var module = this.modules[moduleName]; 16 | 17 | // If we don't have an exact match, 18 | // try to find by part of the name 19 | if (!module) { 20 | moduleName = Object.keys(this.modules).filter(function iterateModules(p) { 21 | if (isWindows()) { 22 | return moduleName.slice(-p.length) === p; 23 | } 24 | 25 | return p.slice(-moduleName.length) === moduleName; 26 | })[0]; 27 | 28 | module = this.modules[moduleName]; 29 | } 30 | 31 | return module; 32 | }, 33 | 34 | add: function add(name, content) { 35 | if (!name || !content) { 36 | return false; 37 | } 38 | 39 | var moduleName = name 40 | .replace(/\.(js|jsx)$/, '') 41 | .replace(/^\//, ''); 42 | 43 | this.modules[moduleName] = content; 44 | }, 45 | 46 | update: function update(name, content) { 47 | var module = this.get(name); 48 | 49 | if (module) { 50 | this.add(name, content); 51 | } else { 52 | throw Error('Can\'t update ' + name + ' before it has been set'); 53 | } 54 | } 55 | }; 56 | 57 | module.exports = StaticModules; 58 | -------------------------------------------------------------------------------- /src/actions/applyPatch.js: -------------------------------------------------------------------------------- 1 | var moment = require('moment'); 2 | var React = require('react'); 3 | var ReactProxy = require('react-proxy'); 4 | var bytesToKb = require('./bytesToKb'); 5 | var diff = require('diff'); 6 | 7 | var forceUpdate = ReactProxy.getForceUpdate(React); 8 | 9 | /** 10 | * Parse patch file and log changes in a fancy way 11 | * @param {String} patch 12 | * @return {Void} 13 | */ 14 | function logChanges(patch) { 15 | patch.split('\n').forEach(function iteratePatch(str) { 16 | if (str.match(/^\+\s/)) { 17 | console.log('%c' + str.replace(/^\+\s+/, '+ '), 'color: green'); 18 | } else if (str.match(/^\-\s/)) { 19 | console.log('%c' + str.replace(/^-\s+/, '- '), 'color: red'); 20 | } 21 | }); 22 | } 23 | 24 | /** 25 | * Check if react hot replacement can 26 | * be applied to the module 27 | * @param {Object} module 28 | * @return {Boolean} 29 | */ 30 | function canBePatched(module) { 31 | return module.exports && 32 | (typeof module.exports === 'function') && 33 | (module.exports.name || module.exports.displayName); 34 | } 35 | 36 | module.exports = function applyPatch(scope, source, data) { 37 | var timestamp = '['+ moment().format('HH:mm:ss') + ']'; 38 | 39 | console.groupCollapsed(timestamp, 'Patch for', 40 | data.file, '(' + bytesToKb(data.patch.length) + 'kb)'); 41 | 42 | logChanges(data.patch); 43 | 44 | var patched = diff.applyPatch(source, data.patch); 45 | 46 | // Build full file path for require 47 | // var filename = scope.__root + '/' + data.file; 48 | var filename = data.file; 49 | 50 | // Building require for specific filename 51 | // In our custom require we'll use filename to scope relative paths 52 | var __require = require('../overrideRequire')(scope, require, filename); 53 | 54 | // Create anon function for our patched source 55 | var f = Function(['require', 'module', 'exports', ], patched); 56 | 57 | // Mock module to get function result 58 | var _module = {}; 59 | 60 | // Run module like as browserify does 61 | f(__require, _module, {}); 62 | 63 | if (canBePatched(_module)) { 64 | var mountedInstances = scope.proxies.update(filename, _module.exports); 65 | if (!mountedInstances) { 66 | throw Error('Can\'t find ' + filename + ' in mounted instances'); 67 | } 68 | 69 | mountedInstances.forEach(forceUpdate); 70 | } else { 71 | scope.modules.update(filename, _module.exports); 72 | } 73 | 74 | scope.files.forEach(function iterateBundles(file) { 75 | if (file.file === data.file) { 76 | file.content = patched; 77 | } 78 | }); 79 | 80 | console.groupEnd(); 81 | }; 82 | -------------------------------------------------------------------------------- /src/actions/bytesToKb.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Convert bytes to kb + round it to xx.xx mask 3 | * @param {Number} bytes 4 | * @return {Number} 5 | */ 6 | module.exports = function bytesToKb(bytes) { 7 | return Math.round((bytes / 1024) * 100) / 100; 8 | }; 9 | -------------------------------------------------------------------------------- /src/actions/processError.js: -------------------------------------------------------------------------------- 1 | var moment = require('moment'); 2 | var Logdown = require('logdown'); 3 | 4 | var error = new Logdown({ prefix: '[BPS:ERROR]', }); 5 | var stripAnsi = require('strip-ansi'); 6 | 7 | module.exports = function processError(data) { 8 | var timestamp = '['+ moment().format('HH:mm:ss') + ']'; 9 | return error.error(timestamp, ' Error in file ' + 10 | data.file + ':\n' + stripAnsi(data.error)); 11 | }; 12 | -------------------------------------------------------------------------------- /src/actions/storeSources.js: -------------------------------------------------------------------------------- 1 | var moment = require('moment'); 2 | var Logdown = require('logdown'); 3 | var bytesToKb = require('./bytesToKb'); 4 | 5 | var system = new Logdown({ prefix: '[BPS:SYSTEM]', }); 6 | 7 | module.exports = function storeSources(scope, sources) { 8 | var timestamp = '['+ moment().format('HH:mm:ss') + ']'; 9 | scope.files = scope.files || sources; 10 | 11 | console.groupCollapsed(timestamp, 'Initialized react-live-patch'); 12 | 13 | scope.files.forEach(function iterateSources(source) { 14 | system.log(timestamp + ' File *' + source.file + '*(' + 15 | bytesToKb(source.content.length) + 'Kb) has been added to react-live-patch'); 16 | }); 17 | 18 | console.groupEnd(); 19 | }; 20 | -------------------------------------------------------------------------------- /src/injectWebSocket.js: -------------------------------------------------------------------------------- 1 | var processError = require('./actions/processError'); 2 | var storeSources = require('./actions/storeSources'); 3 | var applyPatch = require('./actions/applyPatch'); 4 | 5 | module.exports = function injectWebSocket(scope, req, port) { 6 | scope.ws = new WebSocket('ws://localhost:' + (port || 8081)); 7 | scope.ws.onmessage = function onMessage(res) { 8 | var data = JSON.parse(res.data); 9 | 10 | /** 11 | * Handle error reports from patch-server 12 | * @param {String} data.error 13 | */ 14 | if (data.error) { 15 | processError(data); 16 | } 17 | 18 | /** 19 | * Setup initial files 20 | * @param {String} data.sources 21 | */ 22 | if (data.sources) { 23 | storeSources(scope, data.sources); 24 | } 25 | 26 | /** 27 | * Apply patch to initial file 28 | * @param {Diff} data.patch 29 | */ 30 | if (data.patch) { 31 | var source = scope.files 32 | .filter(function filterBundle(file) { 33 | return file.file === data.file; 34 | }) 35 | .map(function getContent(file) { 36 | return file.content; 37 | }) 38 | .pop(); 39 | 40 | applyPatch(scope, source, data); 41 | } 42 | }; 43 | 44 | return scope.ws; 45 | }; 46 | -------------------------------------------------------------------------------- /src/overrideRequire.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | function isNodeModule(name) { 4 | return name[0] !== '.'; 5 | } 6 | 7 | function makeAddStaticModule(scope) { 8 | return function addStaticModule(name, content) { 9 | var module = scope.modules.get(name); 10 | 11 | if (!module) { 12 | module = content 13 | } 14 | 15 | scope.modules.add(name, module); 16 | 17 | return scope.modules.get(name); 18 | } 19 | } 20 | 21 | module.exports = function overrideRequire(scope, req, filename) { 22 | var addStaticModule = makeAddStaticModule(scope); 23 | 24 | return function overrideRequire(name) { 25 | if (isNodeModule(name)) { 26 | return addStaticModule(name, req(name)); 27 | } 28 | 29 | var fullName = path.resolve(path.dirname(filename), name); 30 | 31 | if (fullName.indexOf(scope.baseDir) === 0) { 32 | fullName = fullName.slice(scope.baseDir.length); 33 | } 34 | 35 | // Try to find stored proxy by name 36 | var proxy = scope.proxies.get(fullName); 37 | 38 | // If succeeded, return proxied component 39 | if (proxy) { 40 | return proxy.get(); 41 | } 42 | 43 | // Otherwise try to include a module 44 | if (module = scope.modules.get(fullName || name)) { 45 | return module; 46 | } 47 | 48 | var module = req(name); 49 | 50 | // And then proxy it 51 | proxy = scope.proxies.add(fullName, module); 52 | 53 | if (proxy) { 54 | return proxy.get(); 55 | } 56 | 57 | // if it's not proxible, add it to static 58 | addStaticModule(fullName, module); 59 | 60 | return module; 61 | }; 62 | }; 63 | -------------------------------------------------------------------------------- /src/transform.js: -------------------------------------------------------------------------------- 1 | const through = require('through2'); 2 | const pjson = require('../package.json'); 3 | const path = require('path'); 4 | 5 | /** 6 | * Resolve path to library file 7 | * @param {String} file 8 | * @return {String} 9 | */ 10 | function pathTo(file) { 11 | return pjson.name + '/src/' + file; 12 | } 13 | 14 | /** 15 | * Initialize react live patch 16 | * @description Inject React & WS, create namespace 17 | * @param {Object} options 18 | * @return {String} 19 | */ 20 | function initialize(options) { 21 | var port = options.port; 22 | var baseDir = options.baseDir; 23 | 24 | return '\n' + 25 | 'var $$scope = window.__RLP = (window.__RLP || {});\n' + 26 | 'if (!$$scope.initialized) {\n' + 27 | 'var Proxies = require("' + pathTo('Proxies') + '");\n' + 28 | 'var StaticModules = require("' + pathTo('StaticModules') + '");\n' + 29 | 'require("' + pathTo('injectWebSocket') + '")($$scope, require, ' + port + ');\n' + 30 | '$$scope.baseDir = "' + baseDir + '";\n' + 31 | '$$scope.modules = new StaticModules();\n' + 32 | '$$scope.proxies = new Proxies();\n' + 33 | '$$scope.initialized = true;\n' + 34 | '}\n'; 35 | } 36 | 37 | /** 38 | * Check if file is JSON (duck test) 39 | * @param {String} file 40 | * @return {Boolean} 41 | */ 42 | function isJSON(file) { 43 | return file.slice(-4) === 'json'; 44 | } 45 | 46 | /** 47 | * Generate overriden require function 48 | * @param {String} filename 49 | * @return {String} Overriden require injection 50 | */ 51 | function overrideRequire(filename) { 52 | return 'require = require("' + pathTo('overrideRequire') + '")' + 53 | '($$scope, require, "' + filename + '");\n'; 54 | } 55 | 56 | module.exports = function applyReactHotAPI(file, options) { 57 | var content = []; 58 | var port = options.port; 59 | 60 | return through( 61 | function transform(part, enc, next) { 62 | content.push(part); next(); 63 | }, 64 | 65 | function finish(done) { 66 | var prelude = ''; 67 | content = content.join(''); 68 | 69 | if (!isJSON(file)) { 70 | prelude = initialize({ 71 | port: port, 72 | baseDir: path.resolve('./'), 73 | }) + overrideRequire(file); 74 | } 75 | 76 | this.push(prelude + content); done(); 77 | } 78 | ); 79 | }; 80 | --------------------------------------------------------------------------------