minimaz-cli 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.
@@ -1,176 +0,0 @@
1
- import fs from 'fs-extra'
2
- import path from 'path'
3
- import CleanCSS from 'clean-css'
4
- import { minify as minifyHtml } from 'html-minifier-terser'
5
- import { minify as minifyJs, MinifyOutput } from 'terser'
6
-
7
- import { loadConfig } from '../utils/loadConfig.js'
8
- import { log } from '../utils/logService.js'
9
- import { applyReplacements, getFile } from '../utils/functions.js'
10
-
11
- /**
12
- * Build the project according to configuration.
13
- */
14
- export async function build(): Promise<void> {
15
- try {
16
- const config: any = await loadConfig()
17
- const distDir: string = path.resolve(process.cwd(), config.dist || 'dist')
18
-
19
- // Remove previous dist folder and recreate it
20
- await fs.remove(distDir)
21
- await fs.ensureDir(distDir)
22
-
23
- /**
24
- * Process a single folder
25
- * @param srcPathRel Relative path of the source folder
26
- * @param destName Destination folder name inside dist
27
- */
28
- async function processFolder(srcPathRel: string, destName: any): Promise<void> {
29
- const fullSrc: string = path.resolve(process.cwd(), srcPathRel)
30
- const fullDest: string = path.join(distDir, destName)
31
-
32
- if (!(await fs.pathExists(fullSrc))) {
33
- log('warn', `Folder not found: ${srcPathRel}`)
34
- return
35
- }
36
-
37
- const cssChunks: string[] = []
38
- const jsChunks: string[] = []
39
-
40
- // Recursive walk function to process files
41
- async function walk(src: string, dest: string): Promise<void> {
42
- await fs.ensureDir(dest)
43
-
44
- for (const item of await fs.readdir(src)) {
45
- const srcPath: string = path.join(src, item)
46
- const destPath: string = path.join(dest, item)
47
- const stat: fs.Stats = await fs.stat(srcPath)
48
- const ext: string = path.extname(item).toLowerCase()
49
-
50
- if (stat.isDirectory()) {
51
- await walk(srcPath, destPath)
52
- continue
53
- }
54
-
55
- try {
56
- switch (ext) {
57
- case '.html': {
58
- let content: string = await getFile(srcPath, config.replace)
59
- if (config.minify?.html) {
60
- content = await minifyHtml(content, {
61
- collapseWhitespace: true,
62
- removeComments: true,
63
- minifyJS: config.minify?.ts,
64
- minifyCSS: config.minify?.css
65
- })
66
- }
67
- await fs.outputFile(destPath, content)
68
- break
69
- }
70
-
71
- case '.css': {
72
- const content: string = await getFile(srcPath, config.replace)
73
- cssChunks.push(content)
74
- break
75
- }
76
-
77
- case '.js': {
78
- const content: string = await getFile(srcPath, config.replace)
79
- jsChunks.push(content)
80
- break
81
- }
82
- /*
83
- case '.ts': {
84
- const result = await esbuild.build({
85
- entryPoints: [srcPath],
86
- bundle: false,
87
- minify: !!config.minify?.ts,
88
- platform: 'browser',
89
- format: 'esm',
90
- write: false
91
- })
92
- let compiled = result.outputFiles[0].text
93
- compiled = applyReplacements(compiled, config.replace)
94
- if (srcPathRel === config.src) jsChunks.push(compiled)
95
- break
96
- }
97
- */
98
- default:
99
- await fs.copy(srcPath, destPath)
100
- break
101
- }
102
- } catch (error: any) {
103
- log('error', `Failed to process file: ${srcPath} - ${error.message}`)
104
- }
105
- }
106
- }
107
-
108
- await walk(fullSrc, fullDest)
109
-
110
- // Merge CSS/JS only for the root source folder
111
- if (srcPathRel === config.src) {
112
- try {
113
- if (config.styles?.length) {
114
- for (const style of config.styles) {
115
- const fullPath: string = path.resolve(process.cwd(), style)
116
- if (await fs.pathExists(fullPath)) {
117
- let css: string = await fs.readFile(fullPath, 'utf-8')
118
- css = applyReplacements(css, config.replace)
119
- cssChunks.push(css)
120
- } else {
121
- log('warn', `Style file not found: ${style}`)
122
- }
123
- }
124
- }
125
-
126
- if (config.scripts?.length) {
127
- for (const script of config.scripts) {
128
- const fullPath: string = path.resolve(process.cwd(), script)
129
- if (await fs.pathExists(fullPath)) {
130
- let js: string = await fs.readFile(fullPath, 'utf-8')
131
- js = applyReplacements(js, config.replace)
132
- jsChunks.push(js)
133
- } else {
134
- log('warn', `Script file not found: ${script}`)
135
- }
136
- }
137
- }
138
-
139
- // Write final CSS bundle
140
- if (cssChunks.length > 0) {
141
- let finalCss: string = cssChunks.join('')
142
- if (config.minify?.css) finalCss = new CleanCSS().minify(finalCss).styles
143
- await fs.outputFile(path.join(distDir, 'style.css'), finalCss)
144
- }
145
-
146
- // Write final JS bundle
147
- if (jsChunks.length > 0) {
148
- let finalJs: string = jsChunks.join('')
149
- if (config.minify?.js) {
150
- const minified: MinifyOutput = await minifyJs(finalJs)
151
- finalJs = minified.code ?? ''
152
- }
153
- if (finalJs) await fs.outputFile(path.join(distDir, 'script.js'), finalJs)
154
- }
155
- } catch (error: any) {
156
- log('error', `Failed to merge CSS/JS: ${error.message}`)
157
- }
158
- }
159
-
160
- log('success', `Processed folder: ${srcPathRel} -> /${destName}`)
161
- }
162
-
163
- // Process all folders defined in configuration
164
- if (config.folders && Object.keys(config.folders).length > 0) {
165
- for (const [srcPathRel, destName] of Object.entries(config.folders)) {
166
- await processFolder(srcPathRel, destName)
167
- }
168
- } else {
169
- log('warn', 'No folders defined in config. Nothing to build.')
170
- }
171
-
172
- log('success', `Build completed. Output saved in /${config.dist}`)
173
- } catch (error: any) {
174
- log('error', `Build failed: ${error.message}`)
175
- }
176
- }
@@ -1,33 +0,0 @@
1
- /**
2
- * Displays CLI usage information for Minimaz.
3
- * Lists all available commands and their options.
4
- *
5
- * Example:
6
- * minimaz init my-site -t default
7
- * minimaz build
8
- * minimaz template --list
9
- */
10
- export function help(): void {
11
- console.log([
12
- 'Usage:',
13
- '',
14
- '\tminimaz init | i <project-name>',
15
- '\t\tCreate a new project (default: "minimaz-site")',
16
- '\t\tOptions:',
17
- '\t\t\t--template | -t <template-name>\tUse a global template (default: "default")',
18
- '',
19
- '\tminimaz build | b',
20
- '\t\tBuild and minify files into the dist folder',
21
- '',
22
- '\tminimaz template | t [path]',
23
- '\t\tSave current folder as a template (no path = current folder)',
24
- '\t\tOptions:',
25
- '\t\t\t--list | -l\tList available global templates',
26
- '\t\t\t--delete | -d <template-name>\tDelete a global template',
27
- '',
28
- '\tminimaz help | h',
29
- '\t\tShow this help message'
30
- ].join('\n'));
31
- }
32
-
33
- // @TODO add help for each command
@@ -1,56 +0,0 @@
1
- import fs from 'fs-extra'
2
- import path from 'path'
3
- import os from 'os'
4
- import { log } from '../utils/logService.js'
5
- import { createGlobalDir } from '../utils/functions.js'
6
-
7
- /**
8
- * Initializes a new Minimaz project by copying a predefined template
9
- * from the user's global Minimaz directory (~/.minimaz/templates).
10
- *
11
- * @param projectName - The name of the project folder to create.
12
- * @param options - Optional settings (e.g. { template: 'landing' }).
13
- *
14
- * Process:
15
- * 1. Ensures the global `.minimaz` directory exists.
16
- * 2. Locates the chosen template inside `.minimaz/templates`.
17
- * 3. Copies the template files to the new project folder.
18
- * 4. Adds `.gitignore` if available.
19
- * 5. Logs actions and errors.
20
- */
21
- export async function init(projectName: string, options: any = {}): Promise<void> {
22
- const minimazDir: string = path.join(os.homedir(), '.minimaz')
23
-
24
- // Step 1: Create global folder if missing
25
- if (!await fs.pathExists(minimazDir)) {
26
- log('info', `Global folder '${minimazDir}' not found. Creating...`)
27
- await createGlobalDir()
28
- }
29
-
30
- // Step 2: Determine paths
31
- const templateDir: string = path.join(minimazDir, 'templates', options.template || 'default')
32
- const targetDir: string = path.resolve(process.cwd(), projectName)
33
-
34
- // Step 3: Validate template existence
35
- if (!await fs.pathExists(templateDir))
36
- throw new Error(`Template '${options.template}' not found.`)
37
-
38
- try {
39
- // Step 4: Copy template files to target directory
40
- await fs.copy(templateDir, targetDir)
41
-
42
- // Step 5: Copy .gitignore if available at the parent level
43
- const gitignoreSrc: string = path.join(templateDir, '..', 'gitignore')
44
- const gitignoreDest: string = path.join(targetDir, '.gitignore')
45
- if (await fs.pathExists(gitignoreSrc)) {
46
- await fs.copy(gitignoreSrc, gitignoreDest)
47
- log('info', `.gitignore added to project.`)
48
- }
49
-
50
- // Step 6: Confirm success
51
- log('success', `Project '${projectName}' created using template '${options.template}'.`)
52
- } catch (error: any) {
53
- // Step 7: Handle any failure during copy
54
- throw new Error(`Failed to create project: ${error.message}`)
55
- }
56
- }
@@ -1,146 +0,0 @@
1
- import fs from 'fs-extra'
2
- import os from 'os'
3
- import path from 'path'
4
- import { log } from '../utils/logService.js'
5
- import { askQuestion, listTemplates, getGlobalNodeModulesPath } from '../utils/functions.js'
6
-
7
- /**
8
- * Handles all template-related operations:
9
- * - Save current folder as a global template
10
- * - Update existing templates
11
- * - Delete templates
12
- * - Sync templates from node_modules
13
- *
14
- * @param targetPath - Optional path of the folder to save as template
15
- * @param options - CLI flags (--list, --delete, --update, etc.)
16
- */
17
- export async function template(targetPath?: string, options: any = {}): Promise<void> {
18
- const templatesDir: string = path.join(os.homedir(), '.minimaz', 'templates')
19
- const deleteName: string | undefined = options.delete || options.d
20
- const updateName: string | undefined = options.update || options.u
21
-
22
- if (deleteName) return await deleteTemplate(templatesDir, deleteName)
23
- if (options.list || options.l) return await listTemplates(templatesDir)
24
-
25
- // --- UPDATE MODE ---
26
- if (updateName !== undefined) {
27
- if (typeof updateName === 'string' && updateName.trim()) {
28
- return await updateSingleTemplate(templatesDir, updateName.trim())
29
- } else {
30
- return await updateFromNodeModules(templatesDir)
31
- }
32
- }
33
-
34
- // Default action: save current folder as a template
35
- await saveTemplate(templatesDir, targetPath)
36
- }
37
-
38
- /**
39
- * Updates a single template with files from the current working directory.
40
- *
41
- * @param templatesDir - Global templates directory (~/.minimaz/templates)
42
- * @param templateName - Name of the template to update
43
- */
44
- async function updateSingleTemplate(templatesDir: string, templateName: string): Promise<void> {
45
- const sourceDir: string = path.resolve(process.cwd())
46
- const targetDir: string = path.join(templatesDir, templateName)
47
-
48
- if (!await fs.pathExists(targetDir))
49
- throw new Error(`Template '${templateName}' not found in ~/.minimaz/templates`)
50
-
51
- const answer: string = await askQuestion(`❓ Update template '${templateName}' with current directory? (Y/N) `)
52
- if (answer !== 'y' && answer !== '') {
53
- log('info', 'Update cancelled.')
54
- return
55
- }
56
-
57
- try {
58
- await fs.copy(sourceDir, targetDir, { overwrite: true })
59
- log('success', `Template '${templateName}' updated from current directory.`)
60
- } catch (error: any) {
61
- throw new Error(`Failed to update '${templateName}': ${error.message}`)
62
- }
63
- }
64
-
65
- /**
66
- * Updates all templates from the global node_modules/minimaz/src/templates folder.
67
- * This ensures the local templates are synced with the installed package.
68
- *
69
- * @param templatesDir - Global templates directory (~/.minimaz/templates)
70
- */
71
- async function updateFromNodeModules(templatesDir: string): Promise<void> {
72
- const nodeModulesPath: string = path.join(getGlobalNodeModulesPath(), 'src', 'templates')
73
-
74
- if (!await fs.pathExists(nodeModulesPath)) throw new Error(`'node_modules/minimaz/src/templates' not found.`)
75
-
76
- const items: string[] = await fs.readdir(nodeModulesPath)
77
-
78
- const answer: string = await askQuestion(`⚠️ Update local templates overwriting them with defaults? (Y/N): `)
79
- if (answer !== 'y' && answer !== '') {
80
- log('info', 'Update cancelled.')
81
- return
82
- }
83
-
84
- try {
85
- for (const item of items) {
86
- const src: string = path.join(nodeModulesPath, item)
87
- const dest: string = path.join(templatesDir, item)
88
- await fs.copy(src, dest, { overwrite: true })
89
- log('success', `Updated '${item}'`)
90
- }
91
- log('info', `✨ All templates and files updated successfully.`)
92
- } catch (error: any) {
93
- throw new Error(`Update failed: ${error.message}`)
94
- }
95
- }
96
-
97
- /**
98
- * Deletes a global template by name from ~/.minimaz/templates.
99
- *
100
- * @param dir - Global templates directory
101
- * @param name - Template name to delete
102
- */
103
- async function deleteTemplate(dir: string, name: string): Promise<void> {
104
- if (!name) throw new Error('No template name specified to delete.')
105
- const target: string = path.join(dir, name)
106
- if (!await fs.pathExists(target)) throw new Error(`Template not found: ${name}`)
107
-
108
- const confirm = await askQuestion(`❓ Confirm delete '${name}'? (Y/N) `)
109
- if (confirm.toLowerCase() !== 'y') {
110
- log('info', 'Delete cancelled.')
111
- return
112
- }
113
-
114
- try {
115
- await fs.remove(target)
116
- log('success', `Template '${name}' deleted.`)
117
- } catch (error: any) {
118
- throw new Error(`Delete error: ${error.message}`)
119
- }
120
- }
121
-
122
- /**
123
- * Saves a folder (current or specified) as a new global template.
124
- *
125
- * @param dir - Global templates directory (~/.minimaz/templates)
126
- * @param targetPath - Optional path to save as a template
127
- */
128
- async function saveTemplate(dir: string, targetPath?: string): Promise<void> {
129
- let source: string = targetPath ? path.resolve(process.cwd(), targetPath) : process.cwd()
130
-
131
- if (!await fs.pathExists(source)) {
132
- log('warn', `Path not found: ${source}`)
133
- const answer: string = (await askQuestion('❓ Use current directory instead? (Y/N):\t')).trim().toLowerCase()
134
- if (answer !== 'y' && answer !== '') throw new Error('Operation cancelled.')
135
- source = process.cwd()
136
- }
137
-
138
- try {
139
- await fs.ensureDir(dir)
140
- const dest: string = path.join(dir, path.basename(source))
141
- await fs.copy(source, dest)
142
- log('success', `Template saved to ${dest}`)
143
- } catch (error: any) {
144
- throw new Error(`Failed to save template: ${error.message}`)
145
- }
146
- }
@@ -1,26 +0,0 @@
1
- {
2
- "src": "src",
3
- "dist": "dist",
4
- "public": "public",
5
- "minify": {
6
- "html": true,
7
- "css": true,
8
- "js": true,
9
- "ts": true
10
- },
11
- "replace": {
12
- "../public/": "public/"
13
- },
14
- "styles": [
15
- "style.css",
16
- "style-2.css"
17
- ],
18
- "scripts": [
19
- "script.js",
20
- "script-2.js"
21
- ],
22
- "folders": {
23
- "src": "",
24
- "public": "public"
25
- }
26
- }
File without changes
@@ -1,80 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Welcome to Minimaz</title>
7
- <link rel="stylesheet" href="style.css" />
8
- <link rel="icon" href="../public/favicon.ico" type="image/x-icon" />
9
- </head>
10
- <body>
11
- <header>
12
- <nav>
13
- <a class="btn active" href="#">Home</a>
14
- <a class="btn" href="pages/about.html">About</a>
15
- <a class="btn" href="https://github.com/zeller-dev/minimaz" target="_blank">GitHub</a>
16
- </nav>
17
- </header>
18
-
19
- <main class="container">
20
- <h1>🚀 Welcome to Minimaz</h1>
21
- <p><strong>Minimaz</strong> is a minimal static site builder designed for speed and simplicity. It helps you:</p>
22
- <ul>
23
- <li>💡 Organize your files with a simple structure</li>
24
- <li>⚙️ Automatically build and minify HTML, CSS, JS, and TS</li>
25
- <li>🔄 Customize file inclusion order using <code>minimaz.config.json</code></li>
26
- </ul>
27
-
28
- <h2>🔧 Getting Started</h2>
29
- <p>Install and use Minimaz with the following commands:</p>
30
- <pre><code># 🔨 Initialize a new project
31
- npx minimaz init my-site
32
-
33
- # 🏗️ Build the site
34
- npx minimaz build
35
-
36
- # 📦 Save template or list/delete existing ones
37
- npx minimaz template &lt;path&gt; [--list|-l] [--delete|-d]
38
-
39
- # 💬 Show help guide
40
- npx minimaz help</code></pre>
41
-
42
- <h2>📁 Project Structure</h2>
43
- <pre><code>my-site/
44
- ├── src/ # Source files (HTML, CSS, JS, TS)
45
- ├── public/ # Static assets (images, fonts, etc.)
46
- ├── dist/ # Output directory (auto-generated)
47
- └── minimaz.config.json # Configuration file</code></pre>
48
-
49
- <h2>⚙️ Configuration</h2>
50
- <pre><code>{
51
- "src": "src",
52
- "dist": "dist",
53
- "public": "public",
54
- "minify": { "html": true, "css": true, "js": true, "ts": true },
55
- "replace": { "../public/": "public/" },
56
- "styles": [ "reset.css", "style.css", "theme.css" ],
57
- "scripts": [ "libs/jquery.js", "utils.js", "script.js" ]
58
- }</code></pre>
59
-
60
- <h2>📤 Production</h2>
61
- <p>The final output will be generated in the <code>dist/</code> folder, ready for deployment.</p>
62
-
63
- <h2>❓ Available Commands</h2>
64
- <ul>
65
- <li><code>init</code> / <code>i</code> – Initialize a new project</li>
66
- <li><code>build</code> / <code>b</code> – Build and minify the project</li>
67
- <li><code>help</code> / <code>h</code> – Show the help message</li>
68
- <li><code>template</code> / <code>t</code> – Manage templates (with <code>--list</code> and <code>--delete</code> options)</li>
69
- </ul>
70
-
71
- <p>You're ready to build fast and clean static websites. Happy coding! 🛠️</p>
72
- </main>
73
-
74
- <footer>
75
- <p>&copy; 2025 Minimaz. All rights reserved.</p>
76
- </footer>
77
-
78
- <script src="script.js"></script>
79
- </body>
80
- </html>
@@ -1,46 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
-
4
- <head>
5
- <meta charset="UTF-8" />
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- <title>Welcome to Minimaz</title>
8
- <link rel="stylesheet" href="../style.css" />
9
- <link rel="shortcut icon" href="../../public/favicon.ico" type="image/x-icon">
10
-
11
- </head>
12
-
13
- <body>
14
- <header>
15
- <nav>
16
- <a class='btn' href="../index.html">Home</a>
17
- <a class='btn active' href="pages/about.html">About</a>
18
- <a class='btn' target="_blank" href="https://github.com/zeller-dev/minimaz">GitHub</a>
19
- </nav>
20
- </header>
21
- <div class="container">
22
- <h1>About Minimaz</h1>
23
- <p><strong>Minimaz</strong> is a minimal build tool that helps you quickly scaffold and minify HTML, CSS, and
24
- JavaScript projects.</p>
25
-
26
- <h2>Features</h2>
27
- <ul>
28
- <li>Zero-config HTML/CSS/JS minification</li>
29
- <li>Folder structure based on <code>src</code>, <code>public</code>, and <code>dist</code></li>
30
- <li>Simple CLI with <code>init</code>, <code>build</code>, and <code>help</code> commands</li>
31
- </ul>
32
-
33
- <h2>Getting Started</h2>
34
- <pre><code>$ minimaz init my-project
35
- $ cd my-project
36
- $ minimaz build</code></pre>
37
-
38
- <p>Learn more or contribute on <a href="#">GitHub</a>.</p>
39
- </div>
40
- <footer>
41
- <p>&copy; 2023 Minimaz. All rights reserved.</p>
42
- </footer>
43
- <script src="../script.js"></script>
44
- </body>
45
-
46
- </html>
@@ -1 +0,0 @@
1
- console.log('Minimaz is ready!')
@@ -1,99 +0,0 @@
1
- :root {
2
- --primary-color: #338ca8;
3
- --background-color: #222;
4
- --foreground-color: #f5f5f5;
5
- --code-bg: #333;
6
- --code-text: #eeeeff;
7
- font-size: 16px;
8
- }
9
-
10
- * {
11
- box-sizing: border-box;
12
- }
13
-
14
- header,
15
- footer {
16
- background-color: var(--code-bg);
17
- padding:1rem;
18
- }
19
-
20
- body {
21
- font-family: 'Segoe UI', sans-serif;
22
- margin: 0;
23
- background: var(--background-color);
24
- color: var(--foreground-color);
25
- line-height: 1.6;
26
- }
27
-
28
- .container {
29
- max-width: 800px;
30
- margin: auto;
31
- padding: 1rem;
32
- }
33
-
34
- h1,
35
- h2,
36
- h3 {
37
- color: var(--primary-color);
38
- margin-top: 2rem;
39
- }
40
-
41
- p {
42
- margin: 1rem 0;
43
- }
44
-
45
- code {
46
- background: var(--code-bg);
47
- color: var(--code-text);
48
- padding: 0.2rem 0.4rem;
49
- border-radius: 4px;
50
- font-family: monospace;
51
- }
52
-
53
- pre {
54
- background: var(--code-bg);
55
- color: #ddd;
56
- padding: 1rem;
57
- overflow-x: auto;
58
- border-radius: 6px;
59
- font-family: monospace;
60
- border-left: 4px solid var(--primary-color);
61
- }
62
-
63
- a {
64
- color: var(--primary-color);
65
- text-decoration: none;
66
- }
67
-
68
- .btn {
69
- transition: all 0.15s ease-in-out;
70
- background-color: var(--primary-color);
71
- border: none;
72
- color: #fff;
73
- padding: 10px 20px;
74
- font-size: 1rem;
75
- margin: .5rem .2rem;
76
- cursor: pointer;
77
- border-radius: 5px;
78
- display: inline-block;
79
- text-align: center;
80
- }
81
-
82
- .btn:hover {
83
- opacity: 0.85;
84
- }
85
-
86
- .btn.active {
87
- background-color: var(--code-bg);
88
- color: #fff;
89
- pointer-events: none;
90
- }
91
-
92
- header {
93
- font-weight: 600;
94
- }
95
-
96
- footer p {
97
- text-align: center;
98
- margin: 0;
99
- }
@@ -1,2 +0,0 @@
1
- dist
2
- .vscode