From d23574c5c0275c8b3714a7a6d3e8bf2108af60f1 Mon Sep 17 00:00:00 2001 From: benj-dobs <35073256+benj-dobs@users.noreply.github.com> Date: Mon, 13 May 2024 10:21:26 +0100 Subject: [PATCH 1/3] docs: Clarify usage of `no-unreachable` with TypeScript (#18445) docs: Clarify usage of no-unreachable with TypeScript Fixes: #18378 --- docs/src/rules/no-unreachable.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/src/rules/no-unreachable.md b/docs/src/rules/no-unreachable.md index 15f77f8173e..8d95cbf6093 100644 --- a/docs/src/rules/no-unreachable.md +++ b/docs/src/rules/no-unreachable.md @@ -2,6 +2,10 @@ title: no-unreachable rule_type: problem handled_by_typescript: true +extra_typescript_info: >- + TypeScript must be configured with + [`allowUnreachableCode: false`](https://www.typescriptlang.org/tsconfig#allowUnreachableCode) + for it to consider unreachable code an error. --- From 2465a1e3f3b78f302f64e62e5f0d851626b81b3c Mon Sep 17 00:00:00 2001 From: GitHub Actions Bot Date: Tue, 14 May 2024 08:08:10 +0000 Subject: [PATCH 2/3] docs: Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5e7268339d1..917892d672a 100644 --- a/README.md +++ b/README.md @@ -296,7 +296,7 @@ The following companies, organizations, and individuals support ESLint's ongoing

Platinum Sponsors

Automattic

Gold Sponsors

Salesforce Airbnb

Silver Sponsors

-

JetBrains Liftoff American Express Workleap

Bronze Sponsors

+

JetBrains Liftoff American Express Workleap

Bronze Sponsors

notion Anagram Solver Icons8 Discord Ignition Nx HeroCoders Nextbase Starter Kit

From b67eba4514026ef7e489798fd883beb678817a46 Mon Sep 17 00:00:00 2001 From: Akul Srivastava Date: Tue, 14 May 2024 13:51:44 +0530 Subject: [PATCH 3/3] feat: add `restrictedNamedExportsPattern` to `no-restricted-exports` (#18431) * fix: allow glob patterns for `restrictedNamedExports` in `no-restricted-exports` * chore: update option name * chore: use regex instead of glob patterns * chore: add docs and use set for given regex strings * chore: review changes * chore: add correct/incorrect example doc * chore: review changes * chore: review changes * chore: add default test cases --- docs/src/rules/no-restricted-exports.md | 33 +++++++++ lib/rules/no-restricted-exports.js | 15 +++- tests/lib/rules/no-restricted-exports.js | 89 ++++++++++++++++++++++++ 3 files changed, 135 insertions(+), 2 deletions(-) diff --git a/docs/src/rules/no-restricted-exports.md b/docs/src/rules/no-restricted-exports.md index 44672021a10..446c4fbc337 100644 --- a/docs/src/rules/no-restricted-exports.md +++ b/docs/src/rules/no-restricted-exports.md @@ -17,6 +17,7 @@ By default, this rule doesn't disallow any names. Only the names you specify in This rule has an object option: * `"restrictedNamedExports"` is an array of strings, where each string is a name to be restricted. +* `"restrictedNamedExportsPattern"` is a string representing a regular expression pattern. Named exports matching this pattern will be restricted. This option does not apply to `default` named exports. * `"restrictDefaultExports"` is an object option with boolean properties to restrict certain default export declarations. The option works only if the `restrictedNamedExports` option does not contain the `"default"` value. The following properties are allowed: * `direct`: restricts `export default` declarations. * `named`: restricts `export { foo as default };` declarations. @@ -130,6 +131,38 @@ export default function foo() {} ::: +### restrictedNamedExportsPattern + +Example of **incorrect** code for the `"restrictedNamedExportsPattern"` option: + +::: incorrect + +```js +/*eslint no-restricted-exports: ["error", { + "restrictedNamedExportsPattern": "bar$" +}]*/ + +export const foobar = 1; +``` + +::: + +Example of **correct** code for the `"restrictedNamedExportsPattern"` option: + +::: correct + +```js +/*eslint no-restricted-exports: ["error", { + "restrictedNamedExportsPattern": "bar$" +}]*/ + +export const abc = 1; +``` + +::: + +Note that this option does not apply to `export default` or any `default` named exports. If you want to also restrict `default` exports, use the `restrictDefaultExports` option. + ### restrictDefaultExports This option allows you to restrict certain `default` declarations. The option works only if the `restrictedNamedExports` option does not contain the `"default"` value. This option accepts the following properties: diff --git a/lib/rules/no-restricted-exports.js b/lib/rules/no-restricted-exports.js index a1d54b085fd..8da2f2dfe01 100644 --- a/lib/rules/no-restricted-exports.js +++ b/lib/rules/no-restricted-exports.js @@ -37,7 +37,8 @@ module.exports = { type: "string" }, uniqueItems: true - } + }, + restrictedNamedExportsPattern: { type: "string" } }, additionalProperties: false }, @@ -52,6 +53,7 @@ module.exports = { }, uniqueItems: true }, + restrictedNamedExportsPattern: { type: "string" }, restrictDefaultExports: { type: "object", properties: { @@ -98,6 +100,7 @@ module.exports = { create(context) { const restrictedNames = new Set(context.options[0] && context.options[0].restrictedNamedExports); + const restrictedNamePattern = context.options[0] && context.options[0].restrictedNamedExportsPattern; const restrictDefaultExports = context.options[0] && context.options[0].restrictDefaultExports; const sourceCode = context.sourceCode; @@ -109,7 +112,15 @@ module.exports = { function checkExportedName(node) { const name = astUtils.getModuleExportName(node); - if (restrictedNames.has(name)) { + let matchesRestrictedNamePattern = false; + + if (restrictedNamePattern && name !== "default") { + const patternRegex = new RegExp(restrictedNamePattern, "u"); + + matchesRestrictedNamePattern = patternRegex.test(name); + } + + if (matchesRestrictedNamePattern || restrictedNames.has(name)) { context.report({ node, messageId: "restrictedNamed", diff --git a/tests/lib/rules/no-restricted-exports.js b/tests/lib/rules/no-restricted-exports.js index cea7c046694..aab1043fc70 100644 --- a/tests/lib/rules/no-restricted-exports.js +++ b/tests/lib/rules/no-restricted-exports.js @@ -91,6 +91,50 @@ ruleTester.run("no-restricted-exports", rule, { { code: "import a from 'foo';", options: [{ restrictedNamedExports: ["a"] }] }, { code: "import { a } from 'foo';", options: [{ restrictedNamedExports: ["a"] }] }, { code: "import { b as a } from 'foo';", options: [{ restrictedNamedExports: ["a"] }] }, + { + code: "var setSomething; export { setSomething };", + options: [{ restrictedNamedExportsPattern: "^get" }] + }, + { + code: "var foo, bar; export { foo, bar };", + options: [{ restrictedNamedExportsPattern: "^(?!foo)(?!bar).+$" }] + }, + { + code: "var foobar; export default foobar;", + options: [{ restrictedNamedExportsPattern: "bar$" }] + }, + { + code: "var foobar; export default foobar;", + options: [{ restrictedNamedExportsPattern: "default" }] + }, + { + code: "export default 'default';", + options: [{ restrictedNamedExportsPattern: "default" }] + }, + { + code: "var foobar; export { foobar as default };", + options: [{ restrictedNamedExportsPattern: "default" }] + }, + { + code: "var foobar; export { foobar as 'default' };", + options: [{ restrictedNamedExportsPattern: "default" }] + }, + { + code: "export { default } from 'mod';", + options: [{ restrictedNamedExportsPattern: "default" }] + }, + { + code: "export { default as default } from 'mod';", + options: [{ restrictedNamedExportsPattern: "default" }] + }, + { + code: "export { foobar as default } from 'mod';", + options: [{ restrictedNamedExportsPattern: "default" }] + }, + { + code: "export * as default from 'mod';", + options: [{ restrictedNamedExportsPattern: "default" }] + }, // does not check re-export all declarations { code: "export * from 'foo';", options: [{ restrictedNamedExports: ["a"] }] }, @@ -533,6 +577,51 @@ ruleTester.run("no-restricted-exports", rule, { ] }, + // restrictedNamedExportsPattern + { + code: "var getSomething; export { getSomething };", + options: [{ restrictedNamedExportsPattern: "get*" }], + errors: [ + { messageId: "restrictedNamed", data: { name: "getSomething" }, type: "Identifier" } + ] + }, + { + code: "var getSomethingFromUser; export { getSomethingFromUser };", + options: [{ restrictedNamedExportsPattern: "User$" }], + errors: [ + { messageId: "restrictedNamed", data: { name: "getSomethingFromUser" }, type: "Identifier" } + ] + }, + { + code: "var foo, ab, xy; export { foo, ab, xy };", + options: [{ restrictedNamedExportsPattern: "(b|y)$" }], + errors: [ + { messageId: "restrictedNamed", data: { name: "ab" }, type: "Identifier" }, + { messageId: "restrictedNamed", data: { name: "xy" }, type: "Identifier" } + ] + }, + { + code: "var foo; export { foo as ab };", + options: [{ restrictedNamedExportsPattern: "(b|y)$" }], + errors: [ + { messageId: "restrictedNamed", data: { name: "ab" }, type: "Identifier" } + ] + }, + { + code: "var privateUserEmail; export { privateUserEmail };", + options: [{ restrictedNamedExportsPattern: "^privateUser" }], + errors: [ + { messageId: "restrictedNamed", data: { name: "privateUserEmail" }, type: "Identifier" } + ] + }, + { + code: "export const a = 1;", + options: [{ restrictedNamedExportsPattern: "^(?!foo)(?!bar).+$" }], + errors: [ + { messageId: "restrictedNamed", data: { name: "a" }, type: "Identifier" } + ] + }, + // reports "default" in named export declarations (when configured) { code: "var a; export { a as default };",