mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-10-06 02:47:56 +00:00
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
This commit is contained in:
parent
3fd2ca47c8
commit
8725668865
7 changed files with 74 additions and 101 deletions
|
|
@ -32,6 +32,7 @@
|
|||
"@types/node": "^24.1.0",
|
||||
"globals": "^16.3.0",
|
||||
"tsup": "^8.4.0",
|
||||
"ajv": "^8.18.0",
|
||||
"vitest": "^3.2.3",
|
||||
"zod-to-json-schema": "^3.25.1"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,69 +11,11 @@
|
|||
import * as fs from "fs"
|
||||
import * as path from "path"
|
||||
import { fileURLToPath } from "url"
|
||||
import { zodToJsonSchema } from "zod-to-json-schema"
|
||||
import { z } from "zod"
|
||||
|
||||
import { toolGroups, deprecatedToolGroups } from "../src/tool.js"
|
||||
import { groupOptionsSchema, modeConfigSchema } from "../src/mode.js"
|
||||
import { generateRoomodesJsonSchema } from "../src/roomodes-schema.js"
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 1. Build a ToolGroup enum that includes deprecated groups so existing
|
||||
// configs still validate.
|
||||
// ---------------------------------------------------------------------------
|
||||
const allToolGroups = [...toolGroups, ...deprecatedToolGroups] as [string, ...string[]]
|
||||
const allToolGroupsSchema = z.enum(allToolGroups)
|
||||
const jsonSchema = generateRoomodesJsonSchema()
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 2. Build a GroupEntry schema that uses the extended tool group list.
|
||||
// ---------------------------------------------------------------------------
|
||||
const groupEntrySchema = z.union([allToolGroupsSchema, z.tuple([allToolGroupsSchema, groupOptionsSchema])])
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 3. Build the RuleFile schema (used during import/export but not part of
|
||||
// the core Zod types).
|
||||
// ---------------------------------------------------------------------------
|
||||
const ruleFileSchema = z.object({
|
||||
relativePath: z.string(),
|
||||
content: z.string().optional(),
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 4. Build an extended ModeConfig schema that includes rulesFiles and uses
|
||||
// the extended groups (with deprecated entries).
|
||||
// ---------------------------------------------------------------------------
|
||||
const exportedModeConfigSchema = modeConfigSchema.omit({ groups: true }).extend({
|
||||
groups: z.array(groupEntrySchema),
|
||||
rulesFiles: z.array(ruleFileSchema).optional(),
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 5. Build the top-level .roomodes schema.
|
||||
// ---------------------------------------------------------------------------
|
||||
const roomodesSchema = z
|
||||
.object({
|
||||
customModes: z.array(exportedModeConfigSchema),
|
||||
})
|
||||
.strict()
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 6. Convert to JSON Schema (draft-07).
|
||||
// ---------------------------------------------------------------------------
|
||||
const jsonSchema = zodToJsonSchema(roomodesSchema, {
|
||||
$refStrategy: "none",
|
||||
target: "jsonSchema7",
|
||||
}) as Record<string, unknown>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 7. Add metadata.
|
||||
// ---------------------------------------------------------------------------
|
||||
jsonSchema["$id"] = "https://github.com/RooCodeInc/Roo-Code/blob/main/schemas/roomodes.json"
|
||||
jsonSchema["title"] = "Roo Code Custom Modes"
|
||||
jsonSchema["description"] = "Schema for .roomodes configuration files used by Roo Code to define custom modes."
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 8. Write to disk.
|
||||
// ---------------------------------------------------------------------------
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
const repoRoot = path.resolve(__dirname, "../../..")
|
||||
const outPath = path.join(repoRoot, "schemas", "roomodes.json")
|
||||
|
|
|
|||
|
|
@ -2,11 +2,8 @@ import { describe, it, expect } from "vitest"
|
|||
import * as fs from "fs"
|
||||
import * as path from "path"
|
||||
import { fileURLToPath } from "url"
|
||||
import { zodToJsonSchema } from "zod-to-json-schema"
|
||||
import { z } from "zod"
|
||||
|
||||
import { toolGroups, deprecatedToolGroups } from "../tool.js"
|
||||
import { groupOptionsSchema, modeConfigSchema } from "../mode.js"
|
||||
import { generateRoomodesJsonSchema } from "../roomodes-schema.js"
|
||||
|
||||
/**
|
||||
* This test verifies that the checked-in schemas/roomodes.json matches what
|
||||
|
|
@ -22,32 +19,7 @@ describe("roomodes schema sync", () => {
|
|||
const schemaPath = path.resolve(__dirname, "../../../../schemas/roomodes.json")
|
||||
const checkedIn = JSON.parse(fs.readFileSync(schemaPath, "utf-8"))
|
||||
|
||||
// Reproduce the same generation logic as scripts/generate-roomodes-schema.ts
|
||||
const allToolGroups = [...toolGroups, ...deprecatedToolGroups] as [string, ...string[]]
|
||||
const allToolGroupsSchema = z.enum(allToolGroups)
|
||||
const groupEntrySchema = z.union([allToolGroupsSchema, z.tuple([allToolGroupsSchema, groupOptionsSchema])])
|
||||
const ruleFileSchema = z.object({
|
||||
relativePath: z.string(),
|
||||
content: z.string().optional(),
|
||||
})
|
||||
const exportedModeConfigSchema = modeConfigSchema.omit({ groups: true }).extend({
|
||||
groups: z.array(groupEntrySchema),
|
||||
rulesFiles: z.array(ruleFileSchema).optional(),
|
||||
})
|
||||
const roomodesSchema = z
|
||||
.object({
|
||||
customModes: z.array(exportedModeConfigSchema),
|
||||
})
|
||||
.strict()
|
||||
|
||||
const generated = zodToJsonSchema(roomodesSchema, {
|
||||
$refStrategy: "none",
|
||||
target: "jsonSchema7",
|
||||
}) as Record<string, unknown>
|
||||
|
||||
generated["$id"] = "https://github.com/RooCodeInc/Roo-Code/blob/main/schemas/roomodes.json"
|
||||
generated["title"] = "Roo Code Custom Modes"
|
||||
generated["description"] = "Schema for .roomodes configuration files used by Roo Code to define custom modes."
|
||||
const generated = generateRoomodesJsonSchema()
|
||||
|
||||
expect(checkedIn).toEqual(generated)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -3,23 +3,24 @@
|
|||
* using AJV. The schema itself is dynamically generated from the Zod types in
|
||||
* packages/types/src/mode.ts -- see packages/types/scripts/generate-roomodes-schema.ts.
|
||||
*
|
||||
* A separate drift-detection test in packages/types ensures the checked-in
|
||||
* schema stays in sync with the Zod source of truth.
|
||||
* A separate drift-detection test (roomodes-schema-sync.spec.ts) ensures the
|
||||
* checked-in schema stays in sync with the Zod source of truth.
|
||||
*/
|
||||
import { describe, it, expect, beforeAll } from "vitest"
|
||||
import Ajv from "ajv"
|
||||
import Ajv, { type ValidateFunction } from "ajv"
|
||||
import * as fs from "fs"
|
||||
import * as path from "path"
|
||||
import { fileURLToPath } from "url"
|
||||
|
||||
describe("roomodes JSON schema", () => {
|
||||
let ajv: Ajv
|
||||
let schema: Record<string, unknown>
|
||||
let validate: ReturnType<Ajv["compile"]>
|
||||
let validate: ValidateFunction
|
||||
|
||||
beforeAll(() => {
|
||||
const schemaPath = path.resolve(__dirname, "../../../schemas/roomodes.json")
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
const schemaPath = path.resolve(__dirname, "../../../../schemas/roomodes.json")
|
||||
schema = JSON.parse(fs.readFileSync(schemaPath, "utf-8"))
|
||||
ajv = new Ajv({ strict: false })
|
||||
const ajv = new Ajv.default({ strict: false })
|
||||
validate = ajv.compile(schema)
|
||||
})
|
||||
|
||||
58
packages/types/src/roomodes-schema.ts
Normal file
58
packages/types/src/roomodes-schema.ts
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
* Builds the Zod schema for .roomodes configuration files and converts it
|
||||
* to JSON Schema (draft-07). This module is the single source of truth for
|
||||
* both the generator script (scripts/generate-roomodes-schema.ts) and the
|
||||
* drift-detection test.
|
||||
*/
|
||||
|
||||
import { z } from "zod"
|
||||
import { zodToJsonSchema } from "zod-to-json-schema"
|
||||
|
||||
import { toolGroups, deprecatedToolGroups } from "./tool.js"
|
||||
import { groupOptionsSchema, modeConfigSchema } from "./mode.js"
|
||||
|
||||
// Build a ToolGroup enum that includes deprecated groups so existing configs
|
||||
// still validate.
|
||||
const allToolGroups = [...toolGroups, ...deprecatedToolGroups] as [string, ...string[]]
|
||||
const allToolGroupsSchema = z.enum(allToolGroups)
|
||||
|
||||
// Build a GroupEntry schema that uses the extended tool group list.
|
||||
const groupEntrySchema = z.union([allToolGroupsSchema, z.tuple([allToolGroupsSchema, groupOptionsSchema])])
|
||||
|
||||
// Build the RuleFile schema (used during import/export but not part of the
|
||||
// core Zod types).
|
||||
const ruleFileSchema = z.object({
|
||||
relativePath: z.string(),
|
||||
content: z.string().optional(),
|
||||
})
|
||||
|
||||
// Build an extended ModeConfig schema that includes rulesFiles and uses the
|
||||
// extended groups (with deprecated entries).
|
||||
const exportedModeConfigSchema = modeConfigSchema.omit({ groups: true }).extend({
|
||||
groups: z.array(groupEntrySchema),
|
||||
rulesFiles: z.array(ruleFileSchema).optional(),
|
||||
})
|
||||
|
||||
// Build the top-level .roomodes schema.
|
||||
const roomodesZodSchema = z
|
||||
.object({
|
||||
customModes: z.array(exportedModeConfigSchema),
|
||||
})
|
||||
.strict()
|
||||
|
||||
/**
|
||||
* Generates the JSON Schema object for .roomodes configuration files.
|
||||
* Includes metadata fields ($id, title, description).
|
||||
*/
|
||||
export function generateRoomodesJsonSchema(): Record<string, unknown> {
|
||||
const jsonSchema = zodToJsonSchema(roomodesZodSchema, {
|
||||
$refStrategy: "none",
|
||||
target: "jsonSchema7",
|
||||
}) as Record<string, unknown>
|
||||
|
||||
jsonSchema["$id"] = "https://github.com/RooCodeInc/Roo-Code/blob/main/schemas/roomodes.json"
|
||||
jsonSchema["title"] = "Roo Code Custom Modes"
|
||||
jsonSchema["description"] = "Schema for .roomodes configuration files used by Roo Code to define custom modes."
|
||||
|
||||
return jsonSchema
|
||||
}
|
||||
6
pnpm-lock.yaml
generated
6
pnpm-lock.yaml
generated
|
|
@ -722,6 +722,9 @@ importers:
|
|||
'@types/node':
|
||||
specifier: ^24.1.0
|
||||
version: 24.2.1
|
||||
ajv:
|
||||
specifier: ^8.18.0
|
||||
version: 8.18.0
|
||||
globals:
|
||||
specifier: ^16.3.0
|
||||
version: 16.3.0
|
||||
|
|
@ -1104,9 +1107,6 @@ importers:
|
|||
ai:
|
||||
specifier: ^6.0.75
|
||||
version: 6.0.77(zod@3.25.76)
|
||||
ajv:
|
||||
specifier: ^8.18.0
|
||||
version: 8.18.0
|
||||
esbuild-wasm:
|
||||
specifier: ^0.25.0
|
||||
version: 0.25.12
|
||||
|
|
|
|||
|
|
@ -567,7 +567,6 @@
|
|||
"@vscode/test-electron": "^2.5.2",
|
||||
"@vscode/vsce": "3.3.2",
|
||||
"ai": "^6.0.75",
|
||||
"ajv": "^8.18.0",
|
||||
"esbuild-wasm": "^0.25.0",
|
||||
"execa": "^9.5.2",
|
||||
"glob": "^11.1.0",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue