split-by-codeowners 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +78 -7
- package/dist/index.js.map +1 -1
- package/dist-cli/index.js +78 -7
- package/dist-cli/index.js.map +1 -1
- package/package.json +1 -1
package/dist-cli/index.js
CHANGED
|
@@ -2073,6 +2073,35 @@ exports.parseCodeowners = parseCodeowners;
|
|
|
2073
2073
|
exports.ownersForFile = ownersForFile;
|
|
2074
2074
|
const node_fs_1 = __importDefault(__nccwpck_require__(24));
|
|
2075
2075
|
const minimatch_1 = __nccwpck_require__(507);
|
|
2076
|
+
function splitPatternOwners(line) {
|
|
2077
|
+
// CODEOWNERS lines are: <pattern><whitespace><owner>...
|
|
2078
|
+
// Patterns may contain spaces if escaped as `\ `.
|
|
2079
|
+
// We parse by finding the first whitespace not escaped by a backslash.
|
|
2080
|
+
let i = 0;
|
|
2081
|
+
let escaped = false;
|
|
2082
|
+
for (; i < line.length; i++) {
|
|
2083
|
+
const ch = line[i];
|
|
2084
|
+
if (escaped) {
|
|
2085
|
+
escaped = false;
|
|
2086
|
+
continue;
|
|
2087
|
+
}
|
|
2088
|
+
if (ch === "\\") {
|
|
2089
|
+
escaped = true;
|
|
2090
|
+
continue;
|
|
2091
|
+
}
|
|
2092
|
+
if (/\s/.test(ch))
|
|
2093
|
+
break;
|
|
2094
|
+
}
|
|
2095
|
+
const rawPattern = line.slice(0, i).trim();
|
|
2096
|
+
const rest = line.slice(i).trim();
|
|
2097
|
+
if (!rawPattern || !rest)
|
|
2098
|
+
return null;
|
|
2099
|
+
const pattern = rawPattern.replaceAll("\\ ", " ");
|
|
2100
|
+
const owners = rest.split(/\s+/).filter(Boolean);
|
|
2101
|
+
if (!owners.length)
|
|
2102
|
+
return null;
|
|
2103
|
+
return { pattern, owners };
|
|
2104
|
+
}
|
|
2076
2105
|
function parseCodeowners(path) {
|
|
2077
2106
|
const text = node_fs_1.default.readFileSync(path, "utf8");
|
|
2078
2107
|
const rules = [];
|
|
@@ -2080,18 +2109,60 @@ function parseCodeowners(path) {
|
|
|
2080
2109
|
const line = raw.trim();
|
|
2081
2110
|
if (!line || line.startsWith("#"))
|
|
2082
2111
|
continue;
|
|
2083
|
-
const
|
|
2084
|
-
if (
|
|
2112
|
+
const parsed = splitPatternOwners(line);
|
|
2113
|
+
if (!parsed)
|
|
2085
2114
|
continue;
|
|
2086
|
-
rules.push({ pattern:
|
|
2115
|
+
rules.push({ pattern: parsed.pattern, owners: parsed.owners });
|
|
2087
2116
|
}
|
|
2088
2117
|
return rules;
|
|
2089
2118
|
}
|
|
2119
|
+
function hasGlobMagic(p) {
|
|
2120
|
+
// Good-enough heuristic for gitignore-style globs.
|
|
2121
|
+
return /[*?\[]/.test(p);
|
|
2122
|
+
}
|
|
2090
2123
|
function matches(file, pattern) {
|
|
2091
|
-
|
|
2092
|
-
//
|
|
2093
|
-
|
|
2094
|
-
|
|
2124
|
+
// GitHub CODEOWNERS patterns are gitignore-like:
|
|
2125
|
+
// - Leading `/` anchors to repo root
|
|
2126
|
+
// - Pattern without `/` matches a basename anywhere
|
|
2127
|
+
// - A directory pattern matches everything under it (e.g. `/frontend/admin` should match `/frontend/admin/**`)
|
|
2128
|
+
const f = file.replace(/\\/g, "/").replace(/^\.?\//, "");
|
|
2129
|
+
let pat = pattern.replace(/\\/g, "/").trim();
|
|
2130
|
+
if (!pat)
|
|
2131
|
+
return false;
|
|
2132
|
+
const anchored = pat.startsWith("/");
|
|
2133
|
+
if (anchored)
|
|
2134
|
+
pat = pat.slice(1);
|
|
2135
|
+
// Directory patterns: trailing `/` means directory; also treat non-glob literal paths as directory-or-file match.
|
|
2136
|
+
const trailingSlash = pat.endsWith("/");
|
|
2137
|
+
if (trailingSlash)
|
|
2138
|
+
pat = pat.slice(0, -1);
|
|
2139
|
+
const isLiteral = !hasGlobMagic(pat);
|
|
2140
|
+
const hasSlash = pat.includes("/");
|
|
2141
|
+
// Literal path: match exact file OR directory subtree.
|
|
2142
|
+
if (isLiteral && (anchored || hasSlash)) {
|
|
2143
|
+
return f === pat || f.startsWith(pat + "/");
|
|
2144
|
+
}
|
|
2145
|
+
// Literal basename: match basename OR directory subtree anywhere.
|
|
2146
|
+
if (isLiteral && !hasSlash) {
|
|
2147
|
+
const base = f.split("/").pop() ?? f;
|
|
2148
|
+
if (base === pat)
|
|
2149
|
+
return true;
|
|
2150
|
+
return f.includes("/" + pat + "/");
|
|
2151
|
+
}
|
|
2152
|
+
// Glob patterns: use minimatch.
|
|
2153
|
+
// - If anchored: match from repo root.
|
|
2154
|
+
// - If not anchored and no slash: match basename anywhere.
|
|
2155
|
+
// - If not anchored but includes a slash: match relative to repo root (like a root CODEOWNERS / .gitignore).
|
|
2156
|
+
const matchBase = !anchored && !hasSlash;
|
|
2157
|
+
const mm = (0, minimatch_1.minimatch)(f, pat, { dot: true, matchBase });
|
|
2158
|
+
if (mm)
|
|
2159
|
+
return true;
|
|
2160
|
+
// If the pattern explicitly denotes a directory (trailing slash), also match directory subtree.
|
|
2161
|
+
if (trailingSlash) {
|
|
2162
|
+
// Convert `dir/` to subtree match.
|
|
2163
|
+
return (0, minimatch_1.minimatch)(f, pat + "/**", { dot: true, matchBase: false });
|
|
2164
|
+
}
|
|
2165
|
+
return false;
|
|
2095
2166
|
}
|
|
2096
2167
|
function ownersForFile(file, rules) {
|
|
2097
2168
|
let hit;
|