@@ -83,8 +83,93 @@ function isRuleTesterConstruction (node) {
8383 ) ;
8484}
8585
86- module . exports = {
86+ const INTERESTING_RULE_KEYS = new Set ( [ 'create' , 'meta' ] ) ;
87+
88+ /**
89+ * Helper for `getRuleInfo`. Handles ESM rules.
90+ */
91+ function getRuleExportsESM ( ast ) {
92+ return ast . body
93+ . filter ( statement => statement . type === 'ExportDefaultDeclaration' )
94+ . map ( statement => statement . declaration )
95+ // eslint-disable-next-line unicorn/prefer-object-from-entries
96+ . reduce ( ( currentExports , node ) => {
97+ if ( node . type === 'ObjectExpression' ) {
98+ // eslint-disable-next-line unicorn/prefer-object-from-entries
99+ return node . properties . reduce ( ( parsedProps , prop ) => {
100+ const keyValue = module . exports . getKeyName ( prop ) ;
101+ if ( INTERESTING_RULE_KEYS . has ( keyValue ) ) {
102+ parsedProps [ keyValue ] = prop . value ;
103+ }
104+ return parsedProps ;
105+ } , { } ) ;
106+ } else if ( isNormalFunctionExpression ( node ) ) {
107+ return { create : node , meta : null , isNewStyle : false } ;
108+ }
109+ return currentExports ;
110+ } , { } ) ;
111+ }
112+
113+ /**
114+ * Helper for `getRuleInfo`. Handles CJS rules.
115+ */
116+ function getRuleExportsCJS ( ast ) {
117+ let exportsVarOverridden = false ;
118+ let exportsIsFunction = false ;
119+ return ast . body
120+ . filter ( statement => statement . type === 'ExpressionStatement' )
121+ . map ( statement => statement . expression )
122+ . filter ( expression => expression . type === 'AssignmentExpression' )
123+ . filter ( expression => expression . left . type === 'MemberExpression' )
124+ // eslint-disable-next-line unicorn/prefer-object-from-entries
125+ . reduce ( ( currentExports , node ) => {
126+ if (
127+ node . left . object . type === 'Identifier' && node . left . object . name === 'module' &&
128+ node . left . property . type === 'Identifier' && node . left . property . name === 'exports'
129+ ) {
130+ exportsVarOverridden = true ;
131+ if ( isNormalFunctionExpression ( node . right ) ) {
132+ // Check `module.exports = function () {}`
133+
134+ exportsIsFunction = true ;
135+ return { create : node . right , meta : null , isNewStyle : false } ;
136+ } else if ( node . right . type === 'ObjectExpression' ) {
137+ // Check `module.exports = { create: function () {}, meta: {} }`
138+
139+ // eslint-disable-next-line unicorn/prefer-object-from-entries
140+ return node . right . properties . reduce ( ( parsedProps , prop ) => {
141+ const keyValue = module . exports . getKeyName ( prop ) ;
142+ if ( INTERESTING_RULE_KEYS . has ( keyValue ) ) {
143+ parsedProps [ keyValue ] = prop . value ;
144+ }
145+ return parsedProps ;
146+ } , { } ) ;
147+ }
148+ return { } ;
149+ } else if (
150+ ! exportsIsFunction &&
151+ node . left . object . type === 'MemberExpression' &&
152+ node . left . object . object . type === 'Identifier' && node . left . object . object . name === 'module' &&
153+ node . left . object . property . type === 'Identifier' && node . left . object . property . name === 'exports' &&
154+ node . left . property . type === 'Identifier' && INTERESTING_RULE_KEYS . has ( node . left . property . name )
155+ ) {
156+ // Check `module.exports.create = () => {}`
87157
158+ currentExports [ node . left . property . name ] = node . right ;
159+ } else if (
160+ ! exportsVarOverridden &&
161+ node . left . object . type === 'Identifier' && node . left . object . name === 'exports' &&
162+ node . left . property . type === 'Identifier' && INTERESTING_RULE_KEYS . has ( node . left . property . name )
163+ ) {
164+ // Check `exports.create = () => {}`
165+
166+ currentExports [ node . left . property . name ] = node . right ;
167+ }
168+ return currentExports ;
169+ } , { } ) ;
170+ }
171+
172+ module . exports = {
88173 /**
89174 * Performs static analysis on an AST to try to determine the final value of `module.exports`.
90175 * @param {{ast: ASTNode, scopeManager?: ScopeManager} } sourceCode The object contains `Program` AST node, and optional `scopeManager`
@@ -94,63 +179,7 @@ module.exports = {
94179 from the file, the return value will be `null`.
95180 */
96181 getRuleInfo ( { ast, scopeManager } ) {
97- const INTERESTING_KEYS = new Set ( [ 'create' , 'meta' ] ) ;
98- let exportsVarOverridden = false ;
99- let exportsIsFunction = false ;
100-
101- const exportNodes = ast . body
102- . filter ( statement => statement . type === 'ExpressionStatement' )
103- . map ( statement => statement . expression )
104- . filter ( expression => expression . type === 'AssignmentExpression' )
105- . filter ( expression => expression . left . type === 'MemberExpression' )
106- // eslint-disable-next-line unicorn/prefer-object-from-entries
107- . reduce ( ( currentExports , node ) => {
108- if (
109- node . left . object . type === 'Identifier' && node . left . object . name === 'module' &&
110- node . left . property . type === 'Identifier' && node . left . property . name === 'exports'
111- ) {
112- exportsVarOverridden = true ;
113-
114- if ( isNormalFunctionExpression ( node . right ) ) {
115- // Check `module.exports = function () {}`
116-
117- exportsIsFunction = true ;
118- return { create : node . right , meta : null } ;
119- } else if ( node . right . type === 'ObjectExpression' ) {
120- // Check `module.exports = { create: function () {}, meta: {} }`
121-
122- exportsIsFunction = false ;
123- // eslint-disable-next-line unicorn/prefer-object-from-entries
124- return node . right . properties . reduce ( ( parsedProps , prop ) => {
125- const keyValue = module . exports . getKeyName ( prop ) ;
126- if ( INTERESTING_KEYS . has ( keyValue ) ) {
127- parsedProps [ keyValue ] = prop . value ;
128- }
129- return parsedProps ;
130- } , { } ) ;
131- }
132- return { } ;
133- } else if (
134- ! exportsIsFunction &&
135- node . left . object . type === 'MemberExpression' &&
136- node . left . object . object . type === 'Identifier' && node . left . object . object . name === 'module' &&
137- node . left . object . property . type === 'Identifier' && node . left . object . property . name === 'exports' &&
138- node . left . property . type === 'Identifier' && INTERESTING_KEYS . has ( node . left . property . name )
139- ) {
140- // Check `module.exports.create = () => {}`
141-
142- currentExports [ node . left . property . name ] = node . right ;
143- } else if (
144- ! exportsVarOverridden &&
145- node . left . object . type === 'Identifier' && node . left . object . name === 'exports' &&
146- node . left . property . type === 'Identifier' && INTERESTING_KEYS . has ( node . left . property . name )
147- ) {
148- // Check `exports.create = () => {}`
149-
150- currentExports [ node . left . property . name ] = node . right ;
151- }
152- return currentExports ;
153- } , { } ) ;
182+ const exportNodes = ast . sourceType === 'module' ? getRuleExportsESM ( ast ) : getRuleExportsCJS ( ast ) ;
154183
155184 const createExists = Object . prototype . hasOwnProperty . call ( exportNodes , 'create' ) ;
156185 if ( ! createExists ) {
@@ -164,7 +193,7 @@ module.exports = {
164193 return null ;
165194 }
166195
167- return Object . assign ( { isNewStyle : ! exportsIsFunction , meta : null } , exportNodes ) ;
196+ return Object . assign ( { isNewStyle : true , meta : null } , exportNodes ) ;
168197 } ,
169198
170199 /**
0 commit comments