├── .babelrc ├── .eslintrc ├── .gitignore ├── .npmignore ├── .travis.yml ├── CHANGES.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── example ├── .babelrc ├── README.md ├── index.html ├── index.js ├── package.json └── webpack.config.js ├── karma.conf.js ├── modules ├── applyMiddleware.js └── applyMiddleware.test.js ├── npm-scripts └── postinstall.js ├── package.json ├── scripts ├── build.js └── release.sh ├── tests.webpack.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ "es2015", "react", "stage-1" ] 3 | } 4 | 5 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "rackt", 3 | "rules": { 4 | "react/jsx-uses-react": 1, 5 | "react/jsx-no-undef": 2, 6 | "react/wrap-multilines": 2 7 | }, 8 | "plugins": [ 9 | "react" 10 | ] 11 | } 12 | 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib 3 | umd 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .babelrc 2 | .eslintrc 3 | .travis.yml 4 | __tests__ 5 | scripts 6 | index.html 7 | karma.conf.js 8 | tests.webpack.js 9 | webpack.config.js 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - stable 4 | cache: 5 | directories: 6 | - node_modules 7 | branches: 8 | only: 9 | - master 10 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanflorence/react-router-apply-middleware/1ff8bcfbd99afec10d78c2548bcdea47851cf752/CHANGES.md -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | All development happens on GitHub. When creating a pull request, please make sure that all of the following apply: 2 | 3 | - The tests pass. The test suite will automatically run when you create the PR. All you need to do is wait a few minutes to see the result in the PR. 4 | - Each commit in your PR should describe a significant piece of work. Work that was done in one commit and then undone later should be squashed into a single commit. 5 | 6 | Also, please make sure the `CHANGES.md` document contains a short note about the nature of your change. Just follow the format of the existing entries in that document. If the most recent entry is a release, please start a new section under a `##HEAD` heading. 7 | 8 | Thank you for your contribution! 9 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Ryan Florence 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Router Apply Middleware 2 | 3 | Compose behavior in the render lifecycle of React Router apps. 4 | 5 | A name other than "middleware" would be awesome, I just can't think of 6 | one. Please suggest. 7 | 8 | ## Installation 9 | 10 | ``` 11 | npm install react-router-apply-middleware 12 | ``` 13 | 14 | ## Usage 15 | 16 | ```js 17 | import applyRouterMiddleware from 'react-router-apply-middleware' 18 | import { useAsyncProps } from 'react-router-async-props' 19 | import { useRelativeLinks } from 'react-router-relative-links' 20 | import { useNamedRoutes } from 'react-router-named-routes' 21 | import routes from './routes' 22 | 23 | const renderWithMiddleware = applyRouterMiddleware( 24 | useAsyncProps(), 25 | useRelativeLinks(), 26 | useNamedRoutes(routes) 27 | ) 28 | 29 | render(, el) 30 | ``` 31 | 32 | ## Writing Middleware 33 | 34 | For now you'll have to look at the tests, it's a little bit tricky. As 35 | soon as the middleware libs I'm working on are updated, I'll come add 36 | some notes here about how to do it. 37 | 38 | Hopefully we'll end up with stuff like: 39 | 40 | ```js 41 | useRelay(rootId) 42 | useAlt() 43 | useTransit() 44 | useGroundControl(store) 45 | useScrollBehavior() 46 | ``` 47 | 48 | etc. 49 | 50 | -------------------------------------------------------------------------------- /example/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ "es2015", "react" ] 3 | } 4 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | Relative Links Example 2 | ====================== 3 | 4 | From this directory: 5 | 6 | ```sh 7 | npm install 8 | npm start 9 | # open localhost:8080 10 | ``` 11 | 12 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Relative Links Example 5 |
6 | 7 | 8 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | console.log('hang on ...') 2 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "examples", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --inline --content-base .", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "babel-core": "^6.7.2", 14 | "babel-loader": "^6.2.4", 15 | "babel-preset-es2015": "^6.6.0", 16 | "babel-preset-react": "^6.5.0", 17 | "react": "^0.14.7", 18 | "react-dom": "^0.14.7", 19 | "webpack": "^1.12.14", 20 | "webpack-dev-server": "^1.14.1" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /example/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | entry: path.join(__dirname, 'index.js'), 5 | output: { 6 | filename: 'bundle.js' 7 | }, 8 | resolve: { 9 | alias: { 10 | 'react-router-apply-middleware': 11 | path.resolve(__dirname, '..', 'modules', 'applyMiddleware.js') 12 | } 13 | }, 14 | module: { 15 | loaders: [ 16 | { test: /\.js$/, 17 | exclude: /node_modules/, 18 | loader: 'babel-loader' 19 | } 20 | ] 21 | }, 22 | devServer: { 23 | historyApiFallback: true, 24 | quiet: false, 25 | noInfo: false, 26 | stats: { 27 | assets: true, 28 | version: false, 29 | hash: false, 30 | timings: false, 31 | chunks: false, 32 | chunkModules: true 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack') 2 | 3 | module.exports = function (config) { 4 | // Browsers to run on BrowserStack 5 | var customLaunchers = { 6 | BS_Chrome: { 7 | base: 'BrowserStack', 8 | os: 'Windows', 9 | os_version: '8.1', 10 | browser: 'chrome', 11 | browser_version: '39.0' 12 | }, 13 | BS_Firefox: { 14 | base: 'BrowserStack', 15 | os: 'Windows', 16 | os_version: '8.1', 17 | browser: 'firefox', 18 | browser_version: '32.0' 19 | }, 20 | BS_Safari: { 21 | base: 'BrowserStack', 22 | os: 'OS X', 23 | os_version: 'Yosemite', 24 | browser: 'safari', 25 | browser_version: '8.0' 26 | }, 27 | BS_MobileSafari: { 28 | base: 'BrowserStack', 29 | os: 'ios', 30 | os_version: '7.0', 31 | browser: 'iphone', 32 | real_mobile: false 33 | }, 34 | // BS_InternetExplorer9: { 35 | // base: 'BrowserStack', 36 | // os: 'Windows', 37 | // os_version: '7', 38 | // browser: 'ie', 39 | // browser_version: '9.0' 40 | // }, 41 | BS_InternetExplorer10: { 42 | base: 'BrowserStack', 43 | os: 'Windows', 44 | os_version: '8', 45 | browser: 'ie', 46 | browser_version: '10.0' 47 | }, 48 | BS_InternetExplorer11: { 49 | base: 'BrowserStack', 50 | os: 'Windows', 51 | os_version: '8.1', 52 | browser: 'ie', 53 | browser_version: '11.0' 54 | } 55 | } 56 | 57 | config.set({ 58 | customLaunchers: customLaunchers, 59 | 60 | browsers: [ 'Chrome' ], 61 | frameworks: [ 'mocha' ], 62 | reporters: [ 'mocha' ], 63 | 64 | files: [ 65 | 'tests.webpack.js' 66 | ], 67 | 68 | preprocessors: { 69 | 'tests.webpack.js': [ 'webpack', 'sourcemap' ] 70 | }, 71 | 72 | webpack: { 73 | devtool: 'inline-source-map', 74 | module: { 75 | loaders: [ 76 | { test: /\.js$/, exclude: /node_modules/, loader: 'babel' } 77 | ] 78 | }, 79 | plugins: [ 80 | new webpack.DefinePlugin({ 81 | 'process.env.NODE_ENV': JSON.stringify('test') 82 | }) 83 | ] 84 | }, 85 | 86 | webpackServer: { 87 | noInfo: true 88 | } 89 | }) 90 | 91 | if (process.env.USE_CLOUD) { 92 | config.browsers = Object.keys(customLaunchers) 93 | config.reporters = [ 'dots' ] 94 | config.browserDisconnectTimeout = 10000 95 | config.browserDisconnectTolerance = 3 96 | config.browserNoActivityTimeout = 30000 97 | config.captureTimeout = 120000 98 | 99 | if (process.env.TRAVIS) { 100 | var buildLabel = 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')' 101 | 102 | config.browserStack = { 103 | username: process.env.BROWSER_STACK_USERNAME, 104 | accessKey: process.env.BROWSER_STACK_ACCESS_KEY, 105 | pollingTimeout: 10000, 106 | startTunnel: true, 107 | project: 'history', 108 | build: buildLabel, 109 | name: process.env.TRAVIS_JOB_NUMBER 110 | } 111 | 112 | config.singleRun = true 113 | } else { 114 | config.browserStack = { 115 | username: process.env.BROWSER_STACK_USERNAME, 116 | accessKey: process.env.BROWSER_STACK_ACCESS_KEY, 117 | pollingTimeout: 10000, 118 | startTunnel: true 119 | } 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /modules/applyMiddleware.js: -------------------------------------------------------------------------------- 1 | import React, { cloneElement } from 'react' 2 | import RouterContext from 'react-router/lib/RouterContext' 3 | 4 | export default (...middleware) => ( 5 | ((createElement) => ( 6 | middleware.filter(m => m.renderRootContainer).reduceRight( 7 | (previous, { renderRootContainer }) => ( 8 | (renderProps) => ( 9 | cloneElement( 10 | renderRootContainer(renderProps), 11 | { render: previous } 12 | ) 13 | ) 14 | ), (renderProps) => ( 15 | 16 | ) 17 | ) 18 | ))(middleware.filter(m => m.renderContainer).reduceRight( 19 | (previous, { renderContainer }) => ( 20 | (RouteComponent, props) => ( 21 | cloneElement( 22 | renderContainer(RouteComponent, props), 23 | { createElement: previous } 24 | ) 25 | ) 26 | ), (Component, props) => 27 | )) 28 | ) 29 | 30 | -------------------------------------------------------------------------------- /modules/applyMiddleware.test.js: -------------------------------------------------------------------------------- 1 | /*eslint-env mocha*/ 2 | /*eslint no-console: 0*/ 3 | import expect from 'expect' 4 | import React from 'react' 5 | import { render } from 'react-dom' 6 | import { Router, Route, createMemoryHistory } from 'react-router' 7 | import applyMiddleware from './applyMiddleware' 8 | 9 | /* 10 | `applyMiddleware` turns this: 11 | 12 | ```js 13 | const render = applyMiddleware( 14 | useAsyncProps({ loadContext: { token } }), 15 | useNamedRoutes(), 16 | useRelativeLinks() 17 | ) 18 | ``` 19 | 20 | into this: 21 | 22 | ```js 23 | ( 25 | ( 27 | ( 29 | ( 31 | ( 33 | 34 | )} 35 | /> 36 | )} 37 | /> 38 | )} 39 | /> 40 | )} 41 | /> 42 | )} 43 | /> 44 | ``` 45 | */ 46 | 47 | const FOO_ROOT_CONTAINER_TEXT = 'FOO ROOT CONTAINER' 48 | const BAR_ROOT_CONTAINER_TEXT = 'BAR ROOT CONTAINER' 49 | const BAZ_CONTAINER_TEXT = 'BAZ INJECTED' 50 | 51 | const FooRootContainer = React.createClass({ 52 | propTypes: { 53 | // 1. applyMiddleware is going to pass a render prop 54 | render: React.PropTypes.func 55 | }, 56 | childContextTypes: { foo: React.PropTypes.string }, 57 | getChildContext() { return { foo: FOO_ROOT_CONTAINER_TEXT } }, 58 | render() { 59 | // 2. all RootContainers need to render with the `render` prop and send 60 | // along the bag of props it got (they came from Router) 61 | const { render, ...props } = this.props 62 | return render(props) 63 | } 64 | }) 65 | 66 | const FooContainer = React.createClass({ 67 | propTypes: { 68 | // 1. applyMiddleware is going to pass a createElement prop 69 | createElement: React.PropTypes.func 70 | }, 71 | contextTypes: { foo: React.PropTypes.string.isRequired }, 72 | render() { 73 | const { createElement, Component, routerProps } = this.props 74 | const fooFromContext = this.context.foo 75 | const mergedProps = { ...routerProps, fooFromContext } 76 | // 2. So all Containers need to render with the `createElement` prop, passing 77 | // along the Component to be rendered and the props to render with 78 | return createElement(Component, mergedProps) 79 | } 80 | }) 81 | 82 | const useFoo = () => ({ 83 | renderRootContainer: (renderProps) => ( 84 | // same signature as Router.props.render 85 | 86 | ), 87 | renderContainer: (Component, props) => ( 88 | // same signature as Router.props.createElement 89 | 90 | ) 91 | }) 92 | 93 | const BarRootContainer = React.createClass({ 94 | childContextTypes: { bar: React.PropTypes.string }, 95 | getChildContext() { return { bar: BAR_ROOT_CONTAINER_TEXT } }, 96 | render() { 97 | const { render, ...props } = this.props 98 | return render(props) 99 | } 100 | }) 101 | 102 | const BarContainer = React.createClass({ 103 | contextTypes: { bar: React.PropTypes.string.isRequired }, 104 | render() { 105 | const { createElement, Component, routerProps } = this.props 106 | const barFromContext = this.context.bar 107 | const mergedProps = { ...routerProps, barFromContext } 108 | return createElement(Component, mergedProps) 109 | } 110 | }) 111 | 112 | const useBar = () => ({ 113 | renderRootContainer: (renderProps) => ( 114 | 115 | ), 116 | renderContainer: (Component, props) => ( 117 | 118 | ) 119 | }) 120 | 121 | const BazContainer = React.createClass({ 122 | render() { 123 | const { createElement, Component, routerProps, bazInjected } = this.props 124 | const mergedProps = { ...routerProps, bazInjected } 125 | return createElement(Component, mergedProps) 126 | } 127 | }) 128 | 129 | const useBaz = (bazInjected) => ({ 130 | renderContainer: (Component, props) => ( 131 | 136 | ) 137 | }) 138 | 139 | const run = ({ renderWithMiddleware, Component }, assertion) => { 140 | const div = document.createElement('div') 141 | const routes = 142 | render(, div, () => assertion(div.innerHTML)) 147 | } 148 | 149 | describe('applyMiddleware', () => { 150 | 151 | it('applies one middleware', (done) => { 152 | run({ 153 | renderWithMiddleware: applyMiddleware(useFoo()), 154 | Component: (props) =>
{props.fooFromContext}
155 | }, (html) => { 156 | expect(html).toContain(FOO_ROOT_CONTAINER_TEXT) 157 | done() 158 | }) 159 | }) 160 | 161 | it('applies more than one middleware', (done) => { 162 | run({ 163 | renderWithMiddleware: applyMiddleware(useBar(), useFoo()), 164 | Component: (props) =>
{props.fooFromContext} {props.barFromContext}
165 | }, (html) => { 166 | expect(html).toContain(FOO_ROOT_CONTAINER_TEXT) 167 | expect(html).toContain(BAR_ROOT_CONTAINER_TEXT) 168 | done() 169 | }) 170 | }) 171 | 172 | it('applies more middleware with only `getContainer`', (done) => { 173 | run({ 174 | renderWithMiddleware: applyMiddleware( 175 | useBar(), 176 | useFoo(), 177 | useBaz(BAZ_CONTAINER_TEXT) 178 | ), 179 | Component: (props) => ( 180 |
181 | {props.fooFromContext} 182 | {props.barFromContext} 183 | {props.bazInjected} 184 |
185 | ) 186 | }, (html) => { 187 | expect(html).toContain(FOO_ROOT_CONTAINER_TEXT) 188 | expect(html).toContain(BAR_ROOT_CONTAINER_TEXT) 189 | expect(html).toContain(BAZ_CONTAINER_TEXT) 190 | done() 191 | }) 192 | }) 193 | 194 | it('applies middleware that only has `getContainer`', (done) => { 195 | run({ 196 | renderWithMiddleware: applyMiddleware( 197 | useBaz(BAZ_CONTAINER_TEXT) 198 | ), 199 | Component: (props) => ( 200 |
{props.bazInjected}
201 | ) 202 | }, (html) => { 203 | expect(html).toContain(BAZ_CONTAINER_TEXT) 204 | done() 205 | }) 206 | }) 207 | 208 | }) 209 | -------------------------------------------------------------------------------- /npm-scripts/postinstall.js: -------------------------------------------------------------------------------- 1 | var execSync = require('child_process').execSync 2 | var stat = require('fs').stat 3 | 4 | function exec(command) { 5 | execSync(command, { stdio: [0, 1, 2] }) 6 | } 7 | 8 | stat('lib', function (error, stat) { 9 | if (error || !stat.isDirectory()) 10 | exec('npm run build') 11 | }) 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-router-apply-middleware", 3 | "version": "0.0.2", 4 | "description": "Composable middleware for React Router", 5 | "main": "lib/applyMiddleware.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/ryanflorence/react-router-apply-middleware.git" 9 | }, 10 | "bugs": "https://github.com/ryanflorence/react-router-apply-middleware/issues", 11 | "scripts": { 12 | "build": "babel ./modules -d lib --ignore '__tests__'", 13 | "build-umd": "NODE_ENV=production webpack modules/applyMiddleware.js umd/ReactRouterApplyMiddleware.js", 14 | "build-min": "NODE_ENV=production webpack -p modules/applyMiddleware.js umd/ReactRouterApplyMiddleware.min.js", 15 | "lint": "eslint modules", 16 | "start": "babel-node example/server.js", 17 | "test": "npm run lint && karma start", 18 | "postinstall": "node ./npm-scripts/postinstall.js" 19 | }, 20 | "authors": [ 21 | "Ryan Florence" 22 | ], 23 | "license": "ISC", 24 | "peerDependencies": { 25 | "react": "*", 26 | "react-router": "^2.0.1" 27 | }, 28 | "devDependencies": { 29 | "babel-cli": "^6.6.5", 30 | "babel-core": "^6.7.0", 31 | "babel-eslint": "^3.1.23", 32 | "babel-loader": "^6.2.4", 33 | "babel-preset-es2015": "^6.6.0", 34 | "babel-preset-react": "^6.5.0", 35 | "babel-preset-stage-1": "^6.5.0", 36 | "eslint": "1.4.1", 37 | "eslint-config-rackt": "1.0.0", 38 | "eslint-plugin-react": "3.3.2", 39 | "expect": "^1.12.0", 40 | "express": "^4.13.3", 41 | "form-serialize": "^0.7.0", 42 | "gzip-size": "^3.0.0", 43 | "karma": "^0.13.3", 44 | "karma-browserstack-launcher": "^0.1.3", 45 | "karma-chrome-launcher": "^0.2.0", 46 | "karma-firefox-launcher": "^0.1.6", 47 | "karma-mocha": "^0.2.0", 48 | "karma-mocha-reporter": "^1.0.4", 49 | "karma-sourcemap-loader": "^0.3.5", 50 | "karma-webpack": "^1.7.0", 51 | "mocha": "^2.0.1", 52 | "pretty-bytes": "^2.0.1", 53 | "react": "^0.14.2", 54 | "react-dom": "^0.14.2", 55 | "react-router": "^2.0.1", 56 | "rimraf": "^2.4.2", 57 | "webpack": "^1.12.6", 58 | "webpack-dev-middleware": "^1.2.0" 59 | }, 60 | "tags": [ 61 | "react", 62 | "react router", 63 | "middleware" 64 | ], 65 | "keywords": [ 66 | "react", 67 | "react router", 68 | "middleware" 69 | ] 70 | } 71 | -------------------------------------------------------------------------------- /scripts/build.js: -------------------------------------------------------------------------------- 1 | var execSync = require('child_process').execSync 2 | var readFileSync = require('fs').readFileSync 3 | var prettyBytes = require('pretty-bytes') 4 | var gzipSize = require('gzip-size') 5 | 6 | function exec(command) { 7 | execSync(command, { stdio: [0, 1, 2] }) 8 | } 9 | 10 | exec('npm run build') 11 | exec('npm run build-umd') 12 | exec('npm run build-min') 13 | 14 | console.log( 15 | '\ngzipped, the UMD build is ' + prettyBytes( 16 | gzipSize.sync(readFileSync('umd/ReactRouterApplyMiddleware.min.js')) 17 | ) 18 | ) 19 | -------------------------------------------------------------------------------- /scripts/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | if ! [ -e scripts/release.sh ]; then 4 | echo >&2 "Please run scripts/release.sh from the repo root" 5 | exit 1 6 | fi 7 | 8 | update_version() { 9 | echo "$(node -p "p=require('./${1}');p.version='${2}';JSON.stringify(p,null,2)")" > $1 10 | echo "Updated ${1} version to ${2}" 11 | } 12 | 13 | validate_semver() { 14 | if ! [[ $1 =~ ^[0-9]\.[0-9]+\.[0-9](-.+)? ]]; then 15 | echo >&2 "Version $1 is not valid! It must be a valid semver string like 1.0.2 or 2.3.0-beta1" 16 | exit 1 17 | fi 18 | } 19 | 20 | current_version=$(node -p "require('./package').version") 21 | 22 | printf "Next version (current is $current_version)? " 23 | read next_version 24 | 25 | validate_semver $next_version 26 | 27 | next_ref="v$next_version" 28 | 29 | npm test -- --single-run 30 | 31 | update_version 'package.json' $next_version 32 | 33 | node scripts/build.js 34 | 35 | git commit -am "Version $next_version" 36 | 37 | git tag $next_ref 38 | git tag latest -f 39 | 40 | git push origin master 41 | git push origin $next_ref 42 | git push origin latest -f 43 | 44 | npm publish 45 | -------------------------------------------------------------------------------- /tests.webpack.js: -------------------------------------------------------------------------------- 1 | const context = require.context('./modules', true, /.test\.js$/) 2 | context.keys().forEach(context) 3 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | output: { 4 | library: 'RelativeLinks', 5 | libraryTarget: 'umd' 6 | }, 7 | 8 | externals: [ 9 | { 10 | react: { 11 | root: 'React', 12 | commonjs2: 'react', 13 | commonjs: 'react', 14 | amd: 'react' 15 | }, 16 | 'react-router': { 17 | root: 'ReactRouter', 18 | commonjs2: 'react-router', 19 | commonjs: 'react-router', 20 | amd: 'react-router' 21 | } 22 | } 23 | ], 24 | 25 | module: { 26 | loaders: [ 27 | { test: /\.js$/, exclude: /node_modules/, loader: 'babel' } 28 | ] 29 | }, 30 | 31 | node: { 32 | Buffer: false 33 | } 34 | 35 | } 36 | --------------------------------------------------------------------------------