kontyra-cli 1.0.0 → 1.0.3

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/index.js CHANGED
@@ -38,6 +38,7 @@ const packageJson = {
38
38
  name: projectName,
39
39
  version: "0.1.0",
40
40
  private: true,
41
+ author: "Kontyra",
41
42
  scripts: {
42
43
  "dev": "node build.js && node server.js",
43
44
  "build": "node build.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kontyra-cli",
3
- "version": "1.0.0",
3
+ "version": "1.0.3",
4
4
  "description": "CLI tool to scaffold a new Kontyra Framework application.",
5
5
  "main": "index.js",
6
6
  "bin": {
package/template/build.js CHANGED
@@ -2,26 +2,45 @@ const esbuild = require('esbuild');
2
2
  const fs = require('fs');
3
3
  const path = require('path');
4
4
 
5
+ const WATCH_MODE = process.argv.includes('--watch');
5
6
  const PAGES_DIR = path.join(__dirname, 'src', 'pages');
6
7
 
7
- // 1. Scan pages directory
8
- const files = fs.readdirSync(PAGES_DIR).filter(f => f.endsWith('.kon'));
9
-
10
- // 2. Generate the React Router setup dynamically
11
- let imports = `import React from 'react';\n`;
12
- imports += `import Layout from './src/components/Layout.kon';\n`;
13
- let routes = '';
14
-
15
- files.forEach(file => {
16
- const componentName = file.replace('.kon', '');
17
- // index.kon maps to '/', about.kon maps to '/about'
18
- const routePath = componentName === 'index' ? '/' : `/${componentName}`;
19
-
20
- imports += `import ${componentName}Page from './src/pages/${file}';\n`;
21
- routes += ` <Route path="${routePath}" element={<${componentName}Page />} />\n`;
22
- });
8
+ // ─── Route Scanner ───────────────────────────────────────────────────────────
9
+ const walkSync = (dir, filelist = []) => {
10
+ if (!fs.existsSync(dir)) return filelist;
11
+ fs.readdirSync(dir).forEach(file => {
12
+ const dirFile = path.join(dir, file);
13
+ if (fs.statSync(dirFile).isDirectory()) {
14
+ filelist = walkSync(dirFile, filelist);
15
+ } else if (file.endsWith('.kon')) {
16
+ filelist.push(dirFile);
17
+ }
18
+ });
19
+ return filelist;
20
+ };
21
+
22
+ // ─── Entry Code Generator ────────────────────────────────────────────────────
23
+ function generateEntries() {
24
+ const allFiles = walkSync(PAGES_DIR);
25
+
26
+ let imports = `import React from 'react';\n`;
27
+ imports += `import Layout from './src/components/Layout.kon';\n`;
28
+ let routes = '';
29
+
30
+ allFiles.forEach(absolutePath => {
31
+ const relativePath = path.relative(PAGES_DIR, absolutePath).replace(/\\/g, '/');
32
+ let routePath = relativePath.replace('.kon', '');
33
+ routePath = routePath.replace(/\[([^\]]+)\]/g, ':$1');
34
+ if (routePath.endsWith('index')) routePath = routePath.replace(/index$/, '');
35
+ routePath = '/' + routePath;
36
+ if (routePath.length > 1 && routePath.endsWith('/')) routePath = routePath.slice(0, -1);
23
37
 
24
- const clientEntryCode = `
38
+ const componentName = 'Page_' + relativePath.replace(/[^a-zA-Z0-9]/g, '_');
39
+ imports += `import ${componentName} from './src/pages/${relativePath}';\n`;
40
+ routes += ` <Route path="${routePath}" element={<${componentName} />} />\n`;
41
+ });
42
+
43
+ const clientEntryCode = `
25
44
  ${imports}
26
45
  import { hydrateRoot } from 'react-dom/client';
27
46
  import { BrowserRouter, Routes, Route } from 'react-router-dom';
@@ -29,11 +48,11 @@ import { BrowserRouter, Routes, Route } from 'react-router-dom';
29
48
  function App() {
30
49
  return (
31
50
  <BrowserRouter>
32
- <Layout>
33
- <Routes>
51
+ <Routes>
52
+ <Route path="/" element={<Layout />}>
34
53
  ${routes}
35
- </Routes>
36
- </Layout>
54
+ </Route>
55
+ </Routes>
37
56
  </BrowserRouter>
38
57
  );
39
58
  }
@@ -41,7 +60,7 @@ ${routes}
41
60
  hydrateRoot(document.getElementById('root'), <App />);
42
61
  `;
43
62
 
44
- const serverEntryCode = `
63
+ const serverEntryCode = `
45
64
  ${imports}
46
65
  import { StaticRouter } from 'react-router-dom/server';
47
66
  import { Routes, Route } from 'react-router-dom';
@@ -49,21 +68,21 @@ import { Routes, Route } from 'react-router-dom';
49
68
  export function AppServer({ location }) {
50
69
  return (
51
70
  <StaticRouter location={location}>
52
- <Layout>
53
- <Routes>
71
+ <Routes>
72
+ <Route path="/" element={<Layout />}>
54
73
  ${routes}
55
- </Routes>
56
- </Layout>
74
+ </Route>
75
+ </Routes>
57
76
  </StaticRouter>
58
77
  );
59
78
  }
60
79
  `;
61
80
 
62
- // Write the dynamic entry files
63
- fs.writeFileSync(path.join(__dirname, '.client-entry.jsx'), clientEntryCode);
64
- fs.writeFileSync(path.join(__dirname, '.server-entry.jsx'), serverEntryCode);
81
+ fs.writeFileSync(path.join(__dirname, '.client-entry.jsx'), clientEntryCode);
82
+ fs.writeFileSync(path.join(__dirname, '.server-entry.jsx'), serverEntryCode);
83
+ }
65
84
 
66
- // Custom plugin to handle .kon files as JSX
85
+ // ─── esbuild Plugin ──────────────────────────────────────────────────────────
67
86
  const kontyraPlugin = {
68
87
  name: 'kontyra-plugin',
69
88
  setup(build) {
@@ -77,27 +96,72 @@ const kontyraPlugin = {
77
96
  const commonConfig = {
78
97
  bundle: true,
79
98
  plugins: [kontyraPlugin],
80
- loader: { '.js': 'jsx', '.jsx': 'jsx' },
99
+ loader: { '.js': 'jsx', '.jsx': 'jsx', '.css': 'css' },
81
100
  define: { 'process.env.NODE_ENV': '"development"' },
82
101
  };
83
102
 
84
- // Build Client
85
- esbuild.build({
86
- ...commonConfig,
87
- entryPoints: ['.client-entry.jsx'],
88
- outfile: 'public/dist/client.js',
89
- }).then(() => {
90
- console.log('Client build successful!');
91
- }).catch(() => process.exit(1));
92
-
93
- // Build Server
94
- esbuild.build({
95
- ...commonConfig,
96
- entryPoints: ['.server-entry.jsx'],
97
- outfile: 'dist/server-app.js',
98
- platform: 'node',
99
- format: 'cjs',
100
- external: ['react', 'react-dom', 'react-router-dom', 'react-router'],
101
- }).then(() => {
102
- console.log('Server build successful!');
103
- }).catch(() => process.exit(1));
103
+ // ─── Build Once or Watch ─────────────────────────────────────────────────────
104
+ async function build() {
105
+ generateEntries();
106
+
107
+ if (!WATCH_MODE) {
108
+ await esbuild.build({
109
+ ...commonConfig,
110
+ entryPoints: ['.client-entry.jsx'],
111
+ outfile: 'public/dist/client.js',
112
+ });
113
+ console.log('[kontyra] \u2713 Client build successful!');
114
+
115
+ await esbuild.build({
116
+ ...commonConfig,
117
+ entryPoints: ['.server-entry.jsx'],
118
+ outfile: 'dist/server-app.js',
119
+ platform: 'node',
120
+ format: 'cjs',
121
+ external: ['react', 'react-dom', 'react-router-dom', 'react-router'],
122
+ });
123
+ console.log('[kontyra] \u2713 Server build successful!');
124
+ return;
125
+ }
126
+
127
+ // ── Watch mode ──────────────────────────────────────────────────────────────
128
+ console.log('[kontyra] \uD83D\uDC40 Watch mode active. Waiting for file changes...\n');
129
+
130
+ const clientCtx = await esbuild.context({
131
+ ...commonConfig,
132
+ entryPoints: ['.client-entry.jsx'],
133
+ outfile: 'public/dist/client.js',
134
+ });
135
+
136
+ const serverCtx = await esbuild.context({
137
+ ...commonConfig,
138
+ entryPoints: ['.server-entry.jsx'],
139
+ outfile: 'dist/server-app.js',
140
+ platform: 'node',
141
+ format: 'cjs',
142
+ external: ['react', 'react-dom', 'react-router-dom', 'react-router'],
143
+ });
144
+
145
+ async function rebuild(reason) {
146
+ console.log(`[kontyra] \uD83D\uDD04 Change detected: ${reason}`);
147
+ generateEntries();
148
+ try {
149
+ await Promise.all([clientCtx.rebuild(), serverCtx.rebuild()]);
150
+ console.log(`[kontyra] \u2713 Rebuilt at ${new Date().toLocaleTimeString()}`);
151
+ } catch (e) {
152
+ console.error('[kontyra] \u2717 Build error:', e.message);
153
+ }
154
+ }
155
+
156
+ await rebuild('initial build');
157
+
158
+ fs.watch(path.join(__dirname, 'src'), { recursive: true }, (eventType, filename) => {
159
+ if (!filename) return;
160
+ rebuild(filename);
161
+ });
162
+ }
163
+
164
+ build().catch(e => {
165
+ console.error('[kontyra] Fatal build error:', e);
166
+ process.exit(1);
167
+ });
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "my-kontyra-app",
3
+ "version": "0.1.0",
4
+ "description": "A Kontyra Framework application",
5
+ "author": "Kontyra",
6
+ "private": true,
7
+ "scripts": {
8
+ "build": "node build.js",
9
+ "start": "node server.js",
10
+ "dev": "node build.js && node server.js",
11
+ "dev:build": "node build.js --watch",
12
+ "dev:serve": "node server.js",
13
+ "export": "node export.js"
14
+ },
15
+ "dependencies": {
16
+ "express": "^4.19.0",
17
+ "react": "^18.3.0",
18
+ "react-dom": "^18.3.0",
19
+ "react-router-dom": "^6.30.4"
20
+ },
21
+ "devDependencies": {
22
+ "esbuild": "^0.20.0"
23
+ }
24
+ }