├── .gitignore ├── 01-react ├── package.json ├── src │ ├── App.css │ ├── App.js │ ├── client.js │ ├── index.js │ ├── react.svg │ └── server.js └── yarn.lock ├── 02-react-vue ├── package.json ├── razzle.config.js ├── src │ ├── App.css │ ├── App.js │ ├── App.vue │ ├── client.js │ ├── index.js │ ├── php.svg │ ├── react.svg │ └── server.js └── yarn.lock ├── 03-react-vue-elm ├── elm-package.json ├── package.json ├── razzle.config.js ├── src │ ├── App.css │ ├── App.elm │ ├── App.js │ ├── App.vue │ ├── Main.elm │ ├── client.js │ ├── index.js │ ├── react.svg │ └── server.js └── yarn.lock ├── 04-react-vue-elm-php ├── elm-package.json ├── package.json ├── razzle.config.js ├── src │ ├── App.css │ ├── App.elm │ ├── App.js │ ├── App.vue │ ├── Lol.php │ ├── Main.elm │ ├── client.js │ ├── index.js │ ├── php.svg │ ├── react.svg │ └── server.js └── yarn.lock └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | *.log 3 | npm-debug.log* 4 | .DS_Store 5 | 6 | coverage 7 | node_modules 8 | build 9 | .env.local 10 | .env.development.local 11 | .env.test.local 12 | .env.production.local 13 | elm-stuff 14 | .elm-static-html -------------------------------------------------------------------------------- /01-react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "01-react", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "start": "razzle start", 7 | "build": "razzle build", 8 | "test": "razzle test --env=jsdom", 9 | "start:prod": "NODE_ENV=production node build/server.js" 10 | }, 11 | "dependencies": { 12 | "react": "^16.4.1", 13 | "react-dom": "^16.4.1", 14 | "react-router-dom": "^4.3.1" 15 | }, 16 | "devDependencies": { 17 | "razzle": "^2.1.0" 18 | } 19 | } -------------------------------------------------------------------------------- /01-react/src/App.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: 'Colfax'; 5 | } 6 | 7 | .Home { 8 | text-align: center; 9 | } 10 | 11 | .Home-logo { 12 | animation: Home-logo-spin infinite 20s linear; 13 | height: 100px; 14 | } 15 | 16 | .Home-header { 17 | background-color: #222; 18 | padding: 2rem; 19 | color: white; 20 | } 21 | 22 | .Home-intro { 23 | font-size: large; 24 | } 25 | 26 | .Home-resources { 27 | list-style: none; 28 | } 29 | 30 | @keyframes Home-logo-spin { 31 | from { 32 | transform: rotate(0deg); 33 | } 34 | to { 35 | transform: rotate(360deg); 36 | } 37 | } -------------------------------------------------------------------------------- /01-react/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Route from 'react-router-dom/Route'; 3 | import Switch from 'react-router-dom/Switch'; 4 | import logo from './react.svg'; 5 | 6 | import './App.css'; 7 | 8 | const App = () => ( 9 | 10 | ( 14 | 15 |
16 |
17 | logo 18 |

This is some React Code!

19 |
20 |
21 |
22 | )} 23 | /> 24 |
25 | ); 26 | 27 | export default App; 28 | -------------------------------------------------------------------------------- /01-react/src/client.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { hydrate } from 'react-dom'; 3 | import App from './App'; 4 | import BrowserRouter from 'react-router-dom/BrowserRouter'; 5 | 6 | // REACT 7 | hydrate( 8 | 9 | 10 | , 11 | document.getElementById('react') 12 | ); 13 | 14 | if (module.hot) { 15 | module.hot.accept(); 16 | } 17 | -------------------------------------------------------------------------------- /01-react/src/index.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import app from './server'; 3 | 4 | if (module.hot) { 5 | module.hot.accept('./server', function() { 6 | console.log('🔁 HMR Reloading `./server`...'); 7 | }); 8 | console.info('✅ Server-side HMR Enabled!'); 9 | } 10 | 11 | const port = process.env.PORT || 3000; 12 | 13 | export default express() 14 | .use((req, res) => app.handle(req, res)) 15 | .listen(port, function(err) { 16 | if (err) { 17 | console.error(err); 18 | return; 19 | } 20 | console.log(`> Started on port ${port}`); 21 | }); 22 | -------------------------------------------------------------------------------- /01-react/src/react.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /01-react/src/server.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import React from 'react'; 3 | import { StaticRouter } from 'react-router-dom'; 4 | import { renderToString } from 'react-dom/server'; 5 | import App from './App'; 6 | 7 | const assets = require(process.env.RAZZLE_ASSETS_MANIFEST); 8 | const server = express(); 9 | 10 | server 11 | .disable('x-powered-by') 12 | .use(express.static(process.env.RAZZLE_PUBLIC_DIR)) 13 | .get('*', async (req, res) => { 14 | // REACT 15 | const reactMarkup = renderToString( 16 | 17 | 18 | 19 | ); 20 | 21 | res.status(200).send( 22 | ` 23 | 24 | 25 | 26 | 27 | Welcome to Razzle 28 | 29 | 30 | ${ 31 | assets.client.css 32 | ? `` 33 | : '' 34 | } 35 | ${ 36 | process.env.NODE_ENV === 'production' 37 | ? `` 38 | : `` 39 | } 40 | 41 | 42 |
${reactMarkup}
43 | 44 | ` 45 | ); 46 | }); 47 | 48 | export default server; 49 | -------------------------------------------------------------------------------- /02-react-vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "01-react", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "start": "razzle start", 7 | "build": "razzle build", 8 | "test": "razzle test --env=jsdom", 9 | "start:prod": "NODE_ENV=production node build/server.js" 10 | }, 11 | "dependencies": { 12 | "react": "^16.4.1", 13 | "react-dom": "^16.4.1", 14 | "react-router-dom": "^4.3.1", 15 | "vue": "^2.5.16", 16 | "vue-server-renderer": "^2.5.16" 17 | }, 18 | "devDependencies": { 19 | "razzle": "^2.1.0", 20 | "vue-loader": "^15.2.4", 21 | "vue-style-loader": "^4.1.0", 22 | "vue-template-compiler": "^2.5.16" 23 | } 24 | } -------------------------------------------------------------------------------- /02-react-vue/razzle.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const makeLoaderFinder = require('razzle-dev-utils/makeLoaderFinder'); 4 | const VueLoaderPlugin = require('vue-loader/lib/plugin'); 5 | 6 | module.exports = { 7 | modify(config) { 8 | // ----- 9 | // VUE 10 | // ----- 11 | config.resolve.extensions = [...config.resolve.extensions, '.vue']; 12 | config.resolve.alias = Object.assign({}, config.resolve.alias, { 13 | vue$: 'vue/dist/vue.esm.js', 14 | }); 15 | 16 | config.module.rules = config.module.rules.filter( 17 | rule => !makeLoaderFinder('css-loader')(rule) 18 | ); 19 | config.module.rules.push({ 20 | test: /\.vue$/, 21 | loader: 'vue-loader', 22 | }); 23 | 24 | config.module.rules[0] = { 25 | test: /\.css$/, 26 | use: [require.resolve('vue-style-loader'), require.resolve('css-loader')], 27 | }; 28 | 29 | config.plugins.push(new VueLoaderPlugin()); 30 | 31 | return config; 32 | }, 33 | }; 34 | -------------------------------------------------------------------------------- /02-react-vue/src/App.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: 'Colfax'; 5 | } 6 | 7 | .Home { 8 | text-align: center; 9 | } 10 | 11 | .Home-logo { 12 | animation: Home-logo-spin infinite 20s linear; 13 | height: 100px; 14 | } 15 | 16 | .Home-header { 17 | background-color: #222; 18 | padding: 2rem; 19 | color: white; 20 | } 21 | 22 | .Home-intro { 23 | font-size: large; 24 | } 25 | 26 | .Home-resources { 27 | list-style: none; 28 | } 29 | 30 | @keyframes Home-logo-spin { 31 | from { 32 | transform: rotate(0deg); 33 | } 34 | to { 35 | transform: rotate(360deg); 36 | } 37 | } -------------------------------------------------------------------------------- /02-react-vue/src/App.js: -------------------------------------------------------------------------------- 1 | import './App.css'; 2 | import React from 'react'; 3 | import Route from 'react-router-dom/Route'; 4 | import Switch from 'react-router-dom/Switch'; 5 | import logo from './react.svg'; 6 | import Vue from 'vue'; 7 | import VueApp from './App.vue'; 8 | 9 | const App = () => ( 10 | 11 | ( 15 | 16 |
17 |
18 | logo 19 |

This is some React Code!

20 |
21 |
22 |
23 | )} 24 | /> 25 |
26 | ); 27 | 28 | export default App; 29 | 30 | // export a factory function for creating fresh app, router and store 31 | // instances 32 | export function createVueApp() { 33 | const app = new Vue({ 34 | // the root instance simply renders the App component. 35 | render: h => h(VueApp), 36 | }); 37 | 38 | return { app }; 39 | } 40 | -------------------------------------------------------------------------------- /02-react-vue/src/App.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 22 | 23 | -------------------------------------------------------------------------------- /02-react-vue/src/client.js: -------------------------------------------------------------------------------- 1 | // REACT 2 | import React from 'react'; 3 | import { hydrate } from 'react-dom'; 4 | import BrowserRouter from 'react-router-dom/BrowserRouter'; 5 | 6 | // REACT AND VUE FROM THE SAME FILE! 7 | import App, { createVueApp } from './App'; 8 | 9 | // VUE 10 | const { app } = createVueApp(); 11 | app.$mount('#vue'); 12 | 13 | // REACT 14 | hydrate( 15 | 16 | 17 | , 18 | document.getElementById('react') 19 | ); 20 | 21 | if (module.hot) { 22 | module.hot.accept(); 23 | } 24 | -------------------------------------------------------------------------------- /02-react-vue/src/index.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import app from './server'; 3 | 4 | if (module.hot) { 5 | module.hot.accept('./server', function() { 6 | console.log('🔁 HMR Reloading `./server`...'); 7 | }); 8 | console.info('✅ Server-side HMR Enabled!'); 9 | } 10 | 11 | const port = process.env.PORT || 3000; 12 | 13 | export default express() 14 | .use((req, res) => app.handle(req, res)) 15 | .listen(port, function(err) { 16 | if (err) { 17 | console.error(err); 18 | return; 19 | } 20 | console.log(`> Started on port ${port}`); 21 | }); 22 | -------------------------------------------------------------------------------- /02-react-vue/src/php.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Official PHP Logo 4 | 5 | 6 | 7 | image/svg+xml 8 | 9 | Official PHP Logo 10 | 11 | 12 | Colin Viebrock 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | Copyright Colin Viebrock 1997 - All rights reserved. 25 | 26 | 27 | 1997 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /02-react-vue/src/react.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /02-react-vue/src/server.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | // VUE 3 | import Vue from 'vue'; 4 | const renderer = require('vue-server-renderer').createRenderer(); 5 | // REACT 6 | import React from 'react'; 7 | import { StaticRouter } from 'react-router-dom'; 8 | import { renderToString } from 'react-dom/server'; 9 | 10 | // REACT AND VUE IN ONE FILE! 11 | import App, { createVueApp } from './App'; 12 | 13 | const assets = require(process.env.RAZZLE_ASSETS_MANIFEST); 14 | const server = express(); 15 | 16 | server 17 | .disable('x-powered-by') 18 | .use(express.static(process.env.RAZZLE_PUBLIC_DIR)) 19 | .get('*', async (req, res) => { 20 | // VUE 21 | const context = { url: req.url }; 22 | const { app } = createVueApp(context); 23 | const vueMarkup = await renderer.renderToString(app); 24 | 25 | // REACT 26 | const reactMarkup = renderToString( 27 | 28 | 29 | 30 | ); 31 | 32 | res.status(200).send( 33 | ` 34 | 35 | 36 | 37 | 38 | Welcome to Razzle 39 | 40 | 41 | ${ 42 | assets.client.css 43 | ? `` 44 | : '' 45 | } 46 | ${ 47 | process.env.NODE_ENV === 'production' 48 | ? `` 49 | : `` 50 | } 51 | 52 | 53 |
${vueMarkup}
54 |
${reactMarkup}
55 | 56 | ` 57 | ); 58 | }); 59 | 60 | export default server; 61 | -------------------------------------------------------------------------------- /03-react-vue-elm/elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "summary": "helpful summary of your project, less than 80 characters", 4 | "repository": "https://github.com/user/project.git", 5 | "license": "BSD3", 6 | "source-directories": [ 7 | "src" 8 | ], 9 | "exposed-modules": [], 10 | "dependencies": { 11 | "elm-lang/core": "5.1.1 <= v < 6.0.0", 12 | "elm-lang/html": "2.0.0 <= v < 3.0.0", 13 | "eeue56/elm-html-in-elm": "2.0.0 <= v < 3.0.0" 14 | }, 15 | "elm-version": "0.18.0 <= v < 0.19.0" 16 | } -------------------------------------------------------------------------------- /03-react-vue-elm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "01-react", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "start": "razzle start", 7 | "build": "razzle build", 8 | "test": "razzle test --env=jsdom", 9 | "start:prod": "NODE_ENV=production node build/server.js" 10 | }, 11 | "dependencies": { 12 | "react": "^16.4.1", 13 | "react-dom": "^16.4.1", 14 | "react-router-dom": "^4.3.1", 15 | "vue": "^2.5.16", 16 | "vue-server-renderer": "^2.5.16", 17 | "elm-static-html-lib": "^0.0.8-alpha.2" 18 | }, 19 | "devDependencies": { 20 | "elm-hot-loader": "^0.5.4", 21 | "elm-webpack-loader": "4.5.0", 22 | "razzle": "^2.1.0", 23 | "vue-loader": "^15.2.4", 24 | "vue-style-loader": "^4.1.0", 25 | "vue-template-compiler": "^2.5.16" 26 | } 27 | } -------------------------------------------------------------------------------- /03-react-vue-elm/razzle.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const makeLoaderFinder = require('razzle-dev-utils/makeLoaderFinder'); 4 | const VueLoaderPlugin = require('vue-loader/lib/plugin'); 5 | 6 | module.exports = { 7 | modify(config, { dev }) { 8 | // ----- 9 | // VUE 10 | // ----- 11 | config.resolve.extensions = [...config.resolve.extensions, '.vue']; 12 | config.resolve.alias = Object.assign({}, config.resolve.alias, { 13 | vue$: 'vue/dist/vue.esm.js', 14 | }); 15 | 16 | config.module.rules = config.module.rules.filter( 17 | rule => !makeLoaderFinder('css-loader')(rule) 18 | ); 19 | config.module.rules.push({ 20 | test: /\.vue$/, 21 | loader: 'vue-loader', 22 | }); 23 | 24 | config.module.rules[0] = { 25 | test: /\.css$/, 26 | use: [require.resolve('vue-style-loader'), require.resolve('css-loader')], 27 | }; 28 | 29 | config.plugins.push(new VueLoaderPlugin()); 30 | 31 | // ----- 32 | // ELM 33 | // ----- 34 | config.module.noParse = [/.elm$/]; 35 | 36 | config.module.rules[2].exclude.push(/\.(elm)$/); 37 | 38 | config.resolve.extensions.push('.elm'); 39 | 40 | if (dev) { 41 | config.module.rules.push({ 42 | test: /\.elm$/, 43 | exclude: [/elm-stuff/, /node_modules/], 44 | use: [ 45 | { 46 | loader: 'elm-hot-loader', 47 | }, 48 | { 49 | loader: 'elm-webpack-loader', 50 | options: { 51 | verbose: true, 52 | warn: true, 53 | pathToMake: require('elm/platform').executablePaths['elm-make'], 54 | forceWatch: true, 55 | }, 56 | }, 57 | ], 58 | }); 59 | } else { 60 | // Production 61 | config.module.rules.push({ 62 | test: /\.elm$/, 63 | exclude: [/elm-stuff/, /node_modules/], 64 | use: [ 65 | { 66 | loader: 'elm-webpack-loader', 67 | options: { 68 | pathToMake: require('elm/platform').executablePaths['elm-make'], 69 | }, 70 | }, 71 | ], 72 | }); 73 | } 74 | 75 | return config; 76 | }, 77 | }; 78 | -------------------------------------------------------------------------------- /03-react-vue-elm/src/App.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: 'Colfax'; 5 | } 6 | 7 | .Home { 8 | text-align: center; 9 | } 10 | 11 | .Home-logo { 12 | animation: Home-logo-spin infinite 20s linear; 13 | height: 100px; 14 | } 15 | 16 | .Home-header { 17 | background-color: #222; 18 | padding: 2rem; 19 | color: white; 20 | } 21 | 22 | .Home-intro { 23 | font-size: large; 24 | } 25 | 26 | .Home-resources { 27 | list-style: none; 28 | } 29 | 30 | @keyframes Home-logo-spin { 31 | from { 32 | transform: rotate(0deg); 33 | } 34 | to { 35 | transform: rotate(360deg); 36 | } 37 | } -------------------------------------------------------------------------------- /03-react-vue-elm/src/App.elm: -------------------------------------------------------------------------------- 1 | module App exposing (..) 2 | 3 | import Html exposing (..) 4 | import Html.Attributes exposing (..) 5 | import Html.Events exposing (..) 6 | import Json.Decode 7 | 8 | 9 | type alias Model = 10 | {counter: Int} 11 | 12 | 13 | init : ( Model, Cmd Msg ) 14 | init = 15 | ( Model 0, Cmd.none ) 16 | 17 | decodeModel : Json.Decode.Decoder Model 18 | decodeModel = 19 | Json.Decode.map Model 20 | (Json.Decode.field "counter" Json.Decode.int) 21 | 22 | -- UPDATE 23 | 24 | 25 | type Msg 26 | = Inc 27 | 28 | 29 | update : Msg -> Model -> ( Model, Cmd Msg ) 30 | update message model = 31 | case message of 32 | Inc -> 33 | {model | counter = model.counter + 1} ! [] 34 | 35 | imgStyle : Attribute msg 36 | imgStyle = 37 | style 38 | [ ("margin", "0 auto") 39 | , ("display", "block") 40 | , ("height", "100px") 41 | ] 42 | 43 | containerStyle : Attribute msg 44 | containerStyle = 45 | style 46 | [ ("margin", "0 auto") 47 | , ("display", "block") 48 | , ("text-align", "center") 49 | , ("background", "#eee") 50 | , ("padding", "2rem") 51 | ] 52 | 53 | -- VIEW 54 | 55 | 56 | view : Model -> Html Msg 57 | view model = 58 | div [ containerStyle ] 59 | [ 60 | img [ imgStyle, src "https://upload.wikimedia.org/wikipedia/commons/f/f3/Elm_logo.svg" ] [] 61 | , h1 [] 62 | [ text "This is some Elm code!" 63 | ] 64 | , p [] [ text "Click on the button below to increment the state." ] 65 | 66 | , div [] 67 | [ button 68 | [ class "btn btn-primary" 69 | , onClick Inc 70 | ] 71 | [ text "+ 1" ] 72 | , text <| toString model 73 | ] 74 | ] -------------------------------------------------------------------------------- /03-react-vue-elm/src/App.js: -------------------------------------------------------------------------------- 1 | import './App.css'; 2 | import React from 'react'; 3 | import Route from 'react-router-dom/Route'; 4 | import Switch from 'react-router-dom/Switch'; 5 | import logo from './react.svg'; 6 | import Vue from 'vue'; 7 | import VueApp from './App.vue'; 8 | 9 | const App = () => ( 10 | 11 | ( 15 | 16 |
17 |
18 | logo 19 |

This is some React Code!

20 |
21 |
22 |
23 | )} 24 | /> 25 |
26 | ); 27 | 28 | export default App; 29 | 30 | // export a factory function for creating fresh app, router and store 31 | // instances 32 | export function createVueApp() { 33 | const app = new Vue({ 34 | // the root instance simply renders the App component. 35 | render: h => h(VueApp), 36 | }); 37 | 38 | return { app }; 39 | } 40 | -------------------------------------------------------------------------------- /03-react-vue-elm/src/App.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 22 | 23 | -------------------------------------------------------------------------------- /03-react-vue-elm/src/Main.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (main) 2 | 3 | import Html 4 | import App exposing (init, update, view) 5 | 6 | main : Program Never App.Model App.Msg 7 | main = 8 | Html.program 9 | { init = init 10 | , update = update 11 | , view = view 12 | , subscriptions = always Sub.none 13 | } -------------------------------------------------------------------------------- /03-react-vue-elm/src/client.js: -------------------------------------------------------------------------------- 1 | // REACT 2 | import React from 'react'; 3 | import { hydrate } from 'react-dom'; 4 | import BrowserRouter from 'react-router-dom/BrowserRouter'; 5 | // REACT AND VUE FROM THE SAME FILE! 6 | import App, { createVueApp } from './App'; 7 | import Elm from './Main'; // Import ELM 8 | 9 | // VUE 10 | const { app } = createVueApp(); 11 | app.$mount('#vue'); 12 | 13 | // REACT 14 | hydrate( 15 | 16 | 17 | , 18 | document.getElementById('react') 19 | ); 20 | 21 | // ELM 22 | Elm.Main.embed(document.getElementById('elm')); 23 | 24 | if (module.hot) { 25 | module.hot.accept(); 26 | } 27 | -------------------------------------------------------------------------------- /03-react-vue-elm/src/index.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import app from './server'; 3 | 4 | if (module.hot) { 5 | module.hot.accept('./server', function() { 6 | console.log('🔁 HMR Reloading `./server`...'); 7 | }); 8 | console.info('✅ Server-side HMR Enabled!'); 9 | } 10 | 11 | const port = process.env.PORT || 3000; 12 | 13 | export default express() 14 | .use((req, res) => app.handle(req, res)) 15 | .listen(port, function(err) { 16 | if (err) { 17 | console.error(err); 18 | return; 19 | } 20 | console.log(`> Started on port ${port}`); 21 | }); 22 | -------------------------------------------------------------------------------- /03-react-vue-elm/src/react.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /03-react-vue-elm/src/server.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | // VUE 3 | import Vue from 'vue'; 4 | const renderer = require('vue-server-renderer').createRenderer(); 5 | 6 | // REACT 7 | import React from 'react'; 8 | import { StaticRouter } from 'react-router-dom'; 9 | import { renderToString } from 'react-dom/server'; 10 | 11 | // REACT AND VUE IN ONE FILE! 12 | import App, { createVueApp } from './App'; 13 | 14 | // ELM 15 | import elmStaticHtml from 'elm-static-html-lib'; 16 | require('./Main'); 17 | 18 | const assets = require(process.env.RAZZLE_ASSETS_MANIFEST); 19 | const server = express(); 20 | 21 | server 22 | .disable('x-powered-by') 23 | .use(express.static(process.env.RAZZLE_PUBLIC_DIR)) 24 | .get('*', async (req, res) => { 25 | // VUE 26 | const context = { url: req.url }; 27 | const { app } = createVueApp(context); 28 | const vueMarkup = await renderer.renderToString(app); 29 | 30 | // REACT 31 | const reactMarkup = renderToString( 32 | 33 | 34 | 35 | ); 36 | 37 | // ELM 38 | const model = { counter: 5 }; 39 | const options = { model: model, decoder: 'App.decodeModel' }; 40 | const elmMarkup = await elmStaticHtml(process.cwd(), 'App.view', options); 41 | 42 | res.status(200).send( 43 | ` 44 | 45 | 46 | 47 | 48 | Welcome to Razzle 49 | 50 | 51 | ${ 52 | assets.client.css 53 | ? `` 54 | : '' 55 | } 56 | ${ 57 | process.env.NODE_ENV === 'production' 58 | ? `` 59 | : `` 60 | } 61 | 62 | 63 |
${vueMarkup}
64 |
${reactMarkup}
65 |
${elmMarkup}
66 | 67 | ` 68 | ); 69 | }); 70 | 71 | export default server; 72 | -------------------------------------------------------------------------------- /04-react-vue-elm-php/elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "summary": "helpful summary of your project, less than 80 characters", 4 | "repository": "https://github.com/user/project.git", 5 | "license": "BSD3", 6 | "source-directories": [ 7 | "src" 8 | ], 9 | "exposed-modules": [], 10 | "dependencies": { 11 | "elm-lang/core": "5.1.1 <= v < 6.0.0", 12 | "elm-lang/html": "2.0.0 <= v < 3.0.0", 13 | "eeue56/elm-html-in-elm": "2.0.0 <= v < 3.0.0" 14 | }, 15 | "elm-version": "0.18.0 <= v < 0.19.0" 16 | } -------------------------------------------------------------------------------- /04-react-vue-elm-php/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "01-react", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "start": "razzle start", 7 | "build": "razzle build", 8 | "test": "razzle test --env=jsdom", 9 | "start:prod": "NODE_ENV=production node build/server.js" 10 | }, 11 | "dependencies": { 12 | "react": "^16.4.1", 13 | "react-dom": "^16.4.1", 14 | "react-router-dom": "^4.3.1", 15 | "vue": "^2.5.16", 16 | "vue-server-renderer": "^2.5.16", 17 | "elm-static-html-lib": "^0.0.8-alpha.2" 18 | }, 19 | "devDependencies": { 20 | "elm-hot-loader": "^0.5.4", 21 | "elm-webpack-loader": "4.5.0", 22 | "babel-preset-php": "^1.2.0", 23 | "razzle": "^2.1.0", 24 | "vue-loader": "^15.2.4", 25 | "vue-style-loader": "^4.1.0", 26 | "vue-template-compiler": "^2.5.16" 27 | } 28 | } -------------------------------------------------------------------------------- /04-react-vue-elm-php/razzle.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const makeLoaderFinder = require('razzle-dev-utils/makeLoaderFinder'); 4 | const VueLoaderPlugin = require('vue-loader/lib/plugin'); 5 | 6 | module.exports = { 7 | modify(config, { dev }) { 8 | // ----- 9 | // BABEL PHP WHAT'S GOOD?!?!?!?!? 10 | // ----- 11 | config.resolve.extensions.push('.php'); 12 | 13 | // Exclude from file-loader 14 | config.module.rules[ 15 | config.module.rules.findIndex(makeLoaderFinder('file-loader')) 16 | ].exclude.push(/\.(php)$/); 17 | 18 | // Don't parse as JS 19 | config.module.noParse = config.module.noParse 20 | ? config.module.noParse.concat([/.php$/]) 21 | : [/.php$/]; 22 | 23 | // Add a custom babel loader (in addition to the one for .js) 24 | // making sure to ignore .babelrc 25 | config.module.rules.push({ 26 | test: /\.php$/, 27 | include: config.module.rules.find( 28 | makeLoaderFinder('babel-loader') 29 | ).include, 30 | use: [ 31 | { 32 | loader: 'babel-loader', 33 | options: { 34 | presets: [require.resolve('babel-preset-php')], 35 | babelrc: false, 36 | }, 37 | }, 38 | ], 39 | }); 40 | 41 | // ----- 42 | // VUE 43 | // ----- 44 | config.resolve.extensions = [...config.resolve.extensions, '.vue']; 45 | config.resolve.alias = Object.assign({}, config.resolve.alias, { 46 | vue$: 'vue/dist/vue.esm.js', 47 | }); 48 | 49 | config.module.rules = config.module.rules.filter( 50 | rule => !makeLoaderFinder('css-loader')(rule) 51 | ); 52 | config.module.rules.push({ 53 | test: /\.vue$/, 54 | loader: 'vue-loader', 55 | }); 56 | 57 | config.module.rules.push({ 58 | test: /\.css$/, 59 | use: [ 60 | require.resolve('vue-style-loader'), 61 | require.resolve('css-loader'), 62 | ], 63 | }); 64 | 65 | config.plugins.push(new VueLoaderPlugin()); 66 | 67 | // ----- 68 | // ELM 69 | // ----- 70 | config.module.noParse.concat([/.elm$/]); 71 | 72 | config.module.rules[ 73 | config.module.rules.findIndex(makeLoaderFinder('file-loader')) 74 | ].exclude.push(/\.(elm)$/); 75 | 76 | config.resolve.extensions.push('.elm'); 77 | 78 | if (dev) { 79 | config.module.rules.push({ 80 | test: /\.elm$/, 81 | exclude: [/elm-stuff/, /node_modules/], 82 | use: [ 83 | // { 84 | // loader: 'elm-hot-loader', 85 | // }, 86 | { 87 | loader: 'elm-webpack-loader', 88 | options: { 89 | verbose: true, 90 | warn: true, 91 | pathToMake: require('elm/platform').executablePaths[ 92 | 'elm-make' 93 | ], 94 | forceWatch: true, 95 | }, 96 | }, 97 | ], 98 | }); 99 | } else { 100 | // Production 101 | config.module.rules.push({ 102 | test: /\.elm$/, 103 | exclude: [/elm-stuff/, /node_modules/], 104 | use: [ 105 | { 106 | loader: 'elm-webpack-loader', 107 | options: { 108 | pathToMake: require('elm/platform').executablePaths[ 109 | 'elm-make' 110 | ], 111 | }, 112 | }, 113 | ], 114 | }); 115 | } 116 | // remove eslint 117 | config.module.rules = config.module.rules.filter( 118 | rule => !makeLoaderFinder('eslint-loader')(rule) 119 | ); 120 | 121 | return config; 122 | }, 123 | }; 124 | -------------------------------------------------------------------------------- /04-react-vue-elm-php/src/App.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: 'Colfax'; 5 | } 6 | 7 | .Home { 8 | text-align: center; 9 | } 10 | 11 | .Home-logo { 12 | animation: Home-logo-spin infinite 20s linear; 13 | height: 100px; 14 | } 15 | 16 | .Home-header { 17 | background-color: #222; 18 | padding: 2rem; 19 | color: white; 20 | } 21 | 22 | .Home-intro { 23 | font-size: large; 24 | } 25 | 26 | .Home-resources { 27 | list-style: none; 28 | } 29 | 30 | @keyframes Home-logo-spin { 31 | from { 32 | transform: rotate(0deg); 33 | } 34 | to { 35 | transform: rotate(360deg); 36 | } 37 | } -------------------------------------------------------------------------------- /04-react-vue-elm-php/src/App.elm: -------------------------------------------------------------------------------- 1 | module App exposing (..) 2 | 3 | import Html exposing (..) 4 | import Html.Attributes exposing (..) 5 | import Html.Events exposing (..) 6 | import Json.Decode 7 | 8 | 9 | type alias Model = 10 | {counter: Int} 11 | 12 | 13 | init : ( Model, Cmd Msg ) 14 | init = 15 | ( Model 0, Cmd.none ) 16 | 17 | decodeModel : Json.Decode.Decoder Model 18 | decodeModel = 19 | Json.Decode.map Model 20 | (Json.Decode.field "counter" Json.Decode.int) 21 | 22 | -- UPDATE 23 | 24 | 25 | type Msg 26 | = Inc 27 | 28 | 29 | update : Msg -> Model -> ( Model, Cmd Msg ) 30 | update message model = 31 | case message of 32 | Inc -> 33 | {model | counter = model.counter + 1} ! [] 34 | 35 | imgStyle : Attribute msg 36 | imgStyle = 37 | style 38 | [ ("margin", "0 auto") 39 | , ("display", "block") 40 | , ("height", "100px") 41 | ] 42 | 43 | containerStyle : Attribute msg 44 | containerStyle = 45 | style 46 | [ ("margin", "0 auto") 47 | , ("display", "block") 48 | , ("text-align", "center") 49 | , ("background", "#eee") 50 | , ("padding", "2rem") 51 | ] 52 | 53 | -- VIEW 54 | 55 | 56 | view : Model -> Html Msg 57 | view model = 58 | div [ containerStyle ] 59 | [ 60 | img [ imgStyle, src "https://upload.wikimedia.org/wikipedia/commons/f/f3/Elm_logo.svg" ] [] 61 | , h1 [] 62 | [ text "This is some Elm code!" 63 | ] 64 | , p [] [ text "Click on the button below to increment the state." ] 65 | 66 | , div [] 67 | [ button 68 | [ class "btn btn-primary" 69 | , onClick Inc 70 | ] 71 | [ text "+ 1" ] 72 | , text <| toString model 73 | ] 74 | ] -------------------------------------------------------------------------------- /04-react-vue-elm-php/src/App.js: -------------------------------------------------------------------------------- 1 | import './App.css'; 2 | import React from 'react'; 3 | import Route from 'react-router-dom/Route'; 4 | import Switch from 'react-router-dom/Switch'; 5 | import logo from './react.svg'; 6 | import phpLogo from './php.svg'; 7 | import Lol from './Lol.php'; 8 | import Vue from 'vue'; 9 | import VueApp from './App.vue'; 10 | 11 | const App = () => ( 12 | 13 | ( 17 | 18 |
19 |
20 | logo 21 |

This is some React Code!

22 |
23 |
24 | 25 |
26 | )} 27 | /> 28 |
29 | ); 30 | 31 | export default App; 32 | 33 | // export a factory function for creating fresh app, router and store 34 | // instances 35 | export function createVueApp() { 36 | const app = new Vue({ 37 | // the root instance simply renders the App component. 38 | render: h => h(VueApp), 39 | }); 40 | 41 | return { app }; 42 | } 43 | -------------------------------------------------------------------------------- /04-react-vue-elm-php/src/App.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 22 | 23 | -------------------------------------------------------------------------------- /04-react-vue-elm-php/src/Lol.php: -------------------------------------------------------------------------------- 1 | createElement('div', [ 5 | 'style' => [ 6 | 'width' => '100%', 7 | 'textAlign' => 'center', 8 | 'background' => '#8892BF', 9 | 'padding' => '2rem 0' 10 | ] 11 | ], 12 | [$React->createElement('img', [ 13 | 'key' => 'image!', 14 | 'src' => $props->src, 15 | 'style' => [ 16 | 'width' => 'auto', 17 | 'height' => '100px', 18 | ], 19 | ]), 20 | $React->createElement('h1', [ 21 | 'key' => 'text', 22 | 'style' => [ 23 | 'width' => '100%', 24 | 'height' => '100%', 25 | ], 26 | ], 'This is some more React code!'), 27 | $React->createElement('div', [ 28 | 'key' => 'subtitle', 29 | 'onClick' => function () { 30 | alert('shoot!'); 31 | }, 32 | 'style' => [ 33 | 'width' => '100%', 34 | 'height' => '100%', 35 | 'fontSize' => '1.25rem', 36 | 'fontWeight' => 'bold', 37 | ], 38 | ], 'Written with PHP and transpiled with babel-preset-php!')] 39 | ); 40 | }; 41 | 42 | $module->exports = $App; -------------------------------------------------------------------------------- /04-react-vue-elm-php/src/Main.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (main) 2 | 3 | import Html 4 | import App exposing (init, update, view) 5 | 6 | main : Program Never App.Model App.Msg 7 | main = 8 | Html.program 9 | { init = init 10 | , update = update 11 | , view = view 12 | , subscriptions = always Sub.none 13 | } -------------------------------------------------------------------------------- /04-react-vue-elm-php/src/client.js: -------------------------------------------------------------------------------- 1 | // REACT 2 | import React from 'react'; 3 | import { hydrate } from 'react-dom'; 4 | import BrowserRouter from 'react-router-dom/BrowserRouter'; 5 | // REACT AND VUE FROM THE SAME FILE! 6 | import App, { createVueApp } from './App'; 7 | import Elm from './Main'; // Import ELM 8 | 9 | // VUE 10 | const { app } = createVueApp(); 11 | app.$mount('#vue'); 12 | 13 | // REACT 14 | // Expose React to PHP code. 15 | window.React = React; 16 | hydrate( 17 | 18 | 19 | , 20 | document.getElementById('react') 21 | ); 22 | 23 | // ELM 24 | Elm.Main.embed(document.getElementById('elm')); 25 | 26 | if (module.hot) { 27 | module.hot.accept(); 28 | } 29 | -------------------------------------------------------------------------------- /04-react-vue-elm-php/src/index.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import app from './server'; 3 | 4 | if (module.hot) { 5 | module.hot.accept('./server', function() { 6 | console.log('🔁 HMR Reloading `./server`...'); 7 | }); 8 | console.info('✅ Server-side HMR Enabled!'); 9 | } 10 | 11 | const port = process.env.PORT || 3000; 12 | 13 | export default express() 14 | .use((req, res) => app.handle(req, res)) 15 | .listen(port, function(err) { 16 | if (err) { 17 | console.error(err); 18 | return; 19 | } 20 | console.log(`> Started on port ${port}`); 21 | }); 22 | -------------------------------------------------------------------------------- /04-react-vue-elm-php/src/php.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Official PHP Logo 4 | 5 | 6 | 7 | image/svg+xml 8 | 9 | Official PHP Logo 10 | 11 | 12 | Colin Viebrock 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | Copyright Colin Viebrock 1997 - All rights reserved. 25 | 26 | 27 | 1997 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /04-react-vue-elm-php/src/react.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /04-react-vue-elm-php/src/server.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | // VUE 3 | import Vue from 'vue'; 4 | const renderer = require('vue-server-renderer').createRenderer(); 5 | 6 | // REACT 7 | import React from 'react'; 8 | import { StaticRouter } from 'react-router-dom'; 9 | import { renderToString } from 'react-dom/server'; 10 | 11 | // REACT AND VUE IN ONE FILE! 12 | import App, { createVueApp } from './App'; 13 | 14 | // ELM 15 | import elmStaticHtml from 'elm-static-html-lib'; 16 | require('./Main'); 17 | 18 | // Expose React to PHP. 19 | global.React = React; 20 | const assets = require(process.env.RAZZLE_ASSETS_MANIFEST); 21 | const server = express(); 22 | 23 | server 24 | .disable('x-powered-by') 25 | .use(express.static(process.env.RAZZLE_PUBLIC_DIR)) 26 | .get('*', async (req, res) => { 27 | // VUE 28 | const context = { url: req.url }; 29 | const { app } = createVueApp(context); 30 | const vueMarkup = await renderer.renderToString(app); 31 | 32 | // REACT 33 | const reactMarkup = renderToString( 34 | 35 | 36 | 37 | ); 38 | 39 | // ELM 40 | const model = { counter: 5 }; 41 | const options = { model: model, decoder: 'App.decodeModel' }; 42 | const elmMarkup = await elmStaticHtml(process.cwd(), 'App.view', options); 43 | 44 | res.status(200).send( 45 | ` 46 | 47 | 48 | 49 | 50 | Welcome to Razzle 51 | 52 | 53 | ${ 54 | assets.client.css 55 | ? `` 56 | : '' 57 | } 58 | ${ 59 | process.env.NODE_ENV === 'production' 60 | ? `` 61 | : `` 62 | } 63 | 64 | 65 |
${vueMarkup}
66 |
${reactMarkup}
67 |
${elmMarkup}
68 | 69 | ` 70 | ); 71 | }); 72 | 73 | export default server; 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Razzle React Vue Elm PHP...lol 2 | 3 | ![Blazing FAST!](https://img.shields.io/badge/speed-blazing%20%F0%9F%94%A5-brightgreen.svg) 4 | 5 | These apps are a demonstration of how [Razzle](https://github.com/jaredpalmer/razzle) can adapt to changing needs. 6 | 7 | It includes examples of universal server rendering with React, React in PHP via Babel-PHP, Vue, and Elm with hot module replacement....all at the same time. 8 | 9 | --- 10 | #### The punch line... 11 | 12 | All of the code in `razzle.config.js` in the last example can actually just be replaced with new Razzle v2 plugins: 13 | 14 | ```js 15 | 16 | module.exports = { 17 | plugins: ['elm', 'vue', 'php'] // react is the default 18 | } 19 | 20 | ``` 21 | 22 | 23 | ![screenshot 2018-06-14 18 30 44](https://user-images.githubusercontent.com/4060187/41441580-45fe34b8-7001-11e8-8e66-d7f39eaff571.png) 24 | --------------------------------------------------------------------------------