GithubHelp home page GithubHelp logo

upleveled / eslint-config-upleveled Goto Github PK

View Code? Open in Web Editor NEW
32.0 3.0 11.0 4.15 MB

UpLeveled ESLint defaults for programming in JavaScript, TypeScript, React, Next.js, Node.js, Postgres.js

Home Page: https://www.npmjs.com/package/eslint-config-upleveled

JavaScript 99.96% CSS 0.04%
eslint eslint-config javascript next-js node-js react typescript postgresql

eslint-config-upleveled's Introduction

UpLeveled ESLint Config

UpLeveled ESLint defaults for programming in JavaScript, TypeScript, React, Next.js, Node.js, Postgres.js

Screenshot of errors and warnings generated by this configuration

Setup

To add ESLint configuration to a project, install the dependencies and add the config files:

pnpm add --save-dev eslint-config-upleveled@latest
pnpm upleveled-eslint-install

Verify Setup

To verify that the configuration is working properly, you can try pasting the contents of __tests__/index.js into a new file and seeing whether you get the same warning messages as in the screenshot at the beginning of the readme (you may need to hover over the words underlined with orange / red squiggly lines).

eslint-config-upleveled's People

Contributors

dependabot[bot] avatar eprince-hub avatar josehower avatar karlhorky avatar prochalu avatar renovate-bot avatar renovate[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

eslint-config-upleveled's Issues

SafeQL node-gyp error when switching from macOS/Linux to Windows

When switching from macOS or Linux to Windows, a compatibility issue arises with the SafeQL ESLint configuration. SafeQL, which is currently incompatible with Windows, throws a node-gyp error when installing the dependencies in a Next.js PostgreSQL project developed on Mac/Linux and attempting to run it on a Windows machine. On macOS/Linux, ESLint includes SafeQL and its dependencies, libpg-query and @ts-safeql/eslint-plugin.

The node-gyp error:

$ yarn add --dev @ts-safeql/eslint-plugin libpg-query
yarn add v1.22.19
[1/4] �  Resolving packages...
warning Resolution field "[email protected]" is incompatible with requested version "eslint-plugin-jest@^25.3.0"
[2/4] �  Fetching packages...
[3/4] �  Linking dependencies...
warning "@emotion/react > @emotion/[email protected]" has unmet peer dependency "@babel/core@^7.0.0".
warning "@emotion/react > @emotion/babel-plugin > @babel/[email protected]" has unmet peer dependency "@babel/core@^7.0.0-0".
warning " > @babel/[email protected]" has unmet peer dependency "@babel/core@>=7.11.0".
warning " > [email protected]" has unmet peer dependency "@babel/plugin-syntax-flow@^7.14.5".
warning " > [email protected]" has unmet peer dependency "@babel/plugin-transform-react-jsx@^7.14.9".
[4/4] �  Building fresh packages...
[-/4] ⠁ waiting...
[-/4] ⠁ waiting...
[-/4] ⠂ waiting...
error C:\Users\Victor\projects\debugging-programs\next-js-ecommerce-store\node_modules\libpg-query: Command failed.
Exit code: 1
Command: node-pre-gyp install --fallback-to-build
Arguments:
Directory: C:\Users\Victor\projects\debugging-programs\next-js-ecommerce-store\node_modules\libpg-query
Output:
node-pre-gyp info it worked if it ends with ok
node-pre-gyp info using [email protected]
node-pre-gyp info using [email protected] | win32 | x64
node-pre-gyp info check checked for "C:\Users\Victor\projects\debugging-programs\next-js-ecommerce-store\node_modules\libpg-query\build\Release\queryparser.node" (not found)
node-pre-gyp http GET https://supabase-public-artifacts-bucket.s3.amazonaws.com/libpg-query-node/queryparser-v13.2.5-node-v108-win32-x64.tar.gz
node-pre-gyp ERR! install response status 404 Not Found on https://supabase-public-artifacts-bucket.s3.amazonaws.com/libpg-query-node/queryparser-v13.2.5-node-v108-win32-x64.tar.gz
node-pre-gyp WARN Pre-built binaries not installable for [email protected] and [email protected] (node-v108 ABI, unknown) (falling back to source compile with node-gyp)
node-pre-gyp WARN Hit error response status 404 Not Found on https://supabase-public-artifacts-bucket.s3.amazonaws.com/libpg-query-node/queryparser-v13.2.5-node-v108-win32-x64.tar.gz
gyp info it worked if it ends with ok
gyp info using [email protected]
gyp info using [email protected] | win32 | x64
gyp info ok
gyp info it worked if it ends with ok
gyp info using [email protected]
gyp info using [email protected] | win32 | x64
gyp info find Python using Python version 3.9.6 found at "C:\Users\Victor\AppData\Local\Programs\Python\Python39\python.exe"
gyp info find VS using VS2017 (15.9.28307.1585) found at:
gyp info find VS "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools"
gyp info find VS run with --verbose for detailed information
gyp info spawn C:\Users\Victor\AppData\Local\Programs\Python\Python39\python.exe
gyp info spawn args [
gyp info spawn args   'C:\\Users\\Victor\\projects\\debugging-programs\\next-js-ecommerce-store\\node_modules\\node-gyp\\gyp\\gyp_main.py',
gyp info spawn args   'binding.gyp',
gyp info spawn args   '-f',
gyp info spawn args   'msvs',
gyp info spawn args   '-I',
gyp info spawn args   'C:\\Users\\Victor\\projects\\debugging-programs\\next-js-ecommerce-store\\node_modules\\libpg-query\\build\\config.gypi',
gyp info spawn args   '-I',
gyp info spawn args   'C:\\Users\\Victor\\projects\\debugging-programs\\next-js-ecommerce-store\\node_modules\\node-gyp\\addon.gypi',
gyp info spawn args   '-I',
gyp info spawn args   'C:\\Users\\Victor\\AppData\\Local\\node-gyp\\Cache\\18.5.0\\include\\node\\common.gypi',
gyp info spawn args   '-Dlibrary=shared_library',
gyp info spawn args   '-Dvisibility=default',
gyp info spawn args   '-Dnode_root_dir=C:\\Users\\Victor\\AppData\\Local\\node-gyp\\Cache\\18.5.0',
gyp info spawn args   '-Dnode_gyp_dir=C:\\Users\\Victor\\projects\\debugging-programs\\next-js-ecommerce-store\\node_modules\\node-gyp',
gyp info spawn args   '-Dnode_lib_file=C:\\\\Users\\\\Victor\\\\AppData\\\\Local\\\\node-gyp\\\\Cache\\\\18.5.0\\\\<(target_arch)\\\\node.lib',
gyp info spawn args   '-Dmodule_root_dir=C:\\Users\\Victor\\projects\\debugging-programs\\next-js-ecommerce-store\\node_modules\\libpg-query',
gyp info spawn args   '-Dnode_engine=v8',
gyp info spawn args   '--depth=.',
gyp info spawn args   '--no-parallel',
gyp info spawn args   '--generator-output',
gyp info spawn args   'C:\\Users\\Victor\\projects\\debugging-programs\\next-js-ecommerce-store\\node_modules\\libpg-query\\build',
gyp info spawn args   '-Goutput_dir=.'
gyp info spawn args ]
gyp info ok
gyp info it worked if it ends with ok
gyp info using [email protected]
gyp info using [email protected] | win32 | x64
gyp info spawn C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\MSBuild\15.0\Bin\MSBuild.exe
gyp info spawn args [
gyp info spawn args   'build/binding.sln',
gyp info spawn args   '/clp:Verbosity=minimal',
gyp info spawn args   '/nologo',
gyp info spawn args   '/p:Configuration=Release;Platform=x64'
gyp info spawn args ]
Building the projects in this solution one at a time. To enable parallel build, please add the "/m" switch.
  nothing.c
  win_delay_load_hook.cc
  nothing.vcxproj -> C:\Users\Victor\projects\debugging-programs\next-js-ecommerce-store\node_modules\libpg-query\build\Release\\nothing.lib
  prebuild_dependencies
  The system cannot find the path specified.
C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\Common7\IDE\VC\VCTargets\Microsoft.CppCommon.targets(209,5): error MSB6006: "cmd.exe" exited with code 1. [C:\Users\Victor\projects\debugging-programs\next-js-ecommerce-store\node_modules\libpg-query\build\queryparser.vcxproj]
gyp ERR! build error
gyp ERR! stack Error: `C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\MSBuild\15.0\Bin\MSBuild.exe` failed with exit code: 1
gyp ERR! stack     at ChildProcess.onExit (C:\Users\Victor\projects\debugging-programs\next-js-ecommerce-store\node_modules\node-gyp\lib\build.js:194:23)
gyp ERR! stack     at ChildProcess.emit (node:events:537:28)
gyp ERR! stack     at ChildProcess._handle.onexit (node:internal/child_process:291:12)
gyp ERR! System Windows_NT 10.0.22000
gyp ERR! command "C:\\Program Files\\nodejs\\node.exe" "C:\\Users\\Victor\\projects\\debugging-programs\\next-js-ecommerce-store\\node_modules\\node-gyp\\bin\\node-gyp.js" "build" "--fallback-to-build"
"--module=C:\\Users\\Victor\\projects\\debugging-programs\\next-js-ecommerce-store\\node_modules\\libpg-query\\build\\Release\\queryparser.node" "--module_name=queryparser" "--module_path=C:\\Users\\Victor\\projects\\debugging-programs\\next-js-ecommerce-store\\node_modules\\libpg-query\\build\\Release" "--napi_version=8" "--node_abi_napi=napi" "--napi_build_version=0" "--node_napi_label=node-v108"
gyp ERR! cwd C:\Users\Victor\projects\debugging-programs\next-js-ecommerce-store\node_modules\libpg-query
gyp ERR! node -v v18.5.0
gyp ERR! node-gyp -v v8.4.1
gyp ERR! not ok
node-pre-gyp ERR! build error
node-pre-gyp ERR! stack Error: Failed to execute 'C:\Program Files\nodejs\node.exe C:\Users\Victor\projects\debugging-programs\next-js-ecommerce-store\node_modules\node-gyp\bin\node-gyp.js build --fallback-to-build --module=C:\Users\Victor\projects\debugging-programs\next-js-ecommerce-store\node_modules\libpg-query\build\Release\queryparser.node --module_name=queryparser --module_path=C:\Users\Victor\projects\debugging-programs\next-js-ecommerce-store\node_modules\libpg-query\build\Release --napi_version=8 --node_abi_napi=napi --napi_build_version=0 --node_napi_label=node-v108' (1)
node-pre-gyp ERR! stack     at ChildProcess.<anonymous> (C:\Users\Victor\projects\debugging-programs\next-js-ecommerce-store\node_modules\@mapbox\node-pre-gyp\lib\util\compile.js:89:23)
node-pre-gyp ERR! stack     at ChildProcess.emit (node:events:537:28)
node-pre-gyp ERR! stack     at maybeClose (node:internal/child_process:1091:16)
node-pre-gyp ERR! stack     at ChildProcess._handle.onexit (node:internal/child_process:302:5)
node-pre-gyp ERR! System Windows_NT 10.0.22000
node-pre-gyp ERR! command "C:\\Program Files\\nodejs\\node.exe" "C:\\Users\\Victor\\projects\\debugging-programs\\next-js-ecommerce-store\\node_modules\\@mapbox\\node-pre-gyp\\bin\\node-pre-gyp" "install" "--fallback-to-build"
node-pre-gyp ERR! cwd C:\Users\Victor\projects\debugging-programs\next-js-ecommerce-store\node_modules\libpg-query
node-pre-gyp ERR! node -v v18.5.0
node-pre-gyp ERR! node-pre-gyp -v v1.0.10
node-pre-gyp ERR! not ok
Failed to execute 'C:\Program Files\nodejs\node.exe C:\Users\Victor\projects\debugging-programs\next-js-ecommerce-store\node_modules\node-gyp\bin\node-gyp.js build --fallback-to-build --module=C:\Users\Victor\projects\debugging-programs\next-js-ecommerce-store\node_modules\libpg-query\build\Release\queryparser.node --module_name=queryparser --module_path=C:\Users\Victor\projects\debugging-programs\nex

To resolve this error after switching from macOS/Linux to Windows, follow these steps:

  1. Remove the dependencies libpg-query and @ts-safeql/eslint-plugin using the command: pnpm remove libpg-query @ts-safeql/eslint-plugin
  2. Install the ESLint configuration again

However, this will cause problems if you switch back to macOS. If you want to switch back to macOS, then you will need to add these dependencies back:

  1. Install the ESLint configuration again

npx commands break for some on Windows

$ npx install-peerdeps --yarn --dev @upleveled/eslint-config-upleveled
install-peerdeps v2.0.3
ERR There was a problem connecting to the registry: status code 405
npm ERR! code 1
npm ERR! path C:\Users\matth\projects\random-color-generator
npm ERR! command failed
npm ERR! command C:\Windows\system32\cmd.exe /d /s /c install-peerdeps --yarn --dev @upleveled\eslint-config-upleveled
npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\matth\AppData\Local\npm-cache\_logs\2021-01-20T14_24_32_241Z-debug.log

Contents of C:\Users\matth\AppData\Local\npm-cache\_logs\2021-01-20T14_24_32_241Z-debug.log:

0 verbose cli [
0 verbose cli   'C:\\Program Files\\nodejs\\node.exe',
0 verbose cli   'C:\\Program Files\\nodejs\\node_modules\\npm\\bin\\npm-cli.js',
0 verbose cli   'exec',
0 verbose cli   '--',
0 verbose cli   'install-peerdeps',
0 verbose cli   '--yarn',
0 verbose cli   '--dev',
0 verbose cli   '@upleveled/eslint-config-upleveled'
0 verbose cli ]
1 info using [email protected]
2 info using [email protected]
3 timing config:load:defaults Completed in 4ms
4 timing config:load:file:C:\Program Files\nodejs\node_modules\npm\npmrc Completed in 3ms
5 timing config:load:builtin Completed in 3ms
6 timing config:load:cli Completed in 2ms
7 timing config:load:env Completed in 2ms
8 timing config:load:file:C:\Users\matth\projects\random-color-generator\.npmrc Completed in 1ms
9 timing config:load:project Completed in 4ms
10 timing config:load:file:C:\Users\matth\.npmrc Completed in 0ms
11 timing config:load:user Completed in 0ms
12 timing config:load:file:C:\Users\matth\AppData\Roaming\npm\etc\npmrc Completed in 2ms
13 timing config:load:global Completed in 3ms
14 timing config:load:cafile Completed in 0ms
15 timing config:load:validate Completed in 2ms
16 timing config:load:setUserAgent Completed in 2ms
17 timing config:load:setEnvs Completed in 3ms
18 timing config:load Completed in 26ms
19 verbose npm-session b356bacce3b7f754
20 timing npm:load Completed in 59ms
21 http fetch GET 304 https://registry.npmjs.org/install-peerdeps 416ms (from cache)
22 timing arborist:ctor Completed in 3ms
23 timing arborist:ctor Completed in 0ms
24 timing arborist:ctor Completed in 1ms
25 timing command:exec Completed in 3173ms
26 verbose stack Error: command failed
26 verbose stack     at ChildProcess.<anonymous> (C:\Program Files\nodejs\node_modules\npm\node_modules\@npmcli\promise-spawn\index.js:64:27)
26 verbose stack     at ChildProcess.emit (node:events:376:20)
26 verbose stack     at maybeClose (node:internal/child_process:1063:16)
26 verbose stack     at Process.ChildProcess._handle.onexit (node:internal/child_process:295:5)
27 verbose pkgid [email protected]
28 verbose cwd C:\Users\matth\projects\random-color-generator
29 verbose Windows_NT 10.0.18363
30 verbose argv "C:\\Program Files\\nodejs\\node.exe" "C:\\Program Files\\nodejs\\node_modules\\npm\\bin\\npm-cli.js" "exec" "--" "install-peerdeps" "--yarn" "--dev" "@upleveled/eslint-config-upleveled"
31 verbose node v15.4.0
32 verbose npm  v7.0.15
33 error code 1
34 error path C:\Users\matth\projects\random-color-generator
35 error command failed
36 error command C:\Windows\system32\cmd.exe /d /s /c install-peerdeps --yarn --dev @upleveled\eslint-config-upleveled
37 verbose exit 1

`npm install` peer dependency resolution fails

https://replit.com/@karlhorky/node-random-emoji-generator-fall-2023-vienna-austria-x

$ npm i
npm notice 
npm notice New major version of npm available! 8.19.2 -> 10.0.0
npm notice Changelog: https://github.com/npm/cli/releases/tag/v10.0.0
npm notice Run npm install -g [email protected] to update!
npm notice 
npm ERR! code ERESOLVE
npm ERR! ERESOLVE could not resolve
npm ERR! 
npm ERR! While resolving: [email protected]
npm ERR! Found: @typescript-eslint/[email protected]
npm ERR! node_modules/@typescript-eslint/parser
npm ERR!   peer @typescript-eslint/parser@"^6.3.0" from [email protected]
npm ERR!   node_modules/eslint-config-upleveled
npm ERR!     dev eslint-config-upleveled@"^5.0.4" from the root project
npm ERR!   peer @typescript-eslint/parser@"^6.0.0 || ^6.0.0-alpha" from @typescript-eslint/[email protected]
npm ERR!   node_modules/@typescript-eslint/eslint-plugin
npm ERR!     peer @typescript-eslint/eslint-plugin@"^6.3.0" from [email protected]
npm ERR!     node_modules/eslint-config-upleveled
npm ERR!       dev eslint-config-upleveled@"^5.0.4" from the root project
npm ERR! 
npm ERR! Could not resolve dependency:
npm ERR! peer @typescript-eslint/parser@"^4.0.0 || ^5.0.0" from [email protected]
npm ERR! node_modules/eslint-plugin-jsx-expressions
npm ERR!   peer eslint-plugin-jsx-expressions@"^1.3.1" from [email protected]
npm ERR!   node_modules/eslint-config-upleveled
npm ERR!     dev eslint-config-upleveled@"^5.0.4" from the root project
npm ERR! 
npm ERR! Conflicting peer dependency: @typescript-eslint/[email protected]
npm ERR! node_modules/@typescript-eslint/parser
npm ERR!   peer @typescript-eslint/parser@"^4.0.0 || ^5.0.0" from [email protected]
npm ERR!   node_modules/eslint-plugin-jsx-expressions
npm ERR!     peer eslint-plugin-jsx-expressions@"^1.3.1" from [email protected]
npm ERR!     node_modules/eslint-config-upleveled
npm ERR!       dev eslint-config-upleveled@"^5.0.4" from the root project
npm ERR! 
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force, or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR! 
npm ERR! See /home/runner/.npm/eresolve-report.txt for a full report.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/runner/.npm/_logs/2023-09-05T11_35_09_217Z-debug-0.log

Convert DOM manipulation bans into a custom rule

Currently, we are using no-restricted-syntax config blocks to preventively ban querySelector querySeletorAll and getElementById. One of the issues with this is the warning messages don't allow us to add clickable links to the description of the error:

One way to solve this Is moving these checks into a new custom ESLint rule:

/** Currently it is not possible to use markdown links inside of the eslint warn/error message.
* related links:
* - https://github.com/microsoft/vscode/issues/54272
* - https://github.com/microsoft/vscode-eslint/issues/418
* - https://github.com/microsoft/vscode/issues/11847
*/
{
selector:
"ExpressionStatement CallExpression[callee.object.name='document'][callee.property.name='querySelector']",
message: `Using document.querySelector() can give unexpected results, especially if multiple elements match the query (for example, when the component is used more than once); as a piece of advice, use State or Refs instead
https://github.com/reactjs/reactjs.org/issues/4626#issuecomment-1117535930`,
},
{
selector:
"ExpressionStatement CallExpression[callee.object.name='document'][callee.property.name='querySelectorAll']",
message: `Using document.querySelectorAll() can give unexpected results, especially if multiple elements match the query (for example, when the component is used more than once); as a piece of advice, use State or Refs instead
https://github.com/reactjs/reactjs.org/issues/4626#issuecomment-1117535930`,
},
{
selector:
"ExpressionStatement CallExpression[callee.object.name='document'][callee.property.name='getElementById']",
message: `Using document.getElementById() can give unexpected results, especially if multiple elements match the query (for example, when the component is used more than once); as a piece of advice, use State or Refs instead
https://github.com/reactjs/reactjs.org/issues/4626#issuecomment-1117535930`,
},

`@npmcli/move-file` is deprecated

$ pnpm upleveled-eslint-install
Installing 4 ESLint config dependencies...
 WARN  1 deprecated subdependencies found: @npmcli/[email protected]

Not sure where this transitive dependency comes from, but maybe we can fix this

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

github-actions
.github/workflows/lint-check-types-test.yml
  • actions/checkout v4
  • pnpm/action-setup v2
  • actions/setup-node v4
npm
__tests__/package.json
  • next ^14.0.0
  • @types/react ^18.2.14
  • @types/react-dom ^18.2.6
  • eslint-config-upleveled ^8.0.1
  • postcss-styled-syntax ^0.6.0
  • prettier ^3.2.5
  • stylelint ^16.0.0
  • stylelint-config-upleveled ^1.0.2
  • typescript ^5.1.6
package.json
  • @babel/eslint-parser 7.24.5
  • @next/eslint-plugin-next 14.2.3
  • @typescript-eslint/eslint-plugin 7.8.0
  • @typescript-eslint/parser 7.8.0
  • eslint-config-flat-gitignore 0.1.5
  • eslint-import-resolver-typescript 3.6.1
  • eslint-plugin-import 2.29.1
  • eslint-plugin-jsx-a11y 6.8.0
  • eslint-plugin-jsx-expressions 1.3.2
  • eslint-plugin-react 7.34.1
  • eslint-plugin-react-hooks 4.6.2
  • eslint-plugin-security 3.0.0
  • eslint-plugin-sonarjs 0.25.1
  • eslint-plugin-testing-library 6.2.2
  • eslint-plugin-unicorn 52.0.0
  • eslint-plugin-upleveled 2.1.9
  • sort-package-json 2.10.0
  • @types/eslint 8.56.10
  • @typescript-eslint/utils 7.8.0
  • eslint-config-upleveled 8.0.1
  • prettier 3.2.5
  • prettier-plugin-embed 0.4.15
  • prettier-plugin-sql 0.18.0
  • stylelint 16.5.0
  • typescript 5.4.5
  • @types/eslint ^8.56.10
  • @types/node >=20.12.8
  • @types/react ^18.3.1
  • @types/react-dom ^18.3.0
  • eslint ^8.57.0
  • globals ^15.1.0
  • typescript ^5.4.5
  • node >=20.9.0

  • Check this box to trigger a request for Renovate to run again on this repository

Action Required: Fix Renovate Configuration

There is an error with this repository's Renovate configuration that needs to be fixed. As a precaution, Renovate will stop PRs until it is resolved.

Error type: Cannot find preset's package (group:remark). Note: this is a nested preset so please contact the preset author if you are unable to fix it yourself.

Merge existing `"paths"` config into `tsconfig.json`

Currently, installing the UpLeveled ESLint config will create a new tsconfig.json file, overwriting any existing files.

This is a problem on Next.js projects using shadcn/ui, which relies on the default import alias configuration @/* in the tsconfig.json file, created by create-next-app (see output below)

We could merge in specific values from the existing tsconfig.json from students, starting with the "paths" config.

➜  p mkdir a
➜  p cd a
➜  a pnpm create next-app@latest . --typescript --tailwind --eslint
.../Library/pnpm/store/v3/tmp/dlx-40701  |   +1 +
.../Library/pnpm/store/v3/tmp/dlx-40701  | Progress: resolved 1, reused 1, downloaded 0, added 1, done
✔ Would you like to use `src/` directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to customize the default import alias (@/*)? … No / Yes
Creating a new Next.js app in /Users/k/p/a.

Using pnpm.

Initializing project with template: app-tw


Installing dependencies:
- react
- react-dom
- next

Installing devDependencies:
- typescript
- @types/node
- @types/react
- @types/react-dom
- autoprefixer
- postcss
- tailwindcss
- eslint
- eslint-config-next

Packages: +352
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Progress: resolved 360, reused 352, downloaded 0, added 352, done

dependencies:
+ next 14.1.0
+ react 18.2.0
+ react-dom 18.2.0

devDependencies:
+ @types/node 20.11.24
+ @types/react 18.2.61
+ @types/react-dom 18.2.19
+ autoprefixer 10.4.17
+ eslint 8.57.0
+ eslint-config-next 14.1.0
+ postcss 8.4.35
+ tailwindcss 3.4.1
+ typescript 5.3.3

Done in 12.3s
Initialized a git repository.

Success! Created a at /Users/k/p/a

➜  a git:(main) cat tsconfig.json
{
  "compilerOptions": {
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "plugins": [
      {
        "name": "next"
      }
    ],
    "paths": {
      "@/*": ["./*"]
    }
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
  "exclude": ["node_modules"]
}
➜  a git:(main) pnpm add --save-dev eslint-config-upleveled@latest
pnpm upleveled-eslint-install
Progress: resolved 235, reused 224, downloaded 0, added 0
Packages: +132 -3
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Progress: resolved 484, reused 421, downloaded 55, added 132, done

devDependencies:
+ eslint-config-upleveled 7.8.1

 WARN  Issues with peer dependencies found
.
└─┬ eslint-config-upleveled 7.8.1
  └─┬ eslint-plugin-jsx-expressions 1.3.2
    └── ✕ unmet peer @typescript-eslint/parser@"^4.0.0 || ^5.0.0 || ^6.0.0": found 7.0.2 in eslint-config-upleveled

Done in 12.8s
Detected project type: Next.js
Setting "type": "module" in package.json...
Installing 3 ESLint config dependencies...
Packages: +50 -2
++++++++++++++++++++++++++++++++++++++++++++++++++--
Progress: resolved 533, reused 504, downloaded 21, added 49, done

devDependencies:
+ prettier 3.2.5
+ stylelint 16.2.1
+ stylelint-config-upleveled 1.0.7

 WARN  Issues with peer dependencies found
.
└─┬ eslint-config-upleveled 7.8.1
  └─┬ eslint-plugin-jsx-expressions 1.3.2
    └── ✕ unmet peer @typescript-eslint/parser@"^4.0.0 || ^5.0.0 || ^6.0.0": found 7.0.2 in eslint-config-upleveled

Done in 6.2s
✅ Done installing dependencies
Copying config files...
Copied .vscode/settings.json
Copied eslint.config.js
Copied prettier.config.js
Copied stylelint.config.js
Copied tsconfig.json (existing file overwritten)
✅ Done copying config files
Updating .gitignore...
✅ Done updating .gitignore
➜  a git:(main) ✗
➜  a git:(main) ✗ cat tsconfig.json
{
  "$schema": "https://json.schemastore.org/tsconfig",
  "extends": "eslint-config-upleveled/tsconfig.base.json",
  "compilerOptions": {
    "plugins": [
      {
        "name": "next"
      }
    ]
  },
  "include": [
    "**/*.ts",
    "**/*.tsx",
    "**/*.js",
    "**/*.jsx",
    "**/*.cjs",
    "**/*.mjs",
    ".next/types/**/*.ts"
  ]
}

