tailwind-style-sheets 0.0.1 → 0.0.3

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
@@ -1,10 +1,10 @@
1
1
  # tailwind-style-sheets
2
2
 
3
- A Turbopack loader and Next.js plugin for `.twss` files co-locate your Tailwind class maps alongside components.
3
+ Use standard CSS class names in your components — `.twss` files map them to Tailwind utilities at build time.
4
4
 
5
5
  ## What it does
6
6
 
7
- `.twss` files use a CSS-like syntax to define named Tailwind class groups. Each class can be written on its own line — this is the preferred style as it keeps diffs clean and classes easy to scan:
7
+ Define your class names and their Tailwind mappings in `.twss` files using familiar CSS syntax with `@apply`:
8
8
 
9
9
  ```css
10
10
  /* Button.styles.twss */
@@ -43,22 +43,13 @@ Inline is also valid:
43
43
  }
44
44
  ```
45
45
 
46
- Importing a `.twss` file gives you a plain object:
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 { modcn } from "@michalshelenberg/modcn";
49
+ import { twcn } from "tailwind-style-sheets";
59
50
  import styles from "./Button.styles.twss";
60
51
 
61
- const cn = modcn(styles);
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
@@ -83,9 +76,10 @@ This scaffolds all required files and installs the package.
83
76
  npm install tailwind-style-sheets
84
77
  ```
85
78
 
86
- ### `next.config.ts`
79
+ ### Next.js (Turbopack)
87
80
 
88
81
  ```ts
82
+ // next.config.ts
89
83
  import type { NextConfig } from "next";
90
84
  import path from "path";
91
85
  import { withTwssPlugin } from "tailwind-style-sheets";
