browserify module loading into function - javascript

i have an app.js which is the main file for my webapplication. It initialize the UI and loads different modules. At the moment it is one big file with all the code. I want to refactor this and split the functions into independant modules and then bundle them with browserify. App.js has a dependency to jquery and some modules too.
i have defined the browsersection in package.json
"browser": {
"jquery": "./node_modules/jquery/dist/jquery.js",
"jquery-ui": "./static/js/vendor/jquery-ui-1.12.1/jquery-ui.min.js",
"App": "./static/js/app.js",
"myModule": "./static/js/myModule.js"
}
Also i use browserify-shim to load some jquery plugins which can't be loaded without.
For browserify-shim i've defined a config section:
"browserify": {
"transform": [
"browserify-shim"
]
},
"browserify-shim": {
"jquery": "$",
"jquery-ui": {
"depends": [
"jquery:$"
]
},
"bootstrap": {
"depends": [
"jquery:$"
]
},
"dropdown": {
"depends": [
"jquery:$"
]
},
"App": {
"exports": "App",
"depends": [
"jquery:$"
]
},
}
My entry.js file
var $ = require("jquery");
window.$ = window.jQuery = jQuery;
var App = require('./app.js');
require('./some jquery plugins');
In my app.js
var App = function(){
some code...
var myModule = require('./myModule.js');
myModule();
some code...
return {
init: function() {
someFunctions();
someFunctions();
}
}
}();
$(function(){ App.init(); });
In my myModule.js
module.exports = function() {
some code with jquery...
};
It bundles correctly but if i load the page i'll get
app.js:14Uncaught Error: Cannot find module './myModule.js'
And if i look in the sources in my browser it tells me:
var myModule = __browserify_shim_require__('./myModule.js');
so it seems browserify can't load the module correctly in this scope.
What am i doing wrong? Does i forget something? I think i don't need to shim my own module because i can use commonjs format for browserify. Do i have to use another pattern in this context?
Thanks for help in advance!
Edit:
I don't have a solution yet. i refactored the whole thing and now i don't need browserify shim anymore. You can add most plugins without it.

Related

Exported TypeScript Class not included in WebPack bundle if not used directly

I'm converting a javascript project with Angular 1.x to WebPack and TypeScript (using ts-loader). I got it mostly working, but I'm running into trouble when ts-loader seems to be optimizing my scripts out of the bundle when the exports are not directly used.
Here's a sample project demonstrating the issue (npm install, webpack, then load index.html and watch the console).
https://github.com/bbottema/webpack-typescript
The logging from ClassA is showing up, but angular is reporting ClassB missing (provider). If you look in bundle.js you'll notice ClassB missing entirely. The difference is ClassA begin use directly after importing, and ClassB is only referenced by type for compilation.
Is it a bug, or is there a way to force ClassB to be included? Or am I going about it wrong? Angular 2 would probably solve this issue, but that's a step too large right now.
Relevant scripts from the project above:
package.json
{
"devDependencies": {
"typescript": "^1.7.5",
"ts-loader": "^0.8.1"
},
"dependencies": {
"angular": "1.4.9"
}
}
webpack.config.js
var path = require('path');
module.exports = {
entry: {
app: './src/entry.ts'
},
output: {
filename: './dist/bundle.js'
},
resolve: {
root: [
path.resolve('./src/my_modules'),
path.resolve('node_modules')
],
extensions: ['', '.ts', '.js']
},
module: {
loaders: [{
test: /\.tsx?$/,
loader: 'ts-loader'
}]
}
};
tsconfig.json
{
"compilerOptions": {
"target": "es5",
"module": "commonjs"
},
"exclude": [
"node_modules"
]
}
index.html
<!doctype html>
<html ng-app="myApp">
<body>
<script src="dist/bundle.js"></script>
</body>
</html>
entry.js
declare var require: any;
'use strict';
import ClassA = require('ClassA');
import ClassB = require('ClassB');
var a:ClassA = new ClassA(); // direct use, this works
var angular = require('angular');
angular.module('myApp', []).
// this compiles as it should, but in runtime the provider will not be packaged and angular will throw an error
run(function(myProvider: ClassB) {
}
);
ClassA.ts
// this line will be logged just fine
console.log('ClassA.ts: if you see this, then ClassA.ts was packaged properly');
class ClassA {
}
export = ClassA;
ClassB.ts
declare var require: any;
// this line is never logged
console.log('ClassB.ts: if you see this, then ClassB.ts was packaged properly');
class ClassB {
}
var angular = require(angular);
angular.module('myApp').service(new ClassB());
export = ClassB;
Turns out you have to signal WebPack to explicitly include a module by adding an extra require call without import statement.
I'm not ready to mangle my .ts files by adding duplicate imports, so I made a generic solution for that using the preprocessor loader:
{
"line": false,
"file": true,
"callbacks": {
"fileName": "all",
"scope": "line",
"callback": "(function fixTs(line, fileName, lineNumber) { return line.replace(/^(import.*(require\\(.*?\\)))/g, '$2;$1'); })"
}]
}
As a proof of concept, this regex version is very limited it only support the following format:
import ClassA = require('ClassA');
// becomes
require('ClassA');import ClassA = require('ClassA');
But it works for me. Similarly, I'm adding the require shim:
{
"fileName": "all",
"scope": "source",
"callback": "(function fixTs(source, fileName) { return 'declare var require: any;' + source; })"
}
I made a sample project with this solution.

RequireJs bundling with modules

I recently saw an example of how requirejs bundles work(http://vimeo.com/97519516) and got excited. So I'm trying to get the bundling feature to work with my setup and am banging my head on the wall.
Troubleshooting done so far.
The main.js file will be created properly without bundling and defining the comp1 component in the main.js top depenendecy. But when I nest the comp1 dependency and add the bundling options the page1 and page2 bundle never gets created. So it doesn't seem to be an issue with the rest of the configuration. I have also tried this a few different ways. Removing the module and and putting everything in the root of the config. Moving the bundles to the all-config.js. Any ideas on what I could be doing wrong.
all-config.js
(function() {
if (typeof requirejs != 'function') {
requirejs = function(config) { requirejs = config; };
}
requirejs({
pathes: {
comp1: "path1",
comp2: "path2,
router: "path3"
}
});
build.json
{
"baseUrl": "./",
"appDir": "../src/main/webapp",
"dir": "build",
"mainConfigFile": "app/all-config.js",
modules: [
{
"include": [
"app/lib/require/require.js"
],
bundles":{
"page1" : ["comp1"],
"page2" : ["comp2"]
}
}
]
}
main.js
require(['jquery', 'router'],function($){
//load app
require(["comp1"],function(comp1){
var app = new comp1();
});
});
Gruntfile
module.exports = function(grunt){
var stripper = require('strip-json-comments');
var buildOptionsFile = grunt.file.read( 'build.json' );
var buildOptions = JSON.parse(stripper(buildOptionsFile) );
grunt.initConfig({
requirejs: {
compile: {
options: buildOptions
}
}
});
.....
};
Any ideas on how to get this to work?

Grunt build not exposing the globals I need

When I run my project locally with my grunt:server task, the project works as I expect. However, after building which takes all the vendor code and puts it into one file, two of my needed module aren't avialable, and the project doesn't work.
Here is my requirejs configuration:
requirejs.config
baseUrl: './js'
shim:
'underscore':
exports: '_'
'backbone':
deps: ['underscore', 'jquery']
exports: 'Backbone'
'stack':
deps: ['d3.global']
exports: 'stack'
'highlight':
exports: 'hljs'
paths:
'underscore': '../components/underscore/underscore'
'backbone': '../components/backbone/backbone'
'jquery': '../components/jquery/jquery'
'd3': '../components/d3/d3'
'd3.global': '../components/d3.global/d3.global'
'stack': '../components/stack/stack'
'highlight': '../components/highlightjs/highlight.pack'
require ['app/vendors'],->
console.log("Backbone", Backbone)
console.log("_", _)
console.log("$", $)
console.log("d3", d3)
console.log("stack", stack)
console.log("hljs", hljs)
app/vendors looks like
define [
'underscore'
'jquery'
'backbone'
'text'
'd3.global'
'stack'
'highlight'
], ->
When I run the project locally via grunt, I see all the globals printed out. However, when I build the project, Backbone Underscore and JQuery print out, while stack fails (hljs is also not available, and if I remove stack from app/vendors, it doesn't fix highlight, so its probably not an order thing).
the requirejs optimizer is called with the following configuration:
requirejs:
compile:
options:
baseUrl: 'js/'
appDir: './<%= yeoman.tmp_dist %>/'
dir: './<%= yeoman.dist %>/'
wrap: true
removeCombined: true
keepBuildDir: true
inlineText: true
mainConfigFile: '<%= yeoman.tmp_dist %>/js/main.js'
# no minification, is done by the min task
optimize: "none"
modules: [
{ name: 'app/vendors', exclude: [] }
{ name: 'app/app', exclude: ['app/vendors'] }
{ name: 'main', exclude: ['app/app', 'app/vendors'] }
Could there be something wrong with the stack and highlight files that I need to fix in order to make requirejs optimization and uglify work with them?
I installed highlightjs via bower by adding "highlightjs": "~8.0" to my bower.json file and running bower install. I downloaded stack.js from mbostock's stack project. I'm using v0 at the moment, with minor changes to make it work in this project. The source for all these are in the components directory of my github project.
BOUNTY If anyone is willing to clone the repo themselves, and try running the project with grunt server and grunt build to help me track down the problem, I'd greatly appreciate it. At the moment I have the vendor scripts in the github repo itself, so all you should need is compass and bower to run it.
This is due to wrap: true in the r.js config. Here's a simple configuration that isolates the issue:
main.js
define([ 'legacy' ], function(legacy) {
var greeting = 'hi';
console.log(greeting, legacy.foo);
});
legacy.js
var globalThing = { foo: 1, bar: 2 };
build.json
{
"name": "main",
"optimize": "none",
"out": "main-built.js",
"shim": { "legacy": { "exports": "globalThing" } },
"wrap": true
}
Let's run r.js (r.js -o build.json) and consider the result (formatted by me):
(function() { // this immediately-invoked function expression (IIFE)
// is here because r.js has "wrap: true" in the config
var globalThing = { foo: 1, bar: 2 };
// code generated from the "shim" entry in the config
define('legacy', function(global) {
return function() {
var ret, fn;
// since global.globalThing is undefined,
// that's where it goes wrong
return ret || global.globalThing;
};
}(this));
define('main', [ 'legacy' ], function(legacy) {
var greeting = 'hi';
console.log(greeting, legacy.foo);
});
})(); // end of the IIFE
As you can see from the code above, globalThing isn't global any more. The same happens with the stack and highlight libraries in your project as they use var and function declarations to define their globals.
To tackle this issue, we have a couple of options. The first is to consider whether you really need wrap: true in the config. If you drop it, the globals will get global again and everything should start working as expected. The second option is to try adding wrapShim: true to the config. You can read about nuances of using this option here. If we try it with our sample configuration, we'll get something like this:
(function() {
(function(root) {
define('legacy', [], function() {
return function() {
var globalThing = { foo: 1, bar: 2 };
return root.globalThing = globalThing;
}.apply(root, arguments);
});
})(this);
define('main', [ 'legacy' ], function(legacy) {
var greeting = 'hi';
console.log(greeting, legacy.foo);
});
})();
Looks good to me.

RequireJS: how to dynamically define/add packages

I've got a config for requirejs that is shared with multiple applications. When the app is loaded, the app receives an object with the layout of the app (and various other params). I want to use that object to define modules/packages for the app.
siteMap = { modules: { "foo": { … }, "bar": { … }, … }, other: "stuff" }
/shared/
libs/
bootstrap.js
jquery.js
…
app.js
/$appName/
foo/
index.html
edit.html
main.js
bar/
index.html
stuff.html
main.js
…
I imagine there must be a way to add packages that goes something like this:
// /shared/app.js
require.config({
paths: {
"bootstrap": ["//hosted.bootstrap.js","/shared/libs/bootstrap.js"],
"jquery": ["//hosted.jquery.js","/shared/libs/jquery.js"],
"siteMap": "//appName.example.com/api/siteMap"
},
…
});
require(['jquery','siteMap','bootstrap'], function($,siteMap) {
for ( var module in siteMap.modules )
{
require.config.packages[module] = siteMap.modules[module];
// OR
require.addPackage(siteMap.modules[module]);
}
});
You could nest another require() call to load the additional modules after siteMap tells you what should be loaded. Something like this:
require(['jquery','siteMap','bootstrap'], function($,siteMap) {
// .map assumes modern browser or use of something like es5-shim
var modulesToLoad = siteMap.modules.map(function(m) { return m + '/main'; } );
require(modulesToLoad, function() {
// kick off the rest of app logic
});
});

Require.js with jQueryMobile-Router

I load require.js with jQuery included like this in my html:
<script data-main="requires" src="lib/require-jquery.js"></script>
The content of my requires.js:
require.config( {
paths: {
"jquery.mobile": "lib/jquery.mobile",
"jquery.mobile.router": "lib/jquery.mobile.router"
},
shim: {
"jquery.mobile" : {
"exports": "$.mobile"
},
"jquery.mobile.router": {
"deps": [ "jquery.mobile" ],
"exports": "$.mobile.Router"
}
}
} );
require(["jquery.mobile.router" ], function() {
require(["router"]);
} );
And in my router.js i create a new instance of the jquery mobile router plugin:
router = new $.mobile.Router(...);
Which gives me this error:
Uncaught TypeError: undefined is not a function
When I output $ and $.mobile, they are both defined, just $.mobile.Router is undefined.
What have I done wrong here?
My Problem was that I added jquery.mobile as a dependency for jquery.mobile.router, thus jQuery mobile will be loaded first, where as the documentation for the router states this:
The jQuery Mobile router javascript file must be loaded before jQuery Mobile.
This is how I changed my requires.js to fix the problem:
require.config( {
paths: {
"jquery.mobile": "lib/jquery.mobile",
"jquery.mobile.router": "lib/jquery.mobile.router"
},
shim: {
"router": {
"deps" : ["jquery.mobile"]
},
"jquery.mobile" : {
"deps" : [ "jquery.mobile.router"],
"exports": "$.mobile"
},
"jquery.mobile.router": {
"exports": "$.mobile.Router"
}
}
});
require(["router"]);
Now I just require my router.js and load jquery.mobile and jquery.mobile.router as dependencies. Load order now is this:
jquery.mobile.router
jquery.mobile
router
Try this in your router.js file:-
define(["jquery", "jquery.mobile.router"], function($) {
// your js code in router.js
} );
By specifying jquery in your define call and passing in $ as an argument, the jquery object $ and associated functions defined in jquery.mobile.router are now made available in the scope of your code (contained in the file router.js in its entirety).

Categories