Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
63bcbee
feat(eslint): support eslint for ReactLynx
upupming Jul 11, 2025
ce23e61
chore: changeset
upupming Jul 11, 2025
0df8dc8
feat: simplify the mapESLintTemplate
upupming Jul 11, 2025
7a4871d
chore: update create-rstack
upupming Jul 14, 2025
1c63b9f
Merge remote-tracking branch 'gh/main' into feat/eslint
upupming Jul 14, 2025
8209889
feat: add test for eslint template
upupming Jul 14, 2025
5015c6b
feat: files should be configured by user
upupming Jul 14, 2025
4b3727f
fix: eslint type issue in workspace
upupming Jul 14, 2025
8f3e648
Merge remote-tracking branch 'gh/main' into feat/eslint
upupming Jul 14, 2025
9935b72
chore: remove test scripts
upupming Jul 15, 2025
f1d5c70
Merge remote-tracking branch 'gh/main' into feat/eslint
upupming Jul 15, 2025
2d5f233
chore: update create-rstack
upupming Jul 15, 2025
2cb1a7d
fix: eslint config type issue
upupming Jul 15, 2025
2c9bceb
Merge remote-tracking branch 'gh/main' into feat/eslint
upupming Jul 15, 2025
52714ac
Merge remote-tracking branch 'gh/main' into feat/eslint
upupming Jul 16, 2025
11e4270
feat: remove @lynx-js/eslint-config-reactlynx to keep it simple
upupming Jul 24, 2025
b187872
Merge remote-tracking branch 'gh/main' into feat/eslint
upupming Jul 24, 2025
f3db9c3
Merge remote-tracking branch 'gh/main' into feat/eslint
upupming Jul 24, 2025
1949847
fix: pnpm dedupe
upupming Jul 24, 2025
b9edc80
Update .changeset/floppy-singers-chew.md
upupming Aug 1, 2025
9106933
fix: cr
upupming Aug 1, 2025
2b0cba4
Merge remote-tracking branch 'gh/main' into feat/eslint
upupming Aug 1, 2025
20d001b
feat: pass eslint for examples
upupming Aug 1, 2025
93de49f
Merge remote-tracking branch 'upstream/main' into feat/eslint
colinaaa Aug 6, 2025
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
5 changes: 5 additions & 0 deletions .changeset/floppy-singers-chew.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"create-rspeedy": patch
---

Support eslint for ReactLynx template
5 changes: 5 additions & 0 deletions .changeset/rare-socks-spend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@lynx-js/eslint-config-reactlynx": patch
---

Add new `@lynx-js/eslint-config-reactlynx` package for ESLint configuration
6 changes: 4 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -143,16 +143,18 @@ jobs:
node packages/tools/canary-release/snapshot.js
pnpm --recursive publish --no-git-checks --access public --registry=http://localhost:4873
cd `mktemp -d`
npx --registry http://localhost:4873 create-rspeedy-canary@latest --template react --dir create-rspeedy-regression
npx --registry http://localhost:4873 create-rspeedy-canary@latest --template react --dir create-rspeedy-regression --tools eslint
cd create-rspeedy-regression
pnpm install --registry=http://localhost:4873
pnpm run build
pnpm run build --mode development
npx --registry http://localhost:4873 create-rspeedy-canary@latest --template react-vitest-rltl --dir create-rspeedy-regression-vitest-rltl
pnpm run lint
npx --registry http://localhost:4873 create-rspeedy-canary@latest --template react-vitest-rltl --dir create-rspeedy-regression-vitest-rltl --tools eslint
cd create-rspeedy-regression-vitest-rltl
pnpm install --registry=http://localhost:4873
pnpm run build
pnpm run build --mode development
pnpm run lint
pnpm run test
test-react:
needs: build
Expand Down
9 changes: 9 additions & 0 deletions examples/react/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import configs from '@lynx-js/eslint-config-reactlynx/ts';

