@@ -72,9 +72,9 @@ function generateFlatAST(inputCode, opts = {}) {
7272 opts = { ...generateFlatASTDefaultOptions , ...opts } ;
7373 const rootNode = generateRootNode ( inputCode , opts ) ;
7474 const tree = extractNodesFromRoot ( rootNode , opts ) ;
75- const sm = initScopeManager ( rootNode ) ;
7675 if ( opts . detailed ) {
77- for ( let i = 0 ; i < tree . length ; i ++ ) injectScopeToNode ( tree [ i ] , sm ) ;
76+ const scopes = getAllScopes ( rootNode ) ;
77+ for ( let i = 0 ; i < tree . length ; i ++ ) injectScopeToNode ( tree [ i ] , scopes ) ;
7878 }
7979 return tree ;
8080}
@@ -119,6 +119,7 @@ function extractNodesFromRoot(rootNode, opts) {
119119 const tree = [ ] ;
120120 let nodeId = 0 ;
121121
122+ // noinspection JSUnusedGlobalSymbols
122123 estraverse . traverse ( rootNode , {
123124 /**
124125 * @param {ASTNode } node
@@ -129,8 +130,12 @@ function extractNodesFromRoot(rootNode, opts) {
129130 node . nodeId = nodeId ++ ;
130131 node . childNodes = [ ] ;
131132 node . parentNode = parentNode ;
132- // Keep track of the node's lineage
133133 node . parentKey = parentNode ? getParentKey ( node ) : '' ;
134+ node . lineage = [ ...parentNode ?. lineage || [ ] ] ;
135+ if ( parentNode ) {
136+ node . lineage . push ( parentNode . nodeId ) ;
137+ parentNode . childNodes . push ( node ) ;
138+ }
134139 if ( opts . includeSrc ) Object . defineProperty ( node , 'src' , {
135140 get ( ) { return rootNode . srcClosure ( node . range [ 0 ] , node . range [ 1 ] ) ; } ,
136141 } ) ;
@@ -139,71 +144,147 @@ function extractNodesFromRoot(rootNode, opts) {
139144 return tree ;
140145}
141146
142- function initScopeManager ( rootNode ) {
143- // noinspection JSCheckFunctionSignatures
144- return analyze ( rootNode , {
145- optimistic : true ,
146- ecmaVersion,
147- sourceType} ) ;
148- }
149-
150147/**
151- *
152148 * @param {ASTNode } node
153- * @param {ScopeManager } sm
149+ * @param {ASTScope[] } scopes
154150 */
155- function injectScopeToNode ( node , sm ) {
151+ function injectScopeToNode ( node , scopes ) {
156152 let parentNode = node . parentNode ;
157153 // Acquire scope
158- node . scope = sm . acquire ( node ) ;
159- if ( ! node . scope ) node . scope = node . parentNode . scope ;
160- else if ( node . scope . type . includes ( '-name' ) && node . scope ?. childScopes ?. length === 1 ) node . scope = node . scope . childScopes [ 0 ] ;
161- if ( node . scope . scopeId === undefined ) node . scope . scopeId = node . scope . block . nodeId ;
162- if ( parentNode ) {
163- node . lineage = [ ...parentNode ?. lineage || [ ] , parentNode . nodeId ] ;
164- parentNode . childNodes . push ( node ) ;
165- }
166- if ( node . type === 'Identifier' ) {
154+ node . scope = matchScopeToNode ( node , scopes ) ;
155+ if ( node . type === 'Identifier' && ! ( ! parentNode . computed && [ 'property' , 'key' ] . includes ( node . parentKey ) ) ) {
167156 // Track references and declarations
168157 // Prevent assigning declNode to member expression properties or object keys
169- if ( ! ( [ 'property' , 'key' ] . includes ( node . parentKey ) && ! parentNode . computed ) ) {
170- const variables = node . scope . variables . filter ( n => n . name === node . name ) ;
171- const isDeclaration = variables ?. length && variables [ 0 ] . identifiers . filter ( n => n . nodeId === node . nodeId ) . length ;
172- if ( isDeclaration ) node . references = node . references || [ ] ;
173- else if ( ! ( node . parentKey === 'id' && node . parentNode . type === 'FunctionDeclaration' ) ) {
174- // Find declaration by finding the closest declaration of the same name.
175- let decls = [ ] ;
176- if ( variables ?. length ) decls = variables . filter ( n => n . name === node . name ) [ 0 ] . identifiers ;
177- else {
178- const scopeReferences = node . scope . references . filter ( n => n . identifier . name === node . name ) ;
179- if ( scopeReferences . length ) decls = scopeReferences [ 0 ] . resolved ?. identifiers || [ ] ;
180- }
181- let declNode = decls [ 0 ] ;
182- if ( decls . length > 1 ) { // TODO: Defer setting declaration and references
183- let commonAncestors = node . lineage . reduce ( ( t , c ) => declNode . lineage ?. includes ( c ) ? ++ t : t , 0 ) ;
184- decls . slice ( 1 ) . forEach ( n => {
185- const ca = node . lineage . reduce ( ( t , c ) => n . lineage ?. includes ( c ) ? ++ t : t , 0 ) ;
186- if ( ca > commonAncestors ) {
187- commonAncestors = ca ;
188- declNode = n ;
189- }
190- } ) ;
191- }
192- if ( declNode ) {
193- if ( ! declNode . references ) declNode . references = [ ] ;
194- declNode . references . push ( node ) ;
195- node . declNode = declNode ;
158+ const variables = node . scope . variables . filter ( n => n . name === node . name ) ;
159+ if ( node . parentKey === 'id' || ( variables ?. length && variables [ 0 ] . identifiers . some ( n => n === node ) ) ) {
160+ node . references = node . references || [ ] ;
161+ } else {
162+ // Find declaration by finding the closest declaration of the same name.
163+ let decls = [ ] ;
164+ if ( variables ?. length ) {
165+ decls = variables . find ( n => n . name === node . name ) ?. identifiers ;
166+ }
167+ else {
168+ const scopeReference = node . scope . references . find ( n => n . identifier . name === node . name ) ;
169+ if ( scopeReference ) decls = scopeReference . resolved ?. identifiers || [ ] ;
170+ }
171+ let declNode = decls [ 0 ] ;
172+ if ( decls . length > 1 ) {
173+ let commonAncestors = maxSharedLength ( declNode . lineage , node . lineage ) ;
174+ for ( let i = 1 ; i < decls . length ; i ++ ) {
175+ const ca = maxSharedLength ( decls [ i ] . lineage , node . lineage ) ;
176+ if ( ca > commonAncestors ) {
177+ commonAncestors = ca ;
178+ declNode = decls [ i ] ;
179+ }
196180 }
197181 }
182+ if ( declNode ) {
183+ declNode . references = declNode . references || [ ] ;
184+ declNode . references . push ( node ) ;
185+ node . declNode = declNode ;
186+ }
187+ }
188+ }
189+ }
190+
191+ /**
192+ * @param {number[] } targetArr
193+ * @param {number[] } containedArr
194+ * @return {number } Return the maximum length of shared numbers
195+ */
196+ function maxSharedLength ( targetArr , containedArr ) {
197+ let count = 0 ;
198+ for ( let i = 0 ; i < containedArr . length ; i ++ ) {
199+ if ( targetArr [ i ] !== containedArr [ i ] ) break ;
200+ ++ count ;
201+ }
202+ return count ;
203+ }
204+
205+ /**
206+ * @param {ASTNode } node
207+ * @param {ASTScope[] } scopes
208+ * @return {Promise }
209+ */
210+ async function injectScopeToNodeAsync ( node , scopes ) {
211+ return new Promise ( ( resolve , reject ) => {
212+ try {
213+ injectScopeToNode ( node , scopes ) ;
214+ resolve ( ) ;
215+ } catch ( e ) {
216+ reject ( e ) ;
198217 }
218+ } ) ;
219+ }
220+
221+ function getAllScopes ( rootNode ) {
222+ const globalScope = analyze ( rootNode , {
223+ optimistic : true ,
224+ ecmaVersion,
225+ sourceType} ) . acquireAll ( rootNode ) [ 0 ] ;
226+ const allScopes = { } ;
227+ const stack = [ globalScope ] ;
228+ while ( stack . length ) {
229+ let scope = stack . pop ( ) ;
230+ const scopeId = scope . block . nodeId ;
231+ scope . block . isScopeBlock = true ;
232+ if ( ! allScopes [ scopeId ] ) {
233+ allScopes [ scopeId ] = scope ;
234+ stack . push ( ...scope . childScopes ) ;
235+ }
236+ }
237+ rootNode . allScopes = allScopes ;
238+ return allScopes ;
239+ }
240+
241+ /**
242+ * @param {ASTNode } node
243+ * @param {ASTScope[] } allScopes
244+ * @return {ASTScope }
245+ */
246+ function matchScopeToNode ( node , allScopes ) {
247+ if ( node . lineage ?. length ) {
248+ for ( const nid of [ ...node . lineage ] . reverse ( ) ) {
249+ if ( allScopes [ nid ] ) {
250+ let scope = allScopes [ nid ] ;
251+ if ( scope . type . includes ( '-name' ) && scope ?. childScopes ?. length === 1 ) scope = scope . childScopes [ 0 ] ;
252+ return scope ;
253+ }
254+ }
255+ }
256+ return allScopes [ 0 ] ; // Global scope - this should never be reached
257+ }
258+
259+ /**
260+ *
261+ * @param {string } inputCode
262+ * @param {object } opts
263+ * @return {Promise<ASTNode[]> }
264+ */
265+ async function generateFlatASTAsync ( inputCode , opts = { } ) {
266+ opts = { ...generateFlatASTDefaultOptions , ...opts } ;
267+ const rootNode = generateRootNode ( inputCode , opts ) ;
268+ const tree = extractNodesFromRoot ( rootNode , opts ) ;
269+ const promises = [ ] ;
270+ if ( opts . detailed ) {
271+ const scopes = getAllScopes ( rootNode ) ;
272+ for ( let i = 0 ; i < tree . length ; i ++ ) {
273+ promises . push ( injectScopeToNodeAsync ( tree [ i ] , scopes ) ) ;
274+ }
275+
199276 }
277+ return Promise . all ( promises ) . then ( ( ) => tree ) ;
200278}
201279
202280module . exports = {
203281 estraverse,
204282 extractNodesFromRoot,
205283 generateCode,
206284 generateFlatAST,
285+ generateFlatASTAsync,
207286 generateRootNode,
287+ injectScopeToNode,
288+ injectScopeToNodeAsync,
208289 parseCode,
209290} ;
0 commit comments