/* MIT License http://www.opensource.org/licenses/mit-license.php Author Tobias Koppers @sokra */ "use strict"; const forEachBail = require("./forEachBail"); /** @typedef {import("./Resolver")} Resolver */ /** @typedef {import("./Resolver").JsonObject} JsonObject */ /** @typedef {import("./Resolver").JsonValue} JsonValue */ /** @typedef {import("./Resolver").ResolveContext} ResolveContext */ /** @typedef {import("./Resolver").ResolveRequest} ResolveRequest */ /** * @typedef {Object} DescriptionFileInfo * @property {JsonObject=} content * @property {string} path * @property {string} directory */ /** * @callback ErrorFirstCallback * @param {Error|null=} error * @param {DescriptionFileInfo=} result */ /** * @typedef {Object} Result * @property {string} path path to description file * @property {string} directory directory of description file * @property {JsonObject} content content of description file */ /** * @param {Resolver} resolver resolver * @param {string} directory directory * @param {string[]} filenames filenames * @param {DescriptionFileInfo|undefined} oldInfo oldInfo * @param {ResolveContext} resolveContext resolveContext * @param {ErrorFirstCallback} callback callback */ function loadDescriptionFile( resolver, directory, filenames, oldInfo, resolveContext, callback ) { (function findDescriptionFile() { if (oldInfo && oldInfo.directory === directory) { // We already have info for this directory and can reuse it return callback(null, oldInfo); } forEachBail( filenames, /** * @param {string} filename filename * @param {(err?: null|Error, result?: null|Result) => void} callback callback * @returns {void} */ (filename, callback) => { const descriptionFilePath = resolver.join(directory, filename); if (resolver.fileSystem.readJson) { resolver.fileSystem.readJson(descriptionFilePath, (err, content) => { if (err) { if ( typeof (/** @type {NodeJS.ErrnoException} */ (err).code) !== "undefined" ) { if (resolveContext.missingDependencies) { resolveContext.missingDependencies.add(descriptionFilePath); } return callback(); } if (resolveContext.fileDependencies) { resolveContext.fileDependencies.add(descriptionFilePath); } return onJson(err); } if (resolveContext.fileDependencies) { resolveContext.fileDependencies.add(descriptionFilePath); } onJson(null, content); }); } else { resolver.fileSystem.readFile(descriptionFilePath, (err, content) => { if (err) { if (resolveContext.missingDependencies) { resolveContext.missingDependencies.add(descriptionFilePath); } return callback(); } if (resolveContext.fileDependencies) { resolveContext.fileDependencies.add(descriptionFilePath); } /** @type {JsonObject | undefined} */ let json; if (content) { try { json = JSON.parse(content.toString()); } catch (/** @type {unknown} */ e) { return onJson(/** @type {Error} */ (e)); } } else { return onJson(new Error("No content in file")); } onJson(null, json); }); } /** * @param {null|Error} [err] error * @param {JsonObject} [content] content * @returns {void} */ function onJson(err, content) { if (err) { if (resolveContext.log) resolveContext.log( descriptionFilePath + " (directory description file): " + err ); else err.message = descriptionFilePath + " (directory description file): " + err; return callback(err); } callback(null, { content: /** @type {JsonObject} */ (content), directory, path: descriptionFilePath }); } }, /** * @param {null|Error} [err] error * @param {null|Result} [result] result * @returns {void} */ (err, result) => { if (err) return callback(err); if (result) { return callback(null, result); } else { const dir = cdUp(directory); if (!dir) { return callback(); } else { directory = dir; return findDescriptionFile(); } } } ); })(); } /** * @param {JsonObject} content content * @param {string|string[]} field field * @returns {JsonValue | undefined} field data */ function getField(content, field) { if (!content) return undefined; if (Array.isArray(field)) { /** @type {JsonValue} */ let current = content; for (let j = 0; j < field.length; j++) { if (current === null || typeof current !== "object") { current = null; break; } current = /** @type {JsonObject} */ (current)[field[j]]; } return current; } else { return content[field]; } } /** * @param {string} directory directory * @returns {string|null} parent directory or null */ function cdUp(directory) { if (directory === "/") return null; const i = directory.lastIndexOf("/"), j = directory.lastIndexOf("\\"); const p = i < 0 ? j : j < 0 ? i : i < j ? j : i; if (p < 0) return null; return directory.slice(0, p || 1); } exports.loadDescriptionFile = loadDescriptionFile; exports.getField = getField; exports.cdUp = cdUp;