Grunt-browserify external libs give: Cannot find module - backbone.js

I'm working on app with Backbone Marionette and i'm building my files with grunt-browserify 3.8.
Backbone, Marionette, underscore and jQuery are added with npm.
I'm compiling all my files in one single file.
Everything was working fine but the build was extremly slow (like 1.5mins) so i read about using external config option in grunt-browserify.
Now the build is quite fast but when i access to the page i got:
Uncaught Error: Cannot find module 'underscore' to the line when i first use my require function
I read everywhere but i cannot figured out the correct configuration for grunt-brwoserify.
Here is my GruntFile:
'use strict';
module.exports = function (grunt) {
require('grunt-config-dir')(grunt, {
configDir: require('path').resolve('tasks')
});
require('jit-grunt')(grunt);
// show elapsed time at the end
require('time-grunt')(grunt);
grunt.registerTask('i18n', [ 'clean', 'dustjs', 'clean:tmp' ] ); // not used for now
grunt.registerTask('build', ['i18n', 'less', 'cssmin', 'browserify', 'concurrent:build'] );
grunt.registerTask('serve', [ 'build', 'concurrent:server'] ); // Build & run the server locally, for development.
};
Here my Browserify task:
'use strict';
module.exports = function browserify(grunt) {
// Load task
grunt.loadNpmTasks('grunt-browserify');
// Options
return {
build: {
files: {
'.build/js/theme-smarty.js': ['public/js/assets/smarty-themeApp/plugin/jquery.min.js', 'public/js/assets/smarty-themeApp/**/*.js'],
'.build/js/app-bundled.js': ['public/js/app.js'],
'.build/js/landing-page.js': ['public/js/landing-page.js']
// '.build/js/app-admin-bundled.js': ['public/js/app-admin.js']
},
options: {
// activate watchify
watch: true,
watchifyOptions: {
spawn: false
},
include: [
'public/js/**/*.js'
],
transform: [['babelify', {'presets': 'es2015', 'compact': false}]],
external: [
'backbone',
'underscore',
'jquery',
'backbone.marionette'
]
}
}
};
};
And here my first views where i require libs:
'use strict';
const
_ = require('underscore'),
$ = require('jquery'),
Backbone = require('backbone'),
Marionette = require('backbone.marionette'),
MainRouter = require('./main-router'),
MainController = require('./main-controller');
Backbone.$ = $;
let View = Marionette.LayoutView.extend({
template: require('./main-page.dust'),
regions: {
mainContainer: '.main-container',
modalContainer: '.modal-container'
},
initialize: function () {
this.model = new Backbone.Model({
page: this.$el.data('page')
});
new MainRouter({
controller: new MainController({
mainContainer: this.mainContainer,
modalContainer: this.modalContainer
})
});
},
onRender: function () {
Backbone.history.start({pushState: true});
}
});
module.exports = View;
Look like libs are not even compiled in my app-bundled.js files.
What's the best/correct way to compile them?
Is better to have two separate files? libs and app?
Is possible to do with just one file using libs from npm?

Related

ngHtml2JsPreprocessor does not load the module containing the templates, tested with karma

