@o2b/meta 1.0.0-dev.4 → 1.0.0-dev.6

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-dev.3",
32
+ version: "1.0.0-dev.5",
32
33
  description: "filetree generator for o2b",
33
34
  main: "./dist/index.js",
34
35
  module: "./dist/index.mjs",
@@ -41,7 +42,7 @@ 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",
@@ -61,6 +62,7 @@ var package_default = {
61
62
  "@vitest/ui": "^4.0.16",
62
63
  tsup: "^8.5.1",
63
64
  typescript: "^5.9.3",
65
+ "vite-tsconfig-paths": "^6.0.5",
64
66
  vitest: "^4.0.16",
65
67
  zod: "^3.25.76"
66
68
  },
@@ -96,32 +98,29 @@ function mirror(filetree, srcPath, destPath) {
96
98
  }
97
99
  }
98
100
  aux(filetree);
99
- generateFileTree(filetree, destPath);
100
- }
101
- function generateFileTree(filetree, destPath) {
102
- const jsonFilePath = (0, import_node_path.join)(destPath, "o2bFileTree.json");
103
- (0, import_node_fs.writeFileSync)(jsonFilePath, JSON.stringify(filetree, null, 2), "utf-8");
104
101
  }
105
102
 
106
103
  // src/core/sync/scan.ts
107
- var import_node_fs4 = require("fs");
108
- var import_node_path4 = require("path");
109
- var import_gray_matter = __toESM(require("gray-matter"));
110
-
111
- // src/utils/ignore.ts
112
104
  var import_node_fs3 = require("fs");
113
105
  var import_node_path3 = require("path");
114
- var import_ignore = __toESM(require("ignore"));
106
+ var import_gray_matter = __toESM(require("gray-matter"));
115
107
 
116
108
  // src/utils/path.ts
117
109
  var import_node_fs2 = require("fs");
118
110
  var import_node_path2 = require("path");
111
+
112
+ // src/types/path.ts
113
+ var import_zod = __toESM(require("zod"));
114
+ var absPath = import_zod.default.string().brand();
115
+ var relPath = import_zod.default.string().brand();
116
+
117
+ // src/utils/path.ts
119
118
  function toAbsPath(path) {
120
119
  if (!(0, import_node_path2.isAbsolute)(path)) path = (0, import_node_path2.resolve)(path);
121
- return path;
120
+ return absPath.parse(path);
122
121
  }
123
122
  function toRelPath(from, to) {
124
- return (0, import_node_path2.relative)(from, to);
123
+ return relPath.parse((0, import_node_path2.relative)(from, to));
125
124
  }
