@tinybirdco/sdk 0.0.3 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -14
- package/dist/api/deploy.d.ts +41 -3
- package/dist/api/deploy.d.ts.map +1 -1
- package/dist/api/deploy.js +141 -19
- package/dist/api/deploy.js.map +1 -1
- package/dist/api/deploy.test.js +77 -29
- package/dist/api/deploy.test.js.map +1 -1
- package/dist/api/local.d.ts +92 -0
- package/dist/api/local.d.ts.map +1 -0
- package/dist/api/local.js +176 -0
- package/dist/api/local.js.map +1 -0
- package/dist/api/local.test.d.ts +2 -0
- package/dist/api/local.test.d.ts.map +1 -0
- package/dist/api/local.test.js +182 -0
- package/dist/api/local.test.js.map +1 -0
- package/dist/api/resources.d.ts +178 -0
- package/dist/api/resources.d.ts.map +1 -0
- package/dist/api/resources.js +244 -0
- package/dist/api/resources.js.map +1 -0
- package/dist/api/resources.test.d.ts +2 -0
- package/dist/api/resources.test.d.ts.map +1 -0
- package/dist/api/resources.test.js +255 -0
- package/dist/api/resources.test.js.map +1 -0
- package/dist/cli/commands/build.d.ts +6 -4
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/build.js +95 -47
- package/dist/cli/commands/build.js.map +1 -1
- package/dist/cli/commands/deploy.d.ts +39 -0
- package/dist/cli/commands/deploy.d.ts.map +1 -0
- package/dist/cli/commands/deploy.js +90 -0
- package/dist/cli/commands/deploy.js.map +1 -0
- package/dist/cli/commands/dev.d.ts +9 -2
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +60 -31
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/init.d.ts +24 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +174 -23
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/init.test.js +190 -30
- package/dist/cli/commands/init.test.js.map +1 -1
- package/dist/cli/config.d.ts +14 -0
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +7 -0
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/config.test.js +29 -0
- package/dist/cli/config.test.js.map +1 -1
- package/dist/cli/index.js +107 -11
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/utils/package-manager.d.ts +8 -0
- package/dist/cli/utils/package-manager.d.ts.map +1 -0
- package/dist/cli/utils/package-manager.js +45 -0
- package/dist/cli/utils/package-manager.js.map +1 -0
- package/dist/cli/utils/package-manager.test.d.ts +2 -0
- package/dist/cli/utils/package-manager.test.d.ts.map +1 -0
- package/dist/cli/utils/package-manager.test.js +85 -0
- package/dist/cli/utils/package-manager.test.js.map +1 -0
- package/dist/codegen/index.d.ts +39 -0
- package/dist/codegen/index.d.ts.map +1 -0
- package/dist/codegen/index.js +300 -0
- package/dist/codegen/index.js.map +1 -0
- package/dist/codegen/index.test.d.ts +2 -0
- package/dist/codegen/index.test.d.ts.map +1 -0
- package/dist/codegen/index.test.js +310 -0
- package/dist/codegen/index.test.js.map +1 -0
- package/dist/codegen/type-mapper.d.ts +20 -0
- package/dist/codegen/type-mapper.d.ts.map +1 -0
- package/dist/codegen/type-mapper.js +238 -0
- package/dist/codegen/type-mapper.js.map +1 -0
- package/dist/codegen/type-mapper.test.d.ts +2 -0
- package/dist/codegen/type-mapper.test.d.ts.map +1 -0
- package/dist/codegen/type-mapper.test.js +167 -0
- package/dist/codegen/type-mapper.test.js.map +1 -0
- package/dist/codegen/utils.d.ts +46 -0
- package/dist/codegen/utils.d.ts.map +1 -0
- package/dist/codegen/utils.js +141 -0
- package/dist/codegen/utils.js.map +1 -0
- package/dist/codegen/utils.test.d.ts +2 -0
- package/dist/codegen/utils.test.d.ts.map +1 -0
- package/dist/codegen/utils.test.js +178 -0
- package/dist/codegen/utils.test.js.map +1 -0
- package/dist/generator/index.d.ts +3 -0
- package/dist/generator/index.d.ts.map +1 -1
- package/dist/generator/index.js +17 -1
- package/dist/generator/index.js.map +1 -1
- package/dist/generator/index.test.js +104 -1
- package/dist/generator/index.test.js.map +1 -1
- package/dist/generator/loader.d.ts +15 -0
- package/dist/generator/loader.d.ts.map +1 -1
- package/dist/generator/loader.js +24 -0
- package/dist/generator/loader.js.map +1 -1
- package/dist/test/handlers.d.ts +49 -0
- package/dist/test/handlers.d.ts.map +1 -1
- package/dist/test/handlers.js +45 -0
- package/dist/test/handlers.js.map +1 -1
- package/package.json +4 -2
- package/src/api/deploy.test.ts +135 -34
- package/src/api/deploy.ts +203 -23
- package/src/api/local.test.ts +250 -0
- package/src/api/local.ts +270 -0
- package/src/api/resources.test.ts +332 -0
- package/src/api/resources.ts +554 -0
- package/src/cli/commands/build.ts +115 -53
- package/src/cli/commands/deploy.ts +126 -0
- package/src/cli/commands/dev.ts +81 -36
- package/src/cli/commands/init.test.ts +239 -30
- package/src/cli/commands/init.ts +243 -26
- package/src/cli/config.test.ts +47 -0
- package/src/cli/config.ts +20 -0
- package/src/cli/index.ts +120 -11
- package/src/cli/utils/package-manager.test.ts +118 -0
- package/src/cli/utils/package-manager.ts +44 -0
- package/src/codegen/index.test.ts +367 -0
- package/src/codegen/index.ts +379 -0
- package/src/codegen/type-mapper.test.ts +224 -0
- package/src/codegen/type-mapper.ts +265 -0
- package/src/codegen/utils.test.ts +221 -0
- package/src/codegen/utils.ts +174 -0
- package/src/generator/index.test.ts +121 -1
- package/src/generator/index.ts +19 -1
- package/src/generator/loader.ts +43 -0
- package/src/test/handlers.ts +58 -0
package/src/cli/index.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { dirname, resolve } from "node:path";
|
|
|
16
16
|
import { Command } from "commander";
|
|
17
17
|
import { runInit } from "./commands/init.js";
|
|
18
18
|
import { runBuild } from "./commands/build.js";
|
|
19
|
+
import { runDeploy } from "./commands/deploy.js";
|
|
19
20
|
import { runDev } from "./commands/dev.js";
|
|
20
21
|
import { runLogin } from "./commands/login.js";
|
|
21
22
|
import {
|
|
@@ -23,6 +24,8 @@ import {
|
|
|
23
24
|
runBranchStatus,
|
|
24
25
|
runBranchDelete,
|
|
25
26
|
} from "./commands/branch.js";
|
|
27
|
+
import { detectPackageManagerRunCmd } from "./utils/package-manager.js";
|
|
28
|
+
import type { DevMode } from "./config.js";
|
|
26
29
|
|
|
27
30
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
28
31
|
const packageJson = JSON.parse(
|
|
@@ -54,12 +57,20 @@ function createCli(): Command {
|
|
|
54
57
|
.description("Initialize a new Tinybird TypeScript project")
|
|
55
58
|
.option("-f, --force", "Overwrite existing files")
|
|
56
59
|
.option("--skip-login", "Skip browser login flow")
|
|
60
|
+
.option("-m, --mode <mode>", "Development mode: 'branch' or 'local'")
|
|
61
|
+
.option("-p, --path <path>", "Path for Tinybird client files")
|
|
57
62
|
.action(async (options) => {
|
|
58
|
-
|
|
63
|
+
// Validate mode if provided
|
|
64
|
+
if (options.mode && !["branch", "local"].includes(options.mode)) {
|
|
65
|
+
console.error(`Error: Invalid mode '${options.mode}'. Use 'branch' or 'local'.`);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
59
68
|
|
|
60
69
|
const result = await runInit({
|
|
61
70
|
force: options.force,
|
|
62
71
|
skipLogin: options.skipLogin,
|
|
72
|
+
devMode: options.mode,
|
|
73
|
+
clientPath: options.path,
|
|
63
74
|
});
|
|
64
75
|
|
|
65
76
|
if (!result.success) {
|
|
@@ -81,6 +92,10 @@ function createCli(): Command {
|
|
|
81
92
|
});
|
|
82
93
|
}
|
|
83
94
|
|
|
95
|
+
// Detect package manager for run command
|
|
96
|
+
const runCmd = detectPackageManagerRunCmd();
|
|
97
|
+
const clientPath = result.clientPath ?? "tinybird";
|
|
98
|
+
|
|
84
99
|
if (result.loggedIn) {
|
|
85
100
|
console.log(`\nLogged in successfully!`);
|
|
86
101
|
if (result.workspaceName) {
|
|
@@ -89,19 +104,25 @@ function createCli(): Command {
|
|
|
89
104
|
if (result.userEmail) {
|
|
90
105
|
console.log(` User: ${result.userEmail}`);
|
|
91
106
|
}
|
|
107
|
+
|
|
108
|
+
if (result.existingDatafiles && result.existingDatafiles.length > 0) {
|
|
109
|
+
console.log(
|
|
110
|
+
`\nAdded ${result.existingDatafiles.length} existing datafile(s) to tinybird.json.`
|
|
111
|
+
);
|
|
112
|
+
}
|
|
92
113
|
console.log("\nDone! Next steps:");
|
|
93
|
-
console.log(
|
|
94
|
-
console.log(
|
|
114
|
+
console.log(` 1. Edit your schema in ${clientPath}/`);
|
|
115
|
+
console.log(` 2. Run '${runCmd} tinybird:dev' to start development`);
|
|
95
116
|
} else if (result.loggedIn === false) {
|
|
96
117
|
console.log("\nLogin was skipped or failed.");
|
|
97
118
|
console.log("\nDone! Next steps:");
|
|
98
119
|
console.log(" 1. Run 'npx tinybird login' to authenticate");
|
|
99
|
-
console.log(
|
|
100
|
-
console.log(
|
|
120
|
+
console.log(` 2. Edit your schema in ${clientPath}/`);
|
|
121
|
+
console.log(` 3. Run '${runCmd} tinybird:dev' to start development`);
|
|
101
122
|
} else {
|
|
102
123
|
console.log("\nDone! Next steps:");
|
|
103
|
-
console.log(
|
|
104
|
-
console.log(
|
|
124
|
+
console.log(` 1. Edit your schema in ${clientPath}/`);
|
|
125
|
+
console.log(` 2. Run '${runCmd} tinybird:dev' to start development`);
|
|
105
126
|
}
|
|
106
127
|
});
|
|
107
128
|
|
|
@@ -134,17 +155,27 @@ function createCli(): Command {
|
|
|
134
155
|
// Build command
|
|
135
156
|
program
|
|
136
157
|
.command("build")
|
|
137
|
-
.description("Build and push resources to Tinybird")
|
|
158
|
+
.description("Build and push resources to a Tinybird branch (not main)")
|
|
138
159
|
.option("--dry-run", "Generate without pushing to API")
|
|
139
160
|
.option("--debug", "Show debug output including API requests/responses")
|
|
161
|
+
.option("--local", "Use local Tinybird container")
|
|
140
162
|
.action(async (options) => {
|
|
141
163
|
if (options.debug) {
|
|
142
164
|
process.env.TINYBIRD_DEBUG = "1";
|
|
143
165
|
}
|
|
144
|
-
|
|
166
|
+
|
|
167
|
+
// Determine devMode override
|
|
168
|
+
let devModeOverride: DevMode | undefined;
|
|
169
|
+
if (options.local) {
|
|
170
|
+
devModeOverride = "local";
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const modeLabel = devModeOverride === "local" ? " (local)" : "";
|
|
174
|
+
console.log(`[${formatTime()}] Building${modeLabel}...\n`);
|
|
145
175
|
|
|
146
176
|
const result = await runBuild({
|
|
147
177
|
dryRun: options.dryRun,
|
|
178
|
+
devModeOverride,
|
|
148
179
|
});
|
|
149
180
|
|
|
150
181
|
if (!result.success) {
|
|
@@ -186,16 +217,83 @@ function createCli(): Command {
|
|
|
186
217
|
console.log(`\n[${formatTime()}] Done in ${result.durationMs}ms`);
|
|
187
218
|
});
|
|
188
219
|
|
|
220
|
+
// Deploy command
|
|
221
|
+
program
|
|
222
|
+
.command("deploy")
|
|
223
|
+
.description("Deploy resources to main Tinybird workspace (production)")
|
|
224
|
+
.option("--dry-run", "Generate without pushing to API")
|
|
225
|
+
.option("--debug", "Show debug output including API requests/responses")
|
|
226
|
+
.action(async (options) => {
|
|
227
|
+
if (options.debug) {
|
|
228
|
+
process.env.TINYBIRD_DEBUG = "1";
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
console.log(`[${formatTime()}] Deploying to main workspace...\n`);
|
|
232
|
+
|
|
233
|
+
const result = await runDeploy({
|
|
234
|
+
dryRun: options.dryRun,
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
if (!result.success) {
|
|
238
|
+
console.error(`Error: ${result.error}`);
|
|
239
|
+
process.exit(1);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const { build, deploy } = result;
|
|
243
|
+
|
|
244
|
+
if (build) {
|
|
245
|
+
console.log(`Generated ${build.stats.datasourceCount} datasource(s), ${build.stats.pipeCount} pipe(s)`);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (options.dryRun) {
|
|
249
|
+
console.log("\n[Dry run] Resources not deployed to API");
|
|
250
|
+
|
|
251
|
+
// Show generated content
|
|
252
|
+
if (build) {
|
|
253
|
+
console.log("\n--- Generated Datasources ---");
|
|
254
|
+
build.resources.datasources.forEach((ds) => {
|
|
255
|
+
console.log(`\n${ds.name}.datasource:`);
|
|
256
|
+
console.log(ds.content);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
console.log("\n--- Generated Pipes ---");
|
|
260
|
+
build.resources.pipes.forEach((pipe) => {
|
|
261
|
+
console.log(`\n${pipe.name}.pipe:`);
|
|
262
|
+
console.log(pipe.content);
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
} else if (deploy) {
|
|
266
|
+
if (deploy.result === "no_changes") {
|
|
267
|
+
console.log("No changes detected - already up to date");
|
|
268
|
+
} else {
|
|
269
|
+
console.log(`Deployed to main workspace successfully`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
console.log(`\n[${formatTime()}] Done in ${result.durationMs}ms`);
|
|
274
|
+
});
|
|
275
|
+
|
|
189
276
|
// Dev command
|
|
190
277
|
program
|
|
191
278
|
.command("dev")
|
|
192
279
|
.description("Watch for changes and sync with Tinybird")
|
|
193
|
-
.
|
|
280
|
+
.option("--local", "Use local Tinybird container")
|
|
281
|
+
.option("--branch", "Use Tinybird cloud with branches")
|
|
282
|
+
.action(async (options) => {
|
|
283
|
+
// Determine devMode override
|
|
284
|
+
let devModeOverride: DevMode | undefined;
|
|
285
|
+
if (options.local) {
|
|
286
|
+
devModeOverride = "local";
|
|
287
|
+
} else if (options.branch) {
|
|
288
|
+
devModeOverride = "branch";
|
|
289
|
+
}
|
|
290
|
+
|
|
194
291
|
console.log(`tinybird dev v${VERSION}`);
|
|
195
292
|
console.log("Loading config from tinybird.json...\n");
|
|
196
293
|
|
|
197
294
|
try {
|
|
198
295
|
const controller = await runDev({
|
|
296
|
+
devModeOverride,
|
|
199
297
|
onLoginComplete: (info) => {
|
|
200
298
|
console.log("\nAuthentication successful!");
|
|
201
299
|
if (info.workspaceName) {
|
|
@@ -207,7 +305,18 @@ function createCli(): Command {
|
|
|
207
305
|
console.log("");
|
|
208
306
|
},
|
|
209
307
|
onBranchReady: (info) => {
|
|
210
|
-
if (info.
|
|
308
|
+
if (info.isLocal) {
|
|
309
|
+
// Local mode
|
|
310
|
+
const workspaceName = info.localWorkspace?.name ?? "unknown";
|
|
311
|
+
if (info.wasCreated) {
|
|
312
|
+
console.log(`Using local Tinybird container`);
|
|
313
|
+
console.log(`Creating local workspace '${workspaceName}'...`);
|
|
314
|
+
console.log("Workspace created.\n");
|
|
315
|
+
} else {
|
|
316
|
+
console.log(`Using local Tinybird container`);
|
|
317
|
+
console.log(`Using existing local workspace '${workspaceName}'\n`);
|
|
318
|
+
}
|
|
319
|
+
} else if (info.isMainBranch) {
|
|
211
320
|
console.log("On main branch - deploying to workspace\n");
|
|
212
321
|
} else if (info.gitBranch) {
|
|
213
322
|
const tinybirdName = info.tinybirdBranch?.name ?? info.gitBranch;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import * as os from "os";
|
|
5
|
+
import { detectPackageManagerRunCmd } from "./package-manager.js";
|
|
6
|
+
|
|
7
|
+
describe("detectPackageManagerRunCmd", () => {
|
|
8
|
+
let tempDir: string;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pkg-manager-test-"));
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
try {
|
|
16
|
+
fs.rmSync(tempDir, { recursive: true });
|
|
17
|
+
} catch {
|
|
18
|
+
// Ignore cleanup errors
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe("lockfile detection", () => {
|
|
23
|
+
it("detects pnpm from pnpm-lock.yaml", () => {
|
|
24
|
+
fs.writeFileSync(path.join(tempDir, "pnpm-lock.yaml"), "");
|
|
25
|
+
expect(detectPackageManagerRunCmd(tempDir)).toBe("pnpm");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("detects yarn from yarn.lock", () => {
|
|
29
|
+
fs.writeFileSync(path.join(tempDir, "yarn.lock"), "");
|
|
30
|
+
expect(detectPackageManagerRunCmd(tempDir)).toBe("yarn");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("detects bun from bun.lockb", () => {
|
|
34
|
+
fs.writeFileSync(path.join(tempDir, "bun.lockb"), "");
|
|
35
|
+
expect(detectPackageManagerRunCmd(tempDir)).toBe("bun run");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("detects npm from package-lock.json", () => {
|
|
39
|
+
fs.writeFileSync(path.join(tempDir, "package-lock.json"), "{}");
|
|
40
|
+
expect(detectPackageManagerRunCmd(tempDir)).toBe("npm run");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("prioritizes pnpm lockfile over others", () => {
|
|
44
|
+
fs.writeFileSync(path.join(tempDir, "pnpm-lock.yaml"), "");
|
|
45
|
+
fs.writeFileSync(path.join(tempDir, "yarn.lock"), "");
|
|
46
|
+
fs.writeFileSync(path.join(tempDir, "package-lock.json"), "{}");
|
|
47
|
+
expect(detectPackageManagerRunCmd(tempDir)).toBe("pnpm");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("prioritizes yarn lockfile over npm", () => {
|
|
51
|
+
fs.writeFileSync(path.join(tempDir, "yarn.lock"), "");
|
|
52
|
+
fs.writeFileSync(path.join(tempDir, "package-lock.json"), "{}");
|
|
53
|
+
expect(detectPackageManagerRunCmd(tempDir)).toBe("yarn");
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe("packageManager field detection", () => {
|
|
58
|
+
it("detects pnpm from packageManager field", () => {
|
|
59
|
+
fs.writeFileSync(
|
|
60
|
+
path.join(tempDir, "package.json"),
|
|
61
|
+
JSON.stringify({ packageManager: "pnpm@9.0.0" })
|
|
62
|
+
);
|
|
63
|
+
expect(detectPackageManagerRunCmd(tempDir)).toBe("pnpm");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("detects yarn from packageManager field", () => {
|
|
67
|
+
fs.writeFileSync(
|
|
68
|
+
path.join(tempDir, "package.json"),
|
|
69
|
+
JSON.stringify({ packageManager: "yarn@4.0.0" })
|
|
70
|
+
);
|
|
71
|
+
expect(detectPackageManagerRunCmd(tempDir)).toBe("yarn");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("detects bun from packageManager field", () => {
|
|
75
|
+
fs.writeFileSync(
|
|
76
|
+
path.join(tempDir, "package.json"),
|
|
77
|
+
JSON.stringify({ packageManager: "bun@1.0.0" })
|
|
78
|
+
);
|
|
79
|
+
expect(detectPackageManagerRunCmd(tempDir)).toBe("bun run");
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("prioritizes lockfile over packageManager field", () => {
|
|
83
|
+
fs.writeFileSync(path.join(tempDir, "yarn.lock"), "");
|
|
84
|
+
fs.writeFileSync(
|
|
85
|
+
path.join(tempDir, "package.json"),
|
|
86
|
+
JSON.stringify({ packageManager: "pnpm@9.0.0" })
|
|
87
|
+
);
|
|
88
|
+
expect(detectPackageManagerRunCmd(tempDir)).toBe("yarn");
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe("default behavior", () => {
|
|
93
|
+
it("defaults to npm run when no indicators found", () => {
|
|
94
|
+
expect(detectPackageManagerRunCmd(tempDir)).toBe("npm run");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("defaults to npm run when package.json has no packageManager field", () => {
|
|
98
|
+
fs.writeFileSync(
|
|
99
|
+
path.join(tempDir, "package.json"),
|
|
100
|
+
JSON.stringify({ name: "test-project" })
|
|
101
|
+
);
|
|
102
|
+
expect(detectPackageManagerRunCmd(tempDir)).toBe("npm run");
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("defaults to npm run when package.json is invalid JSON", () => {
|
|
106
|
+
fs.writeFileSync(path.join(tempDir, "package.json"), "not json");
|
|
107
|
+
expect(detectPackageManagerRunCmd(tempDir)).toBe("npm run");
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("defaults to npm run when packageManager is not a string", () => {
|
|
111
|
+
fs.writeFileSync(
|
|
112
|
+
path.join(tempDir, "package.json"),
|
|
113
|
+
JSON.stringify({ packageManager: 123 })
|
|
114
|
+
);
|
|
115
|
+
expect(detectPackageManagerRunCmd(tempDir)).toBe("npm run");
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package manager detection utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Detect package manager and return the appropriate run command
|
|
10
|
+
*/
|
|
11
|
+
export function detectPackageManagerRunCmd(cwd: string = process.cwd()): string {
|
|
12
|
+
// Check lockfiles first (most reliable)
|
|
13
|
+
if (existsSync(join(cwd, "pnpm-lock.yaml"))) {
|
|
14
|
+
return "pnpm";
|
|
15
|
+
}
|
|
16
|
+
if (existsSync(join(cwd, "yarn.lock"))) {
|
|
17
|
+
return "yarn";
|
|
18
|
+
}
|
|
19
|
+
if (existsSync(join(cwd, "bun.lockb"))) {
|
|
20
|
+
return "bun run";
|
|
21
|
+
}
|
|
22
|
+
if (existsSync(join(cwd, "package-lock.json"))) {
|
|
23
|
+
return "npm run";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Check packageManager field in package.json
|
|
27
|
+
const packageJsonPath = join(cwd, "package.json");
|
|
28
|
+
if (existsSync(packageJsonPath)) {
|
|
29
|
+
try {
|
|
30
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
31
|
+
const pm = packageJson.packageManager;
|
|
32
|
+
if (typeof pm === "string") {
|
|
33
|
+
if (pm.startsWith("pnpm")) return "pnpm";
|
|
34
|
+
if (pm.startsWith("yarn")) return "yarn";
|
|
35
|
+
if (pm.startsWith("bun")) return "bun run";
|
|
36
|
+
}
|
|
37
|
+
} catch {
|
|
38
|
+
// Ignore parse errors
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Default to npm
|
|
43
|
+
return "npm run";
|
|
44
|
+
}
|