diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..ba2a97b --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +node_modules +coverage diff --git a/.eslintrc.js b/.eslintrc.js index 84c1b3c..0ad5447 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,3 +1,9 @@ -const { eslintTS } = require('./lib/eslintTS'); +const { eslint, deepmerge } = require('./lib'); -module.exports = eslintTS; +const eslintConfig = deepmerge(eslint, { + env: { + jest: true + } +}); + +module.exports = eslintConfig; diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..f9df002 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,12 @@ +sudo: required +language: node_js +node_js: + - '10' +script: + - npm run lint + - npm run test +after_success: + - npm run coverage +cache: + directories: + - node_modules diff --git a/README.md b/README.md index a92523d..d7d6ea9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # spec +[![NPM version](https://img.shields.io/npm/v/@ice/spec.svg?style=flat)](https://npmjs.org/package/@ice/spec) [![Package Quality](https://npm.packagequality.com/shield/@ice%2Fspec.svg)](https://packagequality.com/#?package=@ice%2Fspec) [![build status](https://img.shields.io/travis/ice-lab/icespec.svg?style=flat-square)](https://travis-ci.com/ice-lab/spec) [![Test coverage](https://img.shields.io/codecov/c/github/ice-lab/spec.svg?style=flat-square)](https://codecov.io/gh/ice-lab/spec) [![NPM downloads](http://img.shields.io/npm/dm/@ice/spec.svg?style=flat)](https://npmjs.org/package/@ice/spec) [![David deps](https://img.shields.io/david/ice-lab/spec.svg?style=flat-square)](https://david-dm.org/ice-lab/spec) + Easy to use eslint/stylelint/prettier. And spec means specification. ## Features diff --git a/lib/deepmerge.js b/lib/deepmerge.js new file mode 100644 index 0000000..2e68c01 --- /dev/null +++ b/lib/deepmerge.js @@ -0,0 +1,18 @@ +module.exports = function(target, source) { + // deep clone + const newObj = JSON.parse(JSON.stringify(target)); + + Object.keys(source).forEach((key) => { + const type = Object.prototype.toString.call(source[key]); + + if (type === '[object Array]') { + newObj[key] = [...target[key], ...source[key]]; + } else if (type === '[object Object]') { + newObj[key] = {...target[key], ...source[key]}; + } else { + newObj[key] = source[key]; + } + }); + + return newObj; +}; diff --git a/lib/eslint.js b/lib/eslint.js index 915db0d..a03ee77 100644 --- a/lib/eslint.js +++ b/lib/eslint.js @@ -5,7 +5,7 @@ module.exports = { ecmaVersion: 2018, ecmaFeatures: { jsx: true, - } + }, }, extends: [ /** Airbnb JavaScript Style Guide https://github.com/airbnb/javascript#types */ @@ -15,12 +15,12 @@ module.exports = { require.resolve('eslint-config-prettier/react'), ], plugins: [ - 'react-hooks' + 'react-hooks', ], env: { es6: true, browser: true, - node: true + node: true, }, rules: { /** React Hooks Style Guide https://reactjs.org/docs/hooks-rules.html */ @@ -57,8 +57,8 @@ module.exports = { "react/jsx-filename-extension": [ 1, { - "extensions": [".js", ".jsx"] - } + "extensions": [".js", ".jsx"], + }, ], // 不允许使用 dangerous 属性:取消 "react/no-danger": 0, @@ -112,5 +112,5 @@ module.exports = { "no-plusplus": 0, // 强制语句结尾必须加逗号:开启 "semi": ["error", "always"], - } -} + }, +}; diff --git a/lib/index.js b/lib/index.js index 1674afd..99a5fb5 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,3 +1,5 @@ +/* eslint-disable global-require */ + module.exports = { eslint: require('./eslint'), tslint: require('./tslint'), @@ -7,5 +9,5 @@ module.exports = { // deprecated, please use tslint eslintTS: require('./tslint'), - deepmerge: require('deepmerge'), + deepmerge: require('./deepmerge'), }; diff --git a/lib/stylelint.js b/lib/stylelint.js index 159147c..9c74ef3 100644 --- a/lib/stylelint.js +++ b/lib/stylelint.js @@ -11,5 +11,5 @@ module.exports = { ], rules: { "no-empty-source": null, - } + }, }; diff --git a/lib/tslint.js b/lib/tslint.js index ead14f6..230193d 100644 --- a/lib/tslint.js +++ b/lib/tslint.js @@ -1,10 +1,10 @@ const eslint = require('./eslint'); -const deepmerge = require('deepmerge') +const deepmerge = require('./deepmerge'); module.exports = deepmerge(eslint, { parser: require.resolve("@typescript-eslint/parser"), extends: [ - "plugin:@typescript-eslint/recommended" + "plugin:@typescript-eslint/recommended", ], plugins: ["@typescript-eslint"], rules: { @@ -21,7 +21,7 @@ module.exports = deepmerge(eslint, { accessibility: 'explicit', overrides: { constructors: 'no-public', - } - }] - } + }, + }], + }, }); diff --git a/package.json b/package.json index 7dfc7f7..e656fc2 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,16 @@ { "name": "@ice/spec", - "version": "0.1.8", + "version": "0.1.9", "description": "eslint/stylelint/editorconfig", "main": "lib/index.js", + "files": [ + "lib" + ], "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "lint": "eslint --cache --ext .ts,.tsx,.js,.jsx ./", + "test": "jest", + "coverage": "codecov", + "prepublishOnly": "npm run lint && npm run test" }, "repository": { "type": "git", @@ -28,7 +34,6 @@ "@typescript-eslint/eslint-plugin": "^1.11.0", "@typescript-eslint/parser": "^1.11.0", "babel-eslint": "^10.0.2", - "deepmerge": "^3.3.0", "eslint-config-airbnb": "^17.1.1", "eslint-config-prettier": "^6.0.0", "eslint-plugin-import": "^2.18.0", @@ -43,6 +48,14 @@ "stylelint-scss": "^3.8.0" }, "devDependencies": { + "codecov": "^3.6.1", + "eslint": "^6.7.1", + "jest": "^24.9.0", + "stylelint": "^12.0.0", "typescript": "^3.5.3" + }, + "jest": { + "coverageDirectory": "./coverage/", + "collectCoverage": true } } diff --git a/test/index.test.js b/test/index.test.js new file mode 100644 index 0000000..706a005 --- /dev/null +++ b/test/index.test.js @@ -0,0 +1,44 @@ +const { deepmerge, eslint } = require('../lib'); + +it('set one rule should be replaced', () => { + const result = deepmerge(eslint, { + rules: { + "comma-dangle": [1, "never"], + }, + }); + + expect(result.rules['comma-dangle']).toEqual([1, "never"]); +}); + +it('root should be replaced', () => { + const result = deepmerge(eslint, { + root: false, + }); + + expect(result.root).toEqual(false); +}); + +it('parserOptions should be merged', () => { + const result = deepmerge(eslint, { + parserOptions: { + ecmaVersion: 2017, + ecmaFeatures: { + js: true, + }, + }, + }); + + expect(result.parserOptions.ecmaVersion).toEqual(2017); + expect(result.parserOptions.ecmaFeatures).toEqual({ + js: true, + }); + expect(result.parserOptions.ecmaFeatures.jsx).toEqual(undefined); +}); + +it('plugins should be merged', () => { + const result = deepmerge(eslint, { + plugins: ['react-xxx'], + }); + expect(result.plugins[0]).toEqual('react-hooks'); + expect(result.plugins[1]).toEqual('react-xxx'); +});