domain-driver 0.0.5 → 0.2.0

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.
Files changed (70) hide show
  1. package/README.md +138 -146
  2. package/dist/commands/action.js +118 -0
  3. package/dist/commands/component.js +18 -20
  4. package/dist/commands/container.js +12 -26
  5. package/dist/commands/controller.js +85 -0
  6. package/dist/commands/feature.js +59 -48
  7. package/dist/commands/hints.js +28 -0
  8. package/dist/commands/hook.js +12 -22
  9. package/dist/commands/repository.js +17 -101
  10. package/dist/commands/resolve.js +61 -0
  11. package/dist/commands/schema.js +22 -73
  12. package/dist/commands/service.js +15 -88
  13. package/dist/commands/sides.js +34 -0
  14. package/dist/commands/target.js +32 -0
  15. package/dist/commands/types.js +49 -0
  16. package/dist/commands/write.js +66 -0
  17. package/dist/index.js +132 -59
  18. package/dist/init/content.js +73 -0
  19. package/dist/init/init.js +60 -0
  20. package/dist/init/markers.js +21 -0
  21. package/dist/postinstall.js +80 -0
  22. package/dist/stack/detect.js +125 -0
  23. package/dist/stack/profiles/nest.js +18 -0
  24. package/dist/stack/profiles/next-frontend.js +28 -0
  25. package/dist/stack/profiles/next-fullstack.js +44 -0
  26. package/dist/stack/profiles/node.js +17 -0
  27. package/dist/stack/profiles/react.js +19 -0
  28. package/dist/stack/registry.js +63 -0
  29. package/dist/stack/types.js +24 -0
  30. package/dist/templates/actions.js +68 -0
  31. package/dist/templates/backend/server-repository.js +22 -0
  32. package/dist/templates/context.js +52 -0
  33. package/dist/templates/controllers/express.js +60 -0
  34. package/dist/templates/controllers/fastify.js +54 -0
  35. package/dist/templates/controllers/generic.js +30 -0
  36. package/dist/templates/controllers/hono.js +52 -0
  37. package/dist/templates/controllers/nest.js +63 -0
  38. package/dist/templates/controllers/next-action-route.js +40 -0
  39. package/dist/templates/controllers/next-route.js +62 -0
  40. package/dist/templates/controllers/node.js +46 -0
  41. package/dist/templates/controllers/shape.js +28 -0
  42. package/dist/templates/frontend/client-repository.js +38 -0
  43. package/dist/templates/frontend/component.js +18 -0
  44. package/dist/templates/frontend/container.js +28 -0
  45. package/dist/templates/frontend/hook.js +109 -0
  46. package/dist/templates/frontend/page.js +26 -0
  47. package/dist/templates/nest/dto.js +12 -0
  48. package/dist/templates/nest/injectable.js +4 -0
  49. package/dist/templates/nest/module.js +41 -0
  50. package/dist/templates/service.js +40 -0
  51. package/dist/templates/shared/schema.js +13 -0
  52. package/dist/templates/shared/types.js +12 -0
  53. package/dist/templates/signatures.js +15 -0
  54. package/dist/utils/alias.js +80 -0
  55. package/dist/utils/fs.js +70 -0
  56. package/dist/utils/imports.js +61 -0
  57. package/dist/utils/naming.js +31 -0
  58. package/dist/utils/paths.js +44 -0
  59. package/package.json +33 -11
  60. package/scripts/postinstall.js +7 -0
  61. package/.github/workflows/publish.yml +0 -28
  62. package/src/commands/component.ts +0 -36
  63. package/src/commands/container.ts +0 -36
  64. package/src/commands/feature.ts +0 -68
  65. package/src/commands/hook.ts +0 -32
  66. package/src/commands/repository.ts +0 -81
  67. package/src/commands/schema.ts +0 -52
  68. package/src/commands/service.ts +0 -68
  69. package/src/index.ts +0 -75
  70. package/tsconfig.json +0 -13
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderService = renderService;
4
+ const injectable_1 = require("./nest/injectable");
5
+ const signatures_1 = require("./signatures");
6
+ function renderService(ctx, spec, entity, fromFile, side) {
7
+ const repositoryClass = `${spec.name}Repository`;
8
+ const serviceClass = `${spec.name}Service`;
9
+ const repositoryLayer = side === 'client' ? 'clientRepository' : 'serverRepository';
10
+ const repositoryPath = ctx.importLayer(fromFile, repositoryLayer, `${spec.name}.repository`);
11
+ const injectable = side === 'server' && ctx.profile.name === 'nest';
12
+ const imports = [
13
+ ...(injectable ? [injectable_1.INJECTABLE_IMPORT] : []),
14
+ ...(0, signatures_1.domainImports)(ctx, fromFile, spec, entity),
15
+ `import { ${repositoryClass} } from '${repositoryPath}';`,
16
+ ].join('\n');
17
+ if (injectable) {
18
+ return `${imports}
19
+
20
+ @Injectable()
21
+ export class ${serviceClass} {
22
+ constructor(private readonly repository: ${repositoryClass}) {}
23
+
24
+ async handle(${spec.params}): ${spec.returns} {
25
+ return this.repository.handle(${spec.args});
26
+ }
27
+ }
28
+ `;
29
+ }
30
+ return `${imports}
31
+
32
+ const repository = new ${repositoryClass}();
33
+
34
+ export class ${serviceClass} {
35
+ async handle(${spec.params}): ${spec.returns} {
36
+ return repository.handle(${spec.args});
37
+ }
38
+ }
39
+ `;
40
+ }
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderSchema = renderSchema;
4
+ function renderSchema(name, label) {
5
+ return `import { z } from 'zod';
6
+
7
+ export const ${name}Schema = z.object({
8
+ // add ${label} fields here
9
+ });
10
+
11
+ export type ${name} = z.infer<typeof ${name}Schema>;
12
+ `;
13
+ }
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderTypes = renderTypes;
4
+ function renderTypes(entity) {
5
+ return `export interface ${entity} {
6
+ id: string;
7
+ // add ${entity} fields here
8
+ createdAt: string;
9
+ updatedAt: string;
10
+ }
11
+ `;
12
+ }
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.domainImports = domainImports;
4
+ function domainImports(ctx, fromFile, spec, entity) {
5
+ const lines = [];
6
+ if (spec.usesEntityType) {
7
+ const typePath = ctx.importLayer(fromFile, 'types', `${entity}.types`);
8
+ lines.push(`import { ${entity} } from '${typePath}';`);
9
+ }
10
+ if (spec.schema !== null) {
11
+ const schemaPath = ctx.importLayer(fromFile, 'schema', `${spec.schema}.schema`);
12
+ lines.push(`import { ${spec.schema} } from '${schemaPath}';`);
13
+ }
14
+ return lines;
15
+ }
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.resetAliasCache = resetAliasCache;
37
+ exports.detectAlias = detectAlias;
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
40
+ const WILDCARD_TARGET = /^(?:\.\/)?(.*?)\/?\*$/;
41
+ let cached;
42
+ function resetAliasCache() {
43
+ cached = undefined;
44
+ }
45
+ function detectAlias() {
46
+ if (cached !== undefined)
47
+ return cached;
48
+ cached = readAlias(path.join(process.cwd(), 'tsconfig.json'));
49
+ return cached;
50
+ }
51
+ function readAlias(tsconfigPath) {
52
+ if (!fs.existsSync(tsconfigPath))
53
+ return null;
54
+ try {
55
+ const raw = fs.readFileSync(tsconfigPath, 'utf-8');
56
+ const stripped = raw.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, '');
57
+ const tsconfig = JSON.parse(stripped);
58
+ return findWildcardAlias(tsconfig.compilerOptions?.paths ?? {});
59
+ }
60
+ catch {
61
+ return null;
62
+ }
63
+ }
64
+ function findWildcardAlias(paths) {
65
+ for (const [key, targets] of Object.entries(paths)) {
66
+ if (!key.endsWith('/*'))
67
+ continue;
68
+ const root = targets.map(aliasRoot).find((candidate) => candidate !== null);
69
+ if (root !== undefined && root !== null) {
70
+ return Object.freeze({ prefix: key.slice(0, -1), root });
71
+ }
72
+ }
73
+ return null;
74
+ }
75
+ function aliasRoot(target) {
76
+ const match = WILDCARD_TARGET.exec(target);
77
+ if (!match)
78
+ return null;
79
+ return match[1] === '' ? '.' : match[1];
80
+ }
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.writeFileSafe = writeFileSafe;
37
+ exports.mkdirSafe = mkdirSafe;
38
+ exports.fileExists = fileExists;
39
+ exports.isDirectory = isDirectory;
40
+ exports.readTextFile = readTextFile;
41
+ const fs = __importStar(require("fs"));
42
+ function writeFileSafe(filePath, content) {
43
+ try {
44
+ fs.writeFileSync(filePath, content);
45
+ }
46
+ catch (error) {
47
+ const message = error instanceof Error ? error.message : 'Unknown error';
48
+ throw new Error(`Failed to write ${filePath}: ${message}`);
49
+ }
50
+ }
51
+ function mkdirSafe(dirPath) {
52
+ try {
53
+ fs.mkdirSync(dirPath, { recursive: true });
54
+ }
55
+ catch (error) {
56
+ const message = error instanceof Error ? error.message : 'Unknown error';
57
+ throw new Error(`Failed to create directory ${dirPath}: ${message}`);
58
+ }
59
+ }
60
+ function fileExists(filePath) {
61
+ return fs.existsSync(filePath);
62
+ }
63
+ function isDirectory(target) {
64
+ return fs.existsSync(target) && fs.statSync(target).isDirectory();
65
+ }
66
+ function readTextFile(filePath) {
67
+ if (!fs.existsSync(filePath))
68
+ return null;
69
+ return fs.readFileSync(filePath, 'utf-8');
70
+ }
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.resolveImport = resolveImport;
37
+ const path = __importStar(require("path"));
38
+ const alias_1 = require("./alias");
39
+ function resolveImport(fromFile, toFile) {
40
+ const target = stripExtension(toFile);
41
+ const aliased = aliasImport(target);
42
+ if (aliased !== null)
43
+ return aliased;
44
+ const relative = toPosix(path.relative(path.dirname(fromFile), target));
45
+ return relative.startsWith('.') ? relative : `./${relative}`;
46
+ }
47
+ function aliasImport(target) {
48
+ const alias = (0, alias_1.detectAlias)();
49
+ if (!alias)
50
+ return null;
51
+ const rootAbs = path.resolve(process.cwd(), alias.root);
52
+ const relative = path.relative(rootAbs, target);
53
+ const inside = relative !== '' && !relative.startsWith('..') && !path.isAbsolute(relative);
54
+ return inside ? `${alias.prefix}${toPosix(relative)}` : null;
55
+ }
56
+ function stripExtension(file) {
57
+ return file.replace(/\.tsx?$/, '');
58
+ }
59
+ function toPosix(target) {
60
+ return target.split(path.sep).join('/');
61
+ }
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toPascalCase = toPascalCase;
4
+ exports.lowerFirst = lowerFirst;
5
+ exports.validateFeatureName = validateFeatureName;
6
+ exports.upperFirst = upperFirst;
7
+ exports.toKebabCase = toKebabCase;
8
+ const FEATURE_NAME_PATTERN = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
9
+ function toPascalCase(name) {
10
+ return name
11
+ .split('-')
12
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
13
+ .join('');
14
+ }
15
+ function lowerFirst(name) {
16
+ return name.charAt(0).toLowerCase() + name.slice(1);
17
+ }
18
+ function validateFeatureName(name) {
19
+ if (!FEATURE_NAME_PATTERN.test(name)) {
20
+ throw new Error(`Feature name "${name}" must be kebab-case, for example coffee-type.`);
21
+ }
22
+ }
23
+ function upperFirst(name) {
24
+ return name.charAt(0).toUpperCase() + name.slice(1);
25
+ }
26
+ function toKebabCase(name) {
27
+ return name
28
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
29
+ .replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')
30
+ .toLowerCase();
31
+ }
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.featureDir = featureDir;
37
+ exports.apiRouteDir = apiRouteDir;
38
+ const path = __importStar(require("path"));
39
+ function featureDir(stack, feature) {
40
+ return path.join(process.cwd(), stack.featureRoot, feature);
41
+ }
42
+ function apiRouteDir(stack, feature) {
43
+ return path.join(process.cwd(), stack.featureRoot, 'api', feature);
44
+ }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "domain-driver",
3
- "version": "0.0.5",
4
- "description": "",
5
- "main": "index.js",
3
+ "version": "0.2.0",
4
+ "description": "CLI scaffolding tool for domain-driven feature folders in Next.js, React, Node, and NestJS projects, with per-action files, bespoke actions, and agent guidance",
5
+ "main": "dist/index.js",
6
6
  "bin": {
7
7
  "domain-driver": "./dist/index.js"
8
8
  },
