Я бы предложил строить меньшие функции с изолированными задачами.Начните с функции files
, которая просто возвращает все файлы и файлы всех подкаталогов -
const { readdir, stat } =
require ("fs") .promises
const { join } =
require ("path")
const files = async (path = ".") =>
(await stat (path)) .isDirectory ()
? Promise
.all
( (await readdir (path))
.map (f => files (join (path, f)))
)
.then
( results =>
[] .concat (...results)
)
: [ path ]
files () .then (console.log, console.error)
// [ './.main.js'
// , './node_modules/anotherpackage/README.md'
// , './node_modules/anotherpackage/package.json'
// , './node_modules/anotherpackage/index.js'
// , './node_modules/somepackage/.npmignore'
// , './node_modules/somepackage/LICENSE'
// , './node_modules/somepackage/README.md'
// , './node_modules/somepackage/package.json'
// , './node_modules/somepackage/index.js'
// , './node_modules/somepackage/test/test.js'
// , './package.json'
// ]
Затем создайте функцию search
, которая зависит от files
и добавляет возможность фильтровать результаты-
const { basename } =
require ("path")
const search = async (query, path = ".") =>
(await files (path))
.filter (x => basename (x) === query)
search ("package.json", ".")
.then (console.log, console.error)
// [ './node_modules/anotherpackage/package.json'
// , './node_modules/somepackage/package.json'
// , './package.json'
// ]
Затем выполните readPackages
функцию, которая зависит от search
и добавляет возможность чтения / анализа пакетов -
const { readFile } =
require ("fs") .promises
const readPackages = async (path = ".") =>
Promise
.all
( (await search ("package.json", path))
.map (package => readFile (package))
)
.then
( buffers =>
buffers .map (b => JSON .parse (String (b)))
)
readPackages ('.')
.then (console.log, console.error)
// [ <contents of anotherpackage/package.json>
// , <contents of somepackage/package.json>
// , <contents of package.json>
// ]
Наконец, обратите внимание, как jsonTable
больше не глобальный.Вместо этого все данные аккуратно содержатся и проходят через нашу последовательность обещаний.
Если вы хотите преобразовывать пакеты по мере их чтения, вы можете сделать transform
параметром *Функция 1020 *.Это делает его универсальным и позволяет читать содержимое пакета указанным пользователем способом -
const readPackages = async (transform, path = ".") =>
Promise
.all
( (await search ("package.json", path))
.map (package => readFile (package))
)
.then
( buffers =>
buffers .map (b => transform (JSON .parse (String (b))))
)
readPackages
( ({ name }) => ({ name })
, '.'
)
.then (console.log, console.error)
// [ { name: 'anotherpackage' }
// , { name: 'somepackage' }
// , { name: 'mypackage' }
// ]
или получать name
, version
и license
-
readPackages
( ({ name, version, license = "None" }) =>
({ name, version, license })
, '.'
)
.then (console.log, console.error)
// [ { name: 'anotherpackage', version: '1.0.0', license: 'None' }
// , { name: 'somepackage', version: '3.2.1', license: 'MIT' }
// , { name: 'mypackage', version: '1.2.3', license: 'BSD-3-Clause' }
// ]
Теперь в этих упрощенных программах мы начинаем видеть некоторые закономерности.Чтобы сделать наши намерения более понятными и избежать повторения, мы проектируем модуль многократного использования -
const Parallel = p =>
( { map: async f =>
Promise .all ((await p) .map (x => f (x)))
, filter: async f =>
(await p) .filter (x => f (x))
, flatMap: async f =>
Promise .all ((await p) .map (x => f (x))) .then (ys => [] .concat (...ys))
, // ...
}
)
Теперь наша функция files
намного лучше -
const files = async (path = ".") =>
(await stat (path)) .isDirectory ()
? Parallel (readdir (path))
.flatMap (f => files (join (path, f)))
: [ path ]
Наша search
функция тоже немного очищена -
const search = async (query, path = ".") =>
Parallel (files (path))
.filter (x => basename (x) === query)
Наконец, readPackages
-
const readPackages = async (f, path = ".") =>
Parallel (search ("package.json", path))
.map (readFile)
.then
( buffers =>
buffers .map (b => f (JSON .parse (String (b))))
)
Поведение каждой функции идентично исходным реализациям.Только теперь у нас есть еще более общие функции, которые можно повторно использовать в других областях нашей программы.
В этих связанных вопросах и ответах мы используем модуль Parallel
для реализации функции dirs
который рекурсивно перечисляет все каталоги по заданному пути.