Skip to content

Commit

Permalink
docs: add comments to tsbuild.mjs
Browse files Browse the repository at this point in the history
  • Loading branch information
Gordon Leigh committed Mar 4, 2024
1 parent 24c0b92 commit 3391f3a
Showing 1 changed file with 34 additions and 0 deletions.
34 changes: 34 additions & 0 deletions tsbuild.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
/**
* This script loads the packages in the npm workspace by inspecting the
* "workspaces" field in the root package.json. It finds cross-references
* between each of the workspace package.json, and then updates each package's
* tsconfig.json with a project reference for each cross reference. Finally, all
* discovered TypeScript projects are added to the root tsconfig.json.
*
* The tsconfig project references are used by the tsc compiler to build the
* projects in the correct order.
*
* @see {@link https://www.typescriptlang.org/docs/handbook/project-references.html}
*/
import { spawnSync } from "child_process";
import { readFile, writeFile } from "fs/promises";
import * as glob from "glob";
Expand All @@ -9,14 +21,17 @@ const WORKSPACE_ROOT = dirname(fileURLToPath(import.meta.url));
const PACKAGE_JSON = "package.json";
const TS_CONFIG = "tsconfig.json";

// load the prettier config from the workspace root
const prettierConfig = await Prettier.resolveConfig(
join(WORKSPACE_ROOT, TS_CONFIG),
);

// read the root package.json
const rootPkg = JSON.parse(
await readFile(join(WORKSPACE_ROOT, PACKAGE_JSON), "utf-8"),
);

// we expect there to be workspaces defined in the root package.
if (
!rootPkg.workspaces ||
!Array.isArray(rootPkg.workspaces) ||
Expand All @@ -26,10 +41,13 @@ if (
process.exit(1);
}

// the workspaces array might contain globs, so expand them
// see https://docs.npmjs.com/cli/v7/using-npm/workspaces
const packages = rootPkg.workspaces.flatMap((x) =>
glob.sync(x, { onlyDirectories: true }).map((p) => resolve(p)),
);

// bad workspace definition, no projects actually found
if (!packages.length) {
console.error(
`ERROR: no packages found in workspaces [${rootPkg.workspaces}]`,
Expand All @@ -39,17 +57,21 @@ if (!packages.length) {

const refs = {};

// look through each package we found
for (const pkg of packages) {
const packageJsonPath = join(pkg, PACKAGE_JSON);
let packageJson;

try {
// read/parse the package.json
packageJson = JSON.parse(await readFile(packageJsonPath, "utf8"));
} catch (err) {
// couldn't parse the package.json
console.warn(`WARN: skipping ${relative(WORKSPACE_ROOT, pkg)}: %o`, err);
continue;
}

// collect all candidate dependencies
const deps = [];
if (packageJson.dependencies) {
deps.push(...Object.keys(packageJson.dependencies));
Expand All @@ -62,14 +84,19 @@ for (const pkg of packages) {

const packageTsConfigPath = join(pkg, TS_CONFIG);
try {
// now load the tsconfig.json
tsConfig = JSON.parse(await readFile(packageTsConfigPath));

if (!tsConfig.compilerOptions?.composite) {
// composite option is required for build mode to work
// https://www.typescriptlang.org/tsconfig#composite
console.warn(
`skipping ${packageJson.name} because composite is not turned on`,
);
continue;
}
} catch {
// couldn't parse the tsconfig, or couldn't find it
continue;
}

Expand All @@ -81,20 +108,24 @@ for (const pkg of packages) {
};
}

// for each package we found, update the tsconfig with the cross-refs
for (const pkg of Object.values(refs)) {
await addRefsToTsConfig(
join(pkg.path, TS_CONFIG),
// filter the candidate refs to only include cross-refs
pkg.deps.filter((x) => x in refs).map((x) => refs[x].path),
pkg.tsConfig,
);
}

// save all the packages we found to the root tsconfig
const rootTsConfigPath = join(WORKSPACE_ROOT, TS_CONFIG);
await addRefsToTsConfig(
rootTsConfigPath,
Object.values(refs).map((x) => x.path),
);

// run tsc and forward any arguments from this script
const result = spawnSync(
join(WORKSPACE_ROOT, "node_modules/.bin/tsc"),
["-b", rootTsConfigPath, ...process.argv.slice(2)],
Expand All @@ -112,11 +143,13 @@ async function addRefsToTsConfig(configPath, references, contents) {
try {
contents = JSON.parse(await readFile(configPath, "utf-8"));
} catch (err) {
// file wasn't found, nothing to add then
if (err?.code !== "ENOENT") {
throw err;
}
}
}
// save the project references for each cross-ref
const output = JSON.stringify(
{
...contents,
Expand All @@ -127,6 +160,7 @@ async function addRefsToTsConfig(configPath, references, contents) {
null,
2,
);
// format the output with prettier to avoid lint problems
await writeFile(
configPath,
await Prettier.format(output, {
Expand Down

0 comments on commit 3391f3a

Please sign in to comment.