import-js / eslint-plugin-import

ESLint plugin with rules that help validate proper imports.
MIT License
5.57k stars 1.57k forks source link

No exported names found in module 'prettier' in an ESM project #3101

Open andersk opened 1 week ago

andersk commented 1 week ago

In an ESM project (.mjs or "type": "module"), eslint-plugin-import can’t find any exports in the prettier package. On import * as prettier from "prettier";, the import/namespace rule complains “No exported names found in module 'prettier'”. Similarly, on import prettier from "prettier";, the import/default rule complains ‘No default export found in imported module "prettier"’.

Complete reproducible example:

eslint.config.mjs

import eslintImport from "eslint-plugin-import";

export default [
  eslintImport.flatConfigs.recommended,
  { languageOptions: { ecmaVersion: "latest" } },
];

foo.mjs

import * as prettier from "prettier";

(async () => {
  console.log(await prettier.format("x=1", { filepath: "test.js" }));
})();
$ npm i eslint eslint-plugin-import prettier

$ npm list
test@ /tmp/test
├── eslint-plugin-import@2.31.0
├── eslint@9.14.0
└── prettier@3.3.3

$ node foo.mjs
x = 1;

$ npx eslint foo.mjs

/tmp/test/foo.mjs
  1:8   error  No exported names found in module 'prettier'         import/namespace
  4:30  error  'format' not found in imported namespace 'prettier'  import/namespace

✖ 2 problems (2 errors, 0 warnings)
ljharb commented 1 week ago

This plugin uses resolve, which doesn't yet support the exports field.

prettier's main points to https://unpkg.com/prettier@3.3.3/index.cjs, which is a CJS file with a single default export.

In other words, as far as the plugin's concerned, you'd need to import prettier from 'prettier'. In node, the native import would point to https://unpkg.com/browse/prettier@3.3.3/index.mjs, which seems to support that same pattern (it would also work with import * as, of course)

What happens if you change how you're importing it?

andersk commented 1 week ago

I tried that above:

Similarly, on import prettier from "prettier";, the import/default rule complains ‘No default export found in imported module "prettier"’.

$ npx eslint foo.mjs

/tmp/test/foo.mjs
  1:8  error  No default export found in imported module "prettier"  import/default

✖ 1 problem (1 error, 0 warnings)
ljharb commented 1 week ago

aha, well then that's certainly not a viable workaround :-)

andersk commented 1 week ago

Also, the plugin does not complain about import * as on other CJS-only modules. (At runtime, such an import causes Node to create a namespace wrapper with a default key, and possibly other keys via cjs-module-lexer.)

ljharb commented 1 week ago

It's likely some complex interaction with the combination of main, module, exports, and ESM-transpiled output used in prettier specifically.