Skip to content

Commit

Permalink
Feat: Allow dynamic config in @snowpack/plugin-postcss
Browse files Browse the repository at this point in the history
  • Loading branch information
drwpow committed May 20, 2021
1 parent eae9121 commit b7d7424
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 23 deletions.
8 changes: 4 additions & 4 deletions plugins/plugin-postcss/README.md
Expand Up @@ -32,7 +32,7 @@ module.exports = {

### Plugin Options

| Name | Type | Description |
| :------- | :--------: | :------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `input` | `string[]` | File extensions to transform (default: `['.css']`) |
| `config` | `string` | (optional) Set a custom path to your PostCSS config (in case PostCSS has trouble loading it automatically, or in case you want multiple PostCSS setups) |
| Name | Type | Description |
| :------- | :----------------: | :-------------------------------------------------------------------------------- |
| `input` | `string[]` | File extensions to transform (default: `['.css']`) |
| `config` | `string \| object` | (optional) Pass in a PostCSS config object or path to `postcss.config.js` on disk |
17 changes: 10 additions & 7 deletions plugins/plugin-postcss/plugin.js
@@ -1,15 +1,18 @@
'use strict';

const {resolve, relative, isAbsolute} = require('path');
const path = require('path');
const workerpool = require('workerpool');

module.exports = function postcssPlugin(snowpackConfig, options) {
// options validation
if (options) {
if (typeof options !== 'object' || Array.isArray(options))
throw new Error('options isn’t an object. Please see README.');
if (options.config && typeof options.config !== 'string')
throw new Error('options.config must be a path to a PostCSS config file.');
if (
(options.config && typeof options !== 'string' && typeof options !== 'object') ||
Array.isArray(options)
)
throw new Error('options.config must be a config object or a path to a PostCSS config file.');
}

let worker, pool;
Expand All @@ -23,8 +26,8 @@ module.exports = function postcssPlugin(snowpackConfig, options) {

if (!input.includes(fileExt) || !contents) return;

if (config) {
config = resolve(config);
if (config && typeof config === 'string') {
config = path.resolve(config);
}

pool = pool || workerpool.pool(require.resolve('./worker.js'));
Expand Down Expand Up @@ -72,9 +75,9 @@ module.exports = function postcssPlugin(snowpackConfig, options) {
}
for (const dir of dirs) {
// https://stackoverflow.com/a/45242825
const relativePath = relative(dir, filePath);
const relativePath = path.relative(dir, filePath);
const dirContainsFilePath =
relativePath && !relativePath.startsWith('..') && !isAbsolute(relativePath);
relativePath && !relativePath.startsWith('..') && !path.isAbsolute(relativePath);

if (dirContainsFilePath) {
this.markChanged(id);
Expand Down
File renamed without changes.
33 changes: 23 additions & 10 deletions plugins/plugin-postcss/test/plugin.test.js
Expand Up @@ -2,15 +2,14 @@ const fs = require('fs');
const path = require('path');
const plugin = require('../plugin.js');

const cssPath = path.resolve(__dirname, 'stubs', 'style.css');
const minCssPath = path.resolve(__dirname, 'stubs', 'style.min.css');
const cssPath = path.join(__dirname, 'fixtures', 'style.css');
const minCssPath = path.join(__dirname, 'fixtures', 'style.min.css');
const cssContent = fs.readFileSync(cssPath, 'utf8');
const minCssContent = fs.readFileSync(minCssPath, 'utf8');
const configFilePath = path.resolve(__dirname, './stubs/postcss.config.js');

describe('@snowpack/plugin-postcss', () => {
test('loads postcss config with no options', async () => {
const pluginInstance = plugin({root: path.resolve(__dirname, 'stubs')}, {});
const pluginInstance = plugin({root: path.join(__dirname, 'fixtures')}, {});
const transformCSSResults = await pluginInstance.transform({
id: cssPath,
fileExt: path.extname(cssPath),
Expand All @@ -23,10 +22,10 @@ describe('@snowpack/plugin-postcss', () => {
});

test('accepts a path to a config file', async () => {
const options = {
config: path.resolve(configFilePath),
};
const pluginInstance = plugin({}, options);
const pluginInstance = plugin(
{},
{config: path.join(__dirname, 'fixtures', 'postcss.config.js')},
);
const transformCSSResults = await pluginInstance.transform({
id: cssPath,
fileExt: path.extname(cssPath),
Expand All @@ -40,7 +39,7 @@ describe('@snowpack/plugin-postcss', () => {

test('produces source maps with sourceMaps: true', async () => {
const pluginInstance = plugin(
{root: path.resolve(__dirname, 'stubs'), buildOptions: {sourceMaps: true}},
{root: path.join(__dirname, 'fixtures'), buildOptions: {sourceMaps: true}},
{},
);
const transformCSSResults = await pluginInstance.transform({
Expand All @@ -61,7 +60,7 @@ describe('@snowpack/plugin-postcss', () => {
});

test('bails with empty input array', async () => {
const pluginInstance = plugin({root: path.resolve(__dirname, 'stubs')}, {input: []});
const pluginInstance = plugin({root: path.join(__dirname, 'fixtures')}, {input: []});
const transformCSSResults = await pluginInstance.transform({
id: cssPath,
fileExt: path.extname(cssPath),
Expand All @@ -70,4 +69,18 @@ describe('@snowpack/plugin-postcss', () => {
expect(transformCSSResults).toBeFalsy();
await pluginInstance.cleanup();
});

test('allows dynamic config', async () => {
// important: make sure to NOT set {root:} here so it doesn’t automatically pick up fixtures/postcss.config.js
const pluginInstance = plugin({}, {config: {plugins: {cssnano: {}}}});
const transformCSSResults = await pluginInstance.transform({
id: cssPath,
fileExt: path.extname(cssPath),
contents: cssContent,
});
expect(transformCSSResults.code).toBe(minCssContent); // TODO: remove this?
expect(transformCSSResults.contents).toBe(minCssContent);
expect(transformCSSResults.map).toBe(undefined);
await pluginInstance.cleanup();
});
});
15 changes: 13 additions & 2 deletions plugins/plugin-postcss/worker.js
Expand Up @@ -3,15 +3,26 @@
const workerpool = require('workerpool');
const postcss = require('postcss');
const postcssrc = require('postcss-load-config');
const loadPlugins = require('postcss-load-config/src/plugins.js');
const loadOptions = require('postcss-load-config/src/options.js');

let process = null;

async function transformAsync(css, {filepath, config, cwd, map}) {
// Initialize processor. `config`, `cwd` won't change until Snowpack is restarted
if (!process) {
const {plugins, rcOptions} = await postcssrc({}, config || cwd);
let plugins = [];
let options = {};
if (typeof config === 'object') {
plugins = loadPlugins(config);
options = loadOptions(config);
} else {
const rc = await await postcssrc({}, config || cwd);
plugins = rc.plugins;
options = rc.options;
}
const processor = postcss(plugins);
process = (css) => processor.process(css, {...rcOptions, from: filepath, map});
process = (css) => processor.process(css, {...options, from: filepath, map});
}

const result = await process(css);
Expand Down

0 comments on commit b7d7424

Please sign in to comment.