@@ -266,14 +266,14 @@ function captureTomDoc(comments) {
266
266
}
267
267
}
268
268
269
- ExportMap . get = function ( source , context ) {
269
+ ExportMap . get = function ( source , context , options ) {
270
270
const path = resolve ( source , context )
271
271
if ( path == null ) return null
272
272
273
- return ExportMap . for ( childContext ( path , context ) )
273
+ return ExportMap . for ( childContext ( path , context ) , options )
274
274
}
275
275
276
- ExportMap . for = function ( context ) {
276
+ ExportMap . for = function ( context , options = { } ) {
277
277
const { path } = context
278
278
279
279
const cacheKey = hashObject ( context ) . digest ( 'hex' )
@@ -300,14 +300,14 @@ ExportMap.for = function (context) {
300
300
const content = fs . readFileSync ( path , { encoding : 'utf8' } )
301
301
302
302
// check for and cache ignore
303
- if ( isIgnored ( path , context ) || ! unambiguous . test ( content ) ) {
303
+ if ( isIgnored ( path , context ) || ( ! options . useCommonjsExports && ! unambiguous . test ( content ) ) ) {
304
304
log ( 'ignored path due to unambiguous regex or ignore settings:' , path )
305
305
exportCache . set ( cacheKey , null )
306
306
return null
307
307
}
308
308
309
309
log ( 'cache miss' , cacheKey , 'for path' , path )
310
- exportMap = ExportMap . parse ( path , content , context )
310
+ exportMap = ExportMap . parse ( path , content , context , options )
311
311
312
312
// ambiguous modules return null
313
313
if ( exportMap == null ) return null
@@ -319,7 +319,9 @@ ExportMap.for = function (context) {
319
319
}
320
320
321
321
322
- ExportMap . parse = function ( path , content , context ) {
322
+ ExportMap . parse = function ( path , content , context , options = { } ) {
323
+ log ( 'using commonjs exports:' , options . useCommonjsExports )
324
+
323
325
var m = new ExportMap ( path )
324
326
325
327
try {
@@ -330,7 +332,7 @@ ExportMap.parse = function (path, content, context) {
330
332
return m // can't continue
331
333
}
332
334
333
- if ( ! unambiguous . isModule ( ast ) ) return null
335
+ if ( ! options . useCommonjsExports && ! unambiguous . isModule ( ast ) ) return null
334
336
335
337
const docstyle = ( context . settings && context . settings [ 'import/docstyle' ] ) || [ 'jsdoc' ]
336
338
const docStyleParsers = { }
@@ -362,7 +364,7 @@ ExportMap.parse = function (path, content, context) {
362
364
function resolveImport ( value ) {
363
365
const rp = remotePath ( value )
364
366
if ( rp == null ) return null
365
- return ExportMap . for ( childContext ( rp , context ) )
367
+ return ExportMap . for ( childContext ( rp , context ) , options )
366
368
}
367
369
368
370
function getNamespace ( identifier ) {
@@ -390,7 +392,7 @@ ExportMap.parse = function (path, content, context) {
390
392
const existing = m . imports . get ( p )
391
393
if ( existing != null ) return existing . getter
392
394
393
- const getter = ( ) => ExportMap . for ( childContext ( p , context ) )
395
+ const getter = ( ) => ExportMap . for ( childContext ( p , context ) , options )
394
396
m . imports . set ( p , {
395
397
getter,
396
398
source : { // capturing actual node reference holds full AST in memory!
@@ -401,8 +403,118 @@ ExportMap.parse = function (path, content, context) {
401
403
return getter
402
404
}
403
405
406
+ // for saving all commonjs exports
407
+ let moduleExports = { }
408
+
409
+ // for if module exports has been declared directly (exports/module.exports = ...)
410
+ let moduleExportsMain = null
411
+
412
+ function parseModuleExportsObjectExpression ( node ) {
413
+ moduleExportsMain = true
414
+ moduleExports = { }
415
+ node . properties . forEach (
416
+ function ( property ) {
417
+ const keyType = property . key . type
418
+
419
+ if ( keyType === 'Identifier' ) {
420
+ const keyName = property . key . name
421
+ moduleExports [ keyName ] = property . value
422
+ }
423
+ else if ( keyType === 'Literal' ) {
424
+ const keyName = property . key . value
425
+ moduleExports [ keyName ] = property . value
426
+ }
427
+ }
428
+ )
429
+ }
430
+
431
+ function handleModuleExports ( ) {
432
+ let isEsModule = false
433
+ const esModule = moduleExports . __esModule
434
+ if ( esModule && esModule . type === 'Literal' && esModule . value ) {
435
+ // for interopRequireDefault calls
436
+ }
437
+
438
+ Object . getOwnPropertyNames ( moduleExports ) . forEach ( function ( propertyName ) {
439
+ m . namespace . set ( propertyName )
440
+ } )
441
+
442
+ if ( ! isEsModule && moduleExportsMain && ! options . noInterop ) {
443
+ // recognizes default for import statements
444
+ m . namespace . set ( 'default' )
445
+ }
446
+ }
404
447
405
448
ast . body . forEach ( function ( n ) {
449
+ if ( options . useCommonjsExports ) {
450
+ if ( n . type === 'ExpressionStatement' ) {
451
+ if ( n . expression . type === 'AssignmentExpression' ) {
452
+ const left = n . expression . left
453
+ const right = n . expression . right
454
+
455
+ // exports/module.exports = ...
456
+ if ( isCommonjsExportsObject ( left ) ) {
457
+ moduleExportsMain = true
458
+
459
+ // exports/module.exports = {...}
460
+ if ( right . type === 'ObjectExpression' ) {
461
+ parseModuleExportsObjectExpression ( right )
462
+ }
463
+ }
464
+ else if ( left . type === 'MemberExpression'
465
+ && isCommonjsExportsObject ( left . object ) ) {
466
+ // (exports/module.exports).<name> = ...
467
+ if ( left . property . type === 'Identifier' ) {
468
+ const keyName = left . property . name
469
+ moduleExports [ keyName ] = right
470
+ }
471
+ // (exports/module.exports).["<name>"] = ...
472
+ else if ( left . property . type === 'Literal' ) {
473
+ const keyName = left . property . value
474
+ moduleExports [ keyName ] = right
475
+ }
476
+ }
477
+ else return
478
+ }
479
+ // Object.defineProperty((exports/module.exports), <name>, {value: <value>})
480
+ else if ( n . expression . type === 'CallExpression' ) {
481
+ const call = n . expression
482
+
483
+ const callee = call . callee
484
+ if ( callee . type !== 'MemberExpression' ) return
485
+ if ( callee . object . type !== 'Identifier' || call . object . type !== 'Object' ) return
486
+ if ( callee . property . type !== 'Identifier' || call . property . name !== 'defineProperty' ) return
487
+
488
+ if ( call . arguments . length !== 3 ) return
489
+ if ( ! isCommonjsExportsObject ( call . arguments [ 0 ] ) ) return
490
+ if ( call . arguments [ 1 ] . type !== 'Literal' ) return
491
+ if ( call . arguments [ 2 ] . type !== 'ObjectExpression' ) return
492
+
493
+ call . arguments [ 2 ] . properties . forEach ( function ( defineProperty ) {
494
+ if ( defineProperty . type !== 'Property' ) return
495
+
496
+ if ( defineProperty . key . type === 'Literal'
497
+ && defineProperty . key . value === 'value' ) {
498
+ // {'value': <value>}
499
+ Object . defineProperty (
500
+ moduleExports ,
501
+ call . arguments [ 1 ] . value ,
502
+ defineProperty . value
503
+ )
504
+ }
505
+ else if ( defineProperty . key . type === 'Identifier'
506
+ && defineProperty . key . name === 'value' ) {
507
+ // {value: <value>}
508
+ Object . defineProperty (
509
+ moduleExports ,
510
+ call . arguments [ 1 ] . value ,
511
+ defineProperty . value
512
+ )
513
+ }
514
+ } )
515
+ }
516
+ }
517
+ }
406
518
407
519
if ( n . type === 'ExportDefaultDeclaration' ) {
408
520
const exportMeta = captureDoc ( docStyleParsers , n )
@@ -483,6 +595,8 @@ ExportMap.parse = function (path, content, context) {
483
595
}
484
596
} )
485
597
598
+ if ( options . useCommonjsExports ) handleModuleExports ( )
599
+
486
600
return m
487
601
}
488
602
@@ -527,3 +641,25 @@ function childContext(path, context) {
527
641
path,
528
642
}
529
643
}
644
+
645
+ /**
646
+ * Check if a given node is exports, module.exports, or module['exports']
647
+ * @param {node } node
648
+ * @return {boolean }
649
+ */
650
+ function isCommonjsExportsObject ( node ) {
651
+ // exports
652
+ if ( node . type === 'Identifier' && node . name === 'exports' ) return true
653
+
654
+ if ( node . type !== 'MemberExpression' ) return false
655
+
656
+ if ( node . object . type === 'Identifier' && node . object . name === 'module' ) {
657
+ // module.exports
658
+ if ( node . property . type === 'Identifier' && node . property . name === 'exports' ) return true
659
+
660
+ // module['exports']
661
+ if ( node . property . type === 'Literal' && node . property . value === 'exports' ) return true
662
+ }
663
+
664
+ return false
665
+ }
0 commit comments