docula 1.2.0 → 1.5.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:")));
581
- 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:")));
585
+ console.log(bold(cyan(" Common Options:")));
589
586
  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_;
@@ -887,7 +919,7 @@ var DoculaOptions = class {
887
919
  /**
888
920
  * Path to the output directory
889
921
  */
890
- output = path3.join(process3.cwd(), "./dist");
922
+ output = "";
891
923
  /**
892
924
  * Path to the site directory
893
925
  */
@@ -928,10 +960,9 @@ var DoculaOptions = class {
928
960
  */
929
961
  enableReleaseChangelog = true;
930
962
  /**
931
- * When false, the first document becomes the home page (index.html)
932
- * and the home.hbs template is not rendered.
963
+ * Number of changelog entries to display per page on the changelog index.
933
964
  */
934
- homePage = true;
965
+ changelogPerPage = 20;
935
966
  /**
936
967
  * When true, generates llms.txt and llms-full.txt files for the built site.
937
968
  */
@@ -1005,6 +1036,9 @@ var DoculaOptions = class {
1005
1036
  if (options) {
1006
1037
  this.parseOptions(options);
1007
1038
  }
1039
+ if (!this.output) {
1040
+ this.output = path3.join(this.sitePath, "dist");
1041
+ }
1008
1042
  }
1009
1043
  // biome-ignore lint/suspicious/noExplicitAny: need to fix
1010
1044
  parseOptions(options) {
@@ -1047,8 +1081,8 @@ var DoculaOptions = class {
1047
1081
  if (options.enableReleaseChangelog !== void 0 && typeof options.enableReleaseChangelog === "boolean") {
1048
1082
  this.enableReleaseChangelog = options.enableReleaseChangelog;
1049
1083
  }
1050
- if (options.homePage !== void 0 && typeof options.homePage === "boolean") {
1051
- this.homePage = options.homePage;
1084
+ if (options.changelogPerPage !== void 0 && typeof options.changelogPerPage === "number" && options.changelogPerPage > 0) {
1085
+ this.changelogPerPage = options.changelogPerPage;
1052
1086
  }
1053
1087
  if (options.enableLlmsTxt !== void 0 && typeof options.enableLlmsTxt === "boolean") {
1054
1088
  this.enableLlmsTxt = options.enableLlmsTxt;
@@ -1076,6 +1110,9 @@ var DoculaOptions = class {
1076
1110
  this.headerLinks = validLinks;
1077
1111
  }
1078
1112
  }
1113
+ if (!options.output && !this.output) {
1114
+ this.output = path3.join(this.sitePath, "dist");
1115
+ }
1079
1116
  }
1080
1117
  };
1081
1118
 
@@ -1113,6 +1150,8 @@ var DoculaBuilder = class {
1113
1150
  _options = new DoculaOptions();
1114
1151
  _ecto;
1115
1152
  _console = new DoculaConsole();
1153
+ _hash = new Hashery();
1154
+ onReleaseChangelog;
1116
1155
  // biome-ignore lint/suspicious/noExplicitAny: need to fix
1117
1156
  constructor(options, engineOptions) {
1118
1157
  if (options) {
@@ -1131,6 +1170,29 @@ var DoculaBuilder = class {
1131
1170
  this.options.sitePath,
1132
1171
  this.options.template
1133
1172
  );
1173
+ const previousManifest = this.loadBuildManifest(this.options.sitePath);
1174
+ const currentConfigHash = this.hashOptions();
1175
+ const currentTemplateHash = this.hashTemplateDirectory(resolvedTemplatePath);
1176
+ const validManifest = previousManifest?.configHash === currentConfigHash ? previousManifest : void 0;
1177
+ const currentDocHashes = this.hashSourceFiles(
1178
+ `${this.options.sitePath}/docs`
1179
+ );
1180
+ const currentChangelogHashes = this.hashSourceFiles(
1181
+ `${this.options.sitePath}/changelog`
1182
+ );
1183
+ const currentAssetHashes = {};
1184
+ if (validManifest && fs3.existsSync(this.options.output) && validManifest.templateHash === currentTemplateHash && this.recordsEqual(validManifest.docs, currentDocHashes) && this.recordsEqual(validManifest.changelog, currentChangelogHashes)) {
1185
+ const assetsChanged = this.hasAssetsChanged(
1186
+ this.options.sitePath,
1187
+ validManifest.assets
1188
+ );
1189
+ if (!assetsChanged) {
1190
+ this._console.success("No changes detected, skipping build");
1191
+ return;
1192
+ }
1193
+ }
1194
+ const cachedDocs = validManifest ? this.loadCachedDocuments(this.options.sitePath) : /* @__PURE__ */ new Map();
1195
+ const cachedChangelog = validManifest ? this.loadCachedChangelog(this.options.sitePath) : /* @__PURE__ */ new Map();
1134
1196
  const doculaData = {
1135
1197
  siteUrl: this.options.siteUrl,
1136
1198
  siteTitle: this.options.siteTitle,
@@ -1141,12 +1203,16 @@ var DoculaBuilder = class {
1141
1203
  githubPath: this.options.githubPath,
1142
1204
  sections: this.options.sections,
1143
1205
  openApiUrl: this.options.openApiUrl,
1144
- homePage: this.options.homePage,
1206
+ hasReadme: fs3.existsSync(`${this.options.sitePath}/README.md`),
1145
1207
  themeMode: this.options.themeMode,
1146
1208
  cookieAuth: this.options.cookieAuth,
1147
1209
  headerLinks: this.options.headerLinks,
1148
1210
  enableLlmsTxt: this.options.enableLlmsTxt
1149
1211
  };
1212
+ const readmePath = `${this.options.sitePath}/README.md`;
1213
+ if (doculaData.hasReadme) {
1214
+ currentAssetHashes["README.md"] = this.hashFile(readmePath);
1215
+ }
1150
1216
  if (!doculaData.openApiUrl && fs3.existsSync(`${doculaData.sitePath}/api/swagger.json`)) {
1151
1217
  doculaData.openApiUrl = "/api/swagger.json";
1152
1218
  }
@@ -1155,7 +1221,10 @@ var DoculaBuilder = class {
1155
1221
  }
1156
1222
  doculaData.documents = this.getDocuments(
1157
1223
  `${doculaData.sitePath}/docs`,
1158
- doculaData
1224
+ doculaData,
1225
+ cachedDocs,
1226
+ validManifest?.docs ?? {},
1227
+ currentDocHashes
1159
1228
  );
1160
1229
  doculaData.sections = this.getSections(
1161
1230
  `${doculaData.sitePath}/docs`,
@@ -1164,14 +1233,31 @@ var DoculaBuilder = class {
1164
1233
  doculaData.hasDocuments = doculaData.documents?.length > 0;
1165
1234
  doculaData.hasFeed = doculaData.hasDocuments;
1166
1235
  const changelogPath = `${doculaData.sitePath}/changelog`;
1167
- const fileChangelogEntries = this.getChangelogEntries(changelogPath);
1236
+ const fileChangelogEntries = this.getChangelogEntries(
1237
+ changelogPath,
1238
+ cachedChangelog,
1239
+ validManifest?.changelog ?? {},
1240
+ currentChangelogHashes
1241
+ );
1168
1242
  const hasChangelogTemplate = await this.getTemplateFile(resolvedTemplatePath, "changelog") !== void 0;
1169
1243
  let allChangelogEntries = [...fileChangelogEntries];
1170
1244
  if (this._options.enableReleaseChangelog && hasChangelogTemplate && doculaData.github?.releases && Array.isArray(doculaData.github.releases) && doculaData.github.releases.length > 0) {
1171
- const releaseEntries = this.getReleasesAsChangelogEntries(
1245
+ let releaseEntries = this.getReleasesAsChangelogEntries(
1172
1246
  // biome-ignore lint/suspicious/noExplicitAny: GitHub release objects
1173
1247
  doculaData.github.releases
1174
1248
  );
1249
+ if (this.onReleaseChangelog) {
1250
+ try {
1251
+ releaseEntries = await this.onReleaseChangelog(
1252
+ releaseEntries,
1253
+ this._console
1254
+ );
1255
+ } catch (error) {
1256
+ this._console.error(
1257
+ `onReleaseChangelog error: ${error.message}`
1258
+ );
1259
+ }
1260
+ }
1175
1261
  allChangelogEntries = [...allChangelogEntries, ...releaseEntries];
1176
1262
  }
1177
1263
  allChangelogEntries.sort((a, b) => {
@@ -1198,17 +1284,21 @@ var DoculaBuilder = class {
1198
1284
  doculaData.hasApi = Boolean(
1199
1285
  doculaData.openApiUrl && doculaData.templates?.api
1200
1286
  );
1287
+ doculaData.lastModified = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1201
1288
  this._console.step("Building pages...");
1202
- if (!this.options.homePage && doculaData.hasDocuments) {
1289
+ if (doculaData.hasReadme) {
1290
+ await this.buildIndexPage(doculaData);
1291
+ this._console.fileBuilt("index.html");
1292
+ } else if (doculaData.hasDocuments) {
1203
1293
  await this.buildDocsHomePage(doculaData);
1204
1294
  this._console.fileBuilt("index.html");
1205
- } else if (!this.options.homePage && !doculaData.hasDocuments) {
1295
+ } else if (doculaData.hasApi) {
1296
+ await this.buildApiHomePage(doculaData);
1297
+ this._console.fileBuilt("index.html");
1298
+ } else {
1206
1299
  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."
1300
+ "No content found for the home page. Add a README.md, docs/ folder, or api/swagger.json to your site directory."
1208
1301
  );
1209
- } else {
1210
- await this.buildIndexPage(doculaData);
1211
- this._console.fileBuilt("index.html");
1212
1302
  }
1213
1303
  await this.buildSiteMapPage(doculaData);
1214
1304
  this._console.fileBuilt("sitemap.xml");
@@ -1240,22 +1330,41 @@ var DoculaBuilder = class {
1240
1330
  }
1241
1331
  }
1242
1332
  const siteRelativePath = this.options.sitePath;
1333
+ const previousAssets = validManifest?.assets ?? {};
1243
1334
  this._console.step("Copying assets...");
1244
- if (fs3.existsSync(`${siteRelativePath}/favicon.ico`)) {
1335
+ if (!this.hashAssetAndCheckSkip(
1336
+ `${siteRelativePath}/favicon.ico`,
1337
+ `${this.options.output}/favicon.ico`,
1338
+ "favicon.ico",
1339
+ previousAssets,
1340
+ currentAssetHashes
1341
+ )) {
1245
1342
  await fs3.promises.copyFile(
1246
1343
  `${siteRelativePath}/favicon.ico`,
1247
1344
  `${this.options.output}/favicon.ico`
1248
1345
  );
1249
1346
  this._console.fileCopied("favicon.ico");
1250
1347
  }
1251
- if (fs3.existsSync(`${siteRelativePath}/logo.svg`)) {
1348
+ if (!this.hashAssetAndCheckSkip(
1349
+ `${siteRelativePath}/logo.svg`,
1350
+ `${this.options.output}/logo.svg`,
1351
+ "logo.svg",
1352
+ previousAssets,
1353
+ currentAssetHashes
1354
+ )) {
1252
1355
  await fs3.promises.copyFile(
1253
1356
  `${siteRelativePath}/logo.svg`,
1254
1357
  `${this.options.output}/logo.svg`
1255
1358
  );
1256
1359
  this._console.fileCopied("logo.svg");
1257
1360
  }
1258
- if (fs3.existsSync(`${siteRelativePath}/logo_horizontal.png`)) {
1361
+ if (!this.hashAssetAndCheckSkip(
1362
+ `${siteRelativePath}/logo_horizontal.png`,
1363
+ `${this.options.output}/logo_horizontal.png`,
1364
+ "logo_horizontal.png",
1365
+ previousAssets,
1366
+ currentAssetHashes
1367
+ )) {
1259
1368
  await fs3.promises.copyFile(
1260
1369
  `${siteRelativePath}/logo_horizontal.png`,
1261
1370
  `${this.options.output}/logo_horizontal.png`
@@ -1263,27 +1372,48 @@ var DoculaBuilder = class {
1263
1372
  this._console.fileCopied("logo_horizontal.png");
1264
1373
  }
1265
1374
  if (fs3.existsSync(`${resolvedTemplatePath}/css`)) {
1266
- this.copyDirectory(
1375
+ this.copyDirectoryWithHashing(
1267
1376
  `${resolvedTemplatePath}/css`,
1268
- `${this.options.output}/css`
1377
+ `${this.options.output}/css`,
1378
+ "css",
1379
+ previousAssets,
1380
+ currentAssetHashes
1269
1381
  );
1270
1382
  this._console.fileCopied("css/");
1271
1383
  }
1272
1384
  if (fs3.existsSync(`${resolvedTemplatePath}/js`)) {
1273
- this.copyDirectory(
1385
+ this.copyDirectoryWithHashing(
1274
1386
  `${resolvedTemplatePath}/js`,
1275
- `${this.options.output}/js`
1387
+ `${this.options.output}/js`,
1388
+ "js",
1389
+ previousAssets,
1390
+ currentAssetHashes
1276
1391
  );
1277
1392
  this._console.fileCopied("js/");
1278
1393
  }
1279
- if (fs3.existsSync(`${siteRelativePath}/variables.css`)) {
1394
+ if (!this.hashAssetAndCheckSkip(
1395
+ `${siteRelativePath}/variables.css`,
1396
+ `${this.options.output}/css/variables.css`,
1397
+ "variables.css",
1398
+ previousAssets,
1399
+ currentAssetHashes
1400
+ )) {
1280
1401
  await fs3.promises.copyFile(
1281
1402
  `${siteRelativePath}/variables.css`,
1282
1403
  `${this.options.output}/css/variables.css`
1283
1404
  );
1284
1405
  this._console.fileCopied("css/variables.css");
1285
1406
  }
1286
- this.copyPublicFolder(siteRelativePath, this.options.output);
1407
+ const swaggerPath = `${siteRelativePath}/api/swagger.json`;
1408
+ if (fs3.existsSync(swaggerPath)) {
1409
+ currentAssetHashes["api/swagger.json"] = this.hashFile(swaggerPath);
1410
+ }
1411
+ this.copyPublicFolder(
1412
+ siteRelativePath,
1413
+ this.options.output,
1414
+ validManifest?.assets ?? {},
1415
+ currentAssetHashes
1416
+ );
1287
1417
  this.copyContentAssets(
1288
1418
  `${doculaData.sitePath}/changelog`,
1289
1419
  `${this.options.output}/changelog`
@@ -1295,6 +1425,18 @@ var DoculaBuilder = class {
1295
1425
  this._console.step("Building LLM files...");
1296
1426
  }
1297
1427
  await this.buildLlmsFiles(doculaData);
1428
+ this.ensureCacheInGitignore(this.options.sitePath);
1429
+ const newManifest = {
1430
+ version: 1,
1431
+ configHash: currentConfigHash,
1432
+ templateHash: currentTemplateHash,
1433
+ docs: currentDocHashes,
1434
+ changelog: currentChangelogHashes,
1435
+ assets: currentAssetHashes
1436
+ };
1437
+ this.saveBuildManifest(this.options.sitePath, newManifest);
1438
+ this.saveCachedDocuments(this.options.sitePath, doculaData.documents ?? []);
1439
+ this.saveCachedChangelog(this.options.sitePath, fileChangelogEntries);
1298
1440
  const endTime = Date.now();
1299
1441
  const executionTime = endTime - startTime;
1300
1442
  this._console.success(`Build completed in ${executionTime}ms`);
@@ -1389,6 +1531,16 @@ var DoculaBuilder = class {
1389
1531
  }
1390
1532
  if (data.hasChangelog && data.templates?.changelog) {
1391
1533
  urls.push({ url: `${data.siteUrl}/changelog` });
1534
+ const perPage = this.options.changelogPerPage;
1535
+ const totalPages = Math.max(
1536
+ 1,
1537
+ Math.ceil((data.changelogEntries ?? []).length / perPage)
1538
+ );
1539
+ for (let page = 2; page <= totalPages; page++) {
1540
+ urls.push({
1541
+ url: `${data.siteUrl}/changelog/page/${page}`
1542
+ });
1543
+ }
1392
1544
  for (const entry of data.changelogEntries ?? []) {
1393
1545
  urls.push({
1394
1546
  url: `${data.siteUrl}/changelog/${entry.slug}`
@@ -1756,10 +1908,10 @@ var DoculaBuilder = class {
1756
1908
  }
1757
1909
  async buildDocsHomePage(data) {
1758
1910
  if (!data.templates?.docPage) {
1759
- throw new Error("No docPage template found for homePage");
1911
+ throw new Error("No docPage template found for docs home page");
1760
1912
  }
1761
1913
  if (!data.documents?.length) {
1762
- throw new Error("No documents found for homePage");
1914
+ throw new Error("No documents found for docs home page");
1763
1915
  }
1764
1916
  const indexPath = `${data.output}/index.html`;
1765
1917
  await fs3.promises.mkdir(data.output, { recursive: true });
@@ -1817,14 +1969,13 @@ var DoculaBuilder = class {
1817
1969
  throw new Error("No templates found");
1818
1970
  }
1819
1971
  }
1820
- async buildApiPage(data) {
1972
+ async renderApiContent(data) {
1821
1973
  if (!data.openApiUrl || !data.templates?.api) {
1822
- return;
1974
+ throw new Error("No API template or openApiUrl found");
1823
1975
  }
1824
- const apiPath = `${data.output}/api/index.html`;
1976
+ const swaggerSource = `${data.sitePath}/api/swagger.json`;
1825
1977
  const apiOutputPath = `${data.output}/api`;
1826
1978
  await fs3.promises.mkdir(apiOutputPath, { recursive: true });
1827
- const swaggerSource = `${data.sitePath}/api/swagger.json`;
1828
1979
  if (fs3.existsSync(swaggerSource)) {
1829
1980
  await fs3.promises.copyFile(
1830
1981
  swaggerSource,
@@ -1855,14 +2006,27 @@ var DoculaBuilder = class {
1855
2006
  }
1856
2007
  }
1857
2008
  const apiTemplate = `${data.templatePath}/${data.templates.api}`;
1858
- const apiContent = await this._ecto.renderFromFile(
2009
+ return this._ecto.renderFromFile(
1859
2010
  apiTemplate,
1860
2011
  { ...data, specUrl: data.openApiUrl, apiSpec },
1861
2012
  data.templatePath
1862
2013
  );
2014
+ }
2015
+ async buildApiPage(data) {
2016
+ if (!data.openApiUrl || !data.templates?.api) {
2017
+ return;
2018
+ }
2019
+ const apiPath = `${data.output}/api/index.html`;
2020
+ const apiContent = await this.renderApiContent(data);
1863
2021
  await fs3.promises.writeFile(apiPath, apiContent, "utf8");
1864
2022
  }
1865
- getChangelogEntries(changelogPath) {
2023
+ async buildApiHomePage(data) {
2024
+ const indexPath = `${data.output}/index.html`;
2025
+ await fs3.promises.mkdir(data.output, { recursive: true });
2026
+ const apiContent = await this.renderApiContent(data);
2027
+ await fs3.promises.writeFile(indexPath, apiContent, "utf8");
2028
+ }
2029
+ getChangelogEntries(changelogPath, cachedEntries, previousHashes, currentHashes) {
1866
2030
  const entries = [];
1867
2031
  if (!fs3.existsSync(changelogPath)) {
1868
2032
  return entries;
@@ -1872,6 +2036,16 @@ var DoculaBuilder = class {
1872
2036
  const filePath = `${changelogPath}/${file}`;
1873
2037
  const stats = fs3.statSync(filePath);
1874
2038
  if (stats.isFile() && (file.endsWith(".md") || file.endsWith(".mdx"))) {
2039
+ if (cachedEntries && previousHashes && currentHashes) {
2040
+ const slug = path5.basename(file, path5.extname(file));
2041
+ const hash = currentHashes[file] ?? this.hashFile(filePath);
2042
+ const prevHash = previousHashes[file];
2043
+ const cached = cachedEntries.get(slug);
2044
+ if (cached && prevHash === hash) {
2045
+ entries.push(cached);
2046
+ continue;
2047
+ }
2048
+ }
1875
2049
  const entry = this.parseChangelogEntry(filePath);
1876
2050
  entries.push(entry);
1877
2051
  }
@@ -1898,7 +2072,7 @@ var DoculaBuilder = class {
1898
2072
  const matterData = writr.frontMatter;
1899
2073
  const markdownContent = writr.body;
1900
2074
  const fileName = path5.basename(filePath, path5.extname(filePath));
1901
- const slug = fileName.replace(/^\d{4}-\d{2}-\d{2}-/, "");
2075
+ const slug = fileName;
1902
2076
  const isMdx = filePath.endsWith(".mdx");
1903
2077
  const tag = matterData.tag;
1904
2078
  const tagClass = tag ? tag.toLowerCase().replace(/\s+/g, "-") : void 0;
@@ -1917,6 +2091,7 @@ var DoculaBuilder = class {
1917
2091
  day: "numeric"
1918
2092
  });
1919
2093
  }
2094
+ const previewImage = matterData.previewImage;
1920
2095
  return {
1921
2096
  title: matterData.title ?? fileName,
1922
2097
  date: dateString,
@@ -1926,9 +2101,73 @@ var DoculaBuilder = class {
1926
2101
  slug,
1927
2102
  content: markdownContent,
1928
2103
  generatedHtml: new Writr(markdownContent).renderSync({ mdx: isMdx }),
1929
- urlPath: `/changelog/${slug}/index.html`
2104
+ preview: this.generateChangelogPreview(markdownContent, 500, isMdx),
2105
+ previewImage,
2106
+ urlPath: `/changelog/${slug}/index.html`,
2107
+ lastModified: fs3.statSync(filePath).mtime.toISOString().split("T")[0]
1930
2108
  };
1931
2109
  }
2110
+ generateChangelogPreview(markdown, maxLength = 500, mdx = false) {
2111
+ const minLength = 300;
2112
+ let cleaned = markdown.split("\n").filter((line) => !/^#{1,6}\s/.test(line)).join("\n");
2113
+ cleaned = cleaned.replace(/^\n+/, "");
2114
+ cleaned = cleaned.replace(/!\[[^\]]*\]\([^)]*\)/g, "");
2115
+ cleaned = cleaned.replace(/\[([^\]]*)\]\([^)]*\)/g, "$1");
2116
+ cleaned = cleaned.replace(/^\n+/, "").trim();
2117
+ if (cleaned.length <= minLength) {
2118
+ return new Writr(cleaned).renderSync({ mdx });
2119
+ }
2120
+ const searchArea = cleaned.slice(0, maxLength);
2121
+ let splitIndex = -1;
2122
+ let pos = searchArea.lastIndexOf("\n\n");
2123
+ while (pos >= 0) {
2124
+ if (pos >= minLength) {
2125
+ splitIndex = pos;
2126
+ break;
2127
+ }
2128
+ if (splitIndex === -1) {
2129
+ splitIndex = pos;
2130
+ }
2131
+ pos = searchArea.lastIndexOf("\n\n", pos - 1);
2132
+ }
2133
+ if (splitIndex === -1) {
2134
+ const lastNewline = searchArea.lastIndexOf("\n");
2135
+ if (lastNewline >= minLength) {
2136
+ const nextLine = cleaned.slice(lastNewline + 1);
2137
+ if (/^[-*]\s/.test(nextLine) || /^\d+\.\s/.test(nextLine)) {
2138
+ splitIndex = lastNewline;
2139
+ }
2140
+ }
2141
+ if (splitIndex === -1) {
2142
+ const lines = searchArea.split("\n");
2143
+ let charCount = 0;
2144
+ let lastItemEnd = -1;
2145
+ for (const line of lines) {
2146
+ const lineEnd = charCount + line.length;
2147
+ if (lineEnd <= maxLength && (/^[-*]\s/.test(line) || /^\d+\.\s/.test(line))) {
2148
+ if (charCount > 0 && charCount >= minLength) {
2149
+ lastItemEnd = charCount - 1;
2150
+ }
2151
+ }
2152
+ charCount = lineEnd + 1;
2153
+ }
2154
+ if (lastItemEnd > 0) {
2155
+ splitIndex = lastItemEnd;
2156
+ }
2157
+ }
2158
+ }
2159
+ if (splitIndex > 0) {
2160
+ const truncated2 = cleaned.slice(0, splitIndex).trim();
2161
+ return new Writr(truncated2).renderSync({ mdx });
2162
+ }
2163
+ let truncated = cleaned.slice(0, maxLength);
2164
+ const lastSpace = truncated.lastIndexOf(" ");
2165
+ if (lastSpace > 0) {
2166
+ truncated = truncated.slice(0, lastSpace);
2167
+ }
2168
+ truncated += "...";
2169
+ return new Writr(truncated).renderSync({ mdx });
2170
+ }
1932
2171
  convertReleaseToChangelogEntry(release) {
1933
2172
  const tagName = release.tag_name ?? "";
1934
2173
  const slug = tagName.replace(/\./g, "-");
@@ -1960,7 +2199,9 @@ var DoculaBuilder = class {
1960
2199
  slug,
1961
2200
  content: body,
1962
2201
  generatedHtml: new Writr(body).renderSync(),
1963
- urlPath: `/changelog/${slug}/index.html`
2202
+ preview: this.generateChangelogPreview(body),
2203
+ urlPath: `/changelog/${slug}/index.html`,
2204
+ lastModified: dateString
1964
2205
  };
1965
2206
  }
1966
2207
  getReleasesAsChangelogEntries(releases) {
@@ -1977,16 +2218,40 @@ var DoculaBuilder = class {
1977
2218
  if (!data.hasChangelog || !data.templates?.changelog) {
1978
2219
  return;
1979
2220
  }
1980
- const changelogOutputPath = `${data.output}/changelog`;
1981
- const changelogIndexPath = `${changelogOutputPath}/index.html`;
1982
- await fs3.promises.mkdir(changelogOutputPath, { recursive: true });
2221
+ const allEntries = data.changelogEntries ?? [];
2222
+ const perPage = this.options.changelogPerPage;
2223
+ const totalPages = Math.max(1, Math.ceil(allEntries.length / perPage));
1983
2224
  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");
2225
+ const promises = [];
2226
+ for (let page = 1; page <= totalPages; page++) {
2227
+ const startIndex = (page - 1) * perPage;
2228
+ const pageEntries = allEntries.slice(startIndex, startIndex + perPage);
2229
+ const outputPath = page === 1 ? `${data.output}/changelog` : `${data.output}/changelog/page/${page}`;
2230
+ const indexPath = `${outputPath}/index.html`;
2231
+ const paginationData = {
2232
+ ...data,
2233
+ entries: pageEntries,
2234
+ currentPage: page,
2235
+ totalPages,
2236
+ hasPagination: totalPages > 1,
2237
+ hasNextPage: page < totalPages,
2238
+ hasPrevPage: page > 1,
2239
+ nextPageUrl: page < totalPages ? `/changelog/page/${page + 1}/` : "",
2240
+ prevPageUrl: page > 1 ? page === 2 ? "/changelog/" : `/changelog/page/${page - 1}/` : ""
2241
+ };
2242
+ promises.push(
2243
+ (async () => {
2244
+ await fs3.promises.mkdir(outputPath, { recursive: true });
2245
+ const content = await this._ecto.renderFromFile(
2246
+ changelogTemplate,
2247
+ paginationData,
2248
+ data.templatePath
2249
+ );
2250
+ await fs3.promises.writeFile(indexPath, content, "utf8");
2251
+ })()
2252
+ );
2253
+ }
2254
+ await Promise.all(promises);
1990
2255
  }
1991
2256
  async buildChangelogEntryPages(data) {
1992
2257
  if (!data.hasChangelog || !data.templates?.changelogEntry || !data.changelogEntries?.length) {
@@ -2007,7 +2272,12 @@ var DoculaBuilder = class {
2007
2272
  await Promise.all(promises);
2008
2273
  }
2009
2274
  generateSidebarItems(data) {
2010
- let sidebarItems = [...data.sections ?? []];
2275
+ let sidebarItems = (data.sections ?? []).map(
2276
+ (section) => ({
2277
+ ...section,
2278
+ children: section.children ? [...section.children] : void 0
2279
+ })
2280
+ );
2011
2281
  for (const document of data.documents ?? []) {
2012
2282
  if (document.isRoot) {
2013
2283
  sidebarItems.unshift({
@@ -2056,27 +2326,49 @@ var DoculaBuilder = class {
2056
2326
  );
2057
2327
  return sidebarItems;
2058
2328
  }
2059
- getDocuments(sitePath, doculaData) {
2329
+ getDocuments(sitePath, doculaData, cachedDocs, previousDocHashes, currentDocHashes) {
2060
2330
  let documents = [];
2061
2331
  if (fs3.existsSync(sitePath)) {
2062
- documents = this.getDocumentInDirectory(sitePath);
2332
+ documents = this.getDocumentInDirectory(
2333
+ sitePath,
2334
+ sitePath,
2335
+ cachedDocs,
2336
+ previousDocHashes,
2337
+ currentDocHashes
2338
+ );
2063
2339
  doculaData.sections = this.getSections(sitePath, this.options);
2064
2340
  for (const section of doculaData.sections) {
2065
2341
  const sectionPath = `${sitePath}/${section.path}`;
2066
- const sectionDocuments = this.getDocumentInDirectory(sectionPath);
2342
+ const sectionDocuments = this.getDocumentInDirectory(
2343
+ sectionPath,
2344
+ sitePath,
2345
+ cachedDocs,
2346
+ previousDocHashes,
2347
+ currentDocHashes
2348
+ );
2067
2349
  documents = [...documents, ...sectionDocuments];
2068
2350
  }
2069
2351
  }
2070
2352
  return documents;
2071
2353
  }
2072
- getDocumentInDirectory(sitePath) {
2354
+ getDocumentInDirectory(sitePath, docsRootPath, cachedDocs, previousDocHashes, currentDocHashes) {
2073
2355
  const documents = [];
2074
2356
  const documentList = fs3.readdirSync(sitePath);
2075
2357
  if (documentList.length > 0) {
2076
2358
  for (const document of documentList) {
2077
2359
  const documentPath = `${sitePath}/${document}`;
2360
+ const relativeKey = path5.relative(docsRootPath, documentPath);
2078
2361
  const stats = fs3.statSync(documentPath);
2079
2362
  if (stats.isFile() && (document.endsWith(".md") || document.endsWith(".mdx"))) {
2363
+ if (cachedDocs && previousDocHashes && currentDocHashes) {
2364
+ const hash = currentDocHashes[relativeKey] ?? this.hashFile(documentPath);
2365
+ const prevHash = previousDocHashes[relativeKey];
2366
+ const cached = cachedDocs.get(relativeKey);
2367
+ if (cached && prevHash === hash) {
2368
+ documents.push(cached);
2369
+ continue;
2370
+ }
2371
+ }
2080
2372
  const documentData = this.parseDocumentData(documentPath);
2081
2373
  documents.push(documentData);
2082
2374
  }
@@ -2166,7 +2458,8 @@ ${markdownContent.slice(firstH2)}`;
2166
2458
  }),
2167
2459
  documentPath,
2168
2460
  urlPath,
2169
- isRoot
2461
+ isRoot,
2462
+ lastModified: fs3.statSync(documentPath).mtime.toISOString().split("T")[0]
2170
2463
  };
2171
2464
  }
2172
2465
  hasTableOfContents(markdown) {
@@ -2200,9 +2493,45 @@ ${markdownContent.slice(firstH2)}`;
2200
2493
  return resolvedTemplatePath;
2201
2494
  }
2202
2495
  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;
2496
+ if (fs3.existsSync(cacheDir)) {
2497
+ const diff = this.getChangedOverrides(
2498
+ overrideDir,
2499
+ cacheDir,
2500
+ overrideFiles
2501
+ );
2502
+ if (diff) {
2503
+ const hasChanges = diff.added.length > 0 || diff.changed.length > 0 || diff.removed.length > 0;
2504
+ if (!hasChanges) {
2505
+ this._console.step("Using cached template overrides...");
2506
+ return cacheDir;
2507
+ }
2508
+ this._console.step("Updating template overrides...");
2509
+ for (const file of diff.added) {
2510
+ this._console.info(`Template override added: ${file}`);
2511
+ const targetPath = path5.join(cacheDir, file);
2512
+ fs3.mkdirSync(path5.dirname(targetPath), { recursive: true });
2513
+ fs3.copyFileSync(path5.join(overrideDir, file), targetPath);
2514
+ }
2515
+ for (const file of diff.changed) {
2516
+ this._console.info(`Template override changed: ${file}`);
2517
+ const targetPath = path5.join(cacheDir, file);
2518
+ fs3.mkdirSync(path5.dirname(targetPath), { recursive: true });
2519
+ fs3.copyFileSync(path5.join(overrideDir, file), targetPath);
2520
+ }
2521
+ for (const file of diff.removed) {
2522
+ this._console.info(`Template override removed: ${file}`);
2523
+ const cachedPath = path5.join(cacheDir, file);
2524
+ const originalPath = path5.join(resolvedTemplatePath, file);
2525
+ if (fs3.existsSync(originalPath)) {
2526
+ fs3.copyFileSync(originalPath, cachedPath);
2527
+ } else if (fs3.existsSync(cachedPath)) {
2528
+ fs3.unlinkSync(cachedPath);
2529
+ }
2530
+ }
2531
+ const manifestPath2 = path5.join(cacheDir, ".manifest.json");
2532
+ fs3.writeFileSync(manifestPath2, JSON.stringify(diff.currentHashes));
2533
+ return cacheDir;
2534
+ }
2206
2535
  }
2207
2536
  if (overrideFiles.length > 0) {
2208
2537
  this._console.step("Applying template overrides...");
@@ -2217,8 +2546,12 @@ ${markdownContent.slice(firstH2)}`;
2217
2546
  fs3.mkdirSync(cacheDir, { recursive: true });
2218
2547
  this.copyDirectory(resolvedTemplatePath, cacheDir);
2219
2548
  this.copyDirectory(overrideDir, cacheDir);
2549
+ const currentHashes = {};
2550
+ for (const file of overrideFiles) {
2551
+ currentHashes[file] = this.hashFile(path5.join(overrideDir, file));
2552
+ }
2220
2553
  const manifestPath = path5.join(cacheDir, ".manifest.json");
2221
- fs3.writeFileSync(manifestPath, JSON.stringify(overrideFiles));
2554
+ fs3.writeFileSync(manifestPath, JSON.stringify(currentHashes));
2222
2555
  return cacheDir;
2223
2556
  }
2224
2557
  ensureCacheInGitignore(sitePath) {
@@ -2245,34 +2578,43 @@ ${entry}
2245
2578
  this._console.info("Created .gitignore with .cache");
2246
2579
  }
2247
2580
  }
2248
- isCacheFresh(overrideDir, cacheDir, overrideFiles) {
2581
+ getChangedOverrides(overrideDir, cacheDir, overrideFiles) {
2249
2582
  const manifestPath = path5.join(cacheDir, ".manifest.json");
2250
2583
  if (!fs3.existsSync(manifestPath)) {
2251
- return false;
2584
+ return void 0;
2252
2585
  }
2586
+ let previousHashes;
2253
2587
  try {
2254
- const previousFiles = JSON.parse(
2588
+ previousHashes = JSON.parse(
2255
2589
  fs3.readFileSync(manifestPath, "utf8")
2256
2590
  );
2257
- if (previousFiles.length !== overrideFiles.length || !previousFiles.every((f, i) => f === overrideFiles[i])) {
2258
- return false;
2259
- }
2260
2591
  } catch {
2261
- return false;
2592
+ return void 0;
2262
2593
  }
2594
+ const currentHashes = {};
2263
2595
  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;
2596
+ currentHashes[file] = this.hashFile(path5.join(overrideDir, file));
2597
+ }
2598
+ const added = [];
2599
+ const changed = [];
2600
+ const removed = [];
2601
+ for (const file of overrideFiles) {
2602
+ if (!(file in previousHashes)) {
2603
+ added.push(file);
2604
+ } else if (currentHashes[file] !== previousHashes[file]) {
2605
+ changed.push(file);
2268
2606
  }
2269
- const overrideMtime = fs3.statSync(overridePath).mtimeMs;
2270
- const cachedMtime = fs3.statSync(cachedPath).mtimeMs;
2271
- if (overrideMtime > cachedMtime) {
2272
- return false;
2607
+ }
2608
+ for (const file of Object.keys(previousHashes)) {
2609
+ if (!overrideFiles.includes(file)) {
2610
+ removed.push(file);
2273
2611
  }
2274
2612
  }
2275
- return true;
2613
+ return { added, changed, removed, currentHashes };
2614
+ }
2615
+ hashFile(filePath) {
2616
+ const content = fs3.readFileSync(filePath);
2617
+ return this._hash.toHashSync(content);
2276
2618
  }
2277
2619
  listFilesRecursive(dir, prefix = "") {
2278
2620
  const results = [];
@@ -2316,16 +2658,23 @@ ${entry}
2316
2658
  }
2317
2659
  }
2318
2660
  }
2319
- copyPublicFolder(sitePath, output) {
2661
+ copyPublicFolder(sitePath, output, previousAssets, currentAssets) {
2320
2662
  const publicPath = `${sitePath}/public`;
2321
2663
  if (!fs3.existsSync(publicPath)) {
2322
2664
  return;
2323
2665
  }
2324
2666
  this._console.step("Copying public folder...");
2325
2667
  const resolvedOutput = path5.resolve(output);
2326
- this.copyPublicDirectory(publicPath, output, publicPath, resolvedOutput);
2668
+ this.copyPublicDirectory(
2669
+ publicPath,
2670
+ output,
2671
+ publicPath,
2672
+ resolvedOutput,
2673
+ previousAssets,
2674
+ currentAssets
2675
+ );
2327
2676
  }
2328
- copyPublicDirectory(source, target, basePath, output) {
2677
+ copyPublicDirectory(source, target, basePath, output, previousAssets, currentAssets) {
2329
2678
  const files = fs3.readdirSync(source);
2330
2679
  for (const file of files) {
2331
2680
  const sourcePath = `${source}/${file}`;
@@ -2341,8 +2690,21 @@ ${entry}
2341
2690
  }
2342
2691
  if (stat.isDirectory()) {
2343
2692
  fs3.mkdirSync(targetPath, { recursive: true });
2344
- this.copyPublicDirectory(sourcePath, targetPath, basePath, output);
2693
+ this.copyPublicDirectory(
2694
+ sourcePath,
2695
+ targetPath,
2696
+ basePath,
2697
+ output,
2698
+ previousAssets,
2699
+ currentAssets
2700
+ );
2345
2701
  } else {
2702
+ const assetKey = `public/${relativePath}`;
2703
+ const hash = this.hashFile(sourcePath);
2704
+ currentAssets[assetKey] = hash;
2705
+ if (previousAssets[assetKey] === hash && fs3.existsSync(targetPath)) {
2706
+ continue;
2707
+ }
2346
2708
  fs3.mkdirSync(target, { recursive: true });
2347
2709
  fs3.copyFileSync(sourcePath, targetPath);
2348
2710
  this._console.fileCopied(relativePath);
@@ -2397,6 +2759,210 @@ ${entry}
2397
2759
  }
2398
2760
  return results;
2399
2761
  }
2762
+ loadBuildManifest(sitePath) {
2763
+ const manifestPath = path5.join(
2764
+ sitePath,
2765
+ ".cache",
2766
+ "build",
2767
+ "manifest.json"
2768
+ );
2769
+ if (!fs3.existsSync(manifestPath)) {
2770
+ return void 0;
2771
+ }
2772
+ try {
2773
+ const data = JSON.parse(
2774
+ fs3.readFileSync(manifestPath, "utf8")
2775
+ );
2776
+ if (data.version !== 1) {
2777
+ return void 0;
2778
+ }
2779
+ return data;
2780
+ } catch {
2781
+ return void 0;
2782
+ }
2783
+ }
2784
+ saveBuildManifest(sitePath, manifest) {
2785
+ const dir = path5.join(sitePath, ".cache", "build");
2786
+ fs3.mkdirSync(dir, { recursive: true });
2787
+ fs3.writeFileSync(path5.join(dir, "manifest.json"), JSON.stringify(manifest));
2788
+ }
2789
+ hashOptions() {
2790
+ const relevant = {
2791
+ siteUrl: this.options.siteUrl,
2792
+ siteTitle: this.options.siteTitle,
2793
+ siteDescription: this.options.siteDescription,
2794
+ githubPath: this.options.githubPath,
2795
+ template: this.options.template,
2796
+ templatePath: this.options.templatePath,
2797
+ enableLlmsTxt: this.options.enableLlmsTxt,
2798
+ changelogPerPage: this.options.changelogPerPage,
2799
+ enableReleaseChangelog: this.options.enableReleaseChangelog,
2800
+ sections: this.options.sections,
2801
+ openApiUrl: this.options.openApiUrl,
2802
+ themeMode: this.options.themeMode,
2803
+ cookieAuth: this.options.cookieAuth,
2804
+ headerLinks: this.options.headerLinks
2805
+ };
2806
+ return this._hash.toHashSync(JSON.stringify(relevant));
2807
+ }
2808
+ hashTemplateDirectory(templatePath) {
2809
+ if (!fs3.existsSync(templatePath)) {
2810
+ return "";
2811
+ }
2812
+ const files = this.listFilesRecursive(templatePath);
2813
+ const hashes = files.map((f) => this.hashFile(path5.join(templatePath, f)));
2814
+ return this._hash.toHashSync(hashes.join(""));
2815
+ }
2816
+ loadCachedDocuments(sitePath) {
2817
+ const cachePath = path5.join(sitePath, ".cache", "build", "documents.json");
2818
+ if (!fs3.existsSync(cachePath)) {
2819
+ return /* @__PURE__ */ new Map();
2820
+ }
2821
+ try {
2822
+ const data = JSON.parse(fs3.readFileSync(cachePath, "utf8"));
2823
+ return new Map(Object.entries(data));
2824
+ } catch {
2825
+ return /* @__PURE__ */ new Map();
2826
+ }
2827
+ }
2828
+ saveCachedDocuments(sitePath, documents) {
2829
+ const dir = path5.join(sitePath, ".cache", "build");
2830
+ fs3.mkdirSync(dir, { recursive: true });
2831
+ const docsRoot = path5.join(sitePath, "docs");
2832
+ const map = {};
2833
+ for (const doc of documents) {
2834
+ const relativeKey = path5.relative(docsRoot, doc.documentPath);
2835
+ map[relativeKey] = doc;
2836
+ }
2837
+ fs3.writeFileSync(path5.join(dir, "documents.json"), JSON.stringify(map));
2838
+ }
2839
+ loadCachedChangelog(sitePath) {
2840
+ const cachePath = path5.join(sitePath, ".cache", "build", "changelog.json");
2841
+ if (!fs3.existsSync(cachePath)) {
2842
+ return /* @__PURE__ */ new Map();
2843
+ }
2844
+ try {
2845
+ const data = JSON.parse(fs3.readFileSync(cachePath, "utf8"));
2846
+ return new Map(Object.entries(data));
2847
+ } catch {
2848
+ return /* @__PURE__ */ new Map();
2849
+ }
2850
+ }
2851
+ saveCachedChangelog(sitePath, entries) {
2852
+ const dir = path5.join(sitePath, ".cache", "build");
2853
+ fs3.mkdirSync(dir, { recursive: true });
2854
+ const map = {};
2855
+ for (const entry of entries) {
2856
+ map[entry.slug] = entry;
2857
+ }
2858
+ fs3.writeFileSync(path5.join(dir, "changelog.json"), JSON.stringify(map));
2859
+ }
2860
+ hashSourceFiles(dir) {
2861
+ const hashes = {};
2862
+ if (!fs3.existsSync(dir)) {
2863
+ return hashes;
2864
+ }
2865
+ const files = this.listFilesRecursive(dir);
2866
+ for (const file of files) {
2867
+ const fullPath = path5.join(dir, file);
2868
+ hashes[file] = this.hashFile(fullPath);
2869
+ }
2870
+ return hashes;
2871
+ }
2872
+ recordsEqual(a, b) {
2873
+ const keysA = Object.keys(a);
2874
+ const keysB = Object.keys(b);
2875
+ if (keysA.length !== keysB.length) {
2876
+ return false;
2877
+ }
2878
+ for (const key of keysA) {
2879
+ if (a[key] !== b[key]) {
2880
+ return false;
2881
+ }
2882
+ }
2883
+ return true;
2884
+ }
2885
+ hasAssetsChanged(sitePath, previousAssets) {
2886
+ const assetFiles = [
2887
+ "favicon.ico",
2888
+ "logo.svg",
2889
+ "logo_horizontal.png",
2890
+ "variables.css",
2891
+ "api/swagger.json",
2892
+ "README.md"
2893
+ ];
2894
+ for (const file of assetFiles) {
2895
+ const filePath = path5.join(sitePath, file);
2896
+ if (fs3.existsSync(filePath)) {
2897
+ const hash = this.hashFile(filePath);
2898
+ if (previousAssets[file] !== hash) {
2899
+ return true;
2900
+ }
2901
+ } else if (previousAssets[file]) {
2902
+ return true;
2903
+ }
2904
+ }
2905
+ const publicPath = path5.join(sitePath, "public");
2906
+ if (fs3.existsSync(publicPath)) {
2907
+ const publicHashes = this.hashSourceFiles(publicPath);
2908
+ for (const [file, hash] of Object.entries(publicHashes)) {
2909
+ if (previousAssets[`public/${file}`] !== hash) {
2910
+ return true;
2911
+ }
2912
+ }
2913
+ }
2914
+ return false;
2915
+ }
2916
+ /**
2917
+ * Hashes the source file, records it in currentAssets, and returns
2918
+ * whether the copy can be skipped (unchanged from previous build).
2919
+ */
2920
+ hashAssetAndCheckSkip(sourcePath, targetPath, assetKey, previousAssets, currentAssets) {
2921
+ if (!fs3.existsSync(sourcePath)) {
2922
+ return true;
2923
+ }
2924
+ const hash = this.hashFile(sourcePath);
2925
+ currentAssets[assetKey] = hash;
2926
+ if (previousAssets[assetKey] === hash && fs3.existsSync(targetPath)) {
2927
+ return true;
2928
+ }
2929
+ return false;
2930
+ }
2931
+ copyDirectoryWithHashing(source, target, prefix, previousAssets, currentAssets) {
2932
+ if (!fs3.existsSync(source)) {
2933
+ return;
2934
+ }
2935
+ const files = fs3.readdirSync(source);
2936
+ for (const file of files) {
2937
+ if (file.startsWith(".")) {
2938
+ continue;
2939
+ }
2940
+ const sourcePath = `${source}/${file}`;
2941
+ const targetPath = `${target}/${file}`;
2942
+ const assetKey = prefix ? `${prefix}/${file}` : file;
2943
+ const stat = fs3.lstatSync(sourcePath);
2944
+ if (stat.isSymbolicLink()) {
2945
+ continue;
2946
+ }
2947
+ if (stat.isDirectory()) {
2948
+ this.copyDirectoryWithHashing(
2949
+ sourcePath,
2950
+ targetPath,
2951
+ assetKey,
2952
+ previousAssets,
2953
+ currentAssets
2954
+ );
2955
+ } else {
2956
+ const hash = this.hashFile(sourcePath);
2957
+ currentAssets[assetKey] = hash;
2958
+ if (previousAssets[assetKey] === hash && fs3.existsSync(targetPath)) {
2959
+ continue;
2960
+ }
2961
+ fs3.mkdirSync(target, { recursive: true });
2962
+ fs3.copyFileSync(sourcePath, targetPath);
2963
+ }
2964
+ }
2965
+ }
2400
2966
  copyContentAssets(sourcePath, targetPath) {
2401
2967
  if (!fs3.existsSync(sourcePath)) {
2402
2968
  return;
@@ -2426,8 +2992,8 @@ ${entry}
2426
2992
  };
2427
2993
 
2428
2994
  // src/init.ts
2429
- var doculaconfigmjs = "ZXhwb3J0IGNvbnN0IG9wdGlvbnMgPSB7CglvdXRwdXQ6ICcuL2Rpc3QnLAoJc2l0ZVBhdGg6ICcuL3NpdGUnLAoJZ2l0aHViUGF0aDogJ2phcmVkd3JheS9kb2N1bGEnLAoJc2l0ZVRpdGxlOiAnRG9jdWxhJywKCXNpdGVEZXNjcmlwdGlvbjogJ0JlYXV0aWZ1bCBXZWJzaXRlIGZvciBZb3VyIFByb2plY3RzJywKCXNpdGVVcmw6ICdodHRwczovL2RvY3VsYS5vcmcnLAoJLy8gb3BlbkFwaVVybDogJy9hcGkvc3dhZ2dlci5qc29uJywKCS8vIGVuYWJsZVJlbGVhc2VDaGFuZ2Vsb2c6IHRydWUsCgkvLyBlbmFibGVMbG1zVHh0OiB0cnVlLAoJLy8gaG9tZVBhZ2U6IHRydWUsCgkvLyB0aGVtZU1vZGU6ICdsaWdodCcsCn07Cg==";
2430
- var doculaconfigts = "aW1wb3J0IHR5cGUgeyBEb2N1bGFPcHRpb25zIH0gZnJvbSAnZG9jdWxhJzsKCmV4cG9ydCBjb25zdCBvcHRpb25zOiBQYXJ0aWFsPERvY3VsYU9wdGlvbnM+ID0gewoJb3V0cHV0OiAnLi9kaXN0JywKCXNpdGVQYXRoOiAnLi9zaXRlJywKCWdpdGh1YlBhdGg6ICdqYXJlZHdyYXkvZG9jdWxhJywKCXNpdGVUaXRsZTogJ0RvY3VsYScsCglzaXRlRGVzY3JpcHRpb246ICdCZWF1dGlmdWwgV2Vic2l0ZSBmb3IgWW91ciBQcm9qZWN0cycsCglzaXRlVXJsOiAnaHR0cHM6Ly9kb2N1bGEub3JnJywKCS8vIG9wZW5BcGlVcmw6ICcvYXBpL3N3YWdnZXIuanNvbicsCgkvLyBlbmFibGVSZWxlYXNlQ2hhbmdlbG9nOiB0cnVlLAoJLy8gZW5hYmxlTGxtc1R4dDogdHJ1ZSwKCS8vIGhvbWVQYWdlOiB0cnVlLAoJLy8gdGhlbWVNb2RlOiAnbGlnaHQnLAp9Owo=";
2995
+ var doculaconfigmjs = "ZXhwb3J0IGNvbnN0IG9wdGlvbnMgPSB7CglvdXRwdXQ6ICcuL2Rpc3QnLAoJc2l0ZVBhdGg6ICcuL3NpdGUnLAoJZ2l0aHViUGF0aDogJ2phcmVkd3JheS9kb2N1bGEnLAoJc2l0ZVRpdGxlOiAnRG9jdWxhJywKCXNpdGVEZXNjcmlwdGlvbjogJ0JlYXV0aWZ1bCBXZWJzaXRlIGZvciBZb3VyIFByb2plY3RzJywKCXNpdGVVcmw6ICdodHRwczovL2RvY3VsYS5vcmcnLAoJLy8gb3BlbkFwaVVybDogJy9hcGkvc3dhZ2dlci5qc29uJywKCS8vIGVuYWJsZVJlbGVhc2VDaGFuZ2Vsb2c6IHRydWUsCgkvLyBlbmFibGVMbG1zVHh0OiB0cnVlLAoJLy8gdGhlbWVNb2RlOiAnbGlnaHQnLAp9Owo=";
2996
+ var doculaconfigts = "aW1wb3J0IHR5cGUgeyBEb2N1bGFPcHRpb25zIH0gZnJvbSAnZG9jdWxhJzsKCmV4cG9ydCBjb25zdCBvcHRpb25zOiBQYXJ0aWFsPERvY3VsYU9wdGlvbnM+ID0gewoJb3V0cHV0OiAnLi9kaXN0JywKCXNpdGVQYXRoOiAnLi9zaXRlJywKCWdpdGh1YlBhdGg6ICdqYXJlZHdyYXkvZG9jdWxhJywKCXNpdGVUaXRsZTogJ0RvY3VsYScsCglzaXRlRGVzY3JpcHRpb246ICdCZWF1dGlmdWwgV2Vic2l0ZSBmb3IgWW91ciBQcm9qZWN0cycsCglzaXRlVXJsOiAnaHR0cHM6Ly9kb2N1bGEub3JnJywKCS8vIG9wZW5BcGlVcmw6ICcvYXBpL3N3YWdnZXIuanNvbicsCgkvLyBlbmFibGVSZWxlYXNlQ2hhbmdlbG9nOiB0cnVlLAoJLy8gZW5hYmxlTGxtc1R4dDogdHJ1ZSwKCS8vIHRoZW1lTW9kZTogJ2xpZ2h0JywKfTsK";
2431
2997
  var faviconico = "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";
2432
2998
  var logopng = "iVBORw0KGgoAAAANSUhEUgAAAxEAAAHXCAYAAADDSdfmAAAACXBIWXMAACxLAAAsSwGlPZapAAAFw2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4xLWMwMDMgNzkuOTY5MGE4N2ZjLCAyMDI1LzAzLzA2LTIwOjUwOjE2ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjYuNiAoTWFjaW50b3NoKSIgeG1wOkNyZWF0ZURhdGU9IjIwMjUtMDUtMThUMTM6MzM6NTgtMDc6MDAiIHhtcDpNb2RpZnlEYXRlPSIyMDI1LTA1LTE4VDEzOjM2OjE5LTA3OjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDI1LTA1LTE4VDEzOjM2OjE5LTA3OjAwIiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo5YTc4ZDY2Ny02NjAzLTQwNmUtYTViZC02MjYzMTNjY2VmY2QiIHhtcE1NOkRvY3VtZW50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";
2433
2999
 
@@ -2534,7 +3100,7 @@ var Docula = class {
2534
3100
  }
2535
3101
  if (this._configFileModule.onPrepare) {
2536
3102
  try {
2537
- await this._configFileModule.onPrepare(this.options);
3103
+ await this._configFileModule.onPrepare(this.options, this._console);
2538
3104
  } catch (error) {
2539
3105
  this._console.error(error.message);
2540
3106
  }
@@ -2542,18 +3108,37 @@ var Docula = class {
2542
3108
  if (consoleProcess.args.template) {
2543
3109
  this.options.template = consoleProcess.args.template;
2544
3110
  }
3111
+ if (consoleProcess.args.templatePath) {
3112
+ this.options.templatePath = consoleProcess.args.templatePath;
3113
+ }
2545
3114
  if (consoleProcess.args.output) {
2546
3115
  this.options.output = consoleProcess.args.output;
3116
+ } else {
3117
+ const configOptions = this._configFileModule.options;
3118
+ if (!configOptions?.output) {
3119
+ this.options.output = path6.join(this.options.sitePath, "dist");
3120
+ }
2547
3121
  }
2548
3122
  if (consoleProcess.args.port !== void 0 && !Number.isNaN(consoleProcess.args.port)) {
2549
3123
  this.options.port = consoleProcess.args.port;
2550
3124
  }
2551
3125
  switch (consoleProcess.command) {
2552
3126
  case "init": {
2553
- this.generateInit(
2554
- this.options.sitePath,
2555
- consoleProcess.args.typescript
2556
- );
3127
+ if (consoleProcess.args.typescript && consoleProcess.args.javascript) {
3128
+ this._console.error(
3129
+ "Cannot use both --typescript and --javascript flags. Please choose one."
3130
+ );
3131
+ break;
3132
+ }
3133
+ let useTypeScript;
3134
+ if (consoleProcess.args.typescript) {
3135
+ useTypeScript = true;
3136
+ } else if (consoleProcess.args.javascript) {
3137
+ useTypeScript = false;
3138
+ } else {
3139
+ useTypeScript = this.detectTypeScript();
3140
+ }
3141
+ this.generateInit(this.options.sitePath, useTypeScript);
2557
3142
  break;
2558
3143
  }
2559
3144
  case "help": {
@@ -2564,16 +3149,23 @@ var Docula = class {
2564
3149
  this._console.log(this.getVersion());
2565
3150
  break;
2566
3151
  }
3152
+ case "dev": {
3153
+ const devBuilder = await this.runBuild(consoleProcess.args.clean);
3154
+ this.watch(this.options, devBuilder);
3155
+ await this.serve(this.options);
3156
+ break;
3157
+ }
3158
+ case "start": {
3159
+ const startBuilder = await this.runBuild(consoleProcess.args.clean);
3160
+ if (consoleProcess.args.watch) {
3161
+ this.watch(this.options, startBuilder);
3162
+ }
3163
+ await this.serve(this.options);
3164
+ break;
3165
+ }
2567
3166
  case "serve": {
2568
3167
  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();
3168
+ const builder = await this.runBuild(consoleProcess.args.clean);
2577
3169
  if (consoleProcess.args.watch) {
2578
3170
  this.watch(this.options, builder);
2579
3171
  }
@@ -2582,18 +3174,32 @@ var Docula = class {
2582
3174
  break;
2583
3175
  }
2584
3176
  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();
3177
+ await this.runBuild(consoleProcess.args.clean);
2593
3178
  break;
2594
3179
  }
2595
3180
  }
2596
3181
  }
3182
+ async runBuild(clean) {
3183
+ if (clean && fs4.existsSync(this.options.output)) {
3184
+ fs4.rmSync(this.options.output, { recursive: true, force: true });
3185
+ }
3186
+ if (clean) {
3187
+ this.cleanCache(this.options.sitePath);
3188
+ }
3189
+ const builder = new DoculaBuilder(this.options);
3190
+ if (this._configFileModule.onReleaseChangelog) {
3191
+ builder.onReleaseChangelog = this._configFileModule.onReleaseChangelog;
3192
+ }
3193
+ await builder.build();
3194
+ return builder;
3195
+ }
3196
+ /**
3197
+ * Detect if the current project uses TypeScript by checking for tsconfig.json
3198
+ * @returns {boolean}
3199
+ */
3200
+ detectTypeScript() {
3201
+ return fs4.existsSync(path6.join(process4.cwd(), "tsconfig.json"));
3202
+ }
2597
3203
  /**
2598
3204
  * Generate the init files
2599
3205
  * @param {string} sitePath
@@ -2672,7 +3278,10 @@ var Docula = class {
2672
3278
  this._console.info(`File changed: ${filename}, rebuilding...`);
2673
3279
  try {
2674
3280
  await builder.build();
2675
- this._console.success("Rebuild complete");
3281
+ this._console.success(`Rebuild complete`);
3282
+ this._console.banner(
3283
+ `\u{1F987} at http://localhost:${options.port}`
3284
+ );
2676
3285
  } catch (error) {
2677
3286
  this._console.error(`Rebuild failed: ${error.message}`);
2678
3287
  } finally {
@@ -2690,6 +3299,9 @@ var Docula = class {
2690
3299
  if (filename && outputRelative && !outputRelative.startsWith("..") && (String(filename) === outputRelative || String(filename).startsWith(`${outputRelative}${path6.sep}`))) {
2691
3300
  return;
2692
3301
  }
3302
+ if (filename && (String(filename) === ".cache" || String(filename).startsWith(`.cache${path6.sep}`))) {
3303
+ return;
3304
+ }
2693
3305
  if (isBuilding) {
2694
3306
  pendingRebuild = true;
2695
3307
  return;
@@ -2746,6 +3358,7 @@ var Docula = class {
2746
3358
  }
2747
3359
  };
2748
3360
  export {
3361
+ DoculaConsole,
2749
3362
  DoculaOptions,
2750
3363
  Writr2 as Writr,
2751
3364
  Docula as default
@@ -2757,4 +3370,5 @@ export {
2757
3370
  /* v8 ignore next 9 -- @preserve */
2758
3371
  /* v8 ignore next 4 -- @preserve */
2759
3372
  /* v8 ignore start -- @preserve */
3373
+ /* v8 ignore next 8 -- @preserve */
2760
3374
  /* v8 ignore stop -- @preserve */