@doccov/cli 0.28.2 → 0.29.0
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/cli.js +2035 -86
- package/package.json +3 -2
package/dist/cli.js
CHANGED
|
@@ -74,8 +74,8 @@ ${formatIssues(issues)}`);
|
|
|
74
74
|
// src/config/index.ts
|
|
75
75
|
var defineConfig = (config) => config;
|
|
76
76
|
// src/cli.ts
|
|
77
|
-
import { readFileSync as
|
|
78
|
-
import * as
|
|
77
|
+
import { readFileSync as readFileSync7 } from "node:fs";
|
|
78
|
+
import * as path12 from "node:path";
|
|
79
79
|
import { fileURLToPath } from "node:url";
|
|
80
80
|
import { Command } from "commander";
|
|
81
81
|
|
|
@@ -1650,6 +1650,1897 @@ function displayApiSurfaceOutput(doccov, deps) {
|
|
|
1650
1650
|
|
|
1651
1651
|
// src/commands/check/validation.ts
|
|
1652
1652
|
import { validateExamples } from "@doccov/sdk";
|
|
1653
|
+
|
|
1654
|
+
// src/commands/check/docs-fetcher.ts
|
|
1655
|
+
import * as cheerio from "cheerio";
|
|
1656
|
+
|
|
1657
|
+
// src/commands/check/docsCache.ts
|
|
1658
|
+
import * as crypto from "node:crypto";
|
|
1659
|
+
import * as fs4 from "node:fs";
|
|
1660
|
+
import * as path6 from "node:path";
|
|
1661
|
+
var DEFAULT_TTL = 60 * 60 * 1000;
|
|
1662
|
+
function getCacheDir(cwd) {
|
|
1663
|
+
return path6.join(cwd, ".doccov", "cache");
|
|
1664
|
+
}
|
|
1665
|
+
function getCacheSubdir(cwd, type) {
|
|
1666
|
+
return path6.join(getCacheDir(cwd), type);
|
|
1667
|
+
}
|
|
1668
|
+
function ensureCacheDir(cwd) {
|
|
1669
|
+
const subdirs = ["urls", "github", "gitlab"];
|
|
1670
|
+
for (const subdir of subdirs) {
|
|
1671
|
+
const dir = getCacheSubdir(cwd, subdir);
|
|
1672
|
+
if (!fs4.existsSync(dir)) {
|
|
1673
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
function getCacheFilename(key) {
|
|
1678
|
+
const hash = crypto.createHash("sha256").update(key).digest("hex").slice(0, 16);
|
|
1679
|
+
const sanitized = key.replace(/[^a-zA-Z0-9-_]/g, "_").slice(0, 50);
|
|
1680
|
+
return `${sanitized}_${hash}.json`;
|
|
1681
|
+
}
|
|
1682
|
+
function getFromCache(cwd, type, key, ttl = DEFAULT_TTL) {
|
|
1683
|
+
const cacheFile = path6.join(getCacheSubdir(cwd, type), getCacheFilename(key));
|
|
1684
|
+
if (!fs4.existsSync(cacheFile)) {
|
|
1685
|
+
return null;
|
|
1686
|
+
}
|
|
1687
|
+
try {
|
|
1688
|
+
const data = JSON.parse(fs4.readFileSync(cacheFile, "utf-8"));
|
|
1689
|
+
if (Date.now() - data.fetchedAt > ttl) {
|
|
1690
|
+
return null;
|
|
1691
|
+
}
|
|
1692
|
+
return data;
|
|
1693
|
+
} catch {
|
|
1694
|
+
return null;
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
function writeToCache(cwd, type, key, content) {
|
|
1698
|
+
ensureCacheDir(cwd);
|
|
1699
|
+
const cacheFile = path6.join(getCacheSubdir(cwd, type), getCacheFilename(key));
|
|
1700
|
+
const entry = {
|
|
1701
|
+
content,
|
|
1702
|
+
fetchedAt: Date.now(),
|
|
1703
|
+
source: key
|
|
1704
|
+
};
|
|
1705
|
+
fs4.writeFileSync(cacheFile, JSON.stringify(entry, null, 2));
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
// src/commands/check/docs-fetcher.ts
|
|
1709
|
+
var DEFAULT_TIMEOUT = 1e4;
|
|
1710
|
+
var DEFAULT_TTL2 = 60 * 60 * 1000;
|
|
1711
|
+
async function fetchUrlDocs(source, options = {}) {
|
|
1712
|
+
const { timeout = DEFAULT_TIMEOUT, ttl = DEFAULT_TTL2, useCache = true, cwd = process.cwd() } = options;
|
|
1713
|
+
if (useCache) {
|
|
1714
|
+
const cached = getFromCache(cwd, "urls", source.url, ttl);
|
|
1715
|
+
if (cached) {
|
|
1716
|
+
const doc = parseContentToDoc(cached.content, source.url);
|
|
1717
|
+
return { source, doc, cached: true };
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
try {
|
|
1721
|
+
const controller = new AbortController;
|
|
1722
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
1723
|
+
const response = await fetch(source.url, {
|
|
1724
|
+
signal: controller.signal,
|
|
1725
|
+
headers: {
|
|
1726
|
+
"User-Agent": "doccov-cli",
|
|
1727
|
+
Accept: "text/html, text/markdown, text/plain, */*"
|
|
1728
|
+
},
|
|
1729
|
+
redirect: "follow"
|
|
1730
|
+
});
|
|
1731
|
+
clearTimeout(timeoutId);
|
|
1732
|
+
if (!response.ok) {
|
|
1733
|
+
return {
|
|
1734
|
+
source,
|
|
1735
|
+
doc: null,
|
|
1736
|
+
error: `HTTP ${response.status}: ${response.statusText}`
|
|
1737
|
+
};
|
|
1738
|
+
}
|
|
1739
|
+
const content = await response.text();
|
|
1740
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
1741
|
+
if (useCache) {
|
|
1742
|
+
writeToCache(cwd, "urls", source.url, content);
|
|
1743
|
+
}
|
|
1744
|
+
const doc = parseContentToDoc(content, source.url, contentType);
|
|
1745
|
+
return { source, doc };
|
|
1746
|
+
} catch (err) {
|
|
1747
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1748
|
+
if (message.includes("abort")) {
|
|
1749
|
+
return { source, doc: null, error: `Timeout after ${timeout}ms` };
|
|
1750
|
+
}
|
|
1751
|
+
return { source, doc: null, error: message };
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
async function fetchUrlDocsBatch(sources, options = {}) {
|
|
1755
|
+
return Promise.all(sources.map((s) => fetchUrlDocs(s, options)));
|
|
1756
|
+
}
|
|
1757
|
+
function parseContentToDoc(content, url, contentType) {
|
|
1758
|
+
const isMarkdown = contentType?.includes("markdown") || url.endsWith(".md") || url.endsWith(".mdx");
|
|
1759
|
+
if (isMarkdown) {
|
|
1760
|
+
return parseMarkdownContent(content, url);
|
|
1761
|
+
}
|
|
1762
|
+
return parseHtmlContent(content, url);
|
|
1763
|
+
}
|
|
1764
|
+
function parseMarkdownContent(content, url) {
|
|
1765
|
+
const codeBlocks = extractMarkdownCodeBlocks(content);
|
|
1766
|
+
return { path: url, codeBlocks };
|
|
1767
|
+
}
|
|
1768
|
+
function parseHtmlContent(content, url) {
|
|
1769
|
+
const $ = cheerio.load(content);
|
|
1770
|
+
const codeBlocks = [];
|
|
1771
|
+
const selectors = [
|
|
1772
|
+
"pre code",
|
|
1773
|
+
"pre.highlight code",
|
|
1774
|
+
"pre.prism-code",
|
|
1775
|
+
".theme-code-block pre",
|
|
1776
|
+
"pre.nextra-code",
|
|
1777
|
+
"[data-language] code",
|
|
1778
|
+
"pre.shiki code",
|
|
1779
|
+
".code-block pre",
|
|
1780
|
+
".codeblock pre"
|
|
1781
|
+
];
|
|
1782
|
+
const seen = new Set;
|
|
1783
|
+
for (const selector of selectors) {
|
|
1784
|
+
$(selector).each((_, el) => {
|
|
1785
|
+
const $el = $(el);
|
|
1786
|
+
const code = $el.text().trim();
|
|
1787
|
+
if (!code || seen.has(code))
|
|
1788
|
+
return;
|
|
1789
|
+
seen.add(code);
|
|
1790
|
+
const lang = detectLanguage($el, $);
|
|
1791
|
+
if (!isExecutableLang(lang))
|
|
1792
|
+
return;
|
|
1793
|
+
codeBlocks.push({
|
|
1794
|
+
lang: lang || "ts",
|
|
1795
|
+
code,
|
|
1796
|
+
lineStart: 0,
|
|
1797
|
+
lineEnd: 0
|
|
1798
|
+
});
|
|
1799
|
+
});
|
|
1800
|
+
}
|
|
1801
|
+
return { path: url, codeBlocks };
|
|
1802
|
+
}
|
|
1803
|
+
function detectLanguage($el, $) {
|
|
1804
|
+
const classes = [
|
|
1805
|
+
$el.attr("class") ?? "",
|
|
1806
|
+
$el.parent().attr("class") ?? "",
|
|
1807
|
+
$el.attr("data-language") ?? "",
|
|
1808
|
+
$el.parent().attr("data-language") ?? ""
|
|
1809
|
+
].join(" ");
|
|
1810
|
+
const langMatch = classes.match(/(?:language|lang|prism)-(\w+)/i);
|
|
1811
|
+
if (langMatch) {
|
|
1812
|
+
return normalizeLanguage(langMatch[1]);
|
|
1813
|
+
}
|
|
1814
|
+
const dataLang = $el.attr("data-lang") || $el.parent().attr("data-lang");
|
|
1815
|
+
if (dataLang) {
|
|
1816
|
+
return normalizeLanguage(dataLang);
|
|
1817
|
+
}
|
|
1818
|
+
return null;
|
|
1819
|
+
}
|
|
1820
|
+
function normalizeLanguage(lang) {
|
|
1821
|
+
const lower = lang.toLowerCase();
|
|
1822
|
+
const aliases = {
|
|
1823
|
+
typescript: "ts",
|
|
1824
|
+
javascript: "js",
|
|
1825
|
+
tsx: "tsx",
|
|
1826
|
+
jsx: "jsx",
|
|
1827
|
+
node: "js",
|
|
1828
|
+
esm: "js"
|
|
1829
|
+
};
|
|
1830
|
+
return aliases[lower] ?? lower;
|
|
1831
|
+
}
|
|
1832
|
+
function isExecutableLang(lang) {
|
|
1833
|
+
if (!lang)
|
|
1834
|
+
return false;
|
|
1835
|
+
const executableLangs = new Set(["ts", "typescript", "js", "javascript", "tsx", "jsx", "mts", "mjs"]);
|
|
1836
|
+
return executableLangs.has(lang.toLowerCase());
|
|
1837
|
+
}
|
|
1838
|
+
function extractMarkdownCodeBlocks(content) {
|
|
1839
|
+
const codeBlocks = [];
|
|
1840
|
+
const lines = content.split(`
|
|
1841
|
+
`);
|
|
1842
|
+
let inBlock = false;
|
|
1843
|
+
let blockLang = "";
|
|
1844
|
+
let blockCode = [];
|
|
1845
|
+
let blockStart = 0;
|
|
1846
|
+
for (let i = 0;i < lines.length; i++) {
|
|
1847
|
+
const line = lines[i];
|
|
1848
|
+
if (!inBlock && line.startsWith("```")) {
|
|
1849
|
+
inBlock = true;
|
|
1850
|
+
blockLang = line.slice(3).trim().split(/\s/)[0] || "";
|
|
1851
|
+
blockCode = [];
|
|
1852
|
+
blockStart = i + 1;
|
|
1853
|
+
} else if (inBlock && line.startsWith("```")) {
|
|
1854
|
+
if (isExecutableLang(blockLang)) {
|
|
1855
|
+
codeBlocks.push({
|
|
1856
|
+
lang: normalizeLanguage(blockLang) || "ts",
|
|
1857
|
+
code: blockCode.join(`
|
|
1858
|
+
`),
|
|
1859
|
+
lineStart: blockStart,
|
|
1860
|
+
lineEnd: i
|
|
1861
|
+
});
|
|
1862
|
+
}
|
|
1863
|
+
inBlock = false;
|
|
1864
|
+
blockLang = "";
|
|
1865
|
+
blockCode = [];
|
|
1866
|
+
} else if (inBlock) {
|
|
1867
|
+
blockCode.push(line);
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
return codeBlocks;
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
// ../../node_modules/@isaacs/balanced-match/dist/esm/index.js
|
|
1874
|
+
var balanced = (a, b, str) => {
|
|
1875
|
+
const ma = a instanceof RegExp ? maybeMatch(a, str) : a;
|
|
1876
|
+
const mb = b instanceof RegExp ? maybeMatch(b, str) : b;
|
|
1877
|
+
const r = ma !== null && mb != null && range(ma, mb, str);
|
|
1878
|
+
return r && {
|
|
1879
|
+
start: r[0],
|
|
1880
|
+
end: r[1],
|
|
1881
|
+
pre: str.slice(0, r[0]),
|
|
1882
|
+
body: str.slice(r[0] + ma.length, r[1]),
|
|
1883
|
+
post: str.slice(r[1] + mb.length)
|
|
1884
|
+
};
|
|
1885
|
+
};
|
|
1886
|
+
var maybeMatch = (reg, str) => {
|
|
1887
|
+
const m = str.match(reg);
|
|
1888
|
+
return m ? m[0] : null;
|
|
1889
|
+
};
|
|
1890
|
+
var range = (a, b, str) => {
|
|
1891
|
+
let begs, beg, left, right = undefined, result;
|
|
1892
|
+
let ai = str.indexOf(a);
|
|
1893
|
+
let bi = str.indexOf(b, ai + 1);
|
|
1894
|
+
let i = ai;
|
|
1895
|
+
if (ai >= 0 && bi > 0) {
|
|
1896
|
+
if (a === b) {
|
|
1897
|
+
return [ai, bi];
|
|
1898
|
+
}
|
|
1899
|
+
begs = [];
|
|
1900
|
+
left = str.length;
|
|
1901
|
+
while (i >= 0 && !result) {
|
|
1902
|
+
if (i === ai) {
|
|
1903
|
+
begs.push(i);
|
|
1904
|
+
ai = str.indexOf(a, i + 1);
|
|
1905
|
+
} else if (begs.length === 1) {
|
|
1906
|
+
const r = begs.pop();
|
|
1907
|
+
if (r !== undefined)
|
|
1908
|
+
result = [r, bi];
|
|
1909
|
+
} else {
|
|
1910
|
+
beg = begs.pop();
|
|
1911
|
+
if (beg !== undefined && beg < left) {
|
|
1912
|
+
left = beg;
|
|
1913
|
+
right = bi;
|
|
1914
|
+
}
|
|
1915
|
+
bi = str.indexOf(b, i + 1);
|
|
1916
|
+
}
|
|
1917
|
+
i = ai < bi && ai >= 0 ? ai : bi;
|
|
1918
|
+
}
|
|
1919
|
+
if (begs.length && right !== undefined) {
|
|
1920
|
+
result = [left, right];
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
return result;
|
|
1924
|
+
};
|
|
1925
|
+
|
|
1926
|
+
// ../../node_modules/@isaacs/brace-expansion/dist/esm/index.js
|
|
1927
|
+
var escSlash = "\x00SLASH" + Math.random() + "\x00";
|
|
1928
|
+
var escOpen = "\x00OPEN" + Math.random() + "\x00";
|
|
1929
|
+
var escClose = "\x00CLOSE" + Math.random() + "\x00";
|
|
1930
|
+
var escComma = "\x00COMMA" + Math.random() + "\x00";
|
|
1931
|
+
var escPeriod = "\x00PERIOD" + Math.random() + "\x00";
|
|
1932
|
+
var escSlashPattern = new RegExp(escSlash, "g");
|
|
1933
|
+
var escOpenPattern = new RegExp(escOpen, "g");
|
|
1934
|
+
var escClosePattern = new RegExp(escClose, "g");
|
|
1935
|
+
var escCommaPattern = new RegExp(escComma, "g");
|
|
1936
|
+
var escPeriodPattern = new RegExp(escPeriod, "g");
|
|
1937
|
+
var slashPattern = /\\\\/g;
|
|
1938
|
+
var openPattern = /\\{/g;
|
|
1939
|
+
var closePattern = /\\}/g;
|
|
1940
|
+
var commaPattern = /\\,/g;
|
|
1941
|
+
var periodPattern = /\\./g;
|
|
1942
|
+
function numeric(str) {
|
|
1943
|
+
return !isNaN(str) ? parseInt(str, 10) : str.charCodeAt(0);
|
|
1944
|
+
}
|
|
1945
|
+
function escapeBraces(str) {
|
|
1946
|
+
return str.replace(slashPattern, escSlash).replace(openPattern, escOpen).replace(closePattern, escClose).replace(commaPattern, escComma).replace(periodPattern, escPeriod);
|
|
1947
|
+
}
|
|
1948
|
+
function unescapeBraces(str) {
|
|
1949
|
+
return str.replace(escSlashPattern, "\\").replace(escOpenPattern, "{").replace(escClosePattern, "}").replace(escCommaPattern, ",").replace(escPeriodPattern, ".");
|
|
1950
|
+
}
|
|
1951
|
+
function parseCommaParts(str) {
|
|
1952
|
+
if (!str) {
|
|
1953
|
+
return [""];
|
|
1954
|
+
}
|
|
1955
|
+
const parts = [];
|
|
1956
|
+
const m = balanced("{", "}", str);
|
|
1957
|
+
if (!m) {
|
|
1958
|
+
return str.split(",");
|
|
1959
|
+
}
|
|
1960
|
+
const { pre, body, post } = m;
|
|
1961
|
+
const p = pre.split(",");
|
|
1962
|
+
p[p.length - 1] += "{" + body + "}";
|
|
1963
|
+
const postParts = parseCommaParts(post);
|
|
1964
|
+
if (post.length) {
|
|
1965
|
+
p[p.length - 1] += postParts.shift();
|
|
1966
|
+
p.push.apply(p, postParts);
|
|
1967
|
+
}
|
|
1968
|
+
parts.push.apply(parts, p);
|
|
1969
|
+
return parts;
|
|
1970
|
+
}
|
|
1971
|
+
function expand(str) {
|
|
1972
|
+
if (!str) {
|
|
1973
|
+
return [];
|
|
1974
|
+
}
|
|
1975
|
+
if (str.slice(0, 2) === "{}") {
|
|
1976
|
+
str = "\\{\\}" + str.slice(2);
|
|
1977
|
+
}
|
|
1978
|
+
return expand_(escapeBraces(str), true).map(unescapeBraces);
|
|
1979
|
+
}
|
|
1980
|
+
function embrace(str) {
|
|
1981
|
+
return "{" + str + "}";
|
|
1982
|
+
}
|
|
1983
|
+
function isPadded(el) {
|
|
1984
|
+
return /^-?0\d/.test(el);
|
|
1985
|
+
}
|
|
1986
|
+
function lte(i, y) {
|
|
1987
|
+
return i <= y;
|
|
1988
|
+
}
|
|
1989
|
+
function gte(i, y) {
|
|
1990
|
+
return i >= y;
|
|
1991
|
+
}
|
|
1992
|
+
function expand_(str, isTop) {
|
|
1993
|
+
const expansions = [];
|
|
1994
|
+
const m = balanced("{", "}", str);
|
|
1995
|
+
if (!m)
|
|
1996
|
+
return [str];
|
|
1997
|
+
const pre = m.pre;
|
|
1998
|
+
const post = m.post.length ? expand_(m.post, false) : [""];
|
|
1999
|
+
if (/\$$/.test(m.pre)) {
|
|
2000
|
+
for (let k = 0;k < post.length; k++) {
|
|
2001
|
+
const expansion = pre + "{" + m.body + "}" + post[k];
|
|
2002
|
+
expansions.push(expansion);
|
|
2003
|
+
}
|
|
2004
|
+
} else {
|
|
2005
|
+
const isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
|
|
2006
|
+
const isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
|
|
2007
|
+
const isSequence = isNumericSequence || isAlphaSequence;
|
|
2008
|
+
const isOptions = m.body.indexOf(",") >= 0;
|
|
2009
|
+
if (!isSequence && !isOptions) {
|
|
2010
|
+
if (m.post.match(/,(?!,).*\}/)) {
|
|
2011
|
+
str = m.pre + "{" + m.body + escClose + m.post;
|
|
2012
|
+
return expand_(str);
|
|
2013
|
+
}
|
|
2014
|
+
return [str];
|
|
2015
|
+
}
|
|
2016
|
+
let n;
|
|
2017
|
+
if (isSequence) {
|
|
2018
|
+
n = m.body.split(/\.\./);
|
|
2019
|
+
} else {
|
|
2020
|
+
n = parseCommaParts(m.body);
|
|
2021
|
+
if (n.length === 1 && n[0] !== undefined) {
|
|
2022
|
+
n = expand_(n[0], false).map(embrace);
|
|
2023
|
+
if (n.length === 1) {
|
|
2024
|
+
return post.map((p) => m.pre + n[0] + p);
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
let N;
|
|
2029
|
+
if (isSequence && n[0] !== undefined && n[1] !== undefined) {
|
|
2030
|
+
const x = numeric(n[0]);
|
|
2031
|
+
const y = numeric(n[1]);
|
|
2032
|
+
const width = Math.max(n[0].length, n[1].length);
|
|
2033
|
+
let incr = n.length === 3 && n[2] !== undefined ? Math.abs(numeric(n[2])) : 1;
|
|
2034
|
+
let test = lte;
|
|
2035
|
+
const reverse = y < x;
|
|
2036
|
+
if (reverse) {
|
|
2037
|
+
incr *= -1;
|
|
2038
|
+
test = gte;
|
|
2039
|
+
}
|
|
2040
|
+
const pad = n.some(isPadded);
|
|
2041
|
+
N = [];
|
|
2042
|
+
for (let i = x;test(i, y); i += incr) {
|
|
2043
|
+
let c;
|
|
2044
|
+
if (isAlphaSequence) {
|
|
2045
|
+
c = String.fromCharCode(i);
|
|
2046
|
+
if (c === "\\") {
|
|
2047
|
+
c = "";
|
|
2048
|
+
}
|
|
2049
|
+
} else {
|
|
2050
|
+
c = String(i);
|
|
2051
|
+
if (pad) {
|
|
2052
|
+
const need = width - c.length;
|
|
2053
|
+
if (need > 0) {
|
|
2054
|
+
const z = new Array(need + 1).join("0");
|
|
2055
|
+
if (i < 0) {
|
|
2056
|
+
c = "-" + z + c.slice(1);
|
|
2057
|
+
} else {
|
|
2058
|
+
c = z + c;
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
N.push(c);
|
|
2064
|
+
}
|
|
2065
|
+
} else {
|
|
2066
|
+
N = [];
|
|
2067
|
+
for (let j = 0;j < n.length; j++) {
|
|
2068
|
+
N.push.apply(N, expand_(n[j], false));
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
for (let j = 0;j < N.length; j++) {
|
|
2072
|
+
for (let k = 0;k < post.length; k++) {
|
|
2073
|
+
const expansion = pre + N[j] + post[k];
|
|
2074
|
+
if (!isTop || isSequence || expansion) {
|
|
2075
|
+
expansions.push(expansion);
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
return expansions;
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
// ../../node_modules/minimatch/dist/esm/assert-valid-pattern.js
|
|
2084
|
+
var MAX_PATTERN_LENGTH = 1024 * 64;
|
|
2085
|
+
var assertValidPattern = (pattern) => {
|
|
2086
|
+
if (typeof pattern !== "string") {
|
|
2087
|
+
throw new TypeError("invalid pattern");
|
|
2088
|
+
}
|
|
2089
|
+
if (pattern.length > MAX_PATTERN_LENGTH) {
|
|
2090
|
+
throw new TypeError("pattern is too long");
|
|
2091
|
+
}
|
|
2092
|
+
};
|
|
2093
|
+
|
|
2094
|
+
// ../../node_modules/minimatch/dist/esm/brace-expressions.js
|
|
2095
|
+
var posixClasses = {
|
|
2096
|
+
"[:alnum:]": ["\\p{L}\\p{Nl}\\p{Nd}", true],
|
|
2097
|
+
"[:alpha:]": ["\\p{L}\\p{Nl}", true],
|
|
2098
|
+
"[:ascii:]": ["\\x" + "00-\\x" + "7f", false],
|
|
2099
|
+
"[:blank:]": ["\\p{Zs}\\t", true],
|
|
2100
|
+
"[:cntrl:]": ["\\p{Cc}", true],
|
|
2101
|
+
"[:digit:]": ["\\p{Nd}", true],
|
|
2102
|
+
"[:graph:]": ["\\p{Z}\\p{C}", true, true],
|
|
2103
|
+
"[:lower:]": ["\\p{Ll}", true],
|
|
2104
|
+
"[:print:]": ["\\p{C}", true],
|
|
2105
|
+
"[:punct:]": ["\\p{P}", true],
|
|
2106
|
+
"[:space:]": ["\\p{Z}\\t\\r\\n\\v\\f", true],
|
|
2107
|
+
"[:upper:]": ["\\p{Lu}", true],
|
|
2108
|
+
"[:word:]": ["\\p{L}\\p{Nl}\\p{Nd}\\p{Pc}", true],
|
|
2109
|
+
"[:xdigit:]": ["A-Fa-f0-9", false]
|
|
2110
|
+
};
|
|
2111
|
+
var braceEscape = (s) => s.replace(/[[\]\\-]/g, "\\$&");
|
|
2112
|
+
var regexpEscape = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
2113
|
+
var rangesToString = (ranges) => ranges.join("");
|
|
2114
|
+
var parseClass = (glob2, position) => {
|
|
2115
|
+
const pos = position;
|
|
2116
|
+
if (glob2.charAt(pos) !== "[") {
|
|
2117
|
+
throw new Error("not in a brace expression");
|
|
2118
|
+
}
|
|
2119
|
+
const ranges = [];
|
|
2120
|
+
const negs = [];
|
|
2121
|
+
let i = pos + 1;
|
|
2122
|
+
let sawStart = false;
|
|
2123
|
+
let uflag = false;
|
|
2124
|
+
let escaping = false;
|
|
2125
|
+
let negate = false;
|
|
2126
|
+
let endPos = pos;
|
|
2127
|
+
let rangeStart = "";
|
|
2128
|
+
WHILE:
|
|
2129
|
+
while (i < glob2.length) {
|
|
2130
|
+
const c = glob2.charAt(i);
|
|
2131
|
+
if ((c === "!" || c === "^") && i === pos + 1) {
|
|
2132
|
+
negate = true;
|
|
2133
|
+
i++;
|
|
2134
|
+
continue;
|
|
2135
|
+
}
|
|
2136
|
+
if (c === "]" && sawStart && !escaping) {
|
|
2137
|
+
endPos = i + 1;
|
|
2138
|
+
break;
|
|
2139
|
+
}
|
|
2140
|
+
sawStart = true;
|
|
2141
|
+
if (c === "\\") {
|
|
2142
|
+
if (!escaping) {
|
|
2143
|
+
escaping = true;
|
|
2144
|
+
i++;
|
|
2145
|
+
continue;
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
if (c === "[" && !escaping) {
|
|
2149
|
+
for (const [cls, [unip, u, neg]] of Object.entries(posixClasses)) {
|
|
2150
|
+
if (glob2.startsWith(cls, i)) {
|
|
2151
|
+
if (rangeStart) {
|
|
2152
|
+
return ["$.", false, glob2.length - pos, true];
|
|
2153
|
+
}
|
|
2154
|
+
i += cls.length;
|
|
2155
|
+
if (neg)
|
|
2156
|
+
negs.push(unip);
|
|
2157
|
+
else
|
|
2158
|
+
ranges.push(unip);
|
|
2159
|
+
uflag = uflag || u;
|
|
2160
|
+
continue WHILE;
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
escaping = false;
|
|
2165
|
+
if (rangeStart) {
|
|
2166
|
+
if (c > rangeStart) {
|
|
2167
|
+
ranges.push(braceEscape(rangeStart) + "-" + braceEscape(c));
|
|
2168
|
+
} else if (c === rangeStart) {
|
|
2169
|
+
ranges.push(braceEscape(c));
|
|
2170
|
+
}
|
|
2171
|
+
rangeStart = "";
|
|
2172
|
+
i++;
|
|
2173
|
+
continue;
|
|
2174
|
+
}
|
|
2175
|
+
if (glob2.startsWith("-]", i + 1)) {
|
|
2176
|
+
ranges.push(braceEscape(c + "-"));
|
|
2177
|
+
i += 2;
|
|
2178
|
+
continue;
|
|
2179
|
+
}
|
|
2180
|
+
if (glob2.startsWith("-", i + 1)) {
|
|
2181
|
+
rangeStart = c;
|
|
2182
|
+
i += 2;
|
|
2183
|
+
continue;
|
|
2184
|
+
}
|
|
2185
|
+
ranges.push(braceEscape(c));
|
|
2186
|
+
i++;
|
|
2187
|
+
}
|
|
2188
|
+
if (endPos < i) {
|
|
2189
|
+
return ["", false, 0, false];
|
|
2190
|
+
}
|
|
2191
|
+
if (!ranges.length && !negs.length) {
|
|
2192
|
+
return ["$.", false, glob2.length - pos, true];
|
|
2193
|
+
}
|
|
2194
|
+
if (negs.length === 0 && ranges.length === 1 && /^\\?.$/.test(ranges[0]) && !negate) {
|
|
2195
|
+
const r = ranges[0].length === 2 ? ranges[0].slice(-1) : ranges[0];
|
|
2196
|
+
return [regexpEscape(r), false, endPos - pos, false];
|
|
2197
|
+
}
|
|
2198
|
+
const sranges = "[" + (negate ? "^" : "") + rangesToString(ranges) + "]";
|
|
2199
|
+
const snegs = "[" + (negate ? "" : "^") + rangesToString(negs) + "]";
|
|
2200
|
+
const comb = ranges.length && negs.length ? "(" + sranges + "|" + snegs + ")" : ranges.length ? sranges : snegs;
|
|
2201
|
+
return [comb, uflag, endPos - pos, true];
|
|
2202
|
+
};
|
|
2203
|
+
|
|
2204
|
+
// ../../node_modules/minimatch/dist/esm/unescape.js
|
|
2205
|
+
var unescape = (s, { windowsPathsNoEscape = false, magicalBraces = true } = {}) => {
|
|
2206
|
+
if (magicalBraces) {
|
|
2207
|
+
return windowsPathsNoEscape ? s.replace(/\[([^\/\\])\]/g, "$1") : s.replace(/((?!\\).|^)\[([^\/\\])\]/g, "$1$2").replace(/\\([^\/])/g, "$1");
|
|
2208
|
+
}
|
|
2209
|
+
return windowsPathsNoEscape ? s.replace(/\[([^\/\\{}])\]/g, "$1") : s.replace(/((?!\\).|^)\[([^\/\\{}])\]/g, "$1$2").replace(/\\([^\/{}])/g, "$1");
|
|
2210
|
+
};
|
|
2211
|
+
|
|
2212
|
+
// ../../node_modules/minimatch/dist/esm/ast.js
|
|
2213
|
+
var types = new Set(["!", "?", "+", "*", "@"]);
|
|
2214
|
+
var isExtglobType = (c) => types.has(c);
|
|
2215
|
+
var startNoTraversal = "(?!(?:^|/)\\.\\.?(?:$|/))";
|
|
2216
|
+
var startNoDot = "(?!\\.)";
|
|
2217
|
+
var addPatternStart = new Set(["[", "."]);
|
|
2218
|
+
var justDots = new Set(["..", "."]);
|
|
2219
|
+
var reSpecials = new Set("().*{}+?[]^$\\!");
|
|
2220
|
+
var regExpEscape = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
2221
|
+
var qmark = "[^/]";
|
|
2222
|
+
var star = qmark + "*?";
|
|
2223
|
+
var starNoEmpty = qmark + "+?";
|
|
2224
|
+
|
|
2225
|
+
class AST {
|
|
2226
|
+
type;
|
|
2227
|
+
#root;
|
|
2228
|
+
#hasMagic;
|
|
2229
|
+
#uflag = false;
|
|
2230
|
+
#parts = [];
|
|
2231
|
+
#parent;
|
|
2232
|
+
#parentIndex;
|
|
2233
|
+
#negs;
|
|
2234
|
+
#filledNegs = false;
|
|
2235
|
+
#options;
|
|
2236
|
+
#toString;
|
|
2237
|
+
#emptyExt = false;
|
|
2238
|
+
constructor(type, parent, options = {}) {
|
|
2239
|
+
this.type = type;
|
|
2240
|
+
if (type)
|
|
2241
|
+
this.#hasMagic = true;
|
|
2242
|
+
this.#parent = parent;
|
|
2243
|
+
this.#root = this.#parent ? this.#parent.#root : this;
|
|
2244
|
+
this.#options = this.#root === this ? options : this.#root.#options;
|
|
2245
|
+
this.#negs = this.#root === this ? [] : this.#root.#negs;
|
|
2246
|
+
if (type === "!" && !this.#root.#filledNegs)
|
|
2247
|
+
this.#negs.push(this);
|
|
2248
|
+
this.#parentIndex = this.#parent ? this.#parent.#parts.length : 0;
|
|
2249
|
+
}
|
|
2250
|
+
get hasMagic() {
|
|
2251
|
+
if (this.#hasMagic !== undefined)
|
|
2252
|
+
return this.#hasMagic;
|
|
2253
|
+
for (const p of this.#parts) {
|
|
2254
|
+
if (typeof p === "string")
|
|
2255
|
+
continue;
|
|
2256
|
+
if (p.type || p.hasMagic)
|
|
2257
|
+
return this.#hasMagic = true;
|
|
2258
|
+
}
|
|
2259
|
+
return this.#hasMagic;
|
|
2260
|
+
}
|
|
2261
|
+
toString() {
|
|
2262
|
+
if (this.#toString !== undefined)
|
|
2263
|
+
return this.#toString;
|
|
2264
|
+
if (!this.type) {
|
|
2265
|
+
return this.#toString = this.#parts.map((p) => String(p)).join("");
|
|
2266
|
+
} else {
|
|
2267
|
+
return this.#toString = this.type + "(" + this.#parts.map((p) => String(p)).join("|") + ")";
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
#fillNegs() {
|
|
2271
|
+
if (this !== this.#root)
|
|
2272
|
+
throw new Error("should only call on root");
|
|
2273
|
+
if (this.#filledNegs)
|
|
2274
|
+
return this;
|
|
2275
|
+
this.toString();
|
|
2276
|
+
this.#filledNegs = true;
|
|
2277
|
+
let n;
|
|
2278
|
+
while (n = this.#negs.pop()) {
|
|
2279
|
+
if (n.type !== "!")
|
|
2280
|
+
continue;
|
|
2281
|
+
let p = n;
|
|
2282
|
+
let pp = p.#parent;
|
|
2283
|
+
while (pp) {
|
|
2284
|
+
for (let i = p.#parentIndex + 1;!pp.type && i < pp.#parts.length; i++) {
|
|
2285
|
+
for (const part of n.#parts) {
|
|
2286
|
+
if (typeof part === "string") {
|
|
2287
|
+
throw new Error("string part in extglob AST??");
|
|
2288
|
+
}
|
|
2289
|
+
part.copyIn(pp.#parts[i]);
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
p = pp;
|
|
2293
|
+
pp = p.#parent;
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
return this;
|
|
2297
|
+
}
|
|
2298
|
+
push(...parts) {
|
|
2299
|
+
for (const p of parts) {
|
|
2300
|
+
if (p === "")
|
|
2301
|
+
continue;
|
|
2302
|
+
if (typeof p !== "string" && !(p instanceof AST && p.#parent === this)) {
|
|
2303
|
+
throw new Error("invalid part: " + p);
|
|
2304
|
+
}
|
|
2305
|
+
this.#parts.push(p);
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
toJSON() {
|
|
2309
|
+
const ret = this.type === null ? this.#parts.slice().map((p) => typeof p === "string" ? p : p.toJSON()) : [this.type, ...this.#parts.map((p) => p.toJSON())];
|
|
2310
|
+
if (this.isStart() && !this.type)
|
|
2311
|
+
ret.unshift([]);
|
|
2312
|
+
if (this.isEnd() && (this === this.#root || this.#root.#filledNegs && this.#parent?.type === "!")) {
|
|
2313
|
+
ret.push({});
|
|
2314
|
+
}
|
|
2315
|
+
return ret;
|
|
2316
|
+
}
|
|
2317
|
+
isStart() {
|
|
2318
|
+
if (this.#root === this)
|
|
2319
|
+
return true;
|
|
2320
|
+
if (!this.#parent?.isStart())
|
|
2321
|
+
return false;
|
|
2322
|
+
if (this.#parentIndex === 0)
|
|
2323
|
+
return true;
|
|
2324
|
+
const p = this.#parent;
|
|
2325
|
+
for (let i = 0;i < this.#parentIndex; i++) {
|
|
2326
|
+
const pp = p.#parts[i];
|
|
2327
|
+
if (!(pp instanceof AST && pp.type === "!")) {
|
|
2328
|
+
return false;
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
return true;
|
|
2332
|
+
}
|
|
2333
|
+
isEnd() {
|
|
2334
|
+
if (this.#root === this)
|
|
2335
|
+
return true;
|
|
2336
|
+
if (this.#parent?.type === "!")
|
|
2337
|
+
return true;
|
|
2338
|
+
if (!this.#parent?.isEnd())
|
|
2339
|
+
return false;
|
|
2340
|
+
if (!this.type)
|
|
2341
|
+
return this.#parent?.isEnd();
|
|
2342
|
+
const pl = this.#parent ? this.#parent.#parts.length : 0;
|
|
2343
|
+
return this.#parentIndex === pl - 1;
|
|
2344
|
+
}
|
|
2345
|
+
copyIn(part) {
|
|
2346
|
+
if (typeof part === "string")
|
|
2347
|
+
this.push(part);
|
|
2348
|
+
else
|
|
2349
|
+
this.push(part.clone(this));
|
|
2350
|
+
}
|
|
2351
|
+
clone(parent) {
|
|
2352
|
+
const c = new AST(this.type, parent);
|
|
2353
|
+
for (const p of this.#parts) {
|
|
2354
|
+
c.copyIn(p);
|
|
2355
|
+
}
|
|
2356
|
+
return c;
|
|
2357
|
+
}
|
|
2358
|
+
static #parseAST(str, ast, pos, opt) {
|
|
2359
|
+
let escaping = false;
|
|
2360
|
+
let inBrace = false;
|
|
2361
|
+
let braceStart = -1;
|
|
2362
|
+
let braceNeg = false;
|
|
2363
|
+
if (ast.type === null) {
|
|
2364
|
+
let i2 = pos;
|
|
2365
|
+
let acc2 = "";
|
|
2366
|
+
while (i2 < str.length) {
|
|
2367
|
+
const c = str.charAt(i2++);
|
|
2368
|
+
if (escaping || c === "\\") {
|
|
2369
|
+
escaping = !escaping;
|
|
2370
|
+
acc2 += c;
|
|
2371
|
+
continue;
|
|
2372
|
+
}
|
|
2373
|
+
if (inBrace) {
|
|
2374
|
+
if (i2 === braceStart + 1) {
|
|
2375
|
+
if (c === "^" || c === "!") {
|
|
2376
|
+
braceNeg = true;
|
|
2377
|
+
}
|
|
2378
|
+
} else if (c === "]" && !(i2 === braceStart + 2 && braceNeg)) {
|
|
2379
|
+
inBrace = false;
|
|
2380
|
+
}
|
|
2381
|
+
acc2 += c;
|
|
2382
|
+
continue;
|
|
2383
|
+
} else if (c === "[") {
|
|
2384
|
+
inBrace = true;
|
|
2385
|
+
braceStart = i2;
|
|
2386
|
+
braceNeg = false;
|
|
2387
|
+
acc2 += c;
|
|
2388
|
+
continue;
|
|
2389
|
+
}
|
|
2390
|
+
if (!opt.noext && isExtglobType(c) && str.charAt(i2) === "(") {
|
|
2391
|
+
ast.push(acc2);
|
|
2392
|
+
acc2 = "";
|
|
2393
|
+
const ext = new AST(c, ast);
|
|
2394
|
+
i2 = AST.#parseAST(str, ext, i2, opt);
|
|
2395
|
+
ast.push(ext);
|
|
2396
|
+
continue;
|
|
2397
|
+
}
|
|
2398
|
+
acc2 += c;
|
|
2399
|
+
}
|
|
2400
|
+
ast.push(acc2);
|
|
2401
|
+
return i2;
|
|
2402
|
+
}
|
|
2403
|
+
let i = pos + 1;
|
|
2404
|
+
let part = new AST(null, ast);
|
|
2405
|
+
const parts = [];
|
|
2406
|
+
let acc = "";
|
|
2407
|
+
while (i < str.length) {
|
|
2408
|
+
const c = str.charAt(i++);
|
|
2409
|
+
if (escaping || c === "\\") {
|
|
2410
|
+
escaping = !escaping;
|
|
2411
|
+
acc += c;
|
|
2412
|
+
continue;
|
|
2413
|
+
}
|
|
2414
|
+
if (inBrace) {
|
|
2415
|
+
if (i === braceStart + 1) {
|
|
2416
|
+
if (c === "^" || c === "!") {
|
|
2417
|
+
braceNeg = true;
|
|
2418
|
+
}
|
|
2419
|
+
} else if (c === "]" && !(i === braceStart + 2 && braceNeg)) {
|
|
2420
|
+
inBrace = false;
|
|
2421
|
+
}
|
|
2422
|
+
acc += c;
|
|
2423
|
+
continue;
|
|
2424
|
+
} else if (c === "[") {
|
|
2425
|
+
inBrace = true;
|
|
2426
|
+
braceStart = i;
|
|
2427
|
+
braceNeg = false;
|
|
2428
|
+
acc += c;
|
|
2429
|
+
continue;
|
|
2430
|
+
}
|
|
2431
|
+
if (isExtglobType(c) && str.charAt(i) === "(") {
|
|
2432
|
+
part.push(acc);
|
|
2433
|
+
acc = "";
|
|
2434
|
+
const ext = new AST(c, part);
|
|
2435
|
+
part.push(ext);
|
|
2436
|
+
i = AST.#parseAST(str, ext, i, opt);
|
|
2437
|
+
continue;
|
|
2438
|
+
}
|
|
2439
|
+
if (c === "|") {
|
|
2440
|
+
part.push(acc);
|
|
2441
|
+
acc = "";
|
|
2442
|
+
parts.push(part);
|
|
2443
|
+
part = new AST(null, ast);
|
|
2444
|
+
continue;
|
|
2445
|
+
}
|
|
2446
|
+
if (c === ")") {
|
|
2447
|
+
if (acc === "" && ast.#parts.length === 0) {
|
|
2448
|
+
ast.#emptyExt = true;
|
|
2449
|
+
}
|
|
2450
|
+
part.push(acc);
|
|
2451
|
+
acc = "";
|
|
2452
|
+
ast.push(...parts, part);
|
|
2453
|
+
return i;
|
|
2454
|
+
}
|
|
2455
|
+
acc += c;
|
|
2456
|
+
}
|
|
2457
|
+
ast.type = null;
|
|
2458
|
+
ast.#hasMagic = undefined;
|
|
2459
|
+
ast.#parts = [str.substring(pos - 1)];
|
|
2460
|
+
return i;
|
|
2461
|
+
}
|
|
2462
|
+
static fromGlob(pattern, options = {}) {
|
|
2463
|
+
const ast = new AST(null, undefined, options);
|
|
2464
|
+
AST.#parseAST(pattern, ast, 0, options);
|
|
2465
|
+
return ast;
|
|
2466
|
+
}
|
|
2467
|
+
toMMPattern() {
|
|
2468
|
+
if (this !== this.#root)
|
|
2469
|
+
return this.#root.toMMPattern();
|
|
2470
|
+
const glob2 = this.toString();
|
|
2471
|
+
const [re, body, hasMagic, uflag] = this.toRegExpSource();
|
|
2472
|
+
const anyMagic = hasMagic || this.#hasMagic || this.#options.nocase && !this.#options.nocaseMagicOnly && glob2.toUpperCase() !== glob2.toLowerCase();
|
|
2473
|
+
if (!anyMagic) {
|
|
2474
|
+
return body;
|
|
2475
|
+
}
|
|
2476
|
+
const flags = (this.#options.nocase ? "i" : "") + (uflag ? "u" : "");
|
|
2477
|
+
return Object.assign(new RegExp(`^${re}$`, flags), {
|
|
2478
|
+
_src: re,
|
|
2479
|
+
_glob: glob2
|
|
2480
|
+
});
|
|
2481
|
+
}
|
|
2482
|
+
get options() {
|
|
2483
|
+
return this.#options;
|
|
2484
|
+
}
|
|
2485
|
+
toRegExpSource(allowDot) {
|
|
2486
|
+
const dot = allowDot ?? !!this.#options.dot;
|
|
2487
|
+
if (this.#root === this)
|
|
2488
|
+
this.#fillNegs();
|
|
2489
|
+
if (!this.type) {
|
|
2490
|
+
const noEmpty = this.isStart() && this.isEnd() && !this.#parts.some((s) => typeof s !== "string");
|
|
2491
|
+
const src = this.#parts.map((p) => {
|
|
2492
|
+
const [re, _, hasMagic, uflag] = typeof p === "string" ? AST.#parseGlob(p, this.#hasMagic, noEmpty) : p.toRegExpSource(allowDot);
|
|
2493
|
+
this.#hasMagic = this.#hasMagic || hasMagic;
|
|
2494
|
+
this.#uflag = this.#uflag || uflag;
|
|
2495
|
+
return re;
|
|
2496
|
+
}).join("");
|
|
2497
|
+
let start2 = "";
|
|
2498
|
+
if (this.isStart()) {
|
|
2499
|
+
if (typeof this.#parts[0] === "string") {
|
|
2500
|
+
const dotTravAllowed = this.#parts.length === 1 && justDots.has(this.#parts[0]);
|
|
2501
|
+
if (!dotTravAllowed) {
|
|
2502
|
+
const aps = addPatternStart;
|
|
2503
|
+
const needNoTrav = dot && aps.has(src.charAt(0)) || src.startsWith("\\.") && aps.has(src.charAt(2)) || src.startsWith("\\.\\.") && aps.has(src.charAt(4));
|
|
2504
|
+
const needNoDot = !dot && !allowDot && aps.has(src.charAt(0));
|
|
2505
|
+
start2 = needNoTrav ? startNoTraversal : needNoDot ? startNoDot : "";
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
let end = "";
|
|
2510
|
+
if (this.isEnd() && this.#root.#filledNegs && this.#parent?.type === "!") {
|
|
2511
|
+
end = "(?:$|\\/)";
|
|
2512
|
+
}
|
|
2513
|
+
const final2 = start2 + src + end;
|
|
2514
|
+
return [
|
|
2515
|
+
final2,
|
|
2516
|
+
unescape(src),
|
|
2517
|
+
this.#hasMagic = !!this.#hasMagic,
|
|
2518
|
+
this.#uflag
|
|
2519
|
+
];
|
|
2520
|
+
}
|
|
2521
|
+
const repeated = this.type === "*" || this.type === "+";
|
|
2522
|
+
const start = this.type === "!" ? "(?:(?!(?:" : "(?:";
|
|
2523
|
+
let body = this.#partsToRegExp(dot);
|
|
2524
|
+
if (this.isStart() && this.isEnd() && !body && this.type !== "!") {
|
|
2525
|
+
const s = this.toString();
|
|
2526
|
+
this.#parts = [s];
|
|
2527
|
+
this.type = null;
|
|
2528
|
+
this.#hasMagic = undefined;
|
|
2529
|
+
return [s, unescape(this.toString()), false, false];
|
|
2530
|
+
}
|
|
2531
|
+
let bodyDotAllowed = !repeated || allowDot || dot || !startNoDot ? "" : this.#partsToRegExp(true);
|
|
2532
|
+
if (bodyDotAllowed === body) {
|
|
2533
|
+
bodyDotAllowed = "";
|
|
2534
|
+
}
|
|
2535
|
+
if (bodyDotAllowed) {
|
|
2536
|
+
body = `(?:${body})(?:${bodyDotAllowed})*?`;
|
|
2537
|
+
}
|
|
2538
|
+
let final = "";
|
|
2539
|
+
if (this.type === "!" && this.#emptyExt) {
|
|
2540
|
+
final = (this.isStart() && !dot ? startNoDot : "") + starNoEmpty;
|
|
2541
|
+
} else {
|
|
2542
|
+
const close = this.type === "!" ? "))" + (this.isStart() && !dot && !allowDot ? startNoDot : "") + star + ")" : this.type === "@" ? ")" : this.type === "?" ? ")?" : this.type === "+" && bodyDotAllowed ? ")" : this.type === "*" && bodyDotAllowed ? `)?` : `)${this.type}`;
|
|
2543
|
+
final = start + body + close;
|
|
2544
|
+
}
|
|
2545
|
+
return [
|
|
2546
|
+
final,
|
|
2547
|
+
unescape(body),
|
|
2548
|
+
this.#hasMagic = !!this.#hasMagic,
|
|
2549
|
+
this.#uflag
|
|
2550
|
+
];
|
|
2551
|
+
}
|
|
2552
|
+
#partsToRegExp(dot) {
|
|
2553
|
+
return this.#parts.map((p) => {
|
|
2554
|
+
if (typeof p === "string") {
|
|
2555
|
+
throw new Error("string type in extglob ast??");
|
|
2556
|
+
}
|
|
2557
|
+
const [re, _, _hasMagic, uflag] = p.toRegExpSource(dot);
|
|
2558
|
+
this.#uflag = this.#uflag || uflag;
|
|
2559
|
+
return re;
|
|
2560
|
+
}).filter((p) => !(this.isStart() && this.isEnd()) || !!p).join("|");
|
|
2561
|
+
}
|
|
2562
|
+
static #parseGlob(glob2, hasMagic, noEmpty = false) {
|
|
2563
|
+
let escaping = false;
|
|
2564
|
+
let re = "";
|
|
2565
|
+
let uflag = false;
|
|
2566
|
+
for (let i = 0;i < glob2.length; i++) {
|
|
2567
|
+
const c = glob2.charAt(i);
|
|
2568
|
+
if (escaping) {
|
|
2569
|
+
escaping = false;
|
|
2570
|
+
re += (reSpecials.has(c) ? "\\" : "") + c;
|
|
2571
|
+
continue;
|
|
2572
|
+
}
|
|
2573
|
+
if (c === "\\") {
|
|
2574
|
+
if (i === glob2.length - 1) {
|
|
2575
|
+
re += "\\\\";
|
|
2576
|
+
} else {
|
|
2577
|
+
escaping = true;
|
|
2578
|
+
}
|
|
2579
|
+
continue;
|
|
2580
|
+
}
|
|
2581
|
+
if (c === "[") {
|
|
2582
|
+
const [src, needUflag, consumed, magic] = parseClass(glob2, i);
|
|
2583
|
+
if (consumed) {
|
|
2584
|
+
re += src;
|
|
2585
|
+
uflag = uflag || needUflag;
|
|
2586
|
+
i += consumed - 1;
|
|
2587
|
+
hasMagic = hasMagic || magic;
|
|
2588
|
+
continue;
|
|
2589
|
+
}
|
|
2590
|
+
}
|
|
2591
|
+
if (c === "*") {
|
|
2592
|
+
re += noEmpty && glob2 === "*" ? starNoEmpty : star;
|
|
2593
|
+
hasMagic = true;
|
|
2594
|
+
continue;
|
|
2595
|
+
}
|
|
2596
|
+
if (c === "?") {
|
|
2597
|
+
re += qmark;
|
|
2598
|
+
hasMagic = true;
|
|
2599
|
+
continue;
|
|
2600
|
+
}
|
|
2601
|
+
re += regExpEscape(c);
|
|
2602
|
+
}
|
|
2603
|
+
return [re, unescape(glob2), !!hasMagic, uflag];
|
|
2604
|
+
}
|
|
2605
|
+
}
|
|
2606
|
+
|
|
2607
|
+
// ../../node_modules/minimatch/dist/esm/escape.js
|
|
2608
|
+
var escape = (s, { windowsPathsNoEscape = false, magicalBraces = false } = {}) => {
|
|
2609
|
+
if (magicalBraces) {
|
|
2610
|
+
return windowsPathsNoEscape ? s.replace(/[?*()[\]{}]/g, "[$&]") : s.replace(/[?*()[\]\\{}]/g, "\\$&");
|
|
2611
|
+
}
|
|
2612
|
+
return windowsPathsNoEscape ? s.replace(/[?*()[\]]/g, "[$&]") : s.replace(/[?*()[\]\\]/g, "\\$&");
|
|
2613
|
+
};
|
|
2614
|
+
|
|
2615
|
+
// ../../node_modules/minimatch/dist/esm/index.js
|
|
2616
|
+
var minimatch = (p, pattern, options = {}) => {
|
|
2617
|
+
assertValidPattern(pattern);
|
|
2618
|
+
if (!options.nocomment && pattern.charAt(0) === "#") {
|
|
2619
|
+
return false;
|
|
2620
|
+
}
|
|
2621
|
+
return new Minimatch(pattern, options).match(p);
|
|
2622
|
+
};
|
|
2623
|
+
var starDotExtRE = /^\*+([^+@!?\*\[\(]*)$/;
|
|
2624
|
+
var starDotExtTest = (ext) => (f) => !f.startsWith(".") && f.endsWith(ext);
|
|
2625
|
+
var starDotExtTestDot = (ext) => (f) => f.endsWith(ext);
|
|
2626
|
+
var starDotExtTestNocase = (ext) => {
|
|
2627
|
+
ext = ext.toLowerCase();
|
|
2628
|
+
return (f) => !f.startsWith(".") && f.toLowerCase().endsWith(ext);
|
|
2629
|
+
};
|
|
2630
|
+
var starDotExtTestNocaseDot = (ext) => {
|
|
2631
|
+
ext = ext.toLowerCase();
|
|
2632
|
+
return (f) => f.toLowerCase().endsWith(ext);
|
|
2633
|
+
};
|
|
2634
|
+
var starDotStarRE = /^\*+\.\*+$/;
|
|
2635
|
+
var starDotStarTest = (f) => !f.startsWith(".") && f.includes(".");
|
|
2636
|
+
var starDotStarTestDot = (f) => f !== "." && f !== ".." && f.includes(".");
|
|
2637
|
+
var dotStarRE = /^\.\*+$/;
|
|
2638
|
+
var dotStarTest = (f) => f !== "." && f !== ".." && f.startsWith(".");
|
|
2639
|
+
var starRE = /^\*+$/;
|
|
2640
|
+
var starTest = (f) => f.length !== 0 && !f.startsWith(".");
|
|
2641
|
+
var starTestDot = (f) => f.length !== 0 && f !== "." && f !== "..";
|
|
2642
|
+
var qmarksRE = /^\?+([^+@!?\*\[\(]*)?$/;
|
|
2643
|
+
var qmarksTestNocase = ([$0, ext = ""]) => {
|
|
2644
|
+
const noext = qmarksTestNoExt([$0]);
|
|
2645
|
+
if (!ext)
|
|
2646
|
+
return noext;
|
|
2647
|
+
ext = ext.toLowerCase();
|
|
2648
|
+
return (f) => noext(f) && f.toLowerCase().endsWith(ext);
|
|
2649
|
+
};
|
|
2650
|
+
var qmarksTestNocaseDot = ([$0, ext = ""]) => {
|
|
2651
|
+
const noext = qmarksTestNoExtDot([$0]);
|
|
2652
|
+
if (!ext)
|
|
2653
|
+
return noext;
|
|
2654
|
+
ext = ext.toLowerCase();
|
|
2655
|
+
return (f) => noext(f) && f.toLowerCase().endsWith(ext);
|
|
2656
|
+
};
|
|
2657
|
+
var qmarksTestDot = ([$0, ext = ""]) => {
|
|
2658
|
+
const noext = qmarksTestNoExtDot([$0]);
|
|
2659
|
+
return !ext ? noext : (f) => noext(f) && f.endsWith(ext);
|
|
2660
|
+
};
|
|
2661
|
+
var qmarksTest = ([$0, ext = ""]) => {
|
|
2662
|
+
const noext = qmarksTestNoExt([$0]);
|
|
2663
|
+
return !ext ? noext : (f) => noext(f) && f.endsWith(ext);
|
|
2664
|
+
};
|
|
2665
|
+
var qmarksTestNoExt = ([$0]) => {
|
|
2666
|
+
const len = $0.length;
|
|
2667
|
+
return (f) => f.length === len && !f.startsWith(".");
|
|
2668
|
+
};
|
|
2669
|
+
var qmarksTestNoExtDot = ([$0]) => {
|
|
2670
|
+
const len = $0.length;
|
|
2671
|
+
return (f) => f.length === len && f !== "." && f !== "..";
|
|
2672
|
+
};
|
|
2673
|
+
var defaultPlatform = typeof process === "object" && process ? typeof process.env === "object" && process.env && process.env.__MINIMATCH_TESTING_PLATFORM__ || process.platform : "posix";
|
|
2674
|
+
var path7 = {
|
|
2675
|
+
win32: { sep: "\\" },
|
|
2676
|
+
posix: { sep: "/" }
|
|
2677
|
+
};
|
|
2678
|
+
var sep = defaultPlatform === "win32" ? path7.win32.sep : path7.posix.sep;
|
|
2679
|
+
minimatch.sep = sep;
|
|
2680
|
+
var GLOBSTAR = Symbol("globstar **");
|
|
2681
|
+
minimatch.GLOBSTAR = GLOBSTAR;
|
|
2682
|
+
var qmark2 = "[^/]";
|
|
2683
|
+
var star2 = qmark2 + "*?";
|
|
2684
|
+
var twoStarDot = "(?:(?!(?:\\/|^)(?:\\.{1,2})($|\\/)).)*?";
|
|
2685
|
+
var twoStarNoDot = "(?:(?!(?:\\/|^)\\.).)*?";
|
|
2686
|
+
var filter = (pattern, options = {}) => (p) => minimatch(p, pattern, options);
|
|
2687
|
+
minimatch.filter = filter;
|
|
2688
|
+
var ext = (a, b = {}) => Object.assign({}, a, b);
|
|
2689
|
+
var defaults = (def) => {
|
|
2690
|
+
if (!def || typeof def !== "object" || !Object.keys(def).length) {
|
|
2691
|
+
return minimatch;
|
|
2692
|
+
}
|
|
2693
|
+
const orig = minimatch;
|
|
2694
|
+
const m = (p, pattern, options = {}) => orig(p, pattern, ext(def, options));
|
|
2695
|
+
return Object.assign(m, {
|
|
2696
|
+
Minimatch: class Minimatch extends orig.Minimatch {
|
|
2697
|
+
constructor(pattern, options = {}) {
|
|
2698
|
+
super(pattern, ext(def, options));
|
|
2699
|
+
}
|
|
2700
|
+
static defaults(options) {
|
|
2701
|
+
return orig.defaults(ext(def, options)).Minimatch;
|
|
2702
|
+
}
|
|
2703
|
+
},
|
|
2704
|
+
AST: class AST2 extends orig.AST {
|
|
2705
|
+
constructor(type, parent, options = {}) {
|
|
2706
|
+
super(type, parent, ext(def, options));
|
|
2707
|
+
}
|
|
2708
|
+
static fromGlob(pattern, options = {}) {
|
|
2709
|
+
return orig.AST.fromGlob(pattern, ext(def, options));
|
|
2710
|
+
}
|
|
2711
|
+
},
|
|
2712
|
+
unescape: (s, options = {}) => orig.unescape(s, ext(def, options)),
|
|
2713
|
+
escape: (s, options = {}) => orig.escape(s, ext(def, options)),
|
|
2714
|
+
filter: (pattern, options = {}) => orig.filter(pattern, ext(def, options)),
|
|
2715
|
+
defaults: (options) => orig.defaults(ext(def, options)),
|
|
2716
|
+
makeRe: (pattern, options = {}) => orig.makeRe(pattern, ext(def, options)),
|
|
2717
|
+
braceExpand: (pattern, options = {}) => orig.braceExpand(pattern, ext(def, options)),
|
|
2718
|
+
match: (list, pattern, options = {}) => orig.match(list, pattern, ext(def, options)),
|
|
2719
|
+
sep: orig.sep,
|
|
2720
|
+
GLOBSTAR
|
|
2721
|
+
});
|
|
2722
|
+
};
|
|
2723
|
+
minimatch.defaults = defaults;
|
|
2724
|
+
var braceExpand = (pattern, options = {}) => {
|
|
2725
|
+
assertValidPattern(pattern);
|
|
2726
|
+
if (options.nobrace || !/\{(?:(?!\{).)*\}/.test(pattern)) {
|
|
2727
|
+
return [pattern];
|
|
2728
|
+
}
|
|
2729
|
+
return expand(pattern);
|
|
2730
|
+
};
|
|
2731
|
+
minimatch.braceExpand = braceExpand;
|
|
2732
|
+
var makeRe = (pattern, options = {}) => new Minimatch(pattern, options).makeRe();
|
|
2733
|
+
minimatch.makeRe = makeRe;
|
|
2734
|
+
var match = (list, pattern, options = {}) => {
|
|
2735
|
+
const mm = new Minimatch(pattern, options);
|
|
2736
|
+
list = list.filter((f) => mm.match(f));
|
|
2737
|
+
if (mm.options.nonull && !list.length) {
|
|
2738
|
+
list.push(pattern);
|
|
2739
|
+
}
|
|
2740
|
+
return list;
|
|
2741
|
+
};
|
|
2742
|
+
minimatch.match = match;
|
|
2743
|
+
var globMagic = /[?*]|[+@!]\(.*?\)|\[|\]/;
|
|
2744
|
+
var regExpEscape2 = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
2745
|
+
|
|
2746
|
+
class Minimatch {
|
|
2747
|
+
options;
|
|
2748
|
+
set;
|
|
2749
|
+
pattern;
|
|
2750
|
+
windowsPathsNoEscape;
|
|
2751
|
+
nonegate;
|
|
2752
|
+
negate;
|
|
2753
|
+
comment;
|
|
2754
|
+
empty;
|
|
2755
|
+
preserveMultipleSlashes;
|
|
2756
|
+
partial;
|
|
2757
|
+
globSet;
|
|
2758
|
+
globParts;
|
|
2759
|
+
nocase;
|
|
2760
|
+
isWindows;
|
|
2761
|
+
platform;
|
|
2762
|
+
windowsNoMagicRoot;
|
|
2763
|
+
regexp;
|
|
2764
|
+
constructor(pattern, options = {}) {
|
|
2765
|
+
assertValidPattern(pattern);
|
|
2766
|
+
options = options || {};
|
|
2767
|
+
this.options = options;
|
|
2768
|
+
this.pattern = pattern;
|
|
2769
|
+
this.platform = options.platform || defaultPlatform;
|
|
2770
|
+
this.isWindows = this.platform === "win32";
|
|
2771
|
+
this.windowsPathsNoEscape = !!options.windowsPathsNoEscape || options.allowWindowsEscape === false;
|
|
2772
|
+
if (this.windowsPathsNoEscape) {
|
|
2773
|
+
this.pattern = this.pattern.replace(/\\/g, "/");
|
|
2774
|
+
}
|
|
2775
|
+
this.preserveMultipleSlashes = !!options.preserveMultipleSlashes;
|
|
2776
|
+
this.regexp = null;
|
|
2777
|
+
this.negate = false;
|
|
2778
|
+
this.nonegate = !!options.nonegate;
|
|
2779
|
+
this.comment = false;
|
|
2780
|
+
this.empty = false;
|
|
2781
|
+
this.partial = !!options.partial;
|
|
2782
|
+
this.nocase = !!this.options.nocase;
|
|
2783
|
+
this.windowsNoMagicRoot = options.windowsNoMagicRoot !== undefined ? options.windowsNoMagicRoot : !!(this.isWindows && this.nocase);
|
|
2784
|
+
this.globSet = [];
|
|
2785
|
+
this.globParts = [];
|
|
2786
|
+
this.set = [];
|
|
2787
|
+
this.make();
|
|
2788
|
+
}
|
|
2789
|
+
hasMagic() {
|
|
2790
|
+
if (this.options.magicalBraces && this.set.length > 1) {
|
|
2791
|
+
return true;
|
|
2792
|
+
}
|
|
2793
|
+
for (const pattern of this.set) {
|
|
2794
|
+
for (const part of pattern) {
|
|
2795
|
+
if (typeof part !== "string")
|
|
2796
|
+
return true;
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
return false;
|
|
2800
|
+
}
|
|
2801
|
+
debug(..._) {}
|
|
2802
|
+
make() {
|
|
2803
|
+
const pattern = this.pattern;
|
|
2804
|
+
const options = this.options;
|
|
2805
|
+
if (!options.nocomment && pattern.charAt(0) === "#") {
|
|
2806
|
+
this.comment = true;
|
|
2807
|
+
return;
|
|
2808
|
+
}
|
|
2809
|
+
if (!pattern) {
|
|
2810
|
+
this.empty = true;
|
|
2811
|
+
return;
|
|
2812
|
+
}
|
|
2813
|
+
this.parseNegate();
|
|
2814
|
+
this.globSet = [...new Set(this.braceExpand())];
|
|
2815
|
+
if (options.debug) {
|
|
2816
|
+
this.debug = (...args) => console.error(...args);
|
|
2817
|
+
}
|
|
2818
|
+
this.debug(this.pattern, this.globSet);
|
|
2819
|
+
const rawGlobParts = this.globSet.map((s) => this.slashSplit(s));
|
|
2820
|
+
this.globParts = this.preprocess(rawGlobParts);
|
|
2821
|
+
this.debug(this.pattern, this.globParts);
|
|
2822
|
+
let set = this.globParts.map((s, _, __) => {
|
|
2823
|
+
if (this.isWindows && this.windowsNoMagicRoot) {
|
|
2824
|
+
const isUNC = s[0] === "" && s[1] === "" && (s[2] === "?" || !globMagic.test(s[2])) && !globMagic.test(s[3]);
|
|
2825
|
+
const isDrive = /^[a-z]:/i.test(s[0]);
|
|
2826
|
+
if (isUNC) {
|
|
2827
|
+
return [...s.slice(0, 4), ...s.slice(4).map((ss) => this.parse(ss))];
|
|
2828
|
+
} else if (isDrive) {
|
|
2829
|
+
return [s[0], ...s.slice(1).map((ss) => this.parse(ss))];
|
|
2830
|
+
}
|
|
2831
|
+
}
|
|
2832
|
+
return s.map((ss) => this.parse(ss));
|
|
2833
|
+
});
|
|
2834
|
+
this.debug(this.pattern, set);
|
|
2835
|
+
this.set = set.filter((s) => s.indexOf(false) === -1);
|
|
2836
|
+
if (this.isWindows) {
|
|
2837
|
+
for (let i = 0;i < this.set.length; i++) {
|
|
2838
|
+
const p = this.set[i];
|
|
2839
|
+
if (p[0] === "" && p[1] === "" && this.globParts[i][2] === "?" && typeof p[3] === "string" && /^[a-z]:$/i.test(p[3])) {
|
|
2840
|
+
p[2] = "?";
|
|
2841
|
+
}
|
|
2842
|
+
}
|
|
2843
|
+
}
|
|
2844
|
+
this.debug(this.pattern, this.set);
|
|
2845
|
+
}
|
|
2846
|
+
preprocess(globParts) {
|
|
2847
|
+
if (this.options.noglobstar) {
|
|
2848
|
+
for (let i = 0;i < globParts.length; i++) {
|
|
2849
|
+
for (let j = 0;j < globParts[i].length; j++) {
|
|
2850
|
+
if (globParts[i][j] === "**") {
|
|
2851
|
+
globParts[i][j] = "*";
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2856
|
+
const { optimizationLevel = 1 } = this.options;
|
|
2857
|
+
if (optimizationLevel >= 2) {
|
|
2858
|
+
globParts = this.firstPhasePreProcess(globParts);
|
|
2859
|
+
globParts = this.secondPhasePreProcess(globParts);
|
|
2860
|
+
} else if (optimizationLevel >= 1) {
|
|
2861
|
+
globParts = this.levelOneOptimize(globParts);
|
|
2862
|
+
} else {
|
|
2863
|
+
globParts = this.adjascentGlobstarOptimize(globParts);
|
|
2864
|
+
}
|
|
2865
|
+
return globParts;
|
|
2866
|
+
}
|
|
2867
|
+
adjascentGlobstarOptimize(globParts) {
|
|
2868
|
+
return globParts.map((parts) => {
|
|
2869
|
+
let gs = -1;
|
|
2870
|
+
while ((gs = parts.indexOf("**", gs + 1)) !== -1) {
|
|
2871
|
+
let i = gs;
|
|
2872
|
+
while (parts[i + 1] === "**") {
|
|
2873
|
+
i++;
|
|
2874
|
+
}
|
|
2875
|
+
if (i !== gs) {
|
|
2876
|
+
parts.splice(gs, i - gs);
|
|
2877
|
+
}
|
|
2878
|
+
}
|
|
2879
|
+
return parts;
|
|
2880
|
+
});
|
|
2881
|
+
}
|
|
2882
|
+
levelOneOptimize(globParts) {
|
|
2883
|
+
return globParts.map((parts) => {
|
|
2884
|
+
parts = parts.reduce((set, part) => {
|
|
2885
|
+
const prev = set[set.length - 1];
|
|
2886
|
+
if (part === "**" && prev === "**") {
|
|
2887
|
+
return set;
|
|
2888
|
+
}
|
|
2889
|
+
if (part === "..") {
|
|
2890
|
+
if (prev && prev !== ".." && prev !== "." && prev !== "**") {
|
|
2891
|
+
set.pop();
|
|
2892
|
+
return set;
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2895
|
+
set.push(part);
|
|
2896
|
+
return set;
|
|
2897
|
+
}, []);
|
|
2898
|
+
return parts.length === 0 ? [""] : parts;
|
|
2899
|
+
});
|
|
2900
|
+
}
|
|
2901
|
+
levelTwoFileOptimize(parts) {
|
|
2902
|
+
if (!Array.isArray(parts)) {
|
|
2903
|
+
parts = this.slashSplit(parts);
|
|
2904
|
+
}
|
|
2905
|
+
let didSomething = false;
|
|
2906
|
+
do {
|
|
2907
|
+
didSomething = false;
|
|
2908
|
+
if (!this.preserveMultipleSlashes) {
|
|
2909
|
+
for (let i = 1;i < parts.length - 1; i++) {
|
|
2910
|
+
const p = parts[i];
|
|
2911
|
+
if (i === 1 && p === "" && parts[0] === "")
|
|
2912
|
+
continue;
|
|
2913
|
+
if (p === "." || p === "") {
|
|
2914
|
+
didSomething = true;
|
|
2915
|
+
parts.splice(i, 1);
|
|
2916
|
+
i--;
|
|
2917
|
+
}
|
|
2918
|
+
}
|
|
2919
|
+
if (parts[0] === "." && parts.length === 2 && (parts[1] === "." || parts[1] === "")) {
|
|
2920
|
+
didSomething = true;
|
|
2921
|
+
parts.pop();
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
let dd = 0;
|
|
2925
|
+
while ((dd = parts.indexOf("..", dd + 1)) !== -1) {
|
|
2926
|
+
const p = parts[dd - 1];
|
|
2927
|
+
if (p && p !== "." && p !== ".." && p !== "**") {
|
|
2928
|
+
didSomething = true;
|
|
2929
|
+
parts.splice(dd - 1, 2);
|
|
2930
|
+
dd -= 2;
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2933
|
+
} while (didSomething);
|
|
2934
|
+
return parts.length === 0 ? [""] : parts;
|
|
2935
|
+
}
|
|
2936
|
+
firstPhasePreProcess(globParts) {
|
|
2937
|
+
let didSomething = false;
|
|
2938
|
+
do {
|
|
2939
|
+
didSomething = false;
|
|
2940
|
+
for (let parts of globParts) {
|
|
2941
|
+
let gs = -1;
|
|
2942
|
+
while ((gs = parts.indexOf("**", gs + 1)) !== -1) {
|
|
2943
|
+
let gss = gs;
|
|
2944
|
+
while (parts[gss + 1] === "**") {
|
|
2945
|
+
gss++;
|
|
2946
|
+
}
|
|
2947
|
+
if (gss > gs) {
|
|
2948
|
+
parts.splice(gs + 1, gss - gs);
|
|
2949
|
+
}
|
|
2950
|
+
let next = parts[gs + 1];
|
|
2951
|
+
const p = parts[gs + 2];
|
|
2952
|
+
const p2 = parts[gs + 3];
|
|
2953
|
+
if (next !== "..")
|
|
2954
|
+
continue;
|
|
2955
|
+
if (!p || p === "." || p === ".." || !p2 || p2 === "." || p2 === "..") {
|
|
2956
|
+
continue;
|
|
2957
|
+
}
|
|
2958
|
+
didSomething = true;
|
|
2959
|
+
parts.splice(gs, 1);
|
|
2960
|
+
const other = parts.slice(0);
|
|
2961
|
+
other[gs] = "**";
|
|
2962
|
+
globParts.push(other);
|
|
2963
|
+
gs--;
|
|
2964
|
+
}
|
|
2965
|
+
if (!this.preserveMultipleSlashes) {
|
|
2966
|
+
for (let i = 1;i < parts.length - 1; i++) {
|
|
2967
|
+
const p = parts[i];
|
|
2968
|
+
if (i === 1 && p === "" && parts[0] === "")
|
|
2969
|
+
continue;
|
|
2970
|
+
if (p === "." || p === "") {
|
|
2971
|
+
didSomething = true;
|
|
2972
|
+
parts.splice(i, 1);
|
|
2973
|
+
i--;
|
|
2974
|
+
}
|
|
2975
|
+
}
|
|
2976
|
+
if (parts[0] === "." && parts.length === 2 && (parts[1] === "." || parts[1] === "")) {
|
|
2977
|
+
didSomething = true;
|
|
2978
|
+
parts.pop();
|
|
2979
|
+
}
|
|
2980
|
+
}
|
|
2981
|
+
let dd = 0;
|
|
2982
|
+
while ((dd = parts.indexOf("..", dd + 1)) !== -1) {
|
|
2983
|
+
const p = parts[dd - 1];
|
|
2984
|
+
if (p && p !== "." && p !== ".." && p !== "**") {
|
|
2985
|
+
didSomething = true;
|
|
2986
|
+
const needDot = dd === 1 && parts[dd + 1] === "**";
|
|
2987
|
+
const splin = needDot ? ["."] : [];
|
|
2988
|
+
parts.splice(dd - 1, 2, ...splin);
|
|
2989
|
+
if (parts.length === 0)
|
|
2990
|
+
parts.push("");
|
|
2991
|
+
dd -= 2;
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2995
|
+
} while (didSomething);
|
|
2996
|
+
return globParts;
|
|
2997
|
+
}
|
|
2998
|
+
secondPhasePreProcess(globParts) {
|
|
2999
|
+
for (let i = 0;i < globParts.length - 1; i++) {
|
|
3000
|
+
for (let j = i + 1;j < globParts.length; j++) {
|
|
3001
|
+
const matched = this.partsMatch(globParts[i], globParts[j], !this.preserveMultipleSlashes);
|
|
3002
|
+
if (matched) {
|
|
3003
|
+
globParts[i] = [];
|
|
3004
|
+
globParts[j] = matched;
|
|
3005
|
+
break;
|
|
3006
|
+
}
|
|
3007
|
+
}
|
|
3008
|
+
}
|
|
3009
|
+
return globParts.filter((gs) => gs.length);
|
|
3010
|
+
}
|
|
3011
|
+
partsMatch(a, b, emptyGSMatch = false) {
|
|
3012
|
+
let ai = 0;
|
|
3013
|
+
let bi = 0;
|
|
3014
|
+
let result = [];
|
|
3015
|
+
let which = "";
|
|
3016
|
+
while (ai < a.length && bi < b.length) {
|
|
3017
|
+
if (a[ai] === b[bi]) {
|
|
3018
|
+
result.push(which === "b" ? b[bi] : a[ai]);
|
|
3019
|
+
ai++;
|
|
3020
|
+
bi++;
|
|
3021
|
+
} else if (emptyGSMatch && a[ai] === "**" && b[bi] === a[ai + 1]) {
|
|
3022
|
+
result.push(a[ai]);
|
|
3023
|
+
ai++;
|
|
3024
|
+
} else if (emptyGSMatch && b[bi] === "**" && a[ai] === b[bi + 1]) {
|
|
3025
|
+
result.push(b[bi]);
|
|
3026
|
+
bi++;
|
|
3027
|
+
} else if (a[ai] === "*" && b[bi] && (this.options.dot || !b[bi].startsWith(".")) && b[bi] !== "**") {
|
|
3028
|
+
if (which === "b")
|
|
3029
|
+
return false;
|
|
3030
|
+
which = "a";
|
|
3031
|
+
result.push(a[ai]);
|
|
3032
|
+
ai++;
|
|
3033
|
+
bi++;
|
|
3034
|
+
} else if (b[bi] === "*" && a[ai] && (this.options.dot || !a[ai].startsWith(".")) && a[ai] !== "**") {
|
|
3035
|
+
if (which === "a")
|
|
3036
|
+
return false;
|
|
3037
|
+
which = "b";
|
|
3038
|
+
result.push(b[bi]);
|
|
3039
|
+
ai++;
|
|
3040
|
+
bi++;
|
|
3041
|
+
} else {
|
|
3042
|
+
return false;
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
return a.length === b.length && result;
|
|
3046
|
+
}
|
|
3047
|
+
parseNegate() {
|
|
3048
|
+
if (this.nonegate)
|
|
3049
|
+
return;
|
|
3050
|
+
const pattern = this.pattern;
|
|
3051
|
+
let negate = false;
|
|
3052
|
+
let negateOffset = 0;
|
|
3053
|
+
for (let i = 0;i < pattern.length && pattern.charAt(i) === "!"; i++) {
|
|
3054
|
+
negate = !negate;
|
|
3055
|
+
negateOffset++;
|
|
3056
|
+
}
|
|
3057
|
+
if (negateOffset)
|
|
3058
|
+
this.pattern = pattern.slice(negateOffset);
|
|
3059
|
+
this.negate = negate;
|
|
3060
|
+
}
|
|
3061
|
+
matchOne(file, pattern, partial = false) {
|
|
3062
|
+
const options = this.options;
|
|
3063
|
+
if (this.isWindows) {
|
|
3064
|
+
const fileDrive = typeof file[0] === "string" && /^[a-z]:$/i.test(file[0]);
|
|
3065
|
+
const fileUNC = !fileDrive && file[0] === "" && file[1] === "" && file[2] === "?" && /^[a-z]:$/i.test(file[3]);
|
|
3066
|
+
const patternDrive = typeof pattern[0] === "string" && /^[a-z]:$/i.test(pattern[0]);
|
|
3067
|
+
const patternUNC = !patternDrive && pattern[0] === "" && pattern[1] === "" && pattern[2] === "?" && typeof pattern[3] === "string" && /^[a-z]:$/i.test(pattern[3]);
|
|
3068
|
+
const fdi = fileUNC ? 3 : fileDrive ? 0 : undefined;
|
|
3069
|
+
const pdi = patternUNC ? 3 : patternDrive ? 0 : undefined;
|
|
3070
|
+
if (typeof fdi === "number" && typeof pdi === "number") {
|
|
3071
|
+
const [fd, pd] = [file[fdi], pattern[pdi]];
|
|
3072
|
+
if (fd.toLowerCase() === pd.toLowerCase()) {
|
|
3073
|
+
pattern[pdi] = fd;
|
|
3074
|
+
if (pdi > fdi) {
|
|
3075
|
+
pattern = pattern.slice(pdi);
|
|
3076
|
+
} else if (fdi > pdi) {
|
|
3077
|
+
file = file.slice(fdi);
|
|
3078
|
+
}
|
|
3079
|
+
}
|
|
3080
|
+
}
|
|
3081
|
+
}
|
|
3082
|
+
const { optimizationLevel = 1 } = this.options;
|
|
3083
|
+
if (optimizationLevel >= 2) {
|
|
3084
|
+
file = this.levelTwoFileOptimize(file);
|
|
3085
|
+
}
|
|
3086
|
+
this.debug("matchOne", this, { file, pattern });
|
|
3087
|
+
this.debug("matchOne", file.length, pattern.length);
|
|
3088
|
+
for (var fi = 0, pi = 0, fl = file.length, pl = pattern.length;fi < fl && pi < pl; fi++, pi++) {
|
|
3089
|
+
this.debug("matchOne loop");
|
|
3090
|
+
var p = pattern[pi];
|
|
3091
|
+
var f = file[fi];
|
|
3092
|
+
this.debug(pattern, p, f);
|
|
3093
|
+
if (p === false) {
|
|
3094
|
+
return false;
|
|
3095
|
+
}
|
|
3096
|
+
if (p === GLOBSTAR) {
|
|
3097
|
+
this.debug("GLOBSTAR", [pattern, p, f]);
|
|
3098
|
+
var fr = fi;
|
|
3099
|
+
var pr = pi + 1;
|
|
3100
|
+
if (pr === pl) {
|
|
3101
|
+
this.debug("** at the end");
|
|
3102
|
+
for (;fi < fl; fi++) {
|
|
3103
|
+
if (file[fi] === "." || file[fi] === ".." || !options.dot && file[fi].charAt(0) === ".")
|
|
3104
|
+
return false;
|
|
3105
|
+
}
|
|
3106
|
+
return true;
|
|
3107
|
+
}
|
|
3108
|
+
while (fr < fl) {
|
|
3109
|
+
var swallowee = file[fr];
|
|
3110
|
+
this.debug(`
|
|
3111
|
+
globstar while`, file, fr, pattern, pr, swallowee);
|
|
3112
|
+
if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) {
|
|
3113
|
+
this.debug("globstar found match!", fr, fl, swallowee);
|
|
3114
|
+
return true;
|
|
3115
|
+
} else {
|
|
3116
|
+
if (swallowee === "." || swallowee === ".." || !options.dot && swallowee.charAt(0) === ".") {
|
|
3117
|
+
this.debug("dot detected!", file, fr, pattern, pr);
|
|
3118
|
+
break;
|
|
3119
|
+
}
|
|
3120
|
+
this.debug("globstar swallow a segment, and continue");
|
|
3121
|
+
fr++;
|
|
3122
|
+
}
|
|
3123
|
+
}
|
|
3124
|
+
if (partial) {
|
|
3125
|
+
this.debug(`
|
|
3126
|
+
>>> no match, partial?`, file, fr, pattern, pr);
|
|
3127
|
+
if (fr === fl) {
|
|
3128
|
+
return true;
|
|
3129
|
+
}
|
|
3130
|
+
}
|
|
3131
|
+
return false;
|
|
3132
|
+
}
|
|
3133
|
+
let hit;
|
|
3134
|
+
if (typeof p === "string") {
|
|
3135
|
+
hit = f === p;
|
|
3136
|
+
this.debug("string match", p, f, hit);
|
|
3137
|
+
} else {
|
|
3138
|
+
hit = p.test(f);
|
|
3139
|
+
this.debug("pattern match", p, f, hit);
|
|
3140
|
+
}
|
|
3141
|
+
if (!hit)
|
|
3142
|
+
return false;
|
|
3143
|
+
}
|
|
3144
|
+
if (fi === fl && pi === pl) {
|
|
3145
|
+
return true;
|
|
3146
|
+
} else if (fi === fl) {
|
|
3147
|
+
return partial;
|
|
3148
|
+
} else if (pi === pl) {
|
|
3149
|
+
return fi === fl - 1 && file[fi] === "";
|
|
3150
|
+
} else {
|
|
3151
|
+
throw new Error("wtf?");
|
|
3152
|
+
}
|
|
3153
|
+
}
|
|
3154
|
+
braceExpand() {
|
|
3155
|
+
return braceExpand(this.pattern, this.options);
|
|
3156
|
+
}
|
|
3157
|
+
parse(pattern) {
|
|
3158
|
+
assertValidPattern(pattern);
|
|
3159
|
+
const options = this.options;
|
|
3160
|
+
if (pattern === "**")
|
|
3161
|
+
return GLOBSTAR;
|
|
3162
|
+
if (pattern === "")
|
|
3163
|
+
return "";
|
|
3164
|
+
let m;
|
|
3165
|
+
let fastTest = null;
|
|
3166
|
+
if (m = pattern.match(starRE)) {
|
|
3167
|
+
fastTest = options.dot ? starTestDot : starTest;
|
|
3168
|
+
} else if (m = pattern.match(starDotExtRE)) {
|
|
3169
|
+
fastTest = (options.nocase ? options.dot ? starDotExtTestNocaseDot : starDotExtTestNocase : options.dot ? starDotExtTestDot : starDotExtTest)(m[1]);
|
|
3170
|
+
} else if (m = pattern.match(qmarksRE)) {
|
|
3171
|
+
fastTest = (options.nocase ? options.dot ? qmarksTestNocaseDot : qmarksTestNocase : options.dot ? qmarksTestDot : qmarksTest)(m);
|
|
3172
|
+
} else if (m = pattern.match(starDotStarRE)) {
|
|
3173
|
+
fastTest = options.dot ? starDotStarTestDot : starDotStarTest;
|
|
3174
|
+
} else if (m = pattern.match(dotStarRE)) {
|
|
3175
|
+
fastTest = dotStarTest;
|
|
3176
|
+
}
|
|
3177
|
+
const re = AST.fromGlob(pattern, this.options).toMMPattern();
|
|
3178
|
+
if (fastTest && typeof re === "object") {
|
|
3179
|
+
Reflect.defineProperty(re, "test", { value: fastTest });
|
|
3180
|
+
}
|
|
3181
|
+
return re;
|
|
3182
|
+
}
|
|
3183
|
+
makeRe() {
|
|
3184
|
+
if (this.regexp || this.regexp === false)
|
|
3185
|
+
return this.regexp;
|
|
3186
|
+
const set = this.set;
|
|
3187
|
+
if (!set.length) {
|
|
3188
|
+
this.regexp = false;
|
|
3189
|
+
return this.regexp;
|
|
3190
|
+
}
|
|
3191
|
+
const options = this.options;
|
|
3192
|
+
const twoStar = options.noglobstar ? star2 : options.dot ? twoStarDot : twoStarNoDot;
|
|
3193
|
+
const flags = new Set(options.nocase ? ["i"] : []);
|
|
3194
|
+
let re = set.map((pattern) => {
|
|
3195
|
+
const pp = pattern.map((p) => {
|
|
3196
|
+
if (p instanceof RegExp) {
|
|
3197
|
+
for (const f of p.flags.split(""))
|
|
3198
|
+
flags.add(f);
|
|
3199
|
+
}
|
|
3200
|
+
return typeof p === "string" ? regExpEscape2(p) : p === GLOBSTAR ? GLOBSTAR : p._src;
|
|
3201
|
+
});
|
|
3202
|
+
pp.forEach((p, i) => {
|
|
3203
|
+
const next = pp[i + 1];
|
|
3204
|
+
const prev = pp[i - 1];
|
|
3205
|
+
if (p !== GLOBSTAR || prev === GLOBSTAR) {
|
|
3206
|
+
return;
|
|
3207
|
+
}
|
|
3208
|
+
if (prev === undefined) {
|
|
3209
|
+
if (next !== undefined && next !== GLOBSTAR) {
|
|
3210
|
+
pp[i + 1] = "(?:\\/|" + twoStar + "\\/)?" + next;
|
|
3211
|
+
} else {
|
|
3212
|
+
pp[i] = twoStar;
|
|
3213
|
+
}
|
|
3214
|
+
} else if (next === undefined) {
|
|
3215
|
+
pp[i - 1] = prev + "(?:\\/|\\/" + twoStar + ")?";
|
|
3216
|
+
} else if (next !== GLOBSTAR) {
|
|
3217
|
+
pp[i - 1] = prev + "(?:\\/|\\/" + twoStar + "\\/)" + next;
|
|
3218
|
+
pp[i + 1] = GLOBSTAR;
|
|
3219
|
+
}
|
|
3220
|
+
});
|
|
3221
|
+
const filtered = pp.filter((p) => p !== GLOBSTAR);
|
|
3222
|
+
if (this.partial && filtered.length >= 1) {
|
|
3223
|
+
const prefixes = [];
|
|
3224
|
+
for (let i = 1;i <= filtered.length; i++) {
|
|
3225
|
+
prefixes.push(filtered.slice(0, i).join("/"));
|
|
3226
|
+
}
|
|
3227
|
+
return "(?:" + prefixes.join("|") + ")";
|
|
3228
|
+
}
|
|
3229
|
+
return filtered.join("/");
|
|
3230
|
+
}).join("|");
|
|
3231
|
+
const [open, close] = set.length > 1 ? ["(?:", ")"] : ["", ""];
|
|
3232
|
+
re = "^" + open + re + close + "$";
|
|
3233
|
+
if (this.partial) {
|
|
3234
|
+
re = "^(?:\\/|" + open + re.slice(1, -1) + close + ")$";
|
|
3235
|
+
}
|
|
3236
|
+
if (this.negate)
|
|
3237
|
+
re = "^(?!" + re + ").+$";
|
|
3238
|
+
try {
|
|
3239
|
+
this.regexp = new RegExp(re, [...flags].join(""));
|
|
3240
|
+
} catch (ex) {
|
|
3241
|
+
this.regexp = false;
|
|
3242
|
+
}
|
|
3243
|
+
return this.regexp;
|
|
3244
|
+
}
|
|
3245
|
+
slashSplit(p) {
|
|
3246
|
+
if (this.preserveMultipleSlashes) {
|
|
3247
|
+
return p.split("/");
|
|
3248
|
+
} else if (this.isWindows && /^\/\/[^\/]+/.test(p)) {
|
|
3249
|
+
return ["", ...p.split(/\/+/)];
|
|
3250
|
+
} else {
|
|
3251
|
+
return p.split(/\/+/);
|
|
3252
|
+
}
|
|
3253
|
+
}
|
|
3254
|
+
match(f, partial = this.partial) {
|
|
3255
|
+
this.debug("match", f, this.pattern);
|
|
3256
|
+
if (this.comment) {
|
|
3257
|
+
return false;
|
|
3258
|
+
}
|
|
3259
|
+
if (this.empty) {
|
|
3260
|
+
return f === "";
|
|
3261
|
+
}
|
|
3262
|
+
if (f === "/" && partial) {
|
|
3263
|
+
return true;
|
|
3264
|
+
}
|
|
3265
|
+
const options = this.options;
|
|
3266
|
+
if (this.isWindows) {
|
|
3267
|
+
f = f.split("\\").join("/");
|
|
3268
|
+
}
|
|
3269
|
+
const ff = this.slashSplit(f);
|
|
3270
|
+
this.debug(this.pattern, "split", ff);
|
|
3271
|
+
const set = this.set;
|
|
3272
|
+
this.debug(this.pattern, "set", set);
|
|
3273
|
+
let filename = ff[ff.length - 1];
|
|
3274
|
+
if (!filename) {
|
|
3275
|
+
for (let i = ff.length - 2;!filename && i >= 0; i--) {
|
|
3276
|
+
filename = ff[i];
|
|
3277
|
+
}
|
|
3278
|
+
}
|
|
3279
|
+
for (let i = 0;i < set.length; i++) {
|
|
3280
|
+
const pattern = set[i];
|
|
3281
|
+
let file = ff;
|
|
3282
|
+
if (options.matchBase && pattern.length === 1) {
|
|
3283
|
+
file = [filename];
|
|
3284
|
+
}
|
|
3285
|
+
const hit = this.matchOne(file, pattern, partial);
|
|
3286
|
+
if (hit) {
|
|
3287
|
+
if (options.flipNegate) {
|
|
3288
|
+
return true;
|
|
3289
|
+
}
|
|
3290
|
+
return !this.negate;
|
|
3291
|
+
}
|
|
3292
|
+
}
|
|
3293
|
+
if (options.flipNegate) {
|
|
3294
|
+
return false;
|
|
3295
|
+
}
|
|
3296
|
+
return this.negate;
|
|
3297
|
+
}
|
|
3298
|
+
static defaults(def) {
|
|
3299
|
+
return minimatch.defaults(def).Minimatch;
|
|
3300
|
+
}
|
|
3301
|
+
}
|
|
3302
|
+
minimatch.AST = AST;
|
|
3303
|
+
minimatch.Minimatch = Minimatch;
|
|
3304
|
+
minimatch.escape = escape;
|
|
3305
|
+
minimatch.unescape = unescape;
|
|
3306
|
+
|
|
3307
|
+
// src/commands/check/github-fetcher.ts
|
|
3308
|
+
var DEFAULT_TIMEOUT2 = 15000;
|
|
3309
|
+
var DEFAULT_TTL3 = 60 * 60 * 1000;
|
|
3310
|
+
function getGitHubToken() {
|
|
3311
|
+
return process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
|
|
3312
|
+
}
|
|
3313
|
+
function buildHeaders() {
|
|
3314
|
+
const headers = {
|
|
3315
|
+
Accept: "application/vnd.github.v3+json",
|
|
3316
|
+
"User-Agent": "doccov-cli"
|
|
3317
|
+
};
|
|
3318
|
+
const token = getGitHubToken();
|
|
3319
|
+
if (token) {
|
|
3320
|
+
headers.Authorization = `Bearer ${token}`;
|
|
3321
|
+
}
|
|
3322
|
+
return headers;
|
|
3323
|
+
}
|
|
3324
|
+
async function fetchFileTree(owner, repo, ref, timeout) {
|
|
3325
|
+
const url = `https://api.github.com/repos/${owner}/${repo}/git/trees/${ref}?recursive=1`;
|
|
3326
|
+
const controller = new AbortController;
|
|
3327
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
3328
|
+
try {
|
|
3329
|
+
const response = await fetch(url, {
|
|
3330
|
+
signal: controller.signal,
|
|
3331
|
+
headers: buildHeaders()
|
|
3332
|
+
});
|
|
3333
|
+
clearTimeout(timeoutId);
|
|
3334
|
+
if (!response.ok) {
|
|
3335
|
+
if (response.status === 401 || response.status === 403) {
|
|
3336
|
+
throw new Error(`GitHub API auth error (${response.status}). Set GITHUB_TOKEN for private repos or higher rate limits.`);
|
|
3337
|
+
}
|
|
3338
|
+
if (response.status === 404) {
|
|
3339
|
+
throw new Error(`GitHub repo not found: ${owner}/${repo}@${ref}`);
|
|
3340
|
+
}
|
|
3341
|
+
throw new Error(`GitHub API error: ${response.status} ${response.statusText}`);
|
|
3342
|
+
}
|
|
3343
|
+
return response.json();
|
|
3344
|
+
} catch (err) {
|
|
3345
|
+
clearTimeout(timeoutId);
|
|
3346
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
3347
|
+
throw new Error(`GitHub API timeout after ${timeout}ms`);
|
|
3348
|
+
}
|
|
3349
|
+
throw err;
|
|
3350
|
+
}
|
|
3351
|
+
}
|
|
3352
|
+
async function fetchRawContent(owner, repo, ref, path8, timeout) {
|
|
3353
|
+
const url = `https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${path8}`;
|
|
3354
|
+
const controller = new AbortController;
|
|
3355
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
3356
|
+
try {
|
|
3357
|
+
const response = await fetch(url, {
|
|
3358
|
+
signal: controller.signal,
|
|
3359
|
+
headers: {
|
|
3360
|
+
"User-Agent": "doccov-cli",
|
|
3361
|
+
...getGitHubToken() ? { Authorization: `Bearer ${getGitHubToken()}` } : {}
|
|
3362
|
+
}
|
|
3363
|
+
});
|
|
3364
|
+
clearTimeout(timeoutId);
|
|
3365
|
+
if (!response.ok) {
|
|
3366
|
+
throw new Error(`Failed to fetch ${path8}: ${response.status}`);
|
|
3367
|
+
}
|
|
3368
|
+
return response.text();
|
|
3369
|
+
} catch (err) {
|
|
3370
|
+
clearTimeout(timeoutId);
|
|
3371
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
3372
|
+
throw new Error(`Timeout fetching ${path8}`);
|
|
3373
|
+
}
|
|
3374
|
+
throw err;
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
3377
|
+
function matchGlob(files, pattern) {
|
|
3378
|
+
const glob2 = pattern || "**/*.md";
|
|
3379
|
+
return files.filter((file) => minimatch(file, glob2, { matchBase: true }));
|
|
3380
|
+
}
|
|
3381
|
+
function parseMarkdownContent2(content, sourcePath) {
|
|
3382
|
+
const codeBlocks = [];
|
|
3383
|
+
const lines = content.split(`
|
|
3384
|
+
`);
|
|
3385
|
+
let inBlock = false;
|
|
3386
|
+
let blockLang = "";
|
|
3387
|
+
let blockCode = [];
|
|
3388
|
+
let blockStart = 0;
|
|
3389
|
+
const executableLangs = new Set(["ts", "typescript", "js", "javascript", "tsx", "jsx", "mts", "mjs"]);
|
|
3390
|
+
for (let i = 0;i < lines.length; i++) {
|
|
3391
|
+
const line = lines[i];
|
|
3392
|
+
if (!inBlock && line.startsWith("```")) {
|
|
3393
|
+
inBlock = true;
|
|
3394
|
+
blockLang = line.slice(3).trim().split(/\s/)[0] || "";
|
|
3395
|
+
blockCode = [];
|
|
3396
|
+
blockStart = i + 1;
|
|
3397
|
+
} else if (inBlock && line.startsWith("```")) {
|
|
3398
|
+
const normalizedLang = normalizeLang(blockLang);
|
|
3399
|
+
if (executableLangs.has(normalizedLang.toLowerCase())) {
|
|
3400
|
+
codeBlocks.push({
|
|
3401
|
+
lang: normalizedLang || "ts",
|
|
3402
|
+
code: blockCode.join(`
|
|
3403
|
+
`),
|
|
3404
|
+
lineStart: blockStart,
|
|
3405
|
+
lineEnd: i
|
|
3406
|
+
});
|
|
3407
|
+
}
|
|
3408
|
+
inBlock = false;
|
|
3409
|
+
blockLang = "";
|
|
3410
|
+
blockCode = [];
|
|
3411
|
+
} else if (inBlock) {
|
|
3412
|
+
blockCode.push(line);
|
|
3413
|
+
}
|
|
3414
|
+
}
|
|
3415
|
+
return { path: sourcePath, codeBlocks };
|
|
3416
|
+
}
|
|
3417
|
+
function normalizeLang(lang) {
|
|
3418
|
+
const aliases = {
|
|
3419
|
+
typescript: "ts",
|
|
3420
|
+
javascript: "js",
|
|
3421
|
+
tsx: "tsx",
|
|
3422
|
+
jsx: "jsx"
|
|
3423
|
+
};
|
|
3424
|
+
return aliases[lang.toLowerCase()] ?? lang;
|
|
3425
|
+
}
|
|
3426
|
+
function getCacheKey(source) {
|
|
3427
|
+
return `${source.org}/${source.repo}/${source.path ?? "**/*.md"}#${source.branch ?? "HEAD"}`;
|
|
3428
|
+
}
|
|
3429
|
+
async function fetchGitHubDocs(source, options = {}) {
|
|
3430
|
+
const {
|
|
3431
|
+
timeout = DEFAULT_TIMEOUT2,
|
|
3432
|
+
ttl = DEFAULT_TTL3,
|
|
3433
|
+
useCache = true,
|
|
3434
|
+
cwd = process.cwd()
|
|
3435
|
+
} = options;
|
|
3436
|
+
const cacheKey = getCacheKey(source);
|
|
3437
|
+
if (useCache) {
|
|
3438
|
+
const cached = getFromCache(cwd, "github", cacheKey, ttl);
|
|
3439
|
+
if (cached) {
|
|
3440
|
+
try {
|
|
3441
|
+
const docs = JSON.parse(cached.content);
|
|
3442
|
+
return { source, docs, cached: true };
|
|
3443
|
+
} catch {}
|
|
3444
|
+
}
|
|
3445
|
+
}
|
|
3446
|
+
try {
|
|
3447
|
+
const ref = source.branch ?? "HEAD";
|
|
3448
|
+
const tree = await fetchFileTree(source.org, source.repo, ref, timeout);
|
|
3449
|
+
const allPaths = tree.tree.filter((item) => item.type === "blob").map((item) => item.path);
|
|
3450
|
+
const mdPaths = matchGlob(allPaths, source.path ?? "**/*.md").filter((p) => p.endsWith(".md") || p.endsWith(".mdx"));
|
|
3451
|
+
if (mdPaths.length === 0) {
|
|
3452
|
+
return { source, docs: [] };
|
|
3453
|
+
}
|
|
3454
|
+
const docs = [];
|
|
3455
|
+
const BATCH_SIZE = 5;
|
|
3456
|
+
for (let i = 0;i < mdPaths.length; i += BATCH_SIZE) {
|
|
3457
|
+
const batch = mdPaths.slice(i, i + BATCH_SIZE);
|
|
3458
|
+
const results = await Promise.all(batch.map(async (filePath) => {
|
|
3459
|
+
try {
|
|
3460
|
+
const content = await fetchRawContent(source.org, source.repo, ref, filePath, timeout);
|
|
3461
|
+
const sourcePath = `github:${source.org}/${source.repo}/${filePath}`;
|
|
3462
|
+
return parseMarkdownContent2(content, sourcePath);
|
|
3463
|
+
} catch {
|
|
3464
|
+
return null;
|
|
3465
|
+
}
|
|
3466
|
+
}));
|
|
3467
|
+
for (const doc of results) {
|
|
3468
|
+
if (doc && doc.codeBlocks.length > 0) {
|
|
3469
|
+
docs.push(doc);
|
|
3470
|
+
}
|
|
3471
|
+
}
|
|
3472
|
+
}
|
|
3473
|
+
if (useCache) {
|
|
3474
|
+
writeToCache(cwd, "github", cacheKey, JSON.stringify(docs));
|
|
3475
|
+
}
|
|
3476
|
+
return { source, docs };
|
|
3477
|
+
} catch (err) {
|
|
3478
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3479
|
+
return { source, docs: [], error: message };
|
|
3480
|
+
}
|
|
3481
|
+
}
|
|
3482
|
+
async function fetchGitHubDocsBatch(sources, options = {}) {
|
|
3483
|
+
const results = [];
|
|
3484
|
+
for (const source of sources) {
|
|
3485
|
+
results.push(await fetchGitHubDocs(source, options));
|
|
3486
|
+
}
|
|
3487
|
+
return results;
|
|
3488
|
+
}
|
|
3489
|
+
|
|
3490
|
+
// src/commands/check/parseDocsPattern.ts
|
|
3491
|
+
function parseDocsPattern(pattern) {
|
|
3492
|
+
if (/^https?:\/\//i.test(pattern)) {
|
|
3493
|
+
return { type: "url", url: pattern };
|
|
3494
|
+
}
|
|
3495
|
+
if (/^(github|gh):/i.test(pattern)) {
|
|
3496
|
+
return parseGitHubPattern(pattern);
|
|
3497
|
+
}
|
|
3498
|
+
if (/^(gitlab|gl):/i.test(pattern)) {
|
|
3499
|
+
return parseGitLabPattern(pattern);
|
|
3500
|
+
}
|
|
3501
|
+
return { type: "local", pattern };
|
|
3502
|
+
}
|
|
3503
|
+
function parseGitHubPattern(pattern) {
|
|
3504
|
+
const rest = pattern.replace(/^(github|gh):/i, "");
|
|
3505
|
+
const [pathPart, branch] = rest.split("#");
|
|
3506
|
+
const parts = pathPart.split("/");
|
|
3507
|
+
if (parts.length < 2) {
|
|
3508
|
+
throw new Error(`Invalid GitHub pattern: ${pattern}. Expected format: github:org/repo[/path][#branch]`);
|
|
3509
|
+
}
|
|
3510
|
+
const org = parts[0];
|
|
3511
|
+
const repo = parts[1];
|
|
3512
|
+
const path8 = parts.length > 2 ? parts.slice(2).join("/") : undefined;
|
|
3513
|
+
return {
|
|
3514
|
+
type: "github",
|
|
3515
|
+
org,
|
|
3516
|
+
repo,
|
|
3517
|
+
...path8 && { path: path8 },
|
|
3518
|
+
...branch && { branch }
|
|
3519
|
+
};
|
|
3520
|
+
}
|
|
3521
|
+
function parseGitLabPattern(pattern) {
|
|
3522
|
+
const rest = pattern.replace(/^(gitlab|gl):/i, "");
|
|
3523
|
+
const [pathPart, branch] = rest.split("#");
|
|
3524
|
+
const parts = pathPart.split("/");
|
|
3525
|
+
if (parts.length < 2) {
|
|
3526
|
+
throw new Error(`Invalid GitLab pattern: ${pattern}. Expected format: gitlab:org/repo[/path][#branch]`);
|
|
3527
|
+
}
|
|
3528
|
+
const org = parts[0];
|
|
3529
|
+
const repo = parts[1];
|
|
3530
|
+
const path8 = parts.length > 2 ? parts.slice(2).join("/") : undefined;
|
|
3531
|
+
return {
|
|
3532
|
+
type: "gitlab",
|
|
3533
|
+
org,
|
|
3534
|
+
repo,
|
|
3535
|
+
...path8 && { path: path8 },
|
|
3536
|
+
...branch && { branch }
|
|
3537
|
+
};
|
|
3538
|
+
}
|
|
3539
|
+
function parseDocsPatterns(patterns) {
|
|
3540
|
+
return patterns.map(parseDocsPattern);
|
|
3541
|
+
}
|
|
3542
|
+
|
|
3543
|
+
// src/commands/check/validation.ts
|
|
1653
3544
|
async function runExampleValidation(openpkg, options) {
|
|
1654
3545
|
const { validations, targetDir, timeout = 5000, installTimeout = 60000 } = options;
|
|
1655
3546
|
const typecheckErrors = [];
|
|
@@ -1682,18 +3573,62 @@ async function runExampleValidation(openpkg, options) {
|
|
|
1682
3573
|
}
|
|
1683
3574
|
return { result, typecheckErrors, runtimeDrifts };
|
|
1684
3575
|
}
|
|
3576
|
+
function parseAndSeparateDocsSources(patterns) {
|
|
3577
|
+
const sources = parseDocsPatterns(patterns);
|
|
3578
|
+
const local = [];
|
|
3579
|
+
const remote = [];
|
|
3580
|
+
for (const source of sources) {
|
|
3581
|
+
if (source.type === "local") {
|
|
3582
|
+
local.push(source.pattern);
|
|
3583
|
+
} else {
|
|
3584
|
+
remote.push(source);
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
return { local, remote };
|
|
3588
|
+
}
|
|
1685
3589
|
async function validateMarkdownDocs(options) {
|
|
1686
|
-
const { docsPatterns, targetDir, exportNames } = options;
|
|
3590
|
+
const { docsPatterns, targetDir, exportNames, useCache = true, fetchTimeout, cacheTtl } = options;
|
|
1687
3591
|
const staleRefs = [];
|
|
1688
3592
|
if (docsPatterns.length === 0) {
|
|
1689
3593
|
return staleRefs;
|
|
1690
3594
|
}
|
|
1691
|
-
const
|
|
1692
|
-
|
|
3595
|
+
const { local, remote } = parseAndSeparateDocsSources(docsPatterns);
|
|
3596
|
+
const allDocs = [];
|
|
3597
|
+
if (local.length > 0) {
|
|
3598
|
+
const localDocs = await loadMarkdownFiles(local, targetDir);
|
|
3599
|
+
allDocs.push(...localDocs);
|
|
3600
|
+
}
|
|
3601
|
+
const urlSources = remote.filter((s) => s.type === "url");
|
|
3602
|
+
if (urlSources.length > 0) {
|
|
3603
|
+
const results = await fetchUrlDocsBatch(urlSources, {
|
|
3604
|
+
useCache,
|
|
3605
|
+
timeout: fetchTimeout,
|
|
3606
|
+
ttl: cacheTtl,
|
|
3607
|
+
cwd: targetDir
|
|
3608
|
+
});
|
|
3609
|
+
for (const result of results) {
|
|
3610
|
+
if (result.doc) {
|
|
3611
|
+
allDocs.push(result.doc);
|
|
3612
|
+
}
|
|
3613
|
+
}
|
|
3614
|
+
}
|
|
3615
|
+
const githubSources = remote.filter((s) => s.type === "github");
|
|
3616
|
+
if (githubSources.length > 0) {
|
|
3617
|
+
const results = await fetchGitHubDocsBatch(githubSources, {
|
|
3618
|
+
useCache,
|
|
3619
|
+
timeout: fetchTimeout,
|
|
3620
|
+
ttl: cacheTtl,
|
|
3621
|
+
cwd: targetDir
|
|
3622
|
+
});
|
|
3623
|
+
for (const result of results) {
|
|
3624
|
+
allDocs.push(...result.docs);
|
|
3625
|
+
}
|
|
3626
|
+
}
|
|
3627
|
+
if (allDocs.length === 0) {
|
|
1693
3628
|
return staleRefs;
|
|
1694
3629
|
}
|
|
1695
3630
|
const exportSet = new Set(exportNames);
|
|
1696
|
-
for (const mdFile of
|
|
3631
|
+
for (const mdFile of allDocs) {
|
|
1697
3632
|
for (const block of mdFile.codeBlocks) {
|
|
1698
3633
|
const codeLines = block.code.split(`
|
|
1699
3634
|
`);
|
|
@@ -1885,8 +3820,8 @@ function registerCheckCommand(program, dependencies = {}) {
|
|
|
1885
3820
|
});
|
|
1886
3821
|
}
|
|
1887
3822
|
// src/commands/diff.ts
|
|
1888
|
-
import * as
|
|
1889
|
-
import * as
|
|
3823
|
+
import * as fs5 from "node:fs";
|
|
3824
|
+
import * as path8 from "node:path";
|
|
1890
3825
|
import {
|
|
1891
3826
|
diffSpecWithDocs,
|
|
1892
3827
|
ensureSpecCoverage,
|
|
@@ -1900,7 +3835,7 @@ import { calculateNextVersion, recommendSemverBump } from "@openpkg-ts/spec";
|
|
|
1900
3835
|
import chalk7 from "chalk";
|
|
1901
3836
|
import { glob as glob2 } from "glob";
|
|
1902
3837
|
var defaultDependencies2 = {
|
|
1903
|
-
readFileSync:
|
|
3838
|
+
readFileSync: fs5.readFileSync,
|
|
1904
3839
|
log: console.log,
|
|
1905
3840
|
error: console.error
|
|
1906
3841
|
};
|
|
@@ -1919,7 +3854,7 @@ function getStrictChecks(preset) {
|
|
|
1919
3854
|
return checks;
|
|
1920
3855
|
}
|
|
1921
3856
|
function registerDiffCommand(program, dependencies = {}) {
|
|
1922
|
-
const { readFileSync:
|
|
3857
|
+
const { readFileSync: readFileSync4, log, error } = {
|
|
1923
3858
|
...defaultDependencies2,
|
|
1924
3859
|
...dependencies
|
|
1925
3860
|
};
|
|
@@ -1933,18 +3868,18 @@ function registerDiffCommand(program, dependencies = {}) {
|
|
|
1933
3868
|
` + " or: doccov diff --base main.json --head feature.json");
|
|
1934
3869
|
}
|
|
1935
3870
|
const spin = spinner("Comparing specs...");
|
|
1936
|
-
const baseSpec = loadSpec(baseFile,
|
|
1937
|
-
const headSpec = loadSpec(headFile,
|
|
3871
|
+
const baseSpec = loadSpec(baseFile, readFileSync4);
|
|
3872
|
+
const headSpec = loadSpec(headFile, readFileSync4);
|
|
1938
3873
|
const config = await loadDocCovConfig(options.cwd);
|
|
1939
3874
|
const baseHash = hashString(JSON.stringify(baseSpec));
|
|
1940
3875
|
const headHash = hashString(JSON.stringify(headSpec));
|
|
1941
3876
|
const cacheEnabled = options.cache !== false;
|
|
1942
|
-
const cachedReportPath =
|
|
3877
|
+
const cachedReportPath = path8.resolve(options.cwd, getDiffReportPath(baseHash, headHash, "json"));
|
|
1943
3878
|
let diff;
|
|
1944
3879
|
let fromCache = false;
|
|
1945
|
-
if (cacheEnabled &&
|
|
3880
|
+
if (cacheEnabled && fs5.existsSync(cachedReportPath)) {
|
|
1946
3881
|
try {
|
|
1947
|
-
const cached = JSON.parse(
|
|
3882
|
+
const cached = JSON.parse(fs5.readFileSync(cachedReportPath, "utf-8"));
|
|
1948
3883
|
diff = cached;
|
|
1949
3884
|
fromCache = true;
|
|
1950
3885
|
} catch {
|
|
@@ -1982,8 +3917,8 @@ function registerDiffCommand(program, dependencies = {}) {
|
|
|
1982
3917
|
const format = options.format ?? "text";
|
|
1983
3918
|
const limit = parseInt(options.limit, 10) || 10;
|
|
1984
3919
|
const checks = getStrictChecks(options.strict);
|
|
1985
|
-
const baseName =
|
|
1986
|
-
const headName =
|
|
3920
|
+
const baseName = path8.basename(baseFile);
|
|
3921
|
+
const headName = path8.basename(headFile);
|
|
1987
3922
|
const reportData = {
|
|
1988
3923
|
baseName,
|
|
1989
3924
|
headName,
|
|
@@ -2088,7 +4023,7 @@ async function loadMarkdownFiles2(patterns) {
|
|
|
2088
4023
|
const matches = await glob2(pattern, { nodir: true });
|
|
2089
4024
|
for (const filePath of matches) {
|
|
2090
4025
|
try {
|
|
2091
|
-
const content =
|
|
4026
|
+
const content = fs5.readFileSync(filePath, "utf-8");
|
|
2092
4027
|
files.push({ path: filePath, content });
|
|
2093
4028
|
} catch {}
|
|
2094
4029
|
}
|
|
@@ -2109,13 +4044,13 @@ async function generateDiff(baseSpec, headSpec, options, config, log) {
|
|
|
2109
4044
|
}
|
|
2110
4045
|
return diffSpecWithDocs(baseSpec, headSpec, { markdownFiles });
|
|
2111
4046
|
}
|
|
2112
|
-
function loadSpec(filePath,
|
|
2113
|
-
const resolvedPath =
|
|
2114
|
-
if (!
|
|
4047
|
+
function loadSpec(filePath, readFileSync4) {
|
|
4048
|
+
const resolvedPath = path8.resolve(filePath);
|
|
4049
|
+
if (!fs5.existsSync(resolvedPath)) {
|
|
2115
4050
|
throw new Error(`File not found: ${filePath}`);
|
|
2116
4051
|
}
|
|
2117
4052
|
try {
|
|
2118
|
-
const content =
|
|
4053
|
+
const content = readFileSync4(resolvedPath, "utf-8");
|
|
2119
4054
|
const spec = JSON.parse(content);
|
|
2120
4055
|
return ensureSpecCoverage(spec);
|
|
2121
4056
|
} catch (parseError) {
|
|
@@ -2235,34 +4170,34 @@ function printGitHubAnnotations(diff, log) {
|
|
|
2235
4170
|
}
|
|
2236
4171
|
|
|
2237
4172
|
// src/commands/init.ts
|
|
2238
|
-
import * as
|
|
2239
|
-
import * as
|
|
4173
|
+
import * as fs6 from "node:fs";
|
|
4174
|
+
import * as path9 from "node:path";
|
|
2240
4175
|
import chalk8 from "chalk";
|
|
2241
4176
|
var defaultDependencies3 = {
|
|
2242
|
-
fileExists:
|
|
2243
|
-
writeFileSync:
|
|
2244
|
-
readFileSync:
|
|
2245
|
-
mkdirSync:
|
|
4177
|
+
fileExists: fs6.existsSync,
|
|
4178
|
+
writeFileSync: fs6.writeFileSync,
|
|
4179
|
+
readFileSync: fs6.readFileSync,
|
|
4180
|
+
mkdirSync: fs6.mkdirSync,
|
|
2246
4181
|
log: console.log,
|
|
2247
4182
|
error: console.error
|
|
2248
4183
|
};
|
|
2249
4184
|
function registerInitCommand(program, dependencies = {}) {
|
|
2250
|
-
const { fileExists: fileExists2, writeFileSync:
|
|
4185
|
+
const { fileExists: fileExists2, writeFileSync: writeFileSync4, readFileSync: readFileSync5, mkdirSync: mkdirSync4, log, error } = {
|
|
2251
4186
|
...defaultDependencies3,
|
|
2252
4187
|
...dependencies
|
|
2253
4188
|
};
|
|
2254
4189
|
program.command("init").description("Initialize DocCov: config, GitHub Action, and badge").option("--cwd <dir>", "Working directory", process.cwd()).option("--skip-action", "Skip GitHub Action workflow creation").action((options) => {
|
|
2255
|
-
const cwd =
|
|
4190
|
+
const cwd = path9.resolve(options.cwd);
|
|
2256
4191
|
const existing = findExistingConfig(cwd, fileExists2);
|
|
2257
4192
|
if (existing) {
|
|
2258
|
-
error(chalk8.red(`A DocCov config already exists at ${
|
|
4193
|
+
error(chalk8.red(`A DocCov config already exists at ${path9.relative(cwd, existing) || "./doccov.config.*"}.`));
|
|
2259
4194
|
process.exitCode = 1;
|
|
2260
4195
|
return;
|
|
2261
4196
|
}
|
|
2262
|
-
const packageType = detectPackageType(cwd, fileExists2,
|
|
4197
|
+
const packageType = detectPackageType(cwd, fileExists2, readFileSync5);
|
|
2263
4198
|
const targetFormat = packageType === "module" ? "ts" : "mts";
|
|
2264
4199
|
const fileName = `doccov.config.${targetFormat}`;
|
|
2265
|
-
const outputPath =
|
|
4200
|
+
const outputPath = path9.join(cwd, fileName);
|
|
2266
4201
|
if (fileExists2(outputPath)) {
|
|
2267
4202
|
error(chalk8.red(`Cannot create ${fileName}; file already exists.`));
|
|
2268
4203
|
process.exitCode = 1;
|
|
@@ -2270,20 +4205,20 @@ function registerInitCommand(program, dependencies = {}) {
|
|
|
2270
4205
|
}
|
|
2271
4206
|
const sym = getSymbols(supportsUnicode());
|
|
2272
4207
|
const template = buildConfigTemplate();
|
|
2273
|
-
|
|
4208
|
+
writeFileSync4(outputPath, template, { encoding: "utf8" });
|
|
2274
4209
|
log(colors.success(`${sym.success} Created ${fileName}`));
|
|
2275
4210
|
if (!options.skipAction) {
|
|
2276
|
-
const workflowDir =
|
|
2277
|
-
const workflowPath =
|
|
4211
|
+
const workflowDir = path9.join(cwd, ".github", "workflows");
|
|
4212
|
+
const workflowPath = path9.join(workflowDir, "doccov.yml");
|
|
2278
4213
|
if (!fileExists2(workflowPath)) {
|
|
2279
|
-
|
|
2280
|
-
|
|
4214
|
+
mkdirSync4(workflowDir, { recursive: true });
|
|
4215
|
+
writeFileSync4(workflowPath, buildWorkflowTemplate(), { encoding: "utf8" });
|
|
2281
4216
|
log(colors.success(`${sym.success} Created .github/workflows/doccov.yml`));
|
|
2282
4217
|
} else {
|
|
2283
4218
|
log(colors.warning(`${sym.bullet} Skipped .github/workflows/doccov.yml (already exists)`));
|
|
2284
4219
|
}
|
|
2285
4220
|
}
|
|
2286
|
-
const repoInfo = detectRepoInfo(cwd, fileExists2,
|
|
4221
|
+
const repoInfo = detectRepoInfo(cwd, fileExists2, readFileSync5);
|
|
2287
4222
|
log("");
|
|
2288
4223
|
log(chalk8.bold("Add this badge to your README:"));
|
|
2289
4224
|
log("");
|
|
@@ -2298,11 +4233,11 @@ function registerInitCommand(program, dependencies = {}) {
|
|
|
2298
4233
|
});
|
|
2299
4234
|
}
|
|
2300
4235
|
var findExistingConfig = (cwd, fileExists2) => {
|
|
2301
|
-
let current =
|
|
2302
|
-
const { root } =
|
|
4236
|
+
let current = path9.resolve(cwd);
|
|
4237
|
+
const { root } = path9.parse(current);
|
|
2303
4238
|
while (true) {
|
|
2304
4239
|
for (const candidate of DOCCOV_CONFIG_FILENAMES) {
|
|
2305
|
-
const candidatePath =
|
|
4240
|
+
const candidatePath = path9.join(current, candidate);
|
|
2306
4241
|
if (fileExists2(candidatePath)) {
|
|
2307
4242
|
return candidatePath;
|
|
2308
4243
|
}
|
|
@@ -2310,17 +4245,17 @@ var findExistingConfig = (cwd, fileExists2) => {
|
|
|
2310
4245
|
if (current === root) {
|
|
2311
4246
|
break;
|
|
2312
4247
|
}
|
|
2313
|
-
current =
|
|
4248
|
+
current = path9.dirname(current);
|
|
2314
4249
|
}
|
|
2315
4250
|
return null;
|
|
2316
4251
|
};
|
|
2317
|
-
var detectPackageType = (cwd, fileExists2,
|
|
4252
|
+
var detectPackageType = (cwd, fileExists2, readFileSync5) => {
|
|
2318
4253
|
const packageJsonPath = findNearestPackageJson(cwd, fileExists2);
|
|
2319
4254
|
if (!packageJsonPath) {
|
|
2320
4255
|
return;
|
|
2321
4256
|
}
|
|
2322
4257
|
try {
|
|
2323
|
-
const raw =
|
|
4258
|
+
const raw = readFileSync5(packageJsonPath, "utf8");
|
|
2324
4259
|
const parsed = JSON.parse(raw);
|
|
2325
4260
|
if (parsed.type === "module") {
|
|
2326
4261
|
return "module";
|
|
@@ -2332,17 +4267,17 @@ var detectPackageType = (cwd, fileExists2, readFileSync4) => {
|
|
|
2332
4267
|
return;
|
|
2333
4268
|
};
|
|
2334
4269
|
var findNearestPackageJson = (cwd, fileExists2) => {
|
|
2335
|
-
let current =
|
|
2336
|
-
const { root } =
|
|
4270
|
+
let current = path9.resolve(cwd);
|
|
4271
|
+
const { root } = path9.parse(current);
|
|
2337
4272
|
while (true) {
|
|
2338
|
-
const candidate =
|
|
4273
|
+
const candidate = path9.join(current, "package.json");
|
|
2339
4274
|
if (fileExists2(candidate)) {
|
|
2340
4275
|
return candidate;
|
|
2341
4276
|
}
|
|
2342
4277
|
if (current === root) {
|
|
2343
4278
|
break;
|
|
2344
4279
|
}
|
|
2345
|
-
current =
|
|
4280
|
+
current = path9.dirname(current);
|
|
2346
4281
|
}
|
|
2347
4282
|
return null;
|
|
2348
4283
|
};
|
|
@@ -2384,11 +4319,11 @@ jobs:
|
|
|
2384
4319
|
comment-on-pr: true
|
|
2385
4320
|
`;
|
|
2386
4321
|
};
|
|
2387
|
-
var detectRepoInfo = (cwd, fileExists2,
|
|
4322
|
+
var detectRepoInfo = (cwd, fileExists2, readFileSync5) => {
|
|
2388
4323
|
const packageJsonPath = findNearestPackageJson(cwd, fileExists2);
|
|
2389
4324
|
if (packageJsonPath) {
|
|
2390
4325
|
try {
|
|
2391
|
-
const raw =
|
|
4326
|
+
const raw = readFileSync5(packageJsonPath, "utf8");
|
|
2392
4327
|
const parsed = JSON.parse(raw);
|
|
2393
4328
|
let repoUrl;
|
|
2394
4329
|
if (typeof parsed.repository === "string") {
|
|
@@ -2397,20 +4332,20 @@ var detectRepoInfo = (cwd, fileExists2, readFileSync4) => {
|
|
|
2397
4332
|
repoUrl = parsed.repository.url;
|
|
2398
4333
|
}
|
|
2399
4334
|
if (repoUrl) {
|
|
2400
|
-
const
|
|
2401
|
-
if (
|
|
2402
|
-
return { owner:
|
|
4335
|
+
const match2 = repoUrl.match(/github\.com[/:]([^/]+)\/([^/.]+)/);
|
|
4336
|
+
if (match2) {
|
|
4337
|
+
return { owner: match2[1], repo: match2[2] };
|
|
2403
4338
|
}
|
|
2404
4339
|
}
|
|
2405
4340
|
} catch {}
|
|
2406
4341
|
}
|
|
2407
|
-
const gitConfigPath =
|
|
4342
|
+
const gitConfigPath = path9.join(cwd, ".git", "config");
|
|
2408
4343
|
if (fileExists2(gitConfigPath)) {
|
|
2409
4344
|
try {
|
|
2410
|
-
const config =
|
|
2411
|
-
const
|
|
2412
|
-
if (
|
|
2413
|
-
return { owner:
|
|
4345
|
+
const config = readFileSync5(gitConfigPath, "utf8");
|
|
4346
|
+
const match2 = config.match(/url\s*=\s*.*github\.com[/:]([^/]+)\/([^/.]+)/);
|
|
4347
|
+
if (match2) {
|
|
4348
|
+
return { owner: match2[1], repo: match2[2].replace(/\.git$/, "") };
|
|
2414
4349
|
}
|
|
2415
4350
|
} catch {}
|
|
2416
4351
|
}
|
|
@@ -2418,8 +4353,8 @@ var detectRepoInfo = (cwd, fileExists2, readFileSync4) => {
|
|
|
2418
4353
|
};
|
|
2419
4354
|
|
|
2420
4355
|
// src/commands/spec.ts
|
|
2421
|
-
import * as
|
|
2422
|
-
import * as
|
|
4356
|
+
import * as fs7 from "node:fs";
|
|
4357
|
+
import * as path10 from "node:path";
|
|
2423
4358
|
import {
|
|
2424
4359
|
buildDocCovSpec as buildDocCovSpec2,
|
|
2425
4360
|
DocCov as DocCov2,
|
|
@@ -2436,7 +4371,7 @@ import {
|
|
|
2436
4371
|
import chalk9 from "chalk";
|
|
2437
4372
|
var defaultDependencies4 = {
|
|
2438
4373
|
createDocCov: (options) => new DocCov2(options),
|
|
2439
|
-
writeFileSync:
|
|
4374
|
+
writeFileSync: fs7.writeFileSync,
|
|
2440
4375
|
log: console.log,
|
|
2441
4376
|
error: console.error
|
|
2442
4377
|
};
|
|
@@ -2445,13 +4380,13 @@ function getArrayLength(value) {
|
|
|
2445
4380
|
}
|
|
2446
4381
|
function formatDiagnosticOutput(prefix2, diagnostic, baseDir) {
|
|
2447
4382
|
const location = diagnostic.location;
|
|
2448
|
-
const relativePath = location?.file ?
|
|
4383
|
+
const relativePath = location?.file ? path10.relative(baseDir, location.file) || location.file : undefined;
|
|
2449
4384
|
const locationText = location && relativePath ? chalk9.gray(`${relativePath}:${location.line ?? 1}:${location.column ?? 1}`) : null;
|
|
2450
4385
|
const locationPrefix = locationText ? `${locationText} ` : "";
|
|
2451
4386
|
return `${prefix2} ${locationPrefix}${diagnostic.message}`;
|
|
2452
4387
|
}
|
|
2453
4388
|
function registerSpecCommand(program, dependencies = {}) {
|
|
2454
|
-
const { createDocCov, writeFileSync:
|
|
4389
|
+
const { createDocCov, writeFileSync: writeFileSync5, log, error } = {
|
|
2455
4390
|
...defaultDependencies4,
|
|
2456
4391
|
...dependencies
|
|
2457
4392
|
};
|
|
@@ -2528,24 +4463,38 @@ function registerSpecCommand(program, dependencies = {}) {
|
|
|
2528
4463
|
}
|
|
2529
4464
|
}
|
|
2530
4465
|
const format = options.format ?? "json";
|
|
2531
|
-
|
|
2532
|
-
|
|
4466
|
+
let packageName;
|
|
4467
|
+
if (resolved.packageInfo) {
|
|
4468
|
+
packageName = resolved.packageInfo.name;
|
|
4469
|
+
} else {
|
|
4470
|
+
const pkgJsonPath = path10.join(targetDir, "package.json");
|
|
4471
|
+
try {
|
|
4472
|
+
const pkgJson = JSON.parse(fs7.readFileSync(pkgJsonPath, "utf-8"));
|
|
4473
|
+
packageName = pkgJson.name ?? "unknown";
|
|
4474
|
+
} catch {
|
|
4475
|
+
packageName = "unknown";
|
|
4476
|
+
}
|
|
4477
|
+
}
|
|
4478
|
+
const baseOutputDir = path10.resolve(options.cwd, options.output);
|
|
4479
|
+
const outputDir = path10.join(baseOutputDir, packageName);
|
|
4480
|
+
fs7.mkdirSync(outputDir, { recursive: true });
|
|
4481
|
+
const displayPath = `${options.output}/${packageName}`;
|
|
2533
4482
|
if (format === "api-surface") {
|
|
2534
4483
|
const apiSurface = renderApiSurface(normalized);
|
|
2535
|
-
const apiSurfacePath =
|
|
2536
|
-
|
|
2537
|
-
spin.success(`Generated ${
|
|
4484
|
+
const apiSurfacePath = path10.join(outputDir, "api-surface.txt");
|
|
4485
|
+
writeFileSync5(apiSurfacePath, apiSurface);
|
|
4486
|
+
spin.success(`Generated ${displayPath}/ (API surface)`);
|
|
2538
4487
|
} else {
|
|
2539
|
-
const openpkgPath =
|
|
2540
|
-
|
|
4488
|
+
const openpkgPath = path10.join(outputDir, "openpkg.json");
|
|
4489
|
+
writeFileSync5(openpkgPath, JSON.stringify(normalized, null, 2));
|
|
2541
4490
|
if (doccovSpec) {
|
|
2542
|
-
const doccovPath =
|
|
2543
|
-
|
|
2544
|
-
spin.success(`Generated ${
|
|
4491
|
+
const doccovPath = path10.join(outputDir, "doccov.json");
|
|
4492
|
+
writeFileSync5(doccovPath, JSON.stringify(doccovSpec, null, 2));
|
|
4493
|
+
spin.success(`Generated ${displayPath}/`);
|
|
2545
4494
|
log(chalk9.gray(` openpkg.json: ${getArrayLength(normalized.exports)} exports`));
|
|
2546
4495
|
log(chalk9.gray(` doccov.json: ${doccovSpec.summary.score}% coverage, ${doccovSpec.summary.drift.total} drift issues`));
|
|
2547
4496
|
} else {
|
|
2548
|
-
spin.success(`Generated ${
|
|
4497
|
+
spin.success(`Generated ${displayPath}/openpkg.json`);
|
|
2549
4498
|
log(chalk9.gray(` ${getArrayLength(normalized.exports)} exports`));
|
|
2550
4499
|
log(chalk9.gray(` ${getArrayLength(normalized.types)} types`));
|
|
2551
4500
|
}
|
|
@@ -2621,8 +4570,8 @@ function registerSpecCommand(program, dependencies = {}) {
|
|
|
2621
4570
|
}
|
|
2622
4571
|
|
|
2623
4572
|
// src/commands/trends.ts
|
|
2624
|
-
import * as
|
|
2625
|
-
import * as
|
|
4573
|
+
import * as fs8 from "node:fs";
|
|
4574
|
+
import * as path11 from "node:path";
|
|
2626
4575
|
import {
|
|
2627
4576
|
formatDelta as formatDelta2,
|
|
2628
4577
|
getExtendedTrend,
|
|
@@ -2672,7 +4621,7 @@ function formatVelocity(velocity) {
|
|
|
2672
4621
|
}
|
|
2673
4622
|
function registerTrendsCommand(program) {
|
|
2674
4623
|
program.command("trends").description("Show coverage trends over time").option("--cwd <dir>", "Working directory", process.cwd()).option("-n, --limit <count>", "Number of snapshots to show", "10").option("--prune <count>", "Prune history to keep only N snapshots").option("--record", "Record current coverage to history").option("--json", "Output as JSON").option("--extended", "Show extended trend analysis (velocity, projections)").option("--weekly", "Show weekly summary breakdown").action(async (options) => {
|
|
2675
|
-
const cwd =
|
|
4624
|
+
const cwd = path11.resolve(options.cwd);
|
|
2676
4625
|
if (options.prune) {
|
|
2677
4626
|
const keepCount = parseInt(options.prune, 10);
|
|
2678
4627
|
if (!Number.isNaN(keepCount)) {
|
|
@@ -2682,14 +4631,14 @@ function registerTrendsCommand(program) {
|
|
|
2682
4631
|
return;
|
|
2683
4632
|
}
|
|
2684
4633
|
if (options.record) {
|
|
2685
|
-
const specPath =
|
|
2686
|
-
if (!
|
|
4634
|
+
const specPath = path11.resolve(cwd, "openpkg.json");
|
|
4635
|
+
if (!fs8.existsSync(specPath)) {
|
|
2687
4636
|
console.error(chalk10.red("No openpkg.json found. Run `doccov spec` first to generate a spec."));
|
|
2688
4637
|
process.exit(1);
|
|
2689
4638
|
}
|
|
2690
4639
|
const spin = spinner("Recording coverage snapshot");
|
|
2691
4640
|
try {
|
|
2692
|
-
const specContent =
|
|
4641
|
+
const specContent = fs8.readFileSync(specPath, "utf-8");
|
|
2693
4642
|
const spec = JSON.parse(specContent);
|
|
2694
4643
|
const trend = getTrend(spec, cwd);
|
|
2695
4644
|
saveSnapshot(trend.current, cwd);
|
|
@@ -2740,10 +4689,10 @@ function registerTrendsCommand(program) {
|
|
|
2740
4689
|
console.log("");
|
|
2741
4690
|
}
|
|
2742
4691
|
if (options.extended) {
|
|
2743
|
-
const specPath =
|
|
2744
|
-
if (
|
|
4692
|
+
const specPath = path11.resolve(cwd, "openpkg.json");
|
|
4693
|
+
if (fs8.existsSync(specPath)) {
|
|
2745
4694
|
try {
|
|
2746
|
-
const specContent =
|
|
4695
|
+
const specContent = fs8.readFileSync(specPath, "utf-8");
|
|
2747
4696
|
const spec = JSON.parse(specContent);
|
|
2748
4697
|
const extended = getExtendedTrend(spec, cwd);
|
|
2749
4698
|
console.log(chalk10.bold("Extended Analysis"));
|
|
@@ -2801,8 +4750,8 @@ function registerTrendsCommand(program) {
|
|
|
2801
4750
|
|
|
2802
4751
|
// src/cli.ts
|
|
2803
4752
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
2804
|
-
var __dirname2 =
|
|
2805
|
-
var packageJson = JSON.parse(
|
|
4753
|
+
var __dirname2 = path12.dirname(__filename2);
|
|
4754
|
+
var packageJson = JSON.parse(readFileSync7(path12.join(__dirname2, "../package.json"), "utf-8"));
|
|
2806
4755
|
var program = new Command;
|
|
2807
4756
|
program.name("doccov").description("DocCov - Documentation coverage and drift detection for TypeScript").version(packageJson.version);
|
|
2808
4757
|
registerCheckCommand(program);
|