vite-file-include 1.1.1 → 1.2.0

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.
Files changed (3) hide show
  1. package/README.md +621 -86
  2. package/package.json +1 -1
  3. package/src/index.js +394 -311
package/README.md CHANGED
@@ -1,87 +1,121 @@
1
- # vite-file-include 🔗
1
+ # vite-file-include
2
2
 
3
- `vite-file-include` is a modern **Vite plugin** for HTML templating that supports **file inclusion**, **looping**, **conditional rendering**, and **live hot-reload** without reloading the full page.
4
- It’s ideal for managing repetitive HTML structures in static sites or prototyping environments.
3
+ `vite-file-include` is a modern **Vite plugin** for HTML templating that supports **file inclusion**, **looping**, **conditional rendering**, and **true hot module replacement (HMR)** for rapid static site development.
4
+ Perfect for managing repetitive HTML structures in static sites or prototyping environments.
5
5
 
6
6
  ---
7
7
 
8
- ## 🚀 Features
8
+ ## Features
9
9
 
10
- - 🧩 **Nested file includes** with variable support
11
- - 🔁 **Loop rendering** for data arrays and JSON files
12
- - ⚙️ **Conditional blocks** using inline JavaScript
13
- - 🧠 **Custom helper functions** for advanced templating
14
- - ⚡ **JavaScript expression evaluation** inside templates
15
- - 🔄 **Hot reload support** live updates without full page refresh
16
- - 🧵 **Async file processing** and better performance
17
- - 🪶 **Enhanced error reporting** with context hints
10
+ - Nested file includes with variable support and data passing
11
+ - Loop rendering for data arrays and JSON files
12
+ - Conditional blocks using inline JavaScript expressions
13
+ - Custom helper functions for advanced templating
14
+ - JavaScript expression evaluation inside templates
15
+ - **True Hot Module Replacement (HMR)** - updates without full page reload
16
+ - Loop indices (\_index, \_total) for enhanced iteration control
17
+ - Circular include detection - prevents infinite loops
18
+ - Infinite loop protection with configurable iteration limits
19
+ - Enhanced error reporting with detailed file path logging
20
+ - Runtime API for dynamic function and context updates
18
21
 
19
22
  ---
20
23
 
21
- ## 📦 Installation
24
+ ## Installation
22
25
 
23
26
  ```bash
24
27
  npm install vite-file-include --save-dev
25
28
  ```
26
29
 
30
+ Or manually copy the plugin file to your project.
31
+
27
32
  ---
28
33
 
29
- ## ⚙️ Configuration
34
+ ## Configuration
30
35
 
31
36
  Add the plugin to your `vite.config.js`:
32
37
 
33
38
  ```js
34
- import { defineConfig } from 'vite'
35
- import fileIncludePlugin from 'vite-file-include'
39
+ import { defineConfig } from "vite";
40
+ import fileIncludePlugin from "./vite-plugin-file-include.js";
41
+ import path from "path";
42
+ import fs from "fs";
43
+ import { fileURLToPath } from "url";
44
+
45
+ const __filename = fileURLToPath(import.meta.url);
46
+ const __dirname = path.dirname(__filename);
36
47
 
37
48
  export default defineConfig({
38
49
  plugins: [
39
50
  fileIncludePlugin({
40
- includePattern: "@@include",
41
- loopPattern: "@@loop",
42
- ifPattern: "@@if",
43
- baseDir: process.cwd(),
51
+ baseDir: "./src",
44
52
  context: {
45
- siteName: 'My Static Site'
53
+ siteName: "My Static Site",
54
+ showFooter: true,
46
55
  },
47
56
  customFunctions: {
48
57
  uppercase: (str) => str.toUpperCase(),
49
- currentYear: () => new Date().getFullYear()
50
- }
51
- })
52
- ]
53
- })
58
+ currentYear: () => new Date().getFullYear(),
59
+ loadSvg: function (svgFile, classes = "") {
60
+ try {
61
+ const svgPath = path.join(
62
+ __dirname,
63
+ "src/assets/images/icons",
64
+ svgFile
65
+ );
66
+ if (!fs.existsSync(svgPath)) {
67
+ console.error(`SVG not found: ${svgPath}`);
68
+ return "";
69
+ }
70
+ const svgContent = fs
71
+ .readFileSync(svgPath, "utf-8")
72
+ .replace(/<\?xml.*?\?>/g, "")
73
+ .replace(/<!--[\s\S]*?-->/g, "")
74
+ .trim();
75
+ return `<span class='app-icon ${classes}'>${svgContent}</span>`;
76
+ } catch (error) {
77
+ console.error(`Error loading SVG: ${svgFile}`, error);
78
+ return "";
79
+ }
80
+ },
81
+ },
82
+ }),
83
+ ],
84
+ });
54
85
  ```
