@fwkui/x-css 1.0.22 → 1.0.24

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/README.md CHANGED
@@ -252,27 +252,22 @@ export function Button({ primary, children }) {
252
252
 
253
253
  ## Shared Instance (Khởi Tạo Một Lần)
254
254
 
255
- Nếu bạn muốn tái sử dụng cùng một instance (giữ mapping key ổn định), dùng factory:
255
+ Nếu bạn muốn tái sử dụng cùng một instance, dùng factory.
256
+ Khuyến nghị mặc định cho app chạy thật:
257
+ 1. Không đặt `prefix`.
258
+ 2. Không bật `cache`.
259
+ 3. Chỉ bật `cache` hoặc `prefix` khi bạn chủ động chấp nhận tradeoff vận hành.
256
260
 
257
261
  ```ts
258
262
  import { createSharedInstance } from '@fwkui/x-css';
259
263
 
260
- export const fx = createSharedInstance({
261
- prefix: 'fk-',
262
- cache: {
263
- styleId: 'fwkui',
264
- version: 'v1',
265
- compression: true,
266
- debounceMs: 1000,
267
- loadOnInit: true
268
- }
269
- });
264
+ export const fx = createSharedInstance();
270
265
 
271
266
  // Browser: gọi 1 lần khi app khởi động
272
267
  fx.observe(document);
273
268
 
274
269
  // Dùng ở mọi nơi
275
- const className = fx.clsx('fk-dF fk-aiC fk-jcC fk-p10px;16px');
270
+ const className = fx.clsx('dF aiC jcC p10px;16px');
276
271
 
277
272
  // Nếu dictionaryImport là true/string và cần chắc chắn CSS đã sẵn sàng:
278
273
  await fx.ready();
@@ -281,17 +276,225 @@ await fx.ready();
281
276
  const cssText = fx.getCss();
282
277
  ```
283
278
 
279
+ ## Tailwind Migration Helper
280
+
281
+ `@fwkui/x-css` có helper bán tự động để hỗ trợ migration từ chuỗi utility Tailwind sang token `x-css`.
282
+
283
+ Public API:
284
+
285
+ ```ts
286
+ import {
287
+ assessTailwindMigrationReadiness,
288
+ classifyTailwindToken,
289
+ convertTailwindClasses,
290
+ convertTailwindToken,
291
+ getTailwindCoverageMatrix
292
+ } from '@fwkui/x-css';
293
+
294
+ const result = convertTailwindClasses(
295
+ 'flex items-center justify-between gap-4 p-4 bg-white text-slate-900 rounded-lg border border-slate-200'
296
+ );
297
+
298
+ console.log(result.output);
299
+ // dF ai[center] jc[space-between] gap16px p16px bgc#ffffff c#0f172a bdra8px bdw1px bds[solid] bdcCurrentColor bdc#e2e8f0
300
+
301
+ console.log(classifyTailwindToken('md:fxd[row]'));
302
+ // 'xcss'
303
+
304
+ const readiness = assessTailwindMigrationReadiness(
305
+ 'transition flex group-hover:bg-slate-50 md:fxd[row]'
306
+ );
307
+
308
+ console.log(readiness.releaseDecision);
309
+ // 'blocked'
310
+
311
+ console.log(readiness.autoApplyOutput);
312
+ // dF md:fxd[row]
313
+ ```
314
+
315
+ Contract hiện tại:
316
+ 1. Helper ưu tiên các utility Tailwind phổ biến cho layout, spacing, color, typography, border, shadow, width/height.
317
+ 2. Responsive/state variants phổ biến như `sm:`, `md:`, `hover:`, `focus:` được chuyển sang format `x-css`.
318
+ 3. Token đầu vào được phân loại thành `tailwind`, `xcss`, `ambiguous`, hoặc `unknown`.
319
+ 4. Đây là migration helper, không phải promise “convert 100% Tailwind không cần review”.
320
+ 5. Khi publish hoặc migrate codebase thật, nên ưu tiên `mode: 'safe'` thay vì `legacy`.
321
+ 6. Helper này làm việc trên raw utility string và bỏ qua config `prefix`; khi migrate Tailwind, nên coi `prefix` là rỗng.
322
+
323
+ Ví dụ:
324
+
325
+ ```ts
326
+ const converted = convertTailwindToken('md:hover:bg-slate-50');
327
+
328
+ console.log(converted.outputs);
329
+ // ['md:bgc#f8fafc@:hover']
330
+
331
+ const safe = convertTailwindClasses('md:fxd[row] p-2.5', { mode: 'safe' });
332
+ console.log(safe.passthrough); // ['md:fxd[row]']
333
+ console.log(safe.converted); // ['p10px']
334
+ console.log(safe.ambiguous); // ['p-2.5']
335
+ ```
336
+
337
+ ### Mode
338
+
339
+ `convertTailwindToken()` và `convertTailwindClasses()` nhận option:
340
+
341
+ ```ts
342
+ type TailwindConversionMode = 'legacy' | 'safe' | 'strict'
343
+ ```
344
+
345
+ Ý nghĩa:
346
+ 1. `legacy`:
347
+ giữ hành vi tương thích cũ.
348
+ Token không convert được có thể được passthrough nếu `preserveUnknown !== false`.
349
+ 2. `safe`:
350
+ chỉ passthrough token đã được xác nhận là `x-css`.
351
+ Token `ambiguous` hoặc `unknown` sẽ không được giữ nguyên mù.
352
+ 3. `strict`:
353
+ dùng cùng `preserveUnknown: false` để ép toàn bộ token không chắc chắn đi vào `unsupported`.
354
+ Đây là mode phù hợp cho codemod, CI, hoặc review trước khi deploy.
355
+
356
+ Ví dụ khuyến nghị:
357
+
358
+ ```ts
359
+ const result = convertTailwindClasses(source, {
360
+ mode: 'safe',
361
+ preserveUnknown: false
362
+ });
363
+ ```
364
+
365
+ ### Kết Quả Trả Về
366
+
367
+ `convertTailwindToken()` trả:
368
+
369
+ ```ts
370
+ {
371
+ input: string
372
+ outputs: string[]
373
+ status: 'converted' | 'passthrough' | 'unsupported'
374
+ classification: 'tailwind' | 'xcss' | 'ambiguous' | 'unknown'
375
+ exact: boolean
376
+ warnings: Array<{ token: string; message: string }>
377
+ }
378
+ ```
379
+
380
+ `convertTailwindClasses()` trả:
381
+
382
+ ```ts
383
+ {
384
+ input: string
385
+ output: string
386
+ details: TailwindTokenConversion[]
387
+ converted: string[]
388
+ passthrough: string[]
389
+ unsupported: string[]
390
+ ambiguous: string[]
391
+ warnings: TailwindConversionWarning[]
392
+ }
393
+ ```
394
+
395
+ Ý nghĩa thực tế:
396
+ 1. `converted`: token đã map sang `x-css`.
397
+ 2. `passthrough`: token được giữ nguyên vì là `x-css` thật, hoặc vì caller vẫn bật giữ token gốc.
398
+ 3. `unsupported`: token chưa có mapping an toàn.
399
+ 4. `ambiguous`: token có hình thức dễ nhầm giữa Tailwind và `x-css`, cần review.
400
+
401
+ ### Readiness API Cho Sản Phẩm Thật
402
+
403
+ Nếu dùng framework này để migrate code chạy production, không nên dùng mỗi `convertTailwindClasses()` làm quyết định release.
404
+ Hãy chạy preflight bằng `assessTailwindMigrationReadiness()`:
405
+
406
+ ```ts
407
+ const report = assessTailwindMigrationReadiness(source);
408
+
409
+ if (report.releaseDecision === 'safe') {
410
+ // Chỉ còn exact conversion hoặc x-css thật
411
+ apply(report.autoApplyOutput);
412
+ }
413
+
414
+ if (report.releaseDecision === 'review') {
415
+ // Có output gần đúng như `transition -> tran0.2s`
416
+ review(report.approximateConverted);
417
+ }
418
+
419
+ if (report.releaseDecision === 'blocked') {
420
+ // Có utility không an toàn để tự động migrate
421
+ fail(report.blocked);
422
+ }
423
+ ```
424
+
425
+ Contract của report:
426
+ 1. `autoApplyOutput`: chỉ chứa token exact-converted và token `x-css` thật, theo đúng thứ tự gốc.
427
+ 2. `approximateConverted`: token đã convert nhưng không đủ an toàn để áp thẳng production.
428
+ 3. `reviewRequired`: tất cả token cần con người xem lại.
429
+ 4. `blocked`: token không nên auto-apply trong flow migration thật.
430
+ 5. `safeToAutoApply`: chỉ `true` khi không còn token phải review.
431
+
432
+ Coverage machine-readable:
433
+
434
+ ```ts
435
+ const matrix = getTailwindCoverageMatrix();
436
+ ```
437
+
438
+ `matrix` dùng được cho codemod, CI gate, dashboard coverage, hoặc rule riêng của team sản phẩm.
439
+
440
+ ### Canonical Output
441
+
442
+ Khi helper sinh token `x-css`, nên coi các output dưới đây là canonical:
443
+ 1. `inline-flex -> dIf`
444
+ 2. `inline-block -> dIb`
445
+ 3. `inline-grid -> dIg`
446
+ 4. `border -> bdw1px bds[solid] bdcCurrentColor`
447
+ 5. `text-transparent -> cTransparent`
448
+ 6. `border-transparent -> bdcTransparent`
449
+ 7. `bg-[currentColor] -> bgcCurrentColor`
450
+
451
+ Không nên dựa vào việc runtime parser “vẫn hiểu được” để sinh các biến thể khác như `dIF`, `dIB`, `bdctransparent`.
452
+
453
+ ### Coverage Hiện Tại
454
+
455
+ Nhóm utility đã hỗ trợ tốt:
456
+ 1. display / position / flex cơ bản
457
+ 2. spacing / size / fraction / width scale / `mx-auto` / `inset-x-*` / `inset-y-*`
458
+ 3. color cơ bản (`bg-*`, `text-*`, `border-*`)
459
+ 4. rounded / border / shadow / `appearance-none`
460
+ 5. font-size / font-weight / line-height / letter-spacing / `basis-*` / `order-*`
461
+ 6. object-fit / object-position cơ bản
462
+ 7. align / place / justify / content helpers phổ biến
463
+ 8. một phần responsive + selector variants (`sm`, `md`, `hover`, `focus`, `before`, `after`, `placeholder`, `selection`)
464
+
465
+ Nhóm nên coi là cần review thủ công hoặc fallback riêng:
466
+ 1. `group-hover:*`, `group-focus:*`, `peer-*`
467
+ 2. `dark:*`
468
+ 3. `divide-*`
469
+ 4. `duration-*`, `ease-*`, `delay-*`
470
+ 5. transform chain phức hợp như `translate-*`, `scale-*`, `rotate-*`
471
+
472
+ ### Quy Trình Dùng An Toàn
473
+
474
+ Khi migrate project thật:
475
+ 1. Chạy converter với `mode: 'safe'`.
476
+ 2. Áp dụng trực tiếp các token trong `converted`.
477
+ 3. Giữ nguyên các token trong `passthrough` chỉ khi chúng là `x-css` thật.
478
+ 4. Review bắt buộc các token trong `ambiguous` và `unsupported`.
479
+ 5. Chạy lại app, build, và kiểm tra giao diện thực tế sau mỗi đợt chuyển đổi.
480
+
481
+ Khuyến nghị:
482
+ 1. Không chạy codemod toàn repo ở `legacy mode` rồi deploy thẳng.
483
+ 2. Không dùng parser `x-css` như bằng chứng rằng token Tailwind “đã an toàn”.
484
+ 3. Nếu output chứa nhiều `ambiguous`, hãy dừng batch hiện tại và bổ sung mapping trước khi chuyển tiếp.
485
+
284
486
  Alias tương đương:
285
487
 
286
488
  ```ts
287
489
  import { createSharedClsx } from '@fwkui/x-css';
288
- const fx = createSharedClsx({ prefix: 'fk-' });
490
+ const fx = createSharedClsx();
289
491
  ```
290
492
 
291
493
  Quy tắc dùng ổn định:
292
494
  1. Tạo shared instance đúng 1 lần ở bootstrap.
293
495
  2. Không khởi tạo lại instance ở mỗi lần render component.
294
- 3. Giữ nguyên `prefix/cache` trong suốt vòng đời app để key hash ổn định.
496
+ 3. Mặc định để `prefix` rỗng `cache` tắt.
497
+ 4. Nếu bạn tự bật `cache` hoặc `prefix`, giữ nguyên cấu hình đó trong suốt vòng đời app.
295
498
 
296
499
  ## Cấu Hình
297
500
 
@@ -307,26 +510,18 @@ xcss.cssObserve(document, {
307
510
  { tablet: 'screen and (min-width: 768px)' }
308
511
  ],
309
512
  base: 'body{margin:0;font-family:system-ui,sans-serif;}',
310
- prefix: 'fk-',
311
513
  excludePrefixes: ['bs-', 'rs-'],
312
514
  excludes: ['legacy-*'],
313
- dictionaryImport: true,
314
- cache: {
315
- styleId: 'fwkui',
316
- version: 'v1',
317
- compression: true,
318
- debounceMs: 1000,
319
- loadOnInit: true
320
- }
515
+ dictionaryImport: true
321
516
  });
