Skip to content

Commit

Permalink
feat: add restrictedNamedExportsPattern to no-restricted-exports (#…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
akulsr0 committed May 14, 2024
1 parent 2465a1e commit b67eba4
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 2 deletions.
33 changes: 33 additions & 0 deletions docs/src/rules/no-restricted-exports.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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:
Expand Down
15 changes: 13 additions & 2 deletions lib/rules/no-restricted-exports.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ module.exports = {
type: "string"
},
uniqueItems: true
}
},
restrictedNamedExportsPattern: { type: "string" }
},
additionalProperties: false
},
Expand All @@ -52,6 +53,7 @@ module.exports = {
},
uniqueItems: true
},
restrictedNamedExportsPattern: { type: "string" },
restrictDefaultExports: {
type: "object",
properties: {
Expand Down Expand Up @@ -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;

Expand All @@ -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",
Expand Down
89 changes: 89 additions & 0 deletions tests/lib/rules/no-restricted-exports.js
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }] },
Expand Down Expand Up @@ -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 };",
Expand Down

0 comments on commit b67eba4

Please sign in to comment.