diff --git a/Gruntfile.js b/Gruntfile.cjs
similarity index 96%
rename from Gruntfile.js
rename to Gruntfile.cjs
index 58a967907495..be027f5ba501 100644
--- a/Gruntfile.js
+++ b/Gruntfile.cjs
@@ -14,7 +14,7 @@ module.exports = function (grunt) {
     pkg: grunt.file.readJSON('package.json'),
 
     clean: {
-      out: ['gen/', 'out/', 'out-wpt/', 'out-node/'],
+      out: ['gen/', 'out/', 'out-wpt/'],
     },
 
     run: {
@@ -89,14 +89,6 @@ module.exports = function (grunt) {
           '--ignore=src/common/tools',
         ],
       },
-      'build-out-node': {
-        cmd: 'node',
-        args: [
-          'node_modules/typescript/lib/tsc.js',
-          '--project', 'node.tsconfig.json',
-          '--outDir', 'out-node/',
-        ],
-      },
       'copy-assets': {
         cmd: 'node',
         args: [
@@ -188,7 +180,6 @@ module.exports = function (grunt) {
         tasks: [
           'build-standalone',
           'build-wpt',
-          'run:build-out-node',
         ],
       },
       'all-checks': {
diff --git a/babel.config.js b/babel.config.js
index ad977bc510ee..f46ac79b1832 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -1,4 +1,4 @@
-module.exports = function (api) {
+export default function (api) {
   api.cache(true);
   return {
     presets: ['@babel/preset-typescript'],
diff --git a/cts.code-workspace b/cts.code-workspace
index 9c7320ce4b64..da65364d1d8b 100644
--- a/cts.code-workspace
+++ b/cts.code-workspace
@@ -26,7 +26,6 @@
       "deploy_key.enc": true,
       "node_modules": true,
       "out": true,
-      "out-node": true,
       "out-wpt": true,
       "docs/tsdoc": true,
       "package-lock.json": true
diff --git a/node.tsconfig.json b/node.tsconfig.json
deleted file mode 100644
index 74707d408d49..000000000000
--- a/node.tsconfig.json
+++ /dev/null
@@ -1,20 +0,0 @@
-// Typescript configuration for compile sources and
-// dependent files for usage directly with Node.js. This
-// is useful for running scripts in tools/ directly with Node
-// without including extra dependencies.
-{
-  "extends": "./tsconfig.json",
-  "compilerOptions": {
-    "module": "commonjs",
-    "incremental": false,
-    "noEmit": false,
-    "declaration": false,
-  },
-
-  "exclude": [
-    "src/common/runtime/wpt.ts",
-    "src/common/runtime/standalone.ts",
-    "src/common/runtime/helper/test_worker.ts",
-    "src/webgpu/web_platform/worker/worker_launcher.ts"
-  ]
-}
diff --git a/package.json b/package.json
index 6f6b3b0df54b..27e7a017ae9f 100644
--- a/package.json
+++ b/package.json
@@ -2,6 +2,7 @@
   "name": "@webgpu/cts",
   "version": "0.1.0",
   "description": "WebGPU Conformance Test Suite",
+  "type": "module",
   "scripts": {
     "test": "grunt all",
     "all": "grunt all",
diff --git a/src/common/runtime/helper/sys.ts b/src/common/runtime/helper/sys.ts
index d2e07ff26d30..9ac9c929bfd5 100644
--- a/src/common/runtime/helper/sys.ts
+++ b/src/common/runtime/helper/sys.ts
@@ -1,8 +1,8 @@
 /* eslint no-process-exit: "off" */
 /* eslint @typescript-eslint/no-namespace: "off" */
 
-function node() {
-  const { existsSync } = require('fs');
+async function node() {
+  const { existsSync } = await import('fs');
 
   return {
     type: 'node',
@@ -41,6 +41,6 @@ function deno() {
   };
 }
 
-const sys = typeof globalThis.process !== 'undefined' ? node() : deno();
+const sys = typeof globalThis.process !== 'undefined' ? await node() : deno();
 
 export default sys;
diff --git a/src/common/tools/commonjs/package.json b/src/common/tools/commonjs/package.json
new file mode 100644
index 000000000000..5bbefffbabee
--- /dev/null
+++ b/src/common/tools/commonjs/package.json
@@ -0,0 +1,3 @@
+{
+  "type": "commonjs"
+}
diff --git a/src/common/tools/setup-ts-in-node.js b/src/common/tools/commonjs/setup-ts-in-node.js
similarity index 95%
rename from src/common/tools/setup-ts-in-node.js
rename to src/common/tools/commonjs/setup-ts-in-node.js
index 89e91e8c9df3..8fe75f500044 100644
--- a/src/common/tools/setup-ts-in-node.js
+++ b/src/common/tools/commonjs/setup-ts-in-node.js
@@ -3,7 +3,7 @@ const path = require('path');
 // Automatically transpile .ts imports
 require('ts-node').register({
   // Specify the project file so ts-node doesn't try to find it itself based on the CWD.
-  project: path.resolve(__dirname, '../../../tsconfig.json'),
+  project: path.resolve(__dirname, '../../../../tsconfig.json'),
   compilerOptions: {
     module: 'commonjs',
   },
diff --git a/src/common/tools/version.ts b/src/common/tools/version.ts
index 2b51700b1276..4691e4b1d03e 100644
--- a/src/common/tools/version.ts
+++ b/src/common/tools/version.ts
@@ -1,4 +1,4 @@
-export const version = require('child_process')
+export const version = (await import('child_process'))
   .execSync('git describe --always --abbrev=0 --dirty')
   .toString()
   .trim();
diff --git a/src/webgpu/web_platform/worker/worker.spec.ts b/src/webgpu/web_platform/worker/worker.spec.ts
index 67f9f693bee0..705380aa3f0d 100644
--- a/src/webgpu/web_platform/worker/worker.spec.ts
+++ b/src/webgpu/web_platform/worker/worker.spec.ts
@@ -27,7 +27,6 @@ g.test('worker')
     // is using commonjs which doesn't support import.meta. Further,
     // we need to put the url in a string add pass the string to import
     // otherwise typescript tries to parse the file which again, fails.
-    // worker_launcher.js is excluded in node.tsconfig.json.
     const url = './worker_launcher.js';
     const { launchWorker } = await import(url);
     const result = await launchWorker();
diff --git a/tools/checklist b/tools/checklist
index 8aace4f387ca..1a9bac84d96a 100755
--- a/tools/checklist
+++ b/tools/checklist
@@ -7,5 +7,5 @@
 // and every query in it is valid (e.g. renames have been applied, and new tests added
 // to the spreadsheet have also been added to the CTS).
 
-require('../src/common/tools/setup-ts-in-node.js');
+require('../src/common/tools/commonjs/setup-ts-in-node.js');
 require('../src/common/tools/checklist.ts');
diff --git a/tools/dev_server b/tools/dev_server
index d400d79c19b0..e12bf1242fa5 100755
--- a/tools/dev_server
+++ b/tools/dev_server
@@ -1,4 +1,4 @@
 #!/usr/bin/env node
 
-require('../src/common/tools/setup-ts-in-node.js');
+require('../src/common/tools/commonjs/setup-ts-in-node.js');
 require('../src/common/tools/dev_server.ts');
diff --git a/tools/gen_cache b/tools/gen_cache
index fd7bf52c2fd0..9c8d304df3e9 100755
--- a/tools/gen_cache
+++ b/tools/gen_cache
@@ -1,4 +1,4 @@
 #!/usr/bin/env node
 
-require('../src/common/tools/setup-ts-in-node.js');
+require('../src/common/tools/commonjs/setup-ts-in-node.js');
 require('../src/common/tools/gen_cache.ts');
diff --git a/tools/gen_listings b/tools/gen_listings
index 6c25622423a1..2570c0fca60a 100755
--- a/tools/gen_listings
+++ b/tools/gen_listings
@@ -3,5 +3,5 @@
 // Crawl a suite directory (e.g. src/webgpu/) to generate a listing.js containing
 // the listing of test files in the suite.
 
-require('../src/common/tools/setup-ts-in-node.js');
+require('../src/common/tools/commonjs/setup-ts-in-node.js');
 require('../src/common/tools/gen_listings.ts');
diff --git a/tools/gen_version b/tools/gen_version
index 2c17db14544b..db8f9349ed44 100755
--- a/tools/gen_version
+++ b/tools/gen_version
@@ -5,7 +5,7 @@
 
 /* eslint-disable no-console */
 
-require('../src/common/tools/setup-ts-in-node.js');
+require('../src/common/tools/commonjs/setup-ts-in-node.js');
 const fs = require('fs');
 
 const myself = 'tools/gen_version';
@@ -14,7 +14,7 @@ if (!fs.existsSync(myself)) {
   process.exit(1);
 }
 
-const { version } = require('../src/common/tools/version.ts');
+const { version } = require('../src/common/tools/version.ts'); // FIXME doesn't work because in order to require the placeholder version.ts it needs to be commonjs, but we can't configure that for a single file
 
 fs.mkdirSync('./gen/common/internal', { recursive: true });
 // This will be copied into the various other build directories.
diff --git a/tools/gen_wpt_cts_html b/tools/gen_wpt_cts_html
index 07f1f465c7fe..023a9072fd2f 100755
--- a/tools/gen_wpt_cts_html
+++ b/tools/gen_wpt_cts_html
@@ -35,5 +35,5 @@
 //   ?q=webgpu:a/bar:bar1={"x":3} <- [ Mac ]
 //   ?q=webgpu:a/bar:bar2~
 
-require('../src/common/tools/setup-ts-in-node.js');
+require('../src/common/tools/commonjs/setup-ts-in-node.js');
 require('../src/common/tools/gen_wpt_cts_html.ts');
diff --git a/tools/merge_listing_times b/tools/merge_listing_times
index a9bcd2e71ae8..41eaeba848c1 100755
--- a/tools/merge_listing_times
+++ b/tools/merge_listing_times
@@ -3,7 +3,7 @@
 // See `docs/adding_timing_metadata.md` for an explanation of listing times, and
 // a walkthrough on adding entries for new tests.
 
-require('../src/common/tools/setup-ts-in-node.js');
+require('../src/common/tools/commonjs/setup-ts-in-node.js');
 
 // See the help message in this file for info on how to use the tool.
 require('../src/common/tools/merge_listing_times.ts');
diff --git a/tools/package.json b/tools/package.json
new file mode 100644
index 000000000000..5bbefffbabee
--- /dev/null
+++ b/tools/package.json
@@ -0,0 +1,3 @@
+{
+  "type": "commonjs"
+}
diff --git a/tools/run_node b/tools/run_node
index b71ec9f134ec..4ac5ae97caa2 100755
--- a/tools/run_node
+++ b/tools/run_node
@@ -2,5 +2,5 @@
 
 // Run test suites under node.
 
-require('../src/common/tools/setup-ts-in-node.js');
+require('../src/common/tools/commonjs/setup-ts-in-node.js');
 require('../src/common/runtime/cmdline.ts');
diff --git a/tools/run_wpt_ref_tests b/tools/run_wpt_ref_tests
index 79fd1b1b7c01..5cf69d7e3a56 100644
--- a/tools/run_wpt_ref_tests
+++ b/tools/run_wpt_ref_tests
@@ -1,4 +1,4 @@
 #!/usr/bin/env node
 
-require('../src/common/tools/setup-ts-in-node.js');
+require('../src/common/tools/commonjs/setup-ts-in-node.js');
 require('../src/common/tools/run_wpt_ref_tests.ts');
diff --git a/tools/validate b/tools/validate
index 03b3a61bb156..06cc00d613cb 100755
--- a/tools/validate
+++ b/tools/validate
@@ -2,5 +2,5 @@
 
 // Validate several properties of test files and the tests in them.
 
-require('../src/common/tools/setup-ts-in-node.js');
+require('../src/common/tools/commonjs/setup-ts-in-node.js');
 require('../src/common/tools/validate.ts');