@vite-electron-simple/core
Version:
一款支持在 vite 构建工具下,实现 electron 的开发、构建引入的脚手架,同时支持单一浏览器环境的开发和构建。支持全量的 vite 以及 electron-builder 的全部功能。
1,516 lines (1,439 loc) • 78.6 kB
JavaScript
#!/usr/bin/env node
'use strict';
var path = require('node:path');
var fs = require('node:fs');
var require$$2 = require('node:os');
var require$$3 = require('node:child_process');
var require$$0 = require('fs');
var path$1 = require('path');
var require$$2$1 = require('os');
var require$$3$1 = require('crypto');
var esbuild = require('esbuild');
var vite = require('vite');
var builder = require('electron-builder');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var require$$0__namespace = /*#__PURE__*/_interopNamespaceDefault(require$$0);
var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path$1);
function getDefaultExportFromCjs (x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}
var mvCommon$1 = {exports: {}};
var mvCommon = mvCommon$1.exports;
var hasRequiredMvCommon;
function requireMvCommon () {
if (hasRequiredMvCommon) return mvCommon$1.exports;
hasRequiredMvCommon = 1;
(function (module, exports) {
(function (global, factory) {
factory(exports, fs, path, require$$2, require$$3) ;
})(mvCommon, (function (exports, fs, path, os, node_child_process) {
/**
* 判断一个url是不是有有效的http格式
* @param { string } url 链接地址
* @returns { boolean } 是否是一个链接
* **/
const isValidUrl = (url) => {
return /^(http|https):\/\//.test(url);
};
/**
* 获取一个数据的类型
* @param { unknown } value 需要判断类型的数据
* @returns { string | null } 判断的数据类型
* **/
const getType = (value) => {
const typeResult = Object.prototype.toString.call(value);
const type = typeResult
.slice(typeResult.indexOf(' ') + 1, -1)
.toLocaleLowerCase();
if (type === 'array') {
if (Array.isArray(value))
return 'array';
else
return null;
}
return type;
};
/**
* 判断一个数据的类型是否为指定的类型
* @param { unknown } value 需要判断类型的数据
* @param { string | Array } type 期望的数据类型
* @return { boolean } 是否为期望的类型
* **/
const isType = (value, type) => {
const valueType = getType(value);
if (valueType === null)
throw new Error('invalid value...');
if (typeof type === 'string')
return valueType === type.toLocaleLowerCase();
return type.map((item) => item.toLocaleLowerCase()).includes(valueType);
};
/**
* 异步或同步延迟等待一段时间
* @param { number } timeout 等待时常,默认为 1S钟
* @param { boolean } sync 是否同步
* @returns { Promise<boolean> } 异步,是否执行完成
* **/
const sleep = (timeout = 1000, sync = false) => {
if (!sync) {
return new Promise((resolve) => {
const timer = setTimeout(() => {
clearTimeout(timer);
resolve(true);
}, timeout);
});
}
else {
return Promise.resolve(true);
}
};
/**
* 防抖方法
* @param { (...rest: Array<unknown>) => unknown } cb 方法
* @param { number } delay 防抖延迟时常, 默认为: 0
* @param { boolean } immediate 是否需要立即执行,默认为: false
* **/
const debounce = (cb, delay = 0, immediate = false) => {
let timer;
return function (...rest) {
clearTimeout(timer);
if (!timer && immediate)
cb.apply(this, rest);
const handler = () => {
clearTimeout(timer);
timer = null;
if (!immediate)
cb.apply(this, rest);
};
timer = setTimeout(handler, delay);
if (immediate && !timer)
cb.apply(this, rest);
};
};
/**
* 节流方法
* @param { (...rest: Array<unknown>) => unknown } cb 方法
* @param { number } delay 节流延迟时常, 默认值为: 0
* **/
const throttle = (cb, delay = 0) => {
let loading = false;
return function (...rest) {
if (!loading)
cb.apply(this, rest);
loading = true;
const timer = setTimeout(() => {
clearTimeout(timer);
loading = false;
}, delay);
};
};
/**
* 查看属性是否存在(原型链方式判断)
* @param { object } value 需要判断的值
* @param { string } attr key 值
* **/
const hasProperty = (value, attr) => {
return Reflect.has(value, attr);
};
/**
* 从一个字符串中查找指定的字符是否存在
* @param { string } str 需要查找的字符串
* @param { Array<string> | string } ident 查找的字符串内容,可以是字符串,也可以是字符串数组
* @param { boolean } absolute 是否绝对匹配
* @returns { boolean } 是否可以查询到结果
* **/
const findString = (str, ident, absolute = false) => {
if (isType(ident, 'array')) {
const findFunction = absolute
? (item) => !str.includes(item)
: (item) => str.includes(item);
const result = ident.find(findFunction);
return absolute ? !result : !!result;
}
return !!str.includes(ident);
};
var src = {exports: {}};
(function (module, exports) {
Object.defineProperty(exports, "__esModule", { value: true });
exports.isPlainObject = exports.clone = exports.recursive = exports.merge = exports.main = void 0;
module.exports = exports = main;
exports.default = main;
function main() {
var items = [];
for (var _i = 0; _i < arguments.length; _i++) {
items[_i] = arguments[_i];
}
return merge.apply(void 0, items);
}
exports.main = main;
main.clone = clone;
main.isPlainObject = isPlainObject;
main.recursive = recursive;
function merge() {
var items = [];
for (var _i = 0; _i < arguments.length; _i++) {
items[_i] = arguments[_i];
}
return _merge(items[0] === true, false, items);
}
exports.merge = merge;
function recursive() {
var items = [];
for (var _i = 0; _i < arguments.length; _i++) {
items[_i] = arguments[_i];
}
return _merge(items[0] === true, true, items);
}
exports.recursive = recursive;
function clone(input) {
if (Array.isArray(input)) {
var output = [];
for (var index = 0; index < input.length; ++index)
output.push(clone(input[index]));
return output;
}
else if (isPlainObject(input)) {
var output = {};
for (var index in input)
output[index] = clone(input[index]);
return output;
}
else {
return input;
}
}
exports.clone = clone;
function isPlainObject(input) {
return input && typeof input === 'object' && !Array.isArray(input);
}
exports.isPlainObject = isPlainObject;
function _recursiveMerge(base, extend) {
if (!isPlainObject(base))
return extend;
for (var key in extend) {
if (key === '__proto__' || key === 'constructor' || key === 'prototype')
continue;
base[key] = (isPlainObject(base[key]) && isPlainObject(extend[key])) ?
_recursiveMerge(base[key], extend[key]) :
extend[key];
}
return base;
}
function _merge(isClone, isRecursive, items) {
var result;
if (isClone || !isPlainObject(result = items.shift()))
result = {};
for (var index = 0; index < items.length; ++index) {
var item = items[index];
if (!isPlainObject(item))
continue;
for (var key in item) {
if (key === '__proto__' || key === 'constructor' || key === 'prototype')
continue;
var value = isClone ? clone(item[key]) : item[key];
result[key] = isRecursive ? _recursiveMerge(result[key], value) : value;
}
}
return result;
}
} (src, src.exports));
var srcExports = src.exports;
/**
*
* 是否是一个空对象
* @param { Object } value 需要判断的数据
* @returns { boolean } 是否为空对象
* **/
const isEmptyJSON = (value) => {
if (!isType(value, 'object'))
throw new Error('invalid isEmptyJSON parameter');
return !Object.keys(value).length;
};
/**
* 扁平化一个JSON对象
* @param { Object } obj 需要扁平化的对象
* @returns { Object } 返回扁平化之后的JSON对象
* **/
const flatJSON = (obj) => {
let value = {};
for (const key in obj) {
const item = obj[key];
if (isType(item, 'object')) {
value = srcExports.recursive(true, value, flatJSON(item));
}
else {
value[key] = item;
}
}
return value;
};
/**
* 将一个JSON对象按照指定的分隔符,拼接为特定的字符串
* @param { Object } content 内容
* @param { string } sep 分隔符字符串, 默认为空字符串
* @return { string } 处理完成之后的字符串
* **/
const splitJsonToContent = (content, sep = '') => {
content = flatJSON(content);
let fileContent = '';
for (const key in content) {
fileContent += `${key}${sep}${content[key]}\n`;
}
return fileContent;
};
/**
* 是否是一个空数组
* @param { Array<unknown> } value 需要判断的数据
* @returns { boolean } 是否为空数组
* **/
const isEmptyArray = (value) => {
if (!isType(value, 'array'))
throw new Error('invalid isEmptyArray parameter');
else
return !value.length;
};
/**
* 判断一个数据或一组数据均为指定的类型
* @param { string } type 类型名称 array、object 等使用原型链判断的类型末尾小写
* @param { Array } args 需要判断的数据
* @returns { boolean } 一个数据或一组数据是否均为指定的类型
* **/
const validType = (type, ...args) => {
return !args.find((item) => !isType(item, type));
};
/**
* 交集方法
* @param { Array<Array<unknown>> } args 数据列表
* @returns { Array<unknown> } 交集数组
* **/
const intersectionArrayList = (...args) => {
return args.reduce((acc, array) => {
return acc.filter((item) => array.includes(item));
});
};
/**
* 差集方法
* @param { Array<unknown> } baseArray 求差集的基准列表
* @param { Array<unknown> } args 差集列表
* @returns { Array<unknown> } 差集
* **/
const differenceArrayList = (baseArray, ...args) => {
const otherElementsSet = new Set(...args);
return baseArray.filter((item) => !otherElementsSet.has(item));
};
/**
* 并集方法
* @param { Array<Array<unknown>> } args 求并集的数组
* @returns { Array<unknown> } 并集数组
* **/
const unionArrayList = (...args) => {
return [...new Set(args.flat(1))];
};
/**
* 包版本管理器配置对照表
* **/
const packageMangerViewer = new Map([
['yarn', 'yarn.lock'],
['npm', 'package-lock.json'],
['pnpm', 'pnpm-lock.yaml']
]);
/**
* 获取当前项目的包版本管理器, 目前支持 yarn|npm|pnpm
* @param { string } targetPath 目标路径
* **/
const getPackageMangerName = (targetPath = process.cwd()) => {
for (const [key, value] of packageMangerViewer) {
if (fs.existsSync(path.resolve(targetPath, value))) {
return key;
}
}
};
/**
* 使用子进程执行一条命令
* @param { string } command 执行的命令
* @param { Partial<SpawnSyncOptionsWithStringEncoding> } options 执行命令的参数
* **/
const execCommand = (command, options = {}) => {
return new Promise((resolve, reject) => {
const commandList = command.split(' '), spawnOption = Object.assign({
shell: true,
encoding: 'utf8',
cwd: process.cwd()
}, options);
if (!fs.existsSync(spawnOption.cwd || ''))
return reject('exec command cwd is not exists...');
const result = node_child_process.spawnSync(commandList[0], commandList.slice(1), spawnOption);
const stdout = result.stdout?.trim?.();
const error = result.error || result.stderr;
if (error && findString(error, ['error', 'Error', 'fatal:']))
return reject(error);
else
return resolve(stdout);
});
};
/**
* 获取系统信息
* @returns { string } platform 系统平台
* @returns { string } digit 系统位数
* @returns { boolean } isWindow 是否是windows系统
* @returns { boolean } isMac 是否时mac系统
* @returns { boolean } isWin64 是否是win64
* @returns { boolean } isWin32 是否是win32
* **/
const getSystemInfo = () => {
const platform = process.platform;
const systemDigit = process.arch;
return {
platform,
digit: systemDigit,
isWindow: platform === 'win32',
isMac: platform === 'darwin',
isWin64: systemDigit === 'x64',
isWin32: systemDigit !== 'x64'
};
};
/**
* 获取当前系统用户的家目录
* **/
const getHome = () => {
return os.homedir();
};
/**
* 获取系统的 appData目录
* **/
const getAppData = () => {
const homedir = getHome();
return getSystemInfo().isWindow
? path.resolve(homedir, 'AppData/Roaming')
: getSystemInfo().isMac
? path.join(homedir, '/')
: path.resolve(__dirname);
};
/**
* 根据appData目录为基准,获取路径
* @param { string } refer 参照路径
* **/
const getReferToAppData = (refer) => {
return path.resolve(getAppData(), refer || '');
};
/**
* 根据进程的名称,模糊查询,获取进程的PID
* @param { string } name 进程的名称
* **/
const getPidByName = (name) => {
const isWindows = getSystemInfo().isWindow;
const command = isWindows ? 'tasklist' : 'ps -ef';
const pidList = [];
return new Promise((resolve) => {
execCommand(command)
.then((result) => {
const lines = result.split(isWindows ? '\r\n' : '\n');
for (let i = 0; i < lines.length; i++) {
if (lines[i].includes(name)) {
const columns = lines[i].split(/\s+/);
const pid = isWindows ? columns[1] : columns[2];
pidList.push(parseInt(pid));
}
}
resolve(pidList.filter((item) => item));
})
.catch(() => {
resolve([]);
});
});
};
/**
* 提供进程的名称,模糊查询进程是否存在
* @param { string } processName 进程名称
* **/
const isActiveProcessByName = async (processName) => {
const processList = await getPidByName(processName);
return !!processList.length;
};
/**
* 提供一个PID,查看这个PID是否正在运行
* @param {string} pid 进程ID
* **/
const isActiveProcessByPid = async (pid) => {
const isWindow = getSystemInfo().isWindow;
const command = isWindow ? `tasklist /FI "PID eq ${pid}"` : 'ps -p ${pid}';
return new Promise((resolve) => {
execCommand(command)
.then((res) => {
resolve(res.includes(pid));
})
.catch(() => {
resolve(false);
});
});
};
/**
* 根据进程的PID,结束此进程
* @param { number | Array<number> } pid 进程PID
* **/
const killProcessPid = (pid) => {
if (!pid)
return Promise.resolve(false);
if (!isType(pid, 'array'))
pid = [pid];
const tasks = [], isWindows = getSystemInfo().isWindow;
function killTak(id) {
const killCommand = isWindows
? `taskkill /F /PID ${id}`
: `kill -9 ${id}`;
return new Promise((resolve) => {
node_child_process.exec(killCommand, (err) => {
if (err) {
resolve(false);
return;
}
resolve(true);
});
});
}
pid.forEach((id) => tasks.push(killTak(id)));
return Promise.all(tasks);
};
/**
* 提供进程的名称,结束掉此进程,名称会模糊查询
* @param { string } processName 进程的名称
* **/
const killProcessName = (processName) => {
return getPidByName(processName).then((list) => {
list.forEach((pid) => {
killProcessPid(pid);
});
return list;
});
};
/**
* 查看一个文件或目录是否在指定的路径下存在
* @param { string } filename 文件名称
* @param { string } cwd 工作目录,默认值为: 当前工作目录
* @returns { boolean } 是否存在
* **/
const exists = (filename, cwd = process.cwd()) => {
return fs.existsSync(path.resolve(cwd, filename));
};
/**
* 浅层读取一个目录下的文件或者文件夹
* @param { string } targetPath 目标路径
* @param { string } type 文件类型,默认值为: 全部类型
* @return { Array<string> } 文件列表
* **/
const readForTypeFileDir = (targetPath, type = 'all') => {
const list = fs.readdirSync(targetPath);
if (type === 'all')
return list;
return list.filter((item) => {
const itemPath = path.resolve(targetPath, item);
const stat = fs.statSync(itemPath);
if (typeof type !== 'string')
return type(itemPath, stat);
return type === 'file' ? !stat.isDirectory() : stat.isDirectory();
});
};
/**
* 创建一个目录(仅支持目录)
* @param { string } targetPath 目标路径
* @param { boolean } cover 是否覆盖创建 默认值为: false
* **/
const createDir = (targetPath, cover = false) => {
if (fs.existsSync(targetPath)) {
if (!cover)
return;
fs.rmSync(targetPath, { recursive: true });
}
fs.mkdirSync(targetPath, { recursive: true });
};
/**
* 写入的一个文件(仅支持文件)
* @param { string } targetPath 目标路径
* @param { string } content 文件内容
* @param { boolean } cover 是否覆盖创建,默认值为: false
* **/
const createFile = (targetPath, content, cover = false) => {
if (fs.existsSync(targetPath)) {
if (!cover)
return;
fs.unlinkSync(targetPath);
}
const dirPathName = path.dirname(targetPath);
if (!fs.existsSync(dirPathName))
createDir(dirPathName);
fs.writeFileSync(targetPath, content, { encoding: 'utf-8' });
};
/**
* 当文件存在时,读取一个文件的文件内容
* @param { string } targetPath 目标文件路径的
* @param { Parameters<typeof fs.readFileSync>[1] } options 读取时需要传递的参数
* @returns { string | Buffer } 返回的文件内容
* **/
const readExistsFile = (targetPath, options = {}) => {
if (!fs.existsSync(targetPath))
return '';
return fs.readFileSync(targetPath, options);
};
/**
* 移动工具类,此方法仅适用文件的移动
* @param { string } sourcePath 需要拷贝的路径
* @param { string } targetPath 目标路径
* @param { boolean } cover 是否强制覆盖
* @returns { number } type 移动是否成功: 1 | 0
* @returns { string } sourcePath 源路径
* @returns { string } targetPath 目标路径
* **/
const copyFile = (sourcePath, targetPath, cover = false) => {
if (!fs.existsSync(targetPath) || cover) {
fs.copyFileSync(sourcePath, targetPath);
return { type: 1, sourcePath, targetPath };
}
return { type: 0, sourcePath, targetPath };
};
/**
* 拷贝整个目录及其子路径至指定的目录
* @param { string } origin 源路径
* @param { string } targetPath 目标路径
* @param { ((sourcePath: string, targetPath: string) => boolean) | boolean } cover 是否覆盖
* @param { ((sourcePath: string, targetPath: string) => boolean) | boolean } ignore 是否忽略
* **/
const copyDirectory = async (origin, targetPath, cover = true, ignore = false) => {
const originStat = fs.statSync(origin).isDirectory();
if (!originStat)
throw new Error('origin or target is not directory');
if (!fs.existsSync(targetPath))
createDir(targetPath);
const entries = fs.readdirSync(origin, { withFileTypes: true });
for (const entry of entries) {
const sourceChildrenPath = path.resolve(origin, entry.name), destChildrenPath = path.resolve(targetPath, entry.name);
// 是否忽略此路径的移动
if (typeof ignore === 'function' &&
isType(ignore, ['function', 'asyncfunction']) &&
(await ignore(sourceChildrenPath, destChildrenPath))) {
continue;
}
// 此路径是否覆盖, 覆盖时,只能文件不能目录
let isCover = false;
if (fs.existsSync(destChildrenPath) && !entry.isDirectory()) {
if ((isType(cover, ['boolean']) && cover) ||
(typeof cover === 'function' &&
(await cover(sourceChildrenPath, destChildrenPath)))) {
isCover = true;
fs.unlinkSync(destChildrenPath);
}
else {
continue;
}
}
if (entry.isDirectory()) {
await copyDirectory(sourceChildrenPath, destChildrenPath, cover, ignore);
}
else {
copyFile(sourceChildrenPath, destChildrenPath, isCover);
}
}
};
/**
* 移除文件, 当传递的是文件,则删除文件,传递的是目录,则递归删除目录
* @param { string } targetPath 文件路径
* */
const removeFileOrDir = (targetPath) => {
if (!fs.existsSync(targetPath))
return;
const stats = fs.statSync(targetPath);
if (stats.isDirectory())
fs.rmSync(targetPath, { recursive: true });
else
fs.unlinkSync(targetPath);
};
/**
* 检测权限,若权限不为读写,则赋值为读写
* @param { string } targetPath 文件路径
* **/
const checkXPermission = (targetPath) => {
fs.accessSync(targetPath, fs.constants.X_OK);
fs.chmodSync(targetPath, fs.constants.S_IXUSR);
};
/**
* 检测文件是否为只读权限
* @param { string } targetPath 文件路径
* **/
const checkReadPermission = (targetPath) => {
fs.accessSync(targetPath, fs.constants.R_OK);
};
/**
* 递归删除目录, 手动方式删除(兼容老版本Node)
* @param { string } dirPath 目录的路径
* @param { Array<string> } whiteList 删除文件的白名单
* **/
const dropCleanFolder = (dirPath, whiteList = []) => {
if (fs.existsSync(dirPath)) {
const files = fs.readdirSync(dirPath);
for (const file of files) {
const filePath = path.join(dirPath, file);
if (fs.lstatSync(filePath).isDirectory()) {
dropCleanFolder(filePath, whiteList);
}
else {
if (!whiteList.includes(filePath)) {
fs.unlinkSync(filePath);
}
}
}
if (!whiteList.includes(dirPath)) {
fs.rmdirSync(dirPath);
}
}
};
/**
* 当文件存在时,则合并内容,反之创建文件
* @param { string } source 源路径
* @param { string } targetPath 目标路径
* @param { Object } options 写入或创建文件的参数
* **/
const mergeOrCreateFile = (source, targetPath, options = {}) => {
options = srcExports.recursive({ wrap: false, jsonOrArray: false, tabWidth: 4 }, options);
const existsSource = fs.existsSync(source), existsTarget = fs.existsSync(targetPath);
if (!existsSource)
throw new Error('source or target path can not be null');
if (!existsTarget)
return fs.copyFileSync(source, targetPath);
const sourceContent = fs.readFileSync(source, { encoding: 'utf-8' });
if (options.jsonOrArray) {
const source = JSON.parse(sourceContent), target = JSON.parse(fs.readFileSync(targetPath, {
encoding: 'utf-8'
}));
fs.writeFileSync(targetPath, JSON.stringify(srcExports.recursive(source, target), null, options.tabWidth), {
encoding: 'utf-8'
});
}
else {
fs.appendFileSync(targetPath, `${options.wrap ? '\n' : ''}${sourceContent}`, { encoding: 'utf-8' });
}
};
/**
* 判断一个目录是否是盘符目录
* @param { string } targetPath 目标路径
* **/
const isDriveDirectory = (targetPath) => {
targetPath = path.resolve(targetPath);
return targetPath === path.parse(targetPath).root;
};
/**
* 向上层目录层级执行一个函数,直到函数返回成功或遇到盘符目录为止
* @param { string } targetPath 需要执行函数的目录
* @param { Function } cb 执行的自定义函数, 此函数返回true则终止执行,反之执行至盘符目录为止
* **/
const parentExecHandlerPromise = (targetPath, cb) => {
return new Promise((resolve) => {
if (isDriveDirectory(targetPath))
return resolve('');
const recursionExecHandler = () => resolve(parentExecHandlerPromise(path.dirname(targetPath), cb));
if (isType(cb, 'asyncfunction')) {
cb(targetPath)
.then((result) => {
if (!result)
throw new Error('');
return resolve(result);
})
.catch(() => {
return recursionExecHandler();
});
}
else {
try {
const result = cb(targetPath);
if (result)
return resolve(result);
else
throw new Error('');
}
catch (e) {
return recursionExecHandler();
}
}
});
};
/**
* 向上查询文件的存在目录
* @param { string } targetPath 基准目录
* @param { string } handler 文件名称或执行函数
* @returns { string } 查询到的文件目录
* **/
const findParentFile = (targetPath, handler) => {
return parentExecHandlerPromise(targetPath, async (cwd) => {
let result = null;
if (isType(handler, 'string')) {
result = fs.existsSync(path.resolve(cwd, handler));
}
else if (isType(handler, 'function')) {
result = handler(cwd);
}
else if (isType(handler, 'asyncfunction')) {
result = await handler(cwd);
}
else {
throw new Error('invalid handler parameter');
}
return result ? cwd : result;
});
};
/**
* 给定一个目录,返回这个目录的根目录
* @param { string } value 给定的目录字符串
* @returns { string } 根目录字符串
* **/
const findRootParentPath = (value) => {
if (!value.trim())
return value;
value = value.replaceAll('//', '/');
const parentPath = path.dirname(value);
if (['.', '/'].includes(parentPath))
return value;
else
return findRootParentPath(parentPath);
};
/**
* 查看一个路径下是否存在列表中这些文件,只要有一个满足则返回 true
* @param { string } basicPath 查询的路径
* @param { Array<string> } fileNameList 需要查询的文件名称列表
* @returns { string } 查询到的文件名称
* **/
const getExistsFilePath = (basicPath = process.cwd(), fileNameList) => {
for (const item of fileNameList) {
const filePath = path.resolve(basicPath, item);
if (fs.existsSync(filePath))
return filePath;
}
return '';
};
/**
* 将 blob 转换为 string 字符串
* @param { Blob } blob 二进制数据内容
* **/
const blobToString = (blob) => {
return new Promise((resolve) => {
const fileRender = new FileReader();
fileRender.onload = () => {
resolve(fileRender.result);
};
fileRender.readAsText(blob);
});
};
/**
* 获取浏览器属性
* @returns { string } name 浏览器名称
* @returns { string } version 浏览器版本
* **/
const getBrowserInfo = () => {
const userAgent = window.navigator.userAgent;
let browserName = '', browserVersion = '';
if (/Chrome/i.test(userAgent)) {
browserName = 'Chrome';
}
else if (/Firefox/i.test(userAgent)) {
browserName = 'Firefox';
}
else if (/Safari/i.test(userAgent)) {
browserName = 'Safari';
}
else if (/Opera|OPR/i.test(userAgent)) {
browserName = 'Opera';
}
else if (/Edge/i.test(userAgent)) {
browserName = 'Edge';
}
else if (/MSIE/i.test(userAgent) || /Trident/i.test(userAgent)) {
browserName = 'IE';
}
else {
browserName = 'Unknown';
}
const versionMatch = userAgent.match(/(Chrome|Firefox|Safari|Opera|Edge|IE)\/?\s*(\.?\d+(\.\d+)*)/i);
if (versionMatch && versionMatch.length >= 3) {
browserVersion = versionMatch[2];
}
else {
browserVersion = 'Unknown';
}
return {
name: browserName,
version: browserVersion
};
};
exports.blobToString = blobToString;
exports.checkReadPermission = checkReadPermission;
exports.checkXPermission = checkXPermission;
exports.copyDirectory = copyDirectory;
exports.copyFile = copyFile;
exports.createDir = createDir;
exports.createFile = createFile;
exports.debounce = debounce;
exports.differenceArrayList = differenceArrayList;
exports.dropCleanFolder = dropCleanFolder;
exports.execCommand = execCommand;
exports.exists = exists;
exports.findParentFile = findParentFile;
exports.findRootParentPath = findRootParentPath;
exports.findString = findString;
exports.flatJSON = flatJSON;
exports.getAppData = getAppData;
exports.getBrowserInfo = getBrowserInfo;
exports.getExistsFilePath = getExistsFilePath;
exports.getHome = getHome;
exports.getPackageMangerName = getPackageMangerName;
exports.getPidByName = getPidByName;
exports.getReferToAppData = getReferToAppData;
exports.getSystemInfo = getSystemInfo;
exports.getType = getType;
exports.hasProperty = hasProperty;
exports.intersectionArrayList = intersectionArrayList;
exports.isActiveProcessByName = isActiveProcessByName;
exports.isActiveProcessByPid = isActiveProcessByPid;
exports.isEmptyArray = isEmptyArray;
exports.isEmptyJSON = isEmptyJSON;
exports.isType = isType;
exports.isValidUrl = isValidUrl;
exports.killProcessName = killProcessName;
exports.killProcessPid = killProcessPid;
exports.mergeOrCreateFile = mergeOrCreateFile;
exports.packageMangerViewer = packageMangerViewer;
exports.readExistsFile = readExistsFile;
exports.readForTypeFileDir = readForTypeFileDir;
exports.removeFileOrDir = removeFileOrDir;
exports.sleep = sleep;
exports.splitJsonToContent = splitJsonToContent;
exports.throttle = throttle;
exports.unionArrayList = unionArrayList;
exports.validType = validType;
}));
} (mvCommon$1, mvCommon$1.exports));
return mvCommon$1.exports;
}
var mvCommonExports = requireMvCommon();
/**
* 判断一个url是不是有有效的http格式
* @param { string } url 链接地址
* @returns { boolean } 是否是一个链接
* **/
/**
* 获取一个数据的类型
* @param { unknown } value 需要判断类型的数据
* @returns { string | null } 判断的数据类型
* **/
const getType = (value) => {
const typeResult = Object.prototype.toString.call(value);
const type = typeResult
.slice(typeResult.indexOf(' ') + 1, -1)
.toLocaleLowerCase();
if (type === 'array') {
if (Array.isArray(value))
return 'array';
else
return null;
}
return type;
};
/**
* 判断一个数据的类型是否为指定的类型
* @param { unknown } value 需要判断类型的数据
* @param { string | Array } type 期望的数据类型
* @return { boolean } 是否为期望的类型
* **/
const isType = (value, type) => {
const valueType = getType(value);
if (valueType === null)
throw new Error('invalid value...');
if (typeof type === 'string')
return valueType === type.toLocaleLowerCase();
return type.map((item) => item.toLocaleLowerCase()).includes(valueType);
};
/**
* 获取系统信息
* @returns { string } platform 系统平台
* @returns { string } digit 系统位数
* @returns { boolean } isWindow 是否是windows系统
* @returns { boolean } isMac 是否时mac系统
* @returns { boolean } isWin64 是否是win64
* @returns { boolean } isWin32 是否是win32
* **/
const getSystemInfo = () => {
const platform = process.platform;
const systemDigit = process.arch;
return {
platform,
digit: systemDigit,
isWindow: platform === 'win32',
isMac: platform === 'darwin',
isWin64: systemDigit === 'x64',
isWin32: systemDigit !== 'x64'
};
};
var src$1 = {exports: {}};
(function (module, exports) {
Object.defineProperty(exports, "__esModule", { value: true });
exports.isPlainObject = exports.clone = exports.recursive = exports.merge = exports.main = void 0;
module.exports = exports = main;
exports.default = main;
function main() {
var items = [];
for (var _i = 0; _i < arguments.length; _i++) {
items[_i] = arguments[_i];
}
return merge.apply(void 0, items);
}
exports.main = main;
main.clone = clone;
main.isPlainObject = isPlainObject;
main.recursive = recursive;
function merge() {
var items = [];
for (var _i = 0; _i < arguments.length; _i++) {
items[_i] = arguments[_i];
}
return _merge(items[0] === true, false, items);
}
exports.merge = merge;
function recursive() {
var items = [];
for (var _i = 0; _i < arguments.length; _i++) {
items[_i] = arguments[_i];
}
return _merge(items[0] === true, true, items);
}
exports.recursive = recursive;
function clone(input) {
if (Array.isArray(input)) {
var output = [];
for (var index = 0; index < input.length; ++index)
output.push(clone(input[index]));
return output;
}
else if (isPlainObject(input)) {
var output = {};
for (var index in input)
output[index] = clone(input[index]);
return output;
}
else {
return input;
}
}
exports.clone = clone;
function isPlainObject(input) {
return input && typeof input === 'object' && !Array.isArray(input);
}
exports.isPlainObject = isPlainObject;
function _recursiveMerge(base, extend) {
if (!isPlainObject(base))
return extend;
for (var key in extend) {
if (key === '__proto__' || key === 'constructor' || key === 'prototype')
continue;
base[key] = (isPlainObject(base[key]) && isPlainObject(extend[key])) ?
_recursiveMerge(base[key], extend[key]) :
extend[key];
}
return base;
}
function _merge(isClone, isRecursive, items) {
var result;
if (isClone || !isPlainObject(result = items.shift()))
result = {};
for (var index = 0; index < items.length; ++index) {
var item = items[index];
if (!isPlainObject(item))
continue;
for (var key in item) {
if (key === '__proto__' || key === 'constructor' || key === 'prototype')
continue;
var value = isClone ? clone(item[key]) : item[key];
result[key] = isRecursive ? _recursiveMerge(result[key], value) : value;
}
}
return result;
}
} (src$1, src$1.exports));
/**
* 判断一个目录是否是盘符目录
* @param { string } targetPath 目标路径
* **/
const isDriveDirectory = (targetPath) => {
targetPath = path.resolve(targetPath);
return targetPath === path.parse(targetPath).root;
};
/**
* 向上层目录层级执行一个函数,直到函数返回成功或遇到盘符目录为止
* @param { string } targetPath 需要执行函数的目录
* @param { Function } cb 执行的自定义函数, 此函数返回true则终止执行,反之执行至盘符目录为止
* **/
const parentExecHandlerPromise = (targetPath, cb) => {
return new Promise((resolve) => {
if (isDriveDirectory(targetPath))
return resolve('');
const recursionExecHandler = () => resolve(parentExecHandlerPromise(path.dirname(targetPath), cb));
if (isType(cb, 'asyncfunction')) {
cb(targetPath)
.then((result) => {
if (!result)
throw new Error('');
return resolve(result);
})
.catch(() => {
return recursionExecHandler();
});
}
else {
try {
const result = cb(targetPath);
if (result)
return resolve(result);
else
throw new Error('');
}
catch (e) {
return recursionExecHandler();
}
}
});
};
/**
* 向上查询文件的存在目录
* @param { string } targetPath 基准目录
* @param { string } handler 文件名称或执行函数
* @returns { string } 查询到的文件目录
* **/
const findParentFile = (targetPath, handler) => {
return parentExecHandlerPromise(targetPath, async (cwd) => {
let result = null;
if (isType(handler, 'string')) {
result = fs.existsSync(path.resolve(cwd, handler));
}
else if (isType(handler, 'function')) {
result = handler(cwd);
}
else if (isType(handler, 'asyncfunction')) {
result = await handler(cwd);
}
else {
throw new Error('invalid handler parameter');
}
return result ? cwd : result;
});
};
let ElectronDev$1 = class ElectronDev {
#config = {
entry: '',
tsConfigPath: '',
envConfig: {}
};
#childProcess = null;
constructor(config) {
this.#config = { ...this.#config, ...config };
const { entry, tsConfigPath, envConfig } = this.#config;
if (!entry || !fs.existsSync(entry))
throw new Error('vite-plugin-electron-dev plugin: entry path is not exists');
if (tsConfigPath && !fs.existsSync(tsConfigPath))
throw new Error('vite-plugin-electron-dev plugin: tsConfigPath path is not exists');
if (!isType(envConfig, 'object'))
throw new Error('vite-plugin-electron-dev plugin: env config must to be object...');
}
/**
* 非开发模式下,不启动此插件
* **/
get open() {
const { NODE_ENV } = this.#config.envConfig;
return NODE_ENV === 'development';
}
/**
* process 子进程关闭,重置子进程
* **/
resetServer(server, type, error) {
console.log(`electron start fail...${type}:`, error);
server.config.inlineConfig.__restartServer = false;
process.exit(0);
}
/**
* 开启process子进程,并监听 tsc的变动,重启子进程
* **/
async startElectronProcess(server) {
const command = this.#config.tsConfigPath
? [
'mv-tsc-watch',
[
'--project',
this.#config.tsConfigPath,
'--onSuccess',
`electron ${this.#config.entry}`
]
]
: ['electron', [this.#config.entry]];
const rootPath = await findParentFile(this.#config.entry, 'package.json');
this.#childProcess = require$$3.spawn(...command, {
shell: getSystemInfo().isWindow,
cwd: rootPath,
env: {
...process.env,
ELECTRON_URL: server.resolvedUrls?.local?.[0] || '[:::]',
...this.#config.envConfig
}
});
this.#childProcess.on('error', this.resetServer.bind(this, server, 'error'));
this.#childProcess.on('exit', this.resetServer.bind(this, server, 'exit'));
this.#childProcess.stderr.pipe(process.stderr);
this.#childProcess.stdout.on('data', (data) => {
const consoleValue = data.toString();
if (consoleValue.startsWith('mv-tsc-watch:close')) {
process.exit(0);
}
else {
console.log(consoleValue);
}
});
}
/**
* 启动Process进程
* **/
async loadElectronProcess(server) {
if (server.config.inlineConfig.__restartServer || !this.open)
return;
await this.startElectronProcess(server);
server.config.inlineConfig.__restartServer = true;
}
/**
* 合并插件配置
* **/
async configResolvedHooks() {
this.#config.envConfig = {
...this.#config.envConfig,
NODE_ENV: process.env.NODE_ENV
};
}
/**
* 监听DevServer,在启动时,启动Electron程序
* **/
async devServerHooks(server) {
const _this = this;
return () => {
const cb = server.listen;
server.listen = async function (...rest) {
const result = await cb.apply(this, rest);
_this.loadElectronProcess(server);
return result;
};
};
}
};
function index (props) {
const electronDev = new ElectronDev$1(props);
return {
name: 'vite-plugin-start-electron',
configResolved: () => electronDev.configResolvedHooks(),
configureServer: (server) => electronDev.devServerHooks(server)
};
}
var main = {exports: {}};
var version = "16.5.0";
var require$$4 = {
version: version};
var hasRequiredMain;
function requireMain () {
if (hasRequiredMain) return main.exports;
hasRequiredMain = 1;
const fs = require$$0;
const path = path$1;
const os = require$$2$1;
const crypto = require$$3$1;
const packageJson = require$$4;
const version = packageJson.version;
const LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg;
// Parse src into an Object
function parse (src) {
const obj = {};
// Convert buffer to string
let lines = src.toString();
// Convert line breaks to same format
lines = lines.replace(/\r\n?/mg, '\n');
let match;
while ((match = LINE.exec(lines)) != null) {
const key = match[1];
// Default undefined or null to empty string
let value = (match[2] || '');
// Remove whitespace
value = value.trim();
// Check if double quoted
const maybeQuote = value[0];
// Remove surrounding quotes
value = value.replace(/^(['"`])([\s\S]*)\1$/mg, '$2');
// Expand newlines if double quoted
if (maybeQuote === '"') {
value = value.replace(/\\n/g, '\n');
value = value.replace(/\\r/g, '\r');
}
// Add to object
obj[key] = value;
}
return obj
}
function _parseVault (options) {
const vaultPath = _vaultPath(options);
// Parse .env.vault
const result = DotenvModule.configDotenv({ path: vaultPath });
if (!result.parsed) {
const err = new Error(`MISSING_DATA: Cannot parse ${vaultPath} for an unknown reason`);
err.code = 'MISSING_DATA';
throw err
}
// handle scenario for comma separated keys - for use with key rotation
// example: DOTENV_KEY="dotenv://:key_1234@dotenvx.com/vault/.env.vault?environment=prod,dotenv://:key_7890@dotenvx.com/vault/.env.vault?environment=prod"
const keys = _dotenvKey(options).split(',');
const length = keys.length;
let decrypted;
for (let i = 0; i < length; i++) {
try {
// Get full key
const key = keys[i].trim();
// Get instructions for decrypt
const attrs = _instructions(result, key);
// Decrypt
decrypted = DotenvModule.decrypt(attrs.ciphertext, attrs.key);
break
} catch (error) {
// last key
if (i + 1 >= length) {
throw error
}
// try next key
}
}
// Parse decrypted .env string
return DotenvModule.parse(decrypted)
}
function _warn (message) {
console.log(`[dotenv@${version}][WARN] ${message}`);
}
function _debug (message) {
console.log(`[dotenv@${version}][DEBUG] ${message}`);
}
function _dotenvKey (options) {
// prioritize developer directly setting options.DOTENV_KEY
if (options && options.DOTENV_KEY && options.DOTENV_KEY.length > 0) {
return options.DOTENV_KEY
}
// secondary infra already contains a DOTENV_KEY environment variable
if (process.env.DOTENV_KEY && process.env.DOTENV_KEY.length > 0) {
return process.env.DOTENV_KEY
}
// fallback to empty string
return ''
}
function _instructions (result, dotenvKey) {
// Parse DOTENV_KEY. Format is a URI
let uri;
try {
uri = new URL(dotenvKey);
} catch (error) {
if (error.code === 'ERR_INVALID_URL') {
const err = new Error('INVALID_DOTENV_KEY: Wrong format. Must be in valid uri format like dotenv://:key_1234@dotenvx.com/vault/.env.vault?environment=development');
err.code = 'INVALID_DOTENV_KEY';
throw err
}
throw error
}
// Get decrypt key
const key = uri.password;
if (!key) {
const err = new Error('INVALID_DOTENV_KEY: Missing key part');
err.code = 'INVALID_DOTENV_KEY';
throw err
}
// Get environment
const environment = uri.searchParams.get('environment');
if (!environment) {
const err = new Error('INVALID_DOTENV_KEY: Missing environment part');
err.code = 'INVALID_DOTENV_KEY';
throw err
}
// Get ciphertext payload
const environmentKey = `DOTENV_VAULT_${environment.toUpperCase()}`;
const ciphertext = result.parsed[environmentKey]; // DOTENV_VAULT_PRODUCTION
if (!ciphertext) {
const err = new Error(`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment ${environmentKey} in your .env.vault file.`);
err.code = 'NOT_FOUND_DOTENV_ENVIRONMENT';
throw err
}
return { ciphertext, key }
}
function _vaultPath (options) {
let possibleVaultPath = null;
if (options && options.path && options.path.length > 0) {
if (Array.isArray(options.path)) {
for (const filepath of options.path) {
if (fs.existsSync(filepath)) {
possibleVaultPath = filepath.endsWith('.vault') ? filepath : `${filepath}.vault`;
}
}
} else {
possibleVaultPath = options.path.endsWith('.vault') ? options.path : `${options.path}.vault`;
}
} else {
possibleVaultPath = path.resolve(process.cwd(), '.env.vault');
}
if (fs.existsSync(possibleVaultPath)) {
return possibleVaultPath
}
return null
}
function _resolveHome (envPath) {
return envPath[0] === '~' ? path.join(os.homedir(), envPath.slice(1)) : envPath
}
function _configVault (options) {
const debug = Boolean(options && options.debug);
if (debug) {
_debug('Loading env from encrypted .env.vault');
}
const parsed = DotenvModule._parseVault(options);
let processEnv = process.env;
if (options && options.processEnv != null) {
processEnv = options.processEnv;
}
DotenvModule.populate(processEnv, parsed, options);
return { parsed }
}
function configDotenv (options) {
const dotenvPath = path.resolve(process.cwd(), '.env');
let encoding = 'utf8';
const debug = Boolean(options && options.debug);
if (options && options.encoding) {
encoding = options.encoding;
} else {
if (debug) {
_debug('No encoding is specified. UTF-8 is used by default');
}
}
let optionPaths = [dotenvPath]; // default, look for .env
if (options && options.path) {
if (!Array.isArray(options.path)) {
optionPaths = [_resolveHome(options.path)];
} else {
optionPaths = []; // reset default
for (const filepath of options.path) {
optionPaths.push(_resolveHome(filepath));
}
}
}
/