html-component-engine 0.1.0 → 0.1.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/README.md CHANGED
@@ -1,247 +1,36 @@
1
1
  # HTML Component Engine
2
2
 
3
- A lightweight Vite plugin that enables reusable HTML components for static site generation. Compiles to pure HTML with inlined CSS/JS - no runtime JavaScript required.
3
+ A lightweight Vite plugin for HTML components.
4
4
 
5
- ## Features
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
- npm install html-component-engine
8
+ npx html-component-engine
19
9
  ```
20
10
 
21
- Requires Vite ^7.0.0
11
+ ## Commands
22
12
 
23
- ## Folder Structure
13
+ ```bash
14
+ npm run dev # Development server
15
+ npm run build # Production build
16
+ ```
24
17
 
25
- Your project must follow this structure:
18
+ ## Project Structure
26
19
 
27
20
  ```
28
21
  src/
29
- pages/ # HTML pages (compiled to dist/*.html)
30
- index.html
31
- about.html
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
- ### JavaScript Components
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 # Compiled HTML with inlined CSS
195
- about.html # All components resolved
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.0",
42
+ "version": "0.1.1",
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.0"
51
+ "html-component-engine": "^0.1.1"
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.0",
3
+ "version": "0.1.1",
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 = true,
25
- inlineScripts = true,
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
- const outDir = outputOptions.dir || path.resolve(projectRoot, 'dist');
134
-
135
- console.log(`\n📦 Generating output to: ${outDir}`);
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,37 +181,24 @@ 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
- // Compile components
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
- // Remove Vite-specific scripts
163
- html = html.replace(/<script[^>]*@vite[^>]*>[\s\S]*?<\/script>/gi, '');
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 assets to dist/assets (including CSS for reference)
201
+ // Copy ALL assets to dist/assets
177
202
  await copyAllAssets(assetsRoot, this);
178
203
  },
179
204
 
@@ -222,7 +247,7 @@ async function getHtmlFiles(dir, base = '', componentsDir = 'components') {
222
247
  }
223
248
 
224
249
  /**
225
- * Copy ALL assets to output directory (including CSS/JS)
250
+ * Copy ALL assets to dist/assets/ directory
226
251
  * @param {string} assetsPath - Assets directory path
227
252
  * @param {object} context - Rollup plugin context
228
253
  */
@@ -236,15 +261,16 @@ async function copyAllAssets(assetsPath, context) {
236
261
 
237
262
  const assetFiles = await getAllFiles(assetsPath);
238
263
 
239
- console.log(` Copying ${assetFiles.length} asset file(s)`);
264
+ console.log(` Copying ${assetFiles.length} asset file(s) to assets/`);
240
265
 
241
266
  for (const file of assetFiles) {
242
267
  const relativePath = path.relative(assetsPath, file);
243
268
  const content = await fs.readFile(file);
244
269
 
270
+ // Copy to dist/assets/ subdirectory
245
271
  context.emitFile({
246
272
  type: 'asset',
247
- fileName: path.join('assets', relativePath).replace(/\\/g, '/'),
273
+ fileName: `assets/${relativePath.replace(/\\/g, '/')}`,
248
274
  source: content,
249
275
  });
250
276
  }