tailwind-to-style 3.2.1 → 3.2.2
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 +37 -2
- package/dist/core/tws.cjs +1 -1
- package/dist/core/tws.esm.js +1 -1
- package/dist/core/twsx.cjs +64 -9
- package/dist/core/twsx.esm.js +64 -9
- package/dist/core/twsx.esm.js.map +1 -1
- package/dist/core/twsxVariants.cjs +64 -9
- package/dist/core/twsxVariants.esm.js +64 -9
- package/dist/core/twsxVariants.esm.js.map +1 -1
- package/dist/cx.cjs +1 -1
- package/dist/cx.esm.js +1 -1
- package/dist/index.cjs +73 -9
- package/dist/index.esm.js +73 -9
- package/dist/index.esm.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/utils/index.cjs +1 -1
- package/dist/utils/index.esm.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# tailwind-to-style
|
|
2
2
|
|
|
3
3
|
[📦 View on npm](https://www.npmjs.com/package/tailwind-to-style)
|
|
4
|
+
| [🌐 Landing Page](https://bigetion.github.io/tailwind-to-style/landing.html)
|
|
5
|
+
| [🛝 Playground](https://bigetion.github.io/tailwind-to-style/sandbox.html)
|
|
4
6
|
|
|
5
7
|
[](https://www.npmjs.com/package/tailwind-to-style)
|
|
6
8
|
[](https://github.com/Bigetion/tailwind-to-style/actions)
|
|
@@ -179,6 +181,8 @@ tws('p-0.5 m-1.5 gap-2.5')
|
|
|
179
181
|
|
|
180
182
|
Generates real CSS from Tailwind classes with full selector support, SCSS-like nesting, and auto-injects a `<style>` tag into the DOM.
|
|
181
183
|
|
|
184
|
+
> **HMR-safe** — each `twsx()` call owns a stable slot in the injected style tag keyed by its top-level selectors. When you edit styles during development, the old slot is **replaced** (not appended), so changes are reflected immediately without a hard refresh.
|
|
185
|
+
|
|
182
186
|
```javascript
|
|
183
187
|
import { twsx } from 'tailwind-to-style'
|
|
184
188
|
|
|
@@ -621,10 +625,41 @@ function App() {
|
|
|
621
625
|
|
|
622
626
|
### Vue
|
|
623
627
|
|
|
628
|
+
```vue
|
|
629
|
+
<script setup>
|
|
630
|
+
import 'tailwind-to-style/preflight.css'
|
|
631
|
+
import { tws, twsx } from 'tailwind-to-style'
|
|
632
|
+
|
|
633
|
+
// twsx() at the top level of <script setup> is HMR-safe.
|
|
634
|
+
// When you edit the classes, Vite's HMR re-runs this block and
|
|
635
|
+
// the old CSS slot is replaced automatically — no hard refresh needed.
|
|
636
|
+
twsx({
|
|
637
|
+
'html': 'bg-gray-100 min-h-screen flex items-center justify-center',
|
|
638
|
+
'.card': [
|
|
639
|
+
'bg-white p-5 border border-gray-300 rounded-xl shadow-md',
|
|
640
|
+
{
|
|
641
|
+
'&:hover': 'shadow-xl',
|
|
642
|
+
'> .title': 'text-xl font-bold text-gray-900 mb-3',
|
|
643
|
+
'> .body': 'text-sm text-gray-700',
|
|
644
|
+
},
|
|
645
|
+
],
|
|
646
|
+
})
|
|
647
|
+
</script>
|
|
648
|
+
|
|
649
|
+
<template>
|
|
650
|
+
<div class="card">
|
|
651
|
+
<div class="title">Card Title</div>
|
|
652
|
+
<p class="body">Styled with twsx — hot reload works out of the box.</p>
|
|
653
|
+
</div>
|
|
654
|
+
</template>
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
For simple inline styles, use `tws()` with the reactive system:
|
|
658
|
+
|
|
624
659
|
```vue
|
|
625
660
|
<script setup>
|
|
626
661
|
import { tws } from 'tailwind-to-style'
|
|
627
|
-
const btnStyle = tws('bg-blue-500 text-white px-4 py-2 rounded-lg
|
|
662
|
+
const btnStyle = tws('bg-blue-500 text-white px-4 py-2 rounded-lg', true)
|
|
628
663
|
</script>
|
|
629
664
|
|
|
630
665
|
<template>
|
|
@@ -670,7 +705,7 @@ v3.2.0 includes major performance optimizations:
|
|
|
670
705
|
- **Pre-compiled regex** — compiled once at module load, reused for every call
|
|
671
706
|
- **Multi-level LRU caching** — class resolution, CSS generation, config lookups
|
|
672
707
|
- **Bounded caches** — Maps capped at 5,000 entries, Sets at 10,000 to prevent memory leaks
|
|
673
|
-
-
|
|
708
|
+
- **Slot-based CSS injection** — each `twsx()` call owns a named slot; updates rebuild the tag instead of appending, preventing HMR accumulation
|
|
674
709
|
- **FNV-1a hashing** — 100x faster than `JSON.stringify` for cache keys
|
|
675
710
|
|
|
676
711
|
```
|
package/dist/core/tws.cjs
CHANGED
package/dist/core/tws.esm.js
CHANGED
package/dist/core/twsx.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* tailwind-to-style v3.2.
|
|
2
|
+
* tailwind-to-style v3.2.2
|
|
3
3
|
* Runtime Tailwind CSS to inline styles converter
|
|
4
4
|
*
|
|
5
5
|
* @author Bigetion
|
|
@@ -8998,27 +8998,40 @@ function fastObjectHash(obj) {
|
|
|
8998
8998
|
*/
|
|
8999
8999
|
function twsx(obj) {
|
|
9000
9000
|
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
9001
|
-
// Create fast hash key from input (100x faster than JSON.stringify)
|
|
9001
|
+
// Create fast hash key from input content (100x faster than JSON.stringify)
|
|
9002
9002
|
const cacheKey = fastObjectHash(obj, options);
|
|
9003
|
+
const {
|
|
9004
|
+
inject = true
|
|
9005
|
+
} = options;
|
|
9006
|
+
|
|
9007
|
+
// Derive a STABLE registry key from the object's top-level selector KEYS only.
|
|
9008
|
+
// This key is independent of the CSS class values, so when styles are edited
|
|
9009
|
+
// during HMR (same selectors, different Tailwind classes) the old slot is
|
|
9010
|
+
// replaced rather than a new slot being created — preventing CSS accumulation.
|
|
9011
|
+
const registryKey = obj && typeof obj === "object" && !Array.isArray(obj) ? Object.keys(obj).sort().join("|") : cacheKey; // fallback for edge cases
|
|
9003
9012
|
|
|
9004
9013
|
// Check cache first
|
|
9005
9014
|
if (_twsxInputCache.has(cacheKey)) {
|
|
9006
9015
|
const cached = _twsxInputCache.get(cacheKey);
|
|
9007
9016
|
|
|
9008
|
-
//
|
|
9009
|
-
const {
|
|
9010
|
-
inject = true
|
|
9011
|
-
} = options;
|
|
9017
|
+
// Re-inject with registryKey so the slot stays registered (no-op when unchanged).
|
|
9012
9018
|
if (inject && (IS_BROWSER || _ssrCollecting)) {
|
|
9013
|
-
autoInjectCss(cached);
|
|
9019
|
+
autoInjectCss(cached, registryKey);
|
|
9014
9020
|
}
|
|
9015
9021
|
return cached;
|
|
9016
9022
|
}
|
|
9017
9023
|
|
|
9018
|
-
// Cache miss:
|
|
9019
|
-
|
|
9024
|
+
// Cache miss: generate CSS without internal auto-injection so that twsx owns
|
|
9025
|
+
// the injection and can pass the selector-based registryKey for slot replacement.
|
|
9026
|
+
const result = twsxNoCache(obj, {
|
|
9027
|
+
...options,
|
|
9028
|
+
inject: false
|
|
9029
|
+
});
|
|
9020
9030
|
_twsxInputCache.set(cacheKey, result);
|
|
9021
9031
|
evictMap(_twsxInputCache);
|
|
9032
|
+
if (inject && (IS_BROWSER || _ssrCollecting)) {
|
|
9033
|
+
autoInjectCss(result, registryKey);
|
|
9034
|
+
}
|
|
9022
9035
|
return result;
|
|
9023
9036
|
}
|
|
9024
9037
|
|
|
@@ -9038,12 +9051,54 @@ function getCssHash(str) {
|
|
|
9038
9051
|
|
|
9039
9052
|
// Enhanced auto-inject CSS with performance monitoring & SSR support
|
|
9040
9053
|
const injectedCssHashSet = new Set();
|
|
9054
|
+
|
|
9055
|
+
// Registry of sourceKey → cssBlock for smart slot-based replacement.
|
|
9056
|
+
// Prevents stale CSS chunks from accumulating across HMR cycles.
|
|
9057
|
+
const _cssBlockRegistry = new Map();
|
|
9058
|
+
|
|
9059
|
+
/**
|
|
9060
|
+
* Rebuild the single twsx style tag from the full CSS block registry.
|
|
9061
|
+
* Called whenever a block is added or updated.
|
|
9062
|
+
*/
|
|
9063
|
+
function rebuildStyleTag() {
|
|
9064
|
+
let styleTag = document.getElementById("twsx-auto-style");
|
|
9065
|
+
if (!styleTag) {
|
|
9066
|
+
styleTag = document.createElement("style");
|
|
9067
|
+
styleTag.id = "twsx-auto-style";
|
|
9068
|
+
styleTag.setAttribute("data-twsx", "");
|
|
9069
|
+
document.head.appendChild(styleTag);
|
|
9070
|
+
}
|
|
9071
|
+
styleTag.textContent = [..._cssBlockRegistry.values()].join("\n");
|
|
9072
|
+
}
|
|
9041
9073
|
function autoInjectCss(cssString) {
|
|
9074
|
+
let sourceKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
|
|
9042
9075
|
const marker = performanceMonitor.start("css:inject");
|
|
9043
9076
|
try {
|
|
9044
9077
|
// SSR mode: collect CSS strings instead of DOM injection
|
|
9045
9078
|
if (_ssrCollecting) ;
|
|
9046
9079
|
if (IS_BROWSER) {
|
|
9080
|
+
if (sourceKey) {
|
|
9081
|
+
// Slot-based update: each unique twsx(obj) call owns its own CSS block.
|
|
9082
|
+
// When styles change (new content, same logical call site via cacheKey)
|
|
9083
|
+
// the old slot is replaced and the style tag is fully rebuilt, so no
|
|
9084
|
+
// stale rules from previous HMR cycles can pile up.
|
|
9085
|
+
const existing = _cssBlockRegistry.get(sourceKey);
|
|
9086
|
+
if (existing === cssString) {
|
|
9087
|
+
// Identical content – nothing to do.
|
|
9088
|
+
performanceMonitor.end(marker);
|
|
9089
|
+
return;
|
|
9090
|
+
}
|
|
9091
|
+
_cssBlockRegistry.set(sourceKey, cssString);
|
|
9092
|
+
rebuildStyleTag();
|
|
9093
|
+
if (_cssBlockRegistry.size % 10 === 0) {
|
|
9094
|
+
logger.debug(`CSS registry stats: ${_cssBlockRegistry.size} blocks registered`);
|
|
9095
|
+
}
|
|
9096
|
+
performanceMonitor.end(marker);
|
|
9097
|
+
return;
|
|
9098
|
+
}
|
|
9099
|
+
|
|
9100
|
+
// Fallback path (e.g. direct autoInjectCss calls without a sourceKey):
|
|
9101
|
+
// keep the original hash-based dedup + append behaviour.
|
|
9047
9102
|
const cssHash = getCssHash(cssString);
|
|
9048
9103
|
if (injectedCssHashSet.has(cssHash)) {
|
|
9049
9104
|
performanceMonitor.end(marker);
|
package/dist/core/twsx.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* tailwind-to-style v3.2.
|
|
2
|
+
* tailwind-to-style v3.2.2
|
|
3
3
|
* Runtime Tailwind CSS to inline styles converter
|
|
4
4
|
*
|
|
5
5
|
* @author Bigetion
|
|
@@ -8996,27 +8996,40 @@ function fastObjectHash(obj) {
|
|
|
8996
8996
|
*/
|
|
8997
8997
|
function twsx(obj) {
|
|
8998
8998
|
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
8999
|
-
// Create fast hash key from input (100x faster than JSON.stringify)
|
|
8999
|
+
// Create fast hash key from input content (100x faster than JSON.stringify)
|
|
9000
9000
|
const cacheKey = fastObjectHash(obj, options);
|
|
9001
|
+
const {
|
|
9002
|
+
inject = true
|
|
9003
|
+
} = options;
|
|
9004
|
+
|
|
9005
|
+
// Derive a STABLE registry key from the object's top-level selector KEYS only.
|
|
9006
|
+
// This key is independent of the CSS class values, so when styles are edited
|
|
9007
|
+
// during HMR (same selectors, different Tailwind classes) the old slot is
|
|
9008
|
+
// replaced rather than a new slot being created — preventing CSS accumulation.
|
|
9009
|
+
const registryKey = obj && typeof obj === "object" && !Array.isArray(obj) ? Object.keys(obj).sort().join("|") : cacheKey; // fallback for edge cases
|
|
9001
9010
|
|
|
9002
9011
|
// Check cache first
|
|
9003
9012
|
if (_twsxInputCache.has(cacheKey)) {
|
|
9004
9013
|
const cached = _twsxInputCache.get(cacheKey);
|
|
9005
9014
|
|
|
9006
|
-
//
|
|
9007
|
-
const {
|
|
9008
|
-
inject = true
|
|
9009
|
-
} = options;
|
|
9015
|
+
// Re-inject with registryKey so the slot stays registered (no-op when unchanged).
|
|
9010
9016
|
if (inject && (IS_BROWSER || _ssrCollecting)) {
|
|
9011
|
-
autoInjectCss(cached);
|
|
9017
|
+
autoInjectCss(cached, registryKey);
|
|
9012
9018
|
}
|
|
9013
9019
|
return cached;
|
|
9014
9020
|
}
|
|
9015
9021
|
|
|
9016
|
-
// Cache miss:
|
|
9017
|
-
|
|
9022
|
+
// Cache miss: generate CSS without internal auto-injection so that twsx owns
|
|
9023
|
+
// the injection and can pass the selector-based registryKey for slot replacement.
|
|
9024
|
+
const result = twsxNoCache(obj, {
|
|
9025
|
+
...options,
|
|
9026
|
+
inject: false
|
|
9027
|
+
});
|
|
9018
9028
|
_twsxInputCache.set(cacheKey, result);
|
|
9019
9029
|
evictMap(_twsxInputCache);
|
|
9030
|
+
if (inject && (IS_BROWSER || _ssrCollecting)) {
|
|
9031
|
+
autoInjectCss(result, registryKey);
|
|
9032
|
+
}
|
|
9020
9033
|
return result;
|
|
9021
9034
|
}
|
|
9022
9035
|
|
|
@@ -9036,12 +9049,54 @@ function getCssHash(str) {
|
|
|
9036
9049
|
|
|
9037
9050
|
// Enhanced auto-inject CSS with performance monitoring & SSR support
|
|
9038
9051
|
const injectedCssHashSet = new Set();
|
|
9052
|
+
|
|
9053
|
+
// Registry of sourceKey → cssBlock for smart slot-based replacement.
|
|
9054
|
+
// Prevents stale CSS chunks from accumulating across HMR cycles.
|
|
9055
|
+
const _cssBlockRegistry = new Map();
|
|
9056
|
+
|
|
9057
|
+
/**
|
|
9058
|
+
* Rebuild the single twsx style tag from the full CSS block registry.
|
|
9059
|
+
* Called whenever a block is added or updated.
|
|
9060
|
+
*/
|
|
9061
|
+
function rebuildStyleTag() {
|
|
9062
|
+
let styleTag = document.getElementById("twsx-auto-style");
|
|
9063
|
+
if (!styleTag) {
|
|
9064
|
+
styleTag = document.createElement("style");
|
|
9065
|
+
styleTag.id = "twsx-auto-style";
|
|
9066
|
+
styleTag.setAttribute("data-twsx", "");
|
|
9067
|
+
document.head.appendChild(styleTag);
|
|
9068
|
+
}
|
|
9069
|
+
styleTag.textContent = [..._cssBlockRegistry.values()].join("\n");
|
|
9070
|
+
}
|
|
9039
9071
|
function autoInjectCss(cssString) {
|
|
9072
|
+
let sourceKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
|
|
9040
9073
|
const marker = performanceMonitor.start("css:inject");
|
|
9041
9074
|
try {
|
|
9042
9075
|
// SSR mode: collect CSS strings instead of DOM injection
|
|
9043
9076
|
if (_ssrCollecting) ;
|
|
9044
9077
|
if (IS_BROWSER) {
|
|
9078
|
+
if (sourceKey) {
|
|
9079
|
+
// Slot-based update: each unique twsx(obj) call owns its own CSS block.
|
|
9080
|
+
// When styles change (new content, same logical call site via cacheKey)
|
|
9081
|
+
// the old slot is replaced and the style tag is fully rebuilt, so no
|
|
9082
|
+
// stale rules from previous HMR cycles can pile up.
|
|
9083
|
+
const existing = _cssBlockRegistry.get(sourceKey);
|
|
9084
|
+
if (existing === cssString) {
|
|
9085
|
+
// Identical content – nothing to do.
|
|
9086
|
+
performanceMonitor.end(marker);
|
|
9087
|
+
return;
|
|
9088
|
+
}
|
|
9089
|
+
_cssBlockRegistry.set(sourceKey, cssString);
|
|
9090
|
+
rebuildStyleTag();
|
|
9091
|
+
if (_cssBlockRegistry.size % 10 === 0) {
|
|
9092
|
+
logger.debug(`CSS registry stats: ${_cssBlockRegistry.size} blocks registered`);
|
|
9093
|
+
}
|
|
9094
|
+
performanceMonitor.end(marker);
|
|
9095
|
+
return;
|
|
9096
|
+
}
|
|
9097
|
+
|
|
9098
|
+
// Fallback path (e.g. direct autoInjectCss calls without a sourceKey):
|
|
9099
|
+
// keep the original hash-based dedup + append behaviour.
|
|
9045
9100
|
const cssHash = getCssHash(cssString);
|
|
9046
9101
|
if (injectedCssHashSet.has(cssHash)) {
|
|
9047
9102
|
performanceMonitor.end(marker);
|