tailwind-style-sheets 0.0.1 → 0.0.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 +6 -19
- package/dist/index.d.ts +5 -1
- package/dist/index.js +10 -0
- package/dist/loader.js +32 -3
- package/dist/vite.d.mts +5 -0
- package/dist/vite.mjs +37 -0
- package/package.json +25 -4
- package/src/index.ts +1 -0
- package/src/loader.ts +3 -11
- package/src/parse.ts +13 -0
- package/src/twcn.ts +10 -0
- package/src/vite-plugin.ts +28 -0
- package/src/vite.ts +1 -0
- package/tsconfig.json +2 -2
- package/tsup.config.ts +6 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# tailwind-style-sheets
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Co-locate Tailwind class maps alongside components using `.twss` files — a CSS-like syntax that pairs with a Turbopack loader and Next.js plugin.
|
|
4
4
|
|
|
5
5
|
## What it does
|
|
6
6
|
|
|
@@ -43,22 +43,13 @@ Inline is also valid:
|
|
|
43
43
|
}
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
```ts
|
|
49
|
-
import styles from "./Button.styles.twss";
|
|
50
|
-
// styles.button → "cursor-pointer px-4 py-2 rounded-full text-sm font-medium transition-all active:scale-95"
|
|
51
|
-
// styles["button--primary"] → "bg-blue-600 text-white hover:bg-blue-700"
|
|
52
|
-
// styles["button--ghost"] → "bg-transparent text-blue-600 hover:bg-blue-50"
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
Pair it with [`@michalshelenberg/modcn`](https://www.npmjs.com/package/@michalshelenberg/modcn) to compose BEM classes ergonomically:
|
|
46
|
+
## Usage
|
|
56
47
|
|
|
57
48
|
```tsx
|
|
58
|
-
import {
|
|
49
|
+
import { twcn } from "tailwind-style-sheets";
|
|
59
50
|
import styles from "./Button.styles.twss";
|
|
60
51
|
|
|
61
|
-
const cn =
|
|
52
|
+
const cn = twcn(styles);
|
|
62
53
|
|
|
63
54
|
export function Button({ variant = "primary", className, children, ...props }) {
|
|
64
55
|
return (
|
|
@@ -69,6 +60,8 @@ export function Button({ variant = "primary", className, children, ...props }) {
|
|
|
69
60
|
}
|
|
70
61
|
```
|
|
71
62
|
|
|
63
|
+
`twcn` takes the imported styles object, and returns a function that resolves class names with `tailwind-merge` — pass it style keys, modifiers, and an optional `className` override.
|
|
64
|
+
|
|
72
65
|
## Installation
|
|
73
66
|
|
|
74
67
|
```bash
|
|
@@ -133,9 +126,3 @@ Add to `.vscode/settings.json` to get CSS syntax highlighting and silence the `@
|
|
|
133
126
|
}
|
|
134
127
|
}
|
|
135
128
|
```
|
|
136
|
-
|
|
137
|
-
## How it works
|
|
138
|
-
|
|
139
|
-
1. **Loader** (`loader.ts`) — Turbopack passes the raw `.twss` file content through the loader. A regex extracts each `.className { @apply ... }` block and converts it to `export default { className: "class1 class2 ..." }`.
|
|
140
|
-
|
|
141
|
-
2. **HMR watcher** (`plugin.ts`) — In development, `fs.watch` monitors `watchDir` for `.twss` changes. When a change is detected, it touches `globalsCSS`, which causes Next.js to re-run Tailwind's class scan and hot-reload styles.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { NextConfig } from 'next';
|
|
2
|
+
import { modcn } from 'modcn';
|
|
2
3
|
|
|
3
4
|
interface TwssPluginOptions {
|
|
4
5
|
/**
|
|
@@ -31,4 +32,7 @@ interface TwssPluginOptions {
|
|
|
31
32
|
*/
|
|
32
33
|
declare function withTwssPlugin(nextConfig: NextConfig, options?: TwssPluginOptions): NextConfig;
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
type StylesMap = Record<string, string>;
|
|
36
|
+
declare function twcn(styles: StylesMap): (...inputs: Parameters<ReturnType<typeof modcn>>) => string;
|
|
37
|
+
|
|
38
|
+
export { type TwssPluginOptions, twcn, withTwssPlugin };
|
package/dist/index.js
CHANGED
|
@@ -47,6 +47,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
47
47
|
// src/index.ts
|
|
48
48
|
var index_exports = {};
|
|
49
49
|
__export(index_exports, {
|
|
50
|
+
twcn: () => twcn,
|
|
50
51
|
withTwssPlugin: () => withTwssPlugin
|
|
51
52
|
});
|
|
52
53
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -75,7 +76,16 @@ function withTwssPlugin(nextConfig, options = {}) {
|
|
|
75
76
|
})
|
|
76
77
|
});
|
|
77
78
|
}
|
|
79
|
+
|
|
80
|
+
// src/twcn.ts
|
|
81
|
+
var import_modcn = require("modcn");
|
|
82
|
+
var import_tailwind_merge = require("tailwind-merge");
|
|
83
|
+
function twcn(styles) {
|
|
84
|
+
const cn = (0, import_modcn.modcn)(styles);
|
|
85
|
+
return (...inputs) => (0, import_tailwind_merge.twMerge)(cn(...inputs));
|
|
86
|
+
}
|
|
78
87
|
// Annotate the CommonJS export names for ESM import in node:
|
|
79
88
|
0 && (module.exports = {
|
|
89
|
+
twcn,
|
|
80
90
|
withTwssPlugin
|
|
81
91
|
});
|
package/dist/loader.js
CHANGED
|
@@ -1,14 +1,43 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
2
19
|
|
|
3
20
|
// src/loader.ts
|
|
21
|
+
var loader_exports = {};
|
|
22
|
+
__export(loader_exports, {
|
|
23
|
+
default: () => loader_default
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(loader_exports);
|
|
26
|
+
|
|
27
|
+
// src/parse.ts
|
|
4
28
|
var BLOCK_REGEX = /\.(\w[\w-]*)\s*\{\s*@apply\s+([\s\S]*?)\s*\}/g;
|
|
5
|
-
|
|
29
|
+
function parseTwss(source) {
|
|
6
30
|
const styles = {};
|
|
7
31
|
let match;
|
|
8
32
|
while ((match = BLOCK_REGEX.exec(source)) !== null) {
|
|
9
33
|
const [, className, classes] = match;
|
|
10
34
|
styles[className] = classes.trim().replace(/\s+/g, " ");
|
|
11
35
|
}
|
|
12
|
-
return
|
|
36
|
+
return styles;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// src/loader.ts
|
|
40
|
+
var twssLoader = function(source) {
|
|
41
|
+
return `export default ${JSON.stringify(parseTwss(source))};`;
|
|
13
42
|
};
|
|
14
|
-
|
|
43
|
+
var loader_default = twssLoader;
|
package/dist/vite.d.mts
ADDED
package/dist/vite.mjs
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// src/parse.ts
|
|
2
|
+
var BLOCK_REGEX = /\.(\w[\w-]*)\s*\{\s*@apply\s+([\s\S]*?)\s*\}/g;
|
|
3
|
+
function parseTwss(source) {
|
|
4
|
+
const styles = {};
|
|
5
|
+
let match;
|
|
6
|
+
while ((match = BLOCK_REGEX.exec(source)) !== null) {
|
|
7
|
+
const [, className, classes] = match;
|
|
8
|
+
styles[className] = classes.trim().replace(/\s+/g, " ");
|
|
9
|
+
}
|
|
10
|
+
return styles;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// src/vite-plugin.ts
|
|
14
|
+
function twssVitePlugin() {
|
|
15
|
+
return {
|
|
16
|
+
name: "tailwind-style-sheets",
|
|
17
|
+
transform(code, id) {
|
|
18
|
+
if (!id.endsWith(".twss")) return null;
|
|
19
|
+
const styles = parseTwss(code);
|
|
20
|
+
return {
|
|
21
|
+
code: `export default ${JSON.stringify(styles)};`,
|
|
22
|
+
map: null
|
|
23
|
+
};
|
|
24
|
+
},
|
|
25
|
+
handleHotUpdate({ file, server }) {
|
|
26
|
+
if (!file.endsWith(".twss")) return;
|
|
27
|
+
const mod = server.moduleGraph.getModuleById(file);
|
|
28
|
+
if (mod) {
|
|
29
|
+
server.moduleGraph.invalidateModule(mod);
|
|
30
|
+
server.hot.send({ type: "full-reload" });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export {
|
|
36
|
+
twssVitePlugin
|
|
37
|
+
};
|
package/package.json
CHANGED
|
@@ -1,17 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tailwind-style-sheets",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"require": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./vite": {
|
|
13
|
+
"types": "./dist/vite.d.mts",
|
|
14
|
+
"import": "./dist/vite.mjs"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
6
17
|
"bin": {
|
|
7
18
|
"tailwind-style-sheets": "./dist/cli.js"
|
|
8
19
|
},
|
|
9
20
|
"scripts": {
|
|
10
|
-
"build": "tsup",
|
|
21
|
+
"build": "rm -rf dist && tsup",
|
|
11
22
|
"dev": "tsup --watch"
|
|
12
23
|
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"tailwind-merge": "^3.5.0"
|
|
26
|
+
},
|
|
13
27
|
"peerDependencies": {
|
|
14
|
-
"
|
|
28
|
+
"modcn": "^0.0.1",
|
|
29
|
+
"next": ">=15",
|
|
30
|
+
"vite": ">=5"
|
|
31
|
+
},
|
|
32
|
+
"peerDependenciesMeta": {
|
|
33
|
+
"next": { "optional": true },
|
|
34
|
+
"vite": { "optional": true }
|
|
15
35
|
},
|
|
16
36
|
"publishConfig": {
|
|
17
37
|
"access": "public"
|
|
@@ -20,6 +40,7 @@
|
|
|
20
40
|
"@types/node": "^20",
|
|
21
41
|
"next": "16.2.1",
|
|
22
42
|
"tsup": "^8.5.1",
|
|
23
|
-
"typescript": "^5"
|
|
43
|
+
"typescript": "^5",
|
|
44
|
+
"vite": "^8.0.1"
|
|
24
45
|
}
|
|
25
46
|
}
|
package/src/index.ts
CHANGED
package/src/loader.ts
CHANGED
|
@@ -15,18 +15,10 @@
|
|
|
15
15
|
* Output (JS module):
|
|
16
16
|
* export default { base: "px-4 py-2 rounded font-semibold", primary: "bg-blue-600 text-white hover:bg-blue-700" }
|
|
17
17
|
*/
|
|
18
|
-
|
|
18
|
+
import { parseTwss } from "./parse";
|
|
19
19
|
|
|
20
20
|
const twssLoader: (source: string) => string = function (source) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
let match: RegExpExecArray | null;
|
|
24
|
-
while ((match = BLOCK_REGEX.exec(source)) !== null) {
|
|
25
|
-
const [, className, classes] = match;
|
|
26
|
-
styles[className] = classes.trim().replace(/\s+/g, " ");
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return `export default ${JSON.stringify(styles)};`;
|
|
21
|
+
return `export default ${JSON.stringify(parseTwss(source))};`;
|
|
30
22
|
};
|
|
31
23
|
|
|
32
|
-
export
|
|
24
|
+
export default twssLoader;
|
package/src/parse.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const BLOCK_REGEX = /\.(\w[\w-]*)\s*\{\s*@apply\s+([\s\S]*?)\s*\}/g;
|
|
2
|
+
|
|
3
|
+
export function parseTwss(source: string): Record<string, string> {
|
|
4
|
+
const styles: Record<string, string> = {};
|
|
5
|
+
|
|
6
|
+
let match: RegExpExecArray | null;
|
|
7
|
+
while ((match = BLOCK_REGEX.exec(source)) !== null) {
|
|
8
|
+
const [, className, classes] = match;
|
|
9
|
+
styles[className] = classes.trim().replace(/\s+/g, " ");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return styles;
|
|
13
|
+
}
|
package/src/twcn.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { modcn } from "modcn";
|
|
2
|
+
import { twMerge } from "tailwind-merge";
|
|
3
|
+
|
|
4
|
+
type StylesMap = Record<string, string>;
|
|
5
|
+
|
|
6
|
+
export function twcn(styles: StylesMap) {
|
|
7
|
+
const cn = modcn(styles);
|
|
8
|
+
return (...inputs: Parameters<ReturnType<typeof modcn>>) =>
|
|
9
|
+
twMerge(cn(...inputs));
|
|
10
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { parseTwss } from "./parse";
|
|
2
|
+
import type { Plugin } from "vite";
|
|
3
|
+
|
|
4
|
+
export function twssVitePlugin(): Plugin {
|
|
5
|
+
return {
|
|
6
|
+
name: "tailwind-style-sheets",
|
|
7
|
+
|
|
8
|
+
transform(code, id) {
|
|
9
|
+
if (!id.endsWith(".twss")) return null;
|
|
10
|
+
|
|
11
|
+
const styles = parseTwss(code);
|
|
12
|
+
return {
|
|
13
|
+
code: `export default ${JSON.stringify(styles)};`,
|
|
14
|
+
map: null,
|
|
15
|
+
};
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
handleHotUpdate({ file, server }) {
|
|
19
|
+
if (!file.endsWith(".twss")) return;
|
|
20
|
+
|
|
21
|
+
const mod = server.moduleGraph.getModuleById(file);
|
|
22
|
+
if (mod) {
|
|
23
|
+
server.moduleGraph.invalidateModule(mod);
|
|
24
|
+
server.hot.send({ type: "full-reload" });
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
package/src/vite.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { twssVitePlugin } from "./vite-plugin";
|
package/tsconfig.json
CHANGED
package/tsup.config.ts
CHANGED