hackmud-script-manager 0.13.0-a60a7a2 → 0.13.0-c461329

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. package/.gitattributes +1 -0
  2. package/.github/workflows/codeql-analysis.yml +39 -0
  3. package/.github/workflows/publish.yml +42 -0
  4. package/.vscode/settings.json +6 -0
  5. package/babel.config.json +6 -0
  6. package/package.json +12 -21
  7. package/rollup.config.js +110 -0
  8. package/scripts/build-package-json.js +36 -0
  9. package/scripts/jsconfig.json +5 -0
  10. package/scripts/version-dev.js +25 -0
  11. package/src/bin/hsm.ts +505 -0
  12. package/src/constants.json +3 -0
  13. package/src/generateTypings.ts +116 -0
  14. package/src/index.ts +19 -0
  15. package/src/modules.d.ts +5 -0
  16. package/src/processScript/index.ts +198 -0
  17. package/src/processScript/minify.ts +529 -0
  18. package/src/processScript/postprocess.ts +38 -0
  19. package/src/processScript/preprocess.ts +146 -0
  20. package/src/processScript/transform.ts +760 -0
  21. package/src/pull.ts +16 -0
  22. package/src/push.ts +314 -0
  23. package/src/syncMacros.ts +52 -0
  24. package/src/test.ts +59 -0
  25. package/src/tsconfig.json +20 -0
  26. package/src/watch.ts +156 -0
  27. package/tsconfig.json +12 -0
  28. package/assert-1b7dada8.js +0 -1
  29. package/bin/hsm.d.ts +0 -2
  30. package/bin/hsm.js +0 -2
  31. package/generateTypings.d.ts +0 -2
  32. package/generateTypings.js +0 -1
  33. package/index.d.ts +0 -15
  34. package/index.js +0 -1
  35. package/processScript/compile.d.ts +0 -17
  36. package/processScript/compile.js +0 -1
  37. package/processScript/index.d.ts +0 -30
  38. package/processScript/index.js +0 -1
  39. package/processScript/minify.d.ts +0 -7
  40. package/processScript/minify.js +0 -1
  41. package/processScript/postProcess.d.ts +0 -2
  42. package/processScript/postProcess.js +0 -1
  43. package/processScript/preProcess.d.ts +0 -15
  44. package/processScript/preProcess.js +0 -1
  45. package/pull.d.ts +0 -9
  46. package/pull.js +0 -1
  47. package/push.d.ts +0 -26
  48. package/push.js +0 -1
  49. package/spliceString-2c6f214f.js +0 -1
  50. package/syncMacros.d.ts +0 -5
  51. package/syncMacros.js +0 -1
  52. package/test.d.ts +0 -6
  53. package/test.js +0 -1
  54. package/watch.d.ts +0 -14
  55. package/watch.js +0 -1
@@ -0,0 +1,760 @@
1
+ import babelTraverse, { NodePath } from "@babel/traverse"
2
+ import t, { BlockStatement, CallExpression, File, FunctionDeclaration, Identifier, Program } from "@babel/types"
3
+ import { clearObject } from "@samual/lib"
4
+ import { assert, ensure } from "@samual/lib/assert"
5
+
6
+ const { default: traverse } = babelTraverse as any as typeof import("@babel/traverse")
7
+
8
+ export type TransformOptions = {
9
+ /** 11 a-z 0-9 characters */
10
+ uniqueID: string
11
+
12
+ /** the user the script will be uploaded to (or set to `true` if it is not yet known) */
13
+ scriptUser: string | true
14
+
15
+ /** the name of this script (or set to `true` if it is not yet known) */
16
+ scriptName: string | true
17
+
18
+ seclevel: number
19
+ }
20
+
21
+ /**
22
+ * transform a given babel `File` to be hackmud compatible
23
+ *
24
+ * (returned File will need `postprocess()`ing)
25
+ *
26
+ * @param options {@link TransformOptions details}
27
+ */
28
+ export async function transform(file: File, sourceCode: string, {
29
+ uniqueID = "00000000000",
30
+ scriptUser = "UNKNOWN",
31
+ scriptName = "UNKNOWN",
32
+ seclevel = -1
33
+ }: Partial<TransformOptions> = {}) {
34
+ const topFunctionName = `_SCRIPT_${uniqueID}_`
35
+ const exports = new Map<string, string>()
36
+ const liveExports = new Map<string, string>()
37
+
38
+ let program!: NodePath<t.Program>
39
+
40
+ traverse(file, {
41
+ Program(path) {
42
+ program = path
43
+ path.skip()
44
+ }
45
+ })
46
+
47
+ if (program.scope.hasGlobal("_START")) {
48
+ for (const referencePath of getReferencePathsToGlobal("_START", program))
49
+ referencePath.replaceWith(t.identifier("_ST"))
50
+ }
51
+
52
+ if (program.scope.hasGlobal("_TIMEOUT")) {
53
+ for (const referencePath of getReferencePathsToGlobal("_START", program))
54
+ referencePath.replaceWith(t.identifier("_TO"))
55
+ }
56
+
57
+ if (program.scope.hasGlobal("_SOURCE")) {
58
+ for (const referencePath of getReferencePathsToGlobal("_SOURCE", program))
59
+ referencePath.replaceWith(t.stringLiteral(sourceCode))
60
+ }
61
+
62
+ if (program.scope.hasGlobal("_BUILD_DATE")) {
63
+ for (const referencePath of getReferencePathsToGlobal("_BUILD_DATE", program))
64
+ referencePath.replaceWith(t.numericLiteral(Date.now()))
65
+ }
66
+
67
+ if (program.scope.hasGlobal("_SCRIPT_USER")) {
68
+ for (const referencePath of getReferencePathsToGlobal("_SCRIPT_USER", program)) {
69
+ if (scriptUser == true)
70
+ referencePath.replaceWith(t.stringLiteral(`$${uniqueID}$SCRIPT_USER`))
71
+ else
72
+ referencePath.replaceWith(t.stringLiteral(scriptUser))
73
+ }
74
+ }
75
+
76
+ if (program.scope.hasGlobal("_SCRIPT_NAME")) {
77
+ for (const referencePath of getReferencePathsToGlobal("_SCRIPT_NAME", program)) {
78
+ if (scriptName == true)
79
+ referencePath.replaceWith(t.stringLiteral(`$${uniqueID}$SCRIPT_NAME`))
80
+ else
81
+ referencePath.replaceWith(t.stringLiteral(scriptName))
82
+ }
83
+ }
84
+
85
+ if (program.scope.hasGlobal("_FULL_SCRIPT_NAME")) {
86
+ for (const referencePath of getReferencePathsToGlobal("_FULL_SCRIPT_NAME", program)) {
87
+ if (scriptUser == true || scriptName == true)
88
+ referencePath.replaceWith(t.stringLiteral(`$${uniqueID}$FULL_SCRIPT_NAME`))
89
+ else
90
+ referencePath.replaceWith(t.stringLiteral(`${scriptUser}.${scriptName}`))
91
+ }
92
+ }
93
+
94
+ // TODO warn when script name is invalid
95
+ // TODO warn when not calling
96
+ for (const fakeSubscriptObjectName of [ "$fs", "$hs", "$ms", "$ls", "$ns", "$4s", "$3s", "$2s", "$1s", "$0s" ]) {
97
+ if (program.scope.hasGlobal(fakeSubscriptObjectName)) {
98
+ for (const referencePath of getReferencePathsToGlobal(fakeSubscriptObjectName, program)) {
99
+ assert(referencePath.parent.type == "MemberExpression")
100
+ assert(referencePath.parent.property.type == "Identifier")
101
+ assert(referencePath.parentPath.parentPath?.node.type == "MemberExpression")
102
+ assert(referencePath.parentPath.parentPath?.node.property.type == "Identifier")
103
+
104
+ // BUG this is causing typescript to be slow
105
+ referencePath.parentPath.parentPath.replaceWith(
106
+ t.identifier(`$${uniqueID}$SUBSCRIPT$${referencePath.parent.property.name}$${referencePath.parentPath.parentPath.node.property.name}`)
107
+ )
108
+ }
109
+ }
110
+ }
111
+
112
+ // TODO warn when db method is invalid
113
+ // TODO warn when not calling
114
+ if (program.scope.hasGlobal("$db")) {
115
+ for (const referencePath of getReferencePathsToGlobal("$db", program)) {
116
+ assert(referencePath.parentPath.node.type == "MemberExpression")
117
+ assert(referencePath.parentPath.node.property.type == "Identifier")
118
+
119
+ referencePath.parentPath.replaceWith(
120
+ t.identifier(`$${uniqueID}$DB$${referencePath.parentPath.node.property.name}`)
121
+ )
122
+ }
123
+ }
124
+
125
+ // TODO detect not being called and warn
126
+ if (program.scope.hasGlobal("$D")) {
127
+ for (const referencePath of getReferencePathsToGlobal("$D", program))
128
+ referencePath.replaceWith(t.identifier(`$${uniqueID}$DEBUG`))
129
+ }
130
+
131
+ if (program.scope.hasGlobal("$FMCL")) {
132
+ for (const referencePath of getReferencePathsToGlobal("$FMCL", program))
133
+ referencePath.replaceWith(t.identifier(`$${uniqueID}$FMCL`))
134
+ }
135
+
136
+ if (program.scope.hasGlobal("$G")) {
137
+ for (const referencePath of getReferencePathsToGlobal("$G", program))
138
+ referencePath.replaceWith(t.identifier(`$${uniqueID}$GLOBAL`))
139
+ }
140
+
141
+ if (program.scope.hasGlobal("_SECLEVEL")) {
142
+ for (const referencePath of getReferencePathsToGlobal("_SECLEVEL", program)) {
143
+ referencePath.replaceWith(
144
+ seclevel < 0
145
+ ? t.unaryExpression(
146
+ "-",
147
+ t.numericLiteral(-seclevel)
148
+ )
149
+ : t.numericLiteral(seclevel)
150
+ )
151
+ }
152
+ }
153
+
154
+ const globalBlock: BlockStatement = t.blockStatement([])
155
+ let mainFunction: FunctionDeclaration | undefined
156
+ const liveGlobalVariables: string[] = []
157
+
158
+ for (const statement of program.node.body) {
159
+ if (statement.type == "ExportDefaultDeclaration") {
160
+ if (statement.declaration.type == "FunctionDeclaration" || statement.declaration.type == "FunctionExpression" || statement.declaration.type == "ArrowFunctionExpression") {
161
+ mainFunction = t.functionDeclaration(
162
+ t.identifier(topFunctionName),
163
+ statement.declaration.params,
164
+ statement.declaration.body.type == "BlockStatement"
165
+ ? statement.declaration.body
166
+ : t.blockStatement([ t.returnStatement(statement.declaration.body) ])
167
+ )
168
+ } else {
169
+ assert(t.isExpression(statement.declaration))
170
+
171
+ mainFunction = t.functionDeclaration(
172
+ t.identifier(topFunctionName),
173
+ [
174
+ t.identifier("context"),
175
+ t.identifier("args")
176
+ ],
177
+ t.blockStatement([
178
+ t.returnStatement(
179
+ t.callExpression(statement.declaration, [])
180
+ )
181
+ ])
182
+ )
183
+ }
184
+ } else if (statement.type == "ExportNamedDeclaration") {
185
+ if (statement.declaration) {
186
+ if (statement.declaration.type == "VariableDeclaration") {
187
+ for (const declarator of statement.declaration.declarations) {
188
+ for (const identifierName in t.getBindingIdentifiers(declarator.id)) {
189
+ if (statement.declaration.kind == "const")
190
+ exports.set(identifierName, identifierName)
191
+ else
192
+ liveExports.set(identifierName, identifierName)
193
+
194
+ globalBlock.body.push(
195
+ t.variableDeclaration(
196
+ "let",
197
+ [ t.variableDeclarator(t.identifier(identifierName)) ]
198
+ )
199
+ )
200
+ }
201
+
202
+ if (declarator.init) {
203
+ globalBlock.body.push(
204
+ t.expressionStatement(
205
+ t.assignmentExpression(
206
+ "=",
207
+ declarator.id,
208
+ declarator.init
209
+ )
210
+ )
211
+ )
212
+ }
213
+ }
214
+ } else {
215
+ assert("id" in statement.declaration && statement.declaration.id, `unsupported export type "${statement.declaration.type}"`)
216
+
217
+ const name = statement.declaration.id.type == "Identifier"
218
+ ? statement.declaration.id.name
219
+ : statement.declaration.id.value
220
+
221
+ exports.set(name, name)
222
+ globalBlock.body.push(statement.declaration)
223
+ }
224
+ } else if (statement.specifiers) {
225
+ for (const specifier of statement.specifiers) {
226
+ assert(specifier.type == "ExportSpecifier", `${specifier.type} is currently unsupported`)
227
+
228
+ const exportedName = specifier.exported.type == "Identifier"
229
+ ? specifier.exported.name
230
+ : specifier.exported.value
231
+
232
+ if (exportedName == "default") {
233
+ mainFunction = t.functionDeclaration(
234
+ t.identifier(topFunctionName),
235
+ [
236
+ t.identifier("context"),
237
+ t.identifier("args")
238
+ ],
239
+ t.blockStatement([
240
+ t.returnStatement(
241
+ t.callExpression(specifier.local, [])
242
+ )
243
+ ])
244
+ )
245
+ } else if (liveGlobalVariables.includes(specifier.local.name)) {
246
+ liveExports.set(
247
+ specifier.local.name,
248
+ exportedName
249
+ )
250
+ } else {
251
+ exports.set(
252
+ specifier.local.name,
253
+ exportedName
254
+ )
255
+ }
256
+ }
257
+ }
258
+ } else if (statement.type == "VariableDeclaration") {
259
+ for (const declarator of statement.declarations) {
260
+ for (const identifierName in t.getBindingIdentifiers(declarator.id)) {
261
+ if (statement.kind != "const") {
262
+ if (exports.has(identifierName)) {
263
+ liveExports.set(identifierName, exports.get(identifierName)!)
264
+ exports.delete(identifierName)
265
+ } else
266
+ liveGlobalVariables.push(identifierName)
267
+ }
268
+
269
+ globalBlock.body.push(
270
+ t.variableDeclaration(
271
+ "let",
272
+ [ t.variableDeclarator(t.identifier(identifierName)) ]
273
+ )
274
+ )
275
+ }
276
+
277
+ if (declarator.init) {
278
+ globalBlock.body.push(
279
+ t.expressionStatement(
280
+ t.assignmentExpression(
281
+ "=",
282
+ declarator.id,
283
+ declarator.init
284
+ )
285
+ )
286
+ )
287
+ }
288
+ }
289
+ } else if (statement.type == "FunctionDeclaration") {
290
+ globalBlock.body.push(
291
+ t.variableDeclaration(
292
+ "let",
293
+ [
294
+ t.variableDeclarator(
295
+ statement.id!,
296
+ t.functionExpression(
297
+ null,
298
+ statement.params,
299
+ statement.body,
300
+ statement.generator,
301
+ statement.async
302
+ )
303
+ )
304
+ ]
305
+ )
306
+ )
307
+ } else
308
+ globalBlock.body.push(statement)
309
+ }
310
+
311
+ mainFunction ||= t.functionDeclaration(
312
+ t.identifier(topFunctionName),
313
+ [
314
+ t.identifier("context"),
315
+ t.identifier("args")
316
+ ],
317
+ t.blockStatement([])
318
+ )
319
+
320
+ program.node.body = [ mainFunction ]
321
+
322
+ if (globalBlock.body.length) {
323
+ if (exports.size || liveExports.size) {
324
+ mainFunction.body.body.push(
325
+ t.returnStatement(
326
+ t.objectExpression([
327
+ ...[ ...exports ].map(
328
+ ([ local, exported ]) =>
329
+ t.objectProperty(t.identifier(exported), t.identifier(local))
330
+ ),
331
+ ...[ ...liveExports ].map(
332
+ ([ local, exported ]) => t.objectMethod(
333
+ "get",
334
+ t.identifier(exported),
335
+ [],
336
+ t.blockStatement([
337
+ t.returnStatement(
338
+ t.identifier(local)
339
+ )
340
+ ])
341
+ )
342
+ )
343
+ ])
344
+ )
345
+ )
346
+ }
347
+
348
+ program.scope.crawl()
349
+
350
+ const globalBlockVariables = new Set<string>()
351
+ let hoistedGlobalBlockFunctions = 0
352
+
353
+ for (const [ globalBlockIndex, globalBlockStatement ] of [ ...globalBlock.body.entries() ].reverse()) {
354
+ if (globalBlockStatement.type == "VariableDeclaration") {
355
+ assert(globalBlockStatement.declarations.length == 1)
356
+
357
+ const declarator = globalBlockStatement.declarations[0]
358
+
359
+ assert(declarator.id.type == "Identifier", `declarator.id.type was "${declarator.id.type}"`)
360
+
361
+ program.scope.crawl()
362
+
363
+ if (program.scope.hasGlobal(declarator.id.name)) {
364
+ globalBlock.body.splice(globalBlockIndex, 1)
365
+
366
+ const [ globalBlockPath ] = program.unshiftContainer(
367
+ "body",
368
+ globalBlock
369
+ )
370
+
371
+ const [ globalBlockStatementPath ] = program.unshiftContainer(
372
+ "body",
373
+ globalBlockStatement
374
+ )
375
+
376
+ program.scope.crawl()
377
+
378
+ if (!declarator.init || (declarator.init.type != "FunctionExpression" && declarator.init.type != "ArrowFunctionExpression") || Object.keys((program.scope as any).globals).find(global => globalBlockVariables.has(global))) {
379
+ const binding = program.scope.getBinding(declarator.id.name)
380
+
381
+ assert(binding)
382
+
383
+ for (const referencePath of binding.referencePaths) {
384
+ assert(referencePath.node.type == "Identifier")
385
+
386
+ referencePath.replaceWith(
387
+ t.memberExpression(
388
+ t.identifier(`$${uniqueID}$GLOBAL`),
389
+ t.identifier(referencePath.node.name)
390
+ )
391
+ )
392
+ }
393
+
394
+ for (const referencePath of binding.constantViolations) {
395
+ assert(referencePath.node.type == "AssignmentExpression")
396
+
397
+ for (const [ name, node ] of Object.entries(t.getBindingIdentifiers(referencePath.node))) {
398
+ if (name == declarator.id.name) {
399
+ Object.assign(
400
+ clearObject(node),
401
+ t.memberExpression(
402
+ t.identifier(`$${uniqueID}$GLOBAL`),
403
+ t.identifier(name)
404
+ )
405
+ )
406
+ }
407
+ }
408
+ }
409
+
410
+ globalBlockPath.remove()
411
+ globalBlockStatementPath.remove()
412
+
413
+ if (declarator.init) {
414
+ globalBlock.body.splice(
415
+ globalBlockIndex,
416
+ 0,
417
+ t.expressionStatement(
418
+ t.assignmentExpression(
419
+ "=",
420
+ t.memberExpression(
421
+ t.identifier(`$${uniqueID}$GLOBAL`),
422
+ t.identifier(declarator.id.name)
423
+ ),
424
+ declarator.init
425
+ )
426
+ )
427
+ )
428
+ }
429
+ } else {
430
+ globalBlockPath.remove()
431
+ globalBlockStatementPath.remove()
432
+
433
+ mainFunction.body.body.unshift(
434
+ globalBlockStatement
435
+ )
436
+
437
+ hoistedGlobalBlockFunctions++
438
+ }
439
+ } else
440
+ globalBlockVariables.add(declarator.id.name)
441
+ } else if (globalBlockStatement.type == "ClassDeclaration") {
442
+ program.scope.crawl()
443
+
444
+ if (program.scope.hasGlobal(globalBlockStatement.id.name)) {
445
+ globalBlock.body.splice(globalBlockIndex, 1)
446
+
447
+ const [ globalBlockPath ] = program.unshiftContainer(
448
+ "body",
449
+ globalBlock
450
+ )
451
+
452
+ const [ globalBlockStatementPath ] = program.unshiftContainer(
453
+ "body",
454
+ globalBlockStatement
455
+ )
456
+
457
+ program.scope.crawl()
458
+
459
+ const binding = program.scope.getBinding(globalBlockStatement.id.name)
460
+
461
+ assert(binding)
462
+
463
+ for (const referencePath of binding.referencePaths) {
464
+ assert(referencePath.node.type == "Identifier")
465
+
466
+ referencePath.replaceWith(
467
+ t.memberExpression(
468
+ t.identifier(`$${uniqueID}$GLOBAL`),
469
+ t.identifier(referencePath.node.name)
470
+ )
471
+ )
472
+ }
473
+
474
+ globalBlockPath.remove()
475
+ globalBlockStatementPath.remove()
476
+
477
+ globalBlock.body.splice(
478
+ globalBlockIndex,
479
+ 0,
480
+ t.expressionStatement(
481
+ t.assignmentExpression(
482
+ "=",
483
+ t.memberExpression(
484
+ t.identifier(`$${uniqueID}$GLOBAL`),
485
+ t.identifier(globalBlockStatement.id.name)
486
+ ),
487
+ t.classExpression(
488
+ null,
489
+ globalBlockStatement.superClass,
490
+ globalBlockStatement.body,
491
+ globalBlockStatement.decorators
492
+ )
493
+ )
494
+ )
495
+ )
496
+ }
497
+ }
498
+ }
499
+
500
+ if (program.scope.hasGlobal("_EXPORTS")) {
501
+ for (const referencePath of getReferencePathsToGlobal("_EXPORTS", program)) {
502
+ referencePath.replaceWith(
503
+ t.arrayExpression(
504
+ [ ...exports.keys(), ...liveExports.keys() ]
505
+ .map(name => t.stringLiteral(name))
506
+ )
507
+ )
508
+ }
509
+ }
510
+
511
+ if (globalBlock.body.length) {
512
+ mainFunction.body.body.splice(
513
+ hoistedGlobalBlockFunctions,
514
+ 0,
515
+ t.ifStatement(
516
+ t.unaryExpression(
517
+ "!",
518
+ t.identifier(`$${uniqueID}$FMCL`)
519
+ ),
520
+ globalBlock
521
+ )
522
+ )
523
+ }
524
+ }
525
+
526
+ traverse(file, {
527
+ BlockStatement({ node: blockStatement }) {
528
+ for (const [ i, functionDeclaration ] of blockStatement.body.entries()) {
529
+ if (functionDeclaration.type != "FunctionDeclaration" || functionDeclaration.generator)
530
+ continue
531
+
532
+ blockStatement.body.splice(i, 1)
533
+
534
+ blockStatement.body.unshift(
535
+ t.variableDeclaration(
536
+ "let",
537
+ [
538
+ t.variableDeclarator(
539
+ functionDeclaration.id!,
540
+ t.arrowFunctionExpression(
541
+ functionDeclaration.params,
542
+ functionDeclaration.body,
543
+ functionDeclaration.async
544
+ )
545
+ )
546
+ ]
547
+ )
548
+ )
549
+ }
550
+ },
551
+
552
+ ClassBody({ node: classBody, scope, parent }) {
553
+ assert(t.isClass(parent))
554
+
555
+ let thisIsReferenced = false
556
+
557
+ for (const classMethod of classBody.body) {
558
+ if (classMethod.type != "ClassMethod")
559
+ continue
560
+
561
+ let methodReferencesThis = false
562
+
563
+ traverse(classMethod.body, {
564
+ ThisExpression(path) {
565
+ methodReferencesThis = true
566
+ thisIsReferenced = true
567
+ path.replaceWith(
568
+ t.identifier(`_THIS_${uniqueID}_`)
569
+ )
570
+ },
571
+
572
+ Function(path) {
573
+ path.skip()
574
+ }
575
+ }, scope)
576
+
577
+ if (!methodReferencesThis)
578
+ continue
579
+
580
+ if (classMethod.kind == "constructor") {
581
+ const superCalls: NodePath<CallExpression>[] = []
582
+
583
+ traverse(classMethod.body, {
584
+ CallExpression(path) {
585
+ if (path.node.callee.type == "Super")
586
+ superCalls.push(path)
587
+ }
588
+ }, scope)
589
+
590
+ if (!superCalls.length) {
591
+ classMethod.body.body.unshift(
592
+ t.variableDeclaration(
593
+ "let",
594
+ [
595
+ t.variableDeclarator(
596
+ t.identifier(`_THIS_${uniqueID}_`),
597
+ t.callExpression(t.super(), [])
598
+ )
599
+ ]
600
+ )
601
+ )
602
+ } else if (superCalls.length == 1 && superCalls[0].parent.type == "ExpressionStatement" && superCalls[0].parentPath.parentPath!.parent == classMethod) {
603
+ superCalls[0].parentPath.replaceWith(
604
+ t.variableDeclaration(
605
+ "let",
606
+ [
607
+ t.variableDeclarator(
608
+ t.identifier(`_THIS_${uniqueID}_`),
609
+ superCalls[0].node
610
+ )
611
+ ]
612
+ )
613
+ )
614
+ } else {
615
+ for (const path of superCalls) {
616
+ path.replaceWith(
617
+ t.assignmentExpression(
618
+ "=",
619
+ t.identifier(`_THIS_${uniqueID}_`),
620
+ path.node
621
+ )
622
+ )
623
+ }
624
+
625
+ classMethod.body.body.unshift(
626
+ t.variableDeclaration(
627
+ "let",
628
+ [
629
+ t.variableDeclarator(
630
+ t.identifier(`_THIS_${uniqueID}_`)
631
+ )
632
+ ]
633
+ )
634
+ )
635
+ }
636
+
637
+ continue
638
+ }
639
+
640
+ // BUG if the class or a super class overwrites `valueOf()` (or `Object.prototype` isn't even in the chain), this breaks
641
+ // TODO track whether the class is extending a class that at some point extends from `Object` (if unsure, assume no)
642
+ // TODO track whether any class in the chain overwrites `valueOf()` (if unsure, assume yes)
643
+ // TODO for classes that need it, create a super class for this one to extend from with `valueOf()` assigned to an unused name
644
+
645
+ classMethod.body.body.unshift(t.variableDeclaration(
646
+ "let",
647
+ [
648
+ t.variableDeclarator(
649
+ t.identifier(`_THIS_${uniqueID}_`),
650
+ t.callExpression(
651
+ t.memberExpression(
652
+ t.super(),
653
+ t.identifier("valueOf")
654
+ ),
655
+ []
656
+ )
657
+ )
658
+ ]
659
+ ))
660
+ }
661
+
662
+ if (!parent.superClass && thisIsReferenced)
663
+ parent.superClass = t.identifier("Object")
664
+ },
665
+
666
+ VariableDeclaration({ node: variableDeclaration }) {
667
+ if (variableDeclaration.kind == "const")
668
+ variableDeclaration.kind = "let"
669
+ },
670
+
671
+ ThisExpression(path) {
672
+ path.replaceWith(t.identifier(`_UNDEFINED_${uniqueID}_`))
673
+ },
674
+
675
+ BigIntLiteral(path) {
676
+ const bigIntAsNumber = Number(path.node.value)
677
+
678
+ if (BigInt(bigIntAsNumber) == BigInt(path.node.value)) {
679
+ path.replaceWith(
680
+ t.callExpression(
681
+ t.identifier("BigInt"),
682
+ [ t.numericLiteral(bigIntAsNumber) ]
683
+ )
684
+ )
685
+ } else {
686
+ path.replaceWith(
687
+ t.callExpression(
688
+ t.identifier("BigInt"),
689
+ [ t.stringLiteral(path.node.value) ]
690
+ )
691
+ )
692
+ }
693
+ }
694
+ })
695
+
696
+ // TODO this should probably be done in the minify step
697
+ // typescript does not like NodePath#get() and becomes very slow so I have to dance around it
698
+ const mainFunctionScope = (program.get("body.0" as string) as NodePath<FunctionDeclaration>).scope
699
+
700
+ for (const parameter of [ ...mainFunction.params ].reverse()) {
701
+ if (parameter.type == "Identifier") {
702
+ const binding = mainFunctionScope.getBinding(parameter.name)!
703
+
704
+ if (!binding.referenced) {
705
+ mainFunction.params.pop()
706
+ continue
707
+ }
708
+ }
709
+
710
+ break
711
+ }
712
+
713
+ // TODO this should be done in the minify step
714
+ for (const global in (program.scope as any).globals as Record<string, any>) {
715
+ if (global == "arguments" || global.startsWith(`$${uniqueID}`))
716
+ continue
717
+
718
+ const referencePaths = getReferencePathsToGlobal(global, program)
719
+
720
+ if (5 + global.length + referencePaths.length >= global.length * referencePaths.length)
721
+ continue
722
+
723
+ for (const path of referencePaths)
724
+ path.replaceWith(t.identifier(`_GLOBAL_${global}_${uniqueID}_`))
725
+
726
+ mainFunction.body.body.unshift(
727
+ t.variableDeclaration(
728
+ "let",
729
+ [
730
+ t.variableDeclarator(
731
+ t.identifier(`_GLOBAL_${global}_${uniqueID}_`),
732
+ t.identifier(global)
733
+ )
734
+ ]
735
+ )
736
+ )
737
+ }
738
+
739
+ return file
740
+ }
741
+
742
+ export default transform
743
+
744
+ function getReferencePathsToGlobal(name: string, program: NodePath<Program>) {
745
+ const [ variableDeclaration ] = program.unshiftContainer(
746
+ "body",
747
+ t.variableDeclaration(
748
+ "let",
749
+ [ t.variableDeclarator(t.identifier(name)) ]
750
+ )
751
+ )
752
+
753
+ program.scope.crawl()
754
+
755
+ const binding = ensure(program.scope.getBinding(name))
756
+
757
+ variableDeclaration.remove()
758
+
759
+ return binding.referencePaths as NodePath<Identifier>[]
760
+ }