diff --git a/README.md b/README.md index 96c89a5..5d940a5 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,9 @@ Library for configuring style _(css/scss/sass)_ modules to generate compressed classes (`.header` -> `.a`, `.nav` -> `.b`, ..., `.footer` -> `.aad`, etc.) with support for changes and rebuilding without clearing the built application. +### **Important** +**This description is for `>=2.2.0`. See instructions for previous versions at [next-classnames-minifier/tree/2.1.1](https://github.com/vordgi/next-classnames-minifier/tree/2.1.1)** + ## Reasons *Compressing classes* can reduce the size of the generated html and css by up to *20%*, which will have a positive effect on page rendering and metrics (primarily [FCP](https://web.dev/first-contentful-paint/)) @@ -27,7 +30,7 @@ Create `next.config.js` file in your project and apply the library. ```js const withClassnamesMinifier = require('next-classnames-minifier').default; -module.exports = withClassnamesMinifier()({ +module.exports = withClassnamesMinifier({ type: process.env.NODE_ENV === 'development' ? 'none' : 'minified' })({ // next.js config }); ``` @@ -38,11 +41,12 @@ const withClassnamesMinifier = require('next-classnames-minifier').default; const withPlugins = require('next-compose-plugins'); module.exports = withPlugins([ - [withClassnamesMinifier()] + [withClassnamesMinifier({ type: process.env.NODE_ENV === 'development' ? 'none' : 'minified' })] ], nextConfig); ``` ## Configuration + next-classname-minifier has 3 types of changing classnames: * minified — the main option. It is not recommended to use this option in development mode, it may slow down the update; @@ -54,14 +58,14 @@ You can choose different options for development and production. Configuration example: ```js module.exports = withPlugins([ - [withClassnamesMinifier({ dev: 'none', prod: 'minified' })] + [withClassnamesMinifier({ type: process.env.NODE_ENV === 'development' ? 'none' : 'minified' })] ], nextConfig); ``` Custom mode example: ```js module.exports = withPlugins([ - [withClassnamesMinifier({ dev: { type: 'custom', templateString: '[path][name]__[local]_[hash:base64:5]' }, prod: 'minified' })] + [withClassnamesMinifier({ type: 'custom', templateString: '[path][name]__[local]_[hash:base64:5]' })] ], nextConfig); ``` diff --git a/package-lock.json b/package-lock.json index 5409e4b..d5ba9c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "next-classnames-minifier", - "version": "2.1.1", + "version": "2.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "next-classnames-minifier", - "version": "2.1.1", + "version": "2.2.0", "license": "MIT", "dependencies": { "uuid": "9.0.1" diff --git a/package.json b/package.json index e0527a8..00378a7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "next-classnames-minifier", - "version": "2.1.1", + "version": "2.2.0", "description": "Library for configuring style modules to generate compressed classes", "main": "dist/withClassnamesMinifier.js", "types": "dist/withClassnamesMinifier.d.ts", diff --git a/src/lib/types/plugin.ts b/src/lib/types/plugin.ts index 210d675..32924f4 100644 --- a/src/lib/types/plugin.ts +++ b/src/lib/types/plugin.ts @@ -1,9 +1,10 @@ -import { VALID_MINIFIERS_KEYS, CUSTOM, MINIFIED, NONE } from '../constants/minifiers'; +import { VALID_MINIFIERS_KEYS } from '../constants/minifiers'; import type BaseConverter from '../converters/ConverterBase'; -export type MINIFIER_KEY = typeof VALID_MINIFIERS_KEYS; -export type Config = typeof MINIFIED | typeof NONE | { type: typeof CUSTOM, templateString: string }; -export type Options = { dev?: Config, prod?: Config }; +export type Config = { + type?: (typeof VALID_MINIFIERS_KEYS)[number]; + templateString?: string; +} export type InjectConfig = { localIdentName?: string, classnamesMinifier: BaseConverter diff --git a/src/lib/validateConfig.ts b/src/lib/validateConfig.ts new file mode 100644 index 0000000..c2c0f46 --- /dev/null +++ b/src/lib/validateConfig.ts @@ -0,0 +1,44 @@ +import { CUSTOM, VALID_MINIFIERS_KEYS } from "./constants/minifiers"; + +type Config = { + type?: (typeof VALID_MINIFIERS_KEYS)[number]; + templateString?: string; +} + +const validKeys = ['type', 'templateString']; + +const validateIsObject = (config: unknown): config is Config => { + if (!config) return false; + + if (typeof config !== 'object' || Array.isArray(config)) { + console.error(`next-classnames-minifier: Invalid configuration. Expected object, received ${typeof config}. See https://github.com/vordgi/next-classnames-minifier#configuration`); + process.exit(); + } + + const isValidKeys = Object.keys(config).every(key => validKeys.includes(key)); + + if (!isValidKeys) { + console.error(`next-classnames-minifier: Invalid configuration. Valid keys are: ${validKeys.join(', ')}. See https://github.com/vordgi/next-classnames-minifier#configuration`); + process.exit(); + } + + return true; +} + +const validateConfig = (config: unknown = {}): Config => { + if (!validateIsObject(config)) return {}; + + if (config.type && !VALID_MINIFIERS_KEYS.includes(config.type)) { + console.error(`next-classnames-minifier: Invalid configuration. Valid types are: ${VALID_MINIFIERS_KEYS.join(', ')}. See https://github.com/vordgi/next-classnames-minifier#configuration`) + process.exit(); + } + + if (config.type === CUSTOM && !config.templateString) { + console.error('next-classnames-minifier: Invalid configuration. The templateString option is required for the "custom" type. See https://github.com/vordgi/next-classnames-minifier#configuration') + process.exit(); + } + + return config; +} + +export default validateConfig; diff --git a/src/withClassnamesMinifier.ts b/src/withClassnamesMinifier.ts index 134bee6..7e7bf18 100644 --- a/src/withClassnamesMinifier.ts +++ b/src/withClassnamesMinifier.ts @@ -1,59 +1,45 @@ import type { Configuration } from 'webpack/types'; -import type { Options } from './lib/types/plugin'; +import type { Config } from './lib/types/plugin'; import type ConverterBase from './lib/converters/ConverterBase'; -import { CUSTOM, MINIFIED, VALID_MINIFIERS_KEYS } from './lib/constants/minifiers'; +import { CUSTOM, MINIFIED } from './lib/constants/minifiers'; import ConverterMinified from './lib/converters/ConverterMinified'; import ConverterCustom from './lib/converters/ConverterCustom'; import injectConfig from './lib/injectConfig'; +import validateConfig from './lib/validateConfig'; import path from 'path'; let classnamesMinifier: ConverterBase; -let infoMessageShown = false; - -const withClassnameMinifier = (pluginOptions: Options = {}) => (nextConfig: any = {}) => ({ - ...nextConfig, - webpack: (config: Configuration, options: any) => { - const { dev = 'none', prod = 'minified' } = pluginOptions; - const isProd = process.env.NODE_ENV === 'production'; - const minifierConfig = isProd ? prod : dev; - const minifierType = typeof minifierConfig === 'string' ? minifierConfig : minifierConfig.type; - - if (!infoMessageShown) { - if (!VALID_MINIFIERS_KEYS.includes(minifierType)) { - console.log(`next-classnames-minifier. Invalid key for target env: ${minifierType}, valid keys are: ${VALID_MINIFIERS_KEYS.join(', ')}`); - process.kill(0); - process.exit(); - } else if (!isProd && minifierType === MINIFIED) { - console.log(`next-classnames-minifier. It is not recommended to use "minified" mode in development mode, it may slow down the update`); - } else if (minifierType === 'custom' && (typeof minifierConfig !== 'object' || !minifierConfig.templateString)) { - console.log(`next-classnames-minifier. Add templateString for custom minifier`); - process.kill(0); - process.exit(); - } - infoMessageShown = true; - } - - if (minifierType === MINIFIED) { - if (!classnamesMinifier) { - const cacheDir = path.join(process.cwd(), '.next/cache/ncm'); - classnamesMinifier = new ConverterMinified(cacheDir); - } - injectConfig({ classnamesMinifier }, config.module?.rules); - } else if (minifierType === CUSTOM && typeof minifierConfig === 'object' && minifierConfig.templateString) { - if (!classnamesMinifier) { - classnamesMinifier = new ConverterCustom(); +const withClassnameMinifier = (pluginOptions: Config = {}) => { + validateConfig(pluginOptions); + + return (nextConfig: any = {}) => ({ + ...nextConfig, + webpack: (config: Configuration, options: any) => { + const { type = MINIFIED, templateString } = pluginOptions; + + if (type === MINIFIED) { + if (!classnamesMinifier) { + const cacheDir = path.join(process.cwd(), '.next/cache/ncm'); + classnamesMinifier = new ConverterMinified(cacheDir); + } + + injectConfig({ classnamesMinifier }, config.module?.rules); + } else if (type === CUSTOM && templateString) { + if (!classnamesMinifier) { + classnamesMinifier = new ConverterCustom(); + } + + injectConfig({ localIdentName: templateString, classnamesMinifier }, config.module?.rules); } - injectConfig({ localIdentName: minifierConfig.templateString, classnamesMinifier }, config.module?.rules); - } + if (typeof nextConfig.webpack === 'function') { + return nextConfig.webpack(config, options); + } - if (typeof nextConfig.webpack === 'function') { - return nextConfig.webpack(config, options); + return config; } - - return config; - } -}); + }) +}; export default withClassnameMinifier;