/* 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. * If you're updating some of the sources, see README for instructions. */ `; const FOOTER = ` // XPCOM internal utility types. /** XPCOM inout param is passed in as a js object with a value property. */ type InOutParam = { value: T }; /** XPCOM out param is written to the passed in object's value property. */ type OutParam = { value?: T }; /** Enable interfaces to inherit from enums: pick variants as optional. */ type Enums = Partial>; /** Callable accepts either form of a [function] interface. */ type Callable = iface | Extract export {}; `; const PLATFORM_SPECIFIC = new Set([ "nsIAboutThirdParty.idl", "nsIAboutWindowsMessages.idl", "nsIAccessibleMacInterface.idl", "nsIApplicationChooser.idl", "nsIDefaultAgent.idl", "nsIGeolocationUIUtilsWin.idl", "nsIGtkTaskbarProgress.idl", "nsIGNOMEShellService.idl", "nsIJumpListBuilder.idl", "nsIKeychainMigrationUtils.idl", "nsILocalFileMac.idl", "nsIMacDockSupport.idl", "nsIMacFinderProgress.idl", "nsIMacPreferencesReader.idl", "nsIMacSharingService.idl", "nsIMacShellService.idl", "nsIMacUserActivityUpdater.idl", "nsIMacWebAppUtils.idl", "nsINamedPipeService.idl", "nsIOpenTabsProvider.idl", "nsIPrintSettingsWin.idl", "nsIStandaloneNativeMenu.idl", "nsITaskbarOverlayIconController.idl", "nsITaskbarPreview.idl", "nsITaskbarPreviewButton.idl", "nsITaskbarPreviewController.idl", "nsITaskbarProgress.idl", "nsITaskbarTabPreview.idl", "nsITaskbarWindowPreview.idl", "nsITouchBarHelper.idl", "nsITouchBarInput.idl", "nsITouchBarUpdater.idl", "nsIWinAppHelper.idl", "nsIWinTaskbar.idl", "nsIWinTaskSchedulerService.idl", "nsIWindowsAlertsService.idl", "nsIWindowsMutex.idl", "nsIWindowsPackageManager.idl", "nsIWindowsRegKey.idl", "nsIWindowsShellService.idl", "nsIWindowsUIUtils.idl", ]); // 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 enum values. lines.push("} // global\n"); for (let e of iface.enums) { lines.push(`declare enum ${iface.id}_${e.id} {`); for (let v of e.variants) { lines.push(` ${v.name} = ${v.value},`); } lines.push("}\n"); } lines.push("declare global {\n"); lines.push(`namespace ${iface.id} {`); for (let e of iface.enums) { lines.push(` type ${e.id} = ${iface.id}_${e.id};`); } lines.push("}\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, filter) { let lines = [HEADER, "declare global {\n"]; let typedefs = {}; let iids = []; for (let djson of files.sort()) { let modules = JSON.parse(fs.readFileSync(`${dir}/${djson}`, "utf8")); for (let mod of modules.filter(m => filter(m.path.split("/").at(-1)))) { 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(FOOTER); return lines.join("\n"); } // For testing. module.exports = { ts_link }; function main(lib_xpcom, djson_dir, ...djson_files) { let dts = ts_link(djson_dir, djson_files, m => !PLATFORM_SPECIFIC.has(m)); console.log(`[INFO] ${lib_xpcom} (${dts.length.toLocaleString()} bytes)`); fs.writeFileSync(lib_xpcom, dts); let lib_plat = lib_xpcom.replace("xpcom", process.platform); let spec = ts_link(djson_dir, djson_files, m => PLATFORM_SPECIFIC.has(m)); console.log(`[INFO] ${lib_plat} (${spec.length.toLocaleString()} bytes)`); fs.writeFileSync(lib_plat, spec); } if (require.main === module) { main(...process.argv.slice(2)); }