fix:  --experimental_split_coverage_postprocessing flag
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 4c2ff10..9901692 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -119,6 +119,8 @@
               run: echo "folder=e2e/vendored_tarfile" >> $GITHUB_OUTPUT
             - id: worker
               run: echo "folder=e2e/worker" >> $GITHUB_OUTPUT
+            - id: split_coverage
+              run: echo "folder=e2e/split_coverage" >> $GITHUB_OUTPUT
         outputs:
             # Will look like [".", "e2e/bzlmod", ...]
             folders: ${{ toJSON(steps.*.outputs.folder) }}
diff --git a/e2e/split_coverage/.bazelignore b/e2e/split_coverage/.bazelignore
new file mode 100644
index 0000000..b512c09
--- /dev/null
+++ b/e2e/split_coverage/.bazelignore
@@ -0,0 +1 @@
+node_modules
\ No newline at end of file
diff --git a/e2e/split_coverage/.bazeliskrc b/e2e/split_coverage/.bazeliskrc
new file mode 120000
index 0000000..0fbe20f
--- /dev/null
+++ b/e2e/split_coverage/.bazeliskrc
@@ -0,0 +1 @@
+../../.bazeliskrc
\ No newline at end of file
diff --git a/e2e/split_coverage/.bazelrc b/e2e/split_coverage/.bazelrc
new file mode 100644
index 0000000..db3624c
--- /dev/null
+++ b/e2e/split_coverage/.bazelrc
@@ -0,0 +1,7 @@
+# import common bazelrc shared with e2e workspaces
+import %workspace%/../../.bazelrc.common
+
+coverage --sandbox_debug
+coverage --experimental_split_coverage_postprocessing
+coverage --experimental_fetch_all_coverage_outputs
+coverage --instrument_test_targets
\ No newline at end of file
diff --git a/e2e/split_coverage/.bazelversion b/e2e/split_coverage/.bazelversion
new file mode 120000
index 0000000..96cf949
--- /dev/null
+++ b/e2e/split_coverage/.bazelversion
@@ -0,0 +1 @@
+../../.bazelversion
\ No newline at end of file
diff --git a/e2e/split_coverage/BUILD.bazel b/e2e/split_coverage/BUILD.bazel
new file mode 100644
index 0000000..e98a045
--- /dev/null
+++ b/e2e/split_coverage/BUILD.bazel
@@ -0,0 +1,3 @@
+load("@npm//:defs.bzl", "npm_link_all_packages")
+
+npm_link_all_packages(name = "node_modules")
diff --git a/e2e/split_coverage/WORKSPACE b/e2e/split_coverage/WORKSPACE
new file mode 100644
index 0000000..c40edfb
--- /dev/null
+++ b/e2e/split_coverage/WORKSPACE
@@ -0,0 +1,27 @@
+local_repository(
+    name = "aspect_rules_js",
+    path = "../..",
+)
+
+load("@aspect_rules_js//js:repositories.bzl", "rules_js_dependencies")
+
+rules_js_dependencies()
+
+load("@rules_nodejs//nodejs:repositories.bzl", "DEFAULT_NODE_VERSION", "nodejs_register_toolchains")
+
+nodejs_register_toolchains(
+    name = "nodejs",
+    node_version = DEFAULT_NODE_VERSION,
+)
+
+load("@aspect_rules_js//npm:npm_import.bzl", "npm_translate_lock")
+
+npm_translate_lock(
+    name = "npm",
+    pnpm_lock = "//:pnpm-lock.yaml",
+    verify_node_modules_ignored = "//:.bazelignore",
+)
+
+load("@npm//:repositories.bzl", "npm_repositories")
+
+npm_repositories()
diff --git a/e2e/split_coverage/package.json b/e2e/split_coverage/package.json
new file mode 100644
index 0000000..b1ef5fe
--- /dev/null
+++ b/e2e/split_coverage/package.json
@@ -0,0 +1,6 @@
+{
+    "private": true,
+    "dependencies": {
+        "@types/node": "18.15.11"
+    }
+}
diff --git a/e2e/split_coverage/pnpm-lock.yaml b/e2e/split_coverage/pnpm-lock.yaml
new file mode 100644
index 0000000..54499e0
--- /dev/null
+++ b/e2e/split_coverage/pnpm-lock.yaml
@@ -0,0 +1,15 @@
+lockfileVersion: 5.4
+
+importers:
+
+  .:
+    specifiers:
+      '@types/node': 18.15.11
+    dependencies:
+      '@types/node': 18.15.11
+
+packages:
+
+  /@types/node/18.15.11:
+    resolution: {integrity: sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==}
+    dev: false
diff --git a/e2e/split_coverage/pnpm-workspace.yaml b/e2e/split_coverage/pnpm-workspace.yaml
new file mode 100644
index 0000000..2cce0eb
--- /dev/null
+++ b/e2e/split_coverage/pnpm-workspace.yaml
@@ -0,0 +1,2 @@
+packages:
+    - '.'
diff --git a/e2e/split_coverage/test/BUILD.bazel b/e2e/split_coverage/test/BUILD.bazel
new file mode 100644
index 0000000..f55f431
--- /dev/null
+++ b/e2e/split_coverage/test/BUILD.bazel
@@ -0,0 +1,10 @@
+load("@aspect_rules_js//js:defs.bzl", "js_test")
+
+js_test(
+    name = "coverage",
+    data = [
+        "coverage.js",
+        "//:node_modules/@types/node",
+    ],
+    entry_point = "test.js",
+)
diff --git a/e2e/split_coverage/test/coverage.js b/e2e/split_coverage/test/coverage.js
new file mode 100644
index 0000000..30e97e9
--- /dev/null
+++ b/e2e/split_coverage/test/coverage.js
@@ -0,0 +1,7 @@
+module.exports.test = function (num) {
+    if (num > 1) {
+        return 'bigger than 1'
+    } else {
+        return 'smaller than 1'
+    }
+}
diff --git a/e2e/split_coverage/test/test.js b/e2e/split_coverage/test/test.js
new file mode 100644
index 0000000..ae2c78d
--- /dev/null
+++ b/e2e/split_coverage/test/test.js
@@ -0,0 +1,7 @@
+const coverage = require('./coverage')
+
+coverage.test(0)
+
+// Disabling the statment below should give your 50% coverage
+//coverage.test(2);
+console.log(process.cwd())
diff --git a/js/private/coverage/bundle/c8.js b/js/private/coverage/bundle/c8.js
index f649816..5174bda 100644
--- a/js/private/coverage/bundle/c8.js
+++ b/js/private/coverage/bundle/c8.js
@@ -22,17 +22,22 @@
 
 // TODO: can or should we instrument files from other repositories as well?
 // if so then the path.join call below will yield invalid paths since files will have external/wksp as their prefix.
