docula 1.2.0 → 1.6.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.js CHANGED
@@ -12,6 +12,7 @@ import updateNotifier from "update-notifier";
12
12
  import fs3 from "fs";
13
13
  import path5 from "path";
14
14
  import { Ecto } from "ecto";
15
+ import { Hashery } from "hashery";
15
16
  import { Writr } from "writr";
16
17
 
17
18
  // src/api-parser.ts
@@ -571,55 +572,52 @@ var DoculaConsole = class {
571
572
  console.log(
572
573
  ` ${green("build")} Build the project. By default just npx docula will build the project if it finds a ./site folder`
573
574
  );
575
+ console.log(
576
+ ` ${green("dev")} Build, watch, and serve the project`
577
+ );
578
+ console.log(` ${green("start")} Build and serve the project`);
574
579
  console.log(
575
580
  ` ${green("serve")} Serve the project as a local website`
576
581
  );
577
582
  console.log(` ${green("help")} Print this help`);
578
583
  console.log(` ${green("version")} Print the version`);
579
584
  console.log();
580
- console.log(bold(cyan(" Arguments init:")));
585
+ console.log(bold(cyan(" Common Options:")));
581
586
  console.log(
582
- ` ${yellow("--typescript")} Generate TypeScript config file (docula.config.ts)`
583
- );
584
- console.log(
585
- ` ${yellow("-s, --site")} Set the path where site files are located`
586
- );
587
- console.log();
588
- console.log(bold(cyan(" Arguments build:")));
589
- console.log(
590
- ` ${yellow("-w, --watch")} watch for changes and rebuild`
587
+ ` ${yellow("-s, --site")} Set the path where site files are located`
591
588
  );
592
589
  console.log(
593
590
  ` ${yellow("-c, --clean")} Clean the output directory before building`
594
591
  );
595
- console.log(
596
- ` ${yellow("-s, --site")} Set the path where site files are located`
597
- );
598
592
  console.log(
599
593
  ` ${yellow("-o, --output")} Set the output directory. Default is ./site/dist`
600
594
  );
601
595
  console.log(
602
- ` ${yellow("-t, --templatePath")} Set the custom template to use`
596
+ ` ${yellow("-p, --port")} Set the port number. Default is 3000`
603
597
  );
604
598
  console.log(
605
- ` ${yellow("-T, --template")} Set the built-in template name (e.g., modern, classic)`
599
+ ` ${yellow("-w, --watch")} Watch for changes and rebuild`
606
600
  );
607
601
  console.log();
608
- console.log(bold(cyan(" Arguments serve:")));
602
+ console.log(bold(cyan(" Init Options:")));
609
603
  console.log(
610
- ` ${yellow("-p, --port")} Set the port number used with serve`
604
+ ` ${yellow("--typescript")} Generate TypeScript config file (docula.config.ts)`
611
605
  );
612
606
  console.log(
613
- ` ${yellow("-b, --build")} Build the site before serving`
607
+ ` ${yellow("--javascript")} Generate JavaScript config file (docula.config.mjs)`
614
608
  );
609
+ console.log();
610
+ console.log(bold(cyan(" Build / Dev Options:")));
615
611
  console.log(
616
- ` ${yellow("-w, --watch")} watch for changes and rebuild`
612
+ ` ${yellow("-t, --templatePath")} Set the custom template to use`
617
613
  );
618
614
  console.log(
619
- ` ${yellow("-c, --clean")} Clean the output directory before building`
615
+ ` ${yellow("-T, --template")} Set the built-in template name (e.g., modern, classic)`
620
616
  );
617
+ console.log();
618
+ console.log(bold(cyan(" Serve Options:")));
621
619
  console.log(
622
- ` ${yellow("-s, --site")} Set the path where site files are located`
620
+ ` ${yellow("-b, --build")} Build the site before serving`
623
621
  );
624
622
  }
625
623
  parseProcessArgv(argv) {
@@ -632,7 +630,28 @@ var DoculaConsole = class {
632
630
  };
633
631
  }
