Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Annotate" exported object to fix named / namespace imports of our API in Node ESM #57133

Merged
merged 20 commits into from
Mar 4, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Support named / namespace imports of TypeScript API
  • Loading branch information
jakebailey committed Jan 22, 2024
commit 43ea520782a5c22445516c28dd42d16499baaba8
28 changes: 27 additions & 1 deletion Herebyfile.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// @ts-check
import {

Check failure on line 2 in Herebyfile.mjs

View workflow job for this annotation

GitHub Actions / lint

Run autofix to sort these imports!
CancelToken,
} from "@esfx/canceltoken";
import chalk from "chalk";
Expand All @@ -16,6 +16,9 @@
task,
} from "hereby";
import path from "path";
import {
pathToFileURL,
} from "url";

import {
localizationDirectories,
Expand All @@ -41,6 +44,9 @@
readJson,
rimraf,
} from "./scripts/build/utils.mjs";
import {
inspect,
} from "util";

/** @typedef {ReturnType<typeof task>} Task */
void 0;
Expand Down Expand Up @@ -208,7 +214,8 @@
// Name the variable ts, matching our old big bundle and so we can use the code below.
options.globalName = "ts";
// If we are in a CJS context, export the ts namespace.
options.footer = { js: `\nif (typeof module !== "undefined" && module.exports) { module.exports = ts; }` };
options.footer = { js: `\nif (typeof module !== "undefined" && module.exports) {\n module.exports = ts;\n}` };
options.metafile = true;

// esbuild converts calls to "require" to "__require"; this function
// calls the real require if it exists, or throws if it does not (rather than
Expand All @@ -233,6 +240,25 @@
let contents = await fs.promises.readFile(outfile, "utf-8");
contents = contents.replace(fakeNameRegExp, require);
await fs.promises.writeFile(outfile, contents);

// This is a trick esbuild uses when emitting CJS to ensure that
// cjs-module-lexer sees the named exports. For example:
//
// module.exports = someComplicatedThing();
// 0 && (module.exports = {
// foo,
// bar,
// });
// TODO(jakebailey): this is slow; can we do this statically?
// esbuild's metafile option does not show exports...
// https://github.com/evanw/esbuild/issues/3110
// https://github.com/evanw/esbuild/issues/3281
const importUrl = pathToFileURL(outfile).toString();
const obj = await import(importUrl);
const names = Object.keys(obj.default);
const fakeExport = ` 0 && (module.exports = {\n${names.map(name => ` ${name},\n`).join("")} });`;
contents = contents.replace("module.exports = ts;", `module.exports = ts;\n${fakeExport}`);
await fs.promises.writeFile(outfile, contents);
});
},
},
Expand Down
2 changes: 1 addition & 1 deletion scripts/checkModuleFormat.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const fns = [
[() => __importDefault(require(typescript)).default.version, true],
[() => __importStar(require(typescript)).version, true],
[() => __importStar(require(typescript)).default.version, true],
[async () => (await import(typescript)).version, false],
[async () => (await import(typescript)).version, true],
[async () => (await import(typescript)).default.version, true],
];

Expand Down
Loading