-const pwd = path.join(process.env.RUNFILES, process.env.TEST_WORKSPACE)
-process.chdir(pwd)
+let resolve = path.join(process.env.RUNFILES, process.env.TEST_WORKSPACE)
+
+if (process.env.SPLIT_COVERAGE_POST_PROCESSING == '1') {
+    resolve = fs
+        .readFileSync(path.resolve(process.env.COVERAGE_DIR, 'pwd'))
+        .toString()
+}
+
+process.cwd = () => resolve
 
 new Report({
-    include: include,
+    include: include.map((f) => path.join(resolve, f)),
     exclude: include.length === 0 ? ['**'] : [],
     reportsDirectory: process.env.COVERAGE_DIR,
     tempDirectory: process.env.COVERAGE_DIR,
-    resolve: '',
-    src: pwd,
-    all: true,
+    resolve: resolve,
     reporter: ['lcovonly'],
 })
     .run()
diff --git a/js/private/coverage/coverage.js b/js/private/coverage/coverage.js
index fb5583a..912e28d 100644
--- a/js/private/coverage/coverage.js
+++ b/js/private/coverage/coverage.js
@@ -9611,7 +9611,7 @@
 const convertSourceMap = convertSourceMap$1;
 const util$1 = require$$2;
 const debuglog$1 = util$1.debuglog('c8');
-const { dirname, isAbsolute: isAbsolute$1, join, resolve: resolve$1 } = require$$0;
+const { dirname, isAbsolute: isAbsolute$1, join, resolve: resolve$2 } = require$$0;
 const { fileURLToPath: fileURLToPath$1 } = require$$1$1;
 const CovBranch = requireBranch();
 const CovFunction = require_function();
@@ -9725,7 +9725,7 @@
     if (isAbsolute$1(candidatePath)) {
       return candidatePath
     } else {
-      return resolve$1(dirname(this.path), candidatePath)
+      return resolve$2(dirname(this.path), candidatePath)
     }
   }
 
@@ -10688,7 +10688,7 @@
 const libReport = istanbulLibReport;
 const reports = istanbulReports;
 const { readdirSync, readFileSync, statSync } = require$$0$1;