@@ -107,6 +101,22 @@ export default withTwssPlugin(nextConfig, {
107
101
 
108
102
  Both options are optional. Omitting them disables the HMR watcher (the loader still works).
109
103
 
104
+ ### Vite
105
+
106
+ ```ts
107
+ // vite.config.ts
108
+ import { defineConfig } from "vite";
109
+ import react from "@vitejs/plugin-react";
110
+ import tailwindcss from "@tailwindcss/vite";
111
+ import { twssVitePlugin } from "tailwind-style-sheets/vite";
112
+
113
+ export default defineConfig({
114
+ plugins: [react(), tailwindcss(), twssVitePlugin()],
115
+ });
116
+ ```
117
+
118
+ The Vite plugin transforms `.twss` files and handles HMR automatically — no additional options needed.
119
+
110
120
  ### TypeScript
111
121
 
112
122
  Add a declaration file so TypeScript knows `.twss` imports return `Record<string, string>`:
@@ -133,9 +143,3 @@ Add to `.vscode/settings.json` to get CSS syntax highlighting and silence the `@
133
143
  }
134
144
  }
135
145
  ```
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.
@@ -0,0 +1,31 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
21
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
22
+ }) : x)(function(x) {
23
+ if (typeof require !== "undefined") return require.apply(this, arguments);
24
+ throw Error('Dynamic require of "' + x + '" is not supported');
25
+ });
26
+
27
+ export {
28
+ __spreadValues,
29
+ __spreadProps,
30
+ __require
31
+ };
@@ -0,0 +1,38 @@
1
+ import { NextConfig } from 'next';
2
+ import { modcn } from 'modcn';
3
+
4
+ interface TwssPluginOptions {
5
+ /**
6
+ * Absolute path to your global CSS file (e.g. `src/app/globals.css`).
7
+ * Touched on `.twss` file changes to trigger Tailwind's class scan and HMR.
8
+ */
9
+ globalsCSS?: string;
10
+ /**
11
+ * Directory to watch recursively for `.twss` file changes.
12
+ * Only active in `development`. Omit to disable the HMR watcher.
13
+ */
14
+ watchDir?: string;
15
+ }
16
+ /**
17
+ * Next.js plugin that enables `.twss` file imports as Tailwind class maps.
18
+ *
19
+ * Registers the Turbopack loader for `*.twss` files and, in development,
20
+ * watches `watchDir` for changes — touching `globalsCSS` on each change
21
+ * so Next.js re-runs Tailwind's class scan and hot-reloads styles.
22
+ *
23
+ * @example
24
+ * // next.config.ts
25
+ * import path from "path";
26
+ * import { withTwssPlugin } from "tailwind-style-sheets";
27
+ *
28
+ * export default withTwssPlugin({}, {
29
+ * globalsCSS: path.resolve(__dirname, "src/app/globals.css"),
30
+ * watchDir: path.resolve(__dirname, "src"),
31
+ * });
32
+ */
33
+ declare function withTwssPlugin(nextConfig: NextConfig, options?: TwssPluginOptions): NextConfig;
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.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
- export { type TwssPluginOptions, withTwssPlugin };
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/index.mjs ADDED
@@ -0,0 +1,42 @@
1
+ import {
2
+ __require,
3
+ __spreadProps,
4
+ __spreadValues
5
+ } from "./chunk-OFTF64MC.mjs";
6
+
7
+ // src/plugin.ts
8
+ import fs from "fs";
9
+ function withTwssPlugin(nextConfig, options = {}) {
10
+ var _a;
11
+ const { globalsCSS, watchDir } = options;
12
+ if (process.env.NODE_ENV === "development" && globalsCSS && watchDir) {
13
+ fs.watch(watchDir, { recursive: true }, (_, filename) => {
14
+ if (filename == null ? void 0 : filename.endsWith(".twss")) {
15
+ const now = /* @__PURE__ */ new Date();
16
+ fs.utimesSync(globalsCSS, now, now);
17
+ }
18
+ });
19
+ }
20
+ return __spreadProps(__spreadValues({}, nextConfig), {
21
+ turbopack: __spreadProps(__spreadValues({}, nextConfig.turbopack), {
22
+ rules: __spreadProps(__spreadValues({}, (_a = nextConfig.turbopack) == null ? void 0 : _a.rules), {
23
+ "*.twss": {
24
+ loaders: [__require.resolve("./loader")],
25
+ as: "*.js"
26
+ }
27
+ })
28
+ })
29
+ });
30
+ }
31
+
32
+ // src/twcn.ts
33
+ import { modcn } from "modcn";
34
+ import { twMerge } from "tailwind-merge";
35
+ function twcn(styles) {
36
+ const cn = modcn(styles);
37
+ return (...inputs) => twMerge(cn(...inputs));
38
+ }
39
+ export {
40
+ twcn,
41
+ withTwssPlugin
42
+ };
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
- var twssLoader = function(source) {
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 `export default ${JSON.stringify(styles)};`;
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
- module.exports = twssLoader;
43
+ var loader_default = twssLoader;
@@ -0,0 +1,5 @@
1
+ import { Plugin } from 'vite';
2
+
3
+ declare function twssVitePlugin(): Plugin;
4
+
5
+ export { twssVitePlugin };
package/dist/vite.mjs ADDED
@@ -0,0 +1,39 @@
1
+ import "./chunk-OFTF64MC.mjs";
2
+
3
+ // src/parse.ts
4
+ var BLOCK_REGEX = /\.(\w[\w-]*)\s*\{\s*@apply\s+([\s\S]*?)\s*\}/g;
5
+ function parseTwss(source) {
6
+ const styles = {};
7
+ let match;
8
+ while ((match = BLOCK_REGEX.exec(source)) !== null) {
9
+ const [, className, classes] = match;
10
+ styles[className] = classes.trim().replace(/\s+/g, " ");
11
+ }
12
+ return styles;
13
+ }
14
+
15
+ // src/vite-plugin.ts
16
+ function twssVitePlugin() {
17
+ return {
18
+ name: "tailwind-style-sheets",
19
+ transform(code, id) {
20
+ if (!id.endsWith(".twss")) return null;
21
+ const styles = parseTwss(code);
22
+ return {
23
+ code: `export default ${JSON.stringify(styles)};`,
24
+ map: null
25
+ };
26
+ },
27
+ handleHotUpdate({ file, server }) {
28
+ if (!file.endsWith(".twss")) return;
29
+ const mod = server.moduleGraph.getModuleById(file);
30
+ if (mod) {
31
+ server.moduleGraph.invalidateModule(mod);
32
+ server.hot.send({ type: "full-reload" });
33
+ }
34
+ }
35
+ };
36
+ }
37
+ export {
38
+ twssVitePlugin
39
+ };
package/package.json CHANGED
@@ -1,17 +1,37 @@
1
1
  {
2
2
  "name": "tailwind-style-sheets",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
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.mjs",
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
- "next": ">=15"
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
@@ -1,2 +1,3 @@
1
1
  export { withTwssPlugin } from "./plugin";
2
2
  export type { TwssPluginOptions } from "./plugin";
3
+ export { twcn } from "./twcn";
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
- const BLOCK_REGEX = /\.(\w[\w-]*)\s*\{\s*@apply\s+([\s\S]*?)\s*\}/g;
18
+ import { parseTwss } from "./parse";
19
19
 
20
20
  const twssLoader: (source: string) => string = function (source) {
21
- const styles: Record<string, string> = {};
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 = twssLoader;
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
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  "target": "ES2017",
4
- "module": "commonjs",
5
- "moduleResolution": "node",
4
+ "module": "esnext",
5
+ "moduleResolution": "bundler",
6
6
  "skipLibCheck": true,
7
7
  "strict": true,
8
8
  "esModuleInterop": true,
package/tsup.config.ts CHANGED
@@ -6,7 +6,12 @@ export default defineConfig([
6
6
  format: ["cjs"],
7
7
  external: ["./loader"],
8
8
  dts: true,
9
- clean: true,
9
+ },
10
+ {
11
+ entry: ["src/index.ts", "src/vite.ts"],
12
+ format: ["esm"],
13
+ external: ["vite"],
14
+ dts: true,
10
15
  },
11
16
  {
12
17
  entry: ["src/cli.ts"],