diff --git a/src/engine/schema.ts b/src/engine/schema.ts new file mode 100644 index 0000000..050695a --- /dev/null +++ b/src/engine/schema.ts @@ -0,0 +1,98 @@ +import type { Template, Block } from '../types/template'; + +interface JSONSchema { + type: string; + properties: Record; + required?: string[]; +} + +export function extractSchema(template: Template): { schema: JSONSchema; example: any } { + const variables = new Set(); + const arrayVariables = new Set(); + + function extractFromString(str: string) { + const matches = str.match(/\{\{([^}]+)\}\}/g); + if (matches) { + matches.forEach(match => { + const varName = match.replace(/\{\{|\}\}/g, '').trim().split('.')[0]; + variables.add(varName); + }); + } + } + + function extractFromBlock(block: Block) { + switch (block.type) { + case 'text': + extractFromString(block.content); + break; + case 'row': + block.columns.forEach(col => { + if (col.content) extractFromString(col.content); + if (col.header) extractFromString(col.header); + }); + break; + case 'table': + extractFromString(block.data); + block.columns.forEach(col => { + if (col.header) extractFromString(col.header); + }); + break; + case 'list': + extractFromString(block.data); + block.itemTemplate.forEach(extractFromBlock); + break; + case 'divider': + break; + case 'image': + extractFromString(block.src); + break; + case 'barcode': + extractFromString(block.data); + break; + case 'space': + break; + } + } + + template.blocks.forEach(extractFromBlock); + + // 构建 JSON Schema + const properties: Record = {}; + + variables.forEach(varName => { + if (varName === 'items' || varName === 'tasks' || varName === 'tickets') { + properties[varName] = { type: 'array', description: `${varName} list` }; + arrayVariables.add(varName); + } else if (varName.endsWith('Count') || varName === 'quantity' || varName === 'total') { + properties[varName] = { type: 'number', description: varName }; + } else { + properties[varName] = { type: 'string', description: varName }; + } + }); + + const schema: JSONSchema = { + type: 'object', + properties + }; + + // 生成示例数据 + const example: any = {}; + variables.forEach(varName => { + if (arrayVariables.has(varName)) { + example[varName] = [ + { name: 'Item 1', value: 'value1' }, + { name: 'Item 2', value: 'value2' } + ]; + } else if (properties[varName].type === 'number') { + example[varName] = 42; + } else if (varName.includes('date') || varName.includes('Date')) { + example[varName] = '2025-02-12'; + } else if (varName.includes('time') || varName.includes('Time')) { + example[varName] = '14:30:00'; + } else { + example[varName] = varName; + } + }); + + return { schema, example }; +} diff --git a/tests/schema.test.ts b/tests/schema.test.ts new file mode 100644 index 0000000..834964c --- /dev/null +++ b/tests/schema.test.ts @@ -0,0 +1,57 @@ +import { describe, it, expect } from 'bun:test'; +import { extractSchema } from '../src/engine/schema'; +import type { Template } from '../src/types/template'; + +describe('Schema Extractor', () => { + it('should extract simple variables', () => { + const template: Template = { + id: 'test', + name: 'Test', + width: '80mm', + blocks: [ + { type: 'text', content: 'Hello {{name}}' }, + { type: 'text', content: 'Date: {{date}}' } + ] + }; + + const { schema } = extractSchema(template); + expect(schema.properties.name).toEqual({ type: 'string', description: 'name' }); + expect(schema.properties.date).toEqual({ type: 'string', description: 'date' }); + }); + + it('should extract list variables', () => { + const template: Template = { + id: 'test', + name: 'Test', + width: '80mm', + blocks: [ + { + type: 'list', + data: '{{items}}', + itemTemplate: [ + { type: 'text', content: '{{title}}' } + ] + } + ] + }; + + const { schema } = extractSchema(template); + expect(schema.properties.items).toEqual({ type: 'array', description: 'items list' }); + expect(schema.properties.title).toEqual({ type: 'string', description: 'title' }); + }); + + it('should generate example data', () => { + const template: Template = { + id: 'test', + name: 'Test', + width: '80mm', + blocks: [ + { type: 'text', content: '{{name}} - {{count}}' } + ] + }; + + const { example } = extractSchema(template); + expect(example).toHaveProperty('name'); + expect(example).toHaveProperty('count'); + }); +});