322
517
  ```
323
518
 
324
- Sau đó dùng class: `fk-cBrand fk-tablet:dB`.
519
+ Sau đó dùng class: `cBrand tablet:dB`.
325
520
 
326
521
  Gợi ý tối ưu bỏ qua parse:
327
- 1. Dùng `prefix` nếu bạn kiểm soát được class framework (nhanh sạch nhất).
328
- 2. Dùng `excludePrefixes` để bỏ qua nhanh theo tiền tố, ví dụ `bs-`, `rs-`.
329
- 3. Dùng `excludes` khi cần rule chính xác hoặc wildcard (`*`), dụ `legacy-*`, `tmp-debug`.
522
+ 1. Ưu tiên `excludePrefixes` để bỏ qua nhanh theo tiền tố, dụ `bs-`, `rs-`.
523
+ 2. Dùng `excludes` khi cần rule chính xác hoặc wildcard (`*`), ví dụ `legacy-*`, `tmp-debug`.
524
+ 3. Không nên lấy `prefix` làm cấu hình mặc định; chỉ dùng khi bạn thật sự cần tách namespace class.
330
525
 
331
526
  Ví dụ đầu vào cho `excludes`:
332
527
 
@@ -356,6 +551,7 @@ Lưu ý khi dùng cùng `prefix`:
356
551
  1. Engine kiểm tra `excludes`/`excludePrefixes` trước, sau đó mới kiểm tra `prefix`.
357
552
  2. Nếu token match exclude thì giữ nguyên class gốc và không parse tiếp.
358
553
  3. Nếu có `prefix: 'fk-'`, token không bắt đầu bằng `fk-` sẽ giữ nguyên class gốc.
554
+ 4. `prefix` là cấu hình tùy chọn, không phải khuyến nghị mặc định.
359
555
 
360
556
 
361
557
  `dictionaryImport`:
@@ -369,11 +565,18 @@ Lưu ý:
369
565
  3. Nếu cần chắc chắn dictionary ngoài đã sẵn sàng trước khi render quan trọng, dùng `await engine.ready`.
370
566
 
371
567
  `cache`:
372
- 1. `styleId` (mặc định `fwkui`): id thẻ `<style>` runtime.
373
- 2. `version` (mặc định `v1`): tham gia vào cache key để chủ động invalidate.
374
- 3. `compression` (mặc định `true`): nén cache trước khi lưu `localStorage`.
375
- 4. `debounceMs` (mặc định `1000`): debounce chu kỳ nén + lưu cache.
376
- 5. `sizeLast` (mặc định `1000`): seed mặc định cho bộ sinh key `D...`.
568
+ 1. Mặc định `cache` đang tắt (`loadOnInit: false`).
569
+ 2. `styleId` (mặc định `fwkui`): id thẻ `<style>` runtime.
570
+ 3. `version` (mặc định `v1`): tham gia vào cache key để chủ động invalidate.
571
+ 4. `compression` (mặc định `true`): nén cache trước khi lưu `localStorage`.
572
+ 5. `debounceMs` (mặc định `1000`): debounce chu kỳ nén + lưu cache.
573
+ 6. `sizeLast` (mặc định `1000`): seed mặc định cho bộ sinh key `D...`.
574
+
575
+ Khuyến nghị:
576
+ 1. Không bật `cache` mặc định cho mọi app.
577
+ 2. Không bật/tắt `cache` lặp lại theo route, component, hoặc session ngắn.
578
+ 3. Chỉ bật `cache` khi bạn chủ động muốn tối ưu first paint hoặc SSR/MPA hydration.
579
+ 4. Nếu đã bật `cache`, hãy cấu hình ổn định và giữ nguyên trong toàn app.
377
580
 
378
581
  Khi `compression: true`:
379
582
  1. Ưu tiên `CompressionStream` (deflate-raw + base64) nếu runtime hỗ trợ.
@@ -463,9 +666,9 @@ Quy trình thay link:
463
666
  3. Thay `dictionaryImport` bằng URL thật.
464
667
  4. Chờ `await engine.ready` trước khi render class.
465
668
 
466
- ## Bootloader Từ Cache (Khuyến nghị)
669
+ ## Bootloader Từ Cache (Tùy Chọn)
467
670
 
468
- Khuyến nghị dùng helper `getBootloaderScript` để chèn script vào `<head>` trước bundle/module, giúp giảm FOUC khi đã cache CSS.
671
+ Chỉ dùng helper `getBootloaderScript` khi bạn chủ động bật `cache` muốn lấy CSS từ `localStorage` trước khi bundle chạy.
469
672
 
470
673
  ### Dùng helper `getBootloaderScript`
471
674
 
@@ -536,10 +739,10 @@ Lưu ý:
536
739
  1. Đồng bộ `styleId` + `version` giữa bootloader và cấu hình `xcss.css(...)`.
537
740
  2. Script tạo từ `getBootloaderScript` ưu tiên `DecompressionStream` (cache deflate-raw) và fallback LZW.
538
741
  3. Sau bootloader vẫn cần gọi `xcss.cssObserve(...)` như bình thường.
539
- 4. Mặc định `loadOnInit` đang bật; muốn tắt hẳn load/save cache thì truyền `{ loadOnInit: false }`.
742
+ 4. Mặc định `loadOnInit` đang tắt; chỉ bật bằng `{ loadOnInit: true }` khi bạn thật sự dùng cache runtime.
540
743
  5. Dùng `{ compact: true }` khi muốn script trả về ở dạng nén gọn để nhúng HTML.
541
744
  6. Nếu dữ liệu cache dưới key hiện tại bị lỗi/không decode được, engine sẽ tự xóa key đó để lần chạy sau lưu lại dữ liệu mới.
542
- 7. Cache runtime chỉ khởi tạo khi browser dùng được cả `localStorage` writable `Web Locks`; thiếu một trong hai thì engine/bootloader sẽ bỏ qua load-save cache.
745
+ 7. Cache runtime chỉ khởi tạo khi browser dùng được `localStorage` writable; nếu không thì engine/bootloader sẽ bỏ qua load-save cache.
543
746
 
544
747
  ### Khi nào nên dùng `getBootloaderScript`
545
748
 
@@ -628,7 +831,7 @@ Yêu cầu:
628
831
  ## Tài Liệu Liên Quan
629
832
 
630
833
  1. Dictionary đầy đủ: [DICTIONARY.md](./DICTIONARY.md)
631
- 2. Source code: [https://github.com/dwork-dev/fwkui](https://github.com/dwork-dev/fwkui)
834
+ 2. Source code: [https://github.com/vuits24/fwkui](https://github.com/vuits24/fwkui)
632
835
 
633
836
  ## License
634
837
 
@@ -1,5 +1,5 @@
1
1
  import _default from './index.mjs';
2
- export { BootloaderScriptOptions, SharedXCSSInstance, XCSSConfig, clsx, createSharedClsx, createSharedInstance, getBootloaderScript, getCss, observe, ready, setClsxRoot } from './index.mjs';
2
+ export { BootloaderScriptOptions, SharedXCSSInstance, TAILWIND_COVERAGE_MATRIX, TailwindConversionMode, TailwindConversionOptions, TailwindConversionResult, TailwindConversionStatus, TailwindConversionWarning, TailwindCoverageEntry, TailwindCoverageSupport, TailwindMigrationReadinessOptions, TailwindMigrationReadinessReport, TailwindTokenClassification, TailwindTokenConversion, XCSSConfig, assessTailwindMigrationReadiness, classifyTailwindToken, clsx, convertTailwindClasses, convertTailwindToken, createSharedClsx, createSharedInstance, getBootloaderScript, getCss, getTailwindCoverageMatrix, observe, ready, setClsxRoot, tailwindToXcss } from './index.mjs';
3
3
 
4
4
 
5
5
 
@@ -1,5 +1,5 @@
1
1
  import _default from './index.js';
2
- export { BootloaderScriptOptions, SharedXCSSInstance, XCSSConfig, clsx, createSharedClsx, createSharedInstance, getBootloaderScript, getCss, observe, ready, setClsxRoot } from './index.js';
2
+ export { BootloaderScriptOptions, SharedXCSSInstance, TAILWIND_COVERAGE_MATRIX, TailwindConversionMode, TailwindConversionOptions, TailwindConversionResult, TailwindConversionStatus, TailwindConversionWarning, TailwindCoverageEntry, TailwindCoverageSupport, TailwindMigrationReadinessOptions, TailwindMigrationReadinessReport, TailwindTokenClassification, TailwindTokenConversion, XCSSConfig, assessTailwindMigrationReadiness, classifyTailwindToken, clsx, convertTailwindClasses, convertTailwindToken, createSharedClsx, createSharedInstance, getBootloaderScript, getCss, getTailwindCoverageMatrix, observe, ready, setClsxRoot, tailwindToXcss } from './index.js';
3
3
 
4
4
 
5
5