-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathindex.ts
149 lines (123 loc) · 6 KB
/
index.ts
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
#! /usr/bin/env node
/*
* SPDX-FileCopyrightText: 2022 Synaptic Simulations and its contributors
* SPDX-License-Identifier: MIT
*/
import fs from "node:fs";
import path from "node:path";
import chalk from "chalk";
import { Command } from "commander";
import dotenv from "dotenv";
// @ts-ignore FIXME: remove the ignore when/if https://github.com/antitoxic/import-single-ts/pull/7 is merged
import { importSingleTs } from "import-single-ts";
import signale from "signale";
import { fromError } from "zod-validation-error";
import { description, version } from "./package.json";
import { machBuild, machWatch } from "./src/mach";
import { MachArgsSchema, MachConfigSchema } from "./src/types";
const CONFIG_FILENAMES = ["mach.config.js", "mach.config.ts"];
try {
dotenv.config();
} catch {
// .env is optional, but dotenv throws an error if it cannot load it
}
const cli = new Command();
const logger = new signale.Signale({
types: {
error: { badge: "×", label: "error", color: "red", stream: process.stderr },
},
});
const commandWithOptions = (name: string) =>
cli
.command(name)
.option("-c, --config <filename>", "specify path to configuration file")
.option("-d, --work-in-config-dir", "use config directory as working directory")
.option("-b, --bundles <dirname>", "bundles output directory", "./bundles")
.option("-e, --werror", "makes all warnings into errors")
.option("-f, --filter <regex>", "regex filter of included instrument names")
.option("-m, --minify", "minify bundle code")
.option("-s, --skip-simulator-package", "skips writing simulator package templates")
.option("-t, --output-metafile", "output `build_meta.json` file to bundles directory")
.option("-u, --output-sourcemaps", "append sourcemaps to the end of bundle files")
.option("-v, --verbose", "output additional build information")
.hook("preAction", async (_thisCommand, actionCommand) => {
signale.info(`Welcome to ${chalk.cyanBright("Mach")}, v${version}`);
const filter = actionCommand.getOptionValue("filter");
if (filter) {
actionCommand.setOptionValue("filter", new RegExp(filter));
}
// Load config
let config = actionCommand.getOptionValue("config") ?? CONFIG_FILENAMES.find((file) => fs.existsSync(file));
if (!config) {
signale.error("Configuration file not found, consider using `-c` or `--config` to specify the path");
process.exit(1);
}
config = path.resolve(config).replace(/\\/g, "/");
// FIXME: remove the type cast when/if https://github.com/antitoxic/import-single-ts/pull/7 is merged
await (config.endsWith(".ts") ? (importSingleTs(config) as Promise<never>) : import(config))
.then((module) => {
// Check config integrity
const result = MachConfigSchema.safeParse(module.default);
if (result.success) {
actionCommand.setOptionValue("config", result.data);
} else {
logger.error("Invalid config file", chalk.redBright(config));
logger.error(chalk.white(fromError(result.error)));
process.exit(1);
}
logger.info("Loaded config file", chalk.cyanBright(config), "\n");
if (actionCommand.getOptionValue("workInConfigDir")) {
process.chdir(path.dirname(config));
}
})
.catch((error) => {
logger.error("Unable to load config file", chalk.redBright(config));
logger.error(error);
process.exit(1);
});
});
cli.name("mach").version(version).description(description);
commandWithOptions("build")
.description("compile instruments specified in configuration file")
.action((args) => {
const parsedArgs = MachArgsSchema.parse(args);
const instruments = parsedArgs.filter
? parsedArgs.config.instruments.filter((instrument) => parsedArgs.filter!.test(instrument.name))
: parsedArgs.config.instruments;
signale.start(`Building ${instruments.length} instrument${instruments.length !== 1 ? "s" : ""}\n`);
const startTime = performance.now();
machBuild(instruments, parsedArgs).then((results) => {
const stopTime = performance.now();
const numSuccess = results.filter(({ status }) => status === "fulfilled").length;
const numFailed = instruments.length - numSuccess;
if (numSuccess > 0) {
signale.success(
`Built ${numSuccess} instrument${instruments.length !== 1 ? "s" : ""} in`,
chalk.greenBright(`${(stopTime - startTime).toFixed()} ms`),
"\n",
);
}
if (numFailed > 0) {
signale.error(`${instruments.length} instrument${instruments.length !== 1 ? "s" : ""} failed to build`);
process.exit(1);
}
});
});
commandWithOptions("watch")
.description("watch instruments for changes and re-compile bundles when updated")
.action((args) => {
const parsedArgs = MachArgsSchema.parse(args);
const instruments = parsedArgs.filter
? parsedArgs.config.instruments.filter((instrument) => parsedArgs.filter!.test(instrument.name))
: args.config.instruments;
machWatch(instruments, parsedArgs).then((results) => {
if (results.some(({ status }) => status === "rejected")) {
signale.error("Watch mode requires a successful build to initialize");
process.exit(1);
}
signale.watch("Watching for changes\n");
});
});
cli.parse();
export { machBuild, machWatch } from "./src/mach";
export * from "./src/types";