eslint-doc-generator icon indicating copy to clipboard operation
eslint-doc-generator copied to clipboard

`exports` are not respected

Open mangs opened this issue 2 years ago • 10 comments

Hi, Thank you for creating this great tool! I found it while looking through the eslint-plugin-unicorn repo and stumbling upon this PR: https://github.com/sindresorhus/eslint-plugin-unicorn/pull/1930

I would love to use your tool for my own ESLint library, but unfortunately it does not respect the exports defined in my library's package.json. Here is the output I see and the associated package.json exports config:

Terminal output

eric@Erics-MacBook-Pro eslint-config-usa-tech.js $ npm run update:readme-eslint-rules-list

> @lessonnine/[email protected] update:readme-eslint-rules-list
> eslint-doc-generator

node:internal/errors:477
    ErrorCaptureStackTrace(err);
    ^

Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/Users/eric/code/babbel/eslint-config-usa-tech.js/index.js' imported from /Users/eric/code/babbel/eslint-config-usa-tech.js/node_modules/eslint-doc-generator/dist/lib/import.js
    at new NodeError (node:internal/errors:387:5)
    at finalizeResolution (node:internal/modules/esm/resolve:429:11)
    at moduleResolve (node:internal/modules/esm/resolve:1006:10)
    at defaultResolve (node:internal/modules/esm/resolve:1214:11)
    at nextResolve (node:internal/modules/esm/loader:165:28)
    at ESMLoader.resolve (node:internal/modules/esm/loader:844:30)
    at ESMLoader.getModuleJob (node:internal/modules/esm/loader:431:18)
    at ESMLoader.import (node:internal/modules/esm/loader:528:22)
    at importModuleDynamically (node:internal/modules/esm/translators:110:35)
    at importModuleDynamicallyCallback (node:internal/process/esm_loader:35:14) {
  code: 'ERR_MODULE_NOT_FOUND'
}

exports config from package.json

"exports": {
  ".": "./lib/eslintBaseConfig.json",
  "./browser": "./lib/eslintBrowserConfig.json",
  "./jest": "./lib/eslintJestConfig.json",
  "./node": "./lib/eslintNodeConfig.json",
  "./preact": "./lib/eslintPreactConfig.json",
  "./preact-typescript": "./lib/eslintPreactTypescriptConfig.json",
  "./react": "./lib/eslintReactConfig.json",
  "./react-typescript": "./lib/eslintReactTypescriptConfig.json",
  "./typescript": "./lib/eslintTypescriptConfig.json"
},

My repo does not have an index.js file, and as you can see above, my root export is defined in exports and comes from a JSON file (./lib/eslintBaseConfig.json). ESLint has no problem consuming this library, and I plan to open source it using your tool to improve my documentation. Any help you could provide would be greatly appreciated.

mangs avatar Oct 14 '22 15:10 mangs

Thanks for the report! I'll definitely plan to support exports ASAP.

bmish avatar Oct 14 '22 17:10 bmish

No worries. I'm happy to work with you to get it working if that would help.

mangs avatar Oct 14 '22 22:10 mangs

I currently have some manual logic for importing the eslint plugin package entry point which only handles main. We could fix it by adding logic to handle exports too: https://github.com/bmish/eslint-doc-generator/blob/ed310e9d0177a772b19c8390b23c33352cf45882/lib/package-json.ts#L27

However, this issue has made me realize that there must be a simpler technique for importing a local package without having to implement all the entry point logic...

bmish avatar Oct 14 '22 23:10 bmish

Hmm, interesting. I know ESLint supports my setup just fine, so unless I'm doing something wrong and ESLint is just super-accepting of incorrect setups, let's look at what ESLint is doing for handling imports. I've found the exact same problem with Stylelint (I haven't filed an issue there yet; I have a Stylelint library too), so I'm sure you're not the only one asking this question.

mangs avatar Oct 16 '22 16:10 mangs

From reading through the documentation on handling packages in Node.js, it should be handling this "automatically" when you import a package path. Perhaps I'm missing something?

mangs avatar Oct 16 '22 16:10 mangs

To be clear, you're not doing anything wrong, we definitely want to handle both CJS/ESM ESLint plugins (using either main or exports).

Node will automatically find/load a package's entry point when importing it with require('package-name') or import('package-name').

But in this tool, we are not importing an ESLint plugin via its package name. Instead, we're dealing with a local package on the filesystem that might not even be published, and we just have the path to this package. So the code I linked above manually looks at package.json at that file path to find the entry point to load.

So if we could find a way to automatically load a local package (which could be CJS or ESM) via its file path, that would solve the problem. Passing a path to import(path) won't work because that tries to load a specific file path instead of resolve the package entry point.

bmish avatar Oct 16 '22 17:10 bmish

A few more questions:

  1. Does your package have main defined in package.json?
  2. Does your package have type defined in package.json?
  3. Anything else about ESLint defined in your package.json?
  4. Is your package written in CJS or ESM?
  5. Is there any compilation step in your plugin?

bmish avatar Oct 16 '22 17:10 bmish

Can you also show me how your ESLint plugin/config is used?

bmish avatar Oct 16 '22 18:10 bmish

A few more questions:

1. Does your package have `main` defined in package.json?

2. Does your package have `type` defined in package.json?

3. Anything else about ESLint defined in your package.json?

4. Is your package written in CJS or ESM?

5. Is there any compilation step in your plugin?

Replies by number:

  1. No
  2. No
  3. No
  4. Neither, it's only JSON files
  5. No

As you can see in the exports list, there are quite a few different ESLint configs; they are meant to be layerable. For example, if you're running a browser environment with Preact, you can use the browser and preact configs simultaneously:

"extends": ["@lessonnine/usa-tech.js/preact", "@lessonnine/usa-tech.js/browser"]

or for Node.js, TypeScript, and Jest:

"extends": ["@lessonnine/usa-tech.js/typescript", "@lessonnine/usa-tech.js/jest", "@lessonnine/usa-tech.js/node"]

Hope that makes sense. It's meant to be a library of configs that are easy to pickup and use regardless of your use case.

mangs avatar Oct 17 '22 22:10 mangs

Thanks for the details. I may have more questions later as I work on a fix.

bmish avatar Oct 19 '22 02:10 bmish