55
86
 
56
87
  ---
57
88
 
58
- ## 🧩 Plugin Options
89
+ ## Plugin Options
59
90
 
60
- | Option | Type | Default | Description |
61
- |--------|------|----------|-------------|
62
- | **`includePattern`** | `string` | `@@include` | Directive for including files |
63
- | **`loopPattern`** | `string` | `@@loop` | Directive for looping over arrays/JSON |
64
- | **`ifPattern`** | `string` | `@@if` | Directive for conditional rendering |
65
- | **`baseDir`** | `string` | `process.cwd()` | Base directory for resolving paths |
66
- | **`context`** | `object` | `{}` | Global variables accessible in templates |
67
- | **`customFunctions`** | `object` | `{}` | Custom functions available in templates |
91
+ | Option | Type | Default | Description |
92
+ | --------------------- | -------- | --------------- | -------------------------------------------- |
93
+ | **`includePattern`** | `string` | `@@include` | Directive for including files |
94
+ | **`loopPattern`** | `string` | `@@loop` | Directive for looping over arrays/JSON |
95
+ | **`ifPattern`** | `string` | `@@if` | Directive for conditional rendering |
96
+ | **`baseDir`** | `string` | `process.cwd()` | Base directory for resolving paths |
97
+ | **`context`** | `object` | `{}` | Global variables accessible in all templates |
98
+ | **`customFunctions`** | `object` | `{}` | Custom functions callable in templates |
68
99
 
69
100
  ---
70
101
 
71
- ## 🧱 Directives
102
+ ## Directives
72
103
 
73
- ### 🔹 `@@include`
104
+ ### `@@include`
74
105
 
75
106
  Include another HTML file into your main file.
76
107
 
108
+ **Basic usage:**
109
+
77
110
  ```html
78
111
  @@include('partials/header.html')
79
112
  ```
80
113
 
81
- With data:
114
+ **With data:**
82
115
 
83
116
  ```html
84
- @@include('partials/header.html', { "title": "Home Page" })
117
+ @@include('partials/header.html', { "title": "Home Page", "subtitle": "Welcome"
118
+ })
85
119
  ```
86
120
 
87
121
  **Example** (`partials/header.html`):
@@ -89,140 +123,641 @@ With data:
89
123
  ```html
90
124
  <header>
91
125
  <h1>{{ title }}</h1>
126
+ <p>{{ subtitle }}</p>
92
127
  </header>
93
128
  ```
94
129
 
130
+ **Nested includes:**
131
+
132
+ ```html
133
+ <!-- main.html -->
134
+ @@include('partials/layout.html', { "page": "home" })
135
+
136
+ <!-- partials/layout.html -->
137
+ <div class="layout">
138
+ @@include('sections/header.html')
139
+ <main>{{ page }} content</main>
140
+ </div>
141
+ ```
142
+
95
143
  ---
96
144
 
97
- ### 🔹 `@@loop`
145
+ ### `@@loop`
98
146
 
99
147
  Repeat an HTML block for each item in a data array or JSON file.
100
148
 
149
+ **From JSON file:**
150
+
101
151
  ```html
102
152
  @@loop('partials/article.html', 'data/articles.json')
103
153
  ```
104
154
 
105
- Or inline data:
155
+ **Inline data:**
106
156
 
107
157
  ```html
108
- @@loop('partials/article.html', [
109
- { "title": "Article 1" },
110
- { "title": "Article 2" }
111
- ])
158
+ @@loop('partials/card.html', [ { "title": "Card 1", "description": "First card"
159
+ }, { "title": "Card 2", "description": "Second card" } ])
112
160
  ```
113
161
 
114
162
  **Example** (`partials/article.html`):
115
163
 
116
164
  ```html
117
- <article>
165
+ <article class="post">
118
166
  <h2>{{ title }}</h2>
167
+ <p>{{ description }}</p>
168
+ <span>{{ author }} - {{ date }}</span>
169
+ <small>Item {{ _index + 1 }} of {{ _total }}</small>
119
170
  </article>
120
171
  ```
121
172
 
173
+ **Loop variables:**
174
+
175
+ - `{{ _index }}` - Current item index (0-based)
176
+ - `{{ _total }}` - Total number of items
177
+
178
+ **Example** (`data/articles.json`):
179
+
180
+ ```json
181
+ [
182
+ {
183
+ "title": "Getting Started with Vite",
184
+ "description": "Learn the basics",
185
+ "author": "John Doe",
186
+ "date": "2025-01-15"
187
+ },
188
+ {
189
+ "title": "Advanced Templating",
190
+ "description": "Pro tips and tricks",
191
+ "author": "Jane Smith",
192
+ "date": "2025-01-20"
193
+ }
194
+ ]
195
+ ```
196
+
122
197
  ---
123
198
 
124
- ### 🔹 `@@if`
199
+ ### `@@if`
125
200
 
126
- Conditionally render content based on an expression.
201
+ Conditionally render content based on JavaScript expressions.
202
+
203
+ **Basic condition:**
127
204
 
128
205
  ```html
129
- @@if(showFooter) {
130
- @@include('partials/footer.html')
131
- };
206
+ @@if(showFooter) { @@include('partials/footer.html') }
132
207
  ```
133
208
 
134
- **Example:**
209
+ **Complex conditions:**
135
210
 
136
211
  ```html
137
- @@if(user.isLoggedIn) {
138
- <p>Welcome, {{ user.name }}</p>
139
- };
212
+ @@if(user.isLoggedIn && user.role === 'admin') {
213
+ <div class="admin-panel">
214
+ <h2>Admin Controls</h2>
215
+ </div>
216
+ }
217
+ ```
218
+
219
+ **With includes and loops:**
220
+
221
+ ```html
222
+ @@if(articles.length > 0) { @@loop('partials/article.html',
223
+ 'data/articles.json') }
140
224
  ```
141
225
 
142
226
  ---
143
227
 
144
- ## 🧮 JavaScript Expressions
228
+ ## Variable Interpolation
145
229
 
146
- Use JS directly inside templates:
230
+ Use double curly braces `{{ }}` for dynamic content:
231
+
232
+ **Simple variables:**
233
+
234
+ ```html
235
+ <h1>{{ title }}</h1>
236
+ <p>{{ description }}</p>
237
+ ```
238
+
239
+ **JavaScript expressions:**
147
240
 
148
241
  ```html
149
242
  <p>Year: {{ new Date().getFullYear() }}</p>
150
243
  <p>Uppercase: {{ 'vite'.toUpperCase() }}</p>
244
+ <p>Math: {{ 5 + 3 * 2 }}</p>
245
+ <p>Ternary: {{ isActive ? 'Active' : 'Inactive' }}</p>
246
+ ```
247
+
248
+ **Accessing nested properties:**
249
+
250
+ ```html
251
+ <p>{{ user.profile.name }}</p>
252
+ <p>{{ config.api.endpoint }}</p>
151
253
  ```
152
254
 
153
255
  ---
154
256
 
155
- ## 🧰 Custom Functions
257
+ ## Custom Functions
156
258
 
157
- Define reusable helpers in your config:
259
+ Define reusable helper functions in your config:
158
260
 
159
261
  ```js
160
262
  customFunctions: {
161
263
  uppercase: (str) => str.toUpperCase(),
162
264
  currentYear: () => new Date().getFullYear(),
265
+ formatDate: (date) => new Date(date).toLocaleDateString(),
266
+ truncate: (str, len) => str.length > len ? str.slice(0, len) + '...' : str,
267
+ loadSvg: (file, classes = '') => {
268
+ // Load and inline SVG files
269
+ }
163
270
  }
164
271
  ```
165
272
 
166
- Usage:
273
+ **Usage in templates:**
167
274
 
