diff --git a/CHANGELOG.md b/CHANGELOG.md index d631bb40..99237338 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [6.1.0](https://github.com/webpack-contrib/postcss-loader/compare/v6.0.0...v6.1.0) (2021-06-10) + + +### Features + +* allow `String` value for the "implementation" option ([0d342b1](https://github.com/webpack-contrib/postcss-loader/commit/0d342b16dabf58c499da4e13310fdfa5c05badd9)) + ## [6.0.0](https://github.com/webpack-contrib/postcss-loader/compare/v5.3.0...v6.0.0) (2021-06-10) diff --git a/README.md b/README.md index 321c507b..c2207791 100644 --- a/README.md +++ b/README.md @@ -597,12 +597,14 @@ module.exports = { ### `implementation` -Type: `Function` +Type: `Function | String` 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.** +#### Function + **webpack.config.js** ```js @@ -626,6 +628,31 @@ module.exports = { }; ``` +#### String + +**webpack.config.js** + +```js +module.exports = { + module: { + rules: [ + { + test: /\.css$/i, + use: [ + { loader: "style-loader" }, + { loader: "css-loader" }, + { + loader: "postcss-loader", + options: { implementation: require.resolve("postcss") }, + }, + { loader: "sass-loader" }, + ], + }, + ], + }, +}; +``` + ## Examples ### SugarSS diff --git a/package-lock.json b/package-lock.json index cba0b813..31794ead 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "postcss-loader", - "version": "6.0.0", + "version": "6.1.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 5cff907f..5586fdbf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "postcss-loader", - "version": "6.0.0", + "version": "6.1.0", "description": "PostCSS loader for webpack", "license": "MIT", "repository": "webpack-contrib/postcss-loader", diff --git a/src/index.js b/src/index.js index 3a7f2a30..4e4d6e30 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,5 @@ import path from "path"; -import postcss from "postcss"; import { satisfies } from "semver"; import postcssPackage from "postcss/package.json"; @@ -14,6 +13,7 @@ import { normalizeSourceMap, normalizeSourceMapAfterPostcss, findPackageJSONDir, + getPostcssImplementation, } from "./utils"; let hasExplicitDependencyOnPostCSS = false; @@ -40,7 +40,17 @@ export default async function loader(content, sourceMap, meta) { ? true : options.postcssOptions.config; - const postcssFactory = options.implementation || postcss; + const postcssFactory = getPostcssImplementation(this, options.implementation); + + if (!postcssFactory) { + callback( + new Error( + `The Postcss implementation "${options.implementation}" not found` + ) + ); + + return; + } let loadedConfig; diff --git a/src/options.json b/src/options.json index 3c2a6ef8..6a3220d3 100644 --- a/src/options.json +++ b/src/options.json @@ -39,7 +39,14 @@ }, "implementation": { "description": "The implementation of postcss to use, instead of the locally installed version (https://github.com/postcss/postcss-loader#implementation)", - "instanceof": "Function" + "anyOf": [ + { + "type": "string" + }, + { + "instanceof": "Function" + } + ] } }, "additionalProperties": false diff --git a/src/utils.js b/src/utils.js index 0e5d9214..03cebe57 100644 --- a/src/utils.js +++ b/src/utils.js @@ -433,6 +433,27 @@ function findPackageJSONDir(cwd, statSync) { return dir; } +function getPostcssImplementation(loaderContext, implementation) { + let resolvedImplementation = implementation; + + if (!implementation || typeof implementation === "string") { + const postcssImplPkg = implementation || "postcss"; + + try { + // eslint-disable-next-line import/no-dynamic-require, global-require + resolvedImplementation = require(postcssImplPkg); + } catch (error) { + loaderContext.emitError(error); + + // eslint-disable-next-line consistent-return + return; + } + } + + // eslint-disable-next-line consistent-return + return resolvedImplementation; +} + export { loadConfig, getPostcssOptions, @@ -440,4 +461,5 @@ export { normalizeSourceMap, normalizeSourceMapAfterPostcss, findPackageJSONDir, + getPostcssImplementation, }; diff --git a/test/__snapshots__/implementation.test.js.snap b/test/__snapshots__/implementation.test.js.snap index fb74cc52..f27b0afc 100644 --- a/test/__snapshots__/implementation.test.js.snap +++ b/test/__snapshots__/implementation.test.js.snap @@ -1,5 +1,17 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`"implementation" option should throw error when unresolved package: errors 1`] = ` +Array [ + "ModuleBuildError: Module build failed (from \`replaced original path\`): +Error: The Postcss implementation \\"unresolved\\" not found + at Object.loader (/src/index.js:47:7)", + "ModuleError: Module Error (from \`replaced original path\`): +(Emitted value instead of an instance of Error) Error: Cannot find module 'unresolved' from 'src/utils.js'", +] +`; + +exports[`"implementation" option should throw error when unresolved package: warnings 1`] = `Array []`; + exports[`"implementation" option should work with a custom instance of PostCSS: css 1`] = ` "a { color: black; @@ -50,3 +62,54 @@ a { 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 []`; + +exports[`"implementation" option should work with implementation is string: 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 implementation is string: errors 1`] = `Array []`; + +exports[`"implementation" option should work with implementation is string: warnings 1`] = `Array []`; diff --git a/test/__snapshots__/loader.test.js.snap b/test/__snapshots__/loader.test.js.snap index c90c167a..48790a2d 100644 --- a/test/__snapshots__/loader.test.js.snap +++ b/test/__snapshots__/loader.test.js.snap @@ -5,7 +5,7 @@ Array [ "ModuleBuildError: Module build failed (from \`replaced original path\`): Error: Something went wrong. at Processor.process (/test/loader.test.js:216:26) - at Object.loader (/src/index.js:106:30)", + at Object.loader (/src/index.js:116:30)", ] `; @@ -21,7 +21,7 @@ Array [ "ModuleBuildError: Module build failed (from \`replaced original path\`): Error: Something went wrong. at Processor.process (/test/loader.test.js:300:26) - at Object.loader (/src/index.js:106:30)", + at Object.loader (/src/index.js:116:30)", ] `; @@ -32,7 +32,7 @@ Array [ "ModuleBuildError: Module build failed (from \`replaced original path\`): Error: Something went wrong. at Processor.process (/test/loader.test.js:245:26) - at Object.loader (/src/index.js:106:30)", + at Object.loader (/src/index.js:116:30)", ] `; @@ -43,7 +43,7 @@ Array [ "ModuleBuildError: Module build failed (from \`replaced original path\`): Error: Something went wrong. at Processor.process (/test/loader.test.js:274:26) - at Object.loader (/src/index.js:106:30)", + at Object.loader (/src/index.js:116:30)", ] `; diff --git a/test/__snapshots__/validate-options.test.js.snap b/test/__snapshots__/validate-options.test.js.snap index adc6357f..848127c4 100644 --- a/test/__snapshots__/validate-options.test.js.snap +++ b/test/__snapshots__/validate-options.test.js.snap @@ -38,32 +38,42 @@ exports[`validate options should throw an error on the "execute" option with "te 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)" + - options.implementation should be one of these: + string | function + -> The implementation of postcss to use, instead of the locally installed version (https://github.com/postcss/postcss-loader#implementation) + Details: + * options.implementation should be a string. + * options.implementation should be an instance of function." `; 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)" + - options.implementation should be one of these: + string | function + -> The implementation of postcss to use, instead of the locally installed version (https://github.com/postcss/postcss-loader#implementation) + Details: + * options.implementation should be a string. + * options.implementation should be an instance of function." `; 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)" + - options.implementation should be one of these: + string | function + -> The implementation of postcss to use, instead of the locally installed version (https://github.com/postcss/postcss-loader#implementation) + Details: + * options.implementation should be a string. + * options.implementation should be an instance of function." `; 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)" + - options.implementation should be one of these: + string | function + -> The implementation of postcss to use, instead of the locally installed version (https://github.com/postcss/postcss-loader#implementation) + Details: + * options.implementation should be a string. + * options.implementation should be an instance of function." `; exports[`validate options should throw an error on the "postcssOptions" option with "{"config":[]}" value 1`] = ` diff --git a/test/implementation.test.js b/test/implementation.test.js index 57832cc3..f1378c66 100644 --- a/test/implementation.test.js +++ b/test/implementation.test.js @@ -9,6 +9,28 @@ import { } from "./helpers"; describe('"implementation" option', () => { + it("should work with implementation is string", async () => { + const compiler = getCompiler("./css/index.js", { + implementation: require.resolve("postcss"), + }); + const stats = await compile(compiler); + const codeFromBundle = getCodeFromBundle("style.css", stats); + + expect(codeFromBundle.css).toMatchSnapshot("css"); + expect(getWarnings(stats)).toMatchSnapshot("warnings"); + expect(getErrors(stats)).toMatchSnapshot("errors"); + }); + + it("should throw error when unresolved package", async () => { + const compiler = getCompiler("./css/index.js", { + implementation: "unresolved", + }); + const stats = await compile(compiler); + + expect(getWarnings(stats)).toMatchSnapshot("warnings"); + expect(getErrors(stats)).toMatchSnapshot("errors"); + }); + it("should work with a custom instance of PostCSS", async () => { const spy = jest.fn(postcss); const compiler = getCompiler("./css/index.js", { diff --git a/test/validate-options.test.js b/test/validate-options.test.js index 7b6c68ea..8122f5bf 100644 --- a/test/validate-options.test.js +++ b/test/validate-options.test.js @@ -51,8 +51,8 @@ describe("validate options", () => { failure: [1, /test/, [], {}, "something"], }, implementation: { - success: [require("postcss")], - failure: [1, /test/, [], {}, "something"], + success: [require("postcss"), "postcss"], + failure: [1, /test/, [], {}], }, }; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy