@h3ravel/console
Version:
CLI utilities for scaffolding, running migrations, tasks and for H3ravel.
540 lines (529 loc) • 18.8 kB
JavaScript
//#region rolldown:runtime
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
key = keys[i];
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
get: ((k) => from[k]).bind(null, key),
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
});
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
value: mod,
enumerable: true
}) : target, mod));
//#endregion
let __h3ravel_shared = require("@h3ravel/shared");
let __h3ravel_musket = require("@h3ravel/musket");
let execa = require("execa");
let preferred_pm = require("preferred-pm");
preferred_pm = __toESM(preferred_pm);
let fs_promises = require("fs/promises");
let crypto = require("crypto");
crypto = __toESM(crypto);
let dotenv = require("dotenv");
dotenv = __toESM(dotenv);
let node_fs_promises = require("node:fs/promises");
let __h3ravel_support = require("@h3ravel/support");
let node_path = require("node:path");
node_path = __toESM(node_path);
let __h3ravel_core = require("@h3ravel/core");
let node_fs = require("node:fs");
let child_process = require("child_process");
let path = require("path");
//#region src/Commands/BuildCommand.ts
var BuildCommand = class BuildCommand extends __h3ravel_musket.Command {
/**
* The name and signature of the console command.
*
* @var string
*/
signature = `build
{--m|minify : Minify your bundle output}
{--d|dev : Build for dev but don't watch for changes}
`;
/**
* The console command description.
*
* @var string
*/
description = "Build the app for production";
async handle() {
try {
await this.fire();
} catch (e) {
__h3ravel_shared.Logger.error(e);
}
}
async fire() {
const outDir$1 = this.option("dev") ? ".h3ravel/serve" : env("DIST_DIR", "dist");
const minify = this.option("minify");
const verbosity = this.getVerbosity();
const debug = verbosity > 0;
this.newLine();
await BuildCommand.build({
outDir: outDir$1,
minify,
verbosity,
debug,
mute: false
});
this.newLine();
}
/**
* build
*/
static async build({ debug, minify, mute, verbosity, outDir: outDir$1 } = {
mute: false,
debug: false,
minify: false,
verbosity: 0,
outDir: "dist"
}) {
const pm = (await (0, preferred_pm.default)(base_path()))?.name ?? "pnpm";
const ENV_VARS = {
EXTENDED_DEBUG: debug ? "true" : "false",
CLI_BUILD: "true",
NODE_ENV: "production",
DIST_DIR: outDir$1,
DIST_MINIFY: minify,
LOG_LEVEL: [
"silent",
"info",
"warn",
"error"
][verbosity]
};
const silent = ENV_VARS.LOG_LEVEL === "silent" ? "--silent" : null;
if (mute) return await (0, execa.execa)(pm, [
"tsdown",
silent,
"--config-loader",
"unconfig",
"-c",
"tsdown.default.config.ts"
].filter((e) => e !== null), {
stdout: "inherit",
stderr: "inherit",
cwd: base_path(),
env: Object.assign({}, process.env, ENV_VARS)
});
const type = outDir$1 === "dist" ? "Production" : "Development";
return await __h3ravel_shared.TaskManager.advancedTaskRunner([[`Creating ${type} Bundle`, "STARTED"], [`${type} Bundle Created`, "COMPLETED"]], async () => {
await (0, execa.execa)(pm, [
"tsdown",
silent,
"--config-loader",
"unconfig",
"-c",
"tsdown.default.config.ts"
].filter((e) => e !== null), {
stdout: "inherit",
stderr: "inherit",
cwd: base_path(),
env: Object.assign({}, process.env, ENV_VARS)
});
});
}
};
//#endregion
//#region src/Commands/KeyGenerateCommand.ts
var KeyGenerateCommand = class extends __h3ravel_musket.Command {
/**
* The name and signature of the console command.
*
* @var string
*/
signature = `key:generate
{--force: Force the operation to run when in production}
{--show: Display the key instead of modifying files}
`;
/**
* The console command description.
*
* @var string
*/
description = "Set the application key";
async handle() {
const config$1 = {
key: crypto.default.randomBytes(32).toString("base64"),
envPath: base_path(".env"),
egEnvPath: base_path(".env.example"),
updated: false,
show: this.option("show")
};
this.newLine();
if (!await __h3ravel_shared.FileSystem.fileExists(config$1.envPath)) if (await __h3ravel_shared.FileSystem.fileExists(config$1.egEnvPath)) await (0, fs_promises.copyFile)(config$1.egEnvPath, config$1.envPath);
else {
this.error(".env file not found.");
this.newLine();
process.exit(0);
}
let content = await (0, fs_promises.readFile)(config$1.envPath, "utf8");
const buf = Buffer.from(content);
const env$2 = dotenv.default.parse(buf);
if (config$1.show) {
if (!env$2.APP_KEY || env$2.APP_KEY === "") {
this.error("Application key not set.");
this.newLine();
process.exit(0);
}
const [enc, key] = env$2.APP_KEY.split(":");
__h3ravel_shared.Logger.log([[enc, "yellow"], [key, "white"]], ":");
this.newLine();
process.exit(0);
} else if (env$2.APP_ENV === "production" && !this.option("force")) {
this.error("Application is currently in production, failed to set key.");
this.newLine();
process.exit(1);
}
if (/^APP_KEY=.*$/m.test(content)) {
config$1.updated = true;
content = content.replace(/^APP_KEY=.*$/m, `APP_KEY=base64:${config$1.key}`);
} else {
config$1.updated = false;
content = `APP_KEY=base64:${config$1.key}\n\n${content}`;
}
await (0, fs_promises.writeFile)(config$1.envPath, content, "utf8");
this.success("Application key set successfully.");
this.newLine();
}
};
//#endregion
//#region src/Commands/MakeCommand.ts
var MakeCommand = class extends __h3ravel_musket.Command {
/**
* The name and signature of the console command.
*
* @var string
*/
signature = `#make:
{controller : Create a new controller class.
| {--a|api : Exclude the create and edit methods from the controller}
| {--m|model= : Generate a resource controller for the given model}
| {--r|resource : Generate a resource controller class}
| {--force : Create the controller even if it already exists}
}
{resource : Create a new resource.
| {--c|collection : Create a resource collection}
| {--force : Create the resource even if it already exists}
}
{command : Create a new Musket command.
| {--command : The terminal command that will be used to invoke the class}
| {--force : Create the class even if the console command already exists}
}
{view : Create a new view.
| {--force : Create the view even if it already exists}
}
{^name : The name of the [name] to generate}
`;
/**
* The console command description.
*
* @var string
*/
description = "Generate component classes";
async handle() {
const command = this.dictionary.baseCommand ?? this.dictionary.name;
if (!this.argument("name")) this.program.error("Please provide a valid name for the " + command);
await this[{
controller: "makeController",
resource: "makeResource",
view: "makeView",
command: "makeCommand"
}[command]]();
}
/**
* Create a new controller class.
*/
async makeController() {
const type = this.option("api") ? "-resource" : "";
const name = this.argument("name");
const force = this.option("force");
const crtlrPath = __h3ravel_shared.FileSystem.findModulePkg("@h3ravel/http", this.kernel.cwd) ?? "";
const stubPath = node_path.default.join(crtlrPath, `dist/stubs/controller${type}.stub`);
const path$4 = app_path(`Http/Controllers/${name}.ts`);
/** The Controller is scoped to a path make sure to create the associated directories */
if (name.includes("/")) await (0, node_fs_promises.mkdir)(__h3ravel_support.Str.beforeLast(path$4, "/"), { recursive: true });
/** Check if the controller already exists */
if (!force && await __h3ravel_shared.FileSystem.fileExists(path$4)) __h3ravel_shared.Logger.error(`ERORR: ${name} controller already exists`);
let stub = await (0, node_fs_promises.readFile)(stubPath, "utf-8");
stub = stub.replace(/{{ name }}/g, name);
await (0, node_fs_promises.writeFile)(path$4, stub);
__h3ravel_shared.Logger.split("INFO: Controller Created", __h3ravel_shared.Logger.log(node_path.default.basename(path$4), "gray", false));
}
makeResource() {
__h3ravel_shared.Logger.success("Resource support is not yet available");
}
/**
* Create a new Musket command
*/
makeCommand() {
__h3ravel_shared.Logger.success("Musket command creation is not yet available");
}
/**
* Create a new view.
*/
async makeView() {
const name = this.argument("name");
const force = this.option("force");
const path$4 = base_path(`src/resources/views/${name}.edge`);
/** The view is scoped to a path make sure to create the associated directories */
if (name.includes("/")) await (0, node_fs_promises.mkdir)(__h3ravel_support.Str.beforeLast(path$4, "/"), { recursive: true });
/** Check if the view already exists */
if (!force && await __h3ravel_shared.FileSystem.fileExists(path$4)) __h3ravel_shared.Logger.error(`ERORR: ${name} view already exists`);
await (0, node_fs_promises.writeFile)(path$4, `{{-- src/resources/views/${name}.edge --}}`);
__h3ravel_shared.Logger.split("INFO: View Created", __h3ravel_shared.Logger.log(`src/resources/views/${name}.edge`, "gray", false));
}
};
//#endregion
//#region src/Commands/PostinstallCommand.ts
var PostinstallCommand = class extends __h3ravel_musket.Command {
/**
* The name and signature of the console command.
*
* @var string
*/
signature = "postinstall";
/**
* The console command description.
*
* @var string
*/
description = "Default post installation command";
async handle() {
this.genEncryptionKey();
this.createSqliteDB();
}
/**
* Create sqlite database if none exist
*
* @returns
*/
async genEncryptionKey() {
new KeyGenerateCommand(this.app, this.kernel).setProgram(this.program).setOption("force", true).setOption("silent", true).setOption("quiet", true).setInput({
force: true,
silent: true,
quiet: true
}, [], [], {}, this.program).handle();
}
/**
* Create sqlite database if none exist
*
* @returns
*/
async createSqliteDB() {
if (config("database.default") !== "sqlite") return;
if (!await __h3ravel_shared.FileSystem.fileExists(database_path())) await (0, node_fs_promises.mkdir)(database_path(), { recursive: true });
if (!await __h3ravel_shared.FileSystem.fileExists(database_path("db.sqlite"))) await (0, node_fs_promises.writeFile)(database_path("db.sqlite"), "");
}
};
//#endregion
//#region src/logo.ts
const logo = String.raw`
111
111111111
1111111111 111111
111111 111 111111
111111 111 111111
11111 111 11111
1111111 111 1111111
111 11111 111 111111 111 1111 1111 11111111 1111
111 11111 1111 111111 111 1111 1111 1111 11111 1111
111 11111 11111 111 1111 1111 111111111111 111111111111 1111 1111111 1111
111 111111 1111 111 111111111111 111111 11111 1111 111 1111 11111111 1111 1111
111 111 11111111 111 1101 1101 111111111 11111111 1111 1111111111111111101
111 1111111111111111 1111 111 1111 1111 111 11111011 1111 111 1111111 1101 1111
111 11111 1110111111111111 111 1111 1111 1111111101 1111 111111111 1111011 111111111 1111
1111111 111110111110 111 1111 1111 111111 1111 11011101 10111 11111 1111
11011 111111 11 11111
111111 11101 111111
111111 111 111111
111111 111 111111
111111111
110
`;
const altLogo = String.raw`
_ _ _____ _
| | | |___ / _ __ __ ___ _____| |
| |_| | |_ \| '__/ _ \ \ / / _ \ |
| _ |___) | | | (_| |\ V / __/ |
|_| |_|____/|_| \__,_| \_/ \___|_|
`;
//#endregion
//#region ../../node_modules/.pnpm/@rollup+plugin-run@3.1.0_rollup@4.52.5/node_modules/@rollup/plugin-run/dist/es/index.js
function run(opts = {}) {
let input;
let proc;
const args = opts.args || [];
const allowRestarts = opts.allowRestarts || false;
const overrideInput = opts.input;
const forkOptions = opts.options || opts;
delete forkOptions.args;
delete forkOptions.allowRestarts;
return {
name: "run",
buildStart(options) {
let inputs = overrideInput !== null && overrideInput !== void 0 ? overrideInput : options.input;
if (typeof inputs === "string") inputs = [inputs];
if (typeof inputs === "object") inputs = Object.values(inputs);
if (inputs.length > 1) throw new Error(`@rollup/plugin-run must have a single entry point; consider setting the \`input\` option`);
input = (0, path.resolve)(inputs[0]);
},
generateBundle(_outputOptions, _bundle, isWrite) {
if (!isWrite) this.error(`@rollup/plugin-run currently only works with bundles that are written to disk`);
},
writeBundle(outputOptions, bundle) {
const forkBundle = (dir$1, entryFileName$1) => {
if (proc) proc.kill();
proc = (0, child_process.fork)((0, path.join)(dir$1, entryFileName$1), args, forkOptions);
};
const dir = outputOptions.dir || (0, path.dirname)(outputOptions.file);
const entryFileName = Object.keys(bundle).find((fileName) => {
const chunk = bundle[fileName];
return chunk.isEntry && chunk.facadeModuleId === input;
});
if (entryFileName) {
forkBundle(dir, entryFileName);
if (allowRestarts) {
process.stdin.resume();
process.stdin.setEncoding("utf8");
process.stdin.on("data", (data) => {
const line = data.toString().trim().toLowerCase();
if (line === "rs" || line === "restart" || data.toString().charCodeAt(0) === 11) forkBundle(dir, entryFileName);
else if (line === "cls" || line === "clear" || data.toString().charCodeAt(0) === 12) console.clear();
});
}
} else this.error(`@rollup/plugin-run could not find output chunk`);
}
};
}
//#endregion
//#region src/TsdownConfig.ts
const env$1 = process.env.NODE_ENV || "development";
let outDir = env$1 === "development" ? ".h3ravel/serve" : "dist";
if (process.env.DIST_DIR) outDir = process.env.DIST_DIR;
const TsDownConfig = {
outDir,
outExtensions: (e) => {
return {
js: e.format === "es" ? ".js" : ".cjs",
dts: ".d.ts"
};
},
entry: ["src/**/*.ts"],
format: ["esm"],
target: "node22",
sourcemap: env$1 === "development",
minify: !!process.env.DIST_MINIFY,
external: [/^@h3ravel\/.*/gi],
clean: true,
shims: true,
copy: [
{
from: "public",
to: outDir
},
"src/resources",
"src/database"
],
env: env$1 === "development" ? {
NODE_ENV: env$1,
DIST_DIR: outDir
} : {},
watch: env$1 === "development" && process.env.CLI_BUILD !== "true" ? [
".env",
".env.*",
"src",
"../../packages"
] : false,
dts: false,
logLevel: "silent",
nodeProtocol: true,
skipNodeModulesBundle: true,
hooks(e) {
e.hook("build:done", async () => {
const paths = [
"database/migrations",
"database/factories",
"database/seeders"
];
for (let i = 0; i < paths.length; i++) {
const name = paths[i];
if ((0, node_fs.existsSync)(node_path.default.join(outDir, name))) await (0, node_fs_promises.rm)(node_path.default.join(outDir, name), { recursive: true });
}
});
},
plugins: env$1 === "development" && process.env.CLI_BUILD !== "true" ? [run({
env: Object.assign({}, process.env, {
NODE_ENV: env$1,
DIST_DIR: outDir
}),
execArgv: ["-r", "source-map-support/register"],
allowRestarts: false,
input: process.cwd() + "/src/server.ts"
})] : []
};
var TsdownConfig_default = TsDownConfig;
//#endregion
//#region src/Providers/ConsoleServiceProvider.ts
/**
* Handles CLI commands and tooling.
*
* Auto-Registered when in CLI mode
*/
var ConsoleServiceProvider = class extends __h3ravel_core.ServiceProvider {
static priority = 992;
/**
* Indicate that this service provider only runs in console
*/
static runsInConsole = true;
runsInConsole = true;
register() {
const DIST_DIR = `/${env("DIST_DIR", ".h3ravel/serve")}/`.replaceAll("//", "");
const baseCommands = [
BuildCommand,
MakeCommand,
PostinstallCommand,
KeyGenerateCommand
];
__h3ravel_musket.Kernel.init(this.app, {
logo: altLogo,
resolver: new __h3ravel_core.ContainerResolver(this.app).resolveMethodParams,
tsDownConfig: TsdownConfig_default,
baseCommands,
packages: [{
name: "@h3ravel/core",
alias: "H3ravel Framework"
}, {
name: "@h3ravel/musket",
alias: "Musket CLI"
}],
cliName: "musket",
hideMusketInfo: true,
discoveryPaths: [app_path("Console/Commands/*.js").replace("/src/", DIST_DIR)]
});
[
"SIGINT",
"SIGTERM",
"SIGTSTP"
].forEach((sig) => process.on(sig, () => {
process.exit(0);
}));
}
};
//#endregion
exports.BuildCommand = BuildCommand;
exports.ConsoleServiceProvider = ConsoleServiceProvider;
exports.KeyGenerateCommand = KeyGenerateCommand;
exports.MakeCommand = MakeCommand;
exports.PostinstallCommand = PostinstallCommand;
exports.TsDownConfig = TsDownConfig;
//# sourceMappingURL=index.cjs.map