create-yeow 0.2.66 → 0.2.68

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.66",
3
+ "version": "0.2.68",
4
4
  "description": "Scaffold a Yeow plugin project",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,7 +1,8 @@
1
1
  import * as esbuild from 'esbuild';
2
- import { readFileSync, writeFileSync, mkdirSync, existsSync, statSync, readdirSync } from 'fs';
3
- import { resolve } from 'path';
2
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, statSync, readdirSync, rmdirSync, copyFileSync } from 'fs';
3
+ import { resolve, dirname, basename, extname } from 'path';
4
4
  import { fileURLToPath } from 'url';
5
+ import { createHash } from 'crypto';
5
6
  import AdmZip from 'adm-zip';
6
7
  import { execSync } from 'child_process';
7
8
 
@@ -11,26 +12,103 @@ const { name, version } = cfg;
11
12
  const isTs = existsSync(resolve(root, 'src', 'index.ts'));
12
13
  const entry = isTs ? 'src/index.ts' : 'src/index.js';
13
14
 
14
- // Optional type check for TypeScript projects
15
15
  if (isTs && cfg.typecheck !== false) {
16
16
  try {
17
17
  execSync('npx -p typescript tsc --noEmit', { cwd: root, stdio: 'pipe' });
18
- console.log(' Type check passed');
18
+ console.log(' \u2713 Type check passed');
19
19
  } catch (e) {
20
20
  const out = e.stdout?.toString().trim();
21
21
  const err = e.stderr?.toString().trim();
22
22
  if (out) console.error(out);
23
23
  if (err) console.error(err);
24
- if (!out && !err) console.error(' Type check failed');
24
+ if (!out && !err) console.error(' \u2717 Type check failed');
25
25
  process.exit(1);
26
26
  }
27
27
  }
28
28
 
29
- const outDir = resolve(root, 'dist', '.yeow');
30
- mkdirSync(outDir, { recursive: true });
31
-
32
29
  const apiVer = cfg.api || '1.18';
33
30
  const isDev = process.env.YEOW_DEV === 'true';
