pug-tail 0.1.0-alpha.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.
Potentially problematic release.
This version of pug-tail might be problematic. Click here for more details.
- package/CHANGELOG.md +37 -0
- package/LICENSE +21 -0
- package/README.md +318 -0
- package/dist/cli/config/loader.d.ts +9 -0
- package/dist/cli/config/loader.d.ts.map +1 -0
- package/dist/cli/config/loader.js +82 -0
- package/dist/cli/config/loader.js.map +1 -0
- package/dist/cli/config/matcher.d.ts +3 -0
- package/dist/cli/config/matcher.d.ts.map +1 -0
- package/dist/cli/config/matcher.js +29 -0
- package/dist/cli/config/matcher.js.map +1 -0
- package/dist/cli/config/types.d.ts +26 -0
- package/dist/cli/config/types.d.ts.map +1 -0
- package/dist/cli/config/types.js +2 -0
- package/dist/cli/config/types.js.map +1 -0
- package/dist/cli/dataLoader.d.ts +8 -0
- package/dist/cli/dataLoader.d.ts.map +1 -0
- package/dist/cli/dataLoader.js +70 -0
- package/dist/cli/dataLoader.js.map +1 -0
- package/dist/cli/dependencyTracker.d.ts +19 -0
- package/dist/cli/dependencyTracker.d.ts.map +1 -0
- package/dist/cli/dependencyTracker.js +149 -0
- package/dist/cli/dependencyTracker.js.map +1 -0
- package/dist/cli/fileProcessor.d.ts +32 -0
- package/dist/cli/fileProcessor.d.ts.map +1 -0
- package/dist/cli/fileProcessor.js +218 -0
- package/dist/cli/fileProcessor.js.map +1 -0
- package/dist/cli/pathResolver.d.ts +12 -0
- package/dist/cli/pathResolver.d.ts.map +1 -0
- package/dist/cli/pathResolver.js +35 -0
- package/dist/cli/pathResolver.js.map +1 -0
- package/dist/cli/watcher.d.ts +22 -0
- package/dist/cli/watcher.d.ts.map +1 -0
- package/dist/cli/watcher.js +186 -0
- package/dist/cli/watcher.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +624 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/astTransformer.d.ts +32 -0
- package/dist/core/astTransformer.d.ts.map +1 -0
- package/dist/core/astTransformer.js +532 -0
- package/dist/core/astTransformer.js.map +1 -0
- package/dist/core/componentRegistry.d.ts +14 -0
- package/dist/core/componentRegistry.d.ts.map +1 -0
- package/dist/core/componentRegistry.js +32 -0
- package/dist/core/componentRegistry.js.map +1 -0
- package/dist/core/errorHandler.d.ts +24 -0
- package/dist/core/errorHandler.d.ts.map +1 -0
- package/dist/core/errorHandler.js +102 -0
- package/dist/core/errorHandler.js.map +1 -0
- package/dist/core/slotResolver.d.ts +9 -0
- package/dist/core/slotResolver.d.ts.map +1 -0
- package/dist/core/slotResolver.js +88 -0
- package/dist/core/slotResolver.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/transform.d.ts +26 -0
- package/dist/transform.d.ts.map +1 -0
- package/dist/transform.js +247 -0
- package/dist/transform.js.map +1 -0
- package/dist/types/index.d.ts +41 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/astHelpers.d.ts +16 -0
- package/dist/utils/astHelpers.d.ts.map +1 -0
- package/dist/utils/astHelpers.js +66 -0
- package/dist/utils/astHelpers.js.map +1 -0
- package/dist/utils/attributeCategorizer.d.ts +6 -0
- package/dist/utils/attributeCategorizer.d.ts.map +1 -0
- package/dist/utils/attributeCategorizer.js +17 -0
- package/dist/utils/attributeCategorizer.js.map +1 -0
- package/dist/utils/attributes/addAttributeFallthrough.d.ts +4 -0
- package/dist/utils/attributes/addAttributeFallthrough.d.ts.map +1 -0
- package/dist/utils/attributes/addAttributeFallthrough.js +22 -0
- package/dist/utils/attributes/addAttributeFallthrough.js.map +1 -0
- package/dist/utils/attributes/createAttributesCode.d.ts +3 -0
- package/dist/utils/attributes/createAttributesCode.d.ts.map +1 -0
- package/dist/utils/attributes/createAttributesCode.js +21 -0
- package/dist/utils/attributes/createAttributesCode.js.map +1 -0
- package/dist/utils/attributes/extractAttributes.d.ts +4 -0
- package/dist/utils/attributes/extractAttributes.d.ts.map +1 -0
- package/dist/utils/attributes/extractAttributes.js +15 -0
- package/dist/utils/attributes/extractAttributes.js.map +1 -0
- package/dist/utils/attributes/findRootElements.d.ts +7 -0
- package/dist/utils/attributes/findRootElements.d.ts.map +1 -0
- package/dist/utils/attributes/findRootElements.js +36 -0
- package/dist/utils/attributes/findRootElements.js.map +1 -0
- package/dist/utils/attributes/index.d.ts +5 -0
- package/dist/utils/attributes/index.d.ts.map +1 -0
- package/dist/utils/attributes/index.js +5 -0
- package/dist/utils/attributes/index.js.map +1 -0
- package/dist/utils/babelHelpers.d.ts +3 -0
- package/dist/utils/babelHelpers.d.ts.map +1 -0
- package/dist/utils/babelHelpers.js +73 -0
- package/dist/utils/babelHelpers.js.map +1 -0
- package/dist/utils/componentDetector.d.ts +12 -0
- package/dist/utils/componentDetector.d.ts.map +1 -0
- package/dist/utils/componentDetector.js +206 -0
- package/dist/utils/componentDetector.js.map +1 -0
- package/dist/utils/dataFilesDetector.d.ts +4 -0
- package/dist/utils/dataFilesDetector.d.ts.map +1 -0
- package/dist/utils/dataFilesDetector.js +88 -0
- package/dist/utils/dataFilesDetector.js.map +1 -0
- package/dist/utils/deepClone.d.ts +5 -0
- package/dist/utils/deepClone.d.ts.map +1 -0
- package/dist/utils/deepClone.js +10 -0
- package/dist/utils/deepClone.js.map +1 -0
- package/dist/utils/index.d.ts +7 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +7 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/scopeAnalyzer.d.ts +5 -0
- package/dist/utils/scopeAnalyzer.d.ts.map +1 -0
- package/dist/utils/scopeAnalyzer.js +267 -0
- package/dist/utils/scopeAnalyzer.js.map +1 -0
- package/dist/utils/usageDetector.d.ts +8 -0
- package/dist/utils/usageDetector.d.ts.map +1 -0
- package/dist/utils/usageDetector.js +148 -0
- package/dist/utils/usageDetector.js.map +1 -0
- package/docs/COMPONENTS.md +708 -0
- package/docs/CONFIGURATION.md +708 -0
- package/package.json +103 -0
|
@@ -0,0 +1,708 @@
|
|
|
1
|
+
# Component DSL Reference
|
|
2
|
+
|
|
3
|
+
This document provides a comprehensive reference for pug-tail's component DSL.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Component Definition](#component-definition)
|
|
8
|
+
- [Component Calls](#component-calls)
|
|
9
|
+
- [Named Slots](#named-slots)
|
|
10
|
+
- [Props and Attrs](#props-and-attrs)
|
|
11
|
+
- [Attribute Fallthrough](#attribute-fallthrough)
|
|
12
|
+
- [Scope Isolation](#scope-isolation)
|
|
13
|
+
- [Advanced Patterns](#advanced-patterns)
|
|
14
|
+
- [Integration with Pug Features](#integration-with-pug-features)
|
|
15
|
+
|
|
16
|
+
## Component Definition
|
|
17
|
+
|
|
18
|
+
### Basic Syntax
|
|
19
|
+
|
|
20
|
+
```pug
|
|
21
|
+
component ComponentName()
|
|
22
|
+
// component body
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Rules**:
|
|
26
|
+
- Component names must start with an uppercase letter
|
|
27
|
+
- Components are defined using the `component` keyword
|
|
28
|
+
- Component body can contain any valid Pug markup
|
|
29
|
+
- Components are block-scoped (defined where declared)
|
|
30
|
+
|
|
31
|
+
### Example
|
|
32
|
+
|
|
33
|
+
```pug
|
|
34
|
+
component Card()
|
|
35
|
+
.card
|
|
36
|
+
.card-header
|
|
37
|
+
h2 Default Title
|
|
38
|
+
.card-body
|
|
39
|
+
p Default content
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Component Calls
|
|
43
|
+
|
|
44
|
+
### Basic Syntax
|
|
45
|
+
|
|
46
|
+
```pug
|
|
47
|
+
ComponentName()
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Rules**:
|
|
51
|
+
- Component calls look like function calls with uppercase names
|
|
52
|
+
- Arguments are passed as Pug attributes
|
|
53
|
+
- Child content is provided via slots
|
|
54
|
+
|
|
55
|
+
### With Arguments
|
|
56
|
+
|
|
57
|
+
```pug
|
|
58
|
+
component Button()
|
|
59
|
+
- const { label, type = "button" } = $props
|
|
60
|
+
button(type=type)= label
|
|
61
|
+
|
|
62
|
+
Button(label="Click me", type="submit")
|
|
63
|
+
Button(label="Cancel") // Uses default type="button"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### With Slots
|
|
67
|
+
|
|
68
|
+
```pug
|
|
69
|
+
component Card()
|
|
70
|
+
.card
|
|
71
|
+
slot(header)
|
|
72
|
+
slot(body)
|
|
73
|
+
|
|
74
|
+
Card()
|
|
75
|
+
slot(header)
|
|
76
|
+
h1 Title
|
|
77
|
+
slot(body)
|
|
78
|
+
p Content
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Named Slots
|
|
82
|
+
|
|
83
|
+
### Defining Slots
|
|
84
|
+
|
|
85
|
+
```pug
|
|
86
|
+
component Layout()
|
|
87
|
+
header
|
|
88
|
+
slot(header)
|
|
89
|
+
p Default header
|
|
90
|
+
|
|
91
|
+
main
|
|
92
|
+
slot(main)
|
|
93
|
+
p Default main content
|
|
94
|
+
|
|
95
|
+
footer
|
|
96
|
+
slot(footer)
|
|
97
|
+
p Default footer
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Features**:
|
|
101
|
+
- `slot(name)` defines a named slot
|
|
102
|
+
- Content inside slot tag is the default (used if no content provided)
|
|
103
|
+
- Slots can be nested
|
|
104
|
+
|
|
105
|
+
### Using Slots
|
|
106
|
+
|
|
107
|
+
```pug
|
|
108
|
+
Layout()
|
|
109
|
+
slot(header)
|
|
110
|
+
h1 My Site
|
|
111
|
+
|
|
112
|
+
slot(main)
|
|
113
|
+
article
|
|
114
|
+
p Main content
|
|
115
|
+
|
|
116
|
+
slot(footer)
|
|
117
|
+
p Copyright 2026
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Rules**:
|
|
121
|
+
- Slot names must match between definition and usage
|
|
122
|
+
- Slots can be provided in any order
|
|
123
|
+
- Unmatched slots use default content
|
|
124
|
+
- Extra slots (not defined) are ignored
|
|
125
|
+
|
|
126
|
+
### Nested Components and Slots
|
|
127
|
+
|
|
128
|
+
```pug
|
|
129
|
+
component Page()
|
|
130
|
+
Layout()
|
|
131
|
+
slot(header)
|
|
132
|
+
slot(pageHeader) // Expose slot to parent
|
|
133
|
+
slot(main)
|
|
134
|
+
slot(pageMain)
|
|
135
|
+
|
|
136
|
+
Page()
|
|
137
|
+
slot(pageHeader)
|
|
138
|
+
h1 Welcome
|
|
139
|
+
slot(pageMain)
|
|
140
|
+
p Content
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Props and Attrs
|
|
144
|
+
|
|
145
|
+
### $props - Component Properties
|
|
146
|
+
|
|
147
|
+
Props are explicitly declared properties that the component expects.
|
|
148
|
+
|
|
149
|
+
```pug
|
|
150
|
+
component Button()
|
|
151
|
+
- const { label, type = "button", disabled = false } = $props
|
|
152
|
+
button(type=type disabled=disabled)= label
|
|
153
|
+
|
|
154
|
+
Button(label="Submit", type="submit")
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Features**:
|
|
158
|
+
- Use JavaScript destructuring syntax
|
|
159
|
+
- Support default values
|
|
160
|
+
- Support renaming: `{ class: className }`
|
|
161
|
+
- Type-safe (values are passed as-is)
|
|
162
|
+
|
|
163
|
+
### $attrs - Additional Attributes
|
|
164
|
+
|
|
165
|
+
Attrs are all other attributes not declared in `$props`.
|
|
166
|
+
|
|
167
|
+
```pug
|
|
168
|
+
component Card()
|
|
169
|
+
- const { title } = $props
|
|
170
|
+
- const { class: className = "card" } = $attrs
|
|
171
|
+
|
|
172
|
+
.card(class=className)
|
|
173
|
+
h2= title
|
|
174
|
+
|
|
175
|
+
Card(title="Hello", class="custom-card", data-id="123")
|
|
176
|
+
// title → $props
|
|
177
|
+
// class, data-id → $attrs
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**Automatic Categorization**:
|
|
181
|
+
|
|
182
|
+
pug-tail automatically splits component arguments:
|
|
183
|
+
1. Analyzes `$props` destructuring
|
|
184
|
+
2. Puts declared properties in `$props`
|
|
185
|
+
3. Puts everything else in `$attrs`
|
|
186
|
+
|
|
187
|
+
### Legacy attributes
|
|
188
|
+
|
|
189
|
+
The traditional `attributes` object still works:
|
|
190
|
+
|
|
191
|
+
```pug
|
|
192
|
+
component Card()
|
|
193
|
+
- const { title, ...attrs } = attributes
|
|
194
|
+
.card&attributes(attrs)
|
|
195
|
+
h2= title
|
|
196
|
+
|
|
197
|
+
Card(title="Hello", class="custom")
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**Recommendation**: Use `$props`/`$attrs` for new code. It's more explicit and follows Vue/React conventions.
|
|
201
|
+
|
|
202
|
+
## Attribute Fallthrough
|
|
203
|
+
|
|
204
|
+
### Automatic Fallthrough
|
|
205
|
+
|
|
206
|
+
By default, `$attrs` are automatically applied to the root element:
|
|
207
|
+
|
|
208
|
+
```pug
|
|
209
|
+
component Card()
|
|
210
|
+
.card
|
|
211
|
+
h2 Title
|
|
212
|
+
|
|
213
|
+
Card(class="my-card", id="card-1", data-test="value")
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**Output**:
|
|
217
|
+
```html
|
|
218
|
+
<div class="card my-card" id="card-1" data-test="value">
|
|
219
|
+
<h2>Title</h2>
|
|
220
|
+
</div>
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**Rules**:
|
|
224
|
+
- Applies only to single root element
|
|
225
|
+
- Classes are merged (not replaced)
|
|
226
|
+
- Other attributes override component's attributes
|
|
227
|
+
|
|
228
|
+
### Disabling Automatic Fallthrough
|
|
229
|
+
|
|
230
|
+
Use `&attributes()` explicitly to control where attrs are applied:
|
|
231
|
+
|
|
232
|
+
```pug
|
|
233
|
+
component Input()
|
|
234
|
+
.wrapper
|
|
235
|
+
label Label
|
|
236
|
+
input.field&attributes(attributes)
|
|
237
|
+
|
|
238
|
+
Input(type="text", class="primary")
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
**Output**:
|
|
242
|
+
```html
|
|
243
|
+
<div class="wrapper">
|
|
244
|
+
<label>Label</label>
|
|
245
|
+
<input class="field primary" type="text"/>
|
|
246
|
+
</div>
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
The explicit `&attributes(attributes)` disables automatic fallthrough to `.wrapper`.
|
|
250
|
+
|
|
251
|
+
### Using $attrs Explicitly
|
|
252
|
+
|
|
253
|
+
```pug
|
|
254
|
+
component Card()
|
|
255
|
+
- const { title } = $props
|
|
256
|
+
|
|
257
|
+
.card&attributes($attrs)
|
|
258
|
+
h2= title
|
|
259
|
+
|
|
260
|
+
Card(title="Test", class="my-card")
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Using `&attributes($attrs)` also disables automatic fallthrough.
|
|
264
|
+
|
|
265
|
+
### Multiple Root Elements
|
|
266
|
+
|
|
267
|
+
Automatic fallthrough is disabled with multiple root elements:
|
|
268
|
+
|
|
269
|
+
```pug
|
|
270
|
+
component Split()
|
|
271
|
+
.left
|
|
272
|
+
slot(left)
|
|
273
|
+
.right
|
|
274
|
+
slot(right)
|
|
275
|
+
|
|
276
|
+
Split(class="container")
|
|
277
|
+
// class="container" is NOT applied (multiple roots)
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
**Workaround**:
|
|
281
|
+
```pug
|
|
282
|
+
component Split()
|
|
283
|
+
- const { class: className } = $attrs
|
|
284
|
+
.split(class=className)
|
|
285
|
+
.left
|
|
286
|
+
slot(left)
|
|
287
|
+
.right
|
|
288
|
+
slot(right)
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## Scope Isolation
|
|
292
|
+
|
|
293
|
+
### Strict Mode (Default)
|
|
294
|
+
|
|
295
|
+
Components cannot access external variables:
|
|
296
|
+
|
|
297
|
+
```pug
|
|
298
|
+
- const message = 'Hello'
|
|
299
|
+
|
|
300
|
+
component Card()
|
|
301
|
+
p= message // ❌ Error: references external variable "message"
|
|
302
|
+
|
|
303
|
+
Card()
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
**Fix**: Pass via props:
|
|
307
|
+
```pug
|
|
308
|
+
- const message = 'Hello'
|
|
309
|
+
|
|
310
|
+
component Card()
|
|
311
|
+
- const { text } = $props
|
|
312
|
+
p= text
|
|
313
|
+
|
|
314
|
+
Card(text=message) // ✅ OK
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Allowed Identifiers
|
|
318
|
+
|
|
319
|
+
These are always accessible:
|
|
320
|
+
|
|
321
|
+
**JavaScript globals**:
|
|
322
|
+
- `console`, `Math`, `Date`, `JSON`, `Object`, `Array`
|
|
323
|
+
- `String`, `Number`, `Boolean`, `RegExp`, `Error`
|
|
324
|
+
- `Promise`, `Set`, `Map`, `WeakSet`, `WeakMap`
|
|
325
|
+
- `parseInt`, `parseFloat`, `isNaN`, `isFinite`
|
|
326
|
+
- `encodeURI`, `decodeURI`, `encodeURIComponent`, `decodeURIComponent`
|
|
327
|
+
|
|
328
|
+
**Pug built-ins**:
|
|
329
|
+
- `attributes`, `block`
|
|
330
|
+
|
|
331
|
+
**pug-tail keywords**:
|
|
332
|
+
- `$props`, `$attrs`
|
|
333
|
+
|
|
334
|
+
### Configuration
|
|
335
|
+
|
|
336
|
+
Control validation mode in config file:
|
|
337
|
+
|
|
338
|
+
```javascript
|
|
339
|
+
// pugtail.config.js
|
|
340
|
+
export default {
|
|
341
|
+
validation: {
|
|
342
|
+
scopeIsolation: 'error', // 'error' | 'warn' | 'off'
|
|
343
|
+
allowedGlobals: ['myHelper', 'APP_VERSION']
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
**Modes**:
|
|
349
|
+
- `'error'` (default): Compilation fails on external references
|
|
350
|
+
- `'warn'`: Logs warnings but compiles successfully
|
|
351
|
+
- `'off'`: Disables validation (legacy compatibility)
|
|
352
|
+
|
|
353
|
+
### Custom Allowed Globals
|
|
354
|
+
|
|
355
|
+
Add project-specific globals:
|
|
356
|
+
|
|
357
|
+
```javascript
|
|
358
|
+
// pugtail.config.js
|
|
359
|
+
export default {
|
|
360
|
+
validation: {
|
|
361
|
+
scopeIsolation: 'error',
|
|
362
|
+
allowedGlobals: [
|
|
363
|
+
'formatDate', // Custom helper
|
|
364
|
+
'APP_CONFIG', // Global config
|
|
365
|
+
'_', // Lodash
|
|
366
|
+
]
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
## Advanced Patterns
|
|
372
|
+
|
|
373
|
+
### Conditional Slots
|
|
374
|
+
|
|
375
|
+
```pug
|
|
376
|
+
component Card()
|
|
377
|
+
.card
|
|
378
|
+
if hasHeader
|
|
379
|
+
.card-header
|
|
380
|
+
slot(header)
|
|
381
|
+
.card-body
|
|
382
|
+
slot(body)
|
|
383
|
+
|
|
384
|
+
- const hasHeader = true
|
|
385
|
+
Card()
|
|
386
|
+
slot(header)
|
|
387
|
+
h1 Title
|
|
388
|
+
slot(body)
|
|
389
|
+
p Content
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
### Iterating with Slots
|
|
393
|
+
|
|
394
|
+
```pug
|
|
395
|
+
component List()
|
|
396
|
+
ul
|
|
397
|
+
each item in items
|
|
398
|
+
li= item
|
|
399
|
+
|
|
400
|
+
- const items = ['A', 'B', 'C']
|
|
401
|
+
List()
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### Dynamic Attributes
|
|
405
|
+
|
|
406
|
+
```pug
|
|
407
|
+
component Button()
|
|
408
|
+
- const { variant = 'primary' } = $props
|
|
409
|
+
- const classes = `btn btn-${variant}`
|
|
410
|
+
button(class=classes)
|
|
411
|
+
slot(default)
|
|
412
|
+
| Click me
|
|
413
|
+
|
|
414
|
+
Button(variant="secondary")
|
|
415
|
+
slot(default)
|
|
416
|
+
| Custom label
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### Computed Properties
|
|
420
|
+
|
|
421
|
+
```pug
|
|
422
|
+
component Price()
|
|
423
|
+
- const { amount, currency = 'USD' } = $props
|
|
424
|
+
- const formatted = new Intl.NumberFormat('en', { style: 'currency', currency }).format(amount)
|
|
425
|
+
span.price= formatted
|
|
426
|
+
|
|
427
|
+
Price(amount=99.99, currency="EUR")
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
### Wrapper Components
|
|
431
|
+
|
|
432
|
+
```pug
|
|
433
|
+
component Container()
|
|
434
|
+
- const { maxWidth = '1200px' } = $props
|
|
435
|
+
- const { class: className } = $attrs
|
|
436
|
+
.container(class=className style=`max-width: ${maxWidth}`)
|
|
437
|
+
slot(default)
|
|
438
|
+
|
|
439
|
+
Container(maxWidth="800px", class="my-container")
|
|
440
|
+
p Content
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
## Integration with Pug Features
|
|
444
|
+
|
|
445
|
+
### With Includes
|
|
446
|
+
|
|
447
|
+
```pug
|
|
448
|
+
// components/Button.pug
|
|
449
|
+
component Button()
|
|
450
|
+
- const { label } = $props
|
|
451
|
+
button= label
|
|
452
|
+
|
|
453
|
+
// pages/index.pug
|
|
454
|
+
include ../components/Button.pug
|
|
455
|
+
|
|
456
|
+
Button(label="Click")
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### With Extends
|
|
460
|
+
|
|
461
|
+
```pug
|
|
462
|
+
// layouts/base.pug
|
|
463
|
+
component Layout()
|
|
464
|
+
html
|
|
465
|
+
head
|
|
466
|
+
block head
|
|
467
|
+
body
|
|
468
|
+
slot(content)
|
|
469
|
+
|
|
470
|
+
// pages/home.pug
|
|
471
|
+
include ../layouts/base.pug
|
|
472
|
+
|
|
473
|
+
extends ../layouts/base.pug
|
|
474
|
+
|
|
475
|
+
block head
|
|
476
|
+
title Home
|
|
477
|
+
|
|
478
|
+
Layout()
|
|
479
|
+
slot(content)
|
|
480
|
+
h1 Home Page
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### With Mixins
|
|
484
|
+
|
|
485
|
+
```pug
|
|
486
|
+
mixin icon(name)
|
|
487
|
+
i(class=`icon-${name}`)
|
|
488
|
+
|
|
489
|
+
component Button()
|
|
490
|
+
- const { icon } = $props
|
|
491
|
+
button
|
|
492
|
+
+icon(icon)
|
|
493
|
+
slot(default)
|
|
494
|
+
|
|
495
|
+
Button(icon="star")
|
|
496
|
+
slot(default)
|
|
497
|
+
| Favorite
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
### With Filters
|
|
501
|
+
|
|
502
|
+
```pug
|
|
503
|
+
component Code()
|
|
504
|
+
pre
|
|
505
|
+
code
|
|
506
|
+
slot(default)
|
|
507
|
+
|
|
508
|
+
Code()
|
|
509
|
+
slot(default)
|
|
510
|
+
:markdown-it
|
|
511
|
+
# Markdown content
|
|
512
|
+
This is **bold**
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
## Best Practices
|
|
516
|
+
|
|
517
|
+
### 1. Keep Components Focused
|
|
518
|
+
|
|
519
|
+
Each component should have a single, clear responsibility:
|
|
520
|
+
|
|
521
|
+
```pug
|
|
522
|
+
// ✅ Good - Focused component
|
|
523
|
+
component Button()
|
|
524
|
+
button
|
|
525
|
+
slot(default)
|
|
526
|
+
|
|
527
|
+
// ❌ Avoid - Too much responsibility
|
|
528
|
+
component ButtonWithModalAndTooltip()
|
|
529
|
+
// ...
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
### 2. Use Props for Data, Attrs for Styling
|
|
533
|
+
|
|
534
|
+
```pug
|
|
535
|
+
component Card()
|
|
536
|
+
- const { title, description } = $props // Data
|
|
537
|
+
- const { class: className } = $attrs // Styling
|
|
538
|
+
|
|
539
|
+
.card(class=className)
|
|
540
|
+
h2= title
|
|
541
|
+
p= description
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
### 3. Provide Sensible Defaults
|
|
545
|
+
|
|
546
|
+
```pug
|
|
547
|
+
component Button()
|
|
548
|
+
- const { type = "button", disabled = false } = $props
|
|
549
|
+
button(type=type disabled=disabled)
|
|
550
|
+
slot(default)
|
|
551
|
+
| Button
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
### 4. Document Complex Components
|
|
555
|
+
|
|
556
|
+
```pug
|
|
557
|
+
//- Button component
|
|
558
|
+
//- @param {string} label - Button text
|
|
559
|
+
//- @param {string} type - Button type (button|submit|reset)
|
|
560
|
+
//- @param {boolean} disabled - Disabled state
|
|
561
|
+
//- @param {string} class - Additional CSS classes
|
|
562
|
+
component Button()
|
|
563
|
+
// ...
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
### 5. Use Descriptive Slot Names
|
|
567
|
+
|
|
568
|
+
```pug
|
|
569
|
+
// ✅ Clear intent
|
|
570
|
+
component Card()
|
|
571
|
+
slot(header)
|
|
572
|
+
slot(body)
|
|
573
|
+
slot(footer)
|
|
574
|
+
|
|
575
|
+
// ❌ Unclear
|
|
576
|
+
component Card()
|
|
577
|
+
slot(a)
|
|
578
|
+
slot(b)
|
|
579
|
+
slot(c)
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
### 6. Avoid Deep Nesting
|
|
583
|
+
|
|
584
|
+
Limit component nesting depth for maintainability:
|
|
585
|
+
|
|
586
|
+
```pug
|
|
587
|
+
// ✅ Reasonable depth
|
|
588
|
+
Layout()
|
|
589
|
+
slot(main)
|
|
590
|
+
Card()
|
|
591
|
+
slot(body)
|
|
592
|
+
p Content
|
|
593
|
+
|
|
594
|
+
// ❌ Too deep (hard to follow)
|
|
595
|
+
A()
|
|
596
|
+
B()
|
|
597
|
+
C()
|
|
598
|
+
D()
|
|
599
|
+
E()
|
|
600
|
+
F()
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
## Common Patterns
|
|
604
|
+
|
|
605
|
+
### Card with Optional Header/Footer
|
|
606
|
+
|
|
607
|
+
```pug
|
|
608
|
+
component Card()
|
|
609
|
+
- const { title } = $props
|
|
610
|
+
.card
|
|
611
|
+
if title
|
|
612
|
+
.card-header
|
|
613
|
+
h2= title
|
|
614
|
+
.card-body
|
|
615
|
+
slot(body)
|
|
616
|
+
.card-footer
|
|
617
|
+
slot(footer)
|
|
618
|
+
|
|
619
|
+
// With all parts
|
|
620
|
+
Card(title="Title")
|
|
621
|
+
slot(body)
|
|
622
|
+
p Content
|
|
623
|
+
slot(footer)
|
|
624
|
+
button OK
|
|
625
|
+
|
|
626
|
+
// Body only
|
|
627
|
+
Card()
|
|
628
|
+
slot(body)
|
|
629
|
+
p Content
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
### Modal Dialog
|
|
633
|
+
|
|
634
|
+
```pug
|
|
635
|
+
component Modal()
|
|
636
|
+
- const { isOpen = false } = $props
|
|
637
|
+
if isOpen
|
|
638
|
+
.modal-overlay
|
|
639
|
+
.modal
|
|
640
|
+
.modal-header
|
|
641
|
+
slot(header)
|
|
642
|
+
button.close ×
|
|
643
|
+
.modal-body
|
|
644
|
+
slot(body)
|
|
645
|
+
.modal-footer
|
|
646
|
+
slot(footer)
|
|
647
|
+
|
|
648
|
+
Modal(isOpen=true)
|
|
649
|
+
slot(header)
|
|
650
|
+
h2 Confirm
|
|
651
|
+
slot(body)
|
|
652
|
+
p Are you sure?
|
|
653
|
+
slot(footer)
|
|
654
|
+
button Cancel
|
|
655
|
+
button OK
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
### Navigation Menu
|
|
659
|
+
|
|
660
|
+
```pug
|
|
661
|
+
component Nav()
|
|
662
|
+
- const { items = [] } = $props
|
|
663
|
+
nav
|
|
664
|
+
ul
|
|
665
|
+
each item in items
|
|
666
|
+
li
|
|
667
|
+
a(href=item.url)= item.label
|
|
668
|
+
|
|
669
|
+
Nav(items=[{label: 'Home', url: '/'}, {label: 'About', url: '/about'}])
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
## Troubleshooting
|
|
673
|
+
|
|
674
|
+
### Common Errors
|
|
675
|
+
|
|
676
|
+
**Error**: `Component "X" not found`
|
|
677
|
+
- **Cause**: Component not defined before use
|
|
678
|
+
- **Fix**: Define component before calling it
|
|
679
|
+
|
|
680
|
+
**Error**: `Component "X" references external variable "Y"`
|
|
681
|
+
- **Cause**: Scope isolation violation
|
|
682
|
+
- **Fix**: Pass variable via props or add to allowedGlobals
|
|
683
|
+
|
|
684
|
+
**Error**: `Unexpected token in $props destructuring`
|
|
685
|
+
- **Cause**: Invalid JavaScript syntax
|
|
686
|
+
- **Fix**: Check destructuring syntax
|
|
687
|
+
|
|
688
|
+
### Debugging
|
|
689
|
+
|
|
690
|
+
Enable debug mode to see transformation details:
|
|
691
|
+
|
|
692
|
+
```javascript
|
|
693
|
+
// pugtail.config.js
|
|
694
|
+
export default {
|
|
695
|
+
debug: true
|
|
696
|
+
}
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
### Getting Help
|
|
700
|
+
|
|
701
|
+
- Check [Configuration Guide](./CONFIGURATION.md) for settings
|
|
702
|
+
- Review [Architecture](./ARCHITECTURE.md) for internals
|
|
703
|
+
- See [Contributing](./CONTRIBUTING.md) for development setup
|
|
704
|
+
- Open an issue on GitHub with a minimal reproduction
|
|
705
|
+
|
|
706
|
+
---
|
|
707
|
+
|
|
708
|
+
For more information, see the main [README](../README.md).
|