create-yeow 0.2.67 → 0.2.70

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.67",
3
+ "version": "0.2.70",
4
4
  "description": "Scaffold a Yeow plugin project",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,38 +1,43 @@
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, rmSync } from 'fs';
3
+ import { resolve, dirname } from 'path';
4
4
  import { fileURLToPath } from 'url';
5
5
  import AdmZip from 'adm-zip';
6
6
  import { execSync } from 'child_process';
7
+ import { makeAssetPlugin, makeDedupePlugin, assetsOutDirFor } from './yeow-assets.mjs';
7
8
 
8
9
  const root = resolve(fileURLToPath(import.meta.url), '..', '..');
9
10
  const cfg = JSON.parse(readFileSync(resolve(root, 'yeow.config.json'), 'utf-8'));
11
+ const pkgJson = JSON.parse(readFileSync(resolve(root, 'package.json'), 'utf-8'));
10
12
  const { name, version } = cfg;
11
13
  const isTs = existsSync(resolve(root, 'src', 'index.ts'));
12
14
  const entry = isTs ? 'src/index.ts' : 'src/index.js';
13
15
 
14
- // Optional type check for TypeScript projects
16
+ // ── 类型检查(TS 项目)──
15
17
  if (isTs && cfg.typecheck !== false) {
16
18
  try {
17
19
  execSync('npx -p typescript tsc --noEmit', { cwd: root, stdio: 'pipe' });
18
- console.log(' Type check passed');
20
+ console.log(' \u2713 Type check passed');
19
21
  } catch (e) {
20
22
  const out = e.stdout?.toString().trim();
21
23
  const err = e.stderr?.toString().trim();
22
24
  if (out) console.error(out);
23
25
  if (err) console.error(err);
24
- if (!out && !err) console.error(' Type check failed');
26
+ if (!out && !err) console.error(' \u2717 Type check failed');
25
27
  process.exit(1);
26
28
  }
27
29
  }
28
30
 
29
- const outDir = resolve(root, 'dist', '.yeow');
30
- mkdirSync(outDir, { recursive: true });
31
-
32
31
  const apiVer = cfg.api || '1.18';
33
32
  const isDev = process.env.YEOW_DEV === 'true';
33
+ const outDir = isDev ? resolve(root, 'dist', '.dev') : resolve(root, 'dist', '.yeow');
34
+ const assetsOut = assetsOutDirFor(outDir);
34
35
 