31
+ const outDir = isDev ? resolve(root, 'dist', '.dev') : resolve(root, 'dist', '.yeow');
32
+ const assetsOutDir = resolve(outDir, '.assets');
33
+ const srcAssetsDir = resolve(root, 'assets');
34
+
35
+ // ── esbuild plugin: __yeow-assets virtual module ──
36
+ // 目录级哈希:目录整体重命名 <name>.<dirHash>/,目录内文件保持原名(内部引用不断)
37
+ // 根级文件独立哈希 <name>.<hash><ext>
38
+ const ASSET_TREE = (() => {
39
+ if (!existsSync(srcAssetsDir)) return { children: {}, hash: null };
40
+ const buildTree = (absDir) => {
41
+ const children = {};
42
+ for (const e of readdirSync(absDir, { withFileTypes: true })) {
43
+ const p = resolve(absDir, e.name);
44
+ if (e.isDirectory()) {
45
+ children[e.name] = buildTree(p);
46
+ } else {
47
+ children[e.name] = { isFile: true, contentHash: createHash('md5').update(readFileSync(p)).digest('hex').slice(0, 8) };
48
+ }
49
+ }
50
+ const parts = Object.keys(children).sort().map(k => {
51
+ const c = children[k];
52
+ return k + ':' + (c.isFile ? c.contentHash : c.hash);
53
+ });
54
+ return { children, isFile: false, hash: createHash('md5').update(parts.join(',')).digest('hex').slice(0, 8) };
55
+ };
56
+ return buildTree(srcAssetsDir);
57
+ })();
58
+
59
+ const yeowAssetPlugin = {
60
+ name: 'yeow-assets',
61
+ setup(build) {
62
+ build.onResolve({ filter: /^__yeow-assets$/ }, args => ({
63
+ path: args.path,
64
+ namespace: 'yeow-assets',
65
+ }));
66
+ build.onLoad({ filter: /.*/, namespace: 'yeow-assets' }, async () => {
67
+ const map = {};
68
+ mkdirSync(assetsOutDir, { recursive: true });
69
+
70
+ const deploy = (node, absSrc, origRel, hashedRel) => {
71
+ if (!node.isFile && hashedRel) {
72
+ mkdirSync(resolve(assetsOutDir, hashedRel), { recursive: true });
73
+ }
74
+ for (const [name, child] of Object.entries(node.children || {})) {
75
+ if (child.isFile) {
76
+ // 根级文件:独立哈希;目录内文件:保持原名
77
+ if (hashedRel === '') {
78
+ const ext = extname(name);
79
+ const base = basename(name, ext);
80
+ const dest = base + '.' + child.contentHash + ext;
81
+ copyFileSync(resolve(absSrc, name), resolve(assetsOutDir, dest));
82
+ map[name] = 'assets/' + dest;
83
+ } else {
84
+ copyFileSync(resolve(absSrc, name), resolve(assetsOutDir, hashedRel, name));
85
+ map[origRel + name] = 'assets/' + hashedRel + name;
86
+ }
87
+ } else {
88
+ const subHashed = (hashedRel === '' ? '' : hashedRel) + name + '.' + child.hash + '/';
89
+ deploy(child, resolve(absSrc, name), origRel + name + '/', subHashed);
90
+ map[origRel + name + '/'] = 'assets/' + subHashed;
91
+ }
92
+ }
93
+ };
94
+
95
+ deploy(ASSET_TREE, srcAssetsDir, '', '');
96
+
97
+ return {
98
+ contents: 'const _map = ' + JSON.stringify(map) + ';\n' +
99
+ 'function _norm(p) { return p.endsWith("/") ? p.slice(0, -1) : p; }\n' +
100
+ 'export function getPath(p) {\n' +
101
+ ' if (_map[p]) return _map[p];\n' +
102
+ ' const n = _norm(p);\n' +
103
+ ' if (_map[n]) return _map[n];\n' +
104
+ ' if (_map[n + "/"]) return _map[n + "/"];\n' +
105
+ ' return p;\n' +
106
+ '}\n',
107
+ loader: 'js',
108
+ };
109
+ });
110
+ },
111
+ };
34
112
 
