summaryrefslogtreecommitdiffstats
path: root/browser/components/storybook/mach_commands.py
blob: 7c8a3dd1d854398d9b8d82a4db7c92df519231ff (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
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
# 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, SubCommand
from mozpack.manifests import InstallManifest


def run_mach(command_context, cmd, **kwargs):
    return command_context._mach_context.commands.dispatch(
        cmd, command_context._mach_context, **kwargs
    )


def run_npm(command_context, args):
    return run_mach(
        command_context, "npm", args=[*args, "--prefix=browser/components/storybook"]
    )


@Command(
    "storybook",
    category="misc",
    description="Start the Storybook server",
)
def storybook_run(command_context):
    storybook_manifest(command_context)
    return run_npm(command_context, args=["run", "storybook"])


@SubCommand("storybook", "launch", description="Start Storybook in your local build.")
def storybook_launch(command_context):
    return run_mach(command_context, "run", argv=["http://localhost:5703"])


@SubCommand(
    "storybook",
    "install",
    description="Install Storybook node dependencies.",
)
def storybook_install(command_context):
    return run_npm(command_context, args=["ci"])


@SubCommand(
    "storybook",
    "build",
    description="Build the Storybook for export.",
)
def storybook_build(command_context):
    storybook_manifest(command_context)
    return run_npm(command_context, args=["run", "build-storybook"])


@SubCommand(
    "storybook",
    "manifest",
    description="Create rewrites.js which has mappings from chrome:// URIs to local paths. "
    "Requires a ./mach build faster build. Required for our chrome-uri-loader.js webpack loader.",
)
def storybook_manifest(command_context):
    config_environment = command_context.config_environment
    # The InstallManifest object will have mappings of JAR entries to paths on disk.
    unified_manifest = InstallManifest(
        mozpath.join(config_environment.topobjdir, "faster", "unified_install_dist_bin")
    )
    paths = {}

    for dest, entry in unified_manifest._dests.items():
        # dest in the JAR path
        # entry can be many things, but we care about the [1, file_path] form
        #   1 essentially means this is a file
        if (
            entry[0] == 1
            and (dest.endswith(".js") or dest.endswith(".mjs"))
            and (
                dest.startswith("chrome/toolkit/") or dest.startswith("browser/chrome/")
            )
        ):
            try:
                # Try to map the dest to a chrome URI. This could fail for some weird cases that
                # don't seem like they're worth handling.
                chrome_uri = _parse_dest_to_chrome_uri(dest)
                # Since we run through mach we're relative to the project root here.
                paths[chrome_uri] = os.path.relpath(entry[1])
            except Exception as e:
                # Log the files that failed, this could get noisy but the list is short for now.
                print('Error rewriting to chrome:// URI "{}" [{}]'.format(dest, e))
                pass

    with open("browser/components/storybook/.storybook/rewrites.js", "w") as f:
        f.write("module.exports = ")
        json.dump(paths, f, indent=2)


def _parse_dest_to_chrome_uri(dest):
    """Turn a jar destination into a chrome:// URI. Will raise an error on unknown input."""

    global_start = dest.find("global/")
    content_start = dest.find("content/")
    skin_classic_browser = "skin/classic/browser/"
    browser_skin_start = dest.find(skin_classic_browser)
    package, provider, path = "", "", ""

    if global_start != -1:
        # e.g. chrome/toolkit/content/global/vendor/lit.all.mjs
        #      chrome://global/content/vendor/lit.all.mjs
        # If the jar location has global in it, then:
        #   * the package is global,
        #   * the portion after global should be the path,
        #   * the provider is in the path somewhere (we want skin or content).
        package = "global"
        provider = "skin" if "/skin/" in dest else "content"
        path = dest[global_start + len("global/") :]
    elif content_start != -1:
        # e.g. browser/chrome/browser/content/browser/aboutDialog.js
        #      chrome://browser/content/aboutDialog.js
        # e.g. chrome/toolkit/content/mozapps/extensions/shortcuts.js
        #      chrome://mozapps/content/extensions/shortcuts.js
        # If the jar location has content/ in it, then:
        #   * starting from "content/" split on slashes and,
        #   * the provider is "content",
        #   * the package is the next part,
        #   * the path is the remainder.
        provider, package, path = dest[content_start:].split("/", 2)
    elif browser_skin_start != -1:
        # e.g. browser/chrome/browser/skin/classic/browser/browser.css
        #      chrome://browser/skin/browser.css
        # If the jar location has skin/classic/browser/ in it, then:
        #   * the package is browser,
        #   * the provider is skin,
        #   * the path is what remains after sking/classic/browser.
        package = "browser"
        provider = "skin"
        path = dest[browser_skin_start + len(skin_classic_browser) :]

    return "chrome://{package}/{provider}/{path}".format(
        package=package, provider=provider, path=path
    )