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

Enhancement: Monorepos and no-extraneous-dependencies #1174

Closed
aravindet opened this issue Sep 28, 2018 · 23 comments
Closed

Enhancement: Monorepos and no-extraneous-dependencies #1174

aravindet opened this issue Sep 28, 2018 · 23 comments

Comments

@aravindet
Copy link

A common configuration for monorepos, popularized by Lerna and Yarn workspaces, is

root
+- package.json
+- packages
  +- a
  |  +- package.json
  |  +- index.js
  +- b
  |  +- package.json
  |  +- index.js

The expected behaviour (especially for devDependencies) is that modules required in a/index.js must be specified in either a/package.json or in root/package.json; specifying it in b/package.json should not matter.

Currently, there does not seem to be a way to accomplish this, as the packageDir option is relative to the project root and is unaffected by the location of the file is being checked.

I propose that the default behaviour (when no packageDir is specified) be the following:

  • From the directory of the file being checked, look for ./package.json, ../package.json, ... until the current working directory (or project root) is reached.
  • Merge all the discovered deps, devDeps, peerDeps and optDeps
  • Check imports against this.

This matches node's module resolution algorithm, which tries to resolve require(foo) in ./node_modules/foo, ../node_modules/foo, ../../node_modules/foo, etc. until it reaches the filesystem root or foo is found. Even if an intermediate level contains a node_modules directory, if it does not contain the module foo, node continues the search.

@alexindigo
Copy link

We're seeing same issue in our code base. Any suggestion how to fix it?

@nmccready
Copy link

@dawidk92
Copy link

Any updates on this issue?

@marcinlanger
Copy link

For anyone in need, I have figured out some sort of workaround using overrides.
The project is using a mono repo utilizing yarn workspaces, but this should work with lerna and other tools as well.

Repository:

root
+- package.json (workspaces: ["packages/*"])
+- .eslintrc.js
+- packages
    +- a
        +- package.json
    +- b
        +- package.json

From no-extraneous-dependencies documentation:

Also there is one more option called packageDir, this option is to specify the path to the folder containing package.json.

Since we know the package paths (from package.json workspaces or from lerna.json) and .eslintrc.js is in the root, we can use this to loop through package paths and add an override for each of them, adding the root path and the package path to packageDir.

Here is an example config. Obviously you could leverage the glob patterns from your mono repo configuration instead of hardcoding one packages folder to make this work with more complex folder structures.

const { resolve } = require('path');
const { readdirSync, lstatSync } = require('fs');

const PACKAGE_DIR = 'packages/'; // this could be replaced utilizing the globs in package.json's "workpackges" or from the lerna.json config

// get files in packages
const noExtraneousOverrides = readdirSync(resolve(__dirname, PACKAGE_DIR))
	// filter for non-hidden dirs to get a list of packages
	.filter(
		entry =>
			entry.substr(0, 1) !== '.' && lstatSync(resolve(__dirname, PACKAGE_DIR, entry)).isDirectory(),
	)
	// map to override rules pointing to local and root package.json for rule
	.map(entry => ({
		files: [`${PACKAGE_DIR}${entry}/**/*`],
		rules: {
			'import/no-extraneous-dependencies': [
				'error',
				{
					devDependencies: true,
					optionalDependencies: false,
					peerDependencies: false,
					packageDir: [__dirname, resolve(__dirname, PACKAGE_DIR, entry)],
				},
			],
		},
	}));

module.exports = {
	extends: 'eslint-config-acme',
	overrides: [...noExtraneousOverrides],
};

@dietergeerts
Copy link

I'm bumping on to this too. This could be solved if the packageDir option would allow a glob, so we can define all our packages with 1 string, instead of needing to list them all.

@paztis
Copy link

paztis commented Apr 9, 2020

Hi all
I've create a PR (#1696) to correctly Work with monorepo.
It does the internal / external detection on the resolved moduelpath instead of the of the hypothetic name.
This means it also correctly work with animy resolver, because it only consider the resolved path. It resolves a lot of problematic cases.

But I've no news about the merge of it...

You can try it from my branch if you want.

@roastlechon
Copy link

hi @paztis, do you have a published temporary module with the changes?

@paztis
Copy link

paztis commented Jun 25, 2020 via email

@ljharb
Copy link
Member

ljharb commented Jun 25, 2020

@paztis I've reviewed your PR and asked you some questions; it needs updating.

@paztis
Copy link

paztis commented Jun 25, 2020 via email

@francisminu
Copy link

When is this planned to be released?

@paztis
Copy link

paztis commented Jul 29, 2020

I do the code review fixes and sync with master.
Just need your last status now

@egemon
Copy link

egemon commented Oct 21, 2020

Hi guys! Any updates?

@paztis
Copy link

paztis commented Oct 21, 2020

I'm still waiting my PR is approved.
The repo authors are not replying since a moment.

It might be better to post on the PR page for info about it:
#1696

@fkoner
Copy link

fkoner commented Jan 28, 2021

¿Hi @benmosher @paztis @ljharb Any updates?

Thank in advance...

@ljharb
Copy link
Member

ljharb commented Jan 28, 2021

@fkoner as you can see, the linked PR has been merged, but not yet released. As such, I'll close this.

@paztis
Copy link

paztis commented Mar 18, 2021

The PR has been merge 2 months ago but there's still no release on it.
Any idea when it will be available ?

@ljharb
Copy link
Member

ljharb commented Mar 18, 2021

When I resolve #1986.

@marcospgp
Copy link

It seems like there has been a lot of activity mentioning this issue, has it made it into a release yet?

I am seeing an error when importing from a package.json that is one directory above the nearest one, so it seems not, but maybe I should update my yarn.lock.

@ljharb
Copy link
Member

ljharb commented Jun 21, 2023

Yes, in >= v2.23.0 802ce7d

@marcospgp
Copy link

marcospgp commented Jun 22, 2023

@ljharb But the documentation still seems to imply that only the nearest package.json will be looked at 🧐 is it outdated?

https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-extraneous-dependencies.md?plain=1#L6

@beanaroo
Copy link

Yes, in >= v2.23.0 802ce7d

We still face this error when using v2.27.5 with npm workspaces when dependencies are declared at the root package.json level. Is this worth opening a separate issue?

@awdyson
Copy link

awdyson commented Jul 26, 2023

I've got a main file + file-per-package setup like this:

/* src/package/some-package */

// @ts-check
/** @type {import('eslint-define-config').ESLintConfig} */
module.exports = {
  extends: ['../../.eslintrc.cjs'],
  parserOptions: {
    project: ['./tsconfig.json'],
  },
  rules: {
    'import/no-extraneous-dependencies': [
      1,
      {
        devDependencies: false,
        includeInternal: false,
        includeTypes: false,
        packageDir: ['.', '../..'], // <--- the key addition
      },
    ],
  },
};

Plus this seemed to be the easiest way to set the project for TS to lower processing time.

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

No branches or pull requests