Skip to content

Commit 292df94

Browse files
authored
Merge TypeScript interface declarations when merging code (#159)
Fixes #151.
1 parent 6da5e9b commit 292df94

File tree

12 files changed

+177
-104
lines changed

12 files changed

+177
-104
lines changed

tools/adventure-pack/goodies/typescript/Array.prototype.slidingWindows/index.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,11 @@ class ArraySlice<T> {
2222
return this.array[this.start + adjustedIndex];
2323
}
2424

25-
[Symbol.iterator] = function* (
26-
this: ArraySlice<T>,
27-
): Generator<T, void, void> {
25+
*[Symbol.iterator](this: ArraySlice<T>): Generator<T, void, void> {
2826
for (let i = this.start; i <= this.end; ++i) {
2927
yield this.array[i];
3028
}
31-
};
29+
}
3230

3331
slide(delta: number = 1): IndexableArraySlice<T> {
3432
return ArraySlice.get(this.array, this.start + delta, this.end + delta);

tools/adventure-pack/src/app/__tests__/__snapshots__/equip-test.ts.snap

Lines changed: 28 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,11 @@ class ArraySlice {
119119
return this.array[this.start + adjustedIndex];
120120
}
121121
122-
[Symbol.iterator] = function* () {
122+
*[Symbol.iterator]() {
123123
for (let i = this.start; i <= this.end; ++i) {
124124
yield this.array[i];
125125
}
126-
};
126+
}
127127
128128
slide(delta = 1) {
129129
return ArraySlice.get(this.array, this.start + delta, this.end + delta);
@@ -907,14 +907,14 @@ exports[`App can equip single goody: TypeScript Array.prototype.slidingWindows 1
907907
// Running at: https://example.com/
908908
909909
declare global {
910-
interface ReadonlyArray<T> {
910+
interface Array<T> {
911911
slidingWindows(
912912
this: ReadonlyArray<T>,
913913
windowSize: number,
914914
): Generator<ArraySlice<T>, void, void>;
915915
}
916916
917-
interface Array<T> {
917+
interface ReadonlyArray<T> {
918918
slidingWindows(
919919
this: ReadonlyArray<T>,
920920
windowSize: number,
@@ -946,13 +946,11 @@ class ArraySlice<T> {
946946
return this.array[this.start + adjustedIndex];
947947
}
948948
949-
[Symbol.iterator] = function* (
950-
this: ArraySlice<T>,
951-
): Generator<T, void, void> {
949+
*[Symbol.iterator](this: ArraySlice<T>): Generator<T, void, void> {
952950
for (let i = this.start; i <= this.end; ++i) {
953951
yield this.array[i];
954952
}
955-
};
953+
}
956954
957955
slide(delta: number = 1): IndexableArraySlice<T> {
958956
return ArraySlice.get(this.array, this.start + delta, this.end + delta);
@@ -1180,15 +1178,13 @@ declare global {
11801178
returnThis<T>(this: T): T;
11811179
}
11821180
1183-
interface Iterator<T> {
1184-
toIterable(this: Iterator<T>): IterableIterator<T>;
1185-
}
1186-
11871181
interface Iterator<T> {
11881182
every(
11891183
this: Iterator<T>,
11901184
callbackfn: (value: T, index: number) => unknown,
11911185
): boolean;
1186+
1187+
toIterable(this: Iterator<T>): IterableIterator<T>;
11921188
}
11931189
}
11941190
@@ -1235,15 +1231,13 @@ declare global {
12351231
returnThis<T>(this: T): T;
12361232
}
12371233
1238-
interface Iterator<T> {
1239-
toIterable(this: Iterator<T>): IterableIterator<T>;
1240-
}
1241-
12421234
interface Iterator<T> {
12431235
filter(
12441236
this: Iterator<T>,
12451237
callbackfn: (value: T, index: number) => unknown,
12461238
): Generator<T, void, void>;
1239+
1240+
toIterable(this: Iterator<T>): IterableIterator<T>;
12471241
}
12481242
}
12491243
@@ -1289,15 +1283,13 @@ declare global {
12891283
returnThis<T>(this: T): T;
12901284
}
12911285
1292-
interface Iterator<T> {
1293-
toIterable(this: Iterator<T>): IterableIterator<T>;
1294-
}
1295-
12961286
interface Iterator<T> {
12971287
find(
12981288
this: Iterator<T>,
12991289
callbackFn: (element: T, index: number) => boolean,
13001290
): T | undefined;
1291+
1292+
toIterable(this: Iterator<T>): IterableIterator<T>;
13011293
}
13021294
}
13031295
@@ -1344,15 +1336,13 @@ declare global {
13441336
returnThis<T>(this: T): T;
13451337
}
13461338
1347-
interface Iterator<T> {
1348-
toIterable(this: Iterator<T>): IterableIterator<T>;
1349-
}
1350-
13511339
interface Iterator<T> {
13521340
forEach(
13531341
this: Iterator<T>,
13541342
callbackFn: (element: T, index: number) => void,
13551343
): void;
1344+
1345+
toIterable(this: Iterator<T>): IterableIterator<T>;
13561346
}
13571347
}
13581348
@@ -1396,15 +1386,13 @@ declare global {
13961386
returnThis<T>(this: T): T;
13971387
}
13981388
1399-
interface Iterator<T> {
1400-
toIterable(this: Iterator<T>): IterableIterator<T>;
1401-
}
1402-
14031389
interface Iterator<T> {
14041390
map<TOut>(
14051391
this: Iterator<T>,
14061392
callbackFn: (element: T, index: number) => TOut,
14071393
): Generator<TOut, void, void>;
1394+
1395+
toIterable(this: Iterator<T>): IterableIterator<T>;
14081396
}
14091397
}
14101398
@@ -1448,16 +1436,14 @@ declare global {
14481436
returnThis<T>(this: T): T;
14491437
}
14501438
1451-
interface Iterator<T> {
1452-
toIterable(this: Iterator<T>): IterableIterator<T>;
1453-
}
1454-
14551439
interface Iterator<T> {
14561440
max(
14571441
this: Iterator<T>,
14581442
compareFn?: (a: T, b: T) => number,
14591443
options?: { nanBehavior?: "avoid" | "compare" },
14601444
): T | undefined;
1445+
1446+
toIterable(this: Iterator<T>): IterableIterator<T>;
14611447
}
14621448
}
14631449
@@ -1535,16 +1521,14 @@ declare global {
15351521
returnThis<T>(this: T): T;
15361522
}
15371523
1538-
interface Iterator<T> {
1539-
toIterable(this: Iterator<T>): IterableIterator<T>;
1540-
}
1541-
15421524
interface Iterator<T> {
15431525
min(
15441526
this: Iterator<T>,
15451527
compareFn?: (a: T, b: T) => number,
15461528
options?: { nanBehavior?: "avoid" | "compare" },
15471529
): T | undefined;
1530+
1531+
toIterable(this: Iterator<T>): IterableIterator<T>;
15481532
}
15491533
}
15501534
@@ -1622,15 +1606,13 @@ declare global {
16221606
returnThis<T>(this: T): T;
16231607
}
16241608
1625-
interface Iterator<T> {
1626-
toIterable(this: Iterator<T>): IterableIterator<T>;
1627-
}
1628-
16291609
interface Iterator<T> {
16301610
some(
16311611
this: Iterator<T>,
16321612
callbackfn: (value: T, index: number) => unknown,
16331613
): boolean;
1614+
1615+
toIterable(this: Iterator<T>): IterableIterator<T>;
16341616
}
16351617
}
16361618
@@ -1677,12 +1659,10 @@ declare global {
16771659
returnThis<T>(this: T): T;
16781660
}
16791661
1680-
interface Iterator<T> {
1681-
toIterable(this: Iterator<T>): IterableIterator<T>;
1682-
}
1683-
16841662
interface Iterator<T> {
16851663
toArray(this: Iterator<T>): T[];
1664+
1665+
toIterable(this: Iterator<T>): IterableIterator<T>;
16861666
}
16871667
}
16881668
@@ -1814,14 +1794,14 @@ exports[`App can equip single goody: TypeScript Number.prototype.digits 1`] = `
18141794
// Running at: https://example.com/
18151795
18161796
declare global {
1817-
interface NumberConstructor {
1818-
isIntegerOrIntegerObject(num: unknown): boolean;
1819-
}
1820-
18211797
interface Number {
18221798
digits(this: Number): Generator<number, void, void>;
18231799
digits(this: Number, radix: number): Generator<number, void, void>;
18241800
}
1801+
1802+
interface NumberConstructor {
1803+
isIntegerOrIntegerObject(num: unknown): boolean;
1804+
}
18251805
}
18261806
18271807
Number.isIntegerOrIntegerObject = function (num: unknown): boolean {

tools/adventure-pack/src/app/__tests__/__snapshots__/render-test.ts.snap

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,11 @@ exports[`App can render goody: JavaScript Array.prototype.slidingWindows 1`] = `
8989
return this.array[this.start + adjustedIndex];
9090
}
9191
92-
[Symbol.iterator] = function* () {
92+
*[Symbol.iterator]() {
9393
for (let i = this.start; i <= this.end; ++i) {
9494
yield this.array[i];
9595
}
96-
};
96+
}
9797
9898
slide(delta = 1) {
9999
return ArraySlice.get(this.array, this.start + delta, this.end + delta);
@@ -560,14 +560,14 @@ del set_up_adventure_pack"
560560
561561
exports[`App can render goody: TypeScript Array.prototype.slidingWindows 1`] = `
562562
"declare global {
563-
interface ReadonlyArray<T> {
563+
interface Array<T> {
564564
slidingWindows(
565565
this: ReadonlyArray<T>,
566566
windowSize: number,
567567
): Generator<ArraySlice<T>, void, void>;
568568
}
569569
570-
interface Array<T> {
570+
interface ReadonlyArray<T> {
571571
slidingWindows(
572572
this: ReadonlyArray<T>,
573573
windowSize: number,
@@ -599,13 +599,11 @@ class ArraySlice<T> {
599599
return this.array[this.start + adjustedIndex];
600600
}
601601
602-
[Symbol.iterator] = function* (
603-
this: ArraySlice<T>,
604-
): Generator<T, void, void> {
602+
*[Symbol.iterator](this: ArraySlice<T>): Generator<T, void, void> {
605603
for (let i = this.start; i <= this.end; ++i) {
606604
yield this.array[i];
607605
}
608-
};
606+
}
609607
610608
slide(delta: number = 1): IndexableArraySlice<T> {
611609
return ArraySlice.get(this.array, this.start + delta, this.end + delta);

tools/adventure-pack/src/app/goodyToText.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Goody } from "./Goody";
22
import { mergeJavaCode } from "./mergeJavaCode";
3+
import { stringifyTypeScriptModuleDeclarations } from "./stringifyTypeScriptModuleDeclarations";
34

45
export function goodyToText(goody: Goody): string {
56
switch (goody.language) {
@@ -28,14 +29,14 @@ export function goodyToText(goody: Goody): string {
2829
return goody.code.trim();
2930
}
3031
case "typescript": {
32+
const moduleDeclarations = stringifyTypeScriptModuleDeclarations(
33+
goody.moduleDeclarations,
34+
);
35+
3136
return (
3237
goody.imports.map((im) => `import ${JSON.stringify(im)};\n`).join("") +
3338
"\n" +
34-
(goody.globalModuleDeclarations.length > 0
35-
? `declare global {\n ${goody.globalModuleDeclarations
36-
.join("\n\n")
37-
.trim()}\n}\n\n`
38-
: "") +
39+
(moduleDeclarations.length > 0 ? moduleDeclarations + "\n\n" : "") +
3940
goody.code
4041
).trim();
4142
}

tools/adventure-pack/src/app/mergeCode.ts

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import type { Goody } from "./Goody";
1010
import type { Language } from "./Language";
1111
import { mergeJavaCode } from "./mergeJavaCode";
1212
import type { JavaGoody } from "./parsers/javaGoodyParser";
13+
import { sortTypeScriptModuleAndInterfaceDeclarations } from "./sortTypeScriptModuleAndInterfaceDeclarations";
14+
import { stringifyTypeScriptModuleDeclarations } from "./stringifyTypeScriptModuleDeclarations";
1315

1416
function topo({
1517
goodies,
@@ -110,19 +112,37 @@ export function mergeCode({
110112
);
111113
}
112114

113-
const globalModuleDeclarations =
114-
language === "typescript"
115-
? orderedGoodies.flatMap((goody) =>
116-
goody.language === "typescript"
117-
? goody.globalModuleDeclarations
118-
: [],
119-
)
120-
: [];
115+
const moduleDeclarations = (() => {
116+
if (language !== "typescript") {
117+
return "";
118+
}
119+
120+
const mergedDeclarations: Record<string, Record<string, string[]>> = {};
121+
for (const goody of orderedGoodies) {
122+
invariant(
123+
goody.language === "typescript",
124+
"Goody language must match language!",
125+
);
126+
127+
for (const [moduleName, interfaceDeclarations] of Object.entries(
128+
goody.moduleDeclarations,
129+
)) {
130+
for (const [interfaceName, codeGroups] of Object.entries(
131+
interfaceDeclarations,
132+
)) {
133+
((mergedDeclarations[moduleName] ??= {})[interfaceName] ??=
134+
[]).push(...codeGroups);
135+
}
136+
}
137+
}
138+
139+
return stringifyTypeScriptModuleDeclarations(
140+
sortTypeScriptModuleAndInterfaceDeclarations(mergedDeclarations),
141+
);
142+
})();
121143

122144
return [
123-
globalModuleDeclarations.length > 0
124-
? `declare global {\n${globalModuleDeclarations.join("\n\n")}\n}`
125-
: "",
145+
moduleDeclarations,
126146

127147
...orderedGoodies.map((goody) => {
128148
invariant(

tools/adventure-pack/src/app/parsers/typeScriptGoodyParser.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ import { nonBlankStringParser } from "./nonBlankStringParser";
66

77
export const typeScriptGoodyParser = goodyBaseParser.extend({
88
code: nonBlankStringParser,
9-
// TODO: generalize to simply declarations
10-
globalModuleDeclarations: z.array(nonBlankStringParser),
9+
moduleDeclarations: z.record(
10+
z.string(),
11+
z.record(z.string(), z.array(nonBlankStringParser)),
12+
),
1113
language: z.literal("typescript"),
1214
});
1315

0 commit comments

Comments
 (0)