Skip to content

Commit fe0daba

Browse files
committed
improve programmatic command executor
1 parent a77cccf commit fe0daba

21 files changed

Lines changed: 1334 additions & 546 deletions

.config/dler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import { defineConfigDler } from "@reliverse/cfg";
1+
import { defineConfig } from "@reliverse/dler-cfg";
22

33
/**
44
* Reliverse Bundler Configuration
55
* Hover over a field to see more details
66
* @see https://github.com/reliverse/dler
77
*/
8-
export default defineConfigDler({
8+
export default defineConfig({
99
// Bump configuration
1010
bumpDisable: false,
1111
bumpFilter: ["package.json", ".config/rse.ts"],

.config/rse.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { defineConfigRse } from "@reliverse/cfg";
1+
import { defineConfig } from "@reliverse/rse-cfg";
22

3-
export default defineConfigRse({
3+
export default defineConfig({
44
// RELIVERSE CONFIG (https://docs.reliverse.org/cli)
55
// Restart the CLI to apply your config changes
66
$schema: "./schema.json",
@@ -10,7 +10,7 @@ export default defineConfigRse({
1010
projectAuthor: "reliverse",
1111
projectDescription:
1212
"@reliverse/rempts is a modern, type-safe toolkit for building delightful cli experiences. it's fast, flexible, and made for developer happiness. file-based commands keep things simple.",
13-
version: "1.7.40",
13+
version: "1.7.42",
1414
projectLicense: "MIT",
1515
projectState: "creating",
1616
projectRepository: "https://github.com/reliverse/rempts",

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,7 @@ node_modules
22
.eslintcache
33
.DS_Store
44
/*txt
5+
/*log
56
dist*
67
logs
8+
dist

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# MIT License
22

3-
Copyright (c) Nazar Kornienko (blefnk), Reliverse
3+
Copyright (c) Nazar Kornienko (blefnk), Bleverse, Reliverse
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

LICENSES

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ LICENSES
44

55
MIT License
66

7-
Copyright (c) Nazar Kornienko (blefnk), Reliverse
7+
Copyright (c) Nazar Kornienko (blefnk), Bleverse, Reliverse
88

99
See LICENSE file for full license text.
1010

README.md

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1210,6 +1210,219 @@ mycli --tags desktop
12101210

12111211
The validation happens after type casting, so for example with numbers, the input will first be converted to a number and then checked against the allowed list.
12121212

1213+
## Typed Commands System
1214+
1215+
The typed commands system provides TypeScript intellisense and type safety for rempts launcher usage while maintaining dynamic code execution.
1216+
1217+
- 🎯 **TypeScript Intellisense**: Full autocomplete for command names and arguments
1218+
- 🔒 **Type Safety**: Compile-time checking for argument types and required fields
1219+
-**Dynamic Execution**: Commands are still loaded and executed dynamically
1220+
- 📝 **Automatic Sync**: Utility script to keep types in sync with actual command definitions
1221+
1222+
### Usage
1223+
1224+
#### Basic Usage
1225+
1226+
```typescript
1227+
import { callCmd } from "~/app/cmds";
1228+
1229+
// Simple command with typed arguments
1230+
await callCmd("pub", { dev: true });
1231+
1232+
// Command with multiple arguments
1233+
await callCmd("check", {
1234+
directory: "src",
1235+
checks: "missing-deps,file-extensions",
1236+
strict: true,
1237+
json: false
1238+
});
1239+
1240+
// Command with no arguments
1241+
await callCmd("update");
1242+
1243+
// Generators with typed arguments
1244+
await callCmd("rempts", {
1245+
init: "new-cmd another-cmd",
1246+
overwrite: true,
1247+
outFile: "src/app/cmds.ts"
1248+
});
1249+
```
1250+
1251+
#### Advanced Usage
1252+
1253+
```typescript
1254+
import { getTypedCmd } from "~/app/cmds";
1255+
1256+
// Get command instance for more control
1257+
const { command, run } = await getTypedCmd("magic");
1258+
1259+
console.log(`Running: ${command.meta.name}`);
1260+
console.log(`Description: ${command.meta.description}`);
1261+
1262+
await run({
1263+
targets: ["dist-npm", "dist-jsr"],
1264+
concurrency: 4,
1265+
stopOnError: true
1266+
});
1267+
```
1268+
1269+
#### TypeScript Benefits
1270+
1271+
##### 1. Command Name Autocomplete
1272+
1273+
When you type `callCmd("`, TypeScript will show all available commands.
1274+
1275+
##### 2. Argument Intellisense
1276+
1277+
When you type the arguments object, you get full autocomplete for:
1278+
1279+
- Argument names
1280+
- Argument types
1281+
- Required vs optional fields
1282+
1283+
##### 3. Type Validation
1284+
1285+
```typescript
1286+
// ✅ Correct usage
1287+
await callCmd("create", {
1288+
mode: "files", // Only "template" | "files" allowed
1289+
multiple: true // boolean
1290+
});
1291+
1292+
// ❌ TypeScript errors
1293+
await callCmd("create", {
1294+
mode: "invalid", // Error: not assignable to type
1295+
multiple: "yes" // Error: string not assignable to boolean
1296+
});
1297+
```
1298+
1299+
##### 4. Required Field Checking
1300+
1301+
```typescript
1302+
// ✅ Required field provided
1303+
await callCmd("magic", {
1304+
targets: ["dist-npm"] // Required field
1305+
});
1306+
1307+
// ❌ TypeScript error: missing required field 'targets'
1308+
await callCmd("magic", {
1309+
concurrency: 4
1310+
});
1311+
```
1312+
1313+
### Maintaining the System
1314+
1315+
#### Adding New Commands
1316+
1317+
1. Create your command in `src/app/<command-name>/cmd.ts` using `defineCommand` and `defineArgs`
1318+
2. Run the generator: `dler rempts --overwrite`
1319+
3. The `CommandArgsMap` interface in `src/app/cmds.ts` will be automatically updated
1320+
1321+
#### Manual Updates
1322+
1323+
The `CommandArgsMap` interface is auto-generated. If you need custom types, you can add manual type assertions (it is more recommended to edit your command file instead and regenerate the types):
1324+
1325+
```typescript
1326+
interface CommandArgsMap {
1327+
myCommand: {
1328+
// Use union types for specific values
1329+
mode: "development" | "production";
1330+
1331+
// Use template literal types for patterns
1332+
version: `${number}.${number}.${number}`;
1333+
1334+
// Use branded types for validation
1335+
port: number & { __brand: "Port" };
1336+
};
1337+
}
1338+
```
1339+
1340+
### Migration from Old System
1341+
1342+
#### Before (Old System, still supported)
1343+
1344+
```typescript
1345+
import { runCmd } from "@reliverse/rempts";
1346+
import { getPubCmd } from "./app/cmds";
1347+
1348+
// No type safety, string-based arguments
1349+
await runCmd(await getPubCmd(), [`--dev=${isDev}`]);
1350+
```
1351+
1352+
### After (New System)
1353+
1354+
```typescript
1355+
import { callCmd } from "./app/cmds";
1356+
1357+
// Full type safety and intellisense
1358+
await callCmd("pub", { dev: isDev });
1359+
```
1360+
1361+
### Implementation Details
1362+
1363+
The system works by:
1364+
1365+
1. **Command Loading**: Commands are still loaded dynamically using `loadCommand()`
1366+
2. **Argument Conversion**: Typed arguments are converted to string array format that `runCmd` expects
1367+
3. **Type Mapping**: `CommandArgsMap` interface maps command names to their argument types
1368+
4. **Generic Types**: `callCmd<T extends keyof CommandArgsMap>` provides type safety
1369+
1370+
### Generator Usage
1371+
1372+
The typed command system also supports calling generators with full intellisense:
1373+
1374+
#### Creating New Commands
1375+
1376+
```typescript
1377+
// Create new commands with typed arguments
1378+
await callCmd("rempts", {
1379+
init: "auth login logout", // Commands to create
1380+
overwrite: true, // Overwrite existing
1381+
outFile: "src/app/cmds.ts" // Export file path
1382+
});
1383+
1384+
// Create commands in custom location
1385+
await callCmd("rempts", {
1386+
init: "api-handler",
1387+
customCmdsRoot: "src/modules/api",
1388+
outFile: "src/modules/api/exports.ts",
1389+
overwrite: true
1390+
});
1391+
```
1392+
1393+
#### Regenerating Exports
1394+
1395+
```typescript
1396+
// Regenerate exports file only
1397+
await callCmd("rempts", {
1398+
overwrite: true,
1399+
outFile: "src/app/cmds.ts"
1400+
});
1401+
1402+
// Generate exports for specific directories
1403+
await callCmd("rempts", {
1404+
cmdDirs: ["build", "pub", "magic"],
1405+
outFile: "src/app/core-cmds.ts",
1406+
overwrite: true
1407+
});
1408+
```
1409+
1410+
#### Batch Operations
1411+
1412+
```typescript
1413+
// Create multiple commands programmatically
1414+
const modules = ["auth", "db", "api", "deploy"];
1415+
1416+
for (const module of modules) {
1417+
await callCmd("rempts", {
1418+
init: `${module}-create ${module}-update ${module}-delete`,
1419+
customCmdsRoot: `src/modules/${module}`,
1420+
outFile: `src/modules/${module}/cmds.ts`,
1421+
overwrite: true
1422+
});
1423+
}
1424+
```
1425+
12131426
## Contributing
12141427

12151428
Bug report? Prompt idea? Want to build the best DX possible?

biome.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
2+
"$schema": "https://biomejs.dev/schemas/2.1.2/schema.json",
33
"vcs": {
44
"enabled": false,
55
"clientKind": "git",
@@ -25,7 +25,13 @@
2525
"lineEnding": "lf",
2626
"lineWidth": 80
2727
},
28-
"assist": { "actions": { "source": { "organizeImports": "off" } } },
28+
"assist": {
29+
"actions": {
30+
"source": {
31+
"organizeImports": "off"
32+
}
33+
}
34+
},
2935
"linter": {
3036
"enabled": true,
3137
"rules": {

0 commit comments

Comments
 (0)