@seayoo-web/scripts 1.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/index.js +668 -0
- package/package.json +45 -0
- package/types/index.d.ts +7 -0
- package/types/src/commit.d.ts +2 -0
- package/types/src/create.d.ts +3 -0
- package/types/src/destory.d.ts +3 -0
- package/types/src/env.d.ts +28 -0
- package/types/src/git.d.ts +51 -0
- package/types/src/inject.d.ts +10 -0
- package/types/src/utils.d.ts +45 -0
- package/types/src/vite.lab.d.ts +13 -0
- package/types/src/vite.page.d.ts +13 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,668 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { defineConfig, loadEnv } from "vite";
|
|
3
|
+
import { viteDeployPlugin } from "@seayoo-web/finder";
|
|
4
|
+
import { sentryVitePlugin } from "@sentry/vite-plugin";
|
|
5
|
+
import legacy from "@vitejs/plugin-legacy";
|
|
6
|
+
import vue from "@vitejs/plugin-vue";
|
|
7
|
+
import vueDevTools from "vite-plugin-vue-devtools";
|
|
8
|
+
import { existsSync, readFileSync } from "fs";
|
|
9
|
+
import "colors";
|
|
10
|
+
import path from "path";
|
|
11
|
+
import fs from "fs-extra";
|
|
12
|
+
import { exec } from "child_process";
|
|
13
|
+
import { Command } from "commander";
|
|
14
|
+
import inquirer from "inquirer";
|
|
15
|
+
import ora from "ora";
|
|
16
|
+
function defineLibBuildConfig(option) {
|
|
17
|
+
return defineConfig({
|
|
18
|
+
build: {
|
|
19
|
+
outDir: (option == null ? void 0 : option.outDir) || "bin",
|
|
20
|
+
lib: {
|
|
21
|
+
entry: join(process.cwd(), (option == null ? void 0 : option.rootEntry) || "index.ts"),
|
|
22
|
+
formats: ["es"],
|
|
23
|
+
fileName: (format, entryName) => {
|
|
24
|
+
return format === "es" ? `${entryName}.js` : format === "cjs" ? entryName + ".cjs" : `${entryName}.${format}.js`;
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
rollupOptions: {
|
|
28
|
+
external: (option == null ? void 0 : option.external) || [/^@seayoo-web\/(?:request|utils)/]
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
const TemplateDir = "template";
|
|
34
|
+
const InternalDir = "internal";
|
|
35
|
+
const WorkspaceFile = "pnpm-workspace.yaml";
|
|
36
|
+
const root = function() {
|
|
37
|
+
let currentDir = process.cwd();
|
|
38
|
+
while (currentDir !== path.parse(currentDir).root) {
|
|
39
|
+
if (fs.existsSync(path.join(currentDir, WorkspaceFile))) {
|
|
40
|
+
return currentDir;
|
|
41
|
+
}
|
|
42
|
+
currentDir = path.dirname(currentDir);
|
|
43
|
+
}
|
|
44
|
+
if (fs.existsSync(path.join(currentDir, "pnpm-workspace.yaml"))) {
|
|
45
|
+
return currentDir;
|
|
46
|
+
}
|
|
47
|
+
throw new Error("Could not find repository root with pnpm-workspace.yaml".bgRed);
|
|
48
|
+
}();
|
|
49
|
+
async function getTemplates() {
|
|
50
|
+
return await getRepos(TemplateDir);
|
|
51
|
+
}
|
|
52
|
+
async function getRepos(project) {
|
|
53
|
+
const projectDir = path.join(root, project);
|
|
54
|
+
const dirs = await fs.readdir(projectDir);
|
|
55
|
+
const repos = dirs.filter((item) => fs.statSync(path.join(projectDir, item)).isDirectory());
|
|
56
|
+
const descs = await Promise.all(repos.map((repo) => getRepoDesc(project, repo)));
|
|
57
|
+
return repos.map((dir, index) => ({ id: dir, desc: descs[index] })).filter((item) => item.desc !== null);
|
|
58
|
+
}
|
|
59
|
+
async function getRepoDesc(project, dir) {
|
|
60
|
+
const packageFile = path.join(root, project, dir, "package.json");
|
|
61
|
+
if (!await fs.exists(packageFile)) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const content = await fs.readJson(packageFile, "utf8");
|
|
65
|
+
return (content.description || "") + "";
|
|
66
|
+
}
|
|
67
|
+
async function getProjects(returnAll) {
|
|
68
|
+
const rootPkgPath = path.join(root, WorkspaceFile);
|
|
69
|
+
if (await fs.pathExists(rootPkgPath)) {
|
|
70
|
+
const content = await fs.readFile(rootPkgPath, "utf8");
|
|
71
|
+
const dirs = (content.match(/- "(?:.+?)\/*"/g) || []).map((t) => t.slice(3, -3));
|
|
72
|
+
const names = await Promise.all(dirs.map(getProjectName));
|
|
73
|
+
const projects = dirs.map((id, index) => ({ id, name: names[index] })).filter((project) => !project.name.includes("disabled"));
|
|
74
|
+
return returnAll === true ? projects : projects.filter((project) => project.id !== InternalDir && project.id !== TemplateDir);
|
|
75
|
+
}
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
async function getProjectName(project) {
|
|
79
|
+
const namef = path.join(project, ".name");
|
|
80
|
+
if (await fs.exists(namef)) {
|
|
81
|
+
const name = await fs.readFile(namef, "utf8");
|
|
82
|
+
return name.trim().replace(/\n[\d\D]+/g, "");
|
|
83
|
+
}
|
|
84
|
+
return project;
|
|
85
|
+
}
|
|
86
|
+
async function copyTemplate(templateName, targetDir) {
|
|
87
|
+
const templateSourceDir = path.join(root, TemplateDir, templateName);
|
|
88
|
+
await fs.copy(templateSourceDir, targetDir, {
|
|
89
|
+
filter: (src) => {
|
|
90
|
+
return !src.includes("node_modules") && !src.includes(".git") && !src.endsWith(".local");
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
async function addProjectToWorkspace(project) {
|
|
95
|
+
const rootWorkspacePath = path.join(root, WorkspaceFile);
|
|
96
|
+
if (await fs.pathExists(rootWorkspacePath)) {
|
|
97
|
+
let content = await fs.readFile(rootWorkspacePath, "utf8");
|
|
98
|
+
if (!content.includes(`- "${project}/*"`)) {
|
|
99
|
+
content += ` - "${project}/*"
|
|
100
|
+
`;
|
|
101
|
+
}
|
|
102
|
+
await fs.writeFile(rootWorkspacePath, content);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async function removeProjectFromWorkspace(project) {
|
|
106
|
+
const rootWorkspacePath = path.join(root, WorkspaceFile);
|
|
107
|
+
if (await fs.pathExists(rootWorkspacePath)) {
|
|
108
|
+
const content = (await fs.readFile(rootWorkspacePath, "utf8")).replace(
|
|
109
|
+
new RegExp(`\\n.*\\- "${project}\\/\\*"`),
|
|
110
|
+
""
|
|
111
|
+
);
|
|
112
|
+
await fs.writeFile(rootWorkspacePath, content);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function getNowTime() {
|
|
116
|
+
const now = /* @__PURE__ */ new Date();
|
|
117
|
+
return `${now.getFullYear()}/${fillZ(now.getMonth() + 1)}/${fillZ(now.getDate())} ${fillZ(now.getHours())}:${fillZ(now.getMinutes())}`;
|
|
118
|
+
}
|
|
119
|
+
function fillZ(a) {
|
|
120
|
+
return ("0" + a).slice(-2);
|
|
121
|
+
}
|
|
122
|
+
const EnvPrefix = "SY_";
|
|
123
|
+
function getBuildEnv(command, mode) {
|
|
124
|
+
const envs = loadEnv(mode, process.cwd(), EnvPrefix);
|
|
125
|
+
const envConfig = {
|
|
126
|
+
command,
|
|
127
|
+
stamp: getNowTime(),
|
|
128
|
+
page: "",
|
|
129
|
+
mode: "",
|
|
130
|
+
deployUser: "",
|
|
131
|
+
deployKey: ""
|
|
132
|
+
};
|
|
133
|
+
envConfig.mode = mode || "preview";
|
|
134
|
+
const d = `${EnvPrefix}DEPLOY_DEBUG`;
|
|
135
|
+
const t = `${EnvPrefix}DEPLOY_TO`;
|
|
136
|
+
const s = `${EnvPrefix}SENTRY_AUTH_TOKEN`;
|
|
137
|
+
const u = `${EnvPrefix}DEPLOY_USER`;
|
|
138
|
+
const k = `${EnvPrefix}DEPLOY_KEY`;
|
|
139
|
+
const execDir = process.cwd().replace(/\\/g, "/").split("/").slice(-2).join("/");
|
|
140
|
+
const packageFile = join(process.cwd(), "./package.json");
|
|
141
|
+
if (!existsSync(packageFile)) {
|
|
142
|
+
console.error("执行代码有误,没有找到 package.json".red);
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
const packageJson = JSON.parse(readFileSync(packageFile, { encoding: "utf-8" }).toString());
|
|
146
|
+
if (!packageJson || !packageJson.name || packageJson.name !== `@${execDir}`) {
|
|
147
|
+
console.error(`工程 package.json/name 属性设置错误,应该跟目录保持一致(@${execDir}),请先调整`.red);
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
envConfig.page = execDir;
|
|
151
|
+
if (envConfig.command === "build") {
|
|
152
|
+
if (process.env.NODE_ENV !== "production") {
|
|
153
|
+
console.error("部署时需要设置 NODE_ENV 为 production,请检查环境变量设置".red);
|
|
154
|
+
process.exit(1);
|
|
155
|
+
}
|
|
156
|
+
envConfig.deployTo = (envs[t] || "").replace(/(?:^https?:\/\/|\/*$)/gi, "").replace(/^(.)/, "https://$1");
|
|
157
|
+
envConfig.deployUser = envs[u];
|
|
158
|
+
envConfig.deployKey = envs[k];
|
|
159
|
+
envConfig.deployDebug = envs[d] === "1" || envs[d] === "y" || envs[d] === "true";
|
|
160
|
+
if (!envConfig.deployTo) {
|
|
161
|
+
console.error(`缺少 ${t.bgRed} 设置,请在 .env.${mode} 中配置`.red);
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
if (!envConfig.deployUser || !envConfig.deployKey) {
|
|
165
|
+
console.error(`缺少 ${u.bgRed} / ${k.bgRed} 设置,请在 .env.local 配置`.red);
|
|
166
|
+
process.exit(1);
|
|
167
|
+
}
|
|
168
|
+
envConfig.sentryAuthToken = envs[s];
|
|
169
|
+
if (!envConfig.sentryAuthToken) {
|
|
170
|
+
console.warn(`尚未设置 ${s.red},推送 sourcemap 到 sentry 的功能将无效`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return envConfig;
|
|
174
|
+
}
|
|
175
|
+
function transformEnvs(envs) {
|
|
176
|
+
return Object.entries(envs).reduce(
|
|
177
|
+
function(result, [key, value]) {
|
|
178
|
+
result[key] = JSON.stringify(value);
|
|
179
|
+
return result;
|
|
180
|
+
},
|
|
181
|
+
{}
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
const MainBranchName = "main";
|
|
185
|
+
const DeployTagPrefix = "deploy#";
|
|
186
|
+
function converToDeployTagName(deployTo) {
|
|
187
|
+
return `${DeployTagPrefix}${deployTo.toLowerCase().replace(/(?:^https?:\/\/|\/*$)/gi, "")}`;
|
|
188
|
+
}
|
|
189
|
+
async function execCmd(cmd, ignoreWarning = false) {
|
|
190
|
+
return new Promise((resolve, reject) => {
|
|
191
|
+
exec(cmd, { cwd: root, env: { ...process.env } }, (error, stdout, stderr) => {
|
|
192
|
+
if (error || stderr && !ignoreWarning) {
|
|
193
|
+
console.error(cmd, error, stderr);
|
|
194
|
+
reject(`执行命令时出错: ${(error == null ? void 0 : error.message) || stderr}`);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
resolve(stdout.trim());
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
async function getCommitInfo(command, mode, deployTo) {
|
|
202
|
+
if (command !== "build" || mode === "preview" || mode === "prev") {
|
|
203
|
+
return { hash: await getCommitHash(), logs: [] };
|
|
204
|
+
}
|
|
205
|
+
if (await hasUncommittedChanges()) {
|
|
206
|
+
console.error(`在部署前需要提交所有代码!`.red);
|
|
207
|
+
process.exit(1);
|
|
208
|
+
}
|
|
209
|
+
if (await getCurrentBranchName() !== MainBranchName) {
|
|
210
|
+
console.error(`正式环境部署需要在 ${`${MainBranchName}分支`.bgRed} 操作!`.red);
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
const unCommitChanges = await countUnsubmitChanges(MainBranchName);
|
|
214
|
+
if (unCommitChanges > 0) {
|
|
215
|
+
console.error("正式环境部署需要先将代码同步到服务器!".red, `发现 ${unCommitChanges} 个 commit 差异`.red);
|
|
216
|
+
process.exit(1);
|
|
217
|
+
}
|
|
218
|
+
const [currentCommit, lastDeployTag] = await Promise.all([getCommitHash(), getLastDeployTag(deployTo)]);
|
|
219
|
+
const commitLogs = await getCommitLogs(currentCommit, lastDeployTag, "./");
|
|
220
|
+
return { hash: currentCommit, logs: commitLogs };
|
|
221
|
+
}
|
|
222
|
+
async function getLastDeployTag(deployTo) {
|
|
223
|
+
await fetchTags();
|
|
224
|
+
const deployTags = await getDeployTags();
|
|
225
|
+
const tag = converToDeployTagName(deployTo);
|
|
226
|
+
return deployTags.includes(tag) ? tag : "";
|
|
227
|
+
}
|
|
228
|
+
async function createPageDeployTag(page, deployTo, finderUser) {
|
|
229
|
+
const tagName = converToDeployTagName(deployTo);
|
|
230
|
+
const gitUser = await execCmd(`git config user.email`).catch(() => "");
|
|
231
|
+
await execCmd(
|
|
232
|
+
[`git tag -f ${tagName} -m "${getNowTime()} deployed by ${gitUser || finderUser}`, page ? `from ${page}` : ""].filter((c) => !!c).join(" ")
|
|
233
|
+
);
|
|
234
|
+
try {
|
|
235
|
+
await execCmd(`git push origin -f ${tagName}`, true);
|
|
236
|
+
} catch (e) {
|
|
237
|
+
await execCmd(`git push origin --tags`, true);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
async function getCommitHash() {
|
|
241
|
+
return (await execCmd("git rev-parse HEAD")).slice(0, 8);
|
|
242
|
+
}
|
|
243
|
+
async function getCurrentBranchName() {
|
|
244
|
+
return await execCmd("git rev-parse --abbrev-ref HEAD");
|
|
245
|
+
}
|
|
246
|
+
async function fetchTags() {
|
|
247
|
+
await execCmd("git fetch --tags", true);
|
|
248
|
+
}
|
|
249
|
+
async function getDeployTags() {
|
|
250
|
+
const result = await execCmd("git tag");
|
|
251
|
+
return result.split("\n").filter((tag) => tag.startsWith(DeployTagPrefix));
|
|
252
|
+
}
|
|
253
|
+
async function getCommitLogs(branchOrCommit, sinceCommit, codePath) {
|
|
254
|
+
const cmd = [
|
|
255
|
+
"git log",
|
|
256
|
+
sinceCommit ? `${sinceCommit}..${branchOrCommit || "HEAD"}` : branchOrCommit || "",
|
|
257
|
+
`--oneline --pretty=format:"[%cd][%h] %s" --date=short -n 60`,
|
|
258
|
+
`-- ${codePath}`
|
|
259
|
+
].filter((f) => !!f).join(" ");
|
|
260
|
+
const result = await execCmd(cmd);
|
|
261
|
+
return result ? result.split("\n") : [];
|
|
262
|
+
}
|
|
263
|
+
async function hasUncommittedChanges() {
|
|
264
|
+
const result = await execCmd("git status --porcelain");
|
|
265
|
+
return result !== "";
|
|
266
|
+
}
|
|
267
|
+
async function countUnsubmitChanges(branch) {
|
|
268
|
+
const result = await execCmd(`git rev-list --count ${branch} "^origin/${branch}"`);
|
|
269
|
+
return parseInt(result, 10);
|
|
270
|
+
}
|
|
271
|
+
async function checkGitStatusBeforeDestory() {
|
|
272
|
+
if (await hasUncommittedChanges()) {
|
|
273
|
+
console.error(`在销毁前需要提交所有代码!`.red);
|
|
274
|
+
process.exit(1);
|
|
275
|
+
}
|
|
276
|
+
if (await getCurrentBranchName() !== MainBranchName) {
|
|
277
|
+
console.error(`销毁操作需要在 ${`${MainBranchName}分支`.bgRed} 进行!`.red);
|
|
278
|
+
process.exit(1);
|
|
279
|
+
}
|
|
280
|
+
const unCommitChanges = await countUnsubmitChanges(MainBranchName);
|
|
281
|
+
if (unCommitChanges > 0) {
|
|
282
|
+
console.error("销毁操作需要先将代码同步到服务器!".red, `发现 ${unCommitChanges} 个 commit 差异`.red);
|
|
283
|
+
process.exit(1);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
async function createBackupTag(action, info) {
|
|
287
|
+
const tagName = `backup#${action}-${info}`;
|
|
288
|
+
const gitUser = await execCmd(`git config user.email`).catch(() => "");
|
|
289
|
+
await execCmd(`git tag -f ${tagName} -m "${getNowTime()} destroyed by ${gitUser || "unknown"}`);
|
|
290
|
+
try {
|
|
291
|
+
await execCmd(`git push origin -f ${tagName}`, true);
|
|
292
|
+
} catch (e) {
|
|
293
|
+
await execCmd(`git push origin --tags`, true);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
async function submitAllDeletionChanges(message) {
|
|
297
|
+
const branch = await getCurrentBranchName();
|
|
298
|
+
await execCmd(`git add --all :/ && git commit -m "${message}" && git push origin ${branch}`, true);
|
|
299
|
+
}
|
|
300
|
+
function htmlInjectPlugin(data) {
|
|
301
|
+
return {
|
|
302
|
+
name: "html-inject-plugin",
|
|
303
|
+
transformIndexHtml: {
|
|
304
|
+
order: "pre",
|
|
305
|
+
handler: (html) => html.replace(/%\s*(.*?)\s*%/g, (match, p1) => data[p1] ?? match).replace(/[\r\n]{2,}/g, "\n").replace(/(?:^[\s\r\n]*|[\s\r\n]*$)/g, "")
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
function definePageBuildConfig(option) {
|
|
310
|
+
return defineConfig(async function(viteEnvs) {
|
|
311
|
+
const { command, mode } = viteEnvs;
|
|
312
|
+
const envs = getBuildEnv(command, mode);
|
|
313
|
+
const gitInfo = await getCommitInfo(command, mode, envs.deployTo || "");
|
|
314
|
+
const isProductMode = mode === "production" || mode === "prod";
|
|
315
|
+
const {
|
|
316
|
+
plugins = [],
|
|
317
|
+
define = {},
|
|
318
|
+
build = {},
|
|
319
|
+
resolve = {},
|
|
320
|
+
server = {},
|
|
321
|
+
sentry = {},
|
|
322
|
+
...optionReset
|
|
323
|
+
} = option || {};
|
|
324
|
+
const { alias, ...resolveReset } = resolve || {};
|
|
325
|
+
return {
|
|
326
|
+
base: "./",
|
|
327
|
+
build: {
|
|
328
|
+
emptyOutDir: true,
|
|
329
|
+
outDir: join(process.cwd(), "./dist"),
|
|
330
|
+
assetsDir: "assets",
|
|
331
|
+
reportCompressedSize: false,
|
|
332
|
+
sourcemap: command === "build" && !!envs.sentryAuthToken,
|
|
333
|
+
...build
|
|
334
|
+
},
|
|
335
|
+
plugins: [
|
|
336
|
+
vue(),
|
|
337
|
+
vueDevTools(),
|
|
338
|
+
legacy({
|
|
339
|
+
targets: ["defaults", "ie >= 10", "chrome 52"],
|
|
340
|
+
additionalLegacyPolyfills: ["regenerator-runtime/runtime"],
|
|
341
|
+
renderLegacyChunks: true
|
|
342
|
+
}),
|
|
343
|
+
htmlInjectPlugin({
|
|
344
|
+
BUILD_TIME: envs.stamp,
|
|
345
|
+
BUILD_MODE: envs.mode,
|
|
346
|
+
BUILD_VERSION: gitInfo.hash
|
|
347
|
+
}),
|
|
348
|
+
...plugins,
|
|
349
|
+
command === "build" && envs.deployTo ? viteDeployPlugin({
|
|
350
|
+
deployTo: envs.deployTo,
|
|
351
|
+
user: envs.deployUser,
|
|
352
|
+
key: envs.deployKey,
|
|
353
|
+
debug: envs.deployDebug,
|
|
354
|
+
commitLogs: isProductMode && gitInfo.logs.length > 0 ? "\n" + gitInfo.logs.join("\n") : void 0,
|
|
355
|
+
onFinished() {
|
|
356
|
+
if (isProductMode) {
|
|
357
|
+
createPageDeployTag(envs.page, envs.deployTo || "", envs.deployUser);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}) : null,
|
|
361
|
+
command === "build" && envs.sentryAuthToken ? sentryVitePlugin({
|
|
362
|
+
authToken: envs.sentryAuthToken,
|
|
363
|
+
org: "sentry",
|
|
364
|
+
url: sentry.url || "https://sentry.seayoo.com/",
|
|
365
|
+
project: sentry.project || "gamer-fe"
|
|
366
|
+
}) : null
|
|
367
|
+
],
|
|
368
|
+
define: transformEnvs({
|
|
369
|
+
BUILD_TIME: envs.stamp,
|
|
370
|
+
BUILD_MODE: envs.mode,
|
|
371
|
+
...envs,
|
|
372
|
+
...define
|
|
373
|
+
}),
|
|
374
|
+
resolve: {
|
|
375
|
+
alias: {
|
|
376
|
+
"@": join(process.cwd(), "./src"),
|
|
377
|
+
...alias
|
|
378
|
+
},
|
|
379
|
+
extensions: [".mts", ".mjs", ".ts", ".js"],
|
|
380
|
+
// allowImportingTsExtensions: true,
|
|
381
|
+
...resolveReset
|
|
382
|
+
},
|
|
383
|
+
server: {
|
|
384
|
+
open: true,
|
|
385
|
+
...server
|
|
386
|
+
},
|
|
387
|
+
...optionReset,
|
|
388
|
+
// envPrefix 不允许覆盖
|
|
389
|
+
envPrefix: EnvPrefix
|
|
390
|
+
};
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
const commitRE = /^(?:revert: )?(?:feat|fix|docs|style|refactor|test|chore|debug|tweak|improve)(?:\(.+\))?: .{1,100}/;
|
|
394
|
+
function checkCommit() {
|
|
395
|
+
const msgPath = process.argv[2];
|
|
396
|
+
const msg = readFileSync(msgPath, "utf-8").trim();
|
|
397
|
+
if (!commitRE.test(msg) && !msg.startsWith("Merge branch")) {
|
|
398
|
+
console.error(`ERROR!【提交的 Commit 信息格式错误,请注意使用正确的类型前缀!】`.red);
|
|
399
|
+
process.exit(1);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
const program$1 = new Command();
|
|
403
|
+
program$1.version("1.0.0").argument("<target>");
|
|
404
|
+
function runCreateScript() {
|
|
405
|
+
program$1.parse(process.argv);
|
|
406
|
+
}
|
|
407
|
+
async function initRepoQuestions$1() {
|
|
408
|
+
const projects = await getProjects();
|
|
409
|
+
const templates = await getTemplates();
|
|
410
|
+
return [
|
|
411
|
+
{
|
|
412
|
+
type: "list",
|
|
413
|
+
name: "project",
|
|
414
|
+
message: "选择项目:",
|
|
415
|
+
choices: projects.map((project) => ({
|
|
416
|
+
name: `${project.id} ${project.name}`,
|
|
417
|
+
short: project.id,
|
|
418
|
+
value: project.id
|
|
419
|
+
}))
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
type: "list",
|
|
423
|
+
name: "template",
|
|
424
|
+
message: "选择模板:",
|
|
425
|
+
choices: templates.map((template) => ({
|
|
426
|
+
name: `${template.id} ${template.desc}`,
|
|
427
|
+
short: template.id,
|
|
428
|
+
value: template.id
|
|
429
|
+
}))
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
type: "input",
|
|
433
|
+
name: "repoName",
|
|
434
|
+
message: "仓库名称:",
|
|
435
|
+
validate: (input) => {
|
|
436
|
+
if (/^[a-z0-9-]+$/.test(input)) return true;
|
|
437
|
+
return "仓库名称只能包含小写字母、数字和中横线";
|
|
438
|
+
}
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
type: "input",
|
|
442
|
+
name: "description",
|
|
443
|
+
message: "仓库描述:",
|
|
444
|
+
default: ""
|
|
445
|
+
}
|
|
446
|
+
];
|
|
447
|
+
}
|
|
448
|
+
async function initProjectQuestions$1() {
|
|
449
|
+
const projects = (await getProjects(true)).map((project) => project.id);
|
|
450
|
+
return [
|
|
451
|
+
{
|
|
452
|
+
type: "input",
|
|
453
|
+
name: "project",
|
|
454
|
+
message: "项目 ID:",
|
|
455
|
+
validate: (input) => {
|
|
456
|
+
if (!/^[a-zA-Z0-9-_]+$/.test(input)) {
|
|
457
|
+
return "项目名称只能包含字母、数字、下划线和横线";
|
|
458
|
+
}
|
|
459
|
+
if (projects.includes(input)) {
|
|
460
|
+
return "项目已经存在,请检查";
|
|
461
|
+
}
|
|
462
|
+
return true;
|
|
463
|
+
}
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
type: "input",
|
|
467
|
+
name: "name",
|
|
468
|
+
message: "中文名称:",
|
|
469
|
+
validate: (input) => {
|
|
470
|
+
if (!input) {
|
|
471
|
+
return "中文名称不能为空";
|
|
472
|
+
}
|
|
473
|
+
return true;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
];
|
|
477
|
+
}
|
|
478
|
+
async function updatePackageFiles(targetDir, answers) {
|
|
479
|
+
await Promise.all([
|
|
480
|
+
// 初始化 package.json
|
|
481
|
+
updatePackageJson(targetDir, answers),
|
|
482
|
+
// 初始化样例文件
|
|
483
|
+
initSampleFiles(targetDir)
|
|
484
|
+
]);
|
|
485
|
+
}
|
|
486
|
+
async function updatePackageJson(targetDir, answers) {
|
|
487
|
+
const pkgPath = path.join(targetDir, "package.json");
|
|
488
|
+
if (await fs.pathExists(pkgPath)) {
|
|
489
|
+
const pkg = await fs.readJson(pkgPath);
|
|
490
|
+
pkg.name = `@${answers.project}/${answers.repoName}`;
|
|
491
|
+
pkg.description = answers.description || pkg.description;
|
|
492
|
+
pkg.version = "1.0.0";
|
|
493
|
+
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
async function initSampleFiles(targetDir) {
|
|
497
|
+
const items = await fs.readdir(targetDir);
|
|
498
|
+
const files = items.filter(
|
|
499
|
+
(item) => !fs.statSync(path.join(targetDir, item)).isDirectory() && item.endsWith(".sample")
|
|
500
|
+
);
|
|
501
|
+
for (const filename of files) {
|
|
502
|
+
const sample = path.join(targetDir, filename);
|
|
503
|
+
if (await fs.pathExists(sample)) {
|
|
504
|
+
await fs.writeFile(path.join(targetDir, filename.replace(/\.sample$/, "")), await fs.readFile(sample));
|
|
505
|
+
fs.unlink(sample);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
async function startCreateJob$1(target) {
|
|
510
|
+
if (target === "project") {
|
|
511
|
+
await createProject();
|
|
512
|
+
} else {
|
|
513
|
+
await createRepo();
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
async function createProject() {
|
|
517
|
+
const questions = await initProjectQuestions$1();
|
|
518
|
+
const answers = await inquirer.prompt(questions);
|
|
519
|
+
const spinner = ora("正在创建项目...").start();
|
|
520
|
+
const projectDir = path.join(root, answers.project);
|
|
521
|
+
await fs.mkdir(projectDir);
|
|
522
|
+
await fs.writeFile(path.join(projectDir, ".name"), answers.name);
|
|
523
|
+
await addProjectToWorkspace(answers.project);
|
|
524
|
+
spinner.succeed("项目创建成功!".green);
|
|
525
|
+
}
|
|
526
|
+
async function createRepo() {
|
|
527
|
+
const questions = await initRepoQuestions$1();
|
|
528
|
+
const answers = await inquirer.prompt(questions);
|
|
529
|
+
const spinner = ora("正在创建仓库...").start();
|
|
530
|
+
const projectDir = path.join(root, answers.project);
|
|
531
|
+
spinner.text = "检查目录...";
|
|
532
|
+
if (!await fs.pathExists(projectDir)) {
|
|
533
|
+
fs.mkdirSync(projectDir, { recursive: true });
|
|
534
|
+
}
|
|
535
|
+
const targetDir = path.join(root, answers.project, answers.repoName);
|
|
536
|
+
if (await fs.pathExists(targetDir)) {
|
|
537
|
+
spinner.fail(`错误:目录(${`${answers.project}/${answers.repoName}`})已存在`.red);
|
|
538
|
+
process.exit(1);
|
|
539
|
+
}
|
|
540
|
+
spinner.text = "复制模板文件...";
|
|
541
|
+
await copyTemplate(answers.template, targetDir);
|
|
542
|
+
spinner.text = "更新 package.json...";
|
|
543
|
+
await updatePackageFiles(targetDir, answers);
|
|
544
|
+
spinner.succeed("仓库创建成功!".green);
|
|
545
|
+
console.log("\n接下来你可以:");
|
|
546
|
+
console.log(`cd ${answers.project}/${answers.repoName}`.cyan);
|
|
547
|
+
console.log("pnpm i".cyan);
|
|
548
|
+
console.log("pnpm dev".cyan);
|
|
549
|
+
}
|
|
550
|
+
program$1.action(startCreateJob$1);
|
|
551
|
+
const program = new Command();
|
|
552
|
+
program.version("1.0.0").argument("<target>");
|
|
553
|
+
function runDestoryScript() {
|
|
554
|
+
program.parse(process.argv);
|
|
555
|
+
}
|
|
556
|
+
async function startCreateJob(target) {
|
|
557
|
+
if (target === "project") {
|
|
558
|
+
await destroyProject();
|
|
559
|
+
} else {
|
|
560
|
+
await destroyRepo();
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
async function initProjectQuestions() {
|
|
564
|
+
const projects = await getProjects();
|
|
565
|
+
return [
|
|
566
|
+
{
|
|
567
|
+
type: "list",
|
|
568
|
+
name: "project",
|
|
569
|
+
message: "删除项目:",
|
|
570
|
+
choices: projects.map((project) => ({
|
|
571
|
+
name: `${project.id} ${project.name}`,
|
|
572
|
+
short: project.id,
|
|
573
|
+
value: project.id
|
|
574
|
+
}))
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
type: "confirm",
|
|
578
|
+
name: "confirm",
|
|
579
|
+
message: function(answers) {
|
|
580
|
+
return `确认删除整个 ${answers.project} 项目 ?`;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
];
|
|
584
|
+
}
|
|
585
|
+
async function destroyProject() {
|
|
586
|
+
await checkGitStatusBeforeDestory();
|
|
587
|
+
const questions = await initProjectQuestions();
|
|
588
|
+
const answers = await inquirer.prompt(questions);
|
|
589
|
+
if (!answers.confirm) {
|
|
590
|
+
process.exit(0);
|
|
591
|
+
}
|
|
592
|
+
const spinner = ora("正在创建 Backup Tag...").start();
|
|
593
|
+
await createBackupTag("project", answers.project);
|
|
594
|
+
spinner.text = "开始清理文件...";
|
|
595
|
+
const projectDir = path.join(root, answers.project);
|
|
596
|
+
await fs.removeSync(projectDir);
|
|
597
|
+
spinner.text = "调整配置文件...";
|
|
598
|
+
await removeProjectFromWorkspace(answers.project);
|
|
599
|
+
spinner.text = "提交代码中...";
|
|
600
|
+
await submitAllDeletionChanges(`chore: destory project ${answers.project}`);
|
|
601
|
+
spinner.succeed("项目已经销毁!".green);
|
|
602
|
+
}
|
|
603
|
+
async function initRepoQuestions() {
|
|
604
|
+
const projects = await getProjects();
|
|
605
|
+
return [
|
|
606
|
+
{
|
|
607
|
+
type: "list",
|
|
608
|
+
name: "project",
|
|
609
|
+
message: "所在项目:",
|
|
610
|
+
choices: projects.map((project) => ({
|
|
611
|
+
name: `${project.id} ${project.name}`,
|
|
612
|
+
short: project.id,
|
|
613
|
+
value: project.id
|
|
614
|
+
}))
|
|
615
|
+
},
|
|
616
|
+
{
|
|
617
|
+
type: "list",
|
|
618
|
+
name: "page",
|
|
619
|
+
message: "删除页面:",
|
|
620
|
+
choices: async function(answers) {
|
|
621
|
+
const repos = await getRepos(answers.project);
|
|
622
|
+
if (repos.length === 0) {
|
|
623
|
+
throw new Error("工程不含有任意有效 Repo!操作中止");
|
|
624
|
+
}
|
|
625
|
+
return repos.map((repo) => ({
|
|
626
|
+
name: `${repo.id} ${repo.desc || ""}`,
|
|
627
|
+
short: repo.id,
|
|
628
|
+
value: repo.id
|
|
629
|
+
}));
|
|
630
|
+
}
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
type: "confirm",
|
|
634
|
+
name: "confirm",
|
|
635
|
+
message: async function(answers) {
|
|
636
|
+
return `确认删除页面 ${answers.project}/${answers.page} ?`;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
];
|
|
640
|
+
}
|
|
641
|
+
async function destroyRepo() {
|
|
642
|
+
await checkGitStatusBeforeDestory();
|
|
643
|
+
const questions = await initRepoQuestions();
|
|
644
|
+
const answers = await inquirer.prompt(questions);
|
|
645
|
+
if (!answers.confirm) {
|
|
646
|
+
process.exit(0);
|
|
647
|
+
}
|
|
648
|
+
const spinner = ora("正在创建 Backup Tag...").start();
|
|
649
|
+
await createBackupTag("page", `${answers.project}/${answers.page}`);
|
|
650
|
+
spinner.text = "开始清理文件...";
|
|
651
|
+
const pageDir = path.join(root, answers.project, answers.page);
|
|
652
|
+
await fs.removeSync(pageDir);
|
|
653
|
+
spinner.text = "提交代码中...";
|
|
654
|
+
await submitAllDeletionChanges(`chore: destory page ${answers.project}/${answers.page}`);
|
|
655
|
+
spinner.succeed("页面已经销毁!".green);
|
|
656
|
+
}
|
|
657
|
+
program.action(startCreateJob);
|
|
658
|
+
export {
|
|
659
|
+
EnvPrefix,
|
|
660
|
+
checkCommit,
|
|
661
|
+
defineLibBuildConfig,
|
|
662
|
+
definePageBuildConfig,
|
|
663
|
+
getBuildEnv,
|
|
664
|
+
htmlInjectPlugin,
|
|
665
|
+
runCreateScript,
|
|
666
|
+
runDestoryScript,
|
|
667
|
+
transformEnvs
|
|
668
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@seayoo-web/scripts",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "scripts for seayoo web monorepos",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"source": "index.ts",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./types/index.d.ts",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"types",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">= 18"
|
|
17
|
+
},
|
|
18
|
+
"author": "web@seayoo.com",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@sentry/vite-plugin": "^3.2.1",
|
|
25
|
+
"@vitejs/plugin-legacy": "^6.0.2",
|
|
26
|
+
"@vitejs/plugin-vue": "^5.2.1",
|
|
27
|
+
"colors": "^1.4.0",
|
|
28
|
+
"commander": "^13.1.0",
|
|
29
|
+
"fs-extra": "^11.3.0",
|
|
30
|
+
"inquirer": "^12.4.2",
|
|
31
|
+
"ora": "^8.2.0",
|
|
32
|
+
"vite-plugin-stylelint": "^6.0.0",
|
|
33
|
+
"vite-plugin-vue-devtools": "^7.7.2",
|
|
34
|
+
"@seayoo-web/finder": "^2.0.13"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/fs-extra": "^11.0.4",
|
|
38
|
+
"@types/node": "^22.13.1",
|
|
39
|
+
"@seayoo-web/tsconfig": "^1.0.2"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "vite build && tsc --emitDeclarationOnly",
|
|
43
|
+
"prepublish": "pnpm build"
|
|
44
|
+
}
|
|
45
|
+
}
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import "colors";
|
|
2
|
+
export declare const EnvPrefix = "SY_";
|
|
3
|
+
interface IBuildEnvs {
|
|
4
|
+
/** 执行的命令,比如 build / serve */
|
|
5
|
+
command: "serve" | "build";
|
|
6
|
+
/** 时间戳字符串,比如 2023/01/31 16:00 */
|
|
7
|
+
stamp: string;
|
|
8
|
+
/** 处理模式,比如 preview / production */
|
|
9
|
+
mode: string;
|
|
10
|
+
/** 页面 repo 地址 */
|
|
11
|
+
page: string;
|
|
12
|
+
/** 页面部署地址 */
|
|
13
|
+
deployTo?: string;
|
|
14
|
+
/** 打印部署详细日志 */
|
|
15
|
+
deployDebug?: boolean;
|
|
16
|
+
/** 部署 sentry sourcemap 用的 token */
|
|
17
|
+
sentryAuthToken?: string;
|
|
18
|
+
/** 部署的 user */
|
|
19
|
+
deployUser: string;
|
|
20
|
+
/** 部署的 key */
|
|
21
|
+
deployKey: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 获取构建环境配置
|
|
25
|
+
*/
|
|
26
|
+
export declare function getBuildEnv(command: "serve" | "build", mode: string): IBuildEnvs;
|
|
27
|
+
export declare function transformEnvs(envs: Record<string, unknown>): Record<string, string>;
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 读取提交信息
|
|
3
|
+
*/
|
|
4
|
+
export declare function getCommitInfo(command: string, mode: string, deployTo: string): Promise<{
|
|
5
|
+
hash: string;
|
|
6
|
+
logs: string[];
|
|
7
|
+
}>;
|
|
8
|
+
/**
|
|
9
|
+
* 更新部署目标的 commit 标记,在页面成功部署后调用
|
|
10
|
+
*/
|
|
11
|
+
export declare function createPageDeployTag(page: string, deployTo: string, finderUser: string): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* 检查最近一次 commit 信息
|
|
14
|
+
*/
|
|
15
|
+
export declare function getCommitHash(): Promise<string>;
|
|
16
|
+
/**
|
|
17
|
+
* 检查当前分支名称
|
|
18
|
+
*/
|
|
19
|
+
export declare function getCurrentBranchName(): Promise<string>;
|
|
20
|
+
/**
|
|
21
|
+
* 拉取所有 tags
|
|
22
|
+
*/
|
|
23
|
+
export declare function fetchTags(): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* 读取所有部署 tag(以 deploy# 开头)
|
|
26
|
+
*/
|
|
27
|
+
export declare function getDeployTags(): Promise<string[]>;
|
|
28
|
+
/**
|
|
29
|
+
* 查询 commit 提交日志,最多返回 60 条日志
|
|
30
|
+
*/
|
|
31
|
+
export declare function getCommitLogs(branchOrCommit: string, sinceCommit?: string, codePath?: string): Promise<string[]>;
|
|
32
|
+
/**
|
|
33
|
+
* 检查当前分支是否有尚未提交的代码
|
|
34
|
+
*/
|
|
35
|
+
export declare function hasUncommittedChanges(): Promise<boolean>;
|
|
36
|
+
/**
|
|
37
|
+
* 检查尚未同步服务器的提交数量
|
|
38
|
+
*/
|
|
39
|
+
export declare function countUnsubmitChanges(branch: string): Promise<number>;
|
|
40
|
+
/**
|
|
41
|
+
* 在删除工程或目录前检查 git 状态
|
|
42
|
+
*/
|
|
43
|
+
export declare function checkGitStatusBeforeDestory(): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* 创建备份 tag
|
|
46
|
+
*/
|
|
47
|
+
export declare function createBackupTag(action: "project" | "page", info: string): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* 将所有删除提交
|
|
50
|
+
*/
|
|
51
|
+
export declare function submitAllDeletionChanges(message: string): Promise<void>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import "colors";
|
|
2
|
+
/** 项目模板目录 */
|
|
3
|
+
export declare const TemplateDir = "template";
|
|
4
|
+
/** 内部模块目录 */
|
|
5
|
+
export declare const InternalDir = "internal";
|
|
6
|
+
/**
|
|
7
|
+
* monorepo 项目根目录
|
|
8
|
+
*/
|
|
9
|
+
export declare const root: string;
|
|
10
|
+
/**
|
|
11
|
+
* 读取所有模板项目信息
|
|
12
|
+
*/
|
|
13
|
+
export declare function getTemplates(): Promise<{
|
|
14
|
+
id: string;
|
|
15
|
+
desc: string | null;
|
|
16
|
+
}[]>;
|
|
17
|
+
/** 读取指定项目的 repo 信息 */
|
|
18
|
+
export declare function getRepos(project: string): Promise<{
|
|
19
|
+
id: string;
|
|
20
|
+
desc: string | null;
|
|
21
|
+
}[]>;
|
|
22
|
+
/**
|
|
23
|
+
* 从 pnpm-workspace.yaml 读取支持的项目名称
|
|
24
|
+
*/
|
|
25
|
+
export declare function getProjects(returnAll?: boolean): Promise<{
|
|
26
|
+
id: string;
|
|
27
|
+
name: string;
|
|
28
|
+
}[]>;
|
|
29
|
+
/**
|
|
30
|
+
* 读取项目的中文名称
|
|
31
|
+
*/
|
|
32
|
+
export declare function getProjectName(project: string): Promise<string>;
|
|
33
|
+
/**
|
|
34
|
+
* 复制模板到目标目录
|
|
35
|
+
*/
|
|
36
|
+
export declare function copyTemplate(templateName: string, targetDir: string): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* 更新 workspace.yaml
|
|
39
|
+
*/
|
|
40
|
+
export declare function addProjectToWorkspace(project: string): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* 更新 workspace.yaml
|
|
43
|
+
*/
|
|
44
|
+
export declare function removeProjectFromWorkspace(project: string): Promise<void>;
|
|
45
|
+
export declare function getNowTime(): string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
interface LibBuildOption {
|
|
2
|
+
/** 输出目录,默认为 "bin" */
|
|
3
|
+
outDir?: string;
|
|
4
|
+
/** 入口文件,默认为 "index.ts" */
|
|
5
|
+
rootEntry?: string;
|
|
6
|
+
/** 要排除的工具包 */
|
|
7
|
+
external?: string[];
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 导出一个动态的配置工厂函数用于 lib 类工具的编译配置
|
|
11
|
+
*/
|
|
12
|
+
export declare function defineLibBuildConfig(option: LibBuildOption): import("vite").UserConfig;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type UserConfig } from "vite";
|
|
2
|
+
interface ExternalConfig {
|
|
3
|
+
/** sentry 配置 */
|
|
4
|
+
sentry?: {
|
|
5
|
+
project?: string;
|
|
6
|
+
url?: string;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 导出一个动态的配置工厂函数
|
|
11
|
+
*/
|
|
12
|
+
export declare function definePageBuildConfig(option: UserConfig & ExternalConfig): import("vite").UserConfigFnPromise;
|
|
13
|
+
export {};
|