├── .markdownlintignore
├── pnpm-workspace.yaml
├── packages
├── eslint-config
│ ├── .gitignore
│ ├── __tests__
│ │ ├── fixtures
│ │ │ ├── index.js
│ │ │ ├── es5.js
│ │ │ ├── ts-import-a.ts
│ │ │ ├── ts-import-b.ts
│ │ │ ├── tsconfig.json
│ │ │ ├── react-display-name.js
│ │ │ ├── vue.vue
│ │ │ ├── ts-node.ts
│ │ │ ├── ts.ts
│ │ │ ├── node.js
│ │ │ ├── ts-react.tsx
│ │ │ ├── ts-vue.vue
│ │ │ ├── react.jsx
│ │ │ └── use-babel-eslint.jsx
│ │ └── use-babel-eslint.test.js
│ ├── .eslintrc.js
│ ├── jsx-a11y.js
│ ├── rules
│ │ ├── base
│ │ │ ├── strict.js
│ │ │ ├── variables.js
│ │ │ ├── possible-errors.js
│ │ │ ├── es6.js
│ │ │ └── best-practices.js
│ │ ├── es5.js
│ │ ├── node.js
│ │ ├── jsx-a11y.js
│ │ ├── vue.js
│ │ └── imports.js
│ ├── node.js
│ ├── typescript
│ │ ├── node.js
│ │ ├── index.js
│ │ ├── react.js
│ │ └── vue.js
│ ├── essential
│ │ ├── typescript
│ │ │ ├── index.js
│ │ │ ├── react.js
│ │ │ └── vue.js
│ │ ├── vue.js
│ │ ├── index.js
│ │ ├── es5.js
│ │ ├── rules
│ │ │ ├── ts-blacklist.js
│ │ │ ├── set-style-to-warn.js
│ │ │ ├── blacklist.js
│ │ │ └── es6-blacklist.js
│ │ └── react.js
│ ├── .eslintignore
│ ├── react.js
│ ├── es5.js
│ ├── vue.js
│ ├── .editorconfig
│ ├── index.js
│ └── package.json
├── encode-fe-lint
│ ├── src
│ │ ├── lints
│ │ │ ├── prettier
│ │ │ │ ├── index.ts
│ │ │ │ └── doPrettier.ts
│ │ │ ├── eslint
│ │ │ │ ├── index.ts
│ │ │ │ ├── doEslint.ts
│ │ │ │ ├── getESLintConfigType.ts
│ │ │ │ ├── formatESLintResults.ts
│ │ │ │ └── getESLintConfig.ts
│ │ │ ├── index.ts
│ │ │ ├── stylelint
│ │ │ │ ├── index.ts
│ │ │ │ ├── getStylelintDocUrl.ts
│ │ │ │ ├── doStylelint.ts
│ │ │ │ ├── formatStylelintResults.ts
│ │ │ │ └── getStylelintConfig.ts
│ │ │ └── markdownlint
│ │ │ │ ├── index.ts
│ │ │ │ ├── getMarkdownlintConfig.ts
│ │ │ │ ├── formatMarkdownlintResults.ts
│ │ │ │ └── doMarkdownlint.ts
│ │ ├── config
│ │ │ ├── commitlint.config.js.ejs
│ │ │ ├── _markdownlint.json.ejs
│ │ │ ├── _stylelintrc.js.ejs
│ │ │ ├── _eslintignore.ejs
│ │ │ ├── _stylelintignore.ejs
│ │ │ ├── _markdownlintignore.ejs
│ │ │ ├── _vscode
│ │ │ │ ├── extensions.json.ejs
│ │ │ │ └── settings.json.ejs
│ │ │ ├── _prettierrc.js.ejs
│ │ │ ├── _eslintrc.js.ejs
│ │ │ ├── encode-fe-lint.config.js.ejs
│ │ │ └── _editorconfig.ejs
│ │ ├── utils
│ │ │ ├── npm-type.ts
│ │ │ ├── log.ts
│ │ │ ├── git.ts
│ │ │ ├── generate-template.ts
│ │ │ ├── constants.ts
│ │ │ ├── print-report.ts
│ │ │ └── conflict-resolve.ts
│ │ ├── index.ts
│ │ ├── actions
│ │ │ ├── update.ts
│ │ │ ├── scan.ts
│ │ │ └── init.ts
│ │ ├── types.ts
│ │ └── cli.ts
│ ├── .eslintignore
│ ├── .gitignore
│ ├── test
│ │ ├── fixtures
│ │ │ ├── autofix
│ │ │ │ ├── semi-error.js
│ │ │ │ └── semi-expected.js
│ │ │ └── template
│ │ │ │ └── init
│ │ │ │ ├── _vscode
│ │ │ │ └── settings.json
│ │ │ │ └── package.json
│ │ ├── index.test.js
│ │ └── cli.test.js
│ ├── .prettierrc.js
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── package.json
│ └── README.md
├── eslint-plugin
│ ├── configs
│ │ └── recommended.js
│ ├── index.js
│ ├── rules
│ │ ├── no-http-url.js
│ │ ├── no-js-in-ts-project.js
│ │ ├── no-secret-info.js
│ │ └── no-broad-semantic-versioning.js
│ ├── test
│ │ └── rules
│ │ │ ├── no-js-in-ts-project.test.js
│ │ │ ├── no-broad-semantic-versioning.test.js
│ │ │ ├── no-http-url.test.js
│ │ │ └── no-secret-info.test.js
│ ├── package.json
│ └── README.md
├── stylelint-config
│ ├── __tests__
│ │ ├── fixtures
│ │ │ ├── sass-test.scss
│ │ │ ├── less-test.less
│ │ │ ├── index.css
│ │ │ ├── essential.css
│ │ │ └── css-module.scss
│ │ └── rules-validate.test.js
│ ├── README.md
│ ├── package.json
│ └── index.js
├── markdownlint-config
│ ├── README.md
│ ├── package.json
│ └── index.json
└── commitlint-config
│ ├── package.json
│ ├── index.js
│ └── README.md
├── .stylelintrc
├── .markdownlint.json
├── commitlint.config.js
├── .husky
└── commit-msg
├── docs
├── .vuepress
│ ├── public
│ │ └── img
│ │ │ └── logo.png
│ └── config.ts
├── npm
│ ├── stylelint.md
│ ├── markdownlint.md
│ ├── commitlint.md
│ └── eslint-plugin.md
├── index.md
├── engineering
│ ├── changelog.md
│ ├── doc.md
│ └── git.md
└── coding
│ └── node.md
├── .vscode
└── settings.json
├── .prettierrc.js
├── .gitignore
├── lerna.json
├── deploy.sh
├── CHANGELOG.md
├── .github
└── workflows
│ └── deploy.yml
├── LICENSE
├── package.json
└── README.md
/.markdownlintignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - 'packages/*'
3 |
--------------------------------------------------------------------------------
/packages/eslint-config/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | print-config.json
3 |
--------------------------------------------------------------------------------
/.stylelintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./packages/stylelint-config/index.js"
3 | }
--------------------------------------------------------------------------------
/.markdownlint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./packages/markdownlint-config/index.json"
3 | }
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/lints/prettier/index.ts:
--------------------------------------------------------------------------------
1 | export * from './doPrettier';
2 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | lib/
3 | test/
4 | src/config/
5 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['./packages/commitlint-config/index.js'],
3 | };
4 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | lib
3 | .DS_Store
4 | .idea
5 | .vscode
6 | coverage
7 |
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npx commitlint --edit $1
5 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/config/commitlint.config.js.ejs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['encode'],
3 | };
4 |
--------------------------------------------------------------------------------
/packages/eslint-config/__tests__/fixtures/index.js:
--------------------------------------------------------------------------------
1 | let foo = [1,2]
2 | console.log(foo);
3 | eval('alert(1)');
4 |
--------------------------------------------------------------------------------
/packages/eslint-config/__tests__/fixtures/es5.js:
--------------------------------------------------------------------------------
1 | var hero = {
2 | firstName: 'Kevin',
3 | lastName: 'Flynn',
4 | };
5 |
--------------------------------------------------------------------------------
/docs/.vuepress/public/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/encode-studio-fe/fe-spec/HEAD/docs/.vuepress/public/img/logo.png
--------------------------------------------------------------------------------
/packages/eslint-config/__tests__/fixtures/ts-import-a.ts:
--------------------------------------------------------------------------------
1 | import { b } from '@/ts-import-b';
2 |
3 | export const a = b;
4 |
--------------------------------------------------------------------------------
/packages/eslint-config/__tests__/fixtures/ts-import-b.ts:
--------------------------------------------------------------------------------
1 | import { a } from '@/ts-import-c';
2 |
3 | export const b = a;
4 |
--------------------------------------------------------------------------------
/packages/eslint-config/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | './index',
4 | ].map(require.resolve),
5 | };
6 |
--------------------------------------------------------------------------------
/packages/eslint-config/jsx-a11y.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | './rules/jsx-a11y',
4 | ].map(require.resolve),
5 | };
6 |
--------------------------------------------------------------------------------
/packages/eslint-config/rules/base/strict.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | rules: {
3 | // 不限制严格模式的使用
4 | strict: 'off',
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "cSpell.words": ["chenghuai", "commitlint", "stylelint", "vuepress"],
3 | "git.ignoreLimitWarning": true
4 | }
5 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/config/_markdownlint.json.ejs:
--------------------------------------------------------------------------------
1 | <%_ if (enableMarkdownlint) { _%> { "extends": "markdownlint-config-encode" } <%_ } _%>
2 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/test/fixtures/autofix/semi-error.js:
--------------------------------------------------------------------------------
1 | const hello = (name) => {
2 | return `hello, ${name}`;
3 | };
4 |
5 | hello('world');
6 |
--------------------------------------------------------------------------------
/packages/eslint-config/node.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | './index',
4 | './rules/node',
5 | ].map(require.resolve),
6 | };
7 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/test/fixtures/autofix/semi-expected.js:
--------------------------------------------------------------------------------
1 | const hello = (name) => {
2 | return `hello, ${name}`;
3 | };
4 |
5 | hello('world');
6 |
--------------------------------------------------------------------------------
/packages/eslint-config/__tests__/fixtures/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "@/*": ["./*"]
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/lints/eslint/index.ts:
--------------------------------------------------------------------------------
1 | export * from './getESLintConfig';
2 | export * from './formatESLintResults';
3 | export * from './doESLint';
4 |
--------------------------------------------------------------------------------
/packages/eslint-config/typescript/node.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | './index',
4 | '../rules/node',
5 | ].map(require.resolve),
6 | };
7 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/lints/index.ts:
--------------------------------------------------------------------------------
1 | export * from './eslint';
2 | export * from './markdownlint';
3 | export * from './stylelint';
4 | export * from './prettier';
5 |
--------------------------------------------------------------------------------
/packages/eslint-config/typescript/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | '../index',
4 | '../rules/typescript',
5 | ].map(require.resolve),
6 | };
7 |
--------------------------------------------------------------------------------
/packages/eslint-config/typescript/react.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | '../react',
4 | '../rules/typescript',
5 | ].map(require.resolve),
6 | };
7 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/config/_stylelintrc.js.ejs:
--------------------------------------------------------------------------------
1 | <%_ if (enableStylelint) { _%>
2 | module.exports = {
3 | extends: 'stylelint-config-encode',
4 | };
5 | <%_ } _%>
6 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/lints/stylelint/index.ts:
--------------------------------------------------------------------------------
1 | export * from './getStylelintConfig';
2 | export * from './formatStylelintResults';
3 | export * from './doStylelint';
4 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/config/_eslintignore.ejs:
--------------------------------------------------------------------------------
1 | build/
2 | coverage/
3 | dist/
4 | es/
5 | lib/
6 | node_modules/
7 | **/*.min.js
8 | **/*-min.js
9 | **/*.bundle.js
10 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/config/_stylelintignore.ejs:
--------------------------------------------------------------------------------
1 | <%_ if (enableStylelint) { _%>
2 | <%_ stylelintIgnores.forEach((txt) => { _%>
3 | <%= txt %>
4 | <% }) %>
5 | <%_ } _%>
6 |
--------------------------------------------------------------------------------
/packages/eslint-config/__tests__/fixtures/react-display-name.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default () => {
4 | return (
5 |
hello, world
6 | )
7 | }
8 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | printWidth: 100,
3 | tabWidth: 2,
4 | semi: true,
5 | singleQuote: true,
6 | trailingComma: 'all',
7 | arrowParens: 'always',
8 | };
9 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/lints/markdownlint/index.ts:
--------------------------------------------------------------------------------
1 | export * from './getMarkdownlintConfig';
2 | export * from './formatMarkdownlintResults';
3 | export * from './doMarkdownlint';
4 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/config/_markdownlintignore.ejs:
--------------------------------------------------------------------------------
1 | <%_ if (enableMarkdownlint) { _%>
2 | <%_ markdownLintIgnores.forEach((txt) => { _%>
3 | <%= txt %>
4 | <% }) %>
5 | <%_ } _%>
6 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/test/fixtures/template/init/_vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "test": true,
3 | "editor.defaultFormatter": "233",
4 | "eslint.validate": [
5 | "233"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | *.swp
3 | *.log
4 |
5 | .DS_Store
6 | .idea/
7 | .temp/
8 |
9 | dist/
10 | build/
11 | lib/
12 | es/
13 | coverage/
14 | release/
15 |
16 | node_modules/
17 |
18 | .cache/
19 | out/
--------------------------------------------------------------------------------
/packages/encode-fe-lint/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | printWidth: 100,
3 | tabWidth: 2,
4 | semi: true,
5 | singleQuote: true,
6 | trailingComma: 'all',
7 | arrowParens: 'always',
8 | };
9 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/config/_vscode/extensions.json.ejs:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "dbaeumer.vscode-eslint",
4 | "stylelint.vscode-stylelint",
5 | "esbenp.prettier-vscode"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/packages/eslint-config/essential/typescript/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | '../index',
4 | '../../rules/typescript',
5 | '../rules/ts-blacklist',
6 | ].map(require.resolve),
7 | };
8 |
--------------------------------------------------------------------------------
/packages/eslint-config/essential/typescript/react.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | '../react',
4 | '../../rules/typescript',
5 | '../rules/ts-blacklist',
6 | ].map(require.resolve),
7 | };
8 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/config/_prettierrc.js.ejs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | printWidth: 100,
3 | tabWidth: 2,
4 | semi: true,
5 | singleQuote: true,
6 | trailingComma: 'all',
7 | arrowParens: 'always',
8 | };
9 |
--------------------------------------------------------------------------------
/packages/eslint-config/essential/vue.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | '../vue',
4 | './rules/set-style-to-warn',
5 | './rules/blacklist',
6 | './rules/es6-blacklist',
7 | ].map(require.resolve),
8 | };
9 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['eslint-config-encode/typescript/node', 'prettier'],
3 | rules: {
4 | '@typescript-eslint/no-require-imports': 0,
5 | 'no-console': 0,
6 | },
7 | };
8 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/config/_eslintrc.js.ejs:
--------------------------------------------------------------------------------
1 | module.exports = { extends: [ '<%= eslintType === 'index' ? 'eslint-config-encode' :
2 | `eslint-config-encode/${eslintType}` %>', <%_ if (enablePrettier) { _%> 'prettier', <%_ } _%> ],
3 | };
4 |
--------------------------------------------------------------------------------
/packages/eslint-plugin/configs/recommended.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: ['eslint-plugin-encode'],
3 | rules: {
4 | 'eslint-plugin-encode/no-http-url': 'warn',
5 | 'eslint-plugin-encode/no-secret-info': 'error',
6 | },
7 | };
8 |
--------------------------------------------------------------------------------
/packages/eslint-config/rules/es5.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 本文件用于覆盖掉个别 ES5 与 ES6 不同的规则
3 | */
4 |
5 | module.exports = {
6 | rules: {
7 | // 逗号风格 - ES5 中不加最后一个逗号
8 | // @unessential
9 | 'comma-dangle': ['error', 'never'],
10 | },
11 | };
12 |
--------------------------------------------------------------------------------
/packages/eslint-config/.eslintignore:
--------------------------------------------------------------------------------
1 | # 忽略目录
2 | node_modules/
3 | build/
4 | dist/
5 | zip/
6 | demo/
7 | coverage/
8 | vendor/
9 | lib/
10 | sea-modules/
11 | APP-META/
12 | test/fixtures/
13 |
14 | # 忽略文件
15 | **/*.min.js
16 | **/*-min.js
17 | **/*.bundle.js
18 |
--------------------------------------------------------------------------------
/packages/eslint-config/react.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | './index',
4 | './rules/react',
5 | ].map(require.resolve),
6 | parserOptions: {
7 | babelOptions: {
8 | presets: ['@babel/preset-react'],
9 | },
10 | },
11 | };
12 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/config/encode-fe-lint.config.js.ejs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | enableESLint: <%= enableESLint %>,
3 | enableStylelint: <%= enableStylelint %>,
4 | enableMarkdownlint: <%= enableMarkdownlint %>,
5 | enablePrettier: <%= enablePrettier %>,
6 | };
7 |
--------------------------------------------------------------------------------
/packages/eslint-config/essential/typescript/vue.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | './index',
4 | '../../rules/vue', // vue 要置于最后,因为里面用了 vue-parser
5 | ].map(require.resolve),
6 | parserOptions: {
7 | parser: '@typescript-eslint/parser',
8 | },
9 | };
10 |
--------------------------------------------------------------------------------
/packages/eslint-config/es5.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | './rules/base/best-practices',
4 | './rules/base/possible-errors',
5 | './rules/base/style',
6 | './rules/base/variables',
7 | './rules/es5',
8 | ].map(require.resolve),
9 | root: true,
10 | };
11 |
--------------------------------------------------------------------------------
/packages/stylelint-config/__tests__/fixtures/sass-test.scss:
--------------------------------------------------------------------------------
1 | @mixin colorTag ($color1, $color2) {
2 | color: $color1;
3 | border-color: $color2;
4 | }
5 |
6 | //css注释
7 | .a {
8 | width: $default-width /2;
9 |
10 | @include colorTag(#abc,#bcd);
11 | @unkown colorTag(#abc);
12 | }
13 |
--------------------------------------------------------------------------------
/packages/eslint-config/vue.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | './index',
4 | './rules/vue',
5 | ].map(require.resolve),
6 | parserOptions: {
7 | // https://github.com/mysticatea/vue-eslint-parser#parseroptionsparser
8 | parser: '@babel/eslint-parser',
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/packages/eslint-config/essential/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * essential 级别出口文件仅将会必要的规则设置为 error 级别
3 | */
4 |
5 | module.exports = {
6 | extends: [
7 | '../index',
8 | './rules/set-style-to-warn',
9 | './rules/blacklist',
10 | './rules/es6-blacklist',
11 | ].map(require.resolve),
12 | };
13 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/config/_editorconfig.ejs:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 | quote_type = single
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/packages/eslint-config/typescript/vue.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | './index',
4 | '../rules/vue',
5 | ].map(require.resolve),
6 | parserOptions: {
7 | // https://github.com/mysticatea/vue-eslint-parser#parseroptionsparser
8 | parser: '@typescript-eslint/parser',
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/packages/eslint-config/essential/es5.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | '../es5',
4 | './rules/set-style-to-warn',
5 | './rules/blacklist',
6 | ].map(require.resolve),
7 | rules: {
8 | // 逗号风格 - ES5 中不加最后一个逗号
9 | // @unessential
10 | 'comma-dangle': ['warn', 'never'],
11 | },
12 | };
13 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/utils/npm-type.ts:
--------------------------------------------------------------------------------
1 | import { sync as commandExistsSync } from 'command-exists';
2 |
3 | /**
4 | * npm 类型
5 | */
6 | const promise: Promise<'npm' | 'pnpm'> = new Promise((resolve) => {
7 | if (!commandExistsSync('pnpm')) return resolve('npm');
8 |
9 | resolve('pnpm');
10 | });
11 |
12 | export default promise;
13 |
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.9",
3 | "npmClient": "pnpm",
4 | "useWorkspaces": true,
5 | "command": {
6 | "publish": {
7 | "npmClient": "npm",
8 | "message": "chore(release): publish %s",
9 | "registry": "https://registry.npmjs.org"
10 | },
11 | "packages": [
12 | "packages/*"
13 | ]
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/test/fixtures/template/init/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "encode-fe-lint-init-test",
3 | "version": "1.0.0",
4 | "description": "encode-fe-lint init test",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC"
11 | }
12 |
--------------------------------------------------------------------------------
/packages/eslint-config/__tests__/fixtures/vue.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
17 |
--------------------------------------------------------------------------------
/packages/eslint-config/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
15 | [makefile]
16 | indent_style = tab
17 | indent_size = 4
18 |
--------------------------------------------------------------------------------
/packages/eslint-config/rules/node.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 本文件继承了 egg-config-egg 的 node 规则,规则由 eslint-plugin-node 提供
3 | * @link https://github.com/eggjs/eslint-config-egg/blob/master/lib/rules/node.js
4 | * @link https://github.com/mysticatea/eslint-plugin-node
5 | */
6 |
7 | module.exports = {
8 | extends: [
9 | 'eslint-config-egg/lib/rules/node',
10 | ],
11 | };
12 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "./lib",
4 | "target": "ES5",
5 | "module": "commonjs",
6 | "esModuleInterop": true,
7 | "removeComments": true,
8 | "resolveJsonModule": true,
9 | "declaration": true,
10 | "downlevelIteration": true,
11 | "types": ["node"]
12 | },
13 | "include": ["src"]
14 | }
15 |
--------------------------------------------------------------------------------
/packages/eslint-config/essential/rules/ts-blacklist.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | rules: {
3 | // 使用 2 个空格缩进
4 | // @unessential
5 | '@typescript-eslint/indent': 'off',
6 |
7 | // 使用分号
8 | // @unessential
9 | '@typescript-eslint/semi': 'off',
10 |
11 | '@typescript-eslint/adjacent-overload-signatures': 'warn',
12 |
13 | '@typescript-eslint/no-parameter-properties': 'warn',
14 | },
15 | };
16 |
--------------------------------------------------------------------------------
/packages/eslint-config/__tests__/fixtures/ts-node.ts:
--------------------------------------------------------------------------------
1 | // node/prefer-promises/fs
2 | import fs from 'fs';
3 |
4 | fs.readFile('../node.js', 'utf-8', (err, data) => {
5 | // no-console
6 | console.log(err, data);
7 | });
8 |
9 | // no-var
10 | // @typescript-eslint/no-unused-vars
11 | var count = 1 + 1;
12 |
13 | class Test {
14 | func(): void {
15 | return 1;
16 | }
17 | }
18 |
19 | export default Test;
20 | // eol-last
--------------------------------------------------------------------------------
/packages/eslint-config/__tests__/fixtures/ts.ts:
--------------------------------------------------------------------------------
1 | interface AppDeveloper {
2 | nickname: string,
3 | name: string,
4 | avatarUrl: string,
5 | id: number,
6 | }
7 |
8 | const foo: string = 1
9 | let bar = [1,2]
10 | const baz = {obj:2}
11 |
12 | function func(arg: string): string;
13 | function func(arg: number): number;
14 | function func(arg: string | number): string | number {
15 | if (typeof arg === 'string') return arg;
16 | return arg;
17 | }
18 |
--------------------------------------------------------------------------------
/deploy.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | # 确保脚本抛出遇到的错误
4 | set -e
5 |
6 |
7 | push_addr=`git remote get-url --push origin`
8 | commit_info=`git describe --all --always --long`
9 | dist_path=docs/.vuepress/dist
10 | push_branch=gh-pages
11 |
12 | # 生成静态文件
13 | npm run docs:build
14 |
15 | # 进入生成的文件夹
16 | cd $dist_path
17 |
18 | git init
19 | git add -A
20 | git commit -m "deploy, $commit_info"
21 | git push -f $push_addr HEAD:$push_branch
22 |
23 | cd -
24 | rm -rf $dist_path
25 |
--------------------------------------------------------------------------------
/packages/eslint-plugin/index.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const requireAll = require('require-all');
3 |
4 | exports.rules = requireAll({
5 | dirname: path.resolve(__dirname, 'rules'),
6 | });
7 |
8 | exports.configs = requireAll({
9 | dirname: path.resolve(__dirname, 'configs'),
10 | });
11 |
12 | exports.processors = {
13 | '.json': {
14 | preprocess(text) {
15 | // As JS file
16 | return [`module.exports = ${text}`];
17 | },
18 | },
19 | };
20 |
--------------------------------------------------------------------------------
/packages/stylelint-config/__tests__/fixtures/less-test.less:
--------------------------------------------------------------------------------
1 | @nice-bg: #abc;
2 |
3 | .normal {
4 | background: @nice-bg;
5 |
6 | .nest {
7 | font-size: 60px
8 | }
9 | }
10 |
11 | .my-hover-mixin() {
12 | &:hover {
13 | border: 1px solid red;
14 | }
15 | }
16 | .btn {
17 | .my-hover-mixin();
18 | width: $default-width /2;
19 | :global(.ant-table) {
20 | font-size: 10px;
21 | }
22 | }
23 | .extProp:extend(.btn) {
24 | background-color:yellowgreen;
25 | }
26 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # [](https://github.com/encode-studio-fe/fe-spec/compare/v0.0.6...v) (2023-06-11)
2 |
3 |
4 | ### Features
5 |
6 | * 更新 README.md ([73edb72](https://github.com/encode-studio-fe/fe-spec/commit/73edb7229db8e918d2045817a3e9a7e34feb3d3d))
7 | * 更新 README.md ([9734505](https://github.com/encode-studio-fe/fe-spec/commit/9734505e1d9832d039c6850f6b58e1c007b23aa0))
8 | * 添加脚手架 ([ae7e862](https://github.com/encode-studio-fe/fe-spec/commit/ae7e8628243033ae999dbcca085b8df9acdb93c2))
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/lints/stylelint/getStylelintDocUrl.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 获取 Stylelint 规则文档地址
3 | */
4 | export function getStylelintRuleDocUrl(rule: string): string {
5 | // stylelint-scss
6 | const match = rule.match(/^@scss\/(\S+)$/);
7 | if (match) {
8 | return `https://github.com/kristerkari/stylelint-scss/tree/master/src/rules/${match[1]}`;
9 | }
10 |
11 | if (rule !== 'CssSyntaxError') return `https://stylelint.io/user-guide/rules/list/${rule}`;
12 |
13 | return '';
14 | }
15 |
--------------------------------------------------------------------------------
/packages/markdownlint-config/README.md:
--------------------------------------------------------------------------------
1 | # markdownlint-config-encode
2 |
3 | > 印客学院 文档 规范
4 |
5 | 支持配套的 [markdownlint 可共享配置](https://www.npmjs.com/package/markdownlint#optionsconfig)。
6 |
7 | ## 安装
8 |
9 | 需要先行安装 [markdownlint](https://www.npmjs.com/package/markdownlint):
10 |
11 | ```bash
12 | npm install markdownlint-config-encode markdownlint --save-dev
13 | ```
14 |
15 | ## 使用
16 |
17 | 在 `.markdownlint.json` 中继承本包:
18 |
19 | ```json
20 | {
21 | "extends": "markdownlint-config-encode"
22 | }
23 | ```
24 |
--------------------------------------------------------------------------------
/packages/stylelint-config/README.md:
--------------------------------------------------------------------------------
1 | # markdownlint-config-encode
2 |
3 | 支持配套的 [stylelint 可共享配置](https://stylelint.io/user-guide/configure)。
4 |
5 | ## 安装
6 |
7 | 需要先行安装 [stylelint](https://www.npmjs.com/package/stylelint) 和 [stylelint-scss](https://www.npmjs.com/package/stylelint-scss):
8 |
9 | ```bash
10 | npm install stylelint-config-encode stylelint stylelint-scss --save-dev
11 | ```
12 |
13 | ## 使用
14 |
15 | 在 `.stylelintrc` 中继承本包:
16 |
17 | ```json
18 | {
19 | "extends": "stylelint-config-encode"
20 | }
21 | ```
22 |
--------------------------------------------------------------------------------
/packages/stylelint-config/__tests__/fixtures/index.css:
--------------------------------------------------------------------------------
1 | .foo {
2 | color: #abc;
3 | }
4 |
5 | .selector{padding-left: 15px;}
6 |
7 | .selector1 {
8 | padding-left :15px;
9 |
10 | background-size: 0,0;
11 | }
12 |
13 | .selector2 {padding-left: 15px;
14 | }
15 | .selector3 {
16 | padding-left: 15px;}
17 |
18 | .selector4 { color: pink; top: 3px; }
19 |
20 | .selector5 {
21 | width: 0rem;
22 |
23 | color: #AAA;
24 | background: #aaaaaa;
25 | }
26 |
27 | #foo {
28 | color: #ccc;
29 | }
30 |
31 | /*comment */
32 |
33 | @unkown colorTag(#abc);
--------------------------------------------------------------------------------
/packages/eslint-config/__tests__/fixtures/node.js:
--------------------------------------------------------------------------------
1 | // 'no-var'
2 | var fs = require('fs');
3 |
4 | // 'node/prefer-global/buffer'
5 | // 'quotes'
6 | const { Buffer } = require("buffer");
7 | // 'node/no-new-require'
8 | // 'new-cap'
9 | const util = new require('util');
10 |
11 | // 'no-console'
12 | console.log(util);
13 | // 'semi'
14 | // 'no-unused-vars'
15 | const b = Buffer.alloc(16)
16 |
17 | // 'node/prefer-promises/fs'
18 | fs.readFile('../node.ts', 'utf-8', (err, data) => {
19 | // 'no-console'
20 | console.log(err, data);
21 | });
22 |
23 | exports = fs;
24 | // 'eol-last'
--------------------------------------------------------------------------------
/packages/eslint-config/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | './rules/base/best-practices',
4 | './rules/base/possible-errors',
5 | './rules/base/style',
6 | './rules/base/variables',
7 | './rules/base/es6',
8 | './rules/base/strict',
9 | './rules/imports',
10 | ].map(require.resolve),
11 | parser: '@babel/eslint-parser',
12 | parserOptions: {
13 | requireConfigFile: false,
14 | ecmaVersion: 2020,
15 | sourceType: 'module',
16 | ecmaFeatures: {
17 | globalReturn: false,
18 | impliedStrict: true,
19 | jsx: true,
20 | },
21 | },
22 | root: true,
23 | };
24 |
--------------------------------------------------------------------------------
/packages/eslint-config/__tests__/fixtures/ts-react.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export interface Props {
4 | name: string;
5 | enthusiasmLevel?: number;
6 | }
7 |
8 | function Hello({ name, enthusiasmLevel = 1 }: Props) {
9 | if (enthusiasmLevel <= 0) {
10 | throw new Error('You could be a little more enthusiastic. :D');
11 | }
12 |
13 | const foo: number = 1
14 |
15 | return (
16 |
17 |
18 | Hello {name + enthusiasmLevel}
19 |
20 |
21 | haha
22 |
23 |
24 | );
25 | }
26 |
27 | export default Hello;
28 |
--------------------------------------------------------------------------------
/packages/markdownlint-config/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "markdownlint-config-encode",
3 | "version": "1.0.8",
4 | "main": "index.json",
5 | "description": "印客学院 文档规范",
6 | "keywords": [
7 | "encode",
8 | "markdown",
9 | "lint"
10 | ],
11 | "author": "chenghuai",
12 | "homepage": "https://github.com/encode-studio-fe/fe-spec#readme",
13 | "license": "ISC",
14 | "repository": {
15 | "type": "git",
16 | "url": "git+https://github.com/encode-studio-fe/fe-spec.git"
17 | },
18 | "bugs": {
19 | "url": "https://github.com/encode-studio-fe/fe-spec/issues"
20 | },
21 | "peerDependencies": {
22 | "markdownlint": "^0.28.1"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/utils/log.ts:
--------------------------------------------------------------------------------
1 | import chalk from 'chalk';
2 | import { PKG_NAME, UNICODE } from './constants';
3 |
4 | const { green, blue, yellow, red } = chalk;
5 |
6 | export default {
7 | success(text: string) {
8 | console.log(green(text));
9 | },
10 | info(text: string) {
11 | console.info(blue(text));
12 | },
13 | warn(text: string) {
14 | console.info(yellow(text));
15 | },
16 | error(text: string) {
17 | console.error(red(text));
18 | },
19 | result(text: string, pass: boolean) {
20 | console.info(
21 | blue(`[${PKG_NAME}] ${text}`),
22 | pass ? green(UNICODE.success) : red(UNICODE.failure),
23 | );
24 | },
25 | };
26 |
--------------------------------------------------------------------------------
/packages/commitlint-config/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "commitlint-config-encode",
3 | "version": "1.0.8",
4 | "main": "index.js",
5 | "description": "印客学院 Git规范",
6 | "keywords": [
7 | "encode",
8 | "commit",
9 | "lint"
10 | ],
11 | "author": "chenghuai",
12 | "homepage": "https://github.com/encode-studio-fe/fe-spec#readme",
13 | "license": "ISC",
14 | "repository": {
15 | "type": "git",
16 | "url": "git+https://github.com/encode-studio-fe/fe-spec.git"
17 | },
18 | "bugs": {
19 | "url": "https://github.com/encode-studio-fe/fe-spec/issues"
20 | },
21 | "dependencies": {
22 | "conventional-changelog-conventionalcommits": "^4.5.0"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/packages/eslint-plugin/rules/no-http-url.js:
--------------------------------------------------------------------------------
1 | const RULE_NAME = 'no-http-url';
2 |
3 | module.exports = {
4 | name: RULE_NAME,
5 | meta: {
6 | type: 'suggestion',
7 | fixable: null,
8 | messages: {
9 | noHttpUrl: 'Recommended "{{url}}" switch to HTTPS',
10 | },
11 | },
12 | create(context) {
13 | return {
14 | Literal: function handleRequires(node) {
15 | if (node.value && typeof node.value === 'string' && node.value.indexOf('http:') === 0) {
16 | context.report({
17 | node,
18 | messageId: 'noHttpUrl',
19 | data: {
20 | url: node.value,
21 | },
22 | });
23 | }
24 | },
25 | };
26 | },
27 | };
28 |
--------------------------------------------------------------------------------
/docs/npm/stylelint.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: stylelint-config-encode
3 | categories:
4 | - 工程规范
5 | tags:
6 | - 工程规范
7 | author:
8 | name: 澄怀
9 | link: https://github.com/encode-studio-fe/fe-spec
10 | ---
11 |
12 | # stylelint-config-encode
13 |
14 | :::tip
15 | 印客学院 CSS 规范
16 | :::
17 |
18 | 支持配套的 [stylelint 可共享配置](https://stylelint.io/user-guide/configure)。
19 |
20 | ## 安装
21 |
22 | 需要先行安装 [stylelint](https://www.npmjs.com/package/stylelint) 和 [stylelint-scss](https://www.npmjs.com/package/stylelint-scss):
23 |
24 | ```bash
25 | npm install stylelint-config-encode stylelint stylelint-scss --save-dev
26 | ```
27 |
28 | ## 使用
29 |
30 | 在 `.stylelintrc` 中继承本包:
31 |
32 | ```json
33 | {
34 | "extends": "stylelint-config-encode"
35 | }
36 | ```
37 |
--------------------------------------------------------------------------------
/packages/commitlint-config/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parserPreset: 'conventional-changelog-conventionalcommits',
3 | rules: {
4 | 'body-leading-blank': [1, 'always'],
5 | 'body-max-line-length': [2, 'always', 100],
6 | 'footer-leading-blank': [1, 'always'],
7 | 'footer-max-line-length': [2, 'always', 100],
8 | 'header-max-length': [2, 'always', 100],
9 | 'scope-case': [2, 'always', 'lower-case'],
10 | 'subject-case': [0],
11 | 'subject-empty': [2, 'never'],
12 | 'subject-full-stop': [2, 'never', '.'],
13 | 'type-case': [2, 'always', 'lower-case'],
14 | 'type-empty': [2, 'never'],
15 | 'type-enum': [2, 'always', ['feat', 'fix', 'docs', 'style', 'test', 'refactor', 'chore', 'revert']],
16 | },
17 | };
18 |
--------------------------------------------------------------------------------
/packages/eslint-plugin/test/rules/no-js-in-ts-project.test.js:
--------------------------------------------------------------------------------
1 | const rule = require('../../rules/no-js-in-ts-project');
2 | const { RuleTester } = require('eslint');
3 |
4 | const ruleTester = new RuleTester();
5 |
6 | ruleTester.run('no-js-in-ts-project', rule, {
7 | valid: [
8 | {
9 | filename: 'index.ts',
10 | code: '',
11 | },
12 | {
13 | filename: '.stylelintrc.js',
14 | code: '',
15 | },
16 | {
17 | filename: 'home.ts',
18 | code: '',
19 | },
20 | ],
21 |
22 | invalid: [
23 | {
24 | filename: 'home.js',
25 | code: '',
26 | errors: [
27 | {
28 | message: 'The "home.js" is not recommended in TS project',
29 | },
30 | ],
31 | },
32 | ],
33 | });
34 |
--------------------------------------------------------------------------------
/docs/npm/markdownlint.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: markdownlint-config-encode
3 | categories:
4 | - 工程规范
5 | tags:
6 | - 工程规范
7 | author:
8 | name: 澄怀
9 | link: https://github.com/encode-studio-fe/fe-spec
10 | ---
11 |
12 | # markdownlint-config-encode
13 |
14 | :::tip
15 | 印客学院 文档 规范
16 | :::
17 |
18 | 支持配套的 [markdownlint 可共享配置](https://www.npmjs.com/package/markdownlint#optionsconfig)。
19 |
20 | ## 安装
21 | 需要先全局安装 [markdownlint-cli](https://www.npmjs.com/package/markdownlint-cli)
22 | ```bash
23 | npm install -g markdownlint-cli
24 | ```
25 |
26 | 再安装 [markdownlint](https://www.npmjs.com/package/markdownlint):
27 |
28 | ```bash
29 | npm install markdownlint-config-encode markdownlint --save-dev
30 | ```
31 |
32 | ## 使用
33 |
34 | 在 `.markdownlint.json` 中继承本包:
35 |
36 | ```json
37 | {
38 | "extends": "markdownlint-config-encode"
39 | }
40 | ```
41 |
--------------------------------------------------------------------------------
/packages/stylelint-config/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "stylelint-config-encode",
3 | "version": "1.0.9",
4 | "description": "印客学院 CSS规范",
5 | "main": "index.js",
6 | "keywords": [
7 | "encode",
8 | "style",
9 | "lint"
10 | ],
11 | "scripts": {
12 | "test": "jest"
13 | },
14 | "author": "chenghuai",
15 | "homepage": "https://github.com/encode-studio-fe/fe-spec#readme",
16 | "license": "ISC",
17 | "peerDependencies": {
18 | "stylelint": ">=8.3.0",
19 | "stylelint-scss": ">=2.0.0"
20 | },
21 | "devDependencies": {
22 | "jest": "^29.5.0",
23 | "stylelint": "^14.3.0",
24 | "stylelint-scss": "^4.1.0"
25 | },
26 | "repository": {
27 | "type": "git",
28 | "url": "git+https://github.com/encode-studio-fe/fe-spec.git"
29 | },
30 | "bugs": {
31 | "url": "https://github.com/encode-studio-fe/fe-spec/issues"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/packages/eslint-config/rules/jsx-a11y.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 本文件的规则由 eslint-plugin-jsx-a11y 提供
3 | * @link https://github.com/evcohen/eslint-plugin-jsx-a11y
4 | */
5 |
6 | module.exports = {
7 | plugins: ['jsx-a11y'],
8 | rules: {
9 | 'jsx-a11y/alt-text': 'warn',
10 | 'jsx-a11y/img-redundant-alt': 'warn',
11 | 'jsx-a11y/anchor-has-content': 'warn',
12 | 'jsx-a11y/aria-props': 'warn',
13 | 'jsx-a11y/aria-proptypes': 'warn',
14 | 'jsx-a11y/aria-unsupported-elements': 'warn',
15 | 'jsx-a11y/aria-role': ['warn', { ignoreNonDOM: true }], // ignoreNonDom 为 true 时不检查用户自定义元素
16 | 'jsx-a11y/role-has-required-aria-props': 'warn',
17 | 'jsx-a11y/role-supports-aria-props': 'warn',
18 | 'jsx-a11y/iframe-has-title': 'warn',
19 | 'jsx-a11y/no-access-key': 'warn',
20 | 'jsx-a11y/no-distracting-elements': 'warn',
21 | 'jsx-a11y/scope': 'warn',
22 | },
23 | };
24 |
--------------------------------------------------------------------------------
/packages/eslint-config/essential/rules/set-style-to-warn.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 将 error 级别的 style 规则降级为 warn
3 | */
4 |
5 | // 将传入 config 中 error 级别规则都改为 warn 级别
6 | function setErrorRulesToWarn(configPath) {
7 | const config = require(configPath);
8 | const { rules } = config;
9 |
10 | for (const ruleName in rules) {
11 | if (Object.prototype.hasOwnProperty.call(rules, ruleName)) {
12 | const ruleValue = rules[ruleName];
13 | if (Array.isArray(ruleValue)) {
14 | // 'array-bracket-spacing': [ 'error', 'never' ] 这种规则写法
15 | if (ruleValue[0] === 'error') {
16 | ruleValue[0] = 'warn';
17 | }
18 | } else if (ruleValue === 'error') {
19 | // 'new-parens': 'error' 这种规则写法
20 | rules[ruleName] = 'warn';
21 | }
22 | }
23 | }
24 |
25 | return {
26 | rules,
27 | };
28 | }
29 |
30 | module.exports = setErrorRulesToWarn('../../rules/base/style.js');
31 |
--------------------------------------------------------------------------------
/packages/eslint-config/__tests__/fixtures/ts-vue.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
![]()
5 |
6 |
7 |
8 |
![]()
9 |
10 |
11 |
12 |
13 |
14 |
34 |
35 |
37 |
--------------------------------------------------------------------------------
/packages/commitlint-config/README.md:
--------------------------------------------------------------------------------
1 | # `commitlint-config-encode`
2 |
3 | > 印客学院 Git 规范
4 |
5 | 支持配套的 [commitlint 配置](https://commitlint.js.org/#/concepts-shareable-config),用于对 `git commit message` 进行校验。
6 |
7 | ## 安装
8 |
9 | 使用时,需要安装 [@commitlint/cli](https://www.npmjs.com/package/@commitlint/cli):
10 |
11 | ```bash
12 | npm install commitlint-config-encode @commitlint/cli --save-dev
13 | ```
14 |
15 | ## 使用
16 |
17 | 在 `commitlint.config.js` 中集成本包:
18 |
19 | ```javascript
20 | module.exports = {
21 | extends: ['encode'],
22 | };
23 | ```
24 |
25 | ## 设置 git hook
26 |
27 | 可通过 [husky](https://www.npmjs.com/package/husky) 设置在 `git commit` 时触发 `commitlint`。
28 |
29 | 首先安装 husky:
30 |
31 | ```bash
32 | npm install husky --save-dev
33 | ```
34 |
35 | 然后执行添加`commit-msg`:
36 |
37 | ```bash
38 | npx husky add .husky/commit-msg 'npx commitlint --edit $1'
39 | ```
40 |
41 | 更多信息可参考 [commitlint 文档](https://commitlint.js.org/#/guides-local-setup?id=install-husky)。
42 |
--------------------------------------------------------------------------------
/packages/eslint-plugin/test/rules/no-broad-semantic-versioning.test.js:
--------------------------------------------------------------------------------
1 | const rule = require('../../rules/no-broad-semantic-versioning');
2 | const { RuleTester } = require('eslint');
3 |
4 | const ruleTester = new RuleTester();
5 |
6 | ruleTester.run('no-broad-semantic-versioning', rule, {
7 | valid: [
8 | {
9 | filename: 'package.json',
10 | code: `module.exports = ${JSON.stringify({
11 | devDependencies: { 'eslint-plugin-encode': '^0.0.5' },
12 | })}`,
13 | },
14 | {
15 | filename: 'package.js',
16 | code: 'var t = 1',
17 | },
18 | ],
19 |
20 | invalid: [
21 | {
22 | filename: 'package.json',
23 | code: `module.exports = ${JSON.stringify({
24 | devDependencies: { 'eslint-plugin-encode': '*' },
25 | })}`,
26 | errors: [
27 | {
28 | message: 'The "eslint-plugin-encode" is not recommended to use "*"',
29 | },
30 | ],
31 | },
32 | ],
33 | });
34 |
--------------------------------------------------------------------------------
/packages/eslint-plugin/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "eslint-plugin-encode",
3 | "version": "1.0.8",
4 | "description": "印客教育自定义eslint插件",
5 | "scripts": {
6 | "test": "jest",
7 | "prepublishOnly": "npm run test"
8 | },
9 | "files": [
10 | "configs/",
11 | "rules/",
12 | "README.md"
13 | ],
14 | "keywords": [
15 | "encode",
16 | "eslint",
17 | "eslint-plugin"
18 | ],
19 | "main": "src/index.js",
20 | "author": "chenghuai",
21 | "homepage": "https://github.com/encode-studio-fe/fe-spec#readme",
22 | "license": "ISC",
23 | "repository": {
24 | "type": "git",
25 | "url": "git+https://github.com/encode-studio-fe/fe-spec.git"
26 | },
27 | "bugs": {
28 | "url": "https://github.com/encode-studio-fe/fe-spec/issues"
29 | },
30 | "dependencies": {
31 | "fs-extra": "^9.0.1",
32 | "require-all": "^3.0.0"
33 | },
34 | "devDependencies": {
35 | "eslint": "^8.7.0",
36 | "jest": "^26.6.3"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/stylelint-config/__tests__/fixtures/essential.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 | @unknown {}
3 |
4 | .a {
5 | color: #fff1az;
6 |
7 | /**/
8 |
9 | font-weight: 400;
10 | color: #fff;
11 |
12 | padding-left: 10px;
13 | padding: 20px;
14 |
15 | font-family: 'Times', Times, serif;
16 |
17 | font-family: Helvetica, Arial, Verdana, Tahoma;
18 |
19 | top: calc(1px +2px);
20 |
21 | background: linear-gradient(bottom, #fff, #000);
22 | }
23 |
24 | @keyframes important1 {
25 | from {
26 | margin-top: 50px;
27 | }
28 | to {
29 | margin-top: 100px !important;
30 | }
31 | }
32 |
33 | @media screen and (unknown) {
34 | .a {
35 | color: #fff;
36 | }
37 | }
38 |
39 | @import 'a.css';
40 | @import 'a.css';
41 |
42 | .a {
43 | background: #000;;
44 | colr: blue;
45 | }
46 | // css注释
47 | .b:unknown {
48 | background: #000;
49 | }
50 | .b::unknown {
51 | background: #000;
52 | }
53 | unknown {
54 | content: "first
55 | second";
56 | width: 10pixels;
57 | background: #000
58 | }
59 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Build and Deploy
2 | on:
3 | push:
4 | branches:
5 | - master
6 | jobs:
7 | build-and-deploy:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - name: Checkout ️
11 | uses: actions/checkout@v2.3.1
12 |
13 | - name: lock npm version
14 | uses: actions/setup-node@v3
15 | with:
16 | node-version: 18.14.0
17 |
18 | - name: Install and Build
19 | run: |
20 | npm i -g pnpm
21 | pnpm run init
22 | pnpm run docs:build
23 | env:
24 | NODE_OPTIONS: '--max_old_space_size=4096 --openssl-legacy-provider'
25 |
26 | - name: Deploy
27 | uses: JamesIves/github-pages-deploy-action@4.1.3
28 | with:
29 | BRANCH: gh-pages
30 | FOLDER: docs/.vuepress/dist
31 | ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
32 |
--------------------------------------------------------------------------------
/packages/eslint-plugin/test/rules/no-http-url.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const rule = require('../../rules/no-http-url');
4 | const { RuleTester } = require('eslint');
5 |
6 | const ruleTester = new RuleTester();
7 |
8 | ruleTester.run('no-http-url', rule, {
9 | valid: [
10 | {
11 | code: "var test = 'https://chenghuai.com';",
12 | },
13 | ],
14 |
15 | invalid: [
16 | {
17 | code: "var test = 'http://chenghuai.com';",
18 | output: "var test = 'http://chenghuai.com';",
19 | errors: [
20 | {
21 | message: 'Recommended "http://chenghuai.com" switch to HTTPS',
22 | },
23 | ],
24 | },
25 | {
26 | code: "
",
27 | output: "
",
28 | parserOptions: {
29 | ecmaFeatures: {
30 | jsx: true,
31 | },
32 | },
33 | errors: [
34 | {
35 | message: 'Recommended "http://chenghuai.com" switch to HTTPS',
36 | },
37 | ],
38 | },
39 | ],
40 | });
41 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/index.ts:
--------------------------------------------------------------------------------
1 | import ora from 'ora';
2 | import scanAction from './actions/scan';
3 | import initAction from './actions/init';
4 | import { PKG_NAME } from './utils/constants';
5 | import printReport from './utils/print-report';
6 | import type { InitOptions, ScanOptions } from './types';
7 |
8 | type IInitOptions = Omit;
9 |
10 | export const init = async (options: IInitOptions) => {
11 | return await initAction({
12 | ...options,
13 | checkVersionUpdate: false,
14 | });
15 | };
16 |
17 | export const scan = async (options: ScanOptions) => {
18 | const checking = ora();
19 | checking.start(`执行 ${PKG_NAME} 代码检查`);
20 |
21 | const report = await scanAction(options);
22 | const { results, errorCount, warningCount } = report;
23 | let type = 'succeed';
24 | if (errorCount > 0) {
25 | type = 'fail';
26 | } else if (warningCount > 0) {
27 | type = 'warn';
28 | }
29 |
30 | checking[type]();
31 | if (results.length > 0) printReport(results, false);
32 |
33 | return report;
34 | };
35 |
--------------------------------------------------------------------------------
/packages/eslint-plugin/test/rules/no-secret-info.test.js:
--------------------------------------------------------------------------------
1 | const rule = require('../../rules/no-secret-info');
2 | const { RuleTester } = require('eslint');
3 |
4 | const ruleTester = new RuleTester();
5 |
6 | ruleTester.run('no-secret-info', rule, {
7 | valid: [
8 | {
9 | code: 'var accessKeySecret = process.env.ACCESS_KEY_SECRET;',
10 | },
11 | {
12 | code: 'var password = "";',
13 | },
14 | {
15 | code: `
16 | var client ={
17 | accessKeyToken: process.env.ACCESS_KEY_SECRET
18 | };
19 | `,
20 | },
21 | ],
22 |
23 | invalid: [
24 | {
25 | code: "var accessKeySecret = 'xxxx';",
26 | errors: [
27 | {
28 | message: 'Detect that the "xxxx" might be a secret token, Please check!',
29 | },
30 | ],
31 | },
32 | {
33 | code: `
34 | var client ={
35 | accessKeyToken: 'xxxx'
36 | };
37 | `,
38 | errors: [
39 | {
40 | message: 'Detect that the "xxxx" might be a secret token, Please check!',
41 | },
42 | ],
43 | },
44 | ],
45 | });
46 |
--------------------------------------------------------------------------------
/packages/stylelint-config/__tests__/fixtures/css-module.scss:
--------------------------------------------------------------------------------
1 | .commits {
2 | padding: 0;
3 |
4 | :global(.ant-timeline-item-content) {
5 | overflow: hidden;
6 | text-overflow: ellipsis;
7 | white-space: nowrap;
8 | color: #fff1aa;
9 |
10 | &>span {
11 | margin-right: 20px;
12 | white-space: nowrap;
13 | user-select: none;
14 | }
15 | }
16 | }
17 |
18 | :global(.tree-node-yellow) {
19 | :global(.ant-tree-node-content-wrapper) {
20 | color: var(--color-yellow) !important;
21 | }
22 | }
23 |
24 | :global(.ant-tree-treenode-switcher-close), :global(.tree-node-file) {
25 | display: flex;
26 | overflow: hidden;
27 |
28 | :global(.ant-tree-switcher) {
29 | flex-shrink: 0;
30 | }
31 |
32 | :global(.ant-tree-node-content-wrapper) {
33 | flex-shrink: 1;
34 | display: inline-flex !important;
35 | overflow: hidden;
36 |
37 | :global(.ant-tree-iconEle) {
38 | flex-shrink: 0;
39 | }
40 |
41 | :global(.ant-tree-title) {
42 | flex-shrink: 1;
43 | overflow: hidden;
44 | text-overflow: ellipsis;
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/packages/eslint-config/essential/rules/blacklist.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | rules: {
3 | // 统一在点号之前换行
4 | // @unessential
5 | 'dot-location': ['warn', 'property'],
6 |
7 | // 禁止使用 eval
8 | // @unessential 部分场景必须使用 eval
9 | 'no-eval': 'warn',
10 |
11 | // 禁止使用类 eval 的方法,如 setTimeout 传入字符串
12 | // @unessential
13 | 'no-implied-eval': 'warn',
14 |
15 | // 禁止使用 javascript:url,如 location.href = 'javascript:void(0)';
16 | // @unessential
17 | 'no-script-url': 'warn',
18 |
19 | // 禁止变量与外层作用域已存在的变量同名
20 | // @unessential
21 | 'no-shadow': 'warn',
22 |
23 | // 禁止出现多个连续空格
24 | // @unessential
25 | 'no-multi-spaces': [
26 | 'warn',
27 | {
28 | ignoreEOLComments: false,
29 | },
30 | ],
31 |
32 | // 使用 2 个空格缩进
33 | // @unessential
34 | indent: 'off',
35 |
36 | // 使用分号
37 | // @unessential
38 | semi: 'off',
39 |
40 | // 分号必须写在行尾
41 | // @unessential
42 | 'semi-style': 'off',
43 |
44 | // 不要直接在对象上调用 Object.prototypes 上的方法
45 | // @unessential
46 | 'no-prototype-builtins': 'warn',
47 | },
48 | };
49 |
--------------------------------------------------------------------------------
/packages/eslint-plugin/README.md:
--------------------------------------------------------------------------------
1 | # eslint-plugin-encode
2 |
3 | ## 安装
4 |
5 | 除了本包,你需要同时安装 [ESlint](https://eslint.org/)
6 |
7 | ```shell
8 | $ npm install eslint-plugin-encode eslint --save-dev
9 | ```
10 |
11 | ## 使用
12 |
13 | ### 引入插件
14 |
15 | ```js
16 | // .eslintrc.js
17 | module.exports = {
18 | plugin: ['eslint-config-encode'],
19 | rules: {
20 | 'eslint-plugin-encode/no-secret-info': 'error',
21 | },
22 | };
23 | ```
24 |
25 | ### 使用 presets
26 |
27 | ```js
28 | // .eslintrc.js
29 | module.exports = {
30 | extends: 'plugin:eslint-plugin-encode/recommended',
31 | };
32 | ```
33 |
34 | ## 支持的规则
35 |
36 | - [`no-broad-semantic-versioning`](https://encode-studio-fe.github.io/fe-spec/plugin/no-broad-semantic-versioning.html) 不要指定宽泛的版本范围
37 | - [`no-http-url`](https://encode-studio-fe.github.io/fe-spec/plugin/no-http-url.html) 使用 HTTPS 协议头的 URL,而不是 HTTP
38 | - [`no-js-in-ts-project`](https://encode-studio-fe.github.io/fe-spec/plugin/no-js-in-ts-project.html) 不要在 TS 项目中使用 JS
39 | - [`no-secret-info`](https://encode-studio-fe.github.io/fe-spec/plugin/no-secret-info.html) 不要在代码中直接设置 `password` `token` and `secret` 信息
40 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 印客学院
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/docs/npm/commitlint.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: commitlint-config-encode
3 | categories:
4 | - 工程规范
5 | tags:
6 | - 工程规范
7 | author:
8 | name: 澄怀
9 | link: https://github.com/encode-studio-fe/fe-spec
10 | ---
11 |
12 | # commitlint-config-encode
13 |
14 | :::tip
15 | 印客学院 Git 规范
16 | :::
17 |
18 | 支持配套的 [commitlint 配置](https://commitlint.js.org/#/concepts-shareable-config),用于对 `git commit message` 进行校验。
19 |
20 | ## 安装
21 |
22 | 使用时,需要安装 [@commitlint/cli](https://www.npmjs.com/package/@commitlint/cli):
23 |
24 | ```bash
25 | npm install commitlint-config-encode @commitlint/cli --save-dev
26 | ```
27 |
28 | ## 使用
29 |
30 | 在 `commitlint.config.js` 中集成本包:
31 |
32 | ```javascript
33 | module.exports = {
34 | extends: ['encode'],
35 | };
36 | ```
37 |
38 | ## 设置 git hook
39 |
40 | 可通过 [husky](https://www.npmjs.com/package/husky) 设置在 `git commit` 时触发 `commitlint`。
41 |
42 | 首先安装 husky:
43 |
44 | ```bash
45 | npm install husky --save-dev
46 | ```
47 |
48 | 然后执行添加`commit-msg`:
49 |
50 | ```bash
51 | npx husky add .husky/commit-msg 'npx commitlint --edit $1'
52 | ```
53 |
54 | 更多信息可参考 [commitlint 文档](https://commitlint.js.org/#/guides-local-setup?id=install-husky)。
55 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/lints/markdownlint/getMarkdownlintConfig.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import glob from 'glob';
3 | import markdownLint from 'markdownlint';
4 | import markdownLintConfig from 'markdownlint-config-encode';
5 | import type { ScanOptions, PKG, Config } from '../../types';
6 |
7 | type LintOptions = markdownLint.Options & { fix?: boolean };
8 |
9 | /**
10 | * 获取 Markdownlint 配置
11 | */
12 | export function getMarkdownlintConfig(opts: ScanOptions, pkg: PKG, config: Config): LintOptions {
13 | const { cwd } = opts;
14 | const lintConfig: LintOptions = {
15 | fix: Boolean(opts.fix),
16 | resultVersion: 3,
17 | };
18 |
19 | if (config.markdownlintOptions) {
20 | // 若用户传入了 markdownlintOptions,则用用户的
21 | Object.assign(lintConfig, config.markdownlintOptions);
22 | } else {
23 | const lintConfigFiles = glob.sync('.markdownlint(.@(yaml|yml|json))', { cwd });
24 | if (lintConfigFiles.length === 0) {
25 | lintConfig.config = markdownLintConfig;
26 | } else {
27 | lintConfig.config = markdownLint.readConfigSync(path.resolve(cwd, lintConfigFiles[0]));
28 | }
29 | }
30 |
31 | return lintConfig;
32 | }
33 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/lints/eslint/doEslint.ts:
--------------------------------------------------------------------------------
1 | import { ESLint } from 'eslint';
2 | import fg from 'fast-glob';
3 | import { extname, join } from 'path';
4 | import { Config, PKG, ScanOptions } from '../../types';
5 | import { ESLINT_FILE_EXT, ESLINT_IGNORE_PATTERN } from '../../utils/constants';
6 | import { formatESLintResults } from './formatESLintResults';
7 | import { getESLintConfig } from './getESLintConfig';
8 |
9 | export interface DoESLintOptions extends ScanOptions {
10 | pkg: PKG;
11 | config?: Config;
12 | }
13 |
14 | export async function doESLint(options: DoESLintOptions) {
15 | let files: string[];
16 | if (options.files) {
17 | files = options.files.filter((name) => ESLINT_FILE_EXT.includes(extname(name)));
18 | } else {
19 | files = await fg(`**/*.{${ESLINT_FILE_EXT.map((t) => t.replace(/^\./, '')).join(',')}}`, {
20 | cwd: options.cwd,
21 | ignore: ESLINT_IGNORE_PATTERN,
22 | });
23 | }
24 |
25 | const eslint = new ESLint(getESLintConfig(options, options.pkg, options.config));
26 | const reports = await eslint.lintFiles(files);
27 | if (options.fix) {
28 | await ESLint.outputFixes(reports);
29 | }
30 |
31 | return formatESLintResults(reports, options.quiet, eslint);
32 | }
33 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/lints/stylelint/doStylelint.ts:
--------------------------------------------------------------------------------
1 | import fg from 'fast-glob';
2 | import { extname, join } from 'path';
3 | import stylelint from 'stylelint';
4 | import { PKG, ScanOptions } from '../../types';
5 | import { STYLELINT_FILE_EXT, STYLELINT_IGNORE_PATTERN } from '../../utils/constants';
6 | import { formatStylelintResults } from './formatStylelintResults';
7 | import { getStylelintConfig } from './getStylelintConfig';
8 |
9 | export interface DoStylelintOptions extends ScanOptions {
10 | pkg: PKG;
11 | }
12 |
13 | export async function doStylelint(options: DoStylelintOptions) {
14 | let files: string[];
15 | if (options.files) {
16 | files = options.files.filter((name) => STYLELINT_FILE_EXT.includes(extname(name)));
17 | } else {
18 | const pattern = join(
19 | options.include,
20 | `**/*.{${STYLELINT_FILE_EXT.map((t) => t.replace(/^\./, '')).join(',')}}`,
21 | );
22 | files = await fg(pattern, {
23 | cwd: options.cwd,
24 | ignore: STYLELINT_IGNORE_PATTERN,
25 | });
26 | }
27 | const data = await stylelint.lint({
28 | ...getStylelintConfig(options, options.pkg, options.config),
29 | files,
30 | });
31 | return formatStylelintResults(data.results, options.quiet);
32 | }
33 |
--------------------------------------------------------------------------------
/packages/eslint-config/rules/base/variables.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | // 预设的环境,使用这些环境中的全局变量不会被 no-undef 报错
3 | // @link https://eslint.org/docs/user-guide/configuring#specifying-environments
4 | env: {
5 | browser: true,
6 | es6: true,
7 | jasmine: true,
8 | jest: true,
9 | jquery: true,
10 | mocha: true,
11 | node: true,
12 | },
13 | rules: {
14 | // 强制或禁止在变量声明时进行赋值
15 | 'init-declarations': 'off',
16 |
17 | // 禁止 delete 变量
18 | 'no-delete-var': 'error',
19 |
20 | // 禁止标签与变量同名
21 | 'no-label-var': 'error',
22 |
23 | // 禁用使用特定的全局变量
24 | 'no-restricted-globals': 'off',
25 |
26 | // 禁止变量与外层作用域已存在的变量同名
27 | // @unessential
28 | 'no-shadow': 'error',
29 |
30 | // 禁止使用保留字命名变量
31 | 'no-shadow-restricted-names': 'error',
32 |
33 | // 禁止使用未声明的变量
34 | 'no-undef': 'error',
35 |
36 | // 不要将变量初始化成 undefined
37 | 'no-undef-init': 'error',
38 |
39 | // 禁止使用 undefined
40 | 'no-undefined': 'off',
41 |
42 | // 声明的变量必须被使用
43 | 'no-unused-vars': ['error', { vars: 'all', args: 'after-used', ignoreRestSiblings: true }],
44 |
45 | // 不要在声明前就使用变量
46 | 'no-use-before-define': ['error', { functions: false, classes: false, variables: false }],
47 | },
48 | };
49 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/lints/prettier/doPrettier.ts:
--------------------------------------------------------------------------------
1 | import fg from 'fast-glob';
2 | import { readFile, writeFile } from 'fs-extra';
3 | import { extname, join } from 'path';
4 | import prettier from 'prettier';
5 | import { ScanOptions } from '../../types';
6 | import { PRETTIER_FILE_EXT, PRETTIER_IGNORE_PATTERN } from '../../utils/constants';
7 |
8 | export interface DoPrettierOptions extends ScanOptions {}
9 |
10 | export async function doPrettier(options: DoPrettierOptions) {
11 | let files: string[] = [];
12 | if (options.files) {
13 | files = options.files.filter((name) => PRETTIER_FILE_EXT.includes(extname(name)));
14 | } else {
15 | const pattern = join(
16 | options.include,
17 | `**/*.{${PRETTIER_FILE_EXT.map((t) => t.replace(/^\./, '')).join(',')}}`,
18 | );
19 | files = await fg(pattern, {
20 | cwd: options.cwd,
21 | ignore: PRETTIER_IGNORE_PATTERN,
22 | });
23 | }
24 | await Promise.all(files.map(formatFile));
25 | }
26 |
27 | async function formatFile(filepath: string) {
28 | const text = await readFile(filepath, 'utf8');
29 | const options = await prettier.resolveConfig(filepath);
30 | const formatted = prettier.format(text, { ...options, filepath });
31 | await writeFile(filepath, formatted, 'utf8');
32 | }
33 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/test/index.test.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const fs = require('fs-extra');
3 | const encodeFeLint = require('../lib/index');
4 |
5 | const { init } = encodeFeLint;
6 |
7 | describe('init', () => {
8 | const templatePath = path.resolve(__dirname, './fixtures/template/init');
9 | const outputPath = path.resolve(__dirname, './fixtures/template/temp');
10 |
11 | beforeEach(() => {
12 | fs.copySync(templatePath, outputPath);
13 | fs.renameSync(`${outputPath}/_vscode`, `${outputPath}/.vscode`);
14 | });
15 |
16 | test('node api init should work as expected', async () => {
17 | await init({
18 | cwd: outputPath,
19 | checkVersionUpdate: false,
20 | eslintType: 'index',
21 | enableStylelint: true,
22 | enableMarkdownlint: true,
23 | enablePrettier: true,
24 | });
25 |
26 | const pkg = require(`${outputPath}/package.json`);
27 | const settings = require(`${outputPath}/.vscode/settings.json`);
28 |
29 | expect(settings['editor.defaultFormatter']).toBe('esbenp.prettier-vscode');
30 | expect(settings['eslint.validate'].includes('233')).toBeTruthy();
31 | expect(settings.test).toBeTruthy();
32 | });
33 |
34 | afterEach(() => {
35 | fs.removeSync(outputPath);
36 | });
37 | });
38 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/lints/stylelint/formatStylelintResults.ts:
--------------------------------------------------------------------------------
1 | import { LintResult } from 'stylelint';
2 | import type { ScanResult } from '../../types';
3 | import { getStylelintRuleDocUrl } from './getStylelintDocUrl';
4 |
5 | /**
6 | * 格式化 Stylelint 输出结果
7 | */
8 | export function formatStylelintResults(results: LintResult[], quiet: boolean): ScanResult[] {
9 | return results.map(({ source, warnings }) => {
10 | let errorCount = 0;
11 | let warningCount = 0;
12 | const messages = warnings
13 | .filter((item) => !quiet || item.severity === 'error')
14 | .map((item) => {
15 | const { line = 0, column = 0, rule, severity, text } = item;
16 | if (severity === 'error') {
17 | errorCount++;
18 | } else {
19 | warningCount++;
20 | }
21 | return {
22 | line,
23 | column,
24 | rule,
25 | url: getStylelintRuleDocUrl(rule),
26 | message: text.replace(/([^ ])\.$/u, '$1').replace(new RegExp(`\\(${rule}\\)`), ''),
27 | errored: severity === 'error',
28 | };
29 | });
30 |
31 | return {
32 | filePath: source,
33 | messages,
34 | errorCount,
35 | warningCount,
36 | fixableErrorCount: 0,
37 | fixableWarningCount: 0,
38 | };
39 | });
40 | }
41 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/lints/eslint/getESLintConfigType.ts:
--------------------------------------------------------------------------------
1 | import glob from 'glob';
2 | import type { PKG } from '../../types';
3 |
4 | /**
5 | * 获取 ESLint 配置类型
6 | * @param cwd
7 | * @param pkg
8 | * @returns eslint-config-encode/index
9 | * @returns eslint-config-encode/react
10 | * @returns eslint-config-encode/typescript/index
11 | * @returns eslint-config-encode/typescript/react
12 | */
13 | export function getESLintConfigType(cwd: string, pkg: PKG): string {
14 | const tsFiles = glob.sync('./!(node_modules)/**/*.@(ts|tsx)', { cwd });
15 | const reactFiles = glob.sync('./!(node_modules)/**/*.@(jsx|tsx)', { cwd });
16 | const vueFiles = glob.sync('./!(node_modules)/**/*.vue', { cwd });
17 | const dependencies = Object.keys(pkg.dependencies || {});
18 | const language = tsFiles.length > 0 ? 'typescript' : '';
19 | let dsl = '';
20 |
21 | // dsl判断
22 | if (reactFiles.length > 0 || dependencies.some((name) => /^react(-|$)/.test(name))) {
23 | dsl = 'react';
24 | } else if (vueFiles.length > 0 || dependencies.some((name) => /^vue(-|$)/.test(name))) {
25 | dsl = 'vue';
26 | } else if (dependencies.some((name) => /^rax(-|$)/.test(name))) {
27 | dsl = 'rax';
28 | }
29 |
30 | return (
31 | 'eslint-config-encode/' + `${language}/${dsl}`.replace(/\/$/, '/index').replace(/^\//, '')
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/utils/git.ts:
--------------------------------------------------------------------------------
1 | import execa from 'execa';
2 |
3 | /**
4 | * 获取此次 commit 修改的文件列表
5 | * @param options
6 | */
7 | export const getCommitFiles = async (options: execa.Options = {}): Promise => {
8 | try {
9 | const { stdout } = await execa(
10 | 'git',
11 | [
12 | 'diff',
13 | '--staged', // 比较 暂缓区 与 last commit 的差别
14 | '--diff-filter=ACMR', // 只显示 added、copied、modified、renamed
15 | '--name-only', // 只显示更改文件的名称
16 | '--ignore-submodules',
17 | ],
18 | {
19 | ...options,
20 | all: true,
21 | cwd: options.cwd || process.cwd(),
22 | },
23 | );
24 |
25 | return stdout ? stdout.split(/\s/).filter(Boolean) : [];
26 | } catch (e) {
27 | return [];
28 | }
29 | };
30 |
31 | /**
32 | * 获取未 add 的修改文件数量
33 | * @param options
34 | */
35 | export const getAmendFiles = async (options: execa.Options = {}): Promise => {
36 | try {
37 | const { stdout } = await execa(
38 | 'git',
39 | [
40 | 'diff', // 比较 工作区 与 暂缓区的差别
41 | '--name-only',
42 | ],
43 | {
44 | ...options,
45 | all: true,
46 | cwd: options.cwd || process.cwd(),
47 | },
48 | );
49 |
50 | return stdout;
51 | } catch (e) {
52 | return '';
53 | }
54 | };
55 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/lints/markdownlint/formatMarkdownlintResults.ts:
--------------------------------------------------------------------------------
1 | import markdownlint from 'markdownlint';
2 | import type { ScanResult } from '../../types';
3 |
4 | /**
5 | * 格式化 markdownlint 输出结果
6 | */
7 | export function formatMarkdownlintResults(
8 | results: markdownlint.LintResults,
9 | quiet: boolean,
10 | ): ScanResult[] {
11 | const parsedResults = [];
12 |
13 | for (const file in results) {
14 | if (!Object.prototype.hasOwnProperty.call(results, file) || quiet) continue;
15 |
16 | let warningCount = 0;
17 | let fixableWarningCount = 0;
18 | const messages = results[file].map(
19 | ({ lineNumber, ruleNames, ruleDescription, ruleInformation, errorRange, fixInfo }) => {
20 | if (fixInfo) fixableWarningCount++;
21 | warningCount++;
22 |
23 | return {
24 | line: lineNumber,
25 | column: Array.isArray(errorRange) ? errorRange[0] : 1,
26 | rule: ruleNames[0],
27 | url: ruleInformation,
28 | message: ruleDescription,
29 | errored: false,
30 | };
31 | },
32 | );
33 |
34 | parsedResults.push({
35 | filePath: file,
36 | messages,
37 | errorCount: 0,
38 | warningCount,
39 | fixableErrorCount: 0,
40 | fixableWarningCount,
41 | });
42 | }
43 |
44 | return parsedResults;
45 | }
46 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/lints/stylelint/getStylelintConfig.ts:
--------------------------------------------------------------------------------
1 | import fs from 'fs-extra';
2 | import glob from 'glob';
3 | import path from 'path';
4 | import { LinterOptions } from 'stylelint';
5 | import type { Config, PKG, ScanOptions } from '../../types';
6 | import { STYLELINT_IGNORE_PATTERN } from '../../utils/constants';
7 |
8 | /**
9 | * 获取 Stylelint 配置
10 | */
11 | export function getStylelintConfig(opts: ScanOptions, pkg: PKG, config: Config): LinterOptions {
12 | const { cwd, fix } = opts;
13 | if (config.enableStylelint === false) return {} as any;
14 |
15 | const lintConfig: any = {
16 | fix: Boolean(fix),
17 | allowEmptyInput: true,
18 | };
19 |
20 | if (config.stylelintOptions) {
21 | // 若用户传入了 stylelintOptions,则用用户的
22 | Object.assign(lintConfig, config.stylelintOptions);
23 | } else {
24 | // 根据扫描目录下有无lintrc文件,若无则使用默认的 lint 配置
25 | const lintConfigFiles = glob.sync('.stylelintrc?(.@(js|yaml|yml|json))', { cwd });
26 | if (lintConfigFiles.length === 0 && !pkg.stylelint) {
27 | lintConfig.config = {
28 | extends: 'stylelint-config-encode',
29 | };
30 | }
31 |
32 | // 根据扫描目录下有无lintignore文件,若无则使用默认的 ignore 配置
33 | const ignoreFilePath = path.resolve(cwd, '.stylelintignore');
34 | if (!fs.existsSync(ignoreFilePath)) {
35 | lintConfig.ignorePattern = STYLELINT_IGNORE_PATTERN;
36 | }
37 | }
38 |
39 | return lintConfig;
40 | }
41 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "encode-fe-spec",
3 | "private": true,
4 | "description": "印客学院--前端编码规范工程化",
5 | "scripts": {
6 | "preinstall": "npx only-allow pnpm",
7 | "prepare": "husky install ",
8 | "init": "pnpm install --no-frozen-lockfile",
9 | "clean": "lerna clean && rm -rf node_modules",
10 | "test": "lerna run test",
11 | "docs:dev": "vuepress dev docs",
12 | "docs:build": "vuepress build docs",
13 | "deploy": "bash deploy.sh",
14 | "publish": "lerna publish",
15 | "lint": "markdownlint README.md",
16 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s"
17 | },
18 | "keywords": [
19 | "encode",
20 | "fe spec"
21 | ],
22 | "author": "chenghuai",
23 | "license": "ISC",
24 | "devDependencies": {
25 | "@commitlint/cli": "^17.6.1",
26 | "husky": "^8.0.3",
27 | "lerna": "^6.6.2",
28 | "markdownlint": "^0.28.1",
29 | "pnpm": "^8.6.0",
30 | "rimraf": "^3.0.2",
31 | "ts-node": "^10.9.1",
32 | "tslib": "^2.5.2",
33 | "typedoc": "^0.24.7",
34 | "typescript": "^5.0.4",
35 | "vue-template-compiler": "^2.7.14",
36 | "vuepress": "^1.9.9",
37 | "vuepress-plugin-one-click-copy": "^1.0.2",
38 | "vuepress-plugin-zooming": "^1.1.7"
39 | },
40 | "dependencies": {
41 | "conventional-changelog-conventionalcommits": "^4.5.0"
42 | },
43 | "publishConfig": {
44 | "access": "public",
45 | "registry": "https://registry.npmjs.org/"
46 | }
47 | }
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/lints/eslint/formatESLintResults.ts:
--------------------------------------------------------------------------------
1 | import { ESLint } from 'eslint';
2 | import type { ScanResult } from '../../types';
3 |
4 | /**
5 | * 格式化 ESLint 输出结果
6 | */
7 | export function formatESLintResults(results: ESLint.LintResult[], quiet: boolean, eslint: ESLint): ScanResult[] {
8 | const rulesMeta = eslint.getRulesMetaForResults(results);
9 |
10 | return results
11 | .filter(({ warningCount, errorCount }) => errorCount || warningCount)
12 | .map(
13 | ({
14 | filePath,
15 | messages,
16 | errorCount,
17 | warningCount,
18 | fixableErrorCount,
19 | fixableWarningCount,
20 | }) => ({
21 | filePath,
22 | errorCount,
23 | warningCount: quiet ? 0 : warningCount,
24 | fixableErrorCount,
25 | fixableWarningCount: quiet ? 0 : fixableWarningCount,
26 | messages: messages
27 | .map(({ line = 0, column = 0, ruleId, message, fatal, severity }) => {
28 | return {
29 | line,
30 | column,
31 | rule: ruleId,
32 | url: rulesMeta[ruleId]?.docs?.url || '',
33 | message: message.replace(/([^ ])\.$/u, '$1'),
34 | errored: fatal || severity === 2,
35 | };
36 | }) // dont check ruleId, which can be null
37 | // https://eslint.org/docs/developer-guide/nodejs-api.html#-lintmessage-type
38 | .filter(({ errored }) => (quiet ? errored : true)),
39 | }),
40 | );
41 | }
42 |
--------------------------------------------------------------------------------
/packages/eslint-config/essential/rules/es6-blacklist.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | rules: {
3 | // 箭头函数的箭头前后各留一个空格
4 | // @unessential
5 | 'arrow-spacing': ['warn', { before: true, after: true }],
6 |
7 | // generator 函数的 * 号前面无空格,后面有一个空格
8 | // @unessential
9 | 'generator-star-spacing': ['warn', { before: false, after: true }],
10 |
11 | // 避免箭头函数与比较操作符产生混淆
12 | // @unessential
13 | 'no-confusing-arrow': 'warn',
14 |
15 | // 回调函数使用箭头函数而不是匿名函数
16 | // @unessential
17 | 'prefer-arrow-callback': [
18 | 'warn',
19 | {
20 | allowNamedFunctions: false,
21 | allowUnboundThis: true,
22 | },
23 | ],
24 |
25 | // 优先使用 const,只有当变量会被重新赋值时才使用 let
26 | // @unessential
27 | 'prefer-const': [
28 | 'warn',
29 | {
30 | destructuring: 'any',
31 | ignoreReadBeforeAssign: true,
32 | },
33 | ],
34 |
35 | // 模板字符串中的大括号内部两侧无空格
36 | // @unessential
37 | 'template-curly-spacing': 'warn',
38 |
39 | // yield* 表达式的 * 号前面无空格,后面有一个空格
40 | // @unessential
41 | 'yield-star-spacing': ['warn', 'after'],
42 |
43 | // import 语句需要放到模块的最上方
44 | // @unessential
45 | 'import/first': 'warn',
46 |
47 | // 使用对象属性和方法的简写语法
48 | // @unessential
49 | 'object-shorthand': [
50 | 'warn',
51 | 'always',
52 | {
53 | ignoreConstructors: false,
54 | avoidQuotes: true,
55 | },
56 | ],
57 |
58 | // 使用 const 或 let 声明变量,不要使用 var
59 | // @unessential
60 | 'no-var': 'warn',
61 | },
62 | };
63 |
--------------------------------------------------------------------------------
/packages/eslint-plugin/rules/no-js-in-ts-project.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | const RULE_NAME = 'no-js-in-ts-project';
4 |
5 | const JS_REG = /\.jsx?$/;
6 |
7 | const DEFAULT_WHITE_LIST = [
8 | 'commitlint.config.js',
9 | 'eslintrc.js',
10 | 'prettierrc.js',
11 | 'stylelintrc.js',
12 | ];
13 |
14 | module.exports = {
15 | name: RULE_NAME,
16 | meta: {
17 | type: 'suggestion',
18 | fixable: null,
19 | messages: {
20 | noJSInTSProject: 'The "{{fileName}}" is not recommended in TS project',
21 | },
22 | },
23 |
24 | create(context) {
25 | const fileName = context.getFilename();
26 | const extName = path.extname(fileName);
27 | const ruleOptions = context.options[0] || {};
28 | let { whiteList = [], autoMerge = true } = ruleOptions;
29 | if (whiteList.length === 0) {
30 | whiteList = DEFAULT_WHITE_LIST;
31 | } else if (autoMerge) {
32 | whiteList = [...new Set([...DEFAULT_WHITE_LIST, ...whiteList])];
33 | }
34 | const whiteListReg = new RegExp(`(${whiteList.join('|')})$`);
35 |
36 | if (!whiteListReg.test(fileName) && JS_REG.test(extName)) {
37 | context.report({
38 | loc: {
39 | start: {
40 | line: 0,
41 | column: 0,
42 | },
43 | end: {
44 | line: 0,
45 | column: 0,
46 | },
47 | },
48 | messageId: 'noJSInTSProject',
49 | data: {
50 | fileName,
51 | },
52 | });
53 | }
54 |
55 | // Necessary
56 | return {};
57 | },
58 | };
59 |
--------------------------------------------------------------------------------
/packages/eslint-config/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "eslint-config-encode",
3 | "version": "1.0.8",
4 | "main": "index.js",
5 | "description": "印客学院 JavaScript TypeScript Node 规范",
6 | "keywords": [
7 | "encode",
8 | "javaScript",
9 | "typescript",
10 | "node",
11 | "lint"
12 | ],
13 | "scripts": {
14 | "lint": "eslint ./",
15 | "test": "mocha ./__tests__/*.test.js --timeout 5000",
16 | "print-config": "eslint --print-config ./index.js > ./print-config.json"
17 | },
18 | "author": "chenghuai",
19 | "homepage": "https://github.com/encode-studio-fe/fe-spec#readme",
20 | "license": "ISC",
21 | "repository": {
22 | "type": "git",
23 | "url": "git+https://github.com/encode-studio-fe/fe-spec.git"
24 | },
25 | "bugs": {
26 | "url": "https://github.com/encode-studio-fe/fe-spec/issues"
27 | },
28 | "devDependencies": {
29 | "@babel/core": "^7.16.0",
30 | "@babel/eslint-parser": "^7.16.3",
31 | "@babel/preset-react": "^7.16.0",
32 | "@typescript-eslint/eslint-plugin": "^5.0.0",
33 | "@typescript-eslint/parser": "^5.0.0",
34 | "eslint": "^8.7.0",
35 | "eslint-config-egg": "^10.0.0",
36 | "eslint-import-resolver-typescript": "^2.5.0",
37 | "eslint-plugin-import": "^2.25.3",
38 | "eslint-plugin-jsx-a11y": "^6.3.1",
39 | "eslint-plugin-jsx-plus": "^0.1.0",
40 | "eslint-plugin-react": "^7.17.0",
41 | "eslint-plugin-react-hooks": "^4.2.0",
42 | "eslint-plugin-vue": "^7.3.0",
43 | "lodash": "^4.17.21",
44 | "mocha": "^8.2.1",
45 | "typescript": "^4.1.2",
46 | "vue-eslint-parser": "^7.3.0"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/packages/eslint-plugin/rules/no-secret-info.js:
--------------------------------------------------------------------------------
1 | const RULE_NAME = 'no-secret-info';
2 |
3 | const DEFAULT_DANGEROUS_KEYS = ['secret', 'token', 'password'];
4 |
5 | module.exports = {
6 | meta: {
7 | type: 'problem',
8 | fixable: null,
9 | messages: {
10 | noSecretInfo: 'Detect that the "{{secret}}" might be a secret token, Please check!',
11 | },
12 | },
13 |
14 | create(context) {
15 | const ruleOptions = context.options[0] || {};
16 | let { dangerousKeys = [], autoMerge = true } = ruleOptions;
17 | if (dangerousKeys.length === 0) {
18 | dangerousKeys = DEFAULT_DANGEROUS_KEYS;
19 | } else if (autoMerge) {
20 | dangerousKeys = [...new Set(...DEFAULT_DANGEROUS_KEYS, ...dangerousKeys)];
21 | }
22 | const reg = new RegExp(dangerousKeys.join('|'));
23 |
24 | return {
25 | Literal: function handleRequires(node) {
26 | if (
27 | node.value &&
28 | node.parent &&
29 | ((node.parent.type === 'VariableDeclarator' &&
30 | node.parent.id &&
31 | node.parent.id.name &&
32 | reg.test(node.parent.id.name.toLocaleLowerCase())) ||
33 | (node.parent.type === 'Property' &&
34 | node.parent.key &&
35 | node.parent.key.name &&
36 | reg.test(node.parent.key.name.toLocaleLowerCase())))
37 | ) {
38 | context.report({
39 | node,
40 | messageId: 'noSecretInfo',
41 | data: {
42 | secret: node.value,
43 | },
44 | });
45 | }
46 | },
47 | };
48 | },
49 | };
50 |
--------------------------------------------------------------------------------
/packages/eslint-config/__tests__/use-babel-eslint.test.js:
--------------------------------------------------------------------------------
1 | const assert = require('assert');
2 | const eslint = require('eslint');
3 | const path = require('path');
4 |
5 | describe('test/use-babel-eslint.test.js', () => {
6 | it('babel-eslint parser run well for react', async () => {
7 | const configPath = './react.js';
8 | const filePath = path.join(__dirname, './fixtures/use-babel-eslint.jsx');
9 |
10 | const cli = new eslint.ESLint({
11 | overrideConfigFile: configPath,
12 | useEslintrc: false,
13 | ignore: false,
14 | });
15 |
16 | const results = await cli.lintFiles([filePath]);
17 | const { messages, errorCount, fatalErrorCount, warningCount } = results[0];
18 |
19 | assert.equal(fatalErrorCount, 0);
20 | assert.equal(errorCount, 27);
21 | assert.equal(warningCount, 7);
22 |
23 | const errorReportedByReactPlugin = messages.filter((result) => {
24 | return result.ruleId && result.ruleId.indexOf('react/') !== -1;
25 | });
26 |
27 | assert.notEqual(errorReportedByReactPlugin.length, 0);
28 | });
29 |
30 | it('babel-eslint parser run well for vue', async () => {
31 | const configPath = './vue.js';
32 | const filePath = path.join(__dirname, './fixtures/vue.vue');
33 |
34 | const cli = new eslint.ESLint({
35 | overrideConfigFile: configPath,
36 | useEslintrc: false,
37 | ignore: false,
38 | });
39 |
40 | const results = await cli.lintFiles([filePath]);
41 | const { errorCount, fatalErrorCount, warningCount } = results[0];
42 |
43 | assert.equal(fatalErrorCount, 0);
44 | assert.equal(errorCount, 4);
45 | assert.equal(warningCount, 0);
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/lints/eslint/getESLintConfig.ts:
--------------------------------------------------------------------------------
1 | import { ESLint } from 'eslint';
2 | import fs from 'fs-extra';
3 | import glob from 'glob';
4 | import path from 'path';
5 | import type { Config, PKG, ScanOptions } from '../../types';
6 | import { ESLINT_FILE_EXT } from '../../utils/constants';
7 | import { getESLintConfigType } from './getESLintConfigType';
8 |
9 | /**
10 | * 获取 ESLint 配置
11 | */
12 | export function getESLintConfig(opts: ScanOptions, pkg: PKG, config: Config): ESLint.Options {
13 | const { cwd, fix, ignore } = opts;
14 | const lintConfig: ESLint.Options = {
15 | cwd,
16 | fix,
17 | ignore,
18 | extensions: ESLINT_FILE_EXT,
19 | errorOnUnmatchedPattern: false,
20 | };
21 |
22 | if (config.eslintOptions) {
23 | // 若用户传入了 eslintOptions,则用用户的
24 | Object.assign(lintConfig, config.eslintOptions);
25 | } else {
26 | // 根据扫描目录下有无lintrc文件,若无则使用默认的 lint 配置
27 | const lintConfigFiles = glob.sync('.eslintrc?(.@(js|yaml|yml|json))', { cwd });
28 | if (lintConfigFiles.length === 0 && !pkg.eslintConfig) {
29 | lintConfig.resolvePluginsRelativeTo = path.resolve(__dirname, '../../');
30 | lintConfig.useEslintrc = false;
31 | lintConfig.baseConfig = {
32 | extends: [
33 | getESLintConfigType(cwd, pkg),
34 | // ESLint 不再管格式问题,直接使用 Prettier 进行格式化
35 | ...(config.enablePrettier ? ['prettier'] : []),
36 | ],
37 | };
38 | }
39 |
40 | // 根据扫描目录下有无lintignore文件,若无则使用默认的 ignore 配置
41 | const lintIgnoreFile = path.resolve(cwd, '.eslintignore');
42 | if (!fs.existsSync(lintIgnoreFile) && !pkg.eslintIgnore) {
43 | lintConfig.ignorePath = path.resolve(__dirname, '../config/_eslintignore.ejs');
44 | }
45 | }
46 |
47 | return lintConfig;
48 | }
49 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/actions/update.ts:
--------------------------------------------------------------------------------
1 | import { execSync } from 'child_process';
2 | import ora from 'ora';
3 | import log from '../utils/log';
4 | import npmType from '../utils/npm-type';
5 | import { PKG_NAME, PKG_VERSION } from '../utils/constants';
6 |
7 | /**
8 | * 检查最新版本号
9 | */
10 | const checkLatestVersion = async (): Promise => {
11 | const npm = await npmType;
12 | const latestVersion = execSync(`${npm} view ${PKG_NAME} version`).toString('utf-8').trim();
13 |
14 | if (PKG_VERSION === latestVersion) return null;
15 |
16 | const compareArr = PKG_VERSION.split('.').map(Number);
17 | const beComparedArr = latestVersion.split('.').map(Number);
18 |
19 | // 依次比较版本号每一位大小
20 | for (let i = 0; i < compareArr.length; i++) {
21 | if (compareArr[i] > beComparedArr[i]) {
22 | return null;
23 | } else if (compareArr[i] < beComparedArr[i]) {
24 | return latestVersion;
25 | }
26 | }
27 | };
28 |
29 | /**
30 | * 检查包的版本
31 | * @param install - 自动安装最新包
32 | */
33 | export default async (install = true) => {
34 | const checking = ora(`[${PKG_NAME}] 正在检查最新版本...`);
35 | checking.start();
36 |
37 | try {
38 | const npm = await npmType;
39 | const latestVersion = await checkLatestVersion();
40 | checking.stop();
41 |
42 | if (latestVersion && install) {
43 | const update = ora(`[${PKG_NAME}] 存在新版本,将升级至 ${latestVersion}`);
44 |
45 | update.start();
46 |
47 | execSync(`${npm} i -g ${PKG_NAME}`);
48 |
49 | update.stop();
50 | } else if (latestVersion) {
51 | log.warn(
52 | `最新版本为 ${latestVersion},本地版本为 ${PKG_VERSION},请尽快升级到最新版本。\n你可以执行 ${npm} install -g ${PKG_NAME}@latest 来安装此版本\n`,
53 | );
54 | } else if (install) {
55 | log.info(`当前没有可用的更新`);
56 | }
57 | } catch (e) {
58 | checking.stop();
59 | log.error(e);
60 | }
61 | };
62 |
--------------------------------------------------------------------------------
/packages/eslint-plugin/rules/no-broad-semantic-versioning.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | const RULE_NAME = 'no-broad-semantic-versioning';
4 |
5 | module.exports = {
6 | name: RULE_NAME,
7 | meta: {
8 | type: 'problem',
9 | fixable: null,
10 | messages: {
11 | noBroadSemanticVersioning:
12 | 'The "{{dependencyName}}" is not recommended to use "{{versioning}}"',
13 | },
14 | },
15 |
16 | create(context) {
17 | if (path.basename(context.getFilename()) !== 'package.json') {
18 | return {};
19 | }
20 |
21 | const cwd = context.getCwd();
22 |
23 | return {
24 | Property: function handleRequires(node) {
25 | if (
26 | node.key &&
27 | node.key.value &&
28 | (node.key.value === 'dependencies' || node.key.value === 'devDependencies') &&
29 | node.value &&
30 | node.value.properties
31 | ) {
32 | node.value.properties.forEach((property) => {
33 | if (property.key && property.key.value) {
34 | const dependencyName = property.key.value;
35 | const dependencyVersion = property.value.value;
36 | if (
37 | // *
38 | dependencyVersion.indexOf('*') > -1 ||
39 | // x.x
40 | dependencyVersion.indexOf('x') > -1 ||
41 | // > x
42 | dependencyVersion.indexOf('>') > -1
43 | ) {
44 | context.report({
45 | loc: property.loc,
46 | messageId: 'noBroadSemanticVersioning',
47 | data: {
48 | dependencyName,
49 | versioning: dependencyVersion,
50 | },
51 | });
52 | }
53 | }
54 | });
55 | }
56 | },
57 | };
58 | },
59 | };
60 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/config/_vscode/settings.json.ejs:
--------------------------------------------------------------------------------
1 | {
2 | <%_ if (enableESLint) { _%>
3 | "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact", "vue"],
4 | <%_ } _%>
5 | <%_ if (enableStylelint) { _%>
6 | "stylelint.validate": [<%- stylelintExt.map((ext) => `"${ext.replace(/^\./, '')}"`).join(',') %>],
7 | <%_ } _%>
8 | "editor.codeActionsOnSave": {
9 | "source.fixAll.eslint": true<%= enableStylelint || enableMarkdownlint ? ',' : ''%>
10 | <%_ if (enableStylelint) { _%>
11 | "source.fixAll.stylelint": true<%= enableMarkdownlint ? ',' : ''%>
12 | <%_ } _%>
13 | <%_ if (enableMarkdownlint) { _%>
14 | "source.fixAll.markdownlint": true
15 | <%_ } _%>
16 | },
17 | <%_ if (enablePrettier) { _%>
18 | "editor.defaultFormatter": "esbenp.prettier-vscode",
19 | "[javascript]": {
20 | "editor.defaultFormatter": "esbenp.prettier-vscode"
21 | },
22 | "[javascriptreact]": {
23 | "editor.defaultFormatter": "esbenp.prettier-vscode"
24 | },
25 | "[typescript]": {
26 | "editor.defaultFormatter": "esbenp.prettier-vscode"
27 | },
28 | "[typescriptreact]": {
29 | "editor.defaultFormatter": "esbenp.prettier-vscode"
30 | },
31 | "[vue]": {
32 | "editor.defaultFormatter": "esbenp.prettier-vscode"
33 | },
34 | "[css]": {
35 | "editor.defaultFormatter": "esbenp.prettier-vscode"
36 | },
37 | "[less]": {
38 | "editor.defaultFormatter": "esbenp.prettier-vscode"
39 | },
40 | "[scss]": {
41 | "editor.defaultFormatter": "esbenp.prettier-vscode"
42 | },
43 | "[html]": {
44 | "editor.defaultFormatter": "esbenp.prettier-vscode"
45 | },
46 | "[json]": {
47 | "editor.defaultFormatter": "esbenp.prettier-vscode"
48 | },
49 | "[jsonc]": {
50 | "editor.defaultFormatter": "esbenp.prettier-vscode"
51 | },
52 | <%_ } _%>
53 | "editor.formatOnSave": <%= enablePrettier ? 'true' : 'false' %>
54 | }
55 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/test/cli.test.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const fs = require('fs-extra');
3 | const execa = require('execa');
4 | const packageJson = require('../package.json');
5 |
6 | const cli = (args, options) => {
7 | return execa('node', [path.resolve(__dirname, '../lib/cli.js'), ...args], options);
8 | };
9 |
10 | test('--version should output right version', async () => {
11 | const { stdout } = await cli(['--version']);
12 | expect(stdout).toBe(packageJson.version);
13 | });
14 |
15 | describe(`'fix' command`, () => {
16 | const dir = path.resolve(__dirname, './fixtures/autofix');
17 | const outputFilePath = path.resolve(dir, './temp/temp.js');
18 | const errorFileContent = fs.readFileSync(path.resolve(dir, './semi-error.js'), 'utf8');
19 | const expectedFileContent = fs.readFileSync(path.resolve(dir, './semi-expected.js'), 'utf8');
20 |
21 | beforeEach(() => {
22 | fs.outputFileSync(outputFilePath, errorFileContent, 'utf8');
23 | });
24 |
25 | test('should autofix problematic code', async () => {
26 | await cli(['fix'], {
27 | cwd: path.dirname(`${dir}/result`),
28 | });
29 | expect(fs.readFileSync(outputFilePath, 'utf8')).toEqual(expectedFileContent);
30 | });
31 |
32 | afterEach(() => {
33 | fs.removeSync(`${dir}/temp`);
34 | });
35 | });
36 |
37 | describe(`'exec' command`, () => {
38 | const semverRegex = /(\d+)\.(\d+)\.(\d+)/;
39 |
40 | test(`'exec eslint' should work as expected`, async () => {
41 | const { stdout } = await cli(['exec', 'eslint', '--version']);
42 | expect(stdout).toMatch(semverRegex);
43 | });
44 |
45 | test(`'exec stylelint' should work as expected`, async () => {
46 | const { stdout } = await cli(['exec', 'stylelint', '--version']);
47 | expect(stdout).toMatch(semverRegex);
48 | });
49 |
50 | test(`'exec commitlint' should work as expected`, async () => {
51 | const { stdout } = await cli(['exec', 'commitlint', '--version']);
52 | expect(stdout).toMatch(semverRegex);
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/utils/generate-template.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import fs from 'fs-extra';
3 | import _ from 'lodash';
4 | import glob from 'glob';
5 | import ejs from 'ejs';
6 | import {
7 | ESLINT_IGNORE_PATTERN,
8 | STYLELINT_FILE_EXT,
9 | STYLELINT_IGNORE_PATTERN,
10 | MARKDOWN_LINT_IGNORE_PATTERN,
11 | } from './constants';
12 |
13 | /**
14 | * vscode 配置合并
15 | * @param filepath
16 | * @param content
17 | */
18 | const mergeVSCodeConfig = (filepath: string, content: string) => {
19 | // 不需要 merge
20 | if (!fs.existsSync(filepath)) return content;
21 |
22 | try {
23 | const targetData = fs.readJSONSync(filepath);
24 | const sourceData = JSON.parse(content);
25 | return JSON.stringify(
26 | _.mergeWith(targetData, sourceData, (target, source) => {
27 | if (Array.isArray(target) && Array.isArray(source)) {
28 | return [...new Set(source.concat(target))];
29 | }
30 | }),
31 | null,
32 | 2,
33 | );
34 | } catch (e) {
35 | return '';
36 | }
37 | };
38 |
39 | /**
40 | * 实例化模板
41 | * @param cwd
42 | * @param data
43 | * @param vscode
44 | */
45 | export default (cwd: string, data: Record, vscode?: boolean) => {
46 | const templatePath = path.resolve(__dirname, '../config');
47 | const templates = glob.sync(`${vscode ? '_vscode' : '**'}/*.ejs`, { cwd: templatePath });
48 | for (const name of templates) {
49 | const filepath = path.resolve(cwd, name.replace(/\.ejs$/, '').replace(/^_/, '.'));
50 | let content = ejs.render(fs.readFileSync(path.resolve(templatePath, name), 'utf8'), {
51 | eslintIgnores: ESLINT_IGNORE_PATTERN,
52 | stylelintExt: STYLELINT_FILE_EXT,
53 | stylelintIgnores: STYLELINT_IGNORE_PATTERN,
54 | markdownLintIgnores: MARKDOWN_LINT_IGNORE_PATTERN,
55 | ...data,
56 | });
57 |
58 | // 合并 vscode config
59 | if (/^_vscode/.test(name)) {
60 | content = mergeVSCodeConfig(filepath, content);
61 | }
62 |
63 | // 跳过空文件
64 | if (!content.trim()) continue;
65 |
66 | fs.outputFileSync(filepath, content, 'utf8');
67 | }
68 | };
69 |
--------------------------------------------------------------------------------
/packages/eslint-config/essential/react.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | '../react',
4 | './rules/set-style-to-warn',
5 | './rules/blacklist',
6 | './rules/es6-blacklist',
7 | ].map(require.resolve),
8 | rules: {
9 | // 标签的属性有多行时,结束标签需另起一行
10 | // @unessential
11 | 'react/jsx-closing-bracket-location': ['warn', 'line-aligned'],
12 |
13 | // JSX 语法闭合标签的缩进和换行
14 | // @unessential
15 | 'react/jsx-closing-tag-location': 'warn',
16 |
17 | // JSX 行内属性间仅有一个空格
18 | // @unessential
19 | 'react/jsx-props-no-multi-spaces': 'warn',
20 |
21 | // JSX 属性的大括号内部两侧无空格
22 | // @unessential
23 | 'react/jsx-curly-spacing': ['warn', 'never', { allowMultiline: true }],
24 |
25 | // JSX 属性使用 2 个空格缩进
26 | // @unessential
27 | 'react/jsx-indent-props': ['off', 2],
28 |
29 | // 标签属性的换行,如果标签有多个属性,且存在换行,则每个属性都需要换行独占一行
30 | // @unessential
31 | 'react/jsx-max-props-per-line': ['warn', { maximum: 1, when: 'multiline' }],
32 |
33 | // 禁止使用已经废弃的方法
34 | // @unessential
35 | 'react/no-deprecated': 'warn',
36 |
37 | // 多行的 JSX 标签需用小括号包裹
38 | // @unessential
39 | 'react/jsx-wrap-multilines': [
40 | 'warn',
41 | {
42 | declaration: true,
43 | assignment: true,
44 | return: true,
45 | arrow: true,
46 | },
47 | ],
48 |
49 | // 设置第一个属性的位置。multiline-multiprop:如果JSX标签占用多行并且有多个属性,则第一个属性应始终放在新行上
50 | // @unessential
51 | 'react/jsx-first-prop-new-line': ['warn', 'multiline-multiprop'],
52 |
53 | // 不要在 JSX 属性的等号两边加空格
54 | // @unessential
55 | 'react/jsx-equals-spacing': ['warn', 'never'],
56 |
57 | // JSX 使用 2 个空格缩进
58 | // @unessential
59 | 'react/jsx-indent': ['off', 2],
60 |
61 | // 不要使用 findDOMNode,严格模式下已经弃用
62 | // @unessential
63 | 'react/no-find-dom-node': 'warn',
64 |
65 | // 自闭合标签的斜线前有且仅有一个空格
66 | // @unessential
67 | 'react/jsx-tag-spacing': [
68 | 'warn',
69 | {
70 | closingSlash: 'never',
71 | beforeSelfClosing: 'always',
72 | afterOpening: 'never',
73 | },
74 | ],
75 | },
76 | };
77 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/actions/scan.ts:
--------------------------------------------------------------------------------
1 | import fs from 'fs-extra';
2 | import path from 'path';
3 | import { doESLint, doMarkdownlint, doPrettier, doStylelint } from '../lints';
4 | import type { Config, PKG, ScanOptions, ScanReport, ScanResult } from '../types';
5 | import { PKG_NAME } from '../utils/constants';
6 |
7 | export default async (options: ScanOptions): Promise => {
8 | const { cwd, fix, outputReport, config: scanConfig } = options;
9 |
10 | const readConfigFile = (pth: string): any => {
11 | const localPath = path.resolve(cwd, pth);
12 | return fs.existsSync(localPath) ? require(localPath) : {};
13 | };
14 | const pkg: PKG = readConfigFile('package.json');
15 | const config: Config = scanConfig || readConfigFile(`${PKG_NAME}.config.js`);
16 | const runErrors: Error[] = [];
17 | let results: ScanResult[] = [];
18 |
19 | // prettier
20 | if (fix && config.enablePrettier !== false) {
21 | await doPrettier(options);
22 | }
23 |
24 | // eslint
25 | if (config.enableESLint !== false) {
26 | try {
27 | const eslintResults = await doESLint({ ...options, pkg, config });
28 | results = results.concat(eslintResults);
29 | } catch (e) {
30 | runErrors.push(e);
31 | }
32 | }
33 |
34 | // stylelint
35 | if (config.enableStylelint !== false) {
36 | try {
37 | const stylelintResults = await doStylelint({ ...options, pkg, config });
38 | results = results.concat(stylelintResults);
39 | } catch (e) {
40 | runErrors.push(e);
41 | }
42 | }
43 |
44 | // markdown
45 | if (config.enableMarkdownlint !== false) {
46 | try {
47 | const markdownlintResults = await doMarkdownlint({ ...options, pkg, config });
48 | results = results.concat(markdownlintResults);
49 | } catch (e) {
50 | runErrors.push(e);
51 | }
52 | }
53 |
54 | // 生成报告报告文件
55 | if (outputReport) {
56 | const reportPath = path.resolve(process.cwd(), `./${PKG_NAME}-report.json`);
57 | fs.outputFile(reportPath, JSON.stringify(results, null, 2), () => {});
58 | }
59 |
60 | return {
61 | results,
62 | errorCount: results.reduce((count, { errorCount }) => count + errorCount, 0),
63 | warningCount: results.reduce((count, { warningCount }) => count + warningCount, 0),
64 | runErrors,
65 | };
66 | };
67 |
--------------------------------------------------------------------------------
/packages/encode-fe-lint/src/lints/markdownlint/doMarkdownlint.ts:
--------------------------------------------------------------------------------
1 | import fg from 'fast-glob';
2 | import { readFile, writeFile } from 'fs-extra';
3 | import markdownlint, { LintError } from 'markdownlint';
4 | import markdownlintRuleHelpers from 'markdownlint-rule-helpers';
5 | import { extname, join } from 'path';
6 | import { Config, PKG, ScanOptions } from '../../types';
7 | import { MARKDOWN_LINT_FILE_EXT, MARKDOWN_LINT_IGNORE_PATTERN } from '../../utils/constants';
8 | import { formatMarkdownlintResults } from './formatMarkdownlintResults';
9 | import { getMarkdownlintConfig } from './getMarkdownlintConfig';
10 |
11 | export interface DoMarkdownlintOptions extends ScanOptions {
12 | pkg: PKG;
13 | config?: Config;
14 | }
15 |
16 | export async function doMarkdownlint(options: DoMarkdownlintOptions) {
17 | let files: string[];
18 | if (options.files) {
19 | files = options.files.filter((name) => MARKDOWN_LINT_FILE_EXT.includes(extname(name)));
20 | } else {
21 | const pattern = join(
22 | options.include,
23 | `**/*.{${MARKDOWN_LINT_FILE_EXT.map((t) => t.replace(/^\./, '')).join(',')}}`,
24 | );
25 | files = await fg(pattern, {
26 | cwd: options.cwd,
27 | ignore: MARKDOWN_LINT_IGNORE_PATTERN,
28 | });
29 | }
30 | const results = await markdownlint.promises.markdownlint({
31 | ...getMarkdownlintConfig(options, options.pkg, options.config),
32 | files,
33 | });
34 | // 修复
35 | if (options.fix) {
36 | await Promise.all(
37 | Object.keys(results).map((filename) => formatMarkdownFile(filename, results[filename])),
38 | );
39 | for (const file in results) {
40 | if (!Object.prototype.hasOwnProperty.call(results, file)) continue;
41 | }
42 | }
43 | return formatMarkdownlintResults(results, options.quiet);
44 | }
45 |
46 | async function formatMarkdownFile(filename: string, errors: LintError[]) {
47 | const fixes = errors?.filter((error) => error.fixInfo);
48 |
49 | if (fixes?.length > 0) {
50 | const originalText = await readFile(filename, 'utf8');
51 | const fixedText = markdownlintRuleHelpers.applyFixes(originalText, fixes);
52 | if (originalText !== fixedText) {
53 | await writeFile(filename, fixedText, 'utf8');
54 | return errors.filter((error) => !error.fixInfo);
55 | }
56 | }
57 | return errors;
58 | }
59 |
--------------------------------------------------------------------------------
/docs/npm/eslint-plugin.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: eslint-plugin-encode
3 | categories:
4 | - 工程规范
5 | tags:
6 | - 工程规范
7 | author:
8 | name: 澄怀
9 | link: https://github.com/encode-studio-fe/fe-spec
10 | ---
11 |
12 | # eslint-plugin-encode
13 |
14 | 除了本包,你需要同时安装 [ESlint](https://eslint.org/)
15 |
16 | ```shell
17 | $ npm install eslint-plugin-encode eslint --save-dev
18 | ```
19 |
20 | ## 使用
21 |
22 | ### 引入插件
23 |
24 | ```js
25 | // .eslintrc.js
26 | module.exports = {
27 | plugin: ['eslint-plugin-encode'],
28 | rules: {
29 | 'eslint-plugin-encode/no-secret-info': 'error',
30 | },
31 | };
32 | ```
33 |
34 | ### 使用 presets
35 |
36 | ```js
37 | // .eslintrc.js
38 | module.exports = {
39 | extends: 'plugin:eslint-plugin-encode/recommended',
40 | };
41 | ```
42 |
43 | ## 支持的规则
44 |
45 | ### `no-broad-semantic-versioning`
46 |
47 | 不要在 `package.json` 中使用太过宽泛的版本指定方式,包括 `*`、`x` 和 `> x` 。
48 |
49 | #### 规则内容
50 |
51 | 参照 [https://docs.npmjs.com/about-semantic-versioning](https://docs.npmjs.com/about-semantic-versioning)。
52 |
53 | 使用 `*`、 `x` 和 `> x` 指定版本会被警告。
54 |
55 | ### `no-http-url`
56 |
57 | 推荐将 HTTP 链接换为 HTTPS 链接。
58 |
59 | #### 规则内容
60 |
61 | **错误代码**示例:
62 |
63 | ```js
64 | var test = 'http://chenghuai.com';
65 | var jsx =
;
66 | ```
67 |
68 | #### 何时不适用
69 |
70 | 如果你的网站只支持 HTTP 时。
71 |
72 | ### `no-js-in-ts-project`
73 |
74 | 不推荐在项目中同时存在 `JS` 和 `TS` 文件。
75 |
76 | #### 规则内容
77 |
78 | **错误示例**,TS 项目中包含 JS 文件:
79 |
80 | ```Bash
81 | .
82 | ├── index.ts
83 | ├── home.js
84 | └── tsconfig.json
85 | ```
86 |
87 | **正确示例**:
88 |
89 | ```Bash
90 | .
91 | ├── index.ts
92 | ├── home.ts
93 | └── tsconfig.json
94 | ```
95 |
96 | #### 规则选项
97 |
98 | 默认当存在 `commitlint.config.js`, `eslintrc.js`, `prettierrc.js`, `stylelintrc.js` 文件时不会报错,支持自定义设置文件白名单。
99 |
100 | ### `no-secret-info`
101 |
102 | 不在代码中直接通过纯文本值设置 `password` `token` 和 `secret` 信息。
103 |
104 | #### 规则内容
105 |
106 | 在包含 `password` `token` and `secret` 名称的 key 中禁止使用纯文本值。
107 |
108 | **错误**代码示例:
109 |
110 | ```js
111 | var accessKeySecret = 'xxxx';
112 |
113 | var client = {
114 | accessKeyToken: 'xxxx',
115 | };
116 | ```
117 |
118 | **正确**代码示例:
119 |
120 | ```js
121 | var accessKeySecret = process.env.ACCESS_KEY_SECRET;
122 |
123 | var client = {
124 | accessKeyToken: process.env.ACCESS_KEY_SECRET,
125 | };
126 | ```
127 |
--------------------------------------------------------------------------------
/packages/markdownlint-config/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://raw.githubusercontent.com/DavidAnson/markdownlint/main/schema/markdownlint-config-schema.json",
3 | "default": true,
4 | "ul-style": {
5 | "style": "dash"
6 | },
7 | "no-trailing-spaces": {
8 | "br_spaces": 0,
9 | "list_item_empty_lines": false
10 | },
11 | "list-marker-space": false,
12 | "line-length": false,
13 | "no-inline-html": false,
14 | "no-duplicate-header": false,
15 | "proper-names": {
16 | "names": [
17 | "JavaScript",
18 | "HTML",
19 | "CSS",
20 | "AJAX",
21 | "JSON",
22 | "DOM",
23 | "BOM",
24 | "Less",
25 | "Sass",
26 | "SCSS",
27 | "HTTP",
28 | "HTTPS",
29 | "WebSocket",
30 | "ECMAScript",
31 | "ECMAScript 2015",
32 | "ECMAScript 6",
33 | "ES6",
34 | "ES2015",
35 | "jQuery",
36 | "jQuery Mobile",
37 | "React",
38 | "React Native",
39 | "Bootstrap",
40 | "Gulp",
41 | "Grunt",
42 | "webpack",
43 | "Yeoman",
44 | "npm",
45 | "spm",
46 | "Babel",
47 | "Mocha",
48 | "Jasmine",
49 | "PHP",
50 | "Java",
51 | "Node.js",
52 | "NodeJS",
53 | "MySQL",
54 | "MongoDB",
55 | "Redis",
56 | "Apache",
57 | "Nginx",
58 | "NGINX",
59 | "GitHub",
60 | "GitLab",
61 | "GitCafe",
62 | "Chrome",
63 | "Firefox",
64 | "Safari",
65 | "Internet Explore",
66 | "IE",
67 | "IE 7",
68 | "Opera",
69 | "UC",
70 | "Android",
71 | "iOS",
72 | "Windows",
73 | "OS X",
74 | "Ubuntu",
75 | "Linux",
76 | "Debian",
77 | "PC",
78 | "Mobile",
79 | "H5",
80 | "MacBook",
81 | "MacBook Pro",
82 | "MacBook Air",
83 | "iMac",
84 | "Mac Pro",
85 | "iPad",
86 | "Mac mini",
87 | "iPad Air",
88 | "iPad Air 2",
89 | "iPad mini",
90 | "iPhone",
91 | "iPhone 6s",
92 | "iPhone 6s Plus",
93 | "Apple Watch",
94 | "Alibaba",
95 | "Taobao",
96 | "Google",
97 | "Alphabet",
98 | "Apple",
99 | "Microsoft",
100 | "Yahoo",
101 | "FPS",
102 | "UI",
103 | "URL",
104 | "URI",
105 | "URLs",
106 | "URIs",
107 | "Visual Studio Code"
108 | ],
109 | "code_blocks": false
110 | }
111 | }
--------------------------------------------------------------------------------
/packages/eslint-config/rules/vue.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 本文件的规则由 eslint-plugin-vue 提供,使用 vue-eslint-parser 作为 parser
3 | * @link https://eslint.vuejs.org/rules/
4 | */
5 |
6 | module.exports = {
7 | parser: 'vue-eslint-parser',
8 | plugins: ['vue'],
9 | rules: {
10 | // 给 template 提供 eslint-disable 的能力,支持如下注释:
11 | // eslint-disable,eslint-enable,eslint-disable-line,eslint-disable-next-line
12 | 'vue/comment-directive': 'error',
13 |
14 | // 本条是对JS规范 no-unused-vars 的补充,防止变量被错误地标记为未使用
15 | 'vue/jsx-uses-vars': 'error',
16 |
17 | // 组件的 data 必须是一个函数
18 | 'vue/no-shared-component-data': 'error',
19 |
20 | // Prop 定义类型应该是构造函数
21 | 'vue/require-prop-type-constructor': 'error',
22 |
23 | // Prop 的默认值必须匹配它的类型
24 | 'vue/require-valid-default-prop': 'error',
25 |
26 | // 为 v-for 设置键值
27 | 'vue/require-v-for-key': 'error',
28 |
29 | // 避免 v-if 和 v-for 用在一起
30 | 'vue/no-use-v-if-with-v-for': 'warn',
31 |
32 | // 计算属性禁止包含异步方法
33 | 'vue/no-async-in-computed-properties': 'error',
34 |
35 | // 禁止在对象字面量中出现重复的键
36 | 'vue/no-dupe-keys': 'error',
37 |
38 | // 禁止出现重复的属性
39 | 'vue/no-duplicate-attributes': 'error',
40 |
41 | // 禁止出现语法错误
42 | // @link https://html.spec.whatwg.org/multipage/parsing.html#parse-errors
43 | 'vue/no-parsing-error': [
44 | 'error',
45 | {
46 | 'x-invalid-end-tag': false,
47 | 'invalid-first-character-of-tag-name': false,
48 | },
49 | ],
50 |
51 | // 禁止使用 vue 中的关键字
52 | 'vue/no-reserved-keys': 'error',
53 |
54 | // 禁止在计算属性中对属性修改
55 | 'vue/no-side-effects-in-computed-properties': 'error',
56 |
57 | // 禁止 使用 key 属性
58 | 'vue/no-template-key': 'warn',
59 |
60 | // 禁止在