11'use strict' ;
22
3- import { visit } from 'estree-util-visit' ;
4-
53import { getLineNumber } from './getLineNumber.mjs' ;
64import { CONSTRUCTOR_EXPRESSION } from '../constants.mjs' ;
75
86/**
9- * @see https://github.com/estree/estree/blob/master/es5.md#assignmentexpression
10- *
117 * @param {import('@oxc-project/types').ExpressionStatement } node
128 * @param {string } basename
139 * @param {Record<string, number> } nameToLineNumberMap
1410 * @param {string } sourceText
11+ * @param {import('@oxc-project/types').Program } program
1512 * @returns {import('../types').ProgramExports | undefined }
1613 */
17- function handleExpression ( node , basename , nameToLineNumberMap , sourceText ) {
14+ function handleExpression (
15+ node ,
16+ basename ,
17+ nameToLineNumberMap ,
18+ sourceText ,
19+ program
20+ ) {
1821 const { expression } = node ;
1922
2023 if ( expression . type !== 'AssignmentExpression' ) {
2124 return ;
2225 }
2326
24- // `a=b`, lhs=`a` and rhs=`b`
2527 let { left : lhs , right : rhs } = expression ;
2628
2729 if ( lhs . type !== 'MemberExpression' ) {
@@ -32,114 +34,73 @@ function handleExpression(node, basename, nameToLineNumberMap, sourceText) {
3234 lhs = lhs . object ;
3335 }
3436
35- /**
36- * @type {import('../types').ProgramExports }
37- */
3837 const exports = {
3938 ctors : [ ] ,
4039 identifiers : [ ] ,
4140 indirects : { } ,
4241 } ;
4342
4443 if ( lhs . object . name === 'exports' ) {
45- // This is an assignment to a property in `module.exports` or `exports`
46- // (i.e. `module.exports.asd = ...` or `exports.asd = ...`)
47-
4844 switch ( rhs . type ) {
49- /** @see https://github.com/estree/estree/blob/master/es5.md#functionexpression */
5045 case 'FunctionExpression' : {
51- // module.exports.something = () => {}
5246 nameToLineNumberMap [ `${ basename } .${ lhs . property . name } ` ] = getLineNumber (
5347 sourceText ,
54- node . range [ 0 ]
48+ node . range [ 0 ] ,
49+ program
5550 ) ;
56-
5751 break ;
5852 }
59- /** @see https://github.com/estree/estree/blob/master/es5.md#identifier */
6053 case 'Identifier' : {
61- // Save this for later in case it's referenced
62- // exports.Buffer = Buffer -> indirect mapping
6354 exports . indirects [ rhs . name ] = `${ basename } .${ lhs . property . name } ` ;
64-
6555 break ;
6656 }
6757 default : {
6858 if ( lhs . property . name !== undefined ) {
6959 exports . identifiers . push ( lhs . property . name ) ;
7060 }
71-
7261 break ;
7362 }
7463 }
7564 } else if ( lhs . object . name === 'module' && lhs . property . name === 'exports' ) {
76- // This is an assignment to `module.exports` as a whole
77- // (i.e. `module.exports = {}`)
78-
79- // We need to move right until we find the value of the assignment.
80- // (if `a=b`, we want `b`)
8165 while ( rhs . type === 'AssignmentExpression' ) {
8266 rhs = rhs . right ;
8367 }
8468
8569 switch ( rhs . type ) {
86- /** @see https://github.com/estree/estree/blob/master/es5.md#newexpression */
8770 case 'NewExpression' : {
88- // module.exports = new Asd()
8971 exports . ctors . push ( rhs . callee . name ) ;
9072 break ;
9173 }
92- /** @see https://github.com/estree/estree/blob/master/es5.md#objectexpression */
9374 case 'ObjectExpression' : {
94- // module.exports = {}
95- // we need to go through all of the properties and register them
9675 rhs . properties . forEach ( ( { value } ) => {
76+ if ( ! value ) {
77+ return ;
78+ }
9779 switch ( value . type ) {
9880 case 'Identifier' : {
9981 exports . identifiers . push ( value . name ) ;
100-
10182 if ( CONSTRUCTOR_EXPRESSION . test ( value . name [ 0 ] ) ) {
10283 exports . ctors . push ( value . name ) ;
10384 }
104-
10585 break ;
10686 }
10787 case 'CallExpression' : {
108- if ( value . callee . name !== 'deprecate' ) {
109- break ;
88+ if ( value . callee . name === 'deprecate' && value . arguments [ 0 ] ) {
89+ exports . identifiers . push ( value . arguments [ 0 ] . name ) ;
11090 }
111-
112- // Handle exports wrapped in the `deprecate` function
113- // Ex/ https://github.com/nodejs/node/blob/e96072ad57348ce423a8dd7639dcc3d1c34e847d/lib/buffer.js#L1334
114-
115- exports . identifiers . push ( value . arguments [ 0 ] . name ) ;
116-
11791 break ;
11892 }
119- default : {
120- // Not relevant
121- }
12293 }
12394 } ) ;
124-
12595 break ;
12696 }
127- /** @see https://github.com/estree/estree/blob/master/es5.md#identifier */
12897 case 'Identifier' : {
129- // Something else, let's save it for when we're searching for
130- // declarations
131-
13298 if ( rhs . name !== undefined ) {
13399 exports . identifiers . push ( rhs . name ) ;
134100 if ( CONSTRUCTOR_EXPRESSION . test ( rhs . name [ 0 ] ) ) {
135101 exports . ctors . push ( rhs . name ) ;
136102 }
137103 }
138-
139- break ;
140- }
141- default : {
142- // Not relevant
143104 break ;
144105 }
145106 }
@@ -149,23 +110,20 @@ function handleExpression(node, basename, nameToLineNumberMap, sourceText) {
149110}
150111
151112/**
152- * @see https://github.com/estree/estree/blob/master/es5.md#variabledeclaration
153- *
154113 * @param {import('@oxc-project/types').VariableDeclaration } node
155114 * @param {string } basename
156115 * @param {Record<string, number> } nameToLineNumberMap
157116 * @param {string } sourceText
117+ * @param {import('@oxc-project/types').Program } program
158118 * @returns {import('../types').ProgramExports | undefined }
159119 */
160120function handleVariableDeclaration (
161121 node ,
162122 basename ,
163123 nameToLineNumberMap ,
164- sourceText
124+ sourceText ,
125+ program
165126) {
166- /**
167- * @type {import('../types').ProgramExports }
168- */
169127 const exports = {
170128 ctors : [ ] ,
171129 identifiers : [ ] ,
@@ -177,14 +135,10 @@ function handleVariableDeclaration(
177135 const id = declarator . id ;
178136
179137 while ( lhs && lhs . type === 'AssignmentExpression' ) {
180- // Move left until we get to what we're assigning to
181- // (if `a=b`, we want `a`)
182138 lhs = lhs . left ;
183139 }
184140
185141 if ( ! lhs || lhs . type !== 'MemberExpression' ) {
186- // Doesn't exist or we're not writing to an object
187- // (aka it's just a regular variable like `const a = 123`)
188142 return ;
189143 }
190144
@@ -194,9 +148,9 @@ function handleVariableDeclaration(
194148 case 'exports' : {
195149 nameToLineNumberMap [ `${ basename } .${ lhs . property . name } ` ] = getLineNumber (
196150 sourceText ,
197- range [ 0 ]
151+ range [ 0 ] ,
152+ program
198153 ) ;
199-
200154 break ;
201155 }
202156 case 'module' : {
@@ -205,12 +159,11 @@ function handleVariableDeclaration(
205159 }
206160
207161 exports . ctors . push ( id . name ) ;
208- nameToLineNumberMap [ id . name ] = getLineNumber ( sourceText , range [ 0 ] ) ;
209-
210- break ;
211- }
212- default : {
213- // Not relevant to us
162+ nameToLineNumberMap [ id . name ] = getLineNumber (
163+ sourceText ,
164+ range [ 0 ] ,
165+ program
166+ ) ;
214167 break ;
215168 }
216169 }
@@ -220,22 +173,32 @@ function handleVariableDeclaration(
220173}
221174
222175/**
223- * We need to find what a source file exports so we know what to include in
224- * the final result. We can do this by going through every statement in the
225- * program looking for assignments to `module.exports`.
226- *
227- * Noteworthy that exports can happen throughout the program so we need to
228- * go through the entire thing.
229- *
176+ * Iterates top-level program statements efficiently without deep AST traversal.
177+ * @param {import('@oxc-project/types').Program } program
178+ * @param {function(import('@oxc-project/types').Node): void } callback
179+ */
180+ function walkTopLevelStatements ( program , callback ) {
181+ const body = program . body ;
182+ if ( ! body ) {
183+ return ;
184+ }
185+
186+ for ( let i = 0 ; i < body . length ; i ++ ) {
187+ const node = body [ i ] ;
188+ callback ( node ) ;
189+ if ( node . type === 'ExportNamedDeclaration' && node . declaration ) {
190+ callback ( node . declaration ) ;
191+ }
192+ }
193+ }
194+
195+ /**
230196 * @param {import('@oxc-project/types').Program } program
231197 * @param {string } basename
232198 * @param {Record<string, number> } nameToLineNumberMap
233199 * @returns {import('../types').ProgramExports }
234200 */
235201export function extractExports ( program , basename , nameToLineNumberMap ) {
236- /**
237- * @type {import('../types').ProgramExports }
238- */
239202 const exports = {
240203 ctors : [ ] ,
241204 identifiers : [ ] ,
@@ -244,27 +207,33 @@ export function extractExports(program, basename, nameToLineNumberMap) {
244207
245208 const TYPE_TO_HANDLER_MAP = {
246209 /**
247- * @param { import('@oxc-project/types').Node } node
210+ *
248211 */
249212 ExpressionStatement : node =>
250- handleExpression ( node , basename , nameToLineNumberMap , program . sourceText ) ,
213+ handleExpression (
214+ node ,
215+ basename ,
216+ nameToLineNumberMap ,
217+ program . sourceText ,
218+ program
219+ ) ,
251220
252221 /**
253- * @param { import('@oxc-project/types').Node } node
222+ *
254223 */
255224 VariableDeclaration : node =>
256225 handleVariableDeclaration (
257226 node ,
258227 basename ,
259228 nameToLineNumberMap ,
260- program . sourceText
229+ program . sourceText ,
230+ program
261231 ) ,
262232 } ;
263233
264- visit ( program , node => {
234+ walkTopLevelStatements ( program , node => {
265235 if ( node . type in TYPE_TO_HANDLER_MAP ) {
266236 const handler = TYPE_TO_HANDLER_MAP [ node . type ] ;
267-
268237 const output = handler ( node ) ;
269238
270239 if ( output ) {
0 commit comments