@@ -10,18 +10,40 @@
10
10
  "type": "git",
11
11
  "url": "https://github.com/IsaacHatilima/domain-driver"
12
12
  },
13
+ "files": [
14
+ "dist",
15
+ "scripts/postinstall.js"
16
+ ],
13
17
  "scripts": {
14
- "build": "npx tsc",
15
- "dev": "ts-node src/index.ts",
16
- "test": "echo \"Error: no test specified\" && exit 1"
18
+ "build": "node -e \"require('fs').rmSync('dist', { recursive: true, force: true })\" && tsc",
19
+ "test": "vitest run",
20
+ "test:coverage": "vitest run --coverage",
21
+ "postinstall": "node ./scripts/postinstall.js"
22
+ },
23
+ "keywords": [
24
+ "cli",
25
+ "scaffolding",
26
+ "domain-driven",
27
+ "nextjs",
28
+ "react",
29
+ "nestjs",
30
+ "node",
31
+ "express",
32
+ "fastify",
33
+ "hono"
34
+ ],
35
+ "author": "Isaac Hatilima",
36
+ "license": "MIT",
37
+ "engines": {
38
+ "node": ">=18"
17
39
  },
18
- "keywords": [],
19
- "author": "",
20
- "license": "ISC",
21
40
  "devDependencies": {
22
- "@types/node": "^25.4.0"
41
+ "@types/node": "^25.4.0",
42
+ "@vitest/coverage-v8": "^4.1.2",
43
+ "typescript": "^5.9.3",
44
+ "vitest": "^4.1.2"
23
45
  },
