feat: add automatic schema extraction from templates

This commit is contained in:
Developer 2026-02-12 08:01:10 +00:00
parent 6cc292d61a
commit 400b716623
2 changed files with 155 additions and 0 deletions

98
src/engine/schema.ts Normal file
View File

@ -0,0 +1,98 @@
import type { Template, Block } from '../types/template';
interface JSONSchema {
type: string;
properties: Record<string, any>;
required?: string[];
}
export function extractSchema(template: Template): { schema: JSONSchema; example: any } {
const variables = new Set<string>();
const arrayVariables = new Set<string>();
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<string, any> = {};
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 };
}

57
tests/schema.test.ts Normal file
View File

@ -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');
});
});