From c8942ab8219364bdab617b360bf5d35c20d782ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Sat, 10 Jul 2021 23:30:49 +0100 Subject: [PATCH 1/5] Use native RegExp#indices --- cucumber-expressions/javascript/package.json | 1 - cucumber-expressions/javascript/src/Group.ts | 6 ++-- .../javascript/src/GroupBuilder.ts | 15 +++++----- .../javascript/src/RegexExecArray.ts | 4 --- .../javascript/src/TreeRegexp.ts | 29 ++++++++++--------- .../javascript/test/TreeRegexpTest.ts | 8 ++++- package-lock.json | 4 --- 7 files changed, 33 insertions(+), 34 deletions(-) delete mode 100644 cucumber-expressions/javascript/src/RegexExecArray.ts diff --git a/cucumber-expressions/javascript/package.json b/cucumber-expressions/javascript/package.json index cf40b0239b..d54ff26ac6 100644 --- a/cucumber-expressions/javascript/package.json +++ b/cucumber-expressions/javascript/package.json @@ -34,7 +34,6 @@ "typescript": "4.3.5" }, "dependencies": { - "becke-ch--regex--s0-0-v1--base--pl--lib": "1.4.0" }, "directories": { "test": "test" diff --git a/cucumber-expressions/javascript/src/Group.ts b/cucumber-expressions/javascript/src/Group.ts index 41787768c8..df6cb21274 100644 --- a/cucumber-expressions/javascript/src/Group.ts +++ b/cucumber-expressions/javascript/src/Group.ts @@ -1,12 +1,12 @@ export default class Group { constructor( public readonly value: string | undefined, - public readonly start: number, - public readonly end: number, + public readonly start: number | undefined, + public readonly end: number | undefined, public readonly children: readonly Group[] ) {} - get values(): string[] { + get values(): readonly string[] { return (this.children.length === 0 ? [this] : this.children).map((g) => g.value) } } diff --git a/cucumber-expressions/javascript/src/GroupBuilder.ts b/cucumber-expressions/javascript/src/GroupBuilder.ts index 75df6617be..a199e3dab6 100644 --- a/cucumber-expressions/javascript/src/GroupBuilder.ts +++ b/cucumber-expressions/javascript/src/GroupBuilder.ts @@ -1,5 +1,4 @@ import Group from './Group' -import RegexExecArray from './RegexExecArray' export default class GroupBuilder { public source: string @@ -10,15 +9,15 @@ export default class GroupBuilder { this.groupBuilders.push(groupBuilder) } - public build(match: RegexExecArray, nextGroupIndex: () => number): Group { + public build(match: RegExpExecArray, nextGroupIndex: () => number): Group { const groupIndex = nextGroupIndex() const children = this.groupBuilders.map((gb) => gb.build(match, nextGroupIndex)) - return new Group( - match[groupIndex] || undefined, - match.index[groupIndex], - match.index[groupIndex] + (match[groupIndex] || '').length, - children - ) + const value = match[groupIndex] || undefined + // @ts-ignore + const index = match.indices[groupIndex] + const start = index ? index[0] : undefined + const end = index ? index[1] : undefined + return new Group(value, start, end, children) } public setNonCapturing() { diff --git a/cucumber-expressions/javascript/src/RegexExecArray.ts b/cucumber-expressions/javascript/src/RegexExecArray.ts deleted file mode 100644 index 968885fbac..0000000000 --- a/cucumber-expressions/javascript/src/RegexExecArray.ts +++ /dev/null @@ -1,4 +0,0 @@ -export default interface RegexExecArray extends ReadonlyArray { - index: ReadonlyArray - input: string -} diff --git a/cucumber-expressions/javascript/src/TreeRegexp.ts b/cucumber-expressions/javascript/src/TreeRegexp.ts index 2cb0c9d60b..b22b4b6945 100644 --- a/cucumber-expressions/javascript/src/TreeRegexp.ts +++ b/cucumber-expressions/javascript/src/TreeRegexp.ts @@ -1,18 +1,21 @@ import GroupBuilder from './GroupBuilder' -// @ts-ignore -import Regex from 'becke-ch--regex--s0-0-v1--base--pl--lib' -import RegexExecArray from './RegexExecArray' import Group from './Group' export default class TreeRegexp { - public regexp: RegExp - private regex: any - public groupBuilder: GroupBuilder + public readonly regexp: RegExp + public readonly groupBuilder: GroupBuilder constructor(regexp: RegExp | string) { - this.regexp = 'string' === typeof regexp ? new RegExp(regexp) : regexp - this.regex = new Regex(this.regexp.source, this.regexp.flags) - this.groupBuilder = TreeRegexp.createGroupBuilder(this.regex) + if (regexp instanceof RegExp) { + if (!regexp.flags.includes('d')) { + this.regexp = new RegExp(regexp.source, regexp.flags + 'd') + } else { + this.regexp = regexp + } + } else { + this.regexp = new RegExp(regexp, 'd') + } + this.groupBuilder = TreeRegexp.createGroupBuilder(this.regexp) } private static createGroupBuilder(regexp: RegExp) { @@ -53,22 +56,22 @@ export default class TreeRegexp { private static isNonCapturing(source: string, i: number): boolean { // Regex is valid. Bounds check not required. - if (source[i + 1] != '?') { + if (source[i + 1] !== '?') { // (X) return false } - if (source[i + 2] != '<') { + if (source[i + 2] !== '<') { // (?:X) // (?=X) // (?!X) return true } // (?<=X) or (?X) - return source[i + 3] == '=' || source[i + 3] == '!' + return source[i + 3] === '=' || source[i + 3] === '!' } public match(s: string): Group | null { - const match: RegexExecArray = this.regex.exec(s) + const match = this.regexp.exec(s) if (!match) { return null } diff --git a/cucumber-expressions/javascript/test/TreeRegexpTest.ts b/cucumber-expressions/javascript/test/TreeRegexpTest.ts index 5993b866a3..29bdf4b68f 100644 --- a/cucumber-expressions/javascript/test/TreeRegexpTest.ts +++ b/cucumber-expressions/javascript/test/TreeRegexpTest.ts @@ -120,12 +120,18 @@ describe('TreeRegexp', () => { assert.strictEqual(group.children.length, 1) }) - it('works with flags', () => { + it('works with case insensitive flag', () => { const tr = new TreeRegexp(/HELLO/i) const group = tr.match('hello') assert.strictEqual(group.value, 'hello') }) + it('works with indices flag already present', () => { + const tr = new TreeRegexp(new RegExp('HELLO', 'id')) + const group = tr.match('hello') + assert.strictEqual(group.value, 'hello') + }) + it('empty capturing group', () => { const tr = new TreeRegexp(/()/) const group = tr.match('') diff --git a/package-lock.json b/package-lock.json index 5d93295e7d..5a93dc3535 100644 --- a/package-lock.json +++ b/package-lock.json @@ -78,9 +78,6 @@ "name": "@cucumber/cucumber-expressions", "version": "12.1.1", "license": "MIT", - "dependencies": { - "becke-ch--regex--s0-0-v1--base--pl--lib": "1.4.0" - }, "devDependencies": { "@types/js-yaml": "4.0.2", "@types/mocha": "8.2.3", @@ -31785,7 +31782,6 @@ "@types/js-yaml": "4.0.2", "@types/mocha": "8.2.3", "@types/node": "14.17.5", - "becke-ch--regex--s0-0-v1--base--pl--lib": "1.4.0", "js-yaml": "4.1.0", "mocha": "9.0.2", "ts-node": "10.0.0", From 66274424f72c36ed285e61baa0d02dd09448b0db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Sun, 11 Jul 2021 00:00:36 +0100 Subject: [PATCH 2/5] Keep Group#values as string[] --- cucumber-expressions/javascript/src/Group.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cucumber-expressions/javascript/src/Group.ts b/cucumber-expressions/javascript/src/Group.ts index df6cb21274..a128607b82 100644 --- a/cucumber-expressions/javascript/src/Group.ts +++ b/cucumber-expressions/javascript/src/Group.ts @@ -6,7 +6,7 @@ export default class Group { public readonly children: readonly Group[] ) {} - get values(): readonly string[] { + get values(): string[] { return (this.children.length === 0 ? [this] : this.children).map((g) => g.value) } } From bf83e580f5ea18bb2caedef640a11de2f1aed5af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Sun, 11 Jul 2021 00:13:00 +0100 Subject: [PATCH 3/5] Add polyfill --- cucumber-expressions/javascript/package.json | 1 + .../javascript/src/TreeRegexp.ts | 11 +++---- .../javascript/test/TreeRegexpTest.ts | 6 ---- package-lock.json | 33 +++++++++++++++++++ 4 files changed, 38 insertions(+), 13 deletions(-) diff --git a/cucumber-expressions/javascript/package.json b/cucumber-expressions/javascript/package.json index 3cd7d6263e..bdcdd1e8d3 100644 --- a/cucumber-expressions/javascript/package.json +++ b/cucumber-expressions/javascript/package.json @@ -34,6 +34,7 @@ "typescript": "4.3.5" }, "dependencies": { + "regexp-match-indices": "1.0.2" }, "directories": { "test": "test" diff --git a/cucumber-expressions/javascript/src/TreeRegexp.ts b/cucumber-expressions/javascript/src/TreeRegexp.ts index b22b4b6945..46dda9f9c2 100644 --- a/cucumber-expressions/javascript/src/TreeRegexp.ts +++ b/cucumber-expressions/javascript/src/TreeRegexp.ts @@ -1,5 +1,6 @@ import GroupBuilder from './GroupBuilder' import Group from './Group' +import execWithIndices from 'regexp-match-indices' export default class TreeRegexp { public readonly regexp: RegExp @@ -7,13 +8,9 @@ export default class TreeRegexp { constructor(regexp: RegExp | string) { if (regexp instanceof RegExp) { - if (!regexp.flags.includes('d')) { - this.regexp = new RegExp(regexp.source, regexp.flags + 'd') - } else { - this.regexp = regexp - } + this.regexp = regexp } else { - this.regexp = new RegExp(regexp, 'd') + this.regexp = new RegExp(regexp) } this.groupBuilder = TreeRegexp.createGroupBuilder(this.regexp) } @@ -71,7 +68,7 @@ export default class TreeRegexp { } public match(s: string): Group | null { - const match = this.regexp.exec(s) + const match = execWithIndices(this.regexp, s) if (!match) { return null } diff --git a/cucumber-expressions/javascript/test/TreeRegexpTest.ts b/cucumber-expressions/javascript/test/TreeRegexpTest.ts index 29bdf4b68f..4d452b3e40 100644 --- a/cucumber-expressions/javascript/test/TreeRegexpTest.ts +++ b/cucumber-expressions/javascript/test/TreeRegexpTest.ts @@ -126,12 +126,6 @@ describe('TreeRegexp', () => { assert.strictEqual(group.value, 'hello') }) - it('works with indices flag already present', () => { - const tr = new TreeRegexp(new RegExp('HELLO', 'id')) - const group = tr.match('hello') - assert.strictEqual(group.value, 'hello') - }) - it('empty capturing group', () => { const tr = new TreeRegexp(/()/) const group = tr.match('') diff --git a/package-lock.json b/package-lock.json index e70e9349ce..478818e124 100644 --- a/package-lock.json +++ b/package-lock.json @@ -78,6 +78,9 @@ "name": "@cucumber/cucumber-expressions", "version": "12.1.1", "license": "MIT", + "dependencies": { + "regexp-match-indices": "1.0.2" + }, "devDependencies": { "@types/js-yaml": "4.0.2", "@types/mocha": "8.2.3", @@ -25348,6 +25351,22 @@ "node": ">=0.10.0" } }, + "node_modules/regexp-match-indices": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regexp-match-indices/-/regexp-match-indices-1.0.2.tgz", + "integrity": "sha512-DwZuAkt8NF5mKwGGER1EGh2PRqyvhRhhLviH+R8y8dIuaQROlUfXjt4s9ZTXstIsSkptf06BSvwcEmmfheJJWQ==", + "dependencies": { + "regexp-tree": "^0.1.11" + } + }, + "node_modules/regexp-tree": { + "version": "0.1.23", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.23.tgz", + "integrity": "sha512-+7HWfb4Bvu8Rs2eQTUIpX9I/PlQkYOuTNbRpKLJlQpSgwSkzFYh+pUj0gtvglnOZLKB6YgnIgRuJ2/IlpL48qw==", + "bin": { + "regexp-tree": "bin/regexp-tree" + } + }, "node_modules/regexp.prototype.flags": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", @@ -31784,6 +31803,7 @@ "@types/node": "14.17.5", "js-yaml": "4.1.0", "mocha": "9.0.2", + "regexp-match-indices": "1.0.2", "ts-node": "10.1.0", "typescript": "4.3.5" }, @@ -50152,6 +50172,19 @@ "safe-regex": "^1.1.0" } }, + "regexp-match-indices": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regexp-match-indices/-/regexp-match-indices-1.0.2.tgz", + "integrity": "sha512-DwZuAkt8NF5mKwGGER1EGh2PRqyvhRhhLviH+R8y8dIuaQROlUfXjt4s9ZTXstIsSkptf06BSvwcEmmfheJJWQ==", + "requires": { + "regexp-tree": "^0.1.11" + } + }, + "regexp-tree": { + "version": "0.1.23", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.23.tgz", + "integrity": "sha512-+7HWfb4Bvu8Rs2eQTUIpX9I/PlQkYOuTNbRpKLJlQpSgwSkzFYh+pUj0gtvglnOZLKB6YgnIgRuJ2/IlpL48qw==" + }, "regexp.prototype.flags": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", From 62480673924456e327be49e008da13bfbd2427f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Sun, 11 Jul 2021 00:16:47 +0100 Subject: [PATCH 4/5] Update changelog --- cucumber-expressions/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cucumber-expressions/CHANGELOG.md b/cucumber-expressions/CHANGELOG.md index bcb3e7dbd6..9c83490168 100644 --- a/cucumber-expressions/CHANGELOG.md +++ b/cucumber-expressions/CHANGELOG.md @@ -20,6 +20,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Fixed +* Use native RegExp Match indices (currently relying on a polyfill) + ([#1652](https://github.com/cucumber/common/pull/1652) + [aslakhellesoy]) + ## [12.1.1] - 2021-04-06 ### Fixed From b82e291c9ff3e3a84ca3a01dffe80a2f1f0276cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Sun, 11 Jul 2021 00:30:02 +0100 Subject: [PATCH 5/5] Use the right type --- cucumber-expressions/javascript/src/GroupBuilder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cucumber-expressions/javascript/src/GroupBuilder.ts b/cucumber-expressions/javascript/src/GroupBuilder.ts index a199e3dab6..f8e1779dad 100644 --- a/cucumber-expressions/javascript/src/GroupBuilder.ts +++ b/cucumber-expressions/javascript/src/GroupBuilder.ts @@ -1,4 +1,5 @@ import Group from './Group' +import { RegExpExecArray } from 'regexp-match-indices' export default class GroupBuilder { public source: string @@ -13,7 +14,6 @@ export default class GroupBuilder { const groupIndex = nextGroupIndex() const children = this.groupBuilders.map((gb) => gb.build(match, nextGroupIndex)) const value = match[groupIndex] || undefined - // @ts-ignore const index = match.indices[groupIndex] const start = index ? index[0] : undefined const end = index ? index[1] : undefined