## Amis 自定义组件开发工具
amis-cmp-cli 是 Amis 自定义组件开发工具，基于 [AKFun](https://github.com/wibetter/akfun) 的工程能力，提供 初始化、编译构建、预览调试、热更新、多技术栈支持和发布等功能。

### 主要特性
- **零配置**: 内置默认配置，开箱可用；
- **多技术栈**: 支持 Vue2、React、React+TypeScript 自定义组件的调试、构建与发布；
- **多构建场景**: 本地预览（含热更新/代理）、外链调试、库构建（UMD/ESM）、部署&发布；
- **灵活可配**: 支持 构建入口、别名、代理、SASS 注入、ESLint/StyleLint、Babel/Loader/Plugin 扩展等配置；
- **样式与规范**: 内置 Autoprefixer、Sass、PostCSS、ESLint、StyleLint；
- **发布至 CDN**: 内置发布到对象存储（OSS）的能力，支持自定义对象存储配置；
- **内置模块共享机制**: 支持自定义组件之间共享模块和复用。

### 内置的自定义组件模板
创建自定义组件时（执行初始化命令 amis init）可选用。
- **React + TS 模板**: [react-ts-custom-cmp-template](https://github.com/wibetter/react-ts-custom-cmp-template) 内置信息卡片列表展示组件示例
- **React 模板(非 TS)**: [react-custom-cmp-template](https://github.com/wibetter/react-custom-cmp-template) 
- **Antd 模板**: [antd-custom-cmp-template](https://github.com/wibetter/antd-custom-cmp-template) 内置 Antd UI 组件实现的数据仪表板展示组件示例
- **Echarts 模板**: [echarts-custom-cmp-template](https://github.com/wibetter/echarts-custom-cmp-template) Echarts 图表自定义组件（含 高德地图展示组件示例）
- **Vue2 模板**: [vue2-custom-cmp-template](https://github.com/wibetter/vue2-custom-cmp-template) Vue2.0 自定义组件

## 快速开始

#### 1) 全局安装自定义组件开发工具
```bash
yarn global add amis-cmp-cli
# 或
npm i -g amis-cmp-cli
```

#### 2) 创建自定义组件项目
```bash
# 方式一：创建空的自定义组件项目
amis create project

# 方式二：根据模板创建自定义组件项目（默认 React+TS，可用 -t 指定模板，-n 指定自定义组件名称）
amis init
```

#### 3) 进入自定义组件项目根目录，安装依赖并运行
```bash
cd xxCmpProject
```

#### 4) 创建自定义组件
```bash
# 在当前项目中创建一个自定义组件
amis create cmp
```
默认在当前项目 src/components/ 目录下新增自定义组件。

#### 5) 本地预览自定义组件内容
```bash
# 预览自定义组件内容
amis preview
```
执行成功后，默认自动打开本地浏览器预览自定义组件内容。

#### 6) 外链调试（在线上页面设计器端预览与调试）
```bash
amis linkDebug
```
需在 amis 页面设计器端挂载控制台输出的外链脚本地址，方可在页面设计器端使用当前自定义组件。

## 常用命令说明
- **amis init**: 交互式创建自定义组件（支持 -t、--name）;
- **amis create project**: 创建自定义组件项目（支持 --name）;
- **amis create cmp**: 在当前项目中创建一个自定义组件（支持 --name）;
- **amis preview**: 本地预览自定义组件内容（支持 --name），默认支持热更新与接口代理;
- **amis linkDebug**: 外链调试模式，在平台端页面设计器中调试自定义组件;
- **amis publish2oss**: 构建并上传到对象存储（支持 --name，可自定义配置对象存储）;

## 开发须知
#### 1）默认自动识别自定义组件
- **自动生成入口配置**: 当 `entry` 未配置时，自动从 `src/components` 目录下扫描并识别自定义组件，`src/components` 下的子目录名称作为自定义组件的名称，并以其目录下的 `index.ts/.tsx/.js/.jsx` 文件作为组件内容文件，plugin.[tj]s  作为插件内容文件；
- **自动注册自定义组件**: 当 `entry` 未配置时，自动生成自定义组件注册文件和插件注册文件，并注入到构建脚本中，无需用户手动编写注册文件（[amis-register](https://www.npmjs.com/package/amis-register)）。默认均注册为 amis普通组件（renderer），如需注册成 amis表单组件，组件目录请以 input 开头（比如 input-xxCmp）。

#### 2）设置自定义组件属性配置项
通过 amis init 初始化的自定义组件，默认自带组件插件文件，所有和页面设计器关联的信息均在此文件中定义（详细见 plugin 文件中的注释说明）。
其中 panelBodyCreator 用于添加自定义组件的属性配置面板。

#### 3）本地调试自定义组件

##### 1. 启动 外链调试模式
```
amis linkDebug
```
启动成功后，当前命令控制台会输出一个可使用的 外链脚本地址（通常为 http://localhost:1024/index.js）。

##### 2. 打开线上页面设计器，并添加外链脚本地址
访问线上[页面设计器](https://wibetter.github.io/amis-editor-demo/reports-demo/index.html) 打开任意页面，在左侧外部链接管理面板添加外链脚本地址，添加完成后即可在此页面设计器 / 组件物料面板中看到对应自定义组件。

#### 4）发布自定义组件至CDN
执行 `amis publish2oss` 即可构建对应自定义组件，并自动将构建后资源上传到对象存储（OSS）中。  

##### 发布前请确保
- **package.json 的 name 唯一**
- **version 不重复**
- 可按需配置对象存储参数（支持自定义），默认使用内置对象存储配置。

##### 支持自定义对象存储配置
```javascript
module.exports = {
  publish2oss: {
    // 用于构建并发布至 OSS 的相关配置
    //【特别说明】以下配置项都自带默认值，非必填。如需自定义请自行配置。
    // NODE_ENV: 'production',
    // cssExtract: false, // 不额外提取css文件
    ossType: 'ali', // oss类型：ali、baidu
    ossConfig: {
      endpoint: 'https://oss-cn-beijing.aliyuncs.com',
      AccessKeyId: 'xxx',
      AccessKeySecret: 'xx',
      bucket: 'amis-cmp-xx-bucket' // 存储桶名称
    },
    assetsRoot: resolve('dist') // 上传指定目录下的脚本文件
  },
}
```

##### 支持发布指定自定义组件
执行 `amis publish2oss --name=xxCmp`

##### 特别说明
此发布方式只是将自定义组件构建产物发布到 CDN 上，如要使用自定义组件还需要手动将其添加到平台运行时。

## 常见问题

**Q1: 在 Windows VSCode 控制台执行 amis 相关命令出现报错**  
A: 如果提示“在此系统上禁止运行脚本”，这个可能是 Windows 客户端的默认安全策略影响，可尝试在控制台输入以下命令解决：

```bash
 Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope CurrentUser
```

说明：以上命令用于设置 允许当前用户在此 Windows 客户端执行所有脚本。[关于 PowerShell 执行策略](https://learn.microsoft.com/zh-cn/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-7.5)


## 项目工程配置说明（amis.config.js）
amis-cmp-cli 默认提供完整配置； 
如需自定义，使用 `amis config init` 生成 `amis.config.js` 并按需修改。

以下为常用配置示例（片段可自由组合）。

### 1) 代码规范与检查相关配置
```javascript
module.exports = {
  settings: {
    enableESLint: true, // 是否开启ESLint，默认开启ESLint检测代码格式
    enableESLintFix: false, // 是否ESLint自动修正代码格式
    enableStyleLint: true, // 是否开启StyleLint，默认开启ESLint检测代码格式
    enableStyleLintFix: false // 是否需要StyleLint自动修正代码格式
  },
}
```

### 2) 构建入口配置（优先级：linkDebug/build2lib/publish2oss.entry > webpack.entry）
【提示】当未配置 entry 时，cli 默认从 `src/components` 目录下扫描并识别自定义组件，并自动生成对应的 entry 构建入口配置。

如需自定义构建入口配置，请按如下结构调整项目工程配置文件（amis.config.js）：
```javascript
module.exports = {
  webpack: {
    entry: { index: './src/index.js' },
  },
  linkDebug: { entry: {} },
  build2lib: { entry: {} },
  publish2oss: { entry: {} },
}
```
备注: 详细配置方法请查看 Webpack 官网 ([关于entry的配置方法](https://www.webpackjs.com/configuration/entry-context/#entry))。

### 3) 解析配置（extensions/alias）
```javascript
module.exports = {
  webpack: {
    resolve: {
      extensions: ['.js', '.jsx', '.vue', 'json'],
      alias: {},
    },
  },
}
```
备注1: extensions 的详细配置方法请查看Webpack官网 ([关于resolve-extensions的配置方法](https://www.webpackjs.com/configuration/resolve/#resolve-extensions))；  
备注2: alias 的详细配置方法请查看 Webpack 官网 ([关于resolve-alias的配置方法](https://www.webpackjs.com/configuration/resolve/#resolve-alias))。

### 4) 页面模板与公共样式资源
```javascript
module.exports = {
  webpack: {
    template: '', // 自定义页面模板路径
    sassResources: [], // sassResources 中添加的 样式文件会作为项目的公共SASS内容（变量、mixin、function等）
  },
}
```

### 5) 依赖打包策略（忽略 node_modules）
```javascript
module.exports = {
  webpack: {
    ignoreNodeModules: true, // 打包时是否忽略 node_modules，默认为false
    allowList: [], // 用于配置会注入bundle中的依赖包（ignoreNodeModules 为 true 时生效）
  },
}
```

### 6) TypeScript 声明文件与工程目录
```javascript
module.exports = {
  webpack: {
    createDeclaration: false, // 可选择是否生成ts声明文件，默认为false
    projectDir: ['./src'], // 构建项目中，设置生效的目录（可同时设置多个目录），用于提高前端工程执行效率。可以不配置，默认为['./src']
  },
}
```

### 7) 环境变量替换（params-replace-loader）
```javascript
module.exports = {
  envParams: {
    common: { '#version#': '20250822.1' },
    local: {
      '#dataApiBase#': 'http://localhost:1024',
      '#assetsPublicPath#': 'http://localhost:1024',
      '#routeBasePath#': '/',
    },
  },
}
```

### 8) 本地开发代理与 HTTPS
```javascript
module.exports = {
  preview: {
    proxyTable: {},
    https: false,
  },
}
```
备注1: [关于proxyTable的配置方法](https://www.webpackjs.com/configuration/dev-server/#devserver-proxy)；  
备注2: 启用 HTTPS 后，若浏览器提示不安全，可在 Chrome 地址栏打开 `chrome://flags/#allow-insecure-localhost` 并设置为 Enabled。

### 9) 库构建（UMD/ESM）
```javascript
module.exports = {
  build2lib: {
    NODE_ENV: 'production',
    libraryName: '',
    assetsRoot: resolve('dist'),
    assetsPublicPath: '/',
    assetsSubDirectory: '',
    productionSourceMap: false,
    productionGzip: false,
    productionGzipExtensions: ['js', 'css', 'json'],
    bundleAnalyzerReport: false,
    ignoreNodeModules: true,
    allowList: [],
  },
}
```

### 10) 自定义 Loader / Plugin / Babel Plugins
```javascript
module.exports = {
  webpack: {
    moduleRules: [], // 用于添加自定义 loaders
    plugins: [], // 用于添加自定义 plugins
    babelPlugins: [  // 用于添加自定义 Babel plugins
      [
        'component',
        { libraryName: 'element-ui', styleLibraryName: 'theme-chalk' },
      ],
    ],
  },
}
```
备注: 以上自定义 babelPlugins 用于实现 [element-ui 组件按需引入](https://element.eleme.cn/#/zh-CN/component/quickstart#an-xu-yin-ru)。  

也支持以函数形式动态调整内置 Babel Plugins：  
```javascript
module.exports = {
  webpack: {
    babelPlugins: (curBabelPlugins) => {
      curBabelPlugins.push(/* your plugin */)
      return curBabelPlugins
    },
  },
}
```

### 11) 自定义组件之间实现模块共享

步骤1：在自定义组件A（cmpType:amis-custom-cmpA）/ 配置文件（amis.config.js）中定义要共享出来的模块A（xxModule_A）
```javascript
module.exports = {
  amisCommonModule: {
    // exports: ['xxModule'], // 数组写法，可用于导出自定义组件中的第三方依赖模块，
    exports: { 'xxModule_A': path.resolve('./src/components/xxModule_A') }, // 对象写法，可用于导出自定义组件中的某个内容模块（需要使用绝对路径导出）
  },
}
```

步骤2：在自定义组件B（cmpType:amis-custom-cmpB）/ 配置文件（amis.config.js）中 声明需要使用 自定义组件 A 分享出来的模块（xxModule_A）
```javascript
module.exports = {
  amisCommonModule: {
    remoteDeps: ['amis-custom-cmpA'], // 远程依赖（自定义组件），表示当前自定义组件 B 会用到哪些自定义组件
    externals: ['xxModule_A'], // 自定义组件中需要剔除的外部模块（远程自定义组件中分享出来的模块），仅支持数组写法，需要和 remoteDeps 配合使用
  },
}
```

步骤3：在自定义组件B 中使用 自定义组件 A 分享出来的模块（xxModule_A）
```javascript
import xxModuleA from 'xxModule_A'; // 导入自定义组件 A 共享出来的模块
```


---
如需更多细节与高级用法，请参考模板项目与源码注释。
