Skip to content

fix(cli): convert to ESM to support @clack/prompts v1.x#206

Merged
jlia0 merged 1 commit intomainfrom
jlia0/fix-clack-esm-import
Mar 12, 2026
Merged

fix(cli): convert to ESM to support @clack/prompts v1.x#206
jlia0 merged 1 commit intomainfrom
jlia0/fix-clack-esm-import

Conversation

@jlia0
Copy link
Copy Markdown
Collaborator

@jlia0 jlia0 commented Mar 12, 2026

Description

Fixes #205 by converting the CLI package from CommonJS to ESM, allowing use of @clack/prompts v1.x instead of the legacy v0.11.0.

Changes

  • Add "type": "module" to packages/cli/package.json
  • Update tsconfig to use Node16 module resolution with rewriteRelativeImportExtensions
  • Update relative imports to use explicit .ts extensions (automatically rewritten to .js at build time)

Testing

  • Build completes without errors
  • All 7 source files compile to proper ESM output

🤖 Generated with Claude Code

Switch the CLI package from CommonJS to ESM to resolve the ERR_REQUIRE_ESM error when importing @clack/prompts v1.x. This enables use of the latest version instead of being pinned to v0.11.0.

Changes:
- Add "type": "module" to package.json
- Update tsconfig to use Node16 module resolution with rewriteRelativeImportExtensions
- Update relative imports to use explicit .ts extensions (rewritten to .js at build time)

Co-Authored-By: Claude Haiku 4.5 <[email protected]>
@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Mar 12, 2026

Greptile Summary

This PR converts packages/cli from CommonJS to ESM by adding "type": "module" to its package.json, switching the TypeScript compiler to "module": "Node16" with rewriteRelativeImportExtensions: true, and updating all relative imports to use explicit .ts extensions (which TypeScript rewrites to .js at build time). This is the correct approach to unblock the use of @clack/prompts v1.x, which dropped CJS support.

Key observations:

  • All 5 source files that import from ./shared have been correctly updated to ./shared.ts — no files were missed (the other two CLI source files, messaging.ts and pairing.ts, do not import from ./shared).
  • rewriteRelativeImportExtensions: true is a TypeScript 5.7+ feature; the workspace uses TypeScript 5.9.3, so this is compatible.
  • The ESM CLI imports from @tinyclaw/core (CJS, no "type": "module"). Node.js supports ESM → CJS interop natively, so this works at runtime.
  • The "main": "dist/shared.js" field in packages/cli/package.json still points to an internal utility module rather than a real package entry. With ESM, any synchronous require('@tinyclaw/cli') from external callers would now fail. This is pre-existing but worth addressing.
  • @tinyclaw/core lacks an "exports" field, which becomes more relevant under the stricter moduleResolution: "Node16" now applied to the CLI. TypeScript falls back to "main" and the build succeeds, but this may cause friction with bundlers and other tooling.

Confidence Score: 4/5

  • This PR is safe to merge — the ESM conversion is mechanically correct and the build succeeds, with only minor package hygiene issues to follow up on.
  • The changes are minimal and well-scoped: one config change, one package.json field, and five identical one-line import-path fixes. All relative imports to ./shared were updated correctly. The ESM → CJS runtime interop with @tinyclaw/core is handled natively by Node.js. The two style-level concerns ("main" pointing to an internal module, and @tinyclaw/core lacking an "exports" field) are pre-existing issues made more visible by this PR but do not block functionality.
  • packages/cli/package.json and packages/cli/tsconfig.json are the most impactful files; the source file changes are trivial one-liners.

Important Files Changed

