diff --git a/.eslintignore b/.eslintignore index baf23598c5..4e4130062a 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,5 @@ +.happo/ +.next/ coverage/ node_modules/ public/ @@ -5,4 +7,10 @@ esm/ lib/ tmp/ dist/ -*.d.ts \ No newline at end of file +out/ +*.d.ts +lerna.json +npm-shrinkwrap.json +package.json +package-lock.json +tsconfig.json \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000000..b0e049ad6e --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,82 @@ +module.exports = { + parser: '@babel/eslint-parser', + extends: [ + // these are relics of nimbus, we could definitely simplify + consolidate + './config-eslint/base.js', + './config-eslint/next.js', + './config-eslint/typescript.js', + './config-eslint/prettier.js', + ], + overrides: [ + { + files: '*.test.{js,jsx,ts,tsx}', + rules: { + 'import/no-extraneous-dependencies': 'off', + 'jest/require-to-throw-message': 'off', + }, + }, + { + files: '*.{js,jsx,ts,tsx}', + rules: { + 'arrow-parens': 'off', + 'consistent-return': 'off', + 'import/prefer-default-export': 'off', + 'linebreak-style': 'off', + 'lines-between-class-members': 'off', + 'no-console': 'off', + 'no-nested-ternary': 'off', + 'no-param-reassign': 'warn', + 'no-restricted-syntax': 'off', + 'no-use-before-define': 'off', + 'no-useless-rename': 'off', + 'object-curly-newline': 'off', + 'operator-linebreak': 'off', + 'promise/param-names': 'off', + 'react/destructuring-assignment': 'off', + 'react/forbid-prop-types': 'off', + 'react/jsx-curly-brace-presence': 'off', + 'react/jsx-filename-extension': 'off', + 'react/jsx-no-literals': 'off', + 'react/jsx-props-no-spreading': 'off', + 'react/jsx-sort-default-props': 'off', + 'react/jsx-sort-props': 'off', + 'react/no-array-index-key': 'off', + 'react/no-children-prop': 'off', + 'react/require-default-props': 'off', + 'react/sort-comp': 'off', + 'react/sort-prop-types': 'off', + 'unicorn/catch-error-name': 'off', + 'unicorn/no-fn-reference-in-iterator': 'off', + 'unicorn/prefer-node-append': 'off', + }, + }, + { + files: './packages/visx-demo/**', + rules: { + '@typescript-eslint/no-explicit-any': [ + 'warn', + { + fixToUnknown: false, + }, + ], + 'import/no-unresolved': [ + 'error', + { + ignore: ['^!!raw-loader!.*'], + }, + ], + 'import/no-webpack-loader-syntax': 'off', + 'jsx-a11y/anchor-is-valid': 'off', + 'jsx-a11y/label-has-associated-control': 'off', + 'jsx-a11y/no-onchange': 'off', + 'no-alert': 'off', + 'no-param-reassign': 'off', + 'react/button-has-type': 'off', + 'react/no-danger': 'off', + 'react/no-unescaped-entities': 'off', + 'react/prop-types': 'off', + 'react/state-in-constructor': 'off', + }, + }, + ], +}; diff --git a/.gitignore b/.gitignore index f3e0e0f2ad..4575208eb0 100644 --- a/.gitignore +++ b/.gitignore @@ -29,16 +29,6 @@ tmp/ # Configs (provided by Nimbus) .babelrc -.eslintignore -.eslintrc.js -.flowconfig -.prettierignore -babel.config.js -jest.config.js -prettier.config.js -tsconfig.eslint.json -tsconfig.json -tsconfig.options.json *.tsbuildinfo webpack.config.js diff --git a/.prettierignore b/.prettierignore index a673dafa74..f8ce6f30fd 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,5 @@ +.happo/ +.next/ coverage/ node_modules/ public/ @@ -5,6 +7,7 @@ esm/ lib/ tmp/ dist/ +out/ *.d.ts lerna.json npm-shrinkwrap.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 82c0e21a74..f768ffe5f6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,15 +52,14 @@ specify multiple packages to build this way, and optionally append `--watch` to for changes. ```sh -# build the specified package(s) as cjs version -# example `yarn build:workspaces --workspaces=@visx/shape` -yarn build:workspaces --workspaces=@visx/package[,@visx/package,...] - -# build the esm version (the @visx/demo next server sources these files) -yarn build:workspaces --workspaces=@visx/package[,@visx/package,...] --esm +# build the specified package(s) as cjs + esm versions +# example `PKG=@visx/axis yarn babel:pkg` +PKG=@visx/{package[,package]} yarn babel:pkg # generate d.ts(definition files) the specified package(s) -yarn type:workspaces --workspaces=@visx/package[,@visx/package,...] +# and rebuild any other packages the specified package(s) depend on +# example `PKG=@visx/axis yarn type:pkg` +PKG=@visx/{package[,package]} yarn type:pkg ``` from the `visx` monorepo root to re-build the package with your changes. diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000000..6f41eeb14f --- /dev/null +++ b/babel.config.js @@ -0,0 +1,53 @@ +const esm = process.env.ESM; + +const envOptions = { + loose: true, + modules: esm ? false : 'commonjs', + shippedProposals: true, + targets: { + ie: 11, + }, + bugfixes: false, +}; + +const presets = [ + ['@babel/preset-env', envOptions], + '@babel/preset-react', + '@babel/preset-typescript', +]; + +const plugins = ['babel-plugin-typescript-to-proptypes']; + +const ignore = [ + 'coverage/', + 'node_modules/', + 'public/', + 'esm/', + 'lib/', + 'tmp/', + 'dist/', + '*.d.ts', + '__tests__', + '__mocks__', +]; + +switch (process.env.NODE_ENV) { + case 'test': { + envOptions.modules = 'commonjs'; + envOptions.targets = { node: 'current' }; + plugins.push('babel-plugin-dynamic-import-node'); + break; + } + + case 'development': + case 'production': + default: { + break; + } +} + +module.exports = { + ignore, + plugins, + presets, +}; diff --git a/config-eslint/base.js b/config-eslint/base.js new file mode 100644 index 0000000000..53865a04fd --- /dev/null +++ b/config-eslint/base.js @@ -0,0 +1,97 @@ +const EXTS = ['.ts', '.tsx', '.js', '.jsx', '.json']; +const EXTS_GROUP = '{ts,tsx,js,jsx}'; +const ASSET_EXT_PATTERN = /\.(ttf|eot|otf|svg|woff|woff2|mp3|png|jpg|jpeg|gif|ico)$/; + +module.exports = { + root: true, + + parser: '@babel/eslint-parser', + + parserOptions: { + requireConfigFile: false, + }, + + extends: ['airbnb', 'plugin:jsx-a11y/recommended'], + + plugins: ['import', 'react', 'react-hooks'], + + globals: { + // Metrics and analytics providers + ga: 'readonly', + // Mostly for easier compatibility between browsers, workers, etc + global: 'readonly', + // Mostly references to `process.env.NODE_ENV` + process: 'readonly', + }, + + env: { + browser: true, + node: false, + }, + + reportUnusedDisableDirectives: true, + + settings: { + propWrapperFunctions: ['forbidExtraProps', 'exact', 'Object.freeze'], + 'import/ignore': ['node_modules', '\\.json$', ASSET_EXT_PATTERN.source], + 'import/extensions': EXTS, + 'import/resolver': { + node: { + extensions: EXTS, + }, + }, + }, + + rules: { + 'react-hooks/exhaustive-deps': 'error', + 'react-hooks/rules-of-hooks': 'error', + }, + + overrides: [ + { + files: [`*.test.${EXTS_GROUP}`], + plugins: ['jest'], + globals: { + jsdom: 'readonly', + }, + env: { + jest: true, + node: true, + }, + rules: { + 'max-classes-per-file': 'off', + 'no-magic-numbers': 'off', + 'sort-keys': 'off', + + // JEST + 'jest/expect-expect': 'error', + 'jest/no-alias-methods': 'error', + 'jest/no-done-callback': 'error', + 'jest/no-disabled-tests': 'error', + 'jest/no-duplicate-hooks': 'error', + 'jest/no-export': 'error', + 'jest/no-focused-tests': 'error', + 'jest/no-identical-title': 'error', + 'jest/no-if': 'error', + 'jest/no-jasmine-globals': 'error', + 'jest/no-standalone-expect': 'error', + 'jest/no-test-prefixes': 'error', + 'jest/no-test-return-statement': 'error', + 'jest/prefer-hooks-on-top': 'error', + 'jest/prefer-spy-on': 'error', + 'jest/prefer-todo': 'error', + 'jest/prefer-to-be': 'error', + 'jest/prefer-to-contain': 'error', + 'jest/prefer-to-have-length': 'error', + 'jest/require-to-throw-message': 'error', + 'jest/require-top-level-describe': 'error', + 'jest/valid-describe-callback': 'error', + 'jest/valid-expect': 'error', + 'jest/valid-title': 'error', + + // REACT + 'react/function-component-definition': 'off', + }, + }, + ], +}; diff --git a/config-eslint/next.js b/config-eslint/next.js new file mode 100644 index 0000000000..5f3d359152 --- /dev/null +++ b/config-eslint/next.js @@ -0,0 +1,151 @@ +const EXTS_GROUP = '{ts,tsx,js,jsx}'; + +module.exports = { + parser: '@babel/eslint-parser', + + plugins: ['promise', 'unicorn'], + + rules: { + // Not enabled in Airbnb + 'default-param-last': 'error', + 'func-name-matching': [ + 'error', + 'always', + { + considerPropertyDescriptor: true, + includeCommonJSModuleExports: false, + }, + ], + 'jsx-quotes': ['error', 'prefer-double'], + 'multiline-comment-style': 'off', + 'multiline-ternary': ['error', 'never'], + 'no-constant-condition': 'error', + 'no-constructor-return': 'error', + 'no-div-regex': 'error', + 'no-dupe-else-if': 'error', + 'no-implicit-coercion': 'error', + 'no-import-assign': 'error', + 'no-native-reassign': 'error', + 'no-negated-condition': 'error', + 'no-setter-return': 'error', + 'no-useless-call': 'error', + 'prefer-exponentiation-operator': 'error', + 'prefer-regex-literals': 'error', + 'require-atomic-updates': 'error', + + // Replaced with new proposals + 'react/jsx-props-no-spreading': 'off', + 'react/state-in-constructor': 'off', + 'react/static-property-placement': 'off', + + // IMPORT + 'import/default': 'error', + 'import/namespace': 'error', + 'import/no-unused-modules': 'off', // Super broken at the moment + 'import/imports-first': 'error', + + // PROMISE + 'promise/no-callback-in-promise': 'error', + 'promise/no-new-statics': 'error', + 'promise/no-promise-in-callback': 'error', + 'promise/no-return-in-finally': 'error', + 'promise/no-return-wrap': ['error', { allowReject: true }], + 'promise/param-names': 'error', + 'promise/valid-params': 'error', + + // REACT + 'react/destructuring-assignment': 'off', // Broken with class properties + 'react/forbid-prop-types': ['error', { forbid: ['any', 'array'] }], + 'react/jsx-handler-names': [ + 'error', + { + eventHandlerPrefix: 'handle', + eventHandlerPropPrefix: 'on', + }, + ], + 'react/jsx-key': 'error', + 'react/jsx-no-literals': 'error', + 'react/jsx-no-useless-fragment': 'error', + 'react/jsx-no-script-url': 'error', + 'react/jsx-sort-default-props': [ + 'error', + { + ignoreCase: true, + }, + ], + 'react/jsx-sort-props': [ + 'error', + { + callbacksLast: true, + shorthandFirst: true, + noSortAlphabetically: true, + reservedFirst: true, + }, + ], + 'react/no-did-mount-set-state': 'error', + 'react/no-direct-mutation-state': 'error', + 'react/no-unknown-property': [ + 2, + { + ignore: ['jsx', 'global'], // used by next + }, + ], + 'react/sort-comp': 'off', + 'react/sort-prop-types': [ + 'error', + { + ignoreCase: true, + callbacksLast: true, + requiredFirst: false, + sortShapeProp: true, + }, + ], + + // UNICORN + 'unicorn/better-regex': 'error', + 'unicorn/catch-error-name': 'error', + 'unicorn/consistent-function-scoping': 'error', + 'unicorn/custom-error-definition': 'error', + 'unicorn/error-message': 'error', + 'unicorn/escape-case': 'error', + 'unicorn/explicit-length-check': 'error', + 'unicorn/filename-case': 'off', + 'unicorn/import-index': 'error', + 'unicorn/new-for-builtins': 'error', + 'unicorn/no-abusive-eslint-disable': 'off', + 'unicorn/no-array-instanceof': 'error', + 'unicorn/no-hex-escape': 'error', + 'unicorn/no-fn-reference-in-iterator': 'error', + 'unicorn/no-for-loop': 'error', + 'unicorn/no-new-buffer': 'error', + 'unicorn/no-process-exit': 'error', + 'unicorn/no-zero-fractions': 'error', + 'unicorn/number-literal-case': 'error', + 'unicorn/prefer-add-event-listener': 'error', + 'unicorn/prefer-dataset': 'error', + 'unicorn/prefer-event-key': 'error', + 'unicorn/prefer-flat-map': 'error', + 'unicorn/prefer-includes': 'error', + 'unicorn/prefer-modern-dom-apis': 'error', + 'unicorn/prefer-negative-index': 'error', + 'unicorn/prefer-node-append': 'error', + 'unicorn/prefer-node-remove': 'error', + 'unicorn/prefer-starts-ends-with': 'error', + 'unicorn/prefer-string-slice': 'error', + 'unicorn/prefer-spread': 'off', // Currently broken + 'unicorn/prefer-text-content': 'error', + 'unicorn/prefer-trim-start-end': 'error', + 'unicorn/prefer-type-error': 'error', + 'unicorn/throw-new-error': 'error', + }, + + overrides: [ + { + files: [`*.test.${EXTS_GROUP}`], + rules: { + 'unicorn/no-fn-reference-in-iterator': 'off', + 'unicorn/consistent-function-scoping': 'off', + }, + }, + ], +}; diff --git a/config-eslint/prettier.js b/config-eslint/prettier.js new file mode 100644 index 0000000000..71451f8a3a --- /dev/null +++ b/config-eslint/prettier.js @@ -0,0 +1,11 @@ +module.exports = { + parser: '@babel/eslint-parser', + + extends: ['prettier'], + + plugins: ['prettier'], + + rules: { + 'prettier/prettier': 'error', + }, +}; diff --git a/config-eslint/typescript.js b/config-eslint/typescript.js new file mode 100644 index 0000000000..1b66634549 --- /dev/null +++ b/config-eslint/typescript.js @@ -0,0 +1,121 @@ +const EXTS_GROUP = '{ts,tsx,js,jsx}'; + +// In TS, all arguments are required for type information, +// so we need to override the base JS setting. +const noUnused = { vars: 'all', args: 'none', ignoreRestSiblings: true }; + +const project = 'tsconfig.eslint.json'; + +module.exports = { + settings: { + 'import/parsers': { + '@typescript-eslint/parser': ['.ts', '.tsx'], + }, + }, + + parserOptions: { + project, + }, + + overrides: [ + { + files: ['*.{ts,tsx}'], + + parser: '@typescript-eslint/parser', + + plugins: ['@typescript-eslint'], + + rules: { + 'func-call-spacing': 'off', + 'no-restricted-globals': 'off', + 'no-unused-vars': 'off', + + // IMPORT (Conflicts with TS patterns) + 'import/extensions': [ + 'error', + 'never', + { + json: 'always', + }, + ], + 'import/named': 'off', + 'import/no-cycle': 'off', + 'import/no-named-as-default': 'off', + 'import/no-extraneous-dependencies': [ + 'error', + { + devDependencies: [ + `test/**/*.${EXTS_GROUP}`, + `tests/**/*.${EXTS_GROUP}`, + `**/*.test.${EXTS_GROUP}`, + `**/jest.config.${EXTS_GROUP}`, + `**/webpack.config.${EXTS_GROUP}`, + `**/webpack.config.*.${EXTS_GROUP}`, + ], + optionalDependencies: false, + }, + ], + + // REACT (We dont use prop types) + 'react/default-props-match-prop-types': 'off', + 'react/jsx-filename-extension': [ + 'error', + { + extensions: ['.tsx'], + }, + ], + 'react/no-unused-prop-types': 'off', + 'react/prop-types': 'off', + 'react/require-default-props': 'off', + + // UNICORN + 'unicorn/no-fn-reference-in-iterator': 'off', + + // TYPESCRIPT + '@typescript-eslint/adjacent-overload-signatures': 'error', + '@typescript-eslint/await-thenable': 'error', + '@typescript-eslint/camelcase': 'off', // broken + '@typescript-eslint/class-name-casing': 'off', // broken + '@typescript-eslint/consistent-type-assertions': [ + 'error', + { assertionStyle: 'as', objectLiteralTypeAssertions: 'allow-as-parameter' }, + ], + '@typescript-eslint/explicit-function-return-type': 'off', // Allow inferrence + '@typescript-eslint/func-call-spacing': ['error', 'never'], + '@typescript-eslint/member-delimiter-style': 'error', + '@typescript-eslint/member-ordering': 'off', // Prefer react/sort-comp + '@typescript-eslint/no-array-constructor': 'error', + '@typescript-eslint/no-empty-function': 'off', // Default props are usually empty + '@typescript-eslint/no-empty-interface': 'error', + '@typescript-eslint/no-explicit-any': [ + 'error', + { fixToUnknown: true, ignoreRestArgs: true }, + ], + '@typescript-eslint/no-extra-parens': 'error', + '@typescript-eslint/no-for-in-array': 'error', + '@typescript-eslint/no-misused-new': 'error', + '@typescript-eslint/no-misused-promises': 'error', + '@typescript-eslint/no-parameter-properties': 'error', + '@typescript-eslint/no-require-imports': 'error', + '@typescript-eslint/no-throw-literal': 'error', + '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'error', + '@typescript-eslint/no-unsafe-call': 'warn', + '@typescript-eslint/no-unsafe-member-access': 'warn', + '@typescript-eslint/no-unsafe-return': 'warn', + '@typescript-eslint/no-unused-vars': ['error', noUnused], + '@typescript-eslint/no-use-before-define': 'error', + '@typescript-eslint/prefer-as-const': 'error', + '@typescript-eslint/prefer-for-of': 'error', + '@typescript-eslint/prefer-namespace-keyword': 'error', + '@typescript-eslint/prefer-nullish-coalescing': 'off', // Lots of false positives + '@typescript-eslint/prefer-optional-chain': 'error', + '@typescript-eslint/promise-function-async': 'off', // Conflicts with other async rules + '@typescript-eslint/require-await': 'error', + '@typescript-eslint/switch-exhaustiveness-check': 'error', + '@typescript-eslint/triple-slash-reference': 'error', + '@typescript-eslint/type-annotation-spacing': 'error', + '@typescript-eslint/unified-signatures': 'error', + }, + }, + ], +}; diff --git a/config-jest/mocks/file.js b/config-jest/mocks/file.js new file mode 100644 index 0000000000..9dc5fc1e4a --- /dev/null +++ b/config-jest/mocks/file.js @@ -0,0 +1 @@ +module.exports = ''; diff --git a/config-jest/setup/console.js b/config-jest/setup/console.js new file mode 100644 index 0000000000..eadb6032ad --- /dev/null +++ b/config-jest/setup/console.js @@ -0,0 +1,3 @@ +console.warn = console.error = function mockedConsole(message) { + throw new Error(message); +}; diff --git a/config-jest/setup/enzyme.js b/config-jest/setup/enzyme.js new file mode 100644 index 0000000000..f27568f58d --- /dev/null +++ b/config-jest/setup/enzyme.js @@ -0,0 +1,6 @@ +const Enzyme = require('enzyme'); +const Adapter = require('enzyme-adapter-react-16'); + +Enzyme.configure({ + adapter: new Adapter(), +}); diff --git a/config-jest/setup/shims.js b/config-jest/setup/shims.js new file mode 100644 index 0000000000..8ede319ee5 --- /dev/null +++ b/config-jest/setup/shims.js @@ -0,0 +1 @@ +require('airbnb-js-shims/target/es2019'); diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000000..728e114753 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,46 @@ +module.exports = { + bail: false, + collectCoverageFrom: [ + '**/src/**/*.{ts,tsx,js,jsx}', + '**/test/**/*.{ts,tsx,js,jsx}', + 'packages/src/**/*.{ts,tsx}', + '!node_modules/**', + '!packages/**/esm/**', + '!packages/**/lib/**', + '!packages/**/node_modules/**', + '!packages/visx-demo/**', + '!packages/visx-visx/**', + ], + coverageDirectory: './coverage', + coveragePathIgnorePatterns: [ + 'coverage/', + 'node_modules/', + 'public/', + 'esm/', + 'lib/', + 'tmp/', + 'dist/', + ], + coverageReporters: ['lcov', 'json-summary', 'html', 'json'], + coverageThreshold: { + global: { + branches: 0, + functions: 0, + lines: 0, + statements: 0, + }, + }, + moduleFileExtensions: ['mock.js', 'ts', 'tsx', 'js', 'jsx', 'json', 'node'], + moduleNameMapper: { + '^.+\\.(ttf|eot|otf|svg|woff|woff2|mp3|png|jpg|jpeg|gif|ico)$': + '/config-jest/mocks/file.js', + }, + roots: ['/packages'], + setupFiles: ['/config-jest/setup/shims.js', '/config-jest/setup/console.js'], + setupFilesAfterEnv: ['/config-jest/setup/enzyme.js'], + testEnvironment: 'jsdom', + testURL: 'http://localhost', + timers: 'fake', + verbose: false, + testPathIgnorePatterns: ['/packages/visx-demo', '/packages/visx-visx'], +}; diff --git a/package.json b/package.json index 0abac189e8..e0b2ad2d29 100644 --- a/package.json +++ b/package.json @@ -31,207 +31,90 @@ "private": true, "scripts": { "babel": "yarn run babel:cjs && yarn run babel:esm", - "babel:cjs": "nimbus babel --clean --workspaces=\"@visx/!(demo)\"", - "babel:esm": "nimbus babel --clean --workspaces=\"@visx/!(demo)\" --esm", + "babel:cjs": "lerna exec --ignore @visx/demo --parallel -- babel --root-mode upward --delete-dir-on-start src/ --out-dir lib --extensions .ts,.tsx", + "babel:esm": "ESM=true lerna exec --ignore @visx/demo --parallel -- babel --root-mode upward --delete-dir-on-start src/ --out-dir esm --extensions .ts,.tsx", + "babel:pkg": "lerna exec --scope $PKG -- babel --root-mode upward --delete-dir-on-start src/ --out-dir lib --extensions .ts,.tsx && lerna exec --scope $PKG -- ESM=true babel --root-mode upward --delete-dir-on-start src/ --out-dir esm --extensions .ts,.tsx", "build": "yarn run babel && yarn run type", "build:sizes": "yarn run ts ./scripts/computeBuildSizes.ts", "build:release": "yarn run ts ./scripts/performRelease/index.ts", - "build:workspaces": "nimbus babel --clean", "dev:demo": "yarn workspace @visx/demo dev", "check:sizes": "yarn run ts ./scripts/compareBuildSizes.ts", "clean": "rm -rf ./packages/**/{lib,esm}", - "format": "yarn run prettier --write", - "jest": "NODE_ENV=test nimbus jest --coverage --verbose", - "lint": "nimbus eslint", + "format": "lerna exec --parallel -- prettier --write --ignore-path ../../.prettierignore .", + "jest": "NODE_ENV=test jest --coverage --verbose", + "lint": "eslint packages/ --quiet", "lint:fix": "yarn run lint --fix", "prepare-release": "git checkout master && git pull --rebase origin master && lerna updated", - "prettier": "nimbus prettier", "release": "yarn run prepare-release && lerna publish --exact", "setup": "yarn run build", "test": "yarn run jest", "test:watch": "yarn run jest --watch", "ts": "ts-node --project ./tsconfig.node.json", - "type": "nimbus typescript --build --reference-workspaces", - "type:workspaces": "nimbus typescript --build" + "type": "lerna exec --parallel -- tsc --build", + "type:pkg": "lerna exec --scope $PKG -- tsc --build --verbose", + "type:update-refs": "yarn run ts ./scripts/updateTsReferences.ts" }, "devDependencies": { - "@airbnb/config-babel": "^3.1.0", - "@airbnb/config-eslint": "^3.1.0", - "@airbnb/config-jest": "^3.0.2", - "@airbnb/config-prettier": "^3.1.0", - "@airbnb/config-typescript": "^3.0.1", - "@airbnb/nimbus": "^3.1.4", + "@babel/cli": "^7.19.3", + "@babel/core": "^7.20.5", + "@babel/eslint-parser": "^7.19.1", + "@babel/preset-env": "^7.20.2", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.18.6", "@octokit/rest": "18.1.0", + "@testing-library/jest-dom": "^5.9.0", + "@testing-library/react": "^13.1.0", + "@testing-library/react-hooks": "^8.0.0", + "@testing-library/user-event": "^11.0.1", "@types/enzyme": "^3.10.3", "@types/jest": "^24.0.18", "@types/jsdom": "^12.2.4", "@types/node-fetch": "1.6.9", "@types/react-test-renderer": "^18.0.0", "@types/webpack": "^4.41.17", - "@testing-library/jest-dom": "^5.9.0", - "@testing-library/react": "^13.1.0", - "@testing-library/react-hooks": "^8.0.0", - "@testing-library/user-event": "^11.0.1", + "@typescript-eslint/eslint-plugin": "^5.46.1", + "@typescript-eslint/parser": "^5.46.1", + "airbnb-js-shims": "^2.2.1", + "babel-plugin-typescript-to-proptypes": "^2.0.0", "chalk": "4.1.0", "coveralls": "^3.0.6", "enzyme": "^3.10.0", "enzyme-adapter-react-16": "^1.14.0", "enzyme-to-json": "^3.4.0", - "eslint-plugin-import": "^2.22.1", + "eslint": "^8.30.0", + "eslint-config-airbnb": "^19.0.4", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-jest": "^27.1.7", + "eslint-plugin-jsx-a11y": "^6.6.1", + "eslint-plugin-prettier": "^4.2.1", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-react": "^7.31.11", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-unicorn": "^45.0.2", "fast-glob": "3.2.5", "filesize": "6.1.0", "fs-jetpack": "^1.3.0", "husky": "^3.0.0", + "jest": "^25.5.4", "jest-mock-console": "^1.0.1", "lerna": "^3.22.0", "marked": "^0.7.0", "node-fetch": "2.6.1", + "prettier": "^2.8.1", "raf": "^3.4.0", "react": "^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0", "react-dom": "^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0", "react-test-renderer": "^18.0.0", "regenerator-runtime": "^0.10.5", "timezone-mock": "^1.1.0", - "ts-node": "9.1.1" + "ts-node": "9.1.1", + "typescript": "^3.8.3" }, "workspaces": [ "./packages/*" ], "resolutions": { "handlebars": "4.5.3" - }, - "nimbus": { - "drivers": [ - "babel", - "eslint", - "jest", - "prettier", - "typescript" - ], - "settings": { - "library": true, - "react": true, - "next": true - }, - "babel": { - "plugins": [ - [ - "@babel/plugin-proposal-private-methods", - { - "loose": false - } - ] - ] - }, - "typescript": { - "compilerOptions": { - "emitDeclarationOnly": true - } - }, - "jest": { - "coverageReporters": [ - "json" - ], - "collectCoverageFrom": [ - "packages/src/**/*.{ts,tsx}", - "!node_modules/**", - "!packages/**/esm/**", - "!packages/**/lib/**", - "!packages/**/node_modules/**", - "!packages/visx-demo/**", - "!packages/visx-visx/**" - ], - "testPathIgnorePatterns": [ - "/packages/visx-demo", - "/packages/visx-visx" - ], - "coverageThreshold": { - "global": { - "branches": 0, - "functions": 0, - "lines": 0, - "statements": 0 - } - }, - "setupFilesAfterEnv": [ - "@airbnb/config-jest/enzyme" - ] - }, - "eslint": { - "overrides": [ - { - "files": "*.test.{js,jsx,ts,tsx}", - "rules": { - "import/no-extraneous-dependencies": "off", - "jest/require-to-throw-message": "off" - } - }, - { - "files": "*.{js,jsx,ts,tsx}", - "rules": { - "arrow-parens": "off", - "consistent-return": "off", - "import/prefer-default-export": "off", - "linebreak-style": "off", - "lines-between-class-members": "off", - "no-console": "off", - "no-nested-ternary": "off", - "no-param-reassign": "warn", - "no-restricted-syntax": "off", - "no-use-before-define": "off", - "no-useless-rename": "off", - "object-curly-newline": "off", - "operator-linebreak": "off", - "promise/param-names": "off", - "react/destructuring-assignment": "off", - "react/forbid-prop-types": "off", - "react/jsx-curly-brace-presence": "off", - "react/jsx-filename-extension": "off", - "react/jsx-no-literals": "off", - "react/jsx-props-no-spreading": "off", - "react/jsx-sort-default-props": "off", - "react/jsx-sort-props": "off", - "react/no-array-index-key": "off", - "react/no-children-prop": "off", - "react/require-default-props": "off", - "react/sort-comp": "off", - "react/sort-prop-types": "off", - "unicorn/catch-error-name": "off", - "unicorn/no-fn-reference-in-iterator": "off", - "unicorn/prefer-node-append": "off" - } - }, - { - "files": "./packages/visx-demo/**", - "rules": { - "@typescript-eslint/no-explicit-any": [ - "warn", - { - "fixToUnknown": false - } - ], - "import/no-unresolved": [ - "error", - { - "ignore": [ - "^!!raw-loader!.*" - ] - } - ], - "import/no-webpack-loader-syntax": "off", - "jsx-a11y/anchor-is-valid": "off", - "jsx-a11y/label-has-associated-control": "off", - "jsx-a11y/no-onchange": "off", - "no-alert": "off", - "no-param-reassign": "off", - "react/button-has-type": "off", - "react/no-danger": "off", - "react/no-unescaped-entities": "off", - "react/prop-types": "off", - "react/state-in-constructor": "off" - } - } - ] - } } } diff --git a/packages/visx-annotation/Readme.md b/packages/visx-annotation/Readme.md index 1fefc81259..e514b3812a 100644 --- a/packages/visx-annotation/Readme.md +++ b/packages/visx-annotation/Readme.md @@ -6,18 +6,24 @@

-SVG `Annotation`s enable you to label points, thresholds, or regions of a visualization to provide additional context to for your chart consumer. This package is heavily inspired by [Susie Lu](https://github.com/susielu/)'s [`react-annotation`](https://github.com/susielu/react-annotation) library. +SVG `Annotation`s enable you to label points, thresholds, or regions of a visualization to provide +additional context to for your chart consumer. This package is heavily inspired by +[Susie Lu](https://github.com/susielu/)'s +[`react-annotation`](https://github.com/susielu/react-annotation) library. Each annotation consists of three (optional) parts: -1) `Subject` (`CircleSubject`, `LineSubject`, more πŸ”œ) – what part of a chart is being annotated (point, line, region) +1. `Subject` (`CircleSubject`, `LineSubject`, more πŸ”œ) – what part of a chart is being annotated + (point, line, region) -2) `Label` – the text component for the annotation. Handles SVG text wrapping using `@visx/text`, and supports `title` and `subtitle` customization as well as vertical & horizontal anchors / alignment +2. `Label` – the text component for the annotation. Handles SVG text wrapping using `@visx/text`, + and supports `title` and `subtitle` customization as well as vertical & horizontal anchors / + alignment -3) `Connector` – line connecting a subject and label +3. `Connector` – line connecting a subject and label - -The `Annotation` or `EditableAnnotation` component wrappers allow you to compose these components and simplify their individual positioning: +The `Annotation` or `EditableAnnotation` component wrappers allow you to compose these components +and simplify their individual positioning: ```tsx ``` -Components can also be used in isolation, in which case you must specify exact positions for each item: +Components can also be used in isolation, in which case you must specify exact positions for each +item: ```tsx () => ( @@ -47,8 +54,8 @@ Components can also be used in isolation, in which case you must specify exact p ##### ⚠️ `ResizeObserver` dependency -The `Label` component relies on `ResizeObserver`s for auto-sizing. If you need a polyfill, you can either polute the `window` object or inject it cleanly through props: - +The `Label` component relies on `ResizeObserver`s for auto-sizing. If you need a polyfill, you can +either polute the `window` object or inject it cleanly through props: ```tsx import { ResizeObserver } from 'your-favorite-polyfill'; @@ -57,7 +64,6 @@ function App() { return