My application depends on library which uses ecma6 syntax for modules:
require lib = 'lib.js' // myapp.js
import Foo from 'bar' // lib.js
...
I want to run node myapp.js, but it results in syntax error, since node does not support ecma6 modules (and --experimental-modules would require lib.js to be named lib.mjs).
Is there any way to run myapp.js without changing the source code of lib.js?
look at https://babeljs.io/, you can compile your code (with that lib) to usual format
Related
Context
I am creating a library with 2 ways of initialization:
Automatic - I download some stuff for you asynchronously, then initialize.
Manual - You already downloaded the stuff before, then I initialize the library immediately (sync).
I have successfully implemented tree-shakable libraries in the past. What we would normally do, is separate the code into two modules, and let the app developer choose which one to import, thus allowing tree-shaking the other part. Similarly to this:
import { LibraryAsyncModule } from 'my-library'; // 🔁 Or LibrarySyncModule
#NgModule({
imports: [LibraryAsyncModule] // 🔁 Or LibrarySyncModule
})
export class AppModule { }
What I want to accomplish ✔
To reduce the learning curve of using my library, I'm trying to design is a single imported module which includes the relevant module and allows tree shaking the other. The following diagram shows the desired structure.
What I want to avoid 🚫
I could create factory providers that will detect the config passed to forRoot() and load the corresponding module at runtime. However, importing the modules at runtime turns initialization to async, and will also prevent angular from bundling the used module with the app.
I need a build time solution. My prototypes show that simply including both sync and async modules in the core module result in both being bundled.
How would that single module look like? Any ideas / suggestions? 🤔
Your approach should work, but you will need to enable it.
Since Tree-shaking works only with es6 modules you need to publish your lib with it & specify module property in your package.json.
// package.json
{
...
"main": "dist/index.js",
"module": "dist/es/index.js", // <- this file should contain es modules not commonjs
...
}
This setup will tell bundlers (webpack in your case) to use the es modules export and allow them to enable tree-shaking feature.
I recommend for package devs to use tsdx cli tool which does this automatically and supports many other features such as TypeScript support etc.
npx tsdx create mylib
I'm trying to include bitcore-lib partially into my webpage using tree-shaking that rollup provides out of the box and rollup-plugin-commonjs to load Node.js module.
To better illustrate the problem I make a demo project that available on the github
You can have a look at bundle.js. If I define a module in the following way:
const useful = "3";
const useless = "4";
export {usefull, useless}
Tree shaking works correctly - the final bundle includes only useful dependency.
But if I define a module in the way it defined in bitcore-lib (node-lib.js) in demo project:
module.exports = {
useful: "1",
useless: "2"
};
In that case, the final bundle includes the whole module.
I've expected that useless: 2 dependency shouldn't be included because of tree-shaking. My index.js is here:
import {usefull as usefull1} from "./my-node-lib"
import {usefull as usefull2} from "./my-es-lib"
console.log(`hi! ${usefull1} ${usefull2}`);
My rollup.config.js is available here
Is it a problem of module definition or rollup config?
Tree shaking works only for ES6 modules. At least it's true for Webpack and I suppose for rollup as well. Your first definition is ES6, second is commonjs.
Therefore if a library is not compiled/transpiled to ES6 modules tree shaking will not work.
Another feature which will not work is module concatenation.
Depending on the library you can try to recompile it.
I am trying to answer,
when to use import/export and when to use require()/module.exports? But as I try to dig, it seems to get complicated.
Here's my understanding
require()/module.exports: this is nodejs implementation of the module system. This loads the modules syncronously.
with es6, we can use import/export. the docs says
The import statement is used to import bindings which are exported by another module. Imported modules are in strict mode whether you declare them as such or not. The import statement cannot be used in embedded scripts unless such script has a type="module".
Ques1: How does this work with babel or webpack or browsers in general?
As I was exploring I came across stuff like CommonJs, requireJs, Asynchronous Module Definition (AMD)
Ques2: I am more interested in knowing the timeline as how these things evolved in javascript ?
How does this work with babel or webpack or browsers in general?
Babel and Webpack follow the ES spec and transpile the import / export statement to one single file. As they also support the require syntax, they usually transpile the import statements to require() calls and the export statements to module exports, and then ship with a custom loader for modules., If you got for example:
// A.js
export default function() { }
// B.js
import A from "./A";
A();
Then it gets transpiled to the following require syntax:
//A.js
exports.default = function() {};
//B.js
var A = require("./A").default;
A();
That could then get wrapped to something like:
(function() { // prevent leaking to global scope
// emulated loader:
var modules = {};
function require(name) { return modules[name]; }
function define(name, fn) {
var module = modules[name] = { exports: {} };
fn(module, module.exports, require);
}
// The code:
define("A", function(module, exports, require) {
// A.js
exports.default = function() { };
});
define("B", function(module, exports, require) {
// B.js
var A = require("A").default;
A();
});
})();
how these things evolved in javascript ?
A few years ago, writing JS was restricted to browsers, and the only way to load multiple js sources was to use multiple <script> tags and use the global object to exchange functionality. That was ugly.
Then Nodejs was invented and they needed a better way to work with modules and invented the require() thing.
The writers of the spec saw a need for a native syntax for that, so import / export were introduced.
Babel and others then wrote transpilers.
What webpack the bundler does is the following:
You specify an input file in the config
You specify an output file the config
Webpack will look at all the files which the input file requires (commomJS module system) or imports (ES6 module system). It then funnels the code based on file name extention through loaders. Loaders can transpile the individual files to code the browser can understand. An example of a loader is babel or the sass/scss compiler.
After the different files are transpiled with loaders, the plugins can work at the
transform the bundle of generated code into something else. The bundle is just a bunch of code which together forms piece of functionality
In won't go into detail in the internals of webpack too deeply, but the most important thing to understand is:
You use webpack so you can use split up your code in multiple files, which makes them more maintainable and easier to work with. However then requesting all these files by the client would be horrible for performance (many HTTP requests overhead). Therefore, we bundle the files into one file, or a couple so this overhead is reduced.
Generally, you should write all modern code with import/export syntax if you are using a bundler like webpack, or translating with Babel... npm modules may favor require/module syntax but you can still import them.
Also worth noting is the import() method which returns a promise that should resolve to the root module being imported asynchronously. Webpack may bundle these as asynchronous modules if you have it configured to do so.
In practice the resolution through tooling like babel and webpack will fallback to node-style behaviors against the node_modules lookup, where the standard is transport paths, favoring relative paths. Additional support is per environment.
You can experiment with esm support in modern browsers and in current node (behind a flag as of this answer). The behaviors are somewhat inconsistent, but well defined. When in doubt, experiment and try.
I'm writing an application that uses Angular2 with Typescript as frontend, and NodeJS as backend. I've written a javascript object I wish to share between the frontend and backend. What's the most elegant way to do this?
My initial idea was to write a .d.ts for the frontend, and add a module.exports in the javascript file, so the backend could require('myobject').
While this works, this causes the browser to throw and exception that shows up in the browser console: 'Uncaught ReferenceError: module is not defined'.
And I'd like to not pollute my console with needless error messages. So is there another, more elegant, way of doing this?
The "cleanest" way I know to do this is to write modular script on both ends and create a library of objects you want to share (so that the shared objects are defined in a single location)
Set-up
Frontend: Typescript with either
target ES6, module: commonjs + Babel (requires Typescript 1.7)
or target ES5, module: commonjs
bundled using webpack or browserify
Backend: ES6 (with --harmony flag on node) or ES5
Library
Create a library, say shared, written in Typescript and create the exported Object class
export default class MyObject{ ... }
Make sure the library is compiled with declaration: true (in tsconfig for instance): tsc will generate the js + the typings (declarations).
In the package.json of the shared library, make sure the entry typings is set to point to the generated MyObject.d.ts file. If there are multiple objects; create an index.ts file that re-exports all the objects and point typings to index.d.ts
Usage
Frontend: since you are now using modular JS/TS, import your object directly from Typescript
import MyObject from 'shared'
The typescript transpiler will automatically find the .d.ts definition from the typings entry of shared's package.json.
Backend: simply require('shared')
Note: if there are multiple shared objects, do not use default exports in shared: they cannot be re-exported.
I am implementing a module which supposed to work in NodeJS and Browser (AMD/non-AMD) env.
Simplified version of it looks like that:
var Backbone = require('backbone');
module.exports = Backbone.Model.extend({...});
But I do not see how to make it work for all envs.
If i use global Backbone (without require) it will not work in NodeJS env
If i use require and exclude backbone from the bundle (using --exclude backbone) - it will not work in browser non-AMD (Error: can't find module backbone)
Is it possible to generate UMD module which will:
use require('backbone') in browser (AMD) / NodeJS env
window.Backbone in browser (non-AMD)?
The umd package has a mode for jQuery plugins that work in non-AMD, AMD, or CommonJS environment. The example is jquery, but
you could adapt it for your own namespace module that's not jquery.
https://github.com/umdjs/umd/blob/master/jqueryPluginCommonjs.js
One way I have handled this in the past is to build my standalone bundles for the plugin using require('namespace') but use a stub that follows the pattern linked above in place of the real module.
bundle
.require('./namespace-stub', {expose: 'namespace'})
.require('plugin', {entry: true});
Where namespace-stub.js is a tiny module following the pattern linked above, but instead of invoking a function with namespace (taken from either AMD, CommonJS, or globals), it can just export it.
In other words, you build the standalone bundle for the plugins by substituting a module in place of the main library package which actually just looks for and discovers the real library from wherever it happens to be available.