diff --git a/packages/create-turbo/__tests__/index.test.ts b/packages/create-turbo/__tests__/index.test.ts index 790855f0c7a3f..2984f6b9861d4 100644 --- a/packages/create-turbo/__tests__/index.test.ts +++ b/packages/create-turbo/__tests__/index.test.ts @@ -39,18 +39,9 @@ describe("create-turbo", () => { const mockAvailablePackageManagers = jest .spyOn(turboUtils, "getAvailablePackageManagers") .mockResolvedValue({ - npm: { - available: true, - version: "8.19.2", - }, - yarn: { - available: true, - version: "1.22.10", - }, - pnpm: { - available: true, - version: "7.22.2", - }, + npm: "8.19.2", + yarn: "1.22.10", + pnpm: "7.22.2", }); const mockCreateProject = jest diff --git a/packages/create-turbo/jest.config.js b/packages/create-turbo/jest.config.js index a273992d5cd70..b70ebdb187706 100644 --- a/packages/create-turbo/jest.config.js +++ b/packages/create-turbo/jest.config.js @@ -4,7 +4,11 @@ module.exports = { testEnvironment: "node", testPathIgnorePatterns: ["/__fixtures__/", "/__tests__/test-utils.ts"], coveragePathIgnorePatterns: ["/__fixtures__/", "/__tests__/test-utils.ts"], - transformIgnorePatterns: ["/node_modules/(?!(ansi-regex)/)"], + transformIgnorePatterns: [ + "node_modules/*", + "packages/turbo-utils/*", + "packages/turbo-workspaces/*", + ], modulePathIgnorePatterns: ["/node_modules", "/dist"], collectCoverage: true, verbose: process.env.RUNNER_DEBUG === "1", diff --git a/packages/create-turbo/src/commands/create/index.ts b/packages/create-turbo/src/commands/create/index.ts index e1df737607944..0db82fb9feb65 100644 --- a/packages/create-turbo/src/commands/create/index.ts +++ b/packages/create-turbo/src/commands/create/index.ts @@ -136,7 +136,7 @@ export async function create( skipTransforms || !selectedPackageManagerDetails ? { name: project.packageManager, - version: availablePackageManagers[project.packageManager].version, + version: availablePackageManagers[project.packageManager], } : selectedPackageManagerDetails; @@ -173,7 +173,7 @@ export async function create( // before we attempt an install if ( opts.skipTransforms && - !availablePackageManagers[project.packageManager].available + !availablePackageManagers[project.packageManager] ) { warn( `Unable to install dependencies - "${exampleName}" uses "${project.packageManager}" which could not be found.` diff --git a/packages/create-turbo/src/commands/create/prompts.ts b/packages/create-turbo/src/commands/create/prompts.ts index 8de4e8d87f40b..bd59aabd448b2 100644 --- a/packages/create-turbo/src/commands/create/prompts.ts +++ b/packages/create-turbo/src/commands/create/prompts.ts @@ -55,11 +55,11 @@ export async function packageManager({ // prompt for package manager if it wasn't provided as an argument, or if it was // provided, but isn't available (always allow npm) !packageManager || - !availablePackageManagers?.[packageManager as PackageManager]?.available, + !availablePackageManagers?.[packageManager as PackageManager], choices: ["npm", "pnpm", "yarn"].map((p) => ({ name: p, value: p, - disabled: availablePackageManagers?.[p as PackageManager]?.available + disabled: availablePackageManagers?.[p as PackageManager] ? false : `not installed`, })), @@ -72,6 +72,6 @@ export async function packageManager({ return { name: selectedPackageManager, - version: availablePackageManagers[selectedPackageManager].version, + version: availablePackageManagers[selectedPackageManager], }; } diff --git a/packages/turbo-codemod/__tests__/add-package-manager.test.ts b/packages/turbo-codemod/__tests__/add-package-manager.test.ts index 5bde7e0c816ae..4802fd1092efd 100644 --- a/packages/turbo-codemod/__tests__/add-package-manager.test.ts +++ b/packages/turbo-codemod/__tests__/add-package-manager.test.ts @@ -1,492 +1,334 @@ import { transformer } from "../src/transforms/add-package-manager"; import { setupTestFixtures } from "@turbo/test-utils"; -import fs from "fs-extra"; -import * as getPackageManager from "../src/utils/getPackageManager"; -import * as getPackageManagerVersion from "../src/utils/getPackageManagerVersion"; - -describe("add-package-manager", () => { - const { useFixture } = setupTestFixtures({ - directory: __dirname, - test: "add-package-manager", - }); - test("no package manager - basic", () => { - // load the fixture for the test - const { root, read } = useFixture({ fixture: "no-package-manager" }); - - const packageManager = "pnpm"; - const packageManagerVersion = "1.2.3"; - - // mock out workspace and version detection so we're not dependent on our actual repo - const mockGetPackageManagerVersion = jest - .spyOn(getPackageManagerVersion, "default") - .mockReturnValue(packageManagerVersion); - - const mockGetPackageManager = jest - .spyOn(getPackageManager, "default") - .mockReturnValue(packageManager); - - // package manager should not exist - expect( - JSON.parse(read("package.json") || "{}").packageManager - ).toBeUndefined(); - // run the transformer - const result = transformer({ - root, - options: { force: false, dry: false, print: false }, - }); +import { TransformerResults } from "../src/runner"; +import { TransformerOptions } from "../src/types"; - expect(mockGetPackageManager).toHaveBeenCalledWith({ directory: root }); - expect(mockGetPackageManagerVersion).toHaveBeenCalledWith( - packageManager, - root - ); - - // package manager should now exist - expect(JSON.parse(read("package.json") || "{}").packageManager).toBe( - `${packageManager}@${packageManagerVersion}` - ); - // result should be correct - expect(result.changes).toMatchInlineSnapshot(` - Object { - "package.json": Object { - "action": "modified", - "additions": 1, - "deletions": 0, +// imports for mocks +import fs from "fs-extra"; +import * as turboWorkspaces from "@turbo/workspaces"; +import * as turboUtils from "@turbo/utils"; +import { getWorkspaceDetailsMockReturnValue } from "./test-utils"; + +jest.mock("@turbo/workspaces", () => ({ + __esModule: true, + ...jest.requireActual("@turbo/workspaces"), +})); + +interface TestCase { + name: string; + fixture: string; + existingPackageManagerString: string | undefined; + packageManager: turboWorkspaces.PackageManager; + packageManagerVersion: string; + options: TransformerOptions; + result: TransformerResults; +} + +const TEST_CASES: Array = [ + { + name: "basic", + fixture: "no-package-manager", + existingPackageManagerString: undefined, + packageManager: "npm", + packageManagerVersion: "7.0.0", + options: { force: false, dry: false, print: false }, + result: { + changes: { + "package.json": { + action: "modified", + additions: 1, + deletions: 0, }, - } - `); - - mockGetPackageManagerVersion.mockRestore(); - mockGetPackageManager.mockRestore(); - }); - - test("no package manager - repeat run", () => { - // load the fixture for the test - const { root, read } = useFixture({ fixture: "no-package-manager" }); - - const packageManager = "pnpm"; - const packageManagerVersion = "1.2.3"; - - // mock out workspace and version detection so we're not dependent on our actual repo - const mockGetPackageManagerVersion = jest - .spyOn(getPackageManagerVersion, "default") - .mockReturnValue(packageManagerVersion); - - const mockGetPackageManager = jest - .spyOn(getPackageManager, "default") - .mockReturnValue(packageManager); - - // package manager should not exist - expect( - JSON.parse(read("package.json") || "{}").packageManager - ).toBeUndefined(); - // run the transformer - const result = transformer({ - root, - options: { force: false, dry: false, print: false }, - }); - - expect(mockGetPackageManager).toHaveBeenCalledWith({ directory: root }); - expect(mockGetPackageManagerVersion).toHaveBeenCalled(); - expect(mockGetPackageManagerVersion).toHaveBeenCalledWith( - packageManager, - root - ); - - // package manager should now exist - expect(JSON.parse(read("package.json") || "{}").packageManager).toBe( - `${packageManager}@${packageManagerVersion}` - ); - // result should be correct - expect(result.fatalError).toBeUndefined(); - expect(result.changes).toMatchInlineSnapshot(` - Object { - "package.json": Object { - "action": "modified", - "additions": 1, - "deletions": 0, + }, + }, + }, + { + name: "dry", + fixture: "no-package-manager", + existingPackageManagerString: undefined, + packageManager: "npm", + packageManagerVersion: "7.0.0", + options: { force: false, dry: true, print: false }, + result: { + changes: { + "package.json": { + action: "skipped", + additions: 1, + deletions: 0, }, - } - `); - - // run the transformer again to ensure nothing changes on a second run - const repeatResult = transformer({ - root, - options: { force: false, dry: false, print: false }, - }); - expect(repeatResult.fatalError).toBeUndefined(); - expect(repeatResult.changes).toMatchInlineSnapshot(` - Object { - "package.json": Object { - "action": "unchanged", - "additions": 0, - "deletions": 0, }, - } - `); - - mockGetPackageManagerVersion.mockRestore(); - mockGetPackageManager.mockRestore(); - }); - - test("no package manager - dry", () => { - // load the fixture for the test - const { root, read } = useFixture({ fixture: "no-package-manager" }); - - const packageManager = "npm"; - const packageManagerVersion = "1.2.3"; - - // mock out workspace and version detection so we're not dependent on our actual repo - const mockGetPackageManagerVersion = jest - .spyOn(getPackageManagerVersion, "default") - .mockReturnValue(packageManagerVersion); - const mockGetPackageManager = jest - .spyOn(getPackageManager, "default") - .mockReturnValue(packageManager); - - // package manager should not exist - expect( - JSON.parse(read("package.json") || "{}").packageManager - ).toBeUndefined(); - // run the transformer - const result = transformer({ - root, - options: { force: false, dry: true, print: false }, - }); - - expect(mockGetPackageManager).toHaveBeenCalledWith({ directory: root }); - expect(mockGetPackageManagerVersion).toHaveBeenCalledWith( - packageManager, - root - ); - - // package manager should not exist - expect( - JSON.parse(read("package.json") || "{}").packageManager - ).toBeUndefined(); - // result should be correct - expect(result.changes).toMatchInlineSnapshot(` - Object { - "package.json": Object { - "action": "skipped", - "additions": 1, - "deletions": 0, + }, + }, + { + name: "print", + fixture: "no-package-manager", + existingPackageManagerString: undefined, + packageManager: "yarn", + packageManagerVersion: "1.2.3", + options: { force: false, dry: false, print: true }, + result: { + changes: { + "package.json": { + action: "modified", + additions: 1, + deletions: 0, }, - } - `); - - mockGetPackageManagerVersion.mockRestore(); - }); - - test("no package manager - print", () => { - // load the fixture for the test - const { root, read } = useFixture({ fixture: "no-package-manager" }); - - const packageManager = "yarn"; - const packageManagerVersion = "1.2.3"; - - // mock out workspace and version detection so we're not dependent on our actual repo - const mockGetPackageManagerVersion = jest - .spyOn(getPackageManagerVersion, "default") - .mockReturnValue(packageManagerVersion); - - const mockGetPackageManager = jest - .spyOn(getPackageManager, "default") - .mockReturnValue(packageManager); - - // package manager should not exist - expect( - JSON.parse(read("package.json") || "{}").packageManager - ).toBeUndefined(); - // run the transformer - const result = transformer({ - root, - options: { force: false, dry: false, print: true }, - }); - - expect(mockGetPackageManager).toHaveBeenCalledWith({ directory: root }); - expect(mockGetPackageManagerVersion).toHaveBeenCalledWith( - packageManager, - root - ); - // package manager should now exist - expect(JSON.parse(read("package.json") || "{}").packageManager).toBe( - `${packageManager}@${packageManagerVersion}` - ); - // result should be correct - expect(result.changes).toMatchInlineSnapshot(` - Object { - "package.json": Object { - "action": "modified", - "additions": 1, - "deletions": 0, + }, + }, + }, + { + name: "print & dry", + fixture: "no-package-manager", + existingPackageManagerString: undefined, + packageManager: "pnpm", + packageManagerVersion: "1.2.3", + options: { force: false, dry: true, print: true }, + result: { + changes: { + "package.json": { + action: "skipped", + additions: 1, + deletions: 0, }, - } - `); - - mockGetPackageManagerVersion.mockRestore(); - }); - - test("no package manager - dry & print", () => { - // load the fixture for the test - const { root, read } = useFixture({ fixture: "no-package-manager" }); - - const packageManager = "npm"; - const packageManagerVersion = "1.2.3"; - - // mock out workspace and version detection so we're not dependent on our actual repo - const mockGetPackageManagerVersion = jest - .spyOn(getPackageManagerVersion, "default") - .mockReturnValue(packageManagerVersion); - - const mockGetPackageManager = jest - .spyOn(getPackageManager, "default") - .mockReturnValue(packageManager); - - // package manager should not exist - expect( - JSON.parse(read("package.json") || "{}").packageManager - ).toBeUndefined(); - // run the transformer - const result = transformer({ - root, - options: { force: false, dry: true, print: true }, - }); - - expect(mockGetPackageManager).toHaveBeenCalledWith({ directory: root }); - expect(mockGetPackageManagerVersion).toHaveBeenCalledWith( - packageManager, - root - ); - - // package manager should not exist - expect( - JSON.parse(read("package.json") || "{}").packageManager - ).toBeUndefined(); - // result should be correct - expect(result.changes).toMatchInlineSnapshot(` - Object { - "package.json": Object { - "action": "skipped", - "additions": 1, - "deletions": 0, + }, + }, + }, + { + name: "basic", + fixture: "has-package-manager", + existingPackageManagerString: "npm@1.2.3", + packageManager: "npm", + packageManagerVersion: "1.2.3", + options: { force: false, dry: false, print: false }, + result: { + changes: { + "package.json": { + action: "unchanged", + additions: 0, + deletions: 0, }, - } - `); - - mockGetPackageManagerVersion.mockRestore(); - mockGetPackageManager.mockRestore(); - }); - - test("package manager already exists", () => { - // load the fixture for the test - const { root, read } = useFixture({ fixture: "has-package-manager" }); - const packageManager = "npm"; - const packageManagerVersion = "1.2.3"; - - // mock out workspace and version detection so we're not dependent on our actual repo - const mockGetPackageManagerVersion = jest - .spyOn(getPackageManagerVersion, "default") - .mockReturnValue(packageManagerVersion); - - const mockGetPackageManager = jest - .spyOn(getPackageManager, "default") - .mockReturnValue(packageManager); - - // package manager should exist - expect(JSON.parse(read("package.json") || "{}").packageManager).toBe( - `${packageManager}@${packageManagerVersion}` - ); - // run the transformer - const result = transformer({ - root, - options: { force: false, dry: false, print: false }, - }); - - expect(mockGetPackageManager).toHaveBeenCalledWith({ directory: root }); - expect(mockGetPackageManagerVersion).toHaveBeenCalledWith( - packageManager, - root - ); - - // package manager should still exist - expect(JSON.parse(read("package.json") || "{}").packageManager).toBe( - `${packageManager}@${packageManagerVersion}` - ); - // result should be correct - expect(result.changes).toMatchInlineSnapshot(` - Object { - "package.json": Object { - "action": "unchanged", - "additions": 0, - "deletions": 0, + }, + }, + }, + { + name: "basic", + fixture: "wrong-package-manager", + existingPackageManagerString: "turbo@1.7.0", + packageManager: "pnpm", + packageManagerVersion: "1.2.3", + options: { force: false, dry: false, print: false }, + result: { + changes: { + "package.json": { + action: "modified", + additions: 1, + deletions: 1, }, - } - `); + }, + }, + }, +]; - mockGetPackageManagerVersion.mockRestore(); - mockGetPackageManager.mockRestore(); +describe("add-package-manager-2", () => { + const { useFixture } = setupTestFixtures({ + directory: __dirname, + test: "add-package-manager", }); - test("package manager exists but is wrong", () => { - // load the fixture for the test - const { root, read } = useFixture({ fixture: "wrong-package-manager" }); - - const packageManager = "pnpm"; - const packageManagerVersion = "1.2.3"; - - // mock out workspace and version detection so we're not dependent on our actual repo - const mockGetPackageManagerVersion = jest - .spyOn(getPackageManagerVersion, "default") - .mockReturnValue(packageManagerVersion); - - const mockGetPackageManager = jest - .spyOn(getPackageManager, "default") - .mockReturnValue(packageManager); - - // package manager should exist - expect(JSON.parse(read("package.json") || "{}").packageManager).toBe( - "turbo@1.7.0" - ); - // run the transformer - const result = transformer({ - root, - options: { force: false, dry: false, print: false }, - }); - - expect(mockGetPackageManager).toHaveBeenCalledWith({ directory: root }); - expect(mockGetPackageManagerVersion).toHaveBeenCalledWith( + test.each(TEST_CASES)( + "$fixture - $name with $packageManager@$packageManagerVersion using $options", + async ({ + fixture, + existingPackageManagerString, packageManager, - root - ); - - // package manager should still exist - expect(JSON.parse(read("package.json") || "{}").packageManager).toBe( - `${packageManager}@${packageManagerVersion}` - ); - // result should be correct - expect(result.changes).toMatchInlineSnapshot(` - Object { - "package.json": Object { - "action": "modified", - "additions": 1, - "deletions": 1, - }, - } - `); - - mockGetPackageManagerVersion.mockRestore(); - mockGetPackageManager.mockRestore(); - }); - - test("errors when unable to determine workspace manager", () => { - // load the fixture for the test - const { root, read } = useFixture({ fixture: "no-package-manager" }); - - const mockGetPackageManager = jest - .spyOn(getPackageManager, "default") - .mockReturnValue(undefined); - - // package manager should not exist - expect( - JSON.parse(read("package.json") || "{}").packageManager - ).toBeUndefined(); - // run the transformer - const result = transformer({ - root, - options: { force: false, dry: false, print: false }, - }); - - expect(mockGetPackageManager).toHaveBeenCalledTimes(1); - expect(mockGetPackageManager).toHaveBeenCalledWith({ directory: root }); + packageManagerVersion, + options, + result, + }) => { + // load the fixture for the test + const { root, read } = useFixture({ fixture }); + + // mock out workspace and version detection so we're not dependent on our actual repo + const mockGetAvailablePackageManagers = jest + .spyOn(turboUtils, "getAvailablePackageManagers") + .mockResolvedValue({ + pnpm: packageManager === "pnpm" ? packageManagerVersion : undefined, + npm: packageManager === "npm" ? packageManagerVersion : undefined, + yarn: packageManager === "yarn" ? packageManagerVersion : undefined, + }); + + const mockGetWorkspaceDetails = jest + .spyOn(turboWorkspaces, "getWorkspaceDetails") + .mockResolvedValue( + getWorkspaceDetailsMockReturnValue({ + root, + packageManager, + }) + ); + + // verify package manager + expect(JSON.parse(read("package.json") || "{}").packageManager).toEqual( + existingPackageManagerString + ); + + // run the transformer + const transformerResult = await transformer({ + root, + options, + }); - // result should be correct - // result should be correct - expect(result.fatalError?.message).toMatch( - /Unable to determine package manager for .*?/ - ); + expect(mockGetAvailablePackageManagers).toHaveBeenCalled(); + expect(mockGetWorkspaceDetails).toHaveBeenCalled(); - mockGetPackageManager.mockRestore(); - }); + expect(JSON.parse(read("package.json") || "{}").packageManager).toEqual( + options.dry ? undefined : `${packageManager}@${packageManagerVersion}` + ); - test("errors when unable to determine package manager", () => { - // load the fixture for the test - const { root, read } = useFixture({ fixture: "no-package-manager" }); + // result should be correct + expect(transformerResult.changes).toMatchObject(result.changes); - const mockGetPackageManagerVersion = jest - .spyOn(getPackageManagerVersion, "default") - .mockImplementation(() => { - throw new Error("package manager not supported"); + // run the transformer again to ensure nothing changes on a second run + const repeatResult = await transformer({ + root, + options, + }); + expect(repeatResult.fatalError).toBeUndefined(); + expect(repeatResult.changes).toMatchObject({ + "package.json": { + action: options.dry ? "skipped" : "unchanged", + additions: options.dry ? result.changes["package.json"].additions : 0, + deletions: options.dry ? result.changes["package.json"].deletions : 0, + }, }); - // package manager should not exist - expect( - JSON.parse(read("package.json") || "{}").packageManager - ).toBeUndefined(); - // run the transformer - const result = transformer({ - root, - options: { force: false, dry: false, print: false }, - }); + mockGetAvailablePackageManagers.mockRestore(); + mockGetWorkspaceDetails.mockRestore(); + } + ); + + describe("errors", () => { + test("unable to determine workspace manager", async () => { + // load the fixture for the test + const { root, read } = useFixture({ fixture: "no-package-manager" }); + + const mockGetWorkspaceDetails = jest + .spyOn(turboWorkspaces, "getWorkspaceDetails") + .mockRejectedValue(undefined); + + // package manager should not exist + expect( + JSON.parse(read("package.json") || "{}").packageManager + ).toBeUndefined(); + // run the transformer + const result = await transformer({ + root, + options: { force: false, dry: false, print: false }, + }); - expect(mockGetPackageManagerVersion).toHaveBeenCalledTimes(1); + expect(mockGetWorkspaceDetails).toHaveBeenCalledTimes(1); - // result should be correct - expect(result.fatalError?.message).toMatch( - /Unable to determine package manager version for .*?/ - ); + // result should be correct + expect(result.fatalError?.message).toMatch( + /Unable to determine package manager for .*?/ + ); - mockGetPackageManagerVersion.mockRestore(); - }); + mockGetWorkspaceDetails.mockRestore(); + }); - test("errors when unable to write json", () => { - // load the fixture for the test - const { root, read } = useFixture({ fixture: "no-package-manager" }); + test("unable to determine package manager version", async () => { + // load the fixture for the test + const { root, read } = useFixture({ fixture: "no-package-manager" }); + + const mockGetAvailablePackageManagers = jest + .spyOn(turboUtils, "getAvailablePackageManagers") + .mockResolvedValue({ + pnpm: undefined, + npm: undefined, + yarn: undefined, + }); + + const mockGetWorkspaceDetails = jest + .spyOn(turboWorkspaces, "getWorkspaceDetails") + .mockResolvedValue( + getWorkspaceDetailsMockReturnValue({ + root, + packageManager: "npm", + }) + ); + + // package manager should not exist + expect( + JSON.parse(read("package.json") || "{}").packageManager + ).toBeUndefined(); + // run the transformer + const result = await transformer({ + root, + options: { force: false, dry: false, print: false }, + }); - const packageManager = "pnpm"; - const packageManagerVersion = "1.2.3"; + expect(mockGetAvailablePackageManagers).toHaveBeenCalledTimes(1); + expect(mockGetWorkspaceDetails).toHaveBeenCalledTimes(1); - // mock out workspace and version detection so we're not dependent on our actual repo - const mockGetPackageManagerVersion = jest - .spyOn(getPackageManagerVersion, "default") - .mockReturnValue(packageManagerVersion); + // result should be correct + expect(result.fatalError?.message).toMatch( + /Unable to determine package manager version for .*?/ + ); - const mockGetPackageManager = jest - .spyOn(getPackageManager, "default") - .mockReturnValue(packageManager); + mockGetAvailablePackageManagers.mockRestore(); + mockGetWorkspaceDetails.mockRestore(); + }); - const mockWriteJsonSync = jest - .spyOn(fs, "writeJsonSync") - .mockImplementation(() => { - throw new Error("could not write file"); + test("unable to write json", async () => { + // load the fixture for the test + const { root, read } = useFixture({ fixture: "no-package-manager" }); + + const packageManager = "pnpm"; + const packageManagerVersion = "1.2.3"; + + // mock out workspace and version detection so we're not dependent on our actual repo + const mockGetAvailablePackageManagers = jest + .spyOn(turboUtils, "getAvailablePackageManagers") + .mockResolvedValue({ + pnpm: packageManagerVersion, + npm: undefined, + yarn: undefined, + }); + + const mockGetWorkspaceDetails = jest + .spyOn(turboWorkspaces, "getWorkspaceDetails") + .mockResolvedValue( + getWorkspaceDetailsMockReturnValue({ + root, + packageManager, + }) + ); + + const mockWriteJsonSync = jest + .spyOn(fs, "writeJsonSync") + .mockImplementation(() => { + throw new Error("could not write file"); + }); + + // package manager should not exist + expect( + JSON.parse(read("package.json") || "{}").packageManager + ).toBeUndefined(); + // run the transformer + const result = await transformer({ + root, + options: { force: false, dry: false, print: false }, }); - // package manager should not exist - expect( - JSON.parse(read("package.json") || "{}").packageManager - ).toBeUndefined(); - // run the transformer - const result = transformer({ - root, - options: { force: false, dry: false, print: false }, - }); + // package manager should still not exist (we couldn't write it) + expect( + JSON.parse(read("package.json") || "{}").packageManager + ).toBeUndefined(); - expect(mockGetPackageManager).toHaveBeenCalledWith({ directory: root }); - expect(mockGetPackageManagerVersion).toHaveBeenCalledWith( - packageManager, - root - ); - - // package manager should still not exist (we couldn't write it) - expect( - JSON.parse(read("package.json") || "{}").packageManager - ).toBeUndefined(); - - // result should be correct - expect(result.fatalError?.message).toMatch( - "Encountered an error while transforming files" - ); - expect(result.changes).toMatchInlineSnapshot(` + // result should be correct + expect(result.fatalError?.message).toMatch( + "Encountered an error while transforming files" + ); + expect(result.changes).toMatchInlineSnapshot(` Object { "package.json": Object { "action": "error", @@ -497,8 +339,9 @@ describe("add-package-manager", () => { } `); - mockWriteJsonSync.mockRestore(); - mockGetPackageManagerVersion.mockRestore(); - mockGetPackageManager.mockRestore(); + mockWriteJsonSync.mockRestore(); + mockGetAvailablePackageManagers.mockRestore(); + mockGetWorkspaceDetails.mockRestore(); + }); }); }); diff --git a/packages/turbo-codemod/__tests__/get-transforms-for-migration.test.ts b/packages/turbo-codemod/__tests__/get-transforms-for-migration.test.ts index b19dcbd3ee77d..584f8de5a2bbb 100644 --- a/packages/turbo-codemod/__tests__/get-transforms-for-migration.test.ts +++ b/packages/turbo-codemod/__tests__/get-transforms-for-migration.test.ts @@ -7,7 +7,7 @@ describe("get-transforms-for-migration", () => { toVersion: "1.10.0", }); - expect(results.map((transform) => transform.value)).toEqual([ + expect(results.map((transform) => transform.name)).toEqual([ "add-package-manager", "create-turbo-config", "migrate-env-var-dependencies", diff --git a/packages/turbo-codemod/__tests__/get-turbo-upgrade-command.test.ts b/packages/turbo-codemod/__tests__/get-turbo-upgrade-command.test.ts index 1015589ac1001..737ce5b06636f 100644 --- a/packages/turbo-codemod/__tests__/get-turbo-upgrade-command.test.ts +++ b/packages/turbo-codemod/__tests__/get-turbo-upgrade-command.test.ts @@ -1,353 +1,429 @@ import { setupTestFixtures } from "@turbo/test-utils"; import getTurboUpgradeCommand from "../src/commands/migrate/steps/getTurboUpgradeCommand"; import * as utils from "../src/commands/migrate/utils"; -import * as getPackageManager from "../src/utils/getPackageManager"; -import * as getPackageManagerVersion from "../src/utils/getPackageManagerVersion"; - -const LOCAL_INSTALL_COMMANDS = [ +import { getWorkspaceDetailsMockReturnValue } from "./test-utils"; + +// imports for mocks +import * as turboWorkspaces from "@turbo/workspaces"; +import * as turboUtils from "@turbo/utils"; + +jest.mock("@turbo/workspaces", () => ({ + __esModule: true, + ...jest.requireActual("@turbo/workspaces"), +})); + +interface TestCase { + version: string; + packageManager: turboWorkspaces.PackageManager; + packageManagerVersion: string; + fixture: string; + expected: string; +} + +const LOCAL_INSTALL_COMMANDS: Array = [ // npm - workspaces - [ - "latest", - "npm", - "7.0.0", - "normal-workspaces-dev-install", - "npm install turbo@latest --save-dev", - ], - [ - "1.6.3", - "npm", - "7.0.0", - "normal-workspaces-dev-install", - "npm install turbo@1.6.3 --save-dev", - ], - [ - "canary", - "npm", - "7.0.0", - "normal-workspaces-dev-install", - "npm install turbo@canary --save-dev", - ], - ["latest", "npm", "7.0.0", "normal-workspaces", "npm install turbo@latest"], + { + version: "latest", + packageManager: "npm", + packageManagerVersion: "7.0.0", + fixture: "normal-workspaces-dev-install", + expected: "npm install turbo@latest --save-dev", + }, + { + version: "1.6.3", + packageManager: "npm", + packageManagerVersion: "7.0.0", + fixture: "normal-workspaces-dev-install", + expected: "npm install turbo@1.6.3 --save-dev", + }, + { + version: "canary", + packageManager: "npm", + packageManagerVersion: "7.0.0", + fixture: "normal-workspaces-dev-install", + expected: "npm install turbo@canary --save-dev", + }, + { + version: "latest", + packageManager: "npm", + packageManagerVersion: "7.0.0", + fixture: "normal-workspaces", + expected: "npm install turbo@latest", + }, // npm - single package - [ - "latest", - "npm", - "7.0.0", - "single-package-dev-install", - "npm install turbo@latest --save-dev", - ], - ["latest", "npm", "7.0.0", "single-package", "npm install turbo@latest"], + { + version: "latest", + packageManager: "npm", + packageManagerVersion: "7.0.0", + fixture: "single-package-dev-install", + expected: "npm install turbo@latest --save-dev", + }, + { + version: "latest", + packageManager: "npm", + packageManagerVersion: "7.0.0", + fixture: "single-package", + expected: "npm install turbo@latest", + }, // pnpm - workspaces - [ - "latest", - "pnpm", - "7.0.0", - "pnpm-workspaces-dev-install", - "pnpm install turbo@latest --save-dev -w", - ], - [ - "1.6.3", - "pnpm", - "7.0.0", - "pnpm-workspaces-dev-install", - "pnpm install turbo@1.6.3 --save-dev -w", - ], - [ - "canary", - "pnpm", - "7.0.0", - "pnpm-workspaces-dev-install", - "pnpm install turbo@canary --save-dev -w", - ], - [ - "latest", - "pnpm", - "7.0.0", - "pnpm-workspaces", - "pnpm install turbo@latest -w", - ], + { + version: "latest", + packageManager: "pnpm", + packageManagerVersion: "7.0.0", + fixture: "pnpm-workspaces-dev-install", + expected: "pnpm add turbo@latest --save-dev -w", + }, + { + version: "1.6.3", + packageManager: "pnpm", + packageManagerVersion: "7.0.0", + fixture: "pnpm-workspaces-dev-install", + expected: "pnpm add turbo@1.6.3 --save-dev -w", + }, + { + version: "canary", + packageManager: "pnpm", + packageManagerVersion: "7.0.0", + fixture: "pnpm-workspaces-dev-install", + expected: "pnpm add turbo@canary --save-dev -w", + }, + { + version: "latest", + packageManager: "pnpm", + packageManagerVersion: "7.0.0", + fixture: "pnpm-workspaces", + expected: "pnpm add turbo@latest -w", + }, // pnpm - single package - [ - "latest", - "pnpm", - "7.0.0", - "single-package-dev-install", - "pnpm install turbo@latest --save-dev", - ], - ["latest", "pnpm", "7.0.0", "single-package", "pnpm install turbo@latest"], + { + version: "latest", + packageManager: "pnpm", + packageManagerVersion: "7.0.0", + fixture: "single-package-dev-install", + expected: "pnpm add turbo@latest --save-dev", + }, + { + version: "latest", + packageManager: "pnpm", + packageManagerVersion: "7.0.0", + fixture: "single-package", + expected: "pnpm add turbo@latest", + }, // yarn 1.x - workspaces - [ - "latest", - "yarn", - "1.22.19", - "normal-workspaces-dev-install", - "yarn add turbo@latest --dev -W", - ], - [ - "latest", - "yarn", - "1.22.19", - "normal-workspaces", - "yarn add turbo@latest -W", - ], - [ - "1.6.3", - "yarn", - "1.22.19", - "normal-workspaces-dev-install", - "yarn add turbo@1.6.3 --dev -W", - ], - [ - "canary", - "yarn", - "1.22.19", - "normal-workspaces-dev-install", - "yarn add turbo@canary --dev -W", - ], + { + version: "latest", + packageManager: "yarn", + packageManagerVersion: "1.22.19", + fixture: "normal-workspaces-dev-install", + expected: "yarn add turbo@latest --dev -W", + }, + { + version: "latest", + packageManager: "yarn", + packageManagerVersion: "1.22.19", + fixture: "normal-workspaces", + expected: "yarn add turbo@latest -W", + }, + { + version: "1.6.3", + packageManager: "yarn", + packageManagerVersion: "1.22.19", + fixture: "normal-workspaces-dev-install", + expected: "yarn add turbo@1.6.3 --dev -W", + }, + { + version: "canary", + packageManager: "yarn", + packageManagerVersion: "1.22.19", + fixture: "normal-workspaces-dev-install", + expected: "yarn add turbo@canary --dev -W", + }, // yarn 1.x - single package - [ - "latest", - "yarn", - "1.22.19", - "single-package-dev-install", - "yarn add turbo@latest --dev", - ], - ["latest", "yarn", "1.22.19", "single-package", "yarn add turbo@latest"], + { + version: "latest", + packageManager: "yarn", + packageManagerVersion: "1.22.19", + fixture: "single-package-dev-install", + expected: "yarn add turbo@latest --dev", + }, + { + version: "latest", + packageManager: "yarn", + packageManagerVersion: "1.22.19", + fixture: "single-package", + expected: "yarn add turbo@latest", + }, // yarn 2.x - workspaces - [ - "latest", - "yarn", - "2.3.4", - "normal-workspaces-dev-install", - "yarn add turbo@latest --dev", - ], - ["latest", "yarn", "2.3.4", "normal-workspaces", "yarn add turbo@latest"], - [ - "1.6.3", - "yarn", - "2.3.4", - "normal-workspaces-dev-install", - "yarn add turbo@1.6.3 --dev", - ], - [ - "canary", - "yarn", - "2.3.4", - "normal-workspaces-dev-install", - "yarn add turbo@canary --dev", - ], + { + version: "latest", + packageManager: "yarn", + packageManagerVersion: "2.3.4", + fixture: "normal-workspaces-dev-install", + expected: "yarn add turbo@latest --dev", + }, + { + version: "latest", + packageManager: "yarn", + packageManagerVersion: "2.3.4", + fixture: "normal-workspaces", + expected: "yarn add turbo@latest", + }, + { + version: "1.6.3", + packageManager: "yarn", + packageManagerVersion: "2.3.4", + fixture: "normal-workspaces-dev-install", + expected: "yarn add turbo@1.6.3 --dev", + }, + { + version: "canary", + packageManager: "yarn", + packageManagerVersion: "2.3.4", + fixture: "normal-workspaces-dev-install", + expected: "yarn add turbo@canary --dev", + }, // yarn 2.x - single package - [ - "latest", - "yarn", - "2.3.4", - "single-package-dev-install", - "yarn add turbo@latest --dev", - ], - ["latest", "yarn", "2.3.4", "single-package", "yarn add turbo@latest"], + { + version: "latest", + packageManager: "yarn", + packageManagerVersion: "2.3.4", + fixture: "single-package-dev-install", + expected: "yarn add turbo@latest --dev", + }, + { + version: "latest", + packageManager: "yarn", + packageManagerVersion: "2.3.4", + fixture: "single-package", + expected: "yarn add turbo@latest", + }, // yarn 3.x - workspaces - [ - "latest", - "yarn", - "3.3.4", - "normal-workspaces-dev-install", - "yarn add turbo@latest --dev", - ], - ["latest", "yarn", "3.3.4", "normal-workspaces", "yarn add turbo@latest"], - [ - "1.6.3", - "yarn", - "3.3.4", - "normal-workspaces-dev-install", - "yarn add turbo@1.6.3 --dev", - ], - [ - "canary", - "yarn", - "3.3.4", - "normal-workspaces-dev-install", - "yarn add turbo@canary --dev", - ], + { + version: "latest", + packageManager: "yarn", + packageManagerVersion: "3.3.4", + fixture: "normal-workspaces-dev-install", + expected: "yarn add turbo@latest --dev", + }, + { + version: "latest", + packageManager: "yarn", + packageManagerVersion: "3.3.4", + fixture: "normal-workspaces", + expected: "yarn add turbo@latest", + }, + { + version: "1.6.3", + packageManager: "yarn", + packageManagerVersion: "3.3.4", + fixture: "normal-workspaces-dev-install", + expected: "yarn add turbo@1.6.3 --dev", + }, + { + version: "canary", + packageManager: "yarn", + packageManagerVersion: "3.3.4", + fixture: "normal-workspaces-dev-install", + expected: "yarn add turbo@canary --dev", + }, // yarn 3.x - single package - [ - "latest", - "yarn", - "3.3.4", - "single-package-dev-install", - "yarn add turbo@latest --dev", - ], - ["latest", "yarn", "3.3.4", "single-package", "yarn add turbo@latest"], + { + version: "latest", + packageManager: "yarn", + packageManagerVersion: "3.3.4", + fixture: "single-package-dev-install", + expected: "yarn add turbo@latest --dev", + }, + { + version: "latest", + packageManager: "yarn", + packageManagerVersion: "3.3.4", + fixture: "single-package", + expected: "yarn add turbo@latest", + }, ]; -const GLOBAL_INSTALL_COMMANDS = [ +const GLOBAL_INSTALL_COMMANDS: Array = [ // npm - [ - "latest", - "npm", - "7.0.0", - "normal-workspaces-dev-install", - "npm install turbo@latest --global", - ], - [ - "1.6.3", - "npm", - "7.0.0", - "normal-workspaces-dev-install", - "npm install turbo@1.6.3 --global", - ], - [ - "latest", - "npm", - "7.0.0", - "normal-workspaces", - "npm install turbo@latest --global", - ], - [ - "latest", - "npm", - "7.0.0", - "single-package", - "npm install turbo@latest --global", - ], - [ - "latest", - "npm", - "7.0.0", - "single-package-dev-install", - "npm install turbo@latest --global", - ], + { + version: "latest", + packageManager: "npm", + packageManagerVersion: "7.0.0", + fixture: "normal-workspaces-dev-install", + expected: "npm install turbo@latest --global", + }, + { + version: "1.6.3", + packageManager: "npm", + packageManagerVersion: "7.0.0", + fixture: "normal-workspaces-dev-install", + expected: "npm install turbo@1.6.3 --global", + }, + { + version: "latest", + packageManager: "npm", + packageManagerVersion: "7.0.0", + fixture: "normal-workspaces", + expected: "npm install turbo@latest --global", + }, + { + version: "latest", + packageManager: "npm", + packageManagerVersion: "7.0.0", + fixture: "single-package", + expected: "npm install turbo@latest --global", + }, + { + version: "latest", + packageManager: "npm", + packageManagerVersion: "7.0.0", + fixture: "single-package-dev-install", + expected: "npm install turbo@latest --global", + }, // pnpm - [ - "latest", - "pnpm", - "7.0.0", - "pnpm-workspaces-dev-install", - "pnpm install turbo@latest --global", - ], - [ - "1.6.3", - "pnpm", - "7.0.0", - "pnpm-workspaces-dev-install", - "pnpm install turbo@1.6.3 --global", - ], - [ - "latest", - "pnpm", - "7.0.0", - "pnpm-workspaces", - "pnpm install turbo@latest --global", - ], - [ - "latest", - "pnpm", - "7.0.0", - "single-package", - "pnpm install turbo@latest --global", - ], - [ - "latest", - "pnpm", - "7.0.0", - "single-package-dev-install", - "pnpm install turbo@latest --global", - ], + { + version: "latest", + packageManager: "pnpm", + packageManagerVersion: "7.0.0", + fixture: "pnpm-workspaces-dev-install", + expected: "pnpm add turbo@latest --global", + }, + { + version: "1.6.3", + packageManager: "pnpm", + packageManagerVersion: "7.0.0", + fixture: "pnpm-workspaces-dev-install", + expected: "pnpm add turbo@1.6.3 --global", + }, + { + version: "latest", + packageManager: "pnpm", + packageManagerVersion: "7.0.0", + fixture: "pnpm-workspaces", + expected: "pnpm add turbo@latest --global", + }, + { + version: "latest", + packageManager: "pnpm", + packageManagerVersion: "7.0.0", + fixture: "single-package", + expected: "pnpm add turbo@latest --global", + }, + { + version: "latest", + packageManager: "pnpm", + packageManagerVersion: "7.0.0", + fixture: "single-package-dev-install", + expected: "pnpm add turbo@latest --global", + }, // yarn 1.x - [ - "latest", - "yarn", - "1.22.19", - "normal-workspaces-dev-install", - "yarn global add turbo@latest", - ], - [ - "latest", - "yarn", - "1.22.19", - "normal-workspaces", - "yarn global add turbo@latest", - ], - [ - "1.6.3", - "yarn", - "1.22.19", - "normal-workspaces-dev-install", - "yarn global add turbo@1.6.3", - ], - [ - "latest", - "yarn", - "1.22.19", - "single-package", - "yarn global add turbo@latest", - ], - [ - "latest", - "yarn", - "1.22.19", - "single-package-dev-install", - "yarn global add turbo@latest", - ], + { + version: "latest", + packageManager: "yarn", + packageManagerVersion: "1.22.19", + fixture: "normal-workspaces-dev-install", + expected: "yarn global add turbo@latest", + }, + { + version: "latest", + packageManager: "yarn", + packageManagerVersion: "1.22.19", + fixture: "normal-workspaces", + expected: "yarn global add turbo@latest", + }, + { + version: "1.6.3", + packageManager: "yarn", + packageManagerVersion: "1.22.19", + fixture: "normal-workspaces-dev-install", + expected: "yarn global add turbo@1.6.3", + }, + { + version: "latest", + packageManager: "yarn", + packageManagerVersion: "1.22.19", + fixture: "single-package", + expected: "yarn global add turbo@latest", + }, + { + version: "latest", + packageManager: "yarn", + packageManagerVersion: "1.22.19", + fixture: "single-package-dev-install", + expected: "yarn global add turbo@latest", + }, // yarn 2.x - [ - "latest", - "yarn", - "2.3.4", - "normal-workspaces-dev-install", - "yarn global add turbo@latest", - ], - [ - "latest", - "yarn", - "2.3.4", - "normal-workspaces", - "yarn global add turbo@latest", - ], - [ - "1.6.3", - "yarn", - "2.3.4", - "normal-workspaces-dev-install", - "yarn global add turbo@1.6.3", - ], - ["latest", "yarn", "2.3.4", "single-package", "yarn global add turbo@latest"], - [ - "latest", - "yarn", - "2.3.4", - "single-package-dev-install", - "yarn global add turbo@latest", - ], + { + version: "latest", + packageManager: "yarn", + packageManagerVersion: "2.3.4", + fixture: "normal-workspaces-dev-install", + expected: "yarn global add turbo@latest", + }, + { + version: "latest", + packageManager: "yarn", + packageManagerVersion: "2.3.4", + fixture: "normal-workspaces", + expected: "yarn global add turbo@latest", + }, + { + version: "1.6.3", + packageManager: "yarn", + packageManagerVersion: "2.3.4", + fixture: "normal-workspaces-dev-install", + expected: "yarn global add turbo@1.6.3", + }, + { + version: "latest", + packageManager: "yarn", + packageManagerVersion: "2.3.4", + fixture: "single-package", + expected: "yarn global add turbo@latest", + }, + { + version: "latest", + packageManager: "yarn", + packageManagerVersion: "2.3.4", + fixture: "single-package-dev-install", + expected: "yarn global add turbo@latest", + }, // yarn 3.x - [ - "latest", - "yarn", - "3.3.3", - "normal-workspaces-dev-install", - "yarn global add turbo@latest", - ], - [ - "latest", - "yarn", - "3.3.3", - "normal-workspaces", - "yarn global add turbo@latest", - ], - [ - "1.6.3", - "yarn", - "3.3.3", - "normal-workspaces-dev-install", - "yarn global add turbo@1.6.3", - ], - ["latest", "yarn", "3.3.4", "single-package", "yarn global add turbo@latest"], - [ - "latest", - "yarn", - "3.3.4", - "single-package-dev-install", - "yarn global add turbo@latest", - ], + { + version: "latest", + packageManager: "yarn", + packageManagerVersion: "3.3.3", + fixture: "normal-workspaces-dev-install", + expected: "yarn global add turbo@latest", + }, + { + version: "latest", + packageManager: "yarn", + packageManagerVersion: "3.3.3", + fixture: "normal-workspaces", + expected: "yarn global add turbo@latest", + }, + { + version: "1.6.3", + packageManager: "yarn", + packageManagerVersion: "3.3.3", + fixture: "normal-workspaces-dev-install", + expected: "yarn global add turbo@1.6.3", + }, + { + version: "latest", + packageManager: "yarn", + packageManagerVersion: "3.3.4", + fixture: "single-package", + expected: "yarn global add turbo@latest", + }, + { + version: "latest", + packageManager: "yarn", + packageManagerVersion: "3.3.4", + fixture: "single-package-dev-install", + expected: "yarn global add turbo@latest", + }, ]; describe("get-turbo-upgrade-command", () => { @@ -357,14 +433,14 @@ describe("get-turbo-upgrade-command", () => { }); test.each(LOCAL_INSTALL_COMMANDS)( - "returns correct upgrade command for local install of turbo@%s using %s@%s (fixture: %s)", - ( - turboVersion, + "returns correct upgrade command for local install of turbo@$version using $packageManager@$packageManagerVersion (fixture: $fixture)", + async ({ + version, packageManager, packageManagerVersion, fixture, - expectedUpgradeCommand - ) => { + expected, + }) => { const { root } = useFixture({ fixture, }); @@ -372,41 +448,59 @@ describe("get-turbo-upgrade-command", () => { const mockedExec = jest .spyOn(utils, "exec") .mockImplementation((command: string) => { - // fail the check for the turbo, and package manager bins to force local + // fail the check for global turbo if (command.includes("bin")) { return undefined; } }); - const mockedGetPackageManagerVersion = jest - .spyOn(getPackageManagerVersion, "default") - .mockReturnValue(packageManagerVersion); - const mockedGetPackageManager = jest - .spyOn(getPackageManager, "default") - .mockReturnValue(packageManager as getPackageManager.PackageManager); + const mockGetPackageManagersBinPaths = jest + .spyOn(turboUtils, "getPackageManagersBinPaths") + .mockResolvedValue({ + pnpm: undefined, + npm: undefined, + yarn: undefined, + }); + const mockGetAvailablePackageManagers = jest + .spyOn(turboUtils, "getAvailablePackageManagers") + .mockResolvedValue({ + pnpm: packageManager === "pnpm" ? packageManagerVersion : undefined, + npm: packageManager === "npm" ? packageManagerVersion : undefined, + yarn: packageManager === "yarn" ? packageManagerVersion : undefined, + }); + + const project = getWorkspaceDetailsMockReturnValue({ + root, + packageManager, + singlePackage: fixture.includes("single-package"), + }); + const mockGetWorkspaceDetails = jest + .spyOn(turboWorkspaces, "getWorkspaceDetails") + .mockResolvedValue(project); // get the command - const upgradeCommand = getTurboUpgradeCommand({ - directory: root, - to: turboVersion === "latest" ? undefined : turboVersion, + const upgradeCommand = await getTurboUpgradeCommand({ + project, + to: version === "latest" ? undefined : version, }); - expect(upgradeCommand).toEqual(expectedUpgradeCommand); + expect(upgradeCommand).toEqual(expected); mockedExec.mockRestore(); - mockedGetPackageManager.mockRestore(); - mockedGetPackageManagerVersion.mockRestore(); + mockGetPackageManagersBinPaths.mockRestore(); + mockGetAvailablePackageManagers.mockRestore(); + mockGetWorkspaceDetails.mockRestore(); } ); test.each(GLOBAL_INSTALL_COMMANDS)( - "returns correct upgrade command for global install of turbo@%s using %s@%s (fixture: %s)", - ( - turboVersion, + "returns correct upgrade command for global install of turbo@$version using $packageManager@$packageManagerVersion (fixture: $fixture)", + async ({ + version, packageManager, packageManagerVersion, fixture, - expectedUpgradeCommand - ) => { + expected, + }) => { const { root } = useFixture({ fixture, }); @@ -417,160 +511,143 @@ describe("get-turbo-upgrade-command", () => { if (command === "turbo bin") { return `/global/${packageManager}/bin/turbo`; } - if (command.includes(packageManager)) { - return `/global/${packageManager}/bin`; - } + return undefined; + }); + const mockGetPackageManagersBinPaths = jest + .spyOn(turboUtils, "getPackageManagersBinPaths") + .mockResolvedValue({ + pnpm: `/global/pnpm/bin`, + npm: `/global/npm/bin`, + yarn: `/global/yarn/bin`, }); - const mockedGetPackageManagerVersion = jest - .spyOn(getPackageManagerVersion, "default") - .mockReturnValue(packageManagerVersion); - const mockedGetPackageManager = jest - .spyOn(getPackageManager, "default") - .mockReturnValue(packageManager as getPackageManager.PackageManager); + + const mockGetAvailablePackageManagers = jest + .spyOn(turboUtils, "getAvailablePackageManagers") + .mockResolvedValue({ + pnpm: packageManager === "pnpm" ? packageManagerVersion : undefined, + npm: packageManager === "npm" ? packageManagerVersion : undefined, + yarn: packageManager === "yarn" ? packageManagerVersion : undefined, + }); + + const project = getWorkspaceDetailsMockReturnValue({ + root, + packageManager, + }); + const mockGetWorkspaceDetails = jest + .spyOn(turboWorkspaces, "getWorkspaceDetails") + .mockResolvedValue(project); // get the command - const upgradeCommand = getTurboUpgradeCommand({ - directory: root, - to: turboVersion === "latest" ? undefined : turboVersion, + const upgradeCommand = await getTurboUpgradeCommand({ + project, + to: version === "latest" ? undefined : version, }); - expect(upgradeCommand).toEqual(expectedUpgradeCommand); + expect(upgradeCommand).toEqual(expected); mockedExec.mockRestore(); - mockedGetPackageManager.mockRestore(); - mockedGetPackageManagerVersion.mockRestore(); + mockGetPackageManagersBinPaths.mockRestore(); + mockGetAvailablePackageManagers.mockRestore(); + mockGetWorkspaceDetails.mockRestore(); } ); - test("fails gracefully if no package.json exists", () => { - const { root } = useFixture({ - fixture: "no-package", - }); - - const mockedExec = jest - .spyOn(utils, "exec") - .mockImplementation((command: string) => { - // fail the check for the turbo, and package manager bins to force local - if (command.includes("bin")) { - return undefined; - } + describe("errors", () => { + test("fails gracefully if no package.json exists", async () => { + const { root } = useFixture({ + fixture: "no-package", }); - const mockedGetPackageManagerVersion = jest - .spyOn(getPackageManagerVersion, "default") - .mockReturnValue("8.0.0"); - const mockedGetPackageManager = jest - .spyOn(getPackageManager, "default") - .mockReturnValue("pnpm" as getPackageManager.PackageManager); - - // get the command - const upgradeCommand = getTurboUpgradeCommand({ - directory: root, - }); - - expect(upgradeCommand).toEqual(undefined); - - mockedExec.mockRestore(); - mockedGetPackageManager.mockRestore(); - mockedGetPackageManagerVersion.mockRestore(); - }); + const mockedExec = jest + .spyOn(utils, "exec") + .mockImplementation((command: string) => { + // fail the check for the turbo to force local + if (command.includes("bin")) { + return undefined; + } + }); - test("fails gracefully if turbo cannot be found in package.json", () => { - const { root } = useFixture({ - fixture: "no-turbo", - }); + const mockGetAvailablePackageManagers = jest + .spyOn(turboUtils, "getAvailablePackageManagers") + .mockResolvedValue({ + pnpm: "8.0.0", + npm: undefined, + yarn: undefined, + }); - const mockedExec = jest - .spyOn(utils, "exec") - .mockImplementation((command: string) => { - // fail the check for the turbo, and package manager bins to force local - if (command.includes("bin")) { - return undefined; - } + const project = getWorkspaceDetailsMockReturnValue({ + root, + packageManager: "pnpm", }); + const mockGetWorkspaceDetails = jest + .spyOn(turboWorkspaces, "getWorkspaceDetails") + .mockResolvedValue(project); - const mockedGetPackageManagerVersion = jest - .spyOn(getPackageManagerVersion, "default") - .mockReturnValue("8.0.0"); - const mockedGetPackageManager = jest - .spyOn(getPackageManager, "default") - .mockReturnValue("pnpm" as getPackageManager.PackageManager); - - // get the command - const upgradeCommand = getTurboUpgradeCommand({ - directory: root, - }); - - expect(upgradeCommand).toEqual(undefined); + // get the command + const upgradeCommand = await getTurboUpgradeCommand({ + project, + }); - mockedExec.mockRestore(); - mockedGetPackageManager.mockRestore(); - mockedGetPackageManagerVersion.mockRestore(); - }); + expect(upgradeCommand).toEqual(undefined); - test("fails gracefully if package.json has no deps or devDeps", () => { - const { root } = useFixture({ - fixture: "no-deps", + mockedExec.mockRestore(); + mockGetAvailablePackageManagers.mockRestore(); + mockGetWorkspaceDetails.mockRestore(); }); - const mockedExec = jest - .spyOn(utils, "exec") - .mockImplementation((command: string) => { - // fail the check for the turbo, and package manager bins to force local - if (command.includes("bin")) { - return undefined; - } + test.each([ + { + fixture: "no-package", + name: "fails gracefully if no package.json exists", + }, + { + fixture: "no-turbo", + name: "fails gracefully if turbo cannot be found in package.json", + }, + { + fixture: "no-deps", + name: "fails gracefully if package.json has no deps or devDeps", + }, + ])("$name", async ({ fixture }) => { + const { root } = useFixture({ + fixture, }); - const mockedGetPackageManagerVersion = jest - .spyOn(getPackageManagerVersion, "default") - .mockReturnValue("8.0.0"); - const mockedGetPackageManager = jest - .spyOn(getPackageManager, "default") - .mockReturnValue("pnpm" as getPackageManager.PackageManager); - - // get the command - const upgradeCommand = getTurboUpgradeCommand({ - directory: root, - }); - - expect(upgradeCommand).toEqual(undefined); + const mockedExec = jest + .spyOn(utils, "exec") + .mockImplementation((command: string) => { + // fail the check for the turbo to force local + if (command.includes("bin")) { + return undefined; + } + }); - mockedExec.mockRestore(); - mockedGetPackageManager.mockRestore(); - mockedGetPackageManagerVersion.mockRestore(); - }); + const mockGetAvailablePackageManagers = jest + .spyOn(turboUtils, "getAvailablePackageManagers") + .mockResolvedValue({ + pnpm: "8.0.0", + npm: undefined, + yarn: undefined, + }); - test("fails gracefully if can't find packageManager", () => { - const { root } = useFixture({ - fixture: "no-deps", - }); + const project = getWorkspaceDetailsMockReturnValue({ + root, + packageManager: "pnpm", + }); + const mockGetWorkspaceDetails = jest + .spyOn(turboWorkspaces, "getWorkspaceDetails") + .mockResolvedValue(project); - const mockedExec = jest - .spyOn(utils, "exec") - .mockImplementation((command: string) => { - // fail the check for the turbo, and package manager bins to force local - if (command.includes("bin")) { - return undefined; - } + // get the command + const upgradeCommand = await getTurboUpgradeCommand({ + project, }); - const mockedGetPackageManagerVersion = jest - .spyOn(getPackageManagerVersion, "default") - .mockReturnValue("8.0.0"); - const mockedGetPackageManager = jest - .spyOn(getPackageManager, "default") - .mockReturnValue("pnpm" as getPackageManager.PackageManager); + expect(upgradeCommand).toEqual(undefined); - // get the command - const upgradeCommand = getTurboUpgradeCommand({ - directory: root, + mockedExec.mockRestore(); + mockGetAvailablePackageManagers.mockRestore(); + mockGetWorkspaceDetails.mockRestore(); }); - - expect(upgradeCommand).toEqual(undefined); - - mockedExec.mockRestore(); - mockedGetPackageManager.mockRestore(); - mockedGetPackageManagerVersion.mockRestore(); }); }); diff --git a/packages/turbo-codemod/__tests__/migrate.test.ts b/packages/turbo-codemod/__tests__/migrate.test.ts index 652ea413bf80b..a6615504be55f 100644 --- a/packages/turbo-codemod/__tests__/migrate.test.ts +++ b/packages/turbo-codemod/__tests__/migrate.test.ts @@ -6,8 +6,14 @@ import * as checkGitStatus from "../src/utils/checkGitStatus"; import * as getCurrentVersion from "../src/commands/migrate/steps/getCurrentVersion"; import * as getLatestVersion from "../src/commands/migrate/steps/getLatestVersion"; import * as getTurboUpgradeCommand from "../src/commands/migrate/steps/getTurboUpgradeCommand"; -import * as workspaceImplementation from "../src/utils/getPackageManager"; -import * as getPackageManagerVersion from "../src/utils/getPackageManagerVersion"; +import * as turboWorkspaces from "@turbo/workspaces"; +import * as turboUtils from "@turbo/utils"; +import { getWorkspaceDetailsMockReturnValue } from "./test-utils"; + +jest.mock("@turbo/workspaces", () => ({ + __esModule: true, + ...jest.requireActual("@turbo/workspaces"), +})); describe("migrate", () => { const mockExit = spyExit(); @@ -36,15 +42,24 @@ describe("migrate", () => { .mockResolvedValue("1.7.0"); const mockedGetTurboUpgradeCommand = jest .spyOn(getTurboUpgradeCommand, "default") - .mockReturnValue("pnpm install -g turbo@latest"); - const mockedGetPackageManagerVersion = jest - .spyOn(getPackageManagerVersion, "default") - .mockReturnValue(packageManagerVersion); - const mockedGetWorkspaceImplementation = jest - .spyOn(workspaceImplementation, "default") - .mockReturnValue(packageManager); - - await migrate(root as MigrateCommandArgument, { + .mockResolvedValue("pnpm install -g turbo@latest"); + const mockGetAvailablePackageManagers = jest + .spyOn(turboUtils, "getAvailablePackageManagers") + .mockResolvedValue({ + pnpm: packageManagerVersion, + npm: undefined, + yarn: undefined, + }); + const mockGetWorkspaceDetails = jest + .spyOn(turboWorkspaces, "getWorkspaceDetails") + .mockResolvedValue( + getWorkspaceDetailsMockReturnValue({ + root, + packageManager, + }) + ); + + await migrate(root, { force: false, dry: false, print: false, @@ -81,16 +96,16 @@ describe("migrate", () => { expect(mockedGetCurrentVersion).toHaveBeenCalled(); expect(mockedGetLatestVersion).toHaveBeenCalled(); expect(mockedGetTurboUpgradeCommand).toHaveBeenCalled(); - expect(mockedGetPackageManagerVersion).toHaveBeenCalled(); - expect(mockedGetWorkspaceImplementation).toHaveBeenCalled(); + expect(mockGetAvailablePackageManagers).toHaveBeenCalled(); + expect(mockGetWorkspaceDetails).toHaveBeenCalled(); // restore mocks mockedCheckGitStatus.mockRestore(); mockedGetCurrentVersion.mockRestore(); mockedGetLatestVersion.mockRestore(); mockedGetTurboUpgradeCommand.mockRestore(); - mockedGetPackageManagerVersion.mockRestore(); - mockedGetWorkspaceImplementation.mockRestore(); + mockGetAvailablePackageManagers.mockRestore(); + mockGetWorkspaceDetails.mockRestore(); }); it("migrates from 1.0.0 to 1.2.0 (dry run)", async () => { @@ -113,18 +128,27 @@ describe("migrate", () => { .mockResolvedValue("1.2.0"); const mockedGetTurboUpgradeCommand = jest .spyOn(getTurboUpgradeCommand, "default") - .mockReturnValue("pnpm install -g turbo@latest"); - const mockedGetPackageManagerVersion = jest - .spyOn(getPackageManagerVersion, "default") - .mockReturnValue(packageManagerVersion); - const mockedGetWorkspaceImplementation = jest - .spyOn(workspaceImplementation, "default") - .mockReturnValue(packageManager); + .mockResolvedValue("pnpm install -g turbo@latest"); + const mockGetAvailablePackageManagers = jest + .spyOn(turboUtils, "getAvailablePackageManagers") + .mockResolvedValue({ + pnpm: packageManagerVersion, + npm: undefined, + yarn: undefined, + }); + const mockGetWorkspaceDetails = jest + .spyOn(turboWorkspaces, "getWorkspaceDetails") + .mockResolvedValue( + getWorkspaceDetailsMockReturnValue({ + root, + packageManager, + }) + ); const packageJson = readJson("package.json"); const turboJson = readJson("turbo.json"); - await migrate(root as MigrateCommandArgument, { + await migrate(root, { force: false, dry: true, print: false, @@ -140,16 +164,16 @@ describe("migrate", () => { expect(mockedGetCurrentVersion).toHaveBeenCalled(); expect(mockedGetLatestVersion).toHaveBeenCalled(); expect(mockedGetTurboUpgradeCommand).toHaveBeenCalled(); - expect(mockedGetPackageManagerVersion).toHaveBeenCalled(); - expect(mockedGetWorkspaceImplementation).toHaveBeenCalled(); + expect(mockGetAvailablePackageManagers).toHaveBeenCalled(); + expect(mockGetWorkspaceDetails).toHaveBeenCalled(); // restore mocks mockedCheckGitStatus.mockRestore(); mockedGetCurrentVersion.mockRestore(); mockedGetLatestVersion.mockRestore(); mockedGetTurboUpgradeCommand.mockRestore(); - mockedGetPackageManagerVersion.mockRestore(); - mockedGetWorkspaceImplementation.mockRestore(); + mockGetAvailablePackageManagers.mockRestore(); + mockGetWorkspaceDetails.mockRestore(); }); it("next version can be passed as an option", async () => { @@ -172,15 +196,24 @@ describe("migrate", () => { .mockResolvedValue("1.7.0"); const mockedGetTurboUpgradeCommand = jest .spyOn(getTurboUpgradeCommand, "default") - .mockReturnValue("pnpm install -g turbo@latest"); - const mockedGetPackageManagerVersion = jest - .spyOn(getPackageManagerVersion, "default") - .mockReturnValue(packageManagerVersion); - const mockedGetWorkspaceImplementation = jest - .spyOn(workspaceImplementation, "default") - .mockReturnValue(packageManager); - - await migrate(root as MigrateCommandArgument, { + .mockResolvedValue("pnpm install -g turbo@latest"); + const mockGetAvailablePackageManagers = jest + .spyOn(turboUtils, "getAvailablePackageManagers") + .mockResolvedValue({ + pnpm: packageManagerVersion, + npm: undefined, + yarn: undefined, + }); + const mockGetWorkspaceDetails = jest + .spyOn(turboWorkspaces, "getWorkspaceDetails") + .mockResolvedValue( + getWorkspaceDetailsMockReturnValue({ + root, + packageManager, + }) + ); + + await migrate(root, { force: false, dry: false, print: false, @@ -218,16 +251,16 @@ describe("migrate", () => { expect(mockedGetCurrentVersion).toHaveBeenCalled(); expect(mockedGetLatestVersion).toHaveBeenCalled(); expect(mockedGetTurboUpgradeCommand).toHaveBeenCalled(); - expect(mockedGetPackageManagerVersion).toHaveBeenCalled(); - expect(mockedGetWorkspaceImplementation).toHaveBeenCalled(); + expect(mockGetAvailablePackageManagers).toHaveBeenCalled(); + expect(mockGetWorkspaceDetails).toHaveBeenCalled(); // restore mocks mockedCheckGitStatus.mockRestore(); mockedGetCurrentVersion.mockRestore(); mockedGetLatestVersion.mockRestore(); mockedGetTurboUpgradeCommand.mockRestore(); - mockedGetPackageManagerVersion.mockRestore(); - mockedGetWorkspaceImplementation.mockRestore(); + mockGetAvailablePackageManagers.mockRestore(); + mockGetWorkspaceDetails.mockRestore(); }); it("current version can be passed as an option", async () => { @@ -247,16 +280,24 @@ describe("migrate", () => { .mockResolvedValue("1.7.0"); const mockedGetTurboUpgradeCommand = jest .spyOn(getTurboUpgradeCommand, "default") - .mockReturnValue("pnpm install -g turbo@latest"); - const mockedGetPackageManagerVersion = jest - .spyOn(getPackageManagerVersion, "default") - .mockReturnValue(packageManagerVersion); - - const mockedGetWorkspaceImplementation = jest - .spyOn(workspaceImplementation, "default") - .mockReturnValue(packageManager); - - await migrate(root as MigrateCommandArgument, { + .mockResolvedValue("pnpm install -g turbo@latest"); + const mockGetAvailablePackageManagers = jest + .spyOn(turboUtils, "getAvailablePackageManagers") + .mockResolvedValue({ + pnpm: packageManagerVersion, + npm: undefined, + yarn: undefined, + }); + const mockGetWorkspaceDetails = jest + .spyOn(turboWorkspaces, "getWorkspaceDetails") + .mockResolvedValue( + getWorkspaceDetailsMockReturnValue({ + root, + packageManager, + }) + ); + + await migrate(root, { force: false, dry: false, print: false, @@ -293,15 +334,15 @@ describe("migrate", () => { expect(mockedCheckGitStatus).toHaveBeenCalled(); expect(mockedGetLatestVersion).toHaveBeenCalled(); expect(mockedGetTurboUpgradeCommand).toHaveBeenCalled(); - expect(mockedGetPackageManagerVersion).toHaveBeenCalled(); - expect(mockedGetWorkspaceImplementation).toHaveBeenCalled(); + expect(mockGetAvailablePackageManagers).toHaveBeenCalled(); + expect(mockGetWorkspaceDetails).toHaveBeenCalled(); // restore mocks mockedCheckGitStatus.mockRestore(); mockedGetLatestVersion.mockRestore(); mockedGetTurboUpgradeCommand.mockRestore(); - mockedGetPackageManagerVersion.mockRestore(); - mockedGetWorkspaceImplementation.mockRestore(); + mockGetAvailablePackageManagers.mockRestore(); + mockGetWorkspaceDetails.mockRestore(); }); it("exits if the current version is the same as the new version", async () => { @@ -309,6 +350,9 @@ describe("migrate", () => { fixture: "old-turbo", }); + const packageManager = "pnpm"; + const packageManagerVersion = "1.2.3"; + // setup mocks const mockedCheckGitStatus = jest .spyOn(checkGitStatus, "default") @@ -319,8 +363,16 @@ describe("migrate", () => { const mockedGetLatestVersion = jest .spyOn(getLatestVersion, "default") .mockResolvedValue("1.7.0"); - - await migrate(root as MigrateCommandArgument, { + const mockGetWorkspaceDetails = jest + .spyOn(turboWorkspaces, "getWorkspaceDetails") + .mockResolvedValue( + getWorkspaceDetailsMockReturnValue({ + root, + packageManager, + }) + ); + + await migrate(root, { force: false, dry: false, print: false, @@ -333,11 +385,12 @@ describe("migrate", () => { expect(mockedCheckGitStatus).toHaveBeenCalled(); expect(mockedGetCurrentVersion).toHaveBeenCalled(); expect(mockedGetLatestVersion).toHaveBeenCalled(); + expect(mockGetWorkspaceDetails).toHaveBeenCalled(); // restore mocks mockedCheckGitStatus.mockRestore(); mockedGetCurrentVersion.mockRestore(); - mockedGetLatestVersion.mockRestore(); + mockGetWorkspaceDetails.mockRestore(); }); it("continues when migration doesn't require codemods", async () => { @@ -345,6 +398,8 @@ describe("migrate", () => { fixture: "old-turbo", }); + const packageManager = "npm"; + // setup mocks const mockedCheckGitStatus = jest .spyOn(checkGitStatus, "default") @@ -357,12 +412,20 @@ describe("migrate", () => { .mockResolvedValue("1.3.1"); const mockedGetTurboUpgradeCommand = jest .spyOn(getTurboUpgradeCommand, "default") - .mockReturnValue("npm install turbo@1.3.1"); + .mockResolvedValue("npm install turbo@1.3.1"); const mockExecSync = jest .spyOn(childProcess, "execSync") .mockReturnValue("installed"); - - await migrate(root as MigrateCommandArgument, { + const mockGetWorkspaceDetails = jest + .spyOn(turboWorkspaces, "getWorkspaceDetails") + .mockResolvedValue( + getWorkspaceDetailsMockReturnValue({ + root, + packageManager, + }) + ); + + await migrate(root, { force: false, dry: false, print: false, @@ -374,8 +437,18 @@ describe("migrate", () => { expect(mockedGetCurrentVersion).toHaveBeenCalled(); expect(mockedGetLatestVersion).toHaveBeenCalled(); expect(mockedGetTurboUpgradeCommand).toHaveBeenCalled(); - expect(mockExecSync).toHaveBeenCalledWith("npm install turbo@1.3.1", { + expect(mockGetWorkspaceDetails).toHaveBeenCalled(); + expect(mockExecSync).toHaveBeenNthCalledWith(1, "turbo bin", { cwd: root, + stdio: "ignore", + }); + expect(mockExecSync).toHaveBeenNthCalledWith(2, "turbo daemon stop", { + cwd: root, + stdio: "ignore", + }); + expect(mockExecSync).toHaveBeenNthCalledWith(3, "npm install turbo@1.3.1", { + cwd: root, + stdio: "pipe", }); // restore mocks @@ -383,6 +456,7 @@ describe("migrate", () => { mockedGetCurrentVersion.mockRestore(); mockedGetLatestVersion.mockRestore(); mockedGetTurboUpgradeCommand.mockRestore(); + mockGetWorkspaceDetails.mockRestore(); mockExecSync.mockRestore(); }); @@ -406,18 +480,27 @@ describe("migrate", () => { .mockResolvedValue("1.7.0"); const mockedGetTurboUpgradeCommand = jest .spyOn(getTurboUpgradeCommand, "default") - .mockReturnValue("pnpm install -g turbo@1.7.0"); - const mockedGetPackageManagerVersion = jest - .spyOn(getPackageManagerVersion, "default") - .mockReturnValue(packageManagerVersion); - const mockedGetWorkspaceImplementation = jest - .spyOn(workspaceImplementation, "default") - .mockReturnValue(packageManager); + .mockResolvedValue("pnpm install -g turbo@1.7.0"); + const mockGetAvailablePackageManagers = jest + .spyOn(turboUtils, "getAvailablePackageManagers") + .mockResolvedValue({ + pnpm: packageManagerVersion, + npm: undefined, + yarn: undefined, + }); + const mockGetWorkspaceDetails = jest + .spyOn(turboWorkspaces, "getWorkspaceDetails") + .mockResolvedValue( + getWorkspaceDetailsMockReturnValue({ + root, + packageManager, + }) + ); const mockExecSync = jest .spyOn(childProcess, "execSync") .mockReturnValue("installed"); - await migrate(root as MigrateCommandArgument, { + await migrate(root, { force: false, dry: false, print: false, @@ -454,20 +537,33 @@ describe("migrate", () => { expect(mockedGetCurrentVersion).toHaveBeenCalled(); expect(mockedGetLatestVersion).toHaveBeenCalled(); expect(mockedGetTurboUpgradeCommand).toHaveBeenCalled(); - expect(mockedGetPackageManagerVersion).toHaveBeenCalled(); - expect(mockedGetWorkspaceImplementation).toHaveBeenCalled(); + expect(mockGetAvailablePackageManagers).toHaveBeenCalled(); + expect(mockGetWorkspaceDetails).toHaveBeenCalled(); expect(mockExecSync).toHaveBeenCalled(); - expect(mockExecSync).toHaveBeenCalledWith("pnpm install -g turbo@1.7.0", { + expect(mockExecSync).toHaveBeenNthCalledWith(1, "turbo bin", { + cwd: root, + stdio: "ignore", + }); + expect(mockExecSync).toHaveBeenNthCalledWith(2, "turbo daemon stop", { cwd: root, + stdio: "ignore", }); + expect(mockExecSync).toHaveBeenNthCalledWith( + 3, + "pnpm install -g turbo@1.7.0", + { + cwd: root, + stdio: "pipe", + } + ); // restore mocks mockedCheckGitStatus.mockRestore(); mockedGetCurrentVersion.mockRestore(); mockedGetLatestVersion.mockRestore(); mockedGetTurboUpgradeCommand.mockRestore(); - mockedGetPackageManagerVersion.mockRestore(); - mockedGetWorkspaceImplementation.mockRestore(); + mockGetAvailablePackageManagers.mockRestore(); + mockGetWorkspaceDetails.mockRestore(); mockExecSync.mockRestore(); }); @@ -491,18 +587,27 @@ describe("migrate", () => { .mockResolvedValue("1.7.0"); const mockedGetTurboUpgradeCommand = jest .spyOn(getTurboUpgradeCommand, "default") - .mockReturnValue(undefined); - const mockedGetPackageManagerVersion = jest - .spyOn(getPackageManagerVersion, "default") - .mockReturnValue(packageManagerVersion); - const mockedGetWorkspaceImplementation = jest - .spyOn(workspaceImplementation, "default") - .mockReturnValue(packageManager); + .mockResolvedValue(undefined); + const mockGetAvailablePackageManagers = jest + .spyOn(turboUtils, "getAvailablePackageManagers") + .mockResolvedValue({ + pnpm: packageManagerVersion, + npm: undefined, + yarn: undefined, + }); + const mockGetWorkspaceDetails = jest + .spyOn(turboWorkspaces, "getWorkspaceDetails") + .mockResolvedValue( + getWorkspaceDetailsMockReturnValue({ + root, + packageManager, + }) + ); const mockExecSync = jest .spyOn(childProcess, "execSync") .mockReturnValue("installed"); - await migrate(root as MigrateCommandArgument, { + await migrate(root, { force: false, dry: false, print: false, @@ -541,17 +646,25 @@ describe("migrate", () => { expect(mockedGetCurrentVersion).toHaveBeenCalled(); expect(mockedGetLatestVersion).toHaveBeenCalled(); expect(mockedGetTurboUpgradeCommand).toHaveBeenCalled(); - expect(mockedGetPackageManagerVersion).toHaveBeenCalled(); - expect(mockedGetWorkspaceImplementation).toHaveBeenCalled(); - expect(mockExecSync).not.toHaveBeenCalled(); + expect(mockGetAvailablePackageManagers).toHaveBeenCalled(); + expect(mockGetWorkspaceDetails).toHaveBeenCalled(); + expect(mockExecSync).toHaveBeenCalledTimes(2); + expect(mockExecSync).toHaveBeenNthCalledWith(1, "turbo bin", { + cwd: root, + stdio: "ignore", + }); + expect(mockExecSync).toHaveBeenNthCalledWith(2, "turbo daemon stop", { + cwd: root, + stdio: "ignore", + }); // restore mocks mockedCheckGitStatus.mockRestore(); mockedGetCurrentVersion.mockRestore(); mockedGetLatestVersion.mockRestore(); mockedGetTurboUpgradeCommand.mockRestore(); - mockedGetPackageManagerVersion.mockRestore(); - mockedGetWorkspaceImplementation.mockRestore(); + mockGetAvailablePackageManagers.mockRestore(); + mockGetWorkspaceDetails.mockRestore(); mockExecSync.mockRestore(); }); @@ -560,6 +673,8 @@ describe("migrate", () => { fixture: "old-turbo", }); + const packageManager = "pnpm"; + // setup mocks const mockedCheckGitStatus = jest .spyOn(checkGitStatus, "default") @@ -567,8 +682,16 @@ describe("migrate", () => { const mockedGetCurrentVersion = jest .spyOn(getCurrentVersion, "default") .mockReturnValue(undefined); - - await migrate(root as MigrateCommandArgument, { + const mockGetWorkspaceDetails = jest + .spyOn(turboWorkspaces, "getWorkspaceDetails") + .mockResolvedValue( + getWorkspaceDetailsMockReturnValue({ + root, + packageManager, + }) + ); + + await migrate(root, { force: false, dry: false, print: false, @@ -602,7 +725,7 @@ describe("migrate", () => { .spyOn(getLatestVersion, "default") .mockResolvedValue(undefined); - await migrate(root as MigrateCommandArgument, { + await migrate(root, { force: false, dry: false, print: false, @@ -638,7 +761,7 @@ describe("migrate", () => { .spyOn(getLatestVersion, "default") .mockRejectedValue(new Error("failed to fetch version")); - await migrate(root as MigrateCommandArgument, { + await migrate(root, { force: false, dry: false, print: false, @@ -676,14 +799,23 @@ describe("migrate", () => { const mockedGetLatestVersion = jest .spyOn(getLatestVersion, "default") .mockResolvedValue("1.7.0"); - const mockedGetPackageManagerVersion = jest - .spyOn(getPackageManagerVersion, "default") - .mockReturnValue(packageManagerVersion); - const mockedGetWorkspaceImplementation = jest - .spyOn(workspaceImplementation, "default") - .mockReturnValue(packageManager); - - await migrate(root as MigrateCommandArgument, { + const mockGetAvailablePackageManagers = jest + .spyOn(turboUtils, "getAvailablePackageManagers") + .mockResolvedValue({ + pnpm: packageManagerVersion, + npm: undefined, + yarn: undefined, + }); + const mockGetWorkspaceDetails = jest + .spyOn(turboWorkspaces, "getWorkspaceDetails") + .mockResolvedValue( + getWorkspaceDetailsMockReturnValue({ + root, + packageManager, + }) + ); + + await migrate(root, { force: false, dry: true, print: false, @@ -696,15 +828,15 @@ describe("migrate", () => { expect(mockedCheckGitStatus).not.toHaveBeenCalled(); expect(mockedGetCurrentVersion).toHaveBeenCalled(); expect(mockedGetLatestVersion).toHaveBeenCalled(); - expect(mockedGetPackageManagerVersion).toHaveBeenCalled(); - expect(mockedGetWorkspaceImplementation).toHaveBeenCalled(); + expect(mockGetAvailablePackageManagers).toHaveBeenCalled(); + expect(mockGetWorkspaceDetails).toHaveBeenCalled(); // restore mocks mockedCheckGitStatus.mockRestore(); mockedGetCurrentVersion.mockRestore(); mockedGetLatestVersion.mockRestore(); - mockedGetPackageManagerVersion.mockRestore(); - mockedGetWorkspaceImplementation.mockRestore(); + mockGetAvailablePackageManagers.mockRestore(); + mockGetWorkspaceDetails.mockRestore(); }); it("exits if invalid directory is passed", async () => { @@ -717,7 +849,7 @@ describe("migrate", () => { .spyOn(checkGitStatus, "default") .mockReturnValue(undefined); - await migrate("~/path/that/does/not/exist" as MigrateCommandArgument, { + await migrate("~/path/that/does/not/exist", { force: false, dry: false, print: false, @@ -743,7 +875,7 @@ describe("migrate", () => { .spyOn(checkGitStatus, "default") .mockReturnValue(undefined); - await migrate(root as MigrateCommandArgument, { + await migrate(root, { force: false, dry: false, print: false, diff --git a/packages/turbo-codemod/__tests__/test-utils.ts b/packages/turbo-codemod/__tests__/test-utils.ts new file mode 100644 index 0000000000000..eaa7660e448cf --- /dev/null +++ b/packages/turbo-codemod/__tests__/test-utils.ts @@ -0,0 +1,44 @@ +import path from "path"; +import { PackageManager, Project } from "@turbo/workspaces"; + +export function getWorkspaceDetailsMockReturnValue({ + root, + packageManager = "npm", + singlePackage = false, +}: { + root: string; + packageManager: PackageManager; + singlePackage?: boolean; +}): Project { + return { + name: "mock-project", + packageManager, + paths: { + root, + packageJson: path.join(root, "package.json"), + lockfile: path.join(root, "yarn.lock"), + nodeModules: path.join(root, "node_modules"), + }, + workspaceData: { + globs: singlePackage ? [] : ["packages/*"], + workspaces: singlePackage + ? [] + : [ + { + name: "packages/mock-package", + paths: { + root: path.join(root, "packages/mock-package"), + packageJson: path.join( + root, + "packages/mock-package/package.json" + ), + nodeModules: path.join( + root, + "packages/mock-package/node_modules" + ), + }, + }, + ], + }, + }; +} diff --git a/packages/turbo-codemod/__tests__/transform.test.ts b/packages/turbo-codemod/__tests__/transform.test.ts index abd015d3be7bc..5d378a864f570 100644 --- a/packages/turbo-codemod/__tests__/transform.test.ts +++ b/packages/turbo-codemod/__tests__/transform.test.ts @@ -2,8 +2,15 @@ import transform from "../src/commands/transform"; import { MigrateCommandArgument } from "../src/commands"; import { setupTestFixtures, spyExit } from "@turbo/test-utils"; import * as checkGitStatus from "../src/utils/checkGitStatus"; -import * as getPackageManager from "../src/utils/getPackageManager"; -import * as getPackageManagerVersion from "../src/utils/getPackageManagerVersion"; + +import * as turboWorkspaces from "@turbo/workspaces"; +import * as turboUtils from "@turbo/utils"; +import { getWorkspaceDetailsMockReturnValue } from "./test-utils"; + +jest.mock("@turbo/workspaces", () => ({ + __esModule: true, + ...jest.requireActual("@turbo/workspaces"), +})); describe("transform", () => { const mockExit = spyExit(); @@ -24,23 +31,29 @@ describe("transform", () => { const mockedCheckGitStatus = jest .spyOn(checkGitStatus, "default") .mockReturnValue(undefined); - const mockedGetPackageManagerVersion = jest - .spyOn(getPackageManagerVersion, "default") - .mockReturnValue(packageManagerVersion); - const mockedGetPackageManager = jest - .spyOn(getPackageManager, "default") - .mockReturnValue(packageManager); - - await transform( - "add-package-manager" as MigrateCommandArgument, - root as MigrateCommandArgument, - { - list: false, - force: false, - dry: false, - print: false, - } - ); + const mockGetAvailablePackageManagers = jest + .spyOn(turboUtils, "getAvailablePackageManagers") + .mockResolvedValue({ + pnpm: packageManagerVersion, + npm: undefined, + yarn: undefined, + }); + + const mockGetWorkspaceDetails = jest + .spyOn(turboWorkspaces, "getWorkspaceDetails") + .mockResolvedValue( + getWorkspaceDetailsMockReturnValue({ + root, + packageManager, + }) + ); + + await transform("add-package-manager", root as MigrateCommandArgument, { + list: false, + force: false, + dry: false, + print: false, + }); expect(readJson("package.json")).toStrictEqual({ dependencies: {}, @@ -54,13 +67,13 @@ describe("transform", () => { // verify mocks were called expect(mockedCheckGitStatus).toHaveBeenCalled(); - expect(mockedGetPackageManagerVersion).toHaveBeenCalled(); - expect(mockedGetPackageManager).toHaveBeenCalled(); + expect(mockGetAvailablePackageManagers).toHaveBeenCalled(); + expect(mockGetWorkspaceDetails).toHaveBeenCalled(); // restore mocks mockedCheckGitStatus.mockRestore(); - mockedGetPackageManagerVersion.mockRestore(); - mockedGetPackageManager.mockRestore(); + mockGetAvailablePackageManagers.mockRestore(); + mockGetWorkspaceDetails.mockRestore(); }); it("runs the selected transform - dry & print", async () => { @@ -75,23 +88,29 @@ describe("transform", () => { const mockedCheckGitStatus = jest .spyOn(checkGitStatus, "default") .mockReturnValue(undefined); - const mockedGetPackageManagerVersion = jest - .spyOn(getPackageManagerVersion, "default") - .mockReturnValue(packageManagerVersion); - const mockedGetPackageManager = jest - .spyOn(getPackageManager, "default") - .mockReturnValue(packageManager); - - await transform( - "add-package-manager" as MigrateCommandArgument, - root as MigrateCommandArgument, - { - list: false, - force: false, - dry: true, - print: true, - } - ); + const mockGetAvailablePackageManagers = jest + .spyOn(turboUtils, "getAvailablePackageManagers") + .mockResolvedValue({ + pnpm: packageManagerVersion, + npm: undefined, + yarn: undefined, + }); + + const mockGetWorkspaceDetails = jest + .spyOn(turboWorkspaces, "getWorkspaceDetails") + .mockResolvedValue( + getWorkspaceDetailsMockReturnValue({ + root, + packageManager, + }) + ); + + await transform("add-package-manager", root, { + list: false, + force: false, + dry: true, + print: true, + }); expect(readJson("package.json")).toStrictEqual({ dependencies: {}, @@ -104,13 +123,13 @@ describe("transform", () => { // verify mocks were called expect(mockedCheckGitStatus).not.toHaveBeenCalled(); - expect(mockedGetPackageManagerVersion).toHaveBeenCalled(); - expect(mockedGetPackageManager).toHaveBeenCalled(); + expect(mockGetAvailablePackageManagers).toHaveBeenCalled(); + expect(mockGetWorkspaceDetails).toHaveBeenCalled(); // restore mocks mockedCheckGitStatus.mockRestore(); - mockedGetPackageManagerVersion.mockRestore(); - mockedGetPackageManager.mockRestore(); + mockGetAvailablePackageManagers.mockRestore(); + mockGetWorkspaceDetails.mockRestore(); }); it("lists transforms", async () => { @@ -118,16 +137,12 @@ describe("transform", () => { fixture: "basic", }); - await transform( - "add-package-manager" as MigrateCommandArgument, - root as MigrateCommandArgument, - { - list: true, - force: false, - dry: false, - print: false, - } - ); + await transform("add-package-manager", root, { + list: true, + force: false, + dry: false, + print: false, + }); expect(mockExit.exit).toHaveBeenCalledWith(0); }); @@ -137,16 +152,12 @@ describe("transform", () => { fixture: "basic", }); - await transform( - "not-a-real-option" as MigrateCommandArgument, - root as MigrateCommandArgument, - { - list: false, - force: false, - dry: false, - print: false, - } - ); + await transform("not-a-real-option", root, { + list: false, + force: false, + dry: false, + print: false, + }); expect(mockExit.exit).toHaveBeenCalledWith(1); }); @@ -156,16 +167,12 @@ describe("transform", () => { fixture: "basic", }); - await transform( - "add-package-manager" as MigrateCommandArgument, - "~/path/that/does/not/exist" as MigrateCommandArgument, - { - list: false, - force: false, - dry: false, - print: false, - } - ); + await transform("add-package-manager", "~/path/that/does/not/exist", { + list: false, + force: false, + dry: false, + print: false, + }); expect(mockExit.exit).toHaveBeenCalledWith(1); }); diff --git a/packages/turbo-codemod/jest.config.js b/packages/turbo-codemod/jest.config.js index b7df7bc86b988..658161974804b 100644 --- a/packages/turbo-codemod/jest.config.js +++ b/packages/turbo-codemod/jest.config.js @@ -2,17 +2,31 @@ module.exports = { preset: "ts-jest/presets/js-with-ts", testEnvironment: "node", - transformIgnorePatterns: ["/node_modules/(?!(ansi-regex)/)"], - modulePathIgnorePatterns: ["/node_modules", "/dist"], - testPathIgnorePatterns: ["/__fixtures__/"], - coveragePathIgnorePatterns: ["/__fixtures__/"], + transformIgnorePatterns: [ + "node_modules/*", + "packages/turbo-utils/*", + "packages/turbo-workspaces/*", + ], + modulePathIgnorePatterns: [ + "/node_modules", + "/dist", + "/__tests__/__fixtures__", + ], + testPathIgnorePatterns: [ + "__tests__/__fixtures__/", + "/__tests__/test-utils.ts", + ], + coveragePathIgnorePatterns: [ + "__tests__/__fixtures__/", + "/__tests__/test-utils.ts", + ], collectCoverage: true, coverageThreshold: { global: { - branches: 80, - functions: 89, - lines: 89, - statements: 89, + branches: 85, + functions: 93, + lines: 92, + statements: 92, }, }, verbose: process.env.RUNNER_DEBUG === "1", diff --git a/packages/turbo-codemod/package.json b/packages/turbo-codemod/package.json index 013ffe57f122a..2bc7b0ffc8a57 100644 --- a/packages/turbo-codemod/package.json +++ b/packages/turbo-codemod/package.json @@ -42,6 +42,7 @@ "@turbo/tsconfig": "workspace:*", "@turbo/types": "workspace:*", "@turbo/utils": "workspace:*", + "@turbo/workspaces": "workspace:*", "@types/chalk-animation": "^1.6.0", "@types/diff": "^5.0.2", "@types/fs-extra": "^9.0.13", diff --git a/packages/turbo-codemod/src/commands/migrate/index.ts b/packages/turbo-codemod/src/commands/migrate/index.ts index c4c6d02ebffea..4569eb91cc5a3 100644 --- a/packages/turbo-codemod/src/commands/migrate/index.ts +++ b/packages/turbo-codemod/src/commands/migrate/index.ts @@ -1,7 +1,7 @@ import chalk from "chalk"; import os from "os"; import inquirer from "inquirer"; -import { execSync } from "child_process"; +import { getWorkspaceDetails } from "@turbo/workspaces"; import getCurrentVersion from "./steps/getCurrentVersion"; import getLatestVersion from "./steps/getLatestVersion"; @@ -12,6 +12,9 @@ import getTurboUpgradeCommand from "./steps/getTurboUpgradeCommand"; import Runner from "../../runner/Runner"; import type { MigrateCommandArgument, MigrateCommandOptions } from "./types"; import looksLikeRepo from "../../utils/looksLikeRepo"; +import { TransformerResults } from "../../runner"; +import { shutdownDaemon } from "./steps/shutdownDaemon"; +import { execSync } from "child_process"; function endMigration({ message, @@ -95,12 +98,22 @@ export default async function migrate( }); } + const project = await getWorkspaceDetails({ root }); + if (!project) { + return endMigration({ + success: false, + message: `Unable to read determine package manager details from ${chalk.dim( + root + )}`, + }); + } + // step 1 - const fromVersion = getCurrentVersion(selectedDirectory, options); + const fromVersion = getCurrentVersion(project, options); if (!fromVersion) { return endMigration({ success: false, - message: `Unable to infer the version of turbo being used by ${directory}`, + message: `Unable to infer the version of turbo being used by ${project.name}`, }); } @@ -144,6 +157,12 @@ export default async function migrate( ); } + // shutdown the turbo daemon before running codemods and upgrading + // the daemon can handle version mismatches, but we do this as an extra precaution + if (!options.dry) { + shutdownDaemon({ project }); + } + // step 4 console.log( `Upgrading turbo from ${chalk.bold(fromVersion)} to ${chalk.bold( @@ -157,17 +176,20 @@ export default async function migrate( })`, os.EOL ); - const results = codemods.map((codemod, idx) => { + + const results: Array = []; + for (let [idx, codemod] of codemods.entries()) { console.log( - `(${idx + 1}/${codemods.length}) ${chalk.bold( - `Running ${codemod.value}` - )}` + `(${idx + 1}/${codemods.length}) ${chalk.bold(`Running ${codemod.name}`)}` ); - const result = codemod.transformer({ root: selectedDirectory, options }); + const result = await codemod.transformer({ + root: project.paths.root, + options, + }); Runner.logResults(result); - return result; - }); + results.push(result); + } const hasTransformError = results.some( (result) => @@ -183,8 +205,10 @@ export default async function migrate( } // step 5 - const upgradeCommand = getTurboUpgradeCommand({ - directory: selectedDirectory, + + // find the upgrade command, and run it + const upgradeCommand = await getTurboUpgradeCommand({ + project, to: options.to, }); @@ -195,6 +219,7 @@ export default async function migrate( }); } + // install if (options.install) { if (options.dry) { console.log( @@ -205,7 +230,14 @@ export default async function migrate( ); } else { console.log(`Upgrading turbo with ${chalk.bold(upgradeCommand)}`, os.EOL); - execSync(upgradeCommand, { cwd: selectedDirectory }); + try { + execSync(upgradeCommand, { stdio: "pipe", cwd: project.paths.root }); + } catch (err) { + return endMigration({ + success: false, + message: `Unable to upgrade turbo: ${err}`, + }); + } } } else { console.log(`Upgrade turbo with ${chalk.bold(upgradeCommand)}`, os.EOL); diff --git a/packages/turbo-codemod/src/commands/migrate/steps/getCurrentVersion.ts b/packages/turbo-codemod/src/commands/migrate/steps/getCurrentVersion.ts index 3644f8b2e15ac..9ae89910010f3 100644 --- a/packages/turbo-codemod/src/commands/migrate/steps/getCurrentVersion.ts +++ b/packages/turbo-codemod/src/commands/migrate/steps/getCurrentVersion.ts @@ -1,12 +1,9 @@ -import path from "path"; -import { existsSync } from "fs-extra"; - -import getPackageManager from "../../../utils/getPackageManager"; +import { type Project } from "@turbo/workspaces"; import { exec } from "../utils"; import type { MigrateCommandOptions } from "../types"; function getCurrentVersion( - directory: string, + project: Project, opts: MigrateCommandOptions ): string | undefined { const { from } = opts; @@ -15,30 +12,25 @@ function getCurrentVersion( } // try global first - const turboVersionFromGlobal = exec(`turbo --version`, { cwd: directory }); + const turboVersionFromGlobal = exec(`turbo --version`, { + cwd: project.paths.root, + }); if (turboVersionFromGlobal) { return turboVersionFromGlobal; } - // try to use the package manager to find the version - const packageManager = getPackageManager({ directory }); - if (packageManager) { - if (packageManager === "yarn") { - return exec(`yarn turbo --version`, { cwd: directory }); - } - if (packageManager === "pnpm") { - return exec(`pnpm turbo --version`, { cwd: directory }); - } else { - // this doesn't work for npm, so manually build the binary path - const turboBin = path.join(directory, "node_modules", ".bin", "turbo"); - if (existsSync(turboBin)) { - return exec(`${turboBin} --version`, { cwd: directory }); - } - } + const { packageManager } = project; + if (packageManager === "yarn") { + return exec(`yarn turbo --version`, { cwd: project.paths.root }); + } + if (packageManager === "pnpm") { + return exec(`pnpm turbo --version`, { cwd: project.paths.root }); + } + if (packageManager === "npm") { + return exec(`npm exec -c 'turbo --version'`, { cwd: project.paths.root }); } - // unable to determine local version, return undefined; } diff --git a/packages/turbo-codemod/src/commands/migrate/steps/getTurboUpgradeCommand.ts b/packages/turbo-codemod/src/commands/migrate/steps/getTurboUpgradeCommand.ts index 8fd5972ab8070..8ee28f2a35fa9 100644 --- a/packages/turbo-codemod/src/commands/migrate/steps/getTurboUpgradeCommand.ts +++ b/packages/turbo-codemod/src/commands/migrate/steps/getTurboUpgradeCommand.ts @@ -1,25 +1,16 @@ -import os from "os"; import path from "path"; import fs from "fs-extra"; import { gte } from "semver"; +import { + getAvailablePackageManagers, + getPackageManagersBinPaths, +} from "@turbo/utils"; +import { type Project, type PackageManager } from "@turbo/workspaces"; import { exec } from "../utils"; -import getPackageManager, { - PackageManager, -} from "../../../utils/getPackageManager"; -import getPackageManagerVersion from "../../../utils/getPackageManagerVersion"; type InstallType = "dependencies" | "devDependencies"; -function getGlobalBinaryPaths(): Record { - return { - // we run these from a tmpdir to avoid corepack interference - yarn: exec(`yarn global bin`, { cwd: os.tmpdir() }), - npm: exec(`npm bin --global`, { cwd: os.tmpdir() }), - pnpm: exec(`pnpm bin --global`, { cwd: os.tmpdir() }), - }; -} - function getGlobalUpgradeCommand( packageManager: PackageManager, to: string = "latest" @@ -30,7 +21,7 @@ function getGlobalUpgradeCommand( case "npm": return `npm install turbo@${to} --global`; case "pnpm": - return `pnpm install turbo@${to} --global`; + return `pnpm add turbo@${to} --global`; } } @@ -81,7 +72,7 @@ function getLocalUpgradeCommand({ case "pnpm": return renderCommand([ "pnpm", - "install", + "add", `turbo@${to}`, installType === "devDependencies" && "--save-dev", isUsingWorkspaces && "-w", @@ -89,18 +80,12 @@ function getLocalUpgradeCommand({ } } -function getInstallType({ directory }: { directory: string }): { - installType?: InstallType; - isUsingWorkspaces?: boolean; -} { +function getInstallType({ root }: { root: string }): InstallType | undefined { // read package.json to make sure we have a reference to turbo - const packageJsonPath = path.join(directory, "package.json"); - const pnpmWorkspaceConfig = path.join(directory, "pnpm-workspace.yaml"); - const isPnpmWorkspaces = fs.existsSync(pnpmWorkspaceConfig); - + const packageJsonPath = path.join(root, "package.json"); if (!fs.existsSync(packageJsonPath)) { console.error(`Unable to find package.json at ${packageJsonPath}`); - return { installType: undefined, isUsingWorkspaces: undefined }; + return undefined; } const packageJson = fs.readJsonSync(packageJsonPath); @@ -108,19 +93,12 @@ function getInstallType({ directory }: { directory: string }): { packageJson.devDependencies && "turbo" in packageJson.devDependencies; const isDependency = packageJson.dependencies && "turbo" in packageJson.dependencies; - let isUsingWorkspaces = "workspaces" in packageJson || isPnpmWorkspaces; if (isDependency || isDevDependency) { - return { - installType: isDependency ? "dependencies" : "devDependencies", - isUsingWorkspaces, - }; + return isDependency ? "dependencies" : "devDependencies"; } - return { - installType: undefined, - isUsingWorkspaces, - }; + return undefined; } /** @@ -130,19 +108,18 @@ function getInstallType({ directory }: { directory: string }): { We try global first to let turbo handle the inference, then we try local. **/ -export default function getTurboUpgradeCommand({ - directory, +export default async function getTurboUpgradeCommand({ + project, to, }: { - directory: string; + project: Project; to?: string; }) { const turboBinaryPathFromGlobal = exec(`turbo bin`, { - cwd: directory, + cwd: project.paths.root, stdio: "pipe", }); - const packageManagerGlobalBinaryPaths = getGlobalBinaryPaths(); - + const packageManagerGlobalBinaryPaths = await getPackageManagersBinPaths(); const globalPackageManager = Object.keys( packageManagerGlobalBinaryPaths ).find((packageManager) => { @@ -153,24 +130,23 @@ export default function getTurboUpgradeCommand({ } return false; - }) as PackageManager; + }) as PackageManager | undefined; if (turboBinaryPathFromGlobal && globalPackageManager) { // figure which package manager we need to upgrade return getGlobalUpgradeCommand(globalPackageManager, to); } else { - const packageManager = getPackageManager({ directory }); + const { packageManager } = project; // we didn't find a global install, so we'll try to find a local one - const { installType, isUsingWorkspaces } = getInstallType({ directory }); - if (packageManager && installType) { - const packageManagerVersion = getPackageManagerVersion( - packageManager, - directory - ); + const isUsingWorkspaces = project.workspaceData.globs.length > 0; + const installType = getInstallType({ root: project.paths.root }); + const availablePackageManagers = await getAvailablePackageManagers(); + const version = availablePackageManagers[packageManager]; + if (packageManager && version && installType) { return getLocalUpgradeCommand({ packageManager, - packageManagerVersion, + packageManagerVersion: version, installType, isUsingWorkspaces, to, diff --git a/packages/turbo-codemod/src/commands/migrate/steps/shutdownDaemon.ts b/packages/turbo-codemod/src/commands/migrate/steps/shutdownDaemon.ts new file mode 100644 index 0000000000000..5947ac0960e87 --- /dev/null +++ b/packages/turbo-codemod/src/commands/migrate/steps/shutdownDaemon.ts @@ -0,0 +1,28 @@ +import type { Project } from "@turbo/workspaces"; +import type { ExecSyncOptions } from "child_process"; +import { exec } from "../utils"; + +export function shutdownDaemon({ project }: { project: Project }) { + try { + const execOpts: ExecSyncOptions = { + cwd: project.paths.root, + stdio: "ignore", + }; + // see if we have a global install + const turboBinaryPathFromGlobal = exec(`turbo bin`, execOpts); + // if we do, shut it down + if (turboBinaryPathFromGlobal) { + exec(`turbo daemon stop`, execOpts); + } else { + // call turbo using the project package manager to shut down the daemon + let command = `${project.packageManager} turbo daemon stop`; + if (project.packageManager === "npm") { + command = `npm exec -c 'turbo daemon stop'`; + } + + exec(command, execOpts); + } + } catch (e) { + // skip + } +} diff --git a/packages/turbo-codemod/src/commands/migrate/types.ts b/packages/turbo-codemod/src/commands/migrate/types.ts index ae9096571ffd2..c310ef09f952c 100644 --- a/packages/turbo-codemod/src/commands/migrate/types.ts +++ b/packages/turbo-codemod/src/commands/migrate/types.ts @@ -1,6 +1,6 @@ import { TransformerOptions } from "../../types"; -export type MigrateCommandArgument = "string" | undefined; +export type MigrateCommandArgument = string | undefined; export interface MigrateCommandOptions extends TransformerOptions { from?: string; diff --git a/packages/turbo-codemod/src/commands/migrate/utils.ts b/packages/turbo-codemod/src/commands/migrate/utils.ts index 512d78bbc4257..d6a42262f79c1 100644 --- a/packages/turbo-codemod/src/commands/migrate/utils.ts +++ b/packages/turbo-codemod/src/commands/migrate/utils.ts @@ -6,7 +6,7 @@ function exec( fallback?: string ): string | undefined { try { - const rawResult = execSync(command, opts); + const rawResult = execSync(command, { stdio: "pipe", ...opts }); return rawResult.toString("utf8").trim(); } catch (err) { return fallback || undefined; diff --git a/packages/turbo-codemod/src/commands/transform/index.ts b/packages/turbo-codemod/src/commands/transform/index.ts index e3b86aa74f261..8cf138f4a4f43 100644 --- a/packages/turbo-codemod/src/commands/transform/index.ts +++ b/packages/turbo-codemod/src/commands/transform/index.ts @@ -19,7 +19,7 @@ export default async function transform( if (options.list) { console.log( transforms - .map((transform) => `- ${chalk.cyan(transform.value)}`) + .map((transform) => `- ${chalk.cyan(transform.name)}`) .join("\n") ); return process.exit(0); @@ -56,7 +56,12 @@ export default async function transform( message: "Which transform would you like to apply?", when: !transform, pageSize: transforms.length, - choices: transforms, + choices: transforms.map((t) => ({ + name: `${chalk.bold(t.name)} - ${chalk.gray( + t.description + )} ${chalk.gray(`(${t.introducedIn})`)}`, + value: t.name, + })), }, ]); @@ -72,9 +77,9 @@ export default async function transform( return process.exit(1); } - const transformKeys = transforms.map((transform) => transform.value); + const transformKeys = transforms.map((transform) => transform.name); const transformData = transforms.find( - (transform) => transform.value === selectedTransformer + (transform) => transform.name === selectedTransformer ); // validate transforms @@ -87,7 +92,7 @@ export default async function transform( } // run the transform - const result = transformData.transformer({ + const result = await transformData.transformer({ root, options, }); diff --git a/packages/turbo-codemod/src/commands/transform/types.ts b/packages/turbo-codemod/src/commands/transform/types.ts index 9ac2db02f88af..9e5bf6f335423 100644 --- a/packages/turbo-codemod/src/commands/transform/types.ts +++ b/packages/turbo-codemod/src/commands/transform/types.ts @@ -1,6 +1,6 @@ import { TransformerOptions } from "../../types"; -export type TransformCommandArgument = "string" | undefined; +export type TransformCommandArgument = string | undefined; export interface TransformCommandOptions extends TransformerOptions { list: boolean; diff --git a/packages/turbo-codemod/src/transforms/README.md b/packages/turbo-codemod/src/transforms/README.md index 8e4430fb58d87..c5b32ce1494e2 100644 --- a/packages/turbo-codemod/src/transforms/README.md +++ b/packages/turbo-codemod/src/transforms/README.md @@ -19,7 +19,7 @@ All new transformers must contain a default export that matches the [`Transforme ```ts export type Transformer = { name: string; - value: string; + description: string; introducedIn: string; transformer: (args: TransformerArgs) => TransformerResults; }; diff --git a/packages/turbo-codemod/src/transforms/add-package-manager.ts b/packages/turbo-codemod/src/transforms/add-package-manager.ts index bd6581f20e741..9a2276b827913 100644 --- a/packages/turbo-codemod/src/transforms/add-package-manager.ts +++ b/packages/turbo-codemod/src/transforms/add-package-manager.ts @@ -1,21 +1,21 @@ import path from "path"; import fs from "fs-extra"; -import getPackageManager from "../utils/getPackageManager"; -import getPackageManagerVersion from "../utils/getPackageManagerVersion"; import getTransformerHelpers from "../utils/getTransformerHelpers"; import { TransformerResults } from "../runner"; import type { TransformerArgs } from "../types"; +import { Project, getWorkspaceDetails } from "@turbo/workspaces"; +import { getAvailablePackageManagers } from "@turbo/utils"; // transformer details const TRANSFORMER = "add-package-manager"; const DESCRIPTION = "Set the `packageManager` key in root `package.json` file"; const INTRODUCED_IN = "1.1.0"; -export function transformer({ +export async function transformer({ root, options, -}: TransformerArgs): TransformerResults { +}: TransformerArgs): Promise { const { log, runner } = getTransformerHelpers({ transformer: TRANSFORMER, rootPath: root, @@ -23,22 +23,24 @@ export function transformer({ }); log.info(`Set "packageManager" key in root "package.json" file...`); - const packageManager = getPackageManager({ directory: root }); - if (!packageManager) { + let project: Project; + try { + project = await getWorkspaceDetails({ root }); + } catch (e) { return runner.abortTransform({ reason: `Unable to determine package manager for ${root}`, }); } - // handle workspaces... - let version = null; - try { - version = getPackageManagerVersion(packageManager, root); - } catch (err) { + const availablePackageManagers = await getAvailablePackageManagers(); + const { packageManager } = project; + const version = availablePackageManagers[packageManager]; + if (!version) { return runner.abortTransform({ reason: `Unable to determine package manager version for ${root}`, }); } + const pkgManagerString = `${packageManager}@${version}`; const rootPackageJsonPath = path.join(root, "package.json"); const rootPackageJson = fs.readJsonSync(rootPackageJsonPath); @@ -66,8 +68,8 @@ export function transformer({ } const transformerMeta = { - name: `${TRANSFORMER}: ${DESCRIPTION}`, - value: TRANSFORMER, + name: TRANSFORMER, + description: DESCRIPTION, introducedIn: INTRODUCED_IN, transformer, }; diff --git a/packages/turbo-codemod/src/transforms/create-turbo-config.ts b/packages/turbo-codemod/src/transforms/create-turbo-config.ts index 0e8549ab61600..bf143dd1dbd72 100644 --- a/packages/turbo-codemod/src/transforms/create-turbo-config.ts +++ b/packages/turbo-codemod/src/transforms/create-turbo-config.ts @@ -61,8 +61,8 @@ export function transformer({ } const transformerMeta = { - name: `${TRANSFORMER}: ${DESCRIPTION}`, - value: TRANSFORMER, + name: TRANSFORMER, + description: DESCRIPTION, introducedIn: INTRODUCED_IN, transformer, }; diff --git a/packages/turbo-codemod/src/transforms/migrate-env-var-dependencies.ts b/packages/turbo-codemod/src/transforms/migrate-env-var-dependencies.ts index ef3a34cf2d5fb..59be28acfc8c2 100644 --- a/packages/turbo-codemod/src/transforms/migrate-env-var-dependencies.ts +++ b/packages/turbo-codemod/src/transforms/migrate-env-var-dependencies.ts @@ -172,8 +172,8 @@ export function transformer({ } const transformerMeta = { - name: `${TRANSFORMER}: ${DESCRIPTION}`, - value: TRANSFORMER, + name: TRANSFORMER, + description: DESCRIPTION, introducedIn: INTRODUCED_IN, transformer, }; diff --git a/packages/turbo-codemod/src/transforms/set-default-outputs.ts b/packages/turbo-codemod/src/transforms/set-default-outputs.ts index 44f7fd1f8b755..5d7ea7a77be4b 100644 --- a/packages/turbo-codemod/src/transforms/set-default-outputs.ts +++ b/packages/turbo-codemod/src/transforms/set-default-outputs.ts @@ -88,8 +88,8 @@ export function transformer({ } const transformerMeta = { - name: `${TRANSFORMER}: ${DESCRIPTION}`, - value: TRANSFORMER, + name: TRANSFORMER, + description: DESCRIPTION, introducedIn: INTRODUCED_IN, transformer, }; diff --git a/packages/turbo-codemod/src/transforms/stabilize-env-mode.ts b/packages/turbo-codemod/src/transforms/stabilize-env-mode.ts index 926392e8ececf..92f283755f399 100644 --- a/packages/turbo-codemod/src/transforms/stabilize-env-mode.ts +++ b/packages/turbo-codemod/src/transforms/stabilize-env-mode.ts @@ -142,8 +142,8 @@ export function transformer({ } const transformerMeta = { - name: `${TRANSFORMER}: ${DESCRIPTION}`, - value: TRANSFORMER, + name: TRANSFORMER, + description: DESCRIPTION, introducedIn: INTRODUCED_IN, transformer, }; diff --git a/packages/turbo-codemod/src/transforms/transform-env-literals-to-wildcards.ts b/packages/turbo-codemod/src/transforms/transform-env-literals-to-wildcards.ts index e6f69c44b3c46..ef15195d85d56 100644 --- a/packages/turbo-codemod/src/transforms/transform-env-literals-to-wildcards.ts +++ b/packages/turbo-codemod/src/transforms/transform-env-literals-to-wildcards.ts @@ -115,8 +115,8 @@ export function transformer({ } const transformerMeta = { - name: `${TRANSFORMER}: ${DESCRIPTION}`, - value: TRANSFORMER, + name: TRANSFORMER, + description: DESCRIPTION, introducedIn: INTRODUCED_IN, transformer, }; diff --git a/packages/turbo-codemod/src/types.ts b/packages/turbo-codemod/src/types.ts index d5c13c35db149..aa3847bb8cf53 100644 --- a/packages/turbo-codemod/src/types.ts +++ b/packages/turbo-codemod/src/types.ts @@ -2,9 +2,11 @@ import { TransformerResults } from "./runner"; export type Transformer = { name: string; - value: string; + description: string; introducedIn: string; - transformer: (args: TransformerArgs) => TransformerResults; + transformer: ( + args: TransformerArgs + ) => Promise | TransformerResults; }; export type TransformerOptions = { diff --git a/packages/turbo-codemod/src/utils/getPackageManager.ts b/packages/turbo-codemod/src/utils/getPackageManager.ts deleted file mode 100644 index 1df0accd73827..0000000000000 --- a/packages/turbo-codemod/src/utils/getPackageManager.ts +++ /dev/null @@ -1,42 +0,0 @@ -import findUp from "find-up"; -import path from "path"; - -export type PackageManager = "yarn" | "pnpm" | "npm"; - -const cache: { [cwd: string]: PackageManager } = {}; - -export default function getPackageManager({ - directory, -}: { directory?: string } = {}): PackageManager | undefined { - const cwd = directory || process.cwd(); - if (cache[cwd]) { - return cache[cwd]; - } - - const lockFile = findUp.sync( - ["yarn.lock", "pnpm-lock.yaml", "package-lock.json"], - { - cwd, - } - ); - - if (!lockFile) { - return; - } - - switch (path.basename(lockFile)) { - case "yarn.lock": - cache[cwd] = "yarn"; - break; - - case "pnpm-lock.yaml": - cache[cwd] = "pnpm"; - break; - - case "package-lock.json": - cache[cwd] = "npm"; - break; - } - - return cache[cwd]; -} diff --git a/packages/turbo-codemod/src/utils/getPackageManagerVersion.ts b/packages/turbo-codemod/src/utils/getPackageManagerVersion.ts deleted file mode 100644 index 54a572af07a83..0000000000000 --- a/packages/turbo-codemod/src/utils/getPackageManagerVersion.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { execSync } from "child_process"; -import type { PackageManager } from "./getPackageManager"; - -export default function getPackageManagerVersion( - packageManager: PackageManager, - root: string -): string { - switch (packageManager) { - case "yarn": - return execSync("yarn --version", { cwd: root }).toString().trim(); - case "pnpm": - return execSync("pnpm --version", { cwd: root }).toString().trim(); - case "npm": - return execSync("npm --version", { cwd: root }).toString().trim(); - } -} diff --git a/packages/turbo-codemod/src/utils/notifyUpdate.ts b/packages/turbo-codemod/src/utils/notifyUpdate.ts index 634ffd8c0fe87..2a2015d393a07 100644 --- a/packages/turbo-codemod/src/utils/notifyUpdate.ts +++ b/packages/turbo-codemod/src/utils/notifyUpdate.ts @@ -2,7 +2,7 @@ import chalk from "chalk"; import checkForUpdate from "update-check"; import cliPkgJson from "../../package.json"; -import getWorkspaceImplementation from "./getPackageManager"; +import { getWorkspaceDetails } from "@turbo/workspaces"; const update = checkForUpdate(cliPkgJson).catch(() => null); @@ -10,7 +10,9 @@ export default async function notifyUpdate(): Promise { try { const res = await update; if (res?.latest) { - const ws = getWorkspaceImplementation(); + const project = await getWorkspaceDetails({ root: process.cwd() }); + + const { packageManager } = project || {}; console.log(); console.log( @@ -19,9 +21,9 @@ export default async function notifyUpdate(): Promise { console.log( "You can update by running: " + chalk.cyan( - ws === "yarn" + packageManager === "yarn" ? "yarn global add @turbo/codemod" - : ws === "pnpm" + : packageManager === "pnpm" ? "pnpm i -g @turbo/codemod" : "npm i -g @turbo/codemod" ) diff --git a/packages/turbo-gen/__tests__/raw.test.ts b/packages/turbo-gen/__tests__/raw.test.ts index 16d795c7702d3..458b8dd853845 100644 --- a/packages/turbo-gen/__tests__/raw.test.ts +++ b/packages/turbo-gen/__tests__/raw.test.ts @@ -115,11 +115,9 @@ describe("raw", () => { // and what they are called with const mockWorkspace = jest .spyOn(workspace, "workspace") - .mockImplementation(() => Promise.resolve(undefined)); + .mockResolvedValue(undefined); - const mockRun = jest - .spyOn(run, "run") - .mockImplementation(() => Promise.resolve(undefined)); + const mockRun = jest.spyOn(run, "run").mockResolvedValue(undefined); await raw(command, { json: JSON.stringify(options) }); diff --git a/packages/turbo-gen/jest.config.js b/packages/turbo-gen/jest.config.js index a273992d5cd70..bf47f779c227b 100644 --- a/packages/turbo-gen/jest.config.js +++ b/packages/turbo-gen/jest.config.js @@ -2,9 +2,13 @@ module.exports = { preset: "ts-jest/presets/js-with-ts", testEnvironment: "node", - testPathIgnorePatterns: ["/__fixtures__/", "/__tests__/test-utils.ts"], - coveragePathIgnorePatterns: ["/__fixtures__/", "/__tests__/test-utils.ts"], - transformIgnorePatterns: ["/node_modules/(?!(ansi-regex)/)"], + testPathIgnorePatterns: ["__fixtures__/", "/__tests__/test-utils.ts"], + coveragePathIgnorePatterns: ["__fixtures__/", "/__tests__/test-utils.ts"], + transformIgnorePatterns: [ + "node_modules/*", + "packages/turbo-utils/*", + "packages/turbo-workspaces/*", + ], modulePathIgnorePatterns: ["/node_modules", "/dist"], collectCoverage: true, verbose: process.env.RUNNER_DEBUG === "1", diff --git a/packages/turbo-ignore/jest.config.js b/packages/turbo-ignore/jest.config.js index e043ffb2da6a1..2e4a1785f17cd 100644 --- a/packages/turbo-ignore/jest.config.js +++ b/packages/turbo-ignore/jest.config.js @@ -14,7 +14,7 @@ module.exports = { }, }, modulePathIgnorePatterns: ["/node_modules", "/dist"], - transformIgnorePatterns: ["/node_modules/(?!(ansi-regex)/)"], + transformIgnorePatterns: ["node_modules/*", "packages/turbo-utils/*"], verbose: process.env.RUNNER_DEBUG === "1", silent: process.env.RUNNER_DEBUG !== "1", }; diff --git a/packages/turbo-utils/src/index.ts b/packages/turbo-utils/src/index.ts index 7ff098e575e01..cd4aaf760fe83 100644 --- a/packages/turbo-utils/src/index.ts +++ b/packages/turbo-utils/src/index.ts @@ -2,7 +2,10 @@ export { getTurboRoot } from "./getTurboRoot"; export { getTurboConfigs, getWorkspaceConfigs } from "./getTurboConfigs"; export { searchUp } from "./searchUp"; -export { getAvailablePackageManagers } from "./managers"; +export { + getAvailablePackageManagers, + getPackageManagersBinPaths, +} from "./managers"; export { isFolderEmpty } from "./isFolderEmpty"; export { validateDirectory } from "./validateDirectory"; export { diff --git a/packages/turbo-utils/src/managers.ts b/packages/turbo-utils/src/managers.ts index ab9c53d5bc044..82456d3292192 100644 --- a/packages/turbo-utils/src/managers.ts +++ b/packages/turbo-utils/src/managers.ts @@ -1,46 +1,33 @@ -import execa from "execa"; +import execa, { Options } from "execa"; import os from "os"; export type PackageManager = "npm" | "yarn" | "pnpm"; export type PackageManagerAvailable = { available: boolean; version?: string }; -async function getVersion( - packageManager: string -): Promise { +async function exec(command: string, args: string[] = [], opts?: Options) { // run the check from tmpdir to avoid corepack conflicting - // this is no longer needed as of https://github.com/nodejs/corepack/pull/167 // but we'll keep the behavior for those on older versions) - const execOptions = { + const execOptions: Options = { cwd: os.tmpdir(), env: { COREPACK_ENABLE_STRICT: "0" }, + ...opts, }; - - let available = false; try { - const userAgent = process.env.npm_config_user_agent; - if (userAgent && userAgent.startsWith(packageManager)) { - available = true; - } - - const result = await execa(packageManager, ["--version"], execOptions); - return { - available: true, - version: result.stdout.trim(), - }; - } catch (e) { - return { - available, - }; + const { stdout } = await execa(command, args, execOptions); + return stdout.trim(); + } catch { + return undefined; } } -async function getAvailablePackageManagers(): Promise< - Record +export async function getAvailablePackageManagers(): Promise< + Record > { const [yarn, npm, pnpm] = await Promise.all([ - getVersion("yarnpkg"), - getVersion("npm"), - getVersion("pnpm"), + exec("yarnpkg", ["--version"]), + exec("npm", ["--version"]), + exec("pnpm", ["--version"]), ]); return { @@ -50,4 +37,18 @@ async function getAvailablePackageManagers(): Promise< }; } -export { getAvailablePackageManagers }; +export async function getPackageManagersBinPaths(): Promise< + Record +> { + const [yarn, npm, pnpm] = await Promise.all([ + exec("yarnpkg", ["global", "bin"]), + exec("npm", ["config", "get", "prefix"]), + exec("pnpm", ["bin", "--global"]), + ]); + + return { + yarn, + pnpm, + npm, + }; +} diff --git a/packages/turbo-workspaces/__tests__/index.test.ts b/packages/turbo-workspaces/__tests__/index.test.ts index 21753be8f3a1b..b05fd503a6492 100644 --- a/packages/turbo-workspaces/__tests__/index.test.ts +++ b/packages/turbo-workspaces/__tests__/index.test.ts @@ -26,18 +26,9 @@ describe("Node entrypoint", () => { const mockedGetAvailablePackageManagers = jest .spyOn(turboUtils, "getAvailablePackageManagers") .mockResolvedValue({ - npm: { - available: true, - version: "8.19.2", - }, - yarn: { - available: true, - version: "1.22.19", - }, - pnpm: { - available: true, - version: "7.29.1", - }, + npm: "8.19.2", + yarn: "1.22.19", + pnpm: "7.29.1", }); const { root } = useFixture({ diff --git a/packages/turbo-workspaces/jest.config.js b/packages/turbo-workspaces/jest.config.js index b813f6c959ffa..e9a8cc9265452 100644 --- a/packages/turbo-workspaces/jest.config.js +++ b/packages/turbo-workspaces/jest.config.js @@ -4,7 +4,7 @@ module.exports = { testEnvironment: "node", testPathIgnorePatterns: ["/__fixtures__/", "/__tests__/test-utils.ts"], coveragePathIgnorePatterns: ["/__fixtures__/", "/__tests__/test-utils.ts"], - transformIgnorePatterns: ["/node_modules/(?!(ansi-regex)/)"], + transformIgnorePatterns: ["node_modules/*", "packages/turbo-utils/*"], modulePathIgnorePatterns: ["/node_modules", "/dist"], collectCoverage: true, coverageThreshold: { diff --git a/packages/turbo-workspaces/package.json b/packages/turbo-workspaces/package.json index ab2048dc03129..a10baf5fce77e 100644 --- a/packages/turbo-workspaces/package.json +++ b/packages/turbo-workspaces/package.json @@ -13,6 +13,7 @@ "url": "https://github.com/vercel/turbo/issues" }, "bin": "dist/cli.js", + "module": "dist/index.mjs", "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { diff --git a/packages/turbo-workspaces/src/commands/convert/index.ts b/packages/turbo-workspaces/src/commands/convert/index.ts index 6cc82fc9d8944..8b24363207196 100644 --- a/packages/turbo-workspaces/src/commands/convert/index.ts +++ b/packages/turbo-workspaces/src/commands/convert/index.ts @@ -15,13 +15,13 @@ function isPackageManagerDisabled({ }: { packageManager: PackageManager; currentWorkspaceManger: PackageManager; - availablePackageManagers: Record; + availablePackageManagers: Record; }) { if (currentWorkspaceManger === packageManager) { return "already in use"; } - if (!availablePackageManagers[packageManager].available) { + if (!availablePackageManagers[packageManager]) { return "not installed"; } @@ -100,8 +100,7 @@ export default async function convertCommand( project, to: { name: selectedPackageManager, - version: availablePackageManagers[selectedPackageManager] - .version as string, + version: availablePackageManagers[selectedPackageManager], }, logger, options, diff --git a/packages/turbo-workspaces/src/index.ts b/packages/turbo-workspaces/src/index.ts index c7fc6b8cd2086..3d0d59b33d85c 100644 --- a/packages/turbo-workspaces/src/index.ts +++ b/packages/turbo-workspaces/src/index.ts @@ -33,7 +33,7 @@ async function convert({ project, to: { name: to, - version: availablePackageManagers[to].version as PackageManager, + version: availablePackageManagers[to], }, logger, options, diff --git a/packages/turbo-workspaces/tsup.config.ts b/packages/turbo-workspaces/tsup.config.ts index 168e1de8dbbe1..a094293a429f4 100644 --- a/packages/turbo-workspaces/tsup.config.ts +++ b/packages/turbo-workspaces/tsup.config.ts @@ -2,7 +2,7 @@ import { defineConfig, Options } from "tsup"; export default defineConfig((options: Options) => ({ entry: ["src/index.ts", "src/cli.ts"], - format: ["cjs"], + format: ["cjs", "esm"], dts: true, clean: true, minify: true, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cea139802acb1..1037943ba4f84 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -476,6 +476,7 @@ importers: '@turbo/tsconfig': workspace:* '@turbo/types': workspace:* '@turbo/utils': workspace:* + '@turbo/workspaces': workspace:* '@types/chalk-animation': ^1.6.0 '@types/diff': ^5.0.2 '@types/fs-extra': ^9.0.13 @@ -525,6 +526,7 @@ importers: '@turbo/tsconfig': link:../tsconfig '@turbo/types': link:../turbo-types '@turbo/utils': link:../turbo-utils + '@turbo/workspaces': link:../turbo-workspaces '@types/chalk-animation': 1.6.1 '@types/diff': 5.0.2 '@types/fs-extra': 9.0.13