@dimm-city/print-md 0.5.0-rc.2 → 0.5.0-rc.4

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/cli.js CHANGED
@@ -758,6 +758,15 @@ var init_images = __esm(() => {
758
758
  PREFIX_PATTERNS = [/^(?:\.\/|\/)?temp\/images\//, /^(?:\.\/|\/)?images\//];
759
759
  });
760
760
 
761
+ // ../lib/src/lib/markdown/chapter-id.ts
762
+ function canonicalChapterId(p) {
763
+ let s = String(p).replace(/\\/g, "/");
764
+ s = s.replace(/\/{2,}/g, "/");
765
+ while (s.startsWith("./"))
766
+ s = s.slice(2);
767
+ return s;
768
+ }
769
+
761
770
  // ../lib/src/lib/markdown/plugins.ts
762
771
  import { existsSync as existsSync2 } from "node:fs";
763
772
  import { resolve as resolve2, join } from "node:path";
@@ -911,12 +920,13 @@ async function renderChapters(inputDir, opts = {}) {
911
920
  }
912
921
  let bodyContent = "";
913
922
  for (const file of files) {
914
- const filePath = join2(inputDir, file);
923
+ const chapterId = canonicalChapterId(file);
924
+ const filePath = join2(inputDir, chapterId);
915
925
  try {
916
926
  const content = await readFile2(filePath, "utf-8");
917
927
  const rendered = md.render(content);
918
928
  if (opts.wrapChapters) {
919
- const safe = String(file).replace(/&/g, "&").replace(/"/g, """);
929
+ const safe = chapterId.replace(/&/g, "&").replace(/"/g, """);
920
930
  bodyContent += `<div class="pmd-chapter" data-chapter-src="${safe}">
921
931
  ${rendered}
922
932
  </div>
@@ -22693,7 +22703,7 @@ var package_default;
22693
22703
  var init_package = __esm(() => {
22694
22704
  package_default = {
22695
22705
  name: "@dimm-city/print-md-lib",
22696
- version: "0.5.0-rc.2",
22706
+ version: "0.5.0-rc.4",
22697
22707
  private: true,
22698
22708
  type: "module",
22699
22709
  main: "dist/index.js",
@@ -22950,7 +22960,7 @@ var pagedjs_bridge_default = "./pagedjs-bridge-k1a8jxtv.js";
22950
22960
  var init_pagedjs_bridge = () => {};
22951
22961
 
22952
22962
  // ../lib/src/assets/preview/scripts/preview-shell.js
22953
- var preview_shell_default = "./preview-shell-d3y4np7a.js";
22963
+ var preview_shell_default = "./preview-shell-fx145xzs.js";
22954
22964
  var init_preview_shell = () => {};
22955
22965
 
22956
22966
  // ../lib/src/assets/vendor/paged.polyfill.js
@@ -93686,7 +93696,7 @@ function createFileWatcher(state) {
93686
93696
  await generateAndWriteHtml(state.currentInputPath, state.tempDir, updatedConfig);
93687
93697
  const only = dests.length === 1 ? dests[0] : null;
93688
93698
  if (incrementalPreviewEnabled() && changes.length === 1 && only && only.ext === ".md" && only.event !== "unlink" && only.event !== "unlinkDir") {
93689
- state.previewServer?.broadcastContentUpdate(only.relativePath);
93699
+ state.previewServer?.broadcastContentUpdate(canonicalChapterId(only.relativePath));
93690
93700
  info(`Chapter updated: ${only.relativePath}`);
93691
93701
  } else {
93692
93702
  state.previewServer?.broadcastReload();
@@ -94477,16 +94487,7 @@ async function findEnclosingRepoDir(folderPath) {
94477
94487
  }
94478
94488
  return;
94479
94489
  }
94480
- async function detectProjectSource(folderPath) {
94481
- const gitDir = path9.join(folderPath, ".git");
94482
- if (!await isDirectory2(gitDir)) {
94483
- const enclosingRepoDir = await findEnclosingRepoDir(folderPath);
94484
- return {
94485
- type: "local-folder",
94486
- path: folderPath,
94487
- ...enclosingRepoDir !== undefined ? { enclosingRepoDir } : {}
94488
- };
94489
- }
94490
+ async function readGitDirInfo(gitDir) {
94490
94491
  let remoteUrl;
94491
94492
  try {
94492
94493
  const configText = await readFile21(path9.join(gitDir, "config"), "utf8");
@@ -94501,13 +94502,40 @@ async function detectProjectSource(folderPath) {
94501
94502
  } catch {
94502
94503
  branch = undefined;
94503
94504
  }
94504
- return {
94505
- type: "local-git-folder",
94506
- path: folderPath,
94507
- hasRemote: remoteUrl !== undefined,
94508
- ...remoteUrl !== undefined ? { remoteUrl } : {},
94509
- ...branch !== undefined ? { branch } : {}
94510
- };
94505
+ return { remoteUrl, branch };
94506
+ }
94507
+ function repoSubPath(repoRoot, folderPath) {
94508
+ const rel = path9.relative(path9.resolve(repoRoot), path9.resolve(folderPath));
94509
+ return rel.split(path9.sep).join("/");
94510
+ }
94511
+ async function detectProjectSource(folderPath) {
94512
+ const ownGitDir = path9.join(folderPath, ".git");
94513
+ if (await isDirectory2(ownGitDir)) {
94514
+ const { remoteUrl, branch } = await readGitDirInfo(ownGitDir);
94515
+ return {
94516
+ type: "local-git-folder",
94517
+ path: folderPath,
94518
+ repoRoot: folderPath,
94519
+ subPath: "",
94520
+ hasRemote: remoteUrl !== undefined,
94521
+ ...remoteUrl !== undefined ? { remoteUrl } : {},
94522
+ ...branch !== undefined ? { branch } : {}
94523
+ };
94524
+ }
94525
+ const enclosingRepoDir = await findEnclosingRepoDir(folderPath);
94526
+ if (enclosingRepoDir !== undefined) {
94527
+ const { remoteUrl, branch } = await readGitDirInfo(path9.join(enclosingRepoDir, ".git"));
94528
+ return {
94529
+ type: "local-git-folder",
94530
+ path: folderPath,
94531
+ repoRoot: enclosingRepoDir,
94532
+ subPath: repoSubPath(enclosingRepoDir, folderPath),
94533
+ hasRemote: remoteUrl !== undefined,
94534
+ ...remoteUrl !== undefined ? { remoteUrl } : {},
94535
+ ...branch !== undefined ? { branch } : {}
94536
+ };
94537
+ }
94538
+ return { type: "local-folder", path: folderPath };
94511
94539
  }
94512
94540
  function capabilitiesFor(source) {
94513
94541
  switch (source.type) {
@@ -94515,11 +94543,10 @@ function capabilitiesFor(source) {
94515
94543
  return {
94516
94544
  canRead: true,
94517
94545
  canWriteLocal: true,
94518
- canEnableVersionHistory: source.enclosingRepoDir === undefined,
94546
+ canEnableVersionHistory: true,
94519
94547
  canSnapshot: false,
94520
94548
  canViewHistory: false,
94521
94549
  canRestoreSnapshot: false,
94522
- canPublish: false,
94523
94550
  canSync: false,
94524
94551
  authManagedByApp: false
94525
94552
  };
@@ -94531,7 +94558,6 @@ function capabilitiesFor(source) {
94531
94558
  canSnapshot: true,
94532
94559
  canViewHistory: true,
94533
94560
  canRestoreSnapshot: true,
94534
- canPublish: source.hasRemote,
94535
94561
  canSync: source.hasRemote,
94536
94562
  authManagedByApp: false
94537
94563
  };
@@ -94543,7 +94569,6 @@ function capabilitiesFor(source) {
94543
94569
  canSnapshot: true,
94544
94570
  canViewHistory: true,
94545
94571
  canRestoreSnapshot: true,
94546
- canPublish: true,
94547
94572
  canSync: true,
94548
94573
  authManagedByApp: true
94549
94574
  };
@@ -107734,6 +107759,7 @@ __export(exports_source_provider, {
107734
107759
  isNoChangesError: () => isNoChangesError,
107735
107760
  isGitInternalPath: () => isGitInternalPath,
107736
107761
  hasPendingChanges: () => hasPendingChanges,
107762
+ gitScopeFor: () => gitScopeFor,
107737
107763
  gitDirFor: () => gitDirFor,
107738
107764
  gitAuthor: () => gitAuthor,
107739
107765
  autoSnapshotDelayMs: () => autoSnapshotDelayMs,
@@ -107745,6 +107771,9 @@ __export(exports_source_provider, {
107745
107771
  });
107746
107772
  import * as fs5 from "node:fs";
107747
107773
  import path10 from "node:path";
107774
+ function gitScopeFor(source) {
107775
+ return { dir: source.repoRoot || source.path, subPath: source.subPath || "" };
107776
+ }
107748
107777
  function withRepoLock(projectDir, fn2) {
107749
107778
  const key = path10.resolve(projectDir);
107750
107779
  const prev = repoQueues.get(key) ?? Promise.resolve();
@@ -107758,12 +107787,20 @@ function gitAuthor(name) {
107758
107787
  const n = (name ?? "").trim();
107759
107788
  return { name: n || DEFAULT_AUTHOR, email: DEFAULT_EMAIL };
107760
107789
  }
107761
- async function stageAll(dir) {
107762
- const status = await import_isomorphic_git.default.statusMatrix({ fs: fs5, dir });
107790
+ async function stageAll(dir, subPath) {
107791
+ const status = await import_isomorphic_git.default.statusMatrix({
107792
+ fs: fs5,
107793
+ dir,
107794
+ ...subPath ? { filepaths: [subPath] } : {}
107795
+ });
107763
107796
  await Promise.all(status.map(([filepath, , worktreeStatus]) => worktreeStatus === 0 ? import_isomorphic_git.default.remove({ fs: fs5, dir, filepath }) : import_isomorphic_git.default.add({ fs: fs5, dir, filepath })));
107764
107797
  }
107765
- async function hasPendingChanges(dir) {
107766
- const status = await import_isomorphic_git.default.statusMatrix({ fs: fs5, dir });
107798
+ async function hasPendingChanges(dir, subPath) {
107799
+ const status = await import_isomorphic_git.default.statusMatrix({
107800
+ fs: fs5,
107801
+ dir,
107802
+ ...subPath ? { filepaths: [subPath] } : {}
107803
+ });
107767
107804
  return status.some(([, head, worktree, stage]) => !(head === 1 && worktree === 1 && stage === 1));
107768
107805
  }
107769
107806
 
@@ -107774,11 +107811,11 @@ class LocalFolderSourceProvider {
107774
107811
  this.source = source;
107775
107812
  this.capabilities = capabilitiesFor(source);
107776
107813
  }
107777
- initVersionHistory(options) {
107778
- if (this.source.type === "local-folder" && this.source.enclosingRepoDir !== undefined) {
107779
- return Promise.reject(new Error("This folder is already inside a versioned project, so print-md " + "won't create a separate history here."));
107780
- }
107814
+ async initVersionHistory(options) {
107781
107815
  const dir = options.projectDir;
107816
+ if (await findEnclosingRepoDir(dir) !== undefined) {
107817
+ throw new Error("This folder is already inside a versioned project, so print-md " + "won't create a separate history here.");
107818
+ }
107782
107819
  return withRepoLock(dir, async () => {
107783
107820
  await import_isomorphic_git.default.init({ fs: fs5, dir, defaultBranch: DEFAULT_BRANCH });
107784
107821
  await stageAll(dir);
@@ -107791,6 +107828,8 @@ class LocalFolderSourceProvider {
107791
107828
  return {
107792
107829
  type: "local-git-folder",
107793
107830
  path: dir,
107831
+ repoRoot: dir,
107832
+ subPath: "",
107794
107833
  hasRemote: false,
107795
107834
  branch: DEFAULT_BRANCH
107796
107835
  };
@@ -107810,9 +107849,11 @@ class LocalFolderSourceProvider {
107810
107849
  class LocalGitSourceProvider {
107811
107850
  source;
107812
107851
  capabilities;
107852
+ scope;
107813
107853
  constructor(source) {
107814
107854
  this.source = source;
107815
107855
  this.capabilities = capabilitiesFor(source);
107856
+ this.scope = gitScopeFor(source);
107816
107857
  }
107817
107858
  async initVersionHistory(options) {
107818
107859
  try {
@@ -107828,14 +107869,23 @@ class LocalGitSourceProvider {
107828
107869
  return this.source;
107829
107870
  }
107830
107871
  snapshot(options) {
107831
- return withRepoLock(options.projectDir, () => this.snapshotUnlocked(options));
107872
+ return withRepoLock(this.scope.dir, () => this.snapshotUnlocked(options));
107832
107873
  }
107833
107874
  snapshotUnlocked(options) {
107834
- return snapshotWorkingTreeUnlocked(options);
107875
+ return snapshotWorkingTreeUnlocked({
107876
+ ...options,
107877
+ repoRoot: this.scope.dir,
107878
+ ...this.scope.subPath ? { subPath: this.scope.subPath } : {}
107879
+ });
107835
107880
  }
107836
- listHistory(projectDir) {
107837
- return withRepoLock(projectDir, async () => {
107838
- const commits = await import_isomorphic_git.default.log({ fs: fs5, dir: projectDir });
107881
+ listHistory(_projectDir) {
107882
+ const { dir, subPath } = this.scope;
107883
+ return withRepoLock(dir, async () => {
107884
+ const commits = await import_isomorphic_git.default.log({
107885
+ fs: fs5,
107886
+ dir,
107887
+ ...subPath ? { filepath: subPath, force: true } : {}
107888
+ });
107839
107889
  return commits.map((c) => ({
107840
107890
  id: c.oid,
107841
107891
  message: c.commit.message.trim(),
@@ -107845,24 +107895,27 @@ class LocalGitSourceProvider {
107845
107895
  });
107846
107896
  }
107847
107897
  restore(options) {
107848
- return withRepoLock(options.projectDir, () => this.restoreUnlocked(options));
107898
+ return withRepoLock(this.scope.dir, () => this.restoreUnlocked(options));
107849
107899
  }
107850
107900
  async restoreUnlocked(options) {
107901
+ const { dir, subPath } = this.scope;
107851
107902
  await import_isomorphic_git.default.checkout({
107852
107903
  fs: fs5,
107853
- dir: options.projectDir,
107904
+ dir,
107854
107905
  ref: options.id,
107855
107906
  force: true,
107856
- noUpdateHead: true
107907
+ noUpdateHead: true,
107908
+ ...subPath ? { filepaths: [subPath] } : {}
107857
107909
  });
107858
107910
  }
107859
107911
  }
107860
107912
  async function snapshotWorkingTreeUnlocked(options) {
107861
- const dir = options.projectDir;
107862
- if (!await hasPendingChanges(dir)) {
107913
+ const dir = options.repoRoot ?? options.projectDir;
107914
+ const subPath = options.subPath || undefined;
107915
+ if (!await hasPendingChanges(dir, subPath)) {
107863
107916
  throw new Error("No changes since the last snapshot — there is nothing new to save.");
107864
107917
  }
107865
- await stageAll(dir);
107918
+ await stageAll(dir, subPath);
107866
107919
  const author = gitAuthor(options.authorName);
107867
107920
  const id2 = await import_isomorphic_git.default.commit({
107868
107921
  fs: fs5,
@@ -107913,9 +107966,10 @@ async function restoreVersionWithBackup(options) {
107913
107966
  throw new Error("This project has no version history yet. Enable version history first.");
107914
107967
  }
107915
107968
  const provider = new LocalGitSourceProvider(source);
107916
- return withRepoLock(projectDir, async () => {
107969
+ const scope = gitScopeFor(source);
107970
+ return withRepoLock(scope.dir, async () => {
107917
107971
  let backupId;
107918
- if (await hasPendingChanges(projectDir)) {
107972
+ if (await hasPendingChanges(scope.dir, scope.subPath || undefined)) {
107919
107973
  const backup = await provider.snapshotUnlocked({
107920
107974
  projectDir,
107921
107975
  message: RESTORE_BACKUP_MESSAGE,
@@ -108156,9 +108210,10 @@ var init_diagnose = __esm(() => {
108156
108210
  init_token_store();
108157
108211
  });
108158
108212
 
108159
- // ../lib/src/lib/remote-auth/publish.ts
108213
+ // ../lib/src/lib/remote-auth/sync.ts
108160
108214
  var import_diff3;
108161
- var init_publish = __esm(() => {
108215
+ var init_sync = __esm(() => {
108216
+ init_project_source();
108162
108217
  init_source_provider();
108163
108218
  init_token_store();
108164
108219
  import_diff3 = __toESM(require_diff3(), 1);
@@ -108179,7 +108234,7 @@ var init_api = __esm(() => {
108179
108234
  init_test_access();
108180
108235
  init_generic_auth();
108181
108236
  init_diagnose();
108182
- init_publish();
108237
+ init_sync();
108183
108238
  });
108184
108239
 
108185
108240
  // ../lib/src/index.ts
@@ -108840,7 +108895,7 @@ var SUBCOMMANDS = {
108840
108895
  audit: () => Promise.resolve().then(() => (init_audit(), exports_audit)).then((m) => m.default),
108841
108896
  preflight: () => Promise.resolve().then(() => (init_preflight(), exports_preflight)).then((m) => m.default)
108842
108897
  };
108843
- var VERSION = "0.5.0-rc.2";
108898
+ var VERSION = "0.5.0-rc.4";
108844
108899
  var main = defineCommand8({
108845
108900
  meta: {
108846
108901
  name: "print-md",
@@ -102,6 +102,43 @@
102
102
  return v ? v.split('\n') : [];
103
103
  }
104
104
 
105
+ // Canonical chapter id (inline copy of lib/markdown/chapter-id.ts — this
106
+ // file is plain embedded JS and cannot import the lib): forward slashes,
107
+ // no './' prefix, no duplicate slashes.
108
+ function normId(s) {
109
+ s = String(s || '').replace(/\\/g, '/').replace(/\/{2,}/g, '/');
110
+ while (s.indexOf('./') === 0) s = s.slice(2);
111
+ return s;
112
+ }
113
+
114
+ // Every distinct chapter id present in the live view's page tags.
115
+ function liveChapterIds(d) {
116
+ var pages = d.querySelectorAll('.pagedjs_page'), ids = [], i, j;
117
+ for (i = 0; i < pages.length; i++) {
118
+ var list = pageChapters(pages[i]);
119
+ for (j = 0; j < list.length; j++) if (ids.indexOf(list[j]) === -1) ids.push(list[j]);
120
+ }
121
+ return ids;
122
+ }
123
+
124
+ // Resolve a broadcast chapter id to the tag string actually used in the
125
+ // live view. Exact match first (the normal, canonical-everywhere case);
126
+ // then normalized match, then unique-basename match — defensive layers for
127
+ // a stale book.html tagged by an older build. Returns null when the chapter
128
+ // genuinely isn't in the live view (new file, or layout we can't resolve).
129
+ function resolveChapterId(d, file) {
130
+ var ids = liveChapterIds(d), i;
131
+ if (ids.indexOf(file) !== -1) return file;
132
+ var want = normId(file), hits = [];
133
+ for (i = 0; i < ids.length; i++) if (normId(ids[i]) === want) hits.push(ids[i]);
134
+ if (hits.length === 1) return hits[0];
135
+ if (!hits.length) {
136
+ var base = want.split('/').pop();
137
+ for (i = 0; i < ids.length; i++) if (normId(ids[i]).split('/').pop() === base) hits.push(ids[i]);
138
+ }
139
+ return hits.length === 1 ? hits[0] : null;
140
+ }
141
+
105
142
  // Collect the live pages a chapter appears on: `owned` = every page that
106
143
  // contains any of it (document order); `shared` = the subset it shares with
107
144
  // another chapter (chapter starts or ends mid-page).
@@ -158,6 +195,20 @@
158
195
  function spliceChapter(file) {
159
196
  var anchor = capture(active);
160
197
  tagPages(active);
198
+ // Presence check BEFORE rendering: if the chapter can't be located in the
199
+ // live view, the splice can never succeed — go straight to the full swap
200
+ // instead of paying a wasted single-chapter render first. The log carries
201
+ // both sides of the identity so any future mismatch is self-diagnosing.
202
+ var ad0 = fdoc(active);
203
+ var liveId = ad0 ? resolveChapterId(ad0, file) : null;
204
+ if (!liveId) {
205
+ var ids = ad0 ? liveChapterIds(ad0) : [];
206
+ var detail = 'broadcast "' + file + '" matched no live data-chapter-src tag (new file?); live tags: [' + ids.join(', ') + ']';
207
+ if (window.console) console.warn('[pmd] incremental splice skipped, full swap: ' + detail);
208
+ logToServer('splice skipped (full swap) for ' + file + ': ' + detail);
209
+ swap();
210
+ return;
211
+ }
161
212
  var f = document.createElement('iframe'); f.style.visibility = 'hidden'; f.setAttribute('aria-hidden', 'true');
162
213
  f.src = '/__chapter?file=' + encodeURIComponent(file) + '&t=' + Date.now();
163
214
  f.addEventListener('load', function () {
@@ -165,7 +216,7 @@
165
216
  try {
166
217
  var ad = fdoc(active), sd = fdoc(f);
167
218
  var container = ad.querySelector('.pagedjs_pages') || ad.body;
168
- var found = pagesFor(ad, file);
219
+ var found = pagesFor(ad, liveId);
169
220
  var newPages = [].slice.call(sd.querySelectorAll('.pagedjs_page'));
170
221
  if (!newPages.length) throw new Error('chapter render produced no pages');
171
222
  if (!found.owned.length) throw new Error('chapter not present in live view (new file?)');
@@ -198,9 +249,9 @@
198
249
  for (i = 0; i < found.shared.length; i++) {
199
250
  var frags = found.shared[i].querySelectorAll('.pmd-chapter[data-chapter-src]');
200
251
  for (j = 0; j < frags.length; j++) {
201
- if (frags[j].getAttribute('data-chapter-src') === file) frags[j].parentNode.removeChild(frags[j]);
252
+ if (frags[j].getAttribute('data-chapter-src') === liveId) frags[j].parentNode.removeChild(frags[j]);
202
253
  }
203
- var rest = pageChapters(found.shared[i]).filter(function (c) { return c !== file; });
254
+ var rest = pageChapters(found.shared[i]).filter(function (c) { return c !== liveId; });
204
255
  found.shared[i].setAttribute('data-chapter-srcs', rest.join('\n'));
205
256
  if (rest.length) found.shared[i].setAttribute('data-chapter-src', rest[0]);
206
257
  else found.shared[i].removeAttribute('data-chapter-src');
@@ -209,8 +260,8 @@
209
260
 
210
261
  for (i = 0; i < newPages.length; i++) {
211
262
  var imp = ad.importNode(newPages[i], true);
212
- imp.setAttribute('data-chapter-src', file);
213
- imp.setAttribute('data-chapter-srcs', file);
263
+ imp.setAttribute('data-chapter-src', liveId);
264
+ imp.setAttribute('data-chapter-srcs', liveId);
214
265
  container.insertBefore(imp, at);
215
266
  }
216
267
  for (j = 0; j < exclusive.length; j++) exclusive[j].parentNode.removeChild(exclusive[j]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dimm-city/print-md",
3
- "version": "0.5.0-rc.2",
3
+ "version": "0.5.0-rc.4",
4
4
  "description": "Markdown-to-PDF converter for professional print layout using Paged.js and Ghostscript.",
5
5
  "author": "itlackey",
6
6
  "license": "MPL-2.0",