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

[ESM Support] Import named 'prompt' not found in module #439

Open
raulfdm opened this issue Sep 17, 2023 · 15 comments
Open

[ESM Support] Import named 'prompt' not found in module #439

raulfdm opened this issue Sep 17, 2023 · 15 comments
Labels

Comments

@raulfdm
Copy link

raulfdm commented Sep 17, 2023

Hey peps 👋 .

I know it's too early, but I was trying to run enquirer with Bun, and I got a problem not related to the environment but maybe to the way the bundle is being shipped.

SyntaxError: Import named 'prompt' not found in module '/home/runner/enquirer-test/node_modules/enquirer/index.js'.

Reproduction

  1. create a folder: take enquirer-with-bun;

  2. start an empty project using bun: bun init -y;

  3. add enquirer: bun add --exact enquirer;

  4. paste the following code in the index.ts:

    import { prompt } from "enquirer";
    
    const response = await prompt({
      type: "input",
      name: "username",
      message: "What is your username?",
    });
    
    console.log(response); // { username: 'jonschlinkert' }
  5. run the index file: bun index.ts;

  6. see the error;

You can also see a code demo on this replit: https://replit.com/@raulfdm/enquirer-test?v=1

CleanShot 2023-09-17 at 18 11 05

Environment

System:

  • OS: macOS 14.0
  • CPU: (10) arm64 Apple M1 Pro
  • Memory: 210.27 MB / 32.00 GB
  • Shell: 5.9 - /bin/zsh

Binaries:

  • Node: 18.15.0
  • Yarn: 1.22.0
  • npm: 9.7.2
  • pnpm: 8.7.5
  • Watchman: 2023.05.22.00
  • Bun: 1.0.2
@raulfdm
Copy link
Author

raulfdm commented Sep 17, 2023

I notice that if I replace the import with require, it works. Does that mean the lib doesn't support ESM? 🤔

(by doing this I lost TS intelisense)

@jonschlinkert
Copy link
Member

Nice! I was just thinking about this a few days ago. I've used bun myself on some personal projects and was wondering what I'd need to do to make Enquirer compatible. This gives me a bit of a head start!

I'm working on the next major and I'll try to make sure it works with bun out of the box. In the meantime, if we figure out the cause of the error and you (or someone else) wants to do a PR, that would be great.

I notice that if I replace the import with require, it works. Does that mean the lib doesn't support ESM? 🤔

Hmm, I have a hunch about what's causing that. But first, maybe try these approaches and see if they work:

import Enquirer from 'enquirer';
const enquirer = new Enquirer();

// either this...
const response = await enquirer.prompt({
  type: 'input',
  name: 'username',
  message: 'What is your username?'
});

// or this
const response = await Enquirer.prompt({
  type: 'input',
  name: 'username',
  message: 'What is your username?'
});

Regarding the issue with using import, my hunch is that it's almost certainly that the prompt method is a static getter on the Enquirer class. It could also be related to exports being defined with non-enumerable descriptors or something, but I'm pretty sure it's the the former.

I'll definitely look into this more, and I'd love to hear about any progress you make or issues you have using enquirer with bun.

thanks for creating the issue!

@Zikoat
Copy link

Zikoat commented Sep 19, 2023

TLDR:

import Enquirer from "enquirer";
const { prompt } = Enquirer;

Hi, there are multiple bugs with enquirer, and only one of these is specific to bun, and only happens if my file is a CommonJS module.

// prompt.ts
const { prompt } = require("enquirer");
Cannot redeclare block-scoped variable 'prompt'.ts(2451)
types.d.ts(2442, 18): 'prompt' was also declared here.

This error happens because bun declares a global variable prompt which would be overwritten by the require import. This is done at runtime, but we are getting typescript errors too because we add the global bun definitions with this line:

// tsconfig.json
{
    "types": ["bun-types"]
}

This can be fixed in 2 different ways:

  1. Rename as we import prompt to fix the a naming conflict:
// prompt.ts
const { prompt: enquirerPrompt } = require("enquirer");
  1. Turn the file into an ESM module by adding an import or export:
// prompt.ts
const { prompt: enquirerPrompt } = require("enquirer");
export {};

However, we don't get any typescript by importing it like this. I am unsure of why this happens, but it seems to be a more general problem with the typescript types. We can get the typescript hinting by using ESM imports:

// prompt.ts
import { prompt } from "enquirer";

However, this gives us the next error:

$ bun ./prompt.ts 
SyntaxError: Import named 'prompt' not found in module '/home/myPrompt/node_modules/enquirer/index.js'.

This error is not specific to bun, as it also happens when running it with node. In fact, the error we get from node is a lot more helpful:

npx ts-node-esm ./prompt.ts
SyntaxError: Import named 'prompt' not found in module '/home/myPrompt/node_modules/enquirer/index.js'.
file:///home/myPrompt/prompt.ts:1
import { prompt } from "enquirer";
         ^^^^^^
SyntaxError: Named export 'prompt' not found. The requested module 'enquirer' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from 'enquirer';
const { prompt } = pkg;

This hints us that enquirer is a CommonJS module, which only supports default exports. We can fix our code by editing it to:

import Enquirer from "enquirer";
const { prompt } = Enquirer;

This code works in both node and bun. @jonschlinkert Could you add this to the documentation as long as the fix below isn't implemented?

To fix this issue, (which is actually a mismatch between the typescript types and the actual exports) we should allow import { prompt } from "enquirer"; by changing this line:
/node_modules/enquirer/index.js

