From c828fe97cb3e8bbac2846a90ba11143cfb1566e4 Mon Sep 17 00:00:00 2001 From: Radu Mojic Date: Wed, 24 Jun 2026 17:55:16 +0300 Subject: [PATCH 1/4] ship esm too --- eslint.config.mjs | 106 ------------------------------ jest.config.js | 5 ++ package.json | 56 ++++++++++++++-- pnpm-workspace.yaml | 4 ++ scripts/write-esm-package-json.js | 17 +++++ src/constants/index.ts | 4 +- src/enums/index.ts | 2 +- src/helpers/formatAmount.ts | 6 +- src/helpers/index.ts | 15 +++-- src/helpers/parseAmount.ts | 2 +- src/helpers/stringIsFloat.ts | 2 +- src/index.ts | 10 +-- src/models/dappProviderBase.ts | 2 +- src/models/index.ts | 2 +- src/types/index.ts | 2 +- tsconfig.base.json | 30 +++++++++ tsconfig.cjs.json | 9 +++ tsconfig.esm.json | 8 +++ tsconfig.json | 29 +------- 19 files changed, 150 insertions(+), 161 deletions(-) delete mode 100644 eslint.config.mjs create mode 100644 pnpm-workspace.yaml create mode 100644 scripts/write-esm-package-json.js create mode 100644 tsconfig.base.json create mode 100644 tsconfig.cjs.json create mode 100644 tsconfig.esm.json diff --git a/eslint.config.mjs b/eslint.config.mjs deleted file mode 100644 index b3bd4b6..0000000 --- a/eslint.config.mjs +++ /dev/null @@ -1,106 +0,0 @@ -import globals from 'globals'; -import pluginJs from '@eslint/js'; -import tseslint from 'typescript-eslint'; -import importPlugin from 'eslint-plugin-import'; -import prettierPlugin from 'eslint-plugin-prettier'; -import jestPlugin from 'eslint-plugin-jest'; - -/** @type {import('eslint').Linter.Config[]} */ -export default [ - { - files: ['**/*.{js,mjs,cjs,ts,tsx}', '**/*.{test,spec}.{js,ts,tsx}'], - languageOptions: { - globals: { - ...globals.browser, - ...globals.node, - ...globals.jest - }, - parser: tseslint.parser, - parserOptions: { - ecmaVersion: 2021, - sourceType: 'module', - ecmaFeatures: { - jsx: true - }, - project: './tsconfig.json' - } - }, - plugins: { - import: importPlugin, - prettier: prettierPlugin, - jest: jestPlugin, - '@typescript-eslint': tseslint.plugin - }, - settings: { - 'import/parsers': { - '@typescript-eslint/parser': ['.ts', '.tsx'] - }, - 'import/resolver': { - node: { - extensions: ['.js', '.jsx', '.ts', '.tsx'], - moduleDirectory: ['node_modules', 'src/'] - }, - typescript: { - alwaysTryTypes: true - } - } - }, - rules: { - ...jestPlugin.configs.recommended.rules, - ...pluginJs.configs.recommended.rules, - ...tseslint.configs.recommended.rules, - 'import/order': [ - 'warn', - { - groups: ['builtin', 'external', 'internal'], - pathGroups: [ - { - pattern: 'react', - group: 'external', - position: 'before' - } - ], - 'newlines-between': 'ignore', - alphabetize: { - order: 'asc', - caseInsensitive: true - } - } - ], - 'prettier/prettier': ['error', { endOfLine: 'lf' }], - '@typescript-eslint/indent': 'off', - '@typescript-eslint/explicit-module-boundary-types': 'off', - '@typescript-eslint/no-use-before-define': [ - 'error', - { functions: false, classes: false } - ], - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-empty-function': 'off', - '@typescript-eslint/no-unused-vars': [ - 'error', - { - argsIgnorePattern: '^_', - caughtErrorsIgnorePattern: '^_' - } - ], - '@typescript-eslint/no-var-requires': 'off', - '@typescript-eslint/explicit-function-return-type': 'off', - 'linebreak-style': ['error', 'unix'], - quotes: ['error', 'single'], - semi: ['error', 'always'], - 'no-unused-vars': ['off'], - 'no-prototype-builtins': 'off', - 'jest/no-mocks-import': 'off', - 'no-async-promise-executor': 'off', - 'object-curly-newline': 'off', - 'arrow-body-style': 'off', - 'implicit-arrow-linebreak': 'off', - 'func-names': 'off', - curly: ['error', 'all'], - 'operator-linebreak': 'off', - 'function-paren-newline': 'off', - 'no-shadow': 'off', - '@typescript-eslint/no-shadow': 'off' - } - } -]; diff --git a/jest.config.js b/jest.config.js index 37097f8..1eea8b4 100644 --- a/jest.config.js +++ b/jest.config.js @@ -5,6 +5,11 @@ module.exports = { moduleDirectories: ['node_modules', 'src'], modulePaths: ['/src'], roots: ['/src'], + // Source uses explicit .js extensions on relative imports (for native-Node ESM + // output), but the on-disk source is .ts — strip the extension so Jest resolves it. + moduleNameMapper: { + '^(\\.{1,2}/.*)\\.js$': '$1' + }, transform: { '^.+\\.(ts|js|tsx|jsx)$': ['@swc/jest'] }, diff --git a/package.json b/package.json index 861440e..79b8349 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,63 @@ { "name": "@multiversx/sdk-dapp-utils", - "version": "3.0.2", + "version": "3.1.0-alpha-0", "description": "SDK for DApp utilities", - "main": "out/index.js", - "types": "out/index.d.js", + "main": "./out/index.js", + "module": "./out/esm/index.js", + "types": "./out/index.d.ts", + "sideEffects": false, + "exports": { + ".": { + "types": "./out/index.d.ts", + "import": "./out/esm/index.js", + "require": "./out/index.js" + }, + "./out": { + "types": "./out/index.d.ts", + "import": "./out/esm/index.js", + "require": "./out/index.js" + }, + "./out/constants": { + "types": "./out/constants/index.d.ts", + "import": "./out/esm/constants/index.js", + "require": "./out/constants/index.js" + }, + "./out/helpers": { + "types": "./out/helpers/index.d.ts", + "import": "./out/esm/helpers/index.js", + "require": "./out/helpers/index.js" + }, + "./out/enums": { + "types": "./out/enums/index.d.ts", + "import": "./out/esm/enums/index.js", + "require": "./out/enums/index.js" + }, + "./out/models": { + "types": "./out/models/index.d.ts", + "import": "./out/esm/models/index.js", + "require": "./out/models/index.js" + }, + "./out/types": { + "types": "./out/types/index.d.ts", + "import": "./out/esm/types/index.js", + "require": "./out/types/index.js" + }, + "./out/*": { + "types": "./out/*.d.ts", + "import": "./out/esm/*.js", + "require": "./out/*.js" + }, + "./package.json": "./package.json" + }, "files": [ "out/**/*" ], "scripts": { "publish-verdaccio": "npm unpublish --registry http://localhost:4873 @multiversx/sdk-dapp-utils@3.0.0 && rm -rf out && yarn compile && npm publish --registry http://localhost:4873", - "compile": "tsc -p tsconfig.json", + "clean-out": "node -e \"require('fs').rmSync('out',{recursive:true,force:true})\"", + "build:cjs": "tsc -p tsconfig.cjs.json", + "build:esm": "tsc -p tsconfig.esm.json && node scripts/write-esm-package-json.js", + "compile": "npm run clean-out && npm run build:cjs && npm run build:esm", "pretest": "npm run compile", "prepare": "npm run compile", "test": "jest" diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..9d51d83 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,4 @@ +allowBuilds: + '@swc/core': false + keccak: false + protobufjs: false diff --git a/scripts/write-esm-package-json.js b/scripts/write-esm-package-json.js new file mode 100644 index 0000000..b9019df --- /dev/null +++ b/scripts/write-esm-package-json.js @@ -0,0 +1,17 @@ +// Marks out/esm as an ES module scope. +// +// tsc emits plain .js files. Without a "type": "module" marker (and with the +// root package defaulting to CommonJS), Node treats out/esm/*.js as CommonJS +// and throws on the `export` syntax in native Node / SSR. This writes the +// marker so the ESM build is interpreted as ESM. + +const fs = require('fs'); +const path = require('path'); + +const esmDir = path.resolve(__dirname, '..', 'out', 'esm'); + +fs.mkdirSync(esmDir, { recursive: true }); +fs.writeFileSync( + path.join(esmDir, 'package.json'), + JSON.stringify({ type: 'module' }, null, 2) + '\n' +); diff --git a/src/constants/index.ts b/src/constants/index.ts index fb51184..d0be18c 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -1,2 +1,2 @@ -export * from "./browserConstants"; -export * from './dappConstants'; +export * from "./browserConstants.js"; +export * from './dappConstants.js'; diff --git a/src/enums/index.ts b/src/enums/index.ts index c58d08e..d9d8fab 100644 --- a/src/enums/index.ts +++ b/src/enums/index.ts @@ -1 +1 @@ -export * from "./signMessageStatusEnum"; +export * from "./signMessageStatusEnum.js"; diff --git a/src/helpers/formatAmount.ts b/src/helpers/formatAmount.ts index 2faf2f8..c004f4f 100644 --- a/src/helpers/formatAmount.ts +++ b/src/helpers/formatAmount.ts @@ -1,7 +1,7 @@ import BigNumber from 'bignumber.js'; -import { DECIMALS, DIGITS, ZERO } from '../constants'; -import { stringIsInteger } from './stringIsInteger'; -import { pipe } from './pipe'; +import { DECIMALS, DIGITS, ZERO } from '../constants/index.js'; +import { stringIsInteger } from './stringIsInteger.js'; +import { pipe } from './pipe.js'; /** * Configuration options for formatting blockchain token amounts. diff --git a/src/helpers/index.ts b/src/helpers/index.ts index feb01a3..3ba797c 100644 --- a/src/helpers/index.ts +++ b/src/helpers/index.ts @@ -1,7 +1,8 @@ -export * from './formatAmount'; -export * from './isWindowAvailable'; -export * from './parseAmount'; -export * from './pipe'; -export * from './providerNotInitializedError'; -export * from './stringIsFloat'; -export * from './stringIsInteger'; +export * from './formatAmount.js'; +export * from './isWindowAvailable.js'; +export * from './parseAmount.js'; +export * from './pipe.js'; +export * from './providerNotInitializedError.js'; +export * from './recommendGasPrice.js'; +export * from './stringIsFloat.js'; +export * from './stringIsInteger.js'; diff --git a/src/helpers/parseAmount.ts b/src/helpers/parseAmount.ts index d9f86c6..38b2946 100644 --- a/src/helpers/parseAmount.ts +++ b/src/helpers/parseAmount.ts @@ -1,5 +1,5 @@ import BigNumber from 'bignumber.js'; -import { DECIMALS } from '../constants'; +import { DECIMALS } from '../constants/index.js'; export function parseAmount(amount: string, numDecimals = DECIMALS): string { const result = new BigNumber(amount).shiftedBy(numDecimals).decimalPlaces(0); diff --git a/src/helpers/stringIsFloat.ts b/src/helpers/stringIsFloat.ts index 223bd00..77cfe66 100755 --- a/src/helpers/stringIsFloat.ts +++ b/src/helpers/stringIsFloat.ts @@ -1,5 +1,5 @@ import BigNumber from 'bignumber.js'; -import { ZERO } from '../constants'; +import { ZERO } from '../constants/index.js'; export const stringIsFloat = (amount: string) => { if (isNaN(amount as any)) { diff --git a/src/index.ts b/src/index.ts index 6eae3ed..ac18d93 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ -export * from "./constants"; -export * from "./enums"; -export * from "./models"; -export * from "./types"; -export * from "./helpers"; +export * from "./constants/index.js"; +export * from "./enums/index.js"; +export * from "./models/index.js"; +export * from "./types/index.js"; +export * from "./helpers/index.js"; diff --git a/src/models/dappProviderBase.ts b/src/models/dappProviderBase.ts index f848539..3107ed6 100644 --- a/src/models/dappProviderBase.ts +++ b/src/models/dappProviderBase.ts @@ -1,5 +1,5 @@ import type { Transaction, Message } from "@multiversx/sdk-core"; -import type { Nullable } from "../types"; +import type { Nullable } from "../types/index.js"; export interface IDAppProviderOptions { [key: PropertyKey]: unknown; diff --git a/src/models/index.ts b/src/models/index.ts index b04fe0d..1787ee7 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -1 +1 @@ -export * from "./dappProviderBase"; +export * from "./dappProviderBase.js"; diff --git a/src/types/index.ts b/src/types/index.ts index d7b316f..f397691 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1 +1 @@ -export * from "./nullable"; +export * from "./nullable.js"; diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 0000000..09dea44 --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "es2015", + "lib": [ + "ES2015", + "DOM" + ], + "sourceMap": true, + "allowJs": true, + "strict": true, + "strictPropertyInitialization": true, + "strictNullChecks": true, + "skipLibCheck": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "noUnusedParameters": true, + "esModuleInterop": true, + "declaration": false + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules", + "out", + "**/*.spec.ts", + "**/*.test.ts", + "src/setupTests.js" + ] +} diff --git a/tsconfig.cjs.json b/tsconfig.cjs.json new file mode 100644 index 0000000..a801af8 --- /dev/null +++ b/tsconfig.cjs.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "module": "CommonJS", + "moduleResolution": "Node", + "outDir": "out", + "declaration": true + } +} diff --git a/tsconfig.esm.json b/tsconfig.esm.json new file mode 100644 index 0000000..26eed2a --- /dev/null +++ b/tsconfig.esm.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "module": "ESNext", + "moduleResolution": "Bundler", + "outDir": "out/esm" + } +} diff --git a/tsconfig.json b/tsconfig.json index 633530d..bfcfc35 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,30 +1,3 @@ { - "compilerOptions": { - "module": "CommonJS", - "target": "es2015", - "outDir": "out", - "lib": [ - "ES2015", - "DOM" - ], - "sourceMap": true, - "allowJs": true, - "strict": true, - "strictPropertyInitialization": true, - "strictNullChecks": true, - "skipLibCheck": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "noUnusedParameters": true, - "esModuleInterop": true, - "declaration": true - }, - "include": [ - "src/**/*" - ], - "exclude": [ - "node_modules", - "out", - "**/*.spec.ts" - ] + "extends": "./tsconfig.esm.json" } From 320a3c428eee3bde4cbfe9787fbcda49f8f26933 Mon Sep 17 00:00:00 2001 From: Radu Mojic Date: Wed, 24 Jun 2026 18:03:24 +0300 Subject: [PATCH 2/4] proper version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 79b8349..51da4e2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@multiversx/sdk-dapp-utils", - "version": "3.1.0-alpha-0", + "version": "3.1.0-alpha.0", "description": "SDK for DApp utilities", "main": "./out/index.js", "module": "./out/esm/index.js", From 73aa86146b72c1800568a2ae3ae163bb72bccdf9 Mon Sep 17 00:00:00 2001 From: Radu Mojic Date: Thu, 2 Jul 2026 17:18:18 +0300 Subject: [PATCH 3/4] use node 24 and actions @v6 --- .github/workflows/build.yml | 6 +++--- .github/workflows/package-lock-checks.yml | 6 +++--- .github/workflows/publish.yml | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 169b36f..971aa80 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,14 +14,14 @@ jobs: sudo chown -R $USER:$USER ${{ github.workspace }} sudo chmod -R 755 ${{ github.workspace }} continue-on-error: true - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ github.ref }} clean: true - name: Set up Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v6 with: - node-version: 18 + node-version: 24 - name: Setup pnpm run: npm install -g pnpm - name: Remove node_modules diff --git a/.github/workflows/package-lock-checks.yml b/.github/workflows/package-lock-checks.yml index 4c46dc5..45d3b48 100644 --- a/.github/workflows/package-lock-checks.yml +++ b/.github/workflows/package-lock-checks.yml @@ -8,12 +8,12 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v6 - name: Use Node.js LTS - uses: actions/setup-node@v1 + uses: actions/setup-node@v6 with: - node-version: '18.x' + node-version: 24 - name: Copy package-lock.json file run: cp package-lock.json package-lock-copy.json diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 3bf3a82..4244e50 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -19,14 +19,14 @@ jobs: sudo chown -R $USER:$USER ${{ github.workspace }} sudo chmod -R 755 ${{ github.workspace }} continue-on-error: true - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ github.ref }} clean: true - name: Set up Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v6 with: - node-version: 18 + node-version: 24 registry-url: https://registry.npmjs.org/ - name: Setup pnpm run: npm install -g pnpm From 79246299a38b6e47bdd647e80a09888b584e13a1 Mon Sep 17 00:00:00 2001 From: Radu Mojic Date: Thu, 2 Jul 2026 17:18:38 +0300 Subject: [PATCH 4/4] update changelog --- CHANGELOG.md | 4 ++++ package.json | 2 +- scripts/write-esm-package-json.js | 5 ----- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4955ee0..4eff836 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [[v3.1.0]](https://github.com/multiversx/mx-sdk-dapp-utils/pull/29) - 2026-07-02 + +- [Ship native Node ESM](https://github.com/multiversx/mx-sdk-dapp-utils/pull/28) + ## [[v3.0.2]](https://github.com/multiversx/mx-sdk-dapp-utils/pull/27) - 2025-09-16 - [Refactored parseAmount to mimic sdk-core v.14 implementation](https://github.com/multiversx/mx-sdk-dapp-utils/pull/26) diff --git a/package.json b/package.json index 51da4e2..a0e9073 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@multiversx/sdk-dapp-utils", - "version": "3.1.0-alpha.0", + "version": "3.1.0", "description": "SDK for DApp utilities", "main": "./out/index.js", "module": "./out/esm/index.js", diff --git a/scripts/write-esm-package-json.js b/scripts/write-esm-package-json.js index b9019df..40e55fc 100644 --- a/scripts/write-esm-package-json.js +++ b/scripts/write-esm-package-json.js @@ -1,9 +1,4 @@ // Marks out/esm as an ES module scope. -// -// tsc emits plain .js files. Without a "type": "module" marker (and with the -// root package defaulting to CommonJS), Node treats out/esm/*.js as CommonJS -// and throws on the `export` syntax in native Node / SSR. This writes the -// marker so the ESM build is interpreted as ESM. const fs = require('fs'); const path = require('path');