# shared/matchers/location.matcher.ts

> 162 lines of code and 35 definitions.

Tree: Governance tree
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/governance#file-governance-shared-matchers-location-matcher-ts
Source text: https://banes-lab.com/source/governance/shared/matchers/location.matcher.ts.txt

Listed in [shared/matchers](https://banes-lab.com/api/source/governance/shared/matchers.md), after [shared/matchers/folder.matcher.ts](https://banes-lab.com/source/governance/shared/matchers/folder.matcher.ts.md) and before [shared/matchers/pattern.matcher.ts](https://banes-lab.com/source/governance/shared/matchers/pattern.matcher.ts.md).

## Definitions

- `stringLiteralsOf` (lexical_declaration, line 111, exported)
- `isSegmentLike` (lexical_declaration, line 7, exported)
- `isMediaType` (lexical_declaration, line 59)
- `isSpecifier` (lexical_declaration, line 3)
- `segmentCount` (lexical_declaration, line 29)
- `isSystemPath` (lexical_declaration, line 33)
- `wordBefore` (lexical_declaration, line 51)
- `skipToLineEnd` (lexical_declaration, line 83)
- `skipToBlockEnd` (lexical_declaration, line 88)
- `isPathLike` (lexical_declaration, line 67, exported)
- `stripComments` (lexical_declaration, line 93, exported)
- `hintLiteralsOf` (lexical_declaration, line 169, exported)
- `NON_PATH_CHARS` (lexical_declaration, line 1)
- `MIN_PATH_SEGMENTS` (lexical_declaration, line 25)
- `SYSTEM_ROOTS` (lexical_declaration, line 27)
- `MEDIA_TOP_LEVEL_TYPES` (lexical_declaration, line 37)
- `WORD_STOPS` (lexical_declaration, line 49)
- `start` (lexical_declaration, line 52)
- `slash` (lexical_declaration, line 60)
- `nl` (lexical_declaration, line 84)
- `end` (lexical_declaration, line 89)
- `QUOTES` (lexical_declaration, line 109)
- `i` (lexical_declaration, line 113, exported)
- `ch` (lexical_declaration, line 115, exported)
- `body` (lexical_declaration, line 124, exported)
- `PATH_QUALIFIER` (lexical_declaration, line 133)
- `isPathJoin` (lexical_declaration, line 135)
- `joinedPathsOf` (lexical_declaration, line 145, exported)
- `marker` (lexical_declaration, line 147, exported)
- `segments` (lexical_declaration, line 154, exported)
- `HINT_MARKER` (lexical_declaration, line 167)
- `out` (lexical_declaration, line 170, exported)
- `at` (lexical_declaration, line 171, exported)
- `open` (lexical_declaration, line 173, exported)
- `close` (lexical_declaration, line 174, exported)

## Used by

- [rules/eslint/closure-rule-declarations-resolve.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/closure-rule-declarations-resolve.eslint.rule.ts.md)

## Source

```typescript
const NON_PATH_CHARS = ["<", ">", "(", ")", ",", "::", "${", "*", "\\"];

const isSpecifier = function isSpecifier(value: string): boolean {
    return value.startsWith("@") || value.startsWith("http") || value.startsWith("node:");
};

export const isSegmentLike = function isSegmentLike(value: string): boolean {
    if (value.length < 2) {
        return false;
    }
    if (value.includes(" ") || value.includes("\n")) {
        return false;
    }
    for (const c of NON_PATH_CHARS) {
        if (value.includes(c)) {
            return false;
        }
    }
    if (value.startsWith("-") || isSpecifier(value)) {
        return false;
    }
    return !value.endsWith(".json");
};

const MIN_PATH_SEGMENTS = 2;

const SYSTEM_ROOTS: ReadonlySet<string> = new Set(["usr", "opt", "bin", "etc", "var", "tmp", "Applications"]);

const segmentCount = function segmentCount(value: string): number {
    return value.split("/").filter((segment) => segment.length > 0).length;
};

const isSystemPath = function isSystemPath(value: string): boolean {
    return value.startsWith("/") && SYSTEM_ROOTS.has(value.split("/")[1] ?? "");
};

const MEDIA_TOP_LEVEL_TYPES: ReadonlySet<string> = new Set([
    "application",
    "audio",
    "font",
    "image",
    "message",
    "model",
    "multipart",
    "text",
    "video",
]);

const WORD_STOPS: ReadonlySet<string> = new Set(['"', "'", "=", "[", " "]);

const wordBefore = function wordBefore(text: string, end: number): string {
    let start = end;
    while (start > 0 && !WORD_STOPS.has(text.charAt(start - 1))) {
        start -= 1;
    }
    return text.slice(start, end);
};

const isMediaType = function isMediaType(value: string): boolean {
    const slash = value.indexOf("/");
    if (slash === -1 || value.includes("/", slash + 1)) {
        return false;
    }
    return MEDIA_TOP_LEVEL_TYPES.has(wordBefore(value, slash));
};

export const isPathLike = function isPathLike(value: string): boolean {
    if (value.length < 4) {
        return false;
    }
    if (!isSegmentLike(value) || isSystemPath(value) || isMediaType(value)) {
        return false;
    }
    if (value.startsWith(".")) {
        return false;
    }
    if (value.includes("/")) {
        return segmentCount(value) >= MIN_PATH_SEGMENTS;
    }
    return value.endsWith(".ts");
};

const skipToLineEnd = function skipToLineEnd(text: string, at: number): number {
    const nl = text.indexOf("\n", at);
    return nl === -1 ? text.length : nl;
};

const skipToBlockEnd = function skipToBlockEnd(text: string, at: number): number {
    const end = text.indexOf("*/", at + 2);
    return end === -1 ? text.length : end + 2;
};

export const stripComments = function stripComments(text: string): string {
    let out = "";
    let i = 0;
    while (i < text.length) {
        if (text.startsWith("//", i)) {
            i = skipToLineEnd(text, i);
        } else if (text.startsWith("/*", i)) {
            i = skipToBlockEnd(text, i);
        } else {
            out += text[i];
            i += 1;
        }
    }
    return out;
};

const QUOTES = new Set(['"', "'"]);

export const stringLiteralsOf = function stringLiteralsOf(text: string): string[] {
    const out: string[] = [];
    let i = 0;
    while (i < text.length) {
        const ch = text[i] ?? "";
        if (!QUOTES.has(ch)) {
            i += 1;
            continue;
        }
        const close = text.indexOf(ch, i + 1);
        if (close === -1) {
            return out;
        }
        const body = text.slice(i + 1, close);
        if (!body.includes("\n")) {
            out.push(body);
        }
        i = close + 1;
    }
    return out;
};

const PATH_QUALIFIER = "path.";

const isPathJoin = function isPathJoin(text: string, at: number): boolean {
    if (at === 0) {
        return true;
    }
    if (text[at - 1] !== ".") {
        return true;
    }
    return text.slice(Math.max(0, at - PATH_QUALIFIER.length), at) === PATH_QUALIFIER;
};

export const joinedPathsOf = function joinedPathsOf(text: string): string[] {
    const out: string[] = [];
    const marker = "join(";
    let at = text.indexOf(marker);
    while (at >= 0) {
        const close = text.indexOf(")", at);
        if (close === -1) {
            break;
        }
        const segments = isPathJoin(text, at)
            ? stringLiteralsOf(text.slice(at + marker.length, close)).filter(
                  (s) => s !== ".." && s !== "." && s.length > 0,
              )
            : [];
        if (segments.length > 0) {
            out.push(segments.join("/"));
        }
        at = text.indexOf(marker, close);
    }
    return out;
};

const HINT_MARKER = "_HINTS";

export const hintLiteralsOf = function hintLiteralsOf(text: string): Set<string> {
    const out = new Set<string>();
    let at = text.indexOf(HINT_MARKER);
    while (at >= 0) {
        const open = text.indexOf("[", at);
        const close = open === -1 ? -1 : text.indexOf("]", open);
        if (open !== -1 && close > open) {
            for (const s of stringLiteralsOf(text.slice(open, close))) {
                out.add(s);
            }
        }
        at = text.indexOf(HINT_MARKER, at + HINT_MARKER.length);
    }
    return out;
};
```
