opnet-transform-web
Version:
OP_NET AssemblyScript transformer
92 lines (91 loc) • 3.87 kB
JavaScript
import OPNetTransformer, { SimpleParser, logger } from './OPNetTransformer.js';
import parserTypeScript from 'prettier/parser-typescript';
import prettier from 'prettier/standalone';
import prettierPluginEstree from 'prettier/plugins/estree';
export default class OpnetWebTransformer extends OPNetTransformer {
virtualFs = new Map();
readFile(filename, baseDir) {
return this.virtualFs.get(`${baseDir}/${filename}`) ?? null;
}
writeFile(filename, contents, baseDir) {
this.virtualFs.set(`${baseDir}/${filename}`, contents.toString());
return;
}
listFiles(dirname, baseDir) {
const files = [];
for (const [key] of this.virtualFs.entries()) {
if (key.startsWith(`${baseDir}/${dirname}`)) {
files.push(key.replace(`${baseDir}/`, ''));
}
}
return files.length > 0 ? files : null;
}
async afterParse(parser) {
// 1) Parse AST
for (const source of parser.sources) {
if (source.isLibrary || source.internalPath.startsWith('~lib/')) {
continue;
}
for (const stmt of source.statements) {
this.visitStatement(stmt);
}
}
// 2) Build ABI per class
const abiMap = this.buildAbiPerClass();
// 4) Write one JSON + .d.ts per class
for (const [className, abiObj] of abiMap.entries()) {
if (abiObj.functions.length === 0)
continue;
// JSON
const filePath = `abis/${className}.abi.json`;
this.writeFile(filePath, JSON.stringify(abiObj, null, 4), '.');
logger.success(`ABI generated to ${filePath}`);
// DTS
const dtsPath = `abis/${className}.d.ts`;
const dtsContents = this.buildDtsForClass(className, abiObj);
const formattedDts = await prettier.format(dtsContents, {
plugins: [parserTypeScript, prettierPluginEstree],
parser: 'typescript',
printWidth: 100,
trailingComma: 'all',
tabWidth: 4,
semi: true,
singleQuote: true,
quoteProps: 'as-needed',
bracketSpacing: true,
bracketSameLine: true,
arrowParens: 'always',
singleAttributePerLine: true,
});
this.writeFile(dtsPath, formattedDts, '.');
logger.success(`Type definitions generated to ${dtsPath}`);
}
// 5) Inject/overwrite `execute` in each relevant class
for (const [className, methods] of this.methodsByClass.entries()) {
if (!methods.length)
continue;
logger.info(`Injecting 'execute' in class ${className}...`);
const classDecl = this.classDeclarations.get(className);
if (!classDecl) {
logger.warn(`ClassDeclaration not found for ${className}`);
continue;
}
const methodText = this.buildExecuteMethod(className, methods);
const newMember = SimpleParser.parseClassMember(methodText, classDecl);
// Overwrite if it exists
const existingIndex = classDecl.members.findIndex((member) => {
return (member.kind === 58 /* NodeKind.MethodDeclaration */ &&
member.name.text === 'execute');
});
if (existingIndex !== -1) {
logger.info(`Overwriting existing 'execute' in class ${className}`);
classDecl.members[existingIndex] = newMember;
}
else {
classDecl.members.push(newMember);
}
}
// 6) Check for "unused" events and log warnings
this.checkUnusedEvents();
}
}