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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-yeow",
3
- "version": "0.2.20",
3
+ "version": "0.2.22",
4
4
  "description": "Scaffold a Yeow plugin project",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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
- esbuild.build({
16
- entryPoints: [resolve(root, entry)],
17
- outfile: resolve(outDir, 'main.js'),
18
- bundle: true,
19
- format: 'iife',
20
- target: 'es2023',
21
- platform: 'neutral',
22
- mainFields: ['module', 'main'],
23
- conditions: ['import', 'browser'],
24
- treeShaking: true,
25
- minify: false,
26
- }).then(() => {
27
- const size = statSync(resolve(outDir, 'main.js')).size;
28
- console.log(` ✓ Bundled (${(size / 1024).toFixed(1)} KB)`);
29
-
30
- const tmplJar = resolve(root, '.yeow', 'assets', 'yeow-template-0.1.0.jar');
31
- const zip = new AdmZip(tmplJar);
32
- zip.updateFile('plugin.yml', Buffer.from(`name: ${name}\nversion: ${version}\nmain: yeow.template.Bootstrap\napi-version: '1.21'\ndepend:\n - Yeow-Runtime\n`));
33
- zip.addFile('.yeow/main.js', readFileSync(resolve(outDir, 'main.js')));
34
- zip.addFile('yeow.json', Buffer.from(JSON.stringify(cfg)));
35
-
36
- // Include assets/ directory
37
- const assetsDir = resolve(root, 'assets');
38
- if (existsSync(assetsDir)) {
39
- for (const f of readdirSync(assetsDir, { recursive: true })) {
40
- const fp = resolve(assetsDir, f);
41
- if (statSync(fp).isFile()) {
42
- zip.addFile('assets/' + f, readFileSync(fp));
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
- const outJar = resolve(root, 'dist', `${name}-${version}.jar`);
49
- zip.writeZip(outJar);
50
- console.log(` ✓ Packaged ${outJar}\n`);
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, join, basename } from 'path';
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
- const devDir = resolve(SERVER, 'plugins', 'Yeow-Dev', cfg.name);
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
- watch(srcDir, { recursive: true }, (event, file) => {
108
- if (!file || !file.endsWith('.' + ext) || building) return;
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
- copyFileSync(compiled, resolve(devDir, 'main.js'));
118
- ok('Hot reload updated');
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) { fail('Build failed: ' + e.message); }
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
- }, 1000);
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
- info(`Watching src/ for changes (hot reload)`);
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); });
@@ -12,6 +12,8 @@
12
12
  "devDependencies": {
13
13
  "esbuild": "^0.25.0",
14
14
  "adm-zip": "^0.5.0",
15
- "https-proxy-agent": "^9.0.0"
15
+ "https-proxy-agent": "^9.0.0",
16
+ "source-map": "^0.7.4",
17
+ "ws": "^8.0.0"
16
18
  }
17
19
  }
@@ -3,6 +3,8 @@
3
3
  "version": "1.0.0",
4
4
  "author": "Unknown",
5
5
  "description": "A Yeow plugin",
6
+ "api": "1.18",
7
+ "java": 21,
6
8
  "dev": {
7
9
  "port": 17367,
8
10
  "memory": "4G"