Skip to content

Instantly share code, notes, and snippets.

@benjie
Last active October 26, 2021 12:03
Show Gist options
  • Save benjie/0e79262ac5b50f86c89cda5df8e3f2ae to your computer and use it in GitHub Desktop.
Save benjie/0e79262ac5b50f86c89cda5df8e3f2ae to your computer and use it in GitHub Desktop.
A script for running SWC in a monorepo that uses TypeScript project references (`tsc --build` / `tsc -b`) compiling everything in parallel with optional watch mode. For this to work you MUST have `isolatedModules: true`.
const swc = require("@swc/core");
const chokidar = require("chokidar");
const glob = require("glob");
const { promises: fsp } = require("fs");
const { basename, dirname } = require("path");
const { createHash } = require("crypto");
const mkdirp = require("mkdirp");
const WATCH = process.argv[2] === "--watch";
const paths = new Set();
const hashes = new Map();
let inProgress = false;
let runAgain = false;
let ready = false;
function doIt() {
inProgress = true;
runAgain = false;
const allPaths = [...paths];
const promises = allPaths.map(async (path) => {
try {
const contents = await fsp.readFile(path, "utf8");
const hash = createHash("sha256").update(contents).digest("hex");
if (hashes.get(path) === hash) {
// No change necessary
return;
}
hashes.set(path, hash);
const distBase = path.replace("/src/", "/dist/").replace(/.[jt]sx?$/, "");
const distBaseFolder = dirname(distBase);
await new Promise((resolve, reject) =>
mkdirp(distBaseFolder, (err) => (err ? reject(err) : resolve()))
);
if (path.endsWith(".json")) {
await fsp.writeFile(`${distBase}.json`, contents);
} else {
const { code, map } = await swc.transform(contents, {
filename: path,
sourceMaps: true,
});
await fsp.writeFile(`${distBase}.js`, code);
await fsp.writeFile(`${distBase}.js.map`, map);
}
} catch (e) {
throw new Error(`Error whilst processing ${path}: ${e.message}`);
}
});
const done = () => {
inProgress = false;
if (runAgain) {
doIt();
}
};
return Promise.all(promises).then(
() => {
done();
},
(err) => {
done();
if (WATCH) {
console.error("COMPILATION ERROR", err);
return null;
} else {
return Promise.reject(err);
}
}
);
}
function queueDoIt() {
if (!inProgress) {
doIt();
} else {
runAgain = true;
}
}
if (WATCH) {
const srcs = glob
.sync("packages/*/", {
cwd: `${__dirname}/..`,
})
.map((p) => `${p}/src`);
console.log(`Watching ${srcs.join(", ")}`);
const watcher = chokidar.watch(srcs);
watcher.on("add", (path) => {
if (path.match(/\.[jt]sx?$|\.json$/)) {
paths.add(path);
if (ready) {
queueDoIt();
}
}
});
watcher.on("change", (path) => {
console.log(`CHANGED: ${path}`);
if (ready) {
queueDoIt();
}
});
watcher.on("unlink", (path) => {
paths.delete(path);
});
watcher.on("ready", () => {
ready = true;
doIt();
});
} else {
const allSrcFiles = glob.sync(
"packages/*/src/**/*.{js,jsx,ts,tsx,json}",
{
cwd: `${__dirname}/..`,
nodir: true,
}
);
allSrcFiles.forEach((path) => paths.add(path));
doIt().catch((e) => {
console.error(e);
process.exit(1);
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment