neo-cmp-cli 1.9.25 → 1.9.27

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
@@ -16,10 +16,10 @@ neo-cmp-cli 是 Neo 自定义组件开发工具,基于 [AKFun](https://github.
16
16
 
17
17
  ## ✨ 主要特性
18
18
 
19
- - **零配置**: 内置默认配置,开箱可用
20
- - **多技术栈**: 支持 Vue2、React、React+TypeScript 自定义组件的调试、构建与发布
21
- - **多构建场景**: 本地预览(含热更新/代理)、外链调试、库构建(UMD/ESM)、部署&发布
22
- - **灵活可配**: 支持构建入口、别名、代理、SASS 注入、ESLint/StyleLint、Babel/Loader/Plugin 扩展等配置
19
+ - **零配置**: 内置默认配置(前端工程最佳实践),开箱可用
20
+ - **多技术体系支持**: 支持 Vue2、React、React+TypeScript 多种技术栈类型自定义组件的调试、构建与发布
21
+ - **多种构建类型**: 本地预览(含热更新/代理)、外链调试、库构建(UMD/ESM)、部署&发布
22
+ - **灵活可配**: 支持构建入口、别名、代理、公共样式注入、ESLint/StyleLint、Babel/Loader/Plugin 扩展等配置
23
23
  - **样式与规范**: 内置 Autoprefixer、Sass、PostCSS、ESLint、StyleLint
24
24
  - **发布至 NeoCRM 平台**: 支持一键发布到 NeoCRM 平台的能力,需自行补充授权配置
25
25
 
@@ -160,6 +160,19 @@ OAuth2 授权码模式更加安全可靠,无需用户配置账户名和密码
160
160
 
161
161
  功能:清除本地保存的 token 文件,下次使用需要重新登录。
162
162
 
163
+ ##### neo login 选择「自定义环境」时的授权配置示例
164
+
165
+ ```javascript
166
+ // neo.config.js
167
+ module.exports = {
168
+ neoConfig: {
169
+ neoBaseURL: 'https://crm-xx.xiaoshouyi.com', // 平台根地址(默认:https://crm.xiaoshouyi.com)
170
+ loginURL: 'https://login-xx.xiaoshouyi.com/auc/oauth2/auth', // 登录授权 URL(用于获取 code)
171
+ tokenAPI: 'https://login-xx.xiaoshouyi.com/auc/oauth2/token', // Token 获取接口地址
172
+ },
173
+ }
174
+ ```
175
+
163
176
  #### Token 有效期
164
177
 
165
178
  - **access_token**:默认有效期 2 小时
package/dist/neo/env.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("lodash");var t,e;exports.__require=function(){if(e)return t;e=1;const a=o,s={neoBaseURL:"https://crm.xiaoshouyi.com",loginURL:"https://login.xiaoshouyi.com/auc/oauth2/auth",tokenURL:"https://login.xiaoshouyi.com/auc/oauth2/token"},n={neoBaseURL:"https://crm-gray.xiaoshouyi.com",loginURL:"https://login.xiaoshouyi.com/auc/oauth2/auth",tokenURL:"https://login.xiaoshouyi.com/auc/oauth2/token"},u={neoBaseURL:"https://crm-sandbox.xiaoshouyi.com",loginURL:"https://login-sandbox.xiaoshouyi.com/auc/oauth2/auth",tokenURL:"https://login-sandbox.xiaoshouyi.com/auc/oauth2/token"},i={neoBaseURL:"https://crm-cd.xiaoshouyi.com",loginURL:"https://login-cd.xiaoshouyi.com/auc/oauth2/auth",tokenURL:"https://login-cd.xiaoshouyi.com/auc/oauth2/token"},c={neoBaseURL:"https://crm-cd.xiaoshouyi.com",loginURL:"https://login-cd.xiaoshouyi.com/auc/oauth2/auth",tokenURL:"https://login-cd.xiaoshouyi.com/auc/oauth2/token",delete:"/rest/metadata/v3.0/ui/customComponents",saveAPI:"/rest/metadata/v3.0/ui/customComponents/actions/saveOrUpdateComponent",queryAll:"/rest/metadata/v3.0/ui/components/filter?custom=true",getCodeLibAPI:o=>`/rest/metadata/v3.0/ui/customComponents/${o}/codeLib`,uploadAPI:"/rest/metadata/v3.0/ui/customComponents/actions/upload",query:"/rest/metadata/v3.0/ui/customComponents/actions/queryCustomComponents"};return t={DefaultNeoCrmAPI:c,getNeoCrmAPI:(o="cd",t={})=>{let e=a.cloneDeep(c),h=i;switch(o){case"production":h=s;break;case"gray":h=n;break;case"sandbox":h=u;break;case"cd":default:h=i;break;case"custom":h={...t,neoBaseURL:t.neoBaseURL,loginURL:t.loginURL||t.loginAPI,tokenURL:t.tokenURL||t.tokenAPI}}return{...e,...h}}}};
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("lodash");var t,e;exports.__require=function(){if(e)return t;e=1;const a=o,s={neoBaseURL:"https://crm.xiaoshouyi.com",loginURL:"https://login.xiaoshouyi.com/auc/oauth2/auth",tokenURL:"https://login.xiaoshouyi.com/auc/oauth2/token"},n={neoBaseURL:"https://crm-gray.xiaoshouyi.com",loginURL:"https://login.xiaoshouyi.com/auc/oauth2/auth",tokenURL:"https://login.xiaoshouyi.com/auc/oauth2/token"},u={neoBaseURL:"https://crm-sandbox.xiaoshouyi.com",loginURL:"https://login-sandbox.xiaoshouyi.com/auc/oauth2/auth",tokenURL:"https://login-sandbox.xiaoshouyi.com/auc/oauth2/token"},i={neoBaseURL:"https://crm-cd.xiaoshouyi.com",loginURL:"https://login-cd.xiaoshouyi.com/auc/oauth2/auth",tokenURL:"https://login-cd.xiaoshouyi.com/auc/oauth2/token"},c={neoBaseURL:"https://crm-cd.xiaoshouyi.com",loginURL:"https://login-cd.xiaoshouyi.com/auc/oauth2/auth",tokenURL:"https://login-cd.xiaoshouyi.com/auc/oauth2/token",delete:"/rest/metadata/v3.0/ui/customComponents",saveAPI:"/rest/metadata/v3.0/ui/customComponents/actions/saveOrUpdateComponent",queryAll:"/rest/metadata/v3.0/ui/components/filter?custom=true",getCodeLibAPI:o=>`/rest/metadata/v3.0/ui/customComponents/${o}/codeLib`,uploadAPI:"/rest/metadata/v3.0/ui/customComponents/actions/upload",query:"/rest/metadata/v3.0/ui/customComponents/actions/queryCustomComponents",getUserInfoAPI:"/rest/auc/v2.0/userInfo"};return t={DefaultNeoCrmAPI:c,getNeoCrmAPI:(o="cd",t={})=>{let e=a.cloneDeep(c),h=i;switch(o){case"production":h=s;break;case"gray":h=n;break;case"sandbox":h=u;break;case"cd":default:h=i;break;case"custom":h={...t,neoBaseURL:t.neoBaseURL,loginURL:t.loginURL||t.loginAPI,tokenURL:t.tokenURL||t.tokenAPI}}return{...e,...h}}}};
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("axios"),t=require("node:fs"),n=require("node:path"),s=require("ora"),r=require("node:http"),o=require("node:url"),i=require("open"),a=require("node:net"),c=require("lodash"),l=require("../utils/common.js"),h=require("./neoEnvManager.js");var p,u;exports.__require=function(){if(u)return p;u=1;const d=e,g=t,y=n,k=s,m=r,_=o,T=i,w=a,f=c,{errorLog:R,successLog:S}=l.__require(),U=h.__require();return p=class{constructor(e){if(!e)return;const{loginURL:t,tokenURL:n}=e,s=U.getAuthConfig(),{redirectUri:r,authType:o,auth:i}=s;if(!t||!n)throw new Error("auth.config.js 配置不完整,需要包含 loginURL、tokenURL");this.loginURL=t,this.tokenURL=n,this.redirectUri=r,this.authType=o||"oauth2",this.response_type=i.response_type||"code",this.client_id=i.client_id,this.client_secret=i.client_secret,this.scope=i.scope||"all",this.oauthType=i.oauthType||"standard",this.access_type=i.access_type||"offline",this.grant_type=i.grant_type||"authorization_code",this.tokenDir=y.join(process.cwd(),".neo-cli"),this.tokenFile=y.join(this.tokenDir,"token.json"),this.pageDir=y.join(__dirname,"../../template/pageHtml"),this.authErrorTemplate=y.join(this.pageDir,"auth-error.html"),this.authSuccessTemplate=y.join(this.pageDir,"auth-success.html"),this.tokenErrorTemplate=y.join(this.pageDir,"token-error.html"),this.authFailedTemplate=y.join(this.pageDir,"auth-failed.html");try{U.setEnvConfig(f.pick(e,["neoBaseURL","loginURL","tokenURL"]))}catch(e){R(`保存环境配置失败: ${e.message||e.msg}`)}}ensureTokenDir(){g.existsSync(this.tokenDir)||g.mkdirSync(this.tokenDir,{recursive:!0})}saveToken(e){this.ensureTokenDir();const t={...e,savedAt:Date.now(),expiresAt:Date.now()+1e3*(e.expires_in||7200)};g.writeFileSync(this.tokenFile,JSON.stringify(t,null,2),"utf-8")}readToken(){if(!g.existsSync(this.tokenFile))return null;try{return JSON.parse(g.readFileSync(this.tokenFile,"utf-8"))}catch(e){return R(`读取 token 文件失败: ${e.message||e.msg}`),null}}isTokenExpired(e){return!e||!e.expiresAt||Date.now()>=e.expiresAt-3e5}clearToken(){g.existsSync(this.tokenFile)&&g.unlinkSync(this.tokenFile)}getRedirectURI(e){return`http://localhost:${e}`}buildAuthUrl(e){const t=new URLSearchParams({response_type:this.response_type,client_id:this.client_id,redirect_uri:e,scope:this.scope,oauthType:this.oauthType,access_type:this.access_type});return`${this.loginURL}?${t.toString()}`}async openBrowser(e){try{await T(e)}catch(t){R(`无法自动打开浏览器: ${t.message||t.msg}`),console.log(`\n请手动访问以下 URL 进行授权:\n${e}\n`)}}getHtmlTemplate(e,t={}){try{let n=g.readFileSync(e,"utf-8");return Object.keys(t).forEach(e=>{const s=`{{${e}}}`;n=n.replace(new RegExp(s,"g"),t[e])}),n}catch(e){return R(`读取 HTML 模板失败: ${e.message||e.msg}`),"<html><body><h1>页面加载失败</h1></body></html>"}}async isPortInUse(e){return new Promise(t=>{const n=w.createServer();n.once("error",e=>{"EADDRINUSE"===e.code?t(!0):t(!1)}),n.once("listening",()=>{n.once("close",()=>{t(!1)}),n.close()}),n.listen(e)})}async startCallbackServer(){let e=this.redirectUri,t=new URL(e),n=parseInt(t.port,10);await this.isPortInUse(n)&&(R(`\n警告: 端口 ${n} 已被占用,请调整 redirectUri 配置项,使其指向一个未被占用的端口。`),process.exit(1));const s=new Promise((s,r)=>{const o=m.createServer(async(n,i)=>{const a=_.parse(n.url,!0);if(a.pathname===t.pathname||"/"===a.pathname){const t=a.query.code,n=a.query.error;if(n){i.writeHead(400,{"Content-Type":"text/html; charset=utf-8"});const e=this.getHtmlTemplate(this.authErrorTemplate,{ERROR:n});return i.end(e),o.close(),void r(new Error(`授权失败: ${n}`))}if(t)try{const n=await this.getTokenByCode(t,e);this.saveToken(n);const r=n.tenant_id||"未返回",a=n.instance_uri||"未返回";i.writeHead(200,{"Content-Type":"text/html; charset=utf-8"});const c=this.getHtmlTemplate(this.authSuccessTemplate,{TENANT_ID:r,INSTANCE_URI:a});return i.end(c),o.close(),void s({code:t,tokenData:n})}catch(e){i.writeHead(500,{"Content-Type":"text/html; charset=utf-8"});const t=this.getHtmlTemplate(this.tokenErrorTemplate,{ERROR:e});return i.end(t),o.close(),void r(e)}i.writeHead(400,{"Content-Type":"text/html; charset=utf-8"});const c=this.getHtmlTemplate(this.authFailedTemplate);i.end(c),o.close(),r(new Error("未获取到授权码"))}else i.writeHead(404,{"Content-Type":"text/plain"}),i.end("Not Found")});o.on("error",e=>{"EADDRINUSE"===e.code?r(new Error(`端口 ${n} 已被占用,无法启动回调服务器`)):r(e)}),o.listen(n,()=>{console.log(`\n本地回调服务器已启动,监听端口: ${n}`),console.log(`回调地址: ${e}`)}),setTimeout(()=>{o.close(),r(new Error("授权超时,请重试"))},3e5)});return{redirectUri:e,getCodeAndTokenPromise:s}}async getTokenByCode(e,t){const n=k("正在获取 access token...").start();try{const s=new URLSearchParams;s.append("grant_type",this.grant_type),s.append("client_id",this.client_id),s.append("client_secret",this.client_secret),s.append("code",e),s.append("redirect_uri",t);const r=(await d.post(this.tokenURL,s.toString(),{headers:{"Content-Type":"application/x-www-form-urlencoded"}})).data;return r&&r.access_token||(R("获取 token 失败:响应中未包含 access_token",n),R(`响应数据: ${JSON.stringify(r)}`),process.exit(1)),S("成功获取 access token",n),r}catch(e){R("获取 token 失败",n),R(`\n获取 token 失败: ${e.message||e.msg}`),e.response&&R(`响应数据: ${JSON.stringify(e.response.data)}`),process.exit(1)}}async refreshToken(e){const t=k("正在刷新授权信息(token)...").start();try{const n=new URLSearchParams;n.append("grant_type","refresh_token"),n.append("client_id",this.client_id),n.append("client_secret",this.client_secret),n.append("refresh_token",e);const s=(await d.post(this.tokenURL,n.toString(),{headers:{"Content-Type":"application/x-www-form-urlencoded"}})).data;return s&&s.access_token?(S("刷新授权信息成功(token)。",t),s):(R("刷新授权信息失败:响应中未包含 access_token",t),R(`响应数据: ${JSON.stringify(s)}`),null)}catch(e){return R("刷新授权信息失败",t),R(`\n刷新授权信息失败: ${e.message||e.msg}`),e.response&&R(`响应数据: ${JSON.stringify(e.response.data)}`),null}}async login(){console.log("\n========== NeoCRM 登录授权 ==========\n");try{const{redirectUri:e,getCodeAndTokenPromise:t}=await this.startCallbackServer(),n=this.buildAuthUrl(e);console.log("授权 URL:",n),console.log("\n正在打开浏览器进行授权..."),await this.openBrowser(n);const{code:s,tokenData:r}=await t;return S("\n✓ 已获取授权码"),console.log("\n========== 登录成功 ==========\n"),console.log(`实例地址: ${r.instance_uri||"未返回"}`),console.log(`租户 ID: ${r.tenant_id||"未返回"}`),r}catch(e){R(`\n登录失败: ${e.message||e.msg}`),process.exit(1)}}async logout(){if(console.log("\n========== NeoCRM 登出 ==========\n"),g.existsSync(this.tokenFile))try{U.clearEnvConfig(),this.clearToken(),S("已清除授权信息,下次登录需要重新授权。"),console.log("\n登出成功!\n")}catch(e){R(`登出失败: ${e.message||e.msg}`),process.exit(1)}else console.log("当前未登录,无需登出。")}async getValidToken(){const e=this.readToken();if(e||(R("未找到授权信息,请先执行 neo login 进行登录。"),process.exit(1)),this.isTokenExpired(e)){console.log("授权信息已过期,正在尝试刷新..."),e.refresh_token||(R("自动刷新授权信息失败,请重新登录(neo login)。"),process.exit(1));const t=await this.refreshToken(e.refresh_token);return t||(R("刷新授权信息失败,请重新登录 (neo login)"),process.exit(1)),this.saveToken(t),t}return e}async getAccessToken(){return await this.getValidToken()}}};
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("axios"),t=require("node:fs"),n=require("node:path"),s=require("ora"),r=require("node:http"),o=require("node:url"),i=require("open"),a=require("node:net"),c=require("lodash"),l=require("../utils/common.js"),h=require("./neoEnvManager.js");var p,u;exports.__require=function(){if(u)return p;u=1;const d=e,g=t,m=n,y=s,k=r,_=o,f=i,T=a,w=c,{errorLog:U,successLog:$}=l.__require(),R=h.__require();return p=class{constructor(e){if(!e)return;const{loginURL:t,tokenURL:n}=e,s=R.getAuthConfig(),{redirectUri:r,authType:o,auth:i}=s;if(!t||!n)throw new Error("auth.config.js 配置不完整,需要包含 loginURL、tokenURL");this.loginURL=t,this.tokenURL=n,this.NeoCrmAPI=s,this.redirectUri=r,this.authType=o||"oauth2",this.response_type=i.response_type||"code",this.client_id=i.client_id,this.client_secret=i.client_secret,this.scope=i.scope||"all",this.oauthType=i.oauthType||"standard",this.access_type=i.access_type||"offline",this.grant_type=i.grant_type||"authorization_code",this.tokenDir=m.join(process.cwd(),".neo-cli"),this.tokenFile=m.join(this.tokenDir,"token.json"),this.pageDir=m.join(__dirname,"../../template/pageHtml"),this.authErrorTemplate=m.join(this.pageDir,"auth-error.html"),this.authSuccessTemplate=m.join(this.pageDir,"auth-success.html"),this.tokenErrorTemplate=m.join(this.pageDir,"token-error.html"),this.authFailedTemplate=m.join(this.pageDir,"auth-failed.html");try{R.setEnvConfig(w.pick(e,["neoBaseURL","loginURL","tokenURL"]))}catch(e){U(`保存环境配置失败: ${e.message||e.msg}`)}}ensureTokenDir(){g.existsSync(this.tokenDir)||g.mkdirSync(this.tokenDir,{recursive:!0})}saveToken(e){this.ensureTokenDir();const t={...e,savedAt:Date.now(),expiresAt:Date.now()+1e3*(e.expires_in||7200)};g.writeFileSync(this.tokenFile,JSON.stringify(t,null,2),"utf-8")}readToken(){if(!g.existsSync(this.tokenFile))return null;try{return JSON.parse(g.readFileSync(this.tokenFile,"utf-8"))}catch(e){return U(`读取 token 文件失败: ${e.message||e.msg}`),null}}isTokenExpired(e){return!e||!e.expiresAt||Date.now()>=e.expiresAt-3e5}clearToken(){g.existsSync(this.tokenFile)&&g.unlinkSync(this.tokenFile)}getRedirectURI(e){return`http://localhost:${e}`}buildAuthUrl(e){const t=new URLSearchParams({response_type:this.response_type,client_id:this.client_id,redirect_uri:e,scope:this.scope,oauthType:this.oauthType,access_type:this.access_type});return`${this.loginURL}?${t.toString()}`}async openBrowser(e){try{await f(e)}catch(t){U(`无法自动打开浏览器: ${t.message||t.msg}`),console.log(`\n请手动访问以下 URL 进行授权:\n${e}\n`)}}getHtmlTemplate(e,t={}){try{let n=g.readFileSync(e,"utf-8");return Object.keys(t).forEach(e=>{const s=`{{${e}}}`;n=n.replace(new RegExp(s,"g"),t[e])}),n}catch(e){return U(`读取 HTML 模板失败: ${e.message||e.msg}`),"<html><body><h1>页面加载失败</h1></body></html>"}}async isPortInUse(e){return new Promise(t=>{const n=T.createServer();n.once("error",e=>{"EADDRINUSE"===e.code?t(!0):t(!1)}),n.once("listening",()=>{n.once("close",()=>{t(!1)}),n.close()}),n.listen(e)})}async startCallbackServer(){let e=this.redirectUri,t=new URL(e),n=parseInt(t.port,10);await this.isPortInUse(n)&&(U(`\n警告: 端口 ${n} 已被占用,请调整 redirectUri 配置项,使其指向一个未被占用的端口。`),process.exit(1));const s=new Promise((s,r)=>{const o=k.createServer(async(n,i)=>{const a=_.parse(n.url,!0);if(a.pathname===t.pathname||"/"===a.pathname){const t=a.query.code,n=a.query.error;if(n){i.writeHead(400,{"Content-Type":"text/html; charset=utf-8"});const e=this.getHtmlTemplate(this.authErrorTemplate,{ERROR:n});return i.end(e),o.close(),void r(new Error(`授权失败: ${n}`))}if(t)try{const n=await this.getTokenByCode(t,e);this.saveToken(n);const r=n.userInfo.tenantName?`${n.userInfo.tenantName}(${n.tenant_id})`:n.tenant_id||"未返回",a=n.instance_uri||"未返回";i.writeHead(200,{"Content-Type":"text/html; charset=utf-8"});const c=this.getHtmlTemplate(this.authSuccessTemplate,{TENANT_ID:r,INSTANCE_URI:a});return i.end(c),o.close(),void s({code:t,tokenData:n})}catch(e){i.writeHead(500,{"Content-Type":"text/html; charset=utf-8"});const t=this.getHtmlTemplate(this.tokenErrorTemplate,{ERROR:e});return i.end(t),o.close(),void r(e)}i.writeHead(400,{"Content-Type":"text/html; charset=utf-8"});const c=this.getHtmlTemplate(this.authFailedTemplate);i.end(c),o.close(),r(new Error("未获取到授权码"))}else i.writeHead(404,{"Content-Type":"text/plain"}),i.end("Not Found")});o.on("error",e=>{"EADDRINUSE"===e.code?r(new Error(`端口 ${n} 已被占用,无法启动回调服务器`)):r(e)}),o.listen(n,()=>{console.log(`\n本地回调服务器已启动,监听端口: ${n}`),console.log(`回调地址: ${e}`)}),setTimeout(()=>{o.close(),r(new Error("授权超时,请重试"))},3e5)});return{redirectUri:e,getCodeAndTokenPromise:s}}async getTokenByCode(e,t){const n=y("正在获取 access token...").start();try{const s=new URLSearchParams;s.append("grant_type",this.grant_type),s.append("client_id",this.client_id),s.append("client_secret",this.client_secret),s.append("code",e),s.append("redirect_uri",t);const r=(await d.post(this.tokenURL,s.toString(),{headers:{"Content-Type":"application/x-www-form-urlencoded"}})).data;if(r&&r.access_token){const e=await this.getUserInfo(r.access_token);r.userInfo=e}else U("获取 token 失败:响应中未包含 access_token",n),U(`响应数据: ${JSON.stringify(r)}`),process.exit(1);return $("成功获取 access token",n),r}catch(e){U("获取 token 失败",n),U(`\n获取 token 失败: ${e.message||e.msg}`),e.response&&U(`响应数据: ${JSON.stringify(e.response.data)}`),process.exit(1)}}async refreshToken(e){const t=y("正在刷新授权信息(token)...").start();try{const n=new URLSearchParams;n.append("grant_type","refresh_token"),n.append("client_id",this.client_id),n.append("client_secret",this.client_secret),n.append("refresh_token",e);const s=(await d.post(this.tokenURL,n.toString(),{headers:{"Content-Type":"application/x-www-form-urlencoded"}})).data;if(!s||!s.access_token)return U("刷新授权信息失败:响应中未包含 access_token",t),U(`响应数据: ${JSON.stringify(s)}`),null;{const e=await this.getUserInfo(s.access_token);s.userInfo=e}return $("刷新授权信息成功(token)。",t),s}catch(e){return U("刷新授权信息失败",t),U(`\n刷新授权信息失败: ${e.message||e.msg}`),e.response&&U(`响应数据: ${JSON.stringify(e.response.data)}`),null}}async getUserInfo(e){let t={};try{let n=this.buildFullUrl(this.NeoCrmAPI.getUserInfoAPI);const s=await d.get(n,{headers:{Authorization:`Bearer ${e}`,"xsy-inner-source":"bff","Content-Type":"application/json"}}),{code:r,message:o,msg:i}=s.data||{};r&&"200"!==r&&(U(`获取用户信息失败: ${o||i||"未知错误"}`),process.exit(1)),t=s.data.result||s.data.data||{}}catch(e){const t=e.message||e.msg;U(t?`获取用户信息失败: ${t}`:`响应数据: ${JSON.stringify(e)}`),process.exit(1)}return t}async login(){console.log("\n========== NeoCRM 登录授权 ==========\n");try{const{redirectUri:e,getCodeAndTokenPromise:t}=await this.startCallbackServer(),n=this.buildAuthUrl(e);console.log("授权 URL:",n),console.log("\n正在打开浏览器进行授权..."),await this.openBrowser(n);const{code:s,tokenData:r}=await t;return $("\n✓ 已获取授权码"),console.log("\n========== 登录成功 ==========\n"),console.log(`实例地址: ${r.instance_uri||"未返回"}`),console.log(`当前租户信息: ${r.userInfo.tenantName||"未返回租户名称"}(${r.tenant_id||"未返回租户 ID"})`),console.log(`当前登录用户: ${r.userInfo.name||"未返回用户名"}`),r}catch(e){U(`\n登录失败: ${e.message||e.msg}`),process.exit(1)}}async logout(){if(console.log("\n========== NeoCRM 登出 ==========\n"),g.existsSync(this.tokenFile))try{R.clearEnvConfig(),this.clearToken(),$("已清除授权信息,下次登录需要重新授权。"),console.log("\n登出成功!\n")}catch(e){U(`登出失败: ${e.message||e.msg}`),process.exit(1)}else console.log("当前未登录,无需登出。")}async getValidToken(){const e=this.readToken();if(e||(U("未找到授权信息,请先执行 neo login 进行登录。"),process.exit(1)),this.isTokenExpired(e)){console.log("授权信息已过期,正在尝试刷新..."),e.refresh_token||(U("自动刷新授权信息失败,请重新登录(neo login)。"),process.exit(1));const t=await this.refreshToken(e.refresh_token);return t||(U("刷新授权信息失败,请重新登录 (neo login)"),process.exit(1)),this.saveToken(t),t}return e}async getAccessToken(){return await this.getValidToken()}buildFullUrl(e){return e.startsWith("http://")||e.startsWith("https://")?e:`${this.NeoCrmAPI.neoBaseURL}${e}`}}};
@@ -1 +1 @@
1
- "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});var e="1.9.23";const o={version:e};exports.default=o,exports.version=e;
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});var e="1.9.27";const o={version:e};exports.default=o,exports.version=e;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo-cmp-cli",
3
- "version": "1.9.25",
3
+ "version": "1.9.27",
4
4
  "description": "Neo 自定义组件开发工具,支持react 和 vue2.0技术栈。",