Improve setup instructions for `libpg-query` and `@ts-safeql/eslint-plugin`

Guide for the Drone ESLint PostgreSQL SafeQL changes:

  • Talk with @Eprince-hub about the changes
  • Remove Next.js from the if condition that adds libpg-query and @ts-safeql/eslint-plugin
if (
  // Install SafeQL dependencies in Postgres.js projects
  'postgres' in projectDependencies &&
  // SafeQL currently not supported on Windows
  // https://github.com/ts-safeql/safeql/issues/80
  process.platform !== 'win32'
) {
  newDevDependenciesToInstall.push('@ts-safeql/eslint-plugin', 'libpg-query');
} 
  • Edit try/catch for the SafeQL config
} catch (error) {
  throw new Error(
    'The required dependencies for linting PostgreSQL have not been installed, please reinstall the ESLint config',
    error,
  );
}
  • Add an installation guide in the Learning Platform how to install the PostgreSQL client in projects and add the installation of ESLint to the steps, so that PostgreSQL is added before the ESLint configuration runs, avoiding the need to add libpg-query and @ts-safeql/eslint-plugin to the ignoreUnusedDependencies.
  • Update the lecture notes to include the guide on how to set up the PostgreSQL client
- connecting to that database from Node.js (databaseExample.mjs)
  - https://learn.upleveled.io/courses/bootcamp-pern/modules/cheatsheet-postgres/#xxx

