nrdocs 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.mjs +419 -275
- package/dist/deploy-worker/index.js +2458 -0
- package/dist/deploy-worker/migrations/0001_initial_schema.sql +98 -0
- package/dist/deploy-worker/migrations/0002_repo_owner_password_optin.sql +3 -0
- package/dist/deploy-worker/migrations/0003_rule_self_password_default.sql +3 -0
- package/package.json +5 -4
package/dist/bin.mjs
CHANGED
|
@@ -599,9 +599,292 @@ function authLogout(opts = {}) {
|
|
|
599
599
|
}
|
|
600
600
|
|
|
601
601
|
// src/commands/init.ts
|
|
602
|
+
import * as fs4 from "node:fs";
|
|
603
|
+
import * as path5 from "node:path";
|
|
604
|
+
import * as readline2 from "node:readline";
|
|
605
|
+
|
|
606
|
+
// src/config/docs-config.ts
|
|
607
|
+
import * as fs3 from "node:fs";
|
|
608
|
+
import * as path4 from "node:path";
|
|
609
|
+
import YAML from "yaml";
|
|
610
|
+
|
|
611
|
+
// src/renderer/navigation.ts
|
|
602
612
|
import * as fs2 from "node:fs";
|
|
603
613
|
import * as path3 from "node:path";
|
|
604
|
-
|
|
614
|
+
function extractTitle(markdownContent, filePath) {
|
|
615
|
+
const match2 = markdownContent.match(/^#\s+(.+)$/m);
|
|
616
|
+
if (match2) {
|
|
617
|
+
return match2[1].trim();
|
|
618
|
+
}
|
|
619
|
+
const basename3 = path3.basename(filePath, ".md");
|
|
620
|
+
if (basename3 === "index") {
|
|
621
|
+
return "Home";
|
|
622
|
+
}
|
|
623
|
+
return basename3.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
624
|
+
}
|
|
625
|
+
function findMarkdownFiles(dir, relativeTo) {
|
|
626
|
+
const results = [];
|
|
627
|
+
if (!fs2.existsSync(dir)) return results;
|
|
628
|
+
const entries = fs2.readdirSync(dir, { withFileTypes: true });
|
|
629
|
+
for (const entry of entries) {
|
|
630
|
+
const fullPath = path3.join(dir, entry.name);
|
|
631
|
+
if (entry.isDirectory()) {
|
|
632
|
+
results.push(...findMarkdownFiles(fullPath, relativeTo));
|
|
633
|
+
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
634
|
+
results.push(path3.relative(relativeTo, fullPath).replace(/\\/g, "/"));
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
return results;
|
|
638
|
+
}
|
|
639
|
+
function sortNavPaths(files, indexPath = "index.md") {
|
|
640
|
+
const normalizedIndex = indexPath.replace(/\\/g, "/");
|
|
641
|
+
const sorted = [...files].sort((a, b) => a.localeCompare(b, void 0, { numeric: true }));
|
|
642
|
+
const indexIdx = sorted.indexOf(normalizedIndex);
|
|
643
|
+
if (indexIdx <= 0) return sorted;
|
|
644
|
+
const without = sorted.filter((f) => f !== normalizedIndex);
|
|
645
|
+
return [normalizedIndex, ...without];
|
|
646
|
+
}
|
|
647
|
+
function mdPathToHref(filePath) {
|
|
648
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
649
|
+
const withoutExt = normalized.replace(/\.md$/, "");
|
|
650
|
+
if (withoutExt === "index" || withoutExt.endsWith("/index")) {
|
|
651
|
+
const dir = withoutExt === "index" ? "" : withoutExt.slice(0, -"/index".length);
|
|
652
|
+
return dir ? `${dir}/` : "";
|
|
653
|
+
}
|
|
654
|
+
return `${withoutExt}/`;
|
|
655
|
+
}
|
|
656
|
+
function navEntryFromFile(contentDir, file) {
|
|
657
|
+
const fullPath = path3.join(contentDir, file);
|
|
658
|
+
const content = fs2.readFileSync(fullPath, "utf-8");
|
|
659
|
+
return {
|
|
660
|
+
title: extractTitle(content, file),
|
|
661
|
+
path: file
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
function folderSegmentToTitle(segment) {
|
|
665
|
+
if (segment === "index") return "Home";
|
|
666
|
+
return segment.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
667
|
+
}
|
|
668
|
+
function groupNavEntriesByFolders(files, contentDir, indexPath = "index.md") {
|
|
669
|
+
const sorted = sortNavPaths(files, indexPath);
|
|
670
|
+
const rootLinks = [];
|
|
671
|
+
const byFolder = /* @__PURE__ */ new Map();
|
|
672
|
+
for (const file of sorted) {
|
|
673
|
+
const dir = path3.dirname(file).replace(/\\/g, "/");
|
|
674
|
+
if (dir === ".") {
|
|
675
|
+
rootLinks.push(navEntryFromFile(contentDir, file));
|
|
676
|
+
continue;
|
|
677
|
+
}
|
|
678
|
+
const top = dir.split("/")[0];
|
|
679
|
+
const list2 = byFolder.get(top) ?? [];
|
|
680
|
+
list2.push(file);
|
|
681
|
+
byFolder.set(top, list2);
|
|
682
|
+
}
|
|
683
|
+
const sections = [];
|
|
684
|
+
for (const folder of [...byFolder.keys()].sort(
|
|
685
|
+
(a, b) => a.localeCompare(b, void 0, { numeric: true })
|
|
686
|
+
)) {
|
|
687
|
+
const paths = sortNavPaths(byFolder.get(folder), indexPath);
|
|
688
|
+
sections.push({
|
|
689
|
+
title: folderSegmentToTitle(folder),
|
|
690
|
+
children: paths.map((f) => navEntryFromFile(contentDir, f))
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
return [...rootLinks, ...sections];
|
|
694
|
+
}
|
|
695
|
+
function discoverNavEntries(contentDir, options) {
|
|
696
|
+
const indexPath = (options?.indexPath ?? "index.md").replace(/\\/g, "/");
|
|
697
|
+
const files = findMarkdownFiles(contentDir, contentDir);
|
|
698
|
+
return groupNavEntriesByFolders(files, contentDir, indexPath);
|
|
699
|
+
}
|
|
700
|
+
function navConfigToNavItems(entries, contentDir) {
|
|
701
|
+
const items = [];
|
|
702
|
+
const walk = (list2) => {
|
|
703
|
+
for (const entry of list2) {
|
|
704
|
+
if (entry.path) {
|
|
705
|
+
const normalizedPath = entry.path.replace(/\\/g, "/");
|
|
706
|
+
items.push({
|
|
707
|
+
title: entry.title,
|
|
708
|
+
path: normalizedPath,
|
|
709
|
+
href: mdPathToHref(normalizedPath)
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
if (entry.children?.length) {
|
|
713
|
+
walk(entry.children);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
};
|
|
717
|
+
walk(entries);
|
|
718
|
+
if (items.length === 0) {
|
|
719
|
+
throw new Error("Nav has no pages (entries need path or nested children with paths)");
|
|
720
|
+
}
|
|
721
|
+
for (const item of items) {
|
|
722
|
+
const full = path3.join(contentDir, item.path);
|
|
723
|
+
if (!fs2.existsSync(full)) {
|
|
724
|
+
throw new Error(`Nav path not found: ${item.path}`);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
return items;
|
|
728
|
+
}
|
|
729
|
+
function navConfigToSidebar(entries, activePath) {
|
|
730
|
+
const normalizedActive = activePath?.replace(/\\/g, "/");
|
|
731
|
+
const mapEntry = (entry) => {
|
|
732
|
+
const children = entry.children?.length ? entry.children.map(mapEntry).filter((n) => n !== null) : [];
|
|
733
|
+
if (entry.path) {
|
|
734
|
+
const normalizedPath = entry.path.replace(/\\/g, "/");
|
|
735
|
+
return {
|
|
736
|
+
kind: "link",
|
|
737
|
+
title: entry.title,
|
|
738
|
+
path: normalizedPath,
|
|
739
|
+
href: mdPathToHref(normalizedPath),
|
|
740
|
+
active: normalizedActive === normalizedPath
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
if (children.length === 0) return null;
|
|
744
|
+
const open = normalizedActive ? children.some((c) => sidebarContainsActive(c, normalizedActive)) : true;
|
|
745
|
+
return { kind: "section", title: entry.title, children, open };
|
|
746
|
+
};
|
|
747
|
+
return entries.map(mapEntry).filter((n) => n !== null);
|
|
748
|
+
}
|
|
749
|
+
function sidebarContainsActive(entry, activePath) {
|
|
750
|
+
if (entry.kind === "link") return entry.path === activePath;
|
|
751
|
+
return entry.children.some((c) => sidebarContainsActive(c, activePath));
|
|
752
|
+
}
|
|
753
|
+
function flattenNavPaths(entries) {
|
|
754
|
+
const paths = [];
|
|
755
|
+
const walk = (list2) => {
|
|
756
|
+
for (const e of list2) {
|
|
757
|
+
if (e.path) paths.push(e.path.replace(/\\/g, "/"));
|
|
758
|
+
if (e.children?.length) walk(e.children);
|
|
759
|
+
}
|
|
760
|
+
};
|
|
761
|
+
walk(entries);
|
|
762
|
+
return paths;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// src/config/docs-config.ts
|
|
766
|
+
function loadDocsConfig(docsDir) {
|
|
767
|
+
const configPath = path4.resolve(docsDir, "nrdocs.yml");
|
|
768
|
+
if (!fs3.existsSync(configPath)) {
|
|
769
|
+
throw new Error(`Config file not found: ${configPath}`);
|
|
770
|
+
}
|
|
771
|
+
const raw = fs3.readFileSync(configPath, "utf-8");
|
|
772
|
+
const config2 = YAML.parse(raw);
|
|
773
|
+
if (!config2 || typeof config2 !== "object") {
|
|
774
|
+
throw new Error(`Invalid config: ${configPath}`);
|
|
775
|
+
}
|
|
776
|
+
const sourceDir = config2.content?.source_dir ?? ".";
|
|
777
|
+
const contentDir = path4.resolve(docsDir, sourceDir);
|
|
778
|
+
return { config: config2, configPath, contentDir };
|
|
779
|
+
}
|
|
780
|
+
function hasExplicitNav(config2) {
|
|
781
|
+
return Array.isArray(config2.content?.nav);
|
|
782
|
+
}
|
|
783
|
+
function parseNavEntries(nav) {
|
|
784
|
+
if (!Array.isArray(nav)) {
|
|
785
|
+
throw new Error('content.nav must be a list or "auto"');
|
|
786
|
+
}
|
|
787
|
+
const entries = [];
|
|
788
|
+
for (const item of nav) {
|
|
789
|
+
if (!item || typeof item !== "object") {
|
|
790
|
+
throw new Error("Each nav entry must be an object with title");
|
|
791
|
+
}
|
|
792
|
+
const rec = item;
|
|
793
|
+
if (typeof rec["title"] !== "string") {
|
|
794
|
+
throw new Error("Each nav entry must have a title string");
|
|
795
|
+
}
|
|
796
|
+
const hasPath = typeof rec["path"] === "string";
|
|
797
|
+
const hasChildren = Array.isArray(rec["children"]) && rec["children"].length > 0;
|
|
798
|
+
if (!hasPath && !hasChildren) {
|
|
799
|
+
throw new Error("Each nav entry needs path and/or children");
|
|
800
|
+
}
|
|
801
|
+
const entry = { title: rec["title"] };
|
|
802
|
+
if (hasPath) {
|
|
803
|
+
entry.path = rec["path"].replace(/\\/g, "/");
|
|
804
|
+
}
|
|
805
|
+
if (Array.isArray(rec["children"])) {
|
|
806
|
+
entry.children = parseNavEntries(rec["children"]);
|
|
807
|
+
}
|
|
808
|
+
entries.push(entry);
|
|
809
|
+
}
|
|
810
|
+
return entries;
|
|
811
|
+
}
|
|
812
|
+
function getExplicitNav(config2) {
|
|
813
|
+
const nav = config2.content?.nav;
|
|
814
|
+
if (nav === void 0 || nav === "auto") return null;
|
|
815
|
+
if (Array.isArray(nav)) return parseNavEntries(nav);
|
|
816
|
+
throw new Error('content.nav must be "auto" or a list of entries');
|
|
817
|
+
}
|
|
818
|
+
function validateNavPaths(entries, contentDir) {
|
|
819
|
+
const errors = [];
|
|
820
|
+
const seen = /* @__PURE__ */ new Set();
|
|
821
|
+
const walk = (list2) => {
|
|
822
|
+
for (const e of list2) {
|
|
823
|
+
if (!e.path && !e.children?.length) {
|
|
824
|
+
errors.push(`Nav entry "${e.title}" has no path or children`);
|
|
825
|
+
continue;
|
|
826
|
+
}
|
|
827
|
+
if (e.path) {
|
|
828
|
+
const p = e.path.replace(/\\/g, "/");
|
|
829
|
+
if (seen.has(p)) {
|
|
830
|
+
errors.push(`Duplicate nav path: ${p}`);
|
|
831
|
+
}
|
|
832
|
+
seen.add(p);
|
|
833
|
+
const full = path4.join(contentDir, p);
|
|
834
|
+
if (!fs3.existsSync(full)) {
|
|
835
|
+
errors.push(`Nav path not found: ${p}`);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
if (e.children?.length) walk(e.children);
|
|
839
|
+
}
|
|
840
|
+
};
|
|
841
|
+
walk(entries);
|
|
842
|
+
return { valid: errors.length === 0, errors };
|
|
843
|
+
}
|
|
844
|
+
function resolveContentIndex(navEntries, indexPath = "index.md") {
|
|
845
|
+
const paths = flattenNavPaths(navEntries);
|
|
846
|
+
const preferred = indexPath.replace(/\\/g, "/");
|
|
847
|
+
if (paths.includes(preferred)) return preferred;
|
|
848
|
+
return paths[0];
|
|
849
|
+
}
|
|
850
|
+
function writeNavToConfig(configPath, navEntries, options) {
|
|
851
|
+
const generatedBy = options?.generatedBy ?? "nrdocs nav generate";
|
|
852
|
+
let raw = fs3.readFileSync(configPath, "utf-8");
|
|
853
|
+
raw = raw.replace(/^# content\.nav generated by: .+\n/gm, "");
|
|
854
|
+
const config2 = YAML.parse(raw);
|
|
855
|
+
if (!config2 || typeof config2 !== "object") {
|
|
856
|
+
throw new Error(`Invalid config: ${configPath}`);
|
|
857
|
+
}
|
|
858
|
+
if (!config2.content) {
|
|
859
|
+
config2.content = {};
|
|
860
|
+
}
|
|
861
|
+
config2.content.nav = navEntries;
|
|
862
|
+
config2.content.index = resolveContentIndex(navEntries, options?.indexPath);
|
|
863
|
+
const doc = new YAML.Document(config2);
|
|
864
|
+
const header = `# content.nav generated by: ${generatedBy}
|
|
865
|
+
`;
|
|
866
|
+
const body = doc.toString();
|
|
867
|
+
fs3.writeFileSync(configPath, header + body, "utf-8");
|
|
868
|
+
}
|
|
869
|
+
function generateNavInConfig(docsDir, options) {
|
|
870
|
+
const loaded = loadDocsConfig(docsDir);
|
|
871
|
+
const indexPath = options?.indexPath ?? loaded.config.content?.index ?? "index.md";
|
|
872
|
+
const entries = discoverNavEntries(loaded.contentDir, { indexPath });
|
|
873
|
+
const pageCount = flattenNavPaths(entries).length;
|
|
874
|
+
if (pageCount === 0) return 0;
|
|
875
|
+
writeNavToConfig(loaded.configPath, entries, { ...options, indexPath });
|
|
876
|
+
return pageCount;
|
|
877
|
+
}
|
|
878
|
+
function formatNavYaml(navEntries) {
|
|
879
|
+
const partial = {
|
|
880
|
+
content: {
|
|
881
|
+
nav: navEntries
|
|
882
|
+
}
|
|
883
|
+
};
|
|
884
|
+
return YAML.stringify(partial).trimEnd();
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
// src/commands/init.ts
|
|
605
888
|
async function prompt2(question, defaultValue) {
|
|
606
889
|
const rl = readline2.createInterface({
|
|
607
890
|
input: process.stdin,
|
|
@@ -622,7 +905,8 @@ site:
|
|
|
622
905
|
api_url: ${apiUrl}
|
|
623
906
|
|
|
624
907
|
content:
|
|
625
|
-
|
|
908
|
+
source_dir: .
|
|
909
|
+
nav: auto
|
|
626
910
|
`;
|
|
627
911
|
if (requestedAccess) {
|
|
628
912
|
yml += `
|
|
@@ -632,16 +916,6 @@ request:
|
|
|
632
916
|
}
|
|
633
917
|
return yml;
|
|
634
918
|
}
|
|
635
|
-
function generateIndexMd(title) {
|
|
636
|
-
return `# ${title}
|
|
637
|
-
|
|
638
|
-
Welcome to your documentation site powered by nrdocs.
|
|
639
|
-
|
|
640
|
-
## Getting Started
|
|
641
|
-
|
|
642
|
-
Edit this file to add your documentation content.
|
|
643
|
-
`;
|
|
644
|
-
}
|
|
645
919
|
function generateWorkflowYml(docsDir, apiUrl) {
|
|
646
920
|
return `name: Publish Docs (nrdocs)
|
|
647
921
|
|
|
@@ -717,9 +991,9 @@ function normalizeUrl(url) {
|
|
|
717
991
|
return normalized;
|
|
718
992
|
}
|
|
719
993
|
function readExistingConfig(configPath) {
|
|
720
|
-
if (!
|
|
994
|
+
if (!fs4.existsSync(configPath)) return {};
|
|
721
995
|
try {
|
|
722
|
-
const content =
|
|
996
|
+
const content = fs4.readFileSync(configPath, "utf-8");
|
|
723
997
|
const titleMatch = content.match(/(?:title|name):\s*["']?([^"'\n]+)["']?/);
|
|
724
998
|
const apiMatch = content.match(/api_url:\s*["']?([^"'\n]+)["']?/);
|
|
725
999
|
return {
|
|
@@ -737,11 +1011,11 @@ async function handleInit(args2) {
|
|
|
737
1011
|
process.exit(2);
|
|
738
1012
|
}
|
|
739
1013
|
const docsDir = opts.docsDir || "docs";
|
|
740
|
-
const docsPath =
|
|
741
|
-
const configFile =
|
|
1014
|
+
const docsPath = path5.resolve(docsDir);
|
|
1015
|
+
const configFile = path5.join(docsPath, "nrdocs.yml");
|
|
742
1016
|
const existing = readExistingConfig(configFile);
|
|
743
|
-
const configExists =
|
|
744
|
-
const dirName =
|
|
1017
|
+
const configExists = fs4.existsSync(configFile);
|
|
1018
|
+
const dirName = path5.basename(process.cwd());
|
|
745
1019
|
let title = opts.title || existing.title;
|
|
746
1020
|
if (!title) {
|
|
747
1021
|
title = await prompt2("Site title", `${dirName} Docs`);
|
|
@@ -774,43 +1048,46 @@ async function handleInit(args2) {
|
|
|
774
1048
|
process.exit(2);
|
|
775
1049
|
}
|
|
776
1050
|
apiUrl = normalizeUrl(apiUrl);
|
|
777
|
-
const
|
|
778
|
-
const
|
|
779
|
-
|
|
780
|
-
if (!opts.force && fs2.existsSync(workflowFile)) {
|
|
1051
|
+
const workflowDir = path5.resolve(".github", "workflows");
|
|
1052
|
+
const workflowFile = path5.join(workflowDir, "nrdocs.yml");
|
|
1053
|
+
if (!opts.force && fs4.existsSync(workflowFile)) {
|
|
781
1054
|
console.error("Error: Workflow already exists:");
|
|
782
1055
|
console.error(` ${workflowFile}`);
|
|
783
1056
|
console.error("Use --force to overwrite.");
|
|
784
1057
|
process.exit(3);
|
|
785
1058
|
}
|
|
786
|
-
|
|
787
|
-
|
|
1059
|
+
fs4.mkdirSync(docsPath, { recursive: true });
|
|
1060
|
+
fs4.mkdirSync(workflowDir, { recursive: true });
|
|
788
1061
|
const createdConfig = !configExists || opts.force;
|
|
789
1062
|
if (createdConfig) {
|
|
790
|
-
|
|
1063
|
+
fs4.writeFileSync(configFile, generateNrdocsYml(title, apiUrl, opts.requestedAccess));
|
|
791
1064
|
}
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
1065
|
+
fs4.writeFileSync(workflowFile, generateWorkflowYml(docsDir, apiUrl));
|
|
1066
|
+
let navPageCount = 0;
|
|
1067
|
+
if (createdConfig || opts.force) {
|
|
1068
|
+
navPageCount = generateNavInConfig(docsDir, { generatedBy: "nrdocs init" });
|
|
795
1069
|
}
|
|
796
|
-
fs2.writeFileSync(workflowFile, generateWorkflowYml(docsDir, apiUrl));
|
|
797
1070
|
console.log("nrdocs initialized successfully!");
|
|
798
1071
|
console.log("");
|
|
799
1072
|
console.log("Created/updated:");
|
|
800
1073
|
if (createdConfig) {
|
|
801
|
-
console.log(` ${
|
|
802
|
-
}
|
|
803
|
-
if (createdIndex) {
|
|
804
|
-
console.log(` ${path3.relative(process.cwd(), indexFile)}`);
|
|
1074
|
+
console.log(` ${path5.relative(process.cwd(), configFile)}`);
|
|
805
1075
|
}
|
|
806
|
-
console.log(` ${
|
|
1076
|
+
console.log(` ${path5.relative(process.cwd(), workflowFile)}`);
|
|
807
1077
|
if (!createdConfig) {
|
|
808
1078
|
console.log("");
|
|
809
|
-
console.log(`Using existing: ${
|
|
1079
|
+
console.log(`Using existing: ${path5.relative(process.cwd(), configFile)}`);
|
|
1080
|
+
}
|
|
1081
|
+
if (navPageCount > 0) {
|
|
1082
|
+
console.log(` content.nav: ${navPageCount} page(s) from markdown under ${docsDir}/`);
|
|
810
1083
|
}
|
|
811
1084
|
console.log("");
|
|
812
1085
|
console.log("Next steps:");
|
|
813
|
-
|
|
1086
|
+
if (navPageCount === 0) {
|
|
1087
|
+
console.log(` 1. Add markdown files under ${docsDir}/, then run: nrdocs nav generate`);
|
|
1088
|
+
} else {
|
|
1089
|
+
console.log(` 1. Edit content.nav in ${path5.relative(process.cwd(), configFile)} to reorder pages`);
|
|
1090
|
+
}
|
|
814
1091
|
console.log(" 2. Commit and push to trigger the workflow");
|
|
815
1092
|
console.log(" 3. Ask your operator to approve the repo");
|
|
816
1093
|
}
|
|
@@ -819,8 +1096,8 @@ async function handleInit(args2) {
|
|
|
819
1096
|
import * as fs8 from "node:fs";
|
|
820
1097
|
|
|
821
1098
|
// src/renderer/index.ts
|
|
822
|
-
import * as
|
|
823
|
-
import * as
|
|
1099
|
+
import * as fs7 from "node:fs";
|
|
1100
|
+
import * as path8 from "node:path";
|
|
824
1101
|
|
|
825
1102
|
// ../../node_modules/.pnpm/markdown-it@14.1.1/node_modules/markdown-it/lib/common/utils.mjs
|
|
826
1103
|
var utils_exports = {};
|
|
@@ -6022,97 +6299,6 @@ function renderMarkdown(content) {
|
|
|
6022
6299
|
return md.render(content);
|
|
6023
6300
|
}
|
|
6024
6301
|
|
|
6025
|
-
// src/renderer/navigation.ts
|
|
6026
|
-
import * as fs3 from "node:fs";
|
|
6027
|
-
import * as path4 from "node:path";
|
|
6028
|
-
function extractTitle(markdownContent, filePath) {
|
|
6029
|
-
const match2 = markdownContent.match(/^#\s+(.+)$/m);
|
|
6030
|
-
if (match2) {
|
|
6031
|
-
return match2[1].trim();
|
|
6032
|
-
}
|
|
6033
|
-
const basename3 = path4.basename(filePath, ".md");
|
|
6034
|
-
if (basename3 === "index") {
|
|
6035
|
-
return "Home";
|
|
6036
|
-
}
|
|
6037
|
-
return basename3.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
6038
|
-
}
|
|
6039
|
-
function findMarkdownFiles(dir, relativeTo) {
|
|
6040
|
-
const results = [];
|
|
6041
|
-
if (!fs3.existsSync(dir)) return results;
|
|
6042
|
-
const entries = fs3.readdirSync(dir, { withFileTypes: true });
|
|
6043
|
-
for (const entry of entries) {
|
|
6044
|
-
const fullPath = path4.join(dir, entry.name);
|
|
6045
|
-
if (entry.isDirectory()) {
|
|
6046
|
-
results.push(...findMarkdownFiles(fullPath, relativeTo));
|
|
6047
|
-
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
6048
|
-
results.push(path4.relative(relativeTo, fullPath).replace(/\\/g, "/"));
|
|
6049
|
-
}
|
|
6050
|
-
}
|
|
6051
|
-
return results;
|
|
6052
|
-
}
|
|
6053
|
-
function sortNavPaths(files, indexPath = "index.md") {
|
|
6054
|
-
const normalizedIndex = indexPath.replace(/\\/g, "/");
|
|
6055
|
-
const sorted = [...files].sort((a, b) => a.localeCompare(b, void 0, { numeric: true }));
|
|
6056
|
-
const indexIdx = sorted.indexOf(normalizedIndex);
|
|
6057
|
-
if (indexIdx <= 0) return sorted;
|
|
6058
|
-
const without = sorted.filter((f) => f !== normalizedIndex);
|
|
6059
|
-
return [normalizedIndex, ...without];
|
|
6060
|
-
}
|
|
6061
|
-
function mdPathToHref(filePath) {
|
|
6062
|
-
const normalized = filePath.replace(/\\/g, "/");
|
|
6063
|
-
const withoutExt = normalized.replace(/\.md$/, "");
|
|
6064
|
-
if (withoutExt === "index" || withoutExt.endsWith("/index")) {
|
|
6065
|
-
const dir = withoutExt === "index" ? "" : withoutExt.slice(0, -"/index".length);
|
|
6066
|
-
return dir ? `${dir}/` : "";
|
|
6067
|
-
}
|
|
6068
|
-
return `${withoutExt}/`;
|
|
6069
|
-
}
|
|
6070
|
-
function discoverNavEntries(contentDir, options) {
|
|
6071
|
-
const indexPath = (options?.indexPath ?? "index.md").replace(/\\/g, "/");
|
|
6072
|
-
const files = findMarkdownFiles(contentDir, contentDir);
|
|
6073
|
-
const sorted = sortNavPaths(files, indexPath);
|
|
6074
|
-
return sorted.map((file) => {
|
|
6075
|
-
const fullPath = path4.join(contentDir, file);
|
|
6076
|
-
const content = fs3.readFileSync(fullPath, "utf-8");
|
|
6077
|
-
return {
|
|
6078
|
-
title: extractTitle(content, file),
|
|
6079
|
-
path: file
|
|
6080
|
-
};
|
|
6081
|
-
});
|
|
6082
|
-
}
|
|
6083
|
-
function navConfigToNavItems(entries, contentDir) {
|
|
6084
|
-
const items = [];
|
|
6085
|
-
const walk = (list2) => {
|
|
6086
|
-
for (const entry of list2) {
|
|
6087
|
-
const normalizedPath = entry.path.replace(/\\/g, "/");
|
|
6088
|
-
items.push({
|
|
6089
|
-
title: entry.title,
|
|
6090
|
-
path: normalizedPath,
|
|
6091
|
-
href: mdPathToHref(normalizedPath)
|
|
6092
|
-
});
|
|
6093
|
-
if (entry.children?.length) {
|
|
6094
|
-
walk(entry.children);
|
|
6095
|
-
}
|
|
6096
|
-
}
|
|
6097
|
-
};
|
|
6098
|
-
walk(entries);
|
|
6099
|
-
for (const item of items) {
|
|
6100
|
-
const full = path4.join(contentDir, item.path);
|
|
6101
|
-
if (!fs3.existsSync(full)) {
|
|
6102
|
-
throw new Error(`Nav path not found: ${item.path}`);
|
|
6103
|
-
}
|
|
6104
|
-
}
|
|
6105
|
-
return items;
|
|
6106
|
-
}
|
|
6107
|
-
function generateAutoNav(docsDir, indexPath = "index.md") {
|
|
6108
|
-
const entries = discoverNavEntries(docsDir, { indexPath });
|
|
6109
|
-
return entries.map((e) => ({
|
|
6110
|
-
title: e.title,
|
|
6111
|
-
path: e.path,
|
|
6112
|
-
href: mdPathToHref(e.path)
|
|
6113
|
-
}));
|
|
6114
|
-
}
|
|
6115
|
-
|
|
6116
6302
|
// src/renderer/links.ts
|
|
6117
6303
|
function rewriteLinks(html, basePath, owner, repo) {
|
|
6118
6304
|
return html.replace(/<a\s+([^>]*?)href="([^"]*)"([^>]*?)>/g, (_match, before, href, after) => {
|
|
@@ -6282,6 +6468,13 @@ nav.sidebar .site-title{font-weight:700;font-size:1.1rem;color:var(--text);flex:
|
|
|
6282
6468
|
#theme-toggle:hover{background:var(--bg-active)}
|
|
6283
6469
|
nav.sidebar ul{list-style:none}
|
|
6284
6470
|
nav.sidebar li{margin-bottom:0.25rem}
|
|
6471
|
+
nav.sidebar .nav-section{margin-top:0.5rem}
|
|
6472
|
+
nav.sidebar .nav-section>summary{cursor:pointer;font-size:0.8rem;font-weight:600;text-transform:uppercase;letter-spacing:0.04em;color:var(--toc-title);padding:0.35rem 0.6rem;list-style:none;border-radius:4px;user-select:none}
|
|
6473
|
+
nav.sidebar .nav-section>summary::-webkit-details-marker{display:none}
|
|
6474
|
+
nav.sidebar .nav-section>summary::before{content:"\u25B8";display:inline-block;margin-right:0.35rem;transition:transform 0.15s ease}
|
|
6475
|
+
nav.sidebar .nav-section[open]>summary::before{transform:rotate(90deg)}
|
|
6476
|
+
nav.sidebar .nav-section>summary:hover{background:var(--bg-hover);color:var(--text)}
|
|
6477
|
+
nav.sidebar .nav-section ul{padding-left:0.25rem;margin-top:0.15rem;margin-bottom:0.35rem}
|
|
6285
6478
|
nav.sidebar a{color:var(--text-muted);text-decoration:none;padding:0.3rem 0.6rem;display:block;border-radius:4px;font-size:0.95rem}
|
|
6286
6479
|
nav.sidebar a:hover{background:var(--bg-hover);color:var(--text)}
|
|
6287
6480
|
nav.sidebar a.active{background:var(--bg-active);color:var(--link-active);font-weight:500}
|
|
@@ -6323,7 +6516,7 @@ footer a{color:var(--link)}
|
|
|
6323
6516
|
<button type="button" id="theme-toggle" aria-label="Toggle color theme" title="Toggle light/dark mode">☽</button>
|
|
6324
6517
|
</div>
|
|
6325
6518
|
<ul>
|
|
6326
|
-
${
|
|
6519
|
+
${renderNavTree(nav, baseUrl)}
|
|
6327
6520
|
</ul>
|
|
6328
6521
|
</nav>
|
|
6329
6522
|
<div class="content-wrapper">
|
|
@@ -6374,22 +6567,30 @@ ${items}
|
|
|
6374
6567
|
</ul>
|
|
6375
6568
|
</aside>`;
|
|
6376
6569
|
}
|
|
6377
|
-
function
|
|
6378
|
-
return
|
|
6379
|
-
|
|
6380
|
-
|
|
6381
|
-
|
|
6382
|
-
|
|
6570
|
+
function renderNavTree(entries, baseUrl) {
|
|
6571
|
+
return entries.map((entry) => renderNavNode(entry, baseUrl)).join("\n");
|
|
6572
|
+
}
|
|
6573
|
+
function renderNavNode(entry, baseUrl) {
|
|
6574
|
+
if (entry.kind === "link") {
|
|
6575
|
+
const href = baseUrl + entry.href;
|
|
6576
|
+
const activeClass = entry.active ? ' class="active"' : "";
|
|
6577
|
+
return `<li><a href="${escapeHtml2(href)}"${activeClass}>${escapeHtml2(entry.title)}</a></li>`;
|
|
6578
|
+
}
|
|
6579
|
+
const openAttr = entry.open !== false ? " open" : "";
|
|
6580
|
+
const childItems = entry.children.map((c) => renderNavNode(c, baseUrl)).join("\n");
|
|
6581
|
+
return `<li class="nav-section"><details class="nav-details"${openAttr}><summary>${escapeHtml2(entry.title)}</summary><ul>
|
|
6582
|
+
${childItems}
|
|
6583
|
+
</ul></details></li>`;
|
|
6383
6584
|
}
|
|
6384
6585
|
function escapeHtml2(str) {
|
|
6385
6586
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
6386
6587
|
}
|
|
6387
6588
|
|
|
6388
6589
|
// src/renderer/assets.ts
|
|
6389
|
-
import * as
|
|
6390
|
-
import * as
|
|
6590
|
+
import * as fs5 from "node:fs";
|
|
6591
|
+
import * as path6 from "node:path";
|
|
6391
6592
|
function collectAssets(docsDir) {
|
|
6392
|
-
const resolvedDocsDir =
|
|
6593
|
+
const resolvedDocsDir = path6.resolve(docsDir);
|
|
6393
6594
|
const files = [];
|
|
6394
6595
|
collectFromDir(resolvedDocsDir, resolvedDocsDir, files);
|
|
6395
6596
|
return files;
|
|
@@ -6397,13 +6598,13 @@ function collectAssets(docsDir) {
|
|
|
6397
6598
|
function collectFromDir(dir, rootDir, results) {
|
|
6398
6599
|
let entries;
|
|
6399
6600
|
try {
|
|
6400
|
-
entries =
|
|
6601
|
+
entries = fs5.readdirSync(dir, { withFileTypes: true });
|
|
6401
6602
|
} catch {
|
|
6402
6603
|
return;
|
|
6403
6604
|
}
|
|
6404
6605
|
for (const entry of entries) {
|
|
6405
|
-
const fullPath =
|
|
6406
|
-
const resolved =
|
|
6606
|
+
const fullPath = path6.join(dir, entry.name);
|
|
6607
|
+
const resolved = path6.resolve(fullPath);
|
|
6407
6608
|
if (!resolved.startsWith(rootDir)) {
|
|
6408
6609
|
continue;
|
|
6409
6610
|
}
|
|
@@ -6411,37 +6612,37 @@ function collectFromDir(dir, rootDir, results) {
|
|
|
6411
6612
|
if (entry.name.startsWith(".")) continue;
|
|
6412
6613
|
collectFromDir(fullPath, rootDir, results);
|
|
6413
6614
|
} else if (entry.isFile()) {
|
|
6414
|
-
const ext =
|
|
6615
|
+
const ext = path6.extname(entry.name).toLowerCase();
|
|
6415
6616
|
if (ext === ".md") continue;
|
|
6416
6617
|
if (REJECTED_EXTENSIONS.has(ext)) continue;
|
|
6417
6618
|
if (!ALLOWED_ASSET_EXTENSIONS.has(ext)) continue;
|
|
6418
|
-
const relativePath =
|
|
6619
|
+
const relativePath = path6.relative(rootDir, fullPath);
|
|
6419
6620
|
if (relativePath.includes("..")) continue;
|
|
6420
6621
|
results.push({
|
|
6421
6622
|
path: relativePath.replace(/\\/g, "/"),
|
|
6422
|
-
content:
|
|
6623
|
+
content: fs5.readFileSync(fullPath)
|
|
6423
6624
|
});
|
|
6424
6625
|
}
|
|
6425
6626
|
}
|
|
6426
6627
|
}
|
|
6427
6628
|
|
|
6428
6629
|
// src/renderer/mermaid-runtime.ts
|
|
6429
|
-
import * as
|
|
6430
|
-
import * as
|
|
6630
|
+
import * as fs6 from "node:fs";
|
|
6631
|
+
import * as path7 from "node:path";
|
|
6431
6632
|
import { fileURLToPath } from "node:url";
|
|
6432
|
-
var __dirname =
|
|
6633
|
+
var __dirname = path7.dirname(fileURLToPath(import.meta.url));
|
|
6433
6634
|
var MERMAID_RUNTIME_REL = "runtime/mermaid.min.js";
|
|
6434
6635
|
var MERMAID_ARTIFACT_PATH = "_nrdocs/mermaid.min.js";
|
|
6435
6636
|
function mermaidRuntimeCandidates() {
|
|
6436
6637
|
return [
|
|
6437
|
-
|
|
6438
|
-
|
|
6638
|
+
path7.join(__dirname, MERMAID_RUNTIME_REL),
|
|
6639
|
+
path7.join(__dirname, "../../dist/runtime/mermaid.min.js")
|
|
6439
6640
|
];
|
|
6440
6641
|
}
|
|
6441
6642
|
function loadMermaidRuntime() {
|
|
6442
6643
|
for (const candidate of mermaidRuntimeCandidates()) {
|
|
6443
|
-
if (
|
|
6444
|
-
return
|
|
6644
|
+
if (fs6.existsSync(candidate)) {
|
|
6645
|
+
return fs6.readFileSync(candidate);
|
|
6445
6646
|
}
|
|
6446
6647
|
}
|
|
6447
6648
|
throw new Error(
|
|
@@ -6455,40 +6656,44 @@ function mermaidScriptSrcForOutput(outputPath) {
|
|
|
6455
6656
|
}
|
|
6456
6657
|
|
|
6457
6658
|
// src/renderer/index.ts
|
|
6458
|
-
function
|
|
6659
|
+
function resolveNav(resolvedDocsDir, nav, indexPath) {
|
|
6459
6660
|
if (nav && nav !== "auto" && Array.isArray(nav)) {
|
|
6460
|
-
return
|
|
6661
|
+
return {
|
|
6662
|
+
items: navConfigToNavItems(nav, resolvedDocsDir),
|
|
6663
|
+
sidebarConfig: nav
|
|
6664
|
+
};
|
|
6461
6665
|
}
|
|
6462
|
-
|
|
6666
|
+
const sidebarConfig = discoverNavEntries(resolvedDocsDir, { indexPath });
|
|
6667
|
+
return {
|
|
6668
|
+
items: navConfigToNavItems(sidebarConfig, resolvedDocsDir),
|
|
6669
|
+
sidebarConfig
|
|
6670
|
+
};
|
|
6463
6671
|
}
|
|
6464
6672
|
async function renderSite(options) {
|
|
6465
6673
|
const { docsDir, siteTitle, baseUrl, owner, repo, nav, indexPath = "index.md" } = options;
|
|
6466
|
-
const resolvedDocsDir =
|
|
6467
|
-
const navItems =
|
|
6674
|
+
const resolvedDocsDir = path8.resolve(docsDir);
|
|
6675
|
+
const { items: navItems, sidebarConfig } = resolveNav(resolvedDocsDir, nav, indexPath);
|
|
6468
6676
|
const siteBase = `/${owner}/${repo}/`;
|
|
6469
6677
|
const renderedFiles = [];
|
|
6470
6678
|
let siteHasMermaid = false;
|
|
6471
6679
|
for (const navItem of navItems) {
|
|
6472
|
-
const filePath =
|
|
6473
|
-
const markdownContent =
|
|
6680
|
+
const filePath = path8.join(resolvedDocsDir, navItem.path);
|
|
6681
|
+
const markdownContent = fs7.readFileSync(filePath, "utf-8");
|
|
6474
6682
|
const pageHasMermaid = contentHasMermaid(markdownContent);
|
|
6475
6683
|
if (pageHasMermaid) siteHasMermaid = true;
|
|
6476
6684
|
let html = renderMarkdown(markdownContent);
|
|
6477
|
-
const fileDir =
|
|
6685
|
+
const fileDir = path8.dirname(navItem.path);
|
|
6478
6686
|
const baseLinkPath = fileDir === "." ? "" : fileDir;
|
|
6479
6687
|
html = rewriteLinks(html, baseLinkPath, owner, repo);
|
|
6480
6688
|
const pageTitle = extractTitle(markdownContent, navItem.path);
|
|
6481
6689
|
const canonicalUrl = `${baseUrl}${siteBase}${navItem.href}`;
|
|
6482
|
-
const
|
|
6483
|
-
...item,
|
|
6484
|
-
active: item.path === navItem.path
|
|
6485
|
-
}));
|
|
6690
|
+
const sidebar = navConfigToSidebar(sidebarConfig, navItem.path);
|
|
6486
6691
|
const outputPath = navItem.href === "" ? "index.html" : navItem.href.replace(/\/$/, "") + "/index.html";
|
|
6487
6692
|
const fullHtml = wrapInTemplate({
|
|
6488
6693
|
title: pageTitle,
|
|
6489
6694
|
siteTitle,
|
|
6490
6695
|
content: html,
|
|
6491
|
-
nav:
|
|
6696
|
+
nav: sidebar,
|
|
6492
6697
|
canonicalUrl,
|
|
6493
6698
|
baseUrl: siteBase,
|
|
6494
6699
|
includeMermaid: pageHasMermaid,
|
|
@@ -6989,102 +7194,6 @@ var ApiClient = class {
|
|
|
6989
7194
|
}
|
|
6990
7195
|
};
|
|
6991
7196
|
|
|
6992
|
-
// src/config/docs-config.ts
|
|
6993
|
-
import * as fs7 from "node:fs";
|
|
6994
|
-
import * as path8 from "node:path";
|
|
6995
|
-
import YAML from "yaml";
|
|
6996
|
-
function loadDocsConfig(docsDir) {
|
|
6997
|
-
const configPath = path8.resolve(docsDir, "nrdocs.yml");
|
|
6998
|
-
if (!fs7.existsSync(configPath)) {
|
|
6999
|
-
throw new Error(`Config file not found: ${configPath}`);
|
|
7000
|
-
}
|
|
7001
|
-
const raw = fs7.readFileSync(configPath, "utf-8");
|
|
7002
|
-
const config2 = YAML.parse(raw);
|
|
7003
|
-
if (!config2 || typeof config2 !== "object") {
|
|
7004
|
-
throw new Error(`Invalid config: ${configPath}`);
|
|
7005
|
-
}
|
|
7006
|
-
const sourceDir = config2.content?.source_dir ?? ".";
|
|
7007
|
-
const contentDir = path8.resolve(docsDir, sourceDir);
|
|
7008
|
-
return { config: config2, configPath, contentDir };
|
|
7009
|
-
}
|
|
7010
|
-
function hasExplicitNav(config2) {
|
|
7011
|
-
return Array.isArray(config2.content?.nav);
|
|
7012
|
-
}
|
|
7013
|
-
function parseNavEntries(nav) {
|
|
7014
|
-
if (!Array.isArray(nav)) {
|
|
7015
|
-
throw new Error('content.nav must be a list or "auto"');
|
|
7016
|
-
}
|
|
7017
|
-
const entries = [];
|
|
7018
|
-
for (const item of nav) {
|
|
7019
|
-
if (!item || typeof item !== "object") {
|
|
7020
|
-
throw new Error("Each nav entry must have title and path");
|
|
7021
|
-
}
|
|
7022
|
-
const rec = item;
|
|
7023
|
-
if (typeof rec["title"] !== "string" || typeof rec["path"] !== "string") {
|
|
7024
|
-
throw new Error("Each nav entry must have title and path strings");
|
|
7025
|
-
}
|
|
7026
|
-
const entry = {
|
|
7027
|
-
title: rec["title"],
|
|
7028
|
-
path: rec["path"].replace(/\\/g, "/")
|
|
7029
|
-
};
|
|
7030
|
-
if (Array.isArray(rec["children"])) {
|
|
7031
|
-
entry.children = parseNavEntries(rec["children"]);
|
|
7032
|
-
}
|
|
7033
|
-
entries.push(entry);
|
|
7034
|
-
}
|
|
7035
|
-
return entries;
|
|
7036
|
-
}
|
|
7037
|
-
function getExplicitNav(config2) {
|
|
7038
|
-
const nav = config2.content?.nav;
|
|
7039
|
-
if (nav === void 0 || nav === "auto") return null;
|
|
7040
|
-
if (Array.isArray(nav)) return parseNavEntries(nav);
|
|
7041
|
-
throw new Error('content.nav must be "auto" or a list of entries');
|
|
7042
|
-
}
|
|
7043
|
-
function validateNavPaths(entries, contentDir) {
|
|
7044
|
-
const errors = [];
|
|
7045
|
-
const seen = /* @__PURE__ */ new Set();
|
|
7046
|
-
const walk = (list2) => {
|
|
7047
|
-
for (const e of list2) {
|
|
7048
|
-
const p = e.path.replace(/\\/g, "/");
|
|
7049
|
-
if (seen.has(p)) {
|
|
7050
|
-
errors.push(`Duplicate nav path: ${p}`);
|
|
7051
|
-
}
|
|
7052
|
-
seen.add(p);
|
|
7053
|
-
const full = path8.join(contentDir, p);
|
|
7054
|
-
if (!fs7.existsSync(full)) {
|
|
7055
|
-
errors.push(`Nav path not found: ${p}`);
|
|
7056
|
-
}
|
|
7057
|
-
if (e.children?.length) walk(e.children);
|
|
7058
|
-
}
|
|
7059
|
-
};
|
|
7060
|
-
walk(entries);
|
|
7061
|
-
return { valid: errors.length === 0, errors };
|
|
7062
|
-
}
|
|
7063
|
-
function writeNavToConfig(configPath, navEntries) {
|
|
7064
|
-
let raw = fs7.readFileSync(configPath, "utf-8");
|
|
7065
|
-
raw = raw.replace(/^# content\.nav generated by: nrdocs nav generate\n/gm, "");
|
|
7066
|
-
const config2 = YAML.parse(raw);
|
|
7067
|
-
if (!config2 || typeof config2 !== "object") {
|
|
7068
|
-
throw new Error(`Invalid config: ${configPath}`);
|
|
7069
|
-
}
|
|
7070
|
-
if (!config2.content) {
|
|
7071
|
-
config2.content = {};
|
|
7072
|
-
}
|
|
7073
|
-
config2.content.nav = navEntries;
|
|
7074
|
-
const doc = new YAML.Document(config2);
|
|
7075
|
-
const header = "# content.nav generated by: nrdocs nav generate\n";
|
|
7076
|
-
const body = doc.toString();
|
|
7077
|
-
fs7.writeFileSync(configPath, header + body, "utf-8");
|
|
7078
|
-
}
|
|
7079
|
-
function formatNavYaml(navEntries) {
|
|
7080
|
-
const partial = {
|
|
7081
|
-
content: {
|
|
7082
|
-
nav: navEntries
|
|
7083
|
-
}
|
|
7084
|
-
};
|
|
7085
|
-
return YAML.stringify(partial).trimEnd();
|
|
7086
|
-
}
|
|
7087
|
-
|
|
7088
7197
|
// src/commands/publish.ts
|
|
7089
7198
|
function parsePublishArgs(args2) {
|
|
7090
7199
|
const opts = {};
|
|
@@ -7569,7 +7678,7 @@ async function handleNavGenerate(args2) {
|
|
|
7569
7678
|
console.log(formatNavYaml(entries));
|
|
7570
7679
|
return;
|
|
7571
7680
|
}
|
|
7572
|
-
|
|
7681
|
+
generateNavInConfig(docsDir, { generatedBy: "nrdocs nav generate", indexPath });
|
|
7573
7682
|
if (!opts.json) {
|
|
7574
7683
|
console.log(`Generated navigation for ${entries.length} page(s) in ${path10.relative(process.cwd(), configPath)}`);
|
|
7575
7684
|
console.log("Edit the file to reorder or rename entries, then run publish.");
|
|
@@ -7647,23 +7756,52 @@ function normalizeUrl2(url) {
|
|
|
7647
7756
|
}
|
|
7648
7757
|
return normalized;
|
|
7649
7758
|
}
|
|
7759
|
+
function packagedWorkerDir() {
|
|
7760
|
+
const cliEntry = process.argv[1];
|
|
7761
|
+
if (!cliEntry) return null;
|
|
7762
|
+
const distDir = path11.dirname(path11.resolve(cliEntry));
|
|
7763
|
+
const dir = path11.join(distDir, "deploy-worker");
|
|
7764
|
+
if (fs11.existsSync(path11.join(dir, "index.js"))) return dir;
|
|
7765
|
+
return null;
|
|
7766
|
+
}
|
|
7650
7767
|
function findWorkerDir() {
|
|
7651
|
-
const
|
|
7768
|
+
const packaged = packagedWorkerDir();
|
|
7769
|
+
if (packaged) return packaged;
|
|
7770
|
+
const monorepoCandidates = [
|
|
7652
7771
|
path11.resolve("packages/worker"),
|
|
7653
7772
|
path11.resolve("../worker")
|
|
7654
7773
|
];
|
|
7655
|
-
|
|
7656
|
-
|
|
7657
|
-
|
|
7658
|
-
|
|
7774
|
+
if (process.argv[1]) {
|
|
7775
|
+
const cliDir = path11.dirname(path11.resolve(process.argv[1]));
|
|
7776
|
+
monorepoCandidates.push(path11.resolve(cliDir, "../../../worker"));
|
|
7777
|
+
monorepoCandidates.push(path11.resolve(cliDir, "../../../../packages/worker"));
|
|
7778
|
+
}
|
|
7779
|
+
for (const candidate of monorepoCandidates) {
|
|
7659
7780
|
if (fs11.existsSync(path11.join(candidate, "src", "index.ts"))) {
|
|
7660
7781
|
return candidate;
|
|
7661
7782
|
}
|
|
7662
7783
|
}
|
|
7663
7784
|
return null;
|
|
7664
7785
|
}
|
|
7786
|
+
function isDocsContentRepo(cwd) {
|
|
7787
|
+
return fs11.existsSync(path11.join(cwd, "docs", "nrdocs.yml")) && !fs11.existsSync(path11.join(cwd, "packages", "worker", "src", "index.ts"));
|
|
7788
|
+
}
|
|
7789
|
+
function workerUsesBundledEntry(workerDir) {
|
|
7790
|
+
return fs11.existsSync(path11.join(workerDir, "index.js"));
|
|
7791
|
+
}
|
|
7665
7792
|
async function handleDeploy(args2) {
|
|
7666
7793
|
const opts = parseDeployArgs(args2);
|
|
7794
|
+
console.log("nrdocs deploy \u2014 Cloudflare infrastructure (Worker, D1, R2)");
|
|
7795
|
+
console.log("This does not publish markdown from the current repo.");
|
|
7796
|
+
console.log("Repo owners publish docs via GitHub Actions: nrdocs publish");
|
|
7797
|
+
console.log("");
|
|
7798
|
+
if (isDocsContentRepo(process.cwd())) {
|
|
7799
|
+
console.warn(
|
|
7800
|
+
"Note: This directory looks like a documentation repository (docs/nrdocs.yml)."
|
|
7801
|
+
);
|
|
7802
|
+
console.warn("You are deploying the nrdocs hosting stack, not site content here.");
|
|
7803
|
+
console.log("");
|
|
7804
|
+
}
|
|
7667
7805
|
if (!checkWrangler()) {
|
|
7668
7806
|
console.error("Error: Wrangler is not available.");
|
|
7669
7807
|
console.error("Install it: npm install -g wrangler");
|
|
@@ -7707,7 +7845,10 @@ async function handleDeploy(args2) {
|
|
|
7707
7845
|
}
|
|
7708
7846
|
} else {
|
|
7709
7847
|
instance = instance || await prompt3("Instance name", "default");
|
|
7710
|
-
baseUrl = baseUrl || await prompt3(
|
|
7848
|
+
baseUrl = baseUrl || await prompt3(
|
|
7849
|
+
"Public site URL (readers visit this host)",
|
|
7850
|
+
"https://docs.example.com"
|
|
7851
|
+
);
|
|
7711
7852
|
}
|
|
7712
7853
|
baseUrl = normalizeUrl2(baseUrl);
|
|
7713
7854
|
const validation = validateInstanceName(instance);
|
|
@@ -7747,10 +7888,11 @@ async function handleDeploy(args2) {
|
|
|
7747
7888
|
}
|
|
7748
7889
|
const workerDir = findWorkerDir();
|
|
7749
7890
|
if (!workerDir) {
|
|
7750
|
-
console.error("Error: Cannot find
|
|
7751
|
-
console.error("
|
|
7891
|
+
console.error("Error: Cannot find the nrdocs Worker bundle.");
|
|
7892
|
+
console.error("Reinstall the CLI (npm install -g nrdocs) or run from the nrdocs monorepo.");
|
|
7752
7893
|
process.exit(4);
|
|
7753
7894
|
}
|
|
7895
|
+
const workerMain = workerUsesBundledEntry(workerDir) ? "index.js" : "src/index.ts";
|
|
7754
7896
|
console.log(`Creating R2 bucket ${names.r2}...`);
|
|
7755
7897
|
const r2Check = runSilent("npx wrangler r2 bucket list");
|
|
7756
7898
|
if (r2Check.ok && r2Check.stdout.includes(names.r2)) {
|
|
@@ -7784,7 +7926,7 @@ async function handleDeploy(args2) {
|
|
|
7784
7926
|
}
|
|
7785
7927
|
}
|
|
7786
7928
|
const wranglerToml = `name = "${names.worker}"
|
|
7787
|
-
main = "
|
|
7929
|
+
main = "${workerMain}"
|
|
7788
7930
|
compatibility_date = "2026-05-07"
|
|
7789
7931
|
|
|
7790
7932
|
[[d1_databases]]
|
|
@@ -7871,13 +8013,15 @@ BASE_URL = "${baseUrl}"
|
|
|
7871
8013
|
console.log(`Operator profile saved: ${profileName}`);
|
|
7872
8014
|
}
|
|
7873
8015
|
console.log("");
|
|
7874
|
-
console.log("
|
|
7875
|
-
console.log(` API:
|
|
7876
|
-
console.log(`
|
|
8016
|
+
console.log("Infrastructure deployment complete.");
|
|
8017
|
+
console.log(` API: ${baseUrl}/api`);
|
|
8018
|
+
console.log(` Public site: ${baseUrl}/`);
|
|
7877
8019
|
console.log("");
|
|
7878
|
-
console.log("Next:");
|
|
8020
|
+
console.log("Next (operator):");
|
|
7879
8021
|
console.log(` nrdocs rules add 'OWNER/*' --access password`);
|
|
7880
8022
|
console.log(" nrdocs repos");
|
|
8023
|
+
console.log("");
|
|
8024
|
+
console.log("Repo owners publish content with GitHub Actions (nrdocs publish), not deploy.");
|
|
7881
8025
|
}
|
|
7882
8026
|
|
|
7883
8027
|
// src/commands/repos.ts
|