diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f5b8609ec3333..cc7d8ffe7c845 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -29,21 +29,13 @@ jobs:
- windows-latest
- macos-14
node-version:
- - '22'
- - '20'
- - '18'
- - '16'
- - '14'
+ - '23'
bundle:
- 'true'
include:
- - node-version: 'lts/*'
+ - node-version: '23'
bundle: false
os: ubuntu-latest
- exclude:
- # No Node 14 on ARM macOS
- - node-version: '14'
- os: macos-14
runs-on: ${{ matrix.os }}
name: Test Node ${{ matrix.node-version }} on ${{ matrix.os }}${{ (!matrix.bundle && ' with --no-bundle') || '' }}
@@ -55,6 +47,7 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
check-latest: true
+
- run: npm ci
- name: Tests
@@ -83,7 +76,9 @@ jobs:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
- node-version: 'lts/*'
+ node-version: '23'
+ check-latest: true
+
- run: npm ci
- name: Run tests with coverage
@@ -108,7 +103,8 @@ jobs:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
- node-version: 'lts/*'
+ node-version: '23'
+ check-latest: true
- run: npm ci
- name: Linter
@@ -134,7 +130,8 @@ jobs:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
- node-version: 'lts/*'
+ node-version: '23'
+ check-latest: true
- run: npm ci
- uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1
@@ -154,7 +151,8 @@ jobs:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
- node-version: 'lts/*'
+ node-version: '23'
+ check-latest: true
- run: npm ci
- name: Installing browsers
@@ -170,7 +168,8 @@ jobs:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
- node-version: 'lts/*'
+ node-version: '23'
+ check-latest: true
- run: npm ci
- name: Build src
@@ -184,7 +183,9 @@ jobs:
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
- node-version: 'lts/*'
+ node-version: '23'
+ check-latest: true
+
- run: |
npm --version
# corepack enable npm
@@ -232,7 +233,8 @@ jobs:
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
- node-version: 'lts/*'
+ node-version: '23'
+ check-latest: true
- run: |
npm --version
# corepack enable npm
@@ -265,7 +267,8 @@ jobs:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
- node-version: 'lts/*'
+ node-version: '23'
+ check-latest: true
- run: npm ci
- name: Build scripts
@@ -281,7 +284,9 @@ jobs:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
- node-version: 'lts/*'
+ node-version: '23'
+ check-latest: true
+
- run: npm ci
- name: Build tsc
@@ -300,7 +305,9 @@ jobs:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
- node-version: 'lts/*'
+ node-version: '23'
+ check-latest: true
+
- run: npm ci
- name: Remove all baselines
diff --git a/.gulp.js b/.gulp.js
index b167b3a3628d8..405da7c654476 100644
--- a/.gulp.js
+++ b/.gulp.js
@@ -1,5 +1,9 @@
-const cp = require("child_process");
-const path = require("path");
+import cp from "child_process";
+import path from "path";
+import url from "url";
+
+const __filename = url.fileURLToPath(new URL(import.meta.url));
+const __dirname = path.dirname(__filename);
const argv = process.argv.slice(2);
diff --git a/Herebyfile.mjs b/Herebyfile.mjs
index 402f7917cf34a..179ee0ced6df9 100644
--- a/Herebyfile.mjs
+++ b/Herebyfile.mjs
@@ -1,6 +1,5 @@
// @ts-check
import { CancelToken } from "@esfx/canceltoken";
-import assert from "assert";
import chalk from "chalk";
import chokidar from "chokidar";
import esbuild from "esbuild";
@@ -172,7 +171,6 @@ async function runDtsBundler(entrypoint, output) {
* @param {BundlerTaskOptions} [taskOptions]
*
* @typedef BundlerTaskOptions
- * @property {boolean} [exportIsTsObject]
* @property {boolean} [treeShaking]
* @property {boolean} [usePublicAPI]
* @property {() => void} [onWatchRebuild]
@@ -180,17 +178,15 @@ async function runDtsBundler(entrypoint, output) {
function createBundler(entrypoint, outfile, taskOptions = {}) {
const getOptions = memoize(async () => {
const copyright = await getCopyrightHeader();
- const banner = taskOptions.exportIsTsObject ? "var ts = {}; ((module) => {" : "";
-
/** @type {esbuild.BuildOptions} */
const options = {
entryPoints: [entrypoint],
- banner: { js: copyright + banner },
+ banner: { js: copyright },
bundle: true,
outfile,
platform: "node",
target: ["es2020", "node14.17"],
- format: "cjs",
+ format: "esm",
sourcemap: "linked",
sourcesContent: false,
treeShaking: taskOptions.treeShaking,
@@ -200,66 +196,17 @@ function createBundler(entrypoint, outfile, taskOptions = {}) {
};
if (taskOptions.usePublicAPI) {
- options.external = ["./typescript.js"];
options.plugins = options.plugins || [];
options.plugins.push({
- name: "remap-typescript-to-require",
+ name: "remap-typescript-to-public-api",
setup(build) {
- build.onLoad({ filter: /src[\\/]typescript[\\/]typescript\.ts$/ }, () => {
- return { contents: `export * from "./typescript.js"` };
+ build.onResolve({ filter: /^(?:\.\.[\\/])*typescript[\\/]typescript\.js$/ }, () => {
+ return { path: "./typescript.js", external: true };
});
},
});
}
- if (taskOptions.exportIsTsObject) {
- // Monaco bundles us as ESM by wrapping our code with something that defines module.exports
- // but then does not use it, instead using the `ts` variable. Ensure that if we think we're CJS
- // that we still set `ts` to the module.exports object.
- options.footer = { js: `})({ get exports() { return ts; }, set exports(v) { ts = v; if (typeof module !== "undefined" && module.exports) { module.exports = v; } } })` };
-
- // esbuild converts calls to "require" to "__require"; this function
- // calls the real require if it exists, or throws if it does not (rather than
- // throwing an error like "require not defined"). But, since we want typescript
- // to be consumable by other bundlers, we need to convert these calls back to
- // require so our imports are visible again.
- //
- // To fix this, we redefine "require" to a name we're unlikely to use with the
- // same length as "require", then replace it back to "require" after bundling,
- // ensuring that source maps still work.
- //
- // See: https://github.com/evanw/esbuild/issues/1905
- const require = "require";
- const fakeName = "Q".repeat(require.length);
- const fakeNameRegExp = new RegExp(fakeName, "g");
- options.define = { [require]: fakeName };
-
- // For historical reasons, TypeScript does not set __esModule. Hack esbuild's __toCommonJS to be a noop.
- // We reference `__copyProps` to ensure the final bundle doesn't have any unreferenced code.
- const toCommonJsRegExp = /var __toCommonJS .*/;
- const toCommonJsRegExpReplacement = "var __toCommonJS = (mod) => (__copyProps, mod); // Modified helper to skip setting __esModule.";
-
- options.plugins = options.plugins || [];
- options.plugins.push(
- {
- name: "post-process",
- setup: build => {
- build.onEnd(async () => {
- let contents = await fs.promises.readFile(outfile, "utf-8");
- contents = contents.replace(fakeNameRegExp, require);
- let matches = 0;
- contents = contents.replace(toCommonJsRegExp, () => {
- matches++;
- return toCommonJsRegExpReplacement;
- });
- assert(matches === 1, "Expected exactly one match for __toCommonJS");
- await fs.promises.writeFile(outfile, contents);
- });
- },
- },
- );
- }
-
return options;
});
@@ -305,6 +252,7 @@ let printedWatchWarning = false;
* @param {string} options.output
* @param {boolean} [options.enableCompileCache]
* @param {Task[]} [options.mainDeps]
+ * @param {boolean} [options.reexportDefault]
* @param {BundlerTaskOptions} [options.bundlerOptions]
*/
function entrypointBuildTask(options) {
@@ -329,13 +277,13 @@ function entrypointBuildTask(options) {
const moduleSpecifier = path.relative(outDir, output);
const lines = [
`// This file is a shim which defers loading the real module until the compile cache is enabled.`,
- `try {`,
- ` const { enableCompileCache } = require("node:module");`,
- ` if (enableCompileCache) {`,
- ` enableCompileCache();`,
- ` }`,
- `} catch {}`,
- `module.exports = require("./${moduleSpecifier.replace(/[\\/]/g, "/")}");`,
+ `import mod from "node:module";`,
+ `if (mod.enableCompileCache) {`,
+ ` mod.enableCompileCache();`,
+ `}`,
+ `// Keep this synchronous so downstream people who required this file do not see TLA.`,
+ `const require = mod.createRequire(import.meta.url);`,
+ `require("./${moduleSpecifier.replace(/[\\/]/g, "/")}");`,
];
await fs.promises.writeFile(originalOutput, lines.join("\n") + "\n");
},
@@ -355,13 +303,13 @@ function entrypointBuildTask(options) {
});
/**
- * Writes a CJS module that reexports another CJS file. E.g. given
+ * Writes a module that reexports another file. E.g. given
* `options.builtEntrypoint = "./built/local/tsc/tsc.js"` and
* `options.output = "./built/local/tsc.js"`, this will create a file
* named "./built/local/tsc.js" containing:
*
* ```
- * module.exports = require("./tsc/tsc.js")
+ * export * from "./tsc/tsc.js";
* ```
*/
const shim = task({
@@ -369,8 +317,19 @@ function entrypointBuildTask(options) {
run: async () => {
const outDir = path.dirname(output);
await fs.promises.mkdir(outDir, { recursive: true });
- const moduleSpecifier = path.relative(outDir, options.builtEntrypoint);
- await fs.promises.writeFile(output, `module.exports = require("./${moduleSpecifier.replace(/[\\/]/g, "/")}")`);
+ const moduleSpecifier = path.relative(outDir, options.builtEntrypoint).replace(/[\\/]/g, "/");
+ const lines = [
+ `export * from "./${moduleSpecifier}";`,
+ ];
+
+ if (options.reexportDefault) {
+ lines.push(
+ `import _default from "./${moduleSpecifier}";`,
+ `export default _default;`,
+ );
+ }
+
+ await fs.promises.writeFile(output, lines.join("\n") + "\n");
},
});
@@ -435,7 +394,7 @@ const { main: services, build: buildServices, watch: watchServices } = entrypoin
builtEntrypoint: "./built/local/typescript/typescript.js",
output: "./built/local/typescript.js",
mainDeps: [generateLibs],
- bundlerOptions: { exportIsTsObject: true },
+ reexportDefault: true,
});
export { services, watchServices };
@@ -477,25 +436,22 @@ export const watchMin = task({
dependencies: [watchTsc, watchTsserver],
});
-// This is technically not enough to make tsserverlibrary loadable in the
-// browser, but it's unlikely that anyone has actually been doing that.
const lsslJs = `
-if (typeof module !== "undefined" && module.exports) {
- module.exports = require("./typescript.js");
-}
-else {
- throw new Error("tsserverlibrary requires CommonJS; use typescript.js instead");
-}
+import ts from "./typescript.js";
+export * from "./typescript.js";
+export default ts;
`;
const lsslDts = `
-import ts = require("./typescript.js");
-export = ts;
+import ts from "./typescript.js";
+export * from "./typescript.js";
+export default ts;
`;
const lsslDtsInternal = `
-import ts = require("./typescript.internal.js");
-export = ts;
+import ts from "./typescript.internal.js";
+export * from "./typescript.internal.js";
+export default ts;
`;
/**
@@ -536,7 +492,7 @@ const { main: tests, watch: watchTests } = entrypointBuildTask({
description: "Builds the test infrastructure",
buildDeps: [generateDiagnostics],
project: "src/testRunner",
- srcEntrypoint: "./src/testRunner/_namespaces/Harness.ts",
+ srcEntrypoint: "./src/testRunner/runner.ts",
builtEntrypoint: "./built/local/testRunner/runner.js",
output: testRunner,
mainDeps: [generateLibs],
diff --git a/bin/tsc b/bin/tsc
index 19c62bf7a0004..bef1027ea20a1 100755
--- a/bin/tsc
+++ b/bin/tsc
@@ -1,2 +1,2 @@
#!/usr/bin/env node
-require('../lib/tsc.js')
+import '../lib/tsc.js';
diff --git a/bin/tsserver b/bin/tsserver
index 7143b6a73ab8a..d90c1a2ece3ff 100755
--- a/bin/tsserver
+++ b/bin/tsserver
@@ -1,2 +1,2 @@
#!/usr/bin/env node
-require('../lib/tsserver.js')
+import '../lib/tsserver.js';
diff --git a/eslint.config.mjs b/eslint.config.mjs
index bc30b791311be..fcfeef896bffc 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -17,6 +17,14 @@ const rulesDir = path.join(__dirname, "scripts", "eslint", "rules");
const ext = ".cjs";
const ruleFiles = fs.readdirSync(rulesDir).filter(p => p.endsWith(ext));
+const restrictedESMGlobals = [
+ { name: "__filename" },
+ { name: "__dirname" },
+ { name: "require" },
+ { name: "module" },
+ { name: "exports" },
+];
+
export default tseslint.config(
{
files: ["**/*.{ts,tsx,cts,mts,js,cjs,mjs}"],
@@ -165,11 +173,7 @@ export default tseslint.config(
// These globals don't exist outside of CJS files.
"no-restricted-globals": [
"error",
- { name: "__filename" },
- { name: "__dirname" },
- { name: "require" },
- { name: "module" },
- { name: "exports" },
+ ...restrictedESMGlobals,
],
},
},
@@ -204,6 +208,7 @@ export default tseslint.config(
{ name: "setImmediate" },
{ name: "clearImmediate" },
{ name: "performance" },
+ ...restrictedESMGlobals,
],
"local/no-direct-import": "error",
},
@@ -211,7 +216,10 @@ export default tseslint.config(
{
files: ["src/harness/**", "src/testRunner/**"],
rules: {
- "no-restricted-globals": "off",
+ "no-restricted-globals": [
+ "error",
+ ...restrictedESMGlobals,
+ ],
"regexp/no-super-linear-backtracking": "off",
"local/no-direct-import": "off",
},
diff --git a/package.json b/package.json
index a8a4f8420f14b..4e2b1687e6afa 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,7 @@
"type": "git",
"url": "https://github.com/microsoft/TypeScript.git"
},
+ "type": "module",
"main": "./lib/typescript.js",
"typings": "./lib/typescript.d.ts",
"bin": {
diff --git a/scripts/browserIntegrationTest.mjs b/scripts/browserIntegrationTest.mjs
index 7b3021f979350..f85c25a0f1036 100644
--- a/scripts/browserIntegrationTest.mjs
+++ b/scripts/browserIntegrationTest.mjs
@@ -28,7 +28,8 @@ for (const browserType of browsers) {
await page.setContent(`
-
+
+
`);
diff --git a/scripts/checkModuleFormat.mjs b/scripts/checkModuleFormat.mjs
index ed33b5fb00382..d37faf353bfa8 100644
--- a/scripts/checkModuleFormat.mjs
+++ b/scripts/checkModuleFormat.mjs
@@ -1,3 +1,4 @@
+import chalk from "chalk";
import { createRequire } from "module";
import {
__importDefault,
@@ -19,7 +20,7 @@ console.log(`Testing ${typescript}...`);
/** @type {[fn: (() => Promise), shouldSucceed: boolean][]} */
const fns = [
[() => require(typescript).version, true],
- [() => require(typescript).default.version, false],
+ [() => require(typescript).default.version, true],
[() => __importDefault(require(typescript)).version, false],
[() => __importDefault(require(typescript)).default.version, true],
[() => __importStar(require(typescript)).version, true],
@@ -41,7 +42,7 @@ for (const [fn, shouldSucceed] of fns) {
console.log(`${fn.toString()} ${status} as expected.`);
}
else {
- console.log(`${fn.toString()} unexpectedly ${status}.`);
+ console.log(chalk.red(`${fn.toString()} unexpectedly ${status}.`));
process.exitCode = 1;
}
}
diff --git a/scripts/dtsBundler.mjs b/scripts/dtsBundler.mjs
index ade134287fe57..17ee501060e01 100644
--- a/scripts/dtsBundler.mjs
+++ b/scripts/dtsBundler.mjs
@@ -365,6 +365,8 @@ function isSelfReference(reference, symbol) {
* @param {ts.Symbol} moduleSymbol
*/
function emitAsNamespace(name, parent, moduleSymbol, needExportModifier) {
+ if (name === "default") return;
+
assert(moduleSymbol.flags & ts.SymbolFlags.ValueModule, "moduleSymbol is not a module");
const fullName = parent ? `${parent}.${name}` : name;
@@ -482,6 +484,7 @@ function emitAsNamespace(name, parent, moduleSymbol, needExportModifier) {
emitAsNamespace("ts", "", moduleSymbol, /*needExportModifier*/ false);
+// TODO(jakebailey): require(ESM) - fix this
write("export = ts;", WriteTarget.Both);
const copyrightNotice = fs.readFileSync(path.join(__dirname, "CopyrightNotice.txt"), "utf-8");
diff --git a/src/cancellationToken/cancellationToken.ts b/src/cancellationToken/cancellationToken.ts
index 4676e9b14e0a3..97c5c7f0612fd 100644
--- a/src/cancellationToken/cancellationToken.ts
+++ b/src/cancellationToken/cancellationToken.ts
@@ -17,7 +17,7 @@ function pipeExists(name: string): boolean {
return fs.existsSync(name);
}
-function createCancellationToken(args: string[]): ServerCancellationToken {
+export function createCancellationToken(args: string[]): ServerCancellationToken {
let cancellationPipeName: string | undefined;
for (let i = 0; i < args.length - 1; i++) {
if (args[i] === "--cancellationPipeName") {
@@ -66,4 +66,3 @@ function createCancellationToken(args: string[]): ServerCancellationToken {
};
}
}
-export = createCancellationToken;
diff --git a/src/compiler/core.ts b/src/compiler/core.ts
index ef83fefd533e0..4600600de622d 100644
--- a/src/compiler/core.ts
+++ b/src/compiler/core.ts
@@ -2588,6 +2588,5 @@ export function isNodeLikeSystem(): boolean {
// use in performanceCore.ts.
return typeof process !== "undefined"
&& !!process.nextTick
- && !(process as any).browser
- && typeof require !== "undefined";
+ && !(process as any).browser;
}
diff --git a/src/compiler/nodeGetBuiltinModule.ts b/src/compiler/nodeGetBuiltinModule.ts
new file mode 100644
index 0000000000000..d3b1b49d6cdf7
--- /dev/null
+++ b/src/compiler/nodeGetBuiltinModule.ts
@@ -0,0 +1,21 @@
+export function nodeCreateRequire(path: string): (id: string) => any {
+ /* eslint-disable no-restricted-globals */
+ // If we're running in an environment that already has `require`, use it.
+ // We're probably in bun or a bundler that provides `require` even within ESM.
+ if (typeof require === "function" && typeof require.resolve === "function") {
+ return id => {
+ const p = require.resolve(id, { paths: [path] });
+ return require(p);
+ };
+ }
+ /* eslint-enable no-restricted-globals */
+
+ // Otherwise, try and build a `require` function from the `module` module.
+ if (typeof process === "undefined" || typeof process.getBuiltinModule !== "function") {
+ throw new Error("process.getBuiltinModule is not supported in this environment.");
+ }
+
+ const mod = process.getBuiltinModule("node:module");
+ if (!mod) throw new Error("missing node:module");
+ return mod.createRequire(path);
+}
diff --git a/src/compiler/performanceCore.ts b/src/compiler/performanceCore.ts
index 38d8badace719..1e707a7fd79a9 100644
--- a/src/compiler/performanceCore.ts
+++ b/src/compiler/performanceCore.ts
@@ -1,4 +1,5 @@
import { isNodeLikeSystem } from "./_namespaces/ts.js";
+import { nodeCreateRequire } from "./nodeGetBuiltinModule.js";
// The following definitions provide the minimum compatible support for the Web Performance User Timings API
// between browsers and NodeJS:
@@ -31,7 +32,7 @@ function tryGetPerformance() {
if (isNodeLikeSystem()) {
try {
// By default, only write native events when generating a cpu profile or using the v8 profiler.
- // Some environments may polyfill this module with an empty object; verify the object has the expected shape.
+ const require = nodeCreateRequire(import.meta.url);
const { performance } = require("perf_hooks") as Partial;
if (performance) {
return {
diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts
index c8f176603002b..ca7d5199b5def 100644
--- a/src/compiler/sys.ts
+++ b/src/compiler/sys.ts
@@ -46,6 +46,7 @@ import {
WatchOptions,
writeFileEnsuringDirectories,
} from "./_namespaces/ts.js";
+import { nodeCreateRequire } from "./nodeGetBuiltinModule.js";
declare function setTimeout(handler: (...args: any[]) => void, timeout: number): any;
declare function clearTimeout(handle: any): void;
@@ -1466,9 +1467,15 @@ export let sys: System = (() => {
const byteOrderMarkIndicator = "\uFEFF";
function getNodeSystem(): System {
+ // TODO(jakebailey): Only use createRequire for sys.require.
+ const require = nodeCreateRequire(import.meta.url);
+ const _path: typeof import("path") = require("path");
+ const _url: typeof import("url") = require("url");
+ const __filename = _url.fileURLToPath(new URL(import.meta.url));
+ const __dirname = _path.dirname(__filename);
+
const nativePattern = /^native |^\([^)]+\)$|^(?:internal[\\/]|[\w\s]+(?:\.js)?$)/;
const _fs: typeof import("fs") = require("fs");
- const _path: typeof import("path") = require("path");
const _os = require("os");
// crypto can be absent on reduced node installations
let _crypto: typeof import("crypto") | undefined;
diff --git a/src/compiler/tracing.ts b/src/compiler/tracing.ts
index 5d7138d1c22eb..ea5f75a6cfda9 100644
--- a/src/compiler/tracing.ts
+++ b/src/compiler/tracing.ts
@@ -22,6 +22,7 @@ import {
UnionType,
} from "./_namespaces/ts.js";
import * as performance from "./_namespaces/ts.performance.js";
+import { nodeCreateRequire } from "./nodeGetBuiltinModule.js";
/* Tracing events for the compiler. */
@@ -60,6 +61,7 @@ export namespace tracingEnabled {
if (fs === undefined) {
try {
+ const require = nodeCreateRequire(import.meta.url);
fs = require("fs");
}
catch (e) {
diff --git a/src/harness/findUpDir.ts b/src/harness/findUpDir.ts
index 20b1afe414ece..29008b44726be 100644
--- a/src/harness/findUpDir.ts
+++ b/src/harness/findUpDir.ts
@@ -4,10 +4,14 @@ import {
join,
resolve,
} from "path";
+import { fileURLToPath } from "url";
// search directories upward to avoid hard-wired paths based on the
// build tree (same as scripts/build/findUpDir.js)
+const __filename = fileURLToPath(new URL(import.meta.url));
+const __dirname = dirname(__filename);
+
export function findUpFile(name: string): string {
let dir = __dirname;
while (true) {
diff --git a/src/testRunner/parallel/host.ts b/src/testRunner/parallel/host.ts
index 400971655c6b4..f8d833bc3ab31 100644
--- a/src/testRunner/parallel/host.ts
+++ b/src/testRunner/parallel/host.ts
@@ -33,6 +33,9 @@ import {
import * as ts from "../_namespaces/ts.js";
import * as Utils from "../_namespaces/Utils.js";
+import { createRequire } from "module";
+const require = createRequire(import.meta.url);
+
export function start(importTests: () => Promise): void {
const Base = Mocha.reporters.Base;
const color = Base.color;
diff --git a/src/tsserver/nodeServer.ts b/src/tsserver/nodeServer.ts
index 376caaa0e6ae8..d619b30563f5f 100644
--- a/src/tsserver/nodeServer.ts
+++ b/src/tsserver/nodeServer.ts
@@ -3,6 +3,7 @@ import fs from "fs";
import net from "net";
import os from "os";
import readline from "readline";
+import { nodeCreateRequire } from "../compiler/nodeGetBuiltinModule.js";
import {
CharacterCodes,
combinePaths,
@@ -277,8 +278,9 @@ export function initializeNodeSystem(): StartInput {
let cancellationToken: ts.server.ServerCancellationToken;
try {
- const factory = require("./cancellationToken.js");
- cancellationToken = factory(sys.args);
+ const require = nodeCreateRequire(import.meta.url);
+ const { createCancellationToken } = require("./cancellationToken.js");
+ cancellationToken = createCancellationToken(sys.args);
}
catch {
cancellationToken = ts.server.nullCancellationToken;
diff --git a/src/typescript/typescript.ts b/src/typescript/typescript.ts
index 9e54bbe9c15b0..fa83f781938b1 100644
--- a/src/typescript/typescript.ts
+++ b/src/typescript/typescript.ts
@@ -23,3 +23,5 @@ if (typeof console !== "undefined") {
}
export * from "./_namespaces/ts.js";
+import * as ts from "./_namespaces/ts.js";
+export default ts;
diff --git a/testImportESM.mjs b/testImportESM.mjs
new file mode 100644
index 0000000000000..d4f29ec3a001a
--- /dev/null
+++ b/testImportESM.mjs
@@ -0,0 +1,5 @@
+import * as ts from "./built/local/typescript.js";
+import ts2 from "./built/local/typescript.js";
+
+console.log(ts.version);
+console.log(ts2.version);
diff --git a/testRequireESM.cjs b/testRequireESM.cjs
new file mode 100644
index 0000000000000..d6c1afae10252
--- /dev/null
+++ b/testRequireESM.cjs
@@ -0,0 +1,2 @@
+const ts = require("./built/local/typescript.js");
+console.log(ts.version);
diff --git a/tests/baselines/reference/APILibCheck.errors.txt b/tests/baselines/reference/APILibCheck.errors.txt
new file mode 100644
index 0000000000000..1dd48855ecd32
--- /dev/null
+++ b/tests/baselines/reference/APILibCheck.errors.txt
@@ -0,0 +1,48 @@
+tsserverlibrary.d.ts(17,15): error TS2498: Module '"typescript"' uses 'export =' and cannot be used with 'export *'.
+tsserverlibrary.internal.d.ts(17,15): error TS2498: Module '"typescript.internal"' uses 'export =' and cannot be used with 'export *'.
+
+
+==== node_modules/typescript/package.json (0 errors) ====
+ {
+ "name": "typescript",
+ "type": "module",
+ "exports": "./lib/typescript.d.ts"
+ }
+
+==== node_modules/typescript-internal/package.json (0 errors) ====
+ {
+ "name": "typescript-internal",
+ "type": "module",
+ "exports": "./lib/typescript.internal.d.ts"
+ }
+
+==== node_modules/tsserverlibrary/package.json (0 errors) ====
+ {
+ "name": "tsserverlibrary",
+ "type": "module",
+ "exports": "./lib/tsserverlibrary.d.ts"
+ }
+
+==== node_modules/tsserverlibrary-internal/package.json (0 errors) ====
+ {
+ "name": "tsserverlibrary-internal",
+ "type": "module",
+ "exports": "./lib/tsserverlibrary.internal.d.ts"
+ }
+
+==== package.json (0 errors) ====
+ {
+ "name": "project",
+ "type": "module"
+ }
+
+==== index.ts (0 errors) ====
+ import * as ts from "typescript";
+ import tsDefault from "typescript";
+ import * as tsInternal from "typescript-internal";
+ import tsInternalDefault from "typescript-internal";
+ import * as tsserverlibrary from "tsserverlibrary";
+ import tsserverlibraryDefault from "tsserverlibrary";
+ import * as tsserverlibraryInternal from "tsserverlibrary-internal";
+ import tsserverlibraryInternalDefault from "tsserverlibrary-internal";
+
\ No newline at end of file
diff --git a/tests/cases/compiler/APILibCheck.ts b/tests/cases/compiler/APILibCheck.ts
index d6620af33f468..6b71d6206115c 100644
--- a/tests/cases/compiler/APILibCheck.ts
+++ b/tests/cases/compiler/APILibCheck.ts
@@ -1,4 +1,4 @@
-// @module: commonjs
+// @module: nodenext
// @noImplicitAny: true
// @strictNullChecks: true
// @lib: es2018
@@ -6,32 +6,50 @@
// @noTypesAndSymbols: true
// @noEmit: true
+// @link: /.ts/typescript.d.ts -> node_modules/typescript/lib/typescript.d.ts
// @filename: node_modules/typescript/package.json
{
"name": "typescript",
- "types": "/.ts/typescript.d.ts"
+ "type": "module",
+ "exports": "./lib/typescript.d.ts"
}
+// @link: /.ts/typescript.internal.d.ts -> node_modules/typescript-internal/lib/typescript.internal.d.ts
// @filename: node_modules/typescript-internal/package.json
{
"name": "typescript-internal",
- "types": "/.ts/typescript.internal.d.ts"
+ "type": "module",
+ "exports": "./lib/typescript.internal.d.ts"
}
+// @link: /.ts/tsserverlibrary.d.ts -> node_modules/tsserverlibrary/lib/tsserverlibrary.d.ts
// @filename: node_modules/tsserverlibrary/package.json
{
"name": "tsserverlibrary",
- "types": "/.ts/tsserverlibrary.d.ts"
+ "type": "module",
+ "exports": "./lib/tsserverlibrary.d.ts"
}
+// @link: /.ts/tsserverlibrary.internal.d.ts -> node_modules/tsserverlibrary-internal/lib/tsserverlibrary.internal.d.ts
// @filename: node_modules/tsserverlibrary-internal/package.json
{
"name": "tsserverlibrary-internal",
- "types": "/.ts/tsserverlibrary.internal.d.ts"
+ "type": "module",
+ "exports": "./lib/tsserverlibrary.internal.d.ts"
+}
+
+// @filename: package.json
+{
+ "name": "project",
+ "type": "module"
}
// @filename: index.ts
-import ts = require("typescript");
-import tsInternal = require("typescript-internal");
-import tsserverlibrary = require("tsserverlibrary");
-import tsserverlibraryInternal = require("tsserverlibrary-internal");
+import * as ts from "typescript";
+import tsDefault from "typescript";
+import * as tsInternal from "typescript-internal";
+import tsInternalDefault from "typescript-internal";
+import * as tsserverlibrary from "tsserverlibrary";
+import tsserverlibraryDefault from "tsserverlibrary";
+import * as tsserverlibraryInternal from "tsserverlibrary-internal";
+import tsserverlibraryInternalDefault from "tsserverlibrary-internal";