Skip to content

Add new-post scaffolding script#17

Merged
mahata merged 1 commit intomainfrom
add-new-post-script
Apr 7, 2026
Merged

Add new-post scaffolding script#17
mahata merged 1 commit intomainfrom
add-new-post-script

Conversation

@mahata
Copy link
Copy Markdown
Owner

@mahata mahata commented Apr 7, 2026

Summary

  • Add a TypeScript script to scaffold new blog posts with frontmatter pre-filled
  • Add 17 unit tests covering the script's utility functions

Usage

npm run new-post -- "My Post Title"
npm run new-post -- "My Draft Post" --draft

Creates a file like src/content/blog/2026-04-07-my-post-title.md with:

---
title: My Post Title
date: 2026-04-07
---

The script errors if no title is given or if the file already exists.

Changes

File Change
scripts/new-post.ts New — scaffolding script (slugify, date format, frontmatter generation, file creation)
scripts/new-post.test.ts New — 17 unit tests for all exported utility functions
package.json Add tsx dev dependency; add new-post script
README.md Document the npm run new-post command

- Add scripts/new-post.ts: generates a new blog post file with
  frontmatter pre-filled (title, date, optional draft flag)
- Add scripts/new-post.test.ts: 17 unit tests for slugify, formatDate,
  buildFilename, and buildFrontmatter
- Install tsx for running TypeScript scripts directly
- Add npm run new-post command to package.json
- Update README with usage documentation
Copilot AI review requested due to automatic review settings April 7, 2026 13:49
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new TypeScript CLI script to scaffold blog posts in src/content/blog/ with pre-filled frontmatter, along with unit tests and npm wiring/documentation.

Changes:

  • Add scripts/new-post.ts to generate dated, slugified filenames and frontmatter (optionally draft: true)
  • Add scripts/new-post.test.ts (Vitest) covering the exported helper functions
  • Add tsx and an npm new-post script; document usage in the README
Show a summary per file
File Description
scripts/new-post.ts New CLI scaffolding script (slug/date/frontmatter + file creation)
scripts/new-post.test.ts New Vitest unit tests for exported helpers
package.json Adds tsx and npm run new-post entry
README.md Documents the new scaffolding command
package-lock.json Lockfile updates for tsx and transitive deps

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comments suppressed due to low confidence (1)

scripts/new-post.ts:59

  • buildFrontmatter(title, new Date(), draft) should use the same captured Date instance as the filename to ensure the date: field matches the file prefix in edge cases (e.g., around midnight).
  const content = buildFrontmatter(title, new Date(), draft);
  fs.writeFileSync(filepath, content, 'utf-8');
  • Files reviewed: 4/5 changed files
  • Comments generated: 5

Comment thread scripts/new-post.ts
Comment on lines +3 to +4

const BLOG_DIR = path.join(import.meta.dirname, '..', 'src', 'content', 'blog');
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import.meta.dirname is not a standard Node ESM feature and appears unused elsewhere in the repo; running this script in Node/tsx will likely throw at startup. Derive the directory from import.meta.url instead (e.g., via fileURLToPath + path.dirname).

Suggested change
const BLOG_DIR = path.join(import.meta.dirname, '..', 'src', 'content', 'blog');
import { fileURLToPath } from 'node:url';
const currentFilePath = fileURLToPath(import.meta.url);
const currentDir = path.dirname(currentFilePath);
const BLOG_DIR = path.join(currentDir, '..', 'src', 'content', 'blog');

Copilot uses AI. Check for mistakes.
Comment thread scripts/new-post.ts
Comment on lines +63 to +65
if (process.argv[1] && import.meta.url.endsWith(process.argv[1])) {
main();
}
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The executable guard import.meta.url.endsWith(process.argv[1]) is brittle (notably on Windows where process.argv[1] is typically a C:\... path while import.meta.url is a file:///C:/... URL). Compare normalized file paths instead (e.g., fileURLToPath(import.meta.url) vs path.resolve(process.argv[1])).

Copilot uses AI. Check for mistakes.
Comment thread scripts/new-post.ts
Comment on lines +22 to +24
export function buildFilename(date: Date, title: string): string {
return `${formatDate(date)}-${slugify(title)}.md`;
}
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If slugify(title) returns an empty string (e.g., a title containing only characters outside [a-z0-9], such as emoji or non-Latin text), buildFilename will produce a name like YYYY-MM-DD-.md. Consider validating that the slug is non-empty and exiting with a clear error (or using a different slugging approach that supports Unicode).

Copilot uses AI. Check for mistakes.
Comment thread scripts/new-post.ts
Comment on lines +49 to +52
const { title, draft } = parseArgs(process.argv);
const filename = buildFilename(new Date(), title);
const filepath = path.join(BLOG_DIR, filename);

Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new Date() is used to compute the filename; for consistency, capture a single now value once in main() and reuse it for both filename and frontmatter generation (avoids mismatches if the script runs across midnight).

This issue also appears on line 58 of the same file.

Copilot uses AI. Check for mistakes.
Comment thread scripts/new-post.ts
Comment on lines +53 to +59
if (fs.existsSync(filepath)) {
console.error(`Error: File already exists: ${filepath}`);
process.exit(1);
}

const content = buildFrontmatter(title, new Date(), draft);
fs.writeFileSync(filepath, content, 'utf-8');
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existsSync pre-check is not atomic with the subsequent write, so the file could still be overwritten if it appears between the check and the write. Prefer an atomic create (e.g., open/write with 'wx') and handle the EEXIST error.

Suggested change
if (fs.existsSync(filepath)) {
console.error(`Error: File already exists: ${filepath}`);
process.exit(1);
}
const content = buildFrontmatter(title, new Date(), draft);
fs.writeFileSync(filepath, content, 'utf-8');
const content = buildFrontmatter(title, new Date(), draft);
try {
fs.writeFileSync(filepath, content, { encoding: 'utf-8', flag: 'wx' });
} catch (error: unknown) {
if (
typeof error === 'object' &&
error !== null &&
'code' in error &&
error.code === 'EEXIST'
) {
console.error(`Error: File already exists: ${filepath}`);
process.exit(1);
}
throw error;
}

Copilot uses AI. Check for mistakes.
@mahata mahata merged commit 3aecf94 into main Apr 7, 2026
5 checks passed
@mahata mahata deleted the add-new-post-script branch April 7, 2026 13:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants