html-component-engine 0.1.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.
package/bin/cli.js ADDED
@@ -0,0 +1,529 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import readline from 'readline';
6
+ import { fileURLToPath } from 'url';
7
+
8
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
+
10
+ // ANSI colors
11
+ const colors = {
12
+ reset: '\x1b[0m',
13
+ bright: '\x1b[1m',
14
+ green: '\x1b[32m',
15
+ cyan: '\x1b[36m',
16
+ yellow: '\x1b[33m',
17
+ red: '\x1b[31m',
18
+ dim: '\x1b[2m',
19
+ };
20
+
21
+ function log(message, color = 'reset') {
22
+ console.log(`${colors[color]}${message}${colors.reset}`);
23
+ }
24
+
25
+ function createReadlineInterface() {
26
+ return readline.createInterface({
27
+ input: process.stdin,
28
+ output: process.stdout,
29
+ });
30
+ }
31
+
32
+ function question(rl, prompt) {
33
+ return new Promise((resolve) => {
34
+ rl.question(prompt, resolve);
35
+ });
36
+ }
37
+
38
+ // Template files
39
+ const templates = {
40
+ 'package.json': (projectName) => `{
41
+ "name": "${projectName}",
42
+ "version": "0.1.0",
43
+ "type": "module",
44
+ "scripts": {
45
+ "dev": "vite",
46
+ "build": "vite build",
47
+ "preview": "vite preview"
48
+ },
49
+ "devDependencies": {
50
+ "vite": "^7.0.0",
51
+ "html-component-engine": "^0.1.0"
52
+ }
53
+ }
54
+ `,
55
+
56
+ 'vite.config.js': () => `import htmlComponentEngine from 'html-component-engine';
57
+
58
+ export default {
59
+ plugins: [
60
+ htmlComponentEngine({
61
+ srcDir: 'src',
62
+ componentsDir: 'components',
63
+ assetsDir: 'assets',
64
+ })
65
+ ],
66
+ publicDir: 'src/assets',
67
+ build: {
68
+ outDir: 'dist',
69
+ emptyOutDir: true,
70
+ }
71
+ };
72
+ `,
73
+
74
+ 'src/index.html': () => `<!DOCTYPE html>
75
+ <html lang="en">
76
+ <head>
77
+ <meta charset="UTF-8">
78
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
79
+ <title>Home | My Website</title>
80
+ <link rel="stylesheet" href="/styles/main.css">
81
+ </head>
82
+ <body>
83
+ <Component src="Header" title="My Website" />
84
+
85
+ <main class="container">
86
+ <h2>Welcome to My Website</h2>
87
+ <p>This is a sample page using HTML Component Engine.</p>
88
+
89
+ <section class="cards">
90
+ <Component name="Card" title="Getting Started">
91
+ <p>Edit <code>src/index.html</code> to modify this page.</p>
92
+ <p>Components are in <code>src/components/</code>.</p>
93
+ </Component>
94
+
95
+ <Component name="Card" title="Features">
96
+ <ul>
97
+ <li>Reusable HTML components</li>
98
+ <li>Props and variants support</li>
99
+ <li>Slot-based children</li>
100
+ <li>CSS/JS inlining for production</li>
101
+ </ul>
102
+ </Component>
103
+ </section>
104
+
105
+ <Component src="Button" text="Learn More" variant="primary" />
106
+ </main>
107
+
108
+ <Component src="Footer" year="2025" />
109
+ </body>
110
+ </html>
111
+ `,
112
+
113
+ 'src/about.html': () => `<!DOCTYPE html>
114
+ <html lang="en">
115
+ <head>
116
+ <meta charset="UTF-8">
117
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
118
+ <title>About | My Website</title>
119
+ <link rel="stylesheet" href="/styles/main.css">
120
+ </head>
121
+ <body>
122
+ <Component src="Header" title="My Website" />
123
+
124
+ <main class="container">
125
+ <h2>About Us</h2>
126
+ <p>This is the about page.</p>
127
+
128
+ <Component name="Card" title="Our Mission">
129
+ <p>Building great websites with simple, reusable HTML components.</p>
130
+ </Component>
131
+
132
+ <Component src="Button" text="Go Home" variant="secondary" href="/" />
133
+ </main>
134
+
135
+ <Component src="Footer" year="2025" />
136
+ </body>
137
+ </html>
138
+ `,
139
+
140
+ 'src/components/Header.html': () => `<header class="header">
141
+ <div class="header-content">
142
+ <h1 class="logo">{{ title }}</h1>
143
+ <nav class="nav">
144
+ <a href="/">Home</a>
145
+ <a href="/about">About</a>
146
+ </nav>
147
+ </div>
148
+ </header>
149
+ `,
150
+
151
+ 'src/components/Footer.html': () => `<footer class="footer">
152
+ <div class="footer-content">
153
+ <p>&copy; {{ year }} My Website. All rights reserved.</p>
154
+ </div>
155
+ </footer>
156
+ `,
157
+
158
+ 'src/components/Card.html': () => `<div class="card">
159
+ <div class="card-header">
160
+ <h3>{{ title }}</h3>
161
+ </div>
162
+ <div class="card-body">
163
+ {{ children }}
164
+ </div>
165
+ </div>
166
+ `,
167
+
168
+ 'src/components/Button.html': () => `<!-- variants: primary=btn-primary, secondary=btn-secondary, outline=btn-outline -->
169
+ <a class="btn {{ variantClasses }}" href="{{ href }}">{{ text }}</a>
170
+ `,
171
+
172
+ 'src/assets/styles/main.css': () => `/* Reset */
173
+ *, *::before, *::after {
174
+ box-sizing: border-box;
175
+ margin: 0;
176
+ padding: 0;
177
+ }
178
+
179
+ /* Base */
180
+ :root {
181
+ --primary-color: #3b82f6;
182
+ --secondary-color: #64748b;
183
+ --text-color: #1e293b;
184
+ --bg-color: #f8fafc;
185
+ --card-bg: #ffffff;
186
+ --border-color: #e2e8f0;
187
+ --radius: 8px;
188
+ }
189
+
190
+ body {
191
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
192
+ line-height: 1.6;
193
+ color: var(--text-color);
194
+ background-color: var(--bg-color);
195
+ min-height: 100vh;
196
+ display: flex;
197
+ flex-direction: column;
198
+ }
199
+
200
+ /* Container */
201
+ .container {
202
+ max-width: 1200px;
203
+ margin: 0 auto;
204
+ padding: 2rem;
205
+ flex: 1;
206
+ }
207
+
208
+ /* Header */
209
+ .header {
210
+ background: var(--card-bg);
211
+ border-bottom: 1px solid var(--border-color);
212
+ padding: 1rem 2rem;
213
+ }
214
+
215
+ .header-content {
216
+ max-width: 1200px;
217
+ margin: 0 auto;
218
+ display: flex;
219
+ justify-content: space-between;
220
+ align-items: center;
221
+ }
222
+
223
+ .logo {
224
+ font-size: 1.5rem;
225
+ color: var(--primary-color);
226
+ }
227
+
228
+ .nav {
229
+ display: flex;
230
+ gap: 1.5rem;
231
+ }
232
+
233
+ .nav a {
234
+ color: var(--text-color);
235
+ text-decoration: none;
236
+ font-weight: 500;
237
+ transition: color 0.2s;
238
+ }
239
+
240
+ .nav a:hover {
241
+ color: var(--primary-color);
242
+ }
243
+
244
+ /* Footer */
245
+ .footer {
246
+ background: var(--text-color);
247
+ color: var(--bg-color);
248
+ padding: 1.5rem 2rem;
249
+ margin-top: auto;
250
+ }
251
+
252
+ .footer-content {
253
+ max-width: 1200px;
254
+ margin: 0 auto;
255
+ text-align: center;
256
+ }
257
+
258
+ /* Cards */
259
+ .cards {
260
+ display: grid;
261
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
262
+ gap: 1.5rem;
263
+ margin: 2rem 0;
264
+ }
265
+
266
+ .card {
267
+ background: var(--card-bg);
268
+ border: 1px solid var(--border-color);
269
+ border-radius: var(--radius);
270
+ overflow: hidden;
271
+ }
272
+
273
+ .card-header {
274
+ padding: 1rem 1.5rem;
275
+ border-bottom: 1px solid var(--border-color);
276
+ background: var(--bg-color);
277
+ }
278
+
279
+ .card-header h3 {
280
+ font-size: 1.125rem;
281
+ }
282
+
283
+ .card-body {
284
+ padding: 1.5rem;
285
+ }
286
+
287
+ .card-body ul {
288
+ padding-left: 1.5rem;
289
+ }
290
+
291
+ .card-body li {
292
+ margin-bottom: 0.5rem;
293
+ }
294
+
295
+ .card-body code {
296
+ background: var(--bg-color);
297
+ padding: 0.2em 0.4em;
298
+ border-radius: 4px;
299
+ font-size: 0.9em;
300
+ }
301
+
302
+ /* Buttons */
303
+ .btn {
304
+ display: inline-block;
305
+ padding: 0.75rem 1.5rem;
306
+ border-radius: var(--radius);
307
+ text-decoration: none;
308
+ font-weight: 500;
309
+ transition: all 0.2s;
310
+ cursor: pointer;
311
+ border: 2px solid transparent;
312
+ }
313
+
314
+ .btn-primary {
315
+ background: var(--primary-color);
316
+ color: white;
317
+ }
318
+
319
+ .btn-primary:hover {
320
+ background: #2563eb;
321
+ }
322
+
323
+ .btn-secondary {
324
+ background: var(--secondary-color);
325
+ color: white;
326
+ }
327
+
328
+ .btn-secondary:hover {
329
+ background: #475569;
330
+ }
331
+
332
+ .btn-outline {
333
+ background: transparent;
334
+ border-color: var(--primary-color);
335
+ color: var(--primary-color);
336
+ }
337
+
338
+ .btn-outline:hover {
339
+ background: var(--primary-color);
340
+ color: white;
341
+ }
342
+
343
+ /* Typography */
344
+ h2 {
345
+ margin-bottom: 1rem;
346
+ }
347
+
348
+ p {
349
+ margin-bottom: 1rem;
350
+ }
351
+
352
+ section {
353
+ margin-bottom: 2rem;
354
+ }
355
+ `,
356
+
357
+ '.gitignore': () => `node_modules
358
+ dist
359
+ .vite
360
+ *.log
361
+ .DS_Store
362
+ `,
363
+
364
+ 'README.md': (projectName) => `# ${projectName}
365
+
366
+ A website built with [HTML Component Engine](https://github.com/taymakz/html-component-engine).
367
+
368
+ ## Getting Started
369
+
370
+ \`\`\`bash
371
+ # Install dependencies
372
+ npm install
373
+
374
+ # Start development server
375
+ npm run dev
376
+
377
+ # Build for production
378
+ npm run build
379
+
380
+ # Preview production build
381
+ npm run preview
382
+ \`\`\`
383
+
384
+ ## Project Structure
385
+
386
+ \`\`\`
387
+ ${projectName}/
388
+ ├── src/
389
+ │ ├── index.html # Home page
390
+ │ ├── about.html # About page
391
+ │ ├── components/ # Reusable components
392
+ │ │ ├── Header.html
393
+ │ │ ├── Footer.html
394
+ │ │ ├── Card.html
395
+ │ │ └── Button.html
396
+ │ └── assets/
397
+ │ └── styles/
398
+ │ └── main.css
399
+ ├── vite.config.js
400
+ └── package.json
401
+ \`\`\`
402
+
403
+ ## Component Syntax
404
+
405
+ ### Self-closing components (no children)
406
+ \`\`\`html
407
+ <Component src="Button" text="Click Me" variant="primary" />
408
+ \`\`\`
409
+
410
+ ### Components with children (slots)
411
+ \`\`\`html
412
+ <Component name="Card" title="My Card">
413
+ <p>This content goes into {{ children }}</p>
414
+ </Component>
415
+ \`\`\`
416
+
417
+ ### Defining variants
418
+ \`\`\`html
419
+ <!-- variants: primary=btn-primary, secondary=btn-secondary -->
420
+ <button class="{{ variantClasses }}">{{ text }}</button>
421
+ \`\`\`
422
+ `,
423
+ };
424
+
425
+ async function init() {
426
+ console.log();
427
+ log('🚀 HTML Component Engine - Project Initializer', 'cyan');
428
+ log('━'.repeat(50), 'dim');
429
+ console.log();
430
+
431
+ const rl = createReadlineInterface();
432
+
433
+ try {
434
+ // Ask for project name
435
+ const projectName = await question(
436
+ rl,
437
+ `${colors.cyan}? ${colors.reset}Project name ${colors.dim}(. for current directory)${colors.reset}: `
438
+ );
439
+
440
+ if (!projectName) {
441
+ log('✖ Project name is required', 'red');
442
+ process.exit(1);
443
+ }
444
+
445
+ const targetDir = projectName === '.' ? process.cwd() : path.resolve(process.cwd(), projectName);
446
+ const displayName = projectName === '.' ? path.basename(process.cwd()) : projectName;
447
+
448
+ // Check if directory exists and is not empty
449
+ if (projectName !== '.' && fs.existsSync(targetDir)) {
450
+ const files = fs.readdirSync(targetDir);
451
+ if (files.length > 0) {
452
+ const overwrite = await question(
453
+ rl,
454
+ `${colors.yellow}⚠ Directory "${projectName}" is not empty. Continue? (y/N): ${colors.reset}`
455
+ );
456
+ if (overwrite.toLowerCase() !== 'y') {
457
+ log('✖ Cancelled', 'red');
458
+ process.exit(1);
459
+ }
460
+ }
461
+ }
462
+
463
+ rl.close();
464
+
465
+ console.log();
466
+ log(`Creating project in ${targetDir}...`, 'dim');
467
+ console.log();
468
+
469
+ // Create directories
470
+ const dirs = [
471
+ '',
472
+ 'src',
473
+ 'src/components',
474
+ 'src/assets',
475
+ 'src/assets/styles',
476
+ ];
477
+
478
+ for (const dir of dirs) {
479
+ const dirPath = path.join(targetDir, dir);
480
+ if (!fs.existsSync(dirPath)) {
481
+ fs.mkdirSync(dirPath, { recursive: true });
482
+ }
483
+ }
484
+
485
+ // Create files
486
+ for (const [filePath, getContent] of Object.entries(templates)) {
487
+ const fullPath = path.join(targetDir, filePath);
488
+ const content = typeof getContent === 'function' ? getContent(displayName) : getContent;
489
+ fs.writeFileSync(fullPath, content);
490
+ log(` ✓ ${filePath}`, 'green');
491
+ }
492
+
493
+ console.log();
494
+ log('━'.repeat(50), 'dim');
495
+ log('✅ Project created successfully!', 'green');
496
+ console.log();
497
+ log('Next steps:', 'bright');
498
+ console.log();
499
+
500
+ if (projectName !== '.') {
501
+ log(` cd ${projectName}`, 'cyan');
502
+ }
503
+ log(' npm install', 'cyan');
504
+ log(' npm run dev', 'cyan');
505
+ console.log();
506
+ log('Happy coding! 🎉', 'yellow');
507
+ console.log();
508
+
509
+ } catch (error) {
510
+ rl.close();
511
+ log(`✖ Error: ${error.message}`, 'red');
512
+ process.exit(1);
513
+ }
514
+ }
515
+
516
+ // Parse command line arguments
517
+ const args = process.argv.slice(2);
518
+ const command = args[0];
519
+
520
+ if (command === 'init' || !command) {
521
+ init();
522
+ } else {
523
+ log(`Unknown command: ${command}`, 'red');
524
+ console.log();
525
+ log('Available commands:', 'bright');
526
+ log(' init Create a new HTML Component Engine project', 'dim');
527
+ console.log();
528
+ process.exit(1);
529
+ }
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "html-component-engine",
3
+ "version": "0.1.0",
4
+ "description": "A Vite plugin for HTML component components with a lightweight static site compiler",
5
+ "type": "module",
6
+ "main": "src/index.js",
7
+ "bin": {
8
+ "html-component": "./bin/cli.js"
9
+ },
10
+ "files": [
11
+ "src",
12
+ "bin",
13
+ "templates"
14
+ ],
15
+ "scripts": {
16
+ "test": "vitest run",
17
+ "test:watch": "vitest",
18
+ "test:coverage": "vitest run --coverage"
19
+ },
20
+ "keywords": [
21
+ "vite",
22
+ "plugin",
23
+ "html",
24
+ "components",
25
+ "static-site",
26
+ "template-engine"
27
+ ],
28
+ "author": "",
29
+ "license": "MIT",
30
+ "peerDependencies": {
31
+ "vite": "^7.0.0"
32
+ },
33
+ "devDependencies": {
34
+ "vitest": "^3.0.0",
35
+ "vite": "^7.0.0"
36
+ }
37
+ }