Filename Overview
packages/cli/package.json Adds "type": "module" and bumps @clack/prompts to v1.1.0; "main" still points to dist/shared.js (internal utility) rather than a real entry point, and there is no "bin" field.
packages/cli/tsconfig.json Overrides base module settings with "module": "Node16", "moduleResolution": "Node16", and adds rewriteRelativeImportExtensions: true (TS 5.7+). Correct for ESM output, but the referenced @tinyclaw/core CJS package lacks an "exports" field which may cause strict-mode resolution friction.
packages/cli/src/agent.ts Only change is updating the import specifier from ./shared to ./shared.ts for Node16 ESM compatibility — correct.
packages/cli/src/provider.ts Import specifier updated from ./shared to ./shared.ts — correct change, no other modifications.
packages/cli/src/setup-wizard.ts Import specifier updated from ./shared to ./shared.ts — correct change, no other modifications.
packages/cli/src/team.ts Import specifier updated from ./shared to ./shared.ts — correct change, no other modifications.
packages/cli/src/update.ts Import specifier updated from ./shared to ./shared.ts — correct change, no other modifications.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    subgraph CLI ["packages/cli (ESM — type: module)"]
        A["agent.ts / provider.ts\nsetup-wizard.ts / team.ts\nupdate.ts"]
        B["shared.ts\n(import rewritten to shared.js at build)"]
        A -->|"import from './shared.ts'\n→ emits './shared.js'"| B
    end

    subgraph Core ["packages/core (CJS — no type: module)"]
        C["dist/index.js\n(CommonJS output)"]
    end

    subgraph Clack ["@clack/prompts v1.x (ESM-only)"]
        D["ESM-only package\n(requires ESM host)"]
    end

    B -->|"ESM → CJS interop\n(Node.js native)"| C
    A -->|"ESM → CJS interop\n(Node.js native)"| C
    A -->|"ESM import\n(now compatible)"| D

    subgraph TS ["TypeScript 5.9.3 — module: Node16"]
        E["rewriteRelativeImportExtensions: true\n.ts → .js in emitted output"]
    end

    CLI -.->|compiled by| TS
Loading

Last reviewed commit: 950984c

"name": "@tinyclaw/cli",
"version": "0.0.10",
"type": "module",
"main": "dist/shared.js",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

main field is misleading for an ESM CLI package

The "main": "dist/shared.js" entry points to an internal utility module rather than a primary package entry. Now that the package is ESM ("type": "module"), any external require('@tinyclaw/cli') call will fail because ESM cannot be synchronously required. For a CLI-only package this is generally fine at runtime (since nothing imports it), but it would be cleaner to either:

  • Remove "main" entirely if the package is not meant to be imported programmatically, or
  • Add a proper "exports" field mapping to communicate intent and support Node16 module resolution tooling properly.

Additionally, the absence of a "bin" field means the CLI scripts cannot be installed globally via npm — this was already the case before this PR, but converting to ESM is a good opportunity to revisit the package's public shape.

Comment on lines +6 to +8
"module": "Node16",
"moduleResolution": "Node16",
"rewriteRelativeImportExtensions": true
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tinyclaw/core lacks "exports" field required for clean Node16 resolution

With "moduleResolution": "Node16", TypeScript (and Node.js at runtime) prefer packages to declare a "exports" field so the module system knows exactly which format (CJS/ESM) is being published and which entry points are available. The referenced @tinyclaw/core package has no "exports" field — only a "main" field — while remaining a CommonJS package (no "type": "module").

TypeScript falls back to "main" in this case and the build will succeed, but strict Node16/NodeNext tooling (bundlers, tsc --build with declaration emit, newer Jest/Vitest configs) may warn or fail to resolve the package correctly. The safer approach is to add an "exports" field to packages/core/package.json:

"exports": {
  ".": {
    "require": "./dist/index.js",
    "types": "./dist/index.d.ts"
  }
}

This is not introduced by this PR per se, but the move to moduleResolution: "Node16" in the CLI makes the gap visible.

@jlia0 jlia0 merged commit b7e8a42 into main Mar 12, 2026
1 check passed
@jlia0 jlia0 deleted the jlia0/fix-clack-esm-import branch March 12, 2026 22:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: problem importing @clack/prompts

1 participant