跳到主要内容

vite-react-typescript

2023年02月24日
柏拉文
越努力,越幸运

一、根目录>.husky


.husky 文件夹创建过程如下:

  1. package.json 增加命令

    "scripts": {
    "start": "vite",
    "tsc": "tsc --noEmit",
    "build": "tsc && vite build",
    "lint-staged": "lint-staged",
    "postinstall": "husky install",
    "prettier": "prettier --write src/**/*.{ts,tsx}",
    "stylelint-fix": "stylelint src/**/*.scss --fix",
    "eslint-fix": "eslint src --ext .jsx,.js,.ts,.tsx --fix",
    "commitlint": "commitlint --config commitlint.config.js -e -V"
    },
  2. 运行命令npm run postinstall,将会自动创建.husky文件

1.1 commit-msg

commit-msg脚本生成过程如下:

  1. package.json添加命令如下

    "scripts": {
    "start": "vite",
    "tsc": "tsc --noEmit",
    "build": "tsc && vite build",
    "lint-staged": "lint-staged",
    "postinstall": "husky install",
    "prettier": "prettier --write src/**/*.{ts,tsx}",
    "stylelint-fix": "stylelint src/**/*.scss --fix",
    "eslint-fix": "eslint src --ext .jsx,.js,.ts,.tsx --fix",
    "commitlint": "commitlint --config commitlint.config.js -e -V"
    },
  2. 根目录运行命令npx husky add .husky/commit-msg "npm run commitlint",生成文件如下:

    #!/bin/sh
    . "$(dirname "$0")/_/husky.sh"

    npm run commitlint

1.2 pre-commit

commit-msg脚本生成过程如下:

  1. package.json添加命令如下

    "scripts": {
    "start": "vite",
    "tsc": "tsc --noEmit",
    "build": "tsc && vite build",
    "lint-staged": "lint-staged",
    "postinstall": "husky install",
    "prettier": "prettier --write src/**/*.{ts,tsx}",
    "stylelint-fix": "stylelint src/**/*.scss --fix",
    "eslint-fix": "eslint src --ext .jsx,.js,.ts,.tsx --fix",
    "commitlint": "commitlint --config commitlint.config.js -e -V"
    },
  2. 根目录运行命令npx husky add .husky/commit-msg "npm run commitlint",生成文件如下:

    #!/bin/sh
    . "$(dirname "$0")/_/husky.sh"

    npm run lint-staged

二、根目录>.vscode


2.1 settings.json

{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.fixAll.stylelint": true
},
"eslint.options": {
"extensions": [".ts", ".tsx"]
},
"eslint.validate": ["typescript", "typescriptreact"],
"stylelint.validate": ["css", "scss"]
}

2.2 extensions.json

{
"recommendations": [
"streetsidesoftware.code-spell-checker",
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"stylelint.vscode-stylelint",
"EditorConfig.EditorConfig",
"Gruntfuggly.todo-tree"
],
"unwantedRecommendations": []
}

三、根目录>.editorconfig


安装EditorConfig for VS Code插件之后,在项目根目录新建.editorconfig。创建完成之后,这个文件里面定义的代码规范规则会高于编译器默认的代码规范规则。

[*.{ts,tsx}]
indent_style = space
indent_size = 4
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
max_line_length = 200

四、根目录>.eslintignore


dist/
public/
node_modules/

五、根目录>.eslintrc.js


module.exports = {
root: true,
env: {
browser: true,
node: true,
es6: true,
commonjs: true,
},
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
extends: [
'eslint:recommended',
'airbnb',
'prettier',
'plugin:react/recommended',
'plugin:@typescript-eslint/eslint-recommended',
'plugin:react/jsx-runtime',
],
plugins: [
'react',
'react-hooks',
'import',
'prettier',
'@typescript-eslint',
],
rules: {
semi: 2,
'no-unused-vars': 0,
'import/no-unresolved': [
2,
{
ignore: ['^@/'], // @ 是设置的路径别名
},
],
'import/extensions': [
'error',
{
ignorePackages: true,
pattern: {
js: 'always',
jsx: 'never',
ts: 'never',
tsx: 'never',
scss: 'never',
vue: 'always',
png: 'always',
jpg: 'always',
svg: 'always',
},
},
],
'react/jsx-filename-extension': [1, {extensions: ['.js', '.tsx']}],
'react/display-name': 0,
'react/jsx-props-no-spreading': 0,
'@typescript-eslint/no-unused-vars': 0,
'@typescript-eslint/no-var-requires': 'off',
'react/function-component-definition': 0,
'react/prefer-stateless-function': [0],
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/triple-slash-reference': 'off',
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'import/no-extraneous-dependencies': [
'error',
{devDependencies: ['vite.config.ts']},
],
},
settings: {
react: {
version: 'detect',
},
'import/resolver': {
node: {
extensions: [
'.js',
'.ts',
'.jsx',
'.tsx',
'.json',
'.scss',
'.less',
],
},
},
'import/extensions': [
'.js',
'.ts',
'.jsx',
'.tsx',
'.json',
'.scss',
'.less',
],
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx'],
},
},
};

六、根目录>.lintstagedrc.js


const fs = require('fs');
const generateTSConfig = (fileName, type) => {
const tsconfig = JSON.parse(fs.readFileSync('tsconfig.json', 'utf8'));
const include = ['src/**/*.d.ts', ...fileName].filter(
(file) => !file.includes('TabContent'),
);
tsconfig.include = include;
fs.writeFileSync('tsconfig.lint.json', JSON.stringify(tsconfig));
return `${type} --noEmit --project tsconfig.lint.json`;
};

module.exports = {
'src/**/*.{ts,tsx}': ['yarn prettier', 'yarn eslint-fix',(fileName) => generateTSConfig(fileName, 'tsc')],
'src/**/*.{css,less,scss}': ['yarn stylelint-fix'],
};

七、根目录>.prettierignore


dist/
public/
node_modules/

八、根目录>.prettierignore


module.exports = {
arrowParens: 'always',
bracketSpacing: false,
eslintIntegration: true,
insertPragma: false,
jsxBracketSameLine: true,
jsxSingleQuote: false,
printWidth: 80,
requirePragma: false,
semi: true,
singleQuote: true,
trailingComma: 'all',
tabWidth: 4,
useTabs: false,
};

九、根目录>.prettierrc.js


*.js
*.ts
*.png
*.jpg
*.webp
*.ttf
*.woff
dist/
public/
node_modules/

十、根目录>.stylelintignore


*.js
*.ts
*.png
*.jpg
*.webp
*.ttf
*.woff
dist/
public/
node_modules/

十一、根目录>.stylelintrc.js


module.exports = {
root: true,
plugins: ['stylelint-order'],
extends: ['stylelint-config-standard', 'stylelint-config-prettier'],
rules: {
'selector-pseudo-class-no-unknown': [
true,
{
ignorePseudoClasses: ['global'],
},
],
'selector-pseudo-element-no-unknown': [
true,
{
ignorePseudoElements: ['v-deep'],
},
],
'at-rule-no-unknown': [
true,
{
ignoreAtRules: [
'tailwind',
'apply',
'variants',
'responsive',
'screen',
'function',
'if',
'each',
'include',
'mixin',
],
},
],
'no-empty-source': null,
'named-grid-areas-no-invalid': null,
'unicode-bom': 'never',
'no-descending-specificity': null,
'font-family-no-missing-generic-family-keyword': null,
'declaration-colon-space-after': 'always-single-line',
'declaration-colon-space-before': 'never',
// 'declaration-block-trailing-semicolon': 'always',
'rule-empty-line-before': [
'always',
{
ignore: ['after-comment', 'first-nested'],
},
],
'unit-no-unknown': [
true,
{
ignoreUnits: ['rpx'],
},
],
'order/order': [
[
'dollar-variables',
'custom-properties',
'at-rules',
'declarations',
{
type: 'at-rule',
name: 'supports',
},
{
type: 'at-rule',
name: 'media',
},
'rules',
],
{
severity: 'warning',
},
],
// 按照指定顺序排列
'order/properties-order': [
'position',
'content',
'top',
'right',
'bottom',
'left',
'z-index',
'display',
'float',
'width',
'height',
'max-width',
'max-height',
'min-width',
'min-height',
'padding',
'padding-top',
'padding-right',
'padding-bottom',
'padding-left',
'margin',
'margin-top',
'margin-right',
'margin-bottom',
'margin-left',
'margin-collapse',
'margin-top-collapse',
'margin-right-collapse',
'margin-bottom-collapse',
'margin-left-collapse',
'overflow',
'overflow-x',
'overflow-y',
'clip',
'clear',
'font',
'font-family',
'font-size',
'font-smoothing',
'osx-font-smoothing',
'font-style',
'font-weight',
'hyphens',
'src',
'line-height',
'letter-spacing',
'word-spacing',
'color',
'text-align',
'text-decoration',
'text-indent',
'text-overflow',
'text-rendering',
'text-size-adjust',
'text-shadow',
'text-transform',
'word-break',
'word-wrap',
'white-space',
'vertical-align',
'list-style',
'list-style-type',
'list-style-position',
'list-style-image',
'pointer-events',
'cursor',
'background',
'background-attachment',
'background-color',
'background-image',
'background-position',
'background-repeat',
'background-size',
'border',
'border-collapse',
'border-top',
'border-right',
'border-bottom',
'border-left',
'border-color',
'border-image',
'border-top-color',
'border-right-color',
'border-bottom-color',
'border-left-color',
'border-spacing',
'border-style',
'border-top-style',
'border-right-style',
'border-bottom-style',
'border-left-style',
'border-width',
'border-top-width',
'border-right-width',
'border-bottom-width',
'border-left-width',
'border-radius',
'border-top-right-radius',
'border-bottom-right-radius',
'border-bottom-left-radius',
'border-top-left-radius',
'border-radius-topright',
'border-radius-bottomright',
'border-radius-bottomleft',
'border-radius-topleft',
'quotes',
'outline',
'outline-offset',
'opacity',
'filter',
'visibility',
'size',
'zoom',
'transform',
'box-align',
'box-flex',
'box-orient',
'box-pack',
'box-shadow',
'box-sizing',
'table-layout',
'animation',
'animation-delay',
'animation-duration',
'animation-iteration-count',
'animation-name',
'animation-play-state',
'animation-timing-function',
'animation-fill-mode',
'transition',
'transition-delay',
'transition-duration',
'transition-property',
'transition-timing-function',
'background-clip',
'backface-visibility',
'resize',
'appearance',
'user-select',
'interpolation-mode',
'direction',
'marks',
'page',
'set-link-source',
'unicode-bidi',
'speak',
],
},
ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts'],
};

十二、根目录>commitlint.config.js


module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
// type枚举
2,
'always',
[
'build', // 编译相关的修改,例如发布版本、对项目构建或者依赖的改动
'feat', // 新功能
'fix', // 修补bug
'docs', // 文档修改
'style', // 代码格式修改, 注意不是 css 修改
'refactor', // 重构
'perf', // 优化相关,比如提升性能、体验
'test', // 测试用例修改
'revert', // 代码回滚
'ci', // 持续集成修改
'config', // 配置修改
'chore', // 其他改动
],
],
'type-empty': [2, 'never'], // never: type不能为空; always: type必须为空
'type-case': [0, 'always', 'lower-case'], // type必须小写,upper-case大写,camel-case小驼峰,kebab-case短横线,pascal-case大驼峰,等等
'scope-empty': [0],
'scope-case': [0],
'subject-empty': [2, 'never'], // subject不能为空
'subject-case': [0],
'subject-full-stop': [0, 'never', '.'], // subject以.为结束标记
'header-max-length': [2, 'always', 72], // header最长72
'body-leading-blank': [0], // body换行
'footer-leading-blank': [0, 'always'], // footer以空行开头
},
};