minimaz-cli 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/LICENSE +6 -6
- package/README.md +106 -106
- package/{dist/bin → bin}/cli.js +1 -1
- package/package.json +42 -57
- package/src/commands/build.js +1 -0
- package/src/commands/help.js +2 -0
- package/src/commands/init.js +1 -0
- package/src/commands/template.js +1 -0
- package/src/templates/default/minimaz.config.json +25 -25
- package/src/templates/default/src/index.html +80 -80
- package/src/templates/default/src/pages/about.html +45 -45
- package/src/templates/default/src/script.js +1 -1
- package/src/templates/default/src/style.css +98 -98
- package/src/templates/gitignore +1 -1
- package/src/templates/simple/minimaz.config.json +25 -25
- package/src/templates/simple/src/index.html +15 -15
- package/src/templates/simple/src/pages/page.html +10 -10
- package/src/templates/simple/src/script.js +1 -1
- package/src/utils/functions.js +1 -0
- package/src/utils/loadConfig.js +1 -0
- package/src/utils/logService.js +1 -0
- package/src/utils/postInstall.js +1 -0
- package/bin/cli.ts +0 -80
- package/dist/LICENSE +0 -7
- package/dist/README.md +0 -103
- package/dist/package.json +0 -42
- package/src/commands/build.ts +0 -176
- package/src/commands/help.ts +0 -33
- package/src/commands/init.ts +0 -56
- package/src/commands/template.ts +0 -146
- package/src/utils/functions.ts +0 -161
- package/src/utils/loadConfig.ts +0 -61
- package/src/utils/logService.ts +0 -19
- package/src/utils/postInstall.ts +0 -16
package/src/commands/build.ts
DELETED
|
@@ -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
|
-
}
|
package/src/commands/help.ts
DELETED
|
@@ -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
|
package/src/commands/init.ts
DELETED
|
@@ -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
|
-
}
|
package/src/commands/template.ts
DELETED
|
@@ -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
|
-
}
|
package/src/utils/functions.ts
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
import readline from 'readline'
|
|
2
|
-
import fs from 'fs-extra'
|
|
3
|
-
import path from 'path'
|
|
4
|
-
import os from 'os'
|
|
5
|
-
import { log } from './logService.js'
|
|
6
|
-
import { execSync } from 'child_process'
|
|
7
|
-
|
|
8
|
-
// ----- Types -----
|
|
9
|
-
interface Args {
|
|
10
|
-
_: string[]
|
|
11
|
-
[key: string]: string | boolean | string[]
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// ----- Parse CLI Arguments -----
|
|
15
|
-
// Converts raw process arguments into structured key-value pairs
|
|
16
|
-
export function parseArgs(rawArgs: string[]): Args {
|
|
17
|
-
const args: Args = { _: [] }
|
|
18
|
-
for (let i = 0; i < rawArgs.length; i++) {
|
|
19
|
-
const arg: string = rawArgs[i]
|
|
20
|
-
if (arg.startsWith('-')) {
|
|
21
|
-
const key: string = arg.startsWith('--') ? arg.slice(2) : arg.slice(1)
|
|
22
|
-
const next: string = rawArgs[i + 1]
|
|
23
|
-
const hasValue: boolean = !!next && !next.startsWith('-')
|
|
24
|
-
args[key] = hasValue ? next : true
|
|
25
|
-
if (hasValue) i++
|
|
26
|
-
} else {
|
|
27
|
-
args._.push(arg)
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return args
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// ----- Ask Question from CLI -----
|
|
34
|
-
// Prompts the user with a question and returns the input
|
|
35
|
-
export function askQuestion(query: string): Promise<string> {
|
|
36
|
-
return new Promise(resolve => {
|
|
37
|
-
const rl: readline.Interface = readline.createInterface({
|
|
38
|
-
input: process.stdin,
|
|
39
|
-
output: process.stdout
|
|
40
|
-
})
|
|
41
|
-
rl.question(query, answer => {
|
|
42
|
-
rl.close()
|
|
43
|
-
resolve(answer.trim())
|
|
44
|
-
})
|
|
45
|
-
})
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// ----- List Templates -----
|
|
49
|
-
// Displays all available global templates in the given directory
|
|
50
|
-
export async function listTemplates(dir: string): Promise<void> {
|
|
51
|
-
if (!await fs.pathExists(dir)) {
|
|
52
|
-
log('info', 'No templates directory found.')
|
|
53
|
-
return
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const templates: string[] = await fs.readdir(dir)
|
|
57
|
-
if (templates.length === 0) log('info', 'No global templates available.')
|
|
58
|
-
else {
|
|
59
|
-
log('info', 'Available global templates:')
|
|
60
|
-
templates.forEach(t => log('info', `- ${t}`))
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// ----- Apply Replacements -----
|
|
65
|
-
// Replaces all occurrences of keys in content with their corresponding values
|
|
66
|
-
export function applyReplacements(content: string, replacements: Record<string, string> = {}): string {
|
|
67
|
-
for (const [from, to] of Object.entries(replacements)) {
|
|
68
|
-
content = content.split(from).join(to)
|
|
69
|
-
}
|
|
70
|
-
return content
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// ----- Read File with Replacements -----
|
|
74
|
-
// Reads a file and applies string replacements if provided
|
|
75
|
-
export async function getFile(srcPath: string, replace?: Record<string, string>): Promise<string> {
|
|
76
|
-
try {
|
|
77
|
-
let file: string = await fs.readFile(srcPath, 'utf-8')
|
|
78
|
-
if (replace) file = applyReplacements(file, replace)
|
|
79
|
-
return file
|
|
80
|
-
} catch (error: any) {
|
|
81
|
-
log('error', `Failed to read file ${srcPath}: ${error.message}`)
|
|
82
|
-
return ''
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// ----- Global Node Modules Path -----
|
|
87
|
-
// Returns the path to global node_modules depending on the platform
|
|
88
|
-
|
|
89
|
-
export function getGlobalNodeModulesPath(): string {
|
|
90
|
-
try {
|
|
91
|
-
const prefix = execSync('npm config get prefix', { encoding: 'utf-8' }).trim();
|
|
92
|
-
if (!prefix) throw new Error('Empty prefix');
|
|
93
|
-
return process.platform === 'win32'
|
|
94
|
-
? path.join(prefix, 'node_modules', 'minimaz-cli')
|
|
95
|
-
: path.join(prefix, 'lib', 'node_modules', 'minimaz-cli');
|
|
96
|
-
} catch {
|
|
97
|
-
// fallback
|
|
98
|
-
return process.platform === 'win32'
|
|
99
|
-
? path.join(process.env.APPDATA || '', 'npm', 'node_modules', 'minimaz-cli')
|
|
100
|
-
: '/usr/local/lib/node_modules/minimaz-cli';
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
// ----- Create Global Templates Folder -----
|
|
106
|
-
// Creates ~/.minimaz/templates and copies default templates if folder is empty
|
|
107
|
-
export async function createGlobalDir(): Promise<void> {
|
|
108
|
-
const minimazDir = path.join(os.homedir(), '.minimaz')
|
|
109
|
-
const globalTemplatesDir = path.join(minimazDir, 'templates')
|
|
110
|
-
const defaultTemplatesDir = path.join(getGlobalNodeModulesPath(), 'src', 'templates')
|
|
111
|
-
const settingsPath = path.join(minimazDir, 'settings.json')
|
|
112
|
-
|
|
113
|
-
console.log(defaultTemplatesDir)
|
|
114
|
-
|
|
115
|
-
// ----- Ensure node_modules path exists (fallback for portable setups) -----
|
|
116
|
-
|
|
117
|
-
try {
|
|
118
|
-
// ----- Ensure minimaz dir exists -----
|
|
119
|
-
await fs.ensureDir(minimazDir)
|
|
120
|
-
|
|
121
|
-
// ----- Create settings.json if missing -----
|
|
122
|
-
if (!await fs.pathExists(settingsPath)) {
|
|
123
|
-
const defaultSettings = {
|
|
124
|
-
createdAt: new Date().toISOString(),
|
|
125
|
-
templatesPath: globalTemplatesDir,
|
|
126
|
-
npmGlobalPath: getGlobalNodeModulesPath()
|
|
127
|
-
}
|
|
128
|
-
await fs.outputJson(settingsPath, defaultSettings, { spaces: 2 })
|
|
129
|
-
log('success', `Created settings.json at ${settingsPath}`)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// ----- Check if templates folder exists -----
|
|
133
|
-
const exists = await fs.pathExists(globalTemplatesDir)
|
|
134
|
-
const isEmpty = exists ? (await fs.readdir(globalTemplatesDir)).length === 0 : true
|
|
135
|
-
|
|
136
|
-
if (!exists) {
|
|
137
|
-
await fs.ensureDir(globalTemplatesDir)
|
|
138
|
-
log('success', 'Created global templates directory.')
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// ----- Skip copy if not empty -----
|
|
142
|
-
if (!isEmpty) {
|
|
143
|
-
log('info', 'Global templates directory not empty. Skipping copy.')
|
|
144
|
-
return
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const templates: string[] = await fs.readdir(defaultTemplatesDir)
|
|
148
|
-
|
|
149
|
-
console.log(templates)
|
|
150
|
-
// ----- Copy default templates -----
|
|
151
|
-
for (const name of await fs.readdir(defaultTemplatesDir)) {
|
|
152
|
-
await fs.copy(path.join(defaultTemplatesDir, name), path.join(globalTemplatesDir, name))
|
|
153
|
-
log('success', `Copied template '${name}'.`)
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
log('success', 'Default templates setup completed.')
|
|
157
|
-
} catch (error: any) {
|
|
158
|
-
log('error', `Failed to create global templates directory: ${error.message}`)
|
|
159
|
-
throw error
|
|
160
|
-
}
|
|
161
|
-
}
|
package/src/utils/loadConfig.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import fs from 'fs-extra'
|
|
2
|
-
import path from 'path'
|
|
3
|
-
import { log } from './logService.js'
|
|
4
|
-
|
|
5
|
-
// ----- Default Config -----
|
|
6
|
-
// Provides default values for project build and minification
|
|
7
|
-
const defaultConfig: any = {
|
|
8
|
-
src: 'src',
|
|
9
|
-
dist: 'dist',
|
|
10
|
-
public: 'public',
|
|
11
|
-
minify: {
|
|
12
|
-
html: true,
|
|
13
|
-
css: true,
|
|
14
|
-
js: true
|
|
15
|
-
},
|
|
16
|
-
replace: {}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// ----- Deep Merge Function -----
|
|
20
|
-
// Recursively merges user config into default config
|
|
21
|
-
function deepMerge(target: any, source: any): any {
|
|
22
|
-
const result: any = { ...target }
|
|
23
|
-
|
|
24
|
-
for (const key in source) {
|
|
25
|
-
if (
|
|
26
|
-
source[key] &&
|
|
27
|
-
typeof source[key] === 'object' &&
|
|
28
|
-
!Array.isArray(source[key])
|
|
29
|
-
) {
|
|
30
|
-
result[key] = deepMerge(target[key] || {}, source[key])
|
|
31
|
-
} else if (source[key] !== undefined) {
|
|
32
|
-
result[key] = source[key]
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return result
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// ----- Load User Config -----
|
|
40
|
-
// Loads minimaz.config.tson if present and merges it with default config
|
|
41
|
-
export async function loadConfig(): Promise<any> {
|
|
42
|
-
const configPath: string = path.resolve(process.cwd(), 'minimaz.config.json')
|
|
43
|
-
|
|
44
|
-
let userConfig: Partial<any> = {}
|
|
45
|
-
if (await fs.pathExists(configPath)) {
|
|
46
|
-
try {
|
|
47
|
-
userConfig = await fs.readJson(configPath)
|
|
48
|
-
log('info', 'Loaded config from minimaz.config.tson')
|
|
49
|
-
} catch (error: any) {
|
|
50
|
-
throw new Error(`Failed to parse minimaz.config.tson: ${error.message}`)
|
|
51
|
-
}
|
|
52
|
-
} else {
|
|
53
|
-
log('info', 'No minimaz.config.tson found. Using default config')
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const config: any = deepMerge(defaultConfig, userConfig)
|
|
57
|
-
|
|
58
|
-
if (!config.src || !config.dist) throw new Error('Invalid configuration: src and dist are required')
|
|
59
|
-
|
|
60
|
-
return config
|
|
61
|
-
}
|
package/src/utils/logService.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// ----- Log Types -----
|
|
2
|
-
type LogType = 'error' | 'warn' | 'success' | 'info' | 'log'
|
|
3
|
-
|
|
4
|
-
// ----- Log Function -----
|
|
5
|
-
// Prints a message to console with icon based on type
|
|
6
|
-
export function log(type: LogType = 'log', message: string): void {
|
|
7
|
-
const icons: Record<LogType, string> = {
|
|
8
|
-
error: '❌',
|
|
9
|
-
warn: '⚠️',
|
|
10
|
-
success: '✅',
|
|
11
|
-
info: 'ℹ️',
|
|
12
|
-
log: '📁' // default icon
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// Use console.error/console.warn only for error and warn
|
|
16
|
-
if (type === 'error') console.error(icons[type], message)
|
|
17
|
-
else if (type === 'warn') console.warn(icons[type], message)
|
|
18
|
-
else console.log(icons[type] || icons.log, message)
|
|
19
|
-
}
|
package/src/utils/postInstall.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { createGlobalDir } from './functions.js'
|
|
2
|
-
import { log } from './logService.js'
|
|
3
|
-
|
|
4
|
-
// ----- Setup Default Templates -----
|
|
5
|
-
// Runs after install to ensure global templates exist
|
|
6
|
-
async function setupDefaultTemplates(): Promise<void> {
|
|
7
|
-
try {
|
|
8
|
-
await createGlobalDir()
|
|
9
|
-
log('success', 'Postinstall: Global templates setup completed.')
|
|
10
|
-
} catch (error: any) {
|
|
11
|
-
log('error', `Postinstall setup failed: ${error.message}`)
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// Execute setup
|
|
16
|
-
setupDefaultTemplates()
|