Skip to content

feat(eslint): add a Playwright concern covering specs and their config - #38

Merged
Eli Bosley (elibosley) merged 2 commits into
mainfrom
feat/playwright-timeout-lint
Aug 4, 2026
Merged

feat(eslint): add a Playwright concern covering specs and their config#38
Eli Bosley (elibosley) merged 2 commits into
mainfrom
feat/playwright-timeout-lint

Conversation

@elibosley

@elibosley Eli Bosley (elibosley) commented Aug 4, 2026

Copy link
Copy Markdown
Member

Summary

Adds an eslint/playwright concern: eslint-plugin-playwright for specs, and a
local rule for the config that runs them.

Why

Playwright defaults actionTimeout and navigationTimeout to 0, which means
no timeout rather than a sensible one. expect() carries its own default, so a
config that sets expect.timeout reads as bounded — but that ceiling only covers
assertions. A bare .click() or .goto() on an element that never becomes
actionable waits out the entire test budget.

On a suite with a 30-minute per-test budget, one unactionable click produced 24
minutes of total silence
with the machine completely idle, and the eventual
error named the test rather than the call that hung. With an explicit ceiling in
place the same run failed in ~3 minutes and named the exact selector it was
waiting on. Nothing in the upstream plugin covers this, so the config half is a
local rule.

What's in it

Specs — the plugin's recommended set (conditional logic in tests, forgotten
await on assertions, page.waitForTimeout sleeps, focused/skipped tests left
behind) plus the tier it ships but leaves out of recommended, all at error:

  • matcher choices that make a failure legible — prefer-to-be,
    prefer-comparison-matcher, prefer-equality-matcher, prefer-strict-equal,
    prefer-to-contain, require-to-throw-message
  • require-to-pass-timeout — an unbounded toPass is the same blackout shape as
    an unbounded action
  • locator quality — no-raw-locators, no-nth-methods, prefer-native-locators,
    no-get-by-title
  • structure — require-hook, require-top-level-describe, no-slowed-test
  • no-commented-out-tests

Configs (playwright*.config.*) — playwright-config/require-action-timeouts
requires both timeouts explicitly. It reads the default export through
defineConfig() and through a satisfies/as wrapper, and reports a config with
no use block at all, since that inherits both defaults just the same.

On severity

These are error deliberately. This package is the bar new work is written
against, so the standard states the target rather than the current state of any
one suite. A repo adopting mid-stream downgrades a specific rule in its own
config, which keeps the exception visible and local instead of hidden in the
shared baseline.

Pure convention stays off — prefer-lowercase-title (style), require-tags and
no-restricted-* (need a project vocabulary), require-soft-assertions (changes
failure semantics), max-expects (an arbitrary budget), and no-hooks (which
contradicts require-hook).

Scoping caveat

