1import "vite-plus/test/config";
2// @effect-diagnostics-next-line nodeBuiltinImport:off -- tsdown needs the asset path when it reads this config.
3import * as NodePath from "node:path";
4import { defineConfig, mergeConfig } from "vite-plus";
5
6import baseConfig from "../../vite.config.ts";
7import { loadRepoEnv } from "../../scripts/lib/public-config.ts";
8import rootPackageJson from "../../package.json" with { type: "json" };
9import packageJson from "./package.json" with { type: "json" };
10import { WeightedShardSequencer } from "./src/testUtils/weightedShardSequencer.ts";
11
12// NSIS install time tracks file count, not bytes.
13import {
14 isExternalCliDependency,
15 shouldBundleCliDependency,
16} from "../../scripts/lib/cli-external-packages.ts";
17
18export { shouldBundleCliDependency };
19
20const repoEnv = loadRepoEnv();
21const cliBuildChannel = /^[^-+]+-(?:nightly|preview)\./.test(packageJson.version)
22 ? "nightly"
23 : "latest";
24// Official release workflows set the telemetry channel; local builds and forks use "dev".
25const TELEMETRY_CHANNELS = ["stable", "nightly", "preview", "dev"];
26const telemetryChannel = process.env.T3CODE_TELEMETRY_CHANNEL?.trim() || "dev";
27if (!TELEMETRY_CHANNELS.includes(telemetryChannel)) {
28 throw new Error(
29 `T3CODE_TELEMETRY_CHANNEL must be one of ${TELEMETRY_CHANNELS.join(", ")}, got "${telemetryChannel}".`,
30 );
31}
32
33// tsdown SEA needs one chunk (sourcemaps count) and Node >=25.7, so package separately.
34// "bundle" emits the embedded CLI. "1" builds dist-exe/t3 from the bootstrap.
35// TEMPORARY one-time Node fix. Remove when fixed: https://github.com/nodejs/node/issues/67048
36// "bootstrap" stops at bin.mjs for scripts/cli.ts to inject (linux-arm64).
37const packMode = process.env.T3CODE_PACK_EXE;
38const packBootstrap = packMode === "1" || packMode === "bootstrap";
39const packExecutable = packBootstrap || packMode === "bundle";
40const cliAssetDir = process.env.T3CODE_CLI_ASSET_DIR ?? "";
41if (packExecutable && !cliAssetDir) throw new Error("Build the executable with build:exe.");
42// TEMPORARY one-time Node fix. Remove when fixed: https://github.com/nodejs/node/issues/67048
43// No linux-arm64: --build-sea output cannot load on 16/64 KiB page kernels, so it builds natively.
44const SEA_TARGETS = {
45 "darwin-arm64": { platform: "darwin", arch: "arm64" },
46 "darwin-x64": { platform: "darwin", arch: "x64" },
47 "linux-x64": { platform: "linux", arch: "x64" },
48 "win-arm64": { platform: "win", arch: "arm64" },
49 "win-x64": { platform: "win", arch: "x64" },
50} as const;
51const packExecutableTarget = process.env.T3CODE_PACK_EXE_TARGET?.trim();
52const cliNodeVersion = rootPackageJson.engines.node;
53if (packExecutable && !packExecutableTarget && process.versions.node !== cliNodeVersion) {
54 throw new Error(
55 `Native CLI executables must be built with Node ${cliNodeVersion}; running ${process.versions.node}.`,
56 );
57}
58if (packExecutableTarget && !Object.hasOwn(SEA_TARGETS, packExecutableTarget)) {
59 throw new Error(
60 `T3CODE_PACK_EXE_TARGET must be one of ${Object.keys(SEA_TARGETS).join(", ")}, got "${packExecutableTarget}".`,
61 );
62}
63const packExecutableTargets = packExecutableTarget
64 ? [
65 {
66 ...SEA_TARGETS[packExecutableTarget as keyof typeof SEA_TARGETS],
67 nodeVersion: cliNodeVersion,
68 },
69 ]
70 : undefined;
71
72// The root config's `projects` route repo-root runs to package configs,
73// including this one. Inheriting them would make this config a container.
74const { projects: _rootTestProjects, ...baseTestConfig } = baseConfig.test ?? {};
75// Workspace cache configuration is invalid on a package config.
76const { run: _rootRunConfig, ...basePackageConfig } = baseConfig;
77
78export default mergeConfig(
79 { ...basePackageConfig, test: baseTestConfig },
80 defineConfig({
81 run: {
82 tasks: {
83 build: {
84 command: "node scripts/cli.ts build",
85 dependsOn: ["@t3tools/web#build"],
86 cache: false,
87 },
88 },
89 },
90 pack: {
91 // SEA embeds one entry; expose the history worker as a hidden subcommand.
92 entry: packBootstrap
93 ? ["src/bin.ts"]
94 : packExecutable
95 ? ["src/binMain.ts"]
96 : ["src/bin.ts", "src/claude-history-worker.ts"],
97 outDir: packBootstrap ? "dist-exe" : packExecutable ? cliAssetDir : "dist",
98 sourcemap: !packExecutable,
99 clean: true,
100 // SEA can only resolve built-ins; keep dynamic imports inside each emitted module.
101 ...(packExecutable ? { outputOptions: { codeSplitting: false } } : {}),
102 ...(packMode === "1"
103 ? {
104 exe: {
105 fileName: "t3",
106 outDir: "dist-exe",
107 ...(packExecutableTargets ? { targets: packExecutableTargets } : {}),
108 // Node SEA useCodeCache breaks import(), which the server needs.
109 seaConfig: {
110 useCodeCache: false,
111 // The bootstrap loads the embedded CLI with require(esm).
112 execArgv: ["--require-module"],
113 assets: { "binMain.mjs": NodePath.join(cliAssetDir, "binMain.mjs") },
114 },
115 },
116 }
117 : {}),
118 // jsonc-parser's UMD main requires its own files at runtime, which the bundle cannot follow.
119 alias: { "jsonc-parser": "jsonc-parser/lib/esm/main.js" },
120 deps: {
121 // alwaysBundle=false has no opinion; neverBundle must retain transitive native acceleration.
122 alwaysBundle: shouldBundleCliDependency,
123 neverBundle: (id: string) =>
124 (packBootstrap && id === "./binMain.ts") || isExternalCliDependency(id),
125 onlyBundle: false,
126 },
127 banner: {
128 js: "#!/usr/bin/env node\n",
129 },
130 define: {
131 __T3CODE_BUILD_CHANNEL__: JSON.stringify(cliBuildChannel),
132 __T3CODE_TELEMETRY_CHANNEL__: JSON.stringify(telemetryChannel),
133 __T3CODE_BUILD_RELAY_URL__: JSON.stringify(repoEnv.T3CODE_RELAY_URL?.trim() ?? ""),
134 __T3CODE_BUILD_CLERK_PUBLISHABLE_KEY__: JSON.stringify(
135 repoEnv.T3CODE_CLERK_PUBLISHABLE_KEY?.trim() ?? "",
136 ),
137 __T3CODE_BUILD_CLERK_CLI_OAUTH_CLIENT_ID__: JSON.stringify(
138 repoEnv.T3CODE_CLERK_CLI_OAUTH_CLIENT_ID?.trim() ?? "",
139 ),
140 __T3CODE_BUILD_RELAY_CLIENT_OTLP_TRACES_URL__: JSON.stringify(
141 repoEnv.T3CODE_RELAY_CLIENT_OTLP_TRACES_URL?.trim() ?? "",
142 ),
143 __T3CODE_BUILD_RELAY_CLIENT_OTLP_TRACES_DATASET__: JSON.stringify(
144 repoEnv.T3CODE_RELAY_CLIENT_OTLP_TRACES_DATASET?.trim() ?? "",
145 ),
146 __T3CODE_BUILD_RELAY_CLIENT_OTLP_TRACES_TOKEN__: JSON.stringify(
147 repoEnv.T3CODE_RELAY_CLIENT_OTLP_TRACES_TOKEN?.trim() ?? "",
148 ),
149 },
150 },
151 test: {
152 // Parallel sqlite/git/worktree/orchestration tests introduce load-sensitive flakes.
153 fileParallelism: false,
154 sequence: { sequencer: WeightedShardSequencer },
155 setupFiles: [
156 "./src/testUtils/gitConfig.setup.ts",
157 "./src/testUtils/managedServiceEnv.setup.ts",
158 ],
159 hookTimeout: 120_000,
160 testTimeout: 120_000,
161 },
162 }),
163);