From 1161cefe88e48e640b26660b8024bcb2acd81727 Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Tue, 25 Mar 2025 12:20:43 -0400 Subject: [PATCH 1/4] Move themes to sass --- src/themes/appletv/{theme.css => theme.scss} | 0 src/themes/blueradiance/{theme.css => theme.scss} | 0 src/themes/dark/{theme.css => theme.scss} | 0 src/themes/light/{theme.css => theme.scss} | 0 src/themes/purplehaze/{theme.css => theme.scss} | 0 src/themes/wmc/{theme.css => theme.scss} | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename src/themes/appletv/{theme.css => theme.scss} (100%) rename src/themes/blueradiance/{theme.css => theme.scss} (100%) rename src/themes/dark/{theme.css => theme.scss} (100%) rename src/themes/light/{theme.css => theme.scss} (100%) rename src/themes/purplehaze/{theme.css => theme.scss} (100%) rename src/themes/wmc/{theme.css => theme.scss} (100%) diff --git a/src/themes/appletv/theme.css b/src/themes/appletv/theme.scss similarity index 100% rename from src/themes/appletv/theme.css rename to src/themes/appletv/theme.scss diff --git a/src/themes/blueradiance/theme.css b/src/themes/blueradiance/theme.scss similarity index 100% rename from src/themes/blueradiance/theme.css rename to src/themes/blueradiance/theme.scss diff --git a/src/themes/dark/theme.css b/src/themes/dark/theme.scss similarity index 100% rename from src/themes/dark/theme.css rename to src/themes/dark/theme.scss diff --git a/src/themes/light/theme.css b/src/themes/light/theme.scss similarity index 100% rename from src/themes/light/theme.css rename to src/themes/light/theme.scss diff --git a/src/themes/purplehaze/theme.css b/src/themes/purplehaze/theme.scss similarity index 100% rename from src/themes/purplehaze/theme.css rename to src/themes/purplehaze/theme.scss diff --git a/src/themes/wmc/theme.css b/src/themes/wmc/theme.scss similarity index 100% rename from src/themes/wmc/theme.css rename to src/themes/wmc/theme.scss From a768ca3037f0fcd17d96977aafdd8035789f1f69 Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Tue, 25 Mar 2025 12:21:57 -0400 Subject: [PATCH 2/4] Update webpack config to build sass themes --- webpack.common.js | 80 +++++++++++++++++++++++++++-------------------- webpack.dev.js | 17 +++++++++- webpack.prod.js | 15 ++++++++- 3 files changed, 76 insertions(+), 36 deletions(-) diff --git a/webpack.common.js b/webpack.common.js index 3534e683e1..39af742598 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -60,13 +60,14 @@ const config = { filename: 'index.html', template: 'index.html', // Append file hashes to bundle urls for cache busting - hash: true + hash: true, + chunks: [ + 'main.jellyfin', + 'serviceworker' + ] }), new CopyPlugin({ patterns: [ - { - from: 'themes/**/*.{css,jpg}' - }, { from: 'assets/**', globOptions: { @@ -107,6 +108,15 @@ const config = { typescript: { configFile: path.resolve(__dirname, 'tsconfig.json') } + }), + new MiniCssExtractPlugin({ + filename: pathData => { + if (pathData.chunk?.name?.startsWith('themes/')) { + return '[name]/theme.css'; + } + return '[name].[contenthash].css'; + }, + chunkFilename: '[name].[contenthash].css' }) ], output: { @@ -288,33 +298,41 @@ const config = { }] }, { - test: /\.s[ac]ss$/i, - use: [ - DEV_MODE ? 'style-loader' : MiniCssExtractPlugin.loader, - 'css-loader', + test: /\.(sa|sc|c)ss$/i, + oneOf: [ { - loader: 'postcss-loader', - options: { - postcssOptions: { - config: path.resolve(__dirname, 'postcss.config.js') - } - } + // Themes always need to use the MiniCssExtractPlugin since they are loaded directly + include: [ + path.resolve(__dirname, 'src/themes/') + ], + use: [ + MiniCssExtractPlugin.loader, + 'css-loader', + { + loader: 'postcss-loader', + options: { + postcssOptions: { + config: path.resolve(__dirname, 'postcss.config.js') + } + } + }, + 'sass-loader' + ] }, - 'sass-loader' - ] - }, - { - test: /\.css$/i, - use: [ - DEV_MODE ? 'style-loader' : MiniCssExtractPlugin.loader, - 'css-loader', { - loader: 'postcss-loader', - options: { - postcssOptions: { - config: path.resolve(__dirname, 'postcss.config.js') - } - } + use: [ + DEV_MODE ? 'style-loader' : MiniCssExtractPlugin.loader, + 'css-loader', + { + loader: 'postcss-loader', + options: { + postcssOptions: { + config: path.resolve(__dirname, 'postcss.config.js') + } + } + }, + 'sass-loader' + ] } ] }, @@ -341,10 +359,4 @@ const config = { } }; -if (!DEV_MODE) { - config.plugins.push(new MiniCssExtractPlugin({ - filename: '[name].[contenthash].css' - })); -} - module.exports = config; diff --git a/webpack.dev.js b/webpack.dev.js index a6288a80ce..5721f54f76 100644 --- a/webpack.dev.js +++ b/webpack.dev.js @@ -1,11 +1,26 @@ const common = require('./webpack.common'); const { merge } = require('webpack-merge'); +const THEMES = [ + 'appletv', + 'blueradiance', + 'dark', + 'light', + 'purplehaze', + 'wmc' +]; + module.exports = merge(common, { // In order for live reload to work we must use "web" as the target not "browserslist" target: process.env.WEBPACK_SERVE ? 'web' : 'browserslist', mode: 'development', - entry: { 'main.jellyfin': './index.jsx' }, + entry: { + 'main.jellyfin': './index.jsx', + ...THEMES.reduce((acc, theme) => { + acc[`themes/${theme}`] = `./themes/${theme}/theme.scss`; + return acc; + }, {}) + }, devtool: 'eval-cheap-module-source-map', module: { rules: [ diff --git a/webpack.prod.js b/webpack.prod.js index 8b2c9aad5e..4912329b3b 100644 --- a/webpack.prod.js +++ b/webpack.prod.js @@ -1,10 +1,23 @@ const common = require('./webpack.common'); const { merge } = require('webpack-merge'); +const THEMES = [ + 'appletv', + 'blueradiance', + 'dark', + 'light', + 'purplehaze', + 'wmc' +]; + module.exports = merge(common, { mode: 'production', entry: { 'main.jellyfin': './index.jsx', - 'serviceworker': './serviceworker.js' + 'serviceworker': './serviceworker.js', + ...THEMES.reduce((acc, theme) => { + acc[`themes/${theme}`] = `./themes/${theme}/theme.scss`; + return acc; + }, {}) } }); From fe6b43d586792026791bc3a1612a1d1b66a505ef Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Tue, 25 Mar 2025 15:36:32 -0400 Subject: [PATCH 3/4] Lookup themes dynamically --- package-lock.json | 2 +- package.json | 1 + webpack.common.js | 11 +++++++++++ webpack.dev.js | 17 +---------------- webpack.prod.js | 18 +++--------------- 5 files changed, 17 insertions(+), 32 deletions(-) diff --git a/package-lock.json b/package-lock.json index d44b8fe158..b7d157358d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -102,6 +102,7 @@ "eslint-plugin-react-hooks": "5.2.0", "eslint-plugin-sonarjs": "3.0.2", "expose-loader": "5.0.1", + "fast-glob": "3.3.3", "fork-ts-checker-webpack-plugin": "9.0.2", "globals": "16.0.0", "html-loader": "5.1.0", @@ -11384,7 +11385,6 @@ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", diff --git a/package.json b/package.json index 46cc20e457..5d4704690c 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "eslint-plugin-react-hooks": "5.2.0", "eslint-plugin-sonarjs": "3.0.2", "expose-loader": "5.0.1", + "fast-glob": "3.3.3", "fork-ts-checker-webpack-plugin": "9.0.2", "globals": "16.0.0", "html-loader": "5.1.0", diff --git a/webpack.common.js b/webpack.common.js index 39af742598..c93ca537e2 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -1,3 +1,4 @@ +const fg = require('fast-glob'); const path = require('path'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const CopyPlugin = require('copy-webpack-plugin'); @@ -33,9 +34,19 @@ try { const NODE_MODULES_REGEX = /[\\/]node_modules[\\/]/; +const THEMES = fg.globSync('themes/**/*.scss', { cwd: path.resolve(__dirname, 'src') }); +const THEMES_BY_ID = THEMES.reduce((acc, theme) => { + acc[theme.substring(0, theme.lastIndexOf('/'))] = `./${theme}`; + return acc; +}, {}); + const config = { context: path.resolve(__dirname, 'src'), target: 'browserslist', + entry: { + 'main.jellyfin': './index.jsx', + ...THEMES_BY_ID + }, resolve: { extensions: ['.tsx', '.ts', '.js'], modules: [ diff --git a/webpack.dev.js b/webpack.dev.js index 5721f54f76..58c5fbbd68 100644 --- a/webpack.dev.js +++ b/webpack.dev.js @@ -1,26 +1,11 @@ -const common = require('./webpack.common'); const { merge } = require('webpack-merge'); -const THEMES = [ - 'appletv', - 'blueradiance', - 'dark', - 'light', - 'purplehaze', - 'wmc' -]; +const common = require('./webpack.common'); module.exports = merge(common, { // In order for live reload to work we must use "web" as the target not "browserslist" target: process.env.WEBPACK_SERVE ? 'web' : 'browserslist', mode: 'development', - entry: { - 'main.jellyfin': './index.jsx', - ...THEMES.reduce((acc, theme) => { - acc[`themes/${theme}`] = `./themes/${theme}/theme.scss`; - return acc; - }, {}) - }, devtool: 'eval-cheap-module-source-map', module: { rules: [ diff --git a/webpack.prod.js b/webpack.prod.js index 4912329b3b..e45f83f19d 100644 --- a/webpack.prod.js +++ b/webpack.prod.js @@ -1,23 +1,11 @@ -const common = require('./webpack.common'); const { merge } = require('webpack-merge'); -const THEMES = [ - 'appletv', - 'blueradiance', - 'dark', - 'light', - 'purplehaze', - 'wmc' -]; +const common = require('./webpack.common'); module.exports = merge(common, { mode: 'production', entry: { - 'main.jellyfin': './index.jsx', - 'serviceworker': './serviceworker.js', - ...THEMES.reduce((acc, theme) => { - acc[`themes/${theme}`] = `./themes/${theme}/theme.scss`; - return acc; - }, {}) + ...common.entry, + 'serviceworker': './serviceworker.js' } }); From 1fdce1615b8c3c815b8835f6d24ce488c5b804e5 Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Tue, 25 Mar 2025 17:41:15 -0400 Subject: [PATCH 4/4] Fix theme asset references --- webpack.common.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/webpack.common.js b/webpack.common.js index c93ca537e2..b66f0b7611 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -135,6 +135,12 @@ const config = { pathData.chunk.name === 'serviceworker' ? '[name].js' : '[name].bundle.js' ), chunkFilename: '[name].[contenthash].chunk.js', + assetModuleFilename: pathData => { + if (pathData.filename.startsWith('assets/') || pathData.filename.startsWith('themes/')) { + return '[path][base][query]'; + } + return '[hash][ext][query]'; + }, path: path.resolve(__dirname, 'dist'), publicPath: '' }, @@ -317,7 +323,12 @@ const config = { path.resolve(__dirname, 'src/themes/') ], use: [ - MiniCssExtractPlugin.loader, + { + loader: MiniCssExtractPlugin.loader, + options: { + publicPath: '/' + } + }, 'css-loader', { loader: 'postcss-loader',