tailwind-to-style 2.8.4 → 2.8.6
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 +122 -14
- package/dist/index.browser.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.esm.js +1 -1
- package/lib/twsx-cli.js +149 -0
- package/package.json +6 -3
- package/plugins/vite-twsx.js +42 -14
- package/plugins/webpack-twsx.js +44 -15
- package/lib/build-twsx.js +0 -59
- package/twsx.css +0 -0
package/README.md
CHANGED
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
|
|
8
8
|
`tailwind-to-style` is a JavaScript library designed to convert Tailwind CSS utility classes into inline styles or JavaScript objects. This is especially useful when you need to dynamically apply styles to elements in frameworks like React, where inline styles or style objects are frequently used.
|
|
9
9
|
|
|
10
|
-
The library exposes two main functions:
|
|
10
|
+
The library exposes two main functions and a CLI tool:
|
|
11
11
|
|
|
12
12
|
1. **`tws`**: Converts Tailwind CSS classes into inline CSS styles or JavaScript objects (JSON).
|
|
13
13
|
2. **`twsx`**: A more advanced function that allows you to define nested and complex styles similar to SCSS, including support for responsive, state variants, and grouping.
|
|
14
|
+
3. **`twsx-cli`**: A command-line tool for generating CSS files from `twsx.*.js` files with watch mode support.
|
|
14
15
|
|
|
15
16
|
## Installation
|
|
16
17
|
|
|
@@ -505,18 +506,29 @@ export default {
|
|
|
505
506
|
plugins: [
|
|
506
507
|
twsxPlugin({
|
|
507
508
|
inputDir: 'src',
|
|
508
|
-
outputDir: 'src/styles'
|
|
509
|
+
outputDir: 'src/styles',
|
|
510
|
+
preserveStructure: false // Set to true to generate CSS next to JS files
|
|
509
511
|
})
|
|
510
512
|
]
|
|
511
513
|
};
|
|
512
514
|
```
|
|
513
515
|
|
|
514
|
-
|
|
516
|
+
**Configuration Options:**
|
|
517
|
+
- `inputDir`: Directory to scan for `twsx.*.js` files (default: `'src'`)
|
|
518
|
+
- `outputDir`: Directory where CSS files will be generated (default: `'src/styles'`)
|
|
519
|
+
- `preserveStructure`: Whether to generate CSS files next to their JS counterparts (default: `false`)
|
|
520
|
+
|
|
521
|
+
**Default mode:** CSS files created in `src/styles/` (e.g., `twsx.card.css`, `twsx.button.css`).
|
|
522
|
+
**Preserve structure mode:** CSS files created next to JS files (e.g., `src/components/Card/twsx.card.css`).
|
|
523
|
+
|
|
515
524
|
Import in your components:
|
|
516
525
|
```js
|
|
517
|
-
//
|
|
526
|
+
// Default mode
|
|
518
527
|
import './styles/twsx.card.css';
|
|
519
528
|
import './styles/twsx.button.css';
|
|
529
|
+
|
|
530
|
+
// Preserve structure mode
|
|
531
|
+
import './twsx.card.css'; // If in same directory
|
|
520
532
|
```
|
|
521
533
|
|
|
522
534
|
#### Webpack Plugin Usage Example
|
|
@@ -529,56 +541,152 @@ module.exports = {
|
|
|
529
541
|
plugins: [
|
|
530
542
|
new TwsxPlugin({
|
|
531
543
|
inputDir: 'src',
|
|
532
|
-
outputDir: 'src/styles'
|
|
544
|
+
outputDir: 'src/styles',
|
|
545
|
+
preserveStructure: false // Set to true to generate CSS next to JS files
|
|
533
546
|
})
|
|
534
547
|
]
|
|
535
548
|
};
|
|
536
549
|
```
|
|
537
550
|
|
|
538
|
-
|
|
551
|
+
**Configuration Options:**
|
|
552
|
+
- `inputDir`: Directory to scan for `twsx.*.js` files (default: `'src'`)
|
|
553
|
+
- `outputDir`: Directory where CSS files will be generated (default: `'src/styles'`)
|
|
554
|
+
- `preserveStructure`: Whether to generate CSS files next to their JS counterparts (default: `false`)
|
|
555
|
+
|
|
556
|
+
**Default mode:** CSS files created in `src/styles/` (e.g., `twsx.card.css`, `twsx.button.css`).
|
|
557
|
+
**Preserve structure mode:** CSS files created next to JS files (e.g., `src/components/Card/twsx.card.css`).
|
|
558
|
+
|
|
539
559
|
Import in your components:
|
|
540
560
|
```js
|
|
541
|
-
//
|
|
561
|
+
// Default mode
|
|
542
562
|
import './styles/twsx.card.css';
|
|
543
563
|
import './styles/twsx.button.css';
|
|
564
|
+
|
|
565
|
+
// Preserve structure mode
|
|
566
|
+
import './twsx.card.css'; // If in same directory
|
|
544
567
|
```
|
|
545
568
|
|
|
546
569
|
## Build-Time CSS Generation via Script
|
|
547
570
|
|
|
548
571
|
In addition to using the Vite/Webpack plugin, you can also use a Node.js script to generate CSS files from `twsx.*.js` files manually or as part of your build workflow.
|
|
549
572
|
|
|
550
|
-
### Script: tailwind-to-style/lib/
|
|
573
|
+
### Script: tailwind-to-style/lib/twsx-cli.js (Legacy)
|
|
574
|
+
|
|
575
|
+
> **💡 Recommended:** Use `npx twsx-cli` instead of calling the script directly.
|
|
551
576
|
|
|
552
577
|
This script will recursively scan for all `twsx.*.js` files in your project, generate CSS using the `twsx` function, and write individual CSS files to the specified output directory.
|
|
553
578
|
|
|
554
579
|
#### How to Use
|
|
555
580
|
|
|
556
581
|
1. Create JS files with `twsx.` prefix containing style objects anywhere in your `src/` folder (e.g., `src/components/twsx.card.js`).
|
|
557
|
-
2. Run the script with the following command:
|
|
558
582
|
|
|
583
|
+
2. **One-time Build:**
|
|
559
584
|
```bash
|
|
560
|
-
node tailwind-to-style/lib/
|
|
585
|
+
node tailwind-to-style/lib/twsx-cli.js
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
3. **Watch Mode (Auto-rebuild on file changes):**
|
|
589
|
+
```bash
|
|
590
|
+
node tailwind-to-style/lib/twsx-cli.js --watch
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
4. **Preserve Structure Mode (Generate CSS next to JS files):**
|
|
594
|
+
```bash
|
|
595
|
+
# One-time build with preserve structure
|
|
596
|
+
node tailwind-to-style/lib/twsx-cli.js --preserve-structure
|
|
597
|
+
|
|
598
|
+
# Watch mode with preserve structure
|
|
599
|
+
node tailwind-to-style/lib/twsx-cli.js --watch --preserve-structure
|
|
561
600
|
```
|
|
562
601
|
|
|
563
602
|
You can configure input and output directories using environment variables:
|
|
564
603
|
```bash
|
|
565
|
-
TWSX_INPUT_DIR=src TWSX_OUTPUT_DIR=dist/styles node tailwind-to-style/lib/
|
|
604
|
+
TWSX_INPUT_DIR=src TWSX_OUTPUT_DIR=dist/styles node tailwind-to-style/lib/twsx-cli.js --watch
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
Or use environment variables for preserve structure:
|
|
608
|
+
```bash
|
|
609
|
+
TWSX_PRESERVE_STRUCTURE=true node tailwind-to-style/lib/twsx-cli.js --watch
|
|
566
610
|
```
|
|
567
611
|
|
|
568
|
-
|
|
612
|
+
5. **Output locations:**
|
|
569
613
|
|
|
614
|
+
**Default mode:** CSS files will be in the output directory (default: `src/styles/`):
|
|
570
615
|
```
|
|
571
616
|
src/styles/twsx.card.css
|
|
572
617
|
src/styles/twsx.button.css
|
|
573
618
|
```
|
|
574
619
|
|
|
575
|
-
|
|
620
|
+
**Preserve structure mode:** CSS files will be generated next to their JS counterparts:
|
|
621
|
+
```
|
|
622
|
+
src/components/Button/twsx.button.js → src/components/Button/twsx.button.css
|
|
623
|
+
src/components/Card/twsx.card.js → src/components/Card/twsx.card.css
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
5. Import the generated CSS files in your components:
|
|
576
627
|
|
|
628
|
+
**Default mode:**
|
|
577
629
|
```js
|
|
578
630
|
import './styles/twsx.card.css';
|
|
579
631
|
import './styles/twsx.button.css';
|
|
580
632
|
```
|
|
581
633
|
|
|
634
|
+
**Preserve structure mode:**
|
|
635
|
+
```js
|
|
636
|
+
// In src/components/Button/Button.jsx
|
|
637
|
+
import './twsx.button.css';
|
|
638
|
+
|
|
639
|
+
// In src/components/Card/Card.jsx
|
|
640
|
+
import './twsx.card.css';
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
#### Usage in Different Projects
|
|
644
|
+
|
|
645
|
+
**React/Next.js/Vue/Any Project:**
|
|
646
|
+
|
|
647
|
+
1. Install the package:
|
|
648
|
+
```bash
|
|
649
|
+
npm install tailwind-to-style
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
2. Add to your `package.json`:
|
|
653
|
+
```json
|
|
654
|
+
{
|
|
655
|
+
"scripts": {
|
|
656
|
+
"twsx:build": "node node_modules/tailwind-to-style/lib/twsx-cli.js",
|
|
657
|
+
"twsx:watch": "node node_modules/tailwind-to-style/lib/twsx-cli.js --watch",
|
|
658
|
+
"twsx:preserve": "node node_modules/tailwind-to-style/lib/twsx-cli.js --preserve-structure",
|
|
659
|
+
"twsx:dev": "node node_modules/tailwind-to-style/lib/twsx-cli.js --watch --preserve-structure",
|
|
660
|
+
"dev": "npm run twsx:watch & next dev"
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
3. For development with auto-rebuild:
|
|
666
|
+
```bash
|
|
667
|
+
npm run twsx:watch
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
4. For production build:
|
|
671
|
+
```bash
|
|
672
|
+
npm run twsx:build
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
**VS Code Integration:**
|
|
676
|
+
Add to your workspace settings (`.vscode/settings.json`):
|
|
677
|
+
```json
|
|
678
|
+
{
|
|
679
|
+
"emeraldwalk.runonsave": {
|
|
680
|
+
"commands": [
|
|
681
|
+
{
|
|
682
|
+
"match": "twsx\\..*\\.js$",
|
|
683
|
+
"cmd": "npm run twsx:build"
|
|
684
|
+
}
|
|
685
|
+
]
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
```
|
|
689
|
+
|
|
582
690
|
#### Automatic Integration
|
|
583
691
|
|
|
584
692
|
You can add this script to the build section in your `package.json`:
|
|
@@ -586,7 +694,7 @@ You can add this script to the build section in your `package.json`:
|
|
|
586
694
|
```json
|
|
587
695
|
{
|
|
588
696
|
"scripts": {
|
|
589
|
-
"build-css": "node tailwind-to-style/lib/
|
|
697
|
+
"build-css": "node tailwind-to-style/lib/twsx-cli.js"
|
|
590
698
|
}
|
|
591
699
|
}
|
|
592
700
|
```
|
package/dist/index.browser.js
CHANGED
package/dist/index.cjs
CHANGED
package/dist/index.esm.js
CHANGED
package/lib/twsx-cli.js
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { pathToFileURL } from "url";
|
|
4
|
+
import { twsx } from "tailwind-to-style";
|
|
5
|
+
import chokidar from "chokidar";
|
|
6
|
+
|
|
7
|
+
function findTwsxFiles(dir, files = []) {
|
|
8
|
+
const items = fs.readdirSync(dir);
|
|
9
|
+
for (const item of items) {
|
|
10
|
+
const fullPath = path.join(dir, item);
|
|
11
|
+
const stat = fs.statSync(fullPath);
|
|
12
|
+
if (stat.isDirectory()) {
|
|
13
|
+
findTwsxFiles(fullPath, files);
|
|
14
|
+
} else if (item.startsWith("twsx.") && item.endsWith(".js")) {
|
|
15
|
+
files.push(fullPath);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return files;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function buildTwsx(inputDir, outputDir, preserveStructure = false) {
|
|
22
|
+
try {
|
|
23
|
+
const twsxFiles = findTwsxFiles(inputDir);
|
|
24
|
+
const generatedCssFiles = [];
|
|
25
|
+
|
|
26
|
+
// Generate CSS from JS files
|
|
27
|
+
for (const filePath of twsxFiles) {
|
|
28
|
+
try {
|
|
29
|
+
const styleModule = await import(
|
|
30
|
+
pathToFileURL(filePath).href + `?update=${Date.now()}`
|
|
31
|
+
);
|
|
32
|
+
const styleObj = styleModule.default || styleModule;
|
|
33
|
+
const css = twsx(styleObj, { inject: false });
|
|
34
|
+
const fileName = path.basename(filePath).replace(/\.js$/, ".css");
|
|
35
|
+
|
|
36
|
+
let cssFilePath;
|
|
37
|
+
if (preserveStructure) {
|
|
38
|
+
// Generate CSS in the same directory as the JS file
|
|
39
|
+
cssFilePath = filePath.replace(/\.js$/, ".css");
|
|
40
|
+
} else {
|
|
41
|
+
// Generate CSS in the output directory
|
|
42
|
+
cssFilePath = path.join(outputDir, fileName);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
fs.writeFileSync(cssFilePath, css);
|
|
46
|
+
generatedCssFiles.push(preserveStructure ? cssFilePath : fileName);
|
|
47
|
+
console.log(`[twsx-cli] CSS written to ${cssFilePath}`);
|
|
48
|
+
} catch (err) {
|
|
49
|
+
console.error(
|
|
50
|
+
`[twsx-cli] Error importing or processing ${filePath}:`,
|
|
51
|
+
err
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Clean up orphaned CSS files
|
|
57
|
+
if (!preserveStructure && fs.existsSync(outputDir)) {
|
|
58
|
+
const existingCssFiles = fs
|
|
59
|
+
.readdirSync(outputDir)
|
|
60
|
+
.filter((file) => file.startsWith("twsx.") && file.endsWith(".css"));
|
|
61
|
+
|
|
62
|
+
for (const cssFile of existingCssFiles) {
|
|
63
|
+
if (!generatedCssFiles.includes(cssFile)) {
|
|
64
|
+
const cssFilePath = path.join(outputDir, cssFile);
|
|
65
|
+
fs.unlinkSync(cssFilePath);
|
|
66
|
+
console.log(`[twsx-cli] Removed orphaned CSS: ${cssFilePath}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
} else if (preserveStructure) {
|
|
70
|
+
// Clean up orphaned CSS files in preserve structure mode
|
|
71
|
+
const twsxFiles = findTwsxFiles(inputDir);
|
|
72
|
+
const expectedCssFiles = twsxFiles.map(file => file.replace(/\.js$/, ".css"));
|
|
73
|
+
|
|
74
|
+
// Find and remove orphaned CSS files
|
|
75
|
+
function cleanupOrphanedCss(dir) {
|
|
76
|
+
if (!fs.existsSync(dir)) return;
|
|
77
|
+
|
|
78
|
+
const items = fs.readdirSync(dir);
|
|
79
|
+
for (const item of items) {
|
|
80
|
+
const fullPath = path.join(dir, item);
|
|
81
|
+
const stat = fs.statSync(fullPath);
|
|
82
|
+
if (stat.isDirectory()) {
|
|
83
|
+
cleanupOrphanedCss(fullPath);
|
|
84
|
+
} else if (item.startsWith("twsx.") && item.endsWith(".css")) {
|
|
85
|
+
if (!expectedCssFiles.includes(fullPath)) {
|
|
86
|
+
fs.unlinkSync(fullPath);
|
|
87
|
+
console.log(`[twsx-cli] Removed orphaned CSS: ${fullPath}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
cleanupOrphanedCss(inputDir);
|
|
94
|
+
}
|
|
95
|
+
} catch (err) {
|
|
96
|
+
console.error("[twsx-cli] Error scanning for twsx files:", err);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const inputDir =
|
|
101
|
+
process.env.TWSX_INPUT_DIR || path.resolve(process.cwd(), "src");
|
|
102
|
+
const outputDir =
|
|
103
|
+
process.env.TWSX_OUTPUT_DIR || path.resolve(process.cwd(), "src/styles");
|
|
104
|
+
const watchMode = process.argv.includes("--watch") || process.env.TWSX_WATCH === "true";
|
|
105
|
+
const preserveStructure = process.argv.includes("--preserve-structure") || process.env.TWSX_PRESERVE_STRUCTURE === "true";
|
|
106
|
+
|
|
107
|
+
if (!preserveStructure && !fs.existsSync(outputDir)) {
|
|
108
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Initial build
|
|
112
|
+
(async () => {
|
|
113
|
+
try {
|
|
114
|
+
await buildTwsx(inputDir, outputDir, preserveStructure);
|
|
115
|
+
console.log(`[twsx-cli] Initial build completed`);
|
|
116
|
+
|
|
117
|
+
if (watchMode) {
|
|
118
|
+
console.log(`[twsx-cli] Watching for changes in ${inputDir}...`);
|
|
119
|
+
|
|
120
|
+
const watcher = chokidar.watch(`${inputDir}/**/twsx.*.js`, {
|
|
121
|
+
ignored: /node_modules/,
|
|
122
|
+
persistent: true,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
watcher
|
|
126
|
+
.on("change", async (filePath) => {
|
|
127
|
+
console.log(`[twsx-cli] File changed: ${filePath}`);
|
|
128
|
+
await buildTwsx(inputDir, outputDir, preserveStructure);
|
|
129
|
+
})
|
|
130
|
+
.on("add", async (filePath) => {
|
|
131
|
+
console.log(`[twsx-cli] File added: ${filePath}`);
|
|
132
|
+
await buildTwsx(inputDir, outputDir, preserveStructure);
|
|
133
|
+
})
|
|
134
|
+
.on("unlink", async (filePath) => {
|
|
135
|
+
console.log(`[twsx-cli] File removed: ${filePath}`);
|
|
136
|
+
await buildTwsx(inputDir, outputDir, preserveStructure);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// Keep the process alive
|
|
140
|
+
process.on("SIGINT", () => {
|
|
141
|
+
console.log("\n[twsx-cli] Stopping watcher...");
|
|
142
|
+
watcher.close();
|
|
143
|
+
process.exit(0);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
} catch (err) {
|
|
147
|
+
console.error("[twsx-cli] Error writing CSS:", err);
|
|
148
|
+
}
|
|
149
|
+
})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tailwind-to-style",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.6",
|
|
4
4
|
"description": "Convert tailwind classes to inline style",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -12,9 +12,11 @@
|
|
|
12
12
|
"README.md",
|
|
13
13
|
"LICENSE",
|
|
14
14
|
"plugins",
|
|
15
|
-
"lib"
|
|
16
|
-
"twsx.css"
|
|
15
|
+
"lib"
|
|
17
16
|
],
|
|
17
|
+
"bin": {
|
|
18
|
+
"twsx-cli": "./lib/twsx-cli.js"
|
|
19
|
+
},
|
|
18
20
|
"scripts": {
|
|
19
21
|
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --passWithNoTests",
|
|
20
22
|
"build": "rollup -c rollup.config.js",
|
|
@@ -54,6 +56,7 @@
|
|
|
54
56
|
"@rollup/plugin-terser": "^0.4.4",
|
|
55
57
|
"babel-jest": "^28.1.0",
|
|
56
58
|
"babel-loader": "^10.0.0",
|
|
59
|
+
"chokidar": "^4.0.1",
|
|
57
60
|
"eslint": "^8.16.0",
|
|
58
61
|
"jest": "^28.1.0",
|
|
59
62
|
"prettier": "^3.5.3",
|
package/plugins/vite-twsx.js
CHANGED
|
@@ -17,7 +17,7 @@ function findTwsxFiles(dir, files = []) {
|
|
|
17
17
|
return files;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
async function buildTwsx(inputDir, outputDir) {
|
|
20
|
+
async function buildTwsx(inputDir, outputDir, preserveStructure = false) {
|
|
21
21
|
try {
|
|
22
22
|
const twsxFiles = findTwsxFiles(inputDir);
|
|
23
23
|
const generatedCssFiles = [];
|
|
@@ -31,9 +31,24 @@ async function buildTwsx(inputDir, outputDir) {
|
|
|
31
31
|
const styleObj = styleModule.default || styleModule;
|
|
32
32
|
const css = twsx(styleObj, { inject: false });
|
|
33
33
|
const fileName = path.basename(filePath).replace(/\.js$/, ".css");
|
|
34
|
-
|
|
34
|
+
|
|
35
|
+
let cssFilePath;
|
|
36
|
+
if (preserveStructure) {
|
|
37
|
+
// Generate CSS file next to the JS file
|
|
38
|
+
cssFilePath = filePath.replace(/\.js$/, ".css");
|
|
39
|
+
} else {
|
|
40
|
+
// Generate CSS file in the output directory
|
|
41
|
+
cssFilePath = path.join(outputDir, fileName);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Ensure the directory exists
|
|
45
|
+
const cssDir = path.dirname(cssFilePath);
|
|
46
|
+
if (!fs.existsSync(cssDir)) {
|
|
47
|
+
fs.mkdirSync(cssDir, { recursive: true });
|
|
48
|
+
}
|
|
49
|
+
|
|
35
50
|
fs.writeFileSync(cssFilePath, css);
|
|
36
|
-
generatedCssFiles.push(fileName);
|
|
51
|
+
generatedCssFiles.push(preserveStructure ? cssFilePath : fileName);
|
|
37
52
|
} catch (err) {
|
|
38
53
|
console.error(
|
|
39
54
|
`[vite-twsx] Error importing or processing ${filePath}:`,
|
|
@@ -43,15 +58,27 @@ async function buildTwsx(inputDir, outputDir) {
|
|
|
43
58
|
}
|
|
44
59
|
|
|
45
60
|
// Clean up orphaned CSS files
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
61
|
+
if (preserveStructure) {
|
|
62
|
+
// For preserve structure mode, clean up next to JS files
|
|
63
|
+
const allJsFiles = findTwsxFiles(inputDir);
|
|
64
|
+
for (const jsFile of allJsFiles) {
|
|
65
|
+
const cssFile = jsFile.replace(/\.js$/, ".css");
|
|
66
|
+
if (fs.existsSync(cssFile) && !generatedCssFiles.includes(cssFile)) {
|
|
67
|
+
fs.unlinkSync(cssFile);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
// For normal mode, clean up in output directory
|
|
72
|
+
if (fs.existsSync(outputDir)) {
|
|
73
|
+
const existingCssFiles = fs
|
|
74
|
+
.readdirSync(outputDir)
|
|
75
|
+
.filter((file) => file.startsWith("twsx.") && file.endsWith(".css"));
|
|
50
76
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
77
|
+
for (const cssFile of existingCssFiles) {
|
|
78
|
+
if (!generatedCssFiles.includes(cssFile)) {
|
|
79
|
+
const cssFilePath = path.join(outputDir, cssFile);
|
|
80
|
+
fs.unlinkSync(cssFilePath);
|
|
81
|
+
}
|
|
55
82
|
}
|
|
56
83
|
}
|
|
57
84
|
}
|
|
@@ -65,8 +92,9 @@ export default function twsxPlugin(options = {}) {
|
|
|
65
92
|
const inputDir = options.inputDir || path.resolve(process.cwd(), "src");
|
|
66
93
|
const outputDir =
|
|
67
94
|
options.outputDir || path.resolve(process.cwd(), "src/styles");
|
|
95
|
+
const preserveStructure = options.preserveStructure || false;
|
|
68
96
|
|
|
69
|
-
if (!fs.existsSync(outputDir)) {
|
|
97
|
+
if (!preserveStructure && !fs.existsSync(outputDir)) {
|
|
70
98
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
71
99
|
}
|
|
72
100
|
|
|
@@ -74,14 +102,14 @@ export default function twsxPlugin(options = {}) {
|
|
|
74
102
|
name: "vite-twsx",
|
|
75
103
|
async buildStart() {
|
|
76
104
|
try {
|
|
77
|
-
await buildTwsx(inputDir, outputDir);
|
|
105
|
+
await buildTwsx(inputDir, outputDir, preserveStructure);
|
|
78
106
|
} catch (err) {
|
|
79
107
|
console.error("[vite-twsx] Error writing CSS:", err);
|
|
80
108
|
}
|
|
81
109
|
},
|
|
82
110
|
async handleHotUpdate() {
|
|
83
111
|
try {
|
|
84
|
-
await buildTwsx(inputDir, outputDir);
|
|
112
|
+
await buildTwsx(inputDir, outputDir, preserveStructure);
|
|
85
113
|
} catch (err) {
|
|
86
114
|
console.error("[vite-twsx] Error updating CSS:", err);
|
|
87
115
|
}
|
package/plugins/webpack-twsx.js
CHANGED
|
@@ -17,7 +17,7 @@ function findTwsxFiles(dir, files = []) {
|
|
|
17
17
|
return files;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
async function buildTwsx(inputDir, outputDir) {
|
|
20
|
+
async function buildTwsx(inputDir, outputDir, preserveStructure = false) {
|
|
21
21
|
try {
|
|
22
22
|
const twsxFiles = findTwsxFiles(inputDir);
|
|
23
23
|
const generatedCssFiles = [];
|
|
@@ -31,9 +31,24 @@ async function buildTwsx(inputDir, outputDir) {
|
|
|
31
31
|
const styleObj = styleModule.default || styleModule;
|
|
32
32
|
const css = twsx(styleObj, { inject: false });
|
|
33
33
|
const fileName = path.basename(filePath).replace(/\.js$/, ".css");
|
|
34
|
-
|
|
34
|
+
|
|
35
|
+
let cssFilePath;
|
|
36
|
+
if (preserveStructure) {
|
|
37
|
+
// Generate CSS file next to the JS file
|
|
38
|
+
cssFilePath = filePath.replace(/\.js$/, ".css");
|
|
39
|
+
} else {
|
|
40
|
+
// Generate CSS file in the output directory
|
|
41
|
+
cssFilePath = path.join(outputDir, fileName);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Ensure the directory exists
|
|
45
|
+
const cssDir = path.dirname(cssFilePath);
|
|
46
|
+
if (!fs.existsSync(cssDir)) {
|
|
47
|
+
fs.mkdirSync(cssDir, { recursive: true });
|
|
48
|
+
}
|
|
49
|
+
|
|
35
50
|
fs.writeFileSync(cssFilePath, css);
|
|
36
|
-
generatedCssFiles.push(fileName);
|
|
51
|
+
generatedCssFiles.push(preserveStructure ? cssFilePath : fileName);
|
|
37
52
|
} catch (err) {
|
|
38
53
|
console.error(
|
|
39
54
|
`[webpack-twsx] Error importing or processing ${filePath}:`,
|
|
@@ -43,16 +58,28 @@ async function buildTwsx(inputDir, outputDir) {
|
|
|
43
58
|
}
|
|
44
59
|
|
|
45
60
|
// Clean up orphaned CSS files
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
61
|
+
if (preserveStructure) {
|
|
62
|
+
// For preserve structure mode, clean up next to JS files
|
|
63
|
+
const allJsFiles = findTwsxFiles(inputDir);
|
|
64
|
+
for (const jsFile of allJsFiles) {
|
|
65
|
+
const cssFile = jsFile.replace(/\.js$/, ".css");
|
|
66
|
+
if (fs.existsSync(cssFile) && !generatedCssFiles.includes(cssFile)) {
|
|
67
|
+
fs.unlinkSync(cssFile);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
// For normal mode, clean up in output directory
|
|
72
|
+
if (fs.existsSync(outputDir)) {
|
|
73
|
+
const existingCssFiles = fs.readdirSync(outputDir).filter((file) => {
|
|
74
|
+
return file.startsWith("twsx.") && file.endsWith(".css");
|
|
75
|
+
});
|
|
50
76
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
77
|
+
if (Array.isArray(existingCssFiles) && Array.isArray(generatedCssFiles)) {
|
|
78
|
+
for (const cssFile of existingCssFiles) {
|
|
79
|
+
if (!generatedCssFiles.includes(cssFile)) {
|
|
80
|
+
const cssFilePath = path.join(outputDir, cssFile);
|
|
81
|
+
fs.unlinkSync(cssFilePath);
|
|
82
|
+
}
|
|
56
83
|
}
|
|
57
84
|
}
|
|
58
85
|
}
|
|
@@ -67,7 +94,9 @@ class TwsxPlugin {
|
|
|
67
94
|
this.inputDir = options.inputDir || path.resolve(process.cwd(), "src");
|
|
68
95
|
this.outputDir =
|
|
69
96
|
options.outputDir || path.resolve(process.cwd(), "src/styles");
|
|
70
|
-
|
|
97
|
+
this.preserveStructure = options.preserveStructure || false;
|
|
98
|
+
|
|
99
|
+
if (!this.preserveStructure && !fs.existsSync(this.outputDir)) {
|
|
71
100
|
fs.mkdirSync(this.outputDir, { recursive: true });
|
|
72
101
|
}
|
|
73
102
|
}
|
|
@@ -75,14 +104,14 @@ class TwsxPlugin {
|
|
|
75
104
|
apply(compiler) {
|
|
76
105
|
compiler.hooks.beforeCompile.tapPromise("TwsxPlugin", async () => {
|
|
77
106
|
try {
|
|
78
|
-
await buildTwsx(this.inputDir, this.outputDir);
|
|
107
|
+
await buildTwsx(this.inputDir, this.outputDir, this.preserveStructure);
|
|
79
108
|
} catch (err) {
|
|
80
109
|
console.error("[webpack-twsx] Error writing CSS:", err);
|
|
81
110
|
}
|
|
82
111
|
});
|
|
83
112
|
compiler.hooks.watchRun.tapPromise("TwsxPlugin", async () => {
|
|
84
113
|
try {
|
|
85
|
-
await buildTwsx(this.inputDir, this.outputDir);
|
|
114
|
+
await buildTwsx(this.inputDir, this.outputDir, this.preserveStructure);
|
|
86
115
|
} catch (err) {
|
|
87
116
|
console.error("[webpack-twsx] Error updating CSS:", err);
|
|
88
117
|
}
|
package/lib/build-twsx.js
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import { pathToFileURL } from "url";
|
|
4
|
-
import { twsx } from "tailwind-to-style";
|
|
5
|
-
|
|
6
|
-
function findTwsxFiles(dir, files = []) {
|
|
7
|
-
const items = fs.readdirSync(dir);
|
|
8
|
-
for (const item of items) {
|
|
9
|
-
const fullPath = path.join(dir, item);
|
|
10
|
-
const stat = fs.statSync(fullPath);
|
|
11
|
-
if (stat.isDirectory()) {
|
|
12
|
-
findTwsxFiles(fullPath, files);
|
|
13
|
-
} else if (item.startsWith("twsx.") && item.endsWith(".js")) {
|
|
14
|
-
files.push(fullPath);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
return files;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async function buildTwsx(inputDir, outputDir) {
|
|
21
|
-
try {
|
|
22
|
-
const twsxFiles = findTwsxFiles(inputDir);
|
|
23
|
-
for (const filePath of twsxFiles) {
|
|
24
|
-
try {
|
|
25
|
-
const styleModule = await import(
|
|
26
|
-
pathToFileURL(filePath).href + `?update=${Date.now()}`
|
|
27
|
-
);
|
|
28
|
-
const styleObj = styleModule.default || styleModule;
|
|
29
|
-
const css = twsx(styleObj, { inject: false });
|
|
30
|
-
const fileName = path.basename(filePath).replace(/\.js$/, ".css");
|
|
31
|
-
const cssFilePath = path.join(outputDir, fileName);
|
|
32
|
-
fs.writeFileSync(cssFilePath, css);
|
|
33
|
-
} catch (err) {
|
|
34
|
-
console.error(
|
|
35
|
-
`[build-twsx] Error importing or processing ${filePath}:`,
|
|
36
|
-
err
|
|
37
|
-
);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
} catch (err) {
|
|
41
|
-
console.error("[build-twsx] Error scanning for twsx files:", err);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const inputDir =
|
|
46
|
-
process.env.TWSX_INPUT_DIR || path.resolve(process.cwd(), "src");
|
|
47
|
-
const outputDir =
|
|
48
|
-
process.env.TWSX_OUTPUT_DIR || path.resolve(process.cwd(), "src/styles");
|
|
49
|
-
if (!fs.existsSync(outputDir)) {
|
|
50
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
(async () => {
|
|
54
|
-
try {
|
|
55
|
-
await buildTwsx(inputDir, outputDir);
|
|
56
|
-
} catch (err) {
|
|
57
|
-
console.error("[build-twsx] Error writing CSS:", err);
|
|
58
|
-
}
|
|
59
|
-
})();
|
package/twsx.css
DELETED
|
File without changes
|