Adjust European short trip heuristic from >3 days to >1 day to correctly detect when user has returned home from European trips. This fixes the April 29-30, 2023 case where the location incorrectly showed "Sankt Georg, Hamburg" instead of "Bristol" when the user was free (no events scheduled) after the foss-north trip ended on April 27. The previous logic required more than 3 days to pass before assuming return home from European countries, but for short European trips by rail/ferry, users typically return within 1-2 days. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			239 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			239 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/**
 | 
						|
 * @fileoverview `IgnorePattern` class.
 | 
						|
 *
 | 
						|
 * `IgnorePattern` class has the set of glob patterns and the base path.
 | 
						|
 *
 | 
						|
 * It provides two static methods.
 | 
						|
 *
 | 
						|
 * - `IgnorePattern.createDefaultIgnore(cwd)`
 | 
						|
 *      Create the default predicate function.
 | 
						|
 * - `IgnorePattern.createIgnore(ignorePatterns)`
 | 
						|
 *      Create the predicate function from multiple `IgnorePattern` objects.
 | 
						|
 *
 | 
						|
 * It provides two properties and a method.
 | 
						|
 *
 | 
						|
 * - `patterns`
 | 
						|
 *      The glob patterns that ignore to lint.
 | 
						|
 * - `basePath`
 | 
						|
 *      The base path of the glob patterns. If absolute paths existed in the
 | 
						|
 *      glob patterns, those are handled as relative paths to the base path.
 | 
						|
 * - `getPatternsRelativeTo(basePath)`
 | 
						|
 *      Get `patterns` as modified for a given base path. It modifies the
 | 
						|
 *      absolute paths in the patterns as prepending the difference of two base
 | 
						|
 *      paths.
 | 
						|
 *
 | 
						|
 * `ConfigArrayFactory` creates `IgnorePattern` objects when it processes
 | 
						|
 * `ignorePatterns` properties.
 | 
						|
 *
 | 
						|
 * @author Toru Nagashima <https://github.com/mysticatea>
 | 
						|
 */
 | 
						|
 | 
						|
//------------------------------------------------------------------------------
 | 
						|
// Requirements
 | 
						|
//------------------------------------------------------------------------------
 | 
						|
 | 
						|
import assert from "assert";
 | 
						|
import path from "path";
 | 
						|
import ignore from "ignore";
 | 
						|
import debugOrig from "debug";
 | 
						|
 | 
						|
const debug = debugOrig("eslintrc:ignore-pattern");
 | 
						|
 | 
						|
/** @typedef {ReturnType<import("ignore").default>} Ignore */
 | 
						|
 | 
						|
//------------------------------------------------------------------------------
 | 
						|
// Helpers
 | 
						|
//------------------------------------------------------------------------------
 | 
						|
 | 
						|
/**
 | 
						|
 * Get the path to the common ancestor directory of given paths.
 | 
						|
 * @param {string[]} sourcePaths The paths to calculate the common ancestor.
 | 
						|
 * @returns {string} The path to the common ancestor directory.
 | 
						|
 */
 | 
						|
