diff --git a/README.md b/README.md index dbd1d993..d7e08807 100644 --- a/README.md +++ b/README.md @@ -593,6 +593,37 @@ module.exports = { }; ``` +### `implementation` + +Type: `Function` + +The special `implementation` option determines which implementation of PostCSS to use. Overrides the locally installed `peerDependency` version of `postcss`. + +**This option is only really useful for downstream tooling authors to ease the PostCSS 7-to-8 transition.** + +**webpack.config.js** + +```js +module.exports = { + module: { + rules: [ + { + test: /\.css$/i, + use: [ + { loader: "style-loader" }, + { loader: "css-loader" }, + { + loader: "postcss-loader", + options: { implementation: require("postcss") }, + }, + { loader: "sass-loader" }, + ], + }, + ], + }, +}; +``` + ## Examples ### SugarSS diff --git a/src/index.js b/src/index.js index 0aa65bb3..9f5ca512 100644 --- a/src/index.js +++ b/src/index.js @@ -46,6 +46,8 @@ export default async function loader(content, sourceMap, meta) { ? true : options.postcssOptions.config; + const postcssFactory = options.implementation || postcss; + let loadedConfig; if (configOption) { @@ -105,7 +107,10 @@ export default async function loader(content, sourceMap, meta) { let result; try { - result = await postcss(plugins).process(root || content, processOptions); + result = await postcssFactory(plugins).process( + root || content, + processOptions + ); } catch (error) { if (error.file) { this.addDependency(error.file); diff --git a/src/options.json b/src/options.json index 6862b099..255e3cfb 100644 --- a/src/options.json +++ b/src/options.json @@ -35,6 +35,10 @@ "sourceMap": { "description": "Enables/Disables generation of source maps (https://github.com/postcss/postcss-loader#sourcemap)", "type": "boolean" + }, + "implementation": { + "description": "The implementation of postcss to use, instead of the locally installed version (https://github.com/postcss/postcss-loader#implementation)", + "instanceof": "Function" } }, "additionalProperties": false diff --git a/test/__snapshots__/implementation.test.js.snap b/test/__snapshots__/implementation.test.js.snap new file mode 100644 index 00000000..fb74cc52 --- /dev/null +++ b/test/__snapshots__/implementation.test.js.snap @@ -0,0 +1,52 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`"implementation" option should work with a custom instance of PostCSS: css 1`] = ` +"a { + color: black; +} + +a { + color: red; +} + +a { + color: green; +} + +a { + color: blue; +} + +.class { + -x-border-color: blue blue *; + -x-color: * #fafafa; +} + +.class-foo { + -z-border-color: blue blue *; + -z-color: * #fafafa; +} + +.phone { + &_title { + width: 500px; + + @media (max-width: 500px) { + width: auto; + } + + body.is_dark & { + color: white; + } + } + + img { + display: block; + } +} +" +`; + +exports[`"implementation" option should work with a custom instance of PostCSS: errors 1`] = `Array []`; + +exports[`"implementation" option should work with a custom instance of PostCSS: warnings 1`] = `Array []`; diff --git a/test/__snapshots__/validate-options.test.js.snap b/test/__snapshots__/validate-options.test.js.snap index a171555e..adc6357f 100644 --- a/test/__snapshots__/validate-options.test.js.snap +++ b/test/__snapshots__/validate-options.test.js.snap @@ -36,6 +36,36 @@ exports[`validate options should throw an error on the "execute" option with "te -> Enables/Disables PostCSS parser support in 'CSS-in-JS' (https://github.com/postcss/postcss-loader#execute)" `; +exports[`validate options should throw an error on the "implementation" option with "/test/" value 1`] = ` +"Invalid options object. PostCSS Loader has been initialized using an options object that does not match the API schema. + - options.implementation should be an instance of function. + -> The implementation of postcss to use, instead of the locally installed version (https://github.com/postcss/postcss-loader#implementation)" +`; + +exports[`validate options should throw an error on the "implementation" option with "[]" value 1`] = ` +"Invalid options object. PostCSS Loader has been initialized using an options object that does not match the API schema. + - options.implementation should be an instance of function. + -> The implementation of postcss to use, instead of the locally installed version (https://github.com/postcss/postcss-loader#implementation)" +`; + +exports[`validate options should throw an error on the "implementation" option with "{}" value 1`] = ` +"Invalid options object. PostCSS Loader has been initialized using an options object that does not match the API schema. + - options.implementation should be an instance of function. + -> The implementation of postcss to use, instead of the locally installed version (https://github.com/postcss/postcss-loader#implementation)" +`; + +exports[`validate options should throw an error on the "implementation" option with "1" value 1`] = ` +"Invalid options object. PostCSS Loader has been initialized using an options object that does not match the API schema. + - options.implementation should be an instance of function. + -> The implementation of postcss to use, instead of the locally installed version (https://github.com/postcss/postcss-loader#implementation)" +`; + +exports[`validate options should throw an error on the "implementation" option with "something" value 1`] = ` +"Invalid options object. PostCSS Loader has been initialized using an options object that does not match the API schema. + - options.implementation should be an instance of function. + -> The implementation of postcss to use, instead of the locally installed version (https://github.com/postcss/postcss-loader#implementation)" +`; + exports[`validate options should throw an error on the "postcssOptions" option with "{"config":[]}" value 1`] = ` "Invalid options object. PostCSS Loader has been initialized using an options object that does not match the API schema. - options.postcssOptions should be one of these: diff --git a/test/implementation.test.js b/test/implementation.test.js new file mode 100644 index 00000000..57832cc3 --- /dev/null +++ b/test/implementation.test.js @@ -0,0 +1,26 @@ +import postcss from "postcss"; + +import { + compile, + getCompiler, + getErrors, + getCodeFromBundle, + getWarnings, +} from "./helpers"; + +describe('"implementation" option', () => { + it("should work with a custom instance of PostCSS", async () => { + const spy = jest.fn(postcss); + const compiler = getCompiler("./css/index.js", { + // Wrap the spy so it is an instanceof Function + implementation: (...args) => spy(...args), + }); + const stats = await compile(compiler); + const codeFromBundle = getCodeFromBundle("style.css", stats); + + expect(spy).toHaveBeenCalledTimes(1); + expect(codeFromBundle.css).toMatchSnapshot("css"); + expect(getWarnings(stats)).toMatchSnapshot("warnings"); + expect(getErrors(stats)).toMatchSnapshot("errors"); + }); +}); diff --git a/test/validate-options.test.js b/test/validate-options.test.js index 77bd6c1d..7b6c68ea 100644 --- a/test/validate-options.test.js +++ b/test/validate-options.test.js @@ -50,6 +50,10 @@ describe("validate options", () => { success: [true, false], failure: [1, /test/, [], {}, "something"], }, + implementation: { + success: [require("postcss")], + failure: [1, /test/, [], {}, "something"], + }, }; function stringifyValue(value) {