nrdocs 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.
Files changed (2) hide show
  1. package/dist/bin.mjs +109 -21
  2. package/package.json +1 -1
package/dist/bin.mjs CHANGED
@@ -694,6 +694,21 @@ function parseInitArgs(args2) {
694
694
  }
695
695
  return opts;
696
696
  }
697
+ function normalizeUrl(url) {
698
+ let normalized = url.trim();
699
+ if (!normalized.startsWith("http://") && !normalized.startsWith("https://")) {
700
+ normalized = `https://${normalized}`;
701
+ }
702
+ normalized = normalized.replace(/\/+$/, "");
703
+ try {
704
+ new URL(normalized);
705
+ } catch {
706
+ console.error(`Error: "${url}" is not a valid URL.`);
707
+ console.error("Expected format: https://docs.example.com");
708
+ process.exit(2);
709
+ }
710
+ return normalized;
711
+ }
697
712
  function readExistingConfig(configPath) {
698
713
  if (!fs2.existsSync(configPath)) return {};
699
714
  try {
@@ -751,6 +766,7 @@ async function handleInit(args2) {
751
766
  console.error(" nrdocs init --api-url https://your-docs-url.com");
752
767
  process.exit(2);
753
768
  }
769
+ apiUrl = normalizeUrl(apiUrl);
754
770
  const indexFile = path3.join(docsPath, "index.md");
755
771
  const workflowDir = path3.resolve(".github", "workflows");
756
772
  const workflowFile = path3.join(workflowDir, "nrdocs.yml");
@@ -6092,9 +6108,33 @@ function rewriteMdLink(href, basePath, owner, repo) {
6092
6108
  }
6093
6109
 
6094
6110
  // src/renderer/template.ts
6111
+ function extractToc(html) {
6112
+ const toc = [];
6113
+ const regex = /<h([2-3])[^>]*id="([^"]*)"[^>]*>(.*?)<\/h[2-3]>/gi;
6114
+ let match2;
6115
+ while ((match2 = regex.exec(html)) !== null) {
6116
+ toc.push({
6117
+ level: parseInt(match2[1], 10),
6118
+ id: match2[2],
6119
+ text: match2[3].replace(/<[^>]*>/g, "")
6120
+ });
6121
+ }
6122
+ return toc;
6123
+ }
6124
+ function addHeadingIds(html) {
6125
+ return html.replace(/<h([2-3])([^>]*)>(.*?)<\/h[2-3]>/gi, (_match, level, attrs, text2) => {
6126
+ if (attrs.includes("id=")) return _match;
6127
+ const plainText = text2.replace(/<[^>]*>/g, "");
6128
+ const id = plainText.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
6129
+ return `<h${level} id="${id}"${attrs}>${text2}</h${level}>`;
6130
+ });
6131
+ }
6095
6132
  function wrapInTemplate(options) {
6096
6133
  const { title, siteTitle, content, nav, canonicalUrl, baseUrl } = options;
6097
6134
  const pageTitle = title === siteTitle ? siteTitle : `${title} - ${siteTitle}`;
6135
+ const contentWithIds = addHeadingIds(content);
6136
+ const toc = extractToc(contentWithIds);
6137
+ const hasToc = toc.length > 1;
6098
6138
  return `<!DOCTYPE html>
6099
6139
  <html lang="en">
6100
6140
  <head>
@@ -6103,34 +6143,46 @@ function wrapInTemplate(options) {
6103
6143
  <meta http-equiv="X-Content-Type-Options" content="nosniff">
6104
6144
  <meta http-equiv="X-Frame-Options" content="DENY">
6105
6145
  <meta name="referrer" content="no-referrer">
6146
+ <meta name="generator" content="nrdocs">
6106
6147
  <title>${escapeHtml2(pageTitle)}</title>
6107
6148
  <link rel="canonical" href="${escapeHtml2(canonicalUrl)}">
6108
6149
  <style>
6109
6150
  *{margin:0;padding:0;box-sizing:border-box}
6110
- body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;line-height:1.6;color:#1a1a1a;display:flex;min-height:100vh}
6111
- nav.sidebar{width:260px;padding:1.5rem;border-right:1px solid #e0e0e0;background:#fafafa;overflow-y:auto;flex-shrink:0}
6151
+ body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;font-size:1.0625rem;line-height:1.7;color:#1a1a1a;display:flex;min-height:100vh}
6152
+ nav.sidebar{width:260px;padding:1.5rem;border-right:1px solid #e0e0e0;background:#fafafa;overflow-y:auto;flex-shrink:0;position:sticky;top:0;height:100vh}
6112
6153
  nav.sidebar .site-title{font-weight:700;font-size:1.1rem;margin-bottom:1rem;color:#111}
6113
6154
  nav.sidebar ul{list-style:none}
6114
6155
  nav.sidebar li{margin-bottom:0.25rem}
6115
- nav.sidebar a{color:#333;text-decoration:none;padding:0.25rem 0.5rem;display:block;border-radius:4px}
6156
+ nav.sidebar a{color:#333;text-decoration:none;padding:0.3rem 0.6rem;display:block;border-radius:4px;font-size:0.95rem}
6116
6157
  nav.sidebar a:hover{background:#e8e8e8}
6117
6158
  nav.sidebar a.active{background:#e0e7ff;color:#1d4ed8;font-weight:500}
6118
- main{flex:1;padding:2rem 3rem;max-width:52rem;overflow-wrap:break-word}
6119
- main h1{font-size:2rem;margin-bottom:1rem;border-bottom:1px solid #e0e0e0;padding-bottom:0.5rem}
6120
- main h2{font-size:1.5rem;margin-top:2rem;margin-bottom:0.75rem}
6121
- main h3{font-size:1.25rem;margin-top:1.5rem;margin-bottom:0.5rem}
6122
- main p{margin-bottom:1rem}
6159
+ .content-wrapper{flex:1;display:flex;min-width:0}
6160
+ main{flex:1;padding:2.5rem 3rem;max-width:52rem;min-width:0;overflow-wrap:break-word}
6161
+ main h1{font-size:2.2rem;margin-bottom:1.2rem;border-bottom:1px solid #e0e0e0;padding-bottom:0.5rem;line-height:1.3}
6162
+ main h2{font-size:1.6rem;margin-top:2.5rem;margin-bottom:0.75rem;line-height:1.3}
6163
+ main h3{font-size:1.3rem;margin-top:1.8rem;margin-bottom:0.5rem;line-height:1.4}
6164
+ main p{margin-bottom:1.1rem}
6123
6165
  main a{color:#1d4ed8;text-decoration:underline}
6124
- main code{background:#f3f4f6;padding:0.15rem 0.35rem;border-radius:3px;font-size:0.9em}
6125
- main pre{background:#1e1e1e;color:#d4d4d4;padding:1rem;border-radius:6px;overflow-x:auto;margin-bottom:1rem}
6126
- main pre code{background:none;padding:0;color:inherit}
6127
- main table{border-collapse:collapse;width:100%;margin-bottom:1rem}
6128
- main th,main td{border:1px solid #d0d0d0;padding:0.5rem 0.75rem;text-align:left}
6166
+ main code{background:#f3f4f6;padding:0.2rem 0.4rem;border-radius:3px;font-size:0.88em}
6167
+ main pre{background:#1e1e1e;color:#d4d4d4;padding:1.2rem;border-radius:6px;overflow-x:auto;margin-bottom:1.2rem;font-size:0.9rem;line-height:1.5}
6168
+ main pre code{background:none;padding:0;color:inherit;font-size:inherit}
6169
+ main table{border-collapse:collapse;width:100%;margin-bottom:1.2rem}
6170
+ main th,main td{border:1px solid #d0d0d0;padding:0.6rem 0.85rem;text-align:left}
6129
6171
  main th{background:#f5f5f5;font-weight:600}
6130
- main img{max-width:100%;height:auto}
6131
- main blockquote{border-left:4px solid #d0d0d0;padding-left:1rem;margin-bottom:1rem;color:#555}
6132
- main ul,main ol{margin-bottom:1rem;padding-left:1.5rem}
6133
- @media(max-width:768px){body{flex-direction:column}nav.sidebar{width:100%;border-right:none;border-bottom:1px solid #e0e0e0}main{padding:1rem}}
6172
+ main img{max-width:100%;height:auto;border-radius:4px}
6173
+ main blockquote{border-left:4px solid #d0d0d0;padding-left:1rem;margin-bottom:1.1rem;color:#555;font-style:italic}
6174
+ main ul,main ol{margin-bottom:1.1rem;padding-left:1.5rem}
6175
+ main li{margin-bottom:0.3rem}
6176
+ aside.toc{width:220px;padding:1.5rem 1rem;position:sticky;top:0;height:100vh;overflow-y:auto;flex-shrink:0;border-left:1px solid #e8e8e8}
6177
+ aside.toc .toc-title{font-size:0.8rem;font-weight:600;text-transform:uppercase;letter-spacing:0.05em;color:#666;margin-bottom:0.75rem}
6178
+ aside.toc ul{list-style:none}
6179
+ aside.toc li{margin-bottom:0.3rem}
6180
+ aside.toc a{color:#555;text-decoration:none;font-size:0.85rem;display:block;padding:0.15rem 0;border-radius:2px}
6181
+ aside.toc a:hover{color:#1d4ed8}
6182
+ aside.toc .toc-h3{padding-left:0.75rem}
6183
+ footer{padding:1.5rem 3rem;border-top:1px solid #e8e8e8;color:#888;font-size:0.8rem;text-align:center}
6184
+ @media(max-width:1100px){aside.toc{display:none}}
6185
+ @media(max-width:768px){body{flex-direction:column}nav.sidebar{width:100%;border-right:none;border-bottom:1px solid #e0e0e0;position:static;height:auto}main{padding:1.5rem}}
6134
6186
  </style>
6135
6187
  </head>
6136
6188
  <body>
@@ -6140,12 +6192,28 @@ main ul,main ol{margin-bottom:1rem;padding-left:1.5rem}
6140
6192
  ${renderNavItems(nav, baseUrl)}
6141
6193
  </ul>
6142
6194
  </nav>
6195
+ <div class="content-wrapper">
6143
6196
  <main>
6144
- ${content}
6197
+ ${contentWithIds}
6198
+ <footer>Generated with <a href="https://github.com/noam-r/nrdocs">nrdocs</a></footer>
6145
6199
  </main>
6200
+ ${hasToc ? renderToc(toc) : ""}
6201
+ </div>
6146
6202
  </body>
6147
6203
  </html>`;
6148
6204
  }
