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..0078a44 100644 --- a/README.md +++ b/README.md @@ -10,16 +10,18 @@ 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 share one task graph and one cache. ## 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 +Two apps and two shared packages, split across a Bun workspace and a Go workspace: + ```text pigo/ ├── apps/ @@ -28,9 +30,21 @@ 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 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 +``` + +`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 @@ -39,7 +53,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/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" +} diff --git a/bun.lock b/bun.lock index aba5e3b..3baa7da 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", "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", "", { "os": "darwin", "cpu": "x64" }, "sha512-m5DBcpkxmcKpEJHHFUfqf9GLUjSmw3cDnBJM9nghPW3kjCNb5OyNKmzG7/yT5x5AwzOO+HMwKN+UI4iVKL87YA=="], - "@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", "", { "os": "darwin", "cpu": "arm64" }, "sha512-vfKPvSuoY8BJGj8M8yN+a7aq1g+CejbEacDSDlJLDkSbPN3J/U1DbYAmPG8Ype5XvImI0SgfvUhIorNnFj67zw=="], - "@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", "", { "os": [ "linux", "android", ], "cpu": "x64" }, "sha512-Zp0N5M6j9iEjJtFLTA2L+52TLuYy0VbXT3zLZzfMeqqdKSUJ7pDo2pA1NhqjoQcF2dIsktZ8xZRW3pGI6T81fw=="], - "@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", "", { "os": [ "linux", "android", ], "cpu": "arm64" }, "sha512-M8XcfWdS2D4EP3wzd5ehUyCANucMIOsRE6gZuV0Mr2NIn+FpsUjXFCh7r9HY7GnNcjXH4tUnT76OsCDg2c0Fng=="], - "@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", "", { "os": "win32", "cpu": "x64" }, "sha512-QFd3KBMpBBCc31TbxMnxgdBivie8lxJXV8EqmanH8fg8l2e1BdCLWpZJbxk48VNvcF4LHUnwO0FJOq+veKN1cw=="], - "@turbo/windows-arm64": ["@turbo/windows-arm64@2.10.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-VN30vh3b3Czh2WzYHNTfF1FE0YMZ5aHsLO8dBMGHJewA6792wX6iJR8ZxlzFW6WdOu0gEAKIvlYhfyT81Wkm4Q=="], + "@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=="], @@ -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", "", { "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 1d455e2..c5baa35 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", "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", "."] + } } } }