{"id":332387,"date":"2022-04-25T15:00:38","date_gmt":"2022-04-25T15:00:38","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=332387"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=332387","title":{"rendered":"<span>Node.js: \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u043f\u0430\u043a\u0435\u0442\u043d\u044b\u0439 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440<\/span>"},"content":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/4-\/hn\/8w\/4-hn8wkkaeafijehpfybkgcounq.png\" data-src=\"https:\/\/habrastorage.org\/webt\/4-\/hn\/8w\/4-hn8wkkaeafijehpfybkgcounq.png\"\/>  <\/p>\n<p>  <\/p>\n<p>  <\/p>\n<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u0434\u0440\u0443\u0437\u044c\u044f!<\/p>\n<p>  <\/p>\n<p>\u0412\u0430\u043c \u043a\u043e\u0433\u0434\u0430-\u043d\u0438\u0431\u0443\u0434\u044c \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0443\u0437\u043d\u0430\u0442\u044c, \u043a\u0430\u043a \u043f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442\u043e\u043c \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 <a href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%A1%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0_%D1%83%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D1%8F_%D0%BF%D0%B0%D0%BA%D0%B5%D1%82%D0%B0%D0%BC%D0%B8\">\u043f\u0430\u043a\u0435\u0442\u043d\u044b\u0435 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440\u044b<\/a> (Package Manager, PM) \u2014 <a href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%98%D0%BD%D1%82%D0%B5%D1%80%D1%84%D0%B5%D0%B9%D1%81_%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%BD%D0%BE%D0%B9_%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8\">\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0438<\/a> (Command Line Interface, CLI) \u0434\u043b\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 \u043d\u0430\u043f\u043e\u0434\u043e\u0431\u0438\u0435 <a href=\"https:\/\/www.npmjs.com\/\">npm<\/a> \u0438\u043b\u0438 <a href=\"https:\/\/yarnpkg.com\/\">yarn<\/a>? \u0415\u0441\u043b\u0438 \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c, \u0442\u043e\u0433\u0434\u0430 \u044d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f \u0434\u043b\u044f \u0432\u0430\u0441.<\/p>\n<p>  <\/p>\n<p>\u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u0435 \u043c\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u043f\u0430\u043a\u0435\u0442\u043d\u044b\u0439 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440 \u043d\u0430 <a href=\"https:\/\/nodejs.org\/\">Node.js<\/a> \u0438 <a href=\"https:\/\/www.typescriptlang.org\/\">TypeScript<\/a>. \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043e\u0431\u0440\u0430\u0437\u0446\u0430 \u0434\u043b\u044f \u043f\u043e\u0434\u0440\u0430\u0436\u0430\u043d\u0438\u044f \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <em>yarn<\/em>. \u0415\u0441\u043b\u0438 \u0432\u044b \u043d\u0435 \u0437\u043d\u0430\u043a\u043e\u043c\u044b \u0441 <em>TS<\/em>, \u0441\u043e\u0432\u0435\u0442\u0443\u044e \u0432\u0437\u0433\u043b\u044f\u043d\u0443\u0442\u044c \u043d\u0430 <a href=\"https:\/\/my-js.org\/docs\/guide\/ts\">\u044d\u0442\u0443 \u043a\u0430\u0440\u043c\u0430\u043d\u043d\u0443\u044e \u043a\u043d\u0438\u0433\u0443<\/a>.<\/p>\n<p>  <\/p>\n<p>\u041d\u0430\u0448 <em>CLI<\/em> \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f <em>my-yarn<\/em>. \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 <a href=\"https:\/\/habr.com\/ru\/company\/domclick\/blog\/513130\/\">lock-\u0444\u0430\u0439\u043b\u0430<\/a> (<em>yarn.lock<\/em>, <em>package-lock.json<\/em>) \u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0444\u0430\u0439\u043b <em>my-yarn.yml<\/em>.<\/p>\n<p>  <\/p>\n<p><a href=\"https:\/\/github.com\/g-plane\/tiny-package-manager\">\u0418\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u0432\u0434\u043e\u0445\u043d\u043e\u0432\u0435\u043d\u0438\u044f<\/a>.<\/p>\n<p>  <\/p>\n<p><a href=\"https:\/\/github.com\/harryheman\/Blog-Posts\/tree\/master\/ts-package-manager\">\u041a\u043e\u0434 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/a>.<\/p>\n<p><a name=\"habracut\"><\/a>  <\/p>\n<p>\u0412 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 <code>CLI<\/code> \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0445 <code>npm-\u043f\u0430\u043a\u0435\u0442\u043e\u0432<\/code>. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043d\u0430\u0447\u043d\u0435\u043c \u043d\u0430\u0448\u0435 \u043f\u0443\u0442\u0435\u0448\u0435\u0441\u0442\u0432\u0438\u0435 \u0441 \u043a\u0440\u0430\u0442\u043a\u043e\u0433\u043e \u0437\u043d\u0430\u043a\u043e\u043c\u0441\u0442\u0432\u0430 \u0441 \u043d\u0438\u043c\u0438.<\/p>\n<p>  <\/p>\n<h2 id=\"pakety\">\u041f\u0430\u043a\u0435\u0442\u044b<\/h2>\n<p>  <\/p>\n<h3 id=\"find-uphttpswwwnpmjscompackagefind-up\"><a href=\"https:\/\/www.npmjs.com\/package\/find-up\">find-up<\/a><\/h3>\n<p>  <\/p>\n<p><em>find-up<\/em> \u2014 \u044d\u0442\u043e \u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u0444\u0430\u0439\u043b\u0430 \u0438\u043b\u0438 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u0432 \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0445 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044f\u0445.<\/p>\n<p>  <\/p>\n<p><strong>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430<\/strong><\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">yarn add find-up<\/code><\/pre>\n<p>  <\/p>\n<p><strong>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435<\/strong><\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import { findUp } from 'find-up' import fs from 'fs-extra'  \/\/ \u043d\u0430\u0445\u043e\u0434\u0438\u043c \u0444\u0430\u0439\u043b `package.json` (\u043f\u0443\u0442\u044c \u043a \u043d\u0435\u043c\u0443) const filePath = await findUp('package.json') \/\/ \u0447\u0438\u0442\u0430\u0435\u043c \u0435\u0433\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u043a\u0430\u043a `JSON` const fileContent = await fs.readJson(filePath)<\/code><\/pre>\n<p>  <\/p>\n<h3 id=\"fs-extrahttpswwwnpmjscompackagefs-extra\"><a href=\"https:\/\/www.npmjs.com\/package\/fs-extra\">fs-extra<\/a><\/h3>\n<p>  <\/p>\n<p><em>fs-extra<\/em> \u2014 \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e <a href=\"https:\/\/nodejs.org\/api\/fs.html\">fs<\/a> \u043d\u0430 \u0441\u0442\u0435\u0440\u043e\u0438\u0434\u0430\u0445.<\/p>\n<p>  <\/p>\n<p><strong>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430<\/strong><\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">yarn add fs-extra<\/code><\/pre>\n<p>  <\/p>\n<h3 id=\"js-yamlhttpswwwnpmjscompackagejs-yaml\"><a href=\"https:\/\/www.npmjs.com\/package\/js-yaml\">js-yaml<\/a><\/h3>\n<p>  <\/p>\n<p><em>js-yaml<\/em> \u2014 \u044d\u0442\u043e \u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u0440\u0430\u0437\u0431\u043e\u0440\u0430 (\u043f\u0430\u0440\u0441\u0438\u043d\u0433\u0430) \u0444\u0430\u0439\u043b\u0430 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 <a href=\"https:\/\/ru.wikipedia.org\/wiki\/YAML\">YAML<\/a> \u0432 \u043e\u0431\u044a\u0435\u043a\u0442 \u0438 \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u0432 <code>yaml<\/code>.<\/p>\n<p>  <\/p>\n<p><strong>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430<\/strong><\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">yarn add js-yaml<\/code><\/pre>\n<p>  <\/p>\n<p><strong>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435<\/strong><\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import { findUp } from 'find-up' import fs from 'fs-extra' import yaml from 'js-yaml'  const filePath = await findUp('my-yarn.yml') const fileContent = await fs.readFile(filePath, 'utf-8') \/\/ \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0435\u043c \u0444\u0430\u0439\u043b \/\/ \u043c\u0435\u0442\u043e\u0434 `load` \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0441\u0442\u0440\u043e\u043a\u0443 \u0438 \u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 const fileObj = yaml.load(fileContent)  \/\/ \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0444\u0430\u0439\u043b \/\/ \u043c\u0435\u0442\u043e\u0434 `dump` \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043e\u0431\u044a\u0435\u043a\u0442 c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u044b\u043c \u0444\u0430\u0439\u043b\u0430 \u0438 \u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 await fs.writeFile(   filePath,   yaml.dump(fileObj, { noRefs: true, sortKeys: true }) ) \/\/ `noRefs: true` \u0437\u0430\u043f\u0440\u0435\u0449\u0430\u0435\u0442 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0434\u0443\u0431\u043b\u0438\u0440\u0443\u044e\u0449\u0438\u0445\u0441\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0432 \u0441\u0441\u044b\u043b\u043a\u0438 \/\/ `sortKeys: true` \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0443 \u043a\u043b\u044e\u0447\u0435\u0439 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u043f\u0440\u0438 \u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u0444\u0430\u0439\u043b\u0430<\/code><\/pre>\n<p>  <\/p>\n<h3 id=\"log-updatehttpswwwnpmjscompackagelog-update\"><a href=\"https:\/\/www.npmjs.com\/package\/log-update\">log-update<\/a><\/h3>\n<p>  <\/p>\n<p><em>log-update<\/em> \u2014 \u044d\u0442\u043e \u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u0432\u044b\u0432\u043e\u0434\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u0432 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b \u0441 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u044c\u044e \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0433\u043e \u0432\u044b\u0432\u043e\u0434\u0430. \u041c\u043e\u0436\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433\u0430 \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u043e\u0432 \u043f\u0440\u043e\u0433\u0440\u0435\u0441\u0441\u0430, \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0438 \u0438 \u0434\u0440.<\/p>\n<p>  <\/p>\n<p><strong>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430<\/strong><\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">yarn add log-update<\/code><\/pre>\n<p>  <\/p>\n<p><strong>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435<\/strong><\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import logUpdate from 'log-update'  export function logResolving(pkgName: string) {   logUpdate(`[1\/2] Resolving: ${pkgName}`) }<\/code><\/pre>\n<p>  <\/p>\n<h3 id=\"node-fetchhttpswwwnpmjscompackagenode-fetch\"><a href=\"https:\/\/www.npmjs.com\/package\/node-fetch\">node-fetch<\/a><\/h3>\n<p>  <\/p>\n<p><em>node-fetch<\/em> \u2014 \u044d\u0442\u043e \u043e\u0431\u0435\u0440\u0442\u043a\u0430 \u043d\u0430\u0434 <a href=\"https:\/\/developer.mozilla.org\/ru\/docs\/Web\/API\/Fetch_API\">Fetch API<\/a> \u0434\u043b\u044f <code>Node.js<\/code>.<\/p>\n<p>  <\/p>\n<p><strong>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430<\/strong><\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">yarn add node-fetch<\/code><\/pre>\n<p>  <\/p>\n<h3 id=\"progresshttpswwwnpmjscompackageprogress\"><a href=\"https:\/\/www.npmjs.com\/package\/progress\">progress<\/a><\/h3>\n<p>  <\/p>\n<p><em>progress<\/em> \u2014 \u044d\u0442\u043e \u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u043e\u0432 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438, \u0441\u043e\u0441\u0442\u043e\u044f\u0449\u0438\u0445 \u0438\u0437 <a href=\"https:\/\/ru.wikipedia.org\/wiki\/ASCII\">ASCII-\u0441\u0438\u043c\u0432\u043e\u043b\u043e\u0432<\/a>.<\/p>\n<p>  <\/p>\n<p><strong>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430<\/strong><\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">yarn add progress<\/code><\/pre>\n<p>  <\/p>\n<p><strong>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435<\/strong><\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import logUpdate from 'log-update' import ProgressBar from 'progress'  export function prepareInstall(total: number) {   logUpdate('[1\/2] Finished resolving.')   \/\/ \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0441\u0442\u0440\u043e\u043a\u0443 \u0441 \u0442\u043e\u043a\u0435\u043d\u0430\u043c\u0438 \u0438 \u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438   progress = new ProgressBar('[2\/2] Installing [:bar]', {     \/\/ \u0441\u0438\u043c\u0432\u043e\u043b \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f     complete: '#',     \/\/ \u043e\u0431\u0449\u0435\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0442\u0438\u043a\u043e\u0432 (ticks)     total   }) }<\/code><\/pre>\n<p>  <\/p>\n<h3 id=\"semverhttpswwwnpmjscompackagesemver\"><a href=\"https:\/\/www.npmjs.com\/package\/semver\">semver<\/a><\/h3>\n<p>  <\/p>\n<p><em>semver<\/em> \u2014 \u044d\u0442\u043e \u0441\u0435\u043c\u0430\u043d\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 &#171;\u0432\u0435\u0440\u0441\u0438\u043e\u043d\u0435\u0440&#187; (semantic versioner) \u0434\u043b\u044f <code>npm<\/code>.<\/p>\n<p>  <\/p>\n<p><strong>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430<\/strong><\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">yarn add semver<\/code><\/pre>\n<p>  <\/p>\n<p><strong>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435<\/strong><\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import semver from 'semver'  const versions = ['1.0.0', '3.0.0', '5.0.0'] const range = '2.0.0 - 4.0.0'  \/\/ \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u0431\u043b\u0438\u0437\u043a\u0443\u044e \u043a \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u0443 \u0432\u0435\u0440\u0441\u0438\u044e \u0438\u043b\u0438 `null` semver.maxSatisfying(versions, range) \/\/ 3.0.0  \/\/ \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 `true`, \u0435\u0441\u043b\u0438 \u0432\u0435\u0440\u0441\u0438\u044f \u0443\u0434\u043e\u0432\u043b\u0435\u0442\u0432\u043e\u0440\u044f\u0435\u0442 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u0443 semver.satisfies(versions[1], range) \/\/ true<\/code><\/pre>\n<p>  <\/p>\n<h3 id=\"tarhttpswwwnpmjscompackagetar\"><a href=\"https:\/\/www.npmjs.com\/package\/tar\">tar<\/a><\/h3>\n<p>  <\/p>\n<p><em>tar<\/em> \u2014 \u044d\u0442\u043e \u043e\u0431\u0435\u0440\u0442\u043a\u0430 \u043d\u0430\u0434 <a href=\"https:\/\/ru.wikipedia.org\/wiki\/Tar\">tar<\/a> \u0434\u043b\u044f <code>Node.js<\/code>.<\/p>\n<p>  <\/p>\n<p><strong>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430<\/strong><\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">yarn add tar<\/code><\/pre>\n<p>  <\/p>\n<p><strong>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435<\/strong><\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import fetch from 'node-fetch' import fs from 'fs-extra' import tar from 'tar'  \/\/ \u0430\u0434\u0440\u0435\u0441 \u0440\u0435\u0435\u0441\u0442\u0440\u0430 const REGISTRY_URI = 'https:\/\/registry.npmjs.org' \/\/ \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u0430\u043a\u0435\u0442\u0430 const pkgName = 'nodemon' \/\/ \u043f\u0443\u0442\u044c \u043a \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u0434\u043b\u044f \u043f\u0430\u043a\u0435\u0442\u0430 const dirPath = `${process.cwd()}\/node_modules\/${pkgName}`  \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u0430\u043a\u0435\u0442\u0435 const pkgJson = await (await fetch(`${REGISTRY_URI}\/${pkgName}`)).json() \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044e\u044e \u0432\u0435\u0440\u0441\u0438\u044e \u043f\u0430\u043a\u0435\u0442\u0430 const latestVersion = Object.keys(pkgJson.versions).at(-1) \/\/ \u043f\u0443\u0442\u044c \u043a \u0442\u0430\u0440\u0431\u0430\u043b\u0443 (tarball) \u043f\u0430\u043a\u0435\u0442\u0430 const tarUrl = pkgJson.versions[latestVersion].dist.tarball  \/\/ \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e \u0434\u043b\u044f \u043f\u0430\u043a\u0435\u0442\u0430 \u043f\u0440\u0438 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0438 if (!(await fs.pathExists(dirPath))) {   await fs.mkdirp(dirPath) }  \/\/ \u0442\u0435\u043b\u043e \u043e\u0442\u0432\u0435\u0442\u0430 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u043e\u0431\u043e\u0439 \u043f\u043e\u0442\u043e\u043a \u0434\u0430\u043d\u043d\u044b\u0445 (application\/octet-stream), \/\/ \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0439 \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f const { body: tarReadableStream } = await fetch(tarUrl) tarReadableStream   \/\/ \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u043f\u0430\u043a\u0435\u0442\u0430   \/\/ `cwd` - \u043f\u0443\u0442\u044c \u043a \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438   \/\/ `strip` - \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0432\u0435\u0434\u0443\u0449\u0438\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u043f\u0443\u0442\u0438 (leading path elements) \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f   .pipe(tar.extract({ cwd: dirPath, strip: 1 }))   .on('close', () =>     console.log(`Package ${pkgName} has been successfully extracted.`)   )<\/code><\/pre>\n<p>  <\/p>\n<h3 id=\"yargshttpswwwnpmjscompackageyargs\"><a href=\"https:\/\/www.npmjs.com\/package\/yargs\">yargs<\/a><\/h3>\n<p>  <\/p>\n<p><em>yargs<\/em> \u2014 \u044d\u0442\u043e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 <code>CLI<\/code> \u0441 \u043e\u0442\u043b\u0438\u0447\u043d\u043e\u0439 <a href=\"https:\/\/yargs.js.org\/\">\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439<\/a>.<\/p>\n<p>  <\/p>\n<p><strong>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430<\/strong><\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">yarn add yargs<\/code><\/pre>\n<p>  <\/p>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 <code>yargs<\/code> \u2014 \u0442\u0435\u043c\u0430 \u0434\u043b\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438. \u042f \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043e\u0431 \u044d\u0442\u043e\u043c, \u043a\u043e\u0433\u0434\u0430 \u043c\u044b \u0434\u043e\u0439\u0434\u0435\u043c \u0434\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0439 \u0447\u0430\u0441\u0442\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<p>  <\/p>\n<h2 id=\"podgotovka-i-nastroyka-proekta\">\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h2>\n<p>  <\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e \u0434\u043b\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u0432 \u043d\u0435\u0435 \u0438 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c <em>Node.js-\u043f\u0440\u043e\u0435\u043a\u0442<\/em>:<\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">mkdir ts-package-manager cd $!  yarn init -y # or npm init -y<\/code><\/pre>\n<p>  <\/p>\n<p>\u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438:<\/p>\n<p>  <\/p>\n<pre><code class=\"bash\"># \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 yarn add find-up fs-extra js-yaml log-update node-fetch progress semver tar yargs # or npm i ...  # \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 # \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 `tsc` \u0438 \u0442\u0438\u043f\u044b \u0434\u043b\u044f \u043f\u0430\u043a\u0435\u0442\u043e\u0432 yarn add -D typescript @types\/find-up @types\/fs-extra @types\/js-yaml @types\/log-update @types\/node-fetch @types\/progress @types\/semver @types\/tar @types\/yargs # or npm i -D ...<\/code><\/pre>\n<p>  <\/p>\n<p>\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0435\u043c \u0444\u0430\u0439\u043b <em>package.json<\/em>:<\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">{   \"name\": \"my-yarn\",   \"private\": false,   \"license\": \"MIT\",   \"version\": \"0.0.1\",   \"main\": \"dist\/main.js\",   \"bin\": {     \"my-yarn\": \"dist\/cli.js\"   },   \"files\": [     \"dist\"   ],   \"engines\": {     \"node\": \">= 13.14.0\"   },   \"scripts\": {     \"build\": \"tsc\"   },   \"type\": \"module\",   \"devDependencies\": {     ...   },   \"dependencies\": {     ...   } }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b <code>my-yarn<\/code> \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u043a\u043e\u0434 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 <em>dist\/cli.js<\/em>.<\/p>\n<p>  <\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0444\u0430\u0439\u043b <em>tsconfig.json<\/em> \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 \u0434\u043b\u044f \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0446\u0438\u0438 <em>TS<\/em> \u0432 <em>JS<\/em> (\u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f <em>tsc<\/em> \u043f\u0440\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u044b <code>build<\/code>):<\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">{   \"compilerOptions\": {     \"target\": \"ESNext\",     \"module\": \"ESNext\",     \"lib\": [       \"ESNext\"     ],     \"moduleResolution\": \"Node\",     \"strict\": true,     \"esModuleInterop\": true,     \"noUnusedLocals\": true,     \"noUnusedParameters\": true,     \"noImplicitReturns\": true,     \"outDir\": \"dist\"   },   \"include\": [     \"src\"   ] }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041d\u0435 \u0437\u0430\u0431\u044b\u0432\u0430\u0435\u043c \u043f\u0440\u043e \u0444\u0430\u0439\u043b <em>.gitignore<\/em>:<\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">node_modules dist # \u0435\u0441\u043b\u0438 \u0432\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 `yarn` yarn-error.log # \u0435\u0441\u043b\u0438 \u0432\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442\u0435 \u043d\u0430 `mac` .DS_Store<\/code><\/pre>\n<p>  <\/p>\n<p>\u0412\u0441\u0435 \u0444\u0430\u0439\u043b\u044b \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0431\u0443\u0434\u0443\u0442 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0432 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 <em>src<\/em> \u0438 \u043a\u043e\u043c\u043f\u0438\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e <em>dist<\/em>. \u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 <em>src<\/em> \u0431\u0443\u0434\u0435\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439:<\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">- cli.ts - install.ts - list.ts - lock.ts - log.ts - main.ts - resolve.ts - utils.ts<\/code><\/pre>\n<p>  <\/p>\n<p>\u0421 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u043e\u0439 \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u043e\u0439 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043c\u044b \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u043b\u0438. \u041c\u043e\u0436\u043d\u043e \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0430\u0442\u044c \u043a \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 <em>CLI<\/em>.<\/p>\n<p>  <\/p>\n<h2 id=\"cli\">CLI<\/h2>\n<p>  <\/p>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u0441 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430 \u043d\u0430\u0448\u0435\u0433\u043e <em>CLI<\/em> \u2014 <em>main.ts<\/em>. \u0412 \u044d\u0442\u043e\u043c \u0444\u0430\u0439\u043b\u0435 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0444\u0443\u043d\u043a\u0446\u0438\u044f <em>main<\/em>, \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c\u0430\u044f \u043f\u0440\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u044b <code>my-yarn install &lt;packageName><\/code>, \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043c\u0430\u0441\u0441\u0438\u0432 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c\u044b\u0445 \u043f\u0430\u043a\u0435\u0442\u043e\u0432, \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0435\u043c\u044b\u0439 <em>yargs<\/em>;<\/li>\n<li>\u043d\u0430\u0445\u043e\u0434\u0438\u043c \u0438 \u0447\u0438\u0442\u0430\u0435\u043c \u0444\u0430\u0439\u043b <em>package.json<\/em>; \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u043e\u043d \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435;<\/li>\n<li>\u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u0438\u0437 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c\u044b\u0445 \u043f\u0430\u043a\u0435\u0442\u043e\u0432 \u0438 \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u043c \u0438\u043c\u0438 <em>package.json<\/em>;<\/li>\n<li>\u0447\u0438\u0442\u0430\u0435\u043c <em>lock-\u0444\u0430\u0439\u043b<\/em>; \u0434\u0430\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u0438 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0438;<\/li>\n<li>\u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u0445 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u043e\u0433\u043e <em>package.json<\/em>;<\/li>\n<li>\u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0439 <em>lock-\u0444\u0430\u0439\u043b<\/em>;<\/li>\n<li>\u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u043f\u0430\u043a\u0435\u0442\u044b;<\/li>\n<li>\u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0439 <em>package.json<\/em>.<\/li>\n<\/ul>\n<p>  <\/p>\n<pre><code class=\"javascript\">import fs from 'fs-extra' import { findUp } from 'find-up' import yargs from 'yargs' \/\/ \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u043c\u044b \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c `JS-\u0444\u0430\u0439\u043b\u044b` import * as utils from '.\/utils.js' import list, { PackageJson } from '.\/list.js' import install from '.\/install.js' import * as log from '.\/log.js' import * as lock from '.\/lock.js'  export default async function main(args: yargs.Arguments) {   \/\/ \u043d\u0430\u0445\u043e\u0434\u0438\u043c \u0438 \u0447\u0438\u0442\u0430\u0435\u043c `package.json`   const jsonPath = (await findUp('package.json'))!   const root = await fs.readJson(jsonPath)    \/\/ \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0435 \u043f\u0430\u043a\u0435\u0442\u044b, \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c\u044b\u0435 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e `my-yarn install &lt;packageName>`,   \/\/ \u0447\u0435\u0440\u0435\u0437 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b `CLI`   const additionalPackages = args._.slice(1)   if (additionalPackages.length) {     if (args['save-dev'] || args.dev) {       root.devDependencies = root.devDependencies || {}        \/\/ \u043c\u044b \u0437\u0430\u043f\u043e\u043b\u043d\u0438\u043c \u044d\u0442\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u043f\u043e\u0441\u043b\u0435 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e \u043f\u0430\u043a\u0435\u0442\u0430\u0445       additionalPackages.forEach((pkg) => (root.devDependencies[pkg] = ''))     } else {       root.dependencies = root.dependencies || {}        additionalPackages.forEach((pkg) => (root.dependencies[pkg] = ''))     }   }    \/\/ \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u043d\u0435 \u043d\u0430\u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u044e\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438   if (args.production) {     delete root.devDependencies   }    \/\/ \u0447\u0438\u0442\u0430\u0435\u043c `lock-\u0444\u0430\u0439\u043b`   await lock.readLock()    \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u0445   const info = await list(root)    \/\/ \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c `lock-\u0444\u0430\u0439\u043b` \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e   lock.writeLock()    \/*   * \u0433\u043e\u0442\u043e\u0432\u0438\u043c\u0441\u044f \u043a \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0435   * \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u0437\u0434\u0435\u0441\u044c \u043c\u044b \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0432\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u0430\u043a\u0435\u0442\u043e\u0432   *   * \u043f\u043e \u043f\u0440\u0438\u0447\u0438\u043d\u0435 \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f   * \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u043d\u044b\u0445 \u043f\u0430\u043a\u0435\u0442\u043e\u0432 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u0442\u044c   * \u0441 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e\u043c \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c\u044b\u0445 \u043f\u0430\u043a\u0435\u0442\u043e\u0432   *\/   log.prepareInstall(     Object.keys(info.topLevel).length + info.unsatisfied.length   )    \/\/ \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u043f\u0430\u043a\u0435\u0442\u044b \u0432\u0435\u0440\u0445\u043d\u0435\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f   await Promise.all(     Object.entries(info.topLevel).map(([name, { url }]) => install(name, url))   )    \/\/ \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u043f\u0430\u043a\u0435\u0442\u044b \u0441 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u0430\u043c\u0438   await Promise.all(     info.unsatisfied.map((item) =>       install(item.name, item.url, `\/node_modules\/${item.parent}`)     )   )    \/\/ \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u0443\u0435\u043c `package.json`   beautifyPackageJson(root)    \/\/ \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c `package.json`   fs.writeJSON(jsonPath, root, { spaces: 2 }) }  \/\/ \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u0443\u0435\u043c \u043f\u043e\u043b\u044f `dependencies` \u0438 `devDependencies` function beautifyPackageJson(packageJson: PackageJson) {   if (packageJson.dependencies) {     packageJson.dependencies = utils.sortKeys(packageJson.dependencies)   }    if (packageJson.devDependencies) {     packageJson.devDependencies = utils.sortKeys(packageJson.devDependencies)   } }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0443\u0442\u0438\u043b\u0438\u0442\u044b \u0434\u043b\u044f \u043b\u043e\u0433\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f (<em>log.ts<\/em>):<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0443\u0442\u0438\u043b\u0438\u0442\u0430 <code>logResolving<\/code> \u0432\u044b\u0432\u043e\u0434\u0438\u0442 \u0432 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c\u043e\u0433\u043e \u043f\u0430\u043a\u0435\u0442\u0430;<\/li>\n<li>\u0443\u0442\u0438\u043b\u0438\u0442\u0430 <code>prepareInstall<\/code> \u0441\u043e\u043e\u0431\u0449\u0430\u0435\u0442 \u043e \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u0438 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c\u044b\u0445 \u043f\u0430\u043a\u0435\u0442\u043e\u0432 \u0438 \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440 \u043f\u0440\u043e\u0433\u0440\u0435\u0441\u0441\u0430 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438;<\/li>\n<li>\u0443\u0442\u0438\u043b\u0438\u0442\u0430 <code>tickInstalling<\/code> \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442 \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440 \u043f\u0440\u043e\u0433\u0440\u0435\u0441\u0441\u0430 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u043f\u043e\u0441\u043b\u0435 \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u044f \u0442\u0430\u0440\u0431\u0430\u043b\u0430 \u043f\u0430\u043a\u0435\u0442\u0430.<\/li>\n<\/ul>\n<p>  <\/p>\n<pre><code class=\"javascript\">import logUpdate from 'log-update' import ProgressBar from 'progress'  let progress: ProgressBar  \/\/ \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u043c\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c \/\/ \u043f\u043e \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0438 \u0441 `yarn` export function logResolving(name: string) {   logUpdate(`[1\/2] Resolving: ${name}`) }  export function prepareInstall(count: number) {   logUpdate('[1\/2] Finished resolving.')    \/\/ \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440 \u043f\u0440\u043e\u0433\u0440\u0435\u0441\u0441\u0430 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438   progress = new ProgressBar('[2\/2] Installing [:bar]', {     complete: '#',     total: count   }) }  \/\/ \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440 \u043f\u0440\u043e\u0433\u0440\u0435\u0441\u0441\u0430 \/\/ \u043f\u043e\u0441\u043b\u0435 \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u044f `tarball` export function tickInstalling() {   progress.tick() }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0443\u0442\u0438\u043b\u0438\u0442\u044b \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 <em>lock-\u0444\u0430\u0439\u043b\u043e\u043c<\/em> (<em>lock.ts<\/em>):<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0443\u0442\u0438\u043b\u0438\u0442\u0430 <code>updateOrCreate<\/code> \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u0430\u043a\u0435\u0442\u0435 \u0432 <em>lock<\/em>;<\/li>\n<li>\u0443\u0442\u0438\u043b\u0438\u0442\u0430 <code>getItem<\/code> \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u0442 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u0430\u043a\u0435\u0442\u0435 \u043f\u043e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044e \u0438 \u0432\u0435\u0440\u0441\u0438\u0438;<\/li>\n<li>\u0443\u0442\u0438\u043b\u0438\u0442\u0430 <code>readLock<\/code> \u0447\u0438\u0442\u0430\u0435\u0442 <em>lock<\/em>;<\/li>\n<li>\u0443\u0442\u0438\u043b\u0438\u0442\u0430 <code>writeLock<\/code> \u043f\u0438\u0448\u0435\u0442 <em>lock<\/em>.<\/li>\n<\/ul>\n<p>  <\/p>\n<pre><code class=\"javascript\">import { findUp } from 'find-up' import fs from 'fs-extra' import yaml from 'js-yaml' import { Manifest } from '.\/resolve.js' import { Obj } from '.\/utils.js'  interface Lock {   \/\/ \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u0430\u043a\u0435\u0442\u0430   [index: string]: {     \/\/ \u0432\u0435\u0440\u0441\u0438\u044f     version: string     \/\/ \u043f\u0443\u0442\u044c \u043a \u0442\u0430\u0440\u0431\u0430\u043b\u0443     url: string     \/\/ \u0445\u0435\u0448-\u0441\u0443\u043c\u043c\u0430 (\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c\u043d\u0430\u044f \u0441\u0443\u043c\u043c\u0430) \u0444\u0430\u0439\u043b\u0430     shasum: string     \/\/ \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438     dependencies: { [dep: string]: string }   } }  \/\/ \u043d\u0430\u0445\u043e\u0434\u0438\u043c `lock-\u0444\u0430\u0439\u043b` const lockPath = (await findUp('my-yarn.yml'))!  \/\/ \u0437\u0430\u0447\u0435\u043c \u043d\u0430\u043c 2 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0445 `lock`? \/\/ \u044d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u043c \u043f\u0440\u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0438 \u043f\u0430\u043a\u0435\u0442\u043e\u0432  \/\/ \u043f\u0440\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0438 \u0438\u043b\u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0438 \u043f\u0430\u043a\u0435\u0442\u043e\u0432 \/\/ `lock` \u043c\u043e\u0436\u0435\u0442 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438  \/\/ \u0441\u0442\u0430\u0440\u044b\u0439 `lock` \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f const oldLock: Lock = Object.create(null)  \/\/ \u043d\u043e\u0432\u044b\u0439 `lock` \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0437\u0430\u043f\u0438\u0441\u0438 const newLock: Lock = Object.create(null)  \/\/ \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u0430\u043a\u0435\u0442\u0435 \u0432 `lock` export function updateOrCreate(name: string, info: Obj) {   if (!newLock[name]) {     newLock[name] = Object.create(null)   }    Object.assign(newLock[name], info) }  \/* * \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u0430\u043a\u0435\u0442\u0435 \u043f\u043e \u0435\u0433\u043e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044e \u0438 \u0432\u0435\u0440\u0441\u0438\u0438 (\u0441\u0435\u043c\u0430\u043d\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u043c\u0443 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u0443) * \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u043c\u044b \u043d\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435, * \u0430 \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u0443\u0435\u043c \u0438\u0445 \u0434\u043b\u044f \u0442\u043e\u0433\u043e, * \u0447\u0442\u043e\u0431\u044b \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u043e\u0432\u0430\u043b\u0430 \u0440\u0435\u0435\u0441\u0442\u0440\u0443 \u043f\u0430\u043a\u0435\u0442\u043e\u0432 (`npm`) * \u044d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043b\u043e\u0433\u0438\u043a\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 `collectDeps` * \u0438\u0437 \u043c\u043e\u0434\u0443\u043b\u044f `list` *\/ export function getItem(name: string, constraint: string): Manifest | null {   \/\/ \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u044d\u043b\u0435\u043c\u0435\u043d\u0442 `lock` \u043f\u043e \u043a\u043b\u044e\u0447\u0443,   \/\/ \u0444\u043e\u0440\u043c\u0430\u0442 \u0432\u0434\u043e\u0445\u043d\u043e\u0432\u043b\u0435\u043d `yarn.lock`   const item = oldLock[`${name}@${constraint}`]    if (!item) {     return null   }    \/\/ \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u043c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0434\u0430\u043d\u043d\u044b\u0445   return {     [item.version]: {       dependencies: item.dependencies,       dist: { tarball: item.url, shasum: item.shasum }     }   } }  \/\/ \u0447\u0438\u0442\u0430\u0435\u043c `lock` export async function readLock() {   if (await fs.pathExists(lockPath)) {     Object.assign(oldLock, yaml.load(await fs.readFile(lockPath, 'utf-8')))   } }  \/\/ \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c `lock` export async function writeLock() {   \/\/ \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u044c \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438 \u043a\u043b\u044e\u0447\u0435\u0439 \u043e\u0431\u0443\u0441\u043b\u043e\u0432\u043b\u0435\u043d\u0430 \u0442\u0435\u043c,   \/\/ \u0447\u0442\u043e \u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440\u0430   \/\/ \u043f\u043e\u0440\u044f\u0434\u043e\u043a \u043f\u0430\u043a\u0435\u0442\u043e\u0432 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0437\u043d\u044b\u043c   \/\/   \/\/ \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0430 \u043c\u043e\u0436\u0435\u0442 \u043e\u0431\u043b\u0435\u0433\u0447\u0438\u0442\u044c \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0435 \u0432\u0435\u0440\u0441\u0438\u0439 `lock` \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e `git diff`   await fs.writeFile(     lockPath,     yaml.dump(newLock, { sortKeys: true, noRefs: true })   ) }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0423\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u043f\u0430\u043a\u0435\u0442\u0430 (<em>install.ts<\/em>):<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import fetch from 'node-fetch' import tar from 'tar' import fs from 'fs-extra' import * as log from '.\/log.js'  export default async function install(   name: string,   url: string,   location = '' ) {   \/\/ \u043f\u0443\u0442\u044c \u043a \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u0434\u043b\u044f \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c\u043e\u0433\u043e \u043f\u0430\u043a\u0435\u0442\u0430   const path = `${process.cwd()}${location}\/node_modules\/${name}`    \/\/ \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0432\u043d\u043e   await fs.mkdirp(path)    const response = await fetch(url)    \/*   * \u0442\u0435\u043b\u043e \u043e\u0442\u0432\u0435\u0442\u0430 - \u044d\u0442\u043e \u043f\u043e\u0442\u043e\u043a \u0434\u0430\u043d\u043d\u044b\u0445, \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0439 \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f   * (readable stream, application\/octet-stream)   *   * `tar.extract` \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0442\u0430\u043a\u043e\u0439 \u043f\u043e\u0442\u043e\u043a   * \u044d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0442\u044c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e -   * \u0431\u0435\u0437 \u0435\u0433\u043e \u0437\u0430\u043f\u0438\u0441\u0438 \u043d\u0430 \u0434\u0438\u0441\u043a   *\/   response     .body!.pipe(tar.extract({ cwd: path, strip: 1 }))     \/\/ \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440 \u043f\u0440\u043e\u0433\u0440\u0435\u0441\u0441\u0430 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u043f\u043e\u0441\u043b\u0435 \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u044f \u0442\u0430\u0440\u0431\u0430\u043b\u0430     .on('close', log.tickInstalling) }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0423\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438 \u043a\u043b\u044e\u0447\u0435\u0439 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 (<em>utils.ts<\/em>):<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">export type Obj = { [key: string]: any }  export const sortKeys = (obj: Obj) =>   Object.keys(obj)     .sort()     .reduce((_obj: Obj, cur) => {       _obj[cur] = obj[cur]       return _obj     }, Object.create(null))<\/code><\/pre>\n<p>  <\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u043f\u043e\u0436\u0430\u043b\u0443\u0439, \u0441\u0430\u043c\u043e\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0435 \u2014 \u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u043f\u0438\u0441\u043a\u0430 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u0432\u0435\u0440\u0445\u043d\u0435\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f \u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u0441 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u0430\u043c\u0438 (\u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442\u043e\u0432) \u0432 \u0444\u0430\u0439\u043b\u0435 <em>list.ts<\/em>.<\/p>\n<p>  <\/p>\n<p>\u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u043f\u0430\u043a\u0435\u0442\u044b, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0442\u0438\u043f\u044b \u0438 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import semver from 'semver' import resolve from '.\/resolve.js' import * as log from '.\/log.js' import * as lock from '.\/lock.js' import { Obj } from '.\/utils.js'  type DependencyStack = Array&lt;{   name: string   version: string   dependencies: Obj }>  export interface PackageJson {   dependencies?: Obj   devDependencies?: Obj }  \/\/ \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f `topLevel` \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0430 \u0434\u043b\u044f \u0432\u044b\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u043d\u0438\u044f (flatten) \/\/ \u0434\u0435\u0440\u0435\u0432\u0430 \u043f\u0430\u043a\u0435\u0442\u043e\u0432 \u0432\u043e \u0438\u0437\u0431\u0435\u0436\u0430\u043d\u0438\u0435 \u0438\u0445 \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f const topLevel: {   [name: string]: { version: string; url: string } } = Object.create(null)  \/\/ \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f `unsatisfied` \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0430 \u0434\u043b\u044f \u0430\u043a\u043a\u0443\u043c\u0443\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u043e\u0432 (\u0434\u0443\u0431\u043b\u0438\u0440\u0443\u044e\u0449\u0438\u0445\u0441\u044f \u043f\u0430\u043a\u0435\u0442\u043e\u0432) const unsatisfied: Array&lt;{ name: string; url: string; parent: string }> = []<\/code><\/pre>\n<p>  <\/p>\n<p>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0434\u043b\u044f \u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u043f\u0438\u0441\u043a\u0430 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 <code>collectDeps<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">\/\/ @ts-ignore async function collectDeps(   name: string,   constraint: string,   stack: DependencyStack = [] ) {   \/\/ \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442 \u043f\u0430\u043a\u0435\u0442\u0430 \u0438\u0437 `lock` \u043f\u043e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044e \u0438 \u0432\u0435\u0440\u0441\u0438\u0438   const fromLock = lock.getItem(name, constraint)    \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0435   \/\/   \/\/ \u0435\u0441\u043b\u0438 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0432 `lock`,   \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0435\u0433\u043e \u0438\u0437 \u0441\u0435\u0442\u0438   const manifest = fromLock || (await resolve(name))    \/\/ \u0432\u044b\u0432\u043e\u0434\u0438\u043c \u0432 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u043c\u043e\u0433\u043e \u043f\u0430\u043a\u0435\u0442\u0430   log.logResolving(name)    \/\/ \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0432\u0435\u0440\u0441\u0438\u044e \u043f\u0430\u043a\u0435\u0442\u0430,   \/\/ \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0431\u043b\u0438\u0436\u0435 \u0432\u0441\u0435\u0433\u043e \u043a \u0441\u0435\u043c\u0430\u043d\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u043c\u0443 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u0443   \/\/   \/\/ \u0435\u0441\u043b\u0438 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d \u043d\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d,   \/\/ \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044e\u044e \u0432\u0435\u0440\u0441\u0438\u044e   const versions = Object.keys(manifest)   const matched = constraint     ? semver.maxSatisfying(versions, constraint)     : versions.at(-1)   if (!matched) {     throw new Error('Cannot resolve suitable package.')   }    \/\/ \u0435\u0441\u043b\u0438 \u043f\u0430\u043a\u0435\u0442 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0432 `topLevel`   if (!topLevel[name]) {     \/\/ \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0435\u0433\u043e     topLevel[name] = { url: manifest[matched].dist.tarball, version: matched }   \/\/ \u0435\u0441\u043b\u0438 \u043f\u0430\u043a\u0435\u0442 \u0438\u043c\u0435\u0435\u0442\u0441\u044f \u0432 `topLevel` \u0438 \u0443\u0434\u043e\u0432\u043b\u0435\u0442\u0432\u043e\u0440\u044f\u0435\u0442 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u0443   } else if (semver.satisfies(topLevel[name].version, constraint)) {     \/\/ \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u043e\u0432     const conflictIndex = checkStackDependencies(name, matched, stack)      \/\/ \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u043f\u0440\u0438 \u043d\u0430\u043b\u0438\u0447\u0438\u0438 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u0430     \/\/ \u044d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u0432\u043e\u0437\u043d\u0438\u043a\u043d\u043e\u0432\u0435\u043d\u0438\u044f \u0446\u0438\u043a\u043b\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439     if (conflictIndex === -1) return      \/*     * \u0438\u0437-\u0437\u0430 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0435\u0439 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u0430, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u043e\u0433\u043e `Node.js`     * \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f \u043c\u043e\u0434\u0443\u043b\u0435\u0439,     * \u043c\u0435\u0436\u0434\u0443 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u043c\u043e\u0433\u0443\u0442 \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u0442\u044c \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u044b     *     * \u043e\u0434\u043d\u0438\u043c \u0438\u0437 \u0440\u0435\u0448\u0435\u043d\u0438\u0439 \u0434\u0430\u043d\u043d\u043e\u0439 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b     * \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e \u0434\u0432\u0443\u0445 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438,     * \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u0443\u044e\u0449\u0438\u0445 \u043c\u0435\u0436\u0434\u0443 \u0441\u043e\u0431\u043e\u0439     *\/     unsatisfied.push({       name,       parent: stack         .map(({ name }) => name)         .slice(conflictIndex - 2)         .join('\/node_modules\/'),       url: manifest[matched].dist.tarball     })   \/\/ \u0435\u0441\u043b\u0438 \u043f\u0430\u043a\u0435\u0442 \u0443\u0436\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442\u0441\u044f \u0432 `topLevel`   \/\/ \u043d\u043e \u0438\u043c\u0435\u0435\u0442 \u0434\u0440\u0443\u0433\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e   } else {     unsatisfied.push({       name,       parent: stack.at(-1)!.name,       url: manifest[matched].dist.tarball     })   }    \/\/ \u043d\u0435 \u0437\u0430\u0431\u044b\u0432\u0430\u0435\u043c \u043e \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438   const dependencies = manifest[matched].dependencies || null    \/\/ \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442 \u0432 `lock`   lock.updateOrCreate(`${name}@${constraint}`, {     version: matched,     url: manifest[matched].dist.tarball,     shasum: manifest[matched].dist.shasum,     dependencies   })    \/\/ \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u043c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438   if (dependencies) {     stack.push({       name,       version: matched,       dependencies     })     await Promise.all(       Object.entries(dependencies)         \/\/ \u043f\u0440\u0435\u0434\u043e\u0442\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0446\u0438\u043a\u043b\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438         .filter(([dep, range]) => !hasCirculation(dep, range, stack))         .map(([dep, range]) => collectDeps(dep, range, stack.slice()))     )     \/\/ \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442     stack.pop()   }    \/\/ \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0441\u0435\u043c\u0430\u043d\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d \u0432\u0435\u0440\u0441\u0438\u0438   \/\/ \u0434\u043b\u044f \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0432 `package.json`   if (!constraint) {     return { name, version: `^${matched}` }   } }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c 2 \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">\/\/ \u0434\u0430\u043d\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u043e\u0432 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 const checkStackDependencies = (   name: string,   version: string,   stack: DependencyStack ) =>   stack.findIndex(({ dependencies }) =>     \/\/ \u0435\u0441\u043b\u0438 \u043f\u0430\u043a\u0435\u0442 \u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c\u044e \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u043f\u0430\u043a\u0435\u0442\u0430,     \/\/ \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c `true`     !dependencies[name] ? true : semver.satisfies(version, dependencies[name])   )  \/\/ \u0434\u0430\u043d\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u0446\u0438\u043a\u043b\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \/\/ \/\/ \u0435\u0441\u043b\u0438 \u043f\u0430\u043a\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442\u0441\u044f \u0432 \u0441\u0442\u0435\u043a\u0435 \u0438 \u0438\u043c\u0435\u0435\u0442 \u0442\u0430\u043a\u0443\u044e \u0436\u0435 \u0432\u0435\u0440\u0441\u0438\u044e \/\/ \u0437\u043d\u0430\u0447\u0438\u0442, \u0438\u043c\u0435\u0435\u0442 \u043c\u0435\u0441\u0442\u043e \u0446\u0438\u043a\u043b\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c const hasCirculation = (name: string, range: string, stack: DependencyStack) =>   stack.some(     (item) => item.name === name &amp;&amp; semver.satisfies(item.version, range)   )<\/code><\/pre>\n<p>  <\/p>\n<p>\u041d\u0430\u043a\u043e\u043d\u0435\u0446, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">\/\/ \u043d\u0430\u0448\u0430 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u043b\u044f \/\/ `dependencies` \u0438 `devDependencies` export default async function list(rootManifest: PackageJson) {   \/\/ \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0432 `package.json` \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u0438 \u0432\u0435\u0440\u0441\u0438\u0438 \u043f\u0430\u043a\u0435\u0442\u043e\u0432    \/\/ \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438   if (rootManifest.dependencies) {     ;(       await Promise.all(         Object.entries(rootManifest.dependencies).map((pair) =>           collectDeps(...pair)         )       )     )       .filter(Boolean)       .forEach(         (item) => (rootManifest.dependencies![item!.name] = item!.version)       )   }    \/\/ \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438   if (rootManifest.devDependencies) {     ;(       await Promise.all(         Object.entries(rootManifest.devDependencies).map((pair) =>           collectDeps(...pair)         )       )     )       .filter(Boolean)       .forEach(         (item) => (rootManifest.devDependencies![item!.name] = item!.version)       )   }    \/\/ \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u043f\u0430\u043a\u0435\u0442\u044b \u0432\u0435\u0440\u0445\u043d\u0435\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f \u0438 \u043f\u0430\u043a\u0435\u0442\u044b \u0441 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u0430\u043c\u0438   return { topLevel, unsatisfied } }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0438 (<em>cli.ts<\/em>):<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">#!\/usr\/bin\/env node import yargs from 'yargs' import main from '.\/main.js'  yargs   \/\/ \u043f\u0440\u0438\u043c\u0435\u0440 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f   .usage('my-yarn &lt;command> [args]')   \/\/ \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e \u0432\u0435\u0440\u0441\u0438\u0438   .version()   \/\/ \u043f\u0441\u0435\u0432\u0434\u043e\u043d\u0438\u043c   .alias('v', 'version')   \/\/ \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f   .help()   \/\/ \u043f\u0441\u0435\u0432\u0434\u043e\u043d\u0438\u043c   .alias('h', 'help')   \/\/ \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u043e\u0439, \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c\u043e\u0439 \u043d\u0430\u0448\u0438\u043c `CLI`,   \/\/ \u0431\u0443\u0434\u0435\u0442 \u043a\u043e\u043c\u0430\u043d\u0434\u0430 `add`   \/\/ \u0434\u0430\u043d\u043d\u0430\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0430 \u0434\u043b\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439   .command(     'add',     'Install dependencies',     (argv) => {       \/\/ \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438       argv.option('production', {         type: 'boolean',         description: 'Install production dependencies only'       })        \/\/ \u043f\u0440\u0438 \u043d\u0430\u043b\u0438\u0447\u0438\u0438 \u0444\u043b\u0430\u0433\u043e\u0432 `save-dev`, `dev` \u0438\u043b\u0438 `D`       \/\/ \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438       argv.boolean('save-dev')       argv.boolean('dev')       argv.alias('D', 'dev')        return argv     },     \/\/ \u043f\u0440\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u044b `yarn add &lt;packageName>`     \/\/ \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u043a\u043e\u0434 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 `main.js`     main   )   \/\/ \u043f\u0430\u0440\u0441\u0438\u043c \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b, \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u044b\u0435 `CLI`   .parse()<\/code><\/pre>\n<p>  <\/p>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043d\u0430\u0448\u0435\u0433\u043e <code>CLI<\/code> \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430. \u041f\u0440\u0438\u0448\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f \u0432 \u0435\u0433\u043e \u0440\u0430\u0431\u043e\u0442\u043e\u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u0438.<\/p>\n<p>  <\/p>\n<h2 id=\"primer\">\u041f\u0440\u0438\u043c\u0435\u0440<\/h2>\n<p>  <\/p>\n<p>\u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0441\u0431\u043e\u0440\u043a\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u044b <code>yarn build<\/code> \u0438\u043b\u0438 <code>npm run build<\/code>.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/7w\/5l\/cd\/7w5lcdrhingn8qs5vmboa0zzjiu.png\" data-src=\"https:\/\/habrastorage.org\/webt\/7w\/5l\/cd\/7w5lcdrhingn8qs5vmboa0zzjiu.png\"\/>  <\/p>\n<p>  <\/p>\n<p>  <\/p>\n<p>\u042d\u0442\u043e \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u043a \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 <em>dist<\/em> \u0441 <em>JS-\u0444\u0430\u0439\u043b\u0430\u043c\u0438<\/em> \u043f\u0440\u043e\u0435\u043a\u0442\u0430.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/8k\/8h\/3s\/8k8h3sgiy6aotn8wg7zq52jbmzy.png\" data-src=\"https:\/\/habrastorage.org\/webt\/8k\/8h\/3s\/8k8h3sgiy6aotn8wg7zq52jbmzy.png\"\/>  <\/p>\n<p>  <\/p>\n<p>  <\/p>\n<p>\u041d\u0430\u0445\u043e\u0434\u044f\u0441\u044c \u0432 \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438, \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u043d\u0430\u0448 <em>CLI<\/em> \u043a <em>npm<\/em> \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u044b <a href=\"https:\/\/docs.npmjs.com\/cli\/v8\/commands\/npm-link\">npm link<\/a> (\u0434\u0430\u043d\u043d\u0430\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c\u044b\u0435 \u043f\u0430\u043a\u0435\u0442\u044b \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e) \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0445 \u043f\u0430\u043a\u0435\u0442\u043e\u0432 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u044b <code>npm -g list --depth 0<\/code>.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/rw\/qr\/w0\/rwqrw0yainnkdzbjfqdl7y9rxyc.png\" data-src=\"https:\/\/habrastorage.org\/webt\/rw\/qr\/w0\/rwqrw0yainnkdzbjfqdl7y9rxyc.png\"\/>  <\/p>\n<p>  <\/p>\n<p>  <\/p>\n<p>\u0412\u0438\u0434\u0438\u043c \u0432 \u0441\u043f\u0438\u0441\u043a\u0435 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u0445 \u043f\u0430\u043a\u0435\u0442\u043e\u0432 <code>my-yarn@0.0.1<\/code>. \u0414\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f <em>my-yarn<\/em> \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 <code>npm -g rm my-yarn<\/code>.<\/p>\n<p>  <\/p>\n<p>\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0432\u0435\u0440\u0441\u0438\u0438 <em>my-yarn<\/em> \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u044b <code>my-yarn -v<\/code> \u0438 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f <em>CLI<\/em> \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u044b <code>my-yarn -h<\/code>.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/5c\/1i\/yh\/5c1iyhggene8logcmei5jzl7zy4.png\" data-src=\"https:\/\/habrastorage.org\/webt\/5c\/1i\/yh\/5c1iyhggene8logcmei5jzl7zy4.png\"\/>  <\/p>\n<p>  <\/p>\n<p>  <\/p>\n<p>\u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0430 <a href=\"https:\/\/expressjs.com\/ru\/\">Express<\/a>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c\u0441\u044f \u0432 \u0440\u0435\u0436\u0438\u043c\u0435 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u0440\u0430\u0437\u043c\u0435\u0442\u043a\u0443.<\/p>\n<p>  <\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e <em>my-yarn<\/em>, \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u0432 \u043d\u0435\u0435 \u0438 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c <em>Node.js-\u043f\u0440\u043e\u0435\u043a\u0442<\/em>:<\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">mkdir my-yarn cd $!  yarn init -yp # \u0438\u043b\u0438 npm init -y<\/code><\/pre>\n<p>  <\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0444\u0430\u0439\u043b <em>index.html<\/em> \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u044f:<\/p>\n<p>  <\/p>\n<pre><code class=\"html\">&lt;!DOCTYPE html> &lt;html lang=\"ru\">   &lt;head>     &lt;meta charset=\"UTF-8\" \/>     &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" \/>     &lt;title>MyYarn&lt;\/title>   &lt;\/head>   &lt;body>     &lt;h1>MyYarn - \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u043f\u0430\u043a\u0435\u0442\u043d\u044b\u0439 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440&lt;\/h1>   &lt;\/body> &lt;\/html><\/code><\/pre>\n<p>  <\/p>\n<p>\u0418 \u0442\u0430\u043a\u043e\u0439 \u0444\u0430\u0439\u043b <em>index.js<\/em>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">\/\/ \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0438\u043c\u0435\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c `ESM`, \/\/ \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c `\"type\": \"module\"` \u0432 \u0444\u0430\u0439\u043b\u0435 `package.json` import express from 'express'  const app = express()  \/\/ \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0441\u0442\u0430\u0442\u0438\u043a\u0443 \u043f\u0440\u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 `GET-\u0437\u0430\u043f\u0440\u043e\u0441\u0430` \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 `\/my-yarn` app.get('\/my-yarn', (_, res) => {   res.sendFile(`${process.cwd()}\/index.html`) })  const PORT = process.env.PORT || 3124 \/\/ \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0441\u0435\u0440\u0432\u0435\u0440 app.listen(PORT, () => {   console.log(`Server is listening on port ${PORT}`) })<\/code><\/pre>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u0430\u043a\u0435\u0442 <a href=\"https:\/\/www.npmjs.com\/package\/express\">express<\/a>, \u0430 \u0434\u043b\u044f \u0435\u0433\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0432 \u0440\u0435\u0436\u0438\u043c\u0435 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u2014 \u043f\u0430\u043a\u0435\u0442 <a href=\"https:\/\/www.npmjs.com\/package\/nodemon\">nodemon<\/a>. \u041c\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0443 \u044d\u0442\u0438\u0445 \u043f\u0430\u043a\u0435\u0442\u043e\u0432 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043d\u0430\u0448\u0435\u0433\u043e <em>CLI<\/em>.<\/p>\n<p>  <\/p>\n<p>\u041d\u0430\u0445\u043e\u0434\u044f\u0441\u044c \u0432 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 <em>my-yarn<\/em>, \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c <em>express<\/em> \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u044b <code>my-yarn add express<\/code> \u0438 <em>nodemon<\/em> \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u044b <code>my-yarn add -D nodemon<\/code>.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/q6\/zz\/lz\/q6zzlzszljmbdycgbdzwlo9xdye.png\" data-src=\"https:\/\/habrastorage.org\/webt\/q6\/zz\/lz\/q6zzlzszljmbdycgbdzwlo9xdye.png\"\/>  <\/p>\n<p>  <\/p>\n<p>  <\/p>\n<p>\u042d\u0442\u043e \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u043a \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 <code>node_modules<\/code>, \u0444\u0430\u0439\u043b\u0430 <em>my-yarn.yml<\/em> \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044e \u0444\u0430\u0439\u043b\u0430 <em>package.json<\/em>.<\/p>\n<p>  <\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0432 <em>package.json<\/em>:<\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">\"scripts\": {   \"dev\": \"node_modules\/nodemon\/bin\/nodemon.js\" }<\/code><\/pre>\n<p>  <\/p>\n<p><em>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435<\/em>: \u043d\u0430\u0448 <em>CLI<\/em> \u043d\u0435 \u0443\u043c\u0435\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0441\u043a\u0440\u0438\u043f\u0442\u044b, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043a\u043e\u043c\u0430\u043d\u0434\u044b <em>dev<\/em> \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <em>yarn<\/em>. \u041e\u0434\u043d\u0430\u043a\u043e, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043c\u044b \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u043b\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <em>my-yarn<\/em>, \u0443 \u043d\u0430\u0441 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0444\u0430\u0439\u043b <em>yarn.lock<\/em>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f <em>yarn<\/em> \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f \u043f\u0443\u0442\u0435\u0439 \u043a \u043f\u0430\u043a\u0435\u0442\u0430\u043c. \u042d\u0442\u043e \u043e\u0431\u0443\u0441\u043b\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u044c \u0443\u043a\u0430\u0437\u0430\u043d\u0438\u044f \u043f\u043e\u043b\u043d\u043e\u0433\u043e \u043f\u0443\u0442\u0438 \u043a \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c\u043e\u043c\u0443 \u0444\u0430\u0439\u043b\u0443 <em>nodemon<\/em>.<\/p>\n<p>  <\/p>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0441\u0435\u0440\u0432\u0435\u0440 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u044b <code>yarn dev<\/code>.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/xl\/ud\/ja\/xludja-5b5h6jwiduys0nl13sem.png\" data-src=\"https:\/\/habrastorage.org\/webt\/xl\/ud\/ja\/xludja-5b5h6jwiduys0nl13sem.png\"\/>  <\/p>\n<p>  <\/p>\n<p>  <\/p>\n<p>\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043e \u0433\u043e\u0442\u043e\u0432\u043d\u043e\u0441\u0442\u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u043a \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432.<\/p>\n<p>  <\/p>\n<p>\u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0432\u043a\u043b\u0430\u0434\u043a\u0443 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430 \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 <em><a href=\"http:\/\/localhost:3124\/my-yarn\">http:\/\/localhost:3124\/my-yarn<\/a><\/em>.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/lb\/zv\/mm\/lbzvmm50naadaiumlas6bi-0ffi.png\" data-src=\"https:\/\/habrastorage.org\/webt\/lb\/zv\/mm\/lbzvmm50naadaiumlas6bi-0ffi.png\"\/>  <\/p>\n<p>\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043d\u0430\u0448 <em>index.html<\/em>.<\/p>\n<p>  <\/p>\n<p>\u041e\u0442\u043b\u0438\u0447\u043d\u043e. \u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442, \u043a\u0430\u043a \u043e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f.<\/p>\n<p>  <\/p>\n<p>\u041f\u043e\u0436\u0430\u043b\u0443\u0439, \u044d\u0442\u043e \u0432\u0441\u0435, \u043e \u0447\u0435\u043c \u044f \u0445\u043e\u0442\u0435\u043b \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c \u0432\u0430\u043c \u0432 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435. \u041d\u0430\u0434\u0435\u044e\u0441\u044c, \u0432\u0430\u043c \u0431\u044b\u043b\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e \u0438 \u0432\u044b \u043d\u0435 \u0436\u0430\u043b\u0435\u0435\u0442\u0435 \u043f\u043e\u0442\u0440\u0430\u0447\u0435\u043d\u043d\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438.<\/p>\n<p>  <\/p>\n<p>\u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044e \u0437\u0430 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u0438 happy coding!<\/p>\n<p>  <\/p>\n<hr\/>\n<p>  <\/p>\n<p><a href=\"https:\/\/cloud.timeweb.com\/vds-promo-10-rub?utm_source=habr&amp;utm_medium=banner&amp;utm_campaign=vds-promo-10-rub\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/co\/e2\/kh\/coe2kha8u8_pypip-2k3wk3ppa0.png\" data-src=\"https:\/\/habrastorage.org\/webt\/co\/e2\/kh\/coe2kha8u8_pypip-2k3wk3ppa0.png\"\/><\/a><\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"v-portal\" style=\"display:none;\"><\/div>\n<\/div>\n<p> <!----> <!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/company\/timeweb\/blog\/662830\/\"> https:\/\/habr.com\/ru\/company\/timeweb\/blog\/662830\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/4-\/hn\/8w\/4-hn8wkkaeafijehpfybkgcounq.png\" data-src=\"https:\/\/habrastorage.org\/webt\/4-\/hn\/8w\/4-hn8wkkaeafijehpfybkgcounq.png\"\/>  <\/p>\n<p>  <\/p>\n<p>  <\/p>\n<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u0434\u0440\u0443\u0437\u044c\u044f!<\/p>\n<p>  <\/p>\n<p>\u0412\u0430\u043c \u043a\u043e\u0433\u0434\u0430-\u043d\u0438\u0431\u0443\u0434\u044c \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0443\u0437\u043d\u0430\u0442\u044c, \u043a\u0430\u043a \u043f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442\u043e\u043c \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 <a href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%A1%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0_%D1%83%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D1%8F_%D0%BF%D0%B0%D0%BA%D0%B5%D1%82%D0%B0%D0%BC%D0%B8\">\u043f\u0430\u043a\u0435\u0442\u043d\u044b\u0435 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440\u044b<\/a> (Package Manager, PM) \u2014 <a href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%98%D0%BD%D1%82%D0%B5%D1%80%D1%84%D0%B5%D0%B9%D1%81_%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%BD%D0%BE%D0%B9_%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8\">\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0438<\/a> (Command Line Interface, CLI) \u0434\u043b\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 \u043d\u0430\u043f\u043e\u0434\u043e\u0431\u0438\u0435 <a href=\"https:\/\/www.npmjs.com\/\">npm<\/a> \u0438\u043b\u0438 <a href=\"https:\/\/yarnpkg.com\/\">yarn<\/a>? \u0415\u0441\u043b\u0438 \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c, \u0442\u043e\u0433\u0434\u0430 \u044d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f \u0434\u043b\u044f \u0432\u0430\u0441.<\/p>\n<p>  <\/p>\n<p>\u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u0435 \u043c\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u043f\u0430\u043a\u0435\u0442\u043d\u044b\u0439 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440 \u043d\u0430 <a href=\"https:\/\/nodejs.org\/\">Node.js<\/a> \u0438 <a href=\"https:\/\/www.typescriptlang.org\/\">TypeScript<\/a>. \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043e\u0431\u0440\u0430\u0437\u0446\u0430 \u0434\u043b\u044f \u043f\u043e\u0434\u0440\u0430\u0436\u0430\u043d\u0438\u044f \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <em>yarn<\/em>. \u0415\u0441\u043b\u0438 \u0432\u044b \u043d\u0435 \u0437\u043d\u0430\u043a\u043e\u043c\u044b \u0441 <em>TS<\/em>, \u0441\u043e\u0432\u0435\u0442\u0443\u044e \u0432\u0437\u0433\u043b\u044f\u043d\u0443\u0442\u044c \u043d\u0430 <a href=\"https:\/\/my-js.org\/docs\/guide\/ts\">\u044d\u0442\u0443 \u043a\u0430\u0440\u043c\u0430\u043d\u043d\u0443\u044e \u043a\u043d\u0438\u0433\u0443<\/a>.<\/p>\n<p>  <\/p>\n<p>\u041d\u0430\u0448 <em>CLI<\/em> \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f <em>my-yarn<\/em>. \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 <a href=\"https:\/\/habr.com\/ru\/company\/domclick\/blog\/513130\/\">lock-\u0444\u0430\u0439\u043b\u0430<\/a> (<em>yarn.lock<\/em>, <em>package-lock.json<\/em>) \u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0444\u0430\u0439\u043b <em>my-yarn.yml<\/em>.<\/p>\n<p>  <\/p>\n<p><a href=\"https:\/\/github.com\/g-plane\/tiny-package-manager\">\u0418\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u0432\u0434\u043e\u0445\u043d\u043e\u0432\u0435\u043d\u0438\u044f<\/a>.<\/p>\n<p>  <\/p>\n<p><a href=\"https:\/\/github.com\/harryheman\/Blog-Posts\/tree\/master\/ts-package-manager\">\u041a\u043e\u0434 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/a>.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-332387","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/332387","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=332387"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/332387\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=332387"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=332387"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=332387"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}