diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8cdf15385a..d8d42aa172 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -80,3 +80,30 @@ jobs: echo "_No coverage report was produced._" fi } >> "$GITHUB_STEP_SUMMARY" + + eslint: + name: eslint + runs-on: ubuntu-latest + timeout-minutes: 15 + + steps: + - uses: actions/checkout@v5 + with: + path: firefly + + - uses: docker/setup-buildx-action@v3 + + - name: Build test image + uses: docker/build-push-action@v6 + with: + context: . + file: firefly/docker/Dockerfile + target: base + tags: ipac/firefly-test-js:latest + load: true + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: ESLint + working-directory: firefly + run: docker compose run --rm lint-js diff --git a/buildScript/tasks.gincl b/buildScript/tasks.gincl index 2689b015cb..706e1cc4cb 100644 --- a/buildScript/tasks.gincl +++ b/buildScript/tasks.gincl @@ -327,6 +327,20 @@ task jsTest (dependsOn: [loadConfig]) { } } +task jsLint (dependsOn: [loadConfig]) { + group = DEV_GROUP + + doLast { + if (file("package.json").exists()) { + println ">> running ESLint..." + def res = project.NODE 'yarn', 'run', 'lint' + if (res.getExitValue() != 0) { + throw new GradleException("ESLint fail.") + } + } + } +} + test { description= ''' Run Java and JavaScript unit test Only JS test: diff --git a/buildScript/webpack.config.js b/buildScript/webpack.config.js index 35d396edd8..4b8af805b3 100644 --- a/buildScript/webpack.config.js +++ b/buildScript/webpack.config.js @@ -52,7 +52,6 @@ export default function makeWebpackConfig(config) { const def_config = { dist : process.env.WP_BUILD_DIR || path.resolve(config.project, `build/${config.name}/war`), - do_lint : process.env.DO_LINT || process.env.DO_LINT_STRICT || false, html_dir : 'html', use_loader : true, loaderPostfix: '_loader.js', diff --git a/component-library/build.gradle b/component-library/build.gradle index 4d874ce379..1f32311467 100644 --- a/component-library/build.gradle +++ b/component-library/build.gradle @@ -10,6 +10,9 @@ def libTgz = "${libPackDir}/firefly-components-v${libVersion}.tgz" // tasks.gincl is applied via subprojects{} — disable JS/Java test pipeline for this subproject jsTest.actions.clear() jsTest.dependsOn = [] +// :firefly:jsLint already lints the whole repo, this directory included +jsLint.actions.clear() +jsLint.dependsOn = [] task installDeps { description = 'Install component-library node dependencies.' diff --git a/compose.yml b/compose.yml index 4d89b9a0cf..4b32c8f0af 100644 --- a/compose.yml +++ b/compose.yml @@ -45,6 +45,19 @@ services: - /opt/work/firefly/.gradle - /opt/work/firefly/node_modules + # ESLint-only run, used by the GitHub Actions PR check. + lint-js: + build: + context: ../ + dockerfile: firefly/docker/Dockerfile + target: base + image: ipac/firefly-test-js:latest + command: bash -c "cd firefly && gradle :firefly:jsLint" + volumes: + - ../firefly:/opt/work/firefly + - /opt/work/firefly/.gradle + - /opt/work/firefly/node_modules + dev: build: context: ../ diff --git a/eslint.config.mjs b/eslint.config.mjs index aaa3794a8a..5c32741215 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,7 +1,8 @@ +import js from "@eslint/js"; import react from "eslint-plugin-react"; import jsdoc from "eslint-plugin-jsdoc"; import reactHooks from "eslint-plugin-react-hooks"; -import stylisticJs from '@stylistic/eslint-plugin-js' +import stylisticJs from '@stylistic/eslint-plugin' import { fileURLToPath } from 'url'; import path from 'path'; import { fixupPluginRules, includeIgnoreFile } from "@eslint/compat"; @@ -19,6 +20,16 @@ const gitIgnorePath = path.resolve(__dirname, ".gitignore") export default [ includeIgnoreFile(gitIgnorePath), //ignore git-ignored dirs/files + + // vendored third-party source + {ignores: [ + "src/firefly/html/gpu-browser.min-*.js", + "src/firefly/js/externalSource/**", + ]}, + + // eslint's recommended rules; the block below layers over these + {files: ["**/*.js", "**/*.jsx"], ...js.configs.recommended}, + { files: ["**/*.js", "**/*.jsx"], @@ -59,21 +70,41 @@ export default [ eqeqeq: 1, "no-console": 0, "no-empty": 1, - "no-undef": 2, "no-use-before-define": 0, "no-unused-vars": 1, "object-shorthand": 1, "prefer-const": 1, "prefer-spread": 1, "prefer-template": 0, + + // rules with existing violations: warn until the sites are addressed + "no-case-declarations": 1, + "no-async-promise-executor": 1, + "no-cond-assign": 1, + "no-constant-binary-expression": 1, + "no-control-regex": 1, + "no-debugger": 1, + "no-empty-pattern": 1, + "no-extra-boolean-cast": 1, + "no-import-assign": 1, + "no-irregular-whitespace": 1, + "no-prototype-builtins": 1, + "no-redeclare": 1, + "no-regex-spaces": 1, + "no-sparse-arrays": 1, + "no-undef": 1, + "no-unreachable": 1, + "no-unsafe-optional-chaining": 1, + "no-useless-escape": 1, + "@stylistic/js/semi": [1, "always"], // restore to [2, "always"] + "@stylistic/js/arrow-parens": [2, "always"], "@stylistic/js/comma-spacing": 0, "@stylistic/js/jsx-quotes": [1, "prefer-single"], "@stylistic/js/key-spacing": 0, "@stylistic/js/no-multi-spaces": 0, - "@stylistic/js/semi": [2, "always"], "@stylistic/js/space-infix-ops": 0, - "@stylistic/js/quotes": [2, "single", "avoid-escape"], + "@stylistic/js/quotes": [2, "single", {avoidEscape: true, allowTemplateLiterals: "avoidEscape"}], "react/jsx-boolean-value": [1, "always"], "react/jsx-no-duplicate-props": 1, "react/jsx-no-undef": 1, @@ -108,13 +139,13 @@ export default [ 'react-hooks/unsupported-syntax': 'warn', 'react-hooks/use-memo': 'warn', 'react-hooks/incompatible-library': 'warn', - "jsdoc/check-param-names": 2, + "jsdoc/check-param-names": 1, "jsdoc/check-tag-names": 0, "jsdoc/check-types": 0, "jsdoc/newline-after-description": 0, "jsdoc/require-description-complete-sentence": 0, "jsdoc/require-hyphen-before-param-description": 0, - "jsdoc/require-param": 2, + "jsdoc/require-param": 1, "jsdoc/require-param-description": 0, "jsdoc/require-param-type": 0, "jsdoc/require-returns-description": 0, diff --git a/package-json-doc.md b/package-json-doc.md index dc6bbe4c01..4994715cdc 100644 --- a/package-json-doc.md +++ b/package-json-doc.md @@ -111,14 +111,16 @@ JSDoc packages: - taffydb - JSDoc claims not to need this for 4.0 but I think ink-docstrap needs it #### `ESLint` -ESLint is completely up to date. We should always keep this up-to-date +Version 9. ESLint packages: - eslint -- babel-eslint -- eslint-loader +- @eslint/js +- @eslint/compat +- @babel/eslint-parser +- @stylistic/eslint-plugin - eslint-plugin-jsdoc -- eslint-plugin-jsx-a11y - eslint-plugin-react - eslint-plugin-react-hooks +- globals diff --git a/package.json b/package.json index 09d312f366..2b531ab754 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,8 @@ "@babel/preset-react": "^7.24.7", "@babel/register": "^7.24.6", "@eslint/compat": "^1.1.0", - "@stylistic/eslint-plugin-js": "~4.4", + "@eslint/js": "~9.38", + "@stylistic/eslint-plugin": "~5.10", "@svgr/webpack": "^8.1.0", "@testing-library/dom": "^10", "@testing-library/jest-dom": "~6.9.1", @@ -79,7 +80,6 @@ "css-loader": "~7.1", "eslint": "~9.38", "eslint-plugin-jsdoc": "~50.8", - "eslint-plugin-jsx-a11y": "^6.9.0", "eslint-plugin-react": "~7.37", "eslint-plugin-react-hooks": "^7.0.1", "globals": "~16.4", diff --git a/src/firefly/package.json b/src/firefly/package.json index df34be3e09..db3a60937c 100644 --- a/src/firefly/package.json +++ b/src/firefly/package.json @@ -1,7 +1,7 @@ { "scripts": { "build": "../../node_modules/webpack/bin/webpack.js", - "lint": "DO_LINT=true yarn run build", + "lint": "node ../../node_modules/eslint/bin/eslint.js ../..", "dev": "DEV_MODE=true yarn run build -w --no-color", "test-unit": "node ../../node_modules/jest/bin/jest --passWithNoTests", "test-debug": "node --inspect-brk ../../node_modules/jest/bin/jest --runInBand --passWithNoTests" diff --git a/yarn.lock b/yarn.lock index 43f7297a65..ba2716bbad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1372,6 +1372,13 @@ dependencies: eslint-visitor-keys "^3.4.3" +"@eslint-community/eslint-utils@^4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz#4e90af67bc51ddee6cdef5284edf572ec376b595" + integrity sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ== + dependencies: + eslint-visitor-keys "^3.4.3" + "@eslint-community/regexpp@^4.12.1": version "4.12.1" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" @@ -1422,7 +1429,7 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@9.38.0": +"@eslint/js@9.38.0", "@eslint/js@~9.38": version "9.38.0" resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.38.0.tgz#f7aa9c7577577f53302c1d795643589d7709ebd1" integrity sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A== @@ -2166,13 +2173,17 @@ resolved "https://registry.yarnpkg.com/@standard-schema/utils/-/utils-0.3.0.tgz#3d5e608f16c2390c10528e98e59aef6bf73cae7b" integrity sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g== -"@stylistic/eslint-plugin-js@~4.4": - version "4.4.1" - resolved "https://registry.yarnpkg.com/@stylistic/eslint-plugin-js/-/eslint-plugin-js-4.4.1.tgz#b926ef82bf32438790d5ce2e5745ad301ed0ee8d" - integrity sha512-eLisyHvx7Sel8vcFZOEwDEBGmYsYM1SqDn81BWgmbqEXfXRf8oe6Rwp+ryM/8odNjlxtaaxp0Ihmt86CnLAxKg== +"@stylistic/eslint-plugin@~5.10": + version "5.10.0" + resolved "https://registry.yarnpkg.com/@stylistic/eslint-plugin/-/eslint-plugin-5.10.0.tgz#471bbd9f7a27ceaac4a217e7f5b3890855e5640c" + integrity sha512-nPK52ZHvot8Ju/0A4ucSX1dcPV2/1clx0kLcH5wDmrE4naKso7TUC/voUyU1O9OTKTrR6MYip6LP0ogEMQ9jPQ== dependencies: - eslint-visitor-keys "^4.2.0" - espree "^10.3.0" + "@eslint-community/eslint-utils" "^4.9.1" + "@typescript-eslint/types" "^8.56.0" + eslint-visitor-keys "^4.2.1" + espree "^10.4.0" + estraverse "^5.3.0" + picomatch "^4.0.3" "@svgr/babel-plugin-add-jsx-attribute@8.0.0": version "8.0.0" @@ -2494,6 +2505,11 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.46.1.tgz#4c5479538ec10b5508b8e982e172911c987446d8" integrity sha512-C+soprGBHwWBdkDpbaRC4paGBrkIXxVlNohadL5o0kfhsXqOC6GYH2S/Obmig+I0HTDl8wMaRySwrfrXVP8/pQ== +"@typescript-eslint/types@^8.56.0": + version "8.63.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.63.0.tgz#6b32b0a5913520554d81a986acfffba2d32b6963" + integrity sha512-xyLtl9DUBBFrcJS4x2pIqGLH68/tC2uOa4Z7pUteW09D3bXnnXUom4dyPikzWgB7llmIc1zoeI3aoUdC4rPK/Q== + "@ungap/structured-clone@^1.3.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.4.0.tgz#5e2e1374c0a30b5a42e8b083523a225c6945f88a" @@ -2914,7 +2930,7 @@ aria-query@5.3.0: dependencies: dequal "^2.0.3" -aria-query@^5.0.0, aria-query@^5.3.2: +aria-query@^5.0.0: version "5.3.2" resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.2.tgz#93f81a43480e33a338f19163a3d10a50c01dcd59" integrity sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw== @@ -2968,7 +2984,7 @@ array.prototype.flat@^1.3.1: es-abstract "^1.23.5" es-shim-unscopables "^1.0.2" -array.prototype.flatmap@^1.3.2, array.prototype.flatmap@^1.3.3: +array.prototype.flatmap@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz#712cc792ae70370ae40586264629e33aab5dd38b" integrity sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg== @@ -3019,11 +3035,6 @@ assert-plus@1.0.0, assert-plus@^1.0.0: resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== -ast-types-flow@^0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.8.tgz#0a85e1c92695769ac13a428bb653e7538bea27d6" - integrity sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ== - ast-types@0.9.6: version "0.9.6" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.6.tgz#102c9e9e9005d3e7e3829bf0c4fa24ee862ee9b9" @@ -3061,16 +3072,6 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.13.2.tgz#0aa167216965ac9474ccfa83892cfb6b3e1e52ef" integrity sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw== -axe-core@^4.10.0: - version "4.11.0" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.11.0.tgz#16f74d6482e343ff263d4f4503829e9ee91a86b6" - integrity sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ== - -axobject-query@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-4.1.0.tgz#28768c76d0e3cff21bc62a9e2d0b6ac30042a1ee" - integrity sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ== - babel-core@7.0.0-bridge.0: version "7.0.0-bridge.0" resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" @@ -3708,11 +3709,6 @@ currently-unhandled@^0.4.1: dependencies: array-find-index "^1.0.1" -damerau-levenshtein@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" - integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== - dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -4231,27 +4227,6 @@ eslint-plugin-jsdoc@~50.8: semver "^7.7.2" spdx-expression-parse "^4.0.0" -eslint-plugin-jsx-a11y@^6.9.0: - version "6.10.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz#d2812bb23bf1ab4665f1718ea442e8372e638483" - integrity sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q== - dependencies: - aria-query "^5.3.2" - array-includes "^3.1.8" - array.prototype.flatmap "^1.3.2" - ast-types-flow "^0.0.8" - axe-core "^4.10.0" - axobject-query "^4.1.0" - damerau-levenshtein "^1.0.8" - emoji-regex "^9.2.2" - hasown "^2.0.2" - jsx-ast-utils "^3.3.5" - language-tags "^1.0.9" - minimatch "^3.1.2" - object.fromentries "^2.0.8" - safe-regex-test "^1.0.3" - string.prototype.includes "^2.0.1" - eslint-plugin-react-hooks@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz#66e258db58ece50723ef20cc159f8aa908219169" @@ -4313,7 +4288,7 @@ eslint-visitor-keys@^3.4.3: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint-visitor-keys@^4.2.0, eslint-visitor-keys@^4.2.1: +eslint-visitor-keys@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1" integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ== @@ -6023,7 +5998,7 @@ jstransform@^11.0.1: object-assign "^2.0.0" source-map "^0.4.2" -"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.5: +"jsx-ast-utils@^2.4.1 || ^3.0.0": version "3.3.5" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a" integrity sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ== @@ -6052,18 +6027,6 @@ klaw@^3.0.0: dependencies: graceful-fs "^4.1.9" -language-subtag-registry@^0.3.20: - version "0.3.23" - resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz#23529e04d9e3b74679d70142df3fd2eb6ec572e7" - integrity sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ== - -language-tags@^1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.9.tgz#1ffdcd0ec0fafb4b1be7f8b11f306ad0f9c08777" - integrity sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA== - dependencies: - language-subtag-registry "^0.3.20" - leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" @@ -7625,7 +7588,7 @@ safe-push-apply@^1.0.0: es-errors "^1.3.0" isarray "^2.0.5" -safe-regex-test@^1.0.3, safe-regex-test@^1.1.0: +safe-regex-test@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz#7f87dfb67a3150782eaaf18583ff5d1711ac10c1" integrity sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw== @@ -8005,15 +7968,6 @@ string-width@^5.0.1, string-width@^5.1.2: emoji-regex "^9.2.2" strip-ansi "^7.0.1" -string.prototype.includes@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz#eceef21283640761a81dbe16d6c7171a4edf7d92" - integrity sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.3" - string.prototype.matchall@^4.0.12: version "4.0.12" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz#6c88740e49ad4956b1332a911e949583a275d4c0"