亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

關(guān)于husky

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

husky有什么用?

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

pre-commit

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

commit-msg

在此階段,可用 @commitlint/cli @commitlint/config-conventional 對(duì)提交信息進(jìn)行驗(yàn)證。但是記信息格式規(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鉤子

看上去簡(jiǎn)簡(jiǎn)單單輕輕松松,那么,開干!

先把用到的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"; // 載入動(dòng)畫(用于安裝依賴的時(shí)候)
import { exec } from "node:child_process";

package驗(yàn)證

既然是為項(xiàng)目添加,那當(dāng)然得有package.json文件啦!

const projectDirectory = cwd();
const pakFile = resolve(projectDirectory, "package.json");
if (!existsSync(pakFile)) {
    console.log(red("未在當(dāng)前目錄中找到package.json,請(qǐng)?jiān)陧?xiàng)目根目錄下運(yùn)行哦~"));
    return;
}

既然需要lint,那當(dāng)然也要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ù)?",
	},
	{
		// 處理上一步的確認(rèn)值。如果用戶沒同意,拋出異常。同意了就繼續(xù)
		type: (_, { isContinue } = {}) => {
			if (isContinue === false) {
				throw new Error(red("? 取消操作"));
			}
			return null;
		},
		name: "isContinueChecker",
	},
	{
		type: "multiselect",
		name: "selectLint",
		message: "請(qǐng)選擇已安裝的依賴:",
		choices: [
			{
				title: "eslint",
				value: "eslint",
			},
			{
				title: "prettier",
				value: "prettier",
			},
			{
				title: "stylelint",
				value: "stylelint",
			},
		],
	},
];
const questions = pakHasLint.length === 0 ? [...noLintQuestions, ...huskyQuestions] : huskyQuestions; // huskyQuestions的husky安裝的詢問語句,下面會(huì)講

husky安裝詢問

因?yàn)椴煌陌芾砥饔胁煌陌惭b命令,以及有些項(xiàng)目會(huì)不需要commit msg驗(yàn)證。所有就會(huì)有以下詢問的出現(xiàn)啦

const huskyQuestions = [
	{
		type: "select",
		name: "manager",
		message: "請(qǐng)選擇包管理器:",
		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信息驗(yàn)證?",
	},
];

使用prompts進(jìn)行交互提示

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 項(xiàng)目使用的包管理
  • commitlint 是否需要commit msg驗(yàn)證
  • selectLint 用戶自己選擇的已安裝的lint依賴

生成命令

通過manager和commitlint,可以生成要運(yùn)行的命令

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"'`;
// 需要?jiǎng)?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ù)項(xiàng)目使用的lint來生成lint-staged.config.js:

// 簡(jiǎn)單粗暴的函數(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 配置

因?yàn)閏ommitlint.config.js中的配置過于復(fù)雜。所以,我選擇在安裝完依賴后直接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: "選擇一個(gè)提交范圍(可選):",
			customScope: "請(qǐng)輸入自定義的提交范圍 :",
			subject: "填寫簡(jiǎn)短精煉的變更描述 :\n",
			body: '填寫更加詳細(xì)的變更描述(可選)。使用 "|" 換行 :\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:      ??修復(fù)缺陷 | 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:     ??測(cè)試相關(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 進(jìn)行中" },
			{ value: "closed", name: "closed:   標(biāo)記 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: "",
	},
};

被復(fù)制的路徑,和目標(biāo)路徑

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

準(zhǔn)備就緒,開始安裝!

  • 執(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);// 復(fù)制commitlint配置至項(xiàng)目中
  spinner.success({ text: green("安裝成功~準(zhǔn)備添加鉤子! ??"), mark: "?" });// 包安裝成功啦~
  const hookSpinner = createSpinner("添加husky鉤子中...").start();// 開始裝鉤子
  exec(`${createHookCommand}`, { cwd: projectDirectory }, (error) => {
    if (error) {
      hookSpinner.error({
        text: red(`添加鉤子失敗,請(qǐng)手動(dòng)執(zhí)行${createHookCommand}`),
        mark: "?",
      });
      console.error(error);
      return;
    }
    hookSpinner.success({ text: green("一切就緒! ??"), mark: "?" });// 鉤子安裝成功啦~一切ok~~
  });
});

