From 25128e97a5b55da267c9a03cca0e10517b6d877b Mon Sep 17 00:00:00 2001 From: okwang Date: Wed, 10 May 2023 11:23:15 +0800 Subject: [PATCH 1/3] feat: enhance typoes --- benchmark/benchmark.ts | 4 ++-- src/rules/noLookAheadLookBehindRegExp.test.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/benchmark/benchmark.ts b/benchmark/benchmark.ts index c5601bd..1489b6f 100644 --- a/benchmark/benchmark.ts +++ b/benchmark/benchmark.ts @@ -21,7 +21,7 @@ const config = { const eslint = new ESLint(config); const benchmark = new Benchmark( - "ESLint self benchmark w/o browserlist", + "ESLint self benchmark w/o browserslist", (deferred: { resolve: Function }) => { eslint .lintFiles("src/**/*.ts") @@ -63,7 +63,7 @@ eslint }); const browserlistBenchmark = new Benchmark( - "ESLint self benchmark with browserlist", + "ESLint self benchmark with browserslist", (deferred: { resolve: Function; reject: Function }) => { eslint .lintFiles("src/**/*.ts") diff --git a/src/rules/noLookAheadLookBehindRegExp.test.ts b/src/rules/noLookAheadLookBehindRegExp.test.ts index 62a3f79..6b8a3ca 100644 --- a/src/rules/noLookAheadLookBehindRegExp.test.ts +++ b/src/rules/noLookAheadLookBehindRegExp.test.ts @@ -1,7 +1,7 @@ import { RuleTester } from "eslint"; import { noLookaheadLookbehindRegexp } from "./noLookaheadLookbehindRegex"; -// Rule tester for when no browserlist is passed, so lookahead and lookbehind should not be allowed +// Rule tester for when no browserslist is passed, so lookahead and lookbehind should not be allowed const tester = new RuleTester({ parser: require.resolve("@typescript-eslint/parser"), parserOptions: { From 479aa944718791011d4bfa6522881754a073c8ad Mon Sep 17 00:00:00 2001 From: okwang Date: Wed, 10 May 2023 11:30:46 +0800 Subject: [PATCH 2/3] feat: fix browserslist support, added options to disable browserlist feature New features: - new option to disable browserslist support Fixed: - fix browserslist feature, eg: report lookahead errors even though lookahead is supported Breaking Changes: - changed rule options format --- README.md | 20 ++++--- package.json | 3 +- ...yzeRegExpForLookaheadAndLookbehind.test.ts | 8 +-- src/helpers/caniuse.ts | 27 ++++----- src/helpers/createReport.ts | 6 +- src/rules/noLookaheadLookbehindRegex.ts | 57 ++++++++++++------- 6 files changed, 69 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index a112b5b..b9da0bd 100644 --- a/README.md +++ b/README.md @@ -39,28 +39,32 @@ We use [browserslist](https://github.com/browserslist/browserslist) to resolve b ## 4. Customizing rules -By default, the plugin will report on all lookahead and lookbehind regexp as well as their negative counterparts. To enable only individual rules like erroring only on lookbehind expressions, you can pass a list of rules that you wish to enable as options in your eslint. **Note that once a single rule is passed as a configuration option, all of the other rules are disabled by default and you are in full control.** +By default, the plugin will report on all lookahead and lookbehind regexp as well as their negative counterparts(if they are not supported with above browserslist target settings). To enable only individual rules like erroring only on lookbehind expressions, you can pass a list of rules that you wish to enable as options in your eslint. **Note that once a single rule is passed as a configuration option, all of the other rules are disabled by default and you are in full control.** ```js rules: { 'no-lookahead-lookbehind-regexp/no-lookahead-lookbehind-regexp': [ 'error', - 'no-lookahead', - 'no-lookbehind', - 'no-negative-lookahead', - 'no-negative-lookbehind', + { + 'no-lookahead': 1, + 'no-negative-lookbehind': 1, + } ], } ``` -As an example, passing both no-lookbehind and no-negative-lookbehind as options will cause the plugin to error on all lookbehind and negative lookbehind expressions, but it will not cause it to report errors on lookahead or negative lookahead expressions. +## 5. Disable Browserslist Support + +By default, the plugin will use yours project's browserslist settings to find availability of lookahead/lookbehind and their negative bros. However, if you want to disable this feature to report all usages as errors, you can pass a second rule options as false. ```js rules: { 'no-lookahead-lookbehind-regexp/no-lookahead-lookbehind-regexp': [ 'error', - 'no-lookbehind', - 'no-negative-lookbehind', + { + 'no-lookahead': 1, + }, + false ], } ``` diff --git a/package.json b/package.json index 13c8dbc..95d5d0b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-no-lookahead-lookbehind-regexp", - "version": "0.1.0", + "version": "1.0.0", "main": "lib/index.js", "types": "lib/index.d.ts", "repository": "git@github.com:JonasBa/eslint-plugin-no-lookahead-lookbehind-regexp.git", @@ -12,7 +12,6 @@ }, "scripts": { "benchmark": "ts-node test/benchmark.ts", - "prepare": "yarn test && yarn build", "build": "rm -rf ./lib && yarn tsc", "tsc": "tsc", "test": "jest", diff --git a/src/helpers/analyzeRegExpForLookaheadAndLookbehind.test.ts b/src/helpers/analyzeRegExpForLookaheadAndLookbehind.test.ts index c4520ee..e45d6bf 100644 --- a/src/helpers/analyzeRegExpForLookaheadAndLookbehind.test.ts +++ b/src/helpers/analyzeRegExpForLookaheadAndLookbehind.test.ts @@ -11,7 +11,7 @@ describe("analyzeRegExpForLookaheadAndLookbehind", () => { for (const group of groups) { expect( analyzeRegExpForLookaheadAndLookbehind(`\\(${group}`, { - rules: getExpressionsToCheckFromConfiguration([]), + rules: getExpressionsToCheckFromConfiguration([]).rules, }).length ).toBe(0); } @@ -25,7 +25,7 @@ describe("analyzeRegExpForLookaheadAndLookbehind", () => { ])(`Single match %s - at %i`, (type, position, expression) => { expect( analyzeRegExpForLookaheadAndLookbehind(expression, { - rules: getExpressionsToCheckFromConfiguration([]), + rules: getExpressionsToCheckFromConfiguration([]).rules, })[0] ).toEqual({ type: type.replace("negative ", ""), @@ -42,7 +42,7 @@ describe("analyzeRegExpForLookaheadAndLookbehind", () => { ])(`Multiple match %s - at %i and %i`, (type, first, second, expression) => { expect( analyzeRegExpForLookaheadAndLookbehind(expression, { - rules: getExpressionsToCheckFromConfiguration([]), + rules: getExpressionsToCheckFromConfiguration([]).rules, }) ).toEqual([ { @@ -83,7 +83,7 @@ describe("analyzeRegExpForLookaheadAndLookbehind", () => { } else { expect( analyzeRegExpForLookaheadAndLookbehind(expressions[expression], { - rules: getExpressionsToCheckFromConfiguration([]), + rules: getExpressionsToCheckFromConfiguration([]).rules, }) ).toEqual([ { diff --git a/src/helpers/caniuse.ts b/src/helpers/caniuse.ts index f6f14a0..8866737 100644 --- a/src/helpers/caniuse.ts +++ b/src/helpers/caniuse.ts @@ -14,6 +14,8 @@ export function collectBrowserTargets( configPath: string, config?: { production: string[]; development: string[] } | Array | string ): { targets: BrowserTarget[]; hasConfig: boolean } { + const browserslistConfig = browserslist.findConfig(configPath); + const hasConfig = (browserslistConfig && browserslistConfig.defaults.length > 0) || false; const targets = new Set(); function addTarget(target: string): void { @@ -35,26 +37,26 @@ export function collectBrowserTargets( }).forEach(addTarget); } - // If user had eslint config and also has browserlist config, then merge the two - if (targets.size > 0 && browserslist.findConfig(configPath)) { + // If user had eslint config and also has browserslist config, then merge the two + if (targets.size > 0 && hasConfig) { browserslist(undefined, { path: configPath }).forEach(addTarget); - return { targets: Array.from(targets).map(transformTarget), hasConfig: true }; + return { targets: Array.from(targets).map(transformTarget), hasConfig }; } // If they only use an eslint config, then return what we have - if (targets.size > 0 && !browserslist.findConfig(configPath)) { - return { targets: Array.from(targets).map(transformTarget), hasConfig: true }; + if (targets.size > 0 && !hasConfig) { + return { targets: Array.from(targets).map(transformTarget), hasConfig }; } // ** Warning - // If they dont use a browserlist config, then return an empty targets array and disable the use of the regexp lookahead and lookbehind entirely. - if (!browserslist.findConfig(configPath)) { - return { targets: [], hasConfig: false }; + // If they don't use a browserslist config, then return an empty targets array and disable the use of the regexp lookahead and lookbehind entirely. + if (!hasConfig) { + return { targets: [], hasConfig }; } browserslist(undefined, { path: configPath }).forEach(addTarget); // If we couldnt find anything, return empty targets and indicate that no config was found - return { targets: Array.from(targets).map(transformTarget), hasConfig: false }; + return { targets: Array.from(targets).map(transformTarget), hasConfig }; } // Returns a list of browser targets that do not support a feature. @@ -62,8 +64,8 @@ export function collectBrowserTargets( // this can result in false positives when querying for versions that may not have been released yet (typo or user mistake) // Since the equivalent can happen in case of specifying some super old version, the proper way to possibly handle // this would be to throw an error, but since I dont know how often that happens or if it may cause false positives later on -// if caniuse db changes... I'm leaning towards throwing an error here, but it's not the plugin's responsability to validate browserlist config - opinions are welcome. -// TODO: check if browserlist throws an error lower in the stack if config is invalid, this would likely be the best solution +// if caniuse db changes... I'm leaning towards throwing an error here, but it's not the plugin's responsability to validate browserslist config - opinions are welcome. +// TODO: check if browserslist throws an error lower in the stack if config is invalid, this would likely be the best solution export function collectUnsupportedTargets(id: string, targets: BrowserTarget[]): BrowserTarget[] { const data = lite.feature(lite.features[id]); @@ -114,8 +116,7 @@ export function resolveCaniUseBrowserTarget(target: string): string { export function formatLinterMessage( violators: ReturnType, - targets: ReturnType -): string { + targets: ReturnType): string { // If browser has no targets and we still want to report an error, it means that the feature is banned from use. if (!targets.length) { if (violators.length === 1) { diff --git a/src/helpers/createReport.ts b/src/helpers/createReport.ts index 711858a..760d9de 100644 --- a/src/helpers/createReport.ts +++ b/src/helpers/createReport.ts @@ -1,14 +1,14 @@ import * as ESTree from "estree"; import { Rule } from "eslint"; -import { analyzeRegExpForLookaheadAndLookbehind } from "./../helpers/analyzeRegExpForLookAheadAndLookbehind"; -import { collectUnsupportedTargets, formatLinterMessage } from "./../helpers/caniuse"; +import { analyzeRegExpForLookaheadAndLookbehind } from "../helpers/analyzeRegExpForLookaheadAndLookbehind"; +import { collectUnsupportedTargets, formatLinterMessage } from "../helpers/caniuse"; export function createContextReport( node: ESTree.Literal & Rule.NodeParentExtension, context: Rule.RuleContext, violators: ReturnType, - targets: ReturnType + targets: ReturnType, ): void { context.report({ node: node, diff --git a/src/rules/noLookaheadLookbehindRegex.ts b/src/rules/noLookaheadLookbehindRegex.ts index b83bfe6..d4353da 100644 --- a/src/rules/noLookaheadLookbehindRegex.ts +++ b/src/rules/noLookaheadLookbehindRegex.ts @@ -5,9 +5,9 @@ import { analyzeRegExpForLookaheadAndLookbehind, AnalyzeOptions, CheckableExpression, -} from "./../helpers/analyzeRegExpForLookAheadAndLookbehind"; -import { collectBrowserTargets, collectUnsupportedTargets } from "./../helpers/caniuse"; -import { isStringLiteralRegExp, isRegExpLiteral } from "./../helpers/ast"; +} from "../helpers/analyzeRegExpForLookaheadAndLookbehind"; +import { collectBrowserTargets, collectUnsupportedTargets } from "../helpers/caniuse"; +import { isStringLiteralRegExp, isRegExpLiteral } from "../helpers/ast"; import { createContextReport } from "../helpers/createReport"; export const DEFAULT_OPTIONS: AnalyzeOptions["rules"] = { @@ -18,20 +18,23 @@ export const DEFAULT_OPTIONS: AnalyzeOptions["rules"] = { }; export const getExpressionsToCheckFromConfiguration = ( - options: Rule.RuleContext["options"] -): AnalyzeOptions["rules"] => { - if (!options.length) return DEFAULT_OPTIONS; + conf: Rule.RuleContext["options"] +): { rules: AnalyzeOptions["rules"]; useBrowserslist: boolean } => { + if (!conf.length) return { rules: DEFAULT_OPTIONS, useBrowserslist: true }; + const [options, useBrowserslist = true] = conf as [AnalyzeOptions["rules"], boolean]; - const validOptions: CheckableExpression[] = options.filter((option: unknown) => { + const validOptions: CheckableExpression[] = ( + Object.keys(options || {}) as CheckableExpression[] + ).filter((option: unknown) => { if (typeof option !== "string") return false; - return DEFAULT_OPTIONS[option as keyof typeof DEFAULT_OPTIONS]; + return option in DEFAULT_OPTIONS && options[option as keyof typeof DEFAULT_OPTIONS]; }); if (!validOptions.length) { - return DEFAULT_OPTIONS; + return { rules: DEFAULT_OPTIONS, useBrowserslist }; } - return validOptions.reduce( + const expressions = validOptions.reduce( (acc: AnalyzeOptions["rules"], opt) => { acc[opt as keyof typeof DEFAULT_OPTIONS] = 1; return acc; @@ -43,12 +46,17 @@ export const getExpressionsToCheckFromConfiguration = ( "no-negative-lookbehind": 0, } ); + return { + rules: expressions, + useBrowserslist, + }; }; const noLookaheadLookbehindRegexp: Rule.RuleModule = { meta: { docs: { - description: "disallow the use of lookahead and lookbehind regexes if unsupported by browser", + description: + "disallow the use of lookahead and lookbehind regular expressions if unsupported by browser", category: "Compatibility", recommended: true, }, @@ -57,27 +65,32 @@ const noLookaheadLookbehindRegexp: Rule.RuleModule = { create(context: Rule.RuleContext) { const browsers = context.settings.browsers || context.settings.targets; const { targets, hasConfig } = collectBrowserTargets(context.getFilename(), browsers); + // Lookahead assertions are part of JavaScript's original regular expression support and are thus supported in all browsers. const unsupportedTargets = collectUnsupportedTargets("js-regexp-lookbehind", targets); - const rules = getExpressionsToCheckFromConfiguration(context.options); + const { rules, useBrowserslist } = getExpressionsToCheckFromConfiguration(context.options); - // If there are no unsupported targets resolved from the browserlist config, then we can skip this rule + // If there are no unsupported targets resolved from the browserslist config, then we can skip this rule if (!unsupportedTargets.length && hasConfig) return {}; return { Literal(node: ESTree.Literal & Rule.NodeParentExtension): void { + let input: string = ""; if (isStringLiteralRegExp(node) && typeof node.raw === "string") { - const unsupportedGroups = analyzeRegExpForLookaheadAndLookbehind( - node.raw, - { rules } // For string literals, we need to pass the raw value which includes escape characters. - ); - if (unsupportedGroups.length > 0) { - createContextReport(node, context, unsupportedGroups, unsupportedTargets); - } + // For string literals, we need to pass the raw value which includes escape characters. + input = node.raw; } else if (isRegExpLiteral(node)) { - const unsupportedGroups = analyzeRegExpForLookaheadAndLookbehind(node.regex.pattern, { + input = node.regex.pattern; + } + if (input) { + const unsupportedGroups = analyzeRegExpForLookaheadAndLookbehind(input, { rules, }); - if (unsupportedGroups.length > 0) { + if (unsupportedGroups.length === 0) return; + if (!useBrowserslist) { + createContextReport(node, context, unsupportedGroups, unsupportedTargets); + return; + } + if (unsupportedGroups.some((group) => group.type === "lookbehind")) { createContextReport(node, context, unsupportedGroups, unsupportedTargets); } } From a7315a1a10a61f3cdcd13e7f71e66c9647211d6d Mon Sep 17 00:00:00 2001 From: okwang Date: Wed, 17 May 2023 20:09:56 +0800 Subject: [PATCH 3/3] feat: keep plugin options compatible, upgrade only minor version --- README.md | 16 ++++--- package.json | 2 +- ...yzeRegExpForLookaheadAndLookbehind.test.ts | 32 +++++++------- .../analyzeRegExpForLookaheadAndLookbehind.ts | 11 ++--- src/rules/noLookaheadLookbehindRegex.ts | 42 ++++++++++++------- 5 files changed, 58 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index b9da0bd..3cbe272 100644 --- a/README.md +++ b/README.md @@ -45,26 +45,24 @@ By default, the plugin will report on all lookahead and lookbehind regexp as wel rules: { 'no-lookahead-lookbehind-regexp/no-lookahead-lookbehind-regexp': [ 'error', - { - 'no-lookahead': 1, - 'no-negative-lookbehind': 1, - } + 'no-lookahead', + 'no-lookbehind', + 'no-negative-lookahead', + 'no-negative-lookbehind', ], } ``` ## 5. Disable Browserslist Support -By default, the plugin will use yours project's browserslist settings to find availability of lookahead/lookbehind and their negative bros. However, if you want to disable this feature to report all usages as errors, you can pass a second rule options as false. +By default, the plugin will use yours project's browserslist settings to find availability of lookahead/lookbehind and their negative counterparts. However, if you want to disable this feature to report all usages(still controlled by above rules settings) as errors, you can pass an additional object options. ```js rules: { 'no-lookahead-lookbehind-regexp/no-lookahead-lookbehind-regexp': [ 'error', - { - 'no-lookahead': 1, - }, - false + 'no-lookahead', + { browserslist: false }, ], } ``` diff --git a/package.json b/package.json index 95d5d0b..2dec2d7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-no-lookahead-lookbehind-regexp", - "version": "1.0.0", + "version": "0.2.0", "main": "lib/index.js", "types": "lib/index.d.ts", "repository": "git@github.com:JonasBa/eslint-plugin-no-lookahead-lookbehind-regexp.git", diff --git a/src/helpers/analyzeRegExpForLookaheadAndLookbehind.test.ts b/src/helpers/analyzeRegExpForLookaheadAndLookbehind.test.ts index e45d6bf..d76e1f5 100644 --- a/src/helpers/analyzeRegExpForLookaheadAndLookbehind.test.ts +++ b/src/helpers/analyzeRegExpForLookaheadAndLookbehind.test.ts @@ -10,9 +10,10 @@ describe("analyzeRegExpForLookaheadAndLookbehind", () => { it("does not return false positives for an escaped sequence", () => { for (const group of groups) { expect( - analyzeRegExpForLookaheadAndLookbehind(`\\(${group}`, { - rules: getExpressionsToCheckFromConfiguration([]).rules, - }).length + analyzeRegExpForLookaheadAndLookbehind( + `\\(${group}`, + getExpressionsToCheckFromConfiguration([]).rules + ).length ).toBe(0); } }); @@ -24,9 +25,10 @@ describe("analyzeRegExpForLookaheadAndLookbehind", () => { ["negative lookbehind", 0, "(? { expect( - analyzeRegExpForLookaheadAndLookbehind(expression, { - rules: getExpressionsToCheckFromConfiguration([]).rules, - })[0] + analyzeRegExpForLookaheadAndLookbehind( + expression, + getExpressionsToCheckFromConfiguration([]).rules + )[0] ).toEqual({ type: type.replace("negative ", ""), position: position, @@ -41,9 +43,10 @@ describe("analyzeRegExpForLookaheadAndLookbehind", () => { ["negative lookbehind", 0, 8, "(? { expect( - analyzeRegExpForLookaheadAndLookbehind(expression, { - rules: getExpressionsToCheckFromConfiguration([]).rules, - }) + analyzeRegExpForLookaheadAndLookbehind( + expression, + getExpressionsToCheckFromConfiguration([]).rules + ) ).toEqual([ { type: type.replace("negative ", ""), @@ -76,15 +79,14 @@ describe("analyzeRegExpForLookaheadAndLookbehind", () => { for (const expression in expressions) { if (rule === expression) { expect( - analyzeRegExpForLookaheadAndLookbehind(expressions[expression], { - rules: { [expression]: 0 }, - }) + analyzeRegExpForLookaheadAndLookbehind(expressions[expression], { [expression]: 0 }) ).toEqual([]); } else { expect( - analyzeRegExpForLookaheadAndLookbehind(expressions[expression], { - rules: getExpressionsToCheckFromConfiguration([]).rules, - }) + analyzeRegExpForLookaheadAndLookbehind( + expressions[expression], + getExpressionsToCheckFromConfiguration([]).rules + ) ).toEqual([ { position: 0, diff --git a/src/helpers/analyzeRegExpForLookaheadAndLookbehind.ts b/src/helpers/analyzeRegExpForLookaheadAndLookbehind.ts index 32e5f6c..f65bd25 100644 --- a/src/helpers/analyzeRegExpForLookaheadAndLookbehind.ts +++ b/src/helpers/analyzeRegExpForLookaheadAndLookbehind.ts @@ -6,6 +6,7 @@ export type CheckableExpression = export type AnalyzeOptions = { rules: Partial<{ [key in `no-${CheckableExpression}`]: 0 | 1 }>; + conf: Partial<{ browserslist: boolean }>; }; type UnsupportedExpression = { @@ -16,7 +17,7 @@ type UnsupportedExpression = { function analyzeRegExpForLookaheadAndLookbehind( input: string, - options: AnalyzeOptions + rules: AnalyzeOptions["rules"] ): UnsupportedExpression[] { // Lookahead and lookbehind require min 5 characters to be useful, however // an expression like /(?=)/ which uses only 4, although not useful, can still crash an application @@ -47,7 +48,7 @@ function analyzeRegExpForLookaheadAndLookbehind( // Lookahead if (peek() === "=") { - if (options.rules["no-lookahead"]) { + if (rules["no-lookahead"]) { matchedExpressions.push({ type: "lookahead", position: start, @@ -58,7 +59,7 @@ function analyzeRegExpForLookaheadAndLookbehind( } // Negative lookahead if (peek() === "!") { - if (options.rules["no-negative-lookahead"]) { + if (rules["no-negative-lookahead"]) { matchedExpressions.push({ type: "lookahead", negative: 1, @@ -72,7 +73,7 @@ function analyzeRegExpForLookaheadAndLookbehind( // Lookbehind if (peek() === "<") { if (input.charAt(current + 2) === "=") { - if (options.rules["no-lookbehind"]) { + if (rules["no-lookbehind"]) { matchedExpressions.push({ type: "lookbehind", position: start, @@ -84,7 +85,7 @@ function analyzeRegExpForLookaheadAndLookbehind( } // Negative Lookbehind if (input.charAt(current + 2) === "!") { - if (options.rules["no-negative-lookbehind"]) { + if (rules["no-negative-lookbehind"]) { matchedExpressions.push({ type: "lookbehind", negative: 1, diff --git a/src/rules/noLookaheadLookbehindRegex.ts b/src/rules/noLookaheadLookbehindRegex.ts index d4353da..d818b64 100644 --- a/src/rules/noLookaheadLookbehindRegex.ts +++ b/src/rules/noLookaheadLookbehindRegex.ts @@ -17,21 +17,32 @@ export const DEFAULT_OPTIONS: AnalyzeOptions["rules"] = { "no-negative-lookbehind": 1, }; +export const DEFAULT_CONF: AnalyzeOptions["conf"] = { + browserslist: true, +}; + +function isPlainObject(obj: any) { + return Object.prototype.toString.call(obj) === "[object Object]"; +} + export const getExpressionsToCheckFromConfiguration = ( - conf: Rule.RuleContext["options"] -): { rules: AnalyzeOptions["rules"]; useBrowserslist: boolean } => { - if (!conf.length) return { rules: DEFAULT_OPTIONS, useBrowserslist: true }; - const [options, useBrowserslist = true] = conf as [AnalyzeOptions["rules"], boolean]; + options: Rule.RuleContext["options"] +): { rules: AnalyzeOptions["rules"]; conf: AnalyzeOptions["conf"] } => { + if (!options.length) return { rules: DEFAULT_OPTIONS, conf: DEFAULT_CONF }; + let rules: CheckableExpression[] = options; + let conf: AnalyzeOptions["conf"] = {}; + if (isPlainObject(options[options.length - 1])) { + rules = options.slice(0, -1); + conf = options[options.length - 1]; + } - const validOptions: CheckableExpression[] = ( - Object.keys(options || {}) as CheckableExpression[] - ).filter((option: unknown) => { + const validOptions: CheckableExpression[] = rules.filter((option: unknown) => { if (typeof option !== "string") return false; - return option in DEFAULT_OPTIONS && options[option as keyof typeof DEFAULT_OPTIONS]; + return DEFAULT_OPTIONS[option as keyof typeof DEFAULT_OPTIONS]; }); if (!validOptions.length) { - return { rules: DEFAULT_OPTIONS, useBrowserslist }; + return { rules: DEFAULT_OPTIONS, conf }; } const expressions = validOptions.reduce( @@ -48,7 +59,7 @@ export const getExpressionsToCheckFromConfiguration = ( ); return { rules: expressions, - useBrowserslist, + conf, }; }; @@ -67,7 +78,10 @@ const noLookaheadLookbehindRegexp: Rule.RuleModule = { const { targets, hasConfig } = collectBrowserTargets(context.getFilename(), browsers); // Lookahead assertions are part of JavaScript's original regular expression support and are thus supported in all browsers. const unsupportedTargets = collectUnsupportedTargets("js-regexp-lookbehind", targets); - const { rules, useBrowserslist } = getExpressionsToCheckFromConfiguration(context.options); + const { + rules, + conf: { browserslist }, + } = getExpressionsToCheckFromConfiguration(context.options); // If there are no unsupported targets resolved from the browserslist config, then we can skip this rule if (!unsupportedTargets.length && hasConfig) return {}; @@ -82,11 +96,9 @@ const noLookaheadLookbehindRegexp: Rule.RuleModule = { input = node.regex.pattern; } if (input) { - const unsupportedGroups = analyzeRegExpForLookaheadAndLookbehind(input, { - rules, - }); + const unsupportedGroups = analyzeRegExpForLookaheadAndLookbehind(input, rules); if (unsupportedGroups.length === 0) return; - if (!useBrowserslist) { + if (!browserslist) { createContextReport(node, context, unsupportedGroups, unsupportedTargets); return; }