2022-04-01 16:47:53 +08:00

376 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const path = require("path")
const shelljs = require("shelljs")
const fs = require("fs-extra")
/**
*
* 匹配指定路径或文件名称
*
* const matcher = fileMatcher([
* "<pattern>", // 匹配正则表达式字符串
* "!<pattern>", // 以!开头代表否定匹配
* /正则表达式/
* ],{
* basePath:"<指定一个基准目录,所有不是以此开头的均视为不匹配>",
* defaultPatterns:["<默认排除的模式>","<默认排除的模式>","<默认排除的模式>"],
* debug:<true/>false,是否输出调试信息,当=true时.test()方法返回[<true/false>,pattern] *
* })
*
*
*
*
* @param {*} patterns
* @param {*} basePath 如果指定basePath则所有不是以basePath开头的文件都排除
* @param {*} defaultPatterns 默认的匹配模式
* @param {*} debug 是否输出调试信息
*/
function fileMatcher(patterns,{basePath,defaultPatterns=[],debug=true}={}) {
if(basePath) {
basePath = path.normalize(basePath)
}
//[[pattern,exclude],[pattern,false],[pattern,true]]
let finalPatterns = []
let inputPatterns = Array.isArray(patterns) ? patterns : (patterns ? [patterns] : [])
// 默认排除模式
if(defaultPatterns.length===0){
finalPatterns.push([/.*\/node_modules\/.*/,true])
finalPatterns.push([/.*\/languages\/.*/,true]) // 默认排除语言文件
finalPatterns.push([/\.babelrc/,true])
finalPatterns.push([/babel\.config\.js/,true])
finalPatterns.push([/package\.json$/,true])
finalPatterns.push([/vite\.config\.js$/,true])
finalPatterns.push([/^plugin-vue:.*/,true])
}
inputPatterns.forEach(pattern=>{
if(typeof pattern === "string"){
pattern.replaceAll("**",".*")
pattern.replaceAll("?","[^\/]?")
pattern.replaceAll(/(?<!\.)\*/g,"[^\/]*")
// 以!开头的表示排除
if(pattern.startsWith("!")){
finalPatterns.unshift([new RegExp(pattern.substring(1),"g"),true])
}else{
finalPatterns.push([new RegExp(pattern,"g"),false])
}
}else{
finalPatterns.push([pattern,false])
}
})
return {
patterns:finalPatterns,
basePath,
test: (filename) => {
let isMatched = false
let file = filename
// 如果指定basePath则文件名称必须是以basePath开头
if(basePath){
if(path.isAbsolute(file)){
if(!path.normalize(file).startsWith(basePath)){
return debug ? [false,`!^${basePath}`] : false
}else{
isMatched = true
}
}
}
if(finalPatterns.length===0){
return debug ? [true,"*"] : true
}else{
for(const pattern of finalPatterns){
pattern[0].lastIndex = 0
if(pattern[1]===true){
if(pattern[0].test(file)) return debug ? [false,pattern[0].toString()] : false
}else{
if(pattern[0].test(file)) return debug ? [true,pattern[0].toString()] : true
}
}
}
return debug ? [isMatched,"*"] : isMatched
}
}
}
/**
* 以floder为基准向上查找文件package.json并返回package.json所在的文件夹
* @param {*} folder 起始文件夹,如果没有指定,则取当前文件夹
* @param {*} exclueCurrent 如果=true则folder的父文件夹开始查找
* @returns
*/
function getProjectRootFolder(folder="./",exclueCurrent=false){
if(!path.isAbsolute(folder)){
folder = path.join(process.cwd(),folder)
}
try{
const pkgFile =exclueCurrent ?
path.join(folder, "..", "package.json")
: path.join(folder, "package.json")
if(fs.existsSync(pkgFile)){
return path.dirname(pkgFile)
}
const parent = path.dirname(folder)
if(parent===folder) return null
return getProjectRootFolder(parent,false)
}catch(e){
return process.cwd()
}
}
/**
* 自动获取当前项目的languages
*
* 1.
*
* @param {*} location
*/
function getProjectLanguageFolder(location="./"){
// 绝对路径
if(!path.isAbsolute(location)){
location = path.join(process.cwd(),location)
}
// 发现当前项目根目录
const projectRoot = getProjectRootFolder(location)
const searchFolders = [
path.join(location,"src","languages"),
path.join(location,"languages")
]
for(let folder of searchFolders){
if(fs.existsSync(folder)){
return folder
}
}
return null
}
/**
* 根据当前输入的文件夹位置自动确定源码文件夹位置
*
* - 如果没有指定,则取当前文件夹
* - 如果指定是非绝对路径则以当前文件夹作为base
* - 查找pack
* - 如果该文件夹中存在src则取src下的文件夹
* -
*
* @param {*} location
* @returns
*/
function getProjectSourceFolder(location){
if(!location) {
location = process.cwd()
}else{
if(!path.isAbsolute(location)){
location = path.join(process.cwd(),location)
}
}
let projectRoot = getProjectRootFolder(location)
// 如果当前工程存在src文件夹则自动使用该文件夹作为源文件夹
if(fs.existsSync(path.join(projectRoot,"src"))){
projectRoot = path.join(projectRoot,"src")
}
return projectRoot
}
/**
* 读取指定文件夹的package.json文件如果当前文件夹没有package.json文件则向上查找
* @param {*} folder
* @param {*} exclueCurrent = true 排除folder从folder的父级开始查找
* @returns
*/
function getCurrentPackageJson(folder,exclueCurrent=true){
let projectFolder = getCurrentProjectRootFolder(folder,exclueCurrent)
if(projectFolder){
return fs.readJSONSync(path.join(projectFolder,"package.json"))
}
}
/**
*
* 返回当前项目的模块类型
*
* 从当前文件夹开始向上查找package.json文件并解析出语言包的类型
*
* @param {*} folder
*/
function findModuleType(folder){
let packageJson = getCurrentPackageJson(folder)
try{
return packageJson.type || "commonjs"
}catch(e){
return "esm"
}
}
/**
* 判断是否已经安装了依赖
*
* isInstallDependent("@voerkai18n/runtime")
*
*/
function isInstallDependent(url){
try{
// 简单判断是否存在该文件夹node_modules/@voerkai18n/runtime
let projectFolder = getCurrentProjectRootFolder(process.cwd())
if(fs.existsSync(path.join(projectFolder,"node_modules","@voerkai18n/runtime"))){
return true
}
// 如果不存在则尝试require
require(url)
}catch(e){
return false
}
}
/**
* 判断是否是JSON对象
* @param {*} obj
* @returns
*/
function isPlainObject(obj){
if (typeof obj !== 'object' || obj === null) return false;
var proto = Object.getPrototypeOf(obj);
if (proto === null) return true;
var baseProto = proto;
while (Object.getPrototypeOf(baseProto) !== null) {
baseProto = Object.getPrototypeOf(baseProto);
}
return proto === baseProto;
}
function isNumber(value){
return !isNaN(parseInt(value))
}
/**
* 检测当前工程是否是git工程
*/
function isGitRepo(){
return shelljs.exec("git status", {silent: true}).code === 0;
}
/**
* 简单进行对象合并
*
* options={
* array:0 , // 数组合并策略0-替换1-合并2-去重合并
* }
*
* @param {*} toObj
* @param {*} formObj
* @returns 合并后的对象
*/
function deepMerge(toObj,formObj,options={}){
let results = Object.assign({},toObj)
Object.entries(formObj).forEach(([key,value])=>{
if(key in results){
if(typeof value === "object" && value !== null){
if(Array.isArray(value)){
if(options.array === 0){
results[key] = value
}else if(options.array === 1){
results[key] = [...results[key],...value]
}else if(options.array === 2){
results[key] = [...new Set([...results[key],...value])]
}
}else{
results[key] = deepMerge(results[key],value,options)
}
}else{
results[key] = value
}
}else{
results[key] = value
}
})
return results
}
/**
* 获取指定变量类型名称
* getDataTypeName(1) == Number
* getDataTypeName("") == String
* getDataTypeName(null) == Null
* getDataTypeName(undefined) == Undefined
* getDataTypeName(new Date()) == Date
* getDataTypeName(new Error()) == Error
*
* @param {*} v
* @returns
*/
function getDataTypeName(v){
if (v === null) return 'Null'
if (v === undefined) return 'Undefined'
if(typeof(v)==="function") return "Function"
return v.constructor && v.constructor.name;
};
/**
* 在当前工程自动安装@voerkai18n/runtime
* @param {*} srcPath
* @param {*} opts
*/
function installVoerkai18nRuntime(srcPath){
const projectFolder = getCurrentProjectRootFolder(srcPath || process.cwd())
if(fs.existsSync("pnpm-lock.yaml")){
shelljs.exec("pnpm add @voerkai18n/runtime")
}else if(fs.existsSync("yarn.lock")){
shelljs.exec("yarn add @voerkai18n/runtime")
}else{
shelljs.exec("npm install @voerkai18n/runtime")
}
}
/**
* 在指定文件夹下创建package.json文件
* @param {*} targetPath
* @param {*} moduleType
* @returns
*/
function createPackageJsonFile(targetPath,moduleType="auto"){
if(moduleType==="auto"){
moduleType = findModuleType(targetPath)
}
const packageJsonFile = path.join(targetPath, "package.json")
if(["esm","es","module"].includes(moduleType)){
fs.writeFileSync(packageJsonFile,JSON.stringify({type:"module",license:"MIT"},null,4))
if(moduleType==="module"){
moduleType = "esm"
}
}else{
fs.writeFileSync(packageJsonFile,JSON.stringify({license:"MIT"},null,4))
}
return moduleType
}
module.exports = {
fileMatcher, // 文件名称匹配器
getProjectRootFolder, // 查找获取项目根目录
createPackageJsonFile, // 创建package.json文件
getProjectSourceFolder, // 获取项目源码目录
getCurrentPackageJson, // 查找获取当前项目package.json
getProjectLanguageFolder, // 获取当前项目的languages目录
findModuleType, // 获取当前项目的模块类型
isInstallDependent, // 判断是否已经安装了依赖
installVoerkai18nRuntime, // 在当前工程自动安装@voerkai18n/runtime
isPlainObject, // 判断是否是普通对象
isNumber, // 判断是否是数字
deepMerge, // 深度合并对象
getDataTypeName, // 获取指定变量类型名称
isGitRepo, // 判断当前工程是否是git工程
}