diff --git a/docs/rules/no-namespace.md b/docs/rules/no-namespace.md index e0b0f0b967..18cd543cfb 100644 --- a/docs/rules/no-namespace.md +++ b/docs/rules/no-namespace.md @@ -13,6 +13,12 @@ Valid: import defaultExport from './foo' import { a, b } from './bar' import defaultExport, { a, b } from './foobar' + +// { allow: ['foo'] } +import * as foo from 'foo'; + +// { forbid: ['foo'] } +import * as bar from 'bar'; ``` Invalid: @@ -25,6 +31,22 @@ import * as foo from 'foo'; import defaultExport, * as foo from 'foo'; ``` +```js +// { allow: ['foo'] } +import * as bar from 'bar'; + +// { forbid: ['foo'] } +import * as foo from 'foo'; +``` + +### Options + +This rule takes the following options: + +`allow`: an array of namespaces the rule allows + +`forbid`: an array of namespaces the rule forbids + ## When Not To Use It If you want to use namespaces, you don't want to use this rule. diff --git a/src/rules/no-namespace.js b/src/rules/no-namespace.js index 0b63132508..e84d92ad81 100644 --- a/src/rules/no-namespace.js +++ b/src/rules/no-namespace.js @@ -17,7 +17,30 @@ module.exports = { url: docsUrl('no-namespace'), }, fixable: 'code', - schema: [], + schema: [ + { + oneOf: [ + { + type: "object", + properties: { + allow: { + type: "array" + } + }, + additionalProperties: false + }, + { + type: "object", + properties: { + forbid: { + type: "array" + } + }, + additionalProperties: false + } + ] + } + ] }, create: function (context) { @@ -31,6 +54,16 @@ module.exports = { const namespaceIdentifiers = namespaceReferences.map(reference => reference.identifier) const canFix = namespaceIdentifiers.length > 0 && !usesNamespaceAsObject(namespaceIdentifiers) + const config = context.options[0] || { + allow: [], + }; + + const [type, list] = Array.isArray(config.forbid) + ? ['forbid', config.forbid] + : ['allow', config.allow]; + + if (!shouldReport(type, list, namespaceVariable.name)) { return; } + context.report({ node, message: `Unexpected namespace import.`, @@ -157,3 +190,17 @@ function generateLocalNames(names, nameConflicts, namespaceName) { }) return localNames } + +/** + * + * @param {'forbid' | 'allow'} type + * @param {string[]} list + * @param {string} name + */ +function shouldReport(type, list, name) { + if (list.includes(name)) { + return type === 'forbid'; + } + + return type === 'allow'; +} diff --git a/tests/src/rules/no-namespace.js b/tests/src/rules/no-namespace.js index a7cb4dd21f..ae062cee24 100644 --- a/tests/src/rules/no-namespace.js +++ b/tests/src/rules/no-namespace.js @@ -78,6 +78,8 @@ ruleTester.run('no-namespace', require('rules/no-namespace'), { { code: 'import { a, b } from \'./foo\';', parserOptions: { ecmaVersion: 2015, sourceType: 'module' } }, { code: 'import bar from \'bar\';', parserOptions: { ecmaVersion: 2015, sourceType: 'module' } }, { code: 'import bar from \'./bar\';', parserOptions: { ecmaVersion: 2015, sourceType: 'module' } }, + { code: 'import * as bar from \'bar\';', parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, options: [{ allow: ['bar']}] }, + { code: 'import bar from \'bar\';', parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, options: [{ forbid: ['bar']}] }, ], invalid: [ @@ -108,6 +110,34 @@ ruleTester.run('no-namespace', require('rules/no-namespace'), { message: ERROR_MESSAGE, } ], }), + test({ + code: 'import * as foo from \'./foo\';', + output: 'import * as foo from \'./foo\';', + options: [ + { + forbid: ['foo'] + } + ], + errors: [ { + line: 1, + column: 8, + message: ERROR_MESSAGE, + } ], + }), + test({ + code: 'import * as foo from \'./foo\';', + output: 'import * as foo from \'./foo\';', + options: [ + { + allow: ['bar'] + } + ], + errors: [ { + line: 1, + column: 8, + message: ERROR_MESSAGE, + } ], + }), ...FIX_TESTS, ], })