Skip to content

build(lint): migrate to eslint 10 + @nextcloud/eslint-config 9 - #201

Merged
rubenvdlinde merged 3 commits into
developmentfrom
feat/eslint-10
Aug 14, 2026
Merged

build(lint): migrate to eslint 10 + @nextcloud/eslint-config 9#201
rubenvdlinde merged 3 commits into
developmentfrom
feat/eslint-10

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

Migrates the JS/Vue lint layer to eslint 10 + @nextcloud/eslint-config@9, matching the pilot (ConductionNL/larpingapp#325) and the template (ConductionNL/nextcloud-app-template#146). eslint.config.mjs is the fleet's canonical copy.

Needs Node 22 — the NC config declares engines.node: ^22.14 || ^24 || >=26 and imports findPackageJSON from node:module. The shared workflows already default to 22 (ConductionNL/.github#450).

Stale eslint-8-era direct deps and overrides are removed; the two load-bearing peers are declared at the right major (vue-eslint-parser@^10.3.0, @typescript-eslint/parser@^8.67.0), and an overrides entry resolves nc-vue's optional eslint peer against eslint 10 — optional means npm won't install it, not that a mismatch is accepted.

Coverage

All 135 .vue files parse, 0 fatal. Get the peers wrong and typescript-eslint/base parses every SFC as TypeScript; eslint then reports one parse error per file and lints nothing else in it, so the whole Vue layer goes unchecked while the problem count looks small.

157 pre-existing violations are recorded, not fixed

v9 enables rules this app has never run. --fix and prettier resolved the mechanical majority; 157 findings across 63 files remain:

count rule
72 @typescript-eslint/no-unused-vars
22 vue/custom-event-name-casing
20 eqeqeq
10 no-console
8 @nextcloud/l10n-enforce-ellipsis
7 no-useless-assignment
6 no-unused-vars
4 jsdoc/require-param-type
4 @nextcloud/no-deprecated-library-props
1 vue/prefer-prop-type-boolean-first
1 jsdoc/check-tag-names
1 no-undef

These live in eslint-suppressions.json via eslint's own bulk suppressionsnot by loosening any rule. Every rule keeps its @nextcloud/eslint-config severity, the debt is counted per file, and --prune-suppressions shrinks it as it's paid down. A new violation still fails the run (verified on openconnector by appending a console.log after suppressing).

Two autofixes deliberately withheld

  • @nextcloud/l10n-enforce-ellipsis rewrites ... to inside translatable strings, changing the translation key and orphaning every l10n/*.json entry for it. On openconnector that turned the l10n parity check red while it was green on development.
  • vue/prefer-define-options rewrites export default {…} in a plain <script> into defineOptions({…}) inside <script setup>, dragging props/data/computed across the block boundary. @vue/compiler-sfc then rejects it outright and the build fails — it hit 20 components in softwarecatalog and 15 in docudesk. That's a semantic refactor, not a lint fix.

Both are recorded as debt instead.

Test globals are declared, not suppressed

Spec files under src/ had no framework globals, so no-undef reported every describe/it/expect as undefined — 1,203 findings in openregister from 7 identifiers. Declaring the environment removed ~1,900 phantom findings fleet-wide. Suppressing them would have buried any real no-undef.

Verified

check result
npm run lint PASS
npm run format PASS
npm run stylelint PASS
npm run build PASS

Same migration as the pilot (ConductionNL/larpingapp#325) and the template
(ConductionNL/nextcloud-app-template#146). eslint.config.mjs is the canonical
copy; only the app-specific blocks at the end differ.

Requires Node 22 — @nextcloud/eslint-config@9 declares
engines.node ^22.14 || ^24 || >=26 and imports findPackageJSON from
node:module, first available in 22.14. The shared workflows already default
to 22 (ConductionNL/.github#450).

Stale eslint-8-era direct deps and overrides are removed, and the two peers
the config needs are declared at the right major (vue-eslint-parser ^10.3.0,
@typescript-eslint/parser ^8.67.0). An overrides entry resolves nc-vue's
OPTIONAL eslint peer against eslint 10 — optional means npm will not install
it, not that a mismatched version is accepted.

Coverage: all 135 .vue files parse, 0 fatal errors.

157 PRE-EXISTING VIOLATIONS ARE RECORDED, NOT FIXED
------------------------------------------------------
v9 enables rules this app has never run. --fix and prettier resolved the
mechanical ones; 157 findings across 63 files remain and are recorded in
eslint-suppressions.json using eslint's own bulk suppressions (--suppress-all),
NOT by loosening any rule. Every rule keeps the severity
@nextcloud/eslint-config gives it, the debt is counted per file, and
--prune-suppressions shrinks it as it is paid down. A NEW violation still
fails the run — verified on openconnector by appending a console.log after
suppressing.

   72  @typescript-eslint/no-unused-vars
   22  vue/custom-event-name-casing
   20  eqeqeq
   10  no-console
   8  @nextcloud/l10n-enforce-ellipsis
   7  no-useless-assignment
   6  no-unused-vars
   4  jsdoc/require-param-type
   4  @nextcloud/no-deprecated-library-props
   1  vue/prefer-prop-type-boolean-first
   1  jsdoc/check-tag-names
   1  no-undef

TWO AUTOFIXES ARE DELIBERATELY WITHHELD (recorded as debt instead)
-----------------------------------------------------------------
- @nextcloud/l10n-enforce-ellipsis rewrites '...' to the typographic '…'
  INSIDE translatable strings. That changes the translation KEY and orphans
  every l10n/*.json entry for it; on openconnector it turned the l10n parity
  check red while it was green on development. The migration must not
  silently drop translations, so the source strings are left alone.
- vue/prefer-define-options rewrites 'export default {…}' in a plain <script>
  into 'defineOptions({…})' inside <script setup>, carrying
  props/data/computed across the block boundary. @vue/compiler-sfc then
  rejects the result outright (defineOptions() cannot declare props), which
  broke the build in softwarecatalog and docudesk. It is a semantic refactor,
  not a lint fix.

TEST GLOBALS ARE DECLARED, NOT SUPPRESSED
-----------------------------------------
Spec files that live under src/ have no framework globals, so no-undef
reported every describe/it/expect as undefined — 1203 findings in
openregister from just 7 identifiers. Declaring the environment removed
~1900 phantom findings fleet-wide. Suppressing them instead would have
buried any REAL no-undef, which is the rule that catches a typo'd identifier.

VERIFIED
--------
  npm run lint  PASS    npm run stylelint  PASS
  npm run format PASS   npm run build      PASS
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/openbuild @ 02433d6

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-manifest
test-l10n
check-gitignore
check-nc-floor
format
composer ✅ 106/106
npm ✅ 638/638
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman ⏭️
Playwright
Hydra gates

Quality workflow — 2026-08-14 13:19 UTC

Download the full PDF report from the workflow artifacts.

`@nextcloud/no-deprecated-library-props` autofixed NcButton's deprecated
`type="primary"` to `variant="primary"`. The rule is right — @nextcloud/vue v9
did repurpose `type` as the native button type and moved the visual style to
`variant` — but the rename changes the RENDERED DOM, and tests assert on it:

  openbuild  10 tests failed on `button[data-nc-button-type="primary"]`
             returning undefined; 1375/1375 pass on `development`.

So this is a runtime change, not a formatting one, and it belongs in its own
PR together with the test updates it requires. It joins the two autofixes
already withheld for the same reason:

  @nextcloud/l10n-enforce-ellipsis  edits the CONTENT of translatable strings,
                                    changing the translation key
  vue/prefer-define-options         moves options across SFC block boundaries
                                    and breaks the build

All three keep their @nextcloud/eslint-config severity and are recorded in
eslint-suppressions.json, so the debt is counted and any NEW occurrence still
fails the run.

Verified after the change: openbuild is back to 1375/1375, and lint / format /
stylelint / build pass on every app touched here.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/openbuild @ dc452e8

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-manifest
test-l10n
check-gitignore
check-nc-floor
format
composer ✅ 106/106
npm ✅ 638/638
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman ⏭️
Playwright
Hydra gates

Quality workflow — 2026-08-14 14:10 UTC

Download the full PDF report from the workflow artifacts.

The version-rollback E2E assertion timed out on `.version-history__row`
being visible; the element WAS present (`version-history__row--current`)
but in state "hidden", and the sibling test that lists the same chain
passed in the same run. That reads as a visibility race rather than a
structural break, so this empty commit re-runs the suite to separate flake
from regression before the PR is judged.
@rubenvdlinde
rubenvdlinde merged commit 3686768 into development Aug 14, 2026
37 of 38 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/openbuild @ ba09453

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-manifest
test-l10n
check-gitignore
check-nc-floor
format
composer ✅ 106/106
npm ✅ 638/638
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman ⏭️
Playwright
Hydra gates

Quality workflow — 2026-08-14 14:53 UTC

Download the full PDF report from the workflow artifacts.

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