neutronium 2.8.7 ā 2.9.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/README.md +2 -22
- package/cli/index.js +144 -128
- package/compiler/compiler.js +119 -235
- package/package.json +1 -1
- package/results.png +0 -0
package/README.md
CHANGED
|
@@ -36,7 +36,7 @@ npm i neutronium@latest -g
|
|
|
36
36
|
## š ļø Setup
|
|
37
37
|
|
|
38
38
|
```
|
|
39
|
-
neu-cli create-app
|
|
39
|
+
neu-cli create-app my-app
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
## Usage Example
|
|
@@ -61,24 +61,4 @@ createApp(App).mount('body');
|
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
## Result:
|
|
64
|
-
|
|
65
|
-
style="width:100%; height:200px; border:1px solid #ccc;"
|
|
66
|
-
srcdoc='
|
|
67
|
-
<!DOCTYPE html>
|
|
68
|
-
<html>
|
|
69
|
-
<head>
|
|
70
|
-
<style>
|
|
71
|
-
body {
|
|
72
|
-
font-family: sans-serif;
|
|
73
|
-
margin: 0;
|
|
74
|
-
padding: 1em;
|
|
75
|
-
}
|
|
76
|
-
</style>
|
|
77
|
-
</head>
|
|
78
|
-
<body>
|
|
79
|
-
<h1>Welcome to Neutronium</h1>
|
|
80
|
-
<h2>Hello, yourName!</h2>
|
|
81
|
-
</body>
|
|
82
|
-
</html>
|
|
83
|
-
'>
|
|
84
|
-
</iframe>
|
|
64
|
+

|
package/cli/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// --- Module Imports ---
|
|
4
3
|
const { default: inquirer } = require('inquirer');
|
|
5
4
|
const fs = require('fs');
|
|
6
5
|
const path = require('path');
|
|
@@ -8,37 +7,34 @@ const { execSync } = require('child_process');
|
|
|
8
7
|
const { transformSync } = require('@babel/core');
|
|
9
8
|
const { compileProject, compileProjectWatch } = require('../compiler/compiler');
|
|
10
9
|
|
|
11
|
-
// --- CLI Arguments ---
|
|
12
10
|
const [, , command, ...args] = process.argv;
|
|
13
11
|
|
|
14
|
-
// --- Default Babel Config ---
|
|
15
12
|
const babelRc = `{
|
|
16
|
-
"plugins": [
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
]
|
|
27
|
-
`;
|
|
13
|
+
"plugins": [
|
|
14
|
+
["@babel/plugin-transform-react-jsx", {
|
|
15
|
+
"pragma": "_neutronium.h",
|
|
16
|
+
"pragmaFrag": "_neutronium.Fragment",
|
|
17
|
+
"runtime": "classic",
|
|
18
|
+
"useBuiltIns": false,
|
|
19
|
+
"sourceMaps": true,
|
|
20
|
+
"comments": false,
|
|
21
|
+
"minified": true
|
|
22
|
+
}]
|
|
23
|
+
]
|
|
24
|
+
}`;
|
|
28
25
|
|
|
29
26
|
const AppTs = `
|
|
30
27
|
import { createApp } from 'neutronium';
|
|
31
28
|
|
|
32
29
|
function App() {
|
|
33
30
|
return (
|
|
34
|
-
<h1>Hello World</h1>
|
|
31
|
+
<h1>Hello World (TypeScript)</h1>
|
|
35
32
|
);
|
|
36
33
|
}
|
|
37
34
|
|
|
38
|
-
createApp(App).mount(
|
|
39
|
-
|
|
35
|
+
createApp(App).mount("body");
|
|
36
|
+
`;
|
|
40
37
|
|
|
41
|
-
// --- Default App.js Starter Template ---
|
|
42
38
|
const AppJs = `
|
|
43
39
|
import { createApp } from 'neutronium';
|
|
44
40
|
|
|
@@ -48,31 +44,28 @@ function App() {
|
|
|
48
44
|
);
|
|
49
45
|
}
|
|
50
46
|
|
|
51
|
-
createApp(App).mount(
|
|
47
|
+
createApp(App).mount("body");
|
|
52
48
|
`;
|
|
53
49
|
|
|
54
|
-
// --- Basic HTML Template Function ---
|
|
55
50
|
const htmlTemplate = (title, jsCode) => `
|
|
56
51
|
<!DOCTYPE html>
|
|
57
52
|
<html>
|
|
58
|
-
<head>
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
</head>
|
|
62
|
-
<body>
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
</body>
|
|
53
|
+
<head>
|
|
54
|
+
<meta charset="UTF-8" />
|
|
55
|
+
<title>${title}</title>
|
|
56
|
+
</head>
|
|
57
|
+
<body>
|
|
58
|
+
<script type="module">
|
|
59
|
+
${jsCode}
|
|
60
|
+
</script>
|
|
61
|
+
</body>
|
|
67
62
|
</html>
|
|
68
63
|
`.trim();
|
|
69
64
|
|
|
70
|
-
// --- Project Initializer Function ---
|
|
71
65
|
async function init() {
|
|
72
|
-
let targetPath = process.cwd();
|
|
66
|
+
let targetPath = process.cwd();
|
|
73
67
|
let createdFolder = false;
|
|
74
68
|
|
|
75
|
-
// Prompt user for init location
|
|
76
69
|
const { confirmInit } = await inquirer.prompt([
|
|
77
70
|
{
|
|
78
71
|
type: 'confirm',
|
|
@@ -84,13 +77,12 @@ async function init() {
|
|
|
84
77
|
|
|
85
78
|
let appName = 'neutronium-app';
|
|
86
79
|
|
|
87
|
-
// If no, ask for folder name and create it
|
|
88
80
|
if (!confirmInit) {
|
|
89
81
|
const { projectName } = await inquirer.prompt([
|
|
90
82
|
{
|
|
91
83
|
type: 'input',
|
|
92
84
|
name: 'projectName',
|
|
93
|
-
message: '
|
|
85
|
+
message: 'Enter your project name:',
|
|
94
86
|
default: 'neutronium-app'
|
|
95
87
|
}
|
|
96
88
|
]);
|
|
@@ -98,142 +90,163 @@ async function init() {
|
|
|
98
90
|
targetPath = path.resolve(process.cwd(), projectName);
|
|
99
91
|
createdFolder = true;
|
|
100
92
|
if (!fs.existsSync(targetPath)) fs.mkdirSync(targetPath);
|
|
101
|
-
else console.log('ā ļø Folder already exists. Using it anyway.');
|
|
102
93
|
}
|
|
103
94
|
|
|
104
|
-
//
|
|
105
|
-
const
|
|
106
|
-
|
|
95
|
+
// Ask language preference
|
|
96
|
+
const { language } = await inquirer.prompt([
|
|
97
|
+
{
|
|
98
|
+
type: 'list',
|
|
99
|
+
name: 'language',
|
|
100
|
+
message: 'Choose your language:',
|
|
101
|
+
choices: ['JavaScript', 'TypeScript']
|
|
102
|
+
}
|
|
103
|
+
]);
|
|
104
|
+
|
|
105
|
+
const appFileName = language === 'TypeScript' ? 'App.ts' : 'App.js';
|
|
106
|
+
fs.writeFileSync(path.join(targetPath, appFileName), (language === 'TypeScript' ? AppTs : AppJs).trim());
|
|
107
107
|
|
|
108
|
-
// Write .babelrc
|
|
109
108
|
fs.writeFileSync(path.join(targetPath, '.babelrc'), babelRc);
|
|
110
109
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
cwd: targetPath,
|
|
116
|
-
stdio: 'inherit'
|
|
110
|
+
execSync('npm init -y', { cwd: targetPath, stdio: 'inherit' });
|
|
111
|
+
execSync('npm install neutronium', { cwd: targetPath, stdio: 'inherit' });
|
|
112
|
+
execSync('npm install --save-dev @babel/core @babel/cli @babel/plugin-transform-react-jsx', {
|
|
113
|
+
cwd: targetPath, stdio: 'inherit'
|
|
117
114
|
});
|
|
118
115
|
|
|
119
|
-
|
|
120
|
-
|
|
116
|
+
if (language === 'TypeScript') {
|
|
117
|
+
execSync('npm install --save-dev typescript', { cwd: targetPath, stdio: 'inherit' });
|
|
118
|
+
fs.writeFileSync(path.join(targetPath, 'tsconfig.json'), `
|
|
119
|
+
{
|
|
120
|
+
"compilerOptions": {
|
|
121
|
+
"outDir": "build",
|
|
122
|
+
"rootDir": ".",
|
|
123
|
+
"target": "ES2020",
|
|
124
|
+
"module": "ESNext",
|
|
125
|
+
"lib": ["DOM", "ES2020"],
|
|
126
|
+
"jsx": "react",
|
|
127
|
+
"jsxFactory": "h",
|
|
128
|
+
"jsxFragmentFactory": "Fragment",
|
|
129
|
+
"moduleResolution": "Node",
|
|
130
|
+
"strict": true,
|
|
131
|
+
"allowJs": true,
|
|
132
|
+
"checkJs": false,
|
|
133
|
+
"noEmit": true,
|
|
134
|
+
"esModuleInterop": true,
|
|
135
|
+
"skipLibCheck": true,
|
|
136
|
+
"forceConsistentCasingInFileNames": true
|
|
137
|
+
},
|
|
138
|
+
"include": ["**/*"],
|
|
139
|
+
"exclude": ["dist", "node_modules"]
|
|
140
|
+
}
|
|
141
|
+
`.trim());
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const packageJsonPath = path.join(targetPath, 'package.json');
|
|
145
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
121
146
|
|
|
122
|
-
// Modify the "start" script
|
|
123
147
|
packageJson.scripts = packageJson.scripts || {};
|
|
124
|
-
packageJson.scripts.start =
|
|
125
|
-
|
|
126
|
-
|
|
148
|
+
packageJson.scripts.start = language === 'TypeScript'
|
|
149
|
+
? 'tsc && neu-cli start --watch'
|
|
150
|
+
: 'neu-cli start --watch';
|
|
151
|
+
|
|
152
|
+
packageJson.scripts.compile = 'neu-cli start';
|
|
153
|
+
packageJson.scripts.update = 'npm i neutronium@latest -g && npm i neutronium@latest';
|
|
154
|
+
|
|
155
|
+
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
127
156
|
|
|
128
|
-
//
|
|
129
|
-
await fs.writeFileSync('package.json', JSON.stringify(packageJson, null, 2));
|
|
130
|
-
// Transpile App.js (in-memory) for preview HTML
|
|
157
|
+
// Create preview HTML
|
|
131
158
|
const jsxWithImport = `import * as _neutronium from 'neutronium';\n\n${AppJs}`;
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
[
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
})
|
|
159
|
+
let compiledCode = '';
|
|
160
|
+
try {
|
|
161
|
+
compiledCode = transformSync(jsxWithImport, {
|
|
162
|
+
filename: 'App.js',
|
|
163
|
+
babelrc: false,
|
|
164
|
+
configFile: false,
|
|
165
|
+
presets: [],
|
|
166
|
+
plugins: [[
|
|
167
|
+
'@babel/plugin-transform-react-jsx',
|
|
168
|
+
{
|
|
169
|
+
pragma: '_neutronium.h',
|
|
170
|
+
pragmaFrag: '_neutronium.Fragment',
|
|
171
|
+
runtime: 'classic'
|
|
172
|
+
}
|
|
173
|
+
]]
|
|
174
|
+
}).code;
|
|
175
|
+
} catch (e) {
|
|
176
|
+
console.warn('ā ļø Babel preview transform failed:', e.message);
|
|
177
|
+
}
|
|
149
178
|
|
|
150
|
-
// Write dist/index.html with inlined JS
|
|
151
|
-
const finalHtml = htmlTemplate(appName, result.code);
|
|
152
179
|
const distPath = path.join(targetPath, 'dist');
|
|
153
180
|
if (!fs.existsSync(distPath)) fs.mkdirSync(distPath);
|
|
154
|
-
fs.writeFileSync(path.join(distPath, 'index.html'),
|
|
181
|
+
fs.writeFileSync(path.join(distPath, 'index.html'), htmlTemplate(appName, compiledCode));
|
|
155
182
|
|
|
156
|
-
// Print instructions to user
|
|
157
183
|
const folderCmd = createdFolder ? `cd ${path.basename(targetPath)}` : '';
|
|
158
184
|
console.log('\nā
Neutronium app is ready!');
|
|
159
|
-
console.log(`ā”ļø Run the following to get started:\n\n ${folderCmd}\n
|
|
160
|
-
|
|
185
|
+
console.log(`ā”ļø Run the following to get started:\n\n ${folderCmd}\n npm start\n`);
|
|
161
186
|
}
|
|
162
187
|
|
|
163
188
|
// --- CLI Command Routing ---
|
|
164
189
|
switch (command) {
|
|
165
190
|
case 'init':
|
|
166
191
|
case 'create-app':
|
|
167
|
-
case 'create-neu-app':
|
|
168
|
-
case 'create-new-app':
|
|
169
192
|
case 'create-neutronium-app':
|
|
170
193
|
init();
|
|
171
194
|
break;
|
|
172
195
|
|
|
173
196
|
case 'start':
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
compileProjectWatch();
|
|
177
|
-
} else {
|
|
178
|
-
// Build once without watching
|
|
179
|
-
compileProject();
|
|
180
|
-
}
|
|
197
|
+
if (args[0] === '--watch') compileProjectWatch();
|
|
198
|
+
else compileProject();
|
|
181
199
|
break;
|
|
200
|
+
|
|
182
201
|
case '--lang':
|
|
183
202
|
const lang = args[0];
|
|
203
|
+
const pkgPath = path.join(process.cwd(), 'package.json');
|
|
204
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
184
205
|
|
|
185
|
-
if (lang ===
|
|
206
|
+
if (lang === 'ts') {
|
|
186
207
|
fs.unlinkSync('App.js');
|
|
187
208
|
fs.writeFileSync('App.ts', AppTs);
|
|
188
209
|
fs.writeFileSync('tsconfig.json', `
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
packageJson.scripts = packageJson.scripts || {};
|
|
214
|
-
packageJson.scripts.start = 'tsc && ' + packageJson.scripts.start;
|
|
215
|
-
fs.writeFileSync('package.json', JSON.stringify(packageJson, null, 2));
|
|
210
|
+
{
|
|
211
|
+
"compilerOptions": {
|
|
212
|
+
"outDir": "build",
|
|
213
|
+
"rootDir": ".",
|
|
214
|
+
"target": "ES2020",
|
|
215
|
+
"module": "ESNext",
|
|
216
|
+
"lib": ["DOM", "ES2020"],
|
|
217
|
+
"jsx": "react",
|
|
218
|
+
"jsxFactory": "h",
|
|
219
|
+
"jsxFragmentFactory": "Fragment",
|
|
220
|
+
"moduleResolution": "Node",
|
|
221
|
+
"strict": true,
|
|
222
|
+
"allowJs": true,
|
|
223
|
+
"checkJs": false,
|
|
224
|
+
"noEmit": true,
|
|
225
|
+
"esModuleInterop": true,
|
|
226
|
+
"skipLibCheck": true,
|
|
227
|
+
"forceConsistentCasingInFileNames": true
|
|
228
|
+
},
|
|
229
|
+
"include": ["**/*"],
|
|
230
|
+
"exclude": ["dist", "node_modules"]
|
|
231
|
+
}
|
|
232
|
+
`.trim());
|
|
233
|
+
pkg.scripts.start = 'tsc && neu-cli start --watch';
|
|
216
234
|
}
|
|
217
235
|
|
|
218
|
-
if (lang ===
|
|
236
|
+
if (lang === 'js') {
|
|
219
237
|
fs.unlinkSync('App.ts');
|
|
220
|
-
fs.writeFileSync('App.js', AppJs);
|
|
221
|
-
if (fs.existsSync('tsconfig.json'))
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
|
|
225
|
-
if (packageJson.scripts && packageJson.scripts.start?.startsWith('tsc &&')) {
|
|
226
|
-
packageJson.scripts.start = packageJson.scripts.start.replace(/^tsc &&\s*/, '');
|
|
227
|
-
}
|
|
228
|
-
fs.writeFileSync('package.json', JSON.stringify(packageJson, null, 2));
|
|
238
|
+
fs.writeFileSync('App.js', AppJs);
|
|
239
|
+
if (fs.existsSync('tsconfig.json')) fs.unlinkSync('tsconfig.json');
|
|
240
|
+
pkg.scripts.start = pkg.scripts.start.replace(/^tsc &&\s*/, '');
|
|
229
241
|
}
|
|
242
|
+
|
|
243
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
|
|
230
244
|
break;
|
|
245
|
+
|
|
231
246
|
default:
|
|
232
|
-
// Help text
|
|
233
247
|
console.log('ā Unknown command.');
|
|
234
248
|
console.log(`
|
|
235
249
|
Available Commands:
|
|
236
|
-
|
|
237
250
|
init | create-app | create-neutronium-app
|
|
238
251
|
š Initialize a new Neutronium project
|
|
239
252
|
|
|
@@ -242,5 +255,8 @@ Available Commands:
|
|
|
242
255
|
|
|
243
256
|
start --watch
|
|
244
257
|
š Start dev server and rebuild on changes
|
|
258
|
+
|
|
259
|
+
--lang js | ts
|
|
260
|
+
š Switch between JS and TS
|
|
245
261
|
`);
|
|
246
262
|
}
|
package/compiler/compiler.js
CHANGED
|
@@ -1,262 +1,146 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const { default: inquirer } = require('inquirer');
|
|
4
|
-
const fs = require('fs');
|
|
1
|
+
const babel = require('@babel/core');
|
|
5
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');
|
|
6
11
|
const { execSync } = require('child_process');
|
|
7
|
-
const { transformSync } = require('@babel/core');
|
|
8
|
-
const { compileProject, compileProjectWatch } = require('../compiler/compiler');
|
|
9
|
-
|
|
10
|
-
const [, , command, ...args] = process.argv;
|
|
11
|
-
|
|
12
|
-
const babelRc = `{
|
|
13
|
-
"plugins": [
|
|
14
|
-
["@babel/plugin-transform-react-jsx", {
|
|
15
|
-
"pragma": "_neutronium.h",
|
|
16
|
-
"pragmaFrag": "_neutronium.Fragment",
|
|
17
|
-
"runtime": "classic",
|
|
18
|
-
"useBuiltIns": false,
|
|
19
|
-
"sourceMaps": true,
|
|
20
|
-
"comments": false,
|
|
21
|
-
"minified": true
|
|
22
|
-
}]
|
|
23
|
-
]
|
|
24
|
-
}`;
|
|
25
|
-
|
|
26
|
-
const AppTs = `
|
|
27
|
-
import { createApp } from 'neutronium';
|
|
28
|
-
|
|
29
|
-
function App() {
|
|
30
|
-
return (
|
|
31
|
-
<h1>Hello World (TypeScript)</h1>
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
createApp(App).mount("body");
|
|
36
|
-
`;
|
|
37
|
-
|
|
38
|
-
const AppJs = `
|
|
39
|
-
import { createApp } from 'neutronium';
|
|
40
12
|
|
|
41
|
-
function
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
createApp(App).mount("body");
|
|
48
|
-
`;
|
|
49
|
-
|
|
50
|
-
const htmlTemplate = (title, jsCode) => `
|
|
51
|
-
<!DOCTYPE html>
|
|
52
|
-
<html>
|
|
53
|
-
<head>
|
|
54
|
-
<meta charset="UTF-8" />
|
|
55
|
-
<title>${title}</title>
|
|
56
|
-
</head>
|
|
57
|
-
<body>
|
|
58
|
-
<script type="module">
|
|
59
|
-
${jsCode}
|
|
60
|
-
</script>
|
|
61
|
-
</body>
|
|
62
|
-
</html>
|
|
63
|
-
`.trim();
|
|
64
|
-
|
|
65
|
-
async function init() {
|
|
66
|
-
let targetPath = process.cwd();
|
|
67
|
-
let createdFolder = false;
|
|
68
|
-
|
|
69
|
-
const { confirmInit } = await inquirer.prompt([
|
|
70
|
-
{
|
|
71
|
-
type: 'confirm',
|
|
72
|
-
name: 'confirmInit',
|
|
73
|
-
message: 'Initialize a Neutronium app in this folder?',
|
|
74
|
-
default: true
|
|
75
|
-
}
|
|
76
|
-
]);
|
|
77
|
-
|
|
78
|
-
let appName = 'neutronium-app';
|
|
13
|
+
async function compileProject(projectDir = process.cwd()) {
|
|
14
|
+
const distDir = path.join(projectDir, 'dist');
|
|
15
|
+
const neutroniumPath = '../node_modules/neutronium/src/index.js';
|
|
16
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(projectDir, 'package.json')));
|
|
17
|
+
const entry = packageJson.main || 'App.js';
|
|
79
18
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
19
|
+
try {
|
|
20
|
+
log('š Scanning project files...');
|
|
21
|
+
const allFiles = fs.readdirSync(projectDir);
|
|
22
|
+
const jsFiles = allFiles.filter(f => f.endsWith('.js') && f !== 'compiler.js' && !f.startsWith('.'));
|
|
23
|
+
|
|
24
|
+
if (fs.existsSync(path.join(projectDir, 'tsconfig.json'))) {
|
|
25
|
+
try {
|
|
26
|
+
require.resolve('typescript');
|
|
27
|
+
} catch {
|
|
28
|
+
log('š¦ Installing TypeScript...');
|
|
29
|
+
execSync('npm install typescript@latest', { stdio: 'inherit' });
|
|
87
30
|
}
|
|
88
|
-
]);
|
|
89
|
-
appName = projectName;
|
|
90
|
-
targetPath = path.resolve(process.cwd(), projectName);
|
|
91
|
-
createdFolder = true;
|
|
92
|
-
if (!fs.existsSync(targetPath)) fs.mkdirSync(targetPath);
|
|
93
|
-
}
|
|
94
31
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
{
|
|
98
|
-
type: 'list',
|
|
99
|
-
name: 'language',
|
|
100
|
-
message: 'Choose your language:',
|
|
101
|
-
choices: ['JavaScript', 'TypeScript']
|
|
32
|
+
log('š ļø Compiling TypeScript...');
|
|
33
|
+
execSync('npx tsc', { stdio: 'inherit' });
|
|
102
34
|
}
|
|
103
|
-
]);
|
|
104
|
-
|
|
105
|
-
const appFileName = language === 'TypeScript' ? 'App.ts' : 'App.js';
|
|
106
|
-
fs.writeFileSync(path.join(targetPath, appFileName), (language === 'TypeScript' ? AppTs : AppJs).trim());
|
|
107
35
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
execSync('npm install neutronium', { cwd: targetPath, stdio: 'inherit' });
|
|
112
|
-
execSync('npm install --save-dev @babel/core @babel/cli @babel/plugin-transform-react-jsx', {
|
|
113
|
-
cwd: targetPath, stdio: 'inherit'
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
if (language === 'TypeScript') {
|
|
117
|
-
execSync('npm install --save-dev typescript', { cwd: targetPath, stdio: 'inherit' });
|
|
118
|
-
fs.writeFileSync(path.join(targetPath, 'tsconfig.json'), `
|
|
119
|
-
{
|
|
120
|
-
"compilerOptions": {
|
|
121
|
-
"outDir": "build",
|
|
122
|
-
"rootDir": ".",
|
|
123
|
-
"target": "ES2020",
|
|
124
|
-
"module": "ESNext",
|
|
125
|
-
"lib": ["DOM", "ES2020"],
|
|
126
|
-
"jsx": "react",
|
|
127
|
-
"jsxFactory": "h",
|
|
128
|
-
"jsxFragmentFactory": "Fragment",
|
|
129
|
-
"moduleResolution": "Node",
|
|
130
|
-
"strict": true,
|
|
131
|
-
"allowJs": true,
|
|
132
|
-
"checkJs": false,
|
|
133
|
-
"noEmit": true,
|
|
134
|
-
"esModuleInterop": true,
|
|
135
|
-
"skipLibCheck": true,
|
|
136
|
-
"forceConsistentCasingInFileNames": true
|
|
137
|
-
},
|
|
138
|
-
"include": ["**/*"],
|
|
139
|
-
"exclude": ["dist", "node_modules"]
|
|
140
|
-
}
|
|
141
|
-
`.trim());
|
|
142
|
-
}
|
|
36
|
+
if (!jsFiles.includes(entry)) {
|
|
37
|
+
throw new Error(`ā Entry file "${entry}" not found!`);
|
|
38
|
+
}
|
|
143
39
|
|
|
144
|
-
|
|
145
|
-
|
|
40
|
+
ensureDir(distDir);
|
|
41
|
+
|
|
42
|
+
for (const file of jsFiles) {
|
|
43
|
+
const inputPath = path.join(projectDir, file);
|
|
44
|
+
const outputPath = path.join(distDir, file);
|
|
45
|
+
|
|
46
|
+
log(`āļø Babel transforming ${file}...`);
|
|
47
|
+
const source = fs.readFileSync(inputPath, 'utf-8');
|
|
48
|
+
|
|
49
|
+
let { code } = babel.transformSync(source, {
|
|
50
|
+
filename: file,
|
|
51
|
+
babelrc: false,
|
|
52
|
+
configFile: false,
|
|
53
|
+
presets: [],
|
|
54
|
+
plugins: [[
|
|
55
|
+
'@babel/plugin-transform-react-jsx',
|
|
56
|
+
{
|
|
57
|
+
pragma: '_neutronium.h',
|
|
58
|
+
pragmaFrag: '_neutronium.Fragment',
|
|
59
|
+
runtime: 'classic',
|
|
60
|
+
}
|
|
61
|
+
]]
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
if (!source.includes('_neutronium')) {
|
|
65
|
+
code = `import * as _neutronium from '${neutroniumPath}';\n\n${code}`;
|
|
66
|
+
}
|
|
146
67
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
? 'tsc && neu-cli start --watch'
|
|
150
|
-
: 'neu-cli start --watch';
|
|
68
|
+
code = code.replace(/from\s+['"]neutronium['"]/g, `from '${neutroniumPath}'`);
|
|
69
|
+
code = code.replace("_neutronium.createApp(App).mount('#app');")
|
|
151
70
|
|
|
152
|
-
|
|
153
|
-
|
|
71
|
+
writeFile(outputPath, code);
|
|
72
|
+
}
|
|
154
73
|
|
|
155
|
-
|
|
74
|
+
log('š ļø Generating index.html...');
|
|
75
|
+
const htmlContent = baseHtml(``, entry);
|
|
76
|
+
writeFile(path.join(distDir, 'index.html'), htmlContent);
|
|
156
77
|
|
|
157
|
-
|
|
158
|
-
const jsxWithImport = `import * as _neutronium from 'neutronium';\n\n${AppJs}`;
|
|
159
|
-
let compiledCode = '';
|
|
160
|
-
try {
|
|
161
|
-
compiledCode = transformSync(jsxWithImport, {
|
|
162
|
-
filename: 'App.js',
|
|
163
|
-
babelrc: false,
|
|
164
|
-
configFile: false,
|
|
165
|
-
presets: [],
|
|
166
|
-
plugins: [[
|
|
167
|
-
'@babel/plugin-transform-react-jsx',
|
|
168
|
-
{
|
|
169
|
-
pragma: '_neutronium.h',
|
|
170
|
-
pragmaFrag: '_neutronium.Fragment',
|
|
171
|
-
runtime: 'classic'
|
|
172
|
-
}
|
|
173
|
-
]]
|
|
174
|
-
}).code;
|
|
78
|
+
log('ā
Compilation complete!');
|
|
175
79
|
} catch (e) {
|
|
176
|
-
console.
|
|
80
|
+
console.error('ā Compilation failed:', e.message);
|
|
177
81
|
}
|
|
178
|
-
|
|
179
|
-
const distPath = path.join(targetPath, 'dist');
|
|
180
|
-
if (!fs.existsSync(distPath)) fs.mkdirSync(distPath);
|
|
181
|
-
fs.writeFileSync(path.join(distPath, 'index.html'), htmlTemplate(appName, compiledCode));
|
|
182
|
-
|
|
183
|
-
const folderCmd = createdFolder ? `cd ${path.basename(targetPath)}` : '';
|
|
184
|
-
console.log('\nā
Neutronium app is ready!');
|
|
185
|
-
console.log(`ā”ļø Run the following to get started:\n\n ${folderCmd}\n npm start\n`);
|
|
186
82
|
}
|
|
187
83
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
84
|
+
function compileProjectWatch(projectDir = process.cwd(), port = 3000) {
|
|
85
|
+
const server = serveProject(projectDir, port);
|
|
86
|
+
compileProject(projectDir);
|
|
87
|
+
|
|
88
|
+
log('š Watching project for changes...');
|
|
89
|
+
let timeout;
|
|
90
|
+
|
|
91
|
+
chokidar.watch([
|
|
92
|
+
path.join(projectDir, '**/*.js'),
|
|
93
|
+
path.join(projectDir, '**/*.ts'),
|
|
94
|
+
path.join(projectDir, '**/*.tsx'),
|
|
95
|
+
]).on('change', () => {
|
|
96
|
+
clearTimeout(timeout);
|
|
97
|
+
timeout = setTimeout(() => {
|
|
98
|
+
console.clear();
|
|
99
|
+
log('š File changed, rebuilding...');
|
|
100
|
+
try {
|
|
101
|
+
compileProject(projectDir);
|
|
102
|
+
if (server.broadcastReload) server.broadcastReload();
|
|
103
|
+
} catch (err) {
|
|
104
|
+
console.error('ā Rebuild failed:', err.message);
|
|
105
|
+
}
|
|
106
|
+
}, 100);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
200
109
|
|
|
201
|
-
|
|
202
|
-
const lang = args[0];
|
|
203
|
-
const pkgPath = path.join(process.cwd(), 'package.json');
|
|
204
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
110
|
+
function serveProject(projectDir = process.cwd(), port = 3000) {
|
|
205
111
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
{
|
|
211
|
-
"compilerOptions": {
|
|
212
|
-
"outDir": "build",
|
|
213
|
-
"rootDir": ".",
|
|
214
|
-
"target": "ES2020",
|
|
215
|
-
"module": "ESNext",
|
|
216
|
-
"lib": ["DOM", "ES2020"],
|
|
217
|
-
"jsx": "react",
|
|
218
|
-
"jsxFactory": "h",
|
|
219
|
-
"jsxFragmentFactory": "Fragment",
|
|
220
|
-
"moduleResolution": "Node",
|
|
221
|
-
"strict": true,
|
|
222
|
-
"allowJs": true,
|
|
223
|
-
"checkJs": false,
|
|
224
|
-
"noEmit": true,
|
|
225
|
-
"esModuleInterop": true,
|
|
226
|
-
"skipLibCheck": true,
|
|
227
|
-
"forceConsistentCasingInFileNames": true
|
|
228
|
-
},
|
|
229
|
-
"include": ["**/*"],
|
|
230
|
-
"exclude": ["dist", "node_modules"]
|
|
231
|
-
}
|
|
232
|
-
`.trim());
|
|
233
|
-
pkg.scripts.start = 'tsc && neu-cli start --watch';
|
|
112
|
+
const server = http.createServer((req, res) => {
|
|
113
|
+
let reqPath = req.url;
|
|
114
|
+
if (reqPath === '/' || reqPath === '/index.html') {
|
|
115
|
+
reqPath = '/dist/index.html';
|
|
234
116
|
}
|
|
235
117
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
pkg.scripts.start = pkg.scripts.start.replace(/^tsc &&\s*/, '');
|
|
118
|
+
const filePath = path.join(projectDir, reqPath);
|
|
119
|
+
if (!fs.existsSync(filePath)) {
|
|
120
|
+
res.writeHead(404);
|
|
121
|
+
return res.end('404 Not Found');
|
|
241
122
|
}
|
|
242
123
|
|
|
243
|
-
fs.
|
|
244
|
-
|
|
124
|
+
const content = fs.readFileSync(filePath);
|
|
125
|
+
res.writeHead(200, { 'Content-Type': Mime.getType(filePath) });
|
|
126
|
+
res.end(content);
|
|
127
|
+
});
|
|
245
128
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
129
|
+
const wss = new WebSocket.Server({ server });
|
|
130
|
+
server.broadcastReload = () => {
|
|
131
|
+
wss.clients.forEach(client => {
|
|
132
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
133
|
+
client.send('reload');
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
};
|
|
252
137
|
|
|
253
|
-
|
|
254
|
-
|
|
138
|
+
server.listen(port, () => {
|
|
139
|
+
log(`š Server running at http://localhost:${port}`);
|
|
140
|
+
open(`http://localhost:${port}/dist/index.html`);
|
|
141
|
+
});
|
|
255
142
|
|
|
256
|
-
|
|
257
|
-
|
|
143
|
+
return server;
|
|
144
|
+
}
|
|
258
145
|
|
|
259
|
-
|
|
260
|
-
š Switch between JS and TS
|
|
261
|
-
`);
|
|
262
|
-
}
|
|
146
|
+
module.exports = { compileProject, compileProjectWatch };
|
package/package.json
CHANGED
package/results.png
ADDED
|
Binary file
|