neutronium 2.9.9 → 3.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/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # ⚛️ Neutronium
1
+ # ⚛️ Neutronium v3.0
2
2
 
3
3
  **Ultra-dense JavaScript framework – maximum performance, minimal overhead**
4
4
 
@@ -8,7 +8,14 @@
8
8
 
9
9
  ---
10
10
 
11
- ## 🚀 About
11
+ ## 🎉 What's new?
12
+ - ⚡ Faster compilation for complex projects
13
+ - ✨ useState, and useEffect
14
+ - ⚛️ React like syntax for easier switching
15
+
16
+ ---
17
+
18
+ ## ℹ️ About
12
19
 
13
20
  **Neutronium** is a lightweight, efficient JavaScript framework designed for building modern web applications with **React-like simplicity** but **minimal overhead**.
14
21
 
@@ -33,12 +40,16 @@
33
40
  npm i neutronium@latest -g
34
41
  ```
35
42
 
43
+ ---
44
+
36
45
  ## 🛠️ Setup
37
46
 
38
47
  ```
39
48
  neu-cli create-app my-app
40
49
  ```
41
50
 
51
+ ---
52
+
42
53
  ## Usage Example
43
54
  ```jsx
44
55
  // App.js
@@ -63,7 +74,12 @@ createApp(App).mount('body');
63
74
  ## Result:
