hackmud-script-manager 0.19.0-43367ba → 0.19.0-c12fd7c
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/bin/hsm.d.ts +0 -1
- package/bin/hsm.js +687 -1
- package/constants.d.ts +2 -0
- package/constants.js +4 -0
- package/generateTypeDeclaration.js +94 -1
- package/index.d.ts +2 -2
- package/index.js +47 -1
- package/package.json +36 -78
- package/processScript/index.d.ts +2 -2
- package/processScript/index.js +310 -1
- package/processScript/minify.d.ts +3 -3
- package/processScript/minify.js +376 -1
- package/processScript/postprocess.js +5 -1
- package/processScript/preprocess.d.ts +1 -1
- package/processScript/preprocess.js +84 -1
- package/processScript/shared.d.ts +3 -3
- package/processScript/shared.js +18 -1
- package/processScript/transform.d.ts +3 -3
- package/processScript/transform.js +394 -1
- package/pull.js +17 -1
- package/push.d.ts +3 -3
- package/push.js +251 -1
- package/syncMacros.js +53 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/watch.d.ts +3 -3
- package/watch.js +228 -1
- package/assert-22a7ef8a.js +0 -1
- package/constants-9bb78688.js +0 -1
- package/countHackmudCharacters-a08a265f.js +0 -1
- package/spliceString-0e6b5d6d.js +0 -1
- package/writeFilePersistent-ee9c9bfd.js +0 -1
@@ -1,6 +1,6 @@
|
|
1
|
-
import { File } from "@babel/types";
|
2
|
-
import { LaxPartial } from "@samual/lib";
|
3
|
-
|
1
|
+
import type { File } from "@babel/types";
|
2
|
+
import type { LaxPartial } from "@samual/lib";
|
3
|
+
type MinifyOptions = {
|
4
4
|
/** 11 a-z 0-9 characters */
|
5
5
|
uniqueID: string;
|
6
6
|
/** whether to mangle function and class names (defaults to `false`) */
|
package/processScript/minify.js
CHANGED
@@ -1 +1,376 @@
|
|
1
|
-
import
|
1
|
+
import babelGenerator from '@babel/generator';
|
2
|
+
import babelTraverse from '@babel/traverse';
|
3
|
+
import t from '@babel/types';
|
4
|
+
import { assert } from '@samual/lib/assert';
|
5
|
+
import { countHackmudCharacters } from '@samual/lib/countHackmudCharacters';
|
6
|
+
import { spliceString } from '@samual/lib/spliceString';
|
7
|
+
import { tokenizer, tokTypes } from 'acorn';
|
8
|
+
import * as terser from 'terser';
|
9
|
+
import { getReferencePathsToGlobal, includesIllegalString, replaceUnsafeStrings } from './shared.js';
|
10
|
+
|
11
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
12
|
+
const {
|
13
|
+
default: generate
|
14
|
+
} = babelGenerator;
|
15
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
16
|
+
const {
|
17
|
+
default: traverse
|
18
|
+
} = babelTraverse;
|
19
|
+
// TODO move autocomplete code outside this function
|
20
|
+
// TODO replace references to `arguments`
|
21
|
+
/**
|
22
|
+
* @param file babel ast node representing a file containing transformed code
|
23
|
+
* @param options {@link MinifyOptions details}
|
24
|
+
*/
|
25
|
+
const minify = async (file, {
|
26
|
+
uniqueID = `00000000000`,
|
27
|
+
mangleNames = false,
|
28
|
+
forceQuineCheats,
|
29
|
+
autocomplete
|
30
|
+
} = {}) => {
|
31
|
+
assert(/^\w{11}$/.exec(uniqueID));
|
32
|
+
let program;
|
33
|
+
traverse(file, {
|
34
|
+
Program(path) {
|
35
|
+
program = path;
|
36
|
+
path.skip();
|
37
|
+
}
|
38
|
+
});
|
39
|
+
if (program.scope.hasGlobal(`_START`)) {
|
40
|
+
for (const referencePath of getReferencePathsToGlobal(`_START`, program)) referencePath.replaceWith(t.identifier(`_ST`));
|
41
|
+
}
|
42
|
+
if (program.scope.hasGlobal(`_TIMEOUT`)) {
|
43
|
+
for (const referencePath of getReferencePathsToGlobal(`_TIMEOUT`, program)) referencePath.replaceWith(t.identifier(`_TO`));
|
44
|
+
}
|
45
|
+
|
46
|
+
// typescript does not like NodePath#get() and becomes slow so I have to dance around it
|
47
|
+
const mainFunctionPath = program.get(`body.0`);
|
48
|
+
for (const parameter of [...mainFunctionPath.node.params].reverse()) {
|
49
|
+
if (parameter.type == `Identifier`) {
|
50
|
+
const binding = mainFunctionPath.scope.getBinding(parameter.name);
|
51
|
+
if (!binding.referenced) {
|
52
|
+
mainFunctionPath.node.params.pop();
|
53
|
+
continue;
|
54
|
+
}
|
55
|
+
}
|
56
|
+
break;
|
57
|
+
}
|
58
|
+
for (const global in program.scope.globals) {
|
59
|
+
if (global == `arguments` || global.startsWith(`$${uniqueID}$`)) continue;
|
60
|
+
const referencePaths = getReferencePathsToGlobal(global, program);
|
61
|
+
if (5 + global.length + referencePaths.length >= global.length * referencePaths.length) continue;
|
62
|
+
for (const path of referencePaths) path.replaceWith(t.identifier(`_${uniqueID}_GLOBAL_${global}_`));
|
63
|
+
mainFunctionPath.node.body.body.unshift(t.variableDeclaration(`let`, [t.variableDeclarator(t.identifier(`_${uniqueID}_GLOBAL_${global}_`), t.identifier(global))]));
|
64
|
+
}
|
65
|
+
const hashGReferencePaths = getReferencePathsToGlobal(`$${uniqueID}$GLOBAL$`, program);
|
66
|
+
if (hashGReferencePaths.length > 3) {
|
67
|
+
for (const path of hashGReferencePaths) path.replaceWith(t.identifier(`_${uniqueID}_G_`));
|
68
|
+
mainFunctionPath.node.body.body.unshift(t.variableDeclaration(`let`, [t.variableDeclarator(t.identifier(`_${uniqueID}_G_`), t.identifier(`$${uniqueID}$GLOBAL$`))]));
|
69
|
+
}
|
70
|
+
const jsonValues = [];
|
71
|
+
// this needs `as boolean` because typescript is dumb
|
72
|
+
let undefinedIsReferenced = false;
|
73
|
+
let scriptBeforeJSONValueReplacement;
|
74
|
+
if (forceQuineCheats != true) {
|
75
|
+
const fileBeforeJSONValueReplacement = t.cloneNode(file);
|
76
|
+
traverse(fileBeforeJSONValueReplacement, {
|
77
|
+
MemberExpression({
|
78
|
+
node: memberExpression
|
79
|
+
}) {
|
80
|
+
if (memberExpression.computed) return;
|
81
|
+
assert(memberExpression.property.type == `Identifier`);
|
82
|
+
if (memberExpression.property.name == `prototype`) {
|
83
|
+
memberExpression.computed = true;
|
84
|
+
memberExpression.property = t.identifier(`_${uniqueID}_PROTOTYPE_PROPERTY_`);
|
85
|
+
} else if (memberExpression.property.name == `__proto__`) {
|
86
|
+
memberExpression.computed = true;
|
87
|
+
memberExpression.property = t.identifier(`_${uniqueID}_PROTO_PROPERTY_`);
|
88
|
+
} else if (includesIllegalString(memberExpression.property.name)) {
|
89
|
+
memberExpression.computed = true;
|
90
|
+
memberExpression.property = t.stringLiteral(replaceUnsafeStrings(uniqueID, memberExpression.property.name));
|
91
|
+
}
|
92
|
+
},
|
93
|
+
ObjectProperty({
|
94
|
+
node: objectProperty
|
95
|
+
}) {
|
96
|
+
if (objectProperty.key.type == `Identifier` && includesIllegalString(objectProperty.key.name)) {
|
97
|
+
objectProperty.key = t.stringLiteral(replaceUnsafeStrings(uniqueID, objectProperty.key.name));
|
98
|
+
objectProperty.shorthand = false;
|
99
|
+
}
|
100
|
+
},
|
101
|
+
StringLiteral({
|
102
|
+
node
|
103
|
+
}) {
|
104
|
+
node.value = replaceUnsafeStrings(uniqueID, node.value);
|
105
|
+
},
|
106
|
+
TemplateLiteral({
|
107
|
+
node
|
108
|
+
}) {
|
109
|
+
for (const templateElement of node.quasis) {
|
110
|
+
if (templateElement.value.cooked) {
|
111
|
+
templateElement.value.cooked = replaceUnsafeStrings(uniqueID, templateElement.value.cooked);
|
112
|
+
templateElement.value.raw = templateElement.value.cooked.replaceAll(`\\`, `\\\\`).replaceAll(`\``, `\\\``).replaceAll(`\${`, `$\\{`);
|
113
|
+
} else templateElement.value.raw = replaceUnsafeStrings(uniqueID, templateElement.value.raw);
|
114
|
+
}
|
115
|
+
},
|
116
|
+
RegExpLiteral(path) {
|
117
|
+
path.node.pattern = replaceUnsafeStrings(uniqueID, path.node.pattern);
|
118
|
+
delete path.node.extra;
|
119
|
+
}
|
120
|
+
});
|
121
|
+
scriptBeforeJSONValueReplacement = (await terser.minify(generate(fileBeforeJSONValueReplacement).code, {
|
122
|
+
ecma: 2015,
|
123
|
+
compress: {
|
124
|
+
passes: Infinity,
|
125
|
+
unsafe: true,
|
126
|
+
unsafe_arrows: true,
|
127
|
+
unsafe_comps: true,
|
128
|
+
unsafe_symbols: true,
|
129
|
+
unsafe_methods: true,
|
130
|
+
unsafe_proto: true,
|
131
|
+
unsafe_regexp: true,
|
132
|
+
unsafe_undefined: true,
|
133
|
+
sequences: false
|
134
|
+
},
|
135
|
+
format: {
|
136
|
+
semicolons: false
|
137
|
+
},
|
138
|
+
keep_classnames: !mangleNames,
|
139
|
+
keep_fnames: !mangleNames
|
140
|
+
})).code.replace(new RegExp(`_${uniqueID}_PROTOTYPE_PROPERTY_`, `g`), `"prototype"`).replace(new RegExp(`_${uniqueID}_PROTO_PROPERTY_`, `g`), `"__proto__"`);
|
141
|
+
if (autocomplete) scriptBeforeJSONValueReplacement = spliceString(scriptBeforeJSONValueReplacement, `//${autocomplete}\n`, getFunctionBodyStart(scriptBeforeJSONValueReplacement) + 1);
|
142
|
+
if (forceQuineCheats == false) return scriptBeforeJSONValueReplacement;
|
143
|
+
}
|
144
|
+
let comment;
|
145
|
+
let hasComment = false;
|
146
|
+
let code;
|
147
|
+
{
|
148
|
+
const promises = [];
|
149
|
+
traverse(file, {
|
150
|
+
FunctionDeclaration(path) {
|
151
|
+
path.traverse({
|
152
|
+
Function(path) {
|
153
|
+
if (path.parent.type != `CallExpression` && path.parentKey != `callee`) path.skip();
|
154
|
+
},
|
155
|
+
Loop(path) {
|
156
|
+
path.skip();
|
157
|
+
},
|
158
|
+
ObjectExpression(path) {
|
159
|
+
const o = {};
|
160
|
+
if (parseObjectExpression(path.node, o)) path.replaceWith(t.identifier(`_${uniqueID}_JSON_VALUE_${jsonValues.push(o) - 1}_`));
|
161
|
+
},
|
162
|
+
ArrayExpression(path) {
|
163
|
+
const o = [];
|
164
|
+
if (parseArrayExpression(path.node, o)) path.replaceWith(t.identifier(`_${uniqueID}_JSON_VALUE_${jsonValues.push(o) - 1}_`));
|
165
|
+
}
|
166
|
+
});
|
167
|
+
path.traverse({
|
168
|
+
TemplateLiteral(path) {
|
169
|
+
if (path.parent.type == `TaggedTemplateExpression`) return;
|
170
|
+
const templateLiteral = path.node;
|
171
|
+
let replacement = t.stringLiteral(templateLiteral.quasis[0].value.cooked);
|
172
|
+
for (let index = 0; index < templateLiteral.expressions.length; index++) {
|
173
|
+
const expression = templateLiteral.expressions[index];
|
174
|
+
const templateElement = templateLiteral.quasis[index + 1];
|
175
|
+
replacement = t.binaryExpression(`+`, replacement, expression);
|
176
|
+
if (!templateElement.value.cooked) continue;
|
177
|
+
replacement = t.binaryExpression(`+`, replacement, t.stringLiteral(templateElement.value.cooked));
|
178
|
+
}
|
179
|
+
path.replaceWith(replacement);
|
180
|
+
},
|
181
|
+
MemberExpression({
|
182
|
+
node: memberExpression
|
183
|
+
}) {
|
184
|
+
if (memberExpression.computed) return;
|
185
|
+
assert(memberExpression.property.type == `Identifier`);
|
186
|
+
if (memberExpression.property.name.length < 3) return;
|
187
|
+
memberExpression.computed = true;
|
188
|
+
memberExpression.property = t.stringLiteral(memberExpression.property.name);
|
189
|
+
},
|
190
|
+
UnaryExpression(path) {
|
191
|
+
if (path.node.operator == `void`) {
|
192
|
+
if (path.node.argument.type == `NumericLiteral` && !path.node.argument.value) {
|
193
|
+
path.replaceWith(t.identifier(`_${uniqueID}_UNDEFINED_`));
|
194
|
+
undefinedIsReferenced = true;
|
195
|
+
}
|
196
|
+
} else if (path.node.operator == `-` && path.node.argument.type == `NumericLiteral`) {
|
197
|
+
const value = -path.node.argument.value;
|
198
|
+
promises.push((async () => {
|
199
|
+
if ((await minifyNumber(value)).length <= 3) return;
|
200
|
+
if (path.parentKey == `key` && path.parent.type == `ObjectProperty`) path.parent.computed = true;
|
201
|
+
let jsonValueIndex = jsonValues.indexOf(value);
|
202
|
+
if (jsonValueIndex == -1) jsonValueIndex += jsonValues.push(value);
|
203
|
+
path.replaceWith(t.identifier(`_${uniqueID}_JSON_VALUE_${jsonValueIndex}_`));
|
204
|
+
})());
|
205
|
+
path.skip();
|
206
|
+
}
|
207
|
+
},
|
208
|
+
NullLiteral(path) {
|
209
|
+
/* eslint-disable unicorn/no-null */
|
210
|
+
let jsonValueIndex = jsonValues.indexOf(null);
|
211
|
+
if (jsonValueIndex == -1) jsonValueIndex += jsonValues.push(null);
|
212
|
+
path.replaceWith(t.identifier(`_${uniqueID}_JSON_VALUE_${jsonValueIndex}_`));
|
213
|
+
/* eslint-enable unicorn/no-null */
|
214
|
+
},
|
215
|
+
|
216
|
+
NumericLiteral(path) {
|
217
|
+
promises.push((async () => {
|
218
|
+
if ((await minifyNumber(path.node.value)).length <= 3) return;
|
219
|
+
if (path.parentKey == `key` && path.parent.type == `ObjectProperty`) path.parent.computed = true;
|
220
|
+
let jsonValueIndex = jsonValues.indexOf(path.node.value);
|
221
|
+
if (jsonValueIndex == -1) jsonValueIndex += jsonValues.push(path.node.value);
|
222
|
+
path.replaceWith(t.identifier(`_${uniqueID}_JSON_VALUE_${jsonValueIndex}_`));
|
223
|
+
})());
|
224
|
+
},
|
225
|
+
StringLiteral(path) {
|
226
|
+
path.node.value = replaceUnsafeStrings(uniqueID, path.node.value);
|
227
|
+
|
228
|
+
// eslint-disable-next-line @typescript-eslint/no-base-to-string -- the `NodePath`'s `.toString()` method compiles and returns the contained `Node`
|
229
|
+
if (JSON.stringify(path.node.value).includes(`\\u00`) || path.toString().length < 4) return;
|
230
|
+
if (path.parentKey == `key` && path.parent.type == `ObjectProperty`) path.parent.computed = true;
|
231
|
+
let jsonValueIndex = jsonValues.indexOf(path.node.value);
|
232
|
+
if (jsonValueIndex == -1) jsonValueIndex += jsonValues.push(path.node.value);
|
233
|
+
path.replaceWith(t.identifier(`_${uniqueID}_JSON_VALUE_${jsonValueIndex}_`));
|
234
|
+
},
|
235
|
+
ObjectProperty({
|
236
|
+
node
|
237
|
+
}) {
|
238
|
+
if (node.computed || node.key.type != `Identifier` || node.key.name.length < 4) return;
|
239
|
+
let jsonValueIndex = jsonValues.indexOf(node.key.name);
|
240
|
+
if (jsonValueIndex == -1) jsonValueIndex += jsonValues.push(node.key.name);
|
241
|
+
node.computed = true;
|
242
|
+
node.key = t.identifier(`_${uniqueID}_JSON_VALUE_${jsonValueIndex}_`);
|
243
|
+
},
|
244
|
+
RegExpLiteral(path) {
|
245
|
+
path.node.pattern = replaceUnsafeStrings(uniqueID, path.node.pattern);
|
246
|
+
delete path.node.extra;
|
247
|
+
}
|
248
|
+
});
|
249
|
+
path.skip();
|
250
|
+
}
|
251
|
+
});
|
252
|
+
await Promise.all(promises);
|
253
|
+
const functionDeclaration = file.program.body[0];
|
254
|
+
assert(functionDeclaration.type == `FunctionDeclaration`);
|
255
|
+
if (jsonValues.length) {
|
256
|
+
hasComment = true;
|
257
|
+
if (jsonValues.length == 1) {
|
258
|
+
if (typeof jsonValues[0] == `string` && !jsonValues[0].includes(`\n`) && !jsonValues[0].includes(`\t`)) {
|
259
|
+
const variableDeclaration = t.variableDeclaration(`let`, [t.variableDeclarator(t.identifier(`_${uniqueID}_JSON_VALUE_0_`), t.memberExpression(t.taggedTemplateExpression(t.memberExpression(t.callExpression(t.identifier(`$${uniqueID}$SUBSCRIPT$scripts$quine$`), []), t.identifier(`split`)), t.templateLiteral([t.templateElement({
|
260
|
+
raw: `\t`,
|
261
|
+
cooked: `\t`
|
262
|
+
}, true)], [])), t.identifier(`$${uniqueID}$SPLIT_INDEX$`), true))]);
|
263
|
+
if (undefinedIsReferenced) variableDeclaration.declarations.push(t.variableDeclarator(t.identifier(`_${uniqueID}_UNDEFINED_`)));
|
264
|
+
functionDeclaration.body.body.unshift(variableDeclaration);
|
265
|
+
comment = jsonValues[0];
|
266
|
+
} else {
|
267
|
+
const variableDeclaration = t.variableDeclaration(`let`, [t.variableDeclarator(t.identifier(`_${uniqueID}_JSON_VALUE_0_`), t.callExpression(t.memberExpression(t.identifier(`JSON`), t.identifier(`parse`)), [t.memberExpression(t.taggedTemplateExpression(t.memberExpression(t.callExpression(t.identifier(`$${uniqueID}$SUBSCRIPT$scripts$quine$`), []), t.identifier(`split`)), t.templateLiteral([t.templateElement({
|
268
|
+
raw: `\t`,
|
269
|
+
cooked: `\t`
|
270
|
+
}, true)], [])), t.identifier(`$${uniqueID}$SPLIT_INDEX$`), true)]))]);
|
271
|
+
if (undefinedIsReferenced) variableDeclaration.declarations.push(t.variableDeclarator(t.identifier(`_${uniqueID}_UNDEFINED_`)));
|
272
|
+
functionDeclaration.body.body.unshift(variableDeclaration);
|
273
|
+
comment = JSON.stringify(jsonValues[0]);
|
274
|
+
}
|
275
|
+
} else {
|
276
|
+
const variableDeclaration = t.variableDeclaration(`let`, [t.variableDeclarator(t.arrayPattern(jsonValues.map((_, index) => t.identifier(`_${uniqueID}_JSON_VALUE_${index}_`))), t.callExpression(t.memberExpression(t.identifier(`JSON`), t.identifier(`parse`)), [t.memberExpression(t.taggedTemplateExpression(t.memberExpression(t.callExpression(t.identifier(`$${uniqueID}$SUBSCRIPT$scripts$quine$`), []), t.identifier(`split`)), t.templateLiteral([t.templateElement({
|
277
|
+
raw: `\t`,
|
278
|
+
cooked: `\t`
|
279
|
+
}, true)], [])), t.identifier(`$${uniqueID}$SPLIT_INDEX$`), true)]))]);
|
280
|
+
if (undefinedIsReferenced) variableDeclaration.declarations.push(t.variableDeclarator(t.identifier(`_${uniqueID}_UNDEFINED_`)));
|
281
|
+
functionDeclaration.body.body.unshift(variableDeclaration);
|
282
|
+
comment = JSON.stringify(jsonValues);
|
283
|
+
}
|
284
|
+
} else if (undefinedIsReferenced) {
|
285
|
+
functionDeclaration.body.body.unshift(t.variableDeclaration(`let`, [t.variableDeclarator(t.identifier(`_${uniqueID}_UNDEFINED_`))]));
|
286
|
+
}
|
287
|
+
code = generate(file).code;
|
288
|
+
}
|
289
|
+
code = (await terser.minify(code, {
|
290
|
+
ecma: 2015,
|
291
|
+
compress: {
|
292
|
+
passes: Infinity,
|
293
|
+
unsafe: true,
|
294
|
+
unsafe_arrows: true,
|
295
|
+
unsafe_comps: true,
|
296
|
+
unsafe_symbols: true,
|
297
|
+
unsafe_methods: true,
|
298
|
+
unsafe_proto: true,
|
299
|
+
unsafe_regexp: true,
|
300
|
+
unsafe_undefined: true,
|
301
|
+
sequences: false
|
302
|
+
},
|
303
|
+
format: {
|
304
|
+
semicolons: false
|
305
|
+
},
|
306
|
+
keep_classnames: !mangleNames,
|
307
|
+
keep_fnames: !mangleNames
|
308
|
+
})).code || ``;
|
309
|
+
|
310
|
+
// this step affects the character count and can't happen after the count comparison
|
311
|
+
if (comment != undefined) {
|
312
|
+
code = spliceString(code, `${autocomplete ? `//${autocomplete}\n` : ``}\n//\t${comment}\t\n`, getFunctionBodyStart(code) + 1);
|
313
|
+
code = code.replace(`$${uniqueID}$SPLIT_INDEX$`, await minifyNumber(code.split(`\t`).findIndex(part => part == comment)));
|
314
|
+
}
|
315
|
+
if (forceQuineCheats == true) return code;
|
316
|
+
assert(scriptBeforeJSONValueReplacement);
|
317
|
+
|
318
|
+
// if the script has a comment, it's gonna contain `SC$scripts$quine()`
|
319
|
+
// which is gonna compile to `#fs.scripts.quine()` which contains
|
320
|
+
// an extra character so we have to account for that
|
321
|
+
if (countHackmudCharacters(scriptBeforeJSONValueReplacement) <= countHackmudCharacters(code) + Number(hasComment)) return scriptBeforeJSONValueReplacement;
|
322
|
+
return code;
|
323
|
+
};
|
324
|
+
const parseObjectExpression = (node, o) => {
|
325
|
+
if (!node.properties.length) return false;
|
326
|
+
for (const property of node.properties) {
|
327
|
+
if (property.type != `ObjectProperty` || property.computed) return false;
|
328
|
+
assert(property.key.type == `Identifier` || property.key.type == `NumericLiteral` || property.key.type == `StringLiteral`);
|
329
|
+
if (property.value.type == `ArrayExpression`) {
|
330
|
+
const childArray = [];
|
331
|
+
if (parseArrayExpression(property.value, childArray)) o[property.key.type == `Identifier` ? property.key.name : property.key.value] = childArray;else return false;
|
332
|
+
} else if (property.value.type == `ObjectExpression`) {
|
333
|
+
const childObject = {};
|
334
|
+
if (parseObjectExpression(property.value, childObject)) o[property.key.type == `Identifier` ? property.key.name : property.key.value] = childObject;else return false;
|
335
|
+
} else if (property.value.type == `NullLiteral`)
|
336
|
+
// eslint-disable-next-line unicorn/no-null
|
337
|
+
o[property.key.type == `Identifier` ? property.key.name : property.key.value] = null;else if (property.value.type == `BooleanLiteral` || property.value.type == `NumericLiteral` || property.value.type == `StringLiteral`) o[property.key.type == `Identifier` ? property.key.name : property.key.value] = property.value.value;else if (property.value.type == `TemplateLiteral` && !property.value.expressions.length) o[property.key.type == `Identifier` ? property.key.name : property.key.value] = property.value.quasis[0].value.cooked;else return false;
|
338
|
+
}
|
339
|
+
return true;
|
340
|
+
};
|
341
|
+
const parseArrayExpression = (node, o) => {
|
342
|
+
if (!node.elements.length) return false;
|
343
|
+
for (const element of node.elements) {
|
344
|
+
if (!element) return false;
|
345
|
+
if (element.type == `ArrayExpression`) {
|
346
|
+
const childArray = [];
|
347
|
+
if (parseArrayExpression(element, childArray)) childArray.push(childArray);else return false;
|
348
|
+
} else if (element.type == `ObjectExpression`) {
|
349
|
+
const childObject = {};
|
350
|
+
if (parseObjectExpression(element, childObject)) o.push(childObject);else return false;
|
351
|
+
} else if (element.type == `NullLiteral`)
|
352
|
+
// eslint-disable-next-line unicorn/no-null
|
353
|
+
o.push(null);else if (element.type == `BooleanLiteral` || element.type == `NumericLiteral` || element.type == `StringLiteral`) o.push(element.value);else if (element.type == `TemplateLiteral` && !element.expressions.length) o.push(element.quasis[0].value.cooked);else return false;
|
354
|
+
}
|
355
|
+
return true;
|
356
|
+
};
|
357
|
+
const minifyNumber = async number => /\$\((?<number>.+)\)/.exec((await terser.minify(`$(${number})`, {
|
358
|
+
ecma: 2015
|
359
|
+
})).code).groups.number;
|
360
|
+
const getFunctionBodyStart = code => {
|
361
|
+
const tokens = tokenizer(code, {
|
362
|
+
ecmaVersion: 2015
|
363
|
+
});
|
364
|
+
tokens.getToken(); // function
|
365
|
+
tokens.getToken(); // name
|
366
|
+
tokens.getToken(); // (
|
367
|
+
|
368
|
+
let nests = 1;
|
369
|
+
while (nests) {
|
370
|
+
const token = tokens.getToken();
|
371
|
+
if (token.type == tokTypes.parenL) nests++;else if (token.type == tokTypes.parenR) nests--;
|
372
|
+
}
|
373
|
+
return tokens.getToken().start; // {
|
374
|
+
};
|
375
|
+
|
376
|
+
export { minify as default, minify };
|
@@ -1 +1,5 @@
|
|
1
|
-
const postprocess=
|
1
|
+
const postprocess = (code, seclevel, uniqueID) => {
|
2
|
+
return code.replace(/^function\s*\w+\(/, `function(`).replace(new RegExp(`\\$${uniqueID}\\$\\\\(?:\\\\)?\\$SC_DOLLAR\\$`, `g`), `S\\C$`).replace(new RegExp(`\\$${uniqueID}\\$\\\\(?:\\\\)?\\$DB_DOLLAR\\$`, `g`), `D\\B$`).replace(new RegExp(`\\$${uniqueID}\\$\\\\(?:\\\\)?\\$D\\$`, `g`), `_\\_D_S`).replace(new RegExp(`\\$${uniqueID}\\$\\\\(?:\\\\)?\\$FMCL\\$`, `g`), `_\\_FMCL_`).replace(new RegExp(`\\$${uniqueID}\\$\\\\(?:\\\\)?\\$G\\$`, `g`), `_\\_G_`).replace(new RegExp(`\\$${uniqueID}\\$SUBSCRIPT\\$(\\w+)\\$(\\w+)\\$`, `g`), `#${`nlmhf`[seclevel]}s.$1.$2`).replace(new RegExp(`\\$${uniqueID}\\$DEBUG\\$`, `g`), `#D`).replace(new RegExp(`\\$${uniqueID}\\$FMCL\\$`, `g`), `#FMCL`).replace(new RegExp(`\\$${uniqueID}\\$GLOBAL\\$`, `g`), `#G`).replace(new RegExp(`\\$${uniqueID}\\$DB\\$(\\w+)\\$`, `g`), `#db.$1`).replace(new RegExp(`\\$${uniqueID}\\$SLASH_SLASH\\$`, `g`), `/\\/`).replace(new RegExp(`\\$${uniqueID}\\$NOT_A_SUBSCRIPT\\$(#[\\w\\.]+)\\(\\$`, `g`), `$1\\(`).replace(new RegExp(`\\$${uniqueID}\\$NOT_A_DB_CALL\\$(\\w+)\\$`, `g`), `#db.$1\\(`).replace(new RegExp(`\\$${uniqueID}\\$NOT_A_DEBUG_CALL\\$`, `g`), `#D\\(`).replace(new RegExp(`\\$${uniqueID}\\$NOT_FMCL\\$`, `g`), `#\\FMCL`).replace(new RegExp(`\\$${uniqueID}\\$NOT_G\\$`, `g`), `#\\G`);
|
3
|
+
};
|
4
|
+
|
5
|
+
export { postprocess as default, postprocess };
|
@@ -1 +1,84 @@
|
|
1
|
-
import
|
1
|
+
import babelGenerator from '@babel/generator';
|
2
|
+
import { parse } from '@babel/parser';
|
3
|
+
import babelTraverse from '@babel/traverse';
|
4
|
+
import t from '@babel/types';
|
5
|
+
import { assert } from '@samual/lib/assert';
|
6
|
+
import { spliceString } from '@samual/lib/spliceString';
|
7
|
+
import { resolve } from 'import-meta-resolve';
|
8
|
+
|
9
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
10
|
+
const {
|
11
|
+
default: traverse
|
12
|
+
} = babelTraverse;
|
13
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
14
|
+
const {
|
15
|
+
default: generate
|
16
|
+
} = babelGenerator;
|
17
|
+
/**
|
18
|
+
* @param code source code for preprocessing
|
19
|
+
* @param options {@link PreprocessOptions details}
|
20
|
+
*/
|
21
|
+
const preprocess = async (code, {
|
22
|
+
uniqueID = `00000000000`
|
23
|
+
} = {}) => {
|
24
|
+
assert(/^\w{11}$/.test(uniqueID));
|
25
|
+
const sourceCode = code;
|
26
|
+
let lengthBefore;
|
27
|
+
do {
|
28
|
+
lengthBefore = code.length;
|
29
|
+
code = code.replace(/^\s+/, ``).replace(/^\/\/.*/, ``).replace(/^\/\*[\s\S]*?\*\//, ``);
|
30
|
+
} while (code.length != lengthBefore);
|
31
|
+
code = code.replace(/^function\s*\(/, `export default function (`);
|
32
|
+
let file;
|
33
|
+
while (true) {
|
34
|
+
let error;
|
35
|
+
try {
|
36
|
+
file = parse(code, {
|
37
|
+
plugins: [`typescript`, [`decorators`, {
|
38
|
+
decoratorsBeforeExport: true
|
39
|
+
}], `doExpressions`, `functionBind`, `functionSent`, `partialApplication`, [`pipelineOperator`, {
|
40
|
+
proposal: `hack`,
|
41
|
+
topicToken: `%`
|
42
|
+
}], `throwExpressions`, [`recordAndTuple`, {
|
43
|
+
syntaxType: `hash`
|
44
|
+
}], `classProperties`, `classPrivateProperties`, `classPrivateMethods`, `logicalAssignment`, `numericSeparator`, `nullishCoalescingOperator`, `optionalChaining`, `optionalCatchBinding`, `objectRestSpread`],
|
45
|
+
sourceType: `module`
|
46
|
+
});
|
47
|
+
break;
|
48
|
+
} catch (error_) {
|
49
|
+
assert(error_ instanceof SyntaxError);
|
50
|
+
error = error_;
|
51
|
+
}
|
52
|
+
if (error.code != `BABEL_PARSER_SYNTAX_ERROR` || error.reasonCode != `PrivateInExpectedIn`) {
|
53
|
+
var _exec;
|
54
|
+
console.log((_exec = /.+/.exec(code.slice(error.pos))) === null || _exec === void 0 ? void 0 : _exec[0]);
|
55
|
+
throw error;
|
56
|
+
}
|
57
|
+
const codeSlice = code.slice(error.pos);
|
58
|
+
let match;
|
59
|
+
if (match = /^#[0-4fhmln]s\.scripts\.quine\(\)/.exec(codeSlice)) code = spliceString(code, JSON.stringify(sourceCode), error.pos, match[0].length);else if (match = /^#[0-4fhmln]?s\./.exec(codeSlice)) code = spliceString(code, `$`, error.pos, 1);else if (match = /^#D[^\w$]/.exec(codeSlice)) code = spliceString(code, `$`, error.pos, 1);else if (match = /^#FMCL/.exec(codeSlice)) code = spliceString(code, `$${uniqueID}$FMCL$`, error.pos, match[0].length);else if (match = /^#G/.exec(codeSlice)) code = spliceString(code, `$${uniqueID}$GLOBAL$`, error.pos, match[0].length);else if (match = /^#db\./.exec(codeSlice)) code = spliceString(code, `$`, error.pos, 1);else throw error;
|
60
|
+
}
|
61
|
+
let program;
|
62
|
+
traverse(file, {
|
63
|
+
Program(path) {
|
64
|
+
program = path;
|
65
|
+
path.skip();
|
66
|
+
}
|
67
|
+
});
|
68
|
+
const needRecord = program.scope.hasGlobal(`Record`);
|
69
|
+
const needTuple = program.scope.hasGlobal(`Tuple`);
|
70
|
+
if (needRecord || needTuple) {
|
71
|
+
file.program.body.unshift(t.importDeclaration(needRecord ? needTuple ? [t.importSpecifier(t.identifier(`Record`), t.identifier(`Record`)), t.importSpecifier(t.identifier(`Tuple`), t.identifier(`Tuple`))] : [t.importSpecifier(t.identifier(`Record`), t.identifier(`Record`))] : [t.importSpecifier(t.identifier(`Tuple`), t.identifier(`Tuple`))], t.stringLiteral(`@bloomberg/record-tuple-polyfill`)));
|
72
|
+
}
|
73
|
+
if (program.scope.hasGlobal(`Proxy`)) {
|
74
|
+
file.program.body.unshift(t.importDeclaration([t.importDefaultSpecifier(t.identifier(`Proxy`))], t.stringLiteral((await resolve(`proxy-polyfill/src/proxy.js`, import.meta.url)).slice(7))));
|
75
|
+
}
|
76
|
+
if (program.node.body.length == 1 && program.node.body[0].type == `FunctionDeclaration`) return {
|
77
|
+
code: `export default ${generate(file).code}`
|
78
|
+
};
|
79
|
+
return {
|
80
|
+
code: generate(file).code
|
81
|
+
};
|
82
|
+
};
|
83
|
+
|
84
|
+
export { preprocess as default, preprocess };
|
@@ -1,5 +1,5 @@
|
|
1
|
-
import { NodePath } from "@babel/traverse";
|
2
|
-
import
|
3
|
-
export declare const getReferencePathsToGlobal: (name: string, program: NodePath<Program>) => NodePath<
|
1
|
+
import type { NodePath } from "@babel/traverse";
|
2
|
+
import type { Identifier, Program } from "@babel/types";
|
3
|
+
export declare const getReferencePathsToGlobal: (name: string, program: NodePath<Program>) => NodePath<Identifier>[];
|
4
4
|
export declare const includesIllegalString: (toCheck: string) => boolean;
|
5
5
|
export declare const replaceUnsafeStrings: (uniqueID: string, toReplace: string) => string;
|
package/processScript/shared.js
CHANGED
@@ -1 +1,18 @@
|
|
1
|
-
import
|
1
|
+
import t from '@babel/types';
|
2
|
+
import { ensure } from '@samual/lib/assert';
|
3
|
+
|
4
|
+
const getReferencePathsToGlobal = (name, program) => {
|
5
|
+
const [variableDeclaration] = program.unshiftContainer(`body`, t.variableDeclaration(`let`, [t.variableDeclarator(t.identifier(name))]));
|
6
|
+
program.scope.crawl();
|
7
|
+
const binding = ensure(program.scope.getBinding(name));
|
8
|
+
variableDeclaration.remove();
|
9
|
+
return binding.referencePaths;
|
10
|
+
};
|
11
|
+
const includesIllegalString = toCheck => {
|
12
|
+
return toCheck.includes(`SC$`) || toCheck.includes(`DB$`) || toCheck.includes(`__D_S`) || toCheck.includes(`__FMCL_`) || toCheck.includes(`__G_`);
|
13
|
+
};
|
14
|
+
const replaceUnsafeStrings = (uniqueID, toReplace) => {
|
15
|
+
return toReplace.replaceAll(`SC$`, `$${uniqueID}$\\$SC_DOLLAR$`).replaceAll(`DB$`, `$${uniqueID}$\\$DB_DOLLAR$`).replaceAll(`__D_S`, `$${uniqueID}$\\$D$`).replaceAll(`__FMCL_`, `$${uniqueID}$\\$FMCL$`).replaceAll(`__G_`, `$${uniqueID}$\\$G$`).replaceAll(`//`, `$${uniqueID}$SLASH_SLASH$`).replaceAll(/#[0-4fhmln]?s(?:\.[_a-z][\d_a-z]{0,24}){2}\(/g, `$${uniqueID}$NOT_A_SUBSCRIPT$$$&$`).replaceAll(/#db\.(?<methodName>[irfu]|u1|us|ObjectId)\(/g, `$${uniqueID}$NOT_A_DB_CALL$$$1$`).replaceAll(`#D(`, `$${uniqueID}$NOT_A_DEBUG_CALL$`).replaceAll(`#FMCL`, `$${uniqueID}$NOT_FMCL$`).replaceAll(`#G`, `$${uniqueID}$NOT_G$`);
|
16
|
+
};
|
17
|
+
|
18
|
+
export { getReferencePathsToGlobal, includesIllegalString, replaceUnsafeStrings };
|
@@ -1,5 +1,5 @@
|
|
1
|
-
import
|
2
|
-
export
|
1
|
+
import type { File } from "@babel/types";
|
2
|
+
export type TransformOptions = {
|
3
3
|
/** 11 a-z 0-9 characters */
|
4
4
|
uniqueID: string;
|
5
5
|
/** the user going to be hosting this script (or set to `true` if not yet known) */
|
@@ -18,7 +18,7 @@ export declare type TransformOptions = {
|
|
18
18
|
* @param options {@link TransformOptions details}
|
19
19
|
*/
|
20
20
|
export declare const transform: (file: File, sourceCode: string, { uniqueID, scriptUser, scriptName, seclevel }?: Partial<TransformOptions>) => {
|
21
|
-
file:
|
21
|
+
file: File;
|
22
22
|
seclevel: number;
|
23
23
|
};
|
24
24
|
export default transform;
|