Module loading order in requirejs - javascript

We ran into an interesting problem in an existing project,
We're using requireJS
All our modules are AMD compliant(and we have a lot of them)
We need to include a subset of babel-polyfill as an AMD module to the project.
It's not possible to manually add this dependency to all modules one by one
Our code is optimized and bundled using r.js
Our main file looks something like this : // main.js
require(
[
'router',
'someOtherModule', /* In reality we have quite a few more here */
], function(Router, AppModule) {/*... app code ...*/}
)
So we'd like to load this polyfill module before any other module is loaded in main.js
What won't work :
Shim - Shim is used for non AMD module.
Adding the polyfill to the list above router - in the minified code that r.js spits out, there's no guarantee that polyfill will actually be in the code before router, it's not the defined behavior and thus cannot be counted on.
Wrapping everything with another require['polyfill'] call seems to screw up the r.js optimizer, it won't seem the bundle together the other modules if they're wrapped in require[].
Since the polyfill is an AMD module, we can't just include it in the <HEAD>
Option 3 is still something we're investigating to see if it's possible.
So the question is - Is what we're trying to do possible?

Whenever I load polyfills I always always always load them in a script element in head. I never load them through RequireJS. Loading them through RequireJS gives rise to all kinds of problems, as you discovered. You wrote:
Since the polyfill is an AMD module, we can't just include it in the <HEAD>.
The babel-polyfill package builds itself into a script that can be loaded through a script element by using Browserify:
#!/bin/sh
set -ex
BROWSERIFY_CMD="../../node_modules/browserify/bin/cmd.js"
UGLIFY_CMD="../../node_modules/uglify-js/bin/uglifyjs"
mkdir -p dist
node $BROWSERIFY_CMD lib/index.js \
--insert-global-vars 'global' \
--plugin bundle-collapser/plugin \
--plugin derequire/plugin \
>dist/polyfill.js
node $UGLIFY_CMD dist/polyfill.js \
--compress keep_fnames,keep_fargs,warnings=false \
--mangle keep_fnames \
>dist/polyfill.min.js
If you inspect dist/polyfill.js, you'll see it is not an AMD module. (The define function it calls is not AMD's define function.)
You should be able to adapt this method to your whatever subset of babel-polyfill you use.

Related

Why do some minified js files contain calls to "require" function

I've been modernizing some old gulp config where js files are concatenated and then minified by migrating to webpack.
Some of bundles contained libraries such as moment.js and isotope-docs.min.js,
When bundling with webpack I would get error that specific file or path is not found.
For example looking at moment.js
There is require("./locale/"+t) which causes my webpack to fail since i dont have locale directory.
Why would bundled js file have require function when browsers dont understand that?
Before ES modules became a thing, JavaScript did not have an official module syntax. Also, developers wanted to write a library once for both Node.js and the browser. The closest thing available was Node.js's require(), which does not exist on the browser.
So what tools like Browserify and Rollup would do is polyfill an implementation of require() (e.g. wrap the code in a "UMD"). This way, the module worked on any plaform and require() calls work as if in Node.js (its implementation may vary and can be extended because dealing with the filesystem is very different from dealing with a network).
Found a fix, you can just add to webpack confing under module noParse e.g
webpack.config.js
module: {
rules: [ ... ]
noParse: /moment.min.js|isotope-docs.min.js/
}

import requirejs amd module with webpack

I'm using Converse.js and it's prebuilt into the RequireJS/AMD syntax. Including the file from a CDN you could use it like require(['converse'], function (converse) { /* .. */ }). How is it possible to use this with webpack? I would like to bundle converse.js with my webpack output.
I have the file on disk, and want to import it like
import converse from './converse.js';
converse.initialize({ .. });
Webpack picks up the file and bundles it correctly, although it's not useable yet as it throws 'initialize is not a function'. What am I missing?
I suspect the way their bundle is built will not work correctly with how Webpack evaluates modules in a limited context.
From their builds, taking the built AMD module via NPM without dependencies should be parsable by Webpack and it will enable you to provide the dependencies to avoid dupes in the final output.
If all else fails, using the script-loader will evaluate the script in the global context and you'd get the same experience as if you'd have followed their usage guidelines to reference it from a CDN, just don't forget to configure the globals for your linter.

RequireJS tries to load webpack externals

