|
import * as flags from "https://deno.land/[email protected]/flags/mod.ts"; |
|
import * as fs from "https://deno.land/[email protected]/fs/mod.ts"; |
|
import * as path from "https://deno.land/[email protected]/path/mod.ts"; |
|
|
|
type EntryId = string; |
|
type EntryGraph = Map<EntryId, Set<EntryId>>; |
|
|
|
function buildEntryGraph(dir: string): EntryGraph { |
|
const entries = new Map<EntryId, Set<EntryId>>(); |
|
const root = path.resolve(dir); |
|
for (const entry of fs.walkSync(root)) { |
|
if (entry.isFile === false) continue; |
|
const filename = entry.path; |
|
if (path.extname(filename) !== ".md") continue; |
|
const [yyyy, mm, dd, _] = path.basename(filename, ".md").split("-"); |
|
const entryId = [yyyy, mm, dd].join("-"); |
|
const data = fs.readFileStrSync(filename, { |
|
encoding: "utf8", |
|
}); |
|
const linkedEntryIds = new Set<EntryId>(); // mutable |
|
for ( |
|
const m of data.matchAll( |
|
/https?:\/\/blog\.bouzuya\.net\/(\d\d\d\d\/\d\d\/\d\d)\//g, |
|
) |
|
) { |
|
const linkedEntryId = m[1].split("/").join("-"); |
|
linkedEntryIds.add(linkedEntryId); |
|
} |
|
entries.set(entryId, linkedEntryIds); |
|
} |
|
return entries; |
|
} |
|
|
|
function toJson(graph: EntryGraph): string { |
|
type JsonMap<T> = { [key: string]: T }; |
|
type JsonSet = JsonMap<null>; |
|
const json: JsonMap<JsonSet> = {}; // mutable |
|
for (const [entryId, linkedEntryIds] of graph) { |
|
const set: { [entryId: string]: null } = {}; // mutable |
|
for (const linkedEntryId of linkedEntryIds) { |
|
set[linkedEntryId] = null; |
|
} |
|
json[entryId] = set; |
|
} |
|
return JSON.stringify(json); |
|
} |
|
|
|
export function main(): void { |
|
const { args } = Deno; |
|
const { dir } = flags.parse(args); |
|
if (typeof dir !== "string") throw new Error("dir is not string"); |
|
const graph = buildEntryGraph(dir); |
|
console.log(toJson(graph)); |
|
} |