diff --git a/CHANGELOG.md b/CHANGELOG.md index 648e481df..42db4af4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel ### Changed - [`no-extraneous-dependencies`]: use `read-pkg-up` to simplify finding + loading `package.json` ([#680], thanks [@wtgtybhertgeghgtwtg]) +- Add support to specify the package.json [`no-extraneous-dependencies`] ([#685], thanks [@ramasilveyra]) ### Fixed - attempt to fix crash in [`no-mutable-exports`]. ([#660]) @@ -385,6 +386,7 @@ for info on changes for earlier releases. [#742]: https://github.com/benmosher/eslint-plugin-import/pull/742 [#712]: https://github.com/benmosher/eslint-plugin-import/pull/712 +[#685]: https://github.com/benmosher/eslint-plugin-import/pull/685 [#680]: https://github.com/benmosher/eslint-plugin-import/pull/680 [#654]: https://github.com/benmosher/eslint-plugin-import/pull/654 [#639]: https://github.com/benmosher/eslint-plugin-import/pull/639 @@ -574,3 +576,4 @@ for info on changes for earlier releases. [@duncanbeevers]: https://github.com/duncanbeevers [@giodamelio]: https://github.com/giodamelio [@ntdb]: https://github.com/ntdb +[@ramasilveyra]: https://github.com/ramasilveyra diff --git a/docs/rules/no-extraneous-dependencies.md b/docs/rules/no-extraneous-dependencies.md index adb532e39..3298e266b 100644 --- a/docs/rules/no-extraneous-dependencies.md +++ b/docs/rules/no-extraneous-dependencies.md @@ -1,7 +1,7 @@ # Forbid the use of extraneous packages Forbid the import of external modules that are not declared in the `package.json`'s `dependencies`, `devDependencies`, `optionalDependencies` or `peerDependencies`. -The closest parent `package.json` will be used. If no `package.json` is found, the rule will not lint anything. +The closest parent `package.json` will be used. If no `package.json` is found, the rule will not lint anything. This behaviour can be changed with the rule option `packageDir`. ### Options @@ -27,6 +27,12 @@ You can also use an array of globs instead of literal booleans: When using an array of globs, the setting will be activated if the name of the file being linted matches a single glob in the array. +Also there is one more option called `packageDir`, this option is to specify the path to the folder containing package.json and is relative to the current working directory. + +```js +"import/no-extraneous-dependencies": ["error", {"packageDir": './some-dir/'}] +``` + ## Rule Details Given the following `package.json`: diff --git a/src/rules/no-extraneous-dependencies.js b/src/rules/no-extraneous-dependencies.js index 166d4f843..ce5041c97 100644 --- a/src/rules/no-extraneous-dependencies.js +++ b/src/rules/no-extraneous-dependencies.js @@ -1,16 +1,20 @@ import path from 'path' +import fs from 'fs' import readPkgUp from 'read-pkg-up' import minimatch from 'minimatch' import importType from '../core/importType' import isStaticRequire from '../core/staticRequire' -function getDependencies(context) { +function getDependencies(context, packageDir) { try { - const pkg = readPkgUp.sync({cwd: context.getFilename(), normalize: false}) - if (!pkg || !pkg.pkg) { + const packageContent = packageDir + ? JSON.parse(fs.readFileSync(path.join(packageDir, 'package.json'), 'utf8')) + : readPkgUp.sync({cwd: context.getFilename(), normalize: false}).pkg + + if (!packageContent) { return null } - const packageContent = pkg.pkg + return { dependencies: packageContent.dependencies || {}, devDependencies: packageContent.devDependencies || {}, @@ -18,6 +22,19 @@ function getDependencies(context) { peerDependencies: packageContent.peerDependencies || {}, } } catch (e) { + if (packageDir && e.code === 'ENOENT') { + context.report({ + message: 'The package.json file could not be found.', + loc: { line: 0, column: 0 }, + }) + } + if (e.name === 'JSONError' || e instanceof SyntaxError) { + context.report({ + message: 'The package.json file could not be parsed: ' + e.message, + loc: { line: 0, column: 0 }, + }) + } + return null } } @@ -93,6 +110,7 @@ module.exports = { 'devDependencies': { 'type': ['boolean', 'array'] }, 'optionalDependencies': { 'type': ['boolean', 'array'] }, 'peerDependencies': { 'type': ['boolean', 'array'] }, + 'packageDir': { 'type': 'string' }, }, 'additionalProperties': false, }, @@ -102,7 +120,7 @@ module.exports = { create: function (context) { const options = context.options[0] || {} const filename = context.getFilename() - const deps = getDependencies(context) + const deps = getDependencies(context, options.packageDir) if (!deps) { return {} diff --git a/tests/files/with-syntax-error/package.json b/tests/files/with-syntax-error/package.json new file mode 100644 index 000000000..1bb4b63fa --- /dev/null +++ b/tests/files/with-syntax-error/package.json @@ -0,0 +1 @@ +{{ "name": "with-syntax-error" } diff --git a/tests/src/rules/no-extraneous-dependencies.js b/tests/src/rules/no-extraneous-dependencies.js index 8bb632d01..a2e1955ec 100644 --- a/tests/src/rules/no-extraneous-dependencies.js +++ b/tests/src/rules/no-extraneous-dependencies.js @@ -1,11 +1,20 @@ import { test } from '../utils' import * as path from 'path' +import * as fs from 'fs' import { RuleTester } from 'eslint' - const ruleTester = new RuleTester() , rule = require('rules/no-extraneous-dependencies') +const packageDirWithSyntaxError = path.join(__dirname, '../../files/with-syntax-error') +const packageFileWithSyntaxErrorMessage = (() => { + try { + JSON.parse(fs.readFileSync(path.join(packageDirWithSyntaxError, 'package.json'))) + } catch (error) { + return error.message + } +})() + ruleTester.run('no-extraneous-dependencies', rule, { valid: [ test({ code: 'import "lodash.cond"'}), @@ -56,6 +65,10 @@ ruleTester.run('no-extraneous-dependencies', rule, { filename: path.join(process.cwd(), 'foo.spec.js'), }), test({ code: 'require(6)' }), + test({ + code: 'import "doctrine"', + options: [{packageDir: path.join(__dirname, '../../../')}], + }), ], invalid: [ test({ @@ -154,5 +167,29 @@ ruleTester.run('no-extraneous-dependencies', rule, { message: '\'lodash.isarray\' should be listed in the project\'s dependencies, not optionalDependencies.', }], }), + test({ + code: 'import "not-a-dependency"', + options: [{packageDir: path.join(__dirname, '../../../')}], + errors: [{ + ruleId: 'no-extraneous-dependencies', + message: '\'not-a-dependency\' should be listed in the project\'s dependencies. Run \'npm i -S not-a-dependency\' to add it', + }], + }), + test({ + code: 'import "bar"', + options: [{packageDir: path.join(__dirname, './doesn-exist/')}], + errors: [{ + ruleId: 'no-extraneous-dependencies', + message: 'The package.json file could not be found.', + }], + }), + test({ + code: 'import foo from "foo"', + options: [{packageDir: packageDirWithSyntaxError}], + errors: [{ + ruleId: 'no-extraneous-dependencies', + message: 'The package.json file could not be parsed: ' + packageFileWithSyntaxErrorMessage, + }], + }), ], })