create-react-app: Cannot read property 'ScopeType' of undefined

When using create-react-app with the following setup instructions, the error below occurs:

Instructions:

yarn create react-app my-app

cd my-app

CI=true npx install-peerdeps@latest --yarn --dev @upleveled/eslint-config-upleveled
cp node_modules/@upleveled/eslint-config-upleveled/templates/{*,.eslint*} .
touch .gitignore && grep -Fxq ".eslintcache" .gitignore || echo ".eslintcache" >> .gitignore

yarn upgrade react-scripts

Error:

Failed to compile

Error while loading rule '@typescript-eslint/naming-convention': Cannot read property 'ScopeType' of undefined

Occurred while linting /Users/k/p/my-app/src/index.js

Screen Shot 2021-06-21 at 11 05 27

Move Next.js patch to install script

We currently instruct students to patch Next.js to fix / add a few features to the app/ directory:

The goal will be to remove this patch as soon as possible, but it's unclear how long it will take for these fixes / new features to be implemented and accepted in Next.js.

For now, implement a patching system (using pnpm) within the ESLint config install script to apply the changes in the patch.

Convert Emotion styles / styled components bans into a custom rule

Currently, we are using no-restricted-syntax config blocks to ban creation of Emotion styles or styled components within components. One of the issues with this is the warning messages don't allow us to add clickable links to the description of the error:

One way to solve this Is moving these checks into a new custom ESLint rule:

// Currently it is not possible to use Markdown eg. links in ESLint warnings / error messages
//
// FIXME: Switch to a custom rule
// https://github.com/upleveled/eslint-config-upleveled/issues/126
{
selector:
'FunctionDeclaration VariableDeclaration:has(VariableDeclarator > TaggedTemplateExpression > MemberExpression[object.name="styled"][property]), FunctionDeclaration VariableDeclaration:has(VariableDeclarator > TaggedTemplateExpression[tag.name="css"])',
message:
'Declaring Emotion styles or a styled component within a React component will cause the element to get recreated, causing loss of state and other problems - see the react/no-unstable-nested-components docs for more info https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-unstable-nested-components.md',
},

Additionally, while we are doing this, we should optimize this rule to only trigger on React components (currently it is triggered on any FunctionDeclaration, which includes any function). One way of detecting a React component would be to use the component utilities from eslint-plugin-react:

https://github.com/jsx-eslint/eslint-plugin-react/blob/c8833f301314dab3e79ef7ac4cf863e4d5fa0019/lib/util/Components.js#L436-L444

Automate publishing of new versions

Prerequisite: #177

Publish new commits of eslint-config-upleveled in main that pass the tests automatically to npm.