-const { isAbsolute, resolve, extname } = require$$0;
+const { isAbsolute, resolve: resolve$1, extname } = require$$0;
 const { pathToFileURL, fileURLToPath } = require$$1$1;
 const getSourceMapFromFile = sourceMapFromFile_1;
 // TODO: switch back to @c88/v8-coverage once patch is landed.
@@ -10782,7 +10782,7 @@
     for (const v8ScriptCov of v8ProcessCov.result) {
       try {
         const sources = this._getSourceMap(v8ScriptCov);
-        const path = resolve(this.resolve, v8ScriptCov.url);
+        const path = resolve$1(this.resolve, v8ScriptCov.url);
         const converter = v8toIstanbul(path, this.wrapperLength, sources, (path) => {
           if (this.excludeAfterRemap) {
             return !this._shouldInstrument(path)
@@ -10881,7 +10881,7 @@
       const { extension } = this.exclude;
       for (const workingDir of workingDirs) {
         this.exclude.globSync(workingDir).forEach((f) => {
-          const fullPath = resolve(workingDir, f);
+          const fullPath = resolve$1(workingDir, f);
           if (!fileIndex.has(fullPath)) {
             const ext = extname(fullPath);
             if (extension.includes(ext)) {
@@ -10892,7 +10892,7 @@
               }
               emptyReports.push({
                 scriptId: 0,
-                url: resolve(fullPath),
+                url: resolve$1(fullPath),
                 functions: [{
                   functionName: '(empty-report)',
                   ranges: [{
@@ -10933,7 +10933,7 @@
     for (const file of readdirSync(this.tempDirectory)) {
       try {
         reports.push(JSON.parse(readFileSync(
-          resolve(this.tempDirectory, file),
+          resolve$1(this.tempDirectory, file),
           'utf8'
         )));
       } catch (err) {
@@ -11046,17 +11046,20 @@
 
 // TODO: can or should we instrument files from other repositories as well?
 // if so then the path.join call below will yield invalid paths since files will have external/wksp as their prefix.
-const pwd = require$$0.join(process.env.RUNFILES, process.env.TEST_WORKSPACE);
-process.chdir(pwd);
+let resolve = require$$0.join(process.env.RUNFILES, process.env.TEST_WORKSPACE);
+
+if (process.env.SPLIT_COVERAGE_POST_PROCESSING == "1") {
+    resolve = require$$0$1.readFileSync(require$$0.resolve(process.env.COVERAGE_DIR, "pwd")).toString();
+}
+
+process.cwd = () => resolve;
 
 new Report({
-    include: include,
+    include: include.map(f => require$$0.join(resolve, f)),
     exclude: include.length === 0 ? ['**'] : [],
     reportsDirectory: process.env.COVERAGE_DIR,
     tempDirectory: process.env.COVERAGE_DIR,
-    resolve: '',
-    src: pwd,
-    all: true,
+    resolve: resolve,
     reporter: ['lcovonly'],
 })
     .run()
diff --git a/js/private/coverage/coverage.sh.tpl b/js/private/coverage/coverage.sh.tpl
index 6f6e100..2c4aa42 100644
--- a/js/private/coverage/coverage.sh.tpl
+++ b/js/private/coverage/coverage.sh.tpl
@@ -18,6 +18,15 @@
 # Initialize RUNFILES environment variable
 # ==============================================================================
 {{initialize_runfiles}}
+
+# When --experimental_split_coverage_postprocessing is enabled, bazel creates
+# separate runfiles directory for the coverage merger. 
+# When --experimental_split_coverage_postprocessing is disabled we observe the issue 
+# in https://github.com/bazelbuild/bazel/issues/4033
+if [ $SPLIT_COVERAGE_POST_PROCESSING == 1 ]; then
+    RUNFILES=$(_normalize_path "$LCOV_MERGER.runfiles")
+fi
+
 export RUNFILES
 
 # ==============================================================================
diff --git a/js/private/js_binary.sh.tpl b/js/private/js_binary.sh.tpl
index 58803e6..50467ff 100644
--- a/js/private/js_binary.sh.tpl
+++ b/js/private/js_binary.sh.tpl
@@ -308,6 +308,10 @@
 # Enable coverage if requested
 if [ "${COVERAGE_DIR:-}" ]; then
   logf_debug "enabling v8 coverage support ${COVERAGE_DIR}"
+	if [ ${SPLIT_COVERAGE_POST_PROCESSING:0} == 1 ]; then
+	  logf_debug "split coverage post processing is requested. writing pwd to ${COVERAGE_DIR}/pwd"
+		echo -n "$PWD" > "${COVERAGE_DIR}/pwd"
+	fi
   export NODE_V8_COVERAGE=${COVERAGE_DIR}
 fi