| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 | /** * @author Toru Nagashima * See LICENSE file in root directory for full license. */"use strict"const fs = require("fs")const path = require("path")const ignore = require("ignore")const Cache = require("./cache")const exists = require("./exists")const getPackageJson = require("./get-package-json")const cache = new Cache()const SLASH_AT_BEGIN_AND_END = /^!?\/+|^!|\/+$/guconst PARENT_RELATIVE_PATH = /^\.\./uconst NEVER_IGNORED = /^(?:readme\.[^.]*|(?:licen[cs]e|changes|changelog|history)(?:\.[^.]*)?)$/iu/** * Checks whether or not a given file name is a relative path to a ancestor * directory. * * @param {string} filePath - A file name to check. * @returns {boolean} `true` if the file name is a relative path to a ancestor *      directory. */function isAncestorFiles(filePath) {    return PARENT_RELATIVE_PATH.test(filePath)}/** * @param {function} f - A function. * @param {function} g - A function. * @returns {function} A logical-and function of `f` and `g`. */function and(f, g) {    return filePath => f(filePath) && g(filePath)}/** * @param {function} f - A function. * @param {function} g - A function. * @param {function|null} h - A function. * @returns {function} A logical-or function of `f`, `g`, and `h`. */function or(f, g, h) {    return filePath => f(filePath) || g(filePath) || (h && h(filePath))}/** * @param {function} f - A function. * @returns {function} A logical-not function of `f`. */function not(f) {    return filePath => !f(filePath)}/** * Creates a function which checks whether or not a given file is ignoreable. * * @param {object} p - An object of package.json. * @returns {function} A function which checks whether or not a given file is ignoreable. */function filterNeverIgnoredFiles(p) {    const basedir = path.dirname(p.filePath)    const mainFilePath =        typeof p.main === "string" ? path.join(basedir, p.main) : null    return filePath =>        path.join(basedir, filePath) !== mainFilePath &&        filePath !== "package.json" &&        !NEVER_IGNORED.test(path.relative(basedir, filePath))}/** * Creates a function which checks whether or not a given file should be ignored. * * @param {string[]|null} files - File names of whitelist. * @returns {function|null} A function which checks whether or not a given file should be ignored. */function parseWhiteList(files) {    if (!files || !Array.isArray(files)) {        return null    }    const ig = ignore()    const igN = ignore()    let hasN = false    for (const file of files) {        if (typeof file === "string" && file) {            const body = file.replace(SLASH_AT_BEGIN_AND_END, "")            if (file.startsWith("!")) {                igN.add(`${body}`)                igN.add(`${body}/**`)                hasN = true            } else {                ig.add(`/${body}`)                ig.add(`/${body}/**`)            }        }    }    return hasN        ? or(ig.createFilter(), not(igN.createFilter()))        : ig.createFilter()}/** * Creates a function which checks whether or not a given file should be ignored. * * @param {string} basedir - The directory path "package.json" exists. * @param {boolean} filesFieldExists - `true` if `files` field of `package.json` exists. * @returns {function|null} A function which checks whether or not a given file should be ignored. */function parseNpmignore(basedir, filesFieldExists) {    let filePath = path.join(basedir, ".npmignore")    if (!exists(filePath)) {        if (filesFieldExists) {            return null        }        filePath = path.join(basedir, ".gitignore")        if (!exists(filePath)) {            return null        }    }    const ig = ignore()    ig.add(fs.readFileSync(filePath, "utf8"))    return not(ig.createFilter())}/** * Gets an object to check whether a given path should be ignored or not. * The object is created from: * * - `files` field of `package.json` * - `.npmignore` * * @param {string} startPath - A file path to lookup. * @returns {object} *      An object to check whther or not a given path should be ignored. *      The object has a method `match`. *      `match` returns `true` if a given file path should be ignored. */module.exports = function getNpmignore(startPath) {    const retv = { match: isAncestorFiles }    const p = getPackageJson(startPath)    if (p) {        const data = cache.get(p.filePath)        if (data) {            return data        }        const filesIgnore = parseWhiteList(p.files)        const npmignoreIgnore = parseNpmignore(            path.dirname(p.filePath),            Boolean(filesIgnore)        )        if (filesIgnore && npmignoreIgnore) {            retv.match = and(                filterNeverIgnoredFiles(p),                or(isAncestorFiles, filesIgnore, npmignoreIgnore)            )        } else if (filesIgnore) {            retv.match = and(                filterNeverIgnoredFiles(p),                or(isAncestorFiles, filesIgnore)            )        } else if (npmignoreIgnore) {            retv.match = and(                filterNeverIgnoredFiles(p),                or(isAncestorFiles, npmignoreIgnore)            )        }        cache.set(p.filePath, retv)    }    return retv}
 |