create-yeow 0.2.20 → 0.2.22
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/package.json +1 -1
- package/templates/default/.yeow/assets/yeow-runtime-0.1.0.jar +0 -0
- package/templates/default/.yeow/assets/yeow-template-0.1.0.jar +0 -0
- package/templates/default/.yeow/build.js +74 -34
- package/templates/default/.yeow/dev-server.js +144 -16
- package/templates/default/package.json +3 -1
- package/templates/default/yeow.config.json +2 -0
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -12,40 +12,80 @@ const entry = existsSync(resolve(root, 'src', 'index.ts')) ? 'src/index.ts' : 's
|
|
|
12
12
|
const outDir = resolve(root, 'dist', '.yeow');
|
|
13
13
|
mkdirSync(outDir, { recursive: true });
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
15
|
+
const isDev = process.env.YEOW_DEV === 'true';
|
|
16
|
+
|
|
17
|
+
if (isDev) {
|
|
18
|
+
// Dev mode: bundle code into .yeow/main.js, write dev.json with paths
|
|
19
|
+
esbuild.build({
|
|
20
|
+
entryPoints: [resolve(root, entry)],
|
|
21
|
+
outfile: resolve(outDir, 'main.js'),
|
|
22
|
+
bundle: true,
|
|
23
|
+
format: 'iife',
|
|
24
|
+
target: 'es2023',
|
|
25
|
+
platform: 'neutral',
|
|
26
|
+
mainFields: ['module', 'main'],
|
|
27
|
+
conditions: ['import', 'browser'],
|
|
28
|
+
treeShaking: true,
|
|
29
|
+
minify: false,
|
|
30
|
+
sourcemap: 'linked',
|
|
31
|
+
}).then(() => {
|
|
32
|
+
const size = statSync(resolve(outDir, 'main.js')).size;
|
|
33
|
+
console.log(` ✓ Bundled (${(size / 1024).toFixed(1)} KB)`);
|
|
34
|
+
|
|
35
|
+
const devInfo = {
|
|
36
|
+
name,
|
|
37
|
+
codeFile: resolve(outDir, 'main.js').replace(/\\/g, '/'),
|
|
38
|
+
assetsDir: existsSync(resolve(root, 'assets')) ? resolve(root, 'assets').replace(/\\/g, '/') : null,
|
|
39
|
+
};
|
|
40
|
+
writeFileSync(resolve(outDir, 'dev.json'), JSON.stringify(devInfo, null, 2));
|
|
41
|
+
console.log(` ✓ Dev info written`);
|
|
42
|
+
|
|
43
|
+
const apiVer = cfg.api || '1.18';
|
|
44
|
+
const tmplJar = resolve(root, '.yeow', 'assets', 'yeow-template-0.1.0.jar');
|
|
45
|
+
const zip = new AdmZip(tmplJar);
|
|
46
|
+
zip.updateFile('plugin.yml', Buffer.from(`name: ${name}\nversion: ${version}\nmain: yeow.template.Bootstrap\napi-version: '${apiVer}'\ndepend:\n - Yeow-Runtime\n`));
|
|
47
|
+
zip.addFile('.yeow/dev.json', readFileSync(resolve(outDir, 'dev.json')));
|
|
48
|
+
zip.addFile('yeow.json', Buffer.from(JSON.stringify(cfg)));
|
|
49
|
+
const outJar = resolve(root, 'dist', `${name}-${version}.jar`);
|
|
50
|
+
zip.writeZip(outJar);
|
|
51
|
+
console.log(` ✓ Dev JAR packaged ${outJar}\n`);
|
|
52
|
+
}).catch(e => { console.error(e); process.exit(1); }); else {
|
|
53
|
+
// Production mode: bundle code into JAR
|
|
54
|
+
esbuild.build({
|
|
55
|
+
entryPoints: [resolve(root, entry)],
|
|
56
|
+
outfile: resolve(outDir, 'main.js'),
|
|
57
|
+
bundle: true,
|
|
58
|
+
format: 'iife',
|
|
59
|
+
target: 'es2023',
|
|
60
|
+
platform: 'neutral',
|
|
61
|
+
mainFields: ['module', 'main'],
|
|
62
|
+
conditions: ['import', 'browser'],
|
|
63
|
+
treeShaking: true,
|
|
64
|
+
minify: false,
|
|
65
|
+
}).then(() => {
|
|
66
|
+
const size = statSync(resolve(outDir, 'main.js')).size;
|
|
67
|
+
console.log(` ✓ Bundled (${(size / 1024).toFixed(1)} KB)`);
|
|
68
|
+
|
|
69
|
+
const apiVer = cfg.api || '1.18';
|
|
70
|
+
const tmplJar = resolve(root, '.yeow', 'assets', 'yeow-template-0.1.0.jar');
|
|
71
|
+
const zip = new AdmZip(tmplJar);
|
|
72
|
+
zip.updateFile('plugin.yml', Buffer.from(`name: ${name}\nversion: ${version}\nmain: yeow.template.Bootstrap\napi-version: '${apiVer}'\ndepend:\n - Yeow-Runtime\n`));
|
|
73
|
+
zip.addFile('.yeow/main.js', readFileSync(resolve(outDir, 'main.js')));
|
|
74
|
+
zip.addFile('yeow.json', Buffer.from(JSON.stringify(cfg)));
|
|
75
|
+
|
|
76
|
+
const assetsDir = resolve(root, 'assets');
|
|
77
|
+
if (existsSync(assetsDir)) {
|
|
78
|
+
for (const f of readdirSync(assetsDir, { recursive: true })) {
|
|
79
|
+
const fp = resolve(assetsDir, f);
|
|
80
|
+
if (statSync(fp).isFile()) {
|
|
81
|
+
zip.addFile('assets/' + f, readFileSync(fp));
|
|
82
|
+
}
|
|
43
83
|
}
|
|
84
|
+
console.log(' ✓ Assets included');
|
|
44
85
|
}
|
|
45
|
-
console.log(' ✓ Assets included');
|
|
46
|
-
}
|
|
47
86
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}).catch(e => { console.error(e); process.exit(1); });
|
|
87
|
+
const outJar = resolve(root, 'dist', `${name}-${version}.jar`);
|
|
88
|
+
zip.writeZip(outJar);
|
|
89
|
+
console.log(` ✓ Packaged ${outJar}\n`);
|
|
90
|
+
}).catch(e => { console.error(e); process.exit(1); });
|
|
91
|
+
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, copyFileSync, writeFileSync, readFileSync, createWriteStream, statSync, watch } from 'fs';
|
|
2
|
-
import { resolve, dirname
|
|
1
|
+
import { existsSync, mkdirSync, copyFileSync, writeFileSync, readFileSync, createWriteStream, statSync, watch, readdirSync } from 'fs';
|
|
2
|
+
import { resolve, dirname } from 'path';
|
|
3
3
|
import { spawn, execSync } from 'child_process';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
5
|
import https from 'https';
|
|
6
|
+
import { createServer } from 'http';
|
|
7
|
+
import { WebSocketServer } from 'ws';
|
|
8
|
+
import { SourceMapConsumer } from 'source-map';
|
|
6
9
|
|
|
7
10
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
11
|
const ROOT = resolve(__dirname, '..');
|
|
@@ -14,6 +17,7 @@ const BUILD = '232';
|
|
|
14
17
|
const JAR = `paper-${PAPER}-${BUILD}.jar`;
|
|
15
18
|
const URL = `https://fill-data.papermc.io/v1/objects/5ee4f542f628a14c644410b08c94ea42e772ef4d29fe92973636b6813d4eaffc/paper-1.21.4-232.jar`;
|
|
16
19
|
const CACHEJAR = resolve(CACHE, JAR);
|
|
20
|
+
const WS_PORT = 17368;
|
|
17
21
|
|
|
18
22
|
const YES = process.argv.includes('-y') || process.env.CI === 'true';
|
|
19
23
|
const PROXY = process.argv.find(a => a.startsWith('--proxy='))?.split('=').slice(1).join('=');
|
|
@@ -30,7 +34,38 @@ const info = msg => log(`${c.info} ${msg}`, c.C);
|
|
|
30
34
|
const warn = msg => log(`${c.warn} ${msg}`, c.y);
|
|
31
35
|
|
|
32
36
|
let proc = null;
|
|
37
|
+
let wss = null;
|
|
33
38
|
|
|
39
|
+
// ── WebSocket Server ────────────────────────────────────────────
|
|
40
|
+
function startWebSocket() {
|
|
41
|
+
const server = createServer();
|
|
42
|
+
wss = new WebSocketServer({ server });
|
|
43
|
+
wss.on('connection', (ws) => {
|
|
44
|
+
info('Java runtime connected');
|
|
45
|
+
ws.on('message', async (data) => {
|
|
46
|
+
try {
|
|
47
|
+
const msg = JSON.parse(data.toString());
|
|
48
|
+
if (msg.type === 'js-error') {
|
|
49
|
+
await printFormattedError(msg);
|
|
50
|
+
}
|
|
51
|
+
} catch (e) { /* ignore parse errors */ }
|
|
52
|
+
});
|
|
53
|
+
ws.on('close', () => info('Java runtime disconnected'));
|
|
54
|
+
});
|
|
55
|
+
server.listen(WS_PORT, () => {
|
|
56
|
+
info(`WebSocket server on port ${WS_PORT}`);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function broadcast(msg) {
|
|
61
|
+
if (!wss) return;
|
|
62
|
+
const data = JSON.stringify(msg);
|
|
63
|
+
wss.clients.forEach((ws) => {
|
|
64
|
+
if (ws.readyState === 1) ws.send(data);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ── Download ────────────────────────────────────────────────────
|
|
34
69
|
function download(url, dest, agent) {
|
|
35
70
|
return new Promise((resolve, reject) => {
|
|
36
71
|
const f = createWriteStream(dest);
|
|
@@ -59,7 +94,7 @@ async function ensurePaper() {
|
|
|
59
94
|
|
|
60
95
|
function buildPlugin() {
|
|
61
96
|
info('Building plugin...');
|
|
62
|
-
try { execSync('node .yeow/build.js', { cwd: ROOT, stdio: 'inherit' }); ok('Plugin built'); }
|
|
97
|
+
try { execSync('node .yeow/build.js', { cwd: ROOT, stdio: 'inherit', env: { ...process.env, YEOW_DEV: 'true' } }); ok('Plugin built'); }
|
|
63
98
|
catch (e) { fail('Build failed: ' + e.message); process.exit(1); }
|
|
64
99
|
}
|
|
65
100
|
|
|
@@ -96,39 +131,131 @@ function serverProps(port) {
|
|
|
96
131
|
writeFileSync(f, out);
|
|
97
132
|
}
|
|
98
133
|
|
|
134
|
+
// ── Hot Reload via WebSocket ────────────────────────────────────
|
|
99
135
|
function startHotReload() {
|
|
100
136
|
const srcDir = resolve(ROOT, 'src');
|
|
137
|
+
const assetsDir = resolve(ROOT, 'assets');
|
|
101
138
|
if (!existsSync(srcDir)) return;
|
|
102
|
-
|
|
103
|
-
mkdirSync(devDir, { recursive: true });
|
|
139
|
+
|
|
104
140
|
let timer = null;
|
|
105
141
|
let building = false;
|
|
106
142
|
const ext = existsSync(resolve(srcDir, 'index.ts')) ? 'ts' : 'js';
|
|
107
|
-
|
|
108
|
-
|
|
143
|
+
|
|
144
|
+
const rebuildAndNotify = () => {
|
|
109
145
|
if (timer) clearTimeout(timer);
|
|
110
146
|
timer = setTimeout(() => {
|
|
147
|
+
if (building) return;
|
|
111
148
|
building = true;
|
|
112
149
|
info('Source changed, rebuilding...');
|
|
113
150
|
try {
|
|
114
|
-
execSync('node .yeow/build.js', { cwd: ROOT, stdio: 'pipe' });
|
|
151
|
+
execSync('node .yeow/build.js', { cwd: ROOT, stdio: 'pipe', env: { ...process.env, YEOW_DEV: 'true' } });
|
|
115
152
|
const compiled = resolve(ROOT, 'dist', '.yeow', 'main.js');
|
|
116
153
|
if (existsSync(compiled)) {
|
|
117
|
-
|
|
118
|
-
|
|
154
|
+
broadcast({
|
|
155
|
+
type: 'hot-reload',
|
|
156
|
+
plugin: cfg.name,
|
|
157
|
+
codeFile: compiled.replace(/\\/g, '/'),
|
|
158
|
+
assetsDir: existsSync(assetsDir) ? assetsDir.replace(/\\/g, '/') : null,
|
|
159
|
+
});
|
|
160
|
+
ok('Hot reload sent via WebSocket');
|
|
119
161
|
}
|
|
120
|
-
} catch (e) {
|
|
162
|
+
} catch (e) {
|
|
163
|
+
fail('Build failed: ' + e.message);
|
|
164
|
+
broadcast({ type: 'build-error', plugin: cfg.name, error: e.message });
|
|
165
|
+
}
|
|
121
166
|
building = false;
|
|
122
|
-
},
|
|
167
|
+
}, 300);
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
// Watch src/ for changes
|
|
171
|
+
watch(srcDir, { recursive: true }, (event, file) => {
|
|
172
|
+
if (!file || !file.endsWith('.' + ext)) return;
|
|
173
|
+
rebuildAndNotify();
|
|
123
174
|
});
|
|
124
|
-
|
|
175
|
+
|
|
176
|
+
// Watch assets/ for changes
|
|
177
|
+
if (existsSync(assetsDir)) {
|
|
178
|
+
watch(assetsDir, { recursive: true }, (event, file) => {
|
|
179
|
+
rebuildAndNotify();
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
info(`Watching src/ + assets/ for changes (WebSocket hot reload)`);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ── Source-Mapped Error Display ─────────────────────────────────
|
|
187
|
+
let _consumer = null;
|
|
188
|
+
async function getSourceMapConsumer() {
|
|
189
|
+
if (_consumer) return _consumer;
|
|
190
|
+
const mapFile = resolve(ROOT, 'dist', '.yeow', 'main.js.map');
|
|
191
|
+
if (!existsSync(mapFile)) return null;
|
|
192
|
+
try {
|
|
193
|
+
const raw = JSON.parse(readFileSync(mapFile, 'utf-8'));
|
|
194
|
+
_consumer = await new SourceMapConsumer(raw);
|
|
195
|
+
return _consumer;
|
|
196
|
+
} catch { return null; }
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async function printFormattedError(err) {
|
|
200
|
+
const c = { r: '\x1b[0m', R: '\x1b[31m', Y: '\x1b[33m', C: '\x1b[36m', B: '\x1b[1m', D: '\x1b[2m' };
|
|
201
|
+
console.log(`\n${c.R}${c.B} ⛔ JS Error [${err.plugin}]${c.r}`);
|
|
202
|
+
console.log(` ${c.Y}${err.message}${c.r}`);
|
|
203
|
+
console.log(` ${c.D}at ${err.fileName}:${err.lineNumber}:${err.columnNumber}${c.r}`);
|
|
204
|
+
|
|
205
|
+
// Try source-map resolution
|
|
206
|
+
if (err.fileName === 'main.js' || err.stack) {
|
|
207
|
+
const consumer = await getSourceMapConsumer();
|
|
208
|
+
if (consumer) {
|
|
209
|
+
if (err.lineNumber > 0) {
|
|
210
|
+
const orig = consumer.originalPositionFor({ line: err.lineNumber, column: err.columnNumber || 0 });
|
|
211
|
+
if (orig.source && orig.line) {
|
|
212
|
+
console.log(` ${c.C}→ ${orig.source}:${orig.line}:${orig.column}${c.r}`);
|
|
213
|
+
// Print source context
|
|
214
|
+
const sources = consumer.sources;
|
|
215
|
+
const idx = sources.indexOf(orig.source);
|
|
216
|
+
if (idx >= 0) {
|
|
217
|
+
const content = consumer.sourceContentFor(orig.source);
|
|
218
|
+
if (content) {
|
|
219
|
+
const lines = content.split('\n');
|
|
220
|
+
const start = Math.max(0, orig.line - 3);
|
|
221
|
+
const end = Math.min(lines.length, orig.line + 2);
|
|
222
|
+
for (let i = start; i < end; i++) {
|
|
223
|
+
const prefix = i === orig.line - 1 ? c.R + ' →' : ' ';
|
|
224
|
+
console.log(` ${prefix} ${c.D}${String(i + 1).padStart(4)}|${c.r} ${lines[i]}`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// Also resolve stack trace lines if present
|
|
231
|
+
if (err.stack) {
|
|
232
|
+
const stackLines = err.stack.split('\n');
|
|
233
|
+
for (let i = 0; i < Math.min(stackLines.length, 8); i++) {
|
|
234
|
+
const line = stackLines[i].trim();
|
|
235
|
+
const m = line.match(/at\s+(?:\S+\s+)?\(?(?:\/main\.js|[^:]+):(\d+):(\d+)\)?/);
|
|
236
|
+
if (m) {
|
|
237
|
+
const orig = consumer.originalPositionFor({ line: parseInt(m[1]), column: parseInt(m[2]) });
|
|
238
|
+
if (orig.source) {
|
|
239
|
+
const mapped = ` at ${orig.source}:${orig.line}:${orig.column}`;
|
|
240
|
+
console.log(` ${c.D}${mapped}${c.r}`);
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
console.log(` ${c.D} ${line}${c.r}`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
} else if (err.stack) {
|
|
248
|
+
err.stack.split('\n').slice(0, 5).forEach(l => console.log(` ${c.D} ${l.trim()}${c.r}`));
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
console.log();
|
|
125
252
|
}
|
|
126
253
|
|
|
127
254
|
function startServer() {
|
|
128
|
-
const jvmArgs = ['-Xmx4G', '-Xms4G', '-Dyeow.dev=true'];
|
|
255
|
+
const jvmArgs = ['-Xmx4G', '-Xms4G', '-Dyeow.dev=true', '-Dyeow.ws.port=' + WS_PORT];
|
|
129
256
|
info(`\nStarting Paper ${PAPER} server...`);
|
|
130
257
|
proc = spawn('java', [...jvmArgs, '-jar', resolve(SERVER, JAR), '--nogui'], { cwd: SERVER, stdio: ['pipe', 'inherit', 'inherit'] });
|
|
131
|
-
proc.on('exit', code => { warn(`Server exited (${code})`); process.exit(0); });
|
|
258
|
+
proc.on('exit', code => { warn(`Server exited (${code})`); if (wss) wss.close(); process.exit(0); });
|
|
132
259
|
process.stdin.on('data', d => { if (proc && !proc.killed) proc.stdin.write(d); });
|
|
133
260
|
if (STOP) { info(`Auto-stop in ${STOP}s`); setTimeout(() => { warn('Auto-stop'); if (proc && !proc.killed) proc.stdin.write('stop\n'); setTimeout(() => { if (proc && !proc.killed) proc.kill(); process.exit(0); }, 10000); }, STOP * 1000); }
|
|
134
261
|
}
|
|
@@ -137,6 +264,7 @@ async function main() {
|
|
|
137
264
|
console.log(`\n${c.b}${c.B} Yeow Dev Server${c.r}\n`);
|
|
138
265
|
if (!existsSync(RUNTIME)) { fail(`Runtime JAR not found: ${RUNTIME}`); process.exit(1); }
|
|
139
266
|
|
|
267
|
+
startWebSocket();
|
|
140
268
|
await ensurePaper();
|
|
141
269
|
mkdirSync(SERVER, { recursive: true });
|
|
142
270
|
await initServer();
|
|
@@ -149,5 +277,5 @@ async function main() {
|
|
|
149
277
|
startServer();
|
|
150
278
|
}
|
|
151
279
|
|
|
152
|
-
process.on('SIGINT', () => { if (proc) { proc.stdin.write('stop\n'); setTimeout(() => { if (proc && !proc.killed) proc.kill(); process.exit(0); }, 5000); } });
|
|
280
|
+
process.on('SIGINT', () => { if (proc) { proc.stdin.write('stop\n'); setTimeout(() => { if (proc && !proc.killed) proc.kill(); if (wss) wss.close(); process.exit(0); }, 5000); } });
|
|
153
281
|
main().catch(e => { fail(e.message); process.exit(1); });
|