不説廢話,直接上貨。
webpack打包後自動部署插件:webpack-auto-upload-j
插件項目地址:https://github.com/jiangji1/w...
在項目中安裝該模塊
npm i webpack-auto-upload-j --save-dev
webpack導入插件並配置
const WebpackAutoUploadJ = require('webpack-auto-upload-j')
{
plugins:
[ new WebpackAutouploadJ({
path: path.resolve(__dirname, '../serviceConf.json'),// 服務器配置文件路徑
key: 'web' }), // 服務器配置文件對象key值
]
}
// serviceConf.json配置文件,根目錄下配置
{
"web": {
// 測試環境服務器配置
"build_upload_test": {
"host": "xxx.xxx.xxx.xxx",
"port": 22,
"user": "xxx",// 服務器用户名
"password": "xxxx",// 服務器遠程連接密碼
"entryDir": "dist",// 打包目錄文件,會將該目錄下所有文件上傳到服務器serviceDir變量指定路徑下
"serviceDir": "/usr/local/tomcat/webapps/test" // 服務器web存放目錄
},
// 生產環境服務器配置
"build_upload_pro": {
"host": "xxx.xxx.xxx.xxx",
"port": 22,
"user": "xxx",
"password": "xxxx",
"entryDir": "dist",
"serviceDir": "/usr/local/tomcat/webapps/test"
}
}
}
// package.json
"up": {
"kaiguan": 2 // 0:表示不上傳部署,1:表示部署到測試環境,2:表示部署到生產環境
}
上傳服務器顯示結果:
插件源碼解析:
// index.js
var tool = require('./tool.js')
var path = require('path')
var fs = require('fs')
var readAndDelEvery = tool.readAndDelEvery
var readAndPut = tool.readAndPut
var opt // 服務器配置變量
function Gouzi(options) {
if (!fs.existsSync(options.path)) {
return
}
opt = JSON.parse(fs.readFileSync(options.path))[options.key] // 讀取服務器配置文件
}
Gouzi.prototype.apply = function(compiler) {
compiler.plugin("beforeRun", function() {
handle({ compiler, type: 'beforeRun' })
})
compiler.plugin("done", function(params) {
// 當webpack打包完後會執行這個鈎子
handle({ compiler, type: 'done' })
});
};
function handle ({ compiler, type }) {
let opt2 = JSON.parse(JSON.stringify(opt))
if (!opt2) {
process.exit()
return
}
const pajp = path.resolve(compiler.context, 'package.json') // 讀取package.json中up配置
if (fs.existsSync(pajp)) {
const config = JSON.parse(fs.readFileSync(pajp))
if (Object.prototype.hasOwnProperty.call(config, 'up')) {
// 判斷打包到哪個環境
if (config.up.kaiguan === 0) {
return
} else if (config.up.kaiguan === 1) {
opt2 = opt2.build_upload_test
} else if (config.up.kaiguan === 2) {
opt2 = opt2.build_upload_pro
} else {
return
}
}
}
opt2.serviceConfig = {
host: opt2.host,
port: opt2.port,
user: opt2.user,
password: opt2.password,
}
// 可以多入口上傳部署
if (Array.isArray(opt2.entryDir)) {
var arr = []
opt2.entryDir.forEach((v, i) => arr.push({
entryDir: v,
serviceDir: opt2.serviceDir[i],
serviceConfig: opt2.serviceConfig
}))
arr.forEach(v => (
type === 'done'
? readAndPut(v, compiler.context)
: readAndDelEvery(v, compiler.context)
))
} else {
if (Array.isArray(opt2.serviceDir)) {
var arr = []
opt2.serviceDir.forEach((v, i) => arr.push({
entryDir: opt2.entryDir,
serviceDir: v,
serviceConfig: opt2.serviceConfig
}))
arr.forEach(v => (
type === 'done'
? readAndPut(v, compiler.context)
: readAndDelEvery(v, compiler.context)
))
} else {
type === 'done'
? readAndPut(opt2, compiler.context)
: readAndDelEvery(opt2, compiler.context)
}
}
}
module.exports = Gouzi
// tool.js
const fs = require('fs')
const path = require('path')
const cc = require('ssh2-sftp-client') // node的ftp包,用於上傳下載
function readAll (dirPath, currentPath) {
dirPath = path.resolve(
currentPath,
dirPath
)
const paths = [dirPath]
const aaa = []
let res = []
while (paths.length) {
const head = paths.shift()
aaa.push(head)
if (!fs.existsSync(head)) continue
const f = fs.statSync(head)
if (!f.isDirectory()) res.push(head)
const d = fs.readdirSync(head)
d.forEach(v => {
const p = path.resolve(head, v)
if (!fs.existsSync(p)) return
const f = fs.statSync(p)
if (!f.isDirectory()) res.push(p)
else paths.push(p)
})
}
const reg = new RegExp(dirPath.replace(/\\/g, '\\\\'), 'g')
return res.map(v => v.replace(reg, '').replace(/\\/g, '/'))
}
// 將指定目錄文件上傳到服務器指定目錄
async function uploadAll ({ serviceDir, allFiles, config, entryDir }, currentPath) {
const successArr = []
const failArr = []
const c = new cc(config)
let a
await c.connect(config)
for (const i in allFiles) {
const v = allFiles[i]
const localDir = path.resolve(currentPath, entryDir, v.slice(1))
const remoteDir = serviceDir + v
try {
const a = await c.exists(remoteDir.slice(0, remoteDir.lastIndexOf('/')))
if (!a) {
await c.mkdir(remoteDir.slice(0, remoteDir.lastIndexOf('/')), true)
}
const p = await c.put(
localDir,
serviceDir + v
)
successArr.push(`${localDir} to: ${remoteDir}`)
} catch (e) {
failArr.push({
failFile: `${localDir} to: ${remoteDir}`,
reason: e + ''
})
}
}
c.end()
successArr.length && console.log(`
\x1B[32m[
上傳完畢. uploaded \n
成功列表.successFiles \n
${successArr.map(v => `success: ${v} \n`)}
]\x1B[39m
`)
failArr.length && console.log(`
\x1B[31m[
失敗列表.failedFiles \n
${failArr.map(v => `failed: ${JSON.stringify(v, 0, ' ')} \n`)}
]\x1B[39m
`)
}
async function delEvery ({ serviceDir, allFiles, config, entryDir }, uploadAfterDel) {
const c = new cc(config)
await c.connect(config)
for (const i in allFiles) {
const v = allFiles[i]
const remoteDir = serviceDir + v
try {
const a = await c.exists(remoteDir.slice(0, remoteDir.lastIndexOf('/')))
if (a) {
await c.delete(remoteDir)
}
} catch (e) {
}
}
c.end()
uploadAfterDel()
}
var delObj
function readAndDelEvery (item, currentPath) {
const allDelFiles = readAll(item.entryDir, currentPath)
delObj = {
entryDir: item.entryDir,
serviceDir: item.serviceDir,
allFiles: allDelFiles,
config: item.serviceConfig,
}
}
function readAndPut (item, currentPath) {
const allFiles = readAll(item.entryDir, currentPath)
var obj = {
entryDir: item.entryDir,
serviceDir: item.serviceDir,
allFiles,
config: item.serviceConfig,
}
delEvery(obj, () => uploadAll(obj, currentPath))
}
module.exports = {
readAndDelEvery,
readAndPut,
}
項目地址:https://github.com/Revelation...
參考:
https://juejin.im/post/5de38e...