Temat 32 · 3 × 45 min
Moduły ES, npm, Vite i jakość kodu
Do tej pory twoje programy mieściły się w jednym pliku skrypt.js podpiętym do index.html. Przy liście zadań z serwerem (lekcja REST i CRUD) ten plik ma już 100 linii i miesza trzy rzeczy: rozmowę z API, rysowanie listy i obsługę formularza. Zawodowy kod dzieli się na moduły – osobne pliki, które jawnie mówią, co udostępniają i czego potrzebują. Wokół modułów wyrósł cały zestaw narzędzi: Node.js i npm (biblioteki), Vite (serwer deweloperski i budowanie), ESLint i Prettier (jakość i formatowanie), Git (historia zmian). W tej lekcji poznasz je w takiej kolejności, w jakiej używa się ich w prawdziwym projekcie.
Problem: kilka plików, jeden wspólny zasięg
Wydaje się, że wystarczy podzielić kod na pliki i podpiąć kilka <script src>. To działa, ale wszystkie klasyczne skrypty dzielą jeden zasięg globalny. Zmienna z a.js jest widoczna w b.js, a kolejność tagów decyduje, czy coś już istnieje.
Co się stanie po załadowaniu strony z tymi dwoma skryptami?
<!-- koszyk.js -->
let licznik = 0;
<!-- galeria.js -->
let licznik = 1;
console.log(licznik);Dwa pliki napisane przez dwie osoby, obie użyły nazwy licznik – i aplikacja nie startuje. Moduły rozwiązują to u źródła: każdy plik ma własny zasięg, a na zewnątrz wychodzi tylko to, co wyraźnie wyeksportujesz.
Moduły ES: export i import
Moduł to zwykły plik .js, który używa export albo import. Są dwa rodzaje eksportu:
// eksport nazwany – może być wiele w jednym pliku
export const VAT = 0.23;
export function policzBrutto(netto) {
return Math.round(netto * (1 + VAT) * 100) / 100;
}
export function sumaKoszyka(produkty) {
return produkty.reduce((suma, p) => suma + p.cena * p.ilosc, 0);
}
// to zostaje prywatne – nie ma export
function formatuj(kwota) {
return kwota.toFixed(2) + ' zł';
}// eksport domyślny – dokładnie jeden na plik, zwykle „główna rzecz” modułu
export default class Koszyk {
#produkty = [];
dodaj(produkt) { this.#produkty.push(produkt); }
get produkty() { return [...this.#produkty]; }
}import Koszyk from './Koszyk.js'; // domyślny: dowolna nazwa, bez klamer
import { policzBrutto, sumaKoszyka } from './koszyk.js'; // nazwane: w klamrach, nazwy muszą się zgadzać
import { VAT as STAWKA_VAT } from './koszyk.js'; // zmiana nazwy przy imporcie
import * as narzedzia from './koszyk.js'; // wszystko jako obiekt: narzedzia.VAT
const koszyk = new Koszyk();
koszyk.dodaj({ nazwa: 'Kabel USB-C', cena: 29.99, ilosc: 2 });
console.log(policzBrutto(sumaKoszyka(koszyk.produkty)), STAWKA_VAT, narzedzia.VAT);Zasady, które warto zapamiętać:
| Rodzaj | W module źródłowym | Przy imporcie | Uwagi |
|---|---|---|---|
| nazwany | export function f() {} lub export { f, g } | import { f, g } from './m.js' | nazwa musi się zgadzać; zmiana przez as |
| domyślny | export default … | import Cokolwiek from './m.js' | jeden na plik; nazwa dowolna |
| oba | export default X; export const y | import X, { y } from './m.js' | domyślny zawsze pierwszy |
| wszystko | – | import * as m from './m.js' | dostęp m.f, m.y |
Ścieżka w from musi być ścieżką zaczynającą się od ./, ../ lub / (albo pełnym adresem URL) i w przeglądarce z rozszerzeniem .js. import { f } from 'koszyk' bez kropki przeglądarka odrzuci – taki „nagi” zapis rozumie bundler (zobaczysz go przy Vite jako import biblioteki z node_modules), a w samej przeglądarce działa tylko wtedy, gdy strona zdefiniuje mapę importów (<script type="importmap">, zobaczysz ją w lekcji 35).
Plik narzedzia.js zawiera export default function suma(a, b) { return a + b; }. Który import jest poprawny?
Podpięcie w HTML: type=“module”
<script type="module" src="main.js"></script>Jeden tag wystarczy – przeglądarka sama pobierze Koszyk.js i koszyk.js, bo main.js je importuje. Moduł różni się od zwykłego skryptu w kilku miejscach:
- własny zasięg –
const xw module nie staje sięwindow.x, - tryb ścisły (
'use strict') automatycznie – literówka w nazwie zmiennej toReferenceError, a nie cicha globalna, deferdomyślnie – wykonuje się po sparsowaniu HTML, więc elementy już istnieją,- wykonuje się raz – nawet jeśli pięć plików importuje ten sam moduł, jego kod uruchomi się jednokrotnie,
thisna najwyższym poziomie toundefined, niewindow.
Przetestuj własny zasięg bez żadnych plików – wystarczy wbudowany moduł:
<script>
var zwykla = 'widoczna wszędzie';
</script>
<script type="module">
const modulowa = 'widoczna tylko w module';
console.log('w module:', typeof zwykla, '/', typeof modulowa);
</script>
<p>Wyniki w konsoli pod spodem.</p>// to jest zwykły skrypt na końcu body
console.log('w skrypcie:', typeof zwykla, '/', typeof modulowa);Zwróć uwagę na kolejność w konsoli: zwykły skrypt wypisał się przed modułem, choć stoi niżej w HTML – moduły są odroczone.
Dlaczego file:// nie działa
Otwórz index.html z type="module" dwuklikiem (adres file:///C:/…) i zobaczysz w konsoli:
Access to script at 'file:///C:/projekt/main.js' from origin 'null' has been blocked by CORS policyModuły są pobierane jak zasoby sieciowe z kontrolą pochodzenia (CORS, lekcja 31), a protokół file:// nie ma „pochodzenia” – przeglądarka zgłasza origin 'null' i blokuje. Potrzebujesz serwera HTTP, dowolnego:
| Sposób | Polecenie / czynność | Adres |
|---|---|---|
| VS Code, rozszerzenie Live Server | prawy klik na index.html → Open with Live Server | http://127.0.0.1:5500 |
| Node bez instalacji | npx serve w folderze projektu | http://localhost:3000 |
| Vite (niżej) | npm run dev | http://localhost:5173 |
| XAMPP | plik w htdocs/projekt/ | http://localhost/projekt/ |
| Python (jeśli masz) | python -m http.server 8000 | http://localhost:8000 |
Na egzaminie INF.03 nie używasz modułów właśnie dlatego: tam otwierasz plik bezpośrednio albo przez XAMPP, a jeden <script> wystarcza.
Node.js i npm
Node.js to środowisko uruchamiające JavaScript poza przeglądarką (silnik V8 z Chrome plus dostęp do plików i sieci). Potrzebujesz go nie po to, by pisać serwery (to lekcja 36), lecz dlatego, że wszystkie narzędzia front-endowe są napisane w JS i uruchamiane w Node. Razem z Node instaluje się npm – menedżer pakietów z rejestru npmjs.com (ponad 3 mln bibliotek).
Pobierz instalator LTS ze strony nodejs.org (jesienią 2026 r. wspierane linie LTS to 22 i 24, a od 28 października 2026 także 26) i sprawdź w terminalu:
node -v # np. v24.x.x albo v26.x.x
npm -v # 11.x.xpackage.json – dowód tożsamości projektu
mkdir lista-zadan && cd lista-zadan
npm init -y # tworzy package.json z domyślnymi wartościami
npm install date-fns # biblioteka do pracy z datami → dependencies
npm install -D eslint # narzędzie tylko dla programisty → devDependencies{
"name": "lista-zadan",
"version": "1.0.0",
"type": "module",
"scripts": {
"start": "node index.js",
"lint": "eslint ."
},
"dependencies": {
"date-fns": "^4.1.0"
},
"devDependencies": {
"eslint": "^10.13.0"
}
}Co tu jest ważne:
scripts– nazwane polecenia.npm run linturuchamiaeslint ., przy czym npm sam znajdzieeslintwnode_modules/.bin.npm startinpm testdziałają bezrun.dependencies– to, czego potrzebuje działająca aplikacja;devDependencies– narzędzia (linter, testy, bundler).^4.1.0– „4.1.0 lub nowsza, ale poniżej 5.0.0”. Dokładne zainstalowane wersje zapisujepackage-lock.json– dzięki niemu kolega ponpm installdostanie identyczne biblioteki."type": "module"– pliki.jsw tym projekcie są modułami ES także dla Node.- Folder
node_modulesbywa ogromny (setki MB). Nigdy nie wysyłasz go do Git ani kolegom – odtwarza gonpm installzpackage.json.
npx nazwa uruchamia pakiet bez instalowania na stałe (tak startowałeś json-server). npm uninstall nazwa usuwa, npm outdated pokazuje, co można zaktualizować.
Kolega wysłał ci projekt bez folderu node_modules. Co robisz, żeby zadziałał?
Vite: serwer deweloperski i budowanie
Pisząc z modułami, chcesz: serwera HTTP (bo file:// nie działa), automatycznego przeładowania po zapisie, możliwości importowania bibliotek z node_modules „nagą” nazwą, importowania CSS i obrazów z JS, a na koniec – jednego małego pliku do wrzucenia na hosting. Wszystko to robi Vite (fr. „szybko”; aktualna wersja 8, wymaga Node 20.19+ lub 22.12+).
npm create vite@latest lista-zadan -- --template vanilla
cd lista-zadan
npm install
npm run devBez --template kreator zapyta interaktywnie o framework (Vanilla, Vue, React, Preact, Lit, Svelte, Solid, Qwik…) i wariant (JavaScript / TypeScript). vanilla to czysty JS – ten chcesz dziś; react przyda się w lekcji 35, vanilla-ts w 36.
Po npm run dev w terminalu pojawi się adres http://localhost:5173/. Struktura wygenerowanego projektu:
lista-zadan/
├── index.html ← punkt wejścia, ma <script type="module" src="/src/main.js">
├── src/
│ ├── main.js ← importuje style.css, obrazki i counter.js
│ ├── counter.js ← przykładowy moduł z eksportem nazwanym
│ ├── style.css
│ └── assets/ ← obrazki importowane z JS
├── public/ ← pliki kopiowane „jak są” (favicon, ikony)
├── package.json
└── node_modules/import './style.css'; // Vite pozwala importować CSS z JS
import { setupCounter } from './counter.js';
setupCounter(document.querySelector('#counter'));Zapisz zmianę w src/counter.js – strona odświeży się sama (HMR, hot module replacement). Gdy aplikacja jest gotowa:
npm run build # → folder dist/ z jednym zminifikowanym JS i CSS
npm run preview # podgląd tego, co jest w dist/, na http://localhost:4173Zawartość dist/ wrzucasz na hosting (albo do htdocs w XAMPP) – to zwykłe pliki HTML/CSS/JS, działające bez Node. Vite na etapie build łączy wszystkie moduły w jeden plik (tzw. bundling), usuwa nieużywany kod i skraca nazwy – strona ładuje się szybciej niż z dziesięciu osobnych <script>.
ESLint: komputer czyta twój kod przed uruchomieniem
Linter analizuje kod bez uruchamiania i wskazuje błędy oraz podejrzane konstrukcje: niezadeklarowane zmienne, nieużywane importy, == zamiast ===, przypisanie w warunku. Połowę błędów z lekcji o pułapkach ESLint złapałby, zanim odświeżysz przeglądarkę. Aktualna wersja to ESLint 10 z konfiguracją w pliku eslint.config.js (tzw. flat config; stary format .eslintrc wersja 9 domyślnie ignorowała, a wersja 10 usunęła całkowicie).
npm init @eslint/config@latestKreator zada kilka pytań: jak chcesz używać ESLint (sprawdzanie składni / szukanie problemów), jaki rodzaj modułów (ESM), framework (none), TypeScript (no), gdzie działa kod (Browser). Na końcu zainstaluje eslint, @eslint/js i globals i utworzy plik:
import { defineConfig } from 'eslint/config';
import globals from 'globals';
import js from '@eslint/js';
export default defineConfig([
{ files: ['**/*.js'], languageOptions: { globals: globals.browser } },
{ files: ['**/*.js'], plugins: { js }, extends: ['js/recommended'] },
{
rules: {
eqeqeq: 'error', // tylko === i !==
'no-unused-vars': 'warn', // ostrzeżenie zamiast błędu
'prefer-const': 'error', // let, którego nie zmieniasz → const
},
},
]);globals.browser mówi ESLintowi, że document, window, fetch istnieją – bez tego każde document.getElementById byłoby „niezadeklarowaną zmienną”. js/recommended włącza około 60 reguł wyłapujących realne błędy. Trzy dopisane reguły to te, które najczęściej dotyczą uczniów.
npx eslint . # sprawdź wszystkie pliki
npx eslint . --fix # napraw to, co da się naprawić automatycznie (np. let → const)Ile problemów zgłosi ESLint z konfiguracją wyżej (js/recommended + eqeqeq + no-unused-vars + prefer-const) dla tego kodu?
let cena = 100;
const rabat = 10;
function policz(kwota, procent) {
if (kwota == 0) return 0;
return kwota - kwota * procnt / 100;
}
console.log(policz(cena));W VS Code zainstaluj rozszerzenie ESLint – podkreśli problemy na czerwono w trakcie pisania, bez uruchamiania terminala.
Prettier: koniec dyskusji o formatowaniu
Prettier nie szuka błędów – przepisuje kod w jednolitym stylu: wcięcia, cudzysłowy, przecinki, długość linii. Dwie osoby w zespole zapisują plik i wygląda identycznie, a code review dotyczy logiki, nie spacji.
npm install -D --save-exact prettier
node --eval "fs.writeFileSync('.prettierrc','{}\n')" # pusta konfiguracja = domyślne ustawienia
npx prettier . --write # sformatuj wszystko
npx prettier . --check # tylko sprawdź (przydatne w CI)Typowe ustawienia w .prettierrc, jeśli chcesz coś zmienić:
{
"singleQuote": true,
"semi": true,
"printWidth": 100
}Dwie rzeczy do zapamiętania:
- ESLint i Prettier mają się nie kłócić. Zainstaluj
eslint-config-prettieri dodaj go na końcueslint.config.js– wyłącza reguły ESLint dotyczące formatowania. ESLint pilnuje błędów, Prettier wyglądu. - W VS Code: rozszerzenie Prettier – Code formatter + ustawienie Format On Save. Od tej chwili formatowanie dzieje się przy
Ctrl+Si nie myślisz o nim.
npm install -D eslint-config-prettierimport prettier from 'eslint-config-prettier';
// … na końcu tablicy defineConfig:
// prettier,Git: historia projektu
Git zapisuje migawki (commity) projektu. Dzięki niemu cofasz nieudaną zmianę, widzisz, kto i kiedy zmienił linię, i pracujesz w zespole bez wysyłania projekt_final_v3_poprawiony.zip. Pobierz z git-scm.com i skonfiguruj raz:
git config --global user.name "Imię Nazwisko"
git config --global user.email "ty@example.com"
git config --global init.defaultBranch main # nowe repozytoria zaczynają od gałęzi main, nie masterZanim zrobisz pierwszy commit, powiedz Gitowi, czego nie śledzić. Szablon Vite tworzy już .gitignore; w projekcie z npm init dopisz sam:
node_modules/
dist/
.envCodzienny cykl pracy to pięć poleceń:
git init # raz: zacznij śledzić folder
git status # co się zmieniło?
git add . # zaznacz zmiany do zapisania („do poczekalni”)
git commit -m "Dodaj formularz zadania z walidacją" # zapisz migawkę z opisem
git log --oneline # historia: skrót + opis każdego commitaKomunikat commita pisz w trybie rozkazującym i konkretnie: „Napraw podwójne wysyłanie formularza”, nie „zmiany” ani „asdf”. Jeden commit = jedna logiczna zmiana. Gdy coś zepsujesz:
git diff # co zmieniłem od ostatniego commita?
git restore app.js # cofnij plik do ostatniego commita
git checkout -b eksperyment # nowa gałąź: testuj bez psucia main
git switch main # wróć na główną gałąźŻeby mieć kopię w chmurze i pracować w zespole, załóż pusty projekt na GitHubie i połącz:
git remote add origin https://github.com/twoj-login/lista-zadan.git
git branch -M main # upewnij się, że gałąź nazywa się main (domyślnie bywa master)
git push -u origin main # wyślij; kolejne razy wystarczy git pushKolega pobiera całość przez git clone <adres>, potem npm install – i ma działający projekt bez node_modules w paczce.
Pułapki
Ściąga
| Składnia / polecenie | Co robi | Przykład |
|---|---|---|
export const x / export function f | eksport nazwany | import { x, f } from './m.js' |
export default X | eksport domyślny (jeden na plik) | import Dowolna from './m.js' |
import * as m from './m.js' | cały moduł jako obiekt | m.f() |
import { a as b } | zmiana nazwy | import { VAT as STAWKA } |
<script type="module" src="main.js"> | uruchom jako moduł | własny zasięg, strict, defer |
npm init -y | nowy package.json | w pustym folderze |
npm install nazwa / -D nazwa | zależność / narzędzie | npm i -D vitest |
npm install | odtwórz node_modules | po git clone |
npm run skrypt | polecenie ze scripts | npm run dev |
npx nazwa | uruchom pakiet bez instalacji | npx json-server db.json |
npm create vite@latest app -- --template vanilla | nowy projekt Vite | cd app && npm i && npm run dev |
npm run build / preview | zbuduj dist/ / podejrzyj | hosting dostaje dist/ |
npm init @eslint/config@latest | utwórz eslint.config.js | potem npx eslint . |
npx eslint . --fix | sprawdź i napraw | let → const |
npx prettier . --write | sformatuj projekt | --check tylko sprawdza |
git init / add . / commit -m "…" | śledź / zaznacz / zapisz | git log --oneline |
git restore plik | cofnij plik | do ostatniego commita |
.gitignore | pliki poza Git | node_modules/, dist/ |
Zadania
Kalkulator rat w trzech modułach
Masz kalkulator rat z lekcji 14 w jednym pliku. Podziel go na moduły:
obliczenia.js– eksport nazwanyobliczRate(kwota, liczbaRat)(zaokrąglenie do 2 miejsc) iformatujZl(kwota)(np."125,50 zł"),ui.js– eksport domyślny: funkcjapodlaczFormularz(form, wynik), która czyta polakwotairaty, liczy i wypisujeRata: … złw elemenciewynik,main.js– importuje i spina z HTML.
Uruchom przez Live Server lub npx serve. Sprawdź w konsoli, że typeof obliczRate na najwyższym poziomie (w konsoli DevTools) daje "undefined" – funkcja nie wyciekła do globalnego zasięgu.
Pokaż rozwiązanie
export function obliczRate(kwota, liczbaRat) {
if (liczbaRat <= 0) return 0; // zabezpieczenie przed dzieleniem przez zero
return Math.round((kwota / liczbaRat) * 100) / 100;
}
export function formatujZl(kwota) {
return kwota.toFixed(2).replace('.', ',') + ' zł';
}import { obliczRate, formatujZl } from './obliczenia.js';
export default function podlaczFormularz(form, wynik) {
form.addEventListener('submit', (e) => {
e.preventDefault();
const kwota = parseFloat(form.elements.kwota.value);
const raty = parseInt(form.elements.raty.value, 10);
if (Number.isNaN(kwota) || Number.isNaN(raty) || raty <= 0) {
wynik.textContent = 'Wprowadź poprawne dane.';
return;
}
wynik.textContent = `Rata: ${formatujZl(obliczRate(kwota, raty))}`;
});
}import podlaczFormularz from './ui.js';
podlaczFormularz(document.getElementById('form-raty'), document.getElementById('wynik'));<!doctype html>
<html lang="pl">
<head><meta charset="utf-8"><title>Raty</title></head>
<body>
<form id="form-raty">
<input name="kwota" type="number" step="0.01" placeholder="Kwota" required>
<input name="raty" type="number" min="1" placeholder="Liczba rat" required>
<button>Oblicz</button>
</form>
<p id="wynik"></p>
<script type="module" src="main.js"></script>
</body>
</html>W konsoli DevTools typeof obliczRate zwraca "undefined", bo funkcja żyje tylko w zasięgu modułu obliczenia.js i tam, gdzie została zaimportowana.
Lista zadań w Vite
Utwórz projekt npm create vite@latest lista-zadan -- --template vanilla. Usuń przykładowy licznik i zbuduj listę zadań z lekcji 16 w trzech modułach: stan.js (tablica zadań + funkcje dodaj, przelacz, usun – eksporty nazwane), widok.js (eksport domyślny rysuj(lista, zadania)), main.js (formularz, delegacja zdarzeń, import ./style.css). Następnie npm run build i otwórz dist/index.html przez npm run preview. Zajrzyj do dist/assets/ – ile plików JS powstało z trzech modułów?
Pokaż rozwiązanie
let zadania = [];
let kolejneId = 1;
export function dodaj(tytul) {
zadania.push({ id: kolejneId++, tytul, wykonane: false });
return zadania;
}
export function przelacz(id) {
const zadanie = zadania.find((z) => z.id === id);
if (zadanie) zadanie.wykonane = !zadanie.wykonane;
return zadania;
}
export function usun(id) {
zadania = zadania.filter((z) => z.id !== id);
return zadania;
}
export function wszystkie() {
return zadania;
}export default function rysuj(lista, zadania) {
lista.innerHTML = '';
for (const z of zadania) {
const li = document.createElement('li');
li.className = z.wykonane ? 'wykonane' : '';
li.innerHTML = `
<input type="checkbox" data-akcja="przelacz" data-id="${z.id}" ${z.wykonane ? 'checked' : ''}>
<span>${z.tytul}</span>
<button data-akcja="usun" data-id="${z.id}">Usuń</button>`;
lista.append(li);
}
}import './style.css';
import { dodaj, przelacz, usun, wszystkie } from './stan.js';
import rysuj from './widok.js';
document.querySelector('#app').innerHTML = `
<form id="form-zadanie">
<input name="tytul" placeholder="Nowe zadanie" required>
<button>Dodaj</button>
</form>
<ul id="lista"></ul>`;
const form = document.getElementById('form-zadanie');
const lista = document.getElementById('lista');
form.addEventListener('submit', (e) => {
e.preventDefault();
rysuj(lista, dodaj(form.elements.tytul.value.trim()));
form.reset();
});
lista.addEventListener('click', (e) => {
const { akcja, id } = e.target.dataset;
if (akcja === 'przelacz') rysuj(lista, przelacz(Number(id)));
if (akcja === 'usun') rysuj(lista, usun(Number(id)));
});
rysuj(lista, wszystkie());body { font-family: system-ui, sans-serif; max-width: 480px; margin: 40px auto; }
.wykonane span { text-decoration: line-through; color: #888; }Po npm run build w dist/assets/ jest jeden plik index-<hash>.js (i jeden CSS) – Vite połączył trzy moduły w jeden i zminifikował. Hash w nazwie zmienia się przy każdej zmianie kodu, dzięki czemu przeglądarki użytkowników nie trzymają starej wersji w pamięci podręcznej.
Napraw to, co zgłasza ESLint
W projekcie z zadania 2 uruchom npm init @eslint/config@latest (ESM, brak frameworka, bez TypeScript, Browser) i dopisz reguły eqeqeq, prefer-const, no-unused-vars jak w lekcji. Następnie wklej do pliku zepsute.js poniższy kod, uruchom npx eslint zepsute.js i napraw wszystkie zgłoszenia bez używania eslint-disable. Zapisz, ile ich było i jakie reguły.
import { dodaj, usun, przelacz } from './stan.js';
var licznik = 0;
let nazwa = 'Lista';
function pokaz(zadania) {
for (var i = 0; i < zadania.length; i++) {
if (zadania[i].wykonane == true) {
licznik++;
}
}
console.log(nazwa + ': ' + liczik + ' wykonanych');
}
export function dodajIpokaz(tytul) {
const zadania = dodaj(tytul);
pokaz(zadania);
return zadania;
}Pokaż rozwiązanie
ESLint zgłosi 6 problemów: usun i przelacz nieużywane (no-unused-vars ×2), licznik tylko zwiększany, a nigdy nieodczytany – bo w console.log jest literówka (no-unused-vars), nazwa nigdy nie zmieniana (prefer-const), == true (eqeqeq), liczik niezdefiniowane (no-undef). Dwa var przejdą bez uwag, bo reguła no-var nie jest w recommended – dopisz 'no-var': 'error' do rules i zgłoszeń będzie 8. Naprawiona wersja:
import { dodaj } from './stan.js';
let licznik = 0;
const nazwa = 'Lista';
function pokaz(zadania) {
for (const zadanie of zadania) {
if (zadanie.wykonane === true) {
licznik++;
}
}
console.log(`${nazwa}: ${licznik} wykonanych`);
}
export function dodajIpokaz(tytul) {
const zadania = dodaj(tytul);
pokaz(zadania);
return zadania;
}Zwróć uwagę, że literówkę liczik linter wykrył bez uruchamiania kodu – w przeglądarce dowiedziałbyś się o niej dopiero po kliknięciu, jako ReferenceError. Po npx eslint zepsute.js --fix ESLint sam zamieni let nazwa na const, ale literówki i == musisz poprawić ręcznie (zamiana == na === mogłaby zmienić działanie programu, więc --fix tego nie robi).
Pełny przepływ: Vite + ESLint + Prettier + Git
Zbuduj od zera projekt „Dziennik ocen” i doprowadź go do stanu „gotowy do pracy w zespole”:
npm create vite@latest dziennik -- --template vanilla, moduły:oceny.js(dodajOcene,srednia,najlepszyPrzedmiot– eksporty nazwane, czysta logika bez DOM),widok.js,main.js.- ESLint z
js/recommended,eqeqeq,prefer-const,no-var+ Prettier zsingleQuote: true+eslint-config-prettier. Wpackage.jsondodaj skryptylint,lint:fix,format. .gitignoreznode_modules/idist/,git init, trzy sensowne commity: szkielet projektu, logika ocen, widok.npm run build– sprawdź, żedist/nie trafił do Git (git statuspo buildzie ma być czysty).- Wyślij na GitHub i podaj koledze adres – ma uruchomić projekt w trzech poleceniach.
Pokaż rozwiązanie
// czysta logika – zero DOM, łatwa do przetestowania w lekcji 33
export function dodajOcene(oceny, przedmiot, wartosc) {
if (wartosc < 1 || wartosc > 6) throw new Error('Ocena musi być z zakresu 1–6');
return [...oceny, { przedmiot, wartosc }];
}
export function srednia(oceny) {
if (oceny.length === 0) return 0;
const suma = oceny.reduce((s, o) => s + o.wartosc, 0);
return Math.round((suma / oceny.length) * 100) / 100;
}
export function najlepszyPrzedmiot(oceny) {
const sumy = {};
for (const { przedmiot, wartosc } of oceny) {
sumy[przedmiot] ??= { suma: 0, ile: 0 };
sumy[przedmiot].suma += wartosc;
sumy[przedmiot].ile++;
}
let najlepszy = null;
let maks = -Infinity;
for (const [przedmiot, { suma, ile }] of Object.entries(sumy)) {
if (suma / ile > maks) {
maks = suma / ile;
najlepszy = przedmiot;
}
}
return najlepszy;
}{
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"format": "prettier . --write"
}
}import { defineConfig } from 'eslint/config';
import globals from 'globals';
import js from '@eslint/js';
import prettier from 'eslint-config-prettier';
export default defineConfig([
{ files: ['**/*.js'], languageOptions: { globals: globals.browser } },
{ files: ['**/*.js'], plugins: { js }, extends: ['js/recommended'] },
{
rules: {
eqeqeq: 'error',
'prefer-const': 'error',
'no-var': 'error',
},
},
prettier, // na końcu – wyłącza reguły formatowania, bo tym zajmuje się Prettier
]);# instalacja narzędzi
npm init @eslint/config@latest
npm install -D --save-exact prettier
npm install -D eslint-config-prettier
node --eval "fs.writeFileSync('.prettierrc','{ \"singleQuote\": true }\n')"
# historia
git init
git add .
git commit -m "Szkielet projektu Vite z ESLint i Prettier"
# … piszesz oceny.js …
git add oceny.js
git commit -m "Dodaj logikę ocen: dodawanie, średnia, najlepszy przedmiot"
# … piszesz widok.js i main.js …
git add .
git commit -m "Dodaj widok listy ocen i formularz"
npm run build
git status # „nothing to commit, working tree clean” – dist/ jest ignorowany
git remote add origin https://github.com/twoj-login/dziennik.git
git branch -M main
git push -u origin mainKolega: git clone https://github.com/twoj-login/dziennik.git, cd dziennik && npm install, npm run dev. Trzy polecenia, działający projekt, identyczne wersje bibliotek dzięki package-lock.json.
Test powtórkowy
Test powtórkowy
Dziewięć pytań o moduły i narzędzia. Jedna poprawna odpowiedź.
Plik koszyk.js zawiera export function suma() {} oraz export const VAT = 0.23. Który import jest poprawny?
Które zdanie o skrypcie z atrybutem type="module" jest prawdziwe?
Dlaczego otwarcie index.html dwuklikiem nie ładuje modułów ES?
Gdzie w package.json powinien trafić ESLint?
Które polecenie tworzy nowy projekt w Vite z czystym JavaScriptem?
Co robi polecenie npm run build w projekcie Vite?
Którą regułę ESLint złamie ten kod?
const cena = '10';
if (cena == 10) console.log('równe');Jaka jest różnica między ESLint a Prettier?
Które polecenia w tej kolejności zapisują zmiany w historii Git?