初始化

This commit is contained in:
2026-03-26 20:18:20 +08:00
commit 120a5b4dfd
368 changed files with 35926 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
import { generateChangelog, generateTotalChangelog } from '@soybeanjs/changelog';
import type { ChangelogOption } from '@soybeanjs/changelog';
export async function genChangelog(options?: Partial<ChangelogOption>, total = false) {
if (total) {
await generateTotalChangelog(options);
} else {
await generateChangelog(options);
}
}

View File

@@ -0,0 +1,5 @@
import { rimraf } from 'rimraf';
export async function cleanup(paths: string[]) {
await rimraf(paths, { glob: true });
}

View File

@@ -0,0 +1,84 @@
import path from 'node:path';
import { readFileSync } from 'node:fs';
import { prompt } from 'enquirer';
import { execCommand } from '../shared';
import { locales } from '../locales';
import type { Lang } from '../locales';
interface PromptObject {
types: string;
scopes: string;
description: string;
}
/**
* Git commit with Conventional Commits standard
*
* @param lang
*/
export async function gitCommit(lang: Lang = 'en-us') {
const { gitCommitMessages, gitCommitTypes, gitCommitScopes } = locales[lang];
const typesChoices = gitCommitTypes.map(([value, msg]) => {
const nameWithSuffix = `${value}:`;
const message = `${nameWithSuffix.padEnd(12)}${msg}`;
return {
name: value,
message
};
});
const scopesChoices = gitCommitScopes.map(([value, msg]) => ({
name: value,
message: `${value.padEnd(30)} (${msg})`
}));
const result = await prompt<PromptObject>([
{
name: 'types',
type: 'select',
message: gitCommitMessages.types,
choices: typesChoices
},
{
name: 'scopes',
type: 'select',
message: gitCommitMessages.scopes,
choices: scopesChoices
},
{
name: 'description',
type: 'text',
message: gitCommitMessages.description
}
]);
const breaking = result.description.startsWith('!') ? '!' : '';
const description = result.description.replace(/^!/, '').trim();
const commitMsg = `${result.types}(${result.scopes})${breaking}: ${description}`;
await execCommand('git', ['commit', '-m', commitMsg], { stdio: 'inherit' });
}
/** Git commit message verify */
export async function gitCommitVerify(lang: Lang = 'en-us', ignores: RegExp[] = []) {
const gitPath = await execCommand('git', ['rev-parse', '--show-toplevel']);
const gitMsgPath = path.join(gitPath, '.git', 'COMMIT_EDITMSG');
const commitMsg = readFileSync(gitMsgPath, 'utf8').trim();
if (ignores.some(regExp => regExp.test(commitMsg))) return;
const REG_EXP = /(?<type>[a-z]+)(?:\((?<scope>.+)\))?(?<breaking>!)?: (?<description>.+)/i;
if (!REG_EXP.test(commitMsg)) {
const errorMsg = locales[lang].gitCommitVerify;
throw new Error(errorMsg);
}
}

View File

@@ -0,0 +1,7 @@
export * from './git-commit';
export * from './cleanup';
export * from './update-pkg';
export * from './changelog';
export * from './release';
export * from './router';
export * from './print-soybean';

View File

