Spaces:
Running
Running
/* | |
MIT License http://www.opensource.org/licenses/mit-license.php | |
Author Tobias Koppers @sokra | |
*/ | |
; | |
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 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, /** @type {JsonObject} */ (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; | |