118 lines
3.9 KiB
TypeScript
118 lines
3.9 KiB
TypeScript
/*!
|
|
* This script generates the interface `CCAPIs` in `src/lib/commandclass/API.ts`
|
|
* which is used to strongly-type the simplified API exposed via
|
|
* `ZWaveNode.commandClasses.xyz`
|
|
*/
|
|
|
|
import { formatWithDprint } from "@zwave-js/maintenance";
|
|
import esMain from "es-main";
|
|
import fs from "node:fs/promises";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
|
const apiRegex = /^@API\(CommandClasses(?:\.|\[)(.+?)(?:\])?\)/m;
|
|
const classNameRegex = /class ([^\s]+) extends (\w+)?CCAPI/;
|
|
const ccDir = path.join(__dirname, "..", "src/cc");
|
|
const libDir = path.join(__dirname, "..", "src/lib");
|
|
const apiFile = path.join(libDir, "API.ts");
|
|
const startTokenType1 = "type CCNameMap = {";
|
|
// const startTokenType2 =
|
|
// "export type CCInstanceToAPI<CC extends CommandClass> =";
|
|
const endTokenType = "};";
|
|
|
|
const startTokenInterface = "\t// AUTO GENERATION BELOW";
|
|
const endTokenInterface = "}";
|
|
|
|
process.on("unhandledRejection", (r) => {
|
|
throw r;
|
|
});
|
|
|
|
export async function generateCCAPIInterface(): Promise<void> {
|
|
const ccFiles = (await fs.readdir(ccDir)).filter(
|
|
(file) => file.endsWith(".ts") && !file.endsWith("test.ts"),
|
|
);
|
|
|
|
const CCsWithAPI: { name: string; className: string; file: string }[] = [];
|
|
|
|
for (const ccFile of ccFiles) {
|
|
const fileContent = await fs.readFile(path.join(ccDir, ccFile), "utf8");
|
|
// Extract the CC name from e.g. `@API(CommandClasses["Binary Sensor"])`
|
|
const apiMatch = apiRegex.exec(fileContent);
|
|
// Extract the class name from e.g. `export class BasicCCAPI extends CCAPI`
|
|
const classMatch = classNameRegex.exec(fileContent);
|
|
if (apiMatch && classMatch) {
|
|
CCsWithAPI.push({
|
|
file: ccFile.replace(/\.ts$/, ".js"),
|
|
name: apiMatch[1],
|
|
className: classMatch[1],
|
|
});
|
|
}
|
|
}
|
|
|
|
console.log(`Found ${CCsWithAPI.length} API classes...`);
|
|
|
|
const originalApiFileContent = await fs.readFile(apiFile, "utf8");
|
|
let apiFileContent = originalApiFileContent;
|
|
|
|
// Generate interface
|
|
let startTokenEnd = apiFileContent.indexOf(startTokenInterface)
|
|
+ startTokenInterface.length;
|
|
let endTokenStart = apiFileContent.indexOf(
|
|
endTokenInterface,
|
|
startTokenEnd,
|
|
);
|
|
apiFileContent = apiFileContent.slice(0, startTokenEnd)
|
|
+ "\n"
|
|
+ CCsWithAPI.map(
|
|
({ name, className, file }) =>
|
|
`\t${name}: import("${
|
|
path
|
|
.relative(libDir, ccDir)
|
|
.replaceAll("\\", "/")
|
|
}/${file}").${className};`,
|
|
).join("\n")
|
|
+ "\n"
|
|
+ apiFileContent.slice(endTokenStart);
|
|
|
|
// Generate lookup types (part 1: CCToName)
|
|
startTokenEnd = apiFileContent.indexOf(startTokenType1)
|
|
+ startTokenType1.length;
|
|
endTokenStart = apiFileContent.indexOf(endTokenType, startTokenEnd);
|
|
apiFileContent = apiFileContent.slice(0, startTokenEnd)
|
|
+ "\n"
|
|
+ CCsWithAPI.map(({ name }) => {
|
|
if (!name.startsWith(`"`)) name = `"${name}"`;
|
|
return `\t${name}: typeof CommandClasses[${name}]`;
|
|
}).join("\n")
|
|
+ "\n"
|
|
+ apiFileContent.slice(endTokenStart);
|
|
|
|
// // Generate lookup types (part 2: CCInstanceToName)
|
|
// startTokenEnd =
|
|
// apiFileContent.indexOf(startTokenType2) + startTokenType2.length;
|
|
// endTokenStart = apiFileContent.indexOf(endTokenType, startTokenEnd);
|
|
// apiFileContent =
|
|
// apiFileContent.slice(0, startTokenEnd) +
|
|
// "\n" +
|
|
// CCsWithAPI.map(({ name, className, file }) => {
|
|
// if (!name.startsWith(`"`)) name = `"${name}"`;
|
|
// return `\t[CC] extends [import("./${file}").${className.replace(
|
|
// /API$/,
|
|
// "",
|
|
// )}] ? import("./${file}").${className} : `;
|
|
// }).join("\n") +
|
|
// "\n" +
|
|
// apiFileContent.slice(endTokenStart);
|
|
|
|
apiFileContent = formatWithDprint(apiFile, apiFileContent);
|
|
// Only update file if necessary - this reduces build time
|
|
if (apiFileContent !== originalApiFileContent) {
|
|
console.log("API interface changed");
|
|
await fs.writeFile(apiFile, apiFileContent, "utf8");
|
|
}
|
|
}
|
|
|
|
if (esMain(import.meta)) void generateCCAPIInterface();
|