@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 +4 -2
- package/dist/help.js +4 -1
- package/dist/sync.js +28 -6
- package/package.json +1 -1
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/ # 지정 폴더만
|
|
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
|
-
파일/폴더를 주면 그 문서만
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
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
|
-
` ${
|
|
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