Using webpack to process an AMD library with external dependencies - javascript

I have a library written in AMD style that can be used with RequireJS. jquery and jquery-ui are assumed to be provided by the user of the library. Say it looks like this:
// main-lib.js
define(['jquery', './aux-lib.js'], function ($) { ..(1).. });
// aux-lib.js
define(['jquery', 'jquery-ui'], function ($) { ..(2).. });
I'm trying to figure out how webpack works. For example, say I want to bundle these files into a single AMD style library file that still assumes jquery and jquery-ui from the outside:
// out.js
define(['jquery', 'jquery-ui'], function ($) { ..(1)..(2).. } );
How is this accomplished?
When I run webpack with main-lib.js as entry-point, it will complain that it can't find jquery and jquery-ui. If I configure the correct paths with resolve.alias, it bundles jquery and jquery-ui into out.js, which is not what I want. I tried using output.externals to no avail.

This was a pretty simple, stupid mistake on my part. The relevant field is not output.externals, but simply externals. See here. The other two relevant fields introduced there are inside output, but externals is not.
PS: externals can also be an array. Here is my current configuration:
{
entry: './main-lib.js',
output: {
path: './',
filename: 'out.js',
libraryTarget: 'amd'
},
externals: ['jquery', 'jquery-ui']
}
It's working quite nicely.

Related

Issues with require.js and sourcemaps when compiling typescript with grunt-ts

