-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathindex.js
106 lines (91 loc) · 3.4 KB
/
index.js
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
'use strict';
var builders = require('ast-types').builders;
var replace = require('estraverse').replace;
var arrayExpression = builders.arrayExpression;
var blockStatement = builders.blockStatement;
var callExpression = builders.callExpression;
var expressionStatement = builders.expressionStatement;
var identifier = builders.identifier;
var functionExpression = builders.functionExpression;
var literal = builders.literal;
var memberExpression = builders.memberExpression;
var variableDeclaration = builders.variableDeclaration;
var variableDeclarator = builders.variableDeclarator;
function isFunction(node) {
return node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression';
}
function isStringLiteral(node) {
return node.type === 'Literal' && typeof node.value === 'string';
}
function isPropertyAccess(node) {
return node.type === 'MemberExpression' && !node.computed;
}
function isPropertyKey(node, parent) {
return parent.type === 'Property' && parent.key === node;
}
function isStrictStatement(statement) {
return statement.type === 'ExpressionStatement' &&
statement.expression.type === 'Literal' &&
statement.expression.value === 'use strict';
}
exports.transformAst = function(ast, createVariableName) {
var usedVariables = {};
var exposesVariables = false;
var strings = [];
var stringIndexes = Object.create(null);
var stringMapIdentifier = identifier('');
function addString(string) {
if (!(string in stringIndexes)) {
stringIndexes[string] = strings.push(string) - 1;
}
return stringIndexes[string];
}
replace(ast, {
enter: function(node, parent) {
var index;
if (node.type === 'VariableDeclaration' || node.type === 'FunctionDeclaration') {
if (!exposesVariables) {
exposesVariables = !this.parents().some(isFunction);
}
} else if (node.type === 'Identifier') {
usedVariables[node.name] = true;
} else if (isStringLiteral(node) && !isPropertyKey(node, parent) && node.value !== 'use strict') {
index = addString(node.value);
return memberExpression(stringMapIdentifier, literal(index), true);
} else if (isPropertyAccess(node)) {
index = addString(node.property.name);
return memberExpression(node.object,
memberExpression(stringMapIdentifier, literal(index), true), true);
}
}
});
stringMapIdentifier.name = createVariableName(Object.keys(usedVariables));
var insertMap = exposesVariables ? prependMap : wrapWithIife;
ast.body = insertMap(ast.body, stringMapIdentifier, arrayExpression(strings.map(literal)));
return ast;
};
function wrapWithIife(body, stringMapName, stringMap) {
var wrapperFunctionBody = blockStatement(body);
var wrapperFunction = functionExpression(null, [stringMapName], wrapperFunctionBody);
var iife = expressionStatement(
callExpression(
memberExpression(wrapperFunction, identifier('call'), false),
[identifier('this'), stringMap]));
return [iife];
}
function prependMap(body, stringMapName, stringMap) {
var insertIndex = isStrictStatement(body[0]) ? 1 : 0;
body.splice(insertIndex, 0,
variableDeclaration('var', [
variableDeclarator(stringMapName, stringMap)
])
);
return body;
}
exports.createVariableName = function(variableNames) {
var name = '_x';
do {
name += (Math.random() * 0xffff) | 0;
} while (variableNames.indexOf(name) !== -1);
return name;
};