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.
Related
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.
If I use target:"es5" to transpile TypeScript code that uses es6-style classes & React, my intended entry point (here a file called App.ts) has transpiled code that looks like this:
Object.defineProperty(exports, "__esModule", { value: true });
var react_1 = require("react");
var react_router_1 = require("react-router");
When that first line in this snippet gets hit, I get an Uncaught ReferenceError: exports is not defined error. A similar issue is described on TypeScript's GitHub Issues pages here.
The quick answer is that exports will exist in an environment that supports class loading.
commonjs means there's an exports variable available. You can't run commonjs code directly in a browser.
Here's my html wrapper:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello World</title>
<script src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<script src="./components/App.js"></script>
</head>
<body>
<div id="root"></div>
<script>
ReactDOM.render(
React.createElement('App', null),
document.getElementById('root')
);
</script>
</body>
</html>
It would appear (1, 2, 3) that the usual response is to put an additional transpilation step between your TypeScript and its release to a client on a browser. Wait, what? I need to transpile the transpiled code? This kinda smells like one of those "I thought the dishwasher washed the dishes" commercials.
Another recommendation is to use a module loading library like RequireJS or SystemJS (1, 2). That makes sense, but I haven't seen an example of adding a module loading lib to TypeScript compiled code for delivery to a browser. And, again, it seems like TypeScript could've intelligently (as in "with minimally duplicated code") included such a lib when it was transpiling.
(It also seems like RequireJS is at least conventionally used with a different syntax with require where you require(['lib1', 'lib2', 'lib3'], function (lib1, lib2, lib3) {...}}) rather than the simpler var lib1 = require('lib1') TypeScript is producing, though perhaps I'm missing something?)
So at least two related questions here:
What's the expected use case for the target="es5" TypeScript transpiled code?
How does one deliver es6 TypeScript code to the browser without transpilation steps beyond what TypeScript can do?
That is, is there a platform:"browser"-style setting I've missed?
If not, should RequireJS work with the code TS has produced? How should that be "bootstrapped"/started?
UPDATE: (Moving my comments from #felixmosh's answer into the question)
I have tried adding "module":"es2015" to my tsconfig.json, but the code started lighting up with errors on lines like this: import Home from './pages/Home'; that read error TS2307: Cannot find module './pages/Home'.
My impression was that the module option defined the way modules are defined in the TypeScript rather than how they'll be transpiled in the resultant JavaScript code. That is, if this option only affected output, why would my untranspiled code throw errors once the module setting is added/changed rather than simply change the resulting JavaScript files' syntax?
#felixmosh also [helpfully] suggests using something like RollUp.js, but that does an end run around loading modules by, well, it says compiles, but it's really just conglomerating "small pieces of code into something larger and more complex". Putting all the modules into one file is sort of cheating. (Though why doesn't TS at least do this conglomeration?)
So +1 to felix & thanks for the help, but I still don't understand why TS stops where it stops -- What's the use case for the code it does produce? Does TypeScript really require a step outside of TS to use es6-style classes in code delivered to browsers?
Why do I care? I've worked with RequireJS based codebases before, and it is nice to have your files broken out in a way that you can debug more easily than a monolithic codebase. But I also don't understand why TS seems like it doesn't produce deployable code in this use case.
If you want to keep the import/export of es6 you should define module:"es2015", by default, it converts those to cjs style that is used within node.
Modern browsers are no supports node's cjs style but es2015 style, so you need to specify that module type, and specify on your script tag an attribute of type="module", something like that:
<script type="module" src="entry-point.js"></script>
Currently there is a native support of modules only in chrome, therefore these days the best practice is to create a bundle from the dependency tree that you have created using imports.
The common boundlers are Webpack & RollUp, you can read their configs in-order to generate bundle that runs in browsers.
Small ref for typescript & webpack config: https://webpack.js.org/guides/typescript/
I see you are getting started with building TypeScript web applications. If so this is my advice:
Transpiling TypeScript to JS is unavoidable,
Don't use RequireJS or SystemJS and instead use a bundler tool like parcel, webpack, rollup, browserify. Particularly I recommend parcel since is super easy and plays nice with TypeScript
With a bundler, get rid of all those those defines() requires() SystemJs.calls() and use just standard JavaScript import declarations (import {foo} form 'bar'). I strongly recommend this too
BTW avoid using require.js, AMD, UMD, SystemJS or similar. Is not a good advice getting started using those. Only use them if really really necessary.
Good luck
I want to use es6's import, require and export in my web app using codeigniter.
i found babel-standalone but it's not working.screenshot of embed js files
With the added detail that
import, require and export is undefined
Here's the problem: that's not what Babel does. Babel transpiles code: it turns one file of ES6 code into a file that contains ES5 code that does the same thing. It does not combine source code files that use require or import into bundles.
You're looking for browserify or Webpack to do that - they are bundlers, tools that let you use require, import, and export to organize code. You'll need to use one of them to accomplish your goal - Babel alone will not do what you're trying to do.
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
Suppose I have two files
a.js:
alert("hello from a.js");
b.js
alert("hello from b.js");
is there any way to bundle them with WebPack so that
I get both alerts synchronously as soon as bundle is loaded
alerts should be in the same order as declared
"hello from a" and then "hello from b"
Webpack natively supports CommonJS ( require / import ) and AMD style, and since yours are not falling into those categories, I believe you should look at the shimming modules section
https://github.com/webpack/docs/wiki/shimming-modules
This is from their header
In some cases webpack cannot parse some file, because it has a
unsupported module format or isn't even in a module format. Therefore
you have many options to convert the file into a module.
For anyone else that comes looking, the new (webpack 4+) link to the docs on shimming is here: https://webpack.js.org/guides/shimming/
For me personally, this webpack plugin was most helpful and headache free: https://www.npmjs.com/package/webpack-merge-and-include-globally