For the version numbers, we may want to introduce some kind of commit message format like Conventional Commits. Should do some research here to determine what the best, least disruptive thing is.

We should probably also use the new npm Granular Access Tokens, including the IP ranges:

npm has supported automation tokens for quite some time. Automation tokens allow you to publish to any packages that the owner of the token has permission to. Until now, it was not possible to create tokens with a least privilege model—to limit the impact of an accidental or deliberate misuse of the token. The new granular access tokens will allow you to do exactly this. You can now create tokens that can publish only to a limited set of packages and/or scopes.

Granular access tokens also let you limit npm API access based on allowed IP ranges and come with an expiration period of up to one year.

Similar to upleveled/preflight#342

Invalid URL error with database credentials including special characters invalid for URLs

If a .env file has been set up with a PGUSERNAME, PGPASSWORD, PGHOST or PGDATABASE which include special characters that are invalid for usage in a URL, the SafeQL configuration value for databaseUrl that we use (which includes interpolation) will cause unusual problems:

(config.rules)['@ts-safeql/check-sql'] = [
'error',
{
connections: [
{
databaseUrl: `postgres://${process.env.PGUSERNAME}:${process.env.PGPASSWORD}@${process.env.PGHOST}:5432/${process.env.PGDATABASE}`,
tagName: 'sql',
fieldTransform: 'camel',
transform: '{type}[]',
},
],
},
];

Running ESLint or Preflight in such a project will lead to error messages such as TypeError [ERR_INVALID_URL]: Invalid URL:

image

We could escape the special characters in these values to avoid these types of errors with the interpolation.

TODOs:

  • research for an official source (eg. official docs) on which characters need to be escaped in a PostgreSQL URL
  • research for an official source (eg. official docs) on how we should escape them. seems like maybe percent-encoding is ok here?

Create new `create-` starter package for 1-command install

pnpm, npm and Yarn support starter kit packages prefixed with create-, allowing running arbitrary commands via:

$ pnpm create xxx  # This example would install a package called create-xxx
                   # globally and then run the `create-xxx` bin inside
  • Convert to monorepo? (or create new repository for the new create- starter kit package)
  • Split out scripts/install.js into new create- package
  • Publish new packages

TODO: Think of name (maybe create-upleveled, following create-sst, create-expo) that also encompasses other things that we want to set up in a student's project:

  • Stylelint, which we added in #171
  • Patching system for Next.js package #204
  • Installing SafeQL dependencies in an existing project (during database setup)
  • Creating Ley migration files with ESM and TypeScript, instead of having to remember pnpm ley new <filename with extension> --esm
  • Creating standard GitHub Actions CI/CD workflows from templates (eg. unit testing, linting and type checking - optionally with integration tests and deployment), like this or this

create-x package examples

Old Suggestion for name:

@upleveled/create-eslint to enable command yarn create @upleveled/eslint

Supersedes #161

Convert to ESLint 8 New Flat Config

  • Convert config to flag config #301
  • Release prerelease version
  • Test with all possible configurations
  • Convert plugin to flag config

ESLint 8 has a new "flat" configuration format:

https://eslint.org/docs/latest/user-guide/configuring/configuration-files-new

It seems like we need a lot of changes to our configuration:

