Skip to content

Commit 5245654

Browse files
committed
Build fixups
1 parent 72b92d6 commit 5245654

File tree

3 files changed

+73
-42
lines changed

3 files changed

+73
-42
lines changed

Herebyfile.mjs

Lines changed: 66 additions & 35 deletions
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,10 +202,33 @@ 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+
],
210+
logLevel: "warning",
207211
// legalComments: "none", // If we add copyright headers to the source files, uncomment.
208212
plugins: [
213+
{
214+
name: "no-node-modules",
215+
setup: (build) => {
216+
build.onLoad({ filter: /[\\/]node_modules[\\/]/ }, () => {
217+
// Ideally, we'd use "--external:./node_modules/*" here, but that doesn't work; we
218+
// will instead end up with paths to node_modules rather than the package names.
219+
// Instead, we'll return a load error when we see that we're trying to bundle from
220+
// node_modules, then explicitly declare which external dependencies we rely on, which
221+
// ensures that the correct module specifier is kept in the output (the non-wildcard
222+
// form works properly). It also helps us keep tabs on what external dependencies we
223+
// may be importing, which is handy.
224+
//
225+
// See: https://github.com/evanw/esbuild/issues/1958
226+
return {
227+
errors: [{ text: 'Attempted to bundle from node_modules; ensure "external" is set correctly.' }]
228+
};
229+
});
230+
}
231+
},
209232
{
210233
name: "fix-require",
211234
setup: (build) => {
@@ -216,36 +239,36 @@ async function runDtsBundler(entrypoint, output) {
216239
// to be consumable by other bundlers, we need to convert these calls back to
217240
// require so our imports are visible again.
218241
//
219-
// Note that this step breaks source maps, but only for lines that reference
220-
// "__require", which is an okay tradeoff for the performance of not running
221-
// the output through transpileModule/babel/etc.
242+
// The leading spaces are to keep the offsets the same within the files to keep
243+
// source maps working (though this only really matters for the line the require is on).
222244
//
223245
// See: https://github.com/evanw/esbuild/issues/1905
224246
let contents = await fs.promises.readFile(outfile, "utf-8");
225-
contents = contents.replace(/__require\(/g, "require(");
247+
contents = contents.replace(/__require\(/g, " require(");
226248
await fs.promises.writeFile(outfile, contents);
227249
});
228250
},
229251
}
230252
]
231253
};
232254

233-
if (exportIsTsObject) {
234-
options.format = "iife"; // We use an IIFE so we can inject the code below.
255+
if (taskOptions.exportIsTsObject) {
256+
// These snippets cannot appear in the actual source files, otherwise they will be rewritten
257+
// to things like exports or requires.
258+
259+
// If we are in a CJS context, export the ts namespace.
260+
let footer = `\nif (typeof module !== "undefined" && module.exports) { module.exports = ts; }`;
261+
262+
if (taskOptions.setDynamicImport) {
263+
// If we are in a server bundle, inject the dynamicImport function.
264+
// This only works because the web server's "start" function returns;
265+
// the node server does not, but we don't use this there.
266+
footer += `\nif (ts.server && ts.server.setDynamicImport) { ts.server.setDynamicImport(id => import(id)); }`;
267+
}
268+
269+
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.
235270
options.globalName = "ts"; // Name the variable ts, matching our old big bundle and so we can use the code below.
236-
options.footer = {
237-
// These snippets cannot appear in the actual source files, otherwise they will be rewritten
238-
// to things like exports or requires.
239-
js: `
240-
if (typeof module !== "undefined" && module.exports) {
241-
// If we are in a CJS context, export the ts namespace.
242-
module.exports = ts;
243-
}
244-
if (ts.server) {
245-
// If we are in a server bundle, inject the dynamicImport function.
246-
ts.server.dynamicImport = id => import(id);
247-
}`
248-
};
271+
options.footer = { js: footer };
249272
}
250273

251274
await esbuild.build(options);
@@ -282,7 +305,7 @@ const cleanDebugTools = task({
282305
cleanTasks.push(cleanDebugTools);
283306

284307

285-
const esbuildTsc = esbuildTask("./src/tsc/tsc.ts", "./built/local/tsc.js", /* exportIsTsObject */ true);
308+
const esbuildTsc = esbuildTask("./src/tsc/tsc.ts", "./built/local/tsc.js");
286309

287310
export const buildTsc = task({
288311
name: "tsc",
@@ -311,7 +334,7 @@ const buildServicesWithTsc = task({
311334

312335
// TODO(jakebailey): rename this; no longer "services".
313336

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

316339
export const buildServices = task({
317340
name: "services",
@@ -339,7 +362,7 @@ export const dtsServices = task({
339362
run: () => runDtsBundler("./built/local/typescript/typescript.d.ts", "./built/local/typescript.d.ts"),
340363
});
341364

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

344367
export const buildServer = task({
345368
name: "tsserver",
@@ -380,7 +403,7 @@ const buildLsslWithTsc = task({
380403
run: () => buildProject("src/tsserverlibrary"),
381404
});
382405

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

385408
export const buildLssl = task({
386409
name: "lssl",
@@ -414,7 +437,15 @@ export const dts = task({
414437

415438

416439
const testRunner = "./built/local/run.js";
417-
const esbuildTests = esbuildTask("./src/testRunner/_namespaces/Harness.ts", testRunner);
440+
const esbuildTests = esbuildTask("./src/testRunner/_namespaces/Harness.ts", testRunner, {
441+
external: [
442+
"chai",
443+
"del",
444+
"diff",
445+
"mocha",
446+
"ms",
447+
],
448+
});
418449

419450
export const buildTests = task({
420451
name: "tests",

src/compiler/sys.ts

Lines changed: 1 addition & 4 deletions
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

Lines changed: 6 additions & 3 deletions
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)