DEV Community

Max Core
Max Core

Posted on • Edited on

2 1

Базовый ультимативный гайд по Node.js (на примере SvelteKit)

dependencies? devDependencies? peerDependencies?
npm install? npm i? npm ci?
--save? --save-dev? --save-prod? -g?
package.json? package-lock.json? .npmrc?
"module": "^1.0.0"?
"module": "~1.0.0"?
"module": "1.0.0"?
Что из этого можно править руками?
Что из этого в Гит?
Какой командой в итоге билдить?

Актуально для: node -v — v20.5.0 и npm -v — 10.2.3

Что предлагает Svelte на старте?

(После npm create svelte@latest my-app)

  1. В package-lock.json только devDependencies (без prod-dependencies):

И все эти зависимости «из коробки» НЕ строгие, т.е. с ^:

```
"devDependencies": {
    "@sveltejs/adapter-auto": "^2.0.0",
    "@sveltejs/kit": "^1.20.4",
    "svelte": "^4.0.5",
    "vite": "^4.4.2"
}, 
```
Enter fullscreen mode Exit fullscreen mode
  1. .npmrc (NPM Run Command) с единственной строкой engine-strict=true.

P.S. package-lock.json нет вообще (и не надо).

Что рекомендуют разработчики Svelte?

Определиться с адаптером, вместо adapter-auto.

It's recommended to install the appropriate adapter to your devDependencies once you've settled on a target environment, since this will add the adapter to your lockfile and slightly improve install times on CI.
https://kit.svelte.dev/docs/adapter-auto

Мы люди простые, — возьмём универсальный adapter-node.
Итак,

Как установить новый модуль?

https://docs.npmjs.com/cli/v10/commands/npm-install

По умолчанию, npm install @sveltejs/adapter-node — запишет в prod-dependencies.
Потом, можно перекинуть в devDependencies путём:
npm install @sveltejs/adapter-auto --save-dev.
Но, снова вернуть в «prod» той же командой:
npm install @sveltejs/adapter-auto уже не получится.
Чтобы форсануть обратно в «prod»:
npm install @sveltejs/adapter-auto --save-prod

  • npm install @sveltejs/adapter-auto --save — такого просто нет (как минимум в npm v10)
  • Но нода не ругается на несуществующие параметры.
  • Более того, ей без разницы — что так: npm install module-name --param, что так: npm install --param module-name.
  • А вообще можно и просто npm i module-name.
  • Можно и npm i module_name -g, если модуль нужен не для проекта, а как консольная утилита для чего-нибудь.

Но это не то, что нам нужно.
Мы знаем, чем оборачивается «НЕ строгая версия».

Как установить строгую версию?

Есть 4 способа:

  1. npm config set save-exact true

    Сомнительно, т.к. делает запись где-то глобально, и только на локальной машине.

    P.S.
    Создаётся на самом деле 2-е переменные в npm config list:
    save-exact = true
    save-prefix = ""

    Если вернуть обратно (npm config set save-exact true), будет:
    save-exact = false
    save-prefix = "^"

  2. npm install @sveltejs/adapter-auto --save-exact

    Делает свою работу, но нам нужна защита от дурака, на всю команду.

  3. Добавить в .npmrc >> save-exact=true.

Т.е., теперь, когда мы делаем npm i module-name он автоматически будет устанавливать строгую версию, без ^.
(Тут, никаких save-prefix = "" не надо, это была внутренняя штука для глобального конфига.)

  1. Можно руками постирать все ^, но — тогда у нас окажутся устаревшие пакеты :D.

То, что было вначале пришло в devDependencies — не совсем свежее.
Но, пока мы ещё ничего не установили, у нас нет ни node_modules/, ни package-lock.json — нам ничего не мешает действительно всё прописать руками.

Итого:

  1. Идём по П.3. — добавляем в .npmrc >> save-exact=true.
  2. Для всего, что видим в devDependencies — заного поочерёдно делаем npm i module-name. (Они перелетят из devDependencies в prod-dependencies. Пока, тут это нам и нужно.)
  3. Ну и наконец-то устанавливаем наш adapter-auto:

npm i @sveltejs/adapter-auto.

P.S. Устанавливая один модуль — установятся все dependencies, как если бы мы делали всё это одним npm i.

А @sveltejs/adapter-auto можно удалить, чтоб не мешался.

Как удалить модуль?

https://docs.npmjs.com/cli/v10/commands/npm-uninstall

  1. npm uninstall @sveltejs/adapter-auto — удалит в каких бы *Dependencies они не находились.

    Раньше нужно было дописывать --save-dev и т.д., иначе удалится из node_modules/, но не из package.json и т.д.
    Сейчас этого нет. Только если прописать в .npmrc >> save=false, тогда нужно будет --save.
    Но не вижу во всём этом смысла для общих случаев.

  2. Можно удалить строку "@sveltejs/adapter-auto": "^2.0.0", руками.

    При этом удалить node_modules/ и package-lock.json.
    И сделать npm i всего проекта заново.
    Но, так делать не стоит, когда всё уже в бою.
    Это может навредить и серверной сборке, и коллегам, но об этом позже.

Как не захламить prod-dependencies?

Понятно, что в процессе разработки мы пробуем много разных модулей, которые чаще всего не пойдут на прод.
И, не хотелось бы, чтобы всё ложилось в prod-dependencies.

