Currently I am working on a JavaScript application and I am trying to build it as modular as possible. What I'm planning to do is to get a folder structure like
js
|__ controllers
|__ services
|__ directives
index.html
I am using angularjs and i want to split all the controllers and services into seperate files.
I am a Java-Developper and I want to use maven to build this project and deploying it on a tomcat.
In production mode i want to a compressor/obfuscator to pack all files into one single application.js. Normally when I build my projects i only got one file for all controllers and one for all services and so on. So what I am doing is have one profile for dev and one for live and with filtering I append .min.js in my index.html to include all the files ( minimized on live, non-minimized on dev )
Finally my question:
What is the best way to do that with multiple files?
In development mode i want
<script type="text/javascript" src="MyFirstController.js">
<script type="text/javascript" src="MySecondController.js">
<script type="text/javascript" src="MyThirdController.js">
<script type="text/javascript" src="MyFirstService.js">
/* ... */
And in production mode i only want one file included in my index.html
<script type="text/javascript" src="myapp.js">
The first solution but clearly a not very pretty solution would be to have a multiline property in my maven file where i got all the script tags which i then replace with filtering.
I hope someone can tell me a good solution for my problem. Thanks in advance!
Take a look at the frontend-maven-plugin. This is useful if you want to bundle scripts with a maven artefact.
I haven't needed to use this because my javascript code has been an entirely separate deployable (decoupling and all that). So this is a good use case for gulp. Take a look at the gulpfile.js from hottowel.
First take a look at the build task and its dependencies:
/**
* Build everything
* This is separate so we can run tests on
* optimize before handling image or fonts
*/
gulp.task('build', ['optimize', 'images', 'fonts', 'test-server'], function() {
log('Building everything');
var msg = {
title: 'gulp build',
subtitle: 'Deployed to the build folder',
message: 'Running `gulp serve-build`'
};
del(config.temp);
log(msg);
notify(msg);
});
These should do most of the things you want. There are also environment specific gulp tasks:
gulp serve-dev
gulp build
There are a lot more in there so take a read through.
I've also worked on java + angular projects and it was handy for deployment and consistent with the java/maven approach to create artefacts for the javascript code that can be stored on nexus and fetched for deployment when needed.
The artefact for the javascript code is just a zip file which I create with the following gulp task (ignoring directories and files with ! in front of the directory name):
/**
* Create a versioned artefact
*/
gulp.task('generate-artifact', function() {
var filename = getFilename();
return gulp
.src([
'./**/*.*',
'!artifacts/',
'!artifacts/**',
'!build/',
'!build/**',
'!node_modules/',
'!node_modules/**',
'!bower_components/',
'!bower_components/**'
])
.pipe($.tar(filename + '.tar'))
.pipe($.gzip())
.pipe(gulp.dest('./artifacts/'));
});
Then to deploy to nexus using the nexus-deployer plugin:
/**
* Deploy release to nexus
*/
gulp.task('deploy-release', ['clean-pomdir'], function(cb) {
var deployer = require('nexus-deployer');
var filename = getFilename();
var pkg = require('./package.json');
log('Retrieving artifact ' + filename + '.tar.gz' + 'from artifacts dir');
log('Deploying version ' + pkg.version + ' of ' + pkg.name);
var release = {
groupId: 'my.group.id',
artifactId: 'my-artefact',
version: pkg.version,
packaging: 'tar.gz',
auth: {
username: args.username,
password: args.password
},
pomDir: config.pomDirReleases,
url: 'http://path-to-nexus.repo',
artifact: './artifacts/' + filename + '.tar.gz',
noproxy: '',
cwd: ''
};
deployer.deploy(release, cb);
});
There are a few custom methods and config things in there too: the great yargs module to pass in username and password as command line arguments; convenience method in gulpfile.js to get the filename:
function getFilename() {
var pkg = require('./package.json');
var filename = pkg.name + '_' + pkg.version;
return filename;
}
Related
I use the FayeJS and the latest version has been modified to use RequireJS, so there is no longer a single file to link into the browser. Instead the structure is as follows:
/adapters
/engines
/mixins
/protocol
/transport
/util
faye_browser.js
I am using the following nodejs build script to try and end up with all the above minified into a single file:
var fs = require('fs-extra'),
requirejs = require('requirejs');
var config = {
baseUrl: 'htdocs/js/dev/faye/'
,name: 'faye_browser'
, out: 'htdocs/js/dev/faye/dist/faye.min.js'
, paths: {
dist: "empty:"
}
,findNestedDependencies: true
};
requirejs.optimize(config, function (buildResponse) {
//buildResponse is just a text output of the modules
//included. Load the built file for the contents.
//Use config.out to get the optimized file contents.
var contents = fs.readFileSync(config.out, 'utf8');
}, function (err) {
//optimization err callback
console.log(err);
});
The content of faye_browser.js is:
'use strict';
var constants = require('./util/constants'),
Logging = require('./mixins/logging');
var Faye = {
VERSION: constants.VERSION,
Client: require('./protocol/client'),
Scheduler: require('./protocol/scheduler')
};
Logging.wrapper = Faye;
module.exports = Faye;
As I under stand it the optimizer should pull in the required files, and then if those files have required files, it should pull in those etc..., and and output a single minified faye.min.js that contains the whole lot, refactored so no additional serverside calls are necessary.
What happens is faye.min.js gets created, but it only contains the content of faye_browser.js, none of the other required files are included.
I have searched all over the web, and looked at a heap of different examples and none of them work for me.
What am I doing wrong here?
For anyone else trying to do this, I mist that on the download page it says:
The Node.js version is available through npm. This package contains a
copy of the browser client, which is served up by the Faye server when
running.
So to get it you have to pull down the code via NPM and then go into the NPM install dir and it is in the "client" dir...
I am doing bundling and minification for javascript files. I am doing this using gulp. Now I want that if I make any change in any of my file and hit gulp then it generate a new bundled and minified file with version number like:
<script src="https://cdn.test.com/bundle-1.0.0-min.js/"></script>
then
<script src="https://cdn.test.com/bundle-1.0.1-min.js/"></script>
I want to do this using gulp because I am already using gulp for other purposes. And one more thing if this is possible then is there any way that I don't specify version no in my html page every time I make a change and my html page get the latest version by its own somehow.
This is just a rename of the file in general. But this should really not be an automated task to increment the version number. Otherwise you will be quickly getting a version like 1.0.2092 what is not helpful. I would suggest to read the version out of the package.json and use it for the name of the file. Should be pretty easy, if you already worked with gulp.
If you don't want to use the global version (version entry) of your package.json, you could add an own entry for your bundle version. Or even use a different file than package.json. You could even use that as config for which files should be bundled, to have everything in one place:
{
"bundle": {
"version": "1.0.1",
"files": [
"path/to/file-one.js",
"another/file.js",
"..."
]
}
}
Just a quick example:
var pkg = require("./package.json");
var gulp = require("gulp");
var rename = require("gulp-rename");
gulp.src(pkg.bundle.files)
.concat("bundle.js")
.pipe(uglify())
.pipe(rename(function(path) {
path.extname = "-" . pkg.bundle.version + "-min" + path.extname;
}))
.pipe(gulp.dest("./"));
Note: instead of rename you can just set the concat name, but I like to split this. But just to be complete:
.concat("bundle-" + pkg.bundle.version + "-min.js")
About the second parts of your question, to replace things in your files:
This would be possible if you build your html pages too, and replace/inject the relevant path into it. You could use the version of the package.json again, to build it and replace. Or use tools like gulp-inject. That simple tool can add js and css files into your html templates. Just create an area where they should be placed in the html file, like: <!-- inject:js --><!-- endinject -->. Afterwards it is a simple gulp taks too:
var pkg = require("./package.json");
var gulp = require("gulp");
var inject = require("gulp-inject");
gulp.src("dev/index.html")
.pipe(inject("bundle-" + pkg.bundle.version + "-min.js"))
.pipe(gulp.dest("prod/"));
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.
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...
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'));
});