Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 3 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
ref: ${{ inputs.source_sha || github.sha }}
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "24.18.0"
node-version-file: .node-version
package-manager-cache: false
- run: npm install --global npm@12.0.2
- run: npm ci
Expand Down Expand Up @@ -75,20 +75,15 @@ jobs:
run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "24.18.0"
node-version-file: .node-version
package-manager-cache: false
- run: npm install --global npm@12.0.2
- run: npm ci
- uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 # v4
with:
just-version: "1.58.0"
- name: Correctness lint
run: |
if node -e "process.exit(require('./package.json').devDependencies?.oxlint ? 0 : 1)"; then
npm exec -- oxlint --deny-warnings -A complexity -A max-lines-per-function -A no-unused-vars -A no-unused-labels -A no-unused-private-class-members -A no-extra-boolean-cast -A no-useless-catch -A no-useless-escape -A no-useless-rename -A typescript/no-duplicate-type-constituents -A typescript/no-redundant-type-constituents -A typescript/no-extra-non-null-assertion -A typescript/no-this-alias -A typescript/no-unnecessary-parameter-property-assignment -A typescript/no-useless-default-assignment -A typescript/no-useless-empty-export -A typescript/prefer-as-const -A typescript/prefer-namespace-keyword -A typescript/triple-slash-reference -A unicorn/no-empty-file -A unicorn/no-single-promise-in-promise-methods -A unicorn/no-unnecessary-await -A unicorn/no-useless-fallback-in-spread -A unicorn/no-useless-length-check -A unicorn/no-useless-spread -A unicorn/prefer-set-size -A unicorn/prefer-string-starts-ends-with
fi
npm exec --workspaces -- eslint --rule no-unused-labels:off --rule no-unused-private-class-members:off --rule no-extra-boolean-cast:off --rule no-useless-catch:off --rule no-useless-escape:off --rule no-empty-static-block:off --rule no-var:off --rule prefer-const:off --rule prefer-rest-params:off --rule prefer-spread:off --rule @typescript-eslint/no-unused-vars:off --rule @typescript-eslint/no-array-constructor:off --rule @typescript-eslint/no-extra-non-null-assertion:off --rule @typescript-eslint/no-namespace:off --rule @typescript-eslint/no-require-imports:off --rule @typescript-eslint/no-this-alias:off --rule @typescript-eslint/no-unnecessary-type-constraint:off --rule @typescript-eslint/prefer-as-const:off --rule @typescript-eslint/prefer-namespace-keyword:off --rule @typescript-eslint/triple-slash-reference:off --rule react-refresh/only-export-components:off
- run: npm run security:watchlist
run: just lint-correctness
- run: npm run typecheck
- name: Install Chromium
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/deadcode.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
- uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 # v4
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "24.18.0"
node-version-file: .node-version
package-manager-cache: false
- run: npm install --global npm@12.0.2
- run: just deadcode-setup
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ jobs:
- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "24.18.0"
node-version-file: .node-version
registry-url: https://registry.npmjs.org
package-manager-cache: false