@@ -0,0 +1,16 @@
const SOYBEANJS =
'2020205f5f5f5f5f202020205f5f5f5f2020205f5f20202020205f5f20205f5f5f5f202020205f5f5f5f5f5f20202020202020202020202020205f2020205f20202020202020205f202020205f5f5f5f5f200a20202f205f5f5f5f7c20202f205f5f205c20205c205c2020202f202f207c20205f205c20207c20205f5f5f5f7c20202020202f5c20202020207c205c207c207c2020202020207c207c20202f205f5f5f5f7c0a207c20285f5f5f2020207c207c20207c207c20205c205c5f2f202f20207c207c5f29207c207c207c5f5f202020202020202f20205c202020207c20205c7c207c2020202020207c207c207c20285f5f5f20200a20205c5f5f5f205c20207c207c20207c207c2020205c2020202f2020207c20205f203c20207c20205f5f7c20202020202f202f5c205c2020207c202e2060207c20205f2020207c207c20205c5f5f5f205c200a20205f5f5f5f29207c207c207c5f5f7c207c202020207c207c202020207c207c5f29207c207c207c5f5f5f5f2020202f205f5f5f5f205c20207c207c5c20207c207c207c5f5f7c207c20205f5f5f5f29207c0a207c5f5f5f5f5f2f2020205c5f5f5f5f2f20202020207c5f7c202020207c5f5f5f5f2f20207c5f5f5f5f5f5f7c202f5f2f202020205c5f5c207c5f7c205c5f7c20205c5f5f5f5f2f20207c5f5f5f5f5f2f20';
function decode16(str: string): string {
const arr: string[] = [];
for (let i = 0; i < str.length; i += 2) {
const asciiCode = Number.parseInt(str.slice(i, i + 2), 16);
const charValue = String.fromCharCode(asciiCode);
arr.push(charValue);
}
return arr.join('');
}
export async function printSoybean() {
console.log(decode16(SOYBEANJS));
}

View File

@@ -0,0 +1,12 @@
import { versionBump } from 'bumpp';
export async function release(execute = 'pnpm sa changelog', push = true) {
await versionBump({
files: ['**/package.json', '!**/node_modules'],
execute,
all: true,
tag: true,
commit: 'chore(projects): release v%s',
push
});
}

View File

@@ -0,0 +1,90 @@
import process from 'node:process';
import path from 'node:path';
import { writeFile } from 'node:fs/promises';
import { existsSync, mkdirSync } from 'node:fs';
import { prompt } from 'enquirer';
import { green, red } from 'kolorist';
interface PromptObject {
routeName: string;
addRouteParams: boolean;
routeParams: string;
}
/** generate route */
export async function generateRoute() {
const result = await prompt<PromptObject>([
{
name: 'routeName',
type: 'text',
message: 'please enter route name',
initial: 'demo-route_child'
},
{
name: 'addRouteParams',
type: 'confirm',
message: 'add route params?',
initial: false
}
]);
if (result.addRouteParams) {
const answers = await prompt<PromptObject>({
name: 'routeParams',
type: 'text',
message: 'please enter route params',
initial: 'id'
});
Object.assign(result, answers);
}
const PAGE_DIR_NAME_PATTERN = /^[\w-]+[0-9a-zA-Z]+$/;
if (!PAGE_DIR_NAME_PATTERN.test(result.routeName)) {
throw new Error(`${red('route name is invalid, it only allow letters, numbers, "-" or "_"')}.
For example:
(1) one level route: ${green('demo-route')}
(2) two level route: ${green('demo-route_child')}
(3) multi level route: ${green('demo-route_child_child')}
(4) group route: ${green('_ignore_demo-route')}'
`);
}
const PARAM_REG = /^\w+$/g;
if (result.routeParams && !PARAM_REG.test(result.routeParams)) {
throw new Error(red('route params is invalid, it only allow letters, numbers or "_".'));
}
const cwd = process.cwd();
const [dir, ...rest] = result.routeName.split('_') as string[];
let routeDir = path.join(cwd, 'src', 'views', dir);
if (rest.length) {
routeDir = path.join(routeDir, rest.join('_'));
}
if (!existsSync(routeDir)) {
mkdirSync(routeDir, { recursive: true });
} else {
throw new Error(red('route already exists'));
}
const fileName = result.routeParams ? `[${result.routeParams}].vue` : 'index.vue';
const vueTemplate = `<script setup lang="ts"></script>
<template>
<div>${result.routeName}</div>
</template>
<style scoped></style>
`;
const filePath = path.join(routeDir, fileName);
await writeFile(filePath, vueTemplate);
}

View File

@@ -0,0 +1,5 @@
import { execCommand } from '../shared';
export async function updatePkg(args: string[] = ['--deep', '-u']) {
execCommand('npx', ['ncu', ...args], { stdio: 'inherit' });
}