+const reactAppConfig = require('eslint-config-react-app');
+const nextPlugin = require('@next/eslint-plugin-next');
+const typescriptEslintPlugin = require('@typescript-eslint/eslint-plugin');
+const upleveledPlugin = require('@upleveled/eslint-plugin-upleveled');
+const cypressPlugin = require('eslint-plugin-cypress');
+const importPlugin = require('eslint-plugin-import');
+const jsxA11yPlugin = require('eslint-plugin-jsx-a11y');
+const jsxExpressionsPlugin = require('eslint-plugin-jsx-expressions');
+const reactPlugin = require('eslint-plugin-react');
+const reactHooksPlugin = require('eslint-plugin-react-hooks');
+const securityPlugin = require('eslint-plugin-security');
+const sonarJsPlugin = require('eslint-plugin-sonarjs');
+const unicornPlugin = require('eslint-plugin-unicorn');
+const typescriptEslintParser = require('@typescript-eslint/parser');
+
module.exports = {
+  ...reactAppConfig,
+  ...jsxA11yPlugin.configs.recommended,
-  extends: ['react-app', 'plugin:jsx-a11y/recommended'],
-  parser: '@typescript-eslint/parser',
-  parserOptions: {
-    project: './tsconfig.json',
-    extraFileExtensions: ['.cjs', '.mjs'],
-  },
+  languageOptions: {
+  parser: typescriptEslintParser,
+  parserOptions: {
+    project: './tsconfig.json',
+    extraFileExtensions: ['.cjs', '.mjs'],
+  },
+  ecmaVersion: '2020'
+},
  plugins: [
-    '@next/next',
-    '@typescript-eslint',
-    '@upleveled/upleveled',
-    'cypress',
-    'import',
-    'jsx-a11y',
-    'jsx-expressions',
-    'react',
-    'react-hooks',
-    'security',
-    'sonarjs',
-    'unicorn',
+    nextPlugin,
+    typescriptEslintPlugin,
+    upleveledPlugin,
+    cypressPlugin,
+    importPlugin,
+    jsxA11yPlugin,
+    jsxExpressionsPlugin,
+    reactPlugin,
+    reactHooksPlugin,
+    securityPlugin,
+    sonarJsPlugin,
+    unicornPlugin,
  ],
-  env: {
-    es2020: true,
-    'cypress/globals': true,
-  },
  settings: {
    'import/resolver': {
      // Load <rootdir>/tsconfig.json
      typescript: {
        // Always try resolving any corresponding @types/* folders
        alwaysTryTypes: true,
      },
    },
  },
  rules: {
    // Error about importing next/document in a page other than pages/_document.js
    // https://github.com/vercel/next.js/blob/canary/errors/no-document-import-in-page.md
    '@next/next/no-document-import-in-page': 'error',
    // Error about importing next/head in pages/_document.js
    // https://github.com/vercel/next.js/blob/canary/errors/no-head-import-in-document.md
    '@next/next/no-head-import-in-document': 'error',
    // Error about using <a> element to navigate to a page route instead of Next.js <Link /> component
    // https://github.com/vercel/next.js/blob/canary/errors/no-html-link-for-pages.md
    '@next/next/no-html-link-for-pages': 'error',
    // Warn about using a custom font in a single page instead of in pages/_document.js
    // https://github.com/vercel/next.js/blob/canary/errors/no-page-custom-font.md
    '@next/next/no-page-custom-font': 'warn',
    // Warn about setting a title for all pages in a single page by importing <Head /> from next/document
    // https://github.com/vercel/next.js/blob/canary/errors/no-title-in-document-head.md
    '@next/next/no-title-in-document-head': 'warn',
    // Error on dangerous types like:
    // - uppercase primitive types
    // - Function
    // - Object and {}
    // https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/ban-types.md
    '@typescript-eslint/ban-types': ['error'],
    // Warn on variables not following naming convention
    // https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/naming-convention.md
    '@typescript-eslint/naming-convention': [
      'warn',
      // Defaults from @typescript-eslint/eslint-plugin
      // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/naming-convention.md#options
      {
        selector: 'default',
        format: ['camelCase'],
        leadingUnderscore: 'allow',
        trailingUnderscore: 'allow',
      },
      {
        selector: 'variable',
        types: ['boolean', 'string', 'number', 'array'],
        format: ['camelCase', 'UPPER_CASE'],
        leadingUnderscore: 'allow',
        trailingUnderscore: 'allow',
      },
      {
        selector: 'typeLike',
        format: ['PascalCase'],
      },
      // Allow PascalCase for functions (React components)
      {
        selector: 'function',
        format: ['camelCase', 'PascalCase'],
        leadingUnderscore: 'allow',
      },
      {
        selector: 'variable',
        types: ['function'],
        format: ['camelCase', 'PascalCase'],
        leadingUnderscore: 'allow',
      },
      {
        selector: 'property',
        format: null,
      },
      {
        selector: 'parameter',
        format: ['camelCase', 'snake_case', 'PascalCase'],
      },
    ],
    // Warn on dangling promises without await
    '@typescript-eslint/no-floating-promises': ['warn', { ignoreVoid: false }],
    // Warn about variable shadowing
    // https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-shadow.md
    '@typescript-eslint/no-shadow': 'warn',
    // Disable built-in ESLint no-constant-condition
    // to use the more powerful @typescript-eslint/no-unnecessary-condition
    // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unnecessary-condition.md
    'no-constant-condition': 'off',
    '@typescript-eslint/no-unnecessary-condition': 'warn',
    // Prevent unnecessary type arguments
    // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unnecessary-type-arguments.md
    '@typescript-eslint/no-unnecessary-type-arguments': 'warn',
    // Prevent unnecessary type assertions
    // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unnecessary-type-assertion.md
    '@typescript-eslint/no-unnecessary-type-assertion': 'warn',
    // Disable built-in ESLint no-unused-vars
    // to use the more powerful @typescript-eslint/no-unused-vars
    // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unused-vars.md
    // https://eslint.org/docs/rules/no-unused-vars
    'no-unused-vars': 'off',
    // No need for this, @typescript-eslint/parser fully understands JSX semantics
    // https://github.com/typescript-eslint/typescript-eslint/issues/2985#issuecomment-771771967
    'react/jsx-uses-vars': 'off',
    '@typescript-eslint/no-unused-vars': [
      'warn',
      {
        args: 'after-used',
        ignoreRestSiblings: true,
      },
    ],
    // Disable built-in ESLint no-use-before-define
    // in order to enable the rule from the
    // @typescript-eslint plugin
    'no-use-before-define': 'off',
    '@typescript-eslint/no-use-before-define': 'error',
    // Warn about submit handler without event.preventDefault()
    // https://github.com/upleveled/eslint-plugin-upleveled/blob/main/docs/rules/no-submit-handler-without-preventDefault.md
    '@upleveled/upleveled/no-submit-handler-without-preventDefault': 'error',
    // Warn about unnecessary HTML attributes
    // https://github.com/upleveled/eslint-plugin-upleveled/blob/main/docs/rules/no-unnecessary-html-attributes.md
    '@upleveled/upleveled/no-unnecessary-html-attributes': 'warn',
    // Warn about unnecessary for and id attributes with inputs nested inside of labels
    // https://github.com/upleveled/eslint-plugin-upleveled/blob/main/docs/rules/no-unnecessary-for-and-id.md
    '@upleveled/upleveled/no-unnecessary-for-and-id': 'warn',
    // Warn about unnecessary interpolations in template strings
    // https://github.com/upleveled/eslint-plugin-upleveled/blob/main/docs/rules/no-unnecessary-interpolations.md
    '@upleveled/upleveled/no-unnecessary-interpolations': 'warn',
    // Allow leaving out curlies only with single-line condition blocks
    // https://github.com/eslint/eslint/blob/master/docs/rules/curly.md#multi-line
    curly: ['error', 'multi-line', 'consistent'],
    // Error out on imports that don't match
    // the underlying file system
    // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-unresolved.md
    'import/no-unresolved': 'error',
    // Remove `href` warnings on anchor tags for Next.js
    // Issue in Next.js: https://github.com/zeit/next.js/issues/5533
    // Fix: https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/issues/402#issuecomment-368305051
    'jsx-a11y/anchor-is-valid': [
      'error',
      {
        components: ['Link'],
        specialLink: ['hrefLeft', 'hrefRight'],
        aspects: ['invalidHref', 'preferButton'],
      },
    ],
    // Disable obsolete rule
    // https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/issues/398#issuecomment-728976688
    'jsx-a11y/no-onchange': 'off',
    // Disallow potentially falsey string and number values in logical && expressions
    // https://github.com/hpersson/eslint-plugin-jsx-expressions/blob/master/docs/rules/strict-logical-expressions.md
    'jsx-expressions/strict-logical-expressions': 'error',
    // Warn on async promise executor function
    // https://github.com/eslint/eslint/blob/main/docs/src/rules/no-async-promise-executor.md
    'no-async-promise-executor': 'warn',
    // Warn on return in promise executor function
    // https://github.com/eslint/eslint/blob/main/docs/src/rules/no-promise-executor-return.md
    'no-promise-executor-return': 'warn',
    // Warn on restricted syntax
    'no-restricted-syntax': [
      'warn',
      // Currently it is not possible to use Markdown eg. links in ESLint warnings / error messages
      //
      // FIXME: Switch to a custom rule
      // https://github.com/upleveled/eslint-config-upleveled/issues/123
      {
        selector:
          "ExpressionStatement CallExpression[callee.object.name='document'][callee.property.name='querySelector'], VariableDeclaration VariableDeclarator CallExpression[callee.object.name='document'][callee.property.name='querySelector']",
        message: `Using document.querySelector() can lead to problems, and is not commonly used in React code - prefer instead usage of basic React patterns such as state and controlled components
https://github.com/reactjs/reactjs.org/issues/4626#issuecomment-1117535930`,
      },
      {
        selector:
          "ExpressionStatement CallExpression[callee.object.name='document'][callee.property.name='querySelectorAll'], VariableDeclaration VariableDeclarator CallExpression[callee.object.name='document'][callee.property.name='querySelectorAll']",
        message: `Using document.querySelectorAll() can lead to problems, and is not commonly used in React code - prefer instead usage of basic React patterns such as state and controlled components
https://github.com/reactjs/reactjs.org/issues/4626#issuecomment-1117535930`,
      },
      {
        selector:
          "ExpressionStatement CallExpression[callee.object.name='document'][callee.property.name='getElementById'], VariableDeclaration VariableDeclarator[init.callee.object.name!='ReactDOM'][init.callee.property.name!='createRoot'] CallExpression[callee.object.name='document'][callee.property.name='getElementById']",
        message: `Using document.getElementById() can lead to problems, and is not commonly used in React code - prefer instead usage of basic React patterns such as state and controlled components
https://github.com/reactjs/reactjs.org/issues/4626#issuecomment-1117535930`,
      },

      // Currently it is not possible to use Markdown eg. links in ESLint warnings / error messages
      //
      // FIXME: Switch to a custom rule
      // https://github.com/upleveled/eslint-config-upleveled/issues/126
      {
        selector:
          'FunctionDeclaration VariableDeclaration:has(VariableDeclarator > TaggedTemplateExpression > MemberExpression[object.name="styled"][property]), FunctionDeclaration VariableDeclaration:has(VariableDeclarator > TaggedTemplateExpression[tag.name="css"])',
        message:
          'Declaring Emotion styles or a styled component within a React component will cause the element to get recreated, causing loss of state and other problems - see the react/no-unstable-nested-components docs for more info https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-unstable-nested-components.md',
      },

      {
        selector:
          "ExpressionStatement CallExpression[callee.object.name='location'][callee.property.name='reload']",
        message:
          'Update content and elements with React instead of using location.reload()',
      },
      {
        selector:
          "ExpressionStatement CallExpression[callee.object.object.name='window'][callee.object.property.name='location'][callee.property.name='reload']",
        message:
          'Update content and elements with React instead of using location.reload()',
      },

      {
        selector:
          "JSXAttribute[name.name='href'] > Literal[value=/^\\./], JSXAttribute[name.name='href'] > JSXExpressionContainer > TemplateLiteral TemplateElement:first-child[value.cooked=/^\\./]",
        message:
          'Always start href relative URLs with a forward slash (aka use root relative URLs) - read more at https://www.webdevbydoing.com/absolute-relative-and-root-relative-file-paths/',
      },
    ],
    // Warn on usage of var (which doesn't follow block scope rules)
    // https://eslint.org/docs/rules/no-var
    'no-var': 'warn',
    // Warn about non-changing variables not being constants
    // https://eslint.org/docs/rules/prefer-const
    'prefer-const': 'warn',
    // Warn on promise rejection without Error object
    // https://github.com/eslint/eslint/blob/main/docs/src/rules/prefer-promise-reject-errors.md
    'prefer-promise-reject-errors': 'warn',
    // Warn about state variable and setter names which are not symmetrically named
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/hook-use-state.md
    'react/hook-use-state': 'warn',
    // Error on missing sandbox attribute on iframes (good security practice)
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/iframe-missing-sandbox.md
    'react/iframe-missing-sandbox': 'error',
    // Warn about unnecessary curly braces around props and string literal children
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-curly-brace-presence.md
    'react/jsx-curly-brace-presence': [
      'warn',
      { props: 'never', children: 'never', propElementValues: 'always' },
    ],
    // Error on missing or incorrect `key` props in maps in JSX
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-key.md
    'react/jsx-key': [
      'error',
      {
        checkFragmentShorthand: true,
        checkKeyMustBeforeSpread: true,
        warnOnDuplicates: true,
      },
    ],
    // Error on useless React fragments
    'react/jsx-no-useless-fragment': 'warn',
    // Warn if a `key` is set to an `index`
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-array-index-key.md
    'react/no-array-index-key': ['error'],
    // Error on invalid HTML attributes (only `rel` as of March 2022)
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-invalid-html-attribute.md
    'react/no-invalid-html-attribute': 'error',
    // Warn on usage of `class` prop instead of `className`
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unknown-property.md
    'react/no-unknown-property': ['warn', { ignore: ['css'] }],
    // Error on creating components within components
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unstable-nested-components.md
    'react/no-unstable-nested-components': 'error',
    // Error on unused React prop types
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unused-prop-types.md
    'react/no-unused-prop-types': 'warn',
    // Disable rule because the new JSX transform in React 17,
    // Next.js and Gatsby no longer requires the import.
    // https://github.com/yannickcr/eslint-plugin-react/issues/2440#issuecomment-683433266
    'react/react-in-jsx-scope': 'off',
    // Warn about components that have a closing
    // tag but no children
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/self-closing-comp.md
    'react/self-closing-comp': 'warn',
    // Error on passing children to void elements
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/void-dom-elements-no-children.md
    'react/void-dom-elements-no-children': 'error',
    // Warn on missing `await` within async functions
    // https://eslint.org/docs/rules/require-await
    'require-await': 'warn',
    // Error on child_process.exec usage with variables
    // https://github.com/nodesecurity/eslint-plugin-security#detect-child-process
    'security/detect-child-process': 'error',
    // Error on running eval with a variable
    // https://github.com/nodesecurity/eslint-plugin-security#detect-eval-with-expression
    'security/detect-eval-with-expression': 'error',
    // Warn on comments without a space between the `//` and the comment
    // https://github.com/eslint/eslint/blob/master/docs/rules/spaced-comment.md
    'spaced-comment': ['warn', 'always', { markers: ['/'] }],
    // Warn on duplicate code in if / else if branches
    // https://github.com/SonarSource/eslint-plugin-sonarjs/blob/master/docs/rules/no-duplicated-branches.md
    'sonarjs/no-duplicated-branches': 'warn',
    // Warn on identical conditions for if / else if chains
    // https://github.com/SonarSource/eslint-plugin-sonarjs/blob/master/docs/rules/no-identical-conditions.md
    'sonarjs/no-identical-conditions': 'warn',
    // Warn on return of boolean literals inside if / else
    // https://github.com/SonarSource/eslint-plugin-sonarjs/blob/master/docs/rules/prefer-single-boolean-return.md
    'sonarjs/prefer-single-boolean-return': 'warn',
    // Warn on usage of .map(...).flat() and recommend .flatMap()
    // https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-array-flat-map.md
    'unicorn/prefer-array-flat-map': 'warn',
    // Warn on legacy techniques to flatten and recommend .flat()
    // https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-array-flat.md
    'unicorn/prefer-array-flat': 'warn',
    // Warn about importing or requiring builtin modules without node: prefix
    // https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-node-protocol.md
    'unicorn/prefer-node-protocol': ['warn'],
    // Warn about usage of substring or substr instead of slice
    'unicorn/prefer-string-slice': 'warn',
  },
};

