@open-mercato/cli 0.4.2-canary-eb5f87d5f9 → 0.4.2-canary-5035717565
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/db/commands.js +17 -8
- package/dist/lib/db/commands.js.map +2 -2
- package/dist/lib/generators/entity-ids.js +2 -3
- package/dist/lib/generators/entity-ids.js.map +2 -2
- package/dist/lib/generators/module-di.js +4 -6
- package/dist/lib/generators/module-di.js.map +2 -2
- package/dist/lib/generators/module-entities.js +3 -4
- package/dist/lib/generators/module-entities.js.map +2 -2
- package/dist/lib/generators/module-registry.js +92 -118
- package/dist/lib/generators/module-registry.js.map +2 -2
- package/dist/lib/resolver.js +2 -2
- package/dist/lib/resolver.js.map +2 -2
- package/dist/mercato.js +21 -33
- package/dist/mercato.js.map +2 -2
- package/package.json +2 -2
- package/src/lib/db/commands.ts +19 -12
- package/src/lib/generators/entity-ids.ts +2 -4
- package/src/lib/generators/module-di.ts +4 -7
- package/src/lib/generators/module-entities.ts +3 -4
- package/src/lib/generators/module-registry.ts +99 -135
- package/src/lib/resolver.ts +5 -4
- package/src/mercato.ts +21 -40
package/dist/lib/db/commands.js
CHANGED
|
@@ -58,16 +58,14 @@ async function loadModuleEntities(entry, resolver) {
|
|
|
58
58
|
path.join(roots.appBase, "db"),
|
|
59
59
|
path.join(roots.pkgBase, "db")
|
|
60
60
|
];
|
|
61
|
-
const candidates = ["entities.ts", "
|
|
61
|
+
const candidates = ["entities.ts", "schema.ts"];
|
|
62
62
|
for (const base of bases) {
|
|
63
63
|
for (const f of candidates) {
|
|
64
64
|
const p = path.join(base, f);
|
|
65
65
|
if (fs.existsSync(p)) {
|
|
66
66
|
const sub = path.basename(base);
|
|
67
67
|
const fromApp = base.startsWith(roots.appBase);
|
|
68
|
-
const
|
|
69
|
-
const baseName = f.replace(ext, "");
|
|
70
|
-
const importPath = isAppModule && fromApp ? `file://${p.replace(/\.ts$/, ".js")}` : `${fromApp ? imps.appBase : imps.pkgBase}/${sub}/${baseName}`;
|
|
68
|
+
const importPath = isAppModule && fromApp ? `file://${p.replace(/\.ts$/, ".js")}` : `${fromApp ? imps.appBase : imps.pkgBase}/${sub}/${f.replace(/\.ts$/, "")}`;
|
|
71
69
|
try {
|
|
72
70
|
const mod = await import(importPath);
|
|
73
71
|
const entities = Object.values(mod).filter((v) => typeof v === "function");
|
|
@@ -83,11 +81,22 @@ async function loadModuleEntities(entry, resolver) {
|
|
|
83
81
|
}
|
|
84
82
|
function getMigrationsPath(entry, resolver) {
|
|
85
83
|
const from = entry.from || "@open-mercato/core";
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
let pkgModRoot;
|
|
85
|
+
if (from === "@open-mercato/core") {
|
|
86
|
+
pkgModRoot = path.join(resolver.getRootDir(), "packages/core/src/modules", entry.id);
|
|
87
|
+
} else if (/^@open-mercato\//.test(from)) {
|
|
88
|
+
const segs = from.split("/");
|
|
89
|
+
if (segs.length > 1 && segs[1]) {
|
|
90
|
+
pkgModRoot = path.join(resolver.getRootDir(), `packages/${segs[1]}/src/modules`, entry.id);
|
|
91
|
+
} else {
|
|
92
|
+
pkgModRoot = path.join(resolver.getRootDir(), "packages/core/src/modules", entry.id);
|
|
93
|
+
}
|
|
94
|
+
} else if (from === "@app") {
|
|
95
|
+
pkgModRoot = path.join(resolver.getAppDir(), "src/modules", entry.id);
|
|
96
|
+
} else {
|
|
97
|
+
pkgModRoot = path.join(resolver.getRootDir(), "packages/core/src/modules", entry.id);
|
|
88
98
|
}
|
|
89
|
-
|
|
90
|
-
return path.join(pkgBase, "migrations");
|
|
99
|
+
return path.join(pkgModRoot, "migrations");
|
|
91
100
|
}
|
|
92
101
|
async function dbGenerate(resolver, options = {}) {
|
|
93
102
|
const modules = resolver.loadEnabledModules();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/db/commands.ts"],
|
|
4
|
-
"sourcesContent": ["import path from 'node:path'\nimport fs from 'node:fs'\nimport { MikroORM, type Logger } from '@mikro-orm/core'\nimport { Migrator } from '@mikro-orm/migrations'\nimport { PostgreSqlDriver } from '@mikro-orm/postgresql'\nimport type { PackageResolver, ModuleEntry } from '../resolver'\n\nconst QUIET_MODE = process.env.OM_CLI_QUIET === '1' || process.env.MERCATO_QUIET === '1'\nconst PROGRESS_EMOJI = ''\n\nfunction formatResult(modId: string, message: string, emoji = '\u2022') {\n return `${emoji} ${modId}: ${message}`\n}\n\nfunction createProgressRenderer(total: number) {\n const width = 20\n const normalizedTotal = total > 0 ? total : 1\n return (current: number) => {\n const clamped = Math.min(Math.max(current, 0), normalizedTotal)\n const filled = Math.round((clamped / normalizedTotal) * width)\n const bar = `${'='.repeat(filled)}${'.'.repeat(Math.max(width - filled, 0))}`\n return `[${bar}] ${clamped}/${normalizedTotal}`\n }\n}\n\nfunction createMinimalLogger(): Logger {\n return {\n log: () => {},\n error: (_namespace, message) => console.error(message),\n warn: (_namespace, message) => {\n if (!QUIET_MODE) console.warn(message)\n },\n logQuery: () => {},\n setDebugMode: () => {},\n isEnabled: () => false,\n }\n}\n\nfunction getClientUrl(): string {\n const url = process.env.DATABASE_URL\n if (!url) throw new Error('DATABASE_URL is not set')\n return url\n}\n\nfunction sortModules(mods: ModuleEntry[]): ModuleEntry[] {\n // Sort modules alphabetically since they are now isomorphic\n return mods.slice().sort((a, b) => a.id.localeCompare(b.id))\n}\n\n/**\n * Sanitizes a module ID for use in SQL identifiers (table names).\n * Replaces non-alphanumeric characters with underscores to prevent SQL injection.\n * @public Exported for testing\n */\nexport function sanitizeModuleId(modId: string): string {\n return modId.replace(/[^a-z0-9_]/gi, '_')\n}\n\n/**\n * Validates that a table name is safe for use in SQL queries.\n * @throws Error if the table name contains invalid characters.\n * @public Exported for testing\n */\nexport function validateTableName(tableName: string): void {\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(tableName)) {\n throw new Error(`Invalid table name: ${tableName}. Table names must start with a letter or underscore and contain only alphanumeric characters and underscores.`)\n }\n}\n\nasync function loadModuleEntities(entry: ModuleEntry, resolver: PackageResolver): Promise<any[]> {\n const roots = resolver.getModulePaths(entry)\n const imps = resolver.getModuleImportBase(entry)\n const isAppModule = entry.from === '@app'\n const bases = [\n path.join(roots.appBase, 'data'),\n path.join(roots.pkgBase, 'data'),\n path.join(roots.appBase, 'db'),\n path.join(roots.pkgBase, 'db'),\n ]\n // Check both .ts (src/monorepo) and .js (dist/production) extensions\n const candidates = ['entities.ts', 'entities.js', 'schema.ts', 'schema.js']\n\n for (const base of bases) {\n for (const f of candidates) {\n const p = path.join(base, f)\n if (fs.existsSync(p)) {\n const sub = path.basename(base)\n const fromApp = base.startsWith(roots.appBase)\n const ext = path.extname(f)\n const baseName = f.replace(ext, '')\n // For @app modules, use file:// URL since @/ alias doesn't work in Node.js runtime\n const importPath = (isAppModule && fromApp)\n ? `file://${p.replace(/\\.ts$/, '.js')}`\n : `${fromApp ? imps.appBase : imps.pkgBase}/${sub}/${baseName}`\n try {\n const mod = await import(importPath)\n const entities = Object.values(mod).filter((v) => typeof v === 'function')\n if (entities.length) return entities as any[]\n } catch (err) {\n // For @app modules with TypeScript files, they can't be directly imported\n // Skip and let MikroORM handle entities through discovery\n if (isAppModule) continue\n throw err\n }\n }\n }\n }\n return []\n}\n\nfunction getMigrationsPath(entry: ModuleEntry, resolver: PackageResolver): string {\n const from = entry.from || '@open-mercato/core'\n\n if (from === '@app') {\n // For @app modules, use the app directory\n return path.join(resolver.getAppDir(), 'src/modules', entry.id, 'migrations')\n }\n\n // Use resolver's getModulePaths which handles monorepo vs production mode\n const { pkgBase } = resolver.getModulePaths(entry)\n return path.join(pkgBase, 'migrations')\n}\n\nexport interface DbOptions {\n quiet?: boolean\n}\n\nexport interface GreenfieldOptions extends DbOptions {\n yes: boolean\n}\n\nexport async function dbGenerate(resolver: PackageResolver, options: DbOptions = {}): Promise<void> {\n const modules = resolver.loadEnabledModules()\n const ordered = sortModules(modules)\n const results: string[] = []\n\n for (const entry of ordered) {\n const modId = entry.id\n const sanitizedModId = sanitizeModuleId(modId)\n const entities = await loadModuleEntities(entry, resolver)\n if (!entities.length) continue\n\n const migrationsPath = getMigrationsPath(entry, resolver)\n fs.mkdirSync(migrationsPath, { recursive: true })\n\n const tableName = `mikro_orm_migrations_${sanitizedModId}`\n validateTableName(tableName)\n\n const orm = await MikroORM.init<PostgreSqlDriver>({\n driver: PostgreSqlDriver,\n clientUrl: getClientUrl(),\n loggerFactory: () => createMinimalLogger(),\n entities,\n migrations: {\n path: migrationsPath,\n glob: '!(*.d).{ts,js}',\n tableName,\n dropTables: false,\n },\n schemaGenerator: {\n disableForeignKeys: true,\n },\n pool: {\n min: 1,\n max: 3,\n idleTimeoutMillis: 30000,\n acquireTimeoutMillis: 60000,\n destroyTimeoutMillis: 30000,\n },\n })\n\n const migrator = orm.getMigrator() as Migrator\n const diff = await migrator.createMigration()\n if (diff && diff.fileName) {\n try {\n const orig = diff.fileName\n const base = path.basename(orig)\n const dir = path.dirname(orig)\n const ext = path.extname(base)\n const stem = base.replace(ext, '')\n const suffix = `_${modId}`\n const newBase = stem.endsWith(suffix) ? base : `${stem}${suffix}${ext}`\n const newPath = path.join(dir, newBase)\n let content = fs.readFileSync(orig, 'utf8')\n // Rename class to ensure uniqueness as well\n content = content.replace(\n /export class (Migration\\d+)/,\n `export class $1_${modId.replace(/[^a-zA-Z0-9]/g, '_')}`\n )\n fs.writeFileSync(newPath, content, 'utf8')\n if (newPath !== orig) fs.unlinkSync(orig)\n results.push(formatResult(modId, `generated ${newBase}`, ''))\n } catch {\n results.push(formatResult(modId, `generated ${path.basename(diff.fileName)} (rename failed)`, ''))\n }\n } else {\n results.push(formatResult(modId, 'no changes', ''))\n }\n\n await orm.close(true)\n }\n\n console.log(results.join('\\n'))\n}\n\nexport async function dbMigrate(resolver: PackageResolver, options: DbOptions = {}): Promise<void> {\n const modules = resolver.loadEnabledModules()\n const ordered = sortModules(modules)\n const results: string[] = []\n\n for (const entry of ordered) {\n const modId = entry.id\n const sanitizedModId = sanitizeModuleId(modId)\n const entities = await loadModuleEntities(entry, resolver)\n\n const migrationsPath = getMigrationsPath(entry, resolver)\n\n // Skip if no entities AND no migrations directory exists\n // (allows @app modules to run migrations even if entities can't be dynamically imported)\n if (!entities.length && !fs.existsSync(migrationsPath)) continue\n fs.mkdirSync(migrationsPath, { recursive: true })\n\n const tableName = `mikro_orm_migrations_${sanitizedModId}`\n validateTableName(tableName)\n\n // For @app modules, entities may be empty since TypeScript files can't be imported at runtime\n // Use discovery.warnWhenNoEntities: false to allow running migrations without entities\n const orm = await MikroORM.init<PostgreSqlDriver>({\n driver: PostgreSqlDriver,\n clientUrl: getClientUrl(),\n loggerFactory: () => createMinimalLogger(),\n entities: entities.length ? entities : [],\n discovery: { warnWhenNoEntities: false },\n migrations: {\n path: migrationsPath,\n glob: '!(*.d).{ts,js}',\n tableName,\n dropTables: false,\n },\n schemaGenerator: {\n disableForeignKeys: true,\n },\n pool: {\n min: 1,\n max: 3,\n idleTimeoutMillis: 30000,\n acquireTimeoutMillis: 60000,\n destroyTimeoutMillis: 30000,\n },\n })\n\n const migrator = orm.getMigrator() as Migrator\n const pending = await migrator.getPendingMigrations()\n if (!pending.length) {\n results.push(formatResult(modId, 'no pending migrations', ''))\n } else {\n const renderProgress = createProgressRenderer(pending.length)\n let applied = 0\n if (!QUIET_MODE) {\n process.stdout.write(` ${PROGRESS_EMOJI} ${modId}: ${renderProgress(applied)}`)\n }\n for (const migration of pending) {\n const migrationName =\n typeof migration === 'string'\n ? migration\n : (migration as any).name ?? (migration as any).fileName\n await migrator.up(migrationName ? { migrations: [migrationName] } : undefined)\n applied += 1\n if (!QUIET_MODE) {\n process.stdout.write(`\\r ${PROGRESS_EMOJI} ${modId}: ${renderProgress(applied)}`)\n }\n }\n if (!QUIET_MODE) process.stdout.write('\\n')\n results.push(\n formatResult(modId, `${pending.length} migration${pending.length === 1 ? '' : 's'} applied`, '')\n )\n }\n\n await orm.close(true)\n }\n\n console.log(results.join('\\n'))\n}\n\nexport async function dbGreenfield(resolver: PackageResolver, options: GreenfieldOptions): Promise<void> {\n if (!options.yes) {\n console.error('This command will DELETE all data. Use --yes to confirm.')\n process.exit(1)\n }\n\n console.log('Cleaning up migrations and snapshots for greenfield setup...')\n\n const modules = resolver.loadEnabledModules()\n const ordered = sortModules(modules)\n const results: string[] = []\n const outputDir = resolver.getOutputDir()\n\n for (const entry of ordered) {\n const modId = entry.id\n const migrationsPath = getMigrationsPath(entry, resolver)\n\n if (fs.existsSync(migrationsPath)) {\n // Remove all migration files\n const migrationFiles = fs\n .readdirSync(migrationsPath)\n .filter((file) => file.endsWith('.ts') && file.startsWith('Migration'))\n\n for (const file of migrationFiles) {\n fs.unlinkSync(path.join(migrationsPath, file))\n }\n\n // Remove snapshot files\n const snapshotFiles = fs\n .readdirSync(migrationsPath)\n .filter((file) => file.endsWith('.json') && file.includes('snapshot'))\n\n for (const file of snapshotFiles) {\n fs.unlinkSync(path.join(migrationsPath, file))\n }\n\n if (migrationFiles.length > 0 || snapshotFiles.length > 0) {\n results.push(\n formatResult(modId, `cleaned ${migrationFiles.length} migrations, ${snapshotFiles.length} snapshots`, '')\n )\n } else {\n results.push(formatResult(modId, 'already clean', ''))\n }\n } else {\n results.push(formatResult(modId, 'no migrations directory', ''))\n }\n\n // Clean up checksum files using glob pattern\n if (fs.existsSync(outputDir)) {\n const files = fs.readdirSync(outputDir)\n const checksumFiles = files.filter((file) => file.endsWith('.checksum'))\n\n for (const file of checksumFiles) {\n fs.unlinkSync(path.join(outputDir, file))\n }\n\n if (checksumFiles.length > 0) {\n results.push(formatResult(modId, `cleaned ${checksumFiles.length} checksum files`, ''))\n }\n }\n }\n\n console.log(results.join('\\n'))\n\n // Drop per-module MikroORM migration tables to ensure clean slate\n console.log('Dropping per-module migration tables...')\n try {\n const { Client } = await import('pg')\n const client = new Client({ connectionString: getClientUrl() })\n await client.connect()\n try {\n await client.query('BEGIN')\n for (const entry of ordered) {\n const modId = entry.id\n const sanitizedModId = sanitizeModuleId(modId)\n const tableName = `mikro_orm_migrations_${sanitizedModId}`\n validateTableName(tableName)\n await client.query(`DROP TABLE IF EXISTS \"${tableName}\"`)\n console.log(` ${modId}: dropped table ${tableName}`)\n }\n await client.query('COMMIT')\n } catch (e) {\n await client.query('ROLLBACK')\n throw e\n } finally {\n try {\n await client.end()\n } catch {}\n }\n } catch (e) {\n console.error('Failed to drop migration tables:', (e as any)?.message || e)\n throw e\n }\n\n // Drop all existing user tables to ensure fresh CREATE-only migrations\n console.log('Dropping ALL public tables for true greenfield...')\n try {\n const { Client } = await import('pg')\n const client = new Client({ connectionString: getClientUrl() })\n await client.connect()\n try {\n const res = await client.query(`SELECT tablename FROM pg_tables WHERE schemaname = 'public'`)\n const tables: string[] = (res.rows || []).map((r: any) => String(r.tablename))\n if (tables.length) {\n await client.query('BEGIN')\n try {\n await client.query(\"SET session_replication_role = 'replica'\")\n for (const t of tables) {\n await client.query(`DROP TABLE IF EXISTS \"${t}\" CASCADE`)\n }\n await client.query(\"SET session_replication_role = 'origin'\")\n await client.query('COMMIT')\n console.log(` Dropped ${tables.length} tables.`)\n } catch (e) {\n await client.query('ROLLBACK')\n throw e\n }\n } else {\n console.log(' No tables found to drop.')\n }\n } finally {\n try {\n await client.end()\n } catch {}\n }\n } catch (e) {\n console.error('Failed to drop public tables:', (e as any)?.message || e)\n throw e\n }\n\n // Generate fresh migrations for all modules\n console.log('Generating fresh migrations for all modules...')\n await dbGenerate(resolver)\n\n // Apply migrations\n console.log('Applying migrations...')\n await dbMigrate(resolver)\n\n console.log('Greenfield reset complete! Fresh migrations generated and applied.')\n}\n"],
|
|
5
|
-
"mappings": "AAAA,OAAO,UAAU;AACjB,OAAO,QAAQ;AACf,SAAS,gBAA6B;AAEtC,SAAS,wBAAwB;AAGjC,MAAM,aAAa,QAAQ,IAAI,iBAAiB,OAAO,QAAQ,IAAI,kBAAkB;AACrF,MAAM,iBAAiB;AAEvB,SAAS,aAAa,OAAe,SAAiB,QAAQ,UAAK;AACjE,SAAO,GAAG,KAAK,IAAI,KAAK,KAAK,OAAO;AACtC;AAEA,SAAS,uBAAuB,OAAe;AAC7C,QAAM,QAAQ;AACd,QAAM,kBAAkB,QAAQ,IAAI,QAAQ;AAC5C,SAAO,CAAC,YAAoB;AAC1B,UAAM,UAAU,KAAK,IAAI,KAAK,IAAI,SAAS,CAAC,GAAG,eAAe;AAC9D,UAAM,SAAS,KAAK,MAAO,UAAU,kBAAmB,KAAK;AAC7D,UAAM,MAAM,GAAG,IAAI,OAAO,MAAM,CAAC,GAAG,IAAI,OAAO,KAAK,IAAI,QAAQ,QAAQ,CAAC,CAAC,CAAC;AAC3E,WAAO,IAAI,GAAG,KAAK,OAAO,IAAI,eAAe;AAAA,EAC/C;AACF;AAEA,SAAS,sBAA8B;AACrC,SAAO;AAAA,IACL,KAAK,MAAM;AAAA,IAAC;AAAA,IACZ,OAAO,CAAC,YAAY,YAAY,QAAQ,MAAM,OAAO;AAAA,IACrD,MAAM,CAAC,YAAY,YAAY;AAC7B,UAAI,CAAC,WAAY,SAAQ,KAAK,OAAO;AAAA,IACvC;AAAA,IACA,UAAU,MAAM;AAAA,IAAC;AAAA,IACjB,cAAc,MAAM;AAAA,IAAC;AAAA,IACrB,WAAW,MAAM;AAAA,EACnB;AACF;AAEA,SAAS,eAAuB;AAC9B,QAAM,MAAM,QAAQ,IAAI;AACxB,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,yBAAyB;AACnD,SAAO;AACT;AAEA,SAAS,YAAY,MAAoC;AAEvD,SAAO,KAAK,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,GAAG,cAAc,EAAE,EAAE,CAAC;AAC7D;AAOO,SAAS,iBAAiB,OAAuB;AACtD,SAAO,MAAM,QAAQ,gBAAgB,GAAG;AAC1C;AAOO,SAAS,kBAAkB,WAAyB;AACzD,MAAI,CAAC,2BAA2B,KAAK,SAAS,GAAG;AAC/C,UAAM,IAAI,MAAM,uBAAuB,SAAS,gHAAgH;AAAA,EAClK;AACF;AAEA,eAAe,mBAAmB,OAAoB,UAA2C;AAC/F,QAAM,QAAQ,SAAS,eAAe,KAAK;AAC3C,QAAM,OAAO,SAAS,oBAAoB,KAAK;AAC/C,QAAM,cAAc,MAAM,SAAS;AACnC,QAAM,QAAQ;AAAA,IACZ,KAAK,KAAK,MAAM,SAAS,MAAM;AAAA,IAC/B,KAAK,KAAK,MAAM,SAAS,MAAM;AAAA,IAC/B,KAAK,KAAK,MAAM,SAAS,IAAI;AAAA,IAC7B,KAAK,KAAK,MAAM,SAAS,IAAI;AAAA,EAC/B;
|
|
4
|
+
"sourcesContent": ["import path from 'node:path'\nimport fs from 'node:fs'\nimport { MikroORM, type Logger } from '@mikro-orm/core'\nimport { Migrator } from '@mikro-orm/migrations'\nimport { PostgreSqlDriver } from '@mikro-orm/postgresql'\nimport type { PackageResolver, ModuleEntry } from '../resolver'\n\nconst QUIET_MODE = process.env.OM_CLI_QUIET === '1' || process.env.MERCATO_QUIET === '1'\nconst PROGRESS_EMOJI = ''\n\nfunction formatResult(modId: string, message: string, emoji = '\u2022') {\n return `${emoji} ${modId}: ${message}`\n}\n\nfunction createProgressRenderer(total: number) {\n const width = 20\n const normalizedTotal = total > 0 ? total : 1\n return (current: number) => {\n const clamped = Math.min(Math.max(current, 0), normalizedTotal)\n const filled = Math.round((clamped / normalizedTotal) * width)\n const bar = `${'='.repeat(filled)}${'.'.repeat(Math.max(width - filled, 0))}`\n return `[${bar}] ${clamped}/${normalizedTotal}`\n }\n}\n\nfunction createMinimalLogger(): Logger {\n return {\n log: () => {},\n error: (_namespace, message) => console.error(message),\n warn: (_namespace, message) => {\n if (!QUIET_MODE) console.warn(message)\n },\n logQuery: () => {},\n setDebugMode: () => {},\n isEnabled: () => false,\n }\n}\n\nfunction getClientUrl(): string {\n const url = process.env.DATABASE_URL\n if (!url) throw new Error('DATABASE_URL is not set')\n return url\n}\n\nfunction sortModules(mods: ModuleEntry[]): ModuleEntry[] {\n // Sort modules alphabetically since they are now isomorphic\n return mods.slice().sort((a, b) => a.id.localeCompare(b.id))\n}\n\n/**\n * Sanitizes a module ID for use in SQL identifiers (table names).\n * Replaces non-alphanumeric characters with underscores to prevent SQL injection.\n * @public Exported for testing\n */\nexport function sanitizeModuleId(modId: string): string {\n return modId.replace(/[^a-z0-9_]/gi, '_')\n}\n\n/**\n * Validates that a table name is safe for use in SQL queries.\n * @throws Error if the table name contains invalid characters.\n * @public Exported for testing\n */\nexport function validateTableName(tableName: string): void {\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(tableName)) {\n throw new Error(`Invalid table name: ${tableName}. Table names must start with a letter or underscore and contain only alphanumeric characters and underscores.`)\n }\n}\n\nasync function loadModuleEntities(entry: ModuleEntry, resolver: PackageResolver): Promise<any[]> {\n const roots = resolver.getModulePaths(entry)\n const imps = resolver.getModuleImportBase(entry)\n const isAppModule = entry.from === '@app'\n const bases = [\n path.join(roots.appBase, 'data'),\n path.join(roots.pkgBase, 'data'),\n path.join(roots.appBase, 'db'),\n path.join(roots.pkgBase, 'db'),\n ]\n const candidates = ['entities.ts', 'schema.ts']\n\n for (const base of bases) {\n for (const f of candidates) {\n const p = path.join(base, f)\n if (fs.existsSync(p)) {\n const sub = path.basename(base)\n const fromApp = base.startsWith(roots.appBase)\n // For @app modules, use file:// URL since @/ alias doesn't work in Node.js runtime\n const importPath = (isAppModule && fromApp)\n ? `file://${p.replace(/\\.ts$/, '.js')}`\n : `${fromApp ? imps.appBase : imps.pkgBase}/${sub}/${f.replace(/\\.ts$/, '')}`\n try {\n const mod = await import(importPath)\n const entities = Object.values(mod).filter((v) => typeof v === 'function')\n if (entities.length) return entities as any[]\n } catch (err) {\n // For @app modules with TypeScript files, they can't be directly imported\n // Skip and let MikroORM handle entities through discovery\n if (isAppModule) continue\n throw err\n }\n }\n }\n }\n return []\n}\n\nfunction getMigrationsPath(entry: ModuleEntry, resolver: PackageResolver): string {\n const from = entry.from || '@open-mercato/core'\n let pkgModRoot: string\n\n if (from === '@open-mercato/core') {\n pkgModRoot = path.join(resolver.getRootDir(), 'packages/core/src/modules', entry.id)\n } else if (/^@open-mercato\\//.test(from)) {\n const segs = from.split('/')\n if (segs.length > 1 && segs[1]) {\n pkgModRoot = path.join(resolver.getRootDir(), `packages/${segs[1]}/src/modules`, entry.id)\n } else {\n pkgModRoot = path.join(resolver.getRootDir(), 'packages/core/src/modules', entry.id)\n }\n } else if (from === '@app') {\n // For @app modules, use the app directory not the monorepo root\n pkgModRoot = path.join(resolver.getAppDir(), 'src/modules', entry.id)\n } else {\n pkgModRoot = path.join(resolver.getRootDir(), 'packages/core/src/modules', entry.id)\n }\n\n return path.join(pkgModRoot, 'migrations')\n}\n\nexport interface DbOptions {\n quiet?: boolean\n}\n\nexport interface GreenfieldOptions extends DbOptions {\n yes: boolean\n}\n\nexport async function dbGenerate(resolver: PackageResolver, options: DbOptions = {}): Promise<void> {\n const modules = resolver.loadEnabledModules()\n const ordered = sortModules(modules)\n const results: string[] = []\n\n for (const entry of ordered) {\n const modId = entry.id\n const sanitizedModId = sanitizeModuleId(modId)\n const entities = await loadModuleEntities(entry, resolver)\n if (!entities.length) continue\n\n const migrationsPath = getMigrationsPath(entry, resolver)\n fs.mkdirSync(migrationsPath, { recursive: true })\n\n const tableName = `mikro_orm_migrations_${sanitizedModId}`\n validateTableName(tableName)\n\n const orm = await MikroORM.init<PostgreSqlDriver>({\n driver: PostgreSqlDriver,\n clientUrl: getClientUrl(),\n loggerFactory: () => createMinimalLogger(),\n entities,\n migrations: {\n path: migrationsPath,\n glob: '!(*.d).{ts,js}',\n tableName,\n dropTables: false,\n },\n schemaGenerator: {\n disableForeignKeys: true,\n },\n pool: {\n min: 1,\n max: 3,\n idleTimeoutMillis: 30000,\n acquireTimeoutMillis: 60000,\n destroyTimeoutMillis: 30000,\n },\n })\n\n const migrator = orm.getMigrator() as Migrator\n const diff = await migrator.createMigration()\n if (diff && diff.fileName) {\n try {\n const orig = diff.fileName\n const base = path.basename(orig)\n const dir = path.dirname(orig)\n const ext = path.extname(base)\n const stem = base.replace(ext, '')\n const suffix = `_${modId}`\n const newBase = stem.endsWith(suffix) ? base : `${stem}${suffix}${ext}`\n const newPath = path.join(dir, newBase)\n let content = fs.readFileSync(orig, 'utf8')\n // Rename class to ensure uniqueness as well\n content = content.replace(\n /export class (Migration\\d+)/,\n `export class $1_${modId.replace(/[^a-zA-Z0-9]/g, '_')}`\n )\n fs.writeFileSync(newPath, content, 'utf8')\n if (newPath !== orig) fs.unlinkSync(orig)\n results.push(formatResult(modId, `generated ${newBase}`, ''))\n } catch {\n results.push(formatResult(modId, `generated ${path.basename(diff.fileName)} (rename failed)`, ''))\n }\n } else {\n results.push(formatResult(modId, 'no changes', ''))\n }\n\n await orm.close(true)\n }\n\n console.log(results.join('\\n'))\n}\n\nexport async function dbMigrate(resolver: PackageResolver, options: DbOptions = {}): Promise<void> {\n const modules = resolver.loadEnabledModules()\n const ordered = sortModules(modules)\n const results: string[] = []\n\n for (const entry of ordered) {\n const modId = entry.id\n const sanitizedModId = sanitizeModuleId(modId)\n const entities = await loadModuleEntities(entry, resolver)\n\n const migrationsPath = getMigrationsPath(entry, resolver)\n\n // Skip if no entities AND no migrations directory exists\n // (allows @app modules to run migrations even if entities can't be dynamically imported)\n if (!entities.length && !fs.existsSync(migrationsPath)) continue\n fs.mkdirSync(migrationsPath, { recursive: true })\n\n const tableName = `mikro_orm_migrations_${sanitizedModId}`\n validateTableName(tableName)\n\n // For @app modules, entities may be empty since TypeScript files can't be imported at runtime\n // Use discovery.warnWhenNoEntities: false to allow running migrations without entities\n const orm = await MikroORM.init<PostgreSqlDriver>({\n driver: PostgreSqlDriver,\n clientUrl: getClientUrl(),\n loggerFactory: () => createMinimalLogger(),\n entities: entities.length ? entities : [],\n discovery: { warnWhenNoEntities: false },\n migrations: {\n path: migrationsPath,\n glob: '!(*.d).{ts,js}',\n tableName,\n dropTables: false,\n },\n schemaGenerator: {\n disableForeignKeys: true,\n },\n pool: {\n min: 1,\n max: 3,\n idleTimeoutMillis: 30000,\n acquireTimeoutMillis: 60000,\n destroyTimeoutMillis: 30000,\n },\n })\n\n const migrator = orm.getMigrator() as Migrator\n const pending = await migrator.getPendingMigrations()\n if (!pending.length) {\n results.push(formatResult(modId, 'no pending migrations', ''))\n } else {\n const renderProgress = createProgressRenderer(pending.length)\n let applied = 0\n if (!QUIET_MODE) {\n process.stdout.write(` ${PROGRESS_EMOJI} ${modId}: ${renderProgress(applied)}`)\n }\n for (const migration of pending) {\n const migrationName =\n typeof migration === 'string'\n ? migration\n : (migration as any).name ?? (migration as any).fileName\n await migrator.up(migrationName ? { migrations: [migrationName] } : undefined)\n applied += 1\n if (!QUIET_MODE) {\n process.stdout.write(`\\r ${PROGRESS_EMOJI} ${modId}: ${renderProgress(applied)}`)\n }\n }\n if (!QUIET_MODE) process.stdout.write('\\n')\n results.push(\n formatResult(modId, `${pending.length} migration${pending.length === 1 ? '' : 's'} applied`, '')\n )\n }\n\n await orm.close(true)\n }\n\n console.log(results.join('\\n'))\n}\n\nexport async function dbGreenfield(resolver: PackageResolver, options: GreenfieldOptions): Promise<void> {\n if (!options.yes) {\n console.error('This command will DELETE all data. Use --yes to confirm.')\n process.exit(1)\n }\n\n console.log('Cleaning up migrations and snapshots for greenfield setup...')\n\n const modules = resolver.loadEnabledModules()\n const ordered = sortModules(modules)\n const results: string[] = []\n const outputDir = resolver.getOutputDir()\n\n for (const entry of ordered) {\n const modId = entry.id\n const migrationsPath = getMigrationsPath(entry, resolver)\n\n if (fs.existsSync(migrationsPath)) {\n // Remove all migration files\n const migrationFiles = fs\n .readdirSync(migrationsPath)\n .filter((file) => file.endsWith('.ts') && file.startsWith('Migration'))\n\n for (const file of migrationFiles) {\n fs.unlinkSync(path.join(migrationsPath, file))\n }\n\n // Remove snapshot files\n const snapshotFiles = fs\n .readdirSync(migrationsPath)\n .filter((file) => file.endsWith('.json') && file.includes('snapshot'))\n\n for (const file of snapshotFiles) {\n fs.unlinkSync(path.join(migrationsPath, file))\n }\n\n if (migrationFiles.length > 0 || snapshotFiles.length > 0) {\n results.push(\n formatResult(modId, `cleaned ${migrationFiles.length} migrations, ${snapshotFiles.length} snapshots`, '')\n )\n } else {\n results.push(formatResult(modId, 'already clean', ''))\n }\n } else {\n results.push(formatResult(modId, 'no migrations directory', ''))\n }\n\n // Clean up checksum files using glob pattern\n if (fs.existsSync(outputDir)) {\n const files = fs.readdirSync(outputDir)\n const checksumFiles = files.filter((file) => file.endsWith('.checksum'))\n\n for (const file of checksumFiles) {\n fs.unlinkSync(path.join(outputDir, file))\n }\n\n if (checksumFiles.length > 0) {\n results.push(formatResult(modId, `cleaned ${checksumFiles.length} checksum files`, ''))\n }\n }\n }\n\n console.log(results.join('\\n'))\n\n // Drop per-module MikroORM migration tables to ensure clean slate\n console.log('Dropping per-module migration tables...')\n try {\n const { Client } = await import('pg')\n const client = new Client({ connectionString: getClientUrl() })\n await client.connect()\n try {\n await client.query('BEGIN')\n for (const entry of ordered) {\n const modId = entry.id\n const sanitizedModId = sanitizeModuleId(modId)\n const tableName = `mikro_orm_migrations_${sanitizedModId}`\n validateTableName(tableName)\n await client.query(`DROP TABLE IF EXISTS \"${tableName}\"`)\n console.log(` ${modId}: dropped table ${tableName}`)\n }\n await client.query('COMMIT')\n } catch (e) {\n await client.query('ROLLBACK')\n throw e\n } finally {\n try {\n await client.end()\n } catch {}\n }\n } catch (e) {\n console.error('Failed to drop migration tables:', (e as any)?.message || e)\n throw e\n }\n\n // Drop all existing user tables to ensure fresh CREATE-only migrations\n console.log('Dropping ALL public tables for true greenfield...')\n try {\n const { Client } = await import('pg')\n const client = new Client({ connectionString: getClientUrl() })\n await client.connect()\n try {\n const res = await client.query(`SELECT tablename FROM pg_tables WHERE schemaname = 'public'`)\n const tables: string[] = (res.rows || []).map((r: any) => String(r.tablename))\n if (tables.length) {\n await client.query('BEGIN')\n try {\n await client.query(\"SET session_replication_role = 'replica'\")\n for (const t of tables) {\n await client.query(`DROP TABLE IF EXISTS \"${t}\" CASCADE`)\n }\n await client.query(\"SET session_replication_role = 'origin'\")\n await client.query('COMMIT')\n console.log(` Dropped ${tables.length} tables.`)\n } catch (e) {\n await client.query('ROLLBACK')\n throw e\n }\n } else {\n console.log(' No tables found to drop.')\n }\n } finally {\n try {\n await client.end()\n } catch {}\n }\n } catch (e) {\n console.error('Failed to drop public tables:', (e as any)?.message || e)\n throw e\n }\n\n // Generate fresh migrations for all modules\n console.log('Generating fresh migrations for all modules...')\n await dbGenerate(resolver)\n\n // Apply migrations\n console.log('Applying migrations...')\n await dbMigrate(resolver)\n\n console.log('Greenfield reset complete! Fresh migrations generated and applied.')\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -131,7 +131,7 @@ async function generateEntityIds(options) {
|
|
|
131
131
|
const appDb = path.join(roots.appBase, "db");
|
|
132
132
|
const pkgDb = path.join(roots.pkgBase, "db");
|
|
133
133
|
const bases = [appData, pkgData, appDb, pkgDb];
|
|
134
|
-
const candidates = ["entities.override.ts", "entities.
|
|
134
|
+
const candidates = ["entities.override.ts", "entities.ts", "schema.ts"];
|
|
135
135
|
let importPath = null;
|
|
136
136
|
let filePath = null;
|
|
137
137
|
for (const base of bases) {
|
|
@@ -140,8 +140,7 @@ async function generateEntityIds(options) {
|
|
|
140
140
|
if (fs.existsSync(p)) {
|
|
141
141
|
const fromApp = base.startsWith(roots.appBase);
|
|
142
142
|
const sub = path.basename(base);
|
|
143
|
-
|
|
144
|
-
importPath = `${fromApp ? imps.appBase : imps.pkgBase}/${sub}/${fileBaseName}`;
|
|
143
|
+
importPath = `${fromApp ? imps.appBase : imps.pkgBase}/${sub}/${f.replace(/\.ts$/, "")}`;
|
|
145
144
|
filePath = p;
|
|
146
145
|
break;
|
|
147
146
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/generators/entity-ids.ts"],
|
|
4
|
-
"sourcesContent": ["import fs from 'node:fs'\nimport path from 'node:path'\nimport ts from 'typescript'\nimport type { PackageResolver, ModuleEntry } from '../resolver'\nimport {\n calculateChecksum,\n readChecksumRecord,\n writeChecksumRecord,\n ensureDir,\n toVar,\n toSnake,\n rimrafDir,\n logGenerationResult,\n type GeneratorResult,\n createGeneratorResult,\n} from '../utils'\n\ntype GroupKey = '@app' | '@open-mercato/core' | string\ntype EntityFieldMap = Record<string, string[]>\n\nexport interface EntityIdsOptions {\n resolver: PackageResolver\n quiet?: boolean\n}\n\n/**\n * Extract exported class names from a TypeScript source file without dynamic import.\n * This is used for @app modules since Node.js can't import TypeScript files directly.\n */\nfunction parseExportedClassNamesFromFile(filePath: string): string[] {\n const src = fs.readFileSync(filePath, 'utf8')\n const sf = ts.createSourceFile(filePath, src, ts.ScriptTarget.ES2020, true, ts.ScriptKind.TS)\n const classNames: string[] = []\n\n sf.forEachChild((node) => {\n // Check for exported class declarations\n if (ts.isClassDeclaration(node) && node.name) {\n const hasExport = node.modifiers?.some(\n (m) => m.kind === ts.SyntaxKind.ExportKeyword\n )\n if (hasExport) {\n classNames.push(node.name.text)\n }\n }\n })\n\n return classNames\n}\n\nfunction parseEntityFieldsFromFile(filePath: string, exportedClassNames: string[]): EntityFieldMap {\n const src = fs.readFileSync(filePath, 'utf8')\n const sf = ts.createSourceFile(filePath, src, ts.ScriptTarget.ES2020, true, ts.ScriptKind.TS)\n\n const exported = new Set(exportedClassNames)\n const result: EntityFieldMap = {}\n\n function getDecoratorArgNameLiteral(dec: ts.Decorator | undefined): string | undefined {\n if (!dec) return undefined\n const expr = dec.expression\n if (!ts.isCallExpression(expr)) return undefined\n if (!expr.arguments.length) return undefined\n const first = expr.arguments[0]\n if (!ts.isObjectLiteralExpression(first)) return undefined\n for (const prop of first.properties) {\n if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === 'name') {\n if (ts.isStringLiteral(prop.initializer)) return prop.initializer.text\n }\n }\n return undefined\n }\n\n function normalizeDbName(propertyName: string, _decoratorName?: string, nameOverride?: string): string {\n if (nameOverride) return nameOverride\n return toSnake(propertyName)\n }\n\n sf.forEachChild((node) => {\n if (!ts.isClassDeclaration(node) || !node.name) return\n const clsName = node.name.text\n if (!exported.has(clsName)) return\n const entityKey = toSnake(clsName)\n const fields: string[] = []\n\n for (const member of node.members) {\n if (!ts.isPropertyDeclaration(member) || !member.name) continue\n const name = ts.isIdentifier(member.name)\n ? member.name.text\n : ts.isStringLiteral(member.name)\n ? member.name.text\n : undefined\n if (!name) continue\n if (member.modifiers?.some((m) => m.kind === ts.SyntaxKind.StaticKeyword)) continue\n const decorators = ts.canHaveDecorators(member)\n ? ts.getDecorators(member) ?? []\n : []\n let dbName: string | undefined\n if (decorators && decorators.length) {\n for (const d of decorators) {\n const nameOverride = getDecoratorArgNameLiteral(d)\n dbName = normalizeDbName(name, undefined, nameOverride)\n if (dbName) break\n }\n }\n if (!dbName) dbName = normalizeDbName(name)\n fields.push(dbName)\n }\n result[entityKey] = Array.from(new Set(fields))\n })\n\n return result\n}\n\nfunction writePerEntityFieldFiles(outRoot: string, fieldsByEntity: EntityFieldMap): void {\n fs.mkdirSync(outRoot, { recursive: true })\n rimrafDir(outRoot)\n fs.mkdirSync(outRoot, { recursive: true })\n for (const [entity, fields] of Object.entries(fieldsByEntity)) {\n const entDir = path.join(outRoot, entity)\n fs.mkdirSync(entDir, { recursive: true })\n const idx = fields.map((f) => `export const ${toVar(f)} = '${f}'`).join('\\n') + '\\n'\n fs.writeFileSync(path.join(entDir, 'index.ts'), idx)\n }\n}\n\nfunction writeEntityFieldsRegistry(generatedRoot: string, fieldsByEntity: EntityFieldMap): void {\n const entities = Object.keys(fieldsByEntity).sort()\n\n // Always write the file, even if empty, to prevent TypeScript import errors\n const imports = entities.length > 0\n ? entities.map((e) => `import * as ${toVar(e)} from './entities/${e}/index'`).join('\\n')\n : ''\n const registryEntries = entities.length > 0\n ? entities.map((e) => ` ${toVar(e)}`).join(',\\n')\n : ''\n\n const src = `// AUTO-GENERATED by mercato generate entity-ids\n// Static registry for entity fields - eliminates dynamic imports for Turbopack compatibility\n${imports}\n\nexport const entityFieldsRegistry: Record<string, Record<string, string>> = {\n${registryEntries}\n}\n\nexport function getEntityFields(slug: string): Record<string, string> | undefined {\n return entityFieldsRegistry[slug]\n}\n`\n const outPath = path.join(generatedRoot, 'entity-fields-registry.ts')\n ensureDir(outPath)\n fs.writeFileSync(outPath, src)\n}\n\nexport async function generateEntityIds(options: EntityIdsOptions): Promise<GeneratorResult> {\n const { resolver, quiet = false } = options\n const result = createGeneratorResult()\n\n const outputDir = resolver.getOutputDir()\n const outFile = path.join(outputDir, 'entities.ids.generated.ts')\n const checksumFile = path.join(outputDir, 'entities.ids.generated.checksum')\n\n const entries = resolver.loadEnabledModules()\n\n const consolidated: Record<string, Record<string, string>> = {}\n const grouped: Record<GroupKey, Record<string, Record<string, string>>> = {}\n const modulesDict: Record<string, string> = {}\n const groupedModulesDict: Record<GroupKey, Record<string, string>> = {}\n\n const fieldsByGroup: Record<GroupKey, Record<string, EntityFieldMap>> = {}\n\n for (const entry of entries) {\n const modId = entry.id\n const roots = resolver.getModulePaths(entry)\n const imps = resolver.getModuleImportBase(entry)\n const group: GroupKey = (entry.from as GroupKey) || '@open-mercato/core'\n\n // Locate entities definition file (prefer app override)\n const appData = path.join(roots.appBase, 'data')\n const pkgData = path.join(roots.pkgBase, 'data')\n const appDb = path.join(roots.appBase, 'db')\n const pkgDb = path.join(roots.pkgBase, 'db')\n const bases = [appData, pkgData, appDb, pkgDb]\n // Check both .ts (src/monorepo) and .js (dist/production) extensions\n const candidates = ['entities.override.ts', 'entities.override.js', 'entities.ts', 'entities.js', 'schema.ts', 'schema.js']\n let importPath: string | null = null\n let filePath: string | null = null\n\n for (const base of bases) {\n for (const f of candidates) {\n const p = path.join(base, f)\n if (fs.existsSync(p)) {\n const fromApp = base.startsWith(roots.appBase)\n const sub = path.basename(base) // 'data' | 'db'\n const fileBaseName = f.replace(/\\.(ts|js)$/, '')\n importPath = `${fromApp ? imps.appBase : imps.pkgBase}/${sub}/${fileBaseName}`\n filePath = p\n break\n }\n }\n if (importPath) break\n }\n\n // No entities file found -> still register module id\n if (!importPath) {\n modulesDict[modId] = modId\n groupedModulesDict[group] = groupedModulesDict[group] || {}\n groupedModulesDict[group][modId] = modId\n continue\n }\n\n // Get exported class names - either via dynamic import or TypeScript parsing\n let exportNames: string[]\n const isAppModule = entry.from === '@app'\n\n if (isAppModule && filePath) {\n // For @app modules, parse TypeScript source directly\n // since Node.js can't import TypeScript files (path alias @/ doesn't resolve at runtime)\n exportNames = parseExportedClassNamesFromFile(filePath)\n } else {\n // For package modules, use dynamic import\n let mod: Record<string, unknown>\n try {\n mod = await import(importPath)\n } catch (err) {\n // Module import failed, record error and skip\n const errorMessage = err instanceof Error ? err.message : String(err)\n result.errors.push(`Failed to import ${importPath}: ${errorMessage}`)\n modulesDict[modId] = modId\n groupedModulesDict[group] = groupedModulesDict[group] || {}\n groupedModulesDict[group][modId] = modId\n continue\n }\n exportNames = Object.keys(mod).filter((k) => typeof mod[k] === 'function')\n }\n\n const entityNames = exportNames\n .map((k) => toSnake(k))\n .filter((k, idx, arr) => arr.indexOf(k) === idx)\n\n // Build dictionaries\n modulesDict[modId] = modId\n groupedModulesDict[group] = groupedModulesDict[group] || {}\n groupedModulesDict[group][modId] = modId\n\n consolidated[modId] = consolidated[modId] || {}\n grouped[group] = grouped[group] || {}\n grouped[group][modId] = grouped[group][modId] || {}\n\n for (const en of entityNames) {\n consolidated[modId][en] = `${modId}:${en}`\n grouped[group][modId][en] = `${modId}:${en}`\n }\n\n if (filePath) {\n // exportNames already contains only class/function names from either source\n const entityFieldMap = parseEntityFieldsFromFile(filePath, exportNames)\n fieldsByGroup[group] = fieldsByGroup[group] || {}\n fieldsByGroup[group][modId] = entityFieldMap\n }\n }\n\n // Write consolidated output\n const consolidatedSrc = `// AUTO-GENERATED by mercato generate entity-ids\nexport const M = ${JSON.stringify(modulesDict, null, 2)} as const\nexport const E = ${JSON.stringify(consolidated, null, 2)} as const\nexport type KnownModuleId = keyof typeof M\nexport type KnownEntities = typeof E\n`\n\n // Check if content has changed\n const newChecksum = calculateChecksum(consolidatedSrc)\n let shouldWrite = true\n\n const existingRecord = readChecksumRecord(checksumFile)\n if (existingRecord && existingRecord.content === newChecksum) {\n shouldWrite = false\n }\n\n if (shouldWrite) {\n ensureDir(outFile)\n fs.writeFileSync(outFile, consolidatedSrc)\n writeChecksumRecord(checksumFile, { content: newChecksum, structure: '' })\n result.filesWritten.push(outFile)\n if (!quiet) {\n logGenerationResult(path.relative(process.cwd(), outFile), true)\n }\n } else {\n result.filesUnchanged.push(outFile)\n }\n\n // Write per-group outputs\n const groups = Object.keys(grouped) as GroupKey[]\n for (const g of groups) {\n const pkgOutputDir = resolver.getPackageOutputDir(g)\n // Skip @app group since it writes to the same location as the consolidated output\n if (g === '@app' && pkgOutputDir === outputDir) {\n continue\n }\n const out = path.join(pkgOutputDir, 'entities.ids.generated.ts')\n\n const src = `// AUTO-GENERATED by mercato generate entity-ids\nexport const M = ${JSON.stringify(groupedModulesDict[g] || {}, null, 2)} as const\nexport const E = ${JSON.stringify(grouped[g] || {}, null, 2)} as const\nexport type KnownModuleId = keyof typeof M\nexport type KnownEntities = typeof E\n`\n ensureDir(out)\n fs.writeFileSync(out, src)\n result.filesWritten.push(out)\n\n const fieldsRoot = path.join(pkgOutputDir, 'entities')\n const fieldsByModule = fieldsByGroup[g] || {}\n const combined: EntityFieldMap = {}\n for (const mId of Object.keys(fieldsByModule)) {\n const mMap = fieldsByModule[mId]\n for (const [entity, fields] of Object.entries(mMap)) {\n combined[entity] = Array.from(new Set([...(combined[entity] || []), ...fields]))\n }\n }\n writePerEntityFieldFiles(fieldsRoot, combined)\n\n // Generate static entity fields registry for Turbopack compatibility\n writeEntityFieldsRegistry(pkgOutputDir, combined)\n }\n\n // Write combined entity fields to root generated/ folder\n const combinedAll: EntityFieldMap = {}\n for (const groupFields of Object.values(fieldsByGroup)) {\n for (const mMap of Object.values(groupFields)) {\n for (const [entity, fields] of Object.entries(mMap)) {\n combinedAll[entity] = Array.from(new Set([...(combinedAll[entity] || []), ...fields]))\n }\n }\n }\n writePerEntityFieldFiles(path.join(outputDir, 'entities'), combinedAll)\n writeEntityFieldsRegistry(outputDir, combinedAll)\n\n return result\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import fs from 'node:fs'\nimport path from 'node:path'\nimport ts from 'typescript'\nimport type { PackageResolver, ModuleEntry } from '../resolver'\nimport {\n calculateChecksum,\n readChecksumRecord,\n writeChecksumRecord,\n ensureDir,\n toVar,\n toSnake,\n rimrafDir,\n logGenerationResult,\n type GeneratorResult,\n createGeneratorResult,\n} from '../utils'\n\ntype GroupKey = '@app' | '@open-mercato/core' | string\ntype EntityFieldMap = Record<string, string[]>\n\nexport interface EntityIdsOptions {\n resolver: PackageResolver\n quiet?: boolean\n}\n\n/**\n * Extract exported class names from a TypeScript source file without dynamic import.\n * This is used for @app modules since Node.js can't import TypeScript files directly.\n */\nfunction parseExportedClassNamesFromFile(filePath: string): string[] {\n const src = fs.readFileSync(filePath, 'utf8')\n const sf = ts.createSourceFile(filePath, src, ts.ScriptTarget.ES2020, true, ts.ScriptKind.TS)\n const classNames: string[] = []\n\n sf.forEachChild((node) => {\n // Check for exported class declarations\n if (ts.isClassDeclaration(node) && node.name) {\n const hasExport = node.modifiers?.some(\n (m) => m.kind === ts.SyntaxKind.ExportKeyword\n )\n if (hasExport) {\n classNames.push(node.name.text)\n }\n }\n })\n\n return classNames\n}\n\nfunction parseEntityFieldsFromFile(filePath: string, exportedClassNames: string[]): EntityFieldMap {\n const src = fs.readFileSync(filePath, 'utf8')\n const sf = ts.createSourceFile(filePath, src, ts.ScriptTarget.ES2020, true, ts.ScriptKind.TS)\n\n const exported = new Set(exportedClassNames)\n const result: EntityFieldMap = {}\n\n function getDecoratorArgNameLiteral(dec: ts.Decorator | undefined): string | undefined {\n if (!dec) return undefined\n const expr = dec.expression\n if (!ts.isCallExpression(expr)) return undefined\n if (!expr.arguments.length) return undefined\n const first = expr.arguments[0]\n if (!ts.isObjectLiteralExpression(first)) return undefined\n for (const prop of first.properties) {\n if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === 'name') {\n if (ts.isStringLiteral(prop.initializer)) return prop.initializer.text\n }\n }\n return undefined\n }\n\n function normalizeDbName(propertyName: string, _decoratorName?: string, nameOverride?: string): string {\n if (nameOverride) return nameOverride\n return toSnake(propertyName)\n }\n\n sf.forEachChild((node) => {\n if (!ts.isClassDeclaration(node) || !node.name) return\n const clsName = node.name.text\n if (!exported.has(clsName)) return\n const entityKey = toSnake(clsName)\n const fields: string[] = []\n\n for (const member of node.members) {\n if (!ts.isPropertyDeclaration(member) || !member.name) continue\n const name = ts.isIdentifier(member.name)\n ? member.name.text\n : ts.isStringLiteral(member.name)\n ? member.name.text\n : undefined\n if (!name) continue\n if (member.modifiers?.some((m) => m.kind === ts.SyntaxKind.StaticKeyword)) continue\n const decorators = ts.canHaveDecorators(member)\n ? ts.getDecorators(member) ?? []\n : []\n let dbName: string | undefined\n if (decorators && decorators.length) {\n for (const d of decorators) {\n const nameOverride = getDecoratorArgNameLiteral(d)\n dbName = normalizeDbName(name, undefined, nameOverride)\n if (dbName) break\n }\n }\n if (!dbName) dbName = normalizeDbName(name)\n fields.push(dbName)\n }\n result[entityKey] = Array.from(new Set(fields))\n })\n\n return result\n}\n\nfunction writePerEntityFieldFiles(outRoot: string, fieldsByEntity: EntityFieldMap): void {\n fs.mkdirSync(outRoot, { recursive: true })\n rimrafDir(outRoot)\n fs.mkdirSync(outRoot, { recursive: true })\n for (const [entity, fields] of Object.entries(fieldsByEntity)) {\n const entDir = path.join(outRoot, entity)\n fs.mkdirSync(entDir, { recursive: true })\n const idx = fields.map((f) => `export const ${toVar(f)} = '${f}'`).join('\\n') + '\\n'\n fs.writeFileSync(path.join(entDir, 'index.ts'), idx)\n }\n}\n\nfunction writeEntityFieldsRegistry(generatedRoot: string, fieldsByEntity: EntityFieldMap): void {\n const entities = Object.keys(fieldsByEntity).sort()\n\n // Always write the file, even if empty, to prevent TypeScript import errors\n const imports = entities.length > 0\n ? entities.map((e) => `import * as ${toVar(e)} from './entities/${e}/index'`).join('\\n')\n : ''\n const registryEntries = entities.length > 0\n ? entities.map((e) => ` ${toVar(e)}`).join(',\\n')\n : ''\n\n const src = `// AUTO-GENERATED by mercato generate entity-ids\n// Static registry for entity fields - eliminates dynamic imports for Turbopack compatibility\n${imports}\n\nexport const entityFieldsRegistry: Record<string, Record<string, string>> = {\n${registryEntries}\n}\n\nexport function getEntityFields(slug: string): Record<string, string> | undefined {\n return entityFieldsRegistry[slug]\n}\n`\n const outPath = path.join(generatedRoot, 'entity-fields-registry.ts')\n ensureDir(outPath)\n fs.writeFileSync(outPath, src)\n}\n\nexport async function generateEntityIds(options: EntityIdsOptions): Promise<GeneratorResult> {\n const { resolver, quiet = false } = options\n const result = createGeneratorResult()\n\n const outputDir = resolver.getOutputDir()\n const outFile = path.join(outputDir, 'entities.ids.generated.ts')\n const checksumFile = path.join(outputDir, 'entities.ids.generated.checksum')\n\n const entries = resolver.loadEnabledModules()\n\n const consolidated: Record<string, Record<string, string>> = {}\n const grouped: Record<GroupKey, Record<string, Record<string, string>>> = {}\n const modulesDict: Record<string, string> = {}\n const groupedModulesDict: Record<GroupKey, Record<string, string>> = {}\n\n const fieldsByGroup: Record<GroupKey, Record<string, EntityFieldMap>> = {}\n\n for (const entry of entries) {\n const modId = entry.id\n const roots = resolver.getModulePaths(entry)\n const imps = resolver.getModuleImportBase(entry)\n const group: GroupKey = (entry.from as GroupKey) || '@open-mercato/core'\n\n // Locate entities definition file (prefer app override)\n const appData = path.join(roots.appBase, 'data')\n const pkgData = path.join(roots.pkgBase, 'data')\n const appDb = path.join(roots.appBase, 'db')\n const pkgDb = path.join(roots.pkgBase, 'db')\n const bases = [appData, pkgData, appDb, pkgDb]\n const candidates = ['entities.override.ts', 'entities.ts', 'schema.ts']\n let importPath: string | null = null\n let filePath: string | null = null\n\n for (const base of bases) {\n for (const f of candidates) {\n const p = path.join(base, f)\n if (fs.existsSync(p)) {\n const fromApp = base.startsWith(roots.appBase)\n const sub = path.basename(base) // 'data' | 'db'\n importPath = `${fromApp ? imps.appBase : imps.pkgBase}/${sub}/${f.replace(/\\.ts$/, '')}`\n filePath = p\n break\n }\n }\n if (importPath) break\n }\n\n // No entities file found -> still register module id\n if (!importPath) {\n modulesDict[modId] = modId\n groupedModulesDict[group] = groupedModulesDict[group] || {}\n groupedModulesDict[group][modId] = modId\n continue\n }\n\n // Get exported class names - either via dynamic import or TypeScript parsing\n let exportNames: string[]\n const isAppModule = entry.from === '@app'\n\n if (isAppModule && filePath) {\n // For @app modules, parse TypeScript source directly\n // since Node.js can't import TypeScript files (path alias @/ doesn't resolve at runtime)\n exportNames = parseExportedClassNamesFromFile(filePath)\n } else {\n // For package modules, use dynamic import\n let mod: Record<string, unknown>\n try {\n mod = await import(importPath)\n } catch (err) {\n // Module import failed, record error and skip\n const errorMessage = err instanceof Error ? err.message : String(err)\n result.errors.push(`Failed to import ${importPath}: ${errorMessage}`)\n modulesDict[modId] = modId\n groupedModulesDict[group] = groupedModulesDict[group] || {}\n groupedModulesDict[group][modId] = modId\n continue\n }\n exportNames = Object.keys(mod).filter((k) => typeof mod[k] === 'function')\n }\n\n const entityNames = exportNames\n .map((k) => toSnake(k))\n .filter((k, idx, arr) => arr.indexOf(k) === idx)\n\n // Build dictionaries\n modulesDict[modId] = modId\n groupedModulesDict[group] = groupedModulesDict[group] || {}\n groupedModulesDict[group][modId] = modId\n\n consolidated[modId] = consolidated[modId] || {}\n grouped[group] = grouped[group] || {}\n grouped[group][modId] = grouped[group][modId] || {}\n\n for (const en of entityNames) {\n consolidated[modId][en] = `${modId}:${en}`\n grouped[group][modId][en] = `${modId}:${en}`\n }\n\n if (filePath) {\n // exportNames already contains only class/function names from either source\n const entityFieldMap = parseEntityFieldsFromFile(filePath, exportNames)\n fieldsByGroup[group] = fieldsByGroup[group] || {}\n fieldsByGroup[group][modId] = entityFieldMap\n }\n }\n\n // Write consolidated output\n const consolidatedSrc = `// AUTO-GENERATED by mercato generate entity-ids\nexport const M = ${JSON.stringify(modulesDict, null, 2)} as const\nexport const E = ${JSON.stringify(consolidated, null, 2)} as const\nexport type KnownModuleId = keyof typeof M\nexport type KnownEntities = typeof E\n`\n\n // Check if content has changed\n const newChecksum = calculateChecksum(consolidatedSrc)\n let shouldWrite = true\n\n const existingRecord = readChecksumRecord(checksumFile)\n if (existingRecord && existingRecord.content === newChecksum) {\n shouldWrite = false\n }\n\n if (shouldWrite) {\n ensureDir(outFile)\n fs.writeFileSync(outFile, consolidatedSrc)\n writeChecksumRecord(checksumFile, { content: newChecksum, structure: '' })\n result.filesWritten.push(outFile)\n if (!quiet) {\n logGenerationResult(path.relative(process.cwd(), outFile), true)\n }\n } else {\n result.filesUnchanged.push(outFile)\n }\n\n // Write per-group outputs\n const groups = Object.keys(grouped) as GroupKey[]\n for (const g of groups) {\n const pkgOutputDir = resolver.getPackageOutputDir(g)\n // Skip @app group since it writes to the same location as the consolidated output\n if (g === '@app' && pkgOutputDir === outputDir) {\n continue\n }\n const out = path.join(pkgOutputDir, 'entities.ids.generated.ts')\n\n const src = `// AUTO-GENERATED by mercato generate entity-ids\nexport const M = ${JSON.stringify(groupedModulesDict[g] || {}, null, 2)} as const\nexport const E = ${JSON.stringify(grouped[g] || {}, null, 2)} as const\nexport type KnownModuleId = keyof typeof M\nexport type KnownEntities = typeof E\n`\n ensureDir(out)\n fs.writeFileSync(out, src)\n result.filesWritten.push(out)\n\n const fieldsRoot = path.join(pkgOutputDir, 'entities')\n const fieldsByModule = fieldsByGroup[g] || {}\n const combined: EntityFieldMap = {}\n for (const mId of Object.keys(fieldsByModule)) {\n const mMap = fieldsByModule[mId]\n for (const [entity, fields] of Object.entries(mMap)) {\n combined[entity] = Array.from(new Set([...(combined[entity] || []), ...fields]))\n }\n }\n writePerEntityFieldFiles(fieldsRoot, combined)\n\n // Generate static entity fields registry for Turbopack compatibility\n writeEntityFieldsRegistry(pkgOutputDir, combined)\n }\n\n // Write combined entity fields to root generated/ folder\n const combinedAll: EntityFieldMap = {}\n for (const groupFields of Object.values(fieldsByGroup)) {\n for (const mMap of Object.values(groupFields)) {\n for (const [entity, fields] of Object.entries(mMap)) {\n combinedAll[entity] = Array.from(new Set([...(combinedAll[entity] || []), ...fields]))\n }\n }\n }\n writePerEntityFieldFiles(path.join(outputDir, 'entities'), combinedAll)\n writeEntityFieldsRegistry(outputDir, combinedAll)\n\n return result\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -23,12 +23,10 @@ async function generateModuleDi(options) {
|
|
|
23
23
|
const modId = entry.id;
|
|
24
24
|
const roots = resolver.getModulePaths(entry);
|
|
25
25
|
const imp = resolver.getModuleImportBase(entry);
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
const useApp = fs.existsSync(appDiTs) || fs.existsSync(appDiJs);
|
|
31
|
-
const usePkg = fs.existsSync(pkgDiTs) || fs.existsSync(pkgDiJs);
|
|
26
|
+
const appDi = path.join(roots.appBase, "di.ts");
|
|
27
|
+
const pkgDi = path.join(roots.pkgBase, "di.ts");
|
|
28
|
+
const useApp = fs.existsSync(appDi);
|
|
29
|
+
const usePkg = fs.existsSync(pkgDi);
|
|
32
30
|
const importName = `D_${toVar(modId)}_${i++}`;
|
|
33
31
|
if (useApp) {
|
|
34
32
|
const isAppModule = entry.from === "@app";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/generators/module-di.ts"],
|
|
4
|
-
"sourcesContent": ["import fs from 'node:fs'\nimport path from 'node:path'\nimport type { PackageResolver } from '../resolver'\nimport {\n calculateChecksum,\n readChecksumRecord,\n writeChecksumRecord,\n ensureDir,\n toVar,\n logGenerationResult,\n type GeneratorResult,\n createGeneratorResult,\n} from '../utils'\n\nexport interface ModuleDiOptions {\n resolver: PackageResolver\n quiet?: boolean\n}\n\nexport async function generateModuleDi(options: ModuleDiOptions): Promise<GeneratorResult> {\n const { resolver, quiet = false } = options\n const result = createGeneratorResult()\n\n const outputDir = resolver.getOutputDir()\n const outFile = path.join(outputDir, 'di.generated.ts')\n const checksumFile = path.join(outputDir, 'di.generated.checksum')\n\n const mods = resolver.loadEnabledModules()\n const imports: string[] = []\n const registrars: string[] = []\n let i = 0\n\n for (const entry of mods) {\n const modId = entry.id\n const roots = resolver.getModulePaths(entry)\n const imp = resolver.getModuleImportBase(entry)\n
|
|
5
|
-
"mappings": "AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AAEjB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,OACK;AAOP,eAAsB,iBAAiB,SAAoD;AACzF,QAAM,EAAE,UAAU,QAAQ,MAAM,IAAI;AACpC,QAAM,SAAS,sBAAsB;AAErC,QAAM,YAAY,SAAS,aAAa;AACxC,QAAM,UAAU,KAAK,KAAK,WAAW,iBAAiB;AACtD,QAAM,eAAe,KAAK,KAAK,WAAW,uBAAuB;AAEjE,QAAM,OAAO,SAAS,mBAAmB;AACzC,QAAM,UAAoB,CAAC;AAC3B,QAAM,aAAuB,CAAC;AAC9B,MAAI,IAAI;AAER,aAAW,SAAS,MAAM;AACxB,UAAM,QAAQ,MAAM;AACpB,UAAM,QAAQ,SAAS,eAAe,KAAK;AAC3C,UAAM,MAAM,SAAS,oBAAoB,KAAK;
|
|
4
|
+
"sourcesContent": ["import fs from 'node:fs'\nimport path from 'node:path'\nimport type { PackageResolver } from '../resolver'\nimport {\n calculateChecksum,\n readChecksumRecord,\n writeChecksumRecord,\n ensureDir,\n toVar,\n logGenerationResult,\n type GeneratorResult,\n createGeneratorResult,\n} from '../utils'\n\nexport interface ModuleDiOptions {\n resolver: PackageResolver\n quiet?: boolean\n}\n\nexport async function generateModuleDi(options: ModuleDiOptions): Promise<GeneratorResult> {\n const { resolver, quiet = false } = options\n const result = createGeneratorResult()\n\n const outputDir = resolver.getOutputDir()\n const outFile = path.join(outputDir, 'di.generated.ts')\n const checksumFile = path.join(outputDir, 'di.generated.checksum')\n\n const mods = resolver.loadEnabledModules()\n const imports: string[] = []\n const registrars: string[] = []\n let i = 0\n\n for (const entry of mods) {\n const modId = entry.id\n const roots = resolver.getModulePaths(entry)\n const imp = resolver.getModuleImportBase(entry)\n const appDi = path.join(roots.appBase, 'di.ts')\n const pkgDi = path.join(roots.pkgBase, 'di.ts')\n const useApp = fs.existsSync(appDi)\n const usePkg = fs.existsSync(pkgDi)\n const importName = `D_${toVar(modId)}_${i++}`\n\n if (useApp) {\n // For @app modules, use relative path to work in both Next.js and Node.js CLI context\n // From .mercato/generated/, go up two levels (../..) to reach the app root, then into src/modules/\n const isAppModule = entry.from === '@app'\n const importPath = isAppModule ? `../../src/modules/${modId}/di` : `${imp.appBase}/di`\n imports.push(`import * as ${importName} from '${importPath}'`)\n registrars.push(`${importName}.register`)\n } else if (usePkg) {\n imports.push(`import * as ${importName} from '${imp.pkgBase}/di'`)\n registrars.push(`${importName}.register`)\n }\n }\n\n const output = `// AUTO-GENERATED by mercato generate di\n${imports.join('\\n')}\n\nconst diRegistrars = [\n ${registrars.join(',\\n ')}\n].filter(Boolean) as (((c: any) => void)|undefined)[]\n\nexport { diRegistrars }\nexport default diRegistrars\n`\n\n // Check if content has changed\n const newChecksum = calculateChecksum(output)\n let shouldWrite = true\n\n const existingRecord = readChecksumRecord(checksumFile)\n if (existingRecord && existingRecord.content === newChecksum) {\n shouldWrite = false\n }\n\n if (shouldWrite) {\n ensureDir(outFile)\n fs.writeFileSync(outFile, output)\n writeChecksumRecord(checksumFile, { content: newChecksum, structure: '' })\n result.filesWritten.push(outFile)\n if (!quiet) {\n logGenerationResult(path.relative(process.cwd(), outFile), true)\n }\n } else {\n result.filesUnchanged.push(outFile)\n }\n\n return result\n}\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AAEjB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,OACK;AAOP,eAAsB,iBAAiB,SAAoD;AACzF,QAAM,EAAE,UAAU,QAAQ,MAAM,IAAI;AACpC,QAAM,SAAS,sBAAsB;AAErC,QAAM,YAAY,SAAS,aAAa;AACxC,QAAM,UAAU,KAAK,KAAK,WAAW,iBAAiB;AACtD,QAAM,eAAe,KAAK,KAAK,WAAW,uBAAuB;AAEjE,QAAM,OAAO,SAAS,mBAAmB;AACzC,QAAM,UAAoB,CAAC;AAC3B,QAAM,aAAuB,CAAC;AAC9B,MAAI,IAAI;AAER,aAAW,SAAS,MAAM;AACxB,UAAM,QAAQ,MAAM;AACpB,UAAM,QAAQ,SAAS,eAAe,KAAK;AAC3C,UAAM,MAAM,SAAS,oBAAoB,KAAK;AAC9C,UAAM,QAAQ,KAAK,KAAK,MAAM,SAAS,OAAO;AAC9C,UAAM,QAAQ,KAAK,KAAK,MAAM,SAAS,OAAO;AAC9C,UAAM,SAAS,GAAG,WAAW,KAAK;AAClC,UAAM,SAAS,GAAG,WAAW,KAAK;AAClC,UAAM,aAAa,KAAK,MAAM,KAAK,CAAC,IAAI,GAAG;AAE3C,QAAI,QAAQ;AAGV,YAAM,cAAc,MAAM,SAAS;AACnC,YAAM,aAAa,cAAc,qBAAqB,KAAK,QAAQ,GAAG,IAAI,OAAO;AACjF,cAAQ,KAAK,eAAe,UAAU,UAAU,UAAU,GAAG;AAC7D,iBAAW,KAAK,GAAG,UAAU,WAAW;AAAA,IAC1C,WAAW,QAAQ;AACjB,cAAQ,KAAK,eAAe,UAAU,UAAU,IAAI,OAAO,MAAM;AACjE,iBAAW,KAAK,GAAG,UAAU,WAAW;AAAA,IAC1C;AAAA,EACF;AAEA,QAAM,SAAS;AAAA,EACf,QAAQ,KAAK,IAAI,CAAC;AAAA;AAAA;AAAA,IAGhB,WAAW,KAAK,OAAO,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAQ1B,QAAM,cAAc,kBAAkB,MAAM;AAC5C,MAAI,cAAc;AAElB,QAAM,iBAAiB,mBAAmB,YAAY;AACtD,MAAI,kBAAkB,eAAe,YAAY,aAAa;AAC5D,kBAAc;AAAA,EAChB;AAEA,MAAI,aAAa;AACf,cAAU,OAAO;AACjB,OAAG,cAAc,SAAS,MAAM;AAChC,wBAAoB,cAAc,EAAE,SAAS,aAAa,WAAW,GAAG,CAAC;AACzE,WAAO,aAAa,KAAK,OAAO;AAChC,QAAI,CAAC,OAAO;AACV,0BAAoB,KAAK,SAAS,QAAQ,IAAI,GAAG,OAAO,GAAG,IAAI;AAAA,IACjE;AAAA,EACF,OAAO;AACL,WAAO,eAAe,KAAK,OAAO;AAAA,EACpC;AAEA,SAAO;AACT;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -28,7 +28,7 @@ async function generateModuleEntities(options) {
|
|
|
28
28
|
const appDb = path.join(roots.appBase, "db");
|
|
29
29
|
const pkgDb = path.join(roots.pkgBase, "db");
|
|
30
30
|
const bases = [appData, pkgData, appDb, pkgDb];
|
|
31
|
-
const candidates = ["entities.override.ts", "entities.
|
|
31
|
+
const candidates = ["entities.override.ts", "entities.ts", "schema.ts"];
|
|
32
32
|
let found = null;
|
|
33
33
|
for (const base of bases) {
|
|
34
34
|
for (const f of candidates) {
|
|
@@ -46,12 +46,11 @@ async function generateModuleEntities(options) {
|
|
|
46
46
|
const fromApp = found.base.startsWith(roots.appBase);
|
|
47
47
|
const isAppModule = entry.from === "@app";
|
|
48
48
|
let relImport;
|
|
49
|
-
const fileBaseName = found.file.replace(/\.(ts|js)$/, "");
|
|
50
49
|
if (isAppModule && fromApp) {
|
|
51
|
-
relImport = `../../src/modules/${modId}/${sub}/${
|
|
50
|
+
relImport = `../../src/modules/${modId}/${sub}/${found.file.replace(/\.ts$/, "")}`;
|
|
52
51
|
} else {
|
|
53
52
|
const baseImport = fromApp ? imp.appBase : imp.pkgBase;
|
|
54
|
-
relImport = `${baseImport}/${sub}/${
|
|
53
|
+
relImport = `${baseImport}/${sub}/${found.file.replace(/\.ts$/, "")}`;
|
|
55
54
|
}
|
|
56
55
|
imports.push(`import * as ${importName} from '${relImport}'`);
|
|
57
56
|
entitySources.push({ importName, moduleId: modId });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/generators/module-entities.ts"],
|
|
4
|
-
"sourcesContent": ["import fs from 'node:fs'\nimport path from 'node:path'\nimport type { PackageResolver } from '../resolver'\nimport {\n calculateChecksum,\n readChecksumRecord,\n writeChecksumRecord,\n ensureDir,\n toVar,\n logGenerationResult,\n type GeneratorResult,\n createGeneratorResult,\n} from '../utils'\n\nexport interface ModuleEntitiesOptions {\n resolver: PackageResolver\n quiet?: boolean\n}\n\nexport async function generateModuleEntities(options: ModuleEntitiesOptions): Promise<GeneratorResult> {\n const { resolver, quiet = false } = options\n const result = createGeneratorResult()\n\n const outputDir = resolver.getOutputDir()\n const outFile = path.join(outputDir, 'entities.generated.ts')\n const checksumFile = path.join(outputDir, 'entities.generated.checksum')\n\n const mods = resolver.loadEnabledModules()\n const imports: string[] = []\n const entitySources: Array<{ importName: string; moduleId: string }> = []\n let n = 0\n\n for (const entry of mods) {\n const modId = entry.id\n const roots = resolver.getModulePaths(entry)\n const imp = resolver.getModuleImportBase(entry)\n\n // prefer app override data/, fallback to core data/, then legacy db/\n const appData = path.join(roots.appBase, 'data')\n const pkgData = path.join(roots.pkgBase, 'data')\n const appDb = path.join(roots.appBase, 'db')\n const pkgDb = path.join(roots.pkgBase, 'db')\n const bases = [appData, pkgData, appDb, pkgDb]\n const candidates = ['entities.override.ts', 'entities.
|
|
5
|
-
"mappings": "AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AAEjB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,OACK;AAOP,eAAsB,uBAAuB,SAA0D;AACrG,QAAM,EAAE,UAAU,QAAQ,MAAM,IAAI;AACpC,QAAM,SAAS,sBAAsB;AAErC,QAAM,YAAY,SAAS,aAAa;AACxC,QAAM,UAAU,KAAK,KAAK,WAAW,uBAAuB;AAC5D,QAAM,eAAe,KAAK,KAAK,WAAW,6BAA6B;AAEvE,QAAM,OAAO,SAAS,mBAAmB;AACzC,QAAM,UAAoB,CAAC;AAC3B,QAAM,gBAAiE,CAAC;AACxE,MAAI,IAAI;AAER,aAAW,SAAS,MAAM;AACxB,UAAM,QAAQ,MAAM;AACpB,UAAM,QAAQ,SAAS,eAAe,KAAK;AAC3C,UAAM,MAAM,SAAS,oBAAoB,KAAK;AAG9C,UAAM,UAAU,KAAK,KAAK,MAAM,SAAS,MAAM;AAC/C,UAAM,UAAU,KAAK,KAAK,MAAM,SAAS,MAAM;AAC/C,UAAM,QAAQ,KAAK,KAAK,MAAM,SAAS,IAAI;AAC3C,UAAM,QAAQ,KAAK,KAAK,MAAM,SAAS,IAAI;AAC3C,UAAM,QAAQ,CAAC,SAAS,SAAS,OAAO,KAAK;AAC7C,UAAM,aAAa,CAAC,wBAAwB,
|
|
4
|
+
"sourcesContent": ["import fs from 'node:fs'\nimport path from 'node:path'\nimport type { PackageResolver } from '../resolver'\nimport {\n calculateChecksum,\n readChecksumRecord,\n writeChecksumRecord,\n ensureDir,\n toVar,\n logGenerationResult,\n type GeneratorResult,\n createGeneratorResult,\n} from '../utils'\n\nexport interface ModuleEntitiesOptions {\n resolver: PackageResolver\n quiet?: boolean\n}\n\nexport async function generateModuleEntities(options: ModuleEntitiesOptions): Promise<GeneratorResult> {\n const { resolver, quiet = false } = options\n const result = createGeneratorResult()\n\n const outputDir = resolver.getOutputDir()\n const outFile = path.join(outputDir, 'entities.generated.ts')\n const checksumFile = path.join(outputDir, 'entities.generated.checksum')\n\n const mods = resolver.loadEnabledModules()\n const imports: string[] = []\n const entitySources: Array<{ importName: string; moduleId: string }> = []\n let n = 0\n\n for (const entry of mods) {\n const modId = entry.id\n const roots = resolver.getModulePaths(entry)\n const imp = resolver.getModuleImportBase(entry)\n\n // prefer app override data/, fallback to core data/, then legacy db/\n const appData = path.join(roots.appBase, 'data')\n const pkgData = path.join(roots.pkgBase, 'data')\n const appDb = path.join(roots.appBase, 'db')\n const pkgDb = path.join(roots.pkgBase, 'db')\n const bases = [appData, pkgData, appDb, pkgDb]\n const candidates = ['entities.override.ts', 'entities.ts', 'schema.ts']\n\n let found: { base: string; file: string } | null = null\n for (const base of bases) {\n for (const f of candidates) {\n const p = path.join(base, f)\n if (fs.existsSync(p)) {\n found = { base, file: f }\n break\n }\n }\n if (found) break\n }\n if (!found) continue\n\n const importName = `E_${toVar(modId)}_${n++}`\n const sub = path.basename(found.base) // 'data' or 'db'\n const fromApp = found.base.startsWith(roots.appBase)\n const isAppModule = entry.from === '@app'\n // For @app modules, use relative path to ensure it works both in Next.js and Node.js CLI context\n // From .mercato/generated/, the relative path to src/modules/ is ../src/modules/\n let relImport: string\n if (isAppModule && fromApp) {\n // From .mercato/generated/, go up two levels (../..) to reach the app root, then into src/modules/\n relImport = `../../src/modules/${modId}/${sub}/${found.file.replace(/\\.ts$/, '')}`\n } else {\n const baseImport = fromApp ? imp.appBase : imp.pkgBase\n relImport = `${baseImport}/${sub}/${found.file.replace(/\\.ts$/, '')}`\n }\n imports.push(`import * as ${importName} from '${relImport}'`)\n entitySources.push({ importName, moduleId: modId })\n }\n\n const output = `// AUTO-GENERATED by mercato generate entities\n${imports.join('\\n')}\n\nfunction enhanceEntities(namespace: Record<string, unknown>, moduleId: string): any[] {\n return Object.entries(namespace)\n .filter(([, value]) => typeof value === 'function')\n .map(([exportName, value]) => {\n const entity = value as { entityName?: string }\n if (entity && typeof entity === 'function' && !Object.prototype.hasOwnProperty.call(entity, 'entityName')) {\n Object.defineProperty(entity, 'entityName', {\n value: \\`\\${moduleId}.\\${exportName}\\`,\n configurable: true,\n enumerable: false,\n writable: false,\n })\n }\n return entity\n })\n}\n\nexport const entities = [\n ${entitySources.map(({ importName, moduleId }) => `...enhanceEntities(${importName}, '${moduleId}')`).join(',\\n ')}\n]\n`\n\n // Check if content has changed\n const newChecksum = calculateChecksum(output)\n let shouldWrite = true\n\n const existingRecord = readChecksumRecord(checksumFile)\n if (existingRecord && existingRecord.content === newChecksum) {\n shouldWrite = false\n }\n\n if (shouldWrite) {\n ensureDir(outFile)\n fs.writeFileSync(outFile, output)\n writeChecksumRecord(checksumFile, { content: newChecksum, structure: '' })\n result.filesWritten.push(outFile)\n if (!quiet) {\n logGenerationResult(path.relative(process.cwd(), outFile), true)\n }\n } else {\n result.filesUnchanged.push(outFile)\n }\n\n return result\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|