Skip to content

Commit

Permalink
feat: init
Browse files Browse the repository at this point in the history
  • Loading branch information
zanminkian committed Apr 14, 2024
1 parent fffe2e1 commit d55dcd7
Show file tree
Hide file tree
Showing 9 changed files with 391 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/tough-tigers-notice.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rnm/tscx": minor
---

feat: init
3 changes: 3 additions & 0 deletions packages/tscx/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
"license": "MIT",
"author": "hellozmj@qq.com",
"type": "module",
"scripts": {
"build": "tsc -p tsconfig.build.json"
},
"dependencies": {
"chokidar": "3.6.0",
"commander": "12.0.0"
Expand Down
68 changes: 68 additions & 0 deletions packages/tscx/src/action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import path from "node:path";
import process from "node:process";
import chokidar, { type FSWatcher } from "chokidar";
import { Compiler, type CompilerOptions } from "./compiler.js";

interface TscxOptions extends CompilerOptions {
watch: boolean;
}

export class Action {
private readonly compiler;
private watcher?: FSWatcher;
constructor(private readonly options: TscxOptions) {
this.compiler = new Compiler(options);
}

private setupWatcher() {
const include = this.compiler.getInclude() ?? [];
const watchFiles =
include.length <= 0
? [process.cwd()]
: include
.map((i) => path.resolve(process.cwd(), i))
.concat(path.resolve(process.cwd(), this.options.project));

this.watcher = chokidar.watch(watchFiles, {
// TODO use include
ignored: ["**/node_modules/**", "**/.git/**", this.compiler.getOutDir()],
ignoreInitial: true,
});
this.watcher
.on("add", (filepath) => this.cb(filepath))
.on("unlink", (filepath) => this.cb(filepath))
.on("change", (filepath) => this.cb(filepath))
.on("ready", () => this.cb());
}

private cb(filepath?: string) {
console.log("Recompile for the file updated", filepath);
if (
!filepath ||
path.resolve(process.cwd(), filepath) !==
path.resolve(process.cwd(), this.options.project)
) {
return this.compiler.exec();
}

this.compiler.refreshTsConfig();
this.watcher
?.close()
.then(() => {
this.setupWatcher();
})
.catch((e) => {
console.error("Close watcher fail!", e);
process.exit(1);
});
}

start() {
if (!this.options.watch) {
this.compiler.exec();
return;
}

this.setupWatcher();
}
}
38 changes: 38 additions & 0 deletions packages/tscx/src/bin/tscx.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import fs from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { Command } from "commander";
import { Action } from "../action.js";

const version: string = JSON.parse(
await fs.readFile(
path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
"..",
"..",
"package.json",
),
"utf8",
),
).version;

new Command()
.name("tscx")
.version(version)
.description("The TypeScript Compiler. Run `tsc` under the hood.")
.option(
"-p, --project <path>",
"Compile the project given the path to its configuration file, or to a folder with a 'tsconfig.json'.",
"tsconfig.json",
)
.option("--clean", "Remove output folder before before every compilation.")
.option(
"--copyfiles",
"Copy non-ts files to output folder after every compilation.",
)
.option("--run <path>", "Run the specified js file after compilation success")
.option("-w, --watch", "Watch input files.")
.action((options) => {
new Action(options).start();
})
.parse();
25 changes: 25 additions & 0 deletions packages/tscx/src/cmd/clean.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import fs from "node:fs";
import process from "node:process";

/**
* @param filepath absolute filepath
*/
async function clean(filepath: string) {
console.log(`Removing ${filepath}`);
await new Promise<void>((resolve, reject) => {
fs.stat(filepath, (err) => {
if (err) {
return err.code === "ENOENT" ? resolve() : reject(err); // do nothing if file not found
}
fs.rm(filepath, { recursive: true }, (e) => (e ? reject(e) : resolve()));
});
});
console.log(`Removed ${filepath}`);
}

const filepath = process.argv[2];
if (!filepath) {
throw new Error("File path is required");
}

await clean(filepath);
46 changes: 46 additions & 0 deletions packages/tscx/src/cmd/copyfiles.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import fs from "node:fs/promises";
import path from "node:path";
import process from "node:process";

/**
* Copy non-ts/non-js files to outDir
* @param rootDir absolute path
* @param outDir absolute path
*/
async function copyfiles(rootDir: string, outDir: string) {
rootDir = path.resolve(rootDir);
outDir = path.resolve(outDir);
async function walkDir(dir: string, cb: (filepath: string) => Promise<void>) {
await Promise.all(
(await fs.readdir(dir))
.map((filepath) => path.resolve(dir, filepath))
.map(async (filepath) => {
if ((await fs.stat(filepath)).isDirectory()) {
if (
filepath !== outDir &&
!filepath.endsWith(`${path.sep}node_modules`)
) {
await walkDir(filepath, cb);
}
} else {
if (!/\.(js|cjs|mjs|jsx|ts|cts|mts|tsx)$/.test(filepath)) {
await cb(filepath);
}
}
}),
);
}
await walkDir(rootDir, async (filepath) => {
const dest = filepath.replace(rootDir, outDir);
console.log("Copy", filepath, "=>", dest);
await fs.copyFile(filepath, dest);
});
}

const rootDir = process.argv[2];
const outDir = process.argv[3];
if (!rootDir || !outDir) {
throw new Error("`rootDir` and `outDir` are required");
}

await copyfiles(rootDir, outDir);
37 changes: 37 additions & 0 deletions packages/tscx/src/cmd/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { spawn } from "node:child_process";
import path from "node:path";
import process from "node:process";
import { fileURLToPath } from "node:url";

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const CLEAN_PATH = path.resolve(__dirname, "clean.mjs");
const COPYFILES_PATH = path.resolve(__dirname, "copyfiles.mjs");
const TSC_PATH = path.resolve(
process.cwd(),
"node_modules",
"typescript",
"bin",
"tsc",
);

export function clean(filepath: string) {
console.log("Clean", filepath);
return spawn("node", [CLEAN_PATH, filepath], { stdio: "inherit" });
}

export function tsc(options: { project: string }) {
console.log("Tsc", options);
return spawn("node", [TSC_PATH, "--project", options.project], {
stdio: "inherit",
});
}

export function copyfiles(rootDir: string, outDir: string) {
console.log("Copyfiles", rootDir, "=>", outDir);
return spawn("node", [COPYFILES_PATH, rootDir, outDir], { stdio: "inherit" });
}

export function run(filepath: string) {
console.log("Run", filepath);
return spawn("node", [filepath], { stdio: "inherit" });
}
Loading

0 comments on commit d55dcd7

Please sign in to comment.