@epxing/cli 0.2.0 → 0.3.0

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/README.md CHANGED
@@ -3,12 +3,13 @@
3
3
  独立的工程开发工具,包含交互菜单、开发服务、工程与产品库构建、创建、Vue3 迁移、静态检查和升级技能安装。CLI 使用自己的版本号,目标平台由 `--epx-frame` 指定;发布包没有 npm 运行时依赖。启动、构建调用当前工程的脚本及本地依赖;迁移代码与框架仓库的 `tools/` 共用,打包时编译为独立 Node 程序。
4
4
 
5
5
  ```powershell
6
- npm install -g @epxing/cli@0.2.0 --registry https://registry.npmjs.org/
6
+ npm install -g @epxing/cli@0.3.0 --registry https://registry.npmjs.org/
7
7
  epx # ↑↓ + Enter 选择;Esc / Ctrl+C 取消
8
8
  epx dev --port 3000
9
9
  epx build --mode prd
10
- epx lib # 产品库整体构建;平台目录构建聚合组件库
11
- epx lib --lazy # 产品按需库(工程需支持此构建形态)
10
+ epx lib # 产品默认懒加载;平台目录构建完整聚合组件库
11
+ epx lib --full # 产品完整打包
12
+ epx lib --lazy # 显式选择懒加载,兼容已有命令
12
13
  epx create my-product --epx-frame <目标平台版本>
13
14
  epx migrate E:/products/old-product --epx-frame <目标平台版本>
14
15
  epx migrate E:/products/old-product --epx-frame <目标平台版本> --apply
@@ -26,8 +27,8 @@ epx skill install --dir E:/products/my-product
26
27
  | `epx` / `epx menu` | 交互选择操作、工程目录、环境、端口和构建方式;管道支持数字输入 |
27
28
  | `epx dev` / `epx serve` | 使用工程 `dev`、`serve` 或 `start` 脚本启动服务 |
28
29
  | `epx build` | 使用工程的构建脚本;只有 `build:lib` 的产品自动构建产品库 |
29
- | `epx lib` / `epx build --lib` | 优先产品 `build:lib` / `lib`,平台使用完整 `build:component`;识别旧 `--target lib` 构建脚本 |
30
- | `epx lib --lazy` | 优先 `build:lib:lazy`,否则为产品库脚本设置 `LAZY_LIB=true`;工程需支持按需构建 |
30
+ | `epx lib` / `epx build --lib` / `epx lib --lazy` | 产品默认懒加载,优先 `build:lib:lazy`,否则使用 `build:lib` / `lib`;平台聚合库仍完整构建 |
31
+ | `epx lib --full` / `epx build --lib --full` | 产品完整打包,明确设置 `LAZY_LIB=false`;兼容旧 `--target lib` 脚本 |
31
32
  | `epx preview` | 使用 `preview` / `vite:preview`;没有脚本时使用工程本地 Vite |
32
33
  | `epx create` | 无参数进入创建引导,也可用参数直接创建 |
33
34
  | `epx migrate` / `epx upgrade` | 无参数引导:目标版本、迁移范围、预览、确认应用 |
@@ -39,6 +40,8 @@ epx skill install --dir E:/products/my-product
39
40
 
40
41
  工程命令支持 `--dir <目录>`,默认从当前目录向上找到最近的 `package.json`;不会改为 CLI 全局安装目录。没有项目脚本时,dev/build/preview 可使用项目已安装的 Vite,不会自动下载构建工具。工程仍需先安装自身依赖。
41
42
 
43
+ 产品懒加载构建需要 Vite 库配置(`build.lib`)、Vue 3 编译器和入口中的 `import.meta.glob('./产品目录/**/*.vue', { eager: true })`。CLI 使用产品本地 Vite 加载原配置;未接入按需插件时通过临时配置补入共享插件,不修改产品源码或原配置。已有按需插件保留其 eager 等自定义设置。构建后验证本次生成的 `index.manifest.json`、组件 chunks 和 `index.full.js`,缺少或沿用旧产物会报错,避免完整包被误判为懒加载成功。部署时上传整个输出目录(包含 CSS、chunks 和兜底包)。`--full` 会关闭按需插件;`--full` 与 `--lazy` 互斥。旧 webpack 产品先迁移到 Vite,或使用 `--full` 完整打包。自定义非 Vite 脚本须自行生成标准 `lib/` 按需产物。直接执行 `npm run build:lib` 仍由产品原脚本决定构建方式。
44
+
42
45
  `--mode/-m`、`--port/-p` 支持旧的 `-mprd`、`-p3000` 写法。build 优先匹配 `build:<mode>`;`localize` 也匹配 `build:local`,其余情况向构建脚本传入 `--mode`。平台 `build:component` 包含多步构建,不追加 `--mode`,环境由工程脚本统一配置。
43
46
 
44
47
  平台 `epx build` 保留 `--thin`、`--no-designer`、`--no-dashboard`;产品工程不接受平台裁剪选项。需向工程脚本透传其他参数时使用 `--`,如 `epx dev -- --host 0.0.0.0`。脚本原有的 pre/post 生命周期、自定义环境和多步构建会保留,失败退出码向上传递。
package/dist/cli.mjs CHANGED
@@ -144,7 +144,10 @@ async function interactive (cwd, action) {
144
144
  }
145
145
  }
146
146
  if (action === 'lib') {
147
- if ((project.scripts['build:lib'] || project.scripts.lib) && await prompts.confirm('构建按需产品库?')) args.push('--lazy')
147
+ if (!platformLib) {
148
+ const mode = await prompts.select('产品库构建形态', [{ label: '懒加载(默认)', value: 'lazy' }, { label: '完整打包', value: 'full' }])
149
+ if (mode === 'full') args.push('--full')
150
+ }
148
151
  }
149
152
  if (action === 'pkg') args.push(await prompts.required('包名或 all', 'all'))
150
153
  if (action === 'lint' && await prompts.confirm('自动修复代码?')) args.push('--fix')
@@ -167,7 +170,7 @@ export async function main (argv = process.argv.slice(2), cwd = process.env.INIT
167
170
  epx / epx menu 交互菜单(↑↓ + Enter;管道支持数字输入)
168
171
  epx dev [-m <模式>] [-p <端口>] [--dir <工程目录>]
169
172
  epx build [-m <模式>] [--thin] [--no-designer] [--no-dashboard] [--dir <目录>]
170
- epx lib [--lazy] [-m <模式>] [--dir <目录>] 产品 build:lib / 平台 build:component
173
+ epx lib [--full | --lazy] [-m <模式>] [--dir <目录>] 产品默认懒加载,--full 完整打包;平台聚合库保持完整
171
174
  epx build --lib 同 epx lib
172
175
  epx preview [-p <端口>] [--dir <目录>]
173
176
  epx create <名称|路径> --epx-frame <目标版本> [--dir <目录>]
@@ -0,0 +1,54 @@
1
+ import h from"node:fs";import y from"node:path";import{build as B}from"epxing:project-vite";import j from"node:fs";import v from"node:path";import{parse as V,babelParse as T}from"epxing:project-sfc";function W(e){return j.readFileSync(e,"utf8").split(`\r
2
+ `).join(`
3
+ `)}function K(e){let s=e.split(`\r
4
+ `).join(`
5
+ `),n="";try{let{descriptor:r}=V(s);n=r.script&&r.script.content||r.scriptSetup&&r.scriptSetup.content||""}catch{return null}if(!n)return null;try{let i=T(n,{sourceType:"module",plugins:["jsx","typescript","decorators-legacy"]}).program.body,t=l=>{if(!l||l.type!=="ObjectExpression")return null;for(let u of l.properties){if(u.type!=="ObjectProperty"||u.computed)continue;if((u.key.type==="Identifier"?u.key.name:u.key.type==="StringLiteral"?u.key.value:null)==="name"&&u.value.type==="StringLiteral")return u.value.value}return null},a=i.find(l=>l.type==="ExportDefaultDeclaration");if(!a)return null;let o=t(a.declaration);if(o)return o;if(a.declaration.type==="Identifier"){let l=a.declaration.name;for(let u of i)if(u.type==="VariableDeclaration"){for(let m of u.declarations)if(m.id.type==="Identifier"&&m.id.name===l){let b=t(m.init);if(b)return b}}}if(a.declaration.type==="CallExpression")for(let l of a.declaration.arguments){let u=t(l);if(u)return u}return null}catch{return null}}function U(e){return e.replace(/(\.\/|\.vue|\/)/g,"")}function O(e,s){let n=v.resolve(e,s),r=[],i=t=>{let a=j.readdirSync(t,{withFileTypes:!0}).sort((o,l)=>o.name<l.name?-1:1);for(let o of a){let l=v.join(t,o.name);if(o.isDirectory())i(l);else if(o.name.endsWith(".vue")){let m="./"+v.relative(e,l).split(v.sep).join("/");r.push({globKey:m,file:l,key:K(W(l))||U(m)})}}};return j.existsSync(n)&&i(n),r.sort((t,a)=>t.globKey<a.globKey?-1:1)}function z(e){let s=new Map;for(let n of e){if(s.has(n.key))throw new Error(`[lazy-lib] \u7EC4\u4EF6\u6CE8\u518C\u540D\u51B2\u7A81\uFF1A'${n.key}' \u540C\u65F6\u6765\u81EA
6
+ ${s.get(n.key)}
7
+ ${n.file}
8
+ \u5355\u4F53\u5F62\u6001\u4E0B\u662F\u540E\u8005\u9759\u9ED8\u8986\u76D6\u524D\u8005\uFF1B\u6309\u9700\u5F62\u6001\u4E0B\u4E24\u4E2A chunk \u62A2\u540C\u4E00\u4E2A\u540D\u5B57\uFF0C\u5FC5\u987B\u5148\u6539\u6389\u5176\u4E2D\u4E00\u4E2A\u7EC4\u4EF6\u7684 name\uFF08\u6216\u6587\u4EF6\u8DEF\u5F84\uFF09\u3002`);s.set(n.key,n.file)}return e}var G=[{test:e=>e==="vue",global:"window.frame && window.frame.Vue"},{test:e=>e==="vuex",global:"window.frame && window.frame.vuex"},{test:e=>e==="vue-router",global:"window.frame && window.frame.router"},{test:e=>e==="epx-frame"||e.startsWith("epx-frame/"),global:"window.epxFrame"}],L=/^[A-Za-z_$][\w$]*$/,R=(e,s)=>{if(!s)return null;let n=typeof s=="function"?s(e):s[e];return typeof n=="string"&&n?n:null};function q(e,s){let n=G.find(i=>i.test(e));if(n)return n.global;let r=R(e,s);if(r)return`window.${r}`;throw new Error(`[lazy-lib] external '${e}' \u6CA1\u6709\u5BF9\u5E94\u7684\u8FD0\u884C\u65F6\u5168\u5C40\u6620\u5C04\u3002
9
+ ESM chunk \u91CC\u7684\u88F8\u8BF4\u660E\u7B26\u6D4F\u89C8\u5668\u89E3\u6790\u4E0D\u4E86\uFF0C\u5FC5\u987B\u7ECF shim \u6865\u5230 window \u4E0A\u7684\u5168\u5C40\u3002
10
+ \u5E73\u53F0\u5185\u7F6E\u6620\u5C04\uFF1Avue \u2192 window.frame.Vue\uFF1Bvuex \u2192 window.frame.vuex\uFF1B
11
+ vue-router \u2192 window.frame.router\uFF1Bepx-frame\uFF08\u542B\u5B50\u8DEF\u5F84\uFF09\u2192 window.epxFrame\u3002
12
+ \u4E09\u6761\u51FA\u8DEF\uFF1A
13
+ \u2460 \u5B83\u7531**\u4EA7\u54C1\u9879\u76EE**\u77E5\u9053\u5168\u5C40\u540D \u2192 \u5728 vite.config.mjs \u7684 output.globals \u91CC\u7ED9 '${e}' \u914D\u4E0A\uFF08\u63A8\u8350\uFF09\uFF1B
14
+ \u2461 \u5B83\u7531**\u5E73\u53F0**\u63D0\u4F9B \u2192 \u53BB build/lazy-lib/gen-shims.mjs \u7684 GLOBALS \u8865\u4E00\u6761\u6620\u5C04\uFF1B
15
+ \u2462 \u5B83\u4E0D\u8BE5\u88AB external\uFF08\u7EC4\u4EF6\u771F\u7684\u9700\u8981\u628A\u5B83\u6253\u8FDB\u4EA7\u7269\uFF09\u2192 \u4ECE external \u5217\u8868\u91CC\u53BB\u6389\u5B83\u3002`)}function S(e){return"shims/"+e.replace(/[^\w.-]/g,"_")+".js"}function D(e){let s=new Map;for(let n of e){let r=S(n);if(s.has(r)&&s.get(r)!==n)throw new Error(`[lazy-lib] shim \u6587\u4EF6\u540D\u51B2\u7A81\uFF1A'${n}' \u548C '${s.get(r)}' \u90FD\u751F\u6210 '${r}'\u3002
16
+ \u5F53\u524D\u7F16\u7801\u65B9\u6848\uFF08/ \u548C\u975E\u6CD5\u5B57\u7B26\u66FF\u6362\u4E3A _\uFF09\u5728 GLOBALS \u7684\u5C01\u95ED\u96C6\u5185\u662F\u5B89\u5168\u7684\u3002
17
+ \u5C06\u6765\u82E5\u771F\u9700\u8981\u652F\u6301\u4F1A\u78B0\u649E\u7684\u8BF4\u660E\u7B26\uFF0C\u518D\u6362\u7F16\u7801\u65B9\u6848\uFF08\u5982 hash \u540E\u7F00\uFF09\u3002`);s.set(r,n)}}function F(e,s,n){let r=q(e,n),i=[...s],t=i.filter(o=>o!=="default"&&L.test(o)).sort();for(let o of i)if(o!=="default"&&!L.test(o))throw new Error(`[lazy-lib] \u65E0\u6CD5\u4E3A external '${e}' \u751F\u6210 shim\uFF1Aimport binding '${o}' \u4E0D\u662F\u5408\u6CD5 JS \u6807\u8BC6\u7B26\u3002
18
+ \u539F\u56E0\uFF1A'*' \u6765\u81EA import * as ns from '...' \u7C7B\u547D\u540D\u7A7A\u95F4\u5BFC\u5165\uFF1Bshim \u65E0\u6CD5\u9759\u6001\u679A\u4E3E\u4F7F\u7528\u65B9\u4F1A\u8BBF\u95EE\u54EA\u4E9B\u540D\u5B57\u3002
19
+ \u6539\u6CD5\uFF1A\u6539\u7528\u5177\u540D\u5BFC\u5165\uFF0C\u5982 import { someExport } from ... \u800C\u975E import * as ns from ...`);return[`// \u6784\u5EFA\u671F\u751F\u6210\uFF08epx-frame lazy-lib\uFF09\uFF1A\u8FD0\u884C\u65F6\u4ECE ${r} \u53D6\u5E73\u53F0\u5355\u4F8B\uFF0C\u52FF\u624B\u6539\u3002`,`const S = ${r}`,...t.map(o=>`export const ${o} = S[${JSON.stringify(o)}]`),"export default (S && S.default) || S"].join(`
20
+ `)+`
21
+ `}function N(e){let s=new Map;for(let n in e){let r=e[n];if(r.type==="chunk")for(let i in r.importedBindings||{}){s.has(i)||s.set(i,new Set);for(let t of r.importedBindings[i])s.get(i).add(t)}}return s}import k from"node:path";var Z=(e,s)=>{let n=k.resolve(e),r=k.resolve(s);return n===r||n.startsWith(r+k.sep)||n.startsWith(r+"/")},E=e=>k.resolve(String(e).split("?")[0]);function A(e,s,n,r){let i=new Set((r||[]).map(E)),t=[...e()].filter(d=>Z(d,n)),a=new Set(t),o=new Map,l=[],u=[],m=d=>{let g=new Set;for(let f of d){let c=E(f);i.has(c)&&g.add(c)}return g.size},b=d=>{let g=o.get(d)||0;if(g===2)return;if(g===1){let c=[...l.slice(l.indexOf(d)),d];m(c)>=2&&u.push(c);return}o.set(d,1),l.push(d);let f=s(d)||{};for(let c of f.importedIds||[])a.has(c)&&b(c);l.pop(),o.set(d,2)};for(let d of t)b(d);return u}var $="\0epx-lazy-glob",I=/import\.meta\.glob\(\s*(['"])([^'"]+)\1\s*,\s*\{\s*eager\s*:\s*true\s*\}\s*\)/;function X(e={}){let{enabled:s=!1,glob:n,eager:r=[],pkgName:i}=e,t=null,a=null,o=[],l=new Set,u="",m="",b="",d="",g=[];return{name:"epx-lazy-lib",apply:"build",enforce:"pre",config(f){return s?(g=(Array.isArray(f.plugins)?f.plugins.flat(1/0):[]).filter(c=>c&&c.name!=="epx-lazy-lib"),{build:{emptyOutDir:!1}}):null},configResolved(f){t=f,m=y.resolve(f.root,f.build.outDir);let c=f.build.lib&&f.build.lib.entry;b=Array.isArray(c)?c[0]:typeof c=="object"?Object.values(c)[0]:c,u=y.dirname(b),d=i||y.basename(f.root)},async buildStart(){if(!s)return;n||this.error("[lazy-lib] \u7F3A\u5C11 glob \u9009\u9879\uFF08\u5F62\u5982 './<\u4EA7\u54C1\u540D>/**/*.vue'\uFF09");let f=Y(t.root,m);f&&this.error(f);let c=n.replace(/^\.\//,"").split("/")[0];o=z(O(u,c)),o.length||this.error(`[lazy-lib] \u6309 glob \u4E00\u4E2A .vue \u90FD\u6CA1\u626B\u5230\uFF0C\u65E0\u6CD5\u6309\u9700\u62C6\u5305\u3002
22
+ glob \u9009\u9879\uFF1A${n}
23
+ \u5B9E\u9645\u626B\u63CF\u76EE\u5F55\uFF1A${y.join(u,c)}\uFF08= \u5165\u53E3\u6240\u5728\u76EE\u5F55 ${u} + glob \u7B2C\u4E00\u6BB5 ${c}\uFF09
24
+ \u6700\u53EF\u80FD\u7684\u539F\u56E0\uFF1Aglob \u662F\u76F8\u5BF9**\u5165\u53E3\u6587\u4EF6\u6240\u5728\u76EE\u5F55**\u5199\u7684\uFF0C\u522B\u6309\u4ED3\u5E93\u6839\u591A\u5199\u4E00\u5C42 \`src/\`\u2014\u2014
25
+ \u5165\u53E3\u662F src/index.js \u65F6\u5E94\u5199 './<\u4EA7\u54C1\u540D>/**/*.vue'\uFF0C\u4E0D\u662F './src/<\u4EA7\u54C1\u540D>/**/*.vue'\u3002`),l=new Set([...r,...Q(b,u,o)]),h.rmSync(m,{recursive:!0,force:!0}),h.mkdirSync(m,{recursive:!0});let x=o.filter(p=>!l.has(p.key));a=await te(t,g,x,m,u,o)},resolveId(f){return f===$?$:null},load(f){return f!==$?null:re({pkgName:d,entries:o,eagerKeys:l,manifest:a})},transform(f,c){if(!s||y.resolve(c.split("?")[0])!==y.resolve(b))return null;let x=I.exec(f);return x||this.error(`[lazy-lib] \u5165\u53E3\u91CC\u6CA1\u627E\u5230\u671F\u671B\u5F62\u6001\u7684 import.meta.glob\u3002
26
+ \u671F\u671B\uFF1Aconst files = import.meta.glob('./<\u4EA7\u54C1\u540D>/**/*.vue', { eager: true })
27
+ \u6587\u4EF6\uFF1A${b}
28
+ \uFF08\u4E0D\u505A\u9759\u9ED8\u964D\u7EA7\uFF1A\u90A3\u4F1A\u6084\u6084\u6253\u51FA\u4E00\u4E2A\u5355\u4F53\uFF0C\u800C\u4F60\u4EE5\u4E3A\u81EA\u5DF1\u5F00\u4E86\u6309\u9700\u3002\uFF09`),x[2]!==n&&this.error(`[lazy-lib] \u63D2\u4EF6\u7684 glob \u9009\u9879\u4E0E\u5165\u53E3\u91CC import.meta.glob \u7684\u5B57\u9762\u91CF\u4E0D\u4E00\u81F4\uFF0C\u4E24\u8FB9\u5FC5\u987B\u9010\u5B57\u76F8\u540C\u3002
29
+ \u63D2\u4EF6\u9009\u9879\uFF1A${n}
30
+ \u5165\u53E3\u5B57\u9762\u91CF\uFF1A${x[2]}
31
+ \u6587\u4EF6\uFF1A${b}
32
+ \uFF08\u4E0D\u4E00\u81F4\u65F6\u58F3\u7684\u952E\u96C6\u4F1A\u4E0E\u6574\u4F53\u5355\u4F53\u5206\u53C9\uFF0C\u964D\u7EA7\u515C\u5E95\u4F1A\u627E\u4E0D\u5230\u7EC4\u4EF6\u3002\uFF09`),{code:`import __epxLazyGlob from ${JSON.stringify($)}
33
+ `+f.replace(I,"__epxLazyGlob"),map:null}},async closeBundle(){if(!s||!a)return;let f={};for(let c of o)f[c.key]=l.has(c.key)?null:a[c.key]||null;h.writeFileSync(y.join(m,"index.manifest.json"),JSON.stringify({v:1,pkgName:d,components:f,eager:[...l].sort()},null,2)+`
34
+ `),ee(m),M(m,t.build.copyPublicDir===!1?"":t.publicDir||""),await ne(t,g,b,m,d),J(m,"index.deps.json"),H(m,t.build.assetsDir)}}}function Y(e,s){let n=y.resolve(e),r=y.resolve(s);return r===n||!r.startsWith(n+y.sep)?`[lazy-lib] build.outDir \u5FC5\u987B\u662F\u9879\u76EE\u6839**\u4E4B\u5185**\u7684\u5B50\u76EE\u5F55\uFF0C\u4E14\u4E0D\u80FD\u5C31\u662F\u9879\u76EE\u6839\u672C\u8EAB\u3002
35
+ \u9879\u76EE\u6839\uFF1A${n}
36
+ outDir\uFF1A${r}
37
+ \u539F\u56E0\uFF1A\u6309\u9700\u62C6\u5305\u7684\u4E09\u8D9F\u4EA7\u7269\u8981\u843D\u8FDB\u540C\u4E00\u4E2A\u76EE\u5F55\uFF0C\u672C\u63D2\u4EF6\u4F1A\u5728\u6784\u5EFA\u5F00\u59CB\u65F6\u628A outDir \u6574\u4E2A\u5220\u6389\u91CD\u5EFA
38
+ \uFF08\u9012\u5F52\u3001\u5F3A\u5236\uFF09\uFF0C\u8FD9\u4E00\u6B65\u7ED5\u5F00\u4E86 vite \u81EA\u5DF1\u7684 emptyOutDir \u4FDD\u62A4\u3002outDir \u89E3\u6790\u6210\u9879\u76EE\u6839\u65F6\uFF0C
39
+ \u8FD9\u4E00\u5220\u4F1A\u628A\u4EA7\u54C1\u7684\u6E90\u7801\u4E00\u8D77\u5220\u6389\u3002\u6539\u6CD5\uFF1A\u7ED9 build.outDir \u4E00\u4E2A\u771F\u6B63\u7684\u5B50\u76EE\u5F55\uFF0C\u5982 'lib'\u3002`:null}function M(e,s,n=""){for(let r of h.readdirSync(e,{withFileTypes:!0})){let i=y.join(e,r.name),t=n?n+"/"+r.name:r.name;if(r.isDirectory())M(i,s,t);else if(r.name.endsWith(".css")){if(s&&h.existsSync(y.join(s,t)))continue;h.rmSync(i,{force:!0})}}}function H(e,s){let n=y.join(e,s||"assets");if(!h.existsSync(n))return[];let r=h.readdirSync(n,{withFileTypes:!0}).filter(t=>t.isFile()).map(t=>t.name);if(!r.length){try{h.rmdirSync(n)}catch{}return[]}let i=s||"assets";return console.warn(["",`[lazy-lib] \u26A0\uFE0F \u4EA7\u51FA\u4E86 ${r.length} \u4E2A\u6587\u4EF6\u578B\u8D44\u6E90\u5230 ${i}/\uFF1A`,...r.map(t=>" "+t),` \u8FDC\u7A0B\u5E93\u5F62\u6001\u4E0B\u5B83\u4EEC\u4F1A\u88AB\u5F15\u7528\u4E3A**\u7EDD\u5BF9\u8DEF\u5F84**\uFF08/${i}/xxx\uFF09\uFF0C`," \u89E3\u6790\u5230\u5E73\u53F0\u9875\u9762\u7684 origin \u6839\u76EE\u5F55 \u2192 \u8FD0\u884C\u65F6 404\uFF0C\u6784\u5EFA\u671F\u770B\u4E0D\u51FA\u6765\u3002"," \u4FEE\u6CD5\uFF1A\u628A build.assetsInlineLimit \u8C03\u5927\u5230\u8986\u76D6\u5B83\u4EEC\uFF08\u4EA7\u54C1\u9879\u76EE\u666E\u904D\u8BBE 50*1024\uFF09\uFF0C"," \u6216\u628A\u8D85\u5927\u8D44\u6E90\u5355\u72EC\u4F20\u6587\u4EF6\u670D\u52A1\u5668\u3001\u4EE3\u7801\u91CC\u5199\u5B8C\u6574 URL\u3002",""].join(`
40
+ `)),r}var C=e=>({assetsInlineLimit:e.assetsInlineLimit,assetsDir:e.assetsDir,target:e.target,cssTarget:e.cssTarget,sourcemap:e.sourcemap,minify:e.minify});function J(e,s,n=""){for(let r of h.readdirSync(e,{withFileTypes:!0})){let i=y.join(e,r.name),t=n?n+"/"+r.name:r.name;r.isDirectory()?J(i,s,t):r.name.endsWith(".deps.json")&&t!==s&&h.rmSync(i,{force:!0})}}function Q(e,s,n){let r=h.readFileSync(e,"utf8").split(`\r
41
+ `).join(`
42
+ `),i=[],t=/^\s*import\s+\w+\s+from\s+['"](\.[^'"]+\.vue)['"]/gm,a;for(;a=t.exec(r);){let o=y.resolve(y.dirname(e),a[1]),l=n.find(u=>y.resolve(u.file)===o);l&&i.push(l.key)}return i}function ee(e){let s=y.join(e,"index.deps.json"),n=y.join(e,"index.js");if(!h.existsSync(s)||!h.existsSync(n))return;let r=[],i=!1;try{let a=JSON.parse(h.readFileSync(s,"utf8"));r=Array.isArray(a.components)?a.components:[],i=!!a.all}catch{return}let t=`
43
+ /*__EPX_FRAME_DEPS__${JSON.stringify(r)}__*/
44
+ `+(i?`/*__EPX_FRAME_DEPS_ALL__*/
45
+ `:"");h.appendFileSync(n,t)}async function te(e,s,n,r,i,t){let a={};for(let p of n)a[p.key]=p.file;let o=new Set,l=(t||n).map(p=>p.file),u=await B({root:e.root,logLevel:"silent",configFile:!1,plugins:[...s,{name:"epx-lazy-lib-cycle-guard",buildEnd(){let p=A(()=>this.getModuleIds(),_=>this.getModuleInfo(_),i,l);p.length&&this.error(`[lazy-lib] \u5165\u53E3\u7EC4\u4EF6\u4E4B\u95F4\u5B58\u5728\u5FAA\u73AF\u4F9D\u8D56\uFF0C\u6309\u9700\u62C6\u5305\u4F1A\u5728\u8FD0\u884C\u65F6\u89E6\u53D1 TDZ\uFF08Cannot access ... before initialization\uFF09\u5E76\u767D\u5C4F\uFF1A
46
+ `+p.map(_=>" "+_.join(`
47
+ -> `)).join(`
48
+ `)+`
49
+ \u4E00\u4E2A .vue = \u4E00\u4E2A chunk\uFF0C\u6210\u73AF\u7684\u4E24\u4E2A\u7EC4\u4EF6\u8DE8 chunk \u540E\u521D\u59CB\u5316\u987A\u5E8F\u65AD\u88C2\u3002\u4E24\u6761\u51FA\u8DEF\uFF1A
50
+ \u2460 \u628A\u73AF\u62C6\u6389\uFF1A\u62BD\u51FA\u53CC\u65B9\u90FD\u8981\u7684\u90A3\u90E8\u5206\u5230\u72EC\u7ACB\u6A21\u5757\uFF0C\u6539\u6210\u4E24\u4E2A\u7EC4\u4EF6\u90FD import \u5B83\uFF1B
51
+ \u2461 \u8FD9\u4E2A\u5E93\u6682\u65F6\u4E0D\u7528\u6309\u9700\u5F62\u6001\uFF0C\u56DE\u5230\u6574\u4F53\u5355\u4F53\uFF08\u4E0D\u8BBE LAZY_LIB / enabled:false\uFF09\u3002
52
+ \uFF08eager \u9009\u9879\u5E2E\u4E0D\u4E0A\u5FD9\uFF1A\u5B83\u53EA\u628A .vue \u4ECE\u7B2C\u2460\u8D9F\u7684 input \u91CC\u62FF\u6389\uFF0C\u7EC4\u4EF6\u4ECD\u5728\u6A21\u5757\u56FE\u91CC\uFF0C\u73AF\u8FD8\u5728\u3002\uFF09`)}}],css:e.css,resolve:e.resolve,define:e.define,publicDir:!1,build:{outDir:r,emptyOutDir:!1,cssCodeSplit:!1,...C(e.build),rollupOptions:{input:a,external:e.build.rollupOptions.external,preserveEntrySignatures:"strict",output:{format:"es",entryFileNames:"chunks/[name]-[hash].js",chunkFileNames:"chunks/[name]-[hash].js",paths:p=>(o.add(p),"./"+S(p))}}}}),m=Array.isArray(u)?u[0]:u,b=m.output.reduce((p,_)=>(p[_.fileName]=_,p),{}),d=e.build.rollupOptions.output&&e.build.rollupOptions.output.globals,g=N(b),f=new Map([...o].map(p=>["./"+S(p),p])),c=new Map;for(let[p,_]of g){let w=f.get(p);if(w){c.has(w)||c.set(w,new Set);for(let P of _)c.get(w).add(P)}}D([...o]);for(let[p,_]of c){let w=y.join(r,"chunks",S(p));h.mkdirSync(y.dirname(w),{recursive:!0}),h.writeFileSync(w,F(p,_,d))}let x={};for(let p of m.output)p.type==="chunk"&&p.isEntry&&(x[p.name]=p.fileName);return x}async function ne(e,s,n,r,i){await B({root:e.root,logLevel:"silent",configFile:!1,plugins:s,css:e.css,resolve:e.resolve,define:e.define,publicDir:!1,build:{outDir:r,emptyOutDir:!1,cssCodeSplit:!1,...C(e.build),lib:{entry:n,name:`__epxFullLibs__.${i}`,formats:["umd"],fileName:()=>"index.full.js"},rollupOptions:{external:e.build.rollupOptions.external,output:{globals:e.build.rollupOptions.output&&e.build.rollupOptions.output.globals,exports:"default",assetFileNames:t=>(t.names||[t.name]).some(a=>String(a).endsWith(".css"))?"index.css":"assets/[name]-[hash][extname]"}}}})}function re({pkgName:e,entries:s,eagerKeys:n,manifest:r}){let i=s.filter(o=>n.has(o.key)),t=[];i.forEach((o,l)=>{t.push(`import __eager${l} from ${JSON.stringify(o.file.split(y.sep).join("/"))}`)});let a={};for(let o of s)n.has(o.key)||(a[o.key]=r[o.key]);return t.push(""),t.push("// \u6784\u5EFA\u671F\u751F\u6210\uFF08epx-frame lazy-lib\uFF09\uFF1A\u628A import.meta.glob \u7684 eager \u7ED3\u679C\u6362\u6210\u6309\u9700 wrapper\u3002"),t.push(`const __pkg = ${JSON.stringify(e)}`),t.push(`const __manifest = ${JSON.stringify(a,null,2)}`),t.push("const __frame = (typeof window !== 'undefined' && window.frame) || {}"),t.push("const __cur = (typeof document !== 'undefined' && document.currentScript && document.currentScript.src) || ''"),t.push("const __base = __cur ? __cur.slice(0, __cur.lastIndexOf('/') + 1) : ''"),t.push("let __lazy = {}"),t.push("if (typeof __frame.registerLazyLibrary === 'function') {"),t.push(" __lazy = __frame.registerLazyLibrary(__pkg, { base: __base, components: __manifest, full: 'index.full.js' })"),t.push("} else if (__frame.Vue && __frame.Vue.defineAsyncComponent) {"),t.push(" // \u964D\u7EA7\u2461\uFF1A\u5E73\u53F0\u6CA1\u6709\u8BE5 API\uFF08\u65E7\u7248\u5E73\u53F0\uFF09\u65F6\u58F3\u81EA\u5EFA wrapper\uFF0C\u529F\u80FD\u7B49\u4EF7\uFF0C\u65E0\u96C6\u4E2D\u9884\u53D6\u3002"),t.push(" for (const k in __manifest) {"),t.push(" const f = __manifest[k]"),t.push(" const w = __frame.Vue.defineAsyncComponent(() => import(/* @vite-ignore */ __base + f))"),t.push(" w.name = k"),t.push(" __lazy[k] = w"),t.push(" }"),t.push("} else {"),t.push(" console.error('[frame.lazy] \u5E73\u53F0\u65E2\u6CA1\u6709 registerLazyLibrary \u4E5F\u6CA1\u6709 Vue \u2014\u2014 \u8BF7\u5347\u7EA7\u5E73\u53F0\uFF0C\u6216\u6539\u7528\u6574\u4F53\u5355\u4F53\u5F62\u6001\u6784\u5EFA\u672C\u5E93', __pkg)"),t.push("}"),t.push("const __out = {}"),s.forEach(o=>{let l=i.indexOf(o),u=l>=0?`__eager${l}`:`__lazy[${JSON.stringify(o.key)}]`;t.push(`__out[${JSON.stringify(o.globKey)}] = { default: ${u} }`)}),t.push("export default __out"),t.join(`
53
+ `)+`
54
+ `}export{Y as checkOutDir,X as default,M as rmCssFiles,J as rmStrayDepsJson,H as warnEmittedAssets,ee as writeFrameDepsMarker};
@@ -0,0 +1,115 @@
1
+ /* eslint-disable no-console -- 输出实际构建形态 */
2
+ import fs from 'node:fs'
3
+ import path from 'node:path'
4
+ import { createRequire } from 'node:module'
5
+ import { fileURLToPath, pathToFileURL } from 'node:url'
6
+ import { manifestStamp } from './lib-vite-config.mjs'
7
+
8
+ const moduleDir = path.dirname(fileURLToPath(import.meta.url))
9
+
10
+ function compilerEntry (require) {
11
+ const contexts = [require]
12
+ for (const name of ['@vitejs/plugin-vue', 'vue', 'epx-frame']) {
13
+ try { contexts.push(createRequire(require.resolve(name))) } catch (error) { /* 尝试其他项目依赖 */ }
14
+ }
15
+ for (const resolve of contexts) {
16
+ for (const name of ['@vue/compiler-sfc', 'vue/compiler-sfc']) {
17
+ try { return resolve.resolve(name) } catch (error) { /* 继续查找 */ }
18
+ }
19
+ }
20
+ throw new Error('按需构建需要产品本地的 Vue 3 SFC 编译器,请先安装产品依赖。')
21
+ }
22
+
23
+ function configArgument (args) {
24
+ let value
25
+ for (let i = 0; i < args.length; i++) {
26
+ if (args[i] === '--config' || args[i] === '-c') value = args[++i]
27
+ else if (args[i].startsWith('--config=')) value = args[i].slice('--config='.length)
28
+ }
29
+ return value
30
+ }
31
+
32
+ function configInScript (script) {
33
+ const matches = [...String(script).matchAll(/(?:^|\s)(?:--config|-c)(?:=|\s+)(?:"([^"]+)"|'([^']+)'|([^\s]+))/g)]
34
+ const last = matches.at(-1)
35
+ return last && (last[1] || last[2] || last[3])
36
+ }
37
+
38
+ function verifyLazy (outDir, previousStamp) {
39
+ const file = path.join(outDir, 'index.manifest.json')
40
+ if (!fs.existsSync(file) || manifestStamp(outDir) === previousStamp || !fs.existsSync(path.join(outDir, 'index.full.js'))) {
41
+ throw new Error('没有生成新的按需库清单和完整兜底包,不能视为懒加载构建成功。请检查构建脚本或使用 epx lib --full。')
42
+ }
43
+ const manifest = JSON.parse(fs.readFileSync(file, 'utf8'))
44
+ const entries = Object.entries(manifest.components || {})
45
+ if (!entries.length) throw new Error('按需库组件清单为空')
46
+ for (const [name, chunk] of entries) {
47
+ if (chunk === null && manifest.eager?.includes(name)) continue
48
+ if (typeof chunk !== 'string') throw new Error('按需组件没有对应的 chunk:' + name)
49
+ const resolved = path.resolve(outDir, chunk)
50
+ const relative = path.relative(outDir, resolved)
51
+ if (relative.startsWith('..') || path.isAbsolute(relative) || !fs.existsSync(resolved)) throw new Error('按需组件 chunk 不存在或路径无效:' + name)
52
+ }
53
+ console.log('✓ 按需库已验证:组件清单、组件 chunks、index.full.js;部署时请上传整个输出目录。')
54
+ }
55
+
56
+ export function prepareLibraryBuild (project, plan) {
57
+ if (!plan.libraryMode) return { args: plan.args, verify () {}, cleanup () {} }
58
+ const lazy = plan.libraryMode === 'lazy'
59
+ console.log('产品库构建形态:' + (lazy ? '懒加载(默认)' : '完整(--full)'))
60
+ const require = createRequire(path.join(project.dir, 'package.json'))
61
+ let viteAvailable = false
62
+ try { require.resolve('vite/package.json'); viteAvailable = true } catch (error) { /* 兼容自定义非 Vite 构建脚本 */ }
63
+ const explicitConfig = configArgument(plan.args) || configInScript(project.scripts[plan.script])
64
+ const configFile = explicitConfig
65
+ ? path.resolve(project.dir, explicitConfig)
66
+ : ['js', 'mjs', 'ts', 'cjs', 'mts', 'cts'].map(ext => path.join(project.dir, 'vite.config.' + ext)).find(file => fs.existsSync(file))
67
+ if (explicitConfig && !fs.existsSync(configFile)) throw new Error('Vite 配置不存在:' + configFile)
68
+ if (!viteAvailable || !configFile) {
69
+ const outDir = path.join(project.dir, 'lib')
70
+ const previousStamp = manifestStamp(outDir)
71
+ return { args: plan.args, verify () { if (lazy) verifyLazy(outDir, previousStamp) }, cleanup () {} }
72
+ }
73
+ const cache = path.resolve(project.dir, 'node_modules/.cache/epxing-cli')
74
+ fs.mkdirSync(cache, { recursive: true })
75
+ const dir = fs.mkdtempSync(path.join(cache, 'lib-'))
76
+ const cleanup = () => {
77
+ const resolved = path.resolve(dir)
78
+ if (path.dirname(resolved) !== cache || !path.basename(resolved).startsWith('lib-')) throw new Error('Unexpected library build cache directory')
79
+ fs.rmSync(resolved, { recursive: true, force: true })
80
+ }
81
+ try {
82
+ const receipt = path.join(dir, 'receipt.json')
83
+ const wrapper = path.join(dir, 'vite.config.mjs')
84
+ let importLazy = ''
85
+ if (lazy) {
86
+ const source = fs.readFileSync(path.resolve(moduleDir, '../dist/lazy-lib.mjs'), 'utf8')
87
+ const compiler = pathToFileURL(compilerEntry(require)).href
88
+ const prepared = source.replaceAll('"epxing:project-vite"', '"vite"').replaceAll('"epxing:project-sfc"', JSON.stringify(compiler))
89
+ if (prepared.includes('"epxing:project-')) throw new Error('按需构建资源包含未解析的项目依赖')
90
+ fs.writeFileSync(path.join(dir, 'lazy-lib.mjs'), prepared)
91
+ importLazy = "import lazyLib from './lazy-lib.mjs'\n"
92
+ }
93
+ const options = JSON.stringify({ projectRoot: project.dir, configFile, lazy, receipt, pkgName: project.pkg.name })
94
+ fs.writeFileSync(wrapper, "import { loadConfigFromFile } from 'vite'\n" +
95
+ `import { configureLibrary } from ${JSON.stringify(pathToFileURL(path.join(moduleDir, 'lib-vite-config.mjs')).href)}\n` + importLazy +
96
+ `export default env => configureLibrary(env, { ...${options}, loadConfigFromFile${lazy ? ', lazyLib' : ''} })\n`)
97
+ const args = []
98
+ for (let i = 0; i < plan.args.length; i++) {
99
+ const arg = plan.args[i]
100
+ if (arg === '--config' || arg === '-c') { i++; continue }
101
+ if (!arg.startsWith('--config=')) args.push(arg)
102
+ }
103
+ args.push('--config', wrapper)
104
+ return {
105
+ args,
106
+ cleanup,
107
+ verify () {
108
+ if (!fs.existsSync(receipt)) throw new Error('产品脚本没有使用本次 Vite 构建配置,无法确认构建形态。请检查脚本是否透传 --config。')
109
+ const result = JSON.parse(fs.readFileSync(receipt, 'utf8'))
110
+ if (lazy) verifyLazy(result.outDir, result.previousStamp)
111
+ else console.log('✓ 完整产品库构建完成。')
112
+ }
113
+ }
114
+ } catch (error) { cleanup(); throw error }
115
+ }
@@ -0,0 +1,54 @@
1
+ import fs from 'node:fs'
2
+ import path from 'node:path'
3
+
4
+ async function flatten (plugins) {
5
+ const result = []
6
+ for (const value of plugins || []) {
7
+ const plugin = await value
8
+ if (Array.isArray(plugin)) result.push(...await flatten(plugin))
9
+ else if (plugin) result.push(plugin)
10
+ }
11
+ return result
12
+ }
13
+
14
+ export function manifestStamp (outDir) {
15
+ try { return fs.statSync(path.join(outDir, 'index.manifest.json'), { bigint: true }).mtimeNs.toString() } catch (error) { return null }
16
+ }
17
+
18
+ // 在产品自己的 Vite 中执行。通过 loadConfigFromFile 保留原配置文件位置、函数配置和 TS 支持。
19
+ export async function configureLibrary (env, options) {
20
+ const { projectRoot, configFile, lazy, receipt, loadConfigFromFile, lazyLib, pkgName } = options
21
+ process.env.LAZY_LIB = lazy ? 'true' : 'false'
22
+ const loaded = await loadConfigFromFile(env, configFile, projectRoot)
23
+ if (!loaded) throw new Error('无法加载产品 Vite 配置:' + configFile)
24
+ const config = loaded.config
25
+ const plugins = await flatten(config.plugins)
26
+ const root = path.resolve(projectRoot, config.root || '.')
27
+ if (!config.build?.lib) throw new Error('当前 Vite 配置没有 build.lib,请为产品配置远程库入口。')
28
+ if (!lazy) {
29
+ config.plugins = plugins.filter(plugin => plugin.name !== 'epx-lazy-lib')
30
+ } else {
31
+ config.plugins = plugins
32
+ if (!plugins.some(plugin => plugin.name === 'epx-lazy-lib')) {
33
+ const entry = config.build.lib.entry
34
+ const file = Array.isArray(entry) ? entry[0] : typeof entry === 'object' ? Object.values(entry)[0] : entry
35
+ if (!file) throw new Error('无法确定产品库入口 build.lib.entry')
36
+ const source = fs.readFileSync(path.resolve(root, file), 'utf8')
37
+ const match = /import\.meta\.glob\(\s*(['"])([^'"]+)\1\s*,\s*\{\s*eager\s*:\s*true\s*\}\s*\)/.exec(source)
38
+ if (!match) throw new Error('按需库需要入口中的 eager import.meta.glob;请先迁移产品入口,或使用 epx lib --full。')
39
+ config.plugins.push(lazyLib({ enabled: true, glob: match[2], pkgName }))
40
+ }
41
+ }
42
+ // 嵌套的 chunks/full 构建会复用插件对象,仅记录外层构建,避免覆盖构建前的时间戳。
43
+ let recorded = false
44
+ config.plugins.push({
45
+ name: 'epxing-cli-library-receipt',
46
+ configResolved (resolved) {
47
+ if (recorded) return
48
+ recorded = true
49
+ const outDir = path.resolve(resolved.root, resolved.build.outDir)
50
+ fs.writeFileSync(receipt, JSON.stringify({ outDir, previousStamp: manifestStamp(outDir) }))
51
+ }
52
+ })
53
+ return config
54
+ }
package/dist/project.mjs CHANGED
@@ -3,6 +3,7 @@ import fs from 'node:fs'
3
3
  import path from 'node:path'
4
4
  import { createRequire } from 'node:module'
5
5
  import { spawn } from 'node:child_process'
6
+ import { prepareLibraryBuild } from './lib-build.mjs'
6
7
 
7
8
  export function findProject (cwd) {
8
9
  let dir = path.resolve(cwd)
@@ -92,6 +93,7 @@ export function parseProjectArgs (argv) {
92
93
  noDesigner: flag('--no-designer'),
93
94
  noDashboard: flag('--no-dashboard'),
94
95
  lazy: flag('--lazy'),
96
+ full: flag('--full'),
95
97
  lib: flag('--lib'),
96
98
  fix: flag('--fix'),
97
99
  forward,
@@ -109,11 +111,15 @@ export function projectPlan (command, options, project) {
109
111
  const env = {}
110
112
  let script
111
113
  let binary
114
+ let libraryMode
112
115
  const platform = project.pkg.name === 'epx-frame' || !!scripts['build:component']
113
116
  if (options.lib) {
114
117
  if (command !== 'build') throw new Error('--lib 用于 epx build;也可直接使用 epx lib')
115
118
  command = 'lib'
116
119
  }
120
+ if (command === 'build' && !scripts.build && scripts['build:lib']) command = 'lib'
121
+ if (options.lazy && options.full) throw new Error('--lazy 和 --full 不能同时使用')
122
+ if (options.full && command !== 'lib') throw new Error('--full 仅用于 lib 或 build --lib')
117
123
  if (options.positional.length && !(command === 'pkg' && options.positional.length === 1 && !options.positional[0].startsWith('-'))) throw new Error(`不支持的参数:${options.positional.join(' ')}`)
118
124
  if (options.port && !['dev', 'preview'].includes(command)) throw new Error('--port 仅用于 dev 或 preview')
119
125
  if (options.lazy && command !== 'lib') throw new Error('--lazy 仅用于 lib 或 build --lib')
@@ -132,12 +138,18 @@ export function projectPlan (command, options, project) {
132
138
  if (options.noDesigner) env.INCLUDE_DESIGNER = 'false'
133
139
  if (options.noDashboard) env.INCLUDE_DASHBOARD = 'false'
134
140
  } else if (command === 'lib') {
135
- script = options.lazy ? pick('build:lib:lazy') : pick('build:lib', 'lib', 'build:component')
136
- if (options.lazy && !script) {
137
- script = pick('build:lib', 'lib')
138
- env.LAZY_LIB = 'true'
141
+ if (platform && !pick('build:lib', 'lib', 'build:lib:lazy')) {
142
+ if (options.lazy) throw new Error('平台聚合库不支持产品懒加载模式')
143
+ script = pick('build:component')
144
+ } else {
145
+ libraryMode = options.full ? 'full' : 'lazy'
146
+ env.LAZY_LIB = options.full ? 'false' : 'true'
147
+ script = options.full ? pick('build:lib:full', 'build:lib', 'lib', 'build:lib:lazy') : pick('build:lib:lazy', 'build:lib', 'lib')
148
+ if (!script && /--target(?:=|\s+)lib\b/.test(scripts.build || '')) {
149
+ if (!options.full) throw new Error('旧 webpack 产品请先迁移到 Vite,或使用 epx lib --full 完整打包')
150
+ script = 'build'
151
+ }
139
152
  }
140
- if (!script && !options.lazy && /--target(?:=|\s+)lib\b/.test(scripts.build || '')) script = 'build'
141
153
  if (!script) throw new Error('当前工程缺少库构建脚本。产品请提供 build:lib(按需库可用 build:lib:lazy),平台使用 build:component。')
142
154
  if (script === 'build:component' && options.mode) throw new Error('平台聚合库包含多步构建,请在 build:component 中配置环境;epx lib 不向该聚合脚本追加 --mode。')
143
155
  } else if (command === 'preview') {
@@ -158,7 +170,7 @@ export function projectPlan (command, options, project) {
158
170
  if (options.port) args.push('--port', options.port)
159
171
  args.push(...options.forward)
160
172
  if (['build', 'lib', 'remote', 'pkg'].includes(command) && !process.env.NODE_OPTIONS) env.NODE_OPTIONS = '--max-old-space-size=8192'
161
- return { command, script, binary, args, env }
173
+ return { command, script, binary, args, env, libraryMode }
162
174
  }
163
175
 
164
176
  export async function runProject (command, argv, cwd) {
@@ -167,8 +179,13 @@ export async function runProject (command, argv, cwd) {
167
179
  const plan = projectPlan(command, options, project)
168
180
  console.log(`项目:${project.pkg.name || project.dir}(${project.dir})`)
169
181
  if (plan.script) {
170
- console.log(`→ npm run ${plan.script}${plan.args.length ? ' -- ' + plan.args.join(' ') : ''}`)
171
- return execute(npmEntry(), ['run', plan.script, ...(plan.args.length ? ['--', ...plan.args] : [])], project.dir, plan.env)
182
+ const library = prepareLibraryBuild(project, plan)
183
+ try {
184
+ console.log(`→ npm run ${plan.script}${library.args.length ? ' -- ' + library.args.join(' ') : ''}`)
185
+ const code = await execute(npmEntry(), ['run', plan.script, ...(library.args.length ? ['--', ...library.args] : [])], project.dir, plan.env)
186
+ if (code === 0) library.verify()
187
+ return code
188
+ } finally { library.cleanup() }
172
189
  }
173
190
  const file = localBinary(project, plan.binary)
174
191
  if (!file) throw new Error(`当前工程没有可用脚本或本地 ${plan.binary},请先在工程内安装依赖并配置相应脚本。`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epxing/cli",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "EPX 交互式工程开发、服务启动、构建、Vue3 迁移及技能安装工具",
5
5
  "type": "module",
6
6
  "bin": {