Skip to content

Commit 2307ef0

Browse files
committed
Make things work in ESM with polyfill
1 parent 5fbe3cf commit 2307ef0

29 files changed

+117
-203
lines changed

Herebyfile.mjs

Lines changed: 9 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// @ts-check
22
import { CancelToken } from "@esfx/canceltoken";
3-
import assert from "assert";
43
import chalk from "chalk";
54
import chokidar from "chokidar";
65
import esbuild from "esbuild";
@@ -180,17 +179,15 @@ async function runDtsBundler(entrypoint, output) {
180179
function createBundler(entrypoint, outfile, taskOptions = {}) {
181180
const getOptions = memoize(async () => {
182181
const copyright = await getCopyrightHeader();
183-
const banner = taskOptions.exportIsTsObject ? "var ts = {}; ((module) => {" : "";
184-
185182
/** @type {esbuild.BuildOptions} */
186183
const options = {
187184
entryPoints: [entrypoint],
188-
banner: { js: copyright + banner },
185+
banner: { js: copyright },
189186
bundle: true,
190187
outfile,
191188
platform: "node",
192189
target: ["es2020", "node14.17"],
193-
format: "cjs",
190+
format: "esm",
194191
sourcemap: "linked",
195192
sourcesContent: false,
196193
treeShaking: taskOptions.treeShaking,
@@ -203,63 +200,15 @@ function createBundler(entrypoint, outfile, taskOptions = {}) {
203200
options.external = ["./typescript.js"];
204201
options.plugins = options.plugins || [];
205202
options.plugins.push({
206-
name: "remap-typescript-to-require",
203+
name: "remap-typescript-to-public-api",
207204
setup(build) {
208205
build.onLoad({ filter: /src[\\/]typescript[\\/]typescript\.ts$/ }, () => {
209-
return { contents: `export * from "./typescript.js"` };
206+
return { contents: `export * from "./typescript.js"` }; // TODO(jakebailey): require(ESM) - this remapping can be fixed up
210207
});
211208
},
212209
});
213210
}
214211

215-
if (taskOptions.exportIsTsObject) {
216-
// Monaco bundles us as ESM by wrapping our code with something that defines module.exports
217-
// but then does not use it, instead using the `ts` variable. Ensure that if we think we're CJS
218-
// that we still set `ts` to the module.exports object.
219-
options.footer = { js: `})(typeof module !== "undefined" && module.exports ? module : { exports: ts });\nif (typeof module !== "undefined" && module.exports) { ts = module.exports; }` };
220-
221-
// esbuild converts calls to "require" to "__require"; this function
222-
// calls the real require if it exists, or throws if it does not (rather than
223-
// throwing an error like "require not defined"). But, since we want typescript
224-
// to be consumable by other bundlers, we need to convert these calls back to
225-
// require so our imports are visible again.
226-
//
227-
// To fix this, we redefine "require" to a name we're unlikely to use with the
228-
// same length as "require", then replace it back to "require" after bundling,
229-
// ensuring that source maps still work.
230-
//
231-
// See: https://github.com/evanw/esbuild/issues/1905
232-
const require = "require";
233-
const fakeName = "Q".repeat(require.length);
234-
const fakeNameRegExp = new RegExp(fakeName, "g");
235-
options.define = { [require]: fakeName };
236-
237-
// For historical reasons, TypeScript does not set __esModule. Hack esbuild's __toCommonJS to be a noop.
238-
// We reference `__copyProps` to ensure the final bundle doesn't have any unreferenced code.
239-
const toCommonJsRegExp = /var __toCommonJS .*/;
240-
const toCommonJsRegExpReplacement = "var __toCommonJS = (mod) => (__copyProps, mod); // Modified helper to skip setting __esModule.";
241-
242-
options.plugins = options.plugins || [];
243-
options.plugins.push(
244-
{
245-
name: "post-process",
246-
setup: build => {
247-
build.onEnd(async () => {
248-
let contents = await fs.promises.readFile(outfile, "utf-8");
249-
contents = contents.replace(fakeNameRegExp, require);
250-
let matches = 0;
251-
contents = contents.replace(toCommonJsRegExp, () => {
252-
matches++;
253-
return toCommonJsRegExpReplacement;
254-
});
255-
assert(matches === 1, "Expected exactly one match for __toCommonJS");
256-
await fs.promises.writeFile(outfile, contents);
257-
});
258-
},
259-
},
260-
);
261-
}
262-
263212
return options;
264213
});
265214

@@ -324,13 +273,13 @@ function entrypointBuildTask(options) {
324273
});
325274

326275
/**
327-
* Writes a CJS module that reexports another CJS file. E.g. given
276+
* Writes a module that reexports another file. E.g. given
328277
* `options.builtEntrypoint = "./built/local/tsc/tsc.js"` and
329278
* `options.output = "./built/local/tsc.js"`, this will create a file
330279
* named "./built/local/tsc.js" containing:
331280
*
332281
* ```
333-
* module.exports = require("./tsc/tsc.js")
282+
* export * from "./tsc/tsc.js";
334283
* ```
335284
*/
336285
const shim = task({
@@ -339,7 +288,7 @@ function entrypointBuildTask(options) {
339288
const outDir = path.dirname(options.output);
340289
await fs.promises.mkdir(outDir, { recursive: true });
341290
const moduleSpecifier = path.relative(outDir, options.builtEntrypoint);
342-
await fs.promises.writeFile(options.output, `module.exports = require("./${moduleSpecifier.replace(/[\\/]/g, "/")}")`);
291+
await fs.promises.writeFile(options.output, `export * from "./${moduleSpecifier.replace(/[\\/]/g, "/")}"`);
343292
},
344293
});
345294

@@ -447,6 +396,7 @@ export const watchMin = task({
447396

448397
// This is technically not enough to make tsserverlibrary loadable in the
449398
// browser, but it's unlikely that anyone has actually been doing that.
399+
// TODO(jakebailey): require(ESM) - fix this
450400
const lsslJs = `
451401
if (typeof module !== "undefined" && module.exports) {
452402
module.exports = require("./typescript.js");
@@ -504,7 +454,7 @@ const { main: tests, watch: watchTests } = entrypointBuildTask({
504454
description: "Builds the test infrastructure",
505455
buildDeps: [generateDiagnostics],
506456
project: "src/testRunner",
507-
srcEntrypoint: "./src/testRunner/_namespaces/Harness.ts",
457+
srcEntrypoint: "./src/testRunner/runner.ts",
508458
builtEntrypoint: "./built/local/testRunner/runner.js",
509459
output: testRunner,
510460
mainDeps: [generateLibs],

bin/tsc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
#!/usr/bin/env node
2-
require('../lib/tsc.js')
2+
import '../lib/tsc.js';

bin/tsserver

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
#!/usr/bin/env node
2-
require('../lib/tsserver.js')
2+
import '../lib/tsserver.js';

package-lock.json

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"@esfx/canceltoken": "^1.0.0",
4444
"@octokit/rest": "^20.0.2",
4545
"@types/chai": "^4.3.14",
46+
"@types/diff": "^5.2.1",
4647
"@types/microsoft__typescript-etw": "^0.1.3",
4748
"@types/minimist": "^1.2.5",
4849
"@types/mocha": "^10.0.6",

patchProcessGetBuiltin.cjs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const _module = require("module");
2+
3+
/** @type {(name: string) => any} */
4+
function getBuiltinModule(name) {
5+
if (!_module.isBuiltin(name)) return undefined;
6+
return require(name);
7+
}
8+
9+
process.getBuiltinModule = getBuiltinModule;

scripts/browserIntegrationTest.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ for (const browserType of browsers) {
2828

2929
await page.setContent(`
3030
<html>
31-
<script>${readFileSync(join("built", "local", "typescript.js"), "utf8")}</script>
31+
<script type="module">${readFileSync(join("built", "local", "typescript.js"), "utf8")}</script>
3232
</html>
3333
`);
3434

scripts/dtsBundler.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ function emitAsNamespace(name, parent, moduleSymbol, needExportModifier) {
465465

466466
emitAsNamespace("ts", "", moduleSymbol, /*needExportModifier*/ false);
467467

468+
// TODO(jakebailey): require(ESM) - fix this
468469
write("export = ts;", WriteTarget.Both);
469470

470471
const copyrightNotice = fs.readFileSync(path.join(__dirname, "CopyrightNotice.txt"), "utf-8");

src/.eslintrc.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@
1414
{ "name": "clearInterval" },
1515
{ "name": "setImmediate" },
1616
{ "name": "clearImmediate" },
17-
{ "name": "performance" }
17+
{ "name": "performance" },
18+
{ "name": "require" },
19+
{ "name": "__dirname" },
20+
{ "name": "__filename" }
1821
]
1922
},
2023
"overrides": [

src/cancellationToken/cancellationToken.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ function pipeExists(name: string): boolean {
1717
return fs.existsSync(name);
1818
}
1919

20-
function createCancellationToken(args: string[]): ServerCancellationToken {
20+
export function createCancellationToken(args: string[]): ServerCancellationToken {
2121
let cancellationPipeName: string | undefined;
2222
for (let i = 0; i < args.length - 1; i++) {
2323
if (args[i] === "--cancellationPipeName") {
@@ -66,4 +66,3 @@ function createCancellationToken(args: string[]): ServerCancellationToken {
6666
};
6767
}
6868
}
69-
export = createCancellationToken;

0 commit comments

Comments
 (0)