@@ -55,35 +55,50 @@ process.argv.slice(2).forEach((file) => {
5555 const ast = acorn . parse ( source , { ecmaVersion : 10 , locations : true } ) ;
5656 const program = ast . body ;
5757
58+ // Build link
59+ const link = `https://github.com/${ repo } /blob/${ tag } /` +
60+ path . relative ( '.' , file ) . replace ( / \\ / g, '/' ) ;
61+
5862 // Scan for exports.
5963 const exported = { constructors : [ ] , identifiers : [ ] } ;
6064 program . forEach ( ( statement ) => {
61- if ( statement . type !== 'ExpressionStatement' ) return ;
62- const expr = statement . expression ;
63- if ( expr . type !== 'AssignmentExpression' ) return ;
64-
65- let lhs = expr . left ;
66- if ( expr . left . object . type === 'MemberExpression' ) lhs = lhs . object ;
67- if ( lhs . type !== 'MemberExpression' ) return ;
68- if ( lhs . object . name !== 'module' ) return ;
69- if ( lhs . property . name !== 'exports' ) return ;
70-
71- let rhs = expr . right ;
72- while ( rhs . type === 'AssignmentExpression' ) rhs = rhs . right ;
73-
74- if ( rhs . type === 'NewExpression' ) {
75- exported . constructors . push ( rhs . callee . name ) ;
76- } else if ( rhs . type === 'ObjectExpression' ) {
77- rhs . properties . forEach ( ( property ) => {
78- if ( property . value . type === 'Identifier' ) {
79- exported . identifiers . push ( property . value . name ) ;
80- if ( / ^ [ A - Z ] / . test ( property . value . name [ 0 ] ) ) {
81- exported . constructors . push ( property . value . name ) ;
65+ if ( statement . type === 'ExpressionStatement' ) {
66+ const expr = statement . expression ;
67+ if ( expr . type !== 'AssignmentExpression' ) return ;
68+
69+ let lhs = expr . left ;
70+ if ( expr . left . object . type === 'MemberExpression' ) lhs = lhs . object ;
71+ if ( lhs . type !== 'MemberExpression' ) return ;
72+ if ( lhs . object . name !== 'module' ) return ;
73+ if ( lhs . property . name !== 'exports' ) return ;
74+
75+ let rhs = expr . right ;
76+ while ( rhs . type === 'AssignmentExpression' ) rhs = rhs . right ;
77+
78+ if ( rhs . type === 'NewExpression' ) {
79+ exported . constructors . push ( rhs . callee . name ) ;
80+ } else if ( rhs . type === 'ObjectExpression' ) {
81+ rhs . properties . forEach ( ( property ) => {
82+ if ( property . value . type === 'Identifier' ) {
83+ exported . identifiers . push ( property . value . name ) ;
84+ if ( / ^ [ A - Z ] / . test ( property . value . name [ 0 ] ) ) {
85+ exported . constructors . push ( property . value . name ) ;
86+ }
8287 }
83- }
84- } ) ;
85- } else if ( rhs . type === 'Identifier' ) {
86- exported . identifiers . push ( rhs . name ) ;
88+ } ) ;
89+ } else if ( rhs . type === 'Identifier' ) {
90+ exported . identifiers . push ( rhs . name ) ;
91+ }
92+ } else if ( statement . type === 'VariableDeclaration' ) {
93+ for ( const decl of statement . declarations ) {
94+ let init = decl . init ;
95+ while ( init && init . type === 'AssignmentExpression' ) init = init . left ;
96+ if ( ! init || init . type !== 'MemberExpression' ) continue ;
97+ if ( init . object . name !== 'module' ) continue ;
98+ if ( init . property . name !== 'exports' ) continue ;
99+ exported . constructors . push ( decl . id . name ) ;
100+ definition [ decl . id . name ] = `${ link } #L${ statement . loc . start . line } ` ;
101+ }
87102 }
88103 } ) ;
89104
@@ -93,8 +108,7 @@ process.argv.slice(2).forEach((file) => {
93108 // ClassName.prototype.foo = ...;
94109 // function Identifier(...) {...};
95110 //
96- const link = `https://github.com/${ repo } /blob/${ tag } /` +
97- path . relative ( '.' , file ) . replace ( / \\ / g, '/' ) ;
111+ const indirect = { } ;
98112
99113 program . forEach ( ( statement ) => {
100114 if ( statement . type === 'ExpressionStatement' ) {
@@ -128,6 +142,11 @@ process.argv.slice(2).forEach((file) => {
128142 }
129143
130144 definition [ name ] = `${ link } #L${ statement . loc . start . line } ` ;
145+
146+ if ( expr . left . property . name === expr . right . name ) {
147+ indirect [ expr . right . name ] = name ;
148+ }
149+
131150 } else if ( statement . type === 'FunctionDeclaration' ) {
132151 const name = statement . id . name ;
133152 if ( ! exported . identifiers . includes ( name ) ) return ;
@@ -136,6 +155,18 @@ process.argv.slice(2).forEach((file) => {
136155 `${ link } #L${ statement . loc . start . line } ` ;
137156 }
138157 } ) ;
158+
159+ // Search for indirect references of the form ClassName.foo = foo;
160+ if ( Object . keys ( indirect ) . length > 0 ) {
161+ program . forEach ( ( statement ) => {
162+ if ( statement . type === 'FunctionDeclaration' ) {
163+ const name = statement . id . name ;
164+ if ( indirect [ name ] ) {
165+ definition [ indirect [ name ] ] = `${ link } #L${ statement . loc . start . line } ` ;
166+ }
167+ }
168+ } ) ;
169+ }
139170} ) ;
140171
141172console . log ( JSON . stringify ( definition , null , 2 ) ) ;
0 commit comments