@seayoo-web/scripts 3.1.11 → 4.0.0

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/dist/node.js CHANGED
@@ -1,649 +1,581 @@
1
- import { readFileSync, readdirSync, statSync, existsSync, rmSync, mkdirSync, writeFileSync } from "fs";
2
- import "colors";
1
+ import { _ as removeProjectFromWorkspace, a as submitAllDeletionChanges, c as copyTemplate, d as getMonorepoRoot, g as isPlainObject, h as getTemplates, l as deepMerge, m as getRepos, n as createBackupTag, o as addProjectToWorkspace, p as getProjects, s as copyGlobalFiles, t as checkGitStatusBeforeDestroy, u as format } from "./git-C1rpf4Ed.js";
2
+ import { finderDeploy, finderUpload, finderUpload as finderUpload$1 } from "@seayoo-web/finder";
3
+ import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "fs";
3
4
  import path, { resolve } from "path";
4
- import { Command } from "commander";
5
5
  import fs from "fs-extra";
6
+ import { Command } from "commander";
6
7
  import inquirer from "inquirer";
7
8
  import ora from "ora";
8
- import { b as getMonorepoRoot, d as addProjectToWorkspace, e as copyTemplate, f as copyGlobalFiles, h as getProjects, i as getTemplates, j as checkGitStatusBeforeDestroy, k as createBackupTag, r as removeProjectFromWorkspace, s as submitAllDeletionChanges, l as getRepos, m as isPlainObject, n as format, o as deepMerge } from "./git-B0Cnp5wC.js";
9
- import { finderUpload } from "@seayoo-web/finder";
10
- import { finderDeploy, finderUpload as finderUpload2 } from "@seayoo-web/finder";
11
9
  import { createServer } from "http";
12
- import chokidar from "chokidar";
10
+ import { watch } from "chokidar";
13
11
  import { minify } from "html-minifier-terser";
14
- const commitRE = /^(?:revert: )?(?:feat|fix|docs|style|refactor|test|build|chore|debug|tweak|improve)(?:\(.+\))?: .{1,100}/;
12
+ //#region src/commit.ts
13
+ var commitRE = /^(?:revert: )?(?:feat|fix|docs|style|refactor|test|build|chore|debug|tweak|improve)(?:\(.+\))?: .{1,100}/;
15
14
  function checkCommit() {
16
- const msgPath = process.argv[2];
17
- const msg = readFileSync(msgPath, "utf-8").trim();
18
- if (!commitRE.test(msg) && !msg.startsWith("Merge branch")) {
19
- console.error(`ERROR!【提交的 Commit 信息格式错误,请注意使用正确的类型前缀!】`.red);
20
- process.exit(1);
21
- }
15
+ const msgPath = process.argv[2];
16
+ const msg = readFileSync(msgPath, "utf-8").trim();
17
+ if (!commitRE.test(msg) && !msg.startsWith("Merge branch")) {
18
+ console.error(`ERROR!【提交的 Commit 信息格式错误,请注意使用正确的类型前缀!】`.red);
19
+ process.exit(1);
20
+ }
22
21
  }
23
- const program$1 = new Command();
22
+ //#endregion
23
+ //#region src/create.ts
24
+ var program$1 = new Command();
24
25
  program$1.version("1.0.0").argument("<target>");
26
+ /** 启动脚本 */
25
27
  function runCreateScript() {
26
- program$1.parse(process.argv);
28
+ program$1.parse(process.argv);
27
29
  }
28
30
  async function initRepoQuestions$1() {
29
- const projects = await getProjects();
30
- const templates = await getTemplates();
31
- return [
32
- {
33
- type: "list",
34
- //@ts-expect-error known issue
35
- name: "project",
36
- message: "选择项目:",
37
- choices: projects.map((project) => ({
38
- name: `${project.id} ${project.name}`,
39
- short: project.id,
40
- value: project.id
41
- }))
42
- },
43
- {
44
- type: "list",
45
- //@ts-expect-error known issue
46
- name: "template",
47
- message: "选择模板:",
48
- choices: templates.map((template) => ({
49
- name: `${template.id} ${template.desc}`,
50
- short: template.id,
51
- value: template.id
52
- }))
53
- },
54
- {
55
- type: "input",
56
- //@ts-expect-error known issue
57
- name: "repoName",
58
- message: "仓库名称:",
59
- validate: (input) => {
60
- if (/^[a-z0-9-]+$/.test(input)) return true;
61
- return "仓库名称只能包含小写字母、数字和中横线";
62
- }
63
- },
64
- {
65
- type: "input",
66
- message: "仓库描述:",
67
- default: ""
68
- }
69
- ];
31
+ const projects = await getProjects();
32
+ const templates = await getTemplates();
33
+ return [
34
+ {
35
+ type: "list",
36
+ name: "project",
37
+ message: "选择项目:",
38
+ choices: projects.map((project) => ({
39
+ name: `${project.id} ${project.name}`,
40
+ short: project.id,
41
+ value: project.id
42
+ }))
43
+ },
44
+ {
45
+ type: "list",
46
+ name: "template",
47
+ message: "选择模板:",
48
+ choices: templates.map((template) => ({
49
+ name: `${template.id} ${template.desc}`,
50
+ short: template.id,
51
+ value: template.id
52
+ }))
53
+ },
54
+ {
55
+ type: "input",
56
+ name: "repoName",
57
+ message: "仓库名称:",
58
+ validate: (input) => {
59
+ if (/^[a-z0-9-]+$/.test(input)) return true;
60
+ return "仓库名称只能包含小写字母、数字和中横线";
61
+ }
62
+ },
63
+ {
64
+ type: "input",
65
+ message: "仓库描述:",
66
+ default: ""
67
+ }
68
+ ];
70
69
  }
