feat: exclude unlisted pages from recent notes

Respect the file.data.unlisted convention. Pages marked unlisted are
filtered out of the recent notes list before any user-supplied opts.filter
runs, so they cannot be accidentally surfaced by a permissive custom
filter.

Extracts the filter into a named filterListedPages() helper and exports
it for reuse and testing. Adds 3 unit tests covering unlisted exclusion,
passthrough when nothing is unlisted, and empty-input handling.
This commit is contained in:
saberzero1 2026-04-11 13:50:16 +02:00
parent 43da4722c5
commit 2e9753c996
No known key found for this signature in database
9 changed files with 48 additions and 9 deletions

View file

@ -9,4 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Respect the `file.data.unlisted` convention. Pages marked `unlisted` are never shown in the recent notes list. The unlisted filter runs before any user-supplied `opts.filter`.
- Exported `filterListedPages` helper for reuse and testing.
- Unit tests for `filterListedPages`.
- Initial Quartz community plugin template.

View file

@ -446,6 +446,9 @@ var withDefaultDateType = (data, defaultDateType) => ({
...data,
defaultDateType
});
function filterListedPages(pages) {
return pages.filter((p) => p.unlisted !== true);
}
var byDateAndAlphabeticalWithConfig = (cfg) => {
const sortFn = byDateAndAlphabetical();
return (f1, f2) => sortFn(
@ -469,7 +472,7 @@ var RecentNotes_default = ((userOpts) => {
}) => {
const globalCfg = cfg;
const opts = { ...defaultOptions(globalCfg), ...userOpts };
const pages = allFiles.filter(opts.filter).sort(opts.sort);
const pages = filterListedPages(allFiles).filter(opts.filter).sort(opts.sort);
const remaining = Math.max(0, pages.length - opts.limit);
const slug2 = fileData.slug;
const locale = cfg.locale ?? "en-US";

File diff suppressed because one or more lines are too long

3
dist/index.d.ts vendored
View file

@ -20,6 +20,7 @@ interface RecentNotesOptions {
filter: (f: RecentNotesPluginData) => boolean;
sort: SortFn;
}
declare function filterListedPages<T>(pages: T[]): T[];
declare const _default: (userOpts?: Partial<RecentNotesOptions>) => QuartzComponent;
export { _default as RecentNotes, type RecentNotesOptions };
export { _default as RecentNotes, type RecentNotesOptions, filterListedPages };

7
dist/index.js vendored
View file

@ -446,6 +446,9 @@ var withDefaultDateType = (data, defaultDateType) => ({
...data,
defaultDateType
});
function filterListedPages(pages) {
return pages.filter((p) => p.unlisted !== true);
}
var byDateAndAlphabeticalWithConfig = (cfg) => {
const sortFn = byDateAndAlphabetical();
return (f1, f2) => sortFn(
@ -469,7 +472,7 @@ var RecentNotes_default = ((userOpts) => {
}) => {
const globalCfg = cfg;
const opts = { ...defaultOptions(globalCfg), ...userOpts };
const pages = allFiles.filter(opts.filter).sort(opts.sort);
const pages = filterListedPages(allFiles).filter(opts.filter).sort(opts.sort);
const remaining = Math.max(0, pages.length - opts.limit);
const slug2 = fileData.slug;
const locale = cfg.locale ?? "en-US";
@ -502,6 +505,6 @@ var RecentNotes_default = ((userOpts) => {
return RecentNotes;
});
export { RecentNotes_default as RecentNotes };
export { RecentNotes_default as RecentNotes, filterListedPages };
//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

View file

@ -51,6 +51,10 @@ const withDefaultDateType = (
defaultDateType,
});
export function filterListedPages<T>(pages: T[]): T[] {
return pages.filter((p) => (p as { unlisted?: unknown }).unlisted !== true);
}
const byDateAndAlphabeticalWithConfig = (cfg: GlobalConfiguration): SortFn => {
const sortFn = byDateAndAlphabetical();
return (f1, f2) =>
@ -77,7 +81,9 @@ export default ((userOpts?: Partial<RecentNotesOptions>) => {
}: QuartzComponentProps & { displayClass?: string }) => {
const globalCfg = cfg as unknown as GlobalConfiguration;
const opts = { ...defaultOptions(globalCfg), ...userOpts };
const pages = (allFiles as RecentNotesPluginData[]).filter(opts.filter).sort(opts.sort);
const pages = filterListedPages(allFiles as RecentNotesPluginData[])
.filter(opts.filter)
.sort(opts.sort);
const remaining = Math.max(0, pages.length - opts.limit);
const slug = fileData.slug as string | undefined;
const locale = cfg.locale ?? "en-US";

View file

@ -1,4 +1,4 @@
export { default as RecentNotes } from "./components/RecentNotes";
export { default as RecentNotes, filterListedPages } from "./components/RecentNotes";
export type { RecentNotesOptions } from "./components/RecentNotes";
// Re-export shared types from @quartz-community/types

View file

@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { RecentNotes } from "../src/index";
import { RecentNotes, filterListedPages } from "../src/index";
describe("RecentNotes", () => {
it("is exported as a function", () => {
@ -12,3 +12,25 @@ describe("RecentNotes", () => {
expect(typeof component.css).toBe("string");
});
});
describe("filterListedPages", () => {
it("excludes pages marked unlisted: true", () => {
const pages = [
{ slug: "a" },
{ slug: "b", unlisted: true },
{ slug: "c" },
{ slug: "d", unlisted: false },
];
const result = filterListedPages(pages);
expect(result.map((p) => p.slug)).toEqual(["a", "c", "d"]);
});
it("returns pages unchanged when none are unlisted", () => {
const pages = [{ slug: "a" }, { slug: "b" }];
expect(filterListedPages(pages)).toEqual(pages);
});
it("handles empty input", () => {
expect(filterListedPages([])).toEqual([]);
});
});