Skip to content

Commit 43f0f4c

Browse files
committed
chore: release v0.9.0 - superjson support and ESM fix
1 parent e265577 commit 43f0f4c

File tree

7 files changed

+129
-5
lines changed

7 files changed

+129
-5
lines changed

CHANGELOG.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,32 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
66
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.9.0] - 2026-04-08
9+
10+
### Added
11+
12+
- **SuperJSON Transformer Support** - The test playground now fully supports tRPC routers using the
13+
`superjson` transformer. Configure with `generateDocsHtml(routes, { transformer: 'superjson' })`
14+
to automatically:
15+
- Wrap request inputs in `{ json: ... }` format expected by superjson
16+
- Unwrap response data from `{ result: { data: { json: ... } } }` structure
17+
- Enable seamless testing of endpoints that return `Date` objects, `undefined`, `BigInt`, `Map`,
18+
`Set`, and other non-JSON types
19+
- See the new "SuperJSON Support" section in README for complete usage guide
20+
21+
### Fixed
22+
23+
- **Node.js Native ESM Compatibility** - Package now works correctly with Node.js native ESM
24+
resolution (Node 16+, Node 22+):
25+
- All relative imports now include explicit `.js` file extensions as required by the ECMAScript
26+
spec
27+
- Updated TypeScript config to use `"moduleResolution": "node16"` and `"module": "Node16"` for
28+
proper ESM compliance
29+
- Resolves `ERR_MODULE_NOT_FOUND` errors when importing the package without a bundler
30+
- Fully backward compatible with existing bundler-based workflows (Vite, webpack, Rollup, etc.)
31+
32+
---
33+
834
## [0.8.0] - 2026-04-04
935

1036
### Added

README.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,10 @@ npm install trpc-docs-generator
137137

138138
- `@trpc/server` ^11.0.0
139139
- Zod v4+ (with `toJSONSchema` support)
140+
- Node.js 16+ (with native ESM support)
141+
142+
**Note:** This package uses native ESM with explicit `.js` file extensions in imports, ensuring
143+
compatibility with Node.js native module resolution (Node 16+, Node 22+).
140144

141145
<br/>
142146

