js-barrels
Version:
A CLI for generating barrels in any Javascript project
182 lines (166 loc) • 4.7 kB
text/typescript
#!/usr/bin/env node
import { readdirSync, readFileSync, writeFileSync } from "fs";
import { join } from "path";
import yargs from "yargs";
import watch from "node-watch";
import chalk from "chalk";
const argv = yargs
.options({
dir: {
alias: "d",
demandOption: true,
type: "string",
description: "Directory to generate barrels for",
},
watch: {
alias: "w",
type: "boolean",
description: "Watch directory and update barrels",
},
ext: {
alias: "e",
type: "string",
description:
"Extension to create the barrel file with, auto-detected by default",
},
})
.parseSync();
function joinPath(...path: string[]) {
return join(process.cwd(), ...path);
}
function makeBarrels(watch: boolean, dir?: string, log?: boolean) {
const dirFiles = readdirSync(joinPath(dir || argv.dir));
const extension =
argv.ext || (dirFiles.join(",").includes(".ts") ? "ts" : "js");
const comments = `// AUTO GENERATED BY JS-BARRELS`;
if (dirFiles.includes(`index.${extension}`)) {
const fileContents = readFileSync(
joinPath(
dir || argv.dir,
dirFiles.find((f) => f === `index.${extension}`)!
)
).toString();
if (!fileContents.startsWith(comments)) {
console.log(
chalk.bgRed("\n ERROR "),
chalk.red(
"Directory already contains an index file not generated by js-barrels."
)
);
process.exit();
}
}
type Export = {
default: boolean;
name: string;
fileName: string;
};
const e: Export[] = [];
for (const file of dirFiles) {
if (file.includes(".")) {
const fileContents = readFileSync(
joinPath(dir || argv.dir, file)
).toString();
if (
(fileContents.includes("export") ||
file.includes("svelte") ||
file.includes("vue")) &&
!file.includes("index")
) {
let stringToSearch = "export default";
if (file.includes("svelte") || file.includes("vue")) {
e.push({
default: true,
name: file.split(".")[0],
fileName: `./${file}`,
});
} else {
if (fileContents.includes(stringToSearch)) {
const contents = fileContents
.substring(
fileContents.indexOf(stringToSearch),
fileContents.length
)
.split("\n")[0];
const filteredContents = contents.substring(
stringToSearch.length + 1,
contents.length
);
if (
filteredContents.startsWith("function") ||
filteredContents.startsWith("interface")
) {
let keyword = filteredContents.startsWith("function")
? "function"
: "interface";
const id = filteredContents.substring(
keyword.length + 1,
filteredContents.length
);
const token =
keyword === "function"
? id.substring(0, id.indexOf("("))
: id.substring(0, id.indexOf(" "));
e.push({
default: true,
name: token,
fileName: `./${
file.includes("ts") || file.includes("js")
? file.split(".")[0]
: file
}`,
});
}
}
if (fileContents.includes("export"))
e.push({
default: false,
name: "",
fileName: `./${
file.includes("ts") || file.includes("js")
? file.split(".")[0]
: file
}`,
});
}
}
} else {
makeBarrels(false, join(argv.dir, file), true);
}
}
const generatedFile = `${comments}
${e
.map((ex) =>
ex.default
? `export { default as ${ex.name} } from "${ex.fileName}";`
: `export * from "${ex.fileName}";`
)
.join("\n")}`;
writeFileSync(joinPath(dir || argv.dir, `index.${extension}`), generatedFile);
if (watch) {
console.log(
chalk.bgYellow(" FILE SAVED "),
chalk.yellow("Barrel files regenerated.")
);
} else if (!log) {
console.log(
"\n" + chalk.bgGreen(" SUCCESS "),
chalk.green("Barrel files generated.\n")
);
}
}
if (argv.watch) {
makeBarrels(false);
watch(
argv.dir,
{
recursive: false,
filter: (f) => !f.includes("index.ts") && !f.includes("index.js"),
},
() => {
makeBarrels(true);
}
);
} else {
makeBarrels(false);
}