Skip to content

Commit 32f39c1

Browse files
committed
Resolve #39 Overload hook.Add with hook names and callback types
1 parent 4cc8e27 commit 32f39c1

File tree

4 files changed

+119
-10
lines changed

4 files changed

+119
-10
lines changed

__tests__/api-writer/plugins.spec.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { GluaApiWriter } from '../../src/api-writer/glua-api-writer';
2+
import { LibraryFunction } from '../../src/scrapers/wiki-page-markup-scraper';
3+
4+
describe('plugins', () => {
5+
it('should write plugin annotations', async () => {
6+
const writer = new GluaApiWriter();
7+
const api = writer.writePage(<LibraryFunction>{
8+
name: 'Add',
9+
address: 'hook.Add',
10+
parent: 'hook',
11+
dontDefineParent: true,
12+
description: '',
13+
realm: 'shared',
14+
type: 'libraryfunc',
15+
url: 'na',
16+
arguments: [
17+
{
18+
args: [{
19+
name: 'intensity',
20+
type: 'number',
21+
description: 'The intensity of the explosion.',
22+
default: '1000',
23+
}]
24+
}
25+
],
26+
returns: [
27+
{
28+
type: 'number',
29+
description: 'The amount of damage done.',
30+
},
31+
],
32+
});
33+
34+
expect(api).toContain('---@overload fun(eventName: "Move", identifier: any, func: fun(ply: Player, mv: CMoveData):(boolean?))');
35+
});
36+
});

custom/plugins/hook-add.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { GluaApiWriter } from "../../src/api-writer/glua-api-writer";
2+
import { Function, HookFunction } from "../../src/scrapers/wiki-page-markup-scraper";
3+
import path from 'path';
4+
import fs from 'fs';
5+
6+
export default function plugin(writer: GluaApiWriter, func: Function) {
7+
// let hookAnnotations = '---@overload fun(eventName: "Move", identifier: any, func: fun(ply: Player, mv: CMoveData): boolean?)\n';
8+
let hookAnnotations = '';
9+
10+
// Iterate writer.outputDirectory to find all hooks in gm/ that have type "hook"
11+
const hookFiles = path.join(writer.outputDirectory, 'gm');
12+
const hookFilesList = fs.readdirSync(hookFiles, { withFileTypes: true })
13+
.filter(dirent => dirent.isFile() && dirent.name.endsWith('.json'))
14+
.map(dirent => dirent.name);
15+
16+
// Iterate all files and parse them as JSON, only those with type "hook" are considered
17+
for (const file of hookFilesList) {
18+
const filePath = path.join(writer.outputDirectory, 'gm', file);
19+
const fileContent = fs.readFileSync(filePath, 'utf8');
20+
const fileJson = JSON.parse(fileContent)[0] as HookFunction;
21+
22+
// Check if the function is a hook
23+
if (fileJson.type !== 'hook') {
24+
continue;
25+
}
26+
27+
// Add the hook annotation to the hookAnnotations string
28+
let args = '';
29+
30+
if (fileJson.arguments) {
31+
for (const arg of fileJson.arguments) {
32+
if (arg.args) {
33+
for (const argItem of arg.args) {
34+
const argType = GluaApiWriter.transformType(argItem.type);
35+
args += `${argItem.name}: ${argType}, `;
36+
}
37+
}
38+
}
39+
40+
// Remove the last comma and space
41+
args = args.slice(0, -2);
42+
}
43+
44+
let returns = '';
45+
if (fileJson.returns) {
46+
for (const ret of fileJson.returns) {
47+
const retType = GluaApiWriter.transformType(ret.type);
48+
returns += `${retType}, `;
49+
}
50+
51+
// Remove the last comma and space
52+
returns = returns.slice(0, -2);
53+
54+
if (returns !== '') {
55+
// We force the return type to be optional, since hooks should only return a value if they want to
56+
returns = `:(${returns}?)`;
57+
}
58+
}
59+
60+
// Create the overload
61+
hookAnnotations += `---@overload fun(eventName: "${fileJson.name}", identifier: any, func: fun(${args})${returns})\n`;
62+
}
63+
64+
return hookAnnotations;
65+
}

