@igniter-js/cli 0.1.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.
- package/dist/index.d.ts +2 -0
- package/dist/index.js +358 -0
- package/dist/templates/components.json.hbs +21 -0
- package/dist/templates/copilot.instructions.hbs +117 -0
- package/dist/templates/docker-compose.hbs +15 -0
- package/dist/templates/env.hbs +33 -0
- package/dist/templates/eslintrc.hbs +3 -0
- package/dist/templates/feature.controller.hbs +83 -0
- package/dist/templates/feature.index.hbs +5 -0
- package/dist/templates/feature.interface.hbs +71 -0
- package/dist/templates/feature.procedure.hbs +76 -0
- package/dist/templates/globals.hbs +198 -0
- package/dist/templates/igniter.client.hbs +21 -0
- package/dist/templates/igniter.context.hbs +23 -0
- package/dist/templates/igniter.hbs +8 -0
- package/dist/templates/igniter.router.hbs +29 -0
- package/dist/templates/layout.hbs +54 -0
- package/dist/templates/page.hbs +313 -0
- package/dist/templates/prisma.hbs +9 -0
- package/dist/templates/readme.hbs +136 -0
- package/dist/templates/vitest.config.hbs +11 -0
- package/dist/utils/analyze.d.ts +17 -0
- package/dist/utils/analyze.js +187 -0
- package/dist/utils/consts.d.ts +26 -0
- package/dist/utils/consts.js +34 -0
- package/dist/utils/handlebars-helpers.d.ts +1 -0
- package/dist/utils/handlebars-helpers.js +43 -0
- package/dist/utils/helpers.d.ts +12 -0
- package/dist/utils/helpers.js +102 -0
- package/dist/utils/prisma-schema-parser.d.ts +59 -0
- package/dist/utils/prisma-schema-parser.js +197 -0
- package/dist/utils/template-handler.d.ts +6 -0
- package/dist/utils/template-handler.js +33 -0
- package/package.json +73 -0
- package/readme.md +143 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare const PROJECT_STRUCTURE: {
|
|
2
|
+
docs: string[];
|
|
3
|
+
public: never[];
|
|
4
|
+
scripts: string[];
|
|
5
|
+
'.github/actions': string[];
|
|
6
|
+
'src/configs': string[];
|
|
7
|
+
'src/core/design-system': never[];
|
|
8
|
+
'src/core/utils': never[];
|
|
9
|
+
'src/core/providers': never[];
|
|
10
|
+
'src/features': string[];
|
|
11
|
+
};
|
|
12
|
+
export declare const LIA_FILES: {
|
|
13
|
+
'project_requirement.md': string;
|
|
14
|
+
'detailed_app_flow.md': string;
|
|
15
|
+
'tech_stack_and_packages.md': string;
|
|
16
|
+
'file_structure.md': string;
|
|
17
|
+
'schema_design.md': string;
|
|
18
|
+
};
|
|
19
|
+
export declare const DEPENDENCIES: {
|
|
20
|
+
required: string[];
|
|
21
|
+
};
|
|
22
|
+
export declare const CONFIG_FILES: {
|
|
23
|
+
name: string;
|
|
24
|
+
template: string;
|
|
25
|
+
}[];
|
|
26
|
+
export declare const INTERACTIVE_COMMANDS: string[];
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.INTERACTIVE_COMMANDS = exports.CONFIG_FILES = exports.DEPENDENCIES = exports.LIA_FILES = exports.PROJECT_STRUCTURE = void 0;
|
|
4
|
+
exports.PROJECT_STRUCTURE = {
|
|
5
|
+
docs: ['.gitkeep'],
|
|
6
|
+
public: [],
|
|
7
|
+
scripts: ['.gitkeep'],
|
|
8
|
+
'.github/actions': ['.gitkeep'],
|
|
9
|
+
'src/configs': ['.gitkeep'],
|
|
10
|
+
'src/core/design-system': [],
|
|
11
|
+
'src/core/utils': [],
|
|
12
|
+
'src/core/providers': [],
|
|
13
|
+
'src/features': ['.gitkeep'],
|
|
14
|
+
};
|
|
15
|
+
exports.LIA_FILES = {
|
|
16
|
+
'project_requirement.md': '',
|
|
17
|
+
'detailed_app_flow.md': '',
|
|
18
|
+
'tech_stack_and_packages.md': '',
|
|
19
|
+
'file_structure.md': '',
|
|
20
|
+
'schema_design.md': ''
|
|
21
|
+
};
|
|
22
|
+
exports.DEPENDENCIES = {
|
|
23
|
+
required: ['docker', 'node', 'npm'],
|
|
24
|
+
};
|
|
25
|
+
exports.CONFIG_FILES = [
|
|
26
|
+
{ name: 'README.md', template: 'readme' },
|
|
27
|
+
{ name: 'eslintrc.json', template: 'eslintrc' },
|
|
28
|
+
{ name: 'components.json', template: 'components.json' },
|
|
29
|
+
{ name: 'docker-compose.yml', template: 'docker-compose' },
|
|
30
|
+
{ name: 'vitest.config.ts', template: 'vitest.config' },
|
|
31
|
+
];
|
|
32
|
+
exports.INTERACTIVE_COMMANDS = [
|
|
33
|
+
'shadcn',
|
|
34
|
+
];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function registerHelpers(): void;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.registerHelpers = registerHelpers;
|
|
7
|
+
const handlebars_1 = __importDefault(require("handlebars"));
|
|
8
|
+
function registerHelpers() {
|
|
9
|
+
handlebars_1.default.registerHelper('camelCase', (str) => {
|
|
10
|
+
return str.replace(/[-_]([a-z])/g, (g) => g[1].toUpperCase());
|
|
11
|
+
});
|
|
12
|
+
handlebars_1.default.registerHelper('pascalCase', (str) => {
|
|
13
|
+
const camelCase = str.replace(/[-_]([a-z])/g, (g) => g[1].toUpperCase());
|
|
14
|
+
return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);
|
|
15
|
+
});
|
|
16
|
+
handlebars_1.default.registerHelper('kebabCase', (str) => {
|
|
17
|
+
return str
|
|
18
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
19
|
+
.replace(/[\s_]+/g, '-')
|
|
20
|
+
.toLowerCase();
|
|
21
|
+
});
|
|
22
|
+
handlebars_1.default.registerHelper('snakeCase', (str) => {
|
|
23
|
+
return str
|
|
24
|
+
.replace(/([a-z])([A-Z])/g, '$1_$2')
|
|
25
|
+
.replace(/[\s-]+/g, '_')
|
|
26
|
+
.toLowerCase();
|
|
27
|
+
});
|
|
28
|
+
handlebars_1.default.registerHelper('lowerFirstLetter', (str) => {
|
|
29
|
+
return str.charAt(0).toLowerCase() + str.slice(1);
|
|
30
|
+
});
|
|
31
|
+
handlebars_1.default.registerHelper('lowerCase', (str) => {
|
|
32
|
+
return str.toLowerCase();
|
|
33
|
+
});
|
|
34
|
+
/**
|
|
35
|
+
* Handlebars helper to compare if two values are equal
|
|
36
|
+
* @param {string} firstValue - First value to compare
|
|
37
|
+
* @param {string} secondValue - Second value to compare
|
|
38
|
+
* @returns {boolean} Returns true if values are equal, false otherwise
|
|
39
|
+
*/
|
|
40
|
+
handlebars_1.default.registerHelper('equals', (firstValue, secondValue) => {
|
|
41
|
+
return firstValue === secondValue;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare abstract class CLIHelper {
|
|
2
|
+
protected execCommand(command: string): void;
|
|
3
|
+
protected createDir(dir: string): void;
|
|
4
|
+
protected createFile(filePath: string, content?: string): void;
|
|
5
|
+
protected updateFile(filePath: string, content: string): void;
|
|
6
|
+
protected checkDependencies(dependencies: string[]): void;
|
|
7
|
+
protected delay(ms: number): Promise<void>;
|
|
8
|
+
protected loadJSON(filePath: string): any;
|
|
9
|
+
protected saveJSON(filePath: string, data: any): void;
|
|
10
|
+
protected createDirectoryStructure(structure: Record<string, any>, basePath?: string): void;
|
|
11
|
+
protected installDependencies(dependencies: Record<string, string>, dev?: boolean): void;
|
|
12
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CLIHelper = void 0;
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const consts_1 = require("./consts");
|
|
11
|
+
class CLIHelper {
|
|
12
|
+
execCommand(command) {
|
|
13
|
+
try {
|
|
14
|
+
const needsInteraction = consts_1.INTERACTIVE_COMMANDS.some((cmd) => command.includes(cmd));
|
|
15
|
+
if (needsInteraction) {
|
|
16
|
+
console.log(`\n📦 Executing: ${command}\n`);
|
|
17
|
+
(0, child_process_1.execSync)(command, { stdio: 'inherit' });
|
|
18
|
+
console.log('\n✅ Command completed successfully\n');
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
(0, child_process_1.execSync)(command, {
|
|
22
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
23
|
+
encoding: 'utf-8'
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
console.error(`Failed to execute command: ${command} \n${error}`);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
createDir(dir) {
|
|
33
|
+
if (!fs_1.default.existsSync(dir)) {
|
|
34
|
+
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
createFile(filePath, content = '') {
|
|
38
|
+
const dir = path_1.default.dirname(filePath);
|
|
39
|
+
if (!fs_1.default.existsSync(dir)) {
|
|
40
|
+
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
fs_1.default.writeFileSync(filePath, content);
|
|
43
|
+
}
|
|
44
|
+
updateFile(filePath, content) {
|
|
45
|
+
fs_1.default.writeFileSync(filePath, content);
|
|
46
|
+
}
|
|
47
|
+
checkDependencies(dependencies) {
|
|
48
|
+
for (const dep of dependencies) {
|
|
49
|
+
try {
|
|
50
|
+
(0, child_process_1.execSync)(`${dep} --version`, { stdio: 'pipe' });
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
console.error(`Required dependency not found: ${dep}. Please install it before proceeding. \n${error}`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async delay(ms) {
|
|
59
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
60
|
+
}
|
|
61
|
+
loadJSON(filePath) {
|
|
62
|
+
try {
|
|
63
|
+
return JSON.parse(fs_1.default.readFileSync(filePath, 'utf8'));
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
console.error(`Failed to load JSON file: ${filePath} \n${error}`);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
saveJSON(filePath, data) {
|
|
71
|
+
try {
|
|
72
|
+
fs_1.default.writeFileSync(filePath, JSON.stringify(data, null, 2));
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
console.error(`Failed to save JSON file: ${filePath} \n${error}`);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
createDirectoryStructure(structure, basePath = '') {
|
|
80
|
+
Object.entries(structure).forEach(([dir, content]) => {
|
|
81
|
+
const fullPath = path_1.default.join(basePath, dir);
|
|
82
|
+
this.createDir(fullPath);
|
|
83
|
+
if (Array.isArray(content)) {
|
|
84
|
+
content.forEach(file => {
|
|
85
|
+
if (typeof file === 'string') {
|
|
86
|
+
this.createFile(path_1.default.join(fullPath, file));
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
else if (content && typeof content === 'object') {
|
|
91
|
+
this.createDirectoryStructure(content, fullPath);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
installDependencies(dependencies, dev = false) {
|
|
96
|
+
const deps = Object.entries(dependencies)
|
|
97
|
+
.map(([name, version]) => `${name}@${version}`)
|
|
98
|
+
.join(' ');
|
|
99
|
+
this.execCommand(`npm install ${dev ? '-D ' : ''}${deps}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
exports.CLIHelper = CLIHelper;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
interface ParsedField {
|
|
2
|
+
name: string;
|
|
3
|
+
type: string;
|
|
4
|
+
zodType: string;
|
|
5
|
+
description: string;
|
|
6
|
+
isOptional: boolean;
|
|
7
|
+
isList: boolean;
|
|
8
|
+
hasDefault: boolean;
|
|
9
|
+
isEnum?: boolean;
|
|
10
|
+
enumValues?: string[];
|
|
11
|
+
relations?: {
|
|
12
|
+
type: 'one-to-one' | 'one-to-many' | 'many-to-many';
|
|
13
|
+
model: string;
|
|
14
|
+
fields: string[];
|
|
15
|
+
references: string[];
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export declare class PrismaSchemaParser {
|
|
19
|
+
private readonly schemaPath;
|
|
20
|
+
private schemaContent;
|
|
21
|
+
constructor(basePath?: string);
|
|
22
|
+
/**
|
|
23
|
+
* Loads the Prisma schema file content
|
|
24
|
+
*/
|
|
25
|
+
private loadSchema;
|
|
26
|
+
/**
|
|
27
|
+
* Gets the raw schema content
|
|
28
|
+
*/
|
|
29
|
+
getSchemaContent(): string;
|
|
30
|
+
/**
|
|
31
|
+
* Checks if a model exists in the Prisma schema
|
|
32
|
+
*/
|
|
33
|
+
hasModel(modelName: string): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Gets fields from a specific model
|
|
36
|
+
*/
|
|
37
|
+
getModelFields(modelName: string): ParsedField[];
|
|
38
|
+
/**
|
|
39
|
+
* Parses field strings into structured field objects
|
|
40
|
+
*/
|
|
41
|
+
private parseFields;
|
|
42
|
+
/**
|
|
43
|
+
* Parses a single field line into a structured field object
|
|
44
|
+
*/
|
|
45
|
+
private parseFieldLine;
|
|
46
|
+
/**
|
|
47
|
+
* Parses a relation decorator into a structured object
|
|
48
|
+
*/
|
|
49
|
+
private parseRelation;
|
|
50
|
+
/**
|
|
51
|
+
* Decodes HTML entities in a string
|
|
52
|
+
*/
|
|
53
|
+
private decodeHtmlEntities;
|
|
54
|
+
/**
|
|
55
|
+
* Converts Prisma types to Zod types
|
|
56
|
+
*/
|
|
57
|
+
private getZodType;
|
|
58
|
+
}
|
|
59
|
+
export {};
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.PrismaSchemaParser = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
class PrismaSchemaParser {
|
|
10
|
+
constructor(basePath = process.cwd()) {
|
|
11
|
+
this.schemaContent = '';
|
|
12
|
+
this.schemaPath = path_1.default.join(basePath, 'prisma/schema.prisma');
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Loads the Prisma schema file content
|
|
16
|
+
*/
|
|
17
|
+
loadSchema() {
|
|
18
|
+
if (!fs_1.default.existsSync(this.schemaPath)) {
|
|
19
|
+
throw new Error('Prisma schema file not found');
|
|
20
|
+
}
|
|
21
|
+
this.schemaContent = fs_1.default.readFileSync(this.schemaPath, 'utf-8');
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Gets the raw schema content
|
|
25
|
+
*/
|
|
26
|
+
getSchemaContent() {
|
|
27
|
+
this.loadSchema();
|
|
28
|
+
return this.schemaContent;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Checks if a model exists in the Prisma schema
|
|
32
|
+
*/
|
|
33
|
+
hasModel(modelName) {
|
|
34
|
+
this.loadSchema();
|
|
35
|
+
const modelRegex = new RegExp(`model\\s+${modelName}\\s*{`, 'i');
|
|
36
|
+
return modelRegex.test(this.schemaContent);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Gets fields from a specific model
|
|
40
|
+
*/
|
|
41
|
+
getModelFields(modelName) {
|
|
42
|
+
this.loadSchema();
|
|
43
|
+
if (!this.hasModel(modelName)) {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
const modelRegex = new RegExp(`model\\s+${modelName}\\s*{([^}]*)}`, 'i');
|
|
47
|
+
const modelMatch = this.schemaContent.match(modelRegex);
|
|
48
|
+
if (!modelMatch || !modelMatch[1]) {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
const fieldsContent = modelMatch[1].trim();
|
|
52
|
+
return this.parseFields(fieldsContent);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Parses field strings into structured field objects
|
|
56
|
+
*/
|
|
57
|
+
parseFields(fieldsContent) {
|
|
58
|
+
this.loadSchema();
|
|
59
|
+
const fieldLines = fieldsContent.split('\n');
|
|
60
|
+
const fields = [];
|
|
61
|
+
for (const line of fieldLines) {
|
|
62
|
+
const trimmedLine = line.trim();
|
|
63
|
+
if (!trimmedLine || trimmedLine.startsWith('@@'))
|
|
64
|
+
continue;
|
|
65
|
+
const field = this.parseFieldLine(trimmedLine);
|
|
66
|
+
if (field) {
|
|
67
|
+
fields.push(field);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return fields;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Parses a single field line into a structured field object
|
|
74
|
+
*/
|
|
75
|
+
parseFieldLine(line) {
|
|
76
|
+
this.loadSchema();
|
|
77
|
+
// Basic field pattern: name type modifiers
|
|
78
|
+
const fieldPattern = /^(\w+)\s+(\w+)(\?|\[\])?\s*(@\w+(?:\([^)]*\))?)*$/;
|
|
79
|
+
const match = line.match(fieldPattern);
|
|
80
|
+
if (!match)
|
|
81
|
+
return null;
|
|
82
|
+
const [, name, type] = match;
|
|
83
|
+
const modifiers = line.slice(match[0].length).trim();
|
|
84
|
+
const isOptional = line.includes('?');
|
|
85
|
+
const isList = line.includes('[]');
|
|
86
|
+
const hasDefault = modifiers.includes('@default');
|
|
87
|
+
// Skip internal Prisma fields
|
|
88
|
+
if (['id', 'createdAt', 'updatedAt'].includes(name)) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
// Verifica se é um enum
|
|
92
|
+
const isEnum = this.schemaContent.includes(`enum ${type} {`);
|
|
93
|
+
// Parse relations
|
|
94
|
+
let relations = undefined;
|
|
95
|
+
if (line.includes('@relation')) {
|
|
96
|
+
const relationMatch = line.match(/@relation\([^)]*\)/);
|
|
97
|
+
if (relationMatch) {
|
|
98
|
+
relations = this.parseRelation(relationMatch[0], isList);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
name,
|
|
103
|
+
type,
|
|
104
|
+
zodType: this.getZodType(type, isOptional, isList),
|
|
105
|
+
description: `${name} field`,
|
|
106
|
+
isOptional,
|
|
107
|
+
isList,
|
|
108
|
+
hasDefault,
|
|
109
|
+
isEnum,
|
|
110
|
+
relations
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Parses a relation decorator into a structured object
|
|
115
|
+
*/
|
|
116
|
+
parseRelation(relationString, isList) {
|
|
117
|
+
var _a;
|
|
118
|
+
const relationDetails = (_a = relationString.match(/@relation\(([^)]+)\)/)) === null || _a === void 0 ? void 0 : _a[1].split(',');
|
|
119
|
+
if (!relationDetails)
|
|
120
|
+
return undefined;
|
|
121
|
+
const nameMatch = relationDetails.find(detail => detail.trim().startsWith('name:'));
|
|
122
|
+
const fieldsMatch = relationDetails.find(detail => detail.trim().startsWith('fields:'));
|
|
123
|
+
const referencesMatch = relationDetails.find(detail => detail.trim().startsWith('references:'));
|
|
124
|
+
if (nameMatch && fieldsMatch && referencesMatch) {
|
|
125
|
+
const model = nameMatch.split(':')[1].trim().replace(/['"]/g, '');
|
|
126
|
+
const fields = fieldsMatch.split(':')[1].trim().replace(/\[|\]/g, '').split(',').map(f => f.trim());
|
|
127
|
+
const references = referencesMatch.split(':')[1].trim().replace(/\[|\]/g, '').split(',').map(f => f.trim());
|
|
128
|
+
return {
|
|
129
|
+
type: fields.length > 1 || references.length > 1 ? 'many-to-many' : (isList ? 'one-to-many' : 'one-to-one'),
|
|
130
|
+
model,
|
|
131
|
+
fields,
|
|
132
|
+
references
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Decodes HTML entities in a string
|
|
139
|
+
*/
|
|
140
|
+
decodeHtmlEntities(text) {
|
|
141
|
+
return text
|
|
142
|
+
.replace(/&/g, '&')
|
|
143
|
+
.replace(/</g, '<')
|
|
144
|
+
.replace(/>/g, '>')
|
|
145
|
+
.replace(/"/g, '"')
|
|
146
|
+
.replace(/'/g, "'")
|
|
147
|
+
.replace(/`/g, '`');
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Converts Prisma types to Zod types
|
|
151
|
+
*/
|
|
152
|
+
getZodType(type, isOptional = false, isList = false) {
|
|
153
|
+
this.loadSchema();
|
|
154
|
+
// Verifica se é um enum primeiro
|
|
155
|
+
const enumMatch = this.schemaContent.match(new RegExp(`enum\\s+${type}\\s*{([^}]*)}`, 'i'));
|
|
156
|
+
if (enumMatch) {
|
|
157
|
+
const enumValues = enumMatch[1]
|
|
158
|
+
.trim()
|
|
159
|
+
.split('\n')
|
|
160
|
+
.map(v => v.trim())
|
|
161
|
+
.filter(Boolean)
|
|
162
|
+
.map(v => this.decodeHtmlEntities(v)) // Decodifica HTML entities
|
|
163
|
+
.map(v => v.replace(/['"]/g, '').trim());
|
|
164
|
+
return `z.enum([${enumValues.map(v => `'${v}'`).join(', ')}])`;
|
|
165
|
+
}
|
|
166
|
+
const uuidMatch = this.schemaContent.match(/@default\(uuid\((\d+)?\)\)/);
|
|
167
|
+
const uuidVersion = uuidMatch ? (uuidMatch[1] || '4') : null;
|
|
168
|
+
const typeMap = {
|
|
169
|
+
'String': 'z.string()',
|
|
170
|
+
'Int': 'z.number().int()',
|
|
171
|
+
'Float': 'z.number()',
|
|
172
|
+
'Boolean': 'z.boolean()',
|
|
173
|
+
'DateTime': 'z.date()',
|
|
174
|
+
'Json': 'z.any()',
|
|
175
|
+
'BigInt': 'z.bigint()',
|
|
176
|
+
'Decimal': 'z.number()',
|
|
177
|
+
'Bytes': 'z.instanceof(Buffer)',
|
|
178
|
+
'UUID': `z.string().uuid(${uuidVersion ? `{ version: ${uuidVersion} }` : ''})`,
|
|
179
|
+
};
|
|
180
|
+
// Handle relationships
|
|
181
|
+
if (!typeMap[type]) {
|
|
182
|
+
if (isList) {
|
|
183
|
+
return 'z.array(z.string().min(1))';
|
|
184
|
+
}
|
|
185
|
+
return 'z.string().min(1)';
|
|
186
|
+
}
|
|
187
|
+
let zodType = typeMap[type] || 'z.any()';
|
|
188
|
+
if (isList) {
|
|
189
|
+
zodType = `z.array(${zodType})`;
|
|
190
|
+
}
|
|
191
|
+
if (isOptional) {
|
|
192
|
+
zodType = `${zodType}.optional().nullable()`;
|
|
193
|
+
}
|
|
194
|
+
return zodType;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
exports.PrismaSchemaParser = PrismaSchemaParser;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.TemplateHandler = void 0;
|
|
7
|
+
const handlebars_1 = __importDefault(require("handlebars"));
|
|
8
|
+
const fs_1 = require("fs");
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
class TemplateHandler {
|
|
11
|
+
static render(templateName, data, customPath) {
|
|
12
|
+
if (!templateName.endsWith('.hbs')) {
|
|
13
|
+
templateName = `${templateName}.hbs`;
|
|
14
|
+
}
|
|
15
|
+
if (customPath) {
|
|
16
|
+
this.templatesPath = path_1.default.join(__dirname, '..', customPath);
|
|
17
|
+
}
|
|
18
|
+
const templatePath = path_1.default.join(this.templatesPath, templateName);
|
|
19
|
+
const templateContent = (0, fs_1.readFileSync)(templatePath, 'utf-8');
|
|
20
|
+
const template = handlebars_1.default.compile(templateContent);
|
|
21
|
+
const result = template(data);
|
|
22
|
+
// If template is for .env file, add an extra line before appending
|
|
23
|
+
if (templateName === '.env' || templateName.endsWith('env')) {
|
|
24
|
+
return '\n' + result;
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
static registerHelper(name, fn) {
|
|
29
|
+
handlebars_1.default.registerHelper(name, fn);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.TemplateHandler = TemplateHandler;
|
|
33
|
+
TemplateHandler.templatesPath = path_1.default.join(__dirname, '..', 'templates');
|
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@igniter-js/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Igniter CLI for projects using Igniter Framework",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"homepage": "https://felipebarcelospro.github.io/igniter",
|
|
7
|
+
"bin": {
|
|
8
|
+
"igniter": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/felipebarcelospro/igniter-cli.git"
|
|
13
|
+
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/felipebarcelospro/igniter-cli/issues"
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc && mkdir -p dist/templates && cp -r src/templates/* dist/templates",
|
|
19
|
+
"dev": "ts-node src/index.ts",
|
|
20
|
+
"lint": "eslint src --ext .ts",
|
|
21
|
+
"lint:fix": "eslint src --ext .ts --fix",
|
|
22
|
+
"format": "prettier --write \"src/**/*.{ts,hbs}\"",
|
|
23
|
+
"prepublishOnly": "bun run build",
|
|
24
|
+
"test": "echo \"No tests specified\" && exit 0",
|
|
25
|
+
"release": "npm publish --access public"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"igniter",
|
|
29
|
+
"igniter-js",
|
|
30
|
+
"next.js",
|
|
31
|
+
"cli",
|
|
32
|
+
"generator",
|
|
33
|
+
"feature-first",
|
|
34
|
+
"typescript",
|
|
35
|
+
"scaffolding",
|
|
36
|
+
"boilerplate",
|
|
37
|
+
"code-generation"
|
|
38
|
+
],
|
|
39
|
+
"author": "Felipe Barcelos <felipe.barcelos@nubler.io>",
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"chalk": "^4.1.2",
|
|
43
|
+
"commander": "^11.1.0",
|
|
44
|
+
"handlebars": "^4.7.8",
|
|
45
|
+
"inquirer": "^8.2.6",
|
|
46
|
+
"ora": "^5.4.1",
|
|
47
|
+
"reflect-metadata": "^0.2.2",
|
|
48
|
+
"zod": "^3.23.8"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/inquirer": "^8.2.10",
|
|
52
|
+
"@types/node": "^20.10.2",
|
|
53
|
+
"@typescript-eslint/eslint-plugin": "^6.13.1",
|
|
54
|
+
"@typescript-eslint/parser": "^6.13.1",
|
|
55
|
+
"eslint": "^8.55.0",
|
|
56
|
+
"prettier": "^3.1.0",
|
|
57
|
+
"ts-node": "^10.9.1",
|
|
58
|
+
"typescript": "^5.3.2"
|
|
59
|
+
},
|
|
60
|
+
"engines": {
|
|
61
|
+
"node": ">=16"
|
|
62
|
+
},
|
|
63
|
+
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e",
|
|
64
|
+
"types": "./dist/index.d.ts",
|
|
65
|
+
"files": [
|
|
66
|
+
"dist",
|
|
67
|
+
"LICENSE",
|
|
68
|
+
"README.md"
|
|
69
|
+
],
|
|
70
|
+
"publishConfig": {
|
|
71
|
+
"access": "public"
|
|
72
|
+
}
|
|
73
|
+
}
|