333
schangxiang@126.com
2025-09-19 18966e02fb573c7e2bb0c6426ed792b38b910940
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
 
/**
 * Module dependencies.
 */
 
var types = require('ast-types');
var esprima = require('esprima');
var escodegen = require('escodegen');
 
/**
 * Helper functions.
 */
 
var n = types.namedTypes;
var b = types.builders;
 
/**
 * Module exports.
 */
 
module.exports = degenerator;
 
/**
 * Turns sync JavaScript code into an JavaScript with async Generator Functions.
 *
 * @param {String} jsStr JavaScript string to convert
 * @param {Array} names Array of function names to add `yield` operators to
 * @return {String} Converted JavaScript string with Generator functions injected
 * @api public
 */
 
function degenerator (jsStr, names) {
  if (!Array.isArray(names)) {
    throw new TypeError('an array of async function "names" is required');
  }
 
  var ast = esprima.parse(jsStr);
 
  // duplicate the `names` array since it's rude to augment the user-provided
  // array
  names = names.slice(0);
 
 
  // first pass is to find the `function` nodes and turn them into `function *`
  // generator functions. We also add the names of the functions to the `names`
  // array
  types.visit(ast, {
    visitFunction: function(path) {
      if (path.node.id) {
        // got a "function" expression/statement,
        // convert it into a "generator function"
        path.node.generator = true;
 
        // add function name to `names` array
        names.push(path.node.id.name);
      }
 
      this.traverse(path);
    }
  });
 
  // second pass is for adding `yield` statements to any function
  // invocations that match the given `names` array.
  types.visit(ast, {
    visitCallExpression: function(path) {
      if (checkNames(path.node, names)) {
        // a "function invocation" expression,
        // we need to inject a `YieldExpression`
        var name = path.name;
        var parent = path.parent.node;
 
        var delegate = false;
        var expr = b.yieldExpression(path.node, delegate);
        if (parent['arguments']) {
          // parent is a `CallExpression` type
          parent['arguments'][name] = expr;
        } else {
          parent[name] = expr;
        }
      }
 
      this.traverse(path);
    }
  });
 
  return escodegen.generate(ast);
}
 
/**
 * Returns `true` if `node` has a matching name to one of the entries in the
 * `names` array.
 *
 * @param {types.Node} node
 * @param {Array} names Array of function names to return true for
 * @return {Boolean}
 * @api private
 */
 
function checkNames (node, names) {
  var name;
  var callee = node.callee;
  if ('Identifier' == callee.type) {
    name = callee.name;
  } else if ('MemberExpression' == callee.type) {
    name = callee.object.name + '.' + (callee.property.name || callee.property.raw);
  } else if ('FunctionExpression' == callee.type) {
    if (callee.id) {
      name = callee.id.name;
    } else {
      return false;
    }
  } else {
    throw new Error('don\'t know how to get name for: ' + callee.type);
  }
 
  // now that we have the `name`, check if any entries match in the `names` array
  var n;
  for (var i = 0; i < names.length; i++) {
    n = names[i];
    if (n.test) {
      // regexp
      if (n.test(name)) return true;
    } else {
      if (name == n) return true;
    }
  }
 
  return false;
}