The plugin's rules key off bare test/expect identifiers. A repo that also runs
Vitest or Jest under **/*.{spec,test}.* should re-scope files on the spec
entry, or the rules will give Playwright advice about a unit test. Documented in
the README.

Verification

  • npm test — 36 tests, all green; ESLint and Prettier clean
  • 11 new tests across two files, following the existing Linter-based convention
  • The config rule was run against a real config that lacked both timeouts: 2
    findings on the exact use: block
  • The spec ruleset was measured against a real 63-spec suite: 318 findings, 0
    parse failures
    , so adoption is a bounded cleanup rather than a rewrite

Summary by CodeRabbit

  • New Features

    • Added an ESLint configuration export for Playwright projects.
    • Applies recommended and curated rules for test quality, locator usage, matcher practices, timeouts, and test structure.
    • Validates required action and navigation timeout settings in Playwright configuration files.
  • Documentation

    • Added guidance for enabling and scoping the Playwright configuration, including projects using multiple test runners.

Playwright defaults actionTimeout and navigationTimeout to 0, which means
"no timeout" rather than a sensible one. expect() carries its own default,
so a config that sets expect.timeout looks bounded -- but that ceiling
only covers assertions. A bare .click() or .goto() on an element that
never becomes actionable waits for the whole test timeout.

On a suite with a generous per-test budget that is a silent blackout: no
output, no failing assertion, and a final error naming the test rather
than the call that hung. A real 30-minute per-test budget turned one
unactionable click into 24 minutes of silence with the box completely
idle, and the eventual message pointed at the test, not the locator.

The rule reads the default-exported config -- through defineConfig() and
through a satisfies/as wrapper -- and reports each missing timeout. A
config with no `use` block at all is reported too, since it inherits both
defaults just the same.
Adds eslint-plugin-playwright and wires its recommended set for specs, plus
the tier it ships but leaves out of recommended -- matcher choices that make
a failure legible, require-to-pass-timeout, locator quality, and structure.
All at error: this package is the bar new work is written against, so the
standard states the target rather than the current state of any one suite. A
repo adopting mid-stream downgrades a rule in its own config, which keeps the
exception visible and local. Pure convention stays off: title casing, tag
vocabularies, soft assertions, an arbitrary expect budget, and no-hooks
(which contradicts require-hook).

The config half is a local rule, because the plugin has nothing for it.
Playwright defaults actionTimeout and navigationTimeout to 0 -- "no timeout"
rather than a sensible one. expect() carries its own default, so a config
setting expect.timeout looks bounded, but that ceiling only covers
assertions. A bare .click() or .goto() on an element that never becomes
actionable waits out the whole test budget. On a 30-minute destructive
budget that was 24 minutes of silence with the box completely idle, and the
error named the test rather than the locator; with the ceiling in place the
same run failed in 3 minutes naming the exact selector.

Measured against a real 63-spec suite: 318 findings, no parse failures.
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a public eslint/playwright export with scoped Playwright rules, configuration timeout validation, integration tests, and README documentation.

Changes

Playwright ESLint concern

Layer / File(s) Summary
Scope and package export
package.json, src/eslint/globs.js
Adds the public export, eslint-plugin-playwright, and shared globs for Playwright specs and configuration files.
Playwright rules and timeout validation
src/eslint/playwright.js, test/playwright-spec-rules.test.mjs, test/playwright-action-timeouts.test.mjs
Adds recommended and curated spec rules, validates explicit action and navigation timeouts, and tests both rule sets.
Usage documentation
README.md
Documents the export, rule choices, file scoping, and required timeout settings.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ESLint
  participant PlaywrightConfig as src/eslint/playwright.js
  participant PlaywrightPlugin as eslint-plugin-playwright
  ESLint->>PlaywrightConfig: Match Playwright spec or config file
  PlaywrightConfig->>PlaywrightPlugin: Apply recommended and curated spec rules
  PlaywrightPlugin-->>ESLint: Return spec diagnostics
  PlaywrightConfig-->>ESLint: Report missing timeout settings
Loading

Possibly related PRs

  • unraid/js-standards#28: Adds another standalone exported ESLint framework concern with scoped globs, a dedicated plugin, documentation, and tests.

Poem

A rabbit checks each test at night,
With locators clean and timeouts right.
No focused runs can hide away,
Config rules guide the path each day.
Hop, hop—Playwright joins the array!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title uses the required conventional commit format and accurately describes the new Playwright ESLint concern.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/playwright-timeout-lint
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feat/playwright-timeout-lint

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@elibosley
Eli Bosley (elibosley) merged commit a999323 into main Aug 4, 2026
2 of 3 checks passed
@elibosley
Eli Bosley (elibosley) deleted the feat/playwright-timeout-lint branch August 4, 2026 02:45

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (3)
test/playwright-spec-rules.test.mjs (1)

67-79: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the effective severity, not key absence.

The test asserts that the rule key is absent. A plugin release can add the same rule to flat/recommended with severity "off". The key then exists, the rule stays disabled, and this test fails for no real regression. Check the severity instead.

♻️ Proposed assertion change
-  const enabled = new Set(Object.keys(concern.rules));
+  const severityOf = (rule) => {
+    const entry = concern.rules[rule];
+    return Array.isArray(entry) ? entry[0] : entry;
+  };
   for (const rule of [
     "playwright/prefer-lowercase-title",
     "playwright/require-tags",
     "playwright/require-soft-assertions",
     "playwright/max-expects",
     "playwright/no-hooks",
   ]) {
-    assert.ok(!enabled.has(rule), `${rule} should stay off`);
+    const severity = severityOf(rule);
+    assert.ok(
+      severity === undefined || severity === "off" || severity === 0,
+      `${rule} should stay off, got ${severity}`,
+    );
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/playwright-spec-rules.test.mjs` around lines 67 - 79, Update the rule
loop in the test named “keeps pure-convention rules off, so strictness stays
about correctness” to inspect each rule’s configured severity in concern.rules
rather than asserting the rule key is absent. Assert that every listed rule is
effectively disabled, including when the plugin exposes it with severity "off".
src/eslint/playwright.js (1)

86-101: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Indirect use values produce false reports.

target becomes null when use is not an object literal. Two realistic shapes hit this:

  • use: sharedUse — a value imported or defined elsewhere.
  • use: { ...sharedUse, baseURL: "..." } — a spread that can carry both timeouts.

In both cases the rule reports both timeouts as missing, and the author cannot satisfy it without inlining the values. Consider skipping the report when use is not an object literal, or when the object literal contains a SpreadElement.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/eslint/playwright.js` around lines 86 - 101, Update the target selection
and timeout-reporting logic around REQUIRED_TIMEOUTS so indirect use values are
not reported as missing: skip validation when use is not an ObjectExpression,
and also skip it when that object contains a SpreadElement. Preserve the
existing per-property checks and config-object reporting for direct, fully
explicit use objects.
test/playwright-action-timeouts.test.mjs (1)

71-80: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the TypeScript wrapper paths.

configObject in src/eslint/playwright.js unwraps TSSatisfiesExpression and TSAsExpression. No test exercises those branches. The default parser used here cannot parse either syntax, so the branches stay unverified. Add a case that sets languageOptions.parser to typescript-eslint's parser and lints export default defineConfig({...}) satisfies PlaywrightTestConfig;.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/playwright-action-timeouts.test.mjs` around lines 71 - 80, Add a test in
test/playwright-action-timeouts.test.mjs that configures languageOptions.parser
with the TypeScript ESLint parser and lints an export default expression using
defineConfig({...}) satisfies PlaywrightTestConfig. Assert the expected clean
result, exercising both TSSatisfiesExpression and TSAsExpression unwrapping in
configObject.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/eslint/globs.js`:
- Around line 31-39: Extend the PLAYWRIGHT_SPEC_FILES glob’s extension set to
include mts, cts, and jsx alongside the existing TypeScript, JavaScript, and
module variants, preserving its current spec/test filename matching behavior.

In `@src/eslint/playwright.js`:
- Around line 82-84: Update the rule around ExportDefaultDeclaration to also
inspect CommonJS module.exports assignments matched by PLAYWRIGHT_CONFIG_FILES.
Extract the existing config validation logic into a local checkConfig(config)
helper, then invoke it for both default exports and module.exports values,
including defineConfig({...}) wrappers.
- Around line 156-161: Update the Playwright spec configuration around
PLAYWRIGHT_SPEC_FILES so TypeScript and TSX files are not processed without a
compatible TypeScript parser concern; either document and enforce the required
composition in the README or exclude .ts/.tsx from this entry unless the parser
is supplied through composition. Preserve the existing JavaScript Playwright
rules and recommended configuration.

---

Nitpick comments:
In `@src/eslint/playwright.js`:
- Around line 86-101: Update the target selection and timeout-reporting logic
around REQUIRED_TIMEOUTS so indirect use values are not reported as missing:
skip validation when use is not an ObjectExpression, and also skip it when that
object contains a SpreadElement. Preserve the existing per-property checks and
config-object reporting for direct, fully explicit use objects.

In `@test/playwright-action-timeouts.test.mjs`:
- Around line 71-80: Add a test in test/playwright-action-timeouts.test.mjs that
configures languageOptions.parser with the TypeScript ESLint parser and lints an
export default expression using defineConfig({...}) satisfies
PlaywrightTestConfig. Assert the expected clean result, exercising both
TSSatisfiesExpression and TSAsExpression unwrapping in configObject.

In `@test/playwright-spec-rules.test.mjs`:
- Around line 67-79: Update the rule loop in the test named “keeps
pure-convention rules off, so strictness stays about correctness” to inspect
each rule’s configured severity in concern.rules rather than asserting the rule
key is absent. Assert that every listed rule is effectively disabled, including
when the plugin exposes it with severity "off".
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d091f5b4-befd-4ebd-b8e1-951b1e4b2dec

📥 Commits

Reviewing files that changed from the base of the PR and between bf9d573 and 9a2885c.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (6)
  • README.md
  • package.json
  • src/eslint/globs.js
  • src/eslint/playwright.js
  • test/playwright-action-timeouts.test.mjs
  • test/playwright-spec-rules.test.mjs

Comment thread src/eslint/globs.js
Comment on lines +31 to +39
/**
* Playwright specs — mirrors Playwright's own default `testMatch`.
*
* The plugin's rules key off bare `test`/`expect` identifiers, so in a repo that
* also runs Vitest or Jest under this pattern, re-scope `files` on the spec entry
* to wherever Playwright actually owns (`e2e/**`, `**\/*.e2e.spec.ts`, …).
* Otherwise the rules give Playwright advice about a unit test.
*/
export const PLAYWRIGHT_SPEC_FILES = ["**/*.{spec,test}.{ts,tsx,js,mjs,cjs}"];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Playwright default testMatch pattern file extensions

