UNPKG

@liam-hq/cli

Version:

Command-line tool designed to generate a web application that displays ER diagrams. See https://liambx.com/docs/cli

1,334 lines (1,323 loc) 3.54 MB
#!/usr/bin/env node import { createRequire } from 'node:module'; import { Command } from 'commander'; import { parseSync } from '@swc/core'; import pkg from '@prisma/internals'; import { readFile } from 'node:fs/promises'; import { fileURLToPath, URL as URL$1 } from 'node:url'; import tty from 'node:tty'; import fs, { existsSync, mkdirSync, cpSync } from 'node:fs'; import path, { resolve, dirname, relative } from 'node:path'; import { glob } from 'glob'; import { exit } from 'node:process'; import inquirer from 'inquirer'; import { Transform, render, Text } from 'ink'; import require$$0 from 'os'; import require$$1 from 'tty'; /** * Type definitions for Drizzle ORM MySQL schema parsing */ /** * Type guard to check if a value is an object */ const isObject$1 = (value) => { return typeof value === 'object' && value !== null; }; /** * Safe property checker without type casting */ const hasProperty$1 = (obj, key) => { return typeof obj === 'object' && obj !== null && key in obj; }; /** * Safe property getter without type casting */ const getPropertyValue$1 = (obj, key) => { if (hasProperty$1(obj, key)) { return obj[key]; } return undefined; }; /** * Type guard for CompositePrimaryKeyDefinition */ const isCompositePrimaryKey$1 = (value) => { return (isObject$1(value) && getPropertyValue$1(value, 'type') === 'primaryKey' && hasProperty$1(value, 'columns') && Array.isArray(getPropertyValue$1(value, 'columns'))); }; /** * Type guard for DrizzleIndexDefinition */ const isDrizzleIndex$1 = (value) => { return (isObject$1(value) && hasProperty$1(value, 'name') && hasProperty$1(value, 'columns') && hasProperty$1(value, 'unique')); }; /** * AST manipulation utilities for Drizzle ORM MySQL schema parsing */ /** * Type guard for SWC Argument wrapper */ const isArgumentWrapper$1 = (arg) => { return isObject$1(arg) && hasProperty$1(arg, 'expression'); }; /** * Extract expression from SWC Argument wrapper */ const getArgumentExpression$1 = (arg) => { if (isArgumentWrapper$1(arg)) { return arg.expression; } return null; }; /** * Type guard for string literal expressions */ const isStringLiteral$1 = (expr) => { return (isObject$1(expr) && getPropertyValue$1(expr, 'type') === 'StringLiteral' && hasProperty$1(expr, 'value') && typeof getPropertyValue$1(expr, 'value') === 'string'); }; /** * Type guard for object expressions */ const isObjectExpression$1 = (expr) => { return isObject$1(expr) && getPropertyValue$1(expr, 'type') === 'ObjectExpression'; }; /** * Type guard for array expressions */ const isArrayExpression$1 = (expr) => { return (isObject$1(expr) && getPropertyValue$1(expr, 'type') === 'ArrayExpression' && hasProperty$1(expr, 'elements') && Array.isArray(getPropertyValue$1(expr, 'elements'))); }; /** * Type guard for identifier nodes */ const isIdentifier$1 = (node) => { return (isObject$1(node) && getPropertyValue$1(node, 'type') === 'Identifier' && hasProperty$1(node, 'value') && typeof getPropertyValue$1(node, 'value') === 'string'); }; /** * Check if a node is an identifier with a specific name */ const isIdentifierWithName$1 = (node, name) => { return isIdentifier$1(node) && node.value === name; }; /** * Type guard for member expressions */ const isMemberExpression$1 = (node) => { return (isObject$1(node) && getPropertyValue$1(node, 'type') === 'MemberExpression' && hasProperty$1(node, 'object') && hasProperty$1(node, 'property') && typeof getPropertyValue$1(node, 'object') === 'object' && typeof getPropertyValue$1(node, 'property') === 'object'); }; /** * Check if a call expression is a mysqlTable call */ const isMysqlTableCall = (callExpr) => { return isIdentifierWithName$1(callExpr.callee, 'mysqlTable'); }; /** * Check if a call expression is a mysqlSchema call */ const isMysqlSchemaCall = (callExpr) => { return isIdentifierWithName$1(callExpr.callee, 'mysqlSchema'); }; /** * Check if a call expression is a schema.table() call */ const isSchemaTableCall$1 = (callExpr) => { return (isMemberExpression$1(callExpr.callee) && isIdentifier$1(callExpr.callee.property) && callExpr.callee.property.value === 'table'); }; /** * Extract string value from a string literal */ const getStringValue$1 = (node) => { if (node.type === 'StringLiteral') { return node.value; } return null; }; /** * Extract identifier name */ const getIdentifierName$1 = (node) => { if (isIdentifier$1(node)) { return node.value; } return null; }; /** * Parse method call chain from a call expression */ const parseMethodChain$1 = (expr) => { const methods = []; let current = expr; while (current.type === 'CallExpression') { if (current.callee.type === 'MemberExpression' && current.callee.property.type === 'Identifier') { methods.unshift({ name: current.callee.property.value, args: current.arguments, }); current = current.callee.object; } else { break; } } return methods; }; /** * Convert Drizzle column types to MySQL column types * ref: https://orm.drizzle.team/docs/column-types/mysql */ const convertDrizzleTypeToMysqlType = (drizzleType, options) => { switch (drizzleType) { // String types with length options case 'varchar': if (options?.['length']) { return `varchar(${options['length']})`; } return 'varchar'; case 'char': if (options?.['length']) { return `char(${options['length']})`; } return 'char'; // Numeric types with precision/scale case 'decimal': case 'numeric': if (options?.['precision'] && options?.['scale']) { return `decimal(${options['precision']},${options['scale']})`; } if (options?.['precision']) { return `decimal(${options['precision']})`; } return 'decimal'; // Enum type - handled as is case 'mysqlEnum': return 'enum'; // Serial type (PostgreSQL compatibility in MySQL context) case 'serial': return 'int'; // Default case: return type name as-is (works for most standard MySQL types) default: return drizzleType; } }; /** * Convert default values from Drizzle to MySQL format */ const convertDefaultValue$1 = (value, _drizzleType) => { if (value === undefined || value === null) { return null; } // Handle function calls like defaultNow(), autoincrement() if (typeof value === 'string') { if (value === 'defaultNow' || value === 'now()') { return 'now()'; } if (value === 'autoincrement' || value === 'autoincrement()') { return 'autoincrement()'; } if (value === 'defaultRandom') { // Note: MySQL's UUID() generates UUID v1 (time-based), not truly random like PostgreSQL's gen_random_uuid() return 'UUID()'; } } // Handle primitive values if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { return value; } return null; }; /** * Convert constraint reference options from Drizzle to MySQL format */ const convertReferenceOption$1 = (option) => { switch (option.toLowerCase()) { case 'cascade': return 'CASCADE'; case 'restrict': return 'RESTRICT'; case 'setnull': case 'set null': return 'SET_NULL'; case 'setdefault': case 'set default': return 'SET_DEFAULT'; default: return 'NO_ACTION'; } }; /** * Data conversion logic for Drizzle ORM MySQL schema parsing */ /** * Convert Drizzle table definition to internal Table format */ const convertToTable$1 = (tableDef, enums = {}, variableToTableMapping = {}) => { const columns = {}; const constraints = {}; const indexes = {}; // Convert columns for (const [columnName, columnDef] of Object.entries(tableDef.columns)) { // Check if this is an enum type and get the actual enum name let columnType = columnDef.type; // Check if this is an enum variable name (like userRoleEnum -> user_role) for (const [enumVarName, enumDef] of Object.entries(enums)) { if (columnDef.type === enumVarName) { columnType = enumDef.name; break; } } // If not found, it might be a call to an enum function (like roleEnum('role')) // In this case, the type is already the enum name from the first argument if (columnType === columnDef.type) { // Check if any enum definition matches this type name for (const enumDef of Object.values(enums)) { if (enumDef.name === columnDef.type) { columnType = enumDef.name; break; } } } const column = { name: columnDef.name, type: convertDrizzleTypeToMysqlType(columnType, columnDef.typeOptions), default: convertDefaultValue$1(columnDef.default || (columnType === 'int' && columnDef.primaryKey ? 'autoincrement()' : undefined)), notNull: columnDef.notNull, comment: columnDef.comment || null, check: null, }; columns[columnName] = column; // Add primary key constraint if (columnDef.primaryKey) { const constraintName = `PRIMARY_${columnDef.name}`; constraints[constraintName] = { type: 'PRIMARY KEY', name: constraintName, columnNames: [columnDef.name], }; // Add primary key index const indexName = `${tableDef.name}_pkey`; indexes[indexName] = { name: indexName, columns: [columnDef.name], unique: true, type: '', }; } // Add unique constraint (inline unique does not create index, only constraint) if (columnDef.unique && !columnDef.primaryKey) { const constraintName = `UNIQUE_${columnDef.name}`; constraints[constraintName] = { type: 'UNIQUE', name: constraintName, columnNames: [columnDef.name], }; } // Add foreign key constraint if (columnDef.references) { // Resolve variable name to actual table name const targetTableName = variableToTableMapping[columnDef.references.table] || columnDef.references.table; const constraintName = `${tableDef.name}_${columnDef.name}_${columnDef.references.table}_${columnDef.references.column}_fk`; const constraint = { type: 'FOREIGN KEY', name: constraintName, columnNames: [columnDef.name], // Use actual column name, not JS property name targetTableName: targetTableName, targetColumnNames: [columnDef.references.column], updateConstraint: columnDef.references.onUpdate ? convertReferenceOption$1(columnDef.references.onUpdate) : 'NO_ACTION', deleteConstraint: columnDef.references.onDelete ? convertReferenceOption$1(columnDef.references.onDelete) : 'NO_ACTION', }; constraints[constraintName] = constraint; } } // Handle composite primary key if (tableDef.compositePrimaryKey) { // Map JS property names to actual column names const actualColumnNames = tableDef.compositePrimaryKey.columns .map((jsPropertyName) => { const columnDef = tableDef.columns[jsPropertyName]; return columnDef ? columnDef.name : jsPropertyName; }) .filter((name) => name && name.length > 0); // Create composite primary key constraint const constraintName = `${tableDef.name}_pkey`; constraints[constraintName] = { type: 'PRIMARY KEY', name: constraintName, columnNames: actualColumnNames, }; // Add composite primary key index indexes[constraintName] = { name: constraintName, columns: actualColumnNames, unique: true, type: '', }; } // Convert indexes for (const [_, indexDef] of Object.entries(tableDef.indexes)) { // Map JS property names to actual column names const actualColumnNames = indexDef.columns.map((jsPropertyName) => { const columnDef = tableDef.columns[jsPropertyName]; return columnDef ? columnDef.name : jsPropertyName; }); // Use the actual index name from the definition const actualIndexName = indexDef.name; indexes[actualIndexName] = { name: actualIndexName, columns: actualColumnNames, unique: indexDef.unique, type: indexDef.type || '', }; } // Convert constraints from Drizzle table definition if (tableDef.constraints) { for (const [constraintName, constraintDef] of Object.entries(tableDef.constraints)) { constraints[constraintName] = constraintDef; } } return { name: tableDef.name, columns, constraints, indexes, comment: tableDef.comment || null, }; }; /** * Fix foreign key constraint targetColumnName from JS property names to actual DB column names */ const fixForeignKeyTargetColumnNames$1 = (tables, drizzleTables) => { for (const table of Object.values(tables)) { for (const constraint of Object.values(table.constraints)) { if (constraint.type === 'FOREIGN KEY') { // Check in drizzleTables for column mapping const drizzleTargetTable = drizzleTables[constraint.targetTableName]; if (drizzleTargetTable) { // Map each target column from JS property name to actual DB column name constraint.targetColumnNames = constraint.targetColumnNames.map((jsPropertyName) => { const targetColumnDef = drizzleTargetTable.columns[jsPropertyName]; return targetColumnDef ? targetColumnDef.name : jsPropertyName; }); } } } } }; /** * Convert parsed Drizzle tables to internal format with error handling */ const convertDrizzleTablesToInternal$1 = (drizzleTables, enums, variableToTableMapping = {}) => { const tables = {}; const errors = []; // Convert Drizzle tables to internal format for (const tableDef of Object.values(drizzleTables)) { try { // Use original table name for the key, not schema-prefixed name // This maintains compatibility while handling schema namespaces internally tables[tableDef.name] = convertToTable$1(tableDef, enums, variableToTableMapping); } catch (error) { errors.push(new Error(`Error parsing table ${tableDef.name}: ${error instanceof Error ? error.message : String(error)}`)); } } // Fix foreign key constraint targetColumnName from JS property names to actual DB column names fixForeignKeyTargetColumnNames$1(tables, drizzleTables); return { tables, errors }; }; /** * Enum definition parsing for Drizzle ORM MySQL schema parsing */ /** * Parse mysqlEnum call expression */ const parseMysqlEnumCall = (callExpr) => { if (callExpr.arguments.length < 2) return null; const enumNameArg = callExpr.arguments[0]; const valuesArg = callExpr.arguments[1]; if (!enumNameArg || !valuesArg) return null; // Extract expression from SWC argument structure const enumNameExpr = getArgumentExpression$1(enumNameArg); const valuesExpr = getArgumentExpression$1(valuesArg); const enumName = enumNameExpr ? getStringValue$1(enumNameExpr) : null; if (!enumName || !valuesExpr || !isArrayExpression$1(valuesExpr)) return null; const values = []; for (const element of valuesExpr.elements) { if (isStringLiteral$1(element)) { values.push(element.value); } } return { name: enumName, values }; }; /** * Schema parsing for Drizzle ORM MySQL schema parsing */ /** * Parse mysqlSchema call expression */ const parseMysqlSchemaCall = (callExpr) => { if (callExpr.arguments.length < 1) return null; const schemaNameArg = callExpr.arguments[0]; if (!schemaNameArg) return null; // Extract expression from SWC argument structure const schemaNameExpr = getArgumentExpression$1(schemaNameArg); const schemaName = schemaNameExpr ? getStringValue$1(schemaNameExpr) : null; if (!schemaName) return null; return { name: schemaName, }; }; /** * Expression parsing utilities for Drizzle ORM MySQL schema parsing */ /** * Parse special function/identifier values */ const parseSpecialValue = (value) => { switch (value) { case 'defaultNow': return 'now()'; case 'defaultRandom': return 'defaultRandom'; default: return value; } }; /** * Parse default value from expression */ const parseDefaultValue$1 = (expr) => { switch (expr.type) { case 'StringLiteral': return expr.value; case 'NumericLiteral': return expr.value; case 'BooleanLiteral': return expr.value; case 'NullLiteral': return null; case 'Identifier': return parseSpecialValue(expr.value); case 'CallExpression': // Handle function calls like defaultNow() if (expr.callee.type === 'Identifier') { return parseSpecialValue(expr.callee.value); } return undefined; default: return undefined; } }; /** * Parse object expression to plain object */ const parseObjectExpression$1 = (obj) => { const result = {}; for (const prop of obj.properties) { if (prop.type === 'KeyValueProperty') { const key = prop.key.type === 'Identifier' ? getIdentifierName$1(prop.key) : prop.key.type === 'StringLiteral' ? getStringValue$1(prop.key) : null; if (key) { result[key] = parsePropertyValue$1(prop.value); } } } return result; }; /** * Type guard for expression-like objects */ const isExpressionLike$1 = (value) => { return (isObject$1(value) && hasProperty$1(value, 'type') && typeof getPropertyValue$1(value, 'type') === 'string'); }; /** * Safe parser for unknown values as expressions */ const parseUnknownValue$1 = (value) => { if (isExpressionLike$1(value)) { return parseDefaultValue$1(value); } return value; }; /** * Parse property value (including arrays) */ const parsePropertyValue$1 = (expr) => { if (isArrayExpression$1(expr)) { const result = []; for (const element of expr.elements) { const elementExpr = getArgumentExpression$1(element); if (elementExpr && elementExpr.type === 'MemberExpression' && elementExpr.object.type === 'Identifier' && elementExpr.property.type === 'Identifier') { // For table.columnName references, use the property name result.push(elementExpr.property.value); } else if (isMemberExpression$1(element) && isIdentifier$1(element.object) && isIdentifier$1(element.property)) { // Direct MemberExpression (not wrapped in { expression }) result.push(element.property.value); } else { const parsed = elementExpr ? parseDefaultValue$1(elementExpr) : parseUnknownValue$1(element); result.push(parsed); } } return result; } return parseUnknownValue$1(expr); }; /** * Column definition parsing for Drizzle ORM MySQL schema parsing */ /** * Parse runtime function from $defaultFn or $onUpdate arguments */ const parseRuntimeFunction = (args) => { if (args.length === 0) { return 'custom_function()'; } const argExpr = getArgumentExpression$1(args[0]); if (!argExpr || argExpr.type !== 'ArrowFunctionExpression') { return 'custom_function()'; } const body = argExpr.body; if (body.type === 'CallExpression' && body.callee.type === 'Identifier') { return `${body.callee.value}()`; } if (body.type === 'NewExpression' && body.callee.type === 'Identifier') { return `new ${body.callee.value}()`; } return 'custom_function()'; }; /** * Parse column definition from object property */ const parseColumnFromProperty$1 = (prop) => { if (prop.type !== 'KeyValueProperty') return null; const columnName = prop.key.type === 'Identifier' ? getIdentifierName$1(prop.key) : null; if (!columnName) return null; if (prop.value.type !== 'CallExpression') return null; // Parse the method chain to find the base type const methods = parseMethodChain$1(prop.value); // Find the base type from the root of the chain let baseType = null; let current = prop.value; // Traverse to the bottom of the method chain to find the base type call while (current.type === 'CallExpression' && current.callee.type === 'MemberExpression') { current = current.callee.object; } if (current.type === 'CallExpression' && current.callee.type === 'Identifier') { baseType = current.callee.value; } if (!baseType) return null; // Extract the actual column name from the first argument of the base type call let actualColumnName = columnName; // Default to JS property name if (current.type === 'CallExpression' && current.arguments.length > 0) { const firstArg = current.arguments[0]; const firstArgExpr = getArgumentExpression$1(firstArg); if (firstArgExpr && isStringLiteral$1(firstArgExpr)) { actualColumnName = firstArgExpr.value; } } const column = { name: actualColumnName, type: baseType, notNull: false, primaryKey: false, unique: false, }; // Parse type options from second argument (like { length: 255 }) if (current.type === 'CallExpression' && current.arguments.length > 1) { const secondArg = current.arguments[1]; const secondArgExpr = getArgumentExpression$1(secondArg); if (secondArgExpr && isObjectExpression$1(secondArgExpr)) { column.typeOptions = parseObjectExpression$1(secondArgExpr); } } // Parse method calls in the chain (already parsed above) for (const method of methods) { switch (method.name) { case 'primaryKey': column.primaryKey = true; column.notNull = true; break; case 'autoincrement': // MySQL specific: int().autoincrement() break; case 'notNull': column.notNull = true; break; case 'unique': column.unique = true; break; case 'default': if (method.args.length > 0) { const argExpr = getArgumentExpression$1(method.args[0]); if (argExpr) { column.default = parseDefaultValue$1(argExpr); } } break; case 'defaultNow': column.default = 'now()'; break; case '$defaultFn': column.default = parseRuntimeFunction(method.args); break; case '$onUpdate': column.onUpdate = parseRuntimeFunction(method.args); break; case 'onUpdateNow': // MySQL specific: ON UPDATE NOW() - ignore for now break; case '$type': // Type assertion - ignore for parsing purposes break; case 'references': if (method.args.length > 0) { const argExpr = getArgumentExpression$1(method.args[0]); // Parse references: () => table.column if (argExpr && argExpr.type === 'ArrowFunctionExpression') { const body = argExpr.body; if (body.type === 'MemberExpression' && body.object.type === 'Identifier' && body.property.type === 'Identifier') { const referencesOptions = { table: body.object.value, column: body.property.value, }; // Parse the second argument for onDelete/onUpdate options if (method.args.length > 1) { const optionsExpr = getArgumentExpression$1(method.args[1]); if (optionsExpr && isObjectExpression$1(optionsExpr)) { const options = parseObjectExpression$1(optionsExpr); if (typeof options['onDelete'] === 'string') { referencesOptions.onDelete = options['onDelete']; } if (typeof options['onUpdate'] === 'string') { referencesOptions.onUpdate = options['onUpdate']; } } } column.references = referencesOptions; } } } break; case '$comment': if (method.args.length > 0) { const argExpr = getArgumentExpression$1(method.args[0]); const commentValue = argExpr ? getStringValue$1(argExpr) : null; if (commentValue) { column.comment = commentValue; } } break; } } // Handle autoincrement for primary key columns const hasAutoincrementMethod = methods.some((m) => m.name === 'autoincrement'); if (column.primaryKey && ((baseType === 'int' && hasAutoincrementMethod) || baseType === 'serial')) { column.default = 'autoincrement()'; } return column; }; /** * Table structure parsing for Drizzle ORM MySQL schema parsing */ /** * Parse columns from object expression */ const parseTableColumns = (columnsExpr) => { const columns = {}; for (const prop of columnsExpr.properties) { if (prop.type === 'KeyValueProperty') { const column = parseColumnFromProperty$1(prop); if (column) { // Use the JS property name as the key const jsPropertyName = prop.key.type === 'Identifier' ? getIdentifierName$1(prop.key) : null; if (jsPropertyName) { columns[jsPropertyName] = column; } } } } return columns; }; /** * Parse indexes, constraints, and composite primary keys from third argument */ const parseTableExtensions = (thirdArgExpr, tableColumns) => { const result = { indexes: {}, }; if (thirdArgExpr.type === 'ArrowFunctionExpression') { // Parse arrow function like (table) => ({ nameIdx: index(...), pk: primaryKey(...) }) let returnExpr = thirdArgExpr.body; // Handle parenthesized expressions like (table) => ({ ... }) if (returnExpr.type === 'ParenthesisExpression') { returnExpr = returnExpr.expression; } if (returnExpr.type === 'ObjectExpression') { for (const prop of returnExpr.properties) { if (prop.type === 'KeyValueProperty') { const indexName = prop.key.type === 'Identifier' ? getIdentifierName$1(prop.key) : null; if (indexName && prop.value.type === 'CallExpression') { const indexDef = parseIndexDefinition$1(prop.value, indexName); if (indexDef) { if (isCompositePrimaryKey$1(indexDef)) { result.compositePrimaryKey = indexDef; } else if (isDrizzleIndex$1(indexDef)) { result.indexes[indexName] = indexDef; } } // Handle check constraints const checkConstraint = parseCheckConstraint(prop.value, indexName); if (checkConstraint) { result.constraints = result.constraints || {}; result.constraints[checkConstraint.name] = { type: 'CHECK', name: checkConstraint.name, detail: checkConstraint.condition, }; } // Handle unique constraints const uniqueConstraint = parseUniqueConstraint(prop.value, indexName, tableColumns); if (uniqueConstraint) { result.constraints = result.constraints || {}; result.constraints[uniqueConstraint.name] = { type: 'UNIQUE', name: uniqueConstraint.name, columnNames: uniqueConstraint.columnNames, }; } } } } } } return result; }; /** * Parse mysqlTable call with comment method chain */ const parseMysqlTableWithComment = (commentCallExpr) => { // Extract the comment from the call arguments let comment = null; if (commentCallExpr.arguments.length > 0) { const commentArg = commentCallExpr.arguments[0]; const commentExpr = getArgumentExpression$1(commentArg); if (commentExpr && isStringLiteral$1(commentExpr)) { comment = commentExpr.value; } } // Get the mysqlTable call from the object of the member expression if (commentCallExpr.callee.type === 'MemberExpression') { const mysqlTableCall = commentCallExpr.callee.object; if (mysqlTableCall.type === 'CallExpression' && isMysqlTableCall(mysqlTableCall)) { const table = parseMysqlTableCall(mysqlTableCall); if (table && comment) { table.comment = comment; } return table; } } return null; }; /** * Parse mysqlTable call expression */ const parseMysqlTableCall = (callExpr) => { if (callExpr.arguments.length < 2) return null; const tableNameArg = callExpr.arguments[0]; const columnsArg = callExpr.arguments[1]; if (!tableNameArg || !columnsArg) return null; // Extract expression from SWC argument structure const tableNameExpr = getArgumentExpression$1(tableNameArg); const columnsExpr = getArgumentExpression$1(columnsArg); const tableName = tableNameExpr ? getStringValue$1(tableNameExpr) : null; if (!tableName || !columnsExpr || !isObjectExpression$1(columnsExpr)) return null; const table = { name: tableName, columns: {}, indexes: {}, }; // Parse columns from the object expression table.columns = parseTableColumns(columnsExpr); // Parse indexes and composite primary key from third argument if present if (callExpr.arguments.length > 2) { const thirdArg = callExpr.arguments[2]; const thirdArgExpr = getArgumentExpression$1(thirdArg); if (thirdArgExpr) { const extensions = parseTableExtensions(thirdArgExpr, table.columns); table.indexes = extensions.indexes; if (extensions.constraints) { table.constraints = extensions.constraints; } if (extensions.compositePrimaryKey) { table.compositePrimaryKey = extensions.compositePrimaryKey; } } } return table; }; /** * Parse schema.table() call expression */ const parseSchemaTableCall$1 = (callExpr) => { if (!isSchemaTableCall$1(callExpr) || callExpr.arguments.length < 2) return null; // Extract expression from SWC argument structure const tableNameExpr = getArgumentExpression$1(callExpr.arguments[0]); const columnsExpr = getArgumentExpression$1(callExpr.arguments[1]); const tableName = tableNameExpr ? getStringValue$1(tableNameExpr) : null; if (!tableName || !columnsExpr || !isObjectExpression$1(columnsExpr)) return null; // Extract schema name from the member expression (e.g., authSchema.table -> authSchema) let schemaName = ''; if (callExpr.callee.type === 'MemberExpression' && callExpr.callee.object.type === 'Identifier') { schemaName = callExpr.callee.object.value; } const table = { name: tableName, // Keep the original table name for DB operations columns: {}, indexes: {}, schemaName, // Add schema information for namespace handling }; // Note: We now handle schema namespace by storing the schema name // and using the original table name for database operations // Parse columns from the object expression table.columns = parseTableColumns(columnsExpr); // Parse indexes and composite primary key from third argument if present if (callExpr.arguments.length > 2) { const thirdArg = callExpr.arguments[2]; const thirdArgExpr = getArgumentExpression$1(thirdArg); if (thirdArgExpr) { const extensions = parseTableExtensions(thirdArgExpr, table.columns); table.indexes = extensions.indexes; if (extensions.constraints) { table.constraints = extensions.constraints; } if (extensions.compositePrimaryKey) { table.compositePrimaryKey = extensions.compositePrimaryKey; } } } return table; }; /** * Parse index or primary key definition */ const parseIndexDefinition$1 = (callExpr, name) => { // Handle primaryKey({ columns: [...] }) if (callExpr.callee.type === 'Identifier' && callExpr.callee.value === 'primaryKey') { if (callExpr.arguments.length > 0) { const configArg = callExpr.arguments[0]; const configExpr = getArgumentExpression$1(configArg); if (configExpr && isObjectExpression$1(configExpr)) { const config = parseObjectExpression$1(configExpr); if (config['columns'] && Array.isArray(config['columns'])) { const columns = config['columns'].filter((col) => typeof col === 'string'); return { type: 'primaryKey', columns, }; } } } return null; } // Handle index('name').on(...) or uniqueIndex('name').on(...) with optional .using(...) let isUnique = false; let indexName = name; let indexType = ''; // Index type (btree, hash, etc.) let currentExpr = callExpr; // Traverse the method chain to find index(), on(), and using() calls const methodCalls = []; while (currentExpr.type === 'CallExpression' && currentExpr.callee.type === 'MemberExpression' && currentExpr.callee.property.type === 'Identifier') { const methodName = currentExpr.callee.property.value; methodCalls.unshift({ method: methodName, expr: currentExpr }); currentExpr = currentExpr.callee.object; } // The base should be index() or uniqueIndex() if (currentExpr.type === 'CallExpression' && currentExpr.callee.type === 'Identifier') { const baseMethod = currentExpr.callee.value; if (baseMethod === 'index' || baseMethod === 'uniqueIndex') { isUnique = baseMethod === 'uniqueIndex'; // Get the index name from the first argument if (currentExpr.arguments.length > 0) { const nameArg = currentExpr.arguments[0]; const nameExpr = getArgumentExpression$1(nameArg); if (nameExpr && isStringLiteral$1(nameExpr)) { indexName = nameExpr.value; } } } } // Parse method chain to extract columns and index type const columns = []; for (const { method, expr } of methodCalls) { if (method === 'on') { // Parse column references from .on(...) arguments for (const arg of expr.arguments) { const argExpr = getArgumentExpression$1(arg); if (argExpr && argExpr.type === 'MemberExpression' && argExpr.object.type === 'Identifier' && argExpr.property.type === 'Identifier') { columns.push(argExpr.property.value); } } } else if (method === 'using') { // Parse index type from .using('type') - only the first argument specifies the type if (expr.arguments.length > 0) { const typeArg = expr.arguments[0]; const typeExpr = getArgumentExpression$1(typeArg); if (typeExpr && isStringLiteral$1(typeExpr)) { indexType = typeExpr.value; } } } } if (columns.length > 0) { return { name: indexName, columns, unique: isUnique, type: indexType, }; } return null; }; /** * Parse check constraint definition */ const parseCheckConstraint = (callExpr, name) => { // Handle check('constraint_name', sql`condition`) if (callExpr.callee.type === 'Identifier' && callExpr.callee.value === 'check') { // Extract the constraint name from the first argument let constraintName = name; if (callExpr.arguments.length > 0) { const nameArg = callExpr.arguments[0]; const nameExpr = getArgumentExpression$1(nameArg); if (nameExpr && isStringLiteral$1(nameExpr)) { constraintName = nameExpr.value; } } // Extract the condition from the second argument (sql template literal) let condition = 'true'; // Default condition if (callExpr.arguments.length > 1) { const conditionArg = callExpr.arguments[1]; const conditionExpr = getArgumentExpression$1(conditionArg); if (conditionExpr) { // Handle sql`condition` template literal if (conditionExpr.type === 'TaggedTemplateExpression' && conditionExpr.tag.type === 'Identifier' && conditionExpr.tag.value === 'sql') { // Extract the condition from template literal if (conditionExpr.template.type === 'TemplateLiteral' && conditionExpr.template.quasis.length > 0) { const firstQuasi = conditionExpr.template.quasis[0]; if (firstQuasi && firstQuasi.type === 'TemplateElement') { // SWC TemplateElement has different structure than TypeScript's // We need to access the raw string from the SWC AST structure // Use property access with type checking to avoid type assertions const hasRaw = 'raw' in firstQuasi && typeof firstQuasi.raw === 'string'; const hasCooked = 'cooked' in firstQuasi && typeof firstQuasi.cooked === 'string'; if (hasRaw) { condition = firstQuasi.raw || ''; } else if (hasCooked) { condition = firstQuasi.cooked || ''; } } } } // Handle direct function call like sql('condition') else if (conditionExpr.type === 'CallExpression' && conditionExpr.callee.type === 'Identifier' && conditionExpr.callee.value === 'sql' && conditionExpr.arguments.length > 0) { const sqlArg = getArgumentExpression$1(conditionExpr.arguments[0]); if (sqlArg && isStringLiteral$1(sqlArg)) { condition = sqlArg.value; } } } } return { type: 'check', name: constraintName, condition, }; } return null; }; /** * Parse unique constraint definition */ const parseUniqueConstraint = (callExpr, name, tableColumns) => { // Handle unique('constraint_name').on(...) method chain let constraintName = name; let currentExpr = callExpr; const columns = []; // First check if we have a method chain ending with .on(...) const methodCalls = []; // Traverse method chain to collect all calls while (currentExpr.type === 'CallExpression' && currentExpr.callee.type === 'MemberExpression' && currentExpr.callee.property.type === 'Identifier') { const methodName = currentExpr.callee.property.value; methodCalls.unshift({ method: methodName, expr: currentExpr }); currentExpr = currentExpr.callee.object; } // The base should be unique() if (currentExpr.type === 'CallExpression' && currentExpr.callee.type === 'Identifier' && currentExpr.callee.value === 'unique') { // Get the constraint name from the first argument if (currentExpr.arguments.length > 0) { const nameArg = currentExpr.arguments[0]; const nameExpr = getArgumentExpression$1(nameArg); if (nameExpr && isStringLiteral$1(nameExpr)) { constraintName = nameExpr.value; } } // Find the .on() method call and parse columns for (const { method, expr } of methodCalls) { if (method === 'on') { // Parse column references from .on(...) arguments for (const arg of expr.arguments) { const argExpr = getArgumentExpression$1(arg); if (argExpr && argExpr.type === 'MemberExpression' && argExpr.object.type === 'Identifier' && argExpr.property.type === 'Identifier') { // Get the JavaScript property name const jsPropertyName = argExpr.property.value; // Find the actual database column name from the table columns const column = tableColumns[jsPropertyName]; if (column) { columns.push(column.name); // Use database column name } else { columns.push(jsPropertyName); // Fallback to JS property name } } } break; } } if (columns.length > 0) { return { type: 'UNIQUE', name: constraintName, columnNames: columns, }; } } return null; }; /** * Main orchestrator for Drizzle ORM MySQL schema parsing */ /** * Parse Drizzle TypeScript schema to extract table definitions using SWC AST */ const parseDrizzleSchema$1 = (sourceCode) => { // Parse TypeScript code into AST const ast = parseSync(sourceCode, { syntax: 'typescript', target: 'es2022', }); const tables = {}; const enums = {}; const schemas = {}; const variableToTableMapping = {}; // Traverse the AST to find mysqlTable, mysqlSchema calls visitModule$1(ast, tables, enums, schemas, variableToTableMapping); return { tables, enums, schemas, variableToTableMapping }; }; /** * Visit and traverse the module AST */ const visitModule$1 = (module, tables, enums, schemas, variableToTableMapping) => { for (const item of module.body) { if (item.type === 'VariableDeclaration') { for (const declarator of item.declarations) { visitVariableDeclarator$1(declarator, tables, enums, schemas, variableToTableMapping); } } else if (item.type === 'ExportDeclaration' && item.declaration?.type === 'VariableDeclaration') { for (const declarator of item.declaration.declarations) { visitVariableDeclarator$1(declarator, tables, enums, schemas, variableToTableMapping); } } } }; /** * Visit variable declarator to find mysqlTable, mysqlEnum, mysqlSchema, or relations calls */ const visitVariableDeclarator$1 = (declarator, tables, enums, schemas, variableToTableMapping) => { if (!declarator.init || declarator.init.type !== 'CallExpression') return; const callExpr = declarator.init; if (isMysqlTableCall(callExpr)) { const table = parseMysqlTableCall(callExpr); if (table && declarator.id.type === 'Identifier') { tables[table.name] = table; // Map variable name to table name variableToTableMapping[declarator.id.value] = table.name; } } else if (isSchemaTableCall$1(callExpr)) { const table = parseSchemaTableCall$1(callExpr); if (table && declarator.id.type === 'Identifier') { tables[table.name] = table; // Map variable name to table name variableToTableMapping[declarator.id.value] = table.name; } } else if (declarator.init.type === 'CallExpression' && declarator.init.callee.type === 'MemberExpression' && declarator.init.callee.property.type === 'Identifier' && declarator.init.callee.property.value === '$comment') { // Handle table comments: mysqlTable(...).comment(...) const table = parseMysqlTableWithComment(declarator.init); if (table && declarator.id.type === 'Identifier') { tables[table.name] = table; // Map variable name to table name variableToTableMapping[declarator.id.value] = table.name; } } else if (callExpr.callee.type === 'Identifier' && callExpr.callee.value === 'mysqlEnum') { const enumDef = parseMysqlEnumCall(callExpr); if (enumDef && declarator.id.type === 'Identifier') { enums[declarator.id.value] = enumDef; } } else if (isMysqlSchemaCall(callExpr)) { const schemaDef = parseMysqlSchemaCall(callExpr); if (schemaDef && declarator.id.type === 'Identifier') { schemas[declarator.id.value] = schemaDef; } } }; /** * Main processor function for Drizzle MySQL schemas */ const parseDrizzleSchemaString$1 = (schemaString) => { try { const { tables: drizzleTables, enums, variableToTableMapping, } = parseDrizzleSchema$1(schemaString); const { tables, errors } = convertDrizzleTablesToInternal$1(drizzleTables, enums, variableToTableMapping); return Promise.resolve({ value: { tables }, errors, }); } catch (error) { return Promise.resolve({ value: { tables: {} }, errors: [ new Error(`Error parsing Drizzle MySQL schema: ${error instanceof Error ? error.message : String(error)}`), ], }); } }; const processor$6 = parseDrizzleSchemaString$1; /** * Type definitions for Drizzle ORM schema parsing */ /** * Type guard to check if a value is an object */ const isObject = (value) => { return typeof value === 'object' && value !== null; }; /** * Safe property checker without type casting */ const hasProperty = (obj, key) => { return typeof obj === 'object' && obj !== null && key in obj; }; /** * Safe property getter without type casting */ const getPropertyValue = (obj, key) => { if (hasProperty(obj, key)) { return obj[key]; } return undefined; }; /** * Type guard for CompositePrimaryKeyDefinition */ const isCompositePrimaryKey = (value) => { return (isObject(val