moduleVisitor.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. 'use strict'
  2. exports.__esModule = true
  3. /**
  4. * Returns an object of node visitors that will call
  5. * 'visitor' with every discovered module path.
  6. *
  7. * todo: correct function prototype for visitor
  8. * @param {Function(String)} visitor [description]
  9. * @param {[type]} options [description]
  10. * @return {object}
  11. */
  12. exports.default = function visitModules(visitor, options) {
  13. // if esmodule is not explicitly disabled, it is assumed to be enabled
  14. options = Object.assign({ esmodule: true }, options)
  15. let ignoreRegExps = []
  16. if (options.ignore != null) {
  17. ignoreRegExps = options.ignore.map(p => new RegExp(p))
  18. }
  19. function checkSourceValue(source, importer) {
  20. if (source == null) return //?
  21. // handle ignore
  22. if (ignoreRegExps.some(re => re.test(source.value))) return
  23. // fire visitor
  24. visitor(source, importer)
  25. }
  26. // for import-y declarations
  27. function checkSource(node) {
  28. checkSourceValue(node.source, node)
  29. }
  30. // for CommonJS `require` calls
  31. // adapted from @mctep: http://git.io/v4rAu
  32. function checkCommon(call) {
  33. if (call.callee.type !== 'Identifier') return
  34. if (call.callee.name !== 'require') return
  35. if (call.arguments.length !== 1) return
  36. const modulePath = call.arguments[0]
  37. if (modulePath.type !== 'Literal') return
  38. if (typeof modulePath.value !== 'string') return
  39. checkSourceValue(modulePath, call)
  40. }
  41. function checkAMD(call) {
  42. if (call.callee.type !== 'Identifier') return
  43. if (call.callee.name !== 'require' &&
  44. call.callee.name !== 'define') return
  45. if (call.arguments.length !== 2) return
  46. const modules = call.arguments[0]
  47. if (modules.type !== 'ArrayExpression') return
  48. for (let element of modules.elements) {
  49. if (element.type !== 'Literal') continue
  50. if (typeof element.value !== 'string') continue
  51. if (element.value === 'require' ||
  52. element.value === 'exports') continue // magic modules: http://git.io/vByan
  53. checkSourceValue(element, element)
  54. }
  55. }
  56. const visitors = {}
  57. if (options.esmodule) {
  58. Object.assign(visitors, {
  59. 'ImportDeclaration': checkSource,
  60. 'ExportNamedDeclaration': checkSource,
  61. 'ExportAllDeclaration': checkSource,
  62. })
  63. }
  64. if (options.commonjs || options.amd) {
  65. visitors['CallExpression'] = function (call) {
  66. if (options.commonjs) checkCommon(call)
  67. if (options.amd) checkAMD(call)
  68. }
  69. }
  70. return visitors
  71. }
  72. /**
  73. * make an options schema for the module visitor, optionally
  74. * adding extra fields.
  75. */
  76. function makeOptionsSchema(additionalProperties) {
  77. const base = {
  78. 'type': 'object',
  79. 'properties': {
  80. 'commonjs': { 'type': 'boolean' },
  81. 'amd': { 'type': 'boolean' },
  82. 'esmodule': { 'type': 'boolean' },
  83. 'ignore': {
  84. 'type': 'array',
  85. 'minItems': 1,
  86. 'items': { 'type': 'string' },
  87. 'uniqueItems': true,
  88. },
  89. },
  90. 'additionalProperties': false,
  91. }
  92. if (additionalProperties){
  93. for (let key in additionalProperties) {
  94. base.properties[key] = additionalProperties[key]
  95. }
  96. }
  97. return base
  98. }
  99. exports.makeOptionsSchema = makeOptionsSchema
  100. /**
  101. * json schema object for options parameter. can be used to build
  102. * rule options schema object.
  103. * @type {Object}
  104. */
  105. exports.optionsSchema = makeOptionsSchema()