@turbo/workspaces 0.0.1

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/dist/cli.js ADDED
@@ -0,0 +1,1038 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
18
+
19
+ // src/cli.ts
20
+ var import_chalk6 = __toESM(require("chalk"));
21
+ var import_commander = require("commander");
22
+
23
+ // src/commands/summary/index.ts
24
+ var import_inquirer = __toESM(require("inquirer"));
25
+ var import_path6 = __toESM(require("path"));
26
+
27
+ // src/logger.ts
28
+ var import_chalk = __toESM(require("chalk"));
29
+ var import_gradient_string = __toESM(require("gradient-string"));
30
+ var INDENTATION = 2;
31
+ var Logger = class {
32
+ constructor({
33
+ interactive,
34
+ dry
35
+ } = {}) {
36
+ this.interactive = interactive != null ? interactive : true;
37
+ this.dry = dry != null ? dry : false;
38
+ this.step = 1;
39
+ }
40
+ logger(...args) {
41
+ if (this.interactive) {
42
+ console.log(...args);
43
+ }
44
+ }
45
+ indented(level, ...args) {
46
+ this.logger(" ".repeat(INDENTATION * level), ...args);
47
+ }
48
+ header(title) {
49
+ this.blankLine();
50
+ this.logger(import_chalk.default.bold(title));
51
+ }
52
+ installerFrames() {
53
+ const prefix = `${" ".repeat(INDENTATION)} - ${this.dry ? import_chalk.default.yellow("SKIPPED | ") : import_chalk.default.green("OK | ")}`;
54
+ return [`${prefix} `, `${prefix}> `, `${prefix}>> `, `${prefix}>>>`];
55
+ }
56
+ gradient(text) {
57
+ const turboGradient = (0, import_gradient_string.default)("#0099F7", "#F11712");
58
+ return turboGradient(text.toString());
59
+ }
60
+ hero() {
61
+ this.logger(import_chalk.default.bold(this.gradient(`
62
+ >>> TURBOREPO
63
+ `)));
64
+ }
65
+ info(...args) {
66
+ this.logger(...args);
67
+ }
68
+ mainStep(title) {
69
+ this.blankLine();
70
+ this.logger(`${this.step}. ${import_chalk.default.underline(title)}`);
71
+ this.step += 1;
72
+ }
73
+ subStep(...args) {
74
+ this.logger(" ".repeat(INDENTATION), `-`, this.dry ? import_chalk.default.yellow("SKIPPED |") : import_chalk.default.green("OK |"), ...args);
75
+ }
76
+ subStepFailure(...args) {
77
+ this.logger(" ".repeat(INDENTATION), `-`, import_chalk.default.red("ERROR |"), ...args);
78
+ }
79
+ rootHeader() {
80
+ this.blankLine();
81
+ this.indented(2, "Root:");
82
+ }
83
+ rootStep(...args) {
84
+ this.logger(" ".repeat(INDENTATION * 3), `-`, this.dry ? import_chalk.default.yellow("SKIPPED |") : import_chalk.default.green("OK |"), ...args);
85
+ }
86
+ workspaceHeader() {
87
+ this.blankLine();
88
+ this.indented(2, "Workspaces:");
89
+ }
90
+ workspaceStep(...args) {
91
+ this.logger(" ".repeat(INDENTATION * 3), `-`, this.dry ? import_chalk.default.yellow("SKIPPED |") : import_chalk.default.green("OK |"), ...args);
92
+ }
93
+ blankLine() {
94
+ this.logger();
95
+ }
96
+ error(...args) {
97
+ console.error(...args);
98
+ }
99
+ };
100
+
101
+ // src/commands/summary/index.ts
102
+ var import_chalk3 = __toESM(require("chalk"));
103
+
104
+ // src/utils.ts
105
+ var import_fs_extra = __toESM(require("fs-extra"));
106
+ var import_path = __toESM(require("path"));
107
+ var import_fast_glob = __toESM(require("fast-glob"));
108
+ var import_js_yaml = __toESM(require("js-yaml"));
109
+
110
+ // src/errors.ts
111
+ var ConvertError = class extends Error {
112
+ constructor(message) {
113
+ super(message);
114
+ this.name = "ConvertError";
115
+ Error.captureStackTrace(this, ConvertError);
116
+ }
117
+ };
118
+
119
+ // src/utils.ts
120
+ var PACKAGE_MANAGER_REGEX = /^(?!_)(.+)@(.+)$/;
121
+ function getPackageJson({
122
+ workspaceRoot
123
+ }) {
124
+ const packageJsonPath = import_path.default.join(workspaceRoot, "package.json");
125
+ try {
126
+ return import_fs_extra.default.readJsonSync(packageJsonPath, "utf8");
127
+ } catch (err) {
128
+ if (err && typeof err === "object" && "code" in err) {
129
+ if (err.code === "ENOENT") {
130
+ throw new ConvertError(`no "package.json" found at ${workspaceRoot}`);
131
+ }
132
+ if (err.code === "EJSONPARSE") {
133
+ throw new ConvertError(`failed to parse "package.json" at ${workspaceRoot}`);
134
+ }
135
+ }
136
+ throw new Error(`unexpected error reading "package.json" at ${workspaceRoot}`);
137
+ }
138
+ }
139
+ function getWorkspacePackageManager({
140
+ workspaceRoot
141
+ }) {
142
+ const { packageManager } = getPackageJson({ workspaceRoot });
143
+ if (packageManager) {
144
+ try {
145
+ const match = packageManager.match(PACKAGE_MANAGER_REGEX);
146
+ if (match) {
147
+ const [_, manager] = match;
148
+ return manager;
149
+ }
150
+ } catch (err) {
151
+ }
152
+ }
153
+ return void 0;
154
+ }
155
+ function getWorkspaceName({
156
+ workspaceRoot
157
+ }) {
158
+ const packageJson = getPackageJson({ workspaceRoot });
159
+ if (packageJson.name) {
160
+ return packageJson.name;
161
+ }
162
+ const workspaceDirectory = import_path.default.basename(workspaceRoot);
163
+ return workspaceDirectory;
164
+ }
165
+ function getPnpmWorkspaces({
166
+ workspaceRoot
167
+ }) {
168
+ const workspaceFile = import_path.default.join(workspaceRoot, "pnpm-workspace.yaml");
169
+ if (import_fs_extra.default.existsSync(workspaceFile)) {
170
+ try {
171
+ const workspaceConfig = import_js_yaml.default.load(import_fs_extra.default.readFileSync(workspaceFile, "utf8"));
172
+ if (workspaceConfig instanceof Object && "packages" in workspaceConfig && Array.isArray(workspaceConfig.packages)) {
173
+ return workspaceConfig.packages;
174
+ }
175
+ } catch (err) {
176
+ throw new ConvertError(`failed to parse ${workspaceFile}`);
177
+ }
178
+ }
179
+ return [];
180
+ }
181
+ function expandPaths({
182
+ root,
183
+ lockFile,
184
+ workspaceConfig
185
+ }) {
186
+ const fromRoot = (p) => import_path.default.join(root, p);
187
+ const paths = {
188
+ root,
189
+ lockfile: fromRoot(lockFile),
190
+ packageJson: fromRoot("package.json"),
191
+ nodeModules: fromRoot("node_modules")
192
+ };
193
+ if (workspaceConfig) {
194
+ paths.workspaceConfig = fromRoot(workspaceConfig);
195
+ }
196
+ return paths;
197
+ }
198
+ function expandWorkspaces({
199
+ workspaceRoot,
200
+ workspaceGlobs
201
+ }) {
202
+ if (!workspaceGlobs) {
203
+ return [];
204
+ }
205
+ return workspaceGlobs.flatMap((workspaceGlob) => {
206
+ const workspacePackageJsonGlob = `${workspaceGlob}/package.json`;
207
+ return import_fast_glob.default.sync(workspacePackageJsonGlob, {
208
+ onlyFiles: true,
209
+ absolute: true,
210
+ cwd: workspaceRoot
211
+ });
212
+ }).map((workspacePackageJson) => {
213
+ const workspaceRoot2 = import_path.default.dirname(workspacePackageJson);
214
+ const name = getWorkspaceName({ workspaceRoot: workspaceRoot2 });
215
+ return {
216
+ name,
217
+ paths: {
218
+ root: workspaceRoot2,
219
+ packageJson: workspacePackageJson,
220
+ nodeModules: import_path.default.join(workspaceRoot2, "node_modules")
221
+ }
222
+ };
223
+ });
224
+ }
225
+ function directoryInfo({ directory }) {
226
+ const dir = import_path.default.resolve(process.cwd(), directory);
227
+ return { exists: import_fs_extra.default.existsSync(dir), absolute: dir };
228
+ }
229
+
230
+ // src/managers/pnpm.ts
231
+ var import_fs_extra3 = __toESM(require("fs-extra"));
232
+ var import_path3 = __toESM(require("path"));
233
+ var import_execa = __toESM(require("execa"));
234
+
235
+ // src/updateDependencies.ts
236
+ var import_fs_extra2 = __toESM(require("fs-extra"));
237
+ var import_chalk2 = __toESM(require("chalk"));
238
+ var import_path2 = __toESM(require("path"));
239
+ function updateDependencyList({
240
+ dependencyList,
241
+ project,
242
+ to
243
+ }) {
244
+ const updated = [];
245
+ project.workspaceData.workspaces.forEach((workspace) => {
246
+ const { name } = workspace;
247
+ if (dependencyList[name]) {
248
+ const workspaceVersion = dependencyList[name];
249
+ const version = workspaceVersion.startsWith("workspace:") ? workspaceVersion.slice("workspace:".length) : workspaceVersion;
250
+ dependencyList[name] = to.name === "pnpm" ? `workspace:${version}` : version;
251
+ updated.push(name);
252
+ }
253
+ });
254
+ return { dependencyList, updated };
255
+ }
256
+ function updateDependencies({
257
+ project,
258
+ workspace,
259
+ to,
260
+ logger,
261
+ options
262
+ }) {
263
+ if (["yarn", "npm"].includes(to.name) && ["yarn", "npm"].includes(project.packageManager)) {
264
+ return;
265
+ }
266
+ const workspacePackageJson = getPackageJson({
267
+ workspaceRoot: workspace.paths.root
268
+ });
269
+ const stats = {
270
+ dependencies: [],
271
+ devDependencies: [],
272
+ peerDependencies: [],
273
+ optionalDependencies: []
274
+ };
275
+ const allDependencyKeys = [
276
+ "dependencies",
277
+ "devDependencies",
278
+ "peerDependencies",
279
+ "optionalDependencies"
280
+ ];
281
+ allDependencyKeys.forEach((depKey) => {
282
+ const depList = workspacePackageJson[depKey];
283
+ if (depList) {
284
+ const { updated, dependencyList } = updateDependencyList({
285
+ dependencyList: depList,
286
+ project,
287
+ to
288
+ });
289
+ workspacePackageJson[depKey] = dependencyList;
290
+ stats[depKey] = updated;
291
+ }
292
+ });
293
+ const toLog = (key) => {
294
+ const total = stats[key].length;
295
+ if (total > 0) {
296
+ return `${import_chalk2.default.green(total.toString())} ${key}`;
297
+ }
298
+ return void 0;
299
+ };
300
+ const allChanges = allDependencyKeys.map(toLog).filter(Boolean);
301
+ const workspaceLocation = `./${import_path2.default.relative(project.paths.root, workspace.paths.packageJson)}`;
302
+ if (allChanges.length >= 1) {
303
+ let logLine = "updating";
304
+ allChanges.forEach((stat, idx) => {
305
+ if (allChanges.length === 1) {
306
+ logLine += ` ${stat} in ${workspaceLocation}`;
307
+ } else {
308
+ if (idx === allChanges.length - 1) {
309
+ logLine += `and ${stat} in ${workspaceLocation}`;
310
+ } else {
311
+ logLine += ` ${stat}, `;
312
+ }
313
+ }
314
+ });
315
+ logger.workspaceStep(logLine);
316
+ } else {
317
+ logger.workspaceStep(`no workspace dependencies found in ${workspaceLocation}`);
318
+ }
319
+ if (!(options == null ? void 0 : options.dry)) {
320
+ import_fs_extra2.default.writeJSONSync(workspace.paths.packageJson, workspacePackageJson, {
321
+ spaces: 2
322
+ });
323
+ }
324
+ }
325
+
326
+ // src/managers/pnpm.ts
327
+ async function detect(args) {
328
+ const lockFile = import_path3.default.join(args.workspaceRoot, "pnpm-lock.yaml");
329
+ const workspaceFile = import_path3.default.join(args.workspaceRoot, "pnpm-workspace.yaml");
330
+ const packageManager = getWorkspacePackageManager({
331
+ workspaceRoot: args.workspaceRoot
332
+ });
333
+ return import_fs_extra3.default.existsSync(lockFile) || import_fs_extra3.default.existsSync(workspaceFile) || packageManager === "pnpm";
334
+ }
335
+ async function read(args) {
336
+ const isPnpm = await detect(args);
337
+ if (!isPnpm) {
338
+ throw new ConvertError("Not a pnpm project");
339
+ }
340
+ return {
341
+ name: getWorkspaceName(args),
342
+ packageManager: "pnpm",
343
+ paths: expandPaths({
344
+ root: args.workspaceRoot,
345
+ lockFile: "pnpm-lock.yaml",
346
+ workspaceConfig: "pnpm-workspace.yaml"
347
+ }),
348
+ workspaceData: {
349
+ globs: getPnpmWorkspaces(args),
350
+ workspaces: expandWorkspaces({
351
+ workspaceGlobs: getPnpmWorkspaces(args),
352
+ ...args
353
+ })
354
+ }
355
+ };
356
+ }
357
+ async function create(args) {
358
+ const { project, to, logger, options } = args;
359
+ const hasWorkspaces = project.workspaceData.globs.length > 0;
360
+ logger.mainStep(`Creating ${project.packageManager}${hasWorkspaces ? "workspaces" : ""}`);
361
+ const packageJson = getPackageJson({ workspaceRoot: project.paths.root });
362
+ logger.rootHeader();
363
+ packageJson.packageManager = `${to.name}@${to.version}`;
364
+ logger.rootStep(`adding "packageManager" field to ${project.name} root "package.json"`);
365
+ if (!(options == null ? void 0 : options.dry)) {
366
+ import_fs_extra3.default.writeJSONSync(project.paths.packageJson, packageJson, { spaces: 2 });
367
+ if (hasWorkspaces) {
368
+ logger.rootStep(`adding "pnpm-workspace.yaml"`);
369
+ import_fs_extra3.default.writeFileSync(import_path3.default.join(project.paths.root, "pnpm-workspace.yaml"), `packages:
370
+ ${project.workspaceData.globs.map((w) => ` - "${w}"`).join("\n")}`);
371
+ }
372
+ }
373
+ if (hasWorkspaces) {
374
+ updateDependencies({
375
+ workspace: { name: "root", paths: project.paths },
376
+ project,
377
+ to,
378
+ logger,
379
+ options
380
+ });
381
+ logger.workspaceHeader();
382
+ project.workspaceData.workspaces.forEach((workspace) => updateDependencies({ workspace, project, to, logger, options }));
383
+ }
384
+ }
385
+ async function remove(args) {
386
+ const { project, logger, options } = args;
387
+ const hasWorkspaces = project.workspaceData.globs.length > 0;
388
+ logger.mainStep(`Removing ${project.packageManager}${hasWorkspaces ? "workspaces" : ""}`);
389
+ const packageJson = getPackageJson({ workspaceRoot: project.paths.root });
390
+ if (project.paths.workspaceConfig && hasWorkspaces) {
391
+ logger.subStep(`removing "pnpm-workspace.yaml"`);
392
+ if (!(options == null ? void 0 : options.dry)) {
393
+ import_fs_extra3.default.rmSync(project.paths.workspaceConfig, { force: true });
394
+ }
395
+ }
396
+ logger.subStep(`removing "packageManager" field in ${project.name} root "package.json"`);
397
+ delete packageJson.packageManager;
398
+ if (!(options == null ? void 0 : options.dry)) {
399
+ import_fs_extra3.default.writeJSONSync(project.paths.packageJson, packageJson, { spaces: 2 });
400
+ const allModulesDirs = [
401
+ project.paths.nodeModules,
402
+ ...project.workspaceData.workspaces.map((w) => w.paths.nodeModules)
403
+ ];
404
+ try {
405
+ logger.subStep(`removing "node_modules"`);
406
+ await Promise.all(allModulesDirs.map((dir) => import_fs_extra3.default.rm(dir, { recursive: true, force: true })));
407
+ } catch (err) {
408
+ throw new ConvertError("Failed to remove node_modules");
409
+ }
410
+ }
411
+ }
412
+ async function clean(args) {
413
+ const { project, logger, options } = args;
414
+ logger.subStep(`removing ${import_path3.default.relative(project.paths.root, project.paths.lockfile)}`);
415
+ if (!(options == null ? void 0 : options.dry)) {
416
+ import_fs_extra3.default.rmSync(project.paths.lockfile, { force: true });
417
+ }
418
+ }
419
+ async function convertLock(args) {
420
+ const { project, logger, options } = args;
421
+ if (project.packageManager !== "pnpm") {
422
+ logger.subStep(`converting ${import_path3.default.relative(project.paths.root, project.paths.lockfile)} to pnpm-lock.yaml`);
423
+ if (!(options == null ? void 0 : options.dry) && import_fs_extra3.default.existsSync(project.paths.lockfile)) {
424
+ try {
425
+ await (0, import_execa.default)("pnpm", ["import"], {
426
+ stdio: "ignore",
427
+ cwd: project.paths.root
428
+ });
429
+ } finally {
430
+ import_fs_extra3.default.rmSync(project.paths.lockfile, { force: true });
431
+ }
432
+ }
433
+ }
434
+ }
435
+ var pnpm = {
436
+ detect,
437
+ read,
438
+ create,
439
+ remove,
440
+ clean,
441
+ convertLock
442
+ };
443
+ var pnpm_default = pnpm;
444
+
445
+ // src/managers/npm.ts
446
+ var import_fs_extra4 = __toESM(require("fs-extra"));
447
+ var import_path4 = __toESM(require("path"));
448
+ async function detect2(args) {
449
+ const lockFile = import_path4.default.join(args.workspaceRoot, "package-lock.json");
450
+ const packageManager = getWorkspacePackageManager({
451
+ workspaceRoot: args.workspaceRoot
452
+ });
453
+ return import_fs_extra4.default.existsSync(lockFile) || packageManager === "npm";
454
+ }
455
+ async function read2(args) {
456
+ const isNpm = await detect2(args);
457
+ if (!isNpm) {
458
+ throw new ConvertError("Not an npm project");
459
+ }
460
+ const packageJson = getPackageJson(args);
461
+ return {
462
+ name: getWorkspaceName(args),
463
+ packageManager: "npm",
464
+ paths: expandPaths({
465
+ root: args.workspaceRoot,
466
+ lockFile: "package-lock.json"
467
+ }),
468
+ workspaceData: {
469
+ globs: packageJson.workspaces || [],
470
+ workspaces: expandWorkspaces({
471
+ workspaceGlobs: packageJson.workspaces,
472
+ ...args
473
+ })
474
+ }
475
+ };
476
+ }
477
+ async function create2(args) {
478
+ const { project, options, to, logger } = args;
479
+ const hasWorkspaces = project.workspaceData.globs.length > 0;
480
+ logger.mainStep(`Creating ${project.packageManager}${hasWorkspaces ? "workspaces" : ""}`);
481
+ const packageJson = getPackageJson({ workspaceRoot: project.paths.root });
482
+ logger.rootHeader();
483
+ logger.rootStep(`adding "packageManager" field to ${project.name} root "package.json"`);
484
+ packageJson.packageManager = `${to.name}@${to.version}`;
485
+ if (hasWorkspaces) {
486
+ logger.rootStep(`adding "workspaces" field to ${project.name} root "package.json"`);
487
+ packageJson.workspaces = project.workspaceData.globs;
488
+ updateDependencies({
489
+ workspace: { name: "root", paths: project.paths },
490
+ project,
491
+ to,
492
+ logger,
493
+ options
494
+ });
495
+ logger.workspaceHeader();
496
+ project.workspaceData.workspaces.forEach((workspace) => updateDependencies({ workspace, project, to, logger, options }));
497
+ }
498
+ if (!(options == null ? void 0 : options.dry)) {
499
+ import_fs_extra4.default.writeJSONSync(project.paths.packageJson, packageJson, { spaces: 2 });
500
+ }
501
+ }
502
+ async function remove2(args) {
503
+ const { project, logger, options } = args;
504
+ const hasWorkspaces = project.workspaceData.globs.length > 0;
505
+ logger.mainStep(`Creating ${project.packageManager}${hasWorkspaces ? "workspaces" : ""}`);
506
+ const packageJson = getPackageJson({ workspaceRoot: project.paths.root });
507
+ if (hasWorkspaces) {
508
+ logger.subStep(`removing "workspaces" field in ${project.name} root "package.json"`);
509
+ delete packageJson.workspaces;
510
+ }
511
+ logger.subStep(`removing "packageManager" field in ${project.name} root "package.json"`);
512
+ delete packageJson.packageManager;
513
+ if (!(options == null ? void 0 : options.dry)) {
514
+ import_fs_extra4.default.writeJSONSync(project.paths.packageJson, packageJson, { spaces: 2 });
515
+ const allModulesDirs = [
516
+ project.paths.nodeModules,
517
+ ...project.workspaceData.workspaces.map((w) => w.paths.nodeModules)
518
+ ];
519
+ try {
520
+ logger.subStep(`removing "node_modules"`);
521
+ await Promise.all(allModulesDirs.map((dir) => import_fs_extra4.default.rm(dir, { recursive: true, force: true })));
522
+ } catch (err) {
523
+ throw new ConvertError("Failed to remove node_modules");
524
+ }
525
+ }
526
+ }
527
+ async function clean2(args) {
528
+ const { project, logger, options } = args;
529
+ logger.subStep(`removing ${import_path4.default.relative(project.paths.root, project.paths.lockfile)}`);
530
+ if (!(options == null ? void 0 : options.dry)) {
531
+ import_fs_extra4.default.rmSync(project.paths.lockfile, { force: true });
532
+ }
533
+ }
534
+ async function convertLock2(args) {
535
+ const { project, options } = args;
536
+ if (project.packageManager !== "npm") {
537
+ if (!(options == null ? void 0 : options.dry)) {
538
+ import_fs_extra4.default.rmSync(project.paths.lockfile, { force: true });
539
+ }
540
+ }
541
+ }
542
+ var npm = {
543
+ detect: detect2,
544
+ read: read2,
545
+ create: create2,
546
+ remove: remove2,
547
+ clean: clean2,
548
+ convertLock: convertLock2
549
+ };
550
+ var npm_default = npm;
551
+
552
+ // src/managers/yarn.ts
553
+ var import_fs_extra5 = __toESM(require("fs-extra"));
554
+ var import_path5 = __toESM(require("path"));
555
+ async function detect3(args) {
556
+ const lockFile = import_path5.default.join(args.workspaceRoot, "yarn.lock");
557
+ const packageManager = getWorkspacePackageManager({
558
+ workspaceRoot: args.workspaceRoot
559
+ });
560
+ return import_fs_extra5.default.existsSync(lockFile) || packageManager === "yarn";
561
+ }
562
+ async function read3(args) {
563
+ const isYarn = await detect3(args);
564
+ if (!isYarn) {
565
+ throw new ConvertError("Not a yarn project");
566
+ }
567
+ const packageJson = getPackageJson(args);
568
+ return {
569
+ name: getWorkspaceName(args),
570
+ packageManager: "yarn",
571
+ paths: expandPaths({
572
+ root: args.workspaceRoot,
573
+ lockFile: "yarn.lock"
574
+ }),
575
+ workspaceData: {
576
+ globs: packageJson.workspaces || [],
577
+ workspaces: expandWorkspaces({
578
+ workspaceGlobs: packageJson.workspaces,
579
+ ...args
580
+ })
581
+ }
582
+ };
583
+ }
584
+ async function create3(args) {
585
+ const { project, to, logger, options } = args;
586
+ const hasWorkspaces = project.workspaceData.globs.length > 0;
587
+ logger.mainStep(`Creating ${project.packageManager}${hasWorkspaces ? "workspaces" : ""}`);
588
+ const packageJson = getPackageJson({ workspaceRoot: project.paths.root });
589
+ logger.rootHeader();
590
+ logger.rootStep(`adding "packageManager" field to ${import_path5.default.relative(project.paths.root, project.paths.packageJson)}`);
591
+ packageJson.packageManager = `${to.name}@${to.version}`;
592
+ if (hasWorkspaces) {
593
+ logger.rootStep(`adding "workspaces" field to ${import_path5.default.relative(project.paths.root, project.paths.packageJson)}`);
594
+ packageJson.workspaces = project.workspaceData.globs;
595
+ updateDependencies({
596
+ workspace: { name: "root", paths: project.paths },
597
+ project,
598
+ to,
599
+ logger,
600
+ options
601
+ });
602
+ logger.workspaceHeader();
603
+ project.workspaceData.workspaces.forEach((workspace) => updateDependencies({ workspace, project, to, logger, options }));
604
+ }
605
+ if (!(options == null ? void 0 : options.dry)) {
606
+ import_fs_extra5.default.writeJSONSync(project.paths.packageJson, packageJson, { spaces: 2 });
607
+ }
608
+ }
609
+ async function remove3(args) {
610
+ const { project, logger, options } = args;
611
+ const hasWorkspaces = project.workspaceData.globs.length > 0;
612
+ logger.mainStep(`Removing ${project.packageManager}${hasWorkspaces ? "workspaces" : ""}`);
613
+ const packageJson = getPackageJson({ workspaceRoot: project.paths.root });
614
+ if (hasWorkspaces) {
615
+ logger.subStep(`removing "workspaces" field in ${project.name} root "package.json"`);
616
+ delete packageJson.workspaces;
617
+ }
618
+ logger.subStep(`removing "packageManager" field in ${project.name} root "package.json"`);
619
+ delete packageJson.packageManager;
620
+ if (!(options == null ? void 0 : options.dry)) {
621
+ import_fs_extra5.default.writeJSONSync(project.paths.packageJson, packageJson, { spaces: 2 });
622
+ const allModulesDirs = [
623
+ project.paths.nodeModules,
624
+ ...project.workspaceData.workspaces.map((w) => w.paths.nodeModules)
625
+ ];
626
+ try {
627
+ logger.subStep(`removing "node_modules"`);
628
+ await Promise.all(allModulesDirs.map((dir) => import_fs_extra5.default.rm(dir, { recursive: true, force: true })));
629
+ } catch (err) {
630
+ throw new ConvertError("Failed to remove node_modules");
631
+ }
632
+ }
633
+ }
634
+ async function clean3(args) {
635
+ const { project, logger, options } = args;
636
+ logger.subStep(`removing ${import_path5.default.relative(project.paths.root, project.paths.lockfile)}`);
637
+ if (!(options == null ? void 0 : options.dry)) {
638
+ import_fs_extra5.default.rmSync(project.paths.lockfile, { force: true });
639
+ }
640
+ }
641
+ async function convertLock3(args) {
642
+ const { project, options } = args;
643
+ if (project.packageManager !== "yarn") {
644
+ if (!(options == null ? void 0 : options.dry)) {
645
+ import_fs_extra5.default.rmSync(project.paths.lockfile, { force: true });
646
+ }
647
+ }
648
+ }
649
+ var yarn = {
650
+ detect: detect3,
651
+ read: read3,
652
+ create: create3,
653
+ remove: remove3,
654
+ clean: clean3,
655
+ convertLock: convertLock3
656
+ };
657
+ var yarn_default = yarn;
658
+
659
+ // src/managers/index.ts
660
+ var MANAGERS = {
661
+ pnpm: pnpm_default,
662
+ yarn: yarn_default,
663
+ npm: npm_default
664
+ };
665
+ var managers_default = MANAGERS;
666
+
667
+ // src/getWorkspaceDetails.ts
668
+ async function getWorkspaceDetails({
669
+ root
670
+ }) {
671
+ const { exists, absolute: workspaceRoot } = directoryInfo({
672
+ directory: root
673
+ });
674
+ if (!exists) {
675
+ throw new ConvertError(`Could not find directory at ${workspaceRoot}. Ensure the directory exists.`);
676
+ }
677
+ for (const { detect: detect4, read: read4 } of Object.values(managers_default)) {
678
+ if (await detect4({ workspaceRoot })) {
679
+ return read4({ workspaceRoot });
680
+ }
681
+ }
682
+ throw new ConvertError("Could not determine workspace manager. Add `packageManager` to `package.json` or ensure a lockfile is present.");
683
+ }
684
+
685
+ // src/commands/summary/index.ts
686
+ async function summary(directory) {
687
+ const logger = new Logger();
688
+ logger.hero();
689
+ const answer = await import_inquirer.default.prompt({
690
+ type: "input",
691
+ name: "directoryInput",
692
+ message: "Where is the root of the repo?",
693
+ when: !directory,
694
+ default: ".",
695
+ validate: (directory2) => {
696
+ const { exists: exists2, absolute } = directoryInfo({ directory: directory2 });
697
+ if (exists2) {
698
+ return true;
699
+ } else {
700
+ return `Directory ${import_chalk3.default.dim(`(${absolute})`)} does not exist`;
701
+ }
702
+ },
703
+ filter: (directory2) => directory2.trim()
704
+ });
705
+ const { directoryInput: selectedDirectory = directory } = answer;
706
+ const { exists, absolute: root } = directoryInfo({
707
+ directory: selectedDirectory
708
+ });
709
+ if (!exists) {
710
+ console.error(`Directory ${import_chalk3.default.dim(`(${root})`)} does not exist`);
711
+ return process.exit(1);
712
+ }
713
+ const project = await getWorkspaceDetails({ root });
714
+ const numWorkspaces = project.workspaceData.workspaces.length;
715
+ const hasWorkspaces = numWorkspaces > 0;
716
+ const workspacesByDirectory = {};
717
+ project.workspaceData.workspaces.forEach((workspace) => {
718
+ const workspacePath = import_path6.default.relative(root, workspace.paths.root);
719
+ const rootDirectory = workspacePath.split(import_path6.default.sep)[0];
720
+ if (!workspacesByDirectory[rootDirectory]) {
721
+ workspacesByDirectory[rootDirectory] = [];
722
+ }
723
+ workspacesByDirectory[rootDirectory].push(workspace);
724
+ });
725
+ const renderWorkspace = (w) => {
726
+ return `${w.name} (${import_chalk3.default.italic(`./${import_path6.default.relative(root, w.paths.root)}`)})`;
727
+ };
728
+ const renderDirectory = ({
729
+ number,
730
+ directory: directory2,
731
+ workspaces
732
+ }) => {
733
+ logger.indented(2, `${number}. ${import_chalk3.default.bold(directory2)}`);
734
+ workspaces.forEach((workspace, idx) => {
735
+ logger.indented(3, `${idx + 1}. ${renderWorkspace(workspace)}`);
736
+ });
737
+ };
738
+ logger.header(`Repository Summary`);
739
+ logger.indented(1, `${import_chalk3.default.underline(project.name)}:`);
740
+ logger.indented(1, `Package Manager: ${import_chalk3.default.bold(import_chalk3.default.italic(project.packageManager))}`);
741
+ if (hasWorkspaces) {
742
+ logger.indented(1, `Workspaces (${import_chalk3.default.bold(numWorkspaces.toString())}):`);
743
+ Object.keys(workspacesByDirectory).forEach((directory2, idx) => {
744
+ renderDirectory({
745
+ number: idx + 1,
746
+ directory: directory2,
747
+ workspaces: workspacesByDirectory[directory2]
748
+ });
749
+ });
750
+ logger.blankLine();
751
+ }
752
+ }
753
+
754
+ // src/commands/convert/index.ts
755
+ var import_inquirer2 = __toESM(require("inquirer"));
756
+ var import_chalk5 = __toESM(require("chalk"));
757
+ var import_turbo_utils = require("turbo-utils");
758
+
759
+ // src/convert.ts
760
+ var import_chalk4 = __toESM(require("chalk"));
761
+
762
+ // src/install.ts
763
+ var import_execa2 = __toESM(require("execa"));
764
+ var import_ora = __toESM(require("ora"));
765
+ var import_semver = require("semver");
766
+ var PACKAGE_MANAGERS = {
767
+ npm: [
768
+ {
769
+ name: "npm",
770
+ template: "npm",
771
+ command: "npm",
772
+ installArgs: ["install"],
773
+ version: "latest",
774
+ executable: "npx",
775
+ semver: "*"
776
+ }
777
+ ],
778
+ pnpm: [
779
+ {
780
+ name: "pnpm6",
781
+ template: "pnpm",
782
+ command: "pnpm",
783
+ installArgs: ["install"],
784
+ version: "latest-6",
785
+ executable: "pnpx",
786
+ semver: "6.x"
787
+ },
788
+ {
789
+ name: "pnpm",
790
+ template: "pnpm",
791
+ command: "pnpm",
792
+ installArgs: ["install"],
793
+ version: "latest",
794
+ executable: "pnpm dlx",
795
+ semver: ">=7"
796
+ }
797
+ ],
798
+ yarn: [
799
+ {
800
+ name: "yarn",
801
+ template: "yarn",
802
+ command: "yarn",
803
+ installArgs: ["install"],
804
+ version: "1.x",
805
+ executable: "npx",
806
+ semver: "<2"
807
+ },
808
+ {
809
+ name: "berry",
810
+ template: "berry",
811
+ command: "yarn",
812
+ installArgs: ["install", "--no-immutable"],
813
+ version: "stable",
814
+ executable: "yarn dlx",
815
+ semver: ">=2"
816
+ }
817
+ ]
818
+ };
819
+ async function install(args) {
820
+ const { to, logger, options } = args;
821
+ let packageManager = PACKAGE_MANAGERS[to.name].find((manager) => (0, import_semver.satisfies)(to.version, manager.semver));
822
+ if (!packageManager) {
823
+ throw new ConvertError("Unsupported package manager version.");
824
+ }
825
+ logger.subStep(`running "${packageManager.command} ${packageManager.installArgs}"`);
826
+ if (!(options == null ? void 0 : options.dry)) {
827
+ let spinner;
828
+ if (options == null ? void 0 : options.interactive) {
829
+ spinner = (0, import_ora.default)({
830
+ text: "Installing dependencies...",
831
+ spinner: {
832
+ frames: logger.installerFrames()
833
+ }
834
+ }).start();
835
+ }
836
+ try {
837
+ await (0, import_execa2.default)(packageManager.command, packageManager.installArgs, {
838
+ cwd: args.project.paths.root
839
+ });
840
+ logger.subStep(`dependencies installed`);
841
+ } catch (err) {
842
+ logger.subStepFailure(`failed to install dependencies`);
843
+ throw err;
844
+ } finally {
845
+ if (spinner) {
846
+ spinner.stop();
847
+ }
848
+ }
849
+ }
850
+ }
851
+ var install_default = install;
852
+
853
+ // src/convert.ts
854
+ async function convert({
855
+ project,
856
+ to,
857
+ logger,
858
+ options
859
+ }) {
860
+ logger.header(`Converting project from ${project.packageManager} to ${to.name}.`);
861
+ if (project.packageManager == to.name) {
862
+ throw new ConvertError("You are already using this package manager");
863
+ }
864
+ await managers_default[project.packageManager].remove({
865
+ project,
866
+ to,
867
+ logger,
868
+ options
869
+ });
870
+ await managers_default[to.name].create({ project, to, logger, options });
871
+ logger.mainStep("Installing dependencies");
872
+ if (!(options == null ? void 0 : options.skipInstall)) {
873
+ await managers_default[to.name].convertLock({ project, logger, options });
874
+ await install_default({ project, to, logger, options });
875
+ } else {
876
+ logger.subStep(import_chalk4.default.yellow("Skipping install"));
877
+ }
878
+ logger.mainStep(`Cleaning up ${project.packageManager} workspaces`);
879
+ await managers_default[project.packageManager].clean({ project, logger });
880
+ }
881
+ var convert_default = convert;
882
+
883
+ // src/commands/convert/index.ts
884
+ function isPackageManagerDisabled({
885
+ packageManager,
886
+ currentWorkspaceManger,
887
+ availablePackageManagers
888
+ }) {
889
+ if (currentWorkspaceManger === packageManager) {
890
+ return "already in use";
891
+ }
892
+ if (!availablePackageManagers[packageManager].available) {
893
+ return "not installed";
894
+ }
895
+ return false;
896
+ }
897
+ async function convertCommand(directory, packageManager, options) {
898
+ const logger = new Logger(options);
899
+ logger.hero();
900
+ logger.header("Welcome, let's convert your project.");
901
+ logger.blankLine();
902
+ const directoryAnswer = await import_inquirer2.default.prompt({
903
+ type: "input",
904
+ name: "directoryInput",
905
+ message: "Where is the root of your repo?",
906
+ when: !directory,
907
+ default: ".",
908
+ validate: (directory2) => {
909
+ const { exists: exists2, absolute } = directoryInfo({ directory: directory2 });
910
+ if (exists2) {
911
+ return true;
912
+ } else {
913
+ return `Directory ${import_chalk5.default.dim(`(${absolute})`)} does not exist`;
914
+ }
915
+ },
916
+ filter: (directory2) => directory2.trim()
917
+ });
918
+ const { directoryInput: selectedDirectory = directory } = directoryAnswer;
919
+ const { exists, absolute: root } = directoryInfo({
920
+ directory: selectedDirectory
921
+ });
922
+ if (!exists) {
923
+ console.error(`Directory ${import_chalk5.default.dim(`(${root})`)} does not exist`);
924
+ return process.exit(1);
925
+ }
926
+ const [project, availablePackageManagers] = await Promise.all([
927
+ getWorkspaceDetails({ root }),
928
+ (0, import_turbo_utils.getAvailablePackageManagers)()
929
+ ]);
930
+ const packageManagerAnswer = await import_inquirer2.default.prompt({
931
+ name: "packageManagerInput",
932
+ type: "list",
933
+ message: `Convert from ${project.packageManager} workspaces to:`,
934
+ when: !packageManager || !Object.keys(availablePackageManagers).includes(packageManager),
935
+ choices: ["npm", "pnpm", "yarn"].map((p) => ({
936
+ name: `${p} workspaces`,
937
+ value: p,
938
+ disabled: isPackageManagerDisabled({
939
+ packageManager: p,
940
+ currentWorkspaceManger: project.packageManager,
941
+ availablePackageManagers
942
+ })
943
+ }))
944
+ });
945
+ const {
946
+ packageManagerInput: selectedPackageManager = packageManager
947
+ } = packageManagerAnswer;
948
+ await convert_default({
949
+ project,
950
+ to: {
951
+ name: selectedPackageManager,
952
+ version: availablePackageManagers[selectedPackageManager].version
953
+ },
954
+ logger,
955
+ options
956
+ });
957
+ }
958
+
959
+ // package.json
960
+ var package_default = {
961
+ name: "@turbo/workspaces",
962
+ version: "0.0.1",
963
+ description: "Tools for working with package manager workspaces",
964
+ homepage: "https://turbo.build/repo",
965
+ license: "MPL-2.0",
966
+ repository: {
967
+ type: "git",
968
+ url: "https://github.com/vercel/turbo",
969
+ directory: "packages/turbo-workspaces"
970
+ },
971
+ bugs: {
972
+ url: "https://github.com/vercel/turbo/issues"
973
+ },
974
+ bin: "dist/cli.js",
975
+ main: "dist/index.js",
976
+ scripts: {
977
+ build: "tsup",
978
+ dev: "tsup --watch",
979
+ test: "jest",
980
+ lint: "eslint src/**/*.ts",
981
+ "check-types": "tsc --noEmit"
982
+ },
983
+ dependencies: {
984
+ chalk: "2.4.2",
985
+ commander: "^10.0.0",
986
+ execa: "5.1.1",
987
+ "fast-glob": "^3.2.12",
988
+ "fs-extra": "^10.1.0",
989
+ "gradient-string": "^2.0.0",
990
+ inquirer: "^8.0.0",
991
+ "js-yaml": "^4.1.0",
992
+ ora: "4.1.1",
993
+ rimraf: "^3.0.2",
994
+ semver: "^7.3.5",
995
+ "turbo-utils": "workspace:*",
996
+ "update-check": "^1.5.4"
997
+ },
998
+ devDependencies: {
999
+ "@types/chalk-animation": "^1.6.0",
1000
+ "@types/fs-extra": "^9.0.13",
1001
+ "@types/gradient-string": "^1.1.2",
1002
+ "@types/inquirer": "^7.3.1",
1003
+ "@types/jest": "^27.4.0",
1004
+ "@types/js-yaml": "^4.0.5",
1005
+ "@types/node": "^16.11.12",
1006
+ "@types/rimraf": "^3.0.2",
1007
+ "@types/semver": "^7.3.9",
1008
+ eslint: "^7.23.0",
1009
+ jest: "^27.4.3",
1010
+ semver: "^7.3.5",
1011
+ "strip-ansi": "^6.0.1",
1012
+ "ts-jest": "^27.1.1",
1013
+ tsconfig: "workspace:*",
1014
+ tsup: "^5.10.3",
1015
+ "turbo-test-utils": "workspace:*",
1016
+ typescript: "^4.5.5"
1017
+ },
1018
+ files: [
1019
+ "dist"
1020
+ ]
1021
+ };
1022
+
1023
+ // src/cli.ts
1024
+ var workspacesCli = new import_commander.Command();
1025
+ workspacesCli.name("@turbo/workspaces").description("Tools for working with package manager workspaces").version(package_default.version, "-v, --version", "output the current version");
1026
+ workspacesCli.command("convert").description("Convert project between workspace managers").argument("[path]", "Project root").argument("[package-manager]", "Package manager to convert to").option("--skip-install", "Do not run a package manager install after conversion", false).option("--dry", "Dry run (no changes are made to files)", false).option("--force", "Bypass Git safety checks and forcibly run conversion", false).action(convertCommand);
1027
+ workspacesCli.command("summary").description("Display a summary of the specified project").argument("[path]", "Project root").action(summary);
1028
+ workspacesCli.parseAsync().catch((error) => {
1029
+ console.log();
1030
+ if (error instanceof ConvertError) {
1031
+ console.log(import_chalk6.default.red(error.message));
1032
+ } else {
1033
+ console.log(import_chalk6.default.red("Unexpected error. Please report it as a bug:"));
1034
+ console.log(error.message);
1035
+ }
1036
+ console.log();
1037
+ process.exit(1);
1038
+ });