168
275
  ```html
169
276
  <h1>{{ uppercase(title) }}</h1>
170
- <footer>&copy; {{ currentYear() }}</footer>
277
+ <footer>&copy; {{ currentYear() }} {{ siteName }}</footer>
278
+ <p>{{ formatDate('2025-01-15') }}</p>
279
+ <p>{{ truncate(description, 100) }}</p>
280
+ <button>{{ loadSvg('icon-search.svg', 'icon-sm') }} Search</button>
281
+ ```
282
+
283
+ ---
284
+
285
+ ## Hot Module Replacement (HMR)
286
+
287
+ The plugin features **true HMR** that updates your HTML without full page reload:
288
+
289
+ **What makes it special:**
290
+
291
+ - Updates HTML changes instantly without page reload
292
+ - Preserves JavaScript state (variables, counters, timers)
293
+ - Maintains scroll position during updates
294
+ - Keeps form data intact (no lost input)
295
+ - Tracks dependencies between HTML files
296
+ - Smart DOM content replacement
297
+ - Automatic fallback to full reload on errors
298
+
299
+ **How it works:**
300
+
301
+ 1. Edit any HTML file (main pages or partials)
302
+ 2. Plugin detects the change and invalidates affected modules
303
+ 3. Sends custom HMR event to the browser
304
+ 4. Browser fetches updated HTML via fetch
305
+ 5. Parses new HTML with DOMParser
306
+ 6. Replaces body content while preserving state
307
+ 7. Restores scroll position automatically
308
+ 8. Re-executes necessary scripts
309
+
310
+ **Technical implementation:**
311
+
312
+ ```javascript
313
+ // Automatically injected HMR client
314
+ if (import.meta.hot) {
315
+ import.meta.hot.on("vite-file-include:update", async (data) => {
316
+ // Fetch updated HTML
317
+ const response = await fetch(window.location.pathname);
318
+ const html = await response.text();
319
+
320
+ // Parse and update body
321
+ const parser = new DOMParser();
322
+ const newDoc = parser.parseFromString(html, "text/html");
323
+
324
+ // Preserve state and update content
325
+ const scrollPos = window.scrollY;
326
+ document.body.innerHTML = newDoc.body.innerHTML;
327
+ window.scrollTo(0, scrollPos);
328
+ });
329
+ }
330
+ ```
331
+
332
+ **Console output:**
333
+
334
+ ```
335
+ [vite-file-include] HTML changed: header.html
336
+ [HMR] HTML file updated: header.html
337
+ [HMR] Content updated successfully
338
+ ```
339
+
340
+ **Testing HMR:**
341
+
342
+ Add a counter to test state preservation:
343
+
344
+ ```html
345
+ <button onclick="this.textContent = parseInt(this.textContent || 0) + 1">
346
+ 0
347
+ </button>
348
+ ```
349
+
350
+ Click it several times, then edit any HTML file. The counter value will remain unchanged, proving no page reload occurred.
351
+
352
+ **Custom HMR events:**
353
+
354
+ Listen for updates in your JavaScript:
355
+
356
+ ```javascript
357
+ if (import.meta.hot) {
358
+ import.meta.hot.on("vite-file-include:update", (data) => {
359
+ console.log("HTML updated:", data.file, data.timestamp);
360
+ // Your custom logic here
361
+ });
362
+ }
171
363
  ```
172
364
 
365
+ **Error handling:**
366
+
367
+ If HMR update fails for any reason, the plugin automatically falls back to a full page reload to ensure the page is never in a broken state.
368
+
173
369
  ---
174
370
 
175
- ## 🔄 Hot Reload
371
+ ## Runtime API
372
+
373
+ Access plugin features at runtime:
374
+
375
+ ```javascript
376
+ // In vite.config.js
377
+ const plugin = fileIncludePlugin(options);
176
378
 
177
- Unlike static include tools, `vite-file-include` supports **Vite’s HMR (Hot Module Replacement)**.
379
+ // Add functions dynamically
380
+ plugin.api.addFunction("customHelper", (val) => val * 2);
178
381
 
179
- - Changes to included files update **instantly** in the browser
180
- - No full page reload
181
- - Works seamlessly with Vite’s dev server
382
+ // Update context at runtime
383
+ plugin.api.updateContext({ newVar: "value" });
182
384
 
183
- 💡 Tip: Useful for quickly editing partials like headers, footers, and repeating components.
385
+ // Access processor directly
386
+ const processor = plugin.api.processor;
387
+ ```
184
388
 
185
389
  ---
186
390
 
187
- ## 🧰 Example Project Structure
391
+ ## Example Project Structure
188
392
 