Expand Down
1 change: 1 addition & 0 deletions .node-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
26.9.0
47 changes: 47 additions & 0 deletions .oxlintrc.correctness.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"extends": ["./.oxlintrc.json"],
"overrides": [
{
"files": ["**/*"],
"rules": {
"eslint/complexity": "off",
"eslint/max-lines-per-function": "off",
"eslint/no-unused-vars": "off",
"eslint/no-unused-labels": "off",
"eslint/no-unused-private-class-members": "off",
"eslint/no-extra-boolean-cast": "off",
"eslint/no-useless-catch": "off",
"eslint/no-useless-escape": "off",
"eslint/no-useless-rename": "off",
"typescript/no-duplicate-type-constituents": "off",
"typescript/no-redundant-type-constituents": "off",
"typescript/no-extra-non-null-assertion": "off",
"typescript/no-this-alias": "off",
"typescript/no-unnecessary-parameter-property-assignment": "off",
"typescript/no-useless-default-assignment": "off",
"typescript/no-useless-empty-export": "off",
"typescript/prefer-as-const": "off",
"typescript/prefer-namespace-keyword": "off",
"typescript/triple-slash-reference": "off",
"unicorn/no-empty-file": "off",
"unicorn/no-single-promise-in-promise-methods": "off",
"unicorn/no-unnecessary-await": "off",
"unicorn/no-useless-fallback-in-spread": "off",
"unicorn/no-useless-length-check": "off",
"unicorn/no-useless-spread": "off",
"unicorn/prefer-set-size": "off",
"unicorn/prefer-string-starts-ends-with": "off",
"eslint/no-empty-static-block": "off",
"eslint/no-var": "off",
"eslint/prefer-const": "off",
"eslint/prefer-rest-params": "off",
"eslint/prefer-spread": "off",
"eslint/no-array-constructor": "off",
"typescript/no-namespace": "off",
"typescript/no-require-imports": "off",
"typescript/no-unnecessary-type-constraint": "off",
"react/only-export-components": "off"
}
}
]
}
209 changes: 208 additions & 1 deletion .oxlintrc.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
{
"extends": ["./.nextide/oxlint.json"],
"jsPlugins": [
{
"name": "hooks-js",
"specifier": "eslint-plugin-react-hooks"
}
],
"env": {
"browser": true
},
"rules": {
"jsx-a11y/label-has-associated-control": [
"error",
Expand All @@ -8,5 +17,203 @@
}
]
},
"ignorePatterns": ["**/dist/**", "output/**"]
"ignorePatterns": ["**/dist/**", "output/**"],
"overrides": [
{
"files": ["packages/ui/**/*.{ts,tsx}", "apps/playground/**/*.{ts,tsx}"],
"rules": {
"eslint/for-direction": "error",
"eslint/no-async-promise-executor": "error",
"eslint/no-case-declarations": "error",
"eslint/no-compare-neg-zero": "error",
"eslint/no-cond-assign": ["error", "except-parens"],
"eslint/no-constant-binary-expression": [
"error",
{
"checkRelationalComparisons": false
}
],
"eslint/no-constant-condition": [
"error",
{
"checkLoops": "allExceptWhileTrue"
}
],
"eslint/no-control-regex": "error",
"eslint/no-debugger": "error",
"eslint/no-delete-var": "error",
"eslint/no-dupe-else-if": "error",
"eslint/no-duplicate-case": "error",
"eslint/no-empty": [
"error",
{
"allowEmptyCatch": false
}
],
"eslint/no-empty-character-class": "error",
"eslint/no-empty-pattern": [
"error",
{
"allowObjectPatternsAsParameters": false
}
],
"eslint/no-empty-static-block": "error",
"eslint/no-ex-assign": "error",
"eslint/no-extra-boolean-cast": ["error", {}],
"eslint/no-fallthrough": [
"error",
{
"allowEmptyCase": false,
"reportUnusedFallthroughComment": false
}
],
"eslint/no-global-assign": [
"error",
{
"exceptions": []
}
],
"eslint/no-invalid-regexp": ["error", {}],
"eslint/no-irregular-whitespace": [
"error",
{
"skipComments": false,
"skipJSXText": false,
"skipRegExps": false,
"skipStrings": true,
"skipTemplates": false
}
],
"eslint/no-loss-of-precision": "error",
"eslint/no-misleading-character-class": [
"error",
{
"allowEscape": false
}
],
"eslint/no-nonoctal-decimal-escape": "error",
"eslint/no-prototype-builtins": "error",
"eslint/no-regex-spaces": "error",
"eslint/no-self-assign": [
"error",
{
"props": true
}
],
"eslint/no-shadow-restricted-names": [
"error",
{
"reportGlobalThis": true
}
],
"eslint/no-sparse-arrays": "error",
"eslint/no-unassigned-vars": "error",
"eslint/no-unexpected-multiline": "error",
"eslint/no-unsafe-finally": "error",
"eslint/no-unsafe-optional-chaining": [
"error",
{
"disallowArithmeticOperators": false
}
],
"eslint/no-unused-labels": "error",
"eslint/no-unused-private-class-members": "error",
"eslint/no-useless-assignment": "error",
"eslint/no-useless-backreference": "error",
"eslint/no-useless-catch": "error",
"eslint/no-useless-escape": [
"error",
{
"allowRegexCharacters": []
}
],
"eslint/preserve-caught-error": [
"error",
{
"requireCatchParameter": false
}
],
"eslint/require-yield": "error",
"eslint/use-isnan": [
"error",
{
"enforceForIndexOf": false,
"enforceForSwitchCase": true
}
],
"eslint/valid-typeof": [
"error",
{
"requireStringLiterals": false
}
],
"eslint/no-var": "error",
"eslint/prefer-const": [
"error",
{
"destructuring": "any",
"ignoreReadBeforeAssign": false
}
],
"eslint/prefer-rest-params": "error",
"eslint/prefer-spread": "error",
"typescript/ban-ts-comment": "error",
"eslint/no-array-constructor": "error",
"typescript/no-duplicate-enum-values": "error",
"typescript/no-empty-object-type": "error",
"typescript/no-explicit-any": "error",
"typescript/no-extra-non-null-assertion": "error",
"typescript/no-misused-new": "error",
"typescript/no-namespace": "error",
"typescript/no-non-null-asserted-optional-chain": "error",
"typescript/no-require-imports": "error",
"typescript/no-this-alias": "error",
"typescript/no-unnecessary-type-constraint": "error",
"typescript/no-unsafe-declaration-merging": "error",
"typescript/no-unsafe-function-type": "error",
"eslint/no-unused-expressions": [
"error",
{
"allowShortCircuit": false,
"allowTaggedTemplates": false,
"allowTernary": false
}
],
"eslint/no-unused-vars": "error",
"typescript/no-wrapper-object-types": "error",
"typescript/prefer-as-const": "error",
"typescript/prefer-namespace-keyword": "error",
"typescript/triple-slash-reference": "error",
"react/rules-of-hooks": "error",
"react/exhaustive-deps": "error",
"react/static-components": "error",
"react/use-memo": "error",
"react/preserve-manual-memoization": "error",
"react/incompatible-library": "warn",
"react/immutability": "error",
"react/globals": "error",
"react/refs": "error",
"react/set-state-in-effect": "error",
"react/error-boundaries": "error",
"react/purity": "off",
"react/set-state-in-render": "error",
"react/unsupported-syntax": "warn",
"hooks-js/config": "error",
"hooks-js/gating": "error",
"hooks-js/purity": "error"
}
},
{
"files": ["apps/playground/**/*.{ts,tsx}"],
"rules": {
"react/only-export-components": [
"error",
{
"allowConstantExport": true,
"allowCompoundComponents": true
}
]
}
}
]
}
16 changes: 10 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ Run `npm run dev`, then open [http://127.0.0.1:5174](http://127.0.0.1:5174) to v

## Development checks

Install Node 24.18.0 (matching CI), npm 12.0.2, `just`, and uv. On Windows, install PowerShell 7.5+
Install Node 26.9.0 (matching `.node-version` and CI), npm 12.0.2, `just`, and uv. On Windows, install PowerShell 7.5+
with `pwsh` available on PATH.

- `just setup` installs the frozen workspace lockfile.
- `just check` runs Prettier, Oxlint, existing ESLint, the supply-chain watchlist,
- `just check` runs Prettier, Vite+ Oxlint, the supply-chain watchlist,
LOC budgets, strict TypeScript checks, and packed-consumer qualification.
- `just fmt`, `just fmt-check`, `just lint`, and `just typecheck` run separately.
- `just quality` runs full formatting, lint, and LOC checks.
Expand All @@ -22,7 +22,7 @@ with `pwsh` available on PATH.
isolated npm consumer, public TypeScript/runtime exports, stylesheet/font assets,
and known chart, scroll-state, and typography regressions. It needs npm registry access.
Native tooling acceptance also invokes the real lint processes against disposable
unsafe-operation and React Hooks probes; source-shape qualification units are removed.
unsafe-operation, React Hooks, accessibility and workspace export probes.
- `just test-integration` builds both workspaces and runs native TesterArmy E2E.
First run `npm exec -- e2e-web install chromium` (Linux CI adds `--with-deps`).
The runner owns an isolated preview port and stops the server on exit.
Expand All @@ -41,9 +41,10 @@ The [shared baseline](https://github.com/nextidemedia/meta/blob/main/docs/develo
sets complexity 12, function length 100, and source/test LOC limits. All source
files meet the 600-line limit and browser suites meet the 900-line test limit;
`.loc.json` has no exceptions. Accessibility exceptions explain specific SVG,
forwarded-prop, or scroll-region semantics. Existing ESLint/React Hooks rules remain until parity
with Oxlint is verified. Oxlint 1.80.0 was the newest stable npm release at least
seven days old on 2026-09-08; the existing formatter and TypeScript remain pinned.
forwarded-prop, or scroll-region semantics. Vite+ 1.1.0 owns lint and builds; TypeScript 7.0.2 owns compiler checks and declarations.
The official React Hooks plugin retains three rules where native parity is incomplete.
See [migration evidence](docs/viteplus-migration.md) for scopes, diagnostic comparisons,
retained checks and the approved release-age waiver. Prettier remains the formatter.

Correctness lint retains the configured React Hooks, accessibility, unsafe-operation,
and accumulating-spread checks. Only explicit cosmetic rules (size/complexity,
Expand Down Expand Up @@ -163,6 +164,9 @@ representative Chromium interaction, accessibility, and responsive checks.
Use `just qualify` for all checks or `just qualify-deploy` for release validation.
Direct dependencies are pinned exactly. The workspace also enforces a seven-day release age (except native E2E packages),
strict peer dependencies, and explicitly approved dependency install scripts.
The library uses `vp pack` in unbundled mode, preserving public `.js`/`.d.ts`
subpaths, client directives, stylesheets and fonts. Native E2E remains the test runner;
there is no starter Vitest suite.

## Releasing

Expand Down
23 changes: 0 additions & 23 deletions apps/playground/eslint.config.js

This file was deleted.

Loading
Loading