Repository: iTwin/itwinjs-core
Pull request #7866: Supporting esm in backend packages
Author: login: pankhur94
Created: 2025-03-19T13:37:42Z | Merged: 2025-03-25T14:42:14Z

- Support esm for backend packages
- update sinon import in core-backend



Diff:
diff --git a/common/changes/@itwin/analytical-backend/pankhur94-add-esm-support_2025-03-20-21-52.json b/common/changes/@itwin/analytical-backend/pankhur94-add-esm-support_2025-03-20-21-52.json
new file mode 100644
index 000000000000..7dcc301d8ab2
--- /dev/null
+++ b/common/changes/@itwin/analytical-backend/pankhur94-add-esm-support_2025-03-20-21-52.json
@@ -0,0 +1,10 @@
+{
+  "changes": [
+    {
+      "packageName": "@itwin/analytical-backend",
+      "comment": "Added ESM support",
+      "type": "none"
+    }
+  ],
+  "packageName": "@itwin/analytical-backend"
+}
\ No newline at end of file
diff --git a/common/changes/@itwin/core-backend/pankhur94-add-esm-support_2025-03-20-21-52.json b/common/changes/@itwin/core-backend/pankhur94-add-esm-support_2025-03-20-21-52.json
new file mode 100644
index 000000000000..5cba0d2527e0
--- /dev/null
+++ b/common/changes/@itwin/core-backend/pankhur94-add-esm-support_2025-03-20-21-52.json
@@ -0,0 +1,10 @@
+{
+  "changes": [
+    {
+      "packageName": "@itwin/core-backend",
+      "comment": "Added ESM support",
+      "type": "none"
+    }
+  ],
+  "packageName": "@itwin/core-backend"
+}
\ No newline at end of file
diff --git a/common/changes/@itwin/ecschema-editing/pankhur94-add-esm-support_2025-03-20-21-52.json b/common/changes/@itwin/ecschema-editing/pankhur94-add-esm-support_2025-03-20-21-52.json
new file mode 100644
index 000000000000..cb9ffa538521
--- /dev/null
+++ b/common/changes/@itwin/ecschema-editing/pankhur94-add-esm-support_2025-03-20-21-52.json
@@ -0,0 +1,10 @@
+{
+  "changes": [
+    {
+      "packageName": "@itwin/ecschema-editing",
+      "comment": "Added ESM support",
+      "type": "none"
+    }
+  ],
+  "packageName": "@itwin/ecschema-editing"
+}
\ No newline at end of file
diff --git a/common/changes/@itwin/ecschema-locaters/pankhur94-add-esm-support_2025-03-20-21-52.json b/common/changes/@itwin/ecschema-locaters/pankhur94-add-esm-support_2025-03-20-21-52.json
new file mode 100644
index 000000000000..98271ebe8bf4
--- /dev/null
+++ b/common/changes/@itwin/ecschema-locaters/pankhur94-add-esm-support_2025-03-20-21-52.json
@@ -0,0 +1,10 @@
+{
+  "changes": [
+    {
+      "packageName": "@itwin/ecschema-locaters",
+      "comment": "Added ESM support",
+      "type": "none"
+    }
+  ],
+  "packageName": "@itwin/ecschema-locaters"
+}
\ No newline at end of file
diff --git a/common/changes/@itwin/ecschema-rpcinterface-impl/pankhur94-add-esm-support_2025-03-20-21-52.json b/common/changes/@itwin/ecschema-rpcinterface-impl/pankhur94-add-esm-support_2025-03-20-21-52.json
new file mode 100644
index 000000000000..42e5f9bcb17a
--- /dev/null
+++ b/common/changes/@itwin/ecschema-rpcinterface-impl/pankhur94-add-esm-support_2025-03-20-21-52.json
@@ -0,0 +1,10 @@
+{
+  "changes": [
+    {
+      "packageName": "@itwin/ecschema-rpcinterface-impl",
+      "comment": "Added ESM support",
+      "type": "none"
+    }
+  ],
+  "packageName": "@itwin/ecschema-rpcinterface-impl"
+}
\ No newline at end of file
diff --git a/common/changes/@itwin/editor-backend/pankhur94-add-esm-support_2025-03-20-21-52.json b/common/changes/@itwin/editor-backend/pankhur94-add-esm-support_2025-03-20-21-52.json
new file mode 100644
index 000000000000..c23484bea06f
--- /dev/null
+++ b/common/changes/@itwin/editor-backend/pankhur94-add-esm-support_2025-03-20-21-52.json
@@ -0,0 +1,10 @@
+{
+  "changes": [
+    {
+      "packageName": "@itwin/editor-backend",
+      "comment": "Added ESM support",
+      "type": "none"
+    }
+  ],
+  "packageName": "@itwin/editor-backend"
+}
\ No newline at end of file
diff --git a/common/changes/@itwin/express-server/pankhur94-add-esm-support_2025-03-20-21-52.json b/common/changes/@itwin/express-server/pankhur94-add-esm-support_2025-03-20-21-52.json
new file mode 100644
index 000000000000..9e9d0dad5adb
--- /dev/null
+++ b/common/changes/@itwin/express-server/pankhur94-add-esm-support_2025-03-20-21-52.json
@@ -0,0 +1,10 @@
+{
+  "changes": [
+    {
+      "packageName": "@itwin/express-server",
+      "comment": "Added ESM support",
+      "type": "none"
+    }
+  ],
+  "packageName": "@itwin/express-server"
+}
\ No newline at end of file
diff --git a/common/changes/@itwin/linear-referencing-backend/pankhur94-add-esm-support_2025-03-20-21-52.json b/common/changes/@itwin/linear-referencing-backend/pankhur94-add-esm-support_2025-03-20-21-52.json
new file mode 100644
index 000000000000..04045890e2f6
--- /dev/null
+++ b/common/changes/@itwin/linear-referencing-backend/pankhur94-add-esm-support_2025-03-20-21-52.json
@@ -0,0 +1,10 @@
+{
+  "changes": [
+    {
+      "packageName": "@itwin/linear-referencing-backend",
+      "comment": "Added ESM support",
+      "type": "none"
+    }
+  ],
+  "packageName": "@itwin/linear-referencing-backend"
+}
\ No newline at end of file
diff --git a/common/changes/@itwin/linear-referencing-common/pankhur94-add-esm-support_2025-03-20-21-52.json b/common/changes/@itwin/linear-referencing-common/pankhur94-add-esm-support_2025-03-20-21-52.json
new file mode 100644
index 000000000000..ff62faa65eec
--- /dev/null
+++ b/common/changes/@itwin/linear-referencing-common/pankhur94-add-esm-support_2025-03-20-21-52.json
@@ -0,0 +1,10 @@
+{
+  "changes": [
+    {
+      "packageName": "@itwin/linear-referencing-common",
+      "comment": "Added ESM support",
+      "type": "none"
+    }
+  ],
+  "packageName": "@itwin/linear-referencing-common"
+}
\ No newline at end of file
diff --git a/common/changes/@itwin/physical-material-backend/pankhur94-add-esm-support_2025-03-20-21-52.json b/common/changes/@itwin/physical-material-backend/pankhur94-add-esm-support_2025-03-20-21-52.json
new file mode 100644
index 000000000000..2daa78e23f12
--- /dev/null
+++ b/common/changes/@itwin/physical-material-backend/pankhur94-add-esm-support_2025-03-20-21-52.json
@@ -0,0 +1,10 @@
+{
+  "changes": [
+    {
+      "packageName": "@itwin/physical-material-backend",
+      "comment": "Added ESM support",
+      "type": "none"
+    }
+  ],
+  "packageName": "@itwin/physical-material-backend"
+}
\ No newline at end of file
diff --git a/common/changes/@itwin/presentation-backend/pankhur94-add-esm-support_2025-03-20-21-52.json b/common/changes/@itwin/presentation-backend/pankhur94-add-esm-support_2025-03-20-21-52.json
new file mode 100644
index 000000000000..ad4d9b87a9be
--- /dev/null
+++ b/common/changes/@itwin/presentation-backend/pankhur94-add-esm-support_2025-03-20-21-52.json
@@ -0,0 +1,10 @@
+{
+  "changes": [
+    {
+      "packageName": "@itwin/presentation-backend",
+      "comment": "Added ESM support",
+      "type": "none"
+    }
+  ],
+  "packageName": "@itwin/presentation-backend"
+}
\ No newline at end of file
diff --git a/core/backend/package.json b/core/backend/package.json
index 2ac9974f25bc..d9c63b343748 100644
--- a/core/backend/package.json
+++ b/core/backend/package.json
@@ -3,17 +3,21 @@
   "version": "5.0.0-dev.90",
   "description": "iTwin.js backend components",
   "main": "lib/cjs/core-backend.js",
