A seed project which shows how to create an SPA Play Framework application using Gulp task runner for frontend asset compilation without depending on custom sbt plugins. This is a standalone version of my SBT Play Gulp plugin and allows for full control over how to integrate SBT and Gulp.
You can use this Play app template to create a Play Framework & Yeoman Gulp Angular hybrid project.
Unlike the demo project for the SBT Play Gulp plugin, you do not need to depend on my custom sbt plugin.
https://play-gulp-standalone.herokuapp.com
- The frontend page built with Gulp is served at the root path
/
. - The default Play landing page is available at the path
/oldhome
. - You can see the list of available routes at
/routes
.
Thanks to the powerful Heroku multi build pack, you can deploy this non-standard hybrid application to Heroku in one go.
Since this is a complex project, the first deployment to Heroku might take 5 to 10 minutes. Enjoy a cup or two of coffee while the Heroku server does heavy lifting for you.
This template Play project is primarily based on the two key files: app/controllers/GulpAssets.scala
and project/PlayGulp.scala
.
GulpAssets.scala
is a controller that serves JavaScript (compiled from coffee script, TypeScript etc.), CSS (compiled from SCSS) and other static assets such as images and icons from the Gulp AngularJS HTML project under ui
directory. PlayGulp.scala
is a Play project file which weaves Gulp tasks into sbt project compilation processes.
The majority of the code which was used in the sbt-play-gulp plugin moved from a sbt project to play-gulp-standalone/project/PlayGulp.scala
directory of the Play project.
Install npm (and optionally the LiveReload browser plugin).
Install gulp and bower packages:
$ npm install -g gulp bower
Clone and initialize the repo:
$ git clone [email protected]:mmizutani/play-gulp-standalone.git
$ cd play-gulp-standalone
$ ./postinstall.sh
Check that Gulp runs successfully by running either
$ activator
> gulp
Using gulpfile ~/play-gulp-standalone/ui/gulpfile.js
Starting 'clean'...
Finished 'clean' after 17 ms
Starting 'default'...
Starting 'scripts'...
Starting 'styles'...
Starting 'partials'...
Starting 'fonts'...
Starting 'other'...
Finished 'default' after 397 ms
gulp-inject 2 files into index.scss.
all files 7.88 kB
Finished 'scripts' after 641 ms
Finished 'partials' after 325 ms
Finished 'fonts' after 292 ms
Finished 'other' after 254 ms
Finished 'styles' after 2.04 s
Starting 'inject'...
gulp-inject 1 files into index.html.
gulp-inject 10 files into index.html.
Finished 'inject' after 70 ms
Starting 'html'...
gulp-inject 1 files into index.html.
'dist/' styles/app-e5505249f9.css 120.34 kB
'dist/' styles/vendor-1dddaadd0b.css 58.62 kB
'dist/' scripts/app-b3239829ef.js 6.26 kB
'dist/' scripts/vendor-3123871ad2.js 442.91 kB
'dist/' index.html 636 B
'dist/' all files 628.78 kB
Finished 'html' after 13 s
Starting 'build'...
Finished 'build' after 6.71 μs
or
$ cd ui
$ gulp
Now we are ready to run the app at http://localhost:9000.
$ cd play-gulp-standalone
$ activator
> compile
> run
You can see the directory paths from which each static asset is served to the web browser:
[info] - controllers.GulpAssets - Serving /home/dev/play-gulp-standalone/ui/.tmp/serve/index.html
[info] - controllers.GulpAssets - Serving /home/dev/play-gulp-standalone/ui/bower_components/angular/angular.js
[info] - controllers.GulpAssets - Serving /home/dev/play-gulp-standalone/ui/bower_components/angular-animate/angular-animate.js
[info] - controllers.GulpAssets - Serving /home/dev/play-gulp-standalone/ui/.tmp/serve/app/index.css
[info] - controllers.GulpAssets - Serving /home/dev/play-gulp-standalone/ui/bower_components/animate.css/animate.css
[info] - controllers.GulpAssets - Serving /home/dev/play-gulp-standalone/ui/bower_components/toastr/toastr.css
[info] - controllers.GulpAssets - Serving /home/dev/play-gulp-standalone/ui/bower_components/jquery/dist/jquery.js
[info] - controllers.GulpAssets - Serving /home/dev/play-gulp-standalone/ui/bower_components/angular-cookies/angular-cookies.js
[info] - controllers.GulpAssets - Serving /home/dev/play-gulp-standalone/ui/bower_components/angular-touch/angular-touch.js
[info] - controllers.GulpAssets - Serving /home/dev/play-gulp-standalone/ui/bower_components/angular-sanitize/angular-sanitize.js
[info] - controllers.GulpAssets - Serving /home/dev/play-gulp-standalone/ui/bower_components/angular-resource/angular-resource.js
[info] - controllers.GulpAssets - Serving /home/dev/play-gulp-standalone/ui/bower_components/angular-ui-router/release/angular-ui-router.js
[info] - controllers.GulpAssets - Serving /home/dev/play-gulp-standalone/ui/bower_components/angular-bootstrap/ui-bootstrap-tpls.js
[info] - controllers.GulpAssets - Serving /home/dev/play-gulp-standalone/ui/src/app/index.module.js
...
You can turn this verbose logging off by editing conf/logback.xml
.
sbt's compile and run commands trigger the gulp watch
task via the run hook added by project/PlayGulp.scala
:
playRunHooks <+= (gulpDirectory, gulpFile).map {
(base, fileName) => GulpWatch(base, fileName)
}
(sbt commands run in the project root folder play-gulp-standalone/
, but gulp tasks inside the sbt console run in the subdirectory 'play-gulp-standalone/ui/')
You can do a local production test normally.
> stage
> testProd
sbt's dist command triggers the gulp build
task and packages the static assets built by Gulp into the classpath of the distribution package.
> dist
[info] Your package is ready in /home/dev/play-gulp-standalone/target/universal/play-gulp-standalone-1.0.zip
You can then unzip target/universal/play-gulp-standalone-1.0.zip
and again unzip ``/lib/play-gulp-standalone.play-gulp-standalone-1.0-assets.jar` to see how the built static assets are packaged for production.
play-gulp-standalone-1.0.zip (unzipped)
+- bin/
+- conf/
+- lib/
+- xxx.jar
+- yyy.jar
+- play-gulp-standalone.play-gulp-standalone-1.0-assets.jar (unzipped)
+- META-INF/
+- public/
+- assets/
+- bower_components/
+- scripts/
+- styles/
+- favicon.co
+- index.html
Install Yeoman and Yeoman gulp-angular template generator in addition to gulp and bower:
$ npm install -g yo generator-gulp-angular gulp bower
Scaffold your frontend AngularJS HTML project in the subdirectory play-gulp-standalone/ui
:
$ git clone [email protected]:mmizutani/play-gulp-standalone.git
$ cd play-gulp-standalone
$ rm -rf ui
$ mkdir ui
$ cd ui
$ yo gulp-angular
and choose any options which you prefer:
? Which version of Angular do you want? 1.4.0 (stable)
? Which Angular's modules would you want to have? (ngRoute and ngResource will be addressed after) angular-animate.js (enable animation features), angular-cookies.js (handle cookie management), angular-touch.js (for mobile development), angular-sanitize.js (to securely parse and manipulate HTML)
? Would you need jQuery or perhaps Zepto? jQuery 2.x (new version, lighter, IE9+)
? Would you like to use a REST resource library? ngResource, the official support for RESTful services
? Would you like to use a router ? UI Router, flexible routing with nested views
? Which UI framework do you want? Bootstrap, the most popular HTML, CSS, and JS framework
? How do you want to implements your Bootstrap components? Angular UI Bootstrap, Bootstrap components written in pure AngularJS by the AngularUI Team
? Which CSS preprocessor do you want? Sass (Node), Node.js binding to libsass, the C version of the popular stylesheet preprocessor, Sass.
? Which JS preprocessor do you want? None, I like to code in standard JavaScript.
? Which html template engine would you want? None, I like to code in standard HTML.
When running npm install gulp-protractor
, you might encounter an error related to selenium-webdriver, node-gyp and bufferutil for unmet optional dependency. As a workaround, this repo contains the gulp-protractor folder so that Gulp works regardless of this bug.
For the error that the libsass
binding was not found, run
$ cd ui
$ npm rebuild node-sass
To adjust the project directory structure, move the bower_components directory and change paths referring to it accordingly:
$ mv bower_components src/
ui/gulp/conf.js
@@ -25,7 +25,7 @@
exports.wiredep = {
exclude: [/bootstrap.js$/, /bootstrap-sass-official\/.*\.js/, /bootstrap\.css/],
- directory: 'bower_components'
+ directory: 'src/bower_components'
};
ui/.bowerrc
@@ -1,3 +1,3 @@
{
- "directory": "bower_components"
+ "directory": "src/bower_components"
}
Then check whether running Gulp's default task finishes successfully:
$ cd ui
$ gulp
Upon the initial push to Heroku, the server executes build processes in the following order.
- Install the Ruby build pack and bundle-install gems (
Gemfile
) for compiling SCSS. - Install the Node.js build pack (node and npm) and, as per the postinstall script in
package.json
, download node packages (ui/package.json
) including Gulp and bower packages (ui/bower.json
) such as AngularJS and Bootstrap. - Install the Scala build pack, install Open-JDK, Scala and sbt and download Java/Scala libraries.
- Build and stage the Play application.
- Run the application with the command in
Procfile
.
If you want to deploy the app to Heroku from local development machine, it is required to install the multi-build pack before pushing:
$ heroku buildpacks:set https://github.com/ddollar/heroku-buildpack-multi.git
$ git push heroku master
For troubleshooting, you can ssh into an ephemeral (read-only) copy of your deployed Heroku app files (a one-off dyno):
$ heroku run bash
~ $ ls
activator activator.bat activator-launch-1.3.6.jar app app.json bin build.sbt conf Gemfile Gemfile.lock LICENSE package.json Procfile project README.md test tmp ui vendor
~ $ exit
and tail the logs:
$ heroku logs -t
After the initial push to Heroku, build with the subsequent deploy will take shorter time thanks to caching by Heroku build packs, but it still takes long time. You should consider using the heroku sbt plugin for faster deployment. See the Play documentation for more details.