5
5
  "keywords": [
6
6
  "neo-cli",
@@ -7,12 +7,12 @@
7
7
  - neo.config.js: neo-cmp-cli 配置文件。
8
8
 
9
9
  ### 组件开发规范
10
- - 存放在 src/components 目录下的自定义组件,默认 index 为自定义组件源码入口文件,register.[tj]s 为注册 自定义组件的脚本文件,model.[tj]s 为自定义组件的模型文件(对接页面设计器需要);
10
+ - 存放在 src/components 目录下的自定义组件,默认 index 为自定义组件源码入口文件,model.[tj]s 为自定义组件的模型文件(对接页面设计器需要);
11
11
  - 当 neo.config.js 中的 entry 为空或者不存在时,cli 将根据 src/components 目录下的自定义组件结构生成对应的 entry 配置(可在命令控制台查看生成的 entry 配置);
12
12
  - 自定义组件中可用的配置项类型 请见 [当前可用表单项](https://github.com/wibetter/neo-register/blob/master/docs/FormItemType.md);
13
13
  - 自定义组件最外层请设置一个唯一的 ClassName(比如 xx-cmpType-container),所有内容样式请放在该 ClassName 中,避免自定义组件样式相互干扰;
14
14
  - 默认开启代码规范检测(含样式内容),如需关闭,请调整 neo.config.js 相关配置;
15
- - 请使用 react 16版本。
15
+ - 请使用 react 16 版本。
16
16
 
17
17
  ### 自定义组件注册器使用说明
18
18
  - [neo-register 使用说明](https://www.npmjs.com/package/neo-register?activeTab=readme)
@@ -71,7 +71,7 @@ OAuth2 授权码模式更加安全可靠,无需用户配置账户名和密码
71
71
 
72
72
  功能:清除本地保存的 token 文件,下次使用需要重新登录。
73
73
 
74
- ##### 配置示例
74
+ ##### neo login 选择「自定义环境」时的授权配置示例
75
75
 
76
76
  ```javascript
77
77
  // neo.config.js
@@ -7,12 +7,12 @@
7
7
  - neo.config.js: neo-cmp-cli 配置文件。
8
8
 
9
9
  ### 组件开发规范
10
- - 存放在 src/components 目录下的自定义组件,默认 index 为自定义组件源码入口文件,register.[tj]s 为注册 自定义组件的脚本文件,model.[tj]s 为自定义组件的模型文件(对接页面设计器需要);
10
+ - 存放在 src/components 目录下的自定义组件,默认 index 为自定义组件源码入口文件,model.[tj]s 为自定义组件的模型文件(对接页面设计器需要);
11
11
  - 当 neo.config.js 中的 entry 为空或者不存在时,cli 将根据 src/components 目录下的自定义组件结构生成对应的 entry 配置(可在命令控制台查看生成的 entry 配置);
12
12
  - 自定义组件中可用的配置项类型 请见 [当前可用表单项](https://github.com/wibetter/neo-register/blob/master/docs/FormItemType.md);
13
13
  - 自定义组件最外层请设置一个唯一的 ClassName(比如 xx-cmpType-container),所有内容样式请放在该 ClassName 中,避免自定义组件样式相互干扰;
14
14
  - 默认开启代码规范检测(含样式内容),如需关闭,请调整 neo.config.js 相关配置;
15
- - 请使用 react 16版本;
15
+ - 请使用 react 16 版本;
16
16
  - 支持在自定义组件中使用 Open API,详细见[使用说明](./docs/README.md)。
17
17
 
18
18
  ### 自定义组件注册器使用说明
@@ -51,12 +51,12 @@ import ChartWidget from './components/chart-widget';
51
51
  更多详细信息请查看 [图表组件文档](./src/components/chart-widget/README.md)。
52
52
 
53
53
  ### 组件开发规范
54
- - 存放在 src/components 目录下的自定义组件,默认 index 为自定义组件源码入口文件,register.[tj]s 为注册 自定义组件的脚本文件,model.[tj]s 为自定义组件的模型文件(对接页面设计器需要);
54
+ - 存放在 src/components 目录下的自定义组件,默认 index 为自定义组件源码入口文件,model.[tj]s 为自定义组件的模型文件(对接页面设计器需要);
55
55
  - 当 neo.config.js 中的 entry 为空或者不存在时,cli 将根据 src/components 目录下的自定义组件结构生成对应的 entry 配置(可在命令控制台查看生成的 entry 配置);
56
56
  - 自定义组件中可用的配置项类型 请见 [当前可用表单项](https://github.com/wibetter/neo-register/blob/master/docs/FormItemType.md);
57
57
  - 自定义组件最外层请设置一个唯一的 ClassName(比如 xx-cmpType-container),所有内容样式请放在该 ClassName 中,避免自定义组件样式相互干扰;
58
58
  - 默认开启代码规范检测(含样式内容),如需关闭,请调整 neo.config.js 相关配置;
59
- - 请使用 react 16版本。
59
+ - 请使用 react 16 版本。
60
60
 
61
61
  ### 自定义组件注册器使用说明
62
62
  - [neo-register 使用说明](https://www.npmjs.com/package/neo-register?activeTab=readme)
@@ -116,7 +116,7 @@ OAuth2 授权码模式更加安全可靠,无需用户配置账户名和密码
116
116
 
117
117
  功能:清除本地保存的 token 文件,下次使用需要重新登录。
118
118
 
119
- ##### 配置示例
119
+ ##### neo login 选择「自定义环境」时的授权配置示例
120
120
 
121
121
  ```javascript
122
122
  // neo.config.js
@@ -7,12 +7,12 @@
7
7
  - neo.config.js: neo-cmp-cli 配置文件。
8
8
 
9
9
  ### 组件开发规范
10
- - 存放在 src/components 目录下的自定义组件,默认 index 为自定义组件源码入口文件,register.[tj]s 为注册 自定义组件的脚本文件,model.[tj]s 为自定义组件的模型文件(对接页面设计器需要);
10
+ - 存放在 src/components 目录下的自定义组件,默认 index 为自定义组件源码入口文件,model.[tj]s 为自定义组件的模型文件(对接页面设计器需要);
11
11
  - 当 neo.config.js 中的 entry 为空或者不存在时,cli 将根据 src/components 目录下的自定义组件结构生成对应的 entry 配置(可在命令控制台查看生成的 entry 配置);
12
12
  - 自定义组件中可用的配置项类型 请见 [当前可用表单项](https://github.com/wibetter/neo-register/blob/master/docs/FormItemType.md);
13
13
  - 自定义组件最外层请设置一个唯一的 ClassName(比如 xx-cmpType-container),所有内容样式请放在该 ClassName 中,避免自定义组件样式相互干扰;
14
14
  - 默认开启代码规范检测(含样式内容),如需关闭,请调整 neo.config.js 相关配置;
15
- - 请使用 react 16版本。
15
+ - 请使用 react 16 版本。
16
16
 
17
17
  ### 自定义组件注册器使用说明
18
18
  - [neo-register 使用说明](https://www.npmjs.com/package/neo-register?activeTab=readme)
@@ -72,7 +72,7 @@ OAuth2 授权码模式更加安全可靠,无需用户配置账户名和密码
72
72
 
73
73
  功能:清除本地保存的 token 文件,下次使用需要重新登录。
74
74
 
75
- ##### 配置示例
75
+ ##### neo login 选择「自定义环境」时的授权配置示例
76
76
 
77
77
  ```javascript
78
78
  // neo.config.js
@@ -7,12 +7,12 @@
7
7
  - neo.config.js: neo-cmp-cli 配置文件。
8
8
 
9
9
  ### 组件开发规范
10
- - 存放在 src/components 目录下的自定义组件,默认 index 为自定义组件源码入口文件,register.[tj]s 为注册 自定义组件的脚本文件,model.[tj]s 为自定义组件的模型文件(对接页面设计器需要);
10
+ - 存放在 src/components 目录下的自定义组件,默认 index 为自定义组件源码入口文件,model.[tj]s 为自定义组件的模型文件(对接页面设计器需要);
11
11
  - 当 neo.config.js 中的 entry 为空或者不存在时,cli 将根据 src/components 目录下的自定义组件结构生成对应的 entry 配置(可在命令控制台查看生成的 entry 配置);
12
12
  - 自定义组件中可用的配置项类型 请见 [当前可用表单项](https://github.com/wibetter/neo-register/blob/master/docs/FormItemType.md);
13
13
  - 自定义组件最外层请设置一个唯一的 ClassName(比如 xx-cmpType-container),所有内容样式请放在该 ClassName 中,避免自定义组件样式相互干扰;
14
14
  - 默认开启代码规范检测(含样式内容),如需关闭,请调整 neo.config.js 相关配置;
15
- - 请使用 react 16版本;
15
+ - 请使用 react 16 版本;
16
16
  - 支持在自定义组件中使用 Open API,详细见[使用说明](https://www.npmjs.com/package/neo-open-api)。
17
17
 
18
18
  ### 自定义组件注册器使用说明
@@ -73,7 +73,7 @@ OAuth2 授权码模式更加安全可靠,无需用户配置账户名和密码
73
73
 
74
74
  功能:清除本地保存的 token 文件,下次使用需要重新登录。
75
75
 
76
- ##### 配置示例
76
+ ##### neo login 选择「自定义环境」时的授权配置示例
77
77
 
78
78
  ```javascript
79
79
  // neo.config.js
@@ -7,12 +7,12 @@
7
7
  - neo.config.js: neo-cmp-cli 配置文件。
8
8
 
9
9
  ### 组件开发规范
10
- - 存放在 src/components 目录下的自定义组件,默认 index 为自定义组件源码入口文件,register.[tj]s 为注册 自定义组件的脚本文件,model.[tj]s 为自定义组件的模型文件(对接页面设计器需要);
10
+ - 存放在 src/components 目录下的自定义组件,默认 index 为自定义组件源码入口文件,model.[tj]s 为自定义组件的模型文件(对接页面设计器需要);
11
11
  - 当 neo.config.js 中的 entry 为空或者不存在时,cli 将根据 src/components 目录下的自定义组件结构生成对应的 entry 配置(可在命令控制台查看生成的 entry 配置);
12
12
  - 自定义组件中可用的配置项类型 请见 [当前可用表单项](https://github.com/wibetter/neo-register/blob/master/docs/FormItemType.md);
13
13
  - 自定义组件最外层请设置一个唯一的 ClassName(比如 xx-cmpType-container),所有内容样式请放在该 ClassName 中,避免自定义组件样式相互干扰;
14
14
  - 默认开启代码规范检测(含样式内容),如需关闭,请调整 neo.config.js 相关配置;
15
- - 请使用 react 16版本。
15
+ - 请使用 react 16 版本。
16
16
 
17
17
  ### 自定义组件注册器使用说明
18
18
  - [neo-register 使用说明](https://www.npmjs.com/package/neo-register?activeTab=readme)
@@ -72,7 +72,7 @@ OAuth2 授权码模式更加安全可靠,无需用户配置账户名和密码
72
72
 
73
73
  功能:清除本地保存的 token 文件,下次使用需要重新登录。
74
74
 
75
- ##### 配置示例
75
+ ##### neo login 选择「自定义环境」时的授权配置示例
76
76
 
77
77
  ```javascript
78
78
  // neo.config.js
@@ -7,12 +7,12 @@
7
7
  - neo.config.js: neo-cmp-cli 配置文件。
8
8
 
9
9
  ### 组件开发规范
10
- - 存放在 src/components 目录下的自定义组件,默认 index 为自定义组件源码入口文件,register.[tj]s 为注册 自定义组件的脚本文件,model.[tj]s 为自定义组件的模型文件(对接页面设计器需要);
10
+ - 存放在 src/components 目录下的自定义组件,默认 index 为自定义组件源码入口文件,model.[tj]s 为自定义组件的模型文件(对接页面设计器需要);
11
11
  - 当 neo.config.js 中的 entry 为空或者不存在时,cli 将根据 src/components 目录下的自定义组件结构生成对应的 entry 配置(可在命令控制台查看生成的 entry 配置);
12
12
  - 自定义组件中可用的配置项类型 请见 [当前可用表单项](https://github.com/wibetter/neo-register/blob/master/docs/FormItemType.md);
13
13
  - 自定义组件最外层请设置一个唯一的 ClassName(比如 xx-cmpType-container),所有内容样式请放在该 ClassName 中,避免自定义组件样式相互干扰;
14
14
  - 默认开启代码规范检测(含样式内容),如需关闭,请调整 neo.config.js 相关配置;
15
- - 请使用 react 16版本。
15
+ - 请使用 react 16 版本。
16
16
 
17
17
  ### 自定义组件注册器使用说明
18
18
  - [neo-register 使用说明](https://www.npmjs.com/package/neo-register?activeTab=readme)
@@ -72,7 +72,7 @@ OAuth2 授权码模式更加安全可靠,无需用户配置账户名和密码
72
72
 
73
73
  功能:清除本地保存的 token 文件,下次使用需要重新登录。
74
74
 
75
- ##### 配置示例
75
+ ##### neo login 选择「自定义环境」时的授权配置示例
76
76
 
77
77
  ```javascript
78
78
  // neo.config.js
@@ -12,7 +12,7 @@
12
12
  - 自定义组件中可用的配置项类型 请见 [当前可用表单项](https://github.com/wibetter/neo-register/blob/master/docs/FormItemType.md);
13
13
  - 自定义组件最外层请设置一个唯一的 ClassName(比如 xx-cmpType-container),所有内容样式请放在该 ClassName 中,避免自定义组件样式相互干扰;
14
14
  - 默认开启代码规范检测(含样式内容),如需关闭,请调整 neo.config.js 相关配置;
15
- - 请使用 react 16版本。
15
+ - 请使用 react 16 版本。
16
16
 
17
17
  ### 自定义组件注册器使用说明
18
18
  - [neo-register 使用说明](https://www.npmjs.com/package/neo-register?activeTab=readme)
@@ -72,7 +72,7 @@ OAuth2 授权码模式更加安全可靠,无需用户配置账户名和密码
72
72
 
73
73
  功能:清除本地保存的 token 文件,下次使用需要重新登录。
74
74
 
75
- ##### 配置示例
75
+ ##### neo login 选择「自定义环境」时的授权配置示例
76
76
 
77
77
  ```javascript
78
78
  // neo.config.js