crossplane / crossplane/function-sdk-typescript

ts-proto ships 25MB of build-time codegen into every function image; @bufbuild/protobuf is undeclared

Open
#36 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
3
Forks
2
Avg merge
9h 49m
Merged PRs (30d)
14

Description

### What happened?

Three of the SDK's seven runtime `dependencies` are not imported by the shipped code, and one dependency that *is* imported at runtime is not declared at all. The largest consequence is that `ts-proto` — a build-time `protoc` plugin — drags ~25MB of native binary into every function image built against the SDK.

### Evidence

`ts-proto` is used only as a `protoc` plugin, in `scripts/protoc-gen.sh`:

```
--plugin=./node_modules/.bin/protoc-gen-ts_proto
```

It is imported nowhere in `src/`, and nothing in a fully installed function tree imports it either (grepped every `.js`/`.mjs`/`.cjs`). But it is in `dependencies`, so it ships — and it brings a formatter with it:

```
@crossplane-org/function-sdk-typescript
└─ ts-proto (908K)
└─ ts-poet (216K)
└─ dprint-node (24M) <- native binary, does not compress
```

Mapping each declared runtime dependency to actual imports in `src/`:

| dependency | files importing it | verdict |
|---|---|---|
| `@grpc/grpc-js` | 4 | runtime |
| `pino` | 4 | runtime |
| `ts-deepmerge` | 2 | runtime |
| `kubernetes-models` | 1 | runtime |
| `ts-proto` | 0 | build-time only (protoc plugin) |
| `google-protobuf` | 0 | appears unused |
| `@grpc/proto-loader` | 0 | unused directly; `@grpc/grpc-js` depends on it anyway |
| `@bufbuild/protobuf` | 3 | **runtime, but undeclared** |

### The undeclared dependency

The ts-proto-generated wire code imports `@bufbuild/protobuf`:

```
src/proto/run_function.ts:8: import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
src/proto/google/protobuf/duration.ts:8: import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
src/proto/google/protobuf/struct.ts:8: import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
```

`@bufbuild/protobuf` is not in `package.json`. It resolves today only because `ts-proto` depends on it. **This makes the two changes order-dependent**: moving `ts-proto` to `devDependencies` without first declaring `@bufbuild/protobuf` would remove it from the runtime tree and break every function at import time.

### Proposed change

1. Add `@bufbuild/protobuf` to `dependencies` (currently resolving as 2.11.0). Do this first — on its own it is a correctness fix regardless of the rest.
2. Move `ts-proto` to `devDependencies`.
3. Drop `google-protobuf` and `@types/google-protobuf` if nothing needs them — they look like leftovers from the pre-ts-proto `--ts_out` path still commented out in `scripts/protoc-gen.sh`.
4. Optionally drop `@grpc/proto-loader` from `dependencies`; it is not imported directly and `@grpc/grpc-js` declares it, so this changes nothing on disk.

### Impact

Measured on a real function project (`configuration-aws-network-ts`), installed with `--omit=dev`:

- Removing the `ts-proto` → `ts-poet` → `dprint-node` chain took `node_modules` from **62MB to 37MB**, and the function still started and served requests.
- Dropping `google-protobuf` saves a further ~944K.

`dprint-node` is a native binary, so unlike the `.d.ts` bulk it does not compress away in the packaged layer — it is close to 25MB of image size for every TypeScript function, and it is streamed out of the build container on every build.

`protobufjs` (3.1M) is *not* removable this way; it arrives via `@grpc/proto-loader` ← `@grpc/grpc-js`.

### Caveat

The 62MB → 37MB check verified process startup, a served gRPC listener, and the static import graph. It did not run a full compose request against the stripped tree. Worth confirming before release.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.