vite-plugin-deploy-ftp 3.3.0 → 3.4.1
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 +172 -140
- package/dist/index.d.ts +1 -1
- package/dist/index.js +32 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,90 +1,148 @@
|
|
|
1
1
|
# vite-plugin-deploy-ftp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Uploads the directory bundled by Vite to an FTP server. It is ideal for projects where you don't want to open FTP tools manually and repeatedly drag files to publish.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/vite-plugin-deploy-ftp)
|
|
6
|
+
[](https://www.npmjs.com/package/vite-plugin-deploy-ftp)
|
|
7
|
+
[](https://github.com/yulin96/vite-plugin-deploy-ftp)
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
- 单个 FTP 服务器配置
|
|
10
|
-
- 多个 FTP 服务器配置(可多选上传目标)
|
|
11
|
-
- 自动备份远程文件
|
|
12
|
-
- 连接重试机制
|
|
13
|
-
- 选择性文件备份
|
|
14
|
-
- 当前版本仅支持 ESM(`import`),不再提供 CommonJS(`require`)入口
|
|
15
|
-
|
|
16
|
-
## 安装
|
|
9
|
+
## Installation
|
|
17
10
|
|
|
18
11
|
```bash
|
|
19
12
|
pnpm add vite-plugin-deploy-ftp -D
|
|
20
13
|
```
|
|
21
14
|
|
|
22
|
-
##
|
|
15
|
+
## Quick Start
|
|
23
16
|
|
|
24
|
-
|
|
17
|
+
It is recommended to control whether to upload using environment variables. By default, local builds will not trigger uploading, and publishing will only happen when explicitly enabled.
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# .env
|
|
21
|
+
FTP_HOST=ftp.example.com
|
|
22
|
+
FTP_PORT=21
|
|
23
|
+
FTP_USER=username
|
|
24
|
+
FTP_PASSWORD=password
|
|
25
|
+
FTP_PATH=/public_html
|
|
26
|
+
FTP_ALIAS=https://example.com
|
|
27
|
+
DEPLOY_FTP=0
|
|
28
|
+
```
|
|
25
29
|
|
|
26
30
|
```ts
|
|
27
31
|
// vite.config.ts
|
|
32
|
+
import { defineConfig, loadEnv } from 'vite'
|
|
28
33
|
import vitePluginDeployFtp from 'vite-plugin-deploy-ftp'
|
|
29
34
|
|
|
30
|
-
export default {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
35
|
+
export default defineConfig(({ mode }) => {
|
|
36
|
+
const env = loadEnv(mode, process.cwd(), '')
|
|
37
|
+
const shouldDeploy = env.DEPLOY_FTP === '1'
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
plugins: [
|
|
41
|
+
vitePluginDeployFtp({
|
|
42
|
+
open: shouldDeploy,
|
|
43
|
+
autoUpload: true,
|
|
44
|
+
failOnError: true,
|
|
45
|
+
host: env.FTP_HOST,
|
|
46
|
+
port: +(env.FTP_PORT || 21),
|
|
47
|
+
user: env.FTP_USER,
|
|
48
|
+
password: env.FTP_PASSWORD,
|
|
49
|
+
uploadPath: env.FTP_PATH?.split(',').map((path) => path.trim()) || '',
|
|
50
|
+
alias: env.FTP_ALIAS,
|
|
51
|
+
singleBack: true,
|
|
52
|
+
singleBackFiles: ['index.html'],
|
|
53
|
+
}),
|
|
54
|
+
],
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Enable upload when building:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# macOS / Linux
|
|
63
|
+
DEPLOY_FTP=1 pnpm build
|
|
64
|
+
|
|
65
|
+
# Windows PowerShell
|
|
66
|
+
$env:DEPLOY_FTP='1'; pnpm build
|
|
47
67
|
```
|
|
48
68
|
|
|
49
|
-
|
|
69
|
+
`FTP_PATH` can be a single directory:
|
|
70
|
+
|
|
71
|
+
```env
|
|
72
|
+
FTP_PATH=/public_html
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Or multiple directories separated by commas:
|
|
76
|
+
|
|
77
|
+
```env
|
|
78
|
+
FTP_PATH=/public_html,/backup_html
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Configuration Guide
|
|
82
|
+
|
|
83
|
+
| Options | Description |
|
|
84
|
+
| :---------------- | :--------------------------------------------------------------------------------------------------------------------------------------- |
|
|
85
|
+
| `open` | Whether to enable upload. It is recommended to control this via environment variables to avoid accidental uploads during routine builds. |
|
|
86
|
+
| `autoUpload` | Skip the "confirm upload" prompt. Recommended to set to `true` for automated deployments. |
|
|
87
|
+
| `failOnError` | Whether to make the build command fail if the upload fails. Recommended to set to `true` in CI/CD pipelines. |
|
|
88
|
+
| `uploadPath` | Upload directory paths. Supports string or array of strings (files will be uploaded to all specified directories). |
|
|
89
|
+
| `alias` | Public URL / domain. If provided, the accessible URL will be printed after uploading. |
|
|
90
|
+
| `singleBack` | Whether to back up only specific files instead of the entire directory. Usually backing up `index.html` is enough and much faster. |
|
|
91
|
+
| `singleBackFiles` | List of files to back up in single-backup mode, supporting sub-directories, e.g., `assets/app.js`. |
|
|
92
|
+
| `ftps` | Multiple FTP configurations. Used when you need to publish to multiple servers. |
|
|
93
|
+
| `defaultFtp` | The default server name when configuring multiple FTPs to reduce manual selection. |
|
|
94
|
+
| `concurrency` | Number of simultaneous file uploads. Keep default if the server connection is unstable. |
|
|
95
|
+
|
|
96
|
+
## Key Behaviors
|
|
97
|
+
|
|
98
|
+
- **Upload timing**: Uploads only after Vite finishes the build process.
|
|
99
|
+
- **Lazy evaluation**: When `open: false`, the plugin will not upload and will not validate FTP connection parameters.
|
|
100
|
+
- **Multiple paths**: When `uploadPath` is an array, the same build output will be uploaded sequentially to all specified paths.
|
|
101
|
+
- **Cross-product upload**: When combining multiple FTP servers and multiple paths, files are uploaded sequentially for every "Server × Directory" combination.
|
|
102
|
+
- **Backup before upload**: If the remote directory already contains files, the plugin will ask for confirmation or execute backups based on your configuration.
|
|
103
|
+
- **Selective backup**: When `singleBack: true` is configured, only files specified in `singleBackFiles` are backed up.
|
|
104
|
+
- **Manual confirmation**: When `autoUpload: false`, the plugin asks for manual confirmation in the CLI before proceeding.
|
|
105
|
+
- **Pipeline integration**: When `failOnError: true` and upload fails, the build command will exit with a non-zero code to block subsequent pipeline steps.
|
|
106
|
+
- **Module format**: This version only supports ESM (`import` syntax); `require` is not supported.
|
|
107
|
+
|
|
108
|
+
## Risks & Best Practices
|
|
109
|
+
|
|
110
|
+
- **Security**: Do not hardcode FTP usernames and passwords in `vite.config.ts`. Always use environment variables.
|
|
111
|
+
- **Safety**: Ensure you control the production deployments via environment variables (like `open: process.env.DEPLOY_FTP === '1'`) to prevent local routine builds from overwriting production files.
|
|
112
|
+
- **Multiple Targets**: Ensure all paths listed in `uploadPath` are intended targets, especially when uploading to production environments.
|
|
113
|
+
- **Backup Speed**: Full backups require downloading the remote directory and uploading a zip archive back. This can be slow if the remote directory is large.
|
|
114
|
+
- **Rate Limits**: If the remote FTP server is unstable or rate-limited, do not set `concurrency` too high.
|
|
115
|
+
|
|
116
|
+
## Examples
|
|
117
|
+
|
|
118
|
+
### Multiple FTP Servers
|
|
50
119
|
|
|
51
120
|
```ts
|
|
52
|
-
// vite.config.ts
|
|
53
121
|
import vitePluginDeployFtp from 'vite-plugin-deploy-ftp'
|
|
54
122
|
|
|
55
123
|
export default {
|
|
56
124
|
plugins: [
|
|
57
125
|
vitePluginDeployFtp({
|
|
58
|
-
open:
|
|
126
|
+
open: process.env.DEPLOY_FTP === '1',
|
|
127
|
+
autoUpload: true,
|
|
59
128
|
uploadPath: '/public_html',
|
|
60
|
-
|
|
61
|
-
singleBackFiles: ['index.html'],
|
|
62
|
-
maxRetries: 3,
|
|
63
|
-
retryDelay: 1000,
|
|
129
|
+
defaultFtp: 'production',
|
|
64
130
|
ftps: [
|
|
65
131
|
{
|
|
66
|
-
name: '
|
|
67
|
-
host:
|
|
132
|
+
name: 'production',
|
|
133
|
+
host: process.env.FTP_PROD_HOST,
|
|
68
134
|
port: 21,
|
|
69
|
-
user:
|
|
70
|
-
password:
|
|
71
|
-
alias: 'https://
|
|
135
|
+
user: process.env.FTP_PROD_USER,
|
|
136
|
+
password: process.env.FTP_PROD_PASSWORD,
|
|
137
|
+
alias: 'https://example.com',
|
|
72
138
|
},
|
|
73
139
|
{
|
|
74
|
-
name: '
|
|
75
|
-
host:
|
|
140
|
+
name: 'test',
|
|
141
|
+
host: process.env.FTP_TEST_HOST,
|
|
76
142
|
port: 21,
|
|
77
|
-
user:
|
|
78
|
-
password:
|
|
79
|
-
alias: 'https://test.com',
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
name: '开发环境',
|
|
83
|
-
host: 'ftp.dev.com',
|
|
84
|
-
port: 21,
|
|
85
|
-
user: 'dev_user',
|
|
86
|
-
password: 'dev_password',
|
|
87
|
-
alias: 'https://dev.com',
|
|
143
|
+
user: process.env.FTP_TEST_USER,
|
|
144
|
+
password: process.env.FTP_TEST_PASSWORD,
|
|
145
|
+
alias: 'https://test.example.com',
|
|
88
146
|
},
|
|
89
147
|
],
|
|
90
148
|
}),
|
|
@@ -92,96 +150,70 @@ export default {
|
|
|
92
150
|
}
|
|
93
151
|
```
|
|
94
152
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
### 通用参数
|
|
98
|
-
|
|
99
|
-
| 参数 | 类型 | 默认值 | 说明 |
|
|
100
|
-
| ----------------- | ---------- | ---------------- | -------------------------------- |
|
|
101
|
-
| `open` | `boolean` | `true` | 是否启用插件 |
|
|
102
|
-
| `uploadPath` | `string` | - | FTP 服务器上的上传路径 |
|
|
103
|
-
| `singleBack` | `boolean` | `false` | 是否使用单文件备份模式 |
|
|
104
|
-
| `singleBackFiles` | `string[]` | `['index.html']` | 单文件备份模式下要备份的文件列表 |
|
|
105
|
-
| `maxRetries` | `number` | `3` | 连接失败时的最大重试次数 |
|
|
106
|
-
| `retryDelay` | `number` | `1000` | 重试延迟时间(毫秒) |
|
|
107
|
-
|
|
108
|
-
### 单个 FTP 配置参数
|
|
109
|
-
|
|
110
|
-
| 参数 | 类型 | 默认值 | 说明 |
|
|
111
|
-
| ---------- | -------- | ------ | -------------------------- |
|
|
112
|
-
| `host` | `string` | - | FTP 服务器地址 |
|
|
113
|
-
| `port` | `number` | `21` | FTP 服务器端口 |
|
|
114
|
-
| `user` | `string` | - | FTP 用户名 |
|
|
115
|
-
| `password` | `string` | - | FTP 密码 |
|
|
116
|
-
| `alias` | `string` | `''` | 网站别名,用于生成完整 URL |
|
|
117
|
-
|
|
118
|
-
### 多个 FTP 配置参数
|
|
119
|
-
|
|
120
|
-
| 参数 | 类型 | 说明 |
|
|
121
|
-
| ------ | ------------- | ------------------ |
|
|
122
|
-
| `ftps` | `FtpConfig[]` | FTP 服务器配置数组 |
|
|
123
|
-
|
|
124
|
-
#### FtpConfig 对象
|
|
125
|
-
|
|
126
|
-
| 参数 | 类型 | 默认值 | 说明 |
|
|
127
|
-
| ---------- | -------- | ------ | ---------------------------------- |
|
|
128
|
-
| `name` | `string` | - | FTP 服务器名称(用于选择界面显示) |
|
|
129
|
-
| `host` | `string` | - | FTP 服务器地址 |
|
|
130
|
-
| `port` | `number` | `21` | FTP 服务器端口 |
|
|
131
|
-
| `user` | `string` | - | FTP 用户名 |
|
|
132
|
-
| `password` | `string` | - | FTP 密码 |
|
|
133
|
-
| `alias` | `string` | `''` | 网站别名,用于生成完整 URL |
|
|
134
|
-
|
|
135
|
-
## 功能特性
|
|
136
|
-
|
|
137
|
-
### 多服务器选择
|
|
138
|
-
|
|
139
|
-
当使用多个 FTP 配置时,插件会显示一个多选界面,让您选择要上传到哪些服务器:
|
|
140
|
-
|
|
141
|
-
```
|
|
142
|
-
? 选择要上传的FTP服务器(可多选)
|
|
143
|
-
❯ ◯ 生产环境
|
|
144
|
-
◯ 测试环境
|
|
145
|
-
◯ 开发环境
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
### 备份功能
|
|
149
|
-
|
|
150
|
-
插件提供两种备份模式:
|
|
151
|
-
|
|
152
|
-
1. **完整备份**: 将远程目录下的所有文件打包备份
|
|
153
|
-
2. **选择性备份**: 只备份指定的文件(通过 `singleBackFiles` 配置)
|
|
154
|
-
|
|
155
|
-
### 连接重试
|
|
156
|
-
|
|
157
|
-
当 FTP 连接失败时,插件会自动重试,您可以通过 `maxRetries` 和 `retryDelay` 参数控制重试行为。
|
|
158
|
-
|
|
159
|
-
## 环境变量示例
|
|
160
|
-
|
|
161
|
-
建议将敏感信息(如用户名和密码)放在环境变量中:
|
|
162
|
-
|
|
163
|
-
```bash
|
|
164
|
-
# .env
|
|
165
|
-
VITE_FTP_HOST=ftp.example.com
|
|
166
|
-
VITE_FTP_PORT=21
|
|
167
|
-
VITE_FTP_USER=username
|
|
168
|
-
VITE_FTP_PASSWORD=password
|
|
169
|
-
VITE_FTP_PATH=/public_html
|
|
170
|
-
VITE_FTP_ALIAS=https://example.com
|
|
171
|
-
```
|
|
153
|
+
### Multiple Upload Directories
|
|
172
154
|
|
|
173
155
|
```ts
|
|
174
|
-
|
|
156
|
+
import vitePluginDeployFtp from 'vite-plugin-deploy-ftp'
|
|
157
|
+
|
|
175
158
|
export default {
|
|
176
159
|
plugins: [
|
|
177
160
|
vitePluginDeployFtp({
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
161
|
+
open: process.env.DEPLOY_FTP === '1',
|
|
162
|
+
autoUpload: true,
|
|
163
|
+
host: process.env.FTP_HOST,
|
|
164
|
+
user: process.env.FTP_USER,
|
|
165
|
+
password: process.env.FTP_PASSWORD,
|
|
166
|
+
uploadPath: ['/public_html', '/backup_html'],
|
|
167
|
+
alias: 'https://example.com',
|
|
184
168
|
}),
|
|
185
169
|
],
|
|
186
170
|
}
|
|
187
171
|
```
|
|
172
|
+
|
|
173
|
+
## Options Reference
|
|
174
|
+
|
|
175
|
+
### General Options
|
|
176
|
+
|
|
177
|
+
| Options | Type | Default | Description |
|
|
178
|
+
| :---------------- | :------------------- | :--------------- | :--------------------------------------------------------------------------- |
|
|
179
|
+
| `open` | `boolean` | `true` | Enable or disable the plugin. |
|
|
180
|
+
| `uploadPath` | `string \| string[]` | - | FTP destination path(s). Array values will upload sequentially to all paths. |
|
|
181
|
+
| `singleBack` | `boolean` | `false` | Enable single file backup mode. |
|
|
182
|
+
| `singleBackFiles` | `string[]` | `['index.html']` | List of file paths to back up when `singleBack` is enabled. |
|
|
183
|
+
| `debug` | `boolean` | `false` | Enable verbose debug logs and duration metrics. |
|
|
184
|
+
| `maxRetries` | `number` | `3` | Maximum retry attempts for connection/upload failures. |
|
|
185
|
+
| `retryDelay` | `number` | `1000` | Delay between retry attempts (ms). |
|
|
186
|
+
| `showBackFile` | `boolean` | `false` | Print backup file list to the console. |
|
|
187
|
+
| `autoUpload` | `boolean` | `false` | Bypass CLI confirmation prompt before starting uploads. |
|
|
188
|
+
| `fancy` | `boolean` | `true` | Enable stylish console UI outputs. |
|
|
189
|
+
| `failOnError` | `boolean` | `true` | Throw errors to fail the Vite build command on upload failure. |
|
|
190
|
+
| `concurrency` | `number` | `1` | Number of simultaneous file uploads. |
|
|
191
|
+
|
|
192
|
+
### Single FTP Configuration
|
|
193
|
+
|
|
194
|
+
| Options | Type | Default | Description |
|
|
195
|
+
| :--------- | :------- | :------ | :-------------------------------------------------------- |
|
|
196
|
+
| `name` | `string` | - | Identifier for the FTP configuration. |
|
|
197
|
+
| `host` | `string` | - | FTP host address. |
|
|
198
|
+
| `port` | `number` | `21` | FTP port. |
|
|
199
|
+
| `user` | `string` | - | FTP username. |
|
|
200
|
+
| `password` | `string` | - | FTP password. |
|
|
201
|
+
| `alias` | `string` | `''` | Public site URL alias used to format the final page link. |
|
|
202
|
+
|
|
203
|
+
### Multiple FTP Configuration
|
|
204
|
+
|
|
205
|
+
| Options | Type | Description |
|
|
206
|
+
| :----------- | :------------ | :----------------------------------------------- |
|
|
207
|
+
| `ftps` | `FtpConfig[]` | List of FTP server configurations. |
|
|
208
|
+
| `defaultFtp` | `string` | Default FTP config name to select automatically. |
|
|
209
|
+
|
|
210
|
+
### FtpConfig Object
|
|
211
|
+
|
|
212
|
+
| Options | Type | Default | Description |
|
|
213
|
+
| :--------- | :------- | :------ | :-------------------------------------------------------- |
|
|
214
|
+
| `name` | `string` | - | FTP configuration name (shown in selection prompt). |
|
|
215
|
+
| `host` | `string` | - | FTP host address. |
|
|
216
|
+
| `port` | `number` | `21` | FTP port. |
|
|
217
|
+
| `user` | `string` | - | FTP username. |
|
|
218
|
+
| `password` | `string` | - | FTP password. |
|
|
219
|
+
| `alias` | `string` | `''` | Public site URL alias used to format the final page link. |
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -135,7 +135,8 @@ var renderPanel = (title, rows, tone = "info", footer) => {
|
|
|
135
135
|
const paddedLabel = padVisual(row.label, labelWidth);
|
|
136
136
|
const prefix = `${paddedLabel} `;
|
|
137
137
|
const availableValueWidth = Math.max(8, innerWidth - stringWidth(prefix));
|
|
138
|
-
|
|
138
|
+
const value = row.preserveValue ? row.value : fitVisual(row.value, availableValueWidth);
|
|
139
|
+
contentLines.push(`${chalk3.gray(prefix)}${value}`);
|
|
139
140
|
}
|
|
140
141
|
}
|
|
141
142
|
if (footer) {
|
|
@@ -144,7 +145,10 @@ var renderPanel = (title, rows, tone = "info", footer) => {
|
|
|
144
145
|
}
|
|
145
146
|
const top = color(`\u256D${"\u2500".repeat(innerWidth + 2)}\u256E`);
|
|
146
147
|
const bottom = color(`\u2570${"\u2500".repeat(innerWidth + 2)}\u256F`);
|
|
147
|
-
const body = contentLines.map((line) =>
|
|
148
|
+
const body = contentLines.map((line) => {
|
|
149
|
+
const fittedLine = stringWidth(line) > innerWidth ? line : fitVisual(line, innerWidth);
|
|
150
|
+
return `${color("\u2502")} ${fittedLine} ${color("\u2502")}`;
|
|
151
|
+
}).join("\n");
|
|
148
152
|
return `${top}
|
|
149
153
|
${body}
|
|
150
154
|
${bottom}`;
|
|
@@ -272,7 +276,8 @@ var renderBackupPanel = (summary) => {
|
|
|
272
276
|
{ label: "\u7ED3\u679C:", value: chalk5.green(`${summary.items.length} \u4E2A\u5907\u4EFD\u6587\u4EF6`) },
|
|
273
277
|
...previewItems.map((item, index) => ({
|
|
274
278
|
label: `\u6587\u4EF6 ${index + 1}:`,
|
|
275
|
-
value: chalk5.cyan(
|
|
279
|
+
value: chalk5.cyan(item),
|
|
280
|
+
preserveValue: true
|
|
276
281
|
}))
|
|
277
282
|
];
|
|
278
283
|
if (summary.items.length > previewItems.length) {
|
|
@@ -311,7 +316,12 @@ function vitePluginDeployFtp(option) {
|
|
|
311
316
|
const isMultiFtp = "ftps" in safeOption;
|
|
312
317
|
const ftpConfigs = isMultiFtp ? safeOption.ftps || [] : [{ ...safeOption, name: safeOption.name || safeOption.alias || safeOption.host }];
|
|
313
318
|
const defaultFtp = isMultiFtp ? safeOption.defaultFtp : void 0;
|
|
314
|
-
const
|
|
319
|
+
const uploadPaths = Array.isArray(uploadPath) ? uploadPath : [uploadPath];
|
|
320
|
+
const normalizedUploadPaths = Array.from(
|
|
321
|
+
new Set(
|
|
322
|
+
uploadPaths.map((targetPath) => typeof targetPath === "string" ? normalizeFtpUploadPath(targetPath) : "/")
|
|
323
|
+
)
|
|
324
|
+
);
|
|
315
325
|
let outDir = normalizePath2(path2.resolve("dist"));
|
|
316
326
|
let upload = false;
|
|
317
327
|
let buildFailed = false;
|
|
@@ -323,7 +333,14 @@ function vitePluginDeployFtp(option) {
|
|
|
323
333
|
};
|
|
324
334
|
const validateOptions = () => {
|
|
325
335
|
const errors = [];
|
|
326
|
-
if (
|
|
336
|
+
if (uploadPaths.length === 0) {
|
|
337
|
+
errors.push("uploadPath is required");
|
|
338
|
+
}
|
|
339
|
+
uploadPaths.forEach((targetPath, index) => {
|
|
340
|
+
if (typeof targetPath !== "string" || targetPath.trim() === "") {
|
|
341
|
+
errors.push(`uploadPath${uploadPaths.length > 1 ? `[${index}]` : ""} is required`);
|
|
342
|
+
}
|
|
343
|
+
});
|
|
327
344
|
if (!Number.isInteger(maxRetries) || maxRetries < 1) errors.push("maxRetries must be >= 1");
|
|
328
345
|
if (!Number.isFinite(retryDelay) || retryDelay < 0) errors.push("retryDelay must be >= 0");
|
|
329
346
|
if (!Number.isInteger(concurrency) || concurrency < 1) errors.push("concurrency must be >= 1");
|
|
@@ -737,7 +754,7 @@ function vitePluginDeployFtp(option) {
|
|
|
737
754
|
);
|
|
738
755
|
return { results, debugEntries };
|
|
739
756
|
};
|
|
740
|
-
const deploySingleTarget = async (ftpConfig) => {
|
|
757
|
+
const deploySingleTarget = async (ftpConfig, normalizedUploadPath) => {
|
|
741
758
|
const { host, port = 21, user, password, alias = "", name } = ftpConfig;
|
|
742
759
|
const normalizedAlias = alias ? normalizeUrlLikeBase(alias) : "";
|
|
743
760
|
if (!host || !user || !password) {
|
|
@@ -757,10 +774,11 @@ function vitePluginDeployFtp(option) {
|
|
|
757
774
|
});
|
|
758
775
|
const totalFiles = allFiles.length;
|
|
759
776
|
const displayName = name || host;
|
|
777
|
+
const resultName = normalizedUploadPaths.length > 1 ? `${displayName} ${normalizedUploadPath}` : displayName;
|
|
760
778
|
const startTime = Date.now();
|
|
761
779
|
if (allFiles.length === 0) {
|
|
762
780
|
console.log(`${getLogSymbol("warning")} \u6CA1\u6709\u627E\u5230\u9700\u8981\u4E0A\u4F20\u7684\u6587\u4EF6`);
|
|
763
|
-
return { name:
|
|
781
|
+
return { name: resultName, totalFiles: 0, failedCount: 0 };
|
|
764
782
|
}
|
|
765
783
|
clearScreen();
|
|
766
784
|
console.log(
|
|
@@ -903,7 +921,7 @@ function vitePluginDeployFtp(option) {
|
|
|
903
921
|
}
|
|
904
922
|
];
|
|
905
923
|
if (accessUrl) {
|
|
906
|
-
resultRows.push({ label: "\u8BBF\u95EE:", value: chalk5.cyan(
|
|
924
|
+
resultRows.push({ label: "\u8BBF\u95EE:", value: chalk5.cyan(accessUrl), preserveValue: true });
|
|
907
925
|
}
|
|
908
926
|
if (failedCount > 0) {
|
|
909
927
|
const failedItems = results.filter((result) => !result.success).slice(0, 2);
|
|
@@ -936,7 +954,7 @@ function vitePluginDeployFtp(option) {
|
|
|
936
954
|
});
|
|
937
955
|
console.log(renderDebugPanel(debugEntries));
|
|
938
956
|
}
|
|
939
|
-
return { name:
|
|
957
|
+
return { name: resultName, totalFiles: results.length, failedCount };
|
|
940
958
|
} catch (error) {
|
|
941
959
|
if (preflightSpinner) preflightSpinner.stop();
|
|
942
960
|
console.log(`
|
|
@@ -950,7 +968,7 @@ ${getLogSymbol("danger")} \u4E0A\u4F20\u8FC7\u7A0B\u4E2D\u53D1\u751F\u9519\u8BEF
|
|
|
950
968
|
console.log(renderDebugPanel(debugEntries));
|
|
951
969
|
}
|
|
952
970
|
return {
|
|
953
|
-
name:
|
|
971
|
+
name: resultName,
|
|
954
972
|
totalFiles,
|
|
955
973
|
failedCount: totalFiles > 0 ? totalFiles : 1,
|
|
956
974
|
error: error instanceof Error ? error : new Error(String(error))
|
|
@@ -1021,8 +1039,10 @@ ${getLogSymbol("danger")} \u4E0A\u4F20\u8FC7\u7A0B\u4E2D\u53D1\u751F\u9519\u8BEF
|
|
|
1021
1039
|
}
|
|
1022
1040
|
const deployResults = [];
|
|
1023
1041
|
for (const ftpConfig of selectedConfigs) {
|
|
1024
|
-
const
|
|
1025
|
-
|
|
1042
|
+
for (const normalizedUploadPath of normalizedUploadPaths) {
|
|
1043
|
+
const targetResult = await deploySingleTarget(ftpConfig, normalizedUploadPath);
|
|
1044
|
+
deployResults.push(targetResult);
|
|
1045
|
+
}
|
|
1026
1046
|
}
|
|
1027
1047
|
return deployResults;
|
|
1028
1048
|
};
|