neutronium 2.5.3 → 2.8.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/index.d.ts +117 -0
- package/package.json +9 -36
- package/LICENSE +0 -21
- package/README.md +0 -52
- package/cli/index.js +0 -178
- package/compiler/compiler.js +0 -131
- package/compiler/template.js +0 -26
- package/compiler/utils.js +0 -22
- package/neutronium.png +0 -0
- package/src/index.js +0 -104
package/index.d.ts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// @types/netronium/index.d.ts
|
|
2
|
+
|
|
3
|
+
type StateUpdater<T> = (newValue: T) => void;
|
|
4
|
+
type Component<T = any> = (props?: T) => HTMLElement | DocumentFragment;
|
|
5
|
+
|
|
6
|
+
let globalState: any[] = [];
|
|
7
|
+
let stateIndex = 0;
|
|
8
|
+
|
|
9
|
+
// Reset index before each render
|
|
10
|
+
export function resetStateIndex(): void {
|
|
11
|
+
stateIndex = 0;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Custom useState
|
|
15
|
+
export function useState<T>(initialValue: T): [T, StateUpdater<T>] {
|
|
16
|
+
const currentIndex = stateIndex;
|
|
17
|
+
|
|
18
|
+
if (globalState[currentIndex] === undefined) {
|
|
19
|
+
globalState[currentIndex] = initialValue;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function setState(newValue: T): void {
|
|
23
|
+
globalState[currentIndex] = newValue;
|
|
24
|
+
|
|
25
|
+
// Re-render
|
|
26
|
+
const root = window.__NEUTRONIUM_ROOT__ as HTMLElement | null;
|
|
27
|
+
const renderFn = window.__NEUTRONIUM_RENDER_FN__ as (() => Node) | null;
|
|
28
|
+
|
|
29
|
+
if (root && typeof renderFn === 'function') {
|
|
30
|
+
root.innerHTML = '';
|
|
31
|
+
resetStateIndex();
|
|
32
|
+
const newVNode = renderFn();
|
|
33
|
+
root.appendChild(newVNode);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
stateIndex++;
|
|
38
|
+
return [globalState[currentIndex], setState];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// JSX-compatible hyperscript function
|
|
42
|
+
export function h(
|
|
43
|
+
type: string | Component,
|
|
44
|
+
props: { [key: string]: any } = {},
|
|
45
|
+
...children: any[]
|
|
46
|
+
): HTMLElement | DocumentFragment {
|
|
47
|
+
if (typeof type === 'function') {
|
|
48
|
+
props = props || {};
|
|
49
|
+
props.children = children.flat();
|
|
50
|
+
return type(props);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const el = document.createElement(type);
|
|
54
|
+
|
|
55
|
+
for (const [key, value] of Object.entries(props)) {
|
|
56
|
+
if (key.startsWith('on') && typeof value === 'function') {
|
|
57
|
+
el.addEventListener(key.slice(2).toLowerCase(), value);
|
|
58
|
+
} else if (key === 'ref' && typeof value === 'function') {
|
|
59
|
+
value(el);
|
|
60
|
+
} else {
|
|
61
|
+
el.setAttribute(key, value);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
children.flat().forEach(child => {
|
|
66
|
+
if (typeof child === 'string' || typeof child === 'number') {
|
|
67
|
+
el.appendChild(document.createTextNode(child));
|
|
68
|
+
} else if (child instanceof Node) {
|
|
69
|
+
el.appendChild(child);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
return el;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Mount app to DOM
|
|
77
|
+
export function createApp(component: () => Node) {
|
|
78
|
+
return {
|
|
79
|
+
mount(selector: string | HTMLElement): Node | null {
|
|
80
|
+
const root =
|
|
81
|
+
typeof selector === 'string'
|
|
82
|
+
? document.querySelector(selector)
|
|
83
|
+
: selector;
|
|
84
|
+
|
|
85
|
+
if (!root) {
|
|
86
|
+
console.error(`❌ Root element '${selector}' not found`);
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
window.__NEUTRONIUM_ROOT__ = root;
|
|
91
|
+
window.__NEUTRONIUM_RENDER_FN__ = component;
|
|
92
|
+
|
|
93
|
+
resetStateIndex();
|
|
94
|
+
const vnode = component();
|
|
95
|
+
root.innerHTML = '';
|
|
96
|
+
root.appendChild(vnode);
|
|
97
|
+
|
|
98
|
+
return vnode;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Fragment support
|
|
104
|
+
export function Fragment(props: { children?: any[] }): DocumentFragment {
|
|
105
|
+
const frag = document.createDocumentFragment();
|
|
106
|
+
const children = props.children ?? [];
|
|
107
|
+
|
|
108
|
+
(Array.isArray(children) ? children : [children]).forEach(child => {
|
|
109
|
+
if (typeof child === 'string' || typeof child === 'number') {
|
|
110
|
+
frag.appendChild(document.createTextNode(child));
|
|
111
|
+
} else if (child instanceof Node) {
|
|
112
|
+
frag.appendChild(child);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
return frag;
|
|
117
|
+
}
|
package/package.json
CHANGED
|
@@ -1,41 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neutronium",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "
|
|
5
|
-
"main": "
|
|
6
|
-
"type": "commonjs",
|
|
3
|
+
"version": "2.8.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "index.ts",
|
|
7
6
|
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
8
|
},
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
"neu": "src/index.js"
|
|
15
|
-
},
|
|
16
|
-
"exports": {
|
|
17
|
-
".": "./src/index.js"
|
|
18
|
-
},
|
|
19
|
-
"keywords": [
|
|
20
|
-
"framework",
|
|
21
|
-
"javascript",
|
|
22
|
-
"frontend",
|
|
23
|
-
"ui",
|
|
24
|
-
"components"
|
|
25
|
-
],
|
|
26
|
-
"author": "PFMCODES",
|
|
27
|
-
"license": "MIT",
|
|
28
|
-
"dependencies": {
|
|
29
|
-
"chokidar": "^4.0.3",
|
|
30
|
-
"inquirer": "^12.7.0",
|
|
31
|
-
"jsdom": "^26.1.0",
|
|
32
|
-
"mime": "^4.0.7",
|
|
33
|
-
"open": "^10.1.2",
|
|
34
|
-
"ws": "^8.18.3",
|
|
35
|
-
"@babel/cli": "^7.28.0",
|
|
36
|
-
"@babel/core": "^7.28.0",
|
|
37
|
-
"@babel/plugin-transform-react-jsx": "^7.27.1",
|
|
38
|
-
"@babel/preset-env": "^7.28.0",
|
|
39
|
-
"@babel/preset-react": "^7.27.1"
|
|
40
|
-
}
|
|
9
|
+
"keywords": [],
|
|
10
|
+
"author": "",
|
|
11
|
+
"license": "ISC",
|
|
12
|
+
"type": "module",
|
|
13
|
+
"types": "./index.d.ts"
|
|
41
14
|
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 PFMCODES
|
|
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
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
# ⚛️ Neutronium
|
|
2
|
-
|
|
3
|
-
**Ultra-dense JavaScript framework – maximum performance, minimal overhead**
|
|
4
|
-
|
|
5
|
-
[](https://www.npmjs.com/package/neutronium)
|
|
6
|
-
[](https://opensource.org/licenses/MIT)
|
|
7
|
-
[](https://www.npmjs.com/package/neutronium)
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## 🚀 About
|
|
12
|
-
|
|
13
|
-
**Neutronium** is a lightweight, efficient JavaScript framework designed for building modern web applications with **React-like simplicity** but **minimal overhead**.
|
|
14
|
-
|
|
15
|
-
> Ultra-fast ⚡️. Tiny footprint 📦. No build tools 🛠️. Pure JavaScript ✨.
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## ✨ Features
|
|
20
|
-
|
|
21
|
-
- ⚡️ **Blazing fast rendering**
|
|
22
|
-
- 🧠 **Simple component logic**
|
|
23
|
-
- 🔌 **No dependencies or virtual DOM**
|
|
24
|
-
- 📦 **Small size (~139kB unpacked)**
|
|
25
|
-
- 🛠️ **Works out of the box**
|
|
26
|
-
- 🔁 **Easy JSX-style structure**
|
|
27
|
-
|
|
28
|
-
---
|
|
29
|
-
|
|
30
|
-
## 📦 Installation
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
npm i neutronium
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## Usage Example
|
|
37
|
-
```jsx
|
|
38
|
-
// App.js
|
|
39
|
-
function Greeting(props) {
|
|
40
|
-
return <h2>Hello, {props.name}!</h2>;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export default function App() {
|
|
44
|
-
return (
|
|
45
|
-
<>
|
|
46
|
-
<h1>Welcome to Neutronium</h1>
|
|
47
|
-
{Greeting({ name: "Krushna" })}
|
|
48
|
-
</>
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
```
|
|
52
|
-
|
package/cli/index.js
DELETED
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// --- Module Imports ---
|
|
4
|
-
const { default: inquirer } = require('inquirer');
|
|
5
|
-
const fs = require('fs');
|
|
6
|
-
const path = require('path');
|
|
7
|
-
const { execSync } = require('child_process');
|
|
8
|
-
const { transformSync } = require('@babel/core');
|
|
9
|
-
const { compileProject, compileProjectWatch } = require('../compiler/compiler');
|
|
10
|
-
|
|
11
|
-
// --- CLI Arguments ---
|
|
12
|
-
const [, , command, ...args] = process.argv;
|
|
13
|
-
|
|
14
|
-
// --- Default Babel Config ---
|
|
15
|
-
const babelRc = `{
|
|
16
|
-
"plugins": [
|
|
17
|
-
["@babel/plugin-transform-react-jsx", {
|
|
18
|
-
"pragma": "_neutronium.h",
|
|
19
|
-
"pragmaFrag": "_neutronium.Fragment",
|
|
20
|
-
"runtime": "classic",
|
|
21
|
-
"useBuiltIns": false,
|
|
22
|
-
"sourceMaps": true,
|
|
23
|
-
"comments": false,
|
|
24
|
-
"minified": true,
|
|
25
|
-
}]
|
|
26
|
-
]
|
|
27
|
-
`;
|
|
28
|
-
|
|
29
|
-
// --- Default App.js Starter Template ---
|
|
30
|
-
const AppJs = `
|
|
31
|
-
import { h, createApp } from 'neutronium';
|
|
32
|
-
|
|
33
|
-
function App() {
|
|
34
|
-
return (
|
|
35
|
-
<div class="container">
|
|
36
|
-
<h1>Hello from Neutronium!</h1>
|
|
37
|
-
<p>Start building your app in JSX!</p>
|
|
38
|
-
</div>
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
createApp(App).mount('#app');
|
|
43
|
-
`;
|
|
44
|
-
|
|
45
|
-
// --- Basic HTML Template Function ---
|
|
46
|
-
const htmlTemplate = (title, jsCode) => `
|
|
47
|
-
<!DOCTYPE html>
|
|
48
|
-
<html>
|
|
49
|
-
<head>
|
|
50
|
-
<meta charset="UTF-8" />
|
|
51
|
-
<title>${title}</title>
|
|
52
|
-
</head>
|
|
53
|
-
<body>
|
|
54
|
-
<div id="app"></div>
|
|
55
|
-
<script type="module">
|
|
56
|
-
${jsCode}
|
|
57
|
-
</script>
|
|
58
|
-
</body>
|
|
59
|
-
</html>
|
|
60
|
-
`.trim();
|
|
61
|
-
|
|
62
|
-
// --- Project Initializer Function ---
|
|
63
|
-
async function init() {
|
|
64
|
-
let targetPath = process.cwd(); // default to current folder
|
|
65
|
-
let createdFolder = false;
|
|
66
|
-
|
|
67
|
-
// Prompt user for init location
|
|
68
|
-
const { confirmInit } = await inquirer.prompt([
|
|
69
|
-
{
|
|
70
|
-
type: 'confirm',
|
|
71
|
-
name: 'confirmInit',
|
|
72
|
-
message: 'Initialize a Neutronium app in this folder?',
|
|
73
|
-
default: true
|
|
74
|
-
}
|
|
75
|
-
]);
|
|
76
|
-
|
|
77
|
-
let appName = 'neutronium-app';
|
|
78
|
-
|
|
79
|
-
// If no, ask for folder name and create it
|
|
80
|
-
if (!confirmInit) {
|
|
81
|
-
const { projectName } = await inquirer.prompt([
|
|
82
|
-
{
|
|
83
|
-
type: 'input',
|
|
84
|
-
name: 'projectName',
|
|
85
|
-
message: 'Please enter your project name:',
|
|
86
|
-
default: 'neutronium-app'
|
|
87
|
-
}
|
|
88
|
-
]);
|
|
89
|
-
appName = projectName;
|
|
90
|
-
targetPath = path.resolve(process.cwd(), projectName);
|
|
91
|
-
createdFolder = true;
|
|
92
|
-
if (!fs.existsSync(targetPath)) fs.mkdirSync(targetPath);
|
|
93
|
-
else console.log('⚠️ Folder already exists. Using it anyway.');
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Write App.js
|
|
97
|
-
const appPath = path.join(targetPath, 'App.js');
|
|
98
|
-
fs.writeFileSync(appPath, AppJs.trim());
|
|
99
|
-
|
|
100
|
-
// Write .babelrc
|
|
101
|
-
fs.writeFileSync(path.join(targetPath, '.babelrc'), babelRc);
|
|
102
|
-
|
|
103
|
-
// Init NPM and install dependencies
|
|
104
|
-
execSync('npm init -y', { cwd: targetPath, stdio: 'inherit' });
|
|
105
|
-
execSync('npm install neutronium', { cwd: targetPath, stdio: 'inherit' });
|
|
106
|
-
execSync('npm install --save-dev @babel/core @babel/cli @babel/plugin-transform-react-jsx', {
|
|
107
|
-
cwd: targetPath,
|
|
108
|
-
stdio: 'inherit'
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
// Transpile App.js (in-memory) for preview HTML
|
|
112
|
-
const jsxWithImport = `import * as _neutronium from 'neutronium';\n\n${AppJs}`;
|
|
113
|
-
const result = transformSync(jsxWithImport, {
|
|
114
|
-
filename: 'App.js',
|
|
115
|
-
babelrc: false,
|
|
116
|
-
configFile: false,
|
|
117
|
-
presets: [],
|
|
118
|
-
plugins: [
|
|
119
|
-
['@babel/plugin-transform-react-jsx', {
|
|
120
|
-
pragma: '_neutronium.h',
|
|
121
|
-
pragmaFrag: '_neutronium.Fragment',
|
|
122
|
-
runtime: 'classic',
|
|
123
|
-
useBuiltIns: false,
|
|
124
|
-
sourceMaps: true,
|
|
125
|
-
comments: false,
|
|
126
|
-
minified: true,
|
|
127
|
-
}]
|
|
128
|
-
]
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
// Write dist/index.html with inlined JS
|
|
132
|
-
const finalHtml = htmlTemplate(appName, result.code);
|
|
133
|
-
const distPath = path.join(targetPath, 'dist');
|
|
134
|
-
if (!fs.existsSync(distPath)) fs.mkdirSync(distPath);
|
|
135
|
-
fs.writeFileSync(path.join(distPath, 'index.html'), finalHtml);
|
|
136
|
-
|
|
137
|
-
// Print instructions to user
|
|
138
|
-
const folderCmd = createdFolder ? `cd ${path.basename(targetPath)}` : '';
|
|
139
|
-
console.log('\n✅ Neutronium app is ready!');
|
|
140
|
-
console.log(`➡️ Run the following to get started:\n\n ${folderCmd}\n npx serve dist\n`);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// --- CLI Command Routing ---
|
|
144
|
-
switch (command) {
|
|
145
|
-
case 'init':
|
|
146
|
-
case 'create-app':
|
|
147
|
-
case 'create-neu-app':
|
|
148
|
-
case 'create-new-app':
|
|
149
|
-
case 'create-neutronium-app':
|
|
150
|
-
init();
|
|
151
|
-
break;
|
|
152
|
-
|
|
153
|
-
case 'start':
|
|
154
|
-
// Watch mode for development
|
|
155
|
-
if (args[0] === '--watch') {
|
|
156
|
-
compileProjectWatch();
|
|
157
|
-
} else {
|
|
158
|
-
// Build once without watching
|
|
159
|
-
compileProject();
|
|
160
|
-
}
|
|
161
|
-
break;
|
|
162
|
-
|
|
163
|
-
default:
|
|
164
|
-
// Help text
|
|
165
|
-
console.log('❌ Unknown command.');
|
|
166
|
-
console.log(`
|
|
167
|
-
Available Commands:
|
|
168
|
-
|
|
169
|
-
init | create-app | create-neutronium-app
|
|
170
|
-
👉 Initialize a new Neutronium project
|
|
171
|
-
|
|
172
|
-
start
|
|
173
|
-
👉 Compiles your app once
|
|
174
|
-
|
|
175
|
-
start --watch
|
|
176
|
-
👉 Start dev server and rebuild on changes
|
|
177
|
-
`);
|
|
178
|
-
}
|
package/compiler/compiler.js
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
const babel = require('@babel/core');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const { baseHtml } = require('./template');
|
|
5
|
-
const { log, writeFile, ensureDir } = require('./utils');
|
|
6
|
-
const chokidar = require('chokidar');
|
|
7
|
-
const http = require('http');
|
|
8
|
-
const { default: Mime } = require('mime');
|
|
9
|
-
const WebSocket = require('ws');
|
|
10
|
-
const { default: open } = require('open');
|
|
11
|
-
|
|
12
|
-
function compileProject(projectDir = process.cwd()) {
|
|
13
|
-
const appJsPath = path.join(projectDir, 'App.js');
|
|
14
|
-
const distDir = path.join(projectDir, 'dist');
|
|
15
|
-
const outHtmlPath = path.join(distDir, 'index.html');
|
|
16
|
-
const outJsPath = path.join(distDir, 'App.compiled.js');
|
|
17
|
-
const neutroniumPath = '../node_modules/neutronium/src/index.js';
|
|
18
|
-
|
|
19
|
-
try {
|
|
20
|
-
log('📖 Reading App.js...');
|
|
21
|
-
const sourceCode = fs.readFileSync(appJsPath, 'utf-8');
|
|
22
|
-
|
|
23
|
-
log('⚙️ Compiling JSX with Babel...');
|
|
24
|
-
let { code: transpiled } = babel.transformSync(sourceCode, {
|
|
25
|
-
filename: 'App.js', // or use path.basename(appJsPath)
|
|
26
|
-
babelrc: false,
|
|
27
|
-
configFile: false,
|
|
28
|
-
presets: [],
|
|
29
|
-
plugins: [
|
|
30
|
-
['@babel/plugin-transform-react-jsx', {
|
|
31
|
-
pragma: '_neutronium.h',
|
|
32
|
-
pragmaFrag: '_neutronium.Fragment',
|
|
33
|
-
runtime: 'classic',
|
|
34
|
-
useBuiltIns: false, // <- IMPORTANT
|
|
35
|
-
sourceMaps: true,
|
|
36
|
-
comments: false,
|
|
37
|
-
minified: true,
|
|
38
|
-
}]
|
|
39
|
-
]
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
// Remove CommonJS require if present
|
|
43
|
-
transpiled = transpiled.replace(
|
|
44
|
-
/(const|var|let)\s+_neutronium\s*=\s*require\(["']neutronium["']\);?/g,
|
|
45
|
-
''
|
|
46
|
-
);
|
|
47
|
-
transpiled = transpiled.replace("from 'neutronium';", "from '../node_modules/neutronium/src/index.js';")
|
|
48
|
-
|
|
49
|
-
const finalJsCode = `
|
|
50
|
-
import * as _neutronium from '${neutroniumPath}';
|
|
51
|
-
|
|
52
|
-
"use strict";
|
|
53
|
-
|
|
54
|
-
${transpiled}
|
|
55
|
-
|
|
56
|
-
_neutronium.createApp(App).mount('#app');
|
|
57
|
-
`.trim();
|
|
58
|
-
|
|
59
|
-
ensureDir(distDir);
|
|
60
|
-
writeFile(outJsPath, finalJsCode);
|
|
61
|
-
|
|
62
|
-
log('🛠️ Generating index.html...');
|
|
63
|
-
const finalHtml = baseHtml(`<div id="app"></div>`, 'App.compiled.js');
|
|
64
|
-
writeFile(outHtmlPath, finalHtml);
|
|
65
|
-
|
|
66
|
-
log('✅ Compilation complete!');
|
|
67
|
-
log(`➡️ Output: ${outHtmlPath}`);
|
|
68
|
-
} catch (e) {
|
|
69
|
-
console.error('❌ Compilation failed:', e.message);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function compileProjectWatch(projectDir = process.cwd(), port = 3000) {
|
|
74
|
-
const appJsPath = path.join(projectDir, 'App.js');
|
|
75
|
-
|
|
76
|
-
const server = serveProject(projectDir, port);
|
|
77
|
-
compileProject(projectDir);
|
|
78
|
-
|
|
79
|
-
log('👀 Watching App.js for changes...');
|
|
80
|
-
chokidar.watch(appJsPath).on('change', () => {
|
|
81
|
-
console.clear();
|
|
82
|
-
log('🔁 Detected change in App.js...');
|
|
83
|
-
compileProject(projectDir);
|
|
84
|
-
if (server.broadcastReload) {
|
|
85
|
-
server.broadcastReload();
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function serveProject(projectDir = process.cwd(), port = 3000) {
|
|
91
|
-
const distDir = path.join(projectDir, 'dist');
|
|
92
|
-
|
|
93
|
-
const server = http.createServer((req, res) => {
|
|
94
|
-
let reqPath = req.url;
|
|
95
|
-
|
|
96
|
-
// Redirect "/" to "dist/index.html"
|
|
97
|
-
if (reqPath === '/' || reqPath === '/index.html') {
|
|
98
|
-
reqPath = '/dist/index.html';
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const filePath = path.join(projectDir, reqPath);
|
|
102
|
-
|
|
103
|
-
if (!fs.existsSync(filePath)) {
|
|
104
|
-
res.writeHead(404);
|
|
105
|
-
return res.end('404 Not Found');
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const content = fs.readFileSync(filePath);
|
|
109
|
-
res.writeHead(200, { 'Content-Type': Mime.getType(filePath) });
|
|
110
|
-
res.end(content);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
const wss = new WebSocket.Server({ server });
|
|
114
|
-
|
|
115
|
-
server.broadcastReload = () => {
|
|
116
|
-
wss.clients.forEach(client => {
|
|
117
|
-
if (client.readyState === WebSocket.OPEN) {
|
|
118
|
-
client.send('reload');
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
server.listen(port, () => {
|
|
124
|
-
log(`🚀 Server running at http://localhost:${port}`);
|
|
125
|
-
open(`http://localhost:${port}/dist/index.html`);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
return server;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
module.exports = { compileProject, compileProjectWatch };
|
package/compiler/template.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
function baseHtml(appHtml, scriptName) {
|
|
2
|
-
return `
|
|
3
|
-
<!DOCTYPE html>
|
|
4
|
-
<html>
|
|
5
|
-
<head>
|
|
6
|
-
<meta charset="UTF-8">
|
|
7
|
-
<title>Neutronium App</title>
|
|
8
|
-
</head>
|
|
9
|
-
<body>
|
|
10
|
-
${appHtml}
|
|
11
|
-
<script type="module" src="./${scriptName}"></script>
|
|
12
|
-
<script>
|
|
13
|
-
const ws = new WebSocket('ws://' + location.host);
|
|
14
|
-
ws.onmessage = (msg) => {
|
|
15
|
-
if (msg.data === 'reload') {
|
|
16
|
-
console.log('[Neutronium] Reloading...');
|
|
17
|
-
location.reload();
|
|
18
|
-
}
|
|
19
|
-
};
|
|
20
|
-
</script>
|
|
21
|
-
</body>
|
|
22
|
-
</html>
|
|
23
|
-
`.trim();
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
module.exports = { baseHtml };
|
package/compiler/utils.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
// compiler/utils.js
|
|
2
|
-
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
|
|
6
|
-
function writeFile(filePath, content) {
|
|
7
|
-
fs.writeFileSync(filePath, content, 'utf-8');
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function readFile(filePath) {
|
|
11
|
-
return fs.readFileSync(filePath, 'utf-8');
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function ensureDir(dir) {
|
|
15
|
-
if (!fs.existsSync(dir)) fs.mkdirSync(dir);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function log(msg) {
|
|
19
|
-
console.log(`🔹 ${msg}`);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
module.exports = { writeFile, readFile, ensureDir, log };
|
package/neutronium.png
DELETED
|
Binary file
|
package/src/index.js
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
// src/index.js
|
|
2
|
-
|
|
3
|
-
let globalState = [];
|
|
4
|
-
let stateIndex = 0;
|
|
5
|
-
|
|
6
|
-
// This will be called before rendering begins
|
|
7
|
-
export function resetStateIndex() {
|
|
8
|
-
stateIndex = 0;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
// Custom useState implementation
|
|
12
|
-
function useState(initialValue) {
|
|
13
|
-
const current = stateIndex;
|
|
14
|
-
|
|
15
|
-
if (globalState[current] === undefined) {
|
|
16
|
-
globalState[current] = initialValue;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function setState(newValue) {
|
|
20
|
-
globalState[current] = newValue;
|
|
21
|
-
|
|
22
|
-
// Re-render the entire app
|
|
23
|
-
if (window.__NEUTRONIUM_ROOT__ && typeof window.__NEUTRONIUM_RENDER_FN__ === 'function') {
|
|
24
|
-
window.__NEUTRONIUM_ROOT__.innerHTML = '';
|
|
25
|
-
resetStateIndex();
|
|
26
|
-
const newVNode = window.__NEUTRONIUM_RENDER_FN__();
|
|
27
|
-
window.__NEUTRONIUM_ROOT__.appendChild(newVNode);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
stateIndex++;
|
|
32
|
-
return [globalState[current], setState];
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function h(type, props = {}, ...children) {
|
|
36
|
-
if (typeof type === 'function') {
|
|
37
|
-
// 🔧 Add children to props
|
|
38
|
-
props = props || {};
|
|
39
|
-
props.children = children.flat(); // ✅ critical fix
|
|
40
|
-
return type(props);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const el = document.createElement(type);
|
|
44
|
-
|
|
45
|
-
for (const [key, value] of Object.entries(props || {})) {
|
|
46
|
-
if (key.startsWith('on') && typeof value === 'function') {
|
|
47
|
-
el.addEventListener(key.slice(2).toLowerCase(), value);
|
|
48
|
-
} else if (key === 'ref' && typeof value === 'function') {
|
|
49
|
-
value(el);
|
|
50
|
-
} else {
|
|
51
|
-
el.setAttribute(key, value);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
children.flat().forEach(child => {
|
|
56
|
-
if (typeof child === 'string' || typeof child === 'number') {
|
|
57
|
-
el.appendChild(document.createTextNode(child));
|
|
58
|
-
} else if (child instanceof Node) {
|
|
59
|
-
el.appendChild(child);
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
return el;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function createApp(component) {
|
|
67
|
-
return {
|
|
68
|
-
mount(selector) {
|
|
69
|
-
const root = typeof selector === 'string' ? document.querySelector(selector) : selector;
|
|
70
|
-
if (!root) {
|
|
71
|
-
console.error(`❌ Root element '${selector}' not found`);
|
|
72
|
-
return null; // return something explicit
|
|
73
|
-
}
|
|
74
|
-
// Save render function + root globally so useState can use them
|
|
75
|
-
window.__NEUTRONIUM_ROOT__ = root;
|
|
76
|
-
window.__NEUTRONIUM_RENDER_FN__ = component;
|
|
77
|
-
|
|
78
|
-
// Reset before first render
|
|
79
|
-
resetStateIndex();
|
|
80
|
-
const vnode = component();
|
|
81
|
-
root.innerHTML = '';
|
|
82
|
-
root.appendChild(vnode);
|
|
83
|
-
|
|
84
|
-
return vnode; // ✅ REQUIRED
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function Fragment(props = {}) {
|
|
90
|
-
const frag = document.createDocumentFragment();
|
|
91
|
-
const children = props.children || [];
|
|
92
|
-
|
|
93
|
-
(Array.isArray(children) ? children : [children]).forEach(child => {
|
|
94
|
-
if (typeof child === 'string' || typeof child === 'number') {
|
|
95
|
-
frag.appendChild(document.createTextNode(child));
|
|
96
|
-
} else if (child instanceof Node) {
|
|
97
|
-
frag.appendChild(child);
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
return frag;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export { h, createApp, Fragment, useState };
|