189
393
  ```
190
394
  project/
191
- ├─ index.html
192
- ├─ partials/
193
- ├─ header.html
194
- ├─ footer.html
195
- └─ article.html
196
- ├─ data/
197
- └─ articles.json
198
- └─ vite.config.js
395
+ ├─ src/
396
+ ├─ assets/
397
+ │ └─ images/
398
+ │ └─ icons/
399
+ │ ├─ calendar.svg
400
+ │ │ └─ search.svg
401
+ ├─ data/
402
+ │ │ ├─ articles.json
403
+ │ │ └─ team.json
404
+ │ ├─ partials/
405
+ │ │ ├─ layouts/
406
+ │ │ │ ├─ header.html
407
+ │ │ │ └─ footer.html
408
+ │ │ ├─ sections/
409
+ │ │ │ ├─ hero.html
410
+ │ │ │ └─ features.html
411
+ │ │ └─ components/
412
+ │ │ ├─ card.html
413
+ │ │ └─ button.html
414
+ │ ├─ index.html
415
+ │ └─ about.html
416
+ ├─ vite.config.js
417
+ └─ vite-plugin-file-include.js
199
418
  ```
200
419
 
201
- **index.html**
420
+ ### Example Files
421
+
422
+ **src/index.html:**
202
423
 
203
424
  ```html
425
+ <!DOCTYPE html>
426
+ <html lang="en">
427
+ @@include('partials/layouts/header.html', { "title": "Home - My Site",
428
+ "metaDescription": "Welcome to our site" })
429
+ <body>
430
+ @@include('partials/sections/hero.html')
431
+
432
+ <section class="articles">
433
+ <h2>Latest Articles</h2>
434
+ @@loop('partials/components/card.html', 'data/articles.json')
435
+ </section>
436
+
437
+ @@if(showTeamSection) {
438
+ <section class="team">
439
+ <h2>Our Team</h2>
440
+ @@loop('partials/components/team-member.html', 'data/team.json')
441
+ </section>
442
+ } @@include('partials/layouts/footer.html')
443
+ </body>
444
+ </html>
445
+ ```
446
+
447
+ **src/partials/components/card.html:**
448
+
449
+ ```html
450
+ <article class="card">
451
+ <div class="card-icon">{{ loadSvg(icon, 'icon-large') }}</div>
452
+ <h3>{{ title }}</h3>
453
+ <p>{{ truncate(description, 120) }}</p>
454
+ <time>{{ formatDate(date) }}</time>
455
+ </article>
456
+ ```
457
+
458
+ **src/data/articles.json:**
459
+
460
+ ```json
461
+ [
462
+ {
463
+ "icon": "calendar.svg",
464
+ "title": "Getting Started",
465
+ "description": "Learn how to set up your first project with this comprehensive guide covering all the basics you need to know.",
466
+ "date": "2025-01-15"
467
+ },
468
+ {
469
+ "icon": "search.svg",
470
+ "title": "Advanced Features",
471
+ "description": "Explore powerful features and best practices for building scalable applications.",
472
+ "date": "2025-01-20"
473
+ }
474
+ ]
475
+ ```
476
+
477
+ ---
478
+
479
+ ## 🛡️ Error Handling & Security
480
+
481
+ ### Circular Include Detection
482
+
483
+ The plugin automatically detects and prevents circular includes:
484
+
485
+ ```
486
+ ⚠️ Circular include detected: /path/to/file.html
487
+ ```
488
+
489
+ ### Error Messages
490
+
491
+ Detailed error logging for common issues:
492
+
493
+ - **Missing files:** `Failed to include file: /path/to/missing.html`
494
+ - **Invalid JSON:** `Failed to parse JSON data: {...}`
495
+ - **Failed expressions:** `Failed to evaluate expression: invalidVar`
496
+ - **Missing SVGs:** `SVG file not found at path: /path/to/icon.svg`
497
+
498
+ ### Security Note
499
+
500
+ The plugin uses `new Function()` to evaluate expressions. Only use trusted content in your templates and avoid user-generated input in template expressions.
501
+
502
+ ---
503
+
504
+ ## 📚 Usage Examples
505
+
506
+ ### Basic Template Usage
507
+
508
+ **Simple page with includes:**
509
+
510
+ ```html
511
+ <!DOCTYPE html>
204
512
  <html>
513
+ @@include('partials/head.html', { "pageTitle": "Home" })
205
514
  <body>
206
- @@include('partials/header.html', { "title": "My Site" })
207
- @@loop('partials/article.html', 'data/articles.json')
208
- @@if(showFooter) { @@include('partials/footer.html') };
515
+ @@include('partials/header.html')
516
+ <main>
517
+ <h1>{{ pageTitle }}</h1>
518
+ </main>
519
+ @@include('partials/footer.html')
209
520
  </body>
210
521
  </html>
211
522
  ```
