feat(projects): 1.0 beta

This commit is contained in:
Soybean
2023-11-17 08:45:00 +08:00
parent 1ea4817f6a
commit e918a2c0f5
499 changed files with 15918 additions and 24708 deletions

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,75 @@
import path from 'node:path';
import { readFileSync } from 'node:fs';
import enquirer from 'enquirer';
import { bgRed, red, green } from 'kolorist';
import { execCommand } from '../shared';
import type { CliOption } from '../types';
interface PromptObject {
types: string;
scopes: string;
description: string;
}
export async function gitCommit(
gitCommitTypes: CliOption['gitCommitTypes'],
gitCommitScopes: CliOption['gitCommitScopes']
) {
const typesChoices = gitCommitTypes.map(([name, title]) => {
const nameWithSuffix = `${name}:`;
const message = `${nameWithSuffix.padEnd(12)}${title}`;
return {
name,
message
};
});
const scopesChoices = gitCommitScopes.map(([name, title]) => ({
name,
message: `${name.padEnd(30)} (${title})`
}));
const result = await enquirer.prompt<PromptObject>([
{
name: 'types',
type: 'select',
message: 'Please select a type',
choices: typesChoices
},
{
name: 'scopes',
type: 'select',
message: 'Please select a scope',
choices: scopesChoices
},
{
name: 'description',
type: 'text',
message: 'Please enter a description'
}
]);
const commitMsg = `${result.types}(${result.scopes}): ${result.description}`;
await execCommand('git', ['commit', '-m', commitMsg], { stdio: 'inherit' });
}
export async function gitCommitVerify() {
const gitPath = await execCommand('git', ['rev-parse', '--show-toplevel']);
const gitMsgPath = path.join(gitPath, '.git', 'COMMIT_EDITMSG');
const commitMsg = readFileSync(gitMsgPath, 'utf8').trim();
const REG_EXP = /(?<type>[a-z]+)(\((?<scope>.+)\))?(?<breaking>!)?: (?<description>.+)/i;
if (!REG_EXP.test(commitMsg)) {
throw new Error(
`${bgRed(' ERROR ')} ${red('git commit message must match the Conventional Commits standard!')}\n\n${green(
'Recommended to use the command `pnpm commit` to generate Conventional Commits compliant commit information.\nGet more info about Conventional Commits, follow this link: https://conventionalcommits.org'
)}`
);
}
}

View File

@ -0,0 +1,5 @@
export * from './git-commit';
export * from './cleanup';
export * from './update-pkg';
export * from './prettier';
export * from './lint-staged';

View File

@ -0,0 +1,5 @@
export async function execLintStaged(config: Record<string, string | string[]>) {
const lintStaged = (await import('lint-staged')).default;
return lintStaged({ config, allowEmpty: true });
}

View File

@ -0,0 +1,7 @@
import { execCommand } from '../shared';
export async function prettierWrite(writeGlob: string[]) {
await execCommand('npx', ['prettier', '--write', '.', ...writeGlob], {
stdio: 'inherit'
});
}

View File

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

View File

@ -0,0 +1,74 @@
import { loadConfig } from 'c12';
import type { CliOption } from '../types';
const eslintExt = '*.{js,jsx,mjs,cjs,ts,tsx,vue}';
const defaultOptions: CliOption = {
cwd: process.cwd(),
cleanupDirs: [
'**/dist',
'**/package-lock.json',
'**/yarn.lock',
'**/pnpm-lock.yaml',
'**/node_modules',
'!node_modules/**'
],
gitCommitTypes: [
['feat', 'A new feature'],
['fix', 'A bug fix'],
['docs', 'Documentation only changes'],
['style', 'Changes that do not affect the meaning of the code'],
['refactor', 'A code change that neither fixes a bug nor adds a feature'],
['perf', 'A code change that improves performance'],
['test', 'Adding missing tests or correcting existing tests'],
['build', 'Changes that affect the build system or external dependencies'],
['ci', 'Changes to our CI configuration files and scripts'],
['chore', "Other changes that don't modify src or test files"],
['revert', 'Reverts a previous commit']
],
gitCommitScopes: [
['projects', 'project'],
['components', 'components'],
['hooks', 'hook functions'],
['utils', 'utils functions'],
['types', 'TS declaration'],
['styles', 'style'],
['deps', 'project dependencies'],
['release', 'release project'],
['other', 'other changes']
],
ncuCommandArgs: ['--deep', '-u'],
prettierWriteGlob: [
`!**/${eslintExt}`,
'!*.min.*',
'!CHANGELOG.md',
'!dist',
'!LICENSE*',
'!output',
'!coverage',
'!public',
'!temp',
'!package-lock.json',
'!pnpm-lock.yaml',
'!yarn.lock',
'!.github',
'!__snapshots__',
'!node_modules'
],
lintStagedConfig: {
[eslintExt]: 'eslint --fix',
'*': 'sa prettier-write'
}
};
export async function loadCliOptions(overrides?: Partial<CliOption>, cwd = process.cwd()) {
const { config } = await loadConfig<Partial<CliOption>>({
name: 'soybean',
defaults: defaultOptions,
overrides,
cwd,
packageJson: true
});
return config as CliOption;
}

