You've already forked AstralRinth
forked from didirus/AstralRinth
refactor: migrate to common eslint+prettier configs (#4168)
* refactor: migrate to common eslint+prettier configs * fix: prettier frontend * feat: config changes * fix: lint issues * fix: lint * fix: type imports * fix: cyclical import issue * fix: lockfile * fix: missing dep * fix: switch to tabs * fix: continue switch to tabs * fix: rustfmt parity * fix: moderation lint issue * fix: lint issues * fix: ui intl * fix: lint issues * Revert "fix: rustfmt parity" This reverts commit cb99d2376c321d813d4b7fc7e2a213bb30a54711. * feat: revert last rs
This commit is contained in:
@@ -1,7 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ['custom/library'],
|
||||
env: {
|
||||
node: true,
|
||||
},
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as path from 'path'
|
||||
|
||||
import { repoPath } from './utils'
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,85 +1,85 @@
|
||||
import { promises as fs } from 'fs'
|
||||
import * as path from 'path'
|
||||
import { repoPath, toVarName } from './utils'
|
||||
import { glob } from 'glob'
|
||||
import * as path from 'path'
|
||||
|
||||
import { PUBLIC_SRC, PUBLIC_LOCATIONS, ARTICLES_GLOB, COMPILED_DIR } from './blog.config'
|
||||
import { ARTICLES_GLOB, COMPILED_DIR, PUBLIC_LOCATIONS, PUBLIC_SRC } from './blog.config'
|
||||
import { repoPath, toVarName } from './utils'
|
||||
|
||||
async function checkPublicAssets() {
|
||||
const srcFiles = await glob('**/*', { cwd: PUBLIC_SRC, dot: true })
|
||||
let allOk = true
|
||||
for (const target of PUBLIC_LOCATIONS) {
|
||||
for (const relativeFile of srcFiles) {
|
||||
const shouldExist = path.posix.join(target, relativeFile)
|
||||
try {
|
||||
await fs.access(shouldExist)
|
||||
} catch {
|
||||
console.error(`⚠️ Missing public asset: ${shouldExist}`)
|
||||
allOk = false
|
||||
}
|
||||
}
|
||||
if (allOk) {
|
||||
console.log(`✅ All public assets exist in: ${target}`)
|
||||
}
|
||||
}
|
||||
if (!allOk) process.exit(1)
|
||||
const srcFiles = await glob('**/*', { cwd: PUBLIC_SRC, dot: true })
|
||||
let allOk = true
|
||||
for (const target of PUBLIC_LOCATIONS) {
|
||||
for (const relativeFile of srcFiles) {
|
||||
const shouldExist = path.posix.join(target, relativeFile)
|
||||
try {
|
||||
await fs.access(shouldExist)
|
||||
} catch {
|
||||
console.error(`⚠️ Missing public asset: ${shouldExist}`)
|
||||
allOk = false
|
||||
}
|
||||
}
|
||||
if (allOk) {
|
||||
console.log(`✅ All public assets exist in: ${target}`)
|
||||
}
|
||||
}
|
||||
if (!allOk) process.exit(1)
|
||||
}
|
||||
|
||||
async function checkCompiledArticles() {
|
||||
const mdFiles = await glob(ARTICLES_GLOB)
|
||||
const compiledFiles = await glob(`${COMPILED_DIR}/*.ts`)
|
||||
const compiledVarNames = compiledFiles.map((f) => path.basename(f, '.ts'))
|
||||
const mdFiles = await glob(ARTICLES_GLOB)
|
||||
const compiledFiles = await glob(`${COMPILED_DIR}/*.ts`)
|
||||
const compiledVarNames = compiledFiles.map((f) => path.basename(f, '.ts'))
|
||||
|
||||
// Check all .md have compiled .ts and .content.ts and the proper public thumbnail
|
||||
for (const file of mdFiles) {
|
||||
const varName = toVarName(path.basename(file, '.md'))
|
||||
const compiledPath = path.posix.join(COMPILED_DIR, varName + '.ts')
|
||||
const contentPath = path.posix.join(COMPILED_DIR, varName + '.content.ts')
|
||||
if (!compiledVarNames.includes(varName)) {
|
||||
console.error(`⚠️ Missing compiled article for: ${file} (should be: ${compiledPath})`)
|
||||
process.exit(1)
|
||||
}
|
||||
try {
|
||||
await fs.access(compiledPath)
|
||||
} catch {
|
||||
console.error(`⚠️ Compiled article file not found: ${compiledPath}`)
|
||||
process.exit(1)
|
||||
}
|
||||
try {
|
||||
await fs.access(contentPath)
|
||||
} catch {
|
||||
console.error(`⚠️ Compiled article content file not found: ${contentPath}`)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
// Check all .md have compiled .ts and .content.ts and the proper public thumbnail
|
||||
for (const file of mdFiles) {
|
||||
const varName = toVarName(path.basename(file, '.md'))
|
||||
const compiledPath = path.posix.join(COMPILED_DIR, varName + '.ts')
|
||||
const contentPath = path.posix.join(COMPILED_DIR, varName + '.content.ts')
|
||||
if (!compiledVarNames.includes(varName)) {
|
||||
console.error(`⚠️ Missing compiled article for: ${file} (should be: ${compiledPath})`)
|
||||
process.exit(1)
|
||||
}
|
||||
try {
|
||||
await fs.access(compiledPath)
|
||||
} catch {
|
||||
console.error(`⚠️ Compiled article file not found: ${compiledPath}`)
|
||||
process.exit(1)
|
||||
}
|
||||
try {
|
||||
await fs.access(contentPath)
|
||||
} catch {
|
||||
console.error(`⚠️ Compiled article content file not found: ${contentPath}`)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Check compiled .ts still have corresponding .md
|
||||
for (const compiled of compiledFiles) {
|
||||
const varName = path.basename(compiled, '.ts')
|
||||
if (varName === 'index' || varName.endsWith('.content')) continue
|
||||
// Check compiled .ts still have corresponding .md
|
||||
for (const compiled of compiledFiles) {
|
||||
const varName = path.basename(compiled, '.ts')
|
||||
if (varName === 'index' || varName.endsWith('.content')) continue
|
||||
|
||||
const mdPathGlob = repoPath(`packages/blog/articles/**/${varName.replace(/_/g, '*')}.md`)
|
||||
const found = await glob(mdPathGlob)
|
||||
if (!found.length) {
|
||||
console.error(`❌ Compiled article ${compiled} has no matching markdown source!`)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
const mdPathGlob = repoPath(`packages/blog/articles/**/${varName.replace(/_/g, '*')}.md`)
|
||||
const found = await glob(mdPathGlob)
|
||||
if (!found.length) {
|
||||
console.error(`❌ Compiled article ${compiled} has no matching markdown source!`)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
console.log(
|
||||
'🎉 All articles are correctly compiled, matched, and have thumbnails (if declared)!',
|
||||
)
|
||||
console.log(
|
||||
'🎉 All articles are correctly compiled, matched, and have thumbnails (if declared)!',
|
||||
)
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('🔎 Checking public assets...')
|
||||
await checkPublicAssets()
|
||||
console.log('🔎 Checking public assets...')
|
||||
await checkPublicAssets()
|
||||
|
||||
console.log('🔎 Checking compiled articles...')
|
||||
await checkCompiledArticles()
|
||||
console.log('🔎 Checking compiled articles...')
|
||||
await checkCompiledArticles()
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
console.error('❌ Error in check.ts:', e)
|
||||
process.exit(1)
|
||||
console.error('❌ Error in check.ts:', e)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
@@ -1,91 +1,91 @@
|
||||
import { promises as fs } from 'fs'
|
||||
import * as path from 'path'
|
||||
import matter from 'gray-matter'
|
||||
import { md } from '@modrinth/utils'
|
||||
import { promises as fs } from 'fs'
|
||||
import { glob } from 'glob'
|
||||
import matter from 'gray-matter'
|
||||
import { minify } from 'html-minifier-terser'
|
||||
import { copyDir, toVarName } from './utils'
|
||||
import * as path from 'path'
|
||||
import RSS from 'rss'
|
||||
import { parseStringPromise } from 'xml2js'
|
||||
import { glob } from 'glob'
|
||||
|
||||
import {
|
||||
ARTICLES_GLOB,
|
||||
COMPILED_DIR,
|
||||
ROOT_FILE,
|
||||
PUBLIC_SRC,
|
||||
PUBLIC_LOCATIONS,
|
||||
RSS_PATH,
|
||||
JSON_PATH,
|
||||
SITE_URL,
|
||||
ARTICLES_GLOB,
|
||||
COMPILED_DIR,
|
||||
JSON_PATH,
|
||||
PUBLIC_LOCATIONS,
|
||||
PUBLIC_SRC,
|
||||
ROOT_FILE,
|
||||
RSS_PATH,
|
||||
SITE_URL,
|
||||
} from './blog.config'
|
||||
import { copyDir, toVarName } from './utils'
|
||||
|
||||
async function ensureCompiledDir() {
|
||||
await fs.mkdir(COMPILED_DIR, { recursive: true })
|
||||
await fs.mkdir(COMPILED_DIR, { recursive: true })
|
||||
}
|
||||
|
||||
async function hasThumbnail(slug: string): Promise<boolean> {
|
||||
const thumbnailPath = path.posix.join(PUBLIC_SRC, slug, 'thumbnail.webp')
|
||||
try {
|
||||
await fs.access(thumbnailPath)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
const thumbnailPath = path.posix.join(PUBLIC_SRC, slug, 'thumbnail.webp')
|
||||
try {
|
||||
await fs.access(thumbnailPath)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function getArticleLink(slug: string): string {
|
||||
return `${SITE_URL}/news/article/${slug}`
|
||||
return `${SITE_URL}/news/article/${slug}`
|
||||
}
|
||||
|
||||
function getThumbnailUrl(slug: string, hasThumb: boolean): string {
|
||||
if (hasThumb) {
|
||||
return `${SITE_URL}/news/article/${slug}/thumbnail.webp`
|
||||
} else {
|
||||
return `${SITE_URL}/news/default.webp`
|
||||
}
|
||||
if (hasThumb) {
|
||||
return `${SITE_URL}/news/article/${slug}/thumbnail.webp`
|
||||
} else {
|
||||
return `${SITE_URL}/news/default.webp`
|
||||
}
|
||||
}
|
||||
|
||||
async function compileArticles() {
|
||||
await ensureCompiledDir()
|
||||
await ensureCompiledDir()
|
||||
|
||||
const files = await glob(ARTICLES_GLOB)
|
||||
console.log(`🔎 Found ${files.length} markdown articles!`)
|
||||
const articleExports: string[] = []
|
||||
const articlesArray: string[] = []
|
||||
const articlesForRss = []
|
||||
const articlesForJson = []
|
||||
const files = await glob(ARTICLES_GLOB)
|
||||
console.log(`🔎 Found ${files.length} markdown articles!`)
|
||||
const articleExports: string[] = []
|
||||
const articlesArray: string[] = []
|
||||
const articlesForRss = []
|
||||
const articlesForJson = []
|
||||
|
||||
for (const file of files) {
|
||||
const src = await fs.readFile(file, 'utf8')
|
||||
const { content, data } = matter(src)
|
||||
for (const file of files) {
|
||||
const src = await fs.readFile(file, 'utf8')
|
||||
const { content, data } = matter(src)
|
||||
|
||||
const { title, summary, date, slug: frontSlug, authors: authorsData, ...rest } = data
|
||||
if (!title || !summary || !date) {
|
||||
console.error(`❌ Missing required frontmatter in ${file}. Required: title, summary, date`)
|
||||
process.exit(1)
|
||||
}
|
||||
const { title, summary, date, slug: frontSlug, authors: authorsData, ...rest } = data
|
||||
if (!title || !summary || !date) {
|
||||
console.error(`❌ Missing required frontmatter in ${file}. Required: title, summary, date`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const html = md().render(content)
|
||||
const minifiedHtml = await minify(html, {
|
||||
collapseWhitespace: true,
|
||||
removeComments: true,
|
||||
})
|
||||
const html = md().render(content)
|
||||
const minifiedHtml = await minify(html, {
|
||||
collapseWhitespace: true,
|
||||
removeComments: true,
|
||||
})
|
||||
|
||||
const authors = authorsData ? authorsData : []
|
||||
const authors = authorsData ? authorsData : []
|
||||
|
||||
const slug = frontSlug || path.basename(file, '.md')
|
||||
const varName = toVarName(slug)
|
||||
const exportFile = path.posix.join(COMPILED_DIR, `${varName}.ts`)
|
||||
const contentFile = path.posix.join(COMPILED_DIR, `${varName}.content.ts`)
|
||||
const thumbnailPresent = await hasThumbnail(slug)
|
||||
const slug = frontSlug || path.basename(file, '.md')
|
||||
const varName = toVarName(slug)
|
||||
const exportFile = path.posix.join(COMPILED_DIR, `${varName}.ts`)
|
||||
const contentFile = path.posix.join(COMPILED_DIR, `${varName}.content.ts`)
|
||||
const thumbnailPresent = await hasThumbnail(slug)
|
||||
|
||||
const contentTs = `
|
||||
const contentTs = `
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const html = \`${minifiedHtml}\`;
|
||||
`.trimStart()
|
||||
await fs.writeFile(contentFile, contentTs, 'utf8')
|
||||
await fs.writeFile(contentFile, contentTs, 'utf8')
|
||||
|
||||
const ts = `
|
||||
const ts = `
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(\`./${varName}.content\`).then(m => m.html),
|
||||
@@ -96,35 +96,35 @@ export const article = {
|
||||
authors: ${JSON.stringify(authors)},
|
||||
thumbnail: ${thumbnailPresent},
|
||||
${Object.keys(rest)
|
||||
.map((k) => `${k}: ${JSON.stringify(rest[k])},`)
|
||||
.join('\n ')}
|
||||
.map((k) => `${k}: ${JSON.stringify(rest[k])},`)
|
||||
.join('\n ')}
|
||||
};
|
||||
`.trimStart()
|
||||
|
||||
await fs.writeFile(exportFile, ts, 'utf8')
|
||||
articleExports.push(`import { article as ${varName} } from "./${varName}";`)
|
||||
articlesArray.push(varName)
|
||||
await fs.writeFile(exportFile, ts, 'utf8')
|
||||
articleExports.push(`import { article as ${varName} } from "./${varName}";`)
|
||||
articlesArray.push(varName)
|
||||
|
||||
articlesForRss.push({
|
||||
title,
|
||||
summary,
|
||||
date,
|
||||
slug,
|
||||
html: minifiedHtml,
|
||||
} as never)
|
||||
articlesForRss.push({
|
||||
title,
|
||||
summary,
|
||||
date,
|
||||
slug,
|
||||
html: minifiedHtml,
|
||||
} as never)
|
||||
|
||||
articlesForJson.push({
|
||||
title,
|
||||
summary,
|
||||
thumbnail: getThumbnailUrl(slug, thumbnailPresent),
|
||||
date: new Date(date).toISOString(),
|
||||
link: getArticleLink(slug),
|
||||
} as never)
|
||||
}
|
||||
articlesForJson.push({
|
||||
title,
|
||||
summary,
|
||||
thumbnail: getThumbnailUrl(slug, thumbnailPresent),
|
||||
date: new Date(date).toISOString(),
|
||||
link: getArticleLink(slug),
|
||||
} as never)
|
||||
}
|
||||
|
||||
console.log(`📂 Compiled ${files.length} articles.`)
|
||||
console.log(`📂 Compiled ${files.length} articles.`)
|
||||
|
||||
const rootExport = `
|
||||
const rootExport = `
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
${articleExports.join('\n')}
|
||||
|
||||
@@ -133,124 +133,124 @@ export const articles = [
|
||||
];
|
||||
`.trimStart()
|
||||
|
||||
await fs.writeFile(ROOT_FILE, rootExport, 'utf8')
|
||||
console.log(`🌟 Done! Wrote root articles export.`)
|
||||
await fs.writeFile(ROOT_FILE, rootExport, 'utf8')
|
||||
console.log(`🌟 Done! Wrote root articles export.`)
|
||||
|
||||
await generateRssFeed(articlesForRss)
|
||||
await generateJsonFile(articlesForJson)
|
||||
await generateRssFeed(articlesForRss)
|
||||
await generateJsonFile(articlesForJson)
|
||||
}
|
||||
|
||||
async function generateRssFeed(articles): Promise<void> {
|
||||
const sorted = [...articles].sort(
|
||||
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime(),
|
||||
)
|
||||
const sorted = [...articles].sort(
|
||||
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime(),
|
||||
)
|
||||
|
||||
let currentRssArticles: { title: string; html: string }[] = []
|
||||
try {
|
||||
const xml = await fs.readFile(RSS_PATH, 'utf8')
|
||||
const parsed = await parseStringPromise(xml)
|
||||
const items = parsed.rss?.channel?.[0]?.item || []
|
||||
currentRssArticles = items.map((item) => ({
|
||||
title: (item.title?.[0] ?? '').trim(),
|
||||
html: (item['content:encoded']?.[0] ?? '').replace(/^<!\[CDATA\[|\]\]>$/g, '').trim(),
|
||||
}))
|
||||
} catch {
|
||||
currentRssArticles = []
|
||||
}
|
||||
let currentRssArticles: { title: string; html: string }[] = []
|
||||
try {
|
||||
const xml = await fs.readFile(RSS_PATH, 'utf8')
|
||||
const parsed = await parseStringPromise(xml)
|
||||
const items = parsed.rss?.channel?.[0]?.item || []
|
||||
currentRssArticles = items.map((item) => ({
|
||||
title: (item.title?.[0] ?? '').trim(),
|
||||
html: (item['content:encoded']?.[0] ?? '').replace(/^<!\[CDATA\[|\]\]>$/g, '').trim(),
|
||||
}))
|
||||
} catch {
|
||||
currentRssArticles = []
|
||||
}
|
||||
|
||||
const newArr = sorted.map((a) => ({
|
||||
title: (a.title ?? '').trim(),
|
||||
html: (a.html ?? '').trim(),
|
||||
}))
|
||||
const newArr = sorted.map((a) => ({
|
||||
title: (a.title ?? '').trim(),
|
||||
html: (a.html ?? '').trim(),
|
||||
}))
|
||||
|
||||
let isEqual = currentRssArticles.length === newArr.length
|
||||
if (isEqual) {
|
||||
for (let i = 0; i < newArr.length; ++i) {
|
||||
if (
|
||||
currentRssArticles[i].title !== newArr[i].title ||
|
||||
currentRssArticles[i].html !== newArr[i].html
|
||||
) {
|
||||
isEqual = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
let isEqual = currentRssArticles.length === newArr.length
|
||||
if (isEqual) {
|
||||
for (let i = 0; i < newArr.length; ++i) {
|
||||
if (
|
||||
currentRssArticles[i].title !== newArr[i].title ||
|
||||
currentRssArticles[i].html !== newArr[i].html
|
||||
) {
|
||||
isEqual = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isEqual) {
|
||||
console.log(`⏭️ RSS feed not regenerated (articles unchanged)`)
|
||||
return
|
||||
}
|
||||
if (isEqual) {
|
||||
console.log(`⏭️ RSS feed not regenerated (articles unchanged)`)
|
||||
return
|
||||
}
|
||||
|
||||
const feed = new RSS({
|
||||
title: 'Modrinth News',
|
||||
description: 'Keep up-to-date on the latest news from Modrinth.',
|
||||
feed_url: `${SITE_URL}/news/feed/rss.xml`,
|
||||
site_url: `${SITE_URL}/news/`,
|
||||
language: 'en',
|
||||
generator: '@modrinth/blog',
|
||||
})
|
||||
const feed = new RSS({
|
||||
title: 'Modrinth News',
|
||||
description: 'Keep up-to-date on the latest news from Modrinth.',
|
||||
feed_url: `${SITE_URL}/news/feed/rss.xml`,
|
||||
site_url: `${SITE_URL}/news/`,
|
||||
language: 'en',
|
||||
generator: '@modrinth/blog',
|
||||
})
|
||||
|
||||
for (const article of sorted) {
|
||||
feed.item({
|
||||
title: article.title,
|
||||
description: article.summary,
|
||||
url: `${SITE_URL}/news/article/${article.slug}/`,
|
||||
guid: `${SITE_URL}/news/article/${article.slug}/`,
|
||||
date: article.date,
|
||||
custom_elements: [{ 'content:encoded': `<![CDATA[${article.html}]]>` }],
|
||||
})
|
||||
}
|
||||
for (const article of sorted) {
|
||||
feed.item({
|
||||
title: article.title,
|
||||
description: article.summary,
|
||||
url: `${SITE_URL}/news/article/${article.slug}/`,
|
||||
guid: `${SITE_URL}/news/article/${article.slug}/`,
|
||||
date: article.date,
|
||||
custom_elements: [{ 'content:encoded': `<![CDATA[${article.html}]]>` }],
|
||||
})
|
||||
}
|
||||
|
||||
await fs.mkdir(path.dirname(RSS_PATH), { recursive: true })
|
||||
await fs.writeFile(RSS_PATH, feed.xml({ indent: true }), 'utf8')
|
||||
console.log(`📂 RSS feed written to ${RSS_PATH}`)
|
||||
await fs.mkdir(path.dirname(RSS_PATH), { recursive: true })
|
||||
await fs.writeFile(RSS_PATH, feed.xml({ indent: true }), 'utf8')
|
||||
console.log(`📂 RSS feed written to ${RSS_PATH}`)
|
||||
}
|
||||
|
||||
async function generateJsonFile(articles): Promise<void> {
|
||||
const sorted = [...articles].sort(
|
||||
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime(),
|
||||
)
|
||||
const json = { articles: sorted }
|
||||
await fs.mkdir(path.dirname(JSON_PATH), { recursive: true })
|
||||
await fs.writeFile(JSON_PATH, JSON.stringify(json, null, 2) + '\n', 'utf8')
|
||||
console.log(`📝 Wrote JSON articles to ${JSON_PATH}`)
|
||||
const sorted = [...articles].sort(
|
||||
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime(),
|
||||
)
|
||||
const json = { articles: sorted }
|
||||
await fs.mkdir(path.dirname(JSON_PATH), { recursive: true })
|
||||
await fs.writeFile(JSON_PATH, JSON.stringify(json, null, 2) + '\n', 'utf8')
|
||||
console.log(`📝 Wrote JSON articles to ${JSON_PATH}`)
|
||||
}
|
||||
|
||||
async function deleteDirContents(dir: string) {
|
||||
try {
|
||||
const entries = await fs.readdir(dir, { withFileTypes: true })
|
||||
await Promise.all(
|
||||
entries.map(async (entry) => {
|
||||
const fullPath = path.posix.join(dir, entry.name)
|
||||
if (entry.isDirectory()) {
|
||||
await fs.rm(fullPath, { recursive: true, force: true })
|
||||
} else {
|
||||
await fs.unlink(fullPath)
|
||||
}
|
||||
}),
|
||||
)
|
||||
} catch (error) {
|
||||
console.error(`❌ Error deleting contents of ${dir}:`, error)
|
||||
throw error
|
||||
}
|
||||
try {
|
||||
const entries = await fs.readdir(dir, { withFileTypes: true })
|
||||
await Promise.all(
|
||||
entries.map(async (entry) => {
|
||||
const fullPath = path.posix.join(dir, entry.name)
|
||||
if (entry.isDirectory()) {
|
||||
await fs.rm(fullPath, { recursive: true, force: true })
|
||||
} else {
|
||||
await fs.unlink(fullPath)
|
||||
}
|
||||
}),
|
||||
)
|
||||
} catch (error) {
|
||||
console.error(`❌ Error deleting contents of ${dir}:`, error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async function copyPublicAssets() {
|
||||
console.log('🚚 Copying ./public to all PUBLIC_LOCATIONS...')
|
||||
for (const loc of PUBLIC_LOCATIONS) {
|
||||
await deleteDirContents(loc)
|
||||
await copyDir(PUBLIC_SRC, loc)
|
||||
console.log(`📂 Copied ./public to ${loc}`)
|
||||
}
|
||||
console.log('🎉 All public assets copied!')
|
||||
console.log('🚚 Copying ./public to all PUBLIC_LOCATIONS...')
|
||||
for (const loc of PUBLIC_LOCATIONS) {
|
||||
await deleteDirContents(loc)
|
||||
await copyDir(PUBLIC_SRC, loc)
|
||||
console.log(`📂 Copied ./public to ${loc}`)
|
||||
}
|
||||
console.log('🎉 All public assets copied!')
|
||||
}
|
||||
|
||||
async function main() {
|
||||
await compileArticles()
|
||||
await copyPublicAssets()
|
||||
await compileArticles()
|
||||
await copyPublicAssets()
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
console.error('❌ Error in compile.ts:', e)
|
||||
process.exit(1)
|
||||
console.error('❌ Error in compile.ts:', e)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(`./a_new_chapter_for_modrinth_servers.content`).then((m) => m.html),
|
||||
title: 'A New Chapter for Modrinth Servers',
|
||||
summary: 'Modrinth Servers is now fully operated in-house by the Modrinth Team.',
|
||||
date: '2025-03-13T00:00:00.000Z',
|
||||
slug: 'a-new-chapter-for-modrinth-servers',
|
||||
authors: ['MpxzqsyW', 'Dc7EYhxG'],
|
||||
thumbnail: true,
|
||||
html: () => import(`./a_new_chapter_for_modrinth_servers.content`).then((m) => m.html),
|
||||
title: 'A New Chapter for Modrinth Servers',
|
||||
summary: 'Modrinth Servers is now fully operated in-house by the Modrinth Team.',
|
||||
date: '2025-03-13T00:00:00.000Z',
|
||||
slug: 'a-new-chapter-for-modrinth-servers',
|
||||
authors: ['MpxzqsyW', 'Dc7EYhxG'],
|
||||
thumbnail: true,
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(`./accelerating_development.content`).then((m) => m.html),
|
||||
title: "Accelerating Modrinth's Development",
|
||||
summary: 'Our fundraiser and the future of Modrinth!',
|
||||
date: '2023-02-01T20:00:00.000Z',
|
||||
slug: 'accelerating-development',
|
||||
authors: ['MpxzqsyW', 'Dc7EYhxG', '6plzAzU4'],
|
||||
thumbnail: false,
|
||||
html: () => import(`./accelerating_development.content`).then((m) => m.html),
|
||||
title: "Accelerating Modrinth's Development",
|
||||
summary: 'Our fundraiser and the future of Modrinth!',
|
||||
date: '2023-02-01T20:00:00.000Z',
|
||||
slug: 'accelerating-development',
|
||||
authors: ['MpxzqsyW', 'Dc7EYhxG', '6plzAzU4'],
|
||||
thumbnail: false,
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(`./becoming_sustainable.content`).then((m) => m.html),
|
||||
title: 'Quintupling Creator Revenue and Becoming Sustainable',
|
||||
summary: 'Announcing an update to our monetization program, creator split, and more!',
|
||||
date: '2024-09-13T20:00:00.000Z',
|
||||
slug: 'becoming-sustainable',
|
||||
authors: ['MpxzqsyW', 'Dc7EYhxG'],
|
||||
thumbnail: true,
|
||||
short_title: 'Becoming Sustainable',
|
||||
short_summary: 'Announcing 5x creator revenue and updates to the monetization program.',
|
||||
html: () => import(`./becoming_sustainable.content`).then((m) => m.html),
|
||||
title: 'Quintupling Creator Revenue and Becoming Sustainable',
|
||||
summary: 'Announcing an update to our monetization program, creator split, and more!',
|
||||
date: '2024-09-13T20:00:00.000Z',
|
||||
slug: 'becoming-sustainable',
|
||||
authors: ['MpxzqsyW', 'Dc7EYhxG'],
|
||||
thumbnail: true,
|
||||
short_title: 'Becoming Sustainable',
|
||||
short_summary: 'Announcing 5x creator revenue and updates to the monetization program.',
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(`./capital_return.content`).then((m) => m.html),
|
||||
title: 'A Sustainable Path Forward for Modrinth',
|
||||
summary: 'Our capital return and what’s next.',
|
||||
date: '2024-04-04T20:00:00.000Z',
|
||||
slug: 'capital-return',
|
||||
authors: ['MpxzqsyW'],
|
||||
thumbnail: false,
|
||||
html: () => import(`./capital_return.content`).then((m) => m.html),
|
||||
title: 'A Sustainable Path Forward for Modrinth',
|
||||
summary: 'Our capital return and what’s next.',
|
||||
date: '2024-04-04T20:00:00.000Z',
|
||||
slug: 'capital-return',
|
||||
authors: ['MpxzqsyW'],
|
||||
thumbnail: false,
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(`./carbon_ads.content`).then((m) => m.html),
|
||||
title: "Modrinth's Carbon Ads experiment",
|
||||
summary: 'Experimenting with a different ad providers to find one which one works for us.',
|
||||
date: '2022-09-08T00:00:00.000Z',
|
||||
slug: 'carbon-ads',
|
||||
authors: ['6plzAzU4'],
|
||||
thumbnail: true,
|
||||
html: () => import(`./carbon_ads.content`).then((m) => m.html),
|
||||
title: "Modrinth's Carbon Ads experiment",
|
||||
summary: 'Experimenting with a different ad providers to find one which one works for us.',
|
||||
date: '2022-09-08T00:00:00.000Z',
|
||||
slug: 'carbon-ads',
|
||||
authors: ['6plzAzU4'],
|
||||
thumbnail: true,
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(`./creator_monetization.content`).then((m) => m.html),
|
||||
title: 'Creators can now make money on Modrinth!',
|
||||
summary:
|
||||
'Introducing the Creator Monetization Program allowing creators to earn revenue from their projects.',
|
||||
date: '2022-11-12T00:00:00.000Z',
|
||||
slug: 'creator-monetization',
|
||||
authors: ['6plzAzU4'],
|
||||
thumbnail: true,
|
||||
html: () => import(`./creator_monetization.content`).then((m) => m.html),
|
||||
title: 'Creators can now make money on Modrinth!',
|
||||
summary:
|
||||
'Introducing the Creator Monetization Program allowing creators to earn revenue from their projects.',
|
||||
date: '2022-11-12T00:00:00.000Z',
|
||||
slug: 'creator-monetization',
|
||||
authors: ['6plzAzU4'],
|
||||
thumbnail: true,
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(`./creator_update.content`).then((m) => m.html),
|
||||
title: 'Creator Update: Analytics, Organizations, Collections, and more',
|
||||
summary: 'December may be over, but we’re not done giving gifts.',
|
||||
date: '2024-01-06T20:00:00.000Z',
|
||||
slug: 'creator-update',
|
||||
authors: ['6plzAzU4'],
|
||||
thumbnail: true,
|
||||
short_title: 'The Creator Update',
|
||||
short_summary: 'Adding analytics, orgs, collections, and more!',
|
||||
html: () => import(`./creator_update.content`).then((m) => m.html),
|
||||
title: 'Creator Update: Analytics, Organizations, Collections, and more',
|
||||
summary: 'December may be over, but we’re not done giving gifts.',
|
||||
date: '2024-01-06T20:00:00.000Z',
|
||||
slug: 'creator-update',
|
||||
authors: ['6plzAzU4'],
|
||||
thumbnail: true,
|
||||
short_title: 'The Creator Update',
|
||||
short_summary: 'Adding analytics, orgs, collections, and more!',
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(`./creator_updates_july_2025.content`).then((m) => m.html),
|
||||
title: 'Creator Updates, July 2025',
|
||||
summary: 'Addressing recent growth and growing pains that have been affecting creators.',
|
||||
date: '2025-07-02T04:20:00.000Z',
|
||||
slug: 'creator-updates-july-2025',
|
||||
authors: ['MpxzqsyW'],
|
||||
thumbnail: false,
|
||||
html: () => import(`./creator_updates_july_2025.content`).then((m) => m.html),
|
||||
title: 'Creator Updates, July 2025',
|
||||
summary: 'Addressing recent growth and growing pains that have been affecting creators.',
|
||||
date: '2025-07-02T04:20:00.000Z',
|
||||
slug: 'creator-updates-july-2025',
|
||||
authors: ['MpxzqsyW'],
|
||||
thumbnail: false,
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(`./design_refresh.content`).then((m) => m.html),
|
||||
title: 'Introducing Modrinth+, a refreshed site look, and a new advertising system!',
|
||||
summary: 'Learn about this major update to Modrinth.',
|
||||
date: '2024-08-21T20:00:00.000Z',
|
||||
slug: 'design-refresh',
|
||||
authors: ['MpxzqsyW', 'Dc7EYhxG'],
|
||||
thumbnail: true,
|
||||
short_title: 'Modrinth+ and New Ads',
|
||||
short_summary:
|
||||
'Introducing a new ad system, a subscription to remove ads, and a redesign of the website!',
|
||||
html: () => import(`./design_refresh.content`).then((m) => m.html),
|
||||
title: 'Introducing Modrinth+, a refreshed site look, and a new advertising system!',
|
||||
summary: 'Learn about this major update to Modrinth.',
|
||||
date: '2024-08-21T20:00:00.000Z',
|
||||
slug: 'design-refresh',
|
||||
authors: ['MpxzqsyW', 'Dc7EYhxG'],
|
||||
thumbnail: true,
|
||||
short_title: 'Modrinth+ and New Ads',
|
||||
short_summary:
|
||||
'Introducing a new ad system, a subscription to remove ads, and a redesign of the website!',
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(`./download_adjustment.content`).then((m) => m.html),
|
||||
title: 'Correcting Inflated Download Counts due to Rate Limiting Issue',
|
||||
summary: 'A rate limiting issue caused inflated download counts in certain countries.',
|
||||
date: '2023-11-10T20:00:00.000Z',
|
||||
slug: 'download-adjustment',
|
||||
authors: ['6plzAzU4', 'MpxzqsyW'],
|
||||
thumbnail: false,
|
||||
short_title: 'Correcting Inflated Download Counts',
|
||||
html: () => import(`./download_adjustment.content`).then((m) => m.html),
|
||||
title: 'Correcting Inflated Download Counts due to Rate Limiting Issue',
|
||||
summary: 'A rate limiting issue caused inflated download counts in certain countries.',
|
||||
date: '2023-11-10T20:00:00.000Z',
|
||||
slug: 'download-adjustment',
|
||||
authors: ['6plzAzU4', 'MpxzqsyW'],
|
||||
thumbnail: false,
|
||||
short_title: 'Correcting Inflated Download Counts',
|
||||
}
|
||||
|
||||
@@ -1,56 +1,56 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
import { article as windows_borderless_malware_disclosure } from './windows_borderless_malware_disclosure'
|
||||
import { article as whats_modrinth } from './whats_modrinth'
|
||||
import { article as a_new_chapter_for_modrinth_servers } from './a_new_chapter_for_modrinth_servers'
|
||||
import { article as accelerating_development } from './accelerating_development'
|
||||
import { article as becoming_sustainable } from './becoming_sustainable'
|
||||
import { article as capital_return } from './capital_return'
|
||||
import { article as carbon_ads } from './carbon_ads'
|
||||
import { article as creator_monetization } from './creator_monetization'
|
||||
import { article as creator_update } from './creator_update'
|
||||
import { article as creator_updates_july_2025 } from './creator_updates_july_2025'
|
||||
import { article as design_refresh } from './design_refresh'
|
||||
import { article as download_adjustment } from './download_adjustment'
|
||||
import { article as knossos_v2_1_0 } from './knossos_v2_1_0'
|
||||
import { article as licensing_guide } from './licensing_guide'
|
||||
import { article as modpack_changes } from './modpack_changes'
|
||||
import { article as modpacks_alpha } from './modpacks_alpha'
|
||||
import { article as modrinth_app_beta } from './modrinth_app_beta'
|
||||
import { article as modrinth_beta } from './modrinth_beta'
|
||||
import { article as modrinth_servers_beta } from './modrinth_servers_beta'
|
||||
import { article as new_site_beta } from './new_site_beta'
|
||||
import { article as plugins_resource_packs } from './plugins_resource_packs'
|
||||
import { article as pride_campaign_2025 } from './pride_campaign_2025'
|
||||
import { article as redesign } from './redesign'
|
||||
import { article as skins_now_in_modrinth_app } from './skins_now_in_modrinth_app'
|
||||
import { article as two_years_of_modrinth } from './two_years_of_modrinth'
|
||||
import { article as two_years_of_modrinth_history } from './two_years_of_modrinth_history'
|
||||
import { article as skins_now_in_modrinth_app } from './skins_now_in_modrinth_app'
|
||||
import { article as redesign } from './redesign'
|
||||
import { article as pride_campaign_2025 } from './pride_campaign_2025'
|
||||
import { article as plugins_resource_packs } from './plugins_resource_packs'
|
||||
import { article as new_site_beta } from './new_site_beta'
|
||||
import { article as modrinth_servers_beta } from './modrinth_servers_beta'
|
||||
import { article as modrinth_beta } from './modrinth_beta'
|
||||
import { article as modrinth_app_beta } from './modrinth_app_beta'
|
||||
import { article as modpacks_alpha } from './modpacks_alpha'
|
||||
import { article as modpack_changes } from './modpack_changes'
|
||||
import { article as licensing_guide } from './licensing_guide'
|
||||
import { article as knossos_v2_1_0 } from './knossos_v2_1_0'
|
||||
import { article as download_adjustment } from './download_adjustment'
|
||||
import { article as design_refresh } from './design_refresh'
|
||||
import { article as creator_updates_july_2025 } from './creator_updates_july_2025'
|
||||
import { article as creator_update } from './creator_update'
|
||||
import { article as creator_monetization } from './creator_monetization'
|
||||
import { article as carbon_ads } from './carbon_ads'
|
||||
import { article as capital_return } from './capital_return'
|
||||
import { article as becoming_sustainable } from './becoming_sustainable'
|
||||
import { article as accelerating_development } from './accelerating_development'
|
||||
import { article as a_new_chapter_for_modrinth_servers } from './a_new_chapter_for_modrinth_servers'
|
||||
import { article as whats_modrinth } from './whats_modrinth'
|
||||
import { article as windows_borderless_malware_disclosure } from './windows_borderless_malware_disclosure'
|
||||
|
||||
export const articles = [
|
||||
windows_borderless_malware_disclosure,
|
||||
whats_modrinth,
|
||||
two_years_of_modrinth,
|
||||
two_years_of_modrinth_history,
|
||||
skins_now_in_modrinth_app,
|
||||
redesign,
|
||||
pride_campaign_2025,
|
||||
plugins_resource_packs,
|
||||
new_site_beta,
|
||||
modrinth_servers_beta,
|
||||
modrinth_beta,
|
||||
modrinth_app_beta,
|
||||
modpacks_alpha,
|
||||
modpack_changes,
|
||||
licensing_guide,
|
||||
knossos_v2_1_0,
|
||||
download_adjustment,
|
||||
design_refresh,
|
||||
creator_updates_july_2025,
|
||||
creator_update,
|
||||
creator_monetization,
|
||||
carbon_ads,
|
||||
capital_return,
|
||||
becoming_sustainable,
|
||||
accelerating_development,
|
||||
a_new_chapter_for_modrinth_servers,
|
||||
windows_borderless_malware_disclosure,
|
||||
whats_modrinth,
|
||||
two_years_of_modrinth,
|
||||
two_years_of_modrinth_history,
|
||||
skins_now_in_modrinth_app,
|
||||
redesign,
|
||||
pride_campaign_2025,
|
||||
plugins_resource_packs,
|
||||
new_site_beta,
|
||||
modrinth_servers_beta,
|
||||
modrinth_beta,
|
||||
modrinth_app_beta,
|
||||
modpacks_alpha,
|
||||
modpack_changes,
|
||||
licensing_guide,
|
||||
knossos_v2_1_0,
|
||||
download_adjustment,
|
||||
design_refresh,
|
||||
creator_updates_july_2025,
|
||||
creator_update,
|
||||
creator_monetization,
|
||||
carbon_ads,
|
||||
capital_return,
|
||||
becoming_sustainable,
|
||||
accelerating_development,
|
||||
a_new_chapter_for_modrinth_servers,
|
||||
]
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(`./knossos_v2_1_0.content`).then((m) => m.html),
|
||||
title: 'This week in Modrinth development: Filters and Fixes',
|
||||
summary:
|
||||
'Continuing to improve the user interface after a great first week since Modrinth launched out of beta.',
|
||||
date: '2022-03-09T00:00:00.000Z',
|
||||
slug: 'knossos-v2.1.0',
|
||||
authors: ['Dc7EYhxG'],
|
||||
thumbnail: true,
|
||||
html: () => import(`./knossos_v2_1_0.content`).then((m) => m.html),
|
||||
title: 'This week in Modrinth development: Filters and Fixes',
|
||||
summary:
|
||||
'Continuing to improve the user interface after a great first week since Modrinth launched out of beta.',
|
||||
date: '2022-03-09T00:00:00.000Z',
|
||||
slug: 'knossos-v2.1.0',
|
||||
authors: ['Dc7EYhxG'],
|
||||
thumbnail: true,
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(`./licensing_guide.content`).then((m) => m.html),
|
||||
title: "Beginner's Guide to Licensing your Mods",
|
||||
summary:
|
||||
"Software licenses; the nitty-gritty legal aspect of software development. They're more important than you think.",
|
||||
date: '2021-05-16T00:00:00.000Z',
|
||||
slug: 'licensing-guide',
|
||||
authors: ['6plzAzU4', 'aNd6VJql'],
|
||||
thumbnail: true,
|
||||
html: () => import(`./licensing_guide.content`).then((m) => m.html),
|
||||
title: "Beginner's Guide to Licensing your Mods",
|
||||
summary:
|
||||
"Software licenses; the nitty-gritty legal aspect of software development. They're more important than you think.",
|
||||
date: '2021-05-16T00:00:00.000Z',
|
||||
slug: 'licensing-guide',
|
||||
authors: ['6plzAzU4', 'aNd6VJql'],
|
||||
thumbnail: true,
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(`./modpack_changes.content`).then((m) => m.html),
|
||||
title: 'Changes to Modrinth Modpacks',
|
||||
summary: 'CurseForge CDN links requested to be removed by the end of the month',
|
||||
date: '2022-05-28T00:00:00.000Z',
|
||||
slug: 'modpack-changes',
|
||||
authors: ['MpxzqsyW', 'Dc7EYhxG'],
|
||||
thumbnail: true,
|
||||
html: () => import(`./modpack_changes.content`).then((m) => m.html),
|
||||
title: 'Changes to Modrinth Modpacks',
|
||||
summary: 'CurseForge CDN links requested to be removed by the end of the month',
|
||||
date: '2022-05-28T00:00:00.000Z',
|
||||
slug: 'modpack-changes',
|
||||
authors: ['MpxzqsyW', 'Dc7EYhxG'],
|
||||
thumbnail: true,
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(`./modpacks_alpha.content`).then((m) => m.html),
|
||||
title: 'Modrinth Modpacks: Now in alpha testing',
|
||||
summary:
|
||||
"After over a year of development, we're happy to announce that modpack support is now in alpha testing.",
|
||||
date: '2022-05-15T00:00:00.000Z',
|
||||
slug: 'modpacks-alpha',
|
||||
authors: ['6plzAzU4'],
|
||||
thumbnail: true,
|
||||
html: () => import(`./modpacks_alpha.content`).then((m) => m.html),
|
||||
title: 'Modrinth Modpacks: Now in alpha testing',
|
||||
summary:
|
||||
"After over a year of development, we're happy to announce that modpack support is now in alpha testing.",
|
||||
date: '2022-05-15T00:00:00.000Z',
|
||||
slug: 'modpacks-alpha',
|
||||
authors: ['6plzAzU4'],
|
||||
thumbnail: true,
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(`./modrinth_app_beta.content`).then((m) => m.html),
|
||||
title: 'Introducing Modrinth App Beta',
|
||||
summary:
|
||||
'Changing the modded Minecraft landscape with the new Modrinth App, alongside several other major features.',
|
||||
date: '2023-08-05T20:00:00.000Z',
|
||||
slug: 'modrinth-app-beta',
|
||||
authors: ['6plzAzU4'],
|
||||
thumbnail: false,
|
||||
short_title: 'Modrinth App Beta and Upgraded Authentication',
|
||||
short_summary: 'Launching Modrinth App Beta and upgrading authentication.',
|
||||
html: () => import(`./modrinth_app_beta.content`).then((m) => m.html),
|
||||
title: 'Introducing Modrinth App Beta',
|
||||
summary:
|
||||
'Changing the modded Minecraft landscape with the new Modrinth App, alongside several other major features.',
|
||||
date: '2023-08-05T20:00:00.000Z',
|
||||
slug: 'modrinth-app-beta',
|
||||
authors: ['6plzAzU4'],
|
||||
thumbnail: false,
|
||||
short_title: 'Modrinth App Beta and Upgraded Authentication',
|
||||
short_summary: 'Launching Modrinth App Beta and upgrading authentication.',
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(`./modrinth_beta.content`).then((m) => m.html),
|
||||
title: 'Welcome to Modrinth Beta',
|
||||
summary:
|
||||
'After six months of work, Modrinth enters Beta, helping modders host their mods with ease!',
|
||||
date: '2020-12-01T00:00:00.000Z',
|
||||
slug: 'modrinth-beta',
|
||||
authors: ['Dc7EYhxG'],
|
||||
thumbnail: true,
|
||||
html: () => import(`./modrinth_beta.content`).then((m) => m.html),
|
||||
title: 'Welcome to Modrinth Beta',
|
||||
summary:
|
||||
'After six months of work, Modrinth enters Beta, helping modders host their mods with ease!',
|
||||
date: '2020-12-01T00:00:00.000Z',
|
||||
slug: 'modrinth-beta',
|
||||
authors: ['Dc7EYhxG'],
|
||||
thumbnail: true,
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(`./modrinth_servers_beta.content`).then((m) => m.html),
|
||||
title: 'Host your own server with Modrinth Servers — now in beta',
|
||||
summary: 'Fast, simple, reliable servers directly integrated into Modrinth.',
|
||||
date: '2024-11-03T06:00:00.000Z',
|
||||
slug: 'modrinth-servers-beta',
|
||||
authors: ['MpxzqsyW', 'Dc7EYhxG'],
|
||||
thumbnail: true,
|
||||
short_title: 'Introducing Modrinth Servers',
|
||||
short_summary: 'Host your next Minecraft server with Modrinth.',
|
||||
html: () => import(`./modrinth_servers_beta.content`).then((m) => m.html),
|
||||
title: 'Host your own server with Modrinth Servers — now in beta',
|
||||
summary: 'Fast, simple, reliable servers directly integrated into Modrinth.',
|
||||
date: '2024-11-03T06:00:00.000Z',
|
||||
slug: 'modrinth-servers-beta',
|
||||
authors: ['MpxzqsyW', 'Dc7EYhxG'],
|
||||
thumbnail: true,
|
||||
short_title: 'Introducing Modrinth Servers',
|
||||
short_summary: 'Host your next Minecraft server with Modrinth.',
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(`./new_site_beta.content`).then((m) => m.html),
|
||||
title: '(April Fools 2023) Powering up your experience: Modrinth Technologies™️ beta launch!',
|
||||
summary: "Welcome to the new era of Modrinth. We can't wait to hear your feedback.",
|
||||
date: '2023-04-01T08:00:00.000Z',
|
||||
slug: 'new-site-beta',
|
||||
authors: [],
|
||||
thumbnail: true,
|
||||
short_title: '(April Fools 2023) Modrinth Technologies™️ beta launch!',
|
||||
short_summary: 'Power up your experience.',
|
||||
html: () => import(`./new_site_beta.content`).then((m) => m.html),
|
||||
title: '(April Fools 2023) Powering up your experience: Modrinth Technologies™️ beta launch!',
|
||||
summary: "Welcome to the new era of Modrinth. We can't wait to hear your feedback.",
|
||||
date: '2023-04-01T08:00:00.000Z',
|
||||
slug: 'new-site-beta',
|
||||
authors: [],
|
||||
thumbnail: true,
|
||||
short_title: '(April Fools 2023) Modrinth Technologies™️ beta launch!',
|
||||
short_summary: 'Power up your experience.',
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(`./plugins_resource_packs.content`).then((m) => m.html),
|
||||
title: 'Plugins and Resource Packs now have a home on Modrinth',
|
||||
summary:
|
||||
'A small update with a big impact: plugins and resource packs are now available on Modrinth!',
|
||||
date: '2022-08-27T00:00:00.000Z',
|
||||
slug: 'plugins-resource-packs',
|
||||
authors: ['6plzAzU4'],
|
||||
thumbnail: true,
|
||||
html: () => import(`./plugins_resource_packs.content`).then((m) => m.html),
|
||||
title: 'Plugins and Resource Packs now have a home on Modrinth',
|
||||
summary:
|
||||
'A small update with a big impact: plugins and resource packs are now available on Modrinth!',
|
||||
date: '2022-08-27T00:00:00.000Z',
|
||||
slug: 'plugins-resource-packs',
|
||||
authors: ['6plzAzU4'],
|
||||
thumbnail: true,
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(`./pride_campaign_2025.content`).then((m) => m.html),
|
||||
title: 'A Pride Month Success: Over $8,400 Raised for The Trevor Project!',
|
||||
summary: 'Reflecting on our Pride Month fundraiser campaign for LGBTQ+ youth.',
|
||||
date: '2025-07-01T18:00:00.000Z',
|
||||
slug: 'pride-campaign-2025',
|
||||
authors: ['6plzAzU4', 'bOHH0P9Z', '2cqK8Q5p', 'vNcGR3Fd'],
|
||||
thumbnail: true,
|
||||
short_title: 'Pride Month Fundraiser 2025',
|
||||
short_summary: 'A reflection on our Pride Month fundraiser campaign.',
|
||||
html: () => import(`./pride_campaign_2025.content`).then((m) => m.html),
|
||||
title: 'A Pride Month Success: Over $8,400 Raised for The Trevor Project!',
|
||||
summary: 'Reflecting on our Pride Month fundraiser campaign for LGBTQ+ youth.',
|
||||
date: '2025-07-01T18:00:00.000Z',
|
||||
slug: 'pride-campaign-2025',
|
||||
authors: ['6plzAzU4', 'bOHH0P9Z', '2cqK8Q5p', 'vNcGR3Fd'],
|
||||
thumbnail: true,
|
||||
short_title: 'Pride Month Fundraiser 2025',
|
||||
short_summary: 'A reflection on our Pride Month fundraiser campaign.',
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(`./redesign.content`).then((m) => m.html),
|
||||
title: 'Now showing on Modrinth: A new look!',
|
||||
summary: 'Releasing many new features and improvements, including a redesign!',
|
||||
date: '2022-02-27T00:00:00.000Z',
|
||||
slug: 'redesign',
|
||||
authors: ['6plzAzU4'],
|
||||
thumbnail: true,
|
||||
html: () => import(`./redesign.content`).then((m) => m.html),
|
||||
title: 'Now showing on Modrinth: A new look!',
|
||||
summary: 'Releasing many new features and improvements, including a redesign!',
|
||||
date: '2022-02-27T00:00:00.000Z',
|
||||
slug: 'redesign',
|
||||
authors: ['6plzAzU4'],
|
||||
thumbnail: true,
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(`./skins_now_in_modrinth_app.content`).then((m) => m.html),
|
||||
title: 'Skins — Now in Modrinth App!',
|
||||
summary:
|
||||
'Customize your look, save your favorite skins, and swap them out in a flash, all within Modrinth App.',
|
||||
date: '2025-07-06T23:45:00.000Z',
|
||||
slug: 'skins-now-in-modrinth-app',
|
||||
authors: ['bOHH0P9Z', 'Dc7EYhxG'],
|
||||
thumbnail: true,
|
||||
html: () => import(`./skins_now_in_modrinth_app.content`).then((m) => m.html),
|
||||
title: 'Skins — Now in Modrinth App!',
|
||||
summary:
|
||||
'Customize your look, save your favorite skins, and swap them out in a flash, all within Modrinth App.',
|
||||
date: '2025-07-06T23:45:00.000Z',
|
||||
slug: 'skins-now-in-modrinth-app',
|
||||
authors: ['bOHH0P9Z', 'Dc7EYhxG'],
|
||||
thumbnail: true,
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(`./two_years_of_modrinth.content`).then((m) => m.html),
|
||||
title: "Modrinth's Anniversary Update",
|
||||
summary: "Marking two years of Modrinth and discussing our New Year's Resolutions for 2023.",
|
||||
date: '2023-01-07T00:00:00.000Z',
|
||||
slug: 'two-years-of-modrinth',
|
||||
authors: ['6plzAzU4'],
|
||||
thumbnail: true,
|
||||
html: () => import(`./two_years_of_modrinth.content`).then((m) => m.html),
|
||||
title: "Modrinth's Anniversary Update",
|
||||
summary: "Marking two years of Modrinth and discussing our New Year's Resolutions for 2023.",
|
||||
date: '2023-01-07T00:00:00.000Z',
|
||||
slug: 'two-years-of-modrinth',
|
||||
authors: ['6plzAzU4'],
|
||||
thumbnail: true,
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(`./two_years_of_modrinth_history.content`).then((m) => m.html),
|
||||
title: 'Two years of Modrinth: a retrospective',
|
||||
summary: 'The history of Modrinth as we know it from December 2020 to December 2022.',
|
||||
date: '2023-01-07T00:00:00.000Z',
|
||||
slug: 'two-years-of-modrinth-history',
|
||||
authors: ['6plzAzU4'],
|
||||
thumbnail: false,
|
||||
html: () => import(`./two_years_of_modrinth_history.content`).then((m) => m.html),
|
||||
title: 'Two years of Modrinth: a retrospective',
|
||||
summary: 'The history of Modrinth as we know it from December 2020 to December 2022.',
|
||||
date: '2023-01-07T00:00:00.000Z',
|
||||
slug: 'two-years-of-modrinth-history',
|
||||
authors: ['6plzAzU4'],
|
||||
thumbnail: false,
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(`./whats_modrinth.content`).then((m) => m.html),
|
||||
title: 'What is Modrinth?',
|
||||
summary:
|
||||
"Hello, we are Modrinth – an open source mods hosting platform. Sounds dry, doesn't it? So let me tell you our story – and I promise, it won't be boring!",
|
||||
date: '2020-11-27T00:00:00.000Z',
|
||||
slug: 'whats-modrinth',
|
||||
authors: ['aNd6VJql'],
|
||||
thumbnail: false,
|
||||
html: () => import(`./whats_modrinth.content`).then((m) => m.html),
|
||||
title: 'What is Modrinth?',
|
||||
summary:
|
||||
"Hello, we are Modrinth – an open source mods hosting platform. Sounds dry, doesn't it? So let me tell you our story – and I promise, it won't be boring!",
|
||||
date: '2020-11-27T00:00:00.000Z',
|
||||
slug: 'whats-modrinth',
|
||||
authors: ['aNd6VJql'],
|
||||
thumbnail: false,
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(`./windows_borderless_malware_disclosure.content`).then((m) => m.html),
|
||||
title: 'Malware Discovery Disclosure: "Windows Borderless" mod',
|
||||
summary: 'Threat Analysis and Plan of Action',
|
||||
date: '2024-05-07T20:00:00.000Z',
|
||||
slug: 'windows-borderless-malware-disclosure',
|
||||
authors: ['Dc7EYhxG', 'MpxzqsyW'],
|
||||
thumbnail: true,
|
||||
html: () => import(`./windows_borderless_malware_disclosure.content`).then((m) => m.html),
|
||||
title: 'Malware Discovery Disclosure: "Windows Borderless" mod',
|
||||
summary: 'Threat Analysis and Plan of Action',
|
||||
date: '2024-05-07T20:00:00.000Z',
|
||||
slug: 'windows-borderless-malware-disclosure',
|
||||
authors: ['Dc7EYhxG', 'MpxzqsyW'],
|
||||
thumbnail: true,
|
||||
}
|
||||
|
||||
2
packages/blog/eslint.config.mjs
Normal file
2
packages/blog/eslint.config.mjs
Normal file
@@ -0,0 +1,2 @@
|
||||
import config from '@modrinth/tooling-config/eslint/nuxt.mjs'
|
||||
export default config
|
||||
@@ -1,29 +1,27 @@
|
||||
{
|
||||
"name": "@modrinth/blog",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"main": "./index.ts",
|
||||
"types": "./index.d.ts",
|
||||
"scripts": {
|
||||
"lint": "jiti ./check.ts && eslint . && prettier --check .",
|
||||
"fix": "jiti ./compile.ts && eslint . --fix && prettier --write ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/glob": "^9.0.0",
|
||||
"@types/html-minifier-terser": "^7.0.2",
|
||||
"@types/rss": "^0.0.32",
|
||||
"@types/xml2js": "^0.4.14",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-custom": "workspace:*",
|
||||
"jiti": "^2.4.2",
|
||||
"tsconfig": "workspace:*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@modrinth/utils": "workspace:*",
|
||||
"glob": "^10.2.7",
|
||||
"gray-matter": "^4.0.3",
|
||||
"html-minifier-terser": "^7.2.0",
|
||||
"rss": "^1.2.2",
|
||||
"xml2js": "^0.6.2"
|
||||
}
|
||||
"name": "@modrinth/blog",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"main": "./index.ts",
|
||||
"types": "./index.d.ts",
|
||||
"scripts": {
|
||||
"lint": "jiti ./check.ts && eslint . && prettier --check .",
|
||||
"fix": "jiti ./compile.ts && eslint . --fix && prettier --write ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@modrinth/tooling-config": "workspace:*",
|
||||
"@types/glob": "^9.0.0",
|
||||
"@types/html-minifier-terser": "^7.0.2",
|
||||
"@types/rss": "^0.0.32",
|
||||
"@types/xml2js": "^0.4.14",
|
||||
"jiti": "^2.4.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@modrinth/utils": "workspace:*",
|
||||
"glob": "^10.2.7",
|
||||
"gray-matter": "^4.0.3",
|
||||
"html-minifier-terser": "^7.2.0",
|
||||
"rss": "^1.2.2",
|
||||
"xml2js": "^0.6.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
{
|
||||
"extends": "tsconfig/base.json",
|
||||
"include": [".", ".eslintrc.js"],
|
||||
"exclude": ["dist", "build", "node_modules"],
|
||||
"compilerOptions": {
|
||||
"lib": ["esnext", "dom"],
|
||||
"noImplicitAny": false
|
||||
}
|
||||
"extends": "@modrinth/tooling-config/typescript/base.json"
|
||||
}
|
||||
|
||||
@@ -3,38 +3,38 @@ import * as path from 'path'
|
||||
|
||||
let REPO_ROOT_CACHE: string | null = null
|
||||
export function getRepoRoot(): string {
|
||||
if (REPO_ROOT_CACHE) return REPO_ROOT_CACHE
|
||||
return (REPO_ROOT_CACHE = execSync('git rev-parse --show-toplevel').toString().trim())
|
||||
if (REPO_ROOT_CACHE) return REPO_ROOT_CACHE
|
||||
return (REPO_ROOT_CACHE = execSync('git rev-parse --show-toplevel').toString().trim())
|
||||
}
|
||||
|
||||
export function repoPath(...segments: string[]): string {
|
||||
return path.posix.join(getRepoRoot(), ...segments)
|
||||
return path.posix.join(getRepoRoot(), ...segments)
|
||||
}
|
||||
|
||||
export async function copyDir(
|
||||
src: string,
|
||||
dest: string,
|
||||
logFn: (src: string, dest: string) => void = () => {},
|
||||
src: string,
|
||||
dest: string,
|
||||
logFn: (src: string, dest: string) => void = () => {},
|
||||
): Promise<void> {
|
||||
const { promises: fs } = await import('fs')
|
||||
await fs.mkdir(dest, { recursive: true })
|
||||
const entries = await fs.readdir(src, { withFileTypes: true })
|
||||
for (const entry of entries) {
|
||||
const srcPath = path.posix.join(src, entry.name)
|
||||
const destPath = path.posix.join(dest, entry.name)
|
||||
if (entry.isDirectory()) {
|
||||
await copyDir(srcPath, destPath, logFn)
|
||||
} else if (entry.isFile()) {
|
||||
await fs.copyFile(srcPath, destPath)
|
||||
logFn(srcPath, destPath)
|
||||
}
|
||||
}
|
||||
const { promises: fs } = await import('fs')
|
||||
await fs.mkdir(dest, { recursive: true })
|
||||
const entries = await fs.readdir(src, { withFileTypes: true })
|
||||
for (const entry of entries) {
|
||||
const srcPath = path.posix.join(src, entry.name)
|
||||
const destPath = path.posix.join(dest, entry.name)
|
||||
if (entry.isDirectory()) {
|
||||
await copyDir(srcPath, destPath, logFn)
|
||||
} else if (entry.isFile()) {
|
||||
await fs.copyFile(srcPath, destPath)
|
||||
logFn(srcPath, destPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function toVarName(file: string): string {
|
||||
return file
|
||||
.replace(/\.md$/, '')
|
||||
.replace(/[^a-zA-Z0-9]/g, '_')
|
||||
.replace(/^_+/, '')
|
||||
.replace(/_+$/, '')
|
||||
return file
|
||||
.replace(/\.md$/, '')
|
||||
.replace(/[^a-zA-Z0-9]/g, '_')
|
||||
.replace(/^_+/, '')
|
||||
.replace(/_+$/, '')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user