UNPKG

vite-auto-deploy

Version:

用于配合 PHP 自动打包并上传打包后的文件到服务器指定目录的插件,此插件需要配合 php [项目地址](https://gitee.com/mxp131011/auto_deploy/)

439 lines (433 loc) 18.4 kB
'use strict'; var formdataNode = require('formdata-node'); var path2 = require('path'); var ora = require('ora'); var chalk = require('chalk'); var os = require('os'); var fs = require('fs'); var crypto = require('crypto'); var machineId = require('node-machine-id'); var JSZip = require('jszip'); var fetch = require('node-fetch'); var https = require('https'); var zlib = require('zlib'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } var path2__default = /*#__PURE__*/_interopDefault(path2); var ora__default = /*#__PURE__*/_interopDefault(ora); var chalk__default = /*#__PURE__*/_interopDefault(chalk); var os__default = /*#__PURE__*/_interopDefault(os); var fs__default = /*#__PURE__*/_interopDefault(fs); var crypto__default = /*#__PURE__*/_interopDefault(crypto); var machineId__default = /*#__PURE__*/_interopDefault(machineId); var JSZip__default = /*#__PURE__*/_interopDefault(JSZip); var fetch__default = /*#__PURE__*/_interopDefault(fetch); var https__default = /*#__PURE__*/_interopDefault(https); var zlib__default = /*#__PURE__*/_interopDefault(zlib); // src/auto-deploy/index.ts function readFile(readPath) { if (fs__default.default.existsSync(readPath)) { return fs__default.default.readFileSync(readPath, "utf8"); } else { return null; } } function deleteFile(removePath) { fs__default.default.existsSync(removePath) && fs__default.default.unlinkSync(removePath); } function writeFile(filePath, data) { const directory = path2__default.default.dirname(filePath); fs__default.default.mkdirSync(directory, { recursive: true }); fs__default.default.writeFileSync(filePath, data); } function getecretKeyPath(saveSecretKeyDirectory) { const rootDirectory = saveSecretKeyDirectory || path2__default.default.join(os__default.default.homedir(), ".vite-auto-deploy"); const publicKeyPath = path2__default.default.join(rootDirectory, "ed25519_public.pem"); const privateKeyPath = path2__default.default.join(rootDirectory, "ed25519_private.pem"); const keyPairIdPath = path2__default.default.join(rootDirectory, ".keyPairId"); return { publicKeyPath, privateKeyPath, keyPairIdPath }; } function createSecretKey(passphrase, pathInfo) { const { publicKey, privateKey } = crypto__default.default.generateKeyPairSync("ed25519", { publicKeyEncoding: { type: "spki", format: "pem" }, privateKeyEncoding: { type: "pkcs8", format: "pem", passphrase, cipher: "aes-256-cbc" } }); let keyPairId = ""; try { keyPairId = machineId__default.default.machineIdSync(); } catch (error) { } keyPairId = keyPairId ? keyPairId : calculateSHA256(crypto__default.default.randomUUID()); keyPairId = calculateSHA256(calculateSHA256(publicKey).substring(8, 16) + keyPairId + calculateSHA256(privateKey).substring(8, 16)); writeFile(pathInfo.publicKeyPath, publicKey); writeFile(pathInfo.privateKeyPath, privateKey); writeFile(pathInfo.keyPairIdPath, keyPairId); return { publicKey, privateKey, keyPairId }; } function getSecretKey(passphrase, pathInfo, resetKey, debug) { resetKey && deleteFile(pathInfo.publicKeyPath); resetKey && deleteFile(pathInfo.privateKeyPath); resetKey && deleteFile(pathInfo.keyPairIdPath); let keys = { privateKey: readFile(pathInfo.privateKeyPath) || "", publicKey: readFile(pathInfo.publicKeyPath) || "", keyPairId: readFile(pathInfo.keyPairIdPath) || "" }; if (!keys.privateKey || !keys.publicKey || !keys.keyPairId) { keys = createSecretKey(passphrase, pathInfo); debug && console.log("\u65E5\u5FD7: \u751F\u6210\u6216\u91CD\u7F6E\u5BC6\u5319\u6210\u529F"); } else { debug && console.log("\u65E5\u5FD7: \u83B7\u53D6\u5BC6\u5319\u6210\u529F"); } return { publicKey: keys.publicKey, privateKey: keys.privateKey, keyPairId: keys.keyPairId }; } function signEd25519(originalText, privateKey, passphrase) { const encryptedData = crypto__default.default.sign(null, Buffer.from(originalText), { key: privateKey, passphrase }); const signature = encryptedData.toString("base64"); return signature; } function calculateSHA256(fileContent) { const hash = crypto__default.default.createHash("sha256"); hash.update(fileContent); const sha256Hash = hash.digest("hex"); return sha256Hash; } async function createZip(outDir, zipName, saveLocalZip) { try { const catalog = path2__default.default.resolve(outDir); const storagePath = path2__default.default.join(catalog, zipName); deleteFile(storagePath); const buf = await zipDir(catalog); if (saveLocalZip) { deleteFile(storagePath); fs__default.default.writeFileSync(storagePath, buf); } return Promise.resolve(buf); } catch (error) { return Promise.reject({ msg: error.message }); } } function zipDir(catalog) { const jszip = new JSZip__default.default(); readDir(jszip, catalog); return jszip.generateAsync({ type: "nodebuffer", // 压缩类型 compression: "DEFLATE", // 压缩算法 compressionOptions: { level: 6 // 压缩级别 } }); } function readDir(jszip, catalog) { const files = fs__default.default.readdirSync(catalog); files.forEach((fileName) => { const fillPath = path2__default.default.join(catalog, "./", fileName); const fileState = fs__default.default.statSync(fillPath); if (fileState.isDirectory()) { const dirZip = jszip.folder(fileName); dirZip && readDir(dirZip, fillPath); } else { jszip.file(fileName, fs__default.default.readFileSync(fillPath)); } }); } function viteAutoDeployPlugin(config) { let viteConfig = void 0; return { name: "vite-auto-deploy", apply: "build", // 在什么模式下执行(构建还是开发) enforce: "post", // 插件执行顺序 /* 在解析 Vite 配置后的回调函数 */ configResolved(resolvedConfig) { viteConfig = resolvedConfig; }, /* 在构建完成后的回调 */ async closeBundle() { try { await viteAutoDeply(config, viteConfig); } catch (error) { } } }; } async function viteAutoDeply(config, viteConfig) { try { const newConfig = { uploadUrl: "", projectName: "", zipName: "viteAutoDeploy.zip", saveLocalZip: false, passphrase: "vite-auto-deploy", autoDeploy: true, root: void 0, outDir: void 0, headers: {}, resetKey: false, debug: false, ...config || {} }; const autoDeploy = newConfig.autoDeploy ?? true; if (autoDeploy) { const spinner = ora__default.default({ spinner: "dots" }); const uploadUrl = newConfig.uploadUrl || false; const projectName = newConfig.projectName || false; console.log(""); spinner.start(chalk__default.default.blue("\u5F00\u59CB\u81EA\u52A8\u90E8\u7F72...")); if (!uploadUrl) { spinner.fail(chalk__default.default.red("\u81EA\u52A8\u90E8\u7F72\u5931\u8D25,\u8BF7\u914D\u7F6EuploadUrl")); } else if (!projectName) { spinner.fail(chalk__default.default.red("\u81EA\u52A8\u90E8\u7F72\u5931\u8D25,\u8BF7\u914D\u7F6EprojectName")); } else { newConfig.root = newConfig.root || viteConfig?.root || process.cwd(); let outDir = newConfig.outDir || viteConfig?.build?.outDir || "dist"; outDir = path2__default.default.isAbsolute(outDir) ? outDir : path2__default.default.join(newConfig.root, outDir); outDir = path2__default.default.resolve(outDir); newConfig.outDir = outDir; await autoDeply(newConfig, newConfig.outDir, spinner); typeof newConfig.success === "function" && newConfig.success(newConfig, viteConfig); } console.log(""); } return Promise.resolve(true); } catch (error) { return Promise.reject(false); } } async function autoDeply(config, outDir, spinner) { const uploadUrl = config.uploadUrl || ""; const projectName = config.projectName || ""; const passphrase = config.passphrase || "vite-auto-deploy"; const keyPathInfo = getecretKeyPath(config.saveSecretKeyDirectory); const { publicKey, privateKey, keyPairId } = getSecretKey(passphrase, keyPathInfo, config.resetKey || false, config.debug || false); config.debug && console.log("\u65E5\u5FD7: \u516C\u5319=======" + publicKey); config.debug && console.log("\u65E5\u5FD7: \u79C1\u5319=======" + privateKey); config.debug && console.log("\u65E5\u5FD7: \u5BC6\u5319\u5BF9ID==== " + keyPairId); try { const zipName = `${config.zipName || "vite-auto-deploy"}.zip`; const zipBuffer = await createZip(outDir, zipName, config.saveLocalZip); const fileSHA256 = calculateSHA256(zipBuffer); const timestamp = (/* @__PURE__ */ new Date()).getTime(); const postData = { projectName, keyPairId, sign: "", timestamp }; const srd = Math.floor(timestamp % 1e4 * 333.3).toString(); const signStr = `${calculateSHA256(srd)}${fileSHA256}${keyPairId}${calculateSHA256(projectName.toString())}`; postData.sign = signEd25519(signStr, privateKey, passphrase); await uploading(uploadUrl, zipBuffer, zipName, postData, config.headers || {}, config.rejectUnauthorized || false, config.debug || false); spinner.succeed(chalk__default.default.bold.green(`\u81EA\u52A8\u90E8\u7F72\u5B8C\u6210!`)); return Promise.resolve(true); } catch (err) { config.debug && console.log("\u65E5\u5FD7: \u9519\u8BEF\u4FE1\u606F2:", err); if (err.code === 427) { spinner.fail(chalk__default.default.yellow("\u81EA\u52A8\u90E8\u7F72\u5931\u8D25,\u8BF7\u628A\u4EE5\u4E0B\u516C\u5319\u548C\u5BC6\u5319\u5BF9ID\u6DFB\u52A0\u5230\u670D\u52A1\u7AEF\uFF1A")); spinner.info("\u516C\u5319\uFF1A"); spinner.warn(chalk__default.default.green(publicKey)); spinner.info("\u5BC6\u5319\u5BF9ID\uFF1A"); spinner.warn(`${chalk__default.default.green(keyPairId)}`); console.log(""); } else { spinner.fail(chalk__default.default.bold.red(err.message || err.msg || "\u81EA\u52A8\u90E8\u7F72\u5931\u8D25")); console.log(""); } spinner.fail(chalk__default.default.yellow("\u6E29\u99A8\u63D0\u793A\uFF1A\u5EFA\u8BAE\u670D\u52A1\u5668\u8FD4\u56DE427\u8868\u793A\u516C\u5319\u4E0D\u53EF\u4FE1(\u5373\u6CA1\u6709\u628A\u516C\u5319\u548C\u5BC6\u5319\u5BF9ID\u6DFB\u52A0\u5230\u670D\u52A1\u5668\u4E0A)")); return Promise.reject(false); } } async function uploading(urlStr, buffer, zipName, postData, headers, rejectUnauthorized, debug) { try { const formData = new formdataNode.FormData(); const file = new formdataNode.File([buffer], zipName, { type: "application/zip" }); formData.append("file", file, zipName); for (const key in postData) { formData.append(key, postData[key]); } debug && console.log("\u65E5\u5FD7: \u6E90\u4EE3\u7801\u4E0A\u4F20url:", urlStr); debug && console.log("\u65E5\u5FD7: \u6E90\u4EE3\u7801\u4E0A\u4F20body:", postData); debug && console.log("\u65E5\u5FD7: \u6E90\u4EE3\u7801\u4E0A\u4F20headers:", headers); const res = await fetch__default.default(urlStr, { method: "POST", body: formData, headers, agent: rejectUnauthorized ? new https__default.default.Agent({ rejectUnauthorized: false }) : void 0 }); const result = await res.json(); debug && console.log("\u65E5\u5FD7: \u8FD4\u56DE\u7ED3\u679C", result); if (result && typeof result === "object" && !Array.isArray(result) && "code" in result && "msg" in result) { if (result.code === 200) { return Promise.resolve("\u90E8\u7F72\u6210\u529F"); } else { return Promise.reject({ msg: typeof result.msg === "string" ? result.msg : "\u90E8\u7F72\u5931\u8D25", code: typeof result.code === "number" ? result.code : -102 }); } } else { return Promise.reject({ msg: "\u7ED3\u679C\u672A\u77E5\uFF0C\u670D\u52A1\u5668\u8FD4\u56DE\u5185\u5BB9\u4E0D\u662F\u4E00\u4E2A\u6709\u6548\u7684json,-BD003" }); } } catch (error) { debug && console.log("\u65E5\u5FD7: \u9519\u8BEF\u4FE1\u606F1: ", error); return Promise.reject({ msg: "\u4E0A\u4F20\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5url\u662F\u5426\u6B63\u786E\u6216\u670D\u52A1\u5668\u4EE3\u7801\u662F\u5426\u6709\u8BEF,-BD002" }); } } function isFunction(arg) { return typeof arg === "function"; } function isRegExp(arg) { return Object.prototype.toString.call(arg) === "[object RegExp]"; } function readAllFile(root, reg) { let resultArr = []; if (fs__default.default.existsSync(root)) { const state = fs__default.default.lstatSync(root); if (state.isDirectory()) { const files = fs__default.default.readdirSync(root); files.forEach((file) => { const t = readAllFile(path2__default.default.join(root, "/", file)); resultArr = resultArr.concat(t); }); } else { { resultArr.push(root); } } } return resultArr; } function viteCompressionPlugin(config = {}) { let viteConfig = null; return { name: "vite-mxp-compression", apply: "build", enforce: "post", configResolved(resolvedConfig) { viteConfig = resolvedConfig; }, closeBundle() { viteCompression(config, viteConfig); } }; } function viteCompression(config, viteConfig) { const newConfig = { verbose: true, threshold: 1024, filter: void 0, disable: false, algorithm: "gzip", ext: ".gz", compressionOptions: void 0, deleteOriginFile: false, ...config || {} }; const spinner = ora__default.default({ spinner: "dots2" }); try { const { disable = false, filter = /\.(js|mjs|json|css|html)$/i, verbose = true, algorithm = "gzip", threshold = 1025, compressionOptions = {}, deleteOriginFile = false, success } = newConfig; if (!disable) { newConfig.root = newConfig.root || viteConfig?.root || process.cwd(); let outDir = newConfig.outDir || viteConfig?.build?.outDir || "dist"; outDir = path2__default.default.isAbsolute(outDir) ? outDir : path2__default.default.join(newConfig.root, outDir); outDir = path2__default.default.resolve(outDir); newConfig.outDir = outDir; const ext = newConfig.ext ? newConfig.ext : algorithm === "gzip" ? ".gz" : ".br"; let files = readAllFile(outDir) || []; if (!files.length) { return; } console.log(""); spinner.start(chalk__default.default.blue("\u6B63\u5728\u538B\u7F29\u6587\u4EF6...")); files = filterFiles(files, filter); const compressOptions = getCompressionOptions(algorithm, compressionOptions); const compressInfoMap = /* @__PURE__ */ new Map(); files.forEach((filePath) => { const { size: oldSize } = fs__default.default.statSync(filePath); if (oldSize >= threshold) { let content = fs__default.default.readFileSync(filePath); deleteOriginFile && deleteFile(filePath); try { content = Buffer.from(compress(content, algorithm, compressOptions)); } catch (error) { spinner.fail(`vite-mxp-compression: ${chalk__default.default.bold.green(`\u538B\u7F29\u5931\u8D25\uFF1A${filePath}`)}`); } const size = content.byteLength; const fullPath = getOutputFileName(filePath, ext); compressInfoMap.set(filePath, { size: size / 1024, oldSize: oldSize / 1024, fullPath }); fs__default.default.writeFileSync(fullPath, content); } }); spinner.succeed(chalk__default.default.bold.green(`\u538B\u7F29\u5B8C\u6210, \u538B\u7F29\u7B97\u6CD5\uFF1A${algorithm}${verbose ? ", \u5DF2\u538B\u7F29\u6587\u4EF6\uFF1A" : ""}`)); verbose && handleOutputLogger(compressInfoMap, algorithm, outDir); typeof success === "function" && success(newConfig, viteConfig); } } catch (error) { console.log(`vite-mxp-compression: ${chalk__default.default.bold.red(error.message)}`); } } function filterFiles(files, filter) { const isRe = isRegExp(filter); const isFn = isFunction(filter); if (isRe) { return files.filter((file) => filter.test(file)); } else if (isFn) { return files.filter((file) => filter(file)); } return files; } function getCompressionOptions(algorithm, compressionOptions = {}) { const defaultOptions = { gzip: { level: zlib__default.default.constants.Z_BEST_COMPRESSION }, deflate: { level: zlib__default.default.constants.Z_BEST_COMPRESSION }, deflateRaw: { level: zlib__default.default.constants.Z_BEST_COMPRESSION }, brotliCompress: { params: { [zlib__default.default.constants.BROTLI_PARAM_QUALITY]: zlib__default.default.constants.BROTLI_MAX_QUALITY, [zlib__default.default.constants.BROTLI_PARAM_MODE]: zlib__default.default.constants.BROTLI_MODE_TEXT } } }; return { ...defaultOptions[algorithm], ...compressionOptions }; } function compress(content, algorithm, options = {}) { switch (algorithm) { case "brotliCompress": return zlib__default.default.brotliCompressSync(content, options); case "deflate": return zlib__default.default.deflateSync(content, options); case "deflateRaw": return zlib__default.default.deflateRawSync(content, options); default: return zlib__default.default.gzipSync(content, options); } } function getOutputFileName(filepath, ext) { const compressExt = ext.startsWith(".") ? ext : `.${ext}`; return `${filepath}${compressExt}`; } function handleOutputLogger(compressInfoMap, algorithm, outDir) { const keyLengths = Array.from(compressInfoMap.keys(), (name) => name.length); const maxKeyLength = Math.max(...keyLengths); console.log(""); compressInfoMap.forEach((value, name) => { const { size, oldSize, fullPath } = value; const rName = fullPath.replace(outDir, ""); const sizeStr = `\u539F\u59CB\u5927\u5C0F\uFF1A${oldSize.toFixed(2)}kb / ${algorithm}\u538B\u7F29\u540E: ${size.toFixed(2)}kb`; const spacings = " ".repeat(2 + maxKeyLength - name.length); const basename = path2__default.default.basename(outDir); console.log(`${chalk__default.default.dim(basename)}${chalk__default.default.blueBright(rName)}${spacings} ${chalk__default.default.dim(sizeStr)}`); }); console.log(" "); } exports.autoDeply = autoDeply; exports.viteAutoDeployPlugin = viteAutoDeployPlugin; exports.viteAutoDeply = viteAutoDeply; exports.viteCompressionPlugin = viteCompressionPlugin;