Next.js-oppsett | ESLint, Jest, React Testing Library og Absolute Imports

En omfattende trinn-for-trinn-veiledning for å konfigurere Jest, React Testing Library, ESLint og Absolute Path Aliases i et Next.js-prosjekt.

Next.js er fantastisk når det gjelder å installere, lære rammeverket og hoppe inn i koden. Den ypperlige dokumentasjonen og nullkonfigurasjonsfilosofien gjør dette mulig, og det er fantastisk å slippe å tenke på konfigurasjon … helt til det punktet når du vil legge til ekstra konfigurasjon.

Konfigurasjonen jeg vil legge til er teknisk sett ubrukelig for det endelige produktet ditt. Det vil ikke gjøre det raskere, eller redusere pakkestørrelsen eller legge til fantastiske nye funksjoner.

Hvordan er det for en krok...🙄

Men det er viktig 🤩 Tenk på dette trinnet som den første påminnelsen om å gå sakte for å gå fort (sjekk spesielt nest til siste avsnitt). Hvis du trenger mer overbevisende, husker du da Facebook endret mottoet sitt til "beveg deg raskt med stabil infra"?

Du trenger ikke engang å bruke alt med en gang. ESLint og banealiaser for absolutt import, når de er konfigurert, er en gratis produktivitetsøkning. Absolutt import betyr at du ikke trenger å bekymre deg for hvor mange nivåer av ../../ som trengs for å finne komponenten du prøver å importere. Linting betyr at du ikke trenger å bekymre deg for at en manglende ) eller } vil få deg til å slå hodet mot veggen i 30 minutter og lurer på hvorfor koden din ikke fungerer.

Jest &React Testing Library, derimot, krever litt innsats etter oppsett. Å teste koden din er en god vane å ha, og det er noen veldig gode ressurser der ute for å hjelpe deg med å finne ut hva du skal teste.

Du begynner kanskje ikke å teste mye med en gang – spesielt hvis brukergrensesnittet og funksjonskravene dine er gjenstand for hyppige, drastiske endringer – men du bør teste det du kan. Hvis du ikke tester i det hele tatt, kan det være lurt å vurdere hvorfor du utsetter det. I det minste nå, med dette oppsettet klart til bruk, er det mer sannsynlig at du kommer inn i en vane.

Problemet?

Høres bra ut ikke sant? Du vil ha stabil infrastruktur for å øke produktiviteten, linting for å håndheve konsistente kodingsstandarder, øke lesbarheten og vedlikeholdsevnen, og testing for å sikre at du ikke ødelegger ting 🥳 men å få alle disse satt opp og leke pent med hverandre, med Next .js og med din IDE, kan være en tidkrevende øvelse i frustrasjon. Det er også MYE mindre gøy enn å skrive kode 😩

Løsningen?

Det var det! Ta deg tid til å sette opp alt én gang, før du skriver en prosjektspesifikk kode, for å lage en kodebase som enkelt kan dupliseres for nye prosjekter.

La oss se på hva som kreves.

Hva vi skal dekke

  1. Forutsetninger

  2. Sluttlager

  3. Next.js:Installerer

  4. ESLint:Installer og konfigurer

  5. Jest &React-testbibliotek:Installer, konfigurer, implementer

  6. Konfigurere banealiaser/absolutt import

Forutsetninger

Jeg kommer til å anta at du har kjente kjørekommandoer i en terminal, og jeg kommer til å bruke npm-kommandoer. Ingen av kommandoene er noe fancy, så du bør kunne konvertere til garn om nødvendig.

Jeg skal gå rett inn i å legge til konfigurasjon, og vil ikke dykke ned i for mye detaljer på et enkelt element – ​​Next.js, React, Jest, React Testing Library eller ESLint – men jeg skal prøve å gi minst en kontekst på høyt nivå for hva som skjer på hvert trinn.

Jeg skal ikke snakke om IDE-spesifikke integrasjoner eller oppsett. Jeg bruker VSCode, og jeg vil nevne det noen få steder. Andre IDE-er bør ha lignende alternativer, men krever sannsynligvis andre spesifikke oppsettstrinn. Gi meg beskjed hvis du får IDE-spesifikke problemer, så kan jeg se om du kan legge til flere merknader.

Et notat før vi starter

