Require.js can't recognize modules in concatenated JS file - angularjs

I am integrating Require.js to AngularJS based web application for performance improvement.
I've imported require.conf in index.html:
<script src="bower_components/requirejs/require.js" data-main="require-conf.js">
Here is code snippet of require-conf.js:
require.config({
paths: {
'jquery': '...',
'Angular': '...',
....
'libs' : 'src/libs.js'
},
shim: {
'Angular': { exports: 'Angular'},
'libs' : ['Angular']
....
}
}
require(
[
'jquery',
'angular',
'app',
'libs',
], function (jquery, angular) {
angular.bootstrap(['app'])
}
);
Here, libs.js is the library I've built by webpack. Some plugins and libraries are concatenated in this file.
Here is webpack configuration code snippet to build libs.js.
In webpack.config.js
plugins: [
new ConcatPlugin({
fileName: 'libs.js',
filesToConcat: [
'./src/utils/bootstrap-plugins.min.js',
'./src/libs/angular-bootstrap-datetimepicker/datetimepicker.js',
'./src/libs/angular-bootstrap-datetimepicker/datetimepicker.templates.js',
'./src/libs/angular-fusioncharts/fusioncharts.js',
'./src/libs/angular-fusioncharts/angular-fusioncharts.min.js',
'./src/libs/angular-fusioncharts/types/fusioncharts.charts.js',
'./src/libs/angular-ui-tour/angular-ui-tour.js',
'./src/libs/JQ_CONFIG/flot/jquery.flot.js',
'./src/libs/JQ_CONFIG/flot/jquery.flot.pie.js',
'./src/libs/JQ_CONFIG/flot/jquery.flot.resize.js',
'./src/libs/JQ_CONFIG/flot-spline/js/jquery.flot.spline.min.js',
'./src/libs/JQ_CONFIG/flot.orderbars/js/jquery.flot.orderBars.js',
'./src/libs/JQ_CONFIG/flot.tooltip/js/jquery.flot.tooltip.min.js',
'./src/libs/JQ_CONFIG/footable/dist/footable.all.min.js',
'./src/libs/JQ_CONFIG/html5sortable/jquery.sortable.js',
'./src/libs/jquery-ui-draggable/jquery-ui-draggable.min.js',
'./src/libs/ng-table/ng-table.js',
'./src/libs/StickyTableHeaders/jquery.stickytableheaders.js',
'./src/libs/ng-quill/quill.js',
'./src/libs/ng-quill/ng-quill.js',
].map(function(fileName) {
return path.resolve(__dirname, fileName);
}),
But, App can't recognize the modules inside libs.js:
Module 'ngquill' is not available! you either misspelled the module
name or forgot it to load it. If registering a module ensure that you
specify the dependencies as the second argument.
Require.js can't recognize the modules concatenated by webpack? Is there any solutions to fix this problem?

You cannot combine AMD modules into a single file just by concatenating them into a single file. When you combine multiple modules into a single file, the modules must get hardcoded names. When you have a single module in a single file, the define for it can be:
define([ ... deps ... ], function (...) {
In this case, RequireJS infers the name of the module from the name under which it was requested.
When you combine multiple modules in a single file, the define calls must be of the form:
define("foo", [ ... deps ...], function (...) {
define("bar", [ ... deps ...], function (...) {
// etc.
The first argument to define is a string, which tells RequireJS which module is being defined. This is necessary because otherwise RequireJS won't know which module is which. This is why you cannot just concatenate.
You most likely could write a Webpack configuration that could both transform the files as I described above and concatenate them. However, that's rife with hurdles. For instance the runtime shim configuration require special handling at build time. In the end you may end up replicating the functionality of RequireJS' optimizer. I would suggest using RequireJS' optimizer instead of reinventing the wheel.

Related

Can't resolve dependency for module function (restangular.IProvider)

I'm trying to setup restangular for a SPA project but I'm unable to resolve it as a dependency with grunt-tsng. I've installed it using bower (and tsd for typescript definitions from definitelyTyped).
grunt task configuration
tsng: {
options: {
extension: ".ng.ts"
},
dev: {
files: [
// TODO: Automate the generation of this config based on convention
{
src: ["Client/app/**/*.ts", "!**/*.ng.ts"],
dest: "Client/app"
}
]
}
},
Module app.ts
/// <reference path="../../typings/tsd.d.ts" />
module App {
var dependencies = [
"ui.router",
"ui.bootstrap",
"restangular"
...
];
function configuration(
$stateProvider: ng.ui.IStateProvider,
$urlRouterProvider: ng.ui.IUrlRouterProvider,
RestangularProvider: restangular.IProvider, <-- here it fails
...
ts.d.ts (reference file)
/// <reference path="restangular/restangular.d.ts" />
grunt error
Running "tsng:dev" (tsng) task
Warning: Error: Can't resolve dependency for module function App.config with name restangular.IProvider Use --force to continue.
Other dependencies, such as ui-router, ui-bootstrap etc. are working fine. What could be the cause of this? Are there any incompatibilities known with grunt-tsng?
Alright, I figured it out.
When executing grunt-tsng, it scans all dependencies/modules etc. to automagically hook them into angular so you don't have to do it yourself. Here two simple rules apply: Dependencies to inject that are your own (e.g. where you have typescript inplementation code present, such as classes in your modules) can be defined simply with their name. E.g.:
constructor(service: MyModule.IMyService) {}
External dependencies, e.g. from angular itself are declared with the '$' notation. grunt-tsng will assume their dependencies itself are resolved at runtime and thus just hooks them up with their name:
constructor($routeProvider: ng.IRouteService) {}
But now services in restangular aren't named '$restangular', but just 'restangular'. This leads to the following:
When declaring it as 'restangular', grunt-tsng will look for implementations in your modules, doesn't find any and fails
When declaring it as '$restangular', angular will look for providers called '$restangular', doesn't find any and fails
Conclusion:
This is retarded.

Webpack ProvidePlugin vs externals?

I'm exploring the idea of using Webpack with Backbone.js.
I've followed the quick start guide and has a general idea of how Webpack works, but I'm unclear on how to load dependency library like jquery / backbone / underscore.
Should they be loaded externally with <script> or is this something Webpack can handle like RequireJS's shim?
According to the webpack doc: shimming modules, ProvidePlugin and externals seem to be related to this (so is bundle! loader somewhere) but I cannot figure out when to use which.
Thanks
It's both possible: You can include libraries with a <script> (i. e. to use a library from a CDN) or include them into the generated bundle.
If you load it via <script> tag, you can use the externals option to allow to write require(...) in your modules.
Example with library from CDN:
<script src="https://code.jquery.com/jquery-git2.min.js"></script>
// the artifial module "jquery" exports the global var "jQuery"
externals: { jquery: "jQuery" }
// inside any module
var $ = require("jquery");
Example with library included in bundle:
copy `jquery-git2.min.js` to your local filesystem
// make "jquery" resolve to your local copy of the library
// i. e. through the resolve.alias option
resolve: { alias: { jquery: "/path/to/jquery-git2.min.js" } }
// inside any module
var $ = require("jquery");
The ProvidePlugin can map modules to (free) variables. So you could define: "Every time I use the (free) variable xyz inside a module you (webpack) should set xyz to require("abc")."
Example without ProvidePlugin:
// You need to require underscore before you can use it
var _ = require("underscore");
_.size(...);
Example with ProvidePlugin:
plugins: [
new webpack.ProvidePlugin({
"_": "underscore"
})
]
// If you use "_", underscore is automatically required
_.size(...)
Summary:
Library from CDN: Use <script> tag and externals option
Library from filesystem: Include the library in the bundle.
(Maybe modify resolve options to find the library)
externals: Make global vars available as module
ProvidePlugin: Make modules available as free variables inside modules
Something cool to note is that if you use the ProvidePlugin in combination with the externals property it will allow you to have jQuery passed into your webpack module closure without having to explicitly require it. This can be useful for refactoring legacy code with a lot of different files referencing $.
//webpack.config.js
module.exports = {
entry: './index.js',
output: {
filename: '[name].js'
},
externals: {
jquery: 'jQuery'
},
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
})
]
};
now in index.js
console.log(typeof $ === 'function');
will have a compiled output with something like below passed into the webpackBootstrap closure:
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function($) {
console.log(typeof $ === 'function');
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(1)))
/***/ },
/* 1 */
/***/ function(module, exports, __webpack_require__) {
module.exports = jQuery;
/***/ }
/******/ ])
Therefore, you can see that $ is referencing the global/window jQuery from the CDN, but is being passed into the closure. I'm not sure if this is intended functionality or a lucky hack but it seems to work well for my use case.
I know this is an old post but thought it would be useful to mention that the webpack script loader may be useful in this case as well. From the webpack docs:
"script: Executes a JavaScript file once in global context (like in script tag), requires are not parsed."
http://webpack.github.io/docs/list-of-loaders.html
https://github.com/webpack/script-loader
I have found this particularly useful when migrating older build processes that concat JS vendor files and app files together. A word of warning is that the script loader seems only to work through overloading require() and doesn't work as far as I can tell by being specified within a webpack.config file. Although, many argue that overloading require is bad practice, it can be quite useful for concating vendor and app script in one bundle, and at the same time exposing JS Globals that don't have to be shimmed into addition webpack bundles. For example:
require('script!jquery-cookie/jquery.cookie');
require('script!history.js/scripts/bundled-uncompressed/html4+html5/jquery.history');
require('script!momentjs');
require('./scripts/main.js');
This would make $.cookie, History, and moment globally available inside and outside of this bundle, and bundle these vendor libs with the main.js script and all it's required files.
Also, useful with this technique is:
resolve: {
extensions: ["", ".js"],
modulesDirectories: ['node_modules', 'bower_components']
},
plugins: [
new webpack.ResolverPlugin(
new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin("bower.json", ["main"])
)
]
which is using Bower, will look at the main file in each required libraries package.json. In the above example, History.js doesn't have a main file specified, so the path to the file is necessary.

One Backbone.js app using require.js depends on another: how do I include the child?

I wrote a backbone.js app that uses require.js and is broken up with models/, collections/ and so forth. I then wrote another app that depends on the first app (and some other things. The files are laid out like so:
/scripts/appA/
models/
collections/
views/
/scripts/appNeedsA/
models/
collections/
views/
What do I put in the needsA to require appA? The below seems logical to me but doesn't work. If I use ../../appA, that finds appA but IT's dependencies can't be found because the root is wrong.
define(
['underscore', 'backbone', '../appA'],
function (_, Backbone, appA) {
...
}
It might not be the answer you were hoping for but, here's one approach:
https://github.com/busticated/RequireLoadingExample
The idea is that you define your module deps using the path the consuming application will use, then in the consumed app you alias the path appropriately.
In my example, I have a top-level main.js file which pulls in both app1.js and app2.js modules. Both of these modules depend on modules within their own sub-directories - e.g. app1.js uses one/mods/a.js and one/mods/b.js. I have another main (main-one.js) file that lives a level down inside the one/ directory. This file calls:
require.config({
paths: {
'jquery': 'libs/jquery',
'one': '.'
}
});
So now when app1.js loads, the one/mods/a.js path is translated to ./mods/a.js and is found / loaded without issue.
You should be able to fork my repo above and load index.html & one.html in a browser with js console open to see it all work.
Hope it helps!
The proper solution is to:
define(
['underscore', 'backbone', 'appA/views/whatever'],
function (_, Backbone, appAWhateverView) {
...
}
and to set your require.config paths to include:
require.config({
paths: {
appA: '../appA'
}
});

Can you use the 'order!' string in the require.js paths object to avoid repeating it in every dependency array?

With require.js, can you specify the order! plugin to be used within the 'paths' config object? I'm trying to avoid having to type 'order!' for several base modules that I always want included in a certain order.
My config:
require.config({
paths: {
jquery: 'libs/jquery/jquery-1.7.2.min',
underscore: 'libs/underscore/underscore-min',
backbone: 'libs/backbone/backbone-min',
order: 'libs/require/plugins/order'
}
});
I tried it this way, but it included the 'order!' string in the actual path it was looking up.
require.config({
paths: {
jquery: 'order!libs/jquery/jquery-1.7.2.min',
underscore: 'order!libs/underscore/underscore-min',
backbone: 'order!libs/backbone/backbone-min',
order: 'libs/require/plugins/order'
}
});
Check out the AMD forks of Backbone and Underscore, maintained by James Burke who created RequireJS. If you use these versions then you won't have to use the order plugin!
More info about these forks...

proper buildstep for require.js backbone.js with text! plugin

in our require.js and backbone.js app we use many views and templates.
in a buildstep we want to inline the templates with the appropriate view shims and remove the text plugin from the build in the process.
is that even possible?
That is automatically taken care of by RrequireJs when you run their r.js utility.
There's no additional effort involved to 'inline' the templates.
As long as your templates are defined like so:
define ( ['text!templates/my.html'], function(myTemplate){})
Running r.js -o app.build.js will inline all your text!templates/my.html into its own string in your optimized javascript file. Here's a sample of my app.build.js
({
appDir: "../project-directory",
baseUrl: ".",
dir: "../../optimized",
modules: [
{
name: "js/bootstrap"
}
],
paths: {
text: 'js/libs/amd/plugins/text',
order: 'js/libs/amd/plugins/order',
jquery: 'js/libs/jquery-1.7.1',
underscore: 'js/libs/underscore',
backbone: 'js/libs/backbone',
'jquery.mobile.router': 'js/libs/jquery.mobile.router'
},
optimize: 'uglify',
optimizeCss: "standard"
})
RequireJS is smart enough to inline your dependencies prefixed with text!. In other words, the text plugin is only used in your development version. The optimized version will not require the text plugin.
If you're interested in more details, you'll find the instructions to optimize your project on RequireJS's site. You can also find a detailed sample of the build file with all possible options documented here

Resources