Migrations ve Seeds düzenlemesi

This commit is contained in:
Sedat ÖZTÜRK 2026-08-12 15:20:00 +03:00
parent 783c1fe1d3
commit f292b6b6a5
30 changed files with 3526 additions and 255 deletions

View file

@ -542,11 +542,11 @@ When in doubt, AI must choose the path that preserves:
AI must learn and teach implementation flow primarily from these seed assets: AI must learn and teach implementation flow primarily from these seed assets:
- api/src/Sozsoft.Platform.DbMigrator/Seeds/ListFormSeeder_Saas.cs - api/src/Sozsoft.Platform.DbMigrator/Migrations/ListFormSeeder_Saas.cs
- api/src/Sozsoft.Platform.DbMigrator/Seeds/MenusData.json - api/src/Sozsoft.Platform.DbMigrator/Migrations/MenusData.json
- api/src/Sozsoft.Platform.DbMigrator/Seeds/PermissionsData.json - api/src/Sozsoft.Platform.DbMigrator/Migrations/PermissionsData.json
- api/src/Sozsoft.Platform.DbMigrator/Seeds/HostData.json - api/src/Sozsoft.Platform.DbMigrator/Migrations/HostData.json
- api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json - api/src/Sozsoft.Platform.DbMigrator/Migrations/LanguagesData.json
If user asks "how to add a new module/screen", AI must answer with this exact operational sequence. If user asks "how to add a new module/screen", AI must answer with this exact operational sequence.

View file

@ -240,11 +240,13 @@ host ayarları, ülke/şehir/ilçe verileri ile seed edilmiş ListForm ekranlar
```powershell ```powershell
cd ui cd ui
npm install npm install
npm start # component metadata üretir + vite dev server (http://localhost:3000) npm start # vite dev server (http://localhost:3000)
``` ```
`npm start`, `vite` öncesinde `scripts/generate-component-metadata.mjs` çalıştırır; bu script Visual Designer ve Custom Component altyapısının kullandığı bileşen kataloğu
Visual Designer ve Custom Component altyapısının kullandığı bileşen kataloğunu günceller. `src/components/visualDesigner/generated/componentProps.json` dosyasında tutulur ve repoya
dahildir. `src/components/ui` altındaki bileşen prop'ları değiştiğinde bu dosya elle
güncellenmelidir.
### 5.5. İlk giriş ### 5.5. İlk giriş
@ -804,11 +806,11 @@ Yeni ABP modülü eklemek için:
Yeni bir modülün nasıl kurulacağını öğrenmek için başvurulacak dosyalar: Yeni bir modülün nasıl kurulacağını öğrenmek için başvurulacak dosyalar:
- `api/src/Sozsoft.Platform.DbMigrator/Seeds/ListFormSeeder_Saas.cs` - `api/src/Sozsoft.Platform.DbMigrator/Seeds/ListFormSeeder_Saas.cs`
- `.../Seeds/ListFormSeeder_Administration.cs` - `.../Migrations/ListFormSeeder_Administration.cs`
- `.../Seeds/MenusData.json` - `.../Migrations/MenusData.json`
- `.../Seeds/PermissionsData.json` - `.../Migrations/PermissionsData.json`
- `.../Seeds/HostData.json` - `.../Migrations/HostData.json`
- `.../Seeds/LanguagesData.json` - `.../Migrations/LanguagesData.json`
--- ---

View file

@ -3,7 +3,7 @@ using Volo.Abp.Authorization.Permissions;
namespace Sozsoft.Platform.Permissions; namespace Sozsoft.Platform.Permissions;
/// <summary> /// <summary>
/// Uygulama yetkileri kod içinde tanımlanmaz; <c>Seeds/PermissionsData.json</c> /// Uygulama yetkileri kod içinde tanımlanmaz; <c>Migrations/PermissionsData.json</c>
/// dosyasından <c>PermissionDataSeeder</c> ile veritabanına seed edilir ve /// dosyasından <c>PermissionDataSeeder</c> ile veritabanına seed edilir ve
/// oradan dinamik olarak yönetilir. Bu sağlayıcı yalnızca kullanılmayan /// oradan dinamik olarak yönetilir. Bu sağlayıcı yalnızca kullanılmayan
/// ABP modül gruplarını kaldırmak için vardır. /// ABP modül gruplarını kaldırmak için vardır.

View file

@ -98,24 +98,23 @@ public class AfterAllMigrationsSqlExecutor : IAfterAllMigrationsSqlExecutor, ITr
var dbContext = await _dbContextProvider.GetDbContextAsync(); var dbContext = await _dbContextProvider.GetDbContextAsync();
_logger.LogInformation( _logger.LogInformation(
"Executing after-all-migrations procedure {Schema}.{Procedure} from {FileName}", "Executing procedure {Schema}.{Procedure}",
schemaName, schemaName,
procedureName, procedureName);
Path.GetFileName(filePath));
await dbContext.Database.ExecuteSqlRawAsync(BuildExecuteScript(schemaName, procedureName)); await dbContext.Database.ExecuteSqlRawAsync(BuildExecuteScript(schemaName, procedureName));
await uow.CompleteAsync(); await uow.CompleteAsync();
_logger.LogInformation( _logger.LogInformation(
"Executed after-all-migrations procedure {Schema}.{Procedure}", "Executed procedure {Schema}.{Procedure}",
schemaName, schemaName,
procedureName); procedureName);
} }
catch (Exception ex) catch (Exception ex)
{ {
failed.Add($"{schemaName}.{procedureName}"); failed.Add($"{schemaName}.{procedureName}");
_logger.LogError(ex, "After-all-migrations procedure failed: {Schema}.{Procedure}", schemaName, procedureName); _logger.LogError(ex, "Procedure failed: {Schema}.{Procedure}", schemaName, procedureName);
} }
} }

View file

@ -161,7 +161,7 @@ public class HostDataSeeder : IDataSeedContributor, ITransientDependency
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
using var fs = File.OpenRead(Path.Combine("Seeds", "CountryGroups.json")); using var fs = File.OpenRead(Path.Combine("Migrations", "CountryGroups.json"));
var buffer = new List<CountryGroup>(capacity: 1000); var buffer = new List<CountryGroup>(capacity: 1000);
var seenCodes = new HashSet<string>(); // JSON içindeki duplicate’leri yakalamak için var seenCodes = new HashSet<string>(); // JSON içindeki duplicate’leri yakalamak için
@ -219,7 +219,7 @@ public class HostDataSeeder : IDataSeedContributor, ITransientDependency
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
using var fs = File.OpenRead(Path.Combine("Seeds", "Countries.json")); using var fs = File.OpenRead(Path.Combine("Migrations", "Countries.json"));
var buffer = new List<Country>(capacity: 1000); var buffer = new List<Country>(capacity: 1000);
var seenCodes = new HashSet<string>(); // JSON içindeki duplicate’leri yakalamak için var seenCodes = new HashSet<string>(); // JSON içindeki duplicate’leri yakalamak için
@ -295,7 +295,7 @@ public class HostDataSeeder : IDataSeedContributor, ITransientDependency
}; };
// 2. JSON’u stream et // 2. JSON’u stream et
using FileStream fs = File.OpenRead(Path.Combine("Seeds", "Cities.json")); using FileStream fs = File.OpenRead(Path.Combine("Migrations", "Cities.json"));
var buffer = new List<City>(capacity: 5000); var buffer = new List<City>(capacity: 5000);
await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable<CityDto>(fs, options)) await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable<CityDto>(fs, options))
@ -355,7 +355,7 @@ public class HostDataSeeder : IDataSeedContributor, ITransientDependency
}; };
// 2. JSON’u stream et // 2. JSON’u stream et
using FileStream fs = File.OpenRead(Path.Combine("Seeds", "Districts.json")); using FileStream fs = File.OpenRead(Path.Combine("Migrations", "Districts.json"));
var buffer = new List<District>(capacity: 5000); var buffer = new List<District>(capacity: 5000);
await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable<DistrictDto>(fs, options)) await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable<DistrictDto>(fs, options))
@ -405,8 +405,8 @@ public class HostDataSeeder : IDataSeedContributor, ITransientDependency
var configuration = new ConfigurationBuilder() var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory()) .SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile(Path.Combine("Seeds", "HostData.json")) .AddJsonFile(Path.Combine("Migrations", "HostData.json"))
.AddJsonFile(Path.Combine("Seeds", $"HostData.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? ""}.json"), true) .AddJsonFile(Path.Combine("Migrations", $"HostData.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? ""}.json"), true)
.Build(); .Build();
var items = configuration.Get<HostSeederDto>(); var items = configuration.Get<HostSeederDto>();

View file

@ -42,7 +42,7 @@ public class LanguageDataSeeder : IDataSeedContributor, ITransientDependency
var configuration = new ConfigurationBuilder() var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory()) .SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile(Path.Combine("Seeds", "LanguagesData.json")) .AddJsonFile(Path.Combine("Migrations", "LanguagesData.json"))
.Build(); .Build();
var items = configuration.Get<LanguageSeederDto>(); var items = configuration.Get<LanguageSeederDto>();

View file

@ -66,7 +66,7 @@ public class MenuDataSeeder : IDataSeedContributor, ITransientDependency
{ {
var configuration = new ConfigurationBuilder() var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory()) .SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile(Path.Combine("Seeds", "MenusData.json")) .AddJsonFile(Path.Combine("Migrations", "MenusData.json"))
.Build(); .Build();
var items = configuration.Get<MenuSeederDto>(); var items = configuration.Get<MenuSeederDto>();

View file

@ -35,7 +35,7 @@ public class PermissionDataSeeder : IDataSeedContributor, ITransientDependency
{ {
var configuration = new ConfigurationBuilder() var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory()) .SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile(Path.Combine("Seeds", "PermissionsData.json")) .AddJsonFile(Path.Combine("Migrations", "PermissionsData.json"))
.Build(); .Build();
var items = configuration.Get<PermissionSeederDto>(); var items = configuration.Get<PermissionSeederDto>();

View file

@ -29,51 +29,51 @@
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<None Remove="Seeds\HostData.json" /> <None Remove="Migrations\HostData.json" />
<Content Include="Seeds\HostData.json"> <Content Include="Migrations\HostData.json">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<Content Include="Seeds\HostData.Dev.json"> <Content Include="Migrations\HostData.Dev.json">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<Content Include="Seeds\HostData.Production.json"> <Content Include="Migrations\HostData.Production.json">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<None Remove="Seeds\CountryGroups.json" /> <None Remove="Migrations\CountryGroups.json" />
<Content Include="Seeds\CountryGroups.json"> <Content Include="Migrations\CountryGroups.json">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<None Remove="Seeds\Countries.json" /> <None Remove="Migrations\Countries.json" />
<Content Include="Seeds\Countries.json"> <Content Include="Migrations\Countries.json">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<None Remove="Seeds\Cities.json" /> <None Remove="Migrations\Cities.json" />
<Content Include="Seeds\Cities.json"> <Content Include="Migrations\Cities.json">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<None Remove="Seeds\Districts.json" /> <None Remove="Migrations\Districts.json" />
<Content Include="Seeds\Districts.json"> <Content Include="Migrations\Districts.json">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<None Remove="Seeds\LanguagesData.json" /> <None Remove="Migrations\LanguagesData.json" />
<Content Include="Seeds\LanguagesData.json"> <Content Include="Migrations\LanguagesData.json">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<None Remove="Seeds\MenusData.json" /> <None Remove="Migrations\MenusData.json" />
<Content Include="Seeds\MenusData.json"> <Content Include="Migrations\MenusData.json">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<None Remove="Seeds\PermissionsData.json" /> <None Remove="Migrations\PermissionsData.json" />
<Content Include="Seeds\PermissionsData.json"> <Content Include="Migrations\PermissionsData.json">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>

View file

@ -528,7 +528,7 @@ public static class PlatformConsts
/// <summary> /// <summary>
/// Public sitedeki sayfa tasarim modu (`?design=1`) yetkileri. /// Public sitedeki sayfa tasarim modu (`?design=1`) yetkileri.
/// Yetkiler <c>Seeds/PermissionsData.json</c> icinde tanimlanir ve seeder ile yonetilir. /// Yetkiler <c>Migrations/PermissionsData.json</c> icinde tanimlanir ve seeder ile yonetilir.
/// </summary> /// </summary>
public static class WebSiteDesign public static class WebSiteDesign
{ {

View file

@ -118,21 +118,21 @@ public class PlatformDbMigrationService : ITransientDependency
{ {
if (!_afterAllMigrationsSqlExecutors.Any()) if (!_afterAllMigrationsSqlExecutors.Any())
{ {
Logger.LogInformation("No after-all-migrations SQL executor registered, skipping ExecuteData scripts."); Logger.LogInformation("No SQL executor registered, skipping ExecuteData scripts.");
return; return;
} }
Logger.LogInformation( Logger.LogInformation(
"Executing after-all-migrations SQL scripts for host and {TenantCount} tenant(s)...", "Executing SQL scripts for host and {TenantCount} tenant(s)...",
tenants.Count); tenants.Count);
using (_currentTenant.Change(null)) using (_currentTenant.Change(null))
{ {
foreach (var executor in _afterAllMigrationsSqlExecutors) foreach (var executor in _afterAllMigrationsSqlExecutors)
{ {
Logger.LogInformation("Running after-all-migrations SQL scripts for host..."); Logger.LogInformation("Running SQL scripts for host...");
await executor.ExecuteAsync(); await executor.ExecuteAsync();
Logger.LogInformation("Completed after-all-migrations SQL scripts for host."); Logger.LogInformation("Completed SQL scripts for host.");
} }
} }
@ -143,17 +143,17 @@ public class PlatformDbMigrationService : ITransientDependency
foreach (var executor in _afterAllMigrationsSqlExecutors) foreach (var executor in _afterAllMigrationsSqlExecutors)
{ {
Logger.LogInformation( Logger.LogInformation(
"Running after-all-migrations SQL scripts for {TenantName} tenant...", "Running SQL scripts for {TenantName} tenant...",
tenant.Name); tenant.Name);
await executor.ExecuteAsync(tenant.Id); await executor.ExecuteAsync(tenant.Id);
Logger.LogInformation( Logger.LogInformation(
"Completed after-all-migrations SQL scripts for {TenantName} tenant.", "Completed SQL scripts for {TenantName} tenant.",
tenant.Name); tenant.Name);
} }
} }
} }
Logger.LogInformation("Successfully completed all after-all-migrations SQL scripts."); Logger.LogInformation("Successfully completed all SQL scripts.");
} }
private async Task MigrateDatabaseSchemaAsync(Tenant? tenant = null) private async Task MigrateDatabaseSchemaAsync(Tenant? tenant = null)

View file

@ -445,7 +445,7 @@ public static class SeedConsts
/// <summary> /// <summary>
/// Public sitedeki sayfa tasarim modu (`?design=1`) yetkileri. /// Public sitedeki sayfa tasarim modu (`?design=1`) yetkileri.
/// Yetkiler <c>Seeds/PermissionsData.json</c> icinde tanimlanir ve seeder ile yonetilir. /// Yetkiler <c>Migrations/PermissionsData.json</c> icinde tanimlanir ve seeder ile yonetilir.
/// </summary> /// </summary>
public static class WebSiteDesign public static class WebSiteDesign
{ {

View file

@ -7,7 +7,7 @@
"AttachmentsPath": "C:\\Private\\Projects\\sozsoft-platform\\configs\\mail-queue\\attachments", "AttachmentsPath": "C:\\Private\\Projects\\sozsoft-platform\\configs\\mail-queue\\attachments",
"CdnUrl": "http://localhost:4005", "CdnUrl": "http://localhost:4005",
"CdnPath": "D:\\Sozsoft\\sozsoft-platform\\configs\\docker\\data\\cdn", "CdnPath": "D:\\Sozsoft\\sozsoft-platform\\configs\\docker\\data\\cdn",
"Version": "1.0.7", "Version": "1.1.2",
"BackupPath": "/var/opt/mssql/backup" "BackupPath": "/var/opt/mssql/backup"
}, },
"ConnectionStrings": { "ConnectionStrings": {

1
ui/.gitignore vendored
View file

@ -26,4 +26,3 @@ build
*.sw? *.sw?
.vite-cache/ .vite-cache/
src/devextreme-license.ts src/devextreme-license.ts
src/components/visualDesigner/generated/componentProps.json

View file

@ -5,10 +5,9 @@
"elstarVersion": "2.1.6", "elstarVersion": "2.1.6",
"type": "module", "type": "module",
"scripts": { "scripts": {
"generate:component-metadata": "node scripts/generate-component-metadata.mjs", "start": "vite",
"start": "node scripts/generate-component-metadata.mjs && vite", "build": "node scripts/generate-version.js && vite build",
"build": "node scripts/generate-component-metadata.mjs && node scripts/generate-version.js && vite build", "build:production": "vite build",
"build:production": "node scripts/generate-component-metadata.mjs && vite build",
"preview": "vite preview", "preview": "vite preview",
"lint": "eslint .", "lint": "eslint .",
"lint:fix": "npm run lint -- --fix", "lint:fix": "npm run lint -- --fix",

View file

@ -1,6 +1,22 @@
{ {
"commit": "81960df", "commit": "95a7b22",
"releases": [ "releases": [
{
"version": "1.1.12",
"buildDate": "2026-08-12",
"commit": "5cfd0105bc7100471edd21f8c1d215fdd6ab4d40",
"changeLog": [
"- Yeni versiyon güncellemesi"
]
},
{
"version": "1.1.11",
"buildDate": "2026-08-11",
"commit": "783c1fe1d35f7cb8f23fb63fad26123cbe54aafc",
"changeLog": [
"- Uygulamanın genel optimizasyonu"
]
},
{ {
"version": "1.1.10", "version": "1.1.10",
"buildDate": "2026-07-31", "buildDate": "2026-07-31",
@ -26,7 +42,7 @@
{ {
"version": "1.1.08", "version": "1.1.08",
"buildDate": "2026-07-16", "buildDate": "2026-07-16",
"commit": "24e92fcb82eef0ee26ca5d65e08c5404f132a09e", "commit": "e5afb66d89cefc48beff4bbaa00c140bdc58fadc",
"changeLog": [ "changeLog": [
"- CardView komponenti geliştirildi.", "- CardView komponenti geliştirildi.",
"- Todo / Kanban komponenti geliştirildi.", "- Todo / Kanban komponenti geliştirildi.",
@ -85,7 +101,7 @@
{ {
"version": "1.1.03", "version": "1.1.03",
"buildDate": "2026-05-30", "buildDate": "2026-05-30",
"commit": "96f7091d46c248ba3c42849fe5d870db0ab96982", "commit": "f9a65a3c8b2920ea40abcba501f0e8673f51f09b",
"changeLog": [ "changeLog": [
"- User Detail komponentinin içerisinde Avatar ekleme", "- User Detail komponentinin içerisinde Avatar ekleme",
"- EditForm un içerisinde EditorOptions dinamik oluşturulması", "- EditForm un içerisinde EditorOptions dinamik oluşturulması",
@ -129,7 +145,7 @@
{ {
"version": "1.0.9", "version": "1.0.9",
"buildDate": "2026-05-09", "buildDate": "2026-05-09",
"commit": "e9d8f5ebac35fc7b21f08139dfe175ed548c14b4", "commit": "13e35b961f4a0cb7288ee04e179fa6f61ee996ab",
"changeLog": [ "changeLog": [
"- Video Rooms düzenlemesi", "- Video Rooms düzenlemesi",
"- Genel Liste düzenlemeleri" "- Genel Liste düzenlemeleri"
@ -218,4 +234,4 @@
] ]
} }
] ]
} }

View file

@ -1,182 +0,0 @@
/* global console, process */
import fs from 'node:fs'
import path from 'node:path'
import prettier from 'prettier'
import ts from 'typescript'
import { fileURLToPath } from 'node:url'
const scriptDirectory = path.dirname(fileURLToPath(import.meta.url))
const uiRoot = path.resolve(scriptDirectory, '..')
const sourceRoot = path.join(uiRoot, 'src', 'components', 'ui')
const outputFile = path.join(sourceRoot, '..', 'visualDesigner', 'generated', 'componentProps.json')
const componentTypeOverrides = {
Calendar: 'CalenderProps',
ScrollBar: 'ScrollbarProps',
}
const namespaceOptions = {
'TypeAttributes.Size': ['lg', 'md', 'sm', 'xs'],
'TypeAttributes.ControlSize': ['lg', 'md', 'sm', 'xs'],
'TypeAttributes.Shape': ['round', 'circle', 'none'],
'TypeAttributes.Status': ['success', 'warning', 'danger', 'info'],
'TypeAttributes.FormLayout': ['horizontal', 'vertical', 'inline'],
'TypeAttributes.MenuVariant': ['light', 'dark', 'themed', 'transparent'],
'TypeAttributes.Direction': ['ltr', 'rtl'],
}
const walkFiles = (directory) =>
fs.readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
const target = path.join(directory, entry.name)
if (entry.isDirectory()) return walkFiles(target)
return /\.(ts|tsx)$/.test(entry.name) ? [target] : []
})
const sourceFiles = walkFiles(sourceRoot)
const metadataInputs = [...sourceFiles, fileURLToPath(import.meta.url)]
const outputIsCurrent =
!process.argv.includes('--force') &&
fs.existsSync(outputFile) &&
fs.statSync(outputFile).mtimeMs >=
Math.max(...metadataInputs.map((fileName) => fs.statSync(fileName).mtimeMs))
if (outputIsCurrent) {
console.log('Designer component metadata is up to date.')
process.exit(0)
}
const program = ts.createProgram(sourceFiles, {
target: ts.ScriptTarget.Latest,
jsx: ts.JsxEmit.ReactJSX,
})
const interfaces = new Map()
const defaultsByFile = new Map()
const literalValue = (node, sourceFile) => {
if (!node) return undefined
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) return node.text
if (ts.isNumericLiteral(node)) return Number(node.text)
if (node.kind === ts.SyntaxKind.TrueKeyword) return true
if (node.kind === ts.SyntaxKind.FalseKeyword) return false
if (node.kind === ts.SyntaxKind.NullKeyword) return null
if (ts.isArrayLiteralExpression(node)) {
const values = node.elements.map((element) => literalValue(element, sourceFile))
return values.some((value) => value === undefined) ? undefined : values
}
if (ts.isObjectLiteralExpression(node)) {
const result = {}
for (const property of node.properties) {
if (!ts.isPropertyAssignment(property)) return undefined
const name = property.name.getText(sourceFile).replace(/^['"]|['"]$/g, '')
const value = literalValue(property.initializer, sourceFile)
if (value === undefined) return undefined
result[name] = value
}
return result
}
return undefined
}
for (const sourceFile of program.getSourceFiles()) {
if (!path.resolve(sourceFile.fileName).startsWith(path.resolve(sourceRoot))) continue
const fileDefaults = {}
const visit = (node) => {
if (ts.isInterfaceDeclaration(node) && node.name.text.endsWith('Props')) {
interfaces.set(node.name.text, { node, sourceFile })
}
if (ts.isVariableDeclaration(node) && ts.isObjectBindingPattern(node.name)) {
for (const element of node.name.elements) {
if (!ts.isIdentifier(element.name) || !element.initializer) continue
const value = literalValue(element.initializer, sourceFile)
if (value !== undefined) fileDefaults[element.name.text] = value
}
}
ts.forEachChild(node, visit)
}
visit(sourceFile)
defaultsByFile.set(sourceFile.fileName, fileDefaults)
}
const indexSource = program.getSourceFile(path.join(sourceRoot, 'index.ts'))
if (!indexSource) throw new Error('UI component index could not be found.')
const componentNames = []
for (const statement of indexSource.statements) {
if (!ts.isExportDeclaration(statement) || statement.isTypeOnly || !statement.exportClause)
continue
if (!ts.isNamedExports(statement.exportClause)) continue
for (const element of statement.exportClause.elements) componentNames.push(element.name.text)
}
const unionOptions = (typeNode, sourceFile) => {
if (!typeNode) return []
const typeText = typeNode.getText(sourceFile)
if (namespaceOptions[typeText]) return namespaceOptions[typeText]
if (!ts.isUnionTypeNode(typeNode)) return []
return typeNode.types
.filter((type) => ts.isLiteralTypeNode(type) && ts.isStringLiteral(type.literal))
.map((type) => type.literal.text)
}
const propertyType = (typeNode, sourceFile) => {
const text = typeNode?.getText(sourceFile) || 'unknown'
const options = unionOptions(typeNode, sourceFile)
if (options.length) return { type: 'select', options }
if (text.includes('=>') || text.startsWith('MouseEventHandler')) return { type: 'function' }
if (/\bboolean\b/.test(text)) return { type: 'boolean' }
if (/\bnumber\b/.test(text)) return { type: 'number' }
if (/\[\]|Array<|ReadonlyArray</.test(text)) return { type: 'array' }
if (/CSSProperties|Record<|object/.test(text)) return { type: 'object' }
return { type: 'string' }
}
const commonProperties = [
{ name: 'className', tsType: 'string', type: 'string', required: false },
{ name: 'children', tsType: 'ReactNode', type: 'string', required: false },
{ name: 'style', tsType: 'CSSProperties', type: 'object', required: false },
]
const metadata = {}
for (const componentName of [...new Set(componentNames)]) {
const interfaceName = componentTypeOverrides[componentName] || `${componentName}Props`
const entry = interfaces.get(interfaceName)
if (!entry) continue
const { node, sourceFile } = entry
const defaults = defaultsByFile.get(sourceFile.fileName) || {}
const extendsCommonProps = node.heritageClauses?.some((clause) =>
clause.types.some((type) => type.expression.getText(sourceFile) === 'CommonProps'),
)
const properties = []
for (const member of node.members) {
if (!ts.isPropertySignature(member) || !member.name) continue
const name = member.name.getText(sourceFile).replace(/^['"]|['"]$/g, '')
const typeInfo = propertyType(member.type, sourceFile)
properties.push({
name,
tsType: member.type?.getText(sourceFile) || 'unknown',
...typeInfo,
required: !member.questionToken,
...(defaults[name] !== undefined ? { defaultValue: defaults[name] } : {}),
})
}
if (extendsCommonProps) {
for (const property of commonProperties) {
if (!properties.some((item) => item.name === property.name)) properties.push(property)
}
}
metadata[componentName] = {
interfaceName,
sourceFile: path.relative(uiRoot, sourceFile.fileName).replaceAll('\\', '/'),
properties,
}
}
fs.mkdirSync(path.dirname(outputFile), { recursive: true })
const formattedMetadata = await prettier.format(JSON.stringify(metadata), { parser: 'json' })
fs.writeFileSync(outputFile, formattedMetadata)
console.log(`Generated designer metadata for ${Object.keys(metadata).length} UI components.`)

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -35,7 +35,6 @@ const HASHED_ASSET_PATTERN = /\/assets\/.+\.(?:js|css|wasm)$/
export default defineConfig(({ mode }) => { export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '') const env = loadEnv(mode, process.cwd(), '')
const isProduction = mode === 'production'
const usePolling = env.VITE_USE_POLLING === 'true' const usePolling = env.VITE_USE_POLLING === 'true'
// Dev'de service worker varsayılan olarak kapalıdır; açık olduğunda HMR // Dev'de service worker varsayılan olarak kapalıdır; açık olduğunda HMR
// sonrası eski chunk'lar cache'den servis edilip kafa karıştırıyor. // sonrası eski chunk'lar cache'den servis edilip kafa karıştırıyor.
@ -179,7 +178,7 @@ export default defineConfig(({ mode }) => {
resolve: { resolve: {
alias: { alias: {
'@': path.join(__dirname, 'src'), '@': path.join(import.meta.dirname, 'src'),
inferno: 'inferno/dist/index.esm.js', inferno: 'inferno/dist/index.esm.js',
'devextreme/ui': 'devextreme/esm/ui', 'devextreme/ui': 'devextreme/esm/ui',
}, },
@ -212,11 +211,6 @@ export default defineConfig(({ mode }) => {
], ],
}, },
esbuild: {
legalComments: 'none',
drop: isProduction ? ['debugger'] : [],
},
build: { build: {
outDir: 'dist', outDir: 'dist',
// Vite'ın varsayılan 'baseline-widely-available' hedefi bilerek korunuyor: // Vite'ın varsayılan 'baseline-widely-available' hedefi bilerek korunuyor:
@ -232,6 +226,12 @@ export default defineConfig(({ mode }) => {
reportCompressedSize: false, reportCompressedSize: false,
rollupOptions: { rollupOptions: {
output: { output: {
// Eski `esbuild.legalComments: 'none'` karşılığı. `annotation`
// (@__PURE__) ve `jsdoc` varsayılan olarak korunur; annotation
// yorumları tree-shaking için gereklidir.
// `debugger` ifadeleri oxc minifier tarafından zaten düşürülüyor
// (compress.dropDebugger varsayılanı true), ayrıca ayar gerekmiyor.
comments: { legal: false },
// `entry-` öneki precache glob'unun sadece gerçek entry chunk'ı // `entry-` öneki precache glob'unun sadece gerçek entry chunk'ı
// yakalamasını sağlar (node_modules kaynaklı `index-*.js` chunk'ları // yakalamasını sağlar (node_modules kaynaklı `index-*.js` chunk'ları
// yanlışlıkla precache'e girmesin). // yanlışlıkla precache'e girmesin).