64
75
  ![Results](https://raw.githubusercontent.com/PFMCODES/neutronium/main/results.png)
65
76
 
66
- ## Other helpful packages you might need
77
+ ---
78
+
79
+ ## NPM Packages using Neutronium
67
80
  ### [@neuhq/alert](https://www.npmjs.com/package/@neuhq/alert)
68
- ## ⚒️ In dev
69
- ### [@neuhq/router](https://www.npmjs.com/package/@neuhq/router)
81
+
82
+ ---
83
+
84
+ ## Found a bug or a problem?
85
+ ### Report [here.](https://github.com/PFMCODES/neutronium/issues/new)
package/cli/index.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  const { default: inquirer } = require('inquirer');
4
4
  const fs = require('fs');
@@ -12,14 +12,19 @@ const { execSync } = require('child_process');
12
12
 
13
13
  async function compileProject(projectDir = process.cwd()) {
14
14
  const distDir = path.join(projectDir, 'dist');
15
- const neutroniumPath = '../node_modules/neutronium/src/index.js';
15
+ const neutroniumPath = path.join(projectDir, 'node_modules', 'neutronium', 'src', 'index.js');
16
16
  const packageJson = JSON.parse(fs.readFileSync(path.join(projectDir, 'package.json')));
17
17
  const entry = packageJson.main || 'App.js';
18
18
 
19
19
  try {
20
20
  log('📁 Scanning project files...');
21
21
  const allFiles = fs.readdirSync(projectDir);
22
- const jsFiles = allFiles.filter(f => f.endsWith('.js') && f !== 'compiler.js' && !f.startsWith('.'));
22
+ const jsFiles = allFiles.filter(f =>
23
+ f.endsWith('.js') &&
24
+ f !== 'compiler.js' &&
25
+ !f.startsWith('.') &&
26
+ !f.startsWith('dist')
27
+ );
23
28
 
24
29
  if (fs.existsSync(path.join(projectDir, 'tsconfig.json'))) {
25
30
  try {
@@ -63,11 +68,11 @@ async function compileProject(projectDir = process.cwd()) {
63
68
 
64
69
  // Ensure _neutronium is imported
65
70
  if (!source.includes('_neutronium')) {
66
- code = `import * as _neutronium from '${neutroniumPath}';\n\n${code}`;
71
+ code = `import * as _neutronium from '${neutroniumPath.replace(/\\/g, '/')}';\n\n${code}`;
67
72
  }
68
73
 
69
- // Replace imports to local neutronium
70
- code = code.replace(/from\s+['"]neutronium['"]/g, `from '${neutroniumPath}'`);
74
+ // Replace imports to "neutronium" with local path
75
+ code = code.replace(/from\s+['"]neutronium['"]/g, `from '${neutroniumPath.replace(/\\/g, '/')}'`);
71
76
 
72
77
  writeFile(outputPath, code);
73
78
  }
@@ -88,32 +93,31 @@ async function compileProject(projectDir = process.cwd()) {
88
93
  }
89
94
 
90
95
  function compileProjectWatch(projectDir = process.cwd(), port = 3000) {
91
- // Start server first
92
96
  const server = serveProject(projectDir, port);
93
-
94
- // Do initial compilation
95
97
  compileProject(projectDir);
96
98
 
97
99
  log('👀 Watching project for changes...');
98
100
  log('✋ Press Ctrl+C to stop the development server');
99
-
101
+
100
102
  let timeout;
101
103
 
102
- // Watch files more specifically
103
- const watcher = chokidar.watch(projectDir, {
104
+ const watcher = chokidar.watch([
105
+ path.join(projectDir, '**/*.js'),
106
+ path.join(projectDir, '**/*.ts'),
107
+ path.join(projectDir, '**/*.tsx'),
108
+ ], {
104
109
  ignoreInitial: true,
105
110
  ignored: [
106
111
  '**/node_modules/**',
107
112
  '**/dist/**',
108
113
  '**/.git/**',
109
- '**/*',
110
114
  'compiler.js'
111
115
  ],
112
116
  persistent: true,
113
117
  followSymlinks: false,
114
- depth: 2,
118
+ depth: 5,
115
119
  awaitWriteFinish: {
116
- stabilityThreshold: 100,
120
+ stabilityThreshold: 300,
117
121
  pollInterval: 100
118
122
  }
119
123
  });
@@ -123,58 +127,45 @@ function compileProjectWatch(projectDir = process.cwd(), port = 3000) {
123
127
  });
124
128
 
125
129
  watcher.on('change', filePath => {
126
- // Only watch js, ts, tsx files
127
- if (!/\.(js|ts|tsx)$/.test(filePath)) {
128
- return;
129
- }
130
+ if (!/\.(js|ts|tsx)$/.test(filePath)) return;
130
131
 
131
132
  log(`🔍 Detected change in: ${path.relative(projectDir, filePath)}`);
132
-
133
+
133
134
  clearTimeout(timeout);
134
135
  timeout = setTimeout(() => {
135
136
  log('🔨 Rebuilding project...');
136
-
137
137
  try {
138
138
  const success = compileProject(projectDir);
139
- if (success) {
140
- log('✅ Rebuild successful!');
141
- // Trigger browser reload
142
- if (server && server.broadcastReload) {
143
- server.broadcastReload();
144
- log('🔄 Browser reload triggered');
145
- }
139
+ if (success && server.broadcastReload) {
140
+ server.broadcastReload();
141
+ log('🔄 Browser reload triggered');
146
142
  }
147
143
  } catch (err) {
148
144
  console.error('❌ Rebuild failed:', err.stack || err.message);
149
145
  }
150
- }, 300); // Slightly longer debounce
146
+ }, 300);
151
147
  });
152
148
 
153
149
  watcher.on('add', filePath => {
154
- if (!/\.(js|ts|tsx)$/.test(filePath)) {
155
- return;
150
+ if (/\.(js|ts|tsx)$/.test(filePath)) {
151
+ log(`📝 New file added: ${path.relative(projectDir, filePath)}`);
156
152
  }
157
- log(`📝 New file added: ${path.relative(projectDir, filePath)}`);
158
153
  });
159
154
 
160
155
  watcher.on('unlink', filePath => {
161
- if (!/\.(js|ts|tsx)$/.test(filePath)) {
162
- return;
156
+ if (/\.(js|ts|tsx)$/.test(filePath)) {
157
+ log(`🗑️ File removed: ${path.relative(projectDir, filePath)}`);
163
158
  }
164
- log(`🗑️ File removed: ${path.relative(projectDir, filePath)}`);
165
159
  });
166
160
 
167
161
  watcher.on('error', error => {
168
162
  console.error('❌ Watcher error:', error);
169
163
  });
170
164
 
171
- // Cleanup function
172
165
  const cleanup = () => {
173
166
  log('🧹 Cleaning up...');
174
167
  watcher.close();
175
- if (server) {
176
- server.close();
177
- }
168
+ if (server) server.close();
178
169
  process.exit(0);
179
170
  };
180
171
 
@@ -187,26 +178,23 @@ function compileProjectWatch(projectDir = process.cwd(), port = 3000) {
187
178
  function serveProject(projectDir = process.cwd(), port = 3000) {
188
179
  const server = http.createServer((req, res) => {
189
180
  let reqPath = req.url;
190
-
191
- // Handle root and index requests
181
+
192
182
  if (reqPath === '/' || reqPath === '/index.html') {
193
183
  reqPath = '/dist/index.html';
194
184
  }
195
-
196
- // Handle favicon requests
185
+
197
186
  if (reqPath === '/favicon.ico') {
198
187
  res.writeHead(204);
199
188
  return res.end();
200
189
  }
201
190
 
202
191
  const filePath = path.join(projectDir, reqPath);
203
-
204
- // Security check - ensure file is within project directory
192
+
205
193
  if (!filePath.startsWith(projectDir)) {
206
194
  res.writeHead(403);
207
195
  return res.end('403 Forbidden');
208
196
  }
209
-
197
+
210
198
  if (!fs.existsSync(filePath)) {
211
199
  res.writeHead(404);
212
200
  return res.end('404 Not Found');
@@ -215,8 +203,8 @@ function serveProject(projectDir = process.cwd(), port = 3000) {
215
203
  try {
216
204
  const content = fs.readFileSync(filePath);
217
205
  const mimeType = Mime.getType(filePath) || 'application/octet-stream';
218
-
219
- res.writeHead(200, {
206
+
207
+ res.writeHead(200, {
220
208
  'Content-Type': mimeType,
221
209
  'Cache-Control': 'no-cache, no-store, must-revalidate',
222
210
  'Pragma': 'no-cache',
@@ -231,14 +219,10 @@ function serveProject(projectDir = process.cwd(), port = 3000) {
231
219
  });
232
220
 
233
221
  const wss = new WebSocket.Server({ server });
234
-
235
- // Add WebSocket connection handling
222
+
236
223
  wss.on('connection', (ws) => {
237
224
  log('🔌 WebSocket client connected');
238
-
239
- ws.on('close', () => {
240
- log('🔌 WebSocket client disconnected');
241
- });
225
+ ws.on('close', () => log('🔌 WebSocket client disconnected'));
242
226
  });
243
227
 
244
228
  server.broadcastReload = () => {
@@ -252,12 +236,13 @@ function serveProject(projectDir = process.cwd(), port = 3000) {
252
236
  server.listen(port, () => {
253
237
  log(`🚀 Server running at http://localhost:${port}`);
254
238
  log(`🌐 Open your browser and navigate to: http://localhost:${port}`);
255
-
256
- // Remove the open() call completely for now to avoid hanging
257
- // User can manually open browser
258
239
  });
259
240
 
260
241
  return server;
261
242
  }
262
243
 
263
- module.exports = { compileProject, compileProjectWatch, serveProject };
244
+ module.exports = {
245
+ compileProject,
246
+ compileProjectWatch,
247
+ serveProject,
248
+ };
@@ -0,0 +1,17 @@
1
+ import chokidar from 'chokidar';
2
+ import path from 'path';
3
+
4
+ const projectDir = process.cwd(); // or absolute path
5
+
6
+ const watcher = chokidar.watch(path.join(projectDir, '**/*.js'), {
7
+ ignoreInitial: true,
8
+ ignored: ['**/node_modules/**'],
9
+ awaitWriteFinish: {
10
+ stabilityThreshold: 300,
11
+ pollInterval: 100
12
+ }
13
+ });
14
+
15
+ watcher.on('all', (event, filePath) => {
16
+ console.log(`[${event}] ${filePath}`);
17
+ });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "neutronium",
3
- "version": "2.9.9",
4
- "description": "A dense, efficient JavaScript framework for building modern web applications",
3
+ "version": "3.0.0",
4
+ "description": "Ultra-dense JavaScript framework maximum performance, minimal overhead",
5
5
  "main": "src/index.js",
6
6
  "type": "commonjs",
7
7
  "types": "./ts-neutronium/index.d.ts",
@@ -22,13 +22,13 @@
22
22
  ],
23
23
  "repository": {
24
24
  "type": "git",
25
- "url": "git+https://github.com/pfm37/n.git"
25
+ "url": "git+https://github.com/pfmcodes/neutronium.git"
26
26
  },
27
27
  "author": "PFMCODES",
28
28
  "bugs": {
29
- "url": "https://github.com/pfm37/n/issues"
29
+ "url": "https://github.com//pfmcodes/neutronium/issues"
30
30
  },
31
- "homepage": "https://github.com/pfm37/n#readme",
31
+ "homepage": "https://github.com/pfmcodes/neutronium#readme",
32
32
  "license": "MIT",
33
33
  "dependencies": {
34
34
  "@babel/core": "^7.28.0",
package/src/index.js CHANGED
@@ -3,42 +3,60 @@
3
3
  let globalState = [];
4
4
  let stateIndex = 0;
5
5
 
6
- // This will be called before rendering begins
7
- export function resetStateIndex() {
6
+ function resetStateIndex() {
8
7
  stateIndex = 0;
9
8
  }
10
9
 
11
- // Custom useState implementation
10
+ let currentEffect = null;
11
+
12
+ function useEffect(fn) {
13
+ currentEffect = fn;
14
+ fn(); // run once to collect dependencies
15
+ currentEffect = null;
16
+ }
17
+
12
18
  function useState(initialValue) {
13
19
  const index = stateIndex;
14
20
 
15
- if (globalState[index] === undefined) {
16
- globalState[index] = initialValue;
17
- }
21
+ if (!globalState[index]) {
22
+ let value = initialValue;
23
+ const subs = new Set();
18
24
 
19
- function setState(newValue) {
20
- globalState[index] = newValue;
25
+ function get() {
26
+ if (currentEffect) subs.add(currentEffect);
27
+ return value;
28
+ }
21
29
 
22
- if (typeof window.__NEUTRONIUM_RENDER_FN__ === 'function') {
23
- window.__NEUTRONIUM_RENDER_FN__(); // triggers re-render
30
+ function set(newVal) {
31
+ if (value !== newVal) {
32
+ value = newVal;
33
+ subs.forEach(fn => fn()); // re-run effects
34
+ if (typeof window.__NEUTRONIUM_RENDER_FN__ === 'function') {
35
+ window.__NEUTRONIUM_RENDER_FN__();
36
+ }
37
+ }
24
38
  }
39
+
40
+ globalState[index] = [get, set];
25
41
  }
26
42
 
43
+ const result = globalState[index];
27
44
  stateIndex++;
28
- return [globalState[index], setState];
45
+ return result;
29
46
  }
30
47
 
31
48
  function h(type, props = {}, ...children) {
49
+ props = props || {};
50
+ props.children = (props.children || []).concat(children).flat();
51
+
32
52
  if (typeof type === 'function') {
33
- // 🔧 Add children to props
34
- props = props || {};
35
- props.children = children.flat(); // ✅ critical fix
36
53
  return type(props);
37
54
  }
38
55
 
39
56
  const el = document.createElement(type);
40
57
 
41
58
  for (const [key, value] of Object.entries(props || {})) {
59
+ if (key === 'children') continue; // skip
42
60
  if (key.startsWith('on') && typeof value === 'function') {
43
61
  el.addEventListener(key.slice(2).toLowerCase(), value);
44
62
  } else if (key === 'ref' && typeof value === 'function') {
@@ -48,7 +66,7 @@ function h(type, props = {}, ...children) {
48
66
  }
49
67
  }
50
68
 
51
- children.flat().forEach(child => {
69
+ props.children.forEach(child => {
52
70
  if (typeof child === 'string' || typeof child === 'number') {
53
71
  el.appendChild(document.createTextNode(child));
54
72
  } else if (child instanceof Node) {
@@ -71,14 +89,14 @@ function createApp(component) {
71
89
  window.__NEUTRONIUM_ROOT__ = root;
72
90
 
73
91
  function render() {
74
- resetStateIndex(); // ✅ this is enough
92
+ resetStateIndex();
75
93
  const vnode = component();
76
94
  root.innerHTML = '';
77
95
  root.appendChild(vnode);
78
96
  }
79
97
 
80
- window.__NEUTRONIUM_RENDER_FN__ = render; // save render function
81
- render(); // initial render
98
+ window.__NEUTRONIUM_RENDER_FN__ = render;
99
+ render();
82
100
 
83
101
  return root;
84
102
  }
@@ -100,4 +118,4 @@ function Fragment(props = {}) {
100
118
  return frag;
101
119
  }
102
120
 
103
- export { h, createApp, Fragment, useState };
121
+ export { h, createApp, Fragment, useState,useEffect, resetStateIndex };
@@ -1,58 +1,71 @@
1
- // @types/netronium/index.d.ts
1
+ // src/index.ts
2
2
 
3
- type StateUpdater<T> = (newValue: T) => void;
4
- type Component<T = any> = (props?: T) => HTMLElement | DocumentFragment;
3
+ type EffectFn = () => void;
4
+ type Setter<T> = (value: T) => void;
5
+ type Getter<T> = () => T;
6
+ type StateTuple<T> = [Getter<T>, Setter<T>];
5
7
 
6
- let globalState: any[] = [];
8
+ let globalState: Array<StateTuple<any>> = [];
7
9
  let stateIndex = 0;
8
10
 
9
- // Reset index before each render
10
- export function resetStateIndex(): void {
11
+ function resetStateIndex(): void {
11
12
  stateIndex = 0;
12
13
  }
13
14
 
14
- // Custom useState
15
- export function useState<T>(initialValue: T): [T, StateUpdater<T>] {
16
- const currentIndex = stateIndex;
15
+ let currentEffect: EffectFn | null = null;
17
16
 
18
- if (globalState[currentIndex] === undefined) {
19
- globalState[currentIndex] = initialValue;
20
- }
21
-
22
- function setState(newValue: T): void {
23
- globalState[currentIndex] = newValue;
17
+ function useEffect(fn: EffectFn): void {
18
+ currentEffect = fn;
19
+ fn(); // run once to collect dependencies
20
+ currentEffect = null;
21
+ }
24
22
 
25
- // Re-render
26
- const root = window.__NEUTRONIUM_ROOT__ as HTMLElement | null;
27
- const renderFn = window.__NEUTRONIUM_RENDER_FN__ as (() => Node) | null;
23
+ function useState<T>(initialValue: T): StateTuple<T> {
24
+ const index = stateIndex;
25
+
26
+ if (!globalState[index]) {
27
+ let value = initialValue;
28
+ const subs = new Set<EffectFn>();
29
+
30
+ const get: Getter<T> = () => {
31
+ if (currentEffect) subs.add(currentEffect);
32
+ return value;
33
+ };
34
+
35
+ const set: Setter<T> = (newVal: T) => {
36
+ if (value !== newVal) {
37
+ value = newVal;
38
+ subs.forEach(fn => fn()); // re-run effects
39
+ if (typeof (window as any).__NEUTRONIUM_RENDER_FN__ === 'function') {
40
+ (window as any).__NEUTRONIUM_RENDER_FN__();
41
+ }
42
+ }
43
+ };
28
44
 
29
- if (root && typeof renderFn === 'function') {
30
- root.innerHTML = '';
31
- resetStateIndex();
32
- const newVNode = renderFn();
33
- root.appendChild(newVNode);
34
- }
45
+ globalState[index] = [get, set];
35
46
  }
36
47
 
48
+ const result = globalState[index] as StateTuple<T>;
37
49
  stateIndex++;
38
- return [globalState[currentIndex], setState];
50
+ return result;
39
51
  }
40
52
 
41
- // JSX-compatible hyperscript function
42
- export function h(
43
- type: string | Component,
44
- props: { [key: string]: any } = {},
45
- ...children: any[]
46
- ): HTMLElement | DocumentFragment {
53
+ type Props = {
54
+ [key: string]: any;
55
+ children?: any;
56
+ };
57
+
58
+ function h(type: string | ((props: Props) => Node), props: Props = {}, ...children: any[]): Node {
59
+ props.children = (props.children || []).concat(children).flat();
60
+
47
61
  if (typeof type === 'function') {
48
- props = props || {};
49
- props.children = children.flat();
50
62
  return type(props);
51
63
  }
52
64
 
53
65
  const el = document.createElement(type);
54
66
 
55
67
  for (const [key, value] of Object.entries(props)) {
68
+ if (key === 'children') continue;
56
69
  if (key.startsWith('on') && typeof value === 'function') {
57
70
  el.addEventListener(key.slice(2).toLowerCase(), value);
58
71
  } else if (key === 'ref' && typeof value === 'function') {
@@ -62,56 +75,68 @@ export function h(
62
75
  }
63
76
  }
64
77
 
65
- children.flat().forEach(child => {
78
+ for (const child of props.children) {
66
79
  if (typeof child === 'string' || typeof child === 'number') {
67
- el.appendChild(document.createTextNode(child));
80
+ el.appendChild(document.createTextNode(String(child)));
68
81
  } else if (child instanceof Node) {
69
82
  el.appendChild(child);
70
83
  }
71
- });
84
+ }
72
85
 
73
86
  return el;
74
87
  }
75
88
 
76
- // Mount app to DOM
77
- export function createApp(component: () => Node) {
89
+ function createApp(component: () => Node) {
78
90
  return {
79
- mount(selector: string | HTMLElement): Node | null {
80
- const root =
81
- typeof selector === 'string'
82
- ? document.querySelector(selector)
83
- : selector;
91
+ mount(selector: string | Element): Element | null {
92
+ const root = typeof selector === 'string'
93
+ ? document.querySelector(selector)
94
+ : selector;
84
95
 
85
96
  if (!root) {
86
97
  console.error(`❌ Root element '${selector}' not found`);
87
98
  return null;
88
99
  }
89
100
 
90
- window.__NEUTRONIUM_ROOT__ = root;
91
- window.__NEUTRONIUM_RENDER_FN__ = component;
101
+ (window as any).__NEUTRONIUM_ROOT__ = root;
102
+
103
+ function render() {
104
+ resetStateIndex();
105
+ const vnode = component();
106
+ root.innerHTML = '';
107
+ root.appendChild(vnode);
108
+ }
92
109
 
93
- resetStateIndex();
94
- const vnode = component();
95
- root.innerHTML = '';
96
- root.appendChild(vnode);
110
+ (window as any).__NEUTRONIUM_RENDER_FN__ = render;
111
+ render();
97
112
 
98
- return vnode;
113
+ return root;
99
114
  }
100
115
  };
101
116
  }
102
117
 
103
- // Fragment support
104
- export function Fragment(props: { children?: any[] }): DocumentFragment {
118
+ function Fragment(props: Props = {}): DocumentFragment {
105
119
  const frag = document.createDocumentFragment();
106
- const children = props.children ?? [];
120
+ const children = props.children || [];
121
+
122
+ const childArray = Array.isArray(children) ? children : [children];
107
123
 
108
- (Array.isArray(children) ? children : [children]).forEach(child => {
124
+ for (const child of childArray) {
109
125
  if (typeof child === 'string' || typeof child === 'number') {
110
- frag.appendChild(document.createTextNode(child));
126
+ frag.appendChild(document.createTextNode(String(child)));
111
127
  } else if (child instanceof Node) {
112
128
  frag.appendChild(child);
113
129
  }
114
- });
130
+ }
115
131
 
116
132
  return frag;
117
- }
133
+ }
134
+
135
+ export {
136
+ h,
137
+ createApp,
138
+ Fragment,
139
+ useState,
140
+ useEffect,
141
+ resetStateIndex
142
+ };