35
36
  async function main() {
37
+ // 清空资产输出,避免旧哈希目录残留
38
+ rmSync(assetsOut, { recursive: true, force: true });
39
+
40
+ // ── 打包 ──
36
41
  await esbuild.build({
37
42
  entryPoints: [resolve(root, entry)],
38
43
  outfile: resolve(outDir, 'main.js'),
@@ -45,42 +50,67 @@ async function main() {
45
50
  treeShaking: true,
46
51
  minify: false,
47
52
  sourcemap: isDev ? 'linked' : false,
53
+ plugins: [
54
+ makeDedupePlugin(root),
55
+ makeAssetPlugin({ root, pkgJson, outDir }),
56
+ ],
48
57
  });
58
+ console.log(' \u2713 Bundled (' + (statSync(resolve(outDir, 'main.js')).size / 1024).toFixed(1) + ' KB)');
49
59
 
50
- const size = statSync(resolve(outDir, 'main.js')).size;
51
- console.log(` ✓ Bundled (${(size / 1024).toFixed(1)} KB)`);
52
-
53
- const tmplJar = resolve(root, '.yeow', 'assets', 'yeow-template-0.1.0.jar');
54
- 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`));
60
+ // ── 组装 JAR ──
61
+ const zip = new AdmZip(resolve(root, '.yeow', 'assets', 'yeow-template-0.1.0.jar'));
62
+ zip.updateFile('plugin.yml', Buffer.from(
63
+ 'name: ' + name + '\n' +
64
+ 'version: ' + version + '\n' +
65
+ 'main: yeow.template.Bootstrap\n' +
66
+ 'api-version: \'' + apiVer + '\'\n' +
67
+ 'depend:\n - Yeow\n'));
56
68
 
57
69
  if (isDev) {
58
70
  const devInfo = {
59
71
  name,
60
72
  codeFile: resolve(outDir, 'main.js').replace(/\\/g, '/'),
61
- assetsDir: existsSync(resolve(root, 'assets')) ? resolve(root, 'assets').replace(/\\/g, '/') : null,
73
+ assetsDir: assetsOut.replace(/\\/g, '/'),
62
74
  };
63
75
  writeFileSync(resolve(outDir, 'dev.json'), JSON.stringify(devInfo, null, 2));
64
- console.log(` ✓ Dev info written`);
65
76
  zip.addFile('.yeow/dev.json', readFileSync(resolve(outDir, 'dev.json')));
77
+ console.log(' \u2713 Dev info written');
66
78
  } else {
67
79
  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
- }
75
- }
76
- console.log(' ✓ Assets included');
80
+ }
81
+
82
+ // 资产(esbuild 插件已写入 assetsOut)
83
+ if (existsSync(assetsOut)) {
84
+ const files = readdirSync(assetsOut, { recursive: true }).filter(f => statSync(resolve(assetsOut, f)).isFile());
85
+ for (const f of files) {
86
+ zip.addFile('assets/' + f.replace(/\\/g, '/'), readFileSync(resolve(assetsOut, f)));
77
87
  }
88
+ console.log(' \u2713 Assets included (' + files.length + ' files)');
78
89
  }
79
90
 
80
91
  zip.addFile('yeow.json', Buffer.from(JSON.stringify(cfg)));
81
- const outJar = resolve(root, 'dist', `${name}-${version}.jar`);
92
+ const outJar = resolve(root, 'dist', isDev ? 'plugins' : '', name + '-' + version + '.jar');
93
+ mkdirSync(dirname(outJar), { recursive: true });
82
94
  zip.writeZip(outJar);
83
- console.log(` Packaged ${outJar}\n`);
95
+ console.log(' \u2713 Packaged ' + outJar + '\n');
96
+
97
+ // ── 组装 .yeow.zip(仅生产)──
98
+ // 平台无关的插件包:放入 plugins/Yeow/ 会被运行时自动扫描加载,或 /yeow load 手动加载。
99
+ if (!isDev) {
100
+ const pkgZip = new AdmZip();
101
+ pkgZip.addFile('.yeow/main.js', readFileSync(resolve(outDir, 'main.js')));
102
+ if (existsSync(assetsOut)) {
103
+ const files = readdirSync(assetsOut, { recursive: true }).filter(f => statSync(resolve(assetsOut, f)).isFile());
104
+ for (const f of files) {
105
+ pkgZip.addFile('assets/' + f.replace(/\\/g, '/'), readFileSync(resolve(assetsOut, f)));
106
+ }
107
+ }
108
+ pkgZip.addFile('yeow.json', Buffer.from(JSON.stringify(cfg)));
109
+ const outZip = resolve(root, 'dist', name + '-' + version + '.yeow.zip');
110
+ mkdirSync(dirname(outZip), { recursive: true });
111
+ pkgZip.writeZip(outZip);
112
+ console.log(' \u2713 Packaged ' + outZip + '\n');
113
+ }
84
114
  }
85
115
 
86
116
  main().catch(e => { console.error(e); process.exit(1); });
@@ -231,13 +231,14 @@ function startHotReload() {
231
231
  info('Source changed, rebuilding...');
232
232
  try {
233
233
  execSync('node .yeow/build.js', { cwd: ROOT, stdio: 'pipe', env: { ...process.env, YEOW_DEV: 'true' } });
234
- const compiled = resolve(ROOT, 'dist', '.yeow', 'main.js');
234
+ const compiled = resolve(ROOT, 'dist', '.dev', 'main.js');
235
235
  if (existsSync(compiled)) {
236
+ const devAssets = resolve(ROOT, 'dist', '.dev', '.assets');
236
237
  broadcast({
237
238
  type: 'hot-reload',
238
239
  plugin: cfg.name,
239
240
  codeFile: compiled.replace(/\\/g, '/'),
240
- assetsDir: existsSync(assetsDir) ? assetsDir.replace(/\\/g, '/') : null,
241
+ assetsDir: existsSync(devAssets) ? devAssets.replace(/\\/g, '/') : null,
241
242
  });
242
243
  _consumer = null;
243
244
  ok('Hot reload sent via WebSocket');
@@ -268,7 +269,7 @@ function startHotReload() {
268
269
  let _consumer = null;
269
270
  async function getSourceMapConsumer() {
270
271
  if (_consumer) return _consumer;
271
- const mapFile = resolve(ROOT, 'dist', '.yeow', 'main.js.map');
272
+ const mapFile = resolve(ROOT, 'dist', '.dev', 'main.js.map');
272
273
  if (!existsSync(mapFile)) return null;
273
274
  try {
274
275
  const raw = JSON.parse(readFileSync(mapFile, 'utf-8'));
@@ -289,7 +290,7 @@ async function printFormattedError(err) {
289
290
  consumer = await getSourceMapConsumer();
290
291
  }
291
292
  if (hasMainJs && !consumer) {
292
- const mapFile = resolve(ROOT, 'dist', '.yeow', 'main.js.map');
293
+ const mapFile = resolve(ROOT, 'dist', '.dev', 'main.js.map');
293
294
  out += ` ${c.D}(source-map not found: ${existsSync(mapFile) ? 'exists but failed to parse' : 'missing at ' + mapFile})${c.r}\n`;
294
295
  }
295
296
 
@@ -374,7 +375,7 @@ async function main() {
374
375
  await initServer();
375
376
  serverProps(cfg.dev?.port || 17367);
376
377
  buildPlugin();
377
- copyToPlugins(resolve(ROOT, 'dist', `${cfg.name}-${cfg.version}.jar`), 'Plugin');
378
+ copyToPlugins(resolve(ROOT, 'dist', 'plugins', `${cfg.name}-${cfg.version}.jar`), 'Plugin');
378
379
  copyToPlugins(RUNTIME, 'Runtime');
379
380
 
380
381
  startHotReload();
@@ -0,0 +1,141 @@
1
+ import { readFileSync, readdirSync, existsSync, statSync, mkdirSync, copyFileSync } from 'fs';
2
+ import { resolve, dirname, basename, extname } from 'path';
3
+ import { createRequire } from 'module';
4
+ import { createHash } from 'crypto';
5
+
6
+ const require = createRequire(import.meta.url);
7
+
8
+ // ── 哈希树 ─────────────────────────────────────────────────────────
9
+ // 哈希规则:
10
+ // - 根级文件:独立哈希 <name>.<contentHash><ext>
11
+ // - 顶层目录(assets/ 直接子目录):整体哈希 <name>.<dirHash>/,内部一切保持原名
12
+ // 保证目录内部(含深层子目录)的相对引用始终有效。
13
+ function buildTree(absDir) {
14
+ const children = {};
15
+ for (const e of readdirSync(absDir, { withFileTypes: true })) {
16
+ const p = resolve(absDir, e.name);
17
+ if (e.isDirectory()) {
18
+ children[e.name] = buildTree(p);
19
+ } else {
20
+ children[e.name] = {
21
+ isFile: true,
22
+ contentHash: createHash('md5').update(readFileSync(p)).digest('hex').slice(0, 8),
23
+ };
24
+ }
25
+ }
26
+ const parts = Object.keys(children).sort().map(k => {
27
+ const c = children[k];
28
+ return k + ':' + (c.isFile ? c.contentHash : c.hash);
29
+ });
30
+ return { children, isFile: false, hash: createHash('md5').update(parts.join(',')).digest('hex').slice(0, 8) };
31
+ }
32
+
33
+ // ── 部署 + map ─────────────────────────────────────────────────────
34
+ // overwrite=false 时(依赖包)不覆盖主项目已有的 map 条目与文件
35
+ // topLevel=true:当前目录是 assets/ 直接子目录,需要哈希;内部目录保持原名
36
+ function deploy(node, absSrc, outDir, map, origRel, hashedRel, overwrite, topLevel) {
37
+ if (!node.isFile && hashedRel) mkdirSync(resolve(outDir, hashedRel), { recursive: true });
38
+ for (const [name, child] of Object.entries(node.children || {})) {
39
+ if (child.isFile) {
40
+ if (hashedRel === '') {
41
+ const ext = extname(name);
42
+ const base = basename(name, ext);
43
+ const dest = base + '.' + child.contentHash + ext;
44
+ const dst = resolve(outDir, dest);
45
+ if (overwrite || !existsSync(dst)) copyFileSync(resolve(absSrc, name), dst);
46
+ if (overwrite || !map[name]) map[name] = 'assets/' + dest;
47
+ } else {
48
+ const dst = resolve(outDir, hashedRel, name);
49
+ if (overwrite || !existsSync(dst)) {
50
+ mkdirSync(resolve(outDir, hashedRel), { recursive: true });
51
+ copyFileSync(resolve(absSrc, name), dst);
52
+ }
53
+ if (overwrite || !map[origRel + name]) map[origRel + name] = 'assets/' + hashedRel + name;
54
+ }
55
+ } else {
56
+ const subOrig = origRel + name + '/';
57
+ // 仅顶层目录哈希;内部目录保持原名(保护深层相对引用)
58
+ const subHashed = topLevel
59
+ ? name + '.' + child.hash + '/'
60
+ : hashedRel + name + '/';
61
+ deploy(child, resolve(absSrc, name), outDir, map, subOrig, subHashed, overwrite, false);
62
+ if (overwrite || !map[subOrig]) map[subOrig] = 'assets/' + subHashed;
63
+ }
64
+ }
65
+ }
66
+
67
+ // ── 收集所有资产来源(主项目 + 依赖包)───────────────────────────
68
+ function collectSources(root, pkgJson) {
69
+ const sources = [];
70
+ const own = resolve(root, 'assets');
71
+ if (existsSync(own)) sources.push({ tree: buildTree(own), absSrc: own, overwrite: true });
72
+
73
+ for (const pkgName of Object.keys(pkgJson.dependencies || {})) {
74
+ try {
75
+ const pkgJsonPath = require.resolve(pkgName + '/package.json', { paths: [root] });
76
+ const pkgAssets = resolve(dirname(pkgJsonPath), 'assets');
77
+ if (existsSync(pkgAssets)) {
78
+ sources.push({ tree: buildTree(pkgAssets), absSrc: pkgAssets, overwrite: false });
79
+ }
80
+ } catch { /* 非 Yeow 包或未安装,跳过 */ }
81
+ }
82
+ return sources;
83
+ }
84
+
85
+ // ── esbuild 插件:__yeow-assets 虚拟模块 ──────────────────────────
86
+ export function makeAssetPlugin({ root, pkgJson, outDir }) {
87
+ const assetsOutDir = resolve(outDir, '.assets');
88
+ return {
89
+ name: 'yeow-assets',
90
+ setup(build) {
91
+ build.onResolve({ filter: /^__yeow-assets$/ }, args => ({
92
+ path: args.path,
93
+ namespace: 'yeow-assets',
94
+ }));
95
+ build.onLoad({ filter: /.*/, namespace: 'yeow-assets' }, async () => {
96
+ const map = {};
97
+ mkdirSync(assetsOutDir, { recursive: true });
98
+ for (const src of collectSources(root, pkgJson)) {
99
+ deploy(src.tree, src.absSrc, assetsOutDir, map, '', '', src.overwrite, true);
100
+ }
101
+ return {
102
+ contents:
103
+ 'const _map = ' + JSON.stringify(map) + ';\n' +
104
+ 'function _norm(p) { return p.endsWith("/") ? p.slice(0, -1) : p; }\n' +
105
+ 'export function getPath(p) {\n' +
106
+ ' if (_map[p]) return _map[p];\n' +
107
+ ' const n = _norm(p);\n' +
108
+ ' if (_map[n]) return _map[n];\n' +
109
+ ' if (_map[n + "/"]) return _map[n + "/"];\n' +
110
+ ' return p;\n' +
111
+ '}\n',
112
+ loader: 'js',
113
+ };
114
+ });
115
+ },
116
+ };
117
+ }
118
+
119
+ // ── esbuild 插件:统一 yeow-api/yeow-utils 到主项目实例 ──────────
120
+ // 避免依赖包自带副本导致全局状态分裂(如 __yeowInitCbs 被覆盖)
121
+ // 注意:返回路径必须用正斜杠,否则 Windows 下 esbuild 解析失败
122
+ export function makeDedupePlugin(root) {
123
+ return {
124
+ name: 'dedupe-yeow-core',
125
+ setup(build) {
126
+ build.onResolve({ filter: /^yeow-api$/ }, () => ({
127
+ path: resolve(root, 'node_modules', 'yeow-api', 'src', 'index.ts').replace(/\\/g, '/'),
128
+ }));
129
+ build.onResolve({ filter: /^yeow-utils$/ }, () => ({
130
+ path: resolve(root, 'node_modules', 'yeow-utils', 'src', 'index.ts').replace(/\\/g, '/'),
131
+ }));
132
+ },
133
+ };
134
+ }
135
+
136
+ // ── 资产输出目录(供 build.js 打包 JAR)────────────────────────────
137
+ export function assetsOutDirFor(outDir) {
138
+ return resolve(outDir, '.assets');
139
+ }
140
+
141
+ export { buildTree };
@@ -6,6 +6,7 @@
6
6
  "api": "1.18",
7
7
  "java": 21,
8
8
  "typecheck": true,
9
+ "permissions": [],
9
10
  "dev": {
10
11
  "port": 17367,
11
12
  "memory": "4G",