@o2b/meta 1.0.0-dev.0 → 1.0.0-dev.10
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/cli.js +142 -108
- package/dist/cli.js.map +1 -1
- package/dist/index.d.mts +5 -7
- package/dist/index.d.ts +5 -7
- package/dist/index.js +132 -104
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +134 -102
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -4
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
2
3
|
var __create = Object.create;
|
|
3
4
|
var __defProp = Object.defineProperty;
|
|
4
5
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -28,7 +29,7 @@ var import_cac = __toESM(require("cac"));
|
|
|
28
29
|
// package.json
|
|
29
30
|
var package_default = {
|
|
30
31
|
name: "@o2b/meta",
|
|
31
|
-
version: "1.0.0-
|
|
32
|
+
version: "1.0.0-dev.10",
|
|
32
33
|
description: "filetree generator for o2b",
|
|
33
34
|
main: "./dist/index.js",
|
|
34
35
|
module: "./dist/index.mjs",
|
|
@@ -41,12 +42,13 @@ var package_default = {
|
|
|
41
42
|
}
|
|
42
43
|
},
|
|
43
44
|
bin: {
|
|
44
|
-
"o2b-meta": "
|
|
45
|
+
"o2b-meta": "dist/cli.js"
|
|
45
46
|
},
|
|
46
47
|
scripts: {
|
|
47
48
|
build: "tsup",
|
|
48
49
|
test: "vitest",
|
|
49
|
-
"test:run": "vitest run"
|
|
50
|
+
"test:run": "vitest run",
|
|
51
|
+
dev: 'tsup src/cli.ts --watch --onSuccess "node dist/cli.js ./tests/fixtures/valid-vault --destPath ./tests/fixtures/'
|
|
50
52
|
},
|
|
51
53
|
keywords: [],
|
|
52
54
|
author: "goonco",
|
|
@@ -61,154 +63,186 @@ var package_default = {
|
|
|
61
63
|
"@vitest/ui": "^4.0.16",
|
|
62
64
|
tsup: "^8.5.1",
|
|
63
65
|
typescript: "^5.9.3",
|
|
64
|
-
|
|
66
|
+
"vite-tsconfig-paths": "^6.0.5",
|
|
67
|
+
vitest: "^4.0.16",
|
|
68
|
+
zod: "^3.25.76"
|
|
65
69
|
},
|
|
66
70
|
dependencies: {
|
|
67
71
|
cac: "^6.7.14",
|
|
68
72
|
"gray-matter": "^4.0.3",
|
|
69
73
|
ignore: "^7.0.5",
|
|
70
74
|
lilconfig: "^3.1.3"
|
|
75
|
+
},
|
|
76
|
+
peerDependencies: {
|
|
77
|
+
zod: "^3.23.8"
|
|
71
78
|
}
|
|
72
79
|
};
|
|
73
80
|
|
|
74
|
-
// src/core/sync/mirror.ts
|
|
75
|
-
var import_node_fs = require("fs");
|
|
76
|
-
var import_node_path = require("path");
|
|
77
|
-
function mirror(filetree, srcPath, destPath) {
|
|
78
|
-
function aux(filetree2) {
|
|
79
|
-
const from = (0, import_node_path.join)(srcPath, filetree2.path);
|
|
80
|
-
const to = (0, import_node_path.join)(destPath, filetree2.path);
|
|
81
|
-
if (filetree2.type === "dir") {
|
|
82
|
-
if (!(0, import_node_fs.existsSync)(to)) (0, import_node_fs.mkdirSync)(to, { recursive: true });
|
|
83
|
-
filetree2.files.forEach((file) => {
|
|
84
|
-
aux(file);
|
|
85
|
-
});
|
|
86
|
-
filetree2.dirs.forEach((dir) => {
|
|
87
|
-
aux(dir);
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
if (filetree2.type === "file") {
|
|
91
|
-
(0, import_node_fs.copyFileSync)(from, to);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
aux(filetree);
|
|
95
|
-
generateFileTree(filetree, destPath);
|
|
96
|
-
}
|
|
97
|
-
function generateFileTree(filetree, destPath) {
|
|
98
|
-
const jsonFilePath = (0, import_node_path.join)(destPath, "o2bFileTree.json");
|
|
99
|
-
(0, import_node_fs.writeFileSync)(jsonFilePath, JSON.stringify(filetree, null, 2), "utf-8");
|
|
100
|
-
}
|
|
101
|
-
|
|
102
81
|
// src/core/sync/scan.ts
|
|
103
|
-
var
|
|
104
|
-
var
|
|
82
|
+
var import_node_fs = require("fs");
|
|
83
|
+
var import_node_path2 = require("path");
|
|
105
84
|
var import_gray_matter = __toESM(require("gray-matter"));
|
|
106
85
|
|
|
107
|
-
// src/utils/
|
|
108
|
-
var
|
|
109
|
-
|
|
110
|
-
|
|
86
|
+
// src/utils/path.ts
|
|
87
|
+
var import_node_path = require("path");
|
|
88
|
+
|
|
89
|
+
// src/types/path.ts
|
|
90
|
+
var import_zod = __toESM(require("zod"));
|
|
91
|
+
var absPath = import_zod.default.string().brand();
|
|
92
|
+
var relPath = import_zod.default.string().brand();
|
|
111
93
|
|
|
112
94
|
// src/utils/path.ts
|
|
113
|
-
var import_node_fs2 = require("fs");
|
|
114
|
-
var import_node_path2 = require("path");
|
|
115
95
|
function toAbsPath(path) {
|
|
116
|
-
if (!(0,
|
|
117
|
-
return path;
|
|
96
|
+
if (!(0, import_node_path.isAbsolute)(path)) path = (0, import_node_path.resolve)(path);
|
|
97
|
+
return absPath.parse(path);
|
|
118
98
|
}
|
|
119
99
|
function toRelPath(from, to) {
|
|
120
|
-
return (0,
|
|
100
|
+
return relPath.parse((0, import_node_path.relative)(from, to));
|
|
121
101
|
}
|
|
122
|
-
function
|
|
123
|
-
|
|
124
|
-
if (!stat.isDirectory()) return false;
|
|
125
|
-
const obsMetaDir = (0, import_node_fs2.readdirSync)(path, { withFileTypes: true }).filter((node) => node.isDirectory()).filter((node) => node.name === ".obsidian");
|
|
126
|
-
if (obsMetaDir.length !== 1) return false;
|
|
127
|
-
return true;
|
|
102
|
+
function joinToAbs(base, ...parts) {
|
|
103
|
+
return absPath.parse((0, import_node_path.join)(base, ...parts));
|
|
128
104
|
}
|
|
129
105
|
|
|
130
|
-
// src/utils/ignore.ts
|
|
131
|
-
var VaultIgnorer = class {
|
|
132
|
-
ign;
|
|
133
|
-
rootPath;
|
|
134
|
-
constructor(rootPath) {
|
|
135
|
-
this.rootPath = rootPath;
|
|
136
|
-
this.ign = (0, import_ignore.default)();
|
|
137
|
-
this.ign.add([".*"]);
|
|
138
|
-
}
|
|
139
|
-
load() {
|
|
140
|
-
const ignorePath = (0, import_node_path3.join)(this.rootPath, ".o2bignore");
|
|
141
|
-
if ((0, import_node_fs3.existsSync)(ignorePath))
|
|
142
|
-
this.ign.add((0, import_node_fs3.readFileSync)(ignorePath).toString());
|
|
143
|
-
}
|
|
144
|
-
ignore(path) {
|
|
145
|
-
if (path === this.rootPath) return false;
|
|
146
|
-
if (!(0, import_node_fs3.existsSync)(path)) throw new Error("somethings wrong!");
|
|
147
|
-
const stat = (0, import_node_fs3.lstatSync)(path);
|
|
148
|
-
const relPath = toRelPath(this.rootPath, path) + (stat.isDirectory() ? "/" : "");
|
|
149
|
-
return this.ign.ignores(relPath);
|
|
150
|
-
}
|
|
151
|
-
};
|
|
152
|
-
|
|
153
106
|
// src/core/sync/scan.ts
|
|
154
107
|
function scan(rootPath, ign) {
|
|
155
|
-
function aux(
|
|
156
|
-
if (ign == null ? void 0 : ign.
|
|
157
|
-
const stat = (0,
|
|
158
|
-
const name = (0,
|
|
159
|
-
if (stat.
|
|
160
|
-
const { dirs, files } = (0, import_node_fs4.readdirSync)(path).reduce(
|
|
161
|
-
(acc, entry) => {
|
|
162
|
-
const node = aux(toAbsPath((0, import_node_path4.join)(path, entry)));
|
|
163
|
-
if ((node == null ? void 0 : node.type) === "dir") acc.dirs.push(node);
|
|
164
|
-
if ((node == null ? void 0 : node.type) === "file") acc.files.push(node);
|
|
165
|
-
return acc;
|
|
166
|
-
},
|
|
167
|
-
{
|
|
168
|
-
dirs: [],
|
|
169
|
-
files: []
|
|
170
|
-
}
|
|
171
|
-
);
|
|
108
|
+
function aux(curPath) {
|
|
109
|
+
if (ign == null ? void 0 : ign.ignores(curPath)) return null;
|
|
110
|
+
const stat = (0, import_node_fs.lstatSync)(curPath);
|
|
111
|
+
const name = (0, import_node_path2.basename)(curPath);
|
|
112
|
+
if (stat.isFile() && name.endsWith(".md")) {
|
|
172
113
|
return {
|
|
173
|
-
path:
|
|
114
|
+
path: curPath,
|
|
174
115
|
name,
|
|
175
|
-
type: "
|
|
176
|
-
|
|
177
|
-
files
|
|
116
|
+
type: "file",
|
|
117
|
+
frontmatter: fetchFrontmatter(curPath, stat.mtime)
|
|
178
118
|
};
|
|
179
119
|
}
|
|
180
|
-
if (stat.
|
|
120
|
+
if (stat.isDirectory()) {
|
|
121
|
+
const dirs = [];
|
|
122
|
+
const files = [];
|
|
123
|
+
const entries = (0, import_node_fs.readdirSync)(curPath);
|
|
124
|
+
for (const entry of entries) {
|
|
125
|
+
const nextPath = toAbsPath((0, import_node_path2.join)(curPath, entry));
|
|
126
|
+
const node = aux(nextPath);
|
|
127
|
+
if ((node == null ? void 0 : node.type) === "dir") dirs.push(node);
|
|
128
|
+
if ((node == null ? void 0 : node.type) === "file") files.push(node);
|
|
129
|
+
}
|
|
181
130
|
return {
|
|
182
|
-
path:
|
|
131
|
+
path: curPath,
|
|
183
132
|
name,
|
|
184
|
-
type: "
|
|
185
|
-
|
|
133
|
+
type: "dir",
|
|
134
|
+
dirs,
|
|
135
|
+
files
|
|
186
136
|
};
|
|
187
137
|
}
|
|
188
138
|
return null;
|
|
189
139
|
}
|
|
140
|
+
if (!isObsidianDirectory(rootPath)) throw new Error("Not a vault!");
|
|
190
141
|
return aux(rootPath);
|
|
191
142
|
}
|
|
192
|
-
function
|
|
193
|
-
const
|
|
143
|
+
function isObsidianDirectory(path) {
|
|
144
|
+
const stat = (0, import_node_fs.lstatSync)(path);
|
|
145
|
+
if (!stat.isDirectory()) return false;
|
|
146
|
+
return (0, import_node_fs.existsSync)((0, import_node_path2.join)(path, ".obsidian"));
|
|
147
|
+
}
|
|
148
|
+
function fetchFrontmatter(path, mtime) {
|
|
149
|
+
const { data } = (0, import_gray_matter.default)((0, import_node_fs.readFileSync)(path, "utf-8"));
|
|
194
150
|
return {
|
|
195
|
-
title: (0,
|
|
151
|
+
title: (0, import_node_path2.basename)(path).replace(/\.md$/, ""),
|
|
196
152
|
description: "No description provided.",
|
|
197
|
-
last_modified:
|
|
198
|
-
|
|
199
|
-
...srcFrontmatter
|
|
153
|
+
last_modified: mtime.toISOString().split("T")[0],
|
|
154
|
+
...data
|
|
200
155
|
};
|
|
201
156
|
}
|
|
202
157
|
|
|
203
|
-
// src/
|
|
204
|
-
|
|
158
|
+
// src/core/sync/sanitizeAndMirror.ts
|
|
159
|
+
var import_gray_matter2 = __toESM(require("gray-matter"));
|
|
160
|
+
var import_node_fs2 = require("fs");
|
|
161
|
+
function sanitizeAndCopy(fileTree, srcPath, destPath) {
|
|
162
|
+
function aux(curNode) {
|
|
163
|
+
const path = sanitizePath(toRelPath(srcPath, curNode.path));
|
|
164
|
+
const to = joinToAbs(destPath, path);
|
|
165
|
+
if (!(0, import_node_fs2.existsSync)(to)) (0, import_node_fs2.mkdirSync)(to, { recursive: true });
|
|
166
|
+
const dirs = curNode.dirs.map((dir) => aux(dir));
|
|
167
|
+
const files = curNode.files.map((file) => {
|
|
168
|
+
const to2 = joinToAbs(
|
|
169
|
+
destPath,
|
|
170
|
+
sanitizePath(toRelPath(srcPath, file.path))
|
|
171
|
+
);
|
|
172
|
+
const fileContent = (0, import_node_fs2.readFileSync)(file.path, "utf-8");
|
|
173
|
+
const { data: existingData, content } = (0, import_gray_matter2.default)(fileContent);
|
|
174
|
+
const newFm = {
|
|
175
|
+
...file.frontmatter,
|
|
176
|
+
...existingData
|
|
177
|
+
};
|
|
178
|
+
(0, import_node_fs2.writeFileSync)(to2, import_gray_matter2.default.stringify(content, newFm));
|
|
179
|
+
return {
|
|
180
|
+
original: file.name,
|
|
181
|
+
sanitized: sanitize(file.name)
|
|
182
|
+
};
|
|
183
|
+
});
|
|
184
|
+
return {
|
|
185
|
+
path,
|
|
186
|
+
dirs,
|
|
187
|
+
files,
|
|
188
|
+
name: {
|
|
189
|
+
original: curNode.name,
|
|
190
|
+
sanitized: sanitize(curNode.name)
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
return aux(fileTree);
|
|
195
|
+
}
|
|
196
|
+
function sanitizePath(s) {
|
|
197
|
+
return s.split(/[\\/]/).map((segment) => sanitize(segment)).join("/");
|
|
198
|
+
}
|
|
199
|
+
function sanitize(s) {
|
|
200
|
+
return s.replace(/[\[\]#?%/:*?"<>|]/g, "").replace(/[\s_]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "").trim();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// src/utils/file.ts
|
|
204
|
+
var import_node_fs3 = require("fs");
|
|
205
|
+
var import_node_path3 = require("path");
|
|
206
|
+
function generateJson(content, name, destPath) {
|
|
207
|
+
const jsonFilePath = (0, import_node_path3.join)(destPath, `${name}.json`);
|
|
208
|
+
(0, import_node_fs3.writeFileSync)(jsonFilePath, JSON.stringify(content, null, 2), "utf-8");
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// src/utils/ignore.ts
|
|
212
|
+
var import_node_fs4 = require("fs");
|
|
213
|
+
var import_node_path4 = require("path");
|
|
214
|
+
var import_ignore = __toESM(require("ignore"));
|
|
215
|
+
var VaultIgnorer = class {
|
|
216
|
+
ign;
|
|
217
|
+
rootPath;
|
|
218
|
+
constructor(rootPath) {
|
|
219
|
+
this.rootPath = rootPath;
|
|
220
|
+
this.ign = (0, import_ignore.default)();
|
|
221
|
+
this.ign.add([".*"]);
|
|
222
|
+
}
|
|
223
|
+
load() {
|
|
224
|
+
const ignorePath = (0, import_node_path4.join)(this.rootPath, ".o2bignore");
|
|
225
|
+
if ((0, import_node_fs4.existsSync)(ignorePath))
|
|
226
|
+
this.ign.add((0, import_node_fs4.readFileSync)(ignorePath).toString());
|
|
227
|
+
}
|
|
228
|
+
ignores(path) {
|
|
229
|
+
if (path === this.rootPath) return false;
|
|
230
|
+
if (!(0, import_node_fs4.existsSync)(path)) throw new Error("somethings wrong!");
|
|
231
|
+
const stat = (0, import_node_fs4.lstatSync)(path);
|
|
232
|
+
const relPath2 = toRelPath(this.rootPath, path) + (stat.isDirectory() ? "/" : "");
|
|
233
|
+
return this.ign.ignores(relPath2);
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
// src/runO2B.ts
|
|
238
|
+
function runO2B(config) {
|
|
205
239
|
const { srcPath, destPath, ignore: ignore2 } = config;
|
|
206
|
-
if (!isObsidianDirectory(srcPath)) throw new Error("Not a obsidian vault");
|
|
207
240
|
const ign = new VaultIgnorer(srcPath);
|
|
208
241
|
if (ignore2) ign.load();
|
|
209
242
|
const fileTree = scan(srcPath, ign);
|
|
210
243
|
if (fileTree === null) throw new Error("No content in vault");
|
|
211
|
-
|
|
244
|
+
const sanitizedFileTree = sanitizeAndCopy(fileTree, srcPath, destPath);
|
|
245
|
+
generateJson(sanitizedFileTree, "file-tree", destPath);
|
|
212
246
|
}
|
|
213
247
|
|
|
214
248
|
// src/cli.ts
|
|
@@ -223,7 +257,7 @@ cli.command("<srcPath>", "target vault file").option("--destPath [destPath]", "d
|
|
|
223
257
|
const { destPath, ignore: ignore2, sync } = option;
|
|
224
258
|
const startTime = performance.now();
|
|
225
259
|
if (sync) {
|
|
226
|
-
|
|
260
|
+
runO2B({
|
|
227
261
|
srcPath: toAbsPath(srcPath),
|
|
228
262
|
destPath: toAbsPath(destPath),
|
|
229
263
|
ignore: ignore2
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../package.json","../src/core/sync/mirror.ts","../src/core/sync/scan.ts","../src/utils/ignore.ts","../src/utils/path.ts","../src/index.ts"],"sourcesContent":["import cac from \"cac\";\nimport pkg from \"../package.json\";\nimport main from \".\";\nimport { toAbsPath } from \"./utils\";\n\nconst cli = cac(\"o2b-meta\");\n\ncli\n\t.command(\"<srcPath>\", \"target vault file\")\n\t.option(\"--destPath [destPath]\", \"dist\", {\n\t\tdefault: \"./o2b\",\n\t})\n\t.option(\"--ignore\", \"read the .o2bIgnore if exists\", {\n\t\tdefault: true,\n\t})\n\t.option(\"--sync\", \"synchro\", {\n\t\tdefault: true,\n\t})\n\t.action((srcPath, option) => {\n\t\tconst { destPath, ignore, sync } = option;\n\n\t\tconst startTime = performance.now();\n\n\t\tif (sync) {\n\t\t\tmain({\n\t\t\t\tsrcPath: toAbsPath(srcPath),\n\t\t\t\tdestPath: toAbsPath(destPath),\n\t\t\t\tignore,\n\t\t\t});\n\t\t}\n\n\t\tconst endTime = performance.now();\n\t\tconst duration = (endTime - startTime).toFixed(2); // 소수점 2자리까지\n\n\t\tconsole.log(`\\n✅ Build Success!`);\n\t\tconsole.log(` Time: ${duration}ms`);\n\t\t// console.log(` Dest: ${config.destPath}\\n`);\n\t});\n\ncli.help();\ncli.version(pkg.version);\n\n// production에 대한 에러처리\ncli.parse();\n","{\n \"name\": \"@o2b/meta\",\n \"version\": \"1.0.0-alpha.0\",\n \"description\": \"filetree generator for o2b\",\n \"main\": \"./dist/index.js\",\n \"module\": \"./dist/index.mjs\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.mjs\",\n \"require\": \"./dist/index.js\"\n }\n },\n \"bin\": {\n \"o2b-meta\": \"./dist/cli.js\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"test\": \"vitest\",\n \"test:run\": \"vitest run\"\n },\n \"keywords\": [],\n \"author\": \"goonco\",\n \"license\": \"ISC\",\n \"type\": \"commonjs\",\n \"files\": [\n \"dist\"\n ],\n \"devDependencies\": {\n \"@biomejs/biome\": \"2.3.10\",\n \"@types/node\": \"^25.0.3\",\n \"@vitest/ui\": \"^4.0.16\",\n \"tsup\": \"^8.5.1\",\n \"typescript\": \"^5.9.3\",\n \"vitest\": \"^4.0.16\"\n },\n \"dependencies\": {\n \"cac\": \"^6.7.14\",\n \"gray-matter\": \"^4.0.3\",\n \"ignore\": \"^7.0.5\",\n \"lilconfig\": \"^3.1.3\"\n }\n}\n","import { copyFileSync, existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { AbsPath, FileTree } from \"../../types\";\n\nexport function mirror(\n\tfiletree: FileTree,\n\tsrcPath: AbsPath,\n\tdestPath: AbsPath,\n) {\n\tfunction aux(filetree: FileTree) {\n\t\tconst from = join(srcPath, filetree.path);\n\t\tconst to = join(destPath, filetree.path);\n\n\t\tif (filetree.type === \"dir\") {\n\t\t\tif (!existsSync(to)) mkdirSync(to, { recursive: true });\n\n\t\t\tfiletree.files.forEach((file) => {\n\t\t\t\taux(file);\n\t\t\t});\n\n\t\t\tfiletree.dirs.forEach((dir) => {\n\t\t\t\taux(dir);\n\t\t\t});\n\t\t}\n\n\t\tif (filetree.type === \"file\") {\n\t\t\tcopyFileSync(from, to);\n\t\t}\n\t}\n\n\taux(filetree);\n\tgenerateFileTree(filetree, destPath);\n}\n\nfunction generateFileTree(filetree: FileTree, destPath: AbsPath) {\n\tconst jsonFilePath = join(destPath, \"o2bFileTree.json\");\n\twriteFileSync(jsonFilePath, JSON.stringify(filetree, null, 2), \"utf-8\");\n}\n","import { lstatSync, readdirSync, readFileSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\nimport matter from \"gray-matter\";\nimport type {\n\tAbsPath,\n\tDirNode,\n\tFileNode,\n\tFileTree,\n\tFrontmatter,\n} from \"../../types\";\nimport { toAbsPath, toRelPath, type VaultIgnorer } from \"../../utils\";\n\nexport function scan(rootPath: AbsPath, ign: VaultIgnorer): FileTree | null {\n\tfunction aux(path: AbsPath): FileTree | null {\n\t\tif (ign?.ignore(path)) return null;\n\n\t\tconst stat = lstatSync(path);\n\t\tconst name = basename(path);\n\n\t\tif (stat.isDirectory()) {\n\t\t\tconst { dirs, files } = readdirSync(path).reduce(\n\t\t\t\t(acc, entry) => {\n\t\t\t\t\tconst node = aux(toAbsPath(join(path, entry)));\n\n\t\t\t\t\tif (node?.type === \"dir\") acc.dirs.push(node);\n\t\t\t\t\tif (node?.type === \"file\") acc.files.push(node);\n\n\t\t\t\t\treturn acc;\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdirs: [] as DirNode[],\n\t\t\t\t\tfiles: [] as FileNode[],\n\t\t\t\t},\n\t\t\t);\n\n\t\t\treturn {\n\t\t\t\tpath: toRelPath(rootPath, path),\n\t\t\t\tname,\n\t\t\t\ttype: \"dir\",\n\t\t\t\tdirs,\n\t\t\t\tfiles,\n\t\t\t};\n\t\t}\n\n\t\tif (stat.isFile() && name.endsWith(\".md\")) {\n\t\t\treturn {\n\t\t\t\tpath: toRelPath(rootPath, path),\n\t\t\t\tname,\n\t\t\t\ttype: \"file\",\n\t\t\t\tfrontmatter: getFrontmatter(path),\n\t\t\t};\n\t\t}\n\n\t\treturn null;\n\t}\n\n\treturn aux(rootPath);\n}\n\nfunction getFrontmatter(path: AbsPath): Frontmatter {\n\tconst { data: srcFrontmatter } = matter(readFileSync(path, \"utf-8\"));\n\treturn {\n\t\ttitle: basename(path).replace(/\\.md$/, \"\"),\n\t\tdescription: \"No description provided.\",\n\t\tlast_modified: lstatSync(path).mtime.toLocaleDateString(\"sv-SE\"), // \"sv-SE\" (스웨덴어)는 YYYY-MM-DD 형식으로 출력되는 유명한 트릭입니다.\n\t\t...srcFrontmatter,\n\t};\n}\n","import { existsSync, lstatSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport ignore, { type Ignore } from \"ignore\";\nimport type { AbsPath } from \"../types\";\nimport { toRelPath } from \"./path\";\n\nexport class VaultIgnorer {\n\tprivate ign: Ignore;\n\tprivate rootPath: AbsPath;\n\n\tconstructor(rootPath: AbsPath) {\n\t\tthis.rootPath = rootPath;\n\t\tthis.ign = ignore();\n\n\t\tthis.ign.add([\".*\"]);\n\t}\n\n\tpublic load() {\n\t\tconst ignorePath = join(this.rootPath, \".o2bignore\");\n\t\tif (existsSync(ignorePath))\n\t\t\tthis.ign.add(readFileSync(ignorePath).toString());\n\t}\n\n\tpublic ignore(path: AbsPath): boolean {\n\t\tif (path === this.rootPath) return false;\n\n\t\tif (!existsSync(path)) throw new Error(\"somethings wrong!\");\n\n\t\tconst stat = lstatSync(path);\n\n\t\tconst relPath =\n\t\t\ttoRelPath(this.rootPath, path) + (stat.isDirectory() ? \"/\" : \"\");\n\n\t\treturn this.ign.ignores(relPath);\n\t}\n}\n","import { lstatSync, readdirSync } from \"node:fs\";\nimport { isAbsolute, relative, resolve } from \"node:path\";\nimport type { AbsPath, RelPath } from \"../types\";\n\nfunction toAbsPath(path: string): AbsPath {\n\tif (!isAbsolute(path)) path = resolve(path);\n\treturn path as AbsPath;\n}\n\nfunction toRelPath(from: AbsPath, to: AbsPath): RelPath {\n\treturn relative(from, to) as RelPath;\n}\n\nfunction isObsidianDirectory(path: AbsPath) {\n\tconst stat = lstatSync(path);\n\n\tif (!stat.isDirectory()) return false;\n\n\tconst obsMetaDir = readdirSync(path, { withFileTypes: true })\n\t\t.filter((node) => node.isDirectory())\n\t\t.filter((node) => node.name === \".obsidian\");\n\n\tif (obsMetaDir.length !== 1) return false;\n\n\treturn true;\n}\n\nexport { toAbsPath, toRelPath, isObsidianDirectory };\n","import { mirror, scan } from \"./core/sync\";\nimport type { Config } from \"./types\";\nimport { isObsidianDirectory, VaultIgnorer } from \"./utils\";\n\nexport default function main(config: Config) {\n const { srcPath, destPath, ignore } = config;\n if (!isObsidianDirectory(srcPath)) throw new Error(\"Not a obsidian vault\");\n\n const ign = new VaultIgnorer(srcPath);\n if (ignore) ign.load();\n\n const fileTree = scan(srcPath, ign);\n if (fileTree === null) throw new Error(\"No content in vault\");\n\n mirror(fileTree, srcPath, destPath);\n}\n"],"mappings":"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","names":["filetree","import_node_fs","import_node_path","import_node_fs","import_node_path","import_node_fs","import_node_path","ignore","matter","ignore","cac","ignore"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../package.json","../src/core/sync/scan.ts","../src/utils/path.ts","../src/types/path.ts","../src/core/sync/sanitizeAndMirror.ts","../src/utils/file.ts","../src/utils/ignore.ts","../src/runO2B.ts"],"sourcesContent":["import cac from \"cac\";\nimport pkg from \"../package.json\";\nimport runO2B from \"./runO2B\";\nimport { toAbsPath } from \"@utils/path\";\n\nconst cli = cac(\"o2b-meta\");\n\ncli\n .command(\"<srcPath>\", \"target vault file\")\n .option(\"--destPath [destPath]\", \"dist\", {\n default: \"./o2b\",\n })\n .option(\"--ignore\", \"read the .o2bIgnore if exists\", {\n default: true,\n })\n .option(\"--sync\", \"synchro\", {\n default: true,\n })\n .action((srcPath, option) => {\n const { destPath, ignore, sync } = option;\n\n const startTime = performance.now();\n\n if (sync) {\n runO2B({\n srcPath: toAbsPath(srcPath),\n destPath: toAbsPath(destPath),\n ignore,\n });\n }\n\n const endTime = performance.now();\n const duration = (endTime - startTime).toFixed(2); // 소수점 2자리까지\n\n console.log(`\\n✅ Build Success!`);\n console.log(` Time: ${duration}ms`);\n // console.log(` Dest: ${config.destPath}\\n`);\n });\n\ncli.help();\ncli.version(pkg.version);\n\n// production에 대한 에러처리\ncli.parse();\n","{\n \"name\": \"@o2b/meta\",\n \"version\": \"1.0.0-dev.10\",\n \"description\": \"filetree generator for o2b\",\n \"main\": \"./dist/index.js\",\n \"module\": \"./dist/index.mjs\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.mjs\",\n \"require\": \"./dist/index.js\"\n }\n },\n \"bin\": {\n \"o2b-meta\": \"dist/cli.js\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"test\": \"vitest\",\n \"test:run\": \"vitest run\",\n \"dev\": \"tsup src/cli.ts --watch --onSuccess \\\"node dist/cli.js ./tests/fixtures/valid-vault --destPath ./tests/fixtures/\"\n },\n \"keywords\": [],\n \"author\": \"goonco\",\n \"license\": \"ISC\",\n \"type\": \"commonjs\",\n \"files\": [\n \"dist\"\n ],\n \"devDependencies\": {\n \"@biomejs/biome\": \"2.3.10\",\n \"@types/node\": \"^25.0.3\",\n \"@vitest/ui\": \"^4.0.16\",\n \"tsup\": \"^8.5.1\",\n \"typescript\": \"^5.9.3\",\n \"vite-tsconfig-paths\": \"^6.0.5\",\n \"vitest\": \"^4.0.16\",\n \"zod\": \"^3.25.76\"\n },\n \"dependencies\": {\n \"cac\": \"^6.7.14\",\n \"gray-matter\": \"^4.0.3\",\n \"ignore\": \"^7.0.5\",\n \"lilconfig\": \"^3.1.3\"\n },\n \"peerDependencies\": {\n \"zod\": \"^3.23.8\"\n }\n}\n","import { existsSync, lstatSync, readdirSync, readFileSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\nimport matter from \"gray-matter\";\nimport type { DirNode, FileNode, Frontmatter } from \"@o2bTypes/filetree\";\nimport { toAbsPath, toRelPath } from \"@utils/path\";\nimport { AbsPath } from \"@o2bTypes/path\";\nimport { VaultIgnorer } from \"@utils/ignore\";\n\nexport function scan(rootPath: AbsPath, ign: VaultIgnorer): DirNode | null {\n function aux(curPath: AbsPath): FileNode | DirNode | null {\n if (ign?.ignores(curPath)) return null;\n\n const stat = lstatSync(curPath);\n const name = basename(curPath);\n\n if (stat.isFile() && name.endsWith(\".md\")) {\n return {\n path: curPath,\n name,\n type: \"file\",\n frontmatter: fetchFrontmatter(curPath, stat.mtime),\n };\n }\n\n if (stat.isDirectory()) {\n const dirs: DirNode[] = [];\n const files: FileNode[] = [];\n const entries = readdirSync(curPath);\n\n for (const entry of entries) {\n const nextPath = toAbsPath(join(curPath, entry));\n const node = aux(nextPath);\n\n if (node?.type === \"dir\") dirs.push(node);\n if (node?.type === \"file\") files.push(node);\n }\n\n return {\n path: curPath,\n name,\n type: \"dir\",\n dirs,\n files,\n };\n }\n\n return null;\n }\n\n if (!isObsidianDirectory(rootPath)) throw new Error(\"Not a vault!\");\n\n return aux(rootPath) as DirNode;\n}\n\nfunction isObsidianDirectory(path: AbsPath) {\n const stat = lstatSync(path);\n\n if (!stat.isDirectory()) return false;\n return existsSync(join(path, \".obsidian\"));\n}\n\nfunction fetchFrontmatter(path: AbsPath, mtime: Date): Frontmatter {\n // [TODO] Matter는 파일 전체 다 읽는거라 stream으로 --- 파트만 가져오도록\n const { data } = matter(readFileSync(path, \"utf-8\"));\n return {\n title: basename(path).replace(/\\.md$/, \"\"),\n description: \"No description provided.\",\n last_modified: mtime.toISOString().split(\"T\")[0],\n ...data,\n };\n}\n","import { isAbsolute, join, relative, resolve } from \"node:path\";\nimport { absPath, relPath, type AbsPath, type RelPath } from \"@o2bTypes/path\";\n\nfunction toAbsPath(path: string): AbsPath {\n if (!isAbsolute(path)) path = resolve(path);\n return absPath.parse(path);\n}\n\nfunction toRelPath(from: AbsPath, to: AbsPath): RelPath {\n return relPath.parse(relative(from, to));\n}\n\nexport function joinToAbs(base: AbsPath, ...parts: string[]): AbsPath {\n return absPath.parse(join(base, ...parts));\n}\n\nexport function joinToRel(base: RelPath, ...parts: string[]): RelPath {\n return relPath.parse(join(base, ...parts));\n}\n\nexport { toAbsPath, toRelPath };\n","import z from \"zod\";\n\ntype AbsPath = z.infer<typeof absPath>;\nconst absPath = z.string().brand<\"absPath\">();\n\ntype RelPath = z.infer<typeof relPath>;\nconst relPath = z.string().brand<\"relpath\">();\n\nexport { type AbsPath, type RelPath, absPath, relPath };\n","import { DirNode, Name, SanitizedFileTree } from \"@o2bTypes/filetree\";\nimport { AbsPath, RelPath } from \"@o2bTypes/path\";\nimport { joinToAbs, toAbsPath, toRelPath } from \"@utils/path\";\nimport matter from \"gray-matter\";\nimport {\n copyFileSync,\n existsSync,\n mkdirSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\";\n\n// 이거 사실 더 효율적으로 할 수 있을 것 같은데, sanitizedName을 계속 넘기면서 path 빌드\n// 근데 우선 좀 해보고 필요없는 것도 좀 깨고\n\nexport function sanitizeAndCopy(\n fileTree: DirNode,\n srcPath: AbsPath,\n destPath: AbsPath,\n): SanitizedFileTree {\n function aux(curNode: DirNode): SanitizedFileTree {\n const path = sanitizePath(toRelPath(srcPath, curNode.path));\n const to = joinToAbs(destPath, path);\n\n if (!existsSync(to)) mkdirSync(to, { recursive: true });\n\n const dirs = curNode.dirs.map((dir) => aux(dir));\n const files: Name[] = curNode.files.map((file) => {\n const to = joinToAbs(\n destPath,\n sanitizePath(toRelPath(srcPath, file.path)),\n );\n\n const fileContent = readFileSync(file.path, \"utf-8\");\n const { data: existingData, content } = matter(fileContent);\n const newFm = {\n ...file.frontmatter,\n ...existingData,\n };\n\n writeFileSync(to, matter.stringify(content, newFm));\n\n return {\n original: file.name,\n sanitized: sanitize(file.name),\n };\n });\n\n return {\n path,\n dirs,\n files,\n name: {\n original: curNode.name,\n sanitized: sanitize(curNode.name),\n },\n };\n }\n\n return aux(fileTree);\n}\n\nfunction sanitizePath<K extends string | RelPath>(s: K): K {\n return s\n .split(/[\\\\/]/)\n .map((segment) => sanitize(segment))\n .join(\"/\") as K;\n}\n\nfunction sanitize<K extends string | RelPath>(s: K): K {\n return (\n s\n // OS 및 URL 예약 특수문자 제거/치환\n .replace(/[\\[\\]#?%/:*?\"<>|]/g, \"\")\n // 공백 및 특수 기호를 하이픈으로 변경\n .replace(/[\\s_]+/g, \"-\")\n // 연속된 하이픈 제거\n .replace(/-+/g, \"-\")\n // 앞뒤에 붙은 하이픈 제거\n .replace(/^-+|-+$/g, \"\")\n .trim() as K\n );\n}\n","import { writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { AbsPath } from \"@o2bTypes/path\";\n\nexport function generateJson(\n content: unknown,\n name: string,\n destPath: AbsPath,\n) {\n const jsonFilePath = join(destPath, `${name}.json`);\n writeFileSync(jsonFilePath, JSON.stringify(content, null, 2), \"utf-8\");\n}\n","import { existsSync, lstatSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport ignore, { type Ignore } from \"ignore\";\n\nimport type { AbsPath } from \"@o2bTypes/path\";\nimport { toRelPath } from \"@utils/path\";\n\nexport class VaultIgnorer {\n private ign: Ignore;\n private rootPath: AbsPath;\n\n constructor(rootPath: AbsPath) {\n this.rootPath = rootPath;\n this.ign = ignore();\n\n this.ign.add([\".*\"]);\n }\n\n public load() {\n const ignorePath = join(this.rootPath, \".o2bignore\");\n if (existsSync(ignorePath))\n this.ign.add(readFileSync(ignorePath).toString());\n }\n\n public ignores(path: AbsPath): boolean {\n if (path === this.rootPath) return false;\n // [TODO] what?\n if (!existsSync(path)) throw new Error(\"somethings wrong!\");\n\n const stat = lstatSync(path);\n const relPath =\n toRelPath(this.rootPath, path) + (stat.isDirectory() ? \"/\" : \"\");\n\n return this.ign.ignores(relPath);\n }\n}\n","import { scan, sanitizeAndCopy } from \"@core/sync\";\nimport type { Config } from \"@o2bTypes/config\";\nimport { generateJson } from \"@utils/file\";\nimport { VaultIgnorer } from \"@utils/ignore\";\n\nexport default function runO2B(config: Config) {\n const { srcPath, destPath, ignore } = config;\n\n const ign = new VaultIgnorer(srcPath);\n if (ignore) ign.load();\n\n const fileTree = scan(srcPath, ign);\n if (fileTree === null) throw new Error(\"No content in vault\");\n\n const sanitizedFileTree = sanitizeAndCopy(fileTree, srcPath, destPath);\n\n generateJson(sanitizedFileTree, \"file-tree\", destPath);\n}\n"],"mappings":"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","names":["import_node_path","z","matter","import_gray_matter","import_node_fs","to","matter","import_node_fs","import_node_path","import_node_fs","import_node_path","ignore","relPath","ignore","cac","ignore"]}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
type Branded<K, T> = K & {
|
|
3
|
-
[__brand]: T;
|
|
4
|
-
};
|
|
1
|
+
import z from 'zod';
|
|
5
2
|
|
|
6
|
-
type AbsPath =
|
|
3
|
+
type AbsPath = z.infer<typeof absPath>;
|
|
4
|
+
declare const absPath: z.ZodBranded<z.ZodString, "absPath">;
|
|
7
5
|
|
|
8
6
|
type Config = {
|
|
9
7
|
srcPath: AbsPath;
|
|
@@ -11,6 +9,6 @@ type Config = {
|
|
|
11
9
|
ignore: boolean;
|
|
12
10
|
};
|
|
13
11
|
|
|
14
|
-
declare function
|
|
12
|
+
declare function runO2B(config: Config): void;
|
|
15
13
|
|
|
16
|
-
export {
|
|
14
|
+
export { runO2B as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
type Branded<K, T> = K & {
|
|
3
|
-
[__brand]: T;
|
|
4
|
-
};
|
|
1
|
+
import z from 'zod';
|
|
5
2
|
|
|
6
|
-
type AbsPath =
|
|
3
|
+
type AbsPath = z.infer<typeof absPath>;
|
|
4
|
+
declare const absPath: z.ZodBranded<z.ZodString, "absPath">;
|
|
7
5
|
|
|
8
6
|
type Config = {
|
|
9
7
|
srcPath: AbsPath;
|
|
@@ -11,6 +9,6 @@ type Config = {
|
|
|
11
9
|
ignore: boolean;
|
|
12
10
|
};
|
|
13
11
|
|
|
14
|
-
declare function
|
|
12
|
+
declare function runO2B(config: Config): void;
|
|
15
13
|
|
|
16
|
-
export {
|
|
14
|
+
export { runO2B as default };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -29,147 +30,174 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
29
30
|
// src/index.ts
|
|
30
31
|
var index_exports = {};
|
|
31
32
|
__export(index_exports, {
|
|
32
|
-
default: () =>
|
|
33
|
+
default: () => runO2B
|
|
33
34
|
});
|
|
34
35
|
module.exports = __toCommonJS(index_exports);
|
|
35
36
|
|
|
36
|
-
// src/core/sync/mirror.ts
|
|
37
|
-
var import_node_fs = require("fs");
|
|
38
|
-
var import_node_path = require("path");
|
|
39
|
-
function mirror(filetree, srcPath, destPath) {
|
|
40
|
-
function aux(filetree2) {
|
|
41
|
-
const from = (0, import_node_path.join)(srcPath, filetree2.path);
|
|
42
|
-
const to = (0, import_node_path.join)(destPath, filetree2.path);
|
|
43
|
-
if (filetree2.type === "dir") {
|
|
44
|
-
if (!(0, import_node_fs.existsSync)(to)) (0, import_node_fs.mkdirSync)(to, { recursive: true });
|
|
45
|
-
filetree2.files.forEach((file) => {
|
|
46
|
-
aux(file);
|
|
47
|
-
});
|
|
48
|
-
filetree2.dirs.forEach((dir) => {
|
|
49
|
-
aux(dir);
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
if (filetree2.type === "file") {
|
|
53
|
-
(0, import_node_fs.copyFileSync)(from, to);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
aux(filetree);
|
|
57
|
-
generateFileTree(filetree, destPath);
|
|
58
|
-
}
|
|
59
|
-
function generateFileTree(filetree, destPath) {
|
|
60
|
-
const jsonFilePath = (0, import_node_path.join)(destPath, "o2bFileTree.json");
|
|
61
|
-
(0, import_node_fs.writeFileSync)(jsonFilePath, JSON.stringify(filetree, null, 2), "utf-8");
|
|
62
|
-
}
|
|
63
|
-
|
|
64
37
|
// src/core/sync/scan.ts
|
|
65
|
-
var
|
|
66
|
-
var
|
|
38
|
+
var import_node_fs = require("fs");
|
|
39
|
+
var import_node_path2 = require("path");
|
|
67
40
|
var import_gray_matter = __toESM(require("gray-matter"));
|
|
68
41
|
|
|
69
|
-
// src/utils/
|
|
70
|
-
var
|
|
71
|
-
|
|
72
|
-
|
|
42
|
+
// src/utils/path.ts
|
|
43
|
+
var import_node_path = require("path");
|
|
44
|
+
|
|
45
|
+
// src/types/path.ts
|
|
46
|
+
var import_zod = __toESM(require("zod"));
|
|
47
|
+
var absPath = import_zod.default.string().brand();
|
|
48
|
+
var relPath = import_zod.default.string().brand();
|
|
73
49
|
|
|
74
50
|
// src/utils/path.ts
|
|
75
|
-
var import_node_fs2 = require("fs");
|
|
76
|
-
var import_node_path2 = require("path");
|
|
77
51
|
function toAbsPath(path) {
|
|
78
|
-
if (!(0,
|
|
79
|
-
return path;
|
|
52
|
+
if (!(0, import_node_path.isAbsolute)(path)) path = (0, import_node_path.resolve)(path);
|
|
53
|
+
return absPath.parse(path);
|
|
80
54
|
}
|
|
81
55
|
function toRelPath(from, to) {
|
|
82
|
-
return (0,
|
|
56
|
+
return relPath.parse((0, import_node_path.relative)(from, to));
|
|
83
57
|
}
|
|
84
|
-
function
|
|
85
|
-
|
|
86
|
-
if (!stat.isDirectory()) return false;
|
|
87
|
-
const obsMetaDir = (0, import_node_fs2.readdirSync)(path, { withFileTypes: true }).filter((node) => node.isDirectory()).filter((node) => node.name === ".obsidian");
|
|
88
|
-
if (obsMetaDir.length !== 1) return false;
|
|
89
|
-
return true;
|
|
58
|
+
function joinToAbs(base, ...parts) {
|
|
59
|
+
return absPath.parse((0, import_node_path.join)(base, ...parts));
|
|
90
60
|
}
|
|
91
61
|
|
|
92
|
-
// src/utils/ignore.ts
|
|
93
|
-
var VaultIgnorer = class {
|
|
94
|
-
ign;
|
|
95
|
-
rootPath;
|
|
96
|
-
constructor(rootPath) {
|
|
97
|
-
this.rootPath = rootPath;
|
|
98
|
-
this.ign = (0, import_ignore.default)();
|
|
99
|
-
this.ign.add([".*"]);
|
|
100
|
-
}
|
|
101
|
-
load() {
|
|
102
|
-
const ignorePath = (0, import_node_path3.join)(this.rootPath, ".o2bignore");
|
|
103
|
-
if ((0, import_node_fs3.existsSync)(ignorePath))
|
|
104
|
-
this.ign.add((0, import_node_fs3.readFileSync)(ignorePath).toString());
|
|
105
|
-
}
|
|
106
|
-
ignore(path) {
|
|
107
|
-
if (path === this.rootPath) return false;
|
|
108
|
-
if (!(0, import_node_fs3.existsSync)(path)) throw new Error("somethings wrong!");
|
|
109
|
-
const stat = (0, import_node_fs3.lstatSync)(path);
|
|
110
|
-
const relPath = toRelPath(this.rootPath, path) + (stat.isDirectory() ? "/" : "");
|
|
111
|
-
return this.ign.ignores(relPath);
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
|
|
115
62
|
// src/core/sync/scan.ts
|
|
116
63
|
function scan(rootPath, ign) {
|
|
117
|
-
function aux(
|
|
118
|
-
if (ign == null ? void 0 : ign.
|
|
119
|
-
const stat = (0,
|
|
120
|
-
const name = (0,
|
|
121
|
-
if (stat.
|
|
122
|
-
const { dirs, files } = (0, import_node_fs4.readdirSync)(path).reduce(
|
|
123
|
-
(acc, entry) => {
|
|
124
|
-
const node = aux(toAbsPath((0, import_node_path4.join)(path, entry)));
|
|
125
|
-
if ((node == null ? void 0 : node.type) === "dir") acc.dirs.push(node);
|
|
126
|
-
if ((node == null ? void 0 : node.type) === "file") acc.files.push(node);
|
|
127
|
-
return acc;
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
dirs: [],
|
|
131
|
-
files: []
|
|
132
|
-
}
|
|
133
|
-
);
|
|
64
|
+
function aux(curPath) {
|
|
65
|
+
if (ign == null ? void 0 : ign.ignores(curPath)) return null;
|
|
66
|
+
const stat = (0, import_node_fs.lstatSync)(curPath);
|
|
67
|
+
const name = (0, import_node_path2.basename)(curPath);
|
|
68
|
+
if (stat.isFile() && name.endsWith(".md")) {
|
|
134
69
|
return {
|
|
135
|
-
path:
|
|
70
|
+
path: curPath,
|
|
136
71
|
name,
|
|
137
|
-
type: "
|
|
138
|
-
|
|
139
|
-
files
|
|
72
|
+
type: "file",
|
|
73
|
+
frontmatter: fetchFrontmatter(curPath, stat.mtime)
|
|
140
74
|
};
|
|
141
75
|
}
|
|
142
|
-
if (stat.
|
|
76
|
+
if (stat.isDirectory()) {
|
|
77
|
+
const dirs = [];
|
|
78
|
+
const files = [];
|
|
79
|
+
const entries = (0, import_node_fs.readdirSync)(curPath);
|
|
80
|
+
for (const entry of entries) {
|
|
81
|
+
const nextPath = toAbsPath((0, import_node_path2.join)(curPath, entry));
|
|
82
|
+
const node = aux(nextPath);
|
|
83
|
+
if ((node == null ? void 0 : node.type) === "dir") dirs.push(node);
|
|
84
|
+
if ((node == null ? void 0 : node.type) === "file") files.push(node);
|
|
85
|
+
}
|
|
143
86
|
return {
|
|
144
|
-
path:
|
|
87
|
+
path: curPath,
|
|
145
88
|
name,
|
|
146
|
-
type: "
|
|
147
|
-
|
|
89
|
+
type: "dir",
|
|
90
|
+
dirs,
|
|
91
|
+
files
|
|
148
92
|
};
|
|
149
93
|
}
|
|
150
94
|
return null;
|
|
151
95
|
}
|
|
96
|
+
if (!isObsidianDirectory(rootPath)) throw new Error("Not a vault!");
|
|
152
97
|
return aux(rootPath);
|
|
153
98
|
}
|
|
154
|
-
function
|
|
155
|
-
const
|
|
99
|
+
function isObsidianDirectory(path) {
|
|
100
|
+
const stat = (0, import_node_fs.lstatSync)(path);
|
|
101
|
+
if (!stat.isDirectory()) return false;
|
|
102
|
+
return (0, import_node_fs.existsSync)((0, import_node_path2.join)(path, ".obsidian"));
|
|
103
|
+
}
|
|
104
|
+
function fetchFrontmatter(path, mtime) {
|
|
105
|
+
const { data } = (0, import_gray_matter.default)((0, import_node_fs.readFileSync)(path, "utf-8"));
|
|
156
106
|
return {
|
|
157
|
-
title: (0,
|
|
107
|
+
title: (0, import_node_path2.basename)(path).replace(/\.md$/, ""),
|
|
158
108
|
description: "No description provided.",
|
|
159
|
-
last_modified:
|
|
160
|
-
|
|
161
|
-
...srcFrontmatter
|
|
109
|
+
last_modified: mtime.toISOString().split("T")[0],
|
|
110
|
+
...data
|
|
162
111
|
};
|
|
163
112
|
}
|
|
164
113
|
|
|
165
|
-
// src/
|
|
166
|
-
|
|
114
|
+
// src/core/sync/sanitizeAndMirror.ts
|
|
115
|
+
var import_gray_matter2 = __toESM(require("gray-matter"));
|
|
116
|
+
var import_node_fs2 = require("fs");
|
|
117
|
+
function sanitizeAndCopy(fileTree, srcPath, destPath) {
|
|
118
|
+
function aux(curNode) {
|
|
119
|
+
const path = sanitizePath(toRelPath(srcPath, curNode.path));
|
|
120
|
+
const to = joinToAbs(destPath, path);
|
|
121
|
+
if (!(0, import_node_fs2.existsSync)(to)) (0, import_node_fs2.mkdirSync)(to, { recursive: true });
|
|
122
|
+
const dirs = curNode.dirs.map((dir) => aux(dir));
|
|
123
|
+
const files = curNode.files.map((file) => {
|
|
124
|
+
const to2 = joinToAbs(
|
|
125
|
+
destPath,
|
|
126
|
+
sanitizePath(toRelPath(srcPath, file.path))
|
|
127
|
+
);
|
|
128
|
+
const fileContent = (0, import_node_fs2.readFileSync)(file.path, "utf-8");
|
|
129
|
+
const { data: existingData, content } = (0, import_gray_matter2.default)(fileContent);
|
|
130
|
+
const newFm = {
|
|
131
|
+
...file.frontmatter,
|
|
132
|
+
...existingData
|
|
133
|
+
};
|
|
134
|
+
(0, import_node_fs2.writeFileSync)(to2, import_gray_matter2.default.stringify(content, newFm));
|
|
135
|
+
return {
|
|
136
|
+
original: file.name,
|
|
137
|
+
sanitized: sanitize(file.name)
|
|
138
|
+
};
|
|
139
|
+
});
|
|
140
|
+
return {
|
|
141
|
+
path,
|
|
142
|
+
dirs,
|
|
143
|
+
files,
|
|
144
|
+
name: {
|
|
145
|
+
original: curNode.name,
|
|
146
|
+
sanitized: sanitize(curNode.name)
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
return aux(fileTree);
|
|
151
|
+
}
|
|
152
|
+
function sanitizePath(s) {
|
|
153
|
+
return s.split(/[\\/]/).map((segment) => sanitize(segment)).join("/");
|
|
154
|
+
}
|
|
155
|
+
function sanitize(s) {
|
|
156
|
+
return s.replace(/[\[\]#?%/:*?"<>|]/g, "").replace(/[\s_]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "").trim();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// src/utils/file.ts
|
|
160
|
+
var import_node_fs3 = require("fs");
|
|
161
|
+
var import_node_path3 = require("path");
|
|
162
|
+
function generateJson(content, name, destPath) {
|
|
163
|
+
const jsonFilePath = (0, import_node_path3.join)(destPath, `${name}.json`);
|
|
164
|
+
(0, import_node_fs3.writeFileSync)(jsonFilePath, JSON.stringify(content, null, 2), "utf-8");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// src/utils/ignore.ts
|
|
168
|
+
var import_node_fs4 = require("fs");
|
|
169
|
+
var import_node_path4 = require("path");
|
|
170
|
+
var import_ignore = __toESM(require("ignore"));
|
|
171
|
+
var VaultIgnorer = class {
|
|
172
|
+
ign;
|
|
173
|
+
rootPath;
|
|
174
|
+
constructor(rootPath) {
|
|
175
|
+
this.rootPath = rootPath;
|
|
176
|
+
this.ign = (0, import_ignore.default)();
|
|
177
|
+
this.ign.add([".*"]);
|
|
178
|
+
}
|
|
179
|
+
load() {
|
|
180
|
+
const ignorePath = (0, import_node_path4.join)(this.rootPath, ".o2bignore");
|
|
181
|
+
if ((0, import_node_fs4.existsSync)(ignorePath))
|
|
182
|
+
this.ign.add((0, import_node_fs4.readFileSync)(ignorePath).toString());
|
|
183
|
+
}
|
|
184
|
+
ignores(path) {
|
|
185
|
+
if (path === this.rootPath) return false;
|
|
186
|
+
if (!(0, import_node_fs4.existsSync)(path)) throw new Error("somethings wrong!");
|
|
187
|
+
const stat = (0, import_node_fs4.lstatSync)(path);
|
|
188
|
+
const relPath2 = toRelPath(this.rootPath, path) + (stat.isDirectory() ? "/" : "");
|
|
189
|
+
return this.ign.ignores(relPath2);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
// src/runO2B.ts
|
|
194
|
+
function runO2B(config) {
|
|
167
195
|
const { srcPath, destPath, ignore: ignore2 } = config;
|
|
168
|
-
if (!isObsidianDirectory(srcPath)) throw new Error("Not a obsidian vault");
|
|
169
196
|
const ign = new VaultIgnorer(srcPath);
|
|
170
197
|
if (ignore2) ign.load();
|
|
171
198
|
const fileTree = scan(srcPath, ign);
|
|
172
199
|
if (fileTree === null) throw new Error("No content in vault");
|
|
173
|
-
|
|
200
|
+
const sanitizedFileTree = sanitizeAndCopy(fileTree, srcPath, destPath);
|
|
201
|
+
generateJson(sanitizedFileTree, "file-tree", destPath);
|
|
174
202
|
}
|
|
175
203
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/core/sync/mirror.ts","../src/core/sync/scan.ts","../src/utils/ignore.ts","../src/utils/path.ts"],"sourcesContent":["import { mirror, scan } from \"./core/sync\";\nimport type { Config } from \"./types\";\nimport { isObsidianDirectory, VaultIgnorer } from \"./utils\";\n\nexport default function main(config: Config) {\n const { srcPath, destPath, ignore } = config;\n if (!isObsidianDirectory(srcPath)) throw new Error(\"Not a obsidian vault\");\n\n const ign = new VaultIgnorer(srcPath);\n if (ignore) ign.load();\n\n const fileTree = scan(srcPath, ign);\n if (fileTree === null) throw new Error(\"No content in vault\");\n\n mirror(fileTree, srcPath, destPath);\n}\n","import { copyFileSync, existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { AbsPath, FileTree } from \"../../types\";\n\nexport function mirror(\n\tfiletree: FileTree,\n\tsrcPath: AbsPath,\n\tdestPath: AbsPath,\n) {\n\tfunction aux(filetree: FileTree) {\n\t\tconst from = join(srcPath, filetree.path);\n\t\tconst to = join(destPath, filetree.path);\n\n\t\tif (filetree.type === \"dir\") {\n\t\t\tif (!existsSync(to)) mkdirSync(to, { recursive: true });\n\n\t\t\tfiletree.files.forEach((file) => {\n\t\t\t\taux(file);\n\t\t\t});\n\n\t\t\tfiletree.dirs.forEach((dir) => {\n\t\t\t\taux(dir);\n\t\t\t});\n\t\t}\n\n\t\tif (filetree.type === \"file\") {\n\t\t\tcopyFileSync(from, to);\n\t\t}\n\t}\n\n\taux(filetree);\n\tgenerateFileTree(filetree, destPath);\n}\n\nfunction generateFileTree(filetree: FileTree, destPath: AbsPath) {\n\tconst jsonFilePath = join(destPath, \"o2bFileTree.json\");\n\twriteFileSync(jsonFilePath, JSON.stringify(filetree, null, 2), \"utf-8\");\n}\n","import { lstatSync, readdirSync, readFileSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\nimport matter from \"gray-matter\";\nimport type {\n\tAbsPath,\n\tDirNode,\n\tFileNode,\n\tFileTree,\n\tFrontmatter,\n} from \"../../types\";\nimport { toAbsPath, toRelPath, type VaultIgnorer } from \"../../utils\";\n\nexport function scan(rootPath: AbsPath, ign: VaultIgnorer): FileTree | null {\n\tfunction aux(path: AbsPath): FileTree | null {\n\t\tif (ign?.ignore(path)) return null;\n\n\t\tconst stat = lstatSync(path);\n\t\tconst name = basename(path);\n\n\t\tif (stat.isDirectory()) {\n\t\t\tconst { dirs, files } = readdirSync(path).reduce(\n\t\t\t\t(acc, entry) => {\n\t\t\t\t\tconst node = aux(toAbsPath(join(path, entry)));\n\n\t\t\t\t\tif (node?.type === \"dir\") acc.dirs.push(node);\n\t\t\t\t\tif (node?.type === \"file\") acc.files.push(node);\n\n\t\t\t\t\treturn acc;\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdirs: [] as DirNode[],\n\t\t\t\t\tfiles: [] as FileNode[],\n\t\t\t\t},\n\t\t\t);\n\n\t\t\treturn {\n\t\t\t\tpath: toRelPath(rootPath, path),\n\t\t\t\tname,\n\t\t\t\ttype: \"dir\",\n\t\t\t\tdirs,\n\t\t\t\tfiles,\n\t\t\t};\n\t\t}\n\n\t\tif (stat.isFile() && name.endsWith(\".md\")) {\n\t\t\treturn {\n\t\t\t\tpath: toRelPath(rootPath, path),\n\t\t\t\tname,\n\t\t\t\ttype: \"file\",\n\t\t\t\tfrontmatter: getFrontmatter(path),\n\t\t\t};\n\t\t}\n\n\t\treturn null;\n\t}\n\n\treturn aux(rootPath);\n}\n\nfunction getFrontmatter(path: AbsPath): Frontmatter {\n\tconst { data: srcFrontmatter } = matter(readFileSync(path, \"utf-8\"));\n\treturn {\n\t\ttitle: basename(path).replace(/\\.md$/, \"\"),\n\t\tdescription: \"No description provided.\",\n\t\tlast_modified: lstatSync(path).mtime.toLocaleDateString(\"sv-SE\"), // \"sv-SE\" (스웨덴어)는 YYYY-MM-DD 형식으로 출력되는 유명한 트릭입니다.\n\t\t...srcFrontmatter,\n\t};\n}\n","import { existsSync, lstatSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport ignore, { type Ignore } from \"ignore\";\nimport type { AbsPath } from \"../types\";\nimport { toRelPath } from \"./path\";\n\nexport class VaultIgnorer {\n\tprivate ign: Ignore;\n\tprivate rootPath: AbsPath;\n\n\tconstructor(rootPath: AbsPath) {\n\t\tthis.rootPath = rootPath;\n\t\tthis.ign = ignore();\n\n\t\tthis.ign.add([\".*\"]);\n\t}\n\n\tpublic load() {\n\t\tconst ignorePath = join(this.rootPath, \".o2bignore\");\n\t\tif (existsSync(ignorePath))\n\t\t\tthis.ign.add(readFileSync(ignorePath).toString());\n\t}\n\n\tpublic ignore(path: AbsPath): boolean {\n\t\tif (path === this.rootPath) return false;\n\n\t\tif (!existsSync(path)) throw new Error(\"somethings wrong!\");\n\n\t\tconst stat = lstatSync(path);\n\n\t\tconst relPath =\n\t\t\ttoRelPath(this.rootPath, path) + (stat.isDirectory() ? \"/\" : \"\");\n\n\t\treturn this.ign.ignores(relPath);\n\t}\n}\n","import { lstatSync, readdirSync } from \"node:fs\";\nimport { isAbsolute, relative, resolve } from \"node:path\";\nimport type { AbsPath, RelPath } from \"../types\";\n\nfunction toAbsPath(path: string): AbsPath {\n\tif (!isAbsolute(path)) path = resolve(path);\n\treturn path as AbsPath;\n}\n\nfunction toRelPath(from: AbsPath, to: AbsPath): RelPath {\n\treturn relative(from, to) as RelPath;\n}\n\nfunction isObsidianDirectory(path: AbsPath) {\n\tconst stat = lstatSync(path);\n\n\tif (!stat.isDirectory()) return false;\n\n\tconst obsMetaDir = readdirSync(path, { withFileTypes: true })\n\t\t.filter((node) => node.isDirectory())\n\t\t.filter((node) => node.name === \".obsidian\");\n\n\tif (obsMetaDir.length !== 1) return false;\n\n\treturn true;\n}\n\nexport { toAbsPath, toRelPath, isObsidianDirectory };\n"],"mappings":"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","names":["filetree","import_node_fs","import_node_path","import_node_fs","import_node_path","import_node_fs","import_node_path","ignore","matter","ignore"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core/sync/scan.ts","../src/utils/path.ts","../src/types/path.ts","../src/core/sync/sanitizeAndMirror.ts","../src/utils/file.ts","../src/utils/ignore.ts","../src/runO2B.ts"],"sourcesContent":["export {} from \"@o2bTypes/filetree\";\nexport { default } from \"./runO2B\";\n","import { existsSync, lstatSync, readdirSync, readFileSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\nimport matter from \"gray-matter\";\nimport type { DirNode, FileNode, Frontmatter } from \"@o2bTypes/filetree\";\nimport { toAbsPath, toRelPath } from \"@utils/path\";\nimport { AbsPath } from \"@o2bTypes/path\";\nimport { VaultIgnorer } from \"@utils/ignore\";\n\nexport function scan(rootPath: AbsPath, ign: VaultIgnorer): DirNode | null {\n function aux(curPath: AbsPath): FileNode | DirNode | null {\n if (ign?.ignores(curPath)) return null;\n\n const stat = lstatSync(curPath);\n const name = basename(curPath);\n\n if (stat.isFile() && name.endsWith(\".md\")) {\n return {\n path: curPath,\n name,\n type: \"file\",\n frontmatter: fetchFrontmatter(curPath, stat.mtime),\n };\n }\n\n if (stat.isDirectory()) {\n const dirs: DirNode[] = [];\n const files: FileNode[] = [];\n const entries = readdirSync(curPath);\n\n for (const entry of entries) {\n const nextPath = toAbsPath(join(curPath, entry));\n const node = aux(nextPath);\n\n if (node?.type === \"dir\") dirs.push(node);\n if (node?.type === \"file\") files.push(node);\n }\n\n return {\n path: curPath,\n name,\n type: \"dir\",\n dirs,\n files,\n };\n }\n\n return null;\n }\n\n if (!isObsidianDirectory(rootPath)) throw new Error(\"Not a vault!\");\n\n return aux(rootPath) as DirNode;\n}\n\nfunction isObsidianDirectory(path: AbsPath) {\n const stat = lstatSync(path);\n\n if (!stat.isDirectory()) return false;\n return existsSync(join(path, \".obsidian\"));\n}\n\nfunction fetchFrontmatter(path: AbsPath, mtime: Date): Frontmatter {\n // [TODO] Matter는 파일 전체 다 읽는거라 stream으로 --- 파트만 가져오도록\n const { data } = matter(readFileSync(path, \"utf-8\"));\n return {\n title: basename(path).replace(/\\.md$/, \"\"),\n description: \"No description provided.\",\n last_modified: mtime.toISOString().split(\"T\")[0],\n ...data,\n };\n}\n","import { isAbsolute, join, relative, resolve } from \"node:path\";\nimport { absPath, relPath, type AbsPath, type RelPath } from \"@o2bTypes/path\";\n\nfunction toAbsPath(path: string): AbsPath {\n if (!isAbsolute(path)) path = resolve(path);\n return absPath.parse(path);\n}\n\nfunction toRelPath(from: AbsPath, to: AbsPath): RelPath {\n return relPath.parse(relative(from, to));\n}\n\nexport function joinToAbs(base: AbsPath, ...parts: string[]): AbsPath {\n return absPath.parse(join(base, ...parts));\n}\n\nexport function joinToRel(base: RelPath, ...parts: string[]): RelPath {\n return relPath.parse(join(base, ...parts));\n}\n\nexport { toAbsPath, toRelPath };\n","import z from \"zod\";\n\ntype AbsPath = z.infer<typeof absPath>;\nconst absPath = z.string().brand<\"absPath\">();\n\ntype RelPath = z.infer<typeof relPath>;\nconst relPath = z.string().brand<\"relpath\">();\n\nexport { type AbsPath, type RelPath, absPath, relPath };\n","import { DirNode, Name, SanitizedFileTree } from \"@o2bTypes/filetree\";\nimport { AbsPath, RelPath } from \"@o2bTypes/path\";\nimport { joinToAbs, toAbsPath, toRelPath } from \"@utils/path\";\nimport matter from \"gray-matter\";\nimport {\n copyFileSync,\n existsSync,\n mkdirSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\";\n\n// 이거 사실 더 효율적으로 할 수 있을 것 같은데, sanitizedName을 계속 넘기면서 path 빌드\n// 근데 우선 좀 해보고 필요없는 것도 좀 깨고\n\nexport function sanitizeAndCopy(\n fileTree: DirNode,\n srcPath: AbsPath,\n destPath: AbsPath,\n): SanitizedFileTree {\n function aux(curNode: DirNode): SanitizedFileTree {\n const path = sanitizePath(toRelPath(srcPath, curNode.path));\n const to = joinToAbs(destPath, path);\n\n if (!existsSync(to)) mkdirSync(to, { recursive: true });\n\n const dirs = curNode.dirs.map((dir) => aux(dir));\n const files: Name[] = curNode.files.map((file) => {\n const to = joinToAbs(\n destPath,\n sanitizePath(toRelPath(srcPath, file.path)),\n );\n\n const fileContent = readFileSync(file.path, \"utf-8\");\n const { data: existingData, content } = matter(fileContent);\n const newFm = {\n ...file.frontmatter,\n ...existingData,\n };\n\n writeFileSync(to, matter.stringify(content, newFm));\n\n return {\n original: file.name,\n sanitized: sanitize(file.name),\n };\n });\n\n return {\n path,\n dirs,\n files,\n name: {\n original: curNode.name,\n sanitized: sanitize(curNode.name),\n },\n };\n }\n\n return aux(fileTree);\n}\n\nfunction sanitizePath<K extends string | RelPath>(s: K): K {\n return s\n .split(/[\\\\/]/)\n .map((segment) => sanitize(segment))\n .join(\"/\") as K;\n}\n\nfunction sanitize<K extends string | RelPath>(s: K): K {\n return (\n s\n // OS 및 URL 예약 특수문자 제거/치환\n .replace(/[\\[\\]#?%/:*?\"<>|]/g, \"\")\n // 공백 및 특수 기호를 하이픈으로 변경\n .replace(/[\\s_]+/g, \"-\")\n // 연속된 하이픈 제거\n .replace(/-+/g, \"-\")\n // 앞뒤에 붙은 하이픈 제거\n .replace(/^-+|-+$/g, \"\")\n .trim() as K\n );\n}\n","import { writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { AbsPath } from \"@o2bTypes/path\";\n\nexport function generateJson(\n content: unknown,\n name: string,\n destPath: AbsPath,\n) {\n const jsonFilePath = join(destPath, `${name}.json`);\n writeFileSync(jsonFilePath, JSON.stringify(content, null, 2), \"utf-8\");\n}\n","import { existsSync, lstatSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport ignore, { type Ignore } from \"ignore\";\n\nimport type { AbsPath } from \"@o2bTypes/path\";\nimport { toRelPath } from \"@utils/path\";\n\nexport class VaultIgnorer {\n private ign: Ignore;\n private rootPath: AbsPath;\n\n constructor(rootPath: AbsPath) {\n this.rootPath = rootPath;\n this.ign = ignore();\n\n this.ign.add([\".*\"]);\n }\n\n public load() {\n const ignorePath = join(this.rootPath, \".o2bignore\");\n if (existsSync(ignorePath))\n this.ign.add(readFileSync(ignorePath).toString());\n }\n\n public ignores(path: AbsPath): boolean {\n if (path === this.rootPath) return false;\n // [TODO] what?\n if (!existsSync(path)) throw new Error(\"somethings wrong!\");\n\n const stat = lstatSync(path);\n const relPath =\n toRelPath(this.rootPath, path) + (stat.isDirectory() ? \"/\" : \"\");\n\n return this.ign.ignores(relPath);\n }\n}\n","import { scan, sanitizeAndCopy } from \"@core/sync\";\nimport type { Config } from \"@o2bTypes/config\";\nimport { generateJson } from \"@utils/file\";\nimport { VaultIgnorer } from \"@utils/ignore\";\n\nexport default function runO2B(config: Config) {\n const { srcPath, destPath, ignore } = config;\n\n const ign = new VaultIgnorer(srcPath);\n if (ignore) ign.load();\n\n const fileTree = scan(srcPath, ign);\n if (fileTree === null) throw new Error(\"No content in vault\");\n\n const sanitizedFileTree = sanitizeAndCopy(fileTree, srcPath, destPath);\n\n generateJson(sanitizedFileTree, \"file-tree\", destPath);\n}\n"],"mappings":"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","names":["import_node_path","z","matter","import_gray_matter","import_node_fs","to","matter","import_node_fs","import_node_path","import_node_fs","import_node_path","ignore","relPath","ignore"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,143 +1,175 @@
|
|
|
1
|
-
// src/core/sync/mirror.ts
|
|
2
|
-
import { copyFileSync, existsSync, mkdirSync, writeFileSync } from "fs";
|
|
3
|
-
import { join } from "path";
|
|
4
|
-
function mirror(filetree, srcPath, destPath) {
|
|
5
|
-
function aux(filetree2) {
|
|
6
|
-
const from = join(srcPath, filetree2.path);
|
|
7
|
-
const to = join(destPath, filetree2.path);
|
|
8
|
-
if (filetree2.type === "dir") {
|
|
9
|
-
if (!existsSync(to)) mkdirSync(to, { recursive: true });
|
|
10
|
-
filetree2.files.forEach((file) => {
|
|
11
|
-
aux(file);
|
|
12
|
-
});
|
|
13
|
-
filetree2.dirs.forEach((dir) => {
|
|
14
|
-
aux(dir);
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
if (filetree2.type === "file") {
|
|
18
|
-
copyFileSync(from, to);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
aux(filetree);
|
|
22
|
-
generateFileTree(filetree, destPath);
|
|
23
|
-
}
|
|
24
|
-
function generateFileTree(filetree, destPath) {
|
|
25
|
-
const jsonFilePath = join(destPath, "o2bFileTree.json");
|
|
26
|
-
writeFileSync(jsonFilePath, JSON.stringify(filetree, null, 2), "utf-8");
|
|
27
|
-
}
|
|
28
|
-
|
|
29
1
|
// src/core/sync/scan.ts
|
|
30
|
-
import { lstatSync
|
|
31
|
-
import { basename, join as
|
|
2
|
+
import { existsSync, lstatSync, readdirSync, readFileSync } from "fs";
|
|
3
|
+
import { basename, join as join2 } from "path";
|
|
32
4
|
import matter from "gray-matter";
|
|
33
5
|
|
|
34
|
-
// src/utils/
|
|
35
|
-
import {
|
|
36
|
-
|
|
37
|
-
|
|
6
|
+
// src/utils/path.ts
|
|
7
|
+
import { isAbsolute, join, relative, resolve } from "path";
|
|
8
|
+
|
|
9
|
+
// src/types/path.ts
|
|
10
|
+
import z from "zod";
|
|
11
|
+
var absPath = z.string().brand();
|
|
12
|
+
var relPath = z.string().brand();
|
|
38
13
|
|
|
39
14
|
// src/utils/path.ts
|
|
40
|
-
import { lstatSync, readdirSync } from "fs";
|
|
41
|
-
import { isAbsolute, relative, resolve } from "path";
|
|
42
15
|
function toAbsPath(path) {
|
|
43
16
|
if (!isAbsolute(path)) path = resolve(path);
|
|
44
|
-
return path;
|
|
17
|
+
return absPath.parse(path);
|
|
45
18
|
}
|
|
46
19
|
function toRelPath(from, to) {
|
|
47
|
-
return relative(from, to);
|
|
20
|
+
return relPath.parse(relative(from, to));
|
|
48
21
|
}
|
|
49
|
-
function
|
|
50
|
-
|
|
51
|
-
if (!stat.isDirectory()) return false;
|
|
52
|
-
const obsMetaDir = readdirSync(path, { withFileTypes: true }).filter((node) => node.isDirectory()).filter((node) => node.name === ".obsidian");
|
|
53
|
-
if (obsMetaDir.length !== 1) return false;
|
|
54
|
-
return true;
|
|
22
|
+
function joinToAbs(base, ...parts) {
|
|
23
|
+
return absPath.parse(join(base, ...parts));
|
|
55
24
|
}
|
|
56
25
|
|
|
57
|
-
// src/utils/ignore.ts
|
|
58
|
-
var VaultIgnorer = class {
|
|
59
|
-
ign;
|
|
60
|
-
rootPath;
|
|
61
|
-
constructor(rootPath) {
|
|
62
|
-
this.rootPath = rootPath;
|
|
63
|
-
this.ign = ignore();
|
|
64
|
-
this.ign.add([".*"]);
|
|
65
|
-
}
|
|
66
|
-
load() {
|
|
67
|
-
const ignorePath = join2(this.rootPath, ".o2bignore");
|
|
68
|
-
if (existsSync2(ignorePath))
|
|
69
|
-
this.ign.add(readFileSync(ignorePath).toString());
|
|
70
|
-
}
|
|
71
|
-
ignore(path) {
|
|
72
|
-
if (path === this.rootPath) return false;
|
|
73
|
-
if (!existsSync2(path)) throw new Error("somethings wrong!");
|
|
74
|
-
const stat = lstatSync2(path);
|
|
75
|
-
const relPath = toRelPath(this.rootPath, path) + (stat.isDirectory() ? "/" : "");
|
|
76
|
-
return this.ign.ignores(relPath);
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
|
|
80
26
|
// src/core/sync/scan.ts
|
|
81
27
|
function scan(rootPath, ign) {
|
|
82
|
-
function aux(
|
|
83
|
-
if (ign == null ? void 0 : ign.
|
|
84
|
-
const stat =
|
|
85
|
-
const name = basename(
|
|
86
|
-
if (stat.
|
|
87
|
-
const { dirs, files } = readdirSync2(path).reduce(
|
|
88
|
-
(acc, entry) => {
|
|
89
|
-
const node = aux(toAbsPath(join3(path, entry)));
|
|
90
|
-
if ((node == null ? void 0 : node.type) === "dir") acc.dirs.push(node);
|
|
91
|
-
if ((node == null ? void 0 : node.type) === "file") acc.files.push(node);
|
|
92
|
-
return acc;
|
|
93
|
-
},
|
|
94
|
-
{
|
|
95
|
-
dirs: [],
|
|
96
|
-
files: []
|
|
97
|
-
}
|
|
98
|
-
);
|
|
28
|
+
function aux(curPath) {
|
|
29
|
+
if (ign == null ? void 0 : ign.ignores(curPath)) return null;
|
|
30
|
+
const stat = lstatSync(curPath);
|
|
31
|
+
const name = basename(curPath);
|
|
32
|
+
if (stat.isFile() && name.endsWith(".md")) {
|
|
99
33
|
return {
|
|
100
|
-
path:
|
|
34
|
+
path: curPath,
|
|
101
35
|
name,
|
|
102
|
-
type: "
|
|
103
|
-
|
|
104
|
-
files
|
|
36
|
+
type: "file",
|
|
37
|
+
frontmatter: fetchFrontmatter(curPath, stat.mtime)
|
|
105
38
|
};
|
|
106
39
|
}
|
|
107
|
-
if (stat.
|
|
40
|
+
if (stat.isDirectory()) {
|
|
41
|
+
const dirs = [];
|
|
42
|
+
const files = [];
|
|
43
|
+
const entries = readdirSync(curPath);
|
|
44
|
+
for (const entry of entries) {
|
|
45
|
+
const nextPath = toAbsPath(join2(curPath, entry));
|
|
46
|
+
const node = aux(nextPath);
|
|
47
|
+
if ((node == null ? void 0 : node.type) === "dir") dirs.push(node);
|
|
48
|
+
if ((node == null ? void 0 : node.type) === "file") files.push(node);
|
|
49
|
+
}
|
|
108
50
|
return {
|
|
109
|
-
path:
|
|
51
|
+
path: curPath,
|
|
110
52
|
name,
|
|
111
|
-
type: "
|
|
112
|
-
|
|
53
|
+
type: "dir",
|
|
54
|
+
dirs,
|
|
55
|
+
files
|
|
113
56
|
};
|
|
114
57
|
}
|
|
115
58
|
return null;
|
|
116
59
|
}
|
|
60
|
+
if (!isObsidianDirectory(rootPath)) throw new Error("Not a vault!");
|
|
117
61
|
return aux(rootPath);
|
|
118
62
|
}
|
|
119
|
-
function
|
|
120
|
-
const
|
|
63
|
+
function isObsidianDirectory(path) {
|
|
64
|
+
const stat = lstatSync(path);
|
|
65
|
+
if (!stat.isDirectory()) return false;
|
|
66
|
+
return existsSync(join2(path, ".obsidian"));
|
|
67
|
+
}
|
|
68
|
+
function fetchFrontmatter(path, mtime) {
|
|
69
|
+
const { data } = matter(readFileSync(path, "utf-8"));
|
|
121
70
|
return {
|
|
122
71
|
title: basename(path).replace(/\.md$/, ""),
|
|
123
72
|
description: "No description provided.",
|
|
124
|
-
last_modified:
|
|
125
|
-
|
|
126
|
-
...srcFrontmatter
|
|
73
|
+
last_modified: mtime.toISOString().split("T")[0],
|
|
74
|
+
...data
|
|
127
75
|
};
|
|
128
76
|
}
|
|
129
77
|
|
|
130
|
-
// src/
|
|
131
|
-
|
|
78
|
+
// src/core/sync/sanitizeAndMirror.ts
|
|
79
|
+
import matter2 from "gray-matter";
|
|
80
|
+
import {
|
|
81
|
+
existsSync as existsSync2,
|
|
82
|
+
mkdirSync,
|
|
83
|
+
readFileSync as readFileSync2,
|
|
84
|
+
writeFileSync
|
|
85
|
+
} from "fs";
|
|
86
|
+
function sanitizeAndCopy(fileTree, srcPath, destPath) {
|
|
87
|
+
function aux(curNode) {
|
|
88
|
+
const path = sanitizePath(toRelPath(srcPath, curNode.path));
|
|
89
|
+
const to = joinToAbs(destPath, path);
|
|
90
|
+
if (!existsSync2(to)) mkdirSync(to, { recursive: true });
|
|
91
|
+
const dirs = curNode.dirs.map((dir) => aux(dir));
|
|
92
|
+
const files = curNode.files.map((file) => {
|
|
93
|
+
const to2 = joinToAbs(
|
|
94
|
+
destPath,
|
|
95
|
+
sanitizePath(toRelPath(srcPath, file.path))
|
|
96
|
+
);
|
|
97
|
+
const fileContent = readFileSync2(file.path, "utf-8");
|
|
98
|
+
const { data: existingData, content } = matter2(fileContent);
|
|
99
|
+
const newFm = {
|
|
100
|
+
...file.frontmatter,
|
|
101
|
+
...existingData
|
|
102
|
+
};
|
|
103
|
+
writeFileSync(to2, matter2.stringify(content, newFm));
|
|
104
|
+
return {
|
|
105
|
+
original: file.name,
|
|
106
|
+
sanitized: sanitize(file.name)
|
|
107
|
+
};
|
|
108
|
+
});
|
|
109
|
+
return {
|
|
110
|
+
path,
|
|
111
|
+
dirs,
|
|
112
|
+
files,
|
|
113
|
+
name: {
|
|
114
|
+
original: curNode.name,
|
|
115
|
+
sanitized: sanitize(curNode.name)
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
return aux(fileTree);
|
|
120
|
+
}
|
|
121
|
+
function sanitizePath(s) {
|
|
122
|
+
return s.split(/[\\/]/).map((segment) => sanitize(segment)).join("/");
|
|
123
|
+
}
|
|
124
|
+
function sanitize(s) {
|
|
125
|
+
return s.replace(/[\[\]#?%/:*?"<>|]/g, "").replace(/[\s_]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "").trim();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// src/utils/file.ts
|
|
129
|
+
import { writeFileSync as writeFileSync2 } from "fs";
|
|
130
|
+
import { join as join3 } from "path";
|
|
131
|
+
function generateJson(content, name, destPath) {
|
|
132
|
+
const jsonFilePath = join3(destPath, `${name}.json`);
|
|
133
|
+
writeFileSync2(jsonFilePath, JSON.stringify(content, null, 2), "utf-8");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// src/utils/ignore.ts
|
|
137
|
+
import { existsSync as existsSync3, lstatSync as lstatSync2, readFileSync as readFileSync3 } from "fs";
|
|
138
|
+
import { join as join4 } from "path";
|
|
139
|
+
import ignore from "ignore";
|
|
140
|
+
var VaultIgnorer = class {
|
|
141
|
+
ign;
|
|
142
|
+
rootPath;
|
|
143
|
+
constructor(rootPath) {
|
|
144
|
+
this.rootPath = rootPath;
|
|
145
|
+
this.ign = ignore();
|
|
146
|
+
this.ign.add([".*"]);
|
|
147
|
+
}
|
|
148
|
+
load() {
|
|
149
|
+
const ignorePath = join4(this.rootPath, ".o2bignore");
|
|
150
|
+
if (existsSync3(ignorePath))
|
|
151
|
+
this.ign.add(readFileSync3(ignorePath).toString());
|
|
152
|
+
}
|
|
153
|
+
ignores(path) {
|
|
154
|
+
if (path === this.rootPath) return false;
|
|
155
|
+
if (!existsSync3(path)) throw new Error("somethings wrong!");
|
|
156
|
+
const stat = lstatSync2(path);
|
|
157
|
+
const relPath2 = toRelPath(this.rootPath, path) + (stat.isDirectory() ? "/" : "");
|
|
158
|
+
return this.ign.ignores(relPath2);
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// src/runO2B.ts
|
|
163
|
+
function runO2B(config) {
|
|
132
164
|
const { srcPath, destPath, ignore: ignore2 } = config;
|
|
133
|
-
if (!isObsidianDirectory(srcPath)) throw new Error("Not a obsidian vault");
|
|
134
165
|
const ign = new VaultIgnorer(srcPath);
|
|
135
166
|
if (ignore2) ign.load();
|
|
136
167
|
const fileTree = scan(srcPath, ign);
|
|
137
168
|
if (fileTree === null) throw new Error("No content in vault");
|
|
138
|
-
|
|
169
|
+
const sanitizedFileTree = sanitizeAndCopy(fileTree, srcPath, destPath);
|
|
170
|
+
generateJson(sanitizedFileTree, "file-tree", destPath);
|
|
139
171
|
}
|
|
140
172
|
export {
|
|
141
|
-
|
|
173
|
+
runO2B as default
|
|
142
174
|
};
|
|
143
175
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/sync/mirror.ts","../src/core/sync/scan.ts","../src/utils/ignore.ts","../src/utils/path.ts","../src/index.ts"],"sourcesContent":["import { copyFileSync, existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { AbsPath, FileTree } from \"../../types\";\n\nexport function mirror(\n\tfiletree: FileTree,\n\tsrcPath: AbsPath,\n\tdestPath: AbsPath,\n) {\n\tfunction aux(filetree: FileTree) {\n\t\tconst from = join(srcPath, filetree.path);\n\t\tconst to = join(destPath, filetree.path);\n\n\t\tif (filetree.type === \"dir\") {\n\t\t\tif (!existsSync(to)) mkdirSync(to, { recursive: true });\n\n\t\t\tfiletree.files.forEach((file) => {\n\t\t\t\taux(file);\n\t\t\t});\n\n\t\t\tfiletree.dirs.forEach((dir) => {\n\t\t\t\taux(dir);\n\t\t\t});\n\t\t}\n\n\t\tif (filetree.type === \"file\") {\n\t\t\tcopyFileSync(from, to);\n\t\t}\n\t}\n\n\taux(filetree);\n\tgenerateFileTree(filetree, destPath);\n}\n\nfunction generateFileTree(filetree: FileTree, destPath: AbsPath) {\n\tconst jsonFilePath = join(destPath, \"o2bFileTree.json\");\n\twriteFileSync(jsonFilePath, JSON.stringify(filetree, null, 2), \"utf-8\");\n}\n","import { lstatSync, readdirSync, readFileSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\nimport matter from \"gray-matter\";\nimport type {\n\tAbsPath,\n\tDirNode,\n\tFileNode,\n\tFileTree,\n\tFrontmatter,\n} from \"../../types\";\nimport { toAbsPath, toRelPath, type VaultIgnorer } from \"../../utils\";\n\nexport function scan(rootPath: AbsPath, ign: VaultIgnorer): FileTree | null {\n\tfunction aux(path: AbsPath): FileTree | null {\n\t\tif (ign?.ignore(path)) return null;\n\n\t\tconst stat = lstatSync(path);\n\t\tconst name = basename(path);\n\n\t\tif (stat.isDirectory()) {\n\t\t\tconst { dirs, files } = readdirSync(path).reduce(\n\t\t\t\t(acc, entry) => {\n\t\t\t\t\tconst node = aux(toAbsPath(join(path, entry)));\n\n\t\t\t\t\tif (node?.type === \"dir\") acc.dirs.push(node);\n\t\t\t\t\tif (node?.type === \"file\") acc.files.push(node);\n\n\t\t\t\t\treturn acc;\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdirs: [] as DirNode[],\n\t\t\t\t\tfiles: [] as FileNode[],\n\t\t\t\t},\n\t\t\t);\n\n\t\t\treturn {\n\t\t\t\tpath: toRelPath(rootPath, path),\n\t\t\t\tname,\n\t\t\t\ttype: \"dir\",\n\t\t\t\tdirs,\n\t\t\t\tfiles,\n\t\t\t};\n\t\t}\n\n\t\tif (stat.isFile() && name.endsWith(\".md\")) {\n\t\t\treturn {\n\t\t\t\tpath: toRelPath(rootPath, path),\n\t\t\t\tname,\n\t\t\t\ttype: \"file\",\n\t\t\t\tfrontmatter: getFrontmatter(path),\n\t\t\t};\n\t\t}\n\n\t\treturn null;\n\t}\n\n\treturn aux(rootPath);\n}\n\nfunction getFrontmatter(path: AbsPath): Frontmatter {\n\tconst { data: srcFrontmatter } = matter(readFileSync(path, \"utf-8\"));\n\treturn {\n\t\ttitle: basename(path).replace(/\\.md$/, \"\"),\n\t\tdescription: \"No description provided.\",\n\t\tlast_modified: lstatSync(path).mtime.toLocaleDateString(\"sv-SE\"), // \"sv-SE\" (스웨덴어)는 YYYY-MM-DD 형식으로 출력되는 유명한 트릭입니다.\n\t\t...srcFrontmatter,\n\t};\n}\n","import { existsSync, lstatSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport ignore, { type Ignore } from \"ignore\";\nimport type { AbsPath } from \"../types\";\nimport { toRelPath } from \"./path\";\n\nexport class VaultIgnorer {\n\tprivate ign: Ignore;\n\tprivate rootPath: AbsPath;\n\n\tconstructor(rootPath: AbsPath) {\n\t\tthis.rootPath = rootPath;\n\t\tthis.ign = ignore();\n\n\t\tthis.ign.add([\".*\"]);\n\t}\n\n\tpublic load() {\n\t\tconst ignorePath = join(this.rootPath, \".o2bignore\");\n\t\tif (existsSync(ignorePath))\n\t\t\tthis.ign.add(readFileSync(ignorePath).toString());\n\t}\n\n\tpublic ignore(path: AbsPath): boolean {\n\t\tif (path === this.rootPath) return false;\n\n\t\tif (!existsSync(path)) throw new Error(\"somethings wrong!\");\n\n\t\tconst stat = lstatSync(path);\n\n\t\tconst relPath =\n\t\t\ttoRelPath(this.rootPath, path) + (stat.isDirectory() ? \"/\" : \"\");\n\n\t\treturn this.ign.ignores(relPath);\n\t}\n}\n","import { lstatSync, readdirSync } from \"node:fs\";\nimport { isAbsolute, relative, resolve } from \"node:path\";\nimport type { AbsPath, RelPath } from \"../types\";\n\nfunction toAbsPath(path: string): AbsPath {\n\tif (!isAbsolute(path)) path = resolve(path);\n\treturn path as AbsPath;\n}\n\nfunction toRelPath(from: AbsPath, to: AbsPath): RelPath {\n\treturn relative(from, to) as RelPath;\n}\n\nfunction isObsidianDirectory(path: AbsPath) {\n\tconst stat = lstatSync(path);\n\n\tif (!stat.isDirectory()) return false;\n\n\tconst obsMetaDir = readdirSync(path, { withFileTypes: true })\n\t\t.filter((node) => node.isDirectory())\n\t\t.filter((node) => node.name === \".obsidian\");\n\n\tif (obsMetaDir.length !== 1) return false;\n\n\treturn true;\n}\n\nexport { toAbsPath, toRelPath, isObsidianDirectory };\n","import { mirror, scan } from \"./core/sync\";\nimport type { Config } from \"./types\";\nimport { isObsidianDirectory, VaultIgnorer } from \"./utils\";\n\nexport default function main(config: Config) {\n const { srcPath, destPath, ignore } = config;\n if (!isObsidianDirectory(srcPath)) throw new Error(\"Not a obsidian vault\");\n\n const ign = new VaultIgnorer(srcPath);\n if (ignore) ign.load();\n\n const fileTree = scan(srcPath, ign);\n if (fileTree === null) throw new Error(\"No content in vault\");\n\n mirror(fileTree, srcPath, destPath);\n}\n"],"mappings":"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","names":["filetree","lstatSync","readdirSync","readFileSync","join","existsSync","lstatSync","join","join","existsSync","lstatSync","lstatSync","readdirSync","join","readFileSync","ignore"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/sync/scan.ts","../src/utils/path.ts","../src/types/path.ts","../src/core/sync/sanitizeAndMirror.ts","../src/utils/file.ts","../src/utils/ignore.ts","../src/runO2B.ts"],"sourcesContent":["import { existsSync, lstatSync, readdirSync, readFileSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\nimport matter from \"gray-matter\";\nimport type { DirNode, FileNode, Frontmatter } from \"@o2bTypes/filetree\";\nimport { toAbsPath, toRelPath } from \"@utils/path\";\nimport { AbsPath } from \"@o2bTypes/path\";\nimport { VaultIgnorer } from \"@utils/ignore\";\n\nexport function scan(rootPath: AbsPath, ign: VaultIgnorer): DirNode | null {\n function aux(curPath: AbsPath): FileNode | DirNode | null {\n if (ign?.ignores(curPath)) return null;\n\n const stat = lstatSync(curPath);\n const name = basename(curPath);\n\n if (stat.isFile() && name.endsWith(\".md\")) {\n return {\n path: curPath,\n name,\n type: \"file\",\n frontmatter: fetchFrontmatter(curPath, stat.mtime),\n };\n }\n\n if (stat.isDirectory()) {\n const dirs: DirNode[] = [];\n const files: FileNode[] = [];\n const entries = readdirSync(curPath);\n\n for (const entry of entries) {\n const nextPath = toAbsPath(join(curPath, entry));\n const node = aux(nextPath);\n\n if (node?.type === \"dir\") dirs.push(node);\n if (node?.type === \"file\") files.push(node);\n }\n\n return {\n path: curPath,\n name,\n type: \"dir\",\n dirs,\n files,\n };\n }\n\n return null;\n }\n\n if (!isObsidianDirectory(rootPath)) throw new Error(\"Not a vault!\");\n\n return aux(rootPath) as DirNode;\n}\n\nfunction isObsidianDirectory(path: AbsPath) {\n const stat = lstatSync(path);\n\n if (!stat.isDirectory()) return false;\n return existsSync(join(path, \".obsidian\"));\n}\n\nfunction fetchFrontmatter(path: AbsPath, mtime: Date): Frontmatter {\n // [TODO] Matter는 파일 전체 다 읽는거라 stream으로 --- 파트만 가져오도록\n const { data } = matter(readFileSync(path, \"utf-8\"));\n return {\n title: basename(path).replace(/\\.md$/, \"\"),\n description: \"No description provided.\",\n last_modified: mtime.toISOString().split(\"T\")[0],\n ...data,\n };\n}\n","import { isAbsolute, join, relative, resolve } from \"node:path\";\nimport { absPath, relPath, type AbsPath, type RelPath } from \"@o2bTypes/path\";\n\nfunction toAbsPath(path: string): AbsPath {\n if (!isAbsolute(path)) path = resolve(path);\n return absPath.parse(path);\n}\n\nfunction toRelPath(from: AbsPath, to: AbsPath): RelPath {\n return relPath.parse(relative(from, to));\n}\n\nexport function joinToAbs(base: AbsPath, ...parts: string[]): AbsPath {\n return absPath.parse(join(base, ...parts));\n}\n\nexport function joinToRel(base: RelPath, ...parts: string[]): RelPath {\n return relPath.parse(join(base, ...parts));\n}\n\nexport { toAbsPath, toRelPath };\n","import z from \"zod\";\n\ntype AbsPath = z.infer<typeof absPath>;\nconst absPath = z.string().brand<\"absPath\">();\n\ntype RelPath = z.infer<typeof relPath>;\nconst relPath = z.string().brand<\"relpath\">();\n\nexport { type AbsPath, type RelPath, absPath, relPath };\n","import { DirNode, Name, SanitizedFileTree } from \"@o2bTypes/filetree\";\nimport { AbsPath, RelPath } from \"@o2bTypes/path\";\nimport { joinToAbs, toAbsPath, toRelPath } from \"@utils/path\";\nimport matter from \"gray-matter\";\nimport {\n copyFileSync,\n existsSync,\n mkdirSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\";\n\n// 이거 사실 더 효율적으로 할 수 있을 것 같은데, sanitizedName을 계속 넘기면서 path 빌드\n// 근데 우선 좀 해보고 필요없는 것도 좀 깨고\n\nexport function sanitizeAndCopy(\n fileTree: DirNode,\n srcPath: AbsPath,\n destPath: AbsPath,\n): SanitizedFileTree {\n function aux(curNode: DirNode): SanitizedFileTree {\n const path = sanitizePath(toRelPath(srcPath, curNode.path));\n const to = joinToAbs(destPath, path);\n\n if (!existsSync(to)) mkdirSync(to, { recursive: true });\n\n const dirs = curNode.dirs.map((dir) => aux(dir));\n const files: Name[] = curNode.files.map((file) => {\n const to = joinToAbs(\n destPath,\n sanitizePath(toRelPath(srcPath, file.path)),\n );\n\n const fileContent = readFileSync(file.path, \"utf-8\");\n const { data: existingData, content } = matter(fileContent);\n const newFm = {\n ...file.frontmatter,\n ...existingData,\n };\n\n writeFileSync(to, matter.stringify(content, newFm));\n\n return {\n original: file.name,\n sanitized: sanitize(file.name),\n };\n });\n\n return {\n path,\n dirs,\n files,\n name: {\n original: curNode.name,\n sanitized: sanitize(curNode.name),\n },\n };\n }\n\n return aux(fileTree);\n}\n\nfunction sanitizePath<K extends string | RelPath>(s: K): K {\n return s\n .split(/[\\\\/]/)\n .map((segment) => sanitize(segment))\n .join(\"/\") as K;\n}\n\nfunction sanitize<K extends string | RelPath>(s: K): K {\n return (\n s\n // OS 및 URL 예약 특수문자 제거/치환\n .replace(/[\\[\\]#?%/:*?\"<>|]/g, \"\")\n // 공백 및 특수 기호를 하이픈으로 변경\n .replace(/[\\s_]+/g, \"-\")\n // 연속된 하이픈 제거\n .replace(/-+/g, \"-\")\n // 앞뒤에 붙은 하이픈 제거\n .replace(/^-+|-+$/g, \"\")\n .trim() as K\n );\n}\n","import { writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { AbsPath } from \"@o2bTypes/path\";\n\nexport function generateJson(\n content: unknown,\n name: string,\n destPath: AbsPath,\n) {\n const jsonFilePath = join(destPath, `${name}.json`);\n writeFileSync(jsonFilePath, JSON.stringify(content, null, 2), \"utf-8\");\n}\n","import { existsSync, lstatSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport ignore, { type Ignore } from \"ignore\";\n\nimport type { AbsPath } from \"@o2bTypes/path\";\nimport { toRelPath } from \"@utils/path\";\n\nexport class VaultIgnorer {\n private ign: Ignore;\n private rootPath: AbsPath;\n\n constructor(rootPath: AbsPath) {\n this.rootPath = rootPath;\n this.ign = ignore();\n\n this.ign.add([\".*\"]);\n }\n\n public load() {\n const ignorePath = join(this.rootPath, \".o2bignore\");\n if (existsSync(ignorePath))\n this.ign.add(readFileSync(ignorePath).toString());\n }\n\n public ignores(path: AbsPath): boolean {\n if (path === this.rootPath) return false;\n // [TODO] what?\n if (!existsSync(path)) throw new Error(\"somethings wrong!\");\n\n const stat = lstatSync(path);\n const relPath =\n toRelPath(this.rootPath, path) + (stat.isDirectory() ? \"/\" : \"\");\n\n return this.ign.ignores(relPath);\n }\n}\n","import { scan, sanitizeAndCopy } from \"@core/sync\";\nimport type { Config } from \"@o2bTypes/config\";\nimport { generateJson } from \"@utils/file\";\nimport { VaultIgnorer } from \"@utils/ignore\";\n\nexport default function runO2B(config: Config) {\n const { srcPath, destPath, ignore } = config;\n\n const ign = new VaultIgnorer(srcPath);\n if (ignore) ign.load();\n\n const fileTree = scan(srcPath, ign);\n if (fileTree === null) throw new Error(\"No content in vault\");\n\n const sanitizedFileTree = sanitizeAndCopy(fileTree, srcPath, destPath);\n\n generateJson(sanitizedFileTree, \"file-tree\", destPath);\n}\n"],"mappings":"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","names":["join","join","matter","existsSync","readFileSync","to","writeFileSync","join","existsSync","lstatSync","readFileSync","join","join","existsSync","readFileSync","lstatSync","relPath","ignore"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@o2b/meta",
|
|
3
|
-
"version": "1.0.0-dev.
|
|
3
|
+
"version": "1.0.0-dev.10",
|
|
4
4
|
"description": "filetree generator for o2b",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -13,12 +13,13 @@
|
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"bin": {
|
|
16
|
-
"o2b-meta": "
|
|
16
|
+
"o2b-meta": "dist/cli.js"
|
|
17
17
|
},
|
|
18
18
|
"scripts": {
|
|
19
19
|
"build": "tsup",
|
|
20
20
|
"test": "vitest",
|
|
21
|
-
"test:run": "vitest run"
|
|
21
|
+
"test:run": "vitest run",
|
|
22
|
+
"dev": "tsup src/cli.ts --watch --onSuccess \"node dist/cli.js ./tests/fixtures/valid-vault --destPath ./tests/fixtures/"
|
|
22
23
|
},
|
|
23
24
|
"keywords": [],
|
|
24
25
|
"author": "goonco",
|
|
@@ -33,12 +34,17 @@
|
|
|
33
34
|
"@vitest/ui": "^4.0.16",
|
|
34
35
|
"tsup": "^8.5.1",
|
|
35
36
|
"typescript": "^5.9.3",
|
|
36
|
-
"
|
|
37
|
+
"vite-tsconfig-paths": "^6.0.5",
|
|
38
|
+
"vitest": "^4.0.16",
|
|
39
|
+
"zod": "^3.25.76"
|
|
37
40
|
},
|
|
38
41
|
"dependencies": {
|
|
39
42
|
"cac": "^6.7.14",
|
|
40
43
|
"gray-matter": "^4.0.3",
|
|
41
44
|
"ignore": "^7.0.5",
|
|
42
45
|
"lilconfig": "^3.1.3"
|
|
46
|
+
},
|
|
47
|
+
"peerDependencies": {
|
|
48
|
+
"zod": "^3.23.8"
|
|
43
49
|
}
|
|
44
50
|
}
|