html-component-engine 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -226
- package/bin/cli.js +5 -5
- package/package.json +2 -2
- package/src/index.js +95 -40
package/README.md
CHANGED
|
@@ -1,247 +1,36 @@
|
|
|
1
1
|
# HTML Component Engine
|
|
2
2
|
|
|
3
|
-
A lightweight Vite plugin
|
|
3
|
+
A lightweight Vite plugin for HTML components.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
- ✅ Reusable HTML components with props
|
|
8
|
-
- ✅ **Slot/children support** - Pass content into components
|
|
9
|
-
- ✅ CSS/JS inlining - No external files in production
|
|
10
|
-
- ✅ Variant system for component styles
|
|
11
|
-
- ✅ Nested components
|
|
12
|
-
- ✅ Hot reload during development
|
|
13
|
-
- ✅ Zero runtime JavaScript
|
|
14
|
-
|
|
15
|
-
## Installation
|
|
5
|
+
## Quick Start
|
|
16
6
|
|
|
17
7
|
```bash
|
|
18
|
-
|
|
8
|
+
npx html-component-engine
|
|
19
9
|
```
|
|
20
10
|
|
|
21
|
-
|
|
11
|
+
## Commands
|
|
22
12
|
|
|
23
|
-
|
|
13
|
+
```bash
|
|
14
|
+
npm run dev # Development server
|
|
15
|
+
npm run build # Production build
|
|
16
|
+
```
|
|
24
17
|
|
|
25
|
-
|
|
18
|
+
## Project Structure
|
|
26
19
|
|
|
27
20
|
```
|
|
28
21
|
src/
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
components/ # Reusable components
|
|
22
|
+
index.html
|
|
23
|
+
about.html
|
|
24
|
+
components/
|
|
33
25
|
Header.html
|
|
34
26
|
Footer.html
|
|
35
27
|
Card.html
|
|
36
|
-
main/
|
|
37
|
-
Button.html
|
|
38
|
-
assets/ # Static assets
|
|
39
|
-
styles/
|
|
40
|
-
styles.css
|
|
41
|
-
images/
|
|
42
|
-
fonts/
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
- `src/pages/`: HTML pages - each becomes an output file
|
|
46
|
-
- `src/components/`: Reusable HTML components (supports nested folders and .js files)
|
|
47
|
-
- `src/assets/`: Static assets - images/fonts copied to `dist/assets/`, CSS/JS inlined
|
|
48
|
-
|
|
49
|
-
## Configuration
|
|
50
|
-
|
|
51
|
-
```javascript
|
|
52
|
-
// vite.config.js
|
|
53
|
-
import htmlComponentEngine from 'html-component-engine';
|
|
54
|
-
|
|
55
|
-
export default {
|
|
56
|
-
plugins: [
|
|
57
|
-
htmlComponentEngine({
|
|
58
|
-
pagesDir: 'src/pages', // Pages directory (default)
|
|
59
|
-
componentsDir: 'src/components', // Components directory (default)
|
|
60
|
-
assetsDir: 'src/assets', // Assets directory (default)
|
|
61
|
-
inlineStyles: true, // Inline CSS into HTML (default: true)
|
|
62
|
-
inlineScripts: true, // Inline JS into HTML (default: true)
|
|
63
|
-
})
|
|
64
|
-
],
|
|
65
|
-
build: {
|
|
66
|
-
outDir: 'dist',
|
|
67
|
-
emptyOutDir: true,
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## Component Syntax
|
|
73
|
-
|
|
74
|
-
### Self-Closing Components (Props Only)
|
|
75
|
-
|
|
76
|
-
Use `<Component src="...">` for simple components:
|
|
77
|
-
|
|
78
|
-
```html
|
|
79
|
-
<!-- src/pages/index.html -->
|
|
80
|
-
<!DOCTYPE html>
|
|
81
|
-
<html lang="en">
|
|
82
|
-
<head>
|
|
83
|
-
<meta charset="UTF-8">
|
|
84
|
-
<title>My Page</title>
|
|
85
|
-
<link rel="stylesheet" href="/styles/styles.css">
|
|
86
|
-
</head>
|
|
87
|
-
<body>
|
|
88
|
-
<Component src="Header" />
|
|
89
|
-
<h1>Welcome</h1>
|
|
90
|
-
<Component src="main/Button" text="Click Me" variant="primary" />
|
|
91
|
-
<Component src="Footer" />
|
|
92
|
-
</body>
|
|
93
|
-
</html>
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### Components with Children (Slots)
|
|
97
|
-
|
|
98
|
-
Use `<Component name="...">` for components that accept children:
|
|
99
|
-
|
|
100
|
-
```html
|
|
101
|
-
<Component name="Card" title="My Card">
|
|
102
|
-
<h2>Hello</h2>
|
|
103
|
-
<p>This content is passed to the component's slot</p>
|
|
104
|
-
</Component>
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
Component template with `{{ children }}` slot:
|
|
108
|
-
|
|
109
|
-
```html
|
|
110
|
-
<!-- src/components/Card.html -->
|
|
111
|
-
<div class="card">
|
|
112
|
-
<div class="card-header">
|
|
113
|
-
<h4>{{ title }}</h4>
|
|
114
|
-
</div>
|
|
115
|
-
<div class="card-body">
|
|
116
|
-
{{ children }}
|
|
117
|
-
</div>
|
|
118
|
-
</div>
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
**Output:**
|
|
122
|
-
|
|
123
|
-
```html
|
|
124
|
-
<div class="card">
|
|
125
|
-
<div class="card-header">
|
|
126
|
-
<h4>My Card</h4>
|
|
127
|
-
</div>
|
|
128
|
-
<div class="card-body">
|
|
129
|
-
<h2>Hello</h2>
|
|
130
|
-
<p>This content is passed to the component's slot</p>
|
|
131
|
-
</div>
|
|
132
|
-
</div>
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
### Props and Variants
|
|
136
|
-
|
|
137
|
-
In component files, use `{{propName}}` placeholders:
|
|
138
|
-
|
|
139
|
-
```html
|
|
140
|
-
<!-- src/components/main/Button.html -->
|
|
141
|
-
<!-- variants: primary=primary-btn, secondary=secondary-btn -->
|
|
142
|
-
<button class="{{variantClasses}}">{{text}}</button>
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
Usage:
|
|
146
|
-
|
|
147
|
-
```html
|
|
148
|
-
<Component src="main/Button" text="Click Me" variant="primary" />
|
|
149
28
|
```
|
|
150
29
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
Components can also be JavaScript files:
|
|
154
|
-
|
|
155
|
-
```javascript
|
|
156
|
-
// src/components/Counter.js
|
|
157
|
-
export default function Counter({ initial = 0 }) {
|
|
158
|
-
return `
|
|
159
|
-
<div class="counter">
|
|
160
|
-
<button onclick="this.nextElementSibling.textContent--">-</button>
|
|
161
|
-
<span>${initial}</span>
|
|
162
|
-
<button onclick="this.previousElementSibling.textContent++">+</button>
|
|
163
|
-
</div>
|
|
164
|
-
`;
|
|
165
|
-
}
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
Usage: `<Component src="Counter" initial="5" />`
|
|
169
|
-
|
|
170
|
-
## Development
|
|
171
|
-
|
|
172
|
-
Run the development server:
|
|
173
|
-
|
|
174
|
-
```bash
|
|
175
|
-
npm run dev
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
Live reload is enabled - changes to pages, components, or assets automatically refresh the browser.
|
|
179
|
-
|
|
180
|
-
## Build
|
|
181
|
-
|
|
182
|
-
Build for production:
|
|
183
|
-
|
|
184
|
-
```bash
|
|
185
|
-
npm run build
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
### Build Output
|
|
189
|
-
|
|
190
|
-
The build generates:
|
|
30
|
+
## Build Output
|
|
191
31
|
|
|
192
32
|
```
|
|
193
33
|
dist/
|
|
194
|
-
index.html
|
|
195
|
-
about.html
|
|
196
|
-
assets/
|
|
197
|
-
images/ # Copied from src/assets
|
|
198
|
-
fonts/ # Only non-CSS/JS assets
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
**Key features:**
|
|
202
|
-
- ✅ All `<Component>` tags replaced with actual HTML
|
|
203
|
-
- ✅ CSS inlined as `<style>` tags
|
|
204
|
-
- ✅ JS inlined as `<script>` tags
|
|
205
|
-
- ✅ No standalone `.css` or `.js` files
|
|
206
|
-
- ✅ Assets (images, fonts) copied to `dist/assets/`
|
|
207
|
-
- ✅ Valid, production-ready HTML
|
|
208
|
-
|
|
209
|
-
## API
|
|
210
|
-
|
|
211
|
-
### Plugin Options
|
|
212
|
-
|
|
213
|
-
| Option | Type | Default | Description |
|
|
214
|
-
|--------|------|---------|-------------|
|
|
215
|
-
| `pagesDir` | string | `'src/pages'` | Directory containing HTML pages |
|
|
216
|
-
| `componentsDir` | string | `'src/components'` | Directory containing components |
|
|
217
|
-
| `assetsDir` | string | `'src/assets'` | Directory containing assets |
|
|
218
|
-
| `inlineStyles` | boolean | `true` | Inline CSS into HTML |
|
|
219
|
-
| `inlineScripts` | boolean | `true` | Inline JS into HTML |
|
|
220
|
-
|
|
221
|
-
### Component Attributes
|
|
222
|
-
|
|
223
|
-
**Self-closing components (`<Component src="..." />`):**
|
|
224
|
-
- `src` (required): Component path relative to `componentsDir`
|
|
225
|
-
- `variant`: Apply variant classes
|
|
226
|
-
- Any other attribute: Passed as props
|
|
227
|
-
|
|
228
|
-
**Components with children (`<Component name="...">`):**
|
|
229
|
-
- `name` (required): Component name/path
|
|
230
|
-
- Any other attribute: Passed as props
|
|
231
|
-
|
|
232
|
-
### Placeholders
|
|
233
|
-
|
|
234
|
-
- `{{ propName }}`: Replaced with prop value
|
|
235
|
-
- `{{ children }}`: Replaced with component's inner content
|
|
236
|
-
- `{{ variantClasses }}`: Replaced with variant classes
|
|
237
|
-
|
|
238
|
-
## Example
|
|
239
|
-
|
|
240
|
-
See the `example/` directory for a complete working example:
|
|
241
|
-
|
|
242
|
-
```bash
|
|
243
|
-
cd example
|
|
244
|
-
npm install
|
|
245
|
-
npm run dev # Development
|
|
246
|
-
npm run build # Production build
|
|
34
|
+
index.html
|
|
35
|
+
about.html
|
|
247
36
|
```
|
package/bin/cli.js
CHANGED
|
@@ -39,7 +39,7 @@ function question(rl, prompt) {
|
|
|
39
39
|
const templates = {
|
|
40
40
|
'package.json': (projectName) => `{
|
|
41
41
|
"name": "${projectName}",
|
|
42
|
-
"version": "0.1.
|
|
42
|
+
"version": "0.1.2",
|
|
43
43
|
"type": "module",
|
|
44
44
|
"scripts": {
|
|
45
45
|
"dev": "vite",
|
|
@@ -48,7 +48,7 @@ const templates = {
|
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"vite": "^7.0.0",
|
|
51
|
-
"html-component-engine": "^0.1.
|
|
51
|
+
"html-component-engine": "^0.1.2"
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
`,
|
|
@@ -63,7 +63,7 @@ export default {
|
|
|
63
63
|
assetsDir: 'assets',
|
|
64
64
|
})
|
|
65
65
|
],
|
|
66
|
-
publicDir: 'src/assets
|
|
66
|
+
publicDir: 'src', // Serve src/assets as /assets during dev
|
|
67
67
|
build: {
|
|
68
68
|
outDir: 'dist',
|
|
69
69
|
emptyOutDir: true,
|
|
@@ -77,7 +77,7 @@ export default {
|
|
|
77
77
|
<meta charset="UTF-8">
|
|
78
78
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
79
79
|
<title>Home | My Website</title>
|
|
80
|
-
<link rel="stylesheet" href="/styles/main.css">
|
|
80
|
+
<link rel="stylesheet" href="./assets/styles/main.css">
|
|
81
81
|
</head>
|
|
82
82
|
<body>
|
|
83
83
|
<Component src="Header" title="My Website" />
|
|
@@ -116,7 +116,7 @@ export default {
|
|
|
116
116
|
<meta charset="UTF-8">
|
|
117
117
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
118
118
|
<title>About | My Website</title>
|
|
119
|
-
<link rel="stylesheet" href="/styles/main.css">
|
|
119
|
+
<link rel="stylesheet" href="./assets/styles/main.css">
|
|
120
120
|
</head>
|
|
121
121
|
<body>
|
|
122
122
|
<Component src="Header" title="My Website" />
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "html-component-engine",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "A Vite plugin for HTML component components with a lightweight static site compiler",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
7
7
|
"bin": {
|
|
8
|
-
"html-component": "./bin/cli.js"
|
|
8
|
+
"html-component-engine": "./bin/cli.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"src",
|
package/src/index.js
CHANGED
|
@@ -21,8 +21,8 @@ export default function htmlComponentEngine(options = {}) {
|
|
|
21
21
|
srcDir = 'src',
|
|
22
22
|
componentsDir = 'components',
|
|
23
23
|
assetsDir = 'assets',
|
|
24
|
-
inlineStyles =
|
|
25
|
-
inlineScripts =
|
|
24
|
+
inlineStyles = false,
|
|
25
|
+
inlineScripts = false,
|
|
26
26
|
} = opts;
|
|
27
27
|
|
|
28
28
|
let projectRoot;
|
|
@@ -34,6 +34,41 @@ export default function htmlComponentEngine(options = {}) {
|
|
|
34
34
|
return {
|
|
35
35
|
name: 'html-component-engine',
|
|
36
36
|
|
|
37
|
+
async config(config, { command }) {
|
|
38
|
+
// Calculate paths early for config setup
|
|
39
|
+
const root = process.cwd();
|
|
40
|
+
const src = path.resolve(root, srcDir);
|
|
41
|
+
const components = componentsDir;
|
|
42
|
+
|
|
43
|
+
if (command === 'build') {
|
|
44
|
+
// Disable Vite's default HTML handling - we do it ourselves
|
|
45
|
+
return {
|
|
46
|
+
// Disable publicDir - we copy assets ourselves
|
|
47
|
+
publicDir: false,
|
|
48
|
+
build: {
|
|
49
|
+
// Use a virtual entry - we'll handle HTML ourselves in generateBundle
|
|
50
|
+
rollupOptions: {
|
|
51
|
+
input: { __virtual_entry__: '\0virtual:html-component-engine-entry' },
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
// Resolve virtual module
|
|
59
|
+
resolveId(id) {
|
|
60
|
+
if (id === '\0virtual:html-component-engine-entry') {
|
|
61
|
+
return id;
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
// Provide empty content for virtual module
|
|
66
|
+
load(id) {
|
|
67
|
+
if (id === '\0virtual:html-component-engine-entry') {
|
|
68
|
+
return 'export default {}';
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
|
|
37
72
|
configResolved(config) {
|
|
38
73
|
resolvedConfig = config;
|
|
39
74
|
// Use process.cwd() as the project root - this is where the vite command is run from
|
|
@@ -127,12 +162,15 @@ export default function htmlComponentEngine(options = {}) {
|
|
|
127
162
|
},
|
|
128
163
|
|
|
129
164
|
/**
|
|
130
|
-
* Generate bundle - compile HTML and copy assets
|
|
165
|
+
* Generate bundle - compile HTML files and copy assets
|
|
131
166
|
*/
|
|
132
167
|
async generateBundle(outputOptions, bundle) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
168
|
+
// Remove virtual entry from bundle
|
|
169
|
+
for (const fileName in bundle) {
|
|
170
|
+
if (fileName.includes('__virtual_entry__') || fileName.includes('virtual_html-component-engine')) {
|
|
171
|
+
delete bundle[fileName];
|
|
172
|
+
}
|
|
173
|
+
}
|
|
136
174
|
|
|
137
175
|
// Get all HTML files from src directory (excluding components)
|
|
138
176
|
const htmlFiles = await getHtmlFiles(srcRoot, '', componentsDir);
|
|
@@ -143,38 +181,25 @@ export default function htmlComponentEngine(options = {}) {
|
|
|
143
181
|
const filePath = path.join(srcRoot, htmlFile);
|
|
144
182
|
let html = await fs.readFile(filePath, 'utf8');
|
|
145
183
|
|
|
146
|
-
//
|
|
184
|
+
// Only compile components - replace <Component> tags
|
|
147
185
|
html = await compileHtml(html, srcRoot, projectRoot);
|
|
148
186
|
|
|
149
|
-
// Inline CSS if enabled
|
|
150
|
-
if (inlineStyles) {
|
|
151
|
-
html = await inlineCss(html, srcRoot, projectRoot);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Inline JS if enabled
|
|
155
|
-
if (inlineScripts) {
|
|
156
|
-
html = await inlineJs(html, srcRoot, projectRoot);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
187
|
// Clean unused placeholders
|
|
160
188
|
html = cleanUnusedPlaceholders(html);
|
|
161
189
|
|
|
162
|
-
//
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
// Add to bundle
|
|
166
|
-
const outputFileName = htmlFile;
|
|
190
|
+
// Output HTML to dist root with just filename (no subdirectories)
|
|
191
|
+
const outputFileName = path.basename(htmlFile);
|
|
167
192
|
this.emitFile({
|
|
168
193
|
type: 'asset',
|
|
169
194
|
fileName: outputFileName,
|
|
170
195
|
source: html,
|
|
171
196
|
});
|
|
172
197
|
|
|
173
|
-
console.log(` ✓ Compiled: ${htmlFile}`);
|
|
198
|
+
console.log(` ✓ Compiled: ${htmlFile} → ${outputFileName}`);
|
|
174
199
|
}
|
|
175
200
|
|
|
176
|
-
// Copy ALL
|
|
177
|
-
await
|
|
201
|
+
// Copy ALL non-HTML files from src/ to dist/ (excluding components/)
|
|
202
|
+
await copyAllNonComponentFiles(srcRoot, componentsDir, this);
|
|
178
203
|
},
|
|
179
204
|
|
|
180
205
|
/**
|
|
@@ -222,32 +247,62 @@ async function getHtmlFiles(dir, base = '', componentsDir = 'components') {
|
|
|
222
247
|
}
|
|
223
248
|
|
|
224
249
|
/**
|
|
225
|
-
* Copy ALL
|
|
226
|
-
*
|
|
250
|
+
* Copy ALL non-HTML files under src/ to dist/ preserving paths
|
|
251
|
+
* Excludes any directory named componentsDir (defaults to "components").
|
|
252
|
+
* @param {string} srcPath - src directory path
|
|
253
|
+
* @param {string} componentsDir - directory name to exclude
|
|
227
254
|
* @param {object} context - Rollup plugin context
|
|
228
255
|
*/
|
|
229
|
-
async function
|
|
230
|
-
|
|
231
|
-
await fs.access(assetsPath);
|
|
232
|
-
} catch {
|
|
233
|
-
console.log(' No assets directory found, skipping asset copy');
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
256
|
+
async function copyAllNonComponentFiles(srcPath, componentsDir = 'components', context) {
|
|
257
|
+
const allFiles = await getAllFilesExcludingDir(srcPath, componentsDir);
|
|
236
258
|
|
|
237
|
-
|
|
259
|
+
let copiedCount = 0;
|
|
260
|
+
for (const file of allFiles) {
|
|
261
|
+
const relativePath = path.relative(srcPath, file).replace(/\\/g, '/');
|
|
238
262
|
|
|
239
|
-
|
|
263
|
+
// HTML is handled separately (compiled + emitted)
|
|
264
|
+
if (relativePath.toLowerCase().endsWith('.html')) continue;
|
|
240
265
|
|
|
241
|
-
for (const file of assetFiles) {
|
|
242
|
-
const relativePath = path.relative(assetsPath, file);
|
|
243
266
|
const content = await fs.readFile(file);
|
|
244
|
-
|
|
245
267
|
context.emitFile({
|
|
246
268
|
type: 'asset',
|
|
247
|
-
fileName:
|
|
269
|
+
fileName: relativePath,
|
|
248
270
|
source: content,
|
|
249
271
|
});
|
|
272
|
+
copiedCount++;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
console.log(` Copied ${copiedCount} non-HTML file(s)`);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Get all files recursively from a directory, excluding a directory name.
|
|
280
|
+
* @param {string} dir - Directory to search
|
|
281
|
+
* @param {string} excludedDirName - Directory name to exclude (e.g., "components")
|
|
282
|
+
* @returns {Promise<string[]>} - Array of absolute file paths
|
|
283
|
+
*/
|
|
284
|
+
async function getAllFilesExcludingDir(dir, excludedDirName) {
|
|
285
|
+
const files = [];
|
|
286
|
+
|
|
287
|
+
try {
|
|
288
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
289
|
+
|
|
290
|
+
for (const entry of entries) {
|
|
291
|
+
const fullPath = path.join(dir, entry.name);
|
|
292
|
+
|
|
293
|
+
if (entry.isDirectory()) {
|
|
294
|
+
if (entry.name === excludedDirName) continue;
|
|
295
|
+
const subFiles = await getAllFilesExcludingDir(fullPath, excludedDirName);
|
|
296
|
+
files.push(...subFiles);
|
|
297
|
+
} else {
|
|
298
|
+
files.push(fullPath);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
} catch (error) {
|
|
302
|
+
console.warn(`Could not read directory ${dir}: ${error.message}`);
|
|
250
303
|
}
|
|
304
|
+
|
|
305
|
+
return files;
|
|
251
306
|
}
|
|
252
307
|
|
|
253
308
|
/**
|