126
125
  function isObsidianDirectory(path) {
127
126
  const stat = (0, import_node_fs2.lstatSync)(path);
@@ -131,79 +130,117 @@ function isObsidianDirectory(path) {
131
130
  return true;
132
131
  }
133
132
 
134
- // src/utils/ignore.ts
135
- var VaultIgnorer = class {
136
- ign;
137
- rootPath;
138
- constructor(rootPath) {
139
- this.rootPath = rootPath;
140
- this.ign = (0, import_ignore.default)();
141
- this.ign.add([".*"]);
142
- }
143
- load() {
144
- const ignorePath = (0, import_node_path3.join)(this.rootPath, ".o2bignore");
145
- if ((0, import_node_fs3.existsSync)(ignorePath))
146
- this.ign.add((0, import_node_fs3.readFileSync)(ignorePath).toString());
147
- }
148
- ignore(path) {
149
- if (path === this.rootPath) return false;
150
- if (!(0, import_node_fs3.existsSync)(path)) throw new Error("somethings wrong!");
151
- const stat = (0, import_node_fs3.lstatSync)(path);
152
- const relPath = toRelPath(this.rootPath, path) + (stat.isDirectory() ? "/" : "");
153
- return this.ign.ignores(relPath);
154
- }
155
- };
156
-
157
133
  // src/core/sync/scan.ts
158
134
  function scan(rootPath, ign) {
159
135
  function aux(path) {
160
136
  if (ign == null ? void 0 : ign.ignore(path)) return null;
161
- const stat = (0, import_node_fs4.lstatSync)(path);
162
- const name = (0, import_node_path4.basename)(path);
163
- if (stat.isDirectory()) {
164
- const { dirs, files } = (0, import_node_fs4.readdirSync)(path).reduce(
165
- (acc, entry) => {
166
- const node = aux(toAbsPath((0, import_node_path4.join)(path, entry)));
167
- if ((node == null ? void 0 : node.type) === "dir") acc.dirs.push(node);
168
- if ((node == null ? void 0 : node.type) === "file") acc.files.push(node);
169
- return acc;
170
- },
171
- {
172
- dirs: [],
173
- files: []
174
- }
175
- );
137
+ const stat = (0, import_node_fs3.lstatSync)(path);
138
+ const name = (0, import_node_path3.basename)(path);
139
+ if (stat.isFile() && name.endsWith(".md")) {
176
140
  return {
177
141
  path: toRelPath(rootPath, path),
178
142
  name,
179
- type: "dir",
180
- dirs,
181
- files
143
+ type: "file",
144
+ frontmatter: fetchFrontmatter(path, stat.mtime)
182
145
  };
183
146
  }
184
- if (stat.isFile() && name.endsWith(".md")) {
147
+ if (stat.isDirectory()) {
148
+ const dirs = [];
149
+ const files = [];
150
+ const entries = (0, import_node_fs3.readdirSync)(path);
151
+ for (const entry of entries) {
152
+ const fullPath = toAbsPath((0, import_node_path3.join)(path, entry));
153
+ const node = aux(fullPath);
154
+ if ((node == null ? void 0 : node.type) === "dir") dirs.push(node);
155
+ if ((node == null ? void 0 : node.type) === "file") files.push(node);
156
+ }
185
157
  return {
186
158
  path: toRelPath(rootPath, path),
187
159
  name,
188
- type: "file",
189
- frontmatter: getFrontmatter(path)
160
+ type: "dir",
161
+ dirs,
162
+ files
190
163
  };
191
164
  }
192
165
  return null;
193
166
  }
194
167
  return aux(rootPath);
195
168
  }
196
- function getFrontmatter(path) {
197
- const { data: srcFrontmatter } = (0, import_gray_matter.default)((0, import_node_fs4.readFileSync)(path, "utf-8"));
169
+ function fetchFrontmatter(path, mtime) {
170
+ const { data } = (0, import_gray_matter.default)((0, import_node_fs3.readFileSync)(path, "utf-8"));
198
171
  return {
199
- title: (0, import_node_path4.basename)(path).replace(/\.md$/, ""),
172
+ title: (0, import_node_path3.basename)(path).replace(/\.md$/, ""),
200
173
  description: "No description provided.",
201
- last_modified: (0, import_node_fs4.lstatSync)(path).mtime.toLocaleDateString("sv-SE"),
202
- // "sv-SE" (스웨덴어)는 YYYY-MM-DD 형식으로 출력되는 유명한 트릭입니다.
203
- ...srcFrontmatter
174
+ last_modified: mtime.toISOString().split("T")[0],
175
+ ...data
176
+ };
177
+ }
178
+
179
+ // src/utils/id.ts
180
+ var idCounter = (start = 0) => {
181
+ let count = start;
182
+ return () => count++;
183
+ };
184
+
185
+ // src/core/sync/normalize.ts
186
+ function normalize(fileTree) {
187
+ const dirNodes = [];
188
+ const fileNodes = [];
189
+ const fileId = idCounter();
190
+ const dirId = idCounter();
191
+ function aux(dir) {
192
+ const id = dirId();
193
+ const files = dir.files.map((file) => {
194
+ const id2 = fileId();
195
+ fileNodes.push({ id: id2, ...file });
196
+ return id2;
197
+ });
198
+ const dirs = dir.dirs.map((dir2) => aux(dir2));
199
+ dirNodes.push({ ...dir, id, files, dirs });
200
+ return id;
201
+ }
202
+ aux(fileTree);
203
+ return {
204
+ dirNodes,
205
+ fileNodes,
206
+ vault: dirNodes[dirNodes.length - 1]
204
207
  };
205
208
  }
206
209
 
210
+ // src/utils/file.ts
211
+ var import_node_fs4 = require("fs");
212
+ var import_node_path4 = require("path");
213
+ function generateJson(content, name, destPath) {
214
+ const jsonFilePath = (0, import_node_path4.join)(destPath, `${name}.json`);
215
+ (0, import_node_fs4.writeFileSync)(jsonFilePath, JSON.stringify(content, null, 2), "utf-8");
216
+ }
217
+
218
+ // src/utils/ignore.ts
219
+ var import_node_fs5 = require("fs");
220
+ var import_node_path5 = require("path");
221
+ var import_ignore = __toESM(require("ignore"));
222
+ var VaultIgnorer = class {
223
+ ign;
224
+ rootPath;
225
+ constructor(rootPath) {
226
+ this.rootPath = rootPath;
227
+ this.ign = (0, import_ignore.default)();
228
+ this.ign.add([".*"]);
229
+ }
230
+ load() {
231
+ const ignorePath = (0, import_node_path5.join)(this.rootPath, ".o2bignore");
232
+ if ((0, import_node_fs5.existsSync)(ignorePath))
233
+ this.ign.add((0, import_node_fs5.readFileSync)(ignorePath).toString());
234
+ }
235
+ ignore(path) {
236
+ if (path === this.rootPath) return false;
237
+ if (!(0, import_node_fs5.existsSync)(path)) throw new Error("somethings wrong!");
238
+ const stat = (0, import_node_fs5.lstatSync)(path);
239
+ const relPath2 = toRelPath(this.rootPath, path) + (stat.isDirectory() ? "/" : "");
240
+ return this.ign.ignores(relPath2);
241
+ }
242
+ };
243
+
207
244
  // src/runO2B.ts
208
245
  function runO2B(config) {
209
246
  const { srcPath, destPath, ignore: ignore2 } = config;
@@ -212,7 +249,12 @@ function runO2B(config) {
212
249
  if (ignore2) ign.load();
213
250
  const fileTree = scan(srcPath, ign);
214
251
  if (fileTree === null) throw new Error("No content in vault");
252
+ if (fileTree.type !== "dir") throw new Error("No Vault?!");
215
253
  mirror(fileTree, srcPath, destPath);
254
+ const { vault, dirNodes, fileNodes } = normalize(fileTree);
255
+ generateJson(vault, "vault", destPath);
256
+ generateJson(dirNodes, "dirNodes", destPath);
257
+ generateJson(fileNodes, "fileNodes", destPath);
216
258
  }
217
259
 
218
260
  // src/cli.ts
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/runO2B.ts"],"sourcesContent":["import cac from \"cac\";\nimport pkg from \"../package.json\";\nimport runO2B from \"./runO2B\";\nimport { toAbsPath } from \"./utils\";\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.3\",\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 \"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 { 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 if (!isAbsolute(path)) path = resolve(path);\n return path as AbsPath;\n}\n\nfunction toRelPath(from: AbsPath, to: AbsPath): RelPath {\n return relative(from, to) as RelPath;\n}\n\nfunction isObsidianDirectory(path: AbsPath) {\n const stat = lstatSync(path);\n\n if (!stat.isDirectory()) return false;\n\n const obsMetaDir = readdirSync(path, { withFileTypes: true })\n .filter((node) => node.isDirectory())\n .filter((node) => node.name === \".obsidian\");\n\n if (obsMetaDir.length !== 1) return false;\n\n return 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 runO2B(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/mirror.ts","../src/core/sync/scan.ts","../src/utils/path.ts","../src/types/path.ts","../src/utils/id.ts","../src/core/sync/normalize.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.5\",\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 \"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 { copyFileSync, existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { AbsPath } from \"@o2bTypes/path\";\nimport type { FileTree } from \"@o2bTypes/filetree\";\n\nexport function mirror(\n filetree: FileTree,\n srcPath: AbsPath,\n destPath: AbsPath,\n) {\n function aux(filetree: FileTree) {\n const from = join(srcPath, filetree.path);\n const to = join(destPath, filetree.path);\n\n if (filetree.type === \"dir\") {\n if (!existsSync(to)) mkdirSync(to, { recursive: true });\n\n filetree.files.forEach((file) => {\n aux(file);\n });\n\n filetree.dirs.forEach((dir) => {\n aux(dir);\n });\n }\n\n if (filetree.type === \"file\") {\n copyFileSync(from, to);\n }\n }\n\n aux(filetree);\n}\n","import { lstatSync, readdirSync, readFileSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\nimport matter from \"gray-matter\";\nimport type {\n DirNode,\n FileNode,\n FileTree,\n Frontmatter,\n} 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): FileTree | null {\n function aux(path: AbsPath): FileTree | null {\n if (ign?.ignore(path)) return null;\n\n const stat = lstatSync(path);\n const name = basename(path);\n\n if (stat.isFile() && name.endsWith(\".md\")) {\n return {\n path: toRelPath(rootPath, path),\n name,\n type: \"file\",\n frontmatter: fetchFrontmatter(path, stat.mtime),\n };\n }\n\n if (stat.isDirectory()) {\n const dirs: DirNode[] = [];\n const files: FileNode[] = [];\n const entries = readdirSync(path);\n\n for (const entry of entries) {\n const fullPath = toAbsPath(join(path, entry));\n const node = aux(fullPath);\n\n if (node?.type === \"dir\") dirs.push(node);\n if (node?.type === \"file\") files.push(node);\n }\n\n return {\n path: toRelPath(rootPath, path),\n name,\n type: \"dir\",\n dirs,\n files,\n };\n }\n\n return null;\n }\n\n return aux(rootPath);\n}\n\nfunction fetchFrontmatter(path: AbsPath, mtime: Date): Frontmatter {\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 { lstatSync, readdirSync } from \"node:fs\";\nimport { isAbsolute, 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\nfunction isObsidianDirectory(path: AbsPath) {\n const stat = lstatSync(path);\n\n if (!stat.isDirectory()) return false;\n\n const obsMetaDir = readdirSync(path, { withFileTypes: true })\n .filter((node) => node.isDirectory())\n .filter((node) => node.name === \".obsidian\");\n\n if (obsMetaDir.length !== 1) return false;\n\n return true;\n}\n\nexport { toAbsPath, toRelPath, isObsidianDirectory };\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 { Id } from \"@o2bTypes/filetree\";\n\nexport const idCounter: (start?: number) => () => Id = (start: number = 0) => {\n let count = start;\n return () => count++;\n};\n","import { DirNode, Id, NormedDirNode, NormedFileNode } from \"@o2bTypes/filetree\";\nimport { idCounter } from \"@utils/id\";\n\nexport function normalize(fileTree: DirNode): {\n vault: NormedDirNode;\n dirNodes: NormedDirNode[];\n fileNodes: NormedFileNode[];\n} {\n const dirNodes: NormedDirNode[] = [];\n const fileNodes: NormedFileNode[] = [];\n\n const fileId = idCounter();\n const dirId = idCounter();\n\n function aux(dir: DirNode): Id {\n const id = dirId();\n\n const files = dir.files.map((file) => {\n const id = fileId();\n fileNodes.push({ id, ...file });\n return id;\n });\n\n const dirs = dir.dirs.map((dir) => aux(dir));\n dirNodes.push({ ...dir, id, files, dirs });\n\n return id;\n }\n aux(fileTree);\n\n return {\n dirNodes,\n fileNodes,\n vault: dirNodes[dirNodes.length - 1],\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 ignore(path: AbsPath): boolean {\n if (path === this.rootPath) return false;\n\n if (!existsSync(path)) throw new Error(\"somethings wrong!\");\n\n const stat = lstatSync(path);\n\n const relPath =\n toRelPath(this.rootPath, path) + (stat.isDirectory() ? \"/\" : \"\");\n\n return this.ign.ignores(relPath);\n }\n}\n","import { mirror, scan, normalize } from \"@core/sync\";\nimport type { Config } from \"@o2bTypes/config\";\nimport { generateJson } from \"@utils/file\";\nimport { isObsidianDirectory } from \"@utils/path\";\nimport { VaultIgnorer } from \"@utils/ignore\";\n\nexport default function runO2B(config: Config) {\n const { srcPath, destPath, ignore } = config;\n\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 if (fileTree.type !== \"dir\") throw new Error(\"No Vault?!\");\n\n mirror(fileTree, srcPath, destPath);\n const { vault, dirNodes, fileNodes } = normalize(fileTree);\n\n generateJson(vault, \"vault\", destPath);\n generateJson(dirNodes, \"dirNodes\", destPath);\n generateJson(fileNodes, \"fileNodes\", destPath);\n}\n"],"mappings":"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","names":["filetree","import_node_fs","import_node_path","import_node_fs","import_node_path","z","matter","id","dir","import_node_fs","import_node_path","import_node_fs","import_node_path","ignore","relPath","ignore","cac","ignore"]}
package/dist/index.d.mts CHANGED
@@ -1,54 +1,81 @@
1
1
  import z from 'zod';
2
2
 
3
- type AbsPath = z.infer<typeof AbsPathSchema>;
4
- declare const AbsPathSchema: z.ZodBranded<z.ZodString, "absPath">;
5
-
6
- type Config = {
7
- srcPath: AbsPath;
8
- destPath: AbsPath;
9
- ignore: boolean;
10
- };
11
-
12
- declare const FileTreeSchema: z.ZodUnion<[z.ZodIntersection<z.ZodObject<{
3
+ declare const normedFileNode: z.ZodObject<{
13
4
  path: z.ZodBranded<z.ZodString, "relpath">;
14
5
  name: z.ZodString;
15
- }, "strip", z.ZodTypeAny, {
16
- path?: string & z.BRAND<"relpath">;
17
- name?: string;
18
- }, {
19
- path?: string;
20
- name?: string;
21
- }>, z.ZodObject<{
6
+ } & {
22
7
  type: z.ZodLiteral<"file">;
23
8
  frontmatter: z.ZodObject<{
24
9
  title: z.ZodString;
25
- descriptionL: z.ZodString;
10
+ description: z.ZodString;
26
11
  last_modified: z.ZodString;
27
12
  }, "strip", z.ZodTypeAny, {
28
- title?: string;
29
- descriptionL?: string;
30
- last_modified?: string;
13
+ title: string;
14
+ description: string;
15
+ last_modified: string;
31
16
  }, {
32
- title?: string;
33
- descriptionL?: string;
34
- last_modified?: string;
17
+ title: string;
18
+ description: string;
19
+ last_modified: string;
35
20
  }>;
21
+ } & {
22
+ id: z.ZodNumber;
36
23
  }, "strip", z.ZodTypeAny, {
37
- type?: "file";
38
- frontmatter?: {
39
- title?: string;
40
- descriptionL?: string;
41
- last_modified?: string;
24
+ path: string & z.BRAND<"relpath">;
25
+ type: "file";
26
+ name: string;
27
+ frontmatter: {
28
+ title: string;
29
+ description: string;
30
+ last_modified: string;
42
31
  };
32
+ id: number;
43
33
  }, {
44
- type?: "file";
45
- frontmatter?: {
46
- title?: string;
47
- descriptionL?: string;
48
- last_modified?: string;
34
+ path: string;
35
+ type: "file";
36
+ name: string;
37
+ frontmatter: {
38
+ title: string;
39
+ description: string;
40
+ last_modified: string;
49
41
  };
50
- }>>, any]>;
42
+ id: number;
43
+ }>;
44
+ declare const normedDirNode: z.ZodObject<{
45
+ path: z.ZodBranded<z.ZodString, "relpath">;
46
+ name: z.ZodString;
47
+ } & {
48
+ id: z.ZodNumber;
49
+ type: z.ZodLiteral<"dir">;
50
+ files: z.ZodArray<z.ZodNumber, "many">;
51
+ dirs: z.ZodArray<z.ZodNumber, "many">;
52
+ }, "strip", z.ZodTypeAny, {
53
+ path: string & z.BRAND<"relpath">;
54
+ type: "dir";
55
+ name: string;
56
+ files: number[];
57
+ dirs: number[];
58
+ id: number;
59
+ }, {
60
+ path: string;
61
+ type: "dir";
62
+ name: string;
63
+ files: number[];
64
+ dirs: number[];
65
+ id: number;
66
+ }>;
67
+ type NormedFileNode = z.infer<typeof normedFileNode>;
68
+ type NormedDirNode = z.infer<typeof normedDirNode>;
69
+
70
+ type AbsPath = z.infer<typeof absPath>;
71
+ declare const absPath: z.ZodBranded<z.ZodString, "absPath">;
72
+
73
+ type Config = {
74
+ srcPath: AbsPath;
75
+ destPath: AbsPath;
76
+ ignore: boolean;
77
+ };
51
78
 
52
79
  declare function runO2B(config: Config): void;
53
80
 
54
- export { FileTreeSchema, runO2B as default };
81
+ export { type NormedDirNode, type NormedFileNode, runO2B as default, normedDirNode, normedFileNode };
package/dist/index.d.ts CHANGED
@@ -1,54 +1,81 @@
1
1
  import z from 'zod';
2
2
 
3
- type AbsPath = z.infer<typeof AbsPathSchema>;
4
- declare const AbsPathSchema: z.ZodBranded<z.ZodString, "absPath">;
5
-
6
- type Config = {
7
- srcPath: AbsPath;
8
- destPath: AbsPath;
9
- ignore: boolean;
10
- };
11
-
12
- declare const FileTreeSchema: z.ZodUnion<[z.ZodIntersection<z.ZodObject<{
3
+ declare const normedFileNode: z.ZodObject<{
13
4
  path: z.ZodBranded<z.ZodString, "relpath">;
14
5
  name: z.ZodString;
15
- }, "strip", z.ZodTypeAny, {
16
- path?: string & z.BRAND<"relpath">;
17
- name?: string;
18
- }, {
19
- path?: string;
20
- name?: string;
21
- }>, z.ZodObject<{
6
+ } & {
22
7
  type: z.ZodLiteral<"file">;
23
8
  frontmatter: z.ZodObject<{
24
9
  title: z.ZodString;
25
- descriptionL: z.ZodString;
10
+ description: z.ZodString;
26
11
  last_modified: z.ZodString;
27
12
  }, "strip", z.ZodTypeAny, {
28
- title?: string;
29
- descriptionL?: string;
30
- last_modified?: string;
13
+ title: string;
14
+ description: string;
15
+ last_modified: string;
31
16
  }, {
32
- title?: string;
33
- descriptionL?: string;
34
- last_modified?: string;
17
+ title: string;
18
+ description: string;
19
+ last_modified: string;
35
20
  }>;
21
+ } & {
22
+ id: z.ZodNumber;
36
23
  }, "strip", z.ZodTypeAny, {
37
- type?: "file";
38
- frontmatter?: {
39
- title?: string;
40
- descriptionL?: string;
41
- last_modified?: string;
24
+ path: string & z.BRAND<"relpath">;
25
+ type: "file";
26
+ name: string;
27
+ frontmatter: {
28
+ title: string;
29
+ description: string;
30
+ last_modified: string;
42
31
  };
32
+ id: number;
43
33
  }, {
44
- type?: "file";
45
- frontmatter?: {
46
- title?: string;
47
- descriptionL?: string;
48
- last_modified?: string;
34
+ path: string;
35
+ type: "file";
36
+ name: string;
37
+ frontmatter: {
38
+ title: string;
39
+ description: string;
40
+ last_modified: string;
49
41
  };
50
- }>>, any]>;
42
+ id: number;
43
+ }>;
44
+ declare const normedDirNode: z.ZodObject<{
45
+ path: z.ZodBranded<z.ZodString, "relpath">;
46
+ name: z.ZodString;
47
+ } & {
48
+ id: z.ZodNumber;
49
+ type: z.ZodLiteral<"dir">;
50
+ files: z.ZodArray<z.ZodNumber, "many">;
51
+ dirs: z.ZodArray<z.ZodNumber, "many">;
52
+ }, "strip", z.ZodTypeAny, {
53
+ path: string & z.BRAND<"relpath">;
54
+ type: "dir";
55
+ name: string;
56
+ files: number[];
57
+ dirs: number[];
58
+ id: number;
59
+ }, {
60
+ path: string;
61
+ type: "dir";
62
+ name: string;
63
+ files: number[];
64
+ dirs: number[];
65
+ id: number;
66
+ }>;
67
+ type NormedFileNode = z.infer<typeof normedFileNode>;
68
+ type NormedDirNode = z.infer<typeof normedDirNode>;
69
+
70
+ type AbsPath = z.infer<typeof absPath>;
71
+ declare const absPath: z.ZodBranded<z.ZodString, "absPath">;
72
+
73
+ type Config = {
74
+ srcPath: AbsPath;
75
+ destPath: AbsPath;
76
+ ignore: boolean;
77
+ };
51
78
 
52
79
  declare function runO2B(config: Config): void;
53
80
 
54
- export { FileTreeSchema, runO2B as default };
81
+ export { type NormedDirNode, type NormedFileNode, runO2B as default, normedDirNode, normedFileNode };
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,8 +30,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
29
30
  // src/index.ts
30
31
  var index_exports = {};
31
32
  __export(index_exports, {
32
- FileTreeSchema: () => FileTreeSchema,
33
- default: () => runO2B
33
+ default: () => runO2B,
34
+ normedDirNode: () => normedDirNode,
35
+ normedFileNode: () => normedFileNode
34
36
  });
35
37
  module.exports = __toCommonJS(index_exports);
36
38
 
@@ -39,38 +41,39 @@ var import_zod2 = __toESM(require("zod"));
39
41
 
40
42
  // src/types/path.ts
41
43
  var import_zod = __toESM(require("zod"));
42
- var AbsPathSchema = import_zod.default.string().brand();
43
- var RelPathSchema = import_zod.default.string().brand();
44
+ var absPath = import_zod.default.string().brand();
45
+ var relPath = import_zod.default.string().brand();
44
46
 
45
47
  // src/types/filetree.ts
46
- var FileTreeNodeSchema = import_zod2.default.object({
47
- path: RelPathSchema,
48
- name: import_zod2.default.string()
49
- });
50
- var FrontmatterSchema = import_zod2.default.object({
48
+ var frontmatter = import_zod2.default.object({
51
49
  title: import_zod2.default.string(),
52
- descriptionL: import_zod2.default.string(),
50
+ description: import_zod2.default.string(),
53
51
  last_modified: import_zod2.default.string()
54
52
  });
55
- var FileNodeSchema = import_zod2.default.intersection(
56
- FileTreeNodeSchema,
57
- import_zod2.default.object({
58
- type: import_zod2.default.literal("file"),
59
- frontmatter: FrontmatterSchema
60
- })
61
- );
62
- var DirNodeScehma = import_zod2.default.intersection(
63
- FileTreeNodeSchema,
64
- import_zod2.default.object({
65
- type: import_zod2.default.literal("dir"),
66
- // https://zod.dev/api?id=recursive-objects
67
- get dirs() {
68
- return import_zod2.default.array(DirNodeScehma);
69
- },
70
- files: import_zod2.default.array(FileNodeSchema)
71
- })
72
- );
73
- var FileTreeSchema = import_zod2.default.union([FileNodeSchema, DirNodeScehma]);
53
+ var baseNode = import_zod2.default.object({
54
+ path: relPath,
55
+ name: import_zod2.default.string()
56
+ });
57
+ var fileNode = baseNode.extend({
58
+ type: import_zod2.default.literal("file"),
59
+ frontmatter
60
+ });
61
+ var dirNode = baseNode.extend({
62
+ type: import_zod2.default.literal("dir"),
63
+ files: import_zod2.default.array(fileNode),
64
+ dirs: import_zod2.default.lazy(() => dirNode.array())
65
+ });
66
+ var fileTree = import_zod2.default.union([dirNode, fileNode]);
67
+ var id = import_zod2.default.number().int();
68
+ var normedFileNode = fileNode.extend({
69
+ id
70
+ });
71
+ var normedDirNode = baseNode.extend({
72
+ id,
73
+ type: import_zod2.default.literal("dir"),
74
+ files: import_zod2.default.array(id),
75
+ dirs: import_zod2.default.array(id)
76
+ });
74
77
 
75
78
  // src/core/sync/mirror.ts
76
79
  var import_node_fs = require("fs");
@@ -93,32 +96,22 @@ function mirror(filetree, srcPath, destPath) {
93
96
  }
94
97
  }
95
98
  aux(filetree);
96
- generateFileTree(filetree, destPath);
97
- }
98
- function generateFileTree(filetree, destPath) {
99
- const jsonFilePath = (0, import_node_path.join)(destPath, "o2bFileTree.json");
100
- (0, import_node_fs.writeFileSync)(jsonFilePath, JSON.stringify(filetree, null, 2), "utf-8");
101
99
  }
102
100
 
103
101
  // src/core/sync/scan.ts
104
- var import_node_fs4 = require("fs");
105
- var import_node_path4 = require("path");
106
- var import_gray_matter = __toESM(require("gray-matter"));
107
-
108
- // src/utils/ignore.ts
109
102
  var import_node_fs3 = require("fs");
110
103
  var import_node_path3 = require("path");
111
- var import_ignore = __toESM(require("ignore"));
104
+ var import_gray_matter = __toESM(require("gray-matter"));
112
105
 
113
106
  // src/utils/path.ts
114
107
  var import_node_fs2 = require("fs");
115
108
  var import_node_path2 = require("path");
116
109
  function toAbsPath(path) {
117
110
  if (!(0, import_node_path2.isAbsolute)(path)) path = (0, import_node_path2.resolve)(path);
118
- return path;
111
+ return absPath.parse(path);
119
112
  }
120
113
  function toRelPath(from, to) {
121
- return (0, import_node_path2.relative)(from, to);
114
+ return relPath.parse((0, import_node_path2.relative)(from, to));
122
115
  }
123
116
  function isObsidianDirectory(path) {
124
117
  const stat = (0, import_node_fs2.lstatSync)(path);
@@ -128,91 +121,135 @@ function isObsidianDirectory(path) {
128
121
  return true;
129
122
  }
130
123
 
131
- // src/utils/ignore.ts
132
- var VaultIgnorer = class {
133
- ign;
134
- rootPath;
135
- constructor(rootPath) {
136
- this.rootPath = rootPath;
137
- this.ign = (0, import_ignore.default)();
138
- this.ign.add([".*"]);
139
- }
140
- load() {
141
- const ignorePath = (0, import_node_path3.join)(this.rootPath, ".o2bignore");
142
- if ((0, import_node_fs3.existsSync)(ignorePath))
143
- this.ign.add((0, import_node_fs3.readFileSync)(ignorePath).toString());
144
- }
145
- ignore(path) {
146
- if (path === this.rootPath) return false;
147
- if (!(0, import_node_fs3.existsSync)(path)) throw new Error("somethings wrong!");
148
- const stat = (0, import_node_fs3.lstatSync)(path);
149
- const relPath = toRelPath(this.rootPath, path) + (stat.isDirectory() ? "/" : "");
150
- return this.ign.ignores(relPath);
151
- }
152
- };
153
-
154
124
  // src/core/sync/scan.ts
155
125
  function scan(rootPath, ign) {
156
126
  function aux(path) {
157
127
  if (ign == null ? void 0 : ign.ignore(path)) return null;
158
- const stat = (0, import_node_fs4.lstatSync)(path);
159
- const name = (0, import_node_path4.basename)(path);
160
- if (stat.isDirectory()) {
161
- const { dirs, files } = (0, import_node_fs4.readdirSync)(path).reduce(
162
- (acc, entry) => {
163
- const node = aux(toAbsPath((0, import_node_path4.join)(path, entry)));
164
- if ((node == null ? void 0 : node.type) === "dir") acc.dirs.push(node);
165
- if ((node == null ? void 0 : node.type) === "file") acc.files.push(node);
166
- return acc;
167
- },
168
- {
169
- dirs: [],
170
- files: []
171
- }
172
- );
128
+ const stat = (0, import_node_fs3.lstatSync)(path);
129
+ const name = (0, import_node_path3.basename)(path);
130
+ if (stat.isFile() && name.endsWith(".md")) {
173
131
  return {
174
132
  path: toRelPath(rootPath, path),
175
133
  name,
176
- type: "dir",
177
- dirs,
178
- files
134
+ type: "file",
135
+ frontmatter: fetchFrontmatter(path, stat.mtime)
179
136
  };
180
137
  }
181
- if (stat.isFile() && name.endsWith(".md")) {
138
+ if (stat.isDirectory()) {
139
+ const dirs = [];
140
+ const files = [];
141
+ const entries = (0, import_node_fs3.readdirSync)(path);
142
+ for (const entry of entries) {
143
+ const fullPath = toAbsPath((0, import_node_path3.join)(path, entry));
144
+ const node = aux(fullPath);
145
+ if ((node == null ? void 0 : node.type) === "dir") dirs.push(node);
146
+ if ((node == null ? void 0 : node.type) === "file") files.push(node);
147
+ }
182
148
  return {
183
149
  path: toRelPath(rootPath, path),
184
150
  name,
185
- type: "file",
186
- frontmatter: getFrontmatter(path)
151
+ type: "dir",
152
+ dirs,
153
+ files
187
154
  };
188
155
  }
189
156
  return null;
190
157
  }
191
158
  return aux(rootPath);
192
159
  }
193
- function getFrontmatter(path) {
194
- const { data: srcFrontmatter } = (0, import_gray_matter.default)((0, import_node_fs4.readFileSync)(path, "utf-8"));
160
+ function fetchFrontmatter(path, mtime) {
161
+ const { data } = (0, import_gray_matter.default)((0, import_node_fs3.readFileSync)(path, "utf-8"));
195
162
  return {
196
- title: (0, import_node_path4.basename)(path).replace(/\.md$/, ""),
163
+ title: (0, import_node_path3.basename)(path).replace(/\.md$/, ""),
197
164
  description: "No description provided.",
198
- last_modified: (0, import_node_fs4.lstatSync)(path).mtime.toLocaleDateString("sv-SE"),
199
- // "sv-SE" (스웨덴어)는 YYYY-MM-DD 형식으로 출력되는 유명한 트릭입니다.
200
- ...srcFrontmatter
165
+ last_modified: mtime.toISOString().split("T")[0],
166
+ ...data
201
167
  };
202
168
  }
203
169
 
170
+ // src/utils/id.ts
171
+ var idCounter = (start = 0) => {
172
+ let count = start;
173
+ return () => count++;
174
+ };
175
+
176
+ // src/core/sync/normalize.ts
177
+ function normalize(fileTree2) {
178
+ const dirNodes = [];
179
+ const fileNodes = [];
180
+ const fileId = idCounter();
181
+ const dirId = idCounter();
182
+ function aux(dir) {
183
+ const id2 = dirId();
184
+ const files = dir.files.map((file) => {
185
+ const id3 = fileId();
186
+ fileNodes.push({ id: id3, ...file });
187
+ return id3;
188
+ });
189
+ const dirs = dir.dirs.map((dir2) => aux(dir2));
190
+ dirNodes.push({ ...dir, id: id2, files, dirs });
191
+ return id2;
192
+ }
193
+ aux(fileTree2);
194
+ return {
195
+ dirNodes,
196
+ fileNodes,
197
+ vault: dirNodes[dirNodes.length - 1]
198
+ };
199
+ }
200
+
201
+ // src/utils/file.ts
202
+ var import_node_fs4 = require("fs");
203
+ var import_node_path4 = require("path");
204
+ function generateJson(content, name, destPath) {
205
+ const jsonFilePath = (0, import_node_path4.join)(destPath, `${name}.json`);
206
+ (0, import_node_fs4.writeFileSync)(jsonFilePath, JSON.stringify(content, null, 2), "utf-8");
207
+ }
208
+
209
+ // src/utils/ignore.ts
210
+ var import_node_fs5 = require("fs");
211
+ var import_node_path5 = require("path");
212
+ var import_ignore = __toESM(require("ignore"));
213
+ var VaultIgnorer = class {
214
+ ign;
215
+ rootPath;
216
+ constructor(rootPath) {
217
+ this.rootPath = rootPath;
218
+ this.ign = (0, import_ignore.default)();
219
+ this.ign.add([".*"]);
220
+ }
221
+ load() {
222
+ const ignorePath = (0, import_node_path5.join)(this.rootPath, ".o2bignore");
223
+ if ((0, import_node_fs5.existsSync)(ignorePath))
224
+ this.ign.add((0, import_node_fs5.readFileSync)(ignorePath).toString());
225
+ }
226
+ ignore(path) {
227
+ if (path === this.rootPath) return false;
228
+ if (!(0, import_node_fs5.existsSync)(path)) throw new Error("somethings wrong!");
229
+ const stat = (0, import_node_fs5.lstatSync)(path);
230
+ const relPath2 = toRelPath(this.rootPath, path) + (stat.isDirectory() ? "/" : "");
231
+ return this.ign.ignores(relPath2);
232
+ }
233
+ };
234
+
204
235
  // src/runO2B.ts
205
236
  function runO2B(config) {
206
237
  const { srcPath, destPath, ignore: ignore2 } = config;
207
238
  if (!isObsidianDirectory(srcPath)) throw new Error("Not a obsidian vault");
208
239
  const ign = new VaultIgnorer(srcPath);
209
240
  if (ignore2) ign.load();
210
- const fileTree = scan(srcPath, ign);
211
- if (fileTree === null) throw new Error("No content in vault");
212
- mirror(fileTree, srcPath, destPath);
241
+ const fileTree2 = scan(srcPath, ign);
242
+ if (fileTree2 === null) throw new Error("No content in vault");
243
+ if (fileTree2.type !== "dir") throw new Error("No Vault?!");
244
+ mirror(fileTree2, srcPath, destPath);
245
+ const { vault, dirNodes, fileNodes } = normalize(fileTree2);
246
+ generateJson(vault, "vault", destPath);
247
+ generateJson(dirNodes, "dirNodes", destPath);
248
+ generateJson(fileNodes, "fileNodes", destPath);
213
249
  }
214
250
  // Annotate the CommonJS export names for ESM import in node:
215
251
  0 && (module.exports = {
216
- FileTreeSchema
252
+ normedDirNode,
253
+ normedFileNode
217
254
  });
218
255
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/types/filetree.ts","../src/types/path.ts","../src/core/sync/mirror.ts","../src/core/sync/scan.ts","../src/utils/ignore.ts","../src/utils/path.ts","../src/runO2B.ts"],"sourcesContent":["export { FileTreeSchema } from \"./types\";\nexport { default } from \"./runO2B\";\n","import z from \"zod\";\nimport { RelPathSchema } from \"./path\";\n\ntype FileTreeNode = z.infer<typeof FileTreeNodeSchema>;\nconst FileTreeNodeSchema = z.object({\n path: RelPathSchema,\n name: z.string(),\n});\n\ntype Frontmatter = z.infer<typeof FrontmatterSchema>;\nconst FrontmatterSchema = z.object({\n title: z.string(),\n descriptionL: z.string(),\n last_modified: z.string(),\n});\n\ntype FileNode = z.infer<typeof FileNodeSchema>;\nconst FileNodeSchema = z.intersection(\n FileTreeNodeSchema,\n z.object({\n type: z.literal(\"file\"),\n frontmatter: FrontmatterSchema,\n })\n);\n\ntype DirNode = z.infer<typeof DirNodeScehma>;\nconst DirNodeScehma = z.intersection(\n FileTreeNodeSchema,\n z.object({\n type: z.literal(\"dir\"),\n // https://zod.dev/api?id=recursive-objects\n get dirs() {\n return z.array(DirNodeScehma);\n },\n files: z.array(FileNodeSchema),\n })\n);\n\ntype FileTree = z.infer<typeof FileTreeSchema>;\nconst FileTreeSchema = z.union([FileNodeSchema, DirNodeScehma]);\n\nexport { FileTree, FileNode, DirNode, Frontmatter, FileTreeSchema };\n","import z from \"zod\";\n\ntype AbsPath = z.infer<typeof AbsPathSchema>;\nconst AbsPathSchema = z.string().brand<\"absPath\">();\n\ntype RelPath = z.infer<typeof RelPathSchema>;\nconst RelPathSchema = z.string().brand<\"relpath\">();\n\nexport { type AbsPath, type RelPath, AbsPathSchema, RelPathSchema };\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 if (!isAbsolute(path)) path = resolve(path);\n return path as AbsPath;\n}\n\nfunction toRelPath(from: AbsPath, to: AbsPath): RelPath {\n return relative(from, to) as RelPath;\n}\n\nfunction isObsidianDirectory(path: AbsPath) {\n const stat = lstatSync(path);\n\n if (!stat.isDirectory()) return false;\n\n const obsMetaDir = readdirSync(path, { withFileTypes: true })\n .filter((node) => node.isDirectory())\n .filter((node) => node.name === \".obsidian\");\n\n if (obsMetaDir.length !== 1) return false;\n\n return 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 runO2B(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":["import_zod","z","z","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/types/filetree.ts","../src/types/path.ts","../src/core/sync/mirror.ts","../src/core/sync/scan.ts","../src/utils/path.ts","../src/utils/id.ts","../src/core/sync/normalize.ts","../src/utils/file.ts","../src/utils/ignore.ts","../src/runO2B.ts"],"sourcesContent":["export {\n NormedDirNode,\n NormedFileNode,\n normedDirNode,\n normedFileNode,\n} from \"@o2bTypes/filetree\";\nexport { default } from \"./runO2B\";\n","import z from \"zod\";\nimport { relPath, type RelPath } from \"@o2bTypes/path\";\n\n/* ********** Schemas ********** */\n\nconst frontmatter = z.object({\n title: z.string(),\n description: z.string(),\n last_modified: z.string(),\n});\n\nconst baseNode = z.object({\n path: relPath,\n name: z.string(),\n});\n\nconst fileNode = baseNode.extend({\n type: z.literal(\"file\"),\n frontmatter: frontmatter,\n});\n\nconst dirNode: z.ZodType<DirNodeOutput, z.ZodTypeDef, DirNodeInput> =\n baseNode.extend({\n type: z.literal(\"dir\"),\n files: z.array(fileNode),\n dirs: z.lazy(() => dirNode.array()),\n });\n\nconst fileTree = z.union([dirNode, fileNode]);\n\n/* ********** Types ********** */\n\ntype BaseNodeOutput = z.infer<typeof baseNode>;\ntype BaseNodeInput = z.input<typeof baseNode>;\n\ntype FileNodeOutput = z.infer<typeof fileNode>;\ntype FileNodeInput = z.input<typeof fileNode>;\n\n// https://v3.zod.dev/?id=recursive-types\ntype DirNodeOutput = BaseNodeOutput & {\n type: \"dir\";\n files: FileNodeOutput[];\n dirs: DirNodeOutput[];\n};\ntype DirNodeInput = BaseNodeInput & {\n type: \"dir\";\n files: FileNodeInput[];\n dirs: DirNodeInput[];\n};\n\nexport type FileTree = z.infer<typeof fileTree>;\nexport type DirNode = DirNodeOutput;\nexport type FileNode = FileNodeOutput;\nexport type Frontmatter = z.infer<typeof frontmatter>;\n\n/* ********** Normalized ********** */\n\nconst id = z.number().int();\n\nexport const normedFileNode = fileNode.extend({\n id,\n});\n\nexport const normedDirNode = baseNode.extend({\n id,\n type: z.literal(\"dir\"),\n files: z.array(id),\n dirs: z.array(id),\n});\n\nexport type Id = z.infer<typeof id>;\nexport type NormedFileNode = z.infer<typeof normedFileNode>;\nexport type NormedDirNode = z.infer<typeof normedDirNode>;\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 { copyFileSync, existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { AbsPath } from \"@o2bTypes/path\";\nimport type { FileTree } from \"@o2bTypes/filetree\";\n\nexport function mirror(\n filetree: FileTree,\n srcPath: AbsPath,\n destPath: AbsPath,\n) {\n function aux(filetree: FileTree) {\n const from = join(srcPath, filetree.path);\n const to = join(destPath, filetree.path);\n\n if (filetree.type === \"dir\") {\n if (!existsSync(to)) mkdirSync(to, { recursive: true });\n\n filetree.files.forEach((file) => {\n aux(file);\n });\n\n filetree.dirs.forEach((dir) => {\n aux(dir);\n });\n }\n\n if (filetree.type === \"file\") {\n copyFileSync(from, to);\n }\n }\n\n aux(filetree);\n}\n","import { lstatSync, readdirSync, readFileSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\nimport matter from \"gray-matter\";\nimport type {\n DirNode,\n FileNode,\n FileTree,\n Frontmatter,\n} 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): FileTree | null {\n function aux(path: AbsPath): FileTree | null {\n if (ign?.ignore(path)) return null;\n\n const stat = lstatSync(path);\n const name = basename(path);\n\n if (stat.isFile() && name.endsWith(\".md\")) {\n return {\n path: toRelPath(rootPath, path),\n name,\n type: \"file\",\n frontmatter: fetchFrontmatter(path, stat.mtime),\n };\n }\n\n if (stat.isDirectory()) {\n const dirs: DirNode[] = [];\n const files: FileNode[] = [];\n const entries = readdirSync(path);\n\n for (const entry of entries) {\n const fullPath = toAbsPath(join(path, entry));\n const node = aux(fullPath);\n\n if (node?.type === \"dir\") dirs.push(node);\n if (node?.type === \"file\") files.push(node);\n }\n\n return {\n path: toRelPath(rootPath, path),\n name,\n type: \"dir\",\n dirs,\n files,\n };\n }\n\n return null;\n }\n\n return aux(rootPath);\n}\n\nfunction fetchFrontmatter(path: AbsPath, mtime: Date): Frontmatter {\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 { lstatSync, readdirSync } from \"node:fs\";\nimport { isAbsolute, 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\nfunction isObsidianDirectory(path: AbsPath) {\n const stat = lstatSync(path);\n\n if (!stat.isDirectory()) return false;\n\n const obsMetaDir = readdirSync(path, { withFileTypes: true })\n .filter((node) => node.isDirectory())\n .filter((node) => node.name === \".obsidian\");\n\n if (obsMetaDir.length !== 1) return false;\n\n return true;\n}\n\nexport { toAbsPath, toRelPath, isObsidianDirectory };\n","import { Id } from \"@o2bTypes/filetree\";\n\nexport const idCounter: (start?: number) => () => Id = (start: number = 0) => {\n let count = start;\n return () => count++;\n};\n","import { DirNode, Id, NormedDirNode, NormedFileNode } from \"@o2bTypes/filetree\";\nimport { idCounter } from \"@utils/id\";\n\nexport function normalize(fileTree: DirNode): {\n vault: NormedDirNode;\n dirNodes: NormedDirNode[];\n fileNodes: NormedFileNode[];\n} {\n const dirNodes: NormedDirNode[] = [];\n const fileNodes: NormedFileNode[] = [];\n\n const fileId = idCounter();\n const dirId = idCounter();\n\n function aux(dir: DirNode): Id {\n const id = dirId();\n\n const files = dir.files.map((file) => {\n const id = fileId();\n fileNodes.push({ id, ...file });\n return id;\n });\n\n const dirs = dir.dirs.map((dir) => aux(dir));\n dirNodes.push({ ...dir, id, files, dirs });\n\n return id;\n }\n aux(fileTree);\n\n return {\n dirNodes,\n fileNodes,\n vault: dirNodes[dirNodes.length - 1],\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 ignore(path: AbsPath): boolean {\n if (path === this.rootPath) return false;\n\n if (!existsSync(path)) throw new Error(\"somethings wrong!\");\n\n const stat = lstatSync(path);\n\n const relPath =\n toRelPath(this.rootPath, path) + (stat.isDirectory() ? \"/\" : \"\");\n\n return this.ign.ignores(relPath);\n }\n}\n","import { mirror, scan, normalize } from \"@core/sync\";\nimport type { Config } from \"@o2bTypes/config\";\nimport { generateJson } from \"@utils/file\";\nimport { isObsidianDirectory } from \"@utils/path\";\nimport { VaultIgnorer } from \"@utils/ignore\";\n\nexport default function runO2B(config: Config) {\n const { srcPath, destPath, ignore } = config;\n\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 if (fileTree.type !== \"dir\") throw new Error(\"No Vault?!\");\n\n mirror(fileTree, srcPath, destPath);\n const { vault, dirNodes, fileNodes } = normalize(fileTree);\n\n generateJson(vault, \"vault\", destPath);\n generateJson(dirNodes, \"dirNodes\", destPath);\n generateJson(fileNodes, \"fileNodes\", destPath);\n}\n"],"mappings":"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","names":["import_zod","z","z","filetree","import_node_fs","import_node_path","import_node_fs","import_node_path","matter","fileTree","id","dir","import_node_fs","import_node_path","import_node_fs","import_node_path","ignore","relPath","ignore","fileTree"]}
package/dist/index.mjs CHANGED
@@ -3,41 +3,42 @@ import z2 from "zod";
3
3
 
4
4
  // src/types/path.ts
5
5
  import z from "zod";
6
- var AbsPathSchema = z.string().brand();
7
- var RelPathSchema = z.string().brand();
6
+ var absPath = z.string().brand();
7
+ var relPath = z.string().brand();
8
8
 
9
9
  // src/types/filetree.ts
10
- var FileTreeNodeSchema = z2.object({
11
- path: RelPathSchema,
12
- name: z2.string()
13
- });
14
- var FrontmatterSchema = z2.object({
10
+ var frontmatter = z2.object({
15
11
  title: z2.string(),
16
- descriptionL: z2.string(),
12
+ description: z2.string(),
17
13
  last_modified: z2.string()
18
14
  });
19
- var FileNodeSchema = z2.intersection(
20
- FileTreeNodeSchema,
21
- z2.object({
22
- type: z2.literal("file"),
23
- frontmatter: FrontmatterSchema
24
- })
25
- );
26
- var DirNodeScehma = z2.intersection(
27
- FileTreeNodeSchema,
28
- z2.object({
29
- type: z2.literal("dir"),
30
- // https://zod.dev/api?id=recursive-objects
31
- get dirs() {
32
- return z2.array(DirNodeScehma);
33
- },
34
- files: z2.array(FileNodeSchema)
35
- })
36
- );
37
- var FileTreeSchema = z2.union([FileNodeSchema, DirNodeScehma]);
15
+ var baseNode = z2.object({
16
+ path: relPath,
17
+ name: z2.string()
18
+ });
19
+ var fileNode = baseNode.extend({
20
+ type: z2.literal("file"),
21
+ frontmatter
22
+ });
23
+ var dirNode = baseNode.extend({
24
+ type: z2.literal("dir"),
25
+ files: z2.array(fileNode),
26
+ dirs: z2.lazy(() => dirNode.array())
27
+ });
28
+ var fileTree = z2.union([dirNode, fileNode]);
29
+ var id = z2.number().int();
30
+ var normedFileNode = fileNode.extend({
31
+ id
32
+ });
33
+ var normedDirNode = baseNode.extend({
34
+ id,
35
+ type: z2.literal("dir"),
36
+ files: z2.array(id),
37
+ dirs: z2.array(id)
38
+ });
38
39
 
39
40
  // src/core/sync/mirror.ts
40
- import { copyFileSync, existsSync, mkdirSync, writeFileSync } from "fs";
41
+ import { copyFileSync, existsSync, mkdirSync } from "fs";
41
42
  import { join } from "path";
42
43
  function mirror(filetree, srcPath, destPath) {
43
44
  function aux(filetree2) {
@@ -57,32 +58,22 @@ function mirror(filetree, srcPath, destPath) {
57
58
  }
58
59
  }
59
60
  aux(filetree);
60
- generateFileTree(filetree, destPath);
61
- }
62
- function generateFileTree(filetree, destPath) {
63
- const jsonFilePath = join(destPath, "o2bFileTree.json");
64
- writeFileSync(jsonFilePath, JSON.stringify(filetree, null, 2), "utf-8");
65
61
  }
66
62
 
67
63
  // src/core/sync/scan.ts
68
- import { lstatSync as lstatSync3, readdirSync as readdirSync2, readFileSync as readFileSync2 } from "fs";
69
- import { basename, join as join3 } from "path";
64
+ import { lstatSync as lstatSync2, readdirSync as readdirSync2, readFileSync } from "fs";
65
+ import { basename, join as join2 } from "path";
70
66
  import matter from "gray-matter";
71
67
 
72
- // src/utils/ignore.ts
73
- import { existsSync as existsSync2, lstatSync as lstatSync2, readFileSync } from "fs";
74
- import { join as join2 } from "path";
75
- import ignore from "ignore";
76
-
77
68
  // src/utils/path.ts
78
69
  import { lstatSync, readdirSync } from "fs";
79
70
  import { isAbsolute, relative, resolve } from "path";
80
71
  function toAbsPath(path) {
81
72
  if (!isAbsolute(path)) path = resolve(path);
82
- return path;
73
+ return absPath.parse(path);
83
74
  }
84
75
  function toRelPath(from, to) {
85
- return relative(from, to);
76
+ return relPath.parse(relative(from, to));
86
77
  }
87
78
  function isObsidianDirectory(path) {
88
79
  const stat = lstatSync(path);
@@ -92,91 +83,135 @@ function isObsidianDirectory(path) {
92
83
  return true;
93
84
  }
94
85
 
95
- // src/utils/ignore.ts
96
- var VaultIgnorer = class {
97
- ign;
98
- rootPath;
99
- constructor(rootPath) {
100
- this.rootPath = rootPath;
101
- this.ign = ignore();
102
- this.ign.add([".*"]);
103
- }
104
- load() {
105
- const ignorePath = join2(this.rootPath, ".o2bignore");
106
- if (existsSync2(ignorePath))
107
- this.ign.add(readFileSync(ignorePath).toString());
108
- }
109
- ignore(path) {
110
- if (path === this.rootPath) return false;
111
- if (!existsSync2(path)) throw new Error("somethings wrong!");
112
- const stat = lstatSync2(path);
113
- const relPath = toRelPath(this.rootPath, path) + (stat.isDirectory() ? "/" : "");
114
- return this.ign.ignores(relPath);
115
- }
116
- };
117
-
118
86
  // src/core/sync/scan.ts
119
87
  function scan(rootPath, ign) {
120
88
  function aux(path) {
121
89
  if (ign == null ? void 0 : ign.ignore(path)) return null;
122
- const stat = lstatSync3(path);
90
+ const stat = lstatSync2(path);
123
91
  const name = basename(path);
124
- if (stat.isDirectory()) {
125
- const { dirs, files } = readdirSync2(path).reduce(
126
- (acc, entry) => {
127
- const node = aux(toAbsPath(join3(path, entry)));
128
- if ((node == null ? void 0 : node.type) === "dir") acc.dirs.push(node);
129
- if ((node == null ? void 0 : node.type) === "file") acc.files.push(node);
130
- return acc;
131
- },
132
- {
133
- dirs: [],
134
- files: []
135
- }
136
- );
92
+ if (stat.isFile() && name.endsWith(".md")) {
137
93
  return {
138
94
  path: toRelPath(rootPath, path),
139
95
  name,
140
- type: "dir",
141
- dirs,
142
- files
96
+ type: "file",
97
+ frontmatter: fetchFrontmatter(path, stat.mtime)
143
98
  };
144
99
  }
145
- if (stat.isFile() && name.endsWith(".md")) {
100
+ if (stat.isDirectory()) {
101
+ const dirs = [];
102
+ const files = [];
103
+ const entries = readdirSync2(path);
104
+ for (const entry of entries) {
105
+ const fullPath = toAbsPath(join2(path, entry));
106
+ const node = aux(fullPath);
107
+ if ((node == null ? void 0 : node.type) === "dir") dirs.push(node);
108
+ if ((node == null ? void 0 : node.type) === "file") files.push(node);
109
+ }
146
110
  return {
147
111
  path: toRelPath(rootPath, path),
148
112
  name,
149
- type: "file",
150
- frontmatter: getFrontmatter(path)
113
+ type: "dir",
114
+ dirs,
115
+ files
151
116
  };
152
117
  }
153
118
  return null;
154
119
  }
155
120
  return aux(rootPath);
156
121
  }
157
- function getFrontmatter(path) {
158
- const { data: srcFrontmatter } = matter(readFileSync2(path, "utf-8"));
122
+ function fetchFrontmatter(path, mtime) {
123
+ const { data } = matter(readFileSync(path, "utf-8"));
159
124
  return {
160
125
  title: basename(path).replace(/\.md$/, ""),
161
126
  description: "No description provided.",
162
- last_modified: lstatSync3(path).mtime.toLocaleDateString("sv-SE"),
163
- // "sv-SE" (스웨덴어)는 YYYY-MM-DD 형식으로 출력되는 유명한 트릭입니다.
164
- ...srcFrontmatter
127
+ last_modified: mtime.toISOString().split("T")[0],
128
+ ...data
165
129
  };
166
130
  }
167
131
 
132
+ // src/utils/id.ts
133
+ var idCounter = (start = 0) => {
134
+ let count = start;
135
+ return () => count++;
136
+ };
137
+
138
+ // src/core/sync/normalize.ts
139
+ function normalize(fileTree2) {
140
+ const dirNodes = [];
141
+ const fileNodes = [];
142
+ const fileId = idCounter();
143
+ const dirId = idCounter();
144
+ function aux(dir) {
145
+ const id2 = dirId();
146
+ const files = dir.files.map((file) => {
147
+ const id3 = fileId();
148
+ fileNodes.push({ id: id3, ...file });
149
+ return id3;
150
+ });
151
+ const dirs = dir.dirs.map((dir2) => aux(dir2));
152
+ dirNodes.push({ ...dir, id: id2, files, dirs });
153
+ return id2;
154
+ }
155
+ aux(fileTree2);
156
+ return {
157
+ dirNodes,
158
+ fileNodes,
159
+ vault: dirNodes[dirNodes.length - 1]
160
+ };
161
+ }
162
+
163
+ // src/utils/file.ts
164
+ import { writeFileSync as writeFileSync2 } from "fs";
165
+ import { join as join3 } from "path";
166
+ function generateJson(content, name, destPath) {
167
+ const jsonFilePath = join3(destPath, `${name}.json`);
168
+ writeFileSync2(jsonFilePath, JSON.stringify(content, null, 2), "utf-8");
169
+ }
170
+
171
+ // src/utils/ignore.ts
172
+ import { existsSync as existsSync2, lstatSync as lstatSync3, readFileSync as readFileSync2 } from "fs";
173
+ import { join as join4 } from "path";
174
+ import ignore from "ignore";
175
+ var VaultIgnorer = class {
176
+ ign;
177
+ rootPath;
178
+ constructor(rootPath) {
179
+ this.rootPath = rootPath;
180
+ this.ign = ignore();
181
+ this.ign.add([".*"]);
182
+ }
183
+ load() {
184
+ const ignorePath = join4(this.rootPath, ".o2bignore");
185
+ if (existsSync2(ignorePath))
186
+ this.ign.add(readFileSync2(ignorePath).toString());
187
+ }
188
+ ignore(path) {
189
+ if (path === this.rootPath) return false;
190
+ if (!existsSync2(path)) throw new Error("somethings wrong!");
191
+ const stat = lstatSync3(path);
192
+ const relPath2 = toRelPath(this.rootPath, path) + (stat.isDirectory() ? "/" : "");
193
+ return this.ign.ignores(relPath2);
194
+ }
195
+ };
196
+
168
197
  // src/runO2B.ts
169
198
  function runO2B(config) {
170
199
  const { srcPath, destPath, ignore: ignore2 } = config;
171
200
  if (!isObsidianDirectory(srcPath)) throw new Error("Not a obsidian vault");
172
201
  const ign = new VaultIgnorer(srcPath);
173
202
  if (ignore2) ign.load();
174
- const fileTree = scan(srcPath, ign);
175
- if (fileTree === null) throw new Error("No content in vault");
176
- mirror(fileTree, srcPath, destPath);
203
+ const fileTree2 = scan(srcPath, ign);
204
+ if (fileTree2 === null) throw new Error("No content in vault");
205
+ if (fileTree2.type !== "dir") throw new Error("No Vault?!");
206
+ mirror(fileTree2, srcPath, destPath);
207
+ const { vault, dirNodes, fileNodes } = normalize(fileTree2);
208
+ generateJson(vault, "vault", destPath);
209
+ generateJson(dirNodes, "dirNodes", destPath);
210
+ generateJson(fileNodes, "fileNodes", destPath);
177
211
  }
178
212
  export {
179
- FileTreeSchema,
180
- runO2B as default
213
+ runO2B as default,
214
+ normedDirNode,
215
+ normedFileNode
181
216
  };
182
217
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types/filetree.ts","../src/types/path.ts","../src/core/sync/mirror.ts","../src/core/sync/scan.ts","../src/utils/ignore.ts","../src/utils/path.ts","../src/runO2B.ts"],"sourcesContent":["import z from \"zod\";\nimport { RelPathSchema } from \"./path\";\n\ntype FileTreeNode = z.infer<typeof FileTreeNodeSchema>;\nconst FileTreeNodeSchema = z.object({\n path: RelPathSchema,\n name: z.string(),\n});\n\ntype Frontmatter = z.infer<typeof FrontmatterSchema>;\nconst FrontmatterSchema = z.object({\n title: z.string(),\n descriptionL: z.string(),\n last_modified: z.string(),\n});\n\ntype FileNode = z.infer<typeof FileNodeSchema>;\nconst FileNodeSchema = z.intersection(\n FileTreeNodeSchema,\n z.object({\n type: z.literal(\"file\"),\n frontmatter: FrontmatterSchema,\n })\n);\n\ntype DirNode = z.infer<typeof DirNodeScehma>;\nconst DirNodeScehma = z.intersection(\n FileTreeNodeSchema,\n z.object({\n type: z.literal(\"dir\"),\n // https://zod.dev/api?id=recursive-objects\n get dirs() {\n return z.array(DirNodeScehma);\n },\n files: z.array(FileNodeSchema),\n })\n);\n\ntype FileTree = z.infer<typeof FileTreeSchema>;\nconst FileTreeSchema = z.union([FileNodeSchema, DirNodeScehma]);\n\nexport { FileTree, FileNode, DirNode, Frontmatter, FileTreeSchema };\n","import z from \"zod\";\n\ntype AbsPath = z.infer<typeof AbsPathSchema>;\nconst AbsPathSchema = z.string().brand<\"absPath\">();\n\ntype RelPath = z.infer<typeof RelPathSchema>;\nconst RelPathSchema = z.string().brand<\"relpath\">();\n\nexport { type AbsPath, type RelPath, AbsPathSchema, RelPathSchema };\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 if (!isAbsolute(path)) path = resolve(path);\n return path as AbsPath;\n}\n\nfunction toRelPath(from: AbsPath, to: AbsPath): RelPath {\n return relative(from, to) as RelPath;\n}\n\nfunction isObsidianDirectory(path: AbsPath) {\n const stat = lstatSync(path);\n\n if (!stat.isDirectory()) return false;\n\n const obsMetaDir = readdirSync(path, { withFileTypes: true })\n .filter((node) => node.isDirectory())\n .filter((node) => node.name === \".obsidian\");\n\n if (obsMetaDir.length !== 1) return false;\n\n return 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 runO2B(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":["z","z","filetree","lstatSync","readdirSync","readFileSync","join","existsSync","lstatSync","join","join","existsSync","lstatSync","lstatSync","readdirSync","join","readFileSync","ignore"]}
1
+ {"version":3,"sources":["../src/types/filetree.ts","../src/types/path.ts","../src/core/sync/mirror.ts","../src/core/sync/scan.ts","../src/utils/path.ts","../src/utils/id.ts","../src/core/sync/normalize.ts","../src/utils/file.ts","../src/utils/ignore.ts","../src/runO2B.ts"],"sourcesContent":["import z from \"zod\";\nimport { relPath, type RelPath } from \"@o2bTypes/path\";\n\n/* ********** Schemas ********** */\n\nconst frontmatter = z.object({\n title: z.string(),\n description: z.string(),\n last_modified: z.string(),\n});\n\nconst baseNode = z.object({\n path: relPath,\n name: z.string(),\n});\n\nconst fileNode = baseNode.extend({\n type: z.literal(\"file\"),\n frontmatter: frontmatter,\n});\n\nconst dirNode: z.ZodType<DirNodeOutput, z.ZodTypeDef, DirNodeInput> =\n baseNode.extend({\n type: z.literal(\"dir\"),\n files: z.array(fileNode),\n dirs: z.lazy(() => dirNode.array()),\n });\n\nconst fileTree = z.union([dirNode, fileNode]);\n\n/* ********** Types ********** */\n\ntype BaseNodeOutput = z.infer<typeof baseNode>;\ntype BaseNodeInput = z.input<typeof baseNode>;\n\ntype FileNodeOutput = z.infer<typeof fileNode>;\ntype FileNodeInput = z.input<typeof fileNode>;\n\n// https://v3.zod.dev/?id=recursive-types\ntype DirNodeOutput = BaseNodeOutput & {\n type: \"dir\";\n files: FileNodeOutput[];\n dirs: DirNodeOutput[];\n};\ntype DirNodeInput = BaseNodeInput & {\n type: \"dir\";\n files: FileNodeInput[];\n dirs: DirNodeInput[];\n};\n\nexport type FileTree = z.infer<typeof fileTree>;\nexport type DirNode = DirNodeOutput;\nexport type FileNode = FileNodeOutput;\nexport type Frontmatter = z.infer<typeof frontmatter>;\n\n/* ********** Normalized ********** */\n\nconst id = z.number().int();\n\nexport const normedFileNode = fileNode.extend({\n id,\n});\n\nexport const normedDirNode = baseNode.extend({\n id,\n type: z.literal(\"dir\"),\n files: z.array(id),\n dirs: z.array(id),\n});\n\nexport type Id = z.infer<typeof id>;\nexport type NormedFileNode = z.infer<typeof normedFileNode>;\nexport type NormedDirNode = z.infer<typeof normedDirNode>;\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 { copyFileSync, existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { AbsPath } from \"@o2bTypes/path\";\nimport type { FileTree } from \"@o2bTypes/filetree\";\n\nexport function mirror(\n filetree: FileTree,\n srcPath: AbsPath,\n destPath: AbsPath,\n) {\n function aux(filetree: FileTree) {\n const from = join(srcPath, filetree.path);\n const to = join(destPath, filetree.path);\n\n if (filetree.type === \"dir\") {\n if (!existsSync(to)) mkdirSync(to, { recursive: true });\n\n filetree.files.forEach((file) => {\n aux(file);\n });\n\n filetree.dirs.forEach((dir) => {\n aux(dir);\n });\n }\n\n if (filetree.type === \"file\") {\n copyFileSync(from, to);\n }\n }\n\n aux(filetree);\n}\n","import { lstatSync, readdirSync, readFileSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\nimport matter from \"gray-matter\";\nimport type {\n DirNode,\n FileNode,\n FileTree,\n Frontmatter,\n} 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): FileTree | null {\n function aux(path: AbsPath): FileTree | null {\n if (ign?.ignore(path)) return null;\n\n const stat = lstatSync(path);\n const name = basename(path);\n\n if (stat.isFile() && name.endsWith(\".md\")) {\n return {\n path: toRelPath(rootPath, path),\n name,\n type: \"file\",\n frontmatter: fetchFrontmatter(path, stat.mtime),\n };\n }\n\n if (stat.isDirectory()) {\n const dirs: DirNode[] = [];\n const files: FileNode[] = [];\n const entries = readdirSync(path);\n\n for (const entry of entries) {\n const fullPath = toAbsPath(join(path, entry));\n const node = aux(fullPath);\n\n if (node?.type === \"dir\") dirs.push(node);\n if (node?.type === \"file\") files.push(node);\n }\n\n return {\n path: toRelPath(rootPath, path),\n name,\n type: \"dir\",\n dirs,\n files,\n };\n }\n\n return null;\n }\n\n return aux(rootPath);\n}\n\nfunction fetchFrontmatter(path: AbsPath, mtime: Date): Frontmatter {\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 { lstatSync, readdirSync } from \"node:fs\";\nimport { isAbsolute, 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\nfunction isObsidianDirectory(path: AbsPath) {\n const stat = lstatSync(path);\n\n if (!stat.isDirectory()) return false;\n\n const obsMetaDir = readdirSync(path, { withFileTypes: true })\n .filter((node) => node.isDirectory())\n .filter((node) => node.name === \".obsidian\");\n\n if (obsMetaDir.length !== 1) return false;\n\n return true;\n}\n\nexport { toAbsPath, toRelPath, isObsidianDirectory };\n","import { Id } from \"@o2bTypes/filetree\";\n\nexport const idCounter: (start?: number) => () => Id = (start: number = 0) => {\n let count = start;\n return () => count++;\n};\n","import { DirNode, Id, NormedDirNode, NormedFileNode } from \"@o2bTypes/filetree\";\nimport { idCounter } from \"@utils/id\";\n\nexport function normalize(fileTree: DirNode): {\n vault: NormedDirNode;\n dirNodes: NormedDirNode[];\n fileNodes: NormedFileNode[];\n} {\n const dirNodes: NormedDirNode[] = [];\n const fileNodes: NormedFileNode[] = [];\n\n const fileId = idCounter();\n const dirId = idCounter();\n\n function aux(dir: DirNode): Id {\n const id = dirId();\n\n const files = dir.files.map((file) => {\n const id = fileId();\n fileNodes.push({ id, ...file });\n return id;\n });\n\n const dirs = dir.dirs.map((dir) => aux(dir));\n dirNodes.push({ ...dir, id, files, dirs });\n\n return id;\n }\n aux(fileTree);\n\n return {\n dirNodes,\n fileNodes,\n vault: dirNodes[dirNodes.length - 1],\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 ignore(path: AbsPath): boolean {\n if (path === this.rootPath) return false;\n\n if (!existsSync(path)) throw new Error(\"somethings wrong!\");\n\n const stat = lstatSync(path);\n\n const relPath =\n toRelPath(this.rootPath, path) + (stat.isDirectory() ? \"/\" : \"\");\n\n return this.ign.ignores(relPath);\n }\n}\n","import { mirror, scan, normalize } from \"@core/sync\";\nimport type { Config } from \"@o2bTypes/config\";\nimport { generateJson } from \"@utils/file\";\nimport { isObsidianDirectory } from \"@utils/path\";\nimport { VaultIgnorer } from \"@utils/ignore\";\n\nexport default function runO2B(config: Config) {\n const { srcPath, destPath, ignore } = config;\n\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 if (fileTree.type !== \"dir\") throw new Error(\"No Vault?!\");\n\n mirror(fileTree, srcPath, destPath);\n const { vault, dirNodes, fileNodes } = normalize(fileTree);\n\n generateJson(vault, \"vault\", destPath);\n generateJson(dirNodes, \"dirNodes\", destPath);\n generateJson(fileNodes, \"fileNodes\", destPath);\n}\n"],"mappings":"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","names":["z","z","filetree","lstatSync","readdirSync","join","lstatSync","readdirSync","join","fileTree","id","dir","writeFileSync","join","existsSync","lstatSync","readFileSync","join","join","existsSync","readFileSync","lstatSync","relPath","ignore","fileTree"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@o2b/meta",
3
- "version": "1.0.0-dev.4",
3
+ "version": "1.0.0-dev.6",
4
4
  "description": "filetree generator for o2b",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -13,7 +13,7 @@
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",
@@ -33,6 +33,7 @@
33
33
  "@vitest/ui": "^4.0.16",
34
34
  "tsup": "^8.5.1",
35
35
  "typescript": "^5.9.3",
36
+ "vite-tsconfig-paths": "^6.0.5",
36
37
  "vitest": "^4.0.16",
37
38
  "zod": "^3.25.76"
38
39
  },