發(fā)包

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

結(jié)尾

這個(gè)是本萌新因?yàn)閼杏窒氚裧it提交規(guī)范下又不想每次創(chuàng)項(xiàng)目都要翻文檔安裝的產(chǎn)物,沒有經(jīng)過測(cè)試,中間部分代碼會(huì)有更好的解決方案~

本代碼倉庫

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

相關(guān)文章

  • Vue登錄功能實(shí)現(xiàn)全套詳解(含封裝axios)

    Vue登錄功能實(shí)現(xiàn)全套詳解(含封裝axios)

    登錄功能對(duì)于前端剛?cè)腴T不久的同學(xué)來說較為困難,下面這篇文章主要給大家介紹了關(guān)于Vue登錄功能實(shí)現(xiàn)(含封裝axios)的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-12-12
  • vue-router路由跳轉(zhuǎn)問題 replace

    vue-router路由跳轉(zhuǎn)問題 replace

    這篇文章主要介紹了vue-router路由跳轉(zhuǎn)問題 replace,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • Vue+Echarts實(shí)現(xiàn)柱狀折線圖

    Vue+Echarts實(shí)現(xiàn)柱狀折線圖

    這篇文章主要為大家詳細(xì)介紹了Vue+Echarts實(shí)現(xiàn)柱狀折線圖,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • Vue3-KeepAlive,多個(gè)頁面使用keepalive方式

    Vue3-KeepAlive,多個(gè)頁面使用keepalive方式

    這篇文章主要介紹了Vue3-KeepAlive,多個(gè)頁面使用keepalive方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • Vue 路由返回恢復(fù)頁面狀態(tài)的操作方法

    Vue 路由返回恢復(fù)頁面狀態(tài)的操作方法

    在使用 Vue 開發(fā)前端的時(shí)候遇到一個(gè)場(chǎng)景在首頁進(jìn)行一些數(shù)據(jù)搜索,點(diǎn)擊搜索結(jié)果進(jìn)入詳情頁面,瀏覽詳情頁后返回主頁,所以需要在返回后恢復(fù)跳轉(zhuǎn)前的頁面參數(shù)狀態(tài),今天通過本文給大家分享Vue 路由頁面狀態(tài)返回的操作方法,一起看看吧
    2021-07-07
  • VUE+Element環(huán)境搭建與安裝的方法步驟

    VUE+Element環(huán)境搭建與安裝的方法步驟

    這篇文章主要介紹了VUE+Element環(huán)境搭建與安裝的方法步驟,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-01-01
  • vue中eventbus被多次觸發(fā)以及踩過的坑

    vue中eventbus被多次觸發(fā)以及踩過的坑

    這篇文章主要介紹了vue中eventbus被多次觸發(fā)以及踩過的坑,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-12-12
  • vue中watch監(jiān)聽路由傳來的參數(shù)變化問題

    vue中watch監(jiān)聽路由傳來的參數(shù)變化問題

    這篇文章主要介紹了vue中watch監(jiān)聽路由傳來的參數(shù)變化,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-07-07
  • vue+elementUI實(shí)現(xiàn)當(dāng)渲染文本超出一定字?jǐn)?shù)時(shí)顯示省略號(hào)

    vue+elementUI實(shí)現(xiàn)當(dāng)渲染文本超出一定字?jǐn)?shù)時(shí)顯示省略號(hào)

    這篇文章主要介紹了vue+elementUI實(shí)現(xiàn)當(dāng)渲染文本超出一定字?jǐn)?shù)時(shí)顯示省略號(hào),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • Element?Plus在el-form-item中設(shè)置justify-content無效的解決方案

    Element?Plus在el-form-item中設(shè)置justify-content無效的解決方案

    這篇文章主要介紹了Element?Plus在el-form-item中設(shè)置justify-content無效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-10-10

最新評(píng)論