@weerachai06/tw-scanner 1.1.0 → 1.1.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 CHANGED
@@ -47,7 +47,10 @@ npx @weerachai06/tw-scanner --src ./src --css ./src/globals.css --json | jq '.in
47
47
  |---|---|---|
48
48
  | ❌ Invalid class | `bg-old-token-500` | **Error** — invalid after migration |
49
49
  | ❌ Invalid in cva variants | `danger: 'bg-red-danger'` | **Error** — detected via AST |
50
+ | ❌ Invalid `@apply` class | `@apply bg-fake-999;` in `.css` | **Error** — validated against your config |
51
+ | ❌ Missing CSS Module class | `styles.nonExistent` | **Error** — class not defined in `.module.css` |
50
52
  | ⚠️ Dynamic expression | `` `text-${color}` `` | **Warning** — cannot validate statically |
53
+ | ⚠️ Dynamic module access | `styles[variable]` | **Warning** — skipped, cannot resolve statically |
51
54
 
52
55
  ## How it works
53
56
 
@@ -60,13 +63,36 @@ Uses `@typescript-eslint/typescript-estree` to parse `.ts/.tsx/.js/.jsx` and tra
60
63
  - `ConditionalExpression` and `LogicalExpression` inside class utilities
61
64
  - `TemplateLiteral` — static parts extracted, dynamic parts flagged as warnings
62
65
 
63
- ### 2. Tailwind v4 Validation
66
+ ### 2. CSS file scanning
67
+
68
+ `.css` and `.module.css` files are also scanned. Classes inside `@apply` directives are extracted and validated against your Tailwind config.
69
+
70
+ ```css
71
+ .btn {
72
+ @apply bg-blue-500 text-white px-4; /* ✓ valid */
73
+ @apply bg-fake-999; /* ✗ error */
74
+ }
75
+ ```
76
+
77
+ ### 3. CSS Modules validation
78
+
79
+ When a JS/TS file imports a `.module.css` file, all `styles.xxx` and `styles['xxx']` usages are checked against the class names actually defined in that file.
80
+
81
+ ```tsx
82
+ import styles from './button.module.css'
83
+
84
+ <button className={styles.btn}>...</button> // ✓ defined
85
+ <button className={styles.nonExistent}>...</button> // ✗ error
86
+ <button className={styles[variant]}>...</button> // ⚠ skipped (dynamic)
87
+ ```
88
+
89
+ ### 4. Tailwind v4 Validation
64
90
 
65
91
  Uses `@tailwindcss/node`'s `compile()` API to load your actual CSS entry file (including all `@theme` tokens and plugins), then calls `build([candidate])` for each class. If the class generates no CSS rule in the output, it's invalid.
66
92
 
67
93
  Validation is **100% accurate against your real config** — custom tokens, plugins, and all.
68
94
 
69
- ### 3. Batch validation
95
+ ### 5. Batch validation
70
96
 
71
97
  All unique class values are validated in a single `build()` call per batch for performance.
72
98
 
package/dist/validator.js CHANGED
@@ -11,10 +11,33 @@ export async function loadTailwindContext(cssFile) {
11
11
  throw new Error(`Tailwind CSS file not found: ${abs}`);
12
12
  }
13
13
  const css = fs.readFileSync(abs, 'utf8');
14
- const result = await compile(css, {
15
- base: path.dirname(abs),
16
- onDependency: () => { },
17
- });
14
+ let result;
15
+ try {
16
+ result = await compile(css, {
17
+ base: path.dirname(abs),
18
+ onDependency: () => { },
19
+ });
20
+ }
21
+ catch (err) {
22
+ const msg = err.message ?? '';
23
+ const match = msg.match(/Cannot apply unknown utility class [`']?([^\s`']+)[`']?/);
24
+ if (match) {
25
+ // Strip all @apply lines from the entry CSS and retry so scanning can continue.
26
+ // Unknown utilities are likely defined via PostCSS plugins or external font systems
27
+ // that @tailwindcss/node's compile() doesn't have access to.
28
+ const strippedCss = css.replace(/^\s*@apply\b[^;]+;/gm, '');
29
+ console.warn(`⚠ Skipping @apply in "${path.relative(process.cwd(), abs)}" — ` +
30
+ `unknown utility: "${match[1]}". ` +
31
+ `Add "@utility ${match[1]} {}" to register it if you want it validated.`);
32
+ result = await compile(strippedCss, {
33
+ base: path.dirname(abs),
34
+ onDependency: () => { },
35
+ });
36
+ }
37
+ else {
38
+ throw err;
39
+ }
40
+ }
18
41
  compileCache.set(abs, result);
19
42
  return result;
20
43
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weerachai06/tw-scanner",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "AST-based Tailwind v4 class validator for React projects",
5
5
  "type": "module",
6
6
  "bin": {