From 70b8e89493954b391684c3e417b25d6295cda340 Mon Sep 17 00:00:00 2001 From: Lizaveta Date: Wed, 15 Nov 2023 15:55:57 +0200 Subject: [PATCH 1/9] feat: translate main module info --- stage1/modules/node-materials/README.md | 72 +++++++++++++------------ 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/stage1/modules/node-materials/README.md b/stage1/modules/node-materials/README.md index 2eb403f16..bb74559e7 100644 --- a/stage1/modules/node-materials/README.md +++ b/stage1/modules/node-materials/README.md @@ -2,44 +2,46 @@ ![Node.js logo](node/images/node-logo.jpg) -**Node.js** — среда выполнения JavaScript вне браузера. - -В основе Node.js — виртуальная машина V8. Она была создана компанией Google для браузера Chrome. V8 умеет выполнять JavaScript быстрее и экономнее, чем любая другая. - -Node.js используется для создания веб-серверов, самостоятельных приложений, программ для компьютеров и мобильных устройств, а также программирования микроконтроллеров. - -У Node.js только одна платформа, в которой в результате постоянного улучшения появляется поддержка новейших возможностей Javascript. В отличие от браузерного JS, не нужно заботиться о поддержке устаревших браузеров, практически никогда не нужно транспилировать код под старые версии платформы (однако, как и в случае с браузерным JS, перед использованием новейших возможностей языка рекомендуется проверить, что они уже поддерживаются выбранной версией Node.js). - -Компании выбирают Node.js за скорость и простоту разработки, возможность использовать один язык при создании фронтенда и бэкенда, скорость работы созданных приложений, кроссплатформенность, экономию ресурсов. - -К относительным недостаткам Node.js можно отнести то, что он несколько хуже подходит для решения задач, требующих интенсивных вычислений (хотя с появлением `worker threads` ситуация значительно улучшилась). - -## Содержание - -1. [Начало работы](node/node-introduction.md) -2. [Операции ввода/вывода](node/node-io.md) -3. [Стандартные потоки ввода/вывода](node/node-stdio.md) -4. [Аргументы командной строки](node/node-argv.md) -5. [Доступ к файловой системе](node/node-fs-access.md) -6. [Модули](node/node-module.md) - - [модуль path](node/module/path.md) - - [модуль fs](node/module/fs.md) - - [модуль os](node/module/os.md) - - [модуль http](node/module/http.md) - - [модули, устанавливаемые через npm](node/module/npm-module.md) - - [создание собственных модулей](node/module/create-module.md) -7. [События](node/events.md) -8. [Потоки](node/stream.md) - - [поток чтения](node/stream-readable.md) - - [поток записи](node/stream-writable.md) - - [объединение потоков](node/stream-pipes.md) -9. [Проекты](node/projects/projects.md) - - [приложение для заметок](node/projects/notes.md) - - [приложение Таймер](node/projects/timer.md) - - [приложение Github](node/projects/github-app.md) +**Node.js** — an open-source, cross-platform JavaScript runtime environment. + +Node.js is based on the V8 virtual machine. It was created by Google for the Chrome browser. V8 can execute JavaScript faster and more efficiently than any other. + +Node.js is used to create web servers, standalone applications, programs for computers and mobile devices, and to program microcontrollers. + +Node.js has only one platform, and through continuous improvement, it supports the latest Javascript features. Unlike browser-based JS, you don't need to worry about supporting legacy browsers, and you almost never need to transpile code for older versions of the platform (however, as with browser-based JS, before using the latest language features, it is recommended to check that they are already supported by the selected version of Node.js). + +Companies opt for Node.js due to its speedy and straightforward development, the convenience of using a single language for both frontend and backend, the rapid performance of the resulting applications, the capability to craft cross-platform software, and the cost-effective use of resources. + +Node.js has its drawbacks, particularly in scenarios demanding intensive calculations, although the introduction of worker threads has notably enhanced its suitability in such cases. + +## Table of Contents + +1. [Getting started](node/node-introduction.md) +2. [Input/Output Operations](node/node-io.md) +3. [Standard Input/Output Streams](node/node-stdio.md) +4. [Command Line Arguments](node/node-argv.md) +5. [File System Access](node/node-fs-access.md) +6. [Modules](node/node-module.md) + - [Path Module](node/module/path.md) + - [File System Module](node/module/fs.md) + - [Operating System Module](node/module/os.md) + - [HTTP Module](node/module/http.md) + - [Modules installed via npm](node/module/npm-module.md) + - [Creating Custom Modules](node/module/create-module.md) +7. [Events](node/events.md) +8. [Streams](node/stream.md) + - [Read Stream](node/stream-readable.md) + - [Write Stream](node/stream-writable.md) + - [Combining Streams](node/stream-pipes.md) +9. [Projects](node/projects/projects.md) + - [Note-taking Application](node/projects/notes.md) + - [Timer Application](node/projects/timer.md) + - [Github Application](node/projects/github-app.md) ## Материалы +// TODO: revise materials + - [Введение в node.js](http://imnotgenius.com/vvedeniya-v-node-js/) - [Гайд по Node.js](https://nodejsdev.ru/guide/) - [Скринкаст по Node.js](https://learn.javascript.ru/screencast/nodejs) From cdb2403551ba50055955427b2c74df5fc30f62b8 Mon Sep 17 00:00:00 2001 From: Lizaveta Date: Thu, 16 Nov 2023 00:39:01 +0200 Subject: [PATCH 2/9] feat: translate module folder files --- stage1/modules/node-materials/README.md | 2 +- .../node/module/create-module.md | 22 ++++---- .../modules/node-materials/node/module/fs.md | 36 ++++++------- .../node-materials/node/module/http.md | 50 +++++++++---------- .../node-materials/node/module/npm-module.md | 8 +-- .../modules/node-materials/node/module/os.md | 20 ++++---- .../node-materials/node/module/path.md | 32 ++++++------ 7 files changed, 85 insertions(+), 85 deletions(-) diff --git a/stage1/modules/node-materials/README.md b/stage1/modules/node-materials/README.md index bb74559e7..72aa041e3 100644 --- a/stage1/modules/node-materials/README.md +++ b/stage1/modules/node-materials/README.md @@ -26,7 +26,7 @@ Node.js has its drawbacks, particularly in scenarios demanding intensive calcula - [File System Module](node/module/fs.md) - [Operating System Module](node/module/os.md) - [HTTP Module](node/module/http.md) - - [Modules installed via npm](node/module/npm-module.md) + - [Installing Modules via npm](node/module/npm-module.md) - [Creating Custom Modules](node/module/create-module.md) 7. [Events](node/events.md) 8. [Streams](node/stream.md) diff --git a/stage1/modules/node-materials/node/module/create-module.md b/stage1/modules/node-materials/node/module/create-module.md index d35897f38..0f5546383 100644 --- a/stage1/modules/node-materials/node/module/create-module.md +++ b/stage1/modules/node-materials/node/module/create-module.md @@ -1,8 +1,8 @@ -## Создание собственных модулей +## Creating Custom Modules [HOME](../../README.md) -Создадим файл `user.js`, в нём напишем следующий код: +Let's create a file called `user.js` and write the following code in it: ```js const user = { @@ -14,9 +14,9 @@ const user = { }; ``` -Предположим, что мы хотим использовать наш объект `user` в других модулях. Для этого нам нужно **экспортировать** его из `user.js`. Для этого есть несколько способов: +Suppose we want to use our `user` object in other modules. To do this, we need to **export** it from `user.js`. There are several ways to achieve this: -1. Записать значение в качестве свойства объекта `exports`, либо `module.exports`: +1. Assign the value as a property of the `exports` object or `module.exports`: ```js // user.js @@ -27,12 +27,12 @@ const user = { console.log(`Hi! My name is ${this.name}`); }, }; -// работает так же, как и module.exports.user = user; +// works the same as module.exports.user = user; exports.user = user; // otherFile.js /* -то же самое, что и +same as const userModuleExport = require('./user.js'); const user = userModuleExport.user; */ @@ -40,7 +40,7 @@ const { user } = require("./user.js"); user.sayHi(); // Hi! My name is Ann ``` -2. Перезаписать сам объект `module.exports` (в этом случае необходимо использовать именно `module.exports`, т.к. перезапись `exports` не приведет к желаемому результату): +2. Overwrite the `module.exports` object itself (in this case, it is necessary to use `module.exports`, as overwriting `exports` will not yield the desired result): ```js // user.js @@ -51,7 +51,7 @@ const user = { console.log(`Hi! My name is ${this.name}`); }, }; -// exports = user не сработает +// exports = user won't work module.exports = user; // otherFile.js @@ -59,7 +59,7 @@ const user = require("./user.js"); user.sayHi(); // Hi! My name is Ann ``` -Также экспортируемое значение нет необходимости предварительно записывать в переменную, если оно не используется где-то еще: +Also, there is no need to assign the exported value to a variable if it is not used elsewhere: ```js // user.js @@ -76,7 +76,7 @@ const user = require("./user.js"); user.sayHi(); // Hi! My name is Ann ``` -`module.exports` можно перезаписать не только объектом, но и примитивом: +You can overwrite `module.exports` not only with an object but also with a primitive: ```js // a.js @@ -87,4 +87,4 @@ const answer = require("./a.js"); console.log(answer); //42 ``` -Снаружи для непосредственного взаимодействия будут доступны только экспортируемые объекты. Если в файле `user.js` есть два объекта, а экспортируется только первый, то второй будет доступен только внутри этого модуля. Благодаря этому в каждом модуле можно вести независимую разработку, а также надежно инкапсулировать то, что не должно быть доступным извне. +Externally, only the exported objects will be available for direct interaction. If the `user.js` file contains two objects but only the first one is exported, the second will be accessible only within that module. This enables independent development in each module and secure encapsulation of what should not be accessible externally. diff --git a/stage1/modules/node-materials/node/module/fs.md b/stage1/modules/node-materials/node/module/fs.md index 0500292ad..43da7e1c1 100644 --- a/stage1/modules/node-materials/node/module/fs.md +++ b/stage1/modules/node-materials/node/module/fs.md @@ -1,23 +1,23 @@ -## Модуль fs +## File System Module [HOME](../../README.md) -Модуль `fs` (file system) нужен для работы с файлами и папками. Он умеет создавать и удалять файлы и папки, переименовывать их, записывать и считывать данные. +The `fs` (file system) module is used for working with files and folders. It can create and delete files and folders, rename them, and read and write data. -Импортируем `fs`: +Import `fs`: ```js const fs = require("fs"); ``` -Создадим папку. Для этого есть два метода: +Let's create a folder. There are two methods for this: -- асинхронный `fs.mkdir` -- синхронный `fs.mkdirSync` +- asynchronous `fs.mkdir` +- synchronous `fs.mkdirSync` -При работе с файловой системой рекомендуется использовать асинхронные методы, которые не блокируют поток выполнения. Если папка или файл будут создаваться синхронно, выполнение кода остановится, пока они не будут созданы. Такие паузы в выполнении кода нежелательны. +When working with the file system, it is recommended to use asynchronous methods that do not block the execution thread. If folders or files are created synchronously, the code execution will pause until they are created. Such pauses in code execution are undesirable. -Создадим папку `notes` в текущей директории +Let's create a folder named `notes` in the current directory: ```js const fs = require("fs"); @@ -25,11 +25,11 @@ const path = require("path"); fs.mkdir(path.join(__dirname, "notes"), (err) => { if (err) throw err; - console.log("Папка была создана"); + console.log("Folder was created"); }); ``` -Создадим файл `mynotes.txt`, содержащий текст `Hello world` внутри папки `notes` +Now, let's create a file named `mynotes.txt` containing the text `Hello world` inside the `notes` folder: ```js const fs = require("fs"); @@ -40,12 +40,12 @@ fs.writeFile( "Hello world", (err) => { if (err) throw err; - console.log("Файл был создан"); + console.log("File was created"); }, ); ``` -Дополним файл, записав в него ещё какую-то информацию +Let's add more information to the file: ```js const fs = require("fs"); @@ -56,12 +56,12 @@ fs.appendFile( " From append file", (err) => { if (err) throw err; - console.log("Файл был изменен"); + console.log("File was modified"); }, ); ``` -Прочитаем информацию из файла +Let's read the information from the file: ```js const fs = require("fs"); @@ -77,7 +77,7 @@ fs.readFile( ); ``` -Переименуем файл +Let's rename the file: ```js const fs = require("fs"); @@ -88,11 +88,11 @@ fs.rename( path.join(__dirname, "notes", "notes.txt"), (err) => { if (err) throw err; - console.log("Файл переименован"); + console.log("File renamed"); }, ); ``` -### Задание +### Project -[Приложение для заметок](../projects/notes.md) +[Note-taking Application](../projects/notes.md) diff --git a/stage1/modules/node-materials/node/module/http.md b/stage1/modules/node-materials/node/module/http.md index f4a078a0c..78a1b184a 100644 --- a/stage1/modules/node-materials/node/module/http.md +++ b/stage1/modules/node-materials/node/module/http.md @@ -1,8 +1,8 @@ -## Модуль http +## HTTP Module [HOME](../../README.md) -В Node.js для работы с сервером и протоколом HTTP используется модуль `http` +In Node.js, the `http` module is used for working with servers and the HTTP protocol. ```js const http = require("http"); @@ -11,7 +11,7 @@ const PORT = 3000; const requestHandler = (request, response) => { const { method, url } = request; - console.log(`Получен ${method}-запрос на ${url}`); + console.log(`Received ${method} request to ${url}`); response.write("Hello Node.js"); response.end("Bye!"); }; @@ -19,23 +19,23 @@ const requestHandler = (request, response) => { const server = http.createServer(requestHandler); server.listen(PORT, "localhost", () => { - console.log(`Сервер запущен на порту ${PORT}`); + console.log(`Server is running on port ${PORT}`); }); ``` -Рассмотрим этот код. -Импортируем модуль `http` +Let's go through this code. +We import the `http` module: ```js const http = require("http"); ``` -Используя его метод `createServer()`, создаем http-сервер: +Using its `createServer()` method, we create an HTTP server: ```js const requestHandler = (request, response) => { const { method, url } = request; - console.log(`Получен ${method}-запрос на ${url}`); + console.log(`Received ${method} request to ${url}`); response.write("Hello Node.js"); response.end("Bye!"); }; @@ -43,36 +43,36 @@ const requestHandler = (request, response) => { const server = http.createServer(requestHandler); ``` -В качестве колбэка данный метод получает функцию `requestHandler` с двумя параметрами `request` и `response` (имена могут быть любыми другими) +This method takes a callback function `requestHandler` with two parameters, `request` and `response` (you can use any names). -- `request` хранит информацию о запросе -- `response` отвечает за отправку ответа +- `request` holds information about the request +- `response` is responsible for sending the response -Наш `requestHandler` выводит в консоль метод запроса и адрес запрашиваемого ресурса, а также в ответ отправляет сообщения `Hello from Node.js` и `Bye!`. -`response.write()` пишет в тело ответа сообщение, а `respone.end()` сообщает серверу, что заголовки и тело ответа записаны и его можно отправлять. -NB! `response.end()` должен завершать каждый ответ. Без этого обработка запроса "зависнет" — запрос будет получен, но не будет до конца обработан. +Our `requestHandler` logs the request method and the address of the requested resource to the console. It also sends the messages `Hello from Node.js` and `Bye!` as responses. +The `response.write()` method writes the message to the response body, and `response.end()` informs the server that the headers and body of the response are written and it can be sent. +Note that `response.end()` should terminate every response. Without it, the request processing will "hang" — the request will be received but not fully processed. ```js const requestHandler = (request, response) => { const { method, url } = request; - console.log(`Получен ${method}-запрос на ${url}`); + console.log(`Received ${method} request to ${url}`); response.write("Hello Node.js"); response.end("Bye!"); }; ``` -Метод `listen` сервера запускает его и он начинает прослушивать определенный порт в ожидании соединений. Он имеет несколько сигнатур, в нашем случае он принимает три параметра: локальный порт, локальный адрес и колбэк-функцию, которая запускается при начале прослушивания подключений. +The server's `listen` method starts it, and it begins to listen on the specified port for connections. It has multiple signatures; in our case, it takes three parameters: the local port, the local address, and a callback function that runs when it starts listening for connections. ```js server.listen(PORT, "localhost", () => { - console.log(`Сервер запущен на порту ${PORT}`); + console.log(`Server is running on port ${PORT}`); }); ``` -Запустите файл с кодом, откройте браузер и перейдите по адресу `localhost:3000/some/page`. -NB! Для того, чтобы запустить сервер с другим кодом, его нужно остановить и запустить заново. Мы уже знаем, как завершить процесс Node.js. На одном порту одновременно можно запустить только один сервер. +Run the file with the code, open your browser, and go to the address `localhost:3000/some/page`. +Note that to run the server with different code, you need to stop it and restart it. You already know how to terminate a Node.js process. Only one server can be running on a port at a time. -В методах `write` и `end` можно передать строку, которая содержит HTML-теги с инлайн стилями. Эти теги будут корректно обработаны браузером +In the `write` and `end` methods, you can pass a string containing HTML tags with inline styles. These tags will be correctly processed by the browser. ```js const http = require("http"); @@ -83,7 +83,7 @@ const requestHandler = (request, response) => { const { method, url } = request; const heading = `

${url} page

