diff --git a/package.json b/package.json index e75578d..f413a6b 100644 --- a/package.json +++ b/package.json @@ -43,13 +43,21 @@ }, "dependencies": { "@expressots/boost-ts": "^1.1.1", + "@swc/cli": "^0.1.62", + "@swc/core": "^1.3.80", + "@swc/register": "^0.1.10", "chalk-animation": "^1", "cli-progress": "^3.11.2", + "concurrently": "^8.2.1", "degit": "^2.8.4", "glob": "^10.2.6", "inquirer": "^8.0.0", "mustache": "^4.2.0", + "nodemon": "^3.0.1", + "rimraf": "^5.0.1", "ts-node": "^10.9.1", + "ts-node-dev": "^2.0.0", + "tsconfig-paths": "^4.2.0", "yargs": "^17.6.2" }, "devDependencies": { @@ -59,6 +67,7 @@ "@types/inquirer": "^9.0.3", "@types/mustache": "^4.2.2", "@types/node": "^18.11.19", + "@types/nodemon": "^1.19.2", "@types/yargs": "^17.0.22", "@typescript-eslint/eslint-plugin": "^5.53.0", "@typescript-eslint/parser": "^5.53.0", @@ -69,8 +78,6 @@ "husky": "^8.0.3", "prettier": "^2.8.4", "release-it": "^15.6.0", - "rimraf": "^4.1.2", - "ts-node-dev": "^2.0.0", "typescript": "^4.9.5" }, "release-it": { diff --git a/src/@types/command-args.ts b/src/@types/command-args.ts new file mode 100644 index 0000000..d9749e2 --- /dev/null +++ b/src/@types/command-args.ts @@ -0,0 +1,7 @@ +export interface CommandDevArgs { + experimental: boolean; +} + +export interface CommandBuildArgs { + experimental: boolean; +} diff --git a/src/@types/index.ts b/src/@types/index.ts index 2267c20..40f2aed 100644 --- a/src/@types/index.ts +++ b/src/@types/index.ts @@ -1,2 +1,3 @@ export * from "./config"; export * from "./template"; +export * from "./command-args"; diff --git a/src/build/cli.ts b/src/build/cli.ts new file mode 100644 index 0000000..e50a445 --- /dev/null +++ b/src/build/cli.ts @@ -0,0 +1,13 @@ +import { CommandModule } from "yargs"; +import { projectForm } from "./form"; +import { CommandDevArgs } from "../@types"; + +const buildProject = (): CommandModule, CommandDevArgs> => { + return { + command: "build", + describe: "Build project", + handler: projectForm, + }; +}; + +export { buildProject }; diff --git a/src/build/form.ts b/src/build/form.ts new file mode 100644 index 0000000..f36cfe4 --- /dev/null +++ b/src/build/form.ts @@ -0,0 +1,45 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +import path from "node:path"; +import { existsSync } from "node:fs"; +import { spawnSync } from "node:child_process"; +import { CommandBuildArgs } from "../@types"; +import { printError } from "../utils/cli-ui"; +import { getPlatformCommand } from "../utils/get-platform-command-bin"; + +const PATH = `${process.env.PATH}${path.delimiter}${path.resolve(__dirname, "../../node_modules/.bin")}`; +const env = { ...process.env, PATH }; + +const projectForm = async ({ experimental }: CommandBuildArgs): Promise => { + console.time("Build succeed"); + + spawnSync("rimraf", ["dist"], { env, stdio: "inherit" }); + + if (experimental) { + if (!existsSync(".swcrc")) { + printError("Experimental features needs .swcrc file", ".swcrc"); + process.exit(1); + } + + const { result } = require("concurrently")([ + { name: "types", command: "tsc --noEmit" }, + { name: "lint", command: "eslint src/**/*.ts" }, + { name: "build", command: "swc src -d dist" }, + ], { raw: true }); + + result + .then(() => { + console.timeEnd("Build succeed"); + }) + .catch(() => { + printError("Build failed", "expressots build"); + process.exit(1); + }); + + return; + } + + spawnSync(getPlatformCommand("tsc"), ["-p", "tsconfig.build.json"], { env, stdio: "inherit" }); + console.timeEnd("Build succeed"); +}; + +export { projectForm }; diff --git a/src/build/index.ts b/src/build/index.ts new file mode 100644 index 0000000..c1d55cf --- /dev/null +++ b/src/build/index.ts @@ -0,0 +1 @@ +export * from "./cli"; diff --git a/src/cli.ts b/src/cli.ts index 81dd17b..1fde5fb 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -5,6 +5,8 @@ import { hideBin } from "yargs/helpers"; import { generateProject } from "./generate"; import { infoProject } from "./info"; import { createProject } from "./new"; +import { devProject } from "./dev"; +import { buildProject } from "./build"; export const CLI_VERSION = "1.3.0"; @@ -15,6 +17,8 @@ yargs(hideBin(process.argv)) .command(createProject()) .command(generateProject()) .command(infoProject()) + .command(devProject()) + .command(buildProject()) .example("$0 new expressots-demo", "Create interactively") .example("$0 new expressots-demo -d ./", "Create interactively with path") .example("$0 new expressots-demo -p yarn -t opinionated", "Create silently") diff --git a/src/dev/cli.ts b/src/dev/cli.ts new file mode 100644 index 0000000..65dbfff --- /dev/null +++ b/src/dev/cli.ts @@ -0,0 +1,13 @@ +import { CommandModule } from "yargs"; +import { projectForm } from "./form"; +import { CommandDevArgs } from "../@types"; + +const devProject = (): CommandModule, CommandDevArgs> => { + return { + command: "dev", + describe: "Run project in development mode", + handler: projectForm, + }; +}; + +export { devProject }; diff --git a/src/dev/form.ts b/src/dev/form.ts new file mode 100644 index 0000000..3097a58 --- /dev/null +++ b/src/dev/form.ts @@ -0,0 +1,38 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +import path from "node:path"; +import { existsSync } from "node:fs"; +import { spawn, spawnSync } from "node:child_process"; +import nodemon from "nodemon"; +import { CommandDevArgs } from "../@types"; +import { printError } from "../utils/cli-ui"; +import { getPlatformCommand } from "../utils/get-platform-command-bin"; + +const projectForm = async ({ experimental }: CommandDevArgs): Promise => { + if (experimental) { + if (!existsSync(".swcrc")) { + printError("Experimental features needs .swcrc file", ".swcrc"); + process.exit(1); + } + + require('@swc/register'); + + function nodemonRestart() { + spawn(getPlatformCommand("eslint"), ["src/**/*.ts"], { stdio: "inherit" }); + spawn(getPlatformCommand("tsc"), ["--noEmit"], { stdio: "inherit" }); + } + + nodemon({ + ext: "ts", + exec: `node -r ${require.resolve("@swc/register")} src/main.ts`, + }) + .on("start", nodemonRestart) + .on("restart", nodemonRestart); + + + return; + } + + spawnSync(getPlatformCommand("ts-node-dev"), ["src/main.ts"], { stdio: "inherit" }); +}; + +export { projectForm }; diff --git a/src/dev/index.ts b/src/dev/index.ts new file mode 100644 index 0000000..c1d55cf --- /dev/null +++ b/src/dev/index.ts @@ -0,0 +1 @@ +export * from "./cli"; diff --git a/src/index.ts b/src/index.ts index 0e1cf69..06b019e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,3 +2,4 @@ export * from "./types" export * from "./generate" export * from "./utils" export * from "./new" +export * from "./dev" diff --git a/src/new/form.ts b/src/new/form.ts index a4a330a..93bd0f7 100644 --- a/src/new/form.ts +++ b/src/new/form.ts @@ -8,6 +8,7 @@ import path from "node:path"; import { centerText } from "../utils/center-text"; import { printError } from "../utils/cli-ui"; import { TemplateEnum } from "../@types"; +import { getPlatformCommand } from "../utils/get-platform-command-bin"; import templateList from "../templates-list"; async function packageManagerInstall({ @@ -20,10 +21,7 @@ async function packageManagerInstall({ progressBar: SingleBar; }) { return new Promise((resolve, reject) => { - const isWindows: boolean = process.platform === "win32"; - const command: string = isWindows - ? `${packageManager}.cmd` - : packageManager; + const command = getPlatformCommand(packageManager); const installProcess = spawn(command, ["install"], { cwd: directory, diff --git a/src/utils/get-platform-command-bin.ts b/src/utils/get-platform-command-bin.ts new file mode 100644 index 0000000..86a38e9 --- /dev/null +++ b/src/utils/get-platform-command-bin.ts @@ -0,0 +1,6 @@ +function getPlatformCommand(binName: string) { + const isWindows = process.platform === "win32"; + return isWindows ? `${binName}.cmd` : binName; +} + +export { getPlatformCommand };