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:
Cal H.
2025-08-14 21:48:38 +01:00
committed by GitHub
parent 82697278dc
commit 2aabcf36ee
702 changed files with 101360 additions and 102020 deletions

View File

@@ -1,7 +0,0 @@
module.exports = {
root: true,
extends: ['custom/library'],
env: {
node: true,
},
}

View File

@@ -1,4 +1,5 @@
import * as path from 'path'
import { repoPath } from './utils'
/**

View File

@@ -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)
})

View File

@@ -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)
})

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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.',
}

View File

@@ -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 whats 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 whats next.',
date: '2024-04-04T20:00:00.000Z',
slug: 'capital-return',
authors: ['MpxzqsyW'],
thumbnail: false,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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 were 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 were 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!',
}

View File

@@ -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,
}

View File

@@ -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!',
}

View File

@@ -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',
}

View File

@@ -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,
]

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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.',
}

View File

@@ -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,
}

View File

@@ -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.',
}

View File

@@ -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.',
}

View File

@@ -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,
}

View File

@@ -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.',
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -0,0 +1,2 @@
import config from '@modrinth/tooling-config/eslint/nuxt.mjs'
export default config

View File

@@ -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"
}
}

View File

@@ -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"
}

View File

@@ -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(/_+$/, '')
}