@cicore/cli 1.0.0
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/bin/ci.js +13 -0
- package/dist/commands/addon/api-actions.d.ts +45 -0
- package/dist/commands/addon/api-actions.d.ts.map +1 -0
- package/dist/commands/addon/api-actions.js +281 -0
- package/dist/commands/addon/api-actions.js.map +1 -0
- package/dist/commands/addon/build.d.ts +11 -0
- package/dist/commands/addon/build.d.ts.map +1 -0
- package/dist/commands/addon/build.js +182 -0
- package/dist/commands/addon/build.js.map +1 -0
- package/dist/commands/addon/create.d.ts +11 -0
- package/dist/commands/addon/create.d.ts.map +1 -0
- package/dist/commands/addon/create.js +1186 -0
- package/dist/commands/addon/create.js.map +1 -0
- package/dist/commands/addon/delete.d.ts +13 -0
- package/dist/commands/addon/delete.d.ts.map +1 -0
- package/dist/commands/addon/delete.js +83 -0
- package/dist/commands/addon/delete.js.map +1 -0
- package/dist/commands/addon/deploy.d.ts +27 -0
- package/dist/commands/addon/deploy.d.ts.map +1 -0
- package/dist/commands/addon/deploy.js +459 -0
- package/dist/commands/addon/deploy.js.map +1 -0
- package/dist/commands/addon/dev-deploy.d.ts +31 -0
- package/dist/commands/addon/dev-deploy.d.ts.map +1 -0
- package/dist/commands/addon/dev-deploy.js +128 -0
- package/dist/commands/addon/dev-deploy.js.map +1 -0
- package/dist/commands/addon/dev.d.ts +36 -0
- package/dist/commands/addon/dev.d.ts.map +1 -0
- package/dist/commands/addon/dev.js +323 -0
- package/dist/commands/addon/dev.js.map +1 -0
- package/dist/commands/addon/extract-classes.d.ts +23 -0
- package/dist/commands/addon/extract-classes.d.ts.map +1 -0
- package/dist/commands/addon/extract-classes.js +281 -0
- package/dist/commands/addon/extract-classes.js.map +1 -0
- package/dist/commands/addon/generate-safelist.d.ts +24 -0
- package/dist/commands/addon/generate-safelist.d.ts.map +1 -0
- package/dist/commands/addon/generate-safelist.js +276 -0
- package/dist/commands/addon/generate-safelist.js.map +1 -0
- package/dist/commands/addon/index.d.ts +19 -0
- package/dist/commands/addon/index.d.ts.map +1 -0
- package/dist/commands/addon/index.js +296 -0
- package/dist/commands/addon/index.js.map +1 -0
- package/dist/commands/addon/init-repo.d.ts +25 -0
- package/dist/commands/addon/init-repo.d.ts.map +1 -0
- package/dist/commands/addon/init-repo.js +171 -0
- package/dist/commands/addon/init-repo.js.map +1 -0
- package/dist/commands/addon/install.d.ts +23 -0
- package/dist/commands/addon/install.d.ts.map +1 -0
- package/dist/commands/addon/install.js +84 -0
- package/dist/commands/addon/install.js.map +1 -0
- package/dist/commands/addon/list.d.ts +10 -0
- package/dist/commands/addon/list.d.ts.map +1 -0
- package/dist/commands/addon/list.js +102 -0
- package/dist/commands/addon/list.js.map +1 -0
- package/dist/commands/addon/manifest-refresh.d.ts +17 -0
- package/dist/commands/addon/manifest-refresh.d.ts.map +1 -0
- package/dist/commands/addon/manifest-refresh.js +48 -0
- package/dist/commands/addon/manifest-refresh.js.map +1 -0
- package/dist/commands/addon/migrate.d.ts +40 -0
- package/dist/commands/addon/migrate.d.ts.map +1 -0
- package/dist/commands/addon/migrate.js +236 -0
- package/dist/commands/addon/migrate.js.map +1 -0
- package/dist/commands/addon/publish.d.ts +33 -0
- package/dist/commands/addon/publish.d.ts.map +1 -0
- package/dist/commands/addon/publish.js +236 -0
- package/dist/commands/addon/publish.js.map +1 -0
- package/dist/commands/addon/scaffold-quality.d.ts +21 -0
- package/dist/commands/addon/scaffold-quality.d.ts.map +1 -0
- package/dist/commands/addon/scaffold-quality.js +90 -0
- package/dist/commands/addon/scaffold-quality.js.map +1 -0
- package/dist/commands/addon/sign.d.ts +9 -0
- package/dist/commands/addon/sign.d.ts.map +1 -0
- package/dist/commands/addon/sign.js +83 -0
- package/dist/commands/addon/sign.js.map +1 -0
- package/dist/commands/addon/toggle.d.ts +6 -0
- package/dist/commands/addon/toggle.d.ts.map +1 -0
- package/dist/commands/addon/toggle.js +46 -0
- package/dist/commands/addon/toggle.js.map +1 -0
- package/dist/commands/agent/index.d.ts +34 -0
- package/dist/commands/agent/index.d.ts.map +1 -0
- package/dist/commands/agent/index.js +564 -0
- package/dist/commands/agent/index.js.map +1 -0
- package/dist/commands/brand/index.d.ts +54 -0
- package/dist/commands/brand/index.d.ts.map +1 -0
- package/dist/commands/brand/index.js +367 -0
- package/dist/commands/brand/index.js.map +1 -0
- package/dist/commands/build/index.d.ts +53 -0
- package/dist/commands/build/index.d.ts.map +1 -0
- package/dist/commands/build/index.js +726 -0
- package/dist/commands/build/index.js.map +1 -0
- package/dist/commands/cache/flush-local.d.ts +31 -0
- package/dist/commands/cache/flush-local.d.ts.map +1 -0
- package/dist/commands/cache/flush-local.js +161 -0
- package/dist/commands/cache/flush-local.js.map +1 -0
- package/dist/commands/cache/index.d.ts +14 -0
- package/dist/commands/cache/index.d.ts.map +1 -0
- package/dist/commands/cache/index.js +453 -0
- package/dist/commands/cache/index.js.map +1 -0
- package/dist/commands/check/index.d.ts +8 -0
- package/dist/commands/check/index.d.ts.map +1 -0
- package/dist/commands/check/index.js +1316 -0
- package/dist/commands/check/index.js.map +1 -0
- package/dist/commands/cloudflare/index.d.ts +8 -0
- package/dist/commands/cloudflare/index.d.ts.map +1 -0
- package/dist/commands/cloudflare/index.js +453 -0
- package/dist/commands/cloudflare/index.js.map +1 -0
- package/dist/commands/core/create.d.ts +12 -0
- package/dist/commands/core/create.d.ts.map +1 -0
- package/dist/commands/core/create.js +206 -0
- package/dist/commands/core/create.js.map +1 -0
- package/dist/commands/core/delete.d.ts +11 -0
- package/dist/commands/core/delete.d.ts.map +1 -0
- package/dist/commands/core/delete.js +64 -0
- package/dist/commands/core/delete.js.map +1 -0
- package/dist/commands/core/env.d.ts +12 -0
- package/dist/commands/core/env.d.ts.map +1 -0
- package/dist/commands/core/env.js +95 -0
- package/dist/commands/core/env.js.map +1 -0
- package/dist/commands/core/health.d.ts +6 -0
- package/dist/commands/core/health.d.ts.map +1 -0
- package/dist/commands/core/health.js +215 -0
- package/dist/commands/core/health.js.map +1 -0
- package/dist/commands/core/index.d.ts +15 -0
- package/dist/commands/core/index.d.ts.map +1 -0
- package/dist/commands/core/index.js +86 -0
- package/dist/commands/core/index.js.map +1 -0
- package/dist/commands/core/list.d.ts +11 -0
- package/dist/commands/core/list.d.ts.map +1 -0
- package/dist/commands/core/list.js +58 -0
- package/dist/commands/core/list.js.map +1 -0
- package/dist/commands/core/rebuild.d.ts +13 -0
- package/dist/commands/core/rebuild.d.ts.map +1 -0
- package/dist/commands/core/rebuild.js +119 -0
- package/dist/commands/core/rebuild.js.map +1 -0
- package/dist/commands/db/index.d.ts +23 -0
- package/dist/commands/db/index.d.ts.map +1 -0
- package/dist/commands/db/index.js +355 -0
- package/dist/commands/db/index.js.map +1 -0
- package/dist/commands/db/promote-silo.d.ts +320 -0
- package/dist/commands/db/promote-silo.d.ts.map +1 -0
- package/dist/commands/db/promote-silo.js +930 -0
- package/dist/commands/db/promote-silo.js.map +1 -0
- package/dist/commands/db/relocate.d.ts +41 -0
- package/dist/commands/db/relocate.d.ts.map +1 -0
- package/dist/commands/db/relocate.js +482 -0
- package/dist/commands/db/relocate.js.map +1 -0
- package/dist/commands/db/rollback-silo.d.ts +44 -0
- package/dist/commands/db/rollback-silo.d.ts.map +1 -0
- package/dist/commands/db/rollback-silo.js +402 -0
- package/dist/commands/db/rollback-silo.js.map +1 -0
- package/dist/commands/deploy/index.d.ts +26 -0
- package/dist/commands/deploy/index.d.ts.map +1 -0
- package/dist/commands/deploy/index.js +107 -0
- package/dist/commands/deploy/index.js.map +1 -0
- package/dist/commands/devops/index.d.ts +6 -0
- package/dist/commands/devops/index.d.ts.map +1 -0
- package/dist/commands/devops/index.js +220 -0
- package/dist/commands/devops/index.js.map +1 -0
- package/dist/commands/domain/index.d.ts +8 -0
- package/dist/commands/domain/index.d.ts.map +1 -0
- package/dist/commands/domain/index.js +386 -0
- package/dist/commands/domain/index.js.map +1 -0
- package/dist/commands/image/index.d.ts +8 -0
- package/dist/commands/image/index.d.ts.map +1 -0
- package/dist/commands/image/index.js +308 -0
- package/dist/commands/image/index.js.map +1 -0
- package/dist/commands/install/factory-reset.d.ts +21 -0
- package/dist/commands/install/factory-reset.d.ts.map +1 -0
- package/dist/commands/install/factory-reset.js +83 -0
- package/dist/commands/install/factory-reset.js.map +1 -0
- package/dist/commands/install/index.d.ts +17 -0
- package/dist/commands/install/index.d.ts.map +1 -0
- package/dist/commands/install/index.js +44 -0
- package/dist/commands/install/index.js.map +1 -0
- package/dist/commands/install/install.d.ts +35 -0
- package/dist/commands/install/install.d.ts.map +1 -0
- package/dist/commands/install/install.js +171 -0
- package/dist/commands/install/install.js.map +1 -0
- package/dist/commands/login/index.d.ts +15 -0
- package/dist/commands/login/index.d.ts.map +1 -0
- package/dist/commands/login/index.js +58 -0
- package/dist/commands/login/index.js.map +1 -0
- package/dist/commands/nginx/index.d.ts +11 -0
- package/dist/commands/nginx/index.d.ts.map +1 -0
- package/dist/commands/nginx/index.js +580 -0
- package/dist/commands/nginx/index.js.map +1 -0
- package/dist/commands/server/bootstrap.d.ts +25 -0
- package/dist/commands/server/bootstrap.d.ts.map +1 -0
- package/dist/commands/server/bootstrap.js +260 -0
- package/dist/commands/server/bootstrap.js.map +1 -0
- package/dist/commands/server/index.d.ts +8 -0
- package/dist/commands/server/index.d.ts.map +1 -0
- package/dist/commands/server/index.js +2524 -0
- package/dist/commands/server/index.js.map +1 -0
- package/dist/commands/setup/index.d.ts +34 -0
- package/dist/commands/setup/index.d.ts.map +1 -0
- package/dist/commands/setup/index.js +423 -0
- package/dist/commands/setup/index.js.map +1 -0
- package/dist/commands/ssl/index.d.ts +8 -0
- package/dist/commands/ssl/index.d.ts.map +1 -0
- package/dist/commands/ssl/index.js +275 -0
- package/dist/commands/ssl/index.js.map +1 -0
- package/dist/commands/superadmin/index.d.ts +16 -0
- package/dist/commands/superadmin/index.d.ts.map +1 -0
- package/dist/commands/superadmin/index.js +81 -0
- package/dist/commands/superadmin/index.js.map +1 -0
- package/dist/commands/tenant/index.d.ts +6 -0
- package/dist/commands/tenant/index.d.ts.map +1 -0
- package/dist/commands/tenant/index.js +192 -0
- package/dist/commands/tenant/index.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +107 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/addon-sign.d.ts +23 -0
- package/dist/lib/addon-sign.d.ts.map +1 -0
- package/dist/lib/addon-sign.js +39 -0
- package/dist/lib/addon-sign.js.map +1 -0
- package/dist/lib/addon-sign.test.d.ts +2 -0
- package/dist/lib/addon-sign.test.d.ts.map +1 -0
- package/dist/lib/addon-sign.test.js +27 -0
- package/dist/lib/addon-sign.test.js.map +1 -0
- package/dist/lib/cdn.d.ts +25 -0
- package/dist/lib/cdn.d.ts.map +1 -0
- package/dist/lib/cdn.js +131 -0
- package/dist/lib/cdn.js.map +1 -0
- package/dist/lib/cloudflare.d.ts +133 -0
- package/dist/lib/cloudflare.d.ts.map +1 -0
- package/dist/lib/cloudflare.js +435 -0
- package/dist/lib/cloudflare.js.map +1 -0
- package/dist/lib/config.d.ts +96 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +132 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/env.d.ts +8 -0
- package/dist/lib/env.d.ts.map +1 -0
- package/dist/lib/env.js +64 -0
- package/dist/lib/env.js.map +1 -0
- package/dist/lib/hosts.d.ts +194 -0
- package/dist/lib/hosts.d.ts.map +1 -0
- package/dist/lib/hosts.js +183 -0
- package/dist/lib/hosts.js.map +1 -0
- package/dist/lib/logger.d.ts +68 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +130 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/nginx-config.d.ts +78 -0
- package/dist/lib/nginx-config.d.ts.map +1 -0
- package/dist/lib/nginx-config.js +736 -0
- package/dist/lib/nginx-config.js.map +1 -0
- package/dist/lib/ops/addon-dev.d.ts +93 -0
- package/dist/lib/ops/addon-dev.d.ts.map +1 -0
- package/dist/lib/ops/addon-dev.js +237 -0
- package/dist/lib/ops/addon-dev.js.map +1 -0
- package/dist/lib/ops/addon-quality.d.ts +38 -0
- package/dist/lib/ops/addon-quality.d.ts.map +1 -0
- package/dist/lib/ops/addon-quality.js +338 -0
- package/dist/lib/ops/addon-quality.js.map +1 -0
- package/dist/lib/ops/addon-routes.d.ts +49 -0
- package/dist/lib/ops/addon-routes.d.ts.map +1 -0
- package/dist/lib/ops/addon-routes.js +189 -0
- package/dist/lib/ops/addon-routes.js.map +1 -0
- package/dist/lib/ops/addon.d.ts +120 -0
- package/dist/lib/ops/addon.d.ts.map +1 -0
- package/dist/lib/ops/addon.js +260 -0
- package/dist/lib/ops/addon.js.map +1 -0
- package/dist/lib/ops/cdn.d.ts +87 -0
- package/dist/lib/ops/cdn.d.ts.map +1 -0
- package/dist/lib/ops/cdn.js +170 -0
- package/dist/lib/ops/cdn.js.map +1 -0
- package/dist/lib/ops/cf.d.ts +36 -0
- package/dist/lib/ops/cf.d.ts.map +1 -0
- package/dist/lib/ops/cf.js +114 -0
- package/dist/lib/ops/cf.js.map +1 -0
- package/dist/lib/ops/compose.d.ts +95 -0
- package/dist/lib/ops/compose.d.ts.map +1 -0
- package/dist/lib/ops/compose.js +165 -0
- package/dist/lib/ops/compose.js.map +1 -0
- package/dist/lib/ops/core.d.ts +117 -0
- package/dist/lib/ops/core.d.ts.map +1 -0
- package/dist/lib/ops/core.js +322 -0
- package/dist/lib/ops/core.js.map +1 -0
- package/dist/lib/ops/db.d.ts +116 -0
- package/dist/lib/ops/db.d.ts.map +1 -0
- package/dist/lib/ops/db.js +351 -0
- package/dist/lib/ops/db.js.map +1 -0
- package/dist/lib/ops/dns.d.ts +111 -0
- package/dist/lib/ops/dns.d.ts.map +1 -0
- package/dist/lib/ops/dns.js +306 -0
- package/dist/lib/ops/dns.js.map +1 -0
- package/dist/lib/ops/image.d.ts +94 -0
- package/dist/lib/ops/image.d.ts.map +1 -0
- package/dist/lib/ops/image.js +159 -0
- package/dist/lib/ops/image.js.map +1 -0
- package/dist/lib/ops/nginx.d.ts +114 -0
- package/dist/lib/ops/nginx.d.ts.map +1 -0
- package/dist/lib/ops/nginx.js +388 -0
- package/dist/lib/ops/nginx.js.map +1 -0
- package/dist/lib/ops/redis.d.ts +7 -0
- package/dist/lib/ops/redis.d.ts.map +1 -0
- package/dist/lib/ops/redis.js +35 -0
- package/dist/lib/ops/redis.js.map +1 -0
- package/dist/lib/ops/ssh.d.ts +127 -0
- package/dist/lib/ops/ssh.d.ts.map +1 -0
- package/dist/lib/ops/ssh.js +269 -0
- package/dist/lib/ops/ssh.js.map +1 -0
- package/dist/lib/prompts.d.ts +46 -0
- package/dist/lib/prompts.d.ts.map +1 -0
- package/dist/lib/prompts.js +113 -0
- package/dist/lib/prompts.js.map +1 -0
- package/dist/lib/sast.d.ts +43 -0
- package/dist/lib/sast.d.ts.map +1 -0
- package/dist/lib/sast.js +79 -0
- package/dist/lib/sast.js.map +1 -0
- package/dist/lib/sast.test.d.ts +2 -0
- package/dist/lib/sast.test.d.ts.map +1 -0
- package/dist/lib/sast.test.js +33 -0
- package/dist/lib/sast.test.js.map +1 -0
- package/dist/lib/shell.d.ts +61 -0
- package/dist/lib/shell.d.ts.map +1 -0
- package/dist/lib/shell.js +183 -0
- package/dist/lib/shell.js.map +1 -0
- package/dist/lib/ssh-config.d.ts +37 -0
- package/dist/lib/ssh-config.d.ts.map +1 -0
- package/dist/lib/ssh-config.js +122 -0
- package/dist/lib/ssh-config.js.map +1 -0
- package/dist/lib/tenant-scope.d.ts +38 -0
- package/dist/lib/tenant-scope.d.ts.map +1 -0
- package/dist/lib/tenant-scope.js +129 -0
- package/dist/lib/tenant-scope.js.map +1 -0
- package/dist/lib/tenant-scope.test.d.ts +2 -0
- package/dist/lib/tenant-scope.test.d.ts.map +1 -0
- package/dist/lib/tenant-scope.test.js +223 -0
- package/dist/lib/tenant-scope.test.js.map +1 -0
- package/package.json +58 -0
- package/templates/bootstrap/.env.template +54 -0
- package/templates/bootstrap/docker-compose.yml +145 -0
- package/templates/vhost.conf.tmpl +446 -0
|
@@ -0,0 +1,1186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vu addon create <name>
|
|
3
|
+
*
|
|
4
|
+
* ciCore HelloWorld şablonundan doğrulanmış pattern'lerle
|
|
5
|
+
* sıfırdan çalışır addon iskeleti oluşturur.
|
|
6
|
+
*
|
|
7
|
+
* Kullanım:
|
|
8
|
+
* vu addon create HesapMakinasi
|
|
9
|
+
*/
|
|
10
|
+
import { log, spinner } from '../../lib/logger.js';
|
|
11
|
+
import { paths } from '../../lib/config.js';
|
|
12
|
+
import { findShellRoot } from '../../lib/ops/addon-dev.js';
|
|
13
|
+
import { genEslintConfig, genPrettierConfig, genEditorConfig, genTsConfig, genGitlabCi, genAmbientTypes, } from '../../lib/ops/addon-quality.js';
|
|
14
|
+
import fs from 'fs-extra';
|
|
15
|
+
import path from 'path';
|
|
16
|
+
// ─── İsim türetme yardımcıları ────────────────────────────────────────────────
|
|
17
|
+
/** HesapMakinasi → hesap-makinasi */
|
|
18
|
+
function toKebab(name) {
|
|
19
|
+
return name
|
|
20
|
+
.replace(/([A-Z])/g, '-$1')
|
|
21
|
+
.toLowerCase()
|
|
22
|
+
.replace(/^-/, '');
|
|
23
|
+
}
|
|
24
|
+
/** HesapMakinasi → hesapMakinasi */
|
|
25
|
+
function toCamel(name) {
|
|
26
|
+
return name.charAt(0).toLowerCase() + name.slice(1);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* HesapMakinasi → hm (her PascalCase kelimenin ilk harfi, max 4 karakter)
|
|
30
|
+
* HelloWorld → hw | ProductManager → pm | AdminMenuManager → amm
|
|
31
|
+
*/
|
|
32
|
+
function toPrefix(name) {
|
|
33
|
+
const words = name.match(/[A-Z][a-z0-9]*/g) ?? [name];
|
|
34
|
+
return words
|
|
35
|
+
.map(w => w[0].toLowerCase())
|
|
36
|
+
.join('')
|
|
37
|
+
.slice(0, 4);
|
|
38
|
+
}
|
|
39
|
+
/** HesapMakinasi → Hesap Makinasi */
|
|
40
|
+
function toDisplay(name) {
|
|
41
|
+
const words = name.match(/[A-Z][a-z0-9]*/g) ?? [name];
|
|
42
|
+
return words.join(' ');
|
|
43
|
+
}
|
|
44
|
+
// ─── Ana fonksiyon ────────────────────────────────────────────────────────────
|
|
45
|
+
export async function createAddon(name) {
|
|
46
|
+
log.title(`ciCore Addon Oluşturucu: ${name}`);
|
|
47
|
+
// 1. İsim doğrulama (PascalCase zorunlu)
|
|
48
|
+
if (!/^[A-Z][a-zA-Z0-9]+$/.test(name)) {
|
|
49
|
+
log.error('Addon adı PascalCase olmalı. Örnek: HesapMakinasi, NotePad, TaskFlow');
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
// Pick the right addons dir for the layout we're in.
|
|
53
|
+
// - dev-shell (personnel laptop): cores/dev/addons/<Name>
|
|
54
|
+
// - monorepo (Mustafa): addons/<Name> (paths.dev.addons default)
|
|
55
|
+
// Auto-detect dev-shell by walking up from cwd; fall back to the
|
|
56
|
+
// monorepo-style path when we're not inside dev-shell.
|
|
57
|
+
const shellRoot = findShellRoot(process.cwd());
|
|
58
|
+
const addonsRoot = shellRoot ? path.join(shellRoot, 'cores', 'dev', 'addons') : paths.dev.addons;
|
|
59
|
+
const addonPath = path.join(addonsRoot, name);
|
|
60
|
+
// 2. Mevcut addon kontrolü
|
|
61
|
+
if (await fs.pathExists(addonPath)) {
|
|
62
|
+
log.error(`"${name}" addon'u zaten mevcut: ${addonPath}`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
// 3. Değerleri türet
|
|
66
|
+
const slug = toKebab(name); // hesap-makinasi
|
|
67
|
+
const camel = toCamel(name); // hesapMakinasi
|
|
68
|
+
const prefix = toPrefix(name); // hm
|
|
69
|
+
const display = toDisplay(name); // Hesap Makinasi
|
|
70
|
+
log.blank();
|
|
71
|
+
log.kv('Klasör adı', name);
|
|
72
|
+
log.kv('URL slug', slug);
|
|
73
|
+
log.kv('i18n namespace', camel);
|
|
74
|
+
log.kv('Tablo prefix', prefix);
|
|
75
|
+
log.kv('Görünen isim', display);
|
|
76
|
+
log.blank();
|
|
77
|
+
// 4. Dosyaları oluştur
|
|
78
|
+
const s = spinner('Addon iskelet dosyaları oluşturuluyor...').start();
|
|
79
|
+
try {
|
|
80
|
+
// Klasörler
|
|
81
|
+
await fs.ensureDir(path.join(addonPath, 'Backend', 'Controllers'));
|
|
82
|
+
await fs.ensureDir(path.join(addonPath, 'Backend', 'Services'));
|
|
83
|
+
await fs.ensureDir(path.join(addonPath, 'Backend', 'Routes'));
|
|
84
|
+
await fs.ensureDir(path.join(addonPath, 'Backend', 'Migrations'));
|
|
85
|
+
await fs.ensureDir(path.join(addonPath, 'pages', 'admin'));
|
|
86
|
+
await fs.ensureDir(path.join(addonPath, 'pages', 'web'));
|
|
87
|
+
await fs.ensureDir(path.join(addonPath, 'components'));
|
|
88
|
+
await fs.ensureDir(path.join(addonPath, 'composables'));
|
|
89
|
+
await fs.ensureDir(path.join(addonPath, 'locales'));
|
|
90
|
+
await fs.ensureDir(path.join(addonPath, 'ui'));
|
|
91
|
+
await fs.ensureDir(path.join(addonPath, 'Assets', 'img'));
|
|
92
|
+
// Dosyalar
|
|
93
|
+
await write(addonPath, 'addon.json', genAddonJson(name, slug, camel, prefix, display));
|
|
94
|
+
await write(addonPath, 'package.json', genPackageJson(name));
|
|
95
|
+
await write(addonPath, '.signature', '');
|
|
96
|
+
await write(addonPath, 'empty-devtools.js', genEmptyDevtools());
|
|
97
|
+
await write(addonPath, 'vite.config.js', genViteConfig(name));
|
|
98
|
+
await write(addonPath, 'Backend/Routes/routes.php', genRoutes(name, slug));
|
|
99
|
+
await write(addonPath, `Backend/Controllers/${name}Controller.php`, genController(name, slug));
|
|
100
|
+
await write(addonPath, `Backend/Services/${name}Service.php`, genService(name, prefix));
|
|
101
|
+
await write(addonPath, `Backend/Migrations/001_Create${prefix.toUpperCase()}Table.php`, genMigration(name, prefix));
|
|
102
|
+
await write(addonPath, 'pages/admin/index.vue', genAdminPage(name, slug, camel));
|
|
103
|
+
await write(addonPath, 'pages/web/index.vue', genWebPage(name, slug, camel));
|
|
104
|
+
await write(addonPath, 'ui/entry.ts', genEntryTs(name));
|
|
105
|
+
await write(addonPath, 'ui/style.css', genStyleCss(name));
|
|
106
|
+
await write(addonPath, 'locales/en.json', genLocaleEn(camel, display));
|
|
107
|
+
await write(addonPath, 'locales/tr.json', genLocaleTr(camel, display));
|
|
108
|
+
// Faz 5 quality template — every new addon ships with lint + format
|
|
109
|
+
// + typecheck config out of the box. Tooling lives in dev-shell's
|
|
110
|
+
// top-level node_modules; the configs here just point the addon's
|
|
111
|
+
// file types at the shared toolchain.
|
|
112
|
+
await write(addonPath, '.eslintrc.json', genEslintConfig());
|
|
113
|
+
await write(addonPath, '.prettierrc.json', genPrettierConfig());
|
|
114
|
+
await write(addonPath, '.editorconfig', genEditorConfig());
|
|
115
|
+
await write(addonPath, 'tsconfig.json', genTsConfig());
|
|
116
|
+
await write(addonPath, '.gitlab-ci.yml', genGitlabCi(name));
|
|
117
|
+
// Ambient type declarations so vue-tsc doesn't choke on the
|
|
118
|
+
// platform-provided globals (`$fetch` from Nuxt, `@cicore/shared`
|
|
119
|
+
// imports resolved at runtime via the import map).
|
|
120
|
+
await write(addonPath, 'env.d.ts', genAmbientTypes());
|
|
121
|
+
await write(addonPath, '.gitignore', genGitignore());
|
|
122
|
+
s.succeed('Tüm dosyalar oluşturuldu');
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
s.fail('Dosya oluşturma hatası');
|
|
126
|
+
log.error(err.message);
|
|
127
|
+
await fs.remove(addonPath).catch(() => { });
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
// 5. Özet
|
|
131
|
+
log.blank();
|
|
132
|
+
log.success(`"${name}" addon'u başarıyla oluşturuldu!`);
|
|
133
|
+
log.blank();
|
|
134
|
+
log.info('Oluşturulan dosyalar:');
|
|
135
|
+
log.item(`addons/${name}/addon.json`);
|
|
136
|
+
log.item(`addons/${name}/Backend/Routes/routes.php`);
|
|
137
|
+
log.item(`addons/${name}/Backend/Controllers/${name}Controller.php`);
|
|
138
|
+
log.item(`addons/${name}/Backend/Services/${name}Service.php`);
|
|
139
|
+
log.item(`addons/${name}/Backend/Migrations/001_Create${prefix.toUpperCase()}Table.php`);
|
|
140
|
+
log.item(`addons/${name}/pages/admin/index.vue`);
|
|
141
|
+
log.item(`addons/${name}/pages/web/index.vue`);
|
|
142
|
+
log.item(`addons/${name}/ui/entry.ts`);
|
|
143
|
+
log.item(`addons/${name}/locales/en.json + tr.json`);
|
|
144
|
+
log.blank();
|
|
145
|
+
log.info('Sonraki adımlar:');
|
|
146
|
+
log.item(`1. addons/${name}/addon.json → icon ve menü sırasını düzenle`);
|
|
147
|
+
log.item(`2. Backend/Services/${name}Service.php → iş mantığını yaz`);
|
|
148
|
+
log.item(`3. pages/admin/index.vue → UI'ı geliştir`);
|
|
149
|
+
log.item(`4. ci addon dev ${name} → vite HMR session (Faz 2A)`);
|
|
150
|
+
log.item(`5. http://localhost:3080/admin/${slug} adresini aç`);
|
|
151
|
+
log.blank();
|
|
152
|
+
log.info('Quality stack (kendi devDeps — npm ci, sonra):');
|
|
153
|
+
log.item(`Lint: npm run lint`);
|
|
154
|
+
log.item(`Typecheck: npm run typecheck`);
|
|
155
|
+
log.item(`Build: npm run build`);
|
|
156
|
+
log.item(`(\`ci addon deploy --core <X>\` runs lint + typecheck before upload)`);
|
|
157
|
+
log.blank();
|
|
158
|
+
log.info('Git başlatmak için (S4-T23):');
|
|
159
|
+
log.item(`cd ${addonPath} && git init && git add . && git commit -m "init: ${name} addon scaffold"`);
|
|
160
|
+
}
|
|
161
|
+
// ─── Yardımcı: dosya yaz ──────────────────────────────────────────────────────
|
|
162
|
+
async function write(base, rel, content) {
|
|
163
|
+
const fullPath = path.join(base, rel);
|
|
164
|
+
await fs.ensureDir(path.dirname(fullPath));
|
|
165
|
+
if (typeof content === 'object') {
|
|
166
|
+
await fs.writeJson(fullPath, content, { spaces: 2 });
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
await fs.writeFile(fullPath, content, 'utf8');
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// ─── Şablon üreticiler ────────────────────────────────────────────────────────
|
|
173
|
+
function genAddonJson(name, slug, camel, prefix, display) {
|
|
174
|
+
// `name` MUST be a string matching the addon directory — `ci addon deploy`
|
|
175
|
+
// and the install-addon.sh receiver both validate the directory ↔ name
|
|
176
|
+
// pair against /^[A-Za-z0-9_-]+$/. The i18n display name lives in
|
|
177
|
+
// `display_name` and the menu title, so multilingual surfaces still
|
|
178
|
+
// render in Turkish/English without breaking the deploy pipeline.
|
|
179
|
+
return {
|
|
180
|
+
name,
|
|
181
|
+
display_name: { en: display, tr: display },
|
|
182
|
+
version: '1.0.0',
|
|
183
|
+
// Plain-string description matches the install-addon.sh receiver's
|
|
184
|
+
// schema (it grep's a single-line summary for the audit log).
|
|
185
|
+
// Multilingual descriptions live in `display_name` and any per-page
|
|
186
|
+
// i18n the addon ships in locales/*.json.
|
|
187
|
+
description: `${display} addon for ciCore`,
|
|
188
|
+
author: 'ciCore Team',
|
|
189
|
+
enabled: true,
|
|
190
|
+
admin: {
|
|
191
|
+
enabled: true,
|
|
192
|
+
locales: true,
|
|
193
|
+
route_prefix: `/admin/${slug}`,
|
|
194
|
+
menu: {
|
|
195
|
+
title: display,
|
|
196
|
+
title_i18n: { en: display, tr: display },
|
|
197
|
+
icon: 'i-lucide-puzzle',
|
|
198
|
+
order: 50,
|
|
199
|
+
to: `/admin/${slug}`,
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
web: {
|
|
203
|
+
enabled: true,
|
|
204
|
+
locales: true,
|
|
205
|
+
route_prefix: `/dashboard/${slug}`,
|
|
206
|
+
menu: {
|
|
207
|
+
title: display,
|
|
208
|
+
title_i18n: { en: display, tr: display },
|
|
209
|
+
icon: 'i-lucide-puzzle',
|
|
210
|
+
order: 50,
|
|
211
|
+
to: `/dashboard/${slug}`,
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
backend: {
|
|
215
|
+
enabled: true,
|
|
216
|
+
route_prefix: `/api/${slug}`,
|
|
217
|
+
namespace: `VuCore\\Addons\\${name}`,
|
|
218
|
+
routes_file: 'Backend/Routes/routes.php',
|
|
219
|
+
migrations_dir: 'Backend/Migrations',
|
|
220
|
+
table_prefix: prefix,
|
|
221
|
+
},
|
|
222
|
+
i18n: { namespace: camel },
|
|
223
|
+
is_system: false,
|
|
224
|
+
licensing: { model: 'free' },
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
228
|
+
function genPackageJson(name) {
|
|
229
|
+
return JSON.stringify({
|
|
230
|
+
name: `@cicore-addon/${name.toLowerCase()}`,
|
|
231
|
+
version: '1.0.0',
|
|
232
|
+
private: true,
|
|
233
|
+
type: 'module',
|
|
234
|
+
scripts: {
|
|
235
|
+
build: 'vite build',
|
|
236
|
+
lint: 'eslint . --max-warnings 0',
|
|
237
|
+
typecheck: 'vue-tsc --noEmit',
|
|
238
|
+
},
|
|
239
|
+
devDependencies: {
|
|
240
|
+
vite: '^7.1.9',
|
|
241
|
+
'@vitejs/plugin-vue': '^6.0.1',
|
|
242
|
+
vue: '^3.5.22',
|
|
243
|
+
'vue-tsc': '^2.1.10',
|
|
244
|
+
typescript: '^5.7.2',
|
|
245
|
+
'@typescript-eslint/eslint-plugin': '^7.18.0',
|
|
246
|
+
'@typescript-eslint/parser': '^7.18.0',
|
|
247
|
+
eslint: '^8.57.1',
|
|
248
|
+
'eslint-plugin-vue': '^9.31.0',
|
|
249
|
+
'vue-eslint-parser': '^9.4.3',
|
|
250
|
+
},
|
|
251
|
+
}, null, 2) + '\n';
|
|
252
|
+
}
|
|
253
|
+
function genGitignore() {
|
|
254
|
+
return `node_modules/
|
|
255
|
+
dist/
|
|
256
|
+
*.local
|
|
257
|
+
.DS_Store
|
|
258
|
+
*.log
|
|
259
|
+
`;
|
|
260
|
+
}
|
|
261
|
+
function genEmptyDevtools() {
|
|
262
|
+
return `// Stub for @vue/devtools-api — not needed in production ESM bundles
|
|
263
|
+
export const setupDevtoolsPlugin = () => {};
|
|
264
|
+
export default { setupDevtoolsPlugin: () => {} };
|
|
265
|
+
`;
|
|
266
|
+
}
|
|
267
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
268
|
+
function genRoutes(name, slug) {
|
|
269
|
+
// PHP değişkenlerini ($app, $group vs.) template literal interpolasyon olarak
|
|
270
|
+
// algılamaması için satırları birleştirerek string oluşturuyoruz.
|
|
271
|
+
const lines = [
|
|
272
|
+
'<?php',
|
|
273
|
+
`/**`,
|
|
274
|
+
` * ${name} Routes`,
|
|
275
|
+
` *`,
|
|
276
|
+
` * KRİTİK: addon.json backend.route_prefix otomatik uygulanmaz!`,
|
|
277
|
+
` * AddonLoader: $routeHandler($app) — prefix eklenmez, tam path yaz.`,
|
|
278
|
+
` */`,
|
|
279
|
+
``,
|
|
280
|
+
`use Slim\\App;`,
|
|
281
|
+
`use Slim\\Routing\\RouteCollectorProxy;`,
|
|
282
|
+
`use VuCore\\Addons\\${name}\\Controllers\\${name}Controller;`,
|
|
283
|
+
`use VuCore\\Core\\Middleware\\DualAuthMiddleware;`,
|
|
284
|
+
``,
|
|
285
|
+
`return function (App $app) {`,
|
|
286
|
+
` $controller = new ${name}Controller();`,
|
|
287
|
+
` $auth = new DualAuthMiddleware('${name}');`,
|
|
288
|
+
``,
|
|
289
|
+
` // Admin API — JWT veya API Key ile korumalı`,
|
|
290
|
+
` $app->group('/api/${slug}', function (RouteCollectorProxy $group) use ($controller) {`,
|
|
291
|
+
` $group->get('/ping', [$controller, 'ping']);`,
|
|
292
|
+
` $group->get('/items', [$controller, 'index']);`,
|
|
293
|
+
` $group->get('/items/{id}', [$controller, 'show']);`,
|
|
294
|
+
` $group->post('/items', [$controller, 'store']);`,
|
|
295
|
+
` $group->patch('/items/{id}', [$controller, 'update']);`,
|
|
296
|
+
` $group->delete('/items/{id}',[$controller, 'destroy']);`,
|
|
297
|
+
` })->add($auth);`,
|
|
298
|
+
``,
|
|
299
|
+
` // Public Web API — auth gerektirmez`,
|
|
300
|
+
` $app->group('/api/${slug}/web', function (RouteCollectorProxy $group) use ($controller) {`,
|
|
301
|
+
` $group->get('/message', [$controller, 'publicMessage'])`,
|
|
302
|
+
` ->setArgument('__public', 'true');`,
|
|
303
|
+
` });`,
|
|
304
|
+
`};`,
|
|
305
|
+
``,
|
|
306
|
+
];
|
|
307
|
+
return lines.join('\n');
|
|
308
|
+
}
|
|
309
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
310
|
+
function genController(name, slug) {
|
|
311
|
+
const lines = [
|
|
312
|
+
`<?php`,
|
|
313
|
+
`declare(strict_types=1);`,
|
|
314
|
+
``,
|
|
315
|
+
`namespace VuCore\\Addons\\${name}\\Controllers;`,
|
|
316
|
+
``,
|
|
317
|
+
`use Psr\\Http\\Message\\ResponseInterface as Response;`,
|
|
318
|
+
`use Psr\\Http\\Message\\ServerRequestInterface as Request;`,
|
|
319
|
+
`use VuCore\\Core\\Controllers\\BaseController;`,
|
|
320
|
+
`use VuCore\\Addons\\${name}\\Services\\${name}Service;`,
|
|
321
|
+
``,
|
|
322
|
+
`class ${name}Controller extends BaseController`,
|
|
323
|
+
`{`,
|
|
324
|
+
` private ?${name}Service $service;`,
|
|
325
|
+
``,
|
|
326
|
+
` public function __construct()`,
|
|
327
|
+
` {`,
|
|
328
|
+
` try {`,
|
|
329
|
+
` $this->service = new ${name}Service();`,
|
|
330
|
+
` } catch (\\Exception $e) {`,
|
|
331
|
+
` error_log("[${name}] Controller init failed: " . $e->getMessage());`,
|
|
332
|
+
` $this->service = null;`,
|
|
333
|
+
` }`,
|
|
334
|
+
` }`,
|
|
335
|
+
``,
|
|
336
|
+
` private function unavailable(Response $response): Response`,
|
|
337
|
+
` {`,
|
|
338
|
+
` return $this->json($response, ['success' => false, 'message' => 'Service unavailable'], 503);`,
|
|
339
|
+
` }`,
|
|
340
|
+
``,
|
|
341
|
+
` /** GET /${slug}/ping */`,
|
|
342
|
+
` public function ping(Request $request, Response $response): Response`,
|
|
343
|
+
` {`,
|
|
344
|
+
` return $this->json($response, [`,
|
|
345
|
+
` 'success' => true,`,
|
|
346
|
+
` 'message' => 'pong',`,
|
|
347
|
+
` 'addon' => '${name}',`,
|
|
348
|
+
` 'timestamp' => date('c'),`,
|
|
349
|
+
` ]);`,
|
|
350
|
+
` }`,
|
|
351
|
+
``,
|
|
352
|
+
` /** GET /${slug}/items */`,
|
|
353
|
+
` public function index(Request $request, Response $response): Response`,
|
|
354
|
+
` {`,
|
|
355
|
+
` if (!$this->service) return $this->unavailable($response);`,
|
|
356
|
+
` try {`,
|
|
357
|
+
` $result = $this->service->getAll($request->getQueryParams());`,
|
|
358
|
+
` return $this->json($response, ['success' => true, 'data' => $result]);`,
|
|
359
|
+
` } catch (\\Exception $e) {`,
|
|
360
|
+
` return $this->json($response, ['success' => false, 'message' => $e->getMessage()], 500);`,
|
|
361
|
+
` }`,
|
|
362
|
+
` }`,
|
|
363
|
+
``,
|
|
364
|
+
` /** GET /${slug}/items/{id} */`,
|
|
365
|
+
` public function show(Request $request, Response $response, array $args): Response`,
|
|
366
|
+
` {`,
|
|
367
|
+
` if (!$this->service) return $this->unavailable($response);`,
|
|
368
|
+
` $item = $this->service->getById($args['id']);`,
|
|
369
|
+
` if (!$item) return $this->json($response, ['success' => false, 'message' => 'Not found'], 404);`,
|
|
370
|
+
` return $this->json($response, ['success' => true, 'data' => $item]);`,
|
|
371
|
+
` }`,
|
|
372
|
+
``,
|
|
373
|
+
` /** POST /${slug}/items */`,
|
|
374
|
+
` public function store(Request $request, Response $response): Response`,
|
|
375
|
+
` {`,
|
|
376
|
+
` if (!$this->service) return $this->unavailable($response);`,
|
|
377
|
+
` try {`,
|
|
378
|
+
` $item = $this->service->create($request->getParsedBody() ?? []);`,
|
|
379
|
+
` return $this->json($response, ['success' => true, 'data' => $item], 201);`,
|
|
380
|
+
` } catch (\\Exception $e) {`,
|
|
381
|
+
` return $this->json($response, ['success' => false, 'message' => $e->getMessage()], 400);`,
|
|
382
|
+
` }`,
|
|
383
|
+
` }`,
|
|
384
|
+
``,
|
|
385
|
+
` /** PATCH /${slug}/items/{id} */`,
|
|
386
|
+
` public function update(Request $request, Response $response, array $args): Response`,
|
|
387
|
+
` {`,
|
|
388
|
+
` if (!$this->service) return $this->unavailable($response);`,
|
|
389
|
+
` try {`,
|
|
390
|
+
` $item = $this->service->update($args['id'], $request->getParsedBody() ?? []);`,
|
|
391
|
+
` if (!$item) return $this->json($response, ['success' => false, 'message' => 'Not found'], 404);`,
|
|
392
|
+
` return $this->json($response, ['success' => true, 'data' => $item]);`,
|
|
393
|
+
` } catch (\\Exception $e) {`,
|
|
394
|
+
` return $this->json($response, ['success' => false, 'message' => $e->getMessage()], 400);`,
|
|
395
|
+
` }`,
|
|
396
|
+
` }`,
|
|
397
|
+
``,
|
|
398
|
+
` /** DELETE /${slug}/items/{id} */`,
|
|
399
|
+
` public function destroy(Request $request, Response $response, array $args): Response`,
|
|
400
|
+
` {`,
|
|
401
|
+
` if (!$this->service) return $this->unavailable($response);`,
|
|
402
|
+
` $this->service->delete($args['id']);`,
|
|
403
|
+
` return $this->json($response, ['success' => true]);`,
|
|
404
|
+
` }`,
|
|
405
|
+
``,
|
|
406
|
+
` /** GET /${slug}/web/message — public */`,
|
|
407
|
+
` public function publicMessage(Request $request, Response $response): Response`,
|
|
408
|
+
` {`,
|
|
409
|
+
` return $this->json($response, ['success' => true, 'message' => 'Hello from ${name}!']);`,
|
|
410
|
+
` }`,
|
|
411
|
+
`}`,
|
|
412
|
+
``,
|
|
413
|
+
];
|
|
414
|
+
return lines.join('\n');
|
|
415
|
+
}
|
|
416
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
417
|
+
function genService(name, prefix) {
|
|
418
|
+
const tbl = `${prefix}_items`;
|
|
419
|
+
const lines = [
|
|
420
|
+
`<?php`,
|
|
421
|
+
`declare(strict_types=1);`,
|
|
422
|
+
``,
|
|
423
|
+
`namespace VuCore\\Addons\\${name}\\Services;`,
|
|
424
|
+
``,
|
|
425
|
+
`use VuCore\\Core\\Services\\DatabaseService;`,
|
|
426
|
+
`use PDO;`,
|
|
427
|
+
``,
|
|
428
|
+
`class ${name}Service`,
|
|
429
|
+
`{`,
|
|
430
|
+
` private PDO $db;`,
|
|
431
|
+
` private string $table = '${tbl}';`,
|
|
432
|
+
``,
|
|
433
|
+
` public function __construct()`,
|
|
434
|
+
` {`,
|
|
435
|
+
` $this->db = DatabaseService::getConnection();`,
|
|
436
|
+
` }`,
|
|
437
|
+
``,
|
|
438
|
+
` public function getAll(array $params = []): array`,
|
|
439
|
+
` {`,
|
|
440
|
+
` $page = (int)($params['page'] ?? 1);`,
|
|
441
|
+
` $perPage = (int)($params['per_page'] ?? 20);`,
|
|
442
|
+
` $offset = ($page - 1) * $perPage;`,
|
|
443
|
+
``,
|
|
444
|
+
` $stmt = $this->db->prepare(`,
|
|
445
|
+
` "SELECT * FROM {$this->table} ORDER BY created_at DESC LIMIT :limit OFFSET :offset"`,
|
|
446
|
+
` );`,
|
|
447
|
+
` $stmt->bindValue(':limit', $perPage, PDO::PARAM_INT);`,
|
|
448
|
+
` $stmt->bindValue(':offset', $offset, PDO::PARAM_INT);`,
|
|
449
|
+
` $stmt->execute();`,
|
|
450
|
+
` $items = $stmt->fetchAll(PDO::FETCH_ASSOC);`,
|
|
451
|
+
``,
|
|
452
|
+
` $total = (int)$this->db->query("SELECT COUNT(*) FROM {$this->table}")->fetchColumn();`,
|
|
453
|
+
``,
|
|
454
|
+
` return [`,
|
|
455
|
+
` 'items' => $items,`,
|
|
456
|
+
` 'pagination' => [`,
|
|
457
|
+
` 'total' => $total,`,
|
|
458
|
+
` 'per_page' => $perPage,`,
|
|
459
|
+
` 'current_page' => $page,`,
|
|
460
|
+
` 'last_page' => (int)ceil($total / $perPage),`,
|
|
461
|
+
` ],`,
|
|
462
|
+
` ];`,
|
|
463
|
+
` }`,
|
|
464
|
+
``,
|
|
465
|
+
` public function getById(string $id): ?array`,
|
|
466
|
+
` {`,
|
|
467
|
+
` $stmt = $this->db->prepare("SELECT * FROM {$this->table} WHERE id = :id");`,
|
|
468
|
+
` $stmt->execute(['id' => $id]);`,
|
|
469
|
+
` return $stmt->fetch(PDO::FETCH_ASSOC) ?: null;`,
|
|
470
|
+
` }`,
|
|
471
|
+
``,
|
|
472
|
+
` public function create(array $data): array`,
|
|
473
|
+
` {`,
|
|
474
|
+
` if (empty($data['name'])) throw new \\InvalidArgumentException('name is required');`,
|
|
475
|
+
``,
|
|
476
|
+
` $stmt = $this->db->prepare("`,
|
|
477
|
+
` INSERT INTO {$this->table} (id, name, description, is_active, created_at, updated_at)`,
|
|
478
|
+
` VALUES (gen_random_uuid(), :name, :description, true, NOW(), NOW())`,
|
|
479
|
+
` RETURNING *`,
|
|
480
|
+
` ");`,
|
|
481
|
+
` $stmt->execute(['name' => $data['name'], 'description' => $data['description'] ?? null]);`,
|
|
482
|
+
` return $stmt->fetch(PDO::FETCH_ASSOC);`,
|
|
483
|
+
` }`,
|
|
484
|
+
``,
|
|
485
|
+
` public function update(string $id, array $data): ?array`,
|
|
486
|
+
` {`,
|
|
487
|
+
` $existing = $this->getById($id);`,
|
|
488
|
+
` if (!$existing) return null;`,
|
|
489
|
+
``,
|
|
490
|
+
` $stmt = $this->db->prepare("`,
|
|
491
|
+
` UPDATE {$this->table}`,
|
|
492
|
+
` SET name = :name, description = :description, updated_at = NOW()`,
|
|
493
|
+
` WHERE id = :id RETURNING *`,
|
|
494
|
+
` ");`,
|
|
495
|
+
` $stmt->execute([`,
|
|
496
|
+
` 'id' => $id,`,
|
|
497
|
+
` 'name' => $data['name'] ?? $existing['name'],`,
|
|
498
|
+
` 'description' => $data['description'] ?? $existing['description'],`,
|
|
499
|
+
` ]);`,
|
|
500
|
+
` return $stmt->fetch(PDO::FETCH_ASSOC);`,
|
|
501
|
+
` }`,
|
|
502
|
+
``,
|
|
503
|
+
` public function delete(string $id): void`,
|
|
504
|
+
` {`,
|
|
505
|
+
` $this->db->prepare("DELETE FROM {$this->table} WHERE id = :id")->execute(['id' => $id]);`,
|
|
506
|
+
` }`,
|
|
507
|
+
`}`,
|
|
508
|
+
``,
|
|
509
|
+
];
|
|
510
|
+
return lines.join('\n');
|
|
511
|
+
}
|
|
512
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
513
|
+
function genMigration(name, prefix) {
|
|
514
|
+
const UP = prefix.toUpperCase();
|
|
515
|
+
const cls = `Migration_001_Create${UP}Table`;
|
|
516
|
+
const tbl = `${prefix}_items`;
|
|
517
|
+
const lines = [
|
|
518
|
+
`<?php`,
|
|
519
|
+
`declare(strict_types=1);`,
|
|
520
|
+
``,
|
|
521
|
+
`namespace VuCore\\Addons\\${name}\\Migrations;`,
|
|
522
|
+
``,
|
|
523
|
+
`use VuCore\\Core\\Services\\Migration;`,
|
|
524
|
+
`use PDO;`,
|
|
525
|
+
``,
|
|
526
|
+
`class ${cls} implements Migration`,
|
|
527
|
+
`{`,
|
|
528
|
+
` private string $prefix = '${prefix}';`,
|
|
529
|
+
``,
|
|
530
|
+
` public function name(): string { return '001_Create${UP}Table'; }`,
|
|
531
|
+
``,
|
|
532
|
+
` public function up(PDO $db): void`,
|
|
533
|
+
` {`,
|
|
534
|
+
` $exists = $db->query("`,
|
|
535
|
+
` SELECT EXISTS (`,
|
|
536
|
+
` SELECT FROM information_schema.tables WHERE table_name = '${tbl}'`,
|
|
537
|
+
` )`,
|
|
538
|
+
` ")->fetchColumn();`,
|
|
539
|
+
` if ($exists) return;`,
|
|
540
|
+
``,
|
|
541
|
+
` $db->exec("`,
|
|
542
|
+
` CREATE TABLE ${tbl} (`,
|
|
543
|
+
` id UUID PRIMARY KEY DEFAULT gen_random_uuid(),`,
|
|
544
|
+
` name VARCHAR(255) NOT NULL,`,
|
|
545
|
+
` description TEXT,`,
|
|
546
|
+
` is_active BOOLEAN DEFAULT TRUE,`,
|
|
547
|
+
` metadata JSONB DEFAULT '{}',`,
|
|
548
|
+
` created_at TIMESTAMPTZ DEFAULT NOW(),`,
|
|
549
|
+
` updated_at TIMESTAMPTZ DEFAULT NOW()`,
|
|
550
|
+
` )`,
|
|
551
|
+
` ");`,
|
|
552
|
+
` $db->exec("CREATE INDEX idx_${prefix}_items_name ON ${tbl}(name)");`,
|
|
553
|
+
` $db->exec("CREATE INDEX idx_${prefix}_items_is_active ON ${tbl}(is_active)");`,
|
|
554
|
+
` }`,
|
|
555
|
+
``,
|
|
556
|
+
` public function down(PDO $db): void`,
|
|
557
|
+
` {`,
|
|
558
|
+
` $db->exec("DROP TABLE IF EXISTS ${tbl} CASCADE");`,
|
|
559
|
+
` }`,
|
|
560
|
+
`}`,
|
|
561
|
+
``,
|
|
562
|
+
];
|
|
563
|
+
return lines.join('\n');
|
|
564
|
+
}
|
|
565
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
566
|
+
function genAdminPage(name, slug, camel) {
|
|
567
|
+
return `<template>
|
|
568
|
+
<div class="space-y-6">
|
|
569
|
+
|
|
570
|
+
<div class="flex items-center justify-between">
|
|
571
|
+
<div>
|
|
572
|
+
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">
|
|
573
|
+
{{ t('${camel}.title') }}
|
|
574
|
+
</h1>
|
|
575
|
+
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">
|
|
576
|
+
{{ t('${camel}.description') }}
|
|
577
|
+
</p>
|
|
578
|
+
</div>
|
|
579
|
+
<UButton icon="i-lucide-plus" @click="openCreate">
|
|
580
|
+
{{ t('${camel}.actions.create') }}
|
|
581
|
+
</UButton>
|
|
582
|
+
</div>
|
|
583
|
+
|
|
584
|
+
<UCard>
|
|
585
|
+
<div v-if="loading" class="flex justify-center py-12">
|
|
586
|
+
<Icon name="i-lucide-loader-2" class="size-8 animate-spin text-primary" />
|
|
587
|
+
</div>
|
|
588
|
+
|
|
589
|
+
<div v-else-if="items.length === 0" class="text-center py-12">
|
|
590
|
+
<Icon name="i-lucide-inbox" class="size-12 text-gray-300 dark:text-gray-700 mx-auto mb-3" />
|
|
591
|
+
<p class="text-gray-500">{{ t('${camel}.empty') }}</p>
|
|
592
|
+
<UButton class="mt-4" @click="openCreate">
|
|
593
|
+
{{ t('${camel}.actions.create') }}
|
|
594
|
+
</UButton>
|
|
595
|
+
</div>
|
|
596
|
+
|
|
597
|
+
<div v-else class="divide-y divide-gray-100 dark:divide-gray-800">
|
|
598
|
+
<div
|
|
599
|
+
v-for="item in items"
|
|
600
|
+
:key="item.id"
|
|
601
|
+
class="flex items-center justify-between py-3"
|
|
602
|
+
>
|
|
603
|
+
<div>
|
|
604
|
+
<p class="font-medium text-gray-900 dark:text-white">{{ item.name }}</p>
|
|
605
|
+
<p class="text-xs text-gray-400 mt-0.5">{{ formatDate(item.created_at) }}</p>
|
|
606
|
+
</div>
|
|
607
|
+
<div class="flex gap-1">
|
|
608
|
+
<UButton size="xs" variant="ghost" color="neutral" icon="i-lucide-pencil"
|
|
609
|
+
@click="openEdit(item)" />
|
|
610
|
+
<UButton size="xs" variant="ghost" color="error" icon="i-lucide-trash-2"
|
|
611
|
+
@click="removeItem(item.id)" />
|
|
612
|
+
</div>
|
|
613
|
+
</div>
|
|
614
|
+
</div>
|
|
615
|
+
</UCard>
|
|
616
|
+
|
|
617
|
+
<!-- Oluştur / Düzenle Modal -->
|
|
618
|
+
<ClientOnly>
|
|
619
|
+
<UModal v-model:open="showModal">
|
|
620
|
+
<template #header>
|
|
621
|
+
<h3 class="text-lg font-semibold">
|
|
622
|
+
{{ editing ? t('${camel}.actions.edit') : t('${camel}.actions.create') }}
|
|
623
|
+
</h3>
|
|
624
|
+
</template>
|
|
625
|
+
<template #body>
|
|
626
|
+
<div class="space-y-4">
|
|
627
|
+
<UFormField :label="t('${camel}.form.name')" required>
|
|
628
|
+
<UInput v-model="form.name" autofocus />
|
|
629
|
+
</UFormField>
|
|
630
|
+
<UFormField :label="t('${camel}.form.description')">
|
|
631
|
+
<UTextarea v-model="form.description" :rows="3" />
|
|
632
|
+
</UFormField>
|
|
633
|
+
</div>
|
|
634
|
+
</template>
|
|
635
|
+
<template #footer>
|
|
636
|
+
<div class="flex justify-end gap-2">
|
|
637
|
+
<UButton variant="ghost" color="neutral" @click="showModal = false">
|
|
638
|
+
{{ t('${camel}.actions.cancel') }}
|
|
639
|
+
</UButton>
|
|
640
|
+
<UButton :loading="saving" @click="saveItem">
|
|
641
|
+
{{ t('${camel}.actions.save') }}
|
|
642
|
+
</UButton>
|
|
643
|
+
</div>
|
|
644
|
+
</template>
|
|
645
|
+
</UModal>
|
|
646
|
+
</ClientOnly>
|
|
647
|
+
|
|
648
|
+
</div>
|
|
649
|
+
</template>
|
|
650
|
+
|
|
651
|
+
<script setup lang="ts">
|
|
652
|
+
import { ref, onMounted } from 'vue'
|
|
653
|
+
import { useRuntimeConfig, useToast, useI18n } from '@cicore/shared'
|
|
654
|
+
import enLocale from '../../locales/en.json'
|
|
655
|
+
import trLocale from '../../locales/tr.json'
|
|
656
|
+
|
|
657
|
+
// KRİTİK: useApi() değil — $fetch + config.public.apiBase kullan
|
|
658
|
+
const config = useRuntimeConfig()
|
|
659
|
+
const toast = useToast()
|
|
660
|
+
const { locale } = useI18n()
|
|
661
|
+
|
|
662
|
+
// Addon i18n — locale dosyalarını doğrudan import et
|
|
663
|
+
const t = (key: string): string => {
|
|
664
|
+
const map: Record<string, any> = { en: enLocale, tr: trLocale }
|
|
665
|
+
let val: any = map[locale.value] ?? map['en']
|
|
666
|
+
for (const k of key.split('.')) val = val?.[k]
|
|
667
|
+
return typeof val === 'string' ? val : key
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
const items = ref<any[]>([])
|
|
671
|
+
const loading = ref(false)
|
|
672
|
+
const saving = ref(false)
|
|
673
|
+
const showModal = ref(false)
|
|
674
|
+
const editing = ref<any>(null)
|
|
675
|
+
const form = ref({ name: '', description: '' })
|
|
676
|
+
|
|
677
|
+
async function loadItems() {
|
|
678
|
+
loading.value = true
|
|
679
|
+
try {
|
|
680
|
+
const res = await $fetch<any>(\`\${config.public.apiBase}/${slug}/items\`)
|
|
681
|
+
if (res.success) items.value = res.data?.items ?? res.data ?? []
|
|
682
|
+
} catch (e: any) {
|
|
683
|
+
toast.add({ title: t('${camel}.messages.error'), color: 'error' })
|
|
684
|
+
} finally {
|
|
685
|
+
loading.value = false
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
function openCreate() {
|
|
690
|
+
editing.value = null
|
|
691
|
+
form.value = { name: '', description: '' }
|
|
692
|
+
showModal.value = true
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
function openEdit(item: any) {
|
|
696
|
+
editing.value = item
|
|
697
|
+
form.value = { name: item.name, description: item.description ?? '' }
|
|
698
|
+
showModal.value = true
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
async function saveItem() {
|
|
702
|
+
if (!form.value.name.trim()) return
|
|
703
|
+
saving.value = true
|
|
704
|
+
try {
|
|
705
|
+
if (editing.value) {
|
|
706
|
+
await $fetch(\`\${config.public.apiBase}/${slug}/items/\${editing.value.id}\`, {
|
|
707
|
+
method: 'PATCH', body: form.value,
|
|
708
|
+
})
|
|
709
|
+
toast.add({ title: t('${camel}.messages.updated'), color: 'success' })
|
|
710
|
+
} else {
|
|
711
|
+
await $fetch(\`\${config.public.apiBase}/${slug}/items\`, {
|
|
712
|
+
method: 'POST', body: form.value,
|
|
713
|
+
})
|
|
714
|
+
toast.add({ title: t('${camel}.messages.created'), color: 'success' })
|
|
715
|
+
}
|
|
716
|
+
showModal.value = false
|
|
717
|
+
await loadItems()
|
|
718
|
+
} catch (e: any) {
|
|
719
|
+
toast.add({ title: t('${camel}.messages.error'), color: 'error' })
|
|
720
|
+
} finally {
|
|
721
|
+
saving.value = false
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
async function removeItem(id: string) {
|
|
726
|
+
try {
|
|
727
|
+
await $fetch(\`\${config.public.apiBase}/${slug}/items/\${id}\`, { method: 'DELETE' })
|
|
728
|
+
toast.add({ title: t('${camel}.messages.deleted'), color: 'success' })
|
|
729
|
+
await loadItems()
|
|
730
|
+
} catch {
|
|
731
|
+
toast.add({ title: t('${camel}.messages.error'), color: 'error' })
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
function formatDate(d: string): string {
|
|
736
|
+
return new Date(d).toLocaleDateString(locale.value === 'tr' ? 'tr-TR' : 'en-US', {
|
|
737
|
+
year: 'numeric', month: 'short', day: 'numeric',
|
|
738
|
+
})
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
onMounted(() => loadItems())
|
|
742
|
+
</script>
|
|
743
|
+
`;
|
|
744
|
+
}
|
|
745
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
746
|
+
function genWebPage(name, slug, camel) {
|
|
747
|
+
return `<template>
|
|
748
|
+
<div class="space-y-6">
|
|
749
|
+
<div>
|
|
750
|
+
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">
|
|
751
|
+
{{ t('${camel}.title') }}
|
|
752
|
+
</h1>
|
|
753
|
+
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">
|
|
754
|
+
{{ t('${camel}.description') }}
|
|
755
|
+
</p>
|
|
756
|
+
</div>
|
|
757
|
+
|
|
758
|
+
<UCard>
|
|
759
|
+
<div class="text-center py-8">
|
|
760
|
+
<Icon name="i-lucide-puzzle" class="size-14 text-primary mx-auto mb-4" />
|
|
761
|
+
<h2 class="text-lg font-semibold mb-2">{{ t('${camel}.web.greeting') }}</h2>
|
|
762
|
+
<p class="text-gray-500 mb-6">{{ t('${camel}.web.subtitle') }}</p>
|
|
763
|
+
|
|
764
|
+
<UButton :loading="loading" icon="i-lucide-send" @click="fetchPublic">
|
|
765
|
+
{{ t('${camel}.web.fetch_button') }}
|
|
766
|
+
</UButton>
|
|
767
|
+
|
|
768
|
+
<div v-if="message"
|
|
769
|
+
class="mt-4 p-3 rounded-lg bg-green-50 dark:bg-green-900/20
|
|
770
|
+
text-green-700 dark:text-green-400 text-sm font-medium">
|
|
771
|
+
{{ message }}
|
|
772
|
+
</div>
|
|
773
|
+
</div>
|
|
774
|
+
</UCard>
|
|
775
|
+
</div>
|
|
776
|
+
</template>
|
|
777
|
+
|
|
778
|
+
<script setup lang="ts">
|
|
779
|
+
import { ref } from 'vue'
|
|
780
|
+
import { useRuntimeConfig, useI18n } from '@cicore/shared'
|
|
781
|
+
import enLocale from '../../locales/en.json'
|
|
782
|
+
import trLocale from '../../locales/tr.json'
|
|
783
|
+
|
|
784
|
+
const config = useRuntimeConfig()
|
|
785
|
+
const { locale } = useI18n()
|
|
786
|
+
|
|
787
|
+
const t = (key: string): string => {
|
|
788
|
+
const map: Record<string, any> = { en: enLocale, tr: trLocale }
|
|
789
|
+
let val: any = map[locale.value] ?? map['en']
|
|
790
|
+
for (const k of key.split('.')) val = val?.[k]
|
|
791
|
+
return typeof val === 'string' ? val : key
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
const loading = ref(false)
|
|
795
|
+
const message = ref('')
|
|
796
|
+
|
|
797
|
+
async function fetchPublic() {
|
|
798
|
+
loading.value = true
|
|
799
|
+
try {
|
|
800
|
+
const res = await $fetch<any>(\`\${config.public.apiBase}/${slug}/web/message\`)
|
|
801
|
+
if (res.success) message.value = res.message
|
|
802
|
+
} catch {
|
|
803
|
+
message.value = 'API bağlantı hatası'
|
|
804
|
+
} finally {
|
|
805
|
+
loading.value = false
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
</script>
|
|
809
|
+
`;
|
|
810
|
+
}
|
|
811
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
812
|
+
function genEntryTs(name) {
|
|
813
|
+
return `// ${name} Addon — ESM Entry Point
|
|
814
|
+
import './style.css'
|
|
815
|
+
|
|
816
|
+
const route_admin_index = () => import('../pages/admin/index.vue')
|
|
817
|
+
const route_web_index = () => import('../pages/web/index.vue')
|
|
818
|
+
|
|
819
|
+
const locale_en = () => import('../locales/en.json')
|
|
820
|
+
const locale_tr = () => import('../locales/tr.json')
|
|
821
|
+
|
|
822
|
+
const entry = {
|
|
823
|
+
routes: {
|
|
824
|
+
'admin/index': route_admin_index,
|
|
825
|
+
'web/index': route_web_index,
|
|
826
|
+
},
|
|
827
|
+
locales: {
|
|
828
|
+
en: locale_en,
|
|
829
|
+
tr: locale_tr,
|
|
830
|
+
},
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
export { entry }
|
|
834
|
+
export default entry
|
|
835
|
+
`;
|
|
836
|
+
}
|
|
837
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
838
|
+
function genStyleCss(name) {
|
|
839
|
+
return `/* ${name} Addon Styles */
|
|
840
|
+
/* Addon'a özel global CSS buraya yazılır */
|
|
841
|
+
/* Tailwind sınıfları Vue dosyalarında direkt kullanılır */
|
|
842
|
+
`;
|
|
843
|
+
}
|
|
844
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
845
|
+
function genViteConfig(name) {
|
|
846
|
+
return `import { defineConfig } from 'vite'
|
|
847
|
+
import vue from '@vitejs/plugin-vue'
|
|
848
|
+
import { resolve } from 'path'
|
|
849
|
+
|
|
850
|
+
export default defineConfig({
|
|
851
|
+
base: './',
|
|
852
|
+
define: {
|
|
853
|
+
process: JSON.stringify({ env: { NODE_ENV: 'production' } }),
|
|
854
|
+
'process.env': JSON.stringify({ NODE_ENV: 'production' }),
|
|
855
|
+
'process.env.NODE_ENV': JSON.stringify('production'),
|
|
856
|
+
global: 'globalThis',
|
|
857
|
+
},
|
|
858
|
+
plugins: [vue({ script: { defineModel: true } })],
|
|
859
|
+
resolve: {
|
|
860
|
+
alias: {
|
|
861
|
+
'@': resolve(__dirname),
|
|
862
|
+
vue: '@cicore/shared',
|
|
863
|
+
'#imports': '@cicore/shared',
|
|
864
|
+
'@vue/devtools-api': resolve(__dirname, 'empty-devtools.js'),
|
|
865
|
+
},
|
|
866
|
+
},
|
|
867
|
+
build: {
|
|
868
|
+
lib: {
|
|
869
|
+
entry: resolve(__dirname, 'ui/entry.ts'),
|
|
870
|
+
formats: ['es'],
|
|
871
|
+
fileName: () => 'entry.js',
|
|
872
|
+
},
|
|
873
|
+
outDir: 'dist',
|
|
874
|
+
emptyOutDir: true,
|
|
875
|
+
minify: 'esbuild',
|
|
876
|
+
sourcemap: false,
|
|
877
|
+
modulePreload: false,
|
|
878
|
+
rollupOptions: {
|
|
879
|
+
preserveEntrySignatures: 'strict',
|
|
880
|
+
external(id, _parentId, isResolved) {
|
|
881
|
+
if (id.includes('entry.ts') || id.includes('entry.js')) return false
|
|
882
|
+
if (id.includes('?vue&type=')) return false
|
|
883
|
+
if (id.startsWith('./') || id.startsWith('../')) return false
|
|
884
|
+
if (isResolved && (id.includes('/addons/') || id.includes('\\\\addons\\\\'))) return false
|
|
885
|
+
if (id.includes('/addons/') || id.includes('\\\\addons\\\\')) return false
|
|
886
|
+
if (id === 'vue' || id.startsWith('vue/')) return true
|
|
887
|
+
if (id === '@vue/devtools-api' || id.startsWith('@vue/devtools-api/')) return false
|
|
888
|
+
if (id.startsWith('@vue/')) return true
|
|
889
|
+
if (id === '@cicore/shared' || id.startsWith('@cicore/')) return true
|
|
890
|
+
if (id === 'pinia' || id.startsWith('pinia/')) return true
|
|
891
|
+
if (!id.startsWith('.') && !id.startsWith('/') && !id.includes(':')) return true
|
|
892
|
+
return false
|
|
893
|
+
},
|
|
894
|
+
output: {
|
|
895
|
+
format: 'es',
|
|
896
|
+
entryFileNames: 'entry.js',
|
|
897
|
+
chunkFileNames: 'chunks/[name]-[hash].js',
|
|
898
|
+
paths: { vue: '@cicore/shared' },
|
|
899
|
+
assetFileNames: (a) =>
|
|
900
|
+
/\\.(png|jpe?g|gif|svg|webp|ico)$/i.test(a.name ?? '')
|
|
901
|
+
? 'assets/img/[name]-[hash][extname]'
|
|
902
|
+
: 'assets/[name]-[hash][extname]',
|
|
903
|
+
exports: 'named',
|
|
904
|
+
manualChunks(id) {
|
|
905
|
+
if (id.includes('/pages/')) {
|
|
906
|
+
const m = id.match(/pages\\/(.+?)\\.vue/)
|
|
907
|
+
if (m) return m[1].replace(/\\//g, '-')
|
|
908
|
+
}
|
|
909
|
+
if (id.includes('/locales/')) {
|
|
910
|
+
const m = id.match(/locales\\/(.+?)\\.json/)
|
|
911
|
+
if (m) return m[1]
|
|
912
|
+
}
|
|
913
|
+
},
|
|
914
|
+
},
|
|
915
|
+
},
|
|
916
|
+
},
|
|
917
|
+
})
|
|
918
|
+
`;
|
|
919
|
+
}
|
|
920
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
921
|
+
function genLocaleEn(camel, display) {
|
|
922
|
+
return JSON.stringify({
|
|
923
|
+
[camel]: {
|
|
924
|
+
title: display,
|
|
925
|
+
description: `${display} addon for ciCore`,
|
|
926
|
+
empty: 'No items yet',
|
|
927
|
+
form: {
|
|
928
|
+
name: 'Name',
|
|
929
|
+
description: 'Description',
|
|
930
|
+
},
|
|
931
|
+
actions: {
|
|
932
|
+
create: 'Create New',
|
|
933
|
+
edit: 'Edit',
|
|
934
|
+
delete: 'Delete',
|
|
935
|
+
save: 'Save',
|
|
936
|
+
cancel: 'Cancel',
|
|
937
|
+
},
|
|
938
|
+
web: {
|
|
939
|
+
greeting: `Hello from ${display}!`,
|
|
940
|
+
subtitle: 'Public web interface',
|
|
941
|
+
fetch_button: 'Fetch Message',
|
|
942
|
+
},
|
|
943
|
+
messages: {
|
|
944
|
+
created: 'Created successfully',
|
|
945
|
+
updated: 'Updated successfully',
|
|
946
|
+
deleted: 'Deleted successfully',
|
|
947
|
+
error: 'An error occurred',
|
|
948
|
+
},
|
|
949
|
+
},
|
|
950
|
+
}, null, 2) + '\n';
|
|
951
|
+
}
|
|
952
|
+
function genLocaleTr(camel, display) {
|
|
953
|
+
return JSON.stringify({
|
|
954
|
+
[camel]: {
|
|
955
|
+
title: display,
|
|
956
|
+
description: `ciCore için${display} addon'u`,
|
|
957
|
+
empty: 'Henüz öğe yok',
|
|
958
|
+
form: {
|
|
959
|
+
name: 'İsim',
|
|
960
|
+
description: 'Açıklama',
|
|
961
|
+
},
|
|
962
|
+
actions: {
|
|
963
|
+
create: 'Yeni Oluştur',
|
|
964
|
+
edit: 'Düzenle',
|
|
965
|
+
delete: 'Sil',
|
|
966
|
+
save: 'Kaydet',
|
|
967
|
+
cancel: 'İptal',
|
|
968
|
+
},
|
|
969
|
+
web: {
|
|
970
|
+
greeting: `${display}'dan merhaba!`,
|
|
971
|
+
subtitle: 'Genel web arayüzü',
|
|
972
|
+
fetch_button: 'Mesajı Getir',
|
|
973
|
+
},
|
|
974
|
+
messages: {
|
|
975
|
+
created: 'Başarıyla oluşturuldu',
|
|
976
|
+
updated: 'Başarıyla güncellendi',
|
|
977
|
+
deleted: 'Başarıyla silindi',
|
|
978
|
+
error: 'Bir hata oluştu',
|
|
979
|
+
},
|
|
980
|
+
},
|
|
981
|
+
}, null, 2) + '\n';
|
|
982
|
+
}
|
|
983
|
+
// ─── Faz 5 quality stack generators ─────────────────────────────────
|
|
984
|
+
// Moved to lib/ops/addon-quality.ts (Faz 5B) so the scaffold-quality
|
|
985
|
+
// command can reuse them. Local definitions below remain for now to
|
|
986
|
+
// avoid breaking the diff — they're now dead code and will be removed
|
|
987
|
+
// in a follow-up cleanup.
|
|
988
|
+
function _unused_genEslintConfig() {
|
|
989
|
+
return {
|
|
990
|
+
root: true,
|
|
991
|
+
env: { browser: true, es2022: true, node: true },
|
|
992
|
+
parser: 'vue-eslint-parser',
|
|
993
|
+
parserOptions: {
|
|
994
|
+
parser: '@typescript-eslint/parser',
|
|
995
|
+
ecmaVersion: 'latest',
|
|
996
|
+
sourceType: 'module',
|
|
997
|
+
extraFileExtensions: ['.vue'],
|
|
998
|
+
},
|
|
999
|
+
plugins: ['vue', '@typescript-eslint'],
|
|
1000
|
+
extends: [
|
|
1001
|
+
'eslint:recommended',
|
|
1002
|
+
'plugin:vue/vue3-recommended',
|
|
1003
|
+
'plugin:@typescript-eslint/recommended',
|
|
1004
|
+
],
|
|
1005
|
+
rules: {
|
|
1006
|
+
// Single-word component names are common in addon page files
|
|
1007
|
+
// (index.vue, list.vue). Vue's default rule would force PascalCase
|
|
1008
|
+
// multi-word; ergonomically wrong for our routing layout.
|
|
1009
|
+
'vue/multi-word-component-names': 'off',
|
|
1010
|
+
'vue/html-self-closing': ['error', { html: { void: 'always', normal: 'always', component: 'always' } }],
|
|
1011
|
+
// any-warnings rather than errors so addon authors can ship a TODO
|
|
1012
|
+
// explicitly; the warning surfaces in the deploy quality gate.
|
|
1013
|
+
'@typescript-eslint/no-explicit-any': 'warn',
|
|
1014
|
+
'no-unused-vars': 'off',
|
|
1015
|
+
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
|
|
1016
|
+
'prefer-const': 'error',
|
|
1017
|
+
'no-var': 'error',
|
|
1018
|
+
},
|
|
1019
|
+
ignorePatterns: ['dist/', 'node_modules/', '.vite-cache/', '*.d.ts'],
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
1022
|
+
function _unused_genPrettierConfig() {
|
|
1023
|
+
return {
|
|
1024
|
+
semi: false,
|
|
1025
|
+
singleQuote: true,
|
|
1026
|
+
tabWidth: 2,
|
|
1027
|
+
printWidth: 100,
|
|
1028
|
+
trailingComma: 'es5',
|
|
1029
|
+
vueIndentScriptAndStyle: false,
|
|
1030
|
+
endOfLine: 'lf',
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
function _unused_genEditorConfig() {
|
|
1034
|
+
return `root = true
|
|
1035
|
+
|
|
1036
|
+
[*]
|
|
1037
|
+
charset = utf-8
|
|
1038
|
+
end_of_line = lf
|
|
1039
|
+
indent_style = space
|
|
1040
|
+
indent_size = 2
|
|
1041
|
+
insert_final_newline = true
|
|
1042
|
+
trim_trailing_whitespace = true
|
|
1043
|
+
|
|
1044
|
+
[*.md]
|
|
1045
|
+
trim_trailing_whitespace = false
|
|
1046
|
+
`;
|
|
1047
|
+
}
|
|
1048
|
+
function _unused_genTsConfig() {
|
|
1049
|
+
return {
|
|
1050
|
+
compilerOptions: {
|
|
1051
|
+
target: 'ES2022',
|
|
1052
|
+
module: 'ESNext',
|
|
1053
|
+
moduleResolution: 'Bundler',
|
|
1054
|
+
strict: true,
|
|
1055
|
+
noUncheckedIndexedAccess: true,
|
|
1056
|
+
noImplicitOverride: true,
|
|
1057
|
+
esModuleInterop: true,
|
|
1058
|
+
skipLibCheck: true,
|
|
1059
|
+
isolatedModules: true,
|
|
1060
|
+
resolveJsonModule: true,
|
|
1061
|
+
noEmit: true,
|
|
1062
|
+
jsx: 'preserve',
|
|
1063
|
+
lib: ['ES2022', 'DOM', 'DOM.Iterable'],
|
|
1064
|
+
// vue types resolved from dev-shell/node_modules/vue (parent
|
|
1065
|
+
// lookup) so the addon doesn't need its own node_modules.
|
|
1066
|
+
types: ['vue'],
|
|
1067
|
+
paths: {
|
|
1068
|
+
'@/*': ['./*'],
|
|
1069
|
+
},
|
|
1070
|
+
},
|
|
1071
|
+
include: ['**/*.ts', '**/*.vue', 'ui/entry.ts'],
|
|
1072
|
+
exclude: ['node_modules', 'dist', '.vite-cache'],
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
/**
|
|
1076
|
+
* Ambient type declarations for the addon source. The runtime resolves
|
|
1077
|
+
* `@cicore/shared` via the host's import map and `$fetch` is Nuxt's
|
|
1078
|
+
* global, but vue-tsc has no way to find either without these shims.
|
|
1079
|
+
* Declared as `any` for now — Faz 6 (SDK docs) ships richer type
|
|
1080
|
+
* surfaces alongside the API reference and replaces these stubs.
|
|
1081
|
+
*/
|
|
1082
|
+
function _unused_genAmbientTypes() {
|
|
1083
|
+
return `// Ambient types for ciCore addon runtime. Replaced by the real
|
|
1084
|
+
// @cicore/shared types when the SDK package ships (Faz 6 FUTURE).
|
|
1085
|
+
|
|
1086
|
+
declare module '@cicore/shared' {
|
|
1087
|
+
// Runtime exports come from the host's import map — every Vue Composition
|
|
1088
|
+
// API name, the useApi/useAuth/useI18n composables, and the Nuxt UI
|
|
1089
|
+
// components are re-exported here. Typed as any to keep new addons
|
|
1090
|
+
// unblocked; Faz 6 (SDK docs) replaces this stub with typed surfaces.
|
|
1091
|
+
//
|
|
1092
|
+
// NOTE: TypeScript doesn't have a "catch-all named exports" syntax, so
|
|
1093
|
+
// every name an addon imports must appear here. If vue-tsc rejects an
|
|
1094
|
+
// import you need, add it to this list (and to docs/addon-sdk in Faz 6).
|
|
1095
|
+
export const ref: any
|
|
1096
|
+
export const reactive: any
|
|
1097
|
+
export const computed: any
|
|
1098
|
+
export const watch: any
|
|
1099
|
+
export const watchEffect: any
|
|
1100
|
+
export const onMounted: any
|
|
1101
|
+
export const onUnmounted: any
|
|
1102
|
+
export const onBeforeMount: any
|
|
1103
|
+
export const onBeforeUnmount: any
|
|
1104
|
+
export const nextTick: any
|
|
1105
|
+
export const defineComponent: any
|
|
1106
|
+
export const defineProps: any
|
|
1107
|
+
export const defineEmits: any
|
|
1108
|
+
export const useI18n: any
|
|
1109
|
+
export const useAuth: any
|
|
1110
|
+
export const useApi: any
|
|
1111
|
+
export const useAddons: any
|
|
1112
|
+
export const useTenant: any
|
|
1113
|
+
export const usePermissions: any
|
|
1114
|
+
export const useIcon: any
|
|
1115
|
+
export const useNuxtUI: any
|
|
1116
|
+
export const useRuntimeConfig: any
|
|
1117
|
+
export const useToast: any
|
|
1118
|
+
export const useRouter: any
|
|
1119
|
+
export const useRoute: any
|
|
1120
|
+
export const useGsap: any
|
|
1121
|
+
export const Icon: any
|
|
1122
|
+
export const UButton: any
|
|
1123
|
+
export const UCard: any
|
|
1124
|
+
export const UInput: any
|
|
1125
|
+
export const UInputMenu: any
|
|
1126
|
+
export const UModal: any
|
|
1127
|
+
export const USelectMenu: any
|
|
1128
|
+
export const USwitch: any
|
|
1129
|
+
export const UAlert: any
|
|
1130
|
+
export const UBadge: any
|
|
1131
|
+
export const UTooltip: any
|
|
1132
|
+
export const UFieldGroup: any
|
|
1133
|
+
export const UForm: any
|
|
1134
|
+
export const UFormField: any
|
|
1135
|
+
export const UTextarea: any
|
|
1136
|
+
export const UCheckbox: any
|
|
1137
|
+
export const UTable: any
|
|
1138
|
+
export const UPagination: any
|
|
1139
|
+
export const UAvatar: any
|
|
1140
|
+
export const UDropdownMenu: any
|
|
1141
|
+
const _default: any
|
|
1142
|
+
export default _default
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
// Nuxt global available to addon pages without explicit import.
|
|
1146
|
+
declare const $fetch: <T = unknown>(url: string, opts?: Record<string, unknown>) => Promise<T>
|
|
1147
|
+
|
|
1148
|
+
// Vue SFC module shim so .vue imports resolve to a component type.
|
|
1149
|
+
declare module '*.vue' {
|
|
1150
|
+
import type { DefineComponent } from 'vue'
|
|
1151
|
+
const component: DefineComponent<Record<string, unknown>, Record<string, unknown>, unknown>
|
|
1152
|
+
export default component
|
|
1153
|
+
}
|
|
1154
|
+
`;
|
|
1155
|
+
}
|
|
1156
|
+
function _unused_genGitlabCi(addonName) {
|
|
1157
|
+
return `# Auto-generated by \`ci addon create ${addonName}\`. Faz 5 quality template.
|
|
1158
|
+
# Runs lint + typecheck on every MR + on default branch. Tooling resolves
|
|
1159
|
+
# from the addon-dev-shell repo's node_modules (cached across pipelines).
|
|
1160
|
+
|
|
1161
|
+
stages:
|
|
1162
|
+
- check
|
|
1163
|
+
|
|
1164
|
+
quality:
|
|
1165
|
+
stage: check
|
|
1166
|
+
image: node:22-alpine
|
|
1167
|
+
cache:
|
|
1168
|
+
key: addon-dev-shell-tools
|
|
1169
|
+
paths: [dev-shell-node_modules/]
|
|
1170
|
+
before_script:
|
|
1171
|
+
# The addon ships with no local node_modules — clone the shared
|
|
1172
|
+
# toolchain from cicore-addons/addon-dev-shell at the same commit
|
|
1173
|
+
# personnel uses locally. Pin to a tag if you need reproducibility.
|
|
1174
|
+
- apk add --no-cache git
|
|
1175
|
+
- git clone --depth 1 http://10.20.20.28/cicore-addons/addon-dev-shell tmp-shell
|
|
1176
|
+
- mv tmp-shell/package.json tmp-shell/package-lock.json ./
|
|
1177
|
+
- npm ci --no-audit --no-fund --prefer-offline
|
|
1178
|
+
script:
|
|
1179
|
+
- ./node_modules/.bin/eslint . --max-warnings 0
|
|
1180
|
+
- ./node_modules/.bin/vue-tsc --noEmit
|
|
1181
|
+
rules:
|
|
1182
|
+
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
|
1183
|
+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
|
1184
|
+
`;
|
|
1185
|
+
}
|
|
1186
|
+
//# sourceMappingURL=create.js.map
|