feat: fix esm exports#1470
Conversation
✅ Deploy Preview for docsstablecoinstudio ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
- Refactor imports to use `type` keyword - update package.json for build process standardization - introduce tsup configuration for consistent TypeScript output formats Signed-off-by: skurzyp-blockydevs <stanislaw.kurzyp@blockydevs.com>
f7bf52b to
64592a1
Compare
3d5a03a to
ee309f2
Compare
…tories directory Signed-off-by: skurzyp-blockydevs <stanislaw.kurzyp@blockydevs.com>
ee309f2 to
993de25
Compare
|
❌ The last analysis has failed. |
|
Tested it and it improves the situation on the build side and allows the SDK package to compile cleanly in an ESM context. When using the SDK in a fully ESM project (e.g. This originates from a dynamic Key points
ContextThis was reproduced in an ESM-based plugin setup using:
Even with the fixes introduced in this PR, the runtime remains unusable in ESM projects due to the above error. ConclusionThis PR addresses part of the problem (build-time compatibility), but full ESM support is still blocked by:
|
Important
This PR is not solving the problem! Please see comment below with further findings.
Disclaimer
Please consider this PR as a suggested approach to resolving this issue. I'm not familiar with the codebase and not able to test if it functions correctly. This implmenetation helped me build a packages that works fine for ESM projects and I'll proceed with testing it while bulding the
stable-coin-studio-pluginfor Hedera Agent Kit.I'm aware that it changes multiple files and might have broke sth. Rationalities for those changes are presented below.
Related issue(s):
Fixes #1459, #1468
fix(sdk): migrate build system to
tsupfor ESM/CJS dual-package compatibilitySummary
Resolves critical ESM compatibility issues in
@hashgraph/stablecoin-npm-sdkthat prevented the package from being imported in any modern Node.js ESM project ("type": "module"). The fix migrates both the SDK and Contracts packages from rawtsc-based transpilation to atsup(esbuild) bundled build system, following established patterns from thehedera-agent-kitproject.Problem
The SDK build (
build/esm/) had three categories of bugs that made it impossible toimportin Node.js ESM:1. Missing
.jsextensions in 162+ relative importsNode.js ESM requires explicit file extensions in all relative import specifiers (Node.js docs). The
tscoutput omitted them entirely:2. Illegal
require()calls in ESM scopeHederaWalletConnectTransactionAdapter.tsused a CJSrequire()pattern guarded by atypeof windowcheck. In an ESM bundle,requireis not defined and throws aReferenceErrorat parse time, before any runtime check can execute:3. Broken directory imports via
exportswildcardsThe
@hashgraph/stablecoin-npm-contractspackage exposed"./typechain-types/*"wildcard exports, and the SDK imported a directory:This violates the Node.js
exportsspecification which does not support directory imports.Solution
Migrate both packages to
tsup, which usesesbuildinternally. The bundler:.jsextension requirement.mjsfiles for ESM output, removing any ambiguity with Node.js module detection@hashgraph/hedera-custodians-integration)Changes
packages/contractstsup.config.ts(new)typechain-types/index.tsandtypechain-types/factories/index.ts.mjs) + CJS (.js) with declaration files (.d.ts/.d.mts)package.jsonmain,module,typesfields to point todist/"./typechain-types/*"export with explicit named exports:"."→ main type index"./factories"→ factory contracts indexbuildscript:npm run compile && rimraf dist && tsupdevDependencies: addedtsuppackages/sdktsup.config.ts(new)Dual-configuration build:
es2022, outputdist/esm/index.mjsnode16, outputdist/cjs/index.jsexternal:['@hashgraph/stablecoin-npm-contracts'](workspace peer)noExternalpatterns for@hashgraph/*,@hiero-ledger/*,tsyringe,reflect-metadata— these are bundled in to work around broken ESM paths in their own outputspackage.jsonmain,module,typesto point todist/cjs/anddist/esm/exportswith modern nested structure:buildscript:rimraf dist && tsupcleanscript: targetsdist/instead ofbuild/devDependencies: addedtsup,@swc/core,@swc/helpers(required foremitDecoratorMetadatasupport)tsconfig.jsonmoduleResolutionfrom"node"to"bundler"(required fortsupcompatibility)outDirfrom./build/esmto./disttsup.config.tstoincludearray.eslintignoretsup.config.ts(not part oftsconfig.project, excluded from linting)dist/(build output must not be linted)Source Code Fixes
HederaWalletConnectTransactionAdapter.tsTraditional synchronous
require()calls and module-leveltypeof windowguards were replaced with inline dynamicimport()calls at the actual point of use.Benefits:
import()is natively supported in ESM and correctly handled by thetsupbundler.typeof window !== 'undefined'blocks at the module level.SupportedWalletscircular dependency was resolved by importing it directly from its source domain file instead of the SDK's public index.TransactionService.tsFixed the directory import to use the new explicit contracts export:
import type/export typefixes across public API filesSeveral public-facing files re-exported TypeScript
interfaces andtypealiases using plainimport/export. Whiletscis aware they are type-only and erases them silently, esbuild validates every import as a runtime binding and throwsNo matching exporterrors for constructs that produce no JavaScript output.Affected files and their types:
The fix in each case is to add the
typekeyword:.gitignoreAdded
**/dist/to root and package-level.gitignorefiles to exclude the newtsupoutput directory.Verification
After building with
npm run build:contracts && npm run build:sdk, the ESM bundle can be loaded in Node.js without errors:This was previously impossible and would throw
ERR_UNSUPPORTED_DIR_IMPORTorReferenceError: require is not defined.Notes
- The SWC compiler (
# fix(sdk): migrate build system to `tsup` for ESM/CJS dual-package compatibility@hiero-ledger/sdkis bundled (not externalized) into the SDK output as requested. This avoids runtime resolution failures caused by broken ESM paths in its ownnode_modulesoutput.@hashgraph/stablecoin-npm-contractsis externalized since it is a workspace peer and is always present alongside the SDK.@swc/core) is added as a dev dependency to correctly handleemitDecoratorMetadata, which is required for thetsyringeDI container used throughout the SDK.Summary
Resolves critical ESM compatibility issues in
@hashgraph/stablecoin-npm-sdkthat prevented the package from being imported in any modern Node.js ESM project ("type": "module"). The fix migrates both the SDK and Contracts packages from rawtsc-based transpilation to atsup(esbuild) bundled build system, following established patterns from thehedera-agent-kitproject.Problem
The SDK build (
build/esm/) had three categories of bugs that made it impossible toimportin Node.js ESM:1. Missing
.jsextensions in 162+ relative importsNode.js ESM requires explicit file extensions in all relative import specifiers ([Node.js docs](https://nodejs.org/api/esm.html#mandatory-file-extensions)). The
tscoutput omitted them entirely:2. Illegal
require()calls in ESM scopeHederaWalletConnectTransactionAdapter.tsused a CJSrequire()pattern guarded by atypeof windowcheck. In an ESM bundle,requireis not defined and throws aReferenceErrorat parse time, before any runtime check can execute:3. Broken directory imports via
exportswildcardsThe
@hashgraph/stablecoin-npm-contractspackage exposed"./typechain-types/*"wildcard exports, and the SDK imported a directory:This violates the Node.js
exportsspecification which does not support directory imports.Solution
Migrate both packages to
tsup, which usesesbuildinternally. The bundler:.jsextension requirement.mjsfiles for ESM output, removing any ambiguity with Node.js module detection@hashgraph/hedera-custodians-integration)Changes
packages/contractstsup.config.ts(new)typechain-types/index.tsandtypechain-types/factories/index.ts.mjs) + CJS (.js) with declaration files (.d.ts/.d.mts)package.jsonmain,module,typesfields to point todist/"./typechain-types/*"export with explicit named exports:"."→ main type index"./factories"→ factory contracts indexbuildscript:npm run compile && rimraf dist && tsupdevDependencies: addedtsuppackages/sdktsup.config.ts(new)Dual-configuration build:
es2022, outputdist/esm/index.mjsnode16, outputdist/cjs/index.jsexternal:['@hashgraph/stablecoin-npm-contracts'](workspace peer)noExternalpatterns for@hashgraph/*,@hiero-ledger/*,tsyringe,reflect-metadata— these are bundled in to work around broken ESM paths in their own outputspackage.jsonmain,module,typesto point todist/cjs/anddist/esm/exportswith modern nested structure:buildscript:rimraf dist && tsupcleanscript: targetsdist/instead ofbuild/devDependencies: addedtsup,@swc/core,@swc/helpers(required foremitDecoratorMetadatasupport)tsconfig.jsonmoduleResolutionfrom"node"to"bundler"(required fortsupcompatibility)outDirfrom./build/esmto./disttsup.config.tstoincludearray.eslintignoretsup.config.ts(not part oftsconfig.project, excluded from linting)dist/(build output must not be linted)Source Code Fixes
HederaWalletConnectTransactionAdapter.tsTraditional synchronous
require()calls and module-leveltypeof windowguards were replaced with inline dynamicimport()calls at the actual point of use.Benefits:
import()is natively supported in ESM and correctly handled by thetsupbundler.typeof window !== 'undefined'blocks at the module level.SupportedWalletscircular dependency was resolved by importing it directly from its source domain file instead of the SDK's public index.TransactionService.tsFixed the directory import to use the new explicit contracts export:
import type/export typefixes across public API filesSeveral public-facing files re-exported TypeScript
interfaces andtypealiases using plainimport/export. Whiletscis aware they are type-only and erases them silently, esbuild validates every import as a runtime binding and throwsNo matching exporterrors for constructs that produce no JavaScript output.Affected files and their types:
port/in/Account.tsStableCoinListViewModel,AccountViewModel(bothinterface)port/in/Event.tsWalletEvent(typealias)port/in/Network.tsInitializationData(interface)port/in/StableCoin.tsStableCoinViewModel,StableCoinListViewModel,PaginationViewModel(allinterface)port/in/response/index.tsConfigInfoViewModel,HoldViewModel(interface)port/in/request/ConnectRequest.tsBaseRequest,RequestAccount(interface)port/in/request/CreateRequest.tsRequestPublicKey(interface)port/in/request/UpdateRequest.tsRequestPublicKey(interface)port/in/request/GetTransactionsRequest.tsRequestPublicKey(interface)The fix in each case is to add the
typekeyword:.gitignoreAdded
**/dist/to root and package-level.gitignorefiles to exclude the newtsupoutput directory.Verification
After building with
npm run build:contracts && npm run build:sdk, the ESM bundle can be loaded in Node.js without errors:This was previously impossible and would throw
ERR_UNSUPPORTED_DIR_IMPORTorReferenceError: require is not defined.Notes
@hiero-ledger/sdkis bundled (not externalized) into the SDK output as requested. This avoids runtime resolution failures caused by broken ESM paths in its ownnode_modulesoutput.@hashgraph/stablecoin-npm-contractsis externalized since it is a workspace peer and is always present alongside the SDK.@swc/core) is added as a dev dependency to correctly handleemitDecoratorMetadata, which is required for thetsyringeDI container used throughout the SDK.Checklist