70
packages/scripts/src/index.ts Executable file
View File

@ -0,0 +1,70 @@
import cac from 'cac';
import { blue, lightGreen } from 'kolorist';
import { version } from '../package.json';
import { cleanup, updatePkg, gitCommit, gitCommitVerify, prettierWrite, execLintStaged } from './commands';
import { loadCliOptions } from './config';
type Command = 'cleanup' | 'update-pkg' | 'git-commit' | 'git-commit-verify' | 'prettier-write' | 'lint-staged';
type CommandAction<A extends object> = (args?: A) => Promise<void> | void;
type CommandWithAction<A extends object = object> = Record<Command, { desc: string; action: CommandAction<A> }>;
interface CommandArg {
total?: boolean;
}
export async function setupCli() {
const cliOptions = await loadCliOptions();
const cli = cac(blue('soybean'));
cli.version(lightGreen(version)).help();
const commands: CommandWithAction<CommandArg> = {
cleanup: {
desc: 'delete dirs: node_modules, dist, etc.',
action: async () => {
await cleanup(cliOptions.cleanupDirs);
}
},
'update-pkg': {
desc: 'update package.json dependencies versions',
action: async () => {
await updatePkg(cliOptions.ncuCommandArgs);
}
},
'git-commit': {
desc: 'git commit, generate commit message which match Conventional Commits standard',
action: async () => {
await gitCommit(cliOptions.gitCommitTypes, cliOptions.gitCommitScopes);
}
},
'git-commit-verify': {
desc: 'verify git commit message, make sure it match Conventional Commits standard',
action: async () => {
await gitCommitVerify();
}
},
'prettier-write': {
desc: 'run prettier --write',
action: async () => {
await prettierWrite(cliOptions.prettierWriteGlob);
}
},
'lint-staged': {
desc: 'run lint-staged',
action: async () => {
await execLintStaged(cliOptions.lintStagedConfig);
}
}
};
for (const [command, { desc, action }] of Object.entries(commands)) {
cli.command(command, lightGreen(desc)).action(action);
}
cli.parse();
}
setupCli();

View File

@ -0,0 +1,7 @@
import type { Options } from 'execa';
export async function execCommand(cmd: string, args: string[], options?: Options) {
const { execa } = await import('execa');
const res = await execa(cmd, args, options);
return res?.stdout?.trim() || '';
}

View File

@ -0,0 +1,37 @@
export interface CliOption {
/**
* the project root directory
*/
cwd: string;
/**
* cleanup dirs
* @default
* ```json
* ["** /dist", "** /pnpm-lock.yaml", "** /node_modules", "!node_modules/**"]
* ```
* @description glob pattern syntax {@link https://github.com/isaacs/minimatch}
*/
cleanupDirs: string[];
/**
* git commit types
*/
gitCommitTypes: [string, string][];
/**
* git commit scopes
*/
gitCommitScopes: [string, string][];
/**
* npm-check-updates command args
* @default ["--deep","-u"]
*/
ncuCommandArgs: string[];
/**
* prettier write glob
* @description glob pattern syntax {@link https://github.com/micromatch/micromatch}
*/
prettierWriteGlob: string[];
/**
* lint-staged config
*/
lintStagedConfig: Record<string, string | string[]>;
}