💡 Result:

By default, Playwright uses the following glob pattern to identify test files: /*.@(spec|test).?(c|m)[jt]s?(x) [1][2][3]. This pattern includes the following file types and naming conventions: - File extensions:.js,.ts,.mjs,.cjs,.jsx, and.tsx [1][2]. - Naming conventions: The files must end with either.spec or.test before the file extension [1][3]. For example, files named login-screen.spec.ts or user-login.test.js would be matched by this default pattern [1][2]. Matching is performed against the absolute file path, and strings provided to the testMatch configuration option are treated as glob patterns [1][3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Tracked globs.js locations:\n'
git ls-files | grep -F 'src/eslint/globs.js' || true

printf '\nRelevant contents:\n'
if [ -f src/eslint/globs.js ]; then
  nl -ba src/eslint/globs.js | sed -n '1,80p'
fi

printf '\nSearch for PLAYWRIGHT_SPEC_FILES usage:\n'
rg -n "PLAYWRIGHT_SPEC_FILES|playwrightSpecFiles|playwright" src/eslint || true

printf '\nPackage references:\n'
rg -n '"playwright"|eslint-plugin-playwright|playwright' package.json package-lock.json pnpm-lock.yaml yarn.lock 2>/dev/null || true

printf '\nParse Playwright default-glob-like extension set vs current glob extensions:\n'
python3 - <<'PY'
from pathlib import Path
import re

txt = Path('src/eslint/globs.js').read_text() if Path('src/eslint/globs.js').exists() else ''
m = re.search(r'PLAYWRIGHT_SPEC_FILES\s*=\s*\[([^]]+)\]', txt)
current = set(re.findall(r'(?<=\\.)[A-Za-z0-9]+(?=[.,}])', m.group(1))) if m else set()
playwright_doc_extensions = {'js','ts','mjs','cjs','jsx','tsx'}

print('current_extensions_in_glob=', sorted(current))
print('doc_extensions=', sorted(playwright_doc_extensions))
print('missing_from_glob=', sorted(playwright_doc_extensions - current))
PY

Repository: unraid/js-standards

Length of output: 265


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Relevant contents:\n'
if [ -f src/eslint/globs.js ]; then
  awk '{printf "%6d\t%s\n", NR, $0}' src/eslint/globs.js | sed -n '1,80p'
fi

printf '\nSearch for PLAYWRIGHT_SPEC_FILES usage:\n'
rg -n "PLAYWRIGHT_SPEC_FILES|playwrightSpecFiles|playwright" src/eslint || true

printf '\nPackage references:\n'
for f in package.json package-lock.json pnpm-lock.yaml yarn.lock; do
  if [ -f "$f" ]; then
    rg -n '"playwright"|eslint-plugin-playwright|playwright' "$f" || true
  fi
done

printf '\nCompare current glob extensions with Playwright docs extension list:\n'
python3 - <<'PY'
from pathlib import Path
import re

txt = Path('src/eslint/globs.js').read_text()
m = re.search(r'PLAYWRIGHT_SPEC_FILES\s*=\s*\[([^]]+)\]', txt, re.S)
current = set(re.findall(r'(?<=\\)[A-Za-z0-9]+(?=[.,}])', m.group(1))) if m else set()
doc = {'js','ts','mjs','cjs','jsx','tsx'}

print('current_extensions_in_glob=', sorted(current))
print('doc_extensions=', sorted(doc))
print('missing_from_glob=', sorted(doc - current))
PY

Repository: unraid/js-standards

Length of output: 4316


Extend PLAYWRIGHT_SPEC_FILES to match Playwright’s default testMatch.

The document states this glob mirrors Playwright’s default, but Playwright also matches Playwright spec files with .mts, .cts, and .jsx. Add those extensions to keep the pattern and comment consistent; otherwise .mts/.cts/.jsx specs may not get Playwright lint rules.

♻️ Proposed glob extension
-export const PLAYWRIGHT_SPEC_FILES = ["**/*.{spec,test}.{ts,tsx,js,mjs,cjs}"];
+export const PLAYWRIGHT_SPEC_FILES = [
+  "**/*.{spec,test}.{ts,tsx,mts,cts,js,jsx,mjs,cjs}",
+];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* Playwright specs mirrors Playwright's own default `testMatch`.
*
* The plugin's rules key off bare `test`/`expect` identifiers, so in a repo that
* also runs Vitest or Jest under this pattern, re-scope `files` on the spec entry
* to wherever Playwright actually owns (`e2e/**`, `**\/*.e2e.spec.ts`, ).
* Otherwise the rules give Playwright advice about a unit test.
*/
export const PLAYWRIGHT_SPEC_FILES = ["**/*.{spec,test}.{ts,tsx,js,mjs,cjs}"];
/**
* Playwright specs mirrors Playwright's own default `testMatch`.
*
* The plugin's rules key off bare `test`/`expect` identifiers, so in a repo that
* also runs Vitest or Jest under this pattern, re-scope `files` on the spec entry
* to wherever Playwright actually owns (`e2e/**`, `**\/*.e2e.spec.ts`, ).
* Otherwise the rules give Playwright advice about a unit test.
*/
export const PLAYWRIGHT_SPEC_FILES = [
"**/*.{spec,test}.{ts,tsx,mts,cts,js,jsx,mjs,cjs}",
];
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/eslint/globs.js` around lines 31 - 39, Extend the PLAYWRIGHT_SPEC_FILES
glob’s extension set to include mts, cts, and jsx alongside the existing
TypeScript, JavaScript, and module variants, preserving its current spec/test
filename matching behavior.

Comment thread src/eslint/playwright.js
Comment on lines +82 to +84
ExportDefaultDeclaration(node) {
const config = configObject(node.declaration);
if (!config) return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

The rule ignores CommonJS configs, which the file glob still matches.

PLAYWRIGHT_CONFIG_FILES includes .js and .cjs. A CommonJS Playwright config uses module.exports = defineConfig({...}). The rule listens only for ExportDefaultDeclaration, so such a config passes with no report. The failure is silent: the file is linted and reports nothing.

Add a visitor for the module.exports assignment.

♻️ Proposed handler for `module.exports`
       ExportDefaultDeclaration(node) {
         const config = configObject(node.declaration);
         if (!config) return;
+        checkConfig(config);
+      },
+      "AssignmentExpression > MemberExpression.left"(node) {
+        if (
+          node.object.type !== "Identifier" ||
+          node.object.name !== "module" ||
+          node.computed ||
+          node.property.name !== "exports"
+        ) {
+          return;
+        }
+        const config = configObject(node.parent.right);
+        if (config) checkConfig(config);
+      },

Extract the body of the existing visitor into a local checkConfig(config) helper so both entry points share it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/eslint/playwright.js` around lines 82 - 84, Update the rule around
ExportDefaultDeclaration to also inspect CommonJS module.exports assignments
matched by PLAYWRIGHT_CONFIG_FILES. Extract the existing config validation logic
into a local checkConfig(config) helper, then invoke it for both default exports
and module.exports values, including defineConfig({...}) wrappers.

