Gulp - How do I control processing order with gulp-concat - angularjs

I'm trying to generate combined JavaScript and CSS resources into a single file using gulp-concat using something like this:
var concatjs = gulp
.src(['app/js/app.js','app/js/*Controller.js', 'app/js/*Service.js'])
.pipe(concat('app.js'))
.pipe(gulp.dest('build'));
I get a concatted file with this, but the order of the javascript files embedded in the combined output file is random - in this case the controllers are showing up before the initial app.js file, which causes problems when trying to load the Angular app that expects app.js before any of the related resources are loaded. Likewise for CSS resources that get combined end up in random order, and again the order is somewhat important - ie. bootstrap needs to load before the theme and any custom style sheets.
How can I set up the concatenation process so that the order remains intact?
Update
So it turns out the ordering above DOES actually work by explicitly specifying the file order in the array of file specs. So in this case the crucial thing is to list app/js/app.js first, then let the rest of the scripts where order doesn't matter in in any order.
The reason I failed to see this behavior (Duh!) is that Gulp Watch was running and the gulpfile.js update wasn't actually reflected in the output. Restarting gulp did update the script. Neophyte error...
Other Thoughts:
Still wondering though - is this the right place to specify build order? It seems you're now stuffing application logic (load order) into the build script, which doesn't feel right. Are there other approaches to address this?

