Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"private": true,
"type": "module",
"scripts": {
"dev": "bun run --filter leadtype build && bun run pipeline:build && PORTLESS_PORT=1355 PORTLESS_HTTPS=0 portless run vite dev",
"build": "bun run --filter leadtype build && bun run pipeline:build && vite build",
"dev": "bun run --filter leadtype build && bun run --silent pipeline:build && PORTLESS_PORT=1355 PORTLESS_HTTPS=0 portless run vite dev",
"build": "bun run --filter leadtype build && bun run --silent pipeline:build && vite build",
"preview": "PORTLESS_PORT=1355 PORTLESS_HTTPS=0 portless run vite preview",
"check-types": "tsgo --noEmit",
"test:e2e": "bun run --filter leadtype build && bun run pipeline:build && playwright test",
Expand Down
2 changes: 0 additions & 2 deletions apps/example/scripts/mdx-convert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,9 @@ if (!existsSync(srcDir)) {
process.exit(1);
}

process.stdout.write(`Converting MDX from ${srcDir} → ${outDir}\n`);
await convertAllMdx({
srcDir,
outDir,
remarkPlugins,
enrichFrontmatterFromGit: true,
});
process.stdout.write("MDX conversion complete\n");
2 changes: 1 addition & 1 deletion packages/leadtype/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
"README.md"
],
"scripts": {
"build": "rollup -c --configPlugin rollup-plugin-esbuild && bun run docs:generate",
"build": "rollup -c --configPlugin rollup-plugin-esbuild --silent && bun run --silent docs:generate",
"dev": "rollup -c --configPlugin rollup-plugin-esbuild -w",
"check-types": "tsgo --noEmit",
"docs:generate": "bun run ./scripts/generate-docs.ts",
Expand Down
21 changes: 17 additions & 4 deletions packages/leadtype/scripts/generate-docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import docsConfig from "../../../docs/docs.config";
import { convertAllMdx } from "../src/convert/index";
import { logger } from "../src/internal/logger";
import { generateAgentsMd, resolveDocsNavigation } from "../src/llm/index";
import { defaultRemarkPlugins } from "../src/remark/index";

Expand Down Expand Up @@ -34,9 +35,13 @@ const navigation = await resolveDocsNavigation({
});
if (navigation.unknown.length > 0) {
for (const { urlPath, slug } of navigation.unknown) {
process.stderr.write(
`error: ${urlPath} declares unknown group "${slug}".\n`
);
logger.error({
human: { message: `${urlPath} declares unknown group "${slug}"` },
json: {
event: "docs.unknown_group",
fields: { urlPath, slug },
},
});
}
process.exit(1);
}
Expand All @@ -52,4 +57,12 @@ const { outputPath } = await generateAgentsMd({
groups: docsConfig.groups,
});

process.stdout.write(`Generated ${outputPath} and ${OUT_DOCS_DIR}/*.md\n`);
logger.info({
human: {
message: `Generated ${outputPath} and ${OUT_DOCS_DIR}/*.md`,
},
json: {
event: "docs.generate.done",
fields: { outputPath, docsDir: OUT_DOCS_DIR },
},
});
3 changes: 2 additions & 1 deletion packages/leadtype/src/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@ describe("leadtype CLI", () => {
);

expect(code).toBe(0);
expect(capture.stdout).toContain("Generated docs pipeline output");
expect(capture.stdout).toBe("");
expect(capture.stderr).toContain("Generated docs pipeline output");
expect(existsSync(path.join(outDir, "docs", "methodology.md"))).toBe(true);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
expect(
existsSync(path.join(outDir, "docs", "build", "connect-docs-site.md"))
Expand Down
7 changes: 6 additions & 1 deletion packages/leadtype/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { resolve } from "node:path";
import { pathToFileURL } from "node:url";
import { getGenerateUsage, runGenerateCommand } from "./cli/generate";
import { logger, setLogStreams } from "./internal/logger";
import { getLintUsage, runLintCommand } from "./lint/cli";

type CliIo = {
Expand Down Expand Up @@ -36,6 +37,7 @@ export async function runCli(
argv: string[],
io: CliIo = { stderr: process.stderr, stdout: process.stdout }
): Promise<number> {
setLogStreams(io);
const [command, ...rest] = argv;

if (!command || command === "-h" || command === "--help") {
Expand Down Expand Up @@ -72,7 +74,10 @@ if (isDirectRun()) {
})
.catch((error) => {
const message = error instanceof Error ? error.message : String(error);
process.stderr.write(`leadtype: ${message}\n`);
logger.error({
human: { message, hint: "set DEBUG=1 to print the stack" },
json: { event: "cli.fatal", fields: { message } },
});
if (process.env.DEBUG && error instanceof Error && error.stack) {
process.stderr.write(`${error.stack}\n`);
}
Expand Down
55 changes: 36 additions & 19 deletions packages/leadtype/src/cli/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import path from "node:path";
import fg from "fast-glob";
import matter from "gray-matter";
import { convertAllMdx } from "../convert";
import {
logger,
setLogFormat,
setLogStreams,
setVerbose,
} from "../internal/logger";
import type { DocsGroup, ProductInfo } from "../llm";
import {
generateAgentReadabilityArtifacts,
Expand Down Expand Up @@ -38,6 +44,7 @@ export type GenerateArgs = {
outDir: string;
srcDir: string;
summary?: string;
verbose: boolean;
};

export type GenerateIo = {
Expand Down Expand Up @@ -106,6 +113,7 @@ Options:
--enrich-git Add lastModified and lastAuthor from git history
--format <fmt> text | json (default: text)
--json Alias for --format json
-v, --verbose Print per-file progress events to stderr
-h, --help Show this help
`;

Expand All @@ -132,6 +140,7 @@ export function parseGenerateArgs(argv: string[]): GenerateArgs {
include: [],
outDir: DEFAULT_OUT_DIR,
srcDir: ".",
verbose: false,
};

for (let i = 0; i < argv.length; i++) {
Expand Down Expand Up @@ -166,6 +175,8 @@ export function parseGenerateArgs(argv: string[]): GenerateArgs {
args.format = value;
} else if (arg === "--json") {
args.format = "json";
} else if (arg === "--verbose" || arg === "-v") {
args.verbose = true;
} else if (arg) {
throw new Error(`unknown option: ${arg}`);
}
Expand Down Expand Up @@ -358,22 +369,23 @@ export async function runGenerateCommand(
return 0;
}

setLogFormat(args.format === "json" ? "json" : "human");
setVerbose(args.verbose);
setLogStreams({ stderr: io.stderr });

Comment thread
coderabbitai[bot] marked this conversation as resolved.
const srcDir = path.resolve(args.srcDir);
const docsDir = path.resolve(srcDir, args.docsDir);
const outDir = path.resolve(args.outDir);

if (!existsSync(docsDir)) {
if (args.format === "json") {
io.stderr.write(
`${JSON.stringify(
{
error: "docs directory not found",
path: docsDir,
},
null,
2
)}\n`
);
logger.error({
human: { message: `docs directory not found at ${docsDir}` },
json: {
event: "generate.docs_not_found",
fields: { error: "docs directory not found", path: docsDir },
},
});
} else {
io.stderr.write(
`leadtype generate: docs directory not found at ${docsDir}\n`
Expand Down Expand Up @@ -465,25 +477,30 @@ export async function runGenerateCommand(

if (args.format === "json") {
io.stdout.write(`${renderGenerateResult(result)}\n`);
} else {
io.stdout.write(`Generated docs pipeline output in ${outDir}\n`);
}
logger.info({
human: { message: `Generated docs pipeline output in ${outDir}` },
json: {
event: "generate.done",
fields: { outDir, mode: result.mode },
},
});
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
if (args.format === "json") {
io.stderr.write(
`${JSON.stringify(
{
logger.error({
human: { message },
json: {
event: "generate.fail",
fields: {
error: message,
filters: {
exclude: args.exclude,
include: args.include,
},
},
null,
2
)}\n`
);
},
});
} else {
io.stderr.write(`leadtype generate: ${message}\n`);
}
Expand Down
86 changes: 75 additions & 11 deletions packages/leadtype/src/convert/convert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
deriveDocContext,
resolvePlaceholderStrings,
} from "../internal/docs-context";
import { log } from "../internal/logger";
import { logger } from "../internal/logger";

const execFileAsync = promisify(execFile);

Expand Down Expand Up @@ -426,11 +426,15 @@ async function processMdxFile(
const resolvedPath = resolve(mdxFilePath);

if (!resolvedPath.endsWith(".mdx")) {
log.error(`Not an MDX file: ${resolvedPath}`);
logger.error({
human: { message: `not an MDX file: ${resolvedPath}` },
json: { event: "convert.skip_non_mdx", fields: { path: resolvedPath } },
});
return false;
}

try {
const startedAt = Date.now();
const { markdown } = await convertMdxToMarkdown(
resolvedPath,
remarkPlugins,
Expand All @@ -446,11 +450,31 @@ async function processMdxFile(
await writeFile(outputPath, markdown);

if (!writeToStdout) {
log.summary(`Converted: ${resolvedPath} → ${outputPath}`);
const ms = Date.now() - startedAt;
logger.debug({
human: { message: `convert ${resolvedPath} → ${outputPath} (${ms}ms)` },
json: {
event: "convert.file",
fields: { src: resolvedPath, out: outputPath, ms },
},
});
}
return true;
} catch (error) {
log.error(`Failed to process ${mdxFilePath}: ${String(error)}`);
const reason = error instanceof Error ? error.message : String(error);
const stack = error instanceof Error ? error.stack : undefined;
logger.error({
human: {
message: `failed to process ${mdxFilePath}: ${reason}`,
hint: stack ?? "run with LEADTYPE_VERBOSE=1 for more verbose logs",
},
json: {
event: "convert.fail",
fields: stack
? { file: mdxFilePath, reason, stack }
: { file: mdxFilePath, reason },
},
});
return false;
}
}
Expand Down Expand Up @@ -495,7 +519,10 @@ export async function convertAllMdx(
: resolve(process.cwd(), "public");

if (!existsSync(srcDir)) {
log.verbose(`Source directory does not exist: ${srcDir}`);
logger.debug({
human: { message: `source directory does not exist: ${srcDir}` },
json: { event: "convert.batch.no_src", fields: { srcDir } },
});
return;
}

Expand Down Expand Up @@ -523,25 +550,62 @@ export async function convertAllMdx(
Array.from(outputDirs, (dir) => mkdir(dir, { recursive: true }))
);

const startedAt = Date.now();
const results = await mapLimit(mdxFiles, concurrency, async (mdxFilePath) => {
try {
const fileStartedAt = Date.now();
const { markdown } = await convertMdxToMarkdown(
mdxFilePath,
remarkPlugins,
enrichFromGitFlag
);
const outputPath = deriveOutputPath(mdxFilePath, srcDir, outDir);
await writeFile(outputPath, markdown);
logger.debug({
human: {
message: `convert ${mdxFilePath} → ${outputPath} (${Date.now() - fileStartedAt}ms)`,
},
json: {
event: "convert.file",
fields: {
src: mdxFilePath,
out: outputPath,
ms: Date.now() - fileStartedAt,
},
},
});
return true;
} catch (fileError) {
log.error(`Failed to process ${mdxFilePath}: ${String(fileError)}`);
const reason =
fileError instanceof Error ? fileError.message : String(fileError);
logger.error({
human: { message: `failed to process ${mdxFilePath}: ${reason}` },
json: {
event: "convert.fail",
fields: { file: mdxFilePath, reason },
},
});
return false;
}
});

const converted = results.filter(Boolean).length;
const failed = results.length - converted;
log.verbose(
`Converted ${converted} MDX files${failed > 0 ? `, ${failed} failed` : ""}`
);
const ok = results.filter(Boolean).length;
const failed = results.length - ok;
const ms = Date.now() - startedAt;
logger.info({
human: {
message: `Converted ${ok} docs in ${ms} ms${failed > 0 ? ` (${failed} failed)` : ""}`,
},
json: {
event: "convert.batch",
fields: {
srcDir,
outDir,
files: results.length,
ok,
failed,
ms,
},
},
});
}
Loading
Loading