Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New: Add no-restricted-exports rule (fixes #10428) #12546

Merged
merged 3 commits into from Dec 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 @@ -175,6 +175,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);
}
}
};
}
};