diff --git a/packages/cssnano/package.json b/packages/cssnano/package.json index e552cbb7c..9dbeb2f35 100644 --- a/packages/cssnano/package.json +++ b/packages/cssnano/package.json @@ -24,7 +24,8 @@ ], "license": "MIT", "dependencies": { - "cosmiconfig": "^7.0.0", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2", "cssnano-preset-default": "^5.1.3", "is-resolvable": "^1.1.0" }, diff --git a/packages/cssnano/src/__tests__/config_loading/.cssnanorc.json b/packages/cssnano/src/__tests__/config_loading/.cssnanorc.json new file mode 100644 index 000000000..2495f19bd --- /dev/null +++ b/packages/cssnano/src/__tests__/config_loading/.cssnanorc.json @@ -0,0 +1,3 @@ +{ + "preset": "lite" +} diff --git a/packages/cssnano/src/__tests__/config_loading/config-loading.js b/packages/cssnano/src/__tests__/config_loading/config-loading.js new file mode 100644 index 000000000..b238ad41e --- /dev/null +++ b/packages/cssnano/src/__tests__/config_loading/config-loading.js @@ -0,0 +1,22 @@ +import process from 'process'; +import postcss from 'postcss'; +import litePreset from 'cssnano-preset-lite'; +import defaultPreset from 'cssnano-preset-default'; +import cssnano from '../..'; + +/* The configuration is loaded relative to the current working directory, + when running the repository tests, the working directory is + the repostiory root, so we need to change it to avoid having to place + the configuration file for this test in the repo root */ +const spy = jest.spyOn(process, 'cwd'); +spy.mockReturnValue(__dirname); + +test('should read the cssnano configuration file', () => { + const processor = postcss([cssnano]); + expect(processor.plugins.length).toBe(litePreset().plugins.length); +}); + +test('PostCSS config should override the cssnano config', () => { + const processor = postcss([cssnano({ preset: 'default' })]); + expect(processor.plugins.length).toBe(defaultPreset().plugins.length); +}); diff --git a/packages/cssnano/src/index.js b/packages/cssnano/src/index.js index 69674c234..4e3558adf 100644 --- a/packages/cssnano/src/index.js +++ b/packages/cssnano/src/index.js @@ -1,6 +1,7 @@ import path from 'path'; import postcss from 'postcss'; -import { cosmiconfigSync } from 'cosmiconfig'; +import yaml from 'yaml'; +import { lilconfigSync } from 'lilconfig'; import isResolvable from 'is-resolvable'; const cssnano = 'cssnano'; @@ -59,7 +60,7 @@ function resolvePreset(preset) { /* * cssnano will look for configuration firstly as options passed - * directly to it, and failing this it will use cosmiconfig to + * directly to it, and failing this it will use lilconfig to * load an external file. */ @@ -76,7 +77,21 @@ function resolveConfig(options) { configPath = path.resolve(process.cwd(), options.configFile); } - const configExplorer = cosmiconfigSync(cssnano); + const configExplorer = lilconfigSync(cssnano, { + searchPlaces: [ + 'package.json', + '.cssnanorc', + '.cssnanorc.json', + '.cssnanorc.yaml', + '.cssnanorc.yml', + '.cssnanorc.js', + 'cssnano.config.js', + ], + loaders: { + '.yaml': (filepath, content) => yaml.parse(content), + '.yml': (filepath, content) => yaml.parse(content), + }, + }); const config = configPath ? configExplorer.load(configPath) : configExplorer.search(searchPath); diff --git a/site/docs/config-file.mdx b/site/docs/config-file.mdx index 0a6a6d34f..ea021f151 100644 --- a/site/docs/config-file.mdx +++ b/site/docs/config-file.mdx @@ -21,10 +21,31 @@ export const ExampleChart = () => { -## `cssnano` config files +You can configure cssnano either in the PostCSS configuration file or in a dedicated cssnano configuration file. The PostCSS configuration file takes precedence over the dedicated cssnano configuration. +If you don't pass any configuration, cssnano runs with the `default` preset. + + +## Configure through PostCSS configuration files + +In the [PostCSS configuration file](https://github.com/postcss/postcss#usage), you can pass both the `preset` and `plugins` options when you add `cssnano` to the PostCSS plugins. For example, if you use PostCSS programmatically, the following uses cssnano with the `lite` preset and adds autoprefixer. + +```js +import postcss from 'postcss'; +import cssnano from 'cssnano'; +import litePreset from 'cssnano-preset-lite'; +import autoprefixer from 'autoprefixer'; +const preset = litePreset({ discardComments: false }); + +postcss([cssnano({ preset, plugins: [autoprefixer] })]).process("/* Your CSS here */") +``` + +## Configure through dedicated `cssnano` configuration + +If you cannot configure cssnano in the PostCSS configuration file, you can configure cssnano with a cssnano configuration option in `package.json` or with a dedicated configuration file. This file can be in different formats. + +* `.cssnanorc.config.json` and `.cssnanorc` must contain a JSON object +* `cssnano.config.js` must export the configuration as a JavaScript object -We use configuration for `cssnano` using file name as `cssnano.config.js` , `cssnano` property in your `package.json`, using `cssnano.config.json` and `.cssnanorc` as well. -We are using `cosmiconfig` in order to load cssnano config. Please read [here](https://github.com/davidtheclark/cosmiconfig) for more details ## Options @@ -32,35 +53,52 @@ We are using `cosmiconfig` in order to load cssnano config. Please read [here](h - **Type:** `string` | `function` | `[string, Objects]` | `[function(preset options here)]` -> For `string`, the name should be of type `cssnano-preset-` and you need to use `name` alone eg : `preset : ['default', {}]` +Pass a preset to choose a pre-configured set of optimizations. You can either import the preset package or use the preset name as a string. -Contains lists of `postcss` plugins where each plugin does their own minification. +With the preset as import: -- **Example:** - - ```js - // cssnano.config.js - module.exports = { - preset: [require('cssnano-preset-default')] - - // or - preset: require('cssnano-preset-default') - - // or - preset: ['advanced', { discardComments: false }] +```js +cssnano({ preset: require('cssnano-preset-default') }) +``` + +Using a string is useful if you use a configuration file in the JSON format. +When you use a string, if the preset is called `cssnano-preset-`, use `name` alone: + +```js +cssnano({ preset: 'default' }) +``` + +Presets themselves can take options. +Pass options to the preset by using an array where the first element is the preset and the second is an object with the preset options. +You can specify a preset with the preset name as a string or by passing the result of importing the preset package. + +```js +// cssnano.config.js +module.exports = { + preset: [ require('cssnano-preset-default'), { discardComments: false } ] +}; +``` + + +You can also pass preset options when you use the preset name as a string: +For example, here's how to deactivate the `discardComments` plugin when using the `advanced` preset: + +```js +cssnano({ preset: ['advanced', { discardComments: false }] }) +``` - // or - preset: [require('cssnano-preset-default'), {discardComments: false}] - } - ``` ### `plugins` - **Type:** `Array<'string' | 'function' | ['string' | 'function', Object]>` -> If you want to pass config to the plugin, you need to use Array's of array for the plugins i.e `plugins: [ ['autoprefixer', {}] ]` +In addition to the preset, you can pass a list of plugins to cssnano. +This is equivalent to adding the plugins after cssnano in the PostCSS plugins array. +If you want to configure the individual plugins, use an array of arrays: -These plugins will run after once all presets operations are complete. +```js +cssnano({ plugins: [['autoprefixer', {}]] }) +``` - **Example:** @@ -84,8 +122,3 @@ These plugins will run after once all presets operations are complete. ] } ``` - -## Alternatives - -You can use the `postcss` config file and both `preset` and `plugins` will be passed as the options for `cssnano`. -Refer [here](https://github.com/postcss/postcss#usage) for more details. diff --git a/yarn.lock b/yarn.lock index 37d9b0dd0..702c7a50b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6325,6 +6325,11 @@ libnpmpublish@^4.0.0: semver "^7.1.3" ssri "^8.0.1" +lilconfig@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.3.tgz#68f3005e921dafbd2a2afb48379986aa6d2579fd" + integrity sha512-EHKqr/+ZvdKCifpNrJCKxBTgk5XupZA3y/aCPY9mxfgBzmgh93Mt/WqjjQ38oMxXuvDokaKiM3lAgvSH2sjtHg== + lines-and-columns@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" @@ -7942,12 +7947,13 @@ postcss-font-magician@^3.0.0: google-fonts-complete "^2.1.1" postcss-load-config@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.0.1.tgz#d214bf9cfec1608ffaf0f4161b3ba20664ab64b9" - integrity sha512-/pDHe30UYZUD11IeG8GWx9lNtu1ToyTsZHnyy45B4Mrwr/Kb6NgYl7k753+05CJNKnjbwh4975amoPJ+TEjHNQ== + version "3.1.0" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.0.tgz#d39c47091c4aec37f50272373a6a648ef5e97829" + integrity sha512-ipM8Ds01ZUophjDTQYSVP70slFSYg3T0/zyfII5vzhN6V57YSxMgG5syXuwi5VtS8wSf3iL30v0uBdoIVx4Q0g== dependencies: - cosmiconfig "^7.0.0" import-cwd "^3.0.0" + lilconfig "^2.0.3" + yaml "^1.10.2" postcss-reporter@^1.3.0: version "1.4.1" @@ -10270,7 +10276,7 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@^1.10.0: +yaml@^1.10.0, yaml@^1.10.2: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==