Migrations ve Seeds düzenlemesi
This commit is contained in:
parent
783c1fe1d3
commit
f292b6b6a5
30 changed files with 3526 additions and 255 deletions
10
.github/instructions/ai.instructions.md
vendored
10
.github/instructions/ai.instructions.md
vendored
|
|
@ -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:
|
||||
|
||||
- api/src/Sozsoft.Platform.DbMigrator/Seeds/ListFormSeeder_Saas.cs
|
||||
- api/src/Sozsoft.Platform.DbMigrator/Seeds/MenusData.json
|
||||
- api/src/Sozsoft.Platform.DbMigrator/Seeds/PermissionsData.json
|
||||
- api/src/Sozsoft.Platform.DbMigrator/Seeds/HostData.json
|
||||
- api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json
|
||||
- api/src/Sozsoft.Platform.DbMigrator/Migrations/ListFormSeeder_Saas.cs
|
||||
- api/src/Sozsoft.Platform.DbMigrator/Migrations/MenusData.json
|
||||
- api/src/Sozsoft.Platform.DbMigrator/Migrations/PermissionsData.json
|
||||
- api/src/Sozsoft.Platform.DbMigrator/Migrations/HostData.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.
|
||||
|
||||
|
|
|
|||
18
README.md
18
README.md
|
|
@ -240,11 +240,13 @@ host ayarları, ülke/şehir/ilçe verileri ile seed edilmiş ListForm ekranlar
|
|||
```powershell
|
||||
cd ui
|
||||
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ğunu günceller.
|
||||
Visual Designer ve Custom Component altyapısının kullandığı bileşen kataloğu
|
||||
`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ş
|
||||
|
||||
|
|
@ -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:
|
||||
|
||||
- `api/src/Sozsoft.Platform.DbMigrator/Seeds/ListFormSeeder_Saas.cs`
|
||||
- `.../Seeds/ListFormSeeder_Administration.cs`
|
||||
- `.../Seeds/MenusData.json`
|
||||
- `.../Seeds/PermissionsData.json`
|
||||
- `.../Seeds/HostData.json`
|
||||
- `.../Seeds/LanguagesData.json`
|
||||
- `.../Migrations/ListFormSeeder_Administration.cs`
|
||||
- `.../Migrations/MenusData.json`
|
||||
- `.../Migrations/PermissionsData.json`
|
||||
- `.../Migrations/HostData.json`
|
||||
- `.../Migrations/LanguagesData.json`
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ using Volo.Abp.Authorization.Permissions;
|
|||
namespace Sozsoft.Platform.Permissions;
|
||||
|
||||
/// <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
|
||||
/// oradan dinamik olarak yönetilir. Bu sağlayıcı yalnızca kullanılmayan
|
||||
/// ABP modül gruplarını kaldırmak için vardır.
|
||||
|
|
|
|||
|
|
@ -98,24 +98,23 @@ public class AfterAllMigrationsSqlExecutor : IAfterAllMigrationsSqlExecutor, ITr
|
|||
var dbContext = await _dbContextProvider.GetDbContextAsync();
|
||||
|
||||
_logger.LogInformation(
|
||||
"Executing after-all-migrations procedure {Schema}.{Procedure} from {FileName}",
|
||||
"Executing procedure {Schema}.{Procedure}",
|
||||
schemaName,
|
||||
procedureName,
|
||||
Path.GetFileName(filePath));
|
||||
procedureName);
|
||||
|
||||
await dbContext.Database.ExecuteSqlRawAsync(BuildExecuteScript(schemaName, procedureName));
|
||||
|
||||
await uow.CompleteAsync();
|
||||
|
||||
_logger.LogInformation(
|
||||
"Executed after-all-migrations procedure {Schema}.{Procedure}",
|
||||
"Executed procedure {Schema}.{Procedure}",
|
||||
schemaName,
|
||||
procedureName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ public class HostDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
|
||||
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 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 };
|
||||
|
||||
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 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
|
||||
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);
|
||||
await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable<CityDto>(fs, options))
|
||||
|
|
@ -355,7 +355,7 @@ public class HostDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
};
|
||||
|
||||
// 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);
|
||||
await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable<DistrictDto>(fs, options))
|
||||
|
|
@ -405,8 +405,8 @@ public class HostDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.SetBasePath(Directory.GetCurrentDirectory())
|
||||
.AddJsonFile(Path.Combine("Seeds", "HostData.json"))
|
||||
.AddJsonFile(Path.Combine("Seeds", $"HostData.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? ""}.json"), true)
|
||||
.AddJsonFile(Path.Combine("Migrations", "HostData.json"))
|
||||
.AddJsonFile(Path.Combine("Migrations", $"HostData.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? ""}.json"), true)
|
||||
.Build();
|
||||
var items = configuration.Get<HostSeederDto>();
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ public class LanguageDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.SetBasePath(Directory.GetCurrentDirectory())
|
||||
.AddJsonFile(Path.Combine("Seeds", "LanguagesData.json"))
|
||||
.AddJsonFile(Path.Combine("Migrations", "LanguagesData.json"))
|
||||
.Build();
|
||||
|
||||
var items = configuration.Get<LanguageSeederDto>();
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ public class MenuDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
{
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.SetBasePath(Directory.GetCurrentDirectory())
|
||||
.AddJsonFile(Path.Combine("Seeds", "MenusData.json"))
|
||||
.AddJsonFile(Path.Combine("Migrations", "MenusData.json"))
|
||||
.Build();
|
||||
|
||||
var items = configuration.Get<MenuSeederDto>();
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public class PermissionDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
{
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.SetBasePath(Directory.GetCurrentDirectory())
|
||||
.AddJsonFile(Path.Combine("Seeds", "PermissionsData.json"))
|
||||
.AddJsonFile(Path.Combine("Migrations", "PermissionsData.json"))
|
||||
.Build();
|
||||
|
||||
var items = configuration.Get<PermissionSeederDto>();
|
||||
|
|
|
|||
|
|
@ -29,51 +29,51 @@
|
|||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<None Remove="Seeds\HostData.json" />
|
||||
<Content Include="Seeds\HostData.json">
|
||||
<None Remove="Migrations\HostData.json" />
|
||||
<Content Include="Migrations\HostData.json">
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Seeds\HostData.Dev.json">
|
||||
<Content Include="Migrations\HostData.Dev.json">
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Seeds\HostData.Production.json">
|
||||
<Content Include="Migrations\HostData.Production.json">
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<None Remove="Seeds\CountryGroups.json" />
|
||||
<Content Include="Seeds\CountryGroups.json">
|
||||
<None Remove="Migrations\CountryGroups.json" />
|
||||
<Content Include="Migrations\CountryGroups.json">
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<None Remove="Seeds\Countries.json" />
|
||||
<Content Include="Seeds\Countries.json">
|
||||
<None Remove="Migrations\Countries.json" />
|
||||
<Content Include="Migrations\Countries.json">
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<None Remove="Seeds\Cities.json" />
|
||||
<Content Include="Seeds\Cities.json">
|
||||
<None Remove="Migrations\Cities.json" />
|
||||
<Content Include="Migrations\Cities.json">
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<None Remove="Seeds\Districts.json" />
|
||||
<Content Include="Seeds\Districts.json">
|
||||
<None Remove="Migrations\Districts.json" />
|
||||
<Content Include="Migrations\Districts.json">
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<None Remove="Seeds\LanguagesData.json" />
|
||||
<Content Include="Seeds\LanguagesData.json">
|
||||
<None Remove="Migrations\LanguagesData.json" />
|
||||
<Content Include="Migrations\LanguagesData.json">
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<None Remove="Seeds\MenusData.json" />
|
||||
<Content Include="Seeds\MenusData.json">
|
||||
<None Remove="Migrations\MenusData.json" />
|
||||
<Content Include="Migrations\MenusData.json">
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<None Remove="Seeds\PermissionsData.json" />
|
||||
<Content Include="Seeds\PermissionsData.json">
|
||||
<None Remove="Migrations\PermissionsData.json" />
|
||||
<Content Include="Migrations\PermissionsData.json">
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
|
|
|||
|
|
@ -528,7 +528,7 @@ public static class PlatformConsts
|
|||
|
||||
/// <summary>
|
||||
/// 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>
|
||||
public static class WebSiteDesign
|
||||
{
|
||||
|
|
|
|||
|
|
@ -118,21 +118,21 @@ public class PlatformDbMigrationService : ITransientDependency
|
|||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
using (_currentTenant.Change(null))
|
||||
{
|
||||
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();
|
||||
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)
|
||||
{
|
||||
Logger.LogInformation(
|
||||
"Running after-all-migrations SQL scripts for {TenantName} tenant...",
|
||||
"Running SQL scripts for {TenantName} tenant...",
|
||||
tenant.Name);
|
||||
await executor.ExecuteAsync(tenant.Id);
|
||||
Logger.LogInformation(
|
||||
"Completed after-all-migrations SQL scripts for {TenantName} tenant.",
|
||||
"Completed SQL scripts for {TenantName} tenant.",
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -445,7 +445,7 @@ public static class SeedConsts
|
|||
|
||||
/// <summary>
|
||||
/// 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>
|
||||
public static class WebSiteDesign
|
||||
{
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
"AttachmentsPath": "C:\\Private\\Projects\\sozsoft-platform\\configs\\mail-queue\\attachments",
|
||||
"CdnUrl": "http://localhost:4005",
|
||||
"CdnPath": "D:\\Sozsoft\\sozsoft-platform\\configs\\docker\\data\\cdn",
|
||||
"Version": "1.0.7",
|
||||
"Version": "1.1.2",
|
||||
"BackupPath": "/var/opt/mssql/backup"
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
|
|
|
|||
1
ui/.gitignore
vendored
1
ui/.gitignore
vendored
|
|
@ -26,4 +26,3 @@ build
|
|||
*.sw?
|
||||
.vite-cache/
|
||||
src/devextreme-license.ts
|
||||
src/components/visualDesigner/generated/componentProps.json
|
||||
|
|
|
|||
|
|
@ -5,10 +5,9 @@
|
|||
"elstarVersion": "2.1.6",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"generate:component-metadata": "node scripts/generate-component-metadata.mjs",
|
||||
"start": "node scripts/generate-component-metadata.mjs && vite",
|
||||
"build": "node scripts/generate-component-metadata.mjs && node scripts/generate-version.js && vite build",
|
||||
"build:production": "node scripts/generate-component-metadata.mjs && vite build",
|
||||
"start": "vite",
|
||||
"build": "node scripts/generate-version.js && vite build",
|
||||
"build:production": "vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "npm run lint -- --fix",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,22 @@
|
|||
{
|
||||
"commit": "81960df",
|
||||
"commit": "95a7b22",
|
||||
"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",
|
||||
"buildDate": "2026-07-31",
|
||||
|
|
@ -26,7 +42,7 @@
|
|||
{
|
||||
"version": "1.1.08",
|
||||
"buildDate": "2026-07-16",
|
||||
"commit": "24e92fcb82eef0ee26ca5d65e08c5404f132a09e",
|
||||
"commit": "e5afb66d89cefc48beff4bbaa00c140bdc58fadc",
|
||||
"changeLog": [
|
||||
"- CardView komponenti geliştirildi.",
|
||||
"- Todo / Kanban komponenti geliştirildi.",
|
||||
|
|
@ -85,7 +101,7 @@
|
|||
{
|
||||
"version": "1.1.03",
|
||||
"buildDate": "2026-05-30",
|
||||
"commit": "96f7091d46c248ba3c42849fe5d870db0ab96982",
|
||||
"commit": "f9a65a3c8b2920ea40abcba501f0e8673f51f09b",
|
||||
"changeLog": [
|
||||
"- User Detail komponentinin içerisinde Avatar ekleme",
|
||||
"- EditForm un içerisinde EditorOptions dinamik oluşturulması",
|
||||
|
|
@ -129,7 +145,7 @@
|
|||
{
|
||||
"version": "1.0.9",
|
||||
"buildDate": "2026-05-09",
|
||||
"commit": "e9d8f5ebac35fc7b21f08139dfe175ed548c14b4",
|
||||
"commit": "13e35b961f4a0cb7288ee04e179fa6f61ee996ab",
|
||||
"changeLog": [
|
||||
"- Video Rooms düzenlemesi",
|
||||
"- Genel Liste düzenlemeleri"
|
||||
|
|
@ -218,4 +234,4 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.`)
|
||||
3437
ui/src/components/visualDesigner/generated/componentProps.json
Normal file
3437
ui/src/components/visualDesigner/generated/componentProps.json
Normal file
File diff suppressed because it is too large
Load diff
1
ui/tsconfig.node.tsbuildinfo
Normal file
1
ui/tsconfig.node.tsbuildinfo
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -35,7 +35,6 @@ const HASHED_ASSET_PATTERN = /\/assets\/.+\.(?:js|css|wasm)$/
|
|||
|
||||
export default defineConfig(({ mode }) => {
|
||||
const env = loadEnv(mode, process.cwd(), '')
|
||||
const isProduction = mode === 'production'
|
||||
const usePolling = env.VITE_USE_POLLING === 'true'
|
||||
// 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.
|
||||
|
|
@ -179,7 +178,7 @@ export default defineConfig(({ mode }) => {
|
|||
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.join(__dirname, 'src'),
|
||||
'@': path.join(import.meta.dirname, 'src'),
|
||||
inferno: 'inferno/dist/index.esm.js',
|
||||
'devextreme/ui': 'devextreme/esm/ui',
|
||||
},
|
||||
|
|
@ -212,11 +211,6 @@ export default defineConfig(({ mode }) => {
|
|||
],
|
||||
},
|
||||
|
||||
esbuild: {
|
||||
legalComments: 'none',
|
||||
drop: isProduction ? ['debugger'] : [],
|
||||
},
|
||||
|
||||
build: {
|
||||
outDir: 'dist',
|
||||
// Vite'ın varsayılan 'baseline-widely-available' hedefi bilerek korunuyor:
|
||||
|
|
@ -232,6 +226,12 @@ export default defineConfig(({ mode }) => {
|
|||
reportCompressedSize: false,
|
||||
rollupOptions: {
|
||||
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'ı
|
||||
// yakalamasını sağlar (node_modules kaynaklı `index-*.js` chunk'ları
|
||||
// yanlışlıkla precache'e girmesin).
|
||||
|
|
|
|||
Loading…
Reference in a new issue