gatsby-source-notion-churnotion 1.1.28 → 1.1.29
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/api/getPages.js +229 -214
- package/dist/api/service/index.d.ts +1 -0
- package/dist/api/service/index.js +17 -0
- package/dist/api/service/notionService.d.ts +37 -0
- package/dist/api/service/notionService.js +105 -0
- package/dist/util/blocks/blockProcessor.d.ts +35 -0
- package/dist/util/blocks/blockProcessor.js +44 -0
- package/dist/util/blocks/blockProcessorRegistry.d.ts +10 -0
- package/dist/util/blocks/blockProcessorRegistry.js +40 -0
- package/dist/util/blocks/imageBlockProcessor.d.ts +6 -0
- package/dist/util/blocks/imageBlockProcessor.js +113 -0
- package/dist/util/blocks/index.d.ts +6 -0
- package/dist/util/blocks/index.js +22 -0
- package/dist/util/blocks/mediaBlockProcessor.d.ts +6 -0
- package/dist/util/blocks/mediaBlockProcessor.js +48 -0
- package/dist/util/blocks/structureBlockProcessor.d.ts +6 -0
- package/dist/util/blocks/structureBlockProcessor.js +81 -0
- package/dist/util/blocks/textBlockProcessor.d.ts +6 -0
- package/dist/util/blocks/textBlockProcessor.js +23 -0
- package/dist/util/processor.js +35 -149
- package/dist/util/timeLimit.d.ts +8 -0
- package/dist/util/timeLimit.js +25 -1
- package/package.json +2 -1
@@ -0,0 +1,35 @@
|
|
1
|
+
import { BaseContentBlock } from "notion-types";
|
2
|
+
import { Actions, GatsbyCache, Reporter } from "gatsby";
|
3
|
+
import { CustomImageBlock } from "../../types";
|
4
|
+
export interface BlockProcessorContext {
|
5
|
+
actions: Actions;
|
6
|
+
getCache: (this: void, id: string) => GatsbyCache;
|
7
|
+
createNodeId: (this: void, input: string) => string;
|
8
|
+
reporter: Reporter;
|
9
|
+
cache: GatsbyCache;
|
10
|
+
}
|
11
|
+
export interface ProcessBlockResult {
|
12
|
+
thumbnail?: string | null;
|
13
|
+
plainText?: string;
|
14
|
+
updatedBlock?: BaseContentBlock;
|
15
|
+
tableOfContents?: {
|
16
|
+
type: string;
|
17
|
+
hash: string;
|
18
|
+
title: string;
|
19
|
+
}[];
|
20
|
+
}
|
21
|
+
export declare abstract class BlockProcessor {
|
22
|
+
protected context: BlockProcessorContext;
|
23
|
+
constructor(context: BlockProcessorContext);
|
24
|
+
abstract canProcess(block: BaseContentBlock): boolean;
|
25
|
+
abstract process(block: BaseContentBlock): Promise<ProcessBlockResult>;
|
26
|
+
protected extractPlainText(block: BaseContentBlock): string | null;
|
27
|
+
protected isTextContentBlock(block: BaseContentBlock): block is BaseContentBlock & {
|
28
|
+
[key in "paragraph" | "heading_1" | "heading_2" | "heading_3" | "quote" | "bulleted_list_item" | "numbered_list_item" | "callout" | "code" | "toggle" | "to_do" | "bookmark" | "table_of_contents" | "breadcrumb" | "divider" | "embed"]?: {
|
29
|
+
rich_text: {
|
30
|
+
plain_text: string;
|
31
|
+
}[];
|
32
|
+
};
|
33
|
+
};
|
34
|
+
protected isImageBlock(block: BaseContentBlock): block is CustomImageBlock;
|
35
|
+
}
|
@@ -0,0 +1,44 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.BlockProcessor = void 0;
|
4
|
+
class BlockProcessor {
|
5
|
+
context;
|
6
|
+
constructor(context) {
|
7
|
+
this.context = context;
|
8
|
+
}
|
9
|
+
extractPlainText(block) {
|
10
|
+
if (this.isTextContentBlock(block)) {
|
11
|
+
const richTextArray = block[block.type]?.rich_text || [];
|
12
|
+
return richTextArray
|
13
|
+
.map((text) => block.type === "code" // code의 \n 제거
|
14
|
+
? text.plain_text.replace(/\\n/g, "")
|
15
|
+
: text.plain_text)
|
16
|
+
.join(" ");
|
17
|
+
}
|
18
|
+
return null;
|
19
|
+
}
|
20
|
+
isTextContentBlock(block) {
|
21
|
+
return [
|
22
|
+
"paragraph",
|
23
|
+
"heading_1",
|
24
|
+
"heading_2",
|
25
|
+
"heading_3",
|
26
|
+
"quote",
|
27
|
+
"bulleted_list_item",
|
28
|
+
"numbered_list_item",
|
29
|
+
"callout",
|
30
|
+
"code",
|
31
|
+
"toggle",
|
32
|
+
"to_do",
|
33
|
+
"bookmark",
|
34
|
+
"table_of_contents",
|
35
|
+
"breadcrumb",
|
36
|
+
"divider",
|
37
|
+
"embed",
|
38
|
+
].includes(block.type);
|
39
|
+
}
|
40
|
+
isImageBlock(block) {
|
41
|
+
return block.type === "image" && "image" in block;
|
42
|
+
}
|
43
|
+
}
|
44
|
+
exports.BlockProcessor = BlockProcessor;
|
@@ -0,0 +1,10 @@
|
|
1
|
+
import { BaseContentBlock } from "notion-types";
|
2
|
+
import { BlockProcessor, BlockProcessorContext, ProcessBlockResult } from "./blockProcessor";
|
3
|
+
export declare class BlockProcessorRegistry {
|
4
|
+
private processors;
|
5
|
+
private context;
|
6
|
+
constructor(context: BlockProcessorContext);
|
7
|
+
private registerDefaultProcessors;
|
8
|
+
registerProcessor(processor: BlockProcessor): void;
|
9
|
+
processBlock(block: BaseContentBlock): Promise<ProcessBlockResult>;
|
10
|
+
}
|
@@ -0,0 +1,40 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.BlockProcessorRegistry = void 0;
|
4
|
+
const textBlockProcessor_1 = require("./textBlockProcessor");
|
5
|
+
const imageBlockProcessor_1 = require("./imageBlockProcessor");
|
6
|
+
const mediaBlockProcessor_1 = require("./mediaBlockProcessor");
|
7
|
+
const structureBlockProcessor_1 = require("./structureBlockProcessor");
|
8
|
+
class BlockProcessorRegistry {
|
9
|
+
processors = [];
|
10
|
+
context;
|
11
|
+
constructor(context) {
|
12
|
+
this.context = context;
|
13
|
+
this.registerDefaultProcessors();
|
14
|
+
}
|
15
|
+
registerDefaultProcessors() {
|
16
|
+
// 기본 프로세서 등록
|
17
|
+
this.registerProcessor(new textBlockProcessor_1.TextBlockProcessor(this.context));
|
18
|
+
this.registerProcessor(new imageBlockProcessor_1.ImageBlockProcessor(this.context));
|
19
|
+
this.registerProcessor(new mediaBlockProcessor_1.MediaBlockProcessor(this.context));
|
20
|
+
this.registerProcessor(new structureBlockProcessor_1.StructureBlockProcessor(this.context));
|
21
|
+
}
|
22
|
+
registerProcessor(processor) {
|
23
|
+
this.processors.push(processor);
|
24
|
+
}
|
25
|
+
async processBlock(block) {
|
26
|
+
for (const processor of this.processors) {
|
27
|
+
if (processor.canProcess(block)) {
|
28
|
+
return await processor.process(block);
|
29
|
+
}
|
30
|
+
}
|
31
|
+
// 어떤 프로세서도 처리할 수 없는 경우 (디버깅을 위해 타입 로그)
|
32
|
+
this.context.reporter.warn(`Unsupported block type: ${block.type}`);
|
33
|
+
// 기본 결과 반환
|
34
|
+
return {
|
35
|
+
updatedBlock: block,
|
36
|
+
plainText: "",
|
37
|
+
};
|
38
|
+
}
|
39
|
+
}
|
40
|
+
exports.BlockProcessorRegistry = BlockProcessorRegistry;
|
@@ -0,0 +1,6 @@
|
|
1
|
+
import { BaseContentBlock } from "notion-types";
|
2
|
+
import { BlockProcessor, ProcessBlockResult } from "./blockProcessor";
|
3
|
+
export declare class ImageBlockProcessor extends BlockProcessor {
|
4
|
+
canProcess(block: BaseContentBlock): boolean;
|
5
|
+
process(block: BaseContentBlock): Promise<ProcessBlockResult>;
|
6
|
+
}
|
@@ -0,0 +1,113 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
exports.ImageBlockProcessor = void 0;
|
7
|
+
const blockProcessor_1 = require("./blockProcessor");
|
8
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
9
|
+
const path_1 = __importDefault(require("path"));
|
10
|
+
const gatsby_source_filesystem_1 = require("gatsby-source-filesystem");
|
11
|
+
class ImageBlockProcessor extends blockProcessor_1.BlockProcessor {
|
12
|
+
canProcess(block) {
|
13
|
+
return this.isImageBlock(block);
|
14
|
+
}
|
15
|
+
async process(block) {
|
16
|
+
const { actions, getCache, createNodeId, reporter, cache } = this.context;
|
17
|
+
const { createNode } = actions;
|
18
|
+
const imageBlock = block;
|
19
|
+
if (block.type === "image" && "image" in block) {
|
20
|
+
const imageSourceType = imageBlock.image.type;
|
21
|
+
const imageUrl = imageSourceType === `external`
|
22
|
+
? imageBlock.image.external?.url
|
23
|
+
: imageBlock.image?.file?.url;
|
24
|
+
if (!imageUrl)
|
25
|
+
return { updatedBlock: block };
|
26
|
+
// GIF 파일 처리
|
27
|
+
if (imageUrl.endsWith(".gif")) {
|
28
|
+
const staticDir = path_1.default.join(process.cwd(), "static"); // Gatsby의 static 디렉토리
|
29
|
+
const gifFileName = path_1.default.basename(imageUrl); // 파일 이름 추출
|
30
|
+
const gifFilePath = path_1.default.join(staticDir, gifFileName);
|
31
|
+
// 이미 static 디렉토리에 파일이 있는지 확인
|
32
|
+
if (!fs_extra_1.default.existsSync(gifFilePath)) {
|
33
|
+
try {
|
34
|
+
reporter.info(`[GIF PROCESSING] Downloading GIF: ${imageUrl}`);
|
35
|
+
const response = await fetch(imageUrl);
|
36
|
+
if (!response.ok) {
|
37
|
+
throw new Error(`Failed to download GIF: ${response.statusText}`);
|
38
|
+
}
|
39
|
+
const arrayBuffer = await response.arrayBuffer();
|
40
|
+
const buffer = Buffer.from(arrayBuffer);
|
41
|
+
await fs_extra_1.default.ensureDir(staticDir); // static 디렉토리 생성
|
42
|
+
await fs_extra_1.default.writeFile(gifFilePath, buffer); // GIF 파일 저장
|
43
|
+
reporter.info(`[GIF SUCCESS] Saved GIF to static: ${gifFilePath}`);
|
44
|
+
}
|
45
|
+
catch (error) {
|
46
|
+
reporter.warn(`[GIF WARNING] Failed to process GIF: ${imageUrl}`);
|
47
|
+
return { updatedBlock: block };
|
48
|
+
}
|
49
|
+
}
|
50
|
+
else {
|
51
|
+
reporter.info(`[GIF CACHE HIT] GIF already exists: ${gifFilePath}`);
|
52
|
+
}
|
53
|
+
// GIF 파일을 정적 파일로 추가
|
54
|
+
const updatedBlock = {
|
55
|
+
...imageBlock,
|
56
|
+
image: {
|
57
|
+
fileId: gifFileName, // static 경로를 기준으로 참조
|
58
|
+
caption: imageBlock.image.caption,
|
59
|
+
},
|
60
|
+
};
|
61
|
+
return {
|
62
|
+
updatedBlock: updatedBlock,
|
63
|
+
thumbnail: gifFileName,
|
64
|
+
};
|
65
|
+
}
|
66
|
+
// GIF가 아닌 경우 기존 로직 유지
|
67
|
+
const cacheKey = `${imageUrl}-post-image`;
|
68
|
+
const cachedFileNodeId = await cache.get(cacheKey);
|
69
|
+
if (cachedFileNodeId) {
|
70
|
+
reporter.info(`[CACHE HIT] Image already processed: ${imageUrl}`);
|
71
|
+
const updatedBlock = {
|
72
|
+
...imageBlock,
|
73
|
+
image: {
|
74
|
+
fileId: cachedFileNodeId,
|
75
|
+
caption: imageBlock.image.caption,
|
76
|
+
},
|
77
|
+
};
|
78
|
+
return {
|
79
|
+
updatedBlock: updatedBlock,
|
80
|
+
thumbnail: cachedFileNodeId,
|
81
|
+
};
|
82
|
+
}
|
83
|
+
try {
|
84
|
+
const fileNode = await (0, gatsby_source_filesystem_1.createRemoteFileNode)({
|
85
|
+
url: imageUrl,
|
86
|
+
parentNodeId: block.id,
|
87
|
+
getCache,
|
88
|
+
createNode,
|
89
|
+
createNodeId,
|
90
|
+
});
|
91
|
+
if (fileNode) {
|
92
|
+
await cache.set(cacheKey, fileNode.id);
|
93
|
+
const updatedBlock = {
|
94
|
+
...imageBlock,
|
95
|
+
image: {
|
96
|
+
fileId: fileNode.id,
|
97
|
+
caption: imageBlock.image.caption,
|
98
|
+
},
|
99
|
+
};
|
100
|
+
return {
|
101
|
+
updatedBlock: updatedBlock,
|
102
|
+
thumbnail: fileNode.id,
|
103
|
+
};
|
104
|
+
}
|
105
|
+
}
|
106
|
+
catch (error) {
|
107
|
+
reporter.warn(`[WARNING] Failed to download or process image: ${imageUrl}`);
|
108
|
+
}
|
109
|
+
}
|
110
|
+
return { updatedBlock: block };
|
111
|
+
}
|
112
|
+
}
|
113
|
+
exports.ImageBlockProcessor = ImageBlockProcessor;
|
@@ -0,0 +1,22 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3
|
+
if (k2 === undefined) k2 = k;
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
7
|
+
}
|
8
|
+
Object.defineProperty(o, k2, desc);
|
9
|
+
}) : (function(o, m, k, k2) {
|
10
|
+
if (k2 === undefined) k2 = k;
|
11
|
+
o[k2] = m[k];
|
12
|
+
}));
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
15
|
+
};
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
17
|
+
__exportStar(require("./blockProcessor"), exports);
|
18
|
+
__exportStar(require("./blockProcessorRegistry"), exports);
|
19
|
+
__exportStar(require("./imageBlockProcessor"), exports);
|
20
|
+
__exportStar(require("./mediaBlockProcessor"), exports);
|
21
|
+
__exportStar(require("./structureBlockProcessor"), exports);
|
22
|
+
__exportStar(require("./textBlockProcessor"), exports);
|
@@ -0,0 +1,6 @@
|
|
1
|
+
import { BaseContentBlock } from "notion-types";
|
2
|
+
import { BlockProcessor, ProcessBlockResult } from "./blockProcessor";
|
3
|
+
export declare class MediaBlockProcessor extends BlockProcessor {
|
4
|
+
canProcess(block: BaseContentBlock): boolean;
|
5
|
+
process(block: BaseContentBlock): Promise<ProcessBlockResult>;
|
6
|
+
}
|
@@ -0,0 +1,48 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.MediaBlockProcessor = void 0;
|
4
|
+
const blockProcessor_1 = require("./blockProcessor");
|
5
|
+
class MediaBlockProcessor extends blockProcessor_1.BlockProcessor {
|
6
|
+
canProcess(block) {
|
7
|
+
return ["bookmark", "embed", "video", "audio", "pdf", "file"].includes(block.type);
|
8
|
+
}
|
9
|
+
async process(block) {
|
10
|
+
const { reporter } = this.context;
|
11
|
+
// 미디어 블록 타입 처리 로직
|
12
|
+
const blockType = block.type;
|
13
|
+
// 각 블록 타입에 맞는 특별한 처리가 필요한 경우 여기에 추가
|
14
|
+
switch (blockType) {
|
15
|
+
case "bookmark":
|
16
|
+
reporter.info(`Processing bookmark block: ${JSON.stringify(block.bookmark?.url)}`);
|
17
|
+
break;
|
18
|
+
case "embed":
|
19
|
+
reporter.info(`Processing embed block: ${JSON.stringify(block.embed?.url)}`);
|
20
|
+
break;
|
21
|
+
case "video":
|
22
|
+
reporter.info(`Processing video block with type: ${JSON.stringify(block.video?.type)}`);
|
23
|
+
break;
|
24
|
+
case "audio":
|
25
|
+
reporter.info(`Processing audio block with type: ${JSON.stringify(block.audio?.type)}`);
|
26
|
+
break;
|
27
|
+
case "pdf":
|
28
|
+
reporter.info(`Processing pdf block with type: ${JSON.stringify(block.pdf?.type)}`);
|
29
|
+
break;
|
30
|
+
case "file":
|
31
|
+
reporter.info(`Processing file block with type: ${JSON.stringify(block.file?.type)}`);
|
32
|
+
break;
|
33
|
+
}
|
34
|
+
// 블록의 텍스트 컨텐츠를 추출하려고 시도합니다.
|
35
|
+
let plainText = "";
|
36
|
+
// bookmark와 같은 일부 블록은 캡션에 텍스트가 있을 수 있습니다.
|
37
|
+
if (blockType === "bookmark" && block.bookmark?.caption) {
|
38
|
+
plainText = block.bookmark.caption
|
39
|
+
.map((item) => item.plain_text)
|
40
|
+
.join(" ");
|
41
|
+
}
|
42
|
+
return {
|
43
|
+
plainText,
|
44
|
+
updatedBlock: block,
|
45
|
+
};
|
46
|
+
}
|
47
|
+
}
|
48
|
+
exports.MediaBlockProcessor = MediaBlockProcessor;
|
@@ -0,0 +1,6 @@
|
|
1
|
+
import { BaseContentBlock } from "notion-types";
|
2
|
+
import { BlockProcessor, ProcessBlockResult } from "./blockProcessor";
|
3
|
+
export declare class StructureBlockProcessor extends BlockProcessor {
|
4
|
+
canProcess(block: BaseContentBlock): boolean;
|
5
|
+
process(block: BaseContentBlock): Promise<ProcessBlockResult>;
|
6
|
+
}
|
@@ -0,0 +1,81 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.StructureBlockProcessor = void 0;
|
4
|
+
const blockProcessor_1 = require("./blockProcessor");
|
5
|
+
class StructureBlockProcessor extends blockProcessor_1.BlockProcessor {
|
6
|
+
canProcess(block) {
|
7
|
+
return [
|
8
|
+
"column",
|
9
|
+
"column_list",
|
10
|
+
"table",
|
11
|
+
"table_row",
|
12
|
+
"divider",
|
13
|
+
"breadcrumb",
|
14
|
+
"table_of_contents",
|
15
|
+
"equation",
|
16
|
+
"synced_block",
|
17
|
+
"template",
|
18
|
+
"link_to_page",
|
19
|
+
"link_preview",
|
20
|
+
].includes(block.type);
|
21
|
+
}
|
22
|
+
async process(block) {
|
23
|
+
const { reporter } = this.context;
|
24
|
+
// 구조 블록 타입 처리 로직
|
25
|
+
const blockType = block.type;
|
26
|
+
// 각 블록 타입에 맞는 특별한 처리가 필요한 경우 여기에 추가
|
27
|
+
switch (blockType) {
|
28
|
+
case "column":
|
29
|
+
reporter.info(`Processing column block`);
|
30
|
+
break;
|
31
|
+
case "column_list":
|
32
|
+
reporter.info(`Processing column_list block`);
|
33
|
+
break;
|
34
|
+
case "table":
|
35
|
+
reporter.info(`Processing table block: table_width=${JSON.stringify(block.table?.table_width)}`);
|
36
|
+
break;
|
37
|
+
case "table_row":
|
38
|
+
const cellCount = block.table_row?.cells?.length || 0;
|
39
|
+
reporter.info(`Processing table_row block with ${cellCount} cells`);
|
40
|
+
break;
|
41
|
+
case "divider":
|
42
|
+
reporter.info(`Processing divider block`);
|
43
|
+
break;
|
44
|
+
case "breadcrumb":
|
45
|
+
reporter.info(`Processing breadcrumb block`);
|
46
|
+
break;
|
47
|
+
case "table_of_contents":
|
48
|
+
reporter.info(`Processing table_of_contents block`);
|
49
|
+
break;
|
50
|
+
case "equation":
|
51
|
+
reporter.info(`Processing equation block: ${JSON.stringify(block.equation?.expression)}`);
|
52
|
+
break;
|
53
|
+
case "synced_block":
|
54
|
+
reporter.info(`Processing synced_block`);
|
55
|
+
break;
|
56
|
+
case "template":
|
57
|
+
reporter.info(`Processing template block`);
|
58
|
+
break;
|
59
|
+
case "link_to_page":
|
60
|
+
reporter.info(`Processing link_to_page block`);
|
61
|
+
break;
|
62
|
+
case "link_preview":
|
63
|
+
reporter.info(`Processing link_preview block: ${JSON.stringify(block.link_preview?.url)}`);
|
64
|
+
break;
|
65
|
+
}
|
66
|
+
// 일부 구조 블록은 텍스트를 포함할 수 있음
|
67
|
+
let plainText = "";
|
68
|
+
// 테이블 셀에서 텍스트 추출 예시
|
69
|
+
if (blockType === "table_row" && block.table_row?.cells) {
|
70
|
+
plainText = block.table_row.cells
|
71
|
+
.flat()
|
72
|
+
.map((cell) => cell.plain_text || "")
|
73
|
+
.join(" ");
|
74
|
+
}
|
75
|
+
return {
|
76
|
+
plainText,
|
77
|
+
updatedBlock: block,
|
78
|
+
};
|
79
|
+
}
|
80
|
+
}
|
81
|
+
exports.StructureBlockProcessor = StructureBlockProcessor;
|
@@ -0,0 +1,6 @@
|
|
1
|
+
import { BaseContentBlock } from "notion-types";
|
2
|
+
import { BlockProcessor, ProcessBlockResult } from "./blockProcessor";
|
3
|
+
export declare class TextBlockProcessor extends BlockProcessor {
|
4
|
+
canProcess(block: BaseContentBlock): boolean;
|
5
|
+
process(block: BaseContentBlock): Promise<ProcessBlockResult>;
|
6
|
+
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.TextBlockProcessor = void 0;
|
4
|
+
const blockProcessor_1 = require("./blockProcessor");
|
5
|
+
const tableOfContent_1 = require("../tableOfContent");
|
6
|
+
class TextBlockProcessor extends blockProcessor_1.BlockProcessor {
|
7
|
+
canProcess(block) {
|
8
|
+
return this.isTextContentBlock(block);
|
9
|
+
}
|
10
|
+
async process(block) {
|
11
|
+
const tableOfContents = [];
|
12
|
+
// 목차 처리
|
13
|
+
await (0, tableOfContent_1.processTableOfContents)(block, tableOfContents);
|
14
|
+
// 텍스트 추출
|
15
|
+
const plainText = this.extractPlainText(block);
|
16
|
+
return {
|
17
|
+
plainText: plainText || "",
|
18
|
+
updatedBlock: block,
|
19
|
+
tableOfContents: tableOfContents.length > 0 ? tableOfContents : undefined,
|
20
|
+
};
|
21
|
+
}
|
22
|
+
}
|
23
|
+
exports.TextBlockProcessor = TextBlockProcessor;
|
package/dist/util/processor.js
CHANGED
@@ -1,14 +1,8 @@
|
|
1
1
|
"use strict";
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
-
};
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
3
|
exports.processor = void 0;
|
7
|
-
const gatsby_source_filesystem_1 = require("gatsby-source-filesystem");
|
8
4
|
const metadataProcessor_1 = require("./metadataProcessor");
|
9
|
-
const
|
10
|
-
const fs_extra_1 = __importDefault(require("fs-extra"));
|
11
|
-
const path_1 = __importDefault(require("path"));
|
5
|
+
const blocks_1 = require("./blocks");
|
12
6
|
const processor = async (blocks, actions, getCache, createNodeId, reporter, cache) => {
|
13
7
|
const { thumbnail, tableOfContents, updatedBlocks, rawText } = await processBlocksForContent(blocks, actions, getCache, createNodeId, reporter, cache);
|
14
8
|
await (0, metadataProcessor_1.processMetadata)(blocks, actions, createNodeId, reporter, cache);
|
@@ -16,154 +10,46 @@ const processor = async (blocks, actions, getCache, createNodeId, reporter, cach
|
|
16
10
|
};
|
17
11
|
exports.processor = processor;
|
18
12
|
const processBlocksForContent = async (blocks, actions, getCache, createNodeId, reporter, cache) => {
|
13
|
+
const context = {
|
14
|
+
actions,
|
15
|
+
getCache,
|
16
|
+
createNodeId,
|
17
|
+
reporter,
|
18
|
+
cache,
|
19
|
+
};
|
20
|
+
// 블록 프로세서 레지스트리 생성
|
21
|
+
const processorRegistry = new blocks_1.BlockProcessorRegistry(context);
|
19
22
|
const tableOfContents = [];
|
20
23
|
let thumbnail = null;
|
21
24
|
let rawText = "";
|
22
25
|
const updatedBlocks = [];
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
if (isImageBlock(block)) {
|
47
|
-
const updatedBlock = await processImageBlock(block, actions, getCache, createNodeId, reporter, cache);
|
48
|
-
updatedBlocks[index] = updatedBlock || block;
|
26
|
+
// 첫 번째 이미지 블록을 찾아 썸네일로 사용
|
27
|
+
let firstImageIndex = blocks.findIndex((block) => block.type === "image");
|
28
|
+
// 블록 처리
|
29
|
+
const processResults = await Promise.all(blocks.map(async (block, index) => {
|
30
|
+
const result = await processorRegistry.processBlock(block);
|
31
|
+
// 썸네일 처리
|
32
|
+
if (index === firstImageIndex && result.thumbnail) {
|
33
|
+
thumbnail = result.thumbnail;
|
34
|
+
}
|
35
|
+
// 텍스트 데이터 추가
|
36
|
+
if (result.plainText) {
|
37
|
+
rawText += result.plainText + " ";
|
38
|
+
}
|
39
|
+
// 목차 데이터 추가
|
40
|
+
if (result.tableOfContents) {
|
41
|
+
tableOfContents.push(...result.tableOfContents);
|
42
|
+
}
|
43
|
+
return result;
|
44
|
+
}));
|
45
|
+
// 업데이트된 블록 적용
|
46
|
+
processResults.forEach((result, index) => {
|
47
|
+
if (result.updatedBlock) {
|
48
|
+
updatedBlocks[index] = result.updatedBlock;
|
49
49
|
}
|
50
50
|
else {
|
51
|
-
updatedBlocks[index] =
|
51
|
+
updatedBlocks[index] = blocks[index];
|
52
52
|
}
|
53
|
-
})
|
53
|
+
});
|
54
54
|
return { thumbnail, tableOfContents, updatedBlocks, rawText };
|
55
55
|
};
|
56
|
-
const isTextContentBlock = (block) => {
|
57
|
-
return [
|
58
|
-
"paragraph",
|
59
|
-
"heading_1",
|
60
|
-
"heading_2",
|
61
|
-
"heading_3",
|
62
|
-
"quote",
|
63
|
-
"bulleted_list_item",
|
64
|
-
"numbered_list_item",
|
65
|
-
"callout",
|
66
|
-
"code",
|
67
|
-
].includes(block.type);
|
68
|
-
};
|
69
|
-
const extractPlainText = (block) => {
|
70
|
-
if (isTextContentBlock(block)) {
|
71
|
-
const richTextArray = block[block.type]?.rich_text || [];
|
72
|
-
return richTextArray
|
73
|
-
.map((text) => block.type === "code" // code의 \n 제거
|
74
|
-
? text.plain_text.replace(/\\n/g, "")
|
75
|
-
: text.plain_text)
|
76
|
-
.join(" ");
|
77
|
-
}
|
78
|
-
return null;
|
79
|
-
};
|
80
|
-
const isImageBlock = (block) => {
|
81
|
-
return block.type === "image" && "image" in block;
|
82
|
-
};
|
83
|
-
const processImageBlock = async (block, actions, getCache, createNodeId, reporter, cache) => {
|
84
|
-
const { createNode } = actions;
|
85
|
-
if (block.type === "image" && "image" in block) {
|
86
|
-
const imageSourceType = block.image.type;
|
87
|
-
const imageUrl = imageSourceType === `external`
|
88
|
-
? block.image.external?.url
|
89
|
-
: block.image?.file?.url;
|
90
|
-
if (!imageUrl)
|
91
|
-
return null;
|
92
|
-
// GIF 파일 처리
|
93
|
-
if (imageUrl.endsWith(".gif")) {
|
94
|
-
const staticDir = path_1.default.join(process.cwd(), "static"); // Gatsby의 static 디렉토리
|
95
|
-
const gifFileName = path_1.default.basename(imageUrl); // 파일 이름 추출
|
96
|
-
const gifFilePath = path_1.default.join(staticDir, gifFileName);
|
97
|
-
// 이미 static 디렉토리에 파일이 있는지 확인
|
98
|
-
if (!fs_extra_1.default.existsSync(gifFilePath)) {
|
99
|
-
try {
|
100
|
-
reporter.info(`[GIF PROCESSING] Downloading GIF: ${imageUrl}`);
|
101
|
-
const response = await fetch(imageUrl);
|
102
|
-
if (!response.ok) {
|
103
|
-
throw new Error(`Failed to download GIF: ${response.statusText}`);
|
104
|
-
}
|
105
|
-
const arrayBuffer = await response.arrayBuffer();
|
106
|
-
const buffer = Buffer.from(arrayBuffer);
|
107
|
-
await fs_extra_1.default.ensureDir(staticDir); // static 디렉토리 생성
|
108
|
-
await fs_extra_1.default.writeFile(gifFilePath, buffer); // GIF 파일 저장
|
109
|
-
reporter.info(`[GIF SUCCESS] Saved GIF to static: ${gifFilePath}`);
|
110
|
-
}
|
111
|
-
catch (error) {
|
112
|
-
reporter.warn(`[GIF WARNING] Failed to process GIF: ${imageUrl}`);
|
113
|
-
return null;
|
114
|
-
}
|
115
|
-
}
|
116
|
-
else {
|
117
|
-
reporter.info(`[GIF CACHE HIT] GIF already exists: ${gifFilePath}`);
|
118
|
-
}
|
119
|
-
// GIF 파일을 정적 파일로 추가
|
120
|
-
const updatedBlock = {
|
121
|
-
...block,
|
122
|
-
image: {
|
123
|
-
fileId: gifFileName, // static 경로를 기준으로 참조
|
124
|
-
caption: block.image.caption,
|
125
|
-
},
|
126
|
-
};
|
127
|
-
return updatedBlock;
|
128
|
-
}
|
129
|
-
// GIF가 아닌 경우 기존 로직 유지
|
130
|
-
const cacheKey = `${imageUrl}-post-image`;
|
131
|
-
const cachedFileNodeId = await cache.get(cacheKey);
|
132
|
-
if (cachedFileNodeId) {
|
133
|
-
reporter.info(`[CACHE HIT] Image already processed: ${imageUrl}`);
|
134
|
-
const updatedBlock = {
|
135
|
-
...block,
|
136
|
-
image: {
|
137
|
-
fileId: cachedFileNodeId,
|
138
|
-
caption: block.image.caption,
|
139
|
-
},
|
140
|
-
};
|
141
|
-
return updatedBlock;
|
142
|
-
}
|
143
|
-
try {
|
144
|
-
const fileNode = await (0, gatsby_source_filesystem_1.createRemoteFileNode)({
|
145
|
-
url: imageUrl,
|
146
|
-
parentNodeId: block.id,
|
147
|
-
getCache,
|
148
|
-
createNode,
|
149
|
-
createNodeId,
|
150
|
-
});
|
151
|
-
if (fileNode) {
|
152
|
-
const updatedBlock = {
|
153
|
-
...block,
|
154
|
-
image: {
|
155
|
-
fileId: fileNode.id,
|
156
|
-
caption: block.image.caption,
|
157
|
-
},
|
158
|
-
};
|
159
|
-
reporter.info(`[SUCCESS] Image processed: ${fileNode.id}`);
|
160
|
-
await cache.set(cacheKey, fileNode.id);
|
161
|
-
return updatedBlock;
|
162
|
-
}
|
163
|
-
}
|
164
|
-
catch (error) {
|
165
|
-
reporter.warn(`[WARNING] Failed to download image: ${imageUrl}`);
|
166
|
-
}
|
167
|
-
}
|
168
|
-
return null;
|
169
|
-
};
|