@repo-toolkit/confluence 0.15.0 → 0.17.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.
Files changed (4) hide show
  1. package/README.md +2 -1
  2. package/cli.js +26 -3
  3. package/index.js +26 -3
  4. package/package.json +2 -2
package/README.md CHANGED
@@ -73,7 +73,8 @@ Flags:
73
73
  - `--parent-page-id <id>` — numeric Confluence page id (required)
74
74
  - `--version-message <text>` — version-message suffix appended to every PUT
75
75
  - `--repository-url <url>` — repository URL appended to synced pages as an
76
- italic source notice. When omitted, GitHub Actions runs infer this from
76
+ italic source notice. The `--folder` path is added to the notice link when it
77
+ is relative to `--cwd`. When omitted, GitHub Actions runs infer this from
77
78
  `GITHUB_SERVER_URL` and `GITHUB_REPOSITORY`.
78
79
  - `--skip-unchanged` / `--no-skip-unchanged` — skip pages whose body is unchanged (default: `skip`)
79
80
  - `--dry-run` — walk the doc tree and validate every markdown file and local
package/cli.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
 
15
15
  // src/index.ts
16
16
  import { readFileSync as readFileSync2 } from "fs";
17
- import { dirname, isAbsolute as isAbsolute2, resolve as resolve2 } from "path";
17
+ import { dirname, isAbsolute as isAbsolute2, relative as relative3, resolve as resolve2 } from "path";
18
18
 
19
19
  // src/confluence-client.ts
20
20
  import { Buffer } from "buffer";
@@ -1688,7 +1688,8 @@ function resolveConfluenceSyncPlan(options = {}) {
1688
1688
  throw new Error(`parentPageId must be numeric, got: ${options.parentPageId}`);
1689
1689
  }
1690
1690
  }
1691
- if (options.repositoryUrl && !isAllowedUrl(options.repositoryUrl)) {
1691
+ const repositoryUrl = options.repositoryUrl ? repositoryNoticeUrl(options.repositoryUrl, cwd, options.folder ?? "") : "";
1692
+ if (repositoryUrl && !isAllowedUrl(repositoryUrl)) {
1692
1693
  throw new Error(
1693
1694
  "repositoryUrl must be an http(s), protocol-relative, server-relative, mailto, tel, or relative URL"
1694
1695
  );
@@ -1705,7 +1706,7 @@ function resolveConfluenceSyncPlan(options = {}) {
1705
1706
  skipUnchanged: options.skipUnchanged ?? true,
1706
1707
  dryRun: options.dryRun ?? false,
1707
1708
  renderHtmlBlocks: options.renderHtmlBlocks === true,
1708
- repositoryUrl: options.repositoryUrl ?? ""
1709
+ repositoryUrl
1709
1710
  };
1710
1711
  }
1711
1712
  async function syncConfluenceToDocs(options = {}) {
@@ -1909,6 +1910,28 @@ function appendRepositoryNotice(html, repositoryUrl) {
1909
1910
  const text = escapeHtml(repositoryUrl);
1910
1911
  return html + '\n<p><em>This document is synced from repository <a href="' + url + '">' + text + "</a>.</em></p>";
1911
1912
  }
1913
+ function repositoryNoticeUrl(repositoryUrl, cwd, folder) {
1914
+ const folderPath = repositoryFolderPath(cwd, folder);
1915
+ if (folderPath.length === 0) {
1916
+ return repositoryUrl;
1917
+ }
1918
+ const trimmedUrl = repositoryUrl.replace(/\/+$/, "").replace(/\.git$/, "");
1919
+ const encodedFolder = folderPath.split("/").map(encodeURIComponent).join("/");
1920
+ if (/^https?:\/\/github\.com\//i.test(trimmedUrl) && !/\/tree\/|\/blob\//.test(trimmedUrl)) {
1921
+ return trimmedUrl + "/tree/HEAD/" + encodedFolder;
1922
+ }
1923
+ return trimmedUrl + "/" + encodedFolder;
1924
+ }
1925
+ function repositoryFolderPath(cwd, folder) {
1926
+ if (folder.length === 0) {
1927
+ return "";
1928
+ }
1929
+ const relativePath = relative3(cwd, resolveInputPath(cwd, folder));
1930
+ if (relativePath === "" || relativePath.startsWith("..") || isAbsolute2(relativePath)) {
1931
+ return "";
1932
+ }
1933
+ return relativePath.split(/[\\/]+/).filter((part) => part !== "" && part !== ".").join("/");
1934
+ }
1912
1935
  async function predictBody(html, mermaidBlocks, pageId, client, ctx) {
1913
1936
  let predicted = html;
1914
1937
  if (ctx.hasMermaidBlocks) {
package/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/index.ts
2
2
  import { readFileSync as readFileSync2 } from "fs";
3
- import { dirname, isAbsolute as isAbsolute2, resolve as resolve2 } from "path";
3
+ import { dirname, isAbsolute as isAbsolute2, relative as relative3, resolve as resolve2 } from "path";
4
4
 
5
5
  // src/confluence-client.ts
6
6
  import { Buffer } from "buffer";
@@ -1675,7 +1675,8 @@ function resolveConfluenceSyncPlan(options = {}) {
1675
1675
  throw new Error(`parentPageId must be numeric, got: ${options.parentPageId}`);
1676
1676
  }
1677
1677
  }
1678
- if (options.repositoryUrl && !isAllowedUrl(options.repositoryUrl)) {
1678
+ const repositoryUrl = options.repositoryUrl ? repositoryNoticeUrl(options.repositoryUrl, cwd, options.folder ?? "") : "";
1679
+ if (repositoryUrl && !isAllowedUrl(repositoryUrl)) {
1679
1680
  throw new Error(
1680
1681
  "repositoryUrl must be an http(s), protocol-relative, server-relative, mailto, tel, or relative URL"
1681
1682
  );
@@ -1692,7 +1693,7 @@ function resolveConfluenceSyncPlan(options = {}) {
1692
1693
  skipUnchanged: options.skipUnchanged ?? true,
1693
1694
  dryRun: options.dryRun ?? false,
1694
1695
  renderHtmlBlocks: options.renderHtmlBlocks === true,
1695
- repositoryUrl: options.repositoryUrl ?? ""
1696
+ repositoryUrl
1696
1697
  };
1697
1698
  }
1698
1699
  async function syncConfluenceToDocs(options = {}) {
@@ -1896,6 +1897,28 @@ function appendRepositoryNotice(html, repositoryUrl) {
1896
1897
  const text = escapeHtml(repositoryUrl);
1897
1898
  return html + '\n<p><em>This document is synced from repository <a href="' + url + '">' + text + "</a>.</em></p>";
1898
1899
  }
1900
+ function repositoryNoticeUrl(repositoryUrl, cwd, folder) {
1901
+ const folderPath = repositoryFolderPath(cwd, folder);
1902
+ if (folderPath.length === 0) {
1903
+ return repositoryUrl;
1904
+ }
1905
+ const trimmedUrl = repositoryUrl.replace(/\/+$/, "").replace(/\.git$/, "");
1906
+ const encodedFolder = folderPath.split("/").map(encodeURIComponent).join("/");
1907
+ if (/^https?:\/\/github\.com\//i.test(trimmedUrl) && !/\/tree\/|\/blob\//.test(trimmedUrl)) {
1908
+ return trimmedUrl + "/tree/HEAD/" + encodedFolder;
1909
+ }
1910
+ return trimmedUrl + "/" + encodedFolder;
1911
+ }
1912
+ function repositoryFolderPath(cwd, folder) {
1913
+ if (folder.length === 0) {
1914
+ return "";
1915
+ }
1916
+ const relativePath = relative3(cwd, resolveInputPath(cwd, folder));
1917
+ if (relativePath === "" || relativePath.startsWith("..") || isAbsolute2(relativePath)) {
1918
+ return "";
1919
+ }
1920
+ return relativePath.split(/[\\/]+/).filter((part) => part !== "" && part !== ".").join("/");
1921
+ }
1899
1922
  async function predictBody(html, mermaidBlocks, pageId, client, ctx) {
1900
1923
  let predicted = html;
1901
1924
  if (ctx.hasMermaidBlocks) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@repo-toolkit/confluence",
3
3
  "description": "Sync a folder of markdown docs to Confluence pages and attachments (GitHub Action compatible)",
4
- "version": "0.15.0",
4
+ "version": "0.17.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "keywords": [
@@ -27,7 +27,7 @@
27
27
  "node": ">=20"
28
28
  },
29
29
  "dependencies": {
30
- "@repo-toolkit/publish-package": "0.15.0"
30
+ "@repo-toolkit/publish-package": "0.17.0"
31
31
  },
32
32
  "files": [
33
33
  "**/*",