Comment thread src/eslint/playwright.js
Comment on lines +156 to +161
export default [
{
...recommended,
files: PLAYWRIGHT_SPEC_FILES,
rules: { ...recommended.rules, ...CURATED_SPEC_RULES },
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
# Locate other concerns and check which entries set a TypeScript parser.
fd . src/eslint -e js --exec rg -n 'parser|languageOptions|files:' {} \;

echo "=== README mentions of composing the playwright concern ==="
fd -i readme -e md --exec rg -n -C 3 -i 'playwright' {} \;

Repository: unraid/js-standards

Length of output: 1044


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== eslint plugin files ==="
git ls-files src/eslint

echo "=== playwright.js outline/sections ==="
fd -e js playwright.js src/eslint --exec sh -c 'echo "--- $1"; wc -l "$1"; sed -n "1,220p" "$1"' sh {}

echo "=== eslint concern files parser context ==="
for f in $(git ls-files src/eslint -e js); do
  echo "--- $f"
  rg -n 'languageOptions|parser|Files|TS_SOURCE|REACTIVE|VUE|PLAYWRIGHT|recommended|rules:' "$f" -C 2 || true
done

echo "=== exports/config consumers ==="
rg -n "playwright|PLAYWRIGHT_SPEC_FILES|PLAYWRIGHT_CONFIG_FILES|import .*playwright|from .*playwright" .

Repository: unraid/js-standards

Length of output: 13824


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== eslint plugin files ==="
git ls-files src/eslint

echo "=== playwright.js outline/sections ==="
fd -e js playwright.js src/eslint --exec sh -c 'echo "--- $1"; wc -l "$1"; sed -n "1,220p" "$1"' sh {}

echo "=== eslint concern files parser context ==="
for f in $(git ls-files src/eslint -e js); do
  echo "--- $f"
  rg -n 'languageOptions|parser|files|TS_SOURCE|REACT_FILES|VUE|PLAYWRIGHT|recommended|rules:' "$f" -C 2 || true
done

echo "=== exports/config consumers ==="
rg -n "playwright|PLAYWRIGHT_SPEC_FILES|PLAYWRIGHT_CONFIG_FILES|import .*playwright|from .*playwright" .

Repository: unraid/js-standards

Length of output: 13824


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== test helper ==="
for f in $(git ls-files test -z | xargs -0 -n1 sed -n '1,120p'); do
  :
done 2>/dev/null || true

echo "=== README compose sections ==="
sed -n '140,195p' README.md

echo "=== package dependencies relevant ==="
sed -n '1,80p' package.json | rg -n -C 2 'eslint|pnpm|ts|typescript|`@typescript-eslint`|playwright' || true

Repository: unraid/js-standards

Length of output: 5966


🌐 Web query:

@eslint/js 10 recommended config languageOptions parser undefined default parser flat config

💡 Result:

In ESLint 10 (as in previous versions using flat config), the @eslint/js recommended configuration does not define a parser [1][2][3]. Instead, ESLint utilizes a built-in default parser, Espree, when no parser is explicitly specified in the languageOptions property [1]. You are not required to define a parser unless you intend to use a non-default parser (such as @babel/eslint-parser or @typescript-eslint/parser) to handle specific syntax [4][5]. Key points regarding parser configuration in flat config: - Default Behavior: If you omit the languageOptions.parser property, ESLint automatically uses its built-in parser, Espree [1]. - How to Override: If you need a custom parser, you import it and assign it to the languageOptions.parser property within your configuration object [6][4]. - Scope: The @eslint/js recommended configuration (imported from @eslint/js) provides rules, not language parsing settings [1][2][3]. While you often include these rules in the same configuration object as your languageOptions, they remain distinct concerns [1][3]. If you encounter errors suggesting a parser is "undefined" or "required," ensure that your configuration object correctly structures the languageOptions property and verify if any plugins you are using require explicit parser mappings for specific file extensions [6][4][7].

Citations:


🌐 Web query:

ESLint Espree parser can parse TypeScript TypeScript source parse error

💡 Result:

The ESLint default parser, Espree, does not support TypeScript syntax [1][2][3]. When Espree encounters TypeScript-specific constructs—such as type annotations (e.g., : string), interfaces, or enums—it fails because it is designed only for standard ECMAScript [4][5][3]. Attempting to use Espree on TypeScript files will result in a "Parsing error: Unexpected token" [3]. To resolve this issue, you must configure ESLint to use the @typescript-eslint/parser instead of the default Espree parser [3][6]. This parser is specifically designed to convert TypeScript code into an ESTree-compatible format that ESLint can understand [4][5][7]. To fix the error: 1. Install the @typescript-eslint/parser package. 2. Update your ESLint configuration (e.g., .eslintrc.js or eslint.config.js) to set the parser to @typescript-eslint/parser [6][8]. Example configuration update: parser: '@typescript-eslint/parser' If you require advanced features like type-aware linting, you must also configure the parser to reference your project's TypeScript configuration (tsconfig.json) [8]. Using Espree in a TypeScript project is not supported and will lead to unpredictable behavior and linting errors [9].

Citations:


Document TypeScript composition for the Playwright spec concern.

PLAYWRIGHT_SPEC_FILES includes .ts and .tsx, but the spec entry does not set languageOptions.parser. ESLint falls back to its default parser for .ts/.tsx, which reports parse errors on TypeScript syntax. Add this requirement to the README, or make the spec entry composition-aware by excluding those extensions unless composable with a TypeScript parser concern.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/eslint/playwright.js` around lines 156 - 161, Update the Playwright spec
configuration around PLAYWRIGHT_SPEC_FILES so TypeScript and TSX files are not
processed without a compatible TypeScript parser concern; either document and
enforce the required composition in the README or exclude .ts/.tsx from this
entry unless the parser is supplied through composition. Preserve the existing
JavaScript Playwright rules and recommended configuration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant