/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; /** * Build: /dist/@types/lib.gecko.xpcom.d.ts, * * from: /config/makefiles/xpidl/*.d.json type definition files, * generated by a previous build step. */ const fs = require("fs"); const URL = "https://searchfox.org/mozilla-central/source/"; const HEADER = `/** * NOTE: Do not modify this file by hand. * Content was generated from source XPCOM .idl files. */ `; // Emit a typescript interface, along with any related enums. function ts_interface(iface) { let lines = []; let base = iface.base; iface.class = iface.id; // Make QueryInterface optional, enable plain objects to pass as nsISupports. let partial = iface.id === "nsISupports" ? "?" : ""; let enums = iface.enums.map(e => `typeof ${iface.id}.${e.id}`).join(" & "); if (enums) { base += `, Enums<${enums}>`; iface.class += `, ${enums}`; // Close the global scope, avoid polluting it with the namespace value. lines.push("} // global\n"); lines.push(`declare namespace ${iface.id} {\n`); for (let e of iface.enums) { lines.push(`enum ${e.id} {`); for (let v of e.variants) { lines.push(` ${v.name} = ${v.value},`); } lines.push("}\n"); } lines.push("}\n"); lines.push("declare global {\n"); } // Handle [function] interfaces. if (iface.callable) { lines.push(`type ${iface.id} = Callable<{`); } else { lines.push(`interface ${iface.id} ${base ? `extends ${base} ` : ""}{`); } for (let c of iface.consts) { lines.push(` readonly ${c.name}: ${c.value};`); } if (iface.consts.length && iface.members.length) { // For style points. lines.push(""); } for (let m of iface.members) { if (!m.args) { lines.push(` ${m.readonly ? "readonly " : ""}${m.name}: ${m.type};`); } else { let args = []; for (let arg of m.args) { // If this is the generic parameter, adjust its type. let type = arg.name === m.iid_is ? "T" : arg.type; args.push(`${arg.name}${arg.optional ? "?" : ""}: ${type}`); } let type = `(${args.join(", ")}): ${m.type}`; // Adjust signature if this is a generic method. let signature = m.iid_is ? `${type}` : type; lines.push(` ${m.name}${partial}${signature};`); } } lines.push(iface.callable ? "}>\n" : "}\n"); return lines; } // Link all generated .d.json files into a self-contained ts typelib. function ts_link(dir, files) { let lines = [HEADER, "declare global {\n"]; let typedefs = {}; let iids = []; for (let djson of files) { let modules = JSON.parse(fs.readFileSync(`${dir}/${djson}`, "utf8")); for (let mod of modules) { lines.push(`// ${URL}${mod.path}\n`); Object.assign(typedefs, Object.fromEntries(mod.typedefs ?? [])); for (let iface of mod.interfaces ?? []) { if (iface.id !== "nsIXPCComponents_Interfaces") { lines = lines.concat(ts_interface(iface)); iids.push(` ${iface.id}: nsJSIID<${iface.class}>;`); } } } } lines.push("interface nsIXPCComponents_Interfaces {"); lines = lines.concat(iids); lines.push("}\n"); lines.push("} // global\n"); lines.push("// Typedefs from xpidl."); for (let [id, type] of Object.entries(typedefs).sort()) { lines.push(`type ${id} = ${type};`); } lines.push(""); // Include xpcom builtins. lines.push(fs.readFileSync(`${__dirname}/fixtures/intrinsics.d.ts`, "utf8")); return lines; } // For testing. module.exports = { ts_link }; function main(lib_dts, djson_dir, ...djson_files) { let dts = ts_link(djson_dir, djson_files).join("\n"); console.log(`[INFO] ${lib_dts} (${dts.length.toLocaleString()} bytes)`); fs.writeFileSync(lib_dts, dts); } if (require.main === module) { main(...process.argv.slice(2)); }