71
70
  async function initProjectQuestions$1() {
72
- const projects = (await getProjects(true)).map((project) => project.id);
73
- return [
74
- {
75
- type: "input",
76
- message: "项目 ID:",
77
- //@ts-expect-error known issue
78
- name: "project",
79
- validate: (input) => {
80
- if (!/^[a-zA-Z0-9-_]+$/.test(input)) {
81
- return "项目名称只能包含字母、数字、下划线和横线";
82
- }
83
- if (projects.includes(input)) {
84
- return "项目已经存在,请检查";
85
- }
86
- return true;
87
- }
88
- },
89
- {
90
- type: "input",
91
- message: "中文名称:",
92
- //@ts-expect-error known issue
93
- name: "name",
94
- validate: (input) => {
95
- if (!input) {
96
- return "中文名称不能为空";
97
- }
98
- return true;
99
- }
100
- }
101
- ];
71
+ const projects = (await getProjects(true)).map((project) => project.id);
72
+ return [{
73
+ type: "input",
74
+ message: "项目 ID:",
75
+ name: "project",
76
+ validate: (input) => {
77
+ if (!/^[a-zA-Z0-9-_]+$/.test(input)) return "项目名称只能包含字母、数字、下划线和横线";
78
+ if (projects.includes(input)) return "项目已经存在,请检查";
79
+ return true;
80
+ }
81
+ }, {
82
+ type: "input",
83
+ message: "中文名称:",
84
+ name: "name",
85
+ validate: (input) => {
86
+ if (!input) return "中文名称不能为空";
87
+ return true;
88
+ }
89
+ }];
102
90
  }
91
+ /**
92
+ * 更新 package.json / env.local 等文件
93
+ */
103
94
  async function updatePackageFiles(targetDir, answers) {
104
- await Promise.all([
105
- // 初始化 package.json
106
- updatePackageJson(targetDir, answers),
107
- // 初始化样例文件
108
- initSampleFiles(targetDir)
109
- ]);
95
+ await Promise.all([updatePackageJson(targetDir, answers), initSampleFiles(targetDir)]);
110
96
  }
111
97
  async function updatePackageJson(targetDir, answers) {
112
- const pkgPath = path.join(targetDir, "package.json");
113
- if (await fs.pathExists(pkgPath)) {
114
- const pkg = await fs.readJson(pkgPath);
115
- pkg.name = `@${answers.project}/${answers.repoName}`;
116
- pkg.description = answers.description || pkg.description;
117
- pkg.version = "1.0.0";
118
- await fs.writeJson(pkgPath, pkg, { spaces: 2 });
119
- }
98
+ const pkgPath = path.join(targetDir, "package.json");
99
+ if (await fs.pathExists(pkgPath)) {
100
+ const pkg = await fs.readJson(pkgPath);
101
+ pkg.name = `@${answers.project}/${answers.repoName}`;
102
+ pkg.description = answers.description || pkg.description;
103
+ pkg.version = "1.0.0";
104
+ await fs.writeJson(pkgPath, pkg, { spaces: 2 });
105
+ }
120
106
  }
121
107
  async function initSampleFiles(targetDir) {
122
- const items = await fs.readdir(targetDir);
123
- const files = items.filter(
124
- (item) => !fs.statSync(path.join(targetDir, item)).isDirectory() && item.endsWith(".sample")
125
- );
126
- for (const filename of files) {
127
- const sample = path.join(targetDir, filename);
128
- if (await fs.pathExists(sample)) {
129
- await fs.writeFile(path.join(targetDir, filename.replace(/\.sample$/, "")), await fs.readFile(sample));
130
- fs.unlink(sample);
131
- }
132
- }
108
+ const files = (await fs.readdir(targetDir)).filter((item) => !fs.statSync(path.join(targetDir, item)).isDirectory() && item.endsWith(".sample"));
109
+ for (const filename of files) {
110
+ const sample = path.join(targetDir, filename);
111
+ if (await fs.pathExists(sample)) {
112
+ await fs.writeFile(path.join(targetDir, filename.replace(/\.sample$/, "")), await fs.readFile(sample));
113
+ fs.unlink(sample);
114
+ }
115
+ }
133
116
  }
117
+ /**
118
+ * 创建工程或项目
119
+ */
134
120
  async function startCreateJob$1(target) {
135
- if (target === "project") {
136
- await createProject();
137
- } else {
138
- await createRepo();
139
- }
121
+ if (target === "project") await createProject();
122
+ else await createRepo();
140
123
  }
141
124
  async function createProject() {
142
- const root = await getMonorepoRoot();
143
- const questions = await initProjectQuestions$1();
144
- const answers = await inquirer.prompt(questions);
145
- const spinner = ora("正在创建项目...").start();
146
- const projectDir = path.join(root, answers.project);
147
- await fs.mkdir(projectDir);
148
- await fs.writeFile(path.join(projectDir, ".name"), answers.name);
149
- await addProjectToWorkspace(answers.project);
150
- spinner.succeed("项目创建成功!".green);
125
+ const root = await getMonorepoRoot();
126
+ const questions = await initProjectQuestions$1();
127
+ const answers = await inquirer.prompt(questions);
128
+ const spinner = ora("正在创建项目...").start();
129
+ const projectDir = path.join(root, answers.project);
130
+ await fs.mkdir(projectDir);
131
+ await fs.writeFile(path.join(projectDir, ".name"), answers.name);
132
+ await addProjectToWorkspace(answers.project);
133
+ spinner.succeed("项目创建成功!".green);
151
134
  }
152
135
  async function createRepo() {
153
- const root = await getMonorepoRoot();
154
- const questions = await initRepoQuestions$1();
155
- const answers = await inquirer.prompt(questions);
156
- const spinner = ora("正在创建仓库...").start();
157
- const projectDir = path.join(root, answers.project);
158
- spinner.text = "检查目录...";
159
- if (!await fs.pathExists(projectDir)) {
160
- fs.mkdirSync(projectDir, { recursive: true });
161
- }
162
- const targetDir = path.join(root, answers.project, answers.repoName);
163
- if (await fs.pathExists(targetDir)) {
164
- spinner.fail(`错误:目录(${`${answers.project}/${answers.repoName}`})已存在`.red);
165
- process.exit(1);
166
- }
167
- spinner.text = "复制模板文件...";
168
- await copyTemplate(answers.template, targetDir);
169
- spinner.text = "更新 package.json...";
170
- await updatePackageFiles(targetDir, answers);
171
- spinner.text = "复制全局文件...";
172
- await copyGlobalFiles(targetDir);
173
- spinner.succeed("仓库创建成功!".green);
174
- console.log("\n接下来你可以:");
175
- console.log(`cd ${answers.project}/${answers.repoName}`.cyan);
176
- console.log("pnpm i".cyan);
177
- console.log("pnpm dev".cyan);
136
+ const root = await getMonorepoRoot();
137
+ const questions = await initRepoQuestions$1();
138
+ const answers = await inquirer.prompt(questions);
139
+ const spinner = ora("正在创建仓库...").start();
140
+ const projectDir = path.join(root, answers.project);
141
+ spinner.text = "检查目录...";
142
+ if (!await fs.pathExists(projectDir)) fs.mkdirSync(projectDir, { recursive: true });
143
+ const targetDir = path.join(root, answers.project, answers.repoName);
144
+ if (await fs.pathExists(targetDir)) {
145
+ spinner.fail(`错误:目录(${`${answers.project}/${answers.repoName}`})已存在`.red);
146
+ process.exit(1);
147
+ }
148
+ spinner.text = "复制模板文件...";
149
+ await copyTemplate(answers.template, targetDir);
150
+ spinner.text = "更新 package.json...";
151
+ await updatePackageFiles(targetDir, answers);
152
+ spinner.text = "复制全局文件...";
153
+ await copyGlobalFiles(targetDir);
154
+ spinner.succeed("仓库创建成功!".green);
155
+ console.log("\n接下来你可以:");
156
+ console.log(`cd ${answers.project}/${answers.repoName}`.cyan);
157
+ console.log("pnpm i".cyan);
158
+ console.log("pnpm dev".cyan);
178
159
  }
