tshex-cli 1.0.15 → 1.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/main.js +28 -6
- package/package.json +1 -1
- package/readme.md +15 -3
- package/source/main.ts +29 -11
- package/templates/ctx/example-ports.ts +5 -0
- package/templates/ctx-react/adapters/hooks/.gitkeep +0 -0
- package/templates/ctx-react/adapters/schemas/.gitkeep +0 -0
- package/templates/ctx-react/application/.gitkeep +0 -0
- package/templates/ctx-react/domain/.gitkeep +0 -0
- package/templates/ctx-react/languages/.gitkeep +0 -0
- package/templates/lib/shared/application/databases.ts +70 -49
- package/templates/lib/shared/application/loggers.ts +6 -30
- package/templates/lib/shared/application/validations.ts +10 -0
- package/templates/ctx/index.ts +0 -9
- /package/{source/index.d.ts → templates/ctx-react/adapters/api/.gitkeep} +0 -0
package/build/main.js
CHANGED
|
@@ -3,13 +3,15 @@ import { program } from 'commander';
|
|
|
3
3
|
import fs from 'node:fs';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
function readPackageJson() {
|
|
6
|
-
|
|
6
|
+
const filePath = path.join(import.meta.dirname, '..', 'package.json');
|
|
7
|
+
const fileContents = fs.readFileSync(filePath, 'utf-8');
|
|
8
|
+
return JSON.parse(fileContents);
|
|
7
9
|
}
|
|
8
10
|
function executeCreateLib(templatesDir, libraryDir) {
|
|
9
11
|
try {
|
|
10
12
|
fs.cpSync(path.join(templatesDir, 'lib'), libraryDir, {
|
|
11
13
|
recursive: true,
|
|
12
|
-
filter: (src) =>
|
|
14
|
+
filter: (src) => src.endsWith('.gitkeep') === false
|
|
13
15
|
});
|
|
14
16
|
console.log('Library created successfully');
|
|
15
17
|
}
|
|
@@ -21,7 +23,7 @@ function executeCreateContext(templatesDir, contextDir) {
|
|
|
21
23
|
try {
|
|
22
24
|
fs.cpSync(path.join(templatesDir, 'ctx'), contextDir, {
|
|
23
25
|
recursive: true,
|
|
24
|
-
filter: (src) =>
|
|
26
|
+
filter: (src) => src.endsWith('.gitkeep') === false
|
|
25
27
|
});
|
|
26
28
|
console.log('Context created successfully');
|
|
27
29
|
}
|
|
@@ -29,24 +31,43 @@ function executeCreateContext(templatesDir, contextDir) {
|
|
|
29
31
|
console.error(err);
|
|
30
32
|
}
|
|
31
33
|
}
|
|
34
|
+
function executeCreateReactContext(templatesDir, contextDir) {
|
|
35
|
+
try {
|
|
36
|
+
fs.cpSync(path.join(templatesDir, 'ctx-react'), contextDir, {
|
|
37
|
+
recursive: true,
|
|
38
|
+
filter: (src) => src.endsWith('.gitkeep') === false
|
|
39
|
+
});
|
|
40
|
+
console.log('React context created successfully');
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
console.error(err);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
32
46
|
function main(program) {
|
|
33
47
|
const templatesDir = path.join(import.meta.dirname, '..', 'templates');
|
|
34
48
|
const options = program.opts();
|
|
35
49
|
let targetDir = path.resolve(options.dir ?? process.cwd());
|
|
36
50
|
if (Object.keys(options).length === 0) {
|
|
37
51
|
program.help();
|
|
38
|
-
return;
|
|
39
52
|
}
|
|
40
|
-
if (
|
|
53
|
+
if (fs.existsSync(targetDir) === false) {
|
|
41
54
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
42
55
|
}
|
|
43
56
|
if (options.lib !== undefined) {
|
|
44
57
|
targetDir = path.join(targetDir, options.lib);
|
|
45
58
|
executeCreateLib(templatesDir, targetDir);
|
|
46
59
|
}
|
|
60
|
+
if (options.react === true && options.ctx === undefined) {
|
|
61
|
+
program.error('Option --react requires --ctx <name>');
|
|
62
|
+
}
|
|
47
63
|
if (options.ctx !== undefined) {
|
|
48
64
|
targetDir = path.join(targetDir, options.ctx);
|
|
49
|
-
|
|
65
|
+
if (options.react === true) {
|
|
66
|
+
executeCreateReactContext(templatesDir, targetDir);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
executeCreateContext(templatesDir, targetDir);
|
|
70
|
+
}
|
|
50
71
|
}
|
|
51
72
|
}
|
|
52
73
|
const packageJson = readPackageJson();
|
|
@@ -55,6 +76,7 @@ program
|
|
|
55
76
|
.version(packageJson.version)
|
|
56
77
|
.option('--lib <name>', "creates a new library with it's shared directory")
|
|
57
78
|
.option('--ctx <name>', 'creates a new context')
|
|
79
|
+
.option('-R, --react', 'creates a React context with --ctx')
|
|
58
80
|
.option('--dir <path>', 'sets the directory to create the new item')
|
|
59
81
|
.parse(process.argv);
|
|
60
82
|
main(program);
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -20,10 +20,11 @@ https://github.com/virtualitems/tshex-cli/issues
|
|
|
20
20
|
|
|
21
21
|
`tshex` scaffolds folders and files for a hexagonal architecture project. It does not write business logic for you; it only generates the structure, so you can fill it in with your own code.
|
|
22
22
|
|
|
23
|
-
There are
|
|
23
|
+
There are four things you can generate:
|
|
24
24
|
|
|
25
25
|
- A **library** (`--lib`): the shared foundation of a project. It contains the domain and application contracts (entities, value objects, services, etc.) that the rest of the code depends on.
|
|
26
26
|
- A **context** (`--ctx`): a specific business area (e.g. `users`, `billing`, `orders`). It contains the `domain/`, `application/`, and `adapters/` folders where you implement that area's logic.
|
|
27
|
+
- A **React context** (`--react-context`): a React-ready context scaffold.
|
|
27
28
|
|
|
28
29
|
A typical project has one library and one or more contexts inside it.
|
|
29
30
|
|
|
@@ -50,18 +51,19 @@ tshex --help
|
|
|
50
51
|
## Usage
|
|
51
52
|
|
|
52
53
|
```bash
|
|
53
|
-
tshex [--lib <name>] [--ctx <name>] [--dir <path>]
|
|
54
|
+
tshex [--lib <name>] [--ctx <name>] [--react-context <name>] [--dir <path>]
|
|
54
55
|
```
|
|
55
56
|
|
|
56
57
|
| Option | Purpose |
|
|
57
58
|
| -------------- | ------------------------------------------------------------------- |
|
|
58
59
|
| `--lib <name>` | Generate a library called `<name>`, based on `templates/lib`. |
|
|
59
60
|
| `--ctx <name>` | Generate a context called `<name>`, based on `templates/ctx`. |
|
|
61
|
+
| `--react-context <name>` | Generate a React context called `<name>`, based on `templates/react-context`. |
|
|
60
62
|
| `--dir <path>` | Parent directory where the library/context will be created. Defaults to the current directory. |
|
|
61
63
|
| `--help` | Show the command options. |
|
|
62
64
|
| `--version` | Show the installed version. |
|
|
63
65
|
|
|
64
|
-
`--lib
|
|
66
|
+
`--lib`, `--ctx`, and `--react-context` can be used together in a single command. Running `tshex` without any options just prints the help text — it does not generate anything.
|
|
65
67
|
|
|
66
68
|
## Create a library
|
|
67
69
|
|
|
@@ -203,3 +205,13 @@ src/
|
|
|
203
205
|
├── domain/
|
|
204
206
|
└── index.ts
|
|
205
207
|
```
|
|
208
|
+
|
|
209
|
+
## Create a React context
|
|
210
|
+
|
|
211
|
+
Use this when you want only the React context scaffold:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
tshex --react-context users
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
This generates a `users/` folder in the current directory.
|
package/source/main.ts
CHANGED
|
@@ -12,19 +12,16 @@ import path from 'node:path'
|
|
|
12
12
|
// FUNCTIONS
|
|
13
13
|
|
|
14
14
|
function readPackageJson() {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
'utf-8'
|
|
19
|
-
)
|
|
20
|
-
)
|
|
15
|
+
const filePath = path.join(import.meta.dirname, '..', 'package.json')
|
|
16
|
+
const fileContents = fs.readFileSync(filePath, 'utf-8')
|
|
17
|
+
return JSON.parse(fileContents)
|
|
21
18
|
}
|
|
22
19
|
|
|
23
20
|
function executeCreateLib(templatesDir: string, libraryDir: string) {
|
|
24
21
|
try {
|
|
25
22
|
fs.cpSync(path.join(templatesDir, 'lib'), libraryDir, {
|
|
26
23
|
recursive: true,
|
|
27
|
-
filter: (src) =>
|
|
24
|
+
filter: (src) => src.endsWith('.gitkeep') === false
|
|
28
25
|
})
|
|
29
26
|
console.log('Library created successfully')
|
|
30
27
|
} catch (err) {
|
|
@@ -36,7 +33,7 @@ function executeCreateContext(templatesDir: string, contextDir: string) {
|
|
|
36
33
|
try {
|
|
37
34
|
fs.cpSync(path.join(templatesDir, 'ctx'), contextDir, {
|
|
38
35
|
recursive: true,
|
|
39
|
-
filter: (src) =>
|
|
36
|
+
filter: (src) => src.endsWith('.gitkeep') === false
|
|
40
37
|
})
|
|
41
38
|
console.log('Context created successfully')
|
|
42
39
|
} catch (err) {
|
|
@@ -44,6 +41,18 @@ function executeCreateContext(templatesDir: string, contextDir: string) {
|
|
|
44
41
|
}
|
|
45
42
|
}
|
|
46
43
|
|
|
44
|
+
function executeCreateReactContext(templatesDir: string, contextDir: string) {
|
|
45
|
+
try {
|
|
46
|
+
fs.cpSync(path.join(templatesDir, 'ctx-react'), contextDir, {
|
|
47
|
+
recursive: true,
|
|
48
|
+
filter: (src) => src.endsWith('.gitkeep') === false
|
|
49
|
+
})
|
|
50
|
+
console.log('React context created successfully')
|
|
51
|
+
} catch (err) {
|
|
52
|
+
console.error(err)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
47
56
|
function main(program: typeof import('commander').program) {
|
|
48
57
|
const templatesDir = path.join(import.meta.dirname, '..', 'templates')
|
|
49
58
|
|
|
@@ -53,10 +62,9 @@ function main(program: typeof import('commander').program) {
|
|
|
53
62
|
|
|
54
63
|
if (Object.keys(options).length === 0) {
|
|
55
64
|
program.help()
|
|
56
|
-
return
|
|
57
65
|
}
|
|
58
66
|
|
|
59
|
-
if (
|
|
67
|
+
if (fs.existsSync(targetDir) === false) {
|
|
60
68
|
fs.mkdirSync(targetDir, { recursive: true })
|
|
61
69
|
}
|
|
62
70
|
|
|
@@ -65,9 +73,18 @@ function main(program: typeof import('commander').program) {
|
|
|
65
73
|
executeCreateLib(templatesDir, targetDir)
|
|
66
74
|
}
|
|
67
75
|
|
|
76
|
+
if (options.react === true && options.ctx === undefined) {
|
|
77
|
+
program.error('Option --react requires --ctx <name>')
|
|
78
|
+
}
|
|
79
|
+
|
|
68
80
|
if (options.ctx !== undefined) {
|
|
69
81
|
targetDir = path.join(targetDir, options.ctx)
|
|
70
|
-
|
|
82
|
+
|
|
83
|
+
if (options.react === true) {
|
|
84
|
+
executeCreateReactContext(templatesDir, targetDir)
|
|
85
|
+
} else {
|
|
86
|
+
executeCreateContext(templatesDir, targetDir)
|
|
87
|
+
}
|
|
71
88
|
}
|
|
72
89
|
}
|
|
73
90
|
|
|
@@ -78,6 +95,7 @@ program
|
|
|
78
95
|
.version(packageJson.version)
|
|
79
96
|
.option('--lib <name>', "creates a new library with it's shared directory")
|
|
80
97
|
.option('--ctx <name>', 'creates a new context')
|
|
98
|
+
.option('-R, --react', 'creates a React context with --ctx')
|
|
81
99
|
.option('--dir <path>', 'sets the directory to create the new item')
|
|
82
100
|
.parse(process.argv)
|
|
83
101
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -1,81 +1,102 @@
|
|
|
1
|
-
|
|
1
|
+
type Generic<T = unknown> = Record<string, T>
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* @description
|
|
5
|
+
*/
|
|
6
|
+
export interface Filterable<S = Generic> {
|
|
7
|
+
filter(selector: S): Promise<Array<S>>
|
|
8
|
+
}
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
/**
|
|
11
|
+
* @description
|
|
12
|
+
*/
|
|
13
|
+
export interface Sortable<S = Generic> {
|
|
14
|
+
sort(selector: S): Promise<Array<S>>
|
|
15
|
+
}
|
|
6
16
|
|
|
7
|
-
|
|
17
|
+
/**
|
|
18
|
+
* @description
|
|
19
|
+
*/
|
|
20
|
+
export interface Creatable<D = Generic> {
|
|
21
|
+
create(data: D): Promise<unknown>
|
|
22
|
+
}
|
|
8
23
|
|
|
9
|
-
|
|
24
|
+
/**
|
|
25
|
+
* @description
|
|
26
|
+
*/
|
|
27
|
+
export interface Updatable<S = Generic, D = Generic> {
|
|
28
|
+
update(selector: S, data: D): Promise<unknown>
|
|
29
|
+
}
|
|
10
30
|
|
|
11
31
|
/**
|
|
12
32
|
* @description
|
|
13
33
|
*/
|
|
14
|
-
export
|
|
15
|
-
|
|
34
|
+
export interface Deletable<S = Generic> {
|
|
35
|
+
delete(selector: S): Promise<unknown>
|
|
36
|
+
}
|
|
16
37
|
|
|
17
|
-
|
|
38
|
+
/**
|
|
39
|
+
* @description
|
|
40
|
+
*/
|
|
41
|
+
export interface Aggregatable<S = Generic> {
|
|
42
|
+
aggregate(selector: S): Promise<S>
|
|
43
|
+
}
|
|
18
44
|
|
|
19
|
-
|
|
45
|
+
/**
|
|
46
|
+
* @description
|
|
47
|
+
*/
|
|
48
|
+
export interface Relatable {
|
|
49
|
+
selectRelated(...args: unknown[]): unknown
|
|
20
50
|
|
|
21
|
-
|
|
51
|
+
prefetchRelated(...args: unknown[]): unknown
|
|
52
|
+
}
|
|
22
53
|
|
|
23
|
-
|
|
54
|
+
/**
|
|
55
|
+
* @description
|
|
56
|
+
*/
|
|
57
|
+
export abstract class DataManager<T = Generic> {
|
|
58
|
+
[property: string]: unknown
|
|
24
59
|
|
|
25
|
-
|
|
60
|
+
public abstract all(): Promise<Array<T>>
|
|
26
61
|
|
|
27
|
-
|
|
62
|
+
public abstract none(): Array<T>
|
|
63
|
+
} //:: class
|
|
28
64
|
|
|
29
|
-
|
|
65
|
+
/**
|
|
66
|
+
* @description
|
|
67
|
+
*/
|
|
68
|
+
export abstract class DatasetManager<T = Generic> extends DataManager<T> {
|
|
69
|
+
[property: string]: unknown
|
|
30
70
|
|
|
31
|
-
|
|
71
|
+
public abstract union(other: Array<T>): Promise<Array<T>>
|
|
32
72
|
|
|
33
|
-
public abstract
|
|
73
|
+
public abstract intersection(other: Array<T>): Promise<Array<T>>
|
|
34
74
|
|
|
35
|
-
public abstract
|
|
75
|
+
public abstract difference(other: Array<T>): Promise<Array<T>>
|
|
36
76
|
|
|
37
|
-
|
|
77
|
+
public abstract symmetric_difference(other: Array<T>): Promise<Array<T>>
|
|
38
78
|
|
|
39
|
-
|
|
79
|
+
public abstract complement(other: Array<T>): Promise<Array<T>>
|
|
80
|
+
} //:: class
|
|
40
81
|
|
|
41
|
-
|
|
82
|
+
/**
|
|
83
|
+
* @description
|
|
84
|
+
*/
|
|
85
|
+
export abstract class DriverManager<M extends DataManager = DataManager> {
|
|
86
|
+
[property: string]: unknown
|
|
42
87
|
|
|
43
|
-
|
|
88
|
+
public abstract connect(...args: unknown[]): Promise<M>
|
|
44
89
|
|
|
45
|
-
|
|
90
|
+
public abstract disconnect(): Promise<unknown>
|
|
46
91
|
} //:: class
|
|
47
92
|
|
|
48
93
|
/**
|
|
49
94
|
* @description Represents a data source.
|
|
50
95
|
*/
|
|
51
|
-
export abstract class Repository<
|
|
96
|
+
export abstract class Repository<M extends DriverManager = DriverManager> {
|
|
52
97
|
[property: string]: unknown
|
|
53
98
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
// Protected Attributes
|
|
57
|
-
|
|
58
|
-
// Private Attributes
|
|
59
|
-
|
|
60
|
-
// Public Static Attributes
|
|
61
|
-
|
|
62
|
-
// Protected Static Attributes
|
|
63
|
-
|
|
64
|
-
// Private Static Attributes
|
|
65
|
-
|
|
66
|
-
// Constructor, Getters, Setters
|
|
67
|
-
|
|
68
|
-
public constructor(public readonly manager: Manager) {}
|
|
69
|
-
|
|
70
|
-
// Public Methods
|
|
71
|
-
|
|
72
|
-
// Protected Methods
|
|
73
|
-
|
|
74
|
-
// Private Methods
|
|
75
|
-
|
|
76
|
-
// Public Static Methods
|
|
77
|
-
|
|
78
|
-
// Protected Static Methods
|
|
99
|
+
public constructor(public readonly manager: M) {}
|
|
79
100
|
|
|
80
|
-
|
|
101
|
+
protected abstract transform<T = Generic>(data: Generic): T
|
|
81
102
|
} //:: class
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
export const DEBUG = 10
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
export const INFO = 20
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
export const WARNING = 30
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
export const ERROR = 40
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
export const CRITICAL = 50
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* @description
|
|
@@ -14,22 +14,6 @@
|
|
|
14
14
|
export abstract class Logger {
|
|
15
15
|
[property: string]: unknown
|
|
16
16
|
|
|
17
|
-
// public ATTRIBUTES
|
|
18
|
-
|
|
19
|
-
// protected ATTRIBUTES
|
|
20
|
-
|
|
21
|
-
// private ATTRIBUTES
|
|
22
|
-
|
|
23
|
-
// public static ATTRIBUTES
|
|
24
|
-
|
|
25
|
-
// protected static ATTRIBUTES
|
|
26
|
-
|
|
27
|
-
// private static ATTRIBUTES
|
|
28
|
-
|
|
29
|
-
// Constructor, Getters, Setters
|
|
30
|
-
|
|
31
|
-
// public METHODS
|
|
32
|
-
|
|
33
17
|
public abstract debug(data: unknown): void
|
|
34
18
|
|
|
35
19
|
public abstract info(data: unknown): void
|
|
@@ -38,13 +22,5 @@ export abstract class Logger {
|
|
|
38
22
|
|
|
39
23
|
public abstract error(data: unknown): void
|
|
40
24
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
// private METHODS
|
|
44
|
-
|
|
45
|
-
// public static METHODS
|
|
46
|
-
|
|
47
|
-
// protected static METHODS
|
|
48
|
-
|
|
49
|
-
// private static METHODS
|
|
25
|
+
public abstract critical(data: unknown): void
|
|
50
26
|
} //:: class
|
package/templates/ctx/index.ts
DELETED
|
File without changes
|