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
text/typescript
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, "/");
}