rollup / rollup

Next-generation ES module bundler

Home Page:https://rollupjs.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Default export umd + typescript

soyuka opened this issue · comments

Hi,

Coming from infernojs/inferno#596, I thought that this issue was related to how typescript handles the default imports, but it might in fact be related to how rollup export it.

Issue summary
import createElement from 'inferno-create-element'
console.log(createElement)

Transpiles to :

const inferno_create_element_1 = require('inferno-create-element');
console.log(inferno_create_element_1.default);

Which outputs:

undefined

If you log inferno_create_element_1 (without the default key), you will get the corresponding function.

The same goes for Component:

I tried to import with the following (because of the issue above):

import * as Component from 'inferno-component'
console.log(Component)
export class Foo extends Component {}

You can now get the Component through, but the typescript compiler isn't happy about it not beeing a constructor:

error TS2507: Type 'typeof 'inferno-component'' is not a constructor function type.

Inferno exports as umd, and by modifying the declaration to the below, I can import everything just fine:

typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('./inferno')) :
typeof define === 'function' && define.amd ? define(['exports', 'inferno'], factory) :(factory((global.Inferno = global.Inferno || {}),global.inferno));
//add this and everything works:
if (typeof module !== 'undefined') {
    module.exports.default = module.exports
}

I think that this might be an easy fix for issues who looks related to this behavior (#1155, #1081).

I'm not really familiar with rollup, and the fix might not be appropriate. WDYT?

Thanks!

It sounds to me like the issue is on TypeScript's end, by failing to handle interop. The last thing Rollup should be doing is creating bundles that normal JS consumers have to use by appending .default to their imports!

In any case, please include a repro 😀

The inferno creator told me that the typescript issue should've been resolved in the latest.

Here is the repro:
https://github.com/soyuka/repro-default-export

Just clone and then run bash init.sh :).

I don't understand what this has to do with Rollup?

Rollup is doing the export (umd header), but maybe it's not related at all? I'm just trying to figure from where the bad export comes from :). Either typescript (who tries to get the .default key of module.exports), or rollup (by not setting up the .default key) or simply the inferno project that shouldn't do global exports from typescript.

Rollup doesn't create .default keys if there's only a default export, because that makes it unnecessarily difficult for people who aren't using ES modules (still the majority) to use libraries bundled by Rollup.

It sounds like this is a TypeScript issue, but it might be something that's easiest to work around on the Inferno end.

Thanks to confirm my thoughts. But still having the .default key referencing module.exports can't be that bad, or is it?

It's far from ideal – it means an export can only be an object or a function, and you're mucking about with the exported object. If it was a widespread issue it might be a different matter, but if it's just to paper over a bug in a specific TypeScript version (which seems like it might be the case?) then it's not worth the extra mess and maintenance.

if (module.default === undefined) {
    return module
}

return module.default

Should work.

I tried every latest typescript version but I'll try to find an answer on their side! Thanks for taking the time to answer!

@Rich-Harris @soyuka I've got exactly the same problem. I'm using rollup to create umd and es6 versions of my library and everything works fine, except when using TypeScript...

In the issue already referenced by @soyuka (microsoft/TypeScript#13017) @RyanCavanaugh says:

The ES6 module semantics are very clear on these points:
import x from 'y' gets the default export from 'y'
(...) It'd be incorrect for us to emit anything other than .default

I completely understand that it doesn't make any sense to require everyone to add .default when using plain JS, but what can be actually done in this case? From this comment above it doesn't look like TypeScript will emit anything else :/ I can always add info to the README that when using TS one should use import * as x from 'y' syntax, but it still doesn't feel right ;)

For future reference, at my company we solved the problem with a rollup plugin, see

https://github.com/dcsfuerth/ngx-build-tools/blob/master/src/utils/rollup.ts#L5

We simply replace the typescript version of imports with the rollup version of imports.

Leaving this here if somebody is gonna stumble on this thread again.

From my point of view if you want to provide a structure that is consumable for your TS typings, you should simply use exports: 'named' in your projects (in rollup.config.js when bundling), instead of relaying on automatic behaviour.

Going further - if you do not enforcing CJS consumers to use require('your-lib').default instead of require('your-lib') you should just use additionally this babel plugin - https://github.com/59naga/babel-plugin-add-module-exports (only when transpiling to CJS, do not use it when transpiling to ESM format)

It seems it is, but as always it will take time until 2.7 propagates though.