diff --git a/.babelrc.js b/.babelrc.js index 9a36788..fa3c8e5 100644 --- a/.babelrc.js +++ b/.babelrc.js @@ -12,10 +12,9 @@ module.exports = { } ], '@babel/preset-react', - '@babel/preset-flow' + '@babel/preset-typescript' ], plugins: [ - '@babel/plugin-transform-flow-strip-types', '@babel/plugin-syntax-dynamic-import', '@babel/plugin-syntax-import-meta', ['@babel/plugin-proposal-class-properties', { loose }], @@ -30,6 +29,8 @@ module.exports = { '@babel/plugin-proposal-export-namespace-from', '@babel/plugin-proposal-numeric-separator', '@babel/plugin-proposal-throw-expressions', + ['@babel/plugin-transform-private-methods', { loose }], + ['@babel/plugin-transform-private-property-in-object', { loose }], test && '@babel/plugin-transform-react-jsx-source' ].filter(Boolean) } diff --git a/.eslintignore b/.eslintignore deleted file mode 100755 index 9c62828..0000000 --- a/.eslintignore +++ /dev/null @@ -1,3 +0,0 @@ -node_modules -coverage -dist diff --git a/.flowconfig b/.flowconfig deleted file mode 100644 index 32087df..0000000 --- a/.flowconfig +++ /dev/null @@ -1,8 +0,0 @@ -[ignore] - -[include] - -[libs] - -[options] -esproposal.decorators=ignore diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..4ba48f9 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,51 @@ +name: CI + +on: [push] + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node_version }} + uses: actions/setup-node@v2 + with: + node-version: "22" + - name: Prepare env + run: yarn install --ignore-scripts --frozen-lockfile + - name: Run linter + run: yarn start lint + + prettier: + name: Prettier Check + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node_version }} + uses: actions/setup-node@v2 + with: + node-version: "22" + - name: Prepare env + run: yarn install --ignore-scripts --frozen-lockfile + - name: Run prettier + run: yarn start prettier + + test: + name: Unit Tests + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node_version }} + uses: actions/setup-node@v2 + with: + node-version: "22" + - name: Prepare env + run: yarn install --ignore-scripts --frozen-lockfile + - name: Run unit tests + run: yarn start test + - name: Run code coverage + uses: codecov/codecov-action@v2.1.0 diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml new file mode 100644 index 0000000..4e08172 --- /dev/null +++ b/.github/workflows/lock.yml @@ -0,0 +1,24 @@ +name: "Lock Threads" + +on: + schedule: + - cron: "0 * * * *" + workflow_dispatch: + +permissions: + issues: write + pull-requests: write + +concurrency: + group: lock + +jobs: + action: + runs-on: ubuntu-latest + steps: + - uses: dessant/lock-threads@v3 + with: + issue-inactive-days: "365" + issue-lock-reason: "resolved" + pr-inactive-days: "365" + pr-lock-reason: "resolved" diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..1eaf748 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,23 @@ +const typescriptParser = require('@typescript-eslint/parser') + +module.exports = [ + { + ignores: ['dist/**', 'node_modules/**', 'coverage/**'] + }, + { + files: ['**/*.js', '**/*.ts', '**/*.tsx'], + languageOptions: { + parser: typescriptParser, + ecmaVersion: 2020, + sourceType: 'module', + parserOptions: { + ecmaFeatures: { + jsx: true + } + } + }, + rules: { + // Minimal rules - can be expanded later + } + } +] diff --git a/package-scripts.js b/package-scripts.js index eed0592..4cce753 100644 --- a/package-scripts.js +++ b/package-scripts.js @@ -53,27 +53,19 @@ module.exports = { }, andTest: series.nps('build', 'test.size') }, - copyTypes: series( - npsUtils.copy('src/*.js.flow src/*.d.ts dist'), - npsUtils.copy( - 'dist/index.js.flow dist --rename="react-final-form-arrays.cjs.js.flow"' - ), - npsUtils.copy( - 'dist/index.js.flow dist --rename="react-final-form-arrays.es.js.flow"' - ) - ), + copyTypes: npsUtils.copy('src/*.d.ts dist'), docs: { description: 'Generates table of contents in README', script: 'doctoc README.md' }, + prettier: { + description: 'Runs prettier on everything', + script: 'prettier --write "**/*.([jt]s*)"' + }, lint: { description: 'lint the entire project', script: 'eslint .' }, - flow: { - description: 'flow check the entire project', - script: 'flow check' - }, typescript: { description: 'typescript check the entire project', script: 'tsc' diff --git a/package.json b/package.json index b94ea4b..bcd48e1 100644 --- a/package.json +++ b/package.json @@ -25,80 +25,79 @@ }, "homepage": "https://github.com/final-form/react-final-form-arrays#readme", "devDependencies": { - "@babel/core": "^7.19.3", + "@babel/core": "^7.27.1", "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-decorators": "^7.19.3", + "@babel/plugin-proposal-decorators": "^7.27.1", "@babel/plugin-proposal-export-namespace-from": "^7.18.9", - "@babel/plugin-proposal-function-sent": "^7.18.6", + "@babel/plugin-proposal-function-sent": "^7.27.1", "@babel/plugin-proposal-json-strings": "^7.18.6", "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-throw-expressions": "^7.18.6", + "@babel/plugin-proposal-throw-expressions": "^7.27.1", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-transform-flow-strip-types": "^7.19.0", - "@babel/plugin-transform-react-jsx-source": "^7.18.6", - "@babel/plugin-transform-runtime": "^7.19.1", - "@babel/preset-env": "^7.19.4", - "@babel/preset-flow": "^7.18.6", - "@babel/preset-react": "^7.18.6", - "@testing-library/jest-dom": "^5.16.5", - "@testing-library/react": "^11.1.0", - "@types/react": "^18.0.21", - "@typescript-eslint/eslint-plugin": "^5.40.1", + "@babel/plugin-transform-private-methods": "^7.27.1", + "@babel/plugin-transform-private-property-in-object": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@babel/plugin-transform-runtime": "^7.27.1", + "@babel/preset-env": "^7.27.2", + "@babel/preset-react": "^7.27.1", + "@babel/preset-typescript": "^7.27.1", + "@rollup/plugin-typescript": "^12.1.2", + "@testing-library/dom": "^10.4.0", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/react": "^16.3.0", + "@types/jest": "^29.5.0", + "@types/react": "^19.1.5", + "@typescript-eslint/eslint-plugin": "^8.32.1", + "@typescript-eslint/parser": "^8.32.1", "babel-core": "^7.0.0-bridge.0", "babel-eslint": "^10.1.0", - "babel-jest": "^29.2.1", - "bundlesize": "^0.18.1", + "babel-jest": "^29.7.0", + "bundlesize": "^0.18.2", "doctoc": "^2.2.1", - "eslint": "^8.25.0", + "eslint": "^9.27.0", "eslint-config-react-app": "^7.0.1", "eslint-plugin-babel": "^5.3.1", - "eslint-plugin-flowtype": "^8.0.3", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jsx-a11y": "^6.6.1", - "eslint-plugin-react": "^7.31.10", - "eslint-plugin-react-hooks": "^4.6.0", - "fast-check": "^3.2.0", - "final-form": "^4.20.7", - "final-form-arrays": "^3.0.2", - "flow-bin": "^0.190.0", - "glow": "^1.2.2", - "husky": "^8.0.1", - "jest": "^29.2.1", - "jest-environment-jsdom": "^29.2.1", - "jest-watch-typeahead": "^2.2.0", - "lint-staged": "^10.4.2", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsx-a11y": "^6.10.2", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^5.2.0", + "fast-check": "^4.1.1", + "final-form": "^5.0.0-3", + "final-form-arrays": "^4.0.0-0", + "husky": "^9.1.7", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "jest-watch-typeahead": "^2.2.2", + "lint-staged": "^16.0.0", "nps": "^5.10.0", "nps-utils": "^1.7.0", - "prettier": "^2.7.1", - "prettier-eslint-cli": "^7.1.0", + "prettier": "^3.5.3", + "prettier-eslint-cli": "^8.0.1", "raf": "^3.4.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-final-form": "^6.5.9", - "rollup": "^3.2.3", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "react-final-form": "^7.0.0-0", + "rollup": "^4.41.1", "rollup-plugin-babel": "^4.4.0", "rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-json": "^4.0.0", "rollup-plugin-node-resolve": "^5.2.0", "rollup-plugin-replace": "^2.2.0", "rollup-plugin-uglify": "^6.0.4", - "typescript": "^4.8.4" + "typescript": "^5.8.3" }, "peerDependencies": { - "final-form": "^4.15.0", - "final-form-arrays": ">=1.0.4", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-final-form": "^6.2.1" + "final-form": "^5.0.0-3", + "final-form-arrays": "^4.0.0-0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-final-form": "^7.0.0-0" }, "jest": { "testEnvironment": "jsdom", "watchPlugins": [ "jest-watch-typeahead/filename", "jest-watch-typeahead/testname" - ], - "testPathIgnorePatterns": [ - ".*\\.tsx?" ] }, "lint-staged": { diff --git a/rollup.config.js b/rollup.config.js index 5b91b61..7f756e4 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -4,6 +4,7 @@ import commonjs from 'rollup-plugin-commonjs' import json from 'rollup-plugin-json' import { uglify } from 'rollup-plugin-uglify' import replace from 'rollup-plugin-replace' +import typescript from '@rollup/plugin-typescript' const minify = process.env.MINIFY const format = process.env.FORMAT @@ -36,12 +37,16 @@ const globals = { react: 'React', 'final-form': 'FinalForm', 'react-final-form': 'ReactFinalForm', - 'react-lifecycles-compat': 'ReactLifecyclesCompat' + 'react-lifecycles-compat': 'ReactLifecyclesCompat', + '@babel/runtime/helpers/extends': '_extends', + '@babel/runtime/helpers/objectWithoutPropertiesLoose': + '_objectWithoutPropertiesLoose' } -// eslint-disable-next-line no-nested-ternary +const loose = true + export default { - input: 'src/index.js', + input: 'src/index.ts', output: Object.assign( { name: 'react-final-form-arrays', @@ -50,35 +55,43 @@ export default { }, output ), - external: id => { + external: (id) => { const isBabelRuntime = id.startsWith('@babel/runtime') const isStaticExternal = globals[id] return isBabelRuntime || isStaticExternal }, plugins: [ - resolve({ jsnext: true, main: true }), + resolve({ + mainFields: ['module', 'jsnext:main', 'main'], + extensions: ['.js', '.jsx', '.ts', '.tsx'] + }), json(), + typescript({ + tsconfig: './tsconfig.build.json', + declaration: true, + declarationMap: true + }), commonjs({ include: 'node_modules/**' }), babel({ exclude: 'node_modules/**', + extensions: ['.js', '.jsx', '.ts', '.tsx'], babelrc: false, presets: [ [ '@babel/preset-env', { - loose: true, + loose, modules: false } ], '@babel/preset-react', - '@babel/preset-flow' + '@babel/preset-typescript' ], plugins: [ - '@babel/plugin-transform-flow-strip-types', '@babel/plugin-transform-runtime', '@babel/plugin-syntax-dynamic-import', '@babel/plugin-syntax-import-meta', - '@babel/plugin-proposal-class-properties', + ['@babel/plugin-proposal-class-properties', { loose }], '@babel/plugin-proposal-json-strings', [ '@babel/plugin-proposal-decorators', @@ -89,7 +102,9 @@ export default { '@babel/plugin-proposal-function-sent', '@babel/plugin-proposal-export-namespace-from', '@babel/plugin-proposal-numeric-separator', - '@babel/plugin-proposal-throw-expressions' + '@babel/plugin-proposal-throw-expressions', + ['@babel/plugin-transform-private-methods', { loose }], + ['@babel/plugin-transform-private-property-in-object', { loose }] ], runtimeHelpers: true }), diff --git a/src/FieldArray.d.test.tsx b/src/FieldArray.d.test.tsx deleted file mode 100644 index bfb943d..0000000 --- a/src/FieldArray.d.test.tsx +++ /dev/null @@ -1,85 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -import * as React from 'react' -import { Form, Field } from 'react-final-form' -import arrayMutators from 'final-form-arrays' -import { FieldArray } from './index' - -const onSubmit = async (values: any) => { - console.log(values) -} - -const basic = () => ( -
- ) - }} - -) diff --git a/src/FieldArray.test.js b/src/FieldArray.test.tsx similarity index 84% rename from src/FieldArray.test.js rename to src/FieldArray.test.tsx index 4cbe075..2499ba6 100644 --- a/src/FieldArray.test.js +++ b/src/FieldArray.test.tsx @@ -1,14 +1,15 @@ -import React from 'react' +import * as React from 'react' import { act, render, fireEvent, cleanup } from '@testing-library/react' -import '@testing-library/jest-dom/extend-expect' +import '@testing-library/jest-dom' import arrayMutators from 'final-form-arrays' import { ErrorBoundary, Toggle, wrapWith } from './testUtils' import { Form, Field } from 'react-final-form' import { FieldArray, version } from '.' -const onSubmitMock = values => { } -const timeout = (ms) => new Promise((resolve) => setTimeout(resolve, ms)) -async function sleep(ms) { +const onSubmitMock = (values: any) => {} +const timeout = (ms: number) => + new Promise((resolve) => setTimeout(resolve, ms)) +async function sleep(ms: number) { await act(async () => { await timeout(ms) }) @@ -22,11 +23,13 @@ describe('FieldArray', () => { }) it('should warn if not used inside a form', () => { - jest.spyOn(console, 'error').mockImplementation(() => { }) + const mockConsoleError = jest + .spyOn(console, 'error') + .mockImplementation(() => {}) const errorSpy = jest.fn() render(