├── lerna.json
├── .eslintignore
├── .prettierignore
├── .husky
├── pre-commit
└── commit-msg
├── packages
├── spec
│ ├── .stylelintignore
│ ├── examples
│ │ ├── rax-ts
│ │ │ ├── index.css
│ │ │ └── index.tsx
│ │ ├── rax
│ │ │ ├── index.css
│ │ │ └── index.jsx
│ │ ├── common
│ │ │ └── index.js
│ │ ├── common-ts
│ │ │ └── index.ts
│ │ ├── vue
│ │ │ └── index.vue
│ │ ├── vue-ts
│ │ │ └── index.vue
│ │ ├── react
│ │ │ ├── index.module.scss
│ │ │ └── index.jsx
│ │ └── react-ts
│ │ │ ├── index.module.scss
│ │ │ └── index.tsx
│ ├── src
│ │ ├── commitlint
│ │ │ ├── rax.js
│ │ │ ├── vue.js
│ │ │ ├── common.js
│ │ │ └── react.js
│ │ ├── stylelint
│ │ │ ├── rax.js
│ │ │ ├── vue.js
│ │ │ ├── common.js
│ │ │ └── react.js
│ │ ├── prettier
│ │ │ ├── rax.js
│ │ │ ├── vue.js
│ │ │ ├── common.js
│ │ │ └── react.js
│ │ ├── eslint
│ │ │ ├── react.js
│ │ │ ├── vue.js
│ │ │ ├── common.js
│ │ │ ├── react-ts.js
│ │ │ ├── rax.js
│ │ │ ├── common-ts.js
│ │ │ ├── rax-ts.js
│ │ │ └── vue-ts.js
│ │ ├── deepmerge.js
│ │ ├── getRaxEslintConfig.js
│ │ └── index.js
│ ├── .eslintrc.rax.js
│ ├── .eslintrc.vue.js
│ ├── .eslintrc.common.js
│ ├── .eslintrc.rax-ts.js
│ ├── .eslintrc.react.js
│ ├── .eslintrc.vue-ts.js
│ ├── .stylelintrc.js
│ ├── .eslintrc.common-ts.js
│ ├── .eslintrc.react-ts.js
│ ├── tsconfig.json
│ ├── .editorconfig
│ ├── CHANGELOG.md
│ ├── test
│ │ └── index.test.js
│ ├── package.json
│ └── README.md
└── eslint-plugin-best-practices
│ ├── src
│ ├── docsUrl.js
│ ├── configs
│ │ ├── rax.js
│ │ ├── react.js
│ │ ├── rax-ts.js
│ │ ├── react-ts.js
│ │ └── common.js
│ ├── index.js
│ └── rules
│ │ ├── deps-no-resolutions.js
│ │ ├── no-http-url.js
│ │ ├── recommend-functional-component.js
│ │ ├── deps-no-ice-scripts.js
│ │ ├── no-js-in-ts-project.js
│ │ ├── recommend-update-rax.js
│ │ ├── deps-no-router-library.js
│ │ ├── no-lowercase-component-name.js
│ │ ├── recommend-add-line-height-unit.js
│ │ ├── no-broad-semantic-versioning.js
│ │ └── recommend-polyfill.js
│ ├── docs
│ └── rules
│ │ ├── no-http-url.md
│ │ ├── deps-no-resolutions.md
│ │ ├── no-broad-semantic-versioning.md
│ │ ├── deps-no-router-library.md
│ │ ├── recommend-update-rax.md
│ │ ├── no-js-in-ts-project.md
│ │ ├── recommend-functional-component.md
│ │ ├── deps-no-ice-scripts.md
│ │ ├── no-lowercase-component-name.md
│ │ ├── recommend-polyfill.md
│ │ └── recommend-add-line-height-unit.md
│ ├── CHANGELOG.md
│ ├── test
│ └── src
│ │ └── rules
│ │ ├── no-js-in-ts-project.test.js
│ │ ├── deps-no-resolutions.test.js
│ │ ├── recommend-update-rax.test.js
│ │ ├── deps-no-ice-scripts.test.js
│ │ ├── no-broad-semantic-versioning.test.js
│ │ ├── deps-no-router-library.test.js
│ │ ├── no-http-url.test.js
│ │ ├── recommend-functional-component.test.js
│ │ ├── recommend-add-line-height-unit.test.js
│ │ ├── no-lowercase-component-name.test.js
│ │ └── recommend-polyfill.test.js
│ ├── package.json
│ └── README.md
├── .prettierrc.js
├── commitlint.config.js
├── tsconfig.json
├── .gitignore
├── .github
├── workflows
│ ├── ci.yml
│ └── auto-publisher.yml
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── .eslintrc.js
├── scripts
├── publish.js
└── getPackageInfos.js
├── package.json
└── README.md
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "packages": [
3 | "packages/*"
4 | ],
5 | "version": "0.0.0"
6 | }
7 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | lib/
3 | dist/
4 | build/
5 | coverage/
6 | demo/
7 | examples/
8 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | lib/
3 | dist/
4 | build/
5 | coverage/
6 | demo/
7 | examples/
8 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | npm run lint && npm run test
5 |
--------------------------------------------------------------------------------
/packages/spec/.stylelintignore:
--------------------------------------------------------------------------------
1 | # 忽略目录
2 | build/
3 | tests/
4 | demo/
5 |
6 | # node 覆盖率文件
7 | coverage/
8 |
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | npx --no-install commitlint --edit "$1"
5 |
--------------------------------------------------------------------------------
/packages/spec/examples/rax-ts/index.css:
--------------------------------------------------------------------------------
1 | .logo {
2 | width: 200rpx;
3 | height: 180rpx;
4 | margin-bottom: 20rpx;
5 | }
6 |
--------------------------------------------------------------------------------
/packages/spec/examples/rax/index.css:
--------------------------------------------------------------------------------
1 | .logo {
2 | width: 200rpx;
3 | height: 180rpx;
4 | margin-bottom: 20rpx;
5 | }
6 |
--------------------------------------------------------------------------------
/packages/spec/src/commitlint/rax.js:
--------------------------------------------------------------------------------
1 | // commitlint config for rax project
2 | module.exports = {
3 | extends: 'ali',
4 | };
5 |
--------------------------------------------------------------------------------
/packages/spec/src/commitlint/vue.js:
--------------------------------------------------------------------------------
1 | // commitlint config for vue project
2 | module.exports = {
3 | extends: 'ali',
4 | };
5 |
--------------------------------------------------------------------------------
/packages/spec/.eslintrc.rax.js:
--------------------------------------------------------------------------------
1 | const { getESLintConfig } = require('./src');
2 |
3 | module.exports = getESLintConfig('rax');
4 |
--------------------------------------------------------------------------------
/packages/spec/.eslintrc.vue.js:
--------------------------------------------------------------------------------
1 | const { getESLintConfig } = require('./src');
2 |
3 | module.exports = getESLintConfig('vue');
4 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | const { getPrettierConfig } = require('./packages/spec/src/');
2 |
3 | module.exports = getPrettierConfig('react');
4 |
--------------------------------------------------------------------------------
/packages/spec/.eslintrc.common.js:
--------------------------------------------------------------------------------
1 | const { getESLintConfig } = require('./src');
2 |
3 | module.exports = getESLintConfig('common');
4 |
--------------------------------------------------------------------------------
/packages/spec/.eslintrc.rax-ts.js:
--------------------------------------------------------------------------------
1 | const { getESLintConfig } = require('./src');
2 |
3 | module.exports = getESLintConfig('rax-ts');
4 |
--------------------------------------------------------------------------------
/packages/spec/.eslintrc.react.js:
--------------------------------------------------------------------------------
1 | const { getESLintConfig } = require('./src');
2 |
3 | module.exports = getESLintConfig('react');
4 |
--------------------------------------------------------------------------------
/packages/spec/.eslintrc.vue-ts.js:
--------------------------------------------------------------------------------
1 | const { getESLintConfig } = require('./src');
2 |
3 | module.exports = getESLintConfig('vue-ts');
4 |
--------------------------------------------------------------------------------
/packages/spec/.stylelintrc.js:
--------------------------------------------------------------------------------
1 | const { getStylelintConfig } = require('./src');
2 |
3 | module.exports = getStylelintConfig('react');
4 |
--------------------------------------------------------------------------------
/packages/spec/examples/common/index.js:
--------------------------------------------------------------------------------
1 | const foo = [1, 2];
2 | console.log(foo);
3 |
4 | const t = { s: 1 };
5 | console.log(t['s']);
6 |
--------------------------------------------------------------------------------
/packages/spec/src/commitlint/common.js:
--------------------------------------------------------------------------------
1 | // commitlint config for common project
2 | module.exports = {
3 | extends: 'ali',
4 | };
5 |
--------------------------------------------------------------------------------
/packages/spec/.eslintrc.common-ts.js:
--------------------------------------------------------------------------------
1 | const { getESLintConfig } = require('./src');
2 |
3 | module.exports = getESLintConfig('common-ts');
4 |
--------------------------------------------------------------------------------
/packages/spec/.eslintrc.react-ts.js:
--------------------------------------------------------------------------------
1 | const { getESLintConfig } = require('./src');
2 |
3 | module.exports = getESLintConfig('react-ts');
4 |
--------------------------------------------------------------------------------
/packages/spec/src/commitlint/react.js:
--------------------------------------------------------------------------------
1 | // commitlint config for ice and react project
2 | module.exports = {
3 | extends: 'ali',
4 | };
5 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | const { getCommitlintConfig } = require('./packages/spec/src/');
2 |
3 | module.exports = getCommitlintConfig('react');
4 |
--------------------------------------------------------------------------------
/packages/spec/src/stylelint/rax.js:
--------------------------------------------------------------------------------
1 | // https://www.npmjs.com/package/stylelint-config-ali
2 | // stylelint config for rax project
3 | module.exports = {
4 | extends: ['stylelint-config-ali'],
5 | };
6 |
--------------------------------------------------------------------------------
/packages/spec/src/stylelint/vue.js:
--------------------------------------------------------------------------------
1 | // https://www.npmjs.com/package/stylelint-config-ali
2 | // stylelint config for vue project
3 | module.exports = {
4 | extends: ['stylelint-config-ali'],
5 | };
6 |
--------------------------------------------------------------------------------
/packages/spec/src/stylelint/common.js:
--------------------------------------------------------------------------------
1 | // https://www.npmjs.com/package/stylelint-config-ali
2 | // stylelint config for common project
3 | module.exports = {
4 | extends: ['stylelint-config-ali'],
5 | };
6 |
--------------------------------------------------------------------------------
/packages/spec/src/prettier/rax.js:
--------------------------------------------------------------------------------
1 | // prettier config for rax project
2 | module.exports = {
3 | printWidth: 120,
4 | tabWidth: 2,
5 | semi: true,
6 | singleQuote: true,
7 | trailingComma: 'all',
8 | };
9 |
--------------------------------------------------------------------------------
/packages/spec/src/prettier/vue.js:
--------------------------------------------------------------------------------
1 | // prettier config for vue project
2 | module.exports = {
3 | printWidth: 120,
4 | tabWidth: 2,
5 | semi: true,
6 | singleQuote: true,
7 | trailingComma: 'all',
8 | };
9 |
--------------------------------------------------------------------------------
/packages/spec/src/stylelint/react.js:
--------------------------------------------------------------------------------
1 | // https://www.npmjs.com/package/stylelint-config-ali
2 | // stylelint config for ice and react project
3 | module.exports = {
4 | extends: ['stylelint-config-ali'],
5 | };
6 |
--------------------------------------------------------------------------------
/packages/spec/src/prettier/common.js:
--------------------------------------------------------------------------------
1 | // prettier config for common project
2 | module.exports = {
3 | printWidth: 120,
4 | tabWidth: 2,
5 | semi: true,
6 | singleQuote: true,
7 | trailingComma: 'all',
8 | };
9 |
--------------------------------------------------------------------------------
/packages/spec/src/prettier/react.js:
--------------------------------------------------------------------------------
1 | // prettier config for ice and react project
2 | module.exports = {
3 | printWidth: 120,
4 | tabWidth: 2,
5 | semi: true,
6 | singleQuote: true,
7 | trailingComma: 'all',
8 | };
9 |
--------------------------------------------------------------------------------
/packages/spec/examples/common-ts/index.ts:
--------------------------------------------------------------------------------
1 | interface AppDeveloper {
2 | name: string;
3 | id: number;
4 | }
5 |
6 | const bar = [1, 2];
7 | const one: AppDeveloper = { name: 'hello', id: 1 };
8 |
9 | const t = { s: 1 };
10 | console.log(t['s']);
11 |
--------------------------------------------------------------------------------
/packages/eslint-plugin-best-practices/src/docsUrl.js:
--------------------------------------------------------------------------------
1 | const repoUrl = 'https://github.com/ice-lab/spec/tree/master/packages/eslint-plugin-best-practices';
2 |
3 | module.exports = function docsUrl(ruleName) {
4 | return `${repoUrl}/docs/rules/${ruleName}.md`;
5 | };
6 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "esNext",
4 | "target": "es2015",
5 | "outDir": "build",
6 | "jsx": "preserve",
7 | "jsxFactory": "createElement",
8 | "moduleResolution": "node",
9 | "baseUrl": "."
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/spec/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "esNext",
4 | "target": "es2015",
5 | "outDir": "build",
6 | "jsx": "preserve",
7 | "jsxFactory": "createElement",
8 | "moduleResolution": "node",
9 | "baseUrl": "."
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/spec/examples/rax/index.jsx:
--------------------------------------------------------------------------------
1 | import { createElement } from 'rax';
2 | import Image from 'rax-image';
3 |
4 | import './index.css';
5 |
6 | const unusedVar = 1;
7 |
8 | export default (props) => {
9 | const { uri } = props;
10 | const source = { uri };
11 | return {{title}};
12 | ```
13 |
14 | ## When Not To Use It
15 |
16 | If your website only support http.
17 |
--------------------------------------------------------------------------------
/packages/eslint-plugin-best-practices/docs/rules/deps-no-resolutions.md:
--------------------------------------------------------------------------------
1 | # deps-no-resolutions
2 |
3 | It is not recommended to use resolutions to lock the version.
4 |
5 | ## Rule Details
6 |
7 | Examples of **incorrect** code for this rule:
8 |
9 | ```json
10 | {
11 | "resolutions": {
12 | "package-a": "1.0.0",
13 | "package-b": "2.0.0",
14 | "package-c": "3.5.2"
15 | }
16 | }
17 | ```
18 |
--------------------------------------------------------------------------------
/packages/spec/examples/rax-ts/index.tsx:
--------------------------------------------------------------------------------
1 | import { createElement } from 'rax';
2 | import Image from 'rax-image';
3 |
4 | import './index.css';
5 |
6 | interface LogoProps {
7 | uri: string;
8 | }
9 |
10 | const unusedVar = 1;
11 |
12 | export default (props: LogoProps) => {
13 | const { uri } = props;
14 | const source = { uri };
15 | return
hello world
; 13 | } 14 | } 15 | ``` 16 | 17 | Examples of **correct** code for this rule: 18 | 19 | ```js 20 | const App = () => { 21 | returnhello world
; 22 | }; 23 | ``` 24 | -------------------------------------------------------------------------------- /packages/spec/src/eslint/common-ts.js: -------------------------------------------------------------------------------- 1 | // https://www.npmjs.com/package/eslint-config-ali 2 | // ESlint config for common ts project 3 | module.exports = { 4 | extends: [ 5 | require.resolve('eslint-config-ali/typescript'), 6 | ], 7 | rules: { 8 | // Change error to warn 9 | 'semi': 'off', 10 | '@typescript-eslint/semi': 'warn', 11 | 'eol-last': 'warn', 12 | 'quote-props': 'warn', 13 | '@typescript-eslint/no-unused-vars': 'warn', 14 | '@typescript-eslint/dot-notation': 'off', 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /packages/spec/src/eslint/rax-ts.js: -------------------------------------------------------------------------------- 1 | // https://www.npmjs.com/package/eslint-config-ali 2 | // ESlint config for Rax TypeScript project 3 | const getRaxEslintConfig = require('../getRaxEslintConfig'); 4 | 5 | module.exports = getRaxEslintConfig({ 6 | extends: [ 7 | require.resolve('eslint-config-ali/typescript/rax'), 8 | // For some ci and jest test env, we chose require.resolve instead 'plugin:@iceworks/best-practices/rax-ts' 9 | require.resolve('@iceworks/eslint-plugin-best-practices/src/configs/rax-ts'), 10 | ], 11 | }); 12 | -------------------------------------------------------------------------------- /packages/spec/src/eslint/vue-ts.js: -------------------------------------------------------------------------------- 1 | // https://www.npmjs.com/package/eslint-config-ali 2 | // ESlint config for Vue TypeScript project 3 | module.exports = { 4 | extends: [ 5 | require.resolve('eslint-config-ali/typescript/vue'), 6 | ], 7 | rules: { 8 | // Change error to warn 9 | 'semi': 'off', 10 | '@typescript-eslint/semi': 'warn', 11 | 'eol-last': 'warn', 12 | 'quote-props': 'warn', 13 | '@typescript-eslint/no-unused-vars': 'warn', 14 | '@typescript-eslint/dot-notation': 'off', 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /packages/eslint-plugin-best-practices/docs/rules/deps-no-ice-scripts.md: -------------------------------------------------------------------------------- 1 | # deps-no-ice-scripts 2 | 3 | It is not recommended to use ice-scripts, the new version is ice.js. See [https://ice.work/](https://ice.work/). 4 | 5 | ## Rule Details 6 | 7 | Examples of **incorrect** code for this rule: 8 | 9 | ```json 10 | { 11 | "devDependencies": { 12 | "ice.js": "^1.0.0" 13 | } 14 | } 15 | ``` 16 | 17 | Examples of **correct** code for this rule: 18 | 19 | ```json 20 | { 21 | "devDependencies": { 22 | "ice-script": "^1.0.0" 23 | } 24 | } 25 | ``` 26 | -------------------------------------------------------------------------------- /packages/eslint-plugin-best-practices/src/configs/react-ts.js: -------------------------------------------------------------------------------- 1 | const commonConfig = require('./common'); 2 | const { deepmerge } = require('@iceworks/spec'); 3 | 4 | module.exports = deepmerge(commonConfig, { 5 | rules: { 6 | 'max-len': ['error', { code: 150 }], 7 | 'max-lines': ['warn', { max: 500 }], 8 | 'no-plusplus': 'off', 9 | 'no-return-await': 'off', 10 | 'react/prop-types': 'off', 11 | 'semi': 'off', 12 | '@typescript-eslint/dot-notation': 'off', 13 | '@typescript-eslint/semi': 'warn', 14 | '@typescript-eslint/no-unused-vars': 'warn', 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /packages/spec/src/deepmerge.js: -------------------------------------------------------------------------------- 1 | module.exports = function (target, source) { 2 | // deep clone 3 | const newObj = JSON.parse(JSON.stringify(target)); 4 | 5 | Object.keys(source).forEach((key) => { 6 | const type = Object.prototype.toString.call(source[key]); 7 | 8 | if (type === '[object Array]') { 9 | newObj[key] = [...(target[key] || []), ...source[key]]; 10 | } else if (type === '[object Object]') { 11 | newObj[key] = { ...(target[key] || {}), ...source[key] }; 12 | } else { 13 | newObj[key] = source[key]; 14 | } 15 | }); 16 | 17 | return newObj; 18 | }; 19 | -------------------------------------------------------------------------------- /.github/workflows/auto-publisher.yml: -------------------------------------------------------------------------------- 1 | name: Auto Publisher 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build-and-publish: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v1 13 | - uses: actions/setup-node@v1 14 | with: 15 | node-version: 10 16 | registry-url: https://registry.npmjs.org/ 17 | - run: | 18 | npm i 19 | npm run setup 20 | npm run publish 21 | env: 22 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 23 | REGISTRY: https://registry.npmjs.org 24 | -------------------------------------------------------------------------------- /packages/spec/examples/vue-ts/index.vue: -------------------------------------------------------------------------------- 1 | 2 |{{welcomeMsg}}
3 | 4 | 5 | 23 | 24 | 33 | -------------------------------------------------------------------------------- /packages/eslint-plugin-best-practices/docs/rules/no-lowercase-component-name.md: -------------------------------------------------------------------------------- 1 | # no-lowercase-component-name 2 | 3 | It is not recommended to name components in lower case. 4 | See: https://github.com/airbnb/javascript/tree/master/react#naming 5 | 6 | ## Rule Details 7 | 8 | Examples of **incorrect** code for this rule: 9 | 10 | ```jsx 11 | // src/components/app/index.jsx 12 | const app = () => { 13 | returnhello world
; 14 | }; 15 | 16 | export default app; 17 | ``` 18 | 19 | Examples of **correct** code for this rule: 20 | 21 | ```jsx 22 | // src/components/App/index.jsx 23 | const App = () => { 24 | returnhello world
; 25 | }; 26 | 27 | export default App; 28 | ``` 29 | -------------------------------------------------------------------------------- /packages/eslint-plugin-best-practices/docs/rules/recommend-polyfill.md: -------------------------------------------------------------------------------- 1 | # recommend-polyfill 2 | 3 | With the updating of operating systems and browsers, more and more new features are beginning to be supported. 4 | While bringing convenience to front-end development and technical solution design, some compatibility issues will also arise. 5 | 6 | Recommend API which not supported in iOS 9 to add polyfill file. 7 | 8 | ## Rule Details 9 | 10 | Examples of **incorrect** code for this rule: 11 | 12 | ```js 13 | navigator.sendBeacon('xxxx'); 14 | ``` 15 | 16 | navigator.sendBeacon is not support in iOS 9. 17 | 18 | Examples of **correct** code for this rule: 19 | 20 | ```js 21 | Array.from('xxx'); 22 | ``` 23 | -------------------------------------------------------------------------------- /packages/spec/examples/react/index.module.scss: -------------------------------------------------------------------------------- 1 | @import '~@alifd/next/variables.scss'; 2 | 3 | .PageHeader { 4 | margin-top: -$shell-dark-content-paddingTop; 5 | margin-right: -$shell-dark-content-paddingLeft; 6 | margin-bottom: 0; 7 | margin-left: -$shell-dark-content-paddingLeft; 8 | padding: $shell-dark-content-paddingTop $shell-dark-content-paddingLeft; 9 | background-color: $color-white; 10 | } 11 | 12 | .Title { 13 | display: block; 14 | color: $color-text1-4; 15 | font-weight: $font-weight-3; 16 | font-size: $font-size-title; 17 | line-height: 1.5; 18 | } 19 | 20 | .Description { 21 | display: block; 22 | color: $color-text1-3; 23 | font-size: $font-size-body-2; 24 | line-height: 1.5; 25 | } 26 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | const { getESLintConfig } = require('./packages/spec/src/'); 2 | 3 | // script: eslint --ext .js,.jsx,.tsx,.ts ./ --resolve-plugins-relative-to ./packages/spec 4 | module.exports = getESLintConfig('react', { 5 | rules: { 6 | // For test file. This project is no UI project, not use line height. 7 | '@iceworks/best-practices/recommend-add-line-height-unit': 'off', 8 | }, 9 | parserOptions: { 10 | babelOptions: { 11 | // @babel/preset-react is not a ESLint plugin, resolve-plugins-relative-to is not work 12 | // fix Parsing error: Cannot find module '@babel/preset-react' 13 | // set presets path to ./packages/spec 14 | presets: [require.resolve('@babel/preset-react', { paths: ['./packages/spec'] })], 15 | }, 16 | }, 17 | }); 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "🐛 Bug Report" 3 | about: 提交一个可复现的缺陷/漏洞 4 | labels: 'bug' 5 | 6 | --- 7 | 8 | 11 | 12 | 包名: 13 | 14 | ## 您的本地环境信息/Your local environment information 15 | 16 | * 操作系统及其版本/System and Version: 17 | * Node version: 18 | * 问题 npm 包 version: 19 | 20 | ## 您遇到的问题及复现步骤/What are your problems and how to reproduce them 21 | 22 | 26 | 27 | ## 您期待的正确结果/The right result you're looking forward to 28 | 29 | 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "🚀 Feature Request" 3 | about: 提一个需求 4 | labels: 'enhancement' 5 | 6 | --- 7 | 8 | ### 问题描述/Problem Description 9 | 10 | 14 | 15 | ### 建议的方案/Proposal 16 | 17 | 21 | 22 | ### 备选的方案/Alternatives 23 | 24 | 28 | 29 | ### 附加信息/Other Information 30 | 31 | 35 | -------------------------------------------------------------------------------- /packages/eslint-plugin-best-practices/test/src/rules/no-js-in-ts-project.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const rule = require('../../../src/rules/no-js-in-ts-project'); 4 | const { RuleTester } = require('eslint'); 5 | 6 | const ruleTester = new RuleTester(); 7 | 8 | ruleTester.run('no-js-in-ts-project', rule, { 9 | valid: [ 10 | { 11 | filename: 'index.ts', 12 | code: '', 13 | }, 14 | { 15 | filename: '.stylelintrc.js', 16 | code: '', 17 | }, 18 | { 19 | filename: 'home.ts', 20 | code: '', 21 | }, 22 | ], 23 | 24 | invalid: [ 25 | { 26 | filename: 'home.js', 27 | code: '', 28 | errors: [ 29 | { 30 | message: "The 'home.js' is not recommended in TS project", 31 | }, 32 | ], 33 | }, 34 | ], 35 | }); 36 | -------------------------------------------------------------------------------- /packages/eslint-plugin-best-practices/docs/rules/recommend-add-line-height-unit.md: -------------------------------------------------------------------------------- 1 | # recommend-add-line-height-unit 2 | 3 | Recommended to add unit for line-height which is more than 5. 4 | 5 | ## Rule Details 6 | 7 | Examples of **incorrect** code for this rule: 8 | 9 | ```js 10 | class App extends React.Component { 11 | render() { 12 | returnhello world
; 13 | } 14 | } 15 | ``` 16 | 17 | ```css 18 | .text { 19 | line-height: 10; 20 | } 21 | ``` 22 | 23 | Should add unit for line-height, like `line-height: 10px;`; 24 | 25 | Examples of **correct** code for this rule: 26 | 27 | ```js 28 | class App extends React.Component { 29 | render() { 30 | returnhello world
; 31 | } 32 | } 33 | ``` 34 | 35 | ```css 36 | .text { 37 | line-height: 2; 38 | } 39 | ``` 40 | -------------------------------------------------------------------------------- /packages/eslint-plugin-best-practices/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@iceworks/eslint-plugin-best-practices", 3 | "version": "0.2.11", 4 | "description": "Iceworks best practices eslint plugin.", 5 | "files": [ 6 | "docs/", 7 | "src/" 8 | ], 9 | "keywords": [ 10 | "iceworks", 11 | "best practices", 12 | "eslint", 13 | "eslintplugin", 14 | "eslint-plugin" 15 | ], 16 | "main": "src/index.js", 17 | "publishConfig": { 18 | "access": "public" 19 | }, 20 | "dependencies": { 21 | "@iceworks/spec": "^1.0.0", 22 | "@mdn/browser-compat-data": "^4.0.5", 23 | "fs-extra": "^9.0.1", 24 | "glob": "^7.1.6", 25 | "line-column": "^1.0.2", 26 | "path-to-regexp": "^6.1.0", 27 | "require-all": "^3.0.0", 28 | "semver": "^7.3.2" 29 | }, 30 | "devDependencies": { 31 | "eslint": "^7.22.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/eslint-plugin-best-practices/test/src/rules/deps-no-resolutions.test.js: -------------------------------------------------------------------------------- 1 | const rule = require('../../../src/rules/deps-no-resolutions'); 2 | const { RuleTester } = require('eslint'); 3 | 4 | const ruleTester = new RuleTester(); 5 | ruleTester.run('deps-no-resolutions', rule, { 6 | valid: [ 7 | { 8 | filename: 'package.json', 9 | code: `module.exports = ${JSON.stringify({ name: 'test' })}`, 10 | }, 11 | { 12 | filename: 'package.js', 13 | code: 'var t = 1', 14 | }, 15 | ], 16 | 17 | invalid: [ 18 | { 19 | filename: 'package.json', 20 | code: `module.exports = ${JSON.stringify({ resolutions: { 'ice.js': '1.0.0' } })}`, 21 | errors: [ 22 | { 23 | message: 'It is not recommended to use resolutions to lock the version', 24 | }, 25 | ], 26 | }, 27 | ], 28 | }); 29 | -------------------------------------------------------------------------------- /packages/eslint-plugin-best-practices/test/src/rules/recommend-update-rax.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const rule = require('../../../src/rules/recommend-update-rax'); 4 | const { RuleTester } = require('eslint'); 5 | 6 | const ruleTester = new RuleTester(); 7 | 8 | ruleTester.run('recommend-update-rax', rule, { 9 | valid: [ 10 | { 11 | filename: 'package.json', 12 | code: `module.exports = ${JSON.stringify({ dependencies: { rax: '^1.1.0' } })}`, 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({ dependencies: { rax: '^0.6.0' } })}`, 24 | errors: [ 25 | { 26 | message: 'Rax version < 1.0 , recommend to update Rax', 27 | }, 28 | ], 29 | }, 30 | ], 31 | }); 32 | -------------------------------------------------------------------------------- /packages/eslint-plugin-best-practices/test/src/rules/deps-no-ice-scripts.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const rule = require('../../../src/rules/deps-no-ice-scripts'); 4 | const { RuleTester } = require('eslint'); 5 | 6 | const ruleTester = new RuleTester(); 7 | 8 | ruleTester.run('deps-no-ice-scripts', rule, { 9 | valid: [ 10 | { 11 | filename: 'package.json', 12 | code: `module.exports = ${JSON.stringify({ devDependencies: { 'ice.js': '^1.0.0' } })}`, 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({ devDependencies: { 'ice-scripts': '^1.0.0' } })}`, 24 | errors: [ 25 | { 26 | message: 'It is not recommended to use ice-scripts, the new version is ice.js', 27 | }, 28 | ], 29 | }, 30 | ], 31 | }); 32 | -------------------------------------------------------------------------------- /packages/eslint-plugin-best-practices/src/rules/deps-no-resolutions.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const docsUrl = require('../docsUrl'); 3 | 4 | const RULE_NAME = 'deps-no-resolutions'; 5 | 6 | module.exports = { 7 | name: RULE_NAME, 8 | meta: { 9 | type: 'suggestion', 10 | docs: { 11 | url: docsUrl(RULE_NAME), 12 | }, 13 | fixable: null, 14 | messages: { 15 | depsNoResolutions: 'It is not recommended to use resolutions to lock the version', 16 | }, 17 | }, 18 | 19 | create(context) { 20 | if (path.basename(context.getFilename()) !== 'package.json') { 21 | return {}; 22 | } 23 | return { 24 | Property: function handleRequires(node) { 25 | if (node.key && node.key.value && node.key.value === 'resolutions') { 26 | context.report({ 27 | node, 28 | messageId: 'depsNoResolutions', 29 | }); 30 | } 31 | }, 32 | }; 33 | }, 34 | }; 35 | -------------------------------------------------------------------------------- /packages/eslint-plugin-best-practices/test/src/rules/no-broad-semantic-versioning.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const rule = require('../../../src/rules/no-broad-semantic-versioning'); 4 | const { RuleTester } = require('eslint'); 5 | 6 | const ruleTester = new RuleTester(); 7 | 8 | ruleTester.run('no-broad-semantic-versioning', rule, { 9 | valid: [ 10 | { 11 | filename: 'package.json', 12 | code: `module.exports = ${JSON.stringify({ devDependencies: { 'ice.js': '^1.0.0' } })}`, 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({ devDependencies: { 'ice.js': '*' } })}`, 24 | errors: [ 25 | { 26 | message: 'The "ice.js" is not recommended to use "*", and it is recommend using "^1.0.0"', 27 | }, 28 | ], 29 | }, 30 | ], 31 | }); 32 | -------------------------------------------------------------------------------- /packages/eslint-plugin-best-practices/test/src/rules/deps-no-router-library.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const rule = require('../../../src/rules/deps-no-router-library'); 4 | const { RuleTester } = require('eslint'); 5 | 6 | const ruleTester = new RuleTester(); 7 | 8 | ruleTester.run('deps-no-router-library', rule, { 9 | valid: [ 10 | { 11 | filename: 'package.json', 12 | code: `module.exports = ${JSON.stringify({ devDependencies: { 'react-router': '^5.2.0' } })}`, 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: { 'ice.js': '^1.0.0', 'react-router': '^5.2.0' }, 25 | })}`, 26 | errors: [ 27 | { 28 | message: 'It is not recommended to directly rely on routing libraries "react-router"', 29 | }, 30 | ], 31 | }, 32 | ], 33 | }); 34 | -------------------------------------------------------------------------------- /packages/spec/examples/react/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Breadcrumb, Box, Typography } from '@alifd/next'; 3 | import styles from './index.module.scss'; 4 | 5 | const unusedVar = 1; 6 | 7 | const PageHeader = (props) => { 8 | const { breadcrumbs, title, description, ...others } = props; 9 | return ( 10 |hello world
); 12 | }; 13 | `, 14 | parserOptions: { 15 | ecmaVersion: 6, 16 | sourceType: 'module', 17 | ecmaFeatures: { 18 | jsx: true, 19 | }, 20 | }, 21 | }, 22 | ], 23 | 24 | invalid: [ 25 | { 26 | code: ` 27 | class App extends React.Component { 28 | render() { 29 | return (hello world
); 30 | } 31 | }; 32 | `, 33 | parserOptions: { 34 | ecmaVersion: 6, 35 | sourceType: 'module', 36 | ecmaFeatures: { 37 | jsx: true, 38 | }, 39 | }, 40 | errors: [ 41 | { 42 | message: "It is not recommended to use class component 'App'", 43 | }, 44 | ], 45 | }, 46 | ], 47 | }); 48 | -------------------------------------------------------------------------------- /packages/eslint-plugin-best-practices/test/src/rules/recommend-add-line-height-unit.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const rule = require('../../../src/rules/recommend-add-line-height-unit'); 4 | const { RuleTester } = require('eslint'); 5 | 6 | const ruleTester = new RuleTester(); 7 | 8 | ruleTester.run('recommend-add-line-height-unit', rule, { 9 | valid: [ 10 | { 11 | filename: 'index.js', 12 | code: 'var style = { position: "relative", lineHeight: "30px" };', 13 | }, 14 | ], 15 | 16 | invalid: [ 17 | { 18 | filename: 'index.js', 19 | code: 'var style = { position: "relative", lineHeight: 30 };', 20 | errors: [ 21 | { 22 | message: 'Please add unit (like px, rpx ...) for "lineHeight: 30 " in "index.js".', 23 | }, 24 | ], 25 | }, 26 | { 27 | filename: 'index.jsx', 28 | code: 'hello world
', 29 | parserOptions: { 30 | ecmaFeatures: { 31 | jsx: true, 32 | }, 33 | }, 34 | errors: [ 35 | { 36 | message: 'Please add unit (like px, rpx ...) for "lineHeight: 10 " in "index.jsx".', 37 | }, 38 | ], 39 | }, 40 | ], 41 | }); 42 | -------------------------------------------------------------------------------- /packages/spec/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # changelog 2 | 3 | ## 1.6.0 4 | 5 | - Feat update peerDependencies ESLint version to `>=7.5.0`. 6 | 7 | ## 1.5.0 8 | 9 | - Feat update eslint-config-ali, babel and typescript dependencies to support ESLint@8.x. 10 | - Update stylistic rules `dot-notation` and `@typescript-eslint/dot-notation` from error to off. 11 | 12 | ## 1.4.2 13 | 14 | - Fix stylelint extends config merge error. 15 | 16 | ## 1.4.1 17 | 18 | - Fix build.json targets config not exist error. 19 | 20 | ## 1.4.0 21 | 22 | - Update stylistic rules `semi` `eol-last` and `quote-props` from error to warn. 23 | - Support auto add specific rules ([eslint-plugin-rax-compile-time-miniapp](https://www.npmjs.com/package/eslint-plugin-rax-compile-time-miniapp)) for rax compile-time miniapp. 24 | 25 | ## 1.3.2 26 | 27 | - Add [eslint-plugin-jsx-plus](https://github.com/jsx-plus/eslint-plugin-jsx-plus) support Rax JSX+. 28 | 29 | ## 1.3.1 30 | 31 | - Add stylelint peerDependencies 32 | 33 | ## 1.3.0 34 | 35 | - Update ESLint from 6.x to 7.x 36 | - Update eslint-config-ali from 11.x to 12.x 37 | 38 | ## 1.2.0 39 | 40 | - ESlint config support `common` and `common-ts` rules. 41 | - Commitlint, prettier, stylelint config support `common` rules. 42 | 43 | ## 1.1.0 44 | 45 | - Support `vue` and `vue-ts` project. 46 | -------------------------------------------------------------------------------- /packages/eslint-plugin-best-practices/src/rules/recommend-functional-component.js: -------------------------------------------------------------------------------- 1 | const docsUrl = require('../docsUrl'); 2 | 3 | const RULE_NAME = 'recommend-functional-component'; 4 | 5 | module.exports = { 6 | name: RULE_NAME, 7 | meta: { 8 | docs: { 9 | url: docsUrl(RULE_NAME), 10 | }, 11 | fixable: null, 12 | messages: { 13 | // eslint-disable-next-line 14 | recommendFunctionalComponent: "It is not recommended to use class component '{{name}}'", 15 | }, 16 | }, 17 | 18 | create(context) { 19 | return { 20 | ClassDeclaration: function handleRequires(node) { 21 | const { name } = node.id || {}; 22 | let superName = ''; 23 | if (node.superClass) { 24 | if (node.superClass.name) { 25 | // class xxx extends Component 26 | superName = node.superClass.name; 27 | } else if (node.superClass.property && node.superClass.property.name) { 28 | // class xxx extends React.Component 29 | superName = node.superClass.property.name; 30 | } 31 | } 32 | 33 | // Class Component 34 | if (superName === 'Component') { 35 | context.report({ 36 | node, 37 | messageId: 'recommendFunctionalComponent', 38 | data: { 39 | name: name || 'Anonymous', 40 | }, 41 | }); 42 | } 43 | }, 44 | }; 45 | }, 46 | }; 47 | -------------------------------------------------------------------------------- /packages/eslint-plugin-best-practices/src/rules/deps-no-ice-scripts.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const docsUrl = require('../docsUrl'); 3 | 4 | const RULE_NAME = 'deps-no-ice-scripts'; 5 | 6 | module.exports = { 7 | name: RULE_NAME, 8 | meta: { 9 | type: 'suggestion', 10 | docs: { 11 | url: docsUrl(RULE_NAME), 12 | }, 13 | fixable: 'code', 14 | messages: { 15 | depsNoResolutions: 'It is not recommended to use ice-scripts, the new version is ice.js', 16 | }, 17 | }, 18 | 19 | create(context) { 20 | if (path.basename(context.getFilename()) !== 'package.json') { 21 | return {}; 22 | } 23 | 24 | return { 25 | Property: function handleRequires(node) { 26 | if ( 27 | node.key && 28 | node.key.value && 29 | (node.key.value === 'dependencies' || node.key.value === 'devDependencies') && 30 | node.value && 31 | node.value.properties 32 | ) { 33 | node.value.properties.forEach((property) => { 34 | if (property.key && property.key.value) { 35 | const dependencyName = property.key.value; 36 | 37 | if (dependencyName === 'ice-scripts') { 38 | context.report({ 39 | loc: property.loc, 40 | messageId: 'depsNoResolutions', 41 | }); 42 | } 43 | } 44 | }); 45 | } 46 | }, 47 | }; 48 | }, 49 | }; 50 | -------------------------------------------------------------------------------- /packages/eslint-plugin-best-practices/src/rules/no-js-in-ts-project.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const docsUrl = require('../docsUrl'); 3 | 4 | const RULE_NAME = 'no-js-in-ts-project'; 5 | 6 | const TS_REG = /\.tsx?$/; 7 | const JS_REG = /\.jsx?$/; 8 | 9 | const WHITE_LIST = ['commitlint.config.js', 'eslintrc.js', 'prettierrc.js', 'stylelintrc.js']; 10 | 11 | const WHITE_LIST_REG = new RegExp(`(${WHITE_LIST.join('|')})$`); 12 | 13 | let isTSProject = false; 14 | 15 | module.exports = { 16 | name: RULE_NAME, 17 | meta: { 18 | type: 'suggestion', 19 | docs: { 20 | url: docsUrl(RULE_NAME), 21 | }, 22 | fixable: null, 23 | messages: { 24 | noJSInTSProject: "The '{{fileName}}' is not recommended in TS project", 25 | }, 26 | }, 27 | 28 | create(context) { 29 | const fileName = context.getFilename(); 30 | const extName = path.extname(fileName); 31 | if (TS_REG.test(extName)) { 32 | isTSProject = true; 33 | } 34 | 35 | if (isTSProject && !WHITE_LIST_REG.test(fileName) && JS_REG.test(extName)) { 36 | context.report({ 37 | loc: { 38 | start: { 39 | line: 0, 40 | column: 0, 41 | }, 42 | end: { 43 | line: 0, 44 | column: 0, 45 | }, 46 | }, 47 | messageId: 'noJSInTSProject', 48 | data: { 49 | fileName, 50 | }, 51 | }); 52 | } 53 | 54 | // Necessary 55 | return {}; 56 | }, 57 | }; 58 | -------------------------------------------------------------------------------- /packages/eslint-plugin-best-practices/src/rules/recommend-update-rax.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const semver = require('semver'); 3 | const docsUrl = require('../docsUrl'); 4 | 5 | const RULE_NAME = 'recommend-update-rax'; 6 | 7 | module.exports = { 8 | name: RULE_NAME, 9 | meta: { 10 | type: 'suggestion', 11 | docs: { 12 | url: docsUrl(RULE_NAME), 13 | }, 14 | fixable: null, 15 | messages: { 16 | depsRecommendUpdateRax: 'Rax version < 1.0 , recommend to update Rax', 17 | }, 18 | }, 19 | 20 | create(context) { 21 | if (path.basename(context.getFilename()) !== 'package.json') { 22 | return {}; 23 | } 24 | 25 | return { 26 | Property: function handleRequires(node) { 27 | if ( 28 | node.key && 29 | node.key.value && 30 | (node.key.value === 'dependencies' || node.key.value === 'devDependencies') && 31 | node.value && 32 | node.value.properties 33 | ) { 34 | node.value.properties.forEach((property) => { 35 | if (property.key && property.key.value) { 36 | const dependencyName = property.key.value; 37 | 38 | if (dependencyName === 'rax' && semver.satisfies('0.6.7', property.value.value)) { 39 | context.report({ 40 | loc: property.loc, 41 | messageId: 'depsRecommendUpdateRax', 42 | }); 43 | } 44 | } 45 | }); 46 | } 47 | }, 48 | }; 49 | }, 50 | }; 51 | -------------------------------------------------------------------------------- /packages/spec/src/getRaxEslintConfig.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const JSON5 = require('json5'); 4 | const deepmerge = require('./deepmerge'); 5 | 6 | // Add specific rules for rax compile-time miniapp 7 | module.exports = function (config) { 8 | try { 9 | const buildConfigFilePath = path.join(process.cwd(), 'build.json'); 10 | 11 | if (fs.existsSync(buildConfigFilePath)) { 12 | 13 | const buildConfig = JSON5.parse(fs.readFileSync(buildConfigFilePath, 'utf8')); 14 | 15 | const isCompileTime = (target) => ( 16 | buildConfig.targets && buildConfig.targets.find((t) => t === target) && 17 | buildConfig[target] && buildConfig[target].buildType === 'compile' 18 | ); 19 | 20 | // At present, only miniapp and wechat-miniprogram support build for compile-time 21 | if (isCompileTime('miniapp') || isCompileTime('wechat-miniprogram')) { 22 | // https://www.npmjs.com/package/eslint-plugin-rax-compile-time-miniapp 23 | const { configs } = require('eslint-plugin-rax-compile-time-miniapp'); 24 | 25 | config.plugins = config.plugins || []; 26 | 27 | config.plugins.push('rax-compile-time-miniapp'); 28 | // For some ci and jest test env, we chose set config instead 'plugin:rax-compile-time-miniapp/recommended' 29 | config.rules = deepmerge(config.rules || {}, configs.recommended.rules); 30 | } 31 | } 32 | } catch (e) { 33 | console.log('Add specific rules for rax compile-time miniapp failed!', e); 34 | } 35 | return config; 36 | }; 37 | -------------------------------------------------------------------------------- /packages/eslint-plugin-best-practices/test/src/rules/no-lowercase-component-name.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const rule = require('../../../src/rules/no-lowercase-component-name'); 4 | const { RuleTester } = require('eslint'); 5 | 6 | const ruleTester = new RuleTester(); 7 | 8 | ruleTester.run('no-lowercase-component-name', rule, { 9 | valid: [ 10 | { 11 | filename: 'pages/App/index.jsx', 12 | code: ` 13 | const App = () => { 14 | return (hello world
); 15 | }; 16 | 17 | export default App; 18 | `, 19 | parserOptions: { 20 | ecmaVersion: 6, 21 | sourceType: 'module', 22 | ecmaFeatures: { 23 | jsx: true, 24 | }, 25 | }, 26 | }, 27 | ], 28 | 29 | invalid: [ 30 | { 31 | filename: 'src/components/app/index.jsx', 32 | code: '', 33 | errors: [ 34 | { 35 | message: "It is not recommended to name components in lower case 'app'", 36 | }, 37 | ], 38 | }, 39 | { 40 | filename: 'src/pages/App/index.jsx', 41 | code: ` 42 | const app = () => { 43 | return (hello world
); 44 | }; 45 | 46 | export default app; 47 | `, 48 | parserOptions: { 49 | ecmaVersion: 6, 50 | sourceType: 'module', 51 | ecmaFeatures: { 52 | jsx: true, 53 | }, 54 | }, 55 | errors: [ 56 | { 57 | message: "It is not recommended to name components in lower case 'app'", 58 | }, 59 | ], 60 | }, 61 | ], 62 | }); 63 | -------------------------------------------------------------------------------- /packages/spec/src/index.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const deepmerge = require('./deepmerge'); 3 | const requireAll = require('require-all'); 4 | 5 | const eslint = requireAll({ 6 | dirname: path.resolve(__dirname, 'eslint'), 7 | }); 8 | const stylelint = requireAll({ 9 | dirname: path.resolve(__dirname, 'stylelint'), 10 | }); 11 | const prettier = requireAll({ 12 | dirname: path.resolve(__dirname, 'prettier'), 13 | }); 14 | const commitlint = requireAll({ 15 | dirname: path.resolve(__dirname, 'commitlint'), 16 | }); 17 | 18 | function getConfig(configs, rule, customConfig) { 19 | if (!configs[rule]) { 20 | throw new Error(`Rule '${rule}' not Support!`); 21 | } 22 | 23 | return deepmerge(configs[rule], customConfig || {}); 24 | } 25 | 26 | // ESLint 27 | exports.getESLintConfig = function (rule, customConfig) { 28 | return getConfig(eslint, rule, customConfig); 29 | }; 30 | 31 | // stylelint 32 | exports.getStylelintConfig = function (rule, customConfig) { 33 | return getConfig(stylelint, rule, customConfig); 34 | }; 35 | 36 | // prettier 37 | exports.getPrettierConfig = function (rule, customConfig) { 38 | return getConfig(prettier, rule, customConfig); 39 | }; 40 | 41 | // commitlint 42 | exports.getCommitlintConfig = function (rule, customConfig) { 43 | return getConfig(commitlint, rule, customConfig); 44 | }; 45 | 46 | // deepmerge for lint rules 47 | // Such as deepmerge({ rules: { 'comma-dangle': [1, 'never'] } }, { rules: { 'comma-dangle': [2, 'always'] } }) 48 | // Get { rules: { 'comma-dangle': [2, 'always'] } Not { rules: { 'comma-dangle': [1, 'never', 2, 'always'] } 49 | exports.deepmerge = deepmerge; 50 | -------------------------------------------------------------------------------- /packages/spec/examples/react-ts/index.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | position: relative; 3 | } 4 | 5 | .actionBar { 6 | display: flex; 7 | align-items: center; 8 | justify-content: space-between; 9 | margin-bottom: 20px; 10 | } 11 | 12 | .buttonGroup { 13 | :global { 14 | .next-btn { 15 | margin-right: 12px; 16 | } 17 | } 18 | } 19 | 20 | .rightButtonGroup { 21 | :global { 22 | .next-btn { 23 | margin-left: 12px; 24 | } 25 | } 26 | } 27 | 28 | .columnSortPanel { 29 | :global { 30 | background: #fff; 31 | padding: 20px; 32 | border-radius: $corner-1; 33 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.5); 34 | max-height: 500px; 35 | overflow-y: scroll; 36 | width: 180px; 37 | 38 | .sort-item-container { 39 | width: 100%; 40 | } 41 | 42 | .sort-item { 43 | display: flex; 44 | align-items: center; 45 | justify-content: space-between; 46 | margin-bottom: 11px; 47 | 48 | .next-checkbox-wrapper.sort-checkbox { 49 | white-space: nowrap; 50 | max-width: calc(100% - 16px - 20px); 51 | 52 | .next-checkbox-label { 53 | white-space: nowrap; 54 | text-overflow: ellipsis; 55 | overflow: hidden; 56 | } 57 | } 58 | 59 | .column-handle { 60 | display: block; 61 | visibility: hidden; 62 | margin-left: 20px; 63 | } 64 | 65 | &:hover { 66 | .column-handle { 67 | visibility: initial; 68 | cursor: move; 69 | } 70 | } 71 | } 72 | 73 | .sort-item-children { 74 | margin-left: 18px; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /packages/eslint-plugin-best-practices/src/configs/common.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: ['@iceworks/best-practices'], 3 | overrides: [ 4 | { 5 | files: ['package.json'], 6 | processor: '@iceworks/best-practices/.json', 7 | rules: { 8 | quotes: 'off', 9 | semi: 'off', 10 | 'eol-last': 'off', 11 | 'quote-props': 'off', 12 | 'comma-dangle': 'off', 13 | '@iceworks/best-practices/deps-no-ice-scripts': 'warn', 14 | '@iceworks/best-practices/deps-no-resolutions': 'warn', 15 | '@iceworks/best-practices/deps-no-router-library': 'warn', 16 | '@iceworks/best-practices/no-broad-semantic-versioning': 'error', 17 | '@iceworks/best-practices/recommend-update-rax': 'warn', 18 | }, 19 | }, 20 | { 21 | files: [ 22 | '**/__tests__/*.{j,t}s?(x)', 23 | '**/tests/*.{j,t}s?(x)', 24 | '**/test/*.{j,t}s?(x)', 25 | ], 26 | env: { jest: true }, 27 | }, 28 | ], 29 | rules: { 30 | 'dot-notation': 'off', 31 | 'max-len': ['warn', 120, 2, { 32 | ignoreUrls: true, 33 | ignoreComments: false, 34 | ignoreRegExpLiterals: true, 35 | ignoreStrings: true, 36 | ignoreTemplateLiterals: true, 37 | }], 38 | 'semi': 'warn', 39 | 'eol-last': 'warn', 40 | 'quote-props': 'warn', 41 | '@iceworks/best-practices/no-http-url': 'warn', 42 | '@iceworks/best-practices/no-js-in-ts-project': 'warn', 43 | '@iceworks/best-practices/no-lowercase-component-name': 'warn', 44 | '@iceworks/best-practices/recommend-add-line-height-unit': 'error', 45 | '@iceworks/best-practices/recommend-functional-component': 'warn', 46 | '@iceworks/best-practices/recommend-polyfill': 'warn', 47 | }, 48 | }; 49 | -------------------------------------------------------------------------------- /packages/spec/test/index.test.js: -------------------------------------------------------------------------------- 1 | const { getESLintConfig, getStylelintConfig, getPrettierConfig } = require('../src'); 2 | 3 | it('getESLintConfig set one rule should be replaced', () => { 4 | const result = getESLintConfig('react', { 5 | rules: { 6 | 'comma-dangle': [1, 'never'], 7 | }, 8 | }); 9 | 10 | expect(result.rules['comma-dangle']).toEqual([1, 'never']); 11 | }); 12 | 13 | it('getESLintConfig parserOptions should be merged', () => { 14 | const result = getESLintConfig('rax', { 15 | parserOptions: { 16 | ecmaVersion: 2017, 17 | ecmaFeatures: { 18 | js: true, 19 | }, 20 | }, 21 | }); 22 | 23 | expect(result.parserOptions.ecmaVersion).toEqual(2017); 24 | expect(result.parserOptions.ecmaFeatures).toEqual({ 25 | js: true, 26 | }); 27 | expect(result.parserOptions.ecmaFeatures.jsx).toEqual(undefined); 28 | }); 29 | 30 | it('getStylelintConfig rules should be merged', () => { 31 | const result = getStylelintConfig('react', { 32 | rules: { 33 | 'block-no-empty': null, 34 | }, 35 | }); 36 | 37 | expect(result.rules['block-no-empty']).toEqual(null); 38 | }); 39 | 40 | it('getPrettierConfig tabWidth should be replaced', () => { 41 | const result = getPrettierConfig('react', { 42 | tabWidth: 4, 43 | }); 44 | 45 | expect(result.tabWidth).toEqual(4); 46 | }); 47 | 48 | it('new attributes should be merged', () => { 49 | const result = getESLintConfig('react', { 50 | newAttribute1: ['xxxx-val1'], 51 | newAttribute2: { a: 'xxxx-val2' }, 52 | newAttribute3: 'xxxx-val3', 53 | }); 54 | 55 | expect(result.newAttribute1[0]).toEqual('xxxx-val1'); 56 | expect(result.newAttribute2.a).toEqual('xxxx-val2'); 57 | expect(result.newAttribute3).toEqual('xxxx-val3'); 58 | }); 59 | -------------------------------------------------------------------------------- /packages/eslint-plugin-best-practices/src/rules/deps-no-router-library.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const docsUrl = require('../docsUrl'); 3 | 4 | const RULE_NAME = 'deps-no-router-library'; 5 | 6 | module.exports = { 7 | name: RULE_NAME, 8 | meta: { 9 | type: 'suggestion', 10 | docs: { 11 | url: docsUrl(RULE_NAME), 12 | }, 13 | fixable: null, 14 | messages: { 15 | depsNoRouterLibrary: 'It is not recommended to directly rely on routing libraries "{{name}}"', 16 | }, 17 | }, 18 | 19 | create(context) { 20 | const sourceCode = context.getSourceCode(); 21 | const sourceCodeText = sourceCode.getText(); 22 | if ( 23 | path.basename(context.getFilename()) !== 'package.json' || 24 | (sourceCodeText.indexOf('ice.js') === -1 && sourceCodeText.indexOf('rax-app') === -1) 25 | ) { 26 | return {}; 27 | } 28 | 29 | const routerLibs = ['react-router', 'vue-router', 'rax-use-router']; 30 | 31 | return { 32 | Property: function handleRequires(node) { 33 | if ( 34 | node.key && 35 | node.key.value && 36 | (node.key.value === 'dependencies' || node.key.value === 'devDependencies') && 37 | node.value && 38 | node.value.properties 39 | ) { 40 | node.value.properties.forEach((property) => { 41 | if (property.key && property.key.value) { 42 | const dependencyName = property.key.value; 43 | const routerLib = routerLibs.find((lib) => dependencyName === lib); 44 | 45 | if (routerLib) { 46 | context.report({ 47 | loc: property.loc, 48 | messageId: 'depsNoRouterLibrary', 49 | data: { 50 | name: routerLib, 51 | }, 52 | }); 53 | } 54 | } 55 | }); 56 | } 57 | }, 58 | }; 59 | }, 60 | }; 61 | -------------------------------------------------------------------------------- /packages/eslint-plugin-best-practices/src/rules/no-lowercase-component-name.js: -------------------------------------------------------------------------------- 1 | const { pathToRegexp } = require('path-to-regexp'); 2 | const docsUrl = require('../docsUrl'); 3 | 4 | const RULE_NAME = 'no-lowercase-component-name'; 5 | 6 | module.exports = { 7 | name: RULE_NAME, 8 | meta: { 9 | type: 'suggestion', 10 | docs: { 11 | url: docsUrl(RULE_NAME), 12 | }, 13 | fixable: null, 14 | messages: { 15 | noLowerCaseComponentName: "It is not recommended to name components in lower case '{{name}}'", 16 | }, 17 | }, 18 | 19 | create(context) { 20 | const COMPONENT_REG = pathToRegexp('components/:name/index.(js|jsx)', [], { start: false }); 21 | const PAGE_REG = pathToRegexp('pages/:name/index.(js|jsx)', [], { start: false }); 22 | 23 | const fileName = context.getFilename(); 24 | 25 | // component or page name (jsx component) 26 | const name = 27 | (COMPONENT_REG.exec(fileName) && COMPONENT_REG.exec(fileName)[1]) || 28 | (PAGE_REG.exec(fileName) && PAGE_REG.exec(fileName)[1]); 29 | 30 | // https://github.com/airbnb/javascript/tree/master/react#naming 31 | // Check filename 32 | if (name && name[0].toUpperCase() !== name[0]) { 33 | context.report({ 34 | loc: { 35 | start: { 36 | line: 0, 37 | column: 0, 38 | }, 39 | end: { 40 | line: 0, 41 | column: 0, 42 | }, 43 | }, 44 | messageId: 'noLowerCaseComponentName', 45 | data: { 46 | name, 47 | }, 48 | }); 49 | } 50 | 51 | return { 52 | // Check export componet name 53 | ExportDefaultDeclaration: function handleRequires(node) { 54 | if ( 55 | name && 56 | node.declaration && 57 | node.declaration.name && 58 | node.declaration.name[0].toUpperCase() !== node.declaration.name[0] 59 | ) { 60 | context.report({ 61 | node, 62 | messageId: 'noLowerCaseComponentName', 63 | data: { 64 | name: node.declaration.name, 65 | }, 66 | }); 67 | } 68 | }, 69 | }; 70 | }, 71 | }; 72 | -------------------------------------------------------------------------------- /scripts/getPackageInfos.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | const { existsSync, readdirSync, readFileSync } = require('fs'); 3 | const { join } = require('path'); 4 | const { getLatestVersion } = require('ice-npm-utils'); 5 | 6 | const TARGET_DIRECTORY = join(__dirname, '../packages'); 7 | 8 | function checkBuildSuccess(directory, mainFile) { 9 | return existsSync(join(directory, mainFile)); 10 | } 11 | 12 | function checkVersionExists(pkg, version) { 13 | return getLatestVersion(pkg) 14 | .then((latestVersion) => version === latestVersion) 15 | .catch(() => false); 16 | } 17 | 18 | module.exports = async function getPackageInfos() { 19 | const packageInfos = []; 20 | if (!existsSync(TARGET_DIRECTORY)) { 21 | console.log(`[ERROR] Directory ${TARGET_DIRECTORY} not exist!`); 22 | } else { 23 | const packageFolders = readdirSync(TARGET_DIRECTORY).filter((filename) => filename[0] !== '.'); 24 | console.log('[PUBLISH] Start check with following packages:'); 25 | await Promise.all( 26 | packageFolders.map(async (packageFolder) => { 27 | const directory = join(TARGET_DIRECTORY, packageFolder); 28 | const packageInfoPath = join(directory, 'package.json'); 29 | 30 | // Process package info. 31 | if (existsSync(packageInfoPath)) { 32 | const packageInfo = JSON.parse(readFileSync(packageInfoPath, 'utf8')); 33 | const packageName = packageInfo.name || packageFolder; 34 | 35 | console.log(`- ${packageName}`); 36 | 37 | try { 38 | packageInfos.push({ 39 | name: packageName, 40 | directory, 41 | localVersion: packageInfo.version, 42 | mainFile: packageInfo.main, 43 | // If localVersion not exist, publish it 44 | shouldPublish: 45 | checkBuildSuccess(directory, packageInfo.main) && 46 | !(await checkVersionExists(packageName, packageInfo.version)), 47 | }); 48 | } catch (e) { 49 | console.log(`[ERROR] get ${packageName} information failed: `, e); 50 | } 51 | } else { 52 | console.log(`[ERROR] ${packageFolder}'s package.json not found.`); 53 | } 54 | }), 55 | ); 56 | } 57 | return packageInfos; 58 | }; 59 | -------------------------------------------------------------------------------- /packages/eslint-plugin-best-practices/test/src/rules/recommend-polyfill.test.js: -------------------------------------------------------------------------------- 1 | const rule = require('../../../src/rules/recommend-polyfill'); 2 | const { RuleTester } = require('eslint'); 3 | 4 | const ruleTester = new RuleTester(); 5 | 6 | ruleTester.run('recommend-polyfill', rule, { 7 | valid: [ 8 | { 9 | code: 'Array.from("xxxx");', 10 | }, 11 | ], 12 | 13 | invalid: [ 14 | { 15 | code: 'navigator.sendBeacon("xxx");', 16 | errors: [ 17 | { 18 | message: 19 | '[Critical] It is recommended to add polyfill for "navigator.sendBeacon", This might be caused by a compatibility problem in "safari@9"', 20 | }, 21 | ], 22 | }, 23 | { 24 | code: '[0, 1, 2, [3, 4]].flat(2)', 25 | errors: [ 26 | { 27 | message: 28 | '[Critical] It is recommended to add polyfill for "Array.flat", This might be caused by a compatibility problem in "safari@9"', 29 | }, 30 | ], 31 | }, 32 | { 33 | code: 'Array.prototype.flat.apply([0, 1, 2, [3, 4]], 2)', 34 | errors: [ 35 | { 36 | message: 37 | '[Critical] It is recommended to add polyfill for "Array.flat", This might be caused by a compatibility problem in "safari@9"', 38 | }, 39 | ], 40 | }, 41 | { 42 | code: 'new Proxy({}, function() {});', 43 | errors: [ 44 | { 45 | message: 46 | '[Critical] It is recommended to add polyfill for "Proxy", This might be caused by a compatibility problem in "safari@9"', 47 | }, 48 | ], 49 | }, 50 | { 51 | code: 'Reflect.apply(Math.floor, undefined, [1.75]);', 52 | errors: [ 53 | { 54 | message: 55 | '[Critical] It is recommended to add polyfill for "Reflect.apply", This might be caused by a compatibility problem in "safari@9"', 56 | }, 57 | ], 58 | }, 59 | { 60 | code: ` 61 | var p = new Promise(function() {}); 62 | p.finally(function() {}); 63 | `, 64 | errors: [ 65 | { 66 | message: 67 | '[Critical] It is recommended to add polyfill for "Promise.finally", This might be caused by a compatibility problem in "iOS9"', 68 | }, 69 | ], 70 | }, 71 | ], 72 | }); 73 | -------------------------------------------------------------------------------- /packages/eslint-plugin-best-practices/src/rules/recommend-add-line-height-unit.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const lineColumn = require('line-column'); 4 | const docsUrl = require('../docsUrl'); 5 | const { glob } = require('glob'); 6 | 7 | const RULE_NAME = 'recommend-add-line-height-unit'; 8 | 9 | const CSS_REG = /line-height:([\s\d]+);/g; 10 | const JSX_REG = /lineHeight:([\s\d]+)/g; 11 | 12 | const FILE_CACHE = {}; 13 | 14 | const getCheckAndReportFn = (context) => { 15 | return (sourceCodeText, fileType, basename) => { 16 | let matched; 17 | const reg = fileType === 'jsx' ? JSX_REG : CSS_REG; 18 | // eslint-disable-next-line 19 | while ((matched = reg.exec(sourceCodeText)) !== null) { 20 | const target = matched[0]; 21 | const value = matched[1]; 22 | 23 | // line height > 5 need add unit 24 | if (Number(value) > 5) { 25 | const startPosition = lineColumn(sourceCodeText).fromIndex(matched.index); 26 | 27 | context.report({ 28 | loc: { 29 | start: { line: startPosition.line, column: startPosition.col }, 30 | end: { line: startPosition.line, column: startPosition.col + target.length }, 31 | }, 32 | messageId: 'recommendAddLineHeightUnit', 33 | data: { target, basename }, 34 | }); 35 | } 36 | } 37 | }; 38 | }; 39 | 40 | module.exports = { 41 | meta: { 42 | type: 'problem', 43 | docs: { 44 | url: docsUrl(RULE_NAME), 45 | }, 46 | fixable: null, 47 | messages: { 48 | // eslint-disable-next-line 49 | recommendAddLineHeightUnit: 'Please add unit (like px, rpx ...) for "{{target}}" in "{{basename}}".', 50 | }, 51 | }, 52 | 53 | create(context) { 54 | const checkAndReport = getCheckAndReportFn(context); 55 | 56 | const fileName = context.getFilename(); 57 | const dirname = path.dirname(fileName); 58 | 59 | const sourceCode = context.getSourceCode(); 60 | const sourceCodeText = sourceCode.getText(); 61 | 62 | checkAndReport(sourceCodeText, 'jsx', path.basename(fileName)); 63 | 64 | glob.sync(`${dirname}/*.{css,scss,less}`).forEach((cssFileName) => { 65 | if (!FILE_CACHE[cssFileName]) { 66 | FILE_CACHE[cssFileName] = true; 67 | checkAndReport(fs.readFileSync(cssFileName, 'utf-8'), 'css', path.basename(cssFileName)); 68 | } 69 | }); 70 | 71 | // Necessary 72 | return {}; 73 | }, 74 | }; 75 | -------------------------------------------------------------------------------- /packages/eslint-plugin-best-practices/src/rules/no-broad-semantic-versioning.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra'); 2 | const path = require('path'); 3 | const docsUrl = require('../docsUrl'); 4 | 5 | const RULE_NAME = 'no-broad-semantic-versioning'; 6 | 7 | module.exports = { 8 | name: RULE_NAME, 9 | meta: { 10 | type: 'problem', 11 | docs: { 12 | url: docsUrl(RULE_NAME), 13 | }, 14 | fixable: 'code', 15 | messages: { 16 | noBroadSemanticVersioning: 17 | 'The "{{dependencyName}}" is not recommended to use "{{versioning}}", and it is recommend using "{{newVersioning}}"', 18 | }, 19 | }, 20 | 21 | create(context) { 22 | if (path.basename(context.getFilename()) !== 'package.json') { 23 | return {}; 24 | } 25 | 26 | const cwd = context.getCwd(); 27 | 28 | return { 29 | Property: function handleRequires(node) { 30 | if ( 31 | node.key && 32 | node.key.value && 33 | (node.key.value === 'dependencies' || node.key.value === 'devDependencies') && 34 | node.value && 35 | node.value.properties 36 | ) { 37 | node.value.properties.forEach((property) => { 38 | if (property.key && property.key.value) { 39 | const dependencyName = property.key.value; 40 | const dependencyVersion = property.value.value; 41 | if ( 42 | // * 43 | dependencyVersion.indexOf('*') > -1 || 44 | // x.x 45 | dependencyVersion.indexOf('x') > -1 || 46 | // > x 47 | dependencyVersion.indexOf('>') > -1 48 | ) { 49 | let newVersioning = '^1.0.0'; 50 | const dependencyPackageFile = path.join( 51 | cwd, 52 | 'node_modules', 53 | dependencyName, 54 | 'package.json', 55 | ); 56 | if (fs.existsSync(dependencyPackageFile)) { 57 | const dependencyPackage = fs.readJSONSync(dependencyPackageFile); 58 | newVersioning = `^${dependencyPackage.version}`; 59 | } 60 | 61 | context.report({ 62 | loc: property.loc, 63 | messageId: 'noBroadSemanticVersioning', 64 | data: { 65 | dependencyName, 66 | versioning: dependencyVersion, 67 | newVersioning, 68 | }, 69 | }); 70 | } 71 | } 72 | }); 73 | } 74 | }, 75 | }; 76 | }, 77 | }; 78 | -------------------------------------------------------------------------------- /packages/spec/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@iceworks/spec", 3 | "version": "1.6.0", 4 | "description": "Easy to use eslint/stylelint/prettier/commitlint in rax, ice and react project.", 5 | "main": "src/index.js", 6 | "files": [ 7 | "src" 8 | ], 9 | "scripts": { 10 | "eslint-common": "eslint --no-eslintrc -c .eslintrc.common.js --ext .js,.jsx ./examples/common/", 11 | "eslint-common-ts": "eslint --no-eslintrc -c .eslintrc.common-ts.js --ext .ts,.tsx ./examples/common-ts/", 12 | "eslint-rax": "eslint --no-eslintrc -c .eslintrc.rax.js --ext .js,.jsx ./examples/rax/", 13 | "eslint-rax-ts": "eslint --no-eslintrc -c .eslintrc.rax-ts.js --ext .ts,.tsx ./examples/rax-ts/", 14 | "eslint-react": "eslint --no-eslintrc -c .eslintrc.react.js --ext .js,.jsx ./examples/react/", 15 | "eslint-react-ts": "eslint --no-eslintrc -c .eslintrc.react-ts.js --ext .ts,.tsx ./examples/react-ts/", 16 | "eslint-vue": "eslint --no-eslintrc -c .eslintrc.vue.js --ext .vue ./examples/vue/", 17 | "eslint-vue-ts": "eslint --no-eslintrc -c .eslintrc.vue-ts.js --ext .vue ./examples/vue-ts/", 18 | "eslint-test": "npm run eslint-common && npm run eslint-common-ts && npm run eslint-rax && npm run eslint-rax-ts && npm run eslint-react && npm run eslint-react-ts && npm run eslint-vue && npm run eslint-vue-ts", 19 | "stylelin-test": "stylelint ./**/*.{css,scss,vue}", 20 | "test": "npm run eslint-test && npm run stylelin-test", 21 | "prepublishOnly": "npm run test" 22 | }, 23 | "repository": { 24 | "type": "git", 25 | "url": "git+https://github.com/ice-lab/spec.git" 26 | }, 27 | "bugs": { 28 | "url": "https://github.com/ice-lab/spec/issues" 29 | }, 30 | "publishConfig": { 31 | "access": "public" 32 | }, 33 | "homepage": "https://github.com/ice-lab/spec#readme", 34 | "peerDependencies": { 35 | "eslint": ">=7.5.0", 36 | "stylelint": ">=8.3.0" 37 | }, 38 | "dependencies": { 39 | "@iceworks/eslint-plugin-best-practices": "^0.2.0", 40 | "@typescript-eslint/eslint-plugin": "^5.0.0", 41 | "@typescript-eslint/parser": "^5.0.0", 42 | "@babel/core": "^7.16.0", 43 | "@babel/eslint-parser": "^7.16.3", 44 | "@babel/preset-react": "^7.16.0", 45 | "commitlint-config-ali": "^0.1.0", 46 | "eslint-config-ali": "^13.0.0", 47 | "eslint-plugin-import": "^2.22.1", 48 | "eslint-plugin-jsx-plus": "^0.1.0", 49 | "eslint-plugin-rax-compile-time-miniapp": "^1.0.0", 50 | "eslint-plugin-react": "^7.21.5", 51 | "eslint-plugin-react-hooks": "^4.2.0", 52 | "eslint-plugin-vue": "^7.3.0", 53 | "json5": "^2.2.0", 54 | "require-all": "^3.0.0", 55 | "stylelint-config-ali": "^0.3.4", 56 | "stylelint-scss": "^3.18.0", 57 | "vue-eslint-parser": "^7.2.0" 58 | }, 59 | "devDependencies": { 60 | "eslint": "^7.22.0", 61 | "jest": "^24.9.0", 62 | "prettier": "^2.1.0", 63 | "rax": "^1.1.0", 64 | "react": "^16.12.0", 65 | "react-dom": "^16.12.0", 66 | "stylelint": "^13.2.0", 67 | "typescript": "^3.5.3" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /packages/eslint-plugin-best-practices/README.md: -------------------------------------------------------------------------------- 1 | # @iceworks/eslint-plugin-best-practices 2 | 3 | Iceworks best practices eslint plugin. 4 | 5 | ## Installation 6 | 7 | Install [esLint](http://eslint.org), `@iceworks/eslint-plugin-best-practices`: 8 | 9 | ```shell 10 | $ npm install --save-dev eslint @iceworks/eslint-plugin-best-practices 11 | ``` 12 | 13 | ## Usage 14 | 15 | Recommend use [@iceworks/spec](https://www.npmjs.com/package/@iceworks/spec) 16 | 17 | Then configure the rules you want to use under the rules section. 18 | 19 | ```js 20 | // .eslintrc.js 21 | const { getESLintConfig } = require('@iceworks/spec'); 22 | 23 | // getESLintConfig(rule: 'rax'|'rax-ts'|'react'|'react-ts', customConfig?); 24 | module.exports = getESLintConfig('rax', { 25 | // custom config it will merge into main config 26 | rules: { 27 | '@iceworks/best-practices/rule-name': 'off', 28 | }, 29 | }); 30 | ``` 31 | 32 | ## Supported Rules 33 | 34 | - [`deps-no-ice-scripts`](https://github.com/ice-lab/spec/tree/master/packages/eslint-plugin-best-practices/docs/rules/deps-no-ice-scripts.md) It is not recommended to use ice-scripts, the new version is ice.js. 35 | - [`deps-no-resolutions`](https://github.com/ice-lab/spec/tree/master/packages/eslint-plugin-best-practices/docs/rules/deps-no-resolutions.md) It is not recommended to use resolutions to lock the version. 36 | - [`deps-no-router-library`](https://github.com/ice-lab/spec/tree/master/packages/eslint-plugin-best-practices/docs/rules/deps-no-router-library.md) It is not recommended to directly rely on routing libraries, such as react-router-dom, react-router. 37 | - [`no-broad-semantic-versioning`](https://github.com/ice-lab/spec/tree/master/packages/eslint-plugin-best-practices/docs/rules/no-broad-semantic-versioning.md) Recommended the semantic versioning include everything greater than a particular version in the same major range. 38 | - [`no-http-url`](https://github.com/ice-lab/spec/tree/master/packages/eslint-plugin-best-practices/docs/rules/no-http-url.md) Recommended the http url switch to HTTPS. 39 | - [`no-js-in-ts-project`](https://github.com/ice-lab/spec/tree/master/packages/eslint-plugin-best-practices/docs/rules/no-js-in-ts-project.md) It is not recommended to use js and ts files at the same time. 40 | - [`no-lowercase-component-name`](https://github.com/ice-lab/spec/tree/master/packages/eslint-plugin-best-practices/docs/rules/no-lowercase-component-name.md) It is not recommended to name components in lower case. 41 | - [`recommend-add-line-height-unit`](https://github.com/ice-lab/spec/tree/master/packages/eslint-plugin-best-practices/docs/rules/recommend-add-line-height-unit.md) Recommended to add unit for line-height which is more than 5. 42 | - [`recommend-functional-component`](https://github.com/ice-lab/spec/tree/master/packages/eslint-plugin-best-practices/docs/rules/recommend-functional-component.md) It is not recommended to use class component. 43 | - [`recommend-polyfill`](https://github.com/ice-lab/spec/tree/master/packages/eslint-plugin-best-practices/docs/rules/recommend-polyfill.md) Recommend API which not supported in iOS 9 to add polyfill file. 44 | - [`recommend-update-rax`](https://github.com/ice-lab/spec/tree/master/packages/eslint-plugin-best-practices/docs/rules/recommend-update-rax.md) Rax version < 1.0 , recommend to update Rax. 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @iceworks/spec 2 | 3 | Easy to use **eslint**(support TypeScript) / **stylelint** / **prettier** / **commitlint** in [rax](https://rax.js.org/), [ice](https://ice.work/) and react project. And spec means specification. 4 | 5 | ## Install 6 | 7 | ```bash 8 | $ npm i --save-dev @iceworks/spec eslint stylelint prettier @commitlint/cli 9 | ``` 10 | 11 | PS: You don't need to install other eslint plugins and parsers. 12 | 13 | ## Usage 14 | 15 | ### ESLint 16 | 17 | #### 1. Create configuration file 18 | 19 | First create a `.eslintrc.js` file. Then edit your config. 20 | 21 | #### 2. Update config 22 | 23 | [rules](https://github.com/ice-lab/spec/tree/master/packages/spec/src/eslint/react.js) base on [eslint-config-ali](https://www.npmjs.com/package/eslint-config-ali) and [@iceworks/eslint-plugin-best-practices](https://www.npmjs.com/package/@iceworks/eslint-plugin-best-practices). 24 | 25 | ```js 26 | // .eslintrc.js 27 | const { getESLintConfig } = require('@iceworks/spec'); 28 | 29 | // getESLintConfig(rule: 'common'|'rax'|'react'|'vue', customConfig?); 30 | module.exports = getESLintConfig('react'); 31 | ``` 32 | 33 | ### stylelint 34 | 35 | #### 1. Create configuration file 36 | 37 | First create a `.stylelintrc.js` file. Then edit your config. 38 | 39 | #### 2. Update config 40 | 41 | [rules](https://github.com/ice-lab/spec/tree/master/packages/spec/src/stylelint/react.js) base on [stylelint-config-ali](https://www.npmjs.com/package/stylelint-config-ali) 42 | 43 | ```js 44 | // .stylelintrc.js 45 | const { getStylelintConfig } = require('@iceworks/spec'); 46 | 47 | // getStylelintConfig(rule: 'common'|'rax'|'react'|'vue', customConfig?); 48 | module.exports = getStylelintConfig('react'); 49 | ``` 50 | 51 | ### prettier [rules](https://github.com/ice-lab/spec/tree/master/packages/spec/src/prettier/react.js) 52 | 53 | #### 1. Create configuration file 54 | 55 | First create a `.prettierrc.js` file. Then edit your config. 56 | 57 | #### 2. Update config 58 | 59 | ```js 60 | // .prettierrc.js 61 | const { getPrettierConfig } = require('@iceworks/spec'); 62 | 63 | // getPrettierConfig(rule: 'common'|'rax'|'react'|'vue', customConfig?); 64 | module.exports = getPrettierConfig('react'); 65 | ``` 66 | 67 | ### commitlint 68 | 69 | #### 1. Create configuration file 70 | 71 | First create a `.commitlintrc.js` file. Then edit your config. 72 | 73 | #### 2. Update config 74 | 75 | [rules](https://github.com/ice-lab/spec/tree/master/packages/spec/src/commitlint/react.js) base on [commitlint-config-ali](https://www.npmjs.com/package/commitlint-config-ali) 76 | 77 | ```js 78 | // .commitlintrc.js 79 | const { getCommitlintConfig } = require('@iceworks/spec'); 80 | 81 | // getCommitlintConfig(rule: 'common'|'rax'|'react'|'vue', customConfig?); 82 | module.exports = getCommitlintConfig('react'); 83 | ``` 84 | 85 | ## FAQ 86 | 87 | ### Custom config 88 | 89 | ```js 90 | // .eslintrc.js 91 | const { getESLintConfig } = require('@iceworks/spec'); 92 | 93 | // getESLintConfig(rule: 'common'|'rax'|'react'|'vue', customConfig?); 94 | module.exports = getESLintConfig('rax', { 95 | // custom config it will merge into main config 96 | rules: { 97 | // ... 98 | }, 99 | }); 100 | ``` 101 | 102 | ### package.json scripts 103 | 104 | Add `scripts` in your `package.json`, example: 105 | 106 | ```json 107 | "scripts": { 108 | "lint": "npm run eslint && npm run stylelint", 109 | "eslint": "eslint --cache --ext .js,.jsx,.ts,.tsx ./", 110 | "stylelint": "stylelint ./**/*.scss", 111 | "prettier": "prettier **/* --write" 112 | } 113 | ``` 114 | 115 | Then use `npm run lint` check your project, ues `npm run prettier` update your code. 116 | 117 | ### Git hooks 118 | 119 | To lint commits before they are created you can use Husky's Git hook. 120 | 121 | Install in your project `npm install husky --save-dev` or `yarn add -D husky`. 122 | 123 | After that, we recommend you to see [husky docs](https://www.npmjs.com/package/husky), then create "`commit-msg`" and "`pre-commit`" config. 124 | 125 | ### Update from @ice/spec 126 | 127 | If you are using [@ice/spec](https://www.npmjs.com/package/@ice/spec) in your project, we recommend use `@iceworks/spec` to get better maintainability and faster response to lint rules support. 128 | 129 | Based on `@iceworks/spec`'s simple API you can quickly migrate your project, install and update your lint config file, the mission is completed 😁. 130 | 131 | ## Develop 132 | 133 | ### Run Test 134 | 135 | ``` 136 | npm run test 137 | ``` 138 | 139 | run test for specific component 140 | 141 | ``` 142 | npm run test -- packages/spec 143 | ``` 144 | 145 | ### Run Prettier 146 | 147 | ``` 148 | npm run prettier 149 | ``` 150 | 151 | ### Run Lint 152 | 153 | ``` 154 | npm run lint 155 | ``` 156 | -------------------------------------------------------------------------------- /packages/spec/examples/react-ts/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Card, Table, Button, Icon, Pagination, Message } from '@alifd/next'; 3 | import { TableProps } from '@alifd/next/lib/table'; 4 | import { useFusionTable, useFullscreen } from 'ahooks'; 5 | 6 | import CustomList, { Column } from './CustomList'; 7 | import { getColumnKey } from './util'; 8 | 9 | import styles from './index.module.scss'; 10 | 11 | const unusedVar = 1; 12 | 13 | const TableActionIcon = Icon.createFromIconfontCN({ 14 | scriptUrl: '//at.alicdn.com/t/font_1899388_oxn3zhg34oj.js', 15 | }); 16 | 17 | const getTableData = ({ 18 | current, 19 | pageSize, 20 | }: { 21 | current: number; 22 | pageSize: number; 23 | }): Promise