`; const content = `
Green block 100px x 100px
`; - console.log(`Получен ${method}-запрос на ${url}`); + console.log(`Received ${method} request to ${url}`); response.write(heading); response.end(content); }; @@ -91,12 +91,12 @@ const requestHandler = (request, response) => { const server = http.createServer(requestHandler); server.listen(PORT, "localhost", () => { - console.log(`Сервер запущен на порту ${PORT}`); + console.log(`Server is running on port ${PORT}`); }); ``` -Повторно зайдя на страницу `localhost:3000/some/page` мы увидим рендер разметки с инлайн стилями, полученную с сервера. +By revisiting the page `localhost:3000/some/page`, you will see the rendered markup with inline styles received from the server. -## Задание +## Project -[Приложение GitHub](../projects/github-app.md) +[Github Application](../projects/github-app.md) diff --git a/stage1/modules/node-materials/node/module/npm-module.md b/stage1/modules/node-materials/node/module/npm-module.md index b32e1361e..ac6b89da0 100644 --- a/stage1/modules/node-materials/node/module/npm-module.md +++ b/stage1/modules/node-materials/node/module/npm-module.md @@ -1,14 +1,14 @@ -### Установка модулей через npm. +### Installing Modules via npm [HOME](../../README.md) -Установим `npm` модуль `colors`. Для этого в терминале выполним команду +Let's install the `colors` module using `npm`. In the terminal, execute the command: ```powershell npm install colors ``` -В папке проекта создадим файл `index.js` и напишем в нём код: +In the project folder, create a file named `index.js` and write the following code: ```js require("colors"); @@ -16,4 +16,4 @@ const text = "Hello, world!"; console.log(text.rainbow); ``` -Данный пакет по умолчанию работает за счет расширения `String.prototype`, поэтому нам не нужно записывать результат `require('colors')` в переменную, поскольку мы не используем его. Запустим файл, выполнив в терминале команду `node index`. +This package by default operates by extending `String.prototype`, so we don't need to assign the result of `require('colors')` to a variable since we're not using it. Run the file by executing the command `node index` in the terminal. diff --git a/stage1/modules/node-materials/node/module/os.md b/stage1/modules/node-materials/node/module/os.md index 2377bbeee..777c41dbd 100644 --- a/stage1/modules/node-materials/node/module/os.md +++ b/stage1/modules/node-materials/node/module/os.md @@ -1,33 +1,33 @@ -## Модуль os +## Operating System Module [HOME](../../README.md) -Модуль `os` (operating system) предоставляет данные об операционной системе пользователя +The `os` (operating system) module provides information about the user's operating system ```js const os = require("os"); -// Платформа +// Platform console.log(os.platform()); -// Архитектура +// Architecture console.log(os.arch()); -// Информация о CPU +// CPU Information console.log(os.cpus()); -// Общий объём памяти +// Total Memory console.log(os.totalmem()); -// Объём свободной памяти +// Free Memory console.log(os.freemem()); -// Корневая директория +// Home Directory console.log(os.homedir()); -// Время работы системы +// System Uptime console.log(os.uptime()); -// Символ окончания строки в данной системе +// End of Line Symbol in the Current System console.log(os.EOL); ``` diff --git a/stage1/modules/node-materials/node/module/path.md b/stage1/modules/node-materials/node/module/path.md index f0f41a746..d29a39181 100644 --- a/stage1/modules/node-materials/node/module/path.md +++ b/stage1/modules/node-materials/node/module/path.md @@ -1,38 +1,38 @@ -## Модуль Path +## Path Module [HOME](../../README.md) -Одним из стандартных модулей является `path`. Модуль `path` предназначен для того, чтобы работать с путями в Node.js. При помощи него можно получить имя файла, расширение файла, имя папки, указать путь к файлу. +One of the standard modules in Node.js is `path`. The `path` module is designed to work with file paths in Node.js. It allows you to get the file name, file extension, folder name, and specify the path to the file. -Чтобы использовать `path`, его необходимо подключить: +To use `path`, you need to require it: ```js const path = require("path"); ``` -Информацию о свойствах и методах `path` можно найти в [документации Node.js](https://nodejs.org/docs/latest-v14.x/api/path.html). +You can find the information about the properties and methods of `path` in the [Node.js documentation](https://nodejs.org/docs/latest-v14.x/api/path.html). -Рассмотрим некоторые из них: +Let's look at some of them: -- Получение данных о файле +- Retrieving file data ```js - // для файла, расположенного по адресу C:\Users\Admin\Desktop\nodejs-basic\index.js + // for a file located at C:\Users\Admin\Desktop\nodejs-basic\index.js const path = require("path"); - console.log(path.basename(__filename)); // index.js - имя файла на Windows, полный путь к файлу на POSIX-системах - console.log(path.dirname(__filename)); // C:\Users\Admin\Desktop\nodejs-basic - название папки - console.log(path.extname(__filename)); // .js - расширение файла - console.log(path.parse(__filename)); // возвращает объект в котором указывается корень диска, имя папки, имя файла, расширение файла, имя файла без расширения + console.log(path.basename(__filename)); // index.js - file name on Windows, full file path on POSIX systems + console.log(path.dirname(__filename)); // C:\Users\Admin\Desktop\nodejs-basic - folder name + console.log(path.extname(__filename)); // .js - file extension + console.log(path.parse(__filename)); // returns an object specifying the disk root, folder name, file name, file extension, file name without extension ``` -- Конкатенация путей +- Concatenating paths ```js - // для файла, расположенного по адресу C:\Users\Admin\Desktop\nodejs-basic\index.js + // for a file located at C:\Users\Admin\Desktop\nodejs-basic\index.js const path = require("path"); - // вернет C:\Users\Admin\Desktop\nodejs-basic\test\second.html + // returns C:\Users\Admin\Desktop\nodejs-basic\test\second.html console.log(path.join(__dirname, "test", "second.html")); ``` - `path.join()` объединяет заданные сегменты пути вместе, используя в качестве разделителя разделитель данной конкретной платформы (для Linux - прямой слэш, для Windows - обратный слэш), результат - относительный путь + `path.join()` concatenates the specified path segments together, using the separator for the specific platform (forward slash `/` for Linux, backslash `\` for Windows). The result is a relative path. ```js const path = require("path"); console.log(path.resolve(__dirname, "./test", "/second.html")); ``` - `path.resolve()` преобразует последовательность путей или сегментов пути в абсолютный путь справа налево и нормализует его: если в некоторых сегментах пути указываются слэши, а в некоторых нет, всё равно будет сгенерирован правильный путь. + `path.resolve()` converts a sequence of paths or path segments into an absolute path from right to left and normalizes it: if some path segments have slashes while others don't, it will still generate the correct path. From 93f108d4333c7a8e993c02e65ab72b7b4f3cd5da Mon Sep 17 00:00:00 2001 From: Lizaveta Date: Thu, 16 Nov 2023 03:15:03 +0200 Subject: [PATCH 3/9] feat: translate projects folder files --- stage1/modules/node-materials/README.md | 6 +- .../node/projects/github-app.md | 130 ++++++++++-------- .../node-materials/node/projects/notes.md | 73 +++++----- .../node-materials/node/projects/projects.md | 8 +- .../node-materials/node/projects/timer.md | 44 +++--- 5 files changed, 140 insertions(+), 121 deletions(-) diff --git a/stage1/modules/node-materials/README.md b/stage1/modules/node-materials/README.md index 72aa041e3..dfad2d8db 100644 --- a/stage1/modules/node-materials/README.md +++ b/stage1/modules/node-materials/README.md @@ -34,9 +34,9 @@ Node.js has its drawbacks, particularly in scenarios demanding intensive calcula - [Write Stream](node/stream-writable.md) - [Combining Streams](node/stream-pipes.md) 9. [Projects](node/projects/projects.md) - - [Note-taking Application](node/projects/notes.md) - - [Timer Application](node/projects/timer.md) - - [Github Application](node/projects/github-app.md) + - [Notes App](node/projects/notes.md) + - [Timer App](node/projects/timer.md) + - [Github App](node/projects/github-app.md) ## Материалы diff --git a/stage1/modules/node-materials/node/projects/github-app.md b/stage1/modules/node-materials/node/projects/github-app.md index 456a747bd..1cd22d951 100644 --- a/stage1/modules/node-materials/node/projects/github-app.md +++ b/stage1/modules/node-materials/node/projects/github-app.md @@ -1,39 +1,41 @@ -## Приложение GitHub +## Github App [HOME](../../README.md) -Создадим консольное приложение, которое в качестве аргумента командной строки будет принимать имя пользователя GitHub и выводить список репозиториев этого пользователя. +Let's create a console application that, as a command-line argument, will take a GitHub username and display a list of that user's repositories. -Для этого воспользуемся GitHub API. По запросу `https://api.github.com/users/USERNAME/repos` возвращается список репозиториев пользователя, `USERNAME` которого указан в ссылке. +To achieve this, we'll use the GitHub API. The request `https://api.github.com/users/USERNAME/repos` returns a list of repositories for the user whose `USERNAME` is specified in the link. -1. Начнём работу с создания нового Node.js приложения. - Создадим папку `github-app`, откроем её в VS Code, в терминале выполним команду +1. Start by creating a new Node.js application + +Create a folder named github-app, open it in VS Code, and run the following command in the terminal: ``` npm init -y ``` -Создадим два файла: +Create two files: + +- `app.js` - the main application file +- `github.js` - the file where we write the logic for interacting with the API -- `app.js` - основной файл приложения -- `github.js` - в нём пишем логику взаимодействия с API. +2. Let's start with the `app.js` file -2. Начнём с файла `app.js` - Импортируем в него модуль `github`. Так как это самостоятельно созданный модуль, в качестве параметра метода `require()` указываем путь к файлу с кодом модуля +Import the `github` module into it. Since this is a manually created module, specify the path to the module code file as a parameter to the `require()` method: ```js const github = require("./github"); ``` -Создадим переменную `username` в которой сохраним аргумент командной строки, переданный при запуске программы +Create a variable `username` to store the command-line argument passed when the program is launched: ```js const username = process.argv[2]; ``` -Используем импортированный объект `github`, у которого есть свойство `getRepos()` - функция, возвращающая список репозиториев пользователя. -Параметры функции `getRepos()` - `username` - имя пользователя и функция обратного вызова, принимающая два параметра - `error` - ошибка и `repos` - полученные данные, в нашем случае - список репозиториев. -В теле функции обработаем ошибку и выведем в консоль названия репозиториев +Use the imported `github` object, which has a `getRepos()` property - a function that returns a list of user repositories. +The parameters of the `getRepos()` function are `username` - the username and a callback function taking two parameters: `error` - an error, and `repos` - the received data, in our case, a list of repositories. +In the callback function, let's handle the error and print the names of the repositories to the console: ```js github.getRepos(username, (error, repos) => { @@ -43,12 +45,16 @@ github.getRepos(username, (error, repos) => { }); ``` -3. Переходим к файлу `github.js`. Наше приложение будет общаться с сервером по протоколу `https`. Для этого в Node.js есть встроенный модуль `https`, аналогичный [модулю http](../module/http.md). - Для отправки запроса к API используем метод `get()` , который позволяет получить данные от сервера. - Импортируем модуль `https` и напишем код функции `getRepos()`. Параметр функции - `username` - имя пользователя GitHub. - У метода `https.get()` два параметра: URL, по которому отправляется запрос, и функция обратного вызова, принимающая один параметр - ответ сервера `responce`, сокращенно `res`. - Свойство `res.statusCode` возвращает ответ сервера. Ответ `200` свидетельствует об успешном подключении, любой другой говорит о проблеме с подключением. - Экспортируем модуль `github` как объект, со свойством `getRepos` и значением `getRepos`: +3. Move on to the `github.js` file + +Our application will communicate with the server over the `https` protocol. For this, Node.js has a built-in `https module`, similar to the [HTTP Module](../module/http.md). + +To send a request to the API, we'll use the `get()` method, which allows us to retrieve data from the server. +Import the `https` module and write the code for the `getRepos()` function. The function parameter is `username` - the GitHub username. +The `https.get()` method has two parameters: the URL to which the request is sent and a callback function taking one parameter - the server response, abbreviated as `res`. + +The `res.statusCode` property returns the server response. A response of `200` indicates a successful connection, while any other response indicates a connection problem. +Export the `github` module as an object with a `getRepos` property and the value of `getRepos`: ```js const https = require("https"); @@ -60,19 +66,19 @@ function getRepos(username) { module.exports = { getRepos }; ``` -Запустим файл `app`. Аргументом командной строки при запуске файла укажем любой известный нам репозиторий: +Run the `app` file. Specify a well-known repository as a command-line argument when running the file: ``` node app goldbergyoni ``` -Приложение возвращает ошибку `403`. -Код ответа на статус ошибки `"HTTP 403 Forbidden"` указывает, что сервер понял запрос, но отказывается его авторизовать. -При этом, если мы попробуем запросить те же данные из бразера, перейдя по ссылке https://api.github.com/users/goldbergyoni/repos , получим страницу с нужными нам данными. +The application returns a `403` error. +The response code for the error status `HTTP 403 Forbidden` indicates that the server understood the request but refuses to authorize it. +However, if we try to request the same data from the browser by visiting the link https://api.github.com/users/goldbergyoni/repos, we get the page with the data we need. -Для запроса к API необходимо указать заголовок `User-Agent`. Браузер при переходе по ссылке добавляет этот заголовок сам, в приложении Node.js его необходимо указать. +To make a request to the API, we need to specify the `User-Agent` header. While the browser adds this header automatically when navigating to a link, in a Node.js application, we need to specify it. -Перед отправкой запроса создадим объект `option`, который отправим вместе с запросом +Before sending the request, create an `option` object that we'll send along with the request: ```js const option = { @@ -84,12 +90,12 @@ const option = { }; ``` -Первым параметром метода `https.get()` укажем не URL-адрес, по которому отправляется запрос, а `option`. -Приложение возвращает статус `200`. который сообщает об успешном подключении. +Use the `option` object as the first parameter in `the https.get` method. The application returns a `200` status, indicating a successful connection. + +4. Handling incoming data -4. Обработка входящих данных. - Практически всё в Node.js, в том числе общение с сервером, реализуется асинхронно, при помощи событий и потоков. Информация от сервера приходит по частям. - У ответа сервера `response` (`res`) есть событие `data`, которое сработает тогда, когда от сервера придёт часть запрошенной информации. Подпишемся на это событие и выведем полученные данные в консоль +Almost everything in Node.js, including communication with the server, is implemented asynchronously, using events and streams. Information from the server comes in parts. +The server response (`res`) has a `data` event, which fires when a part of the requested information comes from the server. Subscribe to this event and print the received data to the console: ```js function getRepos(username) { @@ -106,8 +112,9 @@ function getRepos(username) { } ``` -В консоли несколько объектов `Buffer`. Преобразовать данные в текст можно или используя метод `data.toString()`, или воспользовавшись методом `res.setEncoding('utf-8')`. -Чтобы объединить фрагменты данных, создадим переменную `body = ''` и все фрагменты данных будем к ней присоединять +In the console, you see several `Buffer` objects. You can convert the data to text either by using the `data.toString()` method or by using the `res.setEncoding('utf-8')` method. + +To concatenate the data fragments, create a variable `body = ''`, and append all data fragments to it: ```js function getRepos(username) { @@ -126,8 +133,8 @@ function getRepos(username) { } ``` -У метода `response` (`res`) есть событие `end`, которое сработает когда передача данных закончится. -При наступлении этого события при помощи метода `JSON.parse(body)` преобразуем полученные данные в массив +The response (`res`) method has an `end` event, which triggers when the data transmission is complete. +Upon the occurrence of this event, use the `JSON.parse(body)` method to convert the received data into an array: ```js function getRepos(username) { @@ -150,7 +157,11 @@ function getRepos(username) { } ``` -5. Как передать полученные данные в модуль `app`, который импортирует модуль `github`. Обратите внимание, что модуль `app` ожидает функцию `getRepos()` с двумя параметрами - `username`, и функцией обратного вызова у которой тоже два параметра `(error, repos)`. Эту функцию обратного вызова необходимо указать параметром функции `getRepos()` в модуле `github`. Назовём её `done` - стандартное название подобных функций. И внутри метода `response` (`res`) при наступлении события `end` вызовем функцию `done(null, result);`. Первый параметр функции - `null` - отсутствие ошибки, второй параметр - `result` - массив с репозиториями. +5. Passing data to the `app` module + +To pass the obtained data to the `app` module, which imports the `github` module, note that the `app` module expects the `getRepos()` function with two parameters - `username` and a callback function that also has two parameters `(error, repos)`. This callback function needs to be specified as a parameter to the `getRepos()` function in the `github` module. Let's name it `done` - a standard name for such functions. + +Inside the response method (`res`), upon the occurrence of the `end` event, call the `done(null, result)` function. The first parameter of the function is `null` - no error, and the second parameter is `result` - an array of repositories: ```js function getRepos(username, done) { @@ -173,51 +184,52 @@ function getRepos(username, done) { } ``` -6. Обработка ошибок - При работе приложения ошибки могут быть в следующих случаях +6. Error Handling + +- The application is launched without a username +- An error occurs when sending a request if a nonexistent username is specified +- An error occurs when receiving a response from the server +- An error occurs when converting the received data into an array -- приложение запускается без имени пользователя -- ошибка при отправке запроса, если указано несуществующее имя пользователя -- ошибка при получении ответа от сервера -- при преобразовании полученных данных в массив - Ошибки обрабатываем в модуле `github` и передаём их в модуль `app` указывая первым параметром функции `done()` +Handle errors in the `github` module and pass them to the `app` module, specifying them as the first parameter of the `done()` function. -1. чтобы обработать ошибку, когда приложение запускается без имени пользователя, проверяем, пришёл ли в функцию `done()` параметр `username` +1. To handle the error when the application is launched without a username, check if the `username` parameter is present in the `done()` function: ```js -if (!username) return done(new Error("Необходимо указать имя пользователя")); +if (!username) return done(new Error("Username is required")); ``` -2. Ошибка запроса - событие метода `request` `error`. - Создадим переменную `request` указав её значением метод `https.get()` +2. Request error - the `error` event of the `request` method. -``` -req.on('error', error => done(new Error('Не удалось отправить запрос'))); +Create a variable `request` and set its value to the `https.get()` method: + +```js +req.on('error', error => done(new Error('Failed to send request'))); ``` -3. об ошибке при получении ответа от сервера свидетельствует статус ответа отличный от `200` +3. An error in receiving a response from the server is indicated by a response status other than `200`: ```js if (res.statusCode !== 200) - return done(new Error("Ошибка при работе с сервером")); + return done(new Error("Error working with the server")); ``` -4. Для обработки ошибок в синхронных методах, к которым относится `JSON.parse()` используется конструкция `try {} catch() {}` +4. To handle errors in synchronous methods, including `JSON.parse()`, use the `try {} catch() {}` statement: ```js try { const result = JSON.parse(body); done(null, result); } catch (error) { - done(new Error("Не удалось обработать данные")); + done(new Error("Failed to process data")); } ``` -Код функции с обработанными ошибками +Here is the modified function with error handling: ```js function getRepos(username, done) { - if (!username) return done(new Error("Необходимо указать имя пользователя")); + if (!username) return done(new Error("Username is required")); const option = { hostname: "api.github.com", path: `/users/${username}/repos`, @@ -235,19 +247,19 @@ function getRepos(username, done) { const result = JSON.parse(body); done(null, result); } catch (error) { - done(new Error("Не удалось обработать данные")); + done(new Error("Failed to process data")); } }); } else { done( new Error( - `Ошибка при работе с сервером ${res.statusCode} ${res.statusMessage}`, + `Error working with the server ${res.statusCode} ${res.statusMessage}`, ), ); } }); - req.on("error", (error) => done(new Error("Не удалось отправить запрос"))); + req.on("error", (error) => done(new Error("Failed to send request"))); } ``` -Код приложения https://github.com/irinainina/node.js/tree/github.app/github-app +Application code: [GitHub App](https://github.com/irinainina/node.js/tree/github.app/github-app) diff --git a/stage1/modules/node-materials/node/projects/notes.md b/stage1/modules/node-materials/node/projects/notes.md index 67536f7bf..89d673099 100644 --- a/stage1/modules/node-materials/node/projects/notes.md +++ b/stage1/modules/node-materials/node/projects/notes.md @@ -1,34 +1,34 @@ -## Консольное приложение Notes +## Notes App [HOME](../../README.md) -Напишем простое консольное приложение Notes для работы с заметками. -У приложения необходимо реализовать четыре метода +Let's write a simple console application called Notes for working with notes. The application needs to implement four methods: - `create` - `list` - `view` - `remove` -Метод `create` создаёт новую заметку в файле `notes.json`. У метода `create` два аргумента - название заметки и её содержание. -Метод `list` выводит список заметок. -Метод `view` выводит в консоль содержимое заметки, название которой передано в качестве аргумента. -Метод `remove` удаляет заметку, название которой передано в качестве аргумента. -Для вызова методов они указываются в качестве аргументов командной строки. +The `create` method creates a new note in the notes.json file. The `create` method has two arguments: the note's title and its content. +The `list` method displays a list of notes. +The `view` method outputs the content of a note whose title is passed as an argument. +The `remove` method deletes a note whose title is passed as an argument. -1. Импортируем модуль `fs` для работы с файлами +To call these methods, they are specified as command-line arguments. + +1. Import the `fs` module for file operations: ```js const fs = require("fs"); ``` -2. Передадим в константы три аргумента - название метода, название заметки, содержание заметки +2. Assign three arguments to constants — the method name, the note title, and the note content: ```js const [command, title, content] = process.argv.slice(2); ``` -3. Создадим переключатель `switch`, который будет вызывать соответствующие функции для разных методов. +3. Create a `switch` statement that calls the corresponding functions for different methods: ```js switch (command) { @@ -45,12 +45,11 @@ switch (command) { remove(title); break; default: - console.log("Неизвестная команда"); + console.log("Unknown command"); } ``` -4. Напишем функцию `create()` которая создаст новую заметку. - У функции два параметра - название заметки `title` и содержание заметки `content` +4. Write the `create()` function that creates a new note. The function has two parameters: the title of the note `title` and the content of the note `content`: ```js function create(title, content) { @@ -58,20 +57,25 @@ function create(title, content) { const json = JSON.stringify(notes); fs.writeFile("notes.json", json, (error) => { if (error) return console.error(error.message); - console.log("Заметка создана"); + console.log("Note created"); }); } ``` -Функция работает, но она не добавляет данные, а заменяет их. +The function works, but it doesn't add data; instead, it replaces them. -То есть нам нужно: +So, we need to: - прочитать уже имеющиеся данные из файла `'notes.json'` при помощи метода `fs.readFile()` - преобразовать полученные данные в массив при помощи метода `JSON.parse()` - дополнить массив новыми данными при помощи метода `.push()` - преобразовать массив в JSON при помощи метода `JSON.stringify()` - записать данные в файл `'notes.json'` при помощи метода `fs.writeFile()` +- Read the existing data from the `notes.json` file using the `fs.readFile()` method +- Convert the received data into an array using the `JSON.parse()` method +- Add new data to the array using the `.push()` method +- Convert the array to JSON using the `JSON.stringify()` method +- Write the data to the `notes.json` file using the `fs.writeFile()` method ```js function create(title, content) { @@ -83,25 +87,25 @@ function create(title, content) { fs.writeFile("notes.json", json, (error) => { if (error) return console.error(error.message); - console.log("Заметка создана"); + console.log("Note created"); }); }); } ``` -Запустим файл командой +Run the file with the command: ```powershell node index create title content ``` -где вместо `title` и `content` - название и содержание заметки. +where `title` and `content` are the title and content of the note. -Обратите внимание, если бы мы записывали данные в текстовом формате, дополнить файл новыми данными можно было бы значительно проще, использовав вместо метода `fs.writeFile()` метод `fs.appendFile()`. +Note that if we were writing data in text format, it would be much easier to append new data using the `fs.appendFile()` method instead of the `fs.writeFile()` method. -Также необходимо написать функцию `init()`, которая проверяет наличие файла `'notes.json'` и, если такого файла нет, создаёт его с содержимым `[]`. С этим постарайтесь справиться самостоятельно. +Also, it is necessary to write a function `init()` that checks the existence of the `notes.json` file and, if the file does not exist, creates it with the content `[]`. Try to handle this on your own. -5. Напишем функцию `list()`, которая читает документ `notes.json` и выводит названия заметок +5. Write the `list()` function, which reads the `notes.json` document and outputs the titles of the notes: ```js function list() { @@ -113,11 +117,13 @@ function list() { } ``` -Так как файл `notes.json` лежит в той же директории, что и файл с кодом, можно не прописывать к нему путь, достаточно указать его название первым аргументов метода `readFile()`. Второй аргумент метода - функция обратного вызова, которая принимает два параметра - ошибку `error` и прочитанные из файла данные `data`. -Преобразовать полученные данные в массив позволяет метод `JSON.parse(data)`. -Идём по этому массиву при помощи метода `forEach()` и для каждого элемента выводим в консоль его индекс и название заметки, так что в консоль выводится нумерованный список. Чтобы список начинался не с нуля, а с единицы, к индексу прибавим 1. +Since the `notes.json` file is in the same directory as the code file, there is no need to specify the path; it's enough to provide its name as the first argument to the `readFile()` method. The second argument of the method is a callback function that takes two parameters - an error (`error`) and the data read from the file (`data`). + +Converting the received data into an array is done using the `JSON.parse(data)` method. We iterate through this array using the `forEach()` method and, for each element, print its index and the title of the note to the console, creating a numbered list. To start the list from one instead of zero, we add 1 to the index. -6. Напишем функцию `view()`, которая выводит содержание заметки по её названию. Функция похожа на функцию `list()`. В ней также происходит чтение документа и преобразование полученных данных в массив. Затем мы используем метод `find()`, чтобы найти заметку, название которой совпадает с указанным при вызове функции. Если не таких нет, выводим сообщение, что заметка не найдена, иначе выводим её содержание. +6. Write the `view()` function, which outputs the content of a note by its title + +The function is similar to the `list()` function. It reads the document, converts the received data into an array, and then uses the `find()` method to find the note whose title matches the one specified when calling the function. If there is no such note, it prints a message that the note is not found; otherwise, it prints its content: ```js function view(title) { @@ -126,7 +132,7 @@ function view(title) { const notes = JSON.parse(data); const note = notes.find((note) => note.title === title); if (!note) { - console.log("Заметка не найдена"); + console.log("Note not found"); return; } else { console.log(note.content); @@ -135,8 +141,9 @@ function view(title) { } ``` -7. Напишем функцию `remove()`, которая удаляет заметку по её названию. - Точно так же как в предыдущих функциях мы прочитали файл, преобразовали полученные данные в массив, затем, используя метод `filter()` отфильтровали данные, оставив в массиве только те, заголовки которых не совпадают с переданным, преобразовали массив в JSON и записали его в файл. +7. Write the `remove()` function, which deletes a note by its title + +Just like in the previous functions, we read the file, convert the received data into an array, then use the `filter()` method to filter the data, leaving only those whose titles do not match the passed title. After that, we convert the array to JSON and write it to the file: ```js function remove(title) { @@ -147,12 +154,12 @@ function remove(title) { const json = JSON.stringify(notes); fs.writeFile("notes.json", json, (error) => { if (error) return console.error(error.message); - console.log("Заметка удалена"); + console.log("Note deleted"); }); }); } ``` -Код приложения https://github.com/irinainina/node.js/tree/notes/notes +Application code: [Notes App](https://github.com/irinainina/node.js/tree/notes/notes) -P.S. Код данного приложения очень хорошо выглядел бы в виде класса с соответствующими методами. Вы можете потренироваться и сделать это самостоятельно. +P.S. The code of this application would look very good as a class with corresponding methods. You can practice and do it yourself. diff --git a/stage1/modules/node-materials/node/projects/projects.md b/stage1/modules/node-materials/node/projects/projects.md index 16f6a8612..edcce1409 100644 --- a/stage1/modules/node-materials/node/projects/projects.md +++ b/stage1/modules/node-materials/node/projects/projects.md @@ -1,9 +1,9 @@ -## Проекты +## Projects [HOME](../../README.md) ![](../images/projects.png) -- [Приложение для заметок](notes.md) -- [Приложение Таймер](timer.md) -- [Приложение Github](github-app.md) +- [Notes App](notes.md) +- [Timer App](timer.md) +- [Github App](github-app.md) diff --git a/stage1/modules/node-materials/node/projects/timer.md b/stage1/modules/node-materials/node/projects/timer.md index f84378b19..1f24b222c 100644 --- a/stage1/modules/node-materials/node/projects/timer.md +++ b/stage1/modules/node-materials/node/projects/timer.md @@ -1,10 +1,10 @@ -## Приложение Таймер +## Timer App [HOME](../../README.md) -Непосредственное создание объектов на основе класса `EventEmitter` используется крайне редко. -Чаще всего интерфейс для работы с событиями добавляют другим объектам. Это делается благодаря наследованию. -Создадим класс `Timer`, который будет наследовать от `EventEmitter`, и, как следствие, его экземпляры будут иметь методы `emit()` и `on()` +Directly creating objects based on the `EventEmitter` class is extremely rare. +More often, the interface for working with events is added to other objects. This is done through inheritance. +Let's create a class called `Timer` that will inherit from `EventEmitter`, and, as a result, its instances will have the `emit()` and `on()` methods: ```js const EventEmitter = require("events"); @@ -38,31 +38,31 @@ class Timer extends EventEmitter { } ``` -Конструктор класса `Timer` принимает 2 аргумента: +The constructor of the `Timer` class takes 2 arguments: -- `total` — общее количество срабатываний таймера -- `interval` — интервал между срабатываниями +- `total` — the total number of timer triggers +- `interval` — the interval between triggers -Экземпляр класса `Timer` содержит три метода: +An instance of the `Timer` class has three methods: -- `start()` — запускает наш таймер -- `_tick()` — внутренный метод таймера -- `end()` — завершает работу таймера +- `start()` — starts the timer +- `_tick()` — an internal method of the timer +- `end()` — stops the timer -У метода `start()` есть переменная `intervalId`, содержащая идентификатор таймера, установленного при помощи `setInterval()`, который каждую секунду вызывает метод `tick()`. Также метод `start()` генерирует событие `'start'`. -Метод `tick()` увеличивает переменную `ticks` на единицу и проверяет, не достигнуто ли значение, указанное в свойстве конструктора `total`. -Если не достигнуто, метод `tick()` генерирует событие `tick`, payload которого выступает текущее значение переменной `ticks`. -Если значение `total` достигнуто, метод `tick()` вызывает метод `end()`. -Метод `end()` очищает таймер `setInterval()` и генерирует событие `end`. +The `start()` method has a variable `intervalId` containing the timer identifier set by `setInterval()`, which calls the `tick()` method every second. Also, the `start()` method emits a `start` event. +The `tick()` method increments the ticks variable by one and checks if the value specified in the constructor's property `total` has been reached. +If it hasn't been reached, the `tick()` method emits a `tick` event, with the current value of the `ticks` variable as the payload. +If the `total` value is reached, the `tick()` method calls the `end()` method. +The `end()` method clears the `setInterval()` timer and emits an `end` event. -Создадим экземпляр класса `Timer` +Let's create an instance of the `Timer` class: ```js -// таймер на 10 срабатываний с интервалом 500 мс +// a timer for 10 triggers with an interval of 500 ms const timer = new Timer(10, 500); ``` -Подпишемся на его события +Let's subscribe to its events: ```js timer.once("start", () => console.log("Start")); @@ -70,12 +70,12 @@ timer.on("tick", (tick) => console.log(tick)); timer.once("end", () => console.log("End")); ``` -Запустим таймер +Let's start the timer: ```js timer.start(); ``` -Таймер работает. +The timer is running. -Работа большинства объектов в Node.js основана на событиях, работа с которыми происходит за счет того, что эти объекты наследуют API `EventEmitter`. +The operation of most objects in Node.js is based on events, and they work with events because these objects inherit the `EventEmitter` API. From 55b198a45e15e951ad3a6d9d5f077092ea0f28e9 Mon Sep 17 00:00:00 2001 From: Lizaveta Date: Fri, 17 Nov 2023 08:03:12 +0200 Subject: [PATCH 4/9] feat: translate files with nodejs theory --- stage1/modules/node-materials/README.md | 8 +- stage1/modules/node-materials/node/events.md | 66 ++++++----- .../modules/node-materials/node/node-argv.md | 72 ++++++------ .../node-materials/node/node-fs-access.md | 37 +++--- .../node-materials/node/node-introduction.md | 71 ++++++------ stage1/modules/node-materials/node/node-io.md | 44 +++---- .../node-materials/node/node-module.md | 107 ++++++++++-------- .../modules/node-materials/node/node-stdio.md | 88 +++++++------- .../node-materials/node/stream-pipes.md | 23 ++-- .../node-materials/node/stream-readable.md | 30 +++-- .../node-materials/node/stream-writable.md | 14 +-- stage1/modules/node-materials/node/stream.md | 24 ++-- 12 files changed, 318 insertions(+), 266 deletions(-) diff --git a/stage1/modules/node-materials/README.md b/stage1/modules/node-materials/README.md index dfad2d8db..72bbdb61f 100644 --- a/stage1/modules/node-materials/README.md +++ b/stage1/modules/node-materials/README.md @@ -16,7 +16,7 @@ Node.js has its drawbacks, particularly in scenarios demanding intensive calcula ## Table of Contents -1. [Getting started](node/node-introduction.md) +1. [Getting Started](node/node-introduction.md) 2. [Input/Output Operations](node/node-io.md) 3. [Standard Input/Output Streams](node/node-stdio.md) 4. [Command Line Arguments](node/node-argv.md) @@ -30,9 +30,9 @@ Node.js has its drawbacks, particularly in scenarios demanding intensive calcula - [Creating Custom Modules](node/module/create-module.md) 7. [Events](node/events.md) 8. [Streams](node/stream.md) - - [Read Stream](node/stream-readable.md) - - [Write Stream](node/stream-writable.md) - - [Combining Streams](node/stream-pipes.md) + - [Readable Stream](node/stream-readable.md) + - [Writable Stream](node/stream-writable.md) + - [Combining Readable-Writable Streams](node/stream-pipes.md) 9. [Projects](node/projects/projects.md) - [Notes App](node/projects/notes.md) - [Timer App](node/projects/timer.md) diff --git a/stage1/modules/node-materials/node/events.md b/stage1/modules/node-materials/node/events.md index aa3d6da34..0210a3b1f 100644 --- a/stage1/modules/node-materials/node/events.md +++ b/stage1/modules/node-materials/node/events.md @@ -1,47 +1,47 @@ -## Модуль events +## Events [HOME](../README.md) -Модуль `Events` предназначен для работы с событиями. +The `Events` module is designed to work with events. -С событиями мы уже сталкивались раньше, когда читали файл +We have already encountered events earlier when reading a file: ```js const { stdin, stdout } = process; stdin.on("data", (data) => stdout.write(data)); ``` -и когда выводили сообщение при завершении работы программы +and when displaying a message upon the program's completion: ```js const { stdout } = process; -process.on("exit", () => stdout.write("Удачи в изучении Node.js!")); +process.on("exit", () => stdout.write("Good luck learning Node.js!")); ``` -Модуль `Events` позволяет создавать и генерировать свои собственные события. +The `Events` module allows you to create and generate your own events. -Импортируем класс `EventEmitter` из модуля `events` +Let's import the `EventEmitter` class from the `events` module: ```js const EventEmitter = require("events"); ``` -Создадим экземпляр `EventEmitter` — объект `emitter` +Now, let's create an instance of `EventEmitter` called `emitter`: ```js const EventEmitter = require("events"); const emitter = new EventEmitter(); ``` -У него есть два полезных метода: +It has two useful methods: -- `emit()` - генерирует событие `event`, заставляя срабатывать обработчики этого события у подписчиков -- `on(, )` - подписка на события (выполнение функции `handler` действий при наступлении события `event` +- `emit()` - generates the event, causing the event handlers for this event to be triggered for subscribers +- `on(, )` - subscribes to events (executes the `handler` function when the `event` occurs) -У метода `on(, )` два аргумента: +The `on(, )` method has two arguments: -- `event` - название события. -- `handler` - обработчик события (функция, которая сработает, когда событие произойдёт) +- `event` - the name of the event +- `handler` - the event handler (the function that will run when the event occurs) ```js const EventEmitter = require("events"); @@ -50,8 +50,7 @@ const emitter = new EventEmitter(); emitter.on("start", () => console.log("Start")); ``` -Мы подписались на событие `'start'` для объекта `emitter`. Теперь нам нужно сгенерировать это событие, чтобы сработал его обработчик -Для этого вызываем метод `emit()`, аргументом которого указываем название события +We subscribed to the `start` event for the `emitter` object. Now we need to generate this event to trigger its handler. To do this, we call the `emit()` method, specifying the event name as an argument: ```js const EventEmitter = require("events"); @@ -62,10 +61,9 @@ emitter.on("start", () => console.log("Start")); emitter.emit("start"); ``` -Запускаем файл с кодом, в консоли видим надпись 'Start'. -Самостоятельно созданное событие работает. +When we run the code file, we see the 'Start' message in the console. The custom event works. -При вызове события в методе `emit()` можно передать какое-то дополнительное значение (`payload`). Это значение будет передано в качестве аргумента в функцию-обработчик +When calling the `emit()` method for an event, you can pass some additional value (`payload`). This value will be passed as an argument to the handler function: ```js const EventEmitter = require("events"); @@ -76,7 +74,7 @@ emitter.on("start", (message) => console.log(message)); emitter.emit("start", "Start message"); ``` -Таких значение можно передать несколько +You can pass multiple values as well: ```js const EventEmitter = require("events"); @@ -87,7 +85,7 @@ emitter.on("start", (first, second) => console.log(`${first} and ${second}`)); emitter.emit("start", 1, 2); // 1 and 2 ``` -При подписке на событие его обработчик ставится в очередь обработчиков. Одному и тому же событию можно назначить несколько обработчиков (по умолчанию **не больше 10**, но это не жесткий лимит). Обработчики срабатывают в том порядке, в котором они были назначены: +When subscribing to an event, its handler is added to the handler queue. Multiple handlers (by default, **no more than 10**, but this is not a strict limit) can be assigned to the same event. Handlers are triggered in the order they were assigned: ```js const EventEmitter = require("events"); @@ -99,10 +97,10 @@ const handler2 = () => console.log(2); emitter.on("start", handler1); emitter.on("start", handler2); -emitter.emit("start"); // выводит 1, затем 2 +emitter.emit("start"); // outputs 1, then 2 ``` -Поставить назначенный позже обработчик в начало очереди нам поможет метод `prependListener` +To place a handler assigned later at the beginning of the queue, the `prependListener` method is used: ```js const EventEmitter = require("events"); @@ -116,12 +114,12 @@ const handler4 = () => console.log(4); emitter.on("start", handler1); emitter.on("start", handler2); emitter.on("start", handler3); -emitter.prependListener("start", handler4); // назначет позже, сработает раньше +emitter.prependListener("start", handler4); // assigned later, triggered earlier -emitter.emit("start"); // выведет цифры в следующем порядке: 4 => 1 => 2 => 3 +emitter.emit("start"); // outputs numbers in the following order: 4 => 1 => 2 => 3 ``` -Один и тот же обработчик может быть назначен несколько раз: +The same handler can be assigned multiple times: ```js const EventEmitter = require("events"); @@ -133,10 +131,10 @@ emitter.on("start", handler); emitter.on("start", handler); emitter.on("start", handler); -emitter.emit("start"); // выводит 1 трижды +emitter.emit("start"); // outputs 1 three times ``` -Обработчик срабатывает на каждую генерацию события: +The handler is triggered for each event generation: ```js const EventEmitter = require("events"); @@ -149,7 +147,7 @@ emitter.emit("start", "from"); // from emitter.emit("start", "Node.js"); // Node.js ``` -Если необходимо, чтобы обработчик срабатывал только один раз, для подписки используем метод `once()` +If you want the handler to be triggered only once, use the `once()` method for subscription: ```js const EventEmitter = require("events"); @@ -157,12 +155,12 @@ const emitter = new EventEmitter(); emitter.once("start", (message) => console.log(message)); -emitter.emit("start", "Hello"); // сработает только для этого вызова +emitter.emit("start", "Hello"); // works only for this call emitter.emit("start", "from"); emitter.emit("start", "Node.js"); ``` -Удалить из очереди одну функцию-обработчик определенного события позволяет метод экземпляра `EventEmitter` `off()` (или его алиас `removeListener`) +To remove a specific event handler function from the queue, the `off()` method of the `EventEmitter` instance (or its alias `removeListener`) is used: ```js const EventEmitter = require("events"); @@ -174,13 +172,13 @@ emitter.on("start", handler); emitter.emit("start", "Hello"); // Hello -emitter.off("start", handler); // дальнейшие события не будут обработаны +emitter.off("start", handler); // further events will not be handled emitter.emit("start", "from"); emitter.emit("start", "Node.js"); ``` -Иногда мы хотим, чтобы наш собственный класс имел API `EventEmitter`: +Sometimes, we want our custom class to have the `EventEmitter` API: ```js const EventEmitter = require("events"); @@ -204,4 +202,4 @@ user.on("greetings", user.sayHi); user.emit("greetings"); // Hi! My name is Vasya ``` -Заметили странность? Как правило, в случае передачи метода объекта для использования в качестве обработчика происходит потеря контекста. Но не в этом случае, так как `this` внутри функции-обработчика ссылается на экземпляр `EventEmitter` (в нашем случае объект `user`). +Notice anything strange? Usually, when passing an object's method for use as an event handler, the context is lost. But not in this case because `this` inside the event handler function refers to the `EventEmitter` instance (in our case, the `user` object). diff --git a/stage1/modules/node-materials/node/node-argv.md b/stage1/modules/node-materials/node/node-argv.md index 3396a09fd..c81016463 100644 --- a/stage1/modules/node-materials/node/node-argv.md +++ b/stage1/modules/node-materials/node/node-argv.md @@ -1,38 +1,42 @@ -## Аргументы командной строки +## Command Line Arguments [HOME](../README.md) -В Node.js есть возможность запустить файл с определёнными аргументами командной строки. При запуске файла аргументы передаются после его имени. Например, при запуске +In Node.js, you can run a file with specific command line arguments. When running a file, the arguments are passed after its name. For example, when running: ```powershell node test 1 2 3 ``` -`1`, `2`, `3` - это аргументы. -Как внутри кода получить доступ к переданным при запуске файла аргументам? Для этого используется свойство глобального объекта `process` - `process.argv` +`1`, `2`, `3` are the arguments. +How can you access the arguments passed when the file is launched within the code? This is done using the `process.argv` property of the global `process` object. -В файле `test.js` напишем код -`console.log(process.argv);` -В терминале выполним команду `node test 1 2 3`. -В консоли отображается массив, первые два элемента которого - путь к файлу node.exe и путь к запущенному файлу. Дальше идут переданные аргументы. +In the `test.js` file, let's write the code: -Если нужно получить только аргументы, выполним код +```js +console.log(process.argv); +``` + +In the terminal, execute the command `node test 1 2 3`. +In the console, an array is displayed, the first two elements of which are the path to the node.exe file and the path to the executed file. After that come the passed arguments. + +If you only need to get the arguments, execute the code: ```js console.log(process.argv.slice(2)); ``` -Метод `process.argv.slice(2)` возвращает новый массив, который начинается с элемента с индексом "2". +The `process.argv.slice(2)` method returns a new array that starts from the element at index "2". -### Флаги +### Flags -Чтобы иметь возможность отправлять аргументы в любом порядке или пропускать какие-то из них, аргументы командной строки можно пометить. Для этого используются флаги - слова или символы, которые указывают, что за ними следует аргумент командной строки. Перед флагами, как правило, ставят один или два дефиса, чтобы не перепутать их с аргументами. Например, +To have the ability to send arguments in any order or skip some of them, you can mark command line arguments. For this purpose, flags are used. Flags are words or symbols indicating that a command line argument follows them. Flags are usually preceded by one or two dashes to avoid confusion with arguments. For example: ```powershell node test -m Hello ``` -Чтобы получить аргумент с указанным флагом, напишем код +To get the argument with the specified flag, write the code: ```js const flagIndex = process.argv.indexOf("-m"); @@ -42,7 +46,7 @@ if (flagIndex !== -1) { } ``` -Можно этот код преобразовать в функцию, получающую флаг аргумента и возвращающую его значение +You can transform this code into a function that takes the flag argument and returns its value: ```js function getValue(flag) { @@ -53,14 +57,14 @@ const message = getValue("-m"); console.log(message); ``` -### Практическое применение +### Practical Usage -На практике в случае, если вы пишете код для работы с аргументами командной строки самостоятельно, необходимо корректно обработать всевозможные ситуации — аргумент может отсутствовать, флаг может быть не передан, либо передан без значения, либо само наличие флага является булевым значением и т.д. -Для повышения удобства работы с аргументами командной строки, а также минимизации вероятности возникновения ошибок удобно использовать готовые решения, такие как [minimist](https://www.npmjs.com/package/minimist), [commander](https://www.npmjs.com/package/commander), [yargs](https://www.npmjs.com/package/yargs) и другие. +In practice, when you write code to handle command line arguments yourself, it is necessary to handle various situations correctly — an argument may be missing, a flag may not be passed, or it may be passed without a value. The mere presence of a flag can also be a boolean value, and so on. +To make working with command line arguments more convenient and minimize the likelihood of errors, it is useful to use ready-made solutions such as [minimist](https://www.npmjs.com/package/minimist), [commander](https://www.npmjs.com/package/commander), [yargs](https://www.npmjs.com/package/yargs), and others. -### CLI options +### CLI Options -Помимо пользовательских аргументов командной строки, можно передавать опции командной строки (CLI options). Опции командной строки передаются **до** пути к запускаемому файлу и модифицируют поведение Node.js. Например, можно отключить использование свойства `__proto__`. Так, запуск файла со следующим содержимым: +In addition to custom command line arguments, you can pass command line options (CLI options). Command line options are passed **before** the path to the executed file and modify the behavior of Node.js. For example, you can disable the use of the `__proto__` property. Thus, running a file with the following content: ```js // test.js @@ -74,29 +78,31 @@ obj.__proto__ = protoObj; obj.sayHi(); ``` -с опцией `--disable-proto=throw` +with the option `--disable-proto=throw`: ```powershell node --disable-proto=throw test ``` -приведет к ошибке. Полный список опций можно посмотреть в [документации](https://nodejs.org/dist/latest-v14.x/docs/api/cli.html#cli_options) +will result in an error. You can view the full list of options in the [documentation](https://nodejs.org/dist/latest-v14.x/docs/api/cli.html#cli_options). + +### Environment Variables -### Переменные окружения +Sometimes we need to pass some value from the **outside** to our code, which will be used by our application. For example, we want to implement different behavior when the application is launched on a production server and during development. Environment variables can help us in this case. -Иногда нам нужно **снаружи** передать в код некое значение, которое будет использоваться нашим приложением. Например, мы хотим реализовать различное поведение приложения при запуске на "боевом" сервере и в процессе разработки. В этом нам могут помочь **переменные окружения**. Переменные окружения имеют синтаксис вида `variable_name=variable_value` и размещаются перед `node ...` Вот так при использовании терминала Bash можно передать переменную, которая будет показывать, в каком режиме сейчас запущено приложение: +Environment variables have a syntax like `variable_name=variable_value` and are placed before `node ....`. In Bash, for example, you can pass a variable that shows in which mode the application is currently running: ```bash PRODUCTION=false node test ``` -А так при использовании терминала Powershell +Or in PowerShell: ```powershell $env:PRODUCTION="false"; node test ``` -Доступ к таким переменным внутри кода можно получить через `process.env`: +Access to such variables inside the code can be obtained through `process.env`: ```js const productionMode = process.env.PRODUCTION === "true"; @@ -109,36 +115,36 @@ if (productionMode) { } ``` -### Задание +### Task -Напишите программу, которая просит у пользователя ввести два числа, складывает эти числа, если запускается с флагом `-s`, или перемножает, если запускается с флагом `-m`, после чего завершает свою работу. Для ввода и вывода информации используйте стандартные потоки ввода/вывода. Пример работы (пользовательский ввод начинается с `>`) +Write a program that prompts the user to enter two numbers, adds these numbers if launched with the `-s` flag, or multiplies them if launched with the `-m` flag, and then terminates. Use standard input/output for input and output. Here is an example of how it should work (user input starts with `>`): ```powershell > node test.js -m -Введите 2 числа +Enter 2 numbers > 2 7 2 * 7 = 14 ``` ```powershell > node test.js -s -Введите 2 числа +Enter 2 numbers > 2 7 2 + 7 = 9 ```
-Пример решения +Example Solution ```js const { stdout, stdin, exit } = process; const flag = process.argv[2]; const allowedFlags = ["-m", "-s"]; if (!allowedFlags.includes(flag)) { - stdout.write("Попробуйте ещё раз запустить файл с флагом -s или -m"); + stdout.write("Try running the file again with the -s or -m flag"); exit(); } -stdout.write("Введите, пожалуйста, два числа\n"); +stdout.write("Please enter two numbers\n"); stdin.on("data", (data) => { const numString = data.toString(); const numStringsArray = numString.split(" "); @@ -147,7 +153,7 @@ stdin.on("data", (data) => { Number.isNaN(+numStr), ); if (hasIncorrectLength || hasIncorrectValues) { - stdout.write("Нужно ввести 2 числа, разделенных пробелом"); + stdout.write("You need to enter 2 numbers separated by a space"); exit(); } const [firstNum, secondNum] = numStringsArray.map((numStr) => +numStr); diff --git a/stage1/modules/node-materials/node/node-fs-access.md b/stage1/modules/node-materials/node/node-fs-access.md index 9810d5797..ab2ae0997 100644 --- a/stage1/modules/node-materials/node/node-fs-access.md +++ b/stage1/modules/node-materials/node/node-fs-access.md @@ -1,28 +1,37 @@ -## Доступ к файловой системе +## File System Access [HOME](../README.md) -В отличие от браузерного JavaScript, у Node.js есть доступ к файловой системе. -Например, мы легко можем узнать абсолютный путь к директории, в которой находится наш файл. Для этого откроем файл `test.js` и напишем в нём код -`console.log(__dirname);` -Откроем терминал и запустим файл +Unlike browser-based JavaScript, Node.js has access to the file system. +For example, we can easily determine the absolute path to the directory containing our file. To do this, open the `test.js` file and write the code: + +```js +console.log(__dirname); +``` + +Open the terminal and run the file: ```powershell node test ``` -В консоль выведется абсолютный путь к директории с файлом `test.js`. -Теперь выведем абсолютный путь к файлу. Для этого в файле `test.js` добавим строку -`console.log(__filename);` -Теперь в консоль выводится абсолютный путь к файлу `test.js` вместе с его именем. -_Чтобы в терминале повторно появилась последняя введенная команда, достаточно нажать клавишу "↑" на клавиатуре._ +The console will output the absolute path to the directory with the `test.js` file. +Now let's output the absolute path to the file. To do this, add the line in the `test.js` file: + +```js +console.log(__filename); +``` + +Now the console displays the absolute path to the `test.js` file along with its name. + +_To repeat the last entered command in the terminal, just press the "↑" key on the keyboard._ -### Задание +### Task -Напишите программу, которая возвращает путь к папке, если запускается с флагом '-d', или путь к файлу, если запускается с флагом '-f'. Если файл запускается без флага или с флагом, отличным от указанных в задании, выводится предложение запустить программу с флагом '-d' или '-f'. +Write a program that returns the path to the folder if it is launched with the `-d` flag, or the path to the file if it is launched with the `-f` flag. If the file is launched without a flag or with a flag other than those specified in the task, a suggestion to run the program with the `-d` or `-f` flag is displayed.
- Пример решения + Example Solution ```js const { stdout } = process; @@ -33,7 +42,7 @@ if (flag === "-d") { } else if (flag === "-f") { stdout.write(__filename); } else { - stdout.write("Пожалуйста, запустите программу с флагом -d или -f"); + stdout.write("Please run the program with the -d or -f flag"); } ``` diff --git a/stage1/modules/node-materials/node/node-introduction.md b/stage1/modules/node-materials/node/node-introduction.md index b71cf6bf2..48abca509 100644 --- a/stage1/modules/node-materials/node/node-introduction.md +++ b/stage1/modules/node-materials/node/node-introduction.md @@ -1,70 +1,75 @@ -## Начало работы +## Getting Started [HOME](../README.md) -### Устанавливаем Node.js +### Installing Node.js -Ссылка для скачивания [https://nodejs.org/en/](https://nodejs.org/en/) +Download link https://nodejs.org/en -Скачиваем и устанавливаем последнюю LTS версию (Recommended For Most Users) +Download and install the latest LTS version (Recommended For Most Users) -Проверяем что именно установили. Для этого проверяем версию Node.js. -Открываем Git Bash: клик правой кнопкой по рабочему столу, в контекстном меню выбираем Git Bash Here. Если такого пункта нет, скачайте и установите Git [https://git-scm.com/downloads](https://git-scm.com/downloads). -Выполняем команду `node -v` -Если отображается версия Node.js, значит, с первым пунктом мы справились и Node.js установили. +Check what you have installed. To do this, check the version of Node.js. +Open Git Bash: right-click on the desktop, choose `Git Bash Here` from the context menu. If this option is not available, download and install Git https://git-scm.com/downloads. -### Возможные проблемы при установке +Execute the command `node -v`. +If the Node.js version is displayed, then we have successfully completed the first step, and Node.js is installed. -Иногда после установки Node.js команды в терминале, начинающиеся с `node`, вызывают ошибки наподобие `'node' is not recognized as an internal or external command, operable program or batch file`. В этом случае нужно добавить корректный путь к директории с Node.js в `PATH`. [Пример решения](https://love2dev.com/blog/node-is-not-recognized-as-an-internal-or-external-command/). +### Possible Installation Issues -### Где писать код +Sometimes, after installing Node.js, commands in the terminal starting with `node` may cause errors like `'node' is not recognized as an internal or external command, operable program or batch file`. +In this case, you need to add the correct path to the Node.js directory to the `PATH`. +[Example solution](https://love2dev.com/blog/node-is-not-recognized-as-an-internal-or-external-command/). -В терминале писать не очень удобно. Как и в блокноте. Выбираем привычный VS Code, открываем терминал (вкладка `Терминал` на панели вверху, пункт `Создать терминал`), проверяем, что терминал работает. Для этого выполняем команду `node -v`. +### Where to Write Code -### Режим REPL +Neither writing code in the terminal, nor is Notepad is very convenient. Let's choose VS Code, open the terminal (tab `Terminal` on the top panel, item `Create Terminal`), check that the terminal is working. To do this, execute the command `node -v`. -Код можно писать и выполнять прямо в терминале. -Такой режим называется REPL (от англ. `Read-Eval-Print-Loop` — цикл `чтение — вычисление — вывод` -Чтобы в него перейти, выполните в терминале команду `node` -Теперь код можно писать непосредственно в терминале, REPL вычислит введенное выражение и выведет результат. К примеру, если ввести `2 + 2` и нажать Enter, REPL выведет `4`. -Также можно делать явный вывод в консоль при помощи уже знакомых нам методов. Так, вы можете написать +### REPL Mode + +You can write and execute code directly in the terminal. +This mode is called `REPL` (Read-Eval-Print-Loop). + +To enter it, execute the command `node` in the terminal. +Now you can write code directly in the terminal, and REPL will evaluate the entered expression and display the result. For example, if you enter `2 + 2` and press Enter, REPL will output `4`. + +You can also make explicit console output using the familiar methods. For example, you can write: ```js console.log("Hello, world!"); ``` -REPL имеет некоторые полезные команды, получить информацию о которых можно, отправив команду `.help` -Чтобы выйти из режима REPL, отправьте команду `.exit`(также для более грубого завершения процесса можно применить стандартное для используемого терминала сочетание клавиш наподобие `Ctrl + C`). -Очистить терминал позволят такие команды, как `cls` (для стандартной командной строки Windows и Powershell), `clear` (для Bash). +REPL has some useful commands, information about which can be obtained by sending the `.help` command. +To exit REPL mode, send the `.exit` command (also, for a more forceful process termination, you can use the standard key combination for the terminal used, such as `Ctrl + C`). +Clear the terminal using commands like `cls` (for the standard Windows Command Prompt and PowerShell) or `clear` (for Bash). + +### How to Run a File -### Как запустить файл +The REPL mode is used relatively rarely. Typically, Node.js is used to run code located in files. -Режим REPL используется достаточно редко. -Как правило, при помощи Node.js запускают код, размещенный в файлах. -Создадим файл `test.js` и напишем в нём команду +Let's create a file `test.js` and write the command in it: ```js console.log("Hello, world!"); ``` -Откроем этот файл при помощи VS Code, в терминале выполним команду +Open this file with VS Code, in the terminal execute the command: ```powershell node test.js ``` -Обратите внимание: +Note: -- расширение файла можно не указывать, т.е достаточно написать `node test` -- если возникла ошибка, убедитесь, что терминал открыт в той же директории, в которой находится файл `test.js` +- You don't have to specify the file extension, i.e., it is enough to write `node test` +- If an error occurs, make sure the terminal is open in the same directory where the `test.js` file is located -## Задание +## Task -1. Выведите в консоль строку `"Hello, Node.js!"` используя режим REPL -2. Выведите в консоль строку `"Hello, Node.js!"` запустив при помощи Node.js файл с кодом +1. Output the string `Hello, Node.js!` to the console using REPL mode +2. Output the string `Hello, Node.js!` by running a file with Node.js
-Пример решения +Example Solution ```js console.log("Hello, Node.js!"); diff --git a/stage1/modules/node-materials/node/node-io.md b/stage1/modules/node-materials/node/node-io.md index 232abe5ba..bcc3d326d 100644 --- a/stage1/modules/node-materials/node/node-io.md +++ b/stage1/modules/node-materials/node/node-io.md @@ -1,38 +1,38 @@ -## Операции ввода/вывода +## Input/Output Operations [HOME](../README.md) -### I/O (input/output) +### I/O (Input/Output) -I/O (input/output) означает ввод/вывод +I/O stands for input/output. -- input - получение информации от сетевых ресурсов, или чтение с диска или файла, или ввод с клавиатуры -- output - вывод информации, например, сохранение на диск или запись в файл, или вывод в консоль +- `input` - obtaining information from network resources, reading from disk or file, or keyboard input +- `output` - displaying information, such as saving to disk or writing to a file, or output to the console -Это самые затратные по времени этапы работы программы. Сравните: +These are the most time-consuming stages of a program's operation. Compare: ![](../node/images/io.png) -### Блокирующий I/O +### Blocking I/O -Операции input/output происходят синхронно, одна за другой +Input/output operations occur synchronously, one after another: ```js -const connection = db.connect(); // подключаемся к базе данных -const users = connection.query("SELECT * FROM users"); // делаем запрос -console.log(users); // выводим информацию в консоль +const connection = db.connect(); // connect to the database +const users = connection.query("SELECT * FROM users"); // make a query +console.log(users); // display information in the console ``` -Это простой в реализации, но очень затратный по времени вариант: программа ждёт **250 миллионов тактов процессора**, пока не произойдёт подключение к базе данных. -Вторая проблема синхронного I/O - он не отказоустойчив. Если программа не сможет подключиться к базе данных, или если в базе данных не найдётся затребованной информации, программа остановит свою работу. +This is a simple implementation but a very time-consuming one: the program waits for **250 million processor cycles** until a connection to the database occurs. +The second problem with synchronous I/O is that it is not fault-tolerant. If the program cannot connect to the database or if the requested information is not found in the database, the program will stop working. -Синхронный или блокирующий I/O в Node.js используется очень редко: +Synchronous or blocking I/O in Node.js is used very rarely: -- если необходимо получить данные, без которых работа программы не может начаться. Например, информацию о настройках -- может использоваться в консольных приложениях, у которых только один пользователь +- When you need to get data without which the program cannot start. For example, information about settings +- It can be used in console applications with only one user -### Неблокирующий I/O +### Non-blocking I/O -Неблокирующий I/O происходит асинхронно +Non-blocking I/O occurs asynchronously: ```js db.connect((error, connection) => { @@ -43,8 +43,10 @@ db.connect((error, connection) => { }); ``` -В данном примере программа вызывает `db.connect()`, ставит его колбэк в очередь и переходит к выполнению оставшейся синхронной части кода. Когда весь синхронный код выполнен, программа возвращается к выполнению колбэка `db.connect()`. +In this example, the program calls `db.connect()`, puts its callback in the queue, and proceeds to execute the remaining synchronous part of the code. When all synchronous code is executed, the program returns to execute the `db.connect()` callback. -Первым аргументом колбэка, переданного в `db.connect()`, является ошибка. Если ошибка имеется, то в нашем примере происходит "проброс исключения", в результате которого эта ошибка либо будет обработана в коде выше, либо вывалится в рантайм и "повалит" приложение. Если ошибки нет - `error === null` (в логическом контексте `false`) - функция переходит к работе с базой данных: выполняет `connection.query()` и `console.log()`. +The first argument of the callback passed to `db.connect()` is an error. If an error occurs, in our example, an "exception forwarding" occurs, as a result of which this error will either be handled in the code above or will throw in the runtime, causing the application to crash. -В основе работы Node.js лежат **неблокирующий ввод/вывод** и **асинхронность**. Благодаря этому приложения на Node.js работают быстро и могут обрабатывать большое количество клиентских запросов в единицу времени. +If there is no error - `error === null` (in a logical context `false`) - the function proceeds to work with the database: performs `connection.query()` and `console.log()`. + +Node.js is based on **non-blocking I/O** and **asynchrony**. Thanks to this, Node.js applications work quickly and can handle a large number of client requests in a unit of time. diff --git a/stage1/modules/node-materials/node/node-module.md b/stage1/modules/node-materials/node/node-module.md index c35b2a489..5cc18a50f 100644 --- a/stage1/modules/node-materials/node/node-module.md +++ b/stage1/modules/node-materials/node/node-module.md @@ -1,34 +1,41 @@ -## Модули +## Modules [HOME](../README.md) -Node.js любой файл воспринимает как модуль. В Node.js на данный момент используются 2 системы модулей: **CommonJS** (модули, используемые в Node.js по умолчанию, появились раньше) и **ECMAScript модули** (реализуют функционал JS, впервые появившийся в спецификации ECMAScript 2015). Они имеют существенные отличия друг от друга. В рамках данных материалов мы используем только **CommonJS** модули. -Глобальных переменных вроде `__dirname`, `__filename`, `process` в Node.js [не так много](https://nodejs.org/dist/latest-v14.x/docs/api/globals.html). Остальной функционал реализован в виде подключаемых модулей. -Преимущества использования модулей в Node.js: +Node.js treats any file as a module. -- Лучшее структурирование кода — код, разбитый на модули, гораздо легче для понимания, поддержки, тестирования -- Облегчение переиспользования кода — правильно написанный и документированный модуль легко может быть использован в нескольких местах в одном проекте, а также в разных проектах -- Инкапсуляция — содержимое модуля инкапсулировано, т.е. доступно исключительно внутри модуля. Разработчик сам решает, что импортировать в модуль и что экспортировать за пределы модуля -- Благодаря [кэшированию](https://nodejs.org/dist/latest-v14.x/docs/api/modules.html#modules_caching) модулей их многократный импорт не приводит к дополнительным издержкам производительности +Node.js currently uses two module systems: **CommonJS** (modules used by default in Node.js, appeared earlier) and **ECMAScript modules** (implementing functionality from ECMAScript 2015 specification). They have significant differences from each other. In this material, we only use **CommonJS** modules. -Упрощенно, модули в Node.js можно разделить на 3 типа: +There are not many global variables in Node.js, such as `__dirname`, `__filename`, `process` ([check the list here](https://nodejs.org/dist/latest-v14.x/docs/api/globals.html#globals_global_objects)). The rest of the functionality is implemented as importable modules. -1. Стандартные модули (core modules), которые мы получаем "из коробки", устанавливая Node.js на компьютер. - Примеры стандартных модулей: +Advantages of using modules in Node.js: -- [модуль path](module/path.md) -- [модуль fs](module/fs.md) -- [модуль os](module/os.md) +- Better code organization — code divided into modules is much easier to understand, support, and test +- Code reuse facilitation — a correctly written and documented module can easily be used in multiple places within one project and in different projects +- Encapsulation — the contents of a module are encapsulated, i.e., available exclusively within the module. Developers decide what to import into the module and what to export beyond the module +- Thanks to [module caching](https://nodejs.org/dist/latest-v14.x/docs/api/modules.html#modules_caching), multiple imports of modules do not lead to additional performance overhead -2. [Модули-пакеты](module/npm-module.md) -3. [Модули, которые разработчик создаёт самостоятельно](module/create-module.md) +To simplify, modules in Node.js can be divided into three types: -### Стандартные модули +1. Core modules, which we get "out of the box" by installing Node.js on a computer. +Examples of core modules: -Они уже скомпилированы в двоичный код и описаны в документации. [Перечень стандартных модулей](https://nodejs.org/dist/latest-v14.x/docs/api/). Стандартные модули достаточно подключить и можно с ними работать. + - [path module](module/path.md) + - [fs module](module/fs.md) + - [os module](module/os.md) -Для подключения модуля используется функция `require()` -Примеры подключения модулей: +2. [Packages](module/npm-module.md) +3. [Custom modules](module/create-module.md) + +### Core Modules + +They are already compiled into binary code and described in the documentation. +[List of core modules](https://nodejs.org/dist/latest-v14.x/docs/api/) + +Core modules are easy to include, and you can start working with them. + +To include a module, the `require()` function is used. +Examples of module inclusion: ```js const path = require("path"); @@ -36,50 +43,60 @@ const fs = require("fs"); const os = require("os"); ``` -### Модули-пакеты (Packages) +### Packages -К модулям-пакетам относятся папки с кодом, описываемые при помощи находящегося в них файла `package.json`. -С модулями-пакетами удобно работать при помощи менеджеров пакетов, таких как `npm` или `yarn`. Если мы хотим использовать уже написанные кем-то модули-пакеты (частый способ использования кода других разработчиков), их нужно установить, затем подключить, затем использовать. -Установка модуля-пакета при помощи `npm` осуществляется командой +Packages include folders with code described using the `package.json` file inside them. + +Packages are convenient to work with using package managers such as `npm` or `yarn`. If we want to use modules already written by someone (a common way to use other developers' code), we need to install them, then include them, and then use them. + +To install a package using `npm`, use the command: ```powershell -npm install <имя модуля> +npm install ``` -### Модули, которые разработчик создаёт самостоятельно +### Custom modules -Создание модуля начинается с создания отдельного js-файла, в котором пишется код. Если модуль должен экспортировать что-либо наружу, это делается при помощи записи экспортируемого значения в качестве свойства специального объекта `module.exports`, либо его перезаписи. -Экспортируемые из одних модулей значения мы можем импортировать в других модулях. Как и в случае с другими типами модулей, импорт осуществляется при помощи функции `require()`, только в качестве аргумента функции вместо имени модуля указываем путь к файлу. +Creating a module starts with creating a separate JS file where the code is written. If the module needs to export something, this is done by writing the exported value as a property of the special `module.exports` object, or by overwriting it. -Работу с модулями в Node.js начнём с создания Node.js-приложения +Values exported from one module can be imported into other modules. As with other types of modules, import is done using the `require()` function, but in this case, the path to the file is specified instead of the module name. -### Создание Node.js-приложения +Let's start working with modules in Node.js by creating a Node.js application. -Создадим новый проект. Для этого создадим папку проекта, откроем её в VS Code и в терминале выполним команду -`npm init -y` -Параметр `-y` (Yes) означает, что мы соглашаемся со всеми настройками проекта по умолчанию. -В папке проекта появляется файл `package.json`, который описывает созданное приложение. +### Creating a Node.js Application -### Установка модулей-пакетов через npm. +Let's create a new project. To do this, create a project folder, open it in VS Code, and in the terminal, execute the command: -Установка модуля осуществляется командой +```bash +npm init -y +``` -```powershell -npm install <имя модуля> +The `-y` (Yes) parameter means that we agree with all project settings by default. +A `package.json` file is created in the project folder, describing the created application. + +### Installing Packages via npm + +To install a module, use the command + +```bash +npm install ``` -Установим модуль nodemon. Для этого в терминале выполним команду +Install the `nodemon` module. To do this, execute the command in the terminal: -```powershell +```bash npm install nodemon ``` -Удаление модуля: +Removing a module: -```powershell +```bash npm uninstall nodemon ``` -Установленные модули добавляются в папку `node_modules`, а информация о них добавляется в файл `package.json`. Кроме того, автоматически создается файл `package.lock.json`, гарантирующий идентичность пакетов у различных пользователей, а также выполняющий ряд других полезных функций. -Если удалить папку `node_modules` и выполнить команду `npm install`, папка `node_modules` восстановится вместе со всеми добавленными модулями на основе записей в файле `package.json`. -Проекты, написанные на Node.js, добавляются на GitHub без папки `node_modules`, но с файлом `package.json`, а также `package.lock.json`. После скачивания такого проекта необходимо выполнить в терминале команду `npm install`, чтобы восстановить все установленные через `npm` модули. +Installed modules are added to the `node_modules` folder, and information about them is added to the `package.json` file. Additionally, a `package-lock.json` file is automatically created, ensuring package identity among different users and performing other useful functions. + +If you delete the `node_modules` folder and execute the `npm install` command, the `node_modules` folder will be restored along with all the added modules based on the records in the `package.json` file. + +Node.js projects are added to GitHub without the `node_modules` folder but with the `package.json` and `package-lock.json` files. +After downloading such a project, you need to execute the `npm install` command in the terminal to restore all modules installed through npm. diff --git a/stage1/modules/node-materials/node/node-stdio.md b/stage1/modules/node-materials/node/node-stdio.md index 8a1eb2e54..351bd8ada 100644 --- a/stage1/modules/node-materials/node/node-stdio.md +++ b/stage1/modules/node-materials/node/node-stdio.md @@ -1,140 +1,144 @@ -## Стандартные потоки ввода/вывода +## Standard Input/Output Streams [HOME](../README.md) -Для ввода и вывода информации (I/O - input/output) в Node.js существуют стандартные потоки ввода и вывода: +For input and output operations (I/O) in Node.js, there are standard input and output streams: -- `process.stdin` - поток ввода -- `process.stdout` - поток вывода -- `process.stderr` - поток ошибки как разновидность потока вывода +- `process.stdin` - input stream +- `process.stdout` - output stream +- `process.stderr` - error stream as a variation of the output stream -Например, уже известный нам `console.log()` для вывода информации использует `process.stdout`. +For example, the well-known `console.log()` for outputting information uses `process.stdout`. -### Стандартный поток вывода +### Standard Output Stream -Выведем информацию в консоль при помощи `process.stdout`. +Let's output information to the console using `process.stdout`: ```js const { stdout } = process; stdout.write("Node.js"); ``` -Метод `stdout.write()` принимает в качестве аргумента строку и выводит её в консоль. В отличие от `console.log()` он не добавляет автоматический перенос в конце строки. При необходимости перенос строки `\n` можно добавить вручную. +The `stdout.write()` method takes a string as an argument and outputs it to the console. Unlike `console.log()`, it does not automatically add a line break at the end of the string. If a line break is needed, `\n` can be added manually. -### Стандартный поток ввода +### Standard Input Stream -В файле `test.js` напишем и запустим код: +In the file `test.js`, let's write and run the code: ```js const { stdin, stdout } = process; stdin.on("data", (data) => stdout.write(data)); ``` -При помощи метода `.on()` мы подписываемся на событие `'data'` объекта `stdin`. -Метод `.on()` принимает два параметра - название события `'data'` и стрелочную функцию-обработчик `data => stdout.write(data)`, которая выводит в консоль переданные данные. +Using the `.on()` method, we subscribe to the `data` event of the `stdin` object. +The `.on()` method takes two parameters - the event name `data` and the arrow function handler `data => stdout.write(data)`, which outputs the entered data to the console. -Теперь, когда мы вводим в консоль какой-то текст и нажимаем клавишу Enter, `stdout.write()` возвращает введённый нами текст. +Now, when we enter some text in the console and press the Enter key, `stdout.write()` returns the text we entered. -### Метод process.exit() +### The `process.exit()` Method -Остановить выполнение программы можно нажав комбинацию клавиш `Ctrl + C` или использовав метод `process.exit()`. +To stop the program, you can press the `Ctrl + C` key combination or use the `process.exit()` method. -Метод `process.exit()` при запуске эмитит событие `'exit'`, подписавшись на которое мы можем выполнить определенные действия перед завершением программы: +The `process.exit()` method emits the `exit` event when executed, and by subscribing to this event, we can perform certain actions before the program terminates: ```js -process.on("exit", () => stdout.write("Удачи в изучении Node.js!")); +process.on("exit", () => stdout.write("Good luck learning Node.js!")); ``` -`process.exit()` принимает необязательный аргумент `exitCode`, представленный целым числом. По умолчанию данный метод запускается с параметром `exitCode === 0`. Такое завершение процесса означает, что программа выполнена успешно и отработала без ошибок. Завершение процесса с любым другим `exitCode`, что работа программы завершилась ошибкой. Благодаря этому можно передать разные сообщения на выходе в зависимости от того, сработала программа как нужно, или нет. +`process.exit()` takes an optional argument `exitCode`, represented by an integer. +By default, this method is called with `exitCode === 0`. This type of process termination indicates that the program executed successfully and ran without errors. +Exiting the process with any other `exitCode` signifies that the program terminated with an error. This allows different messages to be passed depending on whether the program executed as intended or not: ```js const { stdout, stderr } = process; process.on("exit", (code) => { if (code === 0) { - stdout.write("Всё в порядке"); + stdout.write("Everything is ok"); } else { - stderr.write(`Что-то пошло не так. Программа завершилась с кодом ${code}`); + stderr.write(`Something went wrong. The program exited with code ${code}`); } }); ``` -## Задание 1 +## Task 1 -Напишите программу, которая спрашивает у пользователя его имя, после ввода имени приветствует его, указывая имя, а затем прекращает свою работу и прощается с пользователем. +Write a program that asks the user for their name, greets them after entering the name, and then stops its execution, saying goodbye to the user.
- Пример решения + Example solution ```js const { stdin, stdout } = process; -stdout.write("Как тебя зовут?\n"); +stdout.write("What is your name?\n"); stdin.on("data", (data) => { - stdout.write("Привет, "); + stdout.write("Hello, "); stdout.write(data); process.exit(); }); -process.on("exit", () => stdout.write("Удачи!")); +process.on("exit", () => stdout.write("Goodbye!")); ```
### Buffer -Несмотря на то, что в предыдущем примере параметр `data` обработчика одноименного события похож на строку, на самом деле он не является строкой. При попытке воспользоваться методами строки мы получим ошибку: +Despite the fact that in the previous example the `data` parameter of the event handler looks like a string, it is not. If we try to use string methods, we will get an error: ```js const { stdin, stdout } = process; stdin.on("data", (data) => { - // После ввода текста в консоль и нажатия Enter получим TypeError: data.toUpperCase is not a function - stdout.write("Cообщение в верхнем регистре: "); + // After entering text in the console and pressing Enter, TypeError: data.toUpperCase is not a function will be thrown + stdout.write("Message in uppercase: "); stdout.write(data.toUpperCase()); }); ``` -Если мы выведем в консоль тип переменной `data`, мы увидим `object`. Применив [трюк со специальным свойством [[Class]]](https://learn.javascript.ru/class-instanceof#sekretnoe-svoystvo-class) мы получим для `data` `[object Uint8Array]`. Поскольку `process.stdin` — это поток, он работает с данными в двоичном формате. Для работы с таким форматом данных в Node.js есть специальный объект `Buffer`, который и является подклассом `Uint8Array`(типизированный массив, хранящий 8-битные целые беззнаковые значения). -Данные, содержащиеся в буфере, можно привести к строке: +If we log the type of the `data` variable to the console, we will see `object`. By applying the [trick with the special `[[Class]]` property](https://learn.javascript.ru/class-instanceof#sekretnoe-svoystvo-class), we get `[object Uint8Array]` for `data`. + +Since `process.stdin` is a stream, it works with data in **binary** format. To handle such data in Node.js, there is a special `Buffer` object, which is a subclass of `Uint8Array` (a typed array storing 8-bit unsigned integer values). +The data contained in the `Buffer` can be converted to a string: ```js -// создадим буфер из строки, вторым параметром передав кодировку (по умолчанию будет использована utf-8) +// create a buffer from a string, specifying the encoding as the second parameter (utf-8 will be used by default) const myBuffer = Buffer.from("Hi Node.js!", "utf-8"); -// получим +// get console.log(myBuffer); -// приведем к строке +// convert to a string const bufferStringified = myBuffer.toString(); // Hi Node.js! console.log(bufferStringified); ``` -Исправим наш предыдущий пример: +Let's fix our previous example: ```js const { stdin, stdout } = process; stdin.on("data", (data) => { const dataStringified = data.toString(); - stdout.write("Cообщение в верхнем регистре: "); + stdout.write("Message in uppercase: "); stdout.write(dataStringified.toUpperCase()); }); ``` -## Задание 2 +## Task 2 -Напишите программу, которая спрашивает у пользователя его имя, после ввода имени возвращает указанное пользователем имя наоборот и прекращает работу. +Write a program that asks the user for their name, returns the user-entered name in reverse, and then stops its execution.
- Пример решения + Example solution ```js const { stdin, stdout } = process; -stdout.write("Как тебя зовут?\n"); +stdout.write("What is your name?\n"); stdin.on("data", (data) => { const name = data.toString(); const reverseName = name.split("").reverse().join(""); - stdout.write(`\nТвоё имя наоборот ${reverseName}`); + stdout.write(`\nYour name in reverse is ${reverseName}`); process.exit(); }); ``` diff --git a/stage1/modules/node-materials/node/stream-pipes.md b/stage1/modules/node-materials/node/stream-pipes.md index d90cced24..dcad955ce 100644 --- a/stage1/modules/node-materials/node/stream-pipes.md +++ b/stage1/modules/node-materials/node/stream-pipes.md @@ -1,8 +1,8 @@ -## Объединение потоков чтения-записи +## Combining Readable-Writable Streams [HOME](../README.md) -Код из [предыдущей части](stream-writable.md) можно сделать ещё проще и лучше: +The code from the [previous section](stream-writable.md) can be made even simpler and better: ```js const fs = require("fs"); @@ -13,25 +13,26 @@ const output = fs.createWriteStream("destination.txt"); input.pipe(output); ``` -Несмотря на то, что кода стало меньше, работает он точно так же, как прежде. -Метод `pipe()`, имеющийся у каждого потока, можно использовать для объединения одних потоков с другими. Такие цепочки могут объединять несколько потоков. +Despite the reduction in code, it works exactly the same as before. +The `pipe()` method, available on every stream, can be used to combine one stream with another. Such chains can connect multiple streams. -Эту особенность метода `pipe()` используют, например, для сжатия файлов. +This feature of the `pipe()` method is used, for example, for compressing files. -Импортируем `zlib`, и используем ее стандартный метод, который отвечает за сжатие файлов. Поток `gzip` является трансформирующим потоком: получает одни данные, преобразует их и возвращаёт другие данные. -После создания потока чтения создадим поток, который отвечает за сжатие файла: +Let's import `zlib` and use its standard method responsible for compressing files. The `gzip` stream is a transforming stream: it receives one set of data, transforms it, and returns another set of data. + +After creating the reading stream, let's create a stream responsible for compressing the file: ```js const gzip = zlib.createGzip(); ``` -затем чего соединим поток сжатия и поток вывода: +then connect the compression stream and the output stream: ```js input.pipe(gzip).pipe(output); ``` -Полный код примера: +The complete code: ```js const fs = require("fs"); @@ -44,7 +45,7 @@ const gzip = zlib.createGzip(); input.pipe(gzip).pipe(output); ``` -Есть довольно удобный способ объединения нескольких потоков, позволяющий использовать один обработчик ошибок — функция `pipeline`: +There is a convenient way to combine multiple streams, allowing the use of a single error handler — the `pipeline` function: ```js const fs = require("fs"); @@ -57,7 +58,7 @@ const gzip = zlib.createGzip(); pipeline(input, gzip, output, (err) => { if (err) { - // обрабатываем ошибки + // handle errors } }); ``` diff --git a/stage1/modules/node-materials/node/stream-readable.md b/stage1/modules/node-materials/node/stream-readable.md index 43028bb3d..d0ae4460c 100644 --- a/stage1/modules/node-materials/node/stream-readable.md +++ b/stage1/modules/node-materials/node/stream-readable.md @@ -1,18 +1,20 @@ -## Поток чтения (Readable stream) +## Readable Stream [HOME](../README.md) -Поток чтения, как понятно из его названия, используется для чтения данных. Источником данных может быть что угодно: ввод пользователя, файл, входящий запрос пользователя при обработке на сервере, другой поток, асинхронный итератор и т.д. +As the name suggests, a readable stream is used for reading data. The data source can be anything: user input, a file, an incoming user request being processed on the server, another stream, an asynchronous iterator, and so on. -Создадим программу, которая будет читать достаточно большой файл и выводить его содержимое в консоль. Для этого используем модуль `fs`, но вместо метода `readFile()` используем метод `createReadStream()`, параметром которого укажем название файла `source.txt`, из которого будем читать информацию. Так как файл лежит в той же директории, что и файл с кодом, путь к файлу прописывать не обязательно. +Let's create a program that reads a fairly large file and outputs its contents to the console. To do this, we'll use the `fs` module, but instead of the `readFile()` method, we'll use the `createReadStream()` method, specifying the file name `source.txt` as its parameter, from which we will read information. + +Since the file is in the same directory as the code file, it is not necessary to specify the path to the file: ```js const fs = require("fs"); const readableStream = fs.createReadStream("source.txt"); ``` -У потока чтения есть событие `data`, которое генерируется, когда стрим прочитал порцию данных и готов отдать ее потребителю этих данных. -При наступлении этого события выведем поступившую часть данных в консоль: +A readable stream has a `data` event that is emitted when the stream has read a chunk of data and is ready to deliver it to the consumer of that data. +When this event occurs, we will output the received part of the data to the console: ```js const fs = require("fs"); @@ -20,10 +22,10 @@ const readableStream = fs.createReadStream("source.txt"); readableStream.on("data", (chunk) => console.log(chunk)); ``` -В консоли вместо текста объекты `Buffer`. Раньше эту проблему мы решали при помощи метода `data.toString()`, но преобразовать `Buffer` в строку можно и другим способом, указав вторым параметром метода `createReadStream()` кодировку `'utf-8'`. +In the console, instead of text, there are `Buffer` objects. Previously, we solved this problem using the `data.toString()` method, but you can convert a `Buffer` to a string in another way by specifying the encoding `'utf-8'` as the second parameter of the `createReadStream()` method. -Как убедиться, что данные приходят по частям? -Выведем в консоль не сами данные, а длину каждой пришедшей части данных +How can we be sure that the data is coming in chunks? +Let's output to the console not the data itself, but the length of each incoming data chunk: ```js const fs = require("fs"); @@ -31,9 +33,9 @@ const readableStream = fs.createReadStream("source.txt"); readableStream.on("data", (chunk) => console.log(chunk.length)); ``` -Если файл с данными достаточно большой, видно, что приходят они частями (чанками) размером 64кБ. +If the file with the data is large enough, you can see that it comes in chunks (chunks) of 64kB. -Чтобы все эти части собрать вместе, определим переменную `datа`. Её значением укажем пустую строку. Каждую пришедшую часть данных будем присоединять к `datа`. +To collect all these parts together, let's define a variable `data`. We'll initialize it with an empty string. We'll concatenate each incoming data chunk to `data`: ```js const fs = require("fs"); @@ -45,6 +47,10 @@ stream.on("data", (chunk) => (data += chunk)); Так как мы имеем дело с потоком данных, нам нужно знать когда поток завершится. Для этого у стрима есть событие `'end'`. Это событие срабатывает когда все данные уже переданы. При наступлении события `'end'` выведем в консоль сообщение и длину полученных данных: +Since we are dealing with a data stream, we need to know when the stream will end. For this, the stream has an `end` event. This event is triggered when all the data has already been passed. + +When the `end` event occurs, we'll output a message to the console and the length of the received data: + ```js const fs = require("fs"); @@ -56,7 +62,9 @@ stream.on("data", (chunk) => (data += chunk)); stream.on("end", () => console.log("End", data.length)); ``` -Обработаем возможную ошибку. При возникновении ошибки будет сгенерировано событие `error`. При наступлении ошибки выведем в консоль сообщение и текст ошибки. Чтобы вызвать ошибку, укажем несуществующее имя файла +Let's handle a possible error. If an error occurs, an `error` event will be generated. + +When an error occurs, we'll output a message and the error text to the console. To trigger an error, specify a nonexistent file name: ```js const fs = require("fs"); diff --git a/stage1/modules/node-materials/node/stream-writable.md b/stage1/modules/node-materials/node/stream-writable.md index ec9e53946..b8ea2a993 100644 --- a/stage1/modules/node-materials/node/stream-writable.md +++ b/stage1/modules/node-materials/node/stream-writable.md @@ -1,21 +1,21 @@ -## Поток записи (Writable stream) +## Writable Stream [HOME](../README.md) -Поток записи, является противоположностью потока чтения. Он используется для записи данных. Записывать данные можно, к примеру: в стандартный поток вывода, файл, response при обработке на сервере, другой поток и т.д. +A writable stream is the opposite of a readable stream. It is used for writing data. Data can be written to various destinations, such as the standard output stream, a file, a server response during server processing, another stream, and so on. -Если мы читаем данные по частям, логично записывать их тоже по частям. -Для этого создадим поток записи `output` +If we read data in chunks, it makes sense to write them in chunks as well. +To do this, let's create a writable stream called `output`: ```js const fs = require("fs"); const output = fs.createWriteStream("destination.txt"); ``` -Если не создать файл, который указан в качестве пункта назначения наших данных, `destination.txt`, перед началом записи он будет создан автоматически. -Поток чтения назовём `input` и каждую часть данных, которую он отдает, будем записывать в файл при помощи метода `output.write()` +If the file specified as the destination for our data, `destination.txt`, is not created before writing begins, it will be created automatically. +Let's name the readable stream `input`, and for each part of the data it provides, we'll write it to the file using the `output.write()` method. -Сравните полученный код потока записи с кодом [потока чтения](stream-readable.md) - они создаются и используются сходным образом. +Compare the code for the writable stream with the code for the [readable stream](stream-readable.md) - they are created and used in a similar way: ```js const fs = require("fs"); diff --git a/stage1/modules/node-materials/node/stream.md b/stage1/modules/node-materials/node/stream.md index f03983714..30eb38875 100644 --- a/stage1/modules/node-materials/node/stream.md +++ b/stage1/modules/node-materials/node/stream.md @@ -1,20 +1,22 @@ -## Streams (потоки, стримы) +## Streams [HOME](../README.md) ![stream schema](https://pawelgrzybek.com/photos/2020-07-14-1.png) -Если нам нужно работать с достаточно большим объёмом данных, работать с ним целиком означает загрузить оперативную память и остановить работу программы на все время операции. -Вместо этого считывание и запись данных можно осуществлять по частям, небольшими фрагментами - чанками (`chunk`). Это позволяет работать с очень большими объемами данных, не повышая объем потребляемой памяти пропорционально их размеру. +When working with a significant amount of data, processing it all at once can lead to loading the entire dataset into memory and freezing the program for the entire duration of the operation. -Стримы используют интерфейс работы с событиями, унаследованный от `EventEmitter`. +Instead of this, reading and writing data can be done in parts, small fragments called `chunks`. This approach allows working with very large volumes of data without proportionally increasing the memory consumption. -Помимо использования готовых стримов, мы можем создавать свои собственные стримы, отнаследовавшись от базовых классов и реализовав некоторые обязательные методы. +Streams use an event-driven interface, inherited from `EventEmitter`. -Для работы с потоковыми данными в Node.js есть абстрактный интерфейс — streams (потоки, стримы). -В Node.js есть 4 основных вида потоков: +In addition to using existing streams, we can create our own streams by inheriting from basic classes and implementing some mandatory methods. -- [Readable](stream-readable.md) — поток чтения, используется для чтения данных -- [Writable](stream-writable.md) — поток записи, используется для записи данных -- Duplex — поток, который может быть использован как для чтения, так и для записи данных -- Transform— разновидность `Duplex`, используемая для преобразования данных +For working with stream data in Node.js, there is an abstract interface — streams. + +In Node.js, there are 4 main types of streams: + +- [Readable](stream-readable.md) — a readable stream used for reading data +- [Writable](stream-writable.md) — a writable stream used for writing data +- Duplex — a stream that can be used for both reading and writing data +- Transform — a type of `Duplex` used for data transformation From 48f5a93e62fc6c018a4f3b81569937c353b098fc Mon Sep 17 00:00:00 2001 From: Lizaveta Date: Fri, 17 Nov 2023 08:08:33 +0200 Subject: [PATCH 5/9] chore: run prettier --- .../modules/node-materials/node/module/fs.md | 8 +++--- .../node-materials/node/module/http.md | 6 ++-- .../modules/node-materials/node/node-argv.md | 10 +++---- .../node-materials/node/node-fs-access.md | 6 ++-- .../node-materials/node/node-introduction.md | 16 +++++------ stage1/modules/node-materials/node/node-io.md | 2 +- .../node-materials/node/node-module.md | 12 ++++---- .../modules/node-materials/node/node-stdio.md | 8 +++--- .../node/projects/github-app.md | 28 +++++++++---------- .../node-materials/node/projects/notes.md | 8 +++--- .../node-materials/node/projects/timer.md | 8 +++--- .../node-materials/node/stream-pipes.md | 2 +- .../node-materials/node/stream-readable.md | 4 +-- .../node-materials/node/stream-writable.md | 4 +-- 14 files changed, 61 insertions(+), 61 deletions(-) diff --git a/stage1/modules/node-materials/node/module/fs.md b/stage1/modules/node-materials/node/module/fs.md index 43da7e1c1..6d9d84704 100644 --- a/stage1/modules/node-materials/node/module/fs.md +++ b/stage1/modules/node-materials/node/module/fs.md @@ -41,7 +41,7 @@ fs.writeFile( (err) => { if (err) throw err; console.log("File was created"); - }, + } ); ``` @@ -57,7 +57,7 @@ fs.appendFile( (err) => { if (err) throw err; console.log("File was modified"); - }, + } ); ``` @@ -73,7 +73,7 @@ fs.readFile( (err, data) => { if (err) throw err; console.log(data); - }, + } ); ``` @@ -89,7 +89,7 @@ fs.rename( (err) => { if (err) throw err; console.log("File renamed"); - }, + } ); ``` diff --git a/stage1/modules/node-materials/node/module/http.md b/stage1/modules/node-materials/node/module/http.md index 78a1b184a..447c797a0 100644 --- a/stage1/modules/node-materials/node/module/http.md +++ b/stage1/modules/node-materials/node/module/http.md @@ -48,8 +48,8 @@ This method takes a callback function `requestHandler` with two parameters, `req - `request` holds information about the request - `response` is responsible for sending the response -Our `requestHandler` logs the request method and the address of the requested resource to the console. It also sends the messages `Hello from Node.js` and `Bye!` as responses. -The `response.write()` method writes the message to the response body, and `response.end()` informs the server that the headers and body of the response are written and it can be sent. +Our `requestHandler` logs the request method and the address of the requested resource to the console. It also sends the messages `Hello from Node.js` and `Bye!` as responses. +The `response.write()` method writes the message to the response body, and `response.end()` informs the server that the headers and body of the response are written and it can be sent. Note that `response.end()` should terminate every response. Without it, the request processing will "hang" — the request will be received but not fully processed. ```js @@ -69,7 +69,7 @@ server.listen(PORT, "localhost", () => { }); ``` -Run the file with the code, open your browser, and go to the address `localhost:3000/some/page`. +Run the file with the code, open your browser, and go to the address `localhost:3000/some/page`. Note that to run the server with different code, you need to stop it and restart it. You already know how to terminate a Node.js process. Only one server can be running on a port at a time. In the `write` and `end` methods, you can pass a string containing HTML tags with inline styles. These tags will be correctly processed by the browser. diff --git a/stage1/modules/node-materials/node/node-argv.md b/stage1/modules/node-materials/node/node-argv.md index c81016463..d0c243041 100644 --- a/stage1/modules/node-materials/node/node-argv.md +++ b/stage1/modules/node-materials/node/node-argv.md @@ -8,7 +8,7 @@ In Node.js, you can run a file with specific command line arguments. When runnin node test 1 2 3 ``` -`1`, `2`, `3` are the arguments. +`1`, `2`, `3` are the arguments. How can you access the arguments passed when the file is launched within the code? This is done using the `process.argv` property of the global `process` object. In the `test.js` file, let's write the code: @@ -17,7 +17,7 @@ In the `test.js` file, let's write the code: console.log(process.argv); ``` -In the terminal, execute the command `node test 1 2 3`. +In the terminal, execute the command `node test 1 2 3`. In the console, an array is displayed, the first two elements of which are the path to the node.exe file and the path to the executed file. After that come the passed arguments. If you only need to get the arguments, execute the code: @@ -59,7 +59,7 @@ console.log(message); ### Practical Usage -In practice, when you write code to handle command line arguments yourself, it is necessary to handle various situations correctly — an argument may be missing, a flag may not be passed, or it may be passed without a value. The mere presence of a flag can also be a boolean value, and so on. +In practice, when you write code to handle command line arguments yourself, it is necessary to handle various situations correctly — an argument may be missing, a flag may not be passed, or it may be passed without a value. The mere presence of a flag can also be a boolean value, and so on. To make working with command line arguments more convenient and minimize the likelihood of errors, it is useful to use ready-made solutions such as [minimist](https://www.npmjs.com/package/minimist), [commander](https://www.npmjs.com/package/commander), [yargs](https://www.npmjs.com/package/yargs), and others. ### CLI Options @@ -88,7 +88,7 @@ will result in an error. You can view the full list of options in the [documenta ### Environment Variables -Sometimes we need to pass some value from the **outside** to our code, which will be used by our application. For example, we want to implement different behavior when the application is launched on a production server and during development. Environment variables can help us in this case. +Sometimes we need to pass some value from the **outside** to our code, which will be used by our application. For example, we want to implement different behavior when the application is launched on a production server and during development. Environment variables can help us in this case. Environment variables have a syntax like `variable_name=variable_value` and are placed before `node ....`. In Bash, for example, you can pass a variable that shows in which mode the application is currently running: @@ -150,7 +150,7 @@ stdin.on("data", (data) => { const numStringsArray = numString.split(" "); const hasIncorrectLength = numStringsArray.length !== 2; const hasIncorrectValues = numStringsArray.some((numStr) => - Number.isNaN(+numStr), + Number.isNaN(+numStr) ); if (hasIncorrectLength || hasIncorrectValues) { stdout.write("You need to enter 2 numbers separated by a space"); diff --git a/stage1/modules/node-materials/node/node-fs-access.md b/stage1/modules/node-materials/node/node-fs-access.md index ab2ae0997..17d89df0d 100644 --- a/stage1/modules/node-materials/node/node-fs-access.md +++ b/stage1/modules/node-materials/node/node-fs-access.md @@ -2,11 +2,11 @@ [HOME](../README.md) -Unlike browser-based JavaScript, Node.js has access to the file system. +Unlike browser-based JavaScript, Node.js has access to the file system. For example, we can easily determine the absolute path to the directory containing our file. To do this, open the `test.js` file and write the code: ```js -console.log(__dirname); +console.log(__dirname); ``` Open the terminal and run the file: @@ -15,7 +15,7 @@ Open the terminal and run the file: node test ``` -The console will output the absolute path to the directory with the `test.js` file. +The console will output the absolute path to the directory with the `test.js` file. Now let's output the absolute path to the file. To do this, add the line in the `test.js` file: ```js diff --git a/stage1/modules/node-materials/node/node-introduction.md b/stage1/modules/node-materials/node/node-introduction.md index 48abca509..4a3954ee8 100644 --- a/stage1/modules/node-materials/node/node-introduction.md +++ b/stage1/modules/node-materials/node/node-introduction.md @@ -8,16 +8,16 @@ Download link https://nodejs.org/en Download and install the latest LTS version (Recommended For Most Users) -Check what you have installed. To do this, check the version of Node.js. +Check what you have installed. To do this, check the version of Node.js. Open Git Bash: right-click on the desktop, choose `Git Bash Here` from the context menu. If this option is not available, download and install Git https://git-scm.com/downloads. -Execute the command `node -v`. +Execute the command `node -v`. If the Node.js version is displayed, then we have successfully completed the first step, and Node.js is installed. ### Possible Installation Issues -Sometimes, after installing Node.js, commands in the terminal starting with `node` may cause errors like `'node' is not recognized as an internal or external command, operable program or batch file`. -In this case, you need to add the correct path to the Node.js directory to the `PATH`. +Sometimes, after installing Node.js, commands in the terminal starting with `node` may cause errors like `'node' is not recognized as an internal or external command, operable program or batch file`. +In this case, you need to add the correct path to the Node.js directory to the `PATH`. [Example solution](https://love2dev.com/blog/node-is-not-recognized-as-an-internal-or-external-command/). ### Where to Write Code @@ -26,10 +26,10 @@ Neither writing code in the terminal, nor is Notepad is very convenient. Let's c ### REPL Mode -You can write and execute code directly in the terminal. +You can write and execute code directly in the terminal. This mode is called `REPL` (Read-Eval-Print-Loop). -To enter it, execute the command `node` in the terminal. +To enter it, execute the command `node` in the terminal. Now you can write code directly in the terminal, and REPL will evaluate the entered expression and display the result. For example, if you enter `2 + 2` and press Enter, REPL will output `4`. You can also make explicit console output using the familiar methods. For example, you can write: @@ -38,8 +38,8 @@ You can also make explicit console output using the familiar methods. For exampl console.log("Hello, world!"); ``` -REPL has some useful commands, information about which can be obtained by sending the `.help` command. -To exit REPL mode, send the `.exit` command (also, for a more forceful process termination, you can use the standard key combination for the terminal used, such as `Ctrl + C`). +REPL has some useful commands, information about which can be obtained by sending the `.help` command. +To exit REPL mode, send the `.exit` command (also, for a more forceful process termination, you can use the standard key combination for the terminal used, such as `Ctrl + C`). Clear the terminal using commands like `cls` (for the standard Windows Command Prompt and PowerShell) or `clear` (for Bash). ### How to Run a File diff --git a/stage1/modules/node-materials/node/node-io.md b/stage1/modules/node-materials/node/node-io.md index bcc3d326d..a8b81711b 100644 --- a/stage1/modules/node-materials/node/node-io.md +++ b/stage1/modules/node-materials/node/node-io.md @@ -22,7 +22,7 @@ const users = connection.query("SELECT * FROM users"); // make a query console.log(users); // display information in the console ``` -This is a simple implementation but a very time-consuming one: the program waits for **250 million processor cycles** until a connection to the database occurs. +This is a simple implementation but a very time-consuming one: the program waits for **250 million processor cycles** until a connection to the database occurs. The second problem with synchronous I/O is that it is not fault-tolerant. If the program cannot connect to the database or if the requested information is not found in the database, the program will stop working. Synchronous or blocking I/O in Node.js is used very rarely: diff --git a/stage1/modules/node-materials/node/node-module.md b/stage1/modules/node-materials/node/node-module.md index 5cc18a50f..a01e99699 100644 --- a/stage1/modules/node-materials/node/node-module.md +++ b/stage1/modules/node-materials/node/node-module.md @@ -17,8 +17,8 @@ Advantages of using modules in Node.js: To simplify, modules in Node.js can be divided into three types: -1. Core modules, which we get "out of the box" by installing Node.js on a computer. -Examples of core modules: +1. Core modules, which we get "out of the box" by installing Node.js on a computer. + Examples of core modules: - [path module](module/path.md) - [fs module](module/fs.md) @@ -29,12 +29,12 @@ Examples of core modules: ### Core Modules -They are already compiled into binary code and described in the documentation. +They are already compiled into binary code and described in the documentation. [List of core modules](https://nodejs.org/dist/latest-v14.x/docs/api/) Core modules are easy to include, and you can start working with them. -To include a module, the `require()` function is used. +To include a module, the `require()` function is used. Examples of module inclusion: ```js @@ -71,7 +71,7 @@ Let's create a new project. To do this, create a project folder, open it in VS C npm init -y ``` -The `-y` (Yes) parameter means that we agree with all project settings by default. +The `-y` (Yes) parameter means that we agree with all project settings by default. A `package.json` file is created in the project folder, describing the created application. ### Installing Packages via npm @@ -98,5 +98,5 @@ Installed modules are added to the `node_modules` folder, and information about If you delete the `node_modules` folder and execute the `npm install` command, the `node_modules` folder will be restored along with all the added modules based on the records in the `package.json` file. -Node.js projects are added to GitHub without the `node_modules` folder but with the `package.json` and `package-lock.json` files. +Node.js projects are added to GitHub without the `node_modules` folder but with the `package.json` and `package-lock.json` files. After downloading such a project, you need to execute the `npm install` command in the terminal to restore all modules installed through npm. diff --git a/stage1/modules/node-materials/node/node-stdio.md b/stage1/modules/node-materials/node/node-stdio.md index 351bd8ada..474cb5f14 100644 --- a/stage1/modules/node-materials/node/node-stdio.md +++ b/stage1/modules/node-materials/node/node-stdio.md @@ -30,7 +30,7 @@ const { stdin, stdout } = process; stdin.on("data", (data) => stdout.write(data)); ``` -Using the `.on()` method, we subscribe to the `data` event of the `stdin` object. +Using the `.on()` method, we subscribe to the `data` event of the `stdin` object. The `.on()` method takes two parameters - the event name `data` and the arrow function handler `data => stdout.write(data)`, which outputs the entered data to the console. Now, when we enter some text in the console and press the Enter key, `stdout.write()` returns the text we entered. @@ -45,8 +45,8 @@ The `process.exit()` method emits the `exit` event when executed, and by subscri process.on("exit", () => stdout.write("Good luck learning Node.js!")); ``` -`process.exit()` takes an optional argument `exitCode`, represented by an integer. -By default, this method is called with `exitCode === 0`. This type of process termination indicates that the program executed successfully and ran without errors. +`process.exit()` takes an optional argument `exitCode`, represented by an integer. +By default, this method is called with `exitCode === 0`. This type of process termination indicates that the program executed successfully and ran without errors. Exiting the process with any other `exitCode` signifies that the program terminated with an error. This allows different messages to be passed depending on whether the program executed as intended or not: ```js @@ -98,7 +98,7 @@ stdin.on("data", (data) => { If we log the type of the `data` variable to the console, we will see `object`. By applying the [trick with the special `[[Class]]` property](https://learn.javascript.ru/class-instanceof#sekretnoe-svoystvo-class), we get `[object Uint8Array]` for `data`. -Since `process.stdin` is a stream, it works with data in **binary** format. To handle such data in Node.js, there is a special `Buffer` object, which is a subclass of `Uint8Array` (a typed array storing 8-bit unsigned integer values). +Since `process.stdin` is a stream, it works with data in **binary** format. To handle such data in Node.js, there is a special `Buffer` object, which is a subclass of `Uint8Array` (a typed array storing 8-bit unsigned integer values). The data contained in the `Buffer` can be converted to a string: ```js diff --git a/stage1/modules/node-materials/node/projects/github-app.md b/stage1/modules/node-materials/node/projects/github-app.md index 1cd22d951..805bbbb90 100644 --- a/stage1/modules/node-materials/node/projects/github-app.md +++ b/stage1/modules/node-materials/node/projects/github-app.md @@ -6,7 +6,7 @@ Let's create a console application that, as a command-line argument, will take a To achieve this, we'll use the GitHub API. The request `https://api.github.com/users/USERNAME/repos` returns a list of repositories for the user whose `USERNAME` is specified in the link. -1. Start by creating a new Node.js application +1. Start by creating a new Node.js application Create a folder named github-app, open it in VS Code, and run the following command in the terminal: @@ -19,7 +19,7 @@ Create two files: - `app.js` - the main application file - `github.js` - the file where we write the logic for interacting with the API -2. Let's start with the `app.js` file +2. Let's start with the `app.js` file Import the `github` module into it. Since this is a manually created module, specify the path to the module code file as a parameter to the `require()` method: @@ -33,7 +33,7 @@ Create a variable `username` to store the command-line argument passed when the const username = process.argv[2]; ``` -Use the imported `github` object, which has a `getRepos()` property - a function that returns a list of user repositories. +Use the imported `github` object, which has a `getRepos()` property - a function that returns a list of user repositories. The parameters of the `getRepos()` function are `username` - the username and a callback function taking two parameters: `error` - an error, and `repos` - the received data, in our case, a list of repositories. In the callback function, let's handle the error and print the names of the repositories to the console: @@ -45,9 +45,9 @@ github.getRepos(username, (error, repos) => { }); ``` -3. Move on to the `github.js` file +3. Move on to the `github.js` file -Our application will communicate with the server over the `https` protocol. For this, Node.js has a built-in `https module`, similar to the [HTTP Module](../module/http.md). +Our application will communicate with the server over the `https` protocol. For this, Node.js has a built-in `https module`, similar to the [HTTP Module](../module/http.md). To send a request to the API, we'll use the `get()` method, which allows us to retrieve data from the server. Import the `https` module and write the code for the `getRepos()` function. The function parameter is `username` - the GitHub username. @@ -72,8 +72,8 @@ Run the `app` file. Specify a well-known repository as a command-line argument w node app goldbergyoni ``` -The application returns a `403` error. -The response code for the error status `HTTP 403 Forbidden` indicates that the server understood the request but refuses to authorize it. +The application returns a `403` error. +The response code for the error status `HTTP 403 Forbidden` indicates that the server understood the request but refuses to authorize it. However, if we try to request the same data from the browser by visiting the link https://api.github.com/users/goldbergyoni/repos, we get the page with the data we need. To make a request to the API, we need to specify the `User-Agent` header. While the browser adds this header automatically when navigating to a link, in a Node.js application, we need to specify it. @@ -92,9 +92,9 @@ const option = { Use the `option` object as the first parameter in `the https.get` method. The application returns a `200` status, indicating a successful connection. -4. Handling incoming data +4. Handling incoming data -Almost everything in Node.js, including communication with the server, is implemented asynchronously, using events and streams. Information from the server comes in parts. +Almost everything in Node.js, including communication with the server, is implemented asynchronously, using events and streams. Information from the server comes in parts. The server response (`res`) has a `data` event, which fires when a part of the requested information comes from the server. Subscribe to this event and print the received data to the console: ```js @@ -133,7 +133,7 @@ function getRepos(username) { } ``` -The response (`res`) method has an `end` event, which triggers when the data transmission is complete. +The response (`res`) method has an `end` event, which triggers when the data transmission is complete. Upon the occurrence of this event, use the `JSON.parse(body)` method to convert the received data into an array: ```js @@ -157,7 +157,7 @@ function getRepos(username) { } ``` -5. Passing data to the `app` module +5. Passing data to the `app` module To pass the obtained data to the `app` module, which imports the `github` module, note that the `app` module expects the `getRepos()` function with two parameters - `username` and a callback function that also has two parameters `(error, repos)`. This callback function needs to be specified as a parameter to the `getRepos()` function in the `github` module. Let's name it `done` - a standard name for such functions. @@ -204,7 +204,7 @@ if (!username) return done(new Error("Username is required")); Create a variable `request` and set its value to the `https.get()` method: ```js -req.on('error', error => done(new Error('Failed to send request'))); +req.on("error", (error) => done(new Error("Failed to send request"))); ``` 3. An error in receiving a response from the server is indicated by a response status other than `200`: @@ -253,8 +253,8 @@ function getRepos(username, done) { } else { done( new Error( - `Error working with the server ${res.statusCode} ${res.statusMessage}`, - ), + `Error working with the server ${res.statusCode} ${res.statusMessage}` + ) ); } }); diff --git a/stage1/modules/node-materials/node/projects/notes.md b/stage1/modules/node-materials/node/projects/notes.md index 89d673099..b57ecc7a7 100644 --- a/stage1/modules/node-materials/node/projects/notes.md +++ b/stage1/modules/node-materials/node/projects/notes.md @@ -9,9 +9,9 @@ Let's write a simple console application called Notes for working with notes. Th - `view` - `remove` -The `create` method creates a new note in the notes.json file. The `create` method has two arguments: the note's title and its content. -The `list` method displays a list of notes. -The `view` method outputs the content of a note whose title is passed as an argument. +The `create` method creates a new note in the notes.json file. The `create` method has two arguments: the note's title and its content. +The `list` method displays a list of notes. +The `view` method outputs the content of a note whose title is passed as an argument. The `remove` method deletes a note whose title is passed as an argument. To call these methods, they are specified as command-line arguments. @@ -117,7 +117,7 @@ function list() { } ``` -Since the `notes.json` file is in the same directory as the code file, there is no need to specify the path; it's enough to provide its name as the first argument to the `readFile()` method. The second argument of the method is a callback function that takes two parameters - an error (`error`) and the data read from the file (`data`). +Since the `notes.json` file is in the same directory as the code file, there is no need to specify the path; it's enough to provide its name as the first argument to the `readFile()` method. The second argument of the method is a callback function that takes two parameters - an error (`error`) and the data read from the file (`data`). Converting the received data into an array is done using the `JSON.parse(data)` method. We iterate through this array using the `forEach()` method and, for each element, print its index and the title of the note to the console, creating a numbered list. To start the list from one instead of zero, we add 1 to the index. diff --git a/stage1/modules/node-materials/node/projects/timer.md b/stage1/modules/node-materials/node/projects/timer.md index 1f24b222c..6abdc31c3 100644 --- a/stage1/modules/node-materials/node/projects/timer.md +++ b/stage1/modules/node-materials/node/projects/timer.md @@ -49,10 +49,10 @@ An instance of the `Timer` class has three methods: - `_tick()` — an internal method of the timer - `end()` — stops the timer -The `start()` method has a variable `intervalId` containing the timer identifier set by `setInterval()`, which calls the `tick()` method every second. Also, the `start()` method emits a `start` event. -The `tick()` method increments the ticks variable by one and checks if the value specified in the constructor's property `total` has been reached. -If it hasn't been reached, the `tick()` method emits a `tick` event, with the current value of the `ticks` variable as the payload. -If the `total` value is reached, the `tick()` method calls the `end()` method. +The `start()` method has a variable `intervalId` containing the timer identifier set by `setInterval()`, which calls the `tick()` method every second. Also, the `start()` method emits a `start` event. +The `tick()` method increments the ticks variable by one and checks if the value specified in the constructor's property `total` has been reached. +If it hasn't been reached, the `tick()` method emits a `tick` event, with the current value of the `ticks` variable as the payload. +If the `total` value is reached, the `tick()` method calls the `end()` method. The `end()` method clears the `setInterval()` timer and emits an `end` event. Let's create an instance of the `Timer` class: diff --git a/stage1/modules/node-materials/node/stream-pipes.md b/stage1/modules/node-materials/node/stream-pipes.md index dcad955ce..444a942fc 100644 --- a/stage1/modules/node-materials/node/stream-pipes.md +++ b/stage1/modules/node-materials/node/stream-pipes.md @@ -13,7 +13,7 @@ const output = fs.createWriteStream("destination.txt"); input.pipe(output); ``` -Despite the reduction in code, it works exactly the same as before. +Despite the reduction in code, it works exactly the same as before. The `pipe()` method, available on every stream, can be used to combine one stream with another. Such chains can connect multiple streams. This feature of the `pipe()` method is used, for example, for compressing files. diff --git a/stage1/modules/node-materials/node/stream-readable.md b/stage1/modules/node-materials/node/stream-readable.md index d0ae4460c..ec31489ec 100644 --- a/stage1/modules/node-materials/node/stream-readable.md +++ b/stage1/modules/node-materials/node/stream-readable.md @@ -13,7 +13,7 @@ const fs = require("fs"); const readableStream = fs.createReadStream("source.txt"); ``` -A readable stream has a `data` event that is emitted when the stream has read a chunk of data and is ready to deliver it to the consumer of that data. +A readable stream has a `data` event that is emitted when the stream has read a chunk of data and is ready to deliver it to the consumer of that data. When this event occurs, we will output the received part of the data to the console: ```js @@ -24,7 +24,7 @@ readableStream.on("data", (chunk) => console.log(chunk)); In the console, instead of text, there are `Buffer` objects. Previously, we solved this problem using the `data.toString()` method, but you can convert a `Buffer` to a string in another way by specifying the encoding `'utf-8'` as the second parameter of the `createReadStream()` method. -How can we be sure that the data is coming in chunks? +How can we be sure that the data is coming in chunks? Let's output to the console not the data itself, but the length of each incoming data chunk: ```js diff --git a/stage1/modules/node-materials/node/stream-writable.md b/stage1/modules/node-materials/node/stream-writable.md index b8ea2a993..9312ff649 100644 --- a/stage1/modules/node-materials/node/stream-writable.md +++ b/stage1/modules/node-materials/node/stream-writable.md @@ -4,7 +4,7 @@ A writable stream is the opposite of a readable stream. It is used for writing data. Data can be written to various destinations, such as the standard output stream, a file, a server response during server processing, another stream, and so on. -If we read data in chunks, it makes sense to write them in chunks as well. +If we read data in chunks, it makes sense to write them in chunks as well. To do this, let's create a writable stream called `output`: ```js @@ -12,7 +12,7 @@ const fs = require("fs"); const output = fs.createWriteStream("destination.txt"); ``` -If the file specified as the destination for our data, `destination.txt`, is not created before writing begins, it will be created automatically. +If the file specified as the destination for our data, `destination.txt`, is not created before writing begins, it will be created automatically. Let's name the readable stream `input`, and for each part of the data it provides, we'll write it to the file using the `output.write()` method. Compare the code for the writable stream with the code for the [readable stream](stream-readable.md) - they are created and used in a similar way: From 0f50dec9d2e091788e61ef83ac7e6b2255d5876b Mon Sep 17 00:00:00 2001 From: Lizaveta Date: Sat, 18 Nov 2023 07:19:30 +0200 Subject: [PATCH 6/9] refactor: update files to maintain consistent text style, update irrelevant links --- .../node/images/stream-schema.png | Bin 0 -> 55754 bytes .../modules/node-materials/node/module/fs.md | 10 ++-- .../node-materials/node/module/http.md | 14 ++--- .../node-materials/node/module/npm-module.md | 2 +- .../node-materials/node/module/path.md | 50 ++++++++++-------- .../modules/node-materials/node/node-argv.md | 10 ++-- .../node-materials/node/node-fs-access.md | 2 +- .../node-materials/node/node-introduction.md | 4 +- .../node-materials/node/node-module.md | 7 +-- .../modules/node-materials/node/node-stdio.md | 2 +- .../node/projects/github-app.md | 21 +++++--- .../node-materials/node/projects/notes.md | 17 +++--- .../node-materials/node/projects/timer.md | 3 +- .../node-materials/node/stream-readable.md | 3 -- stage1/modules/node-materials/node/stream.md | 2 +- 15 files changed, 77 insertions(+), 70 deletions(-) create mode 100644 stage1/modules/node-materials/node/images/stream-schema.png diff --git a/stage1/modules/node-materials/node/images/stream-schema.png b/stage1/modules/node-materials/node/images/stream-schema.png new file mode 100644 index 0000000000000000000000000000000000000000..b2e3486b1c8967aa797ca26758e5ee65ad1f2f88 GIT binary patch literal 55754 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 m1>W%Be;vWy4q@fyZjNyN*YA { if (err) throw err; console.log("File was created"); - } + }, ); ``` @@ -57,7 +57,7 @@ fs.appendFile( (err) => { if (err) throw err; console.log("File was modified"); - } + }, ); ``` @@ -73,7 +73,7 @@ fs.readFile( (err, data) => { if (err) throw err; console.log(data); - } + }, ); ``` @@ -89,10 +89,10 @@ fs.rename( (err) => { if (err) throw err; console.log("File renamed"); - } + }, ); ``` ### Project -[Note-taking Application](../projects/notes.md) +[Notes App](../projects/notes.md) diff --git a/stage1/modules/node-materials/node/module/http.md b/stage1/modules/node-materials/node/module/http.md index 447c797a0..6dc5af9e4 100644 --- a/stage1/modules/node-materials/node/module/http.md +++ b/stage1/modules/node-materials/node/module/http.md @@ -48,9 +48,11 @@ This method takes a callback function `requestHandler` with two parameters, `req - `request` holds information about the request - `response` is responsible for sending the response -Our `requestHandler` logs the request method and the address of the requested resource to the console. It also sends the messages `Hello from Node.js` and `Bye!` as responses. -The `response.write()` method writes the message to the response body, and `response.end()` informs the server that the headers and body of the response are written and it can be sent. -Note that `response.end()` should terminate every response. Without it, the request processing will "hang" — the request will be received but not fully processed. +Our `requestHandler` logs the request method and the address of the requested resource to the console. It also sends the messages `Hello from Node.js` and `Bye!` as responses. + +The `response.write()` method writes the message to the response body, and `response.end()` informs the server that the headers and body of the response are written and it can be sent. + +Note that `response.end()` should terminate every response. Without it, the request processing will "hang" — the request will be received but not fully processed: ```js const requestHandler = (request, response) => { @@ -61,7 +63,7 @@ const requestHandler = (request, response) => { }; ``` -The server's `listen` method starts it, and it begins to listen on the specified port for connections. It has multiple signatures; in our case, it takes three parameters: the local port, the local address, and a callback function that runs when it starts listening for connections. +The server's `listen` method starts it, and it begins to listen on the specified port for connections. It has multiple signatures; in our case, it takes three parameters: the local port, the local address, and a callback function that runs when it starts listening for connections: ```js server.listen(PORT, "localhost", () => { @@ -72,7 +74,7 @@ server.listen(PORT, "localhost", () => { Run the file with the code, open your browser, and go to the address `localhost:3000/some/page`. Note that to run the server with different code, you need to stop it and restart it. You already know how to terminate a Node.js process. Only one server can be running on a port at a time. -In the `write` and `end` methods, you can pass a string containing HTML tags with inline styles. These tags will be correctly processed by the browser. +In the `write` and `end` methods, you can pass a string containing HTML tags with inline styles. These tags will be correctly processed by the browser: ```js const http = require("http"); @@ -99,4 +101,4 @@ By revisiting the page `localhost:3000/some/page`, you will see the rendered mar ## Project -[Github Application](../projects/github-app.md) +[Github App](../projects/github-app.md) diff --git a/stage1/modules/node-materials/node/module/npm-module.md b/stage1/modules/node-materials/node/module/npm-module.md index ac6b89da0..8e443e182 100644 --- a/stage1/modules/node-materials/node/module/npm-module.md +++ b/stage1/modules/node-materials/node/module/npm-module.md @@ -4,7 +4,7 @@ Let's install the `colors` module using `npm`. In the terminal, execute the command: -```powershell +```bash npm install colors ``` diff --git a/stage1/modules/node-materials/node/module/path.md b/stage1/modules/node-materials/node/module/path.md index d29a39181..35aafb895 100644 --- a/stage1/modules/node-materials/node/module/path.md +++ b/stage1/modules/node-materials/node/module/path.md @@ -14,25 +14,31 @@ You can find the information about the properties and methods of `path` in the [ Let's look at some of them: -- Retrieving file data - ```js - // for a file located at C:\Users\Admin\Desktop\nodejs-basic\index.js - const path = require("path"); - console.log(path.basename(__filename)); // index.js - file name on Windows, full file path on POSIX systems - console.log(path.dirname(__filename)); // C:\Users\Admin\Desktop\nodejs-basic - folder name - console.log(path.extname(__filename)); // .js - file extension - console.log(path.parse(__filename)); // returns an object specifying the disk root, folder name, file name, file extension, file name without extension - ``` -- Concatenating paths - ```js - // for a file located at C:\Users\Admin\Desktop\nodejs-basic\index.js - const path = require("path"); - // returns C:\Users\Admin\Desktop\nodejs-basic\test\second.html - console.log(path.join(__dirname, "test", "second.html")); - ``` - `path.join()` concatenates the specified path segments together, using the separator for the specific platform (forward slash `/` for Linux, backslash `\` for Windows). The result is a relative path. - ```js - const path = require("path"); - console.log(path.resolve(__dirname, "./test", "/second.html")); - ``` - `path.resolve()` converts a sequence of paths or path segments into an absolute path from right to left and normalizes it: if some path segments have slashes while others don't, it will still generate the correct path. +**Retrieving file data** + +```js +// for a file located at C:\Users\Admin\Desktop\nodejs-basic\index.js +const path = require("path"); +console.log(path.basename(__filename)); // index.js - file name on Windows, full file path on POSIX systems +console.log(path.dirname(__filename)); // C:\Users\Admin\Desktop\nodejs-basic - folder name +console.log(path.extname(__filename)); // .js - file extension +console.log(path.parse(__filename)); // returns an object specifying the disk root, folder name, file name, file extension, file name without extension +``` + +**Concatenating paths** + +```js +// for a file located at C:\Users\Admin\Desktop\nodejs-basic\index.js +const path = require("path"); +// returns C:\Users\Admin\Desktop\nodejs-basic\test\second.html +console.log(path.join(__dirname, "test", "second.html")); +``` + +`path.join()` concatenates the specified path segments together, using the separator for the specific platform (forward slash `/` for Linux, backslash `\` for Windows). The result is a relative path: + +```js +const path = require("path"); +console.log(path.resolve(__dirname, "./test", "/second.html")); +``` + +`path.resolve()` converts a sequence of paths or path segments into an absolute path from right to left and normalizes it: if some path segments have slashes while others don't, it will still generate the correct path. diff --git a/stage1/modules/node-materials/node/node-argv.md b/stage1/modules/node-materials/node/node-argv.md index d0c243041..c98a5a0b9 100644 --- a/stage1/modules/node-materials/node/node-argv.md +++ b/stage1/modules/node-materials/node/node-argv.md @@ -32,7 +32,7 @@ The `process.argv.slice(2)` method returns a new array that starts from the elem To have the ability to send arguments in any order or skip some of them, you can mark command line arguments. For this purpose, flags are used. Flags are words or symbols indicating that a command line argument follows them. Flags are usually preceded by one or two dashes to avoid confusion with arguments. For example: -```powershell +```bash node test -m Hello ``` @@ -80,7 +80,7 @@ obj.sayHi(); with the option `--disable-proto=throw`: -```powershell +```bash node --disable-proto=throw test ``` @@ -119,14 +119,14 @@ if (productionMode) { Write a program that prompts the user to enter two numbers, adds these numbers if launched with the `-s` flag, or multiplies them if launched with the `-m` flag, and then terminates. Use standard input/output for input and output. Here is an example of how it should work (user input starts with `>`): -```powershell +```bash > node test.js -m Enter 2 numbers > 2 7 2 * 7 = 14 ``` -```powershell +```bash > node test.js -s Enter 2 numbers > 2 7 @@ -150,7 +150,7 @@ stdin.on("data", (data) => { const numStringsArray = numString.split(" "); const hasIncorrectLength = numStringsArray.length !== 2; const hasIncorrectValues = numStringsArray.some((numStr) => - Number.isNaN(+numStr) + Number.isNaN(+numStr), ); if (hasIncorrectLength || hasIncorrectValues) { stdout.write("You need to enter 2 numbers separated by a space"); diff --git a/stage1/modules/node-materials/node/node-fs-access.md b/stage1/modules/node-materials/node/node-fs-access.md index 17d89df0d..fb487d189 100644 --- a/stage1/modules/node-materials/node/node-fs-access.md +++ b/stage1/modules/node-materials/node/node-fs-access.md @@ -11,7 +11,7 @@ console.log(__dirname); Open the terminal and run the file: -```powershell +```bash node test ``` diff --git a/stage1/modules/node-materials/node/node-introduction.md b/stage1/modules/node-materials/node/node-introduction.md index 4a3954ee8..9f4d8a263 100644 --- a/stage1/modules/node-materials/node/node-introduction.md +++ b/stage1/modules/node-materials/node/node-introduction.md @@ -4,7 +4,7 @@ ### Installing Node.js -Download link https://nodejs.org/en +Download link: https://nodejs.org/en Download and install the latest LTS version (Recommended For Most Users) @@ -54,7 +54,7 @@ console.log("Hello, world!"); Open this file with VS Code, in the terminal execute the command: -```powershell +```bash node test.js ``` diff --git a/stage1/modules/node-materials/node/node-module.md b/stage1/modules/node-materials/node/node-module.md index a01e99699..a0686d9aa 100644 --- a/stage1/modules/node-materials/node/node-module.md +++ b/stage1/modules/node-materials/node/node-module.md @@ -51,7 +51,7 @@ Packages are convenient to work with using package managers such as `npm` or `ya To install a package using `npm`, use the command: -```powershell +```bash npm install ``` @@ -76,7 +76,7 @@ A `package.json` file is created in the project folder, describing the created a ### Installing Packages via npm -To install a module, use the command +To install a module, use the command: ```bash npm install @@ -94,7 +94,8 @@ Removing a module: npm uninstall nodemon ``` -Installed modules are added to the `node_modules` folder, and information about them is added to the `package.json` file. Additionally, a `package-lock.json` file is automatically created, ensuring package identity among different users and performing other useful functions. +Installed modules are added to the `node_modules` folder, and information about them is added to the `package.json` file. +Additionally, a `package-lock.json` file is automatically created, ensuring package identity among different users and performing other useful functions. If you delete the `node_modules` folder and execute the `npm install` command, the `node_modules` folder will be restored along with all the added modules based on the records in the `package.json` file. diff --git a/stage1/modules/node-materials/node/node-stdio.md b/stage1/modules/node-materials/node/node-stdio.md index 474cb5f14..fc5aa7f0b 100644 --- a/stage1/modules/node-materials/node/node-stdio.md +++ b/stage1/modules/node-materials/node/node-stdio.md @@ -96,7 +96,7 @@ stdin.on("data", (data) => { }); ``` -If we log the type of the `data` variable to the console, we will see `object`. By applying the [trick with the special `[[Class]]` property](https://learn.javascript.ru/class-instanceof#sekretnoe-svoystvo-class), we get `[object Uint8Array]` for `data`. +If we log the type of the `data` variable to the console, we will see `object`. By applying the [trick with the toString() method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString#using_tostring_to_detect_object_class), we get `[object Uint8Array]` for `data`. Since `process.stdin` is a stream, it works with data in **binary** format. To handle such data in Node.js, there is a special `Buffer` object, which is a subclass of `Uint8Array` (a typed array storing 8-bit unsigned integer values). The data contained in the `Buffer` can be converted to a string: diff --git a/stage1/modules/node-materials/node/projects/github-app.md b/stage1/modules/node-materials/node/projects/github-app.md index 805bbbb90..0886df309 100644 --- a/stage1/modules/node-materials/node/projects/github-app.md +++ b/stage1/modules/node-materials/node/projects/github-app.md @@ -8,7 +8,7 @@ To achieve this, we'll use the GitHub API. The request `https://api.github.com/u 1. Start by creating a new Node.js application -Create a folder named github-app, open it in VS Code, and run the following command in the terminal: +Create a folder named `github-app`, open it in VS Code, and run the following command in the terminal: ``` npm init -y @@ -35,6 +35,7 @@ const username = process.argv[2]; Use the imported `github` object, which has a `getRepos()` property - a function that returns a list of user repositories. The parameters of the `getRepos()` function are `username` - the username and a callback function taking two parameters: `error` - an error, and `repos` - the received data, in our case, a list of repositories. + In the callback function, let's handle the error and print the names of the repositories to the console: ```js @@ -49,11 +50,11 @@ github.getRepos(username, (error, repos) => { Our application will communicate with the server over the `https` protocol. For this, Node.js has a built-in `https module`, similar to the [HTTP Module](../module/http.md). -To send a request to the API, we'll use the `get()` method, which allows us to retrieve data from the server. +To send a request to the API, we'll use the `get()` method, which allows us to retrieve data from the server. Import the `https` module and write the code for the `getRepos()` function. The function parameter is `username` - the GitHub username. The `https.get()` method has two parameters: the URL to which the request is sent and a callback function taking one parameter - the server response, abbreviated as `res`. -The `res.statusCode` property returns the server response. A response of `200` indicates a successful connection, while any other response indicates a connection problem. +The `res.statusCode` property returns the server response. A response of `200` indicates a successful connection, while any other response indicates a connection problem. Export the `github` module as an object with a `getRepos` property and the value of `getRepos`: ```js @@ -94,7 +95,8 @@ Use the `option` object as the first parameter in `the https.get` method. The ap 4. Handling incoming data -Almost everything in Node.js, including communication with the server, is implemented asynchronously, using events and streams. Information from the server comes in parts. +Almost everything in Node.js, including communication with the server, is implemented asynchronously, using events and streams. Information from the server comes in parts. + The server response (`res`) has a `data` event, which fires when a part of the requested information comes from the server. Subscribe to this event and print the received data to the console: ```js @@ -133,7 +135,8 @@ function getRepos(username) { } ``` -The response (`res`) method has an `end` event, which triggers when the data transmission is complete. +The response (`res`) method has an `end` event, which triggers when the data transmission is complete. + Upon the occurrence of this event, use the `JSON.parse(body)` method to convert the received data into an array: ```js @@ -186,6 +189,8 @@ function getRepos(username, done) { 6. Error Handling +When working with the application, errors may occur in the following cases: + - The application is launched without a username - An error occurs when sending a request if a nonexistent username is specified - An error occurs when receiving a response from the server @@ -199,7 +204,7 @@ Handle errors in the `github` module and pass them to the `app` module, specifyi if (!username) return done(new Error("Username is required")); ``` -2. Request error - the `error` event of the `request` method. +2. Request error - the `error` event of the `request` method Create a variable `request` and set its value to the `https.get()` method: @@ -253,8 +258,8 @@ function getRepos(username, done) { } else { done( new Error( - `Error working with the server ${res.statusCode} ${res.statusMessage}` - ) + `Error working with the server ${res.statusCode} ${res.statusMessage}`, + ), ); } }); diff --git a/stage1/modules/node-materials/node/projects/notes.md b/stage1/modules/node-materials/node/projects/notes.md index b57ecc7a7..dc1fbf027 100644 --- a/stage1/modules/node-materials/node/projects/notes.md +++ b/stage1/modules/node-materials/node/projects/notes.md @@ -2,17 +2,17 @@ [HOME](../../README.md) -Let's write a simple console application called Notes for working with notes. The application needs to implement four methods: +Let's write a simple console application called Notes for working with notes. The application needs to implement 4 methods: - `create` - `list` - `view` - `remove` -The `create` method creates a new note in the notes.json file. The `create` method has two arguments: the note's title and its content. -The `list` method displays a list of notes. -The `view` method outputs the content of a note whose title is passed as an argument. -The `remove` method deletes a note whose title is passed as an argument. +1. The `create` method creates a new note in the notes.json file. The `create` method has two arguments: the note's title and its content. +2. The `list` method displays a list of notes. +3. The `view` method outputs the content of a note whose title is passed as an argument. +4. The `remove` method deletes a note whose title is passed as an argument. To call these methods, they are specified as command-line arguments. @@ -66,11 +66,6 @@ The function works, but it doesn't add data; instead, it replaces them. So, we need to: -- прочитать уже имеющиеся данные из файла `'notes.json'` при помощи метода `fs.readFile()` -- преобразовать полученные данные в массив при помощи метода `JSON.parse()` -- дополнить массив новыми данными при помощи метода `.push()` -- преобразовать массив в JSON при помощи метода `JSON.stringify()` -- записать данные в файл `'notes.json'` при помощи метода `fs.writeFile()` - Read the existing data from the `notes.json` file using the `fs.readFile()` method - Convert the received data into an array using the `JSON.parse()` method - Add new data to the array using the `.push()` method @@ -95,7 +90,7 @@ function create(title, content) { Run the file with the command: -```powershell +```bash node index create title content ``` diff --git a/stage1/modules/node-materials/node/projects/timer.md b/stage1/modules/node-materials/node/projects/timer.md index 6abdc31c3..90ec206f8 100644 --- a/stage1/modules/node-materials/node/projects/timer.md +++ b/stage1/modules/node-materials/node/projects/timer.md @@ -2,8 +2,9 @@ [HOME](../../README.md) -Directly creating objects based on the `EventEmitter` class is extremely rare. +Directly creating objects based on the `EventEmitter` class is extremely rare. More often, the interface for working with events is added to other objects. This is done through inheritance. + Let's create a class called `Timer` that will inherit from `EventEmitter`, and, as a result, its instances will have the `emit()` and `on()` methods: ```js diff --git a/stage1/modules/node-materials/node/stream-readable.md b/stage1/modules/node-materials/node/stream-readable.md index ec31489ec..6cd5094ea 100644 --- a/stage1/modules/node-materials/node/stream-readable.md +++ b/stage1/modules/node-materials/node/stream-readable.md @@ -44,9 +44,6 @@ let data = ""; stream.on("data", (chunk) => (data += chunk)); ``` -Так как мы имеем дело с потоком данных, нам нужно знать когда поток завершится. Для этого у стрима есть событие `'end'`. Это событие срабатывает когда все данные уже переданы. -При наступлении события `'end'` выведем в консоль сообщение и длину полученных данных: - Since we are dealing with a data stream, we need to know when the stream will end. For this, the stream has an `end` event. This event is triggered when all the data has already been passed. When the `end` event occurs, we'll output a message to the console and the length of the received data: diff --git a/stage1/modules/node-materials/node/stream.md b/stage1/modules/node-materials/node/stream.md index 30eb38875..d366ea34e 100644 --- a/stage1/modules/node-materials/node/stream.md +++ b/stage1/modules/node-materials/node/stream.md @@ -2,7 +2,7 @@ [HOME](../README.md) -![stream schema](https://pawelgrzybek.com/photos/2020-07-14-1.png) +![stream schema](images/stream-schema.png) When working with a significant amount of data, processing it all at once can lead to loading the entire dataset into memory and freezing the program for the entire duration of the operation. From 03ded65ef4ac97f271aba4e96491ecd2334d9db4 Mon Sep 17 00:00:00 2001 From: Lizaveta Date: Sat, 18 Nov 2023 07:45:54 +0200 Subject: [PATCH 7/9] feat: update module materials --- stage1/modules/node-materials/README.md | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/stage1/modules/node-materials/README.md b/stage1/modules/node-materials/README.md index 72bbdb61f..4f6eff7c3 100644 --- a/stage1/modules/node-materials/README.md +++ b/stage1/modules/node-materials/README.md @@ -38,14 +38,12 @@ Node.js has its drawbacks, particularly in scenarios demanding intensive calcula - [Timer App](node/projects/timer.md) - [Github App](node/projects/github-app.md) -## Материалы - -// TODO: revise materials - -- [Введение в node.js](http://imnotgenius.com/vvedeniya-v-node-js/) -- [Гайд по Node.js](https://nodejsdev.ru/guide/) -- [Скринкаст по Node.js](https://learn.javascript.ru/screencast/nodejs) -- [Создание первого приложения на Node](https://webref.ru/dev/first-node-app) -- [Руководство по Node.js - metanit](https://metanit.com/web/nodejs/) -- [Пишем API на Node.js](https://www.youtube.com/playlist?list=PLY4rE9dstrJzrDaSPKOrhNgQ19GhVl19u) -- [Герман Волков. Node.js](https://youtu.be/qZ5xzkEdkhg) - [Презентация](https://drive.google.com/file/d/1P3mRxOQISJHEatmAEv5X_f1Qk8OEr9rZ/view) +## Materials + +- [How to Get Started with NodeJS](https://www.freecodecamp.org/news/get-started-with-nodejs/) +- [Introduction to NodeJS](https://youtu.be/JZXQ455OT3A?si=9bmAsMq0PDKB-4QA) +- [The Absolute Beginner's Guide to Node.js](https://www.cloudbees.com/blog/node-js-tutorial) +- [Node.js Tutorial for Beginners](https://youtu.be/-u-j7uqU7sI?si=LOEicYHfrPmxwftO) +- [Build a Node.js Project from scratch](https://anotheruiguy.gitbooks.io/nodeexpreslibsass_from-scratch/content/) +- [NodeJS Tutorial](https://www.geeksforgeeks.org/nodejs/) +- [Node.js Full Course for Beginners](https://youtu.be/f2EqECiTBL8?si=WosH_b9hgkH7MPqj) From c430f4f72dc7cf958d4e17c36f0be1bd634d59c5 Mon Sep 17 00:00:00 2001 From: Lizaveta Date: Sat, 9 Dec 2023 14:24:59 +0200 Subject: [PATCH 8/9] refactor: specify wordings --- stage1/modules/node-materials/node/module/fs.md | 8 ++++---- stage1/modules/node-materials/node/node-argv.md | 5 +++-- stage1/modules/node-materials/node/node-fs-access.md | 2 +- stage1/modules/node-materials/node/projects/github-app.md | 4 ++-- stage1/modules/node-materials/node/projects/notes.md | 6 +++--- 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/stage1/modules/node-materials/node/module/fs.md b/stage1/modules/node-materials/node/module/fs.md index 5badda864..b0fd51b6f 100644 --- a/stage1/modules/node-materials/node/module/fs.md +++ b/stage1/modules/node-materials/node/module/fs.md @@ -41,7 +41,7 @@ fs.writeFile( (err) => { if (err) throw err; console.log("File was created"); - }, + } ); ``` @@ -57,7 +57,7 @@ fs.appendFile( (err) => { if (err) throw err; console.log("File was modified"); - }, + } ); ``` @@ -73,7 +73,7 @@ fs.readFile( (err, data) => { if (err) throw err; console.log(data); - }, + } ); ``` @@ -89,7 +89,7 @@ fs.rename( (err) => { if (err) throw err; console.log("File renamed"); - }, + } ); ``` diff --git a/stage1/modules/node-materials/node/node-argv.md b/stage1/modules/node-materials/node/node-argv.md index c98a5a0b9..76fcf26f1 100644 --- a/stage1/modules/node-materials/node/node-argv.md +++ b/stage1/modules/node-materials/node/node-argv.md @@ -117,7 +117,8 @@ if (productionMode) { ### Task -Write a program that prompts the user to enter two numbers, adds these numbers if launched with the `-s` flag, or multiplies them if launched with the `-m` flag, and then terminates. Use standard input/output for input and output. Here is an example of how it should work (user input starts with `>`): +Develop a program that prompts the user to enter two numbers. It should add these numbers if executed with the `-s ` flag, or multiply them if executed with the `-m` flag. After performing the operation, the program should then terminate. +Employ standard input/output streams for data input and output. Below is an example of how it should work (user input starts with `>`): ```bash > node test.js -m @@ -150,7 +151,7 @@ stdin.on("data", (data) => { const numStringsArray = numString.split(" "); const hasIncorrectLength = numStringsArray.length !== 2; const hasIncorrectValues = numStringsArray.some((numStr) => - Number.isNaN(+numStr), + Number.isNaN(+numStr) ); if (hasIncorrectLength || hasIncorrectValues) { stdout.write("You need to enter 2 numbers separated by a space"); diff --git a/stage1/modules/node-materials/node/node-fs-access.md b/stage1/modules/node-materials/node/node-fs-access.md index fb487d189..ef13f79ac 100644 --- a/stage1/modules/node-materials/node/node-fs-access.md +++ b/stage1/modules/node-materials/node/node-fs-access.md @@ -28,7 +28,7 @@ _To repeat the last entered command in the terminal, just press the "↑" key on ### Task -Write a program that returns the path to the folder if it is launched with the `-d` flag, or the path to the file if it is launched with the `-f` flag. If the file is launched without a flag or with a flag other than those specified in the task, a suggestion to run the program with the `-d` or `-f` flag is displayed. +Develop a program that returns the path to the folder if it is launched with the `-d` flag, or the path to the file if it is launched with the `-f` flag. If the file is launched without a flag or with a flag other than those specified in the task, a suggestion to run the program with the `-d` or `-f` flag is displayed.
Example Solution diff --git a/stage1/modules/node-materials/node/projects/github-app.md b/stage1/modules/node-materials/node/projects/github-app.md index 0886df309..82e613b85 100644 --- a/stage1/modules/node-materials/node/projects/github-app.md +++ b/stage1/modules/node-materials/node/projects/github-app.md @@ -258,8 +258,8 @@ function getRepos(username, done) { } else { done( new Error( - `Error working with the server ${res.statusCode} ${res.statusMessage}`, - ), + `Error working with the server ${res.statusCode} ${res.statusMessage}` + ) ); } }); diff --git a/stage1/modules/node-materials/node/projects/notes.md b/stage1/modules/node-materials/node/projects/notes.md index dc1fbf027..cb64d0c99 100644 --- a/stage1/modules/node-materials/node/projects/notes.md +++ b/stage1/modules/node-materials/node/projects/notes.md @@ -112,11 +112,11 @@ function list() { } ``` -Since the `notes.json` file is in the same directory as the code file, there is no need to specify the path; it's enough to provide its name as the first argument to the `readFile()` method. The second argument of the method is a callback function that takes two parameters - an error (`error`) and the data read from the file (`data`). +Since the `notes.json` file is located in the same directory as the code file, there's no need to specify its full path. It's enough to provide its name as the first argument to the `readFile()` method. The second argument of this method is a callback function, which accepts two parameters: an `error` for any encountered errors, and the `data` read from the file. Converting the received data into an array is done using the `JSON.parse(data)` method. We iterate through this array using the `forEach()` method and, for each element, print its index and the title of the note to the console, creating a numbered list. To start the list from one instead of zero, we add 1 to the index. -6. Write the `view()` function, which outputs the content of a note by its title +6. Implement the `view()` function, which displays the content of a note based on its title. The function is similar to the `list()` function. It reads the document, converts the received data into an array, and then uses the `find()` method to find the note whose title matches the one specified when calling the function. If there is no such note, it prints a message that the note is not found; otherwise, it prints its content: @@ -136,7 +136,7 @@ function view(title) { } ``` -7. Write the `remove()` function, which deletes a note by its title +7. Implement the `remove()` function, which deletes a note by its title. Just like in the previous functions, we read the file, convert the received data into an array, then use the `filter()` method to filter the data, leaving only those whose titles do not match the passed title. After that, we convert the array to JSON and write it to the file: From f783aad13060fd50847a4f81869ebc689f31a4df Mon Sep 17 00:00:00 2001 From: Lizaveta Date: Sat, 9 Dec 2023 14:29:28 +0200 Subject: [PATCH 9/9] chore: run prettier --- stage1/modules/node-materials/node/module/fs.md | 8 ++++---- stage1/modules/node-materials/node/node-argv.md | 2 +- stage1/modules/node-materials/node/projects/github-app.md | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/stage1/modules/node-materials/node/module/fs.md b/stage1/modules/node-materials/node/module/fs.md index b0fd51b6f..5badda864 100644 --- a/stage1/modules/node-materials/node/module/fs.md +++ b/stage1/modules/node-materials/node/module/fs.md @@ -41,7 +41,7 @@ fs.writeFile( (err) => { if (err) throw err; console.log("File was created"); - } + }, ); ``` @@ -57,7 +57,7 @@ fs.appendFile( (err) => { if (err) throw err; console.log("File was modified"); - } + }, ); ``` @@ -73,7 +73,7 @@ fs.readFile( (err, data) => { if (err) throw err; console.log(data); - } + }, ); ``` @@ -89,7 +89,7 @@ fs.rename( (err) => { if (err) throw err; console.log("File renamed"); - } + }, ); ``` diff --git a/stage1/modules/node-materials/node/node-argv.md b/stage1/modules/node-materials/node/node-argv.md index 76fcf26f1..d5ac6024f 100644 --- a/stage1/modules/node-materials/node/node-argv.md +++ b/stage1/modules/node-materials/node/node-argv.md @@ -151,7 +151,7 @@ stdin.on("data", (data) => { const numStringsArray = numString.split(" "); const hasIncorrectLength = numStringsArray.length !== 2; const hasIncorrectValues = numStringsArray.some((numStr) => - Number.isNaN(+numStr) + Number.isNaN(+numStr), ); if (hasIncorrectLength || hasIncorrectValues) { stdout.write("You need to enter 2 numbers separated by a space"); diff --git a/stage1/modules/node-materials/node/projects/github-app.md b/stage1/modules/node-materials/node/projects/github-app.md index 82e613b85..0886df309 100644 --- a/stage1/modules/node-materials/node/projects/github-app.md +++ b/stage1/modules/node-materials/node/projects/github-app.md @@ -258,8 +258,8 @@ function getRepos(username, done) { } else { done( new Error( - `Error working with the server ${res.statusCode} ${res.statusMessage}` - ) + `Error working with the server ${res.statusCode} ${res.statusMessage}`, + ), ); } });