TL;DR My Question
To anyone who has successfully used PouchDB and polyfilled es6 (and es5) for older browsers:
How did you do it?
Which implementation of CommonJS / AMD / RequireJS was used (if that was the approach)?
Also... did you end up with several <script> tags in the index.html file, or all in one?
Many Thanks
Background
I need to use the PouchDB browser database in my web app.
A recommendation at PouchDB.com to deal with a Common Error is
Did you include the es6-promise shim library? Not every browser implements ES6 Promises correctly.
The GitHub README of the recommended 'es6-promise shim library' says
To polyfill the global environment (either in Node or in the browser via CommonJS) use the following code snippet:
require('es6-promise').polyfill();
Alternatively
require('es6-promise/auto');
Note: "...or in the browser via CommonJS "
So, I concluded that I need CommonJS to provide the require function.
One issues posted to the es6-promise shim library GitHub page is
Look for AMD first, then CJS in the UMD #246 which includes
...changes in the es6-promise build since 3.3.0 has introduced a new UMD which checks for a CommonJS env first unlike the old UMD which used to check for an AMD env first.
Which suggests I could use the ("more suited for the browser") AMD.
Reading the top answer to Relation between CommonJS, AMD and RequireJS?:
AMD is more suited for the browser, because it supports asynchronous loading of module dependencies.
RequireJS is an implementation of AMD, while at the same time trying to keep the spirit of CommonJS (mainly in the module identifiers).
So perhaps RequireJS?
Also...
In Common.js -- A CommonJS preprocessor that builds you CommonJS style app into code the browser can use, it says:
Step 3: Include the modules into your app and use them
<script src="js/common.js"></script>
<script src="js/foo.js"></script>
<script src="js/bar.js"></script>
<script>
var bar = require('bar');
bar.doTheThing();
</script>
However, this medium.com article says:
Without module systems like CommonJS, dependancies had to be loaded in <script> tags in the header of an HTML file, OR all code had to be lumped together which is incredibly slow and inefficient for file loading.
These seem to conflict.
Note
Polyfilling es5 seems to have been very straightforward:
Simply include <script src="./es5-shim-master/es5-shim.min.js"></script> in the index.html file. The browser doesn't complain.
Related
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/
}
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
I wonder if there a standard way to bundle native ES modules.
Suppose I have such a "brilliant" project (just for example):
<!-- index.html -->
<script type="module" src="./main.js"></script>
// main.js
import value from "./lib.js"
console.log(value);
// lib.js
export default 'hello world';
Now it needs three requests to load — it's too much, I need only two — .html and .js.
It looks simple at first glance (cat *.js > bundle.js) but there is no syntax for multiple ES modules in one file (as I can see).
I know I can translate ES modules syntax to some other module system (e. g. AMD) and then bundle them but it isn't what I want.
I am curious to accomplish this by native module features only. As simple and handmade as possible.
Is there a way to do this? Maybe at least a proposal?
Thank you.
rollup supports ESM as an output format as well. Your example transpiles to a simple script, but if you have exports in your entry module it will keep them.
rollup, webpack etc natively understand and can bundle ES modules.
Unfortunately my knowledge of JavaScript module loaders is still growing and I'm trying to understand their relationship to the new ES6 Modules. As far as I can tell using a module loader like CommonJS or RequireJS using ES5 compliant JavaScript really needed the use of an asynchronous module loader to increase performance and load only as needed using the respective module loader's syntax.
However looking at the ES6 module documentation and reading other information, it appears to me that module loading is natively supported via the import and export keywords. If this is the case am I correct that ES6 JS modules natively support asynchronous module loading and therefore I do not need to use an additional tool like CommonJS or RequireJS?
it appears to me that module loading is natively supported via the import and export keywords.
Not exactly. The import and export declarations only define the dependencies and the interface of each module. They allow to statically extract strings that name the required modules, nothing else.
If this is the case, do I not need to use an additional tool like CommonJS or RequireJS?
No. You still need to use a loader for ES6 modules, which resolves the names or paths or whatever from the imports to actual module files and loads them with an implementation-dependent method.
There are many tools or toolchains available, examples for the different solutions are
webpack: bundles everything into one large script
System.js: loads single modules dynamically and asynchronously (similar to what require.js does)
native: node.js and web browsers are still figuring out how to support module loading without additional libraries
babel transpilation: you can convert ES6 modules to AMD or CommonJS format and use the known tools like require.js for these
As far as my understanding goes, ES6 supports the syntax for defining and importing modules. The actual act of importing the modules that are required are a job of the infrastructure.
In modern browsers (as of 2016 that is) do not have built in functionality to support module loading and as such you will still need something like SystemJS to do the actual loading.
ES6 JavaScript Files are inherently treated as a module. if you define anything in a .js file, it will only be visible within that file (local scope ). what export does is, it exposes the classes / variables defined as export, visible to outside. then you can import it to a another module. There are other ways to define modules such as using Commonjs or AMD etc.. . Module loaders are required if you want to dynamically lazy load modules. ex. Systemjs is a such a Dynamic Module loader. it will fetch the physical module file from server dynamically when it is requested, and will prevent having multiple loads the same file. in SPA application in past had to load everything at the beginning to it to work. with dynamic module loaders now we can have only the files we need to do the intended job. hope this will help you.
https://github.com/systemjs/systemjs
I am going to use RequireJS to take advantage of AMD.
In RequireJS configuration, I found a shim block to define shim JavaScript libraries.
Can anyone please tell me the difference between AMD JavaScript libraries and shim libraries?
A RequireJs module is one that follows a very specific set of rules about code -- mostly having to do with what modules are required (modules can require other modules) and what function or methods it exports.
A shim is a block of code used with non-ADM libraries to provide a set of hints to RequireJS that allows one to use non-AMD libraries to be loaded.
EDIT
In short, AMD modules start like this:
define(
module_id /*optional*/,
[dependencies] /*optional*/,
definition function /*function for instantiating the module or object*/
);
There are some systems that use a weird wrapper to detect if AMD is available and if so they use it, else they turn define into a glorified eval statement.
Anything else is not an AMD module and either needs to be loaded normally or shimmed.