Noen av konfigurasjonsfilene vi lager (jest.config.js, .eslintrc) kan inkluderes i package.json i stedet for å bruke separate filer, hvis det føles renere for deg. Det vil kreve ekstra innpakningssyntaks, som du kan finne på deres respektive lenker. Filene jsconfig.json og jest.setup.js må være separate.

Endelig arkiv

https://github.com/BenjaminWFox/nextjs-base

Next.js:Installerer

For å starte, i terminalen du ønsker, cd inn i en mappe der du vil installere dette prosjektet. En ny undermappe vil bli opprettet etter at du har kjørt oppsettet:

npm init next-app

Gi prosjektet ditt et navn som "nextjs-base" (dette vil også være mappenavnet). Når installasjonen er fullført, cd nextjs-base inn i prosjektmappen din.

Nå, for bedre organisering, opprette en ny mappe kalt src og flytt deretter pages og styles mapper til src . Prosjektet ditt skal se slik ut:

.next/
node_modules/
public/
src/
 - pages/
  - api/
 - styles/
.eslint.json
.gitignore
next.config.js
package-lock.json
package.json
README.md

ESLint:Installer og konfigurer

For konfigurasjon, la oss starte med eslint - det vil sikre at eventuell fremtidig kode vi skriver er linted med en gang, og vi trenger ikke gå tilbake og gjøre endringer. Dette vil også inkludere en plugin for spesifikt linting React, og en annen for linting av import/eksportuttalelser. Du har allerede eslint og eslint-config-next - så la oss legge til to til:

npm i -D eslint-plugin-react eslint-plugin-import

Mens det kjører, åpner du .eslintrc.json fil som er i roten til nettstedet ditt. Erstatt innholdet med konfigurasjonen nedenfor.

Legg merke til at det er massevis av alternativer for å konfigurere ESLint.

Du kan bare forleng next og next/core-web-vitals hvis du vil, utelate de andre. Hvis du gjør det, kan du også utelate alt i rules eiendom. Personlig liker jeg den ekstra strukturen og det som føles som en god standard grunnlinje. Et nummer av react/ spesifikke regler er deaktivert for å forhindre konflikter med standard next-app kodestil.

Hvis du jobber med noen andre, vil jeg på det sterkeste anbefale å la reglene være på plass, det går langt mot å holde en kodebase stilistisk konsistent:

{
  "extends": [
    "next",
    "next/core-web-vitals",
    "eslint:all",
    "plugin:react/all",
    "plugin:import/errors",
    "plugin:import/warnings"
  ],
  "env": {
    "browser": true,
    "es2020": true,
    "node": true,
    "jest": true
  },
  "parserOptions": {
    "sourceType": "module",
    "ecmaFeatures": {
      "jsx": true
    }
  },
  "rules": {
    "indent": ["error", 2],
    "quotes": ["error", "single"],
    "semi": ["error", "never"],
    "func-style": 0,
    "max-len": 0,
    "no-magic-numbers": 0,
    "max-lines-per-function": 0,
    "space-before-function-paren": ["error", {
      "anonymous": "never",
      "named": "never",
      "asyncArrow": "always"
    }],
    "function-call-argument-newline": 0,
    "padded-blocks": 0,
    "padding-line-between-statements": [
      "error",
      { "blankLine": "always", "prev": "*", "next": "return" },
      { "blankLine": "always", "prev": ["const", "let", "var"], "next": "*"},
      { "blankLine": "any",    "prev": ["const", "let", "var"], "next": ["const", "let", "var"]}
    ],
    "object-curly-spacing": ["error", "always"],
    "one-var": ["error", "never"],
    "quote-props": 0,
    "react/prop-types": 0,
    "react/jsx-indent": [2, 2],
    "react/jsx-indent-props": [2, 2],
    "react/jsx-filename-extension": 0,
    "react/react-in-jsx-scope": 0,
    "react/jsx-no-literals": 0,
    "react/jsx-one-expression-per-line": 0,
    "react/jsx-max-depth": 0,
    "react/jsx-newline": 0,
    "react/jsx-props-no-spreading": 0,
    "react/jsx-max-props-per-line": ["error", {"maximum": {"single": 3, "multi": 1}}]
  },
  "ignorePatterns": [
    "node_modules/",
    ".next/"
  ]
}

