diff --git a/.changeset/replace-fast-glob-with-tinyglobby.md b/.changeset/replace-fast-glob-with-tinyglobby.md new file mode 100644 index 0000000..9783b07 --- /dev/null +++ b/.changeset/replace-fast-glob-with-tinyglobby.md @@ -0,0 +1,5 @@ +--- +"leadtype": patch +--- + +Replace `fast-glob` with `tinyglobby` to shrink the dependency tree (16 transitive deps → 3) and reduce install footprint (~1.2 MB → ~240 KB). Globbing behavior and call-site options are unchanged. diff --git a/bun.lock b/bun.lock index ef633ae..7a03cd5 100644 --- a/bun.lock +++ b/bun.lock @@ -73,19 +73,18 @@ "version": "0.0.0", "devDependencies": { "ai": "^6.0.168", - "fast-glob": "3.3.3", + "tinyglobby": "0.2.16", "vitest": "^2.1.8", "zod": "^3.23.8", }, }, "packages/leadtype": { "name": "leadtype", - "version": "0.0.0", + "version": "0.1.0", "bin": "./dist/cli.js", "dependencies": { "@types/mdast": "4.0.4", "decode-named-character-reference": "1.3.0", - "fast-glob": "3.3.3", "gray-matter": "4.0.3", "jiti": "^2.7.0", "json5": "2.2.3", @@ -95,6 +94,7 @@ "remark": "15.0.1", "remark-gfm": "4.0.1", "remark-mdx": "3.1.1", + "tinyglobby": "0.2.16", "unified": "11.0.5", "unist-builder": "4.0.0", "unist-util-is": "6.0.1", diff --git a/evals/lib/tools.ts b/evals/lib/tools.ts index 5f1917c..445107d 100644 --- a/evals/lib/tools.ts +++ b/evals/lib/tools.ts @@ -2,7 +2,7 @@ import { spawn } from "node:child_process"; import { mkdir, readdir, readFile, writeFile } from "node:fs/promises"; import path from "node:path"; import { tool } from "ai"; -import fg from "fast-glob"; +import { glob as fg } from "tinyglobby"; import { z } from "zod"; import type { ToolCall } from "./transcript"; @@ -235,6 +235,9 @@ export function scopedTools(ctx: ToolCtx) { absolute: false, dot: false, followSymbolicLinks: false, + // Agent supplies the pattern; keep fast-glob semantics so a + // bare directory name doesn't silently expand to `dir/**`. + expandDirectories: false, }); return matches.join("\n"); }, diff --git a/evals/package.json b/evals/package.json index 2b10dcd..e7413a4 100644 --- a/evals/package.json +++ b/evals/package.json @@ -12,7 +12,7 @@ }, "devDependencies": { "ai": "^6.0.168", - "fast-glob": "3.3.3", + "tinyglobby": "0.2.16", "vitest": "^2.1.8", "zod": "^3.23.8" } diff --git a/packages/leadtype/package.json b/packages/leadtype/package.json index 55a53cf..9351256 100644 --- a/packages/leadtype/package.json +++ b/packages/leadtype/package.json @@ -92,7 +92,6 @@ "dependencies": { "@types/mdast": "4.0.4", "decode-named-character-reference": "1.3.0", - "fast-glob": "3.3.3", "gray-matter": "4.0.3", "jiti": "^2.7.0", "json5": "2.2.3", @@ -102,6 +101,7 @@ "remark": "15.0.1", "remark-gfm": "4.0.1", "remark-mdx": "3.1.1", + "tinyglobby": "0.2.16", "unified": "11.0.5", "unist-builder": "4.0.0", "unist-util-is": "6.0.1", diff --git a/packages/leadtype/src/cli.test.ts b/packages/leadtype/src/cli.test.ts index da6b774..4774b1a 100644 --- a/packages/leadtype/src/cli.test.ts +++ b/packages/leadtype/src/cli.test.ts @@ -3,7 +3,7 @@ import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import path from "node:path"; import { fileURLToPath } from "node:url"; -import fg from "fast-glob"; +import { glob as fg } from "tinyglobby"; import { afterEach, describe, expect, it } from "vitest"; import { runCli } from "./cli"; @@ -524,6 +524,38 @@ describe("leadtype CLI", () => { expect(error.filters.include).toEqual(["nope/**"]); }); + it("treats a bare directory in --include as matching no MDX files", async () => { + // tinyglobby expands bare directory names to `dir/**` by default; fast-glob + // didn't. With expandDirectories disabled at the call site, `--include build` + // should fail the same way `--include nope` does — not silently include + // every file under `docs/build/`. + const outDir = await createTempDir(); + const capture = createCapture(); + + const code = await runCli( + [ + "generate", + "--src", + repoRoot, + "--out", + outDir, + "--include", + "build", + "--format", + "json", + ], + capture.io + ); + + expect(code).toBe(1); + const error = JSON.parse(capture.stderr) as { + error: string; + filters: { include: string[] }; + }; + expect(error.error).toContain("No MDX files matched"); + expect(error.filters.include).toEqual(["build"]); + }); + it("rejects invalid generate formats as usage errors", async () => { const capture = createCapture(); diff --git a/packages/leadtype/src/cli/generate.ts b/packages/leadtype/src/cli/generate.ts index f421414..16372b2 100644 --- a/packages/leadtype/src/cli/generate.ts +++ b/packages/leadtype/src/cli/generate.ts @@ -2,9 +2,9 @@ import { existsSync } from "node:fs"; import { cp, mkdir, mkdtemp, readFile, rm } from "node:fs/promises"; import { tmpdir } from "node:os"; import path from "node:path"; -import fg from "fast-glob"; import matter from "gray-matter"; import { createJiti } from "jiti"; +import { glob as fg } from "tinyglobby"; import { convertAllMdx } from "../convert"; import { logger, @@ -436,6 +436,11 @@ async function createSourceMirror( const files = await fg(patterns, { absolute: false, cwd: docsDir, + // tinyglobby expands bare directory names (`build` → `build/**`) by + // default; fast-glob did not. Disable it so `--include build` still + // reports "No MDX files matched" instead of silently slurping everything + // under `build/`. + expandDirectories: false, ignore: filters.exclude, onlyFiles: true, }); diff --git a/packages/leadtype/src/convert/convert.ts b/packages/leadtype/src/convert/convert.ts index 5767013..9d5e28d 100644 --- a/packages/leadtype/src/convert/convert.ts +++ b/packages/leadtype/src/convert/convert.ts @@ -4,11 +4,11 @@ import { mkdir, readFile, writeFile } from "node:fs/promises"; import { cpus } from "node:os"; import { basename, dirname, join, relative, resolve, sep } from "node:path"; import { promisify } from "node:util"; -import fg from "fast-glob"; import matter from "gray-matter"; import { remark } from "remark"; import remarkGfm from "remark-gfm"; import remarkMdx from "remark-mdx"; +import { glob as fg } from "tinyglobby"; import type { Pluggable, PluggableList } from "unified"; import { deriveDocContext, diff --git a/packages/leadtype/src/lint/runner.ts b/packages/leadtype/src/lint/runner.ts index 8b14f17..84fc211 100644 --- a/packages/leadtype/src/lint/runner.ts +++ b/packages/leadtype/src/lint/runner.ts @@ -1,10 +1,10 @@ import { existsSync } from "node:fs"; import { readFile } from "node:fs/promises"; import { relative, resolve, sep } from "node:path"; -import fg from "fast-glob"; import matter from "gray-matter"; import { remark } from "remark"; import remarkGfm from "remark-gfm"; +import { glob as fg } from "tinyglobby"; import { visit } from "unist-util-visit"; import * as v from "valibot"; import { convertMdxToMarkdown } from "../convert"; @@ -97,6 +97,9 @@ async function glob( onlyFiles: true, ignore, dot: false, + // Preserve fast-glob semantics: callers can pass user-supplied ignores, + // and bare directory entries should not auto-expand to `dir/**`. + expandDirectories: false, }); }