├── .gitignore ├── Dockerfile ├── README.md ├── circle.yml ├── pom.xml └── src ├── main ├── frontend │ ├── package.json │ ├── src │ │ ├── Application.jsx │ │ ├── Application.less │ │ └── app.js │ └── webpack.config.js ├── java │ └── .gitkeep ├── kotlin │ └── io │ │ └── mikael │ │ └── app │ │ ├── app.kt │ │ └── service.kt └── resources │ ├── application.properties │ └── static │ └── index.html └── test ├── java └── io │ └── mikael │ └── poc │ └── ApplicationTests.java └── kotlin └── .gitkeep /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | .settings 3 | .project 4 | .classpath 5 | /.idea/ 6 | /*.iml 7 | /devdb* 8 | /src/main/resources/git.properties 9 | /data/ 10 | node_modules/ 11 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM java:8 2 | RUN mkdir -p /app 3 | COPY target/*.jar /app/ 4 | USER nobody 5 | CMD ["/bin/sh", "-c", "java ${JAVA_OPTS} -jar /app/*.jar ${APP_OPTS}"] 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### POC: Kotlin + Spring Boot + Webpack + Babel + ReactJS 2 | 3 | ```bash 4 | mvn clean package spring-boot:run 5 | ``` 6 | 7 | The project provides backend services using Spring Boot, and generates a nice clean JavaScript frontend 8 | using the Maven Frontend plugin to first fetch a relevant Node.JS binary package, NPM, then run 9 | `npm install` to fetch a shitload of JS modules, and execute WebPack through `npm run prod-build`. 10 | 11 | WebPack goes on to load its Babel.JS module, and configure it with ES2015 and React Babel profiles. 12 | 13 | Babel then transpiles the JavaScript input files from shiny new ES2015 to ES5, 14 | which shitty browsers can actually run. 15 | 16 | WebPack slams all this JavaScript together: the entrypoint JS file set in the configuration file, 17 | everything that file imports, and everything THAT imports, ad nauseam, into the (possibly giant) 18 | `target/classes/static/bundle.js` file. 19 | 20 | Take a look at the `src/main/frontend` directory, and the configuration files and ES6 entry point there. 21 | 22 | Basically this should work on Linux x86 and x64, Windows 32 and 64 bit, and OSX. **Should.** 23 | 24 | You can also install NVM, have NVM install Node.JS LTS, upgrade your NPM to newest, then run `npm install` 25 | and `npm run-script dev-build` to manually compile the stuff. It should be pretty easy to set up a watchdog 26 | to automatically build things, and trigger LiveReload. 27 | 28 | But that's a story to be explored on some other night. 29 | 30 | ### IntelliJ IDEA users 31 | 32 | File -> Settings -> Languages & Frameworks -> JavaScript: 33 | 34 | JavaScript language version: JSX Harmony 35 | 36 | Libraries -> ECMAScript 6: Enabled 37 | 38 | ----- 39 | 40 | File -> Settings -> Build, Execution, Deployment -> Compiler: 41 | 42 | Make project automatically: Enabled 43 | 44 | ----- 45 | 46 | CTRL-SHIFT A -> Registry... : 47 | 48 | compiler.automake.allow.when.app.running: Enabled 49 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | java: 3 | version: oraclejdk8 4 | services: 5 | - docker 6 | 7 | general: 8 | artifacts: 9 | - "target/*.jar" 10 | 11 | test: 12 | post: 13 | - "mkdir -p $CIRCLE_TEST_REPORTS/junit/" 14 | - "find . -type f -regex \".*/target/surefire-reports/.*xml\" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \\;" 15 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | io.mikael.poc 6 | kotlin-spring-boot-reactjs-poc 7 | 0.0.1-SNAPSHOT 8 | jar 9 | 10 | kotlin-spring-boot-reactjs-poc 11 | Kotlin + Spring Boot + ReactJS POC 12 | 13 | 14 | org.springframework.boot 15 | spring-boot-starter-parent 16 | 1.4.2.RELEASE 17 | 18 | 19 | 20 | 21 | 1.8 22 | 1.0.5 23 | 24 | 25 | 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-devtools 30 | 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-web 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-tomcat 39 | 40 | 41 | 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-starter-undertow 46 | 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-starter-test 51 | test 52 | 53 | 54 | 55 | org.jetbrains.kotlin 56 | kotlin-stdlib 57 | ${kotlin.version} 58 | 59 | 60 | 61 | org.jetbrains.kotlin 62 | kotlin-test 63 | ${kotlin.version} 64 | test 65 | 66 | 67 | 68 | 69 | ${project.basedir}/src/main/kotlin 70 | ${project.basedir}/src/test/kotlin 71 | 72 | 73 | org.springframework.boot 74 | spring-boot-maven-plugin 75 | 76 | 77 | kotlin-maven-plugin 78 | org.jetbrains.kotlin 79 | ${kotlin.version} 80 | 81 | 82 | compile 83 | compile 84 | compile 85 | 86 | 87 | src/main/java 88 | src/main/kotlin 89 | 90 | 91 | 92 | 93 | test-compile 94 | test-compile 95 | test-compile 96 | 97 | 98 | src/test/java 99 | src/test/kotlin 100 | 101 | 102 | 103 | 104 | 105 | 106 | com.github.eirslett 107 | frontend-maven-plugin 108 | 1.0 109 | 110 | src/main/frontend 111 | https://nodejs.org/dist/ 112 | v6.4.0 113 | 3.10.3 114 | target 115 | 116 | 117 | 118 | install node and npm 119 | 120 | install-node-and-npm 121 | 122 | generate-resources 123 | 124 | 125 | npm install 126 | 127 | npm 128 | 129 | 130 | install 131 | target 132 | 133 | 134 | 135 | webpack build 136 | 137 | npm 138 | 139 | generate-resources 140 | 141 | run-script dev-build 142 | 143 | 144 | 145 | 146 | 147 | org.apache.maven.plugins 148 | maven-compiler-plugin 149 | 150 | 151 | compile 152 | compile 153 | 154 | compile 155 | 156 | 157 | 158 | testCompile 159 | test-compile 160 | 161 | testCompile 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /src/main/frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sbmfp-frontend", 3 | "version": "0.0.1", 4 | "description": "SBMFP Frontend", 5 | "license": "MIT", 6 | "repository": "yeah", 7 | "private": true, 8 | "devDependencies": { 9 | "babel-core": "^6.18.2", 10 | "babel-loader": "^6.2.7", 11 | "babel-preset-es2015": "^6.18.0", 12 | "babel-preset-react": "^6.16.0", 13 | "babel-plugin-transform-object-assign": "^6.8.0", 14 | "css-loader": "^0.25.0", 15 | "expose-loader": "^0.7.1", 16 | "imports-loader": "^0.6.5", 17 | "style-loader": "^0.13.1", 18 | "webpack": "^1.13.3", 19 | "extract-text-webpack-plugin": "^1.0.1", 20 | "less": "^2.7.1", 21 | "less-loader": "^2.2.3" 22 | }, 23 | "dependencies": { 24 | "babel-runtime": "^6.18.0", 25 | "babel-polyfill": "^6.16.0", 26 | "history": "^4.4.0", 27 | "moment": "^2.15.2", 28 | "jquery": "^3.1.1", 29 | "react-bootstrap": "^0.30.6", 30 | "react": "^15.3.2", 31 | "react-dom": "^15.3.2", 32 | "react-router": "^3.0.0", 33 | "react-router-bootstrap": "^0.23.1" 34 | }, 35 | "scripts": { 36 | "watch": "webpack -d --watch", 37 | "dev-build": "webpack -d --display-modules", 38 | "prod-build": "webpack -d -p" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/frontend/src/Application.jsx: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import React from 'react'; 4 | import $ from 'jquery'; 5 | 6 | require('!style!css!less!./Application.less'); 7 | 8 | export class Application extends React.Component { 9 | constructor(props) { 10 | super(props); 11 | this.state = { 12 | name: 'Initial', 13 | counter: -1, 14 | when: '' 15 | }; 16 | } 17 | fetchState() { 18 | $.ajax('/api/hello').done((data) => this.setState(data)); 19 | } 20 | render() { 21 | return ( 22 |
23 | Hello {this.state.name} #{this.state.counter} at {this.state.timestamp} 24 |
25 | ); 26 | } 27 | componentDidMount() { 28 | this.interval = window.setInterval(this.fetchState.bind(this), 1000); 29 | } 30 | componentWillUnmount() { 31 | clearInterval(this.interval); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/frontend/src/Application.less: -------------------------------------------------------------------------------- 1 | div.app-hello-message { 2 | font-family: "Trebuchet MS", Verdana, sans-serif; 3 | font-size: 20pt; 4 | margin: 30pt; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/frontend/src/app.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import React from 'react'; 4 | import ReactDOM from 'react-dom'; 5 | 6 | import { Application } from './Application.jsx'; 7 | 8 | window.app = ReactDOM.render(, document.getElementById('content')); 9 | -------------------------------------------------------------------------------- /src/main/frontend/webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var path = require('path'); 3 | var ExtractTextPlugin = require('extract-text-webpack-plugin'); 4 | 5 | module.exports = { 6 | entry: "./src/app.js", 7 | output: { 8 | path: '../../../target/classes/static/', 9 | filename: "bundle.js" 10 | }, 11 | module: { 12 | loaders: [ 13 | { 14 | test: /\.css$/, 15 | loader: "style!css" 16 | }, 17 | { 18 | test: /\.jsx?$/, 19 | include: [ 20 | path.resolve(__dirname, "./src") 21 | ], 22 | loader: 'babel', 23 | query: { 24 | presets: ['es2015', 'react'] 25 | } 26 | }, 27 | { 28 | test: /\.css$/, 29 | loader: ExtractTextPlugin.extract("style-loader", "css-loader") 30 | }, 31 | { 32 | test: /\.less$/, 33 | loader: ExtractTextPlugin.extract("style-loader", "css-loader!less-loader") 34 | } 35 | ] 36 | }, 37 | plugins: [ 38 | new ExtractTextPlugin("[name].css") 39 | ] 40 | }; 41 | -------------------------------------------------------------------------------- /src/main/java/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelhg/spring-boot-webpack-es6-react-poc/ced51824c2cf57cf197c7cc7781bf3c2abcf9b76/src/main/java/.gitkeep -------------------------------------------------------------------------------- /src/main/kotlin/io/mikael/app/app.kt: -------------------------------------------------------------------------------- 1 | package io.mikael.app 2 | 3 | import org.springframework.boot.SpringApplication 4 | import org.springframework.boot.autoconfigure.SpringBootApplication 5 | import org.springframework.web.bind.annotation.GetMapping 6 | import org.springframework.web.bind.annotation.RestController 7 | import java.time.OffsetDateTime 8 | import java.util.concurrent.atomic.AtomicLong 9 | 10 | @SpringBootApplication 11 | open class Application { 12 | 13 | } 14 | 15 | @RestController 16 | open class AppController { 17 | 18 | private val counter = AtomicLong() 19 | 20 | @GetMapping("/api/hello") 21 | fun hello() = mapOf( 22 | "name" to "World", 23 | "counter" to counter.getAndIncrement().toString(), 24 | "timestamp" to OffsetDateTime.now().toString() 25 | ) 26 | 27 | } 28 | 29 | fun main(args: Array) { 30 | SpringApplication.run(Application::class.java, *args) 31 | } 32 | -------------------------------------------------------------------------------- /src/main/kotlin/io/mikael/app/service.kt: -------------------------------------------------------------------------------- 1 | package io.mikael.app 2 | 3 | import org.springframework.stereotype.Service 4 | 5 | @Service 6 | class HelloService { 7 | 8 | fun hello() = "Hello!" 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelhg/spring-boot-webpack-es6-react-poc/ced51824c2cf57cf197c7cc7781bf3c2abcf9b76/src/main/resources/application.properties -------------------------------------------------------------------------------- /src/main/resources/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Spring Boot + Maven Frontend Plugin + Node + Npm + Webpack + Babel + React + JQuery 6 | 7 | 8 |

Spring Boot + Maven Frontend Plugin + Node + Npm + Webpack + Babel + React + JQuery

9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/test/java/io/mikael/poc/ApplicationTests.java: -------------------------------------------------------------------------------- 1 | package io.mikael.poc; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; 9 | 10 | @RunWith(SpringRunner.class) 11 | @SpringBootTest(webEnvironment = RANDOM_PORT) 12 | public class ApplicationTests { 13 | 14 | @Test 15 | public void contextLoads() { 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/test/kotlin/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelhg/spring-boot-webpack-es6-react-poc/ced51824c2cf57cf197c7cc7781bf3c2abcf9b76/src/test/kotlin/.gitkeep --------------------------------------------------------------------------------