From 3a49579f3c242d3e159e88707df090e3f6dc0121 Mon Sep 17 00:00:00 2001 From: Joe Date: Fri, 2 Aug 2024 15:30:58 -0700 Subject: [PATCH] fix(regexp-matcher): correctly remap to original indices in all cases Fixes #71. It is deeply unfortunate that the current test suite was inadequate to both evaluate and catch this case. Though this commit does fix the issue, the test suite still needs revamping, to be done in future. --- pnpm-lock.yaml | 141 ++++++++++++---------- src/matcher/regexp/RegExpMatcher.ts | 76 ++++++------ test/matcher/regexp/RegExpMatcher.test.ts | 6 + 3 files changed, 119 insertions(+), 104 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7e67f66..da6905f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,7 +10,7 @@ importers: devDependencies: '@commitlint/cli': specifier: ^18.0.0 - version: 18.6.0(@types/node@20.2.5)(typescript@5.2.2) + version: 18.6.0(@types/node@22.1.0)(typescript@5.2.2) '@commitlint/config-angular': specifier: ^18.0.0 version: 18.6.0 @@ -34,13 +34,13 @@ importers: version: 8.42.0 eslint-config-neon: specifier: ^0.1.47 - version: 0.1.47(eslint@8.42.0)(jest@29.7.0(@types/node@20.2.5)(ts-node@10.9.1(@types/node@20.2.5)(typescript@5.2.2)))(svelte@3.59.2)(typescript@5.2.2) + version: 0.1.47(eslint@8.42.0)(jest@29.7.0(@types/node@22.1.0)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.2.2)))(svelte@3.59.2)(typescript@5.2.2) eslint-config-prettier: specifier: ^9.0.0 version: 9.0.0(eslint@8.42.0) eslint-plugin-jest: specifier: ^27.2.1 - version: 27.2.1(@typescript-eslint/eslint-plugin@6.20.0(@typescript-eslint/parser@6.20.0(eslint@8.42.0)(typescript@5.2.2))(eslint@8.42.0)(typescript@5.2.2))(eslint@8.42.0)(jest@29.7.0(@types/node@20.2.5)(ts-node@10.9.1(@types/node@20.2.5)(typescript@5.2.2)))(typescript@5.2.2) + version: 27.2.1(@typescript-eslint/eslint-plugin@6.20.0(@typescript-eslint/parser@6.20.0(eslint@8.42.0)(typescript@5.2.2))(eslint@8.42.0)(typescript@5.2.2))(eslint@8.42.0)(jest@29.7.0(@types/node@22.1.0)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.2.2)))(typescript@5.2.2) eslint-plugin-prettier: specifier: ^4.2.1 version: 4.2.1(eslint-config-prettier@9.0.0(eslint@8.42.0))(eslint@8.42.0)(prettier@2.8.8) @@ -55,7 +55,7 @@ importers: version: 3.0.1 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@20.2.5)(ts-node@10.9.1(@types/node@20.2.5)(typescript@5.2.2)) + version: 29.7.0(@types/node@22.1.0)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.2.2)) jest-circus: specifier: ^29.5.0 version: 29.5.0 @@ -70,10 +70,10 @@ importers: version: 9.5.0 ts-jest: specifier: ^29.1.1 - version: 29.1.1(@babel/core@7.22.1)(@jest/types@29.5.0)(babel-jest@29.7.0(@babel/core@7.22.1))(jest@29.7.0(@types/node@20.2.5)(ts-node@10.9.1(@types/node@20.2.5)(typescript@5.2.2)))(typescript@5.2.2) + version: 29.1.1(@babel/core@7.22.1)(@jest/types@29.5.0)(babel-jest@29.7.0(@babel/core@7.22.1))(jest@29.7.0(@types/node@22.1.0)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.2.2)))(typescript@5.2.2) ts-node: specifier: ^10.9.1 - version: 10.9.1(@types/node@20.2.5)(typescript@5.2.2) + version: 10.9.1(@types/node@22.1.0)(typescript@5.2.2) typedoc: specifier: ^0.25.0 version: 0.25.0(typescript@5.2.2) @@ -696,8 +696,8 @@ packages: '@types/node@18.16.18': resolution: {integrity: sha512-/aNaQZD0+iSBAGnvvN2Cx92HqE5sZCPZtx2TsK+4nvV23fFe09jVDvpArXr2j9DnYlzuU9WuoykDDc6wqvpNcw==} - '@types/node@20.2.5': - resolution: {integrity: sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==} + '@types/node@22.1.0': + resolution: {integrity: sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==} '@types/normalize-package-data@2.4.1': resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} @@ -4164,6 +4164,9 @@ packages: unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + undici-types@6.13.0: + resolution: {integrity: sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==} + unified-engine@10.1.0: resolution: {integrity: sha512-5+JDIs4hqKfHnJcVCxTid1yBoI/++FfF/1PFdSMpaftZZZY+qg2JFruRbf7PaIwa9KgLotXQV3gSjtY0IdcFGQ==} @@ -4615,11 +4618,11 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} - '@commitlint/cli@18.6.0(@types/node@20.2.5)(typescript@5.2.2)': + '@commitlint/cli@18.6.0(@types/node@22.1.0)(typescript@5.2.2)': dependencies: '@commitlint/format': 18.6.0 '@commitlint/lint': 18.6.0 - '@commitlint/load': 18.6.0(@types/node@20.2.5)(typescript@5.2.2) + '@commitlint/load': 18.6.0(@types/node@22.1.0)(typescript@5.2.2) '@commitlint/read': 18.6.0 '@commitlint/types': 18.6.0 execa: 5.1.1 @@ -4670,7 +4673,7 @@ snapshots: '@commitlint/rules': 18.6.0 '@commitlint/types': 18.6.0 - '@commitlint/load@18.6.0(@types/node@20.2.5)(typescript@5.2.2)': + '@commitlint/load@18.6.0(@types/node@22.1.0)(typescript@5.2.2)': dependencies: '@commitlint/config-validator': 18.6.0 '@commitlint/execute-rule': 18.4.4 @@ -4678,7 +4681,7 @@ snapshots: '@commitlint/types': 18.6.0 chalk: 4.1.2 cosmiconfig: 8.3.6(typescript@5.2.2) - cosmiconfig-typescript-loader: 5.0.0(@types/node@20.2.5)(cosmiconfig@8.3.6(typescript@5.2.2))(typescript@5.2.2) + cosmiconfig-typescript-loader: 5.0.0(@types/node@22.1.0)(cosmiconfig@8.3.6(typescript@5.2.2))(typescript@5.2.2) lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 lodash.uniq: 4.5.0 @@ -4798,7 +4801,7 @@ snapshots: '@jest/console@29.5.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 20.2.5 + '@types/node': 22.1.0 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -4807,27 +4810,27 @@ snapshots: '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 20.2.5 + '@types/node': 22.1.0 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 slash: 3.0.0 - '@jest/core@29.7.0(ts-node@10.9.1(@types/node@20.2.5)(typescript@5.2.2))': + '@jest/core@29.7.0(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.2.2))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.2.5 + '@types/node': 22.1.0 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.8.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.2.5)(ts-node@10.9.1(@types/node@20.2.5)(typescript@5.2.2)) + jest-config: 29.7.0(@types/node@22.1.0)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.2.2)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -4852,14 +4855,14 @@ snapshots: dependencies: '@jest/fake-timers': 29.5.0 '@jest/types': 29.6.3 - '@types/node': 20.2.5 + '@types/node': 22.1.0 jest-mock: 29.5.0 '@jest/environment@29.7.0': dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.2.5 + '@types/node': 22.1.0 jest-mock: 29.7.0 '@jest/expect-utils@29.5.0': @@ -4888,7 +4891,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.2.0 - '@types/node': 20.2.5 + '@types/node': 22.1.0 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -4897,7 +4900,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.2.0 - '@types/node': 20.2.5 + '@types/node': 22.1.0 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -4928,7 +4931,7 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.18 - '@types/node': 20.2.5 + '@types/node': 22.1.0 chalk: 4.1.2 collect-v8-coverage: 1.0.1 exit: 0.1.2 @@ -5035,7 +5038,7 @@ snapshots: '@jest/schemas': 29.4.3 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 20.2.5 + '@types/node': 22.1.0 '@types/yargs': 17.0.24 chalk: 4.1.2 @@ -5044,7 +5047,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 20.2.5 + '@types/node': 22.1.0 '@types/yargs': 17.0.24 chalk: 4.1.2 @@ -5182,7 +5185,7 @@ snapshots: '@types/concat-stream@2.0.0': dependencies: - '@types/node': 20.2.5 + '@types/node': 22.1.0 '@types/debug@4.1.8': dependencies: @@ -5196,7 +5199,7 @@ snapshots: '@types/graceful-fs@4.1.6': dependencies: - '@types/node': 20.2.5 + '@types/node': 22.1.0 '@types/hast@2.3.4': dependencies: @@ -5223,7 +5226,7 @@ snapshots: '@types/keyv@3.1.4': dependencies: - '@types/node': 20.2.5 + '@types/node': 22.1.0 '@types/mdast@3.0.11': dependencies: @@ -5235,7 +5238,9 @@ snapshots: '@types/node@18.16.18': {} - '@types/node@20.2.5': {} + '@types/node@22.1.0': + dependencies: + undici-types: 6.13.0 '@types/normalize-package-data@2.4.1': {} @@ -5243,7 +5248,7 @@ snapshots: '@types/responselike@1.0.0': dependencies: - '@types/node': 20.2.5 + '@types/node': 22.1.0 '@types/semver@7.5.0': {} @@ -6139,9 +6144,9 @@ snapshots: core-util-is@1.0.3: {} - cosmiconfig-typescript-loader@5.0.0(@types/node@20.2.5)(cosmiconfig@8.3.6(typescript@5.2.2))(typescript@5.2.2): + cosmiconfig-typescript-loader@5.0.0(@types/node@22.1.0)(cosmiconfig@8.3.6(typescript@5.2.2))(typescript@5.2.2): dependencies: - '@types/node': 20.2.5 + '@types/node': 22.1.0 cosmiconfig: 8.3.6(typescript@5.2.2) jiti: 1.21.0 typescript: 5.2.2 @@ -6155,13 +6160,13 @@ snapshots: optionalDependencies: typescript: 5.2.2 - create-jest@29.7.0(@types/node@20.2.5)(ts-node@10.9.1(@types/node@20.2.5)(typescript@5.2.2)): + create-jest@29.7.0(@types/node@22.1.0)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.2.2)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.2.5)(ts-node@10.9.1(@types/node@20.2.5)(typescript@5.2.2)) + jest-config: 29.7.0(@types/node@22.1.0)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.2.2)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -6400,7 +6405,7 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-config-neon@0.1.47(eslint@8.42.0)(jest@29.7.0(@types/node@20.2.5)(ts-node@10.9.1(@types/node@20.2.5)(typescript@5.2.2)))(svelte@3.59.2)(typescript@5.2.2): + eslint-config-neon@0.1.47(eslint@8.42.0)(jest@29.7.0(@types/node@22.1.0)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.2.2)))(svelte@3.59.2)(typescript@5.2.2): dependencies: '@angular-eslint/eslint-plugin': 16.0.3(eslint@8.42.0)(typescript@5.2.2) '@angular-eslint/eslint-plugin-template': 16.0.3(eslint@8.42.0)(typescript@5.2.2) @@ -6415,7 +6420,7 @@ snapshots: eslint-plugin-astro: 0.26.2(eslint@8.42.0) eslint-plugin-cypress: 2.13.3(eslint@8.42.0) eslint-plugin-import: eslint-plugin-i@2.29.1(@typescript-eslint/parser@5.59.9(eslint@8.42.0)(typescript@5.2.2))(eslint-import-resolver-typescript@3.5.5)(eslint@8.42.0) - eslint-plugin-jest: 27.2.1(@typescript-eslint/eslint-plugin@5.59.9(@typescript-eslint/parser@5.59.9(eslint@8.42.0)(typescript@5.2.2))(eslint@8.42.0)(typescript@5.2.2))(eslint@8.42.0)(jest@29.7.0(@types/node@20.2.5)(ts-node@10.9.1(@types/node@20.2.5)(typescript@5.2.2)))(typescript@5.2.2) + eslint-plugin-jest: 27.2.1(@typescript-eslint/eslint-plugin@5.59.9(@typescript-eslint/parser@5.59.9(eslint@8.42.0)(typescript@5.2.2))(eslint@8.42.0)(typescript@5.2.2))(eslint@8.42.0)(jest@29.7.0(@types/node@22.1.0)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.2.2)))(typescript@5.2.2) eslint-plugin-jsdoc: 43.2.0(eslint@8.42.0) eslint-plugin-jsx-a11y: 6.7.1(eslint@8.42.0) eslint-plugin-lodash: 7.4.0(eslint@8.42.0) @@ -6567,24 +6572,24 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-jest@27.2.1(@typescript-eslint/eslint-plugin@5.59.9(@typescript-eslint/parser@5.59.9(eslint@8.42.0)(typescript@5.2.2))(eslint@8.42.0)(typescript@5.2.2))(eslint@8.42.0)(jest@29.7.0(@types/node@20.2.5)(ts-node@10.9.1(@types/node@20.2.5)(typescript@5.2.2)))(typescript@5.2.2): + eslint-plugin-jest@27.2.1(@typescript-eslint/eslint-plugin@5.59.9(@typescript-eslint/parser@5.59.9(eslint@8.42.0)(typescript@5.2.2))(eslint@8.42.0)(typescript@5.2.2))(eslint@8.42.0)(jest@29.7.0(@types/node@22.1.0)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.2.2)))(typescript@5.2.2): dependencies: '@typescript-eslint/utils': 5.59.9(eslint@8.42.0)(typescript@5.2.2) eslint: 8.42.0 optionalDependencies: '@typescript-eslint/eslint-plugin': 5.59.9(@typescript-eslint/parser@5.59.9(eslint@8.42.0)(typescript@5.2.2))(eslint@8.42.0)(typescript@5.2.2) - jest: 29.7.0(@types/node@20.2.5)(ts-node@10.9.1(@types/node@20.2.5)(typescript@5.2.2)) + jest: 29.7.0(@types/node@22.1.0)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.2.2)) transitivePeerDependencies: - supports-color - typescript - eslint-plugin-jest@27.2.1(@typescript-eslint/eslint-plugin@6.20.0(@typescript-eslint/parser@6.20.0(eslint@8.42.0)(typescript@5.2.2))(eslint@8.42.0)(typescript@5.2.2))(eslint@8.42.0)(jest@29.7.0(@types/node@20.2.5)(ts-node@10.9.1(@types/node@20.2.5)(typescript@5.2.2)))(typescript@5.2.2): + eslint-plugin-jest@27.2.1(@typescript-eslint/eslint-plugin@6.20.0(@typescript-eslint/parser@6.20.0(eslint@8.42.0)(typescript@5.2.2))(eslint@8.42.0)(typescript@5.2.2))(eslint@8.42.0)(jest@29.7.0(@types/node@22.1.0)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.2.2)))(typescript@5.2.2): dependencies: '@typescript-eslint/utils': 5.59.9(eslint@8.42.0)(typescript@5.2.2) eslint: 8.42.0 optionalDependencies: '@typescript-eslint/eslint-plugin': 6.20.0(@typescript-eslint/parser@6.20.0(eslint@8.42.0)(typescript@5.2.2))(eslint@8.42.0)(typescript@5.2.2) - jest: 29.7.0(@types/node@20.2.5)(ts-node@10.9.1(@types/node@20.2.5)(typescript@5.2.2)) + jest: 29.7.0(@types/node@22.1.0)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.2.2)) transitivePeerDependencies: - supports-color - typescript @@ -7586,7 +7591,7 @@ snapshots: '@jest/expect': 29.5.0 '@jest/test-result': 29.5.0 '@jest/types': 29.5.0 - '@types/node': 20.2.5 + '@types/node': 22.1.0 chalk: 4.1.2 co: 4.6.0 dedent: 0.7.0 @@ -7611,7 +7616,7 @@ snapshots: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.2.5 + '@types/node': 22.1.0 chalk: 4.1.2 co: 4.6.0 dedent: 1.5.1 @@ -7631,16 +7636,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@20.2.5)(ts-node@10.9.1(@types/node@20.2.5)(typescript@5.2.2)): + jest-cli@29.7.0(@types/node@22.1.0)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.2.2)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.1(@types/node@20.2.5)(typescript@5.2.2)) + '@jest/core': 29.7.0(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.2.2)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.2.5)(ts-node@10.9.1(@types/node@20.2.5)(typescript@5.2.2)) + create-jest: 29.7.0(@types/node@22.1.0)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.2.2)) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@20.2.5)(ts-node@10.9.1(@types/node@20.2.5)(typescript@5.2.2)) + jest-config: 29.7.0(@types/node@22.1.0)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.2.2)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -7650,7 +7655,7 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@20.2.5)(ts-node@10.9.1(@types/node@20.2.5)(typescript@5.2.2)): + jest-config@29.7.0(@types/node@22.1.0)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.2.2)): dependencies: '@babel/core': 7.22.1 '@jest/test-sequencer': 29.7.0 @@ -7675,8 +7680,8 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 20.2.5 - ts-node: 10.9.1(@types/node@20.2.5)(typescript@5.2.2) + '@types/node': 22.1.0 + ts-node: 10.9.1(@types/node@22.1.0)(typescript@5.2.2) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -7720,7 +7725,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.2.5 + '@types/node': 22.1.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -7732,7 +7737,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.6 - '@types/node': 20.2.5 + '@types/node': 22.1.0 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -7748,7 +7753,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.6 - '@types/node': 20.2.5 + '@types/node': 22.1.0 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -7806,13 +7811,13 @@ snapshots: jest-mock@29.5.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 20.2.5 + '@types/node': 22.1.0 jest-util: 29.7.0 jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 20.2.5 + '@types/node': 22.1.0 jest-util: 29.7.0 jest-pnp-resolver@1.2.3(jest-resolve@29.5.0): @@ -7865,7 +7870,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.2.5 + '@types/node': 22.1.0 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -7893,7 +7898,7 @@ snapshots: '@jest/test-result': 29.5.0 '@jest/transform': 29.5.0 '@jest/types': 29.6.3 - '@types/node': 20.2.5 + '@types/node': 22.1.0 chalk: 4.1.2 cjs-module-lexer: 1.2.2 collect-v8-coverage: 1.0.1 @@ -7920,7 +7925,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.2.5 + '@types/node': 22.1.0 chalk: 4.1.2 cjs-module-lexer: 1.2.2 collect-v8-coverage: 1.0.1 @@ -7994,7 +7999,7 @@ snapshots: jest-util@29.5.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 20.2.5 + '@types/node': 22.1.0 chalk: 4.1.2 ci-info: 3.8.0 graceful-fs: 4.2.11 @@ -8003,7 +8008,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 20.2.5 + '@types/node': 22.1.0 chalk: 4.1.2 ci-info: 3.8.0 graceful-fs: 4.2.11 @@ -8022,7 +8027,7 @@ snapshots: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.2.5 + '@types/node': 22.1.0 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -8031,17 +8036,17 @@ snapshots: jest-worker@29.7.0: dependencies: - '@types/node': 20.2.5 + '@types/node': 22.1.0 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@20.2.5)(ts-node@10.9.1(@types/node@20.2.5)(typescript@5.2.2)): + jest@29.7.0(@types/node@22.1.0)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.2.2)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.1(@types/node@20.2.5)(typescript@5.2.2)) + '@jest/core': 29.7.0(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.2.2)) '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@20.2.5)(ts-node@10.9.1(@types/node@20.2.5)(typescript@5.2.2)) + jest-cli: 29.7.0(@types/node@22.1.0)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.2.2)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -9519,11 +9524,11 @@ snapshots: dependencies: typescript: 5.2.2 - ts-jest@29.1.1(@babel/core@7.22.1)(@jest/types@29.5.0)(babel-jest@29.7.0(@babel/core@7.22.1))(jest@29.7.0(@types/node@20.2.5)(ts-node@10.9.1(@types/node@20.2.5)(typescript@5.2.2)))(typescript@5.2.2): + ts-jest@29.1.1(@babel/core@7.22.1)(@jest/types@29.5.0)(babel-jest@29.7.0(@babel/core@7.22.1))(jest@29.7.0(@types/node@22.1.0)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.2.2)))(typescript@5.2.2): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@20.2.5)(ts-node@10.9.1(@types/node@20.2.5)(typescript@5.2.2)) + jest: 29.7.0(@types/node@22.1.0)(ts-node@10.9.1(@types/node@22.1.0)(typescript@5.2.2)) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -9536,14 +9541,14 @@ snapshots: '@jest/types': 29.5.0 babel-jest: 29.7.0(@babel/core@7.22.1) - ts-node@10.9.1(@types/node@20.2.5)(typescript@5.2.2): + ts-node@10.9.1(@types/node@22.1.0)(typescript@5.2.2): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.9 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 20.2.5 + '@types/node': 22.1.0 acorn: 8.8.2 acorn-walk: 8.2.0 arg: 4.1.3 @@ -9621,6 +9626,8 @@ snapshots: has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 + undici-types@6.13.0: {} + unified-engine@10.1.0: dependencies: '@types/concat-stream': 2.0.0 diff --git a/src/matcher/regexp/RegExpMatcher.ts b/src/matcher/regexp/RegExpMatcher.ts index 3b6c562..e744f43 100644 --- a/src/matcher/regexp/RegExpMatcher.ts +++ b/src/matcher/regexp/RegExpMatcher.ts @@ -1,7 +1,7 @@ +import { isHighSurrogate, isLowSurrogate } from '../../util/Char'; import { compilePatternToRegExp, potentiallyMatchesEmptyString } from '../../pattern/Util'; import { TransformerSet } from '../../transformer/TransformerSet'; import type { TransformerContainer } from '../../transformer/Transformers'; -import { isHighSurrogate, isLowSurrogate } from '../../util/Char'; import { CharacterIterator } from '../../util/CharacterIterator'; import type { BlacklistedTerm } from '../BlacklistedTerm'; import { IntervalCollection } from '../IntervalCollection'; @@ -86,26 +86,29 @@ export class RegExpMatcher implements Matcher { public getAllMatches(input: string, sorted = false) { const whitelistedIntervals = this.getWhitelistedIntervals(input); - const [indices, transformed] = this.applyTransformers(input, this.blacklistMatcherTransformers); + const [transformedToOrigIndex, transformed] = this.applyTransformers(input, this.blacklistMatcherTransformers); const matches: MatchPayload[] = []; for (const blacklistedTerm of this.blacklistedTerms) { for (const match of transformed.matchAll(blacklistedTerm.regExp)) { - const matchLength = [...match[0]].length; // spread so we count code points, not code units - const startIndex = indices[match.index!]; - // eslint-disable-next-line @typescript-eslint/restrict-plus-operands - let endIndex = indices[match.index! + matchLength - 1]; - // Adjust the end index if needed. + const origStartIndex = transformedToOrigIndex[match.index!]; + let origEndIndex = transformedToOrigIndex[match.index! + match[0].length - 1]; + // End index is (unfortunately) inclusive, so adjust as necessary. if ( - endIndex < transformed.length - 1 && // not the last character - isHighSurrogate(transformed.charCodeAt(endIndex)) && // character is a high surrogate - isLowSurrogate(transformed.charCodeAt(endIndex + 1)) // next character is a low surrogate + origEndIndex < input.length - 1 && // not the last character + isHighSurrogate(input.charCodeAt(origEndIndex)) && // character is a high surrogate + isLowSurrogate(input.charCodeAt(origEndIndex + 1)) // next character is a low surrogate ) { - endIndex++; + origEndIndex++; } - if (!whitelistedIntervals.query(startIndex, endIndex)) { - matches.push({ termId: blacklistedTerm.id, startIndex, endIndex, matchLength }); + if (!whitelistedIntervals.query(origStartIndex, origEndIndex)) { + matches.push({ + termId: blacklistedTerm.id, + startIndex: origStartIndex, + endIndex: origEndIndex, + matchLength: [...match[0]].length, + }); } } } @@ -116,23 +119,21 @@ export class RegExpMatcher implements Matcher { public hasMatch(input: string) { const whitelistedIntervals = this.getWhitelistedIntervals(input); - const [indices, transformed] = this.applyTransformers(input, this.blacklistMatcherTransformers); + const [transformedToOrigIndex, transformed] = this.applyTransformers(input, this.blacklistMatcherTransformers); for (const blacklistedTerm of this.blacklistedTerms) { for (const match of transformed.matchAll(blacklistedTerm.regExp)) { - const matchLength = [...match[0]].length; // spread so we count code points, not code units - const startIndex = indices[match.index!]; - // eslint-disable-next-line @typescript-eslint/restrict-plus-operands - let endIndex = indices[match.index! + matchLength - 1]; - // Adjust the end index if needed. + const origStartIndex = transformedToOrigIndex[match.index!]; + let origEndIndex = transformedToOrigIndex[match.index! + match[0].length - 1]; + // End index is (unfortunately) inclusive, so adjust as necessary. if ( - endIndex < transformed.length - 1 && // not the last character - isHighSurrogate(transformed.charCodeAt(endIndex)) && // character is a high surrogate - isLowSurrogate(transformed.charCodeAt(endIndex + 1)) // next character is a low surrogate + origEndIndex < input.length - 1 && // not the last character + isHighSurrogate(input.charCodeAt(origEndIndex)) && // character is a high surrogate + isLowSurrogate(input.charCodeAt(origEndIndex + 1)) // next character is a low surrogate ) { - endIndex++; + origEndIndex++; } - if (!whitelistedIntervals.query(startIndex, endIndex)) return true; + if (!whitelistedIntervals.query(origStartIndex, origEndIndex)) return true; } } @@ -141,27 +142,25 @@ export class RegExpMatcher implements Matcher { private getWhitelistedIntervals(input: string) { const matches = new IntervalCollection(); - const [origIndices, transformed] = this.applyTransformers(input, this.whitelistMatcherTransformers); + const [transformedToOrigIndex, transformed] = this.applyTransformers(input, this.whitelistMatcherTransformers); for (const whitelistedTerm of this.whitelistedTerms) { - const length = [...whitelistedTerm].length; - let lastEnd = 0; for ( let startIndex = transformed.indexOf(whitelistedTerm, lastEnd); startIndex !== -1; startIndex = transformed.indexOf(whitelistedTerm, lastEnd) ) { - let origEndIndex = origIndices[startIndex + length - 1]; - // Adjust the end index if needed. + let origEndIndex = transformedToOrigIndex[startIndex + whitelistedTerm.length - 1]; + // End index is (unfortunately) inclusive, so adjust as necessary. if ( - origEndIndex < transformed.length - 1 && // not the last character - isHighSurrogate(transformed.charCodeAt(origEndIndex)) && // character is a high surrogate - isLowSurrogate(transformed.charCodeAt(origEndIndex + 1)) // next character is a low surrogate + origEndIndex < input.length - 1 && // not the last character + isHighSurrogate(input.charCodeAt(origEndIndex)) && // character is a high surrogate + isLowSurrogate(input.charCodeAt(origEndIndex + 1)) // next character is a low surrogate ) { origEndIndex++; } - matches.insert(origIndices[startIndex], origEndIndex); + matches.insert(transformedToOrigIndex[startIndex], origEndIndex); lastEnd = startIndex + whitelistedTerm.length; } } @@ -169,20 +168,23 @@ export class RegExpMatcher implements Matcher { return matches; } - private applyTransformers(input: string, transformers: TransformerSet): [indices: number[], transformed: string] { - const indices: number[] = []; + private applyTransformers( + input: string, + transformers: TransformerSet, + ): [transformedToOrigIndex: number[], transformed: string] { + const transformedToOrigIndex: number[] = []; let transformed = ''; const iter = new CharacterIterator(input); for (const char of iter) { const transformedChar = transformers.applyTo(char); if (transformedChar !== undefined) { - indices.push(iter.position); transformed += String.fromCodePoint(transformedChar); + while (transformedToOrigIndex.length < transformed.length) transformedToOrigIndex.push(iter.position); } } transformers.resetAll(); - return [indices, transformed]; + return [transformedToOrigIndex, transformed]; } private compileTerms(terms: BlacklistedTerm[]) { diff --git a/test/matcher/regexp/RegExpMatcher.test.ts b/test/matcher/regexp/RegExpMatcher.test.ts index f1c138a..0bed4cd 100644 --- a/test/matcher/regexp/RegExpMatcher.test.ts +++ b/test/matcher/regexp/RegExpMatcher.test.ts @@ -55,6 +55,12 @@ describe('matching', () => { ['should only match on the term exactly', ['her'], 'h he! her', { 0: [[6, 8, 3]] }], ['should work with terms that normalize to a different string', ['豈'], '豈', { 0: [[0, 0, 1]] }], ['should work with the null character', ['\u0000'], '\u0000', { 0: [[0, 0, 1]] }], + [ + 'issue #71: correct match indices with codepoints encoded as multiple UTF-16 code units', + ['ass'], + '🤣ass', + { 0: [[2, 4, 3]] }, + ], ])('%s', (_, patterns, input, matches) => { const expected: MatchPayload[] = []; for (const [idStr, matchData] of Object.entries(matches)) {