create-yeow 0.2.68 → 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 +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 +44 -102
- package/templates/default/.yeow/dev-server.js +1 -1
- package/templates/default/.yeow/yeow-assets.mjs +141 -0
- package/templates/default/yeow.config.json +1 -0
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import * as esbuild from 'esbuild';
|
|
2
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync, statSync, readdirSync,
|
|
3
|
-
import { resolve, dirname
|
|
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
|
|
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: [
|
|
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
|
-
|
|
130
|
-
|
|
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
|
|
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:
|
|
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
|
-
//
|
|
155
|
-
if (existsSync(
|
|
156
|
-
const files = readdirSync(
|
|
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
|
-
|
|
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.
|
|
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 =
|
|
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', '.
|
|
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 };
|