@@ -407,6 +411,10 @@ Generates a complete HTML documentation page from route information.
407411
- `routes` - Array of route information from `collectRoutes()`
408412
- `options` - Optional configuration:
409413
- `title` - Page title (default: `'API Documentation'`)
414+
- `transformer` - Data transformer used by tRPC router (optional)
415+
- Set to `'superjson'` if your router uses the superjson transformer
416+
- This ensures the test playground correctly wraps requests/responses in `{json: ...}` format
417+
- See [SuperJSON Support](#superjson-support) for details
410418

411419
**Returns:**
412420

@@ -438,6 +446,67 @@ type RouteMeta = {
438446

439447
<br/>
440448

449+
## SuperJSON Support
450+
451+
If your tRPC router uses the [superjson transformer](https://trpc.io/docs/data-transformers), you
452+
need to configure the docs generator to match. SuperJSON wraps requests/responses in a special
453+
format (`{json: ...}`) to support JavaScript types that standard JSON doesn't handle (Date,
454+
undefined, BigInt, Map, Set, etc.).
455+
456+
### Usage with SuperJSON
457+
458+
```typescript
459+
import { initTRPC } from '@trpc/server';
460+
import superjson from 'superjson';
461+
import { collectRoutes, generateDocsHtml } from 'trpc-docs-generator';
462+
463+
// Your tRPC router with superjson transformer
464+
const t = initTRPC.create({
465+
transformer: superjson // Using superjson
466+
});
467+
468+
const appRouter = t.router({
469+
getEvent: t.procedure.input(z.object({ id: z.string() })).query(() => ({
470+
id: '123',
471+
name: 'Conference',
472+
startDate: new Date() // Date objects work with superjson!
473+
}))
474+
});
475+
476+
// Generate docs WITH superjson support
477+
const routes = collectRoutes(appRouter);
478+
const html = generateDocsHtml(routes, {
479+
title: 'My API Documentation',
480+
transformer: 'superjson' // Enable superjson in test playground
481+
});
482+
```
483+
484+
### What This Does
485+
486+
**Without** `transformer: 'superjson'`:
487+
488+
- Request: `{"id": "123"}`
489+
- Your superjson-enabled tRPC server expects: `{"json": {"id": "123"}}`
490+
- Result: ❌ **Request fails** with "Invalid input: expected object, received undefined"
491+
492+
**With** `transformer: 'superjson'`:
493+
494+
- Request: `{"json": {"id": "123"}}`
495+
- Response unwrapping: Automatically extracts data from `{result: {data: {json: {...}}}}`
496+
- Result: ✅ **Everything works perfectly**
497+
498+
### When to Use
499+
500+
Enable `transformer: 'superjson'` if your tRPC router:
501+
502+
- Uses `superjson` transformer in `initTRPC.create({ transformer: superjson })`
503+
- Returns Date objects, undefined values, BigInt, Map, Set, or other non-JSON types
504+
- Is configured with any custom transformer that wraps data
505+
506+
If you're using standard JSON (no transformer), you don't need this option.
507+
508+
<br/>
509+
441510
## Examples
442511

443512
### Full Featured Router

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "trpc-docs-generator",
3-
"version": "0.8.0",
3+
"version": "0.9.0",
44
"author": "Lior Cohen",
55
"homepage": "https://github.com/liorcodev/trpc-docs-generator#readme",
66
"repository": {
@@ -19,6 +19,7 @@
1919
"@types/node": "^25.5.0",
2020
"prettier": "^3.8.1",
2121
"sharp": "^0.34.5",
22+
"superjson": "^2.2.6",
2223
"typescript": "^5.9.3",
2324
"zod": "^4.3.6"
2425
},

src/assets-inline.ts

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

src/generate-html.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { getStyles, getScripts, getLogo } from './assets-inline.js';
99
* @returns HTML string
1010
*/
1111
export function generateDocsHtml(routes: RouteInfo[], options: DocsGeneratorOptions = {}): string {
12-
const { title = 'API Documentation' } = options;
12+
const { title = 'API Documentation', transformer } = options;
1313
const groupedRoutes = groupRoutesByPrefix(routes);
1414

1515
return `<!DOCTYPE html>
@@ -211,6 +211,9 @@ export function generateDocsHtml(routes: RouteInfo[], options: DocsGeneratorOpti
211211
</div>
212212
213213
<script>
214+
// tRPC transformer configuration
215+
window.TRPC_TRANSFORMER = ${transformer ? `'${transformer}'` : 'undefined'};
216+
214217
${getScripts()}
215218
</script>
216219
</body>

src/scripts.js

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -388,11 +388,17 @@ async function testEndpoint(routeId, path, type) {
388388
headers: headers
389389
};
390390

391+
// Wrap input data if using superjson transformer
392+
let serializedInput = inputData;
393+
if (window.TRPC_TRANSFORMER === 'superjson' && inputData !== null && inputData !== undefined) {
394+
serializedInput = { json: inputData };
395+
}
396+
391397
if (method === 'GET' && inputData) {
392-
const params = new URLSearchParams({ input: JSON.stringify(inputData) });
398+
const params = new URLSearchParams({ input: JSON.stringify(serializedInput) });
393399
url = `${url}?${params}`;
394400
} else if (method === 'POST') {
395-
fetchOptions.body = JSON.stringify(inputData);
401+
fetchOptions.body = JSON.stringify(serializedInput);
396402
}
397403

398404
const response = await fetch(url, fetchOptions);
@@ -401,6 +407,19 @@ async function testEndpoint(routeId, path, type) {
401407
const contentType = response.headers.get('content-type');
402408
if (contentType && contentType.includes('application/json')) {
403409
data = await response.json();
410+
// Unwrap superjson response if configured
411+
if (
412+
window.TRPC_TRANSFORMER === 'superjson' &&
413+
data &&
414+
typeof data === 'object' &&
415+
'result' in data &&
416+
data.result &&
417+
'data' in data.result &&
418+
data.result.data &&
419+
'json' in data.result.data
420+
) {
421+
data = data.result.data.json;
422+
}
404423
} else {
405424
const text = await response.text();
406425
data = { message: text || 'No response body' };

src/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,10 @@ export type RouteMeta = {
2626
export type DocsGeneratorOptions = {
2727
/** Title to display in the documentation page */
2828
title?: string;
29+
/**
30+
* Data transformer used by tRPC router
31+
* Set to 'superjson' if your router uses superjson transformer
32+
* This wraps requests/responses in {json: ...} format
33+
*/
34+
transformer?: 'superjson';
2935
};

0 commit comments

Comments
 (0)