mirror of
https://github.com/gorhill/uBlock.git
synced 2025-10-05 21:32:39 +02:00
Add ability to test against regex in JSONPath
expressions
Related discussion: https://github.com/uBlockOrigin/uAssets/issues/30157#issuecomment-3356048999
This commit is contained in:
@@ -49,6 +49,7 @@
|
||||
* - ^=: stringified value starts with
|
||||
* - $=: stringified value ends with
|
||||
* - *=: stringified value includes
|
||||
* - =/.../: regular expression matches
|
||||
*
|
||||
* - Examples (from "JSONPath examples" at reference link)
|
||||
* - .store.book[*].author
|
||||
@@ -376,7 +377,7 @@ export class JSONPath {
|
||||
continue;
|
||||
}
|
||||
if ( c0 === 0x27 /* ' */ ) {
|
||||
const r = this.#consumeQuotedIdentifier(query, i+1);
|
||||
const r = this.#untilChar(query, 0x27 /* ' */, i+1)
|
||||
if ( r === undefined ) { return; }
|
||||
keys.push(r.s);
|
||||
i = r.i;
|
||||
@@ -397,22 +398,27 @@ export class JSONPath {
|
||||
}
|
||||
return { s: keys.length === 1 ? keys[0] : keys, i };
|
||||
}
|
||||
#consumeQuotedIdentifier(query, i) {
|
||||
#consumeUnquotedIdentifier(query, i) {
|
||||
const match = this.#reUnquotedIdentifier.exec(query.slice(i));
|
||||
if ( match === null ) { return; }
|
||||
return match[0];
|
||||
}
|
||||
#untilChar(query, targetCharCode, i) {
|
||||
const len = query.length;
|
||||
const parts = [];
|
||||
let beg = i, end = i;
|
||||
for (;;) {
|
||||
if ( end === len ) { return; }
|
||||
const c = query.charCodeAt(end);
|
||||
if ( c === 0x27 /* ' */ ) {
|
||||
if ( c === targetCharCode ) {
|
||||
parts.push(query.slice(beg, end));
|
||||
end += 1;
|
||||
break;
|
||||
}
|
||||
if ( c === 0x5C /* \ */ && (end+1) < len ) {
|
||||
parts.push(query.slice(beg, end));
|
||||
const d = query.chatCodeAt(end+1);
|
||||
if ( d === 0x27 || d === 0x5C ) {
|
||||
const d = query.charCodeAt(end+1);
|
||||
if ( d === targetCharCode || d === 0x5C ) {
|
||||
end += 1;
|
||||
beg = end;
|
||||
}
|
||||
@@ -421,12 +427,20 @@ export class JSONPath {
|
||||
}
|
||||
return { s: parts.join(''), i: end };
|
||||
}
|
||||
#consumeUnquotedIdentifier(query, i) {
|
||||
const match = this.#reUnquotedIdentifier.exec(query.slice(i));
|
||||
if ( match === null ) { return; }
|
||||
return match[0];
|
||||
}
|
||||
#compileExpr(query, step, i) {
|
||||
if ( query.startsWith('=/', i) ) {
|
||||
const r = this.#untilChar(query, 0x2F /* / */, i+2);
|
||||
if ( r === undefined ) { return i; }
|
||||
const match = /^[i]/.exec(query.slice(r.i));
|
||||
try {
|
||||
step.rval = new RegExp(r.s, match && match[0] || undefined);
|
||||
} catch {
|
||||
return i;
|
||||
}
|
||||
step.op = 're';
|
||||
if ( match ) { r.i += match[0].length; }
|
||||
return r.i;
|
||||
}
|
||||
const match = this.#reExpr.exec(query.slice(i));
|
||||
if ( match === null ) { return i; }
|
||||
try {
|
||||
@@ -466,6 +480,7 @@ export class JSONPath {
|
||||
case '^=': outcome = `${v}`.startsWith(step.rval) === target; break;
|
||||
case '$=': outcome = `${v}`.endsWith(step.rval) === target; break;
|
||||
case '*=': outcome = `${v}`.includes(step.rval) === target; break;
|
||||
case 're': outcome = step.rval.test(`${v}`); break;
|
||||
default: outcome = hasOwn === target; break;
|
||||
}
|
||||
if ( outcome ) { return k; }
|
||||
|
Reference in New Issue
Block a user