@promptui-lib/codegen 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.
@@ -0,0 +1,116 @@
1
+ /**
2
+ * SCSS Generator
3
+ * Gera código SCSS com metodologia BEM a partir do AST
4
+ */
5
+ /**
6
+ * Gera uma propriedade CSS
7
+ */
8
+ function generateProperty(prop, indent = 2) {
9
+ const spaces = ' '.repeat(indent);
10
+ const comment = prop.comment ? ` // ${prop.comment}` : '';
11
+ return `${spaces}${prop.property}: ${prop.value};${comment}`;
12
+ }
13
+ /**
14
+ * Gera um bloco de estilo
15
+ */
16
+ function generateStyleBlock(block, indent = 0) {
17
+ const spaces = ' '.repeat(indent);
18
+ const lines = [];
19
+ lines.push(`${spaces}${block.selector} {`);
20
+ for (const prop of block.properties) {
21
+ lines.push(generateProperty(prop, indent + 2));
22
+ }
23
+ // Blocos aninhados
24
+ if (block.nested) {
25
+ lines.push('');
26
+ for (const nested of block.nested) {
27
+ lines.push(generateStyleBlock(nested, indent + 2));
28
+ }
29
+ }
30
+ lines.push(`${spaces}}`);
31
+ return lines.join('\n');
32
+ }
33
+ /**
34
+ * Agrupa estilos por bloco BEM
35
+ */
36
+ function groupStylesByBlock(styles, blockName) {
37
+ const rootSelector = `.${blockName}`;
38
+ const root = styles.find((s) => s.selector === rootSelector) ?? null;
39
+ const elements = styles.filter((s) => s.selector.includes('__') && !s.selector.includes('--'));
40
+ const modifiers = styles.filter((s) => s.selector.includes('--'));
41
+ return { root, elements, modifiers };
42
+ }
43
+ /**
44
+ * Gera SCSS com estrutura BEM aninhada
45
+ */
46
+ function generateBEMStructure(styles, blockName) {
47
+ const { root, elements, modifiers } = groupStylesByBlock(styles, blockName);
48
+ const lines = [];
49
+ // Bloco principal
50
+ lines.push(`.${blockName} {`);
51
+ // Propriedades do root
52
+ if (root) {
53
+ for (const prop of root.properties) {
54
+ lines.push(generateProperty(prop));
55
+ }
56
+ }
57
+ // Elementos (__element)
58
+ if (elements.length > 0) {
59
+ lines.push('');
60
+ lines.push(' // Elements');
61
+ for (const element of elements) {
62
+ const elementName = element.selector.replace(`.${blockName}__`, '');
63
+ lines.push('');
64
+ lines.push(` &__${elementName} {`);
65
+ for (const prop of element.properties) {
66
+ lines.push(generateProperty(prop, 4));
67
+ }
68
+ lines.push(' }');
69
+ }
70
+ }
71
+ // Modificadores (--modifier)
72
+ if (modifiers.length > 0) {
73
+ lines.push('');
74
+ lines.push(' // Modifiers');
75
+ for (const modifier of modifiers) {
76
+ const modifierName = modifier.selector.split('--')[1];
77
+ lines.push('');
78
+ lines.push(` &--${modifierName} {`);
79
+ for (const prop of modifier.properties) {
80
+ lines.push(generateProperty(prop, 4));
81
+ }
82
+ lines.push(' }');
83
+ }
84
+ }
85
+ lines.push('}');
86
+ return lines.join('\n');
87
+ }
88
+ /**
89
+ * Gera código SCSS completo
90
+ */
91
+ export function generateSCSS(ast) {
92
+ const lines = [];
93
+ // Import de variáveis
94
+ lines.push("@use '@styles/variables' as *;");
95
+ lines.push('');
96
+ // Gera estrutura BEM
97
+ const blockName = ast.fileName;
98
+ const bemCode = generateBEMStructure(ast.styles, blockName);
99
+ lines.push(bemCode);
100
+ return lines.join('\n');
101
+ }
102
+ /**
103
+ * Gera SCSS simples (não aninhado)
104
+ */
105
+ export function generateFlatSCSS(ast) {
106
+ const lines = [];
107
+ // Import de variáveis
108
+ lines.push("@use '@styles/variables' as *;");
109
+ lines.push('');
110
+ // Blocos individuais
111
+ for (const block of ast.styles) {
112
+ lines.push(generateStyleBlock(block));
113
+ lines.push('');
114
+ }
115
+ return lines.join('\n').trim();
116
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * TSX Generator
3
+ * Gera código TypeScript/JSX a partir do AST do componente
4
+ */
5
+ import type { IComponentAST } from '@promptui-lib/core';
6
+ /**
7
+ * Gera código TSX completo do componente
8
+ */
9
+ export declare function generateTSX(ast: IComponentAST): string;
10
+ /**
11
+ * Gera código do index.ts (barrel export)
12
+ */
13
+ export declare function generateIndex(ast: IComponentAST): string;
14
+ //# sourceMappingURL=tsx-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tsx-generator.d.ts","sourceRoot":"","sources":["../../src/generators/tsx-generator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,aAAa,EAMd,MAAM,oBAAoB,CAAC;AAkI5B;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CA4BtD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAUxD"}
@@ -0,0 +1,152 @@
1
+ /**
2
+ * TSX Generator
3
+ * Gera código TypeScript/JSX a partir do AST do componente
4
+ */
5
+ /**
6
+ * Gera string de imports
7
+ */
8
+ function generateImports(imports) {
9
+ const lines = [];
10
+ for (const imp of imports) {
11
+ if (imp.typeOnly && imp.named && imp.named.length > 0) {
12
+ lines.push(`import type { ${imp.named.join(', ')} } from '${imp.from}';`);
13
+ }
14
+ else if (imp.named && imp.named.length > 0) {
15
+ lines.push(`import { ${imp.named.join(', ')} } from '${imp.from}';`);
16
+ }
17
+ else if (imp.default) {
18
+ lines.push(`import ${imp.default} from '${imp.from}';`);
19
+ }
20
+ else if (imp.from.endsWith('.scss') || imp.from.endsWith('.css')) {
21
+ lines.push(`import '${imp.from}';`);
22
+ }
23
+ }
24
+ return lines.join('\n');
25
+ }
26
+ /**
27
+ * Gera interface de props
28
+ */
29
+ function generatePropsInterface(componentName, props) {
30
+ if (props.length === 0) {
31
+ return '';
32
+ }
33
+ const lines = [];
34
+ lines.push(`export interface I${componentName}Props {`);
35
+ for (const prop of props) {
36
+ const optional = prop.required ? '' : '?';
37
+ const comment = prop.description ? ` /** ${prop.description} */\n` : '';
38
+ lines.push(`${comment} ${prop.name}${optional}: ${prop.type};`);
39
+ }
40
+ lines.push('}');
41
+ return lines.join('\n');
42
+ }
43
+ /**
44
+ * Gera parâmetros de destructuring das props
45
+ */
46
+ function generatePropsDestructuring(props) {
47
+ if (props.length === 0) {
48
+ return '';
49
+ }
50
+ const parts = props.map((prop) => {
51
+ if (prop.defaultValue !== undefined) {
52
+ return `${prop.name} = ${prop.defaultValue}`;
53
+ }
54
+ return prop.name;
55
+ });
56
+ return `{\n ${parts.join(',\n ')},\n}`;
57
+ }
58
+ /**
59
+ * Gera JSX de um node
60
+ */
61
+ function generateJSX(node, indent = 2) {
62
+ const spaces = ' '.repeat(indent);
63
+ const childSpaces = ' '.repeat(indent + 2);
64
+ // Tag de abertura
65
+ let jsx = `${spaces}<${node.tag}`;
66
+ // ClassName
67
+ if (node.className) {
68
+ jsx += ` className="${node.className}"`;
69
+ }
70
+ // Outras props
71
+ for (const [key, value] of Object.entries(node.props)) {
72
+ if (typeof value === 'boolean') {
73
+ jsx += value ? ` ${key}` : '';
74
+ }
75
+ else if (typeof value === 'object' && value.type === 'expression') {
76
+ jsx += ` ${key}={${value.value}}`;
77
+ }
78
+ else {
79
+ jsx += ` ${key}="${value}"`;
80
+ }
81
+ }
82
+ // Self-closing
83
+ if (node.selfClosing) {
84
+ jsx += ' />';
85
+ return jsx;
86
+ }
87
+ jsx += '>';
88
+ // Filhos
89
+ if (node.children.length > 0) {
90
+ const childrenJSX = [];
91
+ for (const child of node.children) {
92
+ if ('type' in child && child.type === 'text') {
93
+ const text = child.value;
94
+ // Se é o único filho, não precisa de nova linha
95
+ if (node.children.length === 1) {
96
+ jsx += text;
97
+ jsx += `</${node.tag}>`;
98
+ return jsx;
99
+ }
100
+ childrenJSX.push(`${childSpaces}{/* ${text} */}`);
101
+ childrenJSX.push(`${childSpaces}{children}`);
102
+ }
103
+ else if ('type' in child && child.type === 'expression') {
104
+ childrenJSX.push(`${childSpaces}{${child.value}}`);
105
+ }
106
+ else {
107
+ childrenJSX.push(generateJSX(child, indent + 2));
108
+ }
109
+ }
110
+ jsx += '\n' + childrenJSX.join('\n') + '\n' + spaces;
111
+ }
112
+ jsx += `</${node.tag}>`;
113
+ return jsx;
114
+ }
115
+ /**
116
+ * Gera código TSX completo do componente
117
+ */
118
+ export function generateTSX(ast) {
119
+ const lines = [];
120
+ // Imports
121
+ const importsCode = generateImports(ast.imports ?? []);
122
+ if (importsCode) {
123
+ lines.push(importsCode);
124
+ lines.push('');
125
+ }
126
+ // Interface de props
127
+ const propsInterface = generatePropsInterface(ast.name, ast.props);
128
+ if (propsInterface) {
129
+ lines.push(propsInterface);
130
+ lines.push('');
131
+ }
132
+ // Componente
133
+ const propsType = ast.props.length > 0 ? `I${ast.name}Props` : '';
134
+ const propsDestructuring = generatePropsDestructuring(ast.props);
135
+ lines.push(`export const ${ast.name} = (${propsDestructuring ? `${propsDestructuring}: ${propsType}` : ''}) => {`);
136
+ lines.push(' return (');
137
+ lines.push(generateJSX(ast.jsx));
138
+ lines.push(' );');
139
+ lines.push('};');
140
+ return lines.join('\n');
141
+ }
142
+ /**
143
+ * Gera código do index.ts (barrel export)
144
+ */
145
+ export function generateIndex(ast) {
146
+ const lines = [];
147
+ lines.push(`export { ${ast.name} } from './${ast.fileName}';`);
148
+ if (ast.props.length > 0) {
149
+ lines.push(`export type { I${ast.name}Props } from './${ast.fileName}';`);
150
+ }
151
+ return lines.join('\n');
152
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @promptui-lib/codegen
3
+ * Code generator for PromptUI - generates React TSX and SCSS
4
+ */
5
+ export * from './generators/index.js';
6
+ export * from './writers/index.js';
7
+ export * from './frameworks/index.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @promptui-lib/codegen
3
+ * Code generator for PromptUI - generates React TSX and SCSS
4
+ */
5
+ export * from './generators/index.js';
6
+ export * from './writers/index.js';
7
+ export * from './frameworks/index.js';
@@ -0,0 +1,35 @@
1
+ /**
2
+ * File Writer
3
+ * Escreve arquivos gerados no sistema de arquivos
4
+ */
5
+ import type { IComponentAST, IGeneratedCode, ComponentLayer } from '@promptui-lib/core';
6
+ export interface IWriteOptions {
7
+ basePath: string;
8
+ forceOverwrite?: boolean;
9
+ }
10
+ export interface IWriteResult {
11
+ success: boolean;
12
+ files: {
13
+ tsx: string;
14
+ scss: string;
15
+ index: string;
16
+ };
17
+ errors?: string[];
18
+ }
19
+ /**
20
+ * Gera o código do componente
21
+ */
22
+ export declare function generateCode(ast: IComponentAST): IGeneratedCode;
23
+ /**
24
+ * Calcula o caminho de output baseado na camada
25
+ */
26
+ export declare function getOutputPath(basePath: string, layer: ComponentLayer, fileName: string): string;
27
+ /**
28
+ * Escreve os arquivos do componente
29
+ */
30
+ export declare function writeComponent(ast: IComponentAST, options: IWriteOptions): Promise<IWriteResult>;
31
+ /**
32
+ * Escreve múltiplos componentes
33
+ */
34
+ export declare function writeComponents(asts: IComponentAST[], options: IWriteOptions): Promise<IWriteResult[]>;
35
+ //# sourceMappingURL=file-writer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-writer.d.ts","sourceRoot":"","sources":["../../src/writers/file-writer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAIxF,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE;QACL,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAqBD;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,aAAa,GAAG,cAAc,CAM/D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,cAAc,EACrB,QAAQ,EAAE,MAAM,GACf,MAAM,CAER;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,YAAY,CAAC,CAmDvB;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,aAAa,EAAE,EACrB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,YAAY,EAAE,CAAC,CASzB"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * File Writer
3
+ * Escreve arquivos gerados no sistema de arquivos
4
+ */
5
+ import { mkdir, writeFile, access } from 'node:fs/promises';
6
+ import { join } from 'node:path';
7
+ import { generateTSX, generateIndex } from '../generators/tsx-generator.js';
8
+ import { generateSCSS } from '../generators/scss-generator.js';
9
+ /**
10
+ * Verifica se um arquivo existe
11
+ */
12
+ async function fileExists(path) {
13
+ try {
14
+ await access(path);
15
+ return true;
16
+ }
17
+ catch {
18
+ return false;
19
+ }
20
+ }
21
+ /**
22
+ * Garante que o diretório existe
23
+ */
24
+ async function ensureDir(path) {
25
+ await mkdir(path, { recursive: true });
26
+ }
27
+ /**
28
+ * Gera o código do componente
29
+ */
30
+ export function generateCode(ast) {
31
+ return {
32
+ tsx: generateTSX(ast),
33
+ scss: generateSCSS(ast),
34
+ index: generateIndex(ast),
35
+ };
36
+ }
37
+ /**
38
+ * Calcula o caminho de output baseado na camada
39
+ */
40
+ export function getOutputPath(basePath, layer, fileName) {
41
+ return join(basePath, layer, fileName);
42
+ }
43
+ /**
44
+ * Escreve os arquivos do componente
45
+ */
46
+ export async function writeComponent(ast, options) {
47
+ const errors = [];
48
+ // Gera código
49
+ const code = generateCode(ast);
50
+ // Calcula caminhos
51
+ const componentDir = getOutputPath(options.basePath, ast.layer, ast.fileName);
52
+ const tsxPath = join(componentDir, `${ast.fileName}.tsx`);
53
+ const scssPath = join(componentDir, `${ast.fileName}.scss`);
54
+ const indexPath = join(componentDir, 'index.ts');
55
+ // Verifica se já existe
56
+ if (!options.forceOverwrite) {
57
+ const exists = await fileExists(tsxPath);
58
+ if (exists) {
59
+ return {
60
+ success: false,
61
+ files: { tsx: tsxPath, scss: scssPath, index: indexPath },
62
+ errors: [`Component already exists at ${componentDir}. Use forceOverwrite to replace.`],
63
+ };
64
+ }
65
+ }
66
+ try {
67
+ // Cria diretório
68
+ await ensureDir(componentDir);
69
+ // Escreve arquivos
70
+ await writeFile(tsxPath, code.tsx, 'utf-8');
71
+ await writeFile(scssPath, code.scss, 'utf-8');
72
+ await writeFile(indexPath, code.index, 'utf-8');
73
+ return {
74
+ success: true,
75
+ files: {
76
+ tsx: tsxPath,
77
+ scss: scssPath,
78
+ index: indexPath,
79
+ },
80
+ };
81
+ }
82
+ catch (error) {
83
+ const message = error instanceof Error ? error.message : 'Unknown error';
84
+ errors.push(`Failed to write files: ${message}`);
85
+ return {
86
+ success: false,
87
+ files: { tsx: tsxPath, scss: scssPath, index: indexPath },
88
+ errors,
89
+ };
90
+ }
91
+ }
92
+ /**
93
+ * Escreve múltiplos componentes
94
+ */
95
+ export async function writeComponents(asts, options) {
96
+ const results = [];
97
+ for (const ast of asts) {
98
+ const result = await writeComponent(ast, options);
99
+ results.push(result);
100
+ }
101
+ return results;
102
+ }
@@ -0,0 +1,3 @@
1
+ export { writeComponent, writeComponents, generateCode, getOutputPath, } from './file-writer.js';
2
+ export type { IWriteOptions, IWriteResult } from './file-writer.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/writers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,eAAe,EACf,YAAY,EACZ,aAAa,GACd,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1 @@
1
+ export { writeComponent, writeComponents, generateCode, getOutputPath, } from './file-writer.js';
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@promptui-lib/codegen",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "description": "Code generator for PromptUI - generates React TSX and SCSS",
6
+ "license": "UNLICENSED",
7
+ "author": {
8
+ "name": "Desiree Menezes",
9
+ "url": "https://github.com/desireemenezes"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/desireemenezes/promptUI.git",
14
+ "directory": "packages/codegen"
15
+ },
16
+ "homepage": "https://github.com/desireemenezes/promptUI#readme",
17
+ "type": "module",
18
+ "main": "./dist/index.js",
19
+ "types": "./dist/index.d.ts",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "import": "./dist/index.js"
24
+ }
25
+ },
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "files": [
30
+ "dist"
31
+ ],
32
+ "dependencies": {
33
+ "@promptui-lib/core": "0.1.0"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "^20.0.0",
37
+ "typescript": "^5.3.0",
38
+ "rimraf": "^5.0.0"
39
+ },
40
+ "scripts": {
41
+ "build": "tsc",
42
+ "dev": "tsc --watch",
43
+ "clean": "rimraf dist"
44
+ }
45
+ }