634
632
  getCommand(argv) {
633
+ const flagsWithValues = /* @__PURE__ */ new Set([
634
+ "-s",
635
+ "--site",
636
+ "-o",
637
+ "--output",
638
+ "-p",
639
+ "--port",
640
+ "-t",
641
+ "--templatePath",
642
+ "-T",
643
+ "--template"
644
+ ]);
645
+ let skipNext = false;
635
646
  for (const argument of argv) {
647
+ if (skipNext) {
648
+ skipNext = false;
649
+ continue;
650
+ }
651
+ if (flagsWithValues.has(argument)) {
652
+ skipNext = true;
653
+ continue;
654
+ }
636
655
  switch (argument) {
637
656
  case "init": {
638
657
  return "init";
@@ -643,7 +662,15 @@ var DoculaConsole = class {
643
662
  case "serve": {
644
663
  return "serve";
645
664
  }
646
- case "help": {
665
+ case "dev": {
666
+ return "dev";
667
+ }
668
+ case "start": {
669
+ return "start";
670
+ }
671
+ case "help":
672
+ case "-h":
673
+ case "--help": {
647
674
  return "help";
648
675
  }
649
676
  case "version": {
@@ -662,7 +689,8 @@ var DoculaConsole = class {
662
689
  clean: false,
663
690
  build: false,
664
691
  port: void 0,
665
- typescript: false
692
+ typescript: false,
693
+ javascript: false
666
694
  };
667
695
  for (let i = 0; i < argv.length; i++) {
668
696
  const argument = argv[i];
@@ -720,6 +748,10 @@ var DoculaConsole = class {
720
748
  arguments_.typescript = true;
721
749
  break;
722
750
  }
751
+ case "--javascript": {
752
+ arguments_.javascript = true;
753
+ break;
754
+ }
723
755
  }
724
756
  }
725
757
  return arguments_;
@@ -875,6 +907,18 @@ var Github = class {
875
907
  // src/options.ts
876
908
  import path3 from "path";
877
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
+ }
878
922
  var DoculaOptions = class {
879
923
  /**
880
924
  * Name of the built-in template to use (e.g., "modern", "classic")
@@ -887,7 +931,7 @@ var DoculaOptions = class {
887
931
  /**
888
932
  * Path to the output directory
889
933
  */
890
- output = path3.join(process3.cwd(), "./dist");
934
+ output = "";
891
935
  /**
892
936
  * Path to the site directory
893
937
  */
@@ -928,10 +972,9 @@ var DoculaOptions = class {
928
972
  */
929
973
  enableReleaseChangelog = true;
930
974
  /**
931
- * When false, the first document becomes the home page (index.html)
932
- * and the home.hbs template is not rendered.
975
+ * Number of changelog entries to display per page on the changelog index.
933
976
  */
934
- homePage = true;
977
+ changelogPerPage = 20;
935
978
  /**
936
979
  * When true, generates llms.txt and llms-full.txt files for the built site.
937
980
  */
@@ -948,9 +991,23 @@ var DoculaOptions = class {
948
991
  */
949
992
  autoUpdateIgnores = true;
950
993
  /**
951
- * File extensions to copy as assets from docs/ and changelog/ directories.
952
- * 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.
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.
953
1009
  */
1010
+ changelogPath = "changelog";
954
1011
  /**
955
1012
  * Cookie-based authentication. When set, shows a Login/Logout button
956
1013
  * in the header based on whether a JWT cookie is present.
@@ -1005,6 +1062,9 @@ var DoculaOptions = class {
1005
1062
  if (options) {
1006
1063
  this.parseOptions(options);
1007
1064
  }
1065
+ if (!this.output) {
1066
+ this.output = path3.join(this.sitePath, "dist");
1067
+ }
1008
1068
  }
1009
1069
  // biome-ignore lint/suspicious/noExplicitAny: need to fix
1010
1070
  parseOptions(options) {
@@ -1047,8 +1107,8 @@ var DoculaOptions = class {
1047
1107
  if (options.enableReleaseChangelog !== void 0 && typeof options.enableReleaseChangelog === "boolean") {
1048
1108
  this.enableReleaseChangelog = options.enableReleaseChangelog;
1049
1109
  }
1050
- if (options.homePage !== void 0 && typeof options.homePage === "boolean") {
1051
- this.homePage = options.homePage;
1110
+ if (options.changelogPerPage !== void 0 && typeof options.changelogPerPage === "number" && options.changelogPerPage > 0) {
1111
+ this.changelogPerPage = options.changelogPerPage;
1052
1112
  }
1053
1113
  if (options.enableLlmsTxt !== void 0 && typeof options.enableLlmsTxt === "boolean") {
1054
1114
  this.enableLlmsTxt = options.enableLlmsTxt;
@@ -1062,6 +1122,18 @@ var DoculaOptions = class {
1062
1122
  if (options.cache && typeof options.cache === "object" && options.cache.github !== null && typeof options.cache.github === "object" && typeof options.cache.github.ttl === "number") {
1063
1123
  this.cache = options.cache;
1064
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
+ }
1065
1137
  if (options.allowedAssets && Array.isArray(options.allowedAssets)) {
1066
1138
  this.allowedAssets = options.allowedAssets;
1067
1139
  }
@@ -1076,6 +1148,9 @@ var DoculaOptions = class {
1076
1148
  this.headerLinks = validLinks;
1077
1149
  }
1078
1150
  }
1151
+ if (!options.output && !this.output) {
1152
+ this.output = path3.join(this.sitePath, "dist");
1153
+ }
1079
1154
  }
1080
1155
  };
1081
1156
 
@@ -1113,6 +1188,8 @@ var DoculaBuilder = class {
1113
1188
  _options = new DoculaOptions();
1114
1189
  _ecto;
1115
1190
  _console = new DoculaConsole();
1191
+ _hash = new Hashery();
1192
+ onReleaseChangelog;
1116
1193
  // biome-ignore lint/suspicious/noExplicitAny: need to fix
1117
1194
  constructor(options, engineOptions) {
1118
1195
  if (options) {
@@ -1131,6 +1208,29 @@ var DoculaBuilder = class {
1131
1208
  this.options.sitePath,
1132
1209
  this.options.template
1133
1210
  );
1211
+ const previousManifest = this.loadBuildManifest(this.options.sitePath);
1212
+ const currentConfigHash = this.hashOptions();
1213
+ const currentTemplateHash = this.hashTemplateDirectory(resolvedTemplatePath);
1214
+ const validManifest = previousManifest?.configHash === currentConfigHash ? previousManifest : void 0;
1215
+ const currentDocHashes = this.hashSourceFiles(
1216
+ `${this.options.sitePath}/docs`
1217
+ );
1218
+ const currentChangelogHashes = this.hashSourceFiles(
1219
+ `${this.options.sitePath}/changelog`
1220
+ );
1221
+ const currentAssetHashes = {};
1222
+ if (validManifest && fs3.existsSync(this.options.output) && validManifest.templateHash === currentTemplateHash && this.recordsEqual(validManifest.docs, currentDocHashes) && this.recordsEqual(validManifest.changelog, currentChangelogHashes)) {
1223
+ const assetsChanged = this.hasAssetsChanged(
1224
+ this.options.sitePath,
1225
+ validManifest.assets
1226
+ );
1227
+ if (!assetsChanged) {
1228
+ this._console.success("No changes detected, skipping build");
1229
+ return;
1230
+ }
1231
+ }
1232
+ const cachedDocs = validManifest ? this.loadCachedDocuments(this.options.sitePath) : /* @__PURE__ */ new Map();
1233
+ const cachedChangelog = validManifest ? this.loadCachedChangelog(this.options.sitePath) : /* @__PURE__ */ new Map();
1134
1234
  const doculaData = {
1135
1235
  siteUrl: this.options.siteUrl,
1136
1236
  siteTitle: this.options.siteTitle,
@@ -1141,21 +1241,42 @@ var DoculaBuilder = class {
1141
1241
  githubPath: this.options.githubPath,
1142
1242
  sections: this.options.sections,
1143
1243
  openApiUrl: this.options.openApiUrl,
1144
- homePage: this.options.homePage,
1244
+ hasReadme: fs3.existsSync(`${this.options.sitePath}/README.md`),
1145
1245
  themeMode: this.options.themeMode,
1146
1246
  cookieAuth: this.options.cookieAuth,
1147
1247
  headerLinks: this.options.headerLinks,
1148
- 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
+ )
1149
1259
  };
1260
+ const readmePath = `${this.options.sitePath}/README.md`;
1261
+ if (doculaData.hasReadme) {
1262
+ currentAssetHashes["README.md"] = this.hashFile(readmePath);
1263
+ }
1150
1264
  if (!doculaData.openApiUrl && fs3.existsSync(`${doculaData.sitePath}/api/swagger.json`)) {
1151
- doculaData.openApiUrl = "/api/swagger.json";
1265
+ doculaData.openApiUrl = this.buildUrlPath(
1266
+ this.options.baseUrl,
1267
+ this.options.apiPath,
1268
+ "swagger.json"
1269
+ );
1152
1270
  }
1153
1271
  if (this.options.githubPath) {
1154
1272
  doculaData.github = await this.getGithubData(this.options.githubPath);
1155
1273
  }
1156
1274
  doculaData.documents = this.getDocuments(
1157
1275
  `${doculaData.sitePath}/docs`,
1158
- doculaData
1276
+ doculaData,
1277
+ cachedDocs,
1278
+ validManifest?.docs ?? {},
1279
+ currentDocHashes
1159
1280
  );
1160
1281
  doculaData.sections = this.getSections(
1161
1282
  `${doculaData.sitePath}/docs`,
@@ -1164,14 +1285,31 @@ var DoculaBuilder = class {
1164
1285
  doculaData.hasDocuments = doculaData.documents?.length > 0;
1165
1286
  doculaData.hasFeed = doculaData.hasDocuments;
1166
1287
  const changelogPath = `${doculaData.sitePath}/changelog`;
1167
- const fileChangelogEntries = this.getChangelogEntries(changelogPath);
1288
+ const fileChangelogEntries = this.getChangelogEntries(
1289
+ changelogPath,
1290
+ cachedChangelog,
1291
+ validManifest?.changelog ?? {},
1292
+ currentChangelogHashes
1293
+ );
1168
1294
  const hasChangelogTemplate = await this.getTemplateFile(resolvedTemplatePath, "changelog") !== void 0;
1169
1295
  let allChangelogEntries = [...fileChangelogEntries];
1170
1296
  if (this._options.enableReleaseChangelog && hasChangelogTemplate && doculaData.github?.releases && Array.isArray(doculaData.github.releases) && doculaData.github.releases.length > 0) {
1171
- const releaseEntries = this.getReleasesAsChangelogEntries(
1297
+ let releaseEntries = this.getReleasesAsChangelogEntries(
1172
1298
  // biome-ignore lint/suspicious/noExplicitAny: GitHub release objects
1173
1299
  doculaData.github.releases
1174
1300
  );
1301
+ if (this.onReleaseChangelog) {
1302
+ try {
1303
+ releaseEntries = await this.onReleaseChangelog(
1304
+ releaseEntries,
1305
+ this._console
1306
+ );
1307
+ } catch (error) {
1308
+ this._console.error(
1309
+ `onReleaseChangelog error: ${error.message}`
1310
+ );
1311
+ }
1312
+ }
1175
1313
  allChangelogEntries = [...allChangelogEntries, ...releaseEntries];
1176
1314
  }
1177
1315
  allChangelogEntries.sort((a, b) => {
@@ -1198,17 +1336,21 @@ var DoculaBuilder = class {
1198
1336
  doculaData.hasApi = Boolean(
1199
1337
  doculaData.openApiUrl && doculaData.templates?.api
1200
1338
  );
1339
+ doculaData.lastModified = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1201
1340
  this._console.step("Building pages...");
1202
- if (!this.options.homePage && doculaData.hasDocuments) {
1341
+ if (doculaData.hasReadme) {
1342
+ await this.buildIndexPage(doculaData);
1343
+ this._console.fileBuilt("index.html");
1344
+ } else if (doculaData.hasDocuments) {
1203
1345
  await this.buildDocsHomePage(doculaData);
1204
1346
  this._console.fileBuilt("index.html");
1205
- } else if (!this.options.homePage && !doculaData.hasDocuments) {
1347
+ } else if (doculaData.hasApi) {
1348
+ await this.buildApiHomePage(doculaData);
1349
+ this._console.fileBuilt("index.html");
1350
+ } else {
1206
1351
  this._console.error(
1207
- "homePage is set to false but no documents were found in the docs directory. Add documents to the docs/ folder or set homePage to true."
1352
+ "No content found for the home page. Add a README.md, docs/ folder, or api/swagger.json to your site directory."
1208
1353
  );
1209
- } else {
1210
- await this.buildIndexPage(doculaData);
1211
- this._console.fileBuilt("index.html");
1212
1354
  }
1213
1355
  await this.buildSiteMapPage(doculaData);
1214
1356
  this._console.fileBuilt("sitemap.xml");
@@ -1228,34 +1370,55 @@ var DoculaBuilder = class {
1228
1370
  if (doculaData.hasApi) {
1229
1371
  this._console.step("Building API page...");
1230
1372
  await this.buildApiPage(doculaData);
1231
- this._console.fileBuilt("api/index.html");
1373
+ this._console.fileBuilt(`${this.options.apiPath}/index.html`);
1232
1374
  }
1233
1375
  if (doculaData.hasChangelog) {
1234
1376
  this._console.step("Building changelog...");
1235
1377
  await this.buildChangelogPage(doculaData);
1236
- this._console.fileBuilt("changelog/index.html");
1378
+ this._console.fileBuilt(`${this.options.changelogPath}/index.html`);
1237
1379
  await this.buildChangelogEntryPages(doculaData);
1238
1380
  for (const entry of doculaData.changelogEntries ?? []) {
1239
- this._console.fileBuilt(`changelog/${entry.slug}/index.html`);
1381
+ this._console.fileBuilt(
1382
+ `${this.options.changelogPath}/${entry.slug}/index.html`
1383
+ );
1240
1384
  }
1241
1385
  }
1242
1386
  const siteRelativePath = this.options.sitePath;
1387
+ const previousAssets = validManifest?.assets ?? {};
1243
1388
  this._console.step("Copying assets...");
1244
- if (fs3.existsSync(`${siteRelativePath}/favicon.ico`)) {
1389
+ if (!this.hashAssetAndCheckSkip(
1390
+ `${siteRelativePath}/favicon.ico`,
1391
+ `${this.options.output}/favicon.ico`,
1392
+ "favicon.ico",
1393
+ previousAssets,
1394
+ currentAssetHashes
1395
+ )) {
1245
1396
  await fs3.promises.copyFile(
1246
1397
  `${siteRelativePath}/favicon.ico`,
1247
1398
  `${this.options.output}/favicon.ico`
1248
1399
  );
1249
1400
  this._console.fileCopied("favicon.ico");
1250
1401
  }
1251
- if (fs3.existsSync(`${siteRelativePath}/logo.svg`)) {
1402
+ if (!this.hashAssetAndCheckSkip(
1403
+ `${siteRelativePath}/logo.svg`,
1404
+ `${this.options.output}/logo.svg`,
1405
+ "logo.svg",
1406
+ previousAssets,
1407
+ currentAssetHashes
1408
+ )) {
1252
1409
  await fs3.promises.copyFile(
1253
1410
  `${siteRelativePath}/logo.svg`,
1254
1411
  `${this.options.output}/logo.svg`
1255
1412
  );
1256
1413
  this._console.fileCopied("logo.svg");
1257
1414
  }
1258
- if (fs3.existsSync(`${siteRelativePath}/logo_horizontal.png`)) {
1415
+ if (!this.hashAssetAndCheckSkip(
1416
+ `${siteRelativePath}/logo_horizontal.png`,
1417
+ `${this.options.output}/logo_horizontal.png`,
1418
+ "logo_horizontal.png",
1419
+ previousAssets,
1420
+ currentAssetHashes
1421
+ )) {
1259
1422
  await fs3.promises.copyFile(
1260
1423
  `${siteRelativePath}/logo_horizontal.png`,
1261
1424
  `${this.options.output}/logo_horizontal.png`
@@ -1263,30 +1426,51 @@ var DoculaBuilder = class {
1263
1426
  this._console.fileCopied("logo_horizontal.png");
1264
1427
  }
1265
1428
  if (fs3.existsSync(`${resolvedTemplatePath}/css`)) {
1266
- this.copyDirectory(
1429
+ this.copyDirectoryWithHashing(
1267
1430
  `${resolvedTemplatePath}/css`,
1268
- `${this.options.output}/css`
1431
+ `${this.options.output}/css`,
1432
+ "css",
1433
+ previousAssets,
1434
+ currentAssetHashes
1269
1435
  );
1270
1436
  this._console.fileCopied("css/");
1271
1437
  }
1272
1438
  if (fs3.existsSync(`${resolvedTemplatePath}/js`)) {
1273
- this.copyDirectory(
1439
+ this.copyDirectoryWithHashing(
1274
1440
  `${resolvedTemplatePath}/js`,
1275
- `${this.options.output}/js`
1441
+ `${this.options.output}/js`,
1442
+ "js",
1443
+ previousAssets,
1444
+ currentAssetHashes
1276
1445
  );
1277
1446
  this._console.fileCopied("js/");
1278
1447
  }
1279
- if (fs3.existsSync(`${siteRelativePath}/variables.css`)) {
1448
+ if (!this.hashAssetAndCheckSkip(
1449
+ `${siteRelativePath}/variables.css`,
1450
+ `${this.options.output}/css/variables.css`,
1451
+ "variables.css",
1452
+ previousAssets,
1453
+ currentAssetHashes
1454
+ )) {
1280
1455
  await fs3.promises.copyFile(
1281
1456
  `${siteRelativePath}/variables.css`,
1282
1457
  `${this.options.output}/css/variables.css`
1283
1458
  );
1284
1459
  this._console.fileCopied("css/variables.css");
1285
1460
  }
1286
- this.copyPublicFolder(siteRelativePath, this.options.output);
1461
+ const swaggerPath = `${siteRelativePath}/api/swagger.json`;
1462
+ if (fs3.existsSync(swaggerPath)) {
1463
+ currentAssetHashes["api/swagger.json"] = this.hashFile(swaggerPath);
1464
+ }
1465
+ this.copyPublicFolder(
1466
+ siteRelativePath,
1467
+ this.options.output,
1468
+ validManifest?.assets ?? {},
1469
+ currentAssetHashes
1470
+ );
1287
1471
  this.copyContentAssets(
1288
1472
  `${doculaData.sitePath}/changelog`,
1289
- `${this.options.output}/changelog`
1473
+ `${this.options.output}/${this.options.changelogPath}`
1290
1474
  );
1291
1475
  if (doculaData.documents?.length) {
1292
1476
  this.copyDocumentSiblingAssets(doculaData);
@@ -1295,6 +1479,18 @@ var DoculaBuilder = class {
1295
1479
  this._console.step("Building LLM files...");
1296
1480
  }
1297
1481
  await this.buildLlmsFiles(doculaData);
1482
+ this.ensureCacheInGitignore(this.options.sitePath);
1483
+ const newManifest = {
1484
+ version: 1,
1485
+ configHash: currentConfigHash,
1486
+ templateHash: currentTemplateHash,
1487
+ docs: currentDocHashes,
1488
+ changelog: currentChangelogHashes,
1489
+ assets: currentAssetHashes
1490
+ };
1491
+ this.saveBuildManifest(this.options.sitePath, newManifest);
1492
+ this.saveCachedDocuments(this.options.sitePath, doculaData.documents ?? []);
1493
+ this.saveCachedChangelog(this.options.sitePath, fileChangelogEntries);
1298
1494
  const endTime = Date.now();
1299
1495
  const executionTime = endTime - startTime;
1300
1496
  this._console.success(`Build completed in ${executionTime}ms`);
@@ -1382,16 +1578,30 @@ var DoculaBuilder = class {
1382
1578
  const sitemapPath = `${data.output}/sitemap.xml`;
1383
1579
  const urls = [{ url: data.siteUrl }];
1384
1580
  if (data.documents?.length) {
1385
- urls.push({ url: `${data.siteUrl}/feed.xml` });
1581
+ urls.push({ url: `${data.siteUrl}${data.baseUrl}/feed.xml` });
1386
1582
  }
1387
1583
  if (data.openApiUrl && data.templates?.api) {
1388
- urls.push({ url: `${data.siteUrl}/api` });
1584
+ urls.push({
1585
+ url: `${data.siteUrl}${data.apiUrl}`
1586
+ });
1389
1587
  }
1390
1588
  if (data.hasChangelog && data.templates?.changelog) {
1391
- urls.push({ url: `${data.siteUrl}/changelog` });
1589
+ urls.push({
1590
+ url: `${data.siteUrl}${data.changelogUrl}`
1591
+ });
1592
+ const perPage = this.options.changelogPerPage;
1593
+ const totalPages = Math.max(
1594
+ 1,
1595
+ Math.ceil((data.changelogEntries ?? []).length / perPage)
1596
+ );
1597
+ for (let page = 2; page <= totalPages; page++) {
1598
+ urls.push({
1599
+ url: `${data.siteUrl}${data.changelogUrl}/page/${page}`
1600
+ });
1601
+ }
1392
1602
  for (const entry of data.changelogEntries ?? []) {
1393
1603
  urls.push({
1394
- url: `${data.siteUrl}/changelog/${entry.slug}`
1604
+ url: `${data.siteUrl}${data.changelogUrl}/${entry.slug}`
1395
1605
  });
1396
1606
  }
1397
1607
  }
@@ -1400,7 +1610,7 @@ var DoculaBuilder = class {
1400
1610
  if (urlPath.endsWith("index.html")) {
1401
1611
  urlPath = urlPath.slice(0, -10);
1402
1612
  }
1403
- urls.push({ url: `${data.siteUrl}${urlPath}` });
1613
+ urls.push({ url: `${data.siteUrl}${data.baseUrl}${urlPath}` });
1404
1614
  }
1405
1615
  let xml = '<?xml version="1.0" encoding="UTF-8"?>';
1406
1616
  xml += '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
@@ -1418,8 +1628,14 @@ var DoculaBuilder = class {
1418
1628
  return;
1419
1629
  }
1420
1630
  const feedPath = `${data.output}/feed.xml`;
1421
- const channelLink = this.buildAbsoluteSiteUrl(data.siteUrl, "/");
1422
- const feedUrl = this.buildAbsoluteSiteUrl(data.siteUrl, "/feed.xml");
1631
+ const channelLink = this.buildAbsoluteSiteUrl(
1632
+ data.siteUrl,
1633
+ `${data.baseUrl}/`
1634
+ );
1635
+ const feedUrl = this.buildAbsoluteSiteUrl(
1636
+ data.siteUrl,
1637
+ `${data.baseUrl}/feed.xml`
1638
+ );
1423
1639
  let xml = '<?xml version="1.0" encoding="UTF-8"?>';
1424
1640
  xml += '<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">';
1425
1641
  xml += "<channel>";
@@ -1432,7 +1648,7 @@ var DoculaBuilder = class {
1432
1648
  const itemTitle = document.navTitle || document.title || document.urlPath;
1433
1649
  const itemLink = this.buildAbsoluteSiteUrl(
1434
1650
  data.siteUrl,
1435
- this.normalizePathForUrl(document.urlPath)
1651
+ `${data.baseUrl}${this.normalizePathForUrl(document.urlPath)}`
1436
1652
  );
1437
1653
  const summary = document.description || this.summarizeMarkdown(new Writr(document.content).body);
1438
1654
  xml += "<item>";
@@ -1490,7 +1706,7 @@ var DoculaBuilder = class {
1490
1706
  lines.push(data.siteDescription);
1491
1707
  lines.push("");
1492
1708
  lines.push(
1493
- `- [Full LLM Content](${this.buildAbsoluteSiteUrl(data.siteUrl, "/llms-full.txt")})`
1709
+ `- [Full LLM Content](${this.buildAbsoluteSiteUrl(data.siteUrl, `${data.baseUrl}/llms-full.txt`)})`
1494
1710
  );
1495
1711
  lines.push("");
1496
1712
  lines.push("## Documentation");
@@ -1498,7 +1714,7 @@ var DoculaBuilder = class {
1498
1714
  for (const document of documents) {
1499
1715
  const documentUrl = this.buildAbsoluteSiteUrl(
1500
1716
  data.siteUrl,
1501
- this.normalizePathForUrl(document.urlPath)
1717
+ `${data.baseUrl}${this.normalizePathForUrl(document.urlPath)}`
1502
1718
  );
1503
1719
  const description = document.description ? ` - ${document.description}` : "";
1504
1720
  lines.push(`- [${document.navTitle}](${documentUrl})${description}`);
@@ -1510,7 +1726,7 @@ var DoculaBuilder = class {
1510
1726
  lines.push("## API Reference");
1511
1727
  if (data.hasApi) {
1512
1728
  lines.push(
1513
- `- [API Documentation](${this.buildAbsoluteSiteUrl(data.siteUrl, "/api")})`
1729
+ `- [API Documentation](${this.buildAbsoluteSiteUrl(data.siteUrl, data.apiUrl)})`
1514
1730
  );
1515
1731
  } else {
1516
1732
  lines.push("- Not available.");
@@ -1519,12 +1735,12 @@ var DoculaBuilder = class {
1519
1735
  lines.push("## Changelog");
1520
1736
  if (data.hasChangelog) {
1521
1737
  lines.push(
1522
- `- [Changelog](${this.buildAbsoluteSiteUrl(data.siteUrl, "/changelog")})`
1738
+ `- [Changelog](${this.buildAbsoluteSiteUrl(data.siteUrl, data.changelogUrl)})`
1523
1739
  );
1524
1740
  for (const entry of changelogEntries.slice(0, 20)) {
1525
1741
  const date = entry.formattedDate || entry.date || "No date";
1526
1742
  lines.push(
1527
- `- [${entry.title}](${this.buildAbsoluteSiteUrl(data.siteUrl, `/changelog/${entry.slug}`)}) (${date})`
1743
+ `- [${entry.title}](${this.buildAbsoluteSiteUrl(data.siteUrl, `${data.changelogUrl}/${entry.slug}`)}) (${date})`
1528
1744
  );
1529
1745
  }
1530
1746
  } else {
@@ -1542,7 +1758,7 @@ var DoculaBuilder = class {
1542
1758
  lines.push(data.siteDescription);
1543
1759
  lines.push("");
1544
1760
  lines.push(
1545
- `Source Index: ${this.buildAbsoluteSiteUrl(data.siteUrl, "/llms.txt")}`
1761
+ `Source Index: ${this.buildAbsoluteSiteUrl(data.siteUrl, `${data.baseUrl}/llms.txt`)}`
1546
1762
  );
1547
1763
  lines.push("");
1548
1764
  lines.push("## Documentation");
@@ -1550,7 +1766,7 @@ var DoculaBuilder = class {
1550
1766
  for (const document of documents) {
1551
1767
  const documentUrl = this.buildAbsoluteSiteUrl(
1552
1768
  data.siteUrl,
1553
- this.normalizePathForUrl(document.urlPath)
1769
+ `${data.baseUrl}${this.normalizePathForUrl(document.urlPath)}`
1554
1770
  );
1555
1771
  const markdownBody = new Writr(document.content).body.trim();
1556
1772
  lines.push("");
@@ -1568,7 +1784,9 @@ var DoculaBuilder = class {
1568
1784
  lines.push("");
1569
1785
  lines.push("## API Reference");
1570
1786
  if (data.hasApi) {
1571
- lines.push(`URL: ${this.buildAbsoluteSiteUrl(data.siteUrl, "/api")}`);
1787
+ lines.push(
1788
+ `URL: ${this.buildAbsoluteSiteUrl(data.siteUrl, data.apiUrl)}`
1789
+ );
1572
1790
  lines.push("");
1573
1791
  const localOpenApiSpec = await this.getSafeLocalOpenApiSpec(data);
1574
1792
  if (localOpenApiSpec) {
@@ -1590,13 +1808,13 @@ var DoculaBuilder = class {
1590
1808
  lines.push("## Changelog");
1591
1809
  if (data.hasChangelog && changelogEntries.length > 0) {
1592
1810
  lines.push(
1593
- `URL: ${this.buildAbsoluteSiteUrl(data.siteUrl, "/changelog")}`
1811
+ `URL: ${this.buildAbsoluteSiteUrl(data.siteUrl, data.changelogUrl)}`
1594
1812
  );
1595
1813
  for (const entry of changelogEntries) {
1596
1814
  lines.push("");
1597
1815
  lines.push(`### ${entry.title}`);
1598
1816
  lines.push(
1599
- `URL: ${this.buildAbsoluteSiteUrl(data.siteUrl, `/changelog/${entry.slug}`)}`
1817
+ `URL: ${this.buildAbsoluteSiteUrl(data.siteUrl, `${data.changelogUrl}/${entry.slug}`)}`
1600
1818
  );
1601
1819
  if (entry.formattedDate || entry.date) {
1602
1820
  lines.push(`Date: ${entry.formattedDate || entry.date}`);
@@ -1613,6 +1831,16 @@ var DoculaBuilder = class {
1613
1831
  lines.push("");
1614
1832
  return lines.join("\n");
1615
1833
  }
1834
+ buildUrlPath(...segments) {
1835
+ const cleaned = segments.filter((s) => Boolean(s)).map((s) => {
1836
+ let start = 0;
1837
+ let end = s.length;
1838
+ while (start < end && s[start] === "/") start++;
1839
+ while (end > start && s[end - 1] === "/") end--;
1840
+ return s.slice(start, end);
1841
+ });
1842
+ return `/${cleaned.filter(Boolean).join("/")}`;
1843
+ }
1616
1844
  buildAbsoluteSiteUrl(siteUrl, urlPath) {
1617
1845
  const normalizedSiteUrl = siteUrl.endsWith("/") ? siteUrl.slice(0, -1) : siteUrl;
1618
1846
  const normalizedPath = urlPath.startsWith("/") ? urlPath : `/${urlPath}`;
@@ -1756,10 +1984,10 @@ var DoculaBuilder = class {
1756
1984
  }
1757
1985
  async buildDocsHomePage(data) {
1758
1986
  if (!data.templates?.docPage) {
1759
- throw new Error("No docPage template found for homePage");
1987
+ throw new Error("No docPage template found for docs home page");
1760
1988
  }
1761
1989
  if (!data.documents?.length) {
1762
- throw new Error("No documents found for homePage");
1990
+ throw new Error("No documents found for docs home page");
1763
1991
  }
1764
1992
  const indexPath = `${data.output}/index.html`;
1765
1993
  await fs3.promises.mkdir(data.output, { recursive: true });
@@ -1797,7 +2025,9 @@ var DoculaBuilder = class {
1797
2025
  async buildDocsPages(data) {
1798
2026
  if (data.templates && data.documents?.length) {
1799
2027
  const documentsTemplate = `${data.templatePath}/${data.templates.docPage}`;
1800
- await fs3.promises.mkdir(`${data.output}/docs`, { recursive: true });
2028
+ const resolvedDocsPath = data.docsPath;
2029
+ const docsOutputDir = resolvedDocsPath ? `${data.output}/${resolvedDocsPath}` : `${data.output}`;
2030
+ await fs3.promises.mkdir(docsOutputDir, { recursive: true });
1801
2031
  data.sidebarItems = this.generateSidebarItems(data);
1802
2032
  const promises = data.documents.map(async (document) => {
1803
2033
  const folder = document.urlPath.split("/").slice(0, -1).join("/");
@@ -1817,14 +2047,13 @@ var DoculaBuilder = class {
1817
2047
  throw new Error("No templates found");
1818
2048
  }
1819
2049
  }
1820
- async buildApiPage(data) {
2050
+ async renderApiContent(data) {
1821
2051
  if (!data.openApiUrl || !data.templates?.api) {
1822
- return;
2052
+ throw new Error("No API template or openApiUrl found");
1823
2053
  }
1824
- const apiPath = `${data.output}/api/index.html`;
1825
- const apiOutputPath = `${data.output}/api`;
1826
- await fs3.promises.mkdir(apiOutputPath, { recursive: true });
1827
2054
  const swaggerSource = `${data.sitePath}/api/swagger.json`;
2055
+ const apiOutputPath = `${data.output}/${data.apiPath}`;
2056
+ await fs3.promises.mkdir(apiOutputPath, { recursive: true });
1828
2057
  if (fs3.existsSync(swaggerSource)) {
1829
2058
  await fs3.promises.copyFile(
1830
2059
  swaggerSource,
@@ -1855,14 +2084,27 @@ var DoculaBuilder = class {
1855
2084
  }
1856
2085
  }
1857
2086
  const apiTemplate = `${data.templatePath}/${data.templates.api}`;
1858
- const apiContent = await this._ecto.renderFromFile(
2087
+ return this._ecto.renderFromFile(
1859
2088
  apiTemplate,
1860
2089
  { ...data, specUrl: data.openApiUrl, apiSpec },
1861
2090
  data.templatePath
1862
2091
  );
2092
+ }
2093
+ async buildApiPage(data) {
2094
+ if (!data.openApiUrl || !data.templates?.api) {
2095
+ return;
2096
+ }
2097
+ const apiPath = `${data.output}/${data.apiPath}/index.html`;
2098
+ const apiContent = await this.renderApiContent(data);
1863
2099
  await fs3.promises.writeFile(apiPath, apiContent, "utf8");
1864
2100
  }
1865
- getChangelogEntries(changelogPath) {
2101
+ async buildApiHomePage(data) {
2102
+ const indexPath = `${data.output}/index.html`;
2103
+ await fs3.promises.mkdir(data.output, { recursive: true });
2104
+ const apiContent = await this.renderApiContent(data);
2105
+ await fs3.promises.writeFile(indexPath, apiContent, "utf8");
2106
+ }
2107
+ getChangelogEntries(changelogPath, cachedEntries, previousHashes, currentHashes) {
1866
2108
  const entries = [];
1867
2109
  if (!fs3.existsSync(changelogPath)) {
1868
2110
  return entries;
@@ -1872,6 +2114,16 @@ var DoculaBuilder = class {
1872
2114
  const filePath = `${changelogPath}/${file}`;
1873
2115
  const stats = fs3.statSync(filePath);
1874
2116
  if (stats.isFile() && (file.endsWith(".md") || file.endsWith(".mdx"))) {
2117
+ if (cachedEntries && previousHashes && currentHashes) {
2118
+ const slug = path5.basename(file, path5.extname(file));
2119
+ const hash = currentHashes[file] ?? this.hashFile(filePath);
2120
+ const prevHash = previousHashes[file];
2121
+ const cached = cachedEntries.get(slug);
2122
+ if (cached && prevHash === hash) {
2123
+ entries.push(cached);
2124
+ continue;
2125
+ }
2126
+ }
1875
2127
  const entry = this.parseChangelogEntry(filePath);
1876
2128
  entries.push(entry);
1877
2129
  }
@@ -1898,7 +2150,7 @@ var DoculaBuilder = class {
1898
2150
  const matterData = writr.frontMatter;
1899
2151
  const markdownContent = writr.body;
1900
2152
  const fileName = path5.basename(filePath, path5.extname(filePath));
1901
- const slug = fileName.replace(/^\d{4}-\d{2}-\d{2}-/, "");
2153
+ const slug = fileName;
1902
2154
  const isMdx = filePath.endsWith(".mdx");
1903
2155
  const tag = matterData.tag;
1904
2156
  const tagClass = tag ? tag.toLowerCase().replace(/\s+/g, "-") : void 0;
@@ -1917,6 +2169,7 @@ var DoculaBuilder = class {
1917
2169
  day: "numeric"
1918
2170
  });
1919
2171
  }
2172
+ const previewImage = matterData.previewImage;
1920
2173
  return {
1921
2174
  title: matterData.title ?? fileName,
1922
2175
  date: dateString,
@@ -1926,9 +2179,73 @@ var DoculaBuilder = class {
1926
2179
  slug,
1927
2180
  content: markdownContent,
1928
2181
  generatedHtml: new Writr(markdownContent).renderSync({ mdx: isMdx }),
1929
- urlPath: `/changelog/${slug}/index.html`
2182
+ preview: this.generateChangelogPreview(markdownContent, 500, isMdx),
2183
+ previewImage,
2184
+ urlPath: `/${this.options.changelogPath}/${slug}/index.html`,
2185
+ lastModified: fs3.statSync(filePath).mtime.toISOString().split("T")[0]
1930
2186
  };
1931
2187
  }
2188
+ generateChangelogPreview(markdown, maxLength = 500, mdx = false) {
2189
+ const minLength = 300;
2190
+ let cleaned = markdown.split("\n").filter((line) => !/^#{1,6}\s/.test(line)).join("\n");
2191
+ cleaned = cleaned.replace(/^\n+/, "");
2192
+ cleaned = cleaned.replace(/!\[[^\]]*\]\([^)]*\)/g, "");
2193
+ cleaned = cleaned.replace(/\[([^\]]*)\]\([^)]*\)/g, "$1");
2194
+ cleaned = cleaned.replace(/^\n+/, "").trim();
2195
+ if (cleaned.length <= minLength) {
2196
+ return new Writr(cleaned).renderSync({ mdx });
2197
+ }
2198
+ const searchArea = cleaned.slice(0, maxLength);
2199
+ let splitIndex = -1;
2200
+ let pos = searchArea.lastIndexOf("\n\n");
2201
+ while (pos >= 0) {
2202
+ if (pos >= minLength) {
2203
+ splitIndex = pos;
2204
+ break;
2205
+ }
2206
+ if (splitIndex === -1) {
2207
+ splitIndex = pos;
2208
+ }
2209
+ pos = searchArea.lastIndexOf("\n\n", pos - 1);
2210
+ }
2211
+ if (splitIndex === -1) {
2212
+ const lastNewline = searchArea.lastIndexOf("\n");
2213
+ if (lastNewline >= minLength) {
2214
+ const nextLine = cleaned.slice(lastNewline + 1);
2215
+ if (/^[-*]\s/.test(nextLine) || /^\d+\.\s/.test(nextLine)) {
2216
+ splitIndex = lastNewline;
2217
+ }
2218
+ }
2219
+ if (splitIndex === -1) {
2220
+ const lines = searchArea.split("\n");
2221
+ let charCount = 0;
2222
+ let lastItemEnd = -1;
2223
+ for (const line of lines) {
2224
+ const lineEnd = charCount + line.length;
2225
+ if (lineEnd <= maxLength && (/^[-*]\s/.test(line) || /^\d+\.\s/.test(line))) {
2226
+ if (charCount > 0 && charCount >= minLength) {
2227
+ lastItemEnd = charCount - 1;
2228
+ }
2229
+ }
2230
+ charCount = lineEnd + 1;
2231
+ }
2232
+ if (lastItemEnd > 0) {
2233
+ splitIndex = lastItemEnd;
2234
+ }
2235
+ }
2236
+ }
2237
+ if (splitIndex > 0) {
2238
+ const truncated2 = cleaned.slice(0, splitIndex).trim();
2239
+ return new Writr(truncated2).renderSync({ mdx });
2240
+ }
2241
+ let truncated = cleaned.slice(0, maxLength);
2242
+ const lastSpace = truncated.lastIndexOf(" ");
2243
+ if (lastSpace > 0) {
2244
+ truncated = truncated.slice(0, lastSpace);
2245
+ }
2246
+ truncated += "...";
2247
+ return new Writr(truncated).renderSync({ mdx });
2248
+ }
1932
2249
  convertReleaseToChangelogEntry(release) {
1933
2250
  const tagName = release.tag_name ?? "";
1934
2251
  const slug = tagName.replace(/\./g, "-");
@@ -1960,7 +2277,9 @@ var DoculaBuilder = class {
1960
2277
  slug,
1961
2278
  content: body,
1962
2279
  generatedHtml: new Writr(body).renderSync(),
1963
- urlPath: `/changelog/${slug}/index.html`
2280
+ preview: this.generateChangelogPreview(body),
2281
+ urlPath: `/${this.options.changelogPath}/${slug}/index.html`,
2282
+ lastModified: dateString
1964
2283
  };
1965
2284
  }
1966
2285
  getReleasesAsChangelogEntries(releases) {
@@ -1977,16 +2296,41 @@ var DoculaBuilder = class {
1977
2296
  if (!data.hasChangelog || !data.templates?.changelog) {
1978
2297
  return;
1979
2298
  }
1980
- const changelogOutputPath = `${data.output}/changelog`;
1981
- const changelogIndexPath = `${changelogOutputPath}/index.html`;
1982
- await fs3.promises.mkdir(changelogOutputPath, { recursive: true });
2299
+ const allEntries = data.changelogEntries ?? [];
2300
+ const perPage = this.options.changelogPerPage;
2301
+ const totalPages = Math.max(1, Math.ceil(allEntries.length / perPage));
1983
2302
  const changelogTemplate = `${data.templatePath}/${data.templates.changelog}`;
1984
- const changelogContent = await this._ecto.renderFromFile(
1985
- changelogTemplate,
1986
- { ...data, entries: data.changelogEntries },
1987
- data.templatePath
1988
- );
1989
- await fs3.promises.writeFile(changelogIndexPath, changelogContent, "utf8");
2303
+ const promises = [];
2304
+ for (let page = 1; page <= totalPages; page++) {
2305
+ const startIndex = (page - 1) * perPage;
2306
+ const pageEntries = allEntries.slice(startIndex, startIndex + perPage);
2307
+ const changelogOutputBase = `${data.output}/${data.changelogPath}`;
2308
+ const outputPath = page === 1 ? changelogOutputBase : `${changelogOutputBase}/page/${page}`;
2309
+ const indexPath = `${outputPath}/index.html`;
2310
+ const paginationData = {
2311
+ ...data,
2312
+ entries: pageEntries,
2313
+ currentPage: page,
2314
+ totalPages,
2315
+ hasPagination: totalPages > 1,
2316
+ hasNextPage: page < totalPages,
2317
+ hasPrevPage: page > 1,
2318
+ nextPageUrl: page < totalPages ? `${data.changelogUrl}/page/${page + 1}/` : "",
2319
+ prevPageUrl: page > 1 ? page === 2 ? `${data.changelogUrl}/` : `${data.changelogUrl}/page/${page - 1}/` : ""
2320
+ };
2321
+ promises.push(
2322
+ (async () => {
2323
+ await fs3.promises.mkdir(outputPath, { recursive: true });
2324
+ const content = await this._ecto.renderFromFile(
2325
+ changelogTemplate,
2326
+ paginationData,
2327
+ data.templatePath
2328
+ );
2329
+ await fs3.promises.writeFile(indexPath, content, "utf8");
2330
+ })()
2331
+ );
2332
+ }
2333
+ await Promise.all(promises);
1990
2334
  }
1991
2335
  async buildChangelogEntryPages(data) {
1992
2336
  if (!data.hasChangelog || !data.templates?.changelogEntry || !data.changelogEntries?.length) {
@@ -1994,7 +2338,7 @@ var DoculaBuilder = class {
1994
2338
  }
1995
2339
  const entryTemplate = `${data.templatePath}/${data.templates.changelogEntry}`;
1996
2340
  const promises = data.changelogEntries.map(async (entry) => {
1997
- const entryOutputPath = `${data.output}/changelog/${entry.slug}`;
2341
+ const entryOutputPath = `${data.output}/${data.changelogPath}/${entry.slug}`;
1998
2342
  await fs3.promises.mkdir(entryOutputPath, { recursive: true });
1999
2343
  const entryContent = await this._ecto.renderFromFile(
2000
2344
  entryTemplate,
@@ -2007,11 +2351,16 @@ var DoculaBuilder = class {
2007
2351
  await Promise.all(promises);
2008
2352
  }
2009
2353
  generateSidebarItems(data) {
2010
- let sidebarItems = [...data.sections ?? []];
2354
+ let sidebarItems = (data.sections ?? []).map(
2355
+ (section) => ({
2356
+ ...section,
2357
+ children: section.children ? [...section.children] : void 0
2358
+ })
2359
+ );
2011
2360
  for (const document of data.documents ?? []) {
2012
2361
  if (document.isRoot) {
2013
2362
  sidebarItems.unshift({
2014
- path: document.urlPath.replace("index.html", ""),
2363
+ path: `${data.baseUrl}${document.urlPath.replace("index.html", "")}`,
2015
2364
  name: document.navTitle,
2016
2365
  order: document.order
2017
2366
  });
@@ -2033,7 +2382,7 @@ var DoculaBuilder = class {
2033
2382
  }
2034
2383
  sidebarItems[sectionIndex].children ??= [];
2035
2384
  sidebarItems[sectionIndex].children.push({
2036
- path: document.urlPath.replace("index.html", ""),
2385
+ path: `${data.baseUrl}${document.urlPath.replace("index.html", "")}`,
2037
2386
  name: document.navTitle,
2038
2387
  order: document.order
2039
2388
  });
@@ -2056,27 +2405,49 @@ var DoculaBuilder = class {
2056
2405
  );
2057
2406
  return sidebarItems;
2058
2407
  }
2059
- getDocuments(sitePath, doculaData) {
2408
+ getDocuments(sitePath, doculaData, cachedDocs, previousDocHashes, currentDocHashes) {
2060
2409
  let documents = [];
2061
2410
  if (fs3.existsSync(sitePath)) {
2062
- documents = this.getDocumentInDirectory(sitePath);
2411
+ documents = this.getDocumentInDirectory(
2412
+ sitePath,
2413
+ sitePath,
2414
+ cachedDocs,
2415
+ previousDocHashes,
2416
+ currentDocHashes
2417
+ );
2063
2418
  doculaData.sections = this.getSections(sitePath, this.options);
2064
2419
  for (const section of doculaData.sections) {
2065
2420
  const sectionPath = `${sitePath}/${section.path}`;
2066
- const sectionDocuments = this.getDocumentInDirectory(sectionPath);
2421
+ const sectionDocuments = this.getDocumentInDirectory(
2422
+ sectionPath,
2423
+ sitePath,
2424
+ cachedDocs,
2425
+ previousDocHashes,
2426
+ currentDocHashes
2427
+ );
2067
2428
  documents = [...documents, ...sectionDocuments];
2068
2429
  }
2069
2430
  }
2070
2431
  return documents;
2071
2432
  }
2072
- getDocumentInDirectory(sitePath) {
2433
+ getDocumentInDirectory(sitePath, docsRootPath, cachedDocs, previousDocHashes, currentDocHashes) {
2073
2434
  const documents = [];
2074
2435
  const documentList = fs3.readdirSync(sitePath);
2075
2436
  if (documentList.length > 0) {
2076
2437
  for (const document of documentList) {
2077
2438
  const documentPath = `${sitePath}/${document}`;
2439
+ const relativeKey = path5.relative(docsRootPath, documentPath);
2078
2440
  const stats = fs3.statSync(documentPath);
2079
2441
  if (stats.isFile() && (document.endsWith(".md") || document.endsWith(".mdx"))) {
2442
+ if (cachedDocs && previousDocHashes && currentDocHashes) {
2443
+ const hash = currentDocHashes[relativeKey] ?? this.hashFile(documentPath);
2444
+ const prevHash = previousDocHashes[relativeKey];
2445
+ const cached = cachedDocs.get(relativeKey);
2446
+ if (cached && prevHash === hash) {
2447
+ documents.push(cached);
2448
+ continue;
2449
+ }
2450
+ }
2080
2451
  const documentData = this.parseDocumentData(documentPath);
2081
2452
  documents.push(documentData);
2082
2453
  }
@@ -2133,13 +2504,19 @@ var DoculaBuilder = class {
2133
2504
  const isMdx = documentPath.endsWith(".mdx");
2134
2505
  const fileExtension = isMdx ? ".mdx" : ".md";
2135
2506
  const documentsFolderIndex = documentPath.lastIndexOf("/docs/");
2136
- let urlPath = documentPath.slice(documentsFolderIndex).replace(fileExtension, "/index.html");
2137
- let isRoot = urlPath.split("/").length === 3;
2138
- if (!documentPath.slice(documentsFolderIndex + 6).includes("/")) {
2139
- isRoot = true;
2140
- const filePath = documentPath.slice(documentsFolderIndex + 6);
2141
- if (filePath === "index.md" || filePath === "index.mdx") {
2142
- urlPath = documentPath.slice(documentsFolderIndex).replace(fileExtension, ".html");
2507
+ const relativePath = documentPath.slice(documentsFolderIndex + 6);
2508
+ const docsPrefix = this.options.docsPath ? `/${this.options.docsPath}` : "";
2509
+ let urlPath = `${docsPrefix}/${relativePath}`.replace(
2510
+ fileExtension,
2511
+ "/index.html"
2512
+ );
2513
+ const isRoot = !relativePath.includes("/");
2514
+ if (isRoot) {
2515
+ if (relativePath === "index.md" || relativePath === "index.mdx") {
2516
+ urlPath = `${docsPrefix}/${relativePath}`.replace(
2517
+ fileExtension,
2518
+ ".html"
2519
+ );
2143
2520
  }
2144
2521
  }
2145
2522
  if (!this.hasTableOfContents(markdownContent)) {
@@ -2166,7 +2543,8 @@ ${markdownContent.slice(firstH2)}`;
2166
2543
  }),
2167
2544
  documentPath,
2168
2545
  urlPath,
2169
- isRoot
2546
+ isRoot,
2547
+ lastModified: fs3.statSync(documentPath).mtime.toISOString().split("T")[0]
2170
2548
  };
2171
2549
  }
2172
2550
  hasTableOfContents(markdown) {
@@ -2200,9 +2578,45 @@ ${markdownContent.slice(firstH2)}`;
2200
2578
  return resolvedTemplatePath;
2201
2579
  }
2202
2580
  const overrideFiles = this.listFilesRecursive(overrideDir);
2203
- if (fs3.existsSync(cacheDir) && this.isCacheFresh(overrideDir, cacheDir, overrideFiles)) {
2204
- this._console.step("Using cached template overrides...");
2205
- return cacheDir;
2581
+ if (fs3.existsSync(cacheDir)) {
2582
+ const diff = this.getChangedOverrides(
2583
+ overrideDir,
2584
+ cacheDir,
2585
+ overrideFiles
2586
+ );
2587
+ if (diff) {
2588
+ const hasChanges = diff.added.length > 0 || diff.changed.length > 0 || diff.removed.length > 0;
2589
+ if (!hasChanges) {
2590
+ this._console.step("Using cached template overrides...");
2591
+ return cacheDir;
2592
+ }
2593
+ this._console.step("Updating template overrides...");
2594
+ for (const file of diff.added) {
2595
+ this._console.info(`Template override added: ${file}`);
2596
+ const targetPath = path5.join(cacheDir, file);
2597
+ fs3.mkdirSync(path5.dirname(targetPath), { recursive: true });
2598
+ fs3.copyFileSync(path5.join(overrideDir, file), targetPath);
2599
+ }
2600
+ for (const file of diff.changed) {
2601
+ this._console.info(`Template override changed: ${file}`);
2602
+ const targetPath = path5.join(cacheDir, file);
2603
+ fs3.mkdirSync(path5.dirname(targetPath), { recursive: true });
2604
+ fs3.copyFileSync(path5.join(overrideDir, file), targetPath);
2605
+ }
2606
+ for (const file of diff.removed) {
2607
+ this._console.info(`Template override removed: ${file}`);
2608
+ const cachedPath = path5.join(cacheDir, file);
2609
+ const originalPath = path5.join(resolvedTemplatePath, file);
2610
+ if (fs3.existsSync(originalPath)) {
2611
+ fs3.copyFileSync(originalPath, cachedPath);
2612
+ } else if (fs3.existsSync(cachedPath)) {
2613
+ fs3.unlinkSync(cachedPath);
2614
+ }
2615
+ }
2616
+ const manifestPath2 = path5.join(cacheDir, ".manifest.json");
2617
+ fs3.writeFileSync(manifestPath2, JSON.stringify(diff.currentHashes));
2618
+ return cacheDir;
2619
+ }
2206
2620
  }
2207
2621
  if (overrideFiles.length > 0) {
2208
2622
  this._console.step("Applying template overrides...");
@@ -2217,8 +2631,12 @@ ${markdownContent.slice(firstH2)}`;
2217
2631
  fs3.mkdirSync(cacheDir, { recursive: true });
2218
2632
  this.copyDirectory(resolvedTemplatePath, cacheDir);
2219
2633
  this.copyDirectory(overrideDir, cacheDir);
2634
+ const currentHashes = {};
2635
+ for (const file of overrideFiles) {
2636
+ currentHashes[file] = this.hashFile(path5.join(overrideDir, file));
2637
+ }
2220
2638
  const manifestPath = path5.join(cacheDir, ".manifest.json");
2221
- fs3.writeFileSync(manifestPath, JSON.stringify(overrideFiles));
2639
+ fs3.writeFileSync(manifestPath, JSON.stringify(currentHashes));
2222
2640
  return cacheDir;
2223
2641
  }
2224
2642
  ensureCacheInGitignore(sitePath) {
@@ -2245,34 +2663,43 @@ ${entry}
2245
2663
  this._console.info("Created .gitignore with .cache");
2246
2664
  }
2247
2665
  }
2248
- isCacheFresh(overrideDir, cacheDir, overrideFiles) {
2666
+ getChangedOverrides(overrideDir, cacheDir, overrideFiles) {
2249
2667
  const manifestPath = path5.join(cacheDir, ".manifest.json");
2250
2668
  if (!fs3.existsSync(manifestPath)) {
2251
- return false;
2669
+ return void 0;
2252
2670
  }
2671
+ let previousHashes;
2253
2672
  try {
2254
- const previousFiles = JSON.parse(
2673
+ previousHashes = JSON.parse(
2255
2674
  fs3.readFileSync(manifestPath, "utf8")
2256
2675
  );
2257
- if (previousFiles.length !== overrideFiles.length || !previousFiles.every((f, i) => f === overrideFiles[i])) {
2258
- return false;
2259
- }
2260
2676
  } catch {
2261
- return false;
2677
+ return void 0;
2262
2678
  }
2679
+ const currentHashes = {};
2263
2680
  for (const file of overrideFiles) {
2264
- const overridePath = path5.join(overrideDir, file);
2265
- const cachedPath = path5.join(cacheDir, file);
2266
- if (!fs3.existsSync(cachedPath)) {
2267
- return false;
2681
+ currentHashes[file] = this.hashFile(path5.join(overrideDir, file));
2682
+ }
2683
+ const added = [];
2684
+ const changed = [];
2685
+ const removed = [];
2686
+ for (const file of overrideFiles) {
2687
+ if (!(file in previousHashes)) {
2688
+ added.push(file);
2689
+ } else if (currentHashes[file] !== previousHashes[file]) {
2690
+ changed.push(file);
2268
2691
  }
2269
- const overrideMtime = fs3.statSync(overridePath).mtimeMs;
2270
- const cachedMtime = fs3.statSync(cachedPath).mtimeMs;
2271
- if (overrideMtime > cachedMtime) {
2272
- return false;
2692
+ }
2693
+ for (const file of Object.keys(previousHashes)) {
2694
+ if (!overrideFiles.includes(file)) {
2695
+ removed.push(file);
2273
2696
  }
2274
2697
  }
2275
- return true;
2698
+ return { added, changed, removed, currentHashes };
2699
+ }
2700
+ hashFile(filePath) {
2701
+ const content = fs3.readFileSync(filePath);
2702
+ return this._hash.toHashSync(content);
2276
2703
  }
2277
2704
  listFilesRecursive(dir, prefix = "") {
2278
2705
  const results = [];
@@ -2316,16 +2743,23 @@ ${entry}
2316
2743
  }
2317
2744
  }
2318
2745
  }
2319
- copyPublicFolder(sitePath, output) {
2746
+ copyPublicFolder(sitePath, output, previousAssets, currentAssets) {
2320
2747
  const publicPath = `${sitePath}/public`;
2321
2748
  if (!fs3.existsSync(publicPath)) {
2322
2749
  return;
2323
2750
  }
2324
2751
  this._console.step("Copying public folder...");
2325
2752
  const resolvedOutput = path5.resolve(output);
2326
- this.copyPublicDirectory(publicPath, output, publicPath, resolvedOutput);
2753
+ this.copyPublicDirectory(
2754
+ publicPath,
2755
+ output,
2756
+ publicPath,
2757
+ resolvedOutput,
2758
+ previousAssets,
2759
+ currentAssets
2760
+ );
2327
2761
  }
2328
- copyPublicDirectory(source, target, basePath, output) {
2762
+ copyPublicDirectory(source, target, basePath, output, previousAssets, currentAssets) {
2329
2763
  const files = fs3.readdirSync(source);
2330
2764
  for (const file of files) {
2331
2765
  const sourcePath = `${source}/${file}`;
@@ -2341,8 +2775,21 @@ ${entry}
2341
2775
  }
2342
2776
  if (stat.isDirectory()) {
2343
2777
  fs3.mkdirSync(targetPath, { recursive: true });
2344
- this.copyPublicDirectory(sourcePath, targetPath, basePath, output);
2778
+ this.copyPublicDirectory(
2779
+ sourcePath,
2780
+ targetPath,
2781
+ basePath,
2782
+ output,
2783
+ previousAssets,
2784
+ currentAssets
2785
+ );
2345
2786
  } else {
2787
+ const assetKey = `public/${relativePath}`;
2788
+ const hash = this.hashFile(sourcePath);
2789
+ currentAssets[assetKey] = hash;
2790
+ if (previousAssets[assetKey] === hash && fs3.existsSync(targetPath)) {
2791
+ continue;
2792
+ }
2346
2793
  fs3.mkdirSync(target, { recursive: true });
2347
2794
  fs3.copyFileSync(sourcePath, targetPath);
2348
2795
  this._console.fileCopied(relativePath);
@@ -2397,6 +2844,214 @@ ${entry}
2397
2844
  }
2398
2845
  return results;
2399
2846
  }
2847
+ loadBuildManifest(sitePath) {
2848
+ const manifestPath = path5.join(
2849
+ sitePath,
2850
+ ".cache",
2851
+ "build",
2852
+ "manifest.json"
2853
+ );
2854
+ if (!fs3.existsSync(manifestPath)) {
2855
+ return void 0;
2856
+ }
2857
+ try {
2858
+ const data = JSON.parse(
2859
+ fs3.readFileSync(manifestPath, "utf8")
2860
+ );
2861
+ if (data.version !== 1) {
2862
+ return void 0;
2863
+ }
2864
+ return data;
2865
+ } catch {
2866
+ return void 0;
2867
+ }
2868
+ }
2869
+ saveBuildManifest(sitePath, manifest) {
2870
+ const dir = path5.join(sitePath, ".cache", "build");
2871
+ fs3.mkdirSync(dir, { recursive: true });
2872
+ fs3.writeFileSync(path5.join(dir, "manifest.json"), JSON.stringify(manifest));
2873
+ }
2874
+ hashOptions() {
2875
+ const relevant = {
2876
+ siteUrl: this.options.siteUrl,
2877
+ siteTitle: this.options.siteTitle,
2878
+ siteDescription: this.options.siteDescription,
2879
+ githubPath: this.options.githubPath,
2880
+ template: this.options.template,
2881
+ templatePath: this.options.templatePath,
2882
+ enableLlmsTxt: this.options.enableLlmsTxt,
2883
+ changelogPerPage: this.options.changelogPerPage,
2884
+ enableReleaseChangelog: this.options.enableReleaseChangelog,
2885
+ sections: this.options.sections,
2886
+ openApiUrl: this.options.openApiUrl,
2887
+ themeMode: this.options.themeMode,
2888
+ cookieAuth: this.options.cookieAuth,
2889
+ headerLinks: this.options.headerLinks,
2890
+ baseUrl: this.options.baseUrl,
2891
+ docsPath: this.options.docsPath,
2892
+ apiPath: this.options.apiPath,
2893
+ changelogPath: this.options.changelogPath
2894
+ };
2895
+ return this._hash.toHashSync(JSON.stringify(relevant));
2896
+ }
2897
+ hashTemplateDirectory(templatePath) {
2898
+ if (!fs3.existsSync(templatePath)) {
2899
+ return "";
2900
+ }
2901
+ const files = this.listFilesRecursive(templatePath);
2902
+ const hashes = files.map((f) => this.hashFile(path5.join(templatePath, f)));
2903
+ return this._hash.toHashSync(hashes.join(""));
2904
+ }
2905
+ loadCachedDocuments(sitePath) {
2906
+ const cachePath = path5.join(sitePath, ".cache", "build", "documents.json");
2907
+ if (!fs3.existsSync(cachePath)) {
2908
+ return /* @__PURE__ */ new Map();
2909
+ }
2910
+ try {
2911
+ const data = JSON.parse(fs3.readFileSync(cachePath, "utf8"));
2912
+ return new Map(Object.entries(data));
2913
+ } catch {
2914
+ return /* @__PURE__ */ new Map();
2915
+ }
2916
+ }
2917
+ saveCachedDocuments(sitePath, documents) {
2918
+ const dir = path5.join(sitePath, ".cache", "build");
2919
+ fs3.mkdirSync(dir, { recursive: true });
2920
+ const docsRoot = path5.join(sitePath, "docs");
2921
+ const map = {};
2922
+ for (const doc of documents) {
2923
+ const relativeKey = path5.relative(docsRoot, doc.documentPath);
2924
+ map[relativeKey] = doc;
2925
+ }
2926
+ fs3.writeFileSync(path5.join(dir, "documents.json"), JSON.stringify(map));
2927
+ }
2928
+ loadCachedChangelog(sitePath) {
2929
+ const cachePath = path5.join(sitePath, ".cache", "build", "changelog.json");
2930
+ if (!fs3.existsSync(cachePath)) {
2931
+ return /* @__PURE__ */ new Map();
2932
+ }
2933
+ try {
2934
+ const data = JSON.parse(fs3.readFileSync(cachePath, "utf8"));
2935
+ return new Map(Object.entries(data));
2936
+ } catch {
2937
+ return /* @__PURE__ */ new Map();
2938
+ }
2939
+ }
2940
+ saveCachedChangelog(sitePath, entries) {
2941
+ const dir = path5.join(sitePath, ".cache", "build");
2942
+ fs3.mkdirSync(dir, { recursive: true });
2943
+ const map = {};
2944
+ for (const entry of entries) {
2945
+ map[entry.slug] = entry;
2946
+ }
2947
+ fs3.writeFileSync(path5.join(dir, "changelog.json"), JSON.stringify(map));
2948
+ }
2949
+ hashSourceFiles(dir) {
2950
+ const hashes = {};
2951
+ if (!fs3.existsSync(dir)) {
2952
+ return hashes;
2953
+ }
2954
+ const files = this.listFilesRecursive(dir);
2955
+ for (const file of files) {
2956
+ const fullPath = path5.join(dir, file);
2957
+ hashes[file] = this.hashFile(fullPath);
2958
+ }
2959
+ return hashes;
2960
+ }
2961
+ recordsEqual(a, b) {
2962
+ const keysA = Object.keys(a);
2963
+ const keysB = Object.keys(b);
2964
+ if (keysA.length !== keysB.length) {
2965
+ return false;
2966
+ }
2967
+ for (const key of keysA) {
2968
+ if (a[key] !== b[key]) {
2969
+ return false;
2970
+ }
2971
+ }
2972
+ return true;
2973
+ }
2974
+ hasAssetsChanged(sitePath, previousAssets) {
2975
+ const assetFiles = [
2976
+ "favicon.ico",
2977
+ "logo.svg",
2978
+ "logo_horizontal.png",
2979
+ "variables.css",
2980
+ "api/swagger.json",
2981
+ "README.md"
2982
+ ];
2983
+ for (const file of assetFiles) {
2984
+ const filePath = path5.join(sitePath, file);
2985
+ if (fs3.existsSync(filePath)) {
2986
+ const hash = this.hashFile(filePath);
2987
+ if (previousAssets[file] !== hash) {
2988
+ return true;
2989
+ }
2990
+ } else if (previousAssets[file]) {
2991
+ return true;
2992
+ }
2993
+ }
2994
+ const publicPath = path5.join(sitePath, "public");
2995
+ if (fs3.existsSync(publicPath)) {
2996
+ const publicHashes = this.hashSourceFiles(publicPath);
2997
+ for (const [file, hash] of Object.entries(publicHashes)) {
2998
+ if (previousAssets[`public/${file}`] !== hash) {
2999
+ return true;
3000
+ }
3001
+ }
3002
+ }
3003
+ return false;
3004
+ }
3005
+ /**
3006
+ * Hashes the source file, records it in currentAssets, and returns
3007
+ * whether the copy can be skipped (unchanged from previous build).
3008
+ */
3009
+ hashAssetAndCheckSkip(sourcePath, targetPath, assetKey, previousAssets, currentAssets) {
3010
+ if (!fs3.existsSync(sourcePath)) {
3011
+ return true;
3012
+ }
3013
+ const hash = this.hashFile(sourcePath);
3014
+ currentAssets[assetKey] = hash;
3015
+ if (previousAssets[assetKey] === hash && fs3.existsSync(targetPath)) {
3016
+ return true;
3017
+ }
3018
+ return false;
3019
+ }
3020
+ copyDirectoryWithHashing(source, target, prefix, previousAssets, currentAssets) {
3021
+ if (!fs3.existsSync(source)) {
3022
+ return;
3023
+ }
3024
+ const files = fs3.readdirSync(source);
3025
+ for (const file of files) {
3026
+ if (file.startsWith(".")) {
3027
+ continue;
3028
+ }
3029
+ const sourcePath = `${source}/${file}`;
3030
+ const targetPath = `${target}/${file}`;
3031
+ const assetKey = prefix ? `${prefix}/${file}` : file;
3032
+ const stat = fs3.lstatSync(sourcePath);
3033
+ if (stat.isSymbolicLink()) {
3034
+ continue;
3035
+ }
3036
+ if (stat.isDirectory()) {
3037
+ this.copyDirectoryWithHashing(
3038
+ sourcePath,
3039
+ targetPath,
3040
+ assetKey,
3041
+ previousAssets,
3042
+ currentAssets
3043
+ );
3044
+ } else {
3045
+ const hash = this.hashFile(sourcePath);
3046
+ currentAssets[assetKey] = hash;
3047
+ if (previousAssets[assetKey] === hash && fs3.existsSync(targetPath)) {
3048
+ continue;
3049
+ }
3050
+ fs3.mkdirSync(target, { recursive: true });
3051
+ fs3.copyFileSync(sourcePath, targetPath);
3052
+ }
3053
+ }
3054
+ }
2400
3055
  copyContentAssets(sourcePath, targetPath) {
2401
3056
  if (!fs3.existsSync(sourcePath)) {
2402
3057
  return;
@@ -2426,8 +3081,8 @@ ${entry}
2426
3081
  };
2427
3082
 
2428
3083
  // src/init.ts
2429
- var doculaconfigmjs = "ZXhwb3J0IGNvbnN0IG9wdGlvbnMgPSB7CglvdXRwdXQ6ICcuL2Rpc3QnLAoJc2l0ZVBhdGg6ICcuL3NpdGUnLAoJZ2l0aHViUGF0aDogJ2phcmVkd3JheS9kb2N1bGEnLAoJc2l0ZVRpdGxlOiAnRG9jdWxhJywKCXNpdGVEZXNjcmlwdGlvbjogJ0JlYXV0aWZ1bCBXZWJzaXRlIGZvciBZb3VyIFByb2plY3RzJywKCXNpdGVVcmw6ICdodHRwczovL2RvY3VsYS5vcmcnLAoJLy8gb3BlbkFwaVVybDogJy9hcGkvc3dhZ2dlci5qc29uJywKCS8vIGVuYWJsZVJlbGVhc2VDaGFuZ2Vsb2c6IHRydWUsCgkvLyBlbmFibGVMbG1zVHh0OiB0cnVlLAoJLy8gaG9tZVBhZ2U6IHRydWUsCgkvLyB0aGVtZU1vZGU6ICdsaWdodCcsCn07Cg==";
2430
- var doculaconfigts = "aW1wb3J0IHR5cGUgeyBEb2N1bGFPcHRpb25zIH0gZnJvbSAnZG9jdWxhJzsKCmV4cG9ydCBjb25zdCBvcHRpb25zOiBQYXJ0aWFsPERvY3VsYU9wdGlvbnM+ID0gewoJb3V0cHV0OiAnLi9kaXN0JywKCXNpdGVQYXRoOiAnLi9zaXRlJywKCWdpdGh1YlBhdGg6ICdqYXJlZHdyYXkvZG9jdWxhJywKCXNpdGVUaXRsZTogJ0RvY3VsYScsCglzaXRlRGVzY3JpcHRpb246ICdCZWF1dGlmdWwgV2Vic2l0ZSBmb3IgWW91ciBQcm9qZWN0cycsCglzaXRlVXJsOiAnaHR0cHM6Ly9kb2N1bGEub3JnJywKCS8vIG9wZW5BcGlVcmw6ICcvYXBpL3N3YWdnZXIuanNvbicsCgkvLyBlbmFibGVSZWxlYXNlQ2hhbmdlbG9nOiB0cnVlLAoJLy8gZW5hYmxlTGxtc1R4dDogdHJ1ZSwKCS8vIGhvbWVQYWdlOiB0cnVlLAoJLy8gdGhlbWVNb2RlOiAnbGlnaHQnLAp9Owo=";
3084
+ var doculaconfigmjs = "ZXhwb3J0IGNvbnN0IG9wdGlvbnMgPSB7CglvdXRwdXQ6ICcuL2Rpc3QnLAoJc2l0ZVBhdGg6ICcuL3NpdGUnLAoJZ2l0aHViUGF0aDogJ2phcmVkd3JheS9kb2N1bGEnLAoJc2l0ZVRpdGxlOiAnRG9jdWxhJywKCXNpdGVEZXNjcmlwdGlvbjogJ0JlYXV0aWZ1bCBXZWJzaXRlIGZvciBZb3VyIFByb2plY3RzJywKCXNpdGVVcmw6ICdodHRwczovL2RvY3VsYS5vcmcnLAoJLy8gb3BlbkFwaVVybDogJy9hcGkvc3dhZ2dlci5qc29uJywKCS8vIGVuYWJsZVJlbGVhc2VDaGFuZ2Vsb2c6IHRydWUsCgkvLyBlbmFibGVMbG1zVHh0OiB0cnVlLAoJLy8gdGhlbWVNb2RlOiAnbGlnaHQnLAp9Owo=";
3085
+ var doculaconfigts = "aW1wb3J0IHR5cGUgeyBEb2N1bGFPcHRpb25zIH0gZnJvbSAnZG9jdWxhJzsKCmV4cG9ydCBjb25zdCBvcHRpb25zOiBQYXJ0aWFsPERvY3VsYU9wdGlvbnM+ID0gewoJb3V0cHV0OiAnLi9kaXN0JywKCXNpdGVQYXRoOiAnLi9zaXRlJywKCWdpdGh1YlBhdGg6ICdqYXJlZHdyYXkvZG9jdWxhJywKCXNpdGVUaXRsZTogJ0RvY3VsYScsCglzaXRlRGVzY3JpcHRpb246ICdCZWF1dGlmdWwgV2Vic2l0ZSBmb3IgWW91ciBQcm9qZWN0cycsCglzaXRlVXJsOiAnaHR0cHM6Ly9kb2N1bGEub3JnJywKCS8vIG9wZW5BcGlVcmw6ICcvYXBpL3N3YWdnZXIuanNvbicsCgkvLyBlbmFibGVSZWxlYXNlQ2hhbmdlbG9nOiB0cnVlLAoJLy8gZW5hYmxlTGxtc1R4dDogdHJ1ZSwKCS8vIHRoZW1lTW9kZTogJ2xpZ2h0JywKfTsK";
2431
3086
  var faviconico = "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";
2432
3087
  var logopng = "iVBORw0KGgoAAAANSUhEUgAAAxEAAAHXCAYAAADDSdfmAAAACXBIWXMAACxLAAAsSwGlPZapAAAFw2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4xLWMwMDMgNzkuOTY5MGE4N2ZjLCAyMDI1LzAzLzA2LTIwOjUwOjE2ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjYuNiAoTWFjaW50b3NoKSIgeG1wOkNyZWF0ZURhdGU9IjIwMjUtMDUtMThUMTM6MzM6NTgtMDc6MDAiIHhtcDpNb2RpZnlEYXRlPSIyMDI1LTA1LTE4VDEzOjM2OjE5LTA3OjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDI1LTA1LTE4VDEzOjM2OjE5LTA3OjAwIiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo5YTc4ZDY2Ny02NjAzLTQwNmUtYTViZC02MjYzMTNjY2VmY2QiIHhtcE1NOkRvY3VtZW50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";
2433
3088
 
@@ -2534,7 +3189,7 @@ var Docula = class {
2534
3189
  }
2535
3190
  if (this._configFileModule.onPrepare) {
2536
3191
  try {
2537
- await this._configFileModule.onPrepare(this.options);
3192
+ await this._configFileModule.onPrepare(this.options, this._console);
2538
3193
  } catch (error) {
2539
3194
  this._console.error(error.message);
2540
3195
  }
@@ -2542,18 +3197,37 @@ var Docula = class {
2542
3197
  if (consoleProcess.args.template) {
2543
3198
  this.options.template = consoleProcess.args.template;
2544
3199
  }
3200
+ if (consoleProcess.args.templatePath) {
3201
+ this.options.templatePath = consoleProcess.args.templatePath;
3202
+ }
2545
3203
  if (consoleProcess.args.output) {
2546
3204
  this.options.output = consoleProcess.args.output;
3205
+ } else {
3206
+ const configOptions = this._configFileModule.options;
3207
+ if (!configOptions?.output) {
3208
+ this.options.output = path6.join(this.options.sitePath, "dist");
3209
+ }
2547
3210
  }
2548
3211
  if (consoleProcess.args.port !== void 0 && !Number.isNaN(consoleProcess.args.port)) {
2549
3212
  this.options.port = consoleProcess.args.port;
2550
3213
  }
2551
3214
  switch (consoleProcess.command) {
2552
3215
  case "init": {
2553
- this.generateInit(
2554
- this.options.sitePath,
2555
- consoleProcess.args.typescript
2556
- );
3216
+ if (consoleProcess.args.typescript && consoleProcess.args.javascript) {
3217
+ this._console.error(
3218
+ "Cannot use both --typescript and --javascript flags. Please choose one."
3219
+ );
3220
+ break;
3221
+ }
3222
+ let useTypeScript;
3223
+ if (consoleProcess.args.typescript) {
3224
+ useTypeScript = true;
3225
+ } else if (consoleProcess.args.javascript) {
3226
+ useTypeScript = false;
3227
+ } else {
3228
+ useTypeScript = this.detectTypeScript();
3229
+ }
3230
+ this.generateInit(this.options.sitePath, useTypeScript);
2557
3231
  break;
2558
3232
  }
2559
3233
  case "help": {
@@ -2564,16 +3238,23 @@ var Docula = class {
2564
3238
  this._console.log(this.getVersion());
2565
3239
  break;
2566
3240
  }
3241
+ case "dev": {
3242
+ const devBuilder = await this.runBuild(consoleProcess.args.clean);
3243
+ this.watch(this.options, devBuilder);
3244
+ await this.serve(this.options);
3245
+ break;
3246
+ }
3247
+ case "start": {
3248
+ const startBuilder = await this.runBuild(consoleProcess.args.clean);
3249
+ if (consoleProcess.args.watch) {
3250
+ this.watch(this.options, startBuilder);
3251
+ }
3252
+ await this.serve(this.options);
3253
+ break;
3254
+ }
2567
3255
  case "serve": {
2568
3256
  if (consoleProcess.args.build || consoleProcess.args.watch) {
2569
- if (consoleProcess.args.clean && fs4.existsSync(this.options.output)) {
2570
- fs4.rmSync(this.options.output, { recursive: true, force: true });
2571
- }
2572
- if (consoleProcess.args.clean) {
2573
- this.cleanCache(this.options.sitePath);
2574
- }
2575
- const builder = new DoculaBuilder(this.options);
2576
- await builder.build();
3257
+ const builder = await this.runBuild(consoleProcess.args.clean);
2577
3258
  if (consoleProcess.args.watch) {
2578
3259
  this.watch(this.options, builder);
2579
3260
  }
@@ -2582,18 +3263,32 @@ var Docula = class {
2582
3263
  break;
2583
3264
  }
2584
3265
  default: {
2585
- if (consoleProcess.args.clean && fs4.existsSync(this.options.output)) {
2586
- fs4.rmSync(this.options.output, { recursive: true, force: true });
2587
- }
2588
- if (consoleProcess.args.clean) {
2589
- this.cleanCache(this.options.sitePath);
2590
- }
2591
- const builder = new DoculaBuilder(this.options);
2592
- await builder.build();
3266
+ await this.runBuild(consoleProcess.args.clean);
2593
3267
  break;
2594
3268
  }
2595
3269
  }
2596
3270
  }
3271
+ async runBuild(clean) {
3272
+ if (clean && fs4.existsSync(this.options.output)) {
3273
+ fs4.rmSync(this.options.output, { recursive: true, force: true });
3274
+ }
3275
+ if (clean) {
3276
+ this.cleanCache(this.options.sitePath);
3277
+ }
3278
+ const builder = new DoculaBuilder(this.options);
3279
+ if (this._configFileModule.onReleaseChangelog) {
3280
+ builder.onReleaseChangelog = this._configFileModule.onReleaseChangelog;
3281
+ }
3282
+ await builder.build();
3283
+ return builder;
3284
+ }
3285
+ /**
3286
+ * Detect if the current project uses TypeScript by checking for tsconfig.json
3287
+ * @returns {boolean}
3288
+ */
3289
+ detectTypeScript() {
3290
+ return fs4.existsSync(path6.join(process4.cwd(), "tsconfig.json"));
3291
+ }
2597
3292
  /**
2598
3293
  * Generate the init files
2599
3294
  * @param {string} sitePath
@@ -2672,7 +3367,10 @@ var Docula = class {
2672
3367
  this._console.info(`File changed: ${filename}, rebuilding...`);
2673
3368
  try {
2674
3369
  await builder.build();
2675
- this._console.success("Rebuild complete");
3370
+ this._console.success(`Rebuild complete`);
3371
+ this._console.banner(
3372
+ `\u{1F987} at http://localhost:${options.port}`
3373
+ );
2676
3374
  } catch (error) {
2677
3375
  this._console.error(`Rebuild failed: ${error.message}`);
2678
3376
  } finally {
@@ -2690,6 +3388,9 @@ var Docula = class {
2690
3388
  if (filename && outputRelative && !outputRelative.startsWith("..") && (String(filename) === outputRelative || String(filename).startsWith(`${outputRelative}${path6.sep}`))) {
2691
3389
  return;
2692
3390
  }
3391
+ if (filename && (String(filename) === ".cache" || String(filename).startsWith(`.cache${path6.sep}`))) {
3392
+ return;
3393
+ }
2693
3394
  if (isBuilding) {
2694
3395
  pendingRebuild = true;
2695
3396
  return;
@@ -2746,6 +3447,7 @@ var Docula = class {
2746
3447
  }
2747
3448
  };
2748
3449
  export {
3450
+ DoculaConsole,
2749
3451
  DoculaOptions,
2750
3452
  Writr2 as Writr,
2751
3453
  Docula as default
@@ -2757,4 +3459,5 @@ export {
2757
3459
  /* v8 ignore next 9 -- @preserve */
2758
3460
  /* v8 ignore next 4 -- @preserve */
2759
3461
  /* v8 ignore start -- @preserve */
3462
+ /* v8 ignore next 8 -- @preserve */
2760
3463
  /* v8 ignore stop -- @preserve */