Skip to content

Commit

Permalink
improve subsequent plugins application & move into own module
Browse files Browse the repository at this point in the history
in the course of this I've also deleted combine-media.js (legacy code that's not used anymore) and introduced a config option to provide more control
  • Loading branch information
SassNinja committed Dec 20, 2019
1 parent 7d6e69e commit 75880bb
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 97 deletions.
31 changes: 30 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,38 @@ By default the plugin uses the `from` value from the options of the loader or of
}
```

### config

By default the plugin looks for a `postcss.config.js` file in your project's root (read [node-app-root-path](https://github.com/inxilpro/node-app-root-path) to understand how root is determined) and tries to apply all subsequent PostCSS plugins to the extracted CSS.

In case this lookup doesn't suite you it's possible to specify the config path yourself.

```javascript
'postcss-extract-media-query': {
config: path.join(__dirname, 'some/path/postcss.config.js')
}
```

It's also possible to pass the config as object to avoid any file resolution.

```javascript
'postcss-extract-media-query': {
config: {
plugins: {
'postcss-extract-media-query': {}
'cssnano': {}
}
}
}
```

## Migration

### coming from 1.x

Both options, `combine` and `minimize`, have been removed in v2 because the plugin parses your `postcss.config.js` now and applies all subsequent plugins to the extracted files as well.

So if you have used them you simply need to install appropriate PostCSS plugins (see below for example) and add them to your config.
So if you have used them you simply need to install appropriate PostCSS plugins (see below for example) and add them to your PostCSS config.

```bash
npm install postcss-combine-media-query cssnano --save-dev
Expand All @@ -145,6 +170,10 @@ plugins: {
}
```

### plugin authors

If you're using this plugin via the api (e.g. for your own plugin) you should note it has changed from sync to async in v2. This was necessary in the course of going with promises. I'm not going to keep support of the sync api because it would make the code more complex than necessary and it's officially recommended to use async. Please check the tests to see how it has to be done now!

## Webpack User?

If you're using webpack you should use [media-query-plugin](https://github.com/SassNinja/media-query-plugin) which is built for webpack only and thus comes with several advantages such as applying all other loaders you've defined and hash support for caching.
Expand Down
34 changes: 0 additions & 34 deletions combine-media.js

This file was deleted.

91 changes: 29 additions & 62 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,9 @@ const fs = require('fs');
const path = require('path');
const { green, yellow } = require('kleur');
const postcss = require('postcss');
const rootPath = require('app-root-path').path
const SubsequentPlugins = require('./subsequent-plugins');

function readConfigFile(file) {
if (file && !path.isAbsolute(file)) {
file = path.join(rootPath, file);
}
const filePath = file || path.join(rootPath, 'postcss.config.js');

if (fs.existsSync(filePath)) {
return require(filePath);
}
return {};
}

const config = readConfigFile();
const allPluginNames = config.plugins ? Object.keys(config.plugins) : [];
const subsequentPluginNames = allPluginNames.slice(allPluginNames.indexOf('postcss-extract-media-query') + 1);
const subsequentPlugins = subsequentPluginNames.map(name => ({ name, mod: require(name), opts: config.plugins[name] }));

function applySubsequentPlugins(css, filePath) {
const plugins = subsequentPlugins.map(plugin => plugin.mod(plugin.opts))

if (plugins.length) {
return new Promise(resolve => {
postcss(plugins)
.process(css, { from: filePath })
.then(result => {
resolve(result.css);
})
});
}
return Promise.resolve(css);
}
const plugins = new SubsequentPlugins();

module.exports = postcss.plugin('postcss-extract-media-query', opts => {

Expand All @@ -51,6 +21,10 @@ module.exports = postcss.plugin('postcss-extract-media-query', opts => {
entry: null
}, opts);

if (opts.config) {
plugins.updateConfig(opts.config);
}

// Deprecation warnings
// TODO: remove in future
if (typeof opts.whitelist === 'boolean') {
Expand Down Expand Up @@ -114,42 +88,35 @@ module.exports = postcss.plugin('postcss-extract-media-query', opts => {

const promises = [];

Object.keys(media).forEach(queryname => {

promises.push(new Promise(resolve => {

let { css } = getMedia(queryname);

if (opts.output.path) {

// gather promises only if output.path specified because otherwise
// nothing has been extracted
if (opts.output.path) {
Object.keys(media).forEach(queryname => {
promises.push(new Promise(resolve => {
let { css } = getMedia(queryname);
const newFile = opts.output.name
.replace(/\[name\]/g, name)
.replace(/\[query\]/g, queryname)
.replace(/\[ext\]/g, ext)

.replace(/\[ext\]/g, ext);
const newFilePath = path.join(opts.output.path, newFile);
const newFileDir = path.dirname(newFilePath);

if (opts.output.path) {

applySubsequentPlugins(css, newFilePath).then(css => {

if (!fs.existsSync(path.dirname(newFilePath))) {
// make sure we can write
fs.mkdirSync(newFileDir, { recursive: true });
}

fs.writeFileSync(newFilePath, css);

if (opts.stats === true) {
console.log(green('[extracted media query]'), newFile);
}
resolve();
});
}
}
}));
});
plugins.applyPlugins(css, newFilePath).then(css => {

if (!fs.existsSync(path.dirname(newFilePath))) {
// make sure we can write
fs.mkdirSync(newFileDir, { recursive: true });
}
fs.writeFileSync(newFilePath, css);

if (opts.stats === true) {
console.log(green('[extracted media query]'), newFile);
}
resolve();
});
}));
});
}

return Promise.all(promises);
};
Expand Down
74 changes: 74 additions & 0 deletions subsequent-plugins.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@

const fs = require('fs');
const path = require('path');
const postcss = require('postcss');
const rootPath = require('app-root-path').path;

class SubsequentPlugins {
constructor() {
this.config = {};
this.updateConfig();
}

/**
* (Re)init with current postcss config
*/
_init() {
this.allNames = this.config.plugins ? Object.keys(this.config.plugins) : [];
this.subsequentNames = this.allNames.slice(this.allNames.indexOf('postcss-extract-media-query') + 1);
this.subsequentPlugins = this.subsequentNames.map(name => ({
name,
mod: this.config.pluginsSrc && this.config.pluginsSrc[name] || require(name),
opts: this.config.plugins[name]
}));
}

/**
* Updates the postcss config by resolving file path
* or by using the config file object
*
* @param {string|Object} file
* @returns {Object}
*/
updateConfig(file) {
if (typeof file === 'object') {
this.config = file;
this._init();
return this.config;
}
if (typeof file === 'string' && !path.isAbsolute(file)) {
file = path.join(rootPath, file);
}
const filePath = file || path.join(rootPath, 'postcss.config.js');

if (fs.existsSync(filePath)) {
this.config = require(filePath);
}
this._init();
return this.config;
}

/**
* Apply all subsequent plugins to the (extracted) css
*
* @param {string} css
* @param {string} filePath
* @returns {string}
*/
applyPlugins(css, filePath) {
const plugins = this.subsequentPlugins.map(plugin => plugin.mod(plugin.opts));

if (plugins.length) {
return new Promise(resolve => {
postcss(plugins)
.process(css, { from: filePath, to: filePath })
.then(result => {
resolve(result.css);
});
});
}
return Promise.resolve(css);
}
}

module.exports = SubsequentPlugins;
39 changes: 39 additions & 0 deletions test/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,43 @@ describe('Options', function() {
});
});

describe('config', function() {
it('should use opts.config if present to apply plugins', (done) => {
let precedingPluginCalls = 0;
const precedingPlugin = postcss.plugin('preceding-plugin', (opts) => {
return (root, result) => {
precedingPluginCalls++;
};
});
let subsequentPluginCalls = 0;
const subsequentPlugin = postcss.plugin('subsequent-plugin', (opts) => {
return (root, result) => {
subsequentPluginCalls++;
};
});
const opts = {
output: {
path: path.join(__dirname, 'output')
},
stats: false,
config: {
pluginsSrc: {
'preceding-plugin': precedingPlugin,
'subsequent-plugin': subsequentPlugin
},
plugins: {
'preceding-plugin': {},
'postcss-extract-media-query': {},
'subsequent-plugin': {}
}
}
};
postcss([ plugin(opts) ]).process(exampleFile, { from: 'test/data/example.css' }).then(() => {
assert.equal(precedingPluginCalls, 0);
assert.isAtLeast(subsequentPluginCalls, 1);
done();
});
});
});

});

0 comments on commit 75880bb

Please sign in to comment.