gatsby-source-notion-churnotion 1.2.3 → 1.2.4

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.
@@ -133,7 +133,7 @@ const createSchemaCustomization = ({ actions, schema }) => {
133
133
  version: Int
134
134
  description: String
135
135
  slug: String
136
- tableOfContents: [JSON]
136
+ tableOfContents: [TocEntry]
137
137
  category_list: [${constants_1.NODE_TYPE.Category}]
138
138
  url: String!
139
139
  thumbnail: File @link(by: "id", from: "thumbnail")
@@ -160,6 +160,15 @@ const createSchemaCustomization = ({ actions, schema }) => {
160
160
  type ${constants_1.NODE_TYPE.RelatedPost} implements Node {
161
161
  posts: [${constants_1.NODE_TYPE.Post}] @link(by: "id")
162
162
  }
163
+
164
+ type TocEntry {
165
+ type: String!
166
+ hash: String!
167
+ title: String!
168
+ parentHash: String
169
+ level: Int
170
+ contextTitle: String
171
+ }
163
172
  `,
164
173
  ]);
165
174
  };
@@ -4,6 +4,7 @@ exports.processor = void 0;
4
4
  const metadataProcessor_1 = require("./metadataProcessor");
5
5
  const blocks_1 = require("./blocks");
6
6
  const tocHelper_1 = require("./tocHelper");
7
+ const tableOfContent_1 = require("./tableOfContent");
7
8
  const processor = async (blocks, actions, getCache, createNodeId, reporter, cache) => {
8
9
  const { thumbnail, tableOfContents, updatedBlocks, rawText } = await processBlocksForContent(blocks, actions, getCache, createNodeId, reporter, cache);
9
10
  await (0, metadataProcessor_1.processMetadata)(blocks, actions, createNodeId, reporter, cache);
@@ -18,6 +19,8 @@ const processBlocksForContent = async (blocks, actions, getCache, createNodeId,
18
19
  reporter,
19
20
  cache,
20
21
  };
22
+ // 목차 컨텍스트 초기화
23
+ (0, tableOfContent_1.resetTocContext)();
21
24
  // 블록 프로세서 레지스트리 생성
22
25
  const processorRegistry = new blocks_1.BlockProcessorRegistry(context);
23
26
  const tableOfContents = [];
@@ -1,3 +1,4 @@
1
1
  import { BaseContentBlock } from "notion-types";
2
2
  import { TocEntry } from "./tocHelper";
3
+ export declare const resetTocContext: () => void;
3
4
  export declare const processTableOfContents: (block: BaseContentBlock, tableOfContents: TocEntry[]) => Promise<void>;
@@ -1,17 +1,52 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.processTableOfContents = void 0;
3
+ exports.processTableOfContents = exports.resetTocContext = void 0;
4
4
  const crypto_1 = require("crypto");
5
5
  // Hash 생성 시 사용할 캐시
6
6
  const hashCache = new Map();
7
+ // 헤딩 레벨 트래킹을 위한 변수들
8
+ let lastH1Hash = "";
9
+ let lastH2Hash = "";
10
+ let lastH1Title = "";
11
+ let lastH2Title = "";
12
+ const resetTocContext = () => {
13
+ lastH1Hash = "";
14
+ lastH2Hash = "";
15
+ lastH1Title = "";
16
+ lastH2Title = "";
17
+ };
18
+ exports.resetTocContext = resetTocContext;
7
19
  const processTableOfContents = async (block, tableOfContents) => {
8
20
  if (["heading_1", "heading_2", "heading_3"].includes(block.type) &&
9
21
  block[block.type]?.rich_text?.length > 0) {
10
22
  const plainText = block[block.type]?.rich_text?.[0]?.plain_text || "";
23
+ // 부모 컨텍스트 추가
24
+ let contextualId = plainText;
25
+ let parentHash = "";
26
+ let level = 0;
27
+ // 상위 헤딩에 따라 컨텍스트 저장
28
+ if (block.type === "heading_1") {
29
+ lastH1Title = plainText;
30
+ lastH2Title = "";
31
+ contextualId = `h1-${plainText}`;
32
+ level = 1;
33
+ // heading_1은 최상위 레벨이므로 부모가 없음
34
+ }
35
+ else if (block.type === "heading_2") {
36
+ lastH2Title = plainText;
37
+ contextualId = `h2-${lastH1Title}-${plainText}`;
38
+ parentHash = lastH1Hash; // heading_2의 부모는 가장 최근의 heading_1
39
+ level = 2;
40
+ }
41
+ else if (block.type === "heading_3") {
42
+ contextualId = `h3-${lastH1Title}-${lastH2Title}-${plainText}`;
43
+ parentHash = lastH2Hash || lastH1Hash; // heading_3의 부모는 가장 최근의 heading_2, 없으면 heading_1
44
+ level = 3;
45
+ }
11
46
  // 이미 처리된 플레인텍스트인지 확인
12
47
  let hash;
13
- if (hashCache.has(plainText)) {
14
- hash = hashCache.get(plainText);
48
+ if (hashCache.has(contextualId)) {
49
+ hash = hashCache.get(contextualId);
15
50
  }
16
51
  else {
17
52
  hash = `link-${plainText
@@ -19,7 +54,14 @@ const processTableOfContents = async (block, tableOfContents) => {
19
54
  .trim()
20
55
  .replace(/\s+/g, "-")
21
56
  .toLowerCase()}-${(0, crypto_1.randomUUID)().substring(0, 4)}`;
22
- hashCache.set(plainText, hash);
57
+ hashCache.set(contextualId, hash);
58
+ }
59
+ // 현재 해시 저장
60
+ if (block.type === "heading_1") {
61
+ lastH1Hash = hash;
62
+ }
63
+ else if (block.type === "heading_2") {
64
+ lastH2Hash = hash;
23
65
  }
24
66
  block.hash = hash;
25
67
  // 중복 체크 - 동일한 hash가 이미 존재하는지 확인
@@ -29,6 +71,8 @@ const processTableOfContents = async (block, tableOfContents) => {
29
71
  type: block.type,
30
72
  hash,
31
73
  title: plainText,
74
+ parentHash,
75
+ level,
32
76
  });
33
77
  }
34
78
  }
@@ -6,13 +6,22 @@ export interface TocEntry {
6
6
  type: string;
7
7
  hash: string;
8
8
  title: string;
9
+ parentHash?: string;
10
+ level?: number;
11
+ contextTitle?: string;
9
12
  }
10
13
  /**
11
14
  * Utility function to efficiently handle large table of contents arrays
12
- * by removing duplicates and optionally limiting size
15
+ * by removing duplicates and optionally limiting size while preserving hierarchy
13
16
  */
14
17
  export declare const optimizeTocArray: (tocEntries: TocEntry[], reporter: Reporter, options?: {
15
18
  maxSize?: number;
16
19
  warnThreshold?: number;
17
20
  removeDuplicates?: boolean;
21
+ structureByLevel?: boolean;
22
+ enhanceDuplicates?: boolean;
18
23
  }) => TocEntry[];
24
+ /**
25
+ * Enhances TOC entries with contextual titles to disambiguate duplicate titles
26
+ */
27
+ export declare const enrichDuplicateTitles: (tocEntries: TocEntry[], reporter: Reporter) => void;
@@ -1,14 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.optimizeTocArray = void 0;
3
+ exports.enrichDuplicateTitles = exports.optimizeTocArray = void 0;
4
4
  /**
5
5
  * Utility function to efficiently handle large table of contents arrays
6
- * by removing duplicates and optionally limiting size
6
+ * by removing duplicates and optionally limiting size while preserving hierarchy
7
7
  */
8
8
  const optimizeTocArray = (tocEntries, reporter, options = {}) => {
9
9
  const { maxSize = 1000, // Maximum entries to include
10
10
  warnThreshold = 300, // Threshold to issue warning
11
11
  removeDuplicates = true, // Whether to remove duplicates
12
+ structureByLevel = true, // Whether to structure by heading level
13
+ enhanceDuplicates = true, // Whether to enhance duplicate titles with context
12
14
  } = options;
13
15
  if (!tocEntries || tocEntries.length === 0) {
14
16
  return [];
@@ -17,17 +19,40 @@ const optimizeTocArray = (tocEntries, reporter, options = {}) => {
17
19
  if (tocEntries.length > warnThreshold) {
18
20
  reporter.warn(`Large table of contents detected (${tocEntries.length} items). This might affect performance.`);
19
21
  }
22
+ // Enrich TOC entries with level information if not present
23
+ const enrichedTocEntries = tocEntries.map((entry) => {
24
+ if (!entry.level) {
25
+ const level = entry.type === "heading_1"
26
+ ? 1
27
+ : entry.type === "heading_2"
28
+ ? 2
29
+ : entry.type === "heading_3"
30
+ ? 3
31
+ : 0;
32
+ return { ...entry, level };
33
+ }
34
+ return entry;
35
+ });
36
+ // Sort by appearance (using array index as proxy) and then by level
37
+ enrichedTocEntries.sort((a, b) => {
38
+ // Sort by level to ensure headers are properly nested
39
+ return (a.level || 0) - (b.level || 0);
40
+ });
41
+ // Add context to duplicate title entries for better display
42
+ if (enhanceDuplicates) {
43
+ (0, exports.enrichDuplicateTitles)(enrichedTocEntries, reporter);
44
+ }
20
45
  // Remove duplicates if requested
21
- let processedToc = tocEntries;
46
+ let processedToc = enrichedTocEntries;
22
47
  if (removeDuplicates) {
23
48
  const startTime = Date.now();
24
49
  const uniqueMap = new Map();
25
- // Use a map for faster duplicate removal - O(n) instead of O(n²)
26
- for (const entry of tocEntries) {
50
+ // Use the hash to ensure uniqueness, but preserve context when display titles are the same
51
+ for (const entry of enrichedTocEntries) {
27
52
  uniqueMap.set(entry.hash, entry);
28
53
  }
29
54
  processedToc = Array.from(uniqueMap.values());
30
- const removedCount = tocEntries.length - processedToc.length;
55
+ const removedCount = enrichedTocEntries.length - processedToc.length;
31
56
  const processTime = Date.now() - startTime;
32
57
  if (removedCount > 0) {
33
58
  reporter.info(`Removed ${removedCount} duplicate TOC entries in ${processTime}ms.`);
@@ -41,3 +66,49 @@ const optimizeTocArray = (tocEntries, reporter, options = {}) => {
41
66
  return processedToc;
42
67
  };
43
68
  exports.optimizeTocArray = optimizeTocArray;
69
+ /**
70
+ * Enhances TOC entries with contextual titles to disambiguate duplicate titles
71
+ */
72
+ const enrichDuplicateTitles = (tocEntries, reporter) => {
73
+ if (!tocEntries || tocEntries.length === 0)
74
+ return;
75
+ // Find duplicate titles
76
+ const titleCounts = new Map();
77
+ const duplicateTitles = new Set();
78
+ // Count occurrences of each title
79
+ for (const entry of tocEntries) {
80
+ const count = (titleCounts.get(entry.title) || 0) + 1;
81
+ titleCounts.set(entry.title, count);
82
+ if (count > 1) {
83
+ duplicateTitles.add(entry.title);
84
+ }
85
+ }
86
+ if (duplicateTitles.size === 0)
87
+ return;
88
+ reporter.info(`Found ${duplicateTitles.size} duplicate title(s) in TOC. Adding context...`);
89
+ // Build parent map for fast lookups
90
+ const entryMap = new Map();
91
+ for (const entry of tocEntries) {
92
+ entryMap.set(entry.hash, entry);
93
+ }
94
+ // Create a hierarchical map to find parents
95
+ for (const entry of tocEntries) {
96
+ if (duplicateTitles.has(entry.title)) {
97
+ let context = "";
98
+ let parentEntry;
99
+ // Find parent context
100
+ if (entry.parentHash && entryMap.has(entry.parentHash)) {
101
+ parentEntry = entryMap.get(entry.parentHash);
102
+ context = parentEntry?.title || "";
103
+ }
104
+ // Add context to title
105
+ if (context) {
106
+ entry.contextTitle = `${entry.title} (${context})`;
107
+ }
108
+ else {
109
+ entry.contextTitle = entry.title;
110
+ }
111
+ }
112
+ }
113
+ };
114
+ exports.enrichDuplicateTitles = enrichDuplicateTitles;
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.3",
4
+ "version": "1.2.4",
5
5
  "skipLibCheck": true,
6
6
  "license": "0BSD",
7
7
  "main": "./dist/gatsby-node.js",