vibe-design-system 2.7.3 → 2.8.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/init.js
CHANGED
|
@@ -149,19 +149,187 @@ function readPackageJson(projectRoot) {
|
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
|
|
153
|
-
|
|
152
|
+
/**
|
|
153
|
+
* Monorepo kök dizini tespiti.
|
|
154
|
+
* workspaces veya pnpm-workspace.yaml ya da apps/packages klasörü varsa monorepo sayılır.
|
|
155
|
+
* Bulunan paket/uygulama dizinlerini döndürür (boş array = monorepo değil).
|
|
156
|
+
*/
|
|
157
|
+
function detectMonorepoPackages(root) {
|
|
158
|
+
const pkg = readPackageJson(root);
|
|
159
|
+
if (pkg && pkg.workspaces) {
|
|
160
|
+
const patterns = Array.isArray(pkg.workspaces) ? pkg.workspaces : pkg.workspaces.packages ?? [];
|
|
161
|
+
const dirs = [];
|
|
162
|
+
for (const p of patterns) {
|
|
163
|
+
const base = p.replace(/\/\*.*$/, "");
|
|
164
|
+
const full = path.join(root, base);
|
|
165
|
+
if (fs.existsSync(full)) {
|
|
166
|
+
const entries = fs.readdirSync(full, { withFileTypes: true });
|
|
167
|
+
for (const e of entries) {
|
|
168
|
+
if (e.isDirectory() && fs.existsSync(path.join(full, e.name, "package.json"))) {
|
|
169
|
+
dirs.push(path.join(full, e.name));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (dirs.length) return dirs;
|
|
175
|
+
}
|
|
176
|
+
const pnpmWs = path.join(root, "pnpm-workspace.yaml");
|
|
177
|
+
if (fs.existsSync(pnpmWs)) {
|
|
178
|
+
const content = fs.readFileSync(pnpmWs, "utf-8");
|
|
179
|
+
const dirs = [];
|
|
180
|
+
for (const line of content.split("\n")) {
|
|
181
|
+
const m = line.match(/^\s*-\s*['"]?(.+?)['"]?\s*$/);
|
|
182
|
+
if (m) {
|
|
183
|
+
const base = m[1].replace(/\/\*.*$/, "");
|
|
184
|
+
const full = path.join(root, base);
|
|
185
|
+
if (fs.existsSync(full)) {
|
|
186
|
+
const entries = fs.readdirSync(full, { withFileTypes: true });
|
|
187
|
+
for (const e of entries) {
|
|
188
|
+
if (e.isDirectory() && fs.existsSync(path.join(full, e.name, "package.json"))) {
|
|
189
|
+
dirs.push(path.join(full, e.name));
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (dirs.length) return dirs;
|
|
196
|
+
}
|
|
197
|
+
for (const candidate of ["apps", "packages"]) {
|
|
198
|
+
const full = path.join(root, candidate);
|
|
199
|
+
if (fs.existsSync(full)) {
|
|
200
|
+
const entries = fs.readdirSync(full, { withFileTypes: true })
|
|
201
|
+
.filter((e) => e.isDirectory() && fs.existsSync(path.join(full, e.name, "package.json")))
|
|
202
|
+
.map((e) => path.join(full, e.name));
|
|
203
|
+
if (entries.length) return entries;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return [];
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Projenin hangi framework'ü kullandığını tespit eder.
|
|
211
|
+
* @returns {"nextjs"|"remix"|"vite"}
|
|
212
|
+
*/
|
|
213
|
+
function detectFramework(pkg) {
|
|
214
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
215
|
+
if (deps["next"]) return "nextjs";
|
|
216
|
+
if (deps["@remix-run/react"] || deps["@remix-run/node"]) return "remix";
|
|
217
|
+
return "vite";
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** Framework'e göre Storybook framework paket adı */
|
|
221
|
+
function storybookFrameworkPackage(framework) {
|
|
222
|
+
if (framework === "nextjs") return "@storybook/nextjs";
|
|
223
|
+
return "@storybook/react-vite"; // vite & remix
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** Framework'e göre .storybook/main.ts içeriği */
|
|
227
|
+
function buildStorybookMainTs(framework) {
|
|
228
|
+
if (framework === "nextjs") {
|
|
229
|
+
return `import type { StorybookConfig } from "@storybook/nextjs";
|
|
230
|
+
|
|
231
|
+
const config: StorybookConfig = {
|
|
232
|
+
stories: [
|
|
233
|
+
"../src/**/*.stories.@(js|jsx|mjs|ts|tsx)",
|
|
234
|
+
"../app/**/*.stories.@(js|jsx|mjs|ts|tsx)",
|
|
235
|
+
],
|
|
236
|
+
addons: ["@storybook/addon-essentials"],
|
|
237
|
+
framework: {
|
|
238
|
+
name: "@storybook/nextjs",
|
|
239
|
+
options: {},
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
export default config;
|
|
244
|
+
`;
|
|
245
|
+
}
|
|
246
|
+
// vite (default) & remix
|
|
247
|
+
return `import type { StorybookConfig } from "@storybook/react-vite";
|
|
248
|
+
import { mergeConfig } from "vite";
|
|
249
|
+
import path from "path";
|
|
250
|
+
|
|
251
|
+
const config: StorybookConfig = {
|
|
252
|
+
stories: ["../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
|
|
253
|
+
addons: ["@storybook/addon-essentials"],
|
|
254
|
+
framework: {
|
|
255
|
+
name: "@storybook/react-vite",
|
|
256
|
+
options: {},
|
|
257
|
+
},
|
|
258
|
+
async viteFinal(config) {
|
|
259
|
+
return mergeConfig(config, {
|
|
260
|
+
resolve: {
|
|
261
|
+
alias: {
|
|
262
|
+
"@": path.resolve(process.cwd(), "src"),
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
export default config;
|
|
270
|
+
`;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/** Framework'e göre CSS import path'i */
|
|
274
|
+
function buildStorybookPreviewTs(framework, projectRoot) {
|
|
275
|
+
// CSS dosyasını bul
|
|
276
|
+
const cssCandidates = [
|
|
277
|
+
["src/index.css", "../src/index.css"],
|
|
278
|
+
["src/globals.css", "../src/globals.css"],
|
|
279
|
+
["src/styles/globals.css", "../src/styles/globals.css"],
|
|
280
|
+
["app/globals.css", "../app/globals.css"],
|
|
281
|
+
];
|
|
282
|
+
let cssImport = '// CSS bulunamadı — projenizin global CSS dosyasını buraya ekleyin';
|
|
283
|
+
for (const [rel, importPath] of cssCandidates) {
|
|
284
|
+
if (fs.existsSync(path.join(projectRoot, rel))) {
|
|
285
|
+
cssImport = `import "${importPath}";`;
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return `import type { Preview } from "@storybook/react";
|
|
291
|
+
import React from "react";
|
|
292
|
+
${cssImport}
|
|
293
|
+
|
|
294
|
+
const preview: Preview = {
|
|
295
|
+
parameters: {
|
|
296
|
+
layout: "fullscreen",
|
|
297
|
+
options: {
|
|
298
|
+
storySort: {
|
|
299
|
+
order: [
|
|
300
|
+
"Foundations",
|
|
301
|
+
["Introduction", "Page to Components", "Colors", "Typography", "Brand", "Icons", "Component Suggestions", "Changelog"],
|
|
302
|
+
"Layout",
|
|
303
|
+
"Components",
|
|
304
|
+
"Actions",
|
|
305
|
+
"Data Display",
|
|
306
|
+
"Examples",
|
|
307
|
+
],
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
export default preview;
|
|
314
|
+
`;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// ADIM 1 — Bağımlılık kontrolü
|
|
318
|
+
function needsStorybook(projectRoot, framework) {
|
|
154
319
|
const pkg = readPackageJson(projectRoot);
|
|
155
320
|
if (!pkg) return false;
|
|
156
321
|
const dev = pkg.devDependencies || {};
|
|
157
|
-
|
|
322
|
+
const fwPkg = storybookFrameworkPackage(framework);
|
|
323
|
+
return !(dev.storybook && dev[fwPkg]);
|
|
158
324
|
}
|
|
159
325
|
|
|
160
326
|
// Storybook 8.6.x — aynı minor sürümde tutarak addon uyumluluk uyarısını önler
|
|
161
327
|
const STORYBOOK_VERSION = "8.6.17";
|
|
162
328
|
|
|
163
|
-
function installStorybook(projectRoot) {
|
|
164
|
-
|
|
329
|
+
function installStorybook(projectRoot, framework) {
|
|
330
|
+
const fwPkg = storybookFrameworkPackage(framework);
|
|
331
|
+
console.log(`📚 Storybook v8 (${fwPkg}) kuruluyor...`);
|
|
332
|
+
const extraDeps = framework === "vite" ? ["@tailwindcss/vite"] : [];
|
|
165
333
|
const r = spawnSync(
|
|
166
334
|
"npm",
|
|
167
335
|
[
|
|
@@ -169,11 +337,11 @@ function installStorybook(projectRoot) {
|
|
|
169
337
|
"--save-dev",
|
|
170
338
|
"--save-exact",
|
|
171
339
|
`storybook@${STORYBOOK_VERSION}`,
|
|
172
|
-
|
|
340
|
+
`${fwPkg}@${STORYBOOK_VERSION}`,
|
|
173
341
|
`@storybook/react@${STORYBOOK_VERSION}`,
|
|
174
342
|
`@storybook/addon-essentials@${STORYBOOK_VERSION}`,
|
|
175
343
|
`@storybook/blocks@${STORYBOOK_VERSION}`,
|
|
176
|
-
|
|
344
|
+
...extraDeps,
|
|
177
345
|
],
|
|
178
346
|
{ cwd: projectRoot, stdio: "inherit", shell: false }
|
|
179
347
|
);
|
|
@@ -183,18 +351,21 @@ function installStorybook(projectRoot) {
|
|
|
183
351
|
}
|
|
184
352
|
|
|
185
353
|
// ADIM 2 — .storybook/
|
|
186
|
-
function ensureStorybook(projectRoot) {
|
|
354
|
+
function ensureStorybook(projectRoot, framework) {
|
|
187
355
|
const storybookDir = path.join(projectRoot, ".storybook");
|
|
188
356
|
if (!fs.existsSync(storybookDir)) {
|
|
189
357
|
fs.mkdirSync(storybookDir, { recursive: true });
|
|
190
358
|
console.log("📁 .storybook/ oluşturuldu.");
|
|
191
359
|
}
|
|
192
360
|
|
|
361
|
+
const mainTs = buildStorybookMainTs(framework);
|
|
193
362
|
const mainPath = path.join(storybookDir, "main.ts");
|
|
194
|
-
fs.writeFileSync(mainPath,
|
|
195
|
-
console.log(
|
|
363
|
+
fs.writeFileSync(mainPath, mainTs, "utf-8");
|
|
364
|
+
console.log(`📝 .storybook/main.ts yazıldı (framework: ${framework}).`);
|
|
365
|
+
|
|
366
|
+
const previewTs = buildStorybookPreviewTs(framework, projectRoot);
|
|
196
367
|
|
|
197
|
-
// preview.ts / preview.tsx — eski VDS template'lerini
|
|
368
|
+
// preview.ts / preview.tsx — eski VDS template'lerini migrate et; kullanıcıya ait preview'e dokunma.
|
|
198
369
|
const previewCandidates = ["preview.tsx", "preview.ts", "preview.jsx", "preview.js"];
|
|
199
370
|
const existingPreview = previewCandidates.find((name) =>
|
|
200
371
|
fs.existsSync(path.join(storybookDir, name))
|
|
@@ -202,11 +373,9 @@ function ensureStorybook(projectRoot) {
|
|
|
202
373
|
const previewPath = existingPreview ? path.join(storybookDir, existingPreview) : null;
|
|
203
374
|
|
|
204
375
|
if (!previewPath) {
|
|
205
|
-
|
|
206
|
-
fs.writeFileSync(path.join(storybookDir, "preview.ts"), STORYBOOK_PREVIEW_TS, "utf-8");
|
|
376
|
+
fs.writeFileSync(path.join(storybookDir, "preview.ts"), previewTs, "utf-8");
|
|
207
377
|
console.log("📝 .storybook/preview.ts yazıldı.");
|
|
208
378
|
} else {
|
|
209
|
-
// Eski VDS preview'lerini tespit edip otomatik migrate et; kullanıcıya ait preview'lere dokunma.
|
|
210
379
|
try {
|
|
211
380
|
const content = fs.readFileSync(previewPath, "utf-8");
|
|
212
381
|
const looksLikeOldVdsPreview =
|
|
@@ -214,8 +383,8 @@ function ensureStorybook(projectRoot) {
|
|
|
214
383
|
content.includes("const withRouter") ||
|
|
215
384
|
content.includes("decorators: [withRouter");
|
|
216
385
|
if (looksLikeOldVdsPreview) {
|
|
217
|
-
fs.writeFileSync(previewPath,
|
|
218
|
-
console.log("📝 .storybook/preview.* eski VDS şablonundan yeni
|
|
386
|
+
fs.writeFileSync(previewPath, previewTs, "utf-8");
|
|
387
|
+
console.log("📝 .storybook/preview.* eski VDS şablonundan yeni şablona güncellendi.");
|
|
219
388
|
} else {
|
|
220
389
|
console.log("ℹ️ .storybook/preview.* bulundu, kullanıcıya ait görünüyor; değiştirilmedi.");
|
|
221
390
|
}
|
|
@@ -294,7 +463,7 @@ function ensureCursorrules(projectRoot) {
|
|
|
294
463
|
console.log("📄 .cursorrules yazıldı.");
|
|
295
464
|
}
|
|
296
465
|
|
|
297
|
-
// ADIM 6 — src/stories/ (
|
|
466
|
+
// ADIM 6 — src/stories/ oluştur (scan artık klasörü kendisi buluyor; zorla yaratmaya gerek yok)
|
|
298
467
|
function ensureStoriesDir(projectRoot) {
|
|
299
468
|
const srcDir = path.join(projectRoot, "src");
|
|
300
469
|
if (!fs.existsSync(srcDir)) fs.mkdirSync(srcDir, { recursive: true });
|
|
@@ -303,15 +472,8 @@ function ensureStoriesDir(projectRoot) {
|
|
|
303
472
|
fs.mkdirSync(storiesDir, { recursive: true });
|
|
304
473
|
console.log("📁 src/stories/ oluşturuldu.");
|
|
305
474
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
fs.mkdirSync(componentsDir, { recursive: true });
|
|
309
|
-
console.log("📁 src/components/ oluşturuldu (VDS taraması için).");
|
|
310
|
-
}
|
|
311
|
-
const pagesDir = path.join(srcDir, "pages");
|
|
312
|
-
if (!fs.existsSync(pagesDir)) {
|
|
313
|
-
fs.mkdirSync(pagesDir, { recursive: true });
|
|
314
|
-
}
|
|
475
|
+
// scan.mjs artık klasörü kendisi buluyor (src/components → components → app/components → ...)
|
|
476
|
+
// Klasör yoksa oluşturma — projeye ait mevcut yapıyı koruyalım.
|
|
315
477
|
}
|
|
316
478
|
|
|
317
479
|
// ADIM 7 — İlk tarama
|
|
@@ -364,6 +526,24 @@ function runStorybookAdapt(projectRoot) {
|
|
|
364
526
|
if (r.status !== 0) process.exitCode = r.status ?? 1;
|
|
365
527
|
}
|
|
366
528
|
|
|
529
|
+
// ADIM 9b — vds.config.js oluştur (yoksa)
|
|
530
|
+
function ensureVdsConfig(projectRoot) {
|
|
531
|
+
const configPath = path.join(projectRoot, "vds.config.js");
|
|
532
|
+
if (fs.existsSync(configPath)) return;
|
|
533
|
+
const content = `/**
|
|
534
|
+
* VDS Configuration — auto-created by VDS installer
|
|
535
|
+
* All fields are optional. Remove comments to activate overrides.
|
|
536
|
+
*/
|
|
537
|
+
module.exports = {
|
|
538
|
+
// skipList: ["MyHeavyPage"], // Replace the default story skip list entirely
|
|
539
|
+
// extraSkipList: ["OrderCard"], // Add to the default skip list
|
|
540
|
+
// extraIgnoreDirs: ["fixtures"], // Additional dirs to skip during component scan
|
|
541
|
+
};
|
|
542
|
+
`;
|
|
543
|
+
fs.writeFileSync(configPath, content, "utf-8");
|
|
544
|
+
console.log("⚙️ vds.config.js oluşturuldu.");
|
|
545
|
+
}
|
|
546
|
+
|
|
367
547
|
// ADIM 9 — Storybook örnek dosyalarını sil
|
|
368
548
|
function removeStorybookExamples(projectRoot) {
|
|
369
549
|
const storiesDir = path.join(projectRoot, "src", "stories");
|
|
@@ -389,15 +569,35 @@ if (!pkg) {
|
|
|
389
569
|
process.exit(1);
|
|
390
570
|
}
|
|
391
571
|
|
|
572
|
+
// Monorepo kök dizininde mi?
|
|
573
|
+
const monorepoPackages = detectMonorepoPackages(projectRoot);
|
|
574
|
+
if (monorepoPackages.length > 0 && pkg.workspaces) {
|
|
575
|
+
console.warn("⚠️ Bu dizin bir monorepo kök dizini gibi görünüyor.");
|
|
576
|
+
console.warn(" VDS'yi her uygulama klasöründe ayrı ayrı kurmanız gerekiyor.\n");
|
|
577
|
+
console.warn(" Tespit edilen paketler:");
|
|
578
|
+
for (const p of monorepoPackages) {
|
|
579
|
+
const rel = path.relative(projectRoot, p);
|
|
580
|
+
console.warn(` → ${rel}`);
|
|
581
|
+
}
|
|
582
|
+
console.warn("\n Örnek kullanım:");
|
|
583
|
+
const firstRel = path.relative(projectRoot, monorepoPackages[0]);
|
|
584
|
+
console.warn(` cd ${firstRel} && npx vibe-design-system init`);
|
|
585
|
+
console.warn("\n VDS'yi bu dizinden çalıştırmaya devam etmek istiyorsanız,");
|
|
586
|
+
console.warn(" VDS_COMPONENTS_DIR env değişkeni ile hedef klasörü belirtin.\n");
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
const framework = detectFramework(pkg);
|
|
590
|
+
console.log(`🔍 Framework tespit edildi: ${framework}\n`);
|
|
591
|
+
|
|
392
592
|
// ADIM 1
|
|
393
|
-
if (needsStorybook(projectRoot)) {
|
|
394
|
-
installStorybook(projectRoot);
|
|
593
|
+
if (needsStorybook(projectRoot, framework)) {
|
|
594
|
+
installStorybook(projectRoot, framework);
|
|
395
595
|
} else {
|
|
396
596
|
console.log("📚 Storybook zaten yüklü.");
|
|
397
597
|
}
|
|
398
598
|
|
|
399
599
|
// ADIM 2
|
|
400
|
-
ensureStorybook(projectRoot);
|
|
600
|
+
ensureStorybook(projectRoot, framework);
|
|
401
601
|
|
|
402
602
|
// ADIM 3
|
|
403
603
|
ensureVdsCore(projectRoot);
|
|
@@ -414,6 +614,9 @@ ensureStoriesDir(projectRoot);
|
|
|
414
614
|
// ADIM 9 (örnek dosyaları taramadan önce silebiliriz; scan src/components ve src/pages tarar, src/stories değil)
|
|
415
615
|
removeStorybookExamples(projectRoot);
|
|
416
616
|
|
|
617
|
+
// ADIM 9b
|
|
618
|
+
ensureVdsConfig(projectRoot);
|
|
619
|
+
|
|
417
620
|
// ADIM 7
|
|
418
621
|
runScan(projectRoot);
|
|
419
622
|
runStoryGenerator(projectRoot);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibe-design-system",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.8.0",
|
|
4
4
|
"description": "Auto-generate design systems for vibe coding projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -38,4 +38,4 @@
|
|
|
38
38
|
"typescript": "^5.8.3",
|
|
39
39
|
"vite": "^5.4.19"
|
|
40
40
|
}
|
|
41
|
-
}
|
|
41
|
+
}
|
|
@@ -26,12 +26,143 @@ const CLI_LOCALES = {
|
|
|
26
26
|
};
|
|
27
27
|
const CLI_LOCALE = (process.env.VDS_LOCALE === "tr" ? "tr" : "en");
|
|
28
28
|
const cliT = (key, n) => CLI_LOCALES[CLI_LOCALE][key].replace("{n}", String(n));
|
|
29
|
-
const SRC_DIR = path.join(PROJECT_ROOT, "src");
|
|
30
|
-
const COMPONENTS_DIR = path.join(PROJECT_ROOT, "src", "components");
|
|
31
|
-
const PAGES_DIR = path.join(PROJECT_ROOT, "src", "pages");
|
|
32
|
-
const APP_DIR = path.join(PROJECT_ROOT, "src", "app");
|
|
33
29
|
|
|
34
|
-
|
|
30
|
+
// ── Klasör çözücüler ─────────────────────────────────────────────────────────
|
|
31
|
+
function resolveDir(envKey, candidates) {
|
|
32
|
+
if (process.env[envKey]) {
|
|
33
|
+
const d = path.resolve(PROJECT_ROOT, process.env[envKey]);
|
|
34
|
+
if (fs.existsSync(d)) return d;
|
|
35
|
+
console.warn(`[VDS] ${envKey}="${process.env[envKey]}" bulunamadı, fallback deneniyor.`);
|
|
36
|
+
}
|
|
37
|
+
for (const d of candidates) {
|
|
38
|
+
if (fs.existsSync(d)) return d;
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const SRC_DIR = resolveDir("VDS_SRC_DIR", [
|
|
44
|
+
path.join(PROJECT_ROOT, "src"),
|
|
45
|
+
path.join(PROJECT_ROOT, "app"),
|
|
46
|
+
PROJECT_ROOT,
|
|
47
|
+
]) || path.join(PROJECT_ROOT, "src");
|
|
48
|
+
|
|
49
|
+
const COMPONENTS_DIR = resolveDir("VDS_COMPONENTS_DIR", [
|
|
50
|
+
path.join(PROJECT_ROOT, "src", "components"),
|
|
51
|
+
path.join(PROJECT_ROOT, "components"),
|
|
52
|
+
path.join(PROJECT_ROOT, "app", "components"),
|
|
53
|
+
path.join(PROJECT_ROOT, "src", "views"),
|
|
54
|
+
path.join(PROJECT_ROOT, "src", "modules"),
|
|
55
|
+
]);
|
|
56
|
+
|
|
57
|
+
const PAGES_DIR = resolveDir("VDS_PAGES_DIR", [
|
|
58
|
+
path.join(PROJECT_ROOT, "src", "pages"),
|
|
59
|
+
path.join(PROJECT_ROOT, "pages"),
|
|
60
|
+
]);
|
|
61
|
+
|
|
62
|
+
const APP_DIR = resolveDir("VDS_APP_DIR", [
|
|
63
|
+
path.join(PROJECT_ROOT, "src", "app"),
|
|
64
|
+
path.join(PROJECT_ROOT, "app"),
|
|
65
|
+
]);
|
|
66
|
+
|
|
67
|
+
// ── Monorepo detection ────────────────────────────────────────────────────────
|
|
68
|
+
function detectMonorepoPackages(root) {
|
|
69
|
+
const pkgPath = path.join(root, "package.json");
|
|
70
|
+
if (fs.existsSync(pkgPath)) {
|
|
71
|
+
try {
|
|
72
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
73
|
+
if (pkg.workspaces) {
|
|
74
|
+
const patterns = Array.isArray(pkg.workspaces) ? pkg.workspaces : pkg.workspaces.packages ?? [];
|
|
75
|
+
const dirs = [];
|
|
76
|
+
for (const p of patterns) {
|
|
77
|
+
const base = p.replace(/\/\*.*$/, "");
|
|
78
|
+
const full = path.join(root, base);
|
|
79
|
+
if (fs.existsSync(full)) {
|
|
80
|
+
const entries = fs.readdirSync(full, { withFileTypes: true });
|
|
81
|
+
for (const e of entries) {
|
|
82
|
+
if (e.isDirectory()) dirs.push(path.join(full, e.name));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (dirs.length) return dirs;
|
|
87
|
+
}
|
|
88
|
+
} catch (_) {}
|
|
89
|
+
}
|
|
90
|
+
const pnpmWs = path.join(root, "pnpm-workspace.yaml");
|
|
91
|
+
if (fs.existsSync(pnpmWs)) {
|
|
92
|
+
const content = fs.readFileSync(pnpmWs, "utf-8");
|
|
93
|
+
const dirs = [];
|
|
94
|
+
for (const line of content.split("\n")) {
|
|
95
|
+
const m = line.match(/^\s*-\s*['"]?(.+?)['"]?\s*$/);
|
|
96
|
+
if (m) {
|
|
97
|
+
const base = m[1].replace(/\/\*.*$/, "");
|
|
98
|
+
const full = path.join(root, base);
|
|
99
|
+
if (fs.existsSync(full)) {
|
|
100
|
+
const entries = fs.readdirSync(full, { withFileTypes: true });
|
|
101
|
+
for (const e of entries) {
|
|
102
|
+
if (e.isDirectory()) dirs.push(path.join(full, e.name));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (dirs.length) return dirs;
|
|
108
|
+
}
|
|
109
|
+
for (const candidate of ["apps", "packages"]) {
|
|
110
|
+
const full = path.join(root, candidate);
|
|
111
|
+
if (fs.existsSync(full)) {
|
|
112
|
+
const entries = fs.readdirSync(full, { withFileTypes: true })
|
|
113
|
+
.filter((e) => e.isDirectory())
|
|
114
|
+
.map((e) => path.join(full, e.name));
|
|
115
|
+
if (entries.length) return entries;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return [];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (COMPONENTS_DIR) {
|
|
122
|
+
console.log(`[VDS] Components: ${path.relative(PROJECT_ROOT, COMPONENTS_DIR)}`);
|
|
123
|
+
} else {
|
|
124
|
+
const monorepoPackages = detectMonorepoPackages(PROJECT_ROOT);
|
|
125
|
+
if (monorepoPackages.length > 0) {
|
|
126
|
+
console.warn("[VDS] Bileşen klasörü bulunamadı — bu bir monorepo kök dizini gibi görünüyor.");
|
|
127
|
+
console.warn("[VDS] Tespit edilen paketler:");
|
|
128
|
+
for (const p of monorepoPackages) {
|
|
129
|
+
console.warn(" →", path.relative(PROJECT_ROOT, p));
|
|
130
|
+
}
|
|
131
|
+
console.warn("[VDS] Çözüm: VDS'yi her uygulama klasöründen ayrı ayrı çalıştırın:");
|
|
132
|
+
console.warn(" cd apps/web && node ../vds-core/scan.mjs");
|
|
133
|
+
console.warn(" ya da: VDS_COMPONENTS_DIR=apps/web/src/components node vds-core/scan.mjs");
|
|
134
|
+
} else {
|
|
135
|
+
console.warn("[VDS] Bileşen klasörü bulunamadı. Tarama foundations-only modda devam edecek.");
|
|
136
|
+
console.warn("[VDS] Çözüm: VDS_COMPONENTS_DIR env değişkeni ile klasör belirtin.");
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ── vds.config.js loader ──────────────────────────────────────────────────────
|
|
141
|
+
function loadVdsConfig() {
|
|
142
|
+
const configPath = path.join(PROJECT_ROOT, "vds.config.js");
|
|
143
|
+
if (!fs.existsSync(configPath)) return {};
|
|
144
|
+
try {
|
|
145
|
+
const raw = fs.readFileSync(configPath, "utf-8");
|
|
146
|
+
const mod = { exports: {} };
|
|
147
|
+
// eslint-disable-next-line no-new-func
|
|
148
|
+
new Function("module", "exports", "require", raw)(mod, mod.exports, () => ({}));
|
|
149
|
+
const cfg = mod.exports.default ?? mod.exports;
|
|
150
|
+
return typeof cfg === "object" && cfg !== null ? cfg : {};
|
|
151
|
+
} catch (e) {
|
|
152
|
+
console.warn("[VDS] vds.config.js yüklenemedi:", e.message);
|
|
153
|
+
return {};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const VDS_CONFIG = loadVdsConfig();
|
|
158
|
+
if (Object.keys(VDS_CONFIG).length > 0) {
|
|
159
|
+
console.log("[VDS] vds.config.js yüklendi:", Object.keys(VDS_CONFIG).join(", "));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const IGNORE_DIRS = [
|
|
163
|
+
"node_modules", "dist", ".next", "build", "vds-generated", ".storybook",
|
|
164
|
+
...(VDS_CONFIG.extraIgnoreDirs ?? []),
|
|
165
|
+
];
|
|
35
166
|
const OUTPUT_FILE = path.join(PROJECT_ROOT, "vds-output.json");
|
|
36
167
|
const PUBLIC_MANIFEST = path.join(PROJECT_ROOT, "public", "vds-output.json");
|
|
37
168
|
const HISTORY_FILE = path.join(PROJECT_ROOT, "vds-history.json");
|
|
@@ -521,7 +652,7 @@ function extractFullElement(content, classNameMatchIndex, tagName) {
|
|
|
521
652
|
/** Return set of component names already in src/components (so we don't suggest them again). */
|
|
522
653
|
function getExistingComponentNames() {
|
|
523
654
|
const names = new Set();
|
|
524
|
-
if (!fs.existsSync(COMPONENTS_DIR)) return names;
|
|
655
|
+
if (!COMPONENTS_DIR || !fs.existsSync(COMPONENTS_DIR)) return names;
|
|
525
656
|
const walk = (dir) => {
|
|
526
657
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
527
658
|
for (const e of entries) {
|
|
@@ -537,13 +668,12 @@ function getExistingComponentNames() {
|
|
|
537
668
|
return names;
|
|
538
669
|
}
|
|
539
670
|
|
|
540
|
-
/** Scan
|
|
671
|
+
/** Scan pages/app/components dirs for repeated visual-section patterns; return component suggestions. */
|
|
541
672
|
function extractComponentSuggestions() {
|
|
542
|
-
const pageDirs = [
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
];
|
|
673
|
+
const pageDirs = [];
|
|
674
|
+
if (PAGES_DIR) pageDirs.push({ dir: PAGES_DIR, prefix: path.relative(PROJECT_ROOT, PAGES_DIR).replace(/\\/g, "/") + "/" });
|
|
675
|
+
if (APP_DIR && APP_DIR !== PAGES_DIR) pageDirs.push({ dir: APP_DIR, prefix: path.relative(PROJECT_ROOT, APP_DIR).replace(/\\/g, "/") + "/" });
|
|
676
|
+
if (COMPONENTS_DIR) pageDirs.push({ dir: COMPONENTS_DIR, prefix: path.relative(PROJECT_ROOT, COMPONENTS_DIR).replace(/\\/g, "/") + "/" });
|
|
547
677
|
const allPageFiles = [];
|
|
548
678
|
for (const { dir, prefix } of pageDirs) {
|
|
549
679
|
if (!fs.existsSync(dir)) continue;
|
|
@@ -707,7 +837,7 @@ function extractUnreleasedSectionCandidates() {
|
|
|
707
837
|
}));
|
|
708
838
|
}
|
|
709
839
|
|
|
710
|
-
const VDS_GENERATED_DIR = path.join(COMPONENTS_DIR, "vds-generated");
|
|
840
|
+
const VDS_GENERATED_DIR = COMPONENTS_DIR ? path.join(COMPONENTS_DIR, "vds-generated") : null;
|
|
711
841
|
|
|
712
842
|
/** Ensure component name is valid JS: no leading digits/special chars, PascalCase. */
|
|
713
843
|
function sanitizeComponentName(name) {
|
|
@@ -805,6 +935,7 @@ function collectComponentTags(jsx) {
|
|
|
805
935
|
/** Write extracted components with full JSX body; add Lucide and UI imports as needed. */
|
|
806
936
|
function writeVdsGeneratedComponents(suggestions) {
|
|
807
937
|
if (!Array.isArray(suggestions) || suggestions.length === 0) return [];
|
|
938
|
+
if (!VDS_GENERATED_DIR) return [];
|
|
808
939
|
if (!fs.existsSync(VDS_GENERATED_DIR)) fs.mkdirSync(VDS_GENERATED_DIR, { recursive: true });
|
|
809
940
|
const usedNames = new Set();
|
|
810
941
|
const entries = [];
|
|
@@ -1000,45 +1131,64 @@ function getTailwindExtendColors() {
|
|
|
1000
1131
|
}
|
|
1001
1132
|
}
|
|
1002
1133
|
|
|
1003
|
-
|
|
1134
|
+
function parseTailwindThemeFromText(raw) {
|
|
1135
|
+
const result = { shadows: {}, spacing: {}, breakpoints: {}, zIndex: {}, transitionDuration: {}, transitionTimingFunction: {}, animation: {}, colors: {} };
|
|
1136
|
+
try {
|
|
1137
|
+
const spacingM = raw.match(/spacing\s*:\s*\{([\s\S]*?)\}/);
|
|
1138
|
+
if (spacingM) {
|
|
1139
|
+
const re = /["']?([\w.-]+)["']?\s*:\s*["']([^"']+)["']/g;
|
|
1140
|
+
let m;
|
|
1141
|
+
while ((m = re.exec(spacingM[1])) !== null) result.spacing[m[1]] = m[2];
|
|
1142
|
+
}
|
|
1143
|
+
const screensM = raw.match(/screens\s*:\s*\{([\s\S]*?)\}/);
|
|
1144
|
+
if (screensM) {
|
|
1145
|
+
const re = /["']?([\w.-]+)["']?\s*:\s*["']([^"']+)["']/g;
|
|
1146
|
+
let m;
|
|
1147
|
+
while ((m = re.exec(screensM[1])) !== null) result.breakpoints[m[1]] = m[2];
|
|
1148
|
+
}
|
|
1149
|
+
} catch (_) {}
|
|
1150
|
+
return result;
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
/** Resolve Tailwind theme for boxShadow, spacing, screens, zIndex, motion. Uses resolveConfig for .js/.mjs, text-parse for .ts. */
|
|
1004
1154
|
function getTailwindTheme() {
|
|
1005
1155
|
const empty = { shadows: {}, spacing: {}, breakpoints: {}, zIndex: {}, transitionDuration: {}, transitionTimingFunction: {}, animation: {}, colors: {} };
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
config = projectRequire(twPathMjs);
|
|
1156
|
+
const twPath = path.join(PROJECT_ROOT, "tailwind.config.js");
|
|
1157
|
+
const twPathMjs = path.join(PROJECT_ROOT, "tailwind.config.mjs");
|
|
1158
|
+
const twPathTs = path.join(PROJECT_ROOT, "tailwind.config.ts");
|
|
1159
|
+
|
|
1160
|
+
if (fs.existsSync(twPath) || fs.existsSync(twPathMjs)) {
|
|
1161
|
+
try {
|
|
1162
|
+
const resolveConfig = projectRequire("tailwindcss/resolveConfig");
|
|
1163
|
+
const pathToUse = fs.existsSync(twPath) ? twPath : twPathMjs;
|
|
1164
|
+
let config = projectRequire(pathToUse);
|
|
1016
1165
|
if (config && typeof config === "object" && config.default) config = config.default;
|
|
1166
|
+
const resolved = resolveConfig(config);
|
|
1167
|
+
const theme = resolved.theme || {};
|
|
1168
|
+
const toObj = (v) => (v && typeof v === "object" && !Array.isArray(v) ? v : {});
|
|
1169
|
+
return {
|
|
1170
|
+
shadows: toObj(theme.boxShadow),
|
|
1171
|
+
spacing: toObj(theme.spacing),
|
|
1172
|
+
breakpoints: toObj(theme.screens),
|
|
1173
|
+
zIndex: toObj(theme.zIndex),
|
|
1174
|
+
transitionDuration: toObj(theme.transitionDuration),
|
|
1175
|
+
transitionTimingFunction: toObj(theme.transitionTimingFunction),
|
|
1176
|
+
animation: toObj(theme.animation),
|
|
1177
|
+
colors: toObj(theme.colors),
|
|
1178
|
+
};
|
|
1179
|
+
} catch (_) {
|
|
1180
|
+
return empty;
|
|
1017
1181
|
}
|
|
1018
|
-
const resolved = resolveConfig(config);
|
|
1019
|
-
const theme = resolved.theme || {};
|
|
1020
|
-
const boxShadow = theme.boxShadow;
|
|
1021
|
-
const spacing = theme.spacing;
|
|
1022
|
-
const screens = theme.screens;
|
|
1023
|
-
const zIndex = theme.zIndex;
|
|
1024
|
-
const transitionDuration = theme.transitionDuration;
|
|
1025
|
-
const transitionTimingFunction = theme.transitionTimingFunction;
|
|
1026
|
-
const animation = theme.animation;
|
|
1027
|
-
const themeColors = theme.colors;
|
|
1028
|
-
const toObj = (v) => (v && typeof v === "object" && !Array.isArray(v) ? v : {});
|
|
1029
|
-
return {
|
|
1030
|
-
shadows: toObj(boxShadow),
|
|
1031
|
-
spacing: toObj(spacing),
|
|
1032
|
-
breakpoints: toObj(screens),
|
|
1033
|
-
zIndex: toObj(zIndex),
|
|
1034
|
-
transitionDuration: toObj(transitionDuration),
|
|
1035
|
-
transitionTimingFunction: toObj(transitionTimingFunction),
|
|
1036
|
-
animation: toObj(animation),
|
|
1037
|
-
colors: toObj(themeColors),
|
|
1038
|
-
};
|
|
1039
|
-
} catch (_) {
|
|
1040
|
-
return empty;
|
|
1041
1182
|
}
|
|
1183
|
+
|
|
1184
|
+
if (fs.existsSync(twPathTs)) {
|
|
1185
|
+
try {
|
|
1186
|
+
const raw = fs.readFileSync(twPathTs, "utf-8");
|
|
1187
|
+
return parseTailwindThemeFromText(raw);
|
|
1188
|
+
} catch (_) {}
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
return empty;
|
|
1042
1192
|
}
|
|
1043
1193
|
|
|
1044
1194
|
function extractFoundations() {
|
|
@@ -1364,11 +1514,10 @@ function extractButtonUsage() {
|
|
|
1364
1514
|
}
|
|
1365
1515
|
|
|
1366
1516
|
function scan() {
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1517
|
+
const relativeFiles = COMPONENTS_DIR ? getAllComponentFiles(COMPONENTS_DIR) : [];
|
|
1518
|
+
if (!COMPONENTS_DIR) {
|
|
1519
|
+
console.warn("[VDS] " + CLI_LOCALES[CLI_LOCALE].componentsNotFound);
|
|
1370
1520
|
}
|
|
1371
|
-
const relativeFiles = getAllComponentFiles(COMPONENTS_DIR);
|
|
1372
1521
|
const results = [];
|
|
1373
1522
|
for (const rel of relativeFiles) {
|
|
1374
1523
|
const fullPath = path.join(COMPONENTS_DIR, rel);
|
|
@@ -1390,7 +1539,7 @@ function scan() {
|
|
|
1390
1539
|
const tokens = extractTailwindTokens(content);
|
|
1391
1540
|
results.push({ file: rel, name, group, category, description, tokens });
|
|
1392
1541
|
}
|
|
1393
|
-
if (fs.existsSync(PAGES_DIR)) {
|
|
1542
|
+
if (PAGES_DIR && fs.existsSync(PAGES_DIR)) {
|
|
1394
1543
|
const pageFiles = getAllComponentFiles(PAGES_DIR);
|
|
1395
1544
|
for (const rel of pageFiles) {
|
|
1396
1545
|
const fullPath = path.join(PAGES_DIR, rel);
|
|
@@ -1398,7 +1547,7 @@ function scan() {
|
|
|
1398
1547
|
const name = humanizeName(rel);
|
|
1399
1548
|
const tokens = extractTailwindTokens(content);
|
|
1400
1549
|
results.push({
|
|
1401
|
-
file: "
|
|
1550
|
+
file: path.relative(PROJECT_ROOT, PAGES_DIR).replace(/\\/g, "/") + "/" + rel,
|
|
1402
1551
|
name,
|
|
1403
1552
|
group: "Pages",
|
|
1404
1553
|
category: "Pages",
|
|
@@ -1548,10 +1697,7 @@ const isCompare = process.argv.includes("--compare");
|
|
|
1548
1697
|
if (isCompare) {
|
|
1549
1698
|
runCompare();
|
|
1550
1699
|
} else if (isWatch) {
|
|
1551
|
-
|
|
1552
|
-
console.error(CLI_LOCALES[CLI_LOCALE].componentsNotFound);
|
|
1553
|
-
process.exit(1);
|
|
1554
|
-
}
|
|
1700
|
+
const watchDir = COMPONENTS_DIR || SRC_DIR;
|
|
1555
1701
|
let debounceTimer = null;
|
|
1556
1702
|
let lastChangedFiles = new Set();
|
|
1557
1703
|
|
|
@@ -1567,23 +1713,23 @@ if (isCompare) {
|
|
|
1567
1713
|
}
|
|
1568
1714
|
}
|
|
1569
1715
|
scan();
|
|
1570
|
-
console.log(
|
|
1716
|
+
console.log(`[VDS] Watch: ${path.relative(PROJECT_ROOT, watchDir)} (.tsx, .jsx). Scan complete.`);
|
|
1571
1717
|
const url = vdsDashboardUrl();
|
|
1572
1718
|
console.log("[VDS] Dashboard: " + formatClickableLink(url) + "\n");
|
|
1573
1719
|
}
|
|
1574
1720
|
|
|
1575
1721
|
function scheduleScan(filename) {
|
|
1576
1722
|
if (!filename || !/\.(tsx|jsx)$/i.test(filename)) return;
|
|
1577
|
-
lastChangedFiles.add(path.join(
|
|
1723
|
+
lastChangedFiles.add(path.join(watchDir, filename));
|
|
1578
1724
|
clearTimeout(debounceTimer);
|
|
1579
1725
|
debounceTimer = setTimeout(runScan, 300);
|
|
1580
1726
|
}
|
|
1581
1727
|
|
|
1582
|
-
console.log(
|
|
1728
|
+
console.log(`[VDS] Watching ${path.relative(PROJECT_ROOT, watchDir)} for .tsx / .jsx changes…\n`);
|
|
1583
1729
|
runScan();
|
|
1584
1730
|
|
|
1585
1731
|
try {
|
|
1586
|
-
const watcher = fs.watch(
|
|
1732
|
+
const watcher = fs.watch(watchDir, { recursive: true }, (eventType, filename) => {
|
|
1587
1733
|
if (filename) scheduleScan(filename);
|
|
1588
1734
|
});
|
|
1589
1735
|
process.on("SIGINT", () => {
|
|
@@ -17,6 +17,25 @@ const PROJECT_ROOT = path.join(__dirname, "..");
|
|
|
17
17
|
const SRC_DIR = path.join(PROJECT_ROOT, "src");
|
|
18
18
|
const STORYBOOK_DIR = path.join(PROJECT_ROOT, ".storybook");
|
|
19
19
|
|
|
20
|
+
// ── vds.config.js loader ──────────────────────────────────────────────────────
|
|
21
|
+
function loadVdsConfig() {
|
|
22
|
+
const configPath = path.join(PROJECT_ROOT, "vds.config.js");
|
|
23
|
+
if (!fs.existsSync(configPath)) return {};
|
|
24
|
+
try {
|
|
25
|
+
const raw = fs.readFileSync(configPath, "utf-8");
|
|
26
|
+
const mod = { exports: {} };
|
|
27
|
+
// eslint-disable-next-line no-new-func
|
|
28
|
+
new Function("module", "exports", "require", raw)(mod, mod.exports, () => ({}));
|
|
29
|
+
const cfg = mod.exports.default ?? mod.exports;
|
|
30
|
+
return typeof cfg === "object" && cfg !== null ? cfg : {};
|
|
31
|
+
} catch (e) {
|
|
32
|
+
console.warn("[VDS] vds.config.js yüklenemedi:", e.message);
|
|
33
|
+
return {};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const VDS_CONFIG = loadVdsConfig();
|
|
38
|
+
|
|
20
39
|
const HOOK_TO_PROVIDER = {
|
|
21
40
|
useTimer: "TimerProvider",
|
|
22
41
|
useCircles: "CirclesProvider",
|
|
@@ -25,8 +44,42 @@ const HOOK_TO_PROVIDER = {
|
|
|
25
44
|
useSearch: "SearchProvider",
|
|
26
45
|
useCms: "CmsProvider",
|
|
27
46
|
useContent: "ContentProvider",
|
|
47
|
+
...(VDS_CONFIG.hookProviders ?? {}),
|
|
28
48
|
};
|
|
29
49
|
|
|
50
|
+
const STANDARD_HOOKS = new Set([
|
|
51
|
+
"useState", "useEffect", "useCallback", "useMemo", "useRef", "useContext",
|
|
52
|
+
"useReducer", "useLayoutEffect", "useImperativeHandle", "useDebugValue",
|
|
53
|
+
"useDeferredValue", "useId", "useInsertionEffect", "useSyncExternalStore",
|
|
54
|
+
"useTransition", "useOptimistic", "useFormState", "useFormStatus", "useActionState",
|
|
55
|
+
"useNavigate", "useParams", "useLocation", "useMatch", "useSearchParams",
|
|
56
|
+
"useRouteError", "useLoaderData", "useActionData", "useSubmit", "useFetcher",
|
|
57
|
+
"useNavigation", "useRevalidator", "useRouteLoaderData", "useMatches", "useHref",
|
|
58
|
+
"useOutlet", "useOutletContext", "useResolvedPath", "useBlocker",
|
|
59
|
+
"useQuery", "useMutation", "useInfiniteQuery", "useQueryClient",
|
|
60
|
+
"useSuspenseQuery", "useSuspenseInfiniteQuery", "usePrefetchQuery",
|
|
61
|
+
"useIsFetching", "useIsMutating",
|
|
62
|
+
"useForm", "useFormContext", "useController", "useWatch", "useFieldArray",
|
|
63
|
+
"useStore", "useShallow",
|
|
64
|
+
"useSelector", "useDispatch",
|
|
65
|
+
"useTranslation", "useI18n", "useLocale",
|
|
66
|
+
"useArgs",
|
|
67
|
+
"useMotionValue", "useAnimate", "useAnimation", "useSpring", "useScroll",
|
|
68
|
+
"useInView", "useReducedMotion", "useWillChange", "useVelocity",
|
|
69
|
+
"useMediaQuery", "useBreakpoint", "useTheme",
|
|
70
|
+
"useToast", "useDisclosure", "useBoolean",
|
|
71
|
+
"useDraggable", "useDroppable", "useSortable",
|
|
72
|
+
"useVirtualizer", "useWindowVirtualizer",
|
|
73
|
+
"useHotkeys", "useEventListener", "useOnClickOutside",
|
|
74
|
+
"useLocalStorage", "useSessionStorage", "useCookies",
|
|
75
|
+
"useDebounce", "useThrottle", "useInterval", "useTimeout",
|
|
76
|
+
"useWindowSize", "useResizeObserver", "useIntersectionObserver",
|
|
77
|
+
"useCopyToClipboard", "useClipboard",
|
|
78
|
+
"useSWR", "useSWRMutation",
|
|
79
|
+
"useCombobox", "useSelect", "useMultipleSelection",
|
|
80
|
+
"useFloating", "useInteractions", "useMergeRefs",
|
|
81
|
+
]);
|
|
82
|
+
|
|
30
83
|
const IGNORE_DIRS = new Set(["node_modules", "dist", ".next", "build", ".storybook", "stories"]);
|
|
31
84
|
|
|
32
85
|
function getAllSourceFiles(dir, baseDir = dir) {
|
|
@@ -48,9 +101,19 @@ function getAllSourceFiles(dir, baseDir = dir) {
|
|
|
48
101
|
|
|
49
102
|
function detectHooksInFile(content) {
|
|
50
103
|
const found = new Set();
|
|
104
|
+
// 1. Always check known/configured hook→provider map
|
|
51
105
|
for (const hook of Object.keys(HOOK_TO_PROVIDER)) {
|
|
52
106
|
if (new RegExp("\\b" + hook + "\\b").test(content)) found.add(hook);
|
|
53
107
|
}
|
|
108
|
+
// 2. Auto-detect any custom hook call (use[A-Z]\w+) not in the standard exclusion list.
|
|
109
|
+
const callRe = /\buse([A-Z]\w*)\s*[(<]/g;
|
|
110
|
+
let m;
|
|
111
|
+
while ((m = callRe.exec(content)) !== null) {
|
|
112
|
+
const hookName = "use" + m[1];
|
|
113
|
+
if (!STANDARD_HOOKS.has(hookName) && !found.has(hookName)) {
|
|
114
|
+
found.add(hookName);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
54
117
|
return found;
|
|
55
118
|
}
|
|
56
119
|
|
|
@@ -25,8 +25,27 @@ let FOUNDATIONS_DATA = null;
|
|
|
25
25
|
|
|
26
26
|
// CSS is loaded from .storybook/preview.tsx — never add CSS import to story files.
|
|
27
27
|
|
|
28
|
+
// --- vds.config.js loader ---
|
|
29
|
+
function loadVdsConfig() {
|
|
30
|
+
const configPath = path.join(PROJECT_ROOT, "vds.config.js");
|
|
31
|
+
if (!fs.existsSync(configPath)) return {};
|
|
32
|
+
try {
|
|
33
|
+
const raw = fs.readFileSync(configPath, "utf-8");
|
|
34
|
+
const mod = { exports: {} };
|
|
35
|
+
// eslint-disable-next-line no-new-func
|
|
36
|
+
new Function("module", "exports", "require", raw)(mod, mod.exports, () => ({}));
|
|
37
|
+
const cfg = mod.exports.default ?? mod.exports;
|
|
38
|
+
return typeof cfg === "object" && cfg !== null ? cfg : {};
|
|
39
|
+
} catch (e) {
|
|
40
|
+
console.warn("[VDS] vds.config.js yüklenemedi:", e.message);
|
|
41
|
+
return {};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const VDS_CONFIG = loadVdsConfig();
|
|
46
|
+
|
|
28
47
|
// Components we don't want to auto-generate stories for (project-specific dashboards, heavy UIs, etc.)
|
|
29
|
-
const
|
|
48
|
+
const DEFAULT_SKIP_LIST = [
|
|
30
49
|
"AnalysisDashboard",
|
|
31
50
|
"ComponentLibrary",
|
|
32
51
|
"EnterprisePushPanel",
|
|
@@ -54,6 +73,12 @@ const SKIP_LIST = [
|
|
|
54
73
|
"Button",
|
|
55
74
|
];
|
|
56
75
|
|
|
76
|
+
// Merge default with project-specific overrides from vds.config.js
|
|
77
|
+
// vds.config.js can set: skipList (array, replaces default) or extraSkipList (array, appends)
|
|
78
|
+
const SKIP_LIST = VDS_CONFIG.skipList
|
|
79
|
+
? [...VDS_CONFIG.skipList]
|
|
80
|
+
: [...DEFAULT_SKIP_LIST, ...(VDS_CONFIG.extraSkipList ?? [])];
|
|
81
|
+
|
|
57
82
|
/** shadcn/ui composite component recipes: component name → imports + render. */
|
|
58
83
|
const RECIPES = {
|
|
59
84
|
Accordion: {
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { watch } from 'fs';
|
|
2
|
+
import { watch, existsSync } from 'fs';
|
|
3
3
|
import { execSync } from 'child_process';
|
|
4
4
|
import { resolve } from 'path';
|
|
5
5
|
|
|
6
6
|
const ROOT = resolve(process.cwd());
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
// Watch the first directory that exists: src/, app/, or project root
|
|
9
|
+
function resolveWatchDir() {
|
|
10
|
+
for (const candidate of ['src', 'app']) {
|
|
11
|
+
const full = resolve(ROOT, candidate);
|
|
12
|
+
if (existsSync(full)) return full;
|
|
13
|
+
}
|
|
14
|
+
return ROOT;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const WATCH_DIR = resolveWatchDir();
|
|
8
18
|
let timeout = null;
|
|
9
19
|
|
|
10
20
|
function run() {
|
|
@@ -14,7 +24,9 @@ function run() {
|
|
|
14
24
|
execSync('node vds-core/scan.mjs', { stdio: 'inherit', cwd: ROOT });
|
|
15
25
|
execSync('node vds-core/story-generator.mjs', { stdio: 'inherit', cwd: ROOT });
|
|
16
26
|
execSync('node vds-core/setup-storybook-providers.mjs', { stdio: 'inherit', cwd: ROOT });
|
|
17
|
-
|
|
27
|
+
if (existsSync(resolve(ROOT, 'vds-core/storybook-adapt.mjs'))) {
|
|
28
|
+
execSync('node vds-core/storybook-adapt.mjs', { stdio: 'inherit', cwd: ROOT });
|
|
29
|
+
}
|
|
18
30
|
console.log(`[VDS ${ts}] ✅ Design system güncellendi.`);
|
|
19
31
|
} catch (e) {
|
|
20
32
|
console.error(`[VDS ${ts}] ❌ Hata:`, e.message);
|
|
@@ -25,7 +37,7 @@ function onFileChange(eventType, filename) {
|
|
|
25
37
|
if (!filename) return;
|
|
26
38
|
if (filename.includes('stories/')) return;
|
|
27
39
|
if (filename.includes('vds-output')) return;
|
|
28
|
-
if (!filename.endsWith('.tsx') && !filename.endsWith('.ts') &&
|
|
40
|
+
if (!filename.endsWith('.tsx') && !filename.endsWith('.ts') &&
|
|
29
41
|
!filename.endsWith('.css') && !filename.endsWith('.js')) return;
|
|
30
42
|
console.log(`[VDS] Değişiklik: ${filename}`);
|
|
31
43
|
clearTimeout(timeout);
|
|
@@ -33,6 +45,6 @@ function onFileChange(eventType, filename) {
|
|
|
33
45
|
}
|
|
34
46
|
|
|
35
47
|
run();
|
|
36
|
-
console.log(
|
|
48
|
+
console.log(`[VDS] 👀 ${WATCH_DIR.replace(ROOT + '/', '')}/ klasörü izleniyor...`);
|
|
37
49
|
console.log('[VDS] Durdurmak için Ctrl+C\n');
|
|
38
|
-
watch(
|
|
50
|
+
watch(WATCH_DIR, { recursive: true }, onFileChange);
|