1
0
Fork 0
firefox/tools/ts/build_xpcom.js
Daniel Baumann 5e9a113729
Adding upstream version 140.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-25 09:37:52 +02:00

209 lines
6.2 KiB
JavaScript

/* 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: <objdir>/dist/@types/lib.gecko.xpcom.d.ts,
*
* from: <objdir>/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<T> = { value: T };
/** XPCOM out param is written to the passed in object's value property. */
type OutParam<T> = { value?: T };
/** Enable interfaces to inherit from enums: pick variants as optional. */
type Enums<enums> = Partial<Pick<enums, keyof enums>>;
/** Callable accepts either form of a [function] interface. */
type Callable<iface> = iface | Extract<iface[keyof iface], Function>
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 ? `<T extends nsIID>${type}<T>` : 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));
}