From fd02c9d9f1f844e5020869678c51dab1978728b6 Mon Sep 17 00:00:00 2001 From: Nikita Samsonau Date: Fri, 8 May 2020 00:26:29 -0400 Subject: [PATCH 01/11] Generate proper array return types --- src/generators/typeDefinitions.ts | 37 ++++++++++++++++++++++++++----- src/templates/samp.d.ts.hjs | 2 +- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/generators/typeDefinitions.ts b/src/generators/typeDefinitions.ts index ede509a..fdef8fb 100644 --- a/src/generators/typeDefinitions.ts +++ b/src/generators/typeDefinitions.ts @@ -46,14 +46,41 @@ Handlebars.registerHelper({ ppd: (pd: IPawnDoc, pa: IParam) => pd.param?.find(pdpa => pdpa.name === pa.name)?.description, // typescript type for specifier tts: (t: IParam['type'], b = false) => { - let r: string; - if (t === 's') r = 'string'; - else if (t === 'a' || t === 'v') r = 'Array'; - else r = 'number'; - return b ? `{${r}}` : r; + const type = referenceToTsType(t); + return b ? `{${type}}` : type; }, + outputtype: (params: Array) => { + const types = params.filter(p => p.isReference).map(r => referenceToTsType(r.type)); + if (!types.length) { + return "number"; + } + if (types.length === 1) { + return types[0]; + } + return `[${types.join(", ")}]`; + } }); +function referenceToTsType(t: IParam['type']) { + switch (t) { + case 's': + case 'S': + return "string"; + case 'f': + case 'F': + case 'd': + case 'D': + case 'i': + case 'I': + return "number"; + case 'a': + case 'A': + case 'v': + case 'V': + return "Array"; + } +} + export const generate = async () => { const generating = ora('Generating type definitions...').start(); diff --git a/src/templates/samp.d.ts.hjs b/src/templates/samp.d.ts.hjs index b90eb34..b40f569 100644 --- a/src/templates/samp.d.ts.hjs +++ b/src/templates/samp.d.ts.hjs @@ -181,7 +181,7 @@ declare class samp { * @returns {*} {{/if}} */ - static callNative{{#if this.returnsFloat}}Float{{/if}}(nativeName: '{{this.name}}', paramTypes: '{{svs this.params}}'{{#if (gt (svwl this.params) 0)}}, {{#each (svw this.params)}}{{this.name}}: {{tts this.type false}}{{#if @last}}{{else}}, {{/if}}{{/each}}{{/if}}): {{#if (gt (rvl this.params) 1)}}Array{{else if (eq (rvs this.params) 'S')}}string{{else}}number{{/if}}; + static callNative{{#if this.returnsFloat}}Float{{/if}}(nativeName: '{{this.name}}', paramTypes: '{{svs this.params}}'{{#if (gt (svwl this.params) 0)}}, {{#each (svw this.params)}}{{this.name}}: {{tts this.type false}}{{#if @last}}{{else}}, {{/if}}{{/each}}{{/if}}): {{outputtype this.params}}; {{/unless}} {{/each}} {{/each}} From ad8f4aeaf05b2619fc157f6bfdd675ea04b2cd9d Mon Sep 17 00:00:00 2001 From: Nikita Samsonau Date: Fri, 8 May 2020 01:24:35 -0400 Subject: [PATCH 02/11] Rearrange some code --- .gitignore | 3 + src/docsStore.ts | 62 +++++++++++ src/enums/paths.ts | 7 +- src/generators/globals.ts | 2 +- src/generators/handlebarsHelper.ts | 68 +++++++++++++ src/generators/index.ts | 7 +- src/generators/typeDefinitions.ts | 158 +++++------------------------ src/index.ts | 13 ++- src/templates/samp.d.ts.hjs | 2 +- 9 files changed, 177 insertions(+), 145 deletions(-) create mode 100644 src/docsStore.ts create mode 100644 src/generators/handlebarsHelper.ts diff --git a/.gitignore b/.gitignore index 1da96fa..b2c43af 100644 --- a/.gitignore +++ b/.gitignore @@ -107,4 +107,7 @@ dist # TernJS port file .tern-port +# VS code +.vscode/ + .DS_Store diff --git a/src/docsStore.ts b/src/docsStore.ts new file mode 100644 index 0000000..96aed3f --- /dev/null +++ b/src/docsStore.ts @@ -0,0 +1,62 @@ +import { IParsed } from "./interfaces"; +import { fetchSampInc, fetchActorInc, fetchHttpInc, fetchObjectsInc, fetchPlayersInc, fetchSampDBInc, fetchVehiclesInc } from './requests'; +import { parseInclude } from './parser'; + +export class DocsStore { + private constructor( + public readonly a_samp: IParsed, + public readonly a_actor: IParsed, + public readonly a_http: IParsed, + // public readonly a_npc: IParsed, + public readonly a_objects: IParsed, + public readonly a_players: IParsed, + public readonly a_sampdb: IParsed, + public readonly a_vehicles: IParsed, + ) {} + + public static async fromSampStdlib() { + // A_SAMP + const a_samp = await fetchSampInc(); + const a_sampParsed = await parseInclude(a_samp, true, true); + + // A_ACTOR + const a_actor = await fetchActorInc(); + const a_actorParsed = await parseInclude(a_actor, true, true); + + // A_HTTP + const a_http = await fetchHttpInc(); + const a_httpParsed = await parseInclude(a_http, true, true); + + // A_NPC + // const a_npc = await fetchNPCInc(); + // a_npcParsed = await parseInclude(a_npc, false, true); + // Removed because it contains most of the things a_samp already has and you shouldn't include both in a pawn gamemode either + + // A_OBJECTS + const a_objects = await fetchObjectsInc(); + const a_objectsParsed = await parseInclude(a_objects, true, true); + + // A_PLAYERS + const a_players = await fetchPlayersInc(); + const a_playersParsed = await parseInclude(a_players, true, true); + + // A_SAMPDB + const a_sampdb = await fetchSampDBInc(); + const a_sampdbParsed = await parseInclude(a_sampdb, false, true); + + // A_VEHICLES + const a_vehicles = await fetchVehiclesInc(); + const a_vehiclesParsed = await parseInclude(a_vehicles, true, true); + + return new DocsStore( + a_sampParsed, + a_actorParsed, + a_httpParsed, + // a_npcParsed, + a_objectsParsed, + a_playersParsed, + a_sampdbParsed, + a_vehiclesParsed, + ); + } +} \ No newline at end of file diff --git a/src/enums/paths.ts b/src/enums/paths.ts index c931a71..5084ad5 100644 --- a/src/enums/paths.ts +++ b/src/enums/paths.ts @@ -2,7 +2,8 @@ export enum EPaths { TEMPLATE_EVENTS = './src/templates/events.d.ts.hjs', TEMPLATE_SAMP = './src/templates/samp.d.ts.hjs', TEMPLATE_GLOBALS = './src/templates/globals.d.ts.hjs', - GENERATED_EVENTS = './generated/events.d.ts', - GENERATED_SAMP = './generated/samp.d.ts', - GENERATED_GLOBALS = './generated/globals.d.ts', + TEMPLATE_WRAPPERS = './src/templates/wrappers.ts.hjs', + GENERATED_EVENT_TYPES = './generated/events.d.ts', + GENERATED_SAMP_TYPES = './generated/samp.d.ts', + GENERATED_GLOBAL_TYPES = './generated/globals.d.ts', } diff --git a/src/generators/globals.ts b/src/generators/globals.ts index 91b200c..a678dd8 100644 --- a/src/generators/globals.ts +++ b/src/generators/globals.ts @@ -16,7 +16,7 @@ export const generateGlobalConstants = async () => { }; const template = Handlebars.compile(await fs.readFile(EPaths.TEMPLATE_GLOBALS, 'utf8')); - await fs.outputFile(EPaths.GENERATED_GLOBALS, template({ globalConstants })); + await fs.outputFile(EPaths.GENERATED_GLOBAL_TYPES, template({ globalConstants })); }; export const applyFixes = () => {}; // Apply fixes to generated code diff --git a/src/generators/handlebarsHelper.ts b/src/generators/handlebarsHelper.ts new file mode 100644 index 0000000..4d92c79 --- /dev/null +++ b/src/generators/handlebarsHelper.ts @@ -0,0 +1,68 @@ +import { IParam, IPawnDoc } from '../interfaces'; +import Handlebars from 'handlebars'; + +export function initHandlerbars() { + Handlebars.registerHelper({ + eq: (v1, v2) => v1 === v2, + ne: (v1, v2) => v1 !== v2, + lt: (v1, v2) => v1 < v2, + gt: (v1, v2) => v1 > v2, + lte: (v1, v2) => v1 <= v2, + gte: (v1, v2) => v1 >= v2, + and: (v1, v2) => v1 && v2, + or: (v1, v2) => v1 || v2, + }); + + Handlebars.registerHelper({ + // has variadic params + hvp: (pa: Array) => pa.some(p => p.isVariadic), + // specifier values string + svs: (pa: Array) => pa.map(p => p.isReference ? p.type.toUpperCase() : p.type).join(''), + // specifier values without references + svw: (pa: Array) => pa.filter(p => !p.isReference), + // specifier values without references length + svwl: (pa: Array) => pa.filter(p => !p.isReference).length, + // reference values string + rvs: (pa: Array) => pa.filter(p => p.isReference).map(p => p.type.toUpperCase()).join(''), + // reference values length + rvl: (pa: Array) => pa.filter(p => p.isReference).length, + + // pawndoc param description + ppd: (pd: IPawnDoc, pa: IParam) => pd.param?.find(pdpa => pdpa.name === pa.name)?.description, + // typescript type for specifier + tts: (t: IParam['type'], b = false) => { + const type = referenceToTsType(t); + return b ? `{${type}}` : type; + }, + outputtype: (params: Array) => { + const types = params.filter(p => p.isReference).map(r => referenceToTsType(r.type)); + if (!types.length) { + return "number"; + } + if (types.length === 1) { + return types[0]; + } + return `[${types.join(", ")}]`; + } + }); +} + +function referenceToTsType(t: IParam['type']) { + switch (t) { + case 's': + case 'S': + return "string"; + case 'f': + case 'F': + case 'd': + case 'D': + case 'i': + case 'I': + return "number"; + case 'a': + case 'A': + case 'v': + case 'V': + return "Array"; + } +} diff --git a/src/generators/index.ts b/src/generators/index.ts index b5178b0..a325c6f 100644 --- a/src/generators/index.ts +++ b/src/generators/index.ts @@ -1,4 +1,3 @@ -import * as typeDefinitions from './typeDefinitions'; -import * as globals from './globals'; - -export { typeDefinitions, globals }; +export * as typeDefinitions from './typeDefinitions'; +export * as globals from './globals'; +export * as handlebarsHelper from './handlebarsHelper'; diff --git a/src/generators/typeDefinitions.ts b/src/generators/typeDefinitions.ts index fdef8fb..f04d6c1 100644 --- a/src/generators/typeDefinitions.ts +++ b/src/generators/typeDefinitions.ts @@ -2,141 +2,33 @@ import fs from 'fs-extra'; import Handlebars from 'handlebars'; import { constantCase } from 'constant-case'; import ora from 'ora'; - -import { fetchSampInc, fetchActorInc, fetchHttpInc, fetchObjectsInc, fetchPlayersInc, fetchSampDBInc, fetchVehiclesInc } from '../requests'; -import { parseInclude } from '../parser'; -import { IParsed, IParam, IPawnDoc } from '../interfaces'; +import { IParsed } from '../interfaces'; import { EPaths } from '../enums'; +import { DocsStore } from '../docsStore'; -let a_sampParsed: IParsed; -let a_actorParsed: IParsed; -let a_httpParsed: IParsed; -// let a_npcParsed: IParsed; -let a_objectsParsed: IParsed; -let a_playersParsed: IParsed; -let a_sampdbParsed: IParsed; -let a_vehiclesParsed: IParsed; - -Handlebars.registerHelper({ - eq: (v1, v2) => v1 === v2, - ne: (v1, v2) => v1 !== v2, - lt: (v1, v2) => v1 < v2, - gt: (v1, v2) => v1 > v2, - lte: (v1, v2) => v1 <= v2, - gte: (v1, v2) => v1 >= v2, - and: (v1, v2) => v1 && v2, - or: (v1, v2) => v1 || v2, -}); - -Handlebars.registerHelper({ - // has variadic params - hvp: (pa: Array) => pa.some(p => p.isVariadic), - // specifier values string - svs: (pa: Array) => pa.map(p => p.isReference ? p.type.toUpperCase() : p.type).join(''), - // specifier values without references - svw: (pa: Array) => pa.filter(p => !p.isReference), - // specifier values without references length - svwl: (pa: Array) => pa.filter(p => !p.isReference).length, - // reference values string - rvs: (pa: Array) => pa.filter(p => p.isReference).map(p => p.type.toUpperCase()).join(''), - // reference values length - rvl: (pa: Array) => pa.filter(p => p.isReference).length, - - // pawndoc param description - ppd: (pd: IPawnDoc, pa: IParam) => pd.param?.find(pdpa => pdpa.name === pa.name)?.description, - // typescript type for specifier - tts: (t: IParam['type'], b = false) => { - const type = referenceToTsType(t); - return b ? `{${type}}` : type; - }, - outputtype: (params: Array) => { - const types = params.filter(p => p.isReference).map(r => referenceToTsType(r.type)); - if (!types.length) { - return "number"; - } - if (types.length === 1) { - return types[0]; - } - return `[${types.join(", ")}]`; - } -}); - -function referenceToTsType(t: IParam['type']) { - switch (t) { - case 's': - case 'S': - return "string"; - case 'f': - case 'F': - case 'd': - case 'D': - case 'i': - case 'I': - return "number"; - case 'a': - case 'A': - case 'v': - case 'V': - return "Array"; - } -} - -export const generate = async () => { +export const generate = async (docsStore: DocsStore) => { const generating = ora('Generating type definitions...').start(); - // A_SAMP - const a_samp = await fetchSampInc(); - a_sampParsed = await parseInclude(a_samp, true, true); - - // A_ACTOR - const a_actor = await fetchActorInc(); - a_actorParsed = await parseInclude(a_actor, true, true); - - // A_HTTP - const a_http = await fetchHttpInc(); - a_httpParsed = await parseInclude(a_http, true, true); - - // A_NPC - // const a_npc = await fetchNPCInc(); - // a_npcParsed = await parseInclude(a_npc, false, true); - // Removed because it contains most of the things a_samp already has and you shouldn't include both in a pawn gamemode either - - // A_OBJECTS - const a_objects = await fetchObjectsInc(); - a_objectsParsed = await parseInclude(a_objects, true, true); - - // A_PLAYERS - const a_players = await fetchPlayersInc(); - a_playersParsed = await parseInclude(a_players, true, true); - - // A_SAMPDB - const a_sampdb = await fetchSampDBInc(); - a_sampdbParsed = await parseInclude(a_sampdb, false, true); - - // A_VEHICLES - const a_vehicles = await fetchVehiclesInc(); - a_vehiclesParsed = await parseInclude(a_vehicles, true, true); - const eventsDefinitionsSpinner = ora('Generating event type definitions...').start(); - await generateEventsDefinitions(); + await generateEventsDefinitions(docsStore); eventsDefinitionsSpinner.succeed('Events type definitions generated.'); const sampDefinitionsSpinner = ora('Generating samp type definitions...').start(); - await generateSampDefinitions(); + await generateSampDefinitions(docsStore); sampDefinitionsSpinner.succeed('Samp type definitions generated.'); generating.succeed('All type definitions generated.'); }; -export const generateEventsDefinitions = async () => { - const a_sampEvents = getEventConstants(a_sampParsed); - const a_actorEvents = getEventConstants(a_actorParsed); - const a_httpEvents = getEventConstants(a_httpParsed); - // const a_npcEvents = getEventConstants(a_npcParsed); - const a_objectsEvents = getEventConstants(a_objectsParsed); - const a_playersEvents = getEventConstants(a_playersParsed); - const a_sampdbEvents = getEventConstants(a_sampdbParsed); - const a_vehiclesEvents = getEventConstants(a_vehiclesParsed); +export const generateEventsDefinitions = async (docsStore: DocsStore) => { + const a_sampEvents = getEventConstants(docsStore.a_samp); + const a_actorEvents = getEventConstants(docsStore.a_actor); + const a_httpEvents = getEventConstants(docsStore.a_http); + // const a_npcEvents = getEventConstants(docsStore.a_npc); + const a_objectsEvents = getEventConstants(docsStore.a_objects); + const a_playersEvents = getEventConstants(docsStore.a_players); + const a_sampdbEvents = getEventConstants(docsStore.a_sampdb); + const a_vehiclesEvents = getEventConstants(docsStore.a_vehicles); const eventConstants = { ...a_sampEvents, @@ -150,26 +42,26 @@ export const generateEventsDefinitions = async () => { }; const template = Handlebars.compile(await fs.readFile(EPaths.TEMPLATE_EVENTS, 'utf8')); - await fs.outputFile(EPaths.GENERATED_EVENTS, template({ eventConstants })); + await fs.outputFile(EPaths.GENERATED_EVENT_TYPES, template({ eventConstants })); }; -export const generateSampDefinitions = async () => { +export const generateSampDefinitions = async (docsStore: DocsStore) => { const eventListenerAliases = ['on', 'addListener', 'addEventListener']; const removeEventListenerAliases = ['removeListener', 'removeEventListener']; const parsedIncludes = [ - a_sampParsed, - a_actorParsed, - a_httpParsed, - // a_npcParsed, - a_objectsParsed, - a_playersParsed, - a_sampdbParsed, - a_vehiclesParsed, + docsStore.a_samp, + docsStore.a_actor, + docsStore.a_http, + // docsStore.a_npc, + docsStore.a_objects, + docsStore.a_players, + docsStore.a_sampdb, + docsStore.a_vehicles, ]; const template = Handlebars.compile(await fs.readFile(EPaths.TEMPLATE_SAMP, 'utf8')); - await fs.outputFile(EPaths.GENERATED_SAMP, template({ eventListenerAliases, removeEventListenerAliases, parsedIncludes })); + await fs.outputFile(EPaths.GENERATED_SAMP_TYPES, template({ eventListenerAliases, removeEventListenerAliases, parsedIncludes })); }; export const getEventConstants = (parsed: IParsed) => { diff --git a/src/index.ts b/src/index.ts index a1436c1..746dcca 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,11 @@ -import { typeDefinitions, globals } from './generators'; +import { typeDefinitions, globals, handlebarsHelper } from './generators'; +import { DocsStore } from './docsStore'; -typeDefinitions.generate(); -globals.generate(); +async function generateAll() { + const docsStore = await DocsStore.fromSampStdlib(); + handlebarsHelper.initHandlerbars(); + typeDefinitions.generate(docsStore); + globals.generate(); +} + +generateAll(); diff --git a/src/templates/samp.d.ts.hjs b/src/templates/samp.d.ts.hjs index b40f569..2c4fe2c 100644 --- a/src/templates/samp.d.ts.hjs +++ b/src/templates/samp.d.ts.hjs @@ -181,7 +181,7 @@ declare class samp { * @returns {*} {{/if}} */ - static callNative{{#if this.returnsFloat}}Float{{/if}}(nativeName: '{{this.name}}', paramTypes: '{{svs this.params}}'{{#if (gt (svwl this.params) 0)}}, {{#each (svw this.params)}}{{this.name}}: {{tts this.type false}}{{#if @last}}{{else}}, {{/if}}{{/each}}{{/if}}): {{outputtype this.params}}; + static callNative{{#if this.returnsFloat}}Float{{/if}}(nativeName: '{{this.name}}', paramTypes: '{{svs this.params}}'{{#each (svw this.params)}}, {{this.name}}: {{tts this.type false}}{{/each}}): {{outputtype this.params}}; {{/unless}} {{/each}} {{/each}} From 3828dbeb478feab6fe4bfae9ab97a2e79baf8539 Mon Sep 17 00:00:00 2001 From: Nikita Samsonau Date: Fri, 8 May 2020 02:35:59 -0400 Subject: [PATCH 03/11] Add wrapper generation --- src/docsStore.ts | 69 +++++++++++++------------- src/enums/paths.ts | 10 ++-- src/generators/handlebarsHelper.ts | 8 +++ src/generators/index.ts | 1 + src/generators/typeDefinitions.ts | 4 -- src/generators/wrappers.ts | 37 ++++++++++++++ src/index.ts | 13 +++-- src/templates/wrappers/index.ts.hjs | 3 ++ src/templates/wrappers/wrappers.ts.hjs | 39 +++++++++++++++ 9 files changed, 139 insertions(+), 45 deletions(-) create mode 100644 src/generators/wrappers.ts create mode 100644 src/templates/wrappers/index.ts.hjs create mode 100644 src/templates/wrappers/wrappers.ts.hjs diff --git a/src/docsStore.ts b/src/docsStore.ts index 96aed3f..cfb515c 100644 --- a/src/docsStore.ts +++ b/src/docsStore.ts @@ -1,6 +1,7 @@ import { IParsed } from "./interfaces"; import { fetchSampInc, fetchActorInc, fetchHttpInc, fetchObjectsInc, fetchPlayersInc, fetchSampDBInc, fetchVehiclesInc } from './requests'; import { parseInclude } from './parser'; +import ora from "ora"; export class DocsStore { private constructor( @@ -15,48 +16,48 @@ export class DocsStore { ) {} public static async fromSampStdlib() { + + const parsingSpinner = ora('Parsing docs from samp-stdlib...').start(); + console.time("Parsing") + // A_SAMP - const a_samp = await fetchSampInc(); - const a_sampParsed = await parseInclude(a_samp, true, true); - + const a_sampPromise = fetchSampInc().then(data => parseInclude(data, true, true)); // A_ACTOR - const a_actor = await fetchActorInc(); - const a_actorParsed = await parseInclude(a_actor, true, true); - + const a_actorPromise = fetchActorInc().then(data => parseInclude(data, true, true)); // A_HTTP - const a_http = await fetchHttpInc(); - const a_httpParsed = await parseInclude(a_http, true, true); - + const a_httpPromise = fetchHttpInc().then(data => parseInclude(data, true, true)); + // A_NPC - // const a_npc = await fetchNPCInc(); - // a_npcParsed = await parseInclude(a_npc, false, true); + // const a_npcPromise = fetchNPCInc().then(data => parseInclude(data, false, true)); // Removed because it contains most of the things a_samp already has and you shouldn't include both in a pawn gamemode either // A_OBJECTS - const a_objects = await fetchObjectsInc(); - const a_objectsParsed = await parseInclude(a_objects, true, true); - + const a_objectsPromise = fetchObjectsInc().then(data => parseInclude(data, true, true)); // A_PLAYERS - const a_players = await fetchPlayersInc(); - const a_playersParsed = await parseInclude(a_players, true, true); - + const a_playersPromise = fetchPlayersInc().then(data => parseInclude(data, true, true)); // A_SAMPDB - const a_sampdb = await fetchSampDBInc(); - const a_sampdbParsed = await parseInclude(a_sampdb, false, true); - + const a_sampdbPromise = fetchSampDBInc().then(data => parseInclude(data, false, true)); // A_VEHICLES - const a_vehicles = await fetchVehiclesInc(); - const a_vehiclesParsed = await parseInclude(a_vehicles, true, true); - - return new DocsStore( - a_sampParsed, - a_actorParsed, - a_httpParsed, - // a_npcParsed, - a_objectsParsed, - a_playersParsed, - a_sampdbParsed, - a_vehiclesParsed, - ); + const a_vehiclesPromise = fetchVehiclesInc().then(data => parseInclude(data, true, true)); + + const results = await Promise.all([ + a_sampPromise, + a_actorPromise, + a_httpPromise, + // a_npcPromise, + a_objectsPromise, + a_playersPromise, + a_sampdbPromise, + a_vehiclesPromise, + ]); + console.timeEnd("Parsing"); + parsingSpinner.succeed("Parsing docs finished."); + + return new DocsStore(...results); } -} \ No newline at end of file +} + +// Equals to `"a_samp" | "a_actor" | ...` etc. +export type ParsedModules = { + [P in keyof DocsStore]: DocsStore[P] extends IParsed ? P : never; +}[keyof DocsStore]; diff --git a/src/enums/paths.ts b/src/enums/paths.ts index 5084ad5..bcda6bb 100644 --- a/src/enums/paths.ts +++ b/src/enums/paths.ts @@ -2,8 +2,10 @@ export enum EPaths { TEMPLATE_EVENTS = './src/templates/events.d.ts.hjs', TEMPLATE_SAMP = './src/templates/samp.d.ts.hjs', TEMPLATE_GLOBALS = './src/templates/globals.d.ts.hjs', - TEMPLATE_WRAPPERS = './src/templates/wrappers.ts.hjs', - GENERATED_EVENT_TYPES = './generated/events.d.ts', - GENERATED_SAMP_TYPES = './generated/samp.d.ts', - GENERATED_GLOBAL_TYPES = './generated/globals.d.ts', + TEMPLATE_WRAPPERS = './src/templates/wrappers/wrappers.ts.hjs', + TEMPLATE_WRAPPERS_INDEX = './src/templates/wrappers/index.ts.hjs', + GENERATED_EVENT_TYPES = './generated/types/events.d.ts', + GENERATED_SAMP_TYPES = './generated/types/samp.d.ts', + GENERATED_GLOBAL_TYPES = './generated/types/globals.d.ts', + GENERATED_WRAPPERS_FOLDER = './generated/wrappers', } diff --git a/src/generators/handlebarsHelper.ts b/src/generators/handlebarsHelper.ts index 4d92c79..5a09e21 100644 --- a/src/generators/handlebarsHelper.ts +++ b/src/generators/handlebarsHelper.ts @@ -34,6 +34,14 @@ export function initHandlerbars() { const type = referenceToTsType(t); return b ? `{${type}}` : type; }, + restriction: (param: IParam) => { + if (param.type === 'i' || param.type === 'd') { + return "Must be a whole number." + } else if (param.type === 'a') { + return "All numbers must be whole"; + } + return null; + }, outputtype: (params: Array) => { const types = params.filter(p => p.isReference).map(r => referenceToTsType(r.type)); if (!types.length) { diff --git a/src/generators/index.ts b/src/generators/index.ts index a325c6f..f4b734f 100644 --- a/src/generators/index.ts +++ b/src/generators/index.ts @@ -1,3 +1,4 @@ export * as typeDefinitions from './typeDefinitions'; export * as globals from './globals'; export * as handlebarsHelper from './handlebarsHelper'; +export * as wrappers from './wrappers'; diff --git a/src/generators/typeDefinitions.ts b/src/generators/typeDefinitions.ts index f04d6c1..dc48a5a 100644 --- a/src/generators/typeDefinitions.ts +++ b/src/generators/typeDefinitions.ts @@ -7,8 +7,6 @@ import { EPaths } from '../enums'; import { DocsStore } from '../docsStore'; export const generate = async (docsStore: DocsStore) => { - const generating = ora('Generating type definitions...').start(); - const eventsDefinitionsSpinner = ora('Generating event type definitions...').start(); await generateEventsDefinitions(docsStore); eventsDefinitionsSpinner.succeed('Events type definitions generated.'); @@ -16,8 +14,6 @@ export const generate = async (docsStore: DocsStore) => { const sampDefinitionsSpinner = ora('Generating samp type definitions...').start(); await generateSampDefinitions(docsStore); sampDefinitionsSpinner.succeed('Samp type definitions generated.'); - - generating.succeed('All type definitions generated.'); }; export const generateEventsDefinitions = async (docsStore: DocsStore) => { diff --git a/src/generators/wrappers.ts b/src/generators/wrappers.ts new file mode 100644 index 0000000..a42cc0a --- /dev/null +++ b/src/generators/wrappers.ts @@ -0,0 +1,37 @@ +import fs from 'fs-extra'; +import Handlebars from 'handlebars'; +import ora from 'ora'; +import { EPaths } from '../enums'; +import { DocsStore, ParsedModules } from '../docsStore'; + +export const generate = async (docsStore: DocsStore) => { + const generating = ora('Generating native wrappers definitions...').start(); + await generateNativeWrappers(docsStore); + generating.succeed('All native wrappers generated.'); +}; + +const generateNativeWrappers = async (docsStore: DocsStore) => { + const moduleNames: Array = [ + "a_samp", + "a_actor", + "a_http", + // "a_npc", + "a_objects", + "a_players", + "a_sampdb", + "a_vehicles", + ]; + + const wrapperTemplate = Handlebars.compile(await fs.readFile(EPaths.TEMPLATE_WRAPPERS, 'utf8')); + const indexTemplate = Handlebars.compile(await fs.readFile(EPaths.TEMPLATE_WRAPPERS_INDEX, 'utf8')); + await Promise.all(moduleNames.map(moduleName => { + const module = docsStore[moduleName]; + return fs.outputFile(generatedWrapperPath(moduleName), wrapperTemplate({ module })); + })); + + await fs.outputFile(generatedWrapperPath("index"), indexTemplate({ moduleNames })); +}; + +function generatedWrapperPath(moduleName: string) { + return `${EPaths.GENERATED_WRAPPERS_FOLDER}/${moduleName}.ts`; +} diff --git a/src/index.ts b/src/index.ts index 746dcca..293fe13 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,11 +1,18 @@ -import { typeDefinitions, globals, handlebarsHelper } from './generators'; +import { typeDefinitions, globals, handlebarsHelper, wrappers } from './generators'; import { DocsStore } from './docsStore'; +import ora from 'ora'; + async function generateAll() { + const generating = ora('Generating definitions...').start(); + const docsStore = await DocsStore.fromSampStdlib(); handlebarsHelper.initHandlerbars(); - typeDefinitions.generate(docsStore); - globals.generate(); + await typeDefinitions.generate(docsStore); + await wrappers.generate(docsStore); + await globals.generate(); + + generating.succeed('All type definitions generated.'); } generateAll(); diff --git a/src/templates/wrappers/index.ts.hjs b/src/templates/wrappers/index.ts.hjs new file mode 100644 index 0000000..a57e69c --- /dev/null +++ b/src/templates/wrappers/index.ts.hjs @@ -0,0 +1,3 @@ +{{#each moduleNames~}} +export * from './{{this}}'; +{{/each}} \ No newline at end of file diff --git a/src/templates/wrappers/wrappers.ts.hjs b/src/templates/wrappers/wrappers.ts.hjs new file mode 100644 index 0000000..7225997 --- /dev/null +++ b/src/templates/wrappers/wrappers.ts.hjs @@ -0,0 +1,39 @@ +// Auto-generated wrappers for natives + +{{#each module.native}} + {{#unless (hvp this.params)}} + /** + * Calls the AMX native {{this.name}} {{#if this.returnsFloat}}that returns a value with a Float tag{{/if}} + * + * @name {{this.name}} + {{#if (gt (svwl this.params) 0)}} + {{#each (svw this.params)}} + * @param {{tts this.type true}} {{this.name}} + {{~#if ../this.pawnDoc}} {{ppd ../this.pawnDoc this}}{{/if}} + {{~#with (restriction this) as |r|}}{{#if r}} | {{r}}{{/if}}{{/with}} + {{/each}} + {{/if}} + {{#if this.pawnDoc}} + {{#each this.pawnDoc.summary}} + * @summary {{this}} + {{/each}} + {{#each this.pawnDoc.see}} + * @see {{this}} + {{/each}} + {{#each this.pawnDoc.remarks}} + * @remarks {{this}} + {{/each}} + {{#each this.pawnDoc.returns}} + * @returns {{this}} + {{/each}} + {{else}} + * @returns {*} + {{/if}} + */ + export function {{this.name}}({{#each (svw this.params) ~}} + {{this.name}}: {{tts this.type false}}{{#if this.defaultValue}} = {{this.defaultValue}}{{/if}}{{#unless @last}}, {{/unless}}{{/each ~}} + ): {{outputtype this.params}} { + return samp.callNative{{#if this.returnsFloat}}Float{{/if}}('{{this.name}}', '{{svs this.params}}'{{#each (svw this.params)}}, {{this.name}}{{/each}}); + } + {{/unless}} + {{/each}} \ No newline at end of file From 059f05873a3c9e2865f5a0b634d0d91206b544ef Mon Sep 17 00:00:00 2001 From: Nikita Samsonau Date: Fri, 8 May 2020 02:37:05 -0400 Subject: [PATCH 04/11] Add todo --- src/generators/wrappers.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/generators/wrappers.ts b/src/generators/wrappers.ts index a42cc0a..dadf456 100644 --- a/src/generators/wrappers.ts +++ b/src/generators/wrappers.ts @@ -5,6 +5,7 @@ import { EPaths } from '../enums'; import { DocsStore, ParsedModules } from '../docsStore'; export const generate = async (docsStore: DocsStore) => { + // TODO: generate default values const generating = ora('Generating native wrappers definitions...').start(); await generateNativeWrappers(docsStore); generating.succeed('All native wrappers generated.'); From 1475a469a5593823a2b6985f0bdc5742556f5c62 Mon Sep 17 00:00:00 2001 From: Nikita Samsonau Date: Fri, 8 May 2020 02:42:42 -0400 Subject: [PATCH 05/11] Remove console timing --- src/docsStore.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/docsStore.ts b/src/docsStore.ts index cfb515c..55ca4d0 100644 --- a/src/docsStore.ts +++ b/src/docsStore.ts @@ -16,9 +16,7 @@ export class DocsStore { ) {} public static async fromSampStdlib() { - const parsingSpinner = ora('Parsing docs from samp-stdlib...').start(); - console.time("Parsing") // A_SAMP const a_sampPromise = fetchSampInc().then(data => parseInclude(data, true, true)); @@ -26,7 +24,7 @@ export class DocsStore { const a_actorPromise = fetchActorInc().then(data => parseInclude(data, true, true)); // A_HTTP const a_httpPromise = fetchHttpInc().then(data => parseInclude(data, true, true)); - + // A_NPC // const a_npcPromise = fetchNPCInc().then(data => parseInclude(data, false, true)); // Removed because it contains most of the things a_samp already has and you shouldn't include both in a pawn gamemode either @@ -50,7 +48,7 @@ export class DocsStore { a_sampdbPromise, a_vehiclesPromise, ]); - console.timeEnd("Parsing"); + parsingSpinner.succeed("Parsing docs finished."); return new DocsStore(...results); From daa56b854751b71b2c890d2bb110441cff2f2c01 Mon Sep 17 00:00:00 2001 From: Nikita Samsonau Date: Fri, 8 May 2020 02:44:19 -0400 Subject: [PATCH 06/11] Add new line --- src/templates/wrappers/index.ts.hjs | 2 +- src/templates/wrappers/wrappers.ts.hjs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/templates/wrappers/index.ts.hjs b/src/templates/wrappers/index.ts.hjs index a57e69c..240819d 100644 --- a/src/templates/wrappers/index.ts.hjs +++ b/src/templates/wrappers/index.ts.hjs @@ -1,3 +1,3 @@ {{#each moduleNames~}} export * from './{{this}}'; -{{/each}} \ No newline at end of file +{{/each}} diff --git a/src/templates/wrappers/wrappers.ts.hjs b/src/templates/wrappers/wrappers.ts.hjs index 7225997..a01a4d3 100644 --- a/src/templates/wrappers/wrappers.ts.hjs +++ b/src/templates/wrappers/wrappers.ts.hjs @@ -36,4 +36,5 @@ return samp.callNative{{#if this.returnsFloat}}Float{{/if}}('{{this.name}}', '{{svs this.params}}'{{#each (svw this.params)}}, {{this.name}}{{/each}}); } {{/unless}} - {{/each}} \ No newline at end of file + {{/each}} + \ No newline at end of file From 9b1cba4a27d592d2bb67d49793e021fbb4a56b86 Mon Sep 17 00:00:00 2001 From: Nikita Samsonau Date: Fri, 8 May 2020 02:47:14 -0400 Subject: [PATCH 07/11] Fix identation --- src/templates/wrappers/wrappers.ts.hjs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/templates/wrappers/wrappers.ts.hjs b/src/templates/wrappers/wrappers.ts.hjs index a01a4d3..7d97c12 100644 --- a/src/templates/wrappers/wrappers.ts.hjs +++ b/src/templates/wrappers/wrappers.ts.hjs @@ -37,4 +37,3 @@ } {{/unless}} {{/each}} - \ No newline at end of file From 15e584aa089c81460fcfa53ac4e2ac781e6af4d1 Mon Sep 17 00:00:00 2001 From: Nikita Samsonau Date: Fri, 8 May 2020 02:47:56 -0400 Subject: [PATCH 08/11] Fix identation --- src/templates/wrappers/wrappers.ts.hjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/templates/wrappers/wrappers.ts.hjs b/src/templates/wrappers/wrappers.ts.hjs index 7d97c12..0e80f62 100644 --- a/src/templates/wrappers/wrappers.ts.hjs +++ b/src/templates/wrappers/wrappers.ts.hjs @@ -36,4 +36,4 @@ return samp.callNative{{#if this.returnsFloat}}Float{{/if}}('{{this.name}}', '{{svs this.params}}'{{#each (svw this.params)}}, {{this.name}}{{/each}}); } {{/unless}} - {{/each}} +{{/each}} From daea511d4d83ab7a678e1b1f9108cc5d3b593499 Mon Sep 17 00:00:00 2001 From: Nikita Samsonau Date: Fri, 8 May 2020 18:40:16 -0400 Subject: [PATCH 09/11] Minor cleanup --- src/docsStore.ts | 8 ++++---- src/generators/wrappers.ts | 2 +- src/templates/wrappers/wrappers.ts.hjs | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/docsStore.ts b/src/docsStore.ts index 55ca4d0..e9ccbf6 100644 --- a/src/docsStore.ts +++ b/src/docsStore.ts @@ -1,5 +1,5 @@ import { IParsed } from "./interfaces"; -import { fetchSampInc, fetchActorInc, fetchHttpInc, fetchObjectsInc, fetchPlayersInc, fetchSampDBInc, fetchVehiclesInc } from './requests'; +import { fetchSampInc, fetchActorInc, fetchHttpInc, fetchNPCInc, fetchObjectsInc, fetchPlayersInc, fetchSampDBInc, fetchVehiclesInc } from './requests'; import { parseInclude } from './parser'; import ora from "ora"; @@ -8,7 +8,7 @@ export class DocsStore { public readonly a_samp: IParsed, public readonly a_actor: IParsed, public readonly a_http: IParsed, - // public readonly a_npc: IParsed, + public readonly a_npc: IParsed, public readonly a_objects: IParsed, public readonly a_players: IParsed, public readonly a_sampdb: IParsed, @@ -26,7 +26,7 @@ export class DocsStore { const a_httpPromise = fetchHttpInc().then(data => parseInclude(data, true, true)); // A_NPC - // const a_npcPromise = fetchNPCInc().then(data => parseInclude(data, false, true)); + const a_npcPromise = fetchNPCInc().then(data => parseInclude(data, false, true)); // Removed because it contains most of the things a_samp already has and you shouldn't include both in a pawn gamemode either // A_OBJECTS @@ -42,7 +42,7 @@ export class DocsStore { a_sampPromise, a_actorPromise, a_httpPromise, - // a_npcPromise, + a_npcPromise, a_objectsPromise, a_playersPromise, a_sampdbPromise, diff --git a/src/generators/wrappers.ts b/src/generators/wrappers.ts index dadf456..95e37d1 100644 --- a/src/generators/wrappers.ts +++ b/src/generators/wrappers.ts @@ -16,7 +16,7 @@ const generateNativeWrappers = async (docsStore: DocsStore) => { "a_samp", "a_actor", "a_http", - // "a_npc", + "a_npc", "a_objects", "a_players", "a_sampdb", diff --git a/src/templates/wrappers/wrappers.ts.hjs b/src/templates/wrappers/wrappers.ts.hjs index 0e80f62..7af3639 100644 --- a/src/templates/wrappers/wrappers.ts.hjs +++ b/src/templates/wrappers/wrappers.ts.hjs @@ -10,7 +10,7 @@ {{#each (svw this.params)}} * @param {{tts this.type true}} {{this.name}} {{~#if ../this.pawnDoc}} {{ppd ../this.pawnDoc this}}{{/if}} - {{~#with (restriction this) as |r|}}{{#if r}} | {{r}}{{/if}}{{/with}} + {{~#with (restriction this) as |r|}}{{#if r}} - {{r}}{{/if}}{{/with}} {{/each}} {{/if}} {{#if this.pawnDoc}} @@ -31,8 +31,8 @@ {{/if}} */ export function {{this.name}}({{#each (svw this.params) ~}} - {{this.name}}: {{tts this.type false}}{{#if this.defaultValue}} = {{this.defaultValue}}{{/if}}{{#unless @last}}, {{/unless}}{{/each ~}} - ): {{outputtype this.params}} { + {{this.name}}: {{tts this.type false}}{{#if this.defaultValue}} = {{this.defaultValue}}{{/if}}{{#unless @last}}, {{/unless}} + {{~/each}}): {{outputtype this.params}} { return samp.callNative{{#if this.returnsFloat}}Float{{/if}}('{{this.name}}', '{{svs this.params}}'{{#each (svw this.params)}}, {{this.name}}{{/each}}); } {{/unless}} From d2d63b58f0d964caba41efcdf97930e537161352 Mon Sep 17 00:00:00 2001 From: Nikita Samsonau Date: Sun, 10 May 2020 14:17:48 -0400 Subject: [PATCH 10/11] Make wrappers return objects rather than arrays --- src/generators/handlebarsHelper.ts | 2 ++ src/templates/wrappers/wrappers.ts.hjs | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/generators/handlebarsHelper.ts b/src/generators/handlebarsHelper.ts index 5a09e21..7b85fd6 100644 --- a/src/generators/handlebarsHelper.ts +++ b/src/generators/handlebarsHelper.ts @@ -22,6 +22,8 @@ export function initHandlerbars() { svw: (pa: Array) => pa.filter(p => !p.isReference), // specifier values without references length svwl: (pa: Array) => pa.filter(p => !p.isReference).length, + // reference values + rv: (pa: Array) => pa.filter(p => p.isReference), // reference values string rvs: (pa: Array) => pa.filter(p => p.isReference).map(p => p.type.toUpperCase()).join(''), // reference values length diff --git a/src/templates/wrappers/wrappers.ts.hjs b/src/templates/wrappers/wrappers.ts.hjs index 7af3639..ae016ad 100644 --- a/src/templates/wrappers/wrappers.ts.hjs +++ b/src/templates/wrappers/wrappers.ts.hjs @@ -32,8 +32,13 @@ */ export function {{this.name}}({{#each (svw this.params) ~}} {{this.name}}: {{tts this.type false}}{{#if this.defaultValue}} = {{this.defaultValue}}{{/if}}{{#unless @last}}, {{/unless}} - {{~/each}}): {{outputtype this.params}} { + {{~/each}}) { + {{#if (gt (rvl this.params) 1)}} + const [{{#each (rv this.params)}}{{this.name}}{{#unless @last}}, {{/unless}}{{/each}}] = samp.callNative{{#if this.returnsFloat}}Float{{/if}}('{{this.name}}', '{{svs this.params}}'{{#each (svw this.params)}}, {{this.name}}{{/each}}); + return { {{#each (rv this.params)}}{{this.name}}{{#unless @last}}, {{/unless}}{{/each}} }; + {{else}} return samp.callNative{{#if this.returnsFloat}}Float{{/if}}('{{this.name}}', '{{svs this.params}}'{{#each (svw this.params)}}, {{this.name}}{{/each}}); + {{/if}} } {{/unless}} {{/each}} From 85ea09fae43a525f3fd7536b9df95b9835da5949 Mon Sep 17 00:00:00 2001 From: Nikita Samsonau Date: Mon, 11 May 2020 02:33:34 -0400 Subject: [PATCH 11/11] Disable default values and npc --- src/generators/wrappers.ts | 2 +- src/templates/wrappers/wrappers.ts.hjs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/generators/wrappers.ts b/src/generators/wrappers.ts index 95e37d1..dadf456 100644 --- a/src/generators/wrappers.ts +++ b/src/generators/wrappers.ts @@ -16,7 +16,7 @@ const generateNativeWrappers = async (docsStore: DocsStore) => { "a_samp", "a_actor", "a_http", - "a_npc", + // "a_npc", "a_objects", "a_players", "a_sampdb", diff --git a/src/templates/wrappers/wrappers.ts.hjs b/src/templates/wrappers/wrappers.ts.hjs index ae016ad..3e88a0a 100644 --- a/src/templates/wrappers/wrappers.ts.hjs +++ b/src/templates/wrappers/wrappers.ts.hjs @@ -31,7 +31,7 @@ {{/if}} */ export function {{this.name}}({{#each (svw this.params) ~}} - {{this.name}}: {{tts this.type false}}{{#if this.defaultValue}} = {{this.defaultValue}}{{/if}}{{#unless @last}}, {{/unless}} + {{this.name}}: {{tts this.type false}}{{#unless @last}}, {{/unless}} {{~/each}}) { {{#if (gt (rvl this.params) 1)}} const [{{#each (rv this.params)}}{{this.name}}{{#unless @last}}, {{/unless}}{{/each}}] = samp.callNative{{#if this.returnsFloat}}Float{{/if}}('{{this.name}}', '{{svs this.params}}'{{#each (svw this.params)}}, {{this.name}}{{/each}});