@wonseok-han/confluence-sync 1.2.0 → 1.2.1

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/README.md CHANGED
@@ -100,8 +100,8 @@ confluence-sync --dry-run # 대상·상태(신규/변경/동일)
100
100
  confluence-sync # 변경된 문서만 갱신 (+ 신규 생성)
101
101
 
102
102
  confluence-sync --base /abs/path/docs # 동기화 루트 지정 (env 미설정 시 필수)
103
- confluence-sync guide/ # 지정 폴더만 (부모 README 자동 포함)
104
- confluence-sync guide/setup.md # 단일 문서만
103
+ confluence-sync guide/ # 지정 폴더만
104
+ confluence-sync guide/setup.md # 단일 문서만 (상위 README 는 발행되지 않음)
105
105
  confluence-sync --force # 변경 감지 무시, 전체 강제 갱신
106
106
  confluence-sync --verify # 변경 없는 문서도 페이지 존재 확인
107
107
  confluence-sync --rebuild # 매핑된 페이지 전부 삭제 후 재생성
@@ -114,6 +114,8 @@ confluence-sync --exclude '*-draft.md' # 제외 패턴 (반복 가능)
114
114
  - README 가 **없는** 폴더는 **Confluence 폴더**로 생성되고 그 아래로 문서가 들어갑니다 (`write:folder` 스코프 필요).
115
115
  - `README.md` 는 폴더 맨 앞, 나머지는 파일명순(숫자 prefix `01-`, `10-` … 로 순서 제어)으로 정렬되어 부모가 자식보다 먼저 생성됩니다.
116
116
 
