From b3b6651cac4af8c07651c8ede464cf3c7b557b8d Mon Sep 17 00:00:00 2001 From: hyonun321 Date: Tue, 14 Jan 2025 18:10:20 +0900 Subject: [PATCH 01/13] =?UTF-8?q?build:=20=ED=8F=B4=EB=8D=94=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD=EC=97=90=20=EB=94=B0=EB=9D=BC=20?= =?UTF-8?q?=EA=B0=81=20config=20=ED=8C=8C=EC=9D=BC=EB=93=A4=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - noctaCRDT -> noctaCRDT/src 등으로 구조 변경 - tsconfig - vite - webpack등의 경로 수정 Co-authored-by: Yeonkyu Min Co-authored-by: minjungw00 --- @noctaCrdt/package.json | 44 ++++++++++++++++++++------------ @noctaCrdt/tsconfig.json | 2 +- client/tsconfig.json | 4 +-- client/vite.config.ts | 2 +- pnpm-lock.yaml | 54 ++++++++++------------------------------ server/tsconfig.json | 4 +-- server/webpack.config.js | 2 +- 7 files changed, 48 insertions(+), 64 deletions(-) diff --git a/@noctaCrdt/package.json b/@noctaCrdt/package.json index 906e52a..cc69e38 100644 --- a/@noctaCrdt/package.json +++ b/@noctaCrdt/package.json @@ -1,32 +1,44 @@ { "name": "@noctaCrdt", "version": "1.0.0", - "main": "./dist/Crdt.js", - "module": "./dist/Crdt.js", - "types": "dist/Crdt.d.ts", + "main": "./dist/src/Crdt.js", + "module": "./dist/src/Crdt.js", + "types": "dist/src/Crdt.d.ts", "scripts": { - "build": "tsc -b" + "build": "tsc -b", + "test": "jest", + "test:watch": "jest --watch" }, "exports": { "./Crdt": { - "require": "./dist/Crdt.js", - "import": "./dist/Crdt.js", - "types": "./dist/Crdt.d.js" + "require": "./dist/src/Crdt.js", + "import": "./dist/src/Crdt.js", + "types": "./dist/src/Crdt.d.js" }, "./Node": { - "require": "./dist/Node.js", - "import": "./dist/Node.js", - "types": "./dist/Node.d.ts" + "require": "./dist/src/Node.js", + "import": "./dist/src/Node.js", + "types": "./dist/src/Node.d.ts" }, "./LinkedList": { - "require": "./dist/LinkedList.js", - "import": "./dist/LinkedList.js", - "types": "./dist/LinkedList.d.ts" + "require": "./dist/src/LinkedList.js", + "import": "./dist/src/LinkedList.js", + "types": "./dist/src/LinkedList.d.ts" }, "./Interfaces": { - "require": "./dist/Interfaces.js", - "import": "./dist/Interfaces.js", - "types": "./dist/Interfaces.d.ts" + "require": "./dist/src/types/Interfaces.js", + "import": "./dist/src/types/Interfaces.js", + "types": "./dist/src/types/Interfaces.d.ts" + }, + "./Workspace": { + "require": "./dist/src/Workspace.js", + "import": "./dist/src/Workspace.js", + "types": "./dist/src/Workspace.d.ts" } + }, + "devDependencies": { + "@types/jest": "^29.5.2", + "jest": "^29.5.0", + "ts-jest": "^29.1.0" } } diff --git a/@noctaCrdt/tsconfig.json b/@noctaCrdt/tsconfig.json index c35ddc9..21228bf 100644 --- a/@noctaCrdt/tsconfig.json +++ b/@noctaCrdt/tsconfig.json @@ -14,6 +14,6 @@ "moduleResolution": "node", // 추가 "target": "ES2021" // 추가 }, - "include": ["**/*.ts"], + "include": ["**/*.ts", "__tests__/**/*"], "exclude": ["node_modules", "dist"] } diff --git a/client/tsconfig.json b/client/tsconfig.json index 35c75b4..94b7c50 100644 --- a/client/tsconfig.json +++ b/client/tsconfig.json @@ -30,8 +30,8 @@ "@utils/*": ["src/utils/*"], "@apis/*": ["src/apis/*"], "@stores/*": ["src/stores/*"], - "@noctaCrdt": ["../@noctaCrdt/dist"], - "@noctaCrdt/*": ["../@noctaCrdt/dist/*"] + "@noctaCrdt": ["../@noctaCrdt/src"], + "@noctaCrdt/*": ["../@noctaCrdt/src/*"] } }, "references": [{ "path": "../@noctaCrdt" }], diff --git a/client/vite.config.ts b/client/vite.config.ts index effd30b..4e8ba5d 100644 --- a/client/vite.config.ts +++ b/client/vite.config.ts @@ -7,7 +7,7 @@ import svgr from "vite-plugin-svgr"; export default defineConfig({ plugins: [react(), tsconfigPaths(), svgr()], resolve: { - alias: { "@noctaCrdt": path.resolve(__dirname, "../@noctaCrdt") }, + alias: { "@noctaCrdt": path.resolve(__dirname, "../@noctaCrdt/src") }, }, publicDir: "public", }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2e743c8..78bd641 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -57,7 +57,17 @@ importers: specifier: ~5.3.3 version: 5.3.3 - '@noctaCrdt': {} + '@noctaCrdt': + devDependencies: + '@types/jest': + specifier: ^29.5.2 + version: 29.5.14 + jest: + specifier: ^29.5.0 + version: 29.7.0(@types/node@20.17.6)(ts-node@10.9.2(@types/node@20.17.6)(typescript@5.3.3)) + ts-jest: + specifier: ^29.1.0 + version: 29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@20.17.6)(ts-node@10.9.2(@types/node@20.17.6)(typescript@5.3.3)))(typescript@5.3.3) client: dependencies: @@ -130,7 +140,7 @@ importers: version: 3.2.1 eslint-plugin-import: specifier: ^2.29.1 - version: 2.31.0(eslint@8.57.1) + version: 2.31.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.3.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.3.3))(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1) eslint-plugin-jsx-a11y: specifier: ^6.8.0 version: 6.10.2(eslint@8.57.1) @@ -7219,7 +7229,7 @@ snapshots: axios@1.7.7: dependencies: - follow-redirects: 1.15.9 + follow-redirects: 1.15.9(debug@4.3.7) form-data: 4.0.1 proxy-from-env: 1.1.0 transitivePeerDependencies: @@ -8002,15 +8012,6 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(eslint-import-resolver-node@0.3.9)(eslint@8.57.1): - dependencies: - debug: 3.2.7 - optionalDependencies: - eslint: 8.57.1 - eslint-import-resolver-node: 0.3.9 - transitivePeerDependencies: - - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.3.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.3.3))(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1): dependencies: '@rtsao/scc': 1.1.0 @@ -8040,33 +8041,6 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.31.0(eslint@8.57.1): - dependencies: - '@rtsao/scc': 1.1.0 - array-includes: 3.1.8 - array.prototype.findlastindex: 1.2.5 - array.prototype.flat: 1.3.2 - array.prototype.flatmap: 1.3.2 - debug: 3.2.7 - doctrine: 2.1.0 - eslint: 8.57.1 - eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(eslint-import-resolver-node@0.3.9)(eslint@8.57.1) - hasown: 2.0.2 - is-core-module: 2.15.1 - is-glob: 4.0.3 - minimatch: 3.1.2 - object.fromentries: 2.0.8 - object.groupby: 1.0.3 - object.values: 1.2.0 - semver: 6.3.1 - string.prototype.trimend: 1.0.8 - tsconfig-paths: 3.15.0 - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - eslint-plugin-jsx-a11y@6.10.2(eslint@8.57.1): dependencies: aria-query: 5.3.2 @@ -8363,8 +8337,6 @@ snapshots: flatted@3.3.1: {} - follow-redirects@1.15.9: {} - follow-redirects@1.15.9(debug@4.3.7): optionalDependencies: debug: 4.3.7 diff --git a/server/tsconfig.json b/server/tsconfig.json index e57851c..5f0fe46 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -20,8 +20,8 @@ "noImplicitAny": false, "strictBindCallApply": false, "paths": { - "@noctaCrdt": ["../@noctaCrdt/dist"], - "@noctaCrdt/*": ["../@noctaCrdt/dist/*"] + "@noctaCrdt": ["../@noctaCrdt/dist/src"], + "@noctaCrdt/*": ["../@noctaCrdt/dist/src/*"] }, "forceConsistentCasingInFileNames": false, "noFallthroughCasesInSwitch": false, diff --git a/server/webpack.config.js b/server/webpack.config.js index 9df8123..53048c5 100644 --- a/server/webpack.config.js +++ b/server/webpack.config.js @@ -5,7 +5,7 @@ module.exports = { resolve: { extensions: [".ts", ".js"], alias: { - "@noctaCrdt": path.resolve(__dirname, "../@noctaCrdt/dist"), + "@noctaCrdt": path.resolve(__dirname, "../@noctaCrdt/dist/src"), }, }, module: { From 35c09cd188662287be92a851f42f26a0f8a03478 Mon Sep 17 00:00:00 2001 From: hyonun321 Date: Tue, 14 Jan 2025 18:11:48 +0900 Subject: [PATCH 02/13] =?UTF-8?q?refactor:=20noctaCrdt=20=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EA=B5=AC=EC=A1=B0?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - src파일에 주요 파일들 이동 - Interface의경우 types로 따로이동 --- @noctaCrdt/{ => src}/Crdt.ts | 2 +- @noctaCrdt/{ => src}/LinkedList.ts | 48 ++++++++++++++++++++---- @noctaCrdt/{ => src}/Node.ts | 10 +++-- @noctaCrdt/{ => src}/NodeId.ts | 0 @noctaCrdt/{ => src}/Page.ts | 2 +- @noctaCrdt/{ => src}/WorkSpace.ts | 2 +- @noctaCrdt/{ => src/types}/Interfaces.ts | 8 ++-- 7 files changed, 55 insertions(+), 17 deletions(-) rename @noctaCrdt/{ => src}/Crdt.ts (99%) rename @noctaCrdt/{ => src}/LinkedList.ts (90%) rename @noctaCrdt/{ => src}/Node.ts (94%) rename @noctaCrdt/{ => src}/NodeId.ts (100%) rename @noctaCrdt/{ => src}/Page.ts (95%) rename @noctaCrdt/{ => src}/WorkSpace.ts (98%) rename @noctaCrdt/{ => src/types}/Interfaces.ts (96%) diff --git a/@noctaCrdt/Crdt.ts b/@noctaCrdt/src/Crdt.ts similarity index 99% rename from @noctaCrdt/Crdt.ts rename to @noctaCrdt/src/Crdt.ts index 7f76129..2b9757f 100644 --- a/@noctaCrdt/Crdt.ts +++ b/@noctaCrdt/src/Crdt.ts @@ -13,7 +13,7 @@ import { RemoteCharUpdateOperation, TextColorType, BackgroundColorType, -} from "./Interfaces"; +} from "./types/Interfaces"; export class CRDT> { clock: number; diff --git a/@noctaCrdt/LinkedList.ts b/@noctaCrdt/src/LinkedList.ts similarity index 90% rename from @noctaCrdt/LinkedList.ts rename to @noctaCrdt/src/LinkedList.ts index 759c07a..d5323af 100644 --- a/@noctaCrdt/LinkedList.ts +++ b/@noctaCrdt/src/LinkedList.ts @@ -1,6 +1,6 @@ import { Node, Char, Block } from "./Node"; import { NodeId, BlockId, CharId } from "./NodeId"; -import { InsertOperation, ReorderNodesProps } from "./Interfaces"; +import { ReorderNodesProps } from "./types/Interfaces"; export abstract class LinkedList> { head: T["id"] | null; @@ -25,7 +25,7 @@ export abstract class LinkedList> { return this.nodeMap[JSON.stringify(id)] || null; } - deleteNode(id: T["id"]): void { + removeNode(id: T["id"]): void { const nodeToDelete = this.getNode(id); if (!nodeToDelete) return; @@ -55,6 +55,25 @@ export abstract class LinkedList> { delete this.nodeMap[JSON.stringify(id)]; } + deleteNode(id: T["id"]): void { + const nodeToDelete = this.getNode(id); + if (!nodeToDelete) return; + nodeToDelete.deleted = true; + } + + clearDeletedNode(): void { + let currentNodeId = this.head; + + while (currentNodeId !== null) { + const currentNode = this.getNode(currentNodeId); + if (!currentNode) return; + if (currentNode.deleted) { + this.removeNode(currentNodeId); + } + currentNodeId = currentNode.next; + } + } + updateAllOrderedListIndices() {} reorderNodes({ targetId, beforeId, afterId }: ReorderNodesProps): void {} @@ -67,13 +86,20 @@ export abstract class LinkedList> { let currentNodeId = this.head; let currentIndex = 0; - while (currentNodeId !== null && currentIndex < index) { + while (currentNodeId !== null) { const currentNode = this.getNode(currentNodeId); if (!currentNode) { throw new Error(`Node not found at index ${currentIndex}`); } - currentNodeId = currentNode.next; - currentIndex += 1; + + if (!currentNode.deleted) { + if (currentIndex === index) { + return currentNode; // 찾은 노드를 바로 반환 + } + currentIndex += 1; + } + + currentNodeId = currentNode.next; // 다음 노드로 이동 } if (currentNodeId === null) { @@ -199,7 +225,9 @@ export abstract class LinkedList> { while (currentNodeId !== null) { const currentNode = this.getNode(currentNodeId); if (!currentNode) break; - result += currentNode.value; + if (!currentNode.deleted) { + result += currentNode.value; + } currentNodeId = currentNode.next; } @@ -212,7 +240,9 @@ export abstract class LinkedList> { while (currentNodeId !== null) { const currentNode = this.getNode(currentNodeId); if (!currentNode) break; - result.push(currentNode!); + if (!currentNode.deleted) { + result.push(currentNode!); + } currentNodeId = currentNode.next; } return result; @@ -248,6 +278,10 @@ export class BlockLinkedList extends LinkedList { let currentIndex = 1; while (currentNode) { + if (currentNode.deleted) { + currentNode = currentNode.next ? this.getNode(currentNode.next) : null; + continue; + } if (currentNode.type === "ol") { const prevNode = currentNode.prev ? this.getNode(currentNode.prev) : null; diff --git a/@noctaCrdt/Node.ts b/@noctaCrdt/src/Node.ts similarity index 94% rename from @noctaCrdt/Node.ts rename to @noctaCrdt/src/Node.ts index c0f6104..d157dea 100644 --- a/@noctaCrdt/Node.ts +++ b/@noctaCrdt/src/Node.ts @@ -1,10 +1,11 @@ // Node.ts import { NodeId, BlockId, CharId } from "./NodeId"; -import { AnimationType, ElementType, TextColorType, BackgroundColorType } from "./Interfaces"; +import { AnimationType, ElementType, TextColorType, BackgroundColorType } from "./types/Interfaces"; import { BlockCRDT } from "./Crdt"; export abstract class Node { id: T; + deleted: boolean; value: string; next: T | null; prev: T | null; @@ -12,6 +13,7 @@ export abstract class Node { constructor(value: string, id: T) { this.id = id; + this.deleted = false; this.value = value; this.next = null; this.prev = null; @@ -19,8 +21,9 @@ export abstract class Node { } precedes(node: Node): boolean { - if (!this.prev || !node.prev) return false; - if (!this.prev.equals(node.prev)) return false; + if (this.prev && node.prev) { + if (!this.prev.equals(node.prev)) return false; + } if (this.id.clock < node.id.clock) return true; if (this.id.clock === node.id.clock && this.id.client < node.id.client) return true; @@ -32,6 +35,7 @@ export abstract class Node { return { id: this.id.serialize(), value: this.value, + deleted: this.deleted, next: this.next ? this.next.serialize() : null, prev: this.prev ? this.prev.serialize() : null, style: this.style, diff --git a/@noctaCrdt/NodeId.ts b/@noctaCrdt/src/NodeId.ts similarity index 100% rename from @noctaCrdt/NodeId.ts rename to @noctaCrdt/src/NodeId.ts diff --git a/@noctaCrdt/Page.ts b/@noctaCrdt/src/Page.ts similarity index 95% rename from @noctaCrdt/Page.ts rename to @noctaCrdt/src/Page.ts index 48b874e..a1f2888 100644 --- a/@noctaCrdt/Page.ts +++ b/@noctaCrdt/src/Page.ts @@ -1,6 +1,6 @@ import { EditorCRDT } from "./Crdt"; import { Block } from "./Node"; -import { CRDTSerializedProps, PageIconType } from "./Interfaces"; +import { CRDTSerializedProps, PageIconType } from "./types/Interfaces"; export interface PageSerializedProps { id: string; diff --git a/@noctaCrdt/WorkSpace.ts b/@noctaCrdt/src/WorkSpace.ts similarity index 98% rename from @noctaCrdt/WorkSpace.ts rename to @noctaCrdt/src/WorkSpace.ts index 4b0fd79..b463493 100644 --- a/@noctaCrdt/WorkSpace.ts +++ b/@noctaCrdt/src/WorkSpace.ts @@ -1,5 +1,5 @@ import { Page } from "./Page"; -import { RemotePageUpdateOperation, WorkSpaceSerializedProps } from "./Interfaces"; +import { RemotePageUpdateOperation, WorkSpaceSerializedProps } from "./types/Interfaces"; import { EditorCRDT } from "./Crdt"; export class WorkSpace { diff --git a/@noctaCrdt/Interfaces.ts b/@noctaCrdt/src/types/Interfaces.ts similarity index 96% rename from @noctaCrdt/Interfaces.ts rename to @noctaCrdt/src/types/Interfaces.ts index 46d5d2c..9b667c6 100644 --- a/@noctaCrdt/Interfaces.ts +++ b/@noctaCrdt/src/types/Interfaces.ts @@ -1,7 +1,7 @@ -import { NodeId, BlockId, CharId } from "./NodeId"; -import { Block, Char } from "./Node"; -import { Page } from "./Page"; -import { EditorCRDT } from "./Crdt"; +import { NodeId, BlockId, CharId } from "../NodeId"; +import { Block, Char } from "../Node"; +import { Page } from "../Page"; +import { EditorCRDT } from "../Crdt"; export type ElementType = | "p" From 8038505932e5bd4f53d09293f9ddd3d4fb0c0f1e Mon Sep 17 00:00:00 2001 From: hyonun321 Date: Tue, 14 Jan 2025 18:12:22 +0900 Subject: [PATCH 03/13] =?UTF-8?q?test:=20LinkedList=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - TombStoen 적용에 따른 메소드 체크 - 그외 기본동작 메소드 체크 --- @noctaCrdt/__tests__/LinkedList.test.ts | 366 ++++++++++++++++++++++++ @noctaCrdt/jest.config.js | 8 + 2 files changed, 374 insertions(+) create mode 100644 @noctaCrdt/__tests__/LinkedList.test.ts create mode 100644 @noctaCrdt/jest.config.js diff --git a/@noctaCrdt/__tests__/LinkedList.test.ts b/@noctaCrdt/__tests__/LinkedList.test.ts new file mode 100644 index 0000000..16480b4 --- /dev/null +++ b/@noctaCrdt/__tests__/LinkedList.test.ts @@ -0,0 +1,366 @@ +import { LinkedList, BlockLinkedList, TextLinkedList } from "../src/LinkedList"; +import { Node, Char, Block } from "../src/Node"; +import { NodeId, BlockId, CharId } from "../src/NodeId"; + +describe("연결 리스트", () => { + let blockList: BlockLinkedList; + let textList: TextLinkedList; + + beforeEach(() => { + blockList = new BlockLinkedList(); + textList = new TextLinkedList(); + }); + + describe("기본 동작", () => { + test("빈 리스트로 초기화되어야 함", () => { + expect(blockList.head).toBeNull(); + expect(blockList.nodeMap).toEqual({}); + }); + + test("노드 생성 및 조회가 가능해야 함", () => { + const id = new BlockId(1, 1); // clock: 1, client: 1 + const node = new Block("테스트 내용", id); + blockList.setNode(id, node); + + const retrievedNode = blockList.getNode(id); + expect(retrievedNode).toBeDefined(); + expect(retrievedNode?.value).toBe("테스트 내용"); + }); + + test("존재하지 않는 노드 조회시 null을 반환해야 함", () => { + const nonExistentId = new BlockId(999, 1); + expect(blockList.getNode(nonExistentId)).toBeNull(); + }); + }); + + describe("삽입 연산", () => { + test("리스트 시작에 노드를 삽입해야 함", () => { + const id = new BlockId(1, 1); + const result = blockList.insertAtIndex(0, "첫 번째 노드", id); + + expect(blockList.head).toEqual(id); + expect(result.node.value).toBe("첫 번째 노드"); + expect(result.node.prev).toBeNull(); + expect(result.node.next).toBeNull(); + }); + + test("기존 노드들 사이에 노드를 삽입해야 함", () => { + // 첫 번째 노드 삽입 + const id1 = new BlockId(1, 1); + blockList.insertAtIndex(0, "첫번째", id1); + + // 두 번째 노드 삽입 + const id2 = new BlockId(2, 1); + blockList.insertAtIndex(1, "두번째", id2); + + // 중간에 노드 삽입 + const id3 = new BlockId(3, 1); + blockList.insertAtIndex(1, "중간", id3); + const middleNode = blockList.getNode(id3); + expect(middleNode?.prev).toEqual(id1); + expect(middleNode?.next).toEqual(id2); + }); + + test("ID로 노드를 삽입해야 함", () => { + const id1 = new BlockId(1, 1); + const node1 = new Block("첫번째", id1); + const id2 = new BlockId(2, 1); + const node2 = new Block("두번째", id2); + + node2.prev = id1; + blockList.insertById(node1); + blockList.insertById(node2); + + expect(blockList.stringify()).toBe("첫번째두번째"); + }); + }); + + describe("삭제 및 톰스톤", () => { + test("노드가 삭제됨으로 표시되어야 함 (톰스톤)", () => { + const id = new BlockId(1, 1); + blockList.insertAtIndex(0, "삭제될 내용", id); + + blockList.deleteNode(id); + const node = blockList.getNode(id); + + expect(node?.deleted).toBe(true); + expect(blockList.stringify()).toBe(""); // 삭제된 노드는 문자열에 나타나지 않아야 함 + }); + + test("연속된 톰스톤을 처리해야 함", () => { + const id1 = new BlockId(1, 1); + const id2 = new BlockId(2, 1); + const id3 = new BlockId(3, 1); + + blockList.insertAtIndex(0, "첫번째", id1); + blockList.insertAtIndex(1, "두번째", id2); + blockList.insertAtIndex(2, "세번째", id3); + + blockList.deleteNode(id1); + blockList.deleteNode(id2); + + expect(blockList.stringify()).toBe("세번째"); + expect(blockList.spread().length).toBe(1); + }); + + test("인덱스로 노드를 찾을 때 톰스톤을 건너뛰어야 함", () => { + const id1 = new BlockId(1, 1); + const id2 = new BlockId(2, 1); + const id3 = new BlockId(3, 1); + + blockList.insertAtIndex(0, "첫번째", id1); + blockList.insertAtIndex(1, "두번째", id2); + blockList.insertAtIndex(2, "세번째", id3); + + blockList.deleteNode(id2); // 중간 노드를 삭제로 표시 + + const thirdNode = blockList.findByIndex(1); // 삭제된 노드를 건너뛰어야 함 + expect(thirdNode.value).toBe("세번째"); + }); + }); + + describe("노드 제거", () => { + test("리스트에서 노드를 완전히 제거해야 함", () => { + const id1 = new BlockId(1, 1); + const id2 = new BlockId(2, 1); + + blockList.insertAtIndex(0, "첫번째", id1); + blockList.insertAtIndex(1, "두번째", id2); + + blockList.removeNode(id1); + + expect(blockList.getNode(id1)).toBeNull(); + expect(blockList.head).toEqual(id2); + }); + + test("헤드 노드 제거를 처리해야 함", () => { + const id = new BlockId(1, 1); + blockList.insertAtIndex(0, "헤드", id); + + blockList.removeNode(id); + + expect(blockList.head).toBeNull(); + expect(blockList.nodeMap).toEqual({}); + }); + }); + + describe("리스트 연산", () => { + test("인덱스 범위 내의 노드들을 가져와야 함", () => { + const nodes = ["첫번째", "두번째", "세번째", "네번째"].map((value, index) => { + const id = new BlockId(index + 1, 1); + blockList.insertAtIndex(index, value, id); + return blockList.getNode(id)!; + }); + + const middle = blockList.getNodesBetween(1, 3); + expect(middle.length).toBe(2); + expect(middle[0].value).toBe("두번째"); + expect(middle[1].value).toBe("세번째"); + }); + + test("리스트를 배열로 변환해야 함", () => { + ["가", "나", "다"].forEach((value, index) => { + const id = new BlockId(index + 1, 1); + blockList.insertAtIndex(index, value, id); + }); + + const array = blockList.spread(); + expect(array.length).toBe(3); + expect(array.map((node) => node.value).join("")).toBe("가나다"); + }); + }); + + describe("직렬화", () => { + test("리스트를 직렬화하고 역직렬화해야 함", () => { + const id1 = new BlockId(1, 1); + const id2 = new BlockId(2, 1); + + blockList.insertAtIndex(0, "첫번째", id1); + blockList.insertAtIndex(1, "두번째", id2); + + const serialized = blockList.serialize(); + const newList = new BlockLinkedList(); + newList.deserialize(serialized); + + expect(newList.stringify()).toBe(blockList.stringify()); + expect(newList.head).toEqual(blockList.head); + }); + + test("BlockId를 올바르게 직렬화하고 역직렬화해야 함", () => { + const originalId = new BlockId(1, 2); + const serialized = originalId.serialize(); + const deserialized = BlockId.deserialize(serialized); + + expect(deserialized.clock).toBe(originalId.clock); + expect(deserialized.client).toBe(originalId.client); + expect(deserialized.equals(originalId)).toBe(true); + }); + }); + + describe("순서가 있는 리스트 연산", () => { + test("순서가 있는 리스트의 인덱스를 갱신해야 함", () => { + const id1 = new BlockId(1, 1); + const id2 = new BlockId(2, 1); + const id3 = new BlockId(3, 1); + + blockList.insertAtIndex(0, "첫번째", id1); + blockList.insertAtIndex(1, "두번째", id2); + blockList.insertAtIndex(2, "세번째", id3); + + const node1 = blockList.getNode(id1)!; + const node2 = blockList.getNode(id2)!; + const node3 = blockList.getNode(id3)!; + + node1.type = "ol"; + node2.type = "ol"; + node3.type = "ol"; + + expect(node1.next).toBe(id2); + expect(node2.prev).toBe(id1); + expect(node2.next).toBe(id3); + expect(node3.prev).toBe(id2); + + blockList.updateAllOrderedListIndices(); + + expect(node1.listIndex).toBe(1); + expect(node2.listIndex).toBe(2); + expect(node3.listIndex).toBe(3); + }); + + test("들여쓰기가 다른 중첩된 순서 리스트를 처리해야 함", () => { + const id1 = new BlockId(1, 1); + const id2 = new BlockId(2, 1); + + blockList.insertAtIndex(0, "부모", id1); + blockList.insertAtIndex(1, "자식", id2); + + const node1 = blockList.getNode(id1)!; + const node2 = blockList.getNode(id2)!; + + node1.type = "ol"; + node2.type = "ol"; + node2.indent = 1; + blockList.updateAllOrderedListIndices(); + + expect(node1.listIndex).toBe(1); + expect(node2.listIndex).toBe(1); // 중첩된 리스트는 1부터 시작 + expect(node1.indent).toBe(0); + expect(node2.indent).toBe(1); + }); + }); + + describe("노드 재정렬", () => { + test("노드 순서를 변경해야 함", () => { + const id1 = new BlockId(1, 1); + const id2 = new BlockId(2, 1); + const id3 = new BlockId(3, 1); + + blockList.insertAtIndex(0, "첫번째", id1); + blockList.insertAtIndex(1, "두번째", id2); + blockList.insertAtIndex(2, "세번째", id3); + + blockList.reorderNodes({ + targetId: id3, + beforeId: id1, + afterId: id2, + }); + expect(blockList.head).toEqual(id1); + const firstNode = blockList.getNode(blockList.head!); + expect(firstNode?.value).toBe("첫번째"); + + blockList.reorderNodes({ + targetId: id2, + beforeId: null, + afterId: id1, + }); + expect(blockList.head).toEqual(id2); + const firstNode2 = blockList.getNode(blockList.head!); + expect(firstNode2?.value).toBe("두번째"); + }); + + test("순서가 있는 리스트의 재정렬을 처리해야 함", () => { + const id1 = new BlockId(1, 1); + const id2 = new BlockId(2, 1); + + const node1 = new Block("첫번째", id1); + const node2 = new Block("두번째", id2); + + node1.type = "ol"; + node2.type = "ol"; + + blockList.insertById(node1); + blockList.insertById(node2); + + blockList.reorderNodes({ + targetId: id2, + beforeId: id1, + afterId: null, + }); + + blockList.updateAllOrderedListIndices(); + + const firstNode = blockList.getNode(blockList.head!); + expect(firstNode?.listIndex).toBe(1); + }); + }); + + describe("CRDT 속성", () => { + test("노드 우선순위를 올바르게 결정해야 함", () => { + const id1 = new BlockId(1, 1); + const id2 = new BlockId(1, 2); + const id3 = new BlockId(2, 1); + + const node1 = new Block("첫번째", id1); + const node2 = new Block("두번째", id2); + const node3 = new Block("세번째", id3); + + // 같은 클록, 다른 클라이언트 + expect(node1.precedes(node2)).toBe(true); + + // 다른 클록 + expect(node1.precedes(node3)).toBe(true); + expect(node3.precedes(node1)).toBe(false); + }); + }); + + describe("TombStone 제거", () => { + test("3개 툼스톤 가비지 컬렉팅 확인", () => { + const id1 = new BlockId(1, 1); + const id2 = new BlockId(2, 1); + const id3 = new BlockId(3, 1); + blockList.insertAtIndex(0, "a", id1); + blockList.insertAtIndex(1, "c", id2); + blockList.insertAtIndex(1, "b", id3); + + blockList.deleteNode(id3); + + blockList.clearDeletedNode(); + + const node1 = blockList.getNode(id1); + + expect(node1?.next).toBe(id2); + }); + + test("여러개 Linked list 가비지 컬렉팅", () => { + const ids = []; + for (let i = 0; i < 10; i++) { + const id = new BlockId(i, 1); + ids.push(id); + blockList.insertAtIndex(i, `블록${i}`, id); + } + + expect(blockList.spread().length).toBe(10); + + for (let i = 1; i < 10; i += 2) { + blockList.deleteNode(ids[i]); + } + + blockList.clearDeletedNode(); + + expect(blockList.spread().length).toBe(5); + const node0 = blockList.getNode(ids[0]); + const node3 = blockList.getNode(ids[2]); + expect(node0?.next).toBe(node3?.id); + }); + }); +}); diff --git a/@noctaCrdt/jest.config.js b/@noctaCrdt/jest.config.js new file mode 100644 index 0000000..8fec0a4 --- /dev/null +++ b/@noctaCrdt/jest.config.js @@ -0,0 +1,8 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} */ +module.exports = { + preset: "ts-jest", + testEnvironment: "node", + testPathIgnorePatterns: ["/dist/"], + // roots: ['/__tests__'], + // ... +}; From 5da15a7e21053755d191fa92d70e089b3369f0de Mon Sep 17 00:00:00 2001 From: hyonun321 Date: Tue, 14 Jan 2025 18:13:42 +0900 Subject: [PATCH 04/13] =?UTF-8?q?refactor:=20=EA=B2=BD=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EC=97=90=20=EB=94=B0=EB=A5=B8=20import=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/button/IconButton.tsx | 2 +- client/src/components/sidebar/Sidebar.tsx | 2 +- .../components/components/WorkspaceSelectItem.tsx | 2 +- .../sidebar/components/pageIconButton/PageIconButton.tsx | 2 +- .../sidebar/components/pageIconButton/PageIconModal.tsx | 2 +- .../components/sidebar/components/pageItem/PageItem.tsx | 2 +- client/src/constants/PageIconButton.config.ts | 2 +- client/src/constants/option.ts | 2 +- client/src/features/editor/Editor.tsx | 2 +- .../ColorOptionModal/BackgroundColorOptionModal.style.ts | 2 +- .../ColorOptionModal/BackgroundColorOptionModal.tsx | 2 +- .../ColorOptionModal/TextColorOptionModal.style.ts | 2 +- .../components/ColorOptionModal/TextColorOptionModal.tsx | 2 +- .../features/editor/components/IconBlock/IconBlock.tsx | 2 +- .../features/editor/components/MenuBlock/MenuBlock.tsx | 2 +- .../editor/components/OptionModal/OptionModal.tsx | 2 +- .../editor/components/TextOptionModal/TextOptionModal.tsx | 2 +- .../editor/components/TypeOptionModal/TypeOptionModal.tsx | 2 +- client/src/features/editor/components/block/Block.tsx | 6 +++--- client/src/features/editor/hooks/useBlockDragAndDrop.ts | 2 +- client/src/features/editor/hooks/useBlockOperation.ts | 5 ++++- client/src/features/editor/hooks/useBlockOption.ts | 8 ++++---- client/src/features/editor/hooks/useCopyAndPaste.ts | 2 +- client/src/features/editor/hooks/useEditorOperation.ts | 6 +++--- client/src/features/editor/hooks/useMarkdownGrammer.ts | 6 +++--- client/src/features/editor/hooks/useTextOptions.ts | 2 +- client/src/features/editor/utils/domSyncUtils.ts | 2 +- client/src/features/page/Page.tsx | 2 +- .../src/features/page/components/PageTitle/PageTitle.tsx | 2 +- client/src/features/workSpace/hooks/usePagesManage.ts | 6 +++--- client/src/stores/useSocketStore.ts | 2 +- client/src/types/markdown.ts | 2 +- client/src/types/page.ts | 2 +- server/src/workspace/workspace.controller.ts | 2 +- server/src/workspace/workspace.gateway.ts | 2 +- server/src/workspace/workspace.service.ts | 4 ++-- 36 files changed, 51 insertions(+), 48 deletions(-) diff --git a/client/src/components/button/IconButton.tsx b/client/src/components/button/IconButton.tsx index ddd1b90..e96a02e 100644 --- a/client/src/components/button/IconButton.tsx +++ b/client/src/components/button/IconButton.tsx @@ -1,4 +1,4 @@ -import { PageIconType } from "@noctaCrdt/Interfaces"; +import { PageIconType } from "@noctaCrdt/types/Interfaces"; import { iconComponents, IconConfig } from "@src/constants/PageIconButton.config"; import { iconButtonContainer } from "./IconButton.style"; diff --git a/client/src/components/sidebar/Sidebar.tsx b/client/src/components/sidebar/Sidebar.tsx index 0dc04dc..7abd5ba 100644 --- a/client/src/components/sidebar/Sidebar.tsx +++ b/client/src/components/sidebar/Sidebar.tsx @@ -1,5 +1,5 @@ +import { PageIconType } from "@noctaCrdt/types/Interfaces"; import { motion } from "framer-motion"; -import { PageIconType } from "node_modules/@noctaCrdt/Interfaces"; import { useState } from "react"; import { IconButton } from "@components/button/IconButton"; import { Modal } from "@components/modal/modal"; diff --git a/client/src/components/sidebar/components/menuButton/components/components/WorkspaceSelectItem.tsx b/client/src/components/sidebar/components/menuButton/components/components/WorkspaceSelectItem.tsx index b387028..4571747 100644 --- a/client/src/components/sidebar/components/menuButton/components/components/WorkspaceSelectItem.tsx +++ b/client/src/components/sidebar/components/menuButton/components/components/WorkspaceSelectItem.tsx @@ -1,4 +1,4 @@ -import { WorkspaceListItem } from "@noctaCrdt/Interfaces"; // 이전에 만든 인터페이스 import +import { WorkspaceListItem } from "@noctaCrdt/types/Interfaces"; // 이전에 만든 인터페이스 import import { useState } from "react"; import PencilIcon from "@assets/icons/pencil.svg?react"; import { RenameModal } from "@src/components/modal/RenameModal"; diff --git a/client/src/components/sidebar/components/pageIconButton/PageIconButton.tsx b/client/src/components/sidebar/components/pageIconButton/PageIconButton.tsx index 52d8e22..6d89c09 100644 --- a/client/src/components/sidebar/components/pageIconButton/PageIconButton.tsx +++ b/client/src/components/sidebar/components/pageIconButton/PageIconButton.tsx @@ -1,4 +1,4 @@ -import { PageIconType } from "@noctaCrdt/Interfaces"; +import { PageIconType } from "@noctaCrdt/types/Interfaces"; import { iconComponents, IconConfig } from "@constants/PageIconButton.config"; import { IconBox } from "./PageIconButton.style"; diff --git a/client/src/components/sidebar/components/pageIconButton/PageIconModal.tsx b/client/src/components/sidebar/components/pageIconButton/PageIconModal.tsx index d36c0f0..2318389 100644 --- a/client/src/components/sidebar/components/pageIconButton/PageIconModal.tsx +++ b/client/src/components/sidebar/components/pageIconButton/PageIconModal.tsx @@ -1,4 +1,4 @@ -import { PageIconType } from "@noctaCrdt/Interfaces"; +import { PageIconType } from "@noctaCrdt/types/Interfaces"; import { RiCloseLine } from "react-icons/ri"; import { iconGroups, iconComponents } from "@constants/PageIconButton.config"; import { css } from "@styled-system/css"; diff --git a/client/src/components/sidebar/components/pageItem/PageItem.tsx b/client/src/components/sidebar/components/pageItem/PageItem.tsx index e2df9f5..bf80003 100644 --- a/client/src/components/sidebar/components/pageItem/PageItem.tsx +++ b/client/src/components/sidebar/components/pageItem/PageItem.tsx @@ -1,4 +1,4 @@ -import { PageIconType } from "@noctaCrdt/Interfaces"; +import { PageIconType } from "@noctaCrdt/types/Interfaces"; import { useEffect, useState } from "react"; import CloseIcon from "@assets/icons/close.svg?react"; import { useModal } from "@src/components/modal/useModal"; diff --git a/client/src/constants/PageIconButton.config.ts b/client/src/constants/PageIconButton.config.ts index 2a868b4..9f60db0 100644 --- a/client/src/constants/PageIconButton.config.ts +++ b/client/src/constants/PageIconButton.config.ts @@ -1,4 +1,4 @@ -import { PageIconType } from "@noctaCrdt/Interfaces"; +import { PageIconType } from "@noctaCrdt/types/Interfaces"; import { IconType } from "react-icons"; import { CgGym } from "react-icons/cg"; import { MdOutlinePlace } from "react-icons/md"; diff --git a/client/src/constants/option.ts b/client/src/constants/option.ts index d9181d4..d0fa8e7 100644 --- a/client/src/constants/option.ts +++ b/client/src/constants/option.ts @@ -1,4 +1,4 @@ -import { AnimationType, ElementType, TextStyleType } from "@noctaCrdt/Interfaces"; +import { AnimationType, ElementType, TextStyleType } from "@noctaCrdt/types/Interfaces"; export const OPTION_CATEGORIES = { TYPE: { diff --git a/client/src/features/editor/Editor.tsx b/client/src/features/editor/Editor.tsx index 008a9ba..2427c60 100644 --- a/client/src/features/editor/Editor.tsx +++ b/client/src/features/editor/Editor.tsx @@ -3,7 +3,7 @@ import { SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable" import { EditorCRDT } from "@noctaCrdt/Crdt"; import { BlockLinkedList } from "@noctaCrdt/LinkedList"; import { Block as CRDTBlock } from "@noctaCrdt/Node"; -import { serializedEditorDataProps } from "node_modules/@noctaCrdt/Interfaces.ts"; +import { serializedEditorDataProps } from "@noctaCrdt/types/Interfaces"; import { useRef, useState, useCallback, useEffect, useMemo } from "react"; import { useSocketStore } from "@src/stores/useSocketStore.ts"; import { setCaretPosition, getAbsoluteCaretPosition } from "@src/utils/caretUtils.ts"; diff --git a/client/src/features/editor/components/ColorOptionModal/BackgroundColorOptionModal.style.ts b/client/src/features/editor/components/ColorOptionModal/BackgroundColorOptionModal.style.ts index b9d7e82..4e0d147 100644 --- a/client/src/features/editor/components/ColorOptionModal/BackgroundColorOptionModal.style.ts +++ b/client/src/features/editor/components/ColorOptionModal/BackgroundColorOptionModal.style.ts @@ -1,4 +1,4 @@ -import { BackgroundColorType } from "@noctaCrdt/Interfaces"; +import { BackgroundColorType } from "@noctaCrdt/types/Interfaces"; import { css, cva } from "@styled-system/css"; type ColorVariants = { diff --git a/client/src/features/editor/components/ColorOptionModal/BackgroundColorOptionModal.tsx b/client/src/features/editor/components/ColorOptionModal/BackgroundColorOptionModal.tsx index 4bed831..470190a 100644 --- a/client/src/features/editor/components/ColorOptionModal/BackgroundColorOptionModal.tsx +++ b/client/src/features/editor/components/ColorOptionModal/BackgroundColorOptionModal.tsx @@ -1,4 +1,4 @@ -import { BackgroundColorType } from "@noctaCrdt/Interfaces"; +import { BackgroundColorType } from "@noctaCrdt/types/Interfaces"; import { backgroundColorIndicator, colorOptionButton, diff --git a/client/src/features/editor/components/ColorOptionModal/TextColorOptionModal.style.ts b/client/src/features/editor/components/ColorOptionModal/TextColorOptionModal.style.ts index 7400a00..d5edceb 100644 --- a/client/src/features/editor/components/ColorOptionModal/TextColorOptionModal.style.ts +++ b/client/src/features/editor/components/ColorOptionModal/TextColorOptionModal.style.ts @@ -1,4 +1,4 @@ -import { TextColorType } from "@noctaCrdt/Interfaces"; +import { TextColorType } from "@noctaCrdt/types/Interfaces"; import { css, cva } from "@styled-system/css"; type ColorVariants = { diff --git a/client/src/features/editor/components/ColorOptionModal/TextColorOptionModal.tsx b/client/src/features/editor/components/ColorOptionModal/TextColorOptionModal.tsx index c1e89dd..6a996d8 100644 --- a/client/src/features/editor/components/ColorOptionModal/TextColorOptionModal.tsx +++ b/client/src/features/editor/components/ColorOptionModal/TextColorOptionModal.tsx @@ -1,4 +1,4 @@ -import { TextColorType } from "@noctaCrdt/Interfaces"; +import { TextColorType } from "@noctaCrdt/types/Interfaces"; import { colorOptionButton, colorPaletteContainer, diff --git a/client/src/features/editor/components/IconBlock/IconBlock.tsx b/client/src/features/editor/components/IconBlock/IconBlock.tsx index 61c0632..a2ea9f8 100644 --- a/client/src/features/editor/components/IconBlock/IconBlock.tsx +++ b/client/src/features/editor/components/IconBlock/IconBlock.tsx @@ -1,4 +1,4 @@ -import { ElementType } from "@noctaCrdt/Interfaces"; +import { ElementType } from "@noctaCrdt/types/Interfaces"; import { iconContainerStyle, iconStyle } from "./IconBlock.style"; interface IconBlockProps { diff --git a/client/src/features/editor/components/MenuBlock/MenuBlock.tsx b/client/src/features/editor/components/MenuBlock/MenuBlock.tsx index da273c6..095765e 100644 --- a/client/src/features/editor/components/MenuBlock/MenuBlock.tsx +++ b/client/src/features/editor/components/MenuBlock/MenuBlock.tsx @@ -1,4 +1,4 @@ -import { AnimationType, ElementType } from "@noctaCrdt/Interfaces"; +import { AnimationType, ElementType } from "@noctaCrdt/types/Interfaces"; import { useState, useRef } from "react"; import DraggableIcon from "@assets/icons/draggable.svg?url"; import { useModal } from "@src/components/modal/useModal"; diff --git a/client/src/features/editor/components/OptionModal/OptionModal.tsx b/client/src/features/editor/components/OptionModal/OptionModal.tsx index d3936c0..4610e6f 100644 --- a/client/src/features/editor/components/OptionModal/OptionModal.tsx +++ b/client/src/features/editor/components/OptionModal/OptionModal.tsx @@ -1,4 +1,4 @@ -import { AnimationType, ElementType } from "@noctaCrdt/Interfaces"; +import { AnimationType, ElementType } from "@noctaCrdt/types/Interfaces"; import { motion } from "framer-motion"; import { useEffect, useRef, useState } from "react"; import { createPortal } from "react-dom"; diff --git a/client/src/features/editor/components/TextOptionModal/TextOptionModal.tsx b/client/src/features/editor/components/TextOptionModal/TextOptionModal.tsx index 0bc7618..dbd384d 100644 --- a/client/src/features/editor/components/TextOptionModal/TextOptionModal.tsx +++ b/client/src/features/editor/components/TextOptionModal/TextOptionModal.tsx @@ -1,5 +1,5 @@ -import { TextColorType, BackgroundColorType } from "@noctaCrdt/Interfaces"; import { Char } from "@noctaCrdt/Node"; +import { TextColorType, BackgroundColorType } from "@noctaCrdt/types/Interfaces"; import { motion } from "framer-motion"; import { useEffect, useRef, useState } from "react"; import { createPortal } from "react-dom"; diff --git a/client/src/features/editor/components/TypeOptionModal/TypeOptionModal.tsx b/client/src/features/editor/components/TypeOptionModal/TypeOptionModal.tsx index 9ccaac9..49b10ba 100644 --- a/client/src/features/editor/components/TypeOptionModal/TypeOptionModal.tsx +++ b/client/src/features/editor/components/TypeOptionModal/TypeOptionModal.tsx @@ -1,5 +1,5 @@ +import { ElementType } from "@noctaCrdt/types/Interfaces"; import { motion } from "framer-motion"; -import { ElementType } from "node_modules/@noctaCrdt/Interfaces"; import { useEffect, useRef, useState } from "react"; import { createPortal } from "react-dom"; import { OPTION_CATEGORIES } from "@src/constants/option"; diff --git a/client/src/features/editor/components/block/Block.tsx b/client/src/features/editor/components/block/Block.tsx index 635181f..c26419d 100644 --- a/client/src/features/editor/components/block/Block.tsx +++ b/client/src/features/editor/components/block/Block.tsx @@ -1,13 +1,13 @@ import { useSortable } from "@dnd-kit/sortable"; +import { Block as CRDTBlock, Char } from "@noctaCrdt/Node"; +import { BlockId } from "@noctaCrdt/NodeId"; import { AnimationType, ElementType, TextColorType, TextStyleType, BackgroundColorType, -} from "@noctaCrdt/Interfaces"; -import { Block as CRDTBlock, Char } from "@noctaCrdt/Node"; -import { BlockId } from "@noctaCrdt/NodeId"; +} from "@noctaCrdt/types/Interfaces"; import { motion } from "framer-motion"; import { memo, useEffect, useRef, useState } from "react"; import { useModal } from "@src/components/modal/useModal"; diff --git a/client/src/features/editor/hooks/useBlockDragAndDrop.ts b/client/src/features/editor/hooks/useBlockDragAndDrop.ts index 517a5b1..e306ab9 100644 --- a/client/src/features/editor/hooks/useBlockDragAndDrop.ts +++ b/client/src/features/editor/hooks/useBlockDragAndDrop.ts @@ -4,7 +4,7 @@ import { Block } from "@noctaCrdt/Node"; import { RemoteBlockReorderOperation, RemoteBlockUpdateOperation, -} from "node_modules/@noctaCrdt/Interfaces"; +} from "@noctaCrdt/types/Interfaces"; import { useSocketStore } from "@src/stores/useSocketStore.ts"; import { EditorStateProps } from "../Editor"; diff --git a/client/src/features/editor/hooks/useBlockOperation.ts b/client/src/features/editor/hooks/useBlockOperation.ts index 481b61e..4d770d5 100644 --- a/client/src/features/editor/hooks/useBlockOperation.ts +++ b/client/src/features/editor/hooks/useBlockOperation.ts @@ -1,7 +1,10 @@ import { EditorCRDT } from "@noctaCrdt/Crdt"; -import { RemoteBlockCheckboxOperation, RemoteCharInsertOperation } from "@noctaCrdt/Interfaces"; import { Block } from "@noctaCrdt/Node"; import { BlockId } from "@noctaCrdt/NodeId"; +import { + RemoteBlockCheckboxOperation, + RemoteCharInsertOperation, +} from "@noctaCrdt/types/Interfaces"; import { useCallback } from "react"; import { useSocketStore } from "@src/stores/useSocketStore"; import { getAbsoluteCaretPosition } from "@src/utils/caretUtils"; diff --git a/client/src/features/editor/hooks/useBlockOption.ts b/client/src/features/editor/hooks/useBlockOption.ts index b591721..dfac265 100644 --- a/client/src/features/editor/hooks/useBlockOption.ts +++ b/client/src/features/editor/hooks/useBlockOption.ts @@ -1,4 +1,7 @@ import { BlockCRDT, EditorCRDT } from "@noctaCrdt/Crdt"; +import { BlockLinkedList } from "@noctaCrdt/LinkedList"; +import { Block } from "@noctaCrdt/Node"; +import { BlockId } from "@noctaCrdt/NodeId"; import { AnimationType, ElementType, @@ -6,10 +9,7 @@ import { RemoteBlockInsertOperation, RemoteBlockUpdateOperation, RemoteCharInsertOperation, -} from "@noctaCrdt/Interfaces"; -import { Block } from "@noctaCrdt/Node"; -import { BlockId } from "@noctaCrdt/NodeId"; -import { BlockLinkedList } from "node_modules/@noctaCrdt/LinkedList"; +} from "@noctaCrdt/types/Interfaces"; import { EditorStateProps } from "../Editor"; interface useBlockOptionSelectProps { diff --git a/client/src/features/editor/hooks/useCopyAndPaste.ts b/client/src/features/editor/hooks/useCopyAndPaste.ts index f7c9c71..485321c 100644 --- a/client/src/features/editor/hooks/useCopyAndPaste.ts +++ b/client/src/features/editor/hooks/useCopyAndPaste.ts @@ -1,6 +1,6 @@ import { EditorCRDT } from "@noctaCrdt/Crdt"; -import { TextColorType, BackgroundColorType } from "@noctaCrdt/Interfaces"; import { Block } from "@noctaCrdt/Node"; +import { TextColorType, BackgroundColorType } from "@noctaCrdt/types/Interfaces"; import { useCallback } from "react"; import { useSocketStore } from "@src/stores/useSocketStore"; import { EditorStateProps } from "../Editor"; diff --git a/client/src/features/editor/hooks/useEditorOperation.ts b/client/src/features/editor/hooks/useEditorOperation.ts index de21675..e5b959f 100644 --- a/client/src/features/editor/hooks/useEditorOperation.ts +++ b/client/src/features/editor/hooks/useEditorOperation.ts @@ -1,4 +1,6 @@ import { EditorCRDT } from "@noctaCrdt/Crdt"; +import { TextLinkedList } from "@noctaCrdt/LinkedList"; +import { CharId } from "@noctaCrdt/NodeId"; import { RemoteBlockDeleteOperation, RemoteCharInsertOperation, @@ -8,9 +10,7 @@ import { RemoteCharUpdateOperation, RemoteBlockInsertOperation, RemoteBlockCheckboxOperation, -} from "@noctaCrdt/Interfaces"; -import { TextLinkedList } from "@noctaCrdt/LinkedList"; -import { CharId } from "@noctaCrdt/NodeId"; +} from "@noctaCrdt/types/Interfaces"; import { useCallback } from "react"; import { useSocketStore } from "@src/stores/useSocketStore"; import { EditorStateProps } from "../Editor"; diff --git a/client/src/features/editor/hooks/useMarkdownGrammer.ts b/client/src/features/editor/hooks/useMarkdownGrammer.ts index a3f5e71..f4e232e 100644 --- a/client/src/features/editor/hooks/useMarkdownGrammer.ts +++ b/client/src/features/editor/hooks/useMarkdownGrammer.ts @@ -1,13 +1,13 @@ import { EditorCRDT, BlockCRDT } from "@noctaCrdt/Crdt"; +import { BlockLinkedList } from "@noctaCrdt/LinkedList"; +import { Block } from "@noctaCrdt/Node"; import { RemoteBlockInsertOperation, RemoteBlockDeleteOperation, RemoteBlockUpdateOperation, RemoteCharInsertOperation, RemoteCharDeleteOperation, -} from "@noctaCrdt/Interfaces"; -import { BlockLinkedList } from "@noctaCrdt/LinkedList"; -import { Block } from "@noctaCrdt/Node"; +} from "@noctaCrdt/types/Interfaces"; import { useCallback } from "react"; import { EditorStateProps } from "@features/editor/Editor"; import { checkMarkdownPattern } from "@src/features/editor/utils/markdownPatterns"; diff --git a/client/src/features/editor/hooks/useTextOptions.ts b/client/src/features/editor/hooks/useTextOptions.ts index 0cb3a3f..377f130 100644 --- a/client/src/features/editor/hooks/useTextOptions.ts +++ b/client/src/features/editor/hooks/useTextOptions.ts @@ -1,7 +1,7 @@ import { EditorCRDT } from "@noctaCrdt/Crdt"; -import { TextStyleType, TextColorType, BackgroundColorType } from "@noctaCrdt/Interfaces"; import { Block, Char } from "@noctaCrdt/Node"; import { BlockId } from "@noctaCrdt/NodeId"; +import { TextStyleType, TextColorType, BackgroundColorType } from "@noctaCrdt/types/Interfaces"; import { useCallback } from "react"; import { useSocketStore } from "@src/stores/useSocketStore"; import { EditorStateProps } from "../Editor"; diff --git a/client/src/features/editor/utils/domSyncUtils.ts b/client/src/features/editor/utils/domSyncUtils.ts index e31bbdf..d636b67 100644 --- a/client/src/features/editor/utils/domSyncUtils.ts +++ b/client/src/features/editor/utils/domSyncUtils.ts @@ -1,5 +1,5 @@ -import { TextColorType, BackgroundColorType } from "@noctaCrdt/Interfaces"; import { Block } from "@noctaCrdt/Node"; +import { TextColorType, BackgroundColorType } from "@noctaCrdt/types/Interfaces"; import { COLOR } from "@src/constants/color"; import { css } from "styled-system/css"; diff --git a/client/src/features/page/Page.tsx b/client/src/features/page/Page.tsx index 474f67f..8b39d8e 100644 --- a/client/src/features/page/Page.tsx +++ b/client/src/features/page/Page.tsx @@ -1,4 +1,4 @@ -import { PageIconType, serializedEditorDataProps } from "@noctaCrdt/Interfaces"; +import { PageIconType, serializedEditorDataProps } from "@noctaCrdt/types/Interfaces"; import { motion, AnimatePresence } from "framer-motion"; import { useEffect, useState } from "react"; import { Editor } from "@features/editor/Editor"; diff --git a/client/src/features/page/components/PageTitle/PageTitle.tsx b/client/src/features/page/components/PageTitle/PageTitle.tsx index 9b33674..4d394c2 100644 --- a/client/src/features/page/components/PageTitle/PageTitle.tsx +++ b/client/src/features/page/components/PageTitle/PageTitle.tsx @@ -1,4 +1,4 @@ -import { PageIconType } from "@noctaCrdt/Interfaces"; +import { PageIconType } from "@noctaCrdt/types/Interfaces"; import { iconComponents } from "@src/constants/PageIconButton.config"; import { pageTitleContainer, pageTitle } from "./PageTitle.style"; diff --git a/client/src/features/workSpace/hooks/usePagesManage.ts b/client/src/features/workSpace/hooks/usePagesManage.ts index a26984a..39e1bbd 100644 --- a/client/src/features/workSpace/hooks/usePagesManage.ts +++ b/client/src/features/workSpace/hooks/usePagesManage.ts @@ -1,11 +1,11 @@ +import { Page as CRDTPage } from "@noctaCrdt/Page"; +import { WorkSpace } from "@noctaCrdt/WorkSpace"; import { PageIconType, RemotePageCreateOperation, RemotePageUpdateOperation, serializedEditorDataProps, -} from "@noctaCrdt/Interfaces"; -import { Page as CRDTPage } from "@noctaCrdt/Page"; -import { WorkSpace } from "@noctaCrdt/WorkSpace"; +} from "@noctaCrdt/types/Interfaces"; import { useEffect, useState, useRef, useCallback } from "react"; import { PAGE, SIDE_BAR } from "@src/constants/size"; import { useSocketStore } from "@src/stores/useSocketStore"; diff --git a/client/src/stores/useSocketStore.ts b/client/src/stores/useSocketStore.ts index 3d74447..3c7241a 100644 --- a/client/src/stores/useSocketStore.ts +++ b/client/src/stores/useSocketStore.ts @@ -13,7 +13,7 @@ import { CursorPosition, WorkSpaceSerializedProps, WorkspaceListItem, -} from "@noctaCrdt/Interfaces"; +} from "@noctaCrdt/types/Interfaces"; import { io, Socket } from "socket.io-client"; import { create } from "zustand"; import { useToastStore } from "./useToastStore"; diff --git a/client/src/types/markdown.ts b/client/src/types/markdown.ts index 44d7d88..d4c91c1 100644 --- a/client/src/types/markdown.ts +++ b/client/src/types/markdown.ts @@ -1,4 +1,4 @@ -import { ElementType } from "@noctaCrdt/Interfaces"; +import { ElementType } from "@noctaCrdt/types/Interfaces"; export interface MarkdownElement { type: ElementType; diff --git a/client/src/types/page.ts b/client/src/types/page.ts index 9facdc0..3fd9ad1 100644 --- a/client/src/types/page.ts +++ b/client/src/types/page.ts @@ -1,4 +1,4 @@ -import { serializedEditorDataProps, PageIconType } from "@noctaCrdt/Interfaces"; +import { serializedEditorDataProps, PageIconType } from "@noctaCrdt/types/Interfaces"; export interface Page { id: string; diff --git a/server/src/workspace/workspace.controller.ts b/server/src/workspace/workspace.controller.ts index 8d4800b..afe7896 100644 --- a/server/src/workspace/workspace.controller.ts +++ b/server/src/workspace/workspace.controller.ts @@ -4,7 +4,7 @@ import { ApiTags } from "@nestjs/swagger"; import { JwtAuthGuard } from "../auth/guards/jwt-auth.guard"; import { Request as ExpressRequest } from "express"; import { Workspace } from "./schemas/workspace.schema"; -import { WorkspaceListItem } from "@noctaCrdt/Interfaces"; +import { WorkspaceListItem } from "@noctaCrdt/types/Interfaces"; @ApiTags("workspace") @UseGuards(JwtAuthGuard) diff --git a/server/src/workspace/workspace.gateway.ts b/server/src/workspace/workspace.gateway.ts index 53834d2..224683d 100644 --- a/server/src/workspace/workspace.gateway.ts +++ b/server/src/workspace/workspace.gateway.ts @@ -23,7 +23,7 @@ import { RemoteBlockCheckboxOperation, RemoteCharUpdateOperation, CursorPosition, -} from "@noctaCrdt/Interfaces"; +} from "@noctaCrdt/types/Interfaces"; import { Logger } from "@nestjs/common"; import { nanoid } from "nanoid"; import { Page } from "@noctaCrdt/Page"; diff --git a/server/src/workspace/workspace.service.ts b/server/src/workspace/workspace.service.ts index 8405f99..ac77c05 100644 --- a/server/src/workspace/workspace.service.ts +++ b/server/src/workspace/workspace.service.ts @@ -1,10 +1,10 @@ import { Injectable, OnModuleInit, Logger } from "@nestjs/common"; import { InjectModel } from "@nestjs/mongoose"; import { Workspace, WorkspaceDocument } from "./schemas/workspace.schema"; -import { WorkSpace as CRDTWorkSpace } from "@noctaCrdt/WorkSpace"; +import { WorkSpace as CRDTWorkSpace } from "@noctaCrdt/Workspace"; import { Model } from "mongoose"; import { Server } from "socket.io"; -import { WorkSpaceSerializedProps, WorkspaceListItem } from "@noctaCrdt/Interfaces"; +import { WorkSpaceSerializedProps, WorkspaceListItem } from "@noctaCrdt/types/Interfaces"; import { Page } from "@noctaCrdt/Page"; import { Block } from "@noctaCrdt/Node"; import { BlockId } from "@noctaCrdt/NodeId"; From c8977182865e3f3eeffbd8f048dca0558f16ba56 Mon Sep 17 00:00:00 2001 From: hyonun321 Date: Tue, 14 Jan 2025 18:14:07 +0900 Subject: [PATCH 05/13] =?UTF-8?q?feat:=20TombStone=20=EA=B0=80=EB=B9=84?= =?UTF-8?q?=EC=A7=80=20=EC=BB=AC=EB=A0=89=ED=8C=85=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - clearDeleteObject 메소드 서버쪽에 추가 --- server/src/workspace/workspace.service.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/server/src/workspace/workspace.service.ts b/server/src/workspace/workspace.service.ts index ac77c05..1b8cec2 100644 --- a/server/src/workspace/workspace.service.ts +++ b/server/src/workspace/workspace.service.ts @@ -59,6 +59,8 @@ export class WorkSpaceService implements OnModuleInit { const clientCount = room ? room.size : 0; // 연결된 클라이언트가 없으면 DB에 저장하고 메모리에서 제거 if (clientCount === 0) { + // const newWorkspace = await this.clearDeletedObject(workspace); + // const serializedData = newWorkspace.serialize(); const serializedData = workspace.serialize(); // 스키마에 맞게 데이터 변환 const workspaceData = { @@ -92,6 +94,18 @@ export class WorkSpaceService implements OnModuleInit { } } + async clearDeletedObject(workspace: CRDTWorkSpace): Promise { + const newWorkspace = workspace; + newWorkspace.pageList.forEach((page) => { + page.crdt.LinkedList.spread().forEach((block) => { + if (block.deleted) return; + block.crdt.LinkedList.clearDeletedNode(); + }); + page.crdt.LinkedList.clearDeletedNode(); + }); + return newWorkspace; + } + async getWorkspace(workspaceId: string): Promise { // 인메모리에서 먼저 찾기 const cachedWorkspace = this.workspaces.get(workspaceId); From 4f7b642695cbe72cc5997a413a3c6406c3850570 Mon Sep 17 00:00:00 2001 From: hyonun321 Date: Tue, 14 Jan 2025 18:14:48 +0900 Subject: [PATCH 06/13] =?UTF-8?q?refactor:=20TombStone=20=EC=97=AD?= =?UTF-8?q?=EC=A7=81=EB=A0=AC=ED=99=94=20=EA=B3=BC=EC=A0=95=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EB=88=84=EB=9D=BD=EB=90=9C=20deleted=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- @noctaCrdt/src/Node.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/@noctaCrdt/src/Node.ts b/@noctaCrdt/src/Node.ts index d157dea..c327d03 100644 --- a/@noctaCrdt/src/Node.ts +++ b/@noctaCrdt/src/Node.ts @@ -84,6 +84,7 @@ export class Block extends Node { static deserialize(data: any): Block { const id = BlockId.deserialize(data.id); const block = new Block(data.value, id); + block.deleted = data.deleted; block.next = data.next ? BlockId.deserialize(data.next) : null; block.prev = data.prev ? BlockId.deserialize(data.prev) : null; block.type = data.type; @@ -121,6 +122,7 @@ export class Char extends Node { static deserialize(data: any): Char { const id = CharId.deserialize(data.id); const char = new Char(data.value, id); + char.deleted = data.deleted; char.next = data.next ? CharId.deserialize(data.next) : null; char.prev = data.prev ? CharId.deserialize(data.prev) : null; char.style = data.style ? data.style : []; From b5126c96ba0c4d06c631c906a1f4e92eb360ad79 Mon Sep 17 00:00:00 2001 From: hyonun321 Date: Tue, 14 Jan 2025 18:18:53 +0900 Subject: [PATCH 07/13] =?UTF-8?q?refactor:=20=EA=B0=80=EB=B9=84=EC=A7=80?= =?UTF-8?q?=20=EC=BB=AC=EB=A0=89=ED=8C=85=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DB에 node들 저장되지 않게 처리 --- server/src/workspace/workspace.service.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/server/src/workspace/workspace.service.ts b/server/src/workspace/workspace.service.ts index 1b8cec2..d95ed6b 100644 --- a/server/src/workspace/workspace.service.ts +++ b/server/src/workspace/workspace.service.ts @@ -59,9 +59,8 @@ export class WorkSpaceService implements OnModuleInit { const clientCount = room ? room.size : 0; // 연결된 클라이언트가 없으면 DB에 저장하고 메모리에서 제거 if (clientCount === 0) { - // const newWorkspace = await this.clearDeletedObject(workspace); - // const serializedData = newWorkspace.serialize(); - const serializedData = workspace.serialize(); + const newWorkspace = await this.clearDeletedObject(workspace); + const serializedData = newWorkspace.serialize(); // 스키마에 맞게 데이터 변환 const workspaceData = { id: roomId, From f52241e5a4d083ed1c0e24df3527fa847be01ee2 Mon Sep 17 00:00:00 2001 From: hyonun321 Date: Wed, 15 Jan 2025 13:49:48 +0900 Subject: [PATCH 08/13] =?UTF-8?q?build:=20noctaCrdt=20=EA=B5=AC=EC=A1=B0?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EC=97=90=EB=94=B0=EB=A5=B8=20jest.config.ts?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/jest.config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/jest.config.ts b/server/jest.config.ts index 05c0c2b..7e2d27a 100644 --- a/server/jest.config.ts +++ b/server/jest.config.ts @@ -21,8 +21,8 @@ const config: Config = { transformIgnorePatterns: ["/node_modules/(?!(nanoid)/)", "/node_modules/(?!@noctaCrdt)"], extensionsToTreatAsEsm: [".ts"], moduleNameMapper: { - "^@noctaCrdt$": "/../@noctaCrdt/dist/Crdt.js", - "^@noctaCrdt/(.*)$": "/../@noctaCrdt/dist/$1.js", + "^@noctaCrdt$": "/../@noctaCrdt/dist/src/Crdt.js", + "^@noctaCrdt/(.*)$": "/../@noctaCrdt/dist/src/$1.js", "^nanoid$": require.resolve("nanoid"), }, }; From 510b49ef58199b9a731c1d540d906b51c1cb5d91 Mon Sep 17 00:00:00 2001 From: hyonun321 Date: Wed, 15 Jan 2025 13:55:51 +0900 Subject: [PATCH 09/13] =?UTF-8?q?chore:=20=EC=84=9C=EB=B2=84=20=EB=B9=8C?= =?UTF-8?q?=EB=93=9C=20=EC=8A=A4=ED=81=AC=EB=A6=BD=ED=8A=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/package.json b/server/package.json index 4f827e6..607409b 100644 --- a/server/package.json +++ b/server/package.json @@ -15,7 +15,7 @@ "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", "lint": "eslint \"src/**/*.{ts,tsx}\" --fix", - "build:noctaCrdt": "pnpm --filter @noctaCrdt build", + "build:noctaCrdt": "pnpm --filter \"@noctaCrdt\" build", "test": "pnpm build:noctaCrdt && jest", "test:watch": "jest --watch", "test:cov": "jest --coverage", From 2d5210b15da976c813ba37794f7201a18c445ef5 Mon Sep 17 00:00:00 2001 From: hyonun321 Date: Wed, 15 Jan 2025 14:21:39 +0900 Subject: [PATCH 10/13] =?UTF-8?q?chore:=20=EB=B9=8C=EB=93=9C=EC=97=90=20?= =?UTF-8?q?=EC=83=81=EC=9C=84=20=EB=94=94=EB=A0=89=ED=86=A0=EB=A6=AC=20?= =?UTF-8?q?=EA=B2=BD=EB=A1=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/webpack.config.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/webpack.config.js b/server/webpack.config.js index 53048c5..0310622 100644 --- a/server/webpack.config.js +++ b/server/webpack.config.js @@ -4,8 +4,12 @@ module.exports = { mode: "development", resolve: { extensions: [".ts", ".js"], + modules: [ + path.resolve(__dirname, ".."), // 상위 디렉토리도 모듈 검색 경로에 추가 + "node_modules", + ], alias: { - "@noctaCrdt": path.resolve(__dirname, "../@noctaCrdt/dist/src"), + "@noctaCrdt": path.resolve(process.cwd(), "../@noctaCrdt/dist/src"), }, }, module: { From a7485499743a0b536b131cc32ec12c3dc6c67c30 Mon Sep 17 00:00:00 2001 From: hyonun321 Date: Wed, 15 Jan 2025 14:28:20 +0900 Subject: [PATCH 11/13] =?UTF-8?q?chore:=20jest=20=EC=83=81=EB=8C=80?= =?UTF-8?q?=EA=B2=BD=EB=A1=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/jest.config.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/jest.config.ts b/server/jest.config.ts index 7e2d27a..1a7fce3 100644 --- a/server/jest.config.ts +++ b/server/jest.config.ts @@ -1,5 +1,5 @@ import type { Config } from "jest"; - +const path = require("path"); const config: Config = { moduleFileExtensions: ["js", "json", "ts"], rootDir: ".", @@ -22,9 +22,10 @@ const config: Config = { extensionsToTreatAsEsm: [".ts"], moduleNameMapper: { "^@noctaCrdt$": "/../@noctaCrdt/dist/src/Crdt.js", - "^@noctaCrdt/(.*)$": "/../@noctaCrdt/dist/src/$1.js", + "^@noctaCrdt/(.*)$": path.join(__dirname, "../@noctaCrdt/dist/src/$1"), "^nanoid$": require.resolve("nanoid"), }, + modulePaths: [path.join(__dirname, ".."), "node_modules"], }; export default config; From f18f8e94fc17798109ba7f9466d64b0c84fec382 Mon Sep 17 00:00:00 2001 From: hyonun321 Date: Wed, 15 Jan 2025 14:31:46 +0900 Subject: [PATCH 12/13] =?UTF-8?q?chore:=20lint=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/jest.config.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/jest.config.ts b/server/jest.config.ts index 1a7fce3..d7175d6 100644 --- a/server/jest.config.ts +++ b/server/jest.config.ts @@ -1,5 +1,6 @@ import type { Config } from "jest"; -const path = require("path"); +import path from "path"; // require 대신 import 사용 + const config: Config = { moduleFileExtensions: ["js", "json", "ts"], rootDir: ".", From c43f6dff7503f2160154d1fb0f160463e204a823 Mon Sep 17 00:00:00 2001 From: hyonun321 Date: Wed, 15 Jan 2025 14:38:52 +0900 Subject: [PATCH 13/13] =?UTF-8?q?chore:=20Workspace=20->=20WorkSpace?= =?UTF-8?q?=EB=A1=9C=20=EB=8C=80=EB=AC=B8=EC=9E=90=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/workspace/workspace.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/workspace/workspace.service.ts b/server/src/workspace/workspace.service.ts index d95ed6b..7c705e8 100644 --- a/server/src/workspace/workspace.service.ts +++ b/server/src/workspace/workspace.service.ts @@ -1,7 +1,7 @@ import { Injectable, OnModuleInit, Logger } from "@nestjs/common"; import { InjectModel } from "@nestjs/mongoose"; import { Workspace, WorkspaceDocument } from "./schemas/workspace.schema"; -import { WorkSpace as CRDTWorkSpace } from "@noctaCrdt/Workspace"; +import { WorkSpace as CRDTWorkSpace } from "@noctaCrdt/WorkSpace"; import { Model } from "mongoose"; import { Server } from "socket.io"; import { WorkSpaceSerializedProps, WorkspaceListItem } from "@noctaCrdt/types/Interfaces";