212
523
 
524
+ ### Loop with JSON File
525
+
526
+ **Display blog posts:**
527
+
528
+ ```html
529
+ <section class="blog">
530
+ <h2>Latest Posts</h2>
531
+ <div class="posts-grid">
532
+ @@loop('partials/post-card.html', 'data/posts.json')
533
+ </div>
534
+ </section>
535
+ ```
536
+
537
+ **data/posts.json:**
538
+
539
+ ```json
540
+ [
541
+ {
542
+ "title": "Getting Started with Vite",
543
+ "excerpt": "Learn the basics of Vite...",
544
+ "author": "John Doe",
545
+ "date": "2025-01-15",
546
+ "image": "vite-intro.jpg"
547
+ }
548
+ ]
549
+ ```
550
+
551
+ **partials/post-card.html:**
552
+
553
+ ```html
554
+ <article class="post-card">
555
+ <img src="/images/{{ image }}" alt="{{ title }}" />
556
+ <h3>{{ title }}</h3>
557
+ <p>{{ excerpt }}</p>
558
+ <div class="meta">
559
+ <span>{{ author }}</span>
560
+ <time>{{ formatDate(date) }}</time>
561
+ </div>
562
+ </article>
563
+ ```
564
+
565
+ ### Inline Loop Data
566
+
567
+ **Quick lists without JSON files:**
568
+
569
+ ```html
570
+ <ul class="features">
571
+ @@loop('partials/feature-item.html', [ { "icon": "speed.svg", "title": "Fast",
572
+ "desc": "Lightning quick builds" }, { "icon": "power.svg", "title":
573
+ "Powerful", "desc": "Full ES6+ support" }, { "icon": "simple.svg", "title":
574
+ "Simple", "desc": "Easy configuration" } ])
575
+ </ul>
576
+ ```
577
+
578
+ ### Conditional Rendering
579
+
580
+ **Show/hide sections based on config:**
581
+
582
+ ```html
583
+ @@if(showBanner) {
584
+ <div class="banner">
585
+ <p>{{ bannerMessage }}</p>
586
+ </div>
587
+ } @@if(userLoggedIn) { @@include('partials/dashboard.html') }
588
+ @@if(!userLoggedIn) { @@include('partials/login-form.html') }
589
+ ```
590
+
591
+ ### Custom Functions in Templates
592
+
593
+ **Using helper functions:**
594
+
595
+ ```html
596
+ <header>
597
+ <h1>{{ uppercase(siteName) }}</h1>
598
+ <p>&copy; {{ currentYear() }} All rights reserved</p>
599
+ </header>
600
+
601
+ <article>
602
+ <p>{{ truncate(longDescription, 150) }}</p>
603
+ <time>{{ formatDate(publishDate) }}</time>
604
+ </article>
605
+
606
+ <button>{{ loadSvg('icons/search.svg', 'icon-sm') }} Search</button>
607
+ ```
608
+
609
+ ### Nested Includes
610
+
611
+ **Build complex layouts:**
612
+
613
+ ```html
614
+ <!-- index.html -->
615
+ @@include('layouts/base.html', { "pageClass": "home-page", "contentFile":
616
+ "sections/home-content.html" })
617
+
618
+ <!-- layouts/base.html -->
619
+ <!DOCTYPE html>
620
+ <html>
621
+ @@include('partials/head.html')
622
+ <body class="{{ pageClass }}">
623
+ @@include('partials/header.html')
624
+ <main>@@include(contentFile)</main>
625
+ @@include('partials/footer.html')
626
+ </body>
627
+ </html>
628
+ ```
629
+
630
+ ### Dynamic SVG Icons
631
+
632
+ **Load SVG icons inline:**
633
+
634
+ ```html
635
+ <!-- Configuration in vite.config.js -->
636
+ customFunctions: { loadSvg: (file, classes = '') => { const svgPath =
637
+ path.join(__dirname, 'src/assets/icons', file); const svg =
638
+ fs.readFileSync(svgPath, 'utf-8') .replace(/<\?xml.*?\?>/g, '') .trim(); return
639
+ `<span class="icon ${classes}">${svg}</span>`; } }
640
+
641
+ <!-- Usage in HTML -->
642
+ <nav>
643
+ <a href="/">{{ loadSvg('home.svg', 'nav-icon') }} Home</a>
644
+ <a href="/about">{{ loadSvg('info.svg', 'nav-icon') }} About</a>
645
+ <a href="/contact">{{ loadSvg('mail.svg', 'nav-icon') }} Contact</a>
646
+ </nav>
647
+ ```
648
+
649
+ ### Combining Features
650
+
651
+ **Complex page structure:**
652
+
653
+ ```html
654
+ <!DOCTYPE html>
655
+ <html>
656
+ @@include('partials/head.html', { "title": "Products" })
657
+ <body>
658
+ @@include('partials/header.html')
659
+
660
+ <main>
661
+ <section class="hero">
662
+ @@include('sections/hero.html', { "heading": "Our Products",
663
+ "subheading": "Discover amazing solutions" })
664
+ </section>
665
+
666
+ @@if(showFeatured) {
667
+ <section class="featured">
668
+ <h2>Featured Products</h2>
669
+ @@loop('partials/product-card.html', 'data/featured.json')
670
+ </section>
671
+ }
672
+
673
+ <section class="all-products">
674
+ <h2>All Products</h2>
675
+ <div class="product-grid">
676
+ @@loop('partials/product-card.html', 'data/products.json')
677
+ </div>
678
+ </section>
679
+
680
+ @@if(showNewsletter) { @@include('sections/newsletter.html') }
681
+ </main>
682
+
683
+ @@include('partials/footer.html')
684
+ </body>
685
+ </html>
686
+ ```
687
+
688
+ ## 💡 Tips & Best Practices
689
+
690
+ 1. **Organize partials by purpose:**
691
+
692
+ - `layouts/` for page structure (header, footer)
693
+ - `sections/` for page sections (hero, features)
694
+ - `components/` for reusable components (cards, buttons)
695
+
696
+ 2. **Use JSON files for large datasets** instead of inline arrays
697
+
698
+ 3. **Keep context data in vite.config.js** for site-wide variables
699
+
700
+ 4. **Create utility functions** for common operations (date formatting, string manipulation)
701
+
702
+ 5. **Use meaningful variable names** in your JSON data
703
+
704
+ 6. **Test partials independently** before including them in larger pages
705
+
706
+ 7. **Leverage conditionals** to create different layouts for different pages
707
+
213
708
  ---
