diff --git a/packages/cli/src/__tests__/studio-runtime.test.ts b/packages/cli/src/__tests__/studio-runtime.test.ts index e5f6d95..c362dcb 100644 --- a/packages/cli/src/__tests__/studio-runtime.test.ts +++ b/packages/cli/src/__tests__/studio-runtime.test.ts @@ -12,6 +12,32 @@ describe("studio runtime resolution", () => { vi.resetModules(); }); + it("prefers the repository-local tsx loader for monorepo sources", async () => { + accessMock.mockImplementation(async (path: string) => { + if ( + path === "/repo/packages/studio/src/api/index.ts" || + path === "/repo/packages/studio/node_modules/tsx/dist/loader.mjs" + ) { + return; + } + throw new Error(`missing: ${path}`); + }); + + const { resolveStudioLaunch } = await import("../commands/studio.js"); + const launch = await resolveStudioLaunch("/repo/test-project"); + + expect(launch).toEqual({ + studioEntry: "/repo/packages/studio/src/api/index.ts", + command: "node", + args: [ + "--import", + "/repo/packages/studio/node_modules/tsx/dist/loader.mjs", + "/repo/packages/studio/src/api/index.ts", + "/repo/test-project", + ], + }); + }); + it("finds monorepo packages/studio sources from a project directory", async () => { accessMock.mockImplementation(async (path: string) => { if (path === "/repo/packages/studio/src/api/index.ts") { diff --git a/packages/cli/src/commands/studio.ts b/packages/cli/src/commands/studio.ts index 11bdaa6..7c46ab6 100644 --- a/packages/cli/src/commands/studio.ts +++ b/packages/cli/src/commands/studio.ts @@ -1,12 +1,12 @@ import { Command } from "commander"; import { findProjectRoot, log, logError } from "../utils.js"; import { spawn } from "node:child_process"; -import { join } from "node:path"; +import { dirname, join } from "node:path"; import { access } from "node:fs/promises"; export interface StudioLaunchSpec { readonly studioEntry: string; - readonly command: "npx" | "node"; + readonly command: string; readonly args: string[]; } @@ -29,6 +29,28 @@ export async function resolveStudioLaunch(root: string): Promise=18'} + get-tsconfig@4.13.7: + resolution: {integrity: sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -2318,6 +2324,9 @@ packages: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + restore-cursor@5.1.0: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} @@ -2557,6 +2566,11 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsx@4.21.0: + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} + hasBin: true + tw-animate-css@1.4.0: resolution: {integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==} @@ -3484,12 +3498,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.2.1 '@tailwindcss/oxide-win32-x64-msvc': 4.2.1 - '@tailwindcss/vite@4.2.1(vite@6.4.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1))': + '@tailwindcss/vite@4.2.1(vite@6.4.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0))': dependencies: '@tailwindcss/node': 4.2.1 '@tailwindcss/oxide': 4.2.1 tailwindcss: 4.2.1 - vite: 6.4.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0) '@ts-morph/common@0.27.0': dependencies: @@ -3554,7 +3568,7 @@ snapshots: '@types/validate-npm-package-name@4.0.2': {} - '@vitejs/plugin-react@4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1))': + '@vitejs/plugin-react@4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) @@ -3562,7 +3576,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.27 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 6.4.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0) transitivePeerDependencies: - supports-color @@ -3574,14 +3588,14 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(msw@2.12.13(@types/node@22.19.15)(typescript@5.9.3))(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1))': + '@vitest/mocker@3.2.4(msw@2.12.13(@types/node@22.19.15)(typescript@5.9.3))(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 2.12.13(@types/node@22.19.15)(typescript@5.9.3) - vite: 7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1) + vite: 7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0) '@vitest/pretty-format@3.2.4': dependencies: @@ -4224,6 +4238,10 @@ snapshots: '@sec-ant/readable-stream': 0.4.1 is-stream: 4.0.1 + get-tsconfig@4.13.7: + dependencies: + resolve-pkg-maps: 1.0.0 + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -4767,6 +4785,8 @@ snapshots: resolve-from@4.0.0: {} + resolve-pkg-maps@1.0.0: {} + restore-cursor@5.1.0: dependencies: onetime: 7.0.0 @@ -5061,6 +5081,13 @@ snapshots: tslib@2.8.1: {} + tsx@4.21.0: + dependencies: + esbuild: 0.27.3 + get-tsconfig: 4.13.7 + optionalDependencies: + fsevents: 2.3.3 + tw-animate-css@1.4.0: {} type-fest@5.4.4: @@ -5105,13 +5132,13 @@ snapshots: vary@1.1.2: {} - vite-node@3.2.4(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1): + vite-node@3.2.4(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 6.4.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0) transitivePeerDependencies: - '@types/node' - jiti @@ -5126,7 +5153,7 @@ snapshots: - tsx - yaml - vite@6.4.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1): + vite@6.4.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) @@ -5139,8 +5166,9 @@ snapshots: fsevents: 2.3.3 jiti: 2.6.1 lightningcss: 1.31.1 + tsx: 4.21.0 - vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1): + vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0): dependencies: esbuild: 0.27.3 fdir: 6.5.0(picomatch@4.0.3) @@ -5153,12 +5181,13 @@ snapshots: fsevents: 2.3.3 jiti: 2.6.1 lightningcss: 1.31.1 + tsx: 4.21.0 - vitest@3.2.4(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(msw@2.12.13(@types/node@22.19.15)(typescript@5.9.3)): + vitest@3.2.4(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(msw@2.12.13(@types/node@22.19.15)(typescript@5.9.3))(tsx@4.21.0): dependencies: '@types/chai': 5.2.3 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(msw@2.12.13(@types/node@22.19.15)(typescript@5.9.3))(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)) + '@vitest/mocker': 3.2.4(msw@2.12.13(@types/node@22.19.15)(typescript@5.9.3))(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -5176,8 +5205,8 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1) - vite-node: 3.2.4(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1) + vite: 7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0) + vite-node: 3.2.4(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.19.15