@@ -24,7 +24,11 @@ define(['./esprimaAdapter', 'lang'], function (esprima, lang) {
24
24
25
25
//This string is saved off because JSLint complains
26
26
//about obj.arguments use, as 'reserved word'
27
- var argPropName = 'arguments' ;
27
+ var argPropName = 'arguments' ,
28
+ //Default object to use for "scope" checking for UMD identifiers.
29
+ emptyScope = { } ,
30
+ mixin = lang . mixin ,
31
+ hasProp = lang . hasProp ;
28
32
29
33
//From an esprima example for traversing its ast.
30
34
function traverse ( object , visitor ) {
@@ -126,7 +130,7 @@ define(['./esprimaAdapter', 'lang'], function (esprima, lang) {
126
130
needsDefine = true ,
127
131
astRoot = esprima . parse ( fileContents ) ;
128
132
129
- parse . recurse ( astRoot , function ( callName , config , name , deps , node , factoryIdentifier ) {
133
+ parse . recurse ( astRoot , function ( callName , config , name , deps , node , factoryIdentifier , fnExpScope ) {
130
134
if ( ! deps ) {
131
135
deps = [ ] ;
132
136
}
@@ -146,7 +150,7 @@ define(['./esprimaAdapter', 'lang'], function (esprima, lang) {
146
150
} ) ;
147
151
}
148
152
149
- if ( factoryIdentifier ) {
153
+ if ( callName === 'define' && factoryIdentifier && hasProp ( fnExpScope , factoryIdentifier ) ) {
150
154
return factoryIdentifier ;
151
155
}
152
156
@@ -199,14 +203,18 @@ define(['./esprimaAdapter', 'lang'], function (esprima, lang) {
199
203
* @param {Function } onMatch function to call on a parse match.
200
204
* @param {Object } [options] This is normally the build config options if
201
205
* it is passed.
206
+ * @param {Object } [fnExpScope] holds list of function expresssion
207
+ * argument identifiers, set up internally, not passed in
202
208
*/
203
- parse . recurse = function ( object , onMatch , options ) {
209
+ parse . recurse = function ( object , onMatch , options , fnExpScope ) {
204
210
//Like traverse, but skips if branches that would not be processed
205
211
//after has application that results in tests of true or false boolean
206
212
//literal values.
207
- var key , child , result ,
213
+ var key , child , result , i , params , param ,
208
214
hasHas = options && options . has ;
209
215
216
+ fnExpScope = fnExpScope || emptyScope ;
217
+
210
218
if ( ! object ) {
211
219
return ;
212
220
}
@@ -217,24 +225,44 @@ define(['./esprimaAdapter', 'lang'], function (esprima, lang) {
217
225
object . test . type === 'Literal' ) {
218
226
if ( object . test . value ) {
219
227
//Take the if branch
220
- this . recurse ( object . consequent , onMatch , options ) ;
228
+ this . recurse ( object . consequent , onMatch , options , fnExpScope ) ;
221
229
} else {
222
230
//Take the else branch
223
- this . recurse ( object . alternate , onMatch , options ) ;
231
+ this . recurse ( object . alternate , onMatch , options , fnExpScope ) ;
224
232
}
225
233
} else {
226
- result = this . parseNode ( object , onMatch ) ;
234
+ result = this . parseNode ( object , onMatch , fnExpScope ) ;
227
235
if ( result === false ) {
228
236
return ;
229
237
} else if ( typeof result === 'string' ) {
230
238
return result ;
231
239
}
232
240
241
+ //Build up a "scope" object that informs nested recurse calls if
242
+ //the define call references an identifier that is likely a UMD
243
+ //wrapped function expresion argument.
244
+ if ( object . type === 'ExpressionStatement' && object . expression &&
245
+ object . expression . type === 'CallExpression' && object . expression . callee &&
246
+ object . expression . callee . type === 'FunctionExpression' ) {
247
+ object = object . expression . callee ;
248
+
249
+ if ( object . params && object . params . length ) {
250
+ params = object . params ;
251
+ fnExpScope = mixin ( { } , fnExpScope , true ) ;
252
+ for ( i = 0 ; i < params . length ; i ++ ) {
253
+ param = params [ i ] ;
254
+ if ( param . type === 'Identifier' ) {
255
+ fnExpScope [ param . name ] = true ;
256
+ }
257
+ }
258
+ }
259
+ }
260
+
233
261
for ( key in object ) {
234
262
if ( object . hasOwnProperty ( key ) ) {
235
263
child = object [ key ] ;
236
264
if ( typeof child === 'object' && child !== null ) {
237
- result = this . recurse ( child , onMatch , options ) ;
265
+ result = this . recurse ( child , onMatch , options , fnExpScope ) ;
238
266
if ( typeof result === 'string' ) {
239
267
break ;
240
268
}
@@ -246,23 +274,10 @@ define(['./esprimaAdapter', 'lang'], function (esprima, lang) {
246
274
//passed in as a function expression, indicating a UMD-type of
247
275
//wrapping.
248
276
if ( typeof result === 'string' ) {
249
- if ( object . type === 'ExpressionStatement' && object . expression &&
250
- object . expression . type === 'CallExpression' && object . expression . callee &&
251
- object . expression . callee . type === 'FunctionExpression' ) {
252
- object = object . expression . callee ;
253
-
254
- if ( object . params && object . params . length ) {
255
- if ( object . params . some ( function ( param ) {
256
- //Found an identifier match, so stop parsing from this
257
- //level down.
258
- return param . type === 'Identifier' &&
259
- param . name === result ;
260
- } ) ) {
261
- //Just a plain return, parsing can continue past this
262
- //point.
263
- return ;
264
- }
265
- }
277
+ if ( hasProp ( fnExpScope , result ) ) {
278
+ //Just a plain return, parsing can continue past this
279
+ //point.
280
+ return ;
266
281
}
267
282
268
283
return result ;
@@ -740,11 +755,14 @@ define(['./esprimaAdapter', 'lang'], function (esprima, lang) {
740
755
* @param {Function } onMatch a function to call when a match is found.
741
756
* It is passed the match name, and the config, name, deps possible args.
742
757
* The config, name and deps args are not normalized.
758
+ * @param {Object } fnExpScope an object whose keys are all function
759
+ * expression identifiers that should be in scope. Useful for UMD wrapper
760
+ * detection to avoid parsing more into the wrapped UMD code.
743
761
*
744
762
* @returns {String } a JS source string with the valid require/define call.
745
763
* Otherwise null.
746
764
*/
747
- parse . parseNode = function ( node , onMatch ) {
765
+ parse . parseNode = function ( node , onMatch , fnExpScope ) {
748
766
var name , deps , cjsDeps , arg , factory , exp , refsDefine , bodyNode ,
749
767
args = node && node [ argPropName ] ,
750
768
callName = parse . hasRequire ( node ) ;
@@ -818,7 +836,8 @@ define(['./esprimaAdapter', 'lang'], function (esprima, lang) {
818
836
}
819
837
820
838
return onMatch ( "define" , null , name , deps , node ,
821
- ( factory && factory . type === 'Identifier' ? factory . name : undefined ) ) ;
839
+ ( factory && factory . type === 'Identifier' ? factory . name : undefined ) ,
840
+ fnExpScope ) ;
822
841
} else if ( node . type === 'CallExpression' && node . callee &&
823
842
node . callee . type === 'FunctionExpression' &&
824
843
node . callee . body && node . callee . body . body &&
@@ -846,7 +865,7 @@ define(['./esprimaAdapter', 'lang'], function (esprima, lang) {
846
865
847
866
if ( refsDefine ) {
848
867
return onMatch ( "define" , null , null , null , exp . expression ,
849
- exp . expression . arguments [ 0 ] . name ) ;
868
+ exp . expression . arguments [ 0 ] . name , fnExpScope ) ;
850
869
}
851
870
}
852
871
}
0 commit comments