diff --git a/platforms/README.md b/platforms/README.md index 36cdf62b2..c51a6215e 100644 --- a/platforms/README.md +++ b/platforms/README.md @@ -14,13 +14,11 @@ ### Легенда -Вы решили создать библиотеку, которую можно будет использовать как в браузере, так и в Node.js. Если вы помните, то мы рассматривали Moment.js, в котором бы хитрый код. +Вы решили создать библиотеку, которую можно будет использовать как в браузере, так и в Node.js. Если вы помните, то мы рассматривали Moment.js, в котором был хитрый код. -Давайте попробуем создать библиотеку, в которой будет такой же код, но генерироваться он, естественно, будет автоматически. +Ранее для создания таких «универсальных» библиотек требовались сборщики вроде Webpack и транспайлеры вроде Babel. Однако современные среды — Node.js (начиная с версии 14) и все актуальные браузеры — нативно поддерживают ES-модули (ESM). Поэтому мы воспользуемся нативным подходом: никаких дополнительных инструментов сборки, только стандартные возможности языка и платформы. -Для этого мы воспользуемся [возможностями Webpack](https://webpack.js.org/guides/author-libraries/). - -Цель этого задания следующая: научить вас публиковать пакеты и познакомить с тем, как публикуется большинство пакетов, с которыми мы будем работать в дальнейшем. +Цель этого задания: убедиться, что один и тот же JavaScript-код (ES-модуль) работает в обеих средах — Node.js и браузере. ### Описание @@ -32,185 +30,50 @@ #### Шаг 1. Генерация проекта -**Важно**: везде далее под: -1. `USERNAME` будет пониматься имя вашего пользователя на GitHub в нижнем регистре (все буквы маленькие) -1. `REPO` будет пониматься название вашего репозитория на GitHub в нижнем регистре (все буквы маленькие) -1. `TOKEN` будет access token (см. ниже), который вы сгенерируете на GitHub +**Важно**: везде далее под `USERNAME` понимается имя вашего пользователя на GitHub в нижнем регистре (все буквы маленькие). -Создайте репозиторий на GitHub, после чего с помощью `npm init --scope=@username` (где `username` - имя вашего пользователя на GitHub в нижнем регистре, например, если на GitHub у меня логин `Netology-Code`, то команда будет `npm init --scope=@netology-code`) сгенерируйте package: +С помощью `npm init --scope=@username` (где `username` — имя вашего пользователя на GitHub в нижнем регистре, например, если на GitHub у меня логин `Netology-Code`, то команда будет `npm init --scope=@netology-code`) сгенерируйте package: 1. package name - @username/ajs (вам предложат автоматически) 2. version - 1.0.0 (по умолчанию) 3. description - оставьте пустым (по умолчанию) -4. entry point - dist/index.js (!надо поменять на dist/index.js) +4. entry point - src/index.js (!надо поменять на src/index.js) 5. repository - ссылка на ваш репозиторий в гитхаб 6. остальное всё по умолчанию (просто нажимайте enter) Добавьте `.gitignore`, который мы для вас [приготовили](../.gitignore). -Сделайте commit и push, убедитесь, что код попал в ваш репозиторий на GitHub - -#### Шаг 2. Получение Access Token - -Мы будем использовать сервис GitHub Packages (куда вы опубликуете свою библиотеку), чтобы не замусоривать npmjs.com. - -Для этого, нам нужно получить Access Token. - -Перейдите в настройки: - -![](pic/settings-menu.png) - -Выберите пункт меню [Developer Settings](https://github.com/settings/apps): - -![](pic/developer-menu.png) - -Выберите пункт меню Personal Access Tokens (1), затем нажмите на кнопку [Generate new token (2)](https://github.com/settings/tokens/new): - -![](pic/generate-token.png) - -Заполните поле Note (1) и поставьте флажок напротив write:packages (2), остальные все флажки выставятся автоматически: - -![](pic/token-settings.png) - -После чего нажмите на кнопку Generate в самом низу страницы: - -![](pic/token-generate-button.png) - -GitHub запроси у вас пароль для подтверждения: - -![](pic/github-sudo.png) - -После чего вам будет сгенерирован токен. **Важно**: токен будет показан только один раз, поэтому убедитесь, что скопировали его: - -![](pic/token.png) - -Если вдруг вы всё-таки не скопировали его, то просто удалите старый и сгенерируйте новый. - -#### Шаг 3. Установка зависимостей +Сделайте commit и push, убедитесь, что код попал в ваш репозиторий на GitHub. -Установите следующие dev-зависимости: +#### Шаг 2. Настройка package.json -* @babel/cli -* @babel/core -* @babel/preset-env -* babel-loader -* webpack -* webpack-cli +Поскольку мы используем нативные ES-модули, дополнительные зависимости (webpack, babel и т.д.) нам **не нужны**. Достаточно правильно настроить `package.json`. -Обратите внимание, для простоты мы не устанавливаем corejs, в случае Node.js он вообще не нужен, а в случае браузера будем считать, что это ответственность подключающего библиотеку. +Добавьте (или измените) в вашем `package.json` следующие поля: - -#### Шаг 4. Создание .npmrc - -Перейдите в ваш домашний каталог (узнать, где он находится, можно с помощью команды `echo $HOME` в Linux/Mac или `echo %USERPROFILE%` в Windows) и создайте там файл `.npmrc` (не забудьте про точку) следующего содержания: - -``` -//npm.pkg.github.com/:_authToken=TOKEN -registry=https://npm.pkg.github.com/USERNAME -``` - -
- Примечание! - - --- - - NPM для работы использует свои глобальные конфигурационные настройки, но эти настройки можно перезаписывать. Документация NPM различает несколько типов `npmrc` файлов: - - * per-project config file (/path/to/my/project/.npmrc) - проектный или локальный `config` файл - * per-user config file (~/.npmrc) - `config` файл пользовательский. (userconfig) - * global config file ($PREFIX/etc/npmrc) - глобальный `config` (globalconfig) - * npm builtin config file (/path/to/npm/npmrc) - встроенный файл конфигурации NPM - - Все файлы конфигурации npm представляют собой список параметров в формате ini (ключ=значение). Каждый из этих файлов загружается, и параметры конфигурации разрешаются в порядке приоритета. Например, параметр в файле userconfig переопределит параметр в файле globalconfig потому что приоритет выше. - Более подробно о npmrc можно почитать в [документации NPM](https://docs.npmjs.com/cli/v9/configuring-npm/npmrc). - - В этом задании вы создаёте пользовательский .npmrc (userconfig) файл для конфигурации и загрузки собственного пакета, в котором переопределяете переменную `registry`. Именно в этой переменной прописан путь до реестра пакетов NPM. Так как теперь при запуске npm переменная registry в globalconfig будет перезаписана, вы будете получать статус 404 на загрузку некоторых пакетов при выполнении следующих ДЗ на курсе. Для разрешения этой проблемы можно либо воспользоваться консольной командой - ```bash - npm config set registry https://registry.npmjs.org/ - ``` -либо просто опустошить userconfig (или удалить его - эффект одинаковый). - ---- -
- -Обратите внимание, вместо `TOKEN` вы подставляете токен, вместо `USERNAME` - имя своего пользователя в нижнем регистре. - -Q: что это нам даёт? - -A: NPM будет использовать эти данные для аутентификации, а с помощью GitHub Packages (запись `registry`) вы создадите собственный мини-аналог npmjs.com. - -Q: почему мы сразу не публикуем на NPM? - -A: чтобы не замусоривать его + из соображений безопасности, вам даётся всего 72 часа, чтобы удалить потом пакет оттуда (при условии, что никто не успел им воспользоваться). А кроме того, если вы удалите пакет, то потом уже не сможете воспользоваться этим именем (например, создадите пакет `ajs` для тестов, удалите его, а затем снова решите создать такой же - не получится). В GitHub Packages же проблем с этим не будет - можно просто удалить репо, создать новый и пробовать заново. - -#### Шаг 5. Конфигурационные файлы - -.babelrc: - -``` +```json { - "presets": [ - [ - "@babel/preset-env" - ] - ] -} -``` - -.browserslistrc: - -``` -last 1 version -> 1% -not dead -``` - -webpack.config.js: - -``` -const path = require('path'); - -module.exports = { - entry: './src/index.js', - output: { - path: path.resolve(__dirname, 'dist'), // каталог для результатов сборки - filename: 'index.js', // имя файла с результатами сборки (должно совпадать с entry point в шаге 1) - library: 'ajs', // название нашей библиотеки - libraryTarget: 'umd', // UMD (Universal Module Definition https://github.com/umdjs/umd) - шаблон, который позволяет использовать RequireJS и браузер - libraryExport: 'default', // экспортируется имя default - globalObject: 'this', // что принимать за глобальный объект, иначе сгенерируется window, а его, как вы знаете, на платформе Node.js нет - }, - module: { - rules: [ - { - test: /\.js$/, - exclude: /node_modules/, - use: { - loader: 'babel-loader', - }, - }, - ], - }, -}; -``` - -Особые настройки package.json: - -``` - "publishConfig": { - "registry":"https://npm.pkg.github.com/" - }, + "type": "module", + "exports": "./src/index.js", + "files": [ + "src" + ], "scripts": { - "build": "webpack --mode production" - }, + "start": "node src/index.js" + } +} ``` -`publishConfig` - куда публикуем наш пакет (в GitHub Packages) +Где: +- `"type": "module"` — указывает Node.js интерпретировать `.js`-файлы как ES-модули (вместо CommonJS) +- `"exports"` — точка входа в пакет; используется современными версиями Node.js для разрешения импортов +- `"files"` — список файлов и каталогов, которые будут включены в пакет при публикации (только `src`) +- `"scripts"."start"` — скрипт для быстрой проверки библиотеки локально -`build` - скрипт сборки +**Важно**: поле `"main"` можно оставить без изменений (`src/index.js`) — оно будет использоваться старыми инструментами, не поддерживающими `"exports"`. -#### Шаг 6. Создание библиотеки +#### Шаг 3. Создание библиотеки -В файле `src/index.js` разместите следующий код: +Создайте каталог `src`, а в нём файл `src/index.js` со следующим кодом: ```js function info() { @@ -225,122 +88,131 @@ export default { }; ``` -После чего запустите сборку: `npm run build` - у вас должен появиться каталог `dist` с файлом `index.js` внутри. - -Теперь добавьте всё в git и сделайте push. Удостоверьтесь, что все файлы были отправлены на GitHub. - -А теперь самое важное: опубликуем пакет. +Обратите внимание: это обычный ES-модуль. Никакой сборки не требуется — Node.js (14+) и все современные браузеры понимают `export`/`import` нативно. -Для этого есть команда `npm publish`. Если вы всё сделали правильно, то её вывод будет выглядеть примерно так (конечно, у вас будут другие адреса): +Сделайте commit и push. Убедитесь, что файл попал на GitHub. -![](pic/publishing.png) +#### Шаг 4. Проверка в Node.js (npm link) -Обратите внимание, что `dist/index.js` есть в опубликованном пакете, но не хранится в GitHub. +Для локальной проверки пакета без публикации удобно использовать `npm link`. -Теперь обновите страницу репозитория. Сбоку вы должны увидеть информацию о пакете: +В каталоге вашего проекта с библиотекой (`ajs`) выполните: -![](pic/published.png) - -
- Примечание - ---- - - Для отображения репозитория как пакета, и для того, чтобы плашка пакета появилась в репозитории необходито изменить видимость пакета с private на public. Для этого следуйте инструкции: +```bash +npm link +``` -Зайдите в раздел "Мои репозитории", в раздел "Packages" +Эта команда создаст глобальную символическую ссылку на ваш пакет. - ![](pic/instr1.png) - -Далее перейдите на страницу своего пакета. +Теперь создайте отдельный каталог для проверки: - ![](pic/instr2.png) - -На странице пакета кликните на настройки. - - ![](pic/instr3.png) - -На странице настроек пролистайте до самого конца страницы, там раздел "Danger Zone", кнопка "Change visibility". +```bash +mkdir ../ajs-test && cd ../ajs-test +npm init -y +``` - ![](pic/instr4.png) - ---- - -
-Кликните на имени пакета, чтобы посмотреть информацию о нём: +Свяжите ваш пакет: -![](pic/package-info.png) +```bash +npm link @username/ajs +``` -#### Шаг 7. Использование библиотеки +Не забудьте подставить свой username. Добавьте в `package.json` этого тестового проекта `"type": "module"`: -Нас будет интересовать два варианта использования (поскольку через шаблон Webpack всё будет работать как обычно): -* в Node.js -* в браузере через тег script +```json +{ + "type": "module", + "scripts": { + "start": "node src/index.js" + } +} +``` -Итак, в Node.js: -1. Создайте новый проект (GitHub репозиторий, clone, перейдите в каталог склонированного репо), выполните в нём команду: `npm init` -1. Установите ваш пакет: `npm install @netology-code/ajs@1.0.0` (естественно, вам нужно писать не netology-code, а имя своего репо) -1. Создайте файл `src/index.js` следующего содержания: +Создайте файл `src/index.js`: ```js -// у вас будет не netology-code, а ваш username -const ajs = require('@netology-code/ajs'); +import ajs from '@username/ajs'; console.log(ajs.info()); ``` -Пропишите скрипт запуска (в package.json): -``` -"scripts": { - "start": "node src/index.js" -} -``` +Запустите: `npm start` -Проверьте, что запускается (`npm start`): +Вы должны увидеть вывод: ![](pic/node.png) -Теперь сделаем почти то же самое в браузере. Здесь нам придётся немного посложнее, поскольку для npmjs.com есть специальный CDN https://unpkg.com/, который вам позволяет через URL подключать конкретные файлы. - -Для GitHub Packages такого пока нет, поэтому придётся сделать чуть по-другому. +Когда закончите проверку, уберите ссылки. В каталоге `ajs-test`: -Удалите каталог dist из .gitignore и запушьте его на GitHub (удостоверьтесь, что он там появился): +```bash +npm unlink @username/ajs +``` -![](pic/dist.png) +И в каталоге библиотеки (`ajs`): -Перейдите к файлу index.js (который в каталоге dist) и нажмите на кнопку Raw: +```bash +npm unlink +``` -![](pic/raw.png) +#### Шаг 5. Проверка в браузере (http-server) + +Теперь проверим, что тот же модуль работает в браузере. Для локального веб-сервера воспользуемся `npx http-server` — он запускается без установки. + +Создайте в корне проекта файл `index.html`: + +```html + + + + + ajs browser test + + +

ajs browser test

+

+  
+
+
+```
 
-Скопируйте URL из строки браузера (в примере он вот такой: https://raw.githubusercontent.com/netology-code/ajs/master/dist/index.js).
- 
-Теперь перейдите на сервис [Statically](https://statically.io/convert/), подставьте получившийся URL в верхнее поле (1), а с нижнего скопируйте итоговый URL (2):
+Обратите внимание: мы импортируем `./src/index.js` напрямую — браузер нативно поддерживает ES-модули через `
+
+
diff --git a/platforms/solution/package.json b/platforms/solution/package.json
new file mode 100644
index 000000000..db43e03f5
--- /dev/null
+++ b/platforms/solution/package.json
@@ -0,0 +1,14 @@
+{
+  "name": "@username/ajs",
+  "version": "1.0.0",
+  "type": "module",
+  "main": "src/index.js",
+  "exports": "./src/index.js",
+  "files": [
+    "src"
+  ],
+  "scripts": {
+    "start:node": "node src/test-node.js",
+    "start:browser": "npx http-server -c-1 -o index.html"
+  }
+}
diff --git a/platforms/solution/src/index.js b/platforms/solution/src/index.js
new file mode 100644
index 000000000..891f0930b
--- /dev/null
+++ b/platforms/solution/src/index.js
@@ -0,0 +1,10 @@
+function info() {
+  const m = new Map();
+  m.set('name', 'ajs');
+  m.set('version', '1.0.0');
+  return m;
+}
+
+export default {
+  info
+};
diff --git a/platforms/solution/src/test-node.js b/platforms/solution/src/test-node.js
new file mode 100644
index 000000000..38b0e8ece
--- /dev/null
+++ b/platforms/solution/src/test-node.js
@@ -0,0 +1,3 @@
+import ajs from './index.js';
+
+console.log(ajs.info());