@o2b/meta 1.0.0-dev.1 → 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 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-alpha.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": "./dist/cli.js"
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
- vitest: "^4.0.16"
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 import_node_fs4 = require("fs");
104
- var import_node_path4 = require("path");
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/ignore.ts
108
- var import_node_fs3 = require("fs");
109
- var import_node_path3 = require("path");
110
- var import_ignore = __toESM(require("ignore"));
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, import_node_path2.isAbsolute)(path)) path = (0, import_node_path2.resolve)(path);
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, import_node_path2.relative)(from, to);
100
+ return relPath.parse((0, import_node_path.relative)(from, to));
121
101
  }
122
- function isObsidianDirectory(path) {
123
- const stat = (0, import_node_fs2.lstatSync)(path);
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(path) {
156
- if (ign == null ? void 0 : ign.ignore(path)) return null;
157
- const stat = (0, import_node_fs4.lstatSync)(path);
158
- const name = (0, import_node_path4.basename)(path);
159
- if (stat.isDirectory()) {
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: toRelPath(rootPath, path),
114
+ path: curPath,
174
115
  name,
175
- type: "dir",
176
- dirs,
177
- files
116
+ type: "file",
117
+ frontmatter: fetchFrontmatter(curPath, stat.mtime)
178
118
  };
179
119
  }
180
- if (stat.isFile() && name.endsWith(".md")) {
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: toRelPath(rootPath, path),
131
+ path: curPath,
183
132
  name,
184
- type: "file",
185
- frontmatter: getFrontmatter(path)
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 getFrontmatter(path) {
193
- const { data: srcFrontmatter } = (0, import_gray_matter.default)((0, import_node_fs4.readFileSync)(path, "utf-8"));
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, import_node_path4.basename)(path).replace(/\.md$/, ""),
151
+ title: (0, import_node_path2.basename)(path).replace(/\.md$/, ""),
196
152
  description: "No description provided.",
197
- last_modified: (0, import_node_fs4.lstatSync)(path).mtime.toLocaleDateString("sv-SE"),
198
- // "sv-SE" (스웨덴어)는 YYYY-MM-DD 형식으로 출력되는 유명한 트릭입니다.
199
- ...srcFrontmatter
153
+ last_modified: mtime.toISOString().split("T")[0],
154
+ ...data
200
155
  };
201
156
  }
202
157
 
203
- // src/index.ts
204
- function main(config) {
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
- mirror(fileTree, srcPath, destPath);
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
- main({
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":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,iBAAgB;;;ACAhB;AAAA,EACE,MAAQ;AAAA,EACR,SAAW;AAAA,EACX,aAAe;AAAA,EACf,MAAQ;AAAA,EACR,QAAU;AAAA,EACV,OAAS;AAAA,EACT,SAAW;AAAA,IACT,KAAK;AAAA,MACH,OAAS;AAAA,MACT,QAAU;AAAA,MACV,SAAW;AAAA,IACb;AAAA,EACF;AAAA,EACA,KAAO;AAAA,IACL,YAAY;AAAA,EACd;AAAA,EACA,SAAW;AAAA,IACT,OAAS;AAAA,IACT,MAAQ;AAAA,IACR,YAAY;AAAA,IACZ,KAAO;AAAA,EACT;AAAA,EACA,UAAY,CAAC;AAAA,EACb,QAAU;AAAA,EACV,SAAW;AAAA,EACX,MAAQ;AAAA,EACR,OAAS;AAAA,IACP;AAAA,EACF;AAAA,EACA,iBAAmB;AAAA,IACjB,kBAAkB;AAAA,IAClB,eAAe;AAAA,IACf,cAAc;AAAA,IACd,MAAQ;AAAA,IACR,YAAc;AAAA,IACd,uBAAuB;AAAA,IACvB,QAAU;AAAA,IACV,KAAO;AAAA,EACT;AAAA,EACA,cAAgB;AAAA,IACd,KAAO;AAAA,IACP,eAAe;AAAA,IACf,QAAU;AAAA,IACV,WAAa;AAAA,EACf;AAAA,EACA,kBAAoB;AAAA,IAClB,KAAO;AAAA,EACT;AACF;;;ACjDA,qBAAiE;AACjE,IAAAA,oBAA+B;AAC/B,yBAAmB;;;ACFnB,uBAAoD;;;ACApD,iBAAc;AAGd,IAAM,UAAU,WAAAC,QAAE,OAAO,EAAE,MAAiB;AAG5C,IAAM,UAAU,WAAAA,QAAE,OAAO,EAAE,MAAiB;;;ADH5C,SAAS,UAAU,MAAuB;AACxC,MAAI,KAAC,6BAAW,IAAI,EAAG,YAAO,0BAAQ,IAAI;AAC1C,SAAO,QAAQ,MAAM,IAAI;AAC3B;AAEA,SAAS,UAAU,MAAe,IAAsB;AACtD,SAAO,QAAQ,UAAM,2BAAS,MAAM,EAAE,CAAC;AACzC;AAEO,SAAS,UAAU,SAAkB,OAA0B;AACpE,SAAO,QAAQ,UAAM,uBAAK,MAAM,GAAG,KAAK,CAAC;AAC3C;;;ADNO,SAAS,KAAK,UAAmB,KAAmC;AACzE,WAAS,IAAI,SAA6C;AACxD,QAAI,2BAAK,QAAQ,SAAU,QAAO;AAElC,UAAM,WAAO,0BAAU,OAAO;AAC9B,UAAM,WAAO,4BAAS,OAAO;AAE7B,QAAI,KAAK,OAAO,KAAK,KAAK,SAAS,KAAK,GAAG;AACzC,aAAO;AAAA,QACL,MAAM;AAAA,QACN;AAAA,QACA,MAAM;AAAA,QACN,aAAa,iBAAiB,SAAS,KAAK,KAAK;AAAA,MACnD;AAAA,IACF;AAEA,QAAI,KAAK,YAAY,GAAG;AACtB,YAAM,OAAkB,CAAC;AACzB,YAAM,QAAoB,CAAC;AAC3B,YAAM,cAAU,4BAAY,OAAO;AAEnC,iBAAW,SAAS,SAAS;AAC3B,cAAM,WAAW,cAAU,wBAAK,SAAS,KAAK,CAAC;AAC/C,cAAM,OAAO,IAAI,QAAQ;AAEzB,aAAI,6BAAM,UAAS,MAAO,MAAK,KAAK,IAAI;AACxC,aAAI,6BAAM,UAAS,OAAQ,OAAM,KAAK,IAAI;AAAA,MAC5C;AAEA,aAAO;AAAA,QACL,MAAM;AAAA,QACN;AAAA,QACA,MAAM;AAAA,QACN;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,oBAAoB,QAAQ,EAAG,OAAM,IAAI,MAAM,cAAc;AAElE,SAAO,IAAI,QAAQ;AACrB;AAEA,SAAS,oBAAoB,MAAe;AAC1C,QAAM,WAAO,0BAAU,IAAI;AAE3B,MAAI,CAAC,KAAK,YAAY,EAAG,QAAO;AAChC,aAAO,+BAAW,wBAAK,MAAM,WAAW,CAAC;AAC3C;AAEA,SAAS,iBAAiB,MAAe,OAA0B;AAEjE,QAAM,EAAE,KAAK,QAAI,mBAAAC,aAAO,6BAAa,MAAM,OAAO,CAAC;AACnD,SAAO;AAAA,IACL,WAAO,4BAAS,IAAI,EAAE,QAAQ,SAAS,EAAE;AAAA,IACzC,aAAa;AAAA,IACb,eAAe,MAAM,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC;AAAA,IAC/C,GAAG;AAAA,EACL;AACF;;;AGnEA,IAAAC,sBAAmB;AACnB,IAAAC,kBAMO;AAKA,SAAS,gBACd,UACA,SACA,UACmB;AACnB,WAAS,IAAI,SAAqC;AAChD,UAAM,OAAO,aAAa,UAAU,SAAS,QAAQ,IAAI,CAAC;AAC1D,UAAM,KAAK,UAAU,UAAU,IAAI;AAEnC,QAAI,KAAC,4BAAW,EAAE,EAAG,gCAAU,IAAI,EAAE,WAAW,KAAK,CAAC;AAEtD,UAAM,OAAO,QAAQ,KAAK,IAAI,CAAC,QAAQ,IAAI,GAAG,CAAC;AAC/C,UAAM,QAAgB,QAAQ,MAAM,IAAI,CAAC,SAAS;AAChD,YAAMC,MAAK;AAAA,QACT;AAAA,QACA,aAAa,UAAU,SAAS,KAAK,IAAI,CAAC;AAAA,MAC5C;AAEA,YAAM,kBAAc,8BAAa,KAAK,MAAM,OAAO;AACnD,YAAM,EAAE,MAAM,cAAc,QAAQ,QAAI,oBAAAC,SAAO,WAAW;AAC1D,YAAM,QAAQ;AAAA,QACZ,GAAG,KAAK;AAAA,QACR,GAAG;AAAA,MACL;AAEA,yCAAcD,KAAI,oBAAAC,QAAO,UAAU,SAAS,KAAK,CAAC;AAElD,aAAO;AAAA,QACL,UAAU,KAAK;AAAA,QACf,WAAW,SAAS,KAAK,IAAI;AAAA,MAC/B;AAAA,IACF,CAAC;AAED,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA,MAAM;AAAA,QACJ,UAAU,QAAQ;AAAA,QAClB,WAAW,SAAS,QAAQ,IAAI;AAAA,MAClC;AAAA,IACF;AAAA,EACF;AAEA,SAAO,IAAI,QAAQ;AACrB;AAEA,SAAS,aAAyC,GAAS;AACzD,SAAO,EACJ,MAAM,OAAO,EACb,IAAI,CAAC,YAAY,SAAS,OAAO,CAAC,EAClC,KAAK,GAAG;AACb;AAEA,SAAS,SAAqC,GAAS;AACrD,SACE,EAEG,QAAQ,sBAAsB,EAAE,EAEhC,QAAQ,WAAW,GAAG,EAEtB,QAAQ,OAAO,GAAG,EAElB,QAAQ,YAAY,EAAE,EACtB,KAAK;AAEZ;;;AClFA,IAAAC,kBAA8B;AAC9B,IAAAC,oBAAqB;AAGd,SAAS,aACd,SACA,MACA,UACA;AACA,QAAM,mBAAe,wBAAK,UAAU,GAAG,IAAI,OAAO;AAClD,qCAAc,cAAc,KAAK,UAAU,SAAS,MAAM,CAAC,GAAG,OAAO;AACvE;;;ACXA,IAAAC,kBAAoD;AACpD,IAAAC,oBAAqB;AACrB,oBAAoC;AAK7B,IAAM,eAAN,MAAmB;AAAA,EAChB;AAAA,EACA;AAAA,EAER,YAAY,UAAmB;AAC7B,SAAK,WAAW;AAChB,SAAK,UAAM,cAAAC,SAAO;AAElB,SAAK,IAAI,IAAI,CAAC,IAAI,CAAC;AAAA,EACrB;AAAA,EAEO,OAAO;AACZ,UAAM,iBAAa,wBAAK,KAAK,UAAU,YAAY;AACnD,YAAI,4BAAW,UAAU;AACvB,WAAK,IAAI,QAAI,8BAAa,UAAU,EAAE,SAAS,CAAC;AAAA,EACpD;AAAA,EAEO,QAAQ,MAAwB;AACrC,QAAI,SAAS,KAAK,SAAU,QAAO;AAEnC,QAAI,KAAC,4BAAW,IAAI,EAAG,OAAM,IAAI,MAAM,mBAAmB;AAE1D,UAAM,WAAO,2BAAU,IAAI;AAC3B,UAAMC,WACJ,UAAU,KAAK,UAAU,IAAI,KAAK,KAAK,YAAY,IAAI,MAAM;AAE/D,WAAO,KAAK,IAAI,QAAQA,QAAO;AAAA,EACjC;AACF;;;AC9Be,SAAR,OAAwB,QAAgB;AAC7C,QAAM,EAAE,SAAS,UAAU,QAAAC,QAAO,IAAI;AAEtC,QAAM,MAAM,IAAI,aAAa,OAAO;AACpC,MAAIA,QAAQ,KAAI,KAAK;AAErB,QAAM,WAAW,KAAK,SAAS,GAAG;AAClC,MAAI,aAAa,KAAM,OAAM,IAAI,MAAM,qBAAqB;AAE5D,QAAM,oBAAoB,gBAAgB,UAAU,SAAS,QAAQ;AAErE,eAAa,mBAAmB,aAAa,QAAQ;AACvD;;;ARZA,IAAM,UAAM,WAAAC,SAAI,UAAU;AAE1B,IACG,QAAQ,aAAa,mBAAmB,EACxC,OAAO,yBAAyB,QAAQ;AAAA,EACvC,SAAS;AACX,CAAC,EACA,OAAO,YAAY,iCAAiC;AAAA,EACnD,SAAS;AACX,CAAC,EACA,OAAO,UAAU,WAAW;AAAA,EAC3B,SAAS;AACX,CAAC,EACA,OAAO,CAAC,SAAS,WAAW;AAC3B,QAAM,EAAE,UAAU,QAAAC,SAAQ,KAAK,IAAI;AAEnC,QAAM,YAAY,YAAY,IAAI;AAElC,MAAI,MAAM;AACR,WAAO;AAAA,MACL,SAAS,UAAU,OAAO;AAAA,MAC1B,UAAU,UAAU,QAAQ;AAAA,MAC5B,QAAAA;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,UAAU,YAAY,IAAI;AAChC,QAAM,YAAY,UAAU,WAAW,QAAQ,CAAC;AAEhD,UAAQ,IAAI;AAAA,sBAAoB;AAChC,UAAQ,IAAI,YAAY,QAAQ,IAAI;AAEtC,CAAC;AAEH,IAAI,KAAK;AACT,IAAI,QAAQ,gBAAI,OAAO;AAGvB,IAAI,MAAM;","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
- declare const __brand: unique symbol;
2
- type Branded<K, T> = K & {
3
- [__brand]: T;
4
- };
1
+ import z from 'zod';
5
2
 
6
- type AbsPath = Branded<string, "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 main(config: Config): void;
12
+ declare function runO2B(config: Config): void;
15
13
 
16
- export { main as default };
14
+ export { runO2B as default };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,7 @@
1
- declare const __brand: unique symbol;
2
- type Branded<K, T> = K & {
3
- [__brand]: T;
4
- };
1
+ import z from 'zod';
5
2
 
6
- type AbsPath = Branded<string, "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 main(config: Config): void;
12
+ declare function runO2B(config: Config): void;
15
13
 
16
- export { main as default };
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: () => main
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 import_node_fs4 = require("fs");
66
- var import_node_path4 = require("path");
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/ignore.ts
70
- var import_node_fs3 = require("fs");
71
- var import_node_path3 = require("path");
72
- var import_ignore = __toESM(require("ignore"));
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, import_node_path2.isAbsolute)(path)) path = (0, import_node_path2.resolve)(path);
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, import_node_path2.relative)(from, to);
56
+ return relPath.parse((0, import_node_path.relative)(from, to));
83
57
  }
