/
PropertyCollector.java
129 lines (107 loc) · 4.37 KB
/
PropertyCollector.java
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
package shadow.build.closure;
import com.google.javascript.jscomp.*;
import com.google.javascript.jscomp.Compiler;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.TokenStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
public class PropertyCollector implements NodeTraversal.Callback, CompilerPass {
private final AbstractCompiler compiler;
public final Map<String, Set<String>> properties = new HashMap<>();
public PropertyCollector(AbstractCompiler compiler) {
this.compiler = compiler;
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node node, Node parent) {
// don't traverse into .json files
return !node.isScript() || !t.getSourceName().endsWith(".json");
}
public final static Set<String> ignoredProps = new HashSet<>();
static {
ignoredProps.add("exports");
ignoredProps.add("module");
ignoredProps.add("prototype");
}
public static boolean isJSIdentifier(String name) {
return TokenStream.isJSIdentifier(name);
}
private void addProperty(NodeTraversal t, String property) {
if (!property.equals("exports") && !property.equals("module") && isJSIdentifier(property)) {
String sourceName = t.getSourceName();
Set<String> x = properties.get(sourceName);
if (x == null) {
x = new HashSet<>();
properties.put(sourceName, x);
}
x.add(property);
}
}
@Override
public void visit(NodeTraversal t, Node node, Node parent) {
// exports.foo = 1;
// function Foo() {}
// Foo.prototype.bar = 1;
// exports.Foo = Foo;
// find every property assign
// extract property name
// use as :externs in :advanced mode build using JS deps
// profit?
// FIXME: bad idea? probably way too many names we don't actually need to preserve
if (node.isAssign()) {
Node getProp = node.getFirstChild();
// only collect assignments to props
// thing.x = 1;
// not
// var x = 1;
// var x; x = 1;
if (getProp.isGetProp()) {
String property = getProp.getString();
addProperty(t, property);
}
} else if (node.isObjectLit()) {
for (int i = 0; i < node.getChildCount(); i++) {
Node keyNode = node.getChildAtIndex(i);
if (keyNode.isStringKey() || keyNode.isGetterDef() || keyNode.isSetterDef()) {
// only collect {foo:"foo"}, not {"foo":"foo"}
// this is far too general already and should probably
// only collect module.exports = {} ...
if (!keyNode.isQuotedStringKey()) {
addProperty(t, keyNode.getString());
}
}
}
} else if (NodeUtil.isCallTo(node,"Object.defineProperty")) {
Node property = node.getChildAtIndex(2);
if (property.isString()) {
addProperty(t, property.getString());
}
}
}
public void process(Node externs, Node root) {
NodeTraversal.traverse(compiler, root, this);
}
public static Node process(Compiler cc, SourceFile srcFile) {
JsAst ast = new JsAst(srcFile);
Node node = ast.getAstRoot(cc);
JsAst.ParseResult result = (JsAst.ParseResult) node.getProp(Node.PARSE_RESULTS);
PropertyCollector pass = new PropertyCollector(cc);
NodeTraversal.traverse(cc, node, pass);
System.out.println(pass.properties);
return node;
}
public static void main(String... args) {
Compiler cc = new Compiler();
CompilerOptions co = new CompilerOptions();
co.setLanguageIn(CompilerOptions.LanguageMode.ECMASCRIPT_2017);
co.setPrettyPrint(true);
cc.initOptions(co);
System.out.println(isJSIdentifier("İ"));
// SourceFile srcFile = SourceFile.fromFile("node_modules/@firebase/util/dist/cjs/src/crypt.js");
SourceFile srcFile = SourceFile.fromFile("tmp/alex.js");
// SourceFile srcFile = SourceFile.fromCode("test.json", "exports.foo = 1;");
cc.toSource(process(cc, srcFile));
}
}