Skip to content

Commit d78f91e

Browse files
authored
feat: Add types of emits (#1)
1 parent f03fb76 commit d78f91e

File tree

4 files changed

+5401
-883
lines changed

4 files changed

+5401
-883
lines changed

generate.js

Lines changed: 89 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,17 @@ const webTypes = JSON.parse(
88
readFileSync("./node_modules/vuetify/dist/json/web-types.json")
99
);
1010

11+
const tags = webTypes.contributions.html.tags;
12+
13+
// https://github.com/vuetifyjs/vuetify/pull/18307
14+
// Remove it after Vuetify 2.7.2 released
15+
tags
16+
.find((component) => component.name === "VDialog")
17+
.slots[0]["vue-properties"].push({
18+
name: "attrs",
19+
type: "{ role: string, aria-haspopup: boolean, aria-expanded: string }",
20+
});
21+
1122
const blackList = ["VFlex", "VLayout"]; // Components not to define in global
1223

1324
function convertType(typeStr) {
@@ -20,6 +31,8 @@ function convertType(typeStr) {
2031
return "Date";
2132
case "regexp":
2233
return "RegExp";
34+
case "event":
35+
return "Event";
2336
default:
2437
return typeStr;
2538
}
@@ -28,7 +41,7 @@ function convertType(typeStr) {
2841
function getPropType(attr) {
2942
const attrType = attr.value.type;
3043
if (attr.name == "rules" && attr.description?.includes("error message")) {
31-
return "InputValidationRules"
44+
return "InputValidationRules";
3245
}
3346
if (typeof attrType === "string") {
3447
return convertType(attrType);
@@ -50,7 +63,7 @@ function getSlotPropType(type) {
5063
.replace(/\/\/.*/, "")
5164
.replaceAll("):", ")=>")
5265
.replace(/(aria-[a-z]*):/g, '"$1":')
53-
.replace(/ = \w*/g, '') // remove default values
66+
.replace(/ = \w*/g, "") // remove default values
5467
.replaceAll("function", "Function")
5568
.replaceAll("Function", "(...args: any[]) => any")
5669
.replaceAll("object", "{ [key: keyof any]: any }")
@@ -62,30 +75,82 @@ function getSlotName(name) {
6275
return "[name:`header.${string}`]";
6376
} else if (name === "item.<name>") {
6477
return "[name:`item.${string}`]";
65-
} else if (name.startsWith("item.data-table") || name.startsWith("header.data-table")) {
78+
} else if (
79+
name.startsWith("item.data-table") ||
80+
name.startsWith("header.data-table")
81+
) {
82+
// Ts doesn't allow overriding template literals with more specific keys/values.
83+
return `//@ts-expect-error\n '${name}'`;
84+
}
85+
return `'${name}'`;
86+
}
87+
88+
function splitByComma(str) {
89+
const result = [];
90+
let current = "";
91+
let level = 0;
92+
for (let i = 0; i < str.length; i++) {
93+
const char = str[i];
94+
if (char === "{") {
95+
level++;
96+
}
97+
if (char === "}") {
98+
level--;
99+
}
100+
if (char === "," && level === 0) {
101+
result.push(current);
102+
current = "";
103+
} else {
104+
current += char;
105+
}
106+
}
107+
result.push(current);
108+
return result;
109+
}
110+
111+
function getEventArguments(args) {
112+
const _args = args
113+
.replaceAll(':"', ":")
114+
.replaceAll('",', ",")
115+
.replaceAll('"}', "}")
116+
.replaceAll("):", ")=>")
117+
.replace(/ = \w*/g, "") // remove default values
118+
.replaceAll("ClickEvent", "MouseEvent");
119+
120+
// event may have multiple arguments
121+
const matches = splitByComma(_args)
122+
.map((a, i) => `arg${i}:${convertType(a)}`)
123+
.join(",");
124+
return matches;
125+
}
126+
127+
function getEventName(name) {
128+
if (name === "<event>:row") {
129+
return "[name:`${string}:row`]";
130+
} else if (name.startsWith("click:row")) {
66131
// Ts doesn't allow overriding template literals with more specific keys/values.
67132
return `//@ts-expect-error\n '${name}'`;
68133
}
69134
return `'${name}'`;
70135
}
71136

72-
const types = webTypes.contributions.html.tags
137+
const types = tags
73138
.filter((vm) => !blackList.includes(vm.name))
74139
.map(
75140
(vm) =>
76141
vm.name +
77142
": DefineComponent<{" +
78-
79143
// Prop types:
80144
vm.attributes
81145
.map(
82146
(attr) =>
83147
getDescription(attr) +
84-
`${attr.name.replace(/-./g, (x) => x[1].toUpperCase())}?: ${getPropType(attr)} | null`
148+
`${attr.name.replace(/-./g, (x) =>
149+
x[1].toUpperCase()
150+
)}?: ${getPropType(attr)} | null`
85151
)
86152
.join("\n") +
87153
"}" +
88-
89154
// Slot types:
90155
(vm.slots?.length
91156
? ",{$scopedSlots: Readonly<{\n" +
@@ -107,15 +172,29 @@ const types = webTypes.contributions.html.tags
107172
)
108173
.join("\n") +
109174
"}>}\n"
175+
: ",{}") +
176+
// Event types:
177+
(vm.events?.length
178+
? ",{},{},{},{},{},{\n" +
179+
vm.events
180+
.map(
181+
(event) =>
182+
getDescription(event) +
183+
`${getEventName(event.name)}:(${getEventArguments(
184+
event.arguments[0].type
185+
)})=>void`
186+
)
187+
.join("\n") +
188+
"}"
110189
: "") +
111190
">"
112191
)
113192
.join("\n\n");
114193

115194
console.log();
116195

117-
console.log(
118-
prettier.format(
196+
prettier
197+
.format(
119198
`
120199
import type { DataTableHeader, DataOptions, CalendarTimestamp as VTimestamp } from 'vuetify'
121200
import type VueComponent from "vue"
@@ -147,4 +226,4 @@ export {}`,
147226
semi: false,
148227
}
149228
)
150-
);
229+
.then((v) => console.log(v));

0 commit comments

Comments
 (0)