From ff35bda31977b03e444585947f200963492341b4 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 10 Sep 2026 19:14:13 +0000 Subject: [PATCH 1/8] build: adopt Turborepo's experimental Go workspace support Turborepo can now read `go.work` directly and treat its members as packages, so the Go modules no longer need `package.json` wrappers to join the task graph. - Enable `futureFlags.experimentalGoWorkspaces`, which turns `apps/api` and `packages/core` into packages named by module path and derives their dependency edge from the `go.mod` require. - Delete `apps/api/package.json` and `apps/api/turbo.json`; the hand-written `$TURBO_ROOT$` inputs that kept the API's cache honest are now inferred from the module graph. Trim `packages/core/package.json` to the manifest changesets needs. - Map the repo's script names onto the synthesized task names: `check` runs `lint` (`go vet`) and `fix` runs `format`, replacing the per-package `check`/`fix` tasks that no longer have implementors. - Override the `go` toolchain's `format` command with `gofmt -l -w .`. The synthesized `go fmt ./apps/api/... ./packages/core/...` fails at a workspace root because `go fmt` does not accept cross-module patterns in workspace mode. - Drop `bin/**` from the build outputs: the API binary is now `apps/api/dist/api`, and `dev` uses `go run .` instead of Air. This requires turbo 2.10.13-canary.4 or later; the flag does not exist in any stable release yet. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01ULAxKvHY6VD6aPxmsop4ni --- .changeset/config.json | 2 +- .changeset/nervous-cars-dance.md | 4 ++++ README.md | 16 ++++++++++---- apps/api/package.json | 15 ------------- apps/api/turbo.json | 36 -------------------------------- bun.lock | 24 +++++++-------------- package.json | 6 +++--- packages/core/package.json | 8 +------ turbo.json | 20 +++++++++++------- 9 files changed, 42 insertions(+), 89 deletions(-) create mode 100644 .changeset/nervous-cars-dance.md delete mode 100644 apps/api/package.json delete mode 100644 apps/api/turbo.json diff --git a/.changeset/config.json b/.changeset/config.json index 879d2df..49783a3 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -7,5 +7,5 @@ "access": "public", "baseBranch": "main", "updateInternalDependencies": "patch", - "ignore": ["web", "@pigo/api", "@pigo/typescript-config"] + "ignore": ["web", "@pigo/typescript-config"] } diff --git a/.changeset/nervous-cars-dance.md b/.changeset/nervous-cars-dance.md new file mode 100644 index 0000000..05f0c09 --- /dev/null +++ b/.changeset/nervous-cars-dance.md @@ -0,0 +1,4 @@ +--- +--- + +Move the Go modules onto Turborepo's experimental Go workspace support. Build tooling only; no change to the published library. diff --git a/README.md b/README.md index 3d913d6..53c4726 100644 --- a/README.md +++ b/README.md @@ -10,13 +10,13 @@ A minimal-dependency, minimal-configuration image optimizer with a Go API backen - **Go API backend**: A REST server built on the standard library's JPEG and PNG codecs, with Catmull-Rom interpolation for resizing. - **Next.js frontend**: Drag-and-drop uploads, clipboard paste, ZIP downloads, and a before/after comparison slider. - **Zero heavy runtime dependencies**: The backend builds and runs without C libraries, GraphicsMagick, or libvips. -- **Turborepo monorepo**: Bun workspaces handle builds and package management. +- **Turborepo monorepo**: Bun workspaces and `go.work` modules are both first-class Turborepo packages. ## Tech stack - **Backend**: Go (1.26+), [`chi`](https://github.com/go-chi/chi) router, `golang.org/x/image/draw` - **Frontend**: Next.js (16.3+), React 19, Tailwind CSS v4, Lucide Icons, JSZip -- **Tooling**: Bun, Turborepo, [Ultracite](https://github.com/PunGrumpy/ultracite) (Oxlint + Oxfmt), Air (Go hot-reloading) +- **Tooling**: Bun, Turborepo (with experimental Go workspace support), [Ultracite](https://github.com/PunGrumpy/ultracite) (Oxlint + Oxfmt) ## Project structure @@ -28,9 +28,18 @@ pigo/ ├── packages/ │ ├── core/ # Go image decoding, encoding, and resizing │ └── typescript-config/ # Shared TypeScript configs -└── package.json # Workspace configuration +├── go.work # Go workspace; its members are Turborepo packages +└── package.json # Bun workspace configuration ``` +The Go modules (`apps/api`, `packages/core`) have no `package.json`. Turborepo reads them from `go.work` and synthesizes their tasks, so they are addressed by module path: + +```bash +turbo run build --filter=github.com/PunGrumpy/pigo/apps/api +``` + +Repository-wide Go tasks (`test`, `lint`, `format`) run in the synthetic `go-workspace` package. + ## Getting started ### Prerequisites @@ -39,7 +48,6 @@ Make sure you have the following installed: - [Go](https://go.dev/doc/install) (1.26 or later) - [Bun](https://bun.sh) (1.3.14 or later) -- [Air](https://github.com/air-verse/air), optional, for API hot-reloading ### Installation diff --git a/apps/api/package.json b/apps/api/package.json deleted file mode 100644 index bb924be..0000000 --- a/apps/api/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "@pigo/api", - "private": true, - "scripts": { - "build": "go build -o bin/pigo-api .", - "dev": "air .", - "test": "go test ./...", - "check": "go vet ./...", - "fix": "go fmt ./...", - "typecheck": "go vet ./..." - }, - "dependencies": { - "@pigo/core": "workspace:*" - } -} diff --git a/apps/api/turbo.json b/apps/api/turbo.json deleted file mode 100644 index dd6d4cb..0000000 --- a/apps/api/turbo.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "$schema": "https://turbo.build/schema.json", - "extends": ["//"], - "tasks": { - "build": { - "inputs": [ - "$TURBO_DEFAULT$", - "$TURBO_ROOT$/packages/core/**/*.go", - "$TURBO_ROOT$/packages/core/go.mod", - "$TURBO_ROOT$/packages/core/go.sum", - "$TURBO_ROOT$/go.work", - "$TURBO_ROOT$/go.work.sum" - ] - }, - "test": { - "inputs": [ - "$TURBO_DEFAULT$", - "$TURBO_ROOT$/packages/core/**/*.go", - "$TURBO_ROOT$/packages/core/go.mod", - "$TURBO_ROOT$/packages/core/go.sum", - "$TURBO_ROOT$/go.work", - "$TURBO_ROOT$/go.work.sum" - ] - }, - "typecheck": { - "inputs": [ - "$TURBO_DEFAULT$", - "$TURBO_ROOT$/packages/core/**/*.go", - "$TURBO_ROOT$/packages/core/go.mod", - "$TURBO_ROOT$/packages/core/go.sum", - "$TURBO_ROOT$/go.work", - "$TURBO_ROOT$/go.work.sum" - ] - } - } -} diff --git a/bun.lock b/bun.lock index aba5e3b..fb8c5cd 100644 --- a/bun.lock +++ b/bun.lock @@ -10,17 +10,11 @@ "oxfmt": "^0.62.0", "oxlint": "^1.77.0", "react-doctor": "^0.9.5", - "turbo": "^2.10.8", + "turbo": "2.10.13-canary.4", "typescript": "^7.0.2", "ultracite": "7.10.1", }, }, - "apps/api": { - "name": "@pigo/api", - "dependencies": { - "@pigo/core": "workspace:*", - }, - }, "apps/web": { "name": "web", "version": "0.1.0", @@ -546,8 +540,6 @@ "@oxlint/binding-win32-x64-msvc": ["@oxlint/binding-win32-x64-msvc@1.77.0", "", { "os": "win32", "cpu": "x64" }, "sha512-+teyvPDZ2RjUvo+SuCqS/UhaJl1QtdW5fWT5NJTV61V5MIuIS90Db9LixmtEGvXixyttiK62P96MSu3UlpviBw=="], - "@pigo/api": ["@pigo/api@workspace:apps/api"], - "@pigo/core": ["@pigo/core@workspace:packages/core"], "@pigo/typescript-config": ["@pigo/typescript-config@workspace:packages/typescript-config"], @@ -772,17 +764,17 @@ "@ts-morph/common": ["@ts-morph/common@0.28.1", "", { "dependencies": { "minimatch": "^10.0.1", "path-browserify": "^1.0.1", "tinyglobby": "^0.2.14" } }, "sha512-W74iWf7ILp1ZKNYXY5qbddNaml7e9Sedv5lvU1V8lftlitkc9Pq1A+jlH23ltDgWYeZFFEqGCD1Ies9hqu3O+g=="], - "@turbo/darwin-64": ["@turbo/darwin-64@2.10.8", "", { "os": "darwin", "cpu": "x64" }, "sha512-po+7rfJfUnFXjWlcoN2RwhErgzCdRtBc1T26vYPcywHlggmCQiQe1uWaE4j+BibI2uY9/2pDoFzMN0rmSaPFOw=="], + "@turbo/darwin-64": ["@turbo/darwin-64@2.10.13-canary.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-yxQKvG/PLHZq/pngKRtso8QDfJgZBN0UK5CHvOX7+B8s3Cw+V1lkf4dBnWhHsiCW3HAJ7DL9ZsbWZC1Ie+JEzw=="], - "@turbo/darwin-arm64": ["@turbo/darwin-arm64@2.10.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-+zB2btDJ00lnPRuqOvpVvgl4x34k/djZQGZTTCfjn7JgNCl8QFY5Njo5+dqkY1g/+9gbbsnAvWm9CmJg9ebcXA=="], + "@turbo/darwin-arm64": ["@turbo/darwin-arm64@2.10.13-canary.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-V1O1hC4TAQGQ+5KnoGcnBrugW2JoIEQ2aLZW3tLOf9N5tJMgQgQZCa2dUGWCB0qZuaOOIqPNxp4jVMHesnVJiQ=="], - "@turbo/linux-64": ["@turbo/linux-64@2.10.8", "", { "os": [ "linux", "android", ], "cpu": "x64" }, "sha512-K1dxqiVisyN7cViVsfQLs6xscQbYuI8aO2nbUhFURDACgEDfZRdP/b4CCxeosBJpcMfhYyiibWqJorCnvz9kKg=="], + "@turbo/linux-64": ["@turbo/linux-64@2.10.13-canary.4", "", { "os": [ "linux", "android", ], "cpu": "x64" }, "sha512-CcB/6TelfKxeGBVEr2Kg9uwNxv0AxkNpxm6wCxlZuduOpIT3kD7s01W3/DH6Vy5BK+EfG//7Ptx/tYXX0nFUtA=="], - "@turbo/linux-arm64": ["@turbo/linux-arm64@2.10.8", "", { "os": [ "linux", "android", ], "cpu": "arm64" }, "sha512-Gi77ibVnrE1fEmvr+/wBD/yvRqhwp/RQuCp2+//lv1U1wNFFyVg0V7Wj8FG9FXPFAw5QHReo8rxc9+wBSDZjzA=="], + "@turbo/linux-arm64": ["@turbo/linux-arm64@2.10.13-canary.4", "", { "os": [ "linux", "android", ], "cpu": "arm64" }, "sha512-gRdJwNZEWQdeZZ5y7QsfR+9hl2XyI8R2l7h3AaKaAo+isUJiQs2/kZTvo656rkxTx+1QDrJnBXkIC73nzWrq0g=="], - "@turbo/windows-64": ["@turbo/windows-64@2.10.8", "", { "os": "win32", "cpu": "x64" }, "sha512-znnLO1haJPYTHoKMKwlAvlkjRiYbbhBzME6wIGaMd+fwir23U6jVd1ecaTWWi1fbnRVqxMfgDBKseQ/hLKb83g=="], + "@turbo/windows-64": ["@turbo/windows-64@2.10.13-canary.4", "", { "os": "win32", "cpu": "x64" }, "sha512-cm/8u5MIkgruBp5GmKEvTcv7qK6KWwyz4+QCG9Kb9dHRR/C3bFaVfwvf5vDoY6sOpOGO2M/E+ao2gsHdWVfg1Q=="], - "@turbo/windows-arm64": ["@turbo/windows-arm64@2.10.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-VN30vh3b3Czh2WzYHNTfF1FE0YMZ5aHsLO8dBMGHJewA6792wX6iJR8ZxlzFW6WdOu0gEAKIvlYhfyT81Wkm4Q=="], + "@turbo/windows-arm64": ["@turbo/windows-arm64@2.10.13-canary.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-g58NPmqZtECuGXVoo8UHSbZZPIl27SWWR6nM0CMFi8WG/G00j3+n+M9xgmY4V/zsJRFjkLt6rmU6FUKp4w/+/A=="], "@tybys/wasm-util": ["@tybys/wasm-util@0.10.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg=="], @@ -1962,7 +1954,7 @@ "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "turbo": ["turbo@2.10.8", "", { "optionalDependencies": { "@turbo/darwin-64": "2.10.8", "@turbo/darwin-arm64": "2.10.8", "@turbo/linux-64": "2.10.8", "@turbo/linux-arm64": "2.10.8", "@turbo/windows-64": "2.10.8", "@turbo/windows-arm64": "2.10.8" }, "bin": { "turbo": "bin/turbo" } }, "sha512-9+8YX5QOkGXzZxcIykTHgaooRHGMWO+jfdyRK0o+rN0U7hBIig2MrJ8r/aNzIPDPhdA73SGb0O+tIztaModTMg=="], + "turbo": ["turbo@2.10.13-canary.4", "", { "optionalDependencies": { "@turbo/darwin-64": "2.10.13-canary.4", "@turbo/darwin-arm64": "2.10.13-canary.4", "@turbo/linux-64": "2.10.13-canary.4", "@turbo/linux-arm64": "2.10.13-canary.4", "@turbo/windows-64": "2.10.13-canary.4", "@turbo/windows-arm64": "2.10.13-canary.4" }, "bin": { "turbo": "bin/turbo" } }, "sha512-BYerLB66rlUvOIrezCwvDaZoMSdR15MltuKCnNkah/UwvC8vnkXImEq3i9LB+0DUmUxG3ygxfDwd0H1psOb1/Q=="], "tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="], diff --git a/package.json b/package.json index 1d455e2..007b24e 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,8 @@ "dev": "turbo run dev", "typecheck": "turbo run typecheck", "test": "turbo run test", - "check": "turbo run check && ultracite check", - "fix": "turbo run fix && ultracite fix", + "check": "turbo run lint && ultracite check", + "fix": "turbo run format && ultracite fix", "changeset": "changeset", "changeset:version": "changeset version", "changeset:publish": "turbo run build && changeset publish", @@ -26,7 +26,7 @@ "oxfmt": "^0.62.0", "oxlint": "^1.77.0", "react-doctor": "^0.9.5", - "turbo": "^2.10.8", + "turbo": "2.10.13-canary.4", "typescript": "^7.0.2", "ultracite": "7.10.1" }, diff --git a/packages/core/package.json b/packages/core/package.json index 955649a..a6f9df1 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,11 +2,5 @@ "name": "@pigo/core", "version": "0.1.1", "private": true, - "description": "Core image processing library for Pigo", - "type": "module", - "scripts": { - "test": "go test ./...", - "check": "go vet ./...", - "typecheck": "go vet ./..." - } + "description": "Core image processing library for Pigo" } diff --git a/turbo.json b/turbo.json index a8de5bf..826b617 100644 --- a/turbo.json +++ b/turbo.json @@ -1,5 +1,9 @@ { "$schema": "https://turbo.build/schema.json", + "futureFlags": { + "experimentalGoWorkspaces": true, + "experimentalTaskCommand": true + }, "ui": "tui", "envMode": "loose", "tasks": { @@ -11,7 +15,7 @@ "VERCEL", "VERCEL_ENV" ], - "outputs": ["bin/**", "dist/**", ".next/**", "!.next/cache/**"] + "outputs": ["dist/**", ".next/**", "!.next/cache/**"] }, "dev": { "cache": false, @@ -21,16 +25,18 @@ "dependsOn": ["^build"], "outputs": [] }, - "check": { - "cache": false, + "test": { "outputs": [] }, - "fix": { - "cache": false, + "lint": { "outputs": [] }, - "test": { - "outputs": [] + "format": { + "cache": false, + "outputs": [], + "command": { + "go": ["gofmt", "-l", "-w", "."] + } } } } From 99005e85f189c4f8eb325ae5323d05a6b412b0e8 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 03:57:41 +0000 Subject: [PATCH 2/8] docs: tighten the Go workspace section of the README Apply the writing guidelines review to the prose added with the Go workspace migration. - Replace "first-class Turborepo packages" with what it buys the reader: one task graph and one cache. - Open the project structure section with a summary sentence. - Show `--filter=./apps/api` alongside the module path; the directory form works and is shorter. - Say what `go-workspace` is before using the name, and note that filtering to one module runs that module's own `go test ./...`. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01ULAxKvHY6VD6aPxmsop4ni --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 53c4726..ad64988 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ A minimal-dependency, minimal-configuration image optimizer with a Go API backen - **Go API backend**: A REST server built on the standard library's JPEG and PNG codecs, with Catmull-Rom interpolation for resizing. - **Next.js frontend**: Drag-and-drop uploads, clipboard paste, ZIP downloads, and a before/after comparison slider. - **Zero heavy runtime dependencies**: The backend builds and runs without C libraries, GraphicsMagick, or libvips. -- **Turborepo monorepo**: Bun workspaces and `go.work` modules are both first-class Turborepo packages. +- **Turborepo monorepo**: Bun workspaces and `go.work` modules share one task graph and one cache. ## Tech stack @@ -20,6 +20,8 @@ A minimal-dependency, minimal-configuration image optimizer with a Go API backen ## Project structure +Two apps and two shared packages, split across a Bun workspace and a Go workspace: + ```text pigo/ ├── apps/ @@ -32,13 +34,14 @@ pigo/ └── package.json # Bun workspace configuration ``` -The Go modules (`apps/api`, `packages/core`) have no `package.json`. Turborepo reads them from `go.work` and synthesizes their tasks, so they are addressed by module path: +The Go modules (`apps/api`, `packages/core`) have no `package.json`. Turborepo reads them from `go.work` and derives their tasks, so you target a module by its directory or by its module path: ```bash +turbo run build --filter=./apps/api turbo run build --filter=github.com/PunGrumpy/pigo/apps/api ``` -Repository-wide Go tasks (`test`, `lint`, `format`) run in the synthetic `go-workspace` package. +`turbo run test` and `turbo run lint` cover every Go module in one pass, through a package Turborepo names `go-workspace`. Filtering to a single module runs `go test ./...` inside that module instead. ## Getting started From 955aaaa04b35f2f7dc940c11a4674315e416cb54 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 04:02:23 +0000 Subject: [PATCH 3/8] Revert "docs: tighten the Go workspace section of the README" This reverts commit 99005e85f189c4f8eb325ae5323d05a6b412b0e8. --- README.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index ad64988..53c4726 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ A minimal-dependency, minimal-configuration image optimizer with a Go API backen - **Go API backend**: A REST server built on the standard library's JPEG and PNG codecs, with Catmull-Rom interpolation for resizing. - **Next.js frontend**: Drag-and-drop uploads, clipboard paste, ZIP downloads, and a before/after comparison slider. - **Zero heavy runtime dependencies**: The backend builds and runs without C libraries, GraphicsMagick, or libvips. -- **Turborepo monorepo**: Bun workspaces and `go.work` modules share one task graph and one cache. +- **Turborepo monorepo**: Bun workspaces and `go.work` modules are both first-class Turborepo packages. ## Tech stack @@ -20,8 +20,6 @@ A minimal-dependency, minimal-configuration image optimizer with a Go API backen ## Project structure -Two apps and two shared packages, split across a Bun workspace and a Go workspace: - ```text pigo/ ├── apps/ @@ -34,14 +32,13 @@ pigo/ └── package.json # Bun workspace configuration ``` -The Go modules (`apps/api`, `packages/core`) have no `package.json`. Turborepo reads them from `go.work` and derives their tasks, so you target a module by its directory or by its module path: +The Go modules (`apps/api`, `packages/core`) have no `package.json`. Turborepo reads them from `go.work` and synthesizes their tasks, so they are addressed by module path: ```bash -turbo run build --filter=./apps/api turbo run build --filter=github.com/PunGrumpy/pigo/apps/api ``` -`turbo run test` and `turbo run lint` cover every Go module in one pass, through a package Turborepo names `go-workspace`. Filtering to a single module runs `go test ./...` inside that module instead. +Repository-wide Go tasks (`test`, `lint`, `format`) run in the synthetic `go-workspace` package. ## Getting started From b89d6af0fc8540447c29ece67db95b7319498674 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 04:02:23 +0000 Subject: [PATCH 4/8] Revert "build: adopt Turborepo's experimental Go workspace support" This reverts commit ff35bda31977b03e444585947f200963492341b4. --- .changeset/config.json | 2 +- .changeset/nervous-cars-dance.md | 4 ---- README.md | 16 ++++---------- apps/api/package.json | 15 +++++++++++++ apps/api/turbo.json | 36 ++++++++++++++++++++++++++++++++ bun.lock | 24 ++++++++++++++------- package.json | 6 +++--- packages/core/package.json | 8 ++++++- turbo.json | 20 +++++++----------- 9 files changed, 89 insertions(+), 42 deletions(-) delete mode 100644 .changeset/nervous-cars-dance.md create mode 100644 apps/api/package.json create mode 100644 apps/api/turbo.json diff --git a/.changeset/config.json b/.changeset/config.json index 49783a3..879d2df 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -7,5 +7,5 @@ "access": "public", "baseBranch": "main", "updateInternalDependencies": "patch", - "ignore": ["web", "@pigo/typescript-config"] + "ignore": ["web", "@pigo/api", "@pigo/typescript-config"] } diff --git a/.changeset/nervous-cars-dance.md b/.changeset/nervous-cars-dance.md deleted file mode 100644 index 05f0c09..0000000 --- a/.changeset/nervous-cars-dance.md +++ /dev/null @@ -1,4 +0,0 @@ ---- ---- - -Move the Go modules onto Turborepo's experimental Go workspace support. Build tooling only; no change to the published library. diff --git a/README.md b/README.md index 53c4726..3d913d6 100644 --- a/README.md +++ b/README.md @@ -10,13 +10,13 @@ A minimal-dependency, minimal-configuration image optimizer with a Go API backen - **Go API backend**: A REST server built on the standard library's JPEG and PNG codecs, with Catmull-Rom interpolation for resizing. - **Next.js frontend**: Drag-and-drop uploads, clipboard paste, ZIP downloads, and a before/after comparison slider. - **Zero heavy runtime dependencies**: The backend builds and runs without C libraries, GraphicsMagick, or libvips. -- **Turborepo monorepo**: Bun workspaces and `go.work` modules are both first-class Turborepo packages. +- **Turborepo monorepo**: Bun workspaces handle builds and package management. ## Tech stack - **Backend**: Go (1.26+), [`chi`](https://github.com/go-chi/chi) router, `golang.org/x/image/draw` - **Frontend**: Next.js (16.3+), React 19, Tailwind CSS v4, Lucide Icons, JSZip -- **Tooling**: Bun, Turborepo (with experimental Go workspace support), [Ultracite](https://github.com/PunGrumpy/ultracite) (Oxlint + Oxfmt) +- **Tooling**: Bun, Turborepo, [Ultracite](https://github.com/PunGrumpy/ultracite) (Oxlint + Oxfmt), Air (Go hot-reloading) ## Project structure @@ -28,18 +28,9 @@ pigo/ ├── packages/ │ ├── core/ # Go image decoding, encoding, and resizing │ └── typescript-config/ # Shared TypeScript configs -├── go.work # Go workspace; its members are Turborepo packages -└── package.json # Bun workspace configuration +└── package.json # Workspace configuration ``` -The Go modules (`apps/api`, `packages/core`) have no `package.json`. Turborepo reads them from `go.work` and synthesizes their tasks, so they are addressed by module path: - -```bash -turbo run build --filter=github.com/PunGrumpy/pigo/apps/api -``` - -Repository-wide Go tasks (`test`, `lint`, `format`) run in the synthetic `go-workspace` package. - ## Getting started ### Prerequisites @@ -48,6 +39,7 @@ Make sure you have the following installed: - [Go](https://go.dev/doc/install) (1.26 or later) - [Bun](https://bun.sh) (1.3.14 or later) +- [Air](https://github.com/air-verse/air), optional, for API hot-reloading ### Installation diff --git a/apps/api/package.json b/apps/api/package.json new file mode 100644 index 0000000..bb924be --- /dev/null +++ b/apps/api/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pigo/api", + "private": true, + "scripts": { + "build": "go build -o bin/pigo-api .", + "dev": "air .", + "test": "go test ./...", + "check": "go vet ./...", + "fix": "go fmt ./...", + "typecheck": "go vet ./..." + }, + "dependencies": { + "@pigo/core": "workspace:*" + } +} diff --git a/apps/api/turbo.json b/apps/api/turbo.json new file mode 100644 index 0000000..dd6d4cb --- /dev/null +++ b/apps/api/turbo.json @@ -0,0 +1,36 @@ +{ + "$schema": "https://turbo.build/schema.json", + "extends": ["//"], + "tasks": { + "build": { + "inputs": [ + "$TURBO_DEFAULT$", + "$TURBO_ROOT$/packages/core/**/*.go", + "$TURBO_ROOT$/packages/core/go.mod", + "$TURBO_ROOT$/packages/core/go.sum", + "$TURBO_ROOT$/go.work", + "$TURBO_ROOT$/go.work.sum" + ] + }, + "test": { + "inputs": [ + "$TURBO_DEFAULT$", + "$TURBO_ROOT$/packages/core/**/*.go", + "$TURBO_ROOT$/packages/core/go.mod", + "$TURBO_ROOT$/packages/core/go.sum", + "$TURBO_ROOT$/go.work", + "$TURBO_ROOT$/go.work.sum" + ] + }, + "typecheck": { + "inputs": [ + "$TURBO_DEFAULT$", + "$TURBO_ROOT$/packages/core/**/*.go", + "$TURBO_ROOT$/packages/core/go.mod", + "$TURBO_ROOT$/packages/core/go.sum", + "$TURBO_ROOT$/go.work", + "$TURBO_ROOT$/go.work.sum" + ] + } + } +} diff --git a/bun.lock b/bun.lock index fb8c5cd..aba5e3b 100644 --- a/bun.lock +++ b/bun.lock @@ -10,11 +10,17 @@ "oxfmt": "^0.62.0", "oxlint": "^1.77.0", "react-doctor": "^0.9.5", - "turbo": "2.10.13-canary.4", + "turbo": "^2.10.8", "typescript": "^7.0.2", "ultracite": "7.10.1", }, }, + "apps/api": { + "name": "@pigo/api", + "dependencies": { + "@pigo/core": "workspace:*", + }, + }, "apps/web": { "name": "web", "version": "0.1.0", @@ -540,6 +546,8 @@ "@oxlint/binding-win32-x64-msvc": ["@oxlint/binding-win32-x64-msvc@1.77.0", "", { "os": "win32", "cpu": "x64" }, "sha512-+teyvPDZ2RjUvo+SuCqS/UhaJl1QtdW5fWT5NJTV61V5MIuIS90Db9LixmtEGvXixyttiK62P96MSu3UlpviBw=="], + "@pigo/api": ["@pigo/api@workspace:apps/api"], + "@pigo/core": ["@pigo/core@workspace:packages/core"], "@pigo/typescript-config": ["@pigo/typescript-config@workspace:packages/typescript-config"], @@ -764,17 +772,17 @@ "@ts-morph/common": ["@ts-morph/common@0.28.1", "", { "dependencies": { "minimatch": "^10.0.1", "path-browserify": "^1.0.1", "tinyglobby": "^0.2.14" } }, "sha512-W74iWf7ILp1ZKNYXY5qbddNaml7e9Sedv5lvU1V8lftlitkc9Pq1A+jlH23ltDgWYeZFFEqGCD1Ies9hqu3O+g=="], - "@turbo/darwin-64": ["@turbo/darwin-64@2.10.13-canary.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-yxQKvG/PLHZq/pngKRtso8QDfJgZBN0UK5CHvOX7+B8s3Cw+V1lkf4dBnWhHsiCW3HAJ7DL9ZsbWZC1Ie+JEzw=="], + "@turbo/darwin-64": ["@turbo/darwin-64@2.10.8", "", { "os": "darwin", "cpu": "x64" }, "sha512-po+7rfJfUnFXjWlcoN2RwhErgzCdRtBc1T26vYPcywHlggmCQiQe1uWaE4j+BibI2uY9/2pDoFzMN0rmSaPFOw=="], - "@turbo/darwin-arm64": ["@turbo/darwin-arm64@2.10.13-canary.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-V1O1hC4TAQGQ+5KnoGcnBrugW2JoIEQ2aLZW3tLOf9N5tJMgQgQZCa2dUGWCB0qZuaOOIqPNxp4jVMHesnVJiQ=="], + "@turbo/darwin-arm64": ["@turbo/darwin-arm64@2.10.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-+zB2btDJ00lnPRuqOvpVvgl4x34k/djZQGZTTCfjn7JgNCl8QFY5Njo5+dqkY1g/+9gbbsnAvWm9CmJg9ebcXA=="], - "@turbo/linux-64": ["@turbo/linux-64@2.10.13-canary.4", "", { "os": [ "linux", "android", ], "cpu": "x64" }, "sha512-CcB/6TelfKxeGBVEr2Kg9uwNxv0AxkNpxm6wCxlZuduOpIT3kD7s01W3/DH6Vy5BK+EfG//7Ptx/tYXX0nFUtA=="], + "@turbo/linux-64": ["@turbo/linux-64@2.10.8", "", { "os": [ "linux", "android", ], "cpu": "x64" }, "sha512-K1dxqiVisyN7cViVsfQLs6xscQbYuI8aO2nbUhFURDACgEDfZRdP/b4CCxeosBJpcMfhYyiibWqJorCnvz9kKg=="], - "@turbo/linux-arm64": ["@turbo/linux-arm64@2.10.13-canary.4", "", { "os": [ "linux", "android", ], "cpu": "arm64" }, "sha512-gRdJwNZEWQdeZZ5y7QsfR+9hl2XyI8R2l7h3AaKaAo+isUJiQs2/kZTvo656rkxTx+1QDrJnBXkIC73nzWrq0g=="], + "@turbo/linux-arm64": ["@turbo/linux-arm64@2.10.8", "", { "os": [ "linux", "android", ], "cpu": "arm64" }, "sha512-Gi77ibVnrE1fEmvr+/wBD/yvRqhwp/RQuCp2+//lv1U1wNFFyVg0V7Wj8FG9FXPFAw5QHReo8rxc9+wBSDZjzA=="], - "@turbo/windows-64": ["@turbo/windows-64@2.10.13-canary.4", "", { "os": "win32", "cpu": "x64" }, "sha512-cm/8u5MIkgruBp5GmKEvTcv7qK6KWwyz4+QCG9Kb9dHRR/C3bFaVfwvf5vDoY6sOpOGO2M/E+ao2gsHdWVfg1Q=="], + "@turbo/windows-64": ["@turbo/windows-64@2.10.8", "", { "os": "win32", "cpu": "x64" }, "sha512-znnLO1haJPYTHoKMKwlAvlkjRiYbbhBzME6wIGaMd+fwir23U6jVd1ecaTWWi1fbnRVqxMfgDBKseQ/hLKb83g=="], - "@turbo/windows-arm64": ["@turbo/windows-arm64@2.10.13-canary.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-g58NPmqZtECuGXVoo8UHSbZZPIl27SWWR6nM0CMFi8WG/G00j3+n+M9xgmY4V/zsJRFjkLt6rmU6FUKp4w/+/A=="], + "@turbo/windows-arm64": ["@turbo/windows-arm64@2.10.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-VN30vh3b3Czh2WzYHNTfF1FE0YMZ5aHsLO8dBMGHJewA6792wX6iJR8ZxlzFW6WdOu0gEAKIvlYhfyT81Wkm4Q=="], "@tybys/wasm-util": ["@tybys/wasm-util@0.10.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg=="], @@ -1954,7 +1962,7 @@ "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "turbo": ["turbo@2.10.13-canary.4", "", { "optionalDependencies": { "@turbo/darwin-64": "2.10.13-canary.4", "@turbo/darwin-arm64": "2.10.13-canary.4", "@turbo/linux-64": "2.10.13-canary.4", "@turbo/linux-arm64": "2.10.13-canary.4", "@turbo/windows-64": "2.10.13-canary.4", "@turbo/windows-arm64": "2.10.13-canary.4" }, "bin": { "turbo": "bin/turbo" } }, "sha512-BYerLB66rlUvOIrezCwvDaZoMSdR15MltuKCnNkah/UwvC8vnkXImEq3i9LB+0DUmUxG3ygxfDwd0H1psOb1/Q=="], + "turbo": ["turbo@2.10.8", "", { "optionalDependencies": { "@turbo/darwin-64": "2.10.8", "@turbo/darwin-arm64": "2.10.8", "@turbo/linux-64": "2.10.8", "@turbo/linux-arm64": "2.10.8", "@turbo/windows-64": "2.10.8", "@turbo/windows-arm64": "2.10.8" }, "bin": { "turbo": "bin/turbo" } }, "sha512-9+8YX5QOkGXzZxcIykTHgaooRHGMWO+jfdyRK0o+rN0U7hBIig2MrJ8r/aNzIPDPhdA73SGb0O+tIztaModTMg=="], "tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="], diff --git a/package.json b/package.json index 007b24e..1d455e2 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,8 @@ "dev": "turbo run dev", "typecheck": "turbo run typecheck", "test": "turbo run test", - "check": "turbo run lint && ultracite check", - "fix": "turbo run format && ultracite fix", + "check": "turbo run check && ultracite check", + "fix": "turbo run fix && ultracite fix", "changeset": "changeset", "changeset:version": "changeset version", "changeset:publish": "turbo run build && changeset publish", @@ -26,7 +26,7 @@ "oxfmt": "^0.62.0", "oxlint": "^1.77.0", "react-doctor": "^0.9.5", - "turbo": "2.10.13-canary.4", + "turbo": "^2.10.8", "typescript": "^7.0.2", "ultracite": "7.10.1" }, diff --git a/packages/core/package.json b/packages/core/package.json index a6f9df1..955649a 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,5 +2,11 @@ "name": "@pigo/core", "version": "0.1.1", "private": true, - "description": "Core image processing library for Pigo" + "description": "Core image processing library for Pigo", + "type": "module", + "scripts": { + "test": "go test ./...", + "check": "go vet ./...", + "typecheck": "go vet ./..." + } } diff --git a/turbo.json b/turbo.json index 826b617..a8de5bf 100644 --- a/turbo.json +++ b/turbo.json @@ -1,9 +1,5 @@ { "$schema": "https://turbo.build/schema.json", - "futureFlags": { - "experimentalGoWorkspaces": true, - "experimentalTaskCommand": true - }, "ui": "tui", "envMode": "loose", "tasks": { @@ -15,7 +11,7 @@ "VERCEL", "VERCEL_ENV" ], - "outputs": ["dist/**", ".next/**", "!.next/cache/**"] + "outputs": ["bin/**", "dist/**", ".next/**", "!.next/cache/**"] }, "dev": { "cache": false, @@ -25,18 +21,16 @@ "dependsOn": ["^build"], "outputs": [] }, - "test": { + "check": { + "cache": false, "outputs": [] }, - "lint": { + "fix": { + "cache": false, "outputs": [] }, - "format": { - "cache": false, - "outputs": [], - "command": { - "go": ["gofmt", "-l", "-w", "."] - } + "test": { + "outputs": [] } } } From bb337e2cc6a32632caa9fbf775e88c53c880175d Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 05:56:13 +0000 Subject: [PATCH 5/8] Reapply "build: adopt Turborepo's experimental Go workspace support" This reverts commit b89d6af0fc8540447c29ece67db95b7319498674. --- .changeset/config.json | 2 +- .changeset/nervous-cars-dance.md | 4 ++++ README.md | 16 ++++++++++---- apps/api/package.json | 15 ------------- apps/api/turbo.json | 36 -------------------------------- bun.lock | 24 +++++++-------------- package.json | 6 +++--- packages/core/package.json | 8 +------ turbo.json | 20 +++++++++++------- 9 files changed, 42 insertions(+), 89 deletions(-) create mode 100644 .changeset/nervous-cars-dance.md delete mode 100644 apps/api/package.json delete mode 100644 apps/api/turbo.json diff --git a/.changeset/config.json b/.changeset/config.json index 879d2df..49783a3 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -7,5 +7,5 @@ "access": "public", "baseBranch": "main", "updateInternalDependencies": "patch", - "ignore": ["web", "@pigo/api", "@pigo/typescript-config"] + "ignore": ["web", "@pigo/typescript-config"] } diff --git a/.changeset/nervous-cars-dance.md b/.changeset/nervous-cars-dance.md new file mode 100644 index 0000000..05f0c09 --- /dev/null +++ b/.changeset/nervous-cars-dance.md @@ -0,0 +1,4 @@ +--- +--- + +Move the Go modules onto Turborepo's experimental Go workspace support. Build tooling only; no change to the published library. diff --git a/README.md b/README.md index 3d913d6..53c4726 100644 --- a/README.md +++ b/README.md @@ -10,13 +10,13 @@ A minimal-dependency, minimal-configuration image optimizer with a Go API backen - **Go API backend**: A REST server built on the standard library's JPEG and PNG codecs, with Catmull-Rom interpolation for resizing. - **Next.js frontend**: Drag-and-drop uploads, clipboard paste, ZIP downloads, and a before/after comparison slider. - **Zero heavy runtime dependencies**: The backend builds and runs without C libraries, GraphicsMagick, or libvips. -- **Turborepo monorepo**: Bun workspaces handle builds and package management. +- **Turborepo monorepo**: Bun workspaces and `go.work` modules are both first-class Turborepo packages. ## Tech stack - **Backend**: Go (1.26+), [`chi`](https://github.com/go-chi/chi) router, `golang.org/x/image/draw` - **Frontend**: Next.js (16.3+), React 19, Tailwind CSS v4, Lucide Icons, JSZip -- **Tooling**: Bun, Turborepo, [Ultracite](https://github.com/PunGrumpy/ultracite) (Oxlint + Oxfmt), Air (Go hot-reloading) +- **Tooling**: Bun, Turborepo (with experimental Go workspace support), [Ultracite](https://github.com/PunGrumpy/ultracite) (Oxlint + Oxfmt) ## Project structure @@ -28,9 +28,18 @@ pigo/ ├── packages/ │ ├── core/ # Go image decoding, encoding, and resizing │ └── typescript-config/ # Shared TypeScript configs -└── package.json # Workspace configuration +├── go.work # Go workspace; its members are Turborepo packages +└── package.json # Bun workspace configuration ``` +The Go modules (`apps/api`, `packages/core`) have no `package.json`. Turborepo reads them from `go.work` and synthesizes their tasks, so they are addressed by module path: + +```bash +turbo run build --filter=github.com/PunGrumpy/pigo/apps/api +``` + +Repository-wide Go tasks (`test`, `lint`, `format`) run in the synthetic `go-workspace` package. + ## Getting started ### Prerequisites @@ -39,7 +48,6 @@ Make sure you have the following installed: - [Go](https://go.dev/doc/install) (1.26 or later) - [Bun](https://bun.sh) (1.3.14 or later) -- [Air](https://github.com/air-verse/air), optional, for API hot-reloading ### Installation diff --git a/apps/api/package.json b/apps/api/package.json deleted file mode 100644 index bb924be..0000000 --- a/apps/api/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "@pigo/api", - "private": true, - "scripts": { - "build": "go build -o bin/pigo-api .", - "dev": "air .", - "test": "go test ./...", - "check": "go vet ./...", - "fix": "go fmt ./...", - "typecheck": "go vet ./..." - }, - "dependencies": { - "@pigo/core": "workspace:*" - } -} diff --git a/apps/api/turbo.json b/apps/api/turbo.json deleted file mode 100644 index dd6d4cb..0000000 --- a/apps/api/turbo.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "$schema": "https://turbo.build/schema.json", - "extends": ["//"], - "tasks": { - "build": { - "inputs": [ - "$TURBO_DEFAULT$", - "$TURBO_ROOT$/packages/core/**/*.go", - "$TURBO_ROOT$/packages/core/go.mod", - "$TURBO_ROOT$/packages/core/go.sum", - "$TURBO_ROOT$/go.work", - "$TURBO_ROOT$/go.work.sum" - ] - }, - "test": { - "inputs": [ - "$TURBO_DEFAULT$", - "$TURBO_ROOT$/packages/core/**/*.go", - "$TURBO_ROOT$/packages/core/go.mod", - "$TURBO_ROOT$/packages/core/go.sum", - "$TURBO_ROOT$/go.work", - "$TURBO_ROOT$/go.work.sum" - ] - }, - "typecheck": { - "inputs": [ - "$TURBO_DEFAULT$", - "$TURBO_ROOT$/packages/core/**/*.go", - "$TURBO_ROOT$/packages/core/go.mod", - "$TURBO_ROOT$/packages/core/go.sum", - "$TURBO_ROOT$/go.work", - "$TURBO_ROOT$/go.work.sum" - ] - } - } -} diff --git a/bun.lock b/bun.lock index aba5e3b..fb8c5cd 100644 --- a/bun.lock +++ b/bun.lock @@ -10,17 +10,11 @@ "oxfmt": "^0.62.0", "oxlint": "^1.77.0", "react-doctor": "^0.9.5", - "turbo": "^2.10.8", + "turbo": "2.10.13-canary.4", "typescript": "^7.0.2", "ultracite": "7.10.1", }, }, - "apps/api": { - "name": "@pigo/api", - "dependencies": { - "@pigo/core": "workspace:*", - }, - }, "apps/web": { "name": "web", "version": "0.1.0", @@ -546,8 +540,6 @@ "@oxlint/binding-win32-x64-msvc": ["@oxlint/binding-win32-x64-msvc@1.77.0", "", { "os": "win32", "cpu": "x64" }, "sha512-+teyvPDZ2RjUvo+SuCqS/UhaJl1QtdW5fWT5NJTV61V5MIuIS90Db9LixmtEGvXixyttiK62P96MSu3UlpviBw=="], - "@pigo/api": ["@pigo/api@workspace:apps/api"], - "@pigo/core": ["@pigo/core@workspace:packages/core"], "@pigo/typescript-config": ["@pigo/typescript-config@workspace:packages/typescript-config"], @@ -772,17 +764,17 @@ "@ts-morph/common": ["@ts-morph/common@0.28.1", "", { "dependencies": { "minimatch": "^10.0.1", "path-browserify": "^1.0.1", "tinyglobby": "^0.2.14" } }, "sha512-W74iWf7ILp1ZKNYXY5qbddNaml7e9Sedv5lvU1V8lftlitkc9Pq1A+jlH23ltDgWYeZFFEqGCD1Ies9hqu3O+g=="], - "@turbo/darwin-64": ["@turbo/darwin-64@2.10.8", "", { "os": "darwin", "cpu": "x64" }, "sha512-po+7rfJfUnFXjWlcoN2RwhErgzCdRtBc1T26vYPcywHlggmCQiQe1uWaE4j+BibI2uY9/2pDoFzMN0rmSaPFOw=="], + "@turbo/darwin-64": ["@turbo/darwin-64@2.10.13-canary.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-yxQKvG/PLHZq/pngKRtso8QDfJgZBN0UK5CHvOX7+B8s3Cw+V1lkf4dBnWhHsiCW3HAJ7DL9ZsbWZC1Ie+JEzw=="], - "@turbo/darwin-arm64": ["@turbo/darwin-arm64@2.10.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-+zB2btDJ00lnPRuqOvpVvgl4x34k/djZQGZTTCfjn7JgNCl8QFY5Njo5+dqkY1g/+9gbbsnAvWm9CmJg9ebcXA=="], + "@turbo/darwin-arm64": ["@turbo/darwin-arm64@2.10.13-canary.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-V1O1hC4TAQGQ+5KnoGcnBrugW2JoIEQ2aLZW3tLOf9N5tJMgQgQZCa2dUGWCB0qZuaOOIqPNxp4jVMHesnVJiQ=="], - "@turbo/linux-64": ["@turbo/linux-64@2.10.8", "", { "os": [ "linux", "android", ], "cpu": "x64" }, "sha512-K1dxqiVisyN7cViVsfQLs6xscQbYuI8aO2nbUhFURDACgEDfZRdP/b4CCxeosBJpcMfhYyiibWqJorCnvz9kKg=="], + "@turbo/linux-64": ["@turbo/linux-64@2.10.13-canary.4", "", { "os": [ "linux", "android", ], "cpu": "x64" }, "sha512-CcB/6TelfKxeGBVEr2Kg9uwNxv0AxkNpxm6wCxlZuduOpIT3kD7s01W3/DH6Vy5BK+EfG//7Ptx/tYXX0nFUtA=="], - "@turbo/linux-arm64": ["@turbo/linux-arm64@2.10.8", "", { "os": [ "linux", "android", ], "cpu": "arm64" }, "sha512-Gi77ibVnrE1fEmvr+/wBD/yvRqhwp/RQuCp2+//lv1U1wNFFyVg0V7Wj8FG9FXPFAw5QHReo8rxc9+wBSDZjzA=="], + "@turbo/linux-arm64": ["@turbo/linux-arm64@2.10.13-canary.4", "", { "os": [ "linux", "android", ], "cpu": "arm64" }, "sha512-gRdJwNZEWQdeZZ5y7QsfR+9hl2XyI8R2l7h3AaKaAo+isUJiQs2/kZTvo656rkxTx+1QDrJnBXkIC73nzWrq0g=="], - "@turbo/windows-64": ["@turbo/windows-64@2.10.8", "", { "os": "win32", "cpu": "x64" }, "sha512-znnLO1haJPYTHoKMKwlAvlkjRiYbbhBzME6wIGaMd+fwir23U6jVd1ecaTWWi1fbnRVqxMfgDBKseQ/hLKb83g=="], + "@turbo/windows-64": ["@turbo/windows-64@2.10.13-canary.4", "", { "os": "win32", "cpu": "x64" }, "sha512-cm/8u5MIkgruBp5GmKEvTcv7qK6KWwyz4+QCG9Kb9dHRR/C3bFaVfwvf5vDoY6sOpOGO2M/E+ao2gsHdWVfg1Q=="], - "@turbo/windows-arm64": ["@turbo/windows-arm64@2.10.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-VN30vh3b3Czh2WzYHNTfF1FE0YMZ5aHsLO8dBMGHJewA6792wX6iJR8ZxlzFW6WdOu0gEAKIvlYhfyT81Wkm4Q=="], + "@turbo/windows-arm64": ["@turbo/windows-arm64@2.10.13-canary.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-g58NPmqZtECuGXVoo8UHSbZZPIl27SWWR6nM0CMFi8WG/G00j3+n+M9xgmY4V/zsJRFjkLt6rmU6FUKp4w/+/A=="], "@tybys/wasm-util": ["@tybys/wasm-util@0.10.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg=="], @@ -1962,7 +1954,7 @@ "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "turbo": ["turbo@2.10.8", "", { "optionalDependencies": { "@turbo/darwin-64": "2.10.8", "@turbo/darwin-arm64": "2.10.8", "@turbo/linux-64": "2.10.8", "@turbo/linux-arm64": "2.10.8", "@turbo/windows-64": "2.10.8", "@turbo/windows-arm64": "2.10.8" }, "bin": { "turbo": "bin/turbo" } }, "sha512-9+8YX5QOkGXzZxcIykTHgaooRHGMWO+jfdyRK0o+rN0U7hBIig2MrJ8r/aNzIPDPhdA73SGb0O+tIztaModTMg=="], + "turbo": ["turbo@2.10.13-canary.4", "", { "optionalDependencies": { "@turbo/darwin-64": "2.10.13-canary.4", "@turbo/darwin-arm64": "2.10.13-canary.4", "@turbo/linux-64": "2.10.13-canary.4", "@turbo/linux-arm64": "2.10.13-canary.4", "@turbo/windows-64": "2.10.13-canary.4", "@turbo/windows-arm64": "2.10.13-canary.4" }, "bin": { "turbo": "bin/turbo" } }, "sha512-BYerLB66rlUvOIrezCwvDaZoMSdR15MltuKCnNkah/UwvC8vnkXImEq3i9LB+0DUmUxG3ygxfDwd0H1psOb1/Q=="], "tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="], diff --git a/package.json b/package.json index 1d455e2..007b24e 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,8 @@ "dev": "turbo run dev", "typecheck": "turbo run typecheck", "test": "turbo run test", - "check": "turbo run check && ultracite check", - "fix": "turbo run fix && ultracite fix", + "check": "turbo run lint && ultracite check", + "fix": "turbo run format && ultracite fix", "changeset": "changeset", "changeset:version": "changeset version", "changeset:publish": "turbo run build && changeset publish", @@ -26,7 +26,7 @@ "oxfmt": "^0.62.0", "oxlint": "^1.77.0", "react-doctor": "^0.9.5", - "turbo": "^2.10.8", + "turbo": "2.10.13-canary.4", "typescript": "^7.0.2", "ultracite": "7.10.1" }, diff --git a/packages/core/package.json b/packages/core/package.json index 955649a..a6f9df1 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,11 +2,5 @@ "name": "@pigo/core", "version": "0.1.1", "private": true, - "description": "Core image processing library for Pigo", - "type": "module", - "scripts": { - "test": "go test ./...", - "check": "go vet ./...", - "typecheck": "go vet ./..." - } + "description": "Core image processing library for Pigo" } diff --git a/turbo.json b/turbo.json index a8de5bf..826b617 100644 --- a/turbo.json +++ b/turbo.json @@ -1,5 +1,9 @@ { "$schema": "https://turbo.build/schema.json", + "futureFlags": { + "experimentalGoWorkspaces": true, + "experimentalTaskCommand": true + }, "ui": "tui", "envMode": "loose", "tasks": { @@ -11,7 +15,7 @@ "VERCEL", "VERCEL_ENV" ], - "outputs": ["bin/**", "dist/**", ".next/**", "!.next/cache/**"] + "outputs": ["dist/**", ".next/**", "!.next/cache/**"] }, "dev": { "cache": false, @@ -21,16 +25,18 @@ "dependsOn": ["^build"], "outputs": [] }, - "check": { - "cache": false, + "test": { "outputs": [] }, - "fix": { - "cache": false, + "lint": { "outputs": [] }, - "test": { - "outputs": [] + "format": { + "cache": false, + "outputs": [], + "command": { + "go": ["gofmt", "-l", "-w", "."] + } } } } From 5dd77451679220d2fbe662047941a7238524f717 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 05:56:13 +0000 Subject: [PATCH 6/8] Reapply "docs: tighten the Go workspace section of the README" This reverts commit 955aaaa04b35f2f7dc940c11a4674315e416cb54. --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 53c4726..ad64988 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ A minimal-dependency, minimal-configuration image optimizer with a Go API backen - **Go API backend**: A REST server built on the standard library's JPEG and PNG codecs, with Catmull-Rom interpolation for resizing. - **Next.js frontend**: Drag-and-drop uploads, clipboard paste, ZIP downloads, and a before/after comparison slider. - **Zero heavy runtime dependencies**: The backend builds and runs without C libraries, GraphicsMagick, or libvips. -- **Turborepo monorepo**: Bun workspaces and `go.work` modules are both first-class Turborepo packages. +- **Turborepo monorepo**: Bun workspaces and `go.work` modules share one task graph and one cache. ## Tech stack @@ -20,6 +20,8 @@ A minimal-dependency, minimal-configuration image optimizer with a Go API backen ## Project structure +Two apps and two shared packages, split across a Bun workspace and a Go workspace: + ```text pigo/ ├── apps/ @@ -32,13 +34,14 @@ pigo/ └── package.json # Bun workspace configuration ``` -The Go modules (`apps/api`, `packages/core`) have no `package.json`. Turborepo reads them from `go.work` and synthesizes their tasks, so they are addressed by module path: +The Go modules (`apps/api`, `packages/core`) have no `package.json`. Turborepo reads them from `go.work` and derives their tasks, so you target a module by its directory or by its module path: ```bash +turbo run build --filter=./apps/api turbo run build --filter=github.com/PunGrumpy/pigo/apps/api ``` -Repository-wide Go tasks (`test`, `lint`, `format`) run in the synthetic `go-workspace` package. +`turbo run test` and `turbo run lint` cover every Go module in one pass, through a package Turborepo names `go-workspace`. Filtering to a single module runs `go test ./...` inside that module instead. ## Getting started From df78db03654222a31beecd8e02fa1a5815a0466f Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 05:58:14 +0000 Subject: [PATCH 7/8] build: give each Vercel app an explicit build command Enabling `experimentalGoWorkspaces` makes Turborepo run `go work edit -json` while building the package graph, before it reads `--filter`, so every `turbo run` in the repo now needs a Go toolchain on PATH. The Next.js build image has none, and the web deployment failed on it even though that app never invokes Go. Both apps now set their own `buildCommand` and skip Turborepo on Vercel: - `apps/web`: `bun run --filter web build`. Resolves the workspace by walking up, so it builds from either the repo root or the app directory, and `.next` still lands in `apps/web`. - `apps/api`: `go build -o "$VERCEL_OUTPUT_FILE" `. The synthesized task writes `dist/api`, which the platform's Go builder could not find; addressing the module by path rather than by `.` keeps the command correct from either working directory. Turborepo still drives every local and CI task; only the two Vercel build commands bypass it. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01ULAxKvHY6VD6aPxmsop4ni --- README.md | 2 ++ apps/api/vercel.json | 1 + apps/web/vercel.json | 5 +++++ 3 files changed, 8 insertions(+) create mode 100644 apps/web/vercel.json diff --git a/README.md b/README.md index ad64988..0078a44 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,8 @@ turbo run build --filter=github.com/PunGrumpy/pigo/apps/api `turbo run test` and `turbo run lint` cover every Go module in one pass, through a package Turborepo names `go-workspace`. Filtering to a single module runs `go test ./...` inside that module instead. +Vercel builds both apps without `turbo`. The flag makes Turborepo shell out to `go work edit -json` on every invocation, so a Next.js build image with no Go toolchain can no longer run it. Each app's `vercel.json` sets an explicit `buildCommand` that skips Turborepo, and the Go one writes to `$VERCEL_OUTPUT_FILE` so the platform finds the binary wherever the build runs from. + ## Getting started ### Prerequisites diff --git a/apps/api/vercel.json b/apps/api/vercel.json index 7e2a4a5..8cca61a 100644 --- a/apps/api/vercel.json +++ b/apps/api/vercel.json @@ -1,6 +1,7 @@ { "$schema": "https://openapi.vercel.sh/vercel.json", "framework": "go", + "buildCommand": "go build -o \"$VERCEL_OUTPUT_FILE\" github.com/PunGrumpy/pigo/apps/api", "redirects": [ { "source": "/", diff --git a/apps/web/vercel.json b/apps/web/vercel.json new file mode 100644 index 0000000..0402629 --- /dev/null +++ b/apps/web/vercel.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://openapi.vercel.sh/vercel.json", + "framework": "nextjs", + "buildCommand": "bun run --filter web build" +} From 757b2050d2a8122e88b1b443933fb7cd6c9d0788 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 15 Sep 2026 01:40:29 +0000 Subject: [PATCH 8/8] build: move off the turbo canary now that Go workspaces ship in 2.10.13 `futureFlags.experimentalGoWorkspaces` reached the stable release, so the exact canary pin is no longer needed. 2.10.13 also carries the fix for the task hash that changed with the invoked task, which we reported against 2.10.13-canary.4: `apps/api#build` now keeps its `^build` edge and one hash under both `turbo run build` and `turbo run typecheck`, so the Go build compiles once per change instead of twice. The two workarounds stay, both still reproduce on 2.10.13: - The `gofmt -l -w .` override, because the synthesized `go fmt ./apps/api/... ./packages/core/...` still fails at a workspace root. - The per-app `buildCommand` in each `vercel.json`, because every `turbo run` still requires `go` on PATH, which the Next.js build image does not have. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01ULAxKvHY6VD6aPxmsop4ni --- bun.lock | 16 ++++++++-------- package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/bun.lock b/bun.lock index fb8c5cd..3baa7da 100644 --- a/bun.lock +++ b/bun.lock @@ -10,7 +10,7 @@ "oxfmt": "^0.62.0", "oxlint": "^1.77.0", "react-doctor": "^0.9.5", - "turbo": "2.10.13-canary.4", + "turbo": "^2.10.13", "typescript": "^7.0.2", "ultracite": "7.10.1", }, @@ -764,17 +764,17 @@ "@ts-morph/common": ["@ts-morph/common@0.28.1", "", { "dependencies": { "minimatch": "^10.0.1", "path-browserify": "^1.0.1", "tinyglobby": "^0.2.14" } }, "sha512-W74iWf7ILp1ZKNYXY5qbddNaml7e9Sedv5lvU1V8lftlitkc9Pq1A+jlH23ltDgWYeZFFEqGCD1Ies9hqu3O+g=="], - "@turbo/darwin-64": ["@turbo/darwin-64@2.10.13-canary.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-yxQKvG/PLHZq/pngKRtso8QDfJgZBN0UK5CHvOX7+B8s3Cw+V1lkf4dBnWhHsiCW3HAJ7DL9ZsbWZC1Ie+JEzw=="], + "@turbo/darwin-64": ["@turbo/darwin-64@2.10.13", "", { "os": "darwin", "cpu": "x64" }, "sha512-m5DBcpkxmcKpEJHHFUfqf9GLUjSmw3cDnBJM9nghPW3kjCNb5OyNKmzG7/yT5x5AwzOO+HMwKN+UI4iVKL87YA=="], - "@turbo/darwin-arm64": ["@turbo/darwin-arm64@2.10.13-canary.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-V1O1hC4TAQGQ+5KnoGcnBrugW2JoIEQ2aLZW3tLOf9N5tJMgQgQZCa2dUGWCB0qZuaOOIqPNxp4jVMHesnVJiQ=="], + "@turbo/darwin-arm64": ["@turbo/darwin-arm64@2.10.13", "", { "os": "darwin", "cpu": "arm64" }, "sha512-vfKPvSuoY8BJGj8M8yN+a7aq1g+CejbEacDSDlJLDkSbPN3J/U1DbYAmPG8Ype5XvImI0SgfvUhIorNnFj67zw=="], - "@turbo/linux-64": ["@turbo/linux-64@2.10.13-canary.4", "", { "os": [ "linux", "android", ], "cpu": "x64" }, "sha512-CcB/6TelfKxeGBVEr2Kg9uwNxv0AxkNpxm6wCxlZuduOpIT3kD7s01W3/DH6Vy5BK+EfG//7Ptx/tYXX0nFUtA=="], + "@turbo/linux-64": ["@turbo/linux-64@2.10.13", "", { "os": [ "linux", "android", ], "cpu": "x64" }, "sha512-Zp0N5M6j9iEjJtFLTA2L+52TLuYy0VbXT3zLZzfMeqqdKSUJ7pDo2pA1NhqjoQcF2dIsktZ8xZRW3pGI6T81fw=="], - "@turbo/linux-arm64": ["@turbo/linux-arm64@2.10.13-canary.4", "", { "os": [ "linux", "android", ], "cpu": "arm64" }, "sha512-gRdJwNZEWQdeZZ5y7QsfR+9hl2XyI8R2l7h3AaKaAo+isUJiQs2/kZTvo656rkxTx+1QDrJnBXkIC73nzWrq0g=="], + "@turbo/linux-arm64": ["@turbo/linux-arm64@2.10.13", "", { "os": [ "linux", "android", ], "cpu": "arm64" }, "sha512-M8XcfWdS2D4EP3wzd5ehUyCANucMIOsRE6gZuV0Mr2NIn+FpsUjXFCh7r9HY7GnNcjXH4tUnT76OsCDg2c0Fng=="], - "@turbo/windows-64": ["@turbo/windows-64@2.10.13-canary.4", "", { "os": "win32", "cpu": "x64" }, "sha512-cm/8u5MIkgruBp5GmKEvTcv7qK6KWwyz4+QCG9Kb9dHRR/C3bFaVfwvf5vDoY6sOpOGO2M/E+ao2gsHdWVfg1Q=="], + "@turbo/windows-64": ["@turbo/windows-64@2.10.13", "", { "os": "win32", "cpu": "x64" }, "sha512-QFd3KBMpBBCc31TbxMnxgdBivie8lxJXV8EqmanH8fg8l2e1BdCLWpZJbxk48VNvcF4LHUnwO0FJOq+veKN1cw=="], - "@turbo/windows-arm64": ["@turbo/windows-arm64@2.10.13-canary.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-g58NPmqZtECuGXVoo8UHSbZZPIl27SWWR6nM0CMFi8WG/G00j3+n+M9xgmY4V/zsJRFjkLt6rmU6FUKp4w/+/A=="], + "@turbo/windows-arm64": ["@turbo/windows-arm64@2.10.13", "", { "os": "win32", "cpu": "arm64" }, "sha512-rM+cFO1P8iAHXdasOdMyK9nDk0UGII7VEFLIA8+jwv/+rh48pnVBbF2OSjJDBOw57pxNLIOEDA0JyKcarTBcIw=="], "@tybys/wasm-util": ["@tybys/wasm-util@0.10.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg=="], @@ -1954,7 +1954,7 @@ "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "turbo": ["turbo@2.10.13-canary.4", "", { "optionalDependencies": { "@turbo/darwin-64": "2.10.13-canary.4", "@turbo/darwin-arm64": "2.10.13-canary.4", "@turbo/linux-64": "2.10.13-canary.4", "@turbo/linux-arm64": "2.10.13-canary.4", "@turbo/windows-64": "2.10.13-canary.4", "@turbo/windows-arm64": "2.10.13-canary.4" }, "bin": { "turbo": "bin/turbo" } }, "sha512-BYerLB66rlUvOIrezCwvDaZoMSdR15MltuKCnNkah/UwvC8vnkXImEq3i9LB+0DUmUxG3ygxfDwd0H1psOb1/Q=="], + "turbo": ["turbo@2.10.13", "", { "optionalDependencies": { "@turbo/darwin-64": "2.10.13", "@turbo/darwin-arm64": "2.10.13", "@turbo/linux-64": "2.10.13", "@turbo/linux-arm64": "2.10.13", "@turbo/windows-64": "2.10.13", "@turbo/windows-arm64": "2.10.13" }, "bin": { "turbo": "bin/turbo" } }, "sha512-69MkPbjk+G8oyC7pC2DeBk0rPdbLz51wuH5eAT0Q4BVJkzq/b1XM4kUUidsARbmDjwIELSJwd1ezITULY9kWNw=="], "tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="], diff --git a/package.json b/package.json index 007b24e..c5baa35 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "oxfmt": "^0.62.0", "oxlint": "^1.77.0", "react-doctor": "^0.9.5", - "turbo": "2.10.13-canary.4", + "turbo": "^2.10.13", "typescript": "^7.0.2", "ultracite": "7.10.1" },