Skip to content

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

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

build(lint): migrate to eslint 10 + @nextcloud/eslint-config 9#511
rubenvdlinde merged 2 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 73 .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.

422 pre-existing violations are recorded, not fixed

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

count rule
159 no-console
134 jsdoc/require-param-type
38 @typescript-eslint/no-unused-vars
20 vue/prefer-define-options
18 @nextcloud/no-deprecated-library-props
17 no-unused-vars
9 vue/custom-event-name-casing
9 no-useless-assignment
4 vue/multi-word-component-names
4 vue/prefer-separate-static-class
3 vue/slot-name-casing
3 @nextcloud/l10n-enforce-ellipsis

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 73 .vue files parse, 0 fatal errors.

422 PRE-EXISTING VIOLATIONS ARE RECORDED, NOT FIXED
------------------------------------------------------
v9 enables rules this app has never run. --fix and prettier resolved the
mechanical ones; 422 findings across 64 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.

   159  no-console
   134  jsdoc/require-param-type
   38  @typescript-eslint/no-unused-vars
   20  vue/prefer-define-options
   18  @nextcloud/no-deprecated-library-props
   17  no-unused-vars
   9  vue/custom-event-name-casing
   9  no-useless-assignment
   4  vue/multi-word-component-names
   4  vue/prefer-separate-static-class
   3  vue/slot-name-casing
   3  @nextcloud/l10n-enforce-ellipsis

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/softwarecatalog @ 024d54f

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

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

Download the full PDF report from the workflow artifacts.

# Conflicts:
#	src/dialogs/SuiteWizard/Step1Details.vue
#	src/views/LifecycleRoadmapView.vue
#	src/views/suites/SuitesIndexView.vue
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/softwarecatalog @ fd224ed

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

Quality workflow — 2026-08-14 15:01 UTC

Download the full PDF report from the workflow artifacts.

@rubenvdlinde
rubenvdlinde merged commit b7916e9 into development Aug 14, 2026
36 of 38 checks passed
@rubenvdlinde
rubenvdlinde deleted the feat/eslint-10 branch August 14, 2026 15:11
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