chromium/ash/webui/recorder_app_ui/resources/scripts/cra/build.py

# Copyright 2024 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import ast
import json
import pathlib
import re

from cra import util
import gen_images_js as gen_images_js_module


def gen_images_js() -> str:
    images_base = util.get_cra_root() / "images"

    with open(images_base / "images.gni") as f:
        gn = f.read()
        match = re.search(r"images\s*=\s*(\[.*?\])", gn, re.DOTALL)
        assert match is not None
        images = ast.literal_eval(match.group(1))
        match = re.search(r"icons\s*=\s*(\[.*?\])", gn, re.DOTALL)
        assert match is not None
        icons = ast.literal_eval(match.group(1))
        images += [f"icons/{icon}" for icon in icons]

    images = [images_base / image for image in images]

    return gen_images_js_module.gen_images_js(images, images_base)


# TODO(pihsun): This & _get_tsc_references is getting more and more
# complicated. Maybe we should just grab the built tsconfig_build_ts.json and
# modify the path to point to the correct location instead.
def _get_tsc_paths(build_dir: pathlib.Path) -> dict[str, list[str]]:
    root_dir = util.get_chromium_root()
    images_dir = util.get_cra_root() / "images"

    resources_dir = build_dir / "gen/ui/webui/resources/tsc"

    mwc_dir = root_dir / "third_party/material_web_components"
    lit_d_ts = mwc_dir / "lit_exports.d.ts"
    mwc_components_dir = mwc_dir / "components-chromium/node_modules/@material"

    cros_components_dir = resources_dir / "cros_components/to_be_rewritten"

    return {
        "//resources/*": [str(resources_dir / "*")],
        "chrome://resources/*": [str(resources_dir / "*")],
        "chrome://resources/mwc/lit/index.js": [str(lit_d_ts)],
        "chrome://resources/mwc/@material/*": [str(mwc_components_dir / "*")],
        "chrome://resources/cros_components/*":
        [str(cros_components_dir / "*")],
        "/images/*": [str(images_dir / "*")],
    }


def _make_mojom_symlink(build_dir: pathlib.Path):
    cra_root = util.get_cra_root()
    root_dir = util.get_chromium_root()

    preprocessed_mojo_dir = build_dir / "gen" / cra_root.relative_to(
        root_dir) / "preprocessed/mojom"
    cra_mojo_dir = cra_root / "mojom"

    assert preprocessed_mojo_dir.exists(), (
        f"{preprocessed_mojo_dir} doesn't exist, "
        "is Chrome compiled at least once?")

    if cra_mojo_dir.is_symlink():
        if cra_mojo_dir.readlink() != preprocessed_mojo_dir:
            # There's a symlink here that's not pointing to the correct path.
            # This might happen when changing build_dir. Remove the symlink and
            # recreate in this case.
            cra_mojo_dir.unlink()
            cra_mojo_dir.symlink_to(preprocessed_mojo_dir)
    elif cra_mojo_dir.exists():
        # Some other things are at the mojom path. cra.py won't work in
        # this case.
        raise Exception("resources/mojom exists but not a symlink."
                        " Please remove it and try again.")
    else:
        cra_mojo_dir.symlink_to(preprocessed_mojo_dir)


def _get_tsc_references(build_dir: pathlib.Path) -> list[dict[str, str]]:
    mwc_gen_dir = build_dir / "gen/third_party/material_web_components/"
    cros_components_tsconfig = (
        build_dir /
        "gen/third_party/cros-components/tsconfig_cros_components_ts.json")

    return [{
        "path": str(mwc_gen_dir / "tsconfig_library.json")
    }, {
        "path": str(mwc_gen_dir / "tsconfig_bundle_lit_ts.json")
    }, {
        "path": str(cros_components_tsconfig)
    }]


def generate_tsconfig(build_dir: pathlib.Path):
    # TODO(pihsun): This needs to be in sync with BUILD.gn, have some heuristic
    # to get the dependency from there or from the generated tsconfig.json
    # instead?
    cra_root = util.get_cra_root()

    with open(cra_root / "tsconfig_base.json") as f:
        tsconfig = json.load(f)

    _make_mojom_symlink(build_dir)

    tsconfig["files"] = [str(p) for p in cra_root.glob("**/*.ts")]
    tsconfig["compilerOptions"]["rootDir"] = str(cra_root)
    tsconfig["compilerOptions"]["noEmit"] = True
    tsconfig["compilerOptions"]["paths"] = _get_tsc_paths(build_dir)
    tsconfig["compilerOptions"]["plugins"] = [{
        "name": "ts-lit-plugin",
        "strict": True
    }]
    tsconfig["references"] = _get_tsc_references(build_dir)

    with open(cra_root / "tsconfig.json", "w") as f:
        json.dump(tsconfig, f)