From 9f525b057d7636aed3367f31fb88dbd80b4fa8f7 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Sat, 14 Jan 2023 14:52:18 +0530 Subject: [PATCH] feat: add `restrictDefaultExports` option to the `no-restricted-exports` rule --- lib/rules/no-restricted-exports.js | 49 +++++++++++++++++++++- tests/lib/rules/no-restricted-exports.js | 53 +++++++++++++++++++++++- 2 files changed, 98 insertions(+), 4 deletions(-) diff --git a/lib/rules/no-restricted-exports.js b/lib/rules/no-restricted-exports.js index d99e8928209b..b88aba18c9db 100644 --- a/lib/rules/no-restricted-exports.js +++ b/lib/rules/no-restricted-exports.js @@ -3,7 +3,6 @@ * @author Milos Djermanovic */ -"use strict"; //------------------------------------------------------------------------------ // Requirements @@ -35,19 +34,51 @@ module.exports = { type: "string" }, uniqueItems: true + }, + restrictDefaultExports: { + type: "object", + properties: { + + // Allow/Disallow `export default foo; export default 42; export default function foo() {}` format + direct: { + type: "boolean" + }, + + // Allow/Disallow `export { foo as default };` format + named: { + type: "boolean" + }, + + // Allow/Disallow `export { default } from "mod"; export { default as default } from "mod";` format + defaultFrom: { + type: "boolean" + }, + + // Allow/Disallow `export { foo as default } from "mod";` format + namedFrom: { + type: "boolean" + }, + + // Allow/Disallow `export * as default from "mod"`; format + namespaceFrom: { + type: "boolean" + } + } } }, additionalProperties: false }], messages: { - restrictedNamed: "'{{name}}' is restricted from being used as an exported name." + restrictedNamed: "'{{name}}' is restricted from being used as an exported name.", + restrictedDefault: "Exporting the 'default' value is restricted." } }, create(context) { const restrictedNames = new Set(context.options[0] && context.options[0].restrictedNamedExports); + const restrictDefaultExports = context.options[0] && !restrictedNames.has("default") && context.options[0].restrictDefaultExports; /** * Checks and reports given exported name. @@ -63,6 +94,11 @@ module.exports = { messageId: "restrictedNamed", data: { name } }); + } else if (name === "default" && restrictDefaultExports && restrictDefaultExports.named) { + context.report({ + node, + messageId: "restrictedDefault" + }); } } @@ -73,6 +109,15 @@ module.exports = { } }, + ExportDefaultDeclaration(node) { + if (restrictDefaultExports && restrictDefaultExports.direct) { + context.report({ + node, + messageId: "restrictedDefault" + }); + } + }, + ExportNamedDeclaration(node) { const declaration = node.declaration; diff --git a/tests/lib/rules/no-restricted-exports.js b/tests/lib/rules/no-restricted-exports.js index 631fd6f02fa3..798efbbeba4b 100644 --- a/tests/lib/rules/no-restricted-exports.js +++ b/tests/lib/rules/no-restricted-exports.js @@ -3,7 +3,6 @@ * @author Milos Djermanovic */ -"use strict"; //------------------------------------------------------------------------------ // Requirements @@ -107,7 +106,16 @@ ruleTester.run("no-restricted-exports", rule, { { code: "export default 1;", options: [{ restrictedNamedExports: ["default"] }] }, // "default" does not disallow re-exporting a renamed default export from another module - { code: "export { default as a } from 'foo';", options: [{ restrictedNamedExports: ["default"] }] } + { code: "export { default as a } from 'foo';", options: [{ restrictedNamedExports: ["default"] }] }, + + // restrictDefaultExports.direct option + { code: "export default foo;", options: [{ restrictDefaultExports: { direct: false } }] }, + { code: "export default 42;", options: [{ restrictDefaultExports: { direct: false } }] }, + { code: "export default function foo() {}", options: [{ restrictDefaultExports: { direct: false } }] }, + { code: "export default foo;", options: [{ restrictedNamedExports: ["default"], restrictDefaultExports: { direct: true } }] }, + + // restrictDefaultExports.named option + { code: "const foo = 123;\nexport { foo as default };", options: [{ restrictDefaultExports: { named: false } }] } ], invalid: [ @@ -519,6 +527,47 @@ ruleTester.run("no-restricted-exports", rule, { code: "export { default } from 'foo';", options: [{ restrictedNamedExports: ["default"] }], errors: [{ messageId: "restrictedNamed", data: { name: "default" }, type: "Identifier", column: 10 }] + }, + + // restrictDefaultExports.direct option + { + code: "export default foo;", + options: [{ restrictDefaultExports: { direct: true } }], + errors: [{ messageId: "restrictedDefault", type: "ExportDefaultDeclaration", column: 1 }] + }, + { + code: "export default 42;", + options: [{ restrictDefaultExports: { direct: true } }], + errors: [{ messageId: "restrictedDefault", type: "ExportDefaultDeclaration", column: 1 }] + }, + { + code: "export default function foo() {};", + options: [{ restrictDefaultExports: { direct: true } }], + errors: [{ messageId: "restrictedDefault", type: "ExportDefaultDeclaration", column: 1 }] + }, + { + code: "export default foo;", + options: [{ restrictedNamedExports: ["bar"], restrictDefaultExports: { direct: true } }], + errors: [{ messageId: "restrictedDefault", type: "ExportDefaultDeclaration", column: 1 }] + }, + + // restrictDefaultExports.named option + { + code: "const foo = 123;\nexport { foo as default };", + options: [{ restrictDefaultExports: { named: true } }], + errors: [{ messageId: "restrictedDefault", type: "Identifier", line: 2, column: 17 }] + }, + + // restrictedNamedExports should take priority over restrictDefaultExports.named + { + code: "const foo = 123;\nexport { foo as default };", + options: [{ restrictedNamedExports: ["default"], restrictDefaultExports: { named: false } }], + errors: [{ messageId: "restrictedNamed", type: "Identifier", line: 2, column: 17 }] + }, + { + code: "const foo = 123;\nexport { foo as default };", + options: [{ restrictedNamedExports: ["default"], restrictDefaultExports: { named: true } }], + errors: [{ messageId: "restrictedNamed", type: "Identifier", line: 2, column: 17 }] } ] });