Skip to content

Commit 564f310

Browse files
committed
add analyze command
1 parent 734ebc3 commit 564f310

File tree

2 files changed

+152
-0
lines changed

2 files changed

+152
-0
lines changed

packages/cli-v3/src/cli/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { configureLogoutCommand } from "../commands/logout.js";
66
import { configureWhoamiCommand } from "../commands/whoami.js";
77
import { COMMAND_NAME } from "../consts.js";
88
import { configureListProfilesCommand } from "../commands/list-profiles.js";
9+
import { configureAnalyzeCommand } from "../commands/analyze.js";
910
import { configureUpdateCommand } from "../commands/update.js";
1011
import { VERSION } from "../version.js";
1112
import { configureDeployCommand } from "../commands/deploy.js";
@@ -34,6 +35,7 @@ configureListProfilesCommand(program);
3435
configureSwitchProfilesCommand(program);
3536
configureUpdateCommand(program);
3637
configurePreviewCommand(program);
38+
configureAnalyzeCommand(program);
3739
// configureWorkersCommand(program);
3840
// configureTriggerTaskCommand(program);
3941

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import { Command } from "commander";
2+
import { z } from "zod";
3+
import { CommonCommandOptions, handleTelemetry, wrapCommandAction } from "../cli/common.js";
4+
import { printInitialBanner } from "../utilities/initialBanner.js";
5+
import { logger } from "../utilities/logger.js";
6+
import { printBundleTree, printBundleSummaryTable } from "../utilities/analyze.js";
7+
import path from "node:path";
8+
import fs from "node:fs";
9+
import { readJSONFile } from "../utilities/fileSystem.js";
10+
import { WorkerManifest } from "@trigger.dev/core/v3";
11+
import { tryCatch } from "@trigger.dev/core";
12+
13+
const AnalyzeOptions = CommonCommandOptions.pick({
14+
logLevel: true,
15+
skipTelemetry: true,
16+
}).extend({
17+
verbose: z.boolean().optional().default(false),
18+
});
19+
20+
type AnalyzeOptions = z.infer<typeof AnalyzeOptions>;
21+
22+
export function configureAnalyzeCommand(program: Command) {
23+
return program
24+
.command("analyze [dir]", { hidden: true })
25+
.description("Analyze your build output (bundle size, timings, etc)")
26+
.option(
27+
"-l, --log-level <level>",
28+
"The CLI log level to use (debug, info, log, warn, error, none). This does not effect the log level of your trigger.dev tasks.",
29+
"log"
30+
)
31+
.option("--skip-telemetry", "Opt-out of sending telemetry")
32+
.option("--verbose", "Show detailed bundle tree (do not collapse bundles)")
33+
.action(async (dir, options) => {
34+
await handleTelemetry(async () => {
35+
await analyzeCommand(dir, options);
36+
});
37+
});
38+
}
39+
40+
export async function analyzeCommand(dir: string | undefined, options: unknown) {
41+
return await wrapCommandAction("analyze", AnalyzeOptions, options, async (opts) => {
42+
await printInitialBanner(false);
43+
return await analyze(dir, opts);
44+
});
45+
}
46+
47+
export async function analyze(dir: string | undefined, options: AnalyzeOptions) {
48+
const cwd = process.cwd();
49+
const targetDir = dir ? path.resolve(cwd, dir) : cwd;
50+
const metafilePath = path.join(targetDir, "metafile.json");
51+
const manifestPath = path.join(targetDir, "index.json");
52+
53+
if (!fs.existsSync(metafilePath)) {
54+
logger.error(`Could not find metafile.json in ${targetDir}`);
55+
logger.info("Make sure you have built your project and metafile.json exists.");
56+
return;
57+
}
58+
if (!fs.existsSync(manifestPath)) {
59+
logger.error(`Could not find index.json (worker manifest) in ${targetDir}`);
60+
logger.info("Make sure you have built your project and index.json exists.");
61+
return;
62+
}
63+
64+
const [metafileError, metafile] = await tryCatch(readMetafile(metafilePath));
65+
66+
if (metafileError) {
67+
logger.error(`Failed to parse metafile.json: ${metafileError.message}`);
68+
return;
69+
}
70+
71+
const [manifestError, manifest] = await tryCatch(readManifest(manifestPath));
72+
73+
if (manifestError) {
74+
logger.error(`Failed to parse index.json: ${manifestError.message}`);
75+
return;
76+
}
77+
78+
printBundleTree(manifest, metafile, {
79+
preservePath: true,
80+
collapseBundles: !options.verbose,
81+
});
82+
83+
printBundleSummaryTable(manifest, metafile, {
84+
preservePath: true,
85+
});
86+
}
87+
88+
async function readMetafile(metafilePath: string): Promise<Metafile> {
89+
const json = await readJSONFile(metafilePath);
90+
const metafile = MetafileSchema.parse(json);
91+
return metafile;
92+
}
93+
94+
async function readManifest(manifestPath: string): Promise<WorkerManifest> {
95+
const json = await readJSONFile(manifestPath);
96+
const manifest = WorkerManifest.parse(json);
97+
return manifest;
98+
}
99+
100+
const ImportKind = z.enum([
101+
"entry-point",
102+
"import-statement",
103+
"require-call",
104+
"dynamic-import",
105+
"require-resolve",
106+
"import-rule",
107+
"composes-from",
108+
"url-token",
109+
]);
110+
111+
const ImportSchema = z.object({
112+
path: z.string(),
113+
kind: ImportKind,
114+
external: z.boolean().optional(),
115+
original: z.string().optional(),
116+
with: z.record(z.string()).optional(),
117+
});
118+
119+
const InputSchema = z.object({
120+
bytes: z.number(),
121+
imports: z.array(ImportSchema),
122+
format: z.enum(["cjs", "esm"]).optional(),
123+
with: z.record(z.string()).optional(),
124+
});
125+
126+
const OutputImportSchema = z.object({
127+
path: z.string(),
128+
kind: z.union([ImportKind, z.literal("file-loader")]),
129+
external: z.boolean().optional(),
130+
});
131+
132+
const OutputInputSchema = z.object({
133+
bytesInOutput: z.number(),
134+
});
135+
136+
const OutputSchema = z.object({
137+
bytes: z.number(),
138+
inputs: z.record(z.string(), OutputInputSchema),
139+
imports: z.array(OutputImportSchema),
140+
exports: z.array(z.string()),
141+
entryPoint: z.string().optional(),
142+
cssBundle: z.string().optional(),
143+
});
144+
145+
const MetafileSchema = z.object({
146+
inputs: z.record(z.string(), InputSchema),
147+
outputs: z.record(z.string(), OutputSchema),
148+
});
149+
150+
type Metafile = z.infer<typeof MetafileSchema>;

0 commit comments

Comments
 (0)