@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 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"}
@@ -1,2 +1,7 @@
1
- export declare function create(fullMode?: boolean): Promise<void>;
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":"AAKA,wBAAsB,MAAM,CAAC,QAAQ,UAAQ,iBA2H5C"}
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"}
@@ -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
- let orm = 'prisma';
27
- if (fullMode) {
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
- const frontend = await select({
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
- let linter = 'eslint';
86
- if (fullMode) {
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,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/create/index.ts"],"names":[],"mappings":"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"}
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;AAEpC,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,KAAK,UAAU,IAAI;IACjB,IAAI,OAAO,KAAK,KAAK,EAAE,CAAC;QACtB,MAAM,GAAG,CAAC,IAAI,CAAC,CAAA;IACjB,CAAC;SAAM,CAAC;QACN,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAA;IACxB,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"}
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,2 @@
1
+ export declare function init(): Promise<void>;
2
+ //# sourceMappingURL=index.d.ts.map
@@ -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.5.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
+ }