Testing my angular app with karma ngHtml2JsPreprocessor, I have read dozens of pages on how ton configure karma to generate js templates instead of html ones that are loaded by Angular Directives, I am stuck with the message :
Module 'js_templates' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
http://errors.angularjs.org/1.4.6/$injector/nomod?p0=js_templates
My folder structure is like this :
/System/Root/Path/
web/
js/
app/
test/
specs/
UserModule/
UserDirectives.spec.js
modules/
UserModule/
UserDirectives.js <=== Where the directive is defined
Templates
login.tpl.html
My directive is defined like this :
directives.directive("loginForm", [function(){
return{
restriction: "E",
templateUrl: assetsRootPath+'/Templates/login.tpl.html' //assetsRootPath is defined to '' for karma
}]);
Karma is configured as follow :
module.exports = function(config) {
config.set({
basePath: './web/js/',
frameworks: ['jasmine', 'requirejs'],
preprocessors: {
'../Templates/**/*.html': ['ng-html2js']
},
ngHtml2JsPreprocessor: {
stripPrefix: '.*web/',
prependPrefix: '/',
moduleName: 'js_templates',
enableRequireJs: false
},
plugins : [
'karma-chrome-launcher',
'karma-jasmine',
'karma-requirejs',
'karma-ng-html2js-preprocessor'
],
// list of files / patterns to load in the browser
files: [
{ pattern: '../Templates/**/*.html', included: false },
//....Other needed files
]
The spec for testing the directive :
define(['angular', 'ngmocks', 'app/modules/UserModule/UserDirectives'], function() {
describe("Test User Directive > ", function () {
var $compiler, compiledElement;
beforeEach(module('User.Directives'));
beforeEach(module('js_templates'));
beforeEach(inject(function (_$rootScope_, _$compile_) {
$rootScope = _$rootScope_;
$compile = _$compile_;
scope = $rootScope.$new();
compiledElement = $compile("<login-form></login-form>")(scope);
scope.$digest();
}));
it('Tries to test', function(){
expect(1).toEqual(1);
});
});
});
When I launch the tests the following error appears :
Module 'js_templates' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
http://errors.angularjs.org/1.4.6/$injector/nomod?p0=js_templates
I have tried a lot of configurations and nothing works, I have also tried to debug ngHtml2JsPreprocessor by adding some log.debug & I see that the module 'js_templates' is rightly created, the problem is why can't I load it for the specs. Information that could be useful (not sure if it changes something) : I am working with requirejs.
Thanks for your help
Well, as no config worked, I have chosen to use a different approach, below is what I ended up to do to make it work :
1 - Remove all pre-processors
2 - Install the simple html2js node plugin : npm install karma-html2js-preprocessor --save-dev
3 - Inside my karma.conf file :
pattern: { files: '../Templates/**/*.html', included : true } //Note the included property to true
preprocessors: {
'../Templates/**/*.html': ['html2js']
},
plugins : [
'karma-chrome-launcher',
'karma-jasmine',
'karma-requirejs',
'karma-html2js-preprocessor'
],
Inside my test-main.js file which is the entry point of require-js I have done this :
require([
'angular',
...
], function(angular, app) {
var $html = angular.element(document.getElementsByTagName('html')[0]);
angular.element().ready(function() {
// bootstrap the app manually
angular.bootstrap(document, ['app']);
//Making all templates to be availabe through js_template module
angular.module('js_templates', []).run(function($templateCache){
angular.forEach(window.__html__, function(content, filename){
var modifiedFilename = filename;
/*****************Modify this to fit your app**********************/
var breakPos = modifiedFilename.indexOf('/Templates');
modifiedFilename = filename.substr(breakPos, modifiedFilename.length - breakPos);
/*****************/Modify this to fit your app*********************/
$templateCache.put(modifiedFilename, content);
});
});
});
}
);
Not very sharp but not so bad as well, anyway it does work and it's exactly what I wanted. When a solution will come to work naturally with ngHtml2JsPreprocessor, I will use it.

JST is undefined - Yeoman , Backbone - Template cannot be loaded

Am new to Yeoman and I generated a Backbone application using generator-backbone
This is my main.js ( require js config)
/*global require*/
'use strict';
require.config({
shim: {,
handlebars: {
exports: 'Handlebars'
}
},
paths: {
jquery: '../bower_components/jquery/dist/jquery',
backbone: '../bower_components/backbone/backbone',
underscore: '../bower_components/lodash/dist/lodash',
handlebars: '../bower_components/handlebars/handlebars'
}
});
require([
'backbone'
], function (Backbone) {
Backbone.history.start();
});
Now I created a view using yo backbone:view Login
This is the generated view
define([
'jquery',
'underscore',
'backbone',
'templates'
], function ($, _, Backbone, JST) {
'use strict';
var LoginViewView = Backbone.View.extend({
template: JST['app/scripts/templates/LoginView.hbs'],
tagName: 'div',
id: '',
className: '',
events: {},
initialize: function () {
this.listenTo(this.model, 'change', this.render);
},
render: function () {
this.$el.html(this.template(this.model.toJSON()));
}
});
return LoginView;
});
When I run the app with LoginView, i get an error
Error: Script error for: templates http://requirejs.org/docs/errors.html#scripterror
Apparently I see templates is no where defined in main.js. What am i missing while running the yeoman generator
If you did the same blunder mistake like m, just verify this
After generating the code, and if its shows template is undefined or u find the template.js missing, check the following steps
run grunt handlebars (include --force if you want to ignore any warning/errors)
This will pre compile the .hbs file make a single template.js file.
But the catch here is, it wont work out of the box, the template.js is written in the location app/.tmp/scripts/template.js location.
You can modify main require configure file accordingly, or open GruntFile.js and modify this function
grunt.registerTask('createDefaultTemplate', function () {
grunt.file.write('/your/custom/path/to/templates.js', 'this.JST = this.JST || {};');
});
Also verify if u have this line in GruntFile.js
grunt.loadNpmTasks('grunt-contrib-handlebars');
if not, add it at the very last (ofcourse not after the function block)

Configure value using a service constant

In my latest project I'm making some API requests.
As far as I need to setup different endpoints (local development, remote production), I set the value of the used endpoint when defining the app. Everything works just fine.
var app = angular.module('app', ['lelylan.dashboards.device', 'ngMockE2E'])
app.value('client.config', { endpoint: 'http://localhost:8000' });
The only problem is that I want to set the endpoint http://localhost:8000 from a service that defines some constants [1]. I tried using the run and config block, but the value is not set. I was doing something like this, without any result.
var app = angular.module('app', ['lelylan.dashboards.device', 'ngMockE2E'])
.config(function(ENV) {
app.value('lelylan.client.config', { endpoint: ENV.endpoint });
})
This looks quite terrible to me, but I've no idea on how to solve this issue.
Thanks a lot.
[1] grunt-ng-constant
Please see here : http://jsbin.com/foqar/1/
var app = angular.module('app', []);
var client = {
endpoint : 'http://localhost:8000'
}
app.value('config', client);
app.controller('firstCtrl', function($scope, config){
$scope.endpoint = config.endpoint;
});
or: http://jsbin.com/foqar/2/edit
var app = angular.module('app', []);
app.value('config',{client :{endpoint : 'http://localhost:8000'}});
app.controller('firstCtrl', function($scope, config){
$scope.endpoint = config.client.endpoint;
});
It looks like the grunt-ng-constant creates a new module file to define the constants. You do not have to bother about it on your app JS file, other than declare the module containing those constants as a dependency.
The below is taken as is on the example config of the documentation of grunt-ng-constant
grunt.initConfig({
ngconstant: {
options: {
name: 'config',
dest: 'config.js',
constants: {
title: 'grunt-ng-constant',
debug: true
}
},
dev: {
constants: {
title: 'grunt-ng-constant-beta'
}
},
prod: {
constants: {
debug: false
}
},
}
});
In the options section, you specify the name of the module, file for the module to be written to and a general set of constants. It works like this,
options: {
name: 'config',
dest: 'config.js',
constants: {
title: 'grunt-ng-constant',
debug: true
}
}
The above code will become,
/*File: config.js*/
angular.module('config', [])
.constant('title', 'grunt-ng-constant')
.constant('debug', true);
To change the constants based on your scenario (development / production) you would be using different task sets. Here is where the dev and prod section comes into play
Considering you are using ng-boilerplate, in the gruntfile.js you have tasks build and compile. The build task is used during development, and the compile gets your app ready to be pushed to production.
In the build task you would add ngconstant:dev and in the compile task you would add ngconstant:prod.
grunt.registerTask('build', ['clean', 'html2js', 'otherTasksComeHere', 'ngconstant:dev']);
grunt.registerTask('compile', ['clean', 'html2js', 'otherTasksComeHere', 'ngconstant:prod']);
For your scenario the code would be as below:
/*gruntfile.js*/
grunt.initConfig({
ngconstant: {
options: {
name: 'lelylan.client.config',
dest: 'config.js',
values: {
endpoint : 'http://localhost:8000'
}
},
dev: {
debug: true
},
prod: {
endpoint: 'http://www.production-server.com/',
debug: false
}
},
});
grunt.registerTask('build', ["ngconstant:dev"]);
grunt.registerTask('compile', ["ngconstant:prod"]);
grunt.registerTask.('default', ["build", "compile"]);
/*app.js*/
var app = angular.module('app', ['lelylan.dashboards.device', 'leylan.client.config', 'ngMockE2E']);
app.controller("appCtrl", ["$scope", "$http", "endpoint",
function($scope, $http, endpoint) {
$scope.submit = function(formData) {
$http.post(endpoint+'/processform.php', formData);
}
}]);
Now it all depends on whether you run grunt build or grunt compile. The default task is run when you use the grunt command.
The solution was to use providers. As the docs states:
during application bootstrap, before Angular goes off creating all
services, it configures and instantiates all providers. We call this
the configuration phase of the application life-cycle. During this
phase services aren't accessible because they haven't been created
yet.
For this reason I created a provider to set the needed configurations.
<script>
angular.module('app', ['lelylan.dashboards.device'])
angular.module('app').config(function(lelylanClientConfigProvider, ENV) {
lelylanClientConfigProvider.configure({ endpoint: ENV.endpoint });
});
</script>
In this way all services will then be able to use the new endpoint. Using .value this was not possible.
Thanks everyone for your help.

Getting angular to manually bootstrap using grunt and r.js/requirejs

I am trying to integrate requirejs into an existing project, which runs on a node/grunt stack. I want to use the r.js optimizer to concat everything together and noodle through dependencies to do its magic as well.
I am able to get r.js to build a single .js file, and there are no errors... but nothing happens. I set breakpoints in my code, but nothing is getting kicked off - the app never actually runs the bootstrap.js file. I have tried putting the bootstrap.js in an immediate function, and of course it does run then, but the dependencies are not loaded yet (it seems). What am I missing here?
File structure:
app
-> modules
- -> common
- - -> auth.js // contains an auth call that needs to return before I bootstrap angular
-> app.js
-> index.html
config
-> main.js
node_modules
vendor
-> angular/jquery/require/domready/etc
gruntfile.js
gruntfile requirejs task:
requirejs: {
compile: {
options: {
name: 'app',
out: 'build/js/app.js',
baseUrl: 'app',
mainConfigFile: 'config/main.js',
optimize: "none"
}
}
},
main.js config:
require.config({
paths: {
'bootstrap': '../app/bootstrap',
'domReady': '../vendor/requirejs-domready/domReady',
'angular': '../vendor/angular/angular',
'jquery': '../vendor/jquery/jquery.min',
'app': 'app',
'auth': '../app/modules/common/auth',
requireLib: '../vendor/requirejs/require'
},
include: ['domReady', 'requireLib'],
shim: {
'angular': {
exports: 'angular'
}
},
// kick start application
deps: ['bootstrap']
});
app.js:
define([
'angular',
'jquery',
], function (angular, $) {
'use strict';
$( "#container" ).css("visibility","visible");
return angular.module('app');
});
bootstrap.js:
define([
'require',
'angular',
'app',
'jquery',
'auth'
], function (require, angular, app, $, auth) {
var Authentication = auth.getInstance();
.. do auth stuff...
if (Authentication.isAuthorized) {
require(['domReady!'], function (document) {
angular.bootstrap(document, ['app']);
});
}
);
You need to set up a main point of entry into your application; here it would be app.js, but all you are doing in that file is defining that files dependencies and not actually loading any code. You need to change your code to this:
require([
'angular',
'jquery',
], function (angular, $) {
'use strict';
$( "#container" ).css("visibility","visible");
return angular.module('app');
});
See this thread for more details on the difference between define and require.

RequireJS + PhoneGap + Backbone

I'm having problem with RequireJS giving me some 'Uncaught TypeError', all i have is the basic structure and when i test it in ripple i often get this.
Uncaught TypeError: Cannot call method 'local_data_exist' of undefined
Which is a function in one of my library.
Uncaught TypeError: Cannot read property 'jqmNavigator' of undefined
Or this.
So since the errors are not consistant they give me a hard time to debug. Wonder if anyone has face this before.
require.config({
deps:['main'],
baseUrl: "js",
paths:{
// Pointing to Main possible fix for random undefined
app: 'main',
// jQuery
jquery:'libs/jquery/jquery-1.9.1.min',
// jQuery Plugin for Dfp
dfp: 'libs/jquery/jquery.dfp.min',
// jQuery Mobile framework
jqm:'libs/jquery.mobile/jquery.mobile-1.3.0.min',
// Backbone.js library
Backbone:'libs/Backbone/Backbone',
// Backbone.js Adapter
localStorage: 'libs/Backbone/backbone.localStorage',
// RequireJS plugin
text:'libs/require/text',
// RequireJS plugin
domReady:'libs/require/domReady',
// underscore library
underscore:'libs/underscore/underscore',
// jQuery Mobile plugin for Backbone views navigation
jqmNavigator:'libs/jquery.mobile/jqmNavigator',
//TouchSwipe
touchSwipe: 'libs/jquery/jquery.touchSwipe.min',
//Moment (Date Plugin)
moment: 'libs/moment/moment',
// Language plugin
lang: 'libs/moment/langs.min',
// Utility Lib
utils: './utils'
},
shim:{
Backbone:{
deps:['underscore', 'jquery'],
exports:'Backbone'
},
app: {
deps:['jquery','Backbone','underscore'],
exports: "App"
},
underscore:{
exports:'_'
},
dfp : {
deps:['jquery']
},
jqm:{
deps:['jquery', 'jqm-config', 'jqmNavigator']
},
touchSwipe:{
deps:['jquery']
},
lang:{
deps:['moment']
},
utils:{
deps:['jquery']
}
},
waitSeconds: 200,
priority: ['jquery']
});
require(['jquery', 'domReady', 'Backbone', 'jqm-router', 'models/ApiModel', 'jqm', 'underscore', 'jqm-config', 'jqmNavigator'],
function ($, domReady, Backbone, AppRouter, Api) {
domReady(function(){
window.Api = Api;
$(this).router = new AppRouter();
});
}
);

Resources