UNPKG

@vite-electron-simple/core

Version:

一款支持在 vite 构建工具下,实现 electron 的开发、构建引入的脚手架,同时支持单一浏览器环境的开发和构建。支持全量的 vite 以及 electron-builder 的全部功能。

1,516 lines (1,439 loc) 78.6 kB
#!/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)); } } } /