stackpress
Version:
Incept is a content management framework.
242 lines (241 loc) • 8.61 kB
JavaScript
import Create from '@stackpress/inquire/Create';
export const typemap = {
String: 'string',
Text: 'text',
Number: 'number',
Integer: 'number',
Float: 'number',
Boolean: 'boolean',
Date: 'date',
Datetime: 'datetime',
Time: 'time',
Json: 'json',
Object: 'json',
Hash: 'json'
};
export default function schema(model, onDelete = 'CASCADE', onUpdate = 'RESTRICT') {
const schema = new Create(model.snake);
for (const column of model.columns.values()) {
field(column, schema);
}
for (const column of model.ids) {
schema.addPrimaryKey(column.snake);
}
for (const column of model.uniques) {
schema.addUniqueKey(`${column.snake}_unique`, column.snake);
}
for (const column of model.indexables) {
schema.addKey(`${model.lower}_${column.snake}_index`, column.snake);
}
const relations = model.relations.map(column => {
const table = column.model?.snake;
const foreign = column.relation?.parent.key.snake;
const local = column.relation?.child.key.snake;
return { table, foreign, local, delete: onDelete, update: onUpdate };
});
for (const relation of relations) {
schema.addForeignKey(`${model.snake}_${relation.local}_foreign`, relation);
}
return schema;
}
;
export function field(column, schema) {
const type = typemap[column.type];
if (!type && !column.fieldset && !column.enum) {
return;
}
const comment = column.attribute('comment')?.[0];
if (column.multiple) {
let hasDefault = false;
try {
hasDefault = typeof column.default === 'string'
&& Array.isArray(JSON.parse(column.default));
}
catch (e) { }
return schema.addField(column.snake, {
type: 'JSON',
default: hasDefault ? column.default : undefined,
nullable: !column.required,
comment: comment ? String(comment) : undefined
});
}
else if (type === 'json' || column.fieldset) {
let hasDefault = false;
try {
hasDefault = typeof column.default === 'string'
&& !!column.default
&& typeof JSON.parse(column.default) === 'object';
}
catch (e) { }
return schema.addField(column.snake, {
type: 'JSON',
default: hasDefault ? column.default : undefined,
nullable: !column.required,
comment: comment ? String(comment) : undefined
});
}
else if (type === 'string') {
const length = clen(column);
const hasDefault = typeof column.attributes.default === 'string'
&& !column.attributes.default.startsWith('uuid(')
&& !column.attributes.default.startsWith('cuid(')
&& !column.attributes.default.startsWith('nanoid(');
return schema.addField(column.snake, {
type: length[0] === length[1] ? 'CHAR' : 'VARCHAR',
length: length[1],
default: hasDefault ? column.attributes.default : undefined,
nullable: !column.required,
comment: comment ? String(comment) : undefined
});
}
else if (type === 'text') {
return schema.addField(column.snake, {
type: 'TEXT',
default: column.attributes.default,
nullable: !column.required,
comment: comment ? String(comment) : undefined
});
}
else if (type === 'boolean') {
return schema.addField(column.snake, {
type: 'BOOLEAN',
default: column.attributes.default,
nullable: !column.required,
comment: comment ? String(comment) : undefined
});
}
else if (type === 'number') {
const { minmax, integerLength, decimalLength } = numdata(column);
if (decimalLength > 0) {
const length = integerLength + decimalLength;
return schema.addField(column.snake, {
type: 'FLOAT',
length: [length, decimalLength],
default: column.attributes.default,
nullable: !column.required,
unsigned: minmax[0] < 0,
comment: comment ? String(comment) : undefined
});
}
else {
return schema.addField(column.snake, {
type: 'INTEGER',
length: integerLength,
default: column.attributes.default,
nullable: !column.required,
unsigned: minmax[0] < 0,
comment: comment ? String(comment) : undefined
});
}
}
else if (type === 'date') {
return schema.addField(column.snake, {
type: 'DATE',
default: column.attributes.default,
nullable: !column.required,
comment: comment ? String(comment) : undefined
});
}
else if (type === 'datetime') {
return schema.addField(column.snake, {
type: 'DATETIME',
default: column.attributes.default,
nullable: !column.required,
comment: comment ? String(comment) : undefined
});
}
else if (type === 'time') {
return schema.addField(column.snake, {
type: 'TIME',
default: column.attributes.default,
nullable: !column.required,
comment: comment ? String(comment) : undefined
});
}
else if (column.enum) {
return schema.addField(column.snake, {
type: 'VARCHAR',
length: 255,
default: column.attributes.default,
nullable: !column.required,
comment: comment ? String(comment) : undefined
});
}
}
export function clen(column) {
const length = [0, 255];
column.assertions.forEach(assertion => {
if (assertion.method === 'ceq') {
length[0] = assertion.args[0];
length[1] = assertion.args[0];
}
else if (assertion.method === 'cgt') {
length[0] = assertion.args[0];
}
else if (assertion.method === 'clt') {
length[1] = assertion.args[0];
}
else if (assertion.method === 'cge') {
length[0] = assertion.args[0];
}
else if (assertion.method === 'cle') {
length[1] = assertion.args[0];
}
});
if (length[1] < 1) {
length[1] = 255;
}
return length;
}
export function numdata(column) {
const minmax = [0, 0];
column.assertions.forEach(assertion => {
if (assertion.method === 'eq') {
minmax[0] = assertion.args[0];
minmax[1] = assertion.args[0];
}
else if (assertion.method === 'gt') {
minmax[0] = assertion.args[0];
}
else if (assertion.method === 'lt') {
minmax[1] = assertion.args[0];
}
else if (assertion.method === 'ge') {
minmax[0] = assertion.args[0];
}
else if (assertion.method === 'le') {
minmax[1] = assertion.args[0];
}
});
const step = Array.isArray(column.attributes.step)
? column.attributes.step[0]
: column.type.toLowerCase() === 'float'
? 10000000.01
: 0;
const stepIntegerLength = step.toString().split('.')[0].length;
const stepDecimalLength = (step.toString().split('.')[1] || '').length;
if (minmax[1] === 0) {
const minDecimalLength = (minmax[0].toString().split('.')[1] || '').length;
const maxDecimalLength = (minmax[1].toString().split('.')[1] || '').length;
const decimalLength = Math.max(minDecimalLength, maxDecimalLength, stepDecimalLength);
minmax[1] = Number('1' + '0'.repeat(9 - decimalLength));
}
const minIntegerLength = minmax[0].toString().split('.')[0].length;
const maxIntegerLength = minmax[1].toString().split('.')[0].length;
const minDecimalLength = (minmax[0].toString().split('.')[1] || '').length;
const maxDecimalLength = (minmax[1].toString().split('.')[1] || '').length;
const integerLength = Math.max(minIntegerLength, maxIntegerLength, stepIntegerLength);
const decimalLength = Math.max(minDecimalLength, maxDecimalLength, stepDecimalLength);
return {
step,
minmax,
minIntegerLength,
maxIntegerLength,
minDecimalLength,
maxDecimalLength,
stepIntegerLength,
stepDecimalLength,
integerLength,
decimalLength
};
}