1 | import { casesExhausted } from './prelude.js'
|
2 |
|
3 | type Parameter = { type: 'name'; value: string } | { type: 'destructured'; names: string[] }
|
4 |
|
5 | export const analyzeFunction = (fn: (...args: [...any[]]) => unknown) => {
|
6 | const groups = fn.toString().match(functionPattern)?.groups
|
7 | if (!groups) throw new Error(`Could not extract groups from function.`)
|
8 |
|
9 | const body = groups[`bodyStatement`] ?? groups[`bodyExpression`]
|
10 | if (body === undefined) throw new Error(`Could not extract body from function.`)
|
11 |
|
12 | const parameters: Parameter[] = []
|
13 |
|
14 | if (groups[`parameters`]) {
|
15 | const results = [...groups[`parameters`].matchAll(functionParametersPattern)]
|
16 | const resultParameters = results.map(result => {
|
17 | const type = result.groups?.[`destructured`] ? `destructured` : result.groups?.[`name`] ? `name` : null
|
18 |
|
19 | switch (type) {
|
20 | case `destructured`:
|
21 | const valueRaw = result.groups![`destructured`]!
|
22 | const names = [...valueRaw.matchAll(destructuredPattern)].map(result => {
|
23 | const name = result.groups![`name`]
|
24 | if (name === undefined) throw new Error(`Could not extract name from destructured parameter.`)
|
25 | return name
|
26 | })
|
27 | return {
|
28 | type,
|
29 | names,
|
30 | } as const
|
31 | case `name`:
|
32 | return {
|
33 | type,
|
34 | value: result.groups![`name`]!,
|
35 | } as const
|
36 | case null:
|
37 | throw new Error(`Could not determine type of parameter.`)
|
38 | default:
|
39 | throw casesExhausted(type)
|
40 | }
|
41 | })
|
42 |
|
43 | parameters.push(...resultParameters)
|
44 | }
|
45 |
|
46 | return {
|
47 | body,
|
48 | parameters,
|
49 | }
|
50 | }
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 | const functionPattern =
|
61 | /^(?:(?<async>async)\s+)?(?:function\s+)?(?:(?<name>[A-z_0-9]+)\s*)?\((?<parameters>[^)]*)\)\s*(?:=>\s*(?<bodyExpression>[^\s{].*)|(?:=>\s*)?{(?<bodyStatement>.*)})$/s
|
62 |
|
63 | /**
|
64 | * @see https://regex101.com/r/tE2dV5/2
|
65 | */
|
66 | const functionParametersPattern = /(?<destructured>\{[^}]+\})|(?<name>[A-z_][A-z_0-9]*)/gs
|
67 |
|
68 | /**
|
69 | * https://regex101.com/r/WHwazx/1
|
70 | */
|
71 | const destructuredPattern = /(?<name>[A-z_][A-z_0-9]*)(?::[^},]+)?/gs
|
72 |
|
\ | No newline at end of file |