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

Add pathTemplate config option for custom URL paths #23

merged opened by stevedylan.dev targeting main from feat/slug-templating

Summary#

  • Adds a pathTemplate config field that supports token-based URL path construction (e.g., /blog/{year}/{month}/{slug}), overriding the default pathPrefix/slug behavior
  • Centralizes path resolution into a single resolvePostPath() helper used across publish, sync, Bluesky posting, and document creation
  • Preserves pathTemplate through config generation and the sequoia update flow
  • Documents the feature with available tokens ({slug}, {year}, {month}, {day}, {title}, and arbitrary frontmatter fields)

Test plan#

  • Verify sequoia publish --dry-run --verbose shows correct URLs with pathTemplate set
  • Verify sequoia update preserves pathTemplate in saved config
  • Verify default behavior unchanged when pathTemplate is not set

Prerelease NPM Package#

This PR can be tested two ways

  • Install the prerelease NPM package with npm i -g sequoia-cli@0.5.0-alpha.0
  • Checkout this branch and build locally
Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:ia2zdnhjaokf5lazhxrmj6eu/sh.tangled.repo.pull/3mepbv5k4po22
+27 -11
Interdiff #0 #1
docs/docs/pages/config.mdx

This file has not been changed.

+5 -1
packages/cli/src/commands/publish.ts
··· 241 241 242 242 let postUrl = ""; 243 243 if (verbose) { 244 - const postPath = resolvePostPath(post, config.pathPrefix, config.pathTemplate); 244 + const postPath = resolvePostPath( 245 + post, 246 + config.pathPrefix, 247 + config.pathTemplate, 248 + ); 245 249 postUrl = `\n ${config.siteUrl}${postPath}`; 246 250 } 247 251 log.message(
+5 -1
packages/cli/src/commands/sync.ts
··· 151 151 // Document path is like /posts/my-post-slug (or custom pathPrefix/pathTemplate) 152 152 const postsByPath = new Map<string, (typeof localPosts)[0]>(); 153 153 for (const post of localPosts) { 154 - const postPath = resolvePostPath(post, config.pathPrefix, config.pathTemplate); 154 + const postPath = resolvePostPath( 155 + post, 156 + config.pathPrefix, 157 + config.pathTemplate, 158 + ); 155 159 postsByPath.set(postPath, post); 156 160 } 157 161
packages/cli/src/commands/update.ts

This file has not been changed.

+10 -2
packages/cli/src/lib/atproto.ts
··· 245 245 config: PublisherConfig, 246 246 coverImage?: BlobObject, 247 247 ): Promise<string> { 248 - const postPath = resolvePostPath(post, config.pathPrefix, config.pathTemplate); 248 + const postPath = resolvePostPath( 249 + post, 250 + config.pathPrefix, 251 + config.pathTemplate, 252 + ); 249 253 const publishDate = new Date(post.frontmatter.publishDate); 250 254 251 255 // Determine textContent: use configured field from frontmatter, or fallback to markdown body ··· 306 310 307 311 const [, , collection, rkey] = uriMatch; 308 312 309 - const postPath = resolvePostPath(post, config.pathPrefix, config.pathTemplate); 313 + const postPath = resolvePostPath( 314 + post, 315 + config.pathPrefix, 316 + config.pathTemplate, 317 + ); 310 318 const publishDate = new Date(post.frontmatter.publishDate); 311 319 312 320 // Determine textContent: use configured field from frontmatter, or fallback to markdown body
packages/cli/src/lib/config.ts

This file has not been changed.

+6 -5
packages/cli/src/lib/markdown.ts
··· 231 231 return slug; 232 232 } 233 233 234 - export function resolvePathTemplate( 235 - template: string, 236 - post: BlogPost, 237 - ): string { 234 + export function resolvePathTemplate(template: string, post: BlogPost): string { 238 235 const publishDate = new Date(post.frontmatter.publishDate); 239 236 const year = String(publishDate.getFullYear()); 240 237 const month = String(publishDate.getMonth() + 1).padStart(2, "0"); ··· 270 267 return result; 271 268 } 272 269 273 - export function resolvePostPath(post: BlogPost, pathPrefix?: string, pathTemplate?: string): string { 270 + export function resolvePostPath( 271 + post: BlogPost, 272 + pathPrefix?: string, 273 + pathTemplate?: string, 274 + ): string { 274 275 if (pathTemplate) { 275 276 return resolvePathTemplate(pathTemplate, post); 276 277 }
packages/cli/src/lib/types.ts

This file has not been changed.

+1 -2
packages/cli/src/components/sequoia-comments.js
··· 588 588 this.commentsContainer = container; 589 589 this.state = { type: "loading" }; 590 590 this.abortController = null; 591 - 592 591 } 593 592 594 593 static get observedAttributes() { ··· 701 700 </div> 702 701 `; 703 702 if (this.hide) { 704 - this.commentsContainer.style.display = 'none'; 703 + this.commentsContainer.style.display = "none"; 705 704 } 706 705 break; 707 706

History

2 rounds 0 comments
sign up or login to add to the discussion
2 commits
expand
feat: added slug templating
chore: lint and format
1/1 success
expand
expand 0 comments
pull request successfully merged
1 commit
expand
feat: added slug templating
1/1 failed
expand
expand 0 comments