no-restricted-exports.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. /**
  2. * @fileoverview Rule to disallow specified names in exports
  3. * @author Milos Djermanovic
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Requirements
  8. //------------------------------------------------------------------------------
  9. const astUtils = require("./utils/ast-utils");
  10. //------------------------------------------------------------------------------
  11. // Rule Definition
  12. //------------------------------------------------------------------------------
  13. /** @type {import('../shared/types').Rule} */
  14. module.exports = {
  15. meta: {
  16. type: "suggestion",
  17. docs: {
  18. description: "Disallow specified names in exports",
  19. recommended: false,
  20. url: "https://eslint.org/docs/latest/rules/no-restricted-exports"
  21. },
  22. schema: [{
  23. anyOf: [
  24. {
  25. type: "object",
  26. properties: {
  27. restrictedNamedExports: {
  28. type: "array",
  29. items: {
  30. type: "string"
  31. },
  32. uniqueItems: true
  33. },
  34. restrictedNamedExportsPattern: { type: "string" }
  35. },
  36. additionalProperties: false
  37. },
  38. {
  39. type: "object",
  40. properties: {
  41. restrictedNamedExports: {
  42. type: "array",
  43. items: {
  44. type: "string",
  45. pattern: "^(?!default$)"
  46. },
  47. uniqueItems: true
  48. },
  49. restrictedNamedExportsPattern: { type: "string" },
  50. restrictDefaultExports: {
  51. type: "object",
  52. properties: {
  53. // Allow/Disallow `export default foo; export default 42; export default function foo() {}` format
  54. direct: {
  55. type: "boolean"
  56. },
  57. // Allow/Disallow `export { foo as default };` declarations
  58. named: {
  59. type: "boolean"
  60. },
  61. // Allow/Disallow `export { default } from "mod"; export { default as default } from "mod";` declarations
  62. defaultFrom: {
  63. type: "boolean"
  64. },
  65. // Allow/Disallow `export { foo as default } from "mod";` declarations
  66. namedFrom: {
  67. type: "boolean"
  68. },
  69. // Allow/Disallow `export * as default from "mod"`; declarations
  70. namespaceFrom: {
  71. type: "boolean"
  72. }
  73. },
  74. additionalProperties: false
  75. }
  76. },
  77. additionalProperties: false
  78. }
  79. ]
  80. }],
  81. messages: {
  82. restrictedNamed: "'{{name}}' is restricted from being used as an exported name.",
  83. restrictedDefault: "Exporting 'default' is restricted."
  84. }
  85. },
  86. create(context) {
  87. const restrictedNames = new Set(context.options[0] && context.options[0].restrictedNamedExports);
  88. const restrictedNamePattern = context.options[0] && context.options[0].restrictedNamedExportsPattern;
  89. const restrictDefaultExports = context.options[0] && context.options[0].restrictDefaultExports;
  90. const sourceCode = context.sourceCode;
  91. /**
  92. * Checks and reports given exported name.
  93. * @param {ASTNode} node exported `Identifier` or string `Literal` node to check.
  94. * @returns {void}
  95. */
  96. function checkExportedName(node) {
  97. const name = astUtils.getModuleExportName(node);
  98. let matchesRestrictedNamePattern = false;
  99. if (restrictedNamePattern && name !== "default") {
  100. const patternRegex = new RegExp(restrictedNamePattern, "u");
  101. matchesRestrictedNamePattern = patternRegex.test(name);
  102. }
  103. if (matchesRestrictedNamePattern || restrictedNames.has(name)) {
  104. context.report({
  105. node,
  106. messageId: "restrictedNamed",
  107. data: { name }
  108. });
  109. return;
  110. }
  111. if (name === "default") {
  112. if (node.parent.type === "ExportAllDeclaration") {
  113. if (restrictDefaultExports && restrictDefaultExports.namespaceFrom) {
  114. context.report({
  115. node,
  116. messageId: "restrictedDefault"
  117. });
  118. }
  119. } else { // ExportSpecifier
  120. const isSourceSpecified = !!node.parent.parent.source;
  121. const specifierLocalName = astUtils.getModuleExportName(node.parent.local);
  122. if (!isSourceSpecified && restrictDefaultExports && restrictDefaultExports.named) {
  123. context.report({
  124. node,
  125. messageId: "restrictedDefault"
  126. });
  127. return;
  128. }
  129. if (isSourceSpecified && restrictDefaultExports) {
  130. if (
  131. (specifierLocalName === "default" && restrictDefaultExports.defaultFrom) ||
  132. (specifierLocalName !== "default" && restrictDefaultExports.namedFrom)
  133. ) {
  134. context.report({
  135. node,
  136. messageId: "restrictedDefault"
  137. });
  138. }
  139. }
  140. }
  141. }
  142. }
  143. return {
  144. ExportAllDeclaration(node) {
  145. if (node.exported) {
  146. checkExportedName(node.exported);
  147. }
  148. },
  149. ExportDefaultDeclaration(node) {
  150. if (restrictDefaultExports && restrictDefaultExports.direct) {
  151. context.report({
  152. node,
  153. messageId: "restrictedDefault"
  154. });
  155. }
  156. },
  157. ExportNamedDeclaration(node) {
  158. const declaration = node.declaration;
  159. if (declaration) {
  160. if (declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration") {
  161. checkExportedName(declaration.id);
  162. } else if (declaration.type === "VariableDeclaration") {
  163. sourceCode.getDeclaredVariables(declaration)
  164. .map(v => v.defs.find(d => d.parent === declaration))
  165. .map(d => d.name) // Identifier nodes
  166. .forEach(checkExportedName);
  167. }
  168. } else {
  169. node.specifiers
  170. .map(s => s.exported)
  171. .forEach(checkExportedName);
  172. }
  173. }
  174. };
  175. }
  176. };