Using Webpack, I'm exporting two distribution files for a project. One with dependencies bundled and one without.
Throughout the application, we use commonjs require('lodash.assign'); includes which webpack understands.
However, I've configured one of our builds to ignore these (for users who already use lodash and have it available).
externals = {
'lodash.assign': 'var _.assign',
'lodash.clonedeep': 'var _.cloneDeep'
};
This works as expected. However, because our libraryTarget is umd we support RequireJS. However, RequireJS still actually thinks these files should be loaded and I'm seeing a ton of errors:
require.min.js:1 GET http://127.0.0.1/_.assign.js req.load
# Uncaught Error: Script error for "_.assign"
It can't figure out where to find script. How can I configure webpack, or even requirejs to ignore these, since they're being mapped by webpack?
By looking at the compiled output, I see the reason requirejs thinks it's supposed to load these files:
define(["_.assign", "_.cloneDeep"], factory);
When I manually modify code to the following, it works:
define(['lodash'], factory);

Predefining AMD module dependencies in RequireJS config

For the sake of loading times, I'm interested in predefining all AMD module dependencies. This is because at the moment, the module file must load before require.js can work out its dependencies. Here's an illustration to show what I mean:
Is there any way to do this with require.js? I know you can define dependencies for the shimmed modules, but can you do this for your own custom AMD modules?
You are looking for something you can put in the configuration you pass to RequireJS that would do what you want. There is no analogue to shim for modules that call define. However, what you could do is add the deps option to your configuration:
deps: ['module', 'dep1', 'dep2', 'dep3']
This will tell RequireJS to start loading immediately your module and the dependencies. You will have to maintain this list yourself but this would be true with shim too.
Otherwise, you can do what kryger suggested in a comment: use r.js to build module into a single bundle that contains it and all of its dependencies. Whenever module is loaded, all of its dependencies are loaded at the same time. This is more efficient than using deps but might make things slightly more complicated if you ever need to load any of the dependencies by themselves. You'd have to use the runtime option bundles to tell RequireJS where these modules are. And just like deps you'd have to maintain this list yourself.

Module definition to work with node.js, require.js and with plain scripttags too

I am working on a javascript module/library that should work in 3 environments:
in node.js
in requirejs
when simply included using tags into the webpage. In this case the whole module should be hooked up under window.myModule
Do you have any suggestions as to how to write the structure of the library so that it works in all these environments?
EDIT: basically I mean some sort of wrapper code around the library so that I can call the file form any of those three methods and I'm fine...
This requirement and its solution is known as Universal Module Definition (UMD). It is currently a draft proposal. Background and current status is described in Addy Osmani - Writing Modular JavaScript With AMD, CommonJS & ES Harmony article. Look for "UMD" link pointing to various templates you can use.
Quite many other templates can be found on the web - UMD is the search keyword.
(did not find the final link myself yet :)
We're working on the same thing, I think.
And we have some success. We have library (we call it 'slib'), compiled to AMD js files. It does not depend on npm modules or browser, so it can be called from node and from browser.
1) To call it from node, we use requirejs:
file require.conf.js
module.exports = function(nodeRequire){
global.requirejs = require('requirejs');
requirejs.config({
baseUrl: __dirname+"/../web/slib/",
paths: {
slib: "."
},
nodeRequire: nodeRequire
});
}
In any other serverside (nodejs) file we add this line at the beginning
require("./require.conf")(require);
then we call slib's code by:
var Computation = requirejs("slib/Computation");
2) To call slib from browser, we just use requirejs. It handles everything fine.
3) We do not need to call slib from < script > directly.
For production, we use r.js to make a bundle js file with most of dependencies and use it on the page with one < script >. And this script downloads all other deps, if they are not included, using standard requirejs and it does not need requirejs (as far as I remember), it just works alone. This is very flexible for large projects: use requirejs while development, use r.js to bundle core files in production to speed up page load, use whole bundle if you need only one < script > without any other requests. r.js bundles all dependencies correctly, including old js libraries, which were commonly loading using only < script > and accessible by window.myOldLibrary using shim param on config.
It seems you can use browserfy to make some npm modules accessible from slib's code, but we did not tried yet.
Also, using requirejs on node's side, I think, can be simpler (why we need second 'requirejs' function together with node's one?) We just have not investigated it well, but this works.
In any slib module you can write
if (window)
window.module1 = this // or whatever
and it will be exported as old js lib upon load

Categories

Resources