From b7d742489d7d91f9790790bac92e384305acd4b8 Mon Sep 17 00:00:00 2001 From: Drew Powers Date: Thu, 20 May 2021 14:58:38 -0600 Subject: [PATCH] Feat: Allow dynamic config in @snowpack/plugin-postcss --- plugins/plugin-postcss/README.md | 8 ++--- plugins/plugin-postcss/plugin.js | 17 ++++++---- .../{stubs => fixtures}/postcss.config.js | 0 .../test/{stubs => fixtures}/style.css | 0 .../test/{stubs => fixtures}/style.min.css | 0 plugins/plugin-postcss/test/plugin.test.js | 33 +++++++++++++------ plugins/plugin-postcss/worker.js | 15 +++++++-- 7 files changed, 50 insertions(+), 23 deletions(-) rename plugins/plugin-postcss/test/{stubs => fixtures}/postcss.config.js (100%) rename plugins/plugin-postcss/test/{stubs => fixtures}/style.css (100%) rename plugins/plugin-postcss/test/{stubs => fixtures}/style.min.css (100%) diff --git a/plugins/plugin-postcss/README.md b/plugins/plugin-postcss/README.md index 12f66d036c..bc811d3d0b 100644 --- a/plugins/plugin-postcss/README.md +++ b/plugins/plugin-postcss/README.md @@ -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 | diff --git a/plugins/plugin-postcss/plugin.js b/plugins/plugin-postcss/plugin.js index b539f37b22..cd6f08bd06 100644 --- a/plugins/plugin-postcss/plugin.js +++ b/plugins/plugin-postcss/plugin.js @@ -1,6 +1,6 @@ 'use strict'; -const {resolve, relative, isAbsolute} = require('path'); +const path = require('path'); const workerpool = require('workerpool'); module.exports = function postcssPlugin(snowpackConfig, options) { @@ -8,8 +8,11 @@ module.exports = function postcssPlugin(snowpackConfig, options) { 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; @@ -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')); @@ -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); diff --git a/plugins/plugin-postcss/test/stubs/postcss.config.js b/plugins/plugin-postcss/test/fixtures/postcss.config.js similarity index 100% rename from plugins/plugin-postcss/test/stubs/postcss.config.js rename to plugins/plugin-postcss/test/fixtures/postcss.config.js diff --git a/plugins/plugin-postcss/test/stubs/style.css b/plugins/plugin-postcss/test/fixtures/style.css similarity index 100% rename from plugins/plugin-postcss/test/stubs/style.css rename to plugins/plugin-postcss/test/fixtures/style.css diff --git a/plugins/plugin-postcss/test/stubs/style.min.css b/plugins/plugin-postcss/test/fixtures/style.min.css similarity index 100% rename from plugins/plugin-postcss/test/stubs/style.min.css rename to plugins/plugin-postcss/test/fixtures/style.min.css diff --git a/plugins/plugin-postcss/test/plugin.test.js b/plugins/plugin-postcss/test/plugin.test.js index f70332808e..5d0fdb6996 100644 --- a/plugins/plugin-postcss/test/plugin.test.js +++ b/plugins/plugin-postcss/test/plugin.test.js @@ -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), @@ -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), @@ -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({ @@ -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), @@ -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(); + }); }); diff --git a/plugins/plugin-postcss/worker.js b/plugins/plugin-postcss/worker.js index 110eca5043..58ee6aedce 100644 --- a/plugins/plugin-postcss/worker.js +++ b/plugins/plugin-postcss/worker.js @@ -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);