179
160
  program$1.action(startCreateJob$1);
180
- const program = new Command();
161
+ //#endregion
162
+ //#region src/destroy.ts
163
+ var program = new Command();
181
164
  program.version("1.0.0").argument("<target>");
165
+ /** 启动脚本 */
182
166
  function runDestroyScript() {
183
- program.parse(process.argv);
167
+ program.parse(process.argv);
184
168
  }
169
+ /**
170
+ * 销毁工程或项目
171
+ */
185
172
  async function startCreateJob(target) {
186
- if (target === "project") {
187
- await destroyProject();
188
- } else {
189
- await destroyRepo();
190
- }
173
+ if (target === "project") await destroyProject();
174
+ else await destroyRepo();
191
175
  }
192
176
  async function initProjectQuestions() {
193
- const projects = await getProjects();
194
- return [
195
- {
196
- type: "list",
197
- name: "project",
198
- message: "删除项目:",
199
- choices: projects.map((project) => ({
200
- name: `${project.id} ${project.name}`,
201
- short: project.id,
202
- value: project.id
203
- }))
204
- },
205
- {
206
- type: "confirm",
207
- name: "confirm",
208
- message: function(answers) {
209
- return `确认删除整个 ${answers.project} 项目 ?`;
210
- }
211
- }
212
- ];
177
+ return [{
178
+ type: "list",
179
+ name: "project",
180
+ message: "删除项目:",
181
+ choices: (await getProjects()).map((project) => ({
182
+ name: `${project.id} ${project.name}`,
183
+ short: project.id,
184
+ value: project.id
185
+ }))
186
+ }, {
187
+ type: "confirm",
188
+ name: "confirm",
189
+ message: function(answers) {
190
+ return `确认删除整个 ${answers.project} 项目 ?`;
191
+ }
192
+ }];
213
193
  }
214
194
  async function destroyProject() {
215
- const root = await getMonorepoRoot();
216
- await checkGitStatusBeforeDestroy();
217
- const questions = await initProjectQuestions();
218
- const answers = await inquirer.prompt(questions);
219
- if (!answers.confirm) {
220
- process.exit(0);
221
- }
222
- const spinner = ora("正在创建 Backup Tag...").start();
223
- await createBackupTag("project", answers.project);
224
- spinner.text = "开始清理文件...";
225
- const projectDir = path.join(root, answers.project);
226
- await fs.removeSync(projectDir);
227
- spinner.text = "调整配置文件...";
228
- await removeProjectFromWorkspace(answers.project);
229
- spinner.text = "提交代码中...";
230
- await submitAllDeletionChanges(`chore: destroy project ${answers.project}`);
231
- spinner.succeed("项目已经销毁!".green);
195
+ const root = await getMonorepoRoot();
196
+ await checkGitStatusBeforeDestroy();
197
+ const questions = await initProjectQuestions();
198
+ const answers = await inquirer.prompt(questions);
199
+ if (!answers.confirm) process.exit(0);
200
+ const spinner = ora("正在创建 Backup Tag...").start();
201
+ await createBackupTag("project", answers.project);
202
+ spinner.text = "开始清理文件...";
203
+ const projectDir = path.join(root, answers.project);
204
+ await fs.removeSync(projectDir);
205
+ spinner.text = "调整配置文件...";
206
+ await removeProjectFromWorkspace(answers.project);
207
+ spinner.text = "提交代码中...";
208
+ await submitAllDeletionChanges(`chore: destroy project ${answers.project}`);
209
+ spinner.succeed("项目已经销毁!".green);
232
210
  }
233
211
  async function initRepoQuestions() {
234
- const projects = await getProjects();
235
- return [
236
- {
237
- type: "list",
238
- name: "project",
239
- message: "所在项目:",
240
- choices: projects.map((project) => ({
241
- name: `${project.id} ${project.name}`,
242
- short: project.id,
243
- value: project.id
244
- }))
245
- },
246
- {
247
- type: "list",
248
- name: "page",
249
- message: "删除页面:",
250
- choices: async function(answers) {
251
- const repos = await getRepos(answers.project);
252
- if (repos.length === 0) {
253
- throw new Error("工程不含有任意有效 Repo!操作中止");
254
- }
255
- return repos.map((repo) => ({
256
- name: `${repo.id} ${repo.desc || ""}`,
257
- short: repo.id,
258
- value: repo.id
259
- }));
260
- }
261
- },
262
- {
263
- type: "confirm",
264
- name: "confirm",
265
- message: async function(answers) {
266
- return `确认删除页面 ${answers.project}/${answers.page} ?`;
267
- }
268
- }
269
- ];
212
+ return [
213
+ {
214
+ type: "list",
215
+ name: "project",
216
+ message: "所在项目:",
217
+ choices: (await getProjects()).map((project) => ({
218
+ name: `${project.id} ${project.name}`,
219
+ short: project.id,
220
+ value: project.id
221
+ }))
222
+ },
223
+ {
224
+ type: "list",
225
+ name: "page",
226
+ message: "删除页面:",
227
+ choices: async function(answers) {
228
+ const repos = await getRepos(answers.project);
229
+ if (repos.length === 0) throw new Error("工程不含有任意有效 Repo!操作中止");
230
+ return repos.map((repo) => ({
231
+ name: `${repo.id} ${repo.desc || ""}`,
232
+ short: repo.id,
233
+ value: repo.id
234
+ }));
235
+ }
236
+ },
237
+ {
238
+ type: "confirm",
239
+ name: "confirm",
240
+ message: async function(answers) {
241
+ return `确认删除页面 ${answers.project}/${answers.page} ?`;
242
+ }
243
+ }
244
+ ];
270
245
  }
