summaryrefslogtreecommitdiffstats
path: root/tools/ts/mach_commands.py
blob: 5e933b463b276026501981fdac59a74fe9e7d827 (plain)
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
# 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 json
import os

import mozpack.path as mozpath
from mach.decorators import Command, CommandArgument, SubCommand


@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=["nsresult", "services", "xpcom"])
def buld(ctx, lib):
    """Command to build one of the typelibs."""

    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_json = mozpath.join(ctx.topsrcdir, "tools/ts/error_list.json")
        return node(ctx, "build_nsresult", lib_dts, xpc_msg, errors_json)

    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 len(files):
            return build_required(lib, f"*.d.json files in {dir}")

        return node(ctx, "build_xpcom", 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 lib references.")
def update(ctx):
    types_dir = mozpath.join(ctx.distdir, "@types")
    index_dts = mozpath.join(ctx.topsrcdir, "tools/@types/index.d.ts")
    return node(ctx, "update_refs", index_dts, types_dir)


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):
    """Check if npm modules are installed, and run setup if needed."""
    dir = mozpath.dirname(__file__)
    package_json = json.load(open(mozpath.join(dir, "package.json")))
    needs_setup = False

    # TODO: Use proper version checking from tools/lint/eslint/setup_helper.py.
    for module in package_json.get("devDependencies", {}):
        path = mozpath.join(dir, "node_modules", module, "package.json")
        if not os.path.isfile(path):
            print(f"Missing node module {path}.")
            needs_setup = True
            break

    if needs_setup:
        print("Running npm install.")
        setup(ctx)


def build_required(lib, item):
    print(f"Missing {item}.")
    print(f"Building {lib} typelib requires a full Firefox build.")
    return 1