diff --git a/package-lock.json b/package-lock.json index 9c87933f6..2db593f33 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", @@ -11376,7 +11377,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 9fa3e2f56..01cba9161 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/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 diff --git a/webpack.common.js b/webpack.common.js index 3534e683e..b66f0b761 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: [ @@ -60,13 +71,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 +119,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: { @@ -114,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: '' }, @@ -288,33 +315,46 @@ 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: [ + { + loader: MiniCssExtractPlugin.loader, + options: { + publicPath: '/' + } + }, + '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 +381,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 a6288a80c..58c5fbbd6 100644 --- a/webpack.dev.js +++ b/webpack.dev.js @@ -1,11 +1,11 @@ -const common = require('./webpack.common'); const { merge } = require('webpack-merge'); +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' }, devtool: 'eval-cheap-module-source-map', module: { rules: [ diff --git a/webpack.prod.js b/webpack.prod.js index 8b2c9aad5..e45f83f19 100644 --- a/webpack.prod.js +++ b/webpack.prod.js @@ -1,10 +1,11 @@ -const common = require('./webpack.common'); const { merge } = require('webpack-merge'); +const common = require('./webpack.common'); + module.exports = merge(common, { mode: 'production', entry: { - 'main.jellyfin': './index.jsx', + ...common.entry, 'serviceworker': './serviceworker.js' } });