src/api-writer/glua-api-writer.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
isStruct,
1111
isEnum,
1212
} from '../scrapers/wiki-page-markup-scraper.js';
13+
import customPluginHookAdd from '../../custom/plugins/hook-add.js';
1314
import fs from 'fs';
1415

1516
export const RESERVERD_KEYWORDS = new Set([
@@ -50,7 +51,9 @@ export class GluaApiWriter {
5051

5152
private readonly files: Map<string, IndexedWikiPage[]> = new Map();
5253

53-
constructor() { }
54+
constructor(
55+
public readonly outputDirectory: string = './output',
56+
) { }
5457

5558
public static safeName(name: string) {
5659
if (name.includes('/'))
@@ -527,6 +530,12 @@ export class GluaApiWriter {
527530
if (func.deprecated)
528531
luaDocComment += `---@deprecated ${removeNewlines(func.deprecated)}\n`;
529532

533+
// TODO: Write a nice API to allow customizing API output from custom/
534+
// See https://github.com/luttje/glua-api-snippets/issues/65
535+
if (func.address === 'hook.Add') {
536+
luaDocComment += customPluginHookAdd(this, func);
537+
}
538+
530539
return luaDocComment;
531540
}
532541

src/cli-scraper.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ async function startScrape() {
3232
const customDirectory = options.customOverrides?.replace(/\/$/, '') ?? null;
3333
const baseUrl = options.url.replace(/\/$/, '');
3434
const pageListScraper = new WikiPageListScraper(`${baseUrl}/~pagelist?format=json`);
35-
const writer = new GluaApiWriter();
35+
const writer = new GluaApiWriter(baseDirectory);
3636

3737
const retryOptions: RequestInitWithRetry = {
3838
retries: 5,
39-
retryDelay: function(attempt, error, response) {
39+
retryDelay: function (attempt, error, response) {
4040
return Math.pow(2, attempt) * 500; // 500, 1000, 2000, 4000, 8000
4141
}
4242
}
@@ -85,7 +85,7 @@ async function startScrape() {
8585

8686
const pageIndexes = await scrapeAndCollect(pageListScraper);
8787

88-
console.log(`Took ${Math.floor((performance.now()-collect_start) / 100) / 10}s!\n`);
88+
console.log(`Took ${Math.floor((performance.now() - collect_start) / 100) / 10}s!\n`);
8989

9090
console.log('Scraping all pages...');
9191
let scrape_start = performance.now();
@@ -109,7 +109,7 @@ async function startScrape() {
109109
}
110110

111111
fileName = fileName.replace(/[^a-z0-9]/gi, '_').toLowerCase();
112-
112+
113113
// Make sure modules like Entity and ENTITY are placed in the same file.
114114
moduleName = moduleName.toLowerCase();
115115

@@ -132,10 +132,9 @@ async function startScrape() {
132132

133133
queue.push(pageMarkupScraper.scrape());
134134

135-
if (queue.length > 20)
136-
{
135+
if (queue.length > 20) {
137136
const results = await Promise.allSettled(queue);
138-
for ( const result of results) {
137+
for (const result of results) {
139138
if (result.status === "rejected") console.warn("Failed to scrape a page!", result.reason);
140139
}
141140
queue = [];
@@ -144,11 +143,11 @@ async function startScrape() {
144143

145144
// Await any after the loop exits
146145
const results = await Promise.allSettled(queue);
147-
for ( const result of results) {
146+
for (const result of results) {
148147
if (result.status === "rejected") console.warn("Failed to scrape a page!", result.reason);
149148
}
150149

151-
console.log(`Took ${Math.floor((performance.now()-scrape_start) / 100) / 10}s!`);
150+
console.log(`Took ${Math.floor((performance.now() - scrape_start) / 100) / 10}s!`);
152151

153152
writer.writeToDisk();
154153

0 commit comments

Comments
 (0)