Requirejs Optimize project to single file - backbone.js

With a Build configuration as below, Why am i seeing all the files in source directory + minified application file when i run the deploy command specified below. I only need a single js file that will kickoff my backbone application
Build Config
({
appDir: "../",
baseUrl: 'Comment',
dir: './deploy',
optimize: 'uglify',
paths: {
text: '../../amd/plugins/text',
CommentView: 'views/feedback',
Feedback: 'models/feedback',
templates: 'templates'
},
modules: [
{
name: "app"
}
]
})
App.js
require.config({
urlArgs: "bust=" + (new Date()).getTime(),
baseUrl: 'scripts/apps/feedback',
paths: {
text: '../../amd/plugins/text',
CommentView: 'views/feedback',
Feedback: 'models/feedback',
templates: 'templates'
}
});
require(["Feedback", "CommentView"], function (feedbackModel, commentView) {
});
Optimization Command
node amd/plugins/r.js -o apps/feedback/build.config.js

By default, the requirejs optimizer does not remove modules from the output. Check the contents of your built app.js, I would guess that it has all of your modules in it. The individual modules shouldn't cause any problems and won't be used, but if you really went to get rid of them, try setting removeCombined: true in your build config.

Related

Bundle the js files based on requirejs structure

We have requirejs.
I have mentioned the paths and the shim in requirejs.
Here, when I load the controller in angularjs router, the files are loading separately.
require.config({
urlArgs: "bust=" + (new Date()).getTime(),
baseUrl: 'Folder',
waitSeconds: 200,
paths: {
// Jquery
'Angular': 'Angular path',
'FileA': 'FileA',
'FileB': 'FileB',
},
shim: {
'FileB'{
Deps:'FileA'
},
'FileA'{
Deps:'Angular'
},
}
When I require FileB, it will automatically retrieve fileA and angular file.
But in this case, I am requesting three http request for the three files.
Is there any solution that, based on requirejs structure bundling the files?
So that when I request the file, I will get the single file instead of multiple files
If there is any dependency between the files that you are trying to bundle then bundling them in separate files doesn't make any sense.
However if you are sure that dependencies would not be any problem you can use bundle a new feature from requirejs:
requirejs.config({
bundles: {
'shared': ['angular'],
'fileA': ['fileA'],
'fileB': ['fileB'],
// etc...
}
});
Also you can add your template to the bundle which is awesome. More info here.

Building Typescript for Angular using Gulp

I'm trying to setup an angularjs project according to Johnpapa's Angular Style Guide whilst using TypeScript and Gulp as a build tool. I believe Gulp is currently recommended over Grunt but I'm not very experienced with Gulp.
What I have:
My project currently looks like this:
src/
+- ts/ # contains .ts source files
+- typings/ # contains .d.ts typing definitions
+- html/ # contains .html files
dist/
+- bundle.js # single .js file containing compiled typescript and sourcemaps
Following the angular style guide I have created a separate .ts file for each angular element.
my-app.module.ts
----------------
angular.module('myApp', []);
for initialization of the module and another for a simple implementation of a controller:
my-controller.controller.ts
----------------------------
export class MyController {
testString = 'test';
}
angular
.module('myApp')
.controller('MyController', MyController);
typescript is configured using a simple tsconfig.json. (Note that filesGlob is not active yet - it will become available from TypeScript 2.0)
tsconfig.json
-------------
{
"exclude" : [
"node_modules"
],
"filesGlob" : [
"./src/typings/index.d.ts",
"./src/ts/**/*.ts",
"!./node_modules/**/*.ts"
],
"compilerOptions": {
"noImplicitAny": true,
"target": "es5",
"sourceMap" : true,
"outFile" : "./dist/bundle.js",
"removeComments": false
}
}
What I want:
I would ideally like to
Have Gulp monitor new or updated .ts files in ./src/ts/**/*.ts
Concatenate all the files from ./src/ts/**/*.ts. This is required for angular to work properly. Other methods I've tried using requirejs or browserify can't find the other .ts files without having to manually input references to these files.
Compile using the definitions from tsconfig.json. This would take into consideration the typings in ./src/typings/index.d.ts (for external modules including 'angular'). Also sourcemaps.
Possibly an uglify or babelify step to finish it.
What I tried:
I've tried following the manual from the typescriptlang handbook but this uses browserify and won't work with angular.
Gulp-typescript also has a note on concatenating files but the out option doesn't work like this:
var gulp = require('gulp');
var ts = require('gulp-typescript');
var tsProject = ts.createProject('tsconfig.json');
gulp.task('default', function () {
var tsResult = tsProject.src().pipe(ts(tsProject));
return tsResult.js.pipe(gulp.dest('dist'));
});
This configuration will output an empty file with only comments.
Another method mentioned in this question:
gulp.task('ts', function () {
gulp.src('./src/ts/**/*.ts')
.pipe(ts({
noImplicitAny: true,
out: 'output.js'
}))
.pipe(gulp.dest('./tmp/ts'));
});
gulp.task('default', ['ts'], function() {
gulp.src(['./tmp/ts/output.js'])
.pipe(sourcemaps.init())
.pipe(uglify())
.pipe(sourcemaps.write('/'))
.pipe(gulp.dest('./dist/'));
});
But this gave two issues: 1. Even though I only pointed at the .ts files in ./src/ts the typescript compiler started spewing errors from .ts in ./node_modules. 2. It still didn't manage to concatenate everything.
I'm at quite a loss here. Can anyone help me set up this build script? I'm surprised I couldn't find a similar working demo anywhere.
Solution:
I've configured the gulp environment based on the solution in this answer and removed the 'export' statement for classes / objects that are not inside a typescript module.
If that helps, here is a Angular Typescript Gulp Tutorial that has a basic TypeScript, Angular, Gulp, etc. setup that concatenate the app and the vendor/nodes files. There is the demo code on github.
/* File: gulpfile.js */
// grab our gulp packages
var gulp = require('gulp');
// Include plugins
var plugins = require("gulp-load-plugins")({
pattern: ['gulp-*', 'gulp.*', 'main-bower-files', 'del'],
replaceString: /\bgulp[\-.]/
});
var browserSync = require('browser-sync').create();
var reload = browserSync.reload;
// create a default task to build the app
gulp.task('default', ['jade', 'typescript', 'bowerjs', 'bowercss', 'appcss'], function() {
return plugins.util.log('App is built!')
});
In my example, we use Jade to HTML:
// Jade to HTML
gulp.task('jade', function() {
return gulp.src('src/**/*.jade')
.pipe(plugins.jade()) // pip to jade plugin
.pipe(gulp.dest('dist')) // tell gulp our output folder
.pipe(reload({stream: true}))
;
});
For TypeScript, we compiled into one single app.js file:
// TYPESCRIPT to JavaScript
gulp.task('typescript', function () {
return gulp.src('src/**/*.ts')
.pipe(plugins.typescript({
noImplicitAny: true,
out: 'app.js'
}))
.pipe(gulp.dest('dist/js/'))
.pipe(reload({stream: true}))
;
});
For bower, we merge all the js files in vendor.js and CSS in vendor.css:
// BOWER
gulp.task('bowerjs', function() {
gulp.src(plugins.mainBowerFiles())
.pipe(plugins.filter('**/*.js'))
.pipe(plugins.debug())
.pipe(plugins.concat('vendor.js'))
.pipe(plugins.uglify())
.pipe(gulp.dest('dist/js'));
});
gulp.task('bowercss', function() {
gulp.src(plugins.mainBowerFiles())
.pipe(plugins.filter('**/*.css'))
.pipe(plugins.debug())
.pipe(plugins.concat('vendor.css'))
.pipe(gulp.dest('dist/css'));
});
Custom CSS:
// APP css
gulp.task('appcss', function () {
return gulp.src('src/css/**/*.css')
.pipe(gulp.dest('dist/css/'))
.pipe(reload({
stream: true
}));
});
// CLEAN
gulp.task('clean', function(done) {
var delconfig = [].concat(
'dist',
'.tmp/js'
);
// force: clean files outside current directory
plugins.del(delconfig, {
force: true
}, done);
});
This is what reloads the browser when changes occur:
// Watch scss AND html files, doing different things with each.
gulp.task('serve', ['default'], function () {
// Serve files from the root of this project
browserSync.init({
server: {
baseDir: "./dist/"
}
});
gulp.watch("src/**/*.jade", ['jade']).on("change", reload);
gulp.watch("src/**/*.ts", ['typescript']).on("change", reload);
gulp.watch("src/**/*.css", ['appcss']).on("change", reload);
});
My tsconfig.json looks like this... I put the JS files that are automatically compiled from the text editor (Atom) into .tmp/js/atom ... some people put the .js in the same directory as the .ts but I find it confusing... less files is better for me:
{
"compilerOptions": {
"target": "ES5",
"module": "commonjs",
"outDir": ".tmp/js/atom"
},
"exclude": [
"node_modules",
"typings"
]
}

r.js cannot resolve dependencies mentioned in shim

I've recently joined a project which is built using Backbonejs, (uses Marionette for view rendering) + nodejs. They also use requirejs to load the backbonejs files. Would like to add at this stage, that I've never worked with backbonejs or requirejs before and hence I'm struggling with the issue I describe later.
Some code that will help explain the issue that I run into (All this code was already written by previous dev's)
Folder Structure:
/public
/js
/collection (consists all Backbone.js collections files)
/lib
/bower_components
/backone
/marionette
/etc
/models (consists all Backbone.js models files)
/views (consists all Backbone.js view files)
/main.js
/main.build.js
/app.js
/controller.js
/router.js
Code from files that I think relate to issue:
main.js
requirejs.config({
paths: {
'async': 'lib/bower_components/requirejs-plugins/src/async',
'jquery': 'lib/bower_components/jquery/dist/jquery.min',
'underscore': 'lib/bower_components/underscore/underscore-min',
'lodash': 'lib/bower_components/lodash/dist/lodash.min',
'backbone': 'lib/bower_components/backbone/backbone',
'marionette': 'lib/bower_components/marionette/lib/backbone.marionette.min',
'markercluster':'lib/markercluster',
'jquerymobile': 'lib/jquery.mobile-1.4.0.min',
'hogan': 'lib/template-2.0.0.min',
'templates': '/templates',
'real': 'lib/mainjs',
'touch': 'lib/jquery.touchSwipe.min',
'mouse': 'lib/jquery.mousewheel',
'moment': 'lib/moment-2.5.1.min',
'humanize': 'lib/bower_components/humanize-plus/public/dist/humanize.min',
'validator': 'lib/bower_components/validator-js/validator.min',
'real': 'lib/mainfile'
},
shim: {
backbone: {
deps: ["underscore"]
},
marionette: {
deps: ["backbone"]
},
templates: {
deps: ["hogan", "jquery"]
},
real: {
deps: ["jquery", "jquerymobile", "touch", "mouse"]
},
markercluster: {
exports: "MarkerClusterer"
},
humanize: {
exports: "humanize"
}
},
waitSeconds: 0
});
define('gmaps', ['async!http://maps.google.com/maps/api/js?v=3&key=AIzaSyBiV8f88yLWJ_IMSdP1fVNO1-gt3eLVSgg&sensor=true&callback=gMapsCallback'], function(){
// define('gmaps', ['http://maps.google.com/maps/api/js?v=3&sensor=false'], function(){
return window.google.maps;
});
require(['app', 'templates', 'real'], function(app) {
app.start({
version: "0.9.9"
});
});
main.build.js
({
baseUrl: ".",
name: "main",
wrapShim: true,
out: "main-built.js"
})
app.js
define(['underscore', 'controller', 'router', 'models/Cache', 'views/RootView'], function(_, Controller, Router, Cache, RootView) {
var Application = Marionette.Application.extend({
propertyListPageSize: 3,
initialize: function() {
_.templateSettings = { interpolate : /\{\{(.+?)\}\}/g };
},
onStart: function(options){
new RootView();
this.controller = new Controller();
this.router = new Router({controller: this.controller});
this.cache = new Cache();
this.context = {};
//this.evHistory = [];//#todo remove once BB/marionette navigation is in place
if(Backbone.history) Backbone.history.start({ pushState: false });
if(Backbone.history.fragment === "") this.navigate('home');
},
navigate: function(fragment, trigger, replace){
this.router.navigate(fragment, {trigger:trigger, replace:replace});
},
back: function() {
window.history.back();
}
});
app = new Application();
return app;
});
rootView.js
define(['marionette', 'views/HomeView', 'views/HeaderView', 'views/FooterView', 'views/MenuView', 'views/VideoView', 'views/LocationSearchView', 'views/LoginView', 'views/FindView', 'views/ServicesView', 'views/ValueView', 'views/PropertyListView', 'views/SideBySideView', 'views/ConfirmRegistrationView', 'views/ForgotPasswordView', 'views/CreateAccountView', 'views/UserHomeView', 'views/MyBrokerView', 'views/GiveFeedbackView', 'views/SeeFeedbackView', 'views/ViewingScheduleView', 'views/MyViewingsSummaryView', 'views/MyAccountView', 'views/ViewingConfirmView', 'views/ValueAddressPropertyListView'],
function(Marionette, HomeView, HeaderView, FooterView, MenuView, VideoView, LocationView, LoginView, FindView, ServicesView, ValueView, PropertyListView, SideBySideView, ConfirmRegistrationView, ForgotPasswordView, CreateAccountView, UserHomeView, MyBrokerView, GiveFeedbackView, SeeFeedbackView, ViewingScheduleView, MyViewingsSummaryView, MyAccountView, ViewingConfirmView, ValueAddressPropertyListView) {
var RootView = Marionette.LayoutView.extend({
...some view code
});
Use case I'm trying to solve:
So when I access the site in the browser, I notice in the debugger that it loads all the js files right at the beginning. During the load process my site is blank and user has to wait a while before he can use the site.
So what I've been able to understand is that when app is 'started' in main.js, app.js creates an instance of rootView.js , which in turn has all the views listed as dependencies. This triggers a download request for all the other views which in turn would solve their own dependencies and download all the relevant models and collections. Hence all files being downloaded when the user accessed the site.
Solution I've been trying:
Since requirejs is being used, I'm trying to use r.js to optimize and combine all the js files to reduce the number of downloads.
Issue I'm running into:
When i run r.js. i get the following error
Tracing dependencies for: main
Error: ENOENT: no such file or directory, open '/var/node_projects/rm/rm.src.server/src/public/js/underscore.js'
In module tree:
main
app
Error: Error: ENOENT: no such file or directory, open '/var/node_projects/rm/rm.src.server/src/public/js/underscore.js'
In module tree:
main
app
at Error (native)
If I add the underscore.js files directly to the specified path in the error, then I get the same error for marionette.js. What I think is happening is that app.js is not recognizing the shim'ed dependencies and hence its trying to find the files directly at specified path in the error.
Things I've tried:
- I've added wrapShim: true in the main.build.js file but that did not help
Honestly, I've been sitting on this for a couple of days and I'm not sure what I can do next and hence this post.
Any help/direction would be appreciated.
You need to include the same shim configuration in your build file, as wrapShim is not sufficient.
If shim config is used in the app during runtime, duplicate the config here. Necessary if shim config is used, so that the shim's dependencies are included in the build. Using "mainConfigFile" is a better way to pass this information though, so that it is only listed in one place. However, if mainConfigFile is not an option, the shim config can be inlined in the build config.
https://github.com/jrburke/r.js/blob/master/build/example.build.js

Compile all angular templates to one js file

I am trying to compile all angulara templates into a single js file.
Something like what ember does with ember-cli.
So I successfully managed to minify and concat all the javascript files.
I have just 2 files now vendor.js and application.js and whole lot of template files which I want to cram into templates.js.
How do I go about it? If some one could give step by step explanation, please. Any links would be appreciated too.
Surprisingly there is no information about this anywhere.
I am using mimosa as build tool, it seemed to me the easiest.
Here is my mimosa config:
exports.config = {
modules: [
"copy",
"stylus",
"minify-css",
"minify-js",
"combine",
"htmlclean",
"html-templates"
],
watch: {
sourceDir: "app",
compiledDir: "public",
javascriptDir: "js",
exclude: [/[/\\](\.|~)[^/\\]+$/]
},
vendor: {
javascripts: "vendor/js"
},
stylus: {
sourceMap: false
},
combine: {
folders: [
{
folder:"vendor/js",
output:"vendor.js",
order: [
"angular.js"
]
},
{
folder:"js",
output:"main.js",
order: [
"application/main.js"
]
}
]
},
htmlclean: {
extensions:["html"]
},
htmlTemplates: {
extensions: ["tpl"]
},
template: {
outputFileName: "templates"
}
}
It does generate templates.js file without any errors. But when I link it, angular spits a bunch of errors.
Once compiled, how do I actually call those templates from ng-include and from the route provider?
I assume that it is the same as I would call a script template using the id which in my case is derived from template original file name, right?
Maybe I am missing some important steps.
The build tool is not important here although desirable. If some one could show how to do it manually without a build tool I would figure out the rest.
Thanks.
I'm using Gulp as my build tool, and in that, there's a plugin gulp-angular-templatecache which pre-compiles and registers all templates for your module in the angular $templateCache - no changes are required to any of the calling code to use these. EDIT: The Angular documentation for $templateCache explains how the templateCache works.
It might be worth reading through the documentation for gulp-angular-templatecache to see how that pre-populates the $templateCache to see if you can crib something that would work with your build process.
Here's my gulp task that does the job:
var templateCache = require('gulp-angular-templatecache');
gulp.task('buildjstemplates', function () {
return gulp.src(['public/javascripts/app/**/*.html'])
.pipe(templateCache({module: 'app'}))
.pipe(gulp.dest('public/javascripts/app/'));
});

Chrome extension using RequireJS, Backbone (Chaplin) conflicts

I am creating a Google Chrome Extension that have to add content on the visited websites (like a toolbox).
I have to use RequireJS and BackboneJS (Chaplin) and everything is ok except when i'm visiting a website using RequireJS (and Backbone, but the problem seems to come from RequireJS conflicts).
(This is when I use content scripts to include a -script- tag that includes RequireJS.)
I suppose it's normal to have conflicts if I add content directly in the page so I tried the solution here : Loading multiple instances of requireJS and Backbone
It seems to work (for now), but the website is trying to reload his own RequireJS file (with his path, but in my extension) before loading mine and I'm afraid it could lead to unexpected behaviour.
Plus, I have to precise my file paths in requirejs.config or it's looking for them in Bitbucket sources (cloudfront). (Maybe it's normal though)
Example with bitbucket :
Denying load of chrome-extension://mgncmiffelpdhlbkkmmaedbodabdchea/https://d3oaxc4q5k2d6q.cloudfront.net/m/7aaf1677069c/amd/build/main.js?8uxr. Resources must be listed in the web_accessible_resources manifest key in order to be loaded by pages outside the extension.
<--------- This file is Bitbucket's RequireJS, Bitbucket is still working fine though
Is there another solution I didn't find yet ? Or am I doing it wrong ? I'm a beginner with RequireJS (and Chrome ext.. and Backbone...) so I might have missed something.
Here is the Content script part in manifest.json
"content_scripts": [
{
"matches": ["https://*/*", "http://*/*"],
"js": ["bower_components/requirejs/require.js",
"extension/init-app.js",
"extension/main.js"]
}],
init-app.js is Rob's script
require.load = function(context, moduleName, url) {
url = chrome.extension.getURL(url);
var x = new XMLHttpRequest();
// Append Math.random()... to bust the cache
x.open('GET', url + '?' + Math.random().toString(36).slice(-4));
x.onload = function() {
var code = x.responseText;
x += '\n//# sourceURL=' + url; // Optional, for debugging.
window.eval(code);
context.completeLoad(moduleName);
};
x.onerror = function() {
// Log error if you wish. This is usually not needed, because
// Chrome's developer tools does already log "404 Not found"
// errors for scripts to the console.
};
x.send();
};
and main.js contain requirejs.config + app
// Configure the AMD module loader
requirejs.config({
skipDataMain: true,
// The path where your JavaScripts are located
baseUrl: 'extension',
// Specify the paths of vendor libraries
paths: {
jquery: '../bower_components/jquery/jquery',
underscore: '../bower_components/lodash/dist/lodash',
backbone: '../bower_components/backbone/backbone',
handlebars: '../bower_components/handlebars/handlebars',
text: '../bower_components/requirejs-text/text',
chaplin: '../bower_components/chaplin/chaplin',
application: '/extension/application',
routes: '/extension/routes',
},
// Underscore and Backbone are not AMD-capable per default,
// so we need to use the AMD wrapping of RequireJS
shim: {
underscore: {
exports: '_'
},
backbone: {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
},
handlebars: {
exports: 'Handlebars'
}
}
// For easier development, disable browser caching
// Of course, this should be removed in a production environment
//, urlArgs: 'bust=' + (new Date()).getTime()
});
// Bootstrap the application
require(['application', 'routes'], function(Application, routes) {
new Application({routes: routes, controllerPath: 'scripts/controllers/', controllerSuffix: '-controller'});
});
It works on gooogle.com for instance, but I get
GET chrome-extension://ccgfmmmnebacpnbdpdnphmnmicaooddg/extension/Home.js?9zfr net::ERR_FILE_NOT_FOUND
on https://www.cloud9trader.com (website using RequireJS) because it has
<script data-main="/0.2.59/scripts/Home.js" src="//cdnjs.cloudflare.com/ajax/libs/require.js/2.1.14/require.min.js"></script>
in its source. To summarize I just need the script to ignore the "current" website Require file.
The skipDataMain option is synchronously checked when require.js is loaded. Setting this variable after loading require.js has no effect on the loader any more, because the data-main scan has already run at that point.
The correct way to skip data-main is to declare the configuration before loading require.js, as follows:
// extension/config.js
var require = {
skipDataMain: true
};
manifest.json:
{
...
"content_scripts": [{
"matches": ["https://*/*", "http://*/*"],
"js": [
"extension/config.js",
"bower_components/requirejs/require.js",
"extension/init-app.js",
"extension/main.js"
]
}],
...
}

Resources