Skip to content
This repository was archived by the owner on May 15, 2026. It is now read-only.

Commit 8725668

Browse files
committed
refactor: move schema tests and ajv dep to packages/types, extract shared schema builder
- Remove ajv from src/package.json (extension workspace) since it was only used for schema validation tests unrelated to extension runtime code - Add ajv as devDependency to packages/types/package.json where the schema generation already lives - Move AJV validation tests from src/utils/__tests__/ to packages/types/src/__tests__/ alongside the sync test - Extract shared schema-building logic into packages/types/src/roomodes-schema.ts to eliminate duplication between the generator script and the sync test - Regenerate schemas/roomodes.json via the updated generator
1 parent 3fd2ca4 commit 8725668

7 files changed

Lines changed: 74 additions & 101 deletions

File tree

packages/types/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"@types/node": "^24.1.0",
3333
"globals": "^16.3.0",
3434
"tsup": "^8.4.0",
35+
"ajv": "^8.18.0",
3536
"vitest": "^3.2.3",
3637
"zod-to-json-schema": "^3.25.1"
3738
}

packages/types/scripts/generate-roomodes-schema.ts

Lines changed: 2 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -11,69 +11,11 @@
1111
import * as fs from "fs"
1212
import * as path from "path"
1313
import { fileURLToPath } from "url"
14-
import { zodToJsonSchema } from "zod-to-json-schema"
15-
import { z } from "zod"
1614

17-
import { toolGroups, deprecatedToolGroups } from "../src/tool.js"
18-
import { groupOptionsSchema, modeConfigSchema } from "../src/mode.js"
15+
import { generateRoomodesJsonSchema } from "../src/roomodes-schema.js"
1916

20-
// ---------------------------------------------------------------------------
21-
// 1. Build a ToolGroup enum that includes deprecated groups so existing
22-
// configs still validate.
23-
// ---------------------------------------------------------------------------
24-
const allToolGroups = [...toolGroups, ...deprecatedToolGroups] as [string, ...string[]]
25-
const allToolGroupsSchema = z.enum(allToolGroups)
17+
const jsonSchema = generateRoomodesJsonSchema()
2618

27-
// ---------------------------------------------------------------------------
28-
// 2. Build a GroupEntry schema that uses the extended tool group list.
29-
// ---------------------------------------------------------------------------
30-
const groupEntrySchema = z.union([allToolGroupsSchema, z.tuple([allToolGroupsSchema, groupOptionsSchema])])
31-
32-
// ---------------------------------------------------------------------------
33-
// 3. Build the RuleFile schema (used during import/export but not part of
34-
// the core Zod types).
35-
// ---------------------------------------------------------------------------
36-
const ruleFileSchema = z.object({
37-
relativePath: z.string(),
38-
content: z.string().optional(),
39-
})
40-
41-
// ---------------------------------------------------------------------------
42-
// 4. Build an extended ModeConfig schema that includes rulesFiles and uses
43-
// the extended groups (with deprecated entries).
44-
// ---------------------------------------------------------------------------
45-
const exportedModeConfigSchema = modeConfigSchema.omit({ groups: true }).extend({
46-
groups: z.array(groupEntrySchema),
47-
rulesFiles: z.array(ruleFileSchema).optional(),
48-
})
49-
50-
// ---------------------------------------------------------------------------
51-
// 5. Build the top-level .roomodes schema.
52-
// ---------------------------------------------------------------------------
53-
const roomodesSchema = z
54-
.object({
55-
customModes: z.array(exportedModeConfigSchema),
56-
})
57-
.strict()
58-
59-
// ---------------------------------------------------------------------------
60-
// 6. Convert to JSON Schema (draft-07).
61-
// ---------------------------------------------------------------------------
62-
const jsonSchema = zodToJsonSchema(roomodesSchema, {
63-
$refStrategy: "none",
64-
target: "jsonSchema7",
65-
}) as Record<string, unknown>
66-
67-
// ---------------------------------------------------------------------------
68-
// 7. Add metadata.
69-
// ---------------------------------------------------------------------------
70-
jsonSchema["$id"] = "https://github.com/RooCodeInc/Roo-Code/blob/main/schemas/roomodes.json"
71-
jsonSchema["title"] = "Roo Code Custom Modes"
72-
jsonSchema["description"] = "Schema for .roomodes configuration files used by Roo Code to define custom modes."
73-
74-
// ---------------------------------------------------------------------------
75-
// 8. Write to disk.
76-
// ---------------------------------------------------------------------------
7719
const __dirname = path.dirname(fileURLToPath(import.meta.url))
7820
const repoRoot = path.resolve(__dirname, "../../..")
7921
const outPath = path.join(repoRoot, "schemas", "roomodes.json")

