chore: init project

This commit is contained in:
vben
2024-05-19 21:20:42 +08:00
commit 399334ac57
630 changed files with 45623 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
import { defineBuildConfig } from 'unbuild';
export default defineBuildConfig({
clean: true,
declaration: true,
entries: ['src/index'],
});

View File

@@ -0,0 +1,45 @@
{
"name": "@vben/node-utils",
"version": "1.0.0",
"private": true,
"type": "module",
"license": "MIT",
"homepage": "https://github.com/vbenjs/vue-vben-admin",
"repository": {
"type": "git",
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
"directory": "internal/node-utils"
},
"bugs": {
"url": "https://github.com/vbenjs/vue-vben-admin/issues"
},
"scripts": {
"stub": "pnpm unbuild --stub"
},
"files": [
"dist"
],
"main": "./dist/index.mjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"imports": {
"#*": "./src/*"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"default": "./dist/index.mjs"
}
},
"dependencies": {
"@changesets/git": "^3.0.0",
"@manypkg/get-packages": "^2.2.1",
"consola": "^3.2.3",
"find-up": "^7.0.0",
"pkg-types": "^1.1.1",
"prettier": "^3.2.5",
"rimraf": "^5.0.7",
"zx": "^7.2.3"
}
}

View File

@@ -0,0 +1,6 @@
enum UNICODE {
FAILURE = '\u2716', // ✖
SUCCESS = '\u2714', // ✔
}
export { UNICODE };

View File

@@ -0,0 +1,48 @@
import fs from 'node:fs';
import { dirname, extname, resolve } from 'node:path';
import { findUpSync } from 'find-up';
/**
* 查找 package.json 文件所在的目录
* @param pathname
*/
function findUpPackageDir(pathname: string = '') {
const file = findUpSync('package.json', {
cwd: dirname(pathname),
type: 'file',
});
return dirname(file || '');
}
/**
* 根据给定的扩展名数组来查找文件是否存在,并返回对应文件路径
* @param pathname 文件路径
* @param extensions 扩展名数组
* @returns 对应文件路径,如果未找到则返回 null
*/
function findFileByExtension(
pathname: string = '',
extensions: string[] = ['.ts'],
): string {
if (extname(pathname)) {
return pathname;
}
for (const ext of extensions) {
const fullpath = resolve(pathname);
if (fs.existsSync(fullpath + ext) && fs.statSync(fullpath + ext).isFile()) {
return fullpath + ext;
}
}
for (const ext of extensions) {
const resultPath = resolve(pathname, `index${ext}`);
if (fs.existsSync(resultPath)) {
return resultPath;
}
}
return pathname;
}
export { findFileByExtension, findUpPackageDir };

View File

@@ -0,0 +1,26 @@
import path from 'node:path';
import { $ } from 'zx';
export * from '@changesets/git';
/**
* 获取暂存区文件
*/
async function getStagedFiles() {
try {
$.verbose = false;
const { stdout: lines } =
await $`git -c submodule.recurse=false diff --staged --diff-filter=ACMR --name-only --ignore-submodules -z`;
let changedList = lines ? lines.replace(/\0$/, '').split('\0') : [];
changedList = changedList.map((item) => path.resolve(process.cwd(), item));
const changedSet = new Set(changedList);
changedSet.delete('');
return [...changedSet];
} catch {
return [];
}
}
export { getStagedFiles };

View File

