"Embed" a global variable in javascript file using grunt - javascript

I have a .js file in my project with code like that:
var API_ENDPOINT = 'http://example.com:8000';
var api = new RemoteApi(API_ENDPOINT);
where API_ENDPOINT changes among dev/prod environments
It's not a js application, mostly a classic server-side app (Django) with some client-side enhacements.
I started using Grunt for managing client-side dependencies and thought, it'd be a good idea to specify API_ENDPOINT in Grunt config and somehow "embed" it into .js file.
But I can't find a way to mangle files with Grunt.
The resulting .js file will be run in browser envorenment, so I need my API_ENDPOINT variable embedded in source.js file or creating a separate .js file like
var API_ENDPOINT = '...';
which I will include before script.js
(Also, I'd like to "embed" this variable into my django's settings.py)

for the clientside js i would extract all configs into a config.json file, and use grunt-replace for injection to your code.
the folder structure could look like this:
- Gruntfile
- config.json
- client/
- src/
- script.js
- dist/
config.json
{
"API_ENDPOINT": "http://example.com:8000"
}
src/script.js
var API_ENDPOINT = '##API_ENDPOINT'; // everything starting with ## will be replaced by grunt-replace by default
var api = new RemoteApi(API_ENDPOINT);
Gruntfile
grunt.initConfig({
replace: {
dist: {
options: {
patterns: [{
json: require('config.json')
}]
},
files: [
{expand: true, flatten: true, src: ['./client/src/*.js'], dest: './client/dist/'}
]
}
}
});
some details:
your final clientsidecode will reside in client/dist
requiring a json-file will automatically parse it
of course you can do it with yaml/cson (see grunt-replace section)
dont know on how to parse a json-config in python, but it shouldn't be to difficult...

Related

handlebars:compile - Cannot read property 'filter' of undefined use

I've been searching around but there seemed to be no situation similar to mine so thought I'd post to ask. I want to run the handlebars task in Gruntfile.js with grunt handlebars to compile a templates.js in my source folder (www) but it doesn't fire up with this error shown:
Warning: Cannot read property 'filter' of undefined Use
Here's my script for the handlebars task in grunt file:
// Create the tasks
grunt.initConfig({
config: config,
handlebars: {
// Compiles the handlebar templates into templates.js
compile: {
options: {
amd: true,
processName: function (filepath) {
var pieces = filepath.split('/');
return pieces[pieces.length - 1].split('.')[0];
}
},
// Specify location of handlebar templates
www: ['<%= config.www %>/html/{,*/}*.handlebars'],
dest: '<%= config.www %>/js/templates.js'
}
}
});
Here's opening script of grunt file and the config object, before grunt.initConfig :
module.exports = (function () {
'use strict';
return function (grunt) {
require('load-grunt-tasks')(grunt); // Several tasks to run using grunt-contrib-xx plugins
// Config object
var config = {
www: 'www', // all source files in one directory
};
.. // grunt.initConfig
};
});
Couldn't figure out what goes wrong here since I don't even define a property/term filter and that's the only error received. Any thoughts would be appreciated.
So I will write what I did to resolve this which might be of use to you.
I simply changed the www property specifically to src, as referred to in the first code block, for the handlebars task in grunt file to fire up. Existing handlebars which are markups are sourced a.k.a. src and to compile the templates.js in your specified destination.
Note: handlebars file itself should end with unspecified file type (.handlebars). Depending on your own set up, noting this will save you lots of time afterwards.
Now run below and you shall see the templates.js found in the dest folder.
grunt handlebars

Browserify dynamic separate bundles

