@presetter/preset-web 9.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 Alvis HT Tang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,545 @@
1
+ # 🚀 @presetter/preset-web
2
+
3
+ <p align="center">
4
+ <img src="https://raw.githubusercontent.com/alvis/presetter/master/assets/logo.svg" alt="Presetter logo" height="128">
5
+ </p>
6
+
7
+ <div align="center">
8
+
9
+ [![npm](https://img.shields.io/npm/v/@presetter/preset-web?style=flat-square)](https://github.com/alvis/presetter/releases)
10
+ [![build](https://img.shields.io/github/actions/workflow/status/alvis/presetter/test.yaml?branch=main&style=flat-square)](https://github.com/alvis/presetter/actions)
11
+ [![maintainability](https://img.shields.io/codeclimate/maintainability/alvis/presetter?style=flat-square)](https://codeclimate.com/github/alvis/presetter/maintainability)
12
+ [![coverage](https://img.shields.io/codeclimate/coverage/alvis/presetter?style=flat-square)](https://codeclimate.com/github/alvis/presetter/test_coverage)
13
+ [![vulnerabilities](https://img.shields.io/sonar/vulnerabilities/presetter/main?server=https%3A%2F%2Fsonarcloud.io&style=flat-square)](https://sonarcloud.io/summary/new_code?id=presetter)
14
+ [![dependencies](https://img.shields.io/librariesio/release/npm/@presetter/preset-web?style=flat-square)](https://libraries.io/npm/@presetter/preset-web)
15
+
16
+ Modern web development — TailwindCSS, Storybook, browser-optimized TypeScript
17
+
18
+ •   [Usage](#-usage)   •   [Configuration](#-configuration-details)   •   [Comparison](#-comparison)   •   [FAQ](#-faq)   •
19
+
20
+ </div>
21
+
22
+ ---
23
+
24
+ **This is a configuration extension that works with [Presetter](https://github.com/alvis/presetter/blob/main/packages/presetter), the configuration management tool.**
25
+
26
+ ## ⚡ TL;DR / Quick Start
27
+
28
+ ```bash
29
+ # Install web preset alongside a base preset
30
+ npm i -D presetter @presetter/preset-essentials @presetter/preset-web
31
+
32
+ # Create presetter.config.ts
33
+ cat > presetter.config.ts << 'EOF'
34
+ import { preset } from 'presetter';
35
+ import essentials from '@presetter/preset-essentials';
36
+ import web from '@presetter/preset-web';
37
+
38
+ export default preset('my-web-app', {
39
+ extends: [essentials, web],
40
+ });
41
+ EOF
42
+
43
+ # Bootstrap your project
44
+ npx presetter bootstrap
45
+ ```
46
+
47
+ Your project now has **modern web development tooling** — TailwindCSS 4, Storybook 9, browser-optimized TypeScript, and intelligent CSS workflows!
48
+
49
+ ---
50
+
51
+ ## ✨ Modern Web Development Stack
52
+
53
+ ### Need comprehensive web tooling?
54
+
55
+ Modern web development requires an intricate stack: CSS frameworks, component libraries, browser compatibility, styling workflows, and visual development tools. Setting these up manually is time-consuming and error-prone.
56
+
57
+ **What if you could get the complete modern web stack instantly?**
58
+
59
+ ### The modern web development challenge
60
+
61
+ | Web Development Need | Manual Setup | With preset-web |
62
+ | ------------------------- | --------------------------- | ------------------------------------ |
63
+ | **CSS Framework** | ⚠️ Manual TailwindCSS setup | ✅ TailwindCSS 4 auto-configured |
64
+ | **Component Development** | ❌ No visual development | ✅ Storybook 9 with addons |
65
+ | **Browser Compatibility** | ⚠️ Manual TypeScript config | ✅ DOM/Browser types included |
66
+ | **Styling Workflow** | ❌ Basic CSS processing | ✅ PostCSS + Autoprefixer + Prettier |
67
+ | **CSS Linting** | ❌ No CSS-specific rules | ✅ TailwindCSS conflict detection |
68
+ | **Development Tools** | ⚠️ Fragmented tooling | ✅ Integrated accessibility testing |
69
+
70
+ ### What you get instead
71
+
72
+ **@presetter/preset-web is a configuration extension that adds comprehensive modern web development tooling to any base preset.**
73
+
74
+ When used with [Presetter](https://github.com/alvis/presetter/blob/main/packages/presetter) (the configuration management tool), this preset extends your chosen base preset with the complete modern web development stack, including TailwindCSS 4, Storybook 9, browser-optimized TypeScript, and intelligent styling workflows.
75
+
76
+ - 💨 **TailwindCSS 4**: Latest utility-first CSS framework with intelligent auto-discovery
77
+ - 📚 **Storybook 9**: Component-driven development with accessibility testing
78
+ - 🎨 **PostCSS Pipeline**: Advanced CSS processing with autoprefixer
79
+ - 🌐 **Browser-Optimized**: TypeScript configured for DOM and modern browser APIs
80
+ - 🔍 **Smart Linting**: TailwindCSS conflict detection and auto-formatting
81
+ - 🚀 **Zero Config**: Intelligent discovery of project structure and entry points
82
+
83
+ ---
84
+
85
+ ## 🎯 Modern Web Without the Configuration
86
+
87
+ ### The modern web tooling setup problem
88
+
89
+ Modern web development requires coordinating multiple complex tools:
90
+
91
+ - **TailwindCSS setup**: Configuration, purging, entry points, plugin integration
92
+ - **Storybook configuration**: Stories, addons, accessibility testing, Vitest integration
93
+ - **Browser TypeScript**: DOM types, modern JavaScript features, web-specific APIs
94
+ - **CSS workflow**: PostCSS, autoprefixer, formatting, conflict detection
95
+ - **Development environment**: Hot reloading, visual development, testing integration
96
+
97
+ **Getting it right requires deep knowledge of each tool's configuration and how they interact.**
98
+
99
+ ### From fragmented tooling to integrated web stack
100
+
101
+ ```diff
102
+ # Before: Manual web development setup
103
+ my-web-app/
104
+ ├── tsconfig.json ← Missing DOM types, browser APIs
105
+ ├── tailwind.config.js ← Manual configuration, no auto-discovery
106
+ ├── postcss.config.js ← Basic setup, missing optimizations
107
+ ├── .storybook/ ← Complex manual setup
108
+ │ ├── main.js ← Missing modern addons
109
+ │ └── preview.js ← No accessibility testing
110
+ ├── src/
111
+ │ ├── styles/
112
+ │ │ └── globals.css ← No auto-discovery
113
+ │ └── components/
114
+ - └── Button.tsx ← No component development environment
115
+
116
+ # After: Extended with modern web stack
117
+ my-web-app/
118
+ +├── presetter.config.ts ← Base preset + web extension
119
+ ├── tsconfig.json ← Enhanced with DOM, browser types
120
+ ├── eslint.config.ts ← Enhanced with TailwindCSS linting
121
+ ├── .prettierrc ← Enhanced with TailwindCSS formatting
122
+ +├── All web dev tools ← Generated with intelligent discovery
123
+ ├── src/
124
+ │ ├── styles/
125
+ │ │ └── globals.css ← Auto-discovered for TailwindCSS
126
+ │ └── components/
127
+ + └── Button.tsx ← Storybook-ready component development
128
+ ```
129
+
130
+ ### How modern web stack integration works
131
+
132
+ 1. **Intelligent Discovery** — Auto-detects TailwindCSS entry points, config files, project structure
133
+ 2. **Browser Optimization** — TypeScript configured with DOM, DOM.Iterable, and ESNext libraries
134
+ 3. **Styling Workflow** — PostCSS + TailwindCSS + Prettier integration with conflict detection
135
+ 4. **Component Development** — Storybook 9 with accessibility, testing, and pseudo-state addons
136
+
137
+ ### Why this solves the real problem
138
+
139
+ - **Complete modern stack**: Everything needed for professional web development
140
+ - **Intelligent auto-configuration**: Discovers project structure automatically
141
+ - **Component-driven development**: Visual development environment with testing
142
+ - **Professional styling**: Advanced CSS workflows with conflict detection
143
+ - **Browser compatibility**: Properly configured for modern web APIs
144
+
145
+ ---
146
+
147
+ ## 🔍 Understanding Presetter vs This Extension
148
+
149
+ **Important distinction:**
150
+
151
+ | Component | Role | What it does |
152
+ | -------------------------------------------------------------------------------- | ----------------------------- | -------------------------------------------------------------------------------- |
153
+ | **[Presetter](https://github.com/alvis/presetter/blob/main/packages/presetter)** | Configuration management tool | CLI that processes presets, generates config files, executes scripts |
154
+ | **Base Preset** | Core development template | Provides TypeScript, testing, building capabilities (essentials, esm, cjs, etc.) |
155
+ | **@presetter/preset-web** | Web development extension | Adds TailwindCSS, Storybook, browser optimization, CSS workflows |
156
+
157
+ **Think of it like:**
158
+
159
+ - **Presetter** = The engine that builds houses
160
+ - **Base preset** = The blueprint for a functional house
161
+ - **This extension** = The modern kitchen, smart home systems, and designer finishes
162
+
163
+ This preset **extends** any base preset with modern web development capabilities. For advanced usage, customization, and troubleshooting, **[visit the main Presetter documentation](https://github.com/alvis/presetter/blob/main/packages/presetter)**.
164
+
165
+ ---
166
+
167
+ ## 🚀 Usage
168
+
169
+ ### 🟢 Basic Modern Web Setup
170
+
171
+ #### Step 1: Install Extension with Base Preset
172
+
173
+ ```jsonc
174
+ // package.json
175
+ {
176
+ "scripts": {
177
+ "build": "run build",
178
+ "test": "run test",
179
+ "storybook": "storybook dev -p 6006",
180
+ "build-storybook": "storybook build",
181
+ },
182
+ "devDependencies": {
183
+ "presetter": "latest",
184
+ "@presetter/preset-essentials": "latest",
185
+ "@presetter/preset-web": "latest",
186
+ },
187
+ }
188
+ ```
189
+
190
+ ```typescript
191
+ // presetter.config.ts
192
+ import { preset } from 'presetter';
193
+ import essentials from '@presetter/preset-essentials';
194
+ import web from '@presetter/preset-web';
195
+
196
+ export default preset('my-web-app', {
197
+ extends: [essentials, web], // Base preset + web extension
198
+ });
199
+ ```
200
+
201
+ #### Step 2: Bootstrap & Develop
202
+
203
+ ```bash
204
+ npm install
205
+ # Modern web development stack generated automatically
206
+ # TailwindCSS, Storybook, and browser optimization ready!
207
+ ```
208
+
209
+ That's it! Start developing with the complete modern web stack.
210
+
211
+ ---
212
+
213
+ ### 🧑‍🔬 Advanced Usage: Custom Web Optimizations
214
+
215
+ ```typescript
216
+ // presetter.config.ts
217
+ import { preset } from 'presetter';
218
+ import essentials from '@presetter/preset-essentials';
219
+ import web from '@presetter/preset-web';
220
+
221
+ export default preset('advanced-web-app', {
222
+ extends: [essentials, web],
223
+ override: {
224
+ assets: {
225
+ 'tsconfig.json': {
226
+ compilerOptions: {
227
+ lib: ['DOM', 'DOM.Iterable', 'ES2023'], // Latest features
228
+ target: 'ES2024', // Modern browser target
229
+ },
230
+ },
231
+ 'eslint.config.ts': {
232
+ rules: {
233
+ // Custom TailwindCSS rules
234
+ 'tailwindcss/no-custom-classname': 'warn',
235
+ },
236
+ },
237
+ },
238
+ },
239
+ });
240
+ ```
241
+
242
+ > **Need more customization options?** Check the [main Presetter documentation](https://github.com/alvis/presetter/blob/main/packages/presetter) for complete guides on overrides, extensions, and advanced configurations.
243
+
244
+ ---
245
+
246
+ ## 📖 API Reference
247
+
248
+ ### Core Web Development Extension
249
+
250
+ This preset extends any base preset with modern web development capabilities:
251
+
252
+ | Enhancement | Purpose | Web Features |
253
+ | ---------------------- | --------------------- | -------------------------------------------------------- |
254
+ | **TailwindCSS 4** | Utility-first CSS | Auto-discovery, conflict detection, formatting |
255
+ | **Storybook 9** | Component development | Accessibility testing, Vitest integration, pseudo-states |
256
+ | **Browser TypeScript** | Web APIs | DOM types, modern JavaScript features |
257
+ | **CSS Workflow** | Styling pipeline | PostCSS, autoprefixer, intelligent formatting |
258
+
259
+ ### TailwindCSS Integration
260
+
261
+ #### Auto-Discovery Features
262
+
263
+ ```typescript
264
+ // Automatically discovers entry files in:
265
+ const searchDirectories = [
266
+ 'source',
267
+ 'src',
268
+ 'app',
269
+ 'styles',
270
+ 'assets',
271
+ 'public',
272
+ 'static',
273
+ ];
274
+
275
+ const entryFiles = [
276
+ 'globals.css',
277
+ 'global.css',
278
+ 'index.css',
279
+ 'main.css',
280
+ 'styles.css',
281
+ ];
282
+
283
+ // Scans for @import "tailwindcss" directives
284
+ ```
285
+
286
+ #### Enhanced Linting
287
+
288
+ ```typescript
289
+ // eslint-plugin-better-tailwindcss
290
+ 'tailwindcss/classnames-order': 'warn', // Consistent ordering
291
+ 'tailwindcss/enforces-negative-arbitrary-values': 'warn', // Proper syntax
292
+ 'tailwindcss/no-contradicting-classname': 'error', // Conflict detection
293
+ ```
294
+
295
+ ### Storybook Configuration
296
+
297
+ #### Core Addons Included
298
+
299
+ - **@storybook/addon-a11y**: Accessibility testing and guidelines
300
+ - **@storybook/addon-vitest**: Seamless Vitest integration for component testing
301
+ - **storybook-addon-pseudo-states**: CSS pseudo-state visualization (hover, focus, etc.)
302
+ - **storybook-addon-test-codegen**: Automatic test code generation from stories
303
+
304
+ #### Browser Environment
305
+
306
+ ```typescript
307
+ // Enhanced TypeScript configuration
308
+ compilerOptions: {
309
+ lib: ['DOM', 'DOM.Iterable', 'ESNext'], // Complete browser API support
310
+ }
311
+ ```
312
+
313
+ ### Configuration Variables
314
+
315
+ Inherited from base preset with web-specific enhancements:
316
+
317
+ | Variable | Default | Description |
318
+ | --------------------- | --------------- | --------------------------------------- |
319
+ | Base preset variables | Inherited | All variables from chosen base preset |
320
+ | TailwindCSS entry | Auto-discovered | Automatically found CSS entry points |
321
+ | Config files | Auto-discovered | Automatically found configuration files |
322
+
323
+ ---
324
+
325
+ ## 🔧 Configuration Details
326
+
327
+ ### Intelligent TailwindCSS Discovery
328
+
329
+ #### Entry Point Detection
330
+
331
+ ```typescript
332
+ // Searches common directories for CSS files
333
+ const directories = ['src', 'app', 'styles', 'assets'];
334
+ const filenames = ['globals.css', 'index.css', 'main.css'];
335
+
336
+ // Scans file contents for TailwindCSS imports
337
+ const tailwindImports = /@import\s+["']tailwindcss/;
338
+ ```
339
+
340
+ #### Config File Detection
341
+
342
+ ```typescript
343
+ // Automatically finds configuration files
344
+ const configPatterns = [
345
+ 'tailwind.config.ts',
346
+ 'tailwind.config.js',
347
+ 'tailwind.config.mjs',
348
+ 'tailwind.config.cjs',
349
+ ];
350
+ ```
351
+
352
+ ### Enhanced ESLint Configuration
353
+
354
+ #### Web-Specific Plugins
355
+
356
+ - **eslint-plugin-better-tailwindcss**: Advanced TailwindCSS linting
357
+ - **eslint-plugin-storybook**: Storybook-specific rules
358
+ - **globals**: Browser environment variables
359
+
360
+ #### Browser Globals
361
+
362
+ ```typescript
363
+ globals: {
364
+ window: 'readonly',
365
+ document: 'readonly',
366
+ navigator: 'readonly',
367
+ // ... all browser globals
368
+ }
369
+ ```
370
+
371
+ ### TypeScript Browser Optimization
372
+
373
+ ```yaml
374
+ # tsconfig.json override
375
+ compilerOptions:
376
+ lib:
377
+ - DOM # Browser DOM APIs
378
+ - DOM.Iterable # Iterable DOM elements
379
+ - ESNext # Latest JavaScript features
380
+ ```
381
+
382
+ ---
383
+
384
+ ## 🏎️ Performance
385
+
386
+ | Metric | Standard Preset | With preset-web |
387
+ | ---------------------- | -------------------- | --------------------------------- |
388
+ | Component development | Manual setup | **Storybook visual environment** |
389
+ | CSS workflow | Basic | **Advanced PostCSS pipeline** |
390
+ | Browser compatibility | Manual configuration | **Auto-optimized TypeScript** |
391
+ | Styling conflicts | Manual detection | **Automated TailwindCSS linting** |
392
+ | Development experience | Fragmented tools | **Integrated modern stack** |
393
+
394
+ ---
395
+
396
+ ## 🌐 Compatibility
397
+
398
+ | Environment | Support |
399
+ | ------------ | --------------------------------------- |
400
+ | Base Presets | Works with essentials, esm, cjs, hybrid |
401
+ | Browsers | Modern browsers (ES2024+) |
402
+ | Node.js | ≥ 18 |
403
+ | TypeScript | ≥ 5.0 |
404
+
405
+ ### Works With All Base Presets
406
+
407
+ - [`@presetter/preset-essentials`](../preset-essentials) + web development
408
+ - [`@presetter/preset-esm`](../preset-esm) + web development
409
+ - [`@presetter/preset-cjs`](../preset-cjs) + web development
410
+ - [`@presetter/preset-hybrid`](../preset-hybrid) + web development
411
+
412
+ ### Can Be Combined With
413
+
414
+ - [`@presetter/preset-react`](../preset-react) - React + modern web stack
415
+ - [`@presetter/preset-strict`](../preset-strict) - Web development + strict quality
416
+
417
+ ### Extended By
418
+
419
+ - [`@presetter/preset-react`](../preset-react) - Adds React-specific features to web stack
420
+
421
+ ---
422
+
423
+ ## 🆚 Comparison
424
+
425
+ | Feature | Standard Presets | With preset-web |
426
+ | ------------------------- | ---------------- | ----------------------------- |
427
+ | **CSS Framework** | None | ✅ TailwindCSS 4 |
428
+ | **Component Development** | Basic | ✅ Storybook 9 environment |
429
+ | **Browser Optimization** | Manual | ✅ Auto-configured TypeScript |
430
+ | **CSS Workflow** | Basic | ✅ PostCSS + Autoprefixer |
431
+ | **Visual Development** | CLI only | ✅ Component playground |
432
+ | **Accessibility Testing** | Manual | ✅ Automated Storybook addon |
433
+
434
+ ### When to Use
435
+
436
+ ✅ **Use preset-web when:**
437
+
438
+ - Building web applications or component libraries
439
+ - Need modern CSS workflows (TailwindCSS)
440
+ - Want visual component development (Storybook)
441
+ - Developing for browser environments
442
+ - Need accessibility testing integration
443
+ - Want professional styling workflows
444
+
445
+ ❌ **Consider alternatives when:**
446
+
447
+ - Building Node.js applications or CLIs
448
+ - No visual UI components needed
449
+ - Basic styling requirements only
450
+ - Minimal web development needs
451
+
452
+ ---
453
+
454
+ ## 🛠️ Troubleshooting
455
+
456
+ > **General Presetter issues?** See the [main troubleshooting guide](https://github.com/alvis/presetter/blob/main/README.md#troubleshooting) for common Presetter problems and solutions.
457
+
458
+ ### Web Development Specific Issues
459
+
460
+ | Issue | Symptoms | Solution |
461
+ | ----------------------------------- | --------------------------------- | -------------------------------------------------------------- |
462
+ | **TailwindCSS not auto-discovered** | No CSS framework integration | Ensure CSS files are in common directories (`src/`, `styles/`) |
463
+ | **Storybook not starting** | Component development unavailable | Check that base preset provides build tooling |
464
+ | **Browser types missing** | TypeScript errors with DOM APIs | Ensure TypeScript override is applied correctly |
465
+ | **CSS conflicts undetected** | TailwindCSS class conflicts | Check ESLint TailwindCSS plugin configuration |
466
+
467
+ > **Need help with Presetter CLI commands?** Check the [CLI reference](https://github.com/alvis/presetter/blob/main/README.md#cli-reference) in the main documentation.
468
+
469
+ ---
470
+
471
+ ## ❓ FAQ
472
+
473
+ > **General Presetter questions?** Check the [main FAQ](https://github.com/alvis/presetter/blob/main/README.md#faq) for general usage, configuration, and customization questions.
474
+
475
+ ### Web Development Specific FAQs
476
+
477
+ #### Do I need a base preset?
478
+
479
+ Yes! preset-web is an **extension preset** that adds web tooling to base functionality:
480
+
481
+ ```typescript
482
+ // ❌ Wrong - web alone doesn't provide TypeScript tooling
483
+ extends: [web]
484
+
485
+ // ✅ Correct - base preset + web extension
486
+ extends: [essentials, web]
487
+ ```
488
+
489
+ #### How does TailwindCSS auto-discovery work?
490
+
491
+ The preset intelligently scans your project:
492
+
493
+ ```typescript
494
+ // Searches these directories for CSS files
495
+ ['src', 'app', 'styles', 'assets', 'public', 'static']
496
+
497
+ // Looks for these common CSS file names
498
+ ['globals.css', 'global.css', 'index.css', 'main.css', 'styles.css']
499
+
500
+ // Scans file contents for TailwindCSS imports
501
+ /@import\s+["']tailwindcss/
502
+ ```
503
+
504
+ #### What Storybook addons are included?
505
+
506
+ Professional component development addons:
507
+
508
+ - **Accessibility testing** (`@storybook/addon-a11y`)
509
+ - **Vitest integration** (`@storybook/addon-vitest`)
510
+ - **Pseudo-state testing** (`storybook-addon-pseudo-states`)
511
+ - **Test code generation** (`storybook-addon-test-codegen`)
512
+
513
+ #### Can I use this without TailwindCSS?
514
+
515
+ Yes! The preset provides value beyond TailwindCSS:
516
+
517
+ - Storybook for component development
518
+ - Browser-optimized TypeScript
519
+ - PostCSS workflow
520
+ - Enhanced ESLint for web development
521
+
522
+ #### Is this compatible with React?
523
+
524
+ Absolutely! Use with [`@presetter/preset-react`](../preset-react):
525
+
526
+ ```typescript
527
+ extends: [essentials, web, react] // Full React + web stack
528
+ ```
529
+
530
+ ---
531
+
532
+ ## 🤝 Contributing
533
+
534
+ We'd love your ideas and contributions!
535
+ Submit issues or suggestions via [GitHub Issues](https://github.com/alvis/presetter/issues).
536
+ See the [Contribution Guide](https://github.com/alvis/presetter/blob/main/CONTRIBUTING.md) for more details.
537
+
538
+ ---
539
+
540
+ ## 📄 License
541
+
542
+ Released under the [MIT License](https://github.com/alvis/presetter/blob/main/LICENSE).
543
+ © 2020, [Alvis Tang](https://github.com/alvis).
544
+
545
+ [![License](https://img.shields.io/github/license/alvis/presetter.svg?style=flat-square)](https://github.com/alvis/presetter/blob/main/LICENSE)
@@ -0,0 +1,4 @@
1
+ import type { Linter } from 'eslint';
2
+ declare const _default: Linter.Config[];
3
+ export default _default;
4
+ //# sourceMappingURL=override.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"override.d.ts","sourceRoot":"","sources":["../../src/eslint/override.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;wBAmChC,MAAM,CAAC,MAAM,EAAE"}
@@ -0,0 +1,31 @@
1
+ import tailwind from 'eslint-plugin-better-tailwindcss';
2
+ import { getDefaultSelectors } from 'eslint-plugin-better-tailwindcss/api/defaults';
3
+ const selectors = [
4
+ ...getDefaultSelectors(),
5
+ {
6
+ kind: 'callee',
7
+ name: 'classnames',
8
+ },
9
+ ];
10
+ export default [
11
+ {
12
+ name: '@presetter/preset-web:override:tailwindcss',
13
+ plugins: {
14
+ 'better-tailwindcss': tailwind,
15
+ },
16
+ settings: {
17
+ 'better-tailwindcss': {
18
+ selectors,
19
+ },
20
+ },
21
+ rules: {
22
+ ...tailwind.configs['recommended-warn'].rules,
23
+ 'better-tailwindcss/no-conflicting-classes': 'warn',
24
+ 'better-tailwindcss/no-unknown-classes': [
25
+ 'warn',
26
+ { detectComponentClasses: true },
27
+ ],
28
+ },
29
+ },
30
+ ];
31
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3ZlcnJpZGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZXNsaW50L292ZXJyaWRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sUUFBUSxNQUFNLGtDQUFrQyxDQUFDO0FBQ3hELE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLCtDQUErQyxDQUFDO0FBU3BGLE1BQU0sU0FBUyxHQUFHO0lBQ2hCLEdBQUcsbUJBQW1CLEVBQUU7SUFDeEI7UUFDRSxJQUFJLEVBQUUsUUFBK0I7UUFDckMsSUFBSSxFQUFFLFlBQVk7S0FDTTtDQUNOLENBQUM7QUFFdkIsZUFBZTtJQUNiO1FBQ0UsSUFBSSxFQUFFLDRDQUE0QztRQUNsRCxPQUFPLEVBQUU7WUFDUCxvQkFBb0IsRUFBRSxRQUFRO1NBQy9CO1FBQ0QsUUFBUSxFQUFFO1lBQ1Isb0JBQW9CLEVBQUU7Z0JBQ3BCLFNBQVM7YUFDVjtTQUNGO1FBQ0QsS0FBSyxFQUFFO1lBQ0wsR0FBRyxRQUFRLENBQUMsT0FBTyxDQUFDLGtCQUFrQixDQUFDLENBQUMsS0FBSztZQUM3QywyQ0FBMkMsRUFBRSxNQUFNO1lBQ25ELHVDQUF1QyxFQUFFO2dCQUN2QyxNQUFNO2dCQUNOLEVBQUUsc0JBQXNCLEVBQUUsSUFBSSxFQUFFO2FBQ2pDO1NBQ0Y7S0FDRjtDQUNpQixDQUFDIn0=
@@ -0,0 +1,6 @@
1
+ import type { Linter } from 'eslint';
2
+ declare const _default: import("presetter").PresetContent<{
3
+ default: Linter.Config[];
4
+ }>;
5
+ export default _default;
6
+ //# sourceMappingURL=template.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../src/eslint/template.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;;aAEL,MAAM,CAAC,MAAM,EAAE"}
@@ -0,0 +1,29 @@
1
+ import globals from 'globals';
2
+ import { asset } from 'presetter';
3
+ import { locateTailwindConfigFile, locateTailwindEntryFile } from '#tailwind';
4
+ export default asset(async (current, { projectRoot }) => {
5
+ const entryPoint = await locateTailwindEntryFile(projectRoot);
6
+ const tailwindConfig = await locateTailwindConfigFile(projectRoot);
7
+ return {
8
+ default: [
9
+ ...(current?.default ?? []),
10
+ {
11
+ name: '@presetter/preset-web',
12
+ languageOptions: {
13
+ globals: globals.browser,
14
+ },
15
+ },
16
+ ...(entryPoint
17
+ ? [
18
+ {
19
+ name: '@presetter/preset-web:tailwindcss',
20
+ settings: {
21
+ 'better-tailwindcss': { entryPoint, tailwindConfig },
22
+ },
23
+ },
24
+ ]
25
+ : []),
26
+ ],
27
+ };
28
+ });
29
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVtcGxhdGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZXNsaW50L3RlbXBsYXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sT0FBTyxNQUFNLFNBQVMsQ0FBQztBQUM5QixPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBRWxDLE9BQU8sRUFBRSx3QkFBd0IsRUFBRSx1QkFBdUIsRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUk5RSxlQUFlLEtBQUssQ0FDbEIsS0FBSyxFQUFFLE9BQU8sRUFBRSxFQUFFLFdBQVcsRUFBRSxFQUFFLEVBQUU7SUFDakMsTUFBTSxVQUFVLEdBQUcsTUFBTSx1QkFBdUIsQ0FBQyxXQUFXLENBQUMsQ0FBQztJQUM5RCxNQUFNLGNBQWMsR0FBRyxNQUFNLHdCQUF3QixDQUFDLFdBQVcsQ0FBQyxDQUFDO0lBRW5FLE9BQU87UUFDTCxPQUFPLEVBQUU7WUFDUCxHQUFHLENBQUMsT0FBTyxFQUFFLE9BQU8sSUFBSSxFQUFFLENBQUM7WUFDM0I7Z0JBQ0UsSUFBSSxFQUFFLHVCQUF1QjtnQkFDN0IsZUFBZSxFQUFFO29CQUNmLE9BQU8sRUFBRSxPQUFPLENBQUMsT0FBTztpQkFDekI7YUFDRjtZQUNELEdBQUcsQ0FBQyxVQUFVO2dCQUNaLENBQUMsQ0FBQztvQkFDRTt3QkFDRSxJQUFJLEVBQUUsbUNBQW1DO3dCQUN6QyxRQUFRLEVBQUU7NEJBQ1Isb0JBQW9CLEVBQUUsRUFBRSxVQUFVLEVBQUUsY0FBYyxFQUFFO3lCQUNyRDtxQkFDRjtpQkFDRjtnQkFDSCxDQUFDLENBQUMsRUFBRSxDQUFDO1NBQ1I7S0FDRixDQUFDO0FBQ0osQ0FBQyxDQUNGLENBQUMifQ==
package/lib/index.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ /** list of configurable variables */
2
+ export interface Variables {
3
+ /** the directory containing all extra typing files (default: types) */
4
+ types: string;
5
+ }
6
+ export declare const DEFAULT_VARIABLES: {
7
+ types: string;
8
+ };
9
+ /**
10
+ * get the list of templates provided by this preset
11
+ * @returns list of preset templates
12
+ */
13
+ declare const _default: import("presetter").Preset;
14
+ export default _default;
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,qCAAqC;AACrC,MAAM,WAAW,SAAS;IACxB,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;CACf;AAED,eAAO,MAAM,iBAAiB;;CAET,CAAC;AAKtB;;;GAGG"}
package/lib/index.js ADDED
@@ -0,0 +1,39 @@
1
+ import { dirname, resolve } from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { preset } from '@presetter/types';
4
+ import * as eslintOverride from './eslint/override.js';
5
+ import eslintTemplate from './eslint/template.js';
6
+ const DIR = dirname(fileURLToPath(import.meta.url));
7
+ // paths to the template directories
8
+ const TEMPLATES = resolve(DIR, '..', 'templates');
9
+ const OVERRIDES = resolve(DIR, '..', 'overrides');
10
+ export const DEFAULT_VARIABLES = {
11
+ types: 'types',
12
+ };
13
+ const IMAGE_TYPE = 'image.d.ts';
14
+ const STYLE_TYPE = 'style.d.ts';
15
+ /**
16
+ * get the list of templates provided by this preset
17
+ * @returns list of preset templates
18
+ */
19
+ export default preset('@presetter/preset-web', {
20
+ root: resolve(import.meta.dirname, '..'),
21
+ variables: DEFAULT_VARIABLES,
22
+ assets: ({ variables }) => ({
23
+ '.gitignore': (current) => [
24
+ ...(current ?? []),
25
+ `/${variables.types}/${IMAGE_TYPE}`,
26
+ `/${variables.types}/${STYLE_TYPE}`,
27
+ ],
28
+ 'eslint.config.ts': eslintTemplate,
29
+ [`${variables.types}/${IMAGE_TYPE}`]: resolve(TEMPLATES, IMAGE_TYPE),
30
+ [`${variables.types}/${STYLE_TYPE}`]: resolve(TEMPLATES, STYLE_TYPE),
31
+ }),
32
+ override: {
33
+ assets: {
34
+ 'tsconfig.json': resolve(OVERRIDES, 'tsconfig.yaml'),
35
+ 'eslint.config.ts': eslintOverride,
36
+ },
37
+ },
38
+ });
39
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFDN0MsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUV6QyxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFFMUMsT0FBTyxLQUFLLGNBQWMsTUFBTSxtQkFBbUIsQ0FBQztBQUNwRCxPQUFPLGNBQWMsTUFBTSxtQkFBbUIsQ0FBQztBQUUvQyxNQUFNLEdBQUcsR0FBRyxPQUFPLENBQUMsYUFBYSxDQUFDLE9BQU8sSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7QUFFcEQsb0NBQW9DO0FBQ3BDLE1BQU0sU0FBUyxHQUFHLE9BQU8sQ0FBQyxHQUFHLEVBQUUsSUFBSSxFQUFFLFdBQVcsQ0FBQyxDQUFDO0FBQ2xELE1BQU0sU0FBUyxHQUFHLE9BQU8sQ0FBQyxHQUFHLEVBQUUsSUFBSSxFQUFFLFdBQVcsQ0FBQyxDQUFDO0FBUWxELE1BQU0sQ0FBQyxNQUFNLGlCQUFpQixHQUFHO0lBQy9CLEtBQUssRUFBRSxPQUFPO0NBQ0ssQ0FBQztBQUV0QixNQUFNLFVBQVUsR0FBRyxZQUFZLENBQUM7QUFDaEMsTUFBTSxVQUFVLEdBQUcsWUFBWSxDQUFDO0FBRWhDOzs7R0FHRztBQUNILGVBQWUsTUFBTSxDQUFDLHVCQUF1QixFQUFFO0lBQzdDLElBQUksRUFBRSxPQUFPLENBQUMsT0FBTyxJQUFJLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQztJQUN4QyxTQUFTLEVBQUUsaUJBQWlCO0lBQzVCLE1BQU0sRUFBRSxDQUFDLEVBQUUsU0FBUyxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFDMUIsWUFBWSxFQUFFLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQztZQUN6QixHQUFHLENBQUMsT0FBTyxJQUFJLEVBQUUsQ0FBQztZQUNsQixJQUFJLFNBQVMsQ0FBQyxLQUFNLElBQUksVUFBVSxFQUFFO1lBQ3BDLElBQUksU0FBUyxDQUFDLEtBQU0sSUFBSSxVQUFVLEVBQUU7U0FDckM7UUFDRCxrQkFBa0IsRUFBRSxjQUFjO1FBQ2xDLENBQUMsR0FBRyxTQUFTLENBQUMsS0FBTSxJQUFJLFVBQVUsRUFBa0IsQ0FBQyxFQUFFLE9BQU8sQ0FDNUQsU0FBUyxFQUNULFVBQVUsQ0FDWDtRQUNELENBQUMsR0FBRyxTQUFTLENBQUMsS0FBTSxJQUFJLFVBQVUsRUFBa0IsQ0FBQyxFQUFFLE9BQU8sQ0FDNUQsU0FBUyxFQUNULFVBQVUsQ0FDWDtLQUNGLENBQUM7SUFDRixRQUFRLEVBQUU7UUFDUixNQUFNLEVBQUU7WUFDTixlQUFlLEVBQUUsT0FBTyxDQUFDLFNBQVMsRUFBRSxlQUFlLENBQUM7WUFDcEQsa0JBQWtCLEVBQUUsY0FBYztTQUNuQztLQUNGO0NBQ0YsQ0FBQyxDQUFDIn0=
@@ -0,0 +1,9 @@
1
+ /**
2
+ * resolves a module specifier to its full path using the native import.meta.resolve
3
+ * @param specifier module specifier to resolve (e.g., package name or relative path)
4
+ * @param parent parent URL or path to resolve from
5
+ * @returns The resolved module path
6
+ * @see https://github.com/vitest-dev/vitest/issues/6953#issuecomment-2765228116
7
+ */
8
+ export declare function resolveModule(specifier: string, parent?: string | URL): string;
9
+ //# sourceMappingURL=module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,MAAM,GAAG,GAAG,GACpB,MAAM,CAER"}
package/lib/module.js ADDED
@@ -0,0 +1,12 @@
1
+ /* v8 ignore file -- @preserve */
2
+ /**
3
+ * resolves a module specifier to its full path using the native import.meta.resolve
4
+ * @param specifier module specifier to resolve (e.g., package name or relative path)
5
+ * @param parent parent URL or path to resolve from
6
+ * @returns The resolved module path
7
+ * @see https://github.com/vitest-dev/vitest/issues/6953#issuecomment-2765228116
8
+ */
9
+ export function resolveModule(specifier, parent) {
10
+ return import.meta.resolve(specifier, parent);
11
+ }
12
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kdWxlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL21vZHVsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxpQ0FBaUM7QUFFakM7Ozs7OztHQU1HO0FBQ0gsTUFBTSxVQUFVLGFBQWEsQ0FDM0IsU0FBaUIsRUFDakIsTUFBcUI7SUFFckIsT0FBTyxPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxFQUFFLE1BQU0sQ0FBQyxDQUFDO0FBQ2hELENBQUMifQ==
@@ -0,0 +1,7 @@
1
+ /**
2
+ * locates the tailwind css config file under the root directory
3
+ * @param rootDirectory the root directory to search for tailwindcss config file
4
+ * @returns path to the tailwindcss config file
5
+ */
6
+ export declare function locateTailwindConfigFile(rootDirectory: string): Promise<string | undefined>;
7
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/tailwind/config.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,wBAAsB,wBAAwB,CAC5C,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAc7B"}
@@ -0,0 +1,22 @@
1
+ import { readdir } from 'node:fs/promises';
2
+ import { resolve } from 'node:path';
3
+ /**
4
+ * locates the tailwind css config file under the root directory
5
+ * @param rootDirectory the root directory to search for tailwindcss config file
6
+ * @returns path to the tailwindcss config file
7
+ */
8
+ export async function locateTailwindConfigFile(rootDirectory) {
9
+ try {
10
+ const files = await readdir(rootDirectory);
11
+ for (const file of files) {
12
+ if (file.startsWith('tailwind.config')) {
13
+ return resolve(rootDirectory, file);
14
+ }
15
+ }
16
+ }
17
+ catch {
18
+ // directory can't be read
19
+ }
20
+ return undefined;
21
+ }
22
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3RhaWx3aW5kL2NvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDM0MsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUVwQzs7OztHQUlHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSx3QkFBd0IsQ0FDNUMsYUFBcUI7SUFFckIsSUFBSSxDQUFDO1FBQ0gsTUFBTSxLQUFLLEdBQUcsTUFBTSxPQUFPLENBQUMsYUFBYSxDQUFDLENBQUM7UUFFM0MsS0FBSyxNQUFNLElBQUksSUFBSSxLQUFLLEVBQUUsQ0FBQztZQUN6QixJQUFJLElBQUksQ0FBQyxVQUFVLENBQUMsaUJBQWlCLENBQUMsRUFBRSxDQUFDO2dCQUN2QyxPQUFPLE9BQU8sQ0FBQyxhQUFhLEVBQUUsSUFBSSxDQUFDLENBQUM7WUFDdEMsQ0FBQztRQUNILENBQUM7SUFDSCxDQUFDO0lBQUMsTUFBTSxDQUFDO1FBQ1AsMEJBQTBCO0lBQzVCLENBQUM7SUFFRCxPQUFPLFNBQVMsQ0FBQztBQUNuQixDQUFDIn0=
@@ -0,0 +1,7 @@
1
+ /**
2
+ * locates the main css file that contains the tailwind css import directive
3
+ * @param rootDirectory the root directory to search for css files
4
+ * @returns path to the css file containing `@import "tailwindcss"`
5
+ */
6
+ export declare function locateTailwindEntryFile(rootDirectory: string): Promise<string | undefined>;
7
+ //# sourceMappingURL=entry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entry.d.ts","sourceRoot":"","sources":["../../src/tailwind/entry.ts"],"names":[],"mappings":"AA+CA;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAY7B"}
@@ -0,0 +1,134 @@
1
+ import { readdir, readFile } from 'node:fs/promises';
2
+ import { dirname, resolve } from 'node:path';
3
+ import { resolveModule } from '#module';
4
+ /**
5
+ * resolves an import path to an absolute file path
6
+ * @param importPath the import path from CSS `@import` statement
7
+ * @param currentFilePath the path of the current file being processed
8
+ * @returns resolved absolute file path
9
+ */
10
+ function resolveImportPath(importPath, currentFilePath) {
11
+ return importPath.startsWith('.')
12
+ ? // resolve relative imports
13
+ resolve(dirname(currentFilePath), importPath)
14
+ : // resolve module imports using import.meta.resolve
15
+ resolveModule(importPath, `file://${currentFilePath}`).replace('file://', '');
16
+ }
17
+ /** common source directories where css files are typically located */
18
+ const commonSourceDirectories = [
19
+ '.storybook',
20
+ 'source',
21
+ 'src',
22
+ 'app',
23
+ 'src/app',
24
+ 'styles',
25
+ 'assets',
26
+ 'public',
27
+ 'static',
28
+ ];
29
+ /** common css file names prioritized during search */
30
+ const commonStyleFileNames = [
31
+ 'globals.css',
32
+ 'global.css',
33
+ 'index.css',
34
+ 'main.css',
35
+ 'styles.css',
36
+ ];
37
+ /**
38
+ * locates the main css file that contains the tailwind css import directive
39
+ * @param rootDirectory the root directory to search for css files
40
+ * @returns path to the css file containing `@import "tailwindcss"`
41
+ */
42
+ export async function locateTailwindEntryFile(rootDirectory) {
43
+ // search in common source directories
44
+ for (const sourceDirectory of commonSourceDirectories) {
45
+ const directoryPath = resolve(rootDirectory, sourceDirectory);
46
+ const result = await findTailwindEntryInDirectory(directoryPath);
47
+ if (result) {
48
+ return result;
49
+ }
50
+ }
51
+ // search in root directory
52
+ return findTailwindEntryInDirectory(rootDirectory);
53
+ }
54
+ /**
55
+ * searches for tailwind css entry file in a specific directory
56
+ * @param directoryPath the directory path to search in
57
+ * @returns path to the css file containing `@import "tailwindcss"` or undefined
58
+ */
59
+ async function findTailwindEntryInDirectory(directoryPath) {
60
+ try {
61
+ const files = await readdir(directoryPath);
62
+ // create a prioritized list of files to check
63
+ const filesToCheck = [
64
+ // common style files first (if they exist)
65
+ ...commonStyleFileNames.filter((name) => files.includes(name)),
66
+ // then all other CSS files
67
+ ...files.filter((file) => file.endsWith('.css') && !commonStyleFileNames.includes(file)),
68
+ ];
69
+ // check each file for tailwind import
70
+ for (const file of filesToCheck) {
71
+ const filePath = resolve(directoryPath, file);
72
+ if (await hasTailwindImport(filePath)) {
73
+ return filePath;
74
+ }
75
+ }
76
+ }
77
+ catch {
78
+ // directory doesn't exist or can't be read
79
+ }
80
+ return undefined;
81
+ }
82
+ /**
83
+ * checks if a css file contains tailwind import directive, following local imports recursively
84
+ * @param path the path to the css file to check
85
+ * @returns true if the file contains `@import "tailwindcss"` directly or indirectly
86
+ */
87
+ async function hasTailwindImport(path) {
88
+ return checkTailwindImportRecursively(path, new Set());
89
+ }
90
+ /**
91
+ * internal recursive helper for hasTailwindImport
92
+ * @param path the path to the css file to check
93
+ * @param visited set of already visited files to prevent infinite loops
94
+ * @returns true if the file contains `@import "tailwindcss"` directly or indirectly
95
+ */
96
+ async function checkTailwindImportRecursively(path, visited) {
97
+ try {
98
+ if (visited.has(path)) {
99
+ return false;
100
+ }
101
+ visited.add(path);
102
+ const content = await readFile(path, 'utf-8');
103
+ // check for direct tailwind import
104
+ if (/@import\s+["']tailwindcss/.exec(content) !== null) {
105
+ return true;
106
+ }
107
+ // find all CSS imports and check them recursively
108
+ const importRegex = /@import\s+["']([^"']+)["']/g;
109
+ let match;
110
+ while ((match = importRegex.exec(content)) !== null) {
111
+ const importPath = match[1];
112
+ // skip URL imports (http/https)
113
+ if (/^https?:\/\//.test(importPath)) {
114
+ continue;
115
+ }
116
+ try {
117
+ const resolvedImportPath = resolveImportPath(importPath, path);
118
+ // recursively check the imported file
119
+ if (await checkTailwindImportRecursively(resolvedImportPath, visited)) {
120
+ return true;
121
+ }
122
+ }
123
+ catch {
124
+ // if import resolution fails, skip this import
125
+ continue;
126
+ }
127
+ }
128
+ return false;
129
+ }
130
+ catch {
131
+ return false;
132
+ }
133
+ }
134
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW50cnkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdGFpbHdpbmQvZW50cnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUNyRCxPQUFPLEVBQUUsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUU3QyxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBRXhDOzs7OztHQUtHO0FBQ0gsU0FBUyxpQkFBaUIsQ0FDeEIsVUFBa0IsRUFDbEIsZUFBdUI7SUFFdkIsT0FBTyxVQUFVLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQztRQUMvQixDQUFDLENBQUMsMkJBQTJCO1lBQzNCLE9BQU8sQ0FBQyxPQUFPLENBQUMsZUFBZSxDQUFDLEVBQUUsVUFBVSxDQUFDO1FBQy9DLENBQUMsQ0FBQyxtREFBbUQ7WUFDbkQsYUFBYSxDQUFDLFVBQVUsRUFBRSxVQUFVLGVBQWUsRUFBRSxDQUFDLENBQUMsT0FBTyxDQUM1RCxTQUFTLEVBQ1QsRUFBRSxDQUNILENBQUM7QUFDUixDQUFDO0FBRUQsc0VBQXNFO0FBQ3RFLE1BQU0sdUJBQXVCLEdBQUc7SUFDOUIsWUFBWTtJQUNaLFFBQVE7SUFDUixLQUFLO0lBQ0wsS0FBSztJQUNMLFNBQVM7SUFDVCxRQUFRO0lBQ1IsUUFBUTtJQUNSLFFBQVE7SUFDUixRQUFRO0NBQ1QsQ0FBQztBQUVGLHNEQUFzRDtBQUN0RCxNQUFNLG9CQUFvQixHQUFHO0lBQzNCLGFBQWE7SUFDYixZQUFZO0lBQ1osV0FBVztJQUNYLFVBQVU7SUFDVixZQUFZO0NBQ2IsQ0FBQztBQUVGOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLHVCQUF1QixDQUMzQyxhQUFxQjtJQUVyQixzQ0FBc0M7SUFDdEMsS0FBSyxNQUFNLGVBQWUsSUFBSSx1QkFBdUIsRUFBRSxDQUFDO1FBQ3RELE1B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
@@ -0,0 +1,3 @@
1
+ export { locateTailwindConfigFile } from './config.js';
2
+ export { locateTailwindEntryFile } from './entry.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tailwind/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC"}
@@ -0,0 +1,4 @@
1
+ /* v8 ignore start */
2
+ export { locateTailwindConfigFile } from './config.js';
3
+ export { locateTailwindEntryFile } from './entry.js';
4
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdGFpbHdpbmQvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEscUJBQXFCO0FBRXJCLE9BQU8sRUFBRSx3QkFBd0IsRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUNwRCxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSxTQUFTLENBQUMifQ==
@@ -0,0 +1,6 @@
1
+ compilerOptions:
2
+ # libraries
3
+ lib:
4
+ - DOM
5
+ - DOM.Iterable
6
+ - ESNext
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@presetter/preset-web",
3
+ "version": "9.0.1",
4
+ "description": "An opinionated presetter preset for a web project",
5
+ "keywords": [
6
+ "presetter",
7
+ "preset"
8
+ ],
9
+ "homepage": "https://github.com/alvis/presetter#readme",
10
+ "bugs": {
11
+ "url": "https://github.com/alvis/presetter/issues"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/alvis/presetter.git"
16
+ },
17
+ "license": "MIT",
18
+ "author": "Alvis HT Tang <alvis@hilbert.space>",
19
+ "type": "module",
20
+ "imports": {
21
+ "#*": "./lib/*.js",
22
+ "#tailwind": "./lib/tailwind/index.js"
23
+ },
24
+ "exports": {
25
+ ".": "./lib/index.js"
26
+ },
27
+ "main": "lib/index.js",
28
+ "types": "lib/index.d.ts",
29
+ "dependencies": {
30
+ "@presetter/types": "9.0.1"
31
+ },
32
+ "devDependencies": {
33
+ "@presetter/preset-essentials": "9.0.1",
34
+ "presetter": "9.0.1"
35
+ },
36
+ "peerDependencies": {
37
+ "autoprefixer": "^10.0.0",
38
+ "eslint-plugin-better-tailwindcss": "^4.0.0",
39
+ "globals": "^17.0.0",
40
+ "postcss": "^8.0.0",
41
+ "tailwindcss": "^4.0.0",
42
+ "vitest": "^4.0.0",
43
+ "presetter": "9.0.1"
44
+ },
45
+ "engines": {
46
+ "node": ">=20.0.0"
47
+ },
48
+ "publishConfig": {
49
+ "access": "public"
50
+ },
51
+ "scripts": {
52
+ "build": "run build",
53
+ "lint": "run lint --",
54
+ "test": "run test --",
55
+ "test:coverage": "run test:coverage --",
56
+ "test:watch": "run test:watch --",
57
+ "typecheck": "run typecheck --"
58
+ }
59
+ }
@@ -0,0 +1,48 @@
1
+ /// <reference types="react" />
2
+ /// <reference types="react-dom" />
3
+
4
+ declare module '*.avif' {
5
+ const src: string;
6
+ export default src;
7
+ }
8
+
9
+ declare module '*.bmp' {
10
+ const src: string;
11
+ export default src;
12
+ }
13
+
14
+ declare module '*.gif' {
15
+ const src: string;
16
+ export default src;
17
+ }
18
+
19
+ declare module '*.jpg' {
20
+ const src: string;
21
+ export default src;
22
+ }
23
+
24
+ declare module '*.jpeg' {
25
+ const src: string;
26
+ export default src;
27
+ }
28
+
29
+ declare module '*.png' {
30
+ const src: string;
31
+ export default src;
32
+ }
33
+
34
+ declare module '*.webp' {
35
+ const src: string;
36
+ export default src;
37
+ }
38
+
39
+ declare module '*.svg' {
40
+ import * as React from 'react';
41
+
42
+ export const ReactComponent: React.FunctionComponent<
43
+ React.SVGProps<SVGSVGElement> & { title?: string }
44
+ >;
45
+
46
+ const src: string;
47
+ export default src;
48
+ }
@@ -0,0 +1,19 @@
1
+ declare module '*.css' {
2
+ const classes: { readonly [key: string]: string };
3
+ export default classes;
4
+ }
5
+
6
+ declare module '*.less' {
7
+ const classes: { readonly [key: string]: string };
8
+ export default classes;
9
+ }
10
+
11
+ declare module '*.scss' {
12
+ const classes: { readonly [key: string]: string };
13
+ export default classes;
14
+ }
15
+
16
+ declare module '*.sass' {
17
+ const classes: { readonly [key: string]: string };
18
+ export default classes;
19
+ }