Skip to content

Commit

Permalink
Updated generate-enums script to generate TS enums
Browse files Browse the repository at this point in the history
  • Loading branch information
DoctorMcKay committed Aug 12, 2024
1 parent 2b810a2 commit 233cc14
Show file tree
Hide file tree
Showing 229 changed files with 6,506 additions and 298 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ test/
resources/servers.json
dev_data.json
dev/
dist/

# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
Expand Down
23 changes: 23 additions & 0 deletions scripts/delete-dist.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const FS = require('fs');
const Path = require('path');

deleteDirectory(Path.join(__dirname, '..', 'dist'));

function deleteDirectory(dirName) {
if (!FS.existsSync(dirName)) {
return;
}

FS.readdirSync(dirName).forEach((filename) => {
let filePath = Path.join(dirName, filename);

let stat = FS.statSync(filePath);
if (stat.isDirectory()) {
deleteDirectory(filePath);
} else {
FS.unlinkSync(filePath);
}
});

FS.rmdirSync(dirName);
}
125 changes: 71 additions & 54 deletions scripts/generate-enums.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,26 @@ download('https://api.github.com/repos/SteamRE/SteamKit/contents/Resources/Steam
if (line.match(/^};?$/)) {
process.stdout.write(`Generating ${currentEnum.name}.js... `);
// We're done parsing this enum, let's go ahead and generate the file
// First make sure it has actually changed
let enumFileName = `${__dirname}/../enums/${currentEnum.name}.js`;
let {changed, valuesToAdd} = validateEnum(enumFileName, currentEnum.values, currentEnum.dynamicValues);

// Resolve any dynamic values
currentEnum.dynamicValues.forEach(({name, value, comment}) => {
let resolvedValue = 0;
value.forEach((flag) => {
let flagEnumValue = currentEnum.values.find(v => v.name == flag);
if (!flagEnumValue) {
throw new Error(`Couldn't find dynamic value "${name}" in enum ${currentEnum.name}`);
}

resolvedValue |= flagEnumValue.value;
});

comment = comment || value.join('|');
currentEnum.values.push({name, value: resolvedValue, comment});
});

// Make sure it has actually changed
let enumFileName = `${__dirname}/../src/enums/${currentEnum.name}.ts`;
let {changed, valuesToAdd} = validateEnum(enumFileName, currentEnum.values);
if (!changed) {
// Enum has not changed
console.log('unchanged');
Expand All @@ -132,36 +149,19 @@ download('https://api.github.com/repos/SteamRE/SteamKit/contents/Resources/Steam
currentEnum.values = currentEnum.values.concat(valuesToAdd);
currentEnum.values.sort(sortEnum);

let file = GENERATED_FILE_HEADER + `/**\n * @enum\n * @readonly\n */\nconst ${currentEnum.name} = {\n`;
let file = `${GENERATED_FILE_HEADER}enum ${currentEnum.name} {\n`;

currentEnum.values.forEach(function(val) {
file += '\t"' + val.name + '": ' + val.value + ',' + (val.comment ? ' // ' + val.comment.trim() : '') + '\n';
});

file += '\n\t// Value-to-name mapping for convenience\n';

// Put down the reverse, for simplicity in use
currentEnum.values.forEach(function(val, idx) {
if (!val.value.match(/^-?[0-9]+/)) {
return; // it's dynamic
}

// Is this the last value in this enum with this value?
if (currentEnum.values.some(function(val2, idx2) { return val2.value == val.value && idx2 > idx; })) {
let comment = val.comment ? val.comment.trim() : null;
if (comment?.startsWith('removed') && currentEnum.values.some(v => v.name == val.name && v.value != val.value)) {
// This name is used for another case
return;
}

file += '\t"' + val.value + '": "' + val.name + '",\n';
file += `\t${quoteObjectKey(val.name)} = ${val.value},${val.comment ? ` // ${val.comment.trim()}` : ''}\n`;
});

file += `};\n\nmodule.exports = ${currentEnum.name};\n`;

if (currentEnum.dynamicValues.length > 0) {
file += '\n';
currentEnum.dynamicValues.forEach(function(val) {
file += 'module.exports.' + val.name + ' = ' + val.value + ';' + (val.comment ? ' // ' + val.comment.trim() : '') + '\n';
});
}
file += `}\n\nexport default ${currentEnum.name};\n`;

FS.writeFileSync(enumFileName, file);
g_EnumNames[currentEnum.name] = true;
Expand All @@ -183,24 +183,16 @@ download('https://api.github.com/repos/SteamRE/SteamKit/contents/Resources/Steam

let isDynamic = false;

let flags = value.split('|').map(function(flag) {
let flags = value.split('|').map((flag) => {
flag = flag.trim();

if (flag.match(/^-?[0-9]+$/)) {
return flag;
} else {
isDynamic = true;
return 'module.exports.' + flag;
}
isDynamic = isDynamic || !flag.match(/^-?[0-9]+$/);
return flag;
});

value = flags.join(' | ');
// If this is a dynamic value, push the array into the current enum's object so we can resolve it later
value = isDynamic ? flags : flags[0];

(isDynamic ? currentEnum.dynamicValues : currentEnum.values).push({
name: name,
value: value,
comment: comment
});
(isDynamic ? currentEnum.dynamicValues : currentEnum.values).push({name, value, comment});
}
}
});
Expand All @@ -211,11 +203,13 @@ download('https://api.github.com/repos/SteamRE/SteamKit/contents/Resources/Steam
g_EnumNames = Object.keys(g_EnumNames);
g_EnumNames.sort();

let loader = GENERATED_FILE_HEADER + 'const SteamUserBase = require(\'./00-base.js\');\n\nclass SteamUserEnums extends SteamUserBase {\n}\n\n';
loader += g_EnumNames.map(name => `SteamUserEnums.${name} = require('../enums/${name}.js');`).join('\n');
loader += '\n\nmodule.exports = SteamUserEnums;\n';
let loader = `${GENERATED_FILE_HEADER}import SteamUserBase from './00-base';\n\n`;
loader += g_EnumNames.map(name => `import ${name} from '../enums/${name}';`).join('\n');
loader += '\n\nclass SteamUserEnums extends SteamUserBase {\n';
loader += g_EnumNames.map(name => `\tstatic ${name} = ${name};`).join('\n');
loader += '\n}\n\nexport default SteamUserEnums;\n';

FS.writeFileSync(__dirname + '/../components/01-enums.js', loader);
FS.writeFileSync(__dirname + '/../src/components/01-enums.ts', loader);
console.log('Wrote loader');
}
});
Expand Down Expand Up @@ -265,7 +259,7 @@ function processProtobufEnums() {
});
}