My app loads an object of messages in a given language into the application. My structure is like so:
/lang
/en.js (100 kb file)
/ru.js (100 kb file)
/... many more
app.js (this is `MyApp` as below)
The language files are very big so I would like to create separate bundles and you then only include the files you need <script src="lang/en.js"></script>. The language can also be 'switched' within the application at any time.
How would I tell browserify to build the main app and separate bundles for all the language files, and still allow MyApp to require those language files?
function MyApp(lang) {
this.messages = {};
this.switchLang(lang);
};
MyApp.prototype.loadLang = function(lang) {
this.messages = require('./lang/' + lang + '.js');
};
MyApp.prototype.switchLang = function(lang) {
this.lang = lang;
this.loadLang(lang);
};
MyApp.prototype.sayHello = function() {
alert(this.messages.HELLO);
};
module.exports = MyApp;
You can separate all languages from your main app by using -r (require) and -x (external) in your browserify command.
Bundle languages together to one file, could look like this:
browserify -r ./lang/en.js -r ./lang/ru.js > languages.js
RECOMMENDED: You can create a separate bundle for each language file with the above command. Just use -r once.
Then include the new file (languages.js) in your html page before MyApp.js. Then you have to ignore them while building MyApp.js.
browserify --ignore-missing -x ./lang/en.js -x ./lang/ru.js -d app.js > MyApp.js
You are still allowed to require those languages.
NOTE: If you have a separate bundle for each language (see RECOMMENDED), you are only allowed to require the included ones in your main app.
There is no browserify-way to do that automatically for each file in lang/.
I recommend you to write a *.cmd (batch) file that executes the above commands for every language file in lang/. So you can still include your favored language.
EDIT: use --ignore-missing or --im when bundleing MyApp.js. So you can require all languages and when they are missing they are still undefined.

How to get the r.js build script working

I've read through the documentation and the example app.build.js file but just can't get my js files to concatenate and minify into one single file. I think I'm just not understanding exactly what settings I need in the build script and was hoping for some help.
My app is set up like this:
src >
js >
build.js
r.js
config.js
app >
main.js
lib >
module1.js
module2.js
module3.js
vendor >
require.js
jquery.js
jquery.validation.js
build >
// Where concat and minified file would go
config.js looks like this:
requirejs.config({
"baseUrl" : "src/js/lib", // Used because when setting dependencies in modules, this is used
"paths" : {
"app" : "../app",
"jquery" : [
"https://code.jquery.com/jquery-1.11.1.min",
"../vendor/jquery"
],
"validate" : "../vendor/jquery.validate.min"
},
"shim" : {
// Allow plugins with dependencies to load asynchronously
validate : ["jquery"]
}
});
// Load the main app module to start the app
requirejs(["app/main"]);
main.js looks like this:
require(["module1", "module2", "module3"], function(Module1, Module2, Module3) {
return [
Module1.init(),
Module2.init(),
Module3.init(),
Module4.init()
];
});
And then the build.js is where I'm lost. I thought I should load a mainConfigFile because I'm using the shim, but I'm not sure. If I do load that config file, it uses the baseUrl from that config file. I'm not sure what name: is supposed to refer to exactly and whether I'm missing some necessary configuration options.
({
//baseUrl: ".",
paths: {
jquery: "empty:",
//main: "../app/main",
//app: "app"
},
name: "app/main",
out: "../build/main.js",
//mainConfigFile: "config"
})
If I run that build file as it is (with those lines commented out) I get:
Error: ENOENT, no such file or directory
'/Users/davidpaul/Sites/require/src/js/module1.js' In module tree:
app/main
I'm not really sure what's being referred to when it says 'module tree'. I keep making changes to paths in the build file but not making progress so hoping for someone to explain this a bit to me.
The builder parses all paths relative to the build file location (unless changed via the baseUrl property). If you look relative to src/js/build.js, your main.js is in ./app/ and module1/2/3.js are in ./lib/. All paths inside modules have to be specified relatively to the common root, so to make your example work it's enough to change the signature of main.js to:
require(["lib/module1", "lib/module2", "lib/module3"], function(M1, M2, M3) {
// (...)
})
Note that config.js doesn't take part in the build process, you may need to change it as well to make your application work both "raw" and optimized.

Compiling dynamically required modules with Browserify

I am using Browserify to compile a large Node.js application into a single file (using options --bare and --ignore-missing [to avoid troubles with lib-cov in Express]). I have some code to dynamically load modules based on what is available in a directory:
var fs = require('fs'),
path = require('path');
fs.readdirSync(__dirname).forEach(function (file) {
if (file !== 'index.js' && fs.statSync(path.join(__dirname, file)).isFile()) {
module.exports[file.substring(0, file.length-3)] = require(path.join(__dirname, file));
}
});
I'm getting strange errors in my application where aribtrary text files are being loaded from the directory my compiled file is loaded in. I think it's because paths are no longer set correctly, and because Browserify won't be able to require() the correct files that are dynamically loaded like this.
Short of making a static index.js file, is there a preferred method of dynamically requiring a directory of modules that is out-of-the-box compatible with Browserify?
This plugin allows to require Glob patterns: require-globify
Then, with a little hack you can add all the files on compilation and not executing them:
// Hack to compile Glob files. Don´t call this function!
function ಠ_ಠ() {
require('views/**/*.js', { glob: true })
}
And, for example, you could require and execute a specific file when you need it :D
var homePage = require('views/'+currentView)
Browserify does not support dynamic requires - see GH issue 377.
The only method for dynamically requiring a directory I am aware of: a build step to list the directory files and write the "static" index.js file.
There's also the bulkify transform, as documented here:
https://github.com/chrisdavies/tech-thoughts/blob/master/browserify-include-directory.md
Basically, you can do this in your app.js or whatever:
var bulk = require('bulk-require');
// Require all of the scripts in the controllers directory
bulk(__dirname, ['controllers/**/*.js']);
And my gulpfile has something like this in it:
gulp.task('js', function () {
return gulp.src('./src/js/init.js')
.pipe(browserify({
transform: ['bulkify']
}))
.pipe(rename('app.js'))
.pipe(uglify())
.pipe(gulp.dest('./dest/js'));
});

how to minify a whole folder content using grunt.js

Instead of mention every js seperatly,
is this the way to minify and concatinate a whole js folder?
module.exports = function(grunt) {
grunt.initConfig({
min: {
dist: {
src: ['scripts/*.js'],
dest: 'dist/built.min.js'
}
}
});
};
Yes, that's correct if you only want to concatenate and minify all .js files in the scripts directory one level deep.
For example, if scripts/ contains a.js and b.js and the foo/ directory, you'd get the concatenation and minified result of a.js + b.js but nothing in the foo/ directory.
What if you want to get everything in the foo/ directory (and all other nested directories) as well? Change the expression from ['scripts/*.js'] to ['scripts/**/*.js'] -- or any minimatch expression:
https://github.com/gruntjs/grunt/blob/master/docs/api_file.md#gruntfileexpand
You're able to use any minimatch expression since the grunt min task uses the expandFiles function:
https://github.com/gruntjs/grunt/blob/master/tasks/min.js#L21
The downside to using a minimatch expression with this task is it's hard to understand what order the files will be concatenated in, which is often very important. Be careful if this matters.
Also, please note that a new version of grunt (0.4) is coming out very soon. This will make this answer obsolete, as the min task has been changed in 0.4 (but will still support minimatch expression).
If your folder consist only js files you are right but if your folder have nested folders such as, foo is our main js folder in which we have another nested folder loo and inside it we also have some js files such as :
foo:
mu.js
su.js
loo:
ku.js
wu.js
In this case you have to modify your code in such manner :
module.exports = function(grunt) {
grunt.initConfig({
min: {
dist: {
src: 'foo/**/*.js',
dest: 'dist/foo.min.js'
}
}
});
};
by doing in such a way you can minimize your all js files of foo folder even nested folder files too. I suggest cocat js files before minimizing.

Categories