Using Gulp to create angular $templateCache per module/directory - javascript

So, I'm moving from grunt to gulp (or trying to anyway), and I'm having trouble getting gulp to do what I'm doing in grunt. Specifically the $templateCache stuff.
My angular app is broken up into several components/modules. Each module contains everything it needs to run (controllers, directives, partials, scss, etc.).
Using Grunt, I've been able to boil each module down into 5 files:
module.min.css // all module scss files compiled and concatenated
module.min.js // all module controllers, directives, services, etc. concatenated
module.tpls.min.js // all partials in $templateCache for this module
module.mocks.min.js // all unit test mock objects for this module
module.specs.min.js // all unit test specs for this module
This has worked really well for 2 years now and been a cornerstone of my modular architecture. My only reasons to try out gulp was 1) Curiosity, 2) My grunt file is getting kinda hairy as we add in deployment and environment specific stuff and so far gulp has really slimmed that down.
For the most part, I've figured out how to do all my grunt tasks in gulp, but I'm having trouble figuring out how to generate a template cache file for each module. All the gulp-ng|angular-templates|templatecache plugins take all my partials and create one file. I'd like to take all my files under module/partials/*.html and create a single module.tpls.min.js; and do that for each module.
This was actually a problem with grunt too, but I figured it out with grunt.file.expand().forEach() like this:
grunt.registerTask('prepModules', '...', function(){
// loop through our modules directory and create subtasks
// for each module, modifying tasks that affect modules.
grunt.file.expand("src/js/modules/*").forEach(function (dir) {
// get the module name by looking at the directory we're in
var mName = dir.substr(dir.lastIndexOf('/') + 1);
// add ngtemplate subtasks for each module, turning
// all module partials into $templateCache objects
ngtemplates[mName] = {
module: mName,
src: dir + "/partials/**/*.html",
dest: 'dev/modules/' + mName + '/' + mName + '.tpls.min.js'
};
grunt.config.set('ngtemplates', ngtemplates);
});
});
My current gulp for this same task:
var compileTemplates = gulp.src('./src/js/modules/**/partials/*.html', {base:'.'})
.pipe(ngTemplates())
.pipe(gulp.dest('.'));
I've only really looked at the options, but none of them seemed to do what I wanted. They were all around changing the file name, or the final destination of the file, or a module name, or whatever else; nothing that said anything about doing it for only the directory it happens to be in.
I had thought about using gulp-rename because it worked well for me when doing the CSS compilation:
var compileScss = gulp.src('./src/js/modules/**/scss/*.scss', {base:'.'})
.pipe(sass({includePaths: ['./src/scss']}))
.pipe(rename(function(path){
path.dirname = path.dirname.replace(/scss/,'css');
}))
.pipe(gulp.dest('.'));
However, when I pipe rename() after doing ngTemplates() it only has the path of the final output file (one log entry). When you console.log() path after sass(), it has all the paths of all the files that it found (lots of log entries).
Any ideas? Thanks!

This SO post has the correct answer, but the wasn't coming up in my searches for this specific usage. I was going to vote to close my question, but since someone else might search using my own specific terms (since I did), it seems more appropriate to leave it alone and just redirect to the original question as well as show how I solved my own particular problem.
var fs = require('fs');
var ngTemplates = require('gulp-ng-templates');
var rename = require('gulp-rename');
var modulesDir = './src/js/modules/';
var getModules = function(dir){
return fs.readdirSync(dir)
.filter(function(file){
return fs.statSync(path.join(dir, file)).isDirectory();
});
};
gulp.task('default', function(){
var modules = getModules(modulesDir);
var moduleTasks = modules.map(function(folder){
// get all partials for this module
// parse into $templateCache file
// rename to be /dev/modules/_____/______.tpls.min.js
return gulp.src(modulesDir + folder + '/partials/*.html', {basedir:'.'})
.pipe(ngTemplates({module:folder}))
.pipe(rename(function(path){
path.dirname = './dev/apps/' + folder + '/';
path.basename = folder + '.tpls.min';
}))
.pipe(gulp.dest('.'));
});
});
It's essentially like the tasks per folder recipe but with a change to use gulp-ng-templates. I'll probably be using this same pattern for my SCSS and JS now that I'm more aware of it.
Seems like the gulp equivalent of grunt.file.expand().forEach().

