1 | var babylon = require('babylon');
|
2 | var traverse = require('babel-traverse').default;
|
3 | var generate = require('babel-generator').default;
|
4 | var t = require('babel-types');
|
5 |
|
6 | exports.tryToParseJS = function (code, options) {
|
7 | try {
|
8 | babylon.parseExpression(code, {
|
9 | plugins: [
|
10 | 'objectRestSpread',
|
11 | 'functionBind'
|
12 | ]
|
13 | });
|
14 |
|
15 | return null;
|
16 | } catch (err) {
|
17 | var pos = err.pos;
|
18 |
|
19 | if (typeof pos !== 'number') {
|
20 | throw err;
|
21 | }
|
22 |
|
23 | if (code[pos] !== '}') {
|
24 | throw new Error('Syntax error in expression: ' + JSON.stringify(code) + ' at pos: ' + pos);
|
25 | }
|
26 |
|
27 | var parsed = parseJS(code.slice(0, pos), options);
|
28 |
|
29 | return {
|
30 | vars: parsed.vars,
|
31 | js: parsed.js,
|
32 | original: parsed.original,
|
33 | rest: code.slice(pos + 1)
|
34 | };
|
35 | }
|
36 | };
|
37 |
|
38 | var parseJS = exports.parseJS = function (code, options) {
|
39 | var newCode = '(' + code + ')';
|
40 |
|
41 | try {
|
42 | var ast = babylon.parse(newCode, {
|
43 | plugins: [
|
44 | 'objectRestSpread',
|
45 | 'functionBind'
|
46 | ]
|
47 | });
|
48 | } catch (err) {
|
49 | var pos = err.pos;
|
50 |
|
51 | if (typeof pos !== 'number') {
|
52 | throw err;
|
53 | }
|
54 |
|
55 | throw new Error('Syntax error in expression: ' + JSON.stringify(code) + ' at pos: ' + (pos - 1));
|
56 | }
|
57 |
|
58 | var uid;
|
59 | var uid2;
|
60 | var used = {};
|
61 |
|
62 | traverse(ast, {
|
63 | enter: function (path) {
|
64 | if (path.isProgram()) {
|
65 | uid = path.scope.generateUid('$');
|
66 | uid2 = path.scope.generateUid('_');
|
67 |
|
68 | return;
|
69 | }
|
70 |
|
71 | if (
|
72 | path.parentPath
|
73 | && path.parentPath.parentPath
|
74 | && path.parentPath.isExpressionStatement()
|
75 | && path.parentPath.parentPath.isProgram()
|
76 | && (!path.node.id || path.node.id.name !== uid2)
|
77 | ) {
|
78 | path.replaceWith(
|
79 | t.functionExpression(
|
80 | t.identifier(uid2),
|
81 | [
|
82 | t.identifier(uid)
|
83 | ],
|
84 | t.blockStatement([
|
85 | t.returnStatement(path.node)
|
86 | ])
|
87 | )
|
88 | );
|
89 | }
|
90 |
|
91 | if (path.isThisExpression()) {
|
92 | var scope = path.scope;
|
93 |
|
94 | while ((!scope.path.node.id || scope.path.node.id.name !== uid2) && scope.path.isArrowFunctionExpression()) {
|
95 | scope = scope.parent;
|
96 | }
|
97 |
|
98 | if (scope.path.node.id && scope.path.node.id.name === uid2) {
|
99 | if (
|
100 | path.parentPath
|
101 | && path.parentPath.isMemberExpression()
|
102 | && !path.parent.computed
|
103 | && isOuterVar(path.parent.property.name)
|
104 | ) {
|
105 | used[path.parent.property.name] = true;
|
106 | }
|
107 |
|
108 | path.replaceWith(t.identifier(
|
109 | options.__keepScope__
|
110 | ? options.__thisUid__
|
111 | : uid
|
112 | ));
|
113 | }
|
114 | }
|
115 |
|
116 | if (options.__keepScope__) {
|
117 | return;
|
118 | }
|
119 |
|
120 | if (
|
121 | path.isIdentifier()
|
122 | && (path.isExpression() || (path.parentPath.isAssignmentExpression() && path.parentPath.node.left === path.node))
|
123 | && !path.isPure()
|
124 | && path.node.name !== uid
|
125 | && options.globalVars.indexOf(path.node.name) === -1
|
126 | ) {
|
127 | if (isOuterVar(path.node.name)) {
|
128 | used[path.node.name] = true;
|
129 | }
|
130 |
|
131 | path.replaceWith(
|
132 | t.memberExpression(
|
133 | t.identifier(uid),
|
134 | t.identifier(path.node.name)
|
135 | )
|
136 | );
|
137 | }
|
138 | }
|
139 | });
|
140 |
|
141 | var generated = generate(ast, {}, newCode).code;
|
142 |
|
143 | return {
|
144 | vars: used,
|
145 | js: generated,
|
146 | original: code
|
147 | };
|
148 | };
|
149 |
|
150 | function isOuterVar(variable) {
|
151 | return (
|
152 | variable !== 'args'
|
153 | && variable !== 'globals'
|
154 | && variable !== '$$'
|
155 | );
|
156 | }
|