- module.exports = Enquirer;
+ export default Enquirer;
+ export const prompt = Enquirer.prompt;

But I don't have the time to create a proper pull request, test it, check if there are some more exports that would need to be exported to match the TS type definitions, or if this is a backwards incompatible change which would break CommonJS projects that are using this, and would warrant a Major version bump.

@jonschlinkert
Copy link
Member

jonschlinkert commented Sep 20, 2023 via email

@Zikoat
Copy link

Zikoat commented Sep 21, 2023

Bun works perfectly with enquirer, i have used it in my very small project, and haven't seen any bugs related to bun. All of the errors i have seen are because of incorrect types, and are fixed in open pull requests. Bun uses typescript by default, which is why the typescript errors are incorrectly labeled as bun errors.

Many of the bugs i encountered are fixed in this open PR: #307
But as user doowb mentioned here #135 (comment), the next step to fix all of those typescript bugs would be to move the maintenance of types to definitelytyped. I also think that the maintenance of types is best done through definitelytyped, but if you still want to keep the types in this repo, then doowb and bitjson have already volunteered to maintain them, so having a good talk with them to figure out how to maintain the types and choose between definitelytyped or self-maintained types would be very beneficial to this project. I won't use this library for long, so i can't maintain it.

Moving the existing work done in PR #307 to definitelytyped would not be too difficult.

@Zikoat
Copy link

Zikoat commented Sep 24, 2023

@raulfdm Could you try to import enquirer like this? Then it should work correctly with bun.

import Enquirer from "enquirer";
const { prompt } = Enquirer;

@jonschlinkert I think this can be closed, as most of the issues are already tracked in other issues.

@raulfdm
Copy link
Author

raulfdm commented Sep 25, 2023

@Zikoat, in that way, it works indeed

CleanShot 2023-09-25 at 08 01 57

Should the README include a side note for that case?

@Zikoat
Copy link

Zikoat commented Sep 25, 2023

Yes it probably should have a note for how to import the package using ESM

@jesusvallez
Copy link

Anyway, if you try to import Form like this:

import Enquirer from "enquirer"
const { Form } = Enquirer

You will encounter a problem with typescript because Form does not exist in the type 'typeof Enquirer'.

@raulfdm
Copy link
Author

raulfdm commented Jan 21, 2024

Anyway, if you try to import Form like this:

import Enquirer from "enquirer"
const { Form } = Enquirer

You will encounter a problem with typescript because Form does not exist in the type 'typeof Enquirer'.

@jesusvallez , using typescript@5.3.3 didn't encounter this issue:
CleanShot 2024-01-21 at 11 14 58@2x

@jesusvallez
Copy link

Anyway, if you try to import Form like this:

import Enquirer from "enquirer"
const { Form } = Enquirer

You will encounter a problem with typescript because Form does not exist in the type 'typeof Enquirer'.

@jesusvallez , using typescript@5.3.3 didn't encounter this issue:
CleanShot 2024-01-21 at 11 14 58@2x

@raulfdm

But you are not importing Form.
Try to import it instead of prompt.

@raulfdm
Copy link
Author

raulfdm commented Jan 22, 2024

Ahh... now I see.

Well, debugging the code base this is more than expected. There's a mismatch between the code and what's being actually exported.

There's a define function which loads some modules and bake into the Enquirer class. No offence to who ever created that but it seems bit hacky.

define('AutoComplete', () => require('./autocomplete'));
define('BasicAuth', () => require('./basicauth'));
define('Confirm', () => require('./confirm'));
define('Editable', () => require('./editable'));
define('Form', () => require('./form'));
define('Input', () => require('./input'));
define('Invisible', () => require('./invisible'));
define('List', () => require('./list'));
define('MultiSelect', () => require('./multiselect'));
define('Numeral', () => require('./numeral'));
define('Password', () => require('./password'));
define('Scale', () => require('./scale'));
define('Select', () => require('./select'));
define('Snippet', () => require('./snippet'));
define('Sort', () => require('./sort'));
define('Survey', () => require('./survey'));
define('Text', () => require('./text'));
define('Toggle', () => require('./toggle'));
define('Quiz', () => require('./quiz'));

While this works in CJS because there's no strict check on what's being declared vs what's being imported, in TS this won't work, unless we define ALL submodules of Enquirer godmodule in the index.d.ts

This lib was written 5 years ago, maybe it would worth a rewrite either in TS or in some way that's explicit what's being exported or not (if in JS, using proper JSDocs annotation)

@karlhorky
Copy link

I think this can be closed, as most of the issues are already tracked in other issues.

@Zikoat which issues do you mean? The only other issues mentioning ESM are these:

Both of which have a lot of extra requests, other than ESM support.

This Bun issue (#439) is the closest that we have to a request only for ESM support.

I would suggest that:

  1. This issue should not be closed

  2. @raulfdm could edit the title of this issue to be ESM support, to support named imports:

    import { prompt } from 'enquirer';

Yes it probably should have a note for how to import the package using ESM

I would suggest to avoid documenting the workaround syntax (import and then destructuring), because it should be actually fixed in enquirer.

@Zikoat
Copy link

Zikoat commented Mar 12, 2024

Ah yes, you're right about keeping this open, and renaming.

I was talking about these open pull requests, which would fix this issue if they were merged:

@karlhorky
Copy link

Nice, thanks for the links! Watching those PRs now too :)

@raulfdm raulfdm changed the title [Bun] Import named 'prompt' not found in module [ESM Support] Import named 'prompt' not found in module May 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants