@turk.net/mui 3.0.7 → 3.0.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@turk.net/mui",
3
- "version": "3.0.7",
3
+ "version": "3.0.9",
4
4
  "description": "A theme library customized for Material UI (v9).",
5
5
  "keywords": [
6
6
  "mui",
@@ -116,14 +116,31 @@
116
116
  "./LLM_GUIDELINES.md": "./LLM_GUIDELINES.md",
117
117
  "./LLM_EXAMPLES.md": "./LLM_EXAMPLES.md",
118
118
  "./SKILL_SETUP.md": "./SKILL_SETUP.md",
119
- "./skills/*": "./.opencode/skills/*"
119
+ "./DESIGN_RULES.md": "./DESIGN_RULES.md",
120
+ "./COMPONENT_MAP.md": "./COMPONENT_MAP.md",
121
+ "./COMPONENT_GLOSSARY.md": "./COMPONENT_GLOSSARY.md",
122
+ "./DESIGN_SOURCES.md": "./DESIGN_SOURCES.md",
123
+ "./DESIGN_TOKENS_MAP.md": "./DESIGN_TOKENS_MAP.md",
124
+ "./PAGE_PATTERNS.md": "./PAGE_PATTERNS.md",
125
+ "./ESLINT_DESIGN_RULES.md": "./ESLINT_DESIGN_RULES.md",
126
+ "./skills/*": "./.opencode/skills/*",
127
+ "./skills/design-verification": "./.opencode/skills/onehub-design-verification/SKILL.md"
120
128
  },
121
129
  "files": [
122
130
  "dist",
123
131
  "LLM_GUIDELINES.md",
124
132
  "LLM_EXAMPLES.md",
125
133
  "SKILL_SETUP.md",
126
- ".opencode/skills"
134
+ "DESIGN_RULES.md",
135
+ "COMPONENT_MAP.md",
136
+ "COMPONENT_GLOSSARY.md",
137
+ "DESIGN_SOURCES.md",
138
+ "DESIGN_TOKENS_MAP.md",
139
+ "PAGE_PATTERNS.md",
140
+ "ESLINT_DESIGN_RULES.md",
141
+ "scripts/verify-design.js",
142
+ ".opencode/skills",
143
+ ".opencode/rules"
127
144
  ],
128
145
  "sideEffects": [
129
146
  "**/*.css",
@@ -0,0 +1,351 @@
1
+ #!/usr/bin/env node
2
+
3
+ // OneHub Design Verification Script
4
+ // Tarama: .tsx dosyalarını tarar, tasarım kurallarına aykırılıkları raporlar.
5
+ // Çıkış kodu: İhlal varsa 1, yoksa 0 (CI uyumlu).
6
+ //
7
+ // VALID_VARIANTS ve VALID_COLORS değerleri doğrudan tema paketinin
8
+ // component-augmentations.ts dosyasındaki type override'larından alınır.
9
+ // SADECE augmentation'da true/false ile belirtilen değerler kontrol edilir.
10
+
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+
14
+ // ============================================================
15
+ // Tanımlar — Tema component-augmentations.ts baz alınarak
16
+ // ============================================================
17
+
18
+ // Variant kontrolü: augmentation'da variant değerleri true/false ile
19
+ // kısıtlanmış bileşenler
20
+ const VALID_VARIANTS = {
21
+ Button: ['contained', 'outlined', 'text'],
22
+ ButtonGroup: ['contained', 'outlined', 'text'],
23
+ Chip: ['filled', 'tint', 'outline', 'ghost'],
24
+ };
25
+
26
+ // Color kontrolü: augmentation'da color değerleri true/false ile
27
+ // kısıtlanmış bileşenler (false = kullanılamaz)
28
+ const VALID_COLORS = {
29
+ Button: ['primary', 'secondary', 'error'],
30
+ ButtonGroup: ['primary', 'secondary', 'error'],
31
+ TextField: ['neutral', 'brand', 'danger'],
32
+ Chip: ['informative', 'brand', 'danger', 'success', 'severewarning', 'important', 'warning'],
33
+ Badge: ['default', 'brand', 'success', 'danger', 'severe-warning', 'warning', 'important', 'white'],
34
+ };
35
+
36
+ // Checkbox için augmentation'da size.large = false
37
+ // Radio ve Switch için augmentation'da color/size kısıtlaması yok
38
+
39
+ // Yasaklı typography variant'ları (MUI default)
40
+ // Augmentation'da h1-h6, body1, body2, subtitle1, subtitle2, caption, button = false
41
+ const FORBIDDEN_TYPOGRAPHY_VARIANTS = [
42
+ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
43
+ 'body1', 'body2',
44
+ 'subtitle1', 'subtitle2',
45
+ 'caption',
46
+ 'button',
47
+ 'overline',
48
+ ];
49
+
50
+ // ============================================================
51
+ // Yardımcı fonksiyonlar
52
+ // ============================================================
53
+
54
+ /**
55
+ * Bir dizindeki tüm .tsx dosyalarını recursive olarak bulur
56
+ */
57
+ function findTsxFiles(dir) {
58
+ const results = [];
59
+ if (!fs.existsSync(dir)) {
60
+ console.error(`Hata: Dizin bulunamadı: ${dir}`);
61
+ process.exit(2);
62
+ }
63
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
64
+ for (const entry of entries) {
65
+ // node_modules ve .next dizinlerini atla
66
+ if (entry.name === 'node_modules' || entry.name === '.next' || entry.name === '.git') {
67
+ continue;
68
+ }
69
+ const fullPath = path.join(dir, entry.name);
70
+ if (entry.isDirectory()) {
71
+ results.push(...findTsxFiles(fullPath));
72
+ } else if (entry.name.endsWith('.tsx') || entry.name.endsWith('.jsx')) {
73
+ results.push(fullPath);
74
+ }
75
+ }
76
+ return results;
77
+ }
78
+
79
+ /**
80
+ * Dosyadaki bir satırın ihlal raporu
81
+ */
82
+ function violation(filePath, lineNumber, rule, message) {
83
+ return { file: filePath, line: lineNumber, rule, message };
84
+ }
85
+
86
+ // ============================================================
87
+ // Denetim (check) fonksiyonları
88
+ // ============================================================
89
+
90
+ /**
91
+ * Check 1: Geçersiz variant prop'ları
92
+ * Tema augmentation'ında kısıtlanmış variant değerleri kontrol edilir
93
+ */
94
+ function checkInvalidVariants(filePath, lines) {
95
+ const violations = [];
96
+ const variantRegex = /<(\w+)[^>]*?\bvariant\s*=\s*["'{]([^"'}]+)["'}][^>]*?\/?>/gi;
97
+
98
+ for (let i = 0; i < lines.length; i++) {
99
+ const line = lines[i];
100
+ let match;
101
+ while ((match = variantRegex.exec(line)) !== null) {
102
+ const componentName = match[1];
103
+ const variantValue = match[2];
104
+ const valid = VALID_VARIANTS[componentName];
105
+ if (valid && !valid.includes(variantValue)) {
106
+ violations.push(violation(
107
+ filePath, i + 1, 'invalid-variant',
108
+ `<${componentName}> bileşeninde geçersiz variant="${variantValue}". Geçerli: [${valid.join(', ')}]`
109
+ ));
110
+ }
111
+ }
112
+ }
113
+ return violations;
114
+ }
115
+
116
+ /**
117
+ * Check 2: Geçersiz color prop'ları
118
+ * Tema augmentation'ında kısıtlanmış color değerleri kontrol edilir
119
+ */
120
+ function checkInvalidColors(filePath, lines) {
121
+ const violations = [];
122
+ const colorRegex = /<(\w+)[^>]*?\bcolor\s*=\s*["'{]([^"'}]+)["'}][^>]*?\/?>/gi;
123
+
124
+ for (let i = 0; i < lines.length; i++) {
125
+ const line = lines[i];
126
+ let match;
127
+ while ((match = colorRegex.exec(line)) !== null) {
128
+ const componentName = match[1];
129
+ const colorValue = match[2];
130
+ const valid = VALID_COLORS[componentName];
131
+ if (valid && !valid.includes(colorValue)) {
132
+ violations.push(violation(
133
+ filePath, i + 1, 'invalid-color',
134
+ `<${componentName}> bileşeninde geçersiz color="${colorValue}". Geçerli: [${valid.join(', ')}]`
135
+ ));
136
+ }
137
+ }
138
+ }
139
+ return violations;
140
+ }
141
+
142
+ /**
143
+ * Check 3: Yasaklı typography variant'ları
144
+ * Tema augmentation'ında false olan MUI default variant'lar
145
+ */
146
+ function checkForbiddenTypographyVariants(filePath, lines) {
147
+ const violations = [];
148
+ const variantRegex = /<Typography[^>]*?\bvariant\s*=\s*["'{]([^"'}]+)["'}]/gi;
149
+
150
+ for (let i = 0; i < lines.length; i++) {
151
+ const line = lines[i];
152
+ let match;
153
+ while ((match = variantRegex.exec(line)) !== null) {
154
+ const variantValue = match[1];
155
+ if (FORBIDDEN_TYPOGRAPHY_VARIANTS.includes(variantValue)) {
156
+ violations.push(violation(
157
+ filePath, i + 1, 'forbidden-typography-variant',
158
+ `<Typography variant="${variantValue}"> kullanılmış. MUI default typography variant'ları DEVRE DIŞI. Untitled UI variant'larını (display.*, text.*) kullanın.`
159
+ ));
160
+ }
161
+ }
162
+ }
163
+ return violations;
164
+ }
165
+
166
+ /**
167
+ * Check 4: Inline style={{}} kullanımı
168
+ */
169
+ function checkInlineStyles(filePath, lines) {
170
+ const violations = [];
171
+ const inlineStyleRegex = /\bstyle\s*=\s*\{\{/;
172
+
173
+ for (let i = 0; i < lines.length; i++) {
174
+ if (inlineStyleRegex.test(lines[i])) {
175
+ violations.push(violation(
176
+ filePath, i + 1, 'inline-style',
177
+ 'Inline style={{}} kullanımı yasak. Bunun yerine sx prop veya styled() kullanın.'
178
+ ));
179
+ }
180
+ }
181
+ return violations;
182
+ }
183
+
184
+ /**
185
+ * Check 5: Magic number spacing — sx içinde raw px değerleri
186
+ * padding: '8px', margin: '16px' gibi
187
+ */
188
+ function checkMagicNumberSpacing(filePath, lines) {
189
+ const violations = [];
190
+
191
+ for (let i = 0; i < lines.length; i++) {
192
+ const line = lines[i];
193
+
194
+ // String px değerleri: '8px', "8px"
195
+ const pxStringRegex = /(?:padding|margin|gap|top|bottom|left|right|px|py|pt|pb|pl|pr|mx|my|mt|mb|ml|mr|spacing)\s*:\s*['"](\d+)px['"]/gi;
196
+ let match;
197
+ while ((match = pxStringRegex.exec(line)) !== null) {
198
+ const pxValue = match[1];
199
+ if (!/theme\.spacing/.test(line)) {
200
+ violations.push(violation(
201
+ filePath, i + 1, 'magic-number-spacing',
202
+ `Raw piksel değeri kullanılmış: '${pxValue}px'. theme.spacing(${Math.round(pxValue / 4)}) kullanın (${pxValue}px = theme.spacing(${Math.round(pxValue / 4)})).`
203
+ ));
204
+ }
205
+ }
206
+ }
207
+ return violations;
208
+ }
209
+
210
+ /**
211
+ * Check 6: Raw hex renk değerleri
212
+ * sx={{ color: '#242424' }} gibi. CSS var() / theme.vars kullanmayanlar.
213
+ * TEMA DOSYALARI HARİÇ (src/theme/ altındakiler kontrol edilmez —
214
+ * tema override dosyalarında hex kullanımı normaldir).
215
+ */
216
+ function checkRawHexColors(filePath, lines) {
217
+ const violations = [];
218
+
219
+ // Tema dosyalarını atla — override tanımlarında hex normaldir
220
+ if (filePath.includes('/theme/') || filePath.includes('\\theme\\')) {
221
+ return violations;
222
+ }
223
+
224
+ const hexRegex = /['"](#[0-9a-fA-F]{3,8})['"]/g;
225
+
226
+ for (let i = 0; i < lines.length; i++) {
227
+ const line = lines[i];
228
+ let match;
229
+ while ((match = hexRegex.exec(line)) !== null) {
230
+ const hexValue = match[1];
231
+ // theme.vars veya CSS var() kullanıyorsa atla
232
+ if (!/theme\.vars/.test(line) && !/var\(--/.test(line)) {
233
+ violations.push(violation(
234
+ filePath, i + 1, 'raw-hex-color',
235
+ `Raw hex rengi kullanılmış: ${hexValue}. CSS custom property veya theme.vars.palette.* kullanın.`
236
+ ));
237
+ }
238
+ }
239
+ }
240
+ return violations;
241
+ }
242
+
243
+ /**
244
+ * Check 7: Checkbox için size="large" kullanımı
245
+ * Augmentation'da CheckboxPropsSizeOverrides.large = false
246
+ */
247
+ function checkInvalidSizes(filePath, lines) {
248
+ const violations = [];
249
+ const sizeCheckRegex = /<(Checkbox)[^>]*?\bsize\s*=\s*["'{](large)["'}]/gi;
250
+
251
+ for (let i = 0; i < lines.length; i++) {
252
+ const line = lines[i];
253
+ let match;
254
+ while ((match = sizeCheckRegex.exec(line)) !== null) {
255
+ violations.push(violation(
256
+ filePath, i + 1, 'invalid-size',
257
+ `<${match[1]}> bileşeninde size="large" kullanımı DEVRE DIŞI (augmentation: large=false). Sadece "small" ve "medium" geçerlidir.`
258
+ ));
259
+ }
260
+ }
261
+ return violations;
262
+ }
263
+
264
+ // ============================================================
265
+ // Ana tarama fonksiyonu
266
+ // ============================================================
267
+
268
+ function scanFile(filePath) {
269
+ const content = fs.readFileSync(filePath, 'utf-8');
270
+ const lines = content.split('\n');
271
+
272
+ return [
273
+ ...checkInvalidVariants(filePath, lines),
274
+ ...checkInvalidColors(filePath, lines),
275
+ ...checkForbiddenTypographyVariants(filePath, lines),
276
+ ...checkInlineStyles(filePath, lines),
277
+ ...checkMagicNumberSpacing(filePath, lines),
278
+ ...checkRawHexColors(filePath, lines),
279
+ ...checkInvalidSizes(filePath, lines),
280
+ ];
281
+ }
282
+
283
+ function main() {
284
+ const args = process.argv.slice(2);
285
+ const targetDir = args[0] || 'src/';
286
+ const absTargetDir = path.resolve(process.cwd(), targetDir);
287
+
288
+ console.log(`\n🔍 OneHub Design Verification`);
289
+ console.log(` Tarama dizini: ${absTargetDir}\n`);
290
+
291
+ const startTime = Date.now();
292
+ const files = findTsxFiles(absTargetDir);
293
+
294
+ if (files.length === 0) {
295
+ console.log(` ⚠️ Hiç .tsx/.jsx dosyası bulunamadı: ${absTargetDir}`);
296
+ console.log(` ✅ İhlal yok.\n`);
297
+ process.exit(0);
298
+ }
299
+
300
+ console.log(` ${files.length} dosya taranıyor...\n`);
301
+
302
+ let allViolations = [];
303
+ for (const file of files) {
304
+ allViolations = allViolations.concat(scanFile(file));
305
+ }
306
+
307
+ const ruleCounts = {};
308
+ for (const v of allViolations) {
309
+ ruleCounts[v.rule] = (ruleCounts[v.rule] || 0) + 1;
310
+ }
311
+
312
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
313
+
314
+ if (allViolations.length === 0) {
315
+ console.log(` ✅ Tasarım ihlali bulunamadı! (${files.length} dosya, ${elapsed}s)\n`);
316
+ process.exit(0);
317
+ }
318
+
319
+ console.log(` ❌ ${allViolations.length} ihlal bulundu (${files.length} dosya, ${elapsed}s):\n`);
320
+ console.log(` Kategori Dağılımı:`);
321
+ for (const [rule, count] of Object.entries(ruleCounts).sort()) {
322
+ console.log(` ${rule}: ${count}`);
323
+ }
324
+ console.log();
325
+
326
+ console.log(` İhlal Detayları:`);
327
+ console.log(`${'─'.repeat(80)}`);
328
+
329
+ const byFile = {};
330
+ for (const v of allViolations) {
331
+ if (!byFile[v.file]) byFile[v.file] = [];
332
+ byFile[v.file].push(v);
333
+ }
334
+
335
+ for (const [file, fileViolations] of Object.entries(byFile).sort()) {
336
+ const relativeFile = path.relative(process.cwd(), file);
337
+ console.log(`\n 📄 ${relativeFile}`);
338
+ for (const v of fileViolations) {
339
+ console.log(` Satır ${v.line}: [${v.rule}] ${v.message}`);
340
+ }
341
+ }
342
+
343
+ console.log(`\n${'─'.repeat(80)}`);
344
+ console.log(` Toplam: ${allViolations.length} ihlal, ${Object.keys(byFile).length} dosya`);
345
+ console.log(` Tasarım kuralları için: DESIGN_RULES.md`);
346
+ console.log(` Bileşen kullanımı için: COMPONENT_MAP.md, COMPONENT_GLOSSARY.md\n`);
347
+
348
+ process.exit(1);
349
+ }
350
+
351
+ main();