271
246
  async function destroyRepo() {
272
- const root = await getMonorepoRoot();
273
- await checkGitStatusBeforeDestroy();
274
- const questions = await initRepoQuestions();
275
- const answers = await inquirer.prompt(questions);
276
- if (!answers.confirm) {
277
- process.exit(0);
278
- }
279
- const spinner = ora("正在创建 Backup Tag...").start();
280
- await createBackupTag("page", `${answers.project}/${answers.page}`);
281
- spinner.text = "开始清理文件...";
282
- const pageDir = path.join(root, answers.project, answers.page);
283
- fs.removeSync(pageDir);
284
- spinner.text = "提交代码中...";
285
- await submitAllDeletionChanges(`chore: destroy page ${answers.project}/${answers.page}`);
286
- spinner.succeed("页面已经销毁!".green);
247
+ const root = await getMonorepoRoot();
248
+ await checkGitStatusBeforeDestroy();
249
+ const questions = await initRepoQuestions();
250
+ const answers = await inquirer.prompt(questions);
251
+ if (!answers.confirm) process.exit(0);
252
+ const spinner = ora("正在创建 Backup Tag...").start();
253
+ await createBackupTag("page", `${answers.project}/${answers.page}`);
254
+ spinner.text = "开始清理文件...";
255
+ const pageDir = path.join(root, answers.project, answers.page);
256
+ fs.removeSync(pageDir);
257
+ spinner.text = "提交代码中...";
258
+ await submitAllDeletionChanges(`chore: destroy page ${answers.project}/${answers.page}`);
259
+ spinner.succeed("页面已经销毁!".green);
287
260
  }
288
261
  program.action(startCreateJob);
289
- const styleReg = /<style>.+?<\/style>/gi;
290
- const scriptReg = /<script>.+?<\/script>/gi;
291
- const scriptMetaReg = /<script meta>.+?<\/script>/gi;
292
- const htmlReg = /<template>.+?<\/template>/gi;
293
- class FooterBuilder {
294
- codeBaseDir;
295
- envFilePath;
296
- distDir;
297
- dataJsonFile;
298
- injectFunction;
299
- $groups;
300
- previewAfterDeploy;
301
- deployDebug;
302
- constructor({
303
- codeBaseDir,
304
- distDir,
305
- dataJsonFile,
306
- envFilePath,
307
- injectFunction,
308
- previewAfterDeploy,
309
- deployDebug
310
- }) {
311
- this.codeBaseDir = codeBaseDir;
312
- this.envFilePath = envFilePath;
313
- this.distDir = distDir;
314
- this.dataJsonFile = dataJsonFile;
315
- this.previewAfterDeploy = !!previewAfterDeploy;
316
- this.deployDebug = !!deployDebug;
317
- this.injectFunction = injectFunction.replace(/^function[^(]*\(/, "function ABC(");
318
- this.$groups = readdirSync(codeBaseDir).filter((name) => {
319
- return statSync(resolve(codeBaseDir, name)).isDirectory();
320
- });
321
- if (this.$groups.length === 0) {
322
- console.warn("⚠️没有找到组信息,请先创建组目录。");
323
- }
324
- if (existsSync(distDir)) {
325
- rmSync(distDir, { recursive: true });
326
- }
327
- for (const group of this.$groups) {
328
- mkdirSync(resolve(distDir, group), { recursive: true });
329
- }
330
- }
331
- get groups() {
332
- return this.$groups;
333
- }
334
- getDeployUserKey() {
335
- if (!existsSync(this.envFilePath)) {
336
- return null;
337
- }
338
- const config = readFileSync(this.envFilePath, "utf8").toString();
339
- const userMatch = config.match(/SY_DEPLOY_USER *= *([a-z\d]+)/);
340
- const keyMatch = config.match(/SY_DEPLOY_KEY *= *([a-z\d]+)/);
341
- return {
342
- user: (userMatch?.[1] || "").trim(),
343
- key: (keyMatch?.[1] || "").trim()
344
- };
345
- }
346
- getAllTemplates() {
347
- return this.$groups.map((group) => {
348
- const groupDir = resolve(this.codeBaseDir, group);
349
- return readdirSync(groupDir).filter((file) => file.endsWith(".html")).map((file) => `${group}/${file}`);
350
- }).flat();
351
- }
352
- getTemplateFile(group, template) {
353
- return `${group}/${template}.html`;
354
- }
355
- loadDefaultData() {
356
- const jsonText = readFileSync(this.dataJsonFile).toString();
357
- function removeCommit(json) {
358
- for (const key in json) {
359
- if (isPlainObject(json[key])) {
360
- removeCommit(json[key]);
361
- } else if (key.startsWith("-") || key.startsWith("+") || key.startsWith("#")) {
362
- delete json[key];
363
- }
364
- }
365
- return json;
366
- }
367
- try {
368
- return removeCommit(JSON.parse(jsonText));
369
- } catch (e) {
370
- console.log("配置数据加载失败", e);
371
- return {};
372
- }
373
- }
374
- formatMetaConfig(string) {
375
- try {
376
- const json = new Function(string.replace(/export default/, ";return "))();
377
- if (!isPlainObject(json)) {
378
- console.error("格式化 meta 信息错误,应该是一个简单对象");
379
- return null;
380
- }
381
- const keys = Object.keys(json);
382
- if (keys.length === 0) {
383
- console.error("格式化 meta 信息错误,对象不能为空");
384
- return null;
385
- }
386
- const result = [];
387
- for (const key of keys) {
388
- if (!key.includes("/public/footer/") && !key.includes("/snippets/")) {
389
- console.error(`deployTo (${key}) 错误, 必须包含路径 "/public/footer/" 或 "/snippets/"`);
390
- return null;
391
- }
392
- const config = json[key];
393
- if (!isPlainObject(config)) {
394
- console.error(`deployTo (${key}) 配置错误,应该是一个简单对象`);
395
- return null;
396
- }
397
- if (!("enabledIn" in config) || !Array.isArray(config.enabledIn) || config.enabledIn.length === 0 || config.enabledIn.some((x) => typeof x !== "string")) {
398
- console.error(`deployTo (${key}) 配置项 enabledIn 错误, 应该是一个包含域名的非空数组`);
399
- return null;
400
- }
401
- if ("payload" in config && !isPlainObject(config.payload)) {
402
- console.error(`deployTo (${key}) 配置项 payload 错误, 应该是一个对象`);
403
- return null;
404
- }
405
- result.push({
406
- deployTo: key,
407
- enabledIn: config.enabledIn,
408
- payload: isPlainObject(config.payload) ? config.payload : void 0
409
- });
410
- }
411
- return result;
412
- } catch (e) {
413
- console.error("Format Meta Config Error", e);
414
- return null;
415
- }
416
- }
417
- async buildEntry(template, payloadData) {
418
- if (!template.endsWith(".html")) {
419
- console.error(`${template} 不是 html 文件!`);
420
- return [];
421
- }
422
- const htmlRaw = readFileSync(resolve(this.codeBaseDir, template)).toString();
423
- const miniHtml = await minify(htmlRaw, {
424
- collapseWhitespace: true,
425
- removeComments: true,
426
- removeScriptTypeAttributes: true,
427
- removeStyleLinkTypeAttributes: true,
428
- sortAttributes: true,
429
- removeEmptyElements: true,
430
- minifyCSS: true,
431
- minifyJS: true
432
- });
433
- const style = (miniHtml.match(styleReg) || []).map((code) => code.slice(7, -8)).join("");
434
- const meta = (miniHtml.match(scriptMetaReg) || []).map((code) => code.slice(13, -9)).join(";");
435
- const script = (miniHtml.match(scriptReg) || []).map((code) => code.slice(8, -9)).join(";");
436
- const html = (miniHtml.match(htmlReg) || []).map((code) => code.slice(10, -11)).join("");
437
- if (!html) {
438
- console.log(`${template} html 数据配置错误,请检查。`);
439
- return [];
440
- }
441
- if (!style) {
442
- console.log(`${template} 的 style 数据配置错误,请检查。`);
443
- return [];
444
- }
445
- const metaConfig = this.formatMetaConfig(meta);
446
- if (!metaConfig) {
447
- console.log(`${template} 的 script meta 数据配置错误,请检查。`);
448
- return [];
449
- }
450
- const buildResult = [];
451
- const templateName = template.replace(/.+\/(.+)\.html$/, "$1");
452
- for (const config of metaConfig) {
453
- const formattedHTML = format(html, deepMerge({}, payloadData || {}, config.payload || {})).replace(/<img [^>]*?src=""[^>]+>/gi, "").replace(/<a [^>]*?href=""[^>]+>(.*?)<\/a>/gi, "$1").replace(/<p class="">/gi, "<p>").replace(/(?:<p>\s*<\/p>|<section>\s*<\/section>|<div>\s*<\/div>)/gi, "");
454
- buildResult.push({
455
- deployTo: config.deployTo,
456
- group: template.replace(/\/.*$/, ""),
457
- template: templateName,
458
- filename: config.deployTo.replace(/[\/.]/g, "-").replace(/\-js$/, ".js"),
459
- code: await this.combineToCode(config.enabledIn, style, script, formattedHTML)
460
- });
461
- }
462
- return buildResult;
463
- }
464
- async combineToCode(enabledIn, style, script, html) {
465
- const func = (await minify(`<script>${this.injectFunction}<\/script>`, {
466
- collapseWhitespace: true,
467
- removeComments: true,
468
- minifyJS: true
469
- })).slice(8, -9).replace(/function ABC/, "function");
470
- return `(${func})(${JSON.stringify(enabledIn)},${JSON.stringify(style)},${JSON.stringify(script)},${JSON.stringify(html)})`;
471
- }
472
- async upload(deployTo, code, user, key) {
473
- return await finderUpload({
474
- fileContent: code,
475
- deployTo,
476
- user,
477
- key,
478
- preview: this.previewAfterDeploy,
479
- debug: this.deployDebug
480
- }).then(() => null).catch((err) => err instanceof Error ? err : new Error(err));
481
- }
482
- /**
483
- * 核心构建方法,根据模板文件生成结果
484
- * @param templateFile 可选模板文件路径,若未提供则使用所有模板
485
- * @returns 返回构建结果数组
486
- */
487
- async coreBuild(templateFile) {
488
- const templateData = this.loadDefaultData();
489
- const templates = templateFile ? [templateFile.replace(/\\/g, "/")] : this.getAllTemplates();
490
- const result = [];
491
- for (const html of templates) {
492
- result.push(...await this.buildEntry(html, templateData));
493
- }
494
- return result;
495
- }
496
- /**
497
- * 构建模板并输出结果文件
498
- * @param templateFile - 模板文件路径
499
- */
500
- async buildAndOutput(templateFile) {
501
- const result = await this.coreBuild(templateFile);
502
- for (const { group, template, filename, code } of result) {
503
- mkdirSync(resolve(this.distDir, group, template), { recursive: true });
504
- writeFileSync(resolve(this.distDir, group, template, filename), code);
505
- }
506
- }
507
- /**
508
- * 监听代码目录变化并触发构建
509
- * @description 使用chokidar监控代码目录,当非目录添加事件发生时:
510
- * - 如果是.html文件,构建并输出相对路径
511
- * - 其他文件变更则触发全量构建
512
- */
513
- startFileWatch() {
514
- const src = this.codeBaseDir;
515
- chokidar.watch(src).on("all", (event, path2) => {
516
- if (event === "addDir") {
517
- return;
518
- }
519
- const file = path2.startsWith(src) ? path2.slice(src.length + 1) : path2;
520
- if (file.endsWith(".html")) {
521
- this.buildAndOutput(file.replace(/\\/g, "/"));
522
- } else {
523
- this.buildAndOutput();
524
- }
525
- });
526
- }
527
- /**
528
- * 启动部署服务器,监听指定端口
529
- * @param port 服务器监听端口,默认为7759
530
- * @description
531
- * 1. 处理跨域请求和预检OPTIONS请求
532
- * 2. 验证部署配置和用户权限
533
- * 3. 支持两种部署模式:
534
- * - 部署单个文件:匹配 /deploy/[group]/[template]/[filename] 路径
535
- * - 部署模板下所有文件:匹配 /deploy/[group]/[template] 路径
536
- * 4. 返回部署结果或错误信息
537
- */
538
- startDeployServer(port = 7759) {
539
- const server = createServer(async (req, res) => {
540
- const cosHeader = {
541
- "Access-Control-Allow-Origin": req.headers.origin || "https://127.0.0.1",
542
- "Access-Control-Allow-Methods": "GET, OPTIONS",
543
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
544
- "Access-Control-Max-Age": "86400"
545
- };
546
- const url = (req.url || "").trim();
547
- const method = (req.method || "get").toLowerCase();
548
- if (method === "options") {
549
- res.writeHead(200, cosHeader);
550
- res.end();
551
- return;
552
- }
553
- const templateDeployInfo = url.match(/deploy\/([^/]+)\/([^/]+)\/?\?target=(.+)$/);
554
- if (!templateDeployInfo) {
555
- res.writeHead(404, cosHeader);
556
- res.write("参数错误,应该是 group/template?target=xxx");
557
- res.end();
558
- return;
559
- }
560
- const user = this.getDeployUserKey();
561
- const group = templateDeployInfo[1];
562
- const template = this.getTemplateFile(templateDeployInfo[1], templateDeployInfo[2]);
563
- const target = templateDeployInfo[3];
564
- if (!user) {
565
- res.writeHead(403, cosHeader);
566
- res.write("请先在根目录添加 .env.local 文件");
567
- res.end();
568
- return;
569
- }
570
- if (!user.user) {
571
- res.writeHead(403, cosHeader);
572
- res.write("请设置 .env.local 文件中 SY_DEPLOY_USER");
573
- res.end();
574
- return;
575
- }
576
- if (!user.key) {
577
- res.writeHead(403, cosHeader);
578
- res.write("请设置 .env.local 文件中 SY_DEPLOY_KEY");
579
- res.end();
580
- return;
581
- }
582
- const allGroups = this.groups;
583
- if (!allGroups.includes(group)) {
584
- res.writeHead(403, cosHeader);
585
- res.write(`分组错误,没有 ${group}`);
586
- res.end();
587
- return;
588
- }
589
- const allTemplates = this.getAllTemplates();
590
- if (!allTemplates.includes(template)) {
591
- res.writeHead(403, cosHeader);
592
- res.write(`模板错误,没有 ${template}`);
593
- res.end();
594
- return;
595
- }
596
- if (target.endsWith(".js")) {
597
- const buildResult = await this.coreBuild(template);
598
- const data = buildResult.find((data2) => data2.deployTo === target);
599
- if (!data) {
600
- res.writeHead(504, cosHeader);
601
- res.write(`没有找到文件配置 ${target}`);
602
- res.end();
603
- return;
604
- }
605
- const err = await this.upload(data.deployTo, data.code, user?.user || "", user?.key || "");
606
- res.writeHead(200, { "Content-Type": "text/plain", ...cosHeader });
607
- res.write(err ? `部署失败 ${err.message}` : `部署完毕 ${data.deployTo}`);
608
- res.end();
609
- return;
610
- }
611
- if (target === "all") {
612
- const buildResult = await this.coreBuild(template);
613
- const success = [];
614
- const failed = [];
615
- for (const data of buildResult) {
616
- const err = await this.upload(data.deployTo, data.code, user?.user || "", user?.key || "");
617
- if (err) {
618
- failed.push(data.deployTo);
619
- } else {
620
- success.push(data.deployTo);
621
- }
622
- }
623
- res.writeHead(200, { "Content-Type": "text/plain", ...cosHeader });
624
- res.write(
625
- [
626
- success.length > 0 ? `部署成功
627
- ${success.join("\n")}` : "",
628
- failed.length > 0 ? `部署失败
629
- ${failed.join("\n")}` : ""
630
- ].join("\n")
631
- );
632
- res.end();
633
- return;
634
- }
635
- res.writeHead(400, { "Content-Type": "text/plain", ...cosHeader });
636
- res.write("参数错误");
637
- res.end();
638
- });
639
- server.listen(port);
640
- }
641
- }
642
- export {
643
- FooterBuilder,
644
- checkCommit,
645
- finderDeploy,
646
- finderUpload2 as finderUpload,
647
- runCreateScript,
648
- runDestroyScript
262
+ //#endregion
263
+ //#region src/footer.ts
264
+ var styleReg = /<style>.+?<\/style>/gi;
265
+ var scriptReg = /<script>.+?<\/script>/gi;
266
+ var scriptMetaReg = /<script meta>.+?<\/script>/gi;
267
+ var htmlReg = /<template>.+?<\/template>/gi;
268
+ var FooterBuilder = class {
269
+ codeBaseDir;
270
+ envFilePath;
271
+ distDir;
272
+ dataJsonFile;
273
+ injectFunction;
274
+ $groups;
275
+ previewAfterDeploy;
276
+ deployDebug;
277
+ constructor({ codeBaseDir, distDir, dataJsonFile, envFilePath, injectFunction, previewAfterDeploy, deployDebug }) {
278
+ this.codeBaseDir = codeBaseDir;
279
+ this.envFilePath = envFilePath;
280
+ this.distDir = distDir;
281
+ this.dataJsonFile = dataJsonFile;
282
+ this.previewAfterDeploy = !!previewAfterDeploy;
283
+ this.deployDebug = !!deployDebug;
284
+ this.injectFunction = injectFunction.replace(/^function[^(]*\(/, "function ABC(");
285
+ this.$groups = readdirSync(codeBaseDir).filter((name) => {
286
+ return statSync(resolve(codeBaseDir, name)).isDirectory();
287
+ });
288
+ if (this.$groups.length === 0) console.warn("⚠️没有找到组信息,请先创建组目录。");
289
+ if (existsSync(distDir)) rmSync(distDir, { recursive: true });
290
+ for (const group of this.$groups) mkdirSync(resolve(distDir, group), { recursive: true });
291
+ }
292
+ get groups() {
293
+ return this.$groups;
294
+ }
295
+ getDeployUserKey() {
296
+ if (!existsSync(this.envFilePath)) return null;
297
+ const config = readFileSync(this.envFilePath, "utf8").toString();
298
+ const userMatch = config.match(/SY_DEPLOY_USER *= *([a-z\d]+)/);
299
+ const keyMatch = config.match(/SY_DEPLOY_KEY *= *([a-z\d]+)/);
300
+ return {
301
+ user: (userMatch?.[1] || "").trim(),
302
+ key: (keyMatch?.[1] || "").trim()
303
+ };
304
+ }
305
+ getAllTemplates() {
306
+ return this.$groups.map((group) => {
307
+ return readdirSync(resolve(this.codeBaseDir, group)).filter((file) => file.endsWith(".html")).map((file) => `${group}/${file}`);
308
+ }).flat();
309
+ }
310
+ getTemplateFile(group, template) {
311
+ return `${group}/${template}.html`;
312
+ }
313
+ loadDefaultData() {
314
+ const jsonText = readFileSync(this.dataJsonFile).toString();
315
+ function removeCommit(json) {
316
+ for (const key in json) if (isPlainObject(json[key])) removeCommit(json[key]);
317
+ else if (key.startsWith("-") || key.startsWith("+") || key.startsWith("#")) delete json[key];
318
+ return json;
319
+ }
320
+ try {
321
+ return removeCommit(JSON.parse(jsonText));
322
+ } catch (e) {
323
+ console.log("配置数据加载失败", e);
324
+ return {};
325
+ }
326
+ }
327
+ formatMetaConfig(string) {
328
+ try {
329
+ const json = new Function(string.replace(/export default/, ";return "))();
330
+ if (!isPlainObject(json)) {
331
+ console.error("格式化 meta 信息错误,应该是一个简单对象");
332
+ return null;
333
+ }
334
+ const keys = Object.keys(json);
335
+ if (keys.length === 0) {
336
+ console.error("格式化 meta 信息错误,对象不能为空");
337
+ return null;
338
+ }
339
+ const result = [];
340
+ for (const key of keys) {
341
+ if (!key.includes("/public/footer/") && !key.includes("/snippets/")) {
342
+ console.error(`deployTo (${key}) 错误, 必须包含路径 "/public/footer/" 或 "/snippets/"`);
343
+ return null;
344
+ }
345
+ const config = json[key];
346
+ if (!isPlainObject(config)) {
347
+ console.error(`deployTo (${key}) 配置错误,应该是一个简单对象`);
348
+ return null;
349
+ }
350
+ if (!("enabledIn" in config) || !Array.isArray(config.enabledIn) || config.enabledIn.length === 0 || config.enabledIn.some((x) => typeof x !== "string")) {
351
+ console.error(`deployTo (${key}) 配置项 enabledIn 错误, 应该是一个包含域名的非空数组`);
352
+ return null;
353
+ }
354
+ if ("payload" in config && !isPlainObject(config.payload)) {
355
+ console.error(`deployTo (${key}) 配置项 payload 错误, 应该是一个对象`);
356
+ return null;
357
+ }
358
+ result.push({
359
+ deployTo: key,
360
+ enabledIn: config.enabledIn,
361
+ payload: isPlainObject(config.payload) ? config.payload : void 0
362
+ });
363
+ }
364
+ return result;
365
+ } catch (e) {
366
+ console.error("Format Meta Config Error", e);
367
+ return null;
368
+ }
369
+ }
370
+ async buildEntry(template, payloadData) {
371
+ if (!template.endsWith(".html")) {
372
+ console.error(`${template} 不是 html 文件!`);
373
+ return [];
374
+ }
375
+ const miniHtml = await minify(readFileSync(resolve(this.codeBaseDir, template)).toString(), {
376
+ collapseWhitespace: true,
377
+ removeComments: true,
378
+ removeScriptTypeAttributes: true,
379
+ removeStyleLinkTypeAttributes: true,
380
+ sortAttributes: true,
381
+ removeEmptyElements: true,
382
+ minifyCSS: true,
383
+ minifyJS: true
384
+ });
385
+ const style = (miniHtml.match(styleReg) || []).map((code) => code.slice(7, -8)).join("");
386
+ const meta = (miniHtml.match(scriptMetaReg) || []).map((code) => code.slice(13, -9)).join(";");
387
+ const script = (miniHtml.match(scriptReg) || []).map((code) => code.slice(8, -9)).join(";");
388
+ const html = (miniHtml.match(htmlReg) || []).map((code) => code.slice(10, -11)).join("");
389
+ if (!html) {
390
+ console.log(`${template} 的 html 数据配置错误,请检查。`);
391
+ return [];
392
+ }
393
+ if (!style) {
394
+ console.log(`${template} 的 style 数据配置错误,请检查。`);
395
+ return [];
396
+ }
397
+ const metaConfig = this.formatMetaConfig(meta);
398
+ if (!metaConfig) {
399
+ console.log(`${template} 的 script meta 数据配置错误,请检查。`);
400
+ return [];
401
+ }
402
+ const buildResult = [];
403
+ const templateName = template.replace(/.+\/(.+)\.html$/, "$1");
404
+ for (const config of metaConfig) {
405
+ const formattedHTML = format(html, deepMerge({}, payloadData || {}, config.payload || {})).replace(/<img [^>]*?src=""[^>]+>/gi, "").replace(/<a [^>]*?href=""[^>]+>(.*?)<\/a>/gi, "$1").replace(/<p class="">/gi, "<p>").replace(/(?:<p>\s*<\/p>|<section>\s*<\/section>|<div>\s*<\/div>)/gi, "");
406
+ buildResult.push({
407
+ deployTo: config.deployTo,
408
+ group: template.replace(/\/.*$/, ""),
409
+ template: templateName,
410
+ filename: config.deployTo.replace(/[/.]/g, "-").replace(/-js$/, ".js"),
411
+ code: await this.combineToCode(config.enabledIn, style, script, formattedHTML)
412
+ });
413
+ }
414
+ return buildResult;
415
+ }
416
+ async combineToCode(enabledIn, style, script, html) {
417
+ return `(${(await minify(`<script>${this.injectFunction}<\/script>`, {
418
+ collapseWhitespace: true,
419
+ removeComments: true,
420
+ minifyJS: true
421
+ })).slice(8, -9).replace(/function ABC/, "function")})(${JSON.stringify(enabledIn)},${JSON.stringify(style)},${JSON.stringify(script)},${JSON.stringify(html)})`;
422
+ }
423
+ async upload(deployTo, code, user, key) {
424
+ return await finderUpload$1({
425
+ fileContent: code,
426
+ deployTo,
427
+ user,
428
+ key,
429
+ preview: this.previewAfterDeploy,
430
+ debug: this.deployDebug
431
+ }).then(() => null).catch((err) => err instanceof Error ? err : new Error(err));
432
+ }
433
+ /**
434
+ * 核心构建方法,根据模板文件生成结果
435
+ * @param templateFile 可选模板文件路径,若未提供则使用所有模板
436
+ * @returns 返回构建结果数组
437
+ */
438
+ async coreBuild(templateFile) {
439
+ const templateData = this.loadDefaultData();
440
+ const templates = templateFile ? [templateFile.replace(/\\/g, "/")] : this.getAllTemplates();
441
+ return (await Promise.all(templates.map((html) => this.buildEntry(html, templateData)))).flat();
442
+ }
443
+ /**
444
+ * 构建模板并输出结果文件
445
+ * @param templateFile - 模板文件路径
446
+ */
447
+ async buildAndOutput(templateFile) {
448
+ const result = await this.coreBuild(templateFile);
449
+ for (const { group, template, filename, code } of result) {
450
+ mkdirSync(resolve(this.distDir, group, template), { recursive: true });
451
+ writeFileSync(resolve(this.distDir, group, template, filename), code);
452
+ }
453
+ }
454
+ /**
455
+ * 监听代码目录变化并触发构建
456
+ * @description 使用chokidar监控代码目录,当非目录添加事件发生时:
457
+ * - 如果是.html文件,构建并输出相对路径
458
+ * - 其他文件变更则触发全量构建
459
+ */
460
+ startFileWatch() {
461
+ const src = this.codeBaseDir;
462
+ watch(src).on("all", (event, path) => {
463
+ if (event === "addDir") return;
464
+ const file = path.startsWith(src) ? path.slice(src.length + 1) : path;
465
+ if (file.endsWith(".html")) this.buildAndOutput(file.replace(/\\/g, "/"));
466
+ else this.buildAndOutput();
467
+ });
468
+ }
469
+ /**
470
+ * 启动部署服务器,监听指定端口
471
+ * @param port 服务器监听端口,默认为7759
472
+ * @description
473
+ * 1. 处理跨域请求和预检OPTIONS请求
474
+ * 2. 验证部署配置和用户权限
475
+ * 3. 支持两种部署模式:
476
+ * - 部署单个文件:匹配 /deploy/[group]/[template]/[filename] 路径
477
+ * - 部署模板下所有文件:匹配 /deploy/[group]/[template] 路径
478
+ * 4. 返回部署结果或错误信息
479
+ */
480
+ startDeployServer(port = 7759) {
481
+ createServer(async (req, res) => {
482
+ const cosHeader = {
483
+ "Access-Control-Allow-Origin": req.headers.origin || "https://127.0.0.1",
484
+ "Access-Control-Allow-Methods": "GET, OPTIONS",
485
+ "Access-Control-Allow-Headers": "Content-Type, Authorization",
486
+ "Access-Control-Max-Age": "86400"
487
+ };
488
+ const url = (req.url || "").trim();
489
+ if ((req.method || "get").toLowerCase() === "options") {
490
+ res.writeHead(200, cosHeader);
491
+ res.end();
492
+ return;
493
+ }
494
+ const templateDeployInfo = url.match(/deploy\/([^/]+)\/([^/]+)\/?\?target=(.+)$/);
495
+ if (!templateDeployInfo) {
496
+ res.writeHead(404, cosHeader);
497
+ res.write("参数错误,应该是 group/template?target=xxx");
498
+ res.end();
499
+ return;
500
+ }
501
+ const user = this.getDeployUserKey();
502
+ const group = templateDeployInfo[1];
503
+ const template = this.getTemplateFile(templateDeployInfo[1], templateDeployInfo[2]);
504
+ const target = templateDeployInfo[3];
505
+ if (!user) {
506
+ res.writeHead(403, cosHeader);
507
+ res.write("请先在根目录添加 .env.local 文件");
508
+ res.end();
509
+ return;
510
+ }
511
+ if (!user.user) {
512
+ res.writeHead(403, cosHeader);
513
+ res.write("请设置 .env.local 文件中 SY_DEPLOY_USER");
514
+ res.end();
515
+ return;
516
+ }
517
+ if (!user.key) {
518
+ res.writeHead(403, cosHeader);
519
+ res.write("请设置 .env.local 文件中 SY_DEPLOY_KEY");
520
+ res.end();
521
+ return;
522
+ }
523
+ if (!this.groups.includes(group)) {
524
+ res.writeHead(403, cosHeader);
525
+ res.write(`分组错误,没有 ${group}`);
526
+ res.end();
527
+ return;
528
+ }
529
+ if (!this.getAllTemplates().includes(template)) {
530
+ res.writeHead(403, cosHeader);
531
+ res.write(`模板错误,没有 ${template}`);
532
+ res.end();
533
+ return;
534
+ }
535
+ if (target.endsWith(".js")) {
536
+ const data = (await this.coreBuild(template)).find((d) => d.deployTo === target);
537
+ if (!data) {
538
+ res.writeHead(504, cosHeader);
539
+ res.write(`没有找到文件配置 ${target}`);
540
+ res.end();
541
+ return;
542
+ }
543
+ const err = await this.upload(data.deployTo, data.code, user?.user || "", user?.key || "");
544
+ res.writeHead(200, {
545
+ "Content-Type": "text/plain",
546
+ ...cosHeader
547
+ });
548
+ res.write(err ? `部署失败 ${err.message}` : `部署完毕 ${data.deployTo}`);
549
+ res.end();
550
+ return;
551
+ }
552
+ if (target === "all") {
553
+ const buildResult = await this.coreBuild(template);
554
+ const results = await Promise.all(buildResult.map(async (data) => {
555
+ const err = await this.upload(data.deployTo, data.code, user?.user || "", user?.key || "");
556
+ return {
557
+ deployTo: data.deployTo,
558
+ err
559
+ };
560
+ }));
561
+ const success = results.filter((r) => !r.err).map((r) => r.deployTo);
562
+ const failed = results.filter((r) => r.err).map((r) => r.deployTo);
563
+ res.writeHead(200, {
564
+ "Content-Type": "text/plain",
565
+ ...cosHeader
566
+ });
567
+ res.write([success.length > 0 ? `部署成功\n ${success.join("\n")}` : "", failed.length > 0 ? `部署失败\n ${failed.join("\n")}` : ""].join("\n"));
568
+ res.end();
569
+ return;
570
+ }
571
+ res.writeHead(400, {
572
+ "Content-Type": "text/plain",
573
+ ...cosHeader
574
+ });
575
+ res.write("参数错误");
576
+ res.end();
577
+ }).listen(port);
578
+ }
649
579
  };
580
+ //#endregion
581
+ export { FooterBuilder, checkCommit, finderDeploy, finderUpload, runCreateScript, runDestroyScript };