最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

項目中一鍵添加husky實現(xiàn)詳解

 更新時間:2022年09月03日 10:41:06   作者:Yomuki  
這篇文章主要為大家介紹了項目中一鍵添加husky實現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

關(guān)于husky

前置條件:項目已關(guān)聯(lián)了git。

husky有什么用?

當我們commit message時,可以進行測試和lint操作,保證倉庫里的代碼是優(yōu)雅的。 當我們進行commit操作時,會觸發(fā)pre-commit,在此階段,可進行test和lint。其后,會觸發(fā)commit-msg,對commit的message內(nèi)容進行驗證。

pre-commit

一般的lint會全局掃描,但是在此階段,我們僅需要對暫存區(qū)的代碼進行l(wèi)int即可。所以使用lint-staged插件。

commit-msg

在此階段,可用 @commitlint/cli @commitlint/config-conventional 對提交信息進行驗證。但是記信息格式規(guī)范真的太太太太麻煩了,所以可用 commitizen cz-git 生成提交信息。

一鍵添加husky

從上述說明中,可以得出husky配置的基本流程:

  • 安裝husky;安裝lint-staged @commitlint/cli @commitlint/config-conventional commitizen cz-git
  • 寫commitlint和lint-staged的配置文件
  • 修改package.json中的scripts和config
  • 添加pre-commit和commit-msg鉤子

看上去簡簡單單輕輕松松,那么,開干!

先把用到的import拿出來溜溜

import { red, cyan, green } from "kolorist"; // 打印顏色文字
import { copyFileSync, existsSync, readFileSync, writeFileSync } from "node:fs";
import { resolve } from "node:path";
import { cwd } from "node:process";
import prompts from "prompts";// 命令行交互提示
import { fileURLToPath } from "node:url";
import { getLintStagedOption } from "./src/index.js";// 獲取lint-staged配置 ,后頭說
import { createSpinner } from "nanospinner"; // 載入動畫(用于安裝依賴的時候)
import { exec } from "node:child_process";

package驗證

既然是為項目添加,那當然得有package.json文件啦!

const projectDirectory = cwd();
const pakFile = resolve(projectDirectory, "package.json");
if (!existsSync(pakFile)) {
    console.log(red("未在當前目錄中找到package.json,請在項目根目錄下運行哦~"));
    return;
}

既然需要lint,那當然也要eslint/prettier/stylelint啦~

const pakContent = JSON.parse(readFileSync(pakFile));
const devs = {
    ...(pakContent?.devDependencies || {}),
    ...(pakContent?.dependencies || {}),
};
const pakHasLint = needDependencies.filter((item) => {
    return item in devs;
});

但是考慮到有可能lint安裝在了全局,所以這邊就不直接return了,而是向questions中插入一些詢問來確定到底安裝了哪些lint。

const noLintQuestions = [
	{
		type: "confirm",
		name: "isContinue",
		message: "未在package.json中找到eslint/prettier/stylelint,是否繼續(xù)?",
	},
	{
		// 處理上一步的確認值。如果用戶沒同意,拋出異常。同意了就繼續(xù)
		type: (_, { isContinue } = {}) => {
			if (isContinue === false) {
				throw new Error(red("? 取消操作"));
			}
			return null;
		},
		name: "isContinueChecker",
	},
	{
		type: "multiselect",
		name: "selectLint",
		message: "請選擇已安裝的依賴:",
		choices: [
			{
				title: "eslint",
				value: "eslint",
			},
			{
				title: "prettier",
				value: "prettier",
			},
			{
				title: "stylelint",
				value: "stylelint",
			},
		],
	},
];
const questions = pakHasLint.length === 0 ? [...noLintQuestions, ...huskyQuestions] : huskyQuestions; // huskyQuestions的husky安裝的詢問語句,下面會講

husky安裝詢問

因為不同的包管理器有不同的安裝命令,以及有些項目會不需要commit msg驗證。所有就會有以下詢問的出現(xiàn)啦

const huskyQuestions = [
	{
		type: "select",
		name: "manager",
		message: "請選擇包管理器:",
		choices: [
			{
				title: "npm",
				value: "npm",
			},
			{
				title: "yarn1",
				value: "yarn1",
			},
			{
				title: "yarn2+",
				value: "yarn2",
			},
			{
				title: "pnpm",
				value: "pnpm",
			},
			{
				title: "pnpm 根工作區(qū)",
				value: "pnpmw",
			},
		],
	},
	{
		type: "confirm",
		name: "commitlint",
		message: "是否需要commit信息驗證?",
	},
];

使用prompts進行交互提示

let result = {};
try {
  result = await prompts(questions, {
      onCancel: () => {
      throw new Error(red("?Bye~"));
    },
  });
} catch (cancelled) {
  console.log(cancelled.message);
  return;
}
const { selectLint, manager, commitlint } = result;

這樣子,我們就獲取到了:

  • manager 項目使用的包管理
  • commitlint 是否需要commit msg驗證
  • selectLint 用戶自己選擇的已安裝的lint依賴

生成命令

通過manager和commitlint,可以生成要運行的命令

const huskyCommandMap = {
  npm: "npx husky-init && npm install && npm install --save-dev ",
  yarn1: "npx husky-init && yarn && yarn add --dev ",
  yarn2: "yarn dlx husky-init --yarn2 && yarn && yarn add --dev ",
  pnpm: "pnpm dlx husky-init && pnpm install && pnpm install --save-dev ",
  pnpmw: "pnpm dlx husky-init && pnpm install -w && pnpm install --save-dev -w ",
};
const preCommitPackages = "lint-staged";
const commitMsgPackages = "@commitlint/cli @commitlint/config-conventional commitizen cz-git";
// 需要安裝的包
const packages = commitlint ? `${preCommitPackages} ${commitMsgPackages}` : preCommitPackages;
// 需要安裝的包的安裝命令
const command = `${huskyCommandMap[manager]}${packages}`;
const createCommitHook = `npx husky set .husky/pre-commit "npm run lint:lint-staged"`;
const createMsgHook = `npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'`;
// 需要創(chuàng)建鉤子的命令
const createHookCommand = commitlint ? `${createCommitHook} && ${createMsgHook}` : createCommitHook;

lint-staged 配置

一般的lint-staged.config.js長這樣:

module.exports = {
	"*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"],
	"{!(package)*.json,*.code-snippets,.!(browserslist)*rc}": ["prettier --write--parser json"],
	"package.json": ["prettier --write"],
	"*.vue": ["eslint --fix", "prettier --write", "stylelint --fix"],
	"*.{scss,less,styl,html}": ["stylelint --fix", "prettier --write"],
	"*.md": ["prettier --write"],
};

所以呢,需要根據(jù)項目使用的lint來生成lint-staged.config.js:

// 簡單粗暴的函數(shù)
export function getLintStagedOption(lint) {
	const jsOp = [],
		jsonOp = [],
		pakOp = [],
		vueOp = [],
		styleOp = [],
		mdOp = [];
	if (lint.includes("eslint")) {
		jsOp.push("eslint --fix");
		vueOp.push("eslint --fix");
	}
	if (lint.includes("prettier")) {
		jsOp.push("prettier --write");
		vueOp.push("prettier --write");
		mdOp.push("prettier --write");
		jsonOp.push("prettier --write--parser json");
		pakOp.push("prettier --write");
		styleOp.push("prettier --write");
	}
	if (lint.includes("stylelint")) {
		vueOp.push("stylelint --fix");
		styleOp.push("stylelint --fix");
	}
	return {
		"*.{js,jsx,ts,tsx}": jsOp,
		"{!(package)*.json,*.code-snippets,.!(browserslist)*rc}": jsonOp,
		"package.json": pakOp,
		"*.vue": vueOp,
		"*.{scss,less,styl,html}": styleOp,
		"*.md": mdOp,
	};
}
// lint-staged.config.js 內(nèi)容
const lintStagedContent = `module.exports =${JSON.stringify(getLintStagedOption(selectLint || pakHasLint))}`;
// lint-staged.config.js 文件
const lintStagedFile = resolve(projectDirectory, "lint-staged.config.js");

