UNPKG

folder-upload-webpack-plugin

Version:

Upload your folder to ssh sftp server after build, add support to 'webpack --watch'

222 lines (192 loc) 7.39 kB
import {spawnSync} from "child_process"; import * as webpack from 'webpack' import * as path from "path"; import * as fs from "fs-extra"; import chalk from "chalk"; import readline from "readline-sync"; import {Chalk} from "chalk"; import SshClient from "./utils/ssh"; import {ConnectConfig} from "ssh2"; export interface Options { confirmation?: boolean, server: Array<ConnectConfig>, paths?: () => {[key: string]: string}, clear?: boolean, enable?: boolean, logging?: boolean, progress?: boolean, firstEmit?: boolean, archive?: string, chmod?: any, ignore?: any, symlink?: { path: string, force: boolean }, before?: Array<any> after?: Array<any> ssh?: any streams?: number } export default class FolderUploadWebpackPlugin { private readonly pathList: any[]; private readonly cl: {[key: string]: boolean }; private readonly options: Options; private readonly paths: {[key: string]: string}; private ssh: SshClient; constructor(options: Options = {server: []}) { if (!options.paths) { throw new Error('paths not set') } options.enable = options.enable === undefined ? true : options.enable; options.clear = options.clear || false; options.logging = !options.logging ? false : options.logging; options.progress = !options.progress ? true : options.progress; // options.folderName = options.folder.match(/([^\/]*)\/*$/)[1]; options.firstEmit = options.firstEmit === undefined ? true : options.firstEmit; options.chmod = !options.chmod ? 0o644 : options.chmod; options.archive = options.archive ? options.archive : 'FolderUploadWebpackPlugin.zip'; options.ignore = options.ignore ? options.ignore : null; this.ssh = new SshClient(options.logging, options.progress, options.streams); options.confirmation = options.confirmation ? options.confirmation : false; this.paths = options.paths ? options.paths() : {}; options.after = options.after ? options.after : []; options.before = options.before ? options.before : []; this.pathList = []; this.cl = {}; this.options = options; this.upload = this.upload.bind(this); } apply(compiler: webpack.Compiler) { if(!this.options.enable) return; compiler.hooks.afterDone.tap('folder-upload-webpack-plugin', this.upload); } pathConverter(local: string, remote: string, size = 0) { return { name: path.basename(local), path: path.dirname(local), remotePath: path.resolve(remote) + '/', fillPath: local, size: size } } walk(dirs: {[key: string]: string}) { for (let i in dirs) { const stat = fs.statSync(i); if (!stat.isDirectory()) { if (!this.options.ignore || !path.join(i).match(this.options.ignore)) { this.pathList.push(this.pathConverter(path.join(i), path.join(dirs[i]), stat.size)); this.cl[path.join(dirs[i], '/')] = true; } continue; } const files = fs.readdirSync(i); for (const file of files) { const stat = fs.statSync(path.join(i, file)); if (stat.isDirectory()) { let data: {[key: string]: string} = {}; data[path.join(i, file)] = path.join(dirs[i], file); this.pathList.concat(this.walk(data)) } else { if (!this.options.ignore || !path.join(i, file).match(this.options.ignore)) { this.pathList.push(this.pathConverter(path.join(i, file), path.join(dirs[i]), stat.size)); this.cl[path.join(dirs[i], '/')] = true; } } } } return [this.pathList, Object.keys(this.cl)] } handleScript(script: string) { const [command, ...args] = script.split(' '); spawnSync(command, args, {stdio: 'inherit'}); } async upload(compilation: webpack.Stats) { const {clear, chmod, server} = this.options; if (this.options.before && this.options.before.length) { for (let i in this.options.before) { this.handleScript(this.options.before[i]) } } if (!this.options.confirmation || readline.keyInYN(chalk.bold.red("\nAre you sure you want to replace the server?"))) { if (this.options.firstEmit) { for (let i in server) { let [filesList, cl] = await this.walk(this.paths); server[i].port = server[i].port || 22; await this.ssh.connect(server[i]); for (let i in this.paths) { if (clear) { try { this.log('Clearing remote folder ' + this.paths[i] + '* ...', chalk.red); await this.ssh.rmdir('rm -rf ' + formatRemotePath(this.paths[i]) + '*'); } catch (e) { } } } for (let i in cl) { let dir = formatRemotePath(cl[i]); try { if (!await this.ssh.exists(dir)) { this.log('MAKE remote folder ' + dir + ' ...', chalk.green); await this.ssh.mkdir(dir, true).catch(() => null); await this.ssh.chmod(dir, chmod).catch(() => null); } } catch (e) { } } this.log('Uploading...', chalk.green); await this.ssh.sendFile(filesList); this.log('end', chalk.green); await this.ssh.end(); } } } this.options.firstEmit = false; if (this.options.symlink) { this.log('Making symlink...', chalk.green); this.createSimlinks(this.options.symlink, compilation.compilation.compiler.outputPath, clear) } if (this.options.after && this.options.after.length) { for (let i in this.options.after) { this.handleScript(this.options.after[i]) } } } createSimlinks(options: { path: string, force: boolean }, inputPath: string, clear: boolean|undefined) { if (options.force || fs.existsSync(inputPath)) { const baseDir = process.cwd(); process.chdir(inputPath); const origin = path.relative(path.dirname(options.path), inputPath); this.log('Symlink path: ' + origin, chalk.green); try { if (clear) { if (fs.existsSync(options.path)) { fs.removeSync(options.path); } } fs.readlinkSync(options.path); // Raises if symlink doesn't exist fs.unlinkSync(options.path); } catch (e) { // symlink doesn't exist } finally { fs.symlinkSync(origin, options.path); } process.chdir(baseDir); } if (clear) { const parentPath = path.dirname(inputPath); const ignore = path.basename(inputPath); const dirs = fs.readdirSync(parentPath); for (let i in dirs) { if (!fs.statSync(path.join(parentPath, dirs[i])).isDirectory() || dirs[i] === ignore) continue; fs.removeSync(path.join(parentPath, dirs[i])); } } } log(text: string, formatter:Chalk = chalk.blue) { if (!this.options.logging) { return; } console.log(formatter(text)); } } function formatRemotePath(remotePath: string, filePath = '') { return (remotePath + '/' + filePath).replace(/\\/g, '/').replace(/\.\//g, "").replace(/\/\/+/g, "/"); }