@exanderal/stackcraft 0.5.0 → 0.6.1
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/README.md +33 -1
- package/dist/create/config.d.ts +13 -0
- package/dist/create/config.d.ts.map +1 -0
- package/dist/create/config.js +50 -0
- package/dist/create/config.js.map +1 -0
- package/dist/create/index.d.ts +6 -1
- package/dist/create/index.d.ts.map +1 -1
- package/dist/create/index.js +27 -11
- package/dist/create/index.js.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/init/index.d.ts +2 -0
- package/dist/init/index.d.ts.map +1 -0
- package/dist/init/index.js +38 -0
- package/dist/init/index.js.map +1 -0
- package/package.json +3 -2
- package/schema.json +60 -0
package/README.md
CHANGED
|
@@ -18,6 +18,38 @@ Add `--full` to unlock ORM and linter selection:
|
|
|
18
18
|
npx @exanderal/stackcraft --full
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
+
### Non-interactive mode
|
|
22
|
+
|
|
23
|
+
Generate a config file with all defaults:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
npx @exanderal/stackcraft init
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
This writes `stackcraft.config.json` to the current directory:
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"$schema": "https://unpkg.com/@exanderal/stackcraft/schema.json",
|
|
34
|
+
"name": "my-app",
|
|
35
|
+
"backend": "nestjs-rest",
|
|
36
|
+
"orm": "prisma",
|
|
37
|
+
"frontend": "vite",
|
|
38
|
+
"mobile": "none",
|
|
39
|
+
"database": "postgres",
|
|
40
|
+
"packageManager": "pnpm",
|
|
41
|
+
"linter": "eslint"
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Edit the fields you want, then scaffold:
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
npx @exanderal/stackcraft --config stackcraft.config.json
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Any field omitted from the config will be prompted interactively. The `$schema` enables autocomplete and validation in VS Code — hover over any field to see valid values.
|
|
52
|
+
|
|
21
53
|
## What you get
|
|
22
54
|
|
|
23
55
|
```
|
|
@@ -229,8 +261,8 @@ const { data, loading } = useGetTrainersQuery()
|
|
|
229
261
|
- [x] Docker Compose for local database + per-app `.env` files
|
|
230
262
|
- [x] Prisma ORM (default)
|
|
231
263
|
- [x] Kysely ORM with repository abstraction (`--full`)
|
|
264
|
+
- [x] `stackcraft init` + `--config` for non-interactive use
|
|
232
265
|
- [ ] `stackcraft add` addon system (auth, Supabase, etc.)
|
|
233
|
-
- [ ] Presets and `--config` for non-interactive use
|
|
234
266
|
|
|
235
267
|
## License
|
|
236
268
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Backend, Database, Frontend, Linter, Mobile, ORM, PackageManager } from './types.js';
|
|
2
|
+
export interface ConfigFile {
|
|
3
|
+
name?: string;
|
|
4
|
+
backend?: Backend;
|
|
5
|
+
orm?: ORM;
|
|
6
|
+
frontend?: Frontend;
|
|
7
|
+
mobile?: Mobile;
|
|
8
|
+
database?: Database;
|
|
9
|
+
packageManager?: PackageManager;
|
|
10
|
+
linter?: Linter;
|
|
11
|
+
}
|
|
12
|
+
export declare function loadConfig(configPath: string): Promise<ConfigFile>;
|
|
13
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/create/config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAElG,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,GAAG,CAAC,EAAE,GAAG,CAAA;IACT,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAaD,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAsCxE"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
const VALID = {
|
|
4
|
+
name: [],
|
|
5
|
+
backend: ['nestjs-rest', 'nestjs-graphql'],
|
|
6
|
+
orm: ['prisma', 'kysely'],
|
|
7
|
+
frontend: ['vite', 'nextjs'],
|
|
8
|
+
mobile: ['none', 'expo'],
|
|
9
|
+
database: ['postgres', 'mysql'],
|
|
10
|
+
packageManager: ['pnpm', 'npm'],
|
|
11
|
+
linter: ['eslint', 'biome'],
|
|
12
|
+
};
|
|
13
|
+
export async function loadConfig(configPath) {
|
|
14
|
+
const abs = resolve(process.cwd(), configPath);
|
|
15
|
+
let raw;
|
|
16
|
+
try {
|
|
17
|
+
raw = await readFile(abs, 'utf-8');
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
throw new Error(`Config file not found: ${abs}`);
|
|
21
|
+
}
|
|
22
|
+
let parsed;
|
|
23
|
+
try {
|
|
24
|
+
parsed = JSON.parse(raw);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
throw new Error(`Config file is not valid JSON: ${abs}`);
|
|
28
|
+
}
|
|
29
|
+
const errors = [];
|
|
30
|
+
for (const [field, valid] of Object.entries(VALID)) {
|
|
31
|
+
const key = field;
|
|
32
|
+
const value = parsed[key];
|
|
33
|
+
if (value === undefined)
|
|
34
|
+
continue;
|
|
35
|
+
if (key === 'name') {
|
|
36
|
+
if (typeof value !== 'string' || !value.trim()) {
|
|
37
|
+
errors.push(`"name" must be a non-empty string`);
|
|
38
|
+
}
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (!valid.includes(value)) {
|
|
42
|
+
errors.push(`"${key}" must be one of: ${valid.join(', ')} — got "${value}"`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (errors.length > 0) {
|
|
46
|
+
throw new Error(`Invalid config:\n${errors.map((e) => ` • ${e}`).join('\n')}`);
|
|
47
|
+
}
|
|
48
|
+
return parsed;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/create/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAcnC,MAAM,KAAK,GAAgD;IACzD,IAAI,EAAE,EAAE;IACR,OAAO,EAAE,CAAC,aAAa,EAAE,gBAAgB,CAAC;IAC1C,GAAG,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC;IACzB,QAAQ,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC5B,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,CAAC,UAAU,EAAE,OAAO,CAAC;IAC/B,cAAc,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC;IAC/B,MAAM,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B,CAAA;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,UAAkB;IACjD,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,CAAC,CAAA;IAC9C,IAAI,GAAW,CAAA;IACf,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,0BAA0B,GAAG,EAAE,CAAC,CAAA;IAClD,CAAC;IAED,IAAI,MAA+B,CAAA;IACnC,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,kCAAkC,GAAG,EAAE,CAAC,CAAA;IAC1D,CAAC;IAED,MAAM,MAAM,GAAa,EAAE,CAAA;IAE3B,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACnD,MAAM,GAAG,GAAG,KAAyB,CAAA;QACrC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;QACzB,IAAI,KAAK,KAAK,SAAS;YAAE,SAAQ;QACjC,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;YACnB,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;gBAC/C,MAAM,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAA;YAClD,CAAC;YACD,SAAQ;QACV,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAe,CAAC,EAAE,CAAC;YACrC,MAAM,CAAC,IAAI,CAAC,IAAI,GAAG,qBAAqB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,KAAK,GAAG,CAAC,CAAA;QAC9E,CAAC;IACH,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,oBAAoB,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACjF,CAAC;IAED,OAAO,MAAoB,CAAA;AAC7B,CAAC"}
|
package/dist/create/index.d.ts
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
interface CreateOptions {
|
|
2
|
+
fullMode?: boolean;
|
|
3
|
+
configPath?: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function create({ fullMode, configPath }?: CreateOptions): Promise<void>;
|
|
6
|
+
export {};
|
|
2
7
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/create/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/create/index.ts"],"names":[],"mappings":"AAMA,UAAU,aAAa;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,wBAAsB,MAAM,CAAC,EAAE,QAAgB,EAAE,UAAU,EAAE,GAAE,aAAkB,iBAuIhF"}
|
package/dist/create/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { cancel, intro, isCancel, outro, select, spinner, text } from '@clack/prompts';
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
|
+
import { loadConfig } from './config.js';
|
|
3
4
|
import { scaffold } from './scaffold.js';
|
|
4
|
-
export async function create(fullMode = false) {
|
|
5
|
+
export async function create({ fullMode = false, configPath } = {}) {
|
|
6
|
+
const cfg = configPath ? await loadConfig(configPath) : {};
|
|
5
7
|
intro('stackcraft — spin up a production-ready monorepo');
|
|
6
|
-
const projectName = await text({
|
|
8
|
+
const projectName = cfg.name ?? await text({
|
|
7
9
|
message: 'Project name',
|
|
8
10
|
placeholder: 'my-app',
|
|
9
11
|
validate: (v) => (!v ? 'Required' : undefined),
|
|
@@ -12,7 +14,7 @@ export async function create(fullMode = false) {
|
|
|
12
14
|
cancel('Cancelled.');
|
|
13
15
|
process.exit(0);
|
|
14
16
|
}
|
|
15
|
-
const backend = await select({
|
|
17
|
+
const backend = cfg.backend ?? await select({
|
|
16
18
|
message: 'Backend',
|
|
17
19
|
options: [
|
|
18
20
|
{ value: 'nestjs-rest', label: 'NestJS REST', hint: 'REST API' },
|
|
@@ -23,8 +25,12 @@ export async function create(fullMode = false) {
|
|
|
23
25
|
cancel('Cancelled.');
|
|
24
26
|
process.exit(0);
|
|
25
27
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
const ormFromConfig = cfg.orm;
|
|
29
|
+
let orm;
|
|
30
|
+
if (ormFromConfig) {
|
|
31
|
+
orm = ormFromConfig;
|
|
32
|
+
}
|
|
33
|
+
else if (fullMode) {
|
|
28
34
|
const ormAnswer = await select({
|
|
29
35
|
message: 'ORM',
|
|
30
36
|
options: [
|
|
@@ -38,7 +44,10 @@ export async function create(fullMode = false) {
|
|
|
38
44
|
}
|
|
39
45
|
orm = ormAnswer;
|
|
40
46
|
}
|
|
41
|
-
|
|
47
|
+
else {
|
|
48
|
+
orm = 'prisma';
|
|
49
|
+
}
|
|
50
|
+
const frontend = cfg.frontend ?? await select({
|
|
42
51
|
message: 'Frontend',
|
|
43
52
|
options: [
|
|
44
53
|
{ value: 'vite', label: 'Vite + React', hint: 'Fast dev server, great for SPAs' },
|
|
@@ -49,7 +58,7 @@ export async function create(fullMode = false) {
|
|
|
49
58
|
cancel('Cancelled.');
|
|
50
59
|
process.exit(0);
|
|
51
60
|
}
|
|
52
|
-
const mobile = await select({
|
|
61
|
+
const mobile = cfg.mobile ?? await select({
|
|
53
62
|
message: 'Mobile',
|
|
54
63
|
options: [
|
|
55
64
|
{ value: 'none', label: 'None' },
|
|
@@ -60,7 +69,7 @@ export async function create(fullMode = false) {
|
|
|
60
69
|
cancel('Cancelled.');
|
|
61
70
|
process.exit(0);
|
|
62
71
|
}
|
|
63
|
-
const database = await select({
|
|
72
|
+
const database = cfg.database ?? await select({
|
|
64
73
|
message: 'Database',
|
|
65
74
|
options: [
|
|
66
75
|
{ value: 'postgres', label: 'PostgreSQL', hint: 'recommended' },
|
|
@@ -71,7 +80,7 @@ export async function create(fullMode = false) {
|
|
|
71
80
|
cancel('Cancelled.');
|
|
72
81
|
process.exit(0);
|
|
73
82
|
}
|
|
74
|
-
const packageManager = await select({
|
|
83
|
+
const packageManager = cfg.packageManager ?? await select({
|
|
75
84
|
message: 'Package manager',
|
|
76
85
|
options: [
|
|
77
86
|
{ value: 'pnpm', label: 'pnpm', hint: 'recommended' },
|
|
@@ -82,8 +91,12 @@ export async function create(fullMode = false) {
|
|
|
82
91
|
cancel('Cancelled.');
|
|
83
92
|
process.exit(0);
|
|
84
93
|
}
|
|
85
|
-
|
|
86
|
-
|
|
94
|
+
const linterFromConfig = cfg.linter;
|
|
95
|
+
let linter;
|
|
96
|
+
if (linterFromConfig) {
|
|
97
|
+
linter = linterFromConfig;
|
|
98
|
+
}
|
|
99
|
+
else if (fullMode) {
|
|
87
100
|
const linterAnswer = await select({
|
|
88
101
|
message: 'Linter / formatter',
|
|
89
102
|
options: [
|
|
@@ -97,6 +110,9 @@ export async function create(fullMode = false) {
|
|
|
97
110
|
}
|
|
98
111
|
linter = linterAnswer;
|
|
99
112
|
}
|
|
113
|
+
else {
|
|
114
|
+
linter = 'eslint';
|
|
115
|
+
}
|
|
100
116
|
const config = {
|
|
101
117
|
projectName: projectName,
|
|
102
118
|
frontend: frontend,
|
package/dist/create/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/create/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAA;AACtF,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/create/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { create } from './create/index.js';
|
|
3
3
|
import { add } from './add/index.js';
|
|
4
|
+
import { init } from './init/index.js';
|
|
4
5
|
const [, , command, ...args] = process.argv;
|
|
5
6
|
const fullMode = process.argv.includes('--full');
|
|
7
|
+
const configFlagIndex = process.argv.indexOf('--config');
|
|
8
|
+
const configPath = configFlagIndex !== -1 ? process.argv[configFlagIndex + 1] : undefined;
|
|
6
9
|
async function main() {
|
|
7
10
|
if (command === 'add') {
|
|
8
11
|
await add(args);
|
|
9
12
|
}
|
|
13
|
+
else if (command === 'init') {
|
|
14
|
+
await init();
|
|
15
|
+
}
|
|
10
16
|
else {
|
|
11
|
-
await create(fullMode);
|
|
17
|
+
await create({ fullMode, configPath });
|
|
12
18
|
}
|
|
13
19
|
}
|
|
14
20
|
main().catch((err) => {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC1C,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC1C,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAA;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAEtC,MAAM,CAAC,EAAE,AAAD,EAAG,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAA;AAC3C,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;AAEhD,MAAM,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;AACxD,MAAM,UAAU,GAAG,eAAe,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;AAEzF,KAAK,UAAU,IAAI;IACjB,IAAI,OAAO,KAAK,KAAK,EAAE,CAAC;QACtB,MAAM,GAAG,CAAC,IAAI,CAAC,CAAA;IACjB,CAAC;SAAM,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QAC9B,MAAM,IAAI,EAAE,CAAA;IACd,CAAC;SAAM,CAAC;QACN,MAAM,MAAM,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAA;IACxC,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAClB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/init/index.ts"],"names":[],"mappings":"AA4BA,wBAAsB,IAAI,kBAgBzB"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { outro } from '@clack/prompts';
|
|
2
|
+
import { writeFile } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
const SCHEMA_URL = 'https://unpkg.com/@exanderal/stackcraft/schema.json';
|
|
5
|
+
const DEFAULT_CONFIG = {
|
|
6
|
+
$schema: SCHEMA_URL,
|
|
7
|
+
name: 'my-app',
|
|
8
|
+
backend: 'nestjs-rest',
|
|
9
|
+
orm: 'prisma',
|
|
10
|
+
frontend: 'vite',
|
|
11
|
+
mobile: 'none',
|
|
12
|
+
database: 'postgres',
|
|
13
|
+
packageManager: 'pnpm',
|
|
14
|
+
linter: 'eslint',
|
|
15
|
+
};
|
|
16
|
+
const OPTIONS = [
|
|
17
|
+
{ field: 'backend', values: ['nestjs-rest', 'nestjs-graphql'] },
|
|
18
|
+
{ field: 'orm', values: ['prisma', 'kysely'] },
|
|
19
|
+
{ field: 'frontend', values: ['vite', 'nextjs'] },
|
|
20
|
+
{ field: 'mobile', values: ['none', 'expo'] },
|
|
21
|
+
{ field: 'database', values: ['postgres', 'mysql'] },
|
|
22
|
+
{ field: 'packageManager', values: ['pnpm', 'npm'] },
|
|
23
|
+
{ field: 'linter', values: ['eslint', 'biome'] },
|
|
24
|
+
];
|
|
25
|
+
export async function init() {
|
|
26
|
+
const dest = join(process.cwd(), 'stackcraft.config.json');
|
|
27
|
+
await writeFile(dest, JSON.stringify(DEFAULT_CONFIG, null, 2) + '\n', 'utf-8');
|
|
28
|
+
const col = Math.max(...OPTIONS.map((o) => o.field.length));
|
|
29
|
+
console.log('\n Created stackcraft.config.json\n');
|
|
30
|
+
console.log(' Edit the file to customize your stack:\n');
|
|
31
|
+
for (const { field, values } of OPTIONS) {
|
|
32
|
+
const padding = ' '.repeat(col - field.length + 2);
|
|
33
|
+
console.log(` ${field}${padding}${values.join(' | ')}`);
|
|
34
|
+
}
|
|
35
|
+
console.log('');
|
|
36
|
+
outro('Run: npx @exanderal/stackcraft --config stackcraft.config.json');
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/init/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC,MAAM,UAAU,GAAG,qDAAqD,CAAA;AAExE,MAAM,cAAc,GAAG;IACrB,OAAO,EAAE,UAAU;IACnB,IAAI,EAAE,QAAQ;IACd,OAAO,EAAE,aAAa;IACtB,GAAG,EAAE,QAAQ;IACb,QAAQ,EAAE,MAAM;IAChB,MAAM,EAAE,MAAM;IACd,QAAQ,EAAE,UAAU;IACpB,cAAc,EAAE,MAAM;IACtB,MAAM,EAAE,QAAQ;CACjB,CAAA;AAED,MAAM,OAAO,GAA+C;IAC1D,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,aAAa,EAAE,gBAAgB,CAAC,EAAE;IAC/D,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE;IAC9C,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE;IACjD,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE;IAC7C,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,UAAU,EAAE,OAAO,CAAC,EAAE;IACpD,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE;IACpD,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE;CACjD,CAAA;AAED,MAAM,CAAC,KAAK,UAAU,IAAI;IACxB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,wBAAwB,CAAC,CAAA;IAC1D,MAAM,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAA;IAE9E,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAA;IAE3D,OAAO,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAA;IACnD,OAAO,CAAC,GAAG,CAAC,4CAA4C,CAAC,CAAA;IAEzD,KAAK,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC;QACxC,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;QAClD,OAAO,CAAC,GAAG,CAAC,OAAO,KAAK,GAAG,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IAC5D,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACf,KAAK,CAAC,gEAAgE,CAAC,CAAA;AACzE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exanderal/stackcraft",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Opinionated full-stack project scaffolding CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
"eslint",
|
|
17
17
|
"tsconfig",
|
|
18
18
|
"prettier",
|
|
19
|
-
"templates"
|
|
19
|
+
"templates",
|
|
20
|
+
"schema.json"
|
|
20
21
|
],
|
|
21
22
|
"scripts": {
|
|
22
23
|
"build": "tsc",
|
package/schema.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://unpkg.com/@exanderal/stackcraft/schema.json",
|
|
4
|
+
"title": "Stackcraft config",
|
|
5
|
+
"description": "Configuration file for npx stackcraft --config",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["name"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"$schema": {
|
|
11
|
+
"type": "string"
|
|
12
|
+
},
|
|
13
|
+
"name": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"description": "Project name (used as the root directory name)"
|
|
16
|
+
},
|
|
17
|
+
"backend": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"enum": ["nestjs-rest", "nestjs-graphql"],
|
|
20
|
+
"default": "nestjs-rest",
|
|
21
|
+
"description": "Backend framework and API style"
|
|
22
|
+
},
|
|
23
|
+
"orm": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"enum": ["prisma", "kysely"],
|
|
26
|
+
"default": "prisma",
|
|
27
|
+
"description": "Database ORM / query builder"
|
|
28
|
+
},
|
|
29
|
+
"frontend": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"enum": ["vite", "nextjs"],
|
|
32
|
+
"default": "vite",
|
|
33
|
+
"description": "Frontend framework"
|
|
34
|
+
},
|
|
35
|
+
"mobile": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"enum": ["none", "expo"],
|
|
38
|
+
"default": "none",
|
|
39
|
+
"description": "Mobile app (Expo with Expo Router, or none)"
|
|
40
|
+
},
|
|
41
|
+
"database": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"enum": ["postgres", "mysql"],
|
|
44
|
+
"default": "postgres",
|
|
45
|
+
"description": "Database engine"
|
|
46
|
+
},
|
|
47
|
+
"packageManager": {
|
|
48
|
+
"type": "string",
|
|
49
|
+
"enum": ["pnpm", "npm"],
|
|
50
|
+
"default": "pnpm",
|
|
51
|
+
"description": "Package manager used to install dependencies"
|
|
52
|
+
},
|
|
53
|
+
"linter": {
|
|
54
|
+
"type": "string",
|
|
55
|
+
"enum": ["eslint", "biome"],
|
|
56
|
+
"default": "eslint",
|
|
57
|
+
"description": "Linter and formatter"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|