export default [
...configs,
{
files: ['src/**/*.{js,jsx,ts,tsx}'],
ignores: ['dist/'],
},
];
2 changes: 2 additions & 0 deletions examples/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
"scripts": {
"build": "rspeedy build",
"dev": "rspeedy dev",
"lint": "eslint .",
"test:type": "vitest --typecheck.only"
},
"dependencies": {
"@lynx-js/react": "workspace:*"
},
"devDependencies": {
"@lynx-js/eslint-config-reactlynx": "workspace:*",
"@lynx-js/qrcode-rsbuild-plugin": "workspace:*",
"@lynx-js/react-rsbuild-plugin": "workspace:*",
"@lynx-js/rspeedy": "workspace:*",
Expand Down
2 changes: 1 addition & 1 deletion examples/react/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export function App() {
to see updates!
</text>
</view>
<view style={{ flex: 1 }}></view>
<view style={{ flex: 1 }} />
</view>
</view>
);
Expand Down
2 changes: 1 addition & 1 deletion examples/react/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"checkJs": true,
"isolatedDeclarations": false,
},
"include": ["src", "lynx.config.js", "test"],
"include": ["src", "lynx.config.js", "test", "eslint.config.js"],
"references": [
{ "path": "../../packages/react/tsconfig.json" },
{ "path": "../../packages/rspeedy/core/tsconfig.build.json" },
Expand Down
22 changes: 22 additions & 0 deletions packages/eslint-config-reactlynx/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
MIT License

Copyright (c) 2020-present The Preact Authors
Copyright (c) 2025 Lynx Authors.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
41 changes: 41 additions & 0 deletions packages/eslint-config-reactlynx/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# @lynx-js/eslint-config-reactlynx

An unopinionated baseline ESLint configuration for ReactLynx codebases. This is based on the [eslint-config-preact](https://github.com/preactjs/eslint-config-preact) package.

## Usage

Install eslint and this config:

```
npm i -D eslint @lynx-js/eslint-config-reactlynx
```

Then you need to configure it in `eslint.config.js`.

In TS project, you can use this config like this:

```ts
import configs from '@lynx-js/eslint-config-reactlynx/ts';

export default [
...configs,
{
files: ['src/**/*.{js,jsx,ts,tsx}'],
ignores: ['dist/'],
},
];
```

In JS project, you can use this config like this:

```js
import configs from '@lynx-js/eslint-config-reactlynx';

export default [
...configs,
{
files: ['src/**/*.{js,jsx}'],
ignores: ['dist/'],
},
];
```
53 changes: 53 additions & 0 deletions packages/eslint-config-reactlynx/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"name": "@lynx-js/eslint-config-reactlynx",
"version": "0.0.0",
"description": "Unopinionated base ESLint configuration for ReactLynx projects.",
"keywords": [
"react",
"lynx"
],
"repository": {
"type": "git",
"url": "https://github.com/lynx-family/lynx-stack.git",
"directory": "packages/eslint-config-reactlynx"
},
"license": "MIT",
"type": "module",
"exports": {
".": {
"types": "./lib/index.d.ts",
"default": "./lib/index.js"
},
"./ts": {
"types": "./lib/ts.d.ts",
"default": "./lib/ts.js"
},
"./package.json": "./package.json"
},
"types": "index.d.ts",
"files": [
"lib",
"LICENSE"
],
"scripts": {
"test": "vitest",
"test:type": "vitest --typecheck.only"
},
"dependencies": {
"@babel/eslint-parser": "^7.28.0",
"@babel/plugin-syntax-class-properties": "^7.12.13",
"@babel/plugin-syntax-jsx": "^7.27.1",
"@eslint/js": "^9.30.0",
"eslint-plugin-compat": "^6.0.2",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^5.2.0",
"globals": "^16.2.0",
"typescript-eslint": "^8.35.1"
},
"devDependencies": {
"eslint": "^9.30.0"
},
"peerDependencies": {
"eslint": "^8.57.1 || ^9.0.0"
}
}
160 changes: 160 additions & 0 deletions packages/eslint-config-reactlynx/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// Copyright 2025 The Lynx Authors. All rights reserved.
// Licensed under the MIT license that can be found in the
// LICENSE file in the root directory of this source tree.

import * as parser from '@babel/eslint-parser';
import js from '@eslint/js';
import type { Linter } from 'eslint';
import { defineConfig } from 'eslint/config';
import compat from 'eslint-plugin-compat';
import react from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';
import knownGlobals from 'globals';

const configs: Linter.Config<Linter.RulesRecord>[] = defineConfig([

Check failure on line 14 in packages/eslint-config-reactlynx/src/index.ts

View workflow job for this annotation

GitHub Actions / code-style-check

Type 'import("/home/runner/_work/lynx-stack/lynx-stack/node_modules/.pnpm/@types+eslint@9.6.1/node_modules/@types/eslint/index").Linter.Config<import("/home/runner/_work/lynx-stack/lynx-stack/node_modules/.pnpm/@types+eslint@9.6.1/node_modules/@types/eslint/index").Linter.RulesRecord>[]' is not assignable to type 'import("/home/runner/_work/lynx-stack/lynx-stack/node_modules/.pnpm/eslint@9.30.0_jiti@2.4.2/node_modules/eslint/lib/types/index").Linter.Config<import("/home/runner/_work/lynx-stack/lynx-stack/node_modules/.pnpm/eslint@9.30.0_jiti@2.4.2/node_modules/eslint/lib/types/index").Linter.RulesRecord>[]'.

Check failure on line 14 in packages/eslint-config-reactlynx/src/index.ts

View workflow job for this annotation

GitHub Actions / build / Build (Windows)

Type 'import("C:/Users/ContainerAdministrator/runner/_work/lynx-stack/lynx-stack/node_modules/.pnpm/@types+eslint@9.6.1/node_modules/@types/eslint/index").Linter.Config<import("C:/Users/ContainerAdministrator/runner/_work/lynx-stack/lynx-stack/node_modules/.pnpm/@types+eslint@9.6.1/node_modules/@types/eslint/index").Linte...' is not assignable to type 'import("C:/Users/ContainerAdministrator/runner/_work/lynx-stack/lynx-stack/node_modules/.pnpm/eslint@9.30.0_jiti@2.4.2/node_modules/eslint/lib/types/index").Linter.Config<import("C:/Users/ContainerAdministrator/runner/_work/lynx-stack/lynx-stack/node_modules/.pnpm/eslint@9.30.0_jiti@2.4.2/node_modules/eslint/lib/typ...'.
{
extends: [
js.configs.recommended,
],
languageOptions: {
// TODO: this is really only required for class property initializer methods, which are seeing declining usage.
// At some point, we should un-ship the custom parser and let ESLint use esprima.
parser: parser as unknown as Linter.Parser,
// Currently ignored due to the custom parser.
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
ecmaFeatures: {
modules: true,
impliedStrict: true,
jsx: true,
},
requireConfigFile: false,
babelOptions: {
plugins: [
'@babel/plugin-syntax-class-properties',
'@babel/plugin-syntax-jsx',
],
},
},
globals: {
...knownGlobals.browser,
...knownGlobals.es2015,
...knownGlobals.node,
expect: true,
browser: true,
global: true,
},
},
plugins: {
compat,

Check failure on line 50 in packages/eslint-config-reactlynx/src/index.ts

View workflow job for this annotation

GitHub Actions / code-style-check

Type '{ config: { "flat/recommended": FlatConfig<RulesRecord>; recommended: Config<RulesRecord>; }; meta: { name: string; version: string; }; configs: { ...; }; rules: { ...; }; }' is not assignable to type 'Plugin'.

Check failure on line 50 in packages/eslint-config-reactlynx/src/index.ts

View workflow job for this annotation

GitHub Actions / build / Build (Windows)

Type '{ config: { "flat/recommended": FlatConfig<RulesRecord>; recommended: Config<RulesRecord>; }; meta: { name: string; version: string; }; configs: { ...; }; rules: { ...; }; }' is not assignable to type 'Plugin'.
react,

Check failure on line 51 in packages/eslint-config-reactlynx/src/index.ts

View workflow job for this annotation

GitHub Actions / code-style-check

Type '{ deprecatedRules: Partial<{ 'boolean-prop-naming': RuleModule; 'button-has-type': RuleModule; 'checked-requires-onchange-or-readonly': RuleModule; 'default-props-match-prop-types': RuleModule; ... 98 more ...; 'void-dom-elements-no-children': RuleModule; }>; rules: { ...; }; configs: { ...; } & { ...; }; }' is not assignable to type 'Plugin'.

Check failure on line 51 in packages/eslint-config-reactlynx/src/index.ts

View workflow job for this annotation

GitHub Actions / build / Build (Windows)

Type '{ deprecatedRules: Partial<{ 'boolean-prop-naming': RuleModule; 'button-has-type': RuleModule; 'checked-requires-onchange-or-readonly': RuleModule; 'default-props-match-prop-types': RuleModule; ... 98 more ...; 'void-dom-elements-no-children': RuleModule; }>; rules: { ...; }; configs: { ...; } & { ...; }; }' is not assignable to type 'Plugin'.
'react-hooks': reactHooks,

Check failure on line 52 in packages/eslint-config-reactlynx/src/index.ts

View workflow job for this annotation

GitHub Actions / code-style-check

Type 'typeof import("/home/runner/_work/lynx-stack/lynx-stack/node_modules/.pnpm/eslint-plugin-react-hooks@5.2.0_eslint@9.30.0_jiti@2.4.2_/node_modules/eslint-plugin-react-hooks/index")' is not assignable to type 'Plugin'.

Check failure on line 52 in packages/eslint-config-reactlynx/src/index.ts

View workflow job for this annotation

GitHub Actions / build / Build (Windows)

Type 'typeof import("C:/Users/ContainerAdministrator/runner/_work/lynx-stack/lynx-stack/node_modules/.pnpm/eslint-plugin-react-hooks@5.2.0_eslint@9.30.0_jiti@2.4.2_/node_modules/eslint-plugin-react-hooks/index")' is not assignable to type 'Plugin'.
},
settings: {
// Preact CLI provides these defaults
targets: ['last 2 versions'],
polyfills: ['fetch', 'Promise'],
react: {
// eslint-plugin-preact interprets this as "h.createElement",
// however we only care about marking h() as being a used variable.
pragma: 'h',
// We use "react 16.0" to avoid pushing folks to UNSAFE_ methods.
version: '16.0',
},
},
rules: {
/**
* Preact / JSX rules
*/
'react/no-deprecated': 2,
'react/react-in-jsx-scope': 0, // handled this automatically
'react/display-name': [1, { ignoreTranspilerName: false }],
'react/jsx-no-bind': [1, {
ignoreRefs: true,
allowFunctions: true,
allowArrowFunctions: true,
}],
'react/jsx-no-comment-textnodes': 2,
'react/jsx-no-duplicate-props': 2,
'react/jsx-no-target-blank': 2,
'react/jsx-no-undef': 2,
'react/jsx-uses-react': 2, // debatable
'react/jsx-uses-vars': 2,
'react/jsx-key': [2, { checkFragmentShorthand: true }],
'react/self-closing-comp': 2,
'react/prefer-es6-class': 2,
'react/prefer-stateless-function': 1,
'react/require-render-return': 2,
'react/no-danger': 1,
// Legacy APIs not supported in Preact:
'react/no-did-mount-set-state': 2,
'react/no-did-update-set-state': 2,
'react/no-find-dom-node': 2,
'react/no-is-mounted': 2,
'react/no-string-refs': 2,

/**
* Hooks
*/
'react-hooks/rules-of-hooks': 2,
'react-hooks/exhaustive-deps': 1,

/**
* General JavaScript error avoidance
*/
'constructor-super': 2,
'no-caller': 2,
'no-const-assign': 2,
'no-delete-var': 2,
'no-dupe-class-members': 2,
'no-dupe-keys': 2,
'no-duplicate-imports': 2,
'no-else-return': 1,
'no-empty-pattern': 0,
'no-empty': 0,
'no-iterator': 2,
'no-lonely-if': 2,
'no-multi-str': 1,
'no-new-wrappers': 2,
'no-proto': 2,
'no-redeclare': 2,
'no-shadow-restricted-names': 2,
'no-shadow': 0,
'no-this-before-super': 2,
'no-undef-init': 2,
'no-unneeded-ternary': 2,
'no-unused-vars': [2, {
args: 'after-used',
ignoreRestSiblings: true,
}],
'no-useless-call': 1,
'no-useless-computed-key': 1,
'no-useless-concat': 1,
'no-useless-constructor': 1,
'no-useless-escape': 1,
'no-useless-rename': 1,
'no-var': 1,
'no-with': 2,

/**
* General JavaScript stylistic rules (disabled)
*/
strict: [2, 'never'], // assume type=module output (cli default)
'object-shorthand': 1,
'prefer-arrow-callback': 1,
'prefer-rest-params': 1,
'prefer-spread': 1,
'prefer-template': 1,
quotes: [0, 'single', {
avoidEscape: true,
allowTemplateLiterals: true,
}],
radix: 1, // parseInt(x, 10)
'unicode-bom': 2,
'valid-jsdoc': 0,
},
},
]);

export default configs;
Loading
Loading