@claudetools/tools 0.9.0 → 0.9.2
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/cli.js +9 -1
- package/dist/codedna/__tests__/examples/mongoose-example.d.ts +6 -0
- package/dist/codedna/__tests__/examples/mongoose-example.js +163 -0
- package/dist/codedna/__tests__/fixtures/typeorm-production-test.d.ts +1 -0
- package/dist/codedna/__tests__/fixtures/typeorm-production-test.js +231 -0
- package/dist/codedna/__tests__/fixtures/typeorm-test.d.ts +1 -0
- package/dist/codedna/__tests__/fixtures/typeorm-test.js +124 -0
- package/dist/codedna/__tests__/laravel-output-review.d.ts +1 -0
- package/dist/codedna/__tests__/laravel-output-review.js +249 -0
- package/dist/codedna/__tests__/mongoose-output-test.d.ts +1 -0
- package/dist/codedna/__tests__/mongoose-output-test.js +178 -0
- package/dist/codedna/examples/radix-example.d.ts +2 -0
- package/dist/codedna/examples/radix-example.js +259 -0
- package/dist/codedna/index.d.ts +5 -3
- package/dist/codedna/index.js +6 -3
- package/dist/codedna/kappa-ast.d.ts +143 -5
- package/dist/codedna/kappa-drizzle-generator.js +8 -5
- package/dist/codedna/kappa-gofiber-generator.d.ts +65 -0
- package/dist/codedna/kappa-gofiber-generator.js +587 -0
- package/dist/codedna/kappa-laravel-generator.d.ts +68 -0
- package/dist/codedna/kappa-laravel-generator.js +741 -0
- package/dist/codedna/kappa-lexer.d.ts +44 -0
- package/dist/codedna/kappa-lexer.js +124 -0
- package/dist/codedna/kappa-mantine-generator.d.ts +65 -0
- package/dist/codedna/kappa-mantine-generator.js +518 -0
- package/dist/codedna/kappa-mongoose-generator.d.ts +44 -0
- package/dist/codedna/kappa-mongoose-generator.js +442 -0
- package/dist/codedna/kappa-parser.d.ts +43 -1
- package/dist/codedna/kappa-parser.js +601 -0
- package/dist/codedna/kappa-radix-generator.d.ts +61 -0
- package/dist/codedna/kappa-radix-generator.js +566 -0
- package/dist/codedna/kappa-typeorm-generator.d.ts +59 -0
- package/dist/codedna/kappa-typeorm-generator.js +723 -0
- package/dist/codedna/kappa-vitest-generator.d.ts +85 -0
- package/dist/codedna/kappa-vitest-generator.js +739 -0
- package/dist/codedna/parser.js +26 -1
- package/dist/codegen/cloud-client.d.ts +160 -0
- package/dist/codegen/cloud-client.js +195 -0
- package/dist/codegen/codegen-tool.d.ts +35 -0
- package/dist/codegen/codegen-tool.js +312 -0
- package/dist/codegen/field-inference.d.ts +24 -0
- package/dist/codegen/field-inference.js +101 -0
- package/dist/codegen/form-parser.d.ts +13 -0
- package/dist/codegen/form-parser.js +186 -0
- package/dist/codegen/index.d.ts +2 -0
- package/dist/codegen/index.js +4 -0
- package/dist/codegen/natural-parser.d.ts +50 -0
- package/dist/codegen/natural-parser.js +769 -0
- package/dist/handlers/codedna-handlers.d.ts +1 -1
- package/dist/handlers/codegen-handlers.d.ts +20 -0
- package/dist/handlers/codegen-handlers.js +60 -0
- package/dist/handlers/kappa-handlers.d.ts +97 -0
- package/dist/handlers/kappa-handlers.js +408 -0
- package/dist/handlers/tool-handlers.js +124 -221
- package/dist/helpers/api-client.js +48 -3
- package/dist/helpers/compact-formatter.d.ts +9 -2
- package/dist/helpers/compact-formatter.js +26 -2
- package/dist/helpers/config.d.ts +7 -2
- package/dist/helpers/config.js +25 -10
- package/dist/helpers/session-validation.d.ts +1 -1
- package/dist/helpers/session-validation.js +2 -4
- package/dist/helpers/tasks.d.ts +21 -0
- package/dist/helpers/tasks.js +52 -0
- package/dist/helpers/workers.d.ts +1 -1
- package/dist/helpers/workers.js +19 -19
- package/dist/setup.d.ts +1 -0
- package/dist/setup.js +228 -3
- package/dist/templates/claude-md.d.ts +1 -1
- package/dist/templates/claude-md.js +37 -152
- package/dist/templates/orchestrator-prompt.d.ts +2 -2
- package/dist/templates/orchestrator-prompt.js +31 -38
- package/dist/templates/self-critique.d.ts +50 -0
- package/dist/templates/self-critique.js +209 -0
- package/dist/templates/worker-prompt.d.ts +3 -3
- package/dist/templates/worker-prompt.js +18 -18
- package/dist/tools.js +77 -413
- package/docs/codedna/generator-testing-summary.md +205 -0
- package/docs/codedna/radix-ui-generator.md +478 -0
- package/docs/kappa-gofiber-generator.md +274 -0
- package/docs/kappa-laravel-fixes.md +172 -0
- package/docs/kappa-mongoose-generator.md +322 -0
- package/docs/kappa-vitest-generator.md +337 -0
- package/package.json +1 -1
- package/dist/context/deduplication.test.d.ts +0 -6
- package/dist/context/deduplication.test.js +0 -84
|
@@ -34,6 +34,7 @@ class KappaParser extends CstParser {
|
|
|
34
34
|
{ ALT: () => this.SUBRULE(this.componentBlock) },
|
|
35
35
|
{ ALT: () => this.SUBRULE(this.formBlock) },
|
|
36
36
|
{ ALT: () => this.SUBRULE(this.designBlock) },
|
|
37
|
+
{ ALT: () => this.SUBRULE(this.testBlock) },
|
|
37
38
|
]);
|
|
38
39
|
});
|
|
39
40
|
});
|
|
@@ -1167,6 +1168,261 @@ class KappaParser extends CstParser {
|
|
|
1167
1168
|
{ ALT: () => this.CONSUME(tokens.StringLiteral) },
|
|
1168
1169
|
]);
|
|
1169
1170
|
});
|
|
1171
|
+
// ---------------------------------------------------------------------------
|
|
1172
|
+
// Test Block
|
|
1173
|
+
// ---------------------------------------------------------------------------
|
|
1174
|
+
// test UserTests {
|
|
1175
|
+
// target: User
|
|
1176
|
+
// framework: vitest
|
|
1177
|
+
// type: unit
|
|
1178
|
+
// describe "User entity" {
|
|
1179
|
+
// it "creates a user" { ... }
|
|
1180
|
+
// }
|
|
1181
|
+
// }
|
|
1182
|
+
testBlock = this.RULE('testBlock', () => {
|
|
1183
|
+
this.CONSUME(tokens.Test);
|
|
1184
|
+
this.CONSUME(tokens.PascalIdentifier); // Test suite name
|
|
1185
|
+
this.CONSUME(tokens.LBrace);
|
|
1186
|
+
this.MANY(() => {
|
|
1187
|
+
this.OR([
|
|
1188
|
+
{ ALT: () => this.SUBRULE(this.testProperty) },
|
|
1189
|
+
{ ALT: () => this.SUBRULE(this.testSuite) },
|
|
1190
|
+
{ ALT: () => this.SUBRULE(this.testMock) },
|
|
1191
|
+
]);
|
|
1192
|
+
});
|
|
1193
|
+
this.CONSUME(tokens.RBrace);
|
|
1194
|
+
});
|
|
1195
|
+
// target: User, framework: vitest, type: unit, coverage: { ... }
|
|
1196
|
+
testProperty = this.RULE('testProperty', () => {
|
|
1197
|
+
this.OR([
|
|
1198
|
+
{
|
|
1199
|
+
ALT: () => {
|
|
1200
|
+
this.CONSUME(tokens.Target);
|
|
1201
|
+
this.CONSUME(tokens.Colon);
|
|
1202
|
+
this.CONSUME(tokens.PascalIdentifier);
|
|
1203
|
+
},
|
|
1204
|
+
},
|
|
1205
|
+
{
|
|
1206
|
+
ALT: () => {
|
|
1207
|
+
this.CONSUME(tokens.Framework);
|
|
1208
|
+
this.CONSUME2(tokens.Colon);
|
|
1209
|
+
this.OR2([
|
|
1210
|
+
{ ALT: () => this.CONSUME(tokens.Vitest) },
|
|
1211
|
+
{ ALT: () => this.CONSUME(tokens.Jest) },
|
|
1212
|
+
{ ALT: () => this.CONSUME(tokens.Playwright) },
|
|
1213
|
+
{ ALT: () => this.CONSUME(tokens.Cypress) },
|
|
1214
|
+
]);
|
|
1215
|
+
},
|
|
1216
|
+
},
|
|
1217
|
+
{
|
|
1218
|
+
ALT: () => {
|
|
1219
|
+
this.CONSUME(tokens.Identifier); // 'type' keyword
|
|
1220
|
+
this.CONSUME3(tokens.Colon);
|
|
1221
|
+
this.OR3([
|
|
1222
|
+
{ ALT: () => this.CONSUME2(tokens.Identifier) }, // unit, integration, e2e
|
|
1223
|
+
]);
|
|
1224
|
+
},
|
|
1225
|
+
},
|
|
1226
|
+
{
|
|
1227
|
+
ALT: () => {
|
|
1228
|
+
this.CONSUME(tokens.Coverage);
|
|
1229
|
+
this.CONSUME4(tokens.Colon);
|
|
1230
|
+
this.CONSUME(tokens.LBrace);
|
|
1231
|
+
this.MANY(() => {
|
|
1232
|
+
this.SUBRULE(this.coverageProperty);
|
|
1233
|
+
});
|
|
1234
|
+
this.CONSUME(tokens.RBrace);
|
|
1235
|
+
},
|
|
1236
|
+
},
|
|
1237
|
+
]);
|
|
1238
|
+
});
|
|
1239
|
+
coverageProperty = this.RULE('coverageProperty', () => {
|
|
1240
|
+
this.CONSUME(tokens.Identifier); // statements, branches, functions, lines
|
|
1241
|
+
this.CONSUME(tokens.Colon);
|
|
1242
|
+
this.CONSUME(tokens.IntegerLiteral);
|
|
1243
|
+
});
|
|
1244
|
+
// describe "Suite name" { ... }
|
|
1245
|
+
testSuite = this.RULE('testSuite', () => {
|
|
1246
|
+
this.CONSUME(tokens.Describe);
|
|
1247
|
+
this.CONSUME(tokens.StringLiteral);
|
|
1248
|
+
this.CONSUME(tokens.LBrace);
|
|
1249
|
+
this.MANY(() => {
|
|
1250
|
+
this.OR([
|
|
1251
|
+
{ ALT: () => this.SUBRULE(this.testLifecycle) },
|
|
1252
|
+
{ ALT: () => this.SUBRULE(this.testCase) },
|
|
1253
|
+
{ ALT: () => this.SUBRULE2(this.testSuite) }, // Nested suites
|
|
1254
|
+
{ ALT: () => this.SUBRULE2(this.testMock) },
|
|
1255
|
+
]);
|
|
1256
|
+
});
|
|
1257
|
+
this.CONSUME(tokens.RBrace);
|
|
1258
|
+
});
|
|
1259
|
+
// beforeAll { ... }, afterEach { ... }, etc.
|
|
1260
|
+
testLifecycle = this.RULE('testLifecycle', () => {
|
|
1261
|
+
this.OR([
|
|
1262
|
+
{ ALT: () => this.CONSUME(tokens.BeforeAll) },
|
|
1263
|
+
{ ALT: () => this.CONSUME(tokens.AfterAll) },
|
|
1264
|
+
{ ALT: () => this.CONSUME(tokens.BeforeEach) },
|
|
1265
|
+
{ ALT: () => this.CONSUME(tokens.AfterEach) },
|
|
1266
|
+
]);
|
|
1267
|
+
this.CONSUME(tokens.LBrace);
|
|
1268
|
+
this.MANY(() => {
|
|
1269
|
+
this.CONSUME(tokens.StringLiteral); // Code statements as strings
|
|
1270
|
+
});
|
|
1271
|
+
this.CONSUME(tokens.RBrace);
|
|
1272
|
+
});
|
|
1273
|
+
// mock ModuleName { returns: "value" }
|
|
1274
|
+
testMock = this.RULE('testMock', () => {
|
|
1275
|
+
this.CONSUME(tokens.Mock);
|
|
1276
|
+
this.OR([
|
|
1277
|
+
{ ALT: () => this.CONSUME(tokens.PascalIdentifier) },
|
|
1278
|
+
{ ALT: () => this.CONSUME(tokens.StringLiteral) },
|
|
1279
|
+
]);
|
|
1280
|
+
this.OPTION(() => {
|
|
1281
|
+
this.CONSUME(tokens.LBrace);
|
|
1282
|
+
this.MANY(() => {
|
|
1283
|
+
this.SUBRULE(this.mockProperty);
|
|
1284
|
+
});
|
|
1285
|
+
this.CONSUME(tokens.RBrace);
|
|
1286
|
+
});
|
|
1287
|
+
});
|
|
1288
|
+
mockProperty = this.RULE('mockProperty', () => {
|
|
1289
|
+
this.OR([
|
|
1290
|
+
{
|
|
1291
|
+
ALT: () => {
|
|
1292
|
+
this.CONSUME(tokens.Returns);
|
|
1293
|
+
this.CONSUME(tokens.Colon);
|
|
1294
|
+
this.CONSUME(tokens.StringLiteral);
|
|
1295
|
+
},
|
|
1296
|
+
},
|
|
1297
|
+
{
|
|
1298
|
+
ALT: () => {
|
|
1299
|
+
this.CONSUME(tokens.Identifier); // implementation, etc.
|
|
1300
|
+
this.CONSUME2(tokens.Colon);
|
|
1301
|
+
this.CONSUME2(tokens.StringLiteral);
|
|
1302
|
+
},
|
|
1303
|
+
},
|
|
1304
|
+
]);
|
|
1305
|
+
});
|
|
1306
|
+
// it "test name" { setup: [...], action: "...", expect: [...] }
|
|
1307
|
+
// it "test name", skip { ... }
|
|
1308
|
+
// it "test name", only { ... }
|
|
1309
|
+
testCase = this.RULE('testCase', () => {
|
|
1310
|
+
this.CONSUME(tokens.It);
|
|
1311
|
+
this.CONSUME(tokens.StringLiteral);
|
|
1312
|
+
this.OPTION(() => {
|
|
1313
|
+
this.CONSUME(tokens.Comma);
|
|
1314
|
+
this.OR([
|
|
1315
|
+
{ ALT: () => this.CONSUME(tokens.Skip) },
|
|
1316
|
+
{ ALT: () => this.CONSUME(tokens.Only) },
|
|
1317
|
+
{ ALT: () => this.CONSUME(tokens.Todo) },
|
|
1318
|
+
]);
|
|
1319
|
+
});
|
|
1320
|
+
this.CONSUME(tokens.LBrace);
|
|
1321
|
+
this.MANY(() => {
|
|
1322
|
+
this.SUBRULE(this.testCaseSection);
|
|
1323
|
+
});
|
|
1324
|
+
this.CONSUME(tokens.RBrace);
|
|
1325
|
+
});
|
|
1326
|
+
testCaseSection = this.RULE('testCaseSection', () => {
|
|
1327
|
+
this.OR([
|
|
1328
|
+
{
|
|
1329
|
+
ALT: () => {
|
|
1330
|
+
this.CONSUME(tokens.Setup);
|
|
1331
|
+
this.CONSUME(tokens.Colon);
|
|
1332
|
+
this.CONSUME(tokens.LBracket);
|
|
1333
|
+
this.MANY_SEP({
|
|
1334
|
+
SEP: tokens.Comma,
|
|
1335
|
+
DEF: () => this.CONSUME(tokens.StringLiteral),
|
|
1336
|
+
});
|
|
1337
|
+
this.CONSUME(tokens.RBracket);
|
|
1338
|
+
},
|
|
1339
|
+
},
|
|
1340
|
+
{
|
|
1341
|
+
ALT: () => {
|
|
1342
|
+
this.CONSUME(tokens.Action);
|
|
1343
|
+
this.CONSUME2(tokens.Colon);
|
|
1344
|
+
this.CONSUME2(tokens.StringLiteral);
|
|
1345
|
+
},
|
|
1346
|
+
},
|
|
1347
|
+
{
|
|
1348
|
+
ALT: () => {
|
|
1349
|
+
this.CONSUME(tokens.Teardown);
|
|
1350
|
+
this.CONSUME3(tokens.Colon);
|
|
1351
|
+
this.CONSUME2(tokens.LBracket);
|
|
1352
|
+
this.MANY_SEP2({
|
|
1353
|
+
SEP: tokens.Comma,
|
|
1354
|
+
DEF: () => this.CONSUME3(tokens.StringLiteral),
|
|
1355
|
+
});
|
|
1356
|
+
this.CONSUME2(tokens.RBracket);
|
|
1357
|
+
},
|
|
1358
|
+
},
|
|
1359
|
+
{
|
|
1360
|
+
ALT: () => {
|
|
1361
|
+
this.CONSUME(tokens.Expect);
|
|
1362
|
+
this.CONSUME4(tokens.Colon);
|
|
1363
|
+
this.CONSUME3(tokens.LBracket);
|
|
1364
|
+
this.MANY_SEP3({
|
|
1365
|
+
SEP: tokens.Comma,
|
|
1366
|
+
DEF: () => this.SUBRULE(this.testAssertion),
|
|
1367
|
+
});
|
|
1368
|
+
this.CONSUME3(tokens.RBracket);
|
|
1369
|
+
},
|
|
1370
|
+
},
|
|
1371
|
+
{
|
|
1372
|
+
ALT: () => {
|
|
1373
|
+
this.CONSUME(tokens.Timeout);
|
|
1374
|
+
this.CONSUME5(tokens.Colon);
|
|
1375
|
+
this.CONSUME(tokens.IntegerLiteral);
|
|
1376
|
+
},
|
|
1377
|
+
},
|
|
1378
|
+
]);
|
|
1379
|
+
});
|
|
1380
|
+
// { actual: "expr", type: equals, expected: "value" }
|
|
1381
|
+
testAssertion = this.RULE('testAssertion', () => {
|
|
1382
|
+
this.CONSUME(tokens.LBrace);
|
|
1383
|
+
this.MANY_SEP({
|
|
1384
|
+
SEP: tokens.Comma,
|
|
1385
|
+
DEF: () => this.SUBRULE(this.assertionProperty),
|
|
1386
|
+
});
|
|
1387
|
+
this.CONSUME(tokens.RBrace);
|
|
1388
|
+
});
|
|
1389
|
+
assertionProperty = this.RULE('assertionProperty', () => {
|
|
1390
|
+
this.OR([
|
|
1391
|
+
{
|
|
1392
|
+
ALT: () => {
|
|
1393
|
+
this.CONSUME(tokens.Identifier); // actual, expected, message
|
|
1394
|
+
this.CONSUME(tokens.Colon);
|
|
1395
|
+
this.CONSUME(tokens.StringLiteral);
|
|
1396
|
+
},
|
|
1397
|
+
},
|
|
1398
|
+
{
|
|
1399
|
+
ALT: () => {
|
|
1400
|
+
this.CONSUME2(tokens.Identifier); // type
|
|
1401
|
+
this.CONSUME2(tokens.Colon);
|
|
1402
|
+
this.SUBRULE(this.assertionType);
|
|
1403
|
+
},
|
|
1404
|
+
},
|
|
1405
|
+
]);
|
|
1406
|
+
});
|
|
1407
|
+
assertionType = this.RULE('assertionType', () => {
|
|
1408
|
+
this.OR([
|
|
1409
|
+
{ ALT: () => this.CONSUME(tokens.EqualsAssertion) },
|
|
1410
|
+
{ ALT: () => this.CONSUME(tokens.NotEquals) },
|
|
1411
|
+
{ ALT: () => this.CONSUME(tokens.Contains) },
|
|
1412
|
+
{ ALT: () => this.CONSUME(tokens.Matches) },
|
|
1413
|
+
{ ALT: () => this.CONSUME(tokens.Throws) },
|
|
1414
|
+
{ ALT: () => this.CONSUME(tokens.Resolves) },
|
|
1415
|
+
{ ALT: () => this.CONSUME(tokens.Rejects) },
|
|
1416
|
+
{ ALT: () => this.CONSUME(tokens.Truthy) },
|
|
1417
|
+
{ ALT: () => this.CONSUME(tokens.Falsy) },
|
|
1418
|
+
{ ALT: () => this.CONSUME(tokens.Defined) },
|
|
1419
|
+
{ ALT: () => this.CONSUME(tokens.Undefined) },
|
|
1420
|
+
{ ALT: () => this.CONSUME(tokens.Null) },
|
|
1421
|
+
{ ALT: () => this.CONSUME(tokens.InstanceOf) },
|
|
1422
|
+
{ ALT: () => this.CONSUME(tokens.LengthOf) },
|
|
1423
|
+
{ ALT: () => this.CONSUME(tokens.DeepEquals) },
|
|
1424
|
+
]);
|
|
1425
|
+
});
|
|
1170
1426
|
}
|
|
1171
1427
|
// =============================================================================
|
|
1172
1428
|
// Parser Instance
|
|
@@ -1252,6 +1508,9 @@ class KappaAstBuilder extends BaseCstVisitor {
|
|
|
1252
1508
|
: [];
|
|
1253
1509
|
const forms = ctx.formBlock ? ctx.formBlock.map((f) => this.visit(f)) : [];
|
|
1254
1510
|
const design = ctx.designBlock ? this.visit(ctx.designBlock) : undefined;
|
|
1511
|
+
const tests = ctx.testBlock
|
|
1512
|
+
? ctx.testBlock.map((t) => this.visit(t))
|
|
1513
|
+
: [];
|
|
1255
1514
|
return {
|
|
1256
1515
|
kind: 'KappaSpec',
|
|
1257
1516
|
loc: this.getLocation(ctx),
|
|
@@ -1264,6 +1523,7 @@ class KappaAstBuilder extends BaseCstVisitor {
|
|
|
1264
1523
|
components,
|
|
1265
1524
|
forms,
|
|
1266
1525
|
design,
|
|
1526
|
+
tests,
|
|
1267
1527
|
};
|
|
1268
1528
|
}
|
|
1269
1529
|
// Project block visitor
|
|
@@ -1545,6 +1805,12 @@ class KappaAstBuilder extends BaseCstVisitor {
|
|
|
1545
1805
|
'Time',
|
|
1546
1806
|
'Text',
|
|
1547
1807
|
'Primary',
|
|
1808
|
+
// Additional keywords commonly used as field names
|
|
1809
|
+
'Title',
|
|
1810
|
+
'Label',
|
|
1811
|
+
'State',
|
|
1812
|
+
'Version',
|
|
1813
|
+
'Color',
|
|
1548
1814
|
];
|
|
1549
1815
|
for (const tokenType of tokenTypes) {
|
|
1550
1816
|
const token = ctx[tokenType]?.[0];
|
|
@@ -2497,6 +2763,341 @@ class KappaAstBuilder extends BaseCstVisitor {
|
|
|
2497
2763
|
value = ctx.StringLiteral[0].image.slice(1, -1);
|
|
2498
2764
|
return { name, value };
|
|
2499
2765
|
}
|
|
2766
|
+
// ---------------------------------------------------------------------------
|
|
2767
|
+
// Test Block Visitors
|
|
2768
|
+
// ---------------------------------------------------------------------------
|
|
2769
|
+
testBlock(ctx) {
|
|
2770
|
+
const name = (ctx.PascalIdentifier?.[0])?.image ?? '';
|
|
2771
|
+
// Parse properties
|
|
2772
|
+
let target = '';
|
|
2773
|
+
let framework = 'vitest';
|
|
2774
|
+
let type = 'unit';
|
|
2775
|
+
let coverage;
|
|
2776
|
+
if (ctx.testProperty) {
|
|
2777
|
+
for (const prop of ctx.testProperty) {
|
|
2778
|
+
const result = this.visit(prop);
|
|
2779
|
+
if (result.type === 'target')
|
|
2780
|
+
target = result.value;
|
|
2781
|
+
else if (result.type === 'framework')
|
|
2782
|
+
framework = result.value;
|
|
2783
|
+
else if (result.type === 'testType')
|
|
2784
|
+
type = result.value;
|
|
2785
|
+
else if (result.type === 'coverage')
|
|
2786
|
+
coverage = result.value;
|
|
2787
|
+
}
|
|
2788
|
+
}
|
|
2789
|
+
// Parse suites
|
|
2790
|
+
const suites = ctx.testSuite
|
|
2791
|
+
? ctx.testSuite.map((s) => this.visit(s))
|
|
2792
|
+
: [];
|
|
2793
|
+
// Parse mocks
|
|
2794
|
+
const mocks = ctx.testMock
|
|
2795
|
+
? ctx.testMock.map((m) => this.visit(m))
|
|
2796
|
+
: [];
|
|
2797
|
+
// Build root suite
|
|
2798
|
+
const rootSuite = {
|
|
2799
|
+
kind: 'TestSuite',
|
|
2800
|
+
loc: this.getLocation(ctx),
|
|
2801
|
+
name,
|
|
2802
|
+
mocks,
|
|
2803
|
+
tests: [],
|
|
2804
|
+
suites,
|
|
2805
|
+
};
|
|
2806
|
+
return {
|
|
2807
|
+
kind: 'TestBlock',
|
|
2808
|
+
loc: this.getLocation(ctx),
|
|
2809
|
+
name,
|
|
2810
|
+
target,
|
|
2811
|
+
framework,
|
|
2812
|
+
type,
|
|
2813
|
+
suite: rootSuite,
|
|
2814
|
+
coverage,
|
|
2815
|
+
};
|
|
2816
|
+
}
|
|
2817
|
+
testProperty(ctx) {
|
|
2818
|
+
if (ctx.Target) {
|
|
2819
|
+
return {
|
|
2820
|
+
type: 'target',
|
|
2821
|
+
value: (ctx.PascalIdentifier?.[0])?.image ?? '',
|
|
2822
|
+
};
|
|
2823
|
+
}
|
|
2824
|
+
if (ctx.Framework) {
|
|
2825
|
+
let fw = 'vitest';
|
|
2826
|
+
if (ctx.Vitest)
|
|
2827
|
+
fw = 'vitest';
|
|
2828
|
+
else if (ctx.Jest)
|
|
2829
|
+
fw = 'jest';
|
|
2830
|
+
else if (ctx.Playwright)
|
|
2831
|
+
fw = 'playwright';
|
|
2832
|
+
else if (ctx.Cypress)
|
|
2833
|
+
fw = 'cypress';
|
|
2834
|
+
return { type: 'framework', value: fw };
|
|
2835
|
+
}
|
|
2836
|
+
if (ctx.Coverage) {
|
|
2837
|
+
const coverageProps = ctx.coverageProperty
|
|
2838
|
+
? ctx.coverageProperty.map((p) => this.visit(p))
|
|
2839
|
+
: [];
|
|
2840
|
+
const coverage = {};
|
|
2841
|
+
for (const prop of coverageProps) {
|
|
2842
|
+
coverage[prop.name] = prop.value;
|
|
2843
|
+
}
|
|
2844
|
+
return { type: 'coverage', value: coverage };
|
|
2845
|
+
}
|
|
2846
|
+
// Default: type property
|
|
2847
|
+
const identifiers = ctx.Identifier;
|
|
2848
|
+
if (identifiers && identifiers.length >= 2) {
|
|
2849
|
+
return { type: 'testType', value: identifiers[1].image };
|
|
2850
|
+
}
|
|
2851
|
+
return { type: 'testType', value: 'unit' };
|
|
2852
|
+
}
|
|
2853
|
+
coverageProperty(ctx) {
|
|
2854
|
+
return {
|
|
2855
|
+
name: ctx.Identifier?.[0]?.image ?? '',
|
|
2856
|
+
value: parseInt(ctx.IntegerLiteral?.[0]?.image ?? '0', 10),
|
|
2857
|
+
};
|
|
2858
|
+
}
|
|
2859
|
+
testSuite(ctx) {
|
|
2860
|
+
const name = (ctx.StringLiteral?.[0])?.image?.slice(1, -1) ?? '';
|
|
2861
|
+
// Parse lifecycle hooks
|
|
2862
|
+
const lifecycles = ctx.testLifecycle
|
|
2863
|
+
? ctx.testLifecycle.map((l) => this.visit(l))
|
|
2864
|
+
: [];
|
|
2865
|
+
let beforeAll;
|
|
2866
|
+
let afterAll;
|
|
2867
|
+
let beforeEach;
|
|
2868
|
+
let afterEach;
|
|
2869
|
+
for (const lc of lifecycles) {
|
|
2870
|
+
if (lc.type === 'beforeAll')
|
|
2871
|
+
beforeAll = lc.statements;
|
|
2872
|
+
else if (lc.type === 'afterAll')
|
|
2873
|
+
afterAll = lc.statements;
|
|
2874
|
+
else if (lc.type === 'beforeEach')
|
|
2875
|
+
beforeEach = lc.statements;
|
|
2876
|
+
else if (lc.type === 'afterEach')
|
|
2877
|
+
afterEach = lc.statements;
|
|
2878
|
+
}
|
|
2879
|
+
// Parse test cases
|
|
2880
|
+
const tests = ctx.testCase
|
|
2881
|
+
? ctx.testCase.map((t) => this.visit(t))
|
|
2882
|
+
: [];
|
|
2883
|
+
// Parse nested suites
|
|
2884
|
+
const suites = ctx.testSuite
|
|
2885
|
+
? ctx.testSuite.map((s) => this.visit(s))
|
|
2886
|
+
: [];
|
|
2887
|
+
// Parse mocks
|
|
2888
|
+
const mocks = ctx.testMock
|
|
2889
|
+
? ctx.testMock.map((m) => this.visit(m))
|
|
2890
|
+
: [];
|
|
2891
|
+
return {
|
|
2892
|
+
kind: 'TestSuite',
|
|
2893
|
+
loc: this.getLocation(ctx),
|
|
2894
|
+
name,
|
|
2895
|
+
beforeAll,
|
|
2896
|
+
afterAll,
|
|
2897
|
+
beforeEach,
|
|
2898
|
+
afterEach,
|
|
2899
|
+
mocks,
|
|
2900
|
+
tests,
|
|
2901
|
+
suites,
|
|
2902
|
+
};
|
|
2903
|
+
}
|
|
2904
|
+
testLifecycle(ctx) {
|
|
2905
|
+
let type = 'beforeEach';
|
|
2906
|
+
if (ctx.BeforeAll)
|
|
2907
|
+
type = 'beforeAll';
|
|
2908
|
+
else if (ctx.AfterAll)
|
|
2909
|
+
type = 'afterAll';
|
|
2910
|
+
else if (ctx.BeforeEach)
|
|
2911
|
+
type = 'beforeEach';
|
|
2912
|
+
else if (ctx.AfterEach)
|
|
2913
|
+
type = 'afterEach';
|
|
2914
|
+
const statements = ctx.StringLiteral
|
|
2915
|
+
? ctx.StringLiteral.map((s) => s.image.slice(1, -1))
|
|
2916
|
+
: [];
|
|
2917
|
+
return { type, statements };
|
|
2918
|
+
}
|
|
2919
|
+
testMock(ctx) {
|
|
2920
|
+
let target = '';
|
|
2921
|
+
if (ctx.PascalIdentifier) {
|
|
2922
|
+
target = (ctx.PascalIdentifier?.[0])?.image ?? '';
|
|
2923
|
+
}
|
|
2924
|
+
else if (ctx.StringLiteral) {
|
|
2925
|
+
target = (ctx.StringLiteral?.[0])?.image?.slice(1, -1) ?? '';
|
|
2926
|
+
}
|
|
2927
|
+
let returns;
|
|
2928
|
+
let implementation;
|
|
2929
|
+
if (ctx.mockProperty) {
|
|
2930
|
+
for (const prop of ctx.mockProperty) {
|
|
2931
|
+
const result = this.visit(prop);
|
|
2932
|
+
if (result.type === 'returns')
|
|
2933
|
+
returns = result.value;
|
|
2934
|
+
else if (result.type === 'implementation')
|
|
2935
|
+
implementation = result.value;
|
|
2936
|
+
}
|
|
2937
|
+
}
|
|
2938
|
+
return {
|
|
2939
|
+
kind: 'TestMock',
|
|
2940
|
+
loc: this.getLocation(ctx),
|
|
2941
|
+
target,
|
|
2942
|
+
returns,
|
|
2943
|
+
implementation,
|
|
2944
|
+
};
|
|
2945
|
+
}
|
|
2946
|
+
mockProperty(ctx) {
|
|
2947
|
+
if (ctx.Returns) {
|
|
2948
|
+
return {
|
|
2949
|
+
type: 'returns',
|
|
2950
|
+
value: (ctx.StringLiteral?.[0])?.image?.slice(1, -1) ?? '',
|
|
2951
|
+
};
|
|
2952
|
+
}
|
|
2953
|
+
// implementation or other property
|
|
2954
|
+
const identifier = (ctx.Identifier?.[0])?.image ?? '';
|
|
2955
|
+
const value = (ctx.StringLiteral?.[0])?.image?.slice(1, -1) ?? '';
|
|
2956
|
+
return { type: identifier, value };
|
|
2957
|
+
}
|
|
2958
|
+
testCase(ctx) {
|
|
2959
|
+
const name = (ctx.StringLiteral?.[0])?.image?.slice(1, -1) ?? '';
|
|
2960
|
+
let skip = false;
|
|
2961
|
+
let only = false;
|
|
2962
|
+
let todo = false;
|
|
2963
|
+
if (ctx.Skip)
|
|
2964
|
+
skip = true;
|
|
2965
|
+
if (ctx.Only)
|
|
2966
|
+
only = true;
|
|
2967
|
+
if (ctx.Todo)
|
|
2968
|
+
todo = true;
|
|
2969
|
+
// Parse sections
|
|
2970
|
+
let setup;
|
|
2971
|
+
let action = '';
|
|
2972
|
+
let teardown;
|
|
2973
|
+
let timeout;
|
|
2974
|
+
const assertions = [];
|
|
2975
|
+
if (ctx.testCaseSection) {
|
|
2976
|
+
for (const section of ctx.testCaseSection) {
|
|
2977
|
+
const result = this.visit(section);
|
|
2978
|
+
if (result.type === 'setup')
|
|
2979
|
+
setup = result.value;
|
|
2980
|
+
else if (result.type === 'action')
|
|
2981
|
+
action = result.value;
|
|
2982
|
+
else if (result.type === 'teardown')
|
|
2983
|
+
teardown = result.value;
|
|
2984
|
+
else if (result.type === 'timeout')
|
|
2985
|
+
timeout = result.value;
|
|
2986
|
+
else if (result.type === 'expect')
|
|
2987
|
+
assertions.push(...result.value);
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
return {
|
|
2991
|
+
kind: 'TestCase',
|
|
2992
|
+
loc: this.getLocation(ctx),
|
|
2993
|
+
name,
|
|
2994
|
+
setup,
|
|
2995
|
+
action,
|
|
2996
|
+
assertions,
|
|
2997
|
+
teardown,
|
|
2998
|
+
skip,
|
|
2999
|
+
only,
|
|
3000
|
+
todo,
|
|
3001
|
+
timeout,
|
|
3002
|
+
};
|
|
3003
|
+
}
|
|
3004
|
+
testCaseSection(ctx) {
|
|
3005
|
+
if (ctx.Setup) {
|
|
3006
|
+
const statements = ctx.StringLiteral
|
|
3007
|
+
? ctx.StringLiteral.map((s) => s.image.slice(1, -1))
|
|
3008
|
+
: [];
|
|
3009
|
+
return { type: 'setup', value: statements };
|
|
3010
|
+
}
|
|
3011
|
+
if (ctx.Action) {
|
|
3012
|
+
const value = (ctx.StringLiteral?.[0])?.image?.slice(1, -1) ?? '';
|
|
3013
|
+
return { type: 'action', value };
|
|
3014
|
+
}
|
|
3015
|
+
if (ctx.Teardown) {
|
|
3016
|
+
const statements = ctx.StringLiteral
|
|
3017
|
+
? ctx.StringLiteral.map((s) => s.image.slice(1, -1))
|
|
3018
|
+
: [];
|
|
3019
|
+
return { type: 'teardown', value: statements };
|
|
3020
|
+
}
|
|
3021
|
+
if (ctx.Expect) {
|
|
3022
|
+
const assertions = ctx.testAssertion
|
|
3023
|
+
? ctx.testAssertion.map((a) => this.visit(a))
|
|
3024
|
+
: [];
|
|
3025
|
+
return { type: 'expect', value: assertions };
|
|
3026
|
+
}
|
|
3027
|
+
if (ctx.Timeout) {
|
|
3028
|
+
const value = parseInt((ctx.IntegerLiteral?.[0])?.image ?? '0', 10);
|
|
3029
|
+
return { type: 'timeout', value };
|
|
3030
|
+
}
|
|
3031
|
+
return { type: 'unknown', value: null };
|
|
3032
|
+
}
|
|
3033
|
+
testAssertion(ctx) {
|
|
3034
|
+
let actual = '';
|
|
3035
|
+
let type = 'equals';
|
|
3036
|
+
let expected;
|
|
3037
|
+
let message;
|
|
3038
|
+
if (ctx.assertionProperty) {
|
|
3039
|
+
for (const prop of ctx.assertionProperty) {
|
|
3040
|
+
const result = this.visit(prop);
|
|
3041
|
+
if (result.name === 'actual')
|
|
3042
|
+
actual = result.value;
|
|
3043
|
+
else if (result.name === 'expected')
|
|
3044
|
+
expected = result.value;
|
|
3045
|
+
else if (result.name === 'message')
|
|
3046
|
+
message = result.value;
|
|
3047
|
+
else if (result.name === 'type')
|
|
3048
|
+
type = result.value;
|
|
3049
|
+
}
|
|
3050
|
+
}
|
|
3051
|
+
return {
|
|
3052
|
+
kind: 'TestAssertion',
|
|
3053
|
+
loc: this.getLocation(ctx),
|
|
3054
|
+
actual,
|
|
3055
|
+
type,
|
|
3056
|
+
expected,
|
|
3057
|
+
message,
|
|
3058
|
+
};
|
|
3059
|
+
}
|
|
3060
|
+
assertionProperty(ctx) {
|
|
3061
|
+
const identifier = (ctx.Identifier?.[0])?.image ?? '';
|
|
3062
|
+
if (ctx.assertionType) {
|
|
3063
|
+
return { name: 'type', value: this.visit(ctx.assertionType) };
|
|
3064
|
+
}
|
|
3065
|
+
const value = (ctx.StringLiteral?.[0])?.image?.slice(1, -1) ?? '';
|
|
3066
|
+
return { name: identifier, value };
|
|
3067
|
+
}
|
|
3068
|
+
assertionType(ctx) {
|
|
3069
|
+
if (ctx.EqualsAssertion)
|
|
3070
|
+
return 'equals';
|
|
3071
|
+
if (ctx.NotEquals)
|
|
3072
|
+
return 'notEquals';
|
|
3073
|
+
if (ctx.Contains)
|
|
3074
|
+
return 'contains';
|
|
3075
|
+
if (ctx.Matches)
|
|
3076
|
+
return 'matches';
|
|
3077
|
+
if (ctx.Throws)
|
|
3078
|
+
return 'throws';
|
|
3079
|
+
if (ctx.Resolves)
|
|
3080
|
+
return 'resolves';
|
|
3081
|
+
if (ctx.Rejects)
|
|
3082
|
+
return 'rejects';
|
|
3083
|
+
if (ctx.Truthy)
|
|
3084
|
+
return 'truthy';
|
|
3085
|
+
if (ctx.Falsy)
|
|
3086
|
+
return 'falsy';
|
|
3087
|
+
if (ctx.Defined)
|
|
3088
|
+
return 'defined';
|
|
3089
|
+
if (ctx.Undefined)
|
|
3090
|
+
return 'undefined';
|
|
3091
|
+
if (ctx.Null)
|
|
3092
|
+
return 'null';
|
|
3093
|
+
if (ctx.InstanceOf)
|
|
3094
|
+
return 'instanceOf';
|
|
3095
|
+
if (ctx.LengthOf)
|
|
3096
|
+
return 'lengthOf';
|
|
3097
|
+
if (ctx.DeepEquals)
|
|
3098
|
+
return 'deepEquals';
|
|
3099
|
+
return 'equals';
|
|
3100
|
+
}
|
|
2500
3101
|
}
|
|
2501
3102
|
// =============================================================================
|
|
2502
3103
|
// Parse Function
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { ComponentBlock } from './kappa-ast.js';
|
|
2
|
+
export interface RadixGeneratorOptions {
|
|
3
|
+
/** Add provenance comments (default: true) */
|
|
4
|
+
provenance?: boolean;
|
|
5
|
+
/** Generate TypeScript (default: true) */
|
|
6
|
+
typescript?: boolean;
|
|
7
|
+
/** Base path for components (default: 'components/ui') */
|
|
8
|
+
basePath?: string;
|
|
9
|
+
/** Include accessibility documentation comments (default: true) */
|
|
10
|
+
a11yDocs?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface GeneratedRadixComponent {
|
|
13
|
+
/** Component file path */
|
|
14
|
+
path: string;
|
|
15
|
+
/** Component content */
|
|
16
|
+
content: string;
|
|
17
|
+
/** Radix primitive used */
|
|
18
|
+
primitive: RadixPrimitive;
|
|
19
|
+
}
|
|
20
|
+
export interface GeneratedRadixComponents {
|
|
21
|
+
/** Generated component files */
|
|
22
|
+
components: GeneratedRadixComponent[];
|
|
23
|
+
/** Required Radix UI dependencies */
|
|
24
|
+
dependencies: string[];
|
|
25
|
+
}
|
|
26
|
+
export type RadixPrimitive = 'Dialog' | 'Popover' | 'Select' | 'Checkbox' | 'RadioGroup' | 'Switch' | 'Slider' | 'Tabs' | 'Accordion' | 'DropdownMenu' | 'ContextMenu' | 'HoverCard' | 'Tooltip' | 'AlertDialog' | 'AspectRatio' | 'Avatar' | 'Collapsible' | 'Label' | 'Progress' | 'ScrollArea' | 'Separator' | 'Toggle' | 'ToggleGroup';
|
|
27
|
+
export declare class KappaRadixGenerator {
|
|
28
|
+
private provenance;
|
|
29
|
+
private typescript;
|
|
30
|
+
private basePath;
|
|
31
|
+
private a11yDocs;
|
|
32
|
+
private usedDependencies;
|
|
33
|
+
constructor(options?: RadixGeneratorOptions);
|
|
34
|
+
/**
|
|
35
|
+
* Generate Radix UI component files from ComponentBlock AST nodes
|
|
36
|
+
*/
|
|
37
|
+
generate(components: ComponentBlock[]): GeneratedRadixComponents;
|
|
38
|
+
private detectPrimitive;
|
|
39
|
+
private generateRadixComponent;
|
|
40
|
+
private generateRadixImports;
|
|
41
|
+
private getUsedRadixParts;
|
|
42
|
+
private mapCompoundPartToRadix;
|
|
43
|
+
private generateSimpleRadixComponent;
|
|
44
|
+
private generateRadixPropsInterface;
|
|
45
|
+
private generateRadixStructureJSX;
|
|
46
|
+
private mapStructureToRadixComponent;
|
|
47
|
+
private generateCompoundRadixComponent;
|
|
48
|
+
private generateRadixCompoundPart;
|
|
49
|
+
private generateSimpleComponent;
|
|
50
|
+
private getSimpleDestructuredProps;
|
|
51
|
+
private getVariantClassName;
|
|
52
|
+
private getElementType;
|
|
53
|
+
private getElementTag;
|
|
54
|
+
private getDestructuredProps;
|
|
55
|
+
private mapPropType;
|
|
56
|
+
private toKebabCase;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Generate Radix UI component files from Kappa ComponentBlock nodes
|
|
60
|
+
*/
|
|
61
|
+
export declare function generateRadixComponents(components: ComponentBlock[], options?: RadixGeneratorOptions): GeneratedRadixComponents;
|