kysely-codegen
Version:
`kysely-codegen` generates Kysely type definitions from your database. That's it.
311 lines • 13.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.transform = void 0;
const alias_declaration_node_1 = require("../ast/alias-declaration-node");
const array_expression_node_1 = require("../ast/array-expression-node");
const export_statement_node_1 = require("../ast/export-statement-node");
const generic_expression_node_1 = require("../ast/generic-expression-node");
const identifier_node_1 = require("../ast/identifier-node");
const import_clause_node_1 = require("../ast/import-clause-node");
const import_statement_node_1 = require("../ast/import-statement-node");
const interface_declaration_node_1 = require("../ast/interface-declaration-node");
const literal_node_1 = require("../ast/literal-node");
const object_expression_node_1 = require("../ast/object-expression-node");
const property_node_1 = require("../ast/property-node");
const raw_expression_node_1 = require("../ast/raw-expression-node");
const runtime_enum_declaration_node_1 = require("../ast/runtime-enum-declaration-node");
const union_expression_node_1 = require("../ast/union-expression-node");
const postgres_dialect_1 = require("../dialects/postgres/postgres-dialect");
const case_converter_1 = require("../utils/case-converter");
const definitions_1 = require("./definitions");
const imports_1 = require("./imports");
const symbol_collection_1 = require("./symbol-collection");
const collectSymbol = (name, context) => {
const definition = context.definitions[name];
if (definition) {
if (context.symbols.has(name)) {
return;
}
context.symbols.set(name, { node: definition, type: 'Definition' });
collectSymbols(definition, context);
return;
}
const moduleReference = context.imports[name];
if (moduleReference) {
if (context.symbols.has(name)) {
return;
}
context.symbols.set(name, {
node: moduleReference,
type: 'ModuleReference',
});
}
};
const collectSymbols = (node, context) => {
switch (node.type) {
case 'ArrayExpression':
collectSymbols(node.values, context);
break;
case 'ExtendsClause':
collectSymbols(node.extendsType, context);
collectSymbols(node.trueType, context);
collectSymbols(node.falseType, context);
break;
case 'GenericExpression': {
collectSymbol(node.name, context);
for (const arg of node.args) {
collectSymbols(arg, context);
}
break;
}
case 'Identifier':
collectSymbol(node.name, context);
break;
case 'InferClause':
break;
case 'Literal':
break;
case 'MappedType':
collectSymbols(node.value, context);
break;
case 'ObjectExpression':
for (const property of node.properties) {
collectSymbols(property.value, context);
}
break;
case 'RawExpression':
collectSymbol(node.expression, context);
break;
case 'Template':
collectSymbols(node.expression, context);
break;
case 'UnionExpression':
for (const arg of node.args) {
collectSymbols(arg, context);
}
break;
}
};
const createContext = (options) => {
return {
camelCase: !!options.camelCase,
defaultScalar: options.dialect.adapter.defaultScalar ?? new identifier_node_1.IdentifierNode('unknown'),
defaultSchemas: options.defaultSchemas && options.defaultSchemas.length > 0
? options.defaultSchemas
: options.dialect.adapter.defaultSchemas,
definitions: {
...definitions_1.GLOBAL_DEFINITIONS,
...options.dialect.adapter.definitions,
},
dialect: options.dialect,
enums: options.metadata.enums,
imports: {
...imports_1.GLOBAL_IMPORTS,
...options.dialect.adapter.imports,
},
metadata: options.metadata,
overrides: options.overrides,
runtimeEnums: options.runtimeEnums ?? false,
scalars: {
...options.dialect.adapter.scalars,
},
symbols: new symbol_collection_1.SymbolCollection(),
};
};
const createDatabaseExportNode = (context) => {
const tableProperties = [];
for (const table of context.metadata.tables) {
const identifier = getTableIdentifier(table, context);
const symbolName = context.symbols.getName(identifier);
if (symbolName) {
const value = new identifier_node_1.TableIdentifierNode(symbolName);
const tableProperty = new property_node_1.PropertyNode(identifier, value);
tableProperties.push(tableProperty);
}
}
tableProperties.sort((a, b) => a.key.localeCompare(b.key));
const body = new object_expression_node_1.ObjectExpressionNode(tableProperties);
const argument = new interface_declaration_node_1.InterfaceDeclarationNode(new identifier_node_1.IdentifierNode('DB'), body);
return new export_statement_node_1.ExportStatementNode(argument);
};
const createRuntimeEnumDefinitionNodes = (context) => {
const exportStatements = [];
for (const { symbol } of context.symbols.entries()) {
if (symbol.type !== 'RuntimeEnumDefinition') {
continue;
}
const exportStatement = new export_statement_node_1.ExportStatementNode(symbol.node);
exportStatements.push(exportStatement);
}
return exportStatements.sort((a, b) => {
return a.argument.id.name.localeCompare(b.argument.id.name);
});
};
const createDefinitionNodes = (context) => {
const definitionNodes = [];
for (const { name, symbol } of context.symbols.entries()) {
if (symbol.type !== 'Definition') {
continue;
}
const argument = new alias_declaration_node_1.AliasDeclarationNode(name, symbol.node);
const definitionNode = new export_statement_node_1.ExportStatementNode(argument);
definitionNodes.push(definitionNode);
}
return definitionNodes.sort((a, b) => a.argument.id.name.localeCompare(b.argument.id.name));
};
const createImportNodes = (context) => {
var _a;
const imports = {};
const importNodes = [];
for (const { id, name, symbol } of context.symbols.entries()) {
if (symbol.type !== 'ModuleReference') {
continue;
}
(imports[_a = symbol.node.name] ?? (imports[_a] = [])).push(new import_clause_node_1.ImportClauseNode(id, name === id ? null : name));
}
for (const [moduleName, symbolImports] of Object.entries(imports)) {
importNodes.push(new import_statement_node_1.ImportStatementNode(moduleName, symbolImports));
}
return importNodes.sort((a, b) => a.moduleName.localeCompare(b.moduleName));
};
const getTableIdentifier = (table, context) => {
const name = table.schema &&
context.defaultSchemas.length > 0 &&
!context.defaultSchemas.includes(table.schema)
? `${table.schema}.${table.name}`
: table.name;
return transformName(name, context);
};
const transformColumn = ({ column, context, table, }) => {
const overrides = context.overrides?.columns;
const isDefaultSchema = !!table.schema && context.defaultSchemas.includes(table.schema);
const path = `${table.name}.${column.name}`;
const override = context.dialect instanceof postgres_dialect_1.PostgresDialect
? isDefaultSchema
? (overrides?.[`${table.schema}.${path}`] ?? overrides?.[path])
: overrides?.[`${table.schema}.${path}`]
: overrides?.[path];
if (override !== undefined) {
const node = typeof override === 'string' ? new raw_expression_node_1.RawExpressionNode(override) : override;
collectSymbols(node, context);
return node;
}
let args = transformColumnToArgs(column, context);
if (column.isArray) {
const unionizedArgs = unionize(args);
const isSimpleNode = unionizedArgs.type === 'Identifier' &&
['boolean', 'number', 'string'].includes(unionizedArgs.name);
args = isSimpleNode
? [new array_expression_node_1.ArrayExpressionNode(unionizedArgs)]
: [new generic_expression_node_1.GenericExpressionNode('ArrayType', [unionizedArgs])];
}
if (column.isNullable) {
args.push(new identifier_node_1.IdentifierNode('null'));
}
let node = unionize(args);
const isGenerated = column.hasDefaultValue || column.isAutoIncrementing;
if (isGenerated) {
node = new generic_expression_node_1.GenericExpressionNode('Generated', [node]);
}
collectSymbols(node, context);
return node;
};
const transformColumnToArgs = (column, context) => {
const dataType = column.dataType.toLowerCase();
const scalarNode = context.scalars[dataType];
if (scalarNode) {
return [scalarNode];
}
// Used as a unique identifier for the data type:
const dataTypeId = `${column.dataTypeSchema ?? context.defaultSchemas}.${dataType}`;
// Used for serializing the name of the symbol:
const symbolId = column.dataTypeSchema &&
context.defaultSchemas.length > 0 &&
!context.defaultSchemas.includes(column.dataTypeSchema)
? `${column.dataTypeSchema}.${dataType}`
: dataType;
const enumValues = context.enums.get(dataTypeId);
if (enumValues) {
if (context.runtimeEnums) {
const symbol = {
node: new runtime_enum_declaration_node_1.RuntimeEnumDeclarationNode(symbolId, enumValues, {
identifierStyle: context.runtimeEnums === 'screaming-snake-case'
? 'screaming-snake-case'
: 'kysely-pascal-case',
}),
type: 'RuntimeEnumDefinition',
};
symbol.node.id.name = context.symbols.set(symbolId, symbol);
const node = new identifier_node_1.IdentifierNode(symbol.node.id.name);
return [node];
}
const symbolName = context.symbols.set(symbolId, {
node: unionize(transformEnum(enumValues)),
type: 'Definition',
});
const node = new identifier_node_1.IdentifierNode(symbolName);
return [node];
}
const symbolName = context.symbols.getName(symbolId);
if (symbolName) {
const node = new identifier_node_1.IdentifierNode(symbolName ?? 'unknown');
return [node];
}
if (column.enumValues) {
return transformEnum(column.enumValues);
}
return [context.defaultScalar];
};
const transformEnum = (enumValues) => {
return enumValues.map((enumValue) => new literal_node_1.LiteralNode(enumValue));
};
const transformName = (name, context) => {
return context.camelCase ? (0, case_converter_1.toKyselyCamelCase)(name) : name;
};
const transformTables = (context) => {
const tableNodes = [];
for (const table of context.metadata.tables) {
const tableProperties = [];
for (const column of table.columns) {
const key = transformName(column.name, context);
const value = transformColumn({ column, context, table });
const comment = column.comment;
const tableProperty = new property_node_1.PropertyNode(key, value, comment);
tableProperties.push(tableProperty);
}
const expression = new object_expression_node_1.ObjectExpressionNode(tableProperties);
const identifier = getTableIdentifier(table, context);
const symbolName = context.symbols.set(identifier, { type: 'Table' });
const tableNode = new export_statement_node_1.ExportStatementNode(new interface_declaration_node_1.InterfaceDeclarationNode(new identifier_node_1.TableIdentifierNode(symbolName), expression));
tableNodes.push(tableNode);
}
tableNodes.sort((a, b) => a.argument.id.name.localeCompare(b.argument.id.name));
return tableNodes;
};
const unionize = (args) => {
switch (args.length) {
case 0:
return new identifier_node_1.IdentifierNode('never');
case 1:
return args[0];
default:
return new union_expression_node_1.UnionExpressionNode(args);
}
};
const transform = (options) => {
const context = createContext(options);
const tableNodes = transformTables(context);
const importNodes = createImportNodes(context);
const runtimeEnumDefinitionNodes = createRuntimeEnumDefinitionNodes(context);
const definitionNodes = createDefinitionNodes(context);
const databaseNode = createDatabaseExportNode(context);
return [
...importNodes,
...runtimeEnumDefinitionNodes,
...definitionNodes,
...tableNodes,
databaseNode,
];
};
exports.transform = transform;
//# sourceMappingURL=transformer.js.map