Whenever I deal with scss/sass for gulp tasks, I will only put one scss file as the source parameter. This parameter file is composed of a list of imports. This way you don't need to rely on gulp to concat the scss file contents for you.
//in gulpfile
gulp.src('./src/js/modules/**/scss/main.scss', {base:'.'})
//in main.scss
#import 'a', 'b', 'c';
a, b, and c would represent your other scss files.

Related

I'm using Gulp and failing to produce the final development script for production.

So I'm having a slight problem with producing production ready scripts for my project. I'm using gulp to concatenate and minify my css and js, and while the css is working fine the gulp js function isn't generating my final file. Please refer to my code below:
gulp.task('js', function() {
return gulp.src([source + 'js/app/**/*.js'])
.pipe(concat('development.js'))
.pipe(gulp.dest(source + 'js'))
.pipe(rename({
basename: 'production',
suffix: '-min',
}))
.pipe(uglify())
.pipe(gulp.dest(source + 'js/'))
.pipe(notify({ message: 'Scripts task complete', onLast: true }));
});
If anyone has encountered a similar problem or has any tips it would be much appreciated :)
There is nothing wrong with your gulpfile. I tested it and it works perfectly.
The only thing I can guess is that your source is not set correctly. Did you forget the trailing slash '/' ?
I would suggest 2 things to figure it out. Include node path library to check where source is actually pointing to like this:
var path = require('path');
// in gulp task ...
path.resolve(path.resolve(source + 'js/app'));
Make sure it points where you think it does.
Secondly, you could use gulp-debug to establish that any files are found:
npm install gulp-debug
Then
var debug = require('gulp-debug');
// in gulp task ...
return gulp.src([source + 'js/app/**/*.js'])
.pipe(concat('development.js'))
.pipe(debug())
.pipe(gulp.dest(source + 'js'))
.pipe(debug())
// etc.
Good luck!
Based on additional infomation in the comments I realise you are generating JS files in a separate process ...
gulp is asynchronous by default. What this boils down to is that all functions try to run at the same time - if you want a specific order it must be by design. This is great because it's very fast but can be a headache to work with.
Problem
Here is what's basically happening:
// SOME TASK THAT SHOULD BE RUN FIRST
gulp.task('copy-vendor-files-to-tempfolder', function (done) {
// copy files to vendor folder
done()
})
// SOME TASKS THAT DEPEND ON FIRST TASK
gulp.task('complile-styles', function () { /* independent task */ })
gulp.task('concat-vendor-files', function () { /* concat files in vendor folder. depends on vendor files existing */ })
// GENERAL TASK WHICH STARTS OTHERS
gulp.task('ready', ['copy-vendor-files-to-tempfolder', 'compile-styles', 'concat-vendor-files])
When you try to run:
$ gulp ready
GULP TASK WILL FAIL! Folder is being created at the same time!!
NOWHERE TO COPY FILES!
Solution
There are many solutions but the following module has come in handy for me again and again:
npm install run-sequence
Then in your gulpfile.js:
var runSequence = require('run-sequence')
gulp.task('ready', function (done) {
runSequence(
'create-folders', // do this first,
[
'copy-css-files',
'copy-html-files'
], // do these AFTER but in parallel
done // callback when ready
)
})
This will guarantee the folder exists when you try to run the other functions.
In your specific case, you should make sure the task that concatenates the JS files is run after the task that copies them out of vendor.
Note: I'm leaving other answer because it contains useful help for debugging similar issues.
HTH!

gulp run-sequence: tasks apparently running when prev task is not yet finished

we're using gulp and run-sequence in order to build our application having the following sequence in place:
gulp.task('build', function(cb) {
runSequence(
'lint',
'clean',
'compile-templates',
'copy-dependencies',
'compile-styles',
'revall',
'cdnize',
'overlays',
cb
);
});
Here is what the single tasks do and use (pasting the specific code would be quite long so I'll try to give the picture briefly describing the tasks):
'compile-templates': using jade (~1.1.5) + gulp-minify-html (^0.1.3), it picks up all our jade files and compiles them to html files
'copy-dependencies': using gulp-spa (0.2.2) + gulp-uglify (^0.2.1) + gulp-concat (^2.1.7) + gulp-ngmin (^0.1.2), it aggregate all our javascript files and angular templates building and minifying our vendor.js and app.js files together with the small/index.html and large/index.html files.
'compile-styles': using gulp-compass (~1.1.3) + gulp-minify-css (~0.3.1), it compiles our scss files into our desktop.css and mobile.css files
'revall': using gulp-rev-all (~0.3.0), takes care of revving all the prev generated files
'cdnize': using gulp-cdnizer (^0.2.6), it substitutes paths for our js and css files updating them with our CDN urls inside the small/index.html and large/index.html files
'overlays': using gulp-replace (^0.3.0), it will find the previously generated and revved css files and fill up their actual name inside our small/index.html and large/index.html files replacing some other placeholders as well.
here is our 'overlays' task code:
gulp.task('overlays', function() {
var styles = fs.readdirSync('./dist/styles');
var desktopCss = '';
var mobileCss = '';
for (var index in styles) {
var style = styles[index];
if (style.indexOf('desktop' + 'rev') > -1) {
desktopCss = style.replace('.css', '');
}
if (style.indexOf('mobile' + 'rev') > -1) {
mobileCss = style.replace('.css', '');
}
}
return gulp.src('./dist/large/index.html')
.pipe(plugins.replace('DIST_CSS', desktopCss))
.pipe(plugins.if(!isDev, gulp.dest('./dist/large')))
.pipe(gulp.src('./dist/small/index.html'))
.pipe(plugins.replace('DIST_CSS', mobileCss))
.pipe(plugins.if(!isDev, gulp.dest('./dist/small')));
});
It's a lot of stuff to do but nothing really complicated.
Everything usually runs nicely BUT for some reason, some times, we're experiencing an odd problem we cannot understand:
The 2 index.html files ends up without the needed final substitution (those happening in the 'overlays' task)
It seems almost like some of the prev tasks in the run-sequence block are actually still running when the next one is run (namely 'copy-dependencies' or 'cdnize')
We triple checked all the tasks and all of them are returning the gulp pipeline as expected by run-sequence.
From the gulp output it doesn't look like anything is running in parallel but we still have those placeholders instead of our values.
I guess we're missing something about gulp or the plugins we're using but we cannot really understand what and why.

how to output multiple bundles with browserify and gulp

I have browserify bundling up files and it's working great. But what if I need to generate multiple bundles?
I would like to end up with dist/appBundle.js and dist/publicBundle.js
gulp.task("js", function(){
return browserify([
"./js/app.js",
"./js/public.js"
])
.bundle()
.pipe(source("bundle.js"))
.pipe(gulp.dest("./dist"));
});
Obviously this isn't going to work since I am only specifying one output (bundle.js). I can accomplish this by repeating the above statement like so (but it doesn't feel right, because of the repetition):
gulp.task("js", function(){
browserify([
"./js/app.js"
])
.bundle()
.pipe(source("appBundle.js"))
.pipe(gulp.dest("./dist"));
browserify([
"./js/public.js"
])
.bundle()
.pipe(source("publicBundle.js"))
.pipe(gulp.dest("./dist"));
});
Is there a better way to tackle this? Thanks!
I don't have a good environment to test this in right now, but my guess is that it would look something like:
gulp.task("js", function(){
var destDir = "./dist";
return browserify([
"./js/app.js",
"./js/public.js"
])
.bundle()
.pipe(source("appBundle.js"))
.pipe(gulp.dest(destDir))
.pipe(rename("publicBundle.js"))
.pipe(gulp.dest(destDir));
});
EDIT: I just realized I mis-read the question, there should be two separate bundles coming from two separate .js files. In light of that, the best alternative I can think of looks like:
gulp.task("js", function(){
var destDir = "./dist";
var bundleThis = function(srcArray) {
_.each(srcArray, function(source) {
var bundle = browserify(["./js/" + source + ".js"]).bundle();
bundle.pipe(source(source + "Bundle.js"))
.pipe(gulp.dest(destDir));
});
};
bundleThis(["app", "public"]);
});
gulp.task("js", function (done) {
[
"app",
"public",
].forEach(function (entry, i, entries) {
// Count remaining bundling operations to track
// when to call done(). Could alternatively use
// merge-stream and return its output.
entries.remaining = entries.remaining || entries.length;
browserify('./js/' + entry + '.js')
.bundle()
// If you need to use gulp plugins after bundling then you can
// pipe to vinyl-source-stream then gulp.dest() here instead
.pipe(
require('fs').createWriteStream('./dist/' + entry + 'Bundle.js')
.on('finish', function () {
if (! --entries.remaining) done();
})
);
});
});
This is similar to #urban_racoons answer, but with some improvements:
That answer will fail as soon as you want the task to be a dependency of another task in gulp 3, or part of a series in gulp 4. This answer uses a callback to signal task completion.
The JS can be simpler and doesn't require underscore.
This answer is based on the premise of having a known list of entry files for each bundle, as opposed to, say, needing to glob a list of entry files.
Multiple bundles with shared dependencies
I recently added support for multiple bundles with shared dependencies to https://github.com/greypants/gulp-starter
Here's the array of browserify config objects I pass to my browserify task. At the end of that task, I iterate over each config, browserifying all the things.
config.bundleConfigs.forEach(browserifyThis);
browserifyThis takes a bundleConfig object, and runs browserify (with watchify if dev mode).
This is the bit that sorts out shared dependencies:
// Sort out shared dependencies.
// b.require exposes modules externally
if(bundleConfig.require) b.require(bundleConfig.require)
// b.external excludes modules from the bundle, and expects
// they'll be available externally
if(bundleConfig.external) b.external(bundleConfig.external)
This browserify task also properly reports when all bundles are finished (the above example isn't returning streams or firing the task's callback), and uses watchify when in devMode for super fast recompiles.
Brian FitzGerald's last comment is spot on. Remember that it's just JavaScript!

Concat scripts in order with Gulp

Say, for example, you are building a project on Backbone or whatever and you need to load scripts in a certain order, e.g. underscore.js needs to be loaded before backbone.js.
How do I get it to concat the scripts so that they’re in order?
// JS concat, strip debugging and minify
gulp.task('scripts', function() {
gulp.src(['./source/js/*.js', './source/js/**/*.js'])
.pipe(concat('script.js'))
.pipe(stripDebug())
.pipe(uglify())
.pipe(gulp.dest('./build/js/'));
});
I have the right order of scripts in my source/index.html, but since files are organized by alphabetic order, gulp will concat underscore.js after backbone.js, and the order of the scripts in my source/index.html does not matter, it looks at the files in the directory.
So does anyone have an idea on this?
Best idea I have is to rename the vendor scripts with 1, 2, 3 to give them the proper order, but I am not sure if I like this.
As I learned more I found Browserify is a great solution, it can be a pain at first but it’s great.
I had a similar problem recently with Grunt when building my AngularJS app. Here's a question I posted.
What I ended up doing is to explicitly list the files in order in the grunt config. The config file will then look like this:
[
'/path/to/app.js',
'/path/to/mymodule/mymodule.js',
'/path/to/mymodule/mymodule/*.js'
]
Grunt is able to figure out which files are duplicates and not include them. The same technique will work with Gulp as well.
Another thing that helps if you need some files to come after a blob of files, is to exclude specific files from your glob, like so:
[
'/src/**/!(foobar)*.js', // all files that end in .js EXCEPT foobar*.js
'/src/js/foobar.js',
]
You can combine this with specifying files that need to come first as explained in Chad Johnson's answer.
I have used the gulp-order plugin but it is not always successful as you can see by my stack overflow post gulp-order node module with merged streams. When browsing through the Gulp docs I came across the streamque module which has worked quite well for specifying order of in my case concatenation. https://github.com/gulpjs/gulp/blob/master/docs/recipes/using-multiple-sources-in-one-task.md
Example of how I used it is below
var gulp = require('gulp');
var concat = require('gulp-concat');
var handleErrors = require('../util/handleErrors');
var streamqueue = require('streamqueue');
gulp.task('scripts', function() {
return streamqueue({ objectMode: true },
gulp.src('./public/angular/config/*.js'),
gulp.src('./public/angular/services/**/*.js'),
gulp.src('./public/angular/modules/**/*.js'),
gulp.src('./public/angular/primitives/**/*.js'),
gulp.src('./public/js/**/*.js')
)
.pipe(concat('app.js'))
.pipe(gulp.dest('./public/build/js'))
.on('error', handleErrors);
});
With gulp-useref you can concatenate every script declared in your index file, in the order in which you declare it.
https://www.npmjs.com/package/gulp-useref
var $ = require('gulp-load-plugins')();
gulp.task('jsbuild', function () {
var assets = $.useref.assets({searchPath: '{.tmp,app}'});
return gulp.src('app/**/*.html')
.pipe(assets)
.pipe($.if('*.js', $.uglify({preserveComments: 'some'})))
.pipe(gulp.dest('dist'))
.pipe($.size({title: 'html'}));
});
And in the HTML you have to declare the name of the build file you want to generate, like this:
<!-- build:js js/main.min.js -->
<script src="js/vendor/vendor.js"></script>
<script src="js/modules/test.js"></script>
<script src="js/main.js"></script>
In your build directory you will have the reference to main.min.js which will contain vendor.js, test.js, and main.js
The sort-stream may also be used to ensure specific order of files with gulp.src. Sample code that puts the backbone.js always as the last file to process:
var gulp = require('gulp');
var sort = require('sort-stream');
gulp.task('scripts', function() {
gulp.src(['./source/js/*.js', './source/js/**/*.js'])
.pipe(sort(function(a, b){
aScore = a.path.match(/backbone.js$/) ? 1 : 0;
bScore = b.path.match(/backbone.js$/) ? 1 : 0;
return aScore - bScore;
}))
.pipe(concat('script.js'))
.pipe(stripDebug())
.pipe(uglify())
.pipe(gulp.dest('./build/js/'));
});
I just add numbers to the beginning of file name:
0_normalize.scss
1_tikitaka.scss
main.scss
It works in gulp without any problems.
I have my scripts organized in different folders for each package I pull in from bower, plus my own script for my app. Since you are going to list the order of these scripts somewhere, why not just list them in your gulp file? For new developers on your project, it's nice that all your script end-points are listed here. You can do this with gulp-add-src:
gulpfile.js
var gulp = require('gulp'),
less = require('gulp-less'),
minifyCSS = require('gulp-minify-css'),
uglify = require('gulp-uglify'),
concat = require('gulp-concat'),
addsrc = require('gulp-add-src'),
sourcemaps = require('gulp-sourcemaps');
// CSS & Less
gulp.task('css', function(){
gulp.src('less/all.less')
.pipe(sourcemaps.init())
.pipe(less())
.pipe(minifyCSS())
.pipe(sourcemaps.write('source-maps'))
.pipe(gulp.dest('public/css'));
});
// JS
gulp.task('js', function() {
gulp.src('resources/assets/bower/jquery/dist/jquery.js')
.pipe(addsrc.append('resources/assets/bower/bootstrap/dist/js/bootstrap.js'))
.pipe(addsrc.append('resources/assets/bower/blahblah/dist/js/blah.js'))
.pipe(addsrc.append('resources/assets/js/my-script.js'))
.pipe(sourcemaps.init())
.pipe(concat('all.js'))
.pipe(uglify())
.pipe(sourcemaps.write('source-maps'))
.pipe(gulp.dest('public/js'));
});
gulp.task('default',['css','js']);
Note: jQuery and Bootstrap added for demonstration purposes of order. Probably better to use CDNs for those since they are so widely used and browsers could have them cached from other sites already.
Try stream-series. It works like merge-stream/event-stream.merge() except that instead of interleaving, it appends to the end. It doesn't require you to specify the object mode like streamqueue, so your code comes out cleaner.
var series = require('stream-series');
gulp.task('minifyInOrder', function() {
return series(gulp.src('vendor/*'),gulp.src('extra'),gulp.src('house/*'))
.pipe(concat('a.js'))
.pipe(uglify())
.pipe(gulp.dest('dest'))
});
merge2 looks like the only working and maintained ordered stream merging tool at the moment.
Update 2020
The APIs are always changing, some libraries become unusable or contain vulnerabilities, or their dependencies contain vulnerabilities, that are not fixed for years. For text files manipulations you'd better use custom NodeJS scripts and popular libraries like globby and fs-extra along with other libraries without Gulp, Grunt, etc wrappers.
import globby from 'globby';
import fs from 'fs-extra';
async function bundleScripts() {
const rootPaths = await globby('./source/js/*.js');
const otherPaths = (await globby('./source/**/*.js'))
.filter(f => !rootFiles.includes(f));
const paths = rootPaths.concat(otherPaths);
const files = Promise.all(
paths.map(
// Returns a Promise
path => fs.readFile(path, {encoding: 'utf8'})
)
);
let bundle = files.join('\n');
bundle = uglify(bundle);
bundle = whatever(bundle);
bundle = bundle.replace(/\/\*.*?\*\//g, '');
await fs.outputFile('./build/js/script.js', bundle, {encoding: 'utf8'});
}
bundleScripts.then(() => console.log('done');
An alternative method is to use a Gulp plugin created specifically for this problem. https://www.npmjs.com/package/gulp-ng-module-sort
It allows you to sort your scripts by adding in a .pipe(ngModuleSort()) as such:
var ngModuleSort = require('gulp-ng-module-sort');
var concat = require('gulp-concat');
gulp.task('angular-scripts', function() {
return gulp.src('./src/app/**/*.js')
.pipe(ngModuleSort())
.pipe(concat('angularAppScripts.js))
.pipe(gulp.dest('./dist/));
});
Assuming a directory convention of:
|——— src/
| |——— app/
| |——— module1/
| |——— sub-module1/
| |——— sub-module1.js
| |——— module1.js
| |——— module2/
| |——— sub-module2/
| |——— sub-module2.js
| |——— sub-module3/
| |——— sub-module3.js
| |——— module2.js
| |——— app.js
Hope this helps!
For me I had natualSort() and angularFileSort() in pipe which was reordering the files. I removed it and now it works fine for me
$.inject( // app/**/*.js files
gulp.src(paths.jsFiles)
.pipe($.plumber()), // use plumber so watch can start despite js errors
//.pipe($.naturalSort())
//.pipe($.angularFilesort()),
{relative: true}))
I just use gulp-angular-filesort
function concatOrder() {
return gulp.src('./build/src/app/**/*.js')
.pipe(sort())
.pipe(plug.concat('concat.js'))
.pipe(gulp.dest('./output/'));
}
I'm in a module environnement where all are core-dependents using gulp.
So, the core module needs to be appended before the others.
What I did:
Move all the scripts to an src folder
Just gulp-rename your core directory to _core
gulp is keeping the order of your gulp.src, my concat src looks like this:
concat: ['./client/src/js/*.js', './client/src/js/**/*.js', './client/src/js/**/**/*.js']
It'll obviously take the _ as the first directory from the list (natural sort?).
Note (angularjs):
I then use gulp-angular-extender to dynamically add the modules to the core module.
Compiled it looks like this:
angular.module('Core', ["ui.router","mm.foundation",(...),"Admin","Products"])
Where Admin and Products are two modules.
if you would like to order third party libraries dependencies, try wiredep. This package basically checks each package dependency in bower.json then wire them up for you.
I tried several solutions from this page, but none worked. I had a series of numbered files which I simply wanted be ordered by alphabetical foldername so when piped to concat() they'd be in the same order. That is, preserve the order of the globbing input. Easy, right?
Here's my specific proof-of-concept code (print is just to see the order printed to the cli):
var order = require('gulp-order');
var gulp = require('gulp');
var print = require('gulp-print').default;
var options = {};
options.rootPath = {
inputDir: process.env.INIT_CWD + '/Draft',
inputGlob: '/**/*.md',
};
gulp.task('default', function(){
gulp.src(options.rootPath.inputDir + options.rootPath.inputGlob, {base: '.'})
.pipe(order([options.rootPath.inputDir + options.rootPath.inputGlob]))
.pipe(print());
});
The reason for the madness of gulp.src? I determined that gulp.src was running async when I was able to use a sleep() function (using a .map with sleeptime incremented by index) to order the stream output properly.
The upshot of the async of src mean dirs with more files in it came after dirs with fewer files, because they took longer to process.
In my gulp setup, I'm specifying the vendor files first and then specifying the (more general) everything, second. And it successfully puts the vendor js before the other custom stuff.
gulp.src([
// vendor folder first
path.join(folder, '/vendor/**/*.js'),
// custom js after vendor
path.join(folder, '/**/*.js')
])
Apparently you can pass in the "nosort" option to gulp.src gulp.src.

requireJS an entire folder

Is it possible to "require" an entire folder using requireJS.
For example, I have a behaviors folder with a ton of behavior js files. I'd really like to be able to simply use require(['behaviors/*'], function() {...}); to load everything in that folder rather than having to keep that list up to date. Once compressed and optimized I'd have all those files lump together, but for development it's easier to work with them individually.
javascript in browser has no filesystem access and so it can't scan a directory for files. If you are building your app in a scripting language like php or ruby you could write a script that scans the directory and adds the file names to the require() call.
I don't know if I can recommend this approach anymore. I think the more explicit way to do this is by manually "requiring"/"exporting" the functionality you need. The exception I think is if you have a "namespace" of files that you want exported see below "Babel and ES6 Module Import Declarations (export-namespace-from) or see below "Babel and ES6 Module Import Declarations.
These solutions also assume that you have a meaningful file structure - where file names become part of that "require" * definition.
However, if you still need to do this there are a few existing tools and methods that might provide the behavior that you're looking for.
Possible Solutions
Babel and ES6 Module Import Declarations (plugin-export-namespace-from)
Have a setup that is ES6 compliant.
You need to update your .babelrc file to include babel-plugin-proposal-export-namespace-from.
Use export namespace plugin by writing syntax like the following:
common/index.js
export * from './common/a'; // export const a = false;
export * from './common/b'; // export const b = true;
main.js
import { a, b } from './common';
console.log(a); // false
console.log(b); // true
Babel and ES6 Module Import Declarations (plugin-wildcard)
Have a setup that is ES6 compliant.
You need to update your .babelrc file to include babel-plugin-wildcard.
Use wildcard namespace plugin by writing syntax like the following:
main.js
import { a, b } from './common/*'; // imports './common/a.js' and './common/b.js'
console.log(a); // false
console.log(b); // true
RequireJS (Now Outdated)
Download and install require-wild npm install require-wild
Configure the declaration as follows
grunt.initConfig({
requireWild: {
app: {
// Input files to look for wildcards (require|define)
src: ["./**/*.js"],
// Output file contains generated namespace modules
dest: "./namespaces.js",
// Load your require config file used to find baseUrl - optional
options: { requireConfigFile: "./main.js" }
}
}
});
grunt.loadNpmTasks("require-wild");
grunt.registerTask('default', ['requireWild']);
Then run the grunt task. Your file will be generated. Modify your setup to load namespaces.js
require(['namespaces'], function () { ... });
This now allows modules under src to use dependencies glob pattern matching.
require(['behaviors/**/*'], function (behaviors) { }
I know this is old, but I'd like to share my solution:
For this solution you need JQuery
1) Create a bash script that will list all the js files in
"MyDirectory/", and save it to "directoryContents.txt":
#!/bin/bash
#Find all the files in that directory...
for file in $( find MyDirectory/ -type f -name "*.js" )
do
fileClean=${file%.js} #Must remove .js from the end!
echo -n "$fileClean " >> MyDirectory/directoryContents.txt
done
File will look like this:
MyDirectory/FirstJavascriptFile MyDirectory/SecondJavascriptFile
MyDirectory/ThirdJavascriptFile
Problem with my script! Puts an extra " " at the end, that messes things up! Make sure to remove the excess space at the end of directoryContents.txt
2) Then in your Client side JS code:
do a "GET" request to retrieve the text file
For each entry (split by the space), 'require' that file:
.
$.get( "MyDirectory/directoryContents.txt", {}, function( data ) {
var allJsFilesInFolder = data.split(" ");
for(var a=0; a<allJsFilesInFolder.length; a++)
{
require([allJsFilesInFolder[a]], function(jsConfig)
{
//Done loading this one file
});
}
}, "text");
I was having a problem with this code not finishing before my other code, so Here's my extended answer:
define([''], function() {
return {
createTestMenu: function()
{
this.loadAllJSFiles(function(){
//Here ALL those files you need are loaded!
});
},
loadAllJSFiles: function(callback)
{
$.get( "MyDirectory/directoryContents.txt", {}, function( data ) {
var allJsFilesInFolder = data.split(" ");
var currentFileNum = 0;
for(var a=0; a<allJsFilesInFolder.length; a++)
{
require([allJsFilesInFolder[a]], function(jsConfig)
{
currentFileNum++;
//If it's the last file that needs to be loaded, run the callback.
if (currentFileNum==allJsFilesInFolder.length)
{
console.log("Done loading all configuration files.");
if (typeof callback != "undefined"){callback();}
}
});
}
}, "text");
}
}
});
What I ended up doing was everytime my Node server boots, it will run the bash script, populating directoryContents.txt. Then My client side just reads directoryContents.txt for the list of files, and requires each in that list.
Hope this helps!
There isn't really a way to do this conceptually on the fly (that I know of).
There's a few work arounds though:
Use grunt and concat and then just require that behemoth...I know, kinda sucky.
What I think is a better solution... use a require hierarchy like so:
require('/js/controllers/init', function(ctrls){
ctrls(app, globals);
});
// /js/controllers/init.js
define('js/controllers/index', 'js/controllers/posts', function(index, posts){
return function protagonist(app, globals){
var indexModule = index(app, globals);
var indexModule = posts(app, globals);
return app || someModule;
};
});
// /js/controllers/index.js
define('js/controllers/index', 'js/controllers/posts', function(index, posts){
return function protagonist(app, globals){
function method1(){}
function method2(){}
return {
m1: method1,
m2: method2
};
};
});
Note that "protagonist" function. That allows you to initialize modules before their use, so now you can pass in a 'sandbox' -- in this case app and globals.
Realistically, you wouldn't have /js/controllers/index.js... It should probably be something like /js/controllers/index/main.js or /js/controllers/index/init.js so that there is a directory adjacent to (sibling of) /js/controllers/init.js called "index". This will make your modules scalable to a given interface -- you can simply swap modules out and keep your interface the same.
Hope this helps! Happy coding!
I wrote a library to solve this problem. Eventually someone else came along and improved my library, here it is:
https://github.com/smartprocure/directory-metagen
You can use my lib with Gulp or whatever - it generates metadata for your project and RequireJS can use that metadata to require the desired files from the filesystem.
Using this lib will produce a RequireJS module that looks something like this:
define(
[
"text!app/templates/dashboardTemplate.ejs",
"text!app/templates/fluxCartTemplate.ejs",
"text!app/templates/footerTemplate.ejs",
"text!app/templates/getAllTemplate.ejs",
"text!app/templates/headerTemplate.ejs",
"text!app/templates/homeTemplate.ejs",
"text!app/templates/indexTemplate.ejs",
"text!app/templates/jobsTemplate.ejs",
"text!app/templates/loginTemplate.ejs",
"text!app/templates/overviewTemplate.ejs",
"text!app/templates/pictureTemplate.ejs",
"text!app/templates/portalTemplate.ejs",
"text!app/templates/registeredUsersTemplate.ejs",
"text!app/templates/userProfileTemplate.ejs"
],
function(){
return {
"templates/dashboardTemplate.ejs": arguments[0],
"templates/fluxCartTemplate.ejs": arguments[1],
"templates/footerTemplate.ejs": arguments[2],
"templates/getAllTemplate.ejs": arguments[3],
"templates/headerTemplate.ejs": arguments[4],
"templates/homeTemplate.ejs": arguments[5],
"templates/indexTemplate.ejs": arguments[6],
"templates/jobsTemplate.ejs": arguments[7],
"templates/loginTemplate.ejs": arguments[8],
"templates/overviewTemplate.ejs": arguments[9],
"templates/pictureTemplate.ejs": arguments[10],
"templates/portalTemplate.ejs": arguments[11],
"templates/registeredUsersTemplate.ejs": arguments[12],
"templates/userProfileTemplate.ejs": arguments[13]
}
});
You can then require modules in your front-end like so:
var footerView = require("app/js/jsx/standardViews/footerView");
however, as you can see this is too verbose, so the magic way is like so:
name the dependency above as allViews!
now you can do:
var allViews = require('allViews');
var footerView = allViews['standardViews/footerView'];
There are two advantages to requiring directories whole:
(1) in production, with the r.js optimizer, you can point to one dependency (module A) and it can then easily trace all of A's dependencies that represent a entire directory
(2) in development, you can require whole directories up front and then use synchronous syntax to require dependencies because you know they have already been loaded
enjoy "RequireJS-Metagen"
https://github.com/smartprocure/directory-metagen
https://www.npmjs.com/package/requirejs-metagen
https://github.com/ORESoftware/requirejs-metagen

Categories

Resources