diff --git a/app/Header.tsx b/app/Header.tsx index 9550284..5e4e11c 100644 --- a/app/Header.tsx +++ b/app/Header.tsx @@ -1,7 +1,7 @@ 'use client' import { usePathname } from 'next/navigation' -import Link from '../src/components/Link' +import Link from './components/Link' export default function Header() { const pathname = usePathname() diff --git a/app/becoming-an-independent-consultant/article.tsx b/app/becoming-an-independent-consultant/article.tsx index c1e21b9..bbc5a90 100644 --- a/app/becoming-an-independent-consultant/article.tsx +++ b/app/becoming-an-independent-consultant/article.tsx @@ -1,12 +1,6 @@ -import Image from '../../src/components/image/Image' -import { - H2, - H3, - P, - Ul, - UlReferences, -} from '../../src/components/article/Common' -import Link from '../../src/components/Link' +import Image from '../components/image/Image' +import { H2, H3, P, Ul, UlReferences } from '../components/article/Common' +import Link from '../components/Link' import independent from './independent.jpg' import consultantVsContractors from './consultant-vs-contractors.jpg' diff --git a/app/becoming-an-independent-consultant/page.tsx b/app/becoming-an-independent-consultant/page.tsx index ce23a34..fb071a4 100644 --- a/app/becoming-an-independent-consultant/page.tsx +++ b/app/becoming-an-independent-consultant/page.tsx @@ -1,6 +1,6 @@ import { article } from './article' import { Metadata } from 'next' -import { Article } from '../../src/components/article/Article' +import { Article } from '../components/article/Article' export const metadata: Metadata = { title: article.title, diff --git a/app/being-considerate/article.tsx b/app/being-considerate/article.tsx index 6cacdcd..ac7555e 100644 --- a/app/being-considerate/article.tsx +++ b/app/being-considerate/article.tsx @@ -1,5 +1,5 @@ -import { H2, P } from '../../src/components/article/Common' -import Image from '../../src/components/image/Image' +import { H2, P } from '../components/article/Common' +import Image from '../components/image/Image' import tBarLift from './t-bar-lift.jpg' import tBarLiftLarge from './t-bar-lift-large.jpg' diff --git a/app/being-considerate/page.tsx b/app/being-considerate/page.tsx index ce23a34..fb071a4 100644 --- a/app/being-considerate/page.tsx +++ b/app/being-considerate/page.tsx @@ -1,6 +1,6 @@ import { article } from './article' import { Metadata } from 'next' -import { Article } from '../../src/components/article/Article' +import { Article } from '../components/article/Article' export const metadata: Metadata = { title: article.title, diff --git a/app/cargo-culting-in-software/article.tsx b/app/cargo-culting-in-software/article.tsx index 9f44b27..2376df4 100644 --- a/app/cargo-culting-in-software/article.tsx +++ b/app/cargo-culting-in-software/article.tsx @@ -1,6 +1,6 @@ -import Image from '../../src/components/image/Image' -import { H2, H3, P, UlReferences } from '../../src/components/article/Common' -import Link from '../../src/components/Link' +import Image from '../components/image/Image' +import { H2, H3, P, UlReferences } from '../components/article/Common' +import Link from '../components/Link' import vanuatuJohnFrumDay from './vanuatu-john-frum-day.jpg' import microservices from './microservices.png' diff --git a/app/cargo-culting-in-software/page.tsx b/app/cargo-culting-in-software/page.tsx index ce23a34..fb071a4 100644 --- a/app/cargo-culting-in-software/page.tsx +++ b/app/cargo-culting-in-software/page.tsx @@ -1,6 +1,6 @@ import { article } from './article' import { Metadata } from 'next' -import { Article } from '../../src/components/article/Article' +import { Article } from '../components/article/Article' export const metadata: Metadata = { title: article.title, diff --git a/src/components/Link.tsx b/app/components/Link.tsx similarity index 100% rename from src/components/Link.tsx rename to app/components/Link.tsx diff --git a/src/components/article/Article.tsx b/app/components/article/Article.tsx similarity index 100% rename from src/components/article/Article.tsx rename to app/components/article/Article.tsx index 6308b3d..4bdfc4a 100644 --- a/src/components/article/Article.tsx +++ b/app/components/article/Article.tsx @@ -1,8 +1,8 @@ import { StaticImageData } from 'next/image' import { ReactNode } from 'react' import { H1 } from './Common' -import { unixTimestampToMonthYear } from '../../utils' import InPageNavigation from './InPageNavigation' +import { unixTimestampToMonthYear } from '../../utils' type Props = { thumbnail: StaticImageData diff --git a/src/components/article/Caption.tsx b/app/components/article/Caption.tsx similarity index 100% rename from src/components/article/Caption.tsx rename to app/components/article/Caption.tsx diff --git a/src/components/article/Code.tsx b/app/components/article/Code.tsx similarity index 100% rename from src/components/article/Code.tsx rename to app/components/article/Code.tsx diff --git a/src/components/article/Common.tsx b/app/components/article/Common.tsx similarity index 99% rename from src/components/article/Common.tsx rename to app/components/article/Common.tsx index 6ca082a..2b89ba1 100644 --- a/src/components/article/Common.tsx +++ b/app/components/article/Common.tsx @@ -1,4 +1,5 @@ import { ReactNode } from 'react' + import { getSlug } from '../../utils' type PropsString = { diff --git a/src/components/article/InPageNavigation.tsx b/app/components/article/InPageNavigation.tsx similarity index 100% rename from src/components/article/InPageNavigation.tsx rename to app/components/article/InPageNavigation.tsx index b3f670b..7edf4f3 100644 --- a/src/components/article/InPageNavigation.tsx +++ b/app/components/article/InPageNavigation.tsx @@ -2,8 +2,8 @@ import React, { useState, useEffect } from 'react' import debounce from 'lodash.debounce' -import { classNames, getSlug } from '../../utils' import Link from '../Link' +import { classNames, getSlug } from '../../utils' /* some hash links, when clicked, result in 0 < getBoundingClientRect().top < 1 diff --git a/src/components/footer/Footer.tsx b/app/components/footer/Footer.tsx similarity index 100% rename from src/components/footer/Footer.tsx rename to app/components/footer/Footer.tsx diff --git a/src/components/footer/johan-li.jpg b/app/components/footer/johan-li.jpg similarity index 100% rename from src/components/footer/johan-li.jpg rename to app/components/footer/johan-li.jpg diff --git a/src/components/image/Image.tsx b/app/components/image/Image.tsx similarity index 100% rename from src/components/image/Image.tsx rename to app/components/image/Image.tsx index 0653831..e01b8ea 100644 --- a/src/components/image/Image.tsx +++ b/app/components/image/Image.tsx @@ -3,8 +3,8 @@ import { useEffect, useState } from 'react' import NextImage, { StaticImageData } from 'next/image' import calculateZoom from './calculateZoom' -import { classNames } from '../../utils' import { Caption } from '../article/Caption' +import { classNames } from '../../utils' /* If zoomSrc is provided: diff --git a/src/components/image/ImageFloat.tsx b/app/components/image/ImageFloat.tsx similarity index 100% rename from src/components/image/ImageFloat.tsx rename to app/components/image/ImageFloat.tsx index 394d1ce..8d348c0 100644 --- a/src/components/image/ImageFloat.tsx +++ b/app/components/image/ImageFloat.tsx @@ -1,6 +1,6 @@ import NextImage, { StaticImageData } from 'next/image' -import { classNames } from '../../utils' import { Caption } from '../article/Caption' +import { classNames } from '../../utils' type Props = { data: StaticImageData diff --git a/src/components/image/calculateZoom.test.ts b/app/components/image/calculateZoom.test.ts similarity index 100% rename from src/components/image/calculateZoom.test.ts rename to app/components/image/calculateZoom.test.ts diff --git a/src/components/image/calculateZoom.ts b/app/components/image/calculateZoom.ts similarity index 100% rename from src/components/image/calculateZoom.ts rename to app/components/image/calculateZoom.ts diff --git a/app/getArticles.ts b/app/getArticles.ts new file mode 100644 index 0000000..327e33c --- /dev/null +++ b/app/getArticles.ts @@ -0,0 +1,41 @@ +import { readdir, stat } from 'fs/promises' +import path from 'path' + +const ARTICLES_DIRECTORY = path.join(process.cwd(), 'app') + +export async function getArticles() { + const slugs: string[] = [] + + async function readDirectory(dir) { + const entries = await readdir(dir) + + for (const entry of entries) { + const entryPath = path.join(dir, entry) + + if (!(await stat(entryPath)).isDirectory()) { + continue + } + + try { + await stat(path.join(entryPath, 'article.tsx')) + } catch (e) { + continue + } + + slugs.push(entry) + } + } + + await readDirectory(ARTICLES_DIRECTORY) + + const articles = await Promise.all( + slugs.map((slug) => + import(`./${slug}/article`).then((m) => ({ + ...m.article, + slug, + })), + ), + ) + + return articles.sort((a, b) => b.published - a.published) +} diff --git a/app/how-not-to-design-an-sdk/article.tsx b/app/how-not-to-design-an-sdk/article.tsx index 3b1c459..e1ac419 100644 --- a/app/how-not-to-design-an-sdk/article.tsx +++ b/app/how-not-to-design-an-sdk/article.tsx @@ -1,4 +1,4 @@ -import Image from '../../src/components/image/Image' +import Image from '../components/image/Image' import { CodeInline, H2, @@ -6,9 +6,9 @@ import { P, BlockQuote, UlReferences, -} from '../../src/components/article/Common' -import Link from '../../src/components/Link' -import Code from '../../src/components/article/Code' +} from '../components/article/Common' +import Link from '../components/Link' +import Code from '../components/article/Code' import feelsBadMan from './feels-bad-man.png' import npmInstallStart from './npm-install-start.png' @@ -16,7 +16,7 @@ import excessiveInformation from './excessive-information.png' import excessiveInformationLarge from './excessive-information-large.png' import objectOrientedProgrammer from './object-oriented-programmer.png' import objectOrientedProgrammerSmall from './object-oriented-programmer-small.png' -import ImageFloat from '../../src/components/image/ImageFloat' +import ImageFloat from '../components/image/ImageFloat' const headings = [ 'Azure Blob Storage', diff --git a/app/how-not-to-design-an-sdk/page.tsx b/app/how-not-to-design-an-sdk/page.tsx index ce23a34..fb071a4 100644 --- a/app/how-not-to-design-an-sdk/page.tsx +++ b/app/how-not-to-design-an-sdk/page.tsx @@ -1,6 +1,6 @@ import { article } from './article' import { Metadata } from 'next' -import { Article } from '../../src/components/article/Article' +import { Article } from '../components/article/Article' export const metadata: Metadata = { title: article.title, diff --git a/app/layout.tsx b/app/layout.tsx index 60acaea..1eb59b2 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,7 +1,7 @@ import React, { ReactNode } from 'react' import { Roboto_Flex } from 'next/font/google' import { Metadata } from 'next' -import Footer from '../src/components/footer/Footer' +import Footer from './components/footer/Footer' import Header from './Header' import Script from 'next/script' diff --git a/app/page.tsx b/app/page.tsx index 8089ba7..7077a7e 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,7 +1,7 @@ -import Link from '../src/components/Link' +import Link from './components/Link' import NextImage from 'next/image' -import { unixTimestampToMonthYear } from '../src/utils' -import { getArticles } from './utils' +import { unixTimestampToMonthYear } from './utils' +import { getArticles } from './getArticles' export default async function Home() { const articles = await getArticles() diff --git a/app/sql-injection-prevention/article.tsx b/app/sql-injection-prevention/article.tsx index 2fd8733..f52badc 100644 --- a/app/sql-injection-prevention/article.tsx +++ b/app/sql-injection-prevention/article.tsx @@ -1,6 +1,6 @@ -import Image from '../../src/components/image/Image' -import Code from '../../src/components/article/Code' -import { H2, P } from '../../src/components/article/Common' +import Image from '../components/image/Image' +import Code from '../components/article/Code' +import { H2, P } from '../components/article/Common' import postgresqlDataInsertion from './postgresql-data-insertion.png' diff --git a/app/sql-injection-prevention/page.tsx b/app/sql-injection-prevention/page.tsx index ce23a34..fb071a4 100644 --- a/app/sql-injection-prevention/page.tsx +++ b/app/sql-injection-prevention/page.tsx @@ -1,6 +1,6 @@ import { article } from './article' import { Metadata } from 'next' -import { Article } from '../../src/components/article/Article' +import { Article } from '../components/article/Article' export const metadata: Metadata = { title: article.title, diff --git a/app/the-rise-of-javascript-frameworks/article.tsx b/app/the-rise-of-javascript-frameworks/article.tsx index 0fc73ba..146f7db 100644 --- a/app/the-rise-of-javascript-frameworks/article.tsx +++ b/app/the-rise-of-javascript-frameworks/article.tsx @@ -1,6 +1,6 @@ -import { H2, P } from '../../src/components/article/Common' -import Image from '../../src/components/image/Image' -import Code from '../../src/components/article/Code' +import { H2, P } from '../components/article/Common' +import Image from '../components/image/Image' +import Code from '../components/article/Code' import angularReactVueTrends from './angular-react-vue-trends.png' import barebonesMessagingApp from './barebones-messaging-app.png' diff --git a/app/the-rise-of-javascript-frameworks/page.tsx b/app/the-rise-of-javascript-frameworks/page.tsx index ce23a34..fb071a4 100644 --- a/app/the-rise-of-javascript-frameworks/page.tsx +++ b/app/the-rise-of-javascript-frameworks/page.tsx @@ -1,6 +1,6 @@ import { article } from './article' import { Metadata } from 'next' -import { Article } from '../../src/components/article/Article' +import { Article } from '../components/article/Article' export const metadata: Metadata = { title: article.title, diff --git a/app/the-store-that-kept-on-giving/article.tsx b/app/the-store-that-kept-on-giving/article.tsx index b5f9ca0..b10926b 100644 --- a/app/the-store-that-kept-on-giving/article.tsx +++ b/app/the-store-that-kept-on-giving/article.tsx @@ -1,8 +1,8 @@ -import { CodeInline, H2, P, Ul } from '../../src/components/article/Common' -import Code from '../../src/components/article/Code' +import { CodeInline, H2, P, Ul } from '../components/article/Common' +import Code from '../components/article/Code' import oprahMeme from './oprah-meme.jpg' import sfccCartoon from './salesforce-commerce-cloud-cartoon.png' -import ImageFloat from '../../src/components/image/ImageFloat' +import ImageFloat from '../components/image/ImageFloat' const headings = [ 'A shiny brand-new store', diff --git a/app/the-store-that-kept-on-giving/page.tsx b/app/the-store-that-kept-on-giving/page.tsx index ce23a34..fb071a4 100644 --- a/app/the-store-that-kept-on-giving/page.tsx +++ b/app/the-store-that-kept-on-giving/page.tsx @@ -1,6 +1,6 @@ import { article } from './article' import { Metadata } from 'next' -import { Article } from '../../src/components/article/Article' +import { Article } from '../components/article/Article' export const metadata: Metadata = { title: article.title, diff --git a/src/utils.test.ts b/app/utils.test.ts similarity index 100% rename from src/utils.test.ts rename to app/utils.test.ts diff --git a/app/utils.ts b/app/utils.ts index 2bc6ebd..5dd1453 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -1,33 +1,18 @@ -import { readdir, stat } from 'fs/promises' -import path from 'path' - -const ARTICLES_DIRECTORY = path.join(process.cwd(), 'app') - -export async function getArticles() { - const slugs: string[] = [] - - async function readDirectory(dir) { - const entries = await readdir(dir) - - for (const entry of entries) { - const entryPath = path.join(dir, entry) - - if ((await stat(entryPath)).isDirectory()) { - slugs.push(entry) - } - } - } +export function classNames(...classes: string[]) { + return classes.filter(Boolean).join(' ') +} - await readDirectory(ARTICLES_DIRECTORY) +export function unixTimestampToMonthYear(unixTimestamp: number) { + const date = new Date(unixTimestamp * 1000) + const month = new Intl.DateTimeFormat('en-US', { month: 'long' }).format(date) + const year = date.getFullYear() - const articles = await Promise.all( - slugs.map((slug) => - import(`./${slug}/article`).then((m) => ({ - ...m.article, - slug, - })), - ), - ) + return `${month}, ${year}` +} - return articles.sort((a, b) => b.published - a.published) +export function getSlug(text: string) { + return text + .toLowerCase() + .replace(/\s+/g, '-') + .replace(/[^0-9a-z\-]/gi, '') } diff --git a/scripts/importFromGoogleDocs.ts b/scripts/importFromGoogleDocs.ts index 7b5770c..5686f8f 100644 --- a/scripts/importFromGoogleDocs.ts +++ b/scripts/importFromGoogleDocs.ts @@ -1,7 +1,8 @@ import { docs, auth } from '@googleapis/docs' -import { writeFile } from 'fs/promises' +import { writeFile, readFile, mkdir } from 'fs/promises' import childProcess from 'child_process' import util from 'util' +import { getSlug } from '../app/utils' const exec = util.promisify(childProcess.exec) @@ -40,8 +41,6 @@ async function main() { const { title, body } = data - const filename = `${title.replace(/\s/g, '-').toLowerCase()}.tsx` - const headings = [] const headingLevels = new Set() const elements = [] @@ -70,26 +69,26 @@ async function main() { elements.push(`

${content}

`) }) - const output = ` + const articleOutput = ` ${ headingLevels.size > 0 && `import { ${Array.from(headingLevels).join( ', ', - )} } from '../src/components/article/Common';` + )} } from '../components/article/Common';` } - import { P } from '../src/components/article/Common'; + import { P } from '../components/article/Common'; const headings = [ ${headings.map((heading) => `'${heading}',`).join('\n')} ] - const body = () => ( + const body = ( <> ${elements.join('\n')} ) - const article = { + export const article = { thumbnail: '', title: '${title}', teaser: '', @@ -98,15 +97,21 @@ async function main() { headings, body, } - - export default article ` - const filepath = `${__dirname}/${filename}` + const slug = getSlug(title) + + await mkdir(`${__dirname}/../app/${slug}`, { recursive: true }) - await writeFile(filepath, output) + const articleFilePath = `${__dirname}/../app/${slug}/article.tsx` + await writeFile(articleFilePath, articleOutput) + await exec(`prettier ${articleFilePath} --write`) - await exec(`prettier ${filepath} --write`) + const pageOutput = await readFile( + `${__dirname}/../app/cargo-culting-in-software/page.tsx`, + 'utf8', + ) + await writeFile(`${__dirname}/../app/${slug}/page.tsx`, pageOutput) process.exit() } diff --git a/src/utils.ts b/src/utils.ts deleted file mode 100644 index 5dd1453..0000000 --- a/src/utils.ts +++ /dev/null @@ -1,18 +0,0 @@ -export function classNames(...classes: string[]) { - return classes.filter(Boolean).join(' ') -} - -export function unixTimestampToMonthYear(unixTimestamp: number) { - const date = new Date(unixTimestamp * 1000) - const month = new Intl.DateTimeFormat('en-US', { month: 'long' }).format(date) - const year = date.getFullYear() - - return `${month}, ${year}` -} - -export function getSlug(text: string) { - return text - .toLowerCase() - .replace(/\s+/g, '-') - .replace(/[^0-9a-z\-]/gi, '') -} diff --git a/tailwind.config.js b/tailwind.config.js index c0be57d..6391385 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,6 +1,6 @@ /** @type {import('tailwindcss').Config} */ module.exports = { - content: ['./app/**/*.{ts,tsx}', './src/**/*.{tsx,ts}'], + content: ['./app/**/*.{ts,tsx}'], theme: { extend: {}, },