6205
+ function renderToc(toc) {
6206
+ const items = toc.map((entry) => {
6207
+ const cls = entry.level === 3 ? ' class="toc-h3"' : "";
6208
+ return `<li${cls}><a href="#${escapeHtml2(entry.id)}">${escapeHtml2(entry.text)}</a></li>`;
6209
+ }).join("\n");
6210
+ return `<aside class="toc">
6211
+ <div class="toc-title">On this page</div>
6212
+ <ul>
6213
+ ${items}
6214
+ </ul>
6215
+ </aside>`;
6216
+ }
6149
6217
  function renderNavItems(items, baseUrl) {
6150
6218
  return items.map((item) => {
6151
6219
  const href = baseUrl + item.href;
@@ -6545,9 +6613,13 @@ async function handlePublish(args2) {
6545
6613
  console.log("Uploading to nrdocs...");
6546
6614
  const client = new ApiClient(apiUrl, token);
6547
6615
  const formData = new FormData();
6548
- formData.append("archive", new Blob([archive], { type: "application/gzip" }), "docs.tar.gz");
6549
- formData.append("owner", owner);
6550
- formData.append("repo", repo);
6616
+ formData.append("artifact", new Blob([archive], { type: "application/gzip" }), "docs.tar.gz");
6617
+ formData.append("metadata", JSON.stringify({
6618
+ schema_version: 1,
6619
+ site: { title: siteTitle, requested_access: "password" },
6620
+ artifact: { format: "tar.gz", size_bytes: archive.length },
6621
+ nrdocs: { cli_version: "0.1.1" }
6622
+ }));
6551
6623
  const result = await client.publish(formData);
6552
6624
  if (result.ok) {
6553
6625
  console.log("Published successfully!");
@@ -6699,6 +6771,21 @@ function checkCloudflareAuth() {
6699
6771
  const result = runSilent("npx wrangler whoami");
6700
6772
  return result.ok && !result.stdout.includes("Not authenticated");
6701
6773
  }
6774
+ function normalizeUrl2(url) {
6775
+ let normalized = url.trim();
6776
+ if (!normalized.startsWith("http://") && !normalized.startsWith("https://")) {
6777
+ normalized = `https://${normalized}`;
6778
+ }
6779
+ normalized = normalized.replace(/\/+$/, "");
6780
+ try {
6781
+ new URL(normalized);
6782
+ } catch {
6783
+ console.error(`Error: "${url}" is not a valid URL.`);
6784
+ console.error("Expected format: https://docs.example.com");
6785
+ process.exit(2);
6786
+ }
6787
+ return normalized;
6788
+ }
6702
6789
  function findWorkerDir() {
6703
6790
  const candidates = [
6704
6791
  path9.resolve("packages/worker"),
@@ -6761,6 +6848,7 @@ async function handleDeploy(args2) {
6761
6848
  instance = instance || await prompt3("Instance name", "default");
6762
6849
  baseUrl = baseUrl || await prompt3("Docs base URL", "https://docs.example.com");
6763
6850
  }
6851
+ baseUrl = normalizeUrl2(baseUrl);
6764
6852
  const validation = validateInstanceName(instance);
6765
6853
  if (!validation.valid) {
6766
6854
  console.error(`Error: ${validation.error}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nrdocs",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "CLI for nrdocs - serverless docs publishing for private GitHub repos",
5
5
  "type": "module",
6
6
  "bin": {