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.
- package/LICENSE +21 -0
- package/README.md +146 -0
- package/bin/aztomiq.js +336 -0
- package/bin/create-aztomiq.js +77 -0
- package/package.json +58 -0
- package/scripts/analyze-screenshots.js +217 -0
- package/scripts/build.js +39 -0
- package/scripts/builds/admin.js +17 -0
- package/scripts/builds/assets.js +167 -0
- package/scripts/builds/cache.js +48 -0
- package/scripts/builds/config.js +31 -0
- package/scripts/builds/data.js +210 -0
- package/scripts/builds/pages.js +288 -0
- package/scripts/builds/playground-examples.js +50 -0
- package/scripts/builds/templates.js +118 -0
- package/scripts/builds/utils.js +37 -0
- package/scripts/create-bug-tracker.js +277 -0
- package/scripts/deploy.js +135 -0
- package/scripts/feedback-generator.js +102 -0
- package/scripts/ui-test.js +624 -0
- package/scripts/utils/extract-examples.js +44 -0
- package/scripts/utils/migrate-icons.js +67 -0
- package/src/includes/breadcrumbs.ejs +100 -0
- package/src/includes/cloud-tags.ejs +120 -0
- package/src/includes/footer.ejs +37 -0
- package/src/includes/generator.ejs +226 -0
- package/src/includes/head.ejs +73 -0
- package/src/includes/header-data-only.ejs +43 -0
- package/src/includes/header.ejs +71 -0
- package/src/includes/layout.ejs +68 -0
- package/src/includes/legacy-banner.ejs +19 -0
- package/src/includes/mega-menu.ejs +80 -0
- package/src/includes/schema.ejs +20 -0
- package/src/templates/manifest.json.ejs +30 -0
- package/src/templates/readme-dist.md.ejs +58 -0
- package/src/templates/robots.txt.ejs +4 -0
- package/src/templates/sitemap.xml.ejs +69 -0
- 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
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
|
+
[](https://nodejs.org/)
|
|
5
|
+
[](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
|
+
}
|