| 1 | import "vite-plus/test/config"; |
| 2 | import { defineConfig, mergeConfig } from "vite-plus"; |
| 3 | |
| 4 | import baseConfig from "../../vite.config.ts"; |
| 5 | import { loadRepoEnv } from "../../scripts/lib/public-config.ts"; |
| 6 | import packageJson from "./package.json" with { type: "json" }; |
| 7 | import { WeightedShardSequencer } from "./src/testUtils/weightedShardSequencer.ts"; |
| 8 | |
| 9 | // The bundle used to inline only workspace packages, leaving every third-party |
| 10 | // runtime dep external. External deps must exist on the real filesystem (the WSL |
| 11 | // backend runs plain `wsl.exe -- node`, which cannot read inside an asar), so the |
| 12 | // desktop build unpacked `**\/node_modules\/**` wholesale: 13,875 loose files to |
| 13 | // support 20 native binaries. NSIS install time tracks file count, not bytes. |
| 14 | // |
| 15 | // Inverted here — bundle everything except the packages that genuinely cannot be |
| 16 | // inlined. See scripts/lib/cli-external-packages.ts for what earns an exemption. |
| 17 | import { |
| 18 | isExternalCliDependency, |
| 19 | shouldBundleCliDependency, |
| 20 | } from "../../scripts/lib/cli-external-packages.ts"; |
| 21 | |
| 22 | export { shouldBundleCliDependency }; |
| 23 | |
| 24 | const repoEnv = loadRepoEnv(); |
| 25 | const cliBuildChannel = /^[^-+]+-(?:nightly|preview)\./.test(packageJson.version) |
| 26 | ? "nightly" |
| 27 | : "latest"; |
| 28 | |
| 29 | // `build:exe` wraps the same bundle in a Node single-executable. tsdown's exe |
| 30 | // step refuses multi-chunk output and counts the sourcemap as a chunk, and the |
| 31 | // executable needs a host Node that supports `--build-sea` (25.7+), so this is |
| 32 | // a separate mode rather than a second entry in the default build. |
| 33 | const packExecutable = process.env.T3CODE_PACK_EXE === "1"; |
| 34 | // `<platform>-<arch>` in nodejs.org naming (darwin-x64, linux-arm64, win-x64). |
| 35 | // When set, tsdown injects the bundle into a downloaded Node of that target |
| 36 | // instead of the host Node, which is how the arm64 macOS runner produces the |
| 37 | // x64 archive. Cross-building is safe because the code cache is off. |
| 38 | // |
| 39 | // The Node inside the executable is pinned here rather than taken from the |
| 40 | // build host, so every archive of a release embeds the same runtime no matter |
| 41 | // which Node happens to run the build. |
| 42 | const SEA_NODE_VERSION = "26.8.2"; |
| 43 | const SEA_TARGETS = { |
| 44 | "darwin-arm64": { platform: "darwin", arch: "arm64" }, |
| 45 | "darwin-x64": { platform: "darwin", arch: "x64" }, |
| 46 | "linux-arm64": { platform: "linux", arch: "arm64" }, |
| 47 | "linux-x64": { platform: "linux", arch: "x64" }, |
| 48 | "win-arm64": { platform: "win", arch: "arm64" }, |
| 49 | "win-x64": { platform: "win", arch: "x64" }, |
| 50 | } as const; |
| 51 | const packExecutableTarget = process.env.T3CODE_PACK_EXE_TARGET?.trim(); |
| 52 | if (packExecutableTarget && !Object.hasOwn(SEA_TARGETS, packExecutableTarget)) { |
| 53 | throw new Error( |
| 54 | `T3CODE_PACK_EXE_TARGET must be one of ${Object.keys(SEA_TARGETS).join(", ")}, got "${packExecutableTarget}".`, |
| 55 | ); |
| 56 | } |
| 57 | const packExecutableTargets = packExecutableTarget |
| 58 | ? [ |
| 59 | { |
| 60 | ...SEA_TARGETS[packExecutableTarget as keyof typeof SEA_TARGETS], |
| 61 | nodeVersion: SEA_NODE_VERSION, |
| 62 | }, |
| 63 | ] |
| 64 | : undefined; |
| 65 | |
| 66 | export default mergeConfig( |
| 67 | baseConfig, |
| 68 | defineConfig({ |
| 69 | run: { |
| 70 | tasks: { |
| 71 | build: { |
| 72 | command: "node scripts/cli.ts build", |
| 73 | dependsOn: ["@t3tools/web#build"], |
| 74 | cache: false, |
| 75 | }, |
| 76 | }, |
| 77 | }, |
| 78 | pack: { |
| 79 | // The executable embeds one entry; the history worker becomes a hidden |
| 80 | // subcommand there instead of a sibling script. |
| 81 | entry: packExecutable ? ["src/bin.ts"] : ["src/bin.ts", "src/claude-history-worker.ts"], |
| 82 | outDir: packExecutable ? "dist-exe" : "dist", |
| 83 | sourcemap: !packExecutable, |
| 84 | clean: true, |
| 85 | ...(packExecutable |
| 86 | ? { |
| 87 | exe: { |
| 88 | fileName: "t3", |
| 89 | outDir: "dist-exe", |
| 90 | ...(packExecutableTargets ? { targets: packExecutableTargets } : {}), |
| 91 | // Node's SEA docs: `import()` does not work when useCodeCache is |
| 92 | // true, and the server reaches several modules that way. The |
| 93 | // cache is also platform-bound, so leaving it off keeps the |
| 94 | // build correct on any host. |
| 95 | seaConfig: { useCodeCache: false }, |
| 96 | }, |
| 97 | } |
| 98 | : {}), |
| 99 | deps: { |
| 100 | // Both halves are required. `alwaysBundle` forces the JS dependencies in |
| 101 | // (declared deps are external by default, which is what this change is |
| 102 | // undoing). `neverBundle` forces the native packages out: returning |
| 103 | // false from `alwaysBundle` only means "no opinion", so a transitive |
| 104 | // dependency would still be bundled — which silently inlined native |
| 105 | // loaders such as node-gyp-build, losing native acceleration. |
| 106 | alwaysBundle: shouldBundleCliDependency, |
| 107 | neverBundle: (id: string) => isExternalCliDependency(id), |
| 108 | onlyBundle: false, |
| 109 | }, |
| 110 | banner: { |
| 111 | js: "#!/usr/bin/env node\n", |
| 112 | }, |
| 113 | define: { |
| 114 | __T3CODE_BUILD_CHANNEL__: JSON.stringify(cliBuildChannel), |
| 115 | __T3CODE_BUILD_RELAY_URL__: JSON.stringify(repoEnv.T3CODE_RELAY_URL?.trim() ?? ""), |
| 116 | __T3CODE_BUILD_CLERK_PUBLISHABLE_KEY__: JSON.stringify( |
| 117 | repoEnv.T3CODE_CLERK_PUBLISHABLE_KEY?.trim() ?? "", |
| 118 | ), |
| 119 | __T3CODE_BUILD_CLERK_CLI_OAUTH_CLIENT_ID__: JSON.stringify( |
| 120 | repoEnv.T3CODE_CLERK_CLI_OAUTH_CLIENT_ID?.trim() ?? "", |
| 121 | ), |
| 122 | }, |
| 123 | }, |
| 124 | test: { |
| 125 | // The server suite exercises sqlite, git, temp worktrees, and orchestration |
| 126 | // runtimes heavily. Running files in parallel introduces load-sensitive flakes. |
| 127 | fileParallelism: false, |
| 128 | // CI runs the suite as `--shard` runs of equal recorded duration. |
| 129 | sequence: { sequencer: WeightedShardSequencer }, |
| 130 | // Appended to the root setup, which mergeConfig concatenates. |
| 131 | setupFiles: ["./src/testUtils/gitConfig.setup.ts"], |
| 132 | // Server integration tests exercise sqlite, git, and orchestration together. |
| 133 | // Under package-wide runs they can exceed the default budget on loaded CI hosts. |
| 134 | hookTimeout: 120_000, |
| 135 | testTimeout: 120_000, |
| 136 | }, |
| 137 | }), |
| 138 | ); |