├── .all-contributorsrc ├── .babelrc ├── .editorconfig ├── .gitignore ├── .nvmrc ├── LICENSE ├── README.md ├── next.config.js ├── now.json ├── package.json ├── server.js ├── src ├── components │ └── Spinner.js ├── constants.js ├── fixtures │ ├── playgroundDefault.js │ └── playgroundDefaultMobile.js ├── lib │ └── react-reboot.js ├── pages │ ├── _document.js │ ├── about.js │ ├── editor.js │ └── index.js ├── static │ ├── base.css │ ├── favicon.ico │ ├── logo.svg │ └── screenshot.png └── utils │ └── codeMirror.js └── yarn.lock /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "projectName": "react-reboot", 3 | "projectOwner": "slorber", 4 | "files": [ 5 | "README.md" 6 | ], 7 | "imageSize": 100, 8 | "commit": true, 9 | "contributors": [ 10 | { 11 | "login": "slorber", 12 | "name": "Sébastien Lorber", 13 | "avatar_url": "https://avatars0.githubusercontent.com/u/749374?v=4", 14 | "profile": "https://github.com/slorber", 15 | "contributions": [ 16 | "code" 17 | ] 18 | }, 19 | { 20 | "login": "sutter", 21 | "name": "Sutterlity Laurent", 22 | "avatar_url": "https://avatars1.githubusercontent.com/u/709456?v=4", 23 | "profile": "http://www.sutterlity.fr", 24 | "contributions": [ 25 | "code" 26 | ] 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "next/babel" 4 | ], 5 | "plugins": [ 6 | "glamorous-displayname", 7 | [ 8 | "module-resolver", 9 | { 10 | "root": [ 11 | "./src" 12 | ], 13 | } 14 | ], 15 | [ 16 | "inline-import", 17 | { 18 | "extensions": [ 19 | ".css", 20 | ".js" 21 | ] 22 | } 23 | ] 24 | ] 25 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [{.eslintrc,.babelrc,*.json}] 2 | indent_style=space 3 | indent_size=2 4 | 5 | [*.js] 6 | indent_style=space 7 | indent_size=2 8 | 9 | [*.jsx] 10 | indent_style=space 11 | indent_size=2 12 | 13 | [*.scss] 14 | indent_style=space 15 | indent_size=2 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | node_modules 4 | .env 5 | .idea 6 | src/.next 7 | yarn-error.log 8 | npm-debug.log 9 | codemods 10 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v6.9.0 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Sébastien Lorber 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-reboot 2 | [![All Contributors](https://img.shields.io/badge/all_contributors-2-orange.svg?style=flat-square)](#contributors) 3 | 4 | # Not maintained 5 | 6 | The online playground is put offline due to low project traction and paid hosting. Many people have already migrated to modern ES6 syntax so this is not so useful anymore. 7 | 8 | # Intro 9 | 10 | The easiest way to refresh your React components with up-to-date syntax. 11 | 12 | The [Playground](https://react-reboot.now.sh/) is available to transform your react components online. 13 | 14 | This is for now a very basic and unflexible MVP, so don't be angry if it does not work well and come back later :) 15 | 16 | Coming soon: Node API and CLI 17 | 18 | 19 | #### Before 20 | 21 | ```javascript 22 | var React = require('react'); 23 | var PureRenderMixin = require("react-addons-pure-render-mixin"); 24 | 25 | var HelloWorld = React.createClass({ 26 | mixins: [PureRenderMixin], 27 | propTypes: { 28 | input: React.PropTypes.string.isRequired, 29 | bool: React.PropTypes.bool.isRequired 30 | }, 31 | handleClick: function(arg) { 32 | console.debug("debug " + arg,React.findDOMNode(this)); 33 | }, 34 | render() { 35 | var x = 2, y = 3, z = (!!x ? true : false); 36 | var {hey, ...rest} = {hey: "hello"} 37 | let newVar = Object.assign({hey},{x,y},rest); 38 | var myString = "[" + newVar.hey + newVar.x + "]" + " ---- " + someFn(); 39 | debugger; 40 | return ( 41 |
47 | {myString} 48 |
49 | ) 50 | }, 51 | }); 52 | ``` 53 | 54 | #### After 55 | ```javascript 56 | import PropTypes from "prop-types"; 57 | import React from "react"; 58 | import ReactDOM from "react-dom"; 59 | 60 | class HelloWorld extends React.PureComponent { 61 | static propTypes = { 62 | input: PropTypes.string.isRequired, 63 | bool: PropTypes.bool.isRequired, 64 | }; 65 | 66 | handleClick = arg => { 67 | console.debug(`debug ${arg}`, ReactDOM.findDOMNode(this)); 68 | }; 69 | 70 | render() { 71 | const x = 2; 72 | const y = 3; 73 | const z = !!x; 74 | const { hey, ...rest } = { hey: "hello" }; 75 | const newVar = { 76 | hey, 77 | x, 78 | y, 79 | ...rest, 80 | }; 81 | const myString = `[${newVar.hey}${newVar.x}] ---- ${someFn()}`; 82 | 83 | return ( 84 |
{ 87 | console.debug("test"); 88 | }} 89 | > 90 | {myString} 91 |
92 | ); 93 | } 94 | } 95 | ``` 96 | 97 | 98 | # How it works 99 | 100 | It simply runs in a row these 4 tools in a Node server, with an opiniated default configuration: 101 | 102 | - JSCodeshift codemods 103 | - ESLint rules with --fix 104 | - Babel transforms (coming soon!) 105 | - Prettier --write 106 | 107 | # Problems 108 | 109 | - Currently, no single tool solve every transform problem, and setting up and integrating all the available tools together is time consuming. 110 | 111 | - Not all transforms available are bugfree, and figuring out which to run in which order 112 | 113 | - Some teams might prefer to update components gradually to avoid git conflicts. This tool focus on transforming completely files one by one, while other tools like JSCodeShift runner are focusing on running transforms one by one efficiently against a very large codebase like Facebook. 114 | 115 | 116 | # TODO 117 | 118 | - Better error handling in case of unparsable input 119 | - Diplay transform log in playground 120 | - Support other parsers (Flow...) 121 | - Fine-tune transformation rules and order 122 | - Publish Node API and CLI (without embedding codemods? licensing problem) 123 | - Provide options (api + playground) 124 | - Tests 125 | - Help me :) 126 | 127 | # Dev 128 | 129 | Works with these versions: 130 | 131 | ``` 132 | "engines": { 133 | "node": ">=6.9.0", 134 | "npm": ">=3.10.10", 135 | "yarn": ">=1.2.1" 136 | }, 137 | ``` 138 | 139 | ### Run local website / playground: 140 | 141 | ``` 142 | yarn install 143 | yarn dev 144 | ``` 145 | 146 | 147 | # Contributors 148 | 149 | Thanks to all project contributors ([all-contributors](https://github.com/kentcdodds/all-contributors) specification) 150 | 151 | 152 | | [
Sébastien Lorber](https://github.com/slorber)
[💻](https://github.com/slorber/react-reboot/commits?author=slorber "Code") | [
Sutterlity Laurent](http://www.sutterlity.fr)
[💻](https://github.com/slorber/react-reboot/commits?author=sutter "Code") | 153 | | :---: | :---: | 154 | 155 | 156 | 157 | Contributions of any kind welcome! 158 | 159 | Thanks to Babel, Jscodeshift, ESlint, Prettier, and [Carbon](https://github.com/dawnlabs/carbon) for some design/layout/code inspiration. 160 | 161 | # Hire a freelance expert 162 | 163 | Looking for a React/ReactNative freelance expert with more than 5 years production experience? 164 | Contact me from my [website](https://sebastienlorber.com/) or with [Twitter](https://twitter.com/sebastienlorber). 165 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | // This file is not going through babel transformation. 2 | // So, we write it in vanilla JS 3 | // (But you could use ES2015 features supported by your Node.js version) 4 | 5 | module.exports = { 6 | webpack: (config, { buildId, dev }) => { 7 | // Perform customizations to webpack config 8 | 9 | // Important: return the modified config 10 | return config 11 | }, 12 | webpackDevMiddleware: config => { 13 | // Perform customizations to webpack dev middleware config 14 | 15 | // Important: return the modified config 16 | return config 17 | } 18 | } -------------------------------------------------------------------------------- /now.json: -------------------------------------------------------------------------------- 1 | { 2 | "public": true, 3 | "alias": "react-reboot.now.sh" 4 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-reboot", 3 | "version": "1.0.0", 4 | "description": "React-reboot: refresh your react components with up-to-date syntax", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/slorber/react-reboot.git" 8 | }, 9 | "bugs": { 10 | "url": "https://github.com/slorber/react-reboot/issues" 11 | }, 12 | "author": "Sébastien Lorber <@slorber>", 13 | "homepage": "https://react-reboot.now.sh", 14 | "license": "MIT", 15 | "main": "src/lib/react-reboot.js", 16 | "engines": { 17 | "node": ">=6.9.0", 18 | "npm": ">=3.10.10", 19 | "yarn": ">=1.2.1" 20 | }, 21 | "scripts": { 22 | "test": "echo \"Error: no test specified\" && exit 1", 23 | "transform": "node index.js", 24 | "dev": "node server.js", 25 | "build": "next build src", 26 | "start": "NODE_ENV=production node server.js", 27 | "deploy": "now && now alias", 28 | "postinstall": "npm run download-codemods-if-needed", 29 | "add-contributor": "all-contributors add", 30 | "generate-contributors": "all-contributors generate", 31 | "download-codemods-if-needed": "[[ -d codemods ]] || npm run download-codemods", 32 | "download-codemods": "npm run download-react-codemod & npm run download-js-codemod", 33 | "download-react-codemod": "mkdir -p codemods && curl -L -o ./codemods/react-codemod.zip 'https://github.com/reactjs/react-codemod/archive/master.zip' && unzip ./codemods/react-codemod.zip -d ./codemods && mv ./codemods/react-codemod-master ./codemods/react-codemod", 34 | "download-js-codemod": "mkdir -p codemods && curl -L -o ./codemods/js-codemod.zip 'https://github.com/cpojer/js-codemod/archive/master.zip' && unzip ./codemods/js-codemod.zip -d ./codemods && mv ./codemods/js-codemod-master ./codemods/js-codemod" 35 | }, 36 | "dependencies": { 37 | "5to6-codemod": "^1.7.1", 38 | "babel-core": "^6.26.0", 39 | "babel-eslint": "^8.0.1", 40 | "babel-plugin-glamorous-displayname": "^2.1.0", 41 | "babel-plugin-inline-import": "^2.0.6", 42 | "babel-plugin-module-resolver": "^2.7.1", 43 | "babel-plugin-syntax-jsx": "^6.18.0", 44 | "babel-plugin-transform-flow-strip-types": "^6.8.0", 45 | "babel-plugin-transform-inline-consecutive-adds": "^0.2.0", 46 | "babel-polyfill": "^6.26.0", 47 | "babel-preset-es2015": "^6.9.0", 48 | "babel-preset-stage-1": "^6.5.0", 49 | "babel-register": "^6.9.0", 50 | "babylon": "^6.18.0", 51 | "body-parser": "^1.18.2", 52 | "codemirror": "^5.31.0", 53 | "eslint": "^4.10.0", 54 | "eslint-plugin-react": "^7.4.0", 55 | "eslint-plugin-react-native": "^3.1.0", 56 | "express": "^4.16.2", 57 | "glamor": "^2.20.40", 58 | "glamorous": "^4.11.0", 59 | "jscodeshift": "^0.3.32", 60 | "lodash": "^4.17.4", 61 | "next": "^4.1.4", 62 | "now": "^8.3.11", 63 | "prettier": "^1.7.4", 64 | "react": "^16.0.0", 65 | "react-codemirror2": "^3.0.6", 66 | "react-dom": "^16.0.0", 67 | "whatwg-fetch": "^2.0.3" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const bodyParser = require('body-parser'); 3 | const next = require('next'); 4 | 5 | 6 | const ReactReboot = require("./src/lib/react-reboot"); 7 | 8 | 9 | 10 | const port = parseInt(process.env.PORT, 10) || 3000; 11 | const dev = process.env.NODE_ENV !== 'production'; 12 | 13 | const app = next({ 14 | dev, 15 | dir: './src' 16 | }); 17 | 18 | const handle = app.getRequestHandler(); 19 | 20 | 21 | 22 | app.prepare() 23 | .then(() => { 24 | const server = express(); 25 | server.use(bodyParser.json()); 26 | 27 | 28 | server.post('/transform', (req, res) => { 29 | const input = req.body.input; 30 | if ( !input ) { 31 | res.status(400).send('no input'); 32 | } 33 | else { 34 | try { 35 | const {output,logger} = ReactReboot.transform(input); 36 | res.set('Content-Type', 'application/json').status(200).send({output,logger}); 37 | } 38 | catch(e) { 39 | res.status(400).send(e.message); 40 | } 41 | } 42 | }); 43 | 44 | server.get('*', (req, res) => { 45 | return handle(req, res); 46 | }); 47 | 48 | server.listen(port, (err) => { 49 | if (err) throw err; 50 | console.log(`> Ready on http://localhost:${port}`); 51 | }); 52 | }); -------------------------------------------------------------------------------- /src/components/Spinner.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | import {Div} from "glamorous"; 5 | 6 | const Spinner = () => ( 7 |
8 |
9 |
10 |
11 | 56 |
57 | ); 58 | export default Spinner; 59 | 60 | export const SpinnerOverlay = (props) => ( 61 |
74 | 75 |
76 | ); -------------------------------------------------------------------------------- /src/constants.js: -------------------------------------------------------------------------------- 1 | export const AppName = "React-reboot"; 2 | export const AppTagline = "Give a new life to your React components"; 3 | export const AppTitle = `${AppName} - ${AppTagline}`; 4 | 5 | export const AuthorTwitterHandle = "sebastienlorber"; 6 | 7 | export const GithubUrl = "https://github.com/slorber/react-reboot"; 8 | 9 | export const BaseURL = "https://react-reboot.now.sh"; 10 | 11 | export const ScreenshotUrl = `/static/screenshot.png`; 12 | export const LogoUrl = `/static/logo.svg`; 13 | 14 | export const Color0 = "#B4BFDD"; 15 | export const Color1 = "#F77FC1"; 16 | export const Color2 = "#F1F898"; 17 | export const Color3 = "#62E482"; 18 | 19 | export const PageEditor = "/"; 20 | export const PageAbout = "/about"; 21 | 22 | export const MediaBreakpoint = 950; 23 | 24 | export const MediaQueries = { 25 | small: `@media only screen and (max-width: ${MediaBreakpoint}px)`, 26 | large: `@media only screen and (min-width: ${MediaBreakpoint + 1}px)`, 27 | }; 28 | -------------------------------------------------------------------------------- /src/fixtures/playgroundDefault.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var PureRenderMixin = require("react-addons-pure-render-mixin"); 3 | 4 | var someFn = function() { 5 | var args = [3, 4].concat([5,6]); 6 | return Math.max.apply(Math, args).map(function(n) {return n * 2;}); 7 | }; 8 | 9 | var HelloWorld = React.createClass({ 10 | mixins: [PureRenderMixin], 11 | propTypes: { 12 | input: React.PropTypes.string.isRequired, 13 | bool: React.PropTypes.bool.isRequired 14 | }, 15 | handleClick: function(arg) { 16 | console.debug("debug " + arg,React.findDOMNode(this)); 17 | }, 18 | render() { 19 | var x = 2, y = (!!x ? true : false); 20 | let newVar = Object.assign({},{x,y},this.props); 21 | var myString = "[" + newVar.x + "]" + " ---- " + someFn(); 22 | debugger; 23 | return ( 24 |
30 | {React.createElement( 31 | SomeDiv, 32 | {className: myString}, 33 | children 34 | )} 35 |
36 | ) 37 | }, 38 | }); 39 | 40 | var SomeDiv = React.createClass({ 41 | render() { 42 | const {width, height, ...rest} = this.props; 43 | return ( 44 |
45 | {React.createElement( 46 | SomeComponent, 47 | {style: {width: 10}}, 48 | children 49 | )} 50 |
51 | ) 52 | }, 53 | }); 54 | 55 | class SomeComponent extends React.Component { 56 | constructor() { 57 | super() 58 | this.onClick = this.onClick.bind(this); 59 | } 60 | onClick() { 61 | console.debug("debug"); 62 | } 63 | render() { 64 | return
; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/fixtures/playgroundDefaultMobile.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var PureRenderMixin = require("react-addons-pure-render-mixin"); 3 | 4 | var HelloWorld = React.createClass({ 5 | mixins: [PureRenderMixin], 6 | propTypes: { 7 | name: React.PropTypes.string.isRequired, 8 | }, 9 | sayHello: function(name) { 10 | alert("Hello " + name) 11 | }, 12 | render() { 13 | var name = this.props.name; 14 | return ( 15 |
20 | {React.createElement("div",{className: "button"},Say hello)} 21 |
22 | ) 23 | }, 24 | }); 25 | -------------------------------------------------------------------------------- /src/lib/react-reboot.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const jscodeshiftCore = require("jscodeshift"); 3 | const prettier = require("prettier"); 4 | const eslint = require("eslint"); 5 | const babel = require("babel-core"); 6 | const babylon = require("babylon"); 7 | 8 | require('babel-register')({ 9 | babelrc: false, 10 | presets: [ 11 | require('babel-preset-es2015'), 12 | require('babel-preset-stage-1'), 13 | ], 14 | plugins: [ 15 | require('babel-plugin-transform-flow-strip-types'), 16 | require('babel-plugin-transform-inline-consecutive-adds'), 17 | ] 18 | }); 19 | 20 | 21 | // Hacky backport of the babel.parse function that is expected by jscodeshift 22 | // https://github.com/babel/babel/blob/5.x/packages/babel/src/api/node.js 23 | babel.parse = (code, opts = {}) => { 24 | opts.allowHashBang = true; 25 | opts.sourceType = "module"; 26 | opts.ecmaVersion = Infinity; 27 | /* 28 | opts.plugins = { 29 | jsx: true, 30 | flow: true, 31 | }; 32 | opts.features = { 33 | "es7.trailingFunctionCommas": true, 34 | "es7.asyncFunctions": true, 35 | "es7.decorators": true, 36 | "es7.classProperties": true, 37 | "es7.doExpressions": true, 38 | "es7.exportExtensions": true, 39 | "es7.functionBind": true, 40 | "es7.functionSent": true, 41 | "es7.objectRestSpread": true, 42 | "es7.dynamicImport": true, 43 | }; 44 | */ 45 | 46 | opts.plugins = [ 47 | "estree", 48 | "flow", 49 | "jsx", 50 | "asyncGenerators", 51 | "classProperties", 52 | "doExpressions", 53 | "exportExtensions", 54 | "functionBind", 55 | "functionSent", 56 | "objectRestSpread", 57 | "dynamicImport" 58 | ]; 59 | 60 | /* 61 | opts.features = {}; 62 | for (var key in transform.pipeline.transformers) { 63 | opts.features[key] = true; 64 | } 65 | */ 66 | const ast = babylon.parse(code, opts); 67 | if (opts.onToken) { 68 | opts.onToken.push(...ast.tokens); 69 | } 70 | 71 | if (opts.onComment) { 72 | opts.onComment.push(...ast.comments); 73 | } 74 | return ast.program; 75 | }; 76 | 77 | 78 | const jscodeshift = jscodeshiftCore.withParser(babel); 79 | 80 | 81 | const Api = { 82 | j: jscodeshift, 83 | jscodeshift: jscodeshift, 84 | stats: {} 85 | }; 86 | 87 | 88 | const Transforms = [ 89 | { 90 | transform: require("5to6-codemod/transforms/amd.js"), 91 | options: {}, 92 | }, 93 | { 94 | transform: require("5to6-codemod/transforms/cjs.js"), 95 | options: {}, 96 | }, 97 | { 98 | transform: require("5to6-codemod/transforms/exports.js"), 99 | options: {}, 100 | }, 101 | { 102 | transform: require("5to6-codemod/transforms/named-export-generation.js"), 103 | options: {}, 104 | }, 105 | { 106 | transform: require("5to6-codemod/transforms/let.js"), 107 | options: {}, 108 | }, 109 | { 110 | transform: require("5to6-codemod/transforms/simple-arrow.js"), 111 | options: {}, 112 | }, 113 | { 114 | transform: require("5to6-codemod/transforms/no-strict.js"), 115 | options: {}, 116 | }, 117 | { 118 | transform: require("5to6-codemod/transforms/import-cleanup.js"), 119 | options: {}, 120 | }, 121 | { 122 | transform: require("../../codemods/react-codemod/transforms/error-boundaries.js"), 123 | options: {}, 124 | }, 125 | { 126 | transform: require("../../codemods/react-codemod/transforms/create-element-to-jsx.js"), 127 | options: {}, 128 | }, 129 | { 130 | transform: require("../../codemods/react-codemod/transforms/findDOMNode.js"), 131 | options: {}, 132 | }, 133 | { 134 | transform: require("../../codemods/react-codemod/transforms/React-PropTypes-to-prop-types.js"), 135 | options: {}, 136 | }, 137 | { 138 | transform: require("../../codemods/js-codemod/transforms/arrow-function-arguments.js"), 139 | options: {}, 140 | }, 141 | { 142 | transform: require("../../codemods/js-codemod/transforms/arrow-function.js"), 143 | options: {}, 144 | }, 145 | { 146 | transform: require("../../codemods/js-codemod/transforms/invalid-requires.js"), 147 | options: {}, 148 | }, 149 | { 150 | transform: require("../../codemods/js-codemod/transforms/outline-require.js"), 151 | options: {}, 152 | }, 153 | { 154 | transform: require("../../codemods/js-codemod/transforms/no-vars.js"), 155 | options: {}, 156 | }, 157 | { 158 | transform: require("../../codemods/js-codemod/transforms/rm-merge.js"), 159 | options: {}, 160 | }, 161 | { 162 | transform: require("../../codemods/js-codemod/transforms/rm-copyProperties.js"), 163 | options: {}, 164 | }, 165 | { 166 | transform: require("../../codemods/js-codemod/transforms/rm-object-assign.js"), 167 | options: {}, 168 | }, 169 | { 170 | transform: require("../../codemods/js-codemod/transforms/rm-requires.js"), 171 | options: {}, 172 | }, 173 | { 174 | transform: require("../../codemods/js-codemod/transforms/template-literals.js"), 175 | options: {}, 176 | }, 177 | { 178 | transform: require("../../codemods/js-codemod/transforms/unchain-variables.js"), 179 | options: {}, 180 | }, 181 | { 182 | transform: require("../../codemods/js-codemod/transforms/object-shorthand.js"), 183 | options: {}, 184 | }, 185 | { 186 | transform: require("../../codemods/react-codemod/transforms/manual-bind-to-arrow.js").default, 187 | options: {}, 188 | }, 189 | { 190 | transform: require("../../codemods/react-codemod/transforms/react-to-react-dom.js"), 191 | options: {}, 192 | }, 193 | { 194 | transform: require("../../codemods/react-codemod/transforms/pure-render-mixin.js"), 195 | options: {"pure-component": true}, 196 | }, 197 | { 198 | transform: require("../../codemods/react-codemod/transforms/class.js"), 199 | options: {"pure-component": true}, 200 | }, 201 | { 202 | transform: require("../../codemods/react-codemod/transforms/pure-component.js"), 203 | options: {"useArrows": true, "destructuring": true}, 204 | } 205 | ]; 206 | 207 | 208 | 209 | const applyTransform = (transform, options, input,logger) => { 210 | const output = transform( 211 | { 212 | path: "react-reboot-string.js", 213 | source: input, 214 | }, 215 | Api, 216 | options 217 | ); 218 | //console.log("single transform output: ",output); 219 | logger.push("Transform: input === output? => " + (input === output)); 220 | return output ? output : input; 221 | }; 222 | 223 | 224 | const applyTransforms = (transforms,input,logger) => { 225 | const output = transforms.reduce( 226 | (currentSource, {transform, options}) => { 227 | try { 228 | return applyTransform(transform, options, currentSource,logger); 229 | } 230 | catch (e) { 231 | console.log("transform error",e); 232 | logger.push("transform error: " + e.message); 233 | return currentSource; 234 | } 235 | }, 236 | input 237 | ); 238 | logger.push("Codemod: input !== output? => " + (input !== output)); 239 | logger.push("output",output); 240 | return output; 241 | }; 242 | 243 | 244 | 245 | const PrettierOptions = { 246 | semi: true, 247 | singleQuote: false, 248 | trailingComma: "all", 249 | }; 250 | 251 | const applyPrettier = (input,logger) => { 252 | try { 253 | const output = prettier.format(input, PrettierOptions); 254 | return output ? output : input; 255 | } catch (e) { 256 | console.error("Prettier failure",e); 257 | logger.push("Prettier failure: "+e.message); 258 | return input; 259 | } 260 | }; 261 | 262 | 263 | 264 | const applyESLint = (input,logger) => { 265 | try { 266 | const eslintConfig = { 267 | fix: true, 268 | parser: "babel-eslint", 269 | plugins: [ 270 | "react", 271 | "react-native", 272 | ], 273 | rules: { 274 | "no-extra-boolean-cast": 2, 275 | "no-debugger": 2, 276 | "no-extra-parens": 2, 277 | "no-regex-spaces": 2, 278 | "curly": 2, 279 | "dot-location": 2, 280 | "dot-notation": 2, 281 | "eqeqeq": 2, 282 | "no-extra-bind": 2, 283 | "no-useless-return": 2, 284 | "yoda": 2, 285 | "wrap-iife": 2, 286 | "no-unneeded-ternary": 2, 287 | 288 | // ES6: 289 | "arrow-body-style": 2, 290 | "arrow-spacing": 2, 291 | "generator-star-spacing": 2, 292 | "no-var": 2, 293 | "no-useless-rename": 2, 294 | "no-useless-computed-key": 2, 295 | "prefer-arrow-callback": 2, 296 | "prefer-const": 2, 297 | "prefer-spread": 2, 298 | "yield-star-spacing": 2, 299 | "template-curly-spacing": 2, 300 | 301 | "react/no-unknown-property": 2, 302 | "react/self-closing-comp": 2, 303 | 304 | "react-native/no-unused-styles": 2, 305 | "react-native/split-platform-components": 2, 306 | "react-native/no-inline-styles": 2, 307 | "react-native/no-color-literals": 2, 308 | } 309 | }; 310 | 311 | 312 | 313 | const engine = new eslint.CLIEngine(eslintConfig); 314 | const report = engine.executeOnText(input) ; 315 | const {messages,output} = report.results[0]; 316 | const fixed = output !== input; 317 | 318 | if ( fixed ) { 319 | logger.push("ESLint fixed the code!"); 320 | messages.forEach(message => { 321 | logger.push(`[${message.line}][${message.ruleId}] ${message.message}`) 322 | }); 323 | return output; 324 | } 325 | else { 326 | logger.push("ESLint didn't fix the code"); 327 | return input; 328 | } 329 | 330 | } catch(e) { 331 | console.error("ESLint failure",e); 332 | logger.push("ESLint failure: "+e.message); 333 | return input; 334 | } 335 | }; 336 | 337 | /* 338 | presets: [ 339 | require('babel-preset-es2015'), 340 | require('babel-preset-stage-1'), 341 | ], 342 | plugins: [ 343 | require('babel-plugin-transform-flow-strip-types'), 344 | require('babel-plugin-transform-inline-consecutive-adds'), 345 | ] 346 | */ 347 | 348 | const applyBabel = (input,logger) => { 349 | try { 350 | const result = babel.transform(input,{ 351 | babelrc: false, 352 | presets: [ 353 | 'es2015', 354 | 'stage-1', 355 | ], 356 | plugins: [ 357 | //'syntax-jsx', 358 | 'transform-flow-strip-types', 359 | 'transform-inline-consecutive-adds', 360 | ] 361 | }); 362 | //console.log("result:",JSON.stringify(result,null,2)); 363 | const {output,map,ast} = result; 364 | //console.log(output); 365 | return output ? output : input; 366 | } catch (e) { 367 | console.error("Babel transform failure",e); 368 | logger.push("Babel transform failure: "+e.message); 369 | return input; 370 | } 371 | }; 372 | 373 | 374 | 375 | const transform = (input) => { 376 | try { 377 | babel.parse(input); 378 | } catch(e) { 379 | throw new Error("Unable to parse code with Babel. Reason=" + e.message); 380 | } 381 | const logger = []; 382 | let output = input; 383 | output = applyTransforms(Transforms,output,logger); 384 | output = applyESLint(output,logger); 385 | output = applyBabel(output,logger); 386 | output = applyPrettier(output,logger); 387 | return {output,logger}; 388 | }; 389 | exports.transform = transform; 390 | 391 | 392 | 393 | const test = () => new Promise((resolve, reject) => { 394 | const inputFile = "../fixtures/input1.js"; 395 | fs.readFile(inputFile, (err, source) => { 396 | if (err) reject(err); 397 | const {output,logger} = transform( 398 | Transforms, 399 | source.toString() 400 | ); 401 | console.log(output); 402 | console.log(JSON.stringify(logger,null,2)); 403 | resolve(output); 404 | }); 405 | }); 406 | exports.test = test; 407 | 408 | 409 | -------------------------------------------------------------------------------- /src/pages/_document.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Document, { Head, Main as NextMain, NextScript } from "next/document"; 3 | import flush from "styled-jsx/server"; 4 | 5 | import Link from "next/link"; 6 | 7 | import { renderStatic } from "glamor/server"; 8 | import { A, Div, Footer, Main, Header, Img } from "glamorous"; 9 | import "babel-polyfill"; 10 | import "whatwg-fetch"; 11 | import { 12 | AppName, 13 | AppTagline, 14 | AppTitle, 15 | AuthorTwitterHandle, 16 | BaseURL, 17 | GithubUrl, 18 | LogoUrl, 19 | MediaQueries, 20 | PageAbout, 21 | ScreenshotUrl, 22 | Color0, 23 | Color1, 24 | Color2, 25 | Color3, 26 | } from "constants"; 27 | import { hover } from "glamor"; 28 | 29 | export default class MyDocument extends Document { 30 | static async getInitialProps({ renderPage }) { 31 | const page = renderPage(); 32 | const styles = renderStatic(() => page.html); 33 | return { 34 | ...page, 35 | ...styles, 36 | jsxStyleCss: flush(), 37 | }; 38 | } 39 | 40 | constructor(props) { 41 | super(props); 42 | const { __NEXT_DATA__, ids } = props; 43 | if (ids) { 44 | __NEXT_DATA__.ids = this.props.ids; 45 | } 46 | } 47 | 48 | render() { 49 | return ( 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | {AppTitle} 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 83 | 87 | 16 | 17 | 27 | 55 | 57 | 61 | 63 | 65 | 67 | 69 | 72 | 74 | 76 | 78 | 80 | 82 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 94 | 96 | 98 | 99 | 100 | 125 | 128 | 130 | 132 | 141 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /src/static/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slorber/react-reboot/980bf2fc05eb310f5bfc7d530cd48659b7dc098f/src/static/screenshot.png -------------------------------------------------------------------------------- /src/utils/codeMirror.js: -------------------------------------------------------------------------------- 1 | 2 | // See https://github.com/dawnlabs/carbon/blob/bdc9211d25196dba9a00244ff23fed7d7b44ed73/lib/react-codemirror.js 3 | 4 | // For SSR, CodeMirror will throw an error, so return a div instead 5 | let CodeMirror = 'div' 6 | if (typeof window !== 'undefined' && typeof window.navigator !== 'undefined') { 7 | CodeMirror = require('react-codemirror2').Controlled; 8 | require('codemirror/mode/javascript/javascript'); 9 | require('codemirror/mode/xml/xml'); 10 | require('codemirror/mode/jsx/jsx'); 11 | } 12 | export default CodeMirror --------------------------------------------------------------------------------