packages/types/src/__tests__/roomodes-schema-sync.spec.ts

Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,8 @@ import { describe, it, expect } from "vitest"
22
import * as fs from "fs"
33
import * as path from "path"
44
import { fileURLToPath } from "url"
5-
import { zodToJsonSchema } from "zod-to-json-schema"
6-
import { z } from "zod"
75

8-
import { toolGroups, deprecatedToolGroups } from "../tool.js"
9-
import { groupOptionsSchema, modeConfigSchema } from "../mode.js"
6+
import { generateRoomodesJsonSchema } from "../roomodes-schema.js"
107

118
/**
129
* This test verifies that the checked-in schemas/roomodes.json matches what
@@ -22,32 +19,7 @@ describe("roomodes schema sync", () => {
2219
const schemaPath = path.resolve(__dirname, "../../../../schemas/roomodes.json")
2320
const checkedIn = JSON.parse(fs.readFileSync(schemaPath, "utf-8"))
2421

25-
// Reproduce the same generation logic as scripts/generate-roomodes-schema.ts
26-
const allToolGroups = [...toolGroups, ...deprecatedToolGroups] as [string, ...string[]]
27-
const allToolGroupsSchema = z.enum(allToolGroups)
28-
const groupEntrySchema = z.union([allToolGroupsSchema, z.tuple([allToolGroupsSchema, groupOptionsSchema])])
29-
const ruleFileSchema = z.object({
30-
relativePath: z.string(),
31-
content: z.string().optional(),
32-
})
33-
const exportedModeConfigSchema = modeConfigSchema.omit({ groups: true }).extend({
34-
groups: z.array(groupEntrySchema),
35-
rulesFiles: z.array(ruleFileSchema).optional(),
36-
})
37-
const roomodesSchema = z
38-
.object({
39-
customModes: z.array(exportedModeConfigSchema),
40-
})
41-
.strict()
42-
43-
const generated = zodToJsonSchema(roomodesSchema, {
44-
$refStrategy: "none",
45-
target: "jsonSchema7",
46-
}) as Record<string, unknown>
47-
48-
generated["$id"] = "https://github.com/RooCodeInc/Roo-Code/blob/main/schemas/roomodes.json"
49-
generated["title"] = "Roo Code Custom Modes"
50-
generated["description"] = "Schema for .roomodes configuration files used by Roo Code to define custom modes."
22+
const generated = generateRoomodesJsonSchema()
5123

5224
expect(checkedIn).toEqual(generated)
5325
})

src/utils/__tests__/roomodes-schema.spec.ts renamed to packages/types/src/__tests__/roomodes-schema.spec.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,24 @@
33
* using AJV. The schema itself is dynamically generated from the Zod types in
44
* packages/types/src/mode.ts -- see packages/types/scripts/generate-roomodes-schema.ts.
55
*
6-
* A separate drift-detection test in packages/types ensures the checked-in
7-
* schema stays in sync with the Zod source of truth.
6+
* A separate drift-detection test (roomodes-schema-sync.spec.ts) ensures the
7+
* checked-in schema stays in sync with the Zod source of truth.
88
*/
99
import { describe, it, expect, beforeAll } from "vitest"
10-
import Ajv from "ajv"
10+
import Ajv, { type ValidateFunction } from "ajv"
1111
import * as fs from "fs"
1212
import * as path from "path"
13+
import { fileURLToPath } from "url"
1314