117
+ > **경로를 지정해 동기화할 때**는 상위 `README.md` 가 **발행 대상이 아닙니다.** 자식을 붙일 부모 pageId 를 얻는 데만 쓰이고, 내용이 바뀌어 있어도 갱신하지 않습니다(`· 상위` 로 표시). 아직 페이지가 없으면 자식을 붙일 곳이 없으므로 그때는 생성합니다. 상위 문서도 갱신하려면 함께 지정하세요.
118
+
117
119
  ```
118
120
  docs/ → [CONFLUENCE_PARENT_ID 또는 공간 최상위]
119
121
  ├── README.md → 루트 페이지
package/dist/help.js CHANGED
@@ -40,7 +40,10 @@ ${h('동기화 옵션:')}
40
40
  ${o('-v, --version')} 버전 출력
41
41
 
42
42
  ${h('경로 인자:')}
43
- 파일/폴더를 주면 그 문서만 동기화합니다(부모 README 자동 포함).
43
+ 파일/폴더를 주면 ${bold('그 문서만')} 발행합니다.
44
+ 상위 ${o('README.md')} 는 자식을 붙일 부모 pageId 를 얻는 데만 쓰이고 내용은 갱신되지 않습니다
45
+ ${dim('(아직 페이지가 없으면 자식을 붙일 곳이 없으므로 그때는 생성됩니다).')}
46
+ 상위 문서까지 갱신하려면 함께 지정하세요.
44
47
  예) ${o('confluence-sync 20-design')} ${o('confluence-sync 90-glossary.md')}
45
48
 
46
49
  ${h('pull(역방향) 옵션:')}
package/dist/sync.js CHANGED
@@ -122,11 +122,18 @@ async function main() {
122
122
  anchorNames: anchorTargets[rel],
123
123
  bodySlugs: docs[rel]?.bodySlugs,
124
124
  });
125
- const selected = positionals.length
126
- ? withParents(resolveSelection(allRel, positionals, BASE_DIR), folderIndex)
127
- : allRel;
125
+ // 사용자가 실제로 고른 문서. 경로 인자가 없으면 전체가 대상이다.
126
+ const picked = positionals.length ? resolveSelection(allRel, positionals, BASE_DIR) : allRel;
127
+ const explicit = new Set(picked);
128
+ // 상위 README 를 함께 넣는 것은 **자식의 부모 pageId 를 얻기 위해서**다(발행 대상이 아니다).
129
+ const selected = positionals.length ? withParents(picked, folderIndex) : allRel;
128
130
  const files = sortForSync(selected);
129
- const scope = positionals.length ? `선택 ${files.length}/${allRel.length}건` : `${files.length}건`;
131
+ /** 딸려온 상위 README 인가 — 이미 페이지가 있으면 내용은 건드리지 않는다. */
132
+ const isCarriedParent = (rel) => !explicit.has(rel);
133
+ const scope = positionals.length
134
+ ? `선택 ${explicit.size}/${allRel.length}건` +
135
+ (files.length > explicit.size ? dim(` (+상위 ${files.length - explicit.size})`) : '')
136
+ : `${files.length}건`;
130
137
  const ignoredNote = ignoredCount ? dim(` (제외 ${ignoredCount})`) : '';
131
138
  console.log(`${dim('base:')} ${cyan(BASE_DIR)}\n${dim('대상:')} ${scope}${ignoredNote}`);
132
139
  // ---- 경로 헬퍼 ----
@@ -167,6 +174,12 @@ async function main() {
167
174
  const fex = mapping[`${d}/`];
168
175
  console.log(` [${fex?.pageId ? gray('폴더') : green('폴더+')}] 📁 ${d}/`);
169
176
  }
177
+ const ex0 = mapping[rel];
178
+ // 딸려온 상위 README 는 pageId 만 쓰고 지나간다 — 고르지 않은 문서를 발행하지 않는다
179
+ if (isCarriedParent(rel) && ex0?.pageId) {
180
+ console.log(` [${gray('상위')}] ${rel} ${dim(`(pageId #${ex0.pageId} 만 사용)`)}`);
181
+ continue;
182
+ }
170
183
  const { title, body, fm } = readDoc(BASE_DIR, rel);
171
184
  const r = render(rel, body, title);
172
185
  const hash = docHash(title, r);
@@ -193,7 +206,7 @@ async function main() {
193
206
  saveMapping(MAPPING_PATH, {});
194
207
  }
195
208
  const work = REBUILD ? {} : mapping;
196
- let created = 0, updated = 0, skipped = 0, recreated = 0, relinked = 0, foldersMade = 0, linked = 0;
209
+ let created = 0, updated = 0, skipped = 0, recreated = 0, relinked = 0, foldersMade = 0, linked = 0, carried = 0;
197
210
  const rendered = new Map();
198
211
  const recreatedTitles = new Set(); // 이번에 재생성된 문서의 제목
199
212
  const touched = new Set(); // 이번에 발행(생성/갱신/재생성)된 문서
@@ -243,6 +256,14 @@ async function main() {
243
256
  console.error(yellow(` ✗ 건너뜀 ${rel} (상위 폴더 미생성)`));
244
257
  continue;
245
258
  }
259
+ // 딸려온 상위 README 는 자식의 부모 pageId 를 얻으려고 들어온 것이다.
260
+ // 이미 페이지가 있으면 내용은 건드리지 않는다 — `csync <파일>` 이 고르지 않은 문서를 발행하면 안 된다.
261
+ // (페이지가 아직 없으면 자식을 붙일 곳이 없으므로 아래에서 생성한다)
262
+ if (isCarriedParent(rel) && work[rel]?.pageId) {
263
+ console.log(` ${gray('· 상위')} ${rel} ${dim(`(pageId #${work[rel].pageId} 만 사용)`)}`);
264
+ carried++;
265
+ continue;
266
+ }
246
267
  const { title, body, fm } = readDoc(BASE_DIR, rel);
247
268
  const r = render(rel, body, title);
248
269
  const hash = docHash(title, r);
@@ -332,6 +353,7 @@ async function main() {
332
353
  ` ${gray(`변경없음 ${skipped}`)}` +
333
354
  (linked ? ` ${cyan(`연결 ${linked}`)}` : '') +
334
355
  (foldersMade ? ` ${cyan(`폴더 ${foldersMade}`)}` : '') +
335
- ` ${dim(`대상 ${files.length}`)}`);
356
+ (carried ? ` ${gray(`상위 ${carried}`)}` : '') +
357
+ ` ${dim(`대상 ${files.length - carried}`)}`);
336
358
  }
337
359
  main().catch((e) => { console.error(e); process.exit(1); });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wonseok-han/confluence-sync",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "type": "module",
5
5
  "description": "Confluence Cloud ↔ Markdown 양방향 동기화 CLI — 마크다운을 Confluence 페이지로 발행(push)하고, Confluence 페이지·폴더·스페이스를 마크다운으로 가져옵니다(pull).",
6
6
  "keywords": [