aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/packages/turbo-codemod/__tests__
diff options
context:
space:
mode:
author简律纯 <hsiangnianian@outlook.com>2023-04-28 01:36:44 +0800
committer简律纯 <hsiangnianian@outlook.com>2023-04-28 01:36:44 +0800
commitdd84b9d64fb98746a230cd24233ff50a562c39c9 (patch)
treeb583261ef00b3afe72ec4d6dacb31e57779a6faf /packages/turbo-codemod/__tests__
parent0b46fcd72ac34382387b2bcf9095233efbcc52f4 (diff)
downloadHydroRoll-dd84b9d64fb98746a230cd24233ff50a562c39c9.tar.gz
HydroRoll-dd84b9d64fb98746a230cd24233ff50a562c39c9.zip
Diffstat (limited to 'packages/turbo-codemod/__tests__')
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/add-package-manager/has-package-manager/package.json7
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/add-package-manager/no-package-manager/package.json6
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/add-package-manager/wrong-package-manager/package.json7
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/both-configs/package.json28
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/both-configs/turbo.json18
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/no-package-json-config/package.json7
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/no-package-json-file/a-random-file.txt1
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/no-turbo-json-config/package.json24
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/turbo-json-config/package.json7
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/turbo-json-config/turbo.json18
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/no-deps/package.json4
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/no-package/README.md1
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/no-turbo/package.json6
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/normal-workspaces-dev-install/package.json12
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/normal-workspaces/package.json12
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/pnpm-workspaces-dev-install/package.json8
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/pnpm-workspaces-dev-install/pnpm-workspace.yaml3
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/pnpm-workspaces/package.json8
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/pnpm-workspaces/pnpm-workspace.yaml3
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/single-package-dev-install/package.json8
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/single-package/package.json8
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/env-dependencies/turbo.json21
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/migrated-env-dependencies/turbo.json25
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/no-turbo-json/package.json7
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/old-config/package.json20
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/old-config/turbo.json12
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/docs/index.js6
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/docs/package.json4
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/docs/turbo.json9
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/web/index.js6
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/web/package.json4
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/web/turbo.json12
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/package.json14
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/packages/ui/index.js6
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/packages/ui/package.json4
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/packages/ui/turbo.json9
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/turbo.json21
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/migrate/no-repo/README.md1
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/migrate/old-turbo/package.json26
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/invalid-outputs/package.json7
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/invalid-outputs/turbo.json36
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-outputs/package.json7
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-outputs/turbo.json14
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-pipeline/package.json7
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-pipeline/turbo.json5
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-turbo-json/package.json7
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/old-config/package.json20
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/old-config/turbo.json12
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/old-outputs/package.json7
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/old-outputs/turbo.json12
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/docs/index.js6
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/docs/package.json4
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/docs/turbo.json7
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/web/index.js6
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/web/package.json4
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/web/turbo.json10
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/package.json14
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/packages/ui/index.js6
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/packages/ui/package.json4
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/packages/ui/turbo.json7
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/turbo.json12
-rw-r--r--packages/turbo-codemod/__tests__/__fixtures__/transform/basic/package.json8
-rw-r--r--packages/turbo-codemod/__tests__/add-package-manager.test.ts504
-rw-r--r--packages/turbo-codemod/__tests__/create-turbo-config.test.ts416
-rw-r--r--packages/turbo-codemod/__tests__/get-turbo-upgrade-command.test.ts576
-rw-r--r--packages/turbo-codemod/__tests__/migrate-env-var-dependencies.test.ts758
-rw-r--r--packages/turbo-codemod/__tests__/migrate.test.ts761
-rw-r--r--packages/turbo-codemod/__tests__/set-default-outputs.test.ts391
-rw-r--r--packages/turbo-codemod/__tests__/transform.test.ts172
69 files changed, 4203 insertions, 0 deletions
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/add-package-manager/has-package-manager/package.json b/packages/turbo-codemod/__tests__/__fixtures__/add-package-manager/has-package-manager/package.json
new file mode 100644
index 0000000..d6edac5
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/add-package-manager/has-package-manager/package.json
@@ -0,0 +1,7 @@
+{
+ "name": "has-package-manager",
+ "version": "1.0.0",
+ "dependencies": {},
+ "devDependencies": {},
+ "packageManager": "npm@1.2.3"
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/add-package-manager/no-package-manager/package.json b/packages/turbo-codemod/__tests__/__fixtures__/add-package-manager/no-package-manager/package.json
new file mode 100644
index 0000000..2e28fe4
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/add-package-manager/no-package-manager/package.json
@@ -0,0 +1,6 @@
+{
+ "name": "no-package-manager",
+ "version": "1.0.0",
+ "dependencies": {},
+ "devDependencies": {}
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/add-package-manager/wrong-package-manager/package.json b/packages/turbo-codemod/__tests__/__fixtures__/add-package-manager/wrong-package-manager/package.json
new file mode 100644
index 0000000..f58aca2
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/add-package-manager/wrong-package-manager/package.json
@@ -0,0 +1,7 @@
+{
+ "name": "has-package-manager",
+ "version": "1.0.0",
+ "dependencies": {},
+ "devDependencies": {},
+ "packageManager": "turbo@1.7.0"
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/both-configs/package.json b/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/both-configs/package.json
new file mode 100644
index 0000000..c4606fa
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/both-configs/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "both-configs",
+ "version": "1.0.0",
+ "dependencies": {},
+ "devDependencies": {},
+ "packageManager": "npm@1.2.3",
+ "turbo": {
+ "$schema": "https://turbo.build/schema.json",
+ "pipeline": {
+ "package-only": {
+ "cache": false,
+ "persistent": true
+ },
+ "build": {
+ "outputs": [
+ ".next/**",
+ "!.next/cache/**"
+ ]
+ },
+ "lint": {
+ "outputs": []
+ },
+ "dev": {
+ "cache": false
+ }
+ }
+ }
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/both-configs/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/both-configs/turbo.json
new file mode 100644
index 0000000..e6eb652
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/both-configs/turbo.json
@@ -0,0 +1,18 @@
+{
+ "$schema": "https://turbo.build/schema.json",
+ "pipeline": {
+ "turbo-only": {
+ "cache": false,
+ "persistent": true
+ },
+ "build": {
+ "outputs": [".next/**", "!.next/cache/**"]
+ },
+ "lint": {
+ "outputs": []
+ },
+ "dev": {
+ "cache": false
+ }
+ }
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/no-package-json-config/package.json b/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/no-package-json-config/package.json
new file mode 100644
index 0000000..b965b7d
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/no-package-json-config/package.json
@@ -0,0 +1,7 @@
+{
+ "name": "no-turbo-json-config",
+ "version": "1.0.0",
+ "dependencies": {},
+ "devDependencies": {},
+ "packageManager": "npm@1.2.3"
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/no-package-json-file/a-random-file.txt b/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/no-package-json-file/a-random-file.txt
new file mode 100644
index 0000000..7488fec
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/no-package-json-file/a-random-file.txt
@@ -0,0 +1 @@
+Nothing exists here
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/no-turbo-json-config/package.json b/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/no-turbo-json-config/package.json
new file mode 100644
index 0000000..7754c7d
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/no-turbo-json-config/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "no-turbo-json-config",
+ "version": "1.0.0",
+ "dependencies": {},
+ "devDependencies": {},
+ "packageManager": "npm@1.2.3",
+ "turbo": {
+ "$schema": "https://turbo.build/schema.json",
+ "pipeline": {
+ "build": {
+ "outputs": [
+ ".next/**",
+ "!.next/cache/**"
+ ]
+ },
+ "lint": {
+ "outputs": []
+ },
+ "dev": {
+ "cache": false
+ }
+ }
+ }
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/turbo-json-config/package.json b/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/turbo-json-config/package.json
new file mode 100644
index 0000000..a48d0ec
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/turbo-json-config/package.json
@@ -0,0 +1,7 @@
+{
+ "name": "both-configs",
+ "version": "1.0.0",
+ "dependencies": {},
+ "devDependencies": {},
+ "packageManager": "npm@1.2.3"
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/turbo-json-config/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/turbo-json-config/turbo.json
new file mode 100644
index 0000000..e6eb652
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/turbo-json-config/turbo.json
@@ -0,0 +1,18 @@
+{
+ "$schema": "https://turbo.build/schema.json",
+ "pipeline": {
+ "turbo-only": {
+ "cache": false,
+ "persistent": true
+ },
+ "build": {
+ "outputs": [".next/**", "!.next/cache/**"]
+ },
+ "lint": {
+ "outputs": []
+ },
+ "dev": {
+ "cache": false
+ }
+ }
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/no-deps/package.json b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/no-deps/package.json
new file mode 100644
index 0000000..b632eef
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/no-deps/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "no-turbo",
+ "version": "0.0.0"
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/no-package/README.md b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/no-package/README.md
new file mode 100644
index 0000000..64355e7
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/no-package/README.md
@@ -0,0 +1 @@
+Nothing here
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/no-turbo/package.json b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/no-turbo/package.json
new file mode 100644
index 0000000..524df50
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/no-turbo/package.json
@@ -0,0 +1,6 @@
+{
+ "name": "no-turbo",
+ "version": "0.0.0",
+ "dependencies": {},
+ "devDependencies": {}
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/normal-workspaces-dev-install/package.json b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/normal-workspaces-dev-install/package.json
new file mode 100644
index 0000000..f5b2368
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/normal-workspaces-dev-install/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "normal-workspaces",
+ "version": "0.0.0",
+ "workspaces": [
+ "apps/*",
+ "packages/*"
+ ],
+ "dependencies": {},
+ "devDependencies": {
+ "turbo": "1.0.0"
+ }
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/normal-workspaces/package.json b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/normal-workspaces/package.json
new file mode 100644
index 0000000..6344a38
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/normal-workspaces/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "normal-workspaces",
+ "version": "0.0.0",
+ "workspaces": [
+ "apps/*",
+ "packages/*"
+ ],
+ "dependencies": {
+ "turbo": "1.0.0"
+ },
+ "devDependencies": {}
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/pnpm-workspaces-dev-install/package.json b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/pnpm-workspaces-dev-install/package.json
new file mode 100644
index 0000000..5c12f28
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/pnpm-workspaces-dev-install/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "pnpm-workspaces",
+ "version": "0.0.0",
+ "dependencies": {},
+ "devDependencies": {
+ "turbo": "1.0.0"
+ }
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/pnpm-workspaces-dev-install/pnpm-workspace.yaml b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/pnpm-workspaces-dev-install/pnpm-workspace.yaml
new file mode 100644
index 0000000..3ff5faa
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/pnpm-workspaces-dev-install/pnpm-workspace.yaml
@@ -0,0 +1,3 @@
+packages:
+ - "apps/*"
+ - "packages/*"
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/pnpm-workspaces/package.json b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/pnpm-workspaces/package.json
new file mode 100644
index 0000000..fedeb8d
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/pnpm-workspaces/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "pnpm-workspaces",
+ "version": "0.0.0",
+ "dependencies": {
+ "turbo": "1.0.0"
+ },
+ "devDependencies": {}
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/pnpm-workspaces/pnpm-workspace.yaml b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/pnpm-workspaces/pnpm-workspace.yaml
new file mode 100644
index 0000000..3ff5faa
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/pnpm-workspaces/pnpm-workspace.yaml
@@ -0,0 +1,3 @@
+packages:
+ - "apps/*"
+ - "packages/*"
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/single-package-dev-install/package.json b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/single-package-dev-install/package.json
new file mode 100644
index 0000000..38bd995
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/single-package-dev-install/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "single-package-dev-install",
+ "version": "0.0.0",
+ "dependencies": {},
+ "devDependencies": {
+ "turbo": "1.0.0"
+ }
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/single-package/package.json b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/single-package/package.json
new file mode 100644
index 0000000..0fd3453
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/single-package/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "single-package",
+ "version": "0.0.0",
+ "dependencies": {
+ "turbo": "1.0.0"
+ },
+ "devDependencies": {}
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/env-dependencies/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/env-dependencies/turbo.json
new file mode 100644
index 0000000..bb3e248
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/env-dependencies/turbo.json
@@ -0,0 +1,21 @@
+{
+ "$schema": "https://turbo.build/schema.json",
+ "globalDependencies": ["$NEXT_PUBLIC_API_KEY", "$STRIPE_API_KEY", ".env"],
+ "pipeline": {
+ "build": {
+ "outputs": [".next/**", "!.next/cache/**"],
+ "dependsOn": ["^build", "$PROD_API_KEY"]
+ },
+ "lint": {
+ "outputs": [],
+ "dependsOn": ["$IS_CI"]
+ },
+ "test": {
+ "outputs": [],
+ "dependsOn": ["$IS_CI", "test"]
+ },
+ "dev": {
+ "cache": false
+ }
+ }
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/migrated-env-dependencies/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/migrated-env-dependencies/turbo.json
new file mode 100644
index 0000000..9217af6
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/migrated-env-dependencies/turbo.json
@@ -0,0 +1,25 @@
+{
+ "$schema": "https://turbo.build/schema.json",
+ "globalDependencies": [],
+ "globalEnv": ["NEXT_PUBLIC_API_KEY", "STRIPE_API_KEY"],
+ "pipeline": {
+ "build": {
+ "dependsOn": ["^build"],
+ "env": ["PROD_API_KEY"],
+ "outputs": [".next/**", "!.next/cache/**"]
+ },
+ "dev": {
+ "cache": false
+ },
+ "lint": {
+ "dependsOn": [],
+ "env": ["IS_CI"],
+ "outputs": []
+ },
+ "test": {
+ "dependsOn": ["test"],
+ "env": ["IS_CI"],
+ "outputs": []
+ }
+ }
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/no-turbo-json/package.json b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/no-turbo-json/package.json
new file mode 100644
index 0000000..83443be
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/no-turbo-json/package.json
@@ -0,0 +1,7 @@
+{
+ "name": "no-turbo-json",
+ "version": "1.0.0",
+ "dependencies": {},
+ "devDependencies": {},
+ "packageManager": "npm@1.2.3"
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/old-config/package.json b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/old-config/package.json
new file mode 100644
index 0000000..6774d3c
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/old-config/package.json
@@ -0,0 +1,20 @@
+{
+ "name": "migrate-env-var-dependencies-old-config",
+ "version": "1.0.0",
+ "dependencies": {},
+ "devDependencies": {},
+ "packageManager": "npm@1.2.3",
+ "turbo": {
+ "pipeline": {
+ "build-one": {
+ "outputs": [
+ "foo"
+ ]
+ },
+ "build-two": {
+ "outputs": []
+ },
+ "build-three": {}
+ }
+ }
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/old-config/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/old-config/turbo.json
new file mode 100644
index 0000000..b0f6150
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/old-config/turbo.json
@@ -0,0 +1,12 @@
+{
+ "$schema": "https://turbo.build/schema.json",
+ "pipeline": {
+ "build-one": {
+ "outputs": ["foo"]
+ },
+ "build-two": {
+ "outputs": []
+ },
+ "build-three": {}
+ }
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/docs/index.js b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/docs/index.js
new file mode 100644
index 0000000..4de53f5
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/docs/index.js
@@ -0,0 +1,6 @@
+export default function docs() {
+ if (process.env.ENV_1 === undefined) {
+ return "does not exist";
+ }
+ return "exists";
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/docs/package.json b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/docs/package.json
new file mode 100644
index 0000000..82f9a44
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/docs/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "docs",
+ "version": "1.0.0"
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/docs/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/docs/turbo.json
new file mode 100644
index 0000000..a3713ef
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/docs/turbo.json
@@ -0,0 +1,9 @@
+{
+ "$schema": "https://turbo.build/schema.json",
+ "extends": ["//"],
+ "pipeline": {
+ "build": {
+ "env": ["ENV_3"]
+ }
+ }
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/web/index.js b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/web/index.js
new file mode 100644
index 0000000..bfd3ab8
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/web/index.js
@@ -0,0 +1,6 @@
+export default function web() {
+ if (!process.env.ENV_2) {
+ return "bar";
+ }
+ return "foo";
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/web/package.json b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/web/package.json
new file mode 100644
index 0000000..d8a83ed
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/web/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "web",
+ "version": "1.0.0"
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/web/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/web/turbo.json
new file mode 100644
index 0000000..dd69c31
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/web/turbo.json
@@ -0,0 +1,12 @@
+{
+ "$schema": "https://turbo.build/schema.json",
+ "extends": ["//"],
+ "pipeline": {
+ "build": {
+ // old
+ "dependsOn": ["build", "$ENV_2"],
+ // new
+ "env": ["ENV_1"]
+ }
+ }
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/package.json b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/package.json
new file mode 100644
index 0000000..c6616a6
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/package.json
@@ -0,0 +1,14 @@
+{
+ "private": true,
+ "workspaces": [
+ "apps/*",
+ "packages/*"
+ ],
+ "scripts": {
+ "build": "turbo run build"
+ },
+ "devDependencies": {
+ "turbo": "latest"
+ },
+ "packageManager": "yarn@1.22.19"
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/packages/ui/index.js b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/packages/ui/index.js
new file mode 100644
index 0000000..dee5e80
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/packages/ui/index.js
@@ -0,0 +1,6 @@
+export default function foo() {
+ if (!process.env.IS_SERVER) {
+ return "bar";
+ }
+ return "foo";
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/packages/ui/package.json b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/packages/ui/package.json
new file mode 100644
index 0000000..7cb7cf1
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/packages/ui/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "ui",
+ "version": "1.0.0"
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/packages/ui/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/packages/ui/turbo.json
new file mode 100644
index 0000000..6ce7b30
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/packages/ui/turbo.json
@@ -0,0 +1,9 @@
+{
+ "$schema": "https://turbo.build/schema.json",
+ "extends": ["//"],
+ "pipeline": {
+ "build": {
+ "dependsOn": ["$IS_SERVER"]
+ }
+ }
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/turbo.json
new file mode 100644
index 0000000..718e461
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/turbo.json
@@ -0,0 +1,21 @@
+{
+ "$schema": "https://turbo.build/schema.json",
+ "globalDependencies": ["$NEXT_PUBLIC_API_KEY", "$STRIPE_API_KEY", ".env"],
+ "pipeline": {
+ "build": {
+ "outputs": [".next/**", "!.next/cache/**"],
+ "dependsOn": ["^build", "$PROD_API_KEY"]
+ },
+ "lint": {
+ "outputs": [],
+ "dependsOn": ["$IS_TEST"]
+ },
+ "test": {
+ "outputs": [],
+ "dependsOn": ["$IS_CI", "test"]
+ },
+ "dev": {
+ "cache": false
+ }
+ }
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate/no-repo/README.md b/packages/turbo-codemod/__tests__/__fixtures__/migrate/no-repo/README.md
new file mode 100644
index 0000000..64355e7
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate/no-repo/README.md
@@ -0,0 +1 @@
+Nothing here
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate/old-turbo/package.json b/packages/turbo-codemod/__tests__/__fixtures__/migrate/old-turbo/package.json
new file mode 100644
index 0000000..62959b8
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate/old-turbo/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "no-turbo-json",
+ "version": "1.0.0",
+ "dependencies": {},
+ "devDependencies": {
+ "turbo": "1.0.0"
+ },
+ "turbo": {
+ "$schema": "https://turbo.build/schema.json",
+ "pipeline": {
+ "build": {
+ "outputs": [
+ ".next/**",
+ "!.next/cache/**"
+ ]
+ },
+ "lint": {
+ "outputs": []
+ },
+ "test": {},
+ "dev": {
+ "cache": false
+ }
+ }
+ }
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/invalid-outputs/package.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/invalid-outputs/package.json
new file mode 100644
index 0000000..6b50aac
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/invalid-outputs/package.json
@@ -0,0 +1,7 @@
+{
+ "name": "invalid-outputs",
+ "version": "1.0.0",
+ "dependencies": {},
+ "devDependencies": {},
+ "packageManager": "npm@1.2.3"
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/invalid-outputs/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/invalid-outputs/turbo.json
new file mode 100644
index 0000000..33c2b93
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/invalid-outputs/turbo.json
@@ -0,0 +1,36 @@
+{
+ "$schema": "https://turbo.build/schema.json",
+ "pipeline": {
+ "build-one": {
+ "outputs": ["foo"]
+ },
+ "build-two": {
+ "outputs": []
+ },
+ "build-three": {},
+ "garbage-in-numeric-0": {
+ "outputs": 0
+ },
+ "garbage-in-numeric": {
+ "outputs": 42
+ },
+ "garbage-in-string": {
+ "outputs": "string"
+ },
+ "garbage-in-empty-string": {
+ "outputs": ""
+ },
+ "garbage-in-null": {
+ "outputs": null
+ },
+ "garbage-in-false": {
+ "outputs": false
+ },
+ "garbage-in-true": {
+ "outputs": true
+ },
+ "garbage-in-object": {
+ "outputs": {}
+ }
+ }
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-outputs/package.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-outputs/package.json
new file mode 100644
index 0000000..4e17dc1
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-outputs/package.json
@@ -0,0 +1,7 @@
+{
+ "name": "no-outputs",
+ "version": "1.0.0",
+ "dependencies": {},
+ "devDependencies": {},
+ "packageManager": "npm@1.2.3"
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-outputs/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-outputs/turbo.json
new file mode 100644
index 0000000..f5d57fc
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-outputs/turbo.json
@@ -0,0 +1,14 @@
+{
+ "$schema": "https://turbo.build/schema.json",
+ "pipeline": {
+ "build-one": {
+ "dependsOn": ["build-two"]
+ },
+ "build-two": {
+ "cache": false
+ },
+ "build-three": {
+ "persistent": true
+ }
+ }
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-pipeline/package.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-pipeline/package.json
new file mode 100644
index 0000000..6e20fc8
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-pipeline/package.json
@@ -0,0 +1,7 @@
+{
+ "name": "no-pipeline",
+ "version": "1.0.0",
+ "dependencies": {},
+ "devDependencies": {},
+ "packageManager": "npm@1.2.3"
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-pipeline/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-pipeline/turbo.json
new file mode 100644
index 0000000..0e2d6fd
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-pipeline/turbo.json
@@ -0,0 +1,5 @@
+{
+ "$schema": "https://turbo.build/schema.json",
+ "globalDependencies": ["$NEXT_PUBLIC_API_KEY", "$STRIPE_API_KEY", ".env"],
+ "pipeline": {}
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-turbo-json/package.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-turbo-json/package.json
new file mode 100644
index 0000000..cd98334
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-turbo-json/package.json
@@ -0,0 +1,7 @@
+{
+ "name": "set-default-outputs-no-turbo-json",
+ "version": "1.0.0",
+ "dependencies": {},
+ "devDependencies": {},
+ "packageManager": "npm@1.2.3"
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/old-config/package.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/old-config/package.json
new file mode 100644
index 0000000..4c816c2
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/old-config/package.json
@@ -0,0 +1,20 @@
+{
+ "name": "set-default-outputs-old-config",
+ "version": "1.0.0",
+ "dependencies": {},
+ "devDependencies": {},
+ "packageManager": "npm@1.2.3",
+ "turbo": {
+ "pipeline": {
+ "build-one": {
+ "outputs": [
+ "foo"
+ ]
+ },
+ "build-two": {
+ "outputs": []
+ },
+ "build-three": {}
+ }
+ }
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/old-config/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/old-config/turbo.json
new file mode 100644
index 0000000..b0f6150
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/old-config/turbo.json
@@ -0,0 +1,12 @@
+{
+ "$schema": "https://turbo.build/schema.json",
+ "pipeline": {
+ "build-one": {
+ "outputs": ["foo"]
+ },
+ "build-two": {
+ "outputs": []
+ },
+ "build-three": {}
+ }
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/old-outputs/package.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/old-outputs/package.json
new file mode 100644
index 0000000..e4220ba
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/old-outputs/package.json
@@ -0,0 +1,7 @@
+{
+ "name": "old-outputs",
+ "version": "1.0.0",
+ "dependencies": {},
+ "devDependencies": {},
+ "packageManager": "npm@1.2.3"
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/old-outputs/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/old-outputs/turbo.json
new file mode 100644
index 0000000..b0f6150
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/old-outputs/turbo.json
@@ -0,0 +1,12 @@
+{
+ "$schema": "https://turbo.build/schema.json",
+ "pipeline": {
+ "build-one": {
+ "outputs": ["foo"]
+ },
+ "build-two": {
+ "outputs": []
+ },
+ "build-three": {}
+ }
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/docs/index.js b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/docs/index.js
new file mode 100644
index 0000000..4de53f5
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/docs/index.js
@@ -0,0 +1,6 @@
+export default function docs() {
+ if (process.env.ENV_1 === undefined) {
+ return "does not exist";
+ }
+ return "exists";
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/docs/package.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/docs/package.json
new file mode 100644
index 0000000..82f9a44
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/docs/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "docs",
+ "version": "1.0.0"
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/docs/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/docs/turbo.json
new file mode 100644
index 0000000..e60cdb7
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/docs/turbo.json
@@ -0,0 +1,7 @@
+{
+ "$schema": "https://turbo.build/schema.json",
+ "extends": ["//"],
+ "pipeline": {
+ "build": {}
+ }
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/web/index.js b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/web/index.js
new file mode 100644
index 0000000..bfd3ab8
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/web/index.js
@@ -0,0 +1,6 @@
+export default function web() {
+ if (!process.env.ENV_2) {
+ return "bar";
+ }
+ return "foo";
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/web/package.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/web/package.json
new file mode 100644
index 0000000..d8a83ed
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/web/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "web",
+ "version": "1.0.0"
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/web/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/web/turbo.json
new file mode 100644
index 0000000..b239cbf
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/web/turbo.json
@@ -0,0 +1,10 @@
+{
+ "$schema": "https://turbo.build/schema.json",
+ "extends": ["//"],
+ "pipeline": {
+ "build": {
+ // old
+ "outputs": []
+ }
+ }
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/package.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/package.json
new file mode 100644
index 0000000..c6616a6
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/package.json
@@ -0,0 +1,14 @@
+{
+ "private": true,
+ "workspaces": [
+ "apps/*",
+ "packages/*"
+ ],
+ "scripts": {
+ "build": "turbo run build"
+ },
+ "devDependencies": {
+ "turbo": "latest"
+ },
+ "packageManager": "yarn@1.22.19"
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/packages/ui/index.js b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/packages/ui/index.js
new file mode 100644
index 0000000..dee5e80
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/packages/ui/index.js
@@ -0,0 +1,6 @@
+export default function foo() {
+ if (!process.env.IS_SERVER) {
+ return "bar";
+ }
+ return "foo";
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/packages/ui/package.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/packages/ui/package.json
new file mode 100644
index 0000000..7cb7cf1
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/packages/ui/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "ui",
+ "version": "1.0.0"
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/packages/ui/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/packages/ui/turbo.json
new file mode 100644
index 0000000..fe51119
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/packages/ui/turbo.json
@@ -0,0 +1,7 @@
+{
+ "$schema": "https://turbo.build/schema.json",
+ "extends": ["//"],
+ "pipeline": {
+ "build-three": {}
+ }
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/turbo.json
new file mode 100644
index 0000000..b0f6150
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/turbo.json
@@ -0,0 +1,12 @@
+{
+ "$schema": "https://turbo.build/schema.json",
+ "pipeline": {
+ "build-one": {
+ "outputs": ["foo"]
+ },
+ "build-two": {
+ "outputs": []
+ },
+ "build-three": {}
+ }
+}
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/transform/basic/package.json b/packages/turbo-codemod/__tests__/__fixtures__/transform/basic/package.json
new file mode 100644
index 0000000..651edb6
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/__fixtures__/transform/basic/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "transform-basic",
+ "version": "1.0.0",
+ "dependencies": {},
+ "devDependencies": {
+ "turbo": "1.0.0"
+ }
+}
diff --git a/packages/turbo-codemod/__tests__/add-package-manager.test.ts b/packages/turbo-codemod/__tests__/add-package-manager.test.ts
new file mode 100644
index 0000000..5bde7e0
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/add-package-manager.test.ts
@@ -0,0 +1,504 @@
+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 },
+ });
+
+ 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,
+ },
+ }
+ `);
+
+ 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,
+ },
+ }
+ `);
+
+ // 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,
+ },
+ }
+ `);
+
+ 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,
+ },
+ }
+ `);
+
+ 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,
+ },
+ }
+ `);
+
+ 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,
+ },
+ }
+ `);
+
+ mockGetPackageManagerVersion.mockRestore();
+ mockGetPackageManager.mockRestore();
+ });
+
+ 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(
+ 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 });
+
+ // result should be correct
+ // result should be correct
+ expect(result.fatalError?.message).toMatch(
+ /Unable to determine package manager for .*?/
+ );
+
+ mockGetPackageManager.mockRestore();
+ });
+
+ test("errors when unable to determine package manager", () => {
+ // load the fixture for the test
+ const { root, read } = useFixture({ fixture: "no-package-manager" });
+
+ const mockGetPackageManagerVersion = jest
+ .spyOn(getPackageManagerVersion, "default")
+ .mockImplementation(() => {
+ throw new Error("package manager not supported");
+ });
+
+ // 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(mockGetPackageManagerVersion).toHaveBeenCalledTimes(1);
+
+ // result should be correct
+ expect(result.fatalError?.message).toMatch(
+ /Unable to determine package manager version for .*?/
+ );
+
+ mockGetPackageManagerVersion.mockRestore();
+ });
+
+ test("errors when unable to write json", () => {
+ // 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);
+
+ 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 = transformer({
+ root,
+ options: { force: false, dry: false, print: false },
+ });
+
+ 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(`
+ Object {
+ "package.json": Object {
+ "action": "error",
+ "additions": 1,
+ "deletions": 0,
+ "error": [Error: could not write file],
+ },
+ }
+ `);
+
+ mockWriteJsonSync.mockRestore();
+ mockGetPackageManagerVersion.mockRestore();
+ mockGetPackageManager.mockRestore();
+ });
+});
diff --git a/packages/turbo-codemod/__tests__/create-turbo-config.test.ts b/packages/turbo-codemod/__tests__/create-turbo-config.test.ts
new file mode 100644
index 0000000..8938c78
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/create-turbo-config.test.ts
@@ -0,0 +1,416 @@
+import { transformer } from "../src/transforms/create-turbo-config";
+import { setupTestFixtures } from "@turbo/test-utils";
+import fs from "fs-extra";
+
+describe("create-turbo-config", () => {
+ const { useFixture } = setupTestFixtures({
+ directory: __dirname,
+ test: "create-turbo-config",
+ });
+
+ test("package.json config exists but no turbo.json config - basic", () => {
+ // load the fixture for the test
+ const { root, read } = useFixture({ fixture: "no-turbo-json-config" });
+
+ // turbo.json should not exist
+ expect(read("turbo.json")).toBeUndefined();
+
+ // get config from package.json for comparison later
+ const turboConfig = JSON.parse(read("package.json") || "{}").turbo;
+ expect(turboConfig).toBeDefined();
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: false, print: false },
+ });
+
+ // turbo.json should now exist (and match the package.json config)
+ expect(JSON.parse(read("turbo.json") || "{}")).toEqual(turboConfig);
+
+ // result should be correct
+ expect(result.fatalError).toBeUndefined();
+ expect(result.changes).toMatchInlineSnapshot(`
+ Object {
+ "package.json": Object {
+ "action": "modified",
+ "additions": 0,
+ "deletions": 1,
+ },
+ "turbo.json": Object {
+ "action": "modified",
+ "additions": 1,
+ "deletions": 0,
+ },
+ }
+ `);
+ });
+
+ test("package.json config exists but no turbo.json config - repeat run", () => {
+ // load the fixture for the test
+ const { root, read } = useFixture({ fixture: "no-turbo-json-config" });
+
+ // turbo.json should not exist
+ expect(read("turbo.json")).toBeUndefined();
+
+ // get config from package.json for comparison later
+ const turboConfig = JSON.parse(read("package.json") || "{}").turbo;
+ expect(turboConfig).toBeDefined();
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: false, print: false },
+ });
+
+ // turbo.json should now exist (and match the package.json config)
+ expect(JSON.parse(read("turbo.json") || "{}")).toEqual(turboConfig);
+
+ // result should be correct
+ expect(result.fatalError).toBeUndefined();
+ expect(result.changes).toMatchInlineSnapshot(`
+ Object {
+ "package.json": Object {
+ "action": "modified",
+ "additions": 0,
+ "deletions": 1,
+ },
+ "turbo.json": Object {
+ "action": "modified",
+ "additions": 1,
+ "deletions": 0,
+ },
+ }
+ `);
+
+ // run the transformer
+ const repeatResult = transformer({
+ root,
+ options: { force: false, dry: false, print: false },
+ });
+ // result should be correct
+ expect(repeatResult.fatalError).toBeUndefined();
+ expect(repeatResult.changes).toMatchInlineSnapshot(`
+ Object {
+ "package.json": Object {
+ "action": "unchanged",
+ "additions": 0,
+ "deletions": 0,
+ },
+ "turbo.json": Object {
+ "action": "unchanged",
+ "additions": 0,
+ "deletions": 0,
+ },
+ }
+ `);
+ });
+
+ test("package.json config exists but no turbo.json config - dry", () => {
+ // load the fixture for the test
+ const { root, read } = useFixture({ fixture: "no-turbo-json-config" });
+
+ // turbo.json should not exist
+ expect(read("turbo.json")).toBeUndefined();
+
+ // get config from package.json for comparison later
+ const turboConfig = JSON.parse(read("package.json") || "{}").turbo;
+ expect(turboConfig).toBeDefined();
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: true, print: false },
+ });
+
+ // turbo.json still not exist (dry run)
+ expect(read("turbo.json")).toBeUndefined();
+
+ // result should be correct
+ expect(result.fatalError).toBeUndefined();
+ expect(result.changes).toMatchInlineSnapshot(`
+ Object {
+ "package.json": Object {
+ "action": "skipped",
+ "additions": 0,
+ "deletions": 1,
+ },
+ "turbo.json": Object {
+ "action": "skipped",
+ "additions": 1,
+ "deletions": 0,
+ },
+ }
+ `);
+ });
+
+ test("package.json config exists but no turbo.json config - print", () => {
+ // load the fixture for the test
+ const { root, read } = useFixture({ fixture: "no-turbo-json-config" });
+
+ // turbo.json should not exist
+ expect(read("turbo.json")).toBeUndefined();
+
+ // get config from package.json for comparison later
+ const turboConfig = JSON.parse(read("package.json") || "{}").turbo;
+ expect(turboConfig).toBeDefined();
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: false, print: true },
+ });
+
+ // turbo.json should now exist (and match the package.json config)
+ expect(JSON.parse(read("turbo.json") || "{}")).toEqual(turboConfig);
+
+ // result should be correct
+ expect(result.fatalError).toBeUndefined();
+ expect(result.changes).toMatchInlineSnapshot(`
+ Object {
+ "package.json": Object {
+ "action": "modified",
+ "additions": 0,
+ "deletions": 1,
+ },
+ "turbo.json": Object {
+ "action": "modified",
+ "additions": 1,
+ "deletions": 0,
+ },
+ }
+ `);
+ });
+
+ test("package.json config exists but no turbo.json config - dry & print", () => {
+ // load the fixture for the test
+ const { root, read } = useFixture({ fixture: "no-turbo-json-config" });
+
+ // turbo.json should not exist
+ expect(read("turbo.json")).toBeUndefined();
+
+ // get config from package.json for comparison later
+ const turboConfig = JSON.parse(read("package.json") || "{}").turbo;
+ expect(turboConfig).toBeDefined();
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: true, print: true },
+ });
+
+ // turbo.json still not exist (dry run)
+ expect(read("turbo.json")).toBeUndefined();
+
+ // result should be correct
+ expect(result.fatalError).toBeUndefined();
+ expect(result.changes).toMatchInlineSnapshot(`
+ Object {
+ "package.json": Object {
+ "action": "skipped",
+ "additions": 0,
+ "deletions": 1,
+ },
+ "turbo.json": Object {
+ "action": "skipped",
+ "additions": 1,
+ "deletions": 0,
+ },
+ }
+ `);
+ });
+
+ test("no package.json config or turbo.json file exists", () => {
+ // load the fixture for the test
+ const { root, read } = useFixture({ fixture: "no-package-json-config" });
+
+ // turbo.json should not exist
+ expect(read("turbo.json")).toBeUndefined();
+
+ // get config from package.json for comparison later
+ const packageJsonConfig = JSON.parse(read("package.json") || "{}");
+ const turboConfig = packageJsonConfig.turbo;
+ expect(turboConfig).toBeUndefined();
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: false, print: false },
+ });
+
+ // turbo.json should still not exist
+ expect(read("turbo.json")).toBeUndefined();
+
+ // make sure we didn't change the package.json
+ expect(JSON.parse(read("package.json") || "{}")).toEqual(packageJsonConfig);
+
+ // result should be correct
+ expect(result.fatalError).toBeUndefined();
+ expect(result.changes).toMatchInlineSnapshot(`
+ Object {
+ "package.json": Object {
+ "action": "unchanged",
+ "additions": 0,
+ "deletions": 0,
+ },
+ "turbo.json": Object {
+ "action": "unchanged",
+ "additions": 0,
+ "deletions": 0,
+ },
+ }
+ `);
+ });
+
+ test("no package.json file exists", () => {
+ // load the fixture for the test
+ const { root, read } = useFixture({ fixture: "no-package-json-file" });
+
+ // turbo.json should not exist
+ expect(read("turbo.json")).toBeUndefined();
+
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: false, print: false },
+ });
+
+ // turbo.json should still not exist
+ expect(read("turbo.json")).toBeUndefined();
+
+ // result should be correct
+ expect(result.fatalError?.message).toMatch(
+ /No package\.json found at .*?\. Is the path correct\?/
+ );
+ });
+
+ test("turbo.json file exists and no package.json config exists", () => {
+ // load the fixture for the test
+ const { root, read } = useFixture({ fixture: "turbo-json-config" });
+
+ // turbo.json should exist
+ expect(read("turbo.json")).toBeDefined();
+
+ // no config should exist in package.json
+ const packageJsonConfig = JSON.parse(read("package.json") || "{}");
+ const turboConfig = packageJsonConfig.turbo;
+ expect(turboConfig).toBeUndefined();
+
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: false, print: false },
+ });
+
+ // turbo.json should still exist
+ expect(read("turbo.json")).toBeDefined();
+
+ // make sure we didn't change the package.json
+ expect(JSON.parse(read("package.json") || "{}")).toEqual(packageJsonConfig);
+
+ // result should be correct
+ expect(result.fatalError).toBeUndefined();
+ expect(result.changes).toMatchInlineSnapshot(`
+ Object {
+ "package.json": Object {
+ "action": "unchanged",
+ "additions": 0,
+ "deletions": 0,
+ },
+ "turbo.json": Object {
+ "action": "unchanged",
+ "additions": 0,
+ "deletions": 0,
+ },
+ }
+ `);
+ });
+
+ test("turbo.json file exists and package.json config exists", () => {
+ // load the fixture for the test
+ const { root, read } = useFixture({ fixture: "both-configs" });
+
+ // turbo.json should exist
+ const turboJsonConfig = JSON.parse(read("turbo.json") || "{}");
+ expect(turboJsonConfig.pipeline).toBeDefined();
+
+ // no config should exist in package.json
+ const packageJsonConfig = JSON.parse(read("package.json") || "{}");
+ const turboConfig = JSON.parse(read("package.json") || "{}").turbo;
+ expect(turboConfig).toBeDefined();
+
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: false, print: false },
+ });
+
+ // make sure we didn't change the package.json
+ expect(JSON.parse(read("package.json") || "{}")).toEqual(packageJsonConfig);
+
+ // make sure we didn't change the turbo.json
+ expect(JSON.parse(read("turbo.json") || "{}")).toEqual(turboJsonConfig);
+
+ // result should be correct
+ expect(result.fatalError?.message).toBeUndefined();
+ expect(result.changes).toMatchInlineSnapshot(`
+ Object {
+ "package.json": Object {
+ "action": "unchanged",
+ "additions": 0,
+ "deletions": 0,
+ },
+ "turbo.json": Object {
+ "action": "unchanged",
+ "additions": 0,
+ "deletions": 0,
+ },
+ }
+ `);
+ });
+
+ test("errors when unable to write json", () => {
+ // load the fixture for the test
+ const { root, read } = useFixture({ fixture: "no-turbo-json-config" });
+
+ // turbo.json should not exist
+ expect(read("turbo.json")).toBeUndefined();
+
+ // get config from package.json for comparison later
+ const turboConfig = JSON.parse(read("package.json") || "{}").turbo;
+ expect(turboConfig).toBeDefined();
+
+ const mockWriteJsonSync = jest
+ .spyOn(fs, "writeJsonSync")
+ .mockImplementation(() => {
+ throw new Error("could not write file");
+ });
+
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: false, print: false },
+ });
+
+ // turbo.json should still not exist (error writing)
+ expect(read("turbo.json")).toBeUndefined();
+
+ // result should be correct
+ expect(result.fatalError).toBeDefined();
+ expect(result.fatalError?.message).toMatch(
+ "Encountered an error while transforming files"
+ );
+ expect(result.changes).toMatchInlineSnapshot(`
+ Object {
+ "package.json": Object {
+ "action": "error",
+ "additions": 0,
+ "deletions": 1,
+ "error": [Error: could not write file],
+ },
+ "turbo.json": Object {
+ "action": "error",
+ "additions": 1,
+ "deletions": 0,
+ "error": [Error: could not write file],
+ },
+ }
+ `);
+
+ mockWriteJsonSync.mockRestore();
+ });
+});
diff --git a/packages/turbo-codemod/__tests__/get-turbo-upgrade-command.test.ts b/packages/turbo-codemod/__tests__/get-turbo-upgrade-command.test.ts
new file mode 100644
index 0000000..1015589
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/get-turbo-upgrade-command.test.ts
@@ -0,0 +1,576 @@
+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 = [
+ // 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"],
+ // 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"],
+ // 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",
+ ],
+ // 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"],
+ // 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",
+ ],
+ // 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"],
+ // 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",
+ ],
+ // 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"],
+ // 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",
+ ],
+ // 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"],
+];
+
+const GLOBAL_INSTALL_COMMANDS = [
+ // 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",
+ ],
+ // 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",
+ ],
+ // 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",
+ ],
+ // 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",
+ ],
+ // 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",
+ ],
+];
+
+describe("get-turbo-upgrade-command", () => {
+ const { useFixture } = setupTestFixtures({
+ directory: __dirname,
+ test: "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,
+ packageManager,
+ packageManagerVersion,
+ fixture,
+ expectedUpgradeCommand
+ ) => {
+ const { root } = useFixture({
+ fixture,
+ });
+
+ 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 mockedGetPackageManagerVersion = jest
+ .spyOn(getPackageManagerVersion, "default")
+ .mockReturnValue(packageManagerVersion);
+ const mockedGetPackageManager = jest
+ .spyOn(getPackageManager, "default")
+ .mockReturnValue(packageManager as getPackageManager.PackageManager);
+
+ // get the command
+ const upgradeCommand = getTurboUpgradeCommand({
+ directory: root,
+ to: turboVersion === "latest" ? undefined : turboVersion,
+ });
+
+ expect(upgradeCommand).toEqual(expectedUpgradeCommand);
+
+ mockedExec.mockRestore();
+ mockedGetPackageManager.mockRestore();
+ mockedGetPackageManagerVersion.mockRestore();
+ }
+ );
+
+ test.each(GLOBAL_INSTALL_COMMANDS)(
+ "returns correct upgrade command for global install of turbo@%s using %s@%s (fixture: %s)",
+ (
+ turboVersion,
+ packageManager,
+ packageManagerVersion,
+ fixture,
+ expectedUpgradeCommand
+ ) => {
+ const { root } = useFixture({
+ fixture,
+ });
+
+ const mockedExec = jest
+ .spyOn(utils, "exec")
+ .mockImplementation((command: string) => {
+ if (command === "turbo bin") {
+ return `/global/${packageManager}/bin/turbo`;
+ }
+ if (command.includes(packageManager)) {
+ return `/global/${packageManager}/bin`;
+ }
+ });
+ const mockedGetPackageManagerVersion = jest
+ .spyOn(getPackageManagerVersion, "default")
+ .mockReturnValue(packageManagerVersion);
+ const mockedGetPackageManager = jest
+ .spyOn(getPackageManager, "default")
+ .mockReturnValue(packageManager as getPackageManager.PackageManager);
+
+ // get the command
+ const upgradeCommand = getTurboUpgradeCommand({
+ directory: root,
+ to: turboVersion === "latest" ? undefined : turboVersion,
+ });
+
+ expect(upgradeCommand).toEqual(expectedUpgradeCommand);
+
+ mockedExec.mockRestore();
+ mockedGetPackageManager.mockRestore();
+ mockedGetPackageManagerVersion.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;
+ }
+ });
+
+ 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();
+ });
+
+ test("fails gracefully if turbo cannot be found in package.json", () => {
+ const { root } = useFixture({
+ fixture: "no-turbo",
+ });
+
+ 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 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();
+ });
+
+ test("fails gracefully if package.json has no deps or devDeps", () => {
+ const { root } = useFixture({
+ fixture: "no-deps",
+ });
+
+ 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 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();
+ });
+
+ test("fails gracefully if can't find packageManager", () => {
+ const { root } = useFixture({
+ fixture: "no-deps",
+ });
+
+ 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 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();
+ });
+});
diff --git a/packages/turbo-codemod/__tests__/migrate-env-var-dependencies.test.ts b/packages/turbo-codemod/__tests__/migrate-env-var-dependencies.test.ts
new file mode 100644
index 0000000..fbc5d8d
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/migrate-env-var-dependencies.test.ts
@@ -0,0 +1,758 @@
+import merge from "deepmerge";
+import {
+ hasLegacyEnvVarDependencies,
+ migratePipeline,
+ migrateConfig,
+ transformer,
+} from "../src/transforms/migrate-env-var-dependencies";
+import { setupTestFixtures } from "@turbo/test-utils";
+import type { Schema } from "@turbo/types";
+
+const getTestTurboConfig = (override: Schema = { pipeline: {} }): Schema => {
+ const config = {
+ $schema: "./docs/public/schema.json",
+ globalDependencies: ["$GLOBAL_ENV_KEY"],
+ pipeline: {
+ test: {
+ outputs: ["coverage/**/*"],
+ dependsOn: ["^build"],
+ },
+ lint: {
+ outputs: [],
+ },
+ dev: {
+ cache: false,
+ },
+ build: {
+ outputs: ["dist/**/*", ".next/**/*", "!.next/cache/**"],
+ dependsOn: ["^build", "$TASK_ENV_KEY", "$ANOTHER_ENV_KEY"],
+ },
+ },
+ };
+
+ return merge(config, override, {
+ arrayMerge: (_, sourceArray) => sourceArray,
+ });
+};
+
+describe("migrate-env-var-dependencies", () => {
+ describe("hasLegacyEnvVarDependencies - utility", () => {
+ it("finds env keys in legacy turbo.json - has keys", async () => {
+ const config = getTestTurboConfig();
+ const { hasKeys, envVars } = hasLegacyEnvVarDependencies(config);
+ expect(hasKeys).toEqual(true);
+ expect(envVars).toMatchInlineSnapshot(`
+ Array [
+ "$GLOBAL_ENV_KEY",
+ "$TASK_ENV_KEY",
+ "$ANOTHER_ENV_KEY",
+ ]
+ `);
+ });
+
+ it("finds env keys in legacy turbo.json - multiple pipeline keys", async () => {
+ const config = getTestTurboConfig({
+ pipeline: { test: { dependsOn: ["$MY_ENV"] } },
+ });
+ const { hasKeys, envVars } = hasLegacyEnvVarDependencies(config);
+ expect(hasKeys).toEqual(true);
+ expect(envVars).toMatchInlineSnapshot(`
+ Array [
+ "$GLOBAL_ENV_KEY",
+ "$MY_ENV",
+ "$TASK_ENV_KEY",
+ "$ANOTHER_ENV_KEY",
+ ]
+ `);
+ });
+
+ it("finds env keys in legacy turbo.json - no keys", async () => {
+ // override to exclude keys
+ const config = getTestTurboConfig({
+ globalDependencies: [],
+ pipeline: { build: { dependsOn: [] } },
+ });
+ const { hasKeys, envVars } = hasLegacyEnvVarDependencies(config);
+ expect(hasKeys).toEqual(false);
+ expect(envVars).toMatchInlineSnapshot(`Array []`);
+ });
+
+ it("finds env keys in turbo.json - no global", async () => {
+ const { hasKeys, envVars } = hasLegacyEnvVarDependencies({
+ pipeline: { build: { dependsOn: ["$cool"] } },
+ });
+ expect(hasKeys).toEqual(true);
+ expect(envVars).toMatchInlineSnapshot(`
+ Array [
+ "$cool",
+ ]
+ `);
+ });
+ });
+
+ describe("migratePipeline - utility", () => {
+ it("migrates pipeline with env var dependencies", async () => {
+ const config = getTestTurboConfig();
+ const { build } = config.pipeline;
+ const pipeline = migratePipeline(build);
+ expect(pipeline).toHaveProperty("env");
+ expect(pipeline?.env).toMatchInlineSnapshot(`
+ Array [
+ "TASK_ENV_KEY",
+ "ANOTHER_ENV_KEY",
+ ]
+ `);
+ expect(pipeline?.dependsOn).toMatchInlineSnapshot(`
+ Array [
+ "^build",
+ ]
+ `);
+ });
+
+ it("migrates pipeline with no env var dependencies", async () => {
+ const config = getTestTurboConfig();
+ const { test } = config.pipeline;
+ const pipeline = migratePipeline(test);
+ expect(pipeline.env).toBeUndefined();
+ expect(pipeline?.dependsOn).toMatchInlineSnapshot(`
+ Array [
+ "^build",
+ ]
+ `);
+ });
+
+ it("migrates pipeline with existing env key", async () => {
+ const config = getTestTurboConfig({
+ pipeline: { test: { env: ["$MY_ENV"], dependsOn: ["^build"] } },
+ });
+ const { test } = config.pipeline;
+ const pipeline = migratePipeline(test);
+ expect(pipeline).toHaveProperty("env");
+ expect(pipeline?.env).toMatchInlineSnapshot(`
+ Array [
+ "$MY_ENV",
+ ]
+ `);
+ expect(pipeline?.dependsOn).toMatchInlineSnapshot(`
+ Array [
+ "^build",
+ ]
+ `);
+ });
+
+ it("migrates pipeline with incomplete env key", async () => {
+ const config = getTestTurboConfig({
+ pipeline: {
+ test: { env: ["$MY_ENV"], dependsOn: ["^build", "$SUPER_COOL"] },
+ },
+ });
+ const { test } = config.pipeline;
+ const pipeline = migratePipeline(test);
+ expect(pipeline).toHaveProperty("env");
+ expect(pipeline?.env).toMatchInlineSnapshot(`
+ Array [
+ "$MY_ENV",
+ "SUPER_COOL",
+ ]
+ `);
+ expect(pipeline?.dependsOn).toMatchInlineSnapshot(`
+ Array [
+ "^build",
+ ]
+ `);
+ });
+
+ it("migrates pipeline with duplicate env keys", async () => {
+ const config = getTestTurboConfig({
+ pipeline: {
+ test: { env: ["$MY_ENV"], dependsOn: ["^build", "$MY_ENV"] },
+ },
+ });
+ const { test } = config.pipeline;
+ const pipeline = migratePipeline(test);
+ expect(pipeline).toHaveProperty("env");
+ expect(pipeline?.env).toMatchInlineSnapshot(`
+ Array [
+ "$MY_ENV",
+ "MY_ENV",
+ ]
+ `);
+ expect(pipeline?.dependsOn).toMatchInlineSnapshot(`
+ Array [
+ "^build",
+ ]
+ `);
+ });
+ });
+
+ describe("migrateConfig - utility", () => {
+ it("migrates config with env var dependencies", async () => {
+ const config = getTestTurboConfig();
+ const pipeline = migrateConfig(config);
+ expect(pipeline).toMatchInlineSnapshot(`
+ Object {
+ "$schema": "./docs/public/schema.json",
+ "globalEnv": Array [
+ "GLOBAL_ENV_KEY",
+ ],
+ "pipeline": Object {
+ "build": Object {
+ "dependsOn": Array [
+ "^build",
+ ],
+ "env": Array [
+ "TASK_ENV_KEY",
+ "ANOTHER_ENV_KEY",
+ ],
+ "outputs": Array [
+ "dist/**/*",
+ ".next/**/*",
+ "!.next/cache/**",
+ ],
+ },
+ "dev": Object {
+ "cache": false,
+ },
+ "lint": Object {
+ "outputs": Array [],
+ },
+ "test": Object {
+ "dependsOn": Array [
+ "^build",
+ ],
+ "outputs": Array [
+ "coverage/**/*",
+ ],
+ },
+ },
+ }
+ `);
+ });
+
+ it("migrates config with no env var dependencies", async () => {
+ const config = getTestTurboConfig({
+ globalDependencies: [],
+ pipeline: {
+ build: { dependsOn: ["^build"] },
+ },
+ });
+ const pipeline = migrateConfig(config);
+ expect(pipeline).toMatchInlineSnapshot(`
+ Object {
+ "$schema": "./docs/public/schema.json",
+ "pipeline": Object {
+ "build": Object {
+ "dependsOn": Array [
+ "^build",
+ ],
+ "outputs": Array [
+ "dist/**/*",
+ ".next/**/*",
+ "!.next/cache/**",
+ ],
+ },
+ "dev": Object {
+ "cache": false,
+ },
+ "lint": Object {
+ "outputs": Array [],
+ },
+ "test": Object {
+ "dependsOn": Array [
+ "^build",
+ ],
+ "outputs": Array [
+ "coverage/**/*",
+ ],
+ },
+ },
+ }
+ `);
+ });
+
+ it("migrates config with inconsistent config", async () => {
+ const config = getTestTurboConfig({
+ pipeline: {
+ test: { env: ["$MY_ENV"], dependsOn: ["^build", "$SUPER_COOL"] },
+ },
+ });
+ const pipeline = migrateConfig(config);
+ expect(pipeline).toMatchInlineSnapshot(`
+ Object {
+ "$schema": "./docs/public/schema.json",
+ "globalEnv": Array [
+ "GLOBAL_ENV_KEY",
+ ],
+ "pipeline": Object {
+ "build": Object {
+ "dependsOn": Array [
+ "^build",
+ ],
+ "env": Array [
+ "TASK_ENV_KEY",
+ "ANOTHER_ENV_KEY",
+ ],
+ "outputs": Array [
+ "dist/**/*",
+ ".next/**/*",
+ "!.next/cache/**",
+ ],
+ },
+ "dev": Object {
+ "cache": false,
+ },
+ "lint": Object {
+ "outputs": Array [],
+ },
+ "test": Object {
+ "dependsOn": Array [
+ "^build",
+ ],
+ "env": Array [
+ "$MY_ENV",
+ "SUPER_COOL",
+ ],
+ "outputs": Array [
+ "coverage/**/*",
+ ],
+ },
+ },
+ }
+ `);
+ });
+
+ it("migrates config with duplicate env keys", async () => {
+ const config = getTestTurboConfig({
+ pipeline: {
+ test: { env: ["$MY_ENV"], dependsOn: ["^build", "$MY_ENV"] },
+ },
+ });
+ const pipeline = migrateConfig(config);
+ expect(pipeline).toMatchInlineSnapshot(`
+ Object {
+ "$schema": "./docs/public/schema.json",
+ "globalEnv": Array [
+ "GLOBAL_ENV_KEY",
+ ],
+ "pipeline": Object {
+ "build": Object {
+ "dependsOn": Array [
+ "^build",
+ ],
+ "env": Array [
+ "TASK_ENV_KEY",
+ "ANOTHER_ENV_KEY",
+ ],
+ "outputs": Array [
+ "dist/**/*",
+ ".next/**/*",
+ "!.next/cache/**",
+ ],
+ },
+ "dev": Object {
+ "cache": false,
+ },
+ "lint": Object {
+ "outputs": Array [],
+ },
+ "test": Object {
+ "dependsOn": Array [
+ "^build",
+ ],
+ "env": Array [
+ "$MY_ENV",
+ "MY_ENV",
+ ],
+ "outputs": Array [
+ "coverage/**/*",
+ ],
+ },
+ },
+ }
+ `);
+ });
+ });
+
+ describe("transform", () => {
+ const { useFixture } = setupTestFixtures({
+ directory: __dirname,
+ test: "migrate-env-var-dependencies",
+ });
+
+ it("migrates turbo.json env var dependencies - basic", async () => {
+ // load the fixture for the test
+ const { root, read } = useFixture({
+ fixture: "env-dependencies",
+ });
+
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: false, print: false },
+ });
+
+ expect(JSON.parse(read("turbo.json") || "{}")).toStrictEqual({
+ $schema: "https://turbo.build/schema.json",
+ globalDependencies: [".env"],
+ globalEnv: ["NEXT_PUBLIC_API_KEY", "STRIPE_API_KEY"],
+ pipeline: {
+ build: {
+ dependsOn: ["^build"],
+ env: ["PROD_API_KEY"],
+ outputs: [".next/**", "!.next/cache/**"],
+ },
+ dev: {
+ cache: false,
+ },
+ lint: {
+ dependsOn: [],
+ env: ["IS_CI"],
+ outputs: [],
+ },
+ test: {
+ dependsOn: ["test"],
+ env: ["IS_CI"],
+ outputs: [],
+ },
+ },
+ });
+
+ expect(result.fatalError).toBeUndefined();
+ expect(result.changes).toMatchInlineSnapshot(`
+ Object {
+ "turbo.json": Object {
+ "action": "modified",
+ "additions": 4,
+ "deletions": 4,
+ },
+ }
+ `);
+ });
+
+ it("migrates turbo.json env var dependencies - workspace configs", async () => {
+ // load the fixture for the test
+ const { root, readJson } = useFixture({
+ fixture: "workspace-configs",
+ });
+
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: false, print: false },
+ });
+
+ expect(readJson("turbo.json") || "{}").toStrictEqual({
+ $schema: "https://turbo.build/schema.json",
+ globalDependencies: [".env"],
+ globalEnv: ["NEXT_PUBLIC_API_KEY", "STRIPE_API_KEY"],
+ pipeline: {
+ build: {
+ dependsOn: ["^build"],
+ env: ["PROD_API_KEY"],
+ outputs: [".next/**", "!.next/cache/**"],
+ },
+ dev: {
+ cache: false,
+ },
+ lint: {
+ dependsOn: [],
+ env: ["IS_TEST"],
+ outputs: [],
+ },
+ test: {
+ dependsOn: ["test"],
+ env: ["IS_CI"],
+ outputs: [],
+ },
+ },
+ });
+
+ expect(readJson("apps/web/turbo.json") || "{}").toStrictEqual({
+ $schema: "https://turbo.build/schema.json",
+ extends: ["//"],
+ pipeline: {
+ build: {
+ // old
+ dependsOn: ["build"],
+ // new
+ env: ["ENV_1", "ENV_2"],
+ },
+ },
+ });
+
+ expect(readJson("packages/ui/turbo.json") || "{}").toStrictEqual({
+ $schema: "https://turbo.build/schema.json",
+ extends: ["//"],
+ pipeline: {
+ build: {
+ dependsOn: [],
+ env: ["IS_SERVER"],
+ },
+ },
+ });
+
+ expect(result.fatalError).toBeUndefined();
+ expect(result.changes).toMatchInlineSnapshot(`
+ Object {
+ "apps/web/turbo.json": Object {
+ "action": "modified",
+ "additions": 1,
+ "deletions": 0,
+ },
+ "packages/ui/turbo.json": Object {
+ "action": "modified",
+ "additions": 1,
+ "deletions": 1,
+ },
+ "turbo.json": Object {
+ "action": "modified",
+ "additions": 4,
+ "deletions": 4,
+ },
+ }
+ `);
+ });
+
+ it("migrates turbo.json env var dependencies - repeat run", async () => {
+ // load the fixture for the test
+ const { root, read } = useFixture({
+ fixture: "env-dependencies",
+ });
+
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: false, print: false },
+ });
+
+ expect(JSON.parse(read("turbo.json") || "{}")).toStrictEqual({
+ $schema: "https://turbo.build/schema.json",
+ globalDependencies: [".env"],
+ globalEnv: ["NEXT_PUBLIC_API_KEY", "STRIPE_API_KEY"],
+ pipeline: {
+ build: {
+ dependsOn: ["^build"],
+ env: ["PROD_API_KEY"],
+ outputs: [".next/**", "!.next/cache/**"],
+ },
+ dev: {
+ cache: false,
+ },
+ lint: {
+ dependsOn: [],
+ env: ["IS_CI"],
+ outputs: [],
+ },
+ test: {
+ dependsOn: ["test"],
+ env: ["IS_CI"],
+ outputs: [],
+ },
+ },
+ });
+
+ expect(result.fatalError).toBeUndefined();
+ expect(result.changes).toMatchInlineSnapshot(`
+ Object {
+ "turbo.json": Object {
+ "action": "modified",
+ "additions": 4,
+ "deletions": 4,
+ },
+ }
+ `);
+
+ // run the transformer
+ const repeatResult = transformer({
+ root,
+ options: { force: false, dry: false, print: false },
+ });
+
+ expect(repeatResult.fatalError).toBeUndefined();
+ expect(repeatResult.changes).toMatchInlineSnapshot(`
+ Object {
+ "turbo.json": Object {
+ "action": "unchanged",
+ "additions": 0,
+ "deletions": 0,
+ },
+ }
+ `);
+ });
+
+ it("migrates turbo.json env var dependencies - dry", async () => {
+ // load the fixture for the test
+ const { root, read } = useFixture({
+ fixture: "env-dependencies",
+ });
+
+ const turboJson = JSON.parse(read("turbo.json") || "{}");
+
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: true, print: false },
+ });
+
+ // make sure it didn't change
+ expect(JSON.parse(read("turbo.json") || "{}")).toEqual(turboJson);
+
+ expect(result.fatalError).toBeUndefined();
+ expect(result.changes).toMatchInlineSnapshot(`
+ Object {
+ "turbo.json": Object {
+ "action": "skipped",
+ "additions": 4,
+ "deletions": 4,
+ },
+ }
+ `);
+ });
+
+ it("migrates turbo.json env var dependencies - print", async () => {
+ // load the fixture for the test
+ const { root, read } = useFixture({
+ fixture: "env-dependencies",
+ });
+
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: false, print: true },
+ });
+
+ expect(JSON.parse(read("turbo.json") || "{}")).toStrictEqual({
+ $schema: "https://turbo.build/schema.json",
+ globalEnv: ["NEXT_PUBLIC_API_KEY", "STRIPE_API_KEY"],
+ globalDependencies: [".env"],
+ pipeline: {
+ build: {
+ dependsOn: ["^build"],
+ env: ["PROD_API_KEY"],
+ outputs: [".next/**", "!.next/cache/**"],
+ },
+ dev: {
+ cache: false,
+ },
+ lint: {
+ dependsOn: [],
+ env: ["IS_CI"],
+ outputs: [],
+ },
+ test: {
+ dependsOn: ["test"],
+ env: ["IS_CI"],
+ outputs: [],
+ },
+ },
+ });
+
+ expect(result.fatalError).toBeUndefined();
+ expect(result.changes).toMatchInlineSnapshot(`
+ Object {
+ "turbo.json": Object {
+ "action": "modified",
+ "additions": 4,
+ "deletions": 4,
+ },
+ }
+ `);
+ });
+
+ it("migrates turbo.json env var dependencies - dry & print", async () => {
+ // load the fixture for the test
+ const { root, read } = useFixture({
+ fixture: "env-dependencies",
+ });
+
+ const turboJson = JSON.parse(read("turbo.json") || "{}");
+
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: true, print: true },
+ });
+
+ // make sure it didn't change
+ expect(JSON.parse(read("turbo.json") || "{}")).toEqual(turboJson);
+
+ expect(result.fatalError).toBeUndefined();
+ expect(result.changes).toMatchInlineSnapshot(`
+ Object {
+ "turbo.json": Object {
+ "action": "skipped",
+ "additions": 4,
+ "deletions": 4,
+ },
+ }
+ `);
+ });
+
+ it("does not change turbo.json if already migrated", async () => {
+ // load the fixture for the test
+ const { root, read } = useFixture({
+ fixture: "migrated-env-dependencies",
+ });
+
+ const turboJson = JSON.parse(read("turbo.json") || "{}");
+
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: false, print: false },
+ });
+
+ expect(JSON.parse(read("turbo.json") || "{}")).toEqual(turboJson);
+
+ expect(result.fatalError).toBeUndefined();
+ expect(result.changes).toMatchInlineSnapshot(`
+ Object {
+ "turbo.json": Object {
+ "action": "unchanged",
+ "additions": 0,
+ "deletions": 0,
+ },
+ }
+ `);
+ });
+
+ it("errors if no turbo.json can be found", async () => {
+ // load the fixture for the test
+ const { root, read } = useFixture({
+ fixture: "no-turbo-json",
+ });
+
+ expect(read("turbo.json")).toBeUndefined();
+
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: false, print: false },
+ });
+
+ expect(read("turbo.json")).toBeUndefined();
+ expect(result.fatalError).toBeDefined();
+ expect(result.fatalError?.message).toMatch(
+ /No turbo\.json found at .*?\. Is the path correct\?/
+ );
+ });
+
+ it("errors if package.json config exists and has not been migrated", async () => {
+ // load the fixture for the test
+ const { root } = useFixture({
+ fixture: "old-config",
+ });
+
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: false, print: false },
+ });
+
+ expect(result.fatalError).toBeDefined();
+ expect(result.fatalError?.message).toMatch(
+ 'turbo" key detected in package.json. Run `npx @turbo/codemod transform create-turbo-config` first'
+ );
+ });
+ });
+});
diff --git a/packages/turbo-codemod/__tests__/migrate.test.ts b/packages/turbo-codemod/__tests__/migrate.test.ts
new file mode 100644
index 0000000..652ea41
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/migrate.test.ts
@@ -0,0 +1,761 @@
+import { MigrateCommandArgument } from "../src/commands";
+import migrate from "../src/commands/migrate";
+import { setupTestFixtures, spyExit } from "@turbo/test-utils";
+import childProcess from "child_process";
+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";
+
+describe("migrate", () => {
+ const mockExit = spyExit();
+ const { useFixture } = setupTestFixtures({
+ directory: __dirname,
+ test: "migrate",
+ });
+
+ it("migrates from 1.0.0 to 1.7.0", async () => {
+ const { root, readJson } = useFixture({
+ fixture: "old-turbo",
+ });
+
+ const packageManager = "pnpm";
+ const packageManagerVersion = "1.2.3";
+
+ // setup mocks
+ const mockedCheckGitStatus = jest
+ .spyOn(checkGitStatus, "default")
+ .mockReturnValue(undefined);
+ const mockedGetCurrentVersion = jest
+ .spyOn(getCurrentVersion, "default")
+ .mockReturnValue("1.0.0");
+ const mockedGetLatestVersion = jest
+ .spyOn(getLatestVersion, "default")
+ .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, {
+ force: false,
+ dry: false,
+ print: false,
+ install: false,
+ });
+
+ expect(readJson("package.json")).toStrictEqual({
+ dependencies: {},
+ devDependencies: {
+ turbo: "1.0.0",
+ },
+ name: "no-turbo-json",
+ packageManager: "pnpm@1.2.3",
+ version: "1.0.0",
+ });
+ expect(readJson("turbo.json")).toStrictEqual({
+ $schema: "https://turbo.build/schema.json",
+ pipeline: {
+ build: {
+ outputs: [".next/**", "!.next/cache/**"],
+ },
+ dev: {
+ cache: false,
+ },
+ lint: {},
+ test: {
+ outputs: ["dist/**", "build/**"],
+ },
+ },
+ });
+
+ // verify mocks were called
+ expect(mockedCheckGitStatus).toHaveBeenCalled();
+ expect(mockedGetCurrentVersion).toHaveBeenCalled();
+ expect(mockedGetLatestVersion).toHaveBeenCalled();
+ expect(mockedGetTurboUpgradeCommand).toHaveBeenCalled();
+ expect(mockedGetPackageManagerVersion).toHaveBeenCalled();
+ expect(mockedGetWorkspaceImplementation).toHaveBeenCalled();
+
+ // restore mocks
+ mockedCheckGitStatus.mockRestore();
+ mockedGetCurrentVersion.mockRestore();
+ mockedGetLatestVersion.mockRestore();
+ mockedGetTurboUpgradeCommand.mockRestore();
+ mockedGetPackageManagerVersion.mockRestore();
+ mockedGetWorkspaceImplementation.mockRestore();
+ });
+
+ it("migrates from 1.0.0 to 1.2.0 (dry run)", async () => {
+ const { root, readJson } = useFixture({
+ fixture: "old-turbo",
+ });
+
+ const packageManager = "pnpm";
+ const packageManagerVersion = "1.2.3";
+
+ // setup mocks
+ const mockedCheckGitStatus = jest
+ .spyOn(checkGitStatus, "default")
+ .mockReturnValue(undefined);
+ const mockedGetCurrentVersion = jest
+ .spyOn(getCurrentVersion, "default")
+ .mockReturnValue("1.0.0");
+ const mockedGetLatestVersion = jest
+ .spyOn(getLatestVersion, "default")
+ .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);
+
+ const packageJson = readJson("package.json");
+ const turboJson = readJson("turbo.json");
+
+ await migrate(root as MigrateCommandArgument, {
+ force: false,
+ dry: true,
+ print: false,
+ install: true,
+ });
+
+ // make sure nothing changed
+ expect(readJson("package.json")).toStrictEqual(packageJson);
+ expect(readJson("turbo.json")).toStrictEqual(turboJson);
+
+ // verify mocks were called
+ expect(mockedCheckGitStatus).not.toHaveBeenCalled();
+ expect(mockedGetCurrentVersion).toHaveBeenCalled();
+ expect(mockedGetLatestVersion).toHaveBeenCalled();
+ expect(mockedGetTurboUpgradeCommand).toHaveBeenCalled();
+ expect(mockedGetPackageManagerVersion).toHaveBeenCalled();
+ expect(mockedGetWorkspaceImplementation).toHaveBeenCalled();
+
+ // restore mocks
+ mockedCheckGitStatus.mockRestore();
+ mockedGetCurrentVersion.mockRestore();
+ mockedGetLatestVersion.mockRestore();
+ mockedGetTurboUpgradeCommand.mockRestore();
+ mockedGetPackageManagerVersion.mockRestore();
+ mockedGetWorkspaceImplementation.mockRestore();
+ });
+
+ it("next version can be passed as an option", async () => {
+ const { root, readJson } = useFixture({
+ fixture: "old-turbo",
+ });
+
+ const packageManager = "pnpm";
+ const packageManagerVersion = "1.2.3";
+
+ // setup mocks
+ const mockedCheckGitStatus = jest
+ .spyOn(checkGitStatus, "default")
+ .mockReturnValue(undefined);
+ const mockedGetCurrentVersion = jest
+ .spyOn(getCurrentVersion, "default")
+ .mockReturnValue("1.0.0");
+ const mockedGetLatestVersion = jest
+ .spyOn(getLatestVersion, "default")
+ .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, {
+ force: false,
+ dry: false,
+ print: false,
+ install: false,
+ to: "1.7.0",
+ });
+
+ expect(readJson("package.json")).toStrictEqual({
+ dependencies: {},
+ devDependencies: {
+ turbo: "1.0.0",
+ },
+ name: "no-turbo-json",
+ packageManager: "pnpm@1.2.3",
+ version: "1.0.0",
+ });
+ expect(readJson("turbo.json")).toStrictEqual({
+ $schema: "https://turbo.build/schema.json",
+ pipeline: {
+ build: {
+ outputs: [".next/**", "!.next/cache/**"],
+ },
+ dev: {
+ cache: false,
+ },
+ test: {
+ outputs: ["dist/**", "build/**"],
+ },
+ lint: {},
+ },
+ });
+
+ // verify mocks were called
+ expect(mockedCheckGitStatus).toHaveBeenCalled();
+ expect(mockedGetCurrentVersion).toHaveBeenCalled();
+ expect(mockedGetLatestVersion).toHaveBeenCalled();
+ expect(mockedGetTurboUpgradeCommand).toHaveBeenCalled();
+ expect(mockedGetPackageManagerVersion).toHaveBeenCalled();
+ expect(mockedGetWorkspaceImplementation).toHaveBeenCalled();
+
+ // restore mocks
+ mockedCheckGitStatus.mockRestore();
+ mockedGetCurrentVersion.mockRestore();
+ mockedGetLatestVersion.mockRestore();
+ mockedGetTurboUpgradeCommand.mockRestore();
+ mockedGetPackageManagerVersion.mockRestore();
+ mockedGetWorkspaceImplementation.mockRestore();
+ });
+
+ it("current version can be passed as an option", async () => {
+ const { root, readJson } = useFixture({
+ fixture: "old-turbo",
+ });
+
+ const packageManager = "pnpm";
+ const packageManagerVersion = "1.2.3";
+
+ // setup mocks
+ const mockedCheckGitStatus = jest
+ .spyOn(checkGitStatus, "default")
+ .mockReturnValue(undefined);
+ const mockedGetLatestVersion = jest
+ .spyOn(getLatestVersion, "default")
+ .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, {
+ force: false,
+ dry: false,
+ print: false,
+ install: false,
+ from: "1.0.0",
+ });
+
+ expect(readJson("package.json")).toStrictEqual({
+ dependencies: {},
+ devDependencies: {
+ turbo: "1.0.0",
+ },
+ name: "no-turbo-json",
+ packageManager: "pnpm@1.2.3",
+ version: "1.0.0",
+ });
+ expect(readJson("turbo.json")).toStrictEqual({
+ $schema: "https://turbo.build/schema.json",
+ pipeline: {
+ build: {
+ outputs: [".next/**", "!.next/cache/**"],
+ },
+ dev: {
+ cache: false,
+ },
+ lint: {},
+ test: {
+ outputs: ["dist/**", "build/**"],
+ },
+ },
+ });
+
+ // verify mocks were called
+ expect(mockedCheckGitStatus).toHaveBeenCalled();
+ expect(mockedGetLatestVersion).toHaveBeenCalled();
+ expect(mockedGetTurboUpgradeCommand).toHaveBeenCalled();
+ expect(mockedGetPackageManagerVersion).toHaveBeenCalled();
+ expect(mockedGetWorkspaceImplementation).toHaveBeenCalled();
+
+ // restore mocks
+ mockedCheckGitStatus.mockRestore();
+ mockedGetLatestVersion.mockRestore();
+ mockedGetTurboUpgradeCommand.mockRestore();
+ mockedGetPackageManagerVersion.mockRestore();
+ mockedGetWorkspaceImplementation.mockRestore();
+ });
+
+ it("exits if the current version is the same as the new version", async () => {
+ const { root } = useFixture({
+ fixture: "old-turbo",
+ });
+
+ // setup mocks
+ const mockedCheckGitStatus = jest
+ .spyOn(checkGitStatus, "default")
+ .mockReturnValue(undefined);
+ const mockedGetCurrentVersion = jest
+ .spyOn(getCurrentVersion, "default")
+ .mockReturnValue("1.7.0");
+ const mockedGetLatestVersion = jest
+ .spyOn(getLatestVersion, "default")
+ .mockResolvedValue("1.7.0");
+
+ await migrate(root as MigrateCommandArgument, {
+ force: false,
+ dry: false,
+ print: false,
+ install: false,
+ });
+
+ expect(mockExit.exit).toHaveBeenCalledWith(0);
+
+ // verify mocks were called
+ expect(mockedCheckGitStatus).toHaveBeenCalled();
+ expect(mockedGetCurrentVersion).toHaveBeenCalled();
+ expect(mockedGetLatestVersion).toHaveBeenCalled();
+
+ // restore mocks
+ mockedCheckGitStatus.mockRestore();
+ mockedGetCurrentVersion.mockRestore();
+ mockedGetLatestVersion.mockRestore();
+ });
+
+ it("continues when migration doesn't require codemods", async () => {
+ const { root } = useFixture({
+ fixture: "old-turbo",
+ });
+
+ // setup mocks
+ const mockedCheckGitStatus = jest
+ .spyOn(checkGitStatus, "default")
+ .mockReturnValue(undefined);
+ const mockedGetCurrentVersion = jest
+ .spyOn(getCurrentVersion, "default")
+ .mockReturnValue("1.3.0");
+ const mockedGetLatestVersion = jest
+ .spyOn(getLatestVersion, "default")
+ .mockResolvedValue("1.3.1");
+ const mockedGetTurboUpgradeCommand = jest
+ .spyOn(getTurboUpgradeCommand, "default")
+ .mockReturnValue("npm install turbo@1.3.1");
+ const mockExecSync = jest
+ .spyOn(childProcess, "execSync")
+ .mockReturnValue("installed");
+
+ await migrate(root as MigrateCommandArgument, {
+ force: false,
+ dry: false,
+ print: false,
+ install: true,
+ });
+
+ // verify mocks were called
+ expect(mockedCheckGitStatus).toHaveBeenCalled();
+ expect(mockedGetCurrentVersion).toHaveBeenCalled();
+ expect(mockedGetLatestVersion).toHaveBeenCalled();
+ expect(mockedGetTurboUpgradeCommand).toHaveBeenCalled();
+ expect(mockExecSync).toHaveBeenCalledWith("npm install turbo@1.3.1", {
+ cwd: root,
+ });
+
+ // restore mocks
+ mockedCheckGitStatus.mockRestore();
+ mockedGetCurrentVersion.mockRestore();
+ mockedGetLatestVersion.mockRestore();
+ mockedGetTurboUpgradeCommand.mockRestore();
+ mockExecSync.mockRestore();
+ });
+
+ it("installs the correct turbo version", async () => {
+ const { root, readJson } = useFixture({
+ fixture: "old-turbo",
+ });
+
+ const packageManager = "pnpm";
+ const packageManagerVersion = "1.2.3";
+
+ // setup mocks
+ const mockedCheckGitStatus = jest
+ .spyOn(checkGitStatus, "default")
+ .mockReturnValue(undefined);
+ const mockedGetCurrentVersion = jest
+ .spyOn(getCurrentVersion, "default")
+ .mockReturnValue("1.0.0");
+ const mockedGetLatestVersion = jest
+ .spyOn(getLatestVersion, "default")
+ .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);
+ const mockExecSync = jest
+ .spyOn(childProcess, "execSync")
+ .mockReturnValue("installed");
+
+ await migrate(root as MigrateCommandArgument, {
+ force: false,
+ dry: false,
+ print: false,
+ install: true,
+ });
+
+ expect(readJson("package.json")).toStrictEqual({
+ dependencies: {},
+ devDependencies: {
+ turbo: "1.0.0",
+ },
+ name: "no-turbo-json",
+ packageManager: "pnpm@1.2.3",
+ version: "1.0.0",
+ });
+ expect(readJson("turbo.json")).toStrictEqual({
+ $schema: "https://turbo.build/schema.json",
+ pipeline: {
+ build: {
+ outputs: [".next/**", "!.next/cache/**"],
+ },
+ dev: {
+ cache: false,
+ },
+ lint: {},
+ test: {
+ outputs: ["dist/**", "build/**"],
+ },
+ },
+ });
+
+ // verify mocks were called
+ expect(mockedCheckGitStatus).toHaveBeenCalled();
+ expect(mockedGetCurrentVersion).toHaveBeenCalled();
+ expect(mockedGetLatestVersion).toHaveBeenCalled();
+ expect(mockedGetTurboUpgradeCommand).toHaveBeenCalled();
+ expect(mockedGetPackageManagerVersion).toHaveBeenCalled();
+ expect(mockedGetWorkspaceImplementation).toHaveBeenCalled();
+ expect(mockExecSync).toHaveBeenCalled();
+ expect(mockExecSync).toHaveBeenCalledWith("pnpm install -g turbo@1.7.0", {
+ cwd: root,
+ });
+
+ // restore mocks
+ mockedCheckGitStatus.mockRestore();
+ mockedGetCurrentVersion.mockRestore();
+ mockedGetLatestVersion.mockRestore();
+ mockedGetTurboUpgradeCommand.mockRestore();
+ mockedGetPackageManagerVersion.mockRestore();
+ mockedGetWorkspaceImplementation.mockRestore();
+ mockExecSync.mockRestore();
+ });
+
+ it("fails gracefully when the correct upgrade command cannot be found", async () => {
+ const { root, readJson } = useFixture({
+ fixture: "old-turbo",
+ });
+
+ const packageManager = "pnpm";
+ const packageManagerVersion = "1.2.3";
+
+ // setup mocks
+ const mockedCheckGitStatus = jest
+ .spyOn(checkGitStatus, "default")
+ .mockReturnValue(undefined);
+ const mockedGetCurrentVersion = jest
+ .spyOn(getCurrentVersion, "default")
+ .mockReturnValue("1.0.0");
+ const mockedGetLatestVersion = jest
+ .spyOn(getLatestVersion, "default")
+ .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);
+ const mockExecSync = jest
+ .spyOn(childProcess, "execSync")
+ .mockReturnValue("installed");
+
+ await migrate(root as MigrateCommandArgument, {
+ force: false,
+ dry: false,
+ print: false,
+ install: true,
+ });
+
+ expect(readJson("package.json")).toStrictEqual({
+ dependencies: {},
+ devDependencies: {
+ turbo: "1.0.0",
+ },
+ name: "no-turbo-json",
+ packageManager: "pnpm@1.2.3",
+ version: "1.0.0",
+ });
+ expect(readJson("turbo.json")).toStrictEqual({
+ $schema: "https://turbo.build/schema.json",
+ pipeline: {
+ build: {
+ outputs: [".next/**", "!.next/cache/**"],
+ },
+ dev: {
+ cache: false,
+ },
+ lint: {},
+ test: {
+ outputs: ["dist/**", "build/**"],
+ },
+ },
+ });
+
+ expect(mockExit.exit).toHaveBeenCalledWith(1);
+
+ // verify mocks were called
+ expect(mockedCheckGitStatus).toHaveBeenCalled();
+ expect(mockedGetCurrentVersion).toHaveBeenCalled();
+ expect(mockedGetLatestVersion).toHaveBeenCalled();
+ expect(mockedGetTurboUpgradeCommand).toHaveBeenCalled();
+ expect(mockedGetPackageManagerVersion).toHaveBeenCalled();
+ expect(mockedGetWorkspaceImplementation).toHaveBeenCalled();
+ expect(mockExecSync).not.toHaveBeenCalled();
+
+ // restore mocks
+ mockedCheckGitStatus.mockRestore();
+ mockedGetCurrentVersion.mockRestore();
+ mockedGetLatestVersion.mockRestore();
+ mockedGetTurboUpgradeCommand.mockRestore();
+ mockedGetPackageManagerVersion.mockRestore();
+ mockedGetWorkspaceImplementation.mockRestore();
+ mockExecSync.mockRestore();
+ });
+
+ it("exits if current version is not passed and cannot be inferred", async () => {
+ const { root } = useFixture({
+ fixture: "old-turbo",
+ });
+
+ // setup mocks
+ const mockedCheckGitStatus = jest
+ .spyOn(checkGitStatus, "default")
+ .mockReturnValue(undefined);
+ const mockedGetCurrentVersion = jest
+ .spyOn(getCurrentVersion, "default")
+ .mockReturnValue(undefined);
+
+ await migrate(root as MigrateCommandArgument, {
+ force: false,
+ dry: false,
+ print: false,
+ install: false,
+ });
+
+ expect(mockExit.exit).toHaveBeenCalledWith(1);
+
+ // verify mocks were called
+ expect(mockedCheckGitStatus).toHaveBeenCalled();
+ expect(mockedGetCurrentVersion).toHaveBeenCalled();
+
+ // restore mocks
+ mockedCheckGitStatus.mockRestore();
+ mockedGetCurrentVersion.mockRestore();
+ });
+
+ it("exits if latest version is not passed and cannot be inferred", async () => {
+ const { root } = useFixture({
+ fixture: "old-turbo",
+ });
+
+ // setup mocks
+ const mockedCheckGitStatus = jest
+ .spyOn(checkGitStatus, "default")
+ .mockReturnValue(undefined);
+ const mockedGetCurrentVersion = jest
+ .spyOn(getCurrentVersion, "default")
+ .mockReturnValue("1.5.0");
+ const mockedGetLatestVersion = jest
+ .spyOn(getLatestVersion, "default")
+ .mockResolvedValue(undefined);
+
+ await migrate(root as MigrateCommandArgument, {
+ force: false,
+ dry: false,
+ print: false,
+ install: false,
+ });
+
+ expect(mockExit.exit).toHaveBeenCalledWith(1);
+
+ // verify mocks were called
+ expect(mockedCheckGitStatus).toHaveBeenCalled();
+ expect(mockedGetCurrentVersion).toHaveBeenCalled();
+ expect(mockedGetLatestVersion).toHaveBeenCalled();
+
+ // restore mocks
+ mockedCheckGitStatus.mockRestore();
+ mockedGetCurrentVersion.mockRestore();
+ mockedGetLatestVersion.mockRestore();
+ });
+
+ it("exits if latest version throws", async () => {
+ const { root } = useFixture({
+ fixture: "old-turbo",
+ });
+
+ // setup mocks
+ const mockedCheckGitStatus = jest
+ .spyOn(checkGitStatus, "default")
+ .mockReturnValue(undefined);
+ const mockedGetCurrentVersion = jest
+ .spyOn(getCurrentVersion, "default")
+ .mockReturnValue("1.5.0");
+ const mockedGetLatestVersion = jest
+ .spyOn(getLatestVersion, "default")
+ .mockRejectedValue(new Error("failed to fetch version"));
+
+ await migrate(root as MigrateCommandArgument, {
+ force: false,
+ dry: false,
+ print: false,
+ install: false,
+ });
+
+ expect(mockExit.exit).toHaveBeenCalledWith(1);
+
+ // verify mocks were called
+ expect(mockedCheckGitStatus).toHaveBeenCalled();
+ expect(mockedGetCurrentVersion).toHaveBeenCalled();
+ expect(mockedGetLatestVersion).toHaveBeenCalled();
+
+ // restore mocks
+ mockedCheckGitStatus.mockRestore();
+ mockedGetCurrentVersion.mockRestore();
+ mockedGetLatestVersion.mockRestore();
+ });
+
+ it("exits if any transforms encounter an error", async () => {
+ const { root } = useFixture({
+ fixture: "old-turbo",
+ });
+
+ const packageManager = "pnpm";
+ const packageManagerVersion = "1.2.3";
+
+ // setup mocks
+ const mockedCheckGitStatus = jest
+ .spyOn(checkGitStatus, "default")
+ .mockReturnValue(undefined);
+ const mockedGetCurrentVersion = jest
+ .spyOn(getCurrentVersion, "default")
+ .mockReturnValue("1.0.0");
+ 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, {
+ force: false,
+ dry: true,
+ print: false,
+ install: true,
+ });
+
+ expect(mockExit.exit).toHaveBeenCalledWith(1);
+
+ // verify mocks were called
+ expect(mockedCheckGitStatus).not.toHaveBeenCalled();
+ expect(mockedGetCurrentVersion).toHaveBeenCalled();
+ expect(mockedGetLatestVersion).toHaveBeenCalled();
+ expect(mockedGetPackageManagerVersion).toHaveBeenCalled();
+ expect(mockedGetWorkspaceImplementation).toHaveBeenCalled();
+
+ // restore mocks
+ mockedCheckGitStatus.mockRestore();
+ mockedGetCurrentVersion.mockRestore();
+ mockedGetLatestVersion.mockRestore();
+ mockedGetPackageManagerVersion.mockRestore();
+ mockedGetWorkspaceImplementation.mockRestore();
+ });
+
+ it("exits if invalid directory is passed", async () => {
+ const { root } = useFixture({
+ fixture: "old-turbo",
+ });
+
+ // setup mocks
+ const mockedCheckGitStatus = jest
+ .spyOn(checkGitStatus, "default")
+ .mockReturnValue(undefined);
+
+ await migrate("~/path/that/does/not/exist" as MigrateCommandArgument, {
+ force: false,
+ dry: false,
+ print: false,
+ install: false,
+ });
+
+ expect(mockExit.exit).toHaveBeenCalledWith(1);
+
+ // verify mocks were called
+ expect(mockedCheckGitStatus).toHaveBeenCalled();
+
+ // restore mocks
+ mockedCheckGitStatus.mockRestore();
+ });
+
+ it("exits if directory with no repo is passed", async () => {
+ const { root } = useFixture({
+ fixture: "no-repo",
+ });
+
+ // setup mocks
+ const mockedCheckGitStatus = jest
+ .spyOn(checkGitStatus, "default")
+ .mockReturnValue(undefined);
+
+ await migrate(root as MigrateCommandArgument, {
+ force: false,
+ dry: false,
+ print: false,
+ install: false,
+ });
+
+ expect(mockExit.exit).toHaveBeenCalledWith(1);
+
+ // verify mocks were called
+ expect(mockedCheckGitStatus).toHaveBeenCalled();
+
+ // restore mocks
+ mockedCheckGitStatus.mockRestore();
+ });
+});
diff --git a/packages/turbo-codemod/__tests__/set-default-outputs.test.ts b/packages/turbo-codemod/__tests__/set-default-outputs.test.ts
new file mode 100644
index 0000000..4a71fa7
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/set-default-outputs.test.ts
@@ -0,0 +1,391 @@
+import { transformer } from "../src/transforms/set-default-outputs";
+import { setupTestFixtures } from "@turbo/test-utils";
+
+describe("set-default-outputs", () => {
+ const { useFixture } = setupTestFixtures({
+ directory: __dirname,
+ test: "set-default-outputs",
+ });
+ it("migrates turbo.json outputs - basic", async () => {
+ // load the fixture for the test
+ const { root, read } = useFixture({
+ fixture: "old-outputs",
+ });
+
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: false, print: false },
+ });
+
+ expect(JSON.parse(read("turbo.json") || "{}")).toStrictEqual({
+ $schema: "https://turbo.build/schema.json",
+ pipeline: {
+ "build-one": {
+ outputs: ["foo"],
+ },
+ "build-two": {},
+ "build-three": {
+ outputs: ["dist/**", "build/**"],
+ },
+ },
+ });
+
+ expect(result.fatalError).toBeUndefined();
+ expect(result.changes).toMatchInlineSnapshot(`
+ Object {
+ "turbo.json": Object {
+ "action": "modified",
+ "additions": 2,
+ "deletions": 1,
+ },
+ }
+ `);
+ });
+
+ it("migrates turbo.json outputs - workspace configs", async () => {
+ // load the fixture for the test
+ const { root, readJson } = useFixture({
+ fixture: "workspace-configs",
+ });
+
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: false, print: false },
+ });
+
+ expect(readJson("turbo.json") || "{}").toStrictEqual({
+ $schema: "https://turbo.build/schema.json",
+ pipeline: {
+ "build-one": {
+ outputs: ["foo"],
+ },
+ "build-two": {},
+ "build-three": {
+ outputs: ["dist/**", "build/**"],
+ },
+ },
+ });
+
+ expect(readJson("apps/docs/turbo.json") || "{}").toStrictEqual({
+ $schema: "https://turbo.build/schema.json",
+ extends: ["//"],
+ pipeline: {
+ build: {
+ outputs: ["dist/**", "build/**"],
+ },
+ },
+ });
+
+ expect(readJson("apps/web/turbo.json") || "{}").toStrictEqual({
+ $schema: "https://turbo.build/schema.json",
+ extends: ["//"],
+ pipeline: {
+ build: {},
+ },
+ });
+
+ expect(readJson("packages/ui/turbo.json") || "{}").toStrictEqual({
+ $schema: "https://turbo.build/schema.json",
+ extends: ["//"],
+ pipeline: {
+ "build-three": {
+ outputs: ["dist/**", "build/**"],
+ },
+ },
+ });
+
+ expect(result.fatalError).toBeUndefined();
+ expect(result.changes).toMatchInlineSnapshot(`
+ Object {
+ "apps/docs/turbo.json": Object {
+ "action": "modified",
+ "additions": 1,
+ "deletions": 1,
+ },
+ "apps/web/turbo.json": Object {
+ "action": "modified",
+ "additions": 1,
+ "deletions": 0,
+ },
+ "packages/ui/turbo.json": Object {
+ "action": "modified",
+ "additions": 1,
+ "deletions": 1,
+ },
+ "turbo.json": Object {
+ "action": "modified",
+ "additions": 2,
+ "deletions": 1,
+ },
+ }
+ `);
+ });
+
+ it("migrates turbo.json outputs - dry", async () => {
+ // load the fixture for the test
+ const { root, read } = useFixture({
+ fixture: "old-outputs",
+ });
+
+ const turboJson = JSON.parse(read("turbo.json") || "{}");
+
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: true, print: false },
+ });
+
+ // make sure it didn't change
+ expect(JSON.parse(read("turbo.json") || "{}")).toEqual(turboJson);
+
+ expect(result.fatalError).toBeUndefined();
+ expect(result.changes).toMatchInlineSnapshot(`
+ Object {
+ "turbo.json": Object {
+ "action": "skipped",
+ "additions": 2,
+ "deletions": 1,
+ },
+ }
+ `);
+ });
+
+ it("migrates turbo.json outputs - print", async () => {
+ // load the fixture for the test
+ const { root, read } = useFixture({
+ fixture: "old-outputs",
+ });
+
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: false, print: true },
+ });
+
+ expect(JSON.parse(read("turbo.json") || "{}")).toStrictEqual({
+ $schema: "https://turbo.build/schema.json",
+ pipeline: {
+ "build-one": {
+ outputs: ["foo"],
+ },
+ "build-two": {},
+ "build-three": {
+ outputs: ["dist/**", "build/**"],
+ },
+ },
+ });
+
+ expect(result.fatalError).toBeUndefined();
+ expect(result.changes).toMatchInlineSnapshot(`
+ Object {
+ "turbo.json": Object {
+ "action": "modified",
+ "additions": 2,
+ "deletions": 1,
+ },
+ }
+ `);
+ });
+
+ it("migrates turbo.json outputs - dry & print", async () => {
+ // load the fixture for the test
+ const { root, read } = useFixture({
+ fixture: "old-outputs",
+ });
+
+ const turboJson = JSON.parse(read("turbo.json") || "{}");
+
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: true, print: false },
+ });
+
+ // make sure it didn't change
+ expect(JSON.parse(read("turbo.json") || "{}")).toEqual(turboJson);
+
+ expect(result.fatalError).toBeUndefined();
+ expect(result.changes).toMatchInlineSnapshot(`
+ Object {
+ "turbo.json": Object {
+ "action": "skipped",
+ "additions": 2,
+ "deletions": 1,
+ },
+ }
+ `);
+ });
+
+ it("migrates turbo.json outputs - invalid", async () => {
+ // load the fixture for the test
+ const { root, read } = useFixture({
+ fixture: "invalid-outputs",
+ });
+
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: false, print: false },
+ });
+
+ expect(JSON.parse(read("turbo.json") || "{}")).toStrictEqual({
+ $schema: "https://turbo.build/schema.json",
+ pipeline: {
+ "build-one": {
+ outputs: ["foo"],
+ },
+ "build-two": {},
+ "build-three": {
+ outputs: ["dist/**", "build/**"],
+ },
+ "garbage-in-numeric-0": {
+ outputs: ["dist/**", "build/**"],
+ },
+ "garbage-in-numeric": {
+ outputs: 42,
+ },
+ "garbage-in-string": {
+ outputs: "string",
+ },
+ "garbage-in-empty-string": {
+ outputs: ["dist/**", "build/**"],
+ },
+ "garbage-in-null": {
+ outputs: ["dist/**", "build/**"],
+ },
+ "garbage-in-false": {
+ outputs: ["dist/**", "build/**"],
+ },
+ "garbage-in-true": {
+ outputs: true,
+ },
+ "garbage-in-object": {
+ outputs: {},
+ },
+ },
+ });
+
+ expect(result.fatalError).toBeUndefined();
+ expect(result.changes).toMatchInlineSnapshot(`
+ Object {
+ "turbo.json": Object {
+ "action": "modified",
+ "additions": 6,
+ "deletions": 5,
+ },
+ }
+ `);
+ });
+
+ it("migrates turbo.json outputs - config with no pipeline", async () => {
+ // load the fixture for the test
+ const { root, read } = useFixture({
+ fixture: "no-pipeline",
+ });
+
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: false, print: false },
+ });
+
+ expect(JSON.parse(read("turbo.json") || "{}")).toStrictEqual({
+ $schema: "https://turbo.build/schema.json",
+ globalDependencies: ["$NEXT_PUBLIC_API_KEY", "$STRIPE_API_KEY", ".env"],
+ pipeline: {},
+ });
+
+ expect(result.fatalError).toBeUndefined();
+ expect(result.changes).toMatchInlineSnapshot(`
+ Object {
+ "turbo.json": Object {
+ "action": "unchanged",
+ "additions": 0,
+ "deletions": 0,
+ },
+ }
+ `);
+ });
+
+ it("migrates turbo.json outputs - config with no outputs", async () => {
+ // load the fixture for the test
+ const { root, read } = useFixture({
+ fixture: "no-outputs",
+ });
+
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: false, print: false },
+ });
+
+ expect(JSON.parse(read("turbo.json") || "{}")).toStrictEqual({
+ $schema: "https://turbo.build/schema.json",
+ pipeline: {
+ "build-one": {
+ dependsOn: ["build-two"],
+ outputs: ["dist/**", "build/**"],
+ },
+ "build-two": {
+ cache: false,
+ },
+ "build-three": {
+ persistent: true,
+ outputs: ["dist/**", "build/**"],
+ },
+ },
+ });
+
+ expect(result.fatalError).toBeUndefined();
+ expect(result.changes).toMatchInlineSnapshot(`
+ Object {
+ "turbo.json": Object {
+ "action": "modified",
+ "additions": 2,
+ "deletions": 0,
+ },
+ }
+ `);
+ });
+
+ it("errors if no turbo.json can be found", async () => {
+ // load the fixture for the test
+ const { root, read } = useFixture({
+ fixture: "no-turbo-json",
+ });
+
+ expect(read("turbo.json")).toBeUndefined();
+
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: false, print: false },
+ });
+
+ expect(read("turbo.json")).toBeUndefined();
+ expect(result.fatalError).toBeDefined();
+ expect(result.fatalError?.message).toMatch(
+ /No turbo\.json found at .*?\. Is the path correct\?/
+ );
+ });
+
+ it("errors if package.json config exists and has not been migrated", async () => {
+ // load the fixture for the test
+ const { root } = useFixture({
+ fixture: "old-config",
+ });
+
+ // run the transformer
+ const result = transformer({
+ root,
+ options: { force: false, dry: false, print: false },
+ });
+
+ expect(result.fatalError).toBeDefined();
+ expect(result.fatalError?.message).toMatch(
+ 'turbo" key detected in package.json. Run `npx @turbo/codemod transform create-turbo-config` first'
+ );
+ });
+});
diff --git a/packages/turbo-codemod/__tests__/transform.test.ts b/packages/turbo-codemod/__tests__/transform.test.ts
new file mode 100644
index 0000000..abd015d
--- /dev/null
+++ b/packages/turbo-codemod/__tests__/transform.test.ts
@@ -0,0 +1,172 @@
+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";
+
+describe("transform", () => {
+ const mockExit = spyExit();
+ const { useFixture } = setupTestFixtures({
+ directory: __dirname,
+ test: "transform",
+ });
+
+ it("runs the selected transform", async () => {
+ const { root, readJson } = useFixture({
+ fixture: "basic",
+ });
+
+ const packageManager = "pnpm";
+ const packageManagerVersion = "1.2.3";
+
+ // setup mocks
+ 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,
+ }
+ );
+
+ expect(readJson("package.json")).toStrictEqual({
+ dependencies: {},
+ devDependencies: {
+ turbo: "1.0.0",
+ },
+ name: "transform-basic",
+ packageManager: "pnpm@1.2.3",
+ version: "1.0.0",
+ });
+
+ // verify mocks were called
+ expect(mockedCheckGitStatus).toHaveBeenCalled();
+ expect(mockedGetPackageManagerVersion).toHaveBeenCalled();
+ expect(mockedGetPackageManager).toHaveBeenCalled();
+
+ // restore mocks
+ mockedCheckGitStatus.mockRestore();
+ mockedGetPackageManagerVersion.mockRestore();
+ mockedGetPackageManager.mockRestore();
+ });
+
+ it("runs the selected transform - dry & print", async () => {
+ const { root, readJson } = useFixture({
+ fixture: "basic",
+ });
+
+ const packageManager = "pnpm";
+ const packageManagerVersion = "1.2.3";
+
+ // setup mocks
+ 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,
+ }
+ );
+
+ expect(readJson("package.json")).toStrictEqual({
+ dependencies: {},
+ devDependencies: {
+ turbo: "1.0.0",
+ },
+ name: "transform-basic",
+ version: "1.0.0",
+ });
+
+ // verify mocks were called
+ expect(mockedCheckGitStatus).not.toHaveBeenCalled();
+ expect(mockedGetPackageManagerVersion).toHaveBeenCalled();
+ expect(mockedGetPackageManager).toHaveBeenCalled();
+
+ // restore mocks
+ mockedCheckGitStatus.mockRestore();
+ mockedGetPackageManagerVersion.mockRestore();
+ mockedGetPackageManager.mockRestore();
+ });
+
+ it("lists transforms", async () => {
+ const { root } = useFixture({
+ fixture: "basic",
+ });
+
+ await transform(
+ "add-package-manager" as MigrateCommandArgument,
+ root as MigrateCommandArgument,
+ {
+ list: true,
+ force: false,
+ dry: false,
+ print: false,
+ }
+ );
+
+ expect(mockExit.exit).toHaveBeenCalledWith(0);
+ });
+
+ it("exits on invalid transform", async () => {
+ const { root } = useFixture({
+ fixture: "basic",
+ });
+
+ await transform(
+ "not-a-real-option" as MigrateCommandArgument,
+ root as MigrateCommandArgument,
+ {
+ list: false,
+ force: false,
+ dry: false,
+ print: false,
+ }
+ );
+
+ expect(mockExit.exit).toHaveBeenCalledWith(1);
+ });
+
+ it("exits on invalid directory", async () => {
+ const { root } = useFixture({
+ fixture: "basic",
+ });
+
+ await transform(
+ "add-package-manager" as MigrateCommandArgument,
+ "~/path/that/does/not/exist" as MigrateCommandArgument,
+ {
+ list: false,
+ force: false,
+ dry: false,
+ print: false,
+ }
+ );
+
+ expect(mockExit.exit).toHaveBeenCalledWith(1);
+ });
+});