Skip to content

Commit 775bb83

Browse files
committed
feat: Use declare class where possible
1 parent 1ba0ad7 commit 775bb83

File tree

1 file changed

+112
-23
lines changed

1 file changed

+112
-23
lines changed

Diff for: src/emitter.ts

+112-23
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,16 @@ function isEventHandler(p: Browser.Property) {
120120
return typeof p["event-handler"] === "string";
121121
}
122122

123+
const newToConstructorRegExp = /^new(?:<.+?>)?(\(.+?\))(?:: ?[^)]+)?$/u;
124+
function convertNewToConstructor(signature: string) {
125+
const result = newToConstructorRegExp.exec(signature);
126+
if (result) {
127+
return `constructor${result[1]}`;
128+
} else {
129+
return signature;
130+
}
131+
}
132+
123133
export function emitWebIdl(webidl: Browser.WebIdl, flavor: Flavor) {
124134
// Global print target
125135
const printer = createTextWriter("\n");
@@ -383,16 +393,16 @@ export function emitWebIdl(webidl: Browser.WebIdl, flavor: Flavor) {
383393
return `${i.name}<${typeParameters.map(t => t.name)}>`;
384394
}
385395

386-
function emitConstant(c: Browser.Constant) {
396+
function emitConstant(c: Browser.Constant, prefix = "") {
387397
emitComments(c, printer.printLine);
388-
printer.printLine(`readonly ${c.name}: ${convertDomTypeToTsType(c)};`);
398+
printer.printLine(`${prefix}readonly ${c.name}: ${convertDomTypeToTsType(c)};`);
389399
}
390400

391-
function emitConstants(i: Browser.Interface) {
401+
function emitConstants(i: Browser.Interface, prefix = "") {
392402
if (i.constants) {
393403
mapToArray(i.constants.constant)
394404
.sort(compareName)
395-
.forEach(emitConstant);
405+
.forEach(c => emitConstant(c, prefix));
396406
}
397407
}
398408

@@ -627,11 +637,11 @@ export function emitWebIdl(webidl: Browser.WebIdl, flavor: Flavor) {
627637
if (!required && prefix) {
628638
pType += " | undefined"
629639
}
630-
const readOnlyModifier = p["read-only"] === 1 && prefix === "" ? "readonly " : "";
640+
const readOnlyModifier = p["read-only"] === 1 && (prefix === "" || prefix === "static ") ? "readonly " : "";
631641
printer.printLine(`${prefix}${readOnlyModifier}${p.name}${requiredModifier}: ${pType};`);
632642
}
633643

634-
if (p.stringifier) {
644+
if (p.stringifier && emitScope !== EmitScope.StaticOnly) {
635645
printer.printLine("toString(): string;")
636646
}
637647
}
@@ -688,19 +698,24 @@ export function emitWebIdl(webidl: Browser.WebIdl, flavor: Flavor) {
688698

689699
function emitSignature(s: Browser.Signature, prefix: string | undefined, name: string | undefined, printLine: (s: string) => void) {
690700
const paramsString = s.param ? paramsToString(s.param) : "";
691-
let returnType = convertDomTypeToTsType(s);
692-
returnType = s.nullable ? makeNullable(returnType) : returnType;
701+
let returnType = "";
702+
if (name !== "constructor") {
703+
returnType = `: ${convertDomTypeToTsType(s)}`;
704+
returnType = s.nullable ? makeNullable(returnType) : returnType;
705+
}
693706
emitComments(s, printLine);
694-
printLine(`${prefix || ""}${name || ""}(${paramsString}): ${returnType};`);
707+
printLine(`${prefix || ""}${name || ""}(${paramsString})${returnType};`);
695708
}
696709

697710
function emitSignatures(method: { signature?: Browser.Signature[], "override-signatures"?: string[], "additional-signatures"?: string[] }, prefix: string, name: string, printLine: (s: string) => void) {
698711
if (method["override-signatures"]) {
699-
method["override-signatures"]!.forEach(s => printLine(`${prefix}${s};`));
712+
method["override-signatures"]!.forEach(s => {
713+
printLine(`${prefix}${name === "constructor" ? convertNewToConstructor(s) : s};`);
714+
});
700715
}
701716
else if (method.signature) {
702717
if (method["additional-signatures"]) {
703-
method["additional-signatures"]!.forEach(s => printLine(`${prefix}${s};`));
718+
method["additional-signatures"]!.forEach(s => printLine(`${prefix}${name === "constructor" ? convertNewToConstructor(s) : s};`));
704719
}
705720
method.signature.forEach(sig => emitSignature(sig, prefix, name, printLine));
706721
}
@@ -715,7 +730,7 @@ export function emitWebIdl(webidl: Browser.WebIdl, flavor: Flavor) {
715730
.sort(compareName)
716731
.forEach(m => emitMethod(prefix, m, conflictedMembers));
717732
}
718-
if (i["anonymous-methods"]) {
733+
if (i["anonymous-methods"] && emitScope !== EmitScope.StaticOnly) {
719734
const stringifier = i["anonymous-methods"].method.find(m => m.stringifier);
720735
if (stringifier) {
721736
printer.printLine("toString(): string;");
@@ -746,7 +761,7 @@ export function emitWebIdl(webidl: Browser.WebIdl, flavor: Flavor) {
746761
function emitMembers(prefix: string, emitScope: EmitScope, i: Browser.Interface) {
747762
const conflictedMembers = extendConflictsBaseTypes[i.name] ? extendConflictsBaseTypes[i.name].memberNames : new Set<string>();
748763
emitProperties(prefix, emitScope, i);
749-
const methodPrefix = prefix.startsWith("declare var") ? "declare function " : "";
764+
const methodPrefix = prefix.startsWith("declare var") ? "declare function " : prefix.startsWith("static") ? "static " : "";
750765
emitMethods(methodPrefix, emitScope, i, conflictedMembers);
751766
if (emitScope === EmitScope.InstanceOnly) {
752767
emitIteratorForEach(i);
@@ -799,16 +814,16 @@ export function emitWebIdl(webidl: Browser.WebIdl, flavor: Flavor) {
799814
}
800815
}
801816

802-
function emitConstructorSignature(i: Browser.Interface) {
817+
function emitConstructorSignature(i: Browser.Interface, keyword = "new") {
803818
const constructor = typeof i.constructor === "object" ? i.constructor : undefined;
804819

805820
// Emit constructor signature
806821
if (constructor) {
807822
emitComments(constructor, printer.print);
808-
emitSignatures(constructor, "", "new", printer.printLine);
823+
emitSignatures(constructor, "", keyword, printer.printLine);
809824
}
810-
else {
811-
printer.printLine(`new(): ${i.name};`);
825+
else if (keyword !== "constructor") {
826+
printer.printLine(`${keyword}(): ${i.name};`);
812827
}
813828
}
814829

@@ -845,6 +860,7 @@ export function emitWebIdl(webidl: Browser.WebIdl, flavor: Flavor) {
845860
printer.printLine(`declare var ${nc.name}: {`);
846861
printer.increaseIndent();
847862
nc.signature.forEach(s => printer.printLine(`new(${s.param ? paramsToString(s.param) : ""}): ${i.name};`));
863+
printer.printLine(`readonly prototype: ${i.name};`);
848864
printer.decreaseIndent();
849865
printer.printLine(`};`);
850866
}
@@ -857,11 +873,11 @@ export function emitWebIdl(webidl: Browser.WebIdl, flavor: Flavor) {
857873
.forEach(emitNamedConstructor);
858874
}
859875

860-
function emitInterfaceDeclaration(i: Browser.Interface) {
861-
function processIName(iName: string) {
862-
return extendConflictsBaseTypes[iName] ? `${iName}Base` : iName;
863-
}
876+
function processIName(iName: string) {
877+
return extendConflictsBaseTypes[iName] ? `${iName}Base` : iName;
878+
}
864879

880+
function emitInterfaceDeclaration(i: Browser.Interface) {
865881
const processedIName = processIName(i.name);
866882

867883
if (processedIName !== i.name) {
@@ -986,6 +1002,71 @@ export function emitWebIdl(webidl: Browser.WebIdl, flavor: Flavor) {
9861002
}
9871003
}
9881004

1005+
function emitClassDeclaration(i: Browser.Interface, prefix = "") {
1006+
let finalExtendsName = i.extends && i.extends !== "Object" ? i.extends : null;
1007+
let extendsKeyword = "extends";
1008+
const finalImplements = (i.implements || []).sort().map(processIName);
1009+
1010+
if (finalExtendsName) {
1011+
if (finalExtendsName !== processIName(finalExtendsName) ||
1012+
!allInterfacesMap[finalExtendsName] ||
1013+
allInterfacesMap[finalExtendsName]["no-interface-object"]) {
1014+
finalImplements.unshift(processIName(finalExtendsName));
1015+
finalExtendsName = null;
1016+
}
1017+
}
1018+
1019+
1020+
if (i.comment) {
1021+
printer.printLine(`/** ${i.comment} */`);
1022+
}
1023+
1024+
if (finalImplements.length) {
1025+
printer.printLine(`interface ${getNameWithTypeParameter(i, i.name)} extends ${finalImplements.join(", ")} {}`);
1026+
}
1027+
1028+
printer.print(`${prefix}class ${getNameWithTypeParameter(i, i.name)}`);
1029+
if (finalExtendsName) {
1030+
printer.print(` ${extendsKeyword} ${finalExtendsName}`)
1031+
}
1032+
printer.print(" {");
1033+
printer.endLine();
1034+
}
1035+
1036+
function emitClass(i: Browser.Interface, prefix = "") {
1037+
printer.clearStack();
1038+
emitInterfaceEventMap(i);
1039+
1040+
emitClassDeclaration(i, prefix);
1041+
printer.increaseIndent();
1042+
emitConstructorSignature(i, "constructor");
1043+
1044+
emitMembers(/*prefix*/ "", EmitScope.InstanceOnly, i);
1045+
emitConstants(i);
1046+
emitEventHandlers(/*prefix*/ "", i);
1047+
emitIndexers(EmitScope.InstanceOnly, i);
1048+
1049+
emitMembers(/*prefix*/ "static ", EmitScope.StaticOnly, i);
1050+
emitConstants(i, /*prefix*/ "static ");
1051+
if (iNameToConstParents[i.name] && iNameToConstParents[i.name].length) {
1052+
for (const parent of iNameToConstParents[i.name]) {
1053+
emitConstants(parent, /*prefix*/ "static ");
1054+
}
1055+
}
1056+
1057+
printer.decreaseIndent();
1058+
printer.printLine("}");
1059+
printer.printLine("");
1060+
1061+
if (flavor === Flavor.Web && i["legacy-window-alias"]) {
1062+
for (const alias of i["legacy-window-alias"]!) {
1063+
printer.printLine(`type ${alias} = ${i.name};`);
1064+
printer.printLine(`declare var ${alias}: typeof ${i.name};`);
1065+
printer.printLine("");
1066+
}
1067+
}
1068+
}
1069+
9891070
function emitStaticInterface(i: Browser.Interface) {
9901071
// Some types are static types with non-static members. For example,
9911072
// NodeFilter is a static method itself, however it has an "acceptNode" method
@@ -1053,6 +1134,9 @@ export function emitWebIdl(webidl: Browser.WebIdl, flavor: Flavor) {
10531134
else if (i["no-interface-object"]) {
10541135
emitInterface(i);
10551136
}
1137+
else if (processIName(i.name) === i.name) {
1138+
emitClass(i, "declare ");
1139+
}
10561140
else {
10571141
emitInterface(i);
10581142
emitConstructor(i, "declare ");
@@ -1080,8 +1164,13 @@ export function emitWebIdl(webidl: Browser.WebIdl, flavor: Flavor) {
10801164
namespace.nested.interfaces
10811165
.sort(compareName)
10821166
.forEach(i => {
1083-
emitInterface(i);
1084-
emitConstructor(i);
1167+
if (processIName(i.name) === i.name) {
1168+
emitClass(i)
1169+
}
1170+
else {
1171+
emitInterface(i);
1172+
emitConstructor(i);
1173+
}
10851174
});
10861175
namespace.nested.dictionaries
10871176
.sort(compareName)

0 commit comments

Comments
 (0)