In addition to that, it's possible that we'll also need support from each of these plugins, as mentioned in the tracking issue here:

Screen Shot 2022-10-18 at 12 35 11

And we will also need to upgrade our own @upleveled/eslint-plugin-upleveled: upleveled/eslint-plugin-upleveled#117

So maybe this means years of waiting still....

Migrate from `@typescript-eslint/*` plugins to `typescript-eslint`

https://typescript-eslint.io/blog/announcing-typescript-eslint-v7/#new-features---flat-config-support

Partial info from blog post above:

With v7 we're releasing a new package, typescript-eslint. This package can be imported within your flat config to access our configs, plugin, and parser. This package also exports a utility function called config which will allow you to write type-checked configuration files!

Switching to typescript-eslint

Because this new package includes dependencies on our plugin and parser, you can replace those dependencies entirely:

pnpm remove @typescript-eslint/parser @typescript-eslint/eslint-plugin
pnpm add typescript-eslint

Republish as `eslint-config-upleveled`

To avoid unusual '@upleveled/upleveled' in .eslintrc.cjs config file:

/** @type {import('@typescript-eslint/utils').TSESLint.Linter.Config} */
const config = {
  extends: ['@upleveled/upleveled'],
};

module.exports = config;

New config would look like this in .eslintrc.cjs:

/** @type {import('@typescript-eslint/utils').TSESLint.Linter.Config} */
const config = {
  extends: ['upleveled'],
};

module.exports = config;

TODO:

  • Republish as eslint-config-upleveled 71aa7a4 [email protected]
  • Mark @upleveled/eslint-config-upleveled as deprecated in best practices way
    • Add lint rule to upgrade off of old package
  • Update documentation
  • Unpublish @upleveled/eslint-config-upleveled?

Add `eslint-plugin-bacon` (or `eslint-plugin-expo` if republished) for Expo / React Native apps

eslint-plugin-bacon is a new Expo ESLint plugin which exposes the following rules:

  • bacon/modern-react-native: require usage of modern imported libraries
  • bacon/no-vector-icon-barrel: prevent usage of barrel imports for vector icons, reducing app size
  • bacon/no-empty-styles: prevent usage of empty arrays, objects and single-item arrays

Update (Apr 2024): eslint-config-expo - maybe this refers to the new plugin too?

Switch .mjs to .js

Since all files that we set up with the UpLeveled ESLint config after #301 have "type": "module", we can remove all .mjs extensions in the templates, as long as the various tools being configured don't have special behavior requiring .mjs still.

cc @Eprince-hub

Install `prettier` during config install, switch `prettier.config.mjs` to `prettier.config.js`

Install prettier dependency in all projects by default during installation of the UpLeveled ESLint Config

We're doing this because the Prettier VS Code extension is not really being maintained, and is staying on the old Prettier v2 version from April 2023:

This also allows us to avoid issues with divergent formatting between Prettier v2 and Prettier v3:

And migrate from the prettier.config.mjs file to prettier.config.js:

Definition for rule 'react/no-unstable-nested-components' was not found

Currently, there is an error with the ESLint config, where it is complaining that a certain ESLint rule was not found:

Definition for rule 'react/no-unstable-nested-components' was not found

This primarily affects create-react-app, which creates the following dependency in yarn.lock, even though it's not the latest version of eslint-plugin-react:

eslint-plugin-react@^7.21.5:
  version "7.22.0"

...

[email protected]:
  version "4.0.3"
  ...
  dependencies:
    ...
    eslint-plugin-react "^7.21.5"

This will not show up right away in the editor or in ESLint on the command line, but it will show up when you start a create-react-app application with yarn start:

Screen Shot 2021-06-21 at 10 47 31


The workaround is to add a resolutions field (for Yarn Resolutions) to the package.json:

  "resolutions": {
    "eslint-plugin-react": "7.23.2"
  }

Use ESLint `"overrides"` to configure based on availability of packages

The 1stG ESLint configs use overrides to enable different things in the config based on whether certain packages are available:

https://github.com/karlhorky/configs/blob/master/packages/eslint-config/overrides.js#L536

This would allow us to enable certain parts of the configure only if they are relevant eg:

  1. React rules only if react package is available
  2. Next.js rules only if next package is available
  3. SafeQL only if the package is available

ERR_PNPM_PATCH_NOT_APPLIED for Next.js patch

I upgraded Next.js to a new version (from [email protected] to [email protected]) and pnpm threw an error that my existing patch could not be applied:

$ pnpm install
 WARN  deprecated @npmcli/[email protected]: This functionality has been moved to @npmcli/fs
Downloading registry.npmjs.org/next/13.3.0: 10.6 MB/10.6 MB, done
Downloading registry.npmjs.org/@next/swc-darwin-arm64/13.3.0: 30.3 MB/30.3 MB, done
 ERR_PNPM_PATCH_NOT_APPLIED  The following patches were not applied: [email protected]

Either remove them from "patchedDependencies" or update them to match packages in your dependencies.
Progress: resolved 893, reused 835, downloaded 9, added 0

What can I do in this case?

Split ESLint config package into multiple modular packages

Instead of providing one monolithic package that has all of our configuration inside (JavaScript, Node.js, React, Next.js, PostgreSQL), it would be nice to be able to only install the configuration and dependencies that a certain student project absolutely needs.

The ESLint configuration option extends receives an array of configurations, which we can add multiple items to:

{
    "extends": [
        "upleveled-js",
        "upleveled-react",
        "upleveled-next-js",
        "upleveled-postgres-js",
    ],

Important: Each of these separate packages should not have rules or plugins which overlap or conflict.

Switch to pnpm

pnpm benefits:

  1. Faster installation approach
  2. Does not seem to require "resolutions" in package.json for ESLint plugin deduplication 🙌 Deduplication and module resolution happens in a compatible way for ESLint

Example of resolutions removal working with ESLint, even when non-matching

  1. "resolutions" removed in karlhorky/github-actions-database-persistence@c3a909a
  2. older and newer versions of various ESLint dependencies added in karlhorky/github-actions-database-persistence@10770bd

Both of these passed their linting checks:

Screenshot 2023-02-11 at 13 53 56

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.