From 0372344d331591e961c3e7365a8aca2ad7c1ed2a Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 27 May 2025 10:50:48 +0200 Subject: [PATCH] Move Mastodon theme handling to custom vite plugin (#34808) Co-authored-by: Echo --- config/vite/plugin-mastodon-themes.ts | 58 +++++++++++++++++++++++++++ vite.config.mts | 31 +------------- 2 files changed, 60 insertions(+), 29 deletions(-) create mode 100644 config/vite/plugin-mastodon-themes.ts diff --git a/config/vite/plugin-mastodon-themes.ts b/config/vite/plugin-mastodon-themes.ts new file mode 100644 index 0000000000..07d79584a3 --- /dev/null +++ b/config/vite/plugin-mastodon-themes.ts @@ -0,0 +1,58 @@ +/* This plugins handles Mastodon's theme system + */ + +import fs from 'node:fs/promises'; +import path from 'node:path'; + +import yaml from 'js-yaml'; +import type { Plugin } from 'vite'; + +export function MastodonThemes(): Plugin { + return { + name: 'mastodon-themes', + async config(userConfig) { + if (!userConfig.root || !userConfig.envDir) { + throw new Error('Unknown project directory'); + } + + const themesFile = path.resolve(userConfig.envDir, 'config/themes.yml'); + const entrypoints: Record = {}; + + // Get all files mentioned in the themes.yml file. + const themesString = await fs.readFile(themesFile, 'utf8'); + const themes = yaml.load(themesString, { + filename: 'themes.yml', + schema: yaml.FAILSAFE_SCHEMA, + }); + + if (!themes || typeof themes !== 'object') { + throw new Error('Invalid themes.yml file'); + } + + for (const themePath of Object.values(themes)) { + if ( + typeof themePath !== 'string' || + themePath.split('.').length !== 2 || // Ensure it has exactly one period + !themePath.endsWith('css') + ) { + console.warn( + `Invalid theme path "${themePath}" in themes.yml, skipping`, + ); + continue; + } + entrypoints[path.basename(themePath)] = path.resolve( + userConfig.root, + themePath, + ); + } + + return { + build: { + rollupOptions: { + input: entrypoints, + }, + }, + }; + }, + }; +} diff --git a/vite.config.mts b/vite.config.mts index a606f965fc..37b5f0f127 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -1,4 +1,3 @@ -import fs from 'node:fs/promises'; import path from 'node:path'; import { optimizeLodashImports } from '@optimize-lodash/rollup-plugin'; @@ -9,7 +8,6 @@ import { visualizer } from 'rollup-plugin-visualizer'; import RailsPlugin from 'vite-plugin-rails'; import { VitePWA } from 'vite-plugin-pwa'; import tsconfigPaths from 'vite-tsconfig-paths'; -import yaml from 'js-yaml'; import legacy from '@vitejs/plugin-legacy'; import { defineConfig, UserConfigFnPromise, UserConfig } from 'vite'; @@ -17,36 +15,11 @@ import postcssPresetEnv from 'postcss-preset-env'; import { MastodonServiceWorkerLocales } from './config/vite/plugin-sw-locales'; import { MastodonEmojiCompressed } from './config/vite/plugin-emoji-compressed'; +import { MastodonThemes } from './config/vite/plugin-mastodon-themes'; const jsRoot = path.resolve(__dirname, 'app/javascript'); -const themesFile = path.resolve(__dirname, 'config/themes.yml'); export const config: UserConfigFnPromise = async ({ mode, command }) => { - const entrypoints: Record = {}; // All JS entrypoints are taken care of by Vite Ruby - - // Get all files mentioned in the themes.yml file. - const themesString = await fs.readFile(themesFile, 'utf8'); - const themes = yaml.load(themesString, { - filename: 'themes.yml', - schema: yaml.FAILSAFE_SCHEMA, - }); - - if (!themes || typeof themes !== 'object') { - throw new Error('Invalid themes.yml file'); - } - - for (const themePath of Object.values(themes)) { - if ( - typeof themePath !== 'string' || - themePath.split('.').length !== 2 || // Ensure it has exactly one period - !themePath.endsWith('css') - ) { - console.warn(`Invalid theme path "${themePath}" in themes.yml, skipping`); - continue; - } - entrypoints[path.basename(themePath)] = path.resolve(jsRoot, themePath); - } - return { root: jsRoot, css: { @@ -72,7 +45,6 @@ export const config: UserConfigFnPromise = async ({ mode, command }) => { chunkSizeWarningLimit: 1 * 1024 * 1024, // 1MB sourcemap: true, rollupOptions: { - input: entrypoints, output: { chunkFileNames({ facadeModuleId, name }) { if (!facadeModuleId) { @@ -113,6 +85,7 @@ export const config: UserConfigFnPromise = async ({ mode, command }) => { manifestPaths: ['.vite/manifest.json', '.vite/manifest-assets.json'], }, }), + MastodonThemes(), react({ babel: { plugins: ['formatjs', 'transform-react-remove-prop-types'],