@dogsbay/detect 0.2.0-beta.131 → 0.2.0-beta.133

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/README.md CHANGED
@@ -18,8 +18,12 @@ detect(["README.md", "docs/gitbook/SUMMARY.md", "docs/gitbook/.gitbook/assets/a.
18
18
  choices to offer (`spaces`, `versions`, `locales`) and whether the browser converts with
19
19
  less fidelity than the CLI.
20
20
 
21
- Path detectors so far: GitBook. Other formats are detected on disk by the CLI, in the same
22
- order, until their browser import lands.
21
+ Path detectors so far: GitBook, VitePress (`.vitepress/config.*` or
22
+ `.vitepress/config/index.*`, outside `node_modules`; the shallowest project wins, then `docs/`,
23
+ `site/`, `website/`) and Docusaurus (`docusaurus.config.{js,ts,mjs,cjs}` outside `node_modules`
24
+ and hidden folders; the shallowest wins, then `website/`, `docs/`, `site/`; its instances and
25
+ versions come from `@dogsbay/format-docusaurus/migrate`'s `docusaurusChoices`). Other formats are detected on disk by the CLI, in the same order, until
26
+ their browser import lands.
23
27
 
24
28
  ## License
25
29
 
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Docusaurus from paths. A `docusaurus.config.{js,ts,mjs,cjs}` is the marker (the CLI's
3
+ * `detectSource` looks for the same names); the project root is the folder holding it. Which
4
+ * docs instances and versions it has is in the config, which `@dogsbay/format-docusaurus/migrate`
5
+ * reads (`docusaurusChoices`): detection stays on paths alone.
6
+ */
7
+ import type { Detection } from "./types.js";
8
+ /** The config file names, in the order Docusaurus (and the CLI) look for them. */
9
+ export declare const DOCUSAURUS_CONFIGS: string[];
10
+ /** Project roots among `paths`: the folders holding a Docusaurus config, best first. */
11
+ export declare function docusaurusRoots(paths: string[]): string[];
12
+ /** Find a Docusaurus project among `paths` (a repo's whole tree, or one folder's listing). */
13
+ export declare function detectDocusaurus(paths: string[]): Detection | undefined;
@@ -0,0 +1,51 @@
1
+ import { depthOf, within } from "./paths.js";
2
+ /** The config file names, in the order Docusaurus (and the CLI) look for them. */
3
+ export const DOCUSAURUS_CONFIGS = ["docusaurus.config.js", "docusaurus.config.ts", "docusaurus.config.mjs", "docusaurus.config.cjs"];
4
+ /** Folders of a Docusaurus project that hold no docs pages of the default instance's kind. */
5
+ const NOT_DOCS = new Set(["node_modules", "src", "static", "blog", "i18n", "build"]);
6
+ /** Where a repo usually keeps its site, preferred among folders at the same depth. */
7
+ const PREFERRED = ["website", "docs", "site"];
8
+ /** Project roots among `paths`: the folders holding a Docusaurus config, best first. */
9
+ export function docusaurusRoots(paths) {
10
+ const roots = new Set();
11
+ for (const p of paths) {
12
+ const slash = p.lastIndexOf("/");
13
+ if (!DOCUSAURUS_CONFIGS.includes(p.slice(slash + 1)))
14
+ continue;
15
+ const root = slash < 0 ? "" : p.slice(0, slash);
16
+ // Not inside node_modules or a hidden folder (a vendored copy, a template in a cache)
17
+ if (root.split("/").some((s) => s === "node_modules" || s.startsWith(".")))
18
+ continue;
19
+ roots.add(root);
20
+ }
21
+ const rank = (r) => {
22
+ const i = PREFERRED.indexOf(r.slice(r.lastIndexOf("/") + 1));
23
+ return i < 0 ? PREFERRED.length : i;
24
+ };
25
+ return [...roots].sort((a, b) => depthOf(a) - depthOf(b) || rank(a) - rank(b) || a.localeCompare(b));
26
+ }
27
+ /** Find a Docusaurus project among `paths` (a repo's whole tree, or one folder's listing). */
28
+ export function detectDocusaurus(paths) {
29
+ const [found] = docusaurusRoots(paths);
30
+ // No config: a `sidebars*.js` at the top beside Markdown is a docs folder, as the CLI's
31
+ // `detectSource` reads it — but it is a weaker signal, so "low"
32
+ const bare = found === undefined && paths.some((p) => /^sidebars[^/]*\.(js|ts|cjs|mjs)$/.test(p)) && paths.some((p) => /\.mdx?$/i.test(p));
33
+ if (found === undefined && !bare)
34
+ return undefined;
35
+ const root = found ?? "";
36
+ // An estimate from paths: Markdown outside the folders that are not docs. The count of one
37
+ // instance is in the config, which the import reads: on pan.dev this says ~10,000 (9,500
38
+ // files under openapi-specs/) for 673 pages imported.
39
+ let pages = 0;
40
+ for (const p of paths) {
41
+ if (!within(p, root) || !/\.mdx?$/i.test(p))
42
+ continue;
43
+ const segs = (root ? p.slice(root.length + 1) : p).split("/");
44
+ if (segs.slice(0, -1).some((s) => NOT_DOCS.has(s) || s.startsWith(".") || s.endsWith("versioned_docs")))
45
+ continue;
46
+ if (segs.some((s) => s.startsWith("_")))
47
+ continue; // partials, as the import skips them
48
+ pages++;
49
+ }
50
+ return { format: "docusaurus", root, confidence: bare ? "low" : "high", pages, fidelity: "full" };
51
+ }
package/dist/index.d.ts CHANGED
@@ -2,7 +2,9 @@ import { type FormatName } from "./order.js";
2
2
  import type { Detection, ReadText } from "./types.js";
3
3
  export { FORMAT_ORDER, type FormatName } from "./order.js";
4
4
  export type { Detection, ReadText } from "./types.js";
5
+ export { detectDocusaurus, docusaurusRoots, DOCUSAURUS_CONFIGS } from "./docusaurus.js";
5
6
  export { detectGitbook, gitbookSpaces, gitbookVisible } from "./gitbook.js";
7
+ export { detectVitepress, vitepressConfigPath, vitepressPage, vitepressRoots, VITEPRESS_SKIP_DIRS } from "./vitepress.js";
6
8
  export { dirOf, isPlainPath, joinPath, normalizePath, within } from "./paths.js";
7
9
  /** The formats with a path detector, by name. */
8
10
  export declare const PATH_DETECTORS: Partial<Record<FormatName, (paths: string[], read?: ReadText) => Detection | undefined>>;
package/dist/index.js CHANGED
@@ -3,17 +3,24 @@
3
3
  * dependencies: the playground runs it on a repo's tree before loading any importer, and the
4
4
  * dogsbay CLI takes its format order from here (`FORMAT_ORDER`).
5
5
  *
6
- * Formats get a path detector here as their browser import lands (GitBook first); until then
6
+ * Formats get a path detector here as their browser import lands (GitBook, VitePress,
7
+ * Docusaurus); until then
7
8
  * the CLI keeps detecting them on disk, in this same order.
8
9
  */
10
+ import { detectDocusaurus } from "./docusaurus.js";
9
11
  import { detectGitbook } from "./gitbook.js";
12
+ import { detectVitepress } from "./vitepress.js";
10
13
  import { FORMAT_ORDER } from "./order.js";
11
14
  export { FORMAT_ORDER } from "./order.js";
15
+ export { detectDocusaurus, docusaurusRoots, DOCUSAURUS_CONFIGS } from "./docusaurus.js";
12
16
  export { detectGitbook, gitbookSpaces, gitbookVisible } from "./gitbook.js";
17
+ export { detectVitepress, vitepressConfigPath, vitepressPage, vitepressRoots, VITEPRESS_SKIP_DIRS } from "./vitepress.js";
13
18
  export { dirOf, isPlainPath, joinPath, normalizePath, within } from "./paths.js";
14
19
  /** The formats with a path detector, by name. */
15
20
  export const PATH_DETECTORS = {
21
+ docusaurus: (paths) => detectDocusaurus(paths),
16
22
  gitbook: detectGitbook,
23
+ vitepress: (paths) => detectVitepress(paths),
17
24
  };
18
25
  /** The first format, in `FORMAT_ORDER`, whose path detector claims `paths`. */
19
26
  export function detect(paths, read) {
@@ -0,0 +1,18 @@
1
+ /**
2
+ * VitePress from paths. `.vitepress/config.*` (or the split layout `.vitepress/config/index.*`)
3
+ * is the only unambiguous marker: requiring it is what keeps VitePress from claiming a
4
+ * plain-Markdown, MkDocs or GitBook corpus. The project root is the folder holding `.vitepress`.
5
+ * `@dogsbay/format-vitepress` finds its config through `vitepressConfigPath`, so detection and
6
+ * import cannot disagree.
7
+ */
8
+ import type { Detection } from "./types.js";
9
+ /** Folders a VitePress project keeps beside its pages, never pages themselves. */
10
+ export declare const VITEPRESS_SKIP_DIRS: Set<string>;
11
+ /** The config file of the project at `root`, as VitePress looks for it; undefined if none. */
12
+ export declare function vitepressConfigPath(paths: Iterable<string>, root: string): string | undefined;
13
+ /** Project roots among `paths`: the folders holding a `.vitepress` with a config, best first. */
14
+ export declare function vitepressRoots(paths: string[]): string[];
15
+ /** Is `path` (relative to a project root) a page VitePress would build, before its config is read? */
16
+ export declare function vitepressPage(rel: string): boolean;
17
+ /** Find a VitePress project among `paths` (a repo's whole tree, or one folder's listing). */
18
+ export declare function detectVitepress(paths: string[]): Detection | undefined;
@@ -0,0 +1,60 @@
1
+ import { depthOf, joinPath, within } from "./paths.js";
2
+ const CONFIG_EXTS = [".ts", ".mts", ".mjs", ".js", ".cts", ".cjs"];
3
+ /** Folders a VitePress project keeps beside its pages, never pages themselves. */
4
+ export const VITEPRESS_SKIP_DIRS = new Set([".vitepress", "node_modules", "public", "dist", ".git", "cache", ".cache"]);
5
+ /** Where a repo usually keeps its docs, preferred among folders at the same depth. */
6
+ const PREFERRED = ["docs", "site", "website"];
7
+ /** The config file of the project at `root`, as VitePress looks for it; undefined if none. */
8
+ export function vitepressConfigPath(paths, root) {
9
+ const all = paths instanceof Set ? paths : new Set(paths);
10
+ const vp = joinPath(root, ".vitepress");
11
+ for (const ext of CONFIG_EXTS)
12
+ if (all.has(`${vp}/config${ext}`))
13
+ return `${vp}/config${ext}`;
14
+ for (const ext of CONFIG_EXTS)
15
+ if (all.has(`${vp}/config/index${ext}`))
16
+ return `${vp}/config/index${ext}`;
17
+ return undefined;
18
+ }
19
+ /** Project roots among `paths`: the folders holding a `.vitepress` with a config, best first. */
20
+ export function vitepressRoots(paths) {
21
+ const all = new Set(paths);
22
+ const roots = new Set();
23
+ for (const p of paths) {
24
+ const m = /(^|\/)\.vitepress\/config(\/index)?\.[cm]?[jt]s$/.exec(p);
25
+ if (!m)
26
+ continue;
27
+ const root = p.slice(0, m.index);
28
+ // Not inside node_modules or another hidden folder (a vendored copy, a cache)
29
+ if (root.split("/").some((s) => s === "node_modules" || (s.startsWith(".") && s !== "")))
30
+ continue;
31
+ if (vitepressConfigPath(all, root))
32
+ roots.add(root);
33
+ }
34
+ const rank = (r) => {
35
+ const i = PREFERRED.indexOf(r.slice(r.lastIndexOf("/") + 1));
36
+ return i < 0 ? PREFERRED.length : i;
37
+ };
38
+ return [...roots].sort((a, b) => depthOf(a) - depthOf(b) || rank(a) - rank(b) || a.localeCompare(b));
39
+ }
40
+ /** Is `path` (relative to a project root) a page VitePress would build, before its config is read? */
41
+ export function vitepressPage(rel) {
42
+ const segs = rel.split("/");
43
+ if (segs.slice(0, -1).some((s) => VITEPRESS_SKIP_DIRS.has(s) || s.startsWith(".")))
44
+ return false;
45
+ return /\.md$/i.test(segs[segs.length - 1]);
46
+ }
47
+ /** Find a VitePress project among `paths` (a repo's whole tree, or one folder's listing). */
48
+ export function detectVitepress(paths) {
49
+ const [root] = vitepressRoots(paths);
50
+ if (root === undefined)
51
+ return undefined;
52
+ let pages = 0;
53
+ for (const p of paths) {
54
+ if (!within(p, root))
55
+ continue;
56
+ if (vitepressPage(root ? p.slice(root.length + 1) : p))
57
+ pages++;
58
+ }
59
+ return { format: "vitepress", root, confidence: "high", pages, fidelity: "full" };
60
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dogsbay/detect",
3
- "version": "0.2.0-beta.131",
3
+ "version": "0.2.0-beta.133",
4
4
  "description": "Which documentation tool made this folder: the import formats' detection order (shared by the dogsbay CLI and the playground) and detectors that read paths only, with no Node and no dependencies",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",