Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@contember/schema-utils

Package Overview
Dependencies
Maintainers
5
Versions
259
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@contember/schema-utils - npm Package Compare versions

Comparing version 1.3.0-alpha.6 to 1.3.0-alpha.7

dist/src/definition-generator/AclDefinitionCodeGenerator.d.ts

3

dist/src/definition-generator/index.d.ts
export * from './TsDefinitionGenerator';
export * from './AclDefinitionCodeGenerator';
export * from './DefinitionCodeGenerator';
export * from './DefinitionNamingConventions';
//# sourceMappingURL=index.d.ts.map

@@ -18,2 +18,5 @@ "use strict";

__exportStar(require("./TsDefinitionGenerator"), exports);
__exportStar(require("./AclDefinitionCodeGenerator"), exports);
__exportStar(require("./DefinitionCodeGenerator"), exports);
__exportStar(require("./DefinitionNamingConventions"), exports);
//# sourceMappingURL=index.js.map

22

dist/src/definition-generator/TsDefinitionGenerator.d.ts

@@ -1,26 +0,12 @@

import { Model, Schema } from '@contember/schema';
import { Schema } from '@contember/schema';
import { NamingConventions } from '../model';
/**
* @deprecated use {@link DefinitionCodeGenerator}
*/
export declare class TsDefinitionGenerator {
private readonly schema;
private readonly conventions;
private static reservedWords;
constructor(schema: Schema, conventions?: NamingConventions);
generate(): string;
private generateEnum;
generateEntity({ entity }: {
entity: Model.Entity;
}): string;
private generateUniqueConstraint;
private generateIndex;
private generateView;
generateField({ entity, field }: {
entity: Model.Entity;
field: Model.AnyField;
}): string | undefined;
private generateColumn;
private formatIdentifier;
private formatLiteral;
private isValidIdentifier;
private isSimpleIdentifier;
}
//# sourceMappingURL=TsDefinitionGenerator.d.ts.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TsDefinitionGenerator = void 0;
const schema_1 = require("@contember/schema");
const model_1 = require("../model");
const DefinitionCodeGenerator_1 = require("./DefinitionCodeGenerator");
/**
* @deprecated use {@link DefinitionCodeGenerator}
*/
class TsDefinitionGenerator {

@@ -12,236 +15,7 @@ constructor(schema, conventions = new model_1.DefaultNamingConventions()) {

generate() {
const enums = Object.entries(this.schema.model.enums).map(([name, values]) => this.generateEnum({
name,
values,
})).join('');
const entities = Object.values(this.schema.model.entities).map(entity => this.generateEntity({ entity })).join('');
return `import { SchemaDefinition as def } from '@contember/schema-definition'
${enums}${entities}`;
const generator = new DefinitionCodeGenerator_1.DefinitionCodeGenerator(this.conventions);
return generator.generate(this.schema);
}
generateEnum({ name, values }) {
return `\nexport const ${this.formatIdentifier(name)} = def.createEnum(${values.map(it => this.formatLiteral(it)).join(', ')})\n`;
}
generateEntity({ entity }) {
const decorators = [
...Object.values(entity.unique).map(constraint => this.generateUniqueConstraint({ entity, constraint })),
...Object.values(entity.indexes).map(index => this.generateIndex({ entity, index })),
this.generateView({ entity }),
].filter(it => !!it).map(it => `${it}\n`).join('');
return `\n${decorators}export class ${this.formatIdentifier(entity.name)} {
${Object.values(entity.fields).map(field => this.generateField({ field, entity })).filter(it => !!it).join('\n')}
}\n`;
}
generateUniqueConstraint({ entity, constraint }) {
const defaultName = model_1.NamingHelper.createUniqueConstraintName(entity.name, constraint.fields);
if (defaultName === constraint.name) {
const fieldsList = `${constraint.fields.map(it => this.formatLiteral(it)).join(', ')}`;
return `@def.Unique(${fieldsList})`;
}
return `@def.Unique(${this.formatLiteral(constraint)})`;
}
generateIndex({ entity, index }) {
const defaultName = model_1.NamingHelper.createIndexName(entity.name, index.fields);
if (defaultName === index.name) {
const fieldsList = `${index.fields.map(it => this.formatLiteral(it)).join(', ')}`;
return `@def.Index(${fieldsList})`;
}
return `@def.Index(${this.formatLiteral(index)})`;
}
generateView({ entity }) {
var _a, _b, _c;
if (!entity.view) {
return undefined;
}
const dependenciesExpr = ((_b = (_a = entity.view.dependencies) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0
? `, {\n\tdependencies: () => [${(_c = entity.view.dependencies) === null || _c === void 0 ? void 0 : _c.map(it => this.formatIdentifier(it))}]\n}`
: '';
return `@def.View(\`${entity.view.sql}\`${dependenciesExpr})`;
}
generateField({ entity, field }) {
const formatRelationFactory = (method, relation) => {
const otherSide = (0, model_1.isInverseRelation)(relation) ? relation.ownedBy : relation.inversedBy;
const otherSideFormatted = otherSide ? `, ${this.formatLiteral(otherSide)}` : '';
return `${method}(${this.formatIdentifier(relation.target)}${otherSideFormatted})`;
};
const formatEnumRef = (enumName, enumValues, providedValue, defaultValue) => {
var _a;
if (!providedValue || providedValue === defaultValue) {
return undefined;
}
const enumValueKey = (_a = Object.entries(schema_1.Model.OrderDirection).find(dir => dir[1] === providedValue)) === null || _a === void 0 ? void 0 : _a[0];
if (!enumValueKey) {
throw new Error(`Value ${providedValue} is not defined in enum ${enumName}`);
}
return `${enumName}.${enumValueKey}`;
};
const formatOrderBy = (orderBy) => {
var _a;
return (_a = orderBy === null || orderBy === void 0 ? void 0 : orderBy.map(it => {
const enumExpr = formatEnumRef(`Model.OrderDirection`, schema_1.Model.OrderDirection, it.direction, schema_1.Model.OrderDirection.asc);
return `orderBy(${this.formatLiteral(it.path)}${enumExpr ? `, ${enumExpr}` : ''})`;
})) !== null && _a !== void 0 ? _a : [];
};
const formatOnDelete = (onDelete) => {
if (onDelete === schema_1.Model.OnDelete.cascade) {
return 'cascadeOnDelete()';
}
if (onDelete === schema_1.Model.OnDelete.setNull) {
return 'setNullOnDelete()';
}
return undefined;
};
const formatJoiningColumn = (joiningColumnName, fieldName) => {
const defaultJoiningColumn = this.conventions.getJoiningColumnName(fieldName);
if (defaultJoiningColumn === joiningColumnName) {
return undefined;
}
return `joiningColumn(${this.formatLiteral(joiningColumnName)})`;
};
const definition = (0, model_1.acceptFieldVisitor)(this.schema.model, entity, field, {
visitColumn: ctx => {
return this.generateColumn(ctx);
},
visitOneHasMany: ({ relation }) => {
return [
formatRelationFactory('oneHasMany', relation),
...formatOrderBy(relation.orderBy),
];
},
visitManyHasOne: ({ relation }) => {
return [
formatRelationFactory('manyHasOne', relation),
!relation.nullable ? 'notNull()' : undefined,
formatOnDelete(relation.joiningColumn.onDelete),
formatJoiningColumn(relation.joiningColumn.columnName, relation.name),
];
},
visitOneHasOneInverse: ({ relation }) => {
return [
formatRelationFactory('oneHasOneInverse', relation),
!relation.nullable ? 'notNull()' : undefined,
];
},
visitOneHasOneOwning: ({ relation }) => {
return [
formatRelationFactory('oneHasOne', relation),
!relation.nullable ? 'notNull()' : undefined,
formatOnDelete(relation.joiningColumn.onDelete),
formatJoiningColumn(relation.joiningColumn.columnName, relation.name),
relation.orphanRemoval ? 'removeOrphan()' : undefined,
];
},
visitManyHasManyOwning: ({ entity, relation }) => {
const columnNames = this.conventions.getJoiningTableColumnNames(entity.name, relation.name, relation.target, relation.inversedBy);
const defaultJoiningTable = this.conventions.getJoiningTableName(entity.name, relation.name);
const joiningTable = {};
if (relation.joiningTable.tableName !== defaultJoiningTable) {
joiningTable.tableName = relation.joiningTable.tableName;
}
if (!relation.joiningTable.eventLog.enabled) {
joiningTable.eventLog = { enabled: false };
}
if (columnNames[0] !== relation.joiningTable.joiningColumn.columnName || relation.joiningTable.joiningColumn.onDelete !== schema_1.Model.OnDelete.cascade) {
joiningTable.joiningColumn = {
columnName: relation.joiningTable.joiningColumn.columnName,
onDelete: relation.joiningTable.joiningColumn.onDelete,
};
}
if (columnNames[1] !== relation.joiningTable.inverseJoiningColumn.columnName || relation.joiningTable.inverseJoiningColumn.onDelete !== schema_1.Model.OnDelete.cascade) {
joiningTable.inverseJoiningColumn = {
columnName: relation.joiningTable.inverseJoiningColumn.columnName,
onDelete: relation.joiningTable.inverseJoiningColumn.onDelete,
};
}
return [
formatRelationFactory('manyHasMany', relation),
...formatOrderBy(relation.orderBy),
Object.keys(joiningTable).length > 0 ? `joiningTable(${this.formatLiteral(joiningTable)})` : undefined,
];
},
visitManyHasManyInverse: ({ relation }) => {
return [
formatRelationFactory('manyHasManyInverse', relation),
...formatOrderBy(relation.orderBy),
];
},
});
const definitionCode = definition.filter(it => !!it).join('.');
if (field.name === 'id' && definitionCode === 'uuidColumn().notNull()') {
return undefined;
}
return `\t${this.formatIdentifier(field.name)} = def.${definitionCode}`;
}
generateColumn({ entity, column }) {
let parts = [];
if (column.type === schema_1.Model.ColumnType.Enum) {
parts.push(`enumColumn(${column.columnType})`);
}
else {
parts.push(`${ColumnToMethodMapping[column.type]}()`);
const defaultColumnType = (0, model_1.resolveDefaultColumnType)(column.type);
if (defaultColumnType !== column.columnType) {
parts.push(`columnType(${this.formatLiteral(column.columnType)})`);
}
}
const defaultColumnName = this.conventions.getColumnName(column.name);
if (defaultColumnName !== column.columnName) {
parts.push(`columnName(${this.formatLiteral(column.columnName)})`);
}
if (!column.nullable) {
parts.push('notNull()');
}
if (column.default !== undefined) {
parts.push(`default(${this.formatLiteral(column.default)})`);
}
if (column.typeAlias) {
parts.push(`typeAlias(${this.formatLiteral(column.typeAlias)})`);
}
// todo: sequence
// todo: maybe single column unique()
return parts;
}
formatIdentifier(id) {
// todo: validate
return id;
}
formatLiteral(value) {
if (value === undefined || value === null || typeof value === 'boolean' || typeof value === 'number' || typeof value === 'function') {
return String(value);
}
if (typeof value === 'bigint') {
return value.toString(10) + 'n';
}
if (typeof value === 'string') {
return `'${value.replaceAll(/'/g, '\\\'')}'`;
}
if (Array.isArray(value)) {
return `[${value.map(it => this.formatLiteral(it)).join(', ')}]`;
}
return `{${Object.entries(value).map(([key, value]) => {
const formattedKey = this.isSimpleIdentifier(key) ? key : `[${this.formatLiteral(key)}]`;
return `${formattedKey}: ${this.formatLiteral(value)}`;
}).join(', ')}`;
}
isValidIdentifier(identifier) {
if (!this.isSimpleIdentifier(identifier)) {
return false;
}
return !TsDefinitionGenerator.reservedWords.has(identifier);
}
isSimpleIdentifier(identifier) {
return !!identifier.match(/^[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*$/);
}
}
exports.TsDefinitionGenerator = TsDefinitionGenerator;
TsDefinitionGenerator.reservedWords = new Set(['do', 'if', 'in', 'for', 'let', 'new', 'try', 'var', 'case', 'else', 'enum', 'eval', 'null', 'this', 'true', 'void', 'with', 'await', 'break', 'catch', 'class', 'const', 'false', 'super', 'throw', 'while', 'yield', 'delete', 'export', 'import', 'public', 'return', 'static', 'switch', 'typeof', 'default', 'extends', 'finally', 'package', 'private', 'continue', 'debugger', 'function', 'arguments', 'interface', 'protected', 'implements', 'instanceof']);
const ColumnToMethodMapping = {
[schema_1.Model.ColumnType.Bool]: 'boolColumn',
[schema_1.Model.ColumnType.Date]: 'dateColumn',
[schema_1.Model.ColumnType.DateTime]: 'dateTimeColumn',
[schema_1.Model.ColumnType.Json]: 'jsonColumn',
[schema_1.Model.ColumnType.Double]: 'doubleColumn',
[schema_1.Model.ColumnType.Uuid]: 'uuidColumn',
[schema_1.Model.ColumnType.Int]: 'intColumn',
[schema_1.Model.ColumnType.String]: 'stringColumn',
};
//# sourceMappingURL=TsDefinitionGenerator.js.map

@@ -137,3 +137,3 @@ "use strict";

if (targetField.inversedBy !== field.name) {
return errors.add('MODEL_INVALID_RELATION_DEFINITION', `${relationDescription} back reference ${entityName}::${field.name} exepcted, ${targetField.target}::${targetField.inversedBy} given`);
return errors.add('MODEL_INVALID_RELATION_DEFINITION', `${relationDescription} back reference ${entityName}::${field.name} expected, ${targetField.target}::${targetField.inversedBy} given`);
}

@@ -176,3 +176,3 @@ if (field.type === schema_1.Model.RelationType.OneHasOne && targetField.type !== schema_1.Model.RelationType.OneHasOne) {

if (targetField.ownedBy !== field.name) {
return errors.add('MODEL_INVALID_RELATION_DEFINITION', `${relationDescription} back reference ${entityName}::${field.name} exepcted, ${targetField.target}::${targetField.ownedBy} given`);
return errors.add('MODEL_INVALID_RELATION_DEFINITION', `${relationDescription} back reference ${entityName}::${field.name} expected, ${targetField.target}::${targetField.ownedBy} given`);
}

@@ -179,0 +179,0 @@ if (field.type === schema_1.Model.RelationType.OneHasOne && targetField.type !== schema_1.Model.RelationType.OneHasOne) {

@@ -32,5 +32,6 @@ "use strict";

const enum_ = __importStar(require("./schemas/enum"));
const acl = __importStar(require("./schemas/acl"));
const promises_1 = require("fs/promises");
const path_1 = require("path");
const src_1 = require("../../../src");
const DefinitionCodeGenerator_1 = require("../../../src/definition-generator/DefinitionCodeGenerator");
const tests = [

@@ -41,2 +42,3 @@ ['basic', basic],

['enum', enum_],
['acl', acl],
];

@@ -46,5 +48,5 @@ for (const [name, def] of tests) {

const schema = (0, schema_definition_1.createSchema)(def);
const generator = new src_1.TsDefinitionGenerator(schema);
const generator = new DefinitionCodeGenerator_1.DefinitionCodeGenerator();
const content = await (0, promises_1.readFile)((0, path_1.join)(__dirname, `schemas/${name}.ts`), 'utf-8');
const generated = generator.generate();
const generated = generator.generate(schema);
try {

@@ -51,0 +53,0 @@ (0, vitest_1.expect)(generated).toBe(content);

{
"name": "@contember/schema-utils",
"version": "1.3.0-alpha.6",
"version": "1.3.0-alpha.7",
"license": "Apache-2.0",

@@ -15,8 +15,8 @@ "main": "dist/src/index.js",

"dependencies": {
"@contember/schema": "^1.3.0-alpha.6",
"@contember/typesafe": "^1.3.0-alpha.6",
"@contember/schema": "^1.3.0-alpha.7",
"@contember/typesafe": "^1.3.0-alpha.7",
"crypto-js": "^4.1.1"
},
"devDependencies": {
"@contember/schema-definition": "^1.3.0-alpha.6",
"@contember/schema-definition": "^1.3.0-alpha.7",
"@types/crypto-js": "^4.1.1",

@@ -23,0 +23,0 @@ "@types/node": "^18"

export * from './TsDefinitionGenerator'
export * from './AclDefinitionCodeGenerator'
export * from './DefinitionCodeGenerator'
export * from './DefinitionNamingConventions'

@@ -1,14 +0,9 @@

import { Model, Schema, Writable } from '@contember/schema'
import {
acceptFieldVisitor,
DefaultNamingConventions,
isInverseRelation,
NamingConventions,
NamingHelper,
resolveDefaultColumnType,
} from '../model'
import { Schema } from '@contember/schema'
import { DefaultNamingConventions, NamingConventions } from '../model'
import { DefinitionCodeGenerator } from './DefinitionCodeGenerator'
/**
* @deprecated use {@link DefinitionCodeGenerator}
*/
export class TsDefinitionGenerator {
private static reservedWords = new Set(['do', 'if', 'in', 'for', 'let', 'new', 'try', 'var', 'case', 'else', 'enum', 'eval', 'null', 'this', 'true', 'void', 'with', 'await', 'break', 'catch', 'class', 'const', 'false', 'super', 'throw', 'while', 'yield', 'delete', 'export', 'import', 'public', 'return', 'static', 'switch', 'typeof', 'default', 'extends', 'finally', 'package', 'private', 'continue', 'debugger', 'function', 'arguments', 'interface', 'protected', 'implements', 'instanceof'])
constructor(

@@ -21,256 +16,5 @@ private readonly schema: Schema,

public generate() {
const enums = Object.entries(this.schema.model.enums).map(([name, values]) => this.generateEnum({
name,
values,
})).join('')
const entities = Object.values(this.schema.model.entities).map(entity => this.generateEntity({ entity })).join('')
return `import { SchemaDefinition as def } from '@contember/schema-definition'
${enums}${entities}`
const generator = new DefinitionCodeGenerator(this.conventions)
return generator.generate(this.schema)
}
private generateEnum({ name, values }: { name: string; values: readonly string[] }): string {
return `\nexport const ${this.formatIdentifier(name)} = def.createEnum(${values.map(it => this.formatLiteral(it)).join(', ')})\n`
}
public generateEntity({ entity }: { entity: Model.Entity }): string {
const decorators = [
...Object.values(entity.unique).map(constraint => this.generateUniqueConstraint({ entity, constraint })),
...Object.values(entity.indexes).map(index => this.generateIndex({ entity, index })),
this.generateView({ entity }),
].filter(it => !!it).map(it => `${it}\n`).join('')
return `\n${decorators}export class ${this.formatIdentifier(entity.name)} {
${Object.values(entity.fields).map(field => this.generateField({ field, entity })).filter(it => !!it).join('\n')}
}\n`
}
private generateUniqueConstraint({ entity, constraint }: { entity: Model.Entity; constraint: Model.UniqueConstraint }): string {
const defaultName = NamingHelper.createUniqueConstraintName(entity.name, constraint.fields)
if (defaultName === constraint.name) {
const fieldsList = `${constraint.fields.map(it => this.formatLiteral(it)).join(', ')}`
return `@def.Unique(${fieldsList})`
}
return `@def.Unique(${this.formatLiteral(constraint)})`
}
private generateIndex({ entity, index }: { entity: Model.Entity; index: Model.Index }): string {
const defaultName = NamingHelper.createIndexName(entity.name, index.fields)
if (defaultName === index.name) {
const fieldsList = `${index.fields.map(it => this.formatLiteral(it)).join(', ')}`
return `@def.Index(${fieldsList})`
}
return `@def.Index(${this.formatLiteral(index)})`
}
private generateView({ entity }: { entity: Model.Entity }): string | undefined {
if (!entity.view) {
return undefined
}
const dependenciesExpr = (entity.view.dependencies?.length ?? 0) > 0
? `, {\n\tdependencies: () => [${entity.view.dependencies?.map(it => this.formatIdentifier(it))}]\n}`
: ''
return `@def.View(\`${entity.view.sql}\`${dependenciesExpr})`
}
public generateField({ entity, field }: { entity: Model.Entity; field: Model.AnyField }): string | undefined {
const formatRelationFactory = (method: string, relation: Model.AnyRelation) => {
const otherSide = isInverseRelation(relation) ? relation.ownedBy : relation.inversedBy
const otherSideFormatted = otherSide ? `, ${this.formatLiteral(otherSide)}` : ''
return `${method}(${this.formatIdentifier(relation.target)}${otherSideFormatted})`
}
const formatEnumRef = (enumName: string, enumValues: Record<string, string>, providedValue?: string, defaultValue?: string): string | undefined => {
if (!providedValue || providedValue === defaultValue) {
return undefined
}
const enumValueKey = Object.entries(Model.OrderDirection).find(dir => dir[1] === providedValue)?.[0]
if (!enumValueKey) {
throw new Error(`Value ${providedValue} is not defined in enum ${enumName}`)
}
return `${enumName}.${enumValueKey}`
}
const formatOrderBy = (orderBy?: readonly Model.OrderBy[]) => {
return orderBy?.map(it => {
const enumExpr = formatEnumRef(`Model.OrderDirection`, Model.OrderDirection, it.direction, Model.OrderDirection.asc)
return `orderBy(${this.formatLiteral(it.path)}${enumExpr ? `, ${enumExpr}` : ''})`
}) ?? []
}
const formatOnDelete = (onDelete?: Model.OnDelete): string | undefined => {
if (onDelete === Model.OnDelete.cascade) {
return 'cascadeOnDelete()'
}
if (onDelete === Model.OnDelete.setNull) {
return 'setNullOnDelete()'
}
return undefined
}
const formatJoiningColumn = (joiningColumnName: string, fieldName: string): string | undefined => {
const defaultJoiningColumn = this.conventions.getJoiningColumnName(fieldName)
if (defaultJoiningColumn === joiningColumnName) {
return undefined
}
return `joiningColumn(${this.formatLiteral(joiningColumnName)})`
}
const definition = acceptFieldVisitor<(string | undefined)[]>(this.schema.model, entity, field, {
visitColumn: ctx => {
return this.generateColumn(ctx)
},
visitOneHasMany: ({ relation }) => {
return [
formatRelationFactory('oneHasMany', relation),
...formatOrderBy(relation.orderBy),
]
},
visitManyHasOne: ({ relation }) => {
return [
formatRelationFactory('manyHasOne', relation),
!relation.nullable ? 'notNull()' : undefined,
formatOnDelete(relation.joiningColumn.onDelete),
formatJoiningColumn(relation.joiningColumn.columnName, relation.name),
]
},
visitOneHasOneInverse: ({ relation }) => {
return [
formatRelationFactory('oneHasOneInverse', relation),
!relation.nullable ? 'notNull()' : undefined,
]
},
visitOneHasOneOwning: ({ relation }) => {
return [
formatRelationFactory('oneHasOne', relation),
!relation.nullable ? 'notNull()' : undefined,
formatOnDelete(relation.joiningColumn.onDelete),
formatJoiningColumn(relation.joiningColumn.columnName, relation.name),
relation.orphanRemoval ? 'removeOrphan()' : undefined,
]
},
visitManyHasManyOwning: ({ entity, relation }) => {
const columnNames = this.conventions.getJoiningTableColumnNames(
entity.name,
relation.name,
relation.target,
relation.inversedBy,
)
const defaultJoiningTable = this.conventions.getJoiningTableName(entity.name, relation.name)
const joiningTable: Writable<Partial<Model.JoiningTable>> = {}
if (relation.joiningTable.tableName !== defaultJoiningTable) {
joiningTable.tableName = relation.joiningTable.tableName
}
if (!relation.joiningTable.eventLog.enabled) {
joiningTable.eventLog = { enabled: false }
}
if (columnNames[0] !== relation.joiningTable.joiningColumn.columnName || relation.joiningTable.joiningColumn.onDelete !== Model.OnDelete.cascade) {
joiningTable.joiningColumn = {
columnName: relation.joiningTable.joiningColumn.columnName,
onDelete: relation.joiningTable.joiningColumn.onDelete,
}
}
if (columnNames[1] !== relation.joiningTable.inverseJoiningColumn.columnName || relation.joiningTable.inverseJoiningColumn.onDelete !== Model.OnDelete.cascade) {
joiningTable.inverseJoiningColumn = {
columnName: relation.joiningTable.inverseJoiningColumn.columnName,
onDelete: relation.joiningTable.inverseJoiningColumn.onDelete,
}
}
return [
formatRelationFactory('manyHasMany', relation),
...formatOrderBy(relation.orderBy),
Object.keys(joiningTable).length > 0 ? `joiningTable(${this.formatLiteral(joiningTable)})` : undefined,
]
},
visitManyHasManyInverse: ({ relation }) => {
return [
formatRelationFactory('manyHasManyInverse', relation),
...formatOrderBy(relation.orderBy),
]
},
})
const definitionCode = definition.filter(it => !!it).join('.')
if (field.name === 'id' && definitionCode === 'uuidColumn().notNull()') {
return undefined
}
return `\t${this.formatIdentifier(field.name)} = def.${definitionCode}`
}
private generateColumn({ entity, column }: { entity: Model.Entity; column: Model.AnyColumn }): string[] {
let parts: string[] = []
if (column.type === Model.ColumnType.Enum) {
parts.push(`enumColumn(${column.columnType})`)
} else {
parts.push(`${ColumnToMethodMapping[column.type]}()`)
const defaultColumnType = resolveDefaultColumnType(column.type)
if (defaultColumnType !== column.columnType) {
parts.push(`columnType(${this.formatLiteral(column.columnType)})`)
}
}
const defaultColumnName = this.conventions.getColumnName(column.name)
if (defaultColumnName !== column.columnName) {
parts.push(`columnName(${this.formatLiteral(column.columnName)})`)
}
if (!column.nullable) {
parts.push('notNull()')
}
if (column.default !== undefined) {
parts.push(`default(${this.formatLiteral(column.default)})`)
}
if (column.typeAlias) {
parts.push(`typeAlias(${this.formatLiteral(column.typeAlias)})`)
}
// todo: sequence
// todo: maybe single column unique()
return parts
}
private formatIdentifier(id: string): string {
// todo: validate
return id
}
private formatLiteral(value: any): string {
if (value === undefined || value === null || typeof value === 'boolean' || typeof value === 'number' || typeof value === 'function') {
return String(value)
}
if (typeof value === 'bigint') {
return value.toString(10) + 'n'
}
if (typeof value === 'string') {
return `'${value.replaceAll(/'/g, '\\\'')}'`
}
if (Array.isArray(value)) {
return `[${value.map(it => this.formatLiteral(it)).join(', ')}]`
}
return `{${Object.entries(value).map(([key, value]) => {
const formattedKey = this.isSimpleIdentifier(key) ? key : `[${this.formatLiteral(key)}]`
return `${formattedKey}: ${this.formatLiteral(value)}`
}).join(', ')}`
}
private isValidIdentifier(identifier: string): boolean {
if (!this.isSimpleIdentifier(identifier)) {
return false
}
return !TsDefinitionGenerator.reservedWords.has(identifier)
}
private isSimpleIdentifier(identifier: string): boolean {
return !!identifier.match(/^[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*$/)
}
}
const ColumnToMethodMapping: {
[K in Exclude<Model.ColumnType, Model.ColumnType.Enum>]: string
} = {
[Model.ColumnType.Bool]: 'boolColumn',
[Model.ColumnType.Date]: 'dateColumn',
[Model.ColumnType.DateTime]: 'dateTimeColumn',
[Model.ColumnType.Json]: 'jsonColumn',
[Model.ColumnType.Double]: 'doubleColumn',
[Model.ColumnType.Uuid]: 'uuidColumn',
[Model.ColumnType.Int]: 'intColumn',
[Model.ColumnType.String]: 'stringColumn',
}

@@ -10,3 +10,3 @@ import { Model } from '@contember/schema'

export class ModelValidator {
constructor(private readonly model: Model.Schema) {}
constructor(private readonly model: Model.Schema) { }

@@ -83,3 +83,3 @@ public validate(): ValidationError[] {

private validateRelation(partialEntity: Model.Entity, field: Model.AnyRelation, errors: ErrorBuilder): void {
private validateRelation(partialEntity: Model.Entity, field: Model.AnyRelation, errors: ErrorBuilder): void {
const entityName = partialEntity.name

@@ -153,3 +153,3 @@ const targetEntityName = field.target

if (targetField.inversedBy !== field.name) {
return errors.add('MODEL_INVALID_RELATION_DEFINITION', `${relationDescription} back reference ${entityName}::${field.name} exepcted, ${targetField.target}::${targetField.inversedBy} given`)
return errors.add('MODEL_INVALID_RELATION_DEFINITION', `${relationDescription} back reference ${entityName}::${field.name} expected, ${targetField.target}::${targetField.inversedBy} given`)
}

@@ -191,3 +191,3 @@ if (field.type === Model.RelationType.OneHasOne && targetField.type !== Model.RelationType.OneHasOne) {

if (targetField.ownedBy !== field.name) {
return errors.add('MODEL_INVALID_RELATION_DEFINITION', `${relationDescription} back reference ${entityName}::${field.name} exepcted, ${targetField.target}::${targetField.ownedBy} given`)
return errors.add('MODEL_INVALID_RELATION_DEFINITION', `${relationDescription} back reference ${entityName}::${field.name} expected, ${targetField.target}::${targetField.ownedBy} given`)
}

@@ -266,7 +266,7 @@ if (field.type === Model.RelationType.OneHasOne && targetField.type !== Model.RelationType.OneHasOne) {

},
visitManyHasManyInverse: () => {},
visitOneHasMany: () => {},
visitOneHasOneInverse: () => {},
visitOneHasOneOwning: () => {},
visitManyHasOne: () => {},
visitManyHasManyInverse: () => { },
visitOneHasMany: () => { },
visitOneHasOneInverse: () => { },
visitOneHasOneOwning: () => { },
visitManyHasOne: () => { },
})

@@ -273,0 +273,0 @@ }

@@ -1,2 +0,2 @@

import { SchemaDefinition as def } from '@contember/schema-definition'
import { SchemaDefinition as def, AclDefinition as acl } from '@contember/schema-definition'

@@ -3,0 +3,0 @@ export class Article {

@@ -1,2 +0,2 @@

import { SchemaDefinition as def } from '@contember/schema-definition'
import { SchemaDefinition as def, AclDefinition as acl } from '@contember/schema-definition'

@@ -3,0 +3,0 @@ export const articleState = def.createEnum('draft', 'published')

@@ -1,2 +0,2 @@

import { SchemaDefinition as def } from '@contember/schema-definition'
import { SchemaDefinition as def, AclDefinition as acl } from '@contember/schema-definition'

@@ -3,0 +3,0 @@ export class Article {

@@ -1,2 +0,2 @@

import { SchemaDefinition as def } from '@contember/schema-definition'
import { SchemaDefinition as def, AclDefinition as acl } from '@contember/schema-definition'

@@ -3,0 +3,0 @@ @def.Unique('title')

import { createSchema } from '@contember/schema-definition'
import { test, expect } from 'vitest'
import { expect, test } from 'vitest'
import * as basic from './schemas/basic'

@@ -7,5 +7,6 @@ import * as relations from './schemas/relations'

import * as enum_ from './schemas/enum'
import * as acl from './schemas/acl'
import { readFile, writeFile } from 'fs/promises'
import { join } from 'path'
import { TsDefinitionGenerator } from '../../../src'
import { DefinitionCodeGenerator } from '../../../src/definition-generator/DefinitionCodeGenerator'

@@ -18,2 +19,3 @@

['enum', enum_],
['acl', acl],
] as const

@@ -23,5 +25,5 @@ for (const [name, def] of tests) {

const schema = createSchema(def)
const generator = new TsDefinitionGenerator(schema)
const generator = new DefinitionCodeGenerator()
const content = await readFile(join(__dirname, `schemas/${name}.ts`), 'utf-8')
const generated = generator.generate()
const generated = generator.generate(schema)
try {

@@ -28,0 +30,0 @@ expect(generated).toBe(content)

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc