Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
FogNetwork
GitHub Repository: FogNetwork/Tsunami
Path: blob/main/lib/js.js
1036 views
1
// -------------------------------------------------------------
2
// WARNING: this file is used by both the client and the server.
3
// Do not use any browser or node-specific API!
4
// -------------------------------------------------------------
5
const { parse } = require('acorn-hammerhead');
6
const { generate } = require('./esotope');
7
8
class JSRewriter {
9
constructor(ctx) {
10
this.parseOptions = {
11
allowReturnOutsideFunction: true,
12
allowImportExportEverywhere: true,
13
ecmaVersion: 2021,
14
};
15
this.generationOptions = {
16
format: {
17
quotes: 'double',
18
escapeless: true,
19
compact: true,
20
},
21
};
22
this.rewrite = ['location', 'parent', 'top'];
23
this.map = [
24
{
25
type: 'MemberExpression',
26
handler: (node, parent) => {
27
let rewrite = false;
28
if (parent.type == 'UnaryExpression' && parent.operator == 'delete') return;
29
if (parent.type == 'NewExpression' && parent.callee == node) return;
30
if (parent.type === 'CallExpression' && parent.callee === node) return;
31
if (node.preventRewrite) return;
32
switch(node.property.type) {
33
case 'Identifier':
34
//if (node.computed) rewrite = true;
35
if (!node.computed && this.rewrite.includes(node.property.name)) {
36
node.property = this.createLiteral(node.property.name);
37
rewrite = true;
38
};
39
break;
40
case 'Literal':
41
if (this.rewrite.includes(node.property.name)) rewrite = true;
42
break;
43
case 'TemplateLiteral':
44
rewrite = true;
45
break;
46
default:
47
if (node.computed) rewrite = true;
48
};
49
if (rewrite) {
50
let identifier = '$corrosionGet$m';
51
let nodeToRewrite = node;
52
const args = [
53
node.object,
54
node.property,
55
];
56
if (node.computed) args[1].preventRewrite = true;
57
if (parent.type == 'AssignmentExpression' && parent.left == node) {
58
identifier = '$corrosionSet$m';
59
nodeToRewrite = parent;
60
args.push(parent.right, this.createLiteral(parent.operator));
61
};
62
if (parent.type == 'CallExpression' && parent.callee == node) {
63
identifier = '$corrosionCall$m';
64
nodeToRewrite = parent;
65
args.push(this.createArrayExpression(...parent.arguments))
66
};
67
if (parent.type == 'UpdateExpression') {
68
identifier = '$corrosionSet$m';
69
nodeToRewrite = parent;
70
args.push(this.createLiteral(null), this.createLiteral(parent.operator));
71
};
72
Object.assign(nodeToRewrite, this.createCallExpression({ type: 'Identifier', name: identifier, }, args));
73
};
74
},
75
},
76
{
77
type: 'Identifier',
78
handler: (node, parent) => {
79
if (parent.type == 'MemberExpression' && parent.property == node) return; // window.location;
80
if (parent.type == 'LabeledStatement') return; // { location: null, };
81
if (parent.type == 'VariableDeclarator' && parent.id == node) return;
82
if (parent.type == 'Property' && parent.key == node) return;
83
if (parent.type == 'MethodDefinition') return;
84
if (parent.type == 'ClassDeclaration') return;
85
if (parent.type == 'RestElement') return;
86
if (parent.type == 'ExportSpecifier') return;
87
if (parent.type == 'ImportSpecifier') return;
88
if ((parent.type == 'FunctionDeclaration' || parent.type == 'FunctionExpression' || parent.type == 'ArrowFunctionExpression') && parent.params.includes(node)) return;
89
if ((parent.type == 'FunctionDeclaration' || parent.type == 'FunctionExpression') && parent.id == node) return;
90
if (parent.type == 'AssignmentPattern' && parent.left == node) return;
91
if (!this.rewrite.includes(node.name)) return;
92
if (node.preventRewrite) return;
93
let identifier = '$corrosionGet$';
94
let nodeToRewrite = node;
95
const args = [
96
this.createIdentifier(node.name, true),
97
];
98
99
if (parent.type == 'AssignmentExpression' && parent.left == node) {
100
identifier = '$corrosionSet$';
101
nodeToRewrite = parent;
102
args.push(parent.right);
103
args.push(this.createLiteral(parent.operator));
104
};
105
106
Object.assign(nodeToRewrite, this.createCallExpression({ type: 'Identifier', name: identifier }, args));
107
},
108
},
109
{
110
type: 'ImportDeclaration',
111
handler: (node, parent, url) => {
112
if (node.source.type != 'Literal' || !url) return;
113
node.source = this.createLiteral(ctx.url.wrap(node.source.value, { base: url, }));
114
},
115
},
116
{
117
type: 'ImportExpression',
118
handler: (node, parent) => {
119
node.source = this.createCallExpression(this.createMemberExpression(this.createMemberExpression(this.createIdentifier('$corrosion'), this.createIdentifier('url')), this.createIdentifier('wrap')), [
120
node.source,
121
this.createMemberExpression(this.createIdentifier('$corrosion'), this.createIdentifier('meta')),
122
]);
123
},
124
},
125
];
126
this.ctx = ctx;
127
};
128
process(source, url) {
129
try {
130
const ast = parse(source, this.parseOptions);
131
this.iterate(ast, (node, parent) => {
132
const fn = this.map.find(entry => entry.type == (node || {}).type);
133
if (fn) fn.handler(node, parent, url);
134
});
135
return (url ? this.createHead(url) : '') + generate(ast, this.generationOptions);
136
} catch(e) {
137
return source;
138
};
139
};
140
createHead(url) {
141
return `
142
if (!self.$corrosion && self.importScripts) {
143
importScripts(location.origin + '${this.ctx.prefix}index.js');
144
self.$corrosion = new Corrosion({ url: '${url}', codec: '${this.ctx.config.codec || 'plain'}', serviceWorker: true, window: self, prefix: '${this.ctx.prefix || '/service/'}', ws: ${this.ctx.config.ws || true}, cookies: ${this.ctx.config.cookies || false}, title: '${this.ctx.config.title}', }); $corrosion.init();
145
};\n`;
146
};
147
iterate(ast, handler) {
148
if (typeof ast != 'object' || !handler) return;
149
walk(ast, null, handler);
150
function walk(node, parent, handler) {
151
if (typeof node != 'object' || !handler) return;
152
handler(node, parent, handler);
153
for (const child in node) {
154
if (Array.isArray(node[child])) {
155
node[child].forEach(entry => walk(entry, node, handler));
156
} else {
157
walk(node[child], node, handler);
158
};
159
};
160
};
161
};
162
createCallExpression(callee, args) {
163
return { type: 'CallExpression', callee, arguments: args, optional: false, };
164
};
165
createArrayExpression(...elements) {
166
return {
167
type: 'ArrayExpression',
168
elements,
169
};
170
};
171
createMemberExpression(object, property) {
172
return {
173
type: 'MemberExpression',
174
object,
175
property,
176
};
177
};
178
createLiteral(value) {
179
return {
180
type: 'Literal',
181
value,
182
}
183
};
184
createIdentifier(name, preventRewrite) {
185
return { type: 'Identifier', name, preventRewrite: preventRewrite || false, };
186
};
187
};
188
189
module.exports = JSRewriter;
190
191