├── .gitignore ├── README.md ├── example ├── MyComponent.css ├── MyComponent.js ├── main.js ├── package.json └── routes.js ├── lib ├── RWB.js ├── StandardWebpack.js ├── Workflows.js ├── entrypoint.js ├── index.html ├── index.js ├── main.js └── template │ ├── MyComponent.css │ ├── MyComponent.js │ ├── main.js │ └── routes.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | example/dist -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # don't use this -- use create react app instead! 2 | -------------------------------------------------------------------------------- /example/MyComponent.css: -------------------------------------------------------------------------------- 1 | .text { 2 | text-decoration: underline; 3 | } 4 | 5 | .MyComponent { 6 | color: red; 7 | font-family: sans-serif; 8 | } 9 | -------------------------------------------------------------------------------- /example/MyComponent.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var React = require('react'); 4 | 5 | var styles = require('./MyComponent.css'); 6 | 7 | var MyComponent = React.createClass({ 8 | render: function() { 9 | return ( 10 |
Hello, world!
11 | ); 12 | }, 13 | }); 14 | 15 | module.exports = MyComponent; 16 | -------------------------------------------------------------------------------- /example/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var MyComponent = require('./MyComponent'); 4 | 5 | module.exports = MyComponent; 6 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | }, 13 | "devDependencies": { 14 | }, 15 | "keywords": [ 16 | "react" 17 | ], 18 | "react": { 19 | "main": "./main.js", 20 | "rwb": "0.0.1" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /example/routes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var MyComponent = require('./MyComponent'); 4 | var React = require('react'); 5 | var {Route} = require('react-router'); 6 | 7 | module.exports = ; 8 | -------------------------------------------------------------------------------- /lib/RWB.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var React = require('react'); 4 | var Router = require('react-router'); 5 | 6 | var history = null; 7 | var router = null; 8 | 9 | var RWB = { 10 | getHistory: function() { 11 | if (!history) { 12 | history = RWB_NO_HISTORY ? Router.createMemoryHistory('/') : Router.browserHistory; 13 | } 14 | return history; 15 | }, 16 | 17 | getRouter: function() { 18 | if (!router) { 19 | // Lazily require() the routes because there may be circular dependencies. 20 | router = React.createElement(Router.Router, {history: RWB.getHistory()}, require('RWB_REACT_ROUTES')); 21 | } 22 | return router; 23 | }, 24 | }; 25 | 26 | module.exports = RWB; 27 | -------------------------------------------------------------------------------- /lib/StandardWebpack.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var invariant = require('invariant'); 4 | 5 | var REQUIRED_EXTENSIONS = ['css', 'jpg', 'png', 'svg', 'js', 'json']; 6 | 7 | function doesLoaderMatch(loaders, extension) { 8 | for (var i = 0; i < loaders.length; i++) { 9 | if (loaders[i].test.test('a.' + extension)) { 10 | return true; 11 | } 12 | } 13 | return false; 14 | } 15 | 16 | function noopRequire(module, filename) { 17 | module._compile('', filename); 18 | } 19 | 20 | var StandardWebpack = { 21 | validate: function(webpackConfig) { 22 | // throw an exception if the correct loader extensions aren't configured 23 | invariant(webpackConfig.hasOwnProperty('module'), 'Missing `module` field'); 24 | invariant(Array.isArray(webpackConfig.module.loaders), '`module.loaders` is not an array'); 25 | invariant(webpackConfig.hasOwnProperty('resolve'), 'Missing `resolve` field'); 26 | invariant(webpackConfig.resolve.hasOwnProperty('alias'), 'Missing `resolve.alias` field'); 27 | invariant(webpackConfig.resolve.alias.hasOwnProperty('react'), 'You must add a `resolve.alias.react` field'); 28 | invariant(webpackConfig.resolve.hasOwnProperty('extensions'), 'Missing `resolve.extensions` field'); 29 | invariant(webpackConfig.resolve.extensions.indexOf('.js') > -1, '.js missing from `resolve.extensions`'); 30 | invariant(webpackConfig.resolve.extensions.indexOf('.web.js') > -1, '.web.js missing from `resolve.extensions`'); 31 | REQUIRED_EXTENSIONS.forEach(function(extension) { 32 | var loaders = webpackConfig.module.loaders; 33 | invariant( 34 | doesLoaderMatch(webpackConfig.module.loaders, extension), 35 | 'You do not have a loader that handles files with the extension: %s', 36 | extension 37 | ); 38 | }); 39 | }, 40 | }; 41 | 42 | module.exports = StandardWebpack; 43 | -------------------------------------------------------------------------------- /lib/Workflows.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var ReactDOMServer = require('react-dom/server'); 4 | var StandardWebpack = require('./StandardWebpack'); 5 | var WebpackDevServer = require('webpack-dev-server'); 6 | 7 | var fs = require('fs'); 8 | var myPackageJson = require('../package.json'); 9 | var ncp = require('ncp'); 10 | var open = require('open'); 11 | var path = require('path'); 12 | var temp = require('temp'); 13 | var webpack = require('webpack'); 14 | 15 | // TODO: SSL? 16 | // TODO: document these 17 | var PORT = process.env.RWB_PORT || 3000; 18 | var PUBLIC_URL = process.env.RWB_PUBLIC_URL || 'http://localhost:' + PORT + '/'; 19 | var SKIP_SOURCEMAPS = !!JSON.parse(process.env.RWB_SKIP_SOURCEMAPS || 'false'); 20 | var DOM_NODE_ID = process.env.RWB_DOM_NODE_ID || '.react-root'; 21 | 22 | function errGuard(err) { 23 | if (err) { 24 | console.error(err); 25 | process.exit(1); 26 | } 27 | } 28 | 29 | var Workflows = { 30 | init: function(args) { 31 | var packageJson = path.join( 32 | args[0] || process.cwd(), 33 | 'package.json' 34 | ); 35 | 36 | if (!fs.existsSync(packageJson)) { 37 | console.error(packageJson + ' does not exist. Did you forget to run `npm init`?'); 38 | process.exit(1); 39 | } 40 | 41 | var packageJsonData = require(packageJson); 42 | 43 | if (packageJsonData.react) { 44 | console.error('This project has already been created.'); 45 | process.exit(1); 46 | } 47 | 48 | packageJsonData.dependencies = packageJsonData.dependencies || {}; 49 | packageJsonData.dependencies.react = packageJsonData.dependencies.react || myPackageJson.dependencies.react; 50 | 51 | packageJsonData.devDependencies = packageJsonData.devDependencies || {}; 52 | packageJsonData.keywords = packageJsonData.keywords || []; 53 | if (packageJsonData.keywords.indexOf('react') === -1) { 54 | packageJsonData.keywords.push('react'); 55 | } 56 | packageJsonData.react = packageJsonData.react || {}; 57 | packageJsonData.react.main = packageJsonData.react.main || './main.js'; 58 | packageJsonData.react.rwb = myPackageJson.version; 59 | 60 | packageJsonData.scripts = packageJsonData.scripts || {}; 61 | packageJsonData.scripts.start = 'rwb serve'; 62 | 63 | fs.writeFileSync(packageJson, JSON.stringify(packageJsonData, undefined, 2), {encoding: 'utf8'}); 64 | 65 | ncp(path.join(__dirname, 'template'), process.cwd(), function(err) { 66 | errGuard(err); 67 | console.log('Project created. Don\'t forget to run `npm install`, since some dependencies may have changed.'); 68 | }); 69 | }, 70 | 71 | serve: function(args) { 72 | args[0] = args[0] || '.'; 73 | var packageRoot = path.resolve(args[0]); 74 | var packageJson = path.join(packageRoot, 'package.json'); 75 | 76 | if (!fs.existsSync(packageJson)) { 77 | console.error(packageJson + ' does not exist.'); 78 | process.exit(1); 79 | } 80 | 81 | var packageJsonData = require(packageJson); 82 | 83 | if (!packageJsonData.react || !packageJsonData.react.main) { 84 | console.error( 85 | 'react.main key does not exist in: ' + packageJson + '. Did you forget to run `rwb init`?' 86 | ); 87 | process.exit(1); 88 | } 89 | 90 | temp.track(); 91 | temp.mkdir('rwb-serve', function(err, dirPath) { 92 | errGuard(err); 93 | fs.writeFileSync( 94 | path.join(dirPath, 'index.html'), 95 | fs.readFileSync( 96 | path.join(__dirname, 'index.html') 97 | ) 98 | ); 99 | 100 | var webpackEntryPath = path.join(__dirname, 'entrypoint.js'); 101 | 102 | var config = { 103 | devtool: SKIP_SOURCEMAPS ? undefined : 'cheap-module-eval-source-map', 104 | entry: [ 105 | require.resolve('webpack-dev-server/client') + '?' + PUBLIC_URL, 106 | require.resolve('webpack/hot/only-dev-server'), 107 | webpackEntryPath, 108 | 'react-hot-loader/patch', 109 | ], 110 | output: { 111 | path: dirPath, 112 | publicPath: PUBLIC_URL, 113 | filename: 'bundle.js', 114 | devtoolModuleFilenameTemplate: '[absolute-resource-path]', 115 | }, 116 | plugins: [ 117 | new webpack.DefinePlugin({ 118 | 'RWB_DOM_NODE_ID': JSON.stringify(DOM_NODE_ID), 119 | 'RWB_NO_HISTORY': JSON.stringify(false), 120 | }), 121 | new webpack.HotModuleReplacementPlugin(), 122 | new webpack.NoErrorsPlugin() 123 | ], 124 | resolve: { 125 | alias: { 126 | RWB_REACT_MAIN: path.join( 127 | packageRoot, 128 | packageJsonData.react.main 129 | ), 130 | react: path.dirname(require.resolve('react')), 131 | 'react-dom': path.dirname(require.resolve('react-dom')), 132 | }, 133 | extensions: ['.web.js', '', '.js', '.json'], 134 | }, 135 | module: { 136 | loaders: [ 137 | { 138 | test: /\.js$/, 139 | loader: require.resolve('babel-loader'), 140 | query: { 141 | presets: ['es2015', 'react', 'stage-3'] 142 | }, 143 | include: packageRoot, 144 | }, 145 | { 146 | test: /\.json$/, 147 | loader: require.resolve('json-loader'), 148 | }, 149 | { 150 | test: /\.css$/, 151 | loader: require.resolve('style-loader'), 152 | }, 153 | { 154 | test: /\.css$/, 155 | loader: require.resolve('css-loader') + '?safe', 156 | }, 157 | { 158 | test: /\.css$/, 159 | loader: require.resolve('autoprefixer-loader'), 160 | }, 161 | { 162 | test: /\.(png|jpg|svg)$/, 163 | loader: require.resolve('url-loader') + '?limit=8192' 164 | }, 165 | ] 166 | }, 167 | }; 168 | 169 | StandardWebpack.validate(config); 170 | 171 | var compiler = webpack(config); 172 | new WebpackDevServer(compiler, { 173 | contentBase: dirPath, 174 | publicPath: config.output.publicPath, 175 | hot: true, 176 | historyApiFallback: true, 177 | headers: { 178 | 'Access-Control-Allow-Origin': '*', 179 | }, 180 | }).listen(PORT, function (err, result) { 181 | errGuard(err); 182 | console.log('Serving ' + dirPath + ' at ' + PUBLIC_URL); 183 | }); 184 | 185 | var opened = false; 186 | compiler.plugin('done', function() { 187 | if (!opened) { 188 | open(PUBLIC_URL); 189 | opened = true; 190 | } 191 | }); 192 | }); 193 | }, 194 | 195 | validate: function(args) { 196 | if (!args[0]) { 197 | console.error('You must pass a path to a webpack config module'); 198 | process.exit(1); 199 | } 200 | if (!fs.existsSync(args[0])) { 201 | console.error(args[0] + ' does not exist.'); 202 | process.exit(1); 203 | } 204 | StandardWebpack.validate(require(path.resolve(args[0]))); 205 | }, 206 | }; 207 | 208 | module.exports = Workflows; 209 | -------------------------------------------------------------------------------- /lib/entrypoint.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('react-hot-loader/patch'); 4 | 5 | var ReactHotLoader = require('react-hot-loader'); 6 | var React = require('react'); 7 | var ReactDOM = require('react-dom'); 8 | 9 | ReactDOM.render( 10 | React.createElement(ReactHotLoader.AppContainer, null, React.createElement(require('RWB_REACT_MAIN'))), 11 | document.getElementById(RWB_DOM_NODE_ID) 12 | ); 13 | 14 | if (module.hot) { 15 | module.hot.accept('RWB_REACT_MAIN', function() { 16 | ReactDOM.render( 17 | React.createElement(ReactHotLoader.AppContainer, null, React.createElement(require('RWB_REACT_MAIN'))), 18 | document.getElementById(RWB_DOM_NODE_ID) 19 | ); 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /lib/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | rwb 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var index = { 4 | main: function() { 5 | require('./main'); 6 | }, 7 | 8 | validate: function(config) { 9 | var StandardWebpack = require('./StandardWebpack'); 10 | 11 | return StandardWebpack.validate(config); 12 | }, 13 | }; 14 | 15 | module.exports = index; 16 | -------------------------------------------------------------------------------- /lib/main.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | 4 | var Workflows = require('./Workflows'); 5 | 6 | var argv = require('yargs').argv; 7 | 8 | var command = argv._[0]; 9 | if (!command) { 10 | console.error('No command provided.\nSee https://github.com/petehunt/rwb for help.'); 11 | process.exit(1); 12 | } 13 | if (!Workflows.hasOwnProperty(command)) { 14 | console.error( 15 | 'Invalid command: ' + command + ' (acceptable commands: ' + Object.keys(Workflows).join(', ') + ')' + 16 | '\nSee https://github.com/petehunt/rwb for help.' 17 | ); 18 | process.exit(1); 19 | } 20 | 21 | Workflows[command](argv._.slice(1)); 22 | -------------------------------------------------------------------------------- /lib/template/MyComponent.css: -------------------------------------------------------------------------------- 1 | :local(.MyComponent) { 2 | color: blue; 3 | font-family: sans-serif; 4 | } -------------------------------------------------------------------------------- /lib/template/MyComponent.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var React = require('react'); 4 | 5 | var styles = require('./MyComponent.css'); 6 | 7 | var MyComponent = React.createClass({ 8 | render: function() { 9 | return ( 10 |
Hello, world!
11 | ); 12 | }, 13 | }); 14 | 15 | module.exports = MyComponent; 16 | -------------------------------------------------------------------------------- /lib/template/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var MyComponent = require('./MyComponent'); 4 | 5 | module.exports = MyComponent; 6 | -------------------------------------------------------------------------------- /lib/template/routes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var MyComponent = require('./MyComponent'); 4 | var React = require('react'); 5 | var {Route} = require('react-router'); 6 | 7 | module.exports = ; 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rwb", 3 | "version": "0.0.21", 4 | "description": "", 5 | "main": "./lib/index.js", 6 | "bin": { 7 | "rwb": "./lib/main.js" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "autoprefixer-loader": "3.2.0", 13 | "babel-core": "6.18.2", 14 | "babel-loader": "6.2.7", 15 | "babel-preset-es2015": "^6.18.0", 16 | "babel-preset-react": "^6.16.0", 17 | "babel-preset-stage-3": "^6.17.0", 18 | "css-loader": "0.25.0", 19 | "file-loader": "0.9.0", 20 | "invariant": "2.2.1", 21 | "json-loader": "^0.5.3", 22 | "ncp": "2.0.0", 23 | "node-libs-browser": "1.0.0", 24 | "open": "0.0.5", 25 | "react": "^15.3.2", 26 | "react-dom": "^15.3.2", 27 | "react-hot-loader": "3.0.0-beta.6", 28 | "style-loader": "0.13.1", 29 | "temp": "0.8.3", 30 | "url-loader": "0.5.7", 31 | "webpack": "1.13.3", 32 | "webpack-dev-server": "1.16.2", 33 | "yargs": "6.3.0" 34 | }, 35 | "preferGlobal": true, 36 | "files": [ 37 | "lib" 38 | ], 39 | "engines": { 40 | "node": ">0.12.0" 41 | } 42 | } 43 | --------------------------------------------------------------------------------