create-yeow 0.2.68 → 0.2.71

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.68",
3
+ "version": "0.2.71",
4
4
  "description": "Scaffold a Yeow plugin project",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,17 +1,19 @@
1
1
  import * as esbuild from 'esbuild';
2
- import { readFileSync, writeFileSync, mkdirSync, existsSync, statSync, readdirSync, rmdirSync, copyFileSync } from 'fs';
3
- import { resolve, dirname, basename, extname } 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
- import { createHash } from 'crypto';
6
5
  import AdmZip from 'adm-zip';
7
6
  import { execSync } from 'child_process';
7
+ import { makeAssetPlugin, makeDedupePlugin, assetsOutDirFor } from './yeow-assets.mjs';
8
8
 
9
9
  const root = resolve(fileURLToPath(import.meta.url), '..', '..');
10
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'));
11
12
  const { name, version } = cfg;
12
13
  const isTs = existsSync(resolve(root, 'src', 'index.ts'));
13
14
  const entry = isTs ? 'src/index.ts' : 'src/index.js';
14
15
 
16
+ // ── 类型检查(TS 项目)──
15
17
  if (isTs && cfg.typecheck !== false) {
16
18
  try {
17
19
  execSync('npx -p typescript tsc --noEmit', { cwd: root, stdio: 'pipe' });
@@ -29,88 +31,13 @@ if (isTs && cfg.typecheck !== false) {
29
31
  const apiVer = cfg.api || '1.18';
30
32
  const isDev = process.env.YEOW_DEV === 'true';
31
33
  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
+ const assetsOut = assetsOutDirFor(outDir);
112
35
 
113
36
  async function main() {
37
+ // 清空资产输出,避免旧哈希目录残留
38
+ rmSync(assetsOut, { recursive: true, force: true });
39
+
40
+ // ── 打包 ──
114
41
  await esbuild.build({
115
42
  entryPoints: [resolve(root, entry)],
116
43
  outfile: resolve(outDir, 'main.js'),
@@ -123,52 +50,67 @@ async function main() {
123
50
  treeShaking: true,
124
51
  minify: false,
125
52
  sourcemap: isDev ? 'linked' : false,
126
- plugins: [yeowAssetPlugin],
53
+ plugins: [
54
+ makeDedupePlugin(root),
55
+ makeAssetPlugin({ root, pkgJson, outDir }),
56
+ ],
127
57
  });
58
+ console.log(' \u2713 Bundled (' + (statSync(resolve(outDir, 'main.js')).size / 1024).toFixed(1) + ' KB)');
128
59
 
129
- const size = statSync(resolve(outDir, 'main.js')).size;
130
- console.log(' \u2713 Bundled (' + (size / 1024).toFixed(1) + ' KB)');
131
-
132
- const tmplJar = resolve(root, '.yeow', 'assets', 'yeow-template-0.1.0.jar');
133
- const zip = new AdmZip(tmplJar);
60
+ // ── 组装 JAR ──
61
+ const zip = new AdmZip(resolve(root, '.yeow', 'assets', 'yeow-template-0.1.0.jar'));
134
62
  zip.updateFile('plugin.yml', Buffer.from(
135
63
  'name: ' + name + '\n' +
136
64
  'version: ' + version + '\n' +
137
65
  'main: yeow.template.Bootstrap\n' +
138
66
  'api-version: \'' + apiVer + '\'\n' +
139
- 'depend:\n - Yeow-Runtime\n'));
67
+ 'depend:\n - Yeow\n'));
140
68
 
141
69
  if (isDev) {
142
70
  const devInfo = {
143
71
  name,
144
72
  codeFile: resolve(outDir, 'main.js').replace(/\\/g, '/'),
145
- assetsDir: assetsOutDir.replace(/\\/g, '/'),
73
+ assetsDir: assetsOut.replace(/\\/g, '/'),
146
74
  };
147
75
  writeFileSync(resolve(outDir, 'dev.json'), JSON.stringify(devInfo, null, 2));
148
- console.log(' \u2713 Dev info written');
149
76
  zip.addFile('.yeow/dev.json', readFileSync(resolve(outDir, 'dev.json')));
77
+ console.log(' \u2713 Dev info written');
150
78
  } else {
151
79
  zip.addFile('.yeow/main.js', readFileSync(resolve(outDir, 'main.js')));
152
80
  }
153
81
 
154
- // Pack hashed assets into JAR
155
- if (existsSync(assetsOutDir)) {
156
- const files = readdirSync(assetsOutDir, { recursive: true });
82
+ // 资产(esbuild 插件已写入 assetsOut)
83
+ if (existsSync(assetsOut)) {
84
+ const files = readdirSync(assetsOut, { recursive: true }).filter(f => statSync(resolve(assetsOut, f)).isFile());
157
85
  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));
161
- }
86
+ zip.addFile('assets/' + f.replace(/\\/g, '/'), readFileSync(resolve(assetsOut, f)));
162
87
  }
163
- console.log(' \u2713 Assets included (' + files.filter(f => statSync(resolve(assetsOutDir, f)).isFile()).length + ' files)');
88
+ console.log(' \u2713 Assets included (' + files.length + ' files)');
164
89
  }
165
90
 
166
91
  zip.addFile('yeow.json', Buffer.from(JSON.stringify(cfg)));
167
- const outJar = isDev ? resolve(root, 'dist', 'plugins', name + '-' + version + '.jar')
168
- : resolve(root, 'dist', name + '-' + version + '.jar');
92
+ const outJar = resolve(root, 'dist', isDev ? 'plugins' : '', name + '-' + version + '.jar');
169
93
  mkdirSync(dirname(outJar), { recursive: true });
170
94
  zip.writeZip(outJar);
171
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
+ }
172
114
  }
173
115
 
174
116
  main().catch(e => { console.error(e); process.exit(1); });
@@ -269,7 +269,7 @@ function startHotReload() {
269
269
  let _consumer = null;
270
270
  async function getSourceMapConsumer() {
271
271
  if (_consumer) return _consumer;
272
- const mapFile = resolve(ROOT, 'dist', '.yeow', 'main.js.map');
272
+ const mapFile = resolve(ROOT, 'dist', '.dev', 'main.js.map');
273
273
  if (!existsSync(mapFile)) return null;
274
274
  try {
275
275
  const raw = JSON.parse(readFileSync(mapFile, 'utf-8'));
@@ -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 };
@@ -7,7 +7,7 @@
7
7
  "dev": "node .yeow/dev-server.js"
8
8
  },
9
9
  "dependencies": {
10
- "yeow-api": "^0.2.57",
10
+ "yeow-api": "^0.2.59",
11
11
  "yeow-utils": "^0.1.14"
12
12
  },
13
13
  "devDependencies": {
@@ -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",