@ruan-cat/vercel-deploy-tool 0.0.2
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/package.json +51 -0
- package/readme.md +14 -0
- package/src/config.ts +213 -0
- package/src/index.ts +455 -0
- package/src/utils/define-promise-tasks.ts +138 -0
- package/src/utils/simple-promise-tools.ts +94 -0
- package/tsconfig.json +38 -0
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ruan-cat/vercel-deploy-tool",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "阮喵喵自用的vercel部署工具,用于实现复杂项目的部署。",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./src/index.ts",
|
|
7
|
+
"types": "./src/index.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./src/index.ts",
|
|
11
|
+
"types": "./src/index.ts",
|
|
12
|
+
"__types": "./dist/index.d.ts",
|
|
13
|
+
"__import": "./dist/index.mjs",
|
|
14
|
+
"__require": "./dist/index.cjs"
|
|
15
|
+
},
|
|
16
|
+
"./*": "./src/*"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"vercel"
|
|
20
|
+
],
|
|
21
|
+
"author": "ruan-cat",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public",
|
|
25
|
+
"registry": "https://registry.npmjs.org/",
|
|
26
|
+
"tag": "beta"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"src",
|
|
30
|
+
"tsconfig.json"
|
|
31
|
+
],
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"c12": "^1.11.2",
|
|
34
|
+
"consola": "^3.2.3",
|
|
35
|
+
"execa": "^9.3.1",
|
|
36
|
+
"pathe": "^1.1.2",
|
|
37
|
+
"vercel": "^34.2.7"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^22.5.1",
|
|
41
|
+
"@vitest/ui": "^2.0.5",
|
|
42
|
+
"vitest": "^2.0.5"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"—build-not-use-for-now": "tsc",
|
|
46
|
+
"start": "node ./dist/index.js",
|
|
47
|
+
"run": "vite-node ./src/index.ts --files",
|
|
48
|
+
"test:config": "vite-node ./tests/config.test.ts --files",
|
|
49
|
+
"test:vitest": "vitest --ui --watch"
|
|
50
|
+
}
|
|
51
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# 部署工具
|
|
2
|
+
|
|
3
|
+
## 下一步计划
|
|
4
|
+
|
|
5
|
+
现在整个流程都跑通了,接下来是去弄:
|
|
6
|
+
|
|
7
|
+
- [x] 封装打包命令,vc deploy 命令,并赋予生产环境 url。
|
|
8
|
+
- [ ] 拆分配置文件到项目根目录,并实现文件读取。
|
|
9
|
+
- [ ] vite 打包 typescript 成 javascript。
|
|
10
|
+
- [ ] 本地运行 javascript 文件,测试。
|
|
11
|
+
- [ ] github action 运行产物。
|
|
12
|
+
- [ ] 封装 node 的 bin 命令,发包。
|
|
13
|
+
- [ ] github action 全局安装新开发的包,实现纯工作流的部署。
|
|
14
|
+
- [ ] 去其他项目,自主完成配置与部署。
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { resolve } from "pathe";
|
|
2
|
+
import { loadConfig } from "c12";
|
|
3
|
+
import { config as dotenvConfig } from "@dotenvx/dotenvx";
|
|
4
|
+
import { consola } from "consola";
|
|
5
|
+
import { merge } from "lodash-es";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @description
|
|
9
|
+
* 从 drizzle-kit 学的
|
|
10
|
+
*/
|
|
11
|
+
type Verify<T, U extends T> = U;
|
|
12
|
+
|
|
13
|
+
const deployTargetTypes = <const>["static", "userCommands"];
|
|
14
|
+
|
|
15
|
+
type DeployTargetType = (typeof deployTargetTypes)[number];
|
|
16
|
+
|
|
17
|
+
/** 配置基类 */
|
|
18
|
+
export type Base = {
|
|
19
|
+
/** 部署目标分类 */
|
|
20
|
+
type: DeployTargetType;
|
|
21
|
+
|
|
22
|
+
/** 目标的工作目录 */
|
|
23
|
+
targetCWD: string;
|
|
24
|
+
|
|
25
|
+
/** 生产环境的访问url */
|
|
26
|
+
url: string[];
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 带有 `pnpm -C` 筛选前缀的用户命令
|
|
31
|
+
* @example pnpm -C=./packages/docs-01-star build:docs
|
|
32
|
+
*/
|
|
33
|
+
// type UserCommandsWithPnpmPath<T extends string> = `pnpm -C=${T} ${string}`;
|
|
34
|
+
type UserCommandsWithPnpmPath<T extends WithUserCommands["targetCWD"]> = `pnpm -C=${T} ${string}`;
|
|
35
|
+
|
|
36
|
+
/** 带有用户命令的配置 */
|
|
37
|
+
|
|
38
|
+
export interface WithUserCommands extends Base {
|
|
39
|
+
type: Verify<DeployTargetType, "userCommands">;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 用户命令
|
|
43
|
+
* @description
|
|
44
|
+
* 实际部署的构建命令 通常是真实参与部署的命令
|
|
45
|
+
*
|
|
46
|
+
* FIXME: 在具体的execa中,无法使用pnpm的筛选命令。只能指定其工作目录。
|
|
47
|
+
* TODO: 实现对 targetCWD 的读取,并实现类型声明。
|
|
48
|
+
*
|
|
49
|
+
* pnpm -F @ruan-cat-vercel-monorepo-test/docs-01-star build:docs
|
|
50
|
+
*
|
|
51
|
+
* @example pnpm -C=./packages/docs-01-star build:docs
|
|
52
|
+
* @example pnpm -C=./packages/monorepo-5 build:docs
|
|
53
|
+
*
|
|
54
|
+
*/
|
|
55
|
+
// userCommands: Array<UserCommandsWithPnpmPath<WithUserCommands["outputDirectory"]> | string>;
|
|
56
|
+
userCommands: UserCommandsWithPnpmPath<WithUserCommands["targetCWD"]>[];
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 部署输出路径
|
|
60
|
+
* @description
|
|
61
|
+
* 这里要填写满足 cpx 库能够识别glob语法的路径
|
|
62
|
+
* @example docs/.vitepress/dist/**\/*
|
|
63
|
+
* @example src/.vuepress/dist/**\/*
|
|
64
|
+
*/
|
|
65
|
+
outputDirectory: string;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 是否移动打包目录至特定的vercel部署目录?
|
|
69
|
+
* @description
|
|
70
|
+
* 执行完用户命令后,一般会执行文件移动命令,以便于部署
|
|
71
|
+
*
|
|
72
|
+
* 该配置用于控制是否执行文件移动命令
|
|
73
|
+
*
|
|
74
|
+
* @default true
|
|
75
|
+
*/
|
|
76
|
+
isCopyDist?: boolean;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** 部署目标的具体项目配置 */
|
|
80
|
+
export type DeployTarget = Base | WithUserCommands;
|
|
81
|
+
|
|
82
|
+
/** vercel部署工具的配置 */
|
|
83
|
+
export interface Config {
|
|
84
|
+
/** 项目名称 */
|
|
85
|
+
vercelProjetName: string;
|
|
86
|
+
|
|
87
|
+
/** 用户token */
|
|
88
|
+
vercelToken: string;
|
|
89
|
+
|
|
90
|
+
/** 用户组织id */
|
|
91
|
+
vercelOrgId: string;
|
|
92
|
+
|
|
93
|
+
/** 用户项目id */
|
|
94
|
+
vercelProjectId: string;
|
|
95
|
+
|
|
96
|
+
/** 在build命令阶段后 执行的用户命令 */
|
|
97
|
+
afterBuildTasks?: string[];
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* 部署目标
|
|
101
|
+
* @description
|
|
102
|
+
* 考虑到可能要部署一揽子的项目,所以这里使用数组
|
|
103
|
+
*
|
|
104
|
+
* 考虑monorepo的情况
|
|
105
|
+
*/
|
|
106
|
+
deployTargets: DeployTarget[];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// 拓展返回值
|
|
110
|
+
declare module "@dotenvx/dotenvx" {
|
|
111
|
+
interface DotenvParseOutput {
|
|
112
|
+
[name: string]: string;
|
|
113
|
+
/**
|
|
114
|
+
* token
|
|
115
|
+
* @description
|
|
116
|
+
* 默认名称为 `VERCEL_TOKEN`
|
|
117
|
+
*/
|
|
118
|
+
VERCEL_TOKEN: string;
|
|
119
|
+
/**
|
|
120
|
+
* 组织id
|
|
121
|
+
* @description
|
|
122
|
+
* 默认名称为 `VERCEL_ORG_ID`
|
|
123
|
+
*/
|
|
124
|
+
VERCEL_ORG_ID: string;
|
|
125
|
+
/**
|
|
126
|
+
* 项目id
|
|
127
|
+
* @description
|
|
128
|
+
* 默认名称为 `VERCEL_PROJECT_ID`
|
|
129
|
+
*/
|
|
130
|
+
VERCEL_PROJECT_ID: string;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** 初始化的当前的环境变量 */
|
|
135
|
+
function initCurrentDotenvConfig() {
|
|
136
|
+
const res = dotenvConfig({
|
|
137
|
+
// 具体识别的路径,会自动识别根目录下面的env文件,故这里不作处理
|
|
138
|
+
// path: "../../../.env"
|
|
139
|
+
}).parsed;
|
|
140
|
+
|
|
141
|
+
consola.info(" 查看来自 @dotenvx/dotenvx 获取的环境变量: ");
|
|
142
|
+
consola.box(res);
|
|
143
|
+
|
|
144
|
+
return res;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** 默认配置 */
|
|
148
|
+
const defConfig = <Config>{
|
|
149
|
+
vercelProjetName: "",
|
|
150
|
+
vercelToken: "",
|
|
151
|
+
vercelOrgId: "",
|
|
152
|
+
vercelProjectId: "",
|
|
153
|
+
deployTargets: [],
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
/** 配置文件的文件名称 */
|
|
157
|
+
export const configFileName = <const>"vercel-deploy-tool";
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* 加载用户配置
|
|
161
|
+
* @description
|
|
162
|
+
* 从约定俗成的配置处,获得用户配置文件
|
|
163
|
+
*/
|
|
164
|
+
async function loadUserConfig() {
|
|
165
|
+
const { config } = await loadConfig<Config>({
|
|
166
|
+
cwd: resolve("."),
|
|
167
|
+
name: configFileName,
|
|
168
|
+
dotenv: true,
|
|
169
|
+
defaults: defConfig,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
consola.success(" 完成加载用户配置 ");
|
|
173
|
+
consola.box(config);
|
|
174
|
+
|
|
175
|
+
return config;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* 初始化配置
|
|
180
|
+
* @description
|
|
181
|
+
* 初始化环境变量
|
|
182
|
+
*/
|
|
183
|
+
export async function initVercelConfig() {
|
|
184
|
+
/** 当前的环境变量 */
|
|
185
|
+
const currentDotenvConfig = initCurrentDotenvConfig();
|
|
186
|
+
|
|
187
|
+
/** 用户配置 */
|
|
188
|
+
const userConfig = await loadUserConfig();
|
|
189
|
+
|
|
190
|
+
const vercelOrgId = currentDotenvConfig!.VERCEL_ORG_ID ?? process.env.VERCEL_ORG_ID;
|
|
191
|
+
const vercelProjectId = currentDotenvConfig!.VERCEL_PROJECT_ID ?? process.env.VERCEL_PROJECT_ID;
|
|
192
|
+
const vercelToken = currentDotenvConfig!.VERCEL_TOKEN ?? process.env.VERCEL_TOKEN;
|
|
193
|
+
|
|
194
|
+
const res: Config = merge(userConfig, {
|
|
195
|
+
vercelOrgId,
|
|
196
|
+
vercelProjectId,
|
|
197
|
+
vercelToken,
|
|
198
|
+
} satisfies Partial<Config>);
|
|
199
|
+
|
|
200
|
+
consola.success(" 完成初始化项目配置 ");
|
|
201
|
+
// 显示效果没有那么好看
|
|
202
|
+
consola.box(res);
|
|
203
|
+
|
|
204
|
+
return res;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** 项目内的vercel配置 */
|
|
208
|
+
export const config = await initVercelConfig();
|
|
209
|
+
|
|
210
|
+
/** 项目内的vercel配置 */
|
|
211
|
+
export function getConfig() {
|
|
212
|
+
return config;
|
|
213
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
// 学习一下如何使用 https://github.com/sindresorhus/execa/blob/main/readme.md
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import { execa } from "execa";
|
|
4
|
+
import { concat, isEmpty, isUndefined } from "lodash-es";
|
|
5
|
+
import { consola } from "consola";
|
|
6
|
+
import { isConditionsEvery, isConditionsSome } from "@ruan-cat/utils";
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
initVercelConfig,
|
|
10
|
+
config,
|
|
11
|
+
getConfig,
|
|
12
|
+
type Config,
|
|
13
|
+
type Base,
|
|
14
|
+
type DeployTarget,
|
|
15
|
+
type WithUserCommands,
|
|
16
|
+
} from "./config";
|
|
17
|
+
import { generateSimpleAsyncTask } from "./utils/simple-promise-tools";
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
definePromiseTasks,
|
|
21
|
+
executePromiseTasks,
|
|
22
|
+
type BaseTask,
|
|
23
|
+
type ParallelTasks,
|
|
24
|
+
type QueueTasks,
|
|
25
|
+
type Task,
|
|
26
|
+
} from "./utils/define-promise-tasks";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* vercel 的空配置
|
|
30
|
+
* @description
|
|
31
|
+
* 设计理由
|
|
32
|
+
*
|
|
33
|
+
* 用于驱动vercel构建简单的目录结构,不需要额外的配置
|
|
34
|
+
*
|
|
35
|
+
* 该配置会被写入到 `vercel.null.def.json` 文件中
|
|
36
|
+
*
|
|
37
|
+
* @see https://github.com/amondnet/vercel-action#method-1---via-vercel-interface
|
|
38
|
+
*/
|
|
39
|
+
export const vercelNullConfig = <const>{
|
|
40
|
+
framework: null,
|
|
41
|
+
buildCommand: null,
|
|
42
|
+
installCommand: null,
|
|
43
|
+
outputDirectory: null,
|
|
44
|
+
devCommand: null,
|
|
45
|
+
public: false,
|
|
46
|
+
git: {
|
|
47
|
+
deploymentEnabled: {
|
|
48
|
+
main: false,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 空配置文件的路径
|
|
55
|
+
* @description
|
|
56
|
+
* 生成空配置文件。这样用户在其他项目内,就不需要自己提供vercel配置文件了。
|
|
57
|
+
*/
|
|
58
|
+
const vercelNullConfigPath = <const>"./vercel.null.def.json";
|
|
59
|
+
|
|
60
|
+
/** vercel文件api指定要求的文件目录 */
|
|
61
|
+
const vercelOutputStatic = <const>".vercel/output/static";
|
|
62
|
+
|
|
63
|
+
/** 初始化vercel的空配置文件 */
|
|
64
|
+
async function generateVercelNullConfig() {
|
|
65
|
+
fs.writeFileSync(vercelNullConfigPath, JSON.stringify(vercelNullConfig, null, 2));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function isDeployTargetsBase(target: DeployTarget): target is Base {
|
|
69
|
+
return target.type === "static";
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function isDeployTargetsWithUserCommands(target: DeployTarget): target is WithUserCommands {
|
|
73
|
+
return target.type === "userCommands";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** 获得 isCopyDist 配置 */
|
|
77
|
+
function getIsCopyDist(target: WithUserCommands) {
|
|
78
|
+
return target?.isCopyDist ?? true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** 是否需要移动文件? */
|
|
82
|
+
function isNeedCopyDist(target: DeployTarget) {
|
|
83
|
+
if (isDeployTargetsWithUserCommands(target)) {
|
|
84
|
+
const isCopyDist = getIsCopyDist(target);
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 每个条件都满足时 就需要移动文件
|
|
88
|
+
* 默认总是认为要移动文件
|
|
89
|
+
*/
|
|
90
|
+
return isConditionsEvery([
|
|
91
|
+
// 如果被用户显性设置为false,不需要移动,那么就直接退出 不移动文件
|
|
92
|
+
() => isCopyDist,
|
|
93
|
+
|
|
94
|
+
// 只要用户提供了非空输出目录 就认为需要移动文件
|
|
95
|
+
// 输出目录是必填项 不做判断
|
|
96
|
+
// () => !isEmpty(target.outputDirectory),
|
|
97
|
+
|
|
98
|
+
// 只要用户提供了非空命令 就认为用户提供了有意义的build构建命令 就默认移动文件
|
|
99
|
+
// 用户不填写命令时 也可能需要移动文件 故这里不做判断
|
|
100
|
+
// () => !isEmpty(target.userCommands),
|
|
101
|
+
]);
|
|
102
|
+
} else {
|
|
103
|
+
// 不是带有用户命令的部署目标 那么就不需要移动文件
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function getYesCommandArgument() {
|
|
109
|
+
return <const>["--yes"];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function getProdCommandArgument() {
|
|
113
|
+
return <const>["--prod"];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function getPrebuiltCommandArgument() {
|
|
117
|
+
return <const>["--prebuilt"];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** 以命令参数数组的形式,获得项目名称 */
|
|
121
|
+
function getVercelProjetNameCommandArgument() {
|
|
122
|
+
return <const>[`--project=${config.vercelProjetName}`];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** 以命令参数数组的形式,获得项目token */
|
|
126
|
+
function getVercelTokenCommandArgument() {
|
|
127
|
+
return <const>[`--token=${config.vercelToken}`];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** 以命令参数数组的形式,获得项目vercel的本地配置 */
|
|
131
|
+
function getVercelLocalConfigCommandArgument() {
|
|
132
|
+
return <const>[`--local-config=${vercelNullConfigPath}`];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** 以命令参数数组的形式,获得工作目录 */
|
|
136
|
+
function getTargetCWDCommandArgument(deployTarget: DeployTarget) {
|
|
137
|
+
return <const>[`--cwd=${deployTarget.targetCWD}`];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* 生成简单的 execa 函数
|
|
142
|
+
* @description
|
|
143
|
+
* 对 execa 做简单的包装
|
|
144
|
+
*/
|
|
145
|
+
function generateExeca(execaSimpleParams: { command: string; parameters: string[] }) {
|
|
146
|
+
const { command, parameters } = execaSimpleParams;
|
|
147
|
+
return generateSimpleAsyncTask(() => execa(command, parameters, { shell: true }));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* 生成link任务
|
|
152
|
+
* @description
|
|
153
|
+
* 旨在于封装类似于这样的命令:
|
|
154
|
+
*
|
|
155
|
+
* vc link --yes --cwd=${{env.p1}} --project=${{env.pjn}} -t ${{env.vct}}
|
|
156
|
+
*/
|
|
157
|
+
function generateLinkTask(deployTarget: DeployTarget) {
|
|
158
|
+
return generateExeca({
|
|
159
|
+
command: "vc link",
|
|
160
|
+
parameters: concat(
|
|
161
|
+
getYesCommandArgument(),
|
|
162
|
+
getTargetCWDCommandArgument(deployTarget),
|
|
163
|
+
getVercelProjetNameCommandArgument(),
|
|
164
|
+
getVercelTokenCommandArgument(),
|
|
165
|
+
),
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* 生成build任务
|
|
171
|
+
* @description
|
|
172
|
+
* 旨在于封装类似于这样的命令:
|
|
173
|
+
*
|
|
174
|
+
* vc build --yes --prod --cwd=${{env.p1}} -A ./vercel.null.json -t ${{env.vct}}
|
|
175
|
+
*/
|
|
176
|
+
function generateBuildTask(deployTarget: DeployTarget) {
|
|
177
|
+
return generateExeca({
|
|
178
|
+
command: "vc build",
|
|
179
|
+
parameters: concat(
|
|
180
|
+
getYesCommandArgument(),
|
|
181
|
+
getProdCommandArgument(),
|
|
182
|
+
getTargetCWDCommandArgument(deployTarget),
|
|
183
|
+
getVercelLocalConfigCommandArgument(),
|
|
184
|
+
getVercelTokenCommandArgument(),
|
|
185
|
+
),
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* 针对单个部署目标,生成一系列移动目录的任务
|
|
191
|
+
* @description
|
|
192
|
+
* 旨在于封装类似于这样的命令:
|
|
193
|
+
*
|
|
194
|
+
* ```bash
|
|
195
|
+
* # 删除目录
|
|
196
|
+
* rimraf .vercel/output/static
|
|
197
|
+
*
|
|
198
|
+
* # 新建目录
|
|
199
|
+
* mkdirp .vercel/output/static
|
|
200
|
+
*
|
|
201
|
+
* # 复制目录到目标
|
|
202
|
+
* cpx \"docs/.vitepress/dist/**\/*\" .vercel/output/static
|
|
203
|
+
*
|
|
204
|
+
* # 输出目录
|
|
205
|
+
* shx ls -R .vercel/output/static
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
function generateCopyDistTasks(deployTarget: WithUserCommands) {
|
|
209
|
+
function delDirectoryCmd() {
|
|
210
|
+
return <const>`rimraf ${vercelOutputStatic}`;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function createDirectoryCmd() {
|
|
214
|
+
return <const>`mkdirp ${vercelOutputStatic}`;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function copyDirectoryFileCmd() {
|
|
218
|
+
return <const>`cpx "${deployTarget.outputDirectory}" ${vercelOutputStatic}`;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function printDirectoryFileCmd() {
|
|
222
|
+
return <const>`shx ls -R ${vercelOutputStatic}`;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function cmdPrefix() {
|
|
226
|
+
return <const>`pnpm -C=${deployTarget.targetCWD}`;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function cmdTemple<T extends (...args: any) => string, R extends ReturnType<T>>(
|
|
230
|
+
cmdFunc: T,
|
|
231
|
+
): `${ReturnType<typeof cmdPrefix>} ${R}` {
|
|
232
|
+
return `${cmdPrefix()} ${<R>cmdFunc()}`;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const delCmd = cmdTemple(delDirectoryCmd);
|
|
236
|
+
const createCmd = cmdTemple(createDirectoryCmd);
|
|
237
|
+
const copyFileCmd = cmdTemple(copyDirectoryFileCmd);
|
|
238
|
+
const printFileCmd = cmdTemple(printDirectoryFileCmd);
|
|
239
|
+
|
|
240
|
+
const copyDistTasks = (<const>[delCmd, createCmd, copyFileCmd, printFileCmd]).map((command) => {
|
|
241
|
+
return generateSimpleAsyncTask(async function () {
|
|
242
|
+
const commandFunction = generateExeca({
|
|
243
|
+
command,
|
|
244
|
+
parameters: [],
|
|
245
|
+
});
|
|
246
|
+
const { code, stdout } = await commandFunction();
|
|
247
|
+
consola.info(` 执行了命令: `, code);
|
|
248
|
+
consola.box(stdout);
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
return copyDistTasks;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* 生成alias任务
|
|
257
|
+
* @description
|
|
258
|
+
* 旨在于封装类似于这样的命令:
|
|
259
|
+
*
|
|
260
|
+
* vc alias set "$url1" ${{env.p1-url}} -t ${{env.vct}}
|
|
261
|
+
*/
|
|
262
|
+
function generateAliasTask(vercelUrl: string, userUrl: string) {
|
|
263
|
+
return generateExeca({
|
|
264
|
+
command: `vc alias set ${vercelUrl} ${userUrl}`,
|
|
265
|
+
parameters: concat(getVercelTokenCommandArgument()),
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* 生成Deploy任务
|
|
271
|
+
* @description
|
|
272
|
+
* 旨在于封装类似于这样的命令:
|
|
273
|
+
*
|
|
274
|
+
* vc deploy --yes --prebuilt --prod --cwd=${{env.p1}} -t ${{env.vct}}
|
|
275
|
+
*/
|
|
276
|
+
function generateDeployTask(deployTarget: DeployTarget) {
|
|
277
|
+
return generateExeca({
|
|
278
|
+
command: "vc deploy",
|
|
279
|
+
parameters: concat(
|
|
280
|
+
getYesCommandArgument(),
|
|
281
|
+
getPrebuiltCommandArgument(),
|
|
282
|
+
getProdCommandArgument(),
|
|
283
|
+
getTargetCWDCommandArgument(deployTarget),
|
|
284
|
+
getVercelTokenCommandArgument(),
|
|
285
|
+
),
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* 生成 afterBuildTasks 阶段的任务配置
|
|
291
|
+
* @description
|
|
292
|
+
* 这里返回的是具体的 Task 任务配置 不是异步函数
|
|
293
|
+
*/
|
|
294
|
+
function generateAfterBuildTasksConfig(config: Config): Task {
|
|
295
|
+
const afterBuildTasks = config.afterBuildTasks;
|
|
296
|
+
|
|
297
|
+
if (isConditionsSome([() => isUndefined(afterBuildTasks), () => isEmpty(afterBuildTasks)])) {
|
|
298
|
+
return {
|
|
299
|
+
type: "single",
|
|
300
|
+
tasks: generateSimpleAsyncTask(() => consola.warn(` 当前没有有意义的 afterBuildTasks 任务配置 `)),
|
|
301
|
+
};
|
|
302
|
+
} else {
|
|
303
|
+
return {
|
|
304
|
+
type: "queue",
|
|
305
|
+
tasks: afterBuildTasks!.map((command) => {
|
|
306
|
+
return generateSimpleAsyncTask(async () => {
|
|
307
|
+
const userCommand = generateExeca({
|
|
308
|
+
command,
|
|
309
|
+
parameters: [],
|
|
310
|
+
});
|
|
311
|
+
consola.start(` 开始用户 afterBuildTasks 命令任务 `);
|
|
312
|
+
const { code, stdout } = await userCommand();
|
|
313
|
+
consola.success(` 完成用户 afterBuildTasks 命令任务 ${code} `);
|
|
314
|
+
consola.box(stdout);
|
|
315
|
+
});
|
|
316
|
+
}),
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* 使用异步函数定义工具的方式
|
|
323
|
+
* @version 2
|
|
324
|
+
*/
|
|
325
|
+
async function main() {
|
|
326
|
+
await generateVercelNullConfig();
|
|
327
|
+
|
|
328
|
+
const config = getConfig();
|
|
329
|
+
const { deployTargets } = config;
|
|
330
|
+
|
|
331
|
+
const promiseTasks = definePromiseTasks({
|
|
332
|
+
type: "queue",
|
|
333
|
+
|
|
334
|
+
tasks: [
|
|
335
|
+
// 全部的link链接任务
|
|
336
|
+
{
|
|
337
|
+
type: "parallel",
|
|
338
|
+
tasks: deployTargets.map((deployTarget) => {
|
|
339
|
+
return generateSimpleAsyncTask(async () => {
|
|
340
|
+
const link = generateLinkTask(deployTarget);
|
|
341
|
+
consola.start(` 开始link任务 `);
|
|
342
|
+
await link();
|
|
343
|
+
consola.success(` 完成link任务 `);
|
|
344
|
+
});
|
|
345
|
+
}),
|
|
346
|
+
},
|
|
347
|
+
|
|
348
|
+
// 全部的build构建任务
|
|
349
|
+
{
|
|
350
|
+
type: "parallel",
|
|
351
|
+
tasks: deployTargets.map((deployTarget) => {
|
|
352
|
+
return generateSimpleAsyncTask(async () => {
|
|
353
|
+
const build = generateBuildTask(deployTarget);
|
|
354
|
+
consola.start(` 开始build任务 `);
|
|
355
|
+
const { code, stdout } = await build();
|
|
356
|
+
consola.success(` 完成build任务 `);
|
|
357
|
+
consola.info(` 完成命令 ${code} `);
|
|
358
|
+
consola.box(stdout);
|
|
359
|
+
});
|
|
360
|
+
}),
|
|
361
|
+
},
|
|
362
|
+
|
|
363
|
+
// afterBuildTasks 在build命令阶段后 执行的用户命令
|
|
364
|
+
generateAfterBuildTasksConfig(config),
|
|
365
|
+
|
|
366
|
+
// 全部的用户命令任务
|
|
367
|
+
{
|
|
368
|
+
type: "parallel",
|
|
369
|
+
tasks: deployTargets.map((deployTarget) => {
|
|
370
|
+
return {
|
|
371
|
+
type: "queue",
|
|
372
|
+
tasks: [
|
|
373
|
+
// 用户命令任务
|
|
374
|
+
// 如果没有用户命令
|
|
375
|
+
!isDeployTargetsWithUserCommands(deployTarget)
|
|
376
|
+
? generateSimpleAsyncTask(() => {
|
|
377
|
+
consola.warn(" 当前目标不属于需要执行一系列用户自定义命令。 ");
|
|
378
|
+
})
|
|
379
|
+
: // 否则有用户命令
|
|
380
|
+
{
|
|
381
|
+
type: "queue",
|
|
382
|
+
tasks: deployTarget.userCommands.map((command) => {
|
|
383
|
+
return generateSimpleAsyncTask(async () => {
|
|
384
|
+
const userCommand = generateExeca({
|
|
385
|
+
command,
|
|
386
|
+
parameters: [],
|
|
387
|
+
});
|
|
388
|
+
consola.start(` 开始用户命令任务 `);
|
|
389
|
+
const { code, stdout } = await userCommand();
|
|
390
|
+
consola.success(` 完成用户命令任务 ${code} `);
|
|
391
|
+
consola.box(stdout);
|
|
392
|
+
});
|
|
393
|
+
}),
|
|
394
|
+
},
|
|
395
|
+
|
|
396
|
+
// 复制移动文件任务
|
|
397
|
+
// 是否需要移动文件?
|
|
398
|
+
isNeedCopyDist(deployTarget) &&
|
|
399
|
+
// 这一行判断其实是冗余的 仅用于满足下面的类型检查
|
|
400
|
+
isDeployTargetsWithUserCommands(deployTarget)
|
|
401
|
+
? {
|
|
402
|
+
type: "queue",
|
|
403
|
+
tasks: generateCopyDistTasks(deployTarget),
|
|
404
|
+
}
|
|
405
|
+
: generateSimpleAsyncTask(() => {
|
|
406
|
+
consola.warn(" 不需要移动文件 ");
|
|
407
|
+
}),
|
|
408
|
+
],
|
|
409
|
+
};
|
|
410
|
+
}),
|
|
411
|
+
},
|
|
412
|
+
|
|
413
|
+
// 全部的部署任务
|
|
414
|
+
{
|
|
415
|
+
type: "parallel",
|
|
416
|
+
tasks: deployTargets.map((deployTarget) => {
|
|
417
|
+
return {
|
|
418
|
+
type: "queue",
|
|
419
|
+
// 串行执行部署任务和别名任务
|
|
420
|
+
tasks: [
|
|
421
|
+
// 部署任务
|
|
422
|
+
generateSimpleAsyncTask(async () => {
|
|
423
|
+
const deploy = generateDeployTask(deployTarget);
|
|
424
|
+
consola.start(` 开始部署任务 `);
|
|
425
|
+
const { stdout: vercelUrl } = await deploy();
|
|
426
|
+
consola.success(` 完成部署任务 检查生成的url为 \n `);
|
|
427
|
+
consola.box(vercelUrl);
|
|
428
|
+
return vercelUrl;
|
|
429
|
+
}),
|
|
430
|
+
|
|
431
|
+
// 并发的别名任务
|
|
432
|
+
{
|
|
433
|
+
type: "parallel",
|
|
434
|
+
tasks: deployTarget.url.map((userUrl) => {
|
|
435
|
+
return generateSimpleAsyncTask(async (vercelUrl: string) => {
|
|
436
|
+
const alias = generateAliasTask(vercelUrl, userUrl);
|
|
437
|
+
consola.start(` 开始别名任务 `);
|
|
438
|
+
const { stdout, command } = await alias();
|
|
439
|
+
consola.success(` 执行了: ${command} `);
|
|
440
|
+
consola.success(` 完成别名任务 可用的别名地址为 \n`);
|
|
441
|
+
consola.box(`https://${userUrl}`);
|
|
442
|
+
});
|
|
443
|
+
}),
|
|
444
|
+
},
|
|
445
|
+
],
|
|
446
|
+
};
|
|
447
|
+
}),
|
|
448
|
+
},
|
|
449
|
+
],
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
await executePromiseTasks(promiseTasks);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
main();
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type SimpleAsyncTask,
|
|
3
|
+
type SimpleAsyncTaskWithType,
|
|
4
|
+
generateSimpleAsyncTask,
|
|
5
|
+
runPromiseByConcurrency,
|
|
6
|
+
runPromiseByQueue,
|
|
7
|
+
} from "./simple-promise-tools";
|
|
8
|
+
|
|
9
|
+
export const taskTypes = <const>["single", "parallel", "queue"];
|
|
10
|
+
|
|
11
|
+
export type TaskType = (typeof taskTypes)[number];
|
|
12
|
+
|
|
13
|
+
export interface BaseTask {
|
|
14
|
+
/** 任务类型 */
|
|
15
|
+
type: TaskType;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// type Task = SimpleAsyncTaskWithType | TasksConfig;
|
|
19
|
+
export type Task = SimpleAsyncTask | TasksConfig;
|
|
20
|
+
|
|
21
|
+
export interface SingleTasks extends BaseTask {
|
|
22
|
+
type: "single";
|
|
23
|
+
tasks: Task;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ParallelTasks extends BaseTask {
|
|
27
|
+
type: "parallel";
|
|
28
|
+
tasks: Task[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface QueueTasks extends BaseTask {
|
|
32
|
+
type: "queue";
|
|
33
|
+
tasks: Task[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type TasksConfig = SingleTasks | ParallelTasks | QueueTasks;
|
|
37
|
+
|
|
38
|
+
export type PromiseTasksConfig = TasksConfig;
|
|
39
|
+
|
|
40
|
+
function isSingleTasks(config: TasksConfig): config is SingleTasks {
|
|
41
|
+
return config.type === "single";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function isParallelTasks(config: TasksConfig): config is ParallelTasks {
|
|
45
|
+
return config.type === "parallel";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isQueueTasks(config: TasksConfig): config is QueueTasks {
|
|
49
|
+
return config.type === "queue";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function isSimpleAsyncTask(config: Task): config is SimpleAsyncTask {
|
|
53
|
+
return typeof config === "function";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function isTasksConfig(config: Task): config is TasksConfig {
|
|
57
|
+
return typeof config === "object";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 定义异步任务对象
|
|
62
|
+
* @description
|
|
63
|
+
* 这个对象是一揽子异步任务的配置
|
|
64
|
+
*/
|
|
65
|
+
export function definePromiseTasks(config: TasksConfig) {
|
|
66
|
+
return config;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @private 一个工具函数 用于生成异步函数数组
|
|
71
|
+
* @deprecated 在处理串行任务时 疑似有故障
|
|
72
|
+
*/
|
|
73
|
+
function getPromises(tasks: Task[]): ((...args: any) => Promise<any>)[] {
|
|
74
|
+
return tasks.map((task) => {
|
|
75
|
+
return async function (...args: any) {
|
|
76
|
+
if (isSimpleAsyncTask(task)) {
|
|
77
|
+
return await task(...args);
|
|
78
|
+
} else {
|
|
79
|
+
return await executePromiseTasks(task);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 执行异步函数对象
|
|
87
|
+
*/
|
|
88
|
+
export async function executePromiseTasks(
|
|
89
|
+
config: TasksConfig,
|
|
90
|
+
/**
|
|
91
|
+
* 上一次递归执行时提供的参数
|
|
92
|
+
* @description
|
|
93
|
+
* 考虑到递归函数 这里提供了一个参数 用于传递上一次递归执行的结果
|
|
94
|
+
*/
|
|
95
|
+
lastParams: any = null,
|
|
96
|
+
): Promise<any> {
|
|
97
|
+
if (isSingleTasks(config)) {
|
|
98
|
+
if (isSimpleAsyncTask(config.tasks)) {
|
|
99
|
+
// 实际执行的 tasks 往往是无参函数 这里为了保险,故主动传递参数
|
|
100
|
+
return await config.tasks(lastParams);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return await executePromiseTasks(config.tasks, lastParams);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (isParallelTasks(config)) {
|
|
107
|
+
return await Promise.all(
|
|
108
|
+
config.tasks.map((task) => {
|
|
109
|
+
if (isSimpleAsyncTask(task)) {
|
|
110
|
+
// console.log(` 并行任务遇到单独的异步函数 `);
|
|
111
|
+
return task(lastParams);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// console.log(` 并行任务遇到嵌套结构 `);
|
|
115
|
+
return executePromiseTasks(task, lastParams);
|
|
116
|
+
}),
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (isQueueTasks(config)) {
|
|
121
|
+
let res: Awaited<any>;
|
|
122
|
+
for await (const task of config.tasks) {
|
|
123
|
+
if (isSimpleAsyncTask(task)) {
|
|
124
|
+
// console.log(` 串行任务遇到单独的异步函数 `);
|
|
125
|
+
|
|
126
|
+
res = await task(lastParams);
|
|
127
|
+
lastParams = res;
|
|
128
|
+
console.log(` 串行任务 单独 res `, res);
|
|
129
|
+
} else {
|
|
130
|
+
res = await executePromiseTasks(task, lastParams);
|
|
131
|
+
lastParams = res;
|
|
132
|
+
console.log(` 串行任务 配置 res `, res);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return res;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { consola } from "consola";
|
|
2
|
+
import { uniqueId } from "lodash-es";
|
|
3
|
+
|
|
4
|
+
export function wait(time: number) {
|
|
5
|
+
return new Promise<void>((resolve) => {
|
|
6
|
+
setTimeout(() => {
|
|
7
|
+
resolve();
|
|
8
|
+
}, time);
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const getCounter = () => uniqueId();
|
|
13
|
+
|
|
14
|
+
/** 创建简单的异步任务 */
|
|
15
|
+
export function generateSimpleAsyncTask<T extends (...args: any) => any>(func: T) {
|
|
16
|
+
const taskId = getCounter();
|
|
17
|
+
|
|
18
|
+
return function (...args: any) {
|
|
19
|
+
// consola.info(` 这是第 ${taskId} 个异步任务 `);
|
|
20
|
+
// consola.start(" 这里是新创建的异步函数 检查参数: ", ...args);
|
|
21
|
+
|
|
22
|
+
return new Promise<ReturnType<T>>((resolve, reject) => {
|
|
23
|
+
// consola.start(" 内部promise 检查参数: ", ...args);
|
|
24
|
+
resolve(func(...args));
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type SimpleAsyncTask = ReturnType<typeof generateSimpleAsyncTask>;
|
|
30
|
+
|
|
31
|
+
/** @deprecated */
|
|
32
|
+
export type SimpleAsyncTaskWithType = <T = any>(...args: any) => Promise<T>;
|
|
33
|
+
|
|
34
|
+
export const initFlag = <const>"initFlag";
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 以队列串行的形式 串行运行异步函数
|
|
38
|
+
* @see https://github.com/ascoders/weekly/blob/master/前沿技术/77.精读《用%20Reduce%20实现%20Promise%20串行执行》.md
|
|
39
|
+
* @version 1
|
|
40
|
+
*/
|
|
41
|
+
async function runPromiseByQueueV1<T>(promises: ((...args: any) => Promise<T>)[]) {
|
|
42
|
+
promises.reduce(
|
|
43
|
+
async function (previousPromise, nextPromise, currentIndex) {
|
|
44
|
+
const response = await previousPromise;
|
|
45
|
+
// consola.log(` reduce串行函数 currentIndex= ${currentIndex} res =`, response);
|
|
46
|
+
return await nextPromise(response);
|
|
47
|
+
},
|
|
48
|
+
Promise.resolve(initFlag) as Promise<any>,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 以队列串行的形式 串行运行异步函数
|
|
54
|
+
* @version 2
|
|
55
|
+
*/
|
|
56
|
+
export async function runPromiseByQueue<T>(promises: ((...args: any) => Promise<T>)[]) {
|
|
57
|
+
let response: typeof initFlag | Awaited<T> = initFlag;
|
|
58
|
+
for await (const promise of promises) {
|
|
59
|
+
response = await promise(response);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 以并行的形式 并发运行异步函数
|
|
65
|
+
*/
|
|
66
|
+
export async function runPromiseByConcurrency<T>(promises: ((...args: any) => Promise<T>)[]) {
|
|
67
|
+
await Promise.all(promises.map((promise) => promise()));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const testPromises = [
|
|
71
|
+
generateSimpleAsyncTask(async function (params) {
|
|
72
|
+
await wait(400);
|
|
73
|
+
consola.log(" 这里是 1 号函数 ");
|
|
74
|
+
consola.log(" 查看上一个函数返回过来的参数: ", params);
|
|
75
|
+
return 1;
|
|
76
|
+
}),
|
|
77
|
+
|
|
78
|
+
generateSimpleAsyncTask(async function (params) {
|
|
79
|
+
await wait(500);
|
|
80
|
+
consola.log(" 这里是 2 号函数 ");
|
|
81
|
+
consola.log(" 查看上一个函数返回过来的参数: ", params);
|
|
82
|
+
return 2;
|
|
83
|
+
}),
|
|
84
|
+
|
|
85
|
+
generateSimpleAsyncTask(async function (params) {
|
|
86
|
+
await wait(500);
|
|
87
|
+
consola.log(" 这里是 3 号函数 ");
|
|
88
|
+
consola.log(" 查看上一个函数返回过来的参数: ", params);
|
|
89
|
+
return 3;
|
|
90
|
+
}),
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
// 测试队列函数的传参能力 发现这里是可以实现传参的
|
|
94
|
+
// runPromiseByQueue(testPromises);
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"target": "ESNext",
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"strict": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"resolveJsonModule": true,
|
|
10
|
+
"moduleResolution": "node",
|
|
11
|
+
// "rootDir": "./",
|
|
12
|
+
// 不能这样配置 导致路径识别错乱
|
|
13
|
+
// "rootDirs": [
|
|
14
|
+
// "src",
|
|
15
|
+
// "tests"
|
|
16
|
+
// ],
|
|
17
|
+
"baseUrl": ".",
|
|
18
|
+
"rootDir": ".",
|
|
19
|
+
"paths": {
|
|
20
|
+
"@/*": [
|
|
21
|
+
"src/*"
|
|
22
|
+
],
|
|
23
|
+
"src/*": [
|
|
24
|
+
"src/*"
|
|
25
|
+
],
|
|
26
|
+
"tests/*": [
|
|
27
|
+
"tests/*"
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"include": [
|
|
32
|
+
// "src/**/*.ts",
|
|
33
|
+
// "tests/**/*.test.ts",
|
|
34
|
+
// "tests/**/*.ts"
|
|
35
|
+
"src",
|
|
36
|
+
"tests"
|
|
37
|
+
]
|
|
38
|
+
}
|