From 0609431dabcd9402720071025c0206d2686e1d78 Mon Sep 17 00:00:00 2001 From: Dennis Kugelmann Date: Fri, 14 Jun 2024 01:39:35 +0000 Subject: [PATCH] feat: Add flag ignorePrivate to no-unpublished-x rules (#298) * feat: Add flag ignorePrivate to no-unpublished-x rules This rule make it possible to selectively disable/enable the rule in private packages * docs: Add documentation of ignorePrivate * Simplify property access and fallback --- docs/rules/no-unpublished-import.md | 28 +++++++++++++++++++++++ docs/rules/no-unpublished-require.md | 28 +++++++++++++++++++++++ lib/rules/no-unpublished-import.js | 9 ++++---- lib/rules/no-unpublished-require.js | 6 ++++- lib/util/check-publish.js | 14 +++++++++--- tests/lib/rules/no-unpublished-import.js | 8 +++++++ tests/lib/rules/no-unpublished-require.js | 8 +++++++ 7 files changed, 92 insertions(+), 9 deletions(-) diff --git a/docs/rules/no-unpublished-import.md b/docs/rules/no-unpublished-import.md index 60b867fd..ad2094e9 100644 --- a/docs/rules/no-unpublished-import.md +++ b/docs/rules/no-unpublished-import.md @@ -66,6 +66,34 @@ In this way, the following code will not be reported: import type foo from "foo"; ``` +### ignorePrivate + +In a private package you sometimes want to disable checking for unpublished dependencies, e.g. if the package is not published. + +However, there are situations where you want to mark it as private, though still ensure only published dependencies are used in your source code. +An example, for such a case would be a package that is deployed to a server. + +Defaults to `true`. + +package.json: + +```json +{ + "private": true, + ... +} +``` + +```json +{ + "rules": { + "n/no-unpublished-import": ["error", { + "ignorePrivate": true + }] + } +} +``` + ## 🔎 Implementation - [Rule source](../../lib/rules/no-unpublished-import.js) diff --git a/docs/rules/no-unpublished-require.md b/docs/rules/no-unpublished-require.md index bfbdfa69..cfd7fff6 100644 --- a/docs/rules/no-unpublished-require.md +++ b/docs/rules/no-unpublished-require.md @@ -53,6 +53,34 @@ Please see the shared settings documentation for more information. This can be configured in the rule options or as a shared setting [`settings.tryExtensions`](../shared-settings.md#tryextensions). Please see the shared settings documentation for more information. +### ignorePrivate + +In a private package you sometimes want to disable checking for unpublished dependencies, e.g. if the package is not published. + +However, there are situations where you want to mark it as private, though still ensure only published dependencies are used in your source code. +An example, for such a case would be a package that is deployed to a server. + +Defaults to `true`. + +package.json: + +```json +{ + "private": true, + ... +} +``` + +```json +{ + "rules": { + "n/no-unpublished-import": ["error", { + "ignorePrivate": true + }] + } +} +``` + ## 🔎 Implementation - [Rule source](../../lib/rules/no-unpublished-require.js) diff --git a/lib/rules/no-unpublished-import.js b/lib/rules/no-unpublished-import.js index 90166940..70911783 100644 --- a/lib/rules/no-unpublished-import.js +++ b/lib/rules/no-unpublished-import.js @@ -29,6 +29,7 @@ module.exports = { convertPath: getConvertPath.schema, resolvePaths: getResolvePaths.schema, ignoreTypeImport: { type: "boolean", default: false }, + ignorePrivate: { type: "boolean", default: true }, }, additionalProperties: false, }, @@ -38,17 +39,15 @@ module.exports = { create(context) { const filePath = context.filename ?? context.getFilename() const options = context.options[0] || {} - const ignoreTypeImport = - options.ignoreTypeImport === void 0 - ? false - : options.ignoreTypeImport + const ignoreTypeImport = options.ignoreTypeImport ?? false + const ignorePrivate = options.ignorePrivate ?? true if (filePath === "") { return {} } return visitImport(context, { ignoreTypeImport }, targets => { - checkPublish(context, filePath, targets) + checkPublish(context, filePath, targets, { ignorePrivate }) }) }, } diff --git a/lib/rules/no-unpublished-require.js b/lib/rules/no-unpublished-require.js index e9edcf60..a5f70bcf 100644 --- a/lib/rules/no-unpublished-require.js +++ b/lib/rules/no-unpublished-require.js @@ -30,6 +30,7 @@ module.exports = { convertPath: getConvertPath.schema, resolvePaths: getResolvePaths.schema, tryExtensions: getTryExtensions.schema, + ignorePrivate: { type: "boolean", default: true }, }, additionalProperties: false, }, @@ -38,12 +39,15 @@ module.exports = { }, create(context) { const filePath = context.filename ?? context.getFilename() + const options = context.options[0] || {} + const ignorePrivate = options.ignorePrivate ?? true + if (filePath === "") { return {} } return visitRequire(context, {}, targets => { - checkPublish(context, filePath, targets) + checkPublish(context, filePath, targets, { ignorePrivate }) }) }, } diff --git a/lib/util/check-publish.js b/lib/util/check-publish.js index 2a10b9d7..ff27486e 100644 --- a/lib/util/check-publish.js +++ b/lib/util/check-publish.js @@ -18,17 +18,25 @@ const { getPackageJson } = require("./get-package-json") * @param {import('eslint').Rule.RuleContext} context - A context to report. * @param {string} filePath - The current file path. * @param {import('./import-target.js')[]} targets - A list of target information to check. + * @param {{ignorePrivate: boolean}} options - Configuration options for checking for published files. * @returns {void} */ -exports.checkPublish = function checkPublish(context, filePath, targets) { +exports.checkPublish = function checkPublish( + context, + filePath, + targets, + options +) { const packageJson = getPackageJson(filePath) if (typeof packageJson?.filePath !== "string") { return } - // Private packages are never published so we don't need to check the imported dependencies either. + // Flag to ignore checking imported dependencies in private packages. + // For projects that need to be deployed to a server checking for imported dependencies may still be desireable + // while making it a private package. // More information: https://docs.npmjs.com/cli/v8/configuring-npm/package-json#private - if (packageJson.private === true) { + if (options.ignorePrivate && packageJson.private === true) { return } diff --git a/tests/lib/rules/no-unpublished-import.js b/tests/lib/rules/no-unpublished-import.js index 7197c969..c56fb691 100644 --- a/tests/lib/rules/no-unpublished-import.js +++ b/tests/lib/rules/no-unpublished-import.js @@ -292,5 +292,13 @@ ruleTester.run("no-unpublished-import", rule, { code: "import type foo from 'foo';", errors: [{ messageId: "notPublished" }], }, + + // devDependency in a private package + { + filename: fixture("private-package/index.js"), + code: "import bbb from 'bbb';", + errors: ['"bbb" is not published.'], + options: [{ ignorePrivate: false }], + }, ], }) diff --git a/tests/lib/rules/no-unpublished-require.js b/tests/lib/rules/no-unpublished-require.js index b33d8c9d..664723ea 100644 --- a/tests/lib/rules/no-unpublished-require.js +++ b/tests/lib/rules/no-unpublished-require.js @@ -359,5 +359,13 @@ ruleTester.run("no-unpublished-require", rule, { code: "require('../2/a.js');", errors: ['"../2/a.js" is not published.'], }, + + // devDependency in a private package + { + filename: fixture("private-package/index.js"), + code: "require('bbb');", + errors: ['"bbb" is not published.'], + options: [{ ignorePrivate: false }], + }, ], })