function getCommonAncestorPath(sourcePaths) {
 | 
						|
    let result = sourcePaths[0];
 | 
						|
 | 
						|
    for (let i = 1; i < sourcePaths.length; ++i) {
 | 
						|
        const a = result;
 | 
						|
        const b = sourcePaths[i];
 | 
						|
 | 
						|
        // Set the shorter one (it's the common ancestor if one includes the other).
 | 
						|
        result = a.length < b.length ? a : b;
 | 
						|
 | 
						|
        // Set the common ancestor.
 | 
						|
        for (let j = 0, lastSepPos = 0; j < a.length && j < b.length; ++j) {
 | 
						|
            if (a[j] !== b[j]) {
 | 
						|
                result = a.slice(0, lastSepPos);
 | 
						|
                break;
 | 
						|
            }
 | 
						|
            if (a[j] === path.sep) {
 | 
						|
                lastSepPos = j;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    let resolvedResult = result || path.sep;
 | 
						|
 | 
						|
    // if Windows common ancestor is root of drive must have trailing slash to be absolute.
 | 
						|
    if (resolvedResult && resolvedResult.endsWith(":") && process.platform === "win32") {
 | 
						|
        resolvedResult += path.sep;
 | 
						|
    }
 | 
						|
    return resolvedResult;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Make relative path.
 | 
						|
 * @param {string} from The source path to get relative path.
 | 
						|
 * @param {string} to The destination path to get relative path.
 | 
						|
 * @returns {string} The relative path.
 | 
						|
 */
 | 
						|
function relative(from, to) {
 | 
						|
    const relPath = path.relative(from, to);
 | 
						|
 | 
						|
    if (path.sep === "/") {
 | 
						|
        return relPath;
 | 
						|
    }
 | 
						|
    return relPath.split(path.sep).join("/");
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Get the trailing slash if existed.
 | 
						|
 * @param {string} filePath The path to check.
 | 
						|
 * @returns {string} The trailing slash if existed.
 | 
						|
 */
 | 
						|
function dirSuffix(filePath) {
 | 
						|
    const isDir = (
 | 
						|
        filePath.endsWith(path.sep) ||
 | 
						|
        (process.platform === "win32" && filePath.endsWith("/"))
 | 
						|
    );
 | 
						|
 | 
						|
    return isDir ? "/" : "";
 | 
						|
}
 | 
						|
 | 
						|
const DefaultPatterns = Object.freeze(["/**/node_modules/*"]);
 | 
						|
const DotPatterns = Object.freeze([".*", "!.eslintrc.*", "!../"]);
 | 
						|
 | 
						|
//------------------------------------------------------------------------------
 | 
						|
// Public
 | 
						|
//------------------------------------------------------------------------------
 | 
						|
 | 
						|
class IgnorePattern {
 | 
						|
 | 
						|
    /**
 | 
						|
     * The default patterns.
 | 
						|
     * @type {string[]}
 | 
						|
     */
 | 
						|
    static get DefaultPatterns() {
 | 
						|
        return DefaultPatterns;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Create the default predicate function.
 | 
						|
     * @param {string} cwd The current working directory.
 | 
						|
     * @returns {((filePath:string, dot:boolean) => boolean) & {basePath:string; patterns:string[]}}
 | 
						|
     * The preficate function.
 | 
						|
     * The first argument is an absolute path that is checked.
 | 
						|
     * The second argument is the flag to not ignore dotfiles.
 | 
						|
     * If the predicate function returned `true`, it means the path should be ignored.
 | 
						|
     */
 | 
						|
    static createDefaultIgnore(cwd) {
 | 
						|
        return this.createIgnore([new IgnorePattern(DefaultPatterns, cwd)]);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Create the predicate function from multiple `IgnorePattern` objects.
 | 
						|
     * @param {IgnorePattern[]} ignorePatterns The list of ignore patterns.
 | 
						|
     * @returns {((filePath:string, dot?:boolean) => boolean) & {basePath:string; patterns:string[]}}
 | 
						|
     * The preficate function.
 | 
						|
     * The first argument is an absolute path that is checked.
 | 
						|
     * The second argument is the flag to not ignore dotfiles.
 | 
						|
     * If the predicate function returned `true`, it means the path should be ignored.
 | 
						|
     */
 | 
						|
    static createIgnore(ignorePatterns) {
 | 
						|
        debug("Create with: %o", ignorePatterns);
 | 
						|
 | 
						|
        const basePath = getCommonAncestorPath(ignorePatterns.map(p => p.basePath));
 | 
						|
        const patterns = [].concat(
 | 
						|
            ...ignorePatterns.map(p => p.getPatternsRelativeTo(basePath))
 | 
						|
        );
 | 
						|
        const ig = ignore({ allowRelativePaths: true }).add([...DotPatterns, ...patterns]);
 | 
						|
        const dotIg = ignore({ allowRelativePaths: true }).add(patterns);
 | 
						|
 | 
						|
        debug("  processed: %o", { basePath, patterns });
 | 
						|
 | 
						|
        return Object.assign(
 | 
						|
            (filePath, dot = false) => {
 | 
						|
                assert(path.isAbsolute(filePath), "'filePath' should be an absolute path.");
 | 
						|
                const relPathRaw = relative(basePath, filePath);
 | 
						|
                const relPath = relPathRaw && (relPathRaw + dirSuffix(filePath));
 | 
						|
                const adoptedIg = dot ? dotIg : ig;
 | 
						|
                const result = relPath !== "" && adoptedIg.ignores(relPath);
 | 
						|
 | 
						|
                debug("Check", { filePath, dot, relativePath: relPath, result });
 | 
						|
                return result;
 | 
						|
            },
 | 
						|
            { basePath, patterns }
 | 
						|
        );
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Initialize a new `IgnorePattern` instance.
 | 
						|
     * @param {string[]} patterns The glob patterns that ignore to lint.
 | 
						|
     * @param {string} basePath The base path of `patterns`.
 | 
						|
     */
 | 
						|
    constructor(patterns, basePath) {
 | 
						|
        assert(path.isAbsolute(basePath), "'basePath' should be an absolute path.");
 | 
						|
 | 
						|
        /**
 | 
						|
         * The glob patterns that ignore to lint.
 | 
						|
         * @type {string[]}
 | 
						|
         */
 | 
						|
        this.patterns = patterns;
 | 
						|
 | 
						|
        /**
 | 
						|
         * The base path of `patterns`.
 | 
						|
         * @type {string}
 | 
						|
         */
 | 
						|
        this.basePath = basePath;
 | 
						|
 | 
						|
        /**
 | 
						|
         * If `true` then patterns which don't start with `/` will match the paths to the outside of `basePath`. Defaults to `false`.
 | 
						|
         *
 | 
						|
         * It's set `true` for `.eslintignore`, `package.json`, and `--ignore-path` for backward compatibility.
 | 
						|
         * It's `false` as-is for `ignorePatterns` property in config files.
 | 
						|
         * @type {boolean}
 | 
						|
         */
 | 
						|
        this.loose = false;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Get `patterns` as modified for a given base path. It modifies the
 | 
						|
     * absolute paths in the patterns as prepending the difference of two base
 | 
						|
     * paths.
 | 
						|
     * @param {string} newBasePath The base path.
 | 
						|
     * @returns {string[]} Modifired patterns.
 | 
						|
     */
 | 
						|
    getPatternsRelativeTo(newBasePath) {
 | 
						|
        assert(path.isAbsolute(newBasePath), "'newBasePath' should be an absolute path.");
 | 
						|
        const { basePath, loose, patterns } = this;
 | 
						|
 | 
						|
        if (newBasePath === basePath) {
 | 
						|
            return patterns;
 | 
						|
        }
 | 
						|
        const prefix = `/${relative(newBasePath, basePath)}`;
 | 
						|
 | 
						|
        return patterns.map(pattern => {
 | 
						|
            const negative = pattern.startsWith("!");
 | 
						|
            const head = negative ? "!" : "";
 | 
						|
            const body = negative ? pattern.slice(1) : pattern;
 | 
						|
 | 
						|
            if (body.startsWith("/") || body.startsWith("../")) {
 | 
						|
                return `${head}${prefix}${body}`;
 | 
						|
            }
 | 
						|
            return loose ? pattern : `${head}${prefix}/**/${body}`;
 | 
						|
        });
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
export { IgnorePattern };
 |