@quartz-community/unlisted-pages 0.1.0

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/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+
12
+ - Initial release.
13
+ - `UnlistedPages` transformer: zero-config rehype plugin that copies `file.data.frontmatter.unlisted` to `file.data.unlisted` when it is a boolean. Enables the `file.data.unlisted` convention respected by `content-index`, `search`, `backlinks`, `recent-notes`, `folder-page`, and `tag-page` to work for any page, not only encrypted ones.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Quartz Community
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,81 @@
1
+ # @quartz-community/unlisted-pages
2
+
3
+ Zero-config transformer that bridges `frontmatter.unlisted` to `file.data.unlisted` so any page can opt out of discovery surfaces while remaining accessible by direct URL.
4
+
5
+ ## What it does
6
+
7
+ Quartz v5 plugins that respect the `file.data.unlisted` convention — `content-index`, `search`, `backlinks`, `recent-notes`, `folder-page`, and `tag-page` — will hide a page from their output when `file.data.unlisted === true`. But nothing in the core pipeline copies `frontmatter.unlisted` onto `file.data.unlisted`, so a user writing:
8
+
9
+ ```yaml
10
+ ---
11
+ title: My Draft
12
+ unlisted: true
13
+ ---
14
+ ```
15
+
16
+ gets no effect. This plugin fixes that. It is a trivially small rehype plugin that does one thing: if `frontmatter.unlisted` is a boolean, copy it to `file.data.unlisted`.
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npx quartz plugin add github:quartz-community/unlisted-pages
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ Add an `unlisted` field to any page's frontmatter:
27
+
28
+ ```yaml
29
+ ---
30
+ title: My Draft
31
+ unlisted: true
32
+ ---
33
+ ```
34
+
35
+ When registered, this plugin marks the page as unlisted. Every Quartz v5 plugin that respects the convention will then hide it:
36
+
37
+ - **Absent from** `contentIndex.json`, `sitemap.xml`, the RSS feed, backlinks, recent notes, folder listings, tag listings, graph, explorer, and search.
38
+ - **Still emitted** as HTML, so the page remains accessible by direct URL.
39
+
40
+ ## Configuration
41
+
42
+ Zero options. Just enable it.
43
+
44
+ ```yaml title="quartz.config.yaml"
45
+ - source: github:quartz-community/unlisted-pages
46
+ enabled: true
47
+ ```
48
+
49
+ ## Interaction with `@quartz-community/encrypted-pages`
50
+
51
+ The `encrypted-pages` plugin also sets `file.data.unlisted` when `unlistWhenEncrypted: true` or when per-page frontmatter specifies `unlisted: true`. The two plugins compose cleanly:
52
+
53
+ - If you install only `unlisted-pages`: any page with `unlisted: true` in frontmatter is hidden from listing surfaces. Encryption is independent.
54
+ - If you install only `encrypted-pages`: `unlisted: true` only works on pages that are ALSO encrypted (have a password). Non-encrypted pages with `unlisted: true` are silently ignored.
55
+ - If you install both: `unlisted: true` works for every page, encrypted or not. This is the recommended setup for sites that use encrypted pages.
56
+
57
+ ## How `unlisted` works across consumer plugins
58
+
59
+ | Plugin | Behavior when `file.data.unlisted === true` |
60
+ | --------------- | -------------------------------------------------------------------------- |
61
+ | `content-index` | Page absent from `contentIndex.json`, `sitemap.xml`, and the RSS feed. |
62
+ | `search` | Page absent from search results (derived from `contentIndex.json`). |
63
+ | `graph` | Page absent from graph nodes and edges (derived from `contentIndex.json`). |
64
+ | `explorer` | Page absent from the sidebar file tree (derived from `contentIndex.json`). |
65
+ | `backlinks` | Page never appears as a backlink source on other pages. |
66
+ | `recent-notes` | Page absent from the recent notes list. |
67
+ | `folder-page` | Page absent from folder listings and folder discovery. |
68
+ | `tag-page` | Page absent from tag listings and tag discovery. |
69
+
70
+ In every case, the page's HTML is still emitted and accessible by direct URL.
71
+
72
+ ## API
73
+
74
+ - Category: Transformer
75
+ - Function name: `UnlistedPages()`
76
+ - Source: [`quartz-community/unlisted-pages`](https://github.com/quartz-community/unlisted-pages)
77
+ - Install: `npx quartz plugin add github:quartz-community/unlisted-pages`
78
+
79
+ ## License
80
+
81
+ MIT
@@ -0,0 +1,6 @@
1
+ import { QuartzTransformerPlugin } from '@quartz-community/types';
2
+ export { QuartzPluginData, QuartzTransformerPlugin, QuartzTransformerPluginInstance } from '@quartz-community/types';
3
+
4
+ declare const UnlistedPages: QuartzTransformerPlugin<undefined>;
5
+
6
+ export { UnlistedPages };
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ // src/transformer.ts
2
+ var rehypeUnlisted = () => {
3
+ return () => (_tree, file) => {
4
+ const frontmatter = file.data?.frontmatter;
5
+ const unlisted = frontmatter?.unlisted;
6
+ if (typeof unlisted === "boolean") {
7
+ file.data.unlisted = unlisted;
8
+ }
9
+ };
10
+ };
11
+ var UnlistedPages = () => {
12
+ return {
13
+ name: "UnlistedPages",
14
+ htmlPlugins() {
15
+ return [rehypeUnlisted()];
16
+ }
17
+ };
18
+ };
19
+
20
+ export { UnlistedPages };
21
+ //# sourceMappingURL=index.js.map
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/transformer.ts"],"names":[],"mappings":";AAKA,IAAM,iBAAiB,MAA4B;AACjD,EAAA,OAAO,MAAM,CAAC,KAAA,EAAiB,IAAA,KAAgB;AAC7C,IAAA,MAAM,WAAA,GAAc,KAAK,IAAA,EAAM,WAAA;AAC/B,IAAA,MAAM,WAAW,WAAA,EAAa,QAAA;AAC9B,IAAA,IAAI,OAAO,aAAa,SAAA,EAAW;AACjC,MAAC,IAAA,CAAK,KAAiC,QAAA,GAAW,QAAA;AAAA,IACpD;AAAA,EACF,CAAA;AACF,CAAA;AAEO,IAAM,gBAAoD,MAAM;AACrE,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,eAAA;AAAA,IACN,WAAA,GAA6B;AAC3B,MAAA,OAAO,CAAC,gBAAgB,CAAA;AAAA,IAC1B;AAAA,GACF;AACF","file":"index.js","sourcesContent":["import type { PluggableList, Plugin } from \"unified\";\nimport type { Root as HastRoot } from \"hast\";\nimport type { VFile } from \"vfile\";\nimport type { QuartzTransformerPlugin } from \"@quartz-community/types\";\n\nconst rehypeUnlisted = (): Plugin<[], HastRoot> => {\n return () => (_tree: HastRoot, file: VFile) => {\n const frontmatter = file.data?.frontmatter as Record<string, unknown> | undefined;\n const unlisted = frontmatter?.unlisted;\n if (typeof unlisted === \"boolean\") {\n (file.data as Record<string, unknown>).unlisted = unlisted;\n }\n };\n};\n\nexport const UnlistedPages: QuartzTransformerPlugin<undefined> = () => {\n return {\n name: \"UnlistedPages\",\n htmlPlugins(): PluggableList {\n return [rehypeUnlisted()];\n },\n };\n};\n"]}
@@ -0,0 +1 @@
1
+ export { BuildCtx, ProcessedContent, QuartzPluginData, QuartzTransformerPlugin, QuartzTransformerPluginInstance } from '@quartz-community/types';
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+
2
+ //# sourceMappingURL=types.js.map
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"types.js"}
package/package.json ADDED
@@ -0,0 +1,98 @@
1
+ {
2
+ "name": "@quartz-community/unlisted-pages",
3
+ "version": "0.1.0",
4
+ "description": "Bridges `frontmatter.unlisted` to `file.data.unlisted` so any page can opt out of discovery surfaces (contentIndex, RSS, sitemap, graph, explorer, search, backlinks, recent-notes, folder-page, tag-page) while remaining accessible by direct URL.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Quartz Community",
8
+ "homepage": "https://quartz.jzhao.xyz",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/quartz-community/unlisted-pages"
12
+ },
13
+ "keywords": [
14
+ "quartz",
15
+ "quartz-plugin",
16
+ "unlisted-pages",
17
+ "unlisted",
18
+ "hidden",
19
+ "rehype",
20
+ "hast",
21
+ "frontmatter"
22
+ ],
23
+ "files": [
24
+ "dist",
25
+ "README.md",
26
+ "LICENSE",
27
+ "CHANGELOG.md"
28
+ ],
29
+ "exports": {
30
+ ".": {
31
+ "types": "./dist/index.d.ts",
32
+ "import": "./dist/index.js"
33
+ },
34
+ "./types": {
35
+ "types": "./dist/types.d.ts",
36
+ "import": "./dist/types.js"
37
+ },
38
+ "./package.json": "./package.json"
39
+ },
40
+ "main": "./dist/index.js",
41
+ "types": "./dist/index.d.ts",
42
+ "sideEffects": false,
43
+ "scripts": {
44
+ "build": "tsup",
45
+ "dev": "tsup --watch",
46
+ "lint": "eslint . --max-warnings=0",
47
+ "format": "prettier . --check",
48
+ "test": "vitest run",
49
+ "typecheck": "tsc --noEmit",
50
+ "check": "npm run typecheck && npm run lint && npm run format && npm run test",
51
+ "prepublishOnly": "npm run build"
52
+ },
53
+ "peerDependencies": {
54
+ "vfile": "^6.0.3"
55
+ },
56
+ "peerDependenciesMeta": {
57
+ "vfile": {
58
+ "optional": true
59
+ }
60
+ },
61
+ "dependencies": {
62
+ "@quartz-community/types": "^0.2.1",
63
+ "@quartz-community/utils": "^0.1.0"
64
+ },
65
+ "devDependencies": {
66
+ "@types/hast": "^3.0.4",
67
+ "@types/node": "^24.10.0",
68
+ "@typescript-eslint/eslint-plugin": "^7.18.0",
69
+ "@typescript-eslint/parser": "^7.18.0",
70
+ "eslint": "^8.57.0",
71
+ "eslint-config-prettier": "^9.1.0",
72
+ "prettier": "^3.6.2",
73
+ "tsup": "^8.5.0",
74
+ "typescript": "^5.9.3",
75
+ "unified": "^11.0.5",
76
+ "vfile": "^6.0.3",
77
+ "vitest": "^2.1.9"
78
+ },
79
+ "quartz": {
80
+ "name": "unlisted-pages",
81
+ "displayName": "Unlisted Pages",
82
+ "category": "transformer",
83
+ "version": "1.0.0",
84
+ "quartzVersion": ">=5.0.0",
85
+ "dependencies": [],
86
+ "defaultOrder": 45,
87
+ "defaultEnabled": true,
88
+ "defaultOptions": {},
89
+ "optionSchema": {}
90
+ },
91
+ "engines": {
92
+ "node": ">=22",
93
+ "npm": ">=10.9.2"
94
+ },
95
+ "publishConfig": {
96
+ "access": "public"
97
+ }
98
+ }