From 7d64e3ad0742093f7603bc2b216cd0a0fd818d86 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Sat, 15 Jul 2023 12:19:34 +0100 Subject: [PATCH] refactor: move dev.to sync integration into module (#1242) --- .github/workflows/sync.yml | 3 +- nuxt.config.ts | 4 ++ scripts/publish.mjs | 99 ---------------------------- src/modules/dev-to.ts | 105 ++++++++++++++++++++++++++++++ src/modules/metadata.ts | 13 ++-- src/modules/shared/serialisers.ts | 7 ++ 6 files changed, 122 insertions(+), 109 deletions(-) delete mode 100644 scripts/publish.mjs create mode 100644 src/modules/dev-to.ts create mode 100644 src/modules/shared/serialisers.ts diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml index b2b4279d..c1d0618e 100644 --- a/.github/workflows/sync.yml +++ b/.github/workflows/sync.yml @@ -24,6 +24,7 @@ jobs: run: pnpm install --frozen-lockfile - name: Update blogs on dev.to - run: node scripts/publish.mjs + run: pnpm nuxi prepare env: DEVTO_TOKEN: ${{ secrets.DEVTO_TOKEN }} + SYNC_DEV_TO: true diff --git a/nuxt.config.ts b/nuxt.config.ts index 03e7fd0f..a1bdb089 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -83,6 +83,10 @@ export default defineNuxtConfig({ sourcemap: false, + devTo: { + enabled: !!process.env.SYNC_DEV_TO, + }, + typescript: { strict: true, tsConfig: { diff --git a/scripts/publish.mjs b/scripts/publish.mjs deleted file mode 100644 index c6bfb77c..00000000 --- a/scripts/publish.mjs +++ /dev/null @@ -1,99 +0,0 @@ -/* eslint-disable camelcase */ - -import fsp from 'node:fs/promises' -import { globby } from 'globby' -import { $fetch } from 'ofetch' - -const token = process.env.DEVTO_TOKEN - -if (!token) { - throw new Error('No token provided.') -} - -const $devto = $fetch.create({ - baseURL: 'https://dev.to/api', - headers: { - 'api-key': token, - }, -}) - -const serializers = new Map([ - [/\(\//g, '(https://roe.dev/'], - [/ ---? /g, ' — '], - [/::SocialPost\{link=([^ ]*)[\s\S]*?::/g, '$1'], - [/:CalSchedule\{meeting=([^ ]*).*\}/g, 'https://cal.com/danielroe/$1'], - [/(\n|^)---\n[\s\S]*\n---\n/, '\n'], -]) - -async function getMarkdownArticles() { - const articles = [] - const files = await globby('./src/content/blog/**/*.md') - for (const file of files) { - let contents = await fsp.readFile(file, 'utf-8') - - if (contents.includes('skip_dev')) continue - for (const item of serializers) { - contents = contents.replace(item[0], item[1]) - } - - const slug = file.match(/\/[^/]*$/)?.[0].slice(1, -3) - articles.push({ - body_markdown: contents, - title: contents.match(/title: (.*)/)?.[1], - slug, - canonical_url: `https://roe.dev/blog/${slug}/`, - }) - } - return articles -} - -function getArticles() { - return $devto('articles/me') -} - -async function postArticle({ title, body_markdown, canonical_url }) { - return await $devto('articles', { - method: 'POST', - body: { - article: { - published: true, - title, - canonical_url, - body_markdown, - }, - }, - }) -} - -async function updateArticle(id, { title, body_markdown, canonical_url }) { - return await $devto(`articles/${id}`, { - method: 'PUT', - body: { - article: { - published: true, - title, - body_markdown, - canonical_url, - }, - }, - }) -} - -async function main() { - const [publishedArticles, localArticles] = await Promise.all([ - getArticles(), - getMarkdownArticles(), - ]) - for (const markdownArticle of localArticles) { - const article = publishedArticles.find( - article => article.canonical_url === markdownArticle.canonical_url - ) - if (article) { - await updateArticle(article.id, markdownArticle) - } else { - await postArticle(markdownArticle) - } - } -} - -main() diff --git a/src/modules/dev-to.ts b/src/modules/dev-to.ts new file mode 100644 index 00000000..61b6406b --- /dev/null +++ b/src/modules/dev-to.ts @@ -0,0 +1,105 @@ +import fsp from 'node:fs/promises' +import { defineNuxtModule, useNuxt } from 'nuxt/kit' +import { globby } from 'globby' +import { $fetch } from 'ofetch' +import { resolve } from 'pathe' +import { serializers } from './shared/serialisers' + +type RawArticle = { + title: string + body_markdown: string + canonical_url: string +} + +export default defineNuxtModule({ + meta: { + name: 'dev-to', + configKey: 'devTo', + }, + defaults: { + enabled: false, + token: process.env.DEVTO_TOKEN, + }, + async setup(options) { + const nuxt = useNuxt() + if (nuxt.options.dev || !options.enabled) { + return + } + + if (!options.token) { + throw new Error('No token provided.') + } + + const $devto = $fetch.create({ + baseURL: 'https://dev.to/api', + headers: { + 'api-key': options.token, + }, + }) + + const [publishedArticles, localArticles] = await Promise.all([ + $devto>('articles/me'), + getMarkdownArticles(), + ]) + for (const markdownArticle of localArticles) { + const article = publishedArticles.find( + article => article.canonical_url === markdownArticle.canonical_url + ) + if (article) { + await $devto(`articles/${article.id}`, { + method: 'PUT', + body: { + article: { + published: true, + title: markdownArticle.title, + body_markdown: markdownArticle.body_markdown, + canonical_url: markdownArticle.canonical_url, + }, + }, + }) + continue + } + + await await $devto('articles', { + method: 'POST', + body: { + article: { + published: true, + title: markdownArticle.title, + canonical_url: markdownArticle.canonical_url, + body_markdown: markdownArticle.body_markdown, + }, + }, + }) + } + }, +}) + +async function getMarkdownArticles() { + const nuxt = useNuxt() + const articles = [] + const files = await globby('./content/blog/**/*.md', { + cwd: nuxt.options.srcDir, + }) + for (const file of files) { + let contents = await fsp.readFile( + resolve(nuxt.options.srcDir, file), + 'utf-8' + ) + + if (contents.includes('skip_dev')) continue + const title = contents.match(/title: (.*)/)![1] + for (const item of serializers) { + contents = contents.replace(item[0], item[1]) + } + + const slug = file.match(/\/[^/]*$/)![0].slice(1, -3) + articles.push({ + body_markdown: contents, + title, + slug, + canonical_url: `https://roe.dev/blog/${slug}/`, + }) + } + return articles +} diff --git a/src/modules/metadata.ts b/src/modules/metadata.ts index 61030baf..18f16845 100644 --- a/src/modules/metadata.ts +++ b/src/modules/metadata.ts @@ -1,18 +1,13 @@ -import { readFile } from 'fs/promises' -import { createResolver, defineNuxtModule, useNuxt } from '@nuxt/kit' +import { readFile } from 'node:fs/promises' +import { createResolver, defineNuxtModule, useNuxt } from 'nuxt/kit' + import { globby } from 'globby' import grayMatter from 'gray-matter' import { filename } from 'pathe/utils' import { remark } from 'remark' import remarkHtml from 'remark-html' -const serializers = new Map([ - [/\(\//g, '(https://roe.dev/'], - [/ ---? /g, ' — '], - [/::SocialPost\{link=([^ ]*)[\s\S]*?::/g, '$1'], - [/:CalSchedule\{meeting=([^ ]*).*\}/g, 'https://cal.com/danielroe/$1'], - [/(\n|^)---\n[\s\S]*\n---\n/, '\n'], -]) +import { serializers } from './shared/serialisers' export default defineNuxtModule({ meta: { diff --git a/src/modules/shared/serialisers.ts b/src/modules/shared/serialisers.ts new file mode 100644 index 00000000..0461fb92 --- /dev/null +++ b/src/modules/shared/serialisers.ts @@ -0,0 +1,7 @@ +export const serializers = new Map([ + [/\(\//g, '(https://roe.dev/'], + [/ ---? /g, ' — '], + [/::SocialPost\{link=([^ ]*)[\s\S]*?::/g, '$1'], + [/:CalSchedule\{meeting=([^ ]*).*\}/g, 'https://cal.com/danielroe/$1'], + [/(\n|^)---\n[\s\S]*\n---\n/, '\n'], +]) -- 2.51.2