+  "module": "lib/esm/core-backend.js",
   "typings": "lib/cjs/core-backend",
   "license": "MIT",
   "engines": {
     "node": "^20.0.0 || ^22.0.0"
   },
   "scripts": {
-    "build": "npm run -s build:cjs && npm run -s copy:assets && npm run -s copy:test-assets && npm run -s copy:ecsqltestmdfiles",
+    "build": "npm run -s build:cjs && npm run -s build:esm && npm run -s copy:assets && npm run -s copy:test-assets && npm run -s copy:ecsqltestmdfiles",
     "build:cjs": "tsc 1>&2 --outDir lib/cjs",
+    "build:esm": "tsc 1>&2 --module ES2022 --outDir lib/esm",
     "clean": "rimraf -g lib .rush/temp/package-deps*.json  ../../tools/internal/ios/core-test-runner/build ../../tools/internal/lib",
     "docs": "betools docs --json=../../generated-docs/core/core-backend/file.json --tsIndexFile=./core-backend.ts --onlyJson --excludeGlob=**/*.d.ts",
-    "copy:assets": "cpx \"./src/assets/**/*\" ./lib/cjs/assets",
+    "copy:assets": " npm run -s copy:assets:cjs && npm run -s copy:assets:esm",
+    "copy:assets:cjs": "cpx \"./src/assets/**/*\" ./lib/cjs/assets",
+    "copy:assets:esm": "cpx \"./src/assets/**/*\" ./lib/esm/assets",
     "copy:ecsqltestmdfiles": "cpx \"./src/test/ecsql/queries/*\" ./lib/cjs/test/ecsql/queries",
     "copy:config": "internal-tools copy-config",
     "copy:test-assets": "cpx \"./src/test/assets/**/*\" ./lib/cjs/test/assets",
@@ -22,7 +26,7 @@
     "lint": "eslint \"./src/**/*.ts\" 1>&2",
     "lint-fix": "eslint --fix -f visualstudio \"./src/**/*.ts\" 1>&2",
     "test": "mocha",
-    "ios:webpack:tests": "TESTS_GLOB=./lib/**/*.test.js webpack --config ../../tools/internal/ios/ios.webpack.config.js",
+    "ios:webpack:tests": "TESTS_GLOB=./lib/esm/**/*.test.js webpack --config ../../tools/internal/ios/ios.webpack.config.js",
     "ios:copy:assets": "cpx \"./src/test/assets/**/*\" ../../tools/internal/lib/ios/assets/assets && cpx \"./src/assets/**/*\" ../../tools/internal/lib/ios/assets/assets",
     "ios:build:tests": "npm run -s build && npm run -s ios:webpack:tests && npm run -s ios:copy:assets",
     "ios:build:core-test-runner": "cd ../../tools/internal/ios/core-test-runner && xcrun xcodebuild -derivedDataPath ./build/DerivedData build CODE_SIGN_STYLE=Manual CODE_SIGN_IDENTITY= CODE_SIGNING_REQUIRED=NO PROVISIONING_PROFILE_SPECIFIER= -scheme core-test-runner -arch arm64 -sdk iphonesimulator -configuration Debug",
@@ -119,4 +123,4 @@
   "nyc": {
     "extends": "./node_modules/@itwin/build-tools/.nycrc"
   }
-}
+}
\ No newline at end of file
diff --git a/core/backend/src/test/schema/FunctionalDomain.test.ts b/core/backend/src/test/schema/FunctionalDomain.test.ts
index 726ffabd2f35..198d5b66b4a0 100644
--- a/core/backend/src/test/schema/FunctionalDomain.test.ts
+++ b/core/backend/src/test/schema/FunctionalDomain.test.ts
@@ -5,7 +5,7 @@
 
 import { assert, expect } from "chai";
 import { join } from "path";