commitlint 配置

因為commitlint.config.js中的配置過于復雜。所以,我選擇在安裝完依賴后直接copy文件!被copy的文件內(nèi)容:

// @see: https://cz-git.qbenben.com/zh/guide
/** @type {import('cz-git').UserConfig} */
module.exports = {
	ignores: [(commit) => commit.includes("init")],
	extends: ["@commitlint/config-conventional"],
	// parserPreset: "conventional-changelog-conventionalcommits",
	rules: {
		// @see: https://commitlint.js.org/#/reference-rules
		"body-leading-blank": [2, "always"],
		"footer-leading-blank": [1, "always"],
		"header-max-length": [2, "always", 108],
		"subject-empty": [2, "never"],
		"type-empty": [2, "never"],
		"subject-case": [0],
		"type-enum": [2, "always", ["feat", "fix", "docs", "style", "refactor", "perf", "test", "build", "ci", "chore", "revert"]],
	},
	prompt: {
		alias: { fd: "docs: fix typos" },
		messages: {
			type: "選擇你要提交的類型 :",
			scope: "選擇一個提交范圍(可選):",
			customScope: "請輸入自定義的提交范圍 :",
			subject: "填寫簡短精煉的變更描述 :\n",
			body: '填寫更加詳細的變更描述(可選)。使用 "|" 換行 :\n',
			breaking: '列舉非兼容性重大的變更(可選)。使用 "|" 換行 :\n',
			footerPrefixsSelect: "選擇關(guān)聯(lián)issue前綴(可選):",
			customFooterPrefixs: "輸入自定義issue前綴 :",
			footer: "列舉關(guān)聯(lián)issue (可選) 例如: #31, #I3244 :\n",
			confirmCommit: "是否提交或修改commit ?",
		},
		types: [
			{ value: "feat", name: "feat:     ??新增功能 | A new feature", emoji: "??" },
			{ value: "fix", name: "fix:      ??修復缺陷 | A bug fix", emoji: "??" },
			{ value: "docs", name: "docs:     ??文檔更新 | Documentation only changes", emoji: "??" },
			{ value: "style", name: "style:    ??代碼格式 | Changes that do not affect the meaning of the code", emoji: "??" },
			{
				value: "refactor",
				name: "refactor: ??代碼重構(gòu) | A code change that neither fixes a bug nor adds a feature",
				emoji: "??",
			},
			{ value: "perf", name: "perf:     ??性能提升 | A code change that improves performance", emoji: "??" },
			{ value: "test", name: "test:     ??測試相關(guān) | Adding missing tests or correcting existing tests", emoji: "??" },
			{ value: "build", name: "build:    ??構(gòu)建相關(guān) | Changes that affect the build system or external dependencies", emoji: "??" },
			{ value: "ci", name: "ci:       ??持續(xù)集成 | Changes to our CI configuration files and scripts", emoji: "??" },
			{ value: "revert", name: "revert:   ??回退代碼 | Revert to a commit", emoji: "??" },
			{ value: "chore", name: "chore:    ??其他修改 | Other changes that do not modify src or test files", emoji: "??" },
		],
		useEmoji: true,
		emojiAlign: "center",
		themeColorCode: "",
		scopes: [],
		allowCustomScopes: true,
		allowEmptyScopes: true,
		customScopesAlign: "bottom",
		customScopesAlias: "custom | 以上都不是?我要自定義",
		emptyScopesAlias: "empty | 跳過",
		upperCaseSubject: false,
		markBreakingChangeMode: false,
		allowBreakingChanges: ["feat", "fix"],
		breaklineNumber: 100,
		breaklineChar: "|",
		skipQuestions: [],
		issuePrefixs: [
			// 如果使用 gitee 作為開發(fā)管理
			{ value: "link", name: "link:     鏈接 ISSUES 進行中" },
			{ value: "closed", name: "closed:   標記 ISSUES 已完成" },
		],
		customIssuePrefixsAlign: "top",
		emptyIssuePrefixsAlias: "skip | 跳過",
		customIssuePrefixsAlias: "custom | 自定義前綴",
		allowCustomIssuePrefixs: true,
		allowEmptyIssuePrefixs: true,
		confirmColorize: true,
		maxHeaderLength: Infinity,
		maxSubjectLength: Infinity,
		minSubjectLength: 0,
		scopeOverrides: undefined,
		defaultBody: "",
		defaultIssues: "",
		defaultScope: "",
		defaultSubject: "",
	},
};

被復制的路徑,和目標路徑

const commitlintFile = resolve(projectDirectory, "commitlint.config.js");
const commitlintFileTemplateDir = resolve(fileURLToPath(import.meta.url), "../src/template", "commitlint.config.js");

準備就緒,開始安裝!

  • 執(zhí)行剛剛生成的安裝命令
  • 更改package.json內(nèi)容
  • 寫入配置文件
  • 添加鉤子
const spinner = createSpinner("Installing packages...").start();
exec(`${command}`, { cwd: projectDirectory }, (error) => {
  if (error) {
    spinner.error({
      text: red("Failed to install packages!"),
      mark: "?",
    });
    console.error(error);
    return;
  }
  /*  更改package.json內(nèi)容 開始  */
  let newPakContent = JSON.parse(readFileSync(pakFile));// 獲取最新的包內(nèi)容
  newPakContent.scripts = {
    ...newPakContent.scripts,
    "lint:lint-staged": "lint-staged",
    commit: "git add . && git-cz",
  };
  newPakContent.config = {
    ...(newPakContent?.config || {}),
    commitizen: {
      path: "node_modules/cz-git",
    },
  };
  writeFileSync(pakFile, JSON.stringify(newPakContent));// 寫入
  /*  更改package.json內(nèi)容 結(jié)束  */
  writeFileSync(lintStagedFile, lintStagedContent);// 寫入lint-staged配置
  copyFileSync(commitlintFileTemplateDir, commitlintFile);// 復制commitlint配置至項目中
  spinner.success({ text: green("安裝成功~準備添加鉤子! ??"), mark: "?" });// 包安裝成功啦~
  const hookSpinner = createSpinner("添加husky鉤子中...").start();// 開始裝鉤子
  exec(`${createHookCommand}`, { cwd: projectDirectory }, (error) => {
    if (error) {
      hookSpinner.error({
        text: red(`添加鉤子失敗,請手動執(zhí)行${createHookCommand}`),
        mark: "?",
      });
      console.error(error);
      return;
    }
    hookSpinner.success({ text: green("一切就緒! ??"), mark: "?" });// 鉤子安裝成功啦~一切ok~~
  });
});

發(fā)包

最后,發(fā)下包,就可以在其他項目中使用啦

結(jié)尾

這個是本萌新因為懶又想把git提交規(guī)范下又不想每次創(chuàng)項目都要翻文檔安裝的產(chǎn)物,沒有經(jīng)過測試,中間部分代碼會有更好的解決方案~

本代碼倉庫

以上就是項目中一鍵添加husky實現(xiàn)詳解的詳細內(nèi)容,更多關(guān)于項目一鍵添加husky的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論

郧西县| 贵阳市| 五指山市| 神木县| 闽清县| 雷山县| 诏安县| 理塘县| 宁河县| 常熟市| 邛崃市| 景洪市| 北川| 五常市| 泉州市| 老河口市| 泾源县| 托克托县| 许昌县| 怀化市| 水富县| 晴隆县| 宣汉县| 通海县| 泽库县| 洛南县| 民勤县| 广南县| 海城市| 景宁| 盐津县| 吉林省| 和平区| 新竹市| 乡城县| 东辽县| 南乐县| 云龙县| 新河县| 桃江县| 朝阳市|