Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error: 'default' is not exported by node_modules/conf/index.js #78

Closed
moranje opened this issue Feb 6, 2020 · 3 comments
Closed

Error: 'default' is not exported by node_modules/conf/index.js #78

moranje opened this issue Feb 6, 2020 · 3 comments

Comments

@moranje
Copy link

moranje commented Feb 6, 2020

Hi!

I'm stuck, and I'm not sure where to look to further debug the issue. I'n not exactly sure this is a problem with this plugin but it seems plausible.

Terminal output:

[!] Error: 'default' is not exported by node_modules/conf/index.js
https://rollupjs.org/guide/en/#error-name-is-not-exported-by-module
src/rollup-bug.ts (1:7)
1: import Conf from 'conf';
          ^
2: 
3: const conf = new Conf();
Error: 'default' is not exported by node_modules/conf/index.js

I've had to move to node 10 to find this problem and am currently on v10.18.1. The easiest way to install it was through homebrew.

brew install node@10

I have tried creating named export in the commonjs plugin (namedExports: { conf: ['Conf'] }) as suggested here with no avail. I've also tried a more minimal repro:

// index.js
class Conf {}

module.exports = Conf;

// index.d.ts
declare class Conf {}

export = Conf;

But this builds just fine. So it must be something with the 'conf' module right? It does do some require trickery here.

// Prevent caching of this module so module.parent is always accurate
delete require.cache[__filename];
const parentDir = path.dirname((module.parent && module.parent.filename) || '.');

Or maybe something with babel and @babel/preset-env? However the error seems to hint at a problem with one of the rollup plugins. I am at a loss to debug this any further so I was rather hoping you or any would know how to find the root cause of the problem.

My setup:

// index.ts
import Conf from 'conf';

const conf = new Conf();

export function init(): Conf {
  return conf;
}
// rollup.config.ts
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import typescript from '@wessberg/rollup-plugin-ts';

let plugins = [
  typescript({
    transpiler: 'babel',
  }),

  resolve(),

  commonjs(),
];

export default {
  input: `index.ts`,
  output: [{ file: `index.js`, format: 'cjs', sourcemap: 'inline' }],
  plugins: plugins,
};
// babel.config.js
module.exports = {
  presets: [
    [
      '@babel/env',
      {
        targets: { node: '10' },
        useBuiltIns: 'usage',
        corejs: 3
      },
    ],
    '@babel/typescript',
  ],
};

I've attached a repro:

rollup-bug.zip

Martien

EDIT: removed comments on failing on specific node versions, it's failing the same on all tested version now.

@wessberg
Copy link
Owner

wessberg commented Feb 7, 2020

Hey there. Thanks for reaching out. The issue you are currently experiencing is unrelated to rollup-plugin-ts. To see for yourself, you can comment out the plugin from your rollup.config.js file, turn your index.js file into valid JavaScript by removing the return type annotation from your init function and try building again.

I'll gladly help you out anyway. 😊

First and foremost, the "conf" package uses exports = <something>, which has no direct equivalent with ES Modules. Is it a default export? Is it a namespace export? It is ambiguous. Because of that, if you were to attempt building this with tsc you would get an error because import Conf from "conf" is ambiguous to TypeScript too since the module doesn't include any explicit default exports.

So you'll need to add the allowSyntheticDefaultImports: true option to your tsconfig.json file to make TypeScript treat exports = <something> as a default export. This only affects static analysis and doesn't affect emit.

Now, moving on from that, I see you are targeting CommonJS but are attempting to bundle your external dependencies. Unless you have a good reason to do it, with Rollup, you generally pass the module specifiers to your external dependencies to Rollup with the external option:

export default {
  // ...
  external: [
    "conf"
  ]
}

That way, a require() call will be present in your bundled output that imports the conf module. Because of that, you won't need to use @rollup/plugin-commonjs which is only relevant when you want to inline your external dependencies inside your bundle and some of them are based on CommonJS modules. @rollup-plugin-commonjs has no way of transforming exports = <something> into equivalent ES Modules, so it doesn't. That's why Rollup doesn't see any default exports inside the file - @rollup/plugin-commonjs had to bail out. Beyond that, one of the transient dependencies of the conf library, ajv, imports some JSON content, so you would also need @rollup/plugin-json. And, another transient dependency, uri-js, is based on UMD and not CommonJS, so you would need to figure out a way to convert that into ES Modules as well.

The general convention with Rollup is to mark all of your dependencies from your package.json as external in your config, unless you are bundling a web application that need to run client-side in which case you'll definitely need to bundle in your external dependencies.

As I hope is clear by now, Rollup understands ESM and only ESM, and everything else - CommonJS, JSON, UMD, AMD, etc, needs separate plugins to work,but it is not always possible or feasible to rewrite that into ESM.

If you still want to inline the conf module, I've built a Custom Transformer that can rewrite CommonJS and UMD into valid ESM, it covers more use cases than @rollup/plugin-commonjs that I can help you with setting up if you want.

Hope this helps! Feel free to get back to me with any questions you might have, but given that this has no relation to rollup-plugin-ts or TypeScript for that matter, I'm going to close this issue. 🙂

@wessberg wessberg closed this as completed Feb 7, 2020
@moranje
Copy link
Author

moranje commented Feb 7, 2020

I'll gladly help you out anyway. 😊

I really appreciate you taking the time to write this reply and help me out.
Thank you. I hope it's okay for me to reply to this issue.

My use case is somewhat unusual. I'm using node to create a 'workflow' for a Mac
OS app called Alfred. I this environment I can get away with asking my users to
install node.js but not with running npm innstall on every update, hence the
need for a bundler that bundles everything but node.js.

It is located here.

Unfortunatly I already rely on allowSyntheticDefaultImports cannot mark the
files as external in rollup because of the above.

If you still want to inline the conf module, I've built a Custom Transformer that can rewrite CommonJS and UMD into valid ESM, it covers more use cases than @rollup/plugin-commonjs that I can help you with setting up if you want.

That looks like a great resource, I been playing around with it and it would
simplify my setup and presumably shake down my app a little further. I have another
question that is again probably ont related to any of these two plugins, but as
you can tell by now I am no Rollup master yet (from where I stand you seem to
be). I hope you'll forgive me this next question.

How could I internalize my dependencies?

// rollup.config.ts
import { builtinModules } from 'module';
import { cjsToEsm } from '@wessberg/cjs-to-esm-transformer';
import { terser } from 'rollup-plugin-terser';
import filesize from 'rollup-plugin-filesize';
import json from '@rollup/plugin-json';
import pkg from './package.json';
import resolve from '@rollup/plugin-node-resolve';
import sourceMaps from 'rollup-plugin-sourcemaps';
import typescript from '@wessberg/rollup-plugin-ts';

const libraryName = 'alfred-workflow-todoist';

let plugins = [
  json(),

  resolve(),

  typescript({
    transformers: [cjsToEsm()],
    // transpiler: 'babel',
  }),

  sourceMaps(),

  filesize(),
];

if (process.env.NODE_ENV === 'production') {
  plugins = [...plugins, terser()];
}

export default {
  input: `src/${libraryName}.ts`,
  output: [{ file: pkg.main, format: 'cjs', sourcemap: 'inline' }],
  watch: {
    include: 'src/**',
  },
  external: [
    ...builtinModules,
    ...Object.keys(pkg.devDependencies),
  ],
  plugins: plugins,
};

Which externalizes all my dependencies.

(!) Unresolved dependencies
https://rollupjs.org/guide/en/#warning-treating-module-as-external-dependency
compare-versions (imported by src/alfred-workflow-todoist.ts, src/lib/updater.ts)
clean-stack (imported by src/lib/error.ts)
new-github-issue-url (imported by src/lib/error.ts)
got (imported by src/lib/updater.ts)
dotenv (imported by src/lib/utils.ts)
macos-version (imported by src/lib/utils.ts)
conf (imported by src/lib/stores/settings-store.ts, src/lib/stores/update-store.ts, src/lib/todoist/local-rest-adapter/conf-cache/index.ts)
md5 (imported by src/lib/workflow/item.ts)
open (imported by src/lib/commands/open-url.ts)
fuzzy-search (imported by src/lib/commands/read-settings.ts, src/lib/commands/helpers.ts, src/lib/todoist/local-rest-adapter/local-rest-adapter.ts)
@sentry/node (imported by src/lib/sentry.ts)
nearley (imported by src/lib/todoist/parser.ts)
todoist-rest-api (imported by src/lib/todoist/local-rest-adapter/index.ts, src/lib/todoist/local-rest-adapter/local-rest-adapter.ts, src/lib/todoist/local-rest-adapter/local-task-adapter.ts)
moo (imported by src/lib/todoist/lexer.ts)
deep-equal (imported by
src/lib/todoist/local-rest-adapter/local-rest-adapter.ts)

Again thanks for your patience, your work and your time.

Martien

@wessberg
Copy link
Owner

wessberg commented Feb 7, 2020

Hi again. It's completely fine that you ask! I'm happy to help.

Haha, yes, it is indeed @wessberg/cjs-to-esm-transformer I'm refering to.
Here's how you would need to configure the typescript plugin:

     transpiler: "babel" // Optional - only included because I can see you're already using babel
     transformers: [cjsToEsm()],
     tsconfig: {
       // You need to do this if you want to do stuff like 'import conf from "conf"'
       allowSyntheticDefaultImports: true,
       // You will need to allow transformijng JavaScript files for the Custom Transformer to actually visit the files inside your node_modules folder
       allowJs: true
     }

If you have a tsconfig.json file, it would be better if you placed the two options from above directly in your config instead.

And since you do want to inline all of your external dependencies, the change the external option you pass to Rollup to simply:

external: [
    ...builtinModules
]

You'll need v0.0.17 of @wessberg/cjs-to-esm-transformer for this to work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants