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
package/src/bin/hsm.ts ADDED
@@ -0,0 +1,505 @@
1
+ #!/usr/bin/env node
2
+ import { countHackmudCharacters, DynamicMap, writeFilePersistent } from "@samual/lib"
3
+ import chalk from "chalk"
4
+ import fs from "fs"
5
+ import { homedir as getHomeDirectory } from "os"
6
+ import { basename as getBaseName, dirname as getPathDirectory, extname as getFileExtension, relative as relativePath, resolve as resolvePath } from "path"
7
+ import { generateTypings, Info, processScript, pull, push, syncMacros, test, watch } from ".."
8
+ import { version as moduleVersion } from "../../package.json"
9
+ import { supportedExtensions } from "../constants.json"
10
+
11
+ const { readFile, rmdir: removeDirectory, writeFile, mkdir: makeDirectory } = fs.promises
12
+
13
+ type ArgValue = boolean | number | string/* | ArgValue[]*/
14
+
15
+ const configDirPath = resolvePath(getHomeDirectory(), ".config")
16
+ const configFilePath = resolvePath(configDirPath, "hsm.json")
17
+
18
+ const options = new Map<string, ArgValue>()
19
+ const commands: string[] = []
20
+
21
+ let config: Record<string, any> & Partial<{
22
+ hackmudPath: string
23
+ defaultUser: string
24
+ users: Record<string, {
25
+ colour: string
26
+ }>
27
+ }> | undefined
28
+
29
+ const colourJ = chalk.rgb(0xFF, 0xF4, 0x04)
30
+ const colourK = chalk.rgb(0xF3, 0xF9, 0x98)
31
+ const colourM = chalk.rgb(0xB3, 0xFF, 0x9B)
32
+ const colourW = chalk.rgb(0xFF, 0x96, 0xE0)
33
+ const colourL = chalk.rgb(0x1E, 0xFF, 0x00)
34
+ const colourB = chalk.rgb(0xCA, 0xCA, 0xCA)
35
+
36
+ const userColours = new DynamicMap<string, string>(user => {
37
+ let hash = 0
38
+
39
+ for (const char of user)
40
+ hash += (hash >> 1) + hash + "xi1_8ratvsw9hlbgm02y5zpdcn7uekof463qj".indexOf(char) + 1
41
+
42
+ return [ colourJ, colourK, colourM, colourW, colourL, colourB ][hash % 6](user)
43
+ })
44
+
45
+ for (const arg of process.argv.slice(2)) {
46
+ if (arg[0] == "-") {
47
+ const [ key, valueRaw ] = arg.split("=")
48
+ let value: ArgValue = valueRaw
49
+
50
+ if (value) {
51
+ if (value == "true")
52
+ value = true
53
+ else if (value == "false")
54
+ value = false
55
+ else {
56
+ const number = Number(value)
57
+
58
+ if (isFinite(number))
59
+ value = number
60
+ }
61
+ } else
62
+ value = true
63
+
64
+ if (arg[1] == "-")
65
+ options.set(key.slice(2), value)
66
+ else {
67
+ for (const option of key.slice(1))
68
+ options.set(option, value)
69
+ }
70
+ } else
71
+ commands.push(arg)
72
+ }
73
+
74
+ (async () => {
75
+ if (options.get("version") || options.get("v")) {
76
+ version()
77
+ return
78
+ }
79
+
80
+ if (options.get("help") || options.get("h")) {
81
+ help()
82
+ return
83
+ }
84
+
85
+ switch (commands[0]) {
86
+ case "push": {
87
+ const config = await getConfig()
88
+
89
+ if (!config.hackmudPath) {
90
+ console.log("you need to set hackmudPath in config before you can use this command")
91
+ break
92
+ }
93
+
94
+ const srcPath = commands[1] || "."
95
+ const hackmudPath = config.hackmudPath
96
+ const scripts = commands.slice(2)
97
+
98
+ if (!scripts.length)
99
+ scripts.push("*.*")
100
+
101
+ const infos = await push(
102
+ srcPath,
103
+ hackmudPath,
104
+ {
105
+ scripts,
106
+ onPush: onPushLogger,
107
+ minify: !options.get("skip-minify")
108
+ }
109
+ )
110
+
111
+ if (!infos.length)
112
+ console.warn("couldn't find any scripts to push")
113
+
114
+ updateConfig()
115
+ } break
116
+
117
+ case "dev":
118
+ case "watch": {
119
+ const config = await getConfig()
120
+
121
+ if (!config.hackmudPath) {
122
+ console.log("you need to set hackmudPath in config before you can use this command")
123
+ break
124
+ }
125
+
126
+ const srcPath = commands[1] || "."
127
+ const hackmudPath = config.hackmudPath
128
+ const users = options.get("users")?.toString().split(",") || []
129
+ const scripts = options.get("scripts")?.toString().split(",") || []
130
+ const genTypes = options.get("gen-types")?.toString()
131
+
132
+ watch(srcPath, hackmudPath, users, scripts, onPushLogger, { genTypes })
133
+ } break
134
+
135
+ case "pull": {
136
+ const config = await getConfig()
137
+
138
+ if (!config.hackmudPath) {
139
+ console.log("you need to set hackmudPath in config before you can use this command")
140
+ break
141
+ }
142
+
143
+ const script = commands[1]
144
+
145
+ if (!script) {
146
+ help()
147
+ break
148
+ }
149
+
150
+ const srcPath = commands[2] || "."
151
+ const hackmudPath = config.hackmudPath
152
+
153
+ try {
154
+ await pull(srcPath, hackmudPath, script)
155
+ } catch (error) {
156
+ console.log("something went wrong, did you forget to #down the script?")
157
+ }
158
+ } break
159
+
160
+ case "sync-macros": {
161
+ const { hackmudPath } = await getConfig()
162
+
163
+ if (!hackmudPath) {
164
+ console.log("you need to set hackmudPath in config before you can use this command")
165
+ break
166
+ }
167
+
168
+ const { macrosSynced, usersSynced } = await syncMacros(hackmudPath)
169
+ console.log(`synced ${macrosSynced} macros to ${usersSynced} users`)
170
+ } break
171
+
172
+ case "test": {
173
+ const srcPath = resolvePath(commands[1] || ".")
174
+ let errors = 0
175
+
176
+ console.log(`testing scripts in ${chalk.bold(srcPath)}\n`)
177
+
178
+ for (const { file, line, message } of await test(srcPath)) {
179
+ console.log(`error "${chalk.bold(message)}" in ${chalk.bold(file)} on line ${chalk.bold(String(line))}`)
180
+ errors++
181
+ }
182
+
183
+ if (!errors) {
184
+ console.log("no errors found")
185
+ break
186
+ }
187
+
188
+ if (errors) {
189
+ process.exitCode = 1
190
+ console.log(`\nencountered ${chalk.bold(String(errors))} errors`)
191
+ break
192
+ }
193
+
194
+ console.log("no errors found")
195
+ } break
196
+
197
+ case "gen-types": {
198
+ const srcPath = resolvePath(commands[1] || ".")
199
+ let targetPath: string
200
+
201
+ if (commands[2])
202
+ targetPath = resolvePath(commands[2])
203
+ else
204
+ targetPath = resolvePath(srcPath, "../player.d.ts")
205
+
206
+ generateTypings(srcPath, targetPath, (await getConfig()).hackmudPath)
207
+ } break
208
+
209
+ case "config":
210
+ switch (commands[1]) {
211
+ case "get": {
212
+ console.log(exploreObject(await getConfig(), commands[2].split(".")))
213
+ } break
214
+
215
+ case "delete": {
216
+ const keys = commands[2].split(".")
217
+
218
+ if (!keys.length) {
219
+ help()
220
+ break
221
+ }
222
+
223
+ const config = await getConfig()
224
+
225
+ delete exploreObject(config, keys)?.[commands[3]]
226
+
227
+ console.log(config)
228
+ } break
229
+
230
+ case "set": {
231
+ const keys = commands[2].split(".")
232
+
233
+ if (!keys.length) {
234
+ help()
235
+ break
236
+ }
237
+
238
+ const config = await getConfig()
239
+ let object = config
240
+
241
+ for (let key of keys.slice(0, -1))
242
+ object = typeof object[key] == "object" ? object[key] : object[key] = {}
243
+
244
+ object[keys.slice(-1)[0]] = commands[3]
245
+
246
+ if (config.hackmudPath)
247
+ config.hackmudPath = resolvePath(config.hackmudPath)
248
+
249
+ console.log(config)
250
+ } break
251
+
252
+ default: {
253
+ if (commands[1])
254
+ console.log("unknown command")
255
+
256
+ help()
257
+ }
258
+ } break
259
+
260
+ case "help":
261
+ case "h": {
262
+ help()
263
+ } break
264
+
265
+ case "version":
266
+ case "v": {
267
+ version()
268
+ } break
269
+
270
+ case "golf":
271
+ case "minify": {
272
+ // TODO `--watch` option
273
+
274
+ if (!commands[1]) {
275
+ console.log(`Target required\nUsage: ${getBaseName(process.argv[1])} ${commands[0]} <target> [output]`)
276
+ break
277
+ }
278
+
279
+ const fileExtension = getFileExtension(commands[1])
280
+
281
+ if (!supportedExtensions.includes(fileExtension)) {
282
+ console.log(`Unsupported file extension "${chalk.bold(fileExtension)}"\nSupported extensions are "${supportedExtensions.map(extension => chalk.bold(extension)).join('", "')}"`)
283
+ break
284
+ }
285
+
286
+ await readFile(commands[1], { encoding: "utf-8" }).then(
287
+ async source => {
288
+ const fileBaseName = getBaseName(commands[1], fileExtension)
289
+ const fileBaseNameEndsWithDotSrc = fileBaseName.endsWith(".src")
290
+
291
+ const scriptName = fileBaseNameEndsWithDotSrc
292
+ ? fileBaseName.slice(0, -4)
293
+ : fileBaseName
294
+
295
+ let scriptUser = "UNKNOWN"
296
+
297
+ if (getBaseName(resolvePath(commands[1], "..")) == "scripts" && getBaseName(resolvePath(commands[1], "../../..")) == "hackmud")
298
+ scriptUser = getBaseName(resolvePath(commands[1], "../.."))
299
+
300
+ const minify = !options.get("skip-minify")
301
+ const mangleNames = Boolean(options.get("mangle-names"))
302
+
303
+ if (!minify && mangleNames)
304
+ console.warn("warning: `--mangle-names` has no effect while `--skip-minify` is active")
305
+
306
+ const { script, srcLength, warnings, timeTook } = await processScript(
307
+ source,
308
+ {
309
+ minify,
310
+ scriptUser,
311
+ scriptName,
312
+ filePath: commands[1],
313
+ mangleNames
314
+ }
315
+ )
316
+
317
+ for (const { message, line } of warnings)
318
+ console.log(`warning "${chalk.bold(message)}" on line ${chalk.bold(String(line))}`)
319
+
320
+ let outputPath: string
321
+
322
+ if (commands[2])
323
+ outputPath = commands[2]
324
+ else {
325
+ outputPath = resolvePath(
326
+ getPathDirectory(commands[1]),
327
+
328
+ fileBaseNameEndsWithDotSrc
329
+ ? `${scriptName}.js` :
330
+ fileExtension == ".js"
331
+ ? `${fileBaseName}.min.js`
332
+ : `${fileBaseName}.js`
333
+ )
334
+ }
335
+
336
+ const scriptLength = countHackmudCharacters(script)
337
+
338
+ await writeFilePersistent(outputPath, script)
339
+ .catch(async (error: NodeJS.ErrnoException) => {
340
+ if (!commands[2] || error.code != "EISDIR")
341
+ throw error
342
+
343
+ outputPath = resolvePath(outputPath, `${getBaseName(commands[1], fileExtension)}.js`)
344
+
345
+ await writeFilePersistent(outputPath, script)
346
+ })
347
+ .then(
348
+ () => console.log(`wrote ${chalk.bold(scriptLength)} chars to ${chalk.bold(relativePath(".", outputPath))} | saved ${chalk.bold(srcLength - scriptLength)} chars | took ${Math.round(timeTook * 100) / 100}ms`),
349
+ (error: NodeJS.ErrnoException) => console.log(error.message)
350
+ )
351
+ },
352
+ (error: NodeJS.ErrnoException) => console.log(error.message)
353
+ )
354
+ } break
355
+
356
+ default: {
357
+ if (commands[0])
358
+ console.log("unknown command")
359
+
360
+ help()
361
+ }
362
+ }
363
+
364
+ updateConfig()
365
+ })()
366
+
367
+ function help() {
368
+ switch (commands[0]) {
369
+ case "config": {
370
+ switch (commands[1]) {
371
+ case "get": {
372
+ console.log("hsm config get <key>")
373
+ } break
374
+
375
+ case "set": {
376
+ console.log("hsm config set <key> <value>")
377
+ } break
378
+
379
+ case "delete": {
380
+ console.log("hsm config delete <key>")
381
+ } break
382
+
383
+ default: {
384
+ console.log("hsm config <get, delete, set>")
385
+ }
386
+ }
387
+ } break
388
+
389
+ case "push": {
390
+ console.log("hsm push [<dir> [...\"<script user>.<script name>\"]]")
391
+ } break
392
+
393
+ case "watch": {
394
+ console.log("hsm watch [dir]")
395
+ } break
396
+
397
+ case "pull": {
398
+ console.log("hsm pull <script user>.<script name>")
399
+ } break
400
+
401
+ case "minify":
402
+ case "golf": {
403
+ console.log(`${getBaseName(process.argv[1])} ${commands[0]} <target> [output]`)
404
+ } break
405
+
406
+ default: {
407
+ console.log("hsm <push, watch, pull, config, golf>")
408
+ }
409
+ }
410
+ }
411
+
412
+ async function version() {
413
+ console.log(moduleVersion)
414
+ }
415
+
416
+ async function getConfig() {
417
+ if (config)
418
+ return config
419
+
420
+ return config = await readFile(configFilePath, { encoding: "utf-8" })
421
+ .then(configFile => {
422
+ let tempConfig
423
+
424
+ try {
425
+ tempConfig = JSON.parse(configFile)
426
+ } catch {
427
+ // TODO log to error log file
428
+ console.log("config file was corrupted, resetting")
429
+ return {}
430
+ }
431
+
432
+ if (!tempConfig || typeof tempConfig != "object") {
433
+ console.log("config file was corrupted, resetting")
434
+ return {}
435
+ }
436
+
437
+ return tempConfig
438
+ }, () => {
439
+ console.log(`creating config file at ${configFilePath}`)
440
+ return {}
441
+ })
442
+ }
443
+
444
+ function exploreObject(object: any, keys: string[], createPath = false) {
445
+ for (const key of keys) {
446
+ if (createPath)
447
+ object = typeof object[key] == "object" ? object[key] : object[key] = {}
448
+ else
449
+ object = object?.[key]
450
+ }
451
+
452
+ return object
453
+ }
454
+
455
+ function updateConfig() {
456
+ if (config) {
457
+ const json = JSON.stringify(config)
458
+
459
+ writeFile(configFilePath, json).catch(async error => {
460
+ switch (error.code) {
461
+ case "EISDIR": {
462
+ await removeDirectory(configFilePath)
463
+ } break
464
+
465
+ case "ENOENT": {
466
+ await makeDirectory(configDirPath)
467
+ } break
468
+
469
+ default: {
470
+ throw error
471
+ }
472
+ }
473
+
474
+ writeFile(configFilePath, json)
475
+ })
476
+ }
477
+ }
478
+
479
+ function onPushLogger({ file, users, srcLength, minLength, error }: Info) {
480
+ if (!users.length)
481
+ return
482
+
483
+ if (error) {
484
+ console.log(`error "${chalk.bold(error.message)}" in ${chalk.bold(file)}`)
485
+ return
486
+ }
487
+
488
+ console.log(
489
+ `pushed ${
490
+ chalk.bold(file)
491
+ } to ${
492
+ users.map(user => chalk.bold(userColours.get(user))).join(", ")
493
+ } | ${
494
+ chalk.bold(String(minLength))
495
+ } chars from ${
496
+ chalk.bold(String(srcLength))
497
+ } | saved ${
498
+ chalk.bold(String(srcLength - minLength))
499
+ } (${
500
+ chalk.bold(`${Math.round((1 - (minLength / srcLength)) * 100)}%`)
501
+ }) | ${
502
+ chalk.bold(`${resolvePath(config!.hackmudPath!, users[0], "scripts", getBaseName(file, getFileExtension(file)))}.js`)
503
+ }`
504
+ )
505
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "supportedExtensions": [ ".js", ".ts" ]
3
+ }
@@ -0,0 +1,116 @@
1
+ import fs from "fs"
2
+ import { basename as getBaseName, extname as getFileExtension, resolve as resolvePath } from "path"
3
+
4
+ const { readdir: readDirectory, writeFile } = fs.promises
5
+
6
+ export async function generateTypings(srcDir: string, target: string, hackmudPath?: string) {
7
+ const users = new Set<string>()
8
+
9
+ if (hackmudPath) {
10
+ for (const dirent of await readDirectory(hackmudPath, { withFileTypes: true })) {
11
+ if (dirent.isFile() && getFileExtension(dirent.name) == ".key")
12
+ users.add(getBaseName(dirent.name, ".key"))
13
+ }
14
+ }
15
+
16
+ const wildScripts: string[] = []
17
+ const wildAnyScripts: string[] = []
18
+ const allScripts: Record<string, string[]> = {}
19
+ const allAnyScripts: Record<string, string[]> = {}
20
+
21
+ for (const dirent of await readDirectory(srcDir, { withFileTypes: true })) {
22
+ if (dirent.isFile()) {
23
+ if (getFileExtension(dirent.name) == ".ts")
24
+ wildScripts.push(getBaseName(dirent.name, ".ts"))
25
+ else if (getFileExtension(dirent.name) == ".js")
26
+ wildAnyScripts.push(getBaseName(dirent.name, ".js"))
27
+ } else if (dirent.isDirectory()) {
28
+ const scripts: string[] = allScripts[dirent.name] = []
29
+ const anyScripts: string[] = allAnyScripts[dirent.name] = []
30
+
31
+ users.add(dirent.name)
32
+
33
+ for (const file of await readDirectory(resolvePath(srcDir, dirent.name), { withFileTypes: true })) {
34
+ if (file.isFile()) {
35
+ if (getFileExtension(file.name) == ".ts")
36
+ scripts.push(getBaseName(file.name, ".ts"))
37
+ else if (getFileExtension(file.name) == ".js")
38
+ anyScripts.push(getBaseName(file.name, ".js"))
39
+ }
40
+ }
41
+ }
42
+ }
43
+
44
+ let o = ""
45
+
46
+ for (const script of wildScripts)
47
+ o += `import { script as $${script}$ } from "./src/${script}"\n`
48
+
49
+ o += "\n"
50
+
51
+ for (const user in allScripts) {
52
+ const scripts = allScripts[user]
53
+
54
+ for (const script of scripts)
55
+ o += `import { script as $${user}$${script}$ } from "./src/${user}/${script}"\n`
56
+ }
57
+
58
+ // TODO detect security level and generate apropriate code
59
+
60
+ // TODO accurate function signatures
61
+ // currently I lose the generic-ness of my functions when I wrap them
62
+ // just regexing isn't enough and it looks like I'm going to need to parse the files in TypeScript to extract the signature
63
+
64
+ o += `
65
+ type ArrayRemoveFirst<A> = A extends [ infer FirstItem, ...infer Rest ] ? Rest : never
66
+
67
+ type Subscript<T extends (...args: any) => any> =
68
+ (...args: ArrayRemoveFirst<Parameters<T>>) => ReturnType<T> | ScriptFailure
69
+
70
+ type WildFullsec = Record<string, () => ScriptFailure> & {
71
+ `
72
+
73
+ for (const script of wildScripts)
74
+ o += `\t${script}: Subscript<typeof $${script}$>\n`
75
+
76
+ for (const script of wildAnyScripts)
77
+ o += `\t${script}: (...args: any) => any\n`
78
+
79
+ o += "}\n\ndeclare global {\n\tinterface PlayerFullsec {"
80
+
81
+ let lastWasMultiLine = true
82
+
83
+ for (const user of users) {
84
+ const scripts = allScripts[user]
85
+ const anyScripts = allAnyScripts[user]
86
+
87
+ if ((scripts && scripts.length) || (anyScripts && anyScripts.length)) {
88
+ lastWasMultiLine = true
89
+
90
+ o += `\n\t\t${user}: WildFullsec & {\n`
91
+
92
+ for (const script of scripts)
93
+ o += `\t\t\t${script}: Subscript<typeof $${user}$${script}$>\n`
94
+
95
+ for (const script of anyScripts)
96
+ o += `\t\t\t${script}: (...args: any) => any\n`
97
+
98
+ o += "\t\t}"
99
+ } else {
100
+ if (lastWasMultiLine) {
101
+ o += "\n"
102
+ lastWasMultiLine = false
103
+ }
104
+
105
+ o += `\t\t${user}: WildFullsec`
106
+ }
107
+
108
+ o += "\n"
109
+ }
110
+
111
+ o += "\t}\n}\n"
112
+
113
+ await writeFile(target, o)
114
+ }
115
+
116
+ export default generateTypings
package/src/index.ts ADDED
@@ -0,0 +1,19 @@
1
+ export { supportedExtensions } from "./constants.json"
2
+ export { generateTypings } from "./generateTypings"
3
+ export { processScript } from "./processScript"
4
+ export { pull } from "./pull"
5
+ export { push } from "./push"
6
+ export { syncMacros } from "./syncMacros"
7
+ export { test } from "./test"
8
+ export { watch } from "./watch"
9
+
10
+ // TODO `clean()` function that delete all scripts in hackmud directory #70
11
+ // TODO optional argument (defaults to false) for `clean()` that makes it only remove scripts without a source file #70
12
+
13
+ export interface Info {
14
+ file: string
15
+ users: string[]
16
+ srcLength: number
17
+ minLength: number
18
+ error: Error | null
19
+ }
@@ -0,0 +1,5 @@
1
+ declare module "@babel/plugin-*" {
2
+ export default {
3
+ default: (...args: any[]) => any
4
+ }
5
+ }