feat: add automatic schema extraction from templates
This commit is contained in:
parent
6cc292d61a
commit
400b716623
98
src/engine/schema.ts
Normal file
98
src/engine/schema.ts
Normal 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
57
tests/schema.test.ts
Normal 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');
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user