When using grunt-ts and specifying and out file my app runs as expected, but since that option has no support for fast compilation, i tried using a regular compilation where all my .ts files live on the dist folder
There are two issues, first, it will fail to load any imported file, since it will try to look for it without extension. As a quick fix i edited the load fn on the require.js file and all my dependencies load correctly, but then the sourcemaps stopped working, all i get is a blank file on the chrome inspector (and of course i don't want to rely on a dirty hack) .
Please note that i'm not very familiar with requirejs so I'm not quite sure if this is a misconfiguration on my side, a bug, or something that is missing.
My grunt config, related to ts
ts: {
options: {
module: 'amd',
target: 'es5',
experimentalDecorators: true
},
dev: {
src: ['client-app/**/*.ts'],
outDir: "dist",
watch: '.'
}
},
My bootstrap.js which is just the entry point and require.js config file
requirejs.config({
baseUrl: '.',
waitSeconds: 20
});
requirejs(['/init'], function(module) {
module.main()
});
A simplified version of the compiled init file
define(["require", "exports", './section-manager.view'], function (require, exports, section_manager_view_1,) {
"use strict";
function main() {
///
}
exports.main = main;
});
//# sourceMappingURL=init.js.map
And the html
<script src="/js/lib/require.js" data-main="/bootstrap"></script>
Any help is appreciated, thanks
Edit:
Using System.js or #Luis answer solves the loading issue.
The sourcemap issue is solved by using sourceRoot or
inlineSourceMap: true,
inlineSources: true
To the ts options
Don't use an absolute module name. If you do use an absolute name, RequireJS assumes that you do not want any alteration when it generates a path from the module name and will not add the .js extension. You should use a relative path, or you could do this:
requirejs.config({
baseUrl: '/'
});
requirejs(['init'], function(module) {
module.main()
});
By doing this, RequireJS will automatically add the .js extension.

Use jspm to load script that depends on global jQuery

Yes I've read How do I shim a non CommonJS, non AMD package which depends on global jQuery & lodash?.
I'm trying to load X.js, through jspm, which is not a 'package' but an old js file I have no control over that needs a global jQuery object and needs to run like it is in a script tag.
I'm using System.import('app/X'); to load it.
I tried various shim / globals tricks to make it load but I can't quite figure it out.
How would one write the config.js to be able to import that X file so that it sees a global jQuery object? Do I have to make X a 'package' and install it to be able to shim it better?
Thanks.
If you installed jquery through jspm, all you need is to set the meta 'deps' property like this:
System.config({
meta: {
'app/X': {
deps: ['jquery']
}
}
});
System.import('app/X');
Be sure to get the X path correctly and check how jspm sets up System.config 'paths' and 'map', by default trailing .js is added automatically (with paths *.js wildcard) so you must not add it.
Maybe try to look at these links from the documentation as well https://github.com/systemjs/systemjs/blob/master/docs/module-formats.md#globals https://github.com/systemjs/systemjs/blob/master/docs/config-api.md#meta
If providing the meta 'deps' property like the following (as suggested by Mathias Rasmussen) doesn't do the trick,
System.config({
meta: {
'app/X': {
deps: ['jquery']
}
}
});
then you may have to provide a 'globals' meta property like the following:
System.config({
meta: {
'app/X': {
globals: {
'jquery': 'jquery'
}
}
}
});
In order for the above to work you will have needed to install jquery via jspm. Doing the above should also allow you to import the plugin by doing System.import('app/X'); or import 'app/X'; without having to import jquery as well. Importing the plugin alone should also bring in jquery as a dependency.

Using webpack with an existing requirejs application

I am working with an existing application (canvas-lms) that uses RequireJS in its build system. I'm working on a pseudo-standalone application that plugs into Canvas (a "client_app" in Canvas parlance). This is a fontend-only app that makes API calls back to the host Canvas app. The details aren't terribly important for my question - all a client_app needs to do is have a build script that spits out a JS file in a defined place within the Canvas app tree.
I'm trying to use Webpack to build my app instead of RequireJS. Everything works great if I keep all my dependencies self-contained (e.g. npm-install everything I need); however, Canvas already provides many of these dependencies (e.g. React, jQuery), and in jQuery's case, it provides a patched version that I'd like to use instead. This is where I start to have problems.
Getting React to work was easy; Canvas installs it with bower, so I was able to add an alias in my webpack config to point at it:
alias: {
'react': __dirname + '/vendor/canvas/public/javascripts/bower/react/react-with-addons',
}
(__dirname + /vendor/canvas is a symlink in my application tree to the host Canvas application's tree)
Where I'm having trouble is trying to load the provided copy of jQuery.
Canvas has the following jQuery structure:
/public/javascripts/jquery.js:
define(['jquery.instructure_jquery_patches'], function($) {
return $;
});
/public/javascripts/jquery.instructure_jquery_patches.js:
define(['vendor/jquery-1.7.2', 'vendor/jquery.cookie'], function($) {
// does a few things to patch jquery ...
// ...
return $;
});
/public/javascripts/vendor/jquery.cookie.js -- looks like the standard jquery.cookie plugin, wrapped in an AMD define:
define(['vendor/jquery-1.7.2'], function(jQuery) {
jQuery.cookie = function(name, value, options) {
//......
};
});
and finally, /public/javascripts/vendor/jquery-1.7.2.js. Not going to paste it in, since it's bog-standard jQuery1.7.2, except that the AMD define has been made anonymous -- reverting it to the stock behaviour doesn't make a difference.
I want to be able to do something like var $ = require('jquery') or import $ from 'jquery' and have webpack do whatever magic, poorly-documented voodoo it needs to do to use jquery.instructure-jquery-patches.
I've tried adding the path to resolve.root in my webpack.config.js file:
resolve: {
extensions: ['', '.js', '.jsx'],
root: [
__dirname + '/src/js',
__dirname + '/vendor/canvas/public/javascripts'
],
alias: {
'react': 'react/addons',
'react/addons/lib': 'react/../lib'
}
},
This should mean that when I do a require('jquery'), it first finds /public/javascripts/jquery.js, which defines a module with instructure_jquery_patches as a dependency. That falls into instructure_jquery_patches, which defines a module with two dependencies ('vendor/jquery-1.7.2', 'vendor/jquery.cookie').
In my main entry point (index.js), I am importing jQuery (also tried a commonjs require, no difference), and trying to use it:
import React from 'react';
import $ from 'jquery';
$('h1').addClass('foo');
if (__DEV__) {
require('../scss/main.scss');
window.React = window.React || React;
console.log('React: ', React.version);
console.log('jQuery:', $.fn.jquery);
}
Building the bundle with webpack seems to work; there are no errors. When I try to load the page in the browser, though, I'm getting an error from within jquery.instructure-jquery-patches.js:
It would seem that jQuery is not availble within jquery.instructure-jquery-patches.
It is, however, available in the global scope after the page loads, so jQuery is being evaluated and executed.
My guess is that I'm running into some sort of requirejs/amd asynchronicity problem, but that's a shot in the dark. I don't know enough about requirejs or amd to know for sure.
TobiasK's comment pointed me at needing to add amd: { jQuery: true } to my webpack config. Everything is working now.

Got confused about define with dependencies in RequireJS

I know in RequireJS, we can use define() to define a module with dependencies.
The things I got confused is such chunk code in jQuery.
// ready.js
define([
"../core",
"../core/init",
"../deferred"
], function( jQuery ) {
// ...
})
Actually I can not figure out the dependencies with "../core" and "../core/init",
as RequireJS says,
RequireJS also assumes by default that all dependencies are scripts, so it does not expect to see a trailing ".js" suffix on module IDs.
But there isn't a file called ../core.js, just a ../core directory.
So is ../core dependency needless?
You're right, you don't have to specify folders as dependencies, only scripts that you want to load.
// ready.js
define([
"../core/init",
"../deferred"
], function(init, deferred) {
// ...
});
And to say more, RequireJS does not even allow you to mark the entire folder as a dependency.

require.js loads dependencies incorrectly

So this is the setup, my base file is main.js which defines the scripts that are needed on all pages of the site I'm building. It looks like this:
define([
'/javascript/requirePlugins/require-order.js!http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js',
'/javascript/requirePlugins/require-order.js!/javascript/jquery-global-plugins.js',
'/javascript/requirePlugins/require-order.js!/javascript/globals.js'
], function () {
loadFonts();
}
);
It loads jQuery, some plugins and the globals script file. On one page I'm trying to load a jQuery plugin, but the plugin tries to load before jQuery is loaded. It looks like this:
require(['/javascript/requirePlugins/require-order.js!/main','/javascript/requirePlugins/require-order.js!/javascript/3rdparty/lemon-slider-0.2.js'], function () {
$j('#carousel<%= ClientID %>').lemmonSlider({loop:false});
});
The function doesn't appear to be following the order requested. I'm not sure I can even nest ordered functions like this. I've also tried just applying jQuery as a dependency, but this also fails:
require(['/javascript/requirePlugins/require-order.js!/jquery','/javascript/requirePlugins/require-order.js!/javascript/3rdparty/lemon-slider-0.2.js'], function () {
$j('#carousel<%= ClientID %>').lemmonSlider({loop:false});
});
Any suggestions to where I'm doing this wrong is appreciated, thanks
order plugin is removed and you may try shim config to load plugins in order
requirejs.config({
paths: {
'jquery': 'https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min',
'bootstrap': '../bootstrap/js/bootstrap.min',
'select2': 'vendor/select2',
'jshashtable': 'vendor/jshashtable-2.1',
'jquery.numberformatter': 'vendor/jquery.numberformatter-1.2.3.min',
'jq-datepicker': 'vendor/bootstrap-datepicker',
'jq-datepicker.da': 'vendor/bootstrap-datepicker.da'
},
// Use shim for plugins that does not support ADM
shim: {
'bootstrap': ['jquery'],
'select2': ['jquery'],
'jq-datepicker': ['jquery'],
'jshashtable': ['jquery'],
'jquery.numberformatter': ['jquery', 'jshashtable']
},
enforceDefine: true
});
EDIT:
require-jquery is also no more maintaining.
The order plugin is useful if you just have a few top-level scripts you wanted loaded in order and those scripts do not use the module API supported by requirejs. It does not work out so well if you mix it/use it to load modules that do use the define() module API.
In particular, order just makes sure the script gets loaded first. However, the define() API specifies other scripts to load, and the order plugin does not know to wait for those scripts to load.
For this particular problem, I suggest using require-jquery.js as sinsedrix suggested. That, or wrap the scripts you use in define() calls. volo can help you do this with its amdify command:
volo.js amdify path/to/lemon-slider-0.2.js depends=jquery
Also, I would set the baseUrl and use "module naming" for the dependencies instead of full paths. This will allow the optimizer to work correctly. You can also map 'order' to the requirePlugins path, which helps cut down some of the line noise. I would also create a 'jquery' paths entry so that if you do wrap the other plugins in define calls, it will map back to the jquery loaded in your main.js file. So, in the top level script for your page:
requirejs.config({
baseUrl: '/javascript/',
paths: {
order: 'requirePlugins/require-order',
jquery: 'http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min'
}
});
Then your main.js can be written like so:
define([
'order!jquery',
'order!jquery-global-plugins',
'order!globals'
], function () {
loadFonts();
}
);
Note that here, the order usage is fine, as long as those dependencies do not call define() themselves.
But if you are wrapping the scripts you use in define calls, then you can get rid of the order! usage above. Keep the jquery paths config though.
Maybe you should try tu use the require-jquery : http://requirejs.org/docs/jquery.html
Then you won't have to worry about when jquery is loaded.

Categories

Resources