Skip to content

Commit 63285f4

Browse files
committed
Build fixups
1 parent 59e7b6f commit 63285f4

File tree

3 files changed

+73
-42
lines changed

3 files changed

+73
-42
lines changed

Herebyfile.mjs

+66-35
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ function getCopyrightHeader() {
3131
const cleanTasks = [];
3232

3333

34-
// TODO(jakebailey): This is really gross. Waiting on https://github.com/microsoft/TypeScript/issues/25613,
35-
// or at least control over noEmit / emitDeclarationOnly in build mode.
34+
// TODO(jakebailey): This is really gross. If the build is cancelled (i.e. Ctrl+C), the modification will persist.
35+
// Waiting on: https://github.com/microsoft/TypeScript/issues/51164
3636
let currentlyBuilding = 0;
3737
let oldTsconfigBase;
3838

@@ -180,18 +180,18 @@ async function runDtsBundler(entrypoint, output) {
180180
}
181181

182182
/**
183+
* @typedef {{
184+
external?: string[];
185+
exportIsTsObject?: boolean;
186+
setDynamicImport?: boolean;
187+
}} ESBuildTaskOptions
183188
* @param {string} entrypoint
184189
* @param {string} outfile
185-
* @param {boolean} exportIsTsObject True if this file exports the TS object and should have relevant code injected.
190+
* @param {ESBuildTaskOptions | undefined} [taskOptions]
186191
*/
187-
function esbuildTask(entrypoint, outfile, exportIsTsObject = false) {
192+
function esbuildTask(entrypoint, outfile, taskOptions = {}) {
188193
return {
189194
build: async () => {
190-
// Note: we do not use --minify, as that would hide function names from user backtraces
191-
// (we don't ship our sourcemaps), and would break consumers like monaco which modify
192-
// typescript.js for their own needs. Also, using --sourcesContent=false doesn't help,
193-
// as even though it's a smaller source map that could be shipped to users for better
194-
// stack traces via names, the maps are bigger than the actual source files themselves.
195195
/** @type {esbuild.BuildOptions} */
196196
const options = {
197197
entryPoints: [entrypoint],
@@ -202,14 +202,37 @@ async function runDtsBundler(entrypoint, output) {
202202
target: "es2018", // Covers Node 10.
203203
format: "cjs",
204204
sourcemap: "linked",
205-
external: ["./node_modules/*"],
206-
conditions: ["require"],
205+
sourcesContent: false,
206+
external: [
207+
...(taskOptions.external ?? []),
208+
"source-map-support",
209+
],
207210
supported: {
208211
// "const-and-let": false, // https://github.com/evanw/esbuild/issues/297
209212
"object-rest-spread": false, // Performance enhancement, see: https://github.com/evanw/esbuild/releases/tag/v0.14.46
210213
},
214+
logLevel: "warning",
211215
// legalComments: "none", // If we add copyright headers to the source files, uncomment.
212216
plugins: [
217+
{
218+
name: "no-node-modules",
219+
setup: (build) => {
220+
build.onLoad({ filter: /[\\/]node_modules[\\/]/ }, () => {
221+
// Ideally, we'd use "--external:./node_modules/*" here, but that doesn't work; we
222+
// will instead end up with paths to node_modules rather than the package names.
223+
// Instead, we'll return a load error when we see that we're trying to bundle from
224+
// node_modules, then explicitly declare which external dependencies we rely on, which
225+
// ensures that the correct module specifier is kept in the output (the non-wildcard
226+
// form works properly). It also helps us keep tabs on what external dependencies we
227+
// may be importing, which is handy.
228+
//
229+
// See: https://github.com/evanw/esbuild/issues/1958
230+
return {
231+
errors: [{ text: 'Attempted to bundle from node_modules; ensure "external" is set correctly.' }]
232+
};
233+
});
234+
}
235+
},
213236
{
214237
name: "fix-require",
215238
setup: (build) => {
@@ -220,36 +243,36 @@ async function runDtsBundler(entrypoint, output) {
220243
// to be consumable by other bundlers, we need to convert these calls back to
221244
// require so our imports are visible again.
222245
//
223-
// Note that this step breaks source maps, but only for lines that reference
224-
// "__require", which is an okay tradeoff for the performance of not running
225-
// the output through transpileModule/babel/etc.
246+
// The leading spaces are to keep the offsets the same within the files to keep
247+
// source maps working (though this only really matters for the line the require is on).
226248
//
227249
// See: https://github.com/evanw/esbuild/issues/1905
228250
let contents = await fs.promises.readFile(outfile, "utf-8");
229-
contents = contents.replace(/__require\(/g, "require(");
251+
contents = contents.replace(/__require\(/g, " require(");
230252
await fs.promises.writeFile(outfile, contents);
231253
});
232254
},
233255
}
234256
]
235257
};
236258

237-
if (exportIsTsObject) {
238-
options.format = "iife"; // We use an IIFE so we can inject the code below.
259+
if (taskOptions.exportIsTsObject) {
260+
// These snippets cannot appear in the actual source files, otherwise they will be rewritten
261+
// to things like exports or requires.
262+
263+
// If we are in a CJS context, export the ts namespace.
264+
let footer = `\nif (typeof module !== "undefined" && module.exports) { module.exports = ts; }`;
265+
266+
if (taskOptions.setDynamicImport) {
267+
// If we are in a server bundle, inject the dynamicImport function.
268+
// This only works because the web server's "start" function returns;
269+
// the node server does not, but we don't use this there.
270+
footer += `\nif (ts.server && ts.server.setDynamicImport) { ts.server.setDynamicImport(id => import(id)); }`;
271+
}
272+
273+
options.format = "iife"; // We use an IIFE so we can inject the footer, and so that "ts" is global if not loaded as a module.
239274
options.globalName = "ts"; // Name the variable ts, matching our old big bundle and so we can use the code below.
240-
options.footer = {
241-
// These snippets cannot appear in the actual source files, otherwise they will be rewritten
242-
// to things like exports or requires.
243-
js: `
244-
if (typeof module !== "undefined" && module.exports) {
245-
// If we are in a CJS context, export the ts namespace.
246-
module.exports = ts;
247-
}
248-
if (ts.server) {
249-
// If we are in a server bundle, inject the dynamicImport function.
250-
ts.server.dynamicImport = id => import(id);
251-
}`
252-
};
275+
options.footer = { js: footer };
253276
}
254277

255278
await esbuild.build(options);
@@ -286,7 +309,7 @@ const cleanDebugTools = task({
286309
cleanTasks.push(cleanDebugTools);
287310

288311

289-
const esbuildTsc = esbuildTask("./src/tsc/tsc.ts", "./built/local/tsc.js", /* exportIsTsObject */ true);
312+
const esbuildTsc = esbuildTask("./src/tsc/tsc.ts", "./built/local/tsc.js");
290313

291314
export const buildTsc = task({
292315
name: "tsc",
@@ -315,7 +338,7 @@ const buildServicesWithTsc = task({
315338

316339
// TODO(jakebailey): rename this; no longer "services".
317340

318-
const esbuildServices = esbuildTask("./src/typescript/typescript.ts", "./built/local/typescript.js", /* exportIsTsObject */ true);
341+
const esbuildServices = esbuildTask("./src/typescript/typescript.ts", "./built/local/typescript.js", { exportIsTsObject: true });
319342

320343
export const buildServices = task({
321344
name: "services",
@@ -343,7 +366,7 @@ export const dtsServices = task({
343366
run: () => runDtsBundler("./built/local/typescript/typescript.d.ts", "./built/local/typescript.d.ts"),
344367
});
345368

346-
const esbuildServer = esbuildTask("./src/tsserver/server.ts", "./built/local/tsserver.js", /* exportIsTsObject */ true);
369+
const esbuildServer = esbuildTask("./src/tsserver/server.ts", "./built/local/tsserver.js", { exportIsTsObject: true, setDynamicImport: true });
347370

348371
export const buildServer = task({
349372
name: "tsserver",
@@ -384,7 +407,7 @@ const buildLsslWithTsc = task({
384407
run: () => buildProject("src/tsserverlibrary"),
385408
});
386409

387-
const esbuildLssl = esbuildTask("./src/tsserverlibrary/tsserverlibrary.ts", "./built/local/tsserverlibrary.js", /* exportIsTsObject */ true);
410+
const esbuildLssl = esbuildTask("./src/tsserverlibrary/tsserverlibrary.ts", "./built/local/tsserverlibrary.js", { exportIsTsObject: true });
388411

389412
export const buildLssl = task({
390413
name: "lssl",
@@ -418,7 +441,15 @@ export const dts = task({
418441

419442

420443
const testRunner = "./built/local/run.js";
421-
const esbuildTests = esbuildTask("./src/testRunner/_namespaces/Harness.ts", testRunner);
444+
const esbuildTests = esbuildTask("./src/testRunner/_namespaces/Harness.ts", testRunner, {
445+
external: [
446+
"chai",
447+
"del",
448+
"diff",
449+
"mocha",
450+
"ms",
451+
],
452+
});
422453

423454
export const buildTests = task({
424455
name: "tests",

src/compiler/sys.ts

+1-4
Original file line numberDiff line numberDiff line change
@@ -1567,10 +1567,7 @@ export let sys: System = (() => {
15671567
debugMode: !!process.env.NODE_INSPECTOR_IPC || !!process.env.VSCODE_INSPECTOR_OPTIONS || some(process.execArgv as string[], arg => /^--(inspect|debug)(-brk)?(=\d+)?$/i.test(arg)),
15681568
tryEnableSourceMapsForHost() {
15691569
try {
1570-
// Trick esbuild into not eagerly resolving a path to a JS file.
1571-
// See: https://github.com/evanw/esbuild/issues/1958
1572-
const moduleName = "source-map-support" as const;
1573-
(require(moduleName) as typeof import("source-map-support")).install();
1570+
(require("source-map-support") as typeof import("source-map-support")).install();
15741571
}
15751572
catch {
15761573
// Could not enable source maps.

src/webServer/webServer.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -125,12 +125,15 @@ export class MainProcessLogger extends BaseLogger {
125125
}
126126
}
127127

128-
/** @internal */
129-
// eslint-disable-next-line prefer-const
130-
export let dynamicImport = async (_id: string): Promise<any> => {
128+
let dynamicImport = async (_id: string): Promise<any> => {
131129
throw new Error("Dynamic import not implemented");
132130
};
133131

132+
/** @internal */
133+
export function setDynamicImport(fn: (id: string) => Promise<any>) {
134+
dynamicImport = fn;
135+
}
136+
134137
/** @internal */
135138
export function createWebSystem(host: WebHost, args: string[], getExecutingFilePath: () => string): ServerHost {
136139
const returnEmptyString = () => "";

0 commit comments

Comments
 (0)