24
46
  "dependencies": {
25
- "typescript": "^5.9.3"
47
+ "commander": "^14.0.3"
26
48
  }
27
49
  }
@@ -0,0 +1,7 @@
1
+ // Runs after `npm install` in a consuming project. Never fails the install:
2
+ // dist/ may be absent on a fresh clone, and init may throw for any reason.
3
+ try {
4
+ require('../dist/postinstall.js').run();
5
+ } catch (_error) {
6
+ // intentionally silent
7
+ }
@@ -1,28 +0,0 @@
1
- name: Publish to npm
2
-
3
- on:
4
- push:
5
- branches:
6
- - master
7
- tags:
8
- - 'v*'
9
-
10
- jobs:
11
- publish:
12
- runs-on: ubuntu-latest
13
- permissions:
14
- id-token: write
15
-
16
- steps:
17
- - uses: actions/checkout@v4
18
-
19
- - uses: actions/setup-node@v4
20
- with:
21
- node-version: '18'
22
- registry-url: 'https://registry.npmjs.org'
23
-
24
- - run: npm install
25
- - run: npm run build
26
- - run: npm publish --provenance --access public
27
- env:
28
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -1,36 +0,0 @@
1
- import * as fs from 'fs';
2
- import * as path from 'path';
3
-
4
- type ComponentType = 'client' | 'server';
5
-
6
- function renderComponent(name: string, type: ComponentType): string {
7
- const directive = type === 'client' ? "'use client';\n\n" : '';
8
-
9
- return `${directive}export default function ${name}() {
10
- return (
11
- <div>
12
- <h1>${name}</h1>
13
- </div>
14
- );
15
- }
16
- `;
17
- }
18
-
19
- export function makeComponent(feature: string, name: string, type: ComponentType = 'client') {
20
- const base = path.join(process.cwd(), 'app', feature, 'components', type);
21
-
22
- if (!fs.existsSync(base)) {
23
- console.error(`❌ Feature "${feature}" does not exist. Run make:feature ${feature} first.`);
24
- process.exit(1);
25
- }
26
-
27
- const filePath = path.join(base, `${name}.tsx`);
28
-
29
- if (fs.existsSync(filePath)) {
30
- console.error(`❌ Component "${name}" already exists at ${filePath}`);
31
- process.exit(1);
32
- }
33
-
34
- fs.writeFileSync(filePath, renderComponent(name, type));
35
- console.log(`✅ Component "${name}" created at ${filePath}`);
36
- }
@@ -1,36 +0,0 @@
1
- import * as fs from 'fs';
2
- import * as path from 'path';
3
-
4
- function renderContainer(name: string): string {
5
- return `'use client';
6
-
7
- interface Props {}
8
-
9
- export default function ${name}({ }: Props) {
10
- return (
11
- <div>
12
- <h1>${name}</h1>
13
- </div>
14
- );
15
- }
16
- `;
17
- }
18
-
19
- export function makeContainer(feature: string, name: string) {
20
- const base = path.join(process.cwd(), 'app', feature, 'containers');
21
-
22
- if (!fs.existsSync(base)) {
23
- console.error(`❌ Feature "${feature}" does not exist. Run make:feature ${feature} first.`);
24
- process.exit(1);
25
- }
26
-
27
- const filePath = path.join(base, `${name}.tsx`);
28
-
29
- if (fs.existsSync(filePath)) {
30
- console.error(`❌ Container "${name}" already exists at ${filePath}`);
31
- process.exit(1);
32
- }
33
-
34
- fs.writeFileSync(filePath, renderContainer(name));
35
- console.log(`✅ Container "${name}" created at ${filePath}`);
36
- }
@@ -1,68 +0,0 @@
1
- import * as fs from 'fs';
2
- import * as path from 'path';
3
- import { makeComponent } from './component';
4
- import { makeHook } from './hook';
5
- import { makeService } from './service';
6
- import { makeRepository } from './repository';
7
- import { makeSchema } from './schema';
8
- import { makeContainer } from './container';
9
-
10
- const FEATURE_DIRS = [
11
- 'components/server',
12
- 'components/client',
13
- 'containers',
14
- 'hooks',
15
- 'services',
16
- 'repositories',
17
- 'schemas',
18
- ];
19
-
20
- function toPascalCase(name: string): string {
21
- return name
22
- .split('-')
23
- .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
24
- .join('');
25
- }
26
-
27
- function renderPage(name: string): string {
28
- const componentName = toPascalCase(name);
29
- return `export default function ${componentName}Page() {
30
- return (
31
- <div>
32
- <h1>${componentName}</h1>
33
- </div>
34
- );
35
- }
36
- `;
37
- }
38
-
39
- export async function makeFeature(name: string, all: boolean = false) {
40
- const base = path.join(process.cwd(), 'app', name);
41
- const pascalName = toPascalCase(name);
42
-
43
- if (fs.existsSync(base)) {
44
- console.error(`❌ Feature "${name}" already exists at ${base}`);
45
- process.exit(1);
46
- }
47
-
48
- for (const dir of FEATURE_DIRS) {
49
- fs.mkdirSync(path.join(base, dir), { recursive: true });
50
- if (!all) {
51
- fs.writeFileSync(path.join(base, dir, '.gitkeep'), '');
52
- }
53
- }
54
-
55
- fs.writeFileSync(path.join(base, 'page.tsx'), renderPage(name));
56
- console.log(`✅ Feature "${name}" scaffolded at ${base}`);
57
-
58
- if (all) {
59
- makeComponent(name, pascalName, 'client');
60
- makeContainer(name, `${pascalName}Container`);
61
- makeHook(name, `use${pascalName}`);
62
- makeService(name, pascalName);
63
- makeRepository(name, `${pascalName}Repository`);
64
- makeSchema(name, `${pascalName}Schema`);
65
-
66
- console.log(`✅ All files scaffolded for "${name}"`);
67
- }
68
- }
@@ -1,32 +0,0 @@
1
- import * as fs from 'fs';
2
- import * as path from 'path';
3
-
4
- function renderHook(name: string): string {
5
- return `import { useState } from 'react';
6
-
7
- export function ${name}() {
8
- const [data, setData] = useState(null);
9
-
10
- return { data };
11
- }
12
- `;
13
- }
14
-
15
- export function makeHook(feature: string, name: string) {
16
- const base = path.join(process.cwd(), 'app', feature, 'hooks');
17
-
18
- if (!fs.existsSync(base)) {
19
- console.error(`❌ Feature "${feature}" does not exist. Run make:feature ${feature} first.`);
20
- process.exit(1);
21
- }
22
-
23
- const filePath = path.join(base, `${name}.ts`);
24
-
25
- if (fs.existsSync(filePath)) {
26
- console.error(`❌ Hook "${name}" already exists at ${filePath}`);
27
- process.exit(1);
28
- }
29
-
30
- fs.writeFileSync(filePath, renderHook(name));
31
- console.log(`✅ Hook "${name}" created at ${filePath}`);
32
- }