Files
computer-playbook/eslint.config.js
T
kevinveenbirkenbachandClaude Opus 5 faadf5b850 refactor(design): compose the per-app sheet and script from one SPOT
sys-front-inj-design now renders templates/css/app.css.j2 ahead of the
role's stylesheet and templates/js/design.js.j2 ahead of its script. The
branding URLs, the slot dimensions and the logo guard live there instead of
being repeated per role: the guard that stood in 41 sheets is one condition
in the composing template, and a sheet reads branding through
--design-url-* / --design-slot-* custom properties.

29 stylesheets and 43 design scripts lose their Jinja and move to static
files. Roles whose sheet carries app-specific URLs declare them as
design_css_urls and read them as --design-url-app-*.

The script prelude is prepended only when the role script references it,
and it carries urls only while a logo is configured, so every role that
needs neither is emitted byte for byte as before.

Five checks keep the mechanism honest: a composed sheet may only read
design custom properties that app.css.j2 declares for its role, a template
may not carry Jinja expression text outside its delimiters, a design script
may not quote its config object, and the injected-script parse check now
covers the design channel.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-18 22:36:32 +02:00

129 lines
4.9 KiB
JavaScript

const js = require("@eslint/js");
const playwright = require("eslint-plugin-playwright");
const globals = require("globals");
module.exports = [
{
ignores: [
"node_modules/**",
"venv/**",
".venv/**",
"**/__pycache__/**",
"roles/web-app-bigbluebutton/files/javascript/ep_redis_publisher_index.js",
],
},
{
linterOptions: {
reportUnusedDisableDirectives: "warn",
},
},
js.configs.recommended,
{
// Repo-root config / build files are CommonJS Node modules.
files: ["*.js", "*.cjs"],
languageOptions: {
sourceType: "commonjs",
globals: { ...globals.node },
},
},
{
files: ["roles/**/files/**/*.js", "roles/**/templates/**/*.js"],
languageOptions: {
ecmaVersion: 2023,
sourceType: "commonjs",
globals: {
...globals.node,
// Specs and persona helpers run inside `page.evaluate(() => …)`
// callbacks where window/document/getComputedStyle/etc. are valid;
// adding the browser globals avoids a sea of `no-undef` false
// positives.
...globals.browser,
INFINITO_DESIGN: "readonly",
},
},
rules: {
"no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
"no-var": "error",
"eqeqeq": ["error", "always"],
"prefer-const": "error",
"prefer-template": "error",
"no-throw-literal": "error",
"no-self-compare": "error",
"default-case-last": "error",
// Defensive: forbid string-driven code execution shapes outright.
"no-eval": "error",
"no-implied-eval": "error",
"no-new-func": "error",
"no-script-url": "error",
// Empty `catch (e) {}` is the project's idiom for fire-and-forget
// best-effort actions (localStorage probes, optional cleanups, …);
// the catch protects the surrounding flow from incidental
// failures it can't act on.
"no-empty": ["error", { allowEmptyCatch: true }],
},
},
{
// SSO sidecar shims (oauth2-proxy header-auth / server patches) are
// authored as ES modules (import/export), not CommonJS.
files: ["roles/**/files/sso/**/*.js"],
languageOptions: {
sourceType: "module",
},
},
{
files: ["roles/web-svc-simpleicons/files/javascript/**/*.js"],
languageOptions: {
sourceType: "module",
},
},
{
files: [
"roles/**/files/playwright/**/*.js",
"roles/test-e2e-playwright/files/personas/**/*.js",
],
plugins: { playwright },
rules: {
...playwright.configs["flat/recommended"].rules,
// skipUnlessServiceEnabled() drives a real test.skip(...) on
// runtime-detected disabled services — that's the project's
// contract, not an oversight.
"playwright/no-skipped-test": "off",
// Persona helpers (runGuestFlow / runBiberFlow / runAdminFlow) branch
// on isVisible() / isServiceEnabled() before expect(); the conditional
// is the contract, not a bug.
"playwright/no-conditional-expect": "off",
"playwright/no-conditional-in-test": "off",
// Some specs use waitForTimeout deliberately for OIDC settle delays
// and federation propagation — banning it would force rewrites.
"playwright/no-wait-for-timeout": "off",
// The OIDC + post-redirect dance settles when the page is idle, not
// on a single load event. Several specs rely on
// waitForLoadState("networkidle") for that; banning it would force
// a coordinated rewrite of every flow. Tracked for a follow-up.
"playwright/no-networkidle": "off",
// Real lint signal, but the autofixer is not safe in every site we
// hit (it produced a broken diff in personas/utils/dashboard.js by
// dropping the `await getAttribute("href")` step and leaving a
// trailing-comma `toHaveAttribute("href", )` call). Keep off until
// a hand-migration pass; promote to "error" as those are reviewed.
"playwright/prefer-web-first-assertions": "off",
"playwright/no-wait-for-navigation": "error",
// An empty collection out of a catch reads downstream as "nothing to
// do": `JSON.parse` on a dotenv-quoted target list yields a string, the
// fallback made it `[]`, and every assertion iterating it passed while
// checking nothing. A catch returning "" or false or a populated object
// is not this class; reviewing all 26 such sites found each of them
// either recording the failure or answering a predicate.
"no-restricted-syntax": [
"error",
{
selector:
"CatchClause > BlockStatement > ReturnStatement > :matches(ArrayExpression[elements.length=0], ObjectExpression[properties.length=0])",
message:
"This catch returns an empty collection, which every caller reads as a legitimate 'nothing to do' and iterates zero times. Rethrow, or decode through a helper that throws on the shapes that cannot be right.",
},
],
},
},
];