@@ -0,0 +1,52 @@
import { createHash } from 'node:crypto';
import { describe, expect, it } from 'vitest';
import { generatorContentHash } from './hash';
describe('generatorContentHash', () => {
it('should generate an MD5 hash for the content', () => {
const content = 'example content';
const expectedHash = createHash('md5')
.update(content, 'utf8')
.digest('hex');
const actualHash = generatorContentHash(content);
expect(actualHash).toBe(expectedHash);
});
it('should generate an MD5 hash with specified length', () => {
const content = 'example content';
const hashLength = 10;
const generatedHash = generatorContentHash(content, hashLength);
expect(generatedHash).toHaveLength(hashLength);
});
it('should correctly generate the hash with specified length', () => {
const content = 'example content';
const hashLength = 8;
const expectedHash = createHash('md5')
.update(content, 'utf8')
.digest('hex')
.slice(0, hashLength);
const generatedHash = generatorContentHash(content, hashLength);
expect(generatedHash).toBe(expectedHash);
});
it('should return full hash if hash length parameter is not provided', () => {
const content = 'example content';
const expectedHash = createHash('md5')
.update(content, 'utf8')
.digest('hex');
const actualHash = generatorContentHash(content);
expect(actualHash).toBe(expectedHash);
});
it('should handle empty content', () => {
const content = '';
const expectedHash = createHash('md5')
.update(content, 'utf8')
.digest('hex');
const actualHash = generatorContentHash(content);
expect(actualHash).toBe(expectedHash);
});
});

View File

@@ -0,0 +1,18 @@
import { createHash } from 'node:crypto';
/**
* 生产基于内容的 hash可自定义长度
* @param content
* @param hashLSize
*/
function generatorContentHash(content: string, hashLSize?: number) {
const hash = createHash('md5').update(content, 'utf8').digest('hex');
if (hashLSize) {
return hash.slice(0, hashLSize);
}
return hash;
}
export { generatorContentHash };

View File

@@ -0,0 +1,17 @@
export { UNICODE } from './constants';
export { findFileByExtension, findUpPackageDir } from './find';
export * from './git';
export { add as gitAdd, getStagedFiles } from './git';
export { generatorContentHash } from './hash';
export {
findMonorepoRoot,
getPackage,
getPackages,
getPackagesSync,
} from './monorepo';
export { prettierFormat } from './prettier';
export type { Package } from '@manypkg/get-packages';
export { consola } from 'consola';
export { readPackageJSON } from 'pkg-types';
export { rimraf } from 'rimraf';
export { $, chalk as colors, fs, spinner } from 'zx';

View File

@@ -0,0 +1,49 @@
import { dirname } from 'node:path';
import {
getPackages as getPackagesFunc,
getPackagesSync as getPackagesSyncFunc,
} from '@manypkg/get-packages';
import { findUpSync } from 'find-up';
/**
* 查找大仓的根目录
* @param cwd
*/
function findMonorepoRoot(cwd: string = process.cwd()) {
const lockFile = findUpSync(
['pnpm-lock.yaml', 'yarn.lock', 'package-lock.json'],
{
cwd,
type: 'file',
},
);
return dirname(lockFile || '');
}
/**
* 获取大仓的所有包
*/
function getPackagesSync() {
const root = findMonorepoRoot();
return getPackagesSyncFunc(root);
}
/**
* 获取大仓的所有包
*/
async function getPackages() {
const root = findMonorepoRoot();
return await getPackagesFunc(root);
}
/**
* 获取大仓指定的包
*/
async function getPackage(pkgName: string) {
const { packages } = await getPackages();
return packages.find((pkg) => pkg.packageJson.name === pkgName);
}
export { findMonorepoRoot, getPackage, getPackages, getPackagesSync };

View File

@@ -0,0 +1,20 @@
import { format, getFileInfo, resolveConfig } from 'prettier';
import { fs } from 'zx';
async function prettierFormat(filepath: string) {
const prettierOptions = await resolveConfig(filepath, {});
const fileInfo = await getFileInfo(filepath);
const input = await fs.readFile(filepath, 'utf8');
const output = await format(input, {
...prettierOptions,
parser: fileInfo.inferredParser as any,
});
if (output !== input) {
fs.writeFileSync(filepath, output, 'utf8');
}
return output;
}
export { prettierFormat };

View File

@@ -0,0 +1,5 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@vben/tsconfig/node.json",
"include": ["src"]
}