alpham8 alpham8 - 1 month ago 9
Node.js Question

best practice for compiling web assets via nodejs modules in a full stack php/js web app

I am searching for a method to compile my web assets together in an full stack web application. For instance, assume we have the following frameworks, libraries and modules used in our web app:

  • PHP with Symfony

  • JavaScript with angular or jquery and many other libraries like cldr, globalize, slider etc.

  • backend compilation using NodeJS and Composer

This is a typically, or better, traditionally set up for modern web apps. And I am struggling how, or with which modules I am able to compile all of my assets in a smooth way into my build destination. In my case, it is a public accessible web folder with all my assets bundled that folder.
It is neccessary to compile the following things:

  • JavaScript libraries (one or more *.js files)

  • fonts and images used in thus JavaScript libraries

  • CSS/LESS/SCSS files used in other frameworks (like Bootstrap) or used in JavaScript libraries

  • or a bunch of all of these coming with a library

The main things does bower at this point. But bower actually doesn't care on how we compile that libraries together in single files and how we copy fonts and/or images to our build destination.
I have already looked at grunt, bower and Yeoman. The last one seems to be a good tool to wrap things up like I need.

Any help, tips, or experience of your usage at this point would be great. I think this is a common question that many developers have, but it hasn't been answered, yet. I searched the internet about that for hours. So, any help would be welcome.


I am now using Node JS, Grunt and bootstrap-sass. So, we have also other assets, which wont be compiled throw my tasks. So I added an sync task. It copies only the modified files between two paths. This works great for me, but I am not sure if its best practice:

 * My Gruntfile.
module.exports = function (grunt)

    var relaxerrors = [

            sass: {
                options: {
                    includePaths: ["node_modules/bootstrap-sass/assets/stylesheets/bootstrap"],
                    precision: 10,
                    sourcemap: "inline",
                    trace: true,
                    unixNewlines: true
                dist: {
                    files: {
                        "web/css/frontend.min.css": "assets/styles/frontend.scss",
                        "web/css/backend.min.css": "assets/styles/backend.scss",
                        "web/lib/bootstrap/css/bootstrap.min.css": "node_modules/bootstrap-sass/assets/stylesheets/bootstrap.scss"
            sasslint: {
                target: [ "assets/styles/*.scss" ]
            jshint: {
                files: [ "assets/js/*.js" ],
                options: {
                    globals: {
                        jquery: true
            bootlint: {
                options: {
                    stoponerror: true,
                    showallerrors: true,
                    stoponwarning: false,
                    relaxerror: {
                        "E001": relaxerrors,
                        "W001": relaxerrors,
                        "W002": relaxerrors,
                        "W003": relaxerrors,
                        "W005": relaxerrors
                files: [ "App/Mvc/views/*.phtml", "App/Mvc/views/*.html" ]
            postcss: {
                options: {
                    map: true,
                    processors: [
                        require("pixrem")(), // add fallbacks for rem units
                            browsers: ["last 2 versions"]
                dist: {
                    src: ["web/lib/bootstrap/css/*.css", "web/css/*.css"]
            sync: {
                main: {
                files: [{
                    src: [
                      "node_modules/bootstrap-sass/assets/fonts/**", // include everything
                      '!**/*.txt' // but exclude txt files
                    dest: 'web/lib/bootstrap/fonts',
                pretend: true, // Don't do any IO. Before you run the task with `updateAndDelete` PLEASE MAKE SURE it doesn't remove too much.
                verbose: true // Display log messages when copying files

    grunt.registerTask("validate:styles", [ "sasslint" ]);
    grunt.registerTask("validate:js", [ "jshint" ]);
    grunt.registerTask("validate:bootstrap", [ "bootlint" ]);
    grunt.registerTask("validate:all", [ "validate:bootstrap", "validate:styles", "validate:js" ]);
    grunt.registerTask("build:styles", [ "validate:styles", "sass", "postcss", "sync" ]);

    // default task. Run all
    grunt.registerTask("default", [ "validate:all", "sass", "postcss", "sync" ]);

and my package.json:

  "name": "myProjectDeps",
  "version": "1.0.0",
  "dependencies": {
    "grunt": "^1.0.1",
    "grunt-bootlint": "^0.10.1",
    "grunt-cli": "^1.2.0",
    "grunt-contrib-compress": "^1.3.0",
    "grunt-contrib-concat": "^1.0.1",
    "grunt-contrib-cssmin": "^1.0.2",
    "grunt-contrib-jshint": "^1.0.0",
    "grunt-contrib-uglify": "^2.0.0",
    "grunt-sass-lint": "^0.2.0",
    "grunt-sync": "^0.6.2"
  "devDependencies": {
    "autoprefixer": "^6.5.1",
    "bootstrap-sass": "^3.3.7",
    "grunt-postcss": "^0.8.0",
    "grunt-sass": "^1.2.1",
    "node-sass": "^3.10.1",
    "pixrem": "^3.0.2",
    "typescript": "^2.0.6",
    "typings": "^1.5.0"

If it does not work for you with this, install grunt globally or call the grunt file relative to your project root in node_modules, e. g.

my/dir/root> ./node_modules/bin/grunt valdiate:styles

(not tested if it works).