-import { restore as sinonRestore, spy as sinonSpy } from "sinon";
+import { restore as sinonRestore, type SinonSpy, spy as sinonSpy } from "sinon";
 import { Guid, Id64 } from "@itwin/core-bentley";
 import { CodeScopeSpec, CodeSpec, ElementProps, IModel } from "@itwin/core-common";
 import { ClassRegistry } from "../../ClassRegistry";
@@ -18,7 +18,6 @@ import {
 import { ElementOwnsChildElements, ElementOwnsUniqueAspect, SubjectOwnsPartitionElements } from "../../NavigationRelationship";
 import { IModelTestUtils } from "../IModelTestUtils";
 import { KnownTestLocations } from "../KnownTestLocations";
-import Sinon = require("sinon"); // eslint-disable-line @typescript-eslint/no-require-imports
 
 let iModelDb: StandaloneDb;
 const insertedLabel = "inserted label";
@@ -276,7 +275,7 @@ describe("Functional Domain", () => {
 
     const testChannelKey1 = "channel 1 for tests";
     const testChannelKey2 = "channel 2 for tests";
-    function testChannel<T>(channelKey: ChannelKey, fn: () => T, spies: Sinon.SinonSpy[]) {
+    function testChannel<T>(channelKey: ChannelKey, fn: () => T, spies: SinonSpy[]) {
       iModelDb.channels.removeAllowedChannel(channelKey);
       expect(fn).throws("not allowed");
       iModelDb.channels.addAllowedChannel(channelKey);
diff --git a/core/backend/src/test/standalone/ChangeMerge.test.ts b/core/backend/src/test/standalone/ChangeMerge.test.ts
index a37884750cca..b4821bfbd099 100644
--- a/core/backend/src/test/standalone/ChangeMerge.test.ts
+++ b/core/backend/src/test/standalone/ChangeMerge.test.ts
@@ -24,7 +24,7 @@ import { HubMock } from "../../HubMock";
 import { RebaseChangesetConflictArgs, TxnArgs } from "../../internal/ChangesetConflictArgs";
 import { IModelTestUtils, TestUserType } from "../IModelTestUtils";
 chai.use(chaiAsPromised);
-import sinon = require("sinon"); // eslint-disable-line @typescript-eslint/no-require-imports
+import * as sinon from "sinon";
 
 async function assertThrowsAsync<T>(test: () => Promise<T>, msg?: string) {
   try {
diff --git a/core/backend/src/test/standalone/MergeConflict.test.ts b/core/backend/src/test/standalone/MergeConflict.test.ts
index cfca28005087..dd6da6a4c449 100644
--- a/core/backend/src/test/standalone/MergeConflict.test.ts
+++ b/core/backend/src/test/standalone/MergeConflict.test.ts
@@ -25,7 +25,8 @@ import {
 import { IModelTestUtils, TestUserType } from "../IModelTestUtils";
 import { RebaseChangesetConflictArgs, SqliteConflictCause } from "../../internal/ChangesetConflictArgs";
 chai.use(chaiAsPromised);
-import sinon = require("sinon"); // eslint-disable-line @typescript-eslint/no-require-imports
+import * as sinon from "sinon";
+
 export async function createNewModelAndCategory(rwIModel: BriefcaseDb, parent?: Id64String) {
   // Create a new physical model.
   const [, modelId] = await IModelTestUtils.createAndInsertPhysicalPartitionAndModelAsync(rwIModel, IModelTestUtils.getUniqueModelCode(rwIModel, "newPhysicalModel"), true, parent);
diff --git a/core/ecschema-editing/package.json b/core/ecschema-editing/package.json
index 83dd210672c4..59fd1b7f4337 100644
--- a/core/ecschema-editing/package.json
+++ b/core/ecschema-editing/package.json
@@ -4,6 +4,7 @@
   "description": "ECSchema editing and validation API",
   "license": "MIT",
   "main": "lib/cjs/ecschema-editing.js",
+  "module": "lib/esm/ecschema-editing.js",
   "typings": "lib/cjs/ecschema-editing",
   "repository": {
     "type": "git",
@@ -11,8 +12,9 @@
     "directory": "core/ecschema-editing"
   },
   "scripts": {
-    "build": "npm run -s build:cjs && npm run -s createLocalization && npm run -s copy:test-assets",
+    "build": "npm run -s build:cjs && npm run -s build:esm && npm run -s createLocalization && npm run -s copy:test-assets",
     "build:cjs": "tsc 1>&2 --outDir lib/cjs",
+    "build:esm": "tsc 1>&2 --module ES2022 --outDir lib/esm",
     "clean": "rimraf -g lib .rush/temp/package-deps*.json",
     "copy:test-assets": "cpx \"./src/test/assets/**/*\" ./lib/cjs/test/assets",
     "extract-api": "betools extract-api --entry=ecschema-editing",
diff --git a/core/ecschema-editing/src/Validation/DiagnosticReporter.ts b/core/ecschema-editing/src/Validation/DiagnosticReporter.ts
index e8ec9db6c349..6abaaab4ebb2 100644
--- a/core/ecschema-editing/src/Validation/DiagnosticReporter.ts
+++ b/core/ecschema-editing/src/Validation/DiagnosticReporter.ts
@@ -9,7 +9,7 @@
 import { Localization } from "@itwin/core-common";
 import { AnyDiagnostic } from "./Diagnostic";
 
-import assert = require("assert"); // eslint-disable-line @typescript-eslint/no-require-imports
+import * as assert from "assert";
 
 const translationNamespace = "ECSchemaMetaData";
 const subTranslationNamespace = "Diagnostics";
diff --git a/core/ecschema-editing/src/test/Validation/DiagnosticReporters.test.ts b/core/ecschema-editing/src/test/Validation/DiagnosticReporters.test.ts
index 1dc0a831ffd2..0cc4dd9b3afc 100644
--- a/core/ecschema-editing/src/test/Validation/DiagnosticReporters.test.ts
+++ b/core/ecschema-editing/src/test/Validation/DiagnosticReporters.test.ts
@@ -12,7 +12,7 @@ import { MutableClass } from "../../Editing/Mutable/MutableClass";
 import { AnyDiagnostic, createPropertyDiagnosticClass, DiagnosticCategory } from "../../Validation/Diagnostic";
 import { LoggingDiagnosticReporter } from "../../Validation/LoggingDiagnosticReporter";
 
-import sinon = require("sinon"); // eslint-disable-line @typescript-eslint/no-require-imports
+import * as sinon from "sinon";
 
 class TestDiagnosticReporter extends FormatDiagnosticReporter {
   constructor(suppressions?: Map<string, string[]>) {
diff --git a/core/ecschema-editing/src/test/Validation/SchemaValidationVisitor.test.ts b/core/ecschema-editing/src/test/Validation/SchemaValidationVisitor.test.ts
index 663c8cbbfde5..717b7174aaf8 100644
--- a/core/ecschema-editing/src/test/Validation/SchemaValidationVisitor.test.ts
+++ b/core/ecschema-editing/src/test/Validation/SchemaValidationVisitor.test.ts
@@ -14,7 +14,7 @@ import { DiagnosticCategory } from "../../Validation/Diagnostic";
 import { SchemaValidationVisitor } from "../../Validation/SchemaValidationVisitor";
 import { ApplySuppressionSet, EmptyRuleSet, IgnoreSuppressionSet, TestDiagnostics, TestReporter, TestRuleSet, TestRuleSetB } from "../TestUtils/DiagnosticHelpers";
 
-import sinon = require("sinon"); // eslint-disable-line @typescript-eslint/no-require-imports
+import * as sinon from "sinon";
 
 describe("SchemaValidationVisitor tests", () => {
   let visitor: SchemaValidationVisitor;
diff --git a/core/ecschema-locaters/package.json b/core/ecschema-locaters/package.json
index 02fad311f9cf..923db607d79c 100644
--- a/core/ecschema-locaters/package.json
+++ b/core/ecschema-locaters/package.json
@@ -4,6 +4,7 @@
   "description": "EC Schema file locaters",
   "license": "MIT",
   "main": "lib/cjs/ecschema-locaters.js",
+  "module": "lib/esm/ecschema-locaters.js",
   "typings": "lib/cjs/ecschema-locaters",
   "repository": {
     "type": "git",
@@ -11,8 +12,9 @@
     "directory": "core/ecschema-locaters"
   },
   "scripts": {
-    "build": "npm run -s build:cjs && npm run -s copy:test-assets",
+    "build": "npm run -s build:cjs && npm run -s build:esm && npm run -s copy:test-assets",
     "build:cjs": "tsc 1>&2 --outDir lib/cjs",
+    "build:esm": "tsc 1>&2 --module ES2022 --outDir lib/esm",
     "clean": "rimraf -g lib .rush/temp/package-deps*.json",
     "copy:test-assets": "cpx \"./src/test/assets/**/*\" ./lib/cjs/test/assets",
     "extract-api": "betools extract-api --entry=ecschema-locaters",
diff --git a/core/ecschema-rpc/impl/package.json b/core/ecschema-rpc/impl/package.json
index 9b9b6a4c3043..aa7bc562dcb1 100644
--- a/core/ecschema-rpc/impl/package.json
+++ b/core/ecschema-rpc/impl/package.json
@@ -3,6 +3,7 @@
   "version": "5.0.0-dev.90",
   "description": "Schema RPC Interface backend implementation",
   "main": "lib/cjs/ecschema-rpc-impl.js",
+  "module": "lib/esm/ecschema-rpc-impl.js",
   "typings": "lib/cjs/ecschema-rpc-impl",
   "license": "MIT",
   "repository": {
@@ -11,8 +12,9 @@
     "directory": "core/ecschema-rpc/impl"
   },
   "scripts": {
-    "build": "npm run -s build:cjs",
+    "build": "npm run -s build:cjs && npm run -s build:esm",
     "build:cjs": "tsc 1>&2 --outDir lib/cjs",
+    "build:esm": "tsc 1>&2 --module ES2022 --outDir lib/esm",
     "clean": "rimraf -g lib .rush/temp/package-deps*.json",
     "cover": "",
     "docs": "",
diff --git a/core/express-server/package.json b/core/express-server/package.json
index b77d06f01baf..94c822499731 100644
--- a/core/express-server/package.json
+++ b/core/express-server/package.json
@@ -3,14 +3,16 @@
   "version": "5.0.0-dev.90",
   "description": "iTwin.js express utilities",
   "main": "lib/cjs/ExpressServer.js",
+  "module": "lib/esm/ExpressServer.js",
   "typings": "lib/cjs/ExpressServer",
   "license": "MIT",
   "engines": {
     "node": "^20.0.0 || ^22.0.0"
   },
   "scripts": {
-    "build": "npm run -s build:cjs",
+    "build": "npm run -s build:cjs && npm run -s build:esm",
     "build:cjs": "tsc 1>&2 --outDir lib/cjs",
+    "build:esm": "tsc 1>&2 --module ES2022 --outDir lib/esm",
     "clean": "rimraf -g lib .rush/temp/package-deps*.json",
     "docs": "betools docs --json=../../generated-docs/core/express-server/file.json --tsIndexFile=./ExpressServer.ts --onlyJson",
     "extract-api": "betools extract-api --entry=ExpressServer",
diff --git a/domains/analytical/backend/package.json b/domains/analytical/backend/package.json
index 91cd2a889e59..87c67eeb5d3c 100644
--- a/domains/analytical/backend/package.json
+++ b/domains/analytical/backend/package.json
@@ -2,13 +2,16 @@
   "name": "@itwin/analytical-backend",
   "version": "5.0.0-dev.90",
   "main": "lib/cjs/analytical-backend.js",
+  "module": "lib/esm/analytical-backend.js",
   "typings": "lib/cjs/analytical-backend",
   "license": "MIT",
   "engines": {
     "node": "^20.0.0 || ^22.0.0"
   },
   "scripts": {
-    "build": "tsc 1>&2 --outDir lib/cjs && npm run -s copy:test-assets",
+    "build": "npm run -s build:cjs && npm run -s build:esm && npm run -s copy:test-assets",
+    "build:cjs": "tsc 1>&2 --outDir lib/cjs",
+    "build:esm": "tsc 1>&2 --module ES2022 --outDir lib/esm",
     "clean": "rimraf -g lib .rush/temp/package-deps*.json",
     "copy:test-assets": "cpx \"./src/test/assets/**/*\" ./lib/cjs/test/assets",
     "cover": "nyc npm -s test",
diff --git a/domains/linear-referencing/backend/package.json b/domains/linear-referencing/backend/package.json
index 5629b454313c..db30fe41aba6 100644
--- a/domains/linear-referencing/backend/package.json
+++ b/domains/linear-referencing/backend/package.json
@@ -2,13 +2,16 @@
   "name": "@itwin/linear-referencing-backend",
   "version": "5.0.0-dev.90",
   "main": "lib/cjs/linear-referencing-backend.js",
+  "module": "lib/esm/linear-referencing-backend.js",
   "typings": "lib/cjs/linear-referencing-backend",
   "license": "MIT",
   "engines": {
     "node": "^20.0.0 || ^22.0.0"
   },
   "scripts": {
-    "build": "tsc 1>&2 --outDir lib/cjs && npm run -s copy:test-assets",
+    "build": "npm run -s build:cjs && npm run -s build:esm && npm run -s copy:test-assets",
+    "build:cjs": "tsc 1>&2 --outDir lib/cjs",
+    "build:esm": "tsc 1>&2 --module ES2022 --outDir lib/esm",
     "clean": "rimraf -g lib .rush/temp/package-deps*.json",
     "copy:test-assets": "cpx \"./src/test/assets/**/*\" ./lib/cjs/test/assets",
     "cover": "nyc npm -s test",
diff --git a/domains/linear-referencing/common/package.json b/domains/linear-referencing/common/package.json
index 2c9b3dffc883..b0cdc66d473e 100644
--- a/domains/linear-referencing/common/package.json
+++ b/domains/linear-referencing/common/package.json
@@ -2,11 +2,13 @@
   "name": "@itwin/linear-referencing-common",
   "version": "5.0.0-dev.90",
   "main": "lib/cjs/linear-referencing-common.js",
+  "module": "lib/esm/linear-referencing-common.js",
   "typings": "lib/cjs/linear-referencing-common",
   "license": "MIT",
   "scripts": {
-    "build": "npm run -s build:cjs",
+    "build": "npm run -s build:cjs && npm run -s build:esm",
     "build:cjs": "tsc 1>&2 --outDir lib/cjs",
+    "build:esm": "tsc 1>&2 --module ES2022 --outDir lib/esm",
     "clean": "rimraf -g lib .rush/temp/package-deps*.json",
     "cover": "nyc npm -s test",
     "docs": "betools docs --json=../../../generated-docs/domains/linear-referencing-common/file.json --tsIndexFile=./linear-referencing-common.ts --onlyJson",
diff --git a/domains/physical-material/backend/package.json b/domains/physical-material/backend/package.json
index 72984fba9cb4..d749c5193227 100644
--- a/domains/physical-material/backend/package.json
+++ b/domains/physical-material/backend/package.json
@@ -2,14 +2,16 @@
   "name": "@itwin/physical-material-backend",
   "version": "5.0.0-dev.90",
   "main": "lib/cjs/physical-material-backend.js",
+  "module": "lib/esm/physical-material-backend.js",
   "typings": "lib/cjs/physical-material-backend",
   "license": "MIT",
   "engines": {
     "node": "^20.0.0 || ^22.0.0"
   },
   "scripts": {
-    "build": "npm run -s build:cjs",
+    "build": "npm run -s build:cjs && npm run -s build:esm",
     "build:cjs": "tsc 1>&2 --outDir lib/cjs",
+    "build:esm": "tsc 1>&2 --module ES2022 --outDir lib/esm",
     "clean": "rimraf -g lib .rush/temp/package-deps*.json",
     "cover": "nyc npm -s test",
     "docs": "betools docs --json=../../../generated-docs/domains/physical-material-backend/file.json --tsIndexFile=./physical-material-backend.ts --onlyJson",
diff --git a/editor/backend/package.json b/editor/backend/package.json
index cf76af8112cd..d3600f4efe0a 100644
--- a/editor/backend/package.json
+++ b/editor/backend/package.json
@@ -3,14 +3,16 @@
   "version": "5.0.0-dev.90",
   "description": "iTwin.js editor backend",
   "main": "lib/cjs/editor-backend.js",
+  "module": "lib/esm/editor-backend.js",
   "typings": "lib/cjs/editor-backend",
   "license": "MIT",
   "engines": {
     "node": "^20.0.0 || ^22.0.0"
   },
   "scripts": {
-    "build": "npm run -s build:cjs",
+    "build": "npm run -s build:cjs && npm run -s build:esm",
     "build:cjs": "tsc 1>&2 --outDir lib/cjs",
+    "build:esm": "tsc 1>&2 --module ES2022 --outDir lib/esm",
     "clean": "rimraf -g lib .rush/temp/package-deps*.json",
     "docs": "betools docs --json=../../generated-docs/editor/editor-backend/file.json --tsIndexFile=./editor-backend.ts --onlyJson",
     "extract-api": "betools extract-api --entry=editor-backend",
diff --git a/presentation/backend/package.json b/presentation/backend/package.json
index 202c93eaaee6..428acb74a71f 100644
--- a/presentation/backend/package.json
+++ b/presentation/backend/package.json
@@ -21,11 +21,12 @@
     "url": "http://www.bentley.com"
   },
   "main": "lib/cjs/presentation-backend.js",
+  "module": "lib/esm/presentation-backend.js",
   "typings": "lib/cjs/presentation-backend",
   "scripts": {
-    "build": "npm run -s build:cjs",
-    "build:cjs": "tsc 1>&2",
-    "build:watch": "npm run -s build:cjs -- -w",
+    "build": "npm run -s build:cjs && npm run -s build:esm",
+    "build:cjs": "tsc 1>&2 -p tsconfig.cjs.json",
+    "build:esm": "tsc 1>&2 -p tsconfig.esm.json",
     "clean": "rimraf -g lib .rush/temp/package-deps*.json",
     "cover": "nyc npm -s test",
     "docs": "npm run -s docs:reference && npm run -s extract && npm run -s docs:changelog",
diff --git a/presentation/backend/tsconfig.cjs.json b/presentation/backend/tsconfig.cjs.json
new file mode 100644
index 000000000000..98f72bdcc071
--- /dev/null
+++ b/presentation/backend/tsconfig.cjs.json
@@ -0,0 +1,6 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "outDir": "lib/cjs",
+  },
+}
diff --git a/presentation/backend/tsconfig.esm.json b/presentation/backend/tsconfig.esm.json
new file mode 100644
index 000000000000..3296abfaa0a6
--- /dev/null
+++ b/presentation/backend/tsconfig.esm.json
@@ -0,0 +1,7 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "module": "ES2022",
+    "outDir": "lib/esm",
+  },
+}
\ No newline at end of file


Reviews:
user: login: grigasp
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-20T05:48:49Z
user: login: pankhur94
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T14:45:34Z
user: login: pankhur94
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T14:46:14Z
user: login: aruniverse
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-20T19:07:15Z
user: login: pankhur94
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T19:28:26Z
user: login: aruniverse
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-20T22:03:55Z
user: login: grigasp
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-21T07:08:59Z
user: login: pankhur94
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T16:05:18Z
user: login: aruniverse
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-24T19:02:56Z
user: login: pmconne
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-24T19:31:47Z
user: login: pankhur94
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T22:00:48Z
user: login: grigasp
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-25T05:37:28Z
user: login: grigasp
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-25T08:15:35Z
user: login: grigasp
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-25T12:17:36Z

Review comments:
diff_hunk: @@ -21,11 +21,26 @@
     "url": "http://www.bentley.com"
   },
   "main": "lib/cjs/presentation-backend.js",
-  "typings": "lib/cjs/presentation-backend",
+  "module": "lib/esm/presentation-backend.js",
+  "exports": {
+    ".": {
+      "types": "./lib/cjs/presentation-backend.d.ts",
+      "require": {
+        "default": "./lib/cjs/presentation-backend.js",
+        "types": "./lib/cjs/presentation-backend.d.ts"
+      },
+      "import": {
+        "default": "./lib/esm/presentation-backend.js",
+        "types": "./lib/esm/presentation-backend.d.ts"
+      }
+    }
+  },
   "scripts": {
-    "build": "npm run -s build:cjs",
-    "build:cjs": "tsc 1>&2",
-    "build:watch": "npm run -s build:cjs -- -w",
+    "build": "npm run -s build:cjs && npm run -s build:esm",
+    "build:cjs": "tsc 1>&2 -p tsconfig.cjs.json",
+    "build:esm": "tsc 1>&2 -p tsconfig.esm.json",
+    "build:watch:cjs": "npm run -s build:cjs --w",
+    "build:watch:esm": "npm run -s build:esm --w",
path: presentation/backend/package.json
user: login: grigasp
body: `--w` won't work (you need `-- -w`), but I don't think we need these `watch` scripts anyway - I'd rather they're removed.
created_at: 2025-03-20T05:47:33Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,8 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "module": "ESNext",
+    "moduleResolution": "node",
path: presentation/backend/tsconfig.esm.json
user: login: grigasp
body: why not the following?

```
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
```
created_at: 2025-03-20T05:48:40Z
author_association: MEMBER
diff_hunk: @@ -21,11 +21,26 @@
     "url": "http://www.bentley.com"
   },
   "main": "lib/cjs/presentation-backend.js",
-  "typings": "lib/cjs/presentation-backend",
+  "module": "lib/esm/presentation-backend.js",
+  "exports": {
+    ".": {
+      "types": "./lib/cjs/presentation-backend.d.ts",
+      "require": {
+        "default": "./lib/cjs/presentation-backend.js",
+        "types": "./lib/cjs/presentation-backend.d.ts"
+      },
+      "import": {
+        "default": "./lib/esm/presentation-backend.js",
+        "types": "./lib/esm/presentation-backend.d.ts"
+      }
+    }
+  },
   "scripts": {
-    "build": "npm run -s build:cjs",
-    "build:cjs": "tsc 1>&2",
-    "build:watch": "npm run -s build:cjs -- -w",
+    "build": "npm run -s build:cjs && npm run -s build:esm",
+    "build:cjs": "tsc 1>&2 -p tsconfig.cjs.json",
+    "build:esm": "tsc 1>&2 -p tsconfig.esm.json",
+    "build:watch:cjs": "npm run -s build:cjs --w",
+    "build:watch:esm": "npm run -s build:esm --w",
path: presentation/backend/package.json
user: login: pankhur94
body: updated

created_at: 2025-03-20T14:45:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,8 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "module": "ESNext",
+    "moduleResolution": "node",
path: presentation/backend/tsconfig.esm.json
user: login: pankhur94
body: keeping in line with the repo, I have used ES2022.
created_at: 2025-03-20T14:46:13Z
author_association: CONTRIBUTOR
path: example-code/snippets/src/backend/AttachDb.test.ts
user: login: aruniverse
body: no actual changes to this, id revert this file; as well as all the other .test.ts files touched by this pr in example-code & full stack tests
created_at: 2025-03-20T19:03:37Z
author_association: MEMBER
path: example-code/snippets/src/backend/AttachDb.test.ts
user: login: pankhur94
body: done
created_at: 2025-03-20T19:28:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,8 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "module": "ESNext",
+    "moduleResolution": "node",
path: presentation/backend/tsconfig.esm.json
user: login: grigasp
body: From https://www.typescriptlang.org/tsconfig/#module:

> You very likely want "nodenext" for modern Node.js projects

Regarding `moduleResolution` - I see you removed it from this config, which means we take it from `tsconfig-base.json` in build tools, and it's set to `node` there. Here's what https://www.typescriptlang.org/tsconfig/#moduleResolution says about `node` resolution mode:

> 'node10' (previously called 'node') for Node.js versions older than v10, which only support CommonJS require. You probably won’t need to use node10 in modern code.

And here: https://www.typescriptlang.org/docs/handbook/modules/reference.html#node10-formerly-known-as-node:

> --moduleResolution node was renamed to node10 (keeping node as an alias for backward compatibility) in TypeScript 5.0. It reflects the CommonJS module resolution algorithm as it existed in Node.js versions earlier than v12. It should no longer be used.
created_at: 2025-03-21T07:08:58Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,8 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "module": "ESNext",
+    "moduleResolution": "node",
path: presentation/backend/tsconfig.esm.json
user: login: pankhur94
body: I discussed this with arun, and we think that it is fine to have node module resolution for now, it has worked fine with frontend packages. However, we will be testing these changes post publish and observe if we have some issues. @aruniverse.
created_at: 2025-03-21T16:05:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,8 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "module": "ESNext",
+    "moduleResolution": "node",
path: presentation/backend/tsconfig.esm.json
user: login: pankhur94
body: I tried replacing es2022 with nodenext, although nodenext is used for both esm and cjs outputs, its output is determined by either the module system of the package(type field in package.json) defaults to commonjs, or the file extensions, which in case of typescript would be  .ts -> cjs, .mts -> esm. If we decide to change type to module instead of current commonjs, we would have to add .js in all the imports statements, which would make this pr heavy. Had a conversation with arun, and I believe it would be better for us to have this pr merged, published and pull this up for testing and may be look at nodenext in a separate pr.
created_at: 2025-03-24T22:00:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,8 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "module": "ESNext",
+    "moduleResolution": "node",
path: presentation/backend/tsconfig.esm.json
user: login: grigasp
body: Right, Node.js relies on `type` attribute in `package.json` to tell if the package is ESM or CommonJS, with the latter being the default. So even though this PR adds ESM build output, that output is not usable in Node.js applications, because it lacks that attribute and Node.js doesn't know it's ESM.

I'm in the process of adding real support for ESM in presentation packages, including running tests on ESM to confirm that's the case. That's going to be in a separate PR. In the meantime, feel free to resolve this comment to complete this PR.
created_at: 2025-03-25T05:37:28Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,8 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "module": "ESNext",
+    "moduleResolution": "node",
path: presentation/backend/tsconfig.esm.json
user: login: grigasp
body: Here's my shot at this: https://github.com/iTwin/itwinjs-core/pull/7885
created_at: 2025-03-25T08:15:35Z
author_association: MEMBER

PR comments:
user: login: aruniverse
created_at: 2025-03-21T02:25:52Z
body: @tcobbs-bentley @toddsouthenbentley can you guys take a look at the ios failures?
author_association: MEMBER
user: login: tcobbs-bentley
created_at: 2025-03-24T16:54:18Z
body: > @tcobbs-bentley @toddsouthenbentley can you guys take a look at the ios failures?

I am able to reproduce the problem locally using Pankhur's branch (while things work fine locally on master). Based on the error message, it seems like the backend is trying to create the default workspace multiple times for some reason, but I unfortunately have to do a local debug build of the add-on in order to debug further. I am working on that now.
author_association: MEMBER
user: login: tcobbs-bentley
created_at: 2025-03-24T17:29:35Z
body: @pankhur94 The cause of the failure in iOS is that the `TestUtils.startBackend` is being called twice during startup. I am currently investigating to find out why that is, but it is a result of the changes in this PR.

CC: @aruniverse 
author_association: MEMBER
user: login: tcobbs-bentley
created_at: 2025-03-24T17:38:25Z
body: @pankhur94, @aruniverse Both calls are from the `before` hook in TestUtils.ts. The first call is from the esm version of that hook, and the second is from the cjs version of that same hook. So it looks like both cjs and esm versions are trying to run, and since the backend is already initialized at the time of the cjs run, the cjs version fails. Obviously having both sets of code loaded and running at the same time is a bad thing. I don't know why it only happens on the mobile test and not `rush cover`.
author_association: MEMBER
user: login: tcobbs-bentley
created_at: 2025-03-24T17:41:13Z
body: @pankhur94 @aruniverse The iOS tests still run using webpack. I'm guessing that the glob pattern for the webpack needs to be updated.
author_association: MEMBER
user: login: tcobbs-bentley
created_at: 2025-03-24T17:45:51Z
body: I pushed what I believe to be the fix: https://github.com/iTwin/itwinjs-core/pull/7866/commits/ae4ef6bb93a1f0241ab44b2f328a082f98d8046b
author_association: MEMBER