How can I integrate custom module loading with webpack? - javascript

I'm using Webpack 1 for a frontend project and I have a legacy internal Javascript library which uses its own module system similar to AMD. A module is defined with a code similar to this:
MyLib.define('module id', ['my', 'module', 'deps'], function (my, module, deps) { /* module code */});
Then you can use the modules with a code like this:
MyLib.require(['dep1', 'dep2'], function (dep1, dep2) {});
I would like to be able to use MyLib along with all the other CommonJS modules I'm already using. Can webpack support this somehow?

As far as I know, Webpack doesn't support custom module formats (if someone else knows otherwise, I'd love to be proven wrong, though).
You perhaps could work around this by writing a Webpack loader or a Babel plugin that converts the syntax to one of the module formats that Webpack supports. The latter is what Babel used to do for ES6 imports before Webpack supported them out of the box - it'd just transform them into CommonJS require calls.

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/
}

What is module option in tsconfig used for?

I am trying to understand the typescript module compiler option.
I went through typescript documentation - docs
It says module option is to Specify module code generation.
What does that mean?
Does it mean if I put module option as commonjs, then the compiler compiles the code to commonjs? But then we have options like esnext, es16. After I went through Docs: Difference between esnext, es6, es2015 module targets, I understood that import() expressions are understood in esnext. Anyway the purpose of compiler is to compile the code into browser understandable syntax(commonjs). So compiling code to the module type given doesn't make sense.
So does it mean the module type you give tells the compiler in what syntax the code is written? Meaning from which code it has to compile it to commonjs? But then we have module type commonjs which is frequently used but we almost never write code in pure commonjs syntax.
what is the purpose of tsconfig.json? stackoverflow answer says module specifies module manager. I don't understand what that means.
I also went through Understanding “target” and “module” in tsconfig and tsconfig module options - does 'System' refer to SystemJS?.
None of these seem to answer my question properly.
tsconfig.json
{
"compilerOptions: {
"module": "esnext"
}
}
TLDR; module in tsconfig.json tells the compiler what syntax to use for the modules in the emitted .js files. Frequently used values are "commonjs" (require/module.exports) or "ES2015" (import/export keywords), but there are other module systems. module affects the module syntax of emitted code while target affects the rest.
What does Specify module code generation mean?
"module" in tsconfig.json tells the Typescript (TS) compiler what module syntax
to use when the files are compiled to Javascript (JS).
When you set "module" to "commonjs" in tsconfig.json, this means that the modules in the compiled .js files will use the commonJS (CJS) syntax, so var x = require(...) and module.exports = {...} to import and export.
If you changed "module" to "ES2015" for example, the compiled code would use the import and export keywords used in ES2015 module syntax. For an overview of the other syntaxes you can take a look here.
There are several different module systems with CJS and the
native ES Module (ESM) format probably being the ones most widely used.
What to choose depends on your requirements. If it's for a server-side project
that uses Node.js then probably CJS, if it's for an Angular front-end application
than perhaps ESM (or their own NgModules but that's going beyond scope here).
A somewhat similar situation is library and package designs and how you would
like to expose them to consumers. This depends on what sort of users are going to consume
the code, what are they working with (browser, Node) and which of the module systems
is best suited for the job?
ES Modules are now the built-in standard for importing/exporting modules in JS but back when there was no native solution other module systems were designed: This is why we also have CJS, AMD and UMD modules around. They are not all obsolete, CJS is still used a lot in Node.js and the AMD module loader for example allows non-JS imports which can be useful in some cases.
Nowadays, all the modern browsers and Node 13.2.0+ support the ESM format (see this page for compatibility data and more background on modules).
But then we have options like esnext
Newer JS versions sometimes contain more features for module import/export.
setting "module" to "ESNext" enables support for these features which often are not added to official specifications yet. Such as the import(...) expression which is a dynamic import.
Does it mean if I put module option as commonjs, then the compiler compiles the code to commonjs?
The "module" setting does not affect the rest of the code, "target" is used for that instead and specifies what JS version the outputs should be compatible with.
This has been explained in other threads, I'm just adding it here for clarity.
Say you want to use require(...) and module.exports = {...} in a Node project but also want the code to utilise ES2015 features like let and const in the code (for readability/performance/other reasons).
In this case you would set "module" to "commonjs" and "target" to "ES2015" in tsconfig.
Anyway the purpose of compiler is to compile the code into browser understandable syntax(commonjs).
Yes, the compiler has to turn TS code into JS that a browser understands.
However, JS is no longer limited to browsers, Node runs in other environments (servers) for example. CJS was in fact intended as a server-side module format while AMD modules were/are used for browser imports/exports.
So does it mean the module type you give tells the compiler in what syntax the code is written?
It tells the compiler in what syntax to write the modules in the output .js files

Best way of including commonjs modules in a project using requirejs

I using requirejs to manage the javascript files in my project. However, there are some external libraries I want to use that do not adhere to the AMD format. A library I want to include is barba.js. How would this be done using the package loading feature of requirejs? Ideally I want to include a commonjs module without running a conversion tool.
Barba doesn't use the CommonJS module format.
Barba uses the UMD (Universal Module Definition) module format. This means that it is compatible with both the AMD module loading (as used by RequireJS) and CommonJS module loading (as used by Node.js).
So, that means you can just include Barba - or any other module in UMD format - with RequireJS the same way you include an AMD module:
define([
"barba/barba"
], function(Barba) {
Barba.Pjax.start(); // You can use Barba here
});
From requireJS doc.
define(function(require, exports, module) {
//Put traditional CommonJS module content here
});
This should make everything alright for you. I'm really not sure if it's needed though.

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.

exports=>false in imports-loader in Webpack

If I use imports-loader, what does it mean exports=>false part in configuration? It should inject variable var exports = false, but I don't know when and why I need this variable.
module : {
loaders : [
{
test : /eonasdan-bootstrap-datetimepicker/,
loader : 'imports?define=>false,exports=>false,moment=moment'
}]
}
Imports is for shimming third party code that is expecting global to be defined such as jQuery $ or AMD's define. The reason you might want to do this is because module bundlers often bundle to formats that both AMD and CommonJS understand aka universal module definition UMD format. When importing a UMD module it will first check to see if define (AMD) exists and then exports (CommonJS). Webpack has an easier time parsing CommonJS (nodes native format) so setting define to false explicitly tells webpack to not parse it as an AMD module.
UPDATE
It seems like they are likely disabling all module exports and defining moment as the moment js library. I would guess that the code in that library is extending the bootstrap datepicker control with features from moment.

Categories

Resources