docula 1.5.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/docula.d.ts CHANGED
@@ -215,9 +215,23 @@ declare class DoculaOptions {
215
215
  */
216
216
  autoUpdateIgnores: boolean;
217
217
  /**
218
- * File extensions to copy as assets from docs/ and changelog/ directories.
219
- * Override in docula.config to customize.
218
+ * Base URL path prefix for all generated paths (e.g., "/docs").
219
+ * When set, all asset and navigation URLs are prefixed with this path.
220
+ */
221
+ baseUrl: string;
222
+ /**
223
+ * Output subdirectory and URL segment for documentation pages.
224
+ * Set to empty string to place docs at the output root.
225
+ */
226
+ docsPath: string;
227
+ /**
228
+ * Output subdirectory and URL segment for API reference pages.
229
+ */
230
+ apiPath: string;
231
+ /**
232
+ * Output subdirectory and URL segment for changelog pages.
220
233
  */
234
+ changelogPath: string;
221
235
  /**
222
236
  * Cookie-based authentication. When set, shows a Login/Logout button
223
237
  * in the header based on whether a JWT cookie is present.
@@ -297,6 +311,13 @@ type DoculaData = {
297
311
  enableLlmsTxt?: boolean;
298
312
  hasFeed?: boolean;
299
313
  lastModified?: string;
314
+ baseUrl: string;
315
+ docsPath: string;
316
+ apiPath: string;
317
+ changelogPath: string;
318
+ docsUrl: string;
319
+ apiUrl: string;
320
+ changelogUrl: string;
300
321
  };
301
322
  type DoculaTemplates = {
302
323
  home: string;
@@ -342,9 +363,13 @@ declare class DoculaBuilder {
342
363
  buildRobotsPage(options: DoculaOptions): Promise<void>;
343
364
  buildSiteMapPage(data: DoculaData): Promise<void>;
344
365
  buildFeedPage(data: DoculaData): Promise<void>;
366
+ buildChangelogFeedJson(data: DoculaData): Promise<void>;
367
+ buildChangelogLatestFeedJson(data: DoculaData): Promise<void>;
368
+ private writeChangelogFeedJson;
345
369
  buildLlmsFiles(data: DoculaData): Promise<void>;
346
370
  private generateLlmsIndexContent;
347
371
  private generateLlmsFullContent;
372
+ private buildUrlPath;
348
373
  private buildAbsoluteSiteUrl;
349
374
  private normalizePathForUrl;
350
375
  private escapeXml;
package/dist/docula.js CHANGED
@@ -907,6 +907,18 @@ var Github = class {
907
907
  // src/options.ts
908
908
  import path3 from "path";
909
909
  import process3 from "process";
910
+ function trimSlashes(value) {
911
+ let start = 0;
912
+ let end = value.length;
913
+ while (start < end && value[start] === "/") start++;
914
+ while (end > start && value[end - 1] === "/") end--;
915
+ return value.slice(start, end);
916
+ }
917
+ function trimTrailingSlashes(value) {
918
+ let end = value.length;
919
+ while (end > 0 && value[end - 1] === "/") end--;
920
+ return value.slice(0, end);
921
+ }
910
922
  var DoculaOptions = class {
911
923
  /**
912
924
  * Name of the built-in template to use (e.g., "modern", "classic")
@@ -979,9 +991,23 @@ var DoculaOptions = class {
979
991
  */
980
992
  autoUpdateIgnores = true;
981
993
  /**
982
- * File extensions to copy as assets from docs/ and changelog/ directories.
983
- * Override in docula.config to customize.
994
+ * Base URL path prefix for all generated paths (e.g., "/docs").
995
+ * When set, all asset and navigation URLs are prefixed with this path.
984
996
  */
997
+ baseUrl = "";
998
+ /**
999
+ * Output subdirectory and URL segment for documentation pages.
1000
+ * Set to empty string to place docs at the output root.
1001
+ */
1002
+ docsPath = "docs";
1003
+ /**
1004
+ * Output subdirectory and URL segment for API reference pages.
1005
+ */
1006
+ apiPath = "api";
1007
+ /**
1008
+ * Output subdirectory and URL segment for changelog pages.
1009
+ */
1010
+ changelogPath = "changelog";
985
1011
  /**
986
1012
  * Cookie-based authentication. When set, shows a Login/Logout button
987
1013
  * in the header based on whether a JWT cookie is present.
@@ -1096,6 +1122,18 @@ var DoculaOptions = class {
1096
1122
  if (options.cache && typeof options.cache === "object" && options.cache.github !== null && typeof options.cache.github === "object" && typeof options.cache.github.ttl === "number") {
1097
1123
  this.cache = options.cache;
1098
1124
  }
1125
+ if (options.baseUrl !== void 0 && typeof options.baseUrl === "string") {
1126
+ this.baseUrl = trimTrailingSlashes(options.baseUrl);
1127
+ }
1128
+ if (options.docsPath !== void 0 && typeof options.docsPath === "string") {
1129
+ this.docsPath = trimSlashes(options.docsPath);
1130
+ }
1131
+ if (options.apiPath !== void 0 && typeof options.apiPath === "string") {
1132
+ this.apiPath = trimSlashes(options.apiPath);
1133
+ }
1134
+ if (options.changelogPath !== void 0 && typeof options.changelogPath === "string") {
1135
+ this.changelogPath = trimSlashes(options.changelogPath);
1136
+ }
1099
1137
  if (options.allowedAssets && Array.isArray(options.allowedAssets)) {
1100
1138
  this.allowedAssets = options.allowedAssets;
1101
1139
  }
@@ -1207,14 +1245,28 @@ var DoculaBuilder = class {
1207
1245
  themeMode: this.options.themeMode,
1208
1246
  cookieAuth: this.options.cookieAuth,
1209
1247
  headerLinks: this.options.headerLinks,
1210
- enableLlmsTxt: this.options.enableLlmsTxt
1248
+ enableLlmsTxt: this.options.enableLlmsTxt,
1249
+ baseUrl: this.options.baseUrl,
1250
+ docsPath: this.options.docsPath,
1251
+ apiPath: this.options.apiPath,
1252
+ changelogPath: this.options.changelogPath,
1253
+ docsUrl: this.buildUrlPath(this.options.baseUrl, this.options.docsPath),
1254
+ apiUrl: this.buildUrlPath(this.options.baseUrl, this.options.apiPath),
1255
+ changelogUrl: this.buildUrlPath(
1256
+ this.options.baseUrl,
1257
+ this.options.changelogPath
1258
+ )
1211
1259
  };
1212
1260
  const readmePath = `${this.options.sitePath}/README.md`;
1213
1261
  if (doculaData.hasReadme) {
1214
1262
  currentAssetHashes["README.md"] = this.hashFile(readmePath);
1215
1263
  }
1216
1264
  if (!doculaData.openApiUrl && fs3.existsSync(`${doculaData.sitePath}/api/swagger.json`)) {
1217
- doculaData.openApiUrl = "/api/swagger.json";
1265
+ doculaData.openApiUrl = this.buildUrlPath(
1266
+ this.options.baseUrl,
1267
+ this.options.apiPath,
1268
+ "swagger.json"
1269
+ );
1218
1270
  }
1219
1271
  if (this.options.githubPath) {
1220
1272
  doculaData.github = await this.getGithubData(this.options.githubPath);
@@ -1308,6 +1360,12 @@ var DoculaBuilder = class {
1308
1360
  await this.buildFeedPage(doculaData);
1309
1361
  this._console.fileBuilt("feed.xml");
1310
1362
  }
1363
+ if (doculaData.hasChangelog && doculaData.templates?.changelogEntry) {
1364
+ await this.buildChangelogFeedJson(doculaData);
1365
+ this._console.fileBuilt("changelog.json");
1366
+ await this.buildChangelogLatestFeedJson(doculaData);
1367
+ this._console.fileBuilt("changelog-latest.json");
1368
+ }
1311
1369
  if (doculaData.hasDocuments) {
1312
1370
  this._console.step("Building documentation pages...");
1313
1371
  await this.buildDocsPages(doculaData);
@@ -1318,15 +1376,17 @@ var DoculaBuilder = class {
1318
1376
  if (doculaData.hasApi) {
1319
1377
  this._console.step("Building API page...");
1320
1378
  await this.buildApiPage(doculaData);
1321
- this._console.fileBuilt("api/index.html");
1379
+ this._console.fileBuilt(`${this.options.apiPath}/index.html`);
1322
1380
  }
1323
1381
  if (doculaData.hasChangelog) {
1324
1382
  this._console.step("Building changelog...");
1325
1383
  await this.buildChangelogPage(doculaData);
1326
- this._console.fileBuilt("changelog/index.html");
1384
+ this._console.fileBuilt(`${this.options.changelogPath}/index.html`);
1327
1385
  await this.buildChangelogEntryPages(doculaData);
1328
1386
  for (const entry of doculaData.changelogEntries ?? []) {
1329
- this._console.fileBuilt(`changelog/${entry.slug}/index.html`);
1387
+ this._console.fileBuilt(
1388
+ `${this.options.changelogPath}/${entry.slug}/index.html`
1389
+ );
1330
1390
  }
1331
1391
  }
1332
1392
  const siteRelativePath = this.options.sitePath;
@@ -1416,7 +1476,7 @@ var DoculaBuilder = class {
1416
1476
  );
1417
1477
  this.copyContentAssets(
1418
1478
  `${doculaData.sitePath}/changelog`,
1419
- `${this.options.output}/changelog`
1479
+ `${this.options.output}/${this.options.changelogPath}`
1420
1480
  );
1421
1481
  if (doculaData.documents?.length) {
1422
1482
  this.copyDocumentSiblingAssets(doculaData);
@@ -1524,13 +1584,23 @@ var DoculaBuilder = class {
1524
1584
  const sitemapPath = `${data.output}/sitemap.xml`;
1525
1585
  const urls = [{ url: data.siteUrl }];
1526
1586
  if (data.documents?.length) {
1527
- urls.push({ url: `${data.siteUrl}/feed.xml` });
1587
+ urls.push({ url: `${data.siteUrl}${data.baseUrl}/feed.xml` });
1588
+ }
1589
+ if (data.hasChangelog && data.templates?.changelogEntry) {
1590
+ urls.push({ url: `${data.siteUrl}${data.baseUrl}/changelog.json` });
1591
+ urls.push({
1592
+ url: `${data.siteUrl}${data.baseUrl}/changelog-latest.json`
1593
+ });
1528
1594
  }
1529
1595
  if (data.openApiUrl && data.templates?.api) {
1530
- urls.push({ url: `${data.siteUrl}/api` });
1596
+ urls.push({
1597
+ url: `${data.siteUrl}${data.apiUrl}`
1598
+ });
1531
1599
  }
1532
1600
  if (data.hasChangelog && data.templates?.changelog) {
1533
- urls.push({ url: `${data.siteUrl}/changelog` });
1601
+ urls.push({
1602
+ url: `${data.siteUrl}${data.changelogUrl}`
1603
+ });
1534
1604
  const perPage = this.options.changelogPerPage;
1535
1605
  const totalPages = Math.max(
1536
1606
  1,
@@ -1538,12 +1608,12 @@ var DoculaBuilder = class {
1538
1608
  );
1539
1609
  for (let page = 2; page <= totalPages; page++) {
1540
1610
  urls.push({
1541
- url: `${data.siteUrl}/changelog/page/${page}`
1611
+ url: `${data.siteUrl}${data.changelogUrl}/page/${page}`
1542
1612
  });
1543
1613
  }
1544
1614
  for (const entry of data.changelogEntries ?? []) {
1545
1615
  urls.push({
1546
- url: `${data.siteUrl}/changelog/${entry.slug}`
1616
+ url: `${data.siteUrl}${data.changelogUrl}/${entry.slug}`
1547
1617
  });
1548
1618
  }
1549
1619
  }
@@ -1552,7 +1622,7 @@ var DoculaBuilder = class {
1552
1622
  if (urlPath.endsWith("index.html")) {
1553
1623
  urlPath = urlPath.slice(0, -10);
1554
1624
  }
1555
- urls.push({ url: `${data.siteUrl}${urlPath}` });
1625
+ urls.push({ url: `${data.siteUrl}${data.baseUrl}${urlPath}` });
1556
1626
  }
1557
1627
  let xml = '<?xml version="1.0" encoding="UTF-8"?>';
1558
1628
  xml += '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
@@ -1570,8 +1640,14 @@ var DoculaBuilder = class {
1570
1640
  return;
1571
1641
  }
1572
1642
  const feedPath = `${data.output}/feed.xml`;
1573
- const channelLink = this.buildAbsoluteSiteUrl(data.siteUrl, "/");
1574
- const feedUrl = this.buildAbsoluteSiteUrl(data.siteUrl, "/feed.xml");
1643
+ const channelLink = this.buildAbsoluteSiteUrl(
1644
+ data.siteUrl,
1645
+ `${data.baseUrl}/`
1646
+ );
1647
+ const feedUrl = this.buildAbsoluteSiteUrl(
1648
+ data.siteUrl,
1649
+ `${data.baseUrl}/feed.xml`
1650
+ );
1575
1651
  let xml = '<?xml version="1.0" encoding="UTF-8"?>';
1576
1652
  xml += '<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">';
1577
1653
  xml += "<channel>";
@@ -1584,7 +1660,7 @@ var DoculaBuilder = class {
1584
1660
  const itemTitle = document.navTitle || document.title || document.urlPath;
1585
1661
  const itemLink = this.buildAbsoluteSiteUrl(
1586
1662
  data.siteUrl,
1587
- this.normalizePathForUrl(document.urlPath)
1663
+ `${data.baseUrl}${this.normalizePathForUrl(document.urlPath)}`
1588
1664
  );
1589
1665
  const summary = document.description || this.summarizeMarkdown(new Writr(document.content).body);
1590
1666
  xml += "<item>";
@@ -1599,6 +1675,73 @@ var DoculaBuilder = class {
1599
1675
  await fs3.promises.mkdir(data.output, { recursive: true });
1600
1676
  await fs3.promises.writeFile(feedPath, xml, "utf8");
1601
1677
  }
1678
+ async buildChangelogFeedJson(data) {
1679
+ const entries = data.changelogEntries;
1680
+ if (!entries?.length) {
1681
+ return;
1682
+ }
1683
+ await this.writeChangelogFeedJson(data, entries, "changelog.json");
1684
+ }
1685
+ async buildChangelogLatestFeedJson(data) {
1686
+ const entries = data.changelogEntries;
1687
+ if (!entries?.length) {
1688
+ return;
1689
+ }
1690
+ const latestEntries = entries.slice(0, this.options.changelogPerPage);
1691
+ await this.writeChangelogFeedJson(
1692
+ data,
1693
+ latestEntries,
1694
+ "changelog-latest.json"
1695
+ );
1696
+ }
1697
+ async writeChangelogFeedJson(data, entries, filename) {
1698
+ const feedUrl = this.buildAbsoluteSiteUrl(
1699
+ data.siteUrl,
1700
+ `${data.baseUrl}/${filename}`
1701
+ );
1702
+ const homeUrl = this.buildAbsoluteSiteUrl(data.siteUrl, `${data.baseUrl}/`);
1703
+ const items = entries.map((entry) => {
1704
+ const itemUrl = this.buildAbsoluteSiteUrl(
1705
+ data.siteUrl,
1706
+ `${data.changelogUrl}/${entry.slug}/`
1707
+ );
1708
+ const item = {
1709
+ id: entry.slug,
1710
+ title: entry.title,
1711
+ url: itemUrl,
1712
+ date_published: entry.date,
1713
+ date_modified: entry.lastModified,
1714
+ summary: entry.preview
1715
+ };
1716
+ if (entry.generatedHtml) {
1717
+ item.content_html = entry.generatedHtml;
1718
+ }
1719
+ if (entry.content) {
1720
+ item.content_text = entry.content;
1721
+ }
1722
+ if (entry.tag) {
1723
+ item.tags = [entry.tag];
1724
+ }
1725
+ if (entry.previewImage) {
1726
+ item.image = entry.previewImage;
1727
+ }
1728
+ return item;
1729
+ });
1730
+ const feed = {
1731
+ version: "https://jsonfeed.org/version/1.1",
1732
+ title: data.siteTitle,
1733
+ description: data.siteDescription,
1734
+ home_page_url: homeUrl,
1735
+ feed_url: feedUrl,
1736
+ items
1737
+ };
1738
+ await fs3.promises.mkdir(data.output, { recursive: true });
1739
+ await fs3.promises.writeFile(
1740
+ `${data.output}/${filename}`,
1741
+ JSON.stringify(feed, null, 2),
1742
+ "utf8"
1743
+ );
1744
+ }
1602
1745
  async buildLlmsFiles(data) {
1603
1746
  if (!this.options.enableLlmsTxt) {
1604
1747
  return;
@@ -1642,7 +1785,7 @@ var DoculaBuilder = class {
1642
1785
  lines.push(data.siteDescription);
1643
1786
  lines.push("");
1644
1787
  lines.push(
1645
- `- [Full LLM Content](${this.buildAbsoluteSiteUrl(data.siteUrl, "/llms-full.txt")})`
1788
+ `- [Full LLM Content](${this.buildAbsoluteSiteUrl(data.siteUrl, `${data.baseUrl}/llms-full.txt`)})`
1646
1789
  );
1647
1790
  lines.push("");
1648
1791
  lines.push("## Documentation");
@@ -1650,7 +1793,7 @@ var DoculaBuilder = class {
1650
1793
  for (const document of documents) {
1651
1794
  const documentUrl = this.buildAbsoluteSiteUrl(
1652
1795
  data.siteUrl,
1653
- this.normalizePathForUrl(document.urlPath)
1796
+ `${data.baseUrl}${this.normalizePathForUrl(document.urlPath)}`
1654
1797
  );
1655
1798
  const description = document.description ? ` - ${document.description}` : "";
1656
1799
  lines.push(`- [${document.navTitle}](${documentUrl})${description}`);
@@ -1662,7 +1805,7 @@ var DoculaBuilder = class {
1662
1805
  lines.push("## API Reference");
1663
1806
  if (data.hasApi) {
1664
1807
  lines.push(
1665
- `- [API Documentation](${this.buildAbsoluteSiteUrl(data.siteUrl, "/api")})`
1808
+ `- [API Documentation](${this.buildAbsoluteSiteUrl(data.siteUrl, data.apiUrl)})`
1666
1809
  );
1667
1810
  } else {
1668
1811
  lines.push("- Not available.");
@@ -1671,12 +1814,12 @@ var DoculaBuilder = class {
1671
1814
  lines.push("## Changelog");
1672
1815
  if (data.hasChangelog) {
1673
1816
  lines.push(
1674
- `- [Changelog](${this.buildAbsoluteSiteUrl(data.siteUrl, "/changelog")})`
1817
+ `- [Changelog](${this.buildAbsoluteSiteUrl(data.siteUrl, data.changelogUrl)})`
1675
1818
  );
1676
1819
  for (const entry of changelogEntries.slice(0, 20)) {
1677
1820
  const date = entry.formattedDate || entry.date || "No date";
1678
1821
  lines.push(
1679
- `- [${entry.title}](${this.buildAbsoluteSiteUrl(data.siteUrl, `/changelog/${entry.slug}`)}) (${date})`
1822
+ `- [${entry.title}](${this.buildAbsoluteSiteUrl(data.siteUrl, `${data.changelogUrl}/${entry.slug}`)}) (${date})`
1680
1823
  );
1681
1824
  }
1682
1825
  } else {
@@ -1694,7 +1837,7 @@ var DoculaBuilder = class {
1694
1837
  lines.push(data.siteDescription);
1695
1838
  lines.push("");
1696
1839
  lines.push(
1697
- `Source Index: ${this.buildAbsoluteSiteUrl(data.siteUrl, "/llms.txt")}`
1840
+ `Source Index: ${this.buildAbsoluteSiteUrl(data.siteUrl, `${data.baseUrl}/llms.txt`)}`
1698
1841
  );
1699
1842
  lines.push("");
1700
1843
  lines.push("## Documentation");
@@ -1702,7 +1845,7 @@ var DoculaBuilder = class {
1702
1845
  for (const document of documents) {
1703
1846
  const documentUrl = this.buildAbsoluteSiteUrl(
1704
1847
  data.siteUrl,
1705
- this.normalizePathForUrl(document.urlPath)
1848
+ `${data.baseUrl}${this.normalizePathForUrl(document.urlPath)}`
1706
1849
  );
1707
1850
  const markdownBody = new Writr(document.content).body.trim();
1708
1851
  lines.push("");
@@ -1720,7 +1863,9 @@ var DoculaBuilder = class {
1720
1863
  lines.push("");
1721
1864
  lines.push("## API Reference");
1722
1865
  if (data.hasApi) {
1723
- lines.push(`URL: ${this.buildAbsoluteSiteUrl(data.siteUrl, "/api")}`);
1866
+ lines.push(
1867
+ `URL: ${this.buildAbsoluteSiteUrl(data.siteUrl, data.apiUrl)}`
1868
+ );
1724
1869
  lines.push("");
1725
1870
  const localOpenApiSpec = await this.getSafeLocalOpenApiSpec(data);
1726
1871
  if (localOpenApiSpec) {
@@ -1742,13 +1887,13 @@ var DoculaBuilder = class {
1742
1887
  lines.push("## Changelog");
1743
1888
  if (data.hasChangelog && changelogEntries.length > 0) {
1744
1889
  lines.push(
1745
- `URL: ${this.buildAbsoluteSiteUrl(data.siteUrl, "/changelog")}`
1890
+ `URL: ${this.buildAbsoluteSiteUrl(data.siteUrl, data.changelogUrl)}`
1746
1891
  );
1747
1892
  for (const entry of changelogEntries) {
1748
1893
  lines.push("");
1749
1894
  lines.push(`### ${entry.title}`);
1750
1895
  lines.push(
1751
- `URL: ${this.buildAbsoluteSiteUrl(data.siteUrl, `/changelog/${entry.slug}`)}`
1896
+ `URL: ${this.buildAbsoluteSiteUrl(data.siteUrl, `${data.changelogUrl}/${entry.slug}`)}`
1752
1897
  );
1753
1898
  if (entry.formattedDate || entry.date) {
1754
1899
  lines.push(`Date: ${entry.formattedDate || entry.date}`);
@@ -1765,6 +1910,16 @@ var DoculaBuilder = class {
1765
1910
  lines.push("");
1766
1911
  return lines.join("\n");
1767
1912
  }
1913
+ buildUrlPath(...segments) {
1914
+ const cleaned = segments.filter((s) => Boolean(s)).map((s) => {
1915
+ let start = 0;
1916
+ let end = s.length;
1917
+ while (start < end && s[start] === "/") start++;
1918
+ while (end > start && s[end - 1] === "/") end--;
1919
+ return s.slice(start, end);
1920
+ });
1921
+ return `/${cleaned.filter(Boolean).join("/")}`;
1922
+ }
1768
1923
  buildAbsoluteSiteUrl(siteUrl, urlPath) {
1769
1924
  const normalizedSiteUrl = siteUrl.endsWith("/") ? siteUrl.slice(0, -1) : siteUrl;
1770
1925
  const normalizedPath = urlPath.startsWith("/") ? urlPath : `/${urlPath}`;
@@ -1949,7 +2104,9 @@ var DoculaBuilder = class {
1949
2104
  async buildDocsPages(data) {
1950
2105
  if (data.templates && data.documents?.length) {
1951
2106
  const documentsTemplate = `${data.templatePath}/${data.templates.docPage}`;
1952
- await fs3.promises.mkdir(`${data.output}/docs`, { recursive: true });
2107
+ const resolvedDocsPath = data.docsPath;
2108
+ const docsOutputDir = resolvedDocsPath ? `${data.output}/${resolvedDocsPath}` : `${data.output}`;
2109
+ await fs3.promises.mkdir(docsOutputDir, { recursive: true });
1953
2110
  data.sidebarItems = this.generateSidebarItems(data);
1954
2111
  const promises = data.documents.map(async (document) => {
1955
2112
  const folder = document.urlPath.split("/").slice(0, -1).join("/");
@@ -1974,7 +2131,7 @@ var DoculaBuilder = class {
1974
2131
  throw new Error("No API template or openApiUrl found");
1975
2132
  }
1976
2133
  const swaggerSource = `${data.sitePath}/api/swagger.json`;
1977
- const apiOutputPath = `${data.output}/api`;
2134
+ const apiOutputPath = `${data.output}/${data.apiPath}`;
1978
2135
  await fs3.promises.mkdir(apiOutputPath, { recursive: true });
1979
2136
  if (fs3.existsSync(swaggerSource)) {
1980
2137
  await fs3.promises.copyFile(
@@ -2016,7 +2173,7 @@ var DoculaBuilder = class {
2016
2173
  if (!data.openApiUrl || !data.templates?.api) {
2017
2174
  return;
2018
2175
  }
2019
- const apiPath = `${data.output}/api/index.html`;
2176
+ const apiPath = `${data.output}/${data.apiPath}/index.html`;
2020
2177
  const apiContent = await this.renderApiContent(data);
2021
2178
  await fs3.promises.writeFile(apiPath, apiContent, "utf8");
2022
2179
  }
@@ -2103,7 +2260,7 @@ var DoculaBuilder = class {
2103
2260
  generatedHtml: new Writr(markdownContent).renderSync({ mdx: isMdx }),
2104
2261
  preview: this.generateChangelogPreview(markdownContent, 500, isMdx),
2105
2262
  previewImage,
2106
- urlPath: `/changelog/${slug}/index.html`,
2263
+ urlPath: `/${this.options.changelogPath}/${slug}/index.html`,
2107
2264
  lastModified: fs3.statSync(filePath).mtime.toISOString().split("T")[0]
2108
2265
  };
2109
2266
  }
@@ -2200,7 +2357,7 @@ var DoculaBuilder = class {
2200
2357
  content: body,
2201
2358
  generatedHtml: new Writr(body).renderSync(),
2202
2359
  preview: this.generateChangelogPreview(body),
2203
- urlPath: `/changelog/${slug}/index.html`,
2360
+ urlPath: `/${this.options.changelogPath}/${slug}/index.html`,
2204
2361
  lastModified: dateString
2205
2362
  };
2206
2363
  }
@@ -2226,7 +2383,8 @@ var DoculaBuilder = class {
2226
2383
  for (let page = 1; page <= totalPages; page++) {
2227
2384
  const startIndex = (page - 1) * perPage;
2228
2385
  const pageEntries = allEntries.slice(startIndex, startIndex + perPage);
2229
- const outputPath = page === 1 ? `${data.output}/changelog` : `${data.output}/changelog/page/${page}`;
2386
+ const changelogOutputBase = `${data.output}/${data.changelogPath}`;
2387
+ const outputPath = page === 1 ? changelogOutputBase : `${changelogOutputBase}/page/${page}`;
2230
2388
  const indexPath = `${outputPath}/index.html`;
2231
2389
  const paginationData = {
2232
2390
  ...data,
@@ -2236,8 +2394,8 @@ var DoculaBuilder = class {
2236
2394
  hasPagination: totalPages > 1,
2237
2395
  hasNextPage: page < totalPages,
2238
2396
  hasPrevPage: page > 1,
2239
- nextPageUrl: page < totalPages ? `/changelog/page/${page + 1}/` : "",
2240
- prevPageUrl: page > 1 ? page === 2 ? "/changelog/" : `/changelog/page/${page - 1}/` : ""
2397
+ nextPageUrl: page < totalPages ? `${data.changelogUrl}/page/${page + 1}/` : "",
2398
+ prevPageUrl: page > 1 ? page === 2 ? `${data.changelogUrl}/` : `${data.changelogUrl}/page/${page - 1}/` : ""
2241
2399
  };
2242
2400
  promises.push(
2243
2401
  (async () => {
@@ -2259,7 +2417,7 @@ var DoculaBuilder = class {
2259
2417
  }
2260
2418
  const entryTemplate = `${data.templatePath}/${data.templates.changelogEntry}`;
2261
2419
  const promises = data.changelogEntries.map(async (entry) => {
2262
- const entryOutputPath = `${data.output}/changelog/${entry.slug}`;
2420
+ const entryOutputPath = `${data.output}/${data.changelogPath}/${entry.slug}`;
2263
2421
  await fs3.promises.mkdir(entryOutputPath, { recursive: true });
2264
2422
  const entryContent = await this._ecto.renderFromFile(
2265
2423
  entryTemplate,
@@ -2281,7 +2439,7 @@ var DoculaBuilder = class {
2281
2439
  for (const document of data.documents ?? []) {
2282
2440
  if (document.isRoot) {
2283
2441
  sidebarItems.unshift({
2284
- path: document.urlPath.replace("index.html", ""),
2442
+ path: `${data.baseUrl}${document.urlPath.replace("index.html", "")}`,
2285
2443
  name: document.navTitle,
2286
2444
  order: document.order
2287
2445
  });
@@ -2303,7 +2461,7 @@ var DoculaBuilder = class {
2303
2461
  }
2304
2462
  sidebarItems[sectionIndex].children ??= [];
2305
2463
  sidebarItems[sectionIndex].children.push({
2306
- path: document.urlPath.replace("index.html", ""),
2464
+ path: `${data.baseUrl}${document.urlPath.replace("index.html", "")}`,
2307
2465
  name: document.navTitle,
2308
2466
  order: document.order
2309
2467
  });
@@ -2425,13 +2583,19 @@ var DoculaBuilder = class {
2425
2583
  const isMdx = documentPath.endsWith(".mdx");
2426
2584
  const fileExtension = isMdx ? ".mdx" : ".md";
2427
2585
  const documentsFolderIndex = documentPath.lastIndexOf("/docs/");
2428
- let urlPath = documentPath.slice(documentsFolderIndex).replace(fileExtension, "/index.html");
2429
- let isRoot = urlPath.split("/").length === 3;
2430
- if (!documentPath.slice(documentsFolderIndex + 6).includes("/")) {
2431
- isRoot = true;
2432
- const filePath = documentPath.slice(documentsFolderIndex + 6);
2433
- if (filePath === "index.md" || filePath === "index.mdx") {
2434
- urlPath = documentPath.slice(documentsFolderIndex).replace(fileExtension, ".html");
2586
+ const relativePath = documentPath.slice(documentsFolderIndex + 6);
2587
+ const docsPrefix = this.options.docsPath ? `/${this.options.docsPath}` : "";
2588
+ let urlPath = `${docsPrefix}/${relativePath}`.replace(
2589
+ fileExtension,
2590
+ "/index.html"
2591
+ );
2592
+ const isRoot = !relativePath.includes("/");
2593
+ if (isRoot) {
2594
+ if (relativePath === "index.md" || relativePath === "index.mdx") {
2595
+ urlPath = `${docsPrefix}/${relativePath}`.replace(
2596
+ fileExtension,
2597
+ ".html"
2598
+ );
2435
2599
  }
2436
2600
  }
2437
2601
  if (!this.hasTableOfContents(markdownContent)) {
@@ -2801,7 +2965,11 @@ ${entry}
2801
2965
  openApiUrl: this.options.openApiUrl,
2802
2966
  themeMode: this.options.themeMode,
2803
2967
  cookieAuth: this.options.cookieAuth,
2804
- headerLinks: this.options.headerLinks
2968
+ headerLinks: this.options.headerLinks,
2969
+ baseUrl: this.options.baseUrl,
2970
+ docsPath: this.options.docsPath,
2971
+ apiPath: this.options.apiPath,
2972
+ changelogPath: this.options.changelogPath
2805
2973
  };
2806
2974
  return this._hash.toHashSync(JSON.stringify(relevant));
2807
2975
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docula",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "description": "Beautiful Website for Your Projects",
5
5
  "type": "module",
6
6
  "main": "./dist/docula.js",
@@ -3,7 +3,7 @@
3
3
 
4
4
  <head>
5
5
  {{> header }}
6
- <link rel="stylesheet" href="/css/api.css">
6
+ <link rel="stylesheet" href="{{baseUrl}}/css/api.css">
7
7
  <title>API Reference - {{ siteTitle }}</title>
8
8
  <meta name="description" content="API Reference for {{ siteTitle }}" />
9
9
  </head>
@@ -250,7 +250,7 @@
250
250
  {{> footer }}
251
251
  {{> scripts }}
252
252
 
253
- <script src="/js/api.js"></script>
253
+ <script src="{{baseUrl}}/js/api.js"></script>
254
254
  </body>
255
255
 
256
256
  </html>
@@ -12,7 +12,7 @@
12
12
  <main class="versions-container">
13
13
  <div class="versions-content">
14
14
  <div class="changelog-entry-nav">
15
- <a href="/changelog/">&larr; Back to Changelog</a>
15
+ <a href="{{changelogUrl}}/">&larr; Back to Changelog</a>
16
16
  </div>
17
17
  <div class="changelog-entry changelog-entry-single">
18
18
  <div class="changelog-entry-header">
@@ -16,7 +16,7 @@
16
16
  {{#each entries as |entry|}}
17
17
  <div class="changelog-entry">
18
18
  <div class="changelog-entry-header">
19
- <a class="changelog-entry-title" href="/changelog/{{entry.slug}}/">{{entry.title}}</a>
19
+ <a class="changelog-entry-title" href="{{changelogUrl}}/{{entry.slug}}/">{{entry.title}}</a>
20
20
  {{#if entry.tag}}
21
21
  <span class="changelog-tag changelog-tag-{{entry.tagClass}}">{{entry.tag}}</span>
22
22
  {{/if}}
@@ -3,7 +3,7 @@
3
3
 
4
4
  <head>
5
5
  {{> header }}
6
- <link rel="stylesheet" href="/css/home.css">
6
+ <link rel="stylesheet" href="{{baseUrl}}/css/home.css">
7
7
  <title>{{ siteTitle }}</title>
8
8
  </head>
9
9
 
@@ -24,7 +24,7 @@
24
24
 
25
25
  {{#if content}}
26
26
  <header class="home-hero">
27
- <img src="/logo.svg" alt="logo" />
27
+ <img src="{{baseUrl}}/logo.svg" alt="logo" />
28
28
  {{#if announcement}}
29
29
  <div class="announcement">{{{announcement}}}</div>
30
30
  {{/if}}
@@ -1 +1 @@
1
- <a href="/api" class="home-docs-button">API Reference</a>
1
+ <a href="{{apiUrl}}" class="home-docs-button">API Reference</a>
@@ -1 +1 @@
1
- <a href="/docs" class="home-docs-button">Documentation</a>
1
+ <a href="{{docsUrl}}" class="home-docs-button">Documentation</a>
@@ -1,9 +1,9 @@
1
1
  <footer>
2
2
  {{#if enableLlmsTxt}}
3
- <a href="/llms.txt" class="footer__link">llms.txt</a>
3
+ <a href="{{baseUrl}}/llms.txt" class="footer__link">llms.txt</a>
4
4
  {{/if}}
5
5
  {{#if hasFeed}}
6
- <a href="/feed.xml" class="footer__link" aria-label="RSS Feed">
6
+ <a href="{{baseUrl}}/feed.xml" class="footer__link" aria-label="RSS Feed">
7
7
  <svg class="footer__icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><circle cx="6.18" cy="17.82" r="2.18"/><path d="M4 4.44v2.83c7.03 0 12.73 5.7 12.73 12.73h2.83c0-8.59-6.97-15.56-15.56-15.56zm0 5.66v2.83c3.9 0 7.07 3.17 7.07 7.07h2.83c0-5.47-4.43-9.9-9.9-9.9z"/></svg>
8
8
  </a>
9
9
  {{/if}}
@@ -4,24 +4,24 @@
4
4
  <button class="mobile-menu-toggle" id="mobile-menu-toggle" aria-label="Toggle navigation menu">
5
5
  <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" x2="20" y1="12" y2="12"/><line x1="4" x2="20" y1="6" y2="6"/><line x1="4" x2="20" y1="18" y2="18"/></svg>
6
6
  </button>
7
- <a href="/">
8
- <img alt="{{siteTitle}}" class="logo__img" src="/logo.svg">
7
+ <a href="{{baseUrl}}/">
8
+ <img alt="{{siteTitle}}" class="logo__img" src="{{baseUrl}}/logo.svg">
9
9
  </a>
10
10
  <nav class="header-bottom__nav">
11
11
  {{#if hasDocuments}}
12
- <a class="header-bottom__item" href="/docs/" id="nav-docs">
12
+ <a class="header-bottom__item" href="{{docsUrl}}/" id="nav-docs">
13
13
  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 7v14"/><path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/></svg>
14
14
  <span>Documentation</span>
15
15
  </a>
16
16
  {{/if}}
17
17
  {{#if openApiUrl}}
18
- <a class="header-bottom__item" href="/api" id="nav-api">
18
+ <a class="header-bottom__item" href="{{apiUrl}}" id="nav-api">
19
19
  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m16 18 6-6-6-6"/><path d="m8 6-6 6 6 6"/></svg>
20
20
  <span>API Reference</span>
21
21
  </a>
22
22
  {{/if}}
23
23
  {{#if hasChangelog}}
24
- <a class="header-bottom__item" href="/changelog" id="nav-changelog">
24
+ <a class="header-bottom__item" href="{{changelogUrl}}" id="nav-changelog">
25
25
  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 6a13 13 0 0 0 8.4-2.8A1 1 0 0 1 21 4v12a1 1 0 0 1-1.6.8A13 13 0 0 0 11 14H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2z"/><path d="M6 14a12 12 0 0 0 2.4 7.2 2 2 0 0 0 3.2-2.4A8 8 0 0 1 10 14"/><path d="M8 6v8"/></svg>
26
26
  <span>Changelog</span>
27
27
  </a>
@@ -68,19 +68,19 @@
68
68
  <aside class="mobile-sidebar" id="mobile-sidebar">
69
69
  <nav class="mobile-nav">
70
70
  {{#if hasDocuments}}
71
- <a class="mobile-nav__item" href="/docs/">
71
+ <a class="mobile-nav__item" href="{{docsUrl}}/">
72
72
  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 7v14"/><path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/></svg>
73
73
  <span>Documentation</span>
74
74
  </a>
75
75
  {{/if}}
76
76
  {{#if openApiUrl}}
77
- <a class="mobile-nav__item" href="/api">
77
+ <a class="mobile-nav__item" href="{{apiUrl}}">
78
78
  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m16 18 6-6-6-6"/><path d="m8 6-6 6 6 6"/></svg>
79
79
  <span>API Reference</span>
80
80
  </a>
81
81
  {{/if}}
82
82
  {{#if hasChangelog}}
83
- <a class="mobile-nav__item" href="/changelog">
83
+ <a class="mobile-nav__item" href="{{changelogUrl}}">
84
84
  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 6a13 13 0 0 0 8.4-2.8A1 1 0 0 1 21 4v12a1 1 0 0 1-1.6.8A13 13 0 0 0 11 14H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2z"/><path d="M6 14a12 12 0 0 0 2.4 7.2 2 2 0 0 0 3.2-2.4A8 8 0 0 1 10 14"/><path d="M8 6v8"/></svg>
85
85
  <span>Changelog</span>
86
86
  </a>
@@ -1,10 +1,10 @@
1
1
  <meta charset="UTF-8">
2
2
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
3
3
  <meta name="description" content="{{siteDescription}}">
4
- <link rel="stylesheet" href="/css/variables.css">
5
- <link rel="stylesheet" href="/css/styles.css">
6
- <link rel="stylesheet" href="/css/highlight/styles/base16/docula.css">
7
- <link rel="icon" href="/favicon.ico">
4
+ <link rel="stylesheet" href="{{baseUrl}}/css/variables.css">
5
+ <link rel="stylesheet" href="{{baseUrl}}/css/styles.css">
6
+ <link rel="stylesheet" href="{{baseUrl}}/css/highlight/styles/base16/docula.css">
7
+ <link rel="icon" href="{{baseUrl}}/favicon.ico">
8
8
  <script>
9
9
  (function(){
10
10
  window.__doculaThemeKey = 'docula:theme:' + ({{#if siteUrl}}'{{siteUrl}}'{{else}}location.origin{{/if}}).replace(/^https?:\/\//, '');
@@ -34,7 +34,7 @@
34
34
 
35
35
  {{#if (gt github.releases.length 6)}}
36
36
  <div>
37
- <a class="release-btn" href="/changelog">
37
+ <a class="release-btn" href="{{changelogUrl}}">
38
38
  Full Changelog
39
39
  <span>&rarr;</span>
40
40
  </a>
@@ -1,4 +1,4 @@
1
- <script src="/css/highlight/highlight.min.js"></script>
1
+ <script src="{{baseUrl}}/css/highlight/highlight.min.js"></script>
2
2
  <script>
3
3
  document.addEventListener('DOMContentLoaded', () => {
4
4
  document.querySelectorAll('pre code').forEach(el => { el.textContent = el.textContent.trimEnd(); });