gatsby-source-notion-churnotion 1.2.5 → 1.2.6
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/util/tableOfContent.js +24 -6
- package/dist/util/tocHelper.d.ts +4 -0
- package/dist/util/tocHelper.js +183 -7
- package/package.json +1 -1
@@ -30,12 +30,14 @@ const processTableOfContents = async (block, tableOfContents) => {
|
|
30
30
|
if (block.type === "heading_1") {
|
31
31
|
lastH1Title = plainText;
|
32
32
|
lastH2Title = "";
|
33
|
+
lastH2Hash = ""; // H1을 만나면 H2 해시도 초기화
|
33
34
|
currentCommandSection = ""; // 새 섹션 시작
|
34
35
|
contextualId = `h1-${plainText}`;
|
35
36
|
level = 1;
|
36
37
|
// heading_1은 최상위 레벨이므로 부모가 없음
|
37
38
|
}
|
38
39
|
else if (block.type === "heading_2") {
|
40
|
+
// H2는 반드시 H1 아래에 속함
|
39
41
|
lastH2Title = plainText;
|
40
42
|
currentCommandSection = plainText; // 현재 명령어 설정 (예: useradd, usermod 등)
|
41
43
|
contextualId = `h2-${lastH1Title}-${plainText}`;
|
@@ -43,10 +45,18 @@ const processTableOfContents = async (block, tableOfContents) => {
|
|
43
45
|
level = 2;
|
44
46
|
}
|
45
47
|
else if (block.type === "heading_3") {
|
46
|
-
//
|
47
|
-
const
|
48
|
+
// H3 처리를 개선
|
49
|
+
const parentSectionInfo = lastH2Hash
|
50
|
+
? currentCommandSection
|
51
|
+
: lastH1Title;
|
52
|
+
const fullContextId = `h3-${lastH1Title}-${parentSectionInfo}-${plainText}`;
|
48
53
|
contextualId = fullContextId;
|
49
|
-
|
54
|
+
// 현재 H2가 없다면 바로 H1을 부모로 설정
|
55
|
+
parentHash = lastH2Hash || lastH1Hash;
|
56
|
+
// H2가 없는 경우 현재 H1 제목을 컨텍스트로 사용
|
57
|
+
if (!lastH2Hash) {
|
58
|
+
currentCommandSection = lastH1Title;
|
59
|
+
}
|
50
60
|
level = 3;
|
51
61
|
}
|
52
62
|
// 이미 처리된 플레인텍스트인지 확인
|
@@ -73,9 +83,17 @@ const processTableOfContents = async (block, tableOfContents) => {
|
|
73
83
|
// 항상 복사본을 사용하여 중복을 방지
|
74
84
|
const displayTitle = plainText;
|
75
85
|
let contextTitle = displayTitle;
|
76
|
-
// 서브헤딩에 컨텍스트 추가
|
77
|
-
if (level === 3
|
78
|
-
|
86
|
+
// 서브헤딩에 컨텍스트 추가 (H3)
|
87
|
+
if (level === 3) {
|
88
|
+
// H2가 없으면 H1을 컨텍스트로 사용
|
89
|
+
const contextSectionName = lastH2Hash
|
90
|
+
? currentCommandSection
|
91
|
+
: lastH1Title;
|
92
|
+
contextTitle = `${plainText} (${contextSectionName})`;
|
93
|
+
}
|
94
|
+
// H2 항목도 컨텍스트 추가
|
95
|
+
else if (level === 2) {
|
96
|
+
contextTitle = `${plainText} (${lastH1Title})`;
|
79
97
|
}
|
80
98
|
// 중복 체크 - 동일한 hash가 이미 존재하는지 확인
|
81
99
|
const existingTocIndex = tableOfContents.findIndex((toc) => toc.hash === hash);
|
package/dist/util/tocHelper.d.ts
CHANGED
@@ -22,6 +22,10 @@ export declare const optimizeTocArray: (tocEntries: TocEntry[], reporter: Report
|
|
22
22
|
enhanceDuplicates?: boolean;
|
23
23
|
buildHierarchy?: boolean;
|
24
24
|
}) => TocEntry[];
|
25
|
+
/**
|
26
|
+
* TOC 구조의 유효성을 검사하고 문제가 있는 항목을 필터링
|
27
|
+
*/
|
28
|
+
export declare const validateTocStructure: (tocEntries: TocEntry[], reporter: Reporter) => TocEntry[];
|
25
29
|
/**
|
26
30
|
* Enhances TOC entries with contextual titles to disambiguate duplicate titles
|
27
31
|
*/
|
package/dist/util/tocHelper.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
exports.enrichDuplicateTitles = exports.optimizeTocArray = void 0;
|
3
|
+
exports.enrichDuplicateTitles = exports.validateTocStructure = exports.optimizeTocArray = void 0;
|
4
4
|
/**
|
5
5
|
* Utility function to efficiently handle large table of contents arrays
|
6
6
|
* by removing duplicates and optionally limiting size while preserving hierarchy
|
@@ -33,20 +33,95 @@ const optimizeTocArray = (tocEntries, reporter, options = {}) => {
|
|
33
33
|
}
|
34
34
|
return entry;
|
35
35
|
});
|
36
|
+
// 구조적 유효성 검증 및 정리
|
37
|
+
const validatedToc = (0, exports.validateTocStructure)(enrichedTocEntries, reporter);
|
36
38
|
// Remove duplicates if requested
|
37
|
-
let processedToc =
|
39
|
+
let processedToc = validatedToc;
|
38
40
|
if (removeDuplicates) {
|
39
41
|
const startTime = Date.now();
|
42
|
+
// 중복 해시 및 타이틀 검사를 위한 맵
|
40
43
|
const uniqueMap = new Map();
|
41
|
-
//
|
42
|
-
|
43
|
-
|
44
|
+
// 섹션별 유효한 하위 항목 분류
|
45
|
+
const h1Entries = new Map(); // 최상위 항목
|
46
|
+
const childEntries = new Map(); // 상위 해시 -> 하위 해시 세트
|
47
|
+
// 1. 먼저 모든 H1 항목을 수집하고 맵에 저장
|
48
|
+
for (const entry of validatedToc) {
|
49
|
+
if (entry.level === 1) {
|
50
|
+
h1Entries.set(entry.hash, entry);
|
51
|
+
childEntries.set(entry.hash, new Set());
|
52
|
+
}
|
53
|
+
}
|
54
|
+
// 2. 각 항목을 처리하여 해시 기반으로 적절한 부모-자식 관계 검증
|
55
|
+
for (const entry of validatedToc) {
|
56
|
+
// H1은 이미 처리됨
|
57
|
+
if (entry.level === 1) {
|
58
|
+
uniqueMap.set(entry.hash, entry);
|
59
|
+
continue;
|
60
|
+
}
|
61
|
+
// H2, H3 항목 처리
|
62
|
+
if (entry.parentHash) {
|
63
|
+
// 부모 해시가 있는 경우 부모가 실제로 존재하는지 확인
|
64
|
+
const parentExists = uniqueMap.has(entry.parentHash) || h1Entries.has(entry.parentHash);
|
65
|
+
if (parentExists) {
|
66
|
+
// 부모에 이 항목을 자식으로 추가
|
67
|
+
if (childEntries.has(entry.parentHash)) {
|
68
|
+
childEntries.get(entry.parentHash).add(entry.hash);
|
69
|
+
}
|
70
|
+
else {
|
71
|
+
childEntries.set(entry.parentHash, new Set([entry.hash]));
|
72
|
+
}
|
73
|
+
// 항목을 유효한 것으로 추가
|
74
|
+
uniqueMap.set(entry.hash, entry);
|
75
|
+
}
|
76
|
+
else {
|
77
|
+
// 부모가 존재하지 않는 고아 항목
|
78
|
+
reporter.warn(`Skipping orphaned TOC entry: ${entry.title} (parent hash ${entry.parentHash} not found)`);
|
79
|
+
}
|
80
|
+
}
|
81
|
+
else if (entry.level === 2) {
|
82
|
+
// H2 항목이지만 부모 해시가 없는 경우 (비정상)
|
83
|
+
reporter.warn(`Skipping H2 entry without parent: ${entry.title}`);
|
84
|
+
}
|
85
|
+
else {
|
86
|
+
// 부모 해시가 없지만 유효할 수 있는 항목
|
87
|
+
uniqueMap.set(entry.hash, entry);
|
88
|
+
}
|
44
89
|
}
|
90
|
+
// 내부 일관성 검사: 동일한 제목의 항목이 여러 섹션에 있는 경우
|
91
|
+
const titleToSections = new Map();
|
92
|
+
for (const entry of uniqueMap.values()) {
|
93
|
+
if (entry.level === 2) {
|
94
|
+
const title = entry.title.toLowerCase();
|
95
|
+
if (!titleToSections.has(title)) {
|
96
|
+
titleToSections.set(title, new Set());
|
97
|
+
}
|
98
|
+
// 이 제목이 속한 부모 H1 추적
|
99
|
+
if (entry.parentHash) {
|
100
|
+
titleToSections.get(title).add(entry.parentHash);
|
101
|
+
}
|
102
|
+
}
|
103
|
+
}
|
104
|
+
// 여러 섹션에 동일한 제목의 H2가 있는지 확인하고, 영향받는 H2의 contextTitle을 더 강화
|
105
|
+
for (const [title, sections] of titleToSections.entries()) {
|
106
|
+
if (sections.size > 1) {
|
107
|
+
reporter.info(`Title "${title}" appears across ${sections.size} different sections. Enhancing context.`);
|
108
|
+
// 해당 제목의 모든 항목에 대해 상위 항목의 제목을 컨텍스트에 추가
|
109
|
+
for (const entry of uniqueMap.values()) {
|
110
|
+
if (entry.title.toLowerCase() === title && entry.parentHash) {
|
111
|
+
const parentEntry = h1Entries.get(entry.parentHash);
|
112
|
+
if (parentEntry) {
|
113
|
+
entry.contextTitle = `${entry.title} (${parentEntry.title})`;
|
114
|
+
}
|
115
|
+
}
|
116
|
+
}
|
117
|
+
}
|
118
|
+
}
|
119
|
+
// 최종 결과를 배열로 변환
|
45
120
|
processedToc = Array.from(uniqueMap.values());
|
46
|
-
const removedCount =
|
121
|
+
const removedCount = validatedToc.length - processedToc.length;
|
47
122
|
const processTime = Date.now() - startTime;
|
48
123
|
if (removedCount > 0) {
|
49
|
-
reporter.info(`Removed ${removedCount} duplicate TOC entries in ${processTime}ms.`);
|
124
|
+
reporter.info(`Removed ${removedCount} duplicate/invalid TOC entries in ${processTime}ms.`);
|
50
125
|
}
|
51
126
|
}
|
52
127
|
// Add context to duplicate title entries for better display
|
@@ -76,6 +151,107 @@ const optimizeTocArray = (tocEntries, reporter, options = {}) => {
|
|
76
151
|
return processedToc;
|
77
152
|
};
|
78
153
|
exports.optimizeTocArray = optimizeTocArray;
|
154
|
+
/**
|
155
|
+
* TOC 구조의 유효성을 검사하고 문제가 있는 항목을 필터링
|
156
|
+
*/
|
157
|
+
const validateTocStructure = (tocEntries, reporter) => {
|
158
|
+
if (!tocEntries || tocEntries.length === 0) {
|
159
|
+
return [];
|
160
|
+
}
|
161
|
+
// 1. 섹션별 매핑 구성
|
162
|
+
const h1Map = new Map();
|
163
|
+
const h1Children = new Map();
|
164
|
+
// 2. 모든 H1 항목 수집
|
165
|
+
for (const entry of tocEntries) {
|
166
|
+
if (entry.level === 1) {
|
167
|
+
h1Map.set(entry.hash, entry);
|
168
|
+
h1Children.set(entry.hash, new Set());
|
169
|
+
}
|
170
|
+
}
|
171
|
+
// H1이 없으면 원본 반환
|
172
|
+
if (h1Map.size === 0) {
|
173
|
+
return tocEntries;
|
174
|
+
}
|
175
|
+
// 3. 각 항목을 올바른 부모에 연결
|
176
|
+
for (const entry of tocEntries) {
|
177
|
+
if (entry.level !== 1 && entry.parentHash) {
|
178
|
+
// 부모가 H1인 경우
|
179
|
+
if (h1Map.has(entry.parentHash)) {
|
180
|
+
h1Children.get(entry.parentHash).add(entry.hash);
|
181
|
+
}
|
182
|
+
}
|
183
|
+
}
|
184
|
+
// 4. 각 H1 섹션 내에서 중복된 H2/H3 타이틀 발견 (같은 섹션에 동일 타이틀 중복)
|
185
|
+
const suspiciousEntries = new Set();
|
186
|
+
const sectionTitles = new Map();
|
187
|
+
for (const [h1Hash, childHashes] of h1Children.entries()) {
|
188
|
+
const titleCounts = new Map();
|
189
|
+
sectionTitles.set(h1Hash, titleCounts);
|
190
|
+
// 이 H1에 속한 모든 자식 항목 검사
|
191
|
+
for (const entry of tocEntries) {
|
192
|
+
if ((entry.level === 2 || entry.level === 3) &&
|
193
|
+
entry.parentHash === h1Hash) {
|
194
|
+
const title = entry.title.toLowerCase();
|
195
|
+
const count = (titleCounts.get(title) || 0) + 1;
|
196
|
+
titleCounts.set(title, count);
|
197
|
+
// 이 섹션에 이 제목이 이미 있으면 중복으로 표시
|
198
|
+
if (count > 1) {
|
199
|
+
suspiciousEntries.add(entry.hash);
|
200
|
+
}
|
201
|
+
}
|
202
|
+
}
|
203
|
+
}
|
204
|
+
// 5. 다른 섹션에 속할 것으로 보이는 항목 식별 (예: 14. Squid 프록시 서버의 잘못된 자식)
|
205
|
+
const titleToSections = new Map();
|
206
|
+
for (const entry of tocEntries) {
|
207
|
+
if (entry.level === 2) {
|
208
|
+
const title = entry.title.toLowerCase();
|
209
|
+
if (!titleToSections.has(title)) {
|
210
|
+
titleToSections.set(title, new Set());
|
211
|
+
}
|
212
|
+
// 이 제목이 어떤 H1 섹션에 속하는지 기록
|
213
|
+
if (entry.parentHash) {
|
214
|
+
titleToSections.get(title).add(entry.parentHash);
|
215
|
+
}
|
216
|
+
}
|
217
|
+
}
|
218
|
+
// 여러 섹션에 동일한 제목의 항목이 있는지 검사
|
219
|
+
for (const [title, sections] of titleToSections.entries()) {
|
220
|
+
if (sections.size > 1) {
|
221
|
+
reporter.info(`Title "${title}" appears across ${sections.size} different sections.`);
|
222
|
+
// 일반적인 명령어 이름인지 확인 (cp, mv, rm 등)
|
223
|
+
const isCommonCommand = /^(cp|mv|rm|find|grep|curl|wget|ls|chmod|chown|ps|sed|awk|cat|service|file|locate|stat)$/.test(title);
|
224
|
+
if (isCommonCommand) {
|
225
|
+
reporter.info(`"${title}" is a common command name that appears in multiple sections.`);
|
226
|
+
// 각 섹션의 항목 분석
|
227
|
+
for (const entry of tocEntries) {
|
228
|
+
if (entry.title.toLowerCase() === title && entry.level === 2) {
|
229
|
+
// 이 H2 항목이 이 섹션의 내용과 일치하는지 확인할 방법이 필요
|
230
|
+
// 예: 이 H2와 연결된 H3 항목이 정당한지 검사
|
231
|
+
let hasValidChildren = false;
|
232
|
+
for (const child of tocEntries) {
|
233
|
+
if (child.level === 3 && child.parentHash === entry.hash) {
|
234
|
+
hasValidChildren = true;
|
235
|
+
break;
|
236
|
+
}
|
237
|
+
}
|
238
|
+
// 자식이 없는 중복된 명령어는 다른 섹션에서 복사된 것일 가능성이 높음
|
239
|
+
if (!hasValidChildren && sections.size > 1) {
|
240
|
+
suspiciousEntries.add(entry.hash);
|
241
|
+
}
|
242
|
+
}
|
243
|
+
}
|
244
|
+
}
|
245
|
+
}
|
246
|
+
}
|
247
|
+
// 6. 의심스러운 항목 수 보고
|
248
|
+
if (suspiciousEntries.size > 0) {
|
249
|
+
reporter.info(`Found ${suspiciousEntries.size} suspicious TOC entries that might be duplicates`);
|
250
|
+
}
|
251
|
+
// 7. 의심스러운 항목 제거된 결과 반환
|
252
|
+
return tocEntries.filter((entry) => !suspiciousEntries.has(entry.hash));
|
253
|
+
};
|
254
|
+
exports.validateTocStructure = validateTocStructure;
|
79
255
|
/**
|
80
256
|
* Enhances TOC entries with contextual titles to disambiguate duplicate titles
|
81
257
|
*/
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "gatsby-source-notion-churnotion",
|
3
3
|
"description": "Gatsby plugin that can connect with One Notion Database RECURSIVELY using official API",
|
4
|
-
"version": "1.2.
|
4
|
+
"version": "1.2.6",
|
5
5
|
"skipLibCheck": true,
|
6
6
|
"license": "0BSD",
|
7
7
|
"main": "./dist/gatsby-node.js",
|