diff --git a/scripts/sync-newsletter-to-resend.ts b/scripts/sync-newsletter-to-resend.ts index d2e18002..44455283 100755 --- a/scripts/sync-newsletter-to-resend.ts +++ b/scripts/sync-newsletter-to-resend.ts @@ -71,11 +71,16 @@ function parseNewsletterDate(dateStr: string | number | Date): Date { /** * Checks if a newsletter should be processed based on its date. */ -function shouldProcessNewsletter(date: string | number | Date, slug: string): boolean { +function shouldProcessNewsletter( + date: string | number | Date, + slug: string +): boolean { try { const newsletterDate = parseNewsletterDate(date); // Newsletter date must be on or after the cutoff date - const shouldProcess = isAfter(newsletterDate, CUTOFF_DATE) || isSameDay(newsletterDate, CUTOFF_DATE); + const shouldProcess = + isAfter(newsletterDate, CUTOFF_DATE) || + isSameDay(newsletterDate, CUTOFF_DATE); if (!shouldProcess) { console.log(`⏭️ Skipping ${slug}: dated before cutoff (${date})`); @@ -97,11 +102,15 @@ function convertImagesToHtml(content: string): string { const imageRegex = /]*\/>/g; return content.replace(imageRegex, (_, publicId) => { - const cloudinaryUrl = `https://res.cloudinary.com/mikebifulco-com/image/upload/${publicId}`; + const cloudinaryUrl = getCloudinaryImageUrl(publicId); return `![](${cloudinaryUrl})`; }); } +const getCloudinaryImageUrl = (publicId: string) => { + return `https://res.cloudinary.com/mikebifulco-com/image/upload/${publicId}`; +}; + /** * Gets list of changed newsletter files in current PR. */ @@ -181,6 +190,9 @@ async function main() { React.createElement(NewsletterEmail, { content: cleanContent, excerpt: frontmatter.excerpt, + coverImage: frontmatter.coverImagePublicId + ? getCloudinaryImageUrl(frontmatter.coverImagePublicId) + : undefined, }) ); diff --git a/src/data/newsletters/open-source-is-community.mdx b/src/data/newsletters/open-source-is-community.mdx new file mode 100644 index 00000000..57ee5986 --- /dev/null +++ b/src/data/newsletters/open-source-is-community.mdx @@ -0,0 +1,66 @@ +--- +title: "Contributing to Open Source without being a Jerk" +excerpt: "Open source doesn't work without good faith - and sometimes you need to patch a dependency to do your part." +tags: [open source, javascript, react, devtools] +date: 11-11-2025 +coverImagePublicId: "newsletters/open-source-is-community/cover" +slug: open-source-is-community +--- + +## The Big Idea + +Open source works best when we treat each other like humans - not vending machines for bug fixes. + +## How We Patched a Broken Package - and What It Taught Us + +Recently at [Craftwork](https://craftwork.com), we hit a snag using the [@payloadcms/storage-uploadthing](https://github.com/payloadcms/storage-uploadthing) plugin for image uploads in [Payload CMS](https://payloadcms.com). + +One of our engineers dug in, found the issue, and fixed it. We opened a [pull request](https://github.com/payloadcms/payload/pull/14250), hoping to help others in the same situation. + +But here's the thing: We couldn't afford to wait for the PR to get merged before moving forward. Publishing a forked npm package is a common fallback - but it can be hard to maintain long-term. + +In addition to creating a fork to submit a fix, we used [`pnpm patch`](https://mikebifulco.com/posts/patching-npm-dependencies-with-pnpm-patch) to apply the fix to our repo. It's clean, version-controlled, and works until the upstream package is updated. + + +## Maintainers Aren't the Problem + +It's easy to forget that open source projects are often maintained by small teams under heavy load. At the time of writing, Payload has over 375 open pull requests. Expecting our fix to jump the line would be absurd. + +This isn't about neglect or indifference. It's about capacity. + +In other repos, we've seen PRs sit untouched for months, with long threads of angsty, impatient comments. I get it - but that doesn't help anyone. Especially not the maintainers. + + +### A Better Way to Contribute + +Here's what we did instead: + +- 🛠 Fixed the issue locally using `pnpm patch` (need a tutorial? I [gotchu](https://mikebifulco.com/posts/patching-npm-dependencies-with-pnpm-patch)) +- 💬 Shared the patch tutorial in the GitHub PR to unblock others +- 🔁 Opened a new, cleaner PR +- 🤝 Let the maintainers off the hook - no pressure, just help + +This approach makes open source better for _everyone_. It gives control back to contributors, and it gives maintainers room to breathe. + +It is easy to forget that contributing to open source isn't just Pull Requests - discussion, community building, managing expectations, and creative solutions help us all build a better internet together. + +I'm confident you'll get more results if you treat people with humanity - and you will [build a reputation for yourself](https://mikebifulco.com/newsletter/serendipity-isnt-an-accident) that makes people eager to help you when they can. + +--- + +## Want to Do Open Source Better? + +Read [*Working in Public*](https://hardcover.app/books/working-in-public) by Nadia Eghbal. It's the best book I've read on the culture of open source: what's broken, what's beautiful, and what's worth fixing. + +Also, if you like learning by watching smart people build in public, check out: + +- [cmgriffing](https://www.twitch.tv/cmgriffing) streams dev work and product experiments on both Twitch and YouTub +- [Rizel Scarlett](https://www.twitch.tv/blackgirlbytes1) does open source work, conference prep, and interviews on her channel + +--- + +## Give without the expectation of receiving + +Open source is a special kind of economy. Show up with something useful. Share what you've learned. Make it easier for the next person. + +Let's be better guests in each other's repos. diff --git a/src/utils/email/templates/EmailLayout.tsx b/src/utils/email/templates/EmailLayout.tsx index bc846a29..bb71b82e 100644 --- a/src/utils/email/templates/EmailLayout.tsx +++ b/src/utils/email/templates/EmailLayout.tsx @@ -35,24 +35,27 @@ export const EmailLayout = ({ {preview} - + {/* Logo Section */}
@@ -94,12 +97,7 @@ export const EmailLayout = ({ }} className="mt-2 text-gray-500" > - + © {new Date().getFullYear()} • 💌 Tiny Improvements •{' '} {' '} {includeUnsubscribeLink && ( - + Not getting what you need? No worries, you can{' '} unsubscribe @@ -139,7 +137,7 @@ const content = { border: '1px solid rgb(0,0,0, 0.1)', borderRadius: '3px', overflow: 'hidden', - maxWidth: '500px', + maxWidth: '630px', backgroundColor: '#fff', paddingTop: '8px', paddingBottom: '8px', diff --git a/src/utils/email/templates/NewsletterEmail.tsx b/src/utils/email/templates/NewsletterEmail.tsx index d505766c..5355f53a 100644 --- a/src/utils/email/templates/NewsletterEmail.tsx +++ b/src/utils/email/templates/NewsletterEmail.tsx @@ -1,11 +1,18 @@ -import * as React from 'react'; -import { Column, Markdown, Row } from '@react-email/components'; +import { + Column, + Img, + Link, + Markdown, + Row, + Text, +} from '@react-email/components'; import { EmailLayout } from './EmailLayout'; type NewsletterEmailProps = { content: string; excerpt: string; + coverImage?: string; }; /** @@ -28,8 +35,9 @@ type NewsletterEmailProps = { * ``` */ export const NewsletterEmail = ({ - content = '', + content = 'lorem ipsum dolor sit amet this is just sample content for email preview', excerpt = 'Preview text for email clients', + coverImage = 'https://picsum.photos/1200/630', }: NewsletterEmailProps) => { return ( + {coverImage && ( + + + Newsletter cover image + + + )} {content} + + + + Give 'em hell out there. ✌️
- Mike +
+
+
+ + + + Thanks for reading 💌 Tiny Improvements. If you found this helpful, + I'd love it if you{' '} + + share it with a friend + + . It helps me out a great deal! + + +
); };