forked from launchql/libpg-query-node
-
Notifications
You must be signed in to change notification settings - Fork 5
/
inferFieldMetadata.ts
100 lines (88 loc) · 2.83 KB
/
inferFieldMetadata.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import fs from 'node:fs'
import path from 'node:path'
import { castArrayIfExists, select, unique } from 'radashi'
import type { Node } from '../src/lib/ast'
import { parseQuerySync } from '../src/lib/binding'
import { $ } from '../src/lib/select'
import { walk } from '../src/lib/walk'
import { parseTestFile } from '../test/parseTestFile'
const testDir = 'libpg_query/test/sql/postgres_regress'
const testFiles = fs.readdirSync(testDir)
export type NodeFieldMetadata = [
nullable: boolean,
tags: string[] | null,
listTags: string[] | null,
]
export type NodeFieldMetadataByTag = {
[typeName: string]: {
[fieldName: string]: NodeFieldMetadata
}
}
const fieldsByNodeTag: NodeFieldMetadataByTag = {}
const toNodeTag = (value: unknown) => {
if (value != null && typeof value === 'object') {
const keys = Object.keys(value)
if (keys.length === 1 && /^[A-Z]/.test(keys[0])) {
return keys[0]
}
if (keys.length === 0) {
return '{}'
}
}
}
const inferNodeTags = (value: unknown) =>
Array.isArray(value)
? unique(select(value, toNodeTag))
: castArrayIfExists(toNodeTag(value)) ?? null
const inferListTags = (value: Node[]) => {
const lists = value.filter($.isList)
const itemTags = lists.flatMap(list => inferNodeTags(list.List.items) ?? [])
return itemTags.length > 0 ? unique(itemTags) : null
}
for (const testFile of testFiles) {
try {
const stmts = parseTestFile(path.join(testDir, testFile))
for (const { stmt } of stmts) {
try {
const ast = parseQuerySync(stmt)
walk(ast, path => {
const defaultNullability = path.tag in fieldsByNodeTag
const seen = (fieldsByNodeTag[path.tag] ??= {})
for (const key in path.node) {
const value = (path.node as any)[key]
const tags = inferNodeTags(value)
const listTags =
tags && Array.isArray(value) && tags.includes('List')
? inferListTags(value)
: null
if (seen[key] == null) {
seen[key] = [defaultNullability, tags, listTags]
} else {
if (tags) {
seen[key][1] = seen[key][1]
? unique([...tags, ...seen[key][1]])
: tags
}
if (listTags) {
seen[key][2] = seen[key][2]
? unique([...listTags, ...seen[key][2]])
: listTags
}
}
}
for (const key in seen) {
if (!(key in path.node)) {
seen[key] ??= [true, null, null]
seen[key][0] = true
}
}
})
} catch {}
}
} catch {}
}
const scriptDir = new URL('.', import.meta.url).pathname
fs.writeFileSync(
path.join(scriptDir, 'data/fieldMetadata.json'),
JSON.stringify(fieldsByNodeTag, null, 2),
)