^ litt oversikt over hva dette gjør:

  • extends setter opp et sett med grunnregler som skal brukes som utgangspunkt. Bruker alle kommer sannsynligvis til å gjøre livet ditt vanskeligere ... men jeg vil anbefale å beholde det, og legge til spesifikke endringer i regler du ikke liker. Det vil gi deg en god følelse av de forskjellige måtene folk kan formatere kode på. Det er alle slags basiskonfigurasjoner du kan utvide i stedet, fra selskaper (airbnb, facebook) og prosjekter (standard, penere).

  • env forteller ESLint hvilke globale variabler og spesiell syntaks du kan forvente. Siden dette er for Next.js, legger vi til nettleseren og node . es2020 (som er ecmaVersion 11 (som i utgangspunktet betyr JavaScript versjon 11)) gjør det mulig å bruke nyere JavaScript-syntaks, og jest er for globale variabler som brukes når du skriver tester.

  • parserOptions er spesielt for å tillate flere JavaScript-språkfunksjoner. kildetype vil forhindre feil fra importsyntaks og ecmaFeatures gir mulighet for tilleggsfunksjoner utenfor standard ecma-syntaks.

  • Regler er der du kan konfigurere lo-atferden etter din smak. Alle som har prefiks med react/ er spesifikke for ESLint react-plugin, vil på samme måte importere/ ville prefiksere alle regler for import-plugin - vi trenger bare ikke legge til noen her. Ellers er de standard ESLint-regler.

  • ignorePatterns lar deg definere spesifikke filer, mapper eller mønstre som skal ekskluderes fra lining. Begge node_modulene og .neste mapper er faktisk ekskludert som standard, og lagt til her bare som eksempler.

Så...det er mye! Men det vil la oss linte Next.js-prosjektet vi har nå med --fix-flagget aktivert for å automatisk formatere koden vår (neste trinn!).

Legg til og kjør Lint-skriptet

Legg nå til ett nytt skript til package.json fil under startskriptet:

    "start": "next start",
    "lint": "next lint",
    "lint.fix": "next lint --fix"

^ Ikke glem , (komma) på slutten av "lint" linje! Hvis du har integrert IDE med ESLint, har du allerede sett en haug med feil hvis du åpner src/pages/index.js. src/pages/api/hello.js skal være feilfri!

Hvis du npm run lint nå kan du også se alle feilene i konsollen. Jeg har justert eslint-konfigurasjonen over tid, så det nøyaktige settet med feil kan være litt annerledes.

…kjør nå npm lint.fix, og du vil se en rekke formateringsendringer for å justere koden med linter-reglene, og ingen flere feil!

To siste merknader om linting

  • Når det gjelder IDE-integrasjon hvis du går den veien - det er veldig praktisk å sette den opp til å lo og fikse når du lagrer filen.

  • Forutsatt at du bruker denne basismalen i nye prosjekter, hvis du finner deg selv å gjøre oppdateringer til .estlintrc-filen for å imøtekomme stilpreferansene dine, husk å kopiere dem tilbake til basisprosjektet!

Jest &Testing Library:Installer, konfigurer, implementer

Installasjonsavhengigheter

La oss deretter legge til testfunksjoner. Start med installasjonen:

npm i -D jest @types/jest @testing-library/react @testing-library/jest-dom

^ spøk for å kjøre testene &@types/jest for å hjelpe med IDE-autofullføring når du skriver tester. @testing-library/reager for å gjengi komponenter i testmiljøet og test dem på en måte som prøver å etterligne hvordan brukere samhandler med dem. @testing-library/jest-dom for ytterligere DOM-relaterte påstander.

Opprett konfigurasjonsfiler

Opprett to nye filer ved prosjektroten for Jest:jest.config.js &jest.setup.js . Legg til dette innholdet i jest.config.js fil:

// Jest.config.js
const nextJest = require('next/jest')

const createJestConfig = nextJest({
  // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
  dir: './'
})

// Jest.config.js
const customConfig = {
  // Automatically clear mock calls and instances between every test
  'clearMocks': true,
  // The directory where Jest should output its coverage files
  'coverageDirectory': '.coverage',
  // A list of paths to modules that run some code to configure or set up the testing framework before each test
  'setupFilesAfterEnv': ['./jest.setup.js'],
  // By default jest will use a node environment, so DOM elements (like document) will be undefined without this
  'testEnvironment': 'jsdom'
}

