Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/fix-destructured-param-docs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"docflow": patch
---

Report missing `@param` documentation for destructured parameters without a JSDoc root name.
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export class FunctionValidator extends Validator<FunctionLikeNode> {
const { value: rootName } = jsDocRootsForDestructured.next();

if (rootName == null) {
return [];
return [entry.name];
}

return [rootName, ...entry.paths.map(path => `${rootName}.${path}`)];
Expand Down
66 changes: 66 additions & 0 deletions packages/cli/src/tests/commands/check/function-validator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,72 @@ describe("FunctionValidator", () => {
});

describe("destructured param validation", () => {
it("should detect missing documentation for a destructured parameter without a virtual root", () => {
const sourceFile = createTSSourceFile(`
/**
* @public
* @returns The greeting
*/
export function greet({ name }: { name: string }): string {
return name;
}
`);

const fn = sourceFile.getFunctions()[0];
assert(fn != null, "Expected function");

const validator = new FunctionValidator(fn, parseJSDocFromNode(fn));
const result = validator.validate();

expect(result.isValid).toBe(false);
expect(result.errors).toEqual([{ type: "missing_param", target: "{ name }" }]);
});

it("should detect an undocumented destructured parameter after a documented one", () => {
const sourceFile = createTSSourceFile(`
/**
* @public
* @param first - The first options
* @param first.name - The name
* @returns The greeting
*/
export function greet({ name }: { name: string }, { greeting }: { greeting: string }): string {
return greeting + name;
}
`);

const fn = sourceFile.getFunctions()[0];
assert(fn != null, "Expected function");

const validator = new FunctionValidator(fn, parseJSDocFromNode(fn));
const result = validator.validate();

expect(result.isValid).toBe(false);
expect(result.errors).toEqual([{ type: "missing_param", target: "{ greeting }" }]);
});

it("should not use a named parameter's documentation as a destructured root", () => {
const sourceFile = createTSSourceFile(`
/**
* @public
* @param prefix - The greeting prefix
* @returns The greeting
*/
export function greet(prefix: string, { name }: { name: string }): string {
return prefix + name;
}
`);

const fn = sourceFile.getFunctions()[0];
assert(fn != null, "Expected function");

const validator = new FunctionValidator(fn, parseJSDocFromNode(fn));
const result = validator.validate();

expect(result.isValid).toBe(false);
expect(result.errors).toEqual([{ type: "missing_param", target: "{ name }" }]);
});

it("should return valid when destructured params are documented with virtual root name", () => {
const sourceFile = createTSSourceFile(`
interface Options {
Expand Down