@dacely/toildefender 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/NOTICE.md +16 -0
- package/README.md +380 -0
- package/cli.js +168 -0
- package/defendjs.js +7 -0
- package/docs/all-modes-output.demo.js +673 -0
- package/estest.js +49 -0
- package/esutils.js +107 -0
- package/logger.js +28 -0
- package/obfuscator.js +534 -0
- package/package.json +108 -0
- package/processors/deadCode.js +62 -0
- package/processors/flattener.js +808 -0
- package/processors/health.js +55 -0
- package/processors/identifiers.js +256 -0
- package/processors/literalObfuscator.js +40 -0
- package/processors/literals.js +233 -0
- package/processors/methods.js +332 -0
- package/processors/modules.js +231 -0
- package/processors/normalizer.js +490 -0
- package/processors/numericVm.js +950 -0
- package/processors/postprocessing.js +69 -0
- package/processors/preprocessing.js +248 -0
- package/processors/scopes.js +177 -0
- package/processors/uglifier.js +26 -0
- package/processors/variables.js +185 -0
- package/toildefender.js +7 -0
- package/traverser.js +115 -0
- package/utils.js +135 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const METHODS_INJECT = `
|
|
4
|
+
function veilmark$mergeArguments(a, b) {
|
|
5
|
+
return Array.prototype.slice.call(a).concat(Array.prototype.slice.call(b));
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function veilmark$bind() {
|
|
9
|
+
var fn = arguments[0], prepend = Array.prototype.slice.call(arguments, 1);
|
|
10
|
+
var wrapper = function() {
|
|
11
|
+
return fn.apply(this, prepend.concat(Array.prototype.slice.call(arguments)));
|
|
12
|
+
};
|
|
13
|
+
wrapper.prototype = fn.prototype;
|
|
14
|
+
return wrapper;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function veilmark$sliceArguments(args, num) {
|
|
18
|
+
return Array.prototype.slice.call(args, num);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function veilmark$toObject(schema, values) {
|
|
22
|
+
var obj = {};
|
|
23
|
+
if (values === undefined) {
|
|
24
|
+
for (var legacy = 0; legacy < schema.length; legacy += 2) {
|
|
25
|
+
obj[schema[legacy]] = schema[legacy + 1];
|
|
26
|
+
}
|
|
27
|
+
return obj;
|
|
28
|
+
}
|
|
29
|
+
var cursor = 2;
|
|
30
|
+
var salt = schema[0];
|
|
31
|
+
var count = schema[1];
|
|
32
|
+
for (var i = 0; i < count; i += 1) {
|
|
33
|
+
var len = schema[cursor++] ^ ((salt + i * 131) & 65535);
|
|
34
|
+
var key = "";
|
|
35
|
+
for (var j = 0; j < len; j += 1) {
|
|
36
|
+
key += String.fromCharCode(schema[cursor++] ^ ((salt + i * 257 + j * 17) & 65535));
|
|
37
|
+
}
|
|
38
|
+
obj[key] = values[i];
|
|
39
|
+
}
|
|
40
|
+
return obj;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function veilmark$decodeString(arr) {
|
|
44
|
+
return arr.map(function(x) { return String.fromCharCode(x & ~0 >>> 16) + String.fromCharCode(x >> 16); }).join("");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function veilmark$fromCharCodes() {
|
|
48
|
+
return String.fromCharCode.apply(null, arguments);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
var assert = require("assert");
|
|
54
|
+
var fs = require("fs");
|
|
55
|
+
|
|
56
|
+
var _ = require("lodash");
|
|
57
|
+
var escope = require("escope");
|
|
58
|
+
var esprima = require("esprima");
|
|
59
|
+
|
|
60
|
+
var estest = require("../estest");
|
|
61
|
+
var traverser = require("../traverser");
|
|
62
|
+
var utils = require("../utils");
|
|
63
|
+
|
|
64
|
+
const ANON_METHOD_ID = "veilmark$anonymousMethodId";
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Wrap function with veilmark$bind.
|
|
68
|
+
* @param {Identifier} Function identifier
|
|
69
|
+
* @returns {Node} Wrapped function
|
|
70
|
+
*/
|
|
71
|
+
function createMethodStub(id) {
|
|
72
|
+
assert.equal(id.type, "Identifier");
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
type: "CallExpression",
|
|
76
|
+
callee: { type: "Identifier", name: "veilmark$bind" },
|
|
77
|
+
arguments: [
|
|
78
|
+
id
|
|
79
|
+
]
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function anonymousMethodName(node) {
|
|
84
|
+
assert.equal(node.type, "FunctionExpression");
|
|
85
|
+
|
|
86
|
+
if (!node[ANON_METHOD_ID]) {
|
|
87
|
+
Object.defineProperty(node, ANON_METHOD_ID, {
|
|
88
|
+
configurable: false,
|
|
89
|
+
enumerable: false,
|
|
90
|
+
value: `veilmark$anon$${utils.hash(node)}`
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return node[ANON_METHOD_ID];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Get index of argument in function.
|
|
99
|
+
* @param {Function} method Function
|
|
100
|
+
* @param {Identifier} identifier} Argument identifier
|
|
101
|
+
* @returns {number} Index of argument
|
|
102
|
+
*/
|
|
103
|
+
function getArgumentIndex(method, identifier) {
|
|
104
|
+
assert.ok(estest.isFunction(method));
|
|
105
|
+
assert.equal(identifier.type, "Identifier");
|
|
106
|
+
|
|
107
|
+
return _.findIndex(method.params, x => x.name == identifier.name);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function rawArgumentsIdentifier() {
|
|
111
|
+
return {
|
|
112
|
+
type: "Identifier",
|
|
113
|
+
name: "arguments",
|
|
114
|
+
veilmark$rawArguments: true
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
module.exports = class Methods {
|
|
119
|
+
|
|
120
|
+
constructor (logger) {
|
|
121
|
+
this.logger = logger;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Adds helper methods to the beginning of the app.
|
|
126
|
+
* @param {Node} Root node
|
|
127
|
+
*/
|
|
128
|
+
addCustomBind (ast) {
|
|
129
|
+
assert.ok(estest.isNode(ast));
|
|
130
|
+
|
|
131
|
+
var code = esprima.parse(METHODS_INJECT);
|
|
132
|
+
code.type = "BlockStatement";
|
|
133
|
+
ast.body.splice(0, 0, code);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Checks whether a method refers to the "arguments" array.
|
|
138
|
+
* @param {Function} method
|
|
139
|
+
* @param {ScopeManager} scopeManager
|
|
140
|
+
* @returns {boolean}
|
|
141
|
+
*/
|
|
142
|
+
methodRefersToArguments (method, scopeManager) {
|
|
143
|
+
assert.ok(estest.isFunction(method));
|
|
144
|
+
assert.ok(scopeManager);
|
|
145
|
+
|
|
146
|
+
return scopeManager
|
|
147
|
+
.acquire(method)
|
|
148
|
+
.references
|
|
149
|
+
.some(reference => !utils.isResolvedReference(reference) && reference.identifier.name == "arguments");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Inserts code to copy/slice arguments from the arguments array like
|
|
154
|
+
* function () { ... }
|
|
155
|
+
* to
|
|
156
|
+
* function () { var veilmark$arguments = veilmark$sliceArguments(arguments, 1); ... }
|
|
157
|
+
* @param {Function} method
|
|
158
|
+
* @param {number} num Number of arguments to be sliced off. 0 if none.
|
|
159
|
+
*/
|
|
160
|
+
removeFirstArguments (method, num) {
|
|
161
|
+
assert.ok(estest.isFunction(method));
|
|
162
|
+
assert.equal(typeof num, "number");
|
|
163
|
+
|
|
164
|
+
method.body.body.splice(0, 0, {
|
|
165
|
+
type: "VariableDeclaration",
|
|
166
|
+
kind: "var",
|
|
167
|
+
declarations: [
|
|
168
|
+
{
|
|
169
|
+
type: "VariableDeclarator",
|
|
170
|
+
id: { type: "Identifier", name: "veilmark$arguments" },
|
|
171
|
+
init: rawArgumentsIdentifier()
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
type: "VariableDeclarator",
|
|
175
|
+
id: { type: "Identifier", name: "veilmark$bareArguments" },
|
|
176
|
+
init: num > 0 ? {
|
|
177
|
+
type: "CallExpression",
|
|
178
|
+
callee: { type: "Identifier", name: "veilmark$sliceArguments" },
|
|
179
|
+
arguments: [
|
|
180
|
+
rawArgumentsIdentifier(),
|
|
181
|
+
{ type: "Literal", value: num, veilmark$removeFirstArguments: true }
|
|
182
|
+
]
|
|
183
|
+
} : rawArgumentsIdentifier()
|
|
184
|
+
}
|
|
185
|
+
],
|
|
186
|
+
veilmark$reassigningArguments: true,
|
|
187
|
+
veilmark$followsSlicingArguments: num > 0
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Lists all methods.
|
|
193
|
+
* @param {Node} ast Root node
|
|
194
|
+
* @returns {string[]} Method names
|
|
195
|
+
*/
|
|
196
|
+
listMethods (ast) {
|
|
197
|
+
assert.ok(estest.isNode(ast));
|
|
198
|
+
|
|
199
|
+
var methods = [];
|
|
200
|
+
|
|
201
|
+
traverser.traverse(ast, [], (node, stack) => {
|
|
202
|
+
if (node.type == "FunctionDeclaration") { // Statement
|
|
203
|
+
methods.push(node.id.name);
|
|
204
|
+
} else if (node.type == "FunctionExpression") { // Expression
|
|
205
|
+
methods.push(anonymousMethodName(node));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return node;
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
return methods;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Extracts all methods from the AST.
|
|
216
|
+
* @param {Node} ast Root node
|
|
217
|
+
* @returns {Function[]}
|
|
218
|
+
*/
|
|
219
|
+
extractMethods (ast) {
|
|
220
|
+
assert.ok(estest.isNode(ast));
|
|
221
|
+
|
|
222
|
+
var methods = [];
|
|
223
|
+
|
|
224
|
+
traverser.traverse(ast, [], (node, stack) => {
|
|
225
|
+
if (node.type == "FunctionDeclaration") { // Statement
|
|
226
|
+
methods.push(node);
|
|
227
|
+
return { type: "ExpressionStatement", expression: createMethodStub(node.id) }; // This is not ideal
|
|
228
|
+
} else if (node.type == "FunctionExpression") { // Expression
|
|
229
|
+
var id = anonymousMethodName(node);
|
|
230
|
+
// Merge into old object instead of creating a new one to preserve object references
|
|
231
|
+
methods.push(_.assign(node, {
|
|
232
|
+
type: "FunctionDeclaration",
|
|
233
|
+
id: { type: "Identifier", name: id }
|
|
234
|
+
}));
|
|
235
|
+
return createMethodStub({ type: "Identifier", name: id });
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return node;
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
return methods;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Replaces direct argument references with arguments references like
|
|
246
|
+
* function (a) { return a; }
|
|
247
|
+
* to
|
|
248
|
+
* function (a) { return veilmark$arguments[0]; }
|
|
249
|
+
* @param {Function} method Function whose body will be transformed
|
|
250
|
+
* @param {boolean} useReassignedVariable Use veilmark$arguments instead of arguments
|
|
251
|
+
* @returns {Function} Function from method parameter
|
|
252
|
+
*/
|
|
253
|
+
replaceArgumentReferences (method, useReassignedVariable) {
|
|
254
|
+
assert.ok(estest.isFunction(method));
|
|
255
|
+
|
|
256
|
+
traverser.traverse(method.body, [], (node, stack) => {
|
|
257
|
+
if (node.type == "Identifier") {
|
|
258
|
+
var nestedFunction = stack.some(frame => estest.isFunction(frame.node));
|
|
259
|
+
if (useReassignedVariable && node.name == "arguments" && !node.veilmark$rawArguments && !nestedFunction) {
|
|
260
|
+
return { type: "Identifier", name: "veilmark$bareArguments" };
|
|
261
|
+
}
|
|
262
|
+
var index = getArgumentIndex(method, node);
|
|
263
|
+
if (index != -1) {
|
|
264
|
+
return {
|
|
265
|
+
type: "MemberExpression",
|
|
266
|
+
object: { type: "Identifier", name: useReassignedVariable ? "veilmark$arguments" : "arguments" },
|
|
267
|
+
property: { type: "Literal", value: index },
|
|
268
|
+
computed: true
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return node;
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
method.params = [];
|
|
277
|
+
|
|
278
|
+
return method;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Replaces function calls with main calls like
|
|
283
|
+
* test()
|
|
284
|
+
* to
|
|
285
|
+
* veilmark$bind(main, 1234)()
|
|
286
|
+
* @param {Node} ast Root node
|
|
287
|
+
* @param {Object[]} methodEntryExitPoints Method entry point table
|
|
288
|
+
* @param {number} methodEntryExitPoints[].entry Entry point
|
|
289
|
+
*/
|
|
290
|
+
replaceFunctionCalls (ast, methodEntryExitPoints) {
|
|
291
|
+
assert.ok(estest.isNode(ast));
|
|
292
|
+
assert.equal(typeof methodEntryExitPoints, "object");
|
|
293
|
+
|
|
294
|
+
traverser.traverse(ast, [], (node, stack) => {
|
|
295
|
+
if (node.type == "Identifier" && methodEntryExitPoints[node.name] && methodEntryExitPoints[node.name].entry) {
|
|
296
|
+
return {
|
|
297
|
+
type: "CallExpression",
|
|
298
|
+
callee: { type: "Identifier", name: "veilmark$bind" },
|
|
299
|
+
arguments: [
|
|
300
|
+
{ type: "Identifier", name: "main" },
|
|
301
|
+
{ type: "Identifier", name: methodEntryExitPoints[node.name].entry }
|
|
302
|
+
]
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
return node;
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Bumps all arguments indices like
|
|
311
|
+
* veilmark$arguments[0]
|
|
312
|
+
* to
|
|
313
|
+
* veilmark$arguments[1]
|
|
314
|
+
* @param {Function} method Function whose body will be transformed
|
|
315
|
+
* @param {number} inc Number to be added to all argument indices
|
|
316
|
+
*/
|
|
317
|
+
bumpArgumentsIndices (method, inc) {
|
|
318
|
+
assert.ok(estest.isFunction(method));
|
|
319
|
+
assert.equal(typeof inc, "number");
|
|
320
|
+
|
|
321
|
+
traverser.traverse(method.body, [], (node, stack) => {
|
|
322
|
+
if (node.type == "MemberExpression" && node.object.type == "Identifier" && node.object.name == "veilmark$arguments") {
|
|
323
|
+
node.property.value += inc;
|
|
324
|
+
}
|
|
325
|
+
if (node.veilmark$removeFirstArguments) {
|
|
326
|
+
node.value += inc;
|
|
327
|
+
}
|
|
328
|
+
return node;
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
};
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var assert = require("assert");
|
|
4
|
+
var path = require("path");
|
|
5
|
+
|
|
6
|
+
var _ = require("lodash");
|
|
7
|
+
var escope = require("escope");
|
|
8
|
+
|
|
9
|
+
var estest = require("../estest");
|
|
10
|
+
var traverser = require("../traverser");
|
|
11
|
+
var ESUtils = require("../esutils");
|
|
12
|
+
var utils = require("../utils");
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Transform calls to require().
|
|
16
|
+
* @param {Node} node Root node
|
|
17
|
+
* @param {Function} processor Transformer
|
|
18
|
+
* @returns {Node} Root node
|
|
19
|
+
*/
|
|
20
|
+
function findRequires(node, processor) {
|
|
21
|
+
assert.ok(estest.isNode(node));
|
|
22
|
+
assert.equal(typeof processor, "function");
|
|
23
|
+
|
|
24
|
+
return traverser.traverse(node, [], (node, stack) => {
|
|
25
|
+
if (node.type == "CallExpression" && node.callee.type == "Identifier" && node.callee.name == "require") {
|
|
26
|
+
return processor(node, stack);
|
|
27
|
+
} else {
|
|
28
|
+
return node;
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Split path into parts.
|
|
35
|
+
* @param {string} path
|
|
36
|
+
* @returns {string[]}
|
|
37
|
+
*/
|
|
38
|
+
function splitPath(path) {
|
|
39
|
+
return path.split(/[\/\\]/g).filter(x => x != null && x.length > 0);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Normalize path.
|
|
44
|
+
* @param {string[]} path
|
|
45
|
+
* @returns {string}
|
|
46
|
+
*/
|
|
47
|
+
function normalizePath(path) {
|
|
48
|
+
var parts = splitPath(path);
|
|
49
|
+
|
|
50
|
+
for (var i = parts.length - 1; i >= 0; --i) {
|
|
51
|
+
if (parts[i] == "" || parts[i] == ".") {
|
|
52
|
+
parts.splice(i, 1);
|
|
53
|
+
} else if (parts[i] == "..") {
|
|
54
|
+
parts.splice(i - 1, 2);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return parts.join("/");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get directory from path.
|
|
63
|
+
* @param {string} path
|
|
64
|
+
* @returns {string}
|
|
65
|
+
*/
|
|
66
|
+
function getPathDir(path) {
|
|
67
|
+
return splitPath(path).slice(0, -1).join("/");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Resolve path.
|
|
72
|
+
* TODO: This doesnt work as expected when path starts with a slash. Fix this.
|
|
73
|
+
* @param {string} curr Executing script
|
|
74
|
+
* @param {string} path Path
|
|
75
|
+
* @returns {string}
|
|
76
|
+
*/
|
|
77
|
+
function resolvePath(curr, path) {
|
|
78
|
+
return normalizePath(getPathDir(curr) + "/" + path);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = class Modules {
|
|
82
|
+
|
|
83
|
+
constructor (logger) {
|
|
84
|
+
this.logger = logger;
|
|
85
|
+
this.esutils = new ESUtils(logger);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Replace references to exports and module.exports.
|
|
90
|
+
* @param {Node} ast Root node
|
|
91
|
+
* @param {Node} replacement Replacement
|
|
92
|
+
* @returns {Node} Root node
|
|
93
|
+
*/
|
|
94
|
+
replaceExportsReferences (ast, replacement) {
|
|
95
|
+
this.esutils.setParentsRecursive(ast);
|
|
96
|
+
|
|
97
|
+
var scopeManager = escope.analyze(ast, { optimistic: true });
|
|
98
|
+
|
|
99
|
+
scopeManager.scopes.forEach(scope => {
|
|
100
|
+
scope.references
|
|
101
|
+
.filter(reference => !utils.isResolvedReference(reference))
|
|
102
|
+
.forEach(reference => {
|
|
103
|
+
var parent = reference.identifier.veilmark$parent;
|
|
104
|
+
|
|
105
|
+
if (reference.identifier.name == "exports") {
|
|
106
|
+
this.esutils.replaceNode(ast, reference.identifier, utils.cloneISwearIKnowWhatImDoing(replacement));
|
|
107
|
+
} else if (
|
|
108
|
+
parent.type == "MemberExpression"
|
|
109
|
+
&& (parent.object.type == "Identifier" && parent.object.name == "module")
|
|
110
|
+
&& ((parent.property.type == "Identifier" && parent.property.name == "exports") || (parent.property.type == "Literal" && parent.property.value == "exports"))
|
|
111
|
+
) {
|
|
112
|
+
this.esutils.replaceNode(ast, parent, utils.cloneISwearIKnowWhatImDoing(replacement));
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
return ast;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Merges multiple modules into a single main module.
|
|
122
|
+
* @param {Object.<string, Node>} modules Module dictionary
|
|
123
|
+
* @param {string} mainKey Main module key
|
|
124
|
+
* @param {ScopeManager} scopeManager Scope manager
|
|
125
|
+
* @returns {Node} Transformed root node
|
|
126
|
+
*/
|
|
127
|
+
merge (modules, mainKey, scopeManager) {
|
|
128
|
+
assert.ok(Object.keys(modules).length > 0);
|
|
129
|
+
assert.equal(typeof mainKey, "string");
|
|
130
|
+
|
|
131
|
+
modules = _.mapKeys(modules, (value, key) => normalizePath(key));
|
|
132
|
+
mainKey = normalizePath(mainKey);
|
|
133
|
+
|
|
134
|
+
var declaration = {
|
|
135
|
+
type: "VariableDeclaration",
|
|
136
|
+
kind: "var",
|
|
137
|
+
declarations: []
|
|
138
|
+
};
|
|
139
|
+
var embeds = [];
|
|
140
|
+
|
|
141
|
+
var rng = new utils.UniqueRandomAlpha(3);
|
|
142
|
+
|
|
143
|
+
var processedModules = {};
|
|
144
|
+
|
|
145
|
+
var requiresOrder = [];
|
|
146
|
+
|
|
147
|
+
function walkDeps(key, stack) {
|
|
148
|
+
stack = stack || [];
|
|
149
|
+
|
|
150
|
+
findRequires(modules[key], node => {
|
|
151
|
+
var path = node.arguments.length > 0 && node.arguments[0].value;
|
|
152
|
+
|
|
153
|
+
if (!path) {
|
|
154
|
+
return node;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (![ "/", "./", "../" ].some(x => path.indexOf(x) == 0)) {
|
|
158
|
+
return node;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
path = resolvePath(key, path);
|
|
162
|
+
|
|
163
|
+
if (path.slice(-3) == ".js") {
|
|
164
|
+
path = path.slice(0, -3);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (!modules[path]) {
|
|
168
|
+
path = path + ".js";
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
requiresOrder.push(path);
|
|
172
|
+
|
|
173
|
+
var _module = modules[path];
|
|
174
|
+
if (!_module) {
|
|
175
|
+
this.logger.warn(`Local module not found: ${path}`);
|
|
176
|
+
return node;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (stack.indexOf(path) == -1) {
|
|
180
|
+
walkDeps.call(this, path, stack.concat(path));
|
|
181
|
+
} else {
|
|
182
|
+
this.logger.warn("Skipping cyclic depedency: " + path);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (!processedModules[path]) {
|
|
186
|
+
var id = processedModules[path] = "$$module$" + rng.get();
|
|
187
|
+
|
|
188
|
+
declaration.declarations.push({
|
|
189
|
+
type: "VariableDeclarator",
|
|
190
|
+
id: { type: "Identifier", name: id },
|
|
191
|
+
init: { type: "ObjectExpression", properties: [] }
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
_module = this.replaceExportsReferences(_module, { type: "Identifier", name: id });
|
|
195
|
+
|
|
196
|
+
embeds.push({
|
|
197
|
+
type: "ExpressionStatement",
|
|
198
|
+
expression: {
|
|
199
|
+
type: "CallExpression",
|
|
200
|
+
callee: {
|
|
201
|
+
type: "FunctionExpression",
|
|
202
|
+
params: [
|
|
203
|
+
],
|
|
204
|
+
body: {
|
|
205
|
+
type: "BlockStatement",
|
|
206
|
+
body: _module.body
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
arguments: [
|
|
210
|
+
|
|
211
|
+
]
|
|
212
|
+
},
|
|
213
|
+
veilmark$module: path
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return { type: "Identifier", name: processedModules[path] };
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
// Method has to be called via .call because otherwise this is not being passed correctly for some reason
|
|
221
|
+
walkDeps.call(this, mainKey);
|
|
222
|
+
|
|
223
|
+
// Check whether the VariableDeclaration contains VariableDeclarators, because an empty VariableDeclaration causes errors
|
|
224
|
+
if (declaration.declarations.length > 0) {
|
|
225
|
+
modules[mainKey].body = [ declaration ].concat(embeds).concat(modules[mainKey].body);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return modules[mainKey];
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
};
|