84
- function isObsidianDirectory(path) {
85
- const stat = (0, import_node_fs2.lstatSync)(path);
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(path) {
118
- if (ign == null ? void 0 : ign.ignore(path)) return null;
119
- const stat = (0, import_node_fs4.lstatSync)(path);
120
- const name = (0, import_node_path4.basename)(path);
121
- if (stat.isDirectory()) {
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: toRelPath(rootPath, path),
70
+ path: curPath,
136
71
  name,
137
- type: "dir",
138
- dirs,
139
- files
72
+ type: "file",
73
+ frontmatter: fetchFrontmatter(curPath, stat.mtime)
140
74
  };
141
75
  }
142
- if (stat.isFile() && name.endsWith(".md")) {
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: toRelPath(rootPath, path),
87
+ path: curPath,
145
88
  name,
146
- type: "file",
147
- frontmatter: getFrontmatter(path)
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 getFrontmatter(path) {
155
- const { data: srcFrontmatter } = (0, import_gray_matter.default)((0, import_node_fs4.readFileSync)(path, "utf-8"));
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, import_node_path4.basename)(path).replace(/\.md$/, ""),
107
+ title: (0, import_node_path2.basename)(path).replace(/\.md$/, ""),
158
108
  description: "No description provided.",
159
- last_modified: (0, import_node_fs4.lstatSync)(path).mtime.toLocaleDateString("sv-SE"),
160
- // "sv-SE" (스웨덴어)는 YYYY-MM-DD 형식으로 출력되는 유명한 트릭입니다.
161
- ...srcFrontmatter
109
+ last_modified: mtime.toISOString().split("T")[0],
110
+ ...data
162
111
  };
163
112
  }
