create-template-html-css 1.6.2 → 1.6.4

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
@@ -2,6 +2,13 @@
2
2
 
3
3
  A powerful CLI library to create HTML+CSS element templates. Generate styled UI components in seconds!
4
4
 
5
+ [![npm version](https://img.shields.io/npm/v/create-template-html-css.svg)](https://www.npmjs.com/package/create-template-html-css)
6
+ [![npm downloads](https://img.shields.io/npm/dm/create-template-html-css.svg)](https://www.npmjs.com/package/create-template-html-css)
7
+
8
+ ```bash
9
+ npm install -g create-template-html-css
10
+ ```
11
+
5
12
  ## 📋 Table of Contents
6
13
 
7
14
  - [Features](#features)
@@ -672,6 +679,6 @@ If you find this project helpful, please consider:
672
679
 
673
680
  ---
674
681
 
675
- Made with ❤️ by Ben Shabbat
682
+ Made with ❤️ by David Chen Benshabbat
676
683
 
677
684
  **Happy coding! 🚀**
package/bin/cli.js CHANGED
@@ -13,7 +13,7 @@ program
13
13
  .description(
14
14
  chalk.cyan("🎨 Create HTML/CSS UI component templates in seconds"),
15
15
  )
16
- .version("1.6.2");
16
+ .version("1.6.4");
17
17
 
18
18
  // Add intro message
19
19
  program.on("--help", () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-template-html-css",
3
- "version": "1.6.2",
3
+ "version": "1.6.4",
4
4
  "description": "CLI tool to generate HTML and CSS templates for common UI elements",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/generator.js CHANGED
@@ -1,165 +1,168 @@
1
- const fs = require("fs").promises;
2
- const path = require("path");
3
-
4
- /**
5
- * Formats HTML content with prettier
6
- */
7
- async function formatHtml(htmlContent) {
8
- const prettier = require("prettier");
9
- try {
10
- return await prettier.format(htmlContent, { parser: "html" });
11
- } catch (error) {
12
- return htmlContent;
13
- }
14
- }
15
-
16
- /**
17
- * Formats CSS content with prettier
18
- */
19
- async function formatCss(cssContent) {
20
- const prettier = require("prettier");
21
- try {
22
- return await prettier.format(cssContent, { parser: "css" });
23
- } catch (error) {
24
- return cssContent;
25
- }
26
- }
27
-
28
- /**
29
- * Formats JavaScript content with prettier
30
- */
31
- async function formatJs(jsContent) {
32
- const prettier = require("prettier");
33
- try {
34
- return await prettier.format(jsContent, { parser: "babel" });
35
- } catch (error) {
36
- return jsContent;
37
- }
38
- }
39
-
40
- // Security: Validate component name against whitelist
41
- const VALID_COMPONENTS = [
42
- "button",
43
- "card",
44
- "form",
45
- "navigation",
46
- "modal",
47
- "footer",
48
- "hero",
49
- "slider",
50
- "table",
51
- "spinner",
52
- "animated-card",
53
- "typing-effect",
54
- "fade-gallery",
55
- "grid-layout",
56
- "masonry-grid",
57
- "dashboard-grid",
58
- "flex-layout",
59
- "flex-cards",
60
- "flex-dashboard",
61
- "todo-list",
62
- "counter",
63
- "accordion",
64
- "tabs",
65
- ];
66
-
67
- // Security: Sanitize filename to prevent path traversal
68
- function sanitizeFilename(filename) {
69
- // Remove any path separators and parent directory references
70
- const sanitized = filename.replace(/[\/\\]/g, "").replace(/\.\.+/g, ".");
71
-
72
- // Additional validation: ensure name contains at least one alphanumeric character
73
- if (!sanitized || !/[a-zA-Z0-9]/.test(sanitized)) {
74
- return null;
75
- }
76
-
77
- // Remove any remaining dangerous characters
78
- return sanitized.replace(/[<>:"|?*]/g, "").trim();
79
- }
80
-
81
- async function generateTemplate(options) {
82
- const { component, name, includeJs } = options;
83
-
84
- // Security: Validate component name
85
- if (!VALID_COMPONENTS.includes(component)) {
86
- throw new Error(
87
- `Invalid component: ${component}. Must be one of: ${VALID_COMPONENTS.join(", ")}`,
88
- );
89
- }
90
-
91
- // Security: Sanitize name to prevent path traversal
92
- const safeName = sanitizeFilename(name);
93
- if (!safeName || safeName.length === 0) {
94
- throw new Error("Invalid name provided");
95
- }
96
-
97
- // Create output directory structure
98
- const outputDir = path.join(process.cwd(), safeName);
99
- const cssDir = path.join(outputDir, "css");
100
- const jsDir = path.join(outputDir, "js");
101
-
102
- await fs.mkdir(outputDir, { recursive: true });
103
- await fs.mkdir(cssDir, { recursive: true });
104
- await fs.mkdir(jsDir, { recursive: true });
105
-
106
- // Get template content
107
- const templateDir = path.join(__dirname, "..", "templates", component);
108
-
109
- // Copy HTML file
110
- let htmlContent = await fs.readFile(
111
- path.join(templateDir, "index.html"),
112
- "utf-8",
113
- );
114
-
115
- // Replace placeholder name
116
- htmlContent = htmlContent.replace(/{{name}}/g, safeName);
117
-
118
- // Add script tag if JavaScript is included (pointing to js/ folder)
119
- if (includeJs) {
120
- // Insert script tag before closing </body> tag
121
- htmlContent = htmlContent.replace(
122
- "</body>",
123
- ' <script src="js/script.js"></script>\n</body>',
124
- );
125
- }
126
-
127
- // Update CSS link to point to css/ folder
128
- htmlContent = htmlContent.replace(
129
- /<link[^>]*href="style\.css"[^>]*>/g,
130
- ' <link rel="stylesheet" href="css/style.css">',
131
- );
132
-
133
- // Format HTML before writing
134
- htmlContent = await formatHtml(htmlContent);
135
-
136
- await fs.writeFile(path.join(outputDir, "index.html"), htmlContent);
137
-
138
- // Copy CSS file to css/ folder
139
- const cssContent = await fs.readFile(
140
- path.join(templateDir, "style.css"),
141
- "utf-8",
142
- );
143
- const formattedCss = await formatCss(cssContent);
144
- await fs.writeFile(path.join(cssDir, "style.css"), formattedCss);
145
-
146
- // Copy JS file to js/ folder if requested
147
- if (includeJs) {
148
- try {
149
- const jsContent = await fs.readFile(
150
- path.join(templateDir, "script.js"),
151
- "utf-8",
152
- );
153
- const formattedJs = await formatJs(jsContent);
154
- await fs.writeFile(path.join(jsDir, "script.js"), formattedJs);
155
- } catch (error) {
156
- // If no JS template exists, create a basic one
157
- const basicJs = await formatJs("// Add your JavaScript here\n");
158
- await fs.writeFile(path.join(jsDir, "script.js"), basicJs);
159
- }
160
- }
161
-
162
- return outputDir;
163
- }
164
-
165
- module.exports = { generateTemplate };
1
+ const fs = require("fs").promises;
2
+ const path = require("path");
3
+
4
+ /**
5
+ * Formats HTML content with prettier (optional - falls back to original if not available)
6
+ */
7
+ async function formatHtml(htmlContent) {
8
+ try {
9
+ const prettier = require("prettier");
10
+ return await prettier.format(htmlContent, { parser: "html" });
11
+ } catch (error) {
12
+ // Prettier not installed or error formatting - return original content
13
+ return htmlContent;
14
+ }
15
+ }
16
+
17
+ /**
18
+ * Formats CSS content with prettier (optional - falls back to original if not available)
19
+ */
20
+ async function formatCss(cssContent) {
21
+ try {
22
+ const prettier = require("prettier");
23
+ return await prettier.format(cssContent, { parser: "css" });
24
+ } catch (error) {
25
+ // Prettier not installed or error formatting - return original content
26
+ return cssContent;
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Formats JavaScript content with prettier (optional - falls back to original if not available)
32
+ */
33
+ async function formatJs(jsContent) {
34
+ try {
35
+ const prettier = require("prettier");
36
+ return await prettier.format(jsContent, { parser: "babel" });
37
+ } catch (error) {
38
+ // Prettier not installed or error formatting - return original content
39
+ return jsContent;
40
+ }
41
+ }
42
+
43
+ // Security: Validate component name against whitelist
44
+ const VALID_COMPONENTS = [
45
+ "button",
46
+ "card",
47
+ "form",
48
+ "navigation",
49
+ "modal",
50
+ "footer",
51
+ "hero",
52
+ "slider",
53
+ "table",
54
+ "spinner",
55
+ "animated-card",
56
+ "typing-effect",
57
+ "fade-gallery",
58
+ "grid-layout",
59
+ "masonry-grid",
60
+ "dashboard-grid",
61
+ "flex-layout",
62
+ "flex-cards",
63
+ "flex-dashboard",
64
+ "todo-list",
65
+ "counter",
66
+ "accordion",
67
+ "tabs",
68
+ ];
69
+
70
+ // Security: Sanitize filename to prevent path traversal
71
+ function sanitizeFilename(filename) {
72
+ // Remove any path separators and parent directory references
73
+ const sanitized = filename.replace(/[\/\\]/g, "").replace(/\.\.+/g, ".");
74
+
75
+ // Additional validation: ensure name contains at least one alphanumeric character
76
+ if (!sanitized || !/[a-zA-Z0-9]/.test(sanitized)) {
77
+ return null;
78
+ }
79
+
80
+ // Remove any remaining dangerous characters
81
+ return sanitized.replace(/[<>:"|?*]/g, "").trim();
82
+ }
83
+
84
+ async function generateTemplate(options) {
85
+ const { component, name, includeJs } = options;
86
+
87
+ // Security: Validate component name
88
+ if (!VALID_COMPONENTS.includes(component)) {
89
+ throw new Error(
90
+ `Invalid component: ${component}. Must be one of: ${VALID_COMPONENTS.join(", ")}`,
91
+ );
92
+ }
93
+
94
+ // Security: Sanitize name to prevent path traversal
95
+ const safeName = sanitizeFilename(name);
96
+ if (!safeName || safeName.length === 0) {
97
+ throw new Error("Invalid name provided");
98
+ }
99
+
100
+ // Create output directory structure
101
+ const outputDir = path.join(process.cwd(), safeName);
102
+ const cssDir = path.join(outputDir, "css");
103
+ const jsDir = path.join(outputDir, "js");
104
+
105
+ await fs.mkdir(outputDir, { recursive: true });
106
+ await fs.mkdir(cssDir, { recursive: true });
107
+ await fs.mkdir(jsDir, { recursive: true });
108
+
109
+ // Get template content
110
+ const templateDir = path.join(__dirname, "..", "templates", component);
111
+
112
+ // Copy HTML file
113
+ let htmlContent = await fs.readFile(
114
+ path.join(templateDir, "index.html"),
115
+ "utf-8",
116
+ );
117
+
118
+ // Replace placeholder name
119
+ htmlContent = htmlContent.replace(/{{name}}/g, safeName);
120
+
121
+ // Add script tag if JavaScript is included (pointing to js/ folder)
122
+ if (includeJs) {
123
+ // Insert script tag before closing </body> tag
124
+ htmlContent = htmlContent.replace(
125
+ "</body>",
126
+ ' <script src="js/script.js"></script>\n</body>',
127
+ );
128
+ }
129
+
130
+ // Update CSS link to point to css/ folder
131
+ htmlContent = htmlContent.replace(
132
+ /<link[^>]*href="style\.css"[^>]*>/g,
133
+ ' <link rel="stylesheet" href="css/style.css">',
134
+ );
135
+
136
+ // Format HTML before writing
137
+ htmlContent = await formatHtml(htmlContent);
138
+
139
+ await fs.writeFile(path.join(outputDir, "index.html"), htmlContent);
140
+
141
+ // Copy CSS file to css/ folder
142
+ const cssContent = await fs.readFile(
143
+ path.join(templateDir, "style.css"),
144
+ "utf-8",
145
+ );
146
+ const formattedCss = await formatCss(cssContent);
147
+ await fs.writeFile(path.join(cssDir, "style.css"), formattedCss);
148
+
149
+ // Copy JS file to js/ folder if requested
150
+ if (includeJs) {
151
+ try {
152
+ const jsContent = await fs.readFile(
153
+ path.join(templateDir, "script.js"),
154
+ "utf-8",
155
+ );
156
+ const formattedJs = await formatJs(jsContent);
157
+ await fs.writeFile(path.join(jsDir, "script.js"), formattedJs);
158
+ } catch (error) {
159
+ // If no JS template exists, create a basic one
160
+ const basicJs = await formatJs("// Add your JavaScript here\n");
161
+ await fs.writeFile(path.join(jsDir, "script.js"), basicJs);
162
+ }
163
+ }
164
+
165
+ return outputDir;
166
+ }
167
+
168
+ module.exports = { generateTemplate };