Rollup with CommonJS module exporting an unnamed function as the module? - javascript

I have a CommonJS module, called inner.js, that defines a function and then exports that function as the entire module:
// inner.js, a legacy CommonJS module
var foo = function() { return 42; };
module.exports = foo;
In Node, I can readily verify this works as-is.
> var inner = require('./inner.js');
> inner() // prints 42
But that’s a legacy module that I’d like to use from a ES6 module, called outer.js:
// outer.js, an ES6 module
import * as inner from "./inner.js";
export function bar() { return inner(); }
I see that rollup-plugin-commonjs is commonly used in these situations but I can’t get it to work when the CommonJS inner.js module exports a function as the whole module. If, after running rollup and dumping the result to loadme.js, I try to run load the ES6 outer module and try to call the function originally defined in the inner CommonJS module, I get an error:
> var outer = require('./loadme.js')
undefined
> outer.bar()
TypeError: inner$2 is not a function
at Object.bar (/.../so-rollup-question/loadme.js:27:25)
I think I’m just failing to load the CommonJS module correctly, in such a way that the module itself functions as a function. I’m not familiar enough with UMD to get anything meaningful out of inspecting the rollup-output.
The rest of this post is about a minimum example.
Here’s my very simple index.js:
// index.js
export {bar} from "./outer.js";
which is read by my rollup config:
// rollup.config.js
import npm from "rollup-plugin-node-resolve";
import commonjs from 'rollup-plugin-commonjs';
export default {
entry : "index.js",
format : "umd",
moduleName : "sphereModule",
plugins : [ npm({jsnext : true}), commonjs() ],
dest : "loadme.js"
};
I have a complete clonable repository demonstrating the problem.

Assigning directly to module.exports is basically equivalent to having a default export. Hence importing the module as follows should work:
import inner from "./inner.js";

Related

How to load named exports with SystemJS

If I have a lib, say utils.js which looks like this
exports.foo = function () {
return 'foo';
};
exports.bar = function () {
return 'bar';
};
Which can be used as follows
import {foo} from './libs/utils';
console.log(foo());
Not very spectacular, but I get the feeling that this problem is the origin of the issue described in this post. Anyway I cannot get this to work in combination with SystemJS. I have to change the code to fix it
import utils from './libs/utils';
console.log(utils.foo());
Here is my systemjs-config file:
SystemJS.config({
map: {
'plugin-babel': 'node_modules/systemjs-plugin-babel/plugin-babel.js',
'systemjs-babel-build': 'node_modules/systemjs-plugin-babel/systemjs-babel-browser.js',
},
packages: {
'.': {
defaultJSExtensions: 'js'
}
},
transpiler: 'plugin-babel'
});
So, it seems only the exports object can be loaded and not the named export. Can this somehow be fixed?
UPDATE I get the impression it could be fixed with formats
meta: {
'./libs/utils.js': {
format: 'cjs'
}
}
But so far it gives the same problems
This behavior is not SystemJS specific. SystemJS behaves like this since version 0.20 because this is what ES6 module interoperability is being standardized to.
When, as in your question, you are importing CommonJS modules (exported via module.exports) using ES6 import, you will only get the entire export, and you cannot immediately destructure the exported names.
However, when you are importing modules which are exported via ES6 export, you will be able to destructure the exported names.
So, it's all by design. Guy Bedford wrote about this on his blog and referenced the module standardization that is going on for NodeJS:
... named exports will no longer be permitted when importing a
CommonJS module from an ES module, and is discussed at
https://github.com/nodejs/CTC/pull/60/files#diff-2b572743d67d8a47685ae4bcb9bec651R217.
That is, import { name } from 'cjs.js', where cjs.js is a CommonJS
module will no longer be supported, and instead will require
import cjs from 'cjs.js'; cjs.name.
An interop workaround by using __esModule:
We will continue to support the __esModule flag in interop though,
allowing lifting of named exports for these cases.
So if the cjs.js module was written:
exports.__esModule = true;
exports.name = function () { ... }
then it would be possible to have import { name } from 'cjs.js';, even
though cjs.js is a CommonJS module, although this __esModule will
eventually in the longer term be deprecated as well.

How to map TypeScript ES6 default imports to return values of AMD modules?

For example, I have an AMD module in vanilla JS (not touched by tsc):
// Foo.js
define([], function() {
return class Foo {}
})
with accompanying declaration file:
// Foo.d.ts
declare class Foo {
// ...
}
export default Foo
then I have a typescript module:
// app.ts
import Foo from './Foo'
new Foo // Uncaught TypeError: Foo_1.default is not a constructor
When I compile my typescript module to AMD format with tsc and run it in my RequireJS environment, there's an error that default is undefined because typescript compiles to:
define("app", ["require", "exports", "Foo"], function (require, exports, Foo_1) {
"use strict";
console.log(Foo_1["default"]);
});
I don't want it to grab the default property. I want it to treat a default import like it is simply grabbing the returned value of my define module. Every single module in my project is a define() module, or a typescript module compiled to define() (AMD). I'd simply like to get that return object.
Is there a way to do (configure) this?
EDIT:
I know I can import * as Foo from './Foo', but that is sort of hacky, and it generates errors because the declared module doesn't have named exports. I would like a non-error way to do it, if possible.
EDIT: I learned I can just do
// app.ts
import Foo = require('./Foo')
new Foo // no error
to achieve the result. The only problem with that is it is not ES6-compatible. This means that if we tell typescript to leave ES6 import statements as-is, then other tools won't understand typescript-specific import/export statements. I need a way to use only official ES6 module syntax in a reasonable way.
I'm think that my solution might be to just do a post-tsc-compile transform step to convert default import access points.
Try
import * as Foo from './Foo'

