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";