@doccov/cli 0.28.1 → 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 +2047 -89
- 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
|
|
|
@@ -1352,7 +1352,7 @@ function displayHealthTree(health, log) {
|
|
|
1352
1352
|
const completenessLabel = missingTotal > 0 ? `${health.completeness.score}% (${missingTotal} missing docs)` : `${health.completeness.score}%`;
|
|
1353
1353
|
const completenessColor = getHealthColor(getHealthStatus(health.completeness.score));
|
|
1354
1354
|
log(`${tree.branch} ${colors.muted("completeness")} ${completenessColor(completenessLabel)}`);
|
|
1355
|
-
const accuracyLabel = health.accuracy.
|
|
1355
|
+
const accuracyLabel = health.accuracy.fixable > 0 ? `${health.accuracy.score}% (${health.accuracy.fixable} fixable with --fix)` : `${health.accuracy.score}%`;
|
|
1356
1356
|
const accuracyColor = getHealthColor(getHealthStatus(health.accuracy.score));
|
|
1357
1357
|
const lastBranch = !health.examples ? tree.corner : tree.branch;
|
|
1358
1358
|
log(`${lastBranch} ${colors.muted("accuracy")} ${accuracyColor(accuracyLabel)}`);
|
|
@@ -1401,6 +1401,7 @@ function displayTextOutput(options, deps) {
|
|
|
1401
1401
|
warnBelowApiSurface,
|
|
1402
1402
|
driftExports,
|
|
1403
1403
|
typecheckErrors,
|
|
1404
|
+
runtimeErrors,
|
|
1404
1405
|
staleRefs,
|
|
1405
1406
|
specWarnings,
|
|
1406
1407
|
specInfos,
|
|
@@ -1420,6 +1421,7 @@ function displayTextOutput(options, deps) {
|
|
|
1420
1421
|
const apiSurfaceFailed = minApiSurface !== undefined && apiSurfaceScore < minApiSurface;
|
|
1421
1422
|
const apiSurfaceWarn = warnBelowApiSurface !== undefined && apiSurfaceScore < warnBelowApiSurface && !apiSurfaceFailed;
|
|
1422
1423
|
const hasTypecheckErrors = typecheckErrors.length > 0;
|
|
1424
|
+
const hasRuntimeErrors = runtimeErrors > 0;
|
|
1423
1425
|
if (specWarnings.length > 0 || specInfos.length > 0) {
|
|
1424
1426
|
log("");
|
|
1425
1427
|
for (const diag of specWarnings) {
|
|
@@ -1514,7 +1516,7 @@ function displayTextOutput(options, deps) {
|
|
|
1514
1516
|
}
|
|
1515
1517
|
}
|
|
1516
1518
|
log("");
|
|
1517
|
-
const failed = healthFailed || apiSurfaceFailed || hasTypecheckErrors || hasStaleRefs;
|
|
1519
|
+
const failed = healthFailed || apiSurfaceFailed || hasTypecheckErrors || hasRuntimeErrors || hasStaleRefs;
|
|
1518
1520
|
if (!failed) {
|
|
1519
1521
|
const thresholdParts = [];
|
|
1520
1522
|
thresholdParts.push(`health ${healthScore}% ≥ ${minHealth}%`);
|
|
@@ -1539,6 +1541,9 @@ function displayTextOutput(options, deps) {
|
|
|
1539
1541
|
if (hasTypecheckErrors) {
|
|
1540
1542
|
log(colors.error(`${sym.error} ${typecheckErrors.length} example type errors`));
|
|
1541
1543
|
}
|
|
1544
|
+
if (hasRuntimeErrors) {
|
|
1545
|
+
log(colors.error(`${sym.error} ${runtimeErrors} example runtime errors`));
|
|
1546
|
+
}
|
|
1542
1547
|
if (hasStaleRefs) {
|
|
1543
1548
|
log(colors.error(`${sym.error} ${staleRefs.length} stale references in docs`));
|
|
1544
1549
|
}
|
|
@@ -1556,6 +1561,7 @@ function handleNonTextOutput(options, deps) {
|
|
|
1556
1561
|
minHealth,
|
|
1557
1562
|
minApiSurface,
|
|
1558
1563
|
typecheckErrors,
|
|
1564
|
+
runtimeErrors,
|
|
1559
1565
|
limit,
|
|
1560
1566
|
stdout,
|
|
1561
1567
|
outputPath,
|
|
@@ -1592,7 +1598,8 @@ function handleNonTextOutput(options, deps) {
|
|
|
1592
1598
|
const apiSurfaceScore = doccov.apiSurface?.completeness ?? 100;
|
|
1593
1599
|
const apiSurfaceFailed = minApiSurface !== undefined && apiSurfaceScore < minApiSurface;
|
|
1594
1600
|
const hasTypecheckErrors = typecheckErrors.length > 0;
|
|
1595
|
-
|
|
1601
|
+
const hasRuntimeErrors = runtimeErrors > 0;
|
|
1602
|
+
return !(healthFailed || apiSurfaceFailed || hasTypecheckErrors || hasRuntimeErrors);
|
|
1596
1603
|
}
|
|
1597
1604
|
function displayApiSurfaceOutput(doccov, deps) {
|
|
1598
1605
|
const { log } = deps;
|
|
@@ -1643,6 +1650,1897 @@ function displayApiSurfaceOutput(doccov, deps) {
|
|
|
1643
1650
|
|
|
1644
1651
|
// src/commands/check/validation.ts
|
|
1645
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
|
|
1646
3544
|
async function runExampleValidation(openpkg, options) {
|
|
1647
3545
|
const { validations, targetDir, timeout = 5000, installTimeout = 60000 } = options;
|
|
1648
3546
|
const typecheckErrors = [];
|
|
@@ -1675,18 +3573,62 @@ async function runExampleValidation(openpkg, options) {
|
|
|
1675
3573
|
}
|
|
1676
3574
|
return { result, typecheckErrors, runtimeDrifts };
|
|
1677
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
|
+
}
|
|
1678
3589
|
async function validateMarkdownDocs(options) {
|
|
1679
|
-
const { docsPatterns, targetDir, exportNames } = options;
|
|
3590
|
+
const { docsPatterns, targetDir, exportNames, useCache = true, fetchTimeout, cacheTtl } = options;
|
|
1680
3591
|
const staleRefs = [];
|
|
1681
3592
|
if (docsPatterns.length === 0) {
|
|
1682
3593
|
return staleRefs;
|
|
1683
3594
|
}
|
|
1684
|
-
const
|
|
1685
|
-
|
|
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) {
|
|
1686
3628
|
return staleRefs;
|
|
1687
3629
|
}
|
|
1688
3630
|
const exportSet = new Set(exportNames);
|
|
1689
|
-
for (const mdFile of
|
|
3631
|
+
for (const mdFile of allDocs) {
|
|
1690
3632
|
for (const block of mdFile.codeBlocks) {
|
|
1691
3633
|
const codeLines = block.code.split(`
|
|
1692
3634
|
`);
|
|
@@ -1842,6 +3784,7 @@ function registerCheckCommand(program, dependencies = {}) {
|
|
|
1842
3784
|
minHealth,
|
|
1843
3785
|
minApiSurface,
|
|
1844
3786
|
typecheckErrors,
|
|
3787
|
+
runtimeErrors: runtimeDrifts.length,
|
|
1845
3788
|
limit: parseInt(options.limit, 10) || 20,
|
|
1846
3789
|
stdout: options.stdout,
|
|
1847
3790
|
outputPath: options.output,
|
|
@@ -1861,6 +3804,7 @@ function registerCheckCommand(program, dependencies = {}) {
|
|
|
1861
3804
|
warnBelowApiSurface,
|
|
1862
3805
|
driftExports,
|
|
1863
3806
|
typecheckErrors,
|
|
3807
|
+
runtimeErrors: runtimeDrifts.length,
|
|
1864
3808
|
staleRefs,
|
|
1865
3809
|
specWarnings,
|
|
1866
3810
|
specInfos,
|
|
@@ -1876,8 +3820,8 @@ function registerCheckCommand(program, dependencies = {}) {
|
|
|
1876
3820
|
});
|
|
1877
3821
|
}
|
|
1878
3822
|
// src/commands/diff.ts
|
|
1879
|
-
import * as
|
|
1880
|
-
import * as
|
|
3823
|
+
import * as fs5 from "node:fs";
|
|
3824
|
+
import * as path8 from "node:path";
|
|
1881
3825
|
import {
|
|
1882
3826
|
diffSpecWithDocs,
|
|
1883
3827
|
ensureSpecCoverage,
|
|
@@ -1891,7 +3835,7 @@ import { calculateNextVersion, recommendSemverBump } from "@openpkg-ts/spec";
|
|
|
1891
3835
|
import chalk7 from "chalk";
|
|
1892
3836
|
import { glob as glob2 } from "glob";
|
|
1893
3837
|
var defaultDependencies2 = {
|
|
1894
|
-
readFileSync:
|
|
3838
|
+
readFileSync: fs5.readFileSync,
|
|
1895
3839
|
log: console.log,
|
|
1896
3840
|
error: console.error
|
|
1897
3841
|
};
|
|
@@ -1910,7 +3854,7 @@ function getStrictChecks(preset) {
|
|
|
1910
3854
|
return checks;
|
|
1911
3855
|
}
|
|
1912
3856
|
function registerDiffCommand(program, dependencies = {}) {
|
|
1913
|
-
const { readFileSync:
|
|
3857
|
+
const { readFileSync: readFileSync4, log, error } = {
|
|
1914
3858
|
...defaultDependencies2,
|
|
1915
3859
|
...dependencies
|
|
1916
3860
|
};
|
|
@@ -1924,18 +3868,18 @@ function registerDiffCommand(program, dependencies = {}) {
|
|
|
1924
3868
|
` + " or: doccov diff --base main.json --head feature.json");
|
|
1925
3869
|
}
|
|
1926
3870
|
const spin = spinner("Comparing specs...");
|
|
1927
|
-
const baseSpec = loadSpec(baseFile,
|
|
1928
|
-
const headSpec = loadSpec(headFile,
|
|
3871
|
+
const baseSpec = loadSpec(baseFile, readFileSync4);
|
|
3872
|
+
const headSpec = loadSpec(headFile, readFileSync4);
|
|
1929
3873
|
const config = await loadDocCovConfig(options.cwd);
|
|
1930
3874
|
const baseHash = hashString(JSON.stringify(baseSpec));
|
|
1931
3875
|
const headHash = hashString(JSON.stringify(headSpec));
|
|
1932
3876
|
const cacheEnabled = options.cache !== false;
|
|
1933
|
-
const cachedReportPath =
|
|
3877
|
+
const cachedReportPath = path8.resolve(options.cwd, getDiffReportPath(baseHash, headHash, "json"));
|
|
1934
3878
|
let diff;
|
|
1935
3879
|
let fromCache = false;
|
|
1936
|
-
if (cacheEnabled &&
|
|
3880
|
+
if (cacheEnabled && fs5.existsSync(cachedReportPath)) {
|
|
1937
3881
|
try {
|
|
1938
|
-
const cached = JSON.parse(
|
|
3882
|
+
const cached = JSON.parse(fs5.readFileSync(cachedReportPath, "utf-8"));
|
|
1939
3883
|
diff = cached;
|
|
1940
3884
|
fromCache = true;
|
|
1941
3885
|
} catch {
|
|
@@ -1973,8 +3917,8 @@ function registerDiffCommand(program, dependencies = {}) {
|
|
|
1973
3917
|
const format = options.format ?? "text";
|
|
1974
3918
|
const limit = parseInt(options.limit, 10) || 10;
|
|
1975
3919
|
const checks = getStrictChecks(options.strict);
|
|
1976
|
-
const baseName =
|
|
1977
|
-
const headName =
|
|
3920
|
+
const baseName = path8.basename(baseFile);
|
|
3921
|
+
const headName = path8.basename(headFile);
|
|
1978
3922
|
const reportData = {
|
|
1979
3923
|
baseName,
|
|
1980
3924
|
headName,
|
|
@@ -2079,7 +4023,7 @@ async function loadMarkdownFiles2(patterns) {
|
|
|
2079
4023
|
const matches = await glob2(pattern, { nodir: true });
|
|
2080
4024
|
for (const filePath of matches) {
|
|
2081
4025
|
try {
|
|
2082
|
-
const content =
|
|
4026
|
+
const content = fs5.readFileSync(filePath, "utf-8");
|
|
2083
4027
|
files.push({ path: filePath, content });
|
|
2084
4028
|
} catch {}
|
|
2085
4029
|
}
|
|
@@ -2100,13 +4044,13 @@ async function generateDiff(baseSpec, headSpec, options, config, log) {
|
|
|
2100
4044
|
}
|
|
2101
4045
|
return diffSpecWithDocs(baseSpec, headSpec, { markdownFiles });
|
|
2102
4046
|
}
|
|
2103
|
-
function loadSpec(filePath,
|
|
2104
|
-
const resolvedPath =
|
|
2105
|
-
if (!
|
|
4047
|
+
function loadSpec(filePath, readFileSync4) {
|
|
4048
|
+
const resolvedPath = path8.resolve(filePath);
|
|
4049
|
+
if (!fs5.existsSync(resolvedPath)) {
|
|
2106
4050
|
throw new Error(`File not found: ${filePath}`);
|
|
2107
4051
|
}
|
|
2108
4052
|
try {
|
|
2109
|
-
const content =
|
|
4053
|
+
const content = readFileSync4(resolvedPath, "utf-8");
|
|
2110
4054
|
const spec = JSON.parse(content);
|
|
2111
4055
|
return ensureSpecCoverage(spec);
|
|
2112
4056
|
} catch (parseError) {
|
|
@@ -2226,34 +4170,34 @@ function printGitHubAnnotations(diff, log) {
|
|
|
2226
4170
|
}
|
|
2227
4171
|
|
|
2228
4172
|
// src/commands/init.ts
|
|
2229
|
-
import * as
|
|
2230
|
-
import * as
|
|
4173
|
+
import * as fs6 from "node:fs";
|
|
4174
|
+
import * as path9 from "node:path";
|
|
2231
4175
|
import chalk8 from "chalk";
|
|
2232
4176
|
var defaultDependencies3 = {
|
|
2233
|
-
fileExists:
|
|
2234
|
-
writeFileSync:
|
|
2235
|
-
readFileSync:
|
|
2236
|
-
mkdirSync:
|
|
4177
|
+
fileExists: fs6.existsSync,
|
|
4178
|
+
writeFileSync: fs6.writeFileSync,
|
|
4179
|
+
readFileSync: fs6.readFileSync,
|
|
4180
|
+
mkdirSync: fs6.mkdirSync,
|
|
2237
4181
|
log: console.log,
|
|
2238
4182
|
error: console.error
|
|
2239
4183
|
};
|
|
2240
4184
|
function registerInitCommand(program, dependencies = {}) {
|
|
2241
|
-
const { fileExists: fileExists2, writeFileSync:
|
|
4185
|
+
const { fileExists: fileExists2, writeFileSync: writeFileSync4, readFileSync: readFileSync5, mkdirSync: mkdirSync4, log, error } = {
|
|
2242
4186
|
...defaultDependencies3,
|
|
2243
4187
|
...dependencies
|
|
2244
4188
|
};
|
|
2245
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) => {
|
|
2246
|
-
const cwd =
|
|
4190
|
+
const cwd = path9.resolve(options.cwd);
|
|
2247
4191
|
const existing = findExistingConfig(cwd, fileExists2);
|
|
2248
4192
|
if (existing) {
|
|
2249
|
-
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.*"}.`));
|
|
2250
4194
|
process.exitCode = 1;
|
|
2251
4195
|
return;
|
|
2252
4196
|
}
|
|
2253
|
-
const packageType = detectPackageType(cwd, fileExists2,
|
|
4197
|
+
const packageType = detectPackageType(cwd, fileExists2, readFileSync5);
|
|
2254
4198
|
const targetFormat = packageType === "module" ? "ts" : "mts";
|
|
2255
4199
|
const fileName = `doccov.config.${targetFormat}`;
|
|
2256
|
-
const outputPath =
|
|
4200
|
+
const outputPath = path9.join(cwd, fileName);
|
|
2257
4201
|
if (fileExists2(outputPath)) {
|
|
2258
4202
|
error(chalk8.red(`Cannot create ${fileName}; file already exists.`));
|
|
2259
4203
|
process.exitCode = 1;
|
|
@@ -2261,20 +4205,20 @@ function registerInitCommand(program, dependencies = {}) {
|
|
|
2261
4205
|
}
|
|
2262
4206
|
const sym = getSymbols(supportsUnicode());
|
|
2263
4207
|
const template = buildConfigTemplate();
|
|
2264
|
-
|
|
4208
|
+
writeFileSync4(outputPath, template, { encoding: "utf8" });
|
|
2265
4209
|
log(colors.success(`${sym.success} Created ${fileName}`));
|
|
2266
4210
|
if (!options.skipAction) {
|
|
2267
|
-
const workflowDir =
|
|
2268
|
-
const workflowPath =
|
|
4211
|
+
const workflowDir = path9.join(cwd, ".github", "workflows");
|
|
4212
|
+
const workflowPath = path9.join(workflowDir, "doccov.yml");
|
|
2269
4213
|
if (!fileExists2(workflowPath)) {
|
|
2270
|
-
|
|
2271
|
-
|
|
4214
|
+
mkdirSync4(workflowDir, { recursive: true });
|
|
4215
|
+
writeFileSync4(workflowPath, buildWorkflowTemplate(), { encoding: "utf8" });
|
|
2272
4216
|
log(colors.success(`${sym.success} Created .github/workflows/doccov.yml`));
|
|
2273
4217
|
} else {
|
|
2274
4218
|
log(colors.warning(`${sym.bullet} Skipped .github/workflows/doccov.yml (already exists)`));
|
|
2275
4219
|
}
|
|
2276
4220
|
}
|
|
2277
|
-
const repoInfo = detectRepoInfo(cwd, fileExists2,
|
|
4221
|
+
const repoInfo = detectRepoInfo(cwd, fileExists2, readFileSync5);
|
|
2278
4222
|
log("");
|
|
2279
4223
|
log(chalk8.bold("Add this badge to your README:"));
|
|
2280
4224
|
log("");
|
|
@@ -2289,11 +4233,11 @@ function registerInitCommand(program, dependencies = {}) {
|
|
|
2289
4233
|
});
|
|
2290
4234
|
}
|
|
2291
4235
|
var findExistingConfig = (cwd, fileExists2) => {
|
|
2292
|
-
let current =
|
|
2293
|
-
const { root } =
|
|
4236
|
+
let current = path9.resolve(cwd);
|
|
4237
|
+
const { root } = path9.parse(current);
|
|
2294
4238
|
while (true) {
|
|
2295
4239
|
for (const candidate of DOCCOV_CONFIG_FILENAMES) {
|
|
2296
|
-
const candidatePath =
|
|
4240
|
+
const candidatePath = path9.join(current, candidate);
|
|
2297
4241
|
if (fileExists2(candidatePath)) {
|
|
2298
4242
|
return candidatePath;
|
|
2299
4243
|
}
|
|
@@ -2301,17 +4245,17 @@ var findExistingConfig = (cwd, fileExists2) => {
|
|
|
2301
4245
|
if (current === root) {
|
|
2302
4246
|
break;
|
|
2303
4247
|
}
|
|
2304
|
-
current =
|
|
4248
|
+
current = path9.dirname(current);
|
|
2305
4249
|
}
|
|
2306
4250
|
return null;
|
|
2307
4251
|
};
|
|
2308
|
-
var detectPackageType = (cwd, fileExists2,
|
|
4252
|
+
var detectPackageType = (cwd, fileExists2, readFileSync5) => {
|
|
2309
4253
|
const packageJsonPath = findNearestPackageJson(cwd, fileExists2);
|
|
2310
4254
|
if (!packageJsonPath) {
|
|
2311
4255
|
return;
|
|
2312
4256
|
}
|
|
2313
4257
|
try {
|
|
2314
|
-
const raw =
|
|
4258
|
+
const raw = readFileSync5(packageJsonPath, "utf8");
|
|
2315
4259
|
const parsed = JSON.parse(raw);
|
|
2316
4260
|
if (parsed.type === "module") {
|
|
2317
4261
|
return "module";
|
|
@@ -2323,17 +4267,17 @@ var detectPackageType = (cwd, fileExists2, readFileSync4) => {
|
|
|
2323
4267
|
return;
|
|
2324
4268
|
};
|
|
2325
4269
|
var findNearestPackageJson = (cwd, fileExists2) => {
|
|
2326
|
-
let current =
|
|
2327
|
-
const { root } =
|
|
4270
|
+
let current = path9.resolve(cwd);
|
|
4271
|
+
const { root } = path9.parse(current);
|
|
2328
4272
|
while (true) {
|
|
2329
|
-
const candidate =
|
|
4273
|
+
const candidate = path9.join(current, "package.json");
|
|
2330
4274
|
if (fileExists2(candidate)) {
|
|
2331
4275
|
return candidate;
|
|
2332
4276
|
}
|
|
2333
4277
|
if (current === root) {
|
|
2334
4278
|
break;
|
|
2335
4279
|
}
|
|
2336
|
-
current =
|
|
4280
|
+
current = path9.dirname(current);
|
|
2337
4281
|
}
|
|
2338
4282
|
return null;
|
|
2339
4283
|
};
|
|
@@ -2375,11 +4319,11 @@ jobs:
|
|
|
2375
4319
|
comment-on-pr: true
|
|
2376
4320
|
`;
|
|
2377
4321
|
};
|
|
2378
|
-
var detectRepoInfo = (cwd, fileExists2,
|
|
4322
|
+
var detectRepoInfo = (cwd, fileExists2, readFileSync5) => {
|
|
2379
4323
|
const packageJsonPath = findNearestPackageJson(cwd, fileExists2);
|
|
2380
4324
|
if (packageJsonPath) {
|
|
2381
4325
|
try {
|
|
2382
|
-
const raw =
|
|
4326
|
+
const raw = readFileSync5(packageJsonPath, "utf8");
|
|
2383
4327
|
const parsed = JSON.parse(raw);
|
|
2384
4328
|
let repoUrl;
|
|
2385
4329
|
if (typeof parsed.repository === "string") {
|
|
@@ -2388,20 +4332,20 @@ var detectRepoInfo = (cwd, fileExists2, readFileSync4) => {
|
|
|
2388
4332
|
repoUrl = parsed.repository.url;
|
|
2389
4333
|
}
|
|
2390
4334
|
if (repoUrl) {
|
|
2391
|
-
const
|
|
2392
|
-
if (
|
|
2393
|
-
return { owner:
|
|
4335
|
+
const match2 = repoUrl.match(/github\.com[/:]([^/]+)\/([^/.]+)/);
|
|
4336
|
+
if (match2) {
|
|
4337
|
+
return { owner: match2[1], repo: match2[2] };
|
|
2394
4338
|
}
|
|
2395
4339
|
}
|
|
2396
4340
|
} catch {}
|
|
2397
4341
|
}
|
|
2398
|
-
const gitConfigPath =
|
|
4342
|
+
const gitConfigPath = path9.join(cwd, ".git", "config");
|
|
2399
4343
|
if (fileExists2(gitConfigPath)) {
|
|
2400
4344
|
try {
|
|
2401
|
-
const config =
|
|
2402
|
-
const
|
|
2403
|
-
if (
|
|
2404
|
-
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$/, "") };
|
|
2405
4349
|
}
|
|
2406
4350
|
} catch {}
|
|
2407
4351
|
}
|
|
@@ -2409,8 +4353,8 @@ var detectRepoInfo = (cwd, fileExists2, readFileSync4) => {
|
|
|
2409
4353
|
};
|
|
2410
4354
|
|
|
2411
4355
|
// src/commands/spec.ts
|
|
2412
|
-
import * as
|
|
2413
|
-
import * as
|
|
4356
|
+
import * as fs7 from "node:fs";
|
|
4357
|
+
import * as path10 from "node:path";
|
|
2414
4358
|
import {
|
|
2415
4359
|
buildDocCovSpec as buildDocCovSpec2,
|
|
2416
4360
|
DocCov as DocCov2,
|
|
@@ -2427,7 +4371,7 @@ import {
|
|
|
2427
4371
|
import chalk9 from "chalk";
|
|
2428
4372
|
var defaultDependencies4 = {
|
|
2429
4373
|
createDocCov: (options) => new DocCov2(options),
|
|
2430
|
-
writeFileSync:
|
|
4374
|
+
writeFileSync: fs7.writeFileSync,
|
|
2431
4375
|
log: console.log,
|
|
2432
4376
|
error: console.error
|
|
2433
4377
|
};
|
|
@@ -2436,13 +4380,13 @@ function getArrayLength(value) {
|
|
|
2436
4380
|
}
|
|
2437
4381
|
function formatDiagnosticOutput(prefix2, diagnostic, baseDir) {
|
|
2438
4382
|
const location = diagnostic.location;
|
|
2439
|
-
const relativePath = location?.file ?
|
|
4383
|
+
const relativePath = location?.file ? path10.relative(baseDir, location.file) || location.file : undefined;
|
|
2440
4384
|
const locationText = location && relativePath ? chalk9.gray(`${relativePath}:${location.line ?? 1}:${location.column ?? 1}`) : null;
|
|
2441
4385
|
const locationPrefix = locationText ? `${locationText} ` : "";
|
|
2442
4386
|
return `${prefix2} ${locationPrefix}${diagnostic.message}`;
|
|
2443
4387
|
}
|
|
2444
4388
|
function registerSpecCommand(program, dependencies = {}) {
|
|
2445
|
-
const { createDocCov, writeFileSync:
|
|
4389
|
+
const { createDocCov, writeFileSync: writeFileSync5, log, error } = {
|
|
2446
4390
|
...defaultDependencies4,
|
|
2447
4391
|
...dependencies
|
|
2448
4392
|
};
|
|
@@ -2519,24 +4463,38 @@ function registerSpecCommand(program, dependencies = {}) {
|
|
|
2519
4463
|
}
|
|
2520
4464
|
}
|
|
2521
4465
|
const format = options.format ?? "json";
|
|
2522
|
-
|
|
2523
|
-
|
|
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}`;
|
|
2524
4482
|
if (format === "api-surface") {
|
|
2525
4483
|
const apiSurface = renderApiSurface(normalized);
|
|
2526
|
-
const apiSurfacePath =
|
|
2527
|
-
|
|
2528
|
-
spin.success(`Generated ${
|
|
4484
|
+
const apiSurfacePath = path10.join(outputDir, "api-surface.txt");
|
|
4485
|
+
writeFileSync5(apiSurfacePath, apiSurface);
|
|
4486
|
+
spin.success(`Generated ${displayPath}/ (API surface)`);
|
|
2529
4487
|
} else {
|
|
2530
|
-
const openpkgPath =
|
|
2531
|
-
|
|
4488
|
+
const openpkgPath = path10.join(outputDir, "openpkg.json");
|
|
4489
|
+
writeFileSync5(openpkgPath, JSON.stringify(normalized, null, 2));
|
|
2532
4490
|
if (doccovSpec) {
|
|
2533
|
-
const doccovPath =
|
|
2534
|
-
|
|
2535
|
-
spin.success(`Generated ${
|
|
4491
|
+
const doccovPath = path10.join(outputDir, "doccov.json");
|
|
4492
|
+
writeFileSync5(doccovPath, JSON.stringify(doccovSpec, null, 2));
|
|
4493
|
+
spin.success(`Generated ${displayPath}/`);
|
|
2536
4494
|
log(chalk9.gray(` openpkg.json: ${getArrayLength(normalized.exports)} exports`));
|
|
2537
4495
|
log(chalk9.gray(` doccov.json: ${doccovSpec.summary.score}% coverage, ${doccovSpec.summary.drift.total} drift issues`));
|
|
2538
4496
|
} else {
|
|
2539
|
-
spin.success(`Generated ${
|
|
4497
|
+
spin.success(`Generated ${displayPath}/openpkg.json`);
|
|
2540
4498
|
log(chalk9.gray(` ${getArrayLength(normalized.exports)} exports`));
|
|
2541
4499
|
log(chalk9.gray(` ${getArrayLength(normalized.types)} types`));
|
|
2542
4500
|
}
|
|
@@ -2612,8 +4570,8 @@ function registerSpecCommand(program, dependencies = {}) {
|
|
|
2612
4570
|
}
|
|
2613
4571
|
|
|
2614
4572
|
// src/commands/trends.ts
|
|
2615
|
-
import * as
|
|
2616
|
-
import * as
|
|
4573
|
+
import * as fs8 from "node:fs";
|
|
4574
|
+
import * as path11 from "node:path";
|
|
2617
4575
|
import {
|
|
2618
4576
|
formatDelta as formatDelta2,
|
|
2619
4577
|
getExtendedTrend,
|
|
@@ -2663,7 +4621,7 @@ function formatVelocity(velocity) {
|
|
|
2663
4621
|
}
|
|
2664
4622
|
function registerTrendsCommand(program) {
|
|
2665
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) => {
|
|
2666
|
-
const cwd =
|
|
4624
|
+
const cwd = path11.resolve(options.cwd);
|
|
2667
4625
|
if (options.prune) {
|
|
2668
4626
|
const keepCount = parseInt(options.prune, 10);
|
|
2669
4627
|
if (!Number.isNaN(keepCount)) {
|
|
@@ -2673,14 +4631,14 @@ function registerTrendsCommand(program) {
|
|
|
2673
4631
|
return;
|
|
2674
4632
|
}
|
|
2675
4633
|
if (options.record) {
|
|
2676
|
-
const specPath =
|
|
2677
|
-
if (!
|
|
4634
|
+
const specPath = path11.resolve(cwd, "openpkg.json");
|
|
4635
|
+
if (!fs8.existsSync(specPath)) {
|
|
2678
4636
|
console.error(chalk10.red("No openpkg.json found. Run `doccov spec` first to generate a spec."));
|
|
2679
4637
|
process.exit(1);
|
|
2680
4638
|
}
|
|
2681
4639
|
const spin = spinner("Recording coverage snapshot");
|
|
2682
4640
|
try {
|
|
2683
|
-
const specContent =
|
|
4641
|
+
const specContent = fs8.readFileSync(specPath, "utf-8");
|
|
2684
4642
|
const spec = JSON.parse(specContent);
|
|
2685
4643
|
const trend = getTrend(spec, cwd);
|
|
2686
4644
|
saveSnapshot(trend.current, cwd);
|
|
@@ -2731,10 +4689,10 @@ function registerTrendsCommand(program) {
|
|
|
2731
4689
|
console.log("");
|
|
2732
4690
|
}
|
|
2733
4691
|
if (options.extended) {
|
|
2734
|
-
const specPath =
|
|
2735
|
-
if (
|
|
4692
|
+
const specPath = path11.resolve(cwd, "openpkg.json");
|
|
4693
|
+
if (fs8.existsSync(specPath)) {
|
|
2736
4694
|
try {
|
|
2737
|
-
const specContent =
|
|
4695
|
+
const specContent = fs8.readFileSync(specPath, "utf-8");
|
|
2738
4696
|
const spec = JSON.parse(specContent);
|
|
2739
4697
|
const extended = getExtendedTrend(spec, cwd);
|
|
2740
4698
|
console.log(chalk10.bold("Extended Analysis"));
|
|
@@ -2792,8 +4750,8 @@ function registerTrendsCommand(program) {
|
|
|
2792
4750
|
|
|
2793
4751
|
// src/cli.ts
|
|
2794
4752
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
2795
|
-
var __dirname2 =
|
|
2796
|
-
var packageJson = JSON.parse(
|
|
4753
|
+
var __dirname2 = path12.dirname(__filename2);
|
|
4754
|
+
var packageJson = JSON.parse(readFileSync7(path12.join(__dirname2, "../package.json"), "utf-8"));
|
|
2797
4755
|
var program = new Command;
|
|
2798
4756
|
program.name("doccov").description("DocCov - Documentation coverage and drift detection for TypeScript").version(packageJson.version);
|
|
2799
4757
|
registerCheckCommand(program);
|