2020-02-21 00:59:18 +02:00
|
|
|
const fs = require('fs-extra');
|
2020-11-05 18:58:23 +02:00
|
|
|
const execa = require('execa');
|
2020-02-21 00:59:18 +02:00
|
|
|
|
|
|
|
const utils = {};
|
|
|
|
|
|
|
|
utils.isLinux = () => {
|
|
|
|
return process && process.platform === 'linux';
|
|
|
|
};
|
|
|
|
|
|
|
|
utils.isWindows = () => {
|
|
|
|
return process && process.platform === 'win32';
|
|
|
|
};
|
|
|
|
|
|
|
|
utils.isMac = () => {
|
|
|
|
return process && process.platform === 'darwin';
|
|
|
|
};
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
utils.execCommandVerbose = function(commandName, args = []) {
|
|
|
|
console.info(`> ${commandName}`, args && args.length ? args : '');
|
|
|
|
const promise = execa(commandName, args);
|
|
|
|
promise.stdout.pipe(process.stdout);
|
2022-01-01 16:06:57 +02:00
|
|
|
promise.stderr.pipe(process.stderr);
|
2020-11-05 18:58:23 +02:00
|
|
|
return promise;
|
|
|
|
};
|
|
|
|
|
2020-02-21 00:59:18 +02:00
|
|
|
utils.execCommand = function(command) {
|
|
|
|
const exec = require('child_process').exec;
|
|
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
2020-06-03 18:07:50 +02:00
|
|
|
exec(command, { maxBuffer: 1024 * 1024 }, (error, stdout) => {
|
2020-02-21 00:59:18 +02:00
|
|
|
if (error) {
|
2020-06-23 00:06:47 +02:00
|
|
|
|
2020-06-24 17:33:35 +02:00
|
|
|
// Special case for robocopy, which will return non-zero error codes
|
|
|
|
// when sucessful. Doc is very imprecise but <= 7 seems more or less
|
|
|
|
// fine and >= 8 seems more errorish. https://ss64.com/nt/robocopy-exit.html
|
|
|
|
if (command.indexOf('robocopy') === 0 && error.code <= 7) {
|
2020-06-23 00:06:47 +02:00
|
|
|
resolve(stdout.trim());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-07-23 11:33:12 +02:00
|
|
|
if (error.signal === 'SIGTERM') {
|
2020-02-21 00:59:18 +02:00
|
|
|
resolve('Process was killed');
|
|
|
|
} else {
|
2020-06-24 17:33:35 +02:00
|
|
|
const newError = new Error(`Code: ${error.code}: ${error.message}: ${stdout.trim()}`);
|
|
|
|
reject(newError);
|
2020-02-21 00:59:18 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
resolve(stdout.trim());
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
utils.dirname = function(path) {
|
|
|
|
if (!path) throw new Error('Path is empty');
|
2020-03-14 01:46:14 +02:00
|
|
|
const s = path.split(/\/|\\/);
|
2020-02-21 00:59:18 +02:00
|
|
|
s.pop();
|
|
|
|
return s.join('/');
|
|
|
|
};
|
|
|
|
|
|
|
|
utils.basename = function(path) {
|
|
|
|
if (!path) throw new Error('Path is empty');
|
2020-03-14 01:46:14 +02:00
|
|
|
const s = path.split(/\/|\\/);
|
2020-02-21 00:59:18 +02:00
|
|
|
return s[s.length - 1];
|
|
|
|
};
|
|
|
|
|
|
|
|
utils.filename = function(path, includeDir = false) {
|
|
|
|
if (!path) throw new Error('Path is empty');
|
|
|
|
let output = includeDir ? path : utils.basename(path);
|
|
|
|
if (output.indexOf('.') < 0) return output;
|
|
|
|
|
|
|
|
output = output.split('.');
|
|
|
|
output.pop();
|
|
|
|
return output.join('.');
|
|
|
|
};
|
|
|
|
|
|
|
|
utils.toSystemSlashes = function(path) {
|
|
|
|
if (utils.isWindows()) return path.replace(/\//g, '\\');
|
|
|
|
return path.replace(/\\/g, '/');
|
|
|
|
};
|
|
|
|
|
|
|
|
utils.fileExtension = function(path) {
|
|
|
|
if (!path) throw new Error('Path is empty');
|
|
|
|
|
2020-03-14 01:46:14 +02:00
|
|
|
const output = path.split('.');
|
2020-02-21 00:59:18 +02:00
|
|
|
if (output.length <= 1) return '';
|
|
|
|
return output[output.length - 1];
|
|
|
|
};
|
|
|
|
|
|
|
|
utils.replaceFileText = async function(filePath, regex, toInsert) {
|
|
|
|
const content = await fs.readFile(filePath, 'utf8');
|
|
|
|
const newContent = content.replace(regex, toInsert);
|
|
|
|
if (newContent === content) return Promise.resolve();
|
|
|
|
await fs.writeFile(filePath, newContent);
|
|
|
|
};
|
|
|
|
|
|
|
|
utils.copyDir = async function(src, dest, options) {
|
2023-06-01 13:02:36 +02:00
|
|
|
options = { excluded: [],
|
|
|
|
delete: true, ...options };
|
2020-02-21 00:59:18 +02:00
|
|
|
|
|
|
|
src = utils.toSystemSlashes(src);
|
|
|
|
dest = utils.toSystemSlashes(dest);
|
|
|
|
|
2020-06-23 00:06:47 +02:00
|
|
|
await utils.mkdir(dest);
|
2020-02-21 00:59:18 +02:00
|
|
|
|
|
|
|
if (utils.isWindows()) {
|
2020-06-23 00:06:47 +02:00
|
|
|
let cmd = ['robocopy'];
|
|
|
|
cmd.push(`"${src}"`);
|
|
|
|
cmd.push(`"${dest}"`);
|
|
|
|
cmd.push('/e');
|
2020-10-09 19:35:46 +02:00
|
|
|
cmd.push('/r:0');
|
2020-06-23 00:06:47 +02:00
|
|
|
if (options.delete) cmd.push('/purge');
|
|
|
|
|
2020-02-21 00:59:18 +02:00
|
|
|
if (options.excluded.length) {
|
2020-06-23 00:06:47 +02:00
|
|
|
cmd.push('/xd');
|
|
|
|
cmd = cmd.concat(options.excluded.map(p => `"${utils.toSystemSlashes(p)}"`).join(' '));
|
2020-02-21 00:59:18 +02:00
|
|
|
}
|
|
|
|
|
2020-06-23 00:06:47 +02:00
|
|
|
await utils.execCommand(cmd.join(' '));
|
2020-02-21 00:59:18 +02:00
|
|
|
} else {
|
|
|
|
let excludedFlag = '';
|
|
|
|
if (options.excluded.length) {
|
2020-05-21 10:14:33 +02:00
|
|
|
excludedFlag = options.excluded.map(f => {
|
2020-02-21 00:59:18 +02:00
|
|
|
return `--exclude "${f}"`;
|
|
|
|
}).join(' ');
|
|
|
|
}
|
|
|
|
|
2020-03-10 01:24:57 +02:00
|
|
|
let deleteFlag = '';
|
|
|
|
if (options.delete) deleteFlag = '--delete';
|
|
|
|
|
|
|
|
await utils.execCommand(`rsync -a ${deleteFlag} ${excludedFlag} "${src}/" "${dest}/"`);
|
2020-02-21 00:59:18 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-10-11 12:43:22 +02:00
|
|
|
// Occasionally, fs.mkdirp throws a "EEXIST" error if the directory already
|
|
|
|
// exists, while it should actually ignore the error. So we have this wrapper
|
|
|
|
// that actually handle the error. It means in general this method should be
|
|
|
|
// preferred to avoid random failures on CI or when building the app.
|
|
|
|
//
|
|
|
|
// https://github.com/laurent22/joplin/issues/6935#issuecomment-1274404470
|
2020-06-23 00:06:47 +02:00
|
|
|
utils.mkdir = async function(dir) {
|
|
|
|
if (utils.isWindows()) {
|
|
|
|
return utils.execCommand(`if not exist "${utils.toSystemSlashes(dir)}" mkdir "${utils.toSystemSlashes(dir)}"`);
|
|
|
|
} else {
|
2022-10-11 12:43:22 +02:00
|
|
|
try {
|
|
|
|
// Can't return right away, or the exception won't be caught
|
|
|
|
const result = await fs.mkdirp(dir);
|
|
|
|
return result;
|
|
|
|
} catch (error) {
|
|
|
|
// Shouldn't happen but sometimes does. So we ignore the error in
|
|
|
|
// this case.
|
|
|
|
if (error.code === 'EEXIST') return;
|
|
|
|
throw error;
|
|
|
|
}
|
2020-06-23 00:06:47 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-10-11 12:43:22 +02:00
|
|
|
utils.mkdirp = async function(dir) {
|
|
|
|
return utils.mkdir(dir);
|
|
|
|
};
|
|
|
|
|
2020-02-21 00:59:18 +02:00
|
|
|
utils.copyFile = async function(src, dest) {
|
|
|
|
await fs.copy(src, dest);
|
|
|
|
};
|
|
|
|
|
|
|
|
utils.rootDir = function() {
|
2020-11-05 18:58:23 +02:00
|
|
|
return utils.dirname(utils.dirname(utils.dirname(__dirname)));
|
2020-02-21 00:59:18 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
utils.registerGulpTasks = function(gulp, tasks) {
|
|
|
|
for (const taskName in tasks) {
|
|
|
|
gulp.task(taskName, tasks[taskName].fn);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-01-18 22:09:11 +02:00
|
|
|
utils.setPackagePrivateField = async function(filePath, value) {
|
|
|
|
const text = await fs.readFile(filePath, 'utf8');
|
|
|
|
const obj = JSON.parse(text);
|
|
|
|
if (!value) {
|
|
|
|
delete obj.private;
|
|
|
|
} else {
|
|
|
|
obj.private = true;
|
|
|
|
}
|
|
|
|
await fs.writeFile(filePath, JSON.stringify(obj, null, 2), 'utf8');
|
|
|
|
};
|
|
|
|
|
2021-09-04 14:43:25 +02:00
|
|
|
utils.insertContentIntoFile = async (filePath, marker, contentToInsert, createIfNotExist = false) => {
|
|
|
|
const fs = require('fs-extra');
|
|
|
|
const fileExists = await fs.pathExists(filePath);
|
|
|
|
|
|
|
|
if (!fileExists) {
|
|
|
|
if (!createIfNotExist) throw new Error(`File not found: ${filePath}`);
|
|
|
|
await fs.writeFile(filePath, `${marker}\n${contentToInsert}\n${marker}`);
|
|
|
|
} else {
|
|
|
|
let content = await fs.readFile(filePath, 'utf-8');
|
|
|
|
// [^]* matches any character including new lines
|
|
|
|
const regex = new RegExp(`${marker}[^]*?${marker}`);
|
|
|
|
content = content.replace(regex, `${marker}\n${contentToInsert}\n${marker}`);
|
|
|
|
await fs.writeFile(filePath, content);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-09-04 16:15:25 +02:00
|
|
|
utils.getFilename = (path) => {
|
|
|
|
const lastPart = path.split('/').pop();
|
|
|
|
if (lastPart.indexOf('.') < 0) return lastPart;
|
|
|
|
|
|
|
|
const splitted = lastPart.split('.');
|
|
|
|
splitted.pop();
|
|
|
|
return splitted.join('.');
|
|
|
|
};
|
|
|
|
|
2022-10-14 12:05:21 +02:00
|
|
|
utils.msleep = (ms) => {
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
setTimeout(() => {
|
|
|
|
resolve();
|
|
|
|
}, ms);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2020-02-21 00:59:18 +02:00
|
|
|
module.exports = utils;
|