35
113
  async function main() {
36
114
  await esbuild.build({
@@ -45,42 +123,52 @@ async function main() {
45
123
  treeShaking: true,
46
124
  minify: false,
47
125
  sourcemap: isDev ? 'linked' : false,
126
+ plugins: [yeowAssetPlugin],
48
127
  });
49
128
 
50
129
  const size = statSync(resolve(outDir, 'main.js')).size;
51
- console.log(` Bundled (${(size / 1024).toFixed(1)} KB)`);
130
+ console.log(' \u2713 Bundled (' + (size / 1024).toFixed(1) + ' KB)');
52
131
 
53
132
  const tmplJar = resolve(root, '.yeow', 'assets', 'yeow-template-0.1.0.jar');
54
133
  const zip = new AdmZip(tmplJar);
55
- zip.updateFile('plugin.yml', Buffer.from(`name: ${name}\nversion: ${version}\nmain: yeow.template.Bootstrap\napi-version: '${apiVer}'\ndepend:\n - Yeow-Runtime\n`));
134
+ zip.updateFile('plugin.yml', Buffer.from(
135
+ 'name: ' + name + '\n' +
136
+ 'version: ' + version + '\n' +
137
+ 'main: yeow.template.Bootstrap\n' +
138
+ 'api-version: \'' + apiVer + '\'\n' +
139
+ 'depend:\n - Yeow-Runtime\n'));
56
140
 
57
141
  if (isDev) {
58
142
  const devInfo = {
59
143
  name,
60
144
  codeFile: resolve(outDir, 'main.js').replace(/\\/g, '/'),
61
- assetsDir: existsSync(resolve(root, 'assets')) ? resolve(root, 'assets').replace(/\\/g, '/') : null,
145
+ assetsDir: assetsOutDir.replace(/\\/g, '/'),
62
146
  };
63
147
  writeFileSync(resolve(outDir, 'dev.json'), JSON.stringify(devInfo, null, 2));
64
- console.log(` Dev info written`);
148
+ console.log(' \u2713 Dev info written');
65
149
  zip.addFile('.yeow/dev.json', readFileSync(resolve(outDir, 'dev.json')));
66
150
  } else {
67
151
  zip.addFile('.yeow/main.js', readFileSync(resolve(outDir, 'main.js')));
68
- const assetsDir = resolve(root, 'assets');
69
- if (existsSync(assetsDir)) {
70
- for (const f of readdirSync(assetsDir, { recursive: true })) {
71
- const fp = resolve(assetsDir, f);
72
- if (statSync(fp).isFile()) {
73
- zip.addFile('assets/' + f, readFileSync(fp));
74
- }
152
+ }
153
+
154
+ // Pack hashed assets into JAR
155
+ if (existsSync(assetsOutDir)) {
156
+ const files = readdirSync(assetsOutDir, { recursive: true });
157
+ for (const f of files) {
158
+ const fp = resolve(assetsOutDir, f);
159
+ if (statSync(fp).isFile()) {
160
+ zip.addFile('assets/' + f.replace(/\\/g, '/'), readFileSync(fp));
75
161
  }
76
- console.log(' ✓ Assets included');
77
162
  }
163
+ console.log(' \u2713 Assets included (' + files.filter(f => statSync(resolve(assetsOutDir, f)).isFile()).length + ' files)');
78
164
  }
79
165
 
80
166
  zip.addFile('yeow.json', Buffer.from(JSON.stringify(cfg)));
81
- const outJar = resolve(root, 'dist', `${name}-${version}.jar`);
167
+ const outJar = isDev ? resolve(root, 'dist', 'plugins', name + '-' + version + '.jar')
168
+ : resolve(root, 'dist', name + '-' + version + '.jar');
169
+ mkdirSync(dirname(outJar), { recursive: true });
82
170
  zip.writeZip(outJar);
83
- console.log(` Packaged ${outJar}\n`);
171
+ console.log(' \u2713 Packaged ' + outJar + '\n');
84
172
  }
85
173
 
86
174
  main().catch(e => { console.error(e); process.exit(1); });
@@ -76,27 +76,22 @@ let proc = null;
76
76
  let wss = null;
77
77
 
78
78
  // ── Graceful shutdown ────────────────────────────────────────────
79
+ let cleaning = false;
79
80
  function cleanup() {
81
+ if (cleaning) return;
82
+ cleaning = true;
83
+ if (wss) { try { wss.close(); } catch {} }
80
84
  if (proc && !proc.killed) {
81
85
  try { proc.stdin.write('stop\n'); } catch {}
82
- setTimeout(() => { if (proc && !proc.killed) try { proc.kill(); } catch {} }, 5000);
86
+ const killer = setTimeout(() => { if (proc && !proc.killed) try { proc.kill('SIGKILL'); } catch {} }, 10000);
87
+ proc.on('close', () => { clearTimeout(killer); process.exit(0); });
88
+ } else {
89
+ process.exit(0);
83
90
  }
84
- if (wss) { try { wss.close(); } catch {} }
85
- process.exit(0);
86
91
  }
87
92
  process.on('SIGINT', cleanup);
88
93
  process.on('SIGTERM', cleanup);
89
- process.on('exit', () => { if (proc && !proc.killed) try { proc.kill(); } catch {} });
90
- // Windows Ctrl+C in terminal
91
- if (process.platform === 'win32') {
92
- import('readline').then(rl => {
93
- rl.emitKeypressEvents(process.stdin);
94
- if (process.stdin.isTTY) process.stdin.setRawMode(true);
95
- process.stdin.on('keypress', (str, key) => {
96
- if ((key.ctrl && key.name === 'c') || key.name === 'escape') cleanup();
97
- });
98
- });
99
- }
94
+ process.on('beforeExit', () => { if (proc && !proc.killed) try { proc.kill(); } catch {} });
100
95
 
101
96
  // ── WebSocket Server ────────────────────────────────────────────
102
97
  function startWebSocket() {
@@ -236,13 +231,14 @@ function startHotReload() {
236
231
  info('Source changed, rebuilding...');
237
232
  try {
238
233
  execSync('node .yeow/build.js', { cwd: ROOT, stdio: 'pipe', env: { ...process.env, YEOW_DEV: 'true' } });
239
- const compiled = resolve(ROOT, 'dist', '.yeow', 'main.js');
234
+ const compiled = resolve(ROOT, 'dist', '.dev', 'main.js');
240
235
  if (existsSync(compiled)) {
236
+ const devAssets = resolve(ROOT, 'dist', '.dev', '.assets');
241
237
  broadcast({
242
238
  type: 'hot-reload',
243
239
  plugin: cfg.name,
244
240
  codeFile: compiled.replace(/\\/g, '/'),
245
- assetsDir: existsSync(assetsDir) ? assetsDir.replace(/\\/g, '/') : null,
241
+ assetsDir: existsSync(devAssets) ? devAssets.replace(/\\/g, '/') : null,
246
242
  });
247
243
  _consumer = null;
248
244
  ok('Hot reload sent via WebSocket');
@@ -294,7 +290,7 @@ async function printFormattedError(err) {
294
290
  consumer = await getSourceMapConsumer();
295
291
  }
296
292
  if (hasMainJs && !consumer) {
297
- const mapFile = resolve(ROOT, 'dist', '.yeow', 'main.js.map');
293
+ const mapFile = resolve(ROOT, 'dist', '.dev', 'main.js.map');
298
294
  out += ` ${c.D}(source-map not found: ${existsSync(mapFile) ? 'exists but failed to parse' : 'missing at ' + mapFile})${c.r}\n`;
299
295
  }
300
296
 
@@ -365,6 +361,7 @@ function startServer() {
365
361
  info(`\nStarting Paper ${PAPER_VERSION} server...`);
366
362
  proc = spawn('java', [...jvmArgs, '-jar', resolve(SERVER, PAPER_JAR), '--nogui'], { cwd: SERVER, stdio: ['pipe', 'inherit', 'inherit'] });
367
363
  proc.on('exit', code => { warn(`Server exited (${code})`); if (wss) wss.close(); process.exit(0); });
364
+ process.stdin.on('data', d => { if (proc && !proc.killed) try { proc.stdin.write(d); } catch {} });
368
365
  if (STOP) { info(`Auto-stop in ${STOP}s`); setTimeout(() => { warn('Auto-stop'); cleanup(); }, STOP * 1000); }
369
366
  }
370
367
 
@@ -378,7 +375,7 @@ async function main() {
378
375
  await initServer();
379
376
  serverProps(cfg.dev?.port || 17367);
380
377
  buildPlugin();
381
- copyToPlugins(resolve(ROOT, 'dist', `${cfg.name}-${cfg.version}.jar`), 'Plugin');
378
+ copyToPlugins(resolve(ROOT, 'dist', 'plugins', `${cfg.name}-${cfg.version}.jar`), 'Plugin');
382
379
  copyToPlugins(RUNTIME, 'Runtime');
383
380
 
384
381
  startHotReload();
@@ -7,7 +7,7 @@
7
7
  "dev": "node .yeow/dev-server.js"
8
8
  },
9
9
  "dependencies": {
10
- "yeow-api": "^0.2.54",
10
+ "yeow-api": "^0.2.57",
11
11
  "yeow-utils": "^0.1.14"
12
12
  },
13
13
  "devDependencies": {
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "my-yeow-plugin",
3
3
  "version": "1.0.0",
4
- "author": "Unknown",
4
+ "author": "",
5
5
  "description": "A Yeow plugin",
6
6
  "api": "1.18",
7
7
  "java": 21,
8
8
  "typecheck": true,
9
9
  "dev": {
10
10
  "port": 17367,
11
- "memory": "4G"
11
+ "memory": "4G",
12
+ "paperJar": "https://fill-data.papermc.io/v1/objects/5ee4f542f628a14c644410b08c94ea42e772ef4d29fe92973636b6813d4eaffc/paper-1.21.4-232.jar"
12
13
  }
13
- }
14
+ }