dommief 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +120 -0
- package/dist/cli.js +168 -143
- package/dist/index.js +5 -1
- package/package.json +1 -72
- package/src/build.ts +290 -0
- package/src/cache.ts +172 -0
- package/src/cli.ts +87 -0
- package/src/config.ts +258 -0
- package/src/exclude.ts +38 -0
- package/src/index.ts +17 -0
- package/src/link-check.ts +101 -0
- package/src/log.ts +10 -0
- package/src/markdown/errors.ts +28 -0
- package/src/markdown/index.ts +96 -0
- package/src/markdown/rehype-api.ts +131 -0
- package/src/markdown/rehype-code.ts +147 -0
- package/src/markdown/rehype-demo.ts +76 -0
- package/src/markdown/rehype-desc.ts +30 -0
- package/src/markdown/rehype-img.ts +33 -0
- package/src/markdown/rehype-link.ts +50 -0
- package/src/markdown/rehype-slug.ts +41 -0
- package/src/markdown/rehype-text.ts +53 -0
- package/src/markdown/remark-container.ts +76 -0
- package/src/markdown/remark-meta.ts +73 -0
- package/src/markdown/to-module.ts +35 -0
- package/src/paths.ts +38 -0
- package/src/plugins.ts +131 -0
- package/src/prefers-color.ts +28 -0
- package/src/preview.ts +127 -0
- package/src/routes.ts +232 -0
- package/src/search.ts +88 -0
- package/src/siteFeatures.ts +123 -0
- package/src/siteModule.ts +47 -0
- package/src/sitemap.ts +59 -0
- package/src/ssr-guard.ts +72 -0
- package/src/theme/index.ts +5 -0
- package/src/theme/loader.ts +151 -0
- package/src/theme/resolver.ts +115 -0
- package/src/theme/types.ts +25 -0
- package/src/theme-modules.d.ts +52 -0
- package/src/utils/path.ts +8 -0
- package/src/vite/plugin.ts +182 -0
- package/src/vite/routesPlugin.ts +39 -0
- package/src/vite/searchIndexPlugin.ts +66 -0
- package/src/vite/sitePlugin.ts +97 -0
- package/src/vite/state.ts +27 -0
package/dist/cli.js
CHANGED
|
@@ -547,7 +547,7 @@ function buildApiTable(src, filePath = "") {
|
|
|
547
547
|
try {
|
|
548
548
|
docs = parser.parse(abs) ?? [];
|
|
549
549
|
} catch {
|
|
550
|
-
|
|
550
|
+
return null;
|
|
551
551
|
}
|
|
552
552
|
const entries = (docs || []).map((d) => ({
|
|
553
553
|
displayName: d.displayName,
|
|
@@ -569,6 +569,7 @@ function rehypeApi() {
|
|
|
569
569
|
return (tree, file) => {
|
|
570
570
|
const apis = Array.isArray(file?.data?.apis) ? file.data.apis : [];
|
|
571
571
|
const watched = Array.isArray(file?.data?.apiSrcFiles) ? file.data.apiSrcFiles : [];
|
|
572
|
+
const warnings = Array.isArray(file?.data?.warnings) ? file.data.warnings : [];
|
|
572
573
|
const filePath = String(file?.path ?? "");
|
|
573
574
|
visit7(tree, "element", (node, index, parent) => {
|
|
574
575
|
if (!parent || typeof index !== "number") return;
|
|
@@ -578,6 +579,7 @@ function rehypeApi() {
|
|
|
578
579
|
const abs = resolveApiSrc(src, filePath);
|
|
579
580
|
const data = buildApiTable(src, filePath);
|
|
580
581
|
watched.push(abs);
|
|
582
|
+
if (!data?.length) warnings.push(`<API src="${src}">: ${data ? "no component found (syntax error or no exported component)" : "file missing or failed to parse"}: ${abs}`);
|
|
581
583
|
apis.push({ id: hashFileContent(src + filePath).slice(0, 8), src: abs, data });
|
|
582
584
|
parent.children[index] = {
|
|
583
585
|
type: "element",
|
|
@@ -592,6 +594,7 @@ function rehypeApi() {
|
|
|
592
594
|
if (file?.data) {
|
|
593
595
|
file.data.apis = apis;
|
|
594
596
|
file.data.apiSrcFiles = watched;
|
|
597
|
+
file.data.warnings = warnings;
|
|
595
598
|
}
|
|
596
599
|
};
|
|
597
600
|
}
|
|
@@ -1010,8 +1013,8 @@ import { cac } from "cac";
|
|
|
1010
1013
|
import { createServer } from "vite";
|
|
1011
1014
|
|
|
1012
1015
|
// src/vite/plugin.ts
|
|
1013
|
-
import
|
|
1014
|
-
import
|
|
1016
|
+
import fs10 from "fs";
|
|
1017
|
+
import path10 from "path";
|
|
1015
1018
|
|
|
1016
1019
|
// src/config.ts
|
|
1017
1020
|
init_errors();
|
|
@@ -1149,6 +1152,7 @@ function resolveConfig(userConfig, opts) {
|
|
|
1149
1152
|
srcDir: c.srcDir,
|
|
1150
1153
|
docDirs: (c.docDirs ?? [c.srcDir]).map((d) => typeof d === "string" ? { dir: d } : { dir: d.dir }),
|
|
1151
1154
|
forceKebabCase: c.forceKebabCase,
|
|
1155
|
+
preset: c.preset,
|
|
1152
1156
|
locales,
|
|
1153
1157
|
themeConfig: c.themeConfig,
|
|
1154
1158
|
theme: c.theme,
|
|
@@ -1767,15 +1771,89 @@ function createState(cwd, get) {
|
|
|
1767
1771
|
// src/vite/plugin.ts
|
|
1768
1772
|
init_path();
|
|
1769
1773
|
|
|
1774
|
+
// src/sitemap.ts
|
|
1775
|
+
import fs6 from "fs";
|
|
1776
|
+
import path6 from "path";
|
|
1777
|
+
|
|
1778
|
+
// src/exclude.ts
|
|
1779
|
+
var cache = /* @__PURE__ */ new Map();
|
|
1780
|
+
function globToRegExp(pattern) {
|
|
1781
|
+
let src = "";
|
|
1782
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
1783
|
+
const c = pattern.charAt(i);
|
|
1784
|
+
if (c === "*" && pattern.charAt(i + 1) === "*") {
|
|
1785
|
+
const slash = pattern.charAt(i + 2) === "/";
|
|
1786
|
+
src += slash ? "(?:.*/)?" : ".*";
|
|
1787
|
+
i += slash ? 2 : 1;
|
|
1788
|
+
} else if (c === "*") {
|
|
1789
|
+
src += "[^/]*";
|
|
1790
|
+
} else if (c === "?") {
|
|
1791
|
+
src += "[^/]";
|
|
1792
|
+
} else {
|
|
1793
|
+
src += c.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
return new RegExp(`^${src}$`);
|
|
1797
|
+
}
|
|
1798
|
+
function matcher(pattern) {
|
|
1799
|
+
const hit = cache.get(pattern);
|
|
1800
|
+
if (hit) return hit;
|
|
1801
|
+
const re = globToRegExp(pattern);
|
|
1802
|
+
cache.set(pattern, re);
|
|
1803
|
+
return re;
|
|
1804
|
+
}
|
|
1805
|
+
function matchExclude(path15, patterns) {
|
|
1806
|
+
return patterns.some((p) => matcher(p).test(path15));
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
// src/sitemap.ts
|
|
1810
|
+
function canonicalUrl(hostname, base, routePath) {
|
|
1811
|
+
const host = hostname.replace(/\/$/, "");
|
|
1812
|
+
const b = base.replace(/\/$/, "");
|
|
1813
|
+
const p = routePath === "/" ? "/" : routePath;
|
|
1814
|
+
return host + (b + p).replace(/\/{2,}/g, "/").replace(/^(https?:\/)([^/])/, "$1/$2");
|
|
1815
|
+
}
|
|
1816
|
+
function buildSitemap(routes, config, base) {
|
|
1817
|
+
const urls = routes.filter((r) => r.path !== "/404" && !/[:*]/.test(r.path) && !matchExclude(r.path, config.exclude)).map((r) => ` <url><loc>${escapeXml(canonicalUrl(config.hostname, base, r.path))}</loc></url>`).join("\n");
|
|
1818
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
1819
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
1820
|
+
${urls}
|
|
1821
|
+
</urlset>
|
|
1822
|
+
`;
|
|
1823
|
+
}
|
|
1824
|
+
function buildRobots(sitemapUrl) {
|
|
1825
|
+
return `User-agent: *
|
|
1826
|
+
Allow: /
|
|
1827
|
+
|
|
1828
|
+
Sitemap: ${sitemapUrl}
|
|
1829
|
+
`;
|
|
1830
|
+
}
|
|
1831
|
+
function escapeXml(s) {
|
|
1832
|
+
return s.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
1833
|
+
}
|
|
1834
|
+
function writeSitemapAndRobots(opts) {
|
|
1835
|
+
if (opts.sitemap === false) return;
|
|
1836
|
+
if (!opts.sitemap.hostname) {
|
|
1837
|
+
opts.log.warn("sitemap.hostname thi\u1EBFu \u2014 b\u1ECF qua sitemap.xml/robots.txt");
|
|
1838
|
+
return;
|
|
1839
|
+
}
|
|
1840
|
+
const xml = buildSitemap(opts.routes, opts.sitemap, opts.base);
|
|
1841
|
+
fs6.writeFileSync(path6.join(opts.outDir, "sitemap.xml"), xml);
|
|
1842
|
+
if (opts.robots) {
|
|
1843
|
+
const sitemapUrl = canonicalUrl(opts.sitemap.hostname, opts.base, "/").replace(/\/$/, "") + "/sitemap.xml";
|
|
1844
|
+
fs6.writeFileSync(path6.join(opts.outDir, "robots.txt"), buildRobots(sitemapUrl));
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1770
1848
|
// src/theme/resolver.ts
|
|
1771
1849
|
init_errors();
|
|
1772
|
-
import
|
|
1773
|
-
import
|
|
1850
|
+
import fs8 from "fs";
|
|
1851
|
+
import path8 from "path";
|
|
1774
1852
|
|
|
1775
1853
|
// src/theme/loader.ts
|
|
1776
1854
|
init_errors();
|
|
1777
|
-
import
|
|
1778
|
-
import
|
|
1855
|
+
import fs7 from "fs";
|
|
1856
|
+
import path7 from "path";
|
|
1779
1857
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1780
1858
|
var EXTS = [".tsx", ".ts", ".jsx", ".js"];
|
|
1781
1859
|
var DEFAULT_THEME = "dommief-theme-default";
|
|
@@ -1783,17 +1861,17 @@ function isComponentName(name) {
|
|
|
1783
1861
|
return /^[A-Z0-9]/.test(name);
|
|
1784
1862
|
}
|
|
1785
1863
|
function repoPackagesDir() {
|
|
1786
|
-
let cur =
|
|
1787
|
-
while (cur !==
|
|
1788
|
-
if (
|
|
1789
|
-
cur =
|
|
1864
|
+
let cur = path7.dirname(fileURLToPath2(import.meta.url));
|
|
1865
|
+
while (cur !== path7.parse(cur).root) {
|
|
1866
|
+
if (fs7.existsSync(path7.join(cur, "pnpm-workspace.yaml"))) return path7.join(cur, "packages");
|
|
1867
|
+
cur = path7.dirname(cur);
|
|
1790
1868
|
}
|
|
1791
1869
|
return null;
|
|
1792
1870
|
}
|
|
1793
1871
|
function assertInside(cwd, target, theme) {
|
|
1794
|
-
const rel =
|
|
1795
|
-
const insideCwd = rel === "" || !rel.startsWith("..") && !
|
|
1796
|
-
const insideNodeModules = target.split(
|
|
1872
|
+
const rel = path7.relative(cwd, target);
|
|
1873
|
+
const insideCwd = rel === "" || !rel.startsWith("..") && !path7.isAbsolute(rel);
|
|
1874
|
+
const insideNodeModules = target.split(path7.sep).includes("node_modules");
|
|
1797
1875
|
if (!insideCwd && !insideNodeModules) {
|
|
1798
1876
|
throw new DommiefError(`\u0111\u01B0\u1EDDng d\u1EABn theme "${theme}" tho\xE1t ra ngo\xE0i th\u01B0 m\u1EE5c d\u1EF1 \xE1n: ${target}`, { code: "theme_path_escape", file: target });
|
|
1799
1877
|
}
|
|
@@ -1802,44 +1880,44 @@ function resolveThemeRoot(cwd, theme) {
|
|
|
1802
1880
|
const isPathLike = theme.startsWith(".") || theme.startsWith("/");
|
|
1803
1881
|
let root = null;
|
|
1804
1882
|
if (isPathLike) {
|
|
1805
|
-
const candidate =
|
|
1806
|
-
if (
|
|
1883
|
+
const candidate = path7.resolve(cwd, theme);
|
|
1884
|
+
if (fs7.existsSync(candidate) && fs7.statSync(candidate).isDirectory()) root = candidate;
|
|
1807
1885
|
if (!root) throw new DommiefError(`kh\xF4ng t\xECm th\u1EA5y theme t\u1EA1i \u0111\u01B0\u1EDDng d\u1EABn "${theme}"`, { code: "theme_package_not_found" });
|
|
1808
1886
|
assertInside(cwd, root, theme);
|
|
1809
1887
|
} else {
|
|
1810
|
-
const nmCandidate =
|
|
1811
|
-
if (
|
|
1888
|
+
const nmCandidate = path7.resolve(cwd, "node_modules", theme);
|
|
1889
|
+
if (fs7.existsSync(nmCandidate)) root = nmCandidate;
|
|
1812
1890
|
else {
|
|
1813
1891
|
const packages = repoPackagesDir();
|
|
1814
1892
|
const pkgDirName = theme === DEFAULT_THEME ? "theme-default" : theme;
|
|
1815
|
-
const srcCandidate = packages ?
|
|
1816
|
-
if (srcCandidate &&
|
|
1893
|
+
const srcCandidate = packages ? path7.join(packages, pkgDirName) : null;
|
|
1894
|
+
if (srcCandidate && fs7.existsSync(srcCandidate)) root = srcCandidate;
|
|
1817
1895
|
}
|
|
1818
1896
|
if (!root) {
|
|
1819
1897
|
throw new DommiefError(`kh\xF4ng t\xECm th\u1EA5y theme package "${theme}" (\u0111\xE3 th\u1EED node_modules/${theme})`, { code: "theme_package_not_found" });
|
|
1820
1898
|
}
|
|
1821
1899
|
}
|
|
1822
|
-
if (
|
|
1900
|
+
if (fs7.existsSync(path7.join(root, "src", "layouts"))) root = path7.join(root, "src");
|
|
1823
1901
|
return root;
|
|
1824
1902
|
}
|
|
1825
1903
|
function pickEntry(dir, name) {
|
|
1826
1904
|
for (const ext of EXTS) {
|
|
1827
|
-
const f =
|
|
1828
|
-
if (
|
|
1905
|
+
const f = path7.join(dir, name + ext);
|
|
1906
|
+
if (fs7.existsSync(f) && fs7.statSync(f).isFile()) return f;
|
|
1829
1907
|
}
|
|
1830
|
-
const subIndexDir =
|
|
1831
|
-
if (
|
|
1908
|
+
const subIndexDir = path7.join(dir, name);
|
|
1909
|
+
if (fs7.existsSync(subIndexDir) && fs7.statSync(subIndexDir).isDirectory()) {
|
|
1832
1910
|
for (const ext of EXTS) {
|
|
1833
|
-
const f =
|
|
1834
|
-
if (
|
|
1911
|
+
const f = path7.join(subIndexDir, "index" + ext);
|
|
1912
|
+
if (fs7.existsSync(f) && fs7.statSync(f).isFile()) return f;
|
|
1835
1913
|
}
|
|
1836
1914
|
}
|
|
1837
1915
|
return null;
|
|
1838
1916
|
}
|
|
1839
1917
|
function scanComponents(dir) {
|
|
1840
1918
|
const out = {};
|
|
1841
|
-
if (!
|
|
1842
|
-
for (const entry of
|
|
1919
|
+
if (!fs7.existsSync(dir)) return out;
|
|
1920
|
+
for (const entry of fs7.readdirSync(dir, { withFileTypes: true })) {
|
|
1843
1921
|
const name = entry.name;
|
|
1844
1922
|
if (!isComponentName(name)) continue;
|
|
1845
1923
|
if (entry.isFile()) {
|
|
@@ -1847,26 +1925,26 @@ function scanComponents(dir) {
|
|
|
1847
1925
|
if (!ext) continue;
|
|
1848
1926
|
const specifier = name.slice(0, -ext.length);
|
|
1849
1927
|
if (!isComponentName(specifier)) continue;
|
|
1850
|
-
out[specifier] ??=
|
|
1928
|
+
out[specifier] ??= path7.resolve(dir, name);
|
|
1851
1929
|
} else if (entry.isDirectory()) {
|
|
1852
1930
|
const found = pickEntry(dir, name);
|
|
1853
|
-
if (found) out[name] =
|
|
1931
|
+
if (found) out[name] = path7.resolve(found);
|
|
1854
1932
|
}
|
|
1855
1933
|
}
|
|
1856
1934
|
return out;
|
|
1857
1935
|
}
|
|
1858
1936
|
function scanLocales(dir) {
|
|
1859
1937
|
const out = {};
|
|
1860
|
-
if (!
|
|
1861
|
-
for (const entry of
|
|
1938
|
+
if (!fs7.existsSync(dir)) return out;
|
|
1939
|
+
for (const entry of fs7.readdirSync(dir, { withFileTypes: true })) {
|
|
1862
1940
|
if (entry.isFile() && entry.name.endsWith(".json")) {
|
|
1863
|
-
out[entry.name.slice(0, -".json".length)] =
|
|
1941
|
+
out[entry.name.slice(0, -".json".length)] = path7.resolve(dir, entry.name);
|
|
1864
1942
|
}
|
|
1865
1943
|
}
|
|
1866
1944
|
return out;
|
|
1867
1945
|
}
|
|
1868
1946
|
function loadThemeManifest(root, name) {
|
|
1869
|
-
const layoutsDir =
|
|
1947
|
+
const layoutsDir = path7.join(root, "layouts");
|
|
1870
1948
|
const layouts = {};
|
|
1871
1949
|
const missing = [];
|
|
1872
1950
|
for (const layoutName of ["GlobalLayout", "DocLayout", "DemoLayout"]) {
|
|
@@ -1877,19 +1955,19 @@ function loadThemeManifest(root, name) {
|
|
|
1877
1955
|
if (missing.length) {
|
|
1878
1956
|
throw new DommiefError(`theme "${name}" thi\u1EBFu layout: ${missing.join(", ")}`, { code: "theme_layout_missing", file: layoutsDir, layout: missing.join(",") });
|
|
1879
1957
|
}
|
|
1880
|
-
const pluginJs = ["plugin.ts", "plugin.js",
|
|
1958
|
+
const pluginJs = ["plugin.ts", "plugin.js", path7.join("plugin", "index.ts"), path7.join("plugin", "index.js")].map((f) => path7.join(root, f)).find((f) => fs7.existsSync(f));
|
|
1881
1959
|
return {
|
|
1882
1960
|
name,
|
|
1883
1961
|
root,
|
|
1884
|
-
builtins: scanComponents(
|
|
1885
|
-
slots: scanComponents(
|
|
1962
|
+
builtins: scanComponents(path7.join(root, "builtins")),
|
|
1963
|
+
slots: scanComponents(path7.join(root, "slots")),
|
|
1886
1964
|
layouts,
|
|
1887
|
-
locales: scanLocales(
|
|
1965
|
+
locales: scanLocales(path7.join(root, "locales")),
|
|
1888
1966
|
plugin: pluginJs ?? null
|
|
1889
1967
|
};
|
|
1890
1968
|
}
|
|
1891
1969
|
function loadTheme(opts) {
|
|
1892
|
-
const cwd =
|
|
1970
|
+
const cwd = path7.resolve(opts.cwd);
|
|
1893
1971
|
const theme = opts.theme ?? DEFAULT_THEME;
|
|
1894
1972
|
const root = resolveThemeRoot(cwd, theme);
|
|
1895
1973
|
return loadThemeManifest(root, theme);
|
|
@@ -1897,9 +1975,9 @@ function loadTheme(opts) {
|
|
|
1897
1975
|
|
|
1898
1976
|
// src/theme/resolver.ts
|
|
1899
1977
|
function tryLoadLocal(cwd) {
|
|
1900
|
-
const localRoot =
|
|
1901
|
-
if (!
|
|
1902
|
-
const layoutsDir =
|
|
1978
|
+
const localRoot = path8.resolve(cwd, ".dommief", "theme");
|
|
1979
|
+
if (!fs8.existsSync(localRoot)) return null;
|
|
1980
|
+
const layoutsDir = path8.join(localRoot, "layouts");
|
|
1903
1981
|
const layouts = {};
|
|
1904
1982
|
for (const name of ["GlobalLayout", "DocLayout", "DemoLayout"]) {
|
|
1905
1983
|
const found = scanComponents(layoutsDir)[name];
|
|
@@ -1908,8 +1986,8 @@ function tryLoadLocal(cwd) {
|
|
|
1908
1986
|
return {
|
|
1909
1987
|
name: ".dommief/theme",
|
|
1910
1988
|
root: localRoot,
|
|
1911
|
-
builtins: scanComponents(
|
|
1912
|
-
slots: scanComponents(
|
|
1989
|
+
builtins: scanComponents(path8.join(localRoot, "builtins")),
|
|
1990
|
+
slots: scanComponents(path8.join(localRoot, "slots")),
|
|
1913
1991
|
layouts,
|
|
1914
1992
|
locales: {},
|
|
1915
1993
|
plugin: null
|
|
@@ -1955,8 +2033,8 @@ function buildAliasChain(cwd, config) {
|
|
|
1955
2033
|
// src/cache.ts
|
|
1956
2034
|
init_rehype_code();
|
|
1957
2035
|
import { createHash as createHash5 } from "crypto";
|
|
1958
|
-
import
|
|
1959
|
-
import
|
|
2036
|
+
import fs9 from "fs";
|
|
2037
|
+
import path9 from "path";
|
|
1960
2038
|
var CACHE_VERSION = 3;
|
|
1961
2039
|
function stableStringify(value) {
|
|
1962
2040
|
if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
|
|
@@ -1983,7 +2061,7 @@ var TransformCache = class {
|
|
|
1983
2061
|
this.enabled = enabled;
|
|
1984
2062
|
if (!enabled) return;
|
|
1985
2063
|
try {
|
|
1986
|
-
this.manifest = JSON.parse(
|
|
2064
|
+
this.manifest = JSON.parse(fs9.readFileSync(path9.join(dir, "manifest.json"), "utf8"));
|
|
1987
2065
|
} catch {
|
|
1988
2066
|
this.manifest = {};
|
|
1989
2067
|
}
|
|
@@ -1996,12 +2074,12 @@ var TransformCache = class {
|
|
|
1996
2074
|
manifest = {};
|
|
1997
2075
|
nextManifest = {};
|
|
1998
2076
|
fileOf(key) {
|
|
1999
|
-
return
|
|
2077
|
+
return path9.join(this.dir, key.slice(0, 2), `${key}.json`);
|
|
2000
2078
|
}
|
|
2001
2079
|
read(key) {
|
|
2002
2080
|
if (!this.enabled) return null;
|
|
2003
2081
|
try {
|
|
2004
|
-
const v = JSON.parse(
|
|
2082
|
+
const v = JSON.parse(fs9.readFileSync(this.fileOf(key), "utf8"));
|
|
2005
2083
|
return typeof v?.code === "string" && v.meta ? v : null;
|
|
2006
2084
|
} catch {
|
|
2007
2085
|
return null;
|
|
@@ -2011,19 +2089,19 @@ var TransformCache = class {
|
|
|
2011
2089
|
write(key, payload) {
|
|
2012
2090
|
if (!this.enabled) return;
|
|
2013
2091
|
const file = this.fileOf(key);
|
|
2014
|
-
|
|
2092
|
+
fs9.mkdirSync(path9.dirname(file), { recursive: true });
|
|
2015
2093
|
const tmp = `${file}.${process.pid}.tmp`;
|
|
2016
|
-
|
|
2017
|
-
|
|
2094
|
+
fs9.writeFileSync(tmp, JSON.stringify(payload));
|
|
2095
|
+
fs9.renameSync(tmp, file);
|
|
2018
2096
|
}
|
|
2019
2097
|
/** Xoá file cache không còn dùng (trang bị xoá/đổi tên/đổi nội dung). Best-effort, không throw. */
|
|
2020
2098
|
gc(keepKeys) {
|
|
2021
2099
|
if (!this.enabled) return;
|
|
2022
2100
|
try {
|
|
2023
|
-
for (const shard of
|
|
2101
|
+
for (const shard of fs9.readdirSync(this.dir, { withFileTypes: true })) {
|
|
2024
2102
|
if (!shard.isDirectory()) continue;
|
|
2025
|
-
for (const f of
|
|
2026
|
-
if (!keepKeys.has(f.replace(/\.json$/, "")) || f.endsWith(".tmp"))
|
|
2103
|
+
for (const f of fs9.readdirSync(path9.join(this.dir, shard.name))) {
|
|
2104
|
+
if (!keepKeys.has(f.replace(/\.json$/, "")) || f.endsWith(".tmp")) fs9.rmSync(path9.join(this.dir, shard.name, f), { force: true });
|
|
2027
2105
|
}
|
|
2028
2106
|
}
|
|
2029
2107
|
} catch {
|
|
@@ -2073,8 +2151,8 @@ var TransformCache = class {
|
|
|
2073
2151
|
if (!this.enabled) return;
|
|
2074
2152
|
this.gc(this.used);
|
|
2075
2153
|
try {
|
|
2076
|
-
|
|
2077
|
-
|
|
2154
|
+
fs9.mkdirSync(this.dir, { recursive: true });
|
|
2155
|
+
fs9.writeFileSync(path9.join(this.dir, "manifest.json"), JSON.stringify(this.nextManifest));
|
|
2078
2156
|
} catch {
|
|
2079
2157
|
}
|
|
2080
2158
|
}
|
|
@@ -2095,6 +2173,7 @@ function resolveConfiguredColor(defaultValue) {
|
|
|
2095
2173
|
|
|
2096
2174
|
// src/vite/plugin.ts
|
|
2097
2175
|
var demoRegistry = /* @__PURE__ */ new Map();
|
|
2176
|
+
var demoDirs = /* @__PURE__ */ new Map();
|
|
2098
2177
|
function dommiefPlugin(opts) {
|
|
2099
2178
|
let loaded = opts.config ? { config: opts.config, host: opts.host ?? createPluginHost(opts.config.plugins ?? []) } : void 0;
|
|
2100
2179
|
const state = createState(opts.root, () => {
|
|
@@ -2122,12 +2201,16 @@ function dommiefPlugin(opts) {
|
|
|
2122
2201
|
const bare = id.replace(/^\//, "");
|
|
2123
2202
|
if (bare === "virtual:dommief/entry") return "\0virtual:dommief/entry";
|
|
2124
2203
|
if (bare.startsWith("virtual:dommief-demo/")) return "\0" + bare;
|
|
2204
|
+
if (importer?.startsWith("\0virtual:dommief-demo/") && id.startsWith(".")) {
|
|
2205
|
+
const dir = demoDirs.get(importer.slice("\0virtual:dommief-demo/".length));
|
|
2206
|
+
if (dir) return this.resolve(path10.resolve(dir, id), path10.join(dir, "_demo.tsx"), { skipSelf: true });
|
|
2207
|
+
}
|
|
2125
2208
|
if (bare === "virtual:dommief/routes" || bare === "virtual:dommief/site") return null;
|
|
2126
2209
|
if (id.endsWith(".md") || id.endsWith(".md?meta")) {
|
|
2127
|
-
const base = importer ?
|
|
2210
|
+
const base = importer ? path10.dirname(importer.replace(/\?meta$/, "")) : process.cwd();
|
|
2128
2211
|
const q = id.endsWith("?meta") ? "?meta" : "";
|
|
2129
2212
|
const clean = id.replace(/\?meta$/, "");
|
|
2130
|
-
return winPath(
|
|
2213
|
+
return winPath(path10.isAbsolute(clean) ? clean : path10.resolve(base, clean)) + q;
|
|
2131
2214
|
}
|
|
2132
2215
|
return null;
|
|
2133
2216
|
},
|
|
@@ -2143,7 +2226,7 @@ function dommiefPlugin(opts) {
|
|
|
2143
2226
|
if (id.endsWith(".md") || id.endsWith(".md?meta")) {
|
|
2144
2227
|
const isMeta = id.endsWith("?meta");
|
|
2145
2228
|
const file = isMeta ? id.slice(0, -"?meta".length) : id;
|
|
2146
|
-
const code = await
|
|
2229
|
+
const code = await fs10.promises.readFile(file, "utf8");
|
|
2147
2230
|
const transformOpts = { hooks: state.host(), byFile: state.getTable().byFile, base: state.config().base, code: state.config().markdown };
|
|
2148
2231
|
let out;
|
|
2149
2232
|
let meta;
|
|
@@ -2168,7 +2251,8 @@ function dommiefPlugin(opts) {
|
|
|
2168
2251
|
hitDemos = hit.demos ?? [];
|
|
2169
2252
|
for (const d of hitDemos) {
|
|
2170
2253
|
demoRegistry.set(d.id, d.source);
|
|
2171
|
-
|
|
2254
|
+
demoDirs.set(d.id, path10.dirname(file));
|
|
2255
|
+
if (d.src) this.addWatchFile(path10.resolve(path10.dirname(file), d.src));
|
|
2172
2256
|
}
|
|
2173
2257
|
} else {
|
|
2174
2258
|
const r = await transformMarkdown(code, file, transformOpts);
|
|
@@ -2176,7 +2260,8 @@ function dommiefPlugin(opts) {
|
|
|
2176
2260
|
const nonCacheDemos = r.demos ?? [];
|
|
2177
2261
|
for (const d of nonCacheDemos) {
|
|
2178
2262
|
demoRegistry.set(d.id, d.source);
|
|
2179
|
-
|
|
2263
|
+
demoDirs.set(d.id, path10.dirname(file));
|
|
2264
|
+
if (d.src) this.addWatchFile(path10.resolve(path10.dirname(file), d.src));
|
|
2180
2265
|
}
|
|
2181
2266
|
}
|
|
2182
2267
|
for (const w of warnings) this.warn(w);
|
|
@@ -2193,7 +2278,7 @@ function dommiefPlugin(opts) {
|
|
|
2193
2278
|
transformIndexHtml: { order: "pre", handler(html) {
|
|
2194
2279
|
const title = state.config().title;
|
|
2195
2280
|
let out = html;
|
|
2196
|
-
if (!/<title>/.test(out)) out = out.replace("<head>", `<head><title>${title}</title>`);
|
|
2281
|
+
if (!/<title>/.test(out)) out = out.replace("<head>", `<head><title>${escapeXml(title)}</title>`);
|
|
2197
2282
|
const tags = state.host().collectHtmlHeadTags();
|
|
2198
2283
|
if (tags.length) out = out.replace("</head>", tags.join("\n") + "</head>");
|
|
2199
2284
|
const themeConfig = state.config().themeConfig;
|
|
@@ -2210,7 +2295,7 @@ function dommiefPlugin(opts) {
|
|
|
2210
2295
|
return out;
|
|
2211
2296
|
} },
|
|
2212
2297
|
configureServer(server) {
|
|
2213
|
-
const localThemeDir = winPath(
|
|
2298
|
+
const localThemeDir = winPath(path10.resolve(opts.root, ".dommief", "theme")) + "/";
|
|
2214
2299
|
const update = () => {
|
|
2215
2300
|
const aliases = buildAliasChain(opts.root, loaded.config);
|
|
2216
2301
|
server.config.resolve.alias = { ...server.config.resolve.alias, ...aliases };
|
|
@@ -2251,8 +2336,8 @@ import { build } from "vite";
|
|
|
2251
2336
|
init_errors();
|
|
2252
2337
|
|
|
2253
2338
|
// src/link-check.ts
|
|
2254
|
-
import
|
|
2255
|
-
import
|
|
2339
|
+
import fs11 from "fs";
|
|
2340
|
+
import path11 from "path";
|
|
2256
2341
|
var HREF_RE = /<a\b[^>]*\shref="([^"]*)"[^>]*>/g;
|
|
2257
2342
|
var ASSET_RE = /<(?:img|link)\b[^>]*\s(?:src|href)="([^"]*)"[^>]*>/g;
|
|
2258
2343
|
var LINE_RE = /\sdata-src-line="(\d+)"/;
|
|
@@ -2287,8 +2372,8 @@ function check(refs, opts) {
|
|
|
2287
2372
|
const cleanPath = (rawPath ?? "").split("?")[0];
|
|
2288
2373
|
if (isAsset) {
|
|
2289
2374
|
const relPath = stripBase(cleanPath, opts.base) || "/";
|
|
2290
|
-
const onDisk =
|
|
2291
|
-
if (!
|
|
2375
|
+
const onDisk = path11.join(opts.outDir, decodeURIComponent(relPath));
|
|
2376
|
+
if (!fs11.existsSync(onDisk)) issues.push({ code: "link_asset_missing", ref });
|
|
2292
2377
|
continue;
|
|
2293
2378
|
}
|
|
2294
2379
|
const routePath = stripBase(cleanPath, opts.base) || "/";
|
|
@@ -2314,78 +2399,6 @@ function formatIssues(issues) {
|
|
|
2314
2399
|
return lines.join("\n");
|
|
2315
2400
|
}
|
|
2316
2401
|
|
|
2317
|
-
// src/exclude.ts
|
|
2318
|
-
var cache = /* @__PURE__ */ new Map();
|
|
2319
|
-
function globToRegExp(pattern) {
|
|
2320
|
-
let src = "";
|
|
2321
|
-
for (let i = 0; i < pattern.length; i++) {
|
|
2322
|
-
const c = pattern.charAt(i);
|
|
2323
|
-
if (c === "*" && pattern.charAt(i + 1) === "*") {
|
|
2324
|
-
const slash = pattern.charAt(i + 2) === "/";
|
|
2325
|
-
src += slash ? "(?:.*/)?" : ".*";
|
|
2326
|
-
i += slash ? 2 : 1;
|
|
2327
|
-
} else if (c === "*") {
|
|
2328
|
-
src += "[^/]*";
|
|
2329
|
-
} else if (c === "?") {
|
|
2330
|
-
src += "[^/]";
|
|
2331
|
-
} else {
|
|
2332
|
-
src += c.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
2333
|
-
}
|
|
2334
|
-
}
|
|
2335
|
-
return new RegExp(`^${src}$`);
|
|
2336
|
-
}
|
|
2337
|
-
function matcher(pattern) {
|
|
2338
|
-
const hit = cache.get(pattern);
|
|
2339
|
-
if (hit) return hit;
|
|
2340
|
-
const re = globToRegExp(pattern);
|
|
2341
|
-
cache.set(pattern, re);
|
|
2342
|
-
return re;
|
|
2343
|
-
}
|
|
2344
|
-
function matchExclude(path15, patterns) {
|
|
2345
|
-
return patterns.some((p) => matcher(p).test(path15));
|
|
2346
|
-
}
|
|
2347
|
-
|
|
2348
|
-
// src/sitemap.ts
|
|
2349
|
-
import fs11 from "fs";
|
|
2350
|
-
import path11 from "path";
|
|
2351
|
-
function canonicalUrl(hostname, base, routePath) {
|
|
2352
|
-
const host = hostname.replace(/\/$/, "");
|
|
2353
|
-
const b = base.replace(/\/$/, "");
|
|
2354
|
-
const p = routePath === "/" ? "/" : routePath;
|
|
2355
|
-
return host + (b + p).replace(/\/{2,}/g, "/").replace(/^(https?:\/)([^/])/, "$1/$2");
|
|
2356
|
-
}
|
|
2357
|
-
function buildSitemap(routes, config, base) {
|
|
2358
|
-
const urls = routes.filter((r) => r.path !== "/404" && !/[:*]/.test(r.path) && !matchExclude(r.path, config.exclude)).map((r) => ` <url><loc>${escapeXml(canonicalUrl(config.hostname, base, r.path))}</loc></url>`).join("\n");
|
|
2359
|
-
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
2360
|
-
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
2361
|
-
${urls}
|
|
2362
|
-
</urlset>
|
|
2363
|
-
`;
|
|
2364
|
-
}
|
|
2365
|
-
function buildRobots(sitemapUrl) {
|
|
2366
|
-
return `User-agent: *
|
|
2367
|
-
Allow: /
|
|
2368
|
-
|
|
2369
|
-
Sitemap: ${sitemapUrl}
|
|
2370
|
-
`;
|
|
2371
|
-
}
|
|
2372
|
-
function escapeXml(s) {
|
|
2373
|
-
return s.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
2374
|
-
}
|
|
2375
|
-
function writeSitemapAndRobots(opts) {
|
|
2376
|
-
if (opts.sitemap === false) return;
|
|
2377
|
-
if (!opts.sitemap.hostname) {
|
|
2378
|
-
opts.log.warn("sitemap.hostname thi\u1EBFu \u2014 b\u1ECF qua sitemap.xml/robots.txt");
|
|
2379
|
-
return;
|
|
2380
|
-
}
|
|
2381
|
-
const xml = buildSitemap(opts.routes, opts.sitemap, opts.base);
|
|
2382
|
-
fs11.writeFileSync(path11.join(opts.outDir, "sitemap.xml"), xml);
|
|
2383
|
-
if (opts.robots) {
|
|
2384
|
-
const sitemapUrl = canonicalUrl(opts.sitemap.hostname, opts.base, "/").replace(/\/$/, "") + "/sitemap.xml";
|
|
2385
|
-
fs11.writeFileSync(path11.join(opts.outDir, "robots.txt"), buildRobots(sitemapUrl));
|
|
2386
|
-
}
|
|
2387
|
-
}
|
|
2388
|
-
|
|
2389
2402
|
// src/build.ts
|
|
2390
2403
|
var ROOT_PLACEHOLDER = /<div id="root"><\/div>/;
|
|
2391
2404
|
async function mapLimit(items, limit, fn) {
|
|
@@ -2501,7 +2514,12 @@ async function runBuild({ root, config, host, log }) {
|
|
|
2501
2514
|
const linkRefs = [];
|
|
2502
2515
|
await timed("prerender", async () => {
|
|
2503
2516
|
await mapLimit(plan.prerender, config.ssg.concurrency, async (r) => {
|
|
2504
|
-
|
|
2517
|
+
let html;
|
|
2518
|
+
try {
|
|
2519
|
+
html = await render(r.path);
|
|
2520
|
+
} catch (e) {
|
|
2521
|
+
throw new DommiefError(`prerender ${r.path}: ${formatError(e)}`, { code: "ssg_render_failed" });
|
|
2522
|
+
}
|
|
2505
2523
|
if (linkCheckEnabled) linkRefs.push(...collect(r.id, html, fileOf(r.id)));
|
|
2506
2524
|
writePage(r, wrap(html, r.path));
|
|
2507
2525
|
});
|
|
@@ -2604,7 +2622,14 @@ function spaFallbackPlugin(opts) {
|
|
|
2604
2622
|
name: "dommief:preview-spa-fallback",
|
|
2605
2623
|
configurePreviewServer(s) {
|
|
2606
2624
|
s.middlewares.use((req, res, next) => {
|
|
2607
|
-
|
|
2625
|
+
let urlPath;
|
|
2626
|
+
try {
|
|
2627
|
+
urlPath = decodeURIComponent((req.url ?? "/").split("?")[0] ?? "/");
|
|
2628
|
+
} catch {
|
|
2629
|
+
res.statusCode = 400;
|
|
2630
|
+
res.end("Invalid URL");
|
|
2631
|
+
return;
|
|
2632
|
+
}
|
|
2608
2633
|
if (req.method !== "GET") return next();
|
|
2609
2634
|
if (base && urlPath !== base && !urlPath.startsWith(base + "/")) {
|
|
2610
2635
|
res.statusCode = 404;
|
package/dist/index.js
CHANGED
|
@@ -272,6 +272,7 @@ function resolveConfig(userConfig, opts) {
|
|
|
272
272
|
srcDir: c.srcDir,
|
|
273
273
|
docDirs: (c.docDirs ?? [c.srcDir]).map((d) => typeof d === "string" ? { dir: d } : { dir: d.dir }),
|
|
274
274
|
forceKebabCase: c.forceKebabCase,
|
|
275
|
+
preset: c.preset,
|
|
275
276
|
locales,
|
|
276
277
|
themeConfig: c.themeConfig,
|
|
277
278
|
theme: c.theme,
|
|
@@ -961,7 +962,7 @@ function buildApiTable(src, filePath = "") {
|
|
|
961
962
|
try {
|
|
962
963
|
docs = parser.parse(abs) ?? [];
|
|
963
964
|
} catch {
|
|
964
|
-
|
|
965
|
+
return null;
|
|
965
966
|
}
|
|
966
967
|
const entries = (docs || []).map((d) => ({
|
|
967
968
|
displayName: d.displayName,
|
|
@@ -983,6 +984,7 @@ function rehypeApi() {
|
|
|
983
984
|
return (tree, file) => {
|
|
984
985
|
const apis = Array.isArray(file?.data?.apis) ? file.data.apis : [];
|
|
985
986
|
const watched = Array.isArray(file?.data?.apiSrcFiles) ? file.data.apiSrcFiles : [];
|
|
987
|
+
const warnings = Array.isArray(file?.data?.warnings) ? file.data.warnings : [];
|
|
986
988
|
const filePath = String(file?.path ?? "");
|
|
987
989
|
visit7(tree, "element", (node, index, parent) => {
|
|
988
990
|
if (!parent || typeof index !== "number") return;
|
|
@@ -992,6 +994,7 @@ function rehypeApi() {
|
|
|
992
994
|
const abs = resolveApiSrc(src, filePath);
|
|
993
995
|
const data = buildApiTable(src, filePath);
|
|
994
996
|
watched.push(abs);
|
|
997
|
+
if (!data?.length) warnings.push(`<API src="${src}">: ${data ? "no component found (syntax error or no exported component)" : "file missing or failed to parse"}: ${abs}`);
|
|
995
998
|
apis.push({ id: hashFileContent(src + filePath).slice(0, 8), src: abs, data });
|
|
996
999
|
parent.children[index] = {
|
|
997
1000
|
type: "element",
|
|
@@ -1006,6 +1009,7 @@ function rehypeApi() {
|
|
|
1006
1009
|
if (file?.data) {
|
|
1007
1010
|
file.data.apis = apis;
|
|
1008
1011
|
file.data.apiSrcFiles = watched;
|
|
1012
|
+
file.data.warnings = warnings;
|
|
1009
1013
|
}
|
|
1010
1014
|
};
|
|
1011
1015
|
}
|