@claudetools/tools 0.8.11 → 0.9.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/codedna/generators/astro.d.ts +18 -0
- package/dist/codedna/generators/astro.js +91 -0
- package/dist/codedna/generators/authjs.d.ts +18 -0
- package/dist/codedna/generators/authjs.js +68 -0
- package/dist/codedna/generators/better-auth.d.ts +18 -0
- package/dist/codedna/generators/better-auth.js +62 -0
- package/dist/codedna/generators/drizzle-orm.d.ts +18 -0
- package/dist/codedna/generators/drizzle-orm.js +65 -0
- package/dist/codedna/generators/elysia-api.d.ts +12 -0
- package/dist/codedna/generators/elysia-api.js +64 -0
- package/dist/codedna/generators/hono-api.d.ts +12 -0
- package/dist/codedna/generators/hono-api.js +64 -0
- package/dist/codedna/generators/lucia-auth.d.ts +18 -0
- package/dist/codedna/generators/lucia-auth.js +69 -0
- package/dist/codedna/generators/prisma.d.ts +18 -0
- package/dist/codedna/generators/prisma.js +64 -0
- package/dist/codedna/generators/react-router-v7.d.ts +18 -0
- package/dist/codedna/generators/react-router-v7.js +77 -0
- package/dist/codedna/generators/react19-shadcn.d.ts +21 -0
- package/dist/codedna/generators/react19-shadcn.js +367 -0
- package/dist/codedna/generators/sveltekit.d.ts +18 -0
- package/dist/codedna/generators/sveltekit.js +73 -0
- package/dist/codedna/generators/tanstack-start-drizzle.d.ts +92 -0
- package/dist/codedna/generators/tanstack-start-drizzle.js +824 -0
- package/dist/codedna/generators/trpc-api.d.ts +12 -0
- package/dist/codedna/generators/trpc-api.js +64 -0
- package/dist/codedna/index.d.ts +31 -0
- package/dist/codedna/index.js +39 -0
- package/dist/codedna/kappa-api-generator.d.ts +89 -0
- package/dist/codedna/kappa-api-generator.js +493 -0
- package/dist/codedna/kappa-ast.d.ts +552 -0
- package/dist/codedna/kappa-ast.js +141 -0
- package/dist/codedna/kappa-cli.d.ts +2 -0
- package/dist/codedna/kappa-cli.js +302 -0
- package/dist/codedna/kappa-component-generator.d.ts +47 -0
- package/dist/codedna/kappa-component-generator.js +295 -0
- package/dist/codedna/kappa-design-generator.d.ts +52 -0
- package/dist/codedna/kappa-design-generator.js +365 -0
- package/dist/codedna/kappa-drizzle-generator.d.ts +45 -0
- package/dist/codedna/kappa-drizzle-generator.js +355 -0
- package/dist/codedna/kappa-form-generator.d.ts +51 -0
- package/dist/codedna/kappa-form-generator.js +319 -0
- package/dist/codedna/kappa-lexer.d.ts +268 -0
- package/dist/codedna/kappa-lexer.js +757 -0
- package/dist/codedna/kappa-page-generator.d.ts +57 -0
- package/dist/codedna/kappa-page-generator.js +338 -0
- package/dist/codedna/kappa-parser.d.ts +261 -0
- package/dist/codedna/kappa-parser.js +2547 -0
- package/dist/codedna/kappa-provenance.d.ts +101 -0
- package/dist/codedna/kappa-provenance.js +199 -0
- package/dist/codedna/kappa-types-generator.d.ts +37 -0
- package/dist/codedna/kappa-types-generator.js +159 -0
- package/dist/codedna/kappa-validator.d.ts +86 -0
- package/dist/codedna/kappa-validator.js +638 -0
- package/dist/codedna/kappa-zod-generator.d.ts +32 -0
- package/dist/codedna/kappa-zod-generator.js +216 -0
- package/dist/handlers/kappa-handlers.d.ts +116 -0
- package/dist/handlers/kappa-handlers.js +465 -0
- package/dist/handlers/tool-handlers.js +121 -0
- package/dist/templates/claude-md.d.ts +1 -1
- package/dist/templates/claude-md.js +166 -9
- package/dist/tools.js +199 -0
- package/docs/research/2026-01-02-codedna-il-specification.md +639 -0
- package/docs/research/2026-01-02-codedna-v2-research.md +943 -0
- package/docs/research/2026-01-02-computation-foundations.md +564 -0
- package/docs/research/2026-01-02-hardware-description.md +814 -0
- package/docs/research/2026-01-02-kappa-specification.md +697 -0
- package/docs/research/2026-01-02-kappa-tanstack-example.md +527 -0
- package/docs/research/2026-01-02-kappa-v2-synthesis.md +406 -0
- package/docs/research/2026-01-02-kappa-v2.5-specification.md +1218 -0
- package/docs/research/2026-01-02-kappa-v3-specification.md +1864 -0
- package/docs/research/2026-01-02-kappa-whitepaper.md +662 -0
- package/docs/research/2026-01-02-logic-constraint.md +731 -0
- package/docs/research/2026-01-02-quantum-computation.md +635 -0
- package/package.json +4 -2
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// =============================================================================
|
|
3
|
+
// Kappa v2.5 CLI
|
|
4
|
+
// =============================================================================
|
|
5
|
+
//
|
|
6
|
+
// Command-line interface for the Kappa DSL.
|
|
7
|
+
// Parse, validate, and generate code from Kappa specifications.
|
|
8
|
+
//
|
|
9
|
+
import { parseArgs } from 'node:util';
|
|
10
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, watch } from 'node:fs';
|
|
11
|
+
import { join, basename, extname } from 'node:path';
|
|
12
|
+
import { parseToAST } from './kappa-parser.js';
|
|
13
|
+
import { validateKappaSpec, formatValidationResults } from './kappa-validator.js';
|
|
14
|
+
import { generateDrizzleSchema } from './kappa-drizzle-generator.js';
|
|
15
|
+
import { generateZodSchemas } from './kappa-zod-generator.js';
|
|
16
|
+
import { generateTypeScriptTypes } from './kappa-types-generator.js';
|
|
17
|
+
import { generateAPI } from './kappa-api-generator.js';
|
|
18
|
+
// =============================================================================
|
|
19
|
+
// Version
|
|
20
|
+
// =============================================================================
|
|
21
|
+
const VERSION = '2.5.0';
|
|
22
|
+
// =============================================================================
|
|
23
|
+
// Help Text
|
|
24
|
+
// =============================================================================
|
|
25
|
+
const HELP_TEXT = `
|
|
26
|
+
Kappa v${VERSION} - Declarative Application Specification Language
|
|
27
|
+
|
|
28
|
+
Usage:
|
|
29
|
+
kappa [command] [options] <file.kappa>
|
|
30
|
+
|
|
31
|
+
Commands:
|
|
32
|
+
parse Parse and display the AST
|
|
33
|
+
validate Validate the specification
|
|
34
|
+
generate Generate code from the specification (default)
|
|
35
|
+
|
|
36
|
+
Options:
|
|
37
|
+
-h, --help Show this help message
|
|
38
|
+
-v, --version Show version number
|
|
39
|
+
-o, --output Output directory (default: ./generated)
|
|
40
|
+
-w, --watch Watch for changes and regenerate
|
|
41
|
+
-q, --quiet Suppress non-error output
|
|
42
|
+
--validate Validate before generating (default: true)
|
|
43
|
+
--generate What to generate: drizzle,zod,types,api (default: all)
|
|
44
|
+
--framework API framework: hono,express,trpc (default: hono)
|
|
45
|
+
--dialect Database dialect: postgresql,sqlite,mysql (default: postgresql)
|
|
46
|
+
|
|
47
|
+
Examples:
|
|
48
|
+
kappa app.kappa # Generate all code
|
|
49
|
+
kappa validate app.kappa # Validate only
|
|
50
|
+
kappa parse app.kappa # Show AST
|
|
51
|
+
kappa -o src/db app.kappa # Output to src/db
|
|
52
|
+
kappa --generate drizzle,zod app.kappa # Generate specific outputs
|
|
53
|
+
kappa -w app.kappa # Watch mode
|
|
54
|
+
|
|
55
|
+
`;
|
|
56
|
+
// =============================================================================
|
|
57
|
+
// CLI Parsing
|
|
58
|
+
// =============================================================================
|
|
59
|
+
function parseCliArgs() {
|
|
60
|
+
const { values, positionals } = parseArgs({
|
|
61
|
+
options: {
|
|
62
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
63
|
+
version: { type: 'boolean', short: 'v', default: false },
|
|
64
|
+
output: { type: 'string', short: 'o', default: './generated' },
|
|
65
|
+
watch: { type: 'boolean', short: 'w', default: false },
|
|
66
|
+
validate: { type: 'boolean', default: true },
|
|
67
|
+
generate: { type: 'string', multiple: true, default: [] },
|
|
68
|
+
framework: { type: 'string', default: 'hono' },
|
|
69
|
+
dialect: { type: 'string', default: 'postgresql' },
|
|
70
|
+
quiet: { type: 'boolean', short: 'q', default: false },
|
|
71
|
+
},
|
|
72
|
+
allowPositionals: true,
|
|
73
|
+
strict: false,
|
|
74
|
+
});
|
|
75
|
+
// Determine command and file
|
|
76
|
+
let command = 'generate';
|
|
77
|
+
let file = '';
|
|
78
|
+
if (positionals.length === 1) {
|
|
79
|
+
// Just a file - default to generate
|
|
80
|
+
file = positionals[0];
|
|
81
|
+
}
|
|
82
|
+
else if (positionals.length >= 2) {
|
|
83
|
+
// Command and file
|
|
84
|
+
command = positionals[0];
|
|
85
|
+
file = positionals[1];
|
|
86
|
+
}
|
|
87
|
+
// Parse generate option (can be comma-separated)
|
|
88
|
+
let generateList = [];
|
|
89
|
+
const generateOpt = values.generate;
|
|
90
|
+
if (generateOpt && generateOpt.length > 0) {
|
|
91
|
+
for (const g of generateOpt) {
|
|
92
|
+
generateList.push(...g.split(','));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (generateList.length === 0) {
|
|
96
|
+
generateList = ['drizzle', 'zod', 'types', 'api'];
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
options: {
|
|
100
|
+
help: values.help ?? false,
|
|
101
|
+
version: values.version ?? false,
|
|
102
|
+
output: values.output ?? './generated',
|
|
103
|
+
watch: values.watch ?? false,
|
|
104
|
+
validate: values.validate ?? true,
|
|
105
|
+
generate: generateList,
|
|
106
|
+
framework: values.framework ?? 'hono',
|
|
107
|
+
dialect: values.dialect ?? 'postgresql',
|
|
108
|
+
quiet: values.quiet ?? false,
|
|
109
|
+
},
|
|
110
|
+
command,
|
|
111
|
+
file,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
// =============================================================================
|
|
115
|
+
// Commands
|
|
116
|
+
// =============================================================================
|
|
117
|
+
function log(options, message) {
|
|
118
|
+
if (!options.quiet) {
|
|
119
|
+
console.log(message);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function parseCommand(file, options) {
|
|
123
|
+
const content = readFileSync(file, 'utf-8');
|
|
124
|
+
const ast = parseToAST(content);
|
|
125
|
+
if (!ast) {
|
|
126
|
+
console.error(`Error: Failed to parse ${file}`);
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
log(options, JSON.stringify(ast, null, 2));
|
|
130
|
+
return ast;
|
|
131
|
+
}
|
|
132
|
+
function validateCommand(file, options) {
|
|
133
|
+
const content = readFileSync(file, 'utf-8');
|
|
134
|
+
const ast = parseToAST(content);
|
|
135
|
+
if (!ast) {
|
|
136
|
+
console.error(`Error: Failed to parse ${file}`);
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
const result = validateKappaSpec(ast);
|
|
140
|
+
const formatted = formatValidationResults(result);
|
|
141
|
+
if (result.valid) {
|
|
142
|
+
log(options, `✓ ${file} is valid`);
|
|
143
|
+
if (result.warnings.length > 0) {
|
|
144
|
+
console.log(formatted);
|
|
145
|
+
}
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
console.error(`✗ ${file} has errors:`);
|
|
150
|
+
console.error(formatted);
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function generateCommand(file, options) {
|
|
155
|
+
const content = readFileSync(file, 'utf-8');
|
|
156
|
+
const ast = parseToAST(content);
|
|
157
|
+
if (!ast) {
|
|
158
|
+
console.error(`Error: Failed to parse ${file}`);
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
// Validate first if requested
|
|
162
|
+
if (options.validate) {
|
|
163
|
+
const result = validateKappaSpec(ast);
|
|
164
|
+
if (!result.valid) {
|
|
165
|
+
console.error(`✗ Validation failed for ${file}:`);
|
|
166
|
+
console.error(formatValidationResults(result));
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// Ensure output directory exists
|
|
171
|
+
if (!existsSync(options.output)) {
|
|
172
|
+
mkdirSync(options.output, { recursive: true });
|
|
173
|
+
}
|
|
174
|
+
const specName = basename(file, extname(file));
|
|
175
|
+
let filesWritten = 0;
|
|
176
|
+
// Generate Drizzle schema
|
|
177
|
+
if (options.generate.includes('drizzle') && ast.entities.length > 0) {
|
|
178
|
+
const drizzle = generateDrizzleSchema(ast.entities, {
|
|
179
|
+
dialect: options.dialect,
|
|
180
|
+
provenance: true,
|
|
181
|
+
});
|
|
182
|
+
writeFileSync(join(options.output, `${specName}.schema.ts`), drizzle.schema);
|
|
183
|
+
filesWritten++;
|
|
184
|
+
if (drizzle.types) {
|
|
185
|
+
writeFileSync(join(options.output, `${specName}.schema.types.ts`), drizzle.types);
|
|
186
|
+
filesWritten++;
|
|
187
|
+
}
|
|
188
|
+
if (drizzle.relations) {
|
|
189
|
+
writeFileSync(join(options.output, `${specName}.relations.ts`), drizzle.relations);
|
|
190
|
+
filesWritten++;
|
|
191
|
+
}
|
|
192
|
+
log(options, ` ✓ Drizzle schema`);
|
|
193
|
+
}
|
|
194
|
+
// Generate Zod schemas
|
|
195
|
+
if (options.generate.includes('zod') && ast.entities.length > 0) {
|
|
196
|
+
const zod = generateZodSchemas(ast.entities, { provenance: true });
|
|
197
|
+
writeFileSync(join(options.output, `${specName}.schemas.ts`), zod.schemas);
|
|
198
|
+
filesWritten++;
|
|
199
|
+
log(options, ` ✓ Zod validation schemas`);
|
|
200
|
+
}
|
|
201
|
+
// Generate TypeScript types
|
|
202
|
+
if (options.generate.includes('types') && ast.entities.length > 0) {
|
|
203
|
+
const types = generateTypeScriptTypes(ast.entities, { provenance: true });
|
|
204
|
+
writeFileSync(join(options.output, `${specName}.types.ts`), types.types);
|
|
205
|
+
filesWritten++;
|
|
206
|
+
log(options, ` ✓ TypeScript types`);
|
|
207
|
+
}
|
|
208
|
+
// Generate API routes
|
|
209
|
+
if (options.generate.includes('api') && ast.apis.length > 0) {
|
|
210
|
+
const api = generateAPI(ast.apis, {
|
|
211
|
+
framework: options.framework,
|
|
212
|
+
provenance: true,
|
|
213
|
+
});
|
|
214
|
+
writeFileSync(join(options.output, `${specName}.routes.ts`), api.routes);
|
|
215
|
+
filesWritten++;
|
|
216
|
+
if (api.validation) {
|
|
217
|
+
writeFileSync(join(options.output, `${specName}.validation.ts`), api.validation);
|
|
218
|
+
filesWritten++;
|
|
219
|
+
}
|
|
220
|
+
if (api.errors) {
|
|
221
|
+
writeFileSync(join(options.output, `${specName}.errors.ts`), api.errors);
|
|
222
|
+
filesWritten++;
|
|
223
|
+
}
|
|
224
|
+
log(options, ` ✓ API routes (${options.framework})`);
|
|
225
|
+
}
|
|
226
|
+
log(options, `\n✓ Generated ${filesWritten} files to ${options.output}/`);
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
function watchMode(file, options) {
|
|
230
|
+
log(options, `Watching ${file} for changes...`);
|
|
231
|
+
log(options, 'Press Ctrl+C to stop.\n');
|
|
232
|
+
// Initial generation
|
|
233
|
+
generateCommand(file, options);
|
|
234
|
+
// Watch for changes
|
|
235
|
+
const watcher = watch(file, (eventType) => {
|
|
236
|
+
if (eventType === 'change') {
|
|
237
|
+
log(options, `\n[${new Date().toLocaleTimeString()}] File changed, regenerating...`);
|
|
238
|
+
generateCommand(file, options);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
// Handle Ctrl+C
|
|
242
|
+
process.on('SIGINT', () => {
|
|
243
|
+
watcher.close();
|
|
244
|
+
log(options, '\nStopped watching.');
|
|
245
|
+
process.exit(0);
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
// =============================================================================
|
|
249
|
+
// Main
|
|
250
|
+
// =============================================================================
|
|
251
|
+
async function main() {
|
|
252
|
+
const { options, command, file } = parseCliArgs();
|
|
253
|
+
// Handle help
|
|
254
|
+
if (options.help) {
|
|
255
|
+
console.log(HELP_TEXT);
|
|
256
|
+
process.exit(0);
|
|
257
|
+
}
|
|
258
|
+
// Handle version
|
|
259
|
+
if (options.version) {
|
|
260
|
+
console.log(`Kappa v${VERSION}`);
|
|
261
|
+
process.exit(0);
|
|
262
|
+
}
|
|
263
|
+
// Require a file
|
|
264
|
+
if (!file) {
|
|
265
|
+
console.error('Error: No input file specified.');
|
|
266
|
+
console.error('Usage: kappa [command] [options] <file.kappa>');
|
|
267
|
+
console.error('Run "kappa --help" for more information.');
|
|
268
|
+
process.exit(1);
|
|
269
|
+
}
|
|
270
|
+
// Check file exists
|
|
271
|
+
if (!existsSync(file)) {
|
|
272
|
+
console.error(`Error: File not found: ${file}`);
|
|
273
|
+
process.exit(1);
|
|
274
|
+
}
|
|
275
|
+
// Execute command
|
|
276
|
+
let success = true;
|
|
277
|
+
switch (command) {
|
|
278
|
+
case 'parse':
|
|
279
|
+
const ast = parseCommand(file, options);
|
|
280
|
+
success = ast !== null;
|
|
281
|
+
break;
|
|
282
|
+
case 'validate':
|
|
283
|
+
success = validateCommand(file, options);
|
|
284
|
+
break;
|
|
285
|
+
case 'generate':
|
|
286
|
+
default:
|
|
287
|
+
if (options.watch) {
|
|
288
|
+
watchMode(file, options);
|
|
289
|
+
return; // Watch mode doesn't exit
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
success = generateCommand(file, options);
|
|
293
|
+
}
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
process.exit(success ? 0 : 1);
|
|
297
|
+
}
|
|
298
|
+
// Run if executed directly
|
|
299
|
+
main().catch((error) => {
|
|
300
|
+
console.error('Fatal error:', error);
|
|
301
|
+
process.exit(1);
|
|
302
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ComponentBlock } from './kappa-ast.js';
|
|
2
|
+
export interface ComponentGeneratorOptions {
|
|
3
|
+
/** Add provenance comments (default: true) */
|
|
4
|
+
provenance?: boolean;
|
|
5
|
+
/** Generate TypeScript (default: true) */
|
|
6
|
+
typescript?: boolean;
|
|
7
|
+
/** Use forwardRef (default: false) */
|
|
8
|
+
forwardRef?: boolean;
|
|
9
|
+
/** Base path for components (default: 'components') */
|
|
10
|
+
basePath?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface GeneratedComponent {
|
|
13
|
+
/** Component file path */
|
|
14
|
+
path: string;
|
|
15
|
+
/** Component content */
|
|
16
|
+
content: string;
|
|
17
|
+
}
|
|
18
|
+
export interface GeneratedComponents {
|
|
19
|
+
/** Generated component files */
|
|
20
|
+
components: GeneratedComponent[];
|
|
21
|
+
}
|
|
22
|
+
export declare class KappaComponentGenerator {
|
|
23
|
+
private provenance;
|
|
24
|
+
private typescript;
|
|
25
|
+
private forwardRef;
|
|
26
|
+
private basePath;
|
|
27
|
+
constructor(options?: ComponentGeneratorOptions);
|
|
28
|
+
/**
|
|
29
|
+
* Generate component files from ComponentBlock AST nodes
|
|
30
|
+
*/
|
|
31
|
+
generate(components: ComponentBlock[]): GeneratedComponents;
|
|
32
|
+
private generateSimpleComponent;
|
|
33
|
+
private generatePropsInterface;
|
|
34
|
+
private generateFunctionComponent;
|
|
35
|
+
private generateForwardRefComponent;
|
|
36
|
+
private getDestructuredProps;
|
|
37
|
+
private generateStructureJSX;
|
|
38
|
+
private generateVariants;
|
|
39
|
+
private generateCompoundComponent;
|
|
40
|
+
private generateCompoundPart;
|
|
41
|
+
private mapPropType;
|
|
42
|
+
private toKebabCase;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Generate component files from Kappa ComponentBlock nodes
|
|
46
|
+
*/
|
|
47
|
+
export declare function generateComponents(components: ComponentBlock[], options?: ComponentGeneratorOptions): GeneratedComponents;
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Kappa v2.5 Component Generator
|
|
3
|
+
// =============================================================================
|
|
4
|
+
//
|
|
5
|
+
// Generates React components from Kappa ComponentBlock AST.
|
|
6
|
+
// Supports compound components, variants, and structured layouts.
|
|
7
|
+
//
|
|
8
|
+
// =============================================================================
|
|
9
|
+
// Generator Class
|
|
10
|
+
// =============================================================================
|
|
11
|
+
export class KappaComponentGenerator {
|
|
12
|
+
provenance;
|
|
13
|
+
typescript;
|
|
14
|
+
forwardRef;
|
|
15
|
+
basePath;
|
|
16
|
+
constructor(options = {}) {
|
|
17
|
+
this.provenance = options.provenance ?? true;
|
|
18
|
+
this.typescript = options.typescript ?? true;
|
|
19
|
+
this.forwardRef = options.forwardRef ?? false;
|
|
20
|
+
this.basePath = options.basePath ?? 'components';
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Generate component files from ComponentBlock AST nodes
|
|
24
|
+
*/
|
|
25
|
+
generate(components) {
|
|
26
|
+
const generatedComponents = [];
|
|
27
|
+
for (const component of components) {
|
|
28
|
+
if (component.compound && component.compound.length > 0) {
|
|
29
|
+
generatedComponents.push(this.generateCompoundComponent(component));
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
generatedComponents.push(this.generateSimpleComponent(component));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return { components: generatedComponents };
|
|
36
|
+
}
|
|
37
|
+
// ===========================================================================
|
|
38
|
+
// Simple Component Generation
|
|
39
|
+
// ===========================================================================
|
|
40
|
+
generateSimpleComponent(component) {
|
|
41
|
+
const lines = [];
|
|
42
|
+
const ext = this.typescript ? 'tsx' : 'jsx';
|
|
43
|
+
// Header
|
|
44
|
+
if (this.provenance) {
|
|
45
|
+
lines.push('// Generated by Kappa v2.5 CodeDNA');
|
|
46
|
+
lines.push(`// Component: ${component.name}`);
|
|
47
|
+
lines.push('');
|
|
48
|
+
}
|
|
49
|
+
// Imports
|
|
50
|
+
if (this.forwardRef) {
|
|
51
|
+
lines.push("import { forwardRef } from 'react';");
|
|
52
|
+
}
|
|
53
|
+
lines.push("import { cn } from '@/lib/utils';");
|
|
54
|
+
lines.push('');
|
|
55
|
+
// Props interface
|
|
56
|
+
if (this.typescript) {
|
|
57
|
+
lines.push(this.generatePropsInterface(component));
|
|
58
|
+
lines.push('');
|
|
59
|
+
}
|
|
60
|
+
// Component
|
|
61
|
+
if (this.forwardRef) {
|
|
62
|
+
lines.push(this.generateForwardRefComponent(component));
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
lines.push(this.generateFunctionComponent(component));
|
|
66
|
+
}
|
|
67
|
+
// Variants
|
|
68
|
+
if (component.variants.length > 0) {
|
|
69
|
+
lines.push('');
|
|
70
|
+
lines.push(this.generateVariants(component));
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
path: `${this.basePath}/${this.toKebabCase(component.name)}.${ext}`,
|
|
74
|
+
content: lines.join('\n'),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
generatePropsInterface(component) {
|
|
78
|
+
const lines = [];
|
|
79
|
+
const propsName = `${component.name}Props`;
|
|
80
|
+
lines.push(`export interface ${propsName} {`);
|
|
81
|
+
for (const prop of component.props) {
|
|
82
|
+
const optional = prop.optional ? '?' : '';
|
|
83
|
+
lines.push(` ${prop.name}${optional}: ${this.mapPropType(prop.type)};`);
|
|
84
|
+
}
|
|
85
|
+
// Add className and children by default
|
|
86
|
+
lines.push(' className?: string;');
|
|
87
|
+
lines.push(' children?: React.ReactNode;');
|
|
88
|
+
lines.push('}');
|
|
89
|
+
return lines.join('\n');
|
|
90
|
+
}
|
|
91
|
+
generateFunctionComponent(component) {
|
|
92
|
+
const lines = [];
|
|
93
|
+
const propsType = this.typescript ? `: ${component.name}Props` : '';
|
|
94
|
+
const destructuredProps = this.getDestructuredProps(component);
|
|
95
|
+
lines.push(`export function ${component.name}({ ${destructuredProps} }${propsType}) {`);
|
|
96
|
+
lines.push(' return (');
|
|
97
|
+
if (component.structure) {
|
|
98
|
+
lines.push(this.generateStructureJSX(component.structure, 4));
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
lines.push(` <div className={cn('${this.toKebabCase(component.name)}', className)}>`);
|
|
102
|
+
lines.push(' {children}');
|
|
103
|
+
lines.push(' </div>');
|
|
104
|
+
}
|
|
105
|
+
lines.push(' );');
|
|
106
|
+
lines.push('}');
|
|
107
|
+
return lines.join('\n');
|
|
108
|
+
}
|
|
109
|
+
generateForwardRefComponent(component) {
|
|
110
|
+
const lines = [];
|
|
111
|
+
const propsType = this.typescript ? `${component.name}Props` : '';
|
|
112
|
+
const refType = this.typescript ? ', HTMLDivElement' : '';
|
|
113
|
+
const destructuredProps = this.getDestructuredProps(component);
|
|
114
|
+
lines.push(`export const ${component.name} = forwardRef<${refType.slice(2)}${propsType ? `, ${propsType}` : ''}>(`);
|
|
115
|
+
lines.push(` function ${component.name}({ ${destructuredProps} }, ref) {`);
|
|
116
|
+
lines.push(' return (');
|
|
117
|
+
if (component.structure) {
|
|
118
|
+
lines.push(this.generateStructureJSX(component.structure, 6, 'ref={ref}'));
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
lines.push(` <div ref={ref} className={cn('${this.toKebabCase(component.name)}', className)}>`);
|
|
122
|
+
lines.push(' {children}');
|
|
123
|
+
lines.push(' </div>');
|
|
124
|
+
}
|
|
125
|
+
lines.push(' );');
|
|
126
|
+
lines.push(' }');
|
|
127
|
+
lines.push(');');
|
|
128
|
+
return lines.join('\n');
|
|
129
|
+
}
|
|
130
|
+
getDestructuredProps(component) {
|
|
131
|
+
const propNames = component.props.map((p) => p.name);
|
|
132
|
+
propNames.push('className', 'children');
|
|
133
|
+
return propNames.join(', ');
|
|
134
|
+
}
|
|
135
|
+
generateStructureJSX(structure, indent, extraProps = '') {
|
|
136
|
+
const spaces = ' '.repeat(indent);
|
|
137
|
+
const lines = [];
|
|
138
|
+
const props = Object.entries(structure.props || {})
|
|
139
|
+
.map(([k, v]) => `${k}="${v}"`)
|
|
140
|
+
.join(' ');
|
|
141
|
+
const allProps = [extraProps, props, 'className={cn(className)}'].filter(Boolean).join(' ');
|
|
142
|
+
if (structure.children && structure.children.length > 0) {
|
|
143
|
+
lines.push(`${spaces}<${structure.type} ${allProps}>`);
|
|
144
|
+
for (const child of structure.children) {
|
|
145
|
+
lines.push(this.generateStructureJSX(child, indent + 2));
|
|
146
|
+
}
|
|
147
|
+
lines.push(`${spaces}</${structure.type}>`);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
lines.push(`${spaces}<${structure.type} ${allProps}>{children}</${structure.type}>`);
|
|
151
|
+
}
|
|
152
|
+
return lines.join('\n');
|
|
153
|
+
}
|
|
154
|
+
generateVariants(component) {
|
|
155
|
+
const lines = [];
|
|
156
|
+
lines.push('// Variants');
|
|
157
|
+
for (const variant of component.variants) {
|
|
158
|
+
const variantName = `${component.name}${variant.name}`;
|
|
159
|
+
lines.push(`export const ${variantName} = ${component.name};`);
|
|
160
|
+
lines.push(`${variantName}.displayName = '${variantName}';`);
|
|
161
|
+
}
|
|
162
|
+
return lines.join('\n');
|
|
163
|
+
}
|
|
164
|
+
// ===========================================================================
|
|
165
|
+
// Compound Component Generation
|
|
166
|
+
// ===========================================================================
|
|
167
|
+
generateCompoundComponent(component) {
|
|
168
|
+
const lines = [];
|
|
169
|
+
const ext = this.typescript ? 'tsx' : 'jsx';
|
|
170
|
+
// Header
|
|
171
|
+
if (this.provenance) {
|
|
172
|
+
lines.push('// Generated by Kappa v2.5 CodeDNA');
|
|
173
|
+
lines.push(`// Compound Component: ${component.name}`);
|
|
174
|
+
lines.push('');
|
|
175
|
+
}
|
|
176
|
+
// Imports
|
|
177
|
+
lines.push("import { createContext, useContext } from 'react';");
|
|
178
|
+
lines.push("import { cn } from '@/lib/utils';");
|
|
179
|
+
lines.push('');
|
|
180
|
+
// Context
|
|
181
|
+
if (this.typescript) {
|
|
182
|
+
lines.push(`interface ${component.name}ContextValue {`);
|
|
183
|
+
for (const prop of component.props) {
|
|
184
|
+
lines.push(` ${prop.name}${prop.optional ? '?' : ''}: ${this.mapPropType(prop.type)};`);
|
|
185
|
+
}
|
|
186
|
+
lines.push('}');
|
|
187
|
+
lines.push('');
|
|
188
|
+
}
|
|
189
|
+
const contextType = this.typescript ? `${component.name}ContextValue | null` : '';
|
|
190
|
+
lines.push(`const ${component.name}Context = createContext<${contextType}>(null);`);
|
|
191
|
+
lines.push('');
|
|
192
|
+
// Hook
|
|
193
|
+
lines.push(`function use${component.name}() {`);
|
|
194
|
+
lines.push(` const context = useContext(${component.name}Context);`);
|
|
195
|
+
lines.push(' if (!context) {');
|
|
196
|
+
lines.push(` throw new Error('${component.name} compound components must be used within ${component.name}');`);
|
|
197
|
+
lines.push(' }');
|
|
198
|
+
lines.push(' return context;');
|
|
199
|
+
lines.push('}');
|
|
200
|
+
lines.push('');
|
|
201
|
+
// Root component props
|
|
202
|
+
if (this.typescript) {
|
|
203
|
+
lines.push(this.generatePropsInterface(component));
|
|
204
|
+
lines.push('');
|
|
205
|
+
}
|
|
206
|
+
// Root component
|
|
207
|
+
const destructuredProps = this.getDestructuredProps(component);
|
|
208
|
+
const propsType = this.typescript ? `: ${component.name}Props` : '';
|
|
209
|
+
lines.push(`function ${component.name}Root({ ${destructuredProps} }${propsType}) {`);
|
|
210
|
+
const contextValue = component.props.map((p) => p.name).join(', ');
|
|
211
|
+
lines.push(` const value = { ${contextValue} };`);
|
|
212
|
+
lines.push('');
|
|
213
|
+
lines.push(' return (');
|
|
214
|
+
lines.push(` <${component.name}Context.Provider value={value}>`);
|
|
215
|
+
lines.push(` <div className={cn('${this.toKebabCase(component.name)}', className)}>`);
|
|
216
|
+
lines.push(' {children}');
|
|
217
|
+
lines.push(' </div>');
|
|
218
|
+
lines.push(` </${component.name}Context.Provider>`);
|
|
219
|
+
lines.push(' );');
|
|
220
|
+
lines.push('}');
|
|
221
|
+
lines.push('');
|
|
222
|
+
// Compound parts
|
|
223
|
+
for (const part of component.compound) {
|
|
224
|
+
lines.push(this.generateCompoundPart(component.name, part));
|
|
225
|
+
lines.push('');
|
|
226
|
+
}
|
|
227
|
+
// Export compound object
|
|
228
|
+
lines.push(`export const ${component.name} = Object.assign(${component.name}Root, {`);
|
|
229
|
+
for (const part of component.compound) {
|
|
230
|
+
lines.push(` ${part.name}: ${component.name}${part.name},`);
|
|
231
|
+
}
|
|
232
|
+
lines.push('});');
|
|
233
|
+
return {
|
|
234
|
+
path: `${this.basePath}/${this.toKebabCase(component.name)}.${ext}`,
|
|
235
|
+
content: lines.join('\n'),
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
generateCompoundPart(parentName, part) {
|
|
239
|
+
const lines = [];
|
|
240
|
+
const partName = `${parentName}${part.name}`;
|
|
241
|
+
if (this.typescript) {
|
|
242
|
+
lines.push(`interface ${partName}Props {`);
|
|
243
|
+
lines.push(' className?: string;');
|
|
244
|
+
lines.push(' children?: React.ReactNode;');
|
|
245
|
+
if (part.props) {
|
|
246
|
+
for (const prop of part.props) {
|
|
247
|
+
lines.push(` ${prop}?: string;`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
lines.push('}');
|
|
251
|
+
lines.push('');
|
|
252
|
+
}
|
|
253
|
+
const propsType = this.typescript ? `: ${partName}Props` : '';
|
|
254
|
+
const propsStr = part.props ? `{ className, children, ${part.props.join(', ')} }` : '{ className, children }';
|
|
255
|
+
lines.push(`function ${partName}(${propsStr}${propsType}) {`);
|
|
256
|
+
lines.push(` const context = use${parentName}();`);
|
|
257
|
+
lines.push('');
|
|
258
|
+
lines.push(' return (');
|
|
259
|
+
lines.push(` <${part.element} className={cn('${this.toKebabCase(partName)}', className)}>`);
|
|
260
|
+
lines.push(' {children}');
|
|
261
|
+
lines.push(` </${part.element}>`);
|
|
262
|
+
lines.push(' );');
|
|
263
|
+
lines.push('}');
|
|
264
|
+
return lines.join('\n');
|
|
265
|
+
}
|
|
266
|
+
// ===========================================================================
|
|
267
|
+
// Helpers
|
|
268
|
+
// ===========================================================================
|
|
269
|
+
mapPropType(type) {
|
|
270
|
+
const typeMap = {
|
|
271
|
+
string: 'string',
|
|
272
|
+
number: 'number',
|
|
273
|
+
boolean: 'boolean',
|
|
274
|
+
node: 'React.ReactNode',
|
|
275
|
+
element: 'React.ReactElement',
|
|
276
|
+
func: '() => void',
|
|
277
|
+
array: 'unknown[]',
|
|
278
|
+
object: 'Record<string, unknown>',
|
|
279
|
+
};
|
|
280
|
+
return typeMap[type] || type;
|
|
281
|
+
}
|
|
282
|
+
toKebabCase(str) {
|
|
283
|
+
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
// =============================================================================
|
|
287
|
+
// Convenience Function
|
|
288
|
+
// =============================================================================
|
|
289
|
+
/**
|
|
290
|
+
* Generate component files from Kappa ComponentBlock nodes
|
|
291
|
+
*/
|
|
292
|
+
export function generateComponents(components, options = {}) {
|
|
293
|
+
const generator = new KappaComponentGenerator(options);
|
|
294
|
+
return generator.generate(components);
|
|
295
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { DesignBlock } from './kappa-ast.js';
|
|
2
|
+
export type DesignOutputFormat = 'css' | 'tailwind' | 'both';
|
|
3
|
+
export interface DesignGeneratorOptions {
|
|
4
|
+
/** Output format (default: 'both') */
|
|
5
|
+
format?: DesignOutputFormat;
|
|
6
|
+
/** Add provenance comments (default: true) */
|
|
7
|
+
provenance?: boolean;
|
|
8
|
+
/** CSS custom property prefix (default: '') */
|
|
9
|
+
prefix?: string;
|
|
10
|
+
/** Use HSL values instead of hex (default: false) */
|
|
11
|
+
hsl?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface GeneratedDesign {
|
|
14
|
+
/** CSS variables file */
|
|
15
|
+
css?: {
|
|
16
|
+
path: string;
|
|
17
|
+
content: string;
|
|
18
|
+
};
|
|
19
|
+
/** Tailwind config extension */
|
|
20
|
+
tailwind?: {
|
|
21
|
+
path: string;
|
|
22
|
+
content: string;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export declare class KappaDesignGenerator {
|
|
26
|
+
private format;
|
|
27
|
+
private provenance;
|
|
28
|
+
private prefix;
|
|
29
|
+
private hsl;
|
|
30
|
+
constructor(options?: DesignGeneratorOptions);
|
|
31
|
+
/**
|
|
32
|
+
* Generate design files from DesignBlock AST
|
|
33
|
+
*/
|
|
34
|
+
generate(design: DesignBlock): GeneratedDesign;
|
|
35
|
+
private generateCSS;
|
|
36
|
+
private generateColorVariables;
|
|
37
|
+
private resolveColorValue;
|
|
38
|
+
private generateTypographyVariables;
|
|
39
|
+
private generateSpacingVariables;
|
|
40
|
+
private generateBorderVariables;
|
|
41
|
+
private generateShadowVariables;
|
|
42
|
+
private generateMotionVariables;
|
|
43
|
+
private generateTailwind;
|
|
44
|
+
private hexToHsl;
|
|
45
|
+
private adjustColor;
|
|
46
|
+
private addAlpha;
|
|
47
|
+
private toKebabCase;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Generate design system files from Kappa DesignBlock
|
|
51
|
+
*/
|
|
52
|
+
export declare function generateDesign(design: DesignBlock, options?: DesignGeneratorOptions): GeneratedDesign;
|