1415
describe("roomodes JSON schema", () => {
15-
let ajv: Ajv
1616
let schema: Record<string, unknown>
17-
let validate: ReturnType<Ajv["compile"]>
17+
let validate: ValidateFunction
1818

1919
beforeAll(() => {
20-
const schemaPath = path.resolve(__dirname, "../../../schemas/roomodes.json")
20+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
21+
const schemaPath = path.resolve(__dirname, "../../../../schemas/roomodes.json")
2122
schema = JSON.parse(fs.readFileSync(schemaPath, "utf-8"))
22-
ajv = new Ajv({ strict: false })
23+
const ajv = new Ajv.default({ strict: false })
2324
validate = ajv.compile(schema)
2425
})
2526

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/**
2+
* Builds the Zod schema for .roomodes configuration files and converts it
3+
* to JSON Schema (draft-07). This module is the single source of truth for
4+
* both the generator script (scripts/generate-roomodes-schema.ts) and the
5+
* drift-detection test.
6+
*/
7+
8+
import { z } from "zod"
9+
import { zodToJsonSchema } from "zod-to-json-schema"
10+
11+
import { toolGroups, deprecatedToolGroups } from "./tool.js"
12+
import { groupOptionsSchema, modeConfigSchema } from "./mode.js"
13+
14+
// Build a ToolGroup enum that includes deprecated groups so existing configs
15+
// still validate.
16+
const allToolGroups = [...toolGroups, ...deprecatedToolGroups] as [string, ...string[]]
17+
const allToolGroupsSchema = z.enum(allToolGroups)
18+
19+
// Build a GroupEntry schema that uses the extended tool group list.
20+
const groupEntrySchema = z.union([allToolGroupsSchema, z.tuple([allToolGroupsSchema, groupOptionsSchema])])
21+
22+
// Build the RuleFile schema (used during import/export but not part of the
23+
// core Zod types).
24+
const ruleFileSchema = z.object({
25+
relativePath: z.string(),
26+
content: z.string().optional(),
27+
})
28+
29+
// Build an extended ModeConfig schema that includes rulesFiles and uses the
30+
// extended groups (with deprecated entries).
31+
const exportedModeConfigSchema = modeConfigSchema.omit({ groups: true }).extend({
32+
groups: z.array(groupEntrySchema),
33+
rulesFiles: z.array(ruleFileSchema).optional(),
34+
})
35+
36+
// Build the top-level .roomodes schema.
37+
const roomodesZodSchema = z
38+
.object({
39+
customModes: z.array(exportedModeConfigSchema),
40+
})
41+
.strict()
42+
43+
/**
44+
* Generates the JSON Schema object for .roomodes configuration files.
45+
* Includes metadata fields ($id, title, description).
46+
*/
47+
export function generateRoomodesJsonSchema(): Record<string, unknown> {
48+
const jsonSchema = zodToJsonSchema(roomodesZodSchema, {
49+
$refStrategy: "none",
50+
target: "jsonSchema7",
51+
}) as Record<string, unknown>
52+
53+
jsonSchema["$id"] = "https://github.com/RooCodeInc/Roo-Code/blob/main/schemas/roomodes.json"
54+
jsonSchema["title"] = "Roo Code Custom Modes"
55+
jsonSchema["description"] = "Schema for .roomodes configuration files used by Roo Code to define custom modes."
56+
57+
return jsonSchema
58+
}

pnpm-lock.yaml

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,6 @@
567567
"@vscode/test-electron": "^2.5.2",
568568
"@vscode/vsce": "3.3.2",
569569
"ai": "^6.0.75",
570-
"ajv": "^8.18.0",
571570
"esbuild-wasm": "^0.25.0",
572571
"execa": "^9.5.2",
573572
"glob": "^11.1.0",

0 commit comments

Comments
 (0)