diff --git a/_tools/check_circular_submodule_dependencies.ts b/_tools/check_circular_submodule_dependencies.ts index d15d6730a1c5..2493989d8e62 100644 --- a/_tools/check_circular_submodule_dependencies.ts +++ b/_tools/check_circular_submodule_dependencies.ts @@ -11,30 +11,43 @@ import { * When run with `--graph` it will output a graphviz graph in dot language. */ +type DepState = "ready" | "not ready" | "needs clean up" | "deprecated"; +type Dep = { + name: string; + set: Set; + state: DepState; +}; + const root = new URL("../", import.meta.url).href; -const deps: Record> = {}; +const deps: Record = {}; -function getSubmoduleName(url: string) { +function getSubmoduleNameFromUrl(url: string) { return url.replace(root, "").split("/")[0]; } -async function check(submod: string, paths: string[] = ["mod.ts"]) { +async function check( + submod: string, + state: DepState, + paths: string[] = ["mod.ts"], +): Promise { const deps = new Set(); for (const path of paths) { const entrypoint = new URL(`../${submod}/${path}`, import.meta.url).href; const graph = await createGraph(entrypoint); - for (const dep of new Set(getDeps(graph, entrypoint))) { + for ( + const dep of new Set(getSubmoduleDepsFromSpecifier(graph, entrypoint)) + ) { deps.add(dep); } } deps.delete(submod); deps.delete("types.d.ts"); - return deps; + return { name: submod, set: deps, state }; } /** Returns submodule dependencies */ -function getDeps( +function getSubmoduleDepsFromSpecifier( graph: ModuleGraphJson, specifier: string, seen: Set = new Set(), @@ -42,7 +55,7 @@ function getDeps( const { dependencies } = graph.modules.find((item: ModuleJson) => item.specifier === specifier )!; - const deps = new Set([getSubmoduleName(specifier)]); + const deps = new Set([getSubmoduleNameFromUrl(specifier)]); seen.add(specifier); if (dependencies) { for (const { code, type } of dependencies) { @@ -50,7 +63,7 @@ function getDeps( if (seen.has(specifier)) { continue; } - const res = getDeps( + const res = getSubmoduleDepsFromSpecifier( graph, specifier, seen, @@ -63,17 +76,17 @@ function getDeps( return deps; } -deps["archive"] = await check("archive"); -deps["assert"] = await check("assert"); -deps["async"] = await check("async"); -deps["bytes"] = await check("bytes"); -deps["collections"] = await check("collections"); -deps["console"] = await check("console"); -deps["crypto"] = await check("crypto"); -deps["csv"] = await check("csv"); -deps["datetime"] = await check("datetime"); -deps["dotenv"] = await check("dotenv"); -deps["encoding"] = await check("encoding", [ +deps["archive"] = await check("archive", "not ready"); +deps["assert"] = await check("assert", "ready"); +deps["async"] = await check("async", "ready"); +deps["bytes"] = await check("bytes", "ready"); +deps["collections"] = await check("collections", "needs clean up"); +deps["console"] = await check("console", "not ready"); +deps["crypto"] = await check("crypto", "needs clean up"); +deps["csv"] = await check("csv", "ready"); +deps["datetime"] = await check("datetime", "deprecated"); +deps["dotenv"] = await check("dotenv", "not ready"); +deps["encoding"] = await check("encoding", "ready", [ "ascii85.ts", "base32.ts", "base58.ts", @@ -83,41 +96,43 @@ deps["encoding"] = await check("encoding", [ "hex.ts", "varint.ts", ]); -deps["flags"] = await check("flags"); -deps["fmt"] = await check("fmt", [ +deps["flags"] = await check("flags", "not ready"); +deps["fmt"] = await check("fmt", "ready", [ "bytes.ts", "colors.ts", "duration.ts", "printf.ts", ]); -deps["front_matter"] = await check("front_matter"); -deps["fs"] = await check("fs"); -deps["html"] = await check("html"); -deps["http"] = await check("http"); -deps["io"] = await check("io"); -deps["json"] = await check("json"); -deps["jsonc"] = await check("jsonc"); -deps["log"] = await check("log"); -deps["media_types"] = await check("media_types"); -deps["msgpack"] = await check("msgpack"); -deps["path"] = await check("path"); -deps["permissions"] = await check("permissions"); -deps["regexp"] = await check("regexp"); -deps["semver"] = await check("semver"); -deps["signal"] = await check("signal"); -deps["streams"] = await check("streams"); -deps["testing"] = await check("testing", [ +deps["front_matter"] = await check("front_matter", "needs clean up"); +deps["fs"] = await check("fs", "ready"); +deps["html"] = await check("html", "not ready"); +deps["http"] = await check("http", "needs clean up"); +deps["io"] = await check("io", "deprecated"); +deps["json"] = await check("json", "ready"); +deps["jsonc"] = await check("jsonc", "ready"); +deps["log"] = await check("log", "not ready"); +deps["media_types"] = await check("media_types", "needs clean up"); +deps["msgpack"] = await check("msgpack", "not ready"); +deps["path"] = await check("path", "needs clean up"); +deps["permissions"] = await check("permissions", "deprecated"); +deps["regexp"] = await check("regexp", "not ready"); +deps["semver"] = await check("semver", "not ready"); +deps["signal"] = await check("signal", "deprecated"); +deps["streams"] = await check("streams", "needs clean up"); +deps["testing"] = await check("testing", "needs clean up", [ "bdd.ts", "mock.ts", "snapshot.ts", "time.ts", "types.ts", ]); -deps["toml"] = await check("toml"); -deps["uuid"] = await check("uuid"); -deps["wasi"] = await check("wasi", ["snapshot_preview1.ts"]); -deps["yaml"] = await check("yaml"); +deps["toml"] = await check("toml", "ready"); +deps["url"] = await check("url", "not ready"); +deps["uuid"] = await check("uuid", "ready"); +deps["wasi"] = await check("wasi", "not ready", ["snapshot_preview1.ts"]); +deps["yaml"] = await check("yaml", "ready"); +/** Checks circular deps between sub modules */ function checkCircularDeps( submod: string, ancestors: string[] = [], @@ -131,7 +146,7 @@ function checkCircularDeps( if (!d) { return; } - for (const mod of d) { + for (const mod of d.set) { const res = checkCircularDeps(mod, currentDeps, seen); if (res) { return res; @@ -139,12 +154,32 @@ function checkCircularDeps( } } +/** Formats label for diagram */ +function formatLabel(mod: string) { + return '"' + mod.replace(/_/g, "_\\n") + '"'; +} + +/** Returns node style (in DOT language) for each state */ +function stateToNodeStyle(state: DepState) { + switch (state) { + case "ready": + return "[shape=doublecircle fixedsize=1 height=1.1]"; + case "not ready": + return "[shape=box style=filled, fillcolor=pink]"; + case "needs clean up": + return "[shape=circle fixedsize=1 height=1.1 style=filled, fillcolor=yellow]"; + case "deprecated": + return "[shape=septagon style=filled, fillcolor=gray]"; + } +} + if (Deno.args.includes("--graph")) { console.log("digraph std_deps {"); for (const mod of Object.keys(deps)) { - console.log(` ${mod};`); - for (const dep of deps[mod]) { - console.log(` ${mod} -> ${dep};`); + const info = deps[mod]; + console.log(` ${formatLabel(mod)} ${stateToNodeStyle(info.state)};`); + for (const dep of deps[mod].set) { + console.log(` ${formatLabel(mod)} -> ${dep};`); } } console.log("}");