How should I define a global TypeScript variable in a definition file so that it can be imported?

I have an external JS library with a global parameter:
function Thing() { ... }
...
var thing = new Thing();
There is a TypeScript definition file, so in thing.d.ts:
declare var thing: ThingStatic;
export default thing;
export interface ThingStatic {
functionOnThing(): ThingFoo;
}
export interface ThingFoo {
... and so on
Then I import this into my own TS files with:
import thing from 'thing';
import {ThingFoo} from 'thing';
...
const x:ThingFoo = thing.functionOnThing();
The problem is that transpiles to:
const thing_1 = require("thing");
...
thing_1.default.functionOnThing();
Which throws an error. I've asked about that in another question, and the suggestion is to use:
import * as thing from 'thing';
That doesn't fix it - it gives me thing.default in TS but then that's undefined once transpiled to JS.
I think there's something wrong with thing.d.ts - there must be a way to define a typed global parameter that can be imported.
How should I write thing.d.ts so that it represents the JS correctly and doesn't transpile to include default or other properties not actually present?
If the only way to use that library is by accessing its globals (as opposed to importing it as node module or amd or umd module), then the easiest way to go is have a declaration file without any exports at top level. Just declaring a variable is enough. To use it, you have to include that declaration file when compiling your typescript code, either by adding it to files or include in tsconfig.json, or directly on command line. You also have to include the library with a <script> tag at runtime.
Example: thing.d.ts
declare var thing: ThingStatic;
declare interface ThingStatic {
functionOnThing(): ThingFoo;
}
declare interface ThingFoo {
}
test-thing.ts
const x:ThingFoo = thing.functionOnThing();
can be compiled together
./node_modules/.bin/tsc test-thing.ts thing.d.ts
the result in test-thing.js:
var x = thing.functionOnThing();
See also this question about ambient declarations.
Note: there are module loaders out there that allow using global libraries as if they were modules, so it's possible to use import statement instead of <script> tag, but how to configure these module loaders to do that is another, more complicated question.

Best way to make accessible api with es6 modules

I'm writing a browser api with es6 (translated with babel). Since other js are going to call my api, I need to make my api accessible from the global (window) scope.
With module pattern in plain js (es5) I would have done something like this:
myApp.js
var myApp = (function(){
var a, b, c;
function setA(){
// set a
}
// other functions
return {
"setA": setA,
// ... other functions
};
}());
myAppExt.js
window.myApp = window.myApp || {};
(function(app){
app.Module1 = (function(){
return {
// methods of this module
};
}());
}(myApp));
With es6 we're not supposed to do something like this but to achieve the same objective I'm writing my app in this way:
myApp.js
import method1 from './common/module1.js'
import init from './folder/init.js'
import {method2, method3} from './folder/module2.js'
import log from './common/log.js'
const myApp = {};
window.myApp = myApp;
myApp.method1 = method1;
myApp.method2 = method2;
myApp.method3 = method3;
myApp.log = log;
init();
Is this the best way to achieve this goal or is there any better design solution?
If you are going to develop a library you will probably end up generating one single bundled file which contains all the contents of your library. To create a a a bundle you need a tool like webpack or browserify, both tools allow you to create your library in a way that can be consumed in many ways (AMD, CommonJS, global...).
So you need to create a root module:
myLibrary.js
import something from './framework/module1.js';
import somethingElse from './framework/module2.js';
// export public parts of your library
export {something};
export {somethingElse };
Then use webpack library setting:
{
output: {
// export itself to a global var
libraryTarget: "var",
// name of the global var: "Foo"
library: "Foo"
},
externals: {
// require("jquery") is external and available
// on the global var jQuery
"jquery": "jQuery"
}
}
More info here.
You can also use browserify standalone setting:
--standalone -s Generate a UMD bundle for the supplied export name.
This bundle works with other module systems and sets the name
given as a window global if no module system is found.
More info here.
I've actually merged the solution proposed from OweR ReLoaDeD with another I've found.
After configuring webpack to export global variable, instead of importing and then exporting methods I've exported directly what I needed to be available in the public api.
export {method} from './common/file1.js';
export * from './dir/file2.js'
export {anothermethod} from './common/file2.js
Thank you for the help

Typescript and Durandal.JS and exports

What is defining the exports module in the following snippet?
define(["require", "exports", 'durandal/app', 'durandal/http'], function(require, exports, __app__, __http__) {
.
.
.
}
I checked out this example, opened the solution and couldn't figure out whatever is defining the "exports" module. Is that just native to RequireJS now, or is there an external reference that I'm still missing?
Ass soon as you import anything in typescript and compile with amd you get a define with require and exports.
exports is requireJS handle for anything you want to export from this module. e.g.
export var foo = 123;
will generate
exports.foo = 123;
Which is utilized by requirejs import on the other end.

Categories