no-restricted-exports.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  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/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. },
  35. additionalProperties: false
  36. },
  37. {
  38. type: "object",
  39. properties: {
  40. restrictedNamedExports: {
  41. type: "array",
  42. items: {
  43. type: "string",
  44. pattern: "^(?!default$)"
  45. },
  46. uniqueItems: true
  47. },
  48. restrictDefaultExports: {
  49. type: "object",
  50. properties: {
  51. // Allow/Disallow `export default foo; export default 42; export default function foo() {}` format
  52. direct: {
  53. type: "boolean"
  54. },
  55. // Allow/Disallow `export { foo as default };` declarations
  56. named: {
  57. type: "boolean"
  58. },
  59. // Allow/Disallow `export { default } from "mod"; export { default as default } from "mod";` declarations
  60. defaultFrom: {
  61. type: "boolean"
  62. },
  63. // Allow/Disallow `export { foo as default } from "mod";` declarations
  64. namedFrom: {
  65. type: "boolean"
  66. },
  67. // Allow/Disallow `export * as default from "mod"`; declarations
  68. namespaceFrom: {
  69. type: "boolean"
  70. }
  71. },
  72. additionalProperties: false
  73. }
  74. },
  75. additionalProperties: false
  76. }
  77. ]
  78. }],
  79. messages: {
  80. restrictedNamed: "'{{name}}' is restricted from being used as an exported name.",
  81. restrictedDefault: "Exporting 'default' is restricted."
  82. }
  83. },
  84. create(context) {
  85. const restrictedNames = new Set(context.options[0] && context.options[0].restrictedNamedExports);
  86. const restrictDefaultExports = context.options[0] && context.options[0].restrictDefaultExports;
  87. /**
  88. * Checks and reports given exported name.
  89. * @param {ASTNode} node exported `Identifier` or string `Literal` node to check.
  90. * @returns {void}
  91. */
  92. function checkExportedName(node) {
  93. const name = astUtils.getModuleExportName(node);
  94. if (restrictedNames.has(name)) {
  95. context.report({
  96. node,
  97. messageId: "restrictedNamed",
  98. data: { name }
  99. });
  100. return;
  101. }
  102. if (name === "default") {
  103. if (node.parent.type === "ExportAllDeclaration") {
  104. if (restrictDefaultExports && restrictDefaultExports.namespaceFrom) {
  105. context.report({
  106. node,
  107. messageId: "restrictedDefault"
  108. });
  109. }
  110. } else { // ExportSpecifier
  111. const isSourceSpecified = !!node.parent.parent.source;
  112. const specifierLocalName = astUtils.getModuleExportName(node.parent.local);
  113. if (!isSourceSpecified && restrictDefaultExports && restrictDefaultExports.named) {
  114. context.report({
  115. node,
  116. messageId: "restrictedDefault"
  117. });
  118. return;
  119. }
  120. if (isSourceSpecified && restrictDefaultExports) {
  121. if (
  122. (specifierLocalName === "default" && restrictDefaultExports.defaultFrom) ||
  123. (specifierLocalName !== "default" && restrictDefaultExports.namedFrom)
  124. ) {
  125. context.report({
  126. node,
  127. messageId: "restrictedDefault"
  128. });
  129. }
  130. }
  131. }
  132. }
  133. }
  134. return {
  135. ExportAllDeclaration(node) {
  136. if (node.exported) {
  137. checkExportedName(node.exported);
  138. }
  139. },
  140. ExportDefaultDeclaration(node) {
  141. if (restrictDefaultExports && restrictDefaultExports.direct) {
  142. context.report({
  143. node,
  144. messageId: "restrictedDefault"
  145. });
  146. }
  147. },
  148. ExportNamedDeclaration(node) {
  149. const declaration = node.declaration;
  150. if (declaration) {
  151. if (declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration") {
  152. checkExportedName(declaration.id);
  153. } else if (declaration.type === "VariableDeclaration") {
  154. context.getDeclaredVariables(declaration)
  155. .map(v => v.defs.find(d => d.parent === declaration))
  156. .map(d => d.name) // Identifier nodes
  157. .forEach(checkExportedName);
  158. }
  159. } else {
  160. node.specifiers
  161. .map(s => s.exported)
  162. .forEach(checkExportedName);
  163. }
  164. }
  165. };
  166. }
  167. };