mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 03:09:39 -06:00
"Annotate" exported object to fix named / namespace imports of our API in Node ESM (#57133)
This commit is contained in:
parent
6d458e81cd
commit
320e17f122
@ -2,6 +2,7 @@
|
||||
import {
|
||||
CancelToken,
|
||||
} from "@esfx/canceltoken";
|
||||
import assert from "assert";
|
||||
import chalk from "chalk";
|
||||
import chokidar from "chokidar";
|
||||
import esbuild from "esbuild";
|
||||
@ -46,7 +47,7 @@ import {
|
||||
void 0;
|
||||
|
||||
const copyrightFilename = "./scripts/CopyrightNotice.txt";
|
||||
const copyright = memoize(async () => {
|
||||
const getCopyrightHeader = memoize(async () => {
|
||||
const contents = await fs.promises.readFile(copyrightFilename, "utf-8");
|
||||
return contents.replace(/\r\n/g, "\n");
|
||||
});
|
||||
@ -76,7 +77,7 @@ export const generateLibs = task({
|
||||
run: async () => {
|
||||
await fs.promises.mkdir("./built/local", { recursive: true });
|
||||
for (const lib of libs()) {
|
||||
let output = await copyright();
|
||||
let output = await getCopyrightHeader();
|
||||
|
||||
for (const source of lib.sources) {
|
||||
const contents = await fs.promises.readFile(source, "utf-8");
|
||||
@ -187,10 +188,13 @@ 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: await copyright() },
|
||||
banner: { js: copyright + banner },
|
||||
bundle: true,
|
||||
outfile,
|
||||
platform: "node",
|
||||
@ -205,12 +209,10 @@ function createBundler(entrypoint, outfile, taskOptions = {}) {
|
||||
};
|
||||
|
||||
if (taskOptions.exportIsTsObject) {
|
||||
// We use an IIFE so we can inject the footer, and so that "ts" is global if not loaded as a module.
|
||||
options.format = "iife";
|
||||
// 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; }` };
|
||||
// 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: `})(typeof module !== "undefined" && module.exports ? module : { exports: ts });\nif (typeof module !== "undefined" && module.exports) { ts = module.exports; }` };
|
||||
|
||||
// esbuild converts calls to "require" to "__require"; this function
|
||||
// calls the real require if it exists, or throws if it does not (rather than
|
||||
@ -227,13 +229,25 @@ function createBundler(entrypoint, outfile, taskOptions = {}) {
|
||||
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 = [
|
||||
{
|
||||
name: "fix-require",
|
||||
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);
|
||||
});
|
||||
},
|
||||
@ -450,7 +464,7 @@ export = ts;
|
||||
* @param {string} contents
|
||||
*/
|
||||
async function fileContentsWithCopyright(contents) {
|
||||
return await copyright() + contents.trim().replace(/\r\n/g, "\n") + "\n";
|
||||
return await getCopyrightHeader() + contents.trim().replace(/\r\n/g, "\n") + "\n";
|
||||
}
|
||||
|
||||
const lssl = task({
|
||||
|
||||
@ -5,31 +5,37 @@ import {
|
||||
__importDefault,
|
||||
__importStar,
|
||||
} from "tslib";
|
||||
import {
|
||||
pathToFileURL,
|
||||
} from "url";
|
||||
|
||||
// This script tests that TypeScript's CJS API is structured
|
||||
// as expected. It calls "require" as though it were in CWD,
|
||||
// so it can be tested on a separate install of TypeScript.
|
||||
|
||||
const require = createRequire(process.cwd() + "/index.js");
|
||||
const typescript = process.argv[2];
|
||||
const resolvedTypeScript = pathToFileURL(require.resolve(typescript)).toString();
|
||||
|
||||
console.log(`Testing ${process.argv[2]}...`);
|
||||
const ts = require(process.argv[2]);
|
||||
console.log(`Testing ${typescript}...`);
|
||||
|
||||
// See: https://github.com/microsoft/TypeScript/pull/51474#issuecomment-1310871623
|
||||
/** @type {[fn: (() => any), shouldSucceed: boolean][]} */
|
||||
/** @type {[fn: (() => Promise<any>), shouldSucceed: boolean][]} */
|
||||
const fns = [
|
||||
[() => ts.version, true],
|
||||
[() => ts.default.version, false],
|
||||
[() => __importDefault(ts).version, false],
|
||||
[() => __importDefault(ts).default.version, true],
|
||||
[() => __importStar(ts).version, true],
|
||||
[() => __importStar(ts).default.version, true],
|
||||
[() => require(typescript).version, true],
|
||||
[() => require(typescript).default.version, false],
|
||||
[() => __importDefault(require(typescript)).version, false],
|
||||
[() => __importDefault(require(typescript)).default.version, true],
|
||||
[() => __importStar(require(typescript)).version, true],
|
||||
[() => __importStar(require(typescript)).default.version, true],
|
||||
[async () => (await import(resolvedTypeScript)).version, true],
|
||||
[async () => (await import(resolvedTypeScript)).default.version, true],
|
||||
];
|
||||
|
||||
for (const [fn, shouldSucceed] of fns) {
|
||||
let success = false;
|
||||
try {
|
||||
success = !!fn();
|
||||
success = !!(await fn());
|
||||
}
|
||||
catch {
|
||||
// Ignore
|
||||
@ -43,4 +49,10 @@ for (const [fn, shouldSucceed] of fns) {
|
||||
process.exitCode = 1;
|
||||
}
|
||||
}
|
||||
console.log("ok");
|
||||
|
||||
if (process.exitCode) {
|
||||
console.log("fail");
|
||||
}
|
||||
else {
|
||||
console.log("ok");
|
||||
}
|
||||
|
||||
@ -2743,12 +2743,8 @@ export function skipWhile<T, U extends T>(array: readonly T[] | undefined, predi
|
||||
export function isNodeLikeSystem(): boolean {
|
||||
// This is defined here rather than in sys.ts to prevent a cycle from its
|
||||
// use in performanceCore.ts.
|
||||
//
|
||||
// We don't use the presence of `require` to check if we are in Node;
|
||||
// when bundled using esbuild, this function will be rewritten to `__require`
|
||||
// and definitely exist.
|
||||
return typeof process !== "undefined"
|
||||
&& !!process.nextTick
|
||||
&& !(process as any).browser
|
||||
&& typeof module === "object";
|
||||
&& typeof require !== "undefined";
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@ import {
|
||||
Debug,
|
||||
LogLevel,
|
||||
} from "./_namespaces/ts";
|
||||
import * as ts from "./_namespaces/ts";
|
||||
|
||||
// enable deprecation logging
|
||||
declare const console: any;
|
||||
@ -23,4 +22,4 @@ if (typeof console !== "undefined") {
|
||||
};
|
||||
}
|
||||
|
||||
export = ts;
|
||||
export * from "./_namespaces/ts";
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user