A CLI for publishing standard.site documents to ATProto sequoia.pub
standard site lexicon cli publishing

Add Draft Post Feature #5

merged opened by stevedylan.dev targeting main from feat/draft-posts

Overview#

Simple change to add a draft field to the frontmatter config that will skip publishing posts that are marked draft: true, completing Issue #4 . Frontmatter config allows mapping this to another term like private and will ask the user during the init command. The sequoia.json config can be updated at any time to add or change this field.

Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:ia2zdnhjaokf5lazhxrmj6eu/sh.tangled.repo.pull/3mdrah7dj7q22
+52 -1
Diff #0
+4 -1
docs/docs/pages/config.mdx
··· 51 51 | `publishDate` | `string` | Yes | `"publishDate"`, `"pubDate"`, `"date"`, `"createdAt"`, `"created_at"` | Publication date | 52 52 | `coverImage` | `string` | No | `"ogImage"` | Cover image filename | 53 53 | `tags` | `string[]` | No | `"tags"` | Post tags/categories | 54 + | `draft` | `boolean` | No | `"draft"` | If `true`, post is skipped during publish | 54 55 55 56 ### Example 56 57 ··· 61 62 publishDate: 2024-01-15 62 63 ogImage: cover.jpg 63 64 tags: [welcome, intro] 65 + draft: false 64 66 --- 65 67 ``` 66 68 ··· 72 74 { 73 75 "frontmatter": { 74 76 "publishDate": "date", 75 - "coverImage": "thumbnail" 77 + "coverImage": "thumbnail", 78 + "draft": "private" 76 79 } 77 80 } 78 81 ```
+21
docs/docs/pages/publishing.mdx
··· 45 45 46 46 The `maxAgeDays` setting prevents flooding your feed when first setting up Sequoia. For example, if you have 40 existing blog posts, only those published within the last 30 days will be posted to Bluesky. 47 47 48 + ## Draft Posts 49 + 50 + Posts with `draft: true` in their frontmatter are automatically skipped during publishing. This lets you work on content without accidentally publishing it. 51 + 52 + ```yaml 53 + --- 54 + title: Work in Progress 55 + draft: true 56 + --- 57 + ``` 58 + 59 + If your framework uses a different field name (like `private` or `hidden`), configure it in `sequoia.json`: 60 + 61 + ```json 62 + { 63 + "frontmatter": { 64 + "draft": "private" 65 + } 66 + } 67 + ``` 68 + 48 69 ## Troubleshooting 49 70 50 71 - If you have files in your markdown directory that should be ignored, use the [`ignore` array in the config](/config#ignoring-files).
+7
packages/cli/src/commands/init.ts
··· 138 138 defaultValue: "tags", 139 139 placeholder: "tags, categories, keywords, etc.", 140 140 }), 141 + draftField: () => 142 + text({ 143 + message: "Field name for draft status:", 144 + defaultValue: "draft", 145 + placeholder: "draft, private, hidden, etc.", 146 + }), 141 147 }, 142 148 { onCancel }, 143 149 ); ··· 149 155 ["publishDate", frontmatterConfig.dateField, "publishDate"], 150 156 ["coverImage", frontmatterConfig.coverField, "ogImage"], 151 157 ["tags", frontmatterConfig.tagsField, "tags"], 158 + ["draft", frontmatterConfig.draftField, "draft"], 152 159 ]; 153 160 154 161 const builtMapping = fieldMappings.reduce<FrontmatterMapping>(
+11
packages/cli/src/commands/publish.ts
··· 96 96 action: "create" | "update"; 97 97 reason: string; 98 98 }> = []; 99 + const draftPosts: BlogPost[] = []; 99 100 100 101 for (const post of posts) { 102 + // Skip draft posts 103 + if (post.frontmatter.draft) { 104 + draftPosts.push(post); 105 + continue; 106 + } 107 + 101 108 const contentHash = await getContentHash(post.rawContent); 102 109 const relativeFilePath = path.relative(configDir, post.filePath); 103 110 const postState = state.posts[relativeFilePath]; ··· 125 132 } 126 133 } 127 134 135 + if (draftPosts.length > 0) { 136 + log.info(`Skipping ${draftPosts.length} draft post${draftPosts.length === 1 ? "" : "s"}`); 137 + } 138 + 128 139 if (postsToPublish.length === 0) { 129 140 log.success("All posts are up to date. Nothing to publish."); 130 141 return;
+7
packages/cli/src/lib/markdown.ts
··· 99 99 const tagsField = mapping?.tags || "tags"; 100 100 frontmatter.tags = raw[tagsField] || raw.tags; 101 101 102 + // Draft mapping 103 + const draftField = mapping?.draft || "draft"; 104 + const draftValue = raw[draftField] ?? raw.draft; 105 + if (draftValue !== undefined) { 106 + frontmatter.draft = draftValue === true || draftValue === "true"; 107 + } 108 + 102 109 // Always preserve atUri (internal field) 103 110 frontmatter.atUri = raw.atUri; 104 111
+2
packages/cli/src/lib/types.ts
··· 4 4 publishDate?: string; // Field name for publish date (default: "publishDate", also checks "pubDate", "date", "createdAt", "created_at") 5 5 coverImage?: string; // Field name for cover image (default: "ogImage") 6 6 tags?: string; // Field name for tags (default: "tags") 7 + draft?: string; // Field name for draft status (default: "draft") 7 8 } 8 9 9 10 // Strong reference for Bluesky post (com.atproto.repo.strongRef) ··· 46 47 tags?: string[]; 47 48 ogImage?: string; 48 49 atUri?: string; 50 + draft?: boolean; 49 51 } 50 52 51 53 export interface BlogPost {

History

1 round 0 comments
sign up or login to add to the discussion
stevedylan.dev submitted #0
1 commit
expand
feat: added draft field to frontmatter config
expand 0 comments
pull request successfully merged