forked from parcel-bundler/lightningcss
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbuild-ast.js
More file actions
109 lines (99 loc) · 3.24 KB
/
build-ast.js
File metadata and controls
109 lines (99 loc) · 3.24 KB
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
101
102
103
104
105
106
107
108
109
const { compileFromFile } = require('json-schema-to-typescript');
const fs = require('fs');
const recast = require('recast');
const traverse = require('@babel/traverse').default;
const {parse} = require('@babel/parser');
const t = require('@babel/types');
const skip = {
FillRule: true,
ImportRule: true,
FontFaceRule: true,
FontPaletteValuesRule: true,
NamespaceRule: true,
CustomMediaRule: true,
LayerStatementRule: true,
PropertyRule: true,
UnknownAtRule: true,
DefaultAtRule: true
}
compileFromFile('node/ast.json', {
additionalProperties: false
}).then(ts => {
ts = ts.replaceAll('For_DefaultAtRule', '');
// Use recast/babel to make some types generic so we can replace them in index.d.ts.
let ast = recast.parse(ts, {
parser: {
parse() {
return parse(ts, {
sourceType: 'module',
plugins: ['typescript'],
tokens: true
});
}
}
});
traverse(ast, {
Program(path) {
process(path.scope.getBinding('Declaration'));
process(path.scope.getBinding('MediaQuery'));
},
TSInterfaceDeclaration(path) {
// Dedupe.
if (path.node.id.name.startsWith('GenericBorderFor_LineStyleAnd_')) {
if (path.node.id.name.endsWith('_0')) {
path.node.id.name = 'GenericBorderFor_LineStyle';
} else {
path.remove();
}
}
},
ReferencedIdentifier(path) {
if (path.node.name.startsWith('GenericBorderFor_LineStyleAnd_')) {
path.node.name = 'GenericBorderFor_LineStyle';
}
}
});
ts = recast.print(ast, {objectCurlySpacing: false}).code;
fs.writeFileSync('node/ast.d.ts', ts)
});
function process(binding) {
// Follow the references upward from the binding to add generics.
for (let reference of binding.referencePaths) {
if (reference.node !== binding.identifier) {
genericize(reference, binding.identifier.name);
}
}
}
function genericize(path, name, seen = new Set()) {
if (seen.has(path.node)) return;
seen.add(path.node);
// Find the parent declaration of the reference, and add a generic if needed.
let parent = path.findParent(p => p.isDeclaration());
if (!parent.node.typeParameters) {
parent.node.typeParameters = t.tsTypeParameterDeclaration([]);
}
let params = parent.get('typeParameters');
let param = params.node.params.find(p => p.default.typeName.name === name);
if (!param) {
params.pushContainer('params', t.tsTypeParameter(null, t.tsTypeReference(t.identifier(name)), name[0]));
}
// Replace the reference with the generic, or add a type parameter.
if (path.node.name === name) {
path.replaceWith(t.identifier(name[0]));
} else {
if (!path.parent.typeParameters) {
path.parent.typeParameters = t.tsTypeParameterInstantiation([]);
}
let param = path.parent.typeParameters.params.find(p => p.typeName.name === name[0]);
if (!param) {
path.parentPath.get('typeParameters').pushContainer('params', t.tsTypeReference(t.identifier(name[0])));
}
}
// Keep going to all references of this reference.
let binding = path.scope.getBinding(parent.node.id.name);
for (let reference of binding.referencePaths) {
if (reference.node !== binding.identifier) {
genericize(reference, name, seen);
}
}
}