1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
|
# 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/.
import os
import shutil
import sys
import mozpack.path as mozpath
from mach.decorators import Command, CommandArgument, SubCommand
targets = ["dom", "nsresult", "services", "xpcom"]
@Command("ts", category="misc", description="Run TypeScript and related commands.")
def ts(ctx):
"""
TypeScript related commands to build/update typelibs and type-check js.
Example:
# Use tsc to check types in extensions framework code:
$ ./mach ts check toolkit/components/extensions
"""
ctx._sub_mach(["help", "ts"])
return 1
@SubCommand("ts", "build", description="Build typelibs.")
@CommandArgument("lib", choices=targets, nargs="?")
def build(ctx, lib):
"""Command to build the target typelibs."""
if lib is None:
for t in targets:
if rv := build(ctx, t):
return rv
return 0
types_dir = mozpath.join(ctx.distdir, "@types")
lib_dts = mozpath.join(types_dir, f"lib.gecko.{lib}.d.ts")
if not os.path.exists(types_dir):
os.makedirs(types_dir)
if lib == "nsresult":
xpc_msg = mozpath.join(ctx.topsrcdir, "js/xpconnect/src/xpc.msg")
errors_obj = mozpath.join(ctx.topobjdir, "xpcom/base/error_list.json")
errors_src = mozpath.join(ctx.topsrcdir, "tools/ts/config/error_list.json")
if os.path.exists(errors_obj):
print(f"[INFO] {errors_obj} -> {errors_src}")
shutil.copy(errors_obj, errors_src)
return node(ctx, "build_nsresult", lib_dts, xpc_msg, errors_src)
if lib == "services":
services_json = mozpath.join(ctx.topobjdir, "xpcom/components/services.json")
if not os.path.isfile(services_json):
return build_required(lib, services_json)
return node(ctx, "build_services", lib_dts, services_json)
if lib == "xpcom":
# When we hook this up to be part of the build, we'll have
# an explicit list of files. For now, just get all of them.
dir = mozpath.join(ctx.topobjdir, "config/makefiles/xpidl")
if not os.path.isdir(dir):
return build_required(lib, dir)
files = [f for f in os.listdir(dir) if f.endswith(".d.json")]
if not files:
return build_required(lib, f"*.d.json files in {dir}")
return node(ctx, "build_xpcom", lib_dts, dir, *files)
if lib == "dom":
# Same as above, get all *.webidl files for now.
dir = mozpath.join(ctx.topsrcdir, "dom")
files = []
for subdir in ["webidl", "chrome-webidl"]:
for file in os.listdir(mozpath.join(dir, subdir)):
if file.endswith(".webidl"):
files.append(subdir + "/" + file)
return node(ctx, "build_dom", lib_dts, dir, *files)
raise ValueError(f"Unknown typelib: {lib}")
@SubCommand("ts", "check", description="Check types in a project using tsc.")
@CommandArgument("paths", nargs="+", help="Path to a (dir with) tsconfig.json.")
def check(ctx, paths):
for p in paths:
rv = node(ctx, "node_modules/typescript/bin/tsc", "--project", p)
if rv:
return rv
return 0
@SubCommand("ts", "setup", description="Install TypeScript and other dependencies.")
def setup(ctx):
# Install locally under tools/ts/node_modules, to avoid any conflicts for now.
os.chdir(mozpath.dirname(__file__))
return ctx._sub_mach(["npm", "ci"])
@SubCommand("ts", "update", description="Update tools/@types libraries.")
def update(ctx):
typelib_dir = mozpath.join(ctx.topsrcdir, "tools/@types/generated")
platforms = ["darwin", "linux", "win32"]
retval = 0
for lib in targets + platforms + ["glean"]:
file = f"lib.gecko.{lib}.d.ts"
path = mozpath.join(ctx.distdir, "@types", file)
if not os.path.exists(path):
if not lib in platforms:
print(f"[ERROR] {path} not found. Did you run `mach ts build`?")
retval = 1
continue
# This command inherently goes in a confusing direction, we're copying:
# from `<topobjdir>/dist/@types` files generated with `mach ts build`,
# into `<topsrcdir>/tools/@types` typelib dir back in your source tree.
print(f"[INFO] Updating {typelib_dir}/{file}")
shutil.copy(path, typelib_dir)
print("[WARNING] Your source tree was updated, you should commit the changes.")
return retval
@SubCommand("ts", "paths", description="Build module path mapping.")
def paths(ctx):
maybe_setup(ctx)
lib = mozpath.join(ctx.topsrcdir, "tools/@types/generated/tspaths.json")
lazy = mozpath.join(ctx.topsrcdir, "tools/@types/generated/lib.gecko.modules.d.ts")
return node(ctx, "build_paths", ctx.topsrcdir, lib, lazy)
@SubCommand("ts", "subs", description="Emit substitution .d.ts for processed sources.")
def subs(ctx):
maybe_setup(ctx)
processed = [
# AppConstants.sys.mjs has a (better) manually created declaration file.
"dist/bin/browser/modules/policies/schema.sys.mjs",
"dist/bin/modules/Readerable.sys.mjs",
"toolkit/components/nimbus/FeatureManifest.sys.mjs",
"toolkit/components/promiseworker/worker/PromiseWorker.js",
"toolkit/components/promiseworker/worker/PromiseWorker.mjs",
"toolkit/components/resistfingerprinting/RFPTargetConstants.sys.mjs",
]
args = ["--declaration", "--emitDeclarationOnly", "--allowJs", "--outDir"]
subs_dir = mozpath.join(ctx.topsrcdir, "tools/@types/subs")
for file in processed:
path = mozpath.join(ctx.topobjdir, file)
print(f"[INFO] {path} -> {subs_dir}/{mozpath.basename(path)}")
node(ctx, "node_modules/typescript/bin/tsc", *args, subs_dir, path)
def node(ctx, script, *args):
maybe_setup(ctx)
path = mozpath.join(mozpath.dirname(__file__), script)
return ctx._sub_mach(["node", path, *args])
def maybe_setup(ctx):
sys.path.append(mozpath.join(ctx.topsrcdir, "tools", "lint", "eslint"))
import setup_helper
if not setup_helper.check_node_executables_valid():
return 1
"""Check if npm modules are installed, and run setup if needed."""
dir = mozpath.dirname(__file__)
setup_helper.eslint_maybe_setup(dir, "TypeScript")
def build_required(lib, item):
print(f"Missing {item}.")
print(f"Building {lib} typelib requires a full Firefox build.")
return 1
|