For an angular application like the one in your example (and it's dependency management), I normally use this kind of syntax: gulp.src(['app\js\app.js', 'app\js\**\*.js']).
You can also use just gulp.src('app\js\**\*.js') if your app.js file is the first one in alphabetic order.
I see your point about moving the load file order into the build script: I had the same feeling till I started using gulp-inject for injecting the unminified files references in my index.html at development time and injecting the bundled, minified and versioned ones in the production index file. Using that glob ordering solution across all my development cycle made so sense to me that i don't think to it anymore.
Finally, a possible solution for this 'ordering smell' can be using browserify but to me it is just complicating the architecture for an angular application: in the end, as you said, you just need that one specific file is called before all the other ones.

For my js i use a particular structure/naming convention which helps. I split it up into directories by feature, where each 'feature' is then treated as a separate encapsulated module.
So for my projects i have,
app/js/
- app.js
- app.routes.js
- app.config.js
/core/
- core.js
- core.controllers.js
- core.services.js
/test/
- .spec.js test files for module here
/feature1/
- feature1.js
- feature1.controllers.js
/feature2/
- feature2.js
- feature2.controllers.js
...
So each directory has a file of the same name that simply has the initial module definition in it, which is all that app.js has in it for the whole app. So for feature1.js
angular.module('feature1', [])
and then subsequent files in the module retrieve the module and add things (controllers/services/factories etc) to it.
angular.module('feature1')
.controller(....)
Anyway, i'll get to the point...
As i have a predefined structure and know that a specific file has to go first for each module, i'm able to use the function below to sort everything into order before it gets processed by gulp.
This function depends on npm install file and npm install path
function getModules(src, app, ignore) {
var modules = [];
file.walkSync(src, function(dirPath, dirs, files) {
if(files.length < 1)
return;
var dir = path.basename(dirPath)
module;
if(ignore.indexOf(dir) === -1) {
module = dirPath === src ? app : dir;
files = files.sort(function(a, b) {
return path.basename(a, '.js') === module ? -1 : 1;
})
.filter(function(value) {
return value.indexOf('.') !== 0;
})
.map(function(value) {
return path.join(dirPath, value);
})
modules = modules.concat(files);
}
})
return modules;
}
It walks the directory structure passed to it, takes the files from each directory (or module) and sorts them into the correct order, ensuring that the module definition file is always first. It also ignores any directories that appear in the 'ignore' array and removes any hidden files that begin with '.'
Usage would be,
getModules(src, appName, ignoreDirs);
src is the dir you want to recurse from
appName is the name of your app.js file - so 'app'
ignoreDirs is an array of directory names you'd like to ignore
so
getModules('app/js', 'app', ['test']);
And it returns an array of all the files in your app in the correct order, which you could then use like:
gulp.task('scripts', function() {
var modules = getModules('app/js', 'app', ['test']);
return gulp.src(modules)
.pipe(concat('app.js'))
.pipe(gulp.dest('build'));
});

Related

Gulp Compiling Angularjs 1 Scripts in, incorrect order

Why does this happen? When I compile the scripts using GULP the console will display errors, explaining that my directives and/or my controllers are not registered. Then to correct this error I create the app variable within the controller file and it then renders a new error, then I put the app variable declaration back and everything works fine.
This is my Gulp Script
var gulp = require('gulp'),
plugins = require('gulp-load-plugins')({
pattern: ['gulp-*', 'gulp.*'],
replaceString: /\bgulp[\-.]/
});
var path = {
jsFiles: "./js/**",
scriptFile: "scripts.min.js",
output: "dist/assets/"
};
var options = {
ie8: true,
warnings: true,
mangle: true
};
gulp.task('scripts', function (cb) {
return gulp.src(path.jsFiles)
.pipe(plugins.sourcemaps.init())
.pipe(plugins.jsdoc3(cb))
.pipe(plugins.concat(path.scriptFile))
.pipe(plugins.babel())
.pipe(plugins.ngAnnotate())
.pipe(plugins.uglify(options))
.pipe(plugins.sourcemaps.write("../../maps"))
.pipe(gulp.dest(path.output))
})
TLDR: MY Gulp task sometimes compiles the AngularJS directives and controllers out of order rendering my app declaration undefined.
When you pass globe to the
gulp.src
No ordered is guaranteed, so it is possible to get wrong order time to time. But gulp.src also accepts array of the pathes you need to include and this should guarantee the order
So, try to split your bundle and pass path to the angular.min.js as a first element like this:
gulp.src(['path/to/angular.min.js', 'path/to/your/code'])
You should sort angular files, and there are some libs that does that.
https://www.npmjs.com/package/gulp-angular-filesort is one of them.

AngularJS application with different customizations

I got a "framework" created by us using AngularJS. It allows to build questionnaire system and it has many different parameters that control the behavior of framework.
Using this framework we've created 2 projects: projectA and projectB. The difference between these projects are the settings and assets (css, img, ...)
Both projects are stored on the same branch in git and only config file defines the project customization.
I can't think of the best way how these 2 projects can be easily deployed separately from the same code source using Gulp or something other.
Here are some ideas I got for the moment:
1. Have both settings files and images (e.g. logo_A.png and logo_B.png) in the code and choose appropriate during build using Gulp
2. Create folder customizations that will have 2 subfolders A and B with corresponding settings and assets
3. Create separate repository for each project installation scripts (not the code) and these scripts will do all the work
What is the best way in this case?
Finally, the easieast and most understandible solution was to create additional custom folder.
Assets
In addition to normal application files I got now custom folder with 2 subfolders: A and B each of them containing assets (css, img) that correspond only to concrete project.
In gulp I've used yargs module which allows to pass parameters. After reading project name from input I can looks inside custom folder to see if there are resources interesting for me (I've just added custom folder into the resources paths).
var customPath = './custom/' + app.name;
exports.paths = {
web: {
//Resources
styles: ['./app/**/*.css', './app/**/*.scss', customPath + '/**/*.css', customPath + '/**/*.scss'],
...
And the call to build task now looks like this: gulp build --name A.
Configuration
One more thing was done for configuration file of AngularJS that contains constants. I've used gulp-ng-config plugin which allows to build AngularJS configuration (constants) file on fly. In my flow, first I check if custom configuration file exists inside custom folder I use it, if no I'm using default one from application.
var getAppScripts = function() {
return $.eventStream.merge(
gulp.src(config.paths.web.scripts)
.pipe($.jshint())
.pipe($.jshint.reporter('jshint-stylish'))
//.pipe($.eslint())
.pipe($.eslint.format()),
getAppConfig())
.pipe($.angularFilesort());
};
var getAppConfig = function() {
var configFile = config.paths.web.custom + "/app.config.yaml";
if (fs.existsSync(configFile)) {
return gulp.src(configFile)
.pipe($.ngConfig(config.app.name, {
parser: 'yml',
createModule: false
}));
}
else {
return gulp.src(config.paths.web.config);
}
}

Using VS 2015/gulp for versioning js files

I'm using VS 2015, ASP.NET 5 (MVC 6) and Gulp to write a SPA with angularjs and supplementary modules. My target framework is dnx451. I've read several best practices which state that the response from Index should have a strict no cache policy set, and all other resources (e.g. js, css, img) should all be heavily cached. In doing so, the browser always downloads the lightweight page and caches the scripts. When publishing, I am trying to have a gulp task which concats/uglifys all my JS files and outputs a single app.min.{version}.js (also for the less -> css file). This gives the benefit of always downloading the latest file version, but keeping them in cache while it is the latest and greatest.
Is there a way to get the Version (from project.json) and the build (from the * portion of project.json) from my gulp task? I am looking for a way to have the file {version} portion of the name match the version/build of the website.
I have seen examples of using process.env in gulp for VS environment variables, but am having trouble putting the pieces together to achieve the desired Version.Build format.
I have tried:
var project = require('./project.json');
gulp.task('js-publish', function(){
project.version; //this give 1.0.0-* (makes sense since its a string)
});
and
gulp.task('js-publish', function(){
process.env.BUILD_VERSION; //which is undefined
});
You want to use the gulp-rename NPM package to rename the file. Add gulp-rename to your package.json file. Here is an example of how it can then be used in your gulpfile.js:
var rename = require("gulp-rename");
// rename via string
gulp.src("./src/main/text/hello.txt")
.pipe(rename("main/text/ciao/goodbye.md"))
.pipe(gulp.dest("./dist")); // ./dist/main/text/ciao/goodbye.md
// rename via function
gulp.src("./src/**/hello.txt")
.pipe(rename(function (path) {
path.dirname += "/ciao";
path.basename += "-goodbye";
path.extname = ".md"
}))
.pipe(gulp.dest("./dist")); // ./dist/main/text/ciao/hello-goodbye.md
// rename via hash
gulp.src("./src/main/text/hello.txt", { base: process.cwd() })
.pipe(rename({
dirname: "main/text/ciao",
basename: "aloha",
prefix: "bonjour-",
suffix: "-hola",
extname: ".md"
}))
.pipe(gulp.dest("./dist")); // ./dist/main/text/ciao/bonjour-aloha-hola.md

Gulp angular templatecache root issue

I'm part of a team developing an AngularJS application and right now I'm working on modifying the Gulp build script. Part of my task is prepopulating the template cache (up till now we have been loading the templates as the routes/directives needed them).
The Gulp task is basically:
var templateCache = require('gulp-angular-templatecache');
gulp.task('cache-templates', function(){
var dest = destinationPath,
src = sourcePath;
return gulp.src(src)
.pipe(plumber())
.pipe(templateCache('templates.js', {root: './templates/'}))
.pipe(gulp.dest(dest));
});
The problem I am getting is that gulp removes the "./" from the root. For instance:
$templateCache.put("templates/foo.html","<div>some html</div>");
in stead of
$templateCache.put("./templates/foo.html","<div>some html</div>");
The module is loaded correctly into app.js and declared as a dependency, and if I do put the "./"'s as a prefix manually, after building, everything works fine. So could you please tell me how to force Gulp to include the "./" prefix in my root?
Note: Every other prefix works fine, it just removes the "./". I would prefer it if I could solve this from within the Gulpfile, without having to modify the templateUrl's in my directives and $routeProvider, because the application is rather large and that would only be asking for trouble. Thanks! :)
What you can do is use gulp-replace and replace 'templates/' with './templates/'.
Old Answer
In the options that you pass to template you can provide a base function
.pipe(templateCache('templates.js', {root: './templates/', base: baseFn}))
you can modify the file-path there
var baseFn = function (file) { return './' + file.relative; }

Load JavaScript and CSS files in folders in AngularJS

I have an AngularJS application and in the future, some developers in other teams will develop modules that will be installed as parts of it. So I defined the folder structure as below.
www/
index.html
app.js
modules/
modulesA/ -- will be copied when module A was installed
moduleA.js
moduleA.css
moduleA.partial.html
modulesB/ -- will be copied when module B was installed
moduleB.js
moduleB.css
moduleB.partial.html
Now I have a problem. When user installed module A, how to let AngularJS (and the application) load JS and CSS under its folder? Is there any library can load JS and CSS by folder so that I can put the code in index.html likes
<script src="/modules/**/*.js"></script>
<link src="/modules/**/*.css"/>
Otherwise, I have to add some placesholders in index.html and change the content when user installed a module, something like
<script src="/app.js"></script>
<!-- $$_JS_$$ -->
<link src="/app.css"/>
<!-- $$_CSS_$$ -->
AngularJS doesn't support what you want, but you could take a look at build tools such as Grunt or Gulp that let you "build" your application for you. In your case, these tools can look for CSS files and concatenate them into one single file. This way your index.html does not have to change if you ever add new modules.
GruntJS: http://gruntjs.com/
GulpJS: http://gulpjs.com/
Personally I use GulpJS, since it seems to be much faster & I found it easier to configure:
Included my configuration file below.
For example, the task "styles" will compile every css file it finds in the folders I specified, concatenate them, and drop them in the distribution folder.
Since there is an initial learning curve on how to use these tools, you can always integrate gulp or grunt at your own pace. For now you could let it build your css files & later expand it by concatenating JS as well and do various other tasks. In my opinion, its worth learning as it saves you so much time & effort.
var gulp = require("gulp");
var concat = require("gulp-concat");
var html2js = require("gulp-ng-html2js");
var sass = require("gulp-sass");
var clean = require("gulp-clean");
var streamqueue = require("streamqueue");
var ngDepOrder = require("gulp-ng-deporder");
var paths = {
"dist": "../server/staffing/static/",
"vendor": ['vendor/underscore/underscore.js',
'vendor/angular/angular.min.js',
'vendor/angular-route/angular-route.min.js',
'vendor/restangular/dist/restangular.min.js',
'vendor/angular-animate/angular-animate.min.js',
'vendor/angular-bootstrap/ui-bootstrap-0.7.0.min.js',
'vendor/angular-bootstrap/ui-bootstrap-tpls-0.7.0.min.js',
'vendor/angular-ui-router/release/angular-ui-router.min.js',
'vendor/angular-bootstrap-colorpicker/js/bootstrap-colorpicker-module.js',
'vendor/momentjs/min/moment.min.js'],
"scripts": ['app/**/*.js'],
"fonts": ['app-data/fonts/*.*'],
"templates": ['app/**/*.html'],
"styles": ['app/**/*.scss','vendor/angular-bootstrap-colorpicker/css/*.css']
}
gulp.task("watch", function () {
gulp.watch('app/**/*.js', ['scripts']);
gulp.watch('app/**/*.html', ['scripts'])
gulp.watch('app/**/*.scss', ['styles']);
})
gulp.task("default", ["clean"], function () {
gulp.start("scripts", "vendor", "styles", "fonts");
})
gulp.task("clean", function () {
return gulp.src(paths.dist, {read: false})
.pipe(clean({force: true}));
})
gulp.task("vendor", function () {
gulp.src(paths.vendor)
.pipe(concat("vendor.js"))
.pipe(gulp.dest(paths.dist + "js/"));
});
gulp.task("scripts", function () {
var stream = streamqueue({objectMode: true});
stream.queue(gulp.src(paths.scripts)
.pipe(ngDepOrder()));
stream.queue(gulp.src(paths.templates)
.pipe(html2js({moduleName: "templates"})));
return stream.done()
.pipe(concat("app.js"))
.pipe(gulp.dest(paths.dist + "js/"))
});
gulp.task("styles", function () {
gulp.src(paths.styles)
.pipe(sass())
.pipe(concat("staffing.css"))
.pipe(gulp.dest(paths.dist + "css/"))
})
gulp.task("fonts", function () {
gulp.src(paths.fonts).
pipe(gulp.dest(paths.dist + "fonts/"))
})
Check out the angular generator for Slush, it does what I think you want using gulp-bower-files and gulp-inject. You specify your app dependencies using bower, and these are collected and injected by gulp using gulp-inject, which then injects in your index.html the proper link/src/style tags that look very much like your own examples above. Modules' JS and CSS is also collected, minimized, concatenated and injected as well. It also compiles partials and injects those into $templateCache.
I have used it to automatically include dependencies from sub-folder modules/views using a project layout similar to yours.
Note that all your vendor dependencies will need to be bower packages that specify their dist files using the 'main' attribute in bower.json. Some packages do not do this properly, but it's easy to fork the package and add them yourself then point bower at your updated repo.

Resources