@cwqt/refract
Advanced tools
Comparing version 1.0.2 to 1.0.3
@@ -28,2 +28,3 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
const Types = __importStar(require("../types")); | ||
const utils_1 = require("../types/utils"); | ||
const column = (column) => { | ||
@@ -46,3 +47,3 @@ if (Types.Fields.isRaw(column)) | ||
.map(m => (0, modifiers_1.modifier)(column.type, m)) | ||
.join(' ')}`; | ||
.join(' ')}`.trimEnd(); | ||
}; | ||
@@ -55,18 +56,20 @@ const primitive = (column) => { | ||
.map(m => (0, modifiers_1.modifier)(column.type, m)) | ||
.join(' ')}`; | ||
.join(' ')}`.trimEnd(); | ||
}; | ||
const relationship = (column) => { | ||
if (column.type == 'ManyToOne') { | ||
if (column.type == 'ManyToOne' || column.type == 'OneToOne') { | ||
const isNullable = column.modifiers.find(({ type }) => type == 'nullable'); | ||
const [model, fields, references, ...modifiers] = column.modifiers; | ||
const isNullable = column.modifiers.find(({ type }) => type == 'nullable'); | ||
return `\t${column.name} ${model.value.name}${isNullable ? '?' : ''} @relation(fields: [${fields.value.join(', ')}], references: [${references.value.join(', ')}])`; | ||
if (column.type == 'OneToOne') { | ||
if (fields?.type !== 'fields' || references?.type !== 'references') { | ||
return `\t${column.name} ${(0, utils_1.isString)(model.value) ? model.value : model.value.name}${isNullable ? '?' : ''}`; | ||
} | ||
} | ||
return `\t${column.name} ${(0, utils_1.isString)(model.value) ? model.value : model.value.name}${isNullable ? '?' : ''} @relation(fields: [${fields.value.join(', ')}], references: [${references.value.join(', ')}])`.trimEnd(); | ||
} | ||
if (column.type == 'OneToMany') { | ||
const [model, ...modifiers] = column.modifiers; | ||
return `\t${column.name} ${model.value.name}[]`; | ||
return `\t${column.name} ${(0, utils_1.isString)(model.value) ? model.value : model.value.name}[]`; | ||
} | ||
if (column.type == 'OneToOne') { | ||
const isNullable = column.modifiers.find(({ type }) => type == 'nullable'); | ||
} | ||
return ''; | ||
}; |
import * as Types from '../types'; | ||
declare const _default: (config: Types.Config) => string; | ||
declare type CodegenResult = { | ||
schema: string; | ||
time: number; | ||
output: string; | ||
}; | ||
declare const _default: (config: Types.Config) => CodegenResult; | ||
export default _default; |
@@ -31,39 +31,19 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
const utils_1 = require("../types/utils"); | ||
const dedent_1 = require("./dedent"); | ||
const dedent_1 = require("./lib/dedent"); | ||
const types_1 = require("../types"); | ||
const align_1 = require("./align"); | ||
exports.default = (config) => { | ||
const start = perf_hooks_1.performance.now(); | ||
(0, types_1.validate)(config); | ||
config.schema = config.schema.map(model => (['Field', 'Relation'].forEach(v => (0, utils_1.del)(model, v)), model)); | ||
config = (0, types_1.validate)(config); | ||
const datasource = config.datasource; | ||
const generators = config.generators; | ||
const enums = config.schema.filter(Types.Blocks.isEnum); | ||
const models = config.schema.filter(Types.Blocks.isModel); | ||
const generators = config.generators; | ||
const datasource = config.datasource; | ||
const group = (header, blocks) => blocks.length == 0 ? null : [header, blocks.join('\n\n')].join('\n\n'); | ||
const schema = (0, dedent_1.dedent)([ | ||
(0, block_1.header)('datasource'), | ||
(0, block_1.block)('datasource db', (0, transform_1.kv)(datasource)), | ||
generators.length | ||
? [ | ||
(0, block_1.header)('generators'), | ||
config.generators | ||
.map(generator => (0, block_1.block)(`generator ${generator.name}`, (0, transform_1.kv)((0, utils_1.del)(generator, 'name')))) | ||
.join('\n'), | ||
] | ||
: null, | ||
enums.length | ||
? [ | ||
(0, block_1.header)('enums'), | ||
enums | ||
.map(e => (0, block_1.block)(`enum ${e.name}`, e.columns.map(c => `\t${c.name}`).join(',\n'))) | ||
.join('\n\n'), | ||
] | ||
: null, | ||
models.length | ||
? [ | ||
(0, block_1.header)('models'), | ||
models | ||
.map(model => (0, block_1.block)(`model ${model.name}`, model.columns.map(column_1.column).join('\n'))) | ||
.join('\n\n'), | ||
] | ||
: null, | ||
(0, block_1.block)('datasource db', (0, align_1.align)((0, transform_1.kv)(datasource), '=')), | ||
group((0, block_1.header)('generators'), generators.map(generator => (0, block_1.block)(`generator ${generator.name}`, (0, align_1.align)((0, transform_1.kv)((0, utils_1.del)(generator, 'name')), '=')))), | ||
group((0, block_1.header)('enums'), enums.map(e => (0, block_1.block)(`enum ${e.name}`, e.columns.map(c => `\t${c.name}`).join('\n')))), | ||
group((0, block_1.header)('models'), models.map(model => (0, block_1.block)(`model ${model.name}`, (0, align_1.align)(model.columns.map(column_1.column).join('\n'))))), | ||
] | ||
@@ -74,6 +54,8 @@ .filter(utils_1.nonNullable) | ||
const end = perf_hooks_1.performance.now(); | ||
return [ | ||
(0, block_1.header)(`refract 1.0.1 - generated in ${(end - start).toFixed(3)} ms`), | ||
schema, | ||
].join('\n'); | ||
const time = Number((end - start).toFixed(3)); | ||
return { | ||
time, | ||
output: config.output, | ||
schema: [(0, block_1.header)(`refract 1.0.3 - generated in ${time} ms`), schema].join('\n'), | ||
}; | ||
}; |
@@ -7,3 +7,3 @@ Object.defineProperty(exports, "__esModule", { value: true }); | ||
case 'default': | ||
return `@default(${(0, transform_1.transform)(modifier.value)})`; | ||
return `@default(${type == 'Enum' ? modifier.value : (0, transform_1.transform)(modifier.value)})`; | ||
case 'index': | ||
@@ -10,0 +10,0 @@ return `@id`; |
@@ -24,4 +24,3 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
const promises_1 = require("fs/promises"); | ||
const path_1 = __importDefault(require("path")); | ||
const codegen_1 = __importDefault(require("./codegen")); | ||
exports.default = (config) => (file => (0, promises_1.writeFile)(config.output || path_1.default.join(process.cwd(), 'schema.prisma'), file, 'utf8'))((0, codegen_1.default)(config)); | ||
exports.default = (config) => (({ schema, output, time }) => (0, promises_1.writeFile)(output, schema, 'utf8').then(() => console.log(`Created schema at: ${output} (${time} ms)`)))((0, codegen_1.default)(config)); |
@@ -7,3 +7,3 @@ import * as Types from '../types'; | ||
constructor(name: string, keys: K); | ||
_call(initial: K[number] | null, ...modifiers: Types.Modifier<'Enum'>[]): Types.Fields.Field<'Enum'>; | ||
_call(initial?: K[number] | null, ...modifiers: Types.Modifier<'Enum'>[]): Types.Fields.Field<'Enum'>; | ||
} |
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Enum = void 0; | ||
const utils_1 = require("../types/utils"); | ||
class Enum extends Function { | ||
@@ -26,5 +27,7 @@ name; | ||
? { type: 'default', value: initial } | ||
: { type: 'nullable', value: true }, | ||
: initial === null | ||
? { type: 'nullable', value: true } | ||
: null, | ||
...modifiers, | ||
].filter((v, i, a) => a.findIndex(m => m.type == v.type) === i), | ||
].filter((v, i, a) => (0, utils_1.nonNullable)(v) && a.findIndex(m => m.type == v.type) === i), | ||
}; | ||
@@ -31,0 +34,0 @@ } |
import * as Types from '../types'; | ||
export declare const Enum: <K extends readonly string[]>(name: string, keys: K) => ((initial: K[number], ...modifiers: Types.Modifier<'Enum'>[]) => Types.Fields.Field<'Enum'>) & Types.Blocks.Enum; | ||
import { Model } from './model'; | ||
export declare const Enum: <K extends readonly string[]>(name: string, keys: K) => ((initial?: K[number], ...modifiers: Types.Modifier<'Enum'>[]) => Types.Fields.Field<'Enum'>) & Types.Blocks.Enum; | ||
export declare const Int: (...modifiers: Types.Modifier<'Int'>[]) => Types.Fields.Field<"Int">; | ||
export declare const Varchar: (...modifiers: Types.Modifier<'Varchar'>[]) => Types.Fields.Field<'Varchar'>; | ||
export declare const Boolean: (...modifiers: Types.Modifier<'Boolean'>[]) => Types.Fields.Field<'Boolean'>; | ||
export declare const Varchar: (...modifiers: Types.Modifier<'Varchar'>[]) => Types.Fields.Field<"Varchar">; | ||
export declare const Boolean: (...modifiers: Types.Modifier<'Boolean'>[]) => Types.Fields.Field<"Boolean">; | ||
export declare const Json: (...modifiers: Types.Modifier<'Json'>[]) => Types.Fields.Field<"Json">; | ||
export declare const DateTime: (...modifiers: Types.Modifier<'DateTime'>[]) => Types.Fields.Field<'DateTime'>; | ||
export declare const OneToMany: <M extends Types.Blocks.Model>(model: M, ...modifiers: Types.Modifier<'OneToMany'>[]) => Types.Fields.Field<'OneToMany'>; | ||
export declare const OneToMany: <M extends Types.Blocks.Model>(model: M, ...modifiers: Types.Modifier<'OneToMany'>[]) => Types.Fields.Field<"OneToMany">; | ||
declare type RelationFn<T extends Types.Blocks.Model> = (m: T | string) => Relation; | ||
export declare type Relation = { | ||
@@ -12,5 +15,7 @@ fields: string[]; | ||
}; | ||
export declare const Pk: (...fields: string[]) => { | ||
Fk: (...references: string[]) => (model: Types.Blocks.Model) => Relation; | ||
export declare const Pk: (...references: string[]) => { | ||
Fk: (...fields: string[]) => (model: Types.Blocks.Model | string) => Relation; | ||
}; | ||
export declare const ManyToOne: <M extends Types.Blocks.Model>(model: M, relation: (m: M) => Relation, ...modifiers: Types.Modifier<'ManyToOne'>[]) => Types.Fields.Field<'ManyToOne'>; | ||
export declare const ManyToOne: <M extends Types.Blocks.Model>(model: string | M, relation: RelationFn<M>, ...modifiers: Types.Modifier<'ManyToOne'>[]) => Types.Fields.Field<"ManyToOne">; | ||
export declare const OneToOne: <M extends Types.Blocks.Model>(model: M, ...modifiers: [RelationFn<M>, ...Types.Modifier<"OneToOne", "model" | "nullable" | "fields" | "references">[]] | Types.Modifier<"OneToOne", "model" | "nullable" | "fields" | "references">[]) => Types.Fields.Field<"OneToOne">; | ||
export {}; |
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ManyToOne = exports.Pk = exports.OneToMany = exports.DateTime = exports.Boolean = exports.Varchar = exports.Int = exports.Enum = void 0; | ||
exports.OneToOne = exports.ManyToOne = exports.Pk = exports.OneToMany = exports.DateTime = exports.Json = exports.Boolean = exports.Varchar = exports.Int = exports.Enum = void 0; | ||
const utils_1 = require("../types/utils"); | ||
const enum_1 = require("./enum"); | ||
@@ -12,2 +13,4 @@ const Enum = (name, keys) => new enum_1.Enum(name, keys); | ||
exports.Boolean = Boolean; | ||
const Json = (...modifiers) => ({ type: 'Json', modifiers }); | ||
exports.Json = Json; | ||
const DateTime = (...modifiers) => ({ type: 'DateTime', modifiers }); | ||
@@ -20,9 +23,6 @@ exports.DateTime = DateTime; | ||
exports.OneToMany = OneToMany; | ||
const Pk = (...fields) => { | ||
const Pk = (...references) => { | ||
return { | ||
Fk: (...references) => { | ||
Fk: (...fields) => { | ||
return (model) => { | ||
const missing = fields.filter(r => !model.columns.map(c => c.name).includes(r)); | ||
if (missing.length) | ||
throw new Error(`RelationshipErr: Referenced columns in 'fields' don't exist in Model '${model.name}': ${missing.map(m => `'${m}'`).join(', ')}`); | ||
return { fields, references }; | ||
@@ -56,1 +56,22 @@ }; | ||
exports.ManyToOne = ManyToOne; | ||
const OneToOne = (model, ...modifiers) => { | ||
const relation = modifiers.shift(); | ||
const relations = (0, utils_1.isFn)(relation) | ||
? (0, utils_1.entries)(relation(model)).map(([key, value]) => ({ | ||
type: key, | ||
value, | ||
})) | ||
: [relation]; | ||
return { | ||
type: 'OneToOne', | ||
modifiers: [ | ||
{ | ||
type: 'model', | ||
value: model, | ||
}, | ||
...relations, | ||
...modifiers, | ||
].filter(utils_1.nonNullable), | ||
}; | ||
}; | ||
exports.OneToOne = OneToOne; |
@@ -9,2 +9,12 @@ import * as Types from '../types'; | ||
export declare const Model: (name: string) => Model; | ||
export declare class CallableModel implements Types.Blocks.Model, Model { | ||
name: string; | ||
type: 'model'; | ||
columns: Types.Column<keyof Types.TypeData>[]; | ||
constructor(name: string); | ||
Mixin(mixin: Types.Mixin): this; | ||
Raw(value: string): this; | ||
Relation(name: string, type: Types.Fields.Field<Types.Fields.Relation>): this; | ||
Field(name: string, type: Types.Fields.Field<Types.Fields.Primitive>): this; | ||
} | ||
export {}; |
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Model = void 0; | ||
const Model = (name) => { | ||
const model = { type: 'model', name, columns: [] }; | ||
const Mixin = (mixin) => { | ||
model.columns.push(...mixin.columns); | ||
return { Mixin, Raw, Field, Relation, ...model }; | ||
}; | ||
const Raw = (value) => { | ||
exports.CallableModel = exports.Model = void 0; | ||
const Model = (name) => new CallableModel(name); | ||
exports.Model = Model; | ||
class CallableModel { | ||
name; | ||
type = 'model'; | ||
columns = []; | ||
constructor(name) { | ||
this.name = name; | ||
} | ||
Mixin(mixin) { | ||
this.columns.push(...mixin.columns); | ||
return this; | ||
} | ||
Raw(value) { | ||
const modifier = { type: 'value', value }; | ||
@@ -16,21 +23,18 @@ const column = { | ||
}; | ||
model.columns.push(column); | ||
return { Mixin, Raw, Field, Relation, ...model }; | ||
}; | ||
const Relation = (name, type) => { | ||
this.columns.push(column); | ||
return this; | ||
} | ||
Relation(name, type) { | ||
if (type.type == 'ManyToOne') { | ||
const references = type.modifiers[2]; | ||
const missing = references.value.filter(f => !model.columns.map(c => c.name).includes(f)); | ||
if (missing.length) | ||
throw new Error(`RelationshipErr: Referenced columns in 'references' don't exist in Model '${model.name}': ${missing.map(m => `'${m}'`).join(', ')}`); | ||
const missing = references.value.filter(f => !this.columns.map(c => c.name).includes(f)); | ||
} | ||
model.columns.push({ name, ...type }); | ||
return { Mixin, Raw, Field, Relation, ...model }; | ||
}; | ||
const Field = (name, type) => { | ||
model.columns.push({ name, ...type }); | ||
return { Mixin, Raw, Field, Relation, ...model }; | ||
}; | ||
return { Mixin, Raw, Field, Relation, ...model }; | ||
}; | ||
exports.Model = Model; | ||
this.columns.push({ name, ...type }); | ||
return this; | ||
} | ||
Field(name, type) { | ||
this.columns.push({ name, ...type }); | ||
return this; | ||
} | ||
} | ||
exports.CallableModel = CallableModel; |
@@ -23,3 +23,3 @@ import { Block } from './blocks'; | ||
}; | ||
export declare const validate: (config: Config) => void; | ||
export declare const validate: (config: Config) => Config; | ||
export {}; |
@@ -0,3 +1,7 @@ | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.validate = void 0; | ||
const path_1 = __importDefault(require("path")); | ||
const validate = (config) => { | ||
@@ -8,3 +12,7 @@ if (config.datasource.referentialIntegrity) { | ||
} | ||
return { | ||
...config, | ||
output: config.output || path_1.default.join(process.cwd(), 'schema.prisma'), | ||
}; | ||
}; | ||
exports.validate = validate; |
@@ -8,3 +8,3 @@ import { Column } from './columns'; | ||
}; | ||
export declare type Primitive = 'Int' | 'Varchar' | 'Boolean' | 'DateTime' | 'Enum'; | ||
export declare type Primitive = 'Int' | 'Varchar' | 'Boolean' | 'DateTime' | 'Enum' | 'Json'; | ||
export declare type Relation = 'OneToMany' | 'OneToOne' | 'ManyToOne'; | ||
@@ -11,0 +11,0 @@ export declare type Any = Primitive | Relation | 'Raw'; |
@@ -12,3 +12,3 @@ Object.defineProperty(exports, "__esModule", { value: true }); | ||
function isRelation(column) { | ||
return ['OneToMany', 'ManyToOne', 'ManyToOne'].includes(column.type); | ||
return ['OneToMany', 'ManyToOne', 'OneToOne'].includes(column.type); | ||
} | ||
@@ -15,0 +15,0 @@ exports.isRelation = isRelation; |
@@ -0,1 +1,2 @@ | ||
import { JsonValue } from '../codegen/lib/json'; | ||
import { Model } from './blocks'; | ||
@@ -26,2 +27,6 @@ export declare type TypeData = { | ||
}; | ||
Json: { | ||
nullable?: true; | ||
default?: JsonValue; | ||
}; | ||
Raw: { | ||
@@ -36,12 +41,14 @@ value: string; | ||
OneToMany: { | ||
model: Model; | ||
model: Model | string; | ||
nullable?: true; | ||
}; | ||
OneToOne: { | ||
model: Model; | ||
model: Model | string; | ||
nullable?: true; | ||
fields: string[]; | ||
references: string[]; | ||
}; | ||
ManyToOne: { | ||
nullable?: true; | ||
model: Model; | ||
model: Model | string; | ||
fields: string[]; | ||
@@ -48,0 +55,0 @@ references: string[]; |
export declare const isDate: (v: any) => v is Date; | ||
export declare const isFn: <F extends Function>(v: any) => v is F; | ||
export declare const isString: (v: any) => v is string; | ||
declare type Entries<T> = { | ||
[K in keyof T]: [K, T[K]]; | ||
}[keyof T][]; | ||
export declare const entries: <T>(obj: T) => Entries<T>; | ||
export declare type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never; | ||
export declare const del: <T extends Object, K extends string | keyof T>(object: T, key: K) => Exclude<T, K>; | ||
export declare function nonNullable<T>(value: T): value is NonNullable<T>; | ||
export declare const shift: <T extends readonly any[]>(v: T) => T[0]; | ||
export {}; |
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.nonNullable = exports.del = exports.isDate = void 0; | ||
exports.shift = exports.nonNullable = exports.del = exports.entries = exports.isString = exports.isFn = exports.isDate = void 0; | ||
const isDate = (v) => v instanceof Date && !isNaN(v); | ||
exports.isDate = isDate; | ||
const isFn = (v) => typeof v == 'function'; | ||
exports.isFn = isFn; | ||
const isString = (v) => typeof v == 'string'; | ||
exports.isString = isString; | ||
const entries = (obj) => Object.entries(obj); | ||
exports.entries = entries; | ||
const del = (object, key) => (delete object[key], object); | ||
@@ -11,1 +17,3 @@ exports.del = del; | ||
exports.nonNullable = nonNullable; | ||
const shift = (v) => v.shift(); | ||
exports.shift = shift; |
{ | ||
"name": "@cwqt/refract", | ||
"version": "1.0.2", | ||
"version": "1.0.3", | ||
"description": "A TypeScript CDK for Prisma", | ||
@@ -5,0 +5,0 @@ "author": "cwqt", |
@@ -5,6 +5,12 @@ # refract | ||
* Only tested on `postgres` | ||
* Doesn't have all features, yet | ||
* Made in two weekends while drinking | ||
### Installation | ||
```sh | ||
yarn add @cwqt/refract | ||
# First create a refract.ts file (see below example) | ||
# Create a refract.ts file (see example) | ||
# Then to generate the Prisma schema | ||
@@ -19,3 +25,3 @@ npx ts-node refract.ts | ||
```ts | ||
// inspired from from: https://www.prisma.io/docs/concepts/components/prisma-schema#example | ||
// example from: https://www.prisma.io/docs/concepts/components/prisma-schema#example | ||
const Role = Enum('Role', ['USER', 'ADMIN'] as const); | ||
@@ -43,3 +49,3 @@ | ||
.Field("authorId", Int(Nullable)) | ||
.Relation("author", ManyToOne(User, Fields("id").Refs("authorId"), Nullable)) | ||
.Relation("author", ManyToOne(User, Pk("id").Fk("authorId"), Nullable)) | ||
.Mixin(Timestamps) | ||
@@ -77,1 +83,33 @@ .Raw(`@@map("comments")`); | ||
``` | ||
### Handling circular relationships | ||
At some point you'll wanna split the schema across files, which introduces issues circular relationships when you're importing for `.Relation()`s | ||
One way to get around this is to have a file with all the models/enums defined, and have files import those & apply the fields, e.g. | ||
```ts | ||
// models.ts | ||
const User = Model("User"); | ||
const Post = Model("Posts"); | ||
// ... and all the other Models | ||
// users.ts | ||
import { User, Post } from './models' | ||
User | ||
.Field("id", Int()) | ||
.Relation("posts", OneToMany(Post)) | ||
// posts.ts | ||
import { User, Post } from './models' | ||
Post | ||
.Field("id", Int()) | ||
.Field("authorId", Int()) | ||
.Relation("author", ManyToOne(User, Pk("id").Fk("authorId"))) | ||
``` | ||
 | ||
Another way is to use a `string` instead of the model as the 1st argument of the Relation type, e.g. `.Relation("posts", OneToMany("Posts"))`. |
268827
53
823
112