├── .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 |
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 |
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 |
2 |
3 |
6 |
7 |
8 |
{{ greeting }}
9 |
10 |
11 |
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 |
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 |
2 |
3 |
4 | My Razzle Dazzle Application
5 |
6 |
7 |
8 |
{{ greeting }}
9 |
10 |
11 |
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 |
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 |
2 |
3 |
4 | My Razzle Dazzle Application
5 |
6 |
7 |
8 |
{{ greeting }}
9 |
10 |
11 |
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 | 
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 | 
24 |
--------------------------------------------------------------------------------