214
709
 
215
- ## ⚠️ Error Handling
710
+ ## 🔧 Advanced Usage
711
+
712
+ ### Dynamic SVG Icons
216
713
 
217
- The plugin provides detailed error messages for:
218
- - Missing include files
219
- - Invalid JSON syntax
220
- - Undefined variables
714
+ ```html
715
+ <button class="btn">
716
+ {{ loadSvg('icon-' + buttonType + '.svg', 'btn-icon') }} {{ buttonLabel }}
717
+ </button>
718
+ ```
719
+
720
+ ### Nested Loops
721
+
722
+ ```html
723
+ @@loop('partials/category.html', 'data/categories.json')
724
+
725
+ <!-- partials/category.html -->
726
+ <div class="category">
727
+ <h3>{{ name }}</h3>
728
+ @@loop('partials/item.html', items)
729
+ </div>
730
+ ```
731
+
732
+ ### Conditional Loops
733
+
734
+ ```html
735
+ @@if(articles && articles.length > 0) {
736
+ <div class="articles-grid">
737
+ @@loop('partials/article.html', 'data/articles.json')
738
+ </div>
739
+ }
740
+ ```
741
+
742
+ ---
221
743
 
222
- Each error logs file path and directive line for easier debugging.
744
+ ## 🤝 Contributing
745
+
746
+ Contributions are welcome! Please feel free to submit issues or pull requests.
223
747
 
224
748
  ---
225
749
 
226
750
  ## 📄 License
227
751
 
228
752
  MIT © 2025
753
+
754
+ ---
755
+
756
+ ## 🔗 Related
757
+
758
+ - [Vite](https://vitejs.dev/)
759
+ - [Vite Plugin API](https://vitejs.dev/guide/api-plugin.html)
760
+
761
+ ---
762
+
763
+ **Made with ❤️ for the Vite community**