aztomiq 1.0.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.
Files changed (38) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +146 -0
  3. package/bin/aztomiq.js +336 -0
  4. package/bin/create-aztomiq.js +77 -0
  5. package/package.json +58 -0
  6. package/scripts/analyze-screenshots.js +217 -0
  7. package/scripts/build.js +39 -0
  8. package/scripts/builds/admin.js +17 -0
  9. package/scripts/builds/assets.js +167 -0
  10. package/scripts/builds/cache.js +48 -0
  11. package/scripts/builds/config.js +31 -0
  12. package/scripts/builds/data.js +210 -0
  13. package/scripts/builds/pages.js +288 -0
  14. package/scripts/builds/playground-examples.js +50 -0
  15. package/scripts/builds/templates.js +118 -0
  16. package/scripts/builds/utils.js +37 -0
  17. package/scripts/create-bug-tracker.js +277 -0
  18. package/scripts/deploy.js +135 -0
  19. package/scripts/feedback-generator.js +102 -0
  20. package/scripts/ui-test.js +624 -0
  21. package/scripts/utils/extract-examples.js +44 -0
  22. package/scripts/utils/migrate-icons.js +67 -0
  23. package/src/includes/breadcrumbs.ejs +100 -0
  24. package/src/includes/cloud-tags.ejs +120 -0
  25. package/src/includes/footer.ejs +37 -0
  26. package/src/includes/generator.ejs +226 -0
  27. package/src/includes/head.ejs +73 -0
  28. package/src/includes/header-data-only.ejs +43 -0
  29. package/src/includes/header.ejs +71 -0
  30. package/src/includes/layout.ejs +68 -0
  31. package/src/includes/legacy-banner.ejs +19 -0
  32. package/src/includes/mega-menu.ejs +80 -0
  33. package/src/includes/schema.ejs +20 -0
  34. package/src/templates/manifest.json.ejs +30 -0
  35. package/src/templates/readme-dist.md.ejs +58 -0
  36. package/src/templates/robots.txt.ejs +4 -0
  37. package/src/templates/sitemap.xml.ejs +69 -0
  38. package/src/templates/sw.js.ejs +78 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 ph4n4n
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,146 @@
1
+ # ⚛️ AZtomiq
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
+ [![Node.js Version](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen)](https://nodejs.org/)
5
+ [![Style](https://img.shields.io/badge/style-Glassmorphism-blue)](https://aztomiq.vercel.app/)
6
+
7
+ A high-performance, **privacy-first**, and ultra-modular multi-tool website framework. Built with a passion for simplicity and speed.
8
+
9
+ [Live Demo](https://aztomiq.vercel.app/) • [Report Bug](https://github.com/ph4n4n/aztomiq/issues) • [Request Feature](https://github.com/ph4n4n/aztomiq/issues)
10
+
11
+ ### In production
12
+
13
+ AZtomiq powers:
14
+
15
+ - https://ztools.site — a full-scale utility tools platform
16
+
17
+ ---
18
+
19
+ ## 🌟 Why AZtomiq?
20
+
21
+ AZtomiq isn't just another static site generator. It's an **Ecosystem** designed for building professional utility toolkits.
22
+
23
+ - **⚛️ Atomic Architecture**: Every feature is a self-contained "Atom". Zero global dependencies, maximum portability.
24
+ - **🛡️ Privacy by Design**: 100% Client-side processing. No data ever leaves the user's browser.
25
+ - **🌍 Production Ready i18n**: Built-in multi-lingual support with atomic translation merging.
26
+ - **⚡ Blazing Fast**: No heavy JS frameworks. Powered by Vanilla JS and optimized EJS templates.
27
+ - **📱 Modern PWA**: Fully offline-capable with automated Service Worker generation.
28
+
29
+ ---
30
+
31
+ ## 🏗️ Core Architecture
32
+
33
+ ```text
34
+ .
35
+ ├── bin/ # 🛠️ Main CLI entry point
36
+ ├── docs/ # 📚 Documentation & Guides
37
+ ├── scripts/ # ⚙️ Modular build logic (Pages, Assets, Cache)
38
+ ├── src/
39
+ │ ├── assets/ # 🎨 Global Design System (CSS/JS)
40
+ │ ├── data/ # 📊 Global site & category metadata
41
+ │ ├── features/ # ⚛️ Atomic Tools (The heart of AZtomiq)
42
+ │ ├── includes/ # 🧩 Reusable EJS components
43
+ │ ├── locales/ # 🌍 System-wide translations
44
+ │ ├── pages/ # 📄 Static landing & system pages
45
+ │ └── templates/ # 🧬 SEO & PWA generators
46
+ └── package.json
47
+ ```
48
+
49
+ ---
50
+
51
+ ## 🚀 Quick Start
52
+
53
+ ### 0. Prerequisites
54
+
55
+ - **Node.js**: v18.0.0 or higher
56
+ - **NPM**: v8.0.0 or higher
57
+ - **Git**: For deployment
58
+
59
+ ### 1. Installation
60
+
61
+ **Option A: Scaffolding a new project (Recommended)**
62
+
63
+ ```bash
64
+ npx create-aztomiq my-awesome-app
65
+ cd my-awesome-app
66
+ npm install
67
+ ```
68
+
69
+ **Option B: Manual Installation**
70
+
71
+ ```bash
72
+ git clone https://github.com/ph4n4n/aztomiq.git
73
+ cd aztomiq
74
+ npm install
75
+ ```
76
+
77
+ ### 2. Development
78
+
79
+ Start the watcher and local server:
80
+
81
+ ```bash
82
+ npm run dev
83
+ ```
84
+
85
+ ### 3. Creating Your First Tool
86
+
87
+ Use our CLI to scaffold a new feature instantly:
88
+
89
+ ```bash
90
+ npm run aztomiq tool:create my-awesome-tool
91
+ ```
92
+
93
+ ### 4. Build for Production
94
+
95
+ Generate the static site in the `dist/` folder:
96
+
97
+ ```bash
98
+ npm run build
99
+ ```
100
+
101
+ ### 5. Deployment
102
+
103
+ Ship it to GitHub Pages (configured in `global.yaml`):
104
+
105
+ ```bash
106
+ npm run deploy
107
+ ```
108
+
109
+ ---
110
+
111
+ ## 📚 Documentation
112
+
113
+ Dive deeper into how AZtomiq works:
114
+
115
+ - [📂 **Project Structure**](./docs/STRUCTURE.md) - Understand the folder organization.
116
+ - [⚛️ **Atomic Features**](./docs/FEATURES.md) - Deep dive into tool architecture.
117
+ - [🛠️ **CLI Reference**](./docs/CLI.md) - Master the power of the `aztomiq` command.
118
+ - [⚙️ **Development Guide**](./docs/DEVELOPMENT.md) - Rules and best practices for creating tools.
119
+ - [📈 **Deployment Guide**](./docs/DEPLOYMENT.md) - How to ship to production.
120
+ - [📜 **Project Changelog**](./docs/CHANGELOG.md) - Track the evolution of the framework.
121
+
122
+ ---
123
+
124
+ ## 🤝 Contribution
125
+
126
+ We love contributors! If you have a cool tool idea:
127
+
128
+ 1. Fork the Project.
129
+ 2. Create your Feature Branch (`git checkout -b feature/AmazingTool`).
130
+ 3. Create your tool using `npm run aztomiq tool:create`.
131
+ 4. Commit your Changes.
132
+ 5. Push to the Branch.
133
+ 6. Open a Pull Request.
134
+
135
+ ---
136
+
137
+ ## 📜 License
138
+
139
+ Distributed under the MIT License. See `LICENSE` for more information.
140
+
141
+ ---
142
+
143
+ <p align="center">
144
+ Built with ❤️ by the AZtomiq Team<br>
145
+ <i>"Fast. Simple. Atomic."</i>
146
+ </p>
package/bin/aztomiq.js ADDED
@@ -0,0 +1,336 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs-extra');
4
+ const path = require('path');
5
+ const { spawn } = require('child_process');
6
+ const yaml = require('js-yaml');
7
+
8
+ const args = process.argv.slice(2);
9
+ const command = args[0];
10
+
11
+ const PROJECT_ROOT = process.cwd();
12
+ const CORE_ROOT = path.join(__dirname, '..');
13
+
14
+ const commands = {
15
+ build: async () => {
16
+ const force = args.includes('--force');
17
+ const obfuscate = args.includes('--obfuscate');
18
+ const cmdArgs = ['scripts/build.js'];
19
+ if (force) cmdArgs.push('--force');
20
+ if (obfuscate) cmdArgs.push('--obfuscate');
21
+ runNode(cmdArgs);
22
+ },
23
+ dev: async () => {
24
+ runNode(['scripts/build.js', '--dev'], () => {
25
+ runNode(['server.js']);
26
+ });
27
+ },
28
+ deploy: async () => {
29
+ runNode(['scripts/deploy.js']);
30
+ },
31
+ test: async () => {
32
+ const type = args[1] || 'all';
33
+ if (type === 'ui') {
34
+ runNode(['scripts/ui-test.js']);
35
+ } else {
36
+ runNpm(['run', 'test']);
37
+ }
38
+ },
39
+ status: async () => {
40
+ console.log('\n🔍 Scanning AZtomiq Ecosystem...');
41
+ const featuresDir = path.join(PROJECT_ROOT, 'src/features');
42
+ const testDir = path.join(PROJECT_ROOT, 'tests/features');
43
+ const dirs = await fs.readdir(featuresDir);
44
+
45
+ console.log('-----------------------------------------------------------------------------------');
46
+ console.log(`${'Feature ID'.padEnd(25)} | ${'Status'.padEnd(10)} | ${'Locales'.padEnd(10)} | ${'Tests'.padEnd(8)} | ${'Mode'}`);
47
+ console.log('-----------------------------------------------------------------------------------');
48
+
49
+ for (const id of dirs) {
50
+ const featPath = path.join(featuresDir, id);
51
+ if (!(await fs.stat(featPath)).isDirectory()) continue;
52
+
53
+ const toolYaml = path.join(featPath, 'tool.yaml');
54
+ if (!await fs.pathExists(toolYaml)) continue;
55
+
56
+ const config = yaml.load(await fs.readFile(toolYaml, 'utf8'));
57
+ const vi = await fs.pathExists(path.join(featPath, 'locales/vi.yaml'));
58
+ const en = await fs.pathExists(path.join(featPath, 'locales/en.yaml'));
59
+ const hasTest = (await fs.pathExists(testDir)) && (await fs.readdir(testDir)).some(f => f.startsWith(id));
60
+
61
+ const status = config.status === 'active' ? '✅ ACTIVE' : '🚧 DRAFT';
62
+ const locales = (vi ? 'VI ' : ' ') + (en ? 'EN' : '');
63
+ const test = hasTest ? '✓ YES' : '✗ NO';
64
+ const mode = (config.mode || 'std').toUpperCase();
65
+
66
+ console.log(`${id.padEnd(25)} | ${status.padEnd(10)} | ${locales.padEnd(10)} | ${test.padEnd(8)} | ${mode}`);
67
+ }
68
+ console.log('-----------------------------------------------------------------------------------\n');
69
+ },
70
+ analyze: async () => {
71
+ console.log('\n📊 Analyzing Tool Payload...');
72
+ let distFolder = 'dist';
73
+ let distAssets = path.join(PROJECT_ROOT, 'dist/assets/features');
74
+
75
+ if (!await fs.pathExists(distAssets)) {
76
+ distFolder = 'dist-dev';
77
+ distAssets = path.join(PROJECT_ROOT, 'dist-dev/assets/features');
78
+ }
79
+
80
+ if (!await fs.pathExists(distAssets)) {
81
+ console.error('❌ Build folder (dist/ or dist-dev/) not found. Please run "aztomiq build" first.');
82
+ return;
83
+ }
84
+
85
+ console.log(`📂 Target: ${distFolder}/`);
86
+ const tools = await fs.readdir(distAssets);
87
+ let totalSize = 0;
88
+
89
+ console.log('------------------------------------------------------------------');
90
+ console.log(`${'Tool ID'.padEnd(25)} | ${'CSS Size'.padEnd(12)} | ${'JS Size'.padEnd(12)} | ${'Total'}`);
91
+ console.log('------------------------------------------------------------------');
92
+
93
+ for (const tool of tools) {
94
+ const toolDir = path.join(distAssets, tool);
95
+ if (!(await fs.stat(toolDir)).isDirectory()) continue;
96
+
97
+ const cssFile = path.join(toolDir, 'style.css');
98
+ const jsFile = path.join(toolDir, 'script.js');
99
+
100
+ const cssSize = await fs.pathExists(cssFile) ? (await fs.stat(cssFile)).size : 0;
101
+ const jsSize = await fs.pathExists(jsFile) ? (await fs.stat(jsFile)).size : 0;
102
+ const sum = cssSize + jsSize;
103
+ totalSize += sum;
104
+
105
+ console.log(`${tool.padEnd(25)} | ${formatBytes(cssSize).padEnd(12)} | ${formatBytes(jsSize).padEnd(12)} | ${formatBytes(sum)}`);
106
+ }
107
+ console.log('------------------------------------------------------------------');
108
+ console.log(`${'TOTAL ECOSYSTEM SIZE'.padEnd(55)} | ${formatBytes(totalSize)}`);
109
+ console.log('------------------------------------------------------------------\n');
110
+ },
111
+ cleanup: async () => {
112
+ console.log('🧹 Cleaning up workspace...');
113
+
114
+ // 1. Remove dist folders
115
+ await fs.remove(path.join(PROJECT_ROOT, 'dist'));
116
+ await fs.remove(path.join(PROJECT_ROOT, 'dist-dev'));
117
+ console.log('✅ Removed dist/ and dist-dev/');
118
+
119
+ // 2. Identify and remove draft tools if requested
120
+ if (args.includes('--drafts')) {
121
+ const featuresDir = path.join(PROJECT_ROOT, 'src/features');
122
+ const dirs = await fs.readdir(featuresDir);
123
+ for (const id of dirs) {
124
+ const toolYamlPath = path.join(featuresDir, id, 'tool.yaml');
125
+ if (await fs.pathExists(toolYamlPath)) {
126
+ const config = yaml.load(await fs.readFile(toolYamlPath, 'utf8'));
127
+ if (config.status === 'draft') {
128
+ await fs.remove(path.join(featuresDir, id));
129
+ console.log(`🗑️ Deleted draft tool: ${id}`);
130
+ }
131
+ }
132
+ }
133
+ }
134
+ console.log('✨ Workspace is clean.\n');
135
+ },
136
+ version: async () => {
137
+ const type = args[1] || 'patch'; // patch, minor, major
138
+ const targetId = args[2]; // specific tool id or 'all'
139
+
140
+ const bump = (v, t) => {
141
+ let [ma, mi, pa] = v.split('.').map(Number);
142
+ if (t === 'major') ma++;
143
+ else if (t === 'minor') mi++;
144
+ else pa++;
145
+ if (t === 'major') mi = 0;
146
+ if (t === 'major' || t === 'minor') pa = 0;
147
+ return `${ma}.${mi}.${pa}`;
148
+ };
149
+
150
+ const featuresDir = path.join(PROJECT_ROOT, 'src/features');
151
+ const tools = await fs.readdir(featuresDir);
152
+
153
+ for (const id of tools) {
154
+ if (targetId && targetId !== 'all' && targetId !== id) continue;
155
+
156
+ const yamlPath = path.join(featuresDir, id, 'tool.yaml');
157
+ if (!await fs.pathExists(yamlPath)) continue;
158
+
159
+ const raw = await fs.readFile(yamlPath, 'utf8');
160
+ const config = yaml.load(raw);
161
+
162
+ if (!config.meta) config.meta = { version: '1.0.0' };
163
+ const oldV = config.meta.version || '1.0.0';
164
+ const newV = bump(oldV, type);
165
+
166
+ config.meta.version = newV;
167
+ await fs.writeFile(yamlPath, yaml.dump(config));
168
+ console.log(`🚀 ${id.padEnd(25)} : ${oldV} -> ${newV}`);
169
+ }
170
+ },
171
+ 'tool:create': async () => {
172
+ const name = args[1];
173
+ if (!name) {
174
+ console.error('❌ Please specify a tool name. Example: aztomiq tool:create my-new-tool');
175
+ return;
176
+ }
177
+
178
+ const toolDir = path.join(PROJECT_ROOT, 'src/features', name);
179
+ if (await fs.pathExists(toolDir)) {
180
+ console.error(`❌ Tool "${name}" already exists at ${toolDir}`);
181
+ return;
182
+ }
183
+
184
+ console.log(`🚀 Creating new tool: ${name}...`);
185
+
186
+ await fs.ensureDir(toolDir);
187
+ await fs.ensureDir(path.join(toolDir, 'locales'));
188
+
189
+ const toolYaml = `id: ${name}
190
+ category: utilities
191
+ icon: box
192
+ title_key: ${name}.title
193
+ desc_key: ${name}.desc
194
+ status: active
195
+ link: /${name}/
196
+ mode: standard
197
+ meta:
198
+ version: 1.0.0
199
+ `;
200
+ await fs.writeFile(path.join(toolDir, 'tool.yaml'), toolYaml);
201
+
202
+ const indexEjs = `<!-- title: <%= t('${name}.title') %> - AZtomiq -->
203
+ <section class="tool-page-container">
204
+ <div class="tool-header">
205
+ <h1><i data-lucide="box"></i> <%= t('${name}.title') %></h1>
206
+ <p><%= t('${name}.desc') %></p>
207
+ <div class="tool-meta">
208
+ <span class="version-badge" id="open-changelog">v<%= toolConfig.meta.version %></span>
209
+ </div>
210
+ </div>
211
+
212
+ <div class="card tool-card">
213
+ <div class="input-group">
214
+ <label>Sample Input</label>
215
+ <input type="text" id="sample-input" placeholder="Type something...">
216
+ </div>
217
+ <button id="action-btn" class="btn-primary">Execute</button>
218
+ <div id="result-area" class="result-box muted">
219
+ Result will appear here...
220
+ </div>
221
+ </div>
222
+ </section>
223
+
224
+ <script src="<%= toolConfig._assets.js %>"></script>
225
+ <link rel="stylesheet" href="<%= toolConfig._assets.css %>">
226
+ `;
227
+ await fs.writeFile(path.join(toolDir, 'index.ejs'), indexEjs);
228
+
229
+ const styleCss = `.tool-card {
230
+ max-width: 600px;
231
+ margin: 2rem auto;
232
+ padding: 2rem;
233
+ }
234
+ .result-box {
235
+ margin-top: 1.5rem;
236
+ padding: 1rem;
237
+ background: var(--bg-hover);
238
+ border-radius: 8px;
239
+ border: 1px solid var(--border-color);
240
+ min-height: 50px;
241
+ }
242
+ `;
243
+ await fs.writeFile(path.join(toolDir, 'style.css'), styleCss);
244
+
245
+ const scriptJs = `document.addEventListener('DOMContentLoaded', () => {
246
+ const input = document.getElementById('sample-input');
247
+ const btn = document.getElementById('action-btn');
248
+ const result = document.getElementById('result-area');
249
+
250
+ btn.addEventListener('click', () => {
251
+ const val = input.value || 'No input';
252
+ result.textContent = 'You entered: ' + val;
253
+ result.classList.remove('muted');
254
+ console.log('[${name}] Action executed');
255
+ });
256
+ });
257
+ `;
258
+ await fs.writeFile(path.join(toolDir, 'script.js'), scriptJs);
259
+
260
+ const enYaml = `${name}:
261
+ title: "${name.charAt(0).toUpperCase() + name.slice(1).replace(/-/g, ' ')}"
262
+ desc: "Professional utility tool for ${name}."
263
+ `;
264
+ const viYaml = `${name}:
265
+ title: "${name.charAt(0).toUpperCase() + name.slice(1).replace(/-/g, ' ')}"
266
+ desc: "Công cụ tiện ích chuyên nghiệp cho ${name}."
267
+ `;
268
+ await fs.writeFile(path.join(toolDir, 'locales/en.yaml'), enYaml);
269
+ await fs.writeFile(path.join(toolDir, 'locales/vi.yaml'), viYaml);
270
+
271
+ console.log(`✅ Tool "${name}" created successfully!`);
272
+ console.log(`👉 Edit it at: src/features/${name}`);
273
+ }
274
+ };
275
+
276
+ if (!command || !commands[command]) {
277
+ console.log(`
278
+ 🛠️ AZtomiq CLI v${require('../package.json').version}
279
+
280
+ Usage:
281
+ aztomiq <command> [options]
282
+
283
+ Commands:
284
+ aztomiq dev Start development server (Watcher + Node)
285
+ aztomiq build Build for production (--force, --obfuscate)
286
+ aztomiq deploy Deploy to public distribution repository
287
+ aztomiq status Scan ecosystem health (Locales, Tests, Config)
288
+ aztomiq analyze Analyze tool payloads and ecosystem size
289
+ aztomiq cleanup Remove build artifacts (--drafts to delete draft tools)
290
+ aztomiq version <type> [id] Bump versions of tools (patch, minor, major)
291
+ aztomiq test Run integrity and logic tests
292
+ aztomiq test ui Run Puppeteer UI automation tests
293
+ aztomiq tool:create <id> Scaffold a new atomic tool
294
+ `);
295
+ process.exit(0);
296
+ }
297
+
298
+ commands[command]().catch(err => {
299
+ console.error('❌ Error executing command:', err);
300
+ process.exit(1);
301
+ });
302
+
303
+ function formatBytes(bytes, decimals = 2) {
304
+ if (bytes === 0) return '0 Bytes';
305
+ const k = 1024;
306
+ const dm = decimals < 0 ? 0 : decimals;
307
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
308
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
309
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
310
+ }
311
+
312
+ function runNode(args, cb) {
313
+ // Smart resolve: Priority 1: Current Project, Priority 2: Framework Core
314
+ let scriptPath = args[0];
315
+ const localScript = path.join(PROJECT_ROOT, scriptPath);
316
+ const coreScript = path.join(CORE_ROOT, scriptPath);
317
+
318
+ if (fs.existsSync(localScript)) {
319
+ args[0] = localScript;
320
+ } else if (fs.existsSync(coreScript)) {
321
+ args[0] = coreScript;
322
+ console.log(`[aztomiq] Running core script: ${scriptPath}`);
323
+ }
324
+
325
+ const proc = spawn('node', args, { stdio: 'inherit', cwd: PROJECT_ROOT });
326
+ proc.on('close', (code) => {
327
+ if (code === 0 && cb) cb();
328
+ });
329
+ }
330
+
331
+ function runNpm(args, cb) {
332
+ const proc = spawn('npm', args, { stdio: 'inherit', cwd: PROJECT_ROOT, shell: true });
333
+ proc.on('close', (code) => {
334
+ if (code === 0 && cb) cb();
335
+ });
336
+ }
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs-extra');
4
+ const path = require('path');
5
+ const { execSync } = require('child_process');
6
+
7
+ const projectName = process.argv[2] || 'my-aztomiq-site';
8
+ const projectPath = path.resolve(process.cwd(), projectName);
9
+
10
+ if (fs.existsSync(projectPath)) {
11
+ console.error(`❌ Directory "${projectName}" already exists.`);
12
+ process.exit(1);
13
+ }
14
+
15
+ console.log(`🚀 Creating a new AZtomiq project: ${projectName}...`);
16
+
17
+ // 1. Create project folder
18
+ fs.ensureDirSync(projectPath);
19
+
20
+ // 2. Define Template Structure (Minimal)
21
+ const templateDirs = [
22
+ 'src/assets/css',
23
+ 'src/assets/js',
24
+ 'src/assets/images',
25
+ 'src/data',
26
+ 'src/features/hello-world/locales',
27
+ 'src/includes',
28
+ 'src/locales/en',
29
+ 'src/locales/vi',
30
+ 'src/pages',
31
+ 'src/templates'
32
+ ];
33
+
34
+ templateDirs.forEach(dir => fs.ensureDirSync(path.join(projectPath, dir)));
35
+
36
+ // 3. Create Basic Files
37
+ const packageJson = {
38
+ name: projectName,
39
+ version: '1.0.0',
40
+ scripts: {
41
+ "dev": "aztomiq dev",
42
+ "build": "aztomiq build",
43
+ "status": "aztomiq status"
44
+ },
45
+ dependencies: {
46
+ "@aztomiq/core": "latest"
47
+ }
48
+ };
49
+ fs.writeJsonSync(path.join(projectPath, 'package.json'), packageJson, { spaces: 2 });
50
+
51
+ const globalYaml = `site:
52
+ title: "${projectName}"
53
+ description: "Built with AZtomiq"
54
+ build:
55
+ locales: ["en", "vi"]
56
+ default_locale: "en"
57
+ `;
58
+ fs.writeFileSync(path.join(projectPath, 'src/data/global.yaml'), globalYaml);
59
+
60
+ // 4. Create a Sample Tool
61
+ const toolYaml = `id: hello-world
62
+ category: general
63
+ icon: smile
64
+ status: active
65
+ `;
66
+ fs.writeFileSync(path.join(projectPath, 'src/features/hello-world/tool.yaml'), toolYaml);
67
+
68
+ const toolEjs = `<h1>Hello World</h1>
69
+ <p>Welcome to your new AZtomiq site!</p>
70
+ `;
71
+ fs.writeFileSync(path.join(projectPath, 'src/features/hello-world/index.ejs'), toolEjs);
72
+
73
+ console.log(`\n✅ Project "${projectName}" initialized successfully!`);
74
+ console.log(`👉 Next steps:`);
75
+ console.log(` cd ${projectName}`);
76
+ console.log(` npm install`);
77
+ console.log(` npm run dev`);
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "aztomiq",
3
+ "version": "1.0.0",
4
+ "description": "AZtomiq - A comprehensive A-Z multi-tool framework with atomic architecture",
5
+ "main": "scripts/build.js",
6
+ "bin": {
7
+ "aztomiq": "bin/aztomiq.js",
8
+ "create-aztomiq": "bin/create-aztomiq.js"
9
+ },
10
+ "files": [
11
+ "bin",
12
+ "scripts",
13
+ "src/includes",
14
+ "src/templates",
15
+ "package.json",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
19
+ "scripts": {
20
+ "aztomiq": "node bin/aztomiq.js",
21
+ "test": "node bin/aztomiq.js test",
22
+ "build:dev": "node bin/aztomiq.js build",
23
+ "build:force": "node bin/aztomiq.js build --force",
24
+ "build": "node bin/aztomiq.js build",
25
+ "dev": "node bin/aztomiq.js dev",
26
+ "deploy": "node bin/aztomiq.js deploy"
27
+ },
28
+ "keywords": [
29
+ "framework",
30
+ "tools",
31
+ "utility",
32
+ "calculator",
33
+ "ejs",
34
+ "pwa"
35
+ ],
36
+ "author": "ph4n4n",
37
+ "license": "MIT",
38
+ "dependencies": {
39
+ "body-parser": "^2.2.1",
40
+ "chokidar": "^5.0.0",
41
+ "cors": "^2.8.5",
42
+ "ejs": "^3.1.9",
43
+ "express": "^5.2.1",
44
+ "fs-extra": "^11.2.0",
45
+ "js-yaml": "^4.1.1",
46
+ "marked": "^17.0.1"
47
+ },
48
+ "devDependencies": {
49
+ "clean-css-cli": "^5.0.0",
50
+ "html-minifier-terser": "^7.0.0",
51
+ "javascript-obfuscator": "^4.0.0",
52
+ "jsdom": "^27.3.0",
53
+ "puppeteer": "^24.33.1",
54
+ "terser": "^5.0.0",
55
+ "vitest": "^4.0.16",
56
+ "xml2js": "^0.6.2"
57
+ }
58
+ }