@macalinao/codama-renderers-js-esm
ESM-native TypeScript renderer for Codama JavaScript code generation
This package extends Codama's JavaScript renderer to produce fully ESM-compatible TypeScript/JavaScript code with proper module resolution and improved type safety.
Installation
bun add -D @macalinao/codama-renderers-js-esm
Or with npm:
npm install -D @macalinao/codama-renderers-js-esm
Why Use This Package?
While @codama/renderers-js
generates excellent TypeScript code, it produces CommonJS-style imports that may not work correctly in pure ESM environments. This package addresses:
- ESM Module Resolution: Adds
.js
extensions to all relative imports - TypeScript ESM Compatibility: Works correctly with
"type": "module"
- Production-Ready Code: Removes Node.js-specific environment checks
- Type Safety Improvements: Enhanced type assertions and null checks
Usage
Basic Usage
Use this visitor in place of the standard Codama JavaScript renderer:
import { renderESMTypeScriptVisitor } from "@macalinao/codama-renderers-js-esm";
import { rootNode, visit } from "codama";
// Your Codama root node (typically from parsing an IDL)
const root = rootNode(/* ... */);
// Generate ESM-compatible TypeScript code
const visitor = renderESMTypeScriptVisitor("./src/generated");
visit(root, visitor);
With Coda Configuration
This renderer is used by default in Coda, but you can explicitly configure it:
import { defineConfig } from "@macalinao/coda";
import { renderESMTypeScriptVisitor } from "@macalinao/codama-renderers-js-esm";
export default defineConfig({
idlPath: "./idls/my_program.json",
outputDir: "./src/generated",
visitors: ({ idl }) => {
// Custom rendering configuration
const visitor = renderESMTypeScriptVisitor("./src/generated", {
// Custom options if needed
});
return [visitor];
},
});
Key Features
1. Automatic Import Extensions
All relative imports automatically get .js
extensions:
Before (Standard Renderer)
import { Address } from "@solana/web3.js";
import { SomeType } from "./types";
import { decodeAccount } from "./accounts";
export * from "./instructions";
After (ESM Renderer)
import { Address } from "@solana/web3.js";
import { SomeType } from "./types/index.js";
import { decodeAccount } from "./accounts/index.js";
export * from "./instructions/index.js";
2. Directory Import Resolution
Bare directory imports are converted to explicit index imports:
// Before
import { something } from "./utils";
// After
import { something } from "./utils/index.js";
3. Strict Null Checks
Loose equality checks are replaced with strict checks:
// Before
if (value == null) {
/* ... */
}
// After
if (value === null || value === undefined) {
/* ... */
}
4. Universal JavaScript Compatibility
Removes Node.js-specific code for broader runtime support:
// Removed: Node.js-specific environment checks
// Works in: Browser, Deno, Bun, Node.js, etc.
Comparison with Standard Renderer
Feature | @codama/renderers-js | @macalinao/codama-renderers-js-esm |
---|---|---|
Module Format | CommonJS-compatible | Pure ESM |
Import Extensions | No extensions | .js extensions |
TypeScript Config | Standard | ESM-native |
Runtime Checks | Node.js-specific | Universal |
Null Checks | Loose (== ) | Strict (=== ) |
Tree Shaking | Good | Excellent |
Configuration
TypeScript Configuration
Ensure your tsconfig.json
is configured for ESM:
{
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"target": "ES2024",
"lib": ["ES2024"]
}
}
Package.json Configuration
Your package.json should specify ESM:
{
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
}
}
API Reference
renderESMTypeScriptVisitor
Creates a visitor that renders ESM-compatible TypeScript:
function renderESMTypeScriptVisitor(
outputDir: string,
options?: RenderOptions
): Visitor;
interface RenderOptions {
// Inherits all options from @codama/renderers-js
// Additional ESM-specific options may be added
}
Parameters:
outputDir
: Directory where files will be generatedoptions
: Optional configuration for the renderer
How It Works
The package applies a series of transformations:
1. Custom Dependency Map
Provides ESM-specific import paths:
{
errors: "../errors/index.js",
generatedAccounts: "../accounts/index.js",
generatedInstructions: "../instructions/index.js",
generatedTypes: "../types/index.js",
generatedPdas: "../pdas/index.js",
hooked: "../../hooked/index.js",
shared: "../shared/index.js"
}
2. Import Path Transformations
- Adds
.js
to imports without extensions - Converts directory imports to
index.js
- Preserves external package imports
3. Code Quality Improvements
- Replaces loose equality with strict equality
- Improves type assertions
- Removes runtime-specific checks
Best Practices
- Use with ESM Projects: This renderer is designed for ESM-first projects
- Configure TypeScript Properly: Ensure your tsconfig supports ESM
- Test in Target Environment: Verify generated code works in your runtime
- Combine with Other Visitors: Works well with other Codama visitors
Troubleshooting
Import Errors
If you see "Cannot find module" errors:
- Verify all imports have
.js
extensions - Check that
"type": "module"
is in package.json - Ensure TypeScript module resolution is correct
Build Tool Compatibility
Works with modern build tools:
- Vite: Full support out of the box
- esbuild: Works with ESM configuration
- Rollup: Native ESM support
- Webpack 5: Configure for ESM output
Related Packages
- @macalinao/coda - Main CLI using this renderer by default
- @codama/renderers-js - Original JavaScript renderer
- @macalinao/codama-renderers-markdown - Markdown documentation renderer