Skip to content

Commit

Permalink
New: Add no-restricted-exports rule (fixes #10428) (#12546)
Browse files Browse the repository at this point in the history
* New: Add no-restricted-exports rule (fixes #10428)

* Remove `fixable` from meta

* Change configuration
  • Loading branch information
mdjermanovic authored and kaicataldo committed Dec 23, 2019
1 parent 1aff21b commit 985dac3
Show file tree
Hide file tree
Showing 5 changed files with 651 additions and 0 deletions.
106 changes: 106 additions & 0 deletions docs/rules/no-restricted-exports.md
@@ -0,0 +1,106 @@
# Disallow specified names in exports (no-restricted-exports)

In a project, certain names may be disallowed from being used as exported names for various reasons.

## Rule Details

This rule disallows specified names from being used as exported names.

## Options

By default, this rule doesn't disallow any names. Only the names you specify in the configuration will be disallowed.

This rule has an object option:

* `"restrictedNamedExports"` is an array of strings, where each string is a name to be restricted.

Examples of **incorrect** code for this rule:

```js
/*eslint no-restricted-exports: ["error", {
"restrictedNamedExports": ["foo", "bar", "Baz", "a", "b", "c", "d"]
}]*/

export const foo = 1;

export function bar() {}

export class Baz {}

const a = {};
export { a };

function someFunction() {}
export { someFunction as b };

export { c } from 'some_module';

export { something as d } from 'some_module';
```

Examples of **correct** code for this rule:

```js
/*eslint no-restricted-exports: ["error", {
"restrictedNamedExports": ["foo", "bar", "Baz", "a", "b", "c", "d"]
}]*/

export const quux = 1;

export function myFunction() {}

export class MyClass {}

const a = {};
export { a as myObject };

function someFunction() {}
export { someFunction };

export { c as someName } from 'some_module';

export { something } from 'some_module';
```

### Default exports

By design, this rule doesn't disallow `export default` declarations. If you configure `"default"` as a restricted name, that restriction will apply only to named export declarations.

Examples of additional **incorrect** code for this rule:

```js
/*eslint no-restricted-exports: ["error", { "restrictedNamedExports": ["default"] }]*/

function foo() {}

export { foo as default };
```

```js
/*eslint no-restricted-exports: ["error", { "restrictedNamedExports": ["default"] }]*/

export { default } from 'some_module';
```

Examples of additional **correct** code for this rule:

```js
/*eslint no-restricted-exports: ["error", { "restrictedNamedExports": ["default", "foo"] }]*/

export default function foo() {}
```

## Known Limitations

This rule doesn't inspect the content of source modules in re-export declarations. In particular, if you are re-exporting everything from another module's export, that export may include a restricted name. This rule cannot detect such cases.

```js

//----- some_module.js -----
export function foo() {}

//----- my_module.js -----
/*eslint no-restricted-exports: ["error", { "restrictedNamedExports": ["foo"] }]*/

export * from 'some_module'; // allowed, although this declaration exports "foo" from my_module
```
1 change: 1 addition & 0 deletions lib/rules/index.js
Expand Up @@ -178,6 +178,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
"no-prototype-builtins": () => require("./no-prototype-builtins"),
"no-redeclare": () => require("./no-redeclare"),
"no-regex-spaces": () => require("./no-regex-spaces"),
"no-restricted-exports": () => require("./no-restricted-exports"),
"no-restricted-globals": () => require("./no-restricted-globals"),
"no-restricted-imports": () => require("./no-restricted-imports"),
"no-restricted-modules": () => require("./no-restricted-modules"),
Expand Down
84 changes: 84 additions & 0 deletions lib/rules/no-restricted-exports.js
@@ -0,0 +1,84 @@
/**
* @fileoverview Rule to disallow specified names in exports
* @author Milos Djermanovic
*/

"use strict";

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = {
meta: {
type: "suggestion",

docs: {
description: "disallow specified names in exports",
category: "ECMAScript 6",
recommended: false,
url: "https://eslint.org/docs/rules/no-restricted-exports"
},

schema: [{
type: "object",
properties: {
restrictedNamedExports: {
type: "array",
items: {
type: "string"
},
uniqueItems: true
}
},
additionalProperties: false
}],

messages: {
restrictedNamed: "'{{name}}' is restricted from being used as an exported name."
}
},

create(context) {

const restrictedNames = new Set(context.options[0] && context.options[0].restrictedNamedExports);

/**
* Checks and reports given exported identifier.
* @param {ASTNode} node exported `Identifer` node to check.
* @returns {void}
*/
function checkExportedName(node) {
const name = node.name;

if (restrictedNames.has(name)) {
context.report({
node,
messageId: "restrictedNamed",
data: { name }
});
}
}

return {
ExportNamedDeclaration(node) {
const declaration = node.declaration;

if (declaration) {
if (declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration") {
checkExportedName(declaration.id);
} else if (declaration.type === "VariableDeclaration") {
context.getDeclaredVariables(declaration)
.map(v => v.defs.find(d => d.parent === declaration))
.map(d => d.name) // Identifier nodes
.forEach(checkExportedName);
}
} else {
node.specifiers
.map(s => s.exported)
.forEach(checkExportedName);
}
}
};
}
};

0 comments on commit 985dac3

Please sign in to comment.