@africode/core 5.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/AFRICODE_FRAMEWORK_GUIDE.md +707 -0
- package/LICENSE +623 -0
- package/README.md +442 -0
- package/bin/africode.js +73 -0
- package/bin/africode.js.1758507140 +343 -0
- package/bin/cli.ts +83 -0
- package/bin/create-africode.js +158 -0
- package/bin/scaffold.ts +219 -0
- package/components/accordion.js +183 -0
- package/components/alert.js +131 -0
- package/components/auth.js +172 -0
- package/components/avatar.js +117 -0
- package/components/badge.js +104 -0
- package/components/base.d.ts +139 -0
- package/components/base.js +184 -0
- package/components/button.js +164 -0
- package/components/card.js +137 -0
- package/components/cultural-card.js +243 -0
- package/components/divider.js +83 -0
- package/components/dropdown.js +171 -0
- package/components/error-boundary.js +155 -0
- package/components/form.js +131 -0
- package/components/grid.js +273 -0
- package/components/hero.js +138 -0
- package/components/icon.js +36 -0
- package/components/index.js +57 -0
- package/components/input.js +256 -0
- package/components/kanga-card.js +185 -0
- package/components/language-switcher.js +108 -0
- package/components/loader.js +80 -0
- package/components/modal.js +262 -0
- package/components/motion.js +84 -0
- package/components/navbar.js +236 -0
- package/components/pattern-showcase.js +225 -0
- package/components/progress.js +134 -0
- package/components/react.js +111 -0
- package/components/section.js +54 -0
- package/components/select.js +322 -0
- package/components/sidebar.js +180 -0
- package/components/skeleton.js +85 -0
- package/components/table.js +181 -0
- package/components/tabs.js +202 -0
- package/components/theme-toggle.js +82 -0
- package/components/toast.js +139 -0
- package/components/tooltip.js +167 -0
- package/core/a2ui-schema-manager.js +344 -0
- package/core/a2ui.js +431 -0
- package/core/bun-runtime.js +799 -0
- package/core/cli/commands/add.js +23 -0
- package/core/cli/commands/audit.js +58 -0
- package/core/cli/commands/build.js +137 -0
- package/core/cli/commands/create-plugin.js +241 -0
- package/core/cli/commands/dev.js +228 -0
- package/core/cli/commands/lint.js +23 -0
- package/core/cli/commands/test.js +34 -0
- package/core/cli/migrator.js +71 -0
- package/core/cli/ui.js +46 -0
- package/core/compliance.js +628 -0
- package/core/config.js +263 -0
- package/core/db-advanced.js +481 -0
- package/core/db.js +284 -0
- package/core/enhanced-hmr.js +404 -0
- package/core/errors.js +222 -0
- package/core/file-router.js +290 -0
- package/core/heartbeat.js +64 -0
- package/core/hmr-client.js +204 -0
- package/core/hmr.js +196 -0
- package/core/html.d.ts +116 -0
- package/core/html.js +160 -0
- package/core/hydration.js +52 -0
- package/core/lipa-namba-journey.js +572 -0
- package/core/motion.js +106 -0
- package/core/nida-cig-middleware.js +455 -0
- package/core/patterns.d.ts +124 -0
- package/core/patterns.js +833 -0
- package/core/plugins/index.js +312 -0
- package/core/router.js +387 -0
- package/core/sdk-client.js +62 -0
- package/core/sdk.d.ts +133 -0
- package/core/sdk.js +123 -0
- package/core/seo.js +76 -0
- package/core/server/auth-endpoints.js +339 -0
- package/core/server/auth.js +180 -0
- package/core/server/csrf.js +206 -0
- package/core/server/db.js +39 -0
- package/core/server/middleware.js +324 -0
- package/core/server/rate-limit.js +238 -0
- package/core/server/render.js +69 -0
- package/core/server/router.js +120 -0
- package/core/shim.js +28 -0
- package/core/state.d.ts +86 -0
- package/core/state.js +242 -0
- package/core/store.d.ts +122 -0
- package/core/store.js +61 -0
- package/core/validation.d.ts +233 -0
- package/core/validation.js +590 -0
- package/core/websocket.js +639 -0
- package/dist/africode.js +2905 -0
- package/dist/africode.js.map +61 -0
- package/dist/build-info.json +23 -0
- package/dist/components.js +2888 -0
- package/dist/components.js.map +58 -0
- package/dist/styles/africanity.css +322 -0
- package/dist/styles/typography.css +141 -0
- package/docs/IDE-Guide.md +50 -0
- package/package.json +110 -0
- package/src/index.ts +196 -0
- package/styles/africanity.css +322 -0
- package/styles/typography.css +141 -0
- package/templates/starter/.env.example +15 -0
- package/templates/starter/africode.config.js +40 -0
- package/templates/starter/package.json +14 -0
- package/templates/starter/src/pages/index.html +46 -0
- package/templates/starter/src/pages/index.js +32 -0
- package/templates/starter/src/styles/main.css +4 -0
- package/templates/starter-3d/.env.example +7 -0
- package/templates/starter-3d/africode.config.js +29 -0
- package/templates/starter-3d/components/af-model-viewer.js +125 -0
- package/templates/starter-3d/package.json +15 -0
- package/templates/starter-3d/src/pages/index.html +46 -0
- package/templates/starter-3d/src/pages/index.js +50 -0
- package/templates/starter-3d/src/styles/main.css +4 -0
- package/templates/starter-react/.env.example +15 -0
- package/templates/starter-react/africode.config.js +40 -0
- package/templates/starter-react/package.json +16 -0
- package/templates/starter-react/src/pages/index.html +46 -0
- package/templates/starter-react/src/pages/index.js +68 -0
- package/templates/starter-react/src/styles/main.css +4 -0
- package/templates/starter-tailwind/.env.example +15 -0
- package/templates/starter-tailwind/africode.config.js +40 -0
- package/templates/starter-tailwind/package.json +20 -0
- package/templates/starter-tailwind/src/pages/index.html +46 -0
- package/templates/starter-tailwind/src/pages/index.js +37 -0
- package/templates/starter-tailwind/src/styles/main.css +4 -0
- package/templates/starter-tailwind/src/styles/tailwind.css +1 -0
- package/templates/starter-tailwind/src/tailwind-loader.js +30 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* AfriCode CLI
|
|
4
|
+
*
|
|
5
|
+
* Commands:
|
|
6
|
+
* africode dev - Start development server
|
|
7
|
+
* africode build - Production build
|
|
8
|
+
* africode add - Add component to project
|
|
9
|
+
*
|
|
10
|
+
* @module bin/africode
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// --- SSR DOM SHIM (Must be first) ---
|
|
14
|
+
if (typeof globalThis.HTMLElement === 'undefined') {
|
|
15
|
+
globalThis.HTMLElement = class HTMLElement { };
|
|
16
|
+
globalThis.customElements = { define: () => { }, get: () => { } };
|
|
17
|
+
globalThis.document = {
|
|
18
|
+
createElement: () => ({
|
|
19
|
+
classList: { add: () => { } },
|
|
20
|
+
setAttribute: () => { },
|
|
21
|
+
style: {}
|
|
22
|
+
}),
|
|
23
|
+
head: { appendChild: () => { } },
|
|
24
|
+
body: { appendChild: () => { } }
|
|
25
|
+
};
|
|
26
|
+
globalThis.window = globalThis;
|
|
27
|
+
globalThis.CSSStyleSheet = class CSSStyleSheet { replaceSync() { } };
|
|
28
|
+
}
|
|
29
|
+
// ------------------------------------
|
|
30
|
+
|
|
31
|
+
const args = process.argv.slice(2);
|
|
32
|
+
const command = args[0];
|
|
33
|
+
// Allows Web Components to be imported in Node/Bun environment
|
|
34
|
+
if (typeof global !== 'undefined') {
|
|
35
|
+
global.HTMLElement = class HTMLElement { };
|
|
36
|
+
global.customElements = {
|
|
37
|
+
define: () => { },
|
|
38
|
+
get: () => { }
|
|
39
|
+
};
|
|
40
|
+
global.document = {
|
|
41
|
+
createElement: () => ({ classList: { add: () => { } }, setAttribute: () => { } }),
|
|
42
|
+
head: { appendChild: () => { } }
|
|
43
|
+
};
|
|
44
|
+
global.window = global;
|
|
45
|
+
}
|
|
46
|
+
// --------------------
|
|
47
|
+
|
|
48
|
+
const colors = {
|
|
49
|
+
green: '\x1b[32m',
|
|
50
|
+
gold: '\x1b[33m',
|
|
51
|
+
blue: '\x1b[34m',
|
|
52
|
+
red: '\x1b[31m',
|
|
53
|
+
reset: '\x1b[0m',
|
|
54
|
+
bold: '\x1b[1m'
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
function logo() {
|
|
58
|
+
console.log(`
|
|
59
|
+
${colors.gold}╔═══════════════════════════════════════╗
|
|
60
|
+
║ ║
|
|
61
|
+
║ ${colors.green}█████╗ ███████╗██████╗ ██╗${colors.gold} ║
|
|
62
|
+
║ ${colors.green}██╔══██╗██╔════╝██╔══██╗██║${colors.gold} ║
|
|
63
|
+
║ ${colors.green}███████║█████╗ ██████╔╝██║${colors.gold} ║
|
|
64
|
+
║ ${colors.green}██╔══██║██╔══╝ ██╔══██╗██║${colors.gold} ║
|
|
65
|
+
║ ${colors.green}██║ ██║██║ ██║ ██║██║${colors.gold} ║
|
|
66
|
+
║ ${colors.green}╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝╚═╝${colors.gold} ║
|
|
67
|
+
║ ║
|
|
68
|
+
║ ${colors.reset}The Rhythmic Web Framework${colors.gold} ║
|
|
69
|
+
╚═══════════════════════════════════════╝${colors.reset}
|
|
70
|
+
`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function help() {
|
|
74
|
+
logo();
|
|
75
|
+
console.log(`
|
|
76
|
+
${colors.bold}Usage:${colors.reset}
|
|
77
|
+
africode <command> [options]
|
|
78
|
+
|
|
79
|
+
${colors.bold}Commands:${colors.reset}
|
|
80
|
+
${colors.green}dev${colors.reset} Start development server
|
|
81
|
+
${colors.green}build${colors.reset} Create production build
|
|
82
|
+
${colors.green}add${colors.reset} Add a component (e.g., africode add sidebar)
|
|
83
|
+
${colors.green}new${colors.reset} Create a new project (options: --template 3d)
|
|
84
|
+
${colors.green}migrate${colors.reset} Run database migrations
|
|
85
|
+
${colors.green}help${colors.reset} Show this help message
|
|
86
|
+
|
|
87
|
+
${colors.bold}Examples:${colors.reset}
|
|
88
|
+
africode dev
|
|
89
|
+
africode new my-app --template 3d
|
|
90
|
+
africode add toast
|
|
91
|
+
|
|
92
|
+
${colors.gold}Powered by the rhythm of the continent.${colors.reset}
|
|
93
|
+
`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function dev() {
|
|
97
|
+
logo();
|
|
98
|
+
console.log(`${colors.green}▶ Starting development server...${colors.reset}\n`);
|
|
99
|
+
|
|
100
|
+
const server = Bun.serve({
|
|
101
|
+
port: 3000,
|
|
102
|
+
async fetch(req) {
|
|
103
|
+
const url = new URL(req.url);
|
|
104
|
+
let path = url.pathname;
|
|
105
|
+
|
|
106
|
+
// 1. API Route Delegation
|
|
107
|
+
if (path.startsWith('/api/')) {
|
|
108
|
+
const { handleApiRequest } = await import('../core/server/router.js');
|
|
109
|
+
const apiRes = await handleApiRequest(req);
|
|
110
|
+
if (apiRes) return apiRes;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 2. Root Redirect
|
|
114
|
+
if (path === '/') path = '/index';
|
|
115
|
+
|
|
116
|
+
// 3. JS-First Page Rendering (Next.js Style)
|
|
117
|
+
// Route /about -> looks for pages/about.js
|
|
118
|
+
const pageName = path.replace(/^\//, '').replace(/\.html$/, '');
|
|
119
|
+
const jsPagePath = `./pages/${pageName}.js`;
|
|
120
|
+
|
|
121
|
+
if (await Bun.file(jsPagePath).exists()) {
|
|
122
|
+
try {
|
|
123
|
+
// Dynamic Import with Cache Busting (for Hot Reloading in Dev)
|
|
124
|
+
const pageModule = await import(`${process.cwd()}/pages/${pageName}.js?t=${Date.now()}`);
|
|
125
|
+
|
|
126
|
+
// execute loader if present
|
|
127
|
+
let loaderData = {};
|
|
128
|
+
if (typeof pageModule.loader === 'function') {
|
|
129
|
+
try {
|
|
130
|
+
loaderData = await pageModule.loader({ request: req, params: {} });
|
|
131
|
+
} catch (loadErr) {
|
|
132
|
+
console.error(`Loader failed for ${pageName}:`, loadErr);
|
|
133
|
+
return new Response(`Loader Error: ${loadErr.message}`, { status: 500 });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Render Default Export
|
|
138
|
+
if (typeof pageModule.default === 'function') {
|
|
139
|
+
// Pass loader data to the component
|
|
140
|
+
const htmlContent = pageModule.default({ data: loaderData });
|
|
141
|
+
return new Response(htmlContent, {
|
|
142
|
+
headers: { 'Content-Type': 'text/html' }
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
} catch (err) {
|
|
146
|
+
console.error(`Error rendering page ${pageName}:`, err);
|
|
147
|
+
return new Response(`Error rendering page: ${err.message}`, { status: 500 });
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// 4. Fallback to Static HTML (Legacy Support)
|
|
152
|
+
// If .js doesn't exist, looking for .html
|
|
153
|
+
if (await Bun.file(`./pages${path}.html`).exists()) {
|
|
154
|
+
const { renderPage } = await import('../core/server/render.js');
|
|
155
|
+
return await renderPage(`./pages${path}.html`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// 5. Static Assets (CSS, Images, etc)
|
|
159
|
+
const file = Bun.file('.' + url.pathname); // Use original path for assets
|
|
160
|
+
|
|
161
|
+
if (await file.exists()) {
|
|
162
|
+
|
|
163
|
+
// SSR Hook for HTML
|
|
164
|
+
if (path.endsWith('.html')) {
|
|
165
|
+
const { renderPage } = await import('../core/server/render.js');
|
|
166
|
+
return await renderPage('.' + path);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const ext = path.split('.').pop();
|
|
170
|
+
const types = {
|
|
171
|
+
html: 'text/html', css: 'text/css', js: 'application/javascript',
|
|
172
|
+
json: 'application/json', svg: 'image/svg+xml', png: 'image/png', jpg: 'image/jpeg'
|
|
173
|
+
};
|
|
174
|
+
return new Response(file, {
|
|
175
|
+
headers: { 'Content-Type': types[ext] || 'application/octet-stream' }
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return new Response('404 Not Found', { status: 404 });
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
console.log(`${colors.green}✓ Server running at ${colors.blue}http://localhost:${server.port}${colors.reset}`);
|
|
184
|
+
console.log(`${colors.gold} Press Ctrl+C to stop${colors.reset}\n`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async function build() {
|
|
188
|
+
logo();
|
|
189
|
+
console.log(`${colors.green}▶ Building for production (SSG + Islands)...${colors.reset}\n`);
|
|
190
|
+
|
|
191
|
+
const { rm, cp, mkdir, readdir, writeFile } = await import('fs/promises');
|
|
192
|
+
const path = await import('path');
|
|
193
|
+
const distDir = './dist';
|
|
194
|
+
const pagesDir = './pages';
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
// Clean
|
|
198
|
+
await rm(distDir, { recursive: true, force: true });
|
|
199
|
+
await mkdir(distDir);
|
|
200
|
+
|
|
201
|
+
// 1. Bundle Core SDK & Components (Islands)
|
|
202
|
+
console.log(' Bundling JavaScript Islands...');
|
|
203
|
+
const buildResult = await Bun.build({
|
|
204
|
+
entrypoints: ['./core/sdk.js', './components/index.js'],
|
|
205
|
+
outdir: './dist/assets',
|
|
206
|
+
minify: true,
|
|
207
|
+
splitting: true,
|
|
208
|
+
target: 'browser',
|
|
209
|
+
naming: '[name]-[hash].[ext]' // Cache busting
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
if (!buildResult.success) {
|
|
213
|
+
throw new Error(buildResult.logs.join('\n'));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// 2. Static Site Generation (Pre-rendering)
|
|
217
|
+
console.log(' Pre-rendering Pages (SSG)...');
|
|
218
|
+
const pageFiles = await readdir(pagesDir);
|
|
219
|
+
const routes = [];
|
|
220
|
+
|
|
221
|
+
// Need the renderPage logic but adapted for Module imports
|
|
222
|
+
// We can't easily reuse the dev server logic verbatim, so we implement the SSG loop here.
|
|
223
|
+
|
|
224
|
+
for (const file of pageFiles) {
|
|
225
|
+
if (!file.endsWith('.js')) continue;
|
|
226
|
+
|
|
227
|
+
const name = file.replace('.js', '');
|
|
228
|
+
const route = name === 'index' ? '/' : `/${name}`;
|
|
229
|
+
routes.push(route);
|
|
230
|
+
|
|
231
|
+
console.log(` • Rendering ${route}...`);
|
|
232
|
+
|
|
233
|
+
// Dynamic Import of the Page Component
|
|
234
|
+
// CAUTION: In a real CLI, we might need to transpile or ignore CSS imports if they exist in JS.
|
|
235
|
+
// But since we use vanilla JS + tagged templates, it should work in Bun.
|
|
236
|
+
const pagePath = path.resolve(process.cwd(), pagesDir, file);
|
|
237
|
+
const pageModule = await import(pagePath);
|
|
238
|
+
|
|
239
|
+
// Execute Loader (Server-Side Data Fetching)
|
|
240
|
+
let loaderData = {};
|
|
241
|
+
if (typeof pageModule.loader === 'function') {
|
|
242
|
+
loaderData = await pageModule.loader({ request: new Request(`http://localhost${route}`), params: {} });
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Render HTML
|
|
246
|
+
if (typeof pageModule.default === 'function') {
|
|
247
|
+
const htmlContent = pageModule.default({ data: loaderData });
|
|
248
|
+
|
|
249
|
+
// We need to inject the hashed SDK script paths into the HTML
|
|
250
|
+
// For simplicity, we'll assume the standard names or use a manifest interaction if we were more advanced.
|
|
251
|
+
// For now, let's keep the SDK imports standard in Layout (or replace them here).
|
|
252
|
+
// NOTE: The Layout component currently hardcodes /core/sdk.js.
|
|
253
|
+
// To support hashing, we'd need to update Layout or use a transform here.
|
|
254
|
+
// We will stick to standard paths for the "Islands" bundles for now to keep it simple.
|
|
255
|
+
// Or better: write a small logic to replace the script src.
|
|
256
|
+
|
|
257
|
+
const outputPath = path.join(distDir, `${name}.html`);
|
|
258
|
+
await writeFile(outputPath, htmlContent);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// 3. Generate Sitemap
|
|
263
|
+
console.log(' Generating Sitemap...');
|
|
264
|
+
const { generateSitemap } = await import('../core/seo.js');
|
|
265
|
+
const sitemapXml = generateSitemap('https://your-domain.com', routes);
|
|
266
|
+
await writeFile(path.join(distDir, 'sitemap.xml'), sitemapXml);
|
|
267
|
+
|
|
268
|
+
// 4. Copy Static Assets
|
|
269
|
+
console.log(' Copying Assets...');
|
|
270
|
+
if (await Bun.file('./styles').exists()) await cp('./styles', `${distDir}/styles`, { recursive: true });
|
|
271
|
+
// Copy public assets if any (assuming root level assets or create a public folder later)
|
|
272
|
+
|
|
273
|
+
// 5. Create Server Entry (Optional, for dynamic hosting)
|
|
274
|
+
const serverBuild = await Bun.build({
|
|
275
|
+
entrypoints: ['./server.js'],
|
|
276
|
+
outdir: './dist',
|
|
277
|
+
target: 'bun'
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
console.log(`${colors.green}✓ Build complete!${colors.reset}`);
|
|
281
|
+
console.log(` Output: ${colors.blue}./dist${colors.reset} (Ready to deploy)`);
|
|
282
|
+
console.log(` Pages: ${colors.bold}${routes.length}${colors.reset} pre-rendered`);
|
|
283
|
+
console.log(` Sitemap: ${colors.blue}./dist/sitemap.xml${colors.reset}\n`);
|
|
284
|
+
|
|
285
|
+
} catch (err) {
|
|
286
|
+
console.error(`${colors.red}✗ Build failed:${colors.reset}`, err.message || err);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
async function addComponent(name) {
|
|
291
|
+
if (!name) {
|
|
292
|
+
console.log(`${colors.red}✗ Please specify a component name${colors.reset}`);
|
|
293
|
+
console.log(` Example: africode add sidebar\n`);
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const components = {
|
|
298
|
+
sidebar: 'af-sidebar',
|
|
299
|
+
tooltip: 'af-tooltip',
|
|
300
|
+
toast: 'af-toast',
|
|
301
|
+
skeleton: 'af-skeleton',
|
|
302
|
+
dropdown: 'af-dropdown'
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
if (!components[name]) {
|
|
306
|
+
console.log(`${colors.red}✗ Unknown component: ${name}${colors.reset}`);
|
|
307
|
+
console.log(` Available: ${Object.keys(components).join(', ')}\n`);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
console.log(`${colors.green}✓ Component ${colors.gold}${components[name]}${colors.green} is available in AfriCode!${colors.reset}`);
|
|
312
|
+
console.log(` Import with: import './components/${name}.js';\n`);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Command router
|
|
316
|
+
switch (command) {
|
|
317
|
+
case 'dev':
|
|
318
|
+
dev();
|
|
319
|
+
break;
|
|
320
|
+
case 'build':
|
|
321
|
+
build();
|
|
322
|
+
break;
|
|
323
|
+
case 'add':
|
|
324
|
+
addComponent(args[1]);
|
|
325
|
+
break;
|
|
326
|
+
case 'migrate':
|
|
327
|
+
const { runMigrations } = await import('../core/cli/migrator.js');
|
|
328
|
+
await runMigrations();
|
|
329
|
+
break;
|
|
330
|
+
case 'new':
|
|
331
|
+
case 'create':
|
|
332
|
+
case 'init':
|
|
333
|
+
const { createProject } = await import('./create-africode.js');
|
|
334
|
+
await createProject(args[1]);
|
|
335
|
+
break;
|
|
336
|
+
case 'help':
|
|
337
|
+
case '--help':
|
|
338
|
+
case '-h':
|
|
339
|
+
help();
|
|
340
|
+
break;
|
|
341
|
+
default:
|
|
342
|
+
help();
|
|
343
|
+
}
|
package/bin/cli.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* AfriCode CLI v5.0.0 - Bun-Native Framework Tool
|
|
4
|
+
* Autonomous application lifecycle management
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import '../core/shim.js';
|
|
8
|
+
import { help } from '../core/cli/ui.js';
|
|
9
|
+
|
|
10
|
+
const args = process.argv.slice(2);
|
|
11
|
+
const command = args[0];
|
|
12
|
+
|
|
13
|
+
async function run(): Promise<void> {
|
|
14
|
+
switch (command) {
|
|
15
|
+
case 'dev': {
|
|
16
|
+
const { dev } = await import('../core/cli/commands/dev.js');
|
|
17
|
+
await dev();
|
|
18
|
+
break;
|
|
19
|
+
}
|
|
20
|
+
case 'build': {
|
|
21
|
+
const { build } = await import('../core/cli/commands/build.js');
|
|
22
|
+
await build();
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
case 'add': {
|
|
26
|
+
const { addComponent } = await import('../core/cli/commands/add.js');
|
|
27
|
+
await addComponent(args[1]);
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
case 'migrate': {
|
|
31
|
+
const { runMigrations } = await import('../core/cli/migrator.js');
|
|
32
|
+
await runMigrations();
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
case 'new':
|
|
36
|
+
case 'create':
|
|
37
|
+
case 'init': {
|
|
38
|
+
if (args[1] === 'plugin') {
|
|
39
|
+
const { createPlugin } = await import('../core/cli/commands/create-plugin.js');
|
|
40
|
+
await createPlugin(args[2], args.slice(3));
|
|
41
|
+
} else {
|
|
42
|
+
const { createProject } = await import('./scaffold.js');
|
|
43
|
+
await createProject(args[1]);
|
|
44
|
+
}
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
case 'lint': {
|
|
48
|
+
const { lint } = await import('../core/cli/commands/lint.js');
|
|
49
|
+
await lint();
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
case 'audit': {
|
|
53
|
+
const { audit } = await import('../core/cli/commands/audit.js');
|
|
54
|
+
await audit();
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
case 'deploy': {
|
|
58
|
+
const { deploy } = await import('../core/cli/commands/deploy.js');
|
|
59
|
+
await deploy();
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
case 'generate': {
|
|
63
|
+
if (args[1] === 'a2ui') {
|
|
64
|
+
const { generateA2UISchema } = await import('../core/a2ui-schema-manager.js');
|
|
65
|
+
await generateA2UISchema();
|
|
66
|
+
} else if (args[1] === 'patterns') {
|
|
67
|
+
const { generatePatterns } = await import('../core/patterns.js');
|
|
68
|
+
await generatePatterns();
|
|
69
|
+
}
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
case 'help':
|
|
73
|
+
default: {
|
|
74
|
+
help();
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
run().catch((error) => {
|
|
81
|
+
console.error('CLI Error:', error.message);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
});
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* create-africode
|
|
4
|
+
*
|
|
5
|
+
* Scaffolds a new AfriCode project by copying the live framework files.
|
|
6
|
+
* usage: bunx create-africode my-app
|
|
7
|
+
*
|
|
8
|
+
* @module bin/create-africode
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { mkdir, writeFile, cp, readFile } from 'fs/promises';
|
|
12
|
+
import { existsSync } from 'fs';
|
|
13
|
+
import path from 'path';
|
|
14
|
+
import { fileURLToPath } from 'url';
|
|
15
|
+
|
|
16
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
+
const __dirname = path.dirname(__filename);
|
|
18
|
+
const FRAMEWORK_ROOT = path.resolve(__dirname, '..');
|
|
19
|
+
|
|
20
|
+
const colors = {
|
|
21
|
+
green: '\x1b[32m',
|
|
22
|
+
gold: '\x1b[33m',
|
|
23
|
+
blue: '\x1b[34m',
|
|
24
|
+
red: '\x1b[31m',
|
|
25
|
+
reset: '\x1b[0m',
|
|
26
|
+
bold: '\x1b[1m',
|
|
27
|
+
dim: '\x1b[2m'
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export async function createProject(targetName) {
|
|
31
|
+
const projectName = targetName || 'africode-app';
|
|
32
|
+
|
|
33
|
+
console.log(`
|
|
34
|
+
${colors.gold}╔═══════════════════════════════════════╗
|
|
35
|
+
║ ${colors.green}Creating AfriCode Project${colors.gold} ║
|
|
36
|
+
╚═══════════════════════════════════════╝${colors.reset}
|
|
37
|
+
`);
|
|
38
|
+
|
|
39
|
+
const projectDir = path.join(process.cwd(), projectName);
|
|
40
|
+
|
|
41
|
+
if (existsSync(projectDir)) {
|
|
42
|
+
console.log(`${colors.red}✗ Directory "${projectName}" already exists${colors.reset}`);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
console.log(`${colors.dim} Creating ${projectName}...${colors.reset}\n`);
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
// 1. Create Project Root
|
|
50
|
+
await mkdir(projectDir);
|
|
51
|
+
|
|
52
|
+
// 2. Copy Template Files
|
|
53
|
+
// Support --template arg
|
|
54
|
+
// node bin/create-africode.js my-app --template 3d
|
|
55
|
+
const args = process.argv;
|
|
56
|
+
const templateIdx = args.indexOf('--template');
|
|
57
|
+
const templateName = templateIdx > -1 ? args[templateIdx + 1] : 'starter';
|
|
58
|
+
|
|
59
|
+
// Validate template
|
|
60
|
+
const validTemplates = ['starter', 'starter-3d', 'starter-tailwind', 'starter-react', '3d', 'tailwind', 'react'];
|
|
61
|
+
// Map short aliases to full template directory names
|
|
62
|
+
const aliasMap = { '3d': 'starter-3d', 'tailwind': 'starter-tailwind', 'react': 'starter-react' };
|
|
63
|
+
const actualTemplate = aliasMap[templateName] || templateName || 'starter';
|
|
64
|
+
|
|
65
|
+
const templateDir = path.join(FRAMEWORK_ROOT, 'templates', actualTemplate);
|
|
66
|
+
|
|
67
|
+
if (!existsSync(templateDir)) {
|
|
68
|
+
throw new Error(`Template "${actualTemplate}" not found. Available: starter, tailwind, react, 3d`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
console.log(`${colors.blue}ℹ Using template: ${actualTemplate}${colors.reset}`);
|
|
72
|
+
|
|
73
|
+
// Recursive copy of the template
|
|
74
|
+
await cp(templateDir, projectDir, { recursive: true });
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
// 3. Package.json is already copied from template
|
|
78
|
+
// We just need to update the name
|
|
79
|
+
const pkgPath = path.join(projectDir, 'package.json');
|
|
80
|
+
const pkgData = await readFile(pkgPath, 'utf8');
|
|
81
|
+
const pkgJson = JSON.parse(pkgData);
|
|
82
|
+
|
|
83
|
+
pkgJson.name = projectName;
|
|
84
|
+
|
|
85
|
+
await writeFile(pkgPath, JSON.stringify(pkgJson, null, 2));
|
|
86
|
+
|
|
87
|
+
// 4. Create bin directory and copy CLI tool (africode.js)
|
|
88
|
+
// This allows 'bun run bin/africode.js dev' to work in the new project
|
|
89
|
+
const binSrc = path.join(FRAMEWORK_ROOT, 'bin', 'africode.js');
|
|
90
|
+
const binDestDir = path.join(projectDir, 'bin');
|
|
91
|
+
await mkdir(binDestDir, { recursive: true });
|
|
92
|
+
|
|
93
|
+
if (existsSync(binSrc)) {
|
|
94
|
+
await cp(binSrc, path.join(binDestDir, 'africode.js'));
|
|
95
|
+
} else {
|
|
96
|
+
console.error(`${colors.red}✗ Critical: CLI entry point not found${colors.reset}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// 5. Auto-generate index.js if missing
|
|
100
|
+
const srcDir = path.join(projectDir, 'src');
|
|
101
|
+
await mkdir(srcDir, { recursive: true });
|
|
102
|
+
const indexPath = path.join(srcDir, 'index.js');
|
|
103
|
+
if (!existsSync(indexPath)) {
|
|
104
|
+
const indexContent = `/**
|
|
105
|
+
* AfriCode App Entry Point
|
|
106
|
+
*
|
|
107
|
+
* This file exports your app. Modify as needed for your use case:
|
|
108
|
+
* - Server-side rendering: import Layout from core
|
|
109
|
+
* - Static site: export your components here
|
|
110
|
+
* - SPA: import and initialize your router here
|
|
111
|
+
*/
|
|
112
|
+
|
|
113
|
+
import { Layout } from './core/server/render.js';
|
|
114
|
+
import { html } from './core/html.js';
|
|
115
|
+
|
|
116
|
+
// Example: Simple HTML page export
|
|
117
|
+
export default function App() {
|
|
118
|
+
return Layout({
|
|
119
|
+
title: 'Welcome to AfriCode',
|
|
120
|
+
children: html\`
|
|
121
|
+
<main>
|
|
122
|
+
<h1>Welcome to AfriCode!</h1>
|
|
123
|
+
<p>Your African-centric full-stack framework is ready.</p>
|
|
124
|
+
<p><a href="/components">View Components</a> | <a href="/docs">Read Docs</a></p>
|
|
125
|
+
</main>
|
|
126
|
+
\`
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
`;
|
|
130
|
+
await writeFile(indexPath, indexContent);
|
|
131
|
+
console.log(`${colors.blue}ℹ Generated index.js entry point${colors.reset}`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// 6. Success Message
|
|
135
|
+
console.log(`${colors.green}✓ Project created successfully!${colors.reset}\n`);
|
|
136
|
+
console.log(`${colors.bold}Your project contains:${colors.reset}`);
|
|
137
|
+
console.log(` ${colors.blue}africode.config.js${colors.reset} - Framework Configuration`);
|
|
138
|
+
console.log(` ${colors.blue}src/components/${colors.reset} - 24+ UI Components`);
|
|
139
|
+
console.log(` ${colors.blue}src/styles/${colors.reset} - Design Tokens`);
|
|
140
|
+
console.log(` ${colors.blue}src/pages/${colors.reset} - Documentation & Demos`);
|
|
141
|
+
console.log(` ${colors.blue}src/index.js${colors.reset} - App Entry Point (auto-generated)`);
|
|
142
|
+
console.log(`\n${colors.bold}Next steps:${colors.reset}`);
|
|
143
|
+
console.log(` ${colors.dim}cd${colors.reset} ${projectName}`);
|
|
144
|
+
console.log(` ${colors.dim}bun install${colors.reset} (if needed)`);
|
|
145
|
+
console.log(` ${colors.dim}bun run${colors.reset} dev\n`);
|
|
146
|
+
console.log(`${colors.gold}Happy coding with the rhythm of the continent!${colors.reset}\n`);
|
|
147
|
+
|
|
148
|
+
} catch (err) {
|
|
149
|
+
console.error(`${colors.red}✗ Failed to create project:${colors.reset}`, err);
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Allow standalone execution if run directly
|
|
155
|
+
if (import.meta.main) {
|
|
156
|
+
const args = process.argv.slice(2);
|
|
157
|
+
createProject(args[0]);
|
|
158
|
+
}
|