dommief 0.1.1 → 0.1.3
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 +182 -147
- package/dist/index.js +5 -1
- package/package.json +1 -1
- 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 +193 -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,9 @@ import { cac } from "cac";
|
|
|
1010
1013
|
import { createServer } from "vite";
|
|
1011
1014
|
|
|
1012
1015
|
// src/vite/plugin.ts
|
|
1013
|
-
import
|
|
1014
|
-
import
|
|
1016
|
+
import { transformWithOxc } from "vite";
|
|
1017
|
+
import fs10 from "fs";
|
|
1018
|
+
import path10 from "path";
|
|
1015
1019
|
|
|
1016
1020
|
// src/config.ts
|
|
1017
1021
|
init_errors();
|
|
@@ -1149,6 +1153,7 @@ function resolveConfig(userConfig, opts) {
|
|
|
1149
1153
|
srcDir: c.srcDir,
|
|
1150
1154
|
docDirs: (c.docDirs ?? [c.srcDir]).map((d) => typeof d === "string" ? { dir: d } : { dir: d.dir }),
|
|
1151
1155
|
forceKebabCase: c.forceKebabCase,
|
|
1156
|
+
preset: c.preset,
|
|
1152
1157
|
locales,
|
|
1153
1158
|
themeConfig: c.themeConfig,
|
|
1154
1159
|
theme: c.theme,
|
|
@@ -1767,15 +1772,89 @@ function createState(cwd, get) {
|
|
|
1767
1772
|
// src/vite/plugin.ts
|
|
1768
1773
|
init_path();
|
|
1769
1774
|
|
|
1775
|
+
// src/sitemap.ts
|
|
1776
|
+
import fs6 from "fs";
|
|
1777
|
+
import path6 from "path";
|
|
1778
|
+
|
|
1779
|
+
// src/exclude.ts
|
|
1780
|
+
var cache = /* @__PURE__ */ new Map();
|
|
1781
|
+
function globToRegExp(pattern) {
|
|
1782
|
+
let src = "";
|
|
1783
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
1784
|
+
const c = pattern.charAt(i);
|
|
1785
|
+
if (c === "*" && pattern.charAt(i + 1) === "*") {
|
|
1786
|
+
const slash = pattern.charAt(i + 2) === "/";
|
|
1787
|
+
src += slash ? "(?:.*/)?" : ".*";
|
|
1788
|
+
i += slash ? 2 : 1;
|
|
1789
|
+
} else if (c === "*") {
|
|
1790
|
+
src += "[^/]*";
|
|
1791
|
+
} else if (c === "?") {
|
|
1792
|
+
src += "[^/]";
|
|
1793
|
+
} else {
|
|
1794
|
+
src += c.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
return new RegExp(`^${src}$`);
|
|
1798
|
+
}
|
|
1799
|
+
function matcher(pattern) {
|
|
1800
|
+
const hit = cache.get(pattern);
|
|
1801
|
+
if (hit) return hit;
|
|
1802
|
+
const re = globToRegExp(pattern);
|
|
1803
|
+
cache.set(pattern, re);
|
|
1804
|
+
return re;
|
|
1805
|
+
}
|
|
1806
|
+
function matchExclude(path15, patterns) {
|
|
1807
|
+
return patterns.some((p) => matcher(p).test(path15));
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
// src/sitemap.ts
|
|
1811
|
+
function canonicalUrl(hostname, base, routePath) {
|
|
1812
|
+
const host = hostname.replace(/\/$/, "");
|
|
1813
|
+
const b = base.replace(/\/$/, "");
|
|
1814
|
+
const p = routePath === "/" ? "/" : routePath;
|
|
1815
|
+
return host + (b + p).replace(/\/{2,}/g, "/").replace(/^(https?:\/)([^/])/, "$1/$2");
|
|
1816
|
+
}
|
|
1817
|
+
function buildSitemap(routes, config, base) {
|
|
1818
|
+
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");
|
|
1819
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
1820
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
1821
|
+
${urls}
|
|
1822
|
+
</urlset>
|
|
1823
|
+
`;
|
|
1824
|
+
}
|
|
1825
|
+
function buildRobots(sitemapUrl) {
|
|
1826
|
+
return `User-agent: *
|
|
1827
|
+
Allow: /
|
|
1828
|
+
|
|
1829
|
+
Sitemap: ${sitemapUrl}
|
|
1830
|
+
`;
|
|
1831
|
+
}
|
|
1832
|
+
function escapeXml(s) {
|
|
1833
|
+
return s.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
1834
|
+
}
|
|
1835
|
+
function writeSitemapAndRobots(opts) {
|
|
1836
|
+
if (opts.sitemap === false) return;
|
|
1837
|
+
if (!opts.sitemap.hostname) {
|
|
1838
|
+
opts.log.warn("sitemap.hostname thi\u1EBFu \u2014 b\u1ECF qua sitemap.xml/robots.txt");
|
|
1839
|
+
return;
|
|
1840
|
+
}
|
|
1841
|
+
const xml = buildSitemap(opts.routes, opts.sitemap, opts.base);
|
|
1842
|
+
fs6.writeFileSync(path6.join(opts.outDir, "sitemap.xml"), xml);
|
|
1843
|
+
if (opts.robots) {
|
|
1844
|
+
const sitemapUrl = canonicalUrl(opts.sitemap.hostname, opts.base, "/").replace(/\/$/, "") + "/sitemap.xml";
|
|
1845
|
+
fs6.writeFileSync(path6.join(opts.outDir, "robots.txt"), buildRobots(sitemapUrl));
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1770
1849
|
// src/theme/resolver.ts
|
|
1771
1850
|
init_errors();
|
|
1772
|
-
import
|
|
1773
|
-
import
|
|
1851
|
+
import fs8 from "fs";
|
|
1852
|
+
import path8 from "path";
|
|
1774
1853
|
|
|
1775
1854
|
// src/theme/loader.ts
|
|
1776
1855
|
init_errors();
|
|
1777
|
-
import
|
|
1778
|
-
import
|
|
1856
|
+
import fs7 from "fs";
|
|
1857
|
+
import path7 from "path";
|
|
1779
1858
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1780
1859
|
var EXTS = [".tsx", ".ts", ".jsx", ".js"];
|
|
1781
1860
|
var DEFAULT_THEME = "dommief-theme-default";
|
|
@@ -1783,17 +1862,17 @@ function isComponentName(name) {
|
|
|
1783
1862
|
return /^[A-Z0-9]/.test(name);
|
|
1784
1863
|
}
|
|
1785
1864
|
function repoPackagesDir() {
|
|
1786
|
-
let cur =
|
|
1787
|
-
while (cur !==
|
|
1788
|
-
if (
|
|
1789
|
-
cur =
|
|
1865
|
+
let cur = path7.dirname(fileURLToPath2(import.meta.url));
|
|
1866
|
+
while (cur !== path7.parse(cur).root) {
|
|
1867
|
+
if (fs7.existsSync(path7.join(cur, "pnpm-workspace.yaml"))) return path7.join(cur, "packages");
|
|
1868
|
+
cur = path7.dirname(cur);
|
|
1790
1869
|
}
|
|
1791
1870
|
return null;
|
|
1792
1871
|
}
|
|
1793
1872
|
function assertInside(cwd, target, theme) {
|
|
1794
|
-
const rel =
|
|
1795
|
-
const insideCwd = rel === "" || !rel.startsWith("..") && !
|
|
1796
|
-
const insideNodeModules = target.split(
|
|
1873
|
+
const rel = path7.relative(cwd, target);
|
|
1874
|
+
const insideCwd = rel === "" || !rel.startsWith("..") && !path7.isAbsolute(rel);
|
|
1875
|
+
const insideNodeModules = target.split(path7.sep).includes("node_modules");
|
|
1797
1876
|
if (!insideCwd && !insideNodeModules) {
|
|
1798
1877
|
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
1878
|
}
|
|
@@ -1802,44 +1881,44 @@ function resolveThemeRoot(cwd, theme) {
|
|
|
1802
1881
|
const isPathLike = theme.startsWith(".") || theme.startsWith("/");
|
|
1803
1882
|
let root = null;
|
|
1804
1883
|
if (isPathLike) {
|
|
1805
|
-
const candidate =
|
|
1806
|
-
if (
|
|
1884
|
+
const candidate = path7.resolve(cwd, theme);
|
|
1885
|
+
if (fs7.existsSync(candidate) && fs7.statSync(candidate).isDirectory()) root = candidate;
|
|
1807
1886
|
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
1887
|
assertInside(cwd, root, theme);
|
|
1809
1888
|
} else {
|
|
1810
|
-
const nmCandidate =
|
|
1811
|
-
if (
|
|
1889
|
+
const nmCandidate = path7.resolve(cwd, "node_modules", theme);
|
|
1890
|
+
if (fs7.existsSync(nmCandidate)) root = nmCandidate;
|
|
1812
1891
|
else {
|
|
1813
1892
|
const packages = repoPackagesDir();
|
|
1814
1893
|
const pkgDirName = theme === DEFAULT_THEME ? "theme-default" : theme;
|
|
1815
|
-
const srcCandidate = packages ?
|
|
1816
|
-
if (srcCandidate &&
|
|
1894
|
+
const srcCandidate = packages ? path7.join(packages, pkgDirName) : null;
|
|
1895
|
+
if (srcCandidate && fs7.existsSync(srcCandidate)) root = srcCandidate;
|
|
1817
1896
|
}
|
|
1818
1897
|
if (!root) {
|
|
1819
1898
|
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
1899
|
}
|
|
1821
1900
|
}
|
|
1822
|
-
if (
|
|
1901
|
+
if (fs7.existsSync(path7.join(root, "src", "layouts"))) root = path7.join(root, "src");
|
|
1823
1902
|
return root;
|
|
1824
1903
|
}
|
|
1825
1904
|
function pickEntry(dir, name) {
|
|
1826
1905
|
for (const ext of EXTS) {
|
|
1827
|
-
const f =
|
|
1828
|
-
if (
|
|
1906
|
+
const f = path7.join(dir, name + ext);
|
|
1907
|
+
if (fs7.existsSync(f) && fs7.statSync(f).isFile()) return f;
|
|
1829
1908
|
}
|
|
1830
|
-
const subIndexDir =
|
|
1831
|
-
if (
|
|
1909
|
+
const subIndexDir = path7.join(dir, name);
|
|
1910
|
+
if (fs7.existsSync(subIndexDir) && fs7.statSync(subIndexDir).isDirectory()) {
|
|
1832
1911
|
for (const ext of EXTS) {
|
|
1833
|
-
const f =
|
|
1834
|
-
if (
|
|
1912
|
+
const f = path7.join(subIndexDir, "index" + ext);
|
|
1913
|
+
if (fs7.existsSync(f) && fs7.statSync(f).isFile()) return f;
|
|
1835
1914
|
}
|
|
1836
1915
|
}
|
|
1837
1916
|
return null;
|
|
1838
1917
|
}
|
|
1839
1918
|
function scanComponents(dir) {
|
|
1840
1919
|
const out = {};
|
|
1841
|
-
if (!
|
|
1842
|
-
for (const entry of
|
|
1920
|
+
if (!fs7.existsSync(dir)) return out;
|
|
1921
|
+
for (const entry of fs7.readdirSync(dir, { withFileTypes: true })) {
|
|
1843
1922
|
const name = entry.name;
|
|
1844
1923
|
if (!isComponentName(name)) continue;
|
|
1845
1924
|
if (entry.isFile()) {
|
|
@@ -1847,26 +1926,26 @@ function scanComponents(dir) {
|
|
|
1847
1926
|
if (!ext) continue;
|
|
1848
1927
|
const specifier = name.slice(0, -ext.length);
|
|
1849
1928
|
if (!isComponentName(specifier)) continue;
|
|
1850
|
-
out[specifier] ??=
|
|
1929
|
+
out[specifier] ??= path7.resolve(dir, name);
|
|
1851
1930
|
} else if (entry.isDirectory()) {
|
|
1852
1931
|
const found = pickEntry(dir, name);
|
|
1853
|
-
if (found) out[name] =
|
|
1932
|
+
if (found) out[name] = path7.resolve(found);
|
|
1854
1933
|
}
|
|
1855
1934
|
}
|
|
1856
1935
|
return out;
|
|
1857
1936
|
}
|
|
1858
1937
|
function scanLocales(dir) {
|
|
1859
1938
|
const out = {};
|
|
1860
|
-
if (!
|
|
1861
|
-
for (const entry of
|
|
1939
|
+
if (!fs7.existsSync(dir)) return out;
|
|
1940
|
+
for (const entry of fs7.readdirSync(dir, { withFileTypes: true })) {
|
|
1862
1941
|
if (entry.isFile() && entry.name.endsWith(".json")) {
|
|
1863
|
-
out[entry.name.slice(0, -".json".length)] =
|
|
1942
|
+
out[entry.name.slice(0, -".json".length)] = path7.resolve(dir, entry.name);
|
|
1864
1943
|
}
|
|
1865
1944
|
}
|
|
1866
1945
|
return out;
|
|
1867
1946
|
}
|
|
1868
1947
|
function loadThemeManifest(root, name) {
|
|
1869
|
-
const layoutsDir =
|
|
1948
|
+
const layoutsDir = path7.join(root, "layouts");
|
|
1870
1949
|
const layouts = {};
|
|
1871
1950
|
const missing = [];
|
|
1872
1951
|
for (const layoutName of ["GlobalLayout", "DocLayout", "DemoLayout"]) {
|
|
@@ -1877,19 +1956,19 @@ function loadThemeManifest(root, name) {
|
|
|
1877
1956
|
if (missing.length) {
|
|
1878
1957
|
throw new DommiefError(`theme "${name}" thi\u1EBFu layout: ${missing.join(", ")}`, { code: "theme_layout_missing", file: layoutsDir, layout: missing.join(",") });
|
|
1879
1958
|
}
|
|
1880
|
-
const pluginJs = ["plugin.ts", "plugin.js",
|
|
1959
|
+
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
1960
|
return {
|
|
1882
1961
|
name,
|
|
1883
1962
|
root,
|
|
1884
|
-
builtins: scanComponents(
|
|
1885
|
-
slots: scanComponents(
|
|
1963
|
+
builtins: scanComponents(path7.join(root, "builtins")),
|
|
1964
|
+
slots: scanComponents(path7.join(root, "slots")),
|
|
1886
1965
|
layouts,
|
|
1887
|
-
locales: scanLocales(
|
|
1966
|
+
locales: scanLocales(path7.join(root, "locales")),
|
|
1888
1967
|
plugin: pluginJs ?? null
|
|
1889
1968
|
};
|
|
1890
1969
|
}
|
|
1891
1970
|
function loadTheme(opts) {
|
|
1892
|
-
const cwd =
|
|
1971
|
+
const cwd = path7.resolve(opts.cwd);
|
|
1893
1972
|
const theme = opts.theme ?? DEFAULT_THEME;
|
|
1894
1973
|
const root = resolveThemeRoot(cwd, theme);
|
|
1895
1974
|
return loadThemeManifest(root, theme);
|
|
@@ -1897,9 +1976,9 @@ function loadTheme(opts) {
|
|
|
1897
1976
|
|
|
1898
1977
|
// src/theme/resolver.ts
|
|
1899
1978
|
function tryLoadLocal(cwd) {
|
|
1900
|
-
const localRoot =
|
|
1901
|
-
if (!
|
|
1902
|
-
const layoutsDir =
|
|
1979
|
+
const localRoot = path8.resolve(cwd, ".dommief", "theme");
|
|
1980
|
+
if (!fs8.existsSync(localRoot)) return null;
|
|
1981
|
+
const layoutsDir = path8.join(localRoot, "layouts");
|
|
1903
1982
|
const layouts = {};
|
|
1904
1983
|
for (const name of ["GlobalLayout", "DocLayout", "DemoLayout"]) {
|
|
1905
1984
|
const found = scanComponents(layoutsDir)[name];
|
|
@@ -1908,8 +1987,8 @@ function tryLoadLocal(cwd) {
|
|
|
1908
1987
|
return {
|
|
1909
1988
|
name: ".dommief/theme",
|
|
1910
1989
|
root: localRoot,
|
|
1911
|
-
builtins: scanComponents(
|
|
1912
|
-
slots: scanComponents(
|
|
1990
|
+
builtins: scanComponents(path8.join(localRoot, "builtins")),
|
|
1991
|
+
slots: scanComponents(path8.join(localRoot, "slots")),
|
|
1913
1992
|
layouts,
|
|
1914
1993
|
locales: {},
|
|
1915
1994
|
plugin: null
|
|
@@ -1955,8 +2034,8 @@ function buildAliasChain(cwd, config) {
|
|
|
1955
2034
|
// src/cache.ts
|
|
1956
2035
|
init_rehype_code();
|
|
1957
2036
|
import { createHash as createHash5 } from "crypto";
|
|
1958
|
-
import
|
|
1959
|
-
import
|
|
2037
|
+
import fs9 from "fs";
|
|
2038
|
+
import path9 from "path";
|
|
1960
2039
|
var CACHE_VERSION = 3;
|
|
1961
2040
|
function stableStringify(value) {
|
|
1962
2041
|
if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
|
|
@@ -1983,7 +2062,7 @@ var TransformCache = class {
|
|
|
1983
2062
|
this.enabled = enabled;
|
|
1984
2063
|
if (!enabled) return;
|
|
1985
2064
|
try {
|
|
1986
|
-
this.manifest = JSON.parse(
|
|
2065
|
+
this.manifest = JSON.parse(fs9.readFileSync(path9.join(dir, "manifest.json"), "utf8"));
|
|
1987
2066
|
} catch {
|
|
1988
2067
|
this.manifest = {};
|
|
1989
2068
|
}
|
|
@@ -1996,12 +2075,12 @@ var TransformCache = class {
|
|
|
1996
2075
|
manifest = {};
|
|
1997
2076
|
nextManifest = {};
|
|
1998
2077
|
fileOf(key) {
|
|
1999
|
-
return
|
|
2078
|
+
return path9.join(this.dir, key.slice(0, 2), `${key}.json`);
|
|
2000
2079
|
}
|
|
2001
2080
|
read(key) {
|
|
2002
2081
|
if (!this.enabled) return null;
|
|
2003
2082
|
try {
|
|
2004
|
-
const v = JSON.parse(
|
|
2083
|
+
const v = JSON.parse(fs9.readFileSync(this.fileOf(key), "utf8"));
|
|
2005
2084
|
return typeof v?.code === "string" && v.meta ? v : null;
|
|
2006
2085
|
} catch {
|
|
2007
2086
|
return null;
|
|
@@ -2011,19 +2090,19 @@ var TransformCache = class {
|
|
|
2011
2090
|
write(key, payload) {
|
|
2012
2091
|
if (!this.enabled) return;
|
|
2013
2092
|
const file = this.fileOf(key);
|
|
2014
|
-
|
|
2093
|
+
fs9.mkdirSync(path9.dirname(file), { recursive: true });
|
|
2015
2094
|
const tmp = `${file}.${process.pid}.tmp`;
|
|
2016
|
-
|
|
2017
|
-
|
|
2095
|
+
fs9.writeFileSync(tmp, JSON.stringify(payload));
|
|
2096
|
+
fs9.renameSync(tmp, file);
|
|
2018
2097
|
}
|
|
2019
2098
|
/** 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
2099
|
gc(keepKeys) {
|
|
2021
2100
|
if (!this.enabled) return;
|
|
2022
2101
|
try {
|
|
2023
|
-
for (const shard of
|
|
2102
|
+
for (const shard of fs9.readdirSync(this.dir, { withFileTypes: true })) {
|
|
2024
2103
|
if (!shard.isDirectory()) continue;
|
|
2025
|
-
for (const f of
|
|
2026
|
-
if (!keepKeys.has(f.replace(/\.json$/, "")) || f.endsWith(".tmp"))
|
|
2104
|
+
for (const f of fs9.readdirSync(path9.join(this.dir, shard.name))) {
|
|
2105
|
+
if (!keepKeys.has(f.replace(/\.json$/, "")) || f.endsWith(".tmp")) fs9.rmSync(path9.join(this.dir, shard.name, f), { force: true });
|
|
2027
2106
|
}
|
|
2028
2107
|
}
|
|
2029
2108
|
} catch {
|
|
@@ -2073,8 +2152,8 @@ var TransformCache = class {
|
|
|
2073
2152
|
if (!this.enabled) return;
|
|
2074
2153
|
this.gc(this.used);
|
|
2075
2154
|
try {
|
|
2076
|
-
|
|
2077
|
-
|
|
2155
|
+
fs9.mkdirSync(this.dir, { recursive: true });
|
|
2156
|
+
fs9.writeFileSync(path9.join(this.dir, "manifest.json"), JSON.stringify(this.nextManifest));
|
|
2078
2157
|
} catch {
|
|
2079
2158
|
}
|
|
2080
2159
|
}
|
|
@@ -2095,16 +2174,23 @@ function resolveConfiguredColor(defaultValue) {
|
|
|
2095
2174
|
|
|
2096
2175
|
// src/vite/plugin.ts
|
|
2097
2176
|
var demoRegistry = /* @__PURE__ */ new Map();
|
|
2177
|
+
var demoDirs = /* @__PURE__ */ new Map();
|
|
2098
2178
|
function dommiefPlugin(opts) {
|
|
2099
2179
|
let loaded = opts.config ? { config: opts.config, host: opts.host ?? createPluginHost(opts.config.plugins ?? []) } : void 0;
|
|
2100
2180
|
const state = createState(opts.root, () => {
|
|
2101
2181
|
if (!loaded) throw new Error("dommief: config ch\u01B0a \u0111\u01B0\u1EE3c n\u1EA1p");
|
|
2102
2182
|
return loaded;
|
|
2103
2183
|
});
|
|
2184
|
+
let isServe = false;
|
|
2185
|
+
const jsx2 = async (code, name) => {
|
|
2186
|
+
const r = await transformWithOxc(code, name + ".jsx", { lang: "jsx", jsx: { runtime: "automatic", development: isServe } });
|
|
2187
|
+
return { code: r.code, map: r.map ?? null, moduleType: "js" };
|
|
2188
|
+
};
|
|
2104
2189
|
const markdownPlugin = {
|
|
2105
2190
|
name: "dommief:markdown",
|
|
2106
2191
|
enforce: "pre",
|
|
2107
|
-
async config() {
|
|
2192
|
+
async config(_, env) {
|
|
2193
|
+
isServe = env.command === "serve";
|
|
2108
2194
|
if (!loaded) {
|
|
2109
2195
|
const config = await loadConfig(opts.root);
|
|
2110
2196
|
loaded = { config, host: createPluginHost(config.plugins ?? []) };
|
|
@@ -2114,7 +2200,10 @@ function dommiefPlugin(opts) {
|
|
|
2114
2200
|
return {
|
|
2115
2201
|
base: loaded.config.base,
|
|
2116
2202
|
resolve: { dedupe: ["react", "react-dom", "react-router"], alias: aliases },
|
|
2117
|
-
ssr: { noExternal: ["dommief"] }
|
|
2203
|
+
ssr: { noExternal: ["dommief"] },
|
|
2204
|
+
// Client dommief nằm trong node_modules nên Vite không quét dep → React CJS bị serve thô
|
|
2205
|
+
// ("does not provide an export named 'jsxDEV'").
|
|
2206
|
+
optimizeDeps: { include: ["react", "react-dom", "react-dom/client", "react/jsx-runtime", "react/jsx-dev-runtime", "react-router"] }
|
|
2118
2207
|
};
|
|
2119
2208
|
},
|
|
2120
2209
|
resolveId(id, importer) {
|
|
@@ -2122,12 +2211,16 @@ function dommiefPlugin(opts) {
|
|
|
2122
2211
|
const bare = id.replace(/^\//, "");
|
|
2123
2212
|
if (bare === "virtual:dommief/entry") return "\0virtual:dommief/entry";
|
|
2124
2213
|
if (bare.startsWith("virtual:dommief-demo/")) return "\0" + bare;
|
|
2214
|
+
if (importer?.startsWith("\0virtual:dommief-demo/") && id.startsWith(".")) {
|
|
2215
|
+
const dir = demoDirs.get(importer.slice("\0virtual:dommief-demo/".length));
|
|
2216
|
+
if (dir) return this.resolve(path10.resolve(dir, id), path10.join(dir, "_demo.tsx"), { skipSelf: true });
|
|
2217
|
+
}
|
|
2125
2218
|
if (bare === "virtual:dommief/routes" || bare === "virtual:dommief/site") return null;
|
|
2126
2219
|
if (id.endsWith(".md") || id.endsWith(".md?meta")) {
|
|
2127
|
-
const base = importer ?
|
|
2220
|
+
const base = importer ? path10.dirname(importer.replace(/\?meta$/, "")) : process.cwd();
|
|
2128
2221
|
const q = id.endsWith("?meta") ? "?meta" : "";
|
|
2129
2222
|
const clean = id.replace(/\?meta$/, "");
|
|
2130
|
-
return winPath(
|
|
2223
|
+
return winPath(path10.isAbsolute(clean) ? clean : path10.resolve(base, clean)) + q;
|
|
2131
2224
|
}
|
|
2132
2225
|
return null;
|
|
2133
2226
|
},
|
|
@@ -2135,7 +2228,7 @@ function dommiefPlugin(opts) {
|
|
|
2135
2228
|
if (id.startsWith("\0virtual:dommief-demo/")) {
|
|
2136
2229
|
const demoId2 = id.slice("\0virtual:dommief-demo/".length);
|
|
2137
2230
|
const source = demoRegistry.get(demoId2) ?? "export default function Demo(){ return null; }";
|
|
2138
|
-
return
|
|
2231
|
+
return jsx2(source, "demo-" + demoId2);
|
|
2139
2232
|
}
|
|
2140
2233
|
if (id === "\0virtual:dommief/entry") {
|
|
2141
2234
|
return { code: `import ${JSON.stringify(resolveClientFile("entry-client.tsx"))};`, map: null, moduleType: "js" };
|
|
@@ -2143,7 +2236,7 @@ function dommiefPlugin(opts) {
|
|
|
2143
2236
|
if (id.endsWith(".md") || id.endsWith(".md?meta")) {
|
|
2144
2237
|
const isMeta = id.endsWith("?meta");
|
|
2145
2238
|
const file = isMeta ? id.slice(0, -"?meta".length) : id;
|
|
2146
|
-
const code = await
|
|
2239
|
+
const code = await fs10.promises.readFile(file, "utf8");
|
|
2147
2240
|
const transformOpts = { hooks: state.host(), byFile: state.getTable().byFile, base: state.config().base, code: state.config().markdown };
|
|
2148
2241
|
let out;
|
|
2149
2242
|
let meta;
|
|
@@ -2168,7 +2261,8 @@ function dommiefPlugin(opts) {
|
|
|
2168
2261
|
hitDemos = hit.demos ?? [];
|
|
2169
2262
|
for (const d of hitDemos) {
|
|
2170
2263
|
demoRegistry.set(d.id, d.source);
|
|
2171
|
-
|
|
2264
|
+
demoDirs.set(d.id, path10.dirname(file));
|
|
2265
|
+
if (d.src) this.addWatchFile(path10.resolve(path10.dirname(file), d.src));
|
|
2172
2266
|
}
|
|
2173
2267
|
} else {
|
|
2174
2268
|
const r = await transformMarkdown(code, file, transformOpts);
|
|
@@ -2176,7 +2270,8 @@ function dommiefPlugin(opts) {
|
|
|
2176
2270
|
const nonCacheDemos = r.demos ?? [];
|
|
2177
2271
|
for (const d of nonCacheDemos) {
|
|
2178
2272
|
demoRegistry.set(d.id, d.source);
|
|
2179
|
-
|
|
2273
|
+
demoDirs.set(d.id, path10.dirname(file));
|
|
2274
|
+
if (d.src) this.addWatchFile(path10.resolve(path10.dirname(file), d.src));
|
|
2180
2275
|
}
|
|
2181
2276
|
}
|
|
2182
2277
|
for (const w of warnings) this.warn(w);
|
|
@@ -2185,7 +2280,7 @@ function dommiefPlugin(opts) {
|
|
|
2185
2280
|
if (isMeta) {
|
|
2186
2281
|
return { code: `export default ${JSON.stringify({ frontmatter: meta.frontmatter, toc: meta.toc })};`, map: null, moduleType: "js" };
|
|
2187
2282
|
}
|
|
2188
|
-
return
|
|
2283
|
+
return jsx2(out, file);
|
|
2189
2284
|
}
|
|
2190
2285
|
return null;
|
|
2191
2286
|
},
|
|
@@ -2193,7 +2288,7 @@ function dommiefPlugin(opts) {
|
|
|
2193
2288
|
transformIndexHtml: { order: "pre", handler(html) {
|
|
2194
2289
|
const title = state.config().title;
|
|
2195
2290
|
let out = html;
|
|
2196
|
-
if (!/<title>/.test(out)) out = out.replace("<head>", `<head><title>${title}</title>`);
|
|
2291
|
+
if (!/<title>/.test(out)) out = out.replace("<head>", `<head><title>${escapeXml(title)}</title>`);
|
|
2197
2292
|
const tags = state.host().collectHtmlHeadTags();
|
|
2198
2293
|
if (tags.length) out = out.replace("</head>", tags.join("\n") + "</head>");
|
|
2199
2294
|
const themeConfig = state.config().themeConfig;
|
|
@@ -2210,7 +2305,7 @@ function dommiefPlugin(opts) {
|
|
|
2210
2305
|
return out;
|
|
2211
2306
|
} },
|
|
2212
2307
|
configureServer(server) {
|
|
2213
|
-
const localThemeDir = winPath(
|
|
2308
|
+
const localThemeDir = winPath(path10.resolve(opts.root, ".dommief", "theme")) + "/";
|
|
2214
2309
|
const update = () => {
|
|
2215
2310
|
const aliases = buildAliasChain(opts.root, loaded.config);
|
|
2216
2311
|
server.config.resolve.alias = { ...server.config.resolve.alias, ...aliases };
|
|
@@ -2251,8 +2346,8 @@ import { build } from "vite";
|
|
|
2251
2346
|
init_errors();
|
|
2252
2347
|
|
|
2253
2348
|
// src/link-check.ts
|
|
2254
|
-
import
|
|
2255
|
-
import
|
|
2349
|
+
import fs11 from "fs";
|
|
2350
|
+
import path11 from "path";
|
|
2256
2351
|
var HREF_RE = /<a\b[^>]*\shref="([^"]*)"[^>]*>/g;
|
|
2257
2352
|
var ASSET_RE = /<(?:img|link)\b[^>]*\s(?:src|href)="([^"]*)"[^>]*>/g;
|
|
2258
2353
|
var LINE_RE = /\sdata-src-line="(\d+)"/;
|
|
@@ -2287,8 +2382,8 @@ function check(refs, opts) {
|
|
|
2287
2382
|
const cleanPath = (rawPath ?? "").split("?")[0];
|
|
2288
2383
|
if (isAsset) {
|
|
2289
2384
|
const relPath = stripBase(cleanPath, opts.base) || "/";
|
|
2290
|
-
const onDisk =
|
|
2291
|
-
if (!
|
|
2385
|
+
const onDisk = path11.join(opts.outDir, decodeURIComponent(relPath));
|
|
2386
|
+
if (!fs11.existsSync(onDisk)) issues.push({ code: "link_asset_missing", ref });
|
|
2292
2387
|
continue;
|
|
2293
2388
|
}
|
|
2294
2389
|
const routePath = stripBase(cleanPath, opts.base) || "/";
|
|
@@ -2314,78 +2409,6 @@ function formatIssues(issues) {
|
|
|
2314
2409
|
return lines.join("\n");
|
|
2315
2410
|
}
|
|
2316
2411
|
|
|
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
2412
|
// src/build.ts
|
|
2390
2413
|
var ROOT_PLACEHOLDER = /<div id="root"><\/div>/;
|
|
2391
2414
|
async function mapLimit(items, limit, fn) {
|
|
@@ -2501,7 +2524,12 @@ async function runBuild({ root, config, host, log }) {
|
|
|
2501
2524
|
const linkRefs = [];
|
|
2502
2525
|
await timed("prerender", async () => {
|
|
2503
2526
|
await mapLimit(plan.prerender, config.ssg.concurrency, async (r) => {
|
|
2504
|
-
|
|
2527
|
+
let html;
|
|
2528
|
+
try {
|
|
2529
|
+
html = await render(r.path);
|
|
2530
|
+
} catch (e) {
|
|
2531
|
+
throw new DommiefError(`prerender ${r.path}: ${formatError(e)}`, { code: "ssg_render_failed" });
|
|
2532
|
+
}
|
|
2505
2533
|
if (linkCheckEnabled) linkRefs.push(...collect(r.id, html, fileOf(r.id)));
|
|
2506
2534
|
writePage(r, wrap(html, r.path));
|
|
2507
2535
|
});
|
|
@@ -2604,7 +2632,14 @@ function spaFallbackPlugin(opts) {
|
|
|
2604
2632
|
name: "dommief:preview-spa-fallback",
|
|
2605
2633
|
configurePreviewServer(s) {
|
|
2606
2634
|
s.middlewares.use((req, res, next) => {
|
|
2607
|
-
|
|
2635
|
+
let urlPath;
|
|
2636
|
+
try {
|
|
2637
|
+
urlPath = decodeURIComponent((req.url ?? "/").split("?")[0] ?? "/");
|
|
2638
|
+
} catch {
|
|
2639
|
+
res.statusCode = 400;
|
|
2640
|
+
res.end("Invalid URL");
|
|
2641
|
+
return;
|
|
2642
|
+
}
|
|
2608
2643
|
if (req.method !== "GET") return next();
|
|
2609
2644
|
if (base && urlPath !== base && !urlPath.startsWith(base + "/")) {
|
|
2610
2645
|
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
|
}
|