regexp-match-indices
Advanced tools
Comparing version
@@ -0,1 +1,16 @@ | ||
/*! | ||
Copyright 2019 Ron Buckton | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
import "./shim/types"; |
17
auto.js
"use strict"; | ||
/*! | ||
Copyright 2019 Ron Buckton | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/* | ||
require('foo/auto') will automatically invoke the shim method. | ||
*/ | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
require("./shim/types"); | ||
@@ -7,0 +22,0 @@ const shim = require("./shim"); |
@@ -0,3 +1,18 @@ | ||
/*! | ||
Copyright 2019 Ron Buckton | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
import { RegExpExecArray } from "./types"; | ||
declare function exec(this: RegExp, string: string): RegExpExecArray | null; | ||
export = exec; |
"use strict"; | ||
/*! | ||
Copyright 2019 Ron Buckton | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
/* | ||
@@ -6,16 +21,18 @@ require('foo').implementation or require('foo/implementation') is a spec-compliant JS function, | ||
*/ | ||
const config = require("./config"); | ||
const nativeExec = require("./native"); | ||
const regexp_tree_1 = require("regexp-tree"); | ||
const nativeExec = RegExp.prototype.exec; | ||
const weakMeasurementRegExp = new WeakMap(); | ||
function exec(string) { | ||
const source = this.source; | ||
const flags = this.flags; | ||
const lastIndex = this.lastIndex; | ||
const result = nativeExec.call(this, string); | ||
return config.mode === "spec-compliant" | ||
? execSpecCompliant(this, string) | ||
: execLazy(this, string); | ||
} | ||
function execLazy(regexp, string) { | ||
const index = regexp.lastIndex; | ||
const result = nativeExec.call(regexp, string); | ||
if (result === null) | ||
return null; | ||
const hasGroups = !!result.groups; | ||
const matchStart = result.index; | ||
const matchEnd = matchStart + result[0].length; | ||
// for performance reasons, we defer getting the indices until later | ||
// For performance reasons, we defer computing the indices until later. This isn't spec compliant, | ||
// but once we compute the indices we convert the result to a data-property. | ||
let indicesArray; | ||
@@ -25,36 +42,15 @@ Object.defineProperty(result, "indices", { | ||
configurable: true, | ||
get: () => { | ||
if (indicesArray !== undefined) { | ||
return indicesArray; | ||
get() { | ||
if (indicesArray === undefined) { | ||
const { measurementRegExp, groupInfos } = getMeasurementRegExp(regexp); | ||
measurementRegExp.lastIndex = index; | ||
const measuredResult = nativeExec.call(measurementRegExp, string); | ||
if (measuredResult === null) | ||
throw new TypeError(); | ||
makeDataProperty(result, "indices", indicesArray = makeIndicesArray(measuredResult, groupInfos)); | ||
} | ||
let transformed = weakMeasurementRegExp.get(this); | ||
if (!transformed) { | ||
transformed = transformMeasurementGroups(regexp_tree_1.parse(`/${source}/${flags}`)); | ||
weakMeasurementRegExp.set(this, transformed); | ||
} | ||
const groupInfos = transformed.getExtra(); | ||
const newRegExp = transformed.toRegExp(); | ||
newRegExp.lastIndex = lastIndex; | ||
const measuredResult = nativeExec.call(newRegExp, string); | ||
if (measuredResult === null) | ||
throw new TypeError(); | ||
const newResult = []; | ||
const groups = hasGroups ? {} : undefined; | ||
newResult.groups = groups; | ||
newResult[0] = [matchStart, matchEnd]; | ||
for (const groupInfo of groupInfos) { | ||
let startIndex = matchStart; | ||
if (groupInfo.measurementGroups) { | ||
for (const measurementGroup of groupInfo.measurementGroups) { | ||
startIndex += measuredResult[measurementGroup].length; | ||
} | ||
} | ||
const endIndex = startIndex + measuredResult[groupInfo.newGroupNumber].length; | ||
const indices = [startIndex, endIndex]; | ||
newResult[groupInfo.oldGroupNumber] = indices; | ||
if (groups && groupInfo.groupName !== undefined) { | ||
groups[groupInfo.groupName] = indices; | ||
} | ||
} | ||
return indicesArray = newResult; | ||
return indicesArray; | ||
}, | ||
set(value) { | ||
makeDataProperty(result, "indices", value); | ||
} | ||
@@ -64,2 +60,69 @@ }); | ||
} | ||
function execSpecCompliant(regexp, string) { | ||
const { measurementRegExp, groupInfos } = getMeasurementRegExp(regexp); | ||
measurementRegExp.lastIndex = regexp.lastIndex; | ||
const measuredResult = nativeExec.call(measurementRegExp, string); | ||
if (measuredResult === null) | ||
return null; | ||
regexp.lastIndex = measurementRegExp.lastIndex; | ||
const result = []; | ||
makeDataProperty(result, 0, measuredResult[0]); | ||
for (const groupInfo of groupInfos) { | ||
makeDataProperty(result, groupInfo.oldGroupNumber, measuredResult[groupInfo.newGroupNumber]); | ||
} | ||
makeDataProperty(result, "index", measuredResult.index); | ||
makeDataProperty(result, "input", measuredResult.input); | ||
makeDataProperty(result, "groups", measuredResult.groups); | ||
makeDataProperty(result, "indices", makeIndicesArray(measuredResult, groupInfos)); | ||
return result; | ||
} | ||
function getMeasurementRegExp(regexp) { | ||
let transformed = weakMeasurementRegExp.get(regexp); | ||
if (!transformed) { | ||
transformed = transformMeasurementGroups(regexp_tree_1.parse(`/${regexp.source}/${regexp.flags}`)); | ||
weakMeasurementRegExp.set(regexp, transformed); | ||
} | ||
const groupInfos = transformed.getExtra(); | ||
const measurementRegExp = transformed.toRegExp(); | ||
return { measurementRegExp, groupInfos }; | ||
} | ||
function makeIndicesArray(measuredResult, groupInfos) { | ||
const matchStart = measuredResult.index; | ||
const matchEnd = matchStart + measuredResult[0].length; | ||
const hasGroups = !!measuredResult.groups; | ||
const indicesArray = []; | ||
const groups = hasGroups ? Object.create(null) : undefined; | ||
makeDataProperty(indicesArray, 0, [matchStart, matchEnd]); | ||
for (const groupInfo of groupInfos) { | ||
let indices; | ||
if (measuredResult[groupInfo.newGroupNumber] !== undefined) { | ||
let startIndex = matchStart; | ||
if (groupInfo.measurementGroups) { | ||
for (const measurementGroup of groupInfo.measurementGroups) { | ||
startIndex += measuredResult[measurementGroup].length; | ||
} | ||
} | ||
const endIndex = startIndex + measuredResult[groupInfo.newGroupNumber].length; | ||
indices = [startIndex, endIndex]; | ||
} | ||
makeDataProperty(indicesArray, groupInfo.oldGroupNumber, indices); | ||
if (groups && groupInfo.groupName !== undefined) { | ||
makeDataProperty(groups, groupInfo.groupName, indices); | ||
} | ||
} | ||
makeDataProperty(indicesArray, "groups", groups); | ||
return indicesArray; | ||
} | ||
function makeDataProperty(result, key, value) { | ||
const existingDesc = Object.getOwnPropertyDescriptor(result, key); | ||
if (existingDesc ? existingDesc.configurable : Object.isExtensible(result)) { | ||
const newDesc = { | ||
enumerable: existingDesc ? existingDesc.enumerable : true, | ||
configurable: existingDesc ? existingDesc.configurable : true, | ||
writable: true, | ||
value | ||
}; | ||
Object.defineProperty(result, key, newDesc); | ||
} | ||
} | ||
let groupRenumbers; | ||
@@ -66,0 +129,0 @@ let hasBackreferences = false; |
@@ -0,1 +1,16 @@ | ||
/*! | ||
Copyright 2019 Ron Buckton | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
import * as types from "./types"; | ||
@@ -5,4 +20,8 @@ declare function exec(regexp: RegExp, string: string): types.RegExpExecArray | null; | ||
var implementation: (this: RegExp, string: string) => RegExpExecArray | null; | ||
var native: (this: RegExp, string: string) => RegExpExecArray | null; | ||
var getPolyfill: () => (this: RegExp, string: string) => RegExpExecArray | null; | ||
var shim: () => void; | ||
var config: { | ||
mode: "lazy" | "spec-compliant"; | ||
}; | ||
} | ||
@@ -9,0 +28,0 @@ declare namespace exec { |
24
index.js
"use strict"; | ||
/* | ||
NOTE require('foo') is a spec-compliant JS or native function. However, if the function’s behavior depends on | ||
a receiver (a “this” value), then the first argument to this function will be used as that receiver. The package | ||
should indicate if this is the case in its README. | ||
*/ | ||
/*! | ||
Copyright 2019 Ron Buckton | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
const implementation = require("./implementation"); | ||
const native = require("./native"); | ||
const getPolyfill = require("./polyfill"); | ||
const shim = require("./shim"); | ||
const config = require("./config"); | ||
const polyfill = getPolyfill(); | ||
@@ -15,4 +27,6 @@ function exec(regexp, string) { | ||
exec.implementation = implementation; | ||
exec.native = native; | ||
exec.getPolyfill = getPolyfill; | ||
exec.shim = shim; | ||
exec.config = config; | ||
(function (exec) { | ||
@@ -19,0 +33,0 @@ })(exec || (exec = {})); |
{ | ||
"name": "regexp-match-indices", | ||
"version": "1.0.1", | ||
"version": "1.0.2", | ||
"description": "RegExp Match Indices polyfill", | ||
@@ -8,4 +8,4 @@ "main": "index.js", | ||
"scripts": { | ||
"clean": "tsc -b --clean ./tsconfig.json", | ||
"build": "tsc -b ./tsconfig.json", | ||
"clean": "tsc -b --clean ./tsconfig.build.json", | ||
"build": "tsc -b ./tsconfig.build.json", | ||
"test": "jest", | ||
@@ -21,4 +21,9 @@ "test-watch": "jest --watch", | ||
"devDependencies": { | ||
"@types/jest": "^22.2.0", | ||
"@types/jest": "^24.0.18", | ||
"@types/jest-expect-message": "^1.0.0", | ||
"@types/jest-matcher-utils": "^21.0.2", | ||
"jest": "^24.8.0", | ||
"jest-expect-message": "^1.0.2", | ||
"jest-extended": "^0.11.2", | ||
"jest-matcher-utils": "^24.9.0", | ||
"ts-jest": "^24.0.2", | ||
@@ -36,3 +41,3 @@ "typescript": "^3.5.3" | ||
}, | ||
"testRegex": "__tests__/.*(?<!\\.d)\\.ts$", | ||
"testRegex": "__tests__/[^_].*(?<!\\.d| copy)\\.ts$", | ||
"collectCoverage": false, | ||
@@ -46,4 +51,8 @@ "collectCoverageFrom": [ | ||
"lcov" | ||
], | ||
"setupFilesAfterEnv": [ | ||
"jest-expect-message", | ||
"jest-extended" | ||
] | ||
} | ||
} |
@@ -0,3 +1,18 @@ | ||
/*! | ||
Copyright 2019 Ron Buckton | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
import * as types from "./types"; | ||
declare function getPolyfill(): (this: RegExp, string: string) => types.RegExpExecArray | null; | ||
export = getPolyfill; |
"use strict"; | ||
/*! | ||
Copyright 2019 Ron Buckton | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
/* | ||
@@ -9,4 +24,4 @@ require('foo').getPolyfill or require('foo/polyfill') is a function that when invoked, will return | ||
*/ | ||
const nativeExec = require("./native"); | ||
const implementation = require("./implementation"); | ||
const nativeExec = RegExp.prototype.exec; | ||
function getPolyfill() { | ||
@@ -13,0 +28,0 @@ const re = new RegExp("a"); |
@@ -40,1 +40,13 @@ # regexp-match-indices | ||
``` | ||
## Configuration | ||
The polyfill can be run in two modes: `"lazy"` (default) or `"spec-compliant"`. In `"spec-compliant"` mode, the `indices` property is populated and stored on the result as soon as `exec()` is called. This can have a significant performance penalty for existing RegExp's that do not use this feature. By default, the polyfill operates in `"lazy"` mode, where the `indices` property is defined using a getter and is only computed when first requested. | ||
You can specify the configuration globally using the following: | ||
```js | ||
require("regexp-match-indices").config.mode = "spec-compliant"; // or "lazy" | ||
// or | ||
require("regexp-match-indices/config").mode = "spec-compliant"; // or "lazy" | ||
``` |
@@ -0,2 +1,17 @@ | ||
/*! | ||
Copyright 2019 Ron Buckton | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
declare function shim(): void; | ||
export = shim; |
15
shim.js
"use strict"; | ||
/*! | ||
Copyright 2019 Ron Buckton | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
/* | ||
@@ -3,0 +18,0 @@ require('foo').shim or require('foo/shim') is a function that when invoked, will call getPolyfill, |
@@ -0,8 +1,26 @@ | ||
/*! | ||
Copyright 2019 Ron Buckton | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
import * as types from "../types"; | ||
declare global { | ||
interface RegExpExecArray { | ||
readonly indices: RegExpExecIndicesArray; | ||
indices: RegExpExecIndicesArray; | ||
} | ||
interface RegExpMatchArray { | ||
indices?: RegExpExecIndicesArray; | ||
} | ||
interface RegExpExecIndicesArray extends types.RegExpExecIndicesArray { | ||
} | ||
} |
"use strict"; | ||
/* | ||
require('foo/auto') will automatically invoke the shim method. | ||
*/ | ||
/*! | ||
Copyright 2019 Ron Buckton | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
//# sourceMappingURL=types.js.map |
@@ -0,4 +1,22 @@ | ||
/*! | ||
Copyright 2019 Ron Buckton | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
export interface RegExpExecArray extends globalThis.RegExpExecArray { | ||
readonly indices: RegExpExecIndicesArray; | ||
indices: RegExpExecIndicesArray; | ||
} | ||
export interface RegExpMatchArray extends globalThis.RegExpMatchArray { | ||
indices?: RegExpExecIndicesArray; | ||
} | ||
export interface RegExpExecIndicesArray extends Array<[number, number]> { | ||
@@ -5,0 +23,0 @@ groups?: { |
15
types.js
"use strict"; | ||
/*! | ||
Copyright 2019 Ron Buckton | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
//# sourceMappingURL=types.js.map |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
91602
41.7%32
28%908
52.35%51
27.5%9
125%