Now days, I develop all of my javascript in separated files with a set of logical folders (Services, Controllers, Lib, etc).
However I do it with Gulp and Bower on Node.js. Note, I don't use Node.JS for my server side code, just my front end assets. Think of it as a distribution tool for client side assets. I use Gulp-Watch to monitor my SRC folder and auto transform the final javascript file as I work on the source.
So I get the benefit of having everything be separated out logically, but still get the performance of a single compressed JS file for Production.
Here is an example bower.json file:
{
"name": "MyApp",
"private": true,
"dependencies": {
"jquery": "2.2.4",
"bootstrap": "v4.0.0-alpha.3",
"angular": "^1.5.8",
"font-awesome": "fontawesome#^4.6.3",
"angular-ui-router": "^0.3.1",
"angular-cookies": "^1.5.8",
"angular-ui-select": "^0.19.4",
"angular-ui-tree": "^2.17.0",
"angular-toastr": "^2.0.0",
"angular-animate": "^1.5.8",
"angular-sanitize": "^1.5.8",
"angular-chart": "^0.5.0",
"angular-loading-bar": "^0.9.0",
"angular-messages": "^1.5.8"
},
"devDependencies": {}
}
And here's the Gulp-File
/// <binding Clean='clean' />
"use strict";
var gulp = require("gulp");
var sass = require("gulp-sass"); //Syntactically Awesome Style Sheets
var sourcemaps = require('gulp-sourcemaps'); //Generate source maps for css and js
var autoprefixer = require('gulp-autoprefixer'); //auto prefix browser specific css
var cleanCss = require('gulp-clean-css'); //minimize css
var rename = require('gulp-rename'); //rename file in gulp stream
var concat = require('gulp-concat'); //concat mutliple files in gulp stream into one
var runSequence = require('run-sequence'); //run mutliple gulp taxes in parrellel or sequence
var uglify = require('gulp-uglify'); //minimize js
var webroot = "./wwwroot/"; //the root folder where the css and js is going
var paths = {
css: webroot + "css/", //path to my css
scss: webroot + "scss/", //path to my scss src files
destJs: webroot + "js/", //path to my js source files (also the destination)
lib: webroot + "/lib/" //path to my lib folder for storing libraries
};
//build both the dev and production styles
gulp.task('build-styles', function (done) {
return runSequence(["build-styles-dev", "build-styles-prod"]);
});
//sass transforms all my scss files individually (producing separate css files for each library I am using)
gulp.task('build-styles-dev', function () {
var sassOptions = {
errLogToConsole: true,
outputStyle: 'expanded',
sourceMap: true
};
return gulp.src([ //include all my libraries css files that isn't sass based in the front of the stream
paths.lib + '/angular-loading-bar/build/loading-bar.css',
paths.lib + '/angular-toastr/dist/angular-toastr.css',
paths.lib + '/angular-ui-select/dist/select.css',
paths.lib + '/angular-ui-tree/dist/angular-ui-tree.css',
paths.lib + '/tether/dist/css/tether.css',
paths.lib + '/tether/dist/css/tether-theme-basic.css',
paths.scss + '/site.scss' //my sass file (does imports etc in it)
])
.pipe(sass({ errLogToConsole: true, outputStyle: 'expanded', sourceMap: true }).on('error', sass.logError))
.pipe(autoprefixer()) //autoprefix browser specific css
.pipe(gulp.dest(paths.css)) //output all the sass and auto prefixed css files based on the source
.resume();
});
//same as above task except this one doesn't output the individual files. It concats them all together into one file, compresses it, and generates the source maps for it.
gulp.task('build-styles-prod', function () {
var sassOptions = {
errLogToConsole: true,
outputStyle: 'expanded',
sourceMap: true
};
return gulp.src([
paths.lib + '/angular-loading-bar/build/loading-bar.css',
paths.lib + '/angular-toastr/dist/angular-toastr.css',
paths.lib + '/angular-ui-select/dist/select.css',
paths.lib + '/angular-ui-tree/dist/angular-ui-tree.css',
paths.lib + '/tether/dist/css/tether.css',
paths.lib + '/tether/dist/css/tether-theme-basic.css',
paths.scss + '/site.scss'
])
.pipe(sass({ errLogToConsole: true, outputStyle: 'expanded', sourceMap: true }).on('error', sass.logError))
.pipe(autoprefixer())
.pipe(concat("compiled.css"))
.pipe(gulp.dest(paths.css))
.pipe(sourcemaps.init({ loadMaps: true }))
.pipe(cleanCss())
.pipe(rename({ suffix: '.min' }))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest(paths.css))
.resume();
});
//grabs all the library scripts and compresses them into one js file
gulp.task('build-scripts', function () {
return gulp.src([
paths.lib + 'jquery/dist/jquery.js',
paths.lib + 'tether/dist/js/tether.js',
paths.lib + 'angular/angular.js',
paths.lib + 'angular-messages/angular-messages.js',
paths.lib + 'angular-animate/angular-animate.js',
paths.lib + 'angular-cookies/angular-cookies.js',
paths.lib + 'angular-chart/angular-chart.js',
paths.lib + 'angular-loading-bar/build/loading-bar.js',
paths.lib + 'angular-toastr/dist/angular-toastr.js',
paths.lib + 'angular-sanitize/angular-sanitize.js',
paths.lib + 'angular-ui-router/release/angular-ui-router.js',
paths.lib + 'angular-ui-select/dist/select.js',
paths.lib + 'angular-ui-tree/dist/angular-ui-tree.js',
paths.lib + 'bootstrap/dist/js/bootstrap.js',
paths.js + 'app.js', //Load app.js first
paths.js + 'services/**/*.js', //Load all angular services next
paths.js + 'directives/**/*.js', //load directives after services
paths.js + 'controllers/**/*.js', //load angular controllers last after services and directives
paths.js + 'appLast.js', //A tail script I have load last for end of page logic, preventing the need for me to use document ready since this loads in the page footer.
])
.pipe(sourcemaps.init())
.pipe(gulp.dest(paths.destJs)) //output all the files individually (so file names need to be unique accross the whole system, generally not a problem)
.pipe(concat('scripts.js')) //concat all the scripts together into one file called scripts.js
.pipe(uglify()) //minimize all the scripts
.pipe(rename({ suffix: '.min' })) //rename to scripts.min.js
.pipe(sourcemaps.write('.')) //write the source maps
.pipe(gulp.dest(paths.destJs)); //output scripts.min.js
});
The missing part of my gulp file is the app scripts, which looks something like this (haven't finished it for this app yet).