let enumFileName = `${__dirname}/../enums/${enumName}.js`;
let enumFileName = `${__dirname}/../src/enums/${enumName}.ts`;

// Check to see if the enum has changed at all
let {changed, valuesToAdd} = validateEnum(enumFileName, processed);
Expand All @@ -286,12 +280,10 @@ function processProtobufEnums() {
processed = processed.concat(valuesToAdd);
processed.sort(sortEnum);

let enumFile = GENERATED_FILE_HEADER + `/**\n * @enum\n * @readonly\n */\nconst ${enumName} = {\n`;
enumFile += processed.map(v => `\t${quoteObjectKey(v.name)}: ${v.value},` + (v.comment ? ` // ${v.comment}` : '')).join('\n');
enumFile += '\n\n\t// Value-to-name mapping for convenience\n';
enumFile += processed.filter(v => v.comment !== 'obsolete').map(v => `\t${quoteObjectKey(v.value)}: '${v.name}',`).join('\n');
enumFile += `\n};\n\nmodule.exports = ${enumName};\n`;
FS.writeFileSync(`${__dirname}/../enums/${enumName}.js`, enumFile);
let enumFile = `${GENERATED_FILE_HEADER}enum ${enumName} {\n`;
enumFile += processed.map(v => `\t${quoteObjectKey(v.name)} = ${v.value},` + (v.comment ? ` // ${v.comment}` : '')).join('\n');
enumFile += `\n}\n\nexport default ${enumName};\n`;
FS.writeFileSync(enumFileName, enumFile);

g_EnumNames[enumName] = true;
let normalized = enumName.toLowerCase();
Expand Down Expand Up @@ -324,14 +316,14 @@ function download(url, callback) {
}).end();
}

function validateEnum(enumFileName, values, dynamicValues = []) {
function validateEnum(enumFileName, values) {
let output = {
changed: true,
valuesToAdd: []
};

if (FS.existsSync(enumFileName)) {
let existingEnum = Object.assign({}, require(enumFileName)); // clone it since we're about to manipulate it
let existingEnum = readEnumFile(enumFileName)
for (let i in existingEnum) {
if (i.match(/^-?\d+$/)) {
delete existingEnum[i];
Expand All @@ -352,7 +344,7 @@ function validateEnum(enumFileName, values, dynamicValues = []) {
}
}

if (values.length + output.valuesToAdd.length + dynamicValues.length == Object.keys(existingEnum).length) {
if (values.length + output.valuesToAdd.length == Object.keys(existingEnum).length) {
// Enum did not change
output.changed = false;
}
Expand All @@ -361,6 +353,31 @@ function validateEnum(enumFileName, values, dynamicValues = []) {
return output;
}

function readEnumFile(filename) {
let fileContent = FS.readFileSync(filename).toString('utf8');

let enumObjMatch = fileContent.match(/enum [^ ]+ ({\n[^}]+)\n}/);
if (!enumObjMatch) {
throw new Error(`Could not parse enum out of ${filename}`);
}

/** @var {{name: string, value: number, comment?: string}[]} cases */
let cases = [];

enumObjMatch[1].split('\n').forEach((enumCase) => {
let caseMatch = enumCase.match(/\t([^ ]+) = (-?\d+),(\W*\/\/[^\n]+)?\W*$/);
if (caseMatch) {
cases.push({
name: caseMatch[1],
value: parseInt(caseMatch[2]),
comment: caseMatch[3]?.match(/^\W*\/\/(.+)$/)[1].trim()
});
}
});

return cases;
}

function sortEnum(a, b) {
let aValue = parseInt(a.value, 10);
let bValue = parseInt(b.value, 10);
Expand Down
10 changes: 6 additions & 4 deletions src/components/00-base.js → src/components/00-base.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
const {EventEmitter} = require('events');
import {EventEmitter} from 'events';

const HandlerManager = require('./classes/HandlerManager.js');
import {HandlerManager} from './classes/HandlerManager';

/**
* I admit, this is a pretty unorthodox pattern, but this is the only way I've found to define a class across multiple
* files while also making sure that IDE intellisense can figure out which methods belong to the final class.
*
* Inheritance follows filenames sorted alphabetically with numbers first.
*/
class SteamUserBase extends EventEmitter {}
class SteamUserBase extends EventEmitter {
_handlerManager: HandlerManager;
}

SteamUserBase.prototype._handlerManager = new HandlerManager();

module.exports = SteamUserBase;
export default SteamUserBase;
Loading

0 comments on commit 233cc14

Please sign in to comment.