chore: init project
This commit is contained in:
7
internal/node-utils/build.config.ts
Normal file
7
internal/node-utils/build.config.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { defineBuildConfig } from 'unbuild';
|
||||
|
||||
export default defineBuildConfig({
|
||||
clean: true,
|
||||
declaration: true,
|
||||
entries: ['src/index'],
|
||||
});
|
45
internal/node-utils/package.json
Normal file
45
internal/node-utils/package.json
Normal 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"
|
||||
}
|
||||
}
|
6
internal/node-utils/src/constants.ts
Normal file
6
internal/node-utils/src/constants.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
enum UNICODE {
|
||||
FAILURE = '\u2716', // ✖
|
||||
SUCCESS = '\u2714', // ✔
|
||||
}
|
||||
|
||||
export { UNICODE };
|
48
internal/node-utils/src/find.ts
Normal file
48
internal/node-utils/src/find.ts
Normal 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 };
|
26
internal/node-utils/src/git.ts
Normal file
26
internal/node-utils/src/git.ts
Normal 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 };
|
52
internal/node-utils/src/hash.test.ts
Normal file
52
internal/node-utils/src/hash.test.ts
Normal 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);
|
||||
});
|
||||
});
|
18
internal/node-utils/src/hash.ts
Normal file
18
internal/node-utils/src/hash.ts
Normal 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 };
|
17
internal/node-utils/src/index.ts
Normal file
17
internal/node-utils/src/index.ts
Normal 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';
|
49
internal/node-utils/src/monorepo.ts
Normal file
49
internal/node-utils/src/monorepo.ts
Normal 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 };
|
20
internal/node-utils/src/prettier.ts
Normal file
20
internal/node-utils/src/prettier.ts
Normal 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 };
|
5
internal/node-utils/tsconfig.json
Normal file
5
internal/node-utils/tsconfig.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@vben/tsconfig/node.json",
|
||||
"include": ["src"]
|
||||
}
|
Reference in New Issue
Block a user