164
113
 
165
- // src/index.ts
166
- function main(config) {
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
- mirror(fileTree, srcPath, destPath);
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 as lstatSync3, readdirSync as readdirSync2, readFileSync as readFileSync2 } from "fs";
31
- import { basename, join as join3 } from "path";
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/ignore.ts
35
- import { existsSync as existsSync2, lstatSync as lstatSync2, readFileSync } from "fs";
36
- import { join as join2 } from "path";
37
- import ignore from "ignore";
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 isObsidianDirectory(path) {
50
- const stat = lstatSync(path);
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(path) {
83
- if (ign == null ? void 0 : ign.ignore(path)) return null;
84
- const stat = lstatSync3(path);
85
- const name = basename(path);
86
- if (stat.isDirectory()) {
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: toRelPath(rootPath, path),
34
+ path: curPath,
101
35
  name,
102
- type: "dir",
103
- dirs,
104
- files
36
+ type: "file",
37
+ frontmatter: fetchFrontmatter(curPath, stat.mtime)
105
38
  };
106
39
  }
107
- if (stat.isFile() && name.endsWith(".md")) {
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: toRelPath(rootPath, path),
51
+ path: curPath,
110
52
  name,
111
- type: "file",
112
- frontmatter: getFrontmatter(path)
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 getFrontmatter(path) {
120
- const { data: srcFrontmatter } = matter(readFileSync2(path, "utf-8"));
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: lstatSync3(path).mtime.toLocaleDateString("sv-SE"),
125
- // "sv-SE" (스웨덴어)는 YYYY-MM-DD 형식으로 출력되는 유명한 트릭입니다.
126
- ...srcFrontmatter
73
+ last_modified: mtime.toISOString().split("T")[0],
74
+ ...data
127
75
  };
128
76
  }
129
77
 
130
- // src/index.ts
131
- function main(config) {
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
- mirror(fileTree, srcPath, destPath);
169
+ const sanitizedFileTree = sanitizeAndCopy(fileTree, srcPath, destPath);
170
+ generateJson(sanitizedFileTree, "file-tree", destPath);
139
171
  }
140
172
  export {
141
- main as default
173
+ runO2B as default
142
174
  };
143
175
  //# sourceMappingURL=index.mjs.map
@@ -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.1",
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": "./dist/cli.js"
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,13 +34,17 @@
33
34
  "@vitest/ui": "^4.0.16",
34
35
  "tsup": "^8.5.1",
35
36
  "typescript": "^5.9.3",
36
- "vitest": "^4.0.16"
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
- "lilconfig": "^3.1.3",
43
- "zod": "^4.3.5"
45
+ "lilconfig": "^3.1.3"
46
+ },
47
+ "peerDependencies": {
48
+ "zod": "^3.23.8"
44
49
  }
45
50
  }