chore: 初始化
commit
d7474504e7
|
@ -0,0 +1,48 @@
|
|||
const { execSync } = require('child_process');
|
||||
|
||||
const metas = [
|
||||
{ type: 'feat', section: '✨ Features | 新功能' },
|
||||
{ type: 'fix', section: '🐛 Bug Fixes | Bug 修复' },
|
||||
{ type: 'test', section: '✅ Tests | 测试' },
|
||||
{ type: 'docs', section: '📝 Documentation | 文档' },
|
||||
{ type: 'build', section: '👷 Build System | 构建' },
|
||||
{ type: 'ci', section: '🔧 Continuous Integration | CI 配置' },
|
||||
{ type: 'perf', section: '⚡ Performance Improvements | 性能优化' },
|
||||
{ type: 'revert', section: '⏪ Reverts | 回退' },
|
||||
{ type: 'chore', section: '📦 Chores | 其他更新' },
|
||||
{ type: 'style', section: '💄 Styles | 风格', hidden: true },
|
||||
{ type: 'refactor', section: '♻ Code Refactoring | 代码重构' },
|
||||
];
|
||||
|
||||
/** @type {import('cz-git').UserConfig} */
|
||||
module.exports = {
|
||||
rules: {
|
||||
// @see: https://commitlint.js.org/#/reference-rules
|
||||
'subject-min-length': [0, 'always', 3],
|
||||
'subject-max-length': [0, 'always', 80],
|
||||
'type-enum': [0, 'always', metas.map((meta) => meta.type)],
|
||||
},
|
||||
prompt: {
|
||||
messages: {
|
||||
type: '请选择提交类型:',
|
||||
subject: '请输入变更描述:\n',
|
||||
footer: '列举关联的 issue (可选) 例如: #31, #I3244:\n',
|
||||
confirmCommit: '是否提交?',
|
||||
},
|
||||
types: metas.map((meta) => ({
|
||||
value: meta.type,
|
||||
name: `${`${meta.type}:`.padEnd(10, ' ')}${meta.section}`,
|
||||
})),
|
||||
skipQuestions: ['scope', 'body', 'breaking', 'footerPrefix'],
|
||||
formatMessageCB: (commit) =>
|
||||
`${commit?.defaultMessage}\n\nCo-authored-by: ${readGitUser(
|
||||
'name',
|
||||
)} <${readGitUser('email')}>`,
|
||||
},
|
||||
};
|
||||
|
||||
function readGitUser(key) {
|
||||
return execSync(`git config user.${key}`)
|
||||
.toString()
|
||||
.replace(/(\r\n\t|\n|\r\t)/g, '');
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
# editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"env": {
|
||||
"es2022": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"root": true,
|
||||
"rules": {
|
||||
"@typescript-eslint/no-explicit-any": 0
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
node_modules
|
||||
dist
|
||||
.eslintcache
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"printWidth": 80,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"bracketSpacing": true,
|
||||
"arrowParens": "always"
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 初秋
|
||||
|
||||
Permission is hereby granted, free of charge, to unknown person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,571 @@
|
|||
# axios-miniprogram
|
||||
|
||||
[](https://badge.fury.io/js/axios-miniprogram)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
|
||||
## 安装
|
||||
|
||||
```bash
|
||||
$ yarn add axios-miniprogram
|
||||
```
|
||||
|
||||
或者
|
||||
|
||||
```bash
|
||||
$ npm i axios-miniprogram
|
||||
```
|
||||
|
||||
## 简介
|
||||
|
||||
为小程序平台量身定制的轻量级请求库,请求配置以微信小程序作为标准,其他平台兼容实现。
|
||||
|
||||
- 支持 微信小程序、支付宝小程序、百度小程序、字节跳动小程序、QQ 小程序、uniapp。
|
||||
- 支持 `Typescript`,健全的类型系统,智能的 `IDE` 提示。
|
||||
- 支持 `Promise`。
|
||||
- 支持 拦截器。
|
||||
- 支持 取消请求。
|
||||
- 支持 自定义合法状态码。
|
||||
- 支持 自定义参数序列化。
|
||||
- 支持 自定义转换数据。
|
||||
- 支持 自定义错误处理。
|
||||
- 支持 自定义平台适配器
|
||||
|
||||
## 使用
|
||||
|
||||
### 如何引入
|
||||
|
||||
```typescript
|
||||
// esm
|
||||
import axios from 'axios-miniprogram';
|
||||
// cjs
|
||||
const axios = require('axios-miniprogram');
|
||||
// 使用
|
||||
axios('/user');
|
||||
```
|
||||
|
||||
### `axios(config)`
|
||||
|
||||
可以通过将相关配置传递给`axios`来发送请求。
|
||||
|
||||
```typescript
|
||||
// 发送 GET 请求
|
||||
axios({
|
||||
url: '/user',
|
||||
method: 'get',
|
||||
params: {
|
||||
id: 1,
|
||||
},
|
||||
})
|
||||
.then((response) => {
|
||||
// 请求成功后做些什么
|
||||
})
|
||||
.catch((error) => {
|
||||
// 请求失败后做些什么
|
||||
});
|
||||
|
||||
// 发送 POST 请求
|
||||
axios({
|
||||
url: '/user',
|
||||
method: 'post',
|
||||
data: {
|
||||
id: 1,
|
||||
},
|
||||
})
|
||||
.then((response) => {
|
||||
// 请求成功后做些什么
|
||||
})
|
||||
.catch((error) => {
|
||||
// 请求失败后做些什么
|
||||
});
|
||||
```
|
||||
|
||||
### `axios(url, config?)`
|
||||
|
||||
也可以通过直接把`url`传给`axios`来发送请求。
|
||||
|
||||
```typescript
|
||||
// 默认发送 GET 请求
|
||||
axios('/user')
|
||||
.then((response) => {
|
||||
// 请求成功后做些什么
|
||||
})
|
||||
.catch((error) => {
|
||||
// 请求失败后做些什么
|
||||
});
|
||||
|
||||
// 发送 POST 请求
|
||||
axios('/user', {
|
||||
method: 'post',
|
||||
})
|
||||
.then((response) => {
|
||||
// 请求成功后做些什么
|
||||
})
|
||||
.catch((error) => {
|
||||
// 请求失败后做些什么
|
||||
});
|
||||
```
|
||||
|
||||
还可以使用请求方法的别名来简化请求。
|
||||
|
||||
- ##### axios.request(config)
|
||||
- ##### axios.options(url, config?)
|
||||
- ##### axios.get(url, params?, config?)
|
||||
- ##### axios.head(url, params?, config?)
|
||||
- ##### axios.post(url, data?, config?)
|
||||
- ##### axios.put(url, data?, config?)
|
||||
- ##### axios.delete(url, params?, config?)
|
||||
- ##### axios.trace(url, config?)
|
||||
- ##### axios.connect(url, config?)
|
||||
|
||||
常用例子,其他同理:
|
||||
|
||||
```typescript
|
||||
// 发送 GET 请求
|
||||
axios.get('/user');
|
||||
|
||||
// 携带参数
|
||||
axios.get('/user', {
|
||||
test: 1,
|
||||
});
|
||||
|
||||
// 携带额外配置
|
||||
axios.get(
|
||||
'/user',
|
||||
{
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
// 发送 POST 请求
|
||||
axios.post('/user');
|
||||
|
||||
// 携带数据
|
||||
axios.post('/user', {
|
||||
id: 1,
|
||||
});
|
||||
|
||||
// 携带额外配置
|
||||
axios.post(
|
||||
'/user',
|
||||
{
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
},
|
||||
},
|
||||
);
|
||||
```
|
||||
|
||||
## 动态 URL
|
||||
|
||||
您可以使用动态 URL 提高 RESTful API 的书写体验。
|
||||
|
||||
```typescript
|
||||
axios.get('/user/:id', {
|
||||
id: 1,
|
||||
});
|
||||
|
||||
// url => '/user/1?id=1'
|
||||
```
|
||||
|
||||
## 配置`config`
|
||||
|
||||
非全平台兼容的属性只会在平台支持的情况下生效。
|
||||
|
||||
| 参数 | 类型 | 默认值 | 说明 | 全平台兼容 |
|
||||
| :---------------- | :-----------------------: | :------------------------------------------------------------------------------------ | :----------------- | :--------- |
|
||||
| adapter | Function | [查看](https://github.com/early-autumn/axios-miniprogram/blob/master/src/defaults.ts) | 自定义适配器 | 是 |
|
||||
| baseURL | String | | 基础地址 | 是 |
|
||||
| url | String | | 请求地址 | 是 |
|
||||
| method | String | get | 请求方法 | |
|
||||
| params | Object | | 请求参数 | 是 |
|
||||
| data | String/Object/ArrayBuffer | | 请求数据 | 是 |
|
||||
| headers | Object | [查看](https://github.com/early-autumn/axios-miniprogram/blob/master/src/defaults.ts) | 请求头 | 是 |
|
||||
| validateStatus | Function | [查看](https://github.com/early-autumn/axios-miniprogram/blob/master/src/defaults.ts) | 自定义合法状态码 | 是 |
|
||||
| paramsSerializer | Function | | 自定义参数序列化 | 是 |
|
||||
| transformRequest | Function/Array<.Function> | | 自定义转换请求数据 | 是 |
|
||||
| transformResponse | Function/Array<.Function> | | 自定义转换响应数据 | 是 |
|
||||
| errorHandler | Function | | 自定义错误处理 | 是 |
|
||||
| cancelToken | Object | | 取消令牌 | 是 |
|
||||
| timeout | Number | 10000 | 超时时间 | |
|
||||
| dataType | String | json | 响应数据格式 | 是 |
|
||||
| responseType | String | text | 响应数据类型 | 是 |
|
||||
| enableHttp2 | Boolean | false | 开启 http2 | |
|
||||
| enableQuic | Boolean | false | 开启 quic | |
|
||||
| enableCache | Boolean | false | 开启 cache | |
|
||||
| sslVerify | Boolean | true | 验证 ssl 证书 | |
|
||||
|
||||
#### `config.method`的合法值
|
||||
|
||||
可以使用大写,也可以使用小写。
|
||||
|
||||
| 值 | 说明 | 全平台兼容 |
|
||||
| :------ | :--- | :--------- |
|
||||
| OPTIONS | | |
|
||||
| GET | | 是 |
|
||||
| HEAD | | |
|
||||
| POST | | 是 |
|
||||
| PUT | | 是 |
|
||||
| DELETE | | 是 |
|
||||
| TRACE | | |
|
||||
| CONNECT | | |
|
||||
|
||||
#### `config.dataType`的合法值
|
||||
|
||||
| 值 | 说明 | 全平台兼容 |
|
||||
| :--- | :--------------------------------------------------------- | :--------- |
|
||||
| json | 返回的数据为 JSON,返回后会对返回的数据进行一次 JSON.parse | 是 |
|
||||
| 其他 | 不对返回的内容进行 JSON.parse | 是 |
|
||||
|
||||
#### `config.responseType`的合法值
|
||||
|
||||
| 值 | 说明 | 全平台兼容 |
|
||||
| :---------- | :----------------------- | :--------- |
|
||||
| text | 响应的数据为文本 | 是 |
|
||||
| arraybuffer | 响应的数据为 ArrayBuffer | 是 |
|
||||
|
||||
#### 自定义合法状态码`config.validateStatus`
|
||||
|
||||
可以让请求按照您的要求成功或者失败。
|
||||
|
||||
```typescript
|
||||
axios('/user', {
|
||||
validateStatus: function validateStatus(status) {
|
||||
// 这样,状态码在 200 到 400 之间都是请求成功
|
||||
return status >= 200 && status < 400;
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
#### 自定义参数序列化`config.paramsSerializer`
|
||||
|
||||
可以使用自己的规则去序列化参数。
|
||||
|
||||
```typescript
|
||||
axios('/user', {
|
||||
paramsSerializer: function paramsSerializer(params) {
|
||||
return qs.stringify(params, {
|
||||
arrayFormat: 'brackets',
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
#### 自定义转换数据
|
||||
|
||||
可以在请求发出之前转换请求数据,在请求成功之后转换响应数据。
|
||||
|
||||
```typescript
|
||||
axios('/user', {
|
||||
transformRequest: [
|
||||
function transformRequest(data, headers) {
|
||||
// 转换请求数据
|
||||
return data;
|
||||
},
|
||||
],
|
||||
transformResponse: [
|
||||
function transformResponse(data) {
|
||||
// 转换响应数据
|
||||
return data;
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
#### 自定义错误处理`config.errorHandler`
|
||||
|
||||
可以添加到默认配置中,统一处理错误。
|
||||
|
||||
```typescript
|
||||
axios.defaults.errorHandler = function errorHandler(error) {
|
||||
// 做一些想做的事情
|
||||
return Promise.reject(error);
|
||||
};
|
||||
|
||||
const instance = axios.create({
|
||||
errorHandler: function errorHandler(error) {
|
||||
// 做一些想做的事情
|
||||
return Promise.reject(error);
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
也可以发送请求时通过自定义配置传入。
|
||||
|
||||
```typescript
|
||||
axios('/user', {
|
||||
errorHandler: function errorHandler(error) {
|
||||
// 做一些想做的事情
|
||||
return Promise.reject(error);
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
#### 自定义平台适配器`config.adapter`
|
||||
|
||||
您可以手动适配当前所处的平台。
|
||||
|
||||
```typescript
|
||||
axios.defaults.adapter = function adapter(adapterConfig) {
|
||||
const {
|
||||
// 请求地址
|
||||
url,
|
||||
// 请求方法
|
||||
method,
|
||||
// 请求数据
|
||||
data,
|
||||
// 请求头 同 headers
|
||||
headers,
|
||||
// 请求头 同 headers
|
||||
headers,
|
||||
// 响应数据格式
|
||||
dataType,
|
||||
// 响应数据类型
|
||||
responseType,
|
||||
// 超时时间
|
||||
timeout,
|
||||
// 开启 http2
|
||||
enableHttp2,
|
||||
// 开启 quic
|
||||
enableQuic,
|
||||
// 开启 cache
|
||||
enableCache,
|
||||
// 验证 ssl 证书
|
||||
sslVerify,
|
||||
// 成功的回调函数
|
||||
success,
|
||||
// 失败的回调函数
|
||||
fail,
|
||||
} = adapterConfig;
|
||||
|
||||
// 在 adapterConfig 中选择您需要的参数发送请求
|
||||
return wx.request({
|
||||
url,
|
||||
method,
|
||||
data,
|
||||
headers,
|
||||
success,
|
||||
fail,
|
||||
});
|
||||
};
|
||||
|
||||
// 如果 adapterConfig 的数据结构适用于当前平台,则可以。
|
||||
axios.defaults.adapter = wx.request;
|
||||
```
|
||||
|
||||
#### 默认配置`defaults`
|
||||
|
||||
##### 全局默认配置`axios.defaults`
|
||||
|
||||
```typescript
|
||||
axios.defaults.baseURL = 'https://www.api.com';
|
||||
axios.defaults.headers.common['Accept'] = 'application/json, test/plain, */*';
|
||||
axios.defaults.headers.post['Content-Type'] =
|
||||
'application/x-www-form-urlencoded; charset=utf-8';
|
||||
```
|
||||
|
||||
##### 自定义实例默认配置
|
||||
|
||||
可以创建时传入。
|
||||
|
||||
```typescript
|
||||
const instance = axios.create({
|
||||
baseURL: 'https://www.api.com',
|
||||
headers: {
|
||||
common: {
|
||||
Accept: 'application/json, test/plain, */*',
|
||||
},
|
||||
post: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
也可以创建后修改。
|
||||
|
||||
```typescript
|
||||
instance.defaults.baseURL = 'https://www.api.com';
|
||||
instance.defaults.headers.common['Accept'] =
|
||||
'application/json, test/plain, */*';
|
||||
instance.defaults.headers.post['Content-Type'] =
|
||||
'application/x-www-form-urlencoded; charset=utf-8';
|
||||
```
|
||||
|
||||
##### 配置优先顺序
|
||||
|
||||
发送请求时,会使用默认配置`defaults`和自定义配置`config`合并出请求配置`requestConfig`,然后用合并出的请求配置`requestConfig`去发送请求,多数情况下,后者优先级要高于前者,具体合并策略可以参考 [mergeConfig.ts](https://github.com/early-autumn/axios-miniprogram/blob/master/src/core/mergeConfig.ts) 的实现。
|
||||
|
||||
## 响应体`response`
|
||||
|
||||
非全平台兼容的属性只会在平台支持的情况下生效。
|
||||
|
||||
| 属性 | 类型 | 说明 | 全平台兼容 |
|
||||
| :--------- | :------------------------ | :------------------------------------------- | :--------- |
|
||||
| status | Number | 状态码 | 是 |
|
||||
| statusText | String | 状态文本 | 是 |
|
||||
| data | String/Object/ArrayBuffer | 开发者服务器返回的数据 | 是 |
|
||||
| headers | Object | 响应头 | 是 |
|
||||
| config | Object | Axios 请求配置 | 是 |
|
||||
| cookies | Array<.String> | 开发者服务器返回的 cookies,格式为字符串数组 | |
|
||||
| profile | Object | 网络请求过程中一些关键时间点的耗时信息 | |
|
||||
|
||||
## API
|
||||
|
||||
### `axios.interceptors`
|
||||
|
||||
可以先拦截请求或响应,然后再由 then 或 catch 处理。
|
||||
|
||||
```typescript
|
||||
// 添加请求拦截器
|
||||
axios.interceptors.request.use(
|
||||
function (config) {
|
||||
// 在发送请求之前做些什么
|
||||
return config;
|
||||
},
|
||||
function (error) {
|
||||
//处理请求错误
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
|
||||
// 添加响应拦截器
|
||||
axios.interceptors.response.use(
|
||||
function (response) {
|
||||
// 请求成功后做些什么
|
||||
return response;
|
||||
},
|
||||
function (error) {
|
||||
// 处理响应错误
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
```
|
||||
|
||||
如果以后需要删除拦截器,则可以。
|
||||
|
||||
```typescript
|
||||
const myInterceptor = axios.interceptors.request.use(function () {
|
||||
// 在发送请求之前做些什么
|
||||
});
|
||||
axios.interceptors.request.eject(myInterceptor);
|
||||
```
|
||||
|
||||
还可以将拦截器添加到`axios`的`自定义实例`中。
|
||||
|
||||
```typescript
|
||||
const myInterceptor = instance.interceptors.request.use(function () {
|
||||
// 在发送请求之前做些什么
|
||||
});
|
||||
instance.interceptors.request.eject(myInterceptor);
|
||||
```
|
||||
|
||||
### `axios.CancelToken`
|
||||
|
||||
可以使用`CancelToken`取消已经发出的请求。
|
||||
|
||||
```typescript
|
||||
let cancel;
|
||||
|
||||
axios('/api', {
|
||||
cancelToken: new axios.CancelToken(function (c) {
|
||||
cancel = c;
|
||||
}),
|
||||
});
|
||||
|
||||
cancel('取消请求');
|
||||
```
|
||||
|
||||
还可以使用`CancelToken.source`工厂方法创建`CancelToken`。
|
||||
|
||||
```typescript
|
||||
const source = axios.CancelToken.source();
|
||||
|
||||
axios('/api', {
|
||||
cancelToken: source.token,
|
||||
});
|
||||
|
||||
source.cancel('取消请求');
|
||||
```
|
||||
|
||||
### `axios.isCancel`
|
||||
|
||||
可以判断当前错误是否来自取消请求
|
||||
|
||||
```typescript
|
||||
axios('/user').catch((error) => {
|
||||
if (axios.isCancel(error)) {
|
||||
// 请求被取消了
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### `axios.getUri(config)`
|
||||
|
||||
根据配置中的`url`和`params`生成一个`URI`。
|
||||
|
||||
```typescript
|
||||
const uri = axios.getUri({
|
||||
url: '/user',
|
||||
params: {
|
||||
id: 1,
|
||||
},
|
||||
});
|
||||
// '/user?id=1'
|
||||
```
|
||||
|
||||
### `axios.create(defaults)`
|
||||
|
||||
创建一个`自定义实例`,传入的自定义默认配置`defaults`会和`axios`的默认配置`axios.defaults`合并成`自定义实例`的默认配置。
|
||||
|
||||
`自定义实例`拥有和`axios`相同的调用方式和请求方法的别名。
|
||||
|
||||
```typescript
|
||||
axios.defaults.baseURL = 'https://www.api.com';
|
||||
|
||||
const instance = axios.create({
|
||||
params: {
|
||||
id: 1,
|
||||
},
|
||||
});
|
||||
|
||||
instance('/user');
|
||||
// 'https://www.api.com/user?id=1'
|
||||
```
|
||||
|
||||
### `axios.Axios`
|
||||
|
||||
`axios.Axios`是一个类,其实`axios`就是`axios.Axios`类的实例改造而来的,`axios.create(defaults)`创建的也是`axios.Axios`的实例。
|
||||
|
||||
直接实例化`axios.Axios`可以得到一个`原始实例`,不能当函数调用,传入的自定义配置就是`原始实例`的默认配置,而不会像`axios.create(defaults)`一样去合并`axios`中的默认配置。
|
||||
|
||||
```typescript
|
||||
const instance = new axios.Axios({
|
||||
beseURL: 'https://www.api.com',
|
||||
params: {
|
||||
id: 1,
|
||||
},
|
||||
});
|
||||
|
||||
instance.get('/user');
|
||||
// 'https://www.api.com/user?id=1'
|
||||
```
|
||||
|
||||
## 执行流程
|
||||
|
||||
```typescript
|
||||
axios('/user').then().catch();
|
||||
|
||||
// 请求成功
|
||||
// axios => axios.interceptors.request => config.transformRequest => config.paramsSerializer => config.adapter => config.validateStatus => config.transformResponse => axios.interceptors.response => then
|
||||
|
||||
// 请求失败
|
||||
// axios => axios.interceptors.request => config.transformRequest => config.paramsSerializer => config.adapter => config.validateStatus => config.transformResponse => config.errorHandler => axios.interceptors.response => catch
|
||||
```
|
|
@ -0,0 +1,6 @@
|
|||
const pkg = require('../../package.json');
|
||||
|
||||
module.exports = {
|
||||
title: pkg.name,
|
||||
description: pkg.description,
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
# Hello
|
|
@ -0,0 +1,11 @@
|
|||
declare const uni: unknown;
|
||||
declare const wx: unknown;
|
||||
declare const my: unknown;
|
||||
declare const swan: unknown;
|
||||
declare const tt: unknown;
|
||||
declare const qq: unknown;
|
||||
declare const qh: unknown;
|
||||
declare const ks: unknown;
|
||||
declare const dd: unknown;
|
||||
|
||||
declare type AnyObject<T = any> = Record<string, T>;
|
|
@ -0,0 +1,79 @@
|
|||
{
|
||||
"name": "axios-miniprogram",
|
||||
"type": "module",
|
||||
"version": "2.0.0.alpha.0",
|
||||
"description": "基于 Promise 的 HTTP 请求库,适用于各大小程序平台。",
|
||||
"main": "dist/axios-miniprogram.esm.js",
|
||||
"module": "dist/axios-miniprogram.cjs.js",
|
||||
"types": "dist/axios-miniprogram.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/fluffff/axios-miniprogram.git"
|
||||
},
|
||||
"keywords": [
|
||||
"axios",
|
||||
"miniprogram",
|
||||
"request",
|
||||
"promise"
|
||||
],
|
||||
"author": "fluffff",
|
||||
"bugs": {
|
||||
"url": "https://github.com/fluffff/axios-miniprogram/issues"
|
||||
},
|
||||
"homepage": "https://github.com/fluffff/axios-miniprogram#readme",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"scripts": {
|
||||
"cz": "czg",
|
||||
"build": "esno scripts/build.ts",
|
||||
"release": "esno scripts/release.ts",
|
||||
"lint": "eslint --cache .",
|
||||
"lint:fix": "pnpm lint --fix",
|
||||
"postinstall": "simple-git-hooks"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^17.4.4",
|
||||
"@commitlint/config-conventional": "^17.4.4",
|
||||
"@esbuild-kit/cjs-loader": "^2.4.2",
|
||||
"@types/jest": "^29.5.0",
|
||||
"@types/node": "^18.15.5",
|
||||
"@typescript-eslint/eslint-plugin": "^5.55.0",
|
||||
"@typescript-eslint/parser": "^5.55.0",
|
||||
"archiver": "^5.3.1",
|
||||
"consola": "^2.15.3",
|
||||
"cz-git": "1.3.8",
|
||||
"czg": "1.3.8",
|
||||
"enquirer": "^2.3.6",
|
||||
"eslint": "^8.36.0",
|
||||
"esno": "^0.16.3",
|
||||
"jest": "^29.5.0",
|
||||
"lint-staged": "13.2.0",
|
||||
"minimist": "^1.2.8",
|
||||
"prettier": "2.8.5",
|
||||
"rimraf": "^4.4.0",
|
||||
"rollup": "^3.20.0",
|
||||
"rollup-plugin-dts": "^5.3.0",
|
||||
"rollup-plugin-esbuild": "^5.0.0",
|
||||
"semver": "^7.3.8",
|
||||
"simple-git-hooks": "^2.8.1",
|
||||
"typescript": "^5.0.2",
|
||||
"vitepress": "1.0.0-alpha.60"
|
||||
},
|
||||
"simple-git-hooks": {
|
||||
"pre-commit": "pnpm lint-staged",
|
||||
"commit-msg": "pnpm commitlint --edit $1"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "node_modules/cz-git"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.(j|t)s": "pnpm lint:fix"
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,55 @@
|
|||
import path from 'node:path';
|
||||
import esbuild from 'rollup-plugin-esbuild';
|
||||
import dtsPlugin from 'rollup-plugin-dts';
|
||||
import { __dirname, getPkgJSON } from './scripts/utils.js';
|
||||
|
||||
const pkg = getPkgJSON();
|
||||
const inputPath = path.resolve(__dirname, 'src/index.ts');
|
||||
const outputPath = path.resolve(__dirname, 'dist');
|
||||
const sourceMap = process.env.SOURCE_MAP === 'true';
|
||||
const dts = process.env.DTS === 'true';
|
||||
|
||||
export default main();
|
||||
|
||||
function main() {
|
||||
const configs = [buildConfig('esm'), buildConfig('cjs')];
|
||||
if (dts) {
|
||||
configs.push(buildConfig('dts'));
|
||||
}
|
||||
return configs;
|
||||
}
|
||||
|
||||
function buildConfig(format) {
|
||||
const isDts = format === 'dts';
|
||||
const output = {
|
||||
file: resolvePath(format, isDts),
|
||||
format: isDts ? 'es' : format,
|
||||
name: pkg.name,
|
||||
exports: 'default',
|
||||
indent: false,
|
||||
sourcemap: isDts ? false : sourceMap,
|
||||
};
|
||||
|
||||
return {
|
||||
input: inputPath,
|
||||
output,
|
||||
plugins: [
|
||||
isDts
|
||||
? dtsPlugin({
|
||||
tsconfig: path.resolve(__dirname, 'tsconfig.build.json'),
|
||||
})
|
||||
: esbuild({
|
||||
tsconfig: path.resolve(__dirname, 'tsconfig.build.json'),
|
||||
sourceMap: output.sourcemap,
|
||||
minify: true,
|
||||
}),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function resolvePath(format, isDts) {
|
||||
return path.resolve(
|
||||
outputPath,
|
||||
`${pkg.name}${isDts ? '.d.ts' : `.${format}.js`}`,
|
||||
);
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import minimist from 'minimist';
|
||||
import consola from 'consola';
|
||||
import { exec } from './utils';
|
||||
|
||||
const args = minimist(process.argv.slice(2));
|
||||
const watching = Boolean(args.watch || args.w);
|
||||
const release = Boolean(args.release || args.r);
|
||||
const sourceMap = release || Boolean(args.sourceMap || args.s);
|
||||
const dts = release || Boolean(args.dts || args.d);
|
||||
|
||||
build();
|
||||
|
||||
function build() {
|
||||
exec('rimraf dist');
|
||||
|
||||
consola.info('构建产物');
|
||||
exec(
|
||||
`rollup -c ${
|
||||
watching ? '-w' : ''
|
||||
} --environment SOURCE_MAP:${sourceMap},DTS:${dts}`,
|
||||
);
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
import fs from 'node:fs';
|
||||
import semver from 'semver';
|
||||
import enquirer from 'enquirer';
|
||||
import consola from 'consola';
|
||||
import { exec, pkgPath, getPkgJSON } from './utils';
|
||||
|
||||
const pkg = getPkgJSON();
|
||||
const { version: currentVersion } = pkg;
|
||||
|
||||
main().catch((err) => {
|
||||
updateVersion(currentVersion);
|
||||
console.error(err);
|
||||
});
|
||||
|
||||
async function main() {
|
||||
checkBranch();
|
||||
|
||||
const version = await inputVersion();
|
||||
|
||||
consola.info(`Update version ${currentVersion} -> ${version}`);
|
||||
updateVersion(version);
|
||||
|
||||
consola.info('Git add');
|
||||
exec('git add .');
|
||||
|
||||
consola.info('Git push');
|
||||
exec(`git commit -m "chore: release v${version}"`);
|
||||
exec('git push');
|
||||
|
||||
consola.info('Git push tag');
|
||||
exec(`git tag -a v${version} -m "v${version}"`);
|
||||
exec(`git push origin v${version}`);
|
||||
}
|
||||
|
||||
function checkBranch() {
|
||||
const releaseBranch = 'main';
|
||||
const currentBranch = exec('git branch --show-current', {
|
||||
stdio: 'pipe',
|
||||
}).toString();
|
||||
|
||||
if (currentBranch !== releaseBranch) {
|
||||
consola.warn(`请切回 ${releaseBranch} 分支进行发版!`);
|
||||
process.exit();
|
||||
}
|
||||
}
|
||||
|
||||
async function inputVersion() {
|
||||
const releases = createReleases();
|
||||
let targetVersion: string;
|
||||
|
||||
const { release } = await enquirer.prompt<{ release: string }>({
|
||||
type: 'select',
|
||||
name: 'release',
|
||||
message: '请选择版本类型',
|
||||
choices: [...releases, 'custom'],
|
||||
});
|
||||
|
||||
if (release === 'custom') {
|
||||
const { version } = await enquirer.prompt<{
|
||||
version: string;
|
||||
}>({
|
||||
type: 'input',
|
||||
name: 'version',
|
||||
message: '请输入自定义版本号',
|
||||
initial: currentVersion,
|
||||
});
|
||||
targetVersion = version;
|
||||
} else {
|
||||
targetVersion = (release.match(/\((.+)\)/) as string[])[1];
|
||||
}
|
||||
|
||||
if (
|
||||
!semver.valid(targetVersion) ||
|
||||
!semver.lt(currentVersion, targetVersion)
|
||||
) {
|
||||
consola.error(`无效的版本号: ${targetVersion}`);
|
||||
process.exit();
|
||||
}
|
||||
|
||||
const { yes: confirmRelease } = await enquirer.prompt<{ yes: boolean }>({
|
||||
type: 'confirm',
|
||||
name: 'yes',
|
||||
message: `确定发布 v${targetVersion} ?`,
|
||||
});
|
||||
|
||||
if (!confirmRelease) {
|
||||
consola.error(`取消发布: v${targetVersion}`);
|
||||
process.exit();
|
||||
}
|
||||
|
||||
return targetVersion;
|
||||
}
|
||||
|
||||
function createReleases() {
|
||||
const types = [
|
||||
['patch'],
|
||||
['minor'],
|
||||
['major'],
|
||||
['prepatch', 'alpha'],
|
||||
['preminor', 'alpha'],
|
||||
['premajor', 'alpha'],
|
||||
['prerelease', 'alpha'],
|
||||
];
|
||||
const releases: string[] = [];
|
||||
for (const [type, preid] of types) {
|
||||
releases.push(`${type} (${semver.inc(currentVersion, type, preid)})`);
|
||||
}
|
||||
return releases;
|
||||
}
|
||||
|
||||
function updateVersion(version: string) {
|
||||
pkg.version = version;
|
||||
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import path from 'node:path';
|
||||
import { createRequire } from 'node:module';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { execSync } from 'node:child_process';
|
||||
|
||||
export const __dirname = fileURLToPath(new URL('../', import.meta.url));
|
||||
export const require = createRequire(import.meta.url);
|
||||
export const exec = (command, options) =>
|
||||
execSync(command, { stdio: 'inherit', ...(options ?? {}) });
|
||||
export const pkgPath = path.resolve(__dirname, 'package.json');
|
||||
export const getPkgJSON = () => require(pkgPath);
|
|
@ -0,0 +1,37 @@
|
|||
// import fs from 'node:fs';
|
||||
// import path from 'node:path';
|
||||
// import archiver from 'archiver';
|
||||
// import { getPkgJSON } from './utils';
|
||||
|
||||
// const pkg = getPkgJSON();
|
||||
|
||||
// const zip = archiver.create('zip', {});
|
||||
// const distPath = path.resolve(__dirname, '..', 'dist');
|
||||
// const distZipName = `download/${pkg.version}.zip`;
|
||||
// const distZipPath = path.resolve(__dirname, '..', distZipName);
|
||||
|
||||
// function main() {
|
||||
// const outputStream = fs.createWriteStream(distZipPath);
|
||||
// const startTime = +new Date();
|
||||
|
||||
// zip.glob()
|
||||
// zip.on('error', (error) => {
|
||||
// throw error;
|
||||
// });
|
||||
// zip.on('finish', () => {
|
||||
// const endTime = +new Date();
|
||||
// const ss = ((endTime - startTime) / 1000).toFixed(1);
|
||||
|
||||
// });
|
||||
// zip.pipe(outputStream);
|
||||
// zip.directory(distPath, pkg.name);
|
||||
// zip.finalize();
|
||||
// }
|
||||
|
||||
// function getFilePaths(){
|
||||
|
||||
// }
|
||||
|
||||
// function createZipName(){
|
||||
// return
|
||||
// }
|
Binary file not shown.
|
@ -0,0 +1,59 @@
|
|||
import { AxiosAdapter, createAdapter, AxiosPlatform } from './core/adapter';
|
||||
import Axios, {
|
||||
AxiosConstructor,
|
||||
AxiosRequestConfig,
|
||||
AxiosResponse,
|
||||
} from './core/Axios';
|
||||
import { CancelToken, CancelTokenConstructor, isCancel } from './core/cancel';
|
||||
import { mergeConfig } from './core/mergeConfig';
|
||||
import { isString } from './helpers/is';
|
||||
import defaults from './defaults';
|
||||
|
||||
export interface AxiosInstance extends Axios {
|
||||
<TData = unknown>(config: AxiosRequestConfig): Promise<AxiosResponse<TData>>;
|
||||
<TData = unknown>(url: string, config?: AxiosRequestConfig): Promise<
|
||||
AxiosResponse<TData>
|
||||
>;
|
||||
}
|
||||
|
||||
export interface AxiosStatic extends AxiosInstance {
|
||||
Axios: AxiosConstructor;
|
||||
CancelToken: CancelTokenConstructor;
|
||||
create(defaults?: AxiosRequestConfig): AxiosInstance;
|
||||
createAdapter(platform: AxiosPlatform): AxiosAdapter;
|
||||
isCancel(value: unknown): boolean;
|
||||
}
|
||||
|
||||
function createInstance(defaults: AxiosRequestConfig): AxiosInstance {
|
||||
const instance = new Axios(defaults);
|
||||
|
||||
function axios<TData = unknown>(
|
||||
url: AxiosRequestConfig | string,
|
||||
config?: AxiosRequestConfig,
|
||||
): Promise<AxiosResponse<TData>> {
|
||||
if (isString(url)) {
|
||||
config = Object.assign({}, config, { url });
|
||||
} else {
|
||||
config = url;
|
||||
}
|
||||
|
||||
return instance.request(config);
|
||||
}
|
||||
|
||||
Object.assign(axios, instance);
|
||||
Object.setPrototypeOf(axios, Object.getPrototypeOf(instance));
|
||||
|
||||
return axios as AxiosInstance;
|
||||
}
|
||||
|
||||
const axios = createInstance(defaults) as AxiosStatic;
|
||||
|
||||
axios.Axios = Axios;
|
||||
axios.CancelToken = CancelToken;
|
||||
axios.create = function create(defaults: AxiosRequestConfig): AxiosInstance {
|
||||
return createInstance(mergeConfig(axios.defaults, defaults));
|
||||
};
|
||||
axios.createAdapter = createAdapter;
|
||||
axios.isCancel = isCancel;
|
||||
|
||||
export default axios;
|
|
@ -0,0 +1,256 @@
|
|||
import { buildURL } from '../helpers/url';
|
||||
import { mergeConfig } from './mergeConfig';
|
||||
import {
|
||||
AxiosAdapterRequestMethod,
|
||||
AxiosAdapter,
|
||||
AxiosAdapterTask,
|
||||
} from './adapter';
|
||||
import { CancelToken } from './cancel';
|
||||
import dispatchRequest from './dispatchRequest';
|
||||
import InterceptorManager from './InterceptorManager';
|
||||
import { AxiosTransformer } from './transformData';
|
||||
|
||||
export type AxiosRequestMethodAlias =
|
||||
| 'options'
|
||||
| 'get'
|
||||
| 'head'
|
||||
| 'post'
|
||||
| 'put'
|
||||
| 'delete'
|
||||
| 'trace'
|
||||
| 'connect';
|
||||
|
||||
export type AxiosRequestMethod =
|
||||
| AxiosAdapterRequestMethod
|
||||
| AxiosRequestMethodAlias;
|
||||
|
||||
export type AxiosRequestHeaders = AnyObject;
|
||||
|
||||
export type AxiosRequestParams = AnyObject;
|
||||
|
||||
export type AxiosRequestData = AnyObject;
|
||||
|
||||
export type AxiosResponseHeaders = AnyObject;
|
||||
|
||||
export interface AxiosRequestFormData extends AxiosRequestData {
|
||||
fileName: string;
|
||||
filePath: string;
|
||||
fileType?: 'image' | 'video' | 'audio';
|
||||
hideLoading?: boolean;
|
||||
}
|
||||
|
||||
export interface AxiosProgressEvent {
|
||||
progress: number;
|
||||
totalBytesSent: number;
|
||||
totalBytesExpectedToSend: number;
|
||||
}
|
||||
|
||||
export interface AxiosProgressCallback {
|
||||
(event: AxiosProgressEvent): void;
|
||||
}
|
||||
|
||||
export interface AxiosRequestConfig {
|
||||
adapter?: AxiosAdapter;
|
||||
baseURL?: string;
|
||||
cancelToken?: CancelToken;
|
||||
data?: AxiosRequestData | AxiosRequestFormData | AxiosRequestFormData;
|
||||
dataType?: 'json' | '其他';
|
||||
download?: boolean;
|
||||
enableHttp2?: boolean;
|
||||
enableQuic?: boolean;
|
||||
enableCache?: boolean;
|
||||
errorHandler?: (error: unknown) => Promise<unknown>;
|
||||
headers?: AxiosRequestHeaders;
|
||||
method?: AxiosRequestMethod;
|
||||
onUploadProgress?: AxiosProgressCallback;
|
||||
onDownloadProgress?: AxiosProgressCallback;
|
||||
params?: AxiosRequestParams;
|
||||
paramsSerializer?: (params?: AxiosRequestParams) => string;
|
||||
responseType?: 'text' | 'arraybuffer';
|
||||
sslVerify?: boolean;
|
||||
transformRequest?: AxiosTransformer | AxiosTransformer[];
|
||||
transformResponse?: AxiosTransformer | AxiosTransformer[];
|
||||
timeout?: number;
|
||||
upload?: boolean;
|
||||
url?: string;
|
||||
validateStatus?: (status: number) => boolean;
|
||||
}
|
||||
|
||||
export interface AxiosResponse<TData = unknown> {
|
||||
status: number;
|
||||
statusText: string;
|
||||
headers: AxiosResponseHeaders;
|
||||
data: TData;
|
||||
config?: AxiosRequestConfig;
|
||||
request?: AxiosAdapterTask;
|
||||
cookies?: string[];
|
||||
profile?: AnyObject;
|
||||
}
|
||||
|
||||
export interface AxiosResponseError extends AnyObject {
|
||||
status: number;
|
||||
statusText: string;
|
||||
headers: AxiosResponseHeaders;
|
||||
config?: AxiosRequestConfig;
|
||||
request?: AxiosAdapterTask;
|
||||
}
|
||||
|
||||
export interface AxiosConstructor {
|
||||
new (config: AxiosRequestConfig): Axios;
|
||||
}
|
||||
|
||||
export default class Axios {
|
||||
public defaults: AxiosRequestConfig;
|
||||
|
||||
public interceptors = {
|
||||
request: new InterceptorManager<AxiosRequestConfig>(),
|
||||
response: new InterceptorManager<AxiosResponse>(),
|
||||
};
|
||||
|
||||
public constructor(defaults: AxiosRequestConfig = {}) {
|
||||
this.defaults = defaults;
|
||||
}
|
||||
|
||||
public getUri(config: AxiosRequestConfig): string {
|
||||
const { url, params, paramsSerializer } = mergeConfig(
|
||||
this.defaults,
|
||||
config,
|
||||
);
|
||||
|
||||
return buildURL(url, params, paramsSerializer).replace(/^\?/, '');
|
||||
}
|
||||
|
||||
public request<TData = unknown>(
|
||||
config: AxiosRequestConfig,
|
||||
): Promise<AxiosResponse<TData>> {
|
||||
const requestConfig = mergeConfig(this.defaults, config);
|
||||
|
||||
let promiseRequest = Promise.resolve(requestConfig);
|
||||
|
||||
this.interceptors.request.forEach(({ resolved, rejected }) => {
|
||||
promiseRequest = promiseRequest.then(
|
||||
resolved,
|
||||
rejected,
|
||||
) as Promise<AxiosRequestConfig>;
|
||||
}, 'reverse');
|
||||
|
||||
let promiseResponse = promiseRequest.then(dispatchRequest);
|
||||
|
||||
this.interceptors.response.forEach(({ resolved, rejected }) => {
|
||||
promiseResponse = promiseResponse.then(resolved, rejected) as Promise<
|
||||
AxiosResponse<unknown>
|
||||
>;
|
||||
});
|
||||
|
||||
return promiseResponse as Promise<AxiosResponse<TData>>;
|
||||
}
|
||||
|
||||
public options<TData = unknown>(
|
||||
url: string,
|
||||
config?: AxiosRequestConfig,
|
||||
): Promise<AxiosResponse<TData>> {
|
||||
return this._requestMethodWithoutParams<TData>(
|
||||
'options',
|
||||
url,
|
||||
undefined,
|
||||
config,
|
||||
);
|
||||
}
|
||||
|
||||
public get<TData = unknown>(
|
||||
url: string,
|
||||
params?: AxiosRequestParams,
|
||||
config?: AxiosRequestConfig,
|
||||
): Promise<AxiosResponse<TData>> {
|
||||
return this._requestMethodWithoutParams<TData>('get', url, params, config);
|
||||
}
|
||||
|
||||
public head<TData = unknown>(
|
||||
url: string,
|
||||
params?: AxiosRequestParams,
|
||||
config?: AxiosRequestConfig,
|
||||
): Promise<AxiosResponse<TData>> {
|
||||
return this._requestMethodWithoutParams<TData>('head', url, params, config);
|
||||
}
|
||||
|
||||
public post<TData = unknown>(
|
||||
url: string,
|
||||
data?: AxiosRequestData | AxiosRequestFormData,
|
||||
config?: AxiosRequestConfig,
|
||||
): Promise<AxiosResponse<TData>> {
|
||||
return this._requestMethodWithoutData<TData>('post', url, data, config);
|
||||
}
|
||||
|
||||
public put<TData = unknown>(
|
||||
url: string,
|
||||
data?: AxiosRequestData | AxiosRequestFormData,
|
||||
config?: AxiosRequestConfig,
|
||||
): Promise<AxiosResponse<TData>> {
|
||||
return this._requestMethodWithoutData<TData>('put', url, data, config);
|
||||
}
|
||||
|
||||
public delete<TData = unknown>(
|
||||
url: string,
|
||||
params?: AxiosRequestParams,
|
||||
config?: AxiosRequestConfig,
|
||||
): Promise<AxiosResponse<TData>> {
|
||||
return this._requestMethodWithoutParams<TData>(
|
||||
'delete',
|
||||
url,
|
||||
params,
|
||||
config,
|
||||
);
|
||||
}
|
||||
|
||||
public trace<TData = unknown>(
|
||||
url: string,
|
||||
config?: AxiosRequestConfig,
|
||||
): Promise<AxiosResponse<TData>> {
|
||||
return this._requestMethodWithoutParams<TData>(
|
||||
'trace',
|
||||
url,
|
||||
undefined,
|
||||
config,
|
||||
);
|
||||
}
|
||||
|
||||
public connect<TData = unknown>(
|
||||
url: string,
|
||||
config?: AxiosRequestConfig,
|
||||
): Promise<AxiosResponse<TData>> {
|
||||
return this._requestMethodWithoutParams<TData>(
|
||||
'connect',
|
||||
url,
|
||||
undefined,
|
||||
config,
|
||||
);
|
||||
}
|
||||
|
||||
private _requestMethodWithoutParams<TData = unknown>(
|
||||
method: AxiosRequestMethod,
|
||||
url: string,
|
||||
params?: AxiosRequestParams,
|
||||
config?: AxiosRequestConfig,
|
||||
): Promise<AxiosResponse<TData>> {
|
||||
return this.request<TData>({
|
||||
...(config ?? {}),
|
||||
method,
|
||||
url,
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
private _requestMethodWithoutData<TData = unknown>(
|
||||
method: AxiosRequestMethod,
|
||||
url: string,
|
||||
data?: AxiosRequestData | AxiosRequestFormData,
|
||||
config?: AxiosRequestConfig,
|
||||
): Promise<AxiosResponse<TData>> {
|
||||
return this.request<TData>({
|
||||
...(config ?? {}),
|
||||
method,
|
||||
url,
|
||||
data,
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
export interface InterceptorResolved<T = unknown> {
|
||||
(value: T): T | Promise<T>;
|
||||
}
|
||||
|
||||
export interface InterceptorRejected {
|
||||
(error: unknown): unknown | Promise<unknown>;
|
||||
}
|
||||
|
||||
export interface Interceptor<T = unknown> {
|
||||
resolved: InterceptorResolved<T>;
|
||||
rejected?: InterceptorRejected;
|
||||
}
|
||||
|
||||
export interface InterceptorExecutor {
|
||||
(interceptor: Interceptor): void;
|
||||
}
|
||||
|
||||
export default class InterceptorManager<T = unknown> {
|
||||
private id = 0;
|
||||
|
||||
private interceptors: AnyObject<Interceptor<T>> = {};
|
||||
|
||||
public use(
|
||||
resolved: InterceptorResolved<T>,
|
||||
rejected?: InterceptorRejected,
|
||||
): number {
|
||||
this.interceptors[++this.id] = {
|
||||
resolved,
|
||||
rejected,
|
||||
};
|
||||
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public eject(id: number): void {
|
||||
delete this.interceptors[id];
|
||||
}
|
||||
|
||||
public forEach(executor: InterceptorExecutor, reverse?: 'reverse'): void {
|
||||
let interceptors: Interceptor<any>[] = Object.keys(this.interceptors).map(
|
||||
(id) => this.interceptors[id],
|
||||
);
|
||||
|
||||
if (reverse === 'reverse') {
|
||||
interceptors = interceptors.reverse();
|
||||
}
|
||||
|
||||
interceptors.forEach(executor);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,292 @@
|
|||
import {
|
||||
isEmptyArray,
|
||||
isFunction,
|
||||
isPlainObject,
|
||||
isString,
|
||||
isUndefined,
|
||||
} from '../helpers/is';
|
||||
import { assert, throwError } from '../helpers/utils';
|
||||
import {
|
||||
AxiosProgressCallback,
|
||||
AxiosRequestConfig,
|
||||
AxiosRequestData,
|
||||
AxiosRequestFormData,
|
||||
AxiosRequestHeaders,
|
||||
AxiosResponse,
|
||||
AxiosResponseError,
|
||||
} from './Axios';
|
||||
|
||||
export type AxiosAdapterRequestType = 'request' | 'download' | 'upload';
|
||||
|
||||
export type AxiosAdapterRequestMethod =
|
||||
| 'OPTIONS'
|
||||
| 'GET'
|
||||
| 'HEAD'
|
||||
| 'POST'
|
||||
| 'PUT'
|
||||
| 'DELETE'
|
||||
| 'TRACE'
|
||||
| 'CONNECT';
|
||||
|
||||
export interface AxiosAdapterRequestConfig extends AxiosRequestConfig {
|
||||
type: AxiosAdapterRequestType;
|
||||
method: AxiosAdapterRequestMethod;
|
||||
url: string;
|
||||
success(response: AxiosResponse): void;
|
||||
fail(error: AxiosResponseError): void;
|
||||
}
|
||||
|
||||
export interface AxiosAdapterBaseOptions extends AxiosAdapterRequestConfig {
|
||||
header?: AxiosRequestHeaders;
|
||||
success(response: unknown): void;
|
||||
fail(error: unknown): void;
|
||||
}
|
||||
|
||||
export interface AxiosAdapterUploadOptions extends AxiosAdapterBaseOptions {
|
||||
filePath: string;
|
||||
name: string;
|
||||
fileName: string;
|
||||
fileType: 'image' | 'video' | 'audio';
|
||||
hideLoading?: boolean;
|
||||
formData?: AxiosRequestData;
|
||||
}
|
||||
|
||||
export interface AxiosAdapterDownloadOptions extends AxiosAdapterBaseOptions {
|
||||
filePath?: string;
|
||||
fileName?: string;
|
||||
}
|
||||
|
||||
export interface AxiosAdapterRequest {
|
||||
(config: AxiosAdapterBaseOptions): AxiosAdapterTask | void;
|
||||
}
|
||||
|
||||
export interface AxiosAdapterUpload {
|
||||
(config: AxiosAdapterUploadOptions): AxiosAdapterTask | void;
|
||||
}
|
||||
|
||||
export interface AxiosAdapterDownload {
|
||||
(config: AxiosAdapterDownloadOptions): AxiosAdapterTask | void;
|
||||
}
|
||||
|
||||
export interface AxiosPlatform {
|
||||
request: AxiosAdapterRequest;
|
||||
upload: AxiosAdapterUpload;
|
||||
download: AxiosAdapterDownload;
|
||||
}
|
||||
|
||||
export interface AxiosAdapterTask {
|
||||
abort?(): void;
|
||||
onProgressUpdate?(callback: AxiosProgressCallback): void;
|
||||
offProgressUpdate?(callback: AxiosProgressCallback): void;
|
||||
}
|
||||
|
||||
export interface AxiosAdapter {
|
||||
(config: AxiosAdapterRequestConfig): AxiosAdapterTask | void;
|
||||
}
|
||||
|
||||
export function getAdapterDefault(): AxiosAdapter | undefined {
|
||||
const tryGetPlatforms = [
|
||||
() => uni,
|
||||
() => wx,
|
||||
() => my,
|
||||
() => swan,
|
||||
() => tt,
|
||||
() => qq,
|
||||
() => qh,
|
||||
() => ks,
|
||||
() => dd,
|
||||
];
|
||||
|
||||
let platform;
|
||||
while (!isEmptyArray(tryGetPlatforms) && !isPlatform(platform)) {
|
||||
try {
|
||||
const tryGetPlatform = tryGetPlatforms.shift();
|
||||
|
||||
if (isPlainObject((platform = tryGetPlatform?.()))) {
|
||||
platform = revisePlatformApiNames(platform);
|
||||
}
|
||||
} catch (err) {
|
||||
// 避免出现异常导致程序被终止
|
||||
}
|
||||
}
|
||||
|
||||
if (!isPlatform(platform)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return createAdapter(platform);
|
||||
}
|
||||
|
||||
export function createAdapter(platform: AxiosPlatform): AxiosAdapter {
|
||||
assert(isPlainObject(platform), 'platform 需要是一个 object');
|
||||
assert(isFunction(platform.request), 'platform.request 需要是一个 function');
|
||||
assert(isFunction(platform.upload), 'platform.upload 需要是一个 function');
|
||||
assert(
|
||||
isFunction(platform.download),
|
||||
'platform.download 需要是一个 function',
|
||||
);
|
||||
|
||||
function adapterDefault(
|
||||
config: AxiosAdapterRequestConfig,
|
||||
): AxiosAdapterTask | void {
|
||||
const baseOptions = transformOptions(config);
|
||||
|
||||
switch (config.type) {
|
||||
case 'request':
|
||||
return callRequest(platform.request, baseOptions);
|
||||
case 'upload':
|
||||
return callUpload(platform.upload, baseOptions);
|
||||
case 'download':
|
||||
return callDownload(platform.download, baseOptions);
|
||||
default:
|
||||
throwError(`无法识别的请求类型 ${config.type}`);
|
||||
}
|
||||
}
|
||||
|
||||
function callRequest(
|
||||
request: AxiosAdapterRequest,
|
||||
baseOptions: AxiosAdapterBaseOptions,
|
||||
): AxiosAdapterTask | void {
|
||||
return request(baseOptions);
|
||||
}
|
||||
|
||||
function callUpload(
|
||||
upload: AxiosAdapterUpload,
|
||||
baseOptions: AxiosAdapterBaseOptions,
|
||||
): AxiosAdapterTask | void {
|
||||
assert(
|
||||
isPlainObject(baseOptions.data),
|
||||
'上传文件时 data 需要是一个 object',
|
||||
);
|
||||
assert(
|
||||
isString(baseOptions.data?.fileName),
|
||||
'上传文件时 data.fileName 需要是一个 string',
|
||||
);
|
||||
assert(
|
||||
isString(baseOptions.data?.filePath),
|
||||
'上传文件时 data.filePath 需要是一个 string',
|
||||
);
|
||||
|
||||
const { fileName, filePath, fileType, hideLoading, ...formData } =
|
||||
baseOptions.data as AxiosRequestFormData;
|
||||
const options = {
|
||||
...baseOptions,
|
||||
name: fileName,
|
||||
fileName: fileName,
|
||||
filePath,
|
||||
fileType: fileType ?? 'image',
|
||||
hideLoading,
|
||||
formData,
|
||||
};
|
||||
|
||||
return upload(options);
|
||||
}
|
||||
|
||||
function callDownload(
|
||||
download: AxiosAdapterDownload,
|
||||
baseOptions: AxiosAdapterBaseOptions,
|
||||
): AxiosAdapterTask | void {
|
||||
const options = {
|
||||
...baseOptions,
|
||||
filePath: baseOptions.params?.filePath,
|
||||
fileName: baseOptions.params?.fileName,
|
||||
success(response: AnyObject): void {
|
||||
injectDownloadData(response);
|
||||
baseOptions.success(response);
|
||||
},
|
||||
};
|
||||
|
||||
return download(options);
|
||||
}
|
||||
|
||||
function transformResult(result: AnyObject): void {
|
||||
if (!isUndefined(result.statusCode)) {
|
||||
result.status = result.statusCode;
|
||||
delete result.statusCode;
|
||||
}
|
||||
|
||||
if (isUndefined(result.status)) {
|
||||
result.status = isUndefined(result.data) ? 400 : 200;
|
||||
}
|
||||
|
||||
if (!isUndefined(result.header)) {
|
||||
result.headers = result.header;
|
||||
delete result.header;
|
||||
}
|
||||
|
||||
if (isUndefined(result.headers)) {
|
||||
result.headers = {};
|
||||
}
|
||||
|
||||
if (!isUndefined(result.errMsg)) {
|
||||
result.statusText = result.errMsg;
|
||||
delete result.errMsg;
|
||||
}
|
||||
|
||||
if (isUndefined(result.statusText)) {
|
||||
result.statusText =
|
||||
result.status === 200
|
||||
? 'OK'
|
||||
: result.status === 400
|
||||
? 'Bad Adapter'
|
||||
: '';
|
||||
}
|
||||
}
|
||||
|
||||
function transformOptions(
|
||||
config: AxiosAdapterRequestConfig,
|
||||
): AxiosAdapterBaseOptions {
|
||||
return {
|
||||
...config,
|
||||
header: config.headers,
|
||||
success(response: AxiosResponse<unknown>): void {
|
||||
transformResult(response);
|
||||
config.success(response);
|
||||
},
|
||||
fail(error: AxiosResponseError): void {
|
||||
transformResult(error);
|
||||
config.fail(error);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function injectDownloadData(response: AnyObject): void {
|
||||
if (!isPlainObject(response.data)) {
|
||||
response.data = {};
|
||||
}
|
||||
|
||||
if (!isUndefined(response.tempFilePath)) {
|
||||
response.data.tempFilePath = response.tempFilePath;
|
||||
delete response.tempFilePath;
|
||||
}
|
||||
|
||||
if (!isUndefined(response.apFilePath)) {
|
||||
response.data.tempFilePath = response.apFilePath;
|
||||
delete response.apFilePath;
|
||||
}
|
||||
|
||||
if (!isUndefined(response.filePath)) {
|
||||
response.data.filePath = response.filePath;
|
||||
delete response.filePath;
|
||||
}
|
||||
}
|
||||
|
||||
return adapterDefault;
|
||||
}
|
||||
|
||||
export function isPlatform(value: unknown): value is AxiosPlatform {
|
||||
return (
|
||||
isPlainObject(value) &&
|
||||
isFunction(value.request) &&
|
||||
isFunction(value.upload) &&
|
||||
isFunction(value.download)
|
||||
);
|
||||
}
|
||||
|
||||
export function revisePlatformApiNames(platform: AnyObject): AxiosPlatform {
|
||||
return {
|
||||
request: platform.request ?? platform.httpRequest,
|
||||
upload: platform.upload ?? platform.uploadFile,
|
||||
download: platform.download ?? platform.downloadFile,
|
||||
};
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
export interface CancelAction {
|
||||
(message?: string): void;
|
||||
}
|
||||
|
||||
export interface CancelExecutor {
|
||||
(cancel: CancelAction): void;
|
||||
}
|
||||
|
||||
export interface CancelTokenSource {
|
||||
token: CancelToken;
|
||||
cancel: CancelAction;
|
||||
}
|
||||
|
||||
export interface CancelTokenConstructor {
|
||||
new (executor: CancelExecutor): CancelToken;
|
||||
source(): CancelTokenSource;
|
||||
}
|
||||
|
||||
export class Cancel {
|
||||
public message?: string;
|
||||
|
||||
public constructor(message?: string) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
const message = this.message ? `: ${this.message}` : '';
|
||||
|
||||
return `Cancel${message}`;
|
||||
}
|
||||
}
|
||||
|
||||
export function isCancel(value: unknown): value is Cancel {
|
||||
return value instanceof Cancel;
|
||||
}
|
||||
|
||||
export class CancelToken {
|
||||
private reason?: Cancel;
|
||||
|
||||
public listener: Promise<Cancel>;
|
||||
|
||||
public constructor(executor: CancelExecutor) {
|
||||
let action!: CancelAction;
|
||||
this.listener = new Promise<Cancel>((resolve) => {
|
||||
action = (message) => {
|
||||
if (this.reason) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.reason = new Cancel(message);
|
||||
|
||||
resolve(this.reason);
|
||||
};
|
||||
});
|
||||
|
||||
executor(action);
|
||||
}
|
||||
|
||||
public static source(): CancelTokenSource {
|
||||
let cancel!: CancelAction;
|
||||
const token = new CancelToken((action) => {
|
||||
cancel = action;
|
||||
});
|
||||
|
||||
return {
|
||||
token,
|
||||
cancel,
|
||||
};
|
||||
}
|
||||
|
||||
public throwIfRequested(): void {
|
||||
if (this.reason) {
|
||||
throw this.reason;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function isCancelToken(value: unknown): value is CancelToken {
|
||||
return value instanceof CancelToken;
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
import { AxiosAdapterTask } from './adapter';
|
||||
import { AxiosRequestConfig, AxiosResponse, AxiosResponseError } from './Axios';
|
||||
|
||||
export type AxiosErrorResponse = AxiosResponse | AxiosResponseError;
|
||||
|
||||
class AxiosError extends Error {
|
||||
public isAxiosError = true;
|
||||
|
||||
public config: AxiosRequestConfig;
|
||||
|
||||
public request?: AxiosAdapterTask;
|
||||
|
||||
public response?: AxiosErrorResponse;
|
||||
|
||||
public constructor(
|
||||
message: string,
|
||||
config: AxiosRequestConfig,
|
||||
request?: AxiosAdapterTask,
|
||||
response?: AxiosErrorResponse,
|
||||
) {
|
||||
super(message);
|
||||
|
||||
this.config = config;
|
||||
this.request = request;
|
||||
this.response = response;
|
||||
|
||||
Object.setPrototypeOf(this, AxiosError.prototype);
|
||||
}
|
||||
}
|
||||
|
||||
export function createError(
|
||||
message: string,
|
||||
config: AxiosRequestConfig,
|
||||
request?: AxiosAdapterTask,
|
||||
response?: AxiosErrorResponse,
|
||||
): AxiosError {
|
||||
return new AxiosError(message, config, request, response);
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
import { isPlainObject } from '../helpers/is';
|
||||
import { isCancel } from './cancel';
|
||||
import { flattenHeaders } from './flattenHeaders';
|
||||
import { transformData } from './transformData';
|
||||
import { request } from './request';
|
||||
import { AxiosRequestConfig, AxiosResponse } from './Axios';
|
||||
import { transformURL } from './transformURL';
|
||||
|
||||
function throwIfCancellationRequested(config: AxiosRequestConfig) {
|
||||
if (config.cancelToken) {
|
||||
config.cancelToken.throwIfRequested();
|
||||
}
|
||||
}
|
||||
|
||||
export default function dispatchRequest<TData = unknown>(
|
||||
config: AxiosRequestConfig,
|
||||
): Promise<AxiosResponse> {
|
||||
throwIfCancellationRequested(config);
|
||||
|
||||
config.url = transformURL(config);
|
||||
config.headers = flattenHeaders(config);
|
||||
config.data = transformData(
|
||||
config.data,
|
||||
config.headers,
|
||||
config.transformRequest,
|
||||
);
|
||||
|
||||
return request<TData>(config).then(
|
||||
(response: AxiosResponse<TData>) => {
|
||||
throwIfCancellationRequested(config);
|
||||
|
||||
response.data = transformData(
|
||||
response.data as AnyObject,
|
||||
response.headers,
|
||||
config.transformResponse,
|
||||
) as TData;
|
||||
|
||||
return response;
|
||||
},
|
||||
(reason: unknown) => {
|
||||
if (!isCancel(reason)) {
|
||||
throwIfCancellationRequested(config);
|
||||
|
||||
if (isPlainObject(reason) && isPlainObject(reason.response)) {
|
||||
reason.response.data = transformData(
|
||||
reason.response.data,
|
||||
reason.response.headers,
|
||||
config.transformResponse,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
throw config.errorHandler?.(reason) ?? reason;
|
||||
},
|
||||
);
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
import { isPlainObject } from '../helpers/is';
|
||||
import { omit, toLowerCase } from '../helpers/utils';
|
||||
import {
|
||||
AxiosRequestConfig,
|
||||
AxiosRequestMethodAlias,
|
||||
AxiosRequestHeaders,
|
||||
} from './Axios';
|
||||
|
||||
export function flattenHeaders(
|
||||
config: AxiosRequestConfig,
|
||||
): AxiosRequestHeaders | undefined {
|
||||
if (!isPlainObject(config.headers)) {
|
||||
return config.headers;
|
||||
}
|
||||
|
||||
return {
|
||||
...(config.headers.common ?? {}),
|
||||
...(config.headers[
|
||||
toLowerCase<AxiosRequestMethodAlias>(config.method, 'get')
|
||||
] ?? {}),
|
||||
...omit(
|
||||
config.headers,
|
||||
'common',
|
||||
'options',
|
||||
'get',
|
||||
'head',
|
||||
'post',
|
||||
'put',
|
||||
'delete',
|
||||
'trace',
|
||||
'connect',
|
||||
),
|
||||
};
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
import { toLowerCase } from '../helpers/utils';
|
||||
import { AxiosAdapterRequestType } from './adapter';
|
||||
import { AxiosRequestConfig, AxiosRequestMethodAlias } from './Axios';
|
||||
|
||||
export function generateType(
|
||||
config: AxiosRequestConfig,
|
||||
): AxiosAdapterRequestType {
|
||||
let requestType: AxiosAdapterRequestType = 'request';
|
||||
const method = toLowerCase<AxiosRequestMethodAlias>(config.method, 'get');
|
||||
|
||||
if (config.upload && method === 'post') {
|
||||
requestType = 'upload';
|
||||
}
|
||||
if (config.download && method === 'get') {
|
||||
requestType = 'download';
|
||||
}
|
||||
|
||||
return requestType;
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
import { isUndefined, isPlainObject } from '../helpers/is';
|
||||
import { deepMerge } from '../helpers/utils';
|
||||
import { AxiosRequestConfig } from './Axios';
|
||||
|
||||
type AxiosRequestConfigKey = keyof AxiosRequestConfig;
|
||||
|
||||
const onlyFromConfig2Keys: AxiosRequestConfigKey[] = [
|
||||
'url',
|
||||
'method',
|
||||
'data',
|
||||
'upload',
|
||||
'download',
|
||||
];
|
||||
const priorityFromConfig2Keys: AxiosRequestConfigKey[] = [
|
||||
'adapter',
|
||||
'baseURL',
|
||||
'paramsSerializer',
|
||||
'transformRequest',
|
||||
'transformResponse',
|
||||
'errorHandler',
|
||||
'cancelToken',
|
||||
'dataType',
|
||||
'responseType',
|
||||
'timeout',
|
||||
'enableHttp2',
|
||||
'enableQuic',
|
||||
'enableCache',
|
||||
'sslVerify',
|
||||
'validateStatus',
|
||||
'onUploadProgress',
|
||||
'onDownloadProgress',
|
||||
];
|
||||
const deepMergeConfigKeys: AxiosRequestConfigKey[] = ['headers', 'params'];
|
||||
|
||||
export function mergeConfig(
|
||||
config1: AxiosRequestConfig = {},
|
||||
config2: AxiosRequestConfig = {},
|
||||
): AxiosRequestConfig {
|
||||
const config: AxiosRequestConfig = {};
|
||||
|
||||
for (const key of onlyFromConfig2Keys) {
|
||||
const value = config2[key];
|
||||
|
||||
if (!isUndefined(value)) {
|
||||
config[key] = value as any;
|
||||
}
|
||||
}
|
||||
|
||||
for (const key of priorityFromConfig2Keys) {
|
||||
const value1 = config1[key];
|
||||
const value2 = config2[key];
|
||||
|
||||
if (!isUndefined(value2)) {
|
||||
config[key] = value2 as any;
|
||||
} else if (!isUndefined(value1)) {
|
||||
config[key] = value1 as any;
|
||||
}
|
||||
}
|
||||
|
||||
for (const key of deepMergeConfigKeys) {
|
||||
const value1 = config1[key];
|
||||
const value2 = config2[key];
|
||||
|
||||
if (isPlainObject(value2)) {
|
||||
config[key] = deepMerge(value1 ?? {}, value2) as any;
|
||||
} else if (isPlainObject(value1)) {
|
||||
config[key] = deepMerge(value1) as any;
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
import { isFunction, isPlainObject } from '../helpers/is';
|
||||
import { assert, toUpperCase } from '../helpers/utils';
|
||||
import {
|
||||
AxiosAdapterRequestConfig,
|
||||
AxiosAdapterRequestMethod,
|
||||
AxiosAdapterTask,
|
||||
} from './adapter';
|
||||
import {
|
||||
AxiosProgressCallback,
|
||||
AxiosRequestConfig,
|
||||
AxiosResponse,
|
||||
AxiosResponseError,
|
||||
} from './Axios';
|
||||
import { isCancelToken } from './cancel';
|
||||
import { AxiosErrorResponse, createError } from './createError';
|
||||
import { generateType } from './generateType';
|
||||
|
||||
function tryToggleProgressUpdate(
|
||||
adapterConfig: AxiosAdapterRequestConfig,
|
||||
progressUpdate?: (callback: AxiosProgressCallback) => void,
|
||||
) {
|
||||
if (isFunction(progressUpdate)) {
|
||||
switch (adapterConfig.type) {
|
||||
case 'upload':
|
||||
if (isFunction(adapterConfig.onUploadProgress)) {
|
||||
progressUpdate(adapterConfig.onUploadProgress);
|
||||
}
|
||||
break;
|
||||
case 'download':
|
||||
if (isFunction(adapterConfig.onDownloadProgress)) {
|
||||
progressUpdate(adapterConfig.onDownloadProgress);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function request<TData = unknown>(
|
||||
config: AxiosRequestConfig,
|
||||
): Promise<AxiosResponse<TData>> {
|
||||
return new Promise((resolve, reject) => {
|
||||
assert(isFunction(config.adapter), 'adapter 需要是一个 function');
|
||||
|
||||
const adapterConfig: AxiosAdapterRequestConfig = {
|
||||
...config,
|
||||
url: config.url ?? '',
|
||||
type: generateType(config),
|
||||
method: toUpperCase<AxiosAdapterRequestMethod>(config.method, 'GET'),
|
||||
success,
|
||||
fail,
|
||||
};
|
||||
|
||||
const adapterTask = config.adapter?.(adapterConfig) as
|
||||
| AxiosAdapterTask
|
||||
| undefined;
|
||||
|
||||
function success(response: AxiosResponse<TData>): void {
|
||||
response.config = config;
|
||||
response.request = adapterTask;
|
||||
if (
|
||||
!isFunction(config.validateStatus) ||
|
||||
config.validateStatus(response.status)
|
||||
) {
|
||||
resolve(response);
|
||||
} else {
|
||||
catchError('请求失败', response);
|
||||
}
|
||||
}
|
||||
|
||||
function fail(error: AxiosResponseError): void {
|
||||
error.config = config;
|
||||
error.request = adapterTask;
|
||||
catchError('网络错误', error);
|
||||
}
|
||||
|
||||
function catchError(message: string, response?: AxiosErrorResponse): void {
|
||||
reject(createError(message, config, adapterTask, response));
|
||||
}
|
||||
|
||||
if (isPlainObject(adapterTask)) {
|
||||
tryToggleProgressUpdate(adapterConfig, adapterTask.onProgressUpdate);
|
||||
}
|
||||
|
||||
if (isCancelToken(config.cancelToken)) {
|
||||
config.cancelToken.listener.then((reason: unknown) => {
|
||||
if (isPlainObject(adapterTask)) {
|
||||
tryToggleProgressUpdate(adapterConfig, adapterTask.offProgressUpdate);
|
||||
|
||||
if (isFunction(adapterTask.abort)) {
|
||||
adapterTask.abort();
|
||||
}
|
||||
}
|
||||
|
||||
reject(reason);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
import { isArray, isUndefined } from '../helpers/is';
|
||||
import {
|
||||
AxiosRequestData,
|
||||
AxiosRequestFormData,
|
||||
AxiosResponseHeaders,
|
||||
} from './Axios';
|
||||
|
||||
export interface AxiosTransformer {
|
||||
(
|
||||
data?: AxiosRequestData | AxiosRequestFormData,
|
||||
headers?: AxiosResponseHeaders,
|
||||
): AxiosRequestData | AxiosRequestFormData;
|
||||
}
|
||||
|
||||
export function transformData(
|
||||
data?: AxiosRequestData | AxiosRequestFormData,
|
||||
headers?: AxiosResponseHeaders,
|
||||
transforms?: AxiosTransformer | AxiosTransformer[],
|
||||
): AxiosRequestData | AxiosRequestFormData | undefined {
|
||||
if (isUndefined(transforms)) {
|
||||
return data;
|
||||
}
|
||||
|
||||
if (!isArray(transforms)) {
|
||||
transforms = [transforms];
|
||||
}
|
||||
|
||||
transforms.forEach((transform: AxiosTransformer) => {
|
||||
data = transform(data, headers);
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
import {
|
||||
buildURL,
|
||||
combineURL,
|
||||
dynamicInterpolation,
|
||||
isAbsoluteURL,
|
||||
isDynamicURL,
|
||||
} from '../helpers/url';
|
||||
import { AxiosRequestConfig } from './Axios';
|
||||
|
||||
export function transformURL(config: AxiosRequestConfig): string {
|
||||
let url = config.url ?? '';
|
||||
|
||||
if (!isAbsoluteURL(url)) {
|
||||
url = combineURL(config.baseURL, url);
|
||||
}
|
||||
|
||||
if (isDynamicURL(url)) {
|
||||
const sourceData = Object.assign({}, config.params, config.data);
|
||||
url = dynamicInterpolation(url, sourceData);
|
||||
}
|
||||
|
||||
url = buildURL(url, config.params, config.paramsSerializer);
|
||||
|
||||
return url;
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
import { getAdapterDefault } from './core/adapter';
|
||||
import { AxiosRequestConfig } from './core/Axios';
|
||||
|
||||
const defaults: AxiosRequestConfig = {
|
||||
adapter: getAdapterDefault(),
|
||||
headers: {
|
||||
common: {
|
||||
Accept: 'application/json, test/plain, */*',
|
||||
},
|
||||
options: {},
|
||||
get: {},
|
||||
head: {},
|
||||
post: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
|
||||
},
|
||||
put: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
|
||||
},
|
||||
delete: {},
|
||||
trace: {},
|
||||
connect: {},
|
||||
},
|
||||
validateStatus(status: number): boolean {
|
||||
return status >= 200 && status < 300;
|
||||
},
|
||||
timeout: 10000,
|
||||
dataType: 'json',
|
||||
responseType: 'text',
|
||||
enableHttp2: false,
|
||||
enableQuic: false,
|
||||
enableCache: false,
|
||||
sslVerify: true,
|
||||
};
|
||||
|
||||
export default defaults;
|
|
@ -0,0 +1,37 @@
|
|||
const _toString = Object.prototype.toString;
|
||||
|
||||
export function isArray<T = unknown>(value: any): value is T[] {
|
||||
return Array.isArray(value);
|
||||
}
|
||||
|
||||
export function isDate(date: any): date is Date {
|
||||
return _toString.call(date) === '[object Date]';
|
||||
}
|
||||
|
||||
export function isEmptyArray<T = unknown>(value: any): value is [] {
|
||||
return isArray<T>(value) && value.length === 0;
|
||||
}
|
||||
|
||||
export function isEmptyObject(value: any): value is object {
|
||||
return isPlainObject(value) && Object.keys(value).length === 0;
|
||||
}
|
||||
|
||||
export function isFunction<T extends () => void>(value: any): value is T {
|
||||
return typeof value === 'function';
|
||||
}
|
||||
|
||||
export function isNull(value: any): value is null {
|
||||
return value === null;
|
||||
}
|
||||
|
||||
export function isPlainObject(value: any): value is object {
|
||||
return _toString.call(value) === '[object Object]';
|
||||
}
|
||||
|
||||
export function isString(value: any): value is string {
|
||||
return typeof value === 'string';
|
||||
}
|
||||
|
||||
export function isUndefined(value: any): value is undefined {
|
||||
return typeof value === 'undefined';
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
import { isDate, isNull, isPlainObject, isUndefined } from './is';
|
||||
|
||||
function encode(str: string): string {
|
||||
return encodeURIComponent(str)
|
||||
.replace(/%40/gi, '@')
|
||||
.replace(/%3A/gi, ':')
|
||||
.replace(/%24/g, '$')
|
||||
.replace(/%2C/gi, ',')
|
||||
.replace(/%20/g, '+')
|
||||
.replace(/%5B/gi, '[')
|
||||
.replace(/%5D/gi, ']');
|
||||
}
|
||||
|
||||
export function buildURL(
|
||||
url = '',
|
||||
params?: unknown,
|
||||
paramsSerializer = paramsSerialization,
|
||||
): string {
|
||||
if (!isPlainObject(params)) {
|
||||
return url;
|
||||
}
|
||||
|
||||
return generateURL(url, paramsSerializer(params));
|
||||
}
|
||||
|
||||
const combineREG = /([^:])\/{2,}/g;
|
||||
export function combineURL(baseURL = '', url: string): string {
|
||||
const separator = '/';
|
||||
const replaceStr = `$1${separator}`;
|
||||
|
||||
return `${baseURL}${separator}${url}`.replace(combineREG, replaceStr);
|
||||
}
|
||||
|
||||
const dynamicREG = /\/?(:([a-zA-Z_$][\w-$]*))\/??/g;
|
||||
export function dynamicInterpolation(
|
||||
url: string,
|
||||
sourceData?: unknown,
|
||||
): string {
|
||||
if (!isPlainObject(sourceData)) {
|
||||
return url;
|
||||
}
|
||||
|
||||
return url.replace(dynamicREG, ($1, $2, $3) =>
|
||||
$1.replace($2, sourceData[$3]),
|
||||
);
|
||||
}
|
||||
|
||||
const absoluteREG = /^([a-z][a-z\d+\-.]*:)?\/\//i;
|
||||
export function isAbsoluteURL(url: string): boolean {
|
||||
return absoluteREG.test(url);
|
||||
}
|
||||
|
||||
export function isDynamicURL(url: string): boolean {
|
||||
dynamicREG.lastIndex = 0;
|
||||
return dynamicREG.test(url);
|
||||
}
|
||||
|
||||
function generateURL(url: string, serializedParams: string): string {
|
||||
const hashIndex = url.indexOf('#');
|
||||
|
||||
if (hashIndex !== -1) {
|
||||
url = url.slice(0, hashIndex);
|
||||
}
|
||||
|
||||
if (serializedParams === '') {
|
||||
return url;
|
||||
}
|
||||
|
||||
const prefix = url.indexOf('?') === -1 ? '?' : '&';
|
||||
|
||||
serializedParams = `${prefix}${serializedParams}`;
|
||||
|
||||
return `${url}${serializedParams}`;
|
||||
}
|
||||
|
||||
function paramsSerialization(params?: AnyObject): string {
|
||||
if (!isPlainObject(params)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const parts: string[] = [];
|
||||
|
||||
Object.keys(params).forEach((key): void => {
|
||||
const value = params[key];
|
||||
|
||||
if (isNull(value) || isUndefined(value) || value !== value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
key += '[]';
|
||||
}
|
||||
|
||||
const values = [].concat(value);
|
||||
|
||||
values.forEach((val: any): void => {
|
||||
if (isPlainObject(val)) {
|
||||
val = JSON.stringify(val);
|
||||
} else if (isDate(val)) {
|
||||
val = (val as Date).toISOString();
|
||||
}
|
||||
|
||||
parts.push(`${encode(key)}=${encode(val)}`);
|
||||
});
|
||||
});
|
||||
|
||||
return parts.join('&');
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
import { isPlainObject, isString } from './is';
|
||||
|
||||
export function deepMerge<T = unknown>(...objs: T[]): T {
|
||||
const result: AnyObject = {};
|
||||
|
||||
objs.forEach((obj: AnyObject) =>
|
||||
Object.keys(obj).forEach((key) => {
|
||||
const val = obj[key];
|
||||
const resultVal = result[key];
|
||||
|
||||
if (isPlainObject(resultVal) && isPlainObject(val)) {
|
||||
result[key] = deepMerge(resultVal, val);
|
||||
} else if (isPlainObject(val)) {
|
||||
result[key] = deepMerge(val);
|
||||
} else {
|
||||
result[key] = val;
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
return result as T;
|
||||
}
|
||||
|
||||
export function pick<T extends AnyObject, K extends keyof T>(
|
||||
obj: T,
|
||||
...keys: K[]
|
||||
): Pick<T, K> {
|
||||
const _pick: Partial<T> = {};
|
||||
|
||||
keys.forEach((key: K) => (_pick[key] = obj[key]));
|
||||
|
||||
return _pick as Pick<T, K>;
|
||||
}
|
||||
|
||||
export function omit<T extends AnyObject, K extends keyof T>(
|
||||
obj: T,
|
||||
...keys: K[]
|
||||
): Omit<T, K> {
|
||||
const _omit = Object.assign({}, obj);
|
||||
|
||||
keys.forEach((key: K) => delete _omit[key]);
|
||||
|
||||
return _omit;
|
||||
}
|
||||
|
||||
export function assert(condition: boolean, msg: string) {
|
||||
if (!condition) {
|
||||
throwError(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export function throwError(msg: string): void {
|
||||
throw new Error(`[axios-miniprogram]: ${msg}`);
|
||||
}
|
||||
|
||||
export function toLowerCase<T extends string>(value: unknown, defaultValue: T): T {
|
||||
if (!isString(value)) {
|
||||
value = defaultValue;
|
||||
}
|
||||
|
||||
return value.toLowerCase() as T;
|
||||
}
|
||||
|
||||
export function toUpperCase<T extends string>(value: unknown, defaultValue: T): T {
|
||||
if (!isString(value)) {
|
||||
value = defaultValue;
|
||||
}
|
||||
|
||||
return value.toUpperCase() as T;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import axios from './axios';
|
||||
|
||||
export type {
|
||||
AxiosRequestConfig,
|
||||
AxiosRequestFormData,
|
||||
AxiosResponse,
|
||||
AxiosResponseError,
|
||||
} from './core/Axios';
|
||||
export type {
|
||||
AxiosAdapterRequestConfig,
|
||||
AxiosAdapter,
|
||||
AxiosPlatform,
|
||||
} from './core/adapter';
|
||||
export type { AxiosInstance, AxiosStatic } from './axios';
|
||||
export default axios;
|
|
@ -0,0 +1,116 @@
|
|||
import {
|
||||
isArray,
|
||||
isDate,
|
||||
isEmptyArray,
|
||||
isEmptyObject,
|
||||
isFunction,
|
||||
isNull,
|
||||
isPlainObject,
|
||||
isString,
|
||||
isUndefined,
|
||||
} from '../../src/helpers/is';
|
||||
|
||||
describe('对 src/helpers/is.ts 进行测试', () => {
|
||||
it('测试 isArray() 执行结果是否符合预期', () => {
|
||||
expect(isArray([0])).toBe(true);
|
||||
expect(isArray([])).toBe(true);
|
||||
expect(isArray({})).toBe(false);
|
||||
expect(isArray(0)).toBe(false);
|
||||
expect(isArray('')).toBe(false);
|
||||
expect(isArray(undefined)).toBe(false);
|
||||
expect(isArray(null)).toBe(false);
|
||||
});
|
||||
|
||||
it('测试 isDate() 执行结果是否符合预期', () => {
|
||||
expect(isDate(new Date())).toBe(true);
|
||||
expect(isDate({})).toBe(false);
|
||||
expect(isDate([])).toBe(false);
|
||||
expect(isDate(0)).toBe(false);
|
||||
expect(isDate('')).toBe(false);
|
||||
expect(isDate(undefined)).toBe(false);
|
||||
expect(isDate(null)).toBe(false);
|
||||
});
|
||||
|
||||
it('测试 isEmptyArray() 执行结果是否符合预期', () => {
|
||||
expect(isEmptyArray([])).toBe(true);
|
||||
expect(isEmptyArray([0])).toBe(false);
|
||||
expect(isEmptyArray({})).toBe(false);
|
||||
expect(isEmptyArray(0)).toBe(false);
|
||||
expect(isEmptyArray('')).toBe(false);
|
||||
expect(isEmptyArray(undefined)).toBe(false);
|
||||
expect(isEmptyArray(null)).toBe(false);
|
||||
});
|
||||
|
||||
it('测试 isEmptyObject() 执行结果是否符合预期', () => {
|
||||
expect(isEmptyObject({})).toBe(true);
|
||||
expect(isEmptyObject({ a: 0 })).toBe(false);
|
||||
expect(isEmptyObject([0])).toBe(false);
|
||||
expect(isEmptyObject([])).toBe(false);
|
||||
expect(isEmptyObject(0)).toBe(false);
|
||||
expect(isEmptyObject('')).toBe(false);
|
||||
expect(isEmptyObject(undefined)).toBe(false);
|
||||
expect(isEmptyObject(null)).toBe(false);
|
||||
});
|
||||
|
||||
it('测试 isFunction() 执行结果是否符合预期', () => {
|
||||
expect(
|
||||
isFunction(() => {
|
||||
return;
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
isFunction(function () {
|
||||
return;
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(isFunction({})).toBe(false);
|
||||
expect(isFunction([])).toBe(false);
|
||||
expect(isFunction(0)).toBe(false);
|
||||
expect(isFunction('')).toBe(false);
|
||||
expect(isFunction(undefined)).toBe(false);
|
||||
expect(isFunction(null)).toBe(false);
|
||||
});
|
||||
|
||||
it('测试 isNull() 执行结果是否符合预期', () => {
|
||||
expect(isNull(null)).toBe(true);
|
||||
expect(isNull({ a: 0 })).toBe(false);
|
||||
expect(isNull([0])).toBe(false);
|
||||
expect(isNull([])).toBe(false);
|
||||
expect(isNull(0)).toBe(false);
|
||||
expect(isNull('')).toBe(false);
|
||||
expect(isNull(undefined)).toBe(false);
|
||||
});
|
||||
|
||||
it('测试 isPlainObject() 执行结果是否符合预期', () => {
|
||||
expect(isPlainObject({})).toBe(true);
|
||||
expect(isPlainObject({ a: 0 })).toBe(true);
|
||||
expect(isPlainObject([0])).toBe(false);
|
||||
expect(isPlainObject([])).toBe(false);
|
||||
expect(isPlainObject(0)).toBe(false);
|
||||
expect(isPlainObject('')).toBe(false);
|
||||
expect(isPlainObject(undefined)).toBe(false);
|
||||
expect(isPlainObject(null)).toBe(false);
|
||||
});
|
||||
|
||||
it('测试 isString() 执行结果是否符合预期', () => {
|
||||
expect(isString('')).toBe(true);
|
||||
expect(isString({})).toBe(false);
|
||||
expect(isString({ a: 0 })).toBe(false);
|
||||
expect(isString([0])).toBe(false);
|
||||
expect(isString([])).toBe(false);
|
||||
expect(isString(0)).toBe(false);
|
||||
expect(isString(undefined)).toBe(false);
|
||||
expect(isString(null)).toBe(false);
|
||||
});
|
||||
|
||||
it('测试 isUndefined() 执行结果是否符合预期', () => {
|
||||
expect(isUndefined(undefined)).toBe(true);
|
||||
expect(isUndefined('')).toBe(false);
|
||||
expect(isUndefined({})).toBe(false);
|
||||
expect(isUndefined({ a: 0 })).toBe(false);
|
||||
expect(isUndefined([0])).toBe(false);
|
||||
expect(isUndefined([])).toBe(false);
|
||||
expect(isUndefined(0)).toBe(false);
|
||||
expect(isUndefined(null)).toBe(false);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,87 @@
|
|||
import {
|
||||
buildURL,
|
||||
combineURL,
|
||||
dynamicInterpolation,
|
||||
isAbsoluteURL,
|
||||
isDynamicURL,
|
||||
} from '../../src/helpers/url';
|
||||
|
||||
describe('对 src/helpers/url.ts 进行测试', () => {
|
||||
it('测试 buildURL() 执行结果是否符合预期', () => {
|
||||
expect(buildURL('/api')).toBe('/api');
|
||||
expect(buildURL('/api', {})).toBe('/api');
|
||||
expect(buildURL('/api#id=1', {})).toBe('/api');
|
||||
expect(
|
||||
buildURL('/api', {
|
||||
id: 1,
|
||||
}),
|
||||
).toBe('/api?id=1');
|
||||
expect(buildURL('/api', { id: 100 }, () => 'id=1')).toBe('/api?id=1');
|
||||
expect(
|
||||
buildURL('/api?sid=0', {
|
||||
id: 1,
|
||||
}),
|
||||
).toBe('/api?sid=0&id=1');
|
||||
expect(buildURL('/api?sid=0', { id: 100 }, () => 'id=1')).toBe(
|
||||
'/api?sid=0&id=1',
|
||||
);
|
||||
});
|
||||
|
||||
it('测试 combineURL() 执行结果是否符合预期', () => {
|
||||
expect(combineURL('https://www.server.com', 'api')).toBe(
|
||||
'https://www.server.com/api',
|
||||
);
|
||||
expect(combineURL('https://www.server.com/', '/api')).toBe(
|
||||
'https://www.server.com/api',
|
||||
);
|
||||
expect(combineURL('https://www.server.com:8080//', '//api//')).toBe(
|
||||
'https://www.server.com:8080/api/',
|
||||
);
|
||||
});
|
||||
|
||||
it('测试 dynamicInterpolation() 执行结果是否符合预期', () => {
|
||||
expect(
|
||||
dynamicInterpolation('https://www.server.com/api/user/:id', {
|
||||
id: 1,
|
||||
name: 'user',
|
||||
}),
|
||||
).toBe('https://www.server.com/api/user/1');
|
||||
expect(
|
||||
dynamicInterpolation('https://www.server.com:8080/api/user/:id', {
|
||||
id: 1,
|
||||
name: 'user',
|
||||
}),
|
||||
).toBe('https://www.server.com:8080/api/user/1');
|
||||
expect(
|
||||
dynamicInterpolation('https://www.server.com/api/user/:id/:name', {
|
||||
id: 1,
|
||||
}),
|
||||
).toBe('https://www.server.com/api/user/1/undefined');
|
||||
expect(
|
||||
dynamicInterpolation('https://www.server.com/api/user/:id:name', {
|
||||
id: 1,
|
||||
}),
|
||||
).toBe('https://www.server.com/api/user/1undefined');
|
||||
});
|
||||
|
||||
it('测试 isAbsoluteURL() 执行结果是否符合预期', () => {
|
||||
expect(isAbsoluteURL('')).toBe(false);
|
||||
expect(isAbsoluteURL('/api')).toBe(false);
|
||||
expect(isAbsoluteURL('http:')).toBe(false);
|
||||
expect(isAbsoluteURL('//file')).toBe(true);
|
||||
expect(isAbsoluteURL('https://www.server.com')).toBe(true);
|
||||
expect(isAbsoluteURL('file://')).toBe(true);
|
||||
});
|
||||
|
||||
it('测试 isDynamicURL() 执行结果是否符合预期', () => {
|
||||
expect(isDynamicURL('')).toBe(false);
|
||||
expect(isDynamicURL(':id')).toBe(true);
|
||||
expect(isDynamicURL(':8080')).toBe(false);
|
||||
expect(isDynamicURL('/:id')).toBe(true);
|
||||
expect(isDynamicURL('/:8080')).toBe(false);
|
||||
expect(isDynamicURL('https://www.server.com:8080')).toBe(false);
|
||||
expect(isDynamicURL('/api')).toBe(false);
|
||||
expect(isDynamicURL('/api:id')).toBe(true);
|
||||
expect(isDynamicURL('/api/:id')).toBe(true);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,65 @@
|
|||
import {
|
||||
assert,
|
||||
deepMerge,
|
||||
omit,
|
||||
pick,
|
||||
throwError,
|
||||
toLowerCase,
|
||||
toUpperCase,
|
||||
} from '../../src/helpers/utils';
|
||||
|
||||
describe('对 src/helpers/utils.ts 进行测试', () => {
|
||||
it('测试 assert() 执行结果是否符合预期', () => {
|
||||
expect(assert(true, '')).toBeUndefined();
|
||||
expect(() => assert(false, '')).toThrow();
|
||||
expect(() => assert(false, 'msg')).toThrowError('[axios-miniprogram]: msg');
|
||||
});
|
||||
|
||||
it('测试 deepMerge() 执行结果是否符合预期', () => {
|
||||
expect(deepMerge({})).toEqual({});
|
||||
expect(deepMerge({ a: 1 }, { b: 2 })).toEqual({ a: 1, b: 2 });
|
||||
expect(deepMerge({ a: { a: 1 } }, { a: { b: 2 } })).toEqual({
|
||||
a: { a: 1, b: 2 },
|
||||
});
|
||||
expect(deepMerge({ a: { a: 1, b: 1 } }, { a: { a: 2, b: 2 } })).toEqual({
|
||||
a: { a: 2, b: 2 },
|
||||
});
|
||||
expect(deepMerge({ a: { a: 1 } }, { a: 2 })).toEqual({
|
||||
a: 2,
|
||||
});
|
||||
});
|
||||
|
||||
it('测试 omit() 执行结果是否符合预期', () => {
|
||||
expect(omit({})).toEqual({});
|
||||
expect(omit({ a: 1, b: 1 }, 'a')).toEqual({ b: 1 });
|
||||
expect(omit({ a: 1, b: 1 }, 'a', 'b')).toEqual({});
|
||||
});
|
||||
|
||||
it('测试 pick() 执行结果是否符合预期', () => {
|
||||
expect(pick({})).toEqual({});
|
||||
expect(pick({ a: 1, b: 1 }, 'a')).toEqual({ a: 1 });
|
||||
expect(pick({ a: 1, b: 1 }, 'a', 'b')).toEqual({ a: 1, b: 1 });
|
||||
});
|
||||
|
||||
it('测试 throwError() 执行结果是否符合预期', () => {
|
||||
expect(() => throwError('')).toThrowError('[axios-miniprogram]: ');
|
||||
expect(() => throwError('msg')).toThrowError('[axios-miniprogram]: msg');
|
||||
expect(() => throwError(' msg ')).toThrowError(
|
||||
'[axios-miniprogram]: msg ',
|
||||
);
|
||||
});
|
||||
|
||||
it('测试 toLowerCase() 执行结果是否符合预期', () => {
|
||||
expect(toLowerCase('', 'GET')).toBe('');
|
||||
expect(toLowerCase(undefined, 'GET')).toBe('get');
|
||||
expect(toLowerCase('GET', '')).toBe('get');
|
||||
expect(toLowerCase('Get', '')).toBe('get');
|
||||
});
|
||||
|
||||
it('测试 toUpperCase() 执行结果是否符合预期', () => {
|
||||
expect(toUpperCase('', 'get')).toBe('');
|
||||
expect(toUpperCase(undefined, 'get')).toBe('GET');
|
||||
expect(toUpperCase('get', '')).toBe('GET');
|
||||
expect(toUpperCase('Get', '')).toBe('GET');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": ["src", "global.d.ts"]
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"rootDir": ".",
|
||||
"baseUrl": ".",
|
||||
"lib": ["ESNext"],
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"strict": true,
|
||||
"noEmit": true
|
||||
},
|
||||
"include": ["src", "test", "global.d.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
Loading…
Reference in New Issue