Тут выясняется, что возможен ещё какой-то peerDependencies,
где вообще — у каждого разработчика появляется свой личный блок с модулями:
npm i node_module --save-peer (--save-peer не задокументирован, но работает. https://stackoverflow.com/a/74549787/4117781)

Пропустил я эту команду, и получил что-то новое — звёздочку! в devDependencies:

"devDependencies": {
    "@sveltejs/adapter-auto": "*",
},
"peerDependencies": {
    "@sveltejs/adapter-auto": "2.1.1"
}
Enter fullscreen mode Exit fullscreen mode

Испугался, и решил не использовать.
Давайте лучше дружно хламить devDependencies, но уж лучше без этого.
Там ещё как-то должен был прописаться мой личный скоуп, на основе имени компа, но и этого автоматически не произошло.
Валим из этой идеи короче :D
Как минимум в этой статье.

Не будем тянуть.
Чтобы все модули по-умолчанию сваливались в devDependencies:
.npmrc >> save-dev=true

Теперь, чтобы вогнать что-то в prod-dependencies нужно будет npm i module-name --save-prod.
Такое делается не каждый день. Но — это ответственно.
Потому, вероятно — это должен делать кто-то один из команды.

Зачем нужен package-lock.json?

У модулей, которые у нас в зависимостях, — есть свои зависимости.
Каждая мажорная версия (1.0.0 —> 2.0.0) по конвенции допускает нарушение обратной совместимости.
https://docs.npmjs.com/about-semantic-versioning

Зафиксируем:
^1.0.0 — тут разработчик модуля говорит, что версия зависимости сойдёт вплоть до 2.0.0.
~1.0.0 — тут разработчик модуля говорит, что версия зависимости сойдёт вплоть до 1.1.0.

И — наши зависимости, зависимости модулей — могут между собой пересекаться.
А сама конечная зависимость — может быть только одна.
И, делая npm i — каждый новый раз — нода может выкачивать из своих репозиториев чутка разные набор зависимостей, более оптимальные на текущий момент, удовлетворяя при этом всем настройкам выше — и в наших зависимостях, и в зависимостях зависимостей.
Поэтому, несмотря на то, что package-lock.json такой страшный — к сожалению, его лучше всё же класть в git.
Чтобы не попасть в ситуацию, когда набор зависимостей на локале и на проде разный, а ты не можешь понять в чём ошибка.
А ошибка может быть в любой даже минорной версии с баг-фиксом в любой из зависимостей.
Хотя, это и бывает редко. Но бьёт больно.

Чтобы package-lock.json так не раздражал в git-e, парни предлагают:
.gitattributes —> package-lock.json binary
https://stackoverflow.com/a/50982431/4117781

Итого в .npmrc

https://docs.npmjs.com/cli/v10/configuring-npm/npmrc

engine-strict=true <— добрая рекомендация от Svelte
save-exact=true
save-dev=true

Что ещё интересного в package.json?

1. "private": true,
Consider also setting "private": true to prevent accidental publication.
https://docs.npmjs.com/cli/v10/configuring-npm/package-json
Не знаю как это возможно, но, если «accidental», то убирать не будем.

2. "type": module,
Это то, что позволяет нам делать модные import a from '/b.js, вместо старых a = require('b') и т.д.

3. Авторская рекомендация
Т.к. у нас кроме ноды есть строгое API со всякими проверками на всякие хосты, порты и заголовки,
стоит гарантировать на каком хосте/порте будет открываться dev-версия.
Заменить:
"dev": "vite dev",
На:
"dev": "vite dev --host 127.0.0.1 --port 3000",
А ещё добавить:
"host": "vite dev --host --port 3000",
чтобы просто npm run host, и проект можно посмотреть хоть на телефоне (если подключен к тому же wi-fi)

Во имя науки

  1. Можно руками закинуть модуль и в «prod» и в «dev». И даже ничего не сломается, но — не знаю зачем. При попытке обновить/форсировать установку ещё раз — одна из записей сотрётся, в зависимости от куда --save-x и т.д.
  2. optionalDependencies — когда допускаем, что модуль может не установиться, а он нам и не очень-то и нужен.
  3. bundleDependencies — когда пилим свой модуль.
  4. Постоянно встречаются упоминания npm-shrinkwrap.json — Это «freeze» package-lock.json для тех случаев когда это внезапно нужно:
    • Ноддерживает старые версии ноды (когда package-lock.json только с v5),
    • Необходим для публикации в качестве npm-модуля,
    • А также, если встряли с версиями зависимостей. Тут npm-shrinkwrap.json позволит управлять версиями вручную (https://nodejs.org/en/blog/npm/managing-node-js-dependencies-with-shrinkwrap/). В общем, не думая об этом — мы ничего не упускаем. Просто будем знать на всякий случай.

Как делать install на проде?

Принято делать вот так:

npm ci --[ТОЛЬКО PROD-DEPENDENCIES]

  1. [ТОЛЬКО PROD-DEPENDENCIES]

Можно встретить много рекомендаций, обещающих один и тот же результат:
--omit=dev, --include=prod, --only=prod, --production

По всей видимости это из-за долгой эволюции ноды.
Для последней 10-й версии в документации есть только --include=prod (https://docs.npmjs.com/cli/v10/commands/npm-ci).
Но, так — у меня устанавливаются всё — и «prod» и «dev».
В итоге, делаю так:
npm ci --omit=dev
Множественное «опущение» делается так:
npm ci --omit=dev --omit=peer

  1. Что за ci? https://docs.npmjs.com/cli/v10/commands/npm-ci

Собственно, это то, что выкачивает node_modules/ на основе package-lock.json, в обход package.json, гарантируя то, что на проде будет так, как было на локале.

Иии, внииимаааниииеее...

Спасибо, за внимание.

Sentry image

Hands-on debugging session: instrument, monitor, and fix

Join Lazar for a hands-on session where you’ll build it, break it, debug it, and fix it. You’ll set up Sentry, track errors, use Session Replay and Tracing, and leverage some good ol’ AI to find and fix issues fast.

RSVP here →

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay