gatsby-source-notion-churnotion 1.2.7 → 1.2.8
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/resolvers.js +104 -73
- package/dist/util/tableOfContent.js +55 -75
- package/package.json +1 -1
package/dist/util/resolvers.js
CHANGED
@@ -26,98 +26,129 @@ const formatTableOfContents = (tableOfContents, reporter) => {
|
|
26
26
|
entry.contextTitle = entry.title;
|
27
27
|
}
|
28
28
|
}
|
29
|
-
//
|
30
|
-
const
|
31
|
-
const
|
32
|
-
const
|
33
|
-
//
|
34
|
-
for (
|
35
|
-
|
36
|
-
h1Sections.set(entry.hash, entry);
|
37
|
-
h1Children.set(entry.hash, new Set());
|
38
|
-
}
|
29
|
+
// Build the heading hierarchy based on level and parentHash
|
30
|
+
const headingsByHash = new Map();
|
31
|
+
const childrenByParent = new Map();
|
32
|
+
const validHeadingsByLevel = new Map();
|
33
|
+
// Initialize valid headings collections for each level
|
34
|
+
for (let level = 1; level <= 3; level++) {
|
35
|
+
validHeadingsByLevel.set(level, new Set());
|
39
36
|
}
|
40
|
-
//
|
37
|
+
// First pass: Collect all headings by hash
|
41
38
|
for (const entry of sortedToc) {
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
h1Children.get(entry.parentHash).add(entry.hash);
|
47
|
-
h2Children.set(entry.hash, new Set());
|
39
|
+
headingsByHash.set(entry.hash, entry);
|
40
|
+
// Store valid headings by level
|
41
|
+
if (entry.level && entry.level >= 1 && entry.level <= 3) {
|
42
|
+
validHeadingsByLevel.get(entry.level).add(entry.hash);
|
48
43
|
}
|
49
44
|
}
|
50
|
-
//
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
h2Children.get(entry.parentHash).add(entry.hash);
|
56
|
-
}
|
45
|
+
// Second pass: Validate parent-child relationships
|
46
|
+
const invalidHashesCollectionByReason = new Map();
|
47
|
+
const addInvalidHash = (hash, reason) => {
|
48
|
+
if (!invalidHashesCollectionByReason.has(reason)) {
|
49
|
+
invalidHashesCollectionByReason.set(reason, new Set());
|
57
50
|
}
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
const duplicateCommandNames = new Set();
|
62
|
-
// 모든 섹션에서 명령어 이름 수집
|
51
|
+
invalidHashesCollectionByReason.get(reason).add(hash);
|
52
|
+
};
|
53
|
+
// Build parent-child relationship map
|
63
54
|
for (const entry of sortedToc) {
|
64
|
-
if (entry.
|
65
|
-
//
|
66
|
-
const
|
67
|
-
if (
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
55
|
+
if (entry.parentHash) {
|
56
|
+
// Check if parent exists
|
57
|
+
const parentExists = headingsByHash.has(entry.parentHash);
|
58
|
+
if (parentExists) {
|
59
|
+
// Add this entry as a child of its parent
|
60
|
+
if (!childrenByParent.has(entry.parentHash)) {
|
61
|
+
childrenByParent.set(entry.parentHash, new Set());
|
62
|
+
}
|
63
|
+
childrenByParent.get(entry.parentHash).add(entry.hash);
|
64
|
+
}
|
65
|
+
else {
|
66
|
+
// Invalid parent reference
|
67
|
+
addInvalidHash(entry.hash, "orphaned");
|
68
|
+
}
|
69
|
+
// Validate level relationships
|
70
|
+
const parent = headingsByHash.get(entry.parentHash);
|
71
|
+
if (parent && entry.level && parent.level) {
|
72
|
+
if (entry.level <= parent.level) {
|
73
|
+
// Invalid level hierarchy (e.g. H2 with H2 or H3 parent)
|
74
|
+
addInvalidHash(entry.hash, "invalid_level");
|
75
|
+
}
|
76
|
+
if (entry.level > parent.level + 1) {
|
77
|
+
// Skip levels (e.g. H1 -> H3 directly)
|
78
|
+
// This is actually valid in the new implementation, but we want to note it
|
79
|
+
reporter.info(`Found H${entry.level} heading "${entry.title}" directly under H${parent.level}`);
|
81
80
|
}
|
82
81
|
}
|
83
82
|
}
|
83
|
+
else if (entry.level && entry.level > 1) {
|
84
|
+
// No parent hash but not a top-level heading
|
85
|
+
addInvalidHash(entry.hash, "missing_parent");
|
86
|
+
}
|
84
87
|
}
|
85
|
-
//
|
88
|
+
// Identify common command names for special handling
|
89
|
+
const commonCommandPattern = /^(cp|mv|rm|find|locate|grep|curl|wget|ls|chmod|chown|ps|sed|awk|cat|service|file|stat)$/i;
|
90
|
+
// Third pass: Identify structurally invalid entries based on context
|
86
91
|
const invalidEntries = new Set();
|
87
|
-
//
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
92
|
+
// Process invalid entries from previous validations
|
93
|
+
for (const [reason, hashes] of invalidHashesCollectionByReason.entries()) {
|
94
|
+
if (reason === "orphaned" ||
|
95
|
+
reason === "invalid_level" ||
|
96
|
+
reason === "missing_parent") {
|
97
|
+
for (const hash of hashes) {
|
98
|
+
invalidEntries.add(hash);
|
99
|
+
}
|
100
|
+
}
|
101
|
+
}
|
102
|
+
// Find server/service sections
|
103
|
+
const serverSections = sortedToc.filter((entry) => entry.level === 1 &&
|
104
|
+
(entry.title.includes("서버") ||
|
105
|
+
entry.title.includes("Server") ||
|
106
|
+
entry.title.includes("프록시")));
|
107
|
+
// Process special handling for server sections
|
108
|
+
for (const serverSection of serverSections) {
|
109
|
+
// Get direct children of this server section
|
110
|
+
const directChildren = childrenByParent.get(serverSection.hash) || new Set();
|
111
|
+
// Check each child for common command pattern matching
|
112
|
+
for (const childHash of directChildren) {
|
113
|
+
const child = headingsByHash.get(childHash);
|
114
|
+
if (child && commonCommandPattern.test(child.title)) {
|
115
|
+
// Check if this child has valid descendants or content
|
116
|
+
const childHasChildren = childrenByParent.has(childHash) &&
|
117
|
+
childrenByParent.get(childHash).size > 0;
|
118
|
+
// Special check for service section headings - common commands shouldn't be here
|
119
|
+
// if they don't have meaningful content
|
120
|
+
if (!childHasChildren) {
|
121
|
+
invalidEntries.add(childHash);
|
122
|
+
// Also, mark children as invalid if there are any
|
123
|
+
if (childrenByParent.has(childHash)) {
|
124
|
+
for (const grandChildHash of childrenByParent.get(childHash)) {
|
125
|
+
invalidEntries.add(grandChildHash);
|
111
126
|
}
|
112
127
|
}
|
113
128
|
}
|
114
129
|
}
|
115
130
|
}
|
116
131
|
}
|
117
|
-
//
|
132
|
+
// Ensure the correct context titles reflect the parent relationship
|
133
|
+
for (const entry of sortedToc) {
|
134
|
+
if (entry.level === 2 && entry.parentHash) {
|
135
|
+
const parent = headingsByHash.get(entry.parentHash);
|
136
|
+
if (parent) {
|
137
|
+
entry.contextTitle = `${entry.title} (${parent.title})`;
|
138
|
+
}
|
139
|
+
}
|
140
|
+
else if (entry.level === 3 && entry.parentHash) {
|
141
|
+
// For H3, prefer its direct parent, whether H2 or H1
|
142
|
+
const parent = headingsByHash.get(entry.parentHash);
|
143
|
+
if (parent) {
|
144
|
+
entry.contextTitle = `${entry.title} (${parent.title})`;
|
145
|
+
}
|
146
|
+
}
|
147
|
+
}
|
148
|
+
// Create a filtered TOC without invalid entries
|
118
149
|
const validToc = sortedToc.filter((entry) => !invalidEntries.has(entry.hash));
|
119
150
|
if (invalidEntries.size > 0) {
|
120
|
-
reporter.info(`Removed ${invalidEntries.size} invalid TOC entries
|
151
|
+
reporter.info(`Removed ${invalidEntries.size} invalid TOC entries`);
|
121
152
|
}
|
122
153
|
return validToc;
|
123
154
|
}
|
@@ -4,104 +4,84 @@ exports.processTableOfContents = exports.resetTocContext = void 0;
|
|
4
4
|
const crypto_1 = require("crypto");
|
5
5
|
// Hash 생성 시 사용할 캐시
|
6
6
|
const hashCache = new Map();
|
7
|
-
//
|
8
|
-
|
9
|
-
let
|
10
|
-
let lastH1Title = "";
|
11
|
-
let lastH2Title = "";
|
12
|
-
let currentCommandSection = ""; // 현재 명령어 섹션 추적
|
7
|
+
// 계층별 컨텍스트 저장
|
8
|
+
const headingStack = [];
|
9
|
+
let lastHeadingByLevel = [null, null, null, null]; // 인덱스 1부터 3까지 사용
|
13
10
|
const resetTocContext = () => {
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
lastH2Title = "";
|
18
|
-
currentCommandSection = "";
|
11
|
+
headingStack.length = 0;
|
12
|
+
lastHeadingByLevel = [null, null, null, null];
|
13
|
+
hashCache.clear();
|
19
14
|
};
|
20
15
|
exports.resetTocContext = resetTocContext;
|
16
|
+
// 필요한 수준의 상위 헤딩 찾기
|
17
|
+
const findParentHeading = (currentLevel) => {
|
18
|
+
// 자신보다 낮은 레벨의 가장 가까운 헤딩 찾기 (1 < 2 < 3)
|
19
|
+
for (let level = currentLevel - 1; level > 0; level--) {
|
20
|
+
if (lastHeadingByLevel[level]) {
|
21
|
+
return lastHeadingByLevel[level];
|
22
|
+
}
|
23
|
+
}
|
24
|
+
return null;
|
25
|
+
};
|
26
|
+
const generateHash = (text) => {
|
27
|
+
return `link-${text
|
28
|
+
.replace(/[^a-zA-Z0-9가-힣\s-_]/g, "")
|
29
|
+
.trim()
|
30
|
+
.replace(/\s+/g, "-")
|
31
|
+
.toLowerCase()}-${(0, crypto_1.randomUUID)().substring(0, 4)}`;
|
32
|
+
};
|
21
33
|
const processTableOfContents = async (block, tableOfContents) => {
|
22
34
|
if (["heading_1", "heading_2", "heading_3"].includes(block.type) &&
|
23
35
|
block[block.type]?.rich_text?.length > 0) {
|
24
36
|
const plainText = block[block.type]?.rich_text?.[0]?.plain_text || "";
|
25
|
-
//
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
//
|
30
|
-
if (block.type === "heading_1") {
|
31
|
-
lastH1Title = plainText;
|
32
|
-
lastH2Title = "";
|
33
|
-
lastH2Hash = ""; // H1을 만나면 H2 해시도 초기화
|
34
|
-
currentCommandSection = ""; // 새 섹션 시작
|
35
|
-
contextualId = `h1-${plainText}`;
|
36
|
-
level = 1;
|
37
|
-
// heading_1은 최상위 레벨이므로 부모가 없음
|
38
|
-
}
|
39
|
-
else if (block.type === "heading_2") {
|
40
|
-
// H2는 반드시 H1 아래에 속함
|
41
|
-
lastH2Title = plainText;
|
42
|
-
currentCommandSection = plainText; // 현재 명령어 설정 (예: useradd, usermod 등)
|
43
|
-
contextualId = `h2-${lastH1Title}-${plainText}`;
|
44
|
-
parentHash = lastH1Hash; // heading_2의 부모는 가장 최근의 heading_1
|
45
|
-
level = 2;
|
46
|
-
}
|
47
|
-
else if (block.type === "heading_3") {
|
48
|
-
// H3 처리를 개선
|
49
|
-
const parentSectionInfo = lastH2Hash
|
50
|
-
? currentCommandSection
|
51
|
-
: lastH1Title;
|
52
|
-
const fullContextId = `h3-${lastH1Title}-${parentSectionInfo}-${plainText}`;
|
53
|
-
contextualId = fullContextId;
|
54
|
-
// 현재 H2가 없다면 바로 H1을 부모로 설정
|
55
|
-
parentHash = lastH2Hash || lastH1Hash;
|
56
|
-
// H2가 없는 경우 현재 H1 제목을 컨텍스트로 사용
|
57
|
-
if (!lastH2Hash) {
|
58
|
-
currentCommandSection = lastH1Title;
|
59
|
-
}
|
60
|
-
level = 3;
|
61
|
-
}
|
62
|
-
// 이미 처리된 플레인텍스트인지 확인
|
37
|
+
// 헤딩 레벨 추출 (heading_1 -> 1, heading_2 -> 2, heading_3 -> 3)
|
38
|
+
const level = parseInt(block.type.split("_")[1]);
|
39
|
+
// 현재 헤딩의 컨텍스트 ID 생성
|
40
|
+
const contextualId = `h${level}-${plainText}`;
|
41
|
+
// 해시 생성 또는 캐시에서 가져오기
|
63
42
|
let hash;
|
64
43
|
if (hashCache.has(contextualId)) {
|
65
44
|
hash = hashCache.get(contextualId);
|
66
45
|
}
|
67
46
|
else {
|
68
|
-
hash =
|
69
|
-
.replace(/[^a-zA-Z0-9가-힣\s-_]/g, "")
|
70
|
-
.trim()
|
71
|
-
.replace(/\s+/g, "-")
|
72
|
-
.toLowerCase()}-${(0, crypto_1.randomUUID)().substring(0, 4)}`;
|
47
|
+
hash = generateHash(plainText);
|
73
48
|
hashCache.set(contextualId, hash);
|
74
49
|
}
|
75
|
-
// 현재 해시 저장
|
76
|
-
if (block.type === "heading_1") {
|
77
|
-
lastH1Hash = hash;
|
78
|
-
}
|
79
|
-
else if (block.type === "heading_2") {
|
80
|
-
lastH2Hash = hash;
|
81
|
-
}
|
82
50
|
block.hash = hash;
|
83
|
-
//
|
84
|
-
|
85
|
-
let
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
51
|
+
// 계층 구조 관리 - 현재 레벨보다 높은 모든 하위 헤딩 컨텍스트 삭제
|
52
|
+
// 예: H1이 나오면 H2, H3 컨텍스트 초기화
|
53
|
+
for (let i = level + 1; i < lastHeadingByLevel.length; i++) {
|
54
|
+
lastHeadingByLevel[i] = null;
|
55
|
+
}
|
56
|
+
// 부모 헤딩 찾기
|
57
|
+
const parentHeading = findParentHeading(level);
|
58
|
+
const parentHash = parentHeading ? parentHeading.hash : "";
|
59
|
+
// 부모 헤딩 스택에 자식으로 등록
|
60
|
+
if (parentHeading) {
|
61
|
+
parentHeading.children.push(hash);
|
93
62
|
}
|
94
|
-
//
|
95
|
-
|
96
|
-
|
63
|
+
// 컨텍스트 제목 생성
|
64
|
+
let contextTitle = plainText;
|
65
|
+
// 부모 컨텍스트 추가
|
66
|
+
if (level > 1 && parentHeading) {
|
67
|
+
contextTitle = `${plainText} (${parentHeading.title})`;
|
97
68
|
}
|
69
|
+
// 자신을 현재 레벨의 마지막 헤딩으로 등록
|
70
|
+
const currentHeading = {
|
71
|
+
hash,
|
72
|
+
title: plainText,
|
73
|
+
level,
|
74
|
+
children: [],
|
75
|
+
};
|
76
|
+
lastHeadingByLevel[level] = currentHeading;
|
98
77
|
// 중복 체크 - 동일한 hash가 이미 존재하는지 확인
|
99
78
|
const existingTocIndex = tableOfContents.findIndex((toc) => toc.hash === hash);
|
100
79
|
if (existingTocIndex === -1) {
|
80
|
+
// TOC 항목 추가
|
101
81
|
tableOfContents.push({
|
102
82
|
type: block.type,
|
103
83
|
hash,
|
104
|
-
title:
|
84
|
+
title: plainText,
|
105
85
|
contextTitle,
|
106
86
|
parentHash,
|
107
87
|
level,
|
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.8",
|
5
5
|
"skipLibCheck": true,
|
6
6
|
"license": "0BSD",
|
7
7
|
"main": "./dist/gatsby-node.js",
|