/* 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: /tools/@types/generated/tspaths.json, and * /tools/@types/generated/lib.gecko.modules.d.ts * * from: various import forms in all *.js and *.mjs sources. */ const fs = require("fs"); const path = require("path"); const { fixed } = require("./config/fixed_paths.js"); const HEADER = `/** * NOTE: Do not modify this file by hand. * Content was generated by running "mach ts paths". */ `; const IGNORE = [/\.git/, /\.hg/, /node_modules/, /^obj.*/, /test262/]; const IMPORT = /(\bimport |import\(|require\(|\.importESModule\(|\.(defineESModuleGetters?|declareLazy|defineLazy)\()[^;]+/gm; const URI = /("|')((resource|chrome|moz-src):\/\/[\w\d\/_.-]+\.m?js)\1/gm; function ignore(filePath) { return IGNORE.some(re => filePath.match(re)); } // Scan the root dir for *.js and *.mjs files, recursivelly. function scan(root, dir, files) { for (let file of fs.readdirSync(`${root}/${dir}`, { withFileTypes: true })) { if (file.isDirectory() && !ignore(dir + file.name)) { scan(root, `${dir}${file.name}/`, files); } else if (file.name.match(/\.(x?html|m?js)$/)) { files.push(dir + file.name); } } } // Emit path mapping for all found module URIs. function emitPaths(files, uris, modules, relativeBasePath) { let paths = {}; for (let uri of [...uris].sort()) { if (uri in fixed) { continue; } let parts = uri.split("/"); let filePath = ""; let matches; // Check for a substitution .d.ts file from processed/generated sources. let sub = parts.at(-1).replace(/\.(m)?js$/, ".d.$1ts"); if (fs.existsSync(`${__dirname}/../@types/subs/${sub}`)) { paths[uri] = [`tools/@types/subs/${sub}`]; continue; } // Starting with just the file name, add each path part going backwards. do { filePath = "/" + parts.pop() + filePath; matches = files.filter(f => f.endsWith(filePath)); // While multiple files match, add parts used for filtering. } while (matches.length > 1 && parts.length); // Unique match is almost certainy correct. if (matches.length === 1) { paths[uri] = [matches[0]]; } else { // URI matched more than one, or failed to match any file. console.warn("[WARN]", uri); modules.delete(uri); } } Object.assign(paths, fixed); let tspaths = { compilerOptions: { baseUrl: relativeBasePath, paths } }; return JSON.stringify(tspaths, null, 2) + "\n"; } // Emit type mapping for all modules imported via lazy getters. function emitLazy(modules) { let lines = [HEADER]; lines.push("export interface LazyModules {"); for (let uri of [...modules].sort()) { lines.push(` "${uri}": typeof import("${uri}"),`); } lines.push("}\n"); return lines.join("\n"); } function main(root_dir, paths_json, lib_lazy) { let files = []; scan(root_dir, "", files); let uris = new Set(); let modules = new Set(); for (let file of files) { let src = fs.readFileSync(`${root_dir}/${file}`, "utf-8"); for (let [exp, , method] of src.matchAll(IMPORT)) { for (let [, , uri, proto] of exp.matchAll(URI)) { if (proto !== "moz-src") { uris.add(uri); } if (method?.match(/ModuleGetter|Lazy/)) { modules.add(uri); } } } } let json = emitPaths( files, uris, modules, path.relative(path.dirname(paths_json), root_dir).replaceAll("\\", "/") ); console.log(`[INFO] ${paths_json} (${json.length.toLocaleString()} bytes)`); fs.writeFileSync(paths_json, json); let lib = emitLazy(modules); console.log(`[INFO] ${lib_lazy} (${lib.length.toLocaleString()} bytes)`); fs.writeFileSync(lib_lazy, lib); } main(...process.argv.slice(2));