Skip to content

Commit 3d411b6

Browse files
malbertsclaude
authored andcommitted
Add self-contained frontend linting to the RedHerb example
RedHerb's JavaScript, Vue, and LESS resources were not linted. The lint scope of ci-ts.yml (resources/**) predates RedHerb and never grew to cover tests/RedHerb, so the example extension was held to a lower standard than the code it is meant to demonstrate. RedHerb now carries its own lint setup, so the example stands on its own and can be lifted wholesale into a real extension rather than borrowing ext.neowiki's TypeScript toolchain: * tests/RedHerb/package.json with eslint, stylelint, and banana-checker, plus an "npm run lint" script that runs all three, a committed package-lock.json, and a local .gitignore so the directory is self-contained. * .eslintrc.json using the Wikimedia plain-JavaScript profile (wikimedia/client + wikimedia/mediawiki) for the .js and Vue SFC resources, not the TypeScript profile ext.neowiki uses. * .stylelintrc.json using stylelint-config-wikimedia for the <style> blocks. * .github/workflows/ci-redherb.yml running the lint on every push and PR. The aim is to mirror a real third-party MediaWiki extension, so RedHerb follows MediaWiki's standard plain-JavaScript lint defaults and code style even where those differ from ext.neowiki's TypeScript-specific choices (for example, the profile requires explicit-close Vue component tags, while ext.neowiki allows self-closing). A NeoWiki choice is carried over only where it is deliberate and applies: the dependency versions track ext.neowiki rather than MediaWiki core's older pins, and the max-len / no-descending-specificity relaxations are kept. The README documents the three rule disables: max-len and no-descending-specificity mirror ext.neowiki's own config, while compat/compat is off because NeoWiki targets a modern browser baseline above the browserslist floor the rule checks (it comes from the wikimedia/mediawiki profile, which ext.neowiki's TypeScript setup does not include). Every existing RedHerb resource is made lint-clean with mechanical, behaviour-preserving changes only: var to const/let, function callbacks to arrow functions, indexOf to includes, redundant 'use strict' and a stale eslint-disable removed, a shadowed variable renamed, and // @vue/component markers added to the component exports. The JavaScript snippets in docs/reference/extending.md are updated to the same modern style so the documented examples stay consistent with the linted code. banana-checker runs against RedHerb's own i18n only; NeoWiki's i18n check remains as it was. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 4827fa3 commit 3d411b6

22 files changed

Lines changed: 5652 additions & 201 deletions

.github/workflows/ci-php.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ on:
55
push:
66
paths-ignore:
77
- '.github/workflows/ci-ts.yml'
8+
- '.github/workflows/ci-redherb.yml'
89
- '.github/workflows/neo-ci-ts.yml'
910
- 'resources/**'
1011
- 'Neo/neojs/**'
1112
pull_request:
1213
paths-ignore:
1314
- '.github/workflows/ci-ts.yml'
15+
- '.github/workflows/ci-redherb.yml'
1416
- '.github/workflows/neo-ci-ts.yml'
1517
- 'resources/**'
1618
- 'Neo/neojs/**'

.github/workflows/ci-redherb.yml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: NeoWiki CI (RedHerb example)
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
paths:
7+
- '.github/workflows/ci-redherb.yml'
8+
- 'tests/RedHerb/**'
9+
pull_request:
10+
paths:
11+
- '.github/workflows/ci-redherb.yml'
12+
- 'tests/RedHerb/**'
13+
14+
jobs:
15+
lint:
16+
name: "Linting"
17+
18+
runs-on: ubuntu-latest
19+
20+
defaults:
21+
run:
22+
working-directory: ./tests/RedHerb
23+
24+
steps:
25+
- name: Checkout
26+
uses: actions/checkout@v6
27+
28+
- name: Setup Node.js
29+
uses: actions/setup-node@v6
30+
with:
31+
node-version: '24'
32+
33+
- name: Silence warnings
34+
run: |
35+
echo "::remove-matcher owner=eslint-compact::"
36+
echo "::remove-matcher owner=eslint-stylish::"
37+
38+
- name: Install dependencies
39+
run: npm ci
40+
41+
- name: Lint
42+
run: npm run lint

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ vendor/
88
composer.lock
99
package-lock.json
1010
!resources/ext.neowiki/package-lock.json
11+
!tests/RedHerb/package-lock.json

docs/reference/extending.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,9 @@ A backend Property Type needs a matching frontend: a display component, an input
143143
attributes editor. Register them through the `neowiki.registration` JS hook:
144144

145145
```javascript
146-
var nw = require( 'ext.neowiki' );
146+
const nw = require( 'ext.neowiki' );
147147

148-
mw.hook( 'neowiki.registration' ).add( function ( registrar ) {
148+
mw.hook( 'neowiki.registration' ).add( ( registrar ) => {
149149
registrar.registerPropertyType( {
150150
typeName: 'color',
151151
valueType: nw.ValueType.String,
@@ -188,10 +188,10 @@ A View Type renders a Subject in a particular visual format; `infobox` is the on
188188
component for a new View Type through the same `neowiki.registration` hook, at parity with Property Types:
189189

190190
```javascript
191-
var nw = require( 'ext.neowiki' );
192-
var RedHerbCard = require( './RedHerbCard.vue' );
191+
const nw = require( 'ext.neowiki' );
192+
const RedHerbCard = require( './RedHerbCard.vue' );
193193

194-
mw.hook( 'neowiki.registration' ).add( function ( registrar ) {
194+
mw.hook( 'neowiki.registration' ).add( ( registrar ) => {
195195
registrar.registerViewType( {
196196
typeName: 'redherb-card',
197197
component: RedHerbCard
@@ -230,8 +230,8 @@ To build a Vue feature wired to NeoWiki's services, obtain NeoWiki's Pinia insta
230230
services on your app:
231231

232232
```javascript
233-
var nw = require( 'ext.neowiki' );
234-
var app = Vue.createMwApp( MyComponent );
233+
const nw = require( 'ext.neowiki' );
234+
const app = Vue.createMwApp( MyComponent );
235235

236236
app.use( nw.NeoWikiExtension.getInstance().getPinia() );
237237
nw.NeoWikiServices.registerServices( app );

tests/RedHerb/.eslintrc.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"root": true,
3+
"extends": [
4+
"wikimedia/client",
5+
"wikimedia/mediawiki"
6+
],
7+
"rules": {
8+
"max-len": "off",
9+
"compat/compat": "off"
10+
}
11+
}

tests/RedHerb/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules/

tests/RedHerb/.stylelintrc.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"extends": [
3+
"stylelint-config-wikimedia"
4+
],
5+
"rules": {
6+
"no-descending-specificity": null
7+
}
8+
}

tests/RedHerb/README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,35 @@ extension point to the file that demonstrates it.
3636
- **Mount standalone Vue features wired to NeoWiki services**
3737
[`resources/createChild/`](resources/createChild), [`resources/editMainSubject/`](resources/editMainSubject),
3838
and [`resources/subjectFinder/`](resources/subjectFinder).
39+
40+
## Frontend linting
41+
42+
RedHerb carries its own lint setup so the example is self-contained and can be lifted into a
43+
real extension. Everything lives in this directory: [`package.json`](package.json) (dev
44+
dependencies and scripts), [`.eslintrc.json`](.eslintrc.json) (the Wikimedia plain-JavaScript
45+
profile, `wikimedia/client` plus `wikimedia/mediawiki`, for the `.js` and Vue SFC resources),
46+
and [`.stylelintrc.json`](.stylelintrc.json) (`stylelint-config-wikimedia` for the
47+
`<style lang="less">` blocks). NeoWiki's own TypeScript frontend under `resources/ext.neowiki`
48+
is linted separately; RedHerb does not share its toolchain.
49+
50+
The aim is to mirror a real third-party MediaWiki extension, so RedHerb follows MediaWiki's
51+
standard plain-JavaScript lint defaults and code style (the `wikimedia/client` +
52+
`wikimedia/mediawiki` profile) even where those differ from `ext.neowiki`'s TypeScript-specific
53+
choices: for example, the profile requires explicit-close Vue component tags
54+
(`<cdx-icon></cdx-icon>`), whereas `ext.neowiki` allows self-closing. A NeoWiki choice is carried
55+
over only where it is deliberate and applies here, namely the dependency versions (which track
56+
`ext.neowiki` rather than MediaWiki core's older pins) and the two rule relaxations below.
57+
58+
```bash
59+
npm install
60+
npm run lint # ESLint, Stylelint, and banana-checker (i18n)
61+
```
62+
63+
Three rules are disabled. `max-len` (`.eslintrc.json`) and `no-descending-specificity`
64+
(`.stylelintrc.json`) are off in `ext.neowiki`'s config too: the example's Vue templates and
65+
MediaWiki service calls have some long lines, and `no-descending-specificity` is noisy with the
66+
nested BEM-style LESS these components use. `compat/compat` (`.eslintrc.json`) is off because
67+
NeoWiki targets a modern browser baseline above the `browserslist-config-wikimedia` floor the
68+
rule checks against (`queueMicrotask` and similar APIs are used throughout NeoWiki); it comes
69+
from the `wikimedia/mediawiki` profile, which `ext.neowiki`'s TypeScript setup does not include,
70+
so there is no equivalent disable there.

0 commit comments

Comments
 (0)