module.exports = createJestConfig(customConfig)

^ Det er et stort antall konfigurasjonsalternativer for Jest. Dette er en veldig liten delmengde. clearMocks kan forhindre hodepine med utilsiktet utholdenhet av falske data mellom testene. dekningskatalog er for å generere testdekning, spøk med --dekningsflagget. Den viktigste delen her er setupFilesAfterEnv , som kjøres før hver testfil. Legg dette til jest.setup.js fil:

// Jest.setup.js
import '@testing-library/jest-dom'

^ Dette gir tilgang til tilleggspåstandene levert av pakken @testing-library/jest-dom.

Skriv en testfil

Opprett en fil src/page-tests/index.test.js og legg til litt testkode:

import { render, screen } from '@testing-library/react'
import Home from '../pages/index'

// `describe` is not required, but it helps the tests read nicely
describe('The Home Page Component', () => {
  // Each test for the component will get an `it` block
  it('should have exactly 1 `main` section', () => {
    // The getByRole will error if there are less or more than 1 element found
    render(<Home />)
    const main = screen.getByRole('main')

    expect(main).toBeInTheDocument()
  })
})

Legg til et testskript

Den siste endringen for Jest er til package.json fil; Oppdater det for å legge til et testskript under lint-skriptet du la til tidligere:

"lint.fix": "eslint --fix --ext .js ./",
"test": "jest"

Så i prosjektroten i terminalen kan du npm kjøre test - og bør se at den passerer!

Konfigurere banealiaser/absolutt import

Jeg har sett en eller annen debatt som får meg til å tro at banealiaser er et kjærlighet-det eller hat-tilskudd til en kodebase. Jeg personlig hater å måtte huske hvilken bestemt fil jeg jobber i og hvor mange nivåer det er å importere en annen komponent eller metode ... så jeg elsker å aliasere importbanene mine. Forskjellen er:

// (Default) Relative imports 😈: 
import { Awesome } from '../../components/awesome
import { Method } from '../../../classes/method

// (Aliased) Absolute imports 😇:
import { Awesome } from '@/components/awesome
import { Method } from '@/classes/method

^ Merk at syntaksen jeg bruker, @/mappe/bane, er vilkårlig – @ kan se fancy ut, men den er bare der for å gjøre det åpenbart at dette ikke er en npm-pakke eller en relativ import – du kan navngi aliasbaner slik du vil!

Utfordringen med å sette opp disse er at når du begynner å bruke dem i applikasjonen din og i testene dine, alle de forskjellige systemene i koden din som må løse importer (<-- god forklaring på å løse moduler - ignorer TypeScript-delene 😅) må forstå disse aliasene. For oss betyr det å legge til konfigurasjon for Next.js, Jest, ESLint og VSCode 😰 … så mange oppdateringer til konfigurasjonen vi har gjort så langt, men ikke bekymre deg – det er ikke også drastisk.

Opprett en testkomponent

For å bekrefte at aliasbanene fungerer, trenger vi noe å importere. Vanligvis vil du kalle toppnivåmappene for å referere til importbanen derfra, men de eneste to toppnivåmappene vi har for øyeblikket er egentlig ikke noe vi trenger å kalle; Alt på sider/ bør sannsynligvis ikke importeres noe annet sted, og alt offentlig/ kan allerede refereres til med absolutt bane i src eller href attributter.

La oss i stedet lage en ny seksjon i koden spesielt for komponenter. Dette vil være to nye mapper og en fil:src/components/calout/callout.js . Legg dette til calout.js fil:

import PropTypes from 'prop-types'

export default function Callout({ children }) {
  return <p><strong style={{ color: 'red' }}>!</strong> {children} <strong style={{ color: 'red' }}>!</strong></p>
}

Callout.propTypes = {
  children: PropTypes.node.isRequired
}

Prøv komponenten

Hvis du importerer den komponenten i src/pages/index.js via en relativ import kan du bekrefte at den fungerer:

import Callout from '../components/callout/callout'
import Head from 'next/head'

Pakk deretter komponenten rundt "Velkommen..."-meldingen i h1-taggen:

<h1 className={styles.title}>
  <Callout>Welcome to <a href="https://nextjs.org">Next.js!</a></Callout>
</h1>

Så kjør npm dev og se:❗️️ Velkommen til Next.js! ❗️

Endre nå src/pages/index.js for å bruke den aliasede absolutte importen:

import Callout from '@/components/callout/callout'

…og du burde se en feil, yay! La oss fikse det!

Next.js &VSCode

Nå som vi har en komponent å teste og vi kan se at den ikke fungerer, la oss starte konfigurasjonsoppdateringene. Opprett en fil i prosjektroten med navnet jsconfig.json . Dette lar oss slå to fluer i en smekk siden både VSCode og Next.js bruker dette formatet for aliaser. Legg dette til filen du nettopp opprettet:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/components/*": ["src/components/*"]
    }
  }
}

^ Dette vil ikke utløse en HRM-oppdatering, så du må stoppe dev-serveren og npm kjøre dev igjen, men etter det - komponenten din skal være oppe og kjøre igjen!

I IDE, hvis du har integrert ESLint, vil du sannsynligvis fortsatt se en feil om hvordan det er "Kan ikke løse banen til modul," så la oss oppdatere ESLint neste gang.

Oppdater ESLint

Konfigurasjonen for ESLint vil bli lagt til .eslintrc , men først må vi installere en annen pakke:

npm i -D eslint-import-resolver-alias

^ denne pakken legger til funksjonaliteten for ESLint for å håndtere oppløsningen av aliasede baner, noe den ikke kan gjøre som standard. Oppdater .eslintrc fil ved å legge til følgende nederst, etter egenskapen ignorePatterns:

"ignorePatterns": ["node_modules/", ".next/"],
"settings": {
  "import/resolver": {
    "alias": [
        ["@/components", "./src/components"],
        ["@/classes", "./src/classes"]
    ]
  }
}

^ Jeg har lagt til en ekstra oppføring for en hypotetisk /klasser katalog for å vise syntaksen for flere aliaser. Behovet for at hver oppføring skal være sin egen matrise var ikke intuitivt for meg.

Hvis du npm kjører lint nå, skal det ikke være noen modulimportfeil (du kan ha noen mellomrom/mindre problemer ved å kopiere og lime inn, så kanskje npm kjører lint.fix), og IDE-feilen burde ha forsvunnet!

Oppdater Jest

Til slutt må vi oppdatere Jest. I filen src/pages/index.test.js legg til en import for vår Callout-komponent:

import Callout from '@/components/callout/callout'
import Home from './index'
import { render } from '@testing-library/react'
...

... så prøv npm run test. Du skal se en feilmelding om modulen:

Kan ikke finne modulen '@/components/callout/callout' fra 'src/pages/ index.test.js'

Tillegget for å fikse dette vil gå inn i jest.config.js , en egenskap kalt moduleNameMapper som bruker RegEx-syntaks, så det er litt mer komplisert:

const customConfig = {
  // Automatically clear mock calls and instances between every test
  'clearMocks': true,
  // The directory where Jest should output its coverage files
  'coverageDirectory': '.coverage',
  // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
  moduleNameMapper: {
    '^@/components(.*)$': '<rootDir>/src/components$1'
  },
  // A list of paths to modules that run some code to configure or set up the testing framework before each test
  'setupFilesAfterEnv': ['./jest.setup.js'],
  // By default jest will use a node environment, so DOM elements (like document) will be undefined without this
  'testEnvironment': 'jsdom'
}

^ Regex bruker en fangstgruppe for å ta alt som kommer etter @/components og løse det i stedet fra /components spesifisert til høyre. Sjekk den ut på regex101.com for en mer fullstendig oversikt over hva som skjer.

...nå prøv npm run test, feilen skal være borte!

Siden vi bare har lagt det til for testing, kan du fjerne linjen for import av bildeforklaring ... vi la til src/pages/index.test.js .

Viktig å huske

Når du legger til nye aliaser i fremtiden, må du legge dem til i tre filer:

  • jsconfig.json

  • .eslintrc

  • jest.config.js

Fullfør!

Oj, det var mye 😰 Fantastisk jobb med å komme deg gjennom alt, og du har nå et robust Next.js-grunnprosjekt du kan bruke til å bygge fra i fremtiden!

Spørsmål? Kommentarer?

Finn meg på twitter — @BenjaminWFox