참고
- 파일 브라우저 만들기
- chonky 기본 설정 확인하기
- 액션 추가하고 다크 모드 구현하기
- 커스텀 액션 추가하기
- Chonky 파일 맵 만들기
- 리포지토리의 폴더 정보 저장하기
- 깃허브 리포지토리를 파일 브라우저로 불러오기
- useNavigate로 Toast UI Editor 이동하기
Chonky 브라우저에서 사용하는 예시 demo.json은 다음과 같다.
1) 최상위 폴더의 ID인 rootFolderId가 필요하다.
2) fileMap에서 전체 폴더/파일에 대한 정보가 있다.
3) 폴더의 경우, isDir과 childrenIds, childrenCount 정보가 있다.
4) 파일의 경우, size와 isHidden 정보가 있다.
5) 폴더와 파일 모두 parentId를 알고 있어야 한다.
{
"rootFolderId": "qwerty123456",
"fileMap": {
"qwerty123456": {
"id": "qwerty123456",
"name": "Chonkyz Demo",
"isDir": true,
"childrenIds": [
"e598a85f843c",
"b53aa057fad1",
"b6667221f24b",
"002705459ca4",
"a9fd7c8a04db",
"549c1f93247a",
"zFe",
"zJr",
"zHy",
"vCt"
],
"childrenCount": 6
},
...
"9514a3d74d57": {
"id": "9514a3d74d57",
"name": ".eslintrc.js",
"isHidden": true,
"size": 2293,
"modDate": "2020-10-20T03:11:50.570Z",
"parentId": "e598a85f843c"
},
...
요약하면 폴더는 아래와 같은 정보를 가져야 한다.
let dir = {
id: "",
name: "",
isDir: true,
modDate: "",
childrenIds: [],
childrenCount: 0,
parentId: "",
};
파일은 isHidden과 같은 정보는 제외하고 아래의 정보를 가지도록 Node JS로 구현해 보자.
let file = {
id: "",
name: "",
size: "",
modDate: "",
parentId: "",
};
구현
아래의 폴더, 파일이 루트 폴더(= myfiles)에 있다고 가정하자.
파일이 필요하면 아래 zip 파일을 참고하면 된다.
먼저 각 폴더, 파일마다 recursive하게 유일한 ID를 만들자.
makePathIdMap에서 directory인 경우, 해당 폴더에 들어가서 ID를 만들면 된다.
ID가 유일하기만 하면 되기 때문에, 순서는 상관없고 pathIdMap[전체 경로 이름] = ID 로 저장하였다.
전체 경로는 path.join을 이용해서 얻을 수 있다.
// make_dir_map.js
let pathIdMap = {};
let id_counter = 0;
const makePathIdMap = (dir) => {
pathIdMap[dir] = `id_${id_counter++}`;
let items = fs.readdirSync(dir);
for (let item of items) {
let itemPath = path.join(dir, item);
let stats = fs.statSync(itemPath);
if (stats.isDirectory()) {
makePathIdMap(itemPath);
} else if (stats.isFile()) {
pathIdMap[itemPath] = `id_${id_counter++}`;
}
}
};
childrenCount와 childrenIds는 다음과 같이 구할 수 있다.
const getChildrenCount = (dir) => {
let items = fs.readdirSync(dir);
return items.length;
};
const getChildrenIds = (dir) => {
let ret = [];
let items = fs.readdirSync(dir);
for (let item of items) {
let itemPath = path.join(dir, item);
ret.push(pathIdMap[itemPath]);
}
return ret;
};
이제 makeChonkyMap을 이용해서 위와 마찬가지로 recursive하게 chonky map을 만들면 된다.
id, childrenIds, childrenCount, parentId는 미리 만들어두었고, 그 외 내용은 statSync를 이용해서 얻을 수 있다.
폴더인 경우에만 isDir : true로 설정하고, 파일의 경우에는 size를 구하도록 하였다.
let makeChonkyMap = (dir) => {
let items = fs.readdirSync(dir);
for (let item of items) {
let itemPath = path.join(dir, item);
let stats = fs.statSync(itemPath);
// 현재 폴더에 있는 폴더와 파일을 먼저 처리
if (stats.isDirectory()) {
let obj = {
id: pathIdMap[itemPath],
name: item,
isDir: true,
modDate: fs.statSync(itemPath).mtime,
childrenIds: getChildrenIds(itemPath),
childrenCount: getChildrenCount(itemPath),
parentId: pathIdMap[dir],
};
fileMap[pathIdMap[itemPath]] = obj;
makeChonkyMap(itemPath);
} else if (stats.isFile()) {
let obj = {
id: pathIdMap[itemPath],
name: item,
size: fs.statSync(itemPath).size,
modDate: fs.statSync(itemPath).mtime,
parentId: pathIdMap[dir],
};
fileMap[pathIdMap[itemPath]] = obj;
}
}
};
이제 설정한 루트 폴더(dirPath)에 대해 map을 만들면 된다.
makePathIdMap으로 ID를 먼저 만들고, fileMap에 root 폴더를 초기화하였다.
마지막으로 dir_map.json을 만들 때 rootFolderId와 fileMap을 write하면 된다.
makePathIdMap(dirPath);
let rootFolderId = `id_0`;
let initCount = getChildrenCount(dirPath);
let initChildrenIds = getChildrenIds(dirPath);
let fileMap = {
id_0: {
id: rootFolderId,
name: "myfiles", // root folder name
isDir: true,
childrenIds: initChildrenIds,
childrenCount: initCount,
},
};
makeChonkyMap(dirPath);
let json = JSON.stringify({ rootFolderId, fileMap }, null, 4);
fs.writeFileSync("dir_map.json", json, "utf-8");
예시의 루트 폴더에 대한 chonky file map은 다음과 같다.
{
"rootFolderId": "id_0",
"fileMap": {
"id_0": {
"id": "id_0",
"name": "myfiles",
"isDir": true,
"childrenIds": [
"id_1",
"id_5",
"id_11",
"id_12",
"id_13"
],
"childrenCount": 5
},
"id_1": {
"id": "id_1",
"name": "handsontable",
"isDir": true,
"modDate": "2024-03-16T08:55:22.649Z",
"childrenIds": [
"id_2",
"id_3",
"id_4"
],
"childrenCount": 3,
"parentId": "id_0"
},
"id_2": {
"id": "id_2",
"name": "data1.json",
"size": 0,
"modDate": "2024-03-16T08:55:06.480Z",
"parentId": "id_1"
},
"id_3": {
"id": "id_3",
"name": "data2.json",
"size": 0,
"modDate": "2024-03-16T08:55:06.480Z",
"parentId": "id_1"
},
"id_4": {
"id": "id_4",
"name": "data3.json",
"size": 0,
"modDate": "2024-03-16T08:55:06.480Z",
"parentId": "id_1"
},
"id_5": {
"id": "id_5",
"name": "markdown",
"isDir": true,
"modDate": "2024-03-16T08:54:25.907Z",
"childrenIds": [
"id_6",
"id_10"
],
"childrenCount": 2,
"parentId": "id_0"
},
"id_6": {
"id": "id_6",
"name": "information",
"isDir": true,
"modDate": "2024-03-16T08:54:40.968Z",
"childrenIds": [
"id_7",
"id_8",
"id_9"
],
"childrenCount": 3,
"parentId": "id_5"
},
"id_7": {
"id": "id_7",
"name": "info1.md",
"size": 14,
"modDate": "2023-06-02T06:45:33.755Z",
"parentId": "id_6"
},
"id_8": {
"id": "id_8",
"name": "info2.md",
"size": 14,
"modDate": "2023-06-02T06:45:33.755Z",
"parentId": "id_6"
},
"id_9": {
"id": "id_9",
"name": "info3.md",
"size": 14,
"modDate": "2023-06-02T06:45:33.755Z",
"parentId": "id_6"
},
"id_10": {
"id": "id_10",
"name": "Manual.md",
"size": 14,
"modDate": "2023-06-02T06:45:33.755Z",
"parentId": "id_5"
},
"id_11": {
"id": "id_11",
"name": "package-lock.json",
"size": 3,
"modDate": "2023-06-02T06:44:17.496Z",
"parentId": "id_0"
},
"id_12": {
"id": "id_12",
"name": "package.json",
"size": 3,
"modDate": "2023-06-02T06:44:07.929Z",
"parentId": "id_0"
},
"id_13": {
"id": "id_13",
"name": "README.md",
"size": 3,
"modDate": "2023-06-02T06:44:17.496Z",
"parentId": "id_0"
}
}
}
dir_map.json을 Chonky Browser의 demo.json에 붙여넣자.
정상적으로 폴더가 반영되는 것을 알 수 있다.
전체 코드는 다음과 같다.
const fs = require("fs");
const path = require("path");
const dirPath = "D:\\github\\node-server\\macro\\myfiles"; // window path
let pathIdMap = {};
let id_counter = 0;
const makePathIdMap = (dir) => {
pathIdMap[dir] = `id_${id_counter++}`;
let items = fs.readdirSync(dir);
for (let item of items) {
let itemPath = path.join(dir, item);
let stats = fs.statSync(itemPath);
if (stats.isDirectory()) {
makePathIdMap(itemPath);
} else if (stats.isFile()) {
pathIdMap[itemPath] = `id_${id_counter++}`;
}
}
};
const getChildrenCount = (dir) => {
let items = fs.readdirSync(dir);
return items.length;
};
const getChildrenIds = (dir) => {
let ret = [];
let items = fs.readdirSync(dir);
for (let item of items) {
let itemPath = path.join(dir, item);
ret.push(pathIdMap[itemPath]);
}
return ret;
};
let makeChonkyMap = (dir) => {
let items = fs.readdirSync(dir);
for (let item of items) {
let itemPath = path.join(dir, item);
let stats = fs.statSync(itemPath);
// 현재 폴더에 있는 폴더와 파일을 먼저 처리
if (stats.isDirectory()) {
let obj = {
id: pathIdMap[itemPath],
name: item,
isDir: true,
modDate: fs.statSync(itemPath).mtime,
childrenIds: getChildrenIds(itemPath),
childrenCount: getChildrenCount(itemPath),
parentId: pathIdMap[dir],
};
fileMap[pathIdMap[itemPath]] = obj;
makeChonkyMap(itemPath);
} else if (stats.isFile()) {
let obj = {
id: pathIdMap[itemPath],
name: item,
size: fs.statSync(itemPath).size,
modDate: fs.statSync(itemPath).mtime,
parentId: pathIdMap[dir],
};
fileMap[pathIdMap[itemPath]] = obj;
}
}
};
makePathIdMap(dirPath);
let rootFolderId = `id_0`;
let initCount = getChildrenCount(dirPath);
let initChildrenIds = getChildrenIds(dirPath);
let fileMap = {
id_0: {
id: rootFolderId,
name: "myfiles",
isDir: true,
childrenIds: initChildrenIds,
childrenCount: initCount,
},
};
makeChonkyMap(dirPath);
let json = JSON.stringify({ rootFolderId, fileMap }, null, 4);
fs.writeFileSync("dir_map.json", json, "utf-8");
댓글