From 7a19ce3b5c42e78cd9217c6a4b2ecd51cee3bdcb Mon Sep 17 00:00:00 2001 From: Alexey Shlyk Date: Sat, 15 May 2021 15:05:52 +0300 Subject: [PATCH 1/9] feat: check postcss versions to avoid using PostCSS 7 --- src/index.js | 17 +++++++++++++++++ src/utils.js | 8 ++++++++ test/__snapshots__/loader.test.js.snap | 8 ++++++++ test/loader.test.js | 16 ++++++++++++++++ 4 files changed, 49 insertions(+) diff --git a/src/index.js b/src/index.js index a27bc0e0..61276d73 100644 --- a/src/index.js +++ b/src/index.js @@ -11,6 +11,7 @@ import { exec, normalizeSourceMap, normalizeSourceMapAfterPostcss, + readPackageJson, } from "./utils"; /** @@ -27,6 +28,8 @@ import { * @return {callback} callback Result */ +let isCheckedPostCSSVersion = false; + export default async function loader(content, sourceMap, meta) { const options = this.getOptions(schema); const callback = this.async(); @@ -38,6 +41,20 @@ export default async function loader(content, sourceMap, meta) { const postcssFactory = options.implementation || postcss; + // Check postcss versions to avoid using PostCSS 7 + if (!isCheckedPostCSSVersion && postcss().version.startsWith("7.")) { + isCheckedPostCSSVersion = true; + const pkg = readPackageJson(); + if (!pkg.dependencies.postcss && !pkg.devDependencies.postcss) { + callback( + new Error( + "Add postcss as project dependency. postcss is not a peer dependency for postcss-loader. Use `npm install postcss` or `yarn add postcss`" + ) + ); + return; + } + } + let loadedConfig; if (configOption) { diff --git a/src/utils.js b/src/utils.js index 4ca898ab..a1ef5288 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,4 +1,5 @@ import path from "path"; +import fs from "fs"; import Module from "module"; import { klona } from "klona/full"; @@ -408,10 +409,17 @@ function normalizeSourceMapAfterPostcss(map, resourceContext) { return newMap; } +function readPackageJson() { + return JSON.parse( + fs.readFileSync(path.resolve(process.cwd(), "package.json"), "utf8") + ); +} + export { loadConfig, getPostcssOptions, exec, normalizeSourceMap, normalizeSourceMapAfterPostcss, + readPackageJson, }; diff --git a/test/__snapshots__/loader.test.js.snap b/test/__snapshots__/loader.test.js.snap index e26bce3e..4ada1e7e 100644 --- a/test/__snapshots__/loader.test.js.snap +++ b/test/__snapshots__/loader.test.js.snap @@ -161,6 +161,14 @@ exports[`loader should reuse PostCSS AST: errors 1`] = `Array []`; exports[`loader should reuse PostCSS AST: warnings 1`] = `Array []`; +exports[`loader should throw an error if postcss version is not explicitly specified: errors 1`] = ` +Array [ + "ModuleBuildError: Module build failed (from \`replaced original path\`): +Error: Add postcss as project dependency. postcss is not a peer dependency for postcss-loader. Use \`npm install postcss\` or \`yarn add postcss\` + at Object.loader (/src/index.js:49:16)", +] +`; + exports[`loader should throw an error on invalid syntax: errors 1`] = ` Array [ "ModuleBuildError: Module build failed (from \`replaced original path\`): diff --git a/test/loader.test.js b/test/loader.test.js index 4cd9b196..77712a0f 100644 --- a/test/loader.test.js +++ b/test/loader.test.js @@ -197,4 +197,20 @@ describe("loader", () => { expect(getWarnings(stats)).toMatchSnapshot("warnings"); expect(getErrors(stats)).toMatchSnapshot("errors"); }); + + it("should throw an error if postcss version is not explicitly specified", async () => { + jest.mock("../src/utils", () => { + return { + readPackageJson: () => { + return { dependencies: {}, devDependencies: {} }; + }, + }; + }); + jest.mock("postcss", () => () => { + return { version: "7." }; + }); + const compiler = getCompiler("./css/index.js"); + const stats = await compile(compiler); + expect(getErrors(stats)).toMatchSnapshot("errors"); + }); }); From e33c5d62b04eaa1e2332ae0768e034832aacd85f Mon Sep 17 00:00:00 2001 From: Alexey Shlyk Date: Sat, 15 May 2021 15:40:44 +0300 Subject: [PATCH 2/9] feat: added a flag which is responsible for having an explicit dependency on PostCSS --- src/index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/index.js b/src/index.js index 61276d73..53805229 100644 --- a/src/index.js +++ b/src/index.js @@ -28,7 +28,7 @@ import { * @return {callback} callback Result */ -let isCheckedPostCSSVersion = false; +let hasExplicitDependencyOnPostCSS = false; export default async function loader(content, sourceMap, meta) { const options = this.getOptions(schema); @@ -42,8 +42,7 @@ export default async function loader(content, sourceMap, meta) { const postcssFactory = options.implementation || postcss; // Check postcss versions to avoid using PostCSS 7 - if (!isCheckedPostCSSVersion && postcss().version.startsWith("7.")) { - isCheckedPostCSSVersion = true; + if (!hasExplicitDependencyOnPostCSS && postcss().version.startsWith("7.")) { const pkg = readPackageJson(); if (!pkg.dependencies.postcss && !pkg.devDependencies.postcss) { callback( @@ -51,7 +50,8 @@ export default async function loader(content, sourceMap, meta) { "Add postcss as project dependency. postcss is not a peer dependency for postcss-loader. Use `npm install postcss` or `yarn add postcss`" ) ); - return; + } else { + hasExplicitDependencyOnPostCSS = true; } } From aae55dfe0a2b9679e62c88fd91e3b5d9b29f7e4f Mon Sep 17 00:00:00 2001 From: Alexey Shlyk Date: Sat, 15 May 2021 16:28:22 +0300 Subject: [PATCH 3/9] feat: use readFileSync function from Webpack --- src/index.js | 7 +- src/utils.js | 7 +- test/__snapshots__/loader.test.js.snap | 227 +------------------------ 3 files changed, 10 insertions(+), 231 deletions(-) diff --git a/src/index.js b/src/index.js index 53805229..32f59976 100644 --- a/src/index.js +++ b/src/index.js @@ -43,16 +43,17 @@ export default async function loader(content, sourceMap, meta) { // Check postcss versions to avoid using PostCSS 7 if (!hasExplicitDependencyOnPostCSS && postcss().version.startsWith("7.")) { - const pkg = readPackageJson(); + // For caching reasons, we use the Webpack readFileSync function, not the function from `fs` module. + const pkg = readPackageJson(this.fs.readFileSync); if (!pkg.dependencies.postcss && !pkg.devDependencies.postcss) { callback( new Error( "Add postcss as project dependency. postcss is not a peer dependency for postcss-loader. Use `npm install postcss` or `yarn add postcss`" ) ); - } else { - hasExplicitDependencyOnPostCSS = true; + return; } + hasExplicitDependencyOnPostCSS = true; } let loadedConfig; diff --git a/src/utils.js b/src/utils.js index a1ef5288..62f1cce3 100644 --- a/src/utils.js +++ b/src/utils.js @@ -409,9 +409,12 @@ function normalizeSourceMapAfterPostcss(map, resourceContext) { return newMap; } -function readPackageJson() { +function readPackageJson(readFileSync) { return JSON.parse( - fs.readFileSync(path.resolve(process.cwd(), "package.json"), "utf8") + (readFileSync || fs.readFileSync)( + path.resolve(process.cwd(), "package.json"), + "utf8" + ) ); } diff --git a/test/__snapshots__/loader.test.js.snap b/test/__snapshots__/loader.test.js.snap index 4ada1e7e..0e9f41f5 100644 --- a/test/__snapshots__/loader.test.js.snap +++ b/test/__snapshots__/loader.test.js.snap @@ -1,171 +1,10 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`loader should emit asset using the "messages" API: errors 1`] = `Array []`; - -exports[`loader should emit asset using the "messages" API: warnings 1`] = `Array []`; - -exports[`loader should emit warning using the "messages" API: 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[`loader should emit warning using the "messages" API: errors 1`] = `Array []`; - -exports[`loader should emit warning using the "messages" API: warnings 1`] = ` -Array [ - "ModuleWarning: Module Warning (from \`replaced original path\`): -Warning - -(10:3) postcss-plugin: ", - "ModuleWarning: Module Warning (from \`replaced original path\`): -Warning - -(14:3) postcss-plugin: ", - "ModuleWarning: Module Warning (from \`replaced original path\`): -Warning - -(18:3) postcss-plugin: ", - "ModuleWarning: Module Warning (from \`replaced original path\`): -Warning - -(19:3) postcss-plugin: ", - "ModuleWarning: Module Warning (from \`replaced original path\`): -Warning - -(23:3) postcss-plugin: ", - "ModuleWarning: Module Warning (from \`replaced original path\`): -Warning - -(24:3) postcss-plugin: ", - "ModuleWarning: Module Warning (from \`replaced original path\`): -Warning - -(29:5) postcss-plugin: ", - "ModuleWarning: Module Warning (from \`replaced original path\`): -Warning - -(2:3) postcss-plugin: ", - "ModuleWarning: Module Warning (from \`replaced original path\`): -Warning - -(32:7) postcss-plugin: ", - "ModuleWarning: Module Warning (from \`replaced original path\`): -Warning - -(36:7) postcss-plugin: ", - "ModuleWarning: Module Warning (from \`replaced original path\`): -Warning - -(41:5) postcss-plugin: ", - "ModuleWarning: Module Warning (from \`replaced original path\`): -Warning - -(6:3) postcss-plugin: ", -] -`; - -exports[`loader should register dependencies using the "messages" API: errors 1`] = `Array []`; - -exports[`loader should register dependencies using the "messages" API: warnings 1`] = `Array []`; - -exports[`loader should reuse PostCSS AST: 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[`loader should reuse PostCSS AST: errors 1`] = `Array []`; - -exports[`loader should reuse PostCSS AST: warnings 1`] = `Array []`; - exports[`loader should throw an error if postcss version is not explicitly specified: errors 1`] = ` Array [ "ModuleBuildError: Module build failed (from \`replaced original path\`): Error: Add postcss as project dependency. postcss is not a peer dependency for postcss-loader. Use \`npm install postcss\` or \`yarn add postcss\` - at Object.loader (/src/index.js:49:16)", + at Object.loader (/src/index.js:50:9)", ] `; @@ -183,67 +22,3 @@ SyntaxError ", ] `; - -exports[`loader should throw an error on invalid syntax: warnings 1`] = `Array []`; - -exports[`loader should work with SugarSS: css 1`] = ` -"a { - color: black -} -" -`; - -exports[`loader should work with SugarSS: errors 1`] = `Array []`; - -exports[`loader should work with SugarSS: warnings 1`] = `Array []`; - -exports[`loader should work: 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[`loader should work: errors 1`] = `Array []`; - -exports[`loader should work: warnings 1`] = `Array []`; From ba3446be2de1fadf5b3beae7bfd4cca32483b0b2 Mon Sep 17 00:00:00 2001 From: Alexey Shlyk Date: Sat, 15 May 2021 16:30:27 +0300 Subject: [PATCH 4/9] feat: reverted snapshots --- test/__snapshots__/loader.test.js.snap | 227 ++++++++++++++++++++++++- 1 file changed, 223 insertions(+), 4 deletions(-) diff --git a/test/__snapshots__/loader.test.js.snap b/test/__snapshots__/loader.test.js.snap index 0e9f41f5..8eb0481f 100644 --- a/test/__snapshots__/loader.test.js.snap +++ b/test/__snapshots__/loader.test.js.snap @@ -1,13 +1,168 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`loader should throw an error if postcss version is not explicitly specified: errors 1`] = ` +exports[`loader should emit asset using the "messages" API: errors 1`] = `Array []`; + +exports[`loader should emit asset using the "messages" API: warnings 1`] = `Array []`; + +exports[`loader should emit warning using the "messages" API: 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[`loader should emit warning using the "messages" API: errors 1`] = `Array []`; + +exports[`loader should emit warning using the "messages" API: warnings 1`] = ` Array [ - "ModuleBuildError: Module build failed (from \`replaced original path\`): -Error: Add postcss as project dependency. postcss is not a peer dependency for postcss-loader. Use \`npm install postcss\` or \`yarn add postcss\` - at Object.loader (/src/index.js:50:9)", + "ModuleWarning: Module Warning (from \`replaced original path\`): +Warning + +(10:3) postcss-plugin: ", + "ModuleWarning: Module Warning (from \`replaced original path\`): +Warning + +(14:3) postcss-plugin: ", + "ModuleWarning: Module Warning (from \`replaced original path\`): +Warning + +(18:3) postcss-plugin: ", + "ModuleWarning: Module Warning (from \`replaced original path\`): +Warning + +(19:3) postcss-plugin: ", + "ModuleWarning: Module Warning (from \`replaced original path\`): +Warning + +(23:3) postcss-plugin: ", + "ModuleWarning: Module Warning (from \`replaced original path\`): +Warning + +(24:3) postcss-plugin: ", + "ModuleWarning: Module Warning (from \`replaced original path\`): +Warning + +(29:5) postcss-plugin: ", + "ModuleWarning: Module Warning (from \`replaced original path\`): +Warning + +(2:3) postcss-plugin: ", + "ModuleWarning: Module Warning (from \`replaced original path\`): +Warning + +(32:7) postcss-plugin: ", + "ModuleWarning: Module Warning (from \`replaced original path\`): +Warning + +(36:7) postcss-plugin: ", + "ModuleWarning: Module Warning (from \`replaced original path\`): +Warning + +(41:5) postcss-plugin: ", + "ModuleWarning: Module Warning (from \`replaced original path\`): +Warning + +(6:3) postcss-plugin: ", ] `; +exports[`loader should register dependencies using the "messages" API: errors 1`] = `Array []`; + +exports[`loader should register dependencies using the "messages" API: warnings 1`] = `Array []`; + +exports[`loader should reuse PostCSS AST: 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[`loader should reuse PostCSS AST: errors 1`] = `Array []`; + +exports[`loader should reuse PostCSS AST: warnings 1`] = `Array []`; + +exports[`loader should throw an error if postcss version is not explicitly specified: errors 1`] = `Array []`; + exports[`loader should throw an error on invalid syntax: errors 1`] = ` Array [ "ModuleBuildError: Module build failed (from \`replaced original path\`): @@ -22,3 +177,67 @@ SyntaxError ", ] `; + +exports[`loader should throw an error on invalid syntax: warnings 1`] = `Array []`; + +exports[`loader should work with SugarSS: css 1`] = ` +"a { + color: black +} +" +`; + +exports[`loader should work with SugarSS: errors 1`] = `Array []`; + +exports[`loader should work with SugarSS: warnings 1`] = `Array []`; + +exports[`loader should work: 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[`loader should work: errors 1`] = `Array []`; + +exports[`loader should work: warnings 1`] = `Array []`; From a4a762577b7ea13dceb17b45b60f56a00fcca02d Mon Sep 17 00:00:00 2001 From: Alexey Shlyk Date: Sat, 15 May 2021 20:52:55 +0300 Subject: [PATCH 5/9] feat: print warning when the loader is failed --- src/index.js | 36 +++++++++++++------------- test/__snapshots__/loader.test.js.snap | 9 +++++-- test/loader.test.js | 29 ++++++++++++--------- 3 files changed, 42 insertions(+), 32 deletions(-) diff --git a/src/index.js b/src/index.js index 32f59976..ba6f6702 100644 --- a/src/index.js +++ b/src/index.js @@ -14,6 +14,8 @@ import { readPackageJson, } from "./utils"; +let hasExplicitDependencyOnPostCSS = false; + /** * **PostCSS Loader** * @@ -27,9 +29,6 @@ import { * * @return {callback} callback Result */ - -let hasExplicitDependencyOnPostCSS = false; - export default async function loader(content, sourceMap, meta) { const options = this.getOptions(schema); const callback = this.async(); @@ -41,21 +40,6 @@ export default async function loader(content, sourceMap, meta) { const postcssFactory = options.implementation || postcss; - // Check postcss versions to avoid using PostCSS 7 - if (!hasExplicitDependencyOnPostCSS && postcss().version.startsWith("7.")) { - // For caching reasons, we use the Webpack readFileSync function, not the function from `fs` module. - const pkg = readPackageJson(this.fs.readFileSync); - if (!pkg.dependencies.postcss && !pkg.devDependencies.postcss) { - callback( - new Error( - "Add postcss as project dependency. postcss is not a peer dependency for postcss-loader. Use `npm install postcss` or `yarn add postcss`" - ) - ); - return; - } - hasExplicitDependencyOnPostCSS = true; - } - let loadedConfig; if (configOption) { @@ -120,6 +104,22 @@ export default async function loader(content, sourceMap, meta) { processOptions ); } catch (error) { + // Check postcss versions to avoid using PostCSS 7. + if ( + !hasExplicitDependencyOnPostCSS && + postcssFactory().version.startsWith("7.") + ) { + // For caching reasons, we use the readFileSync function from the context, not the function from the `fs` module. + const pkg = readPackageJson(this.fs.readFileSync); + if (!pkg.dependencies.postcss && !pkg.devDependencies.postcss) { + this.emitWarning( + "Add postcss as project dependency. postcss is not a peer dependency for postcss-loader. Use `npm install postcss` or `yarn add postcss`" + ); + } else { + hasExplicitDependencyOnPostCSS = true; + } + } + if (error.file) { this.addDependency(error.file); } diff --git a/test/__snapshots__/loader.test.js.snap b/test/__snapshots__/loader.test.js.snap index 8eb0481f..a6bfb504 100644 --- a/test/__snapshots__/loader.test.js.snap +++ b/test/__snapshots__/loader.test.js.snap @@ -1,5 +1,12 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`loader should emit a warning if postcss version is not explicitly specified when the loader is failed: warnings 1`] = ` +Array [ + "ModuleWarning: Module Warning (from \`replaced original path\`): +(Emitted value instead of an instance of Error) Add postcss as project dependency. postcss is not a peer dependency for postcss-loader. Use \`npm install postcss\` or \`yarn add postcss\`", +] +`; + exports[`loader should emit asset using the "messages" API: errors 1`] = `Array []`; exports[`loader should emit asset using the "messages" API: warnings 1`] = `Array []`; @@ -161,8 +168,6 @@ exports[`loader should reuse PostCSS AST: errors 1`] = `Array []`; exports[`loader should reuse PostCSS AST: warnings 1`] = `Array []`; -exports[`loader should throw an error if postcss version is not explicitly specified: errors 1`] = `Array []`; - exports[`loader should throw an error on invalid syntax: errors 1`] = ` Array [ "ModuleBuildError: Module build failed (from \`replaced original path\`): diff --git a/test/loader.test.js b/test/loader.test.js index 77712a0f..f63e1fc0 100644 --- a/test/loader.test.js +++ b/test/loader.test.js @@ -2,6 +2,9 @@ import path from "path"; import postcss from "postcss"; +// eslint-disable-next-line import/no-namespace +import * as utils from "../src/utils"; + import { compile, getCompiler, @@ -198,19 +201,21 @@ describe("loader", () => { expect(getErrors(stats)).toMatchSnapshot("errors"); }); - it("should throw an error if postcss version is not explicitly specified", async () => { - jest.mock("../src/utils", () => { - return { - readPackageJson: () => { - return { dependencies: {}, devDependencies: {} }; - }, - }; - }); - jest.mock("postcss", () => () => { - return { version: "7." }; + it("should emit a warning if postcss version is not explicitly specified when the loader is failed", async () => { + jest + .spyOn(utils, "readPackageJson") + .mockReturnValue({ dependencies: {}, devDependencies: {} }); + const spy = jest.fn(postcss); + const compiler = getCompiler("./css/index.js", { + implementation: (...args) => { + const result = spy(...args); + result.version = "7.0.0"; + result.process = () => + Promise.reject(new Error("Something went wrong.")); + return result; + }, }); - const compiler = getCompiler("./css/index.js"); const stats = await compile(compiler); - expect(getErrors(stats)).toMatchSnapshot("errors"); + expect(getWarnings(stats)).toMatchSnapshot("warnings"); }); }); From 4fd7cde62a87460078998d7e66e64f1f94558788 Mon Sep 17 00:00:00 2001 From: Alexey Shlyk Date: Sat, 15 May 2021 21:37:39 +0300 Subject: [PATCH 6/9] feat: there should be no warnings if the version is defined in package.json --- test/__snapshots__/loader.test.js.snap | 2 +- test/loader.test.js | 30 ++++++++++++++++++++------ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/test/__snapshots__/loader.test.js.snap b/test/__snapshots__/loader.test.js.snap index a6bfb504..75379459 100644 --- a/test/__snapshots__/loader.test.js.snap +++ b/test/__snapshots__/loader.test.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`loader should emit a warning if postcss version is not explicitly specified when the loader is failed: warnings 1`] = ` +exports[`check postcss versions to avoid using PostCSS 7 should emit a warning if postcss version is not explicitly specified when the loader is failed: warnings 1`] = ` Array [ "ModuleWarning: Module Warning (from \`replaced original path\`): (Emitted value instead of an instance of Error) Add postcss as project dependency. postcss is not a peer dependency for postcss-loader. Use \`npm install postcss\` or \`yarn add postcss\`", diff --git a/test/loader.test.js b/test/loader.test.js index f63e1fc0..de7930d1 100644 --- a/test/loader.test.js +++ b/test/loader.test.js @@ -200,22 +200,38 @@ describe("loader", () => { expect(getWarnings(stats)).toMatchSnapshot("warnings"); expect(getErrors(stats)).toMatchSnapshot("errors"); }); +}); - it("should emit a warning if postcss version is not explicitly specified when the loader is failed", async () => { - jest - .spyOn(utils, "readPackageJson") - .mockReturnValue({ dependencies: {}, devDependencies: {} }); - const spy = jest.fn(postcss); +describe("check postcss versions to avoid using PostCSS 7", async () => { + async function getStats() { const compiler = getCompiler("./css/index.js", { implementation: (...args) => { - const result = spy(...args); + const result = postcss(...args); result.version = "7.0.0"; result.process = () => Promise.reject(new Error("Something went wrong.")); return result; }, }); - const stats = await compile(compiler); + return compile(compiler); + } + + it("should emit a warning if postcss version is not explicitly specified when the loader is failed", async () => { + jest + .spyOn(utils, "readPackageJson") + .mockReturnValue({ dependencies: {}, devDependencies: {} }); + const stats = await getStats(); expect(getWarnings(stats)).toMatchSnapshot("warnings"); }); + + it("disable checking when postcss version is explicitly defined", async () => { + jest + .spyOn(utils, "readPackageJson") + .mockReturnValue({ + dependencies: {}, + devDependencies: { postcss: "8.0.0" }, + }); + const stats = await getStats(); + expect(stats.compilation.warnings.length).toBe(0); + }); }); From 216f462ea114e226387b151646eb8be9bb7e943e Mon Sep 17 00:00:00 2001 From: Alexey Shlyk Date: Sun, 16 May 2021 11:18:13 +0300 Subject: [PATCH 7/9] feat: check the package.json file before the main check --- src/index.js | 16 ++++++++++++---- src/utils.js | 20 ++++++++++++-------- test/loader.test.js | 22 +++++++++++++++------- 3 files changed, 39 insertions(+), 19 deletions(-) diff --git a/src/index.js b/src/index.js index ba6f6702..52cc295a 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,5 @@ +import path from "path"; + import postcss from "postcss"; import { satisfies } from "semver"; import postcssPackage from "postcss/package.json"; @@ -11,11 +13,14 @@ import { exec, normalizeSourceMap, normalizeSourceMapAfterPostcss, - readPackageJson, + parsePackageJson, + isFileExists, } from "./utils"; let hasExplicitDependencyOnPostCSS = false; +const PACKAGE_JSON_PATH = path.resolve(process.cwd(), "package.json"); + /** * **PostCSS Loader** * @@ -105,15 +110,18 @@ export default async function loader(content, sourceMap, meta) { ); } catch (error) { // Check postcss versions to avoid using PostCSS 7. + // For caching reasons, we use the readFileSync and existsSync functions from the context, + // not the functions from the `fs` module. if ( !hasExplicitDependencyOnPostCSS && + isFileExists(PACKAGE_JSON_PATH, this.fs.existsSync) && postcssFactory().version.startsWith("7.") ) { - // For caching reasons, we use the readFileSync function from the context, not the function from the `fs` module. - const pkg = readPackageJson(this.fs.readFileSync); + const pkg = parsePackageJson(PACKAGE_JSON_PATH, this.fs.readFileSync); if (!pkg.dependencies.postcss && !pkg.devDependencies.postcss) { this.emitWarning( - "Add postcss as project dependency. postcss is not a peer dependency for postcss-loader. Use `npm install postcss` or `yarn add postcss`" + "Add postcss as project dependency. postcss is not a peer dependency for postcss-loader. " + + "Use `npm install postcss` or `yarn add postcss`" ); } else { hasExplicitDependencyOnPostCSS = true; diff --git a/src/utils.js b/src/utils.js index 62f1cce3..ac294734 100644 --- a/src/utils.js +++ b/src/utils.js @@ -409,13 +409,16 @@ function normalizeSourceMapAfterPostcss(map, resourceContext) { return newMap; } -function readPackageJson(readFileSync) { - return JSON.parse( - (readFileSync || fs.readFileSync)( - path.resolve(process.cwd(), "package.json"), - "utf8" - ) - ); +function isFileExists(filePath, existsSync = fs.existsSync) { + try { + return existsSync(filePath); + } catch (error) { + return false; + } +} + +function parsePackageJson(filePath, readFileSync = fs.readFileSync) { + return JSON.parse(readFileSync(filePath, "utf8")); } export { @@ -424,5 +427,6 @@ export { exec, normalizeSourceMap, normalizeSourceMapAfterPostcss, - readPackageJson, + parsePackageJson, + isFileExists, }; diff --git a/test/loader.test.js b/test/loader.test.js index de7930d1..54e3e4f7 100644 --- a/test/loader.test.js +++ b/test/loader.test.js @@ -218,19 +218,27 @@ describe("check postcss versions to avoid using PostCSS 7", async () => { it("should emit a warning if postcss version is not explicitly specified when the loader is failed", async () => { jest - .spyOn(utils, "readPackageJson") + .spyOn(utils, "parsePackageJson") .mockReturnValue({ dependencies: {}, devDependencies: {} }); const stats = await getStats(); expect(getWarnings(stats)).toMatchSnapshot("warnings"); }); it("disable checking when postcss version is explicitly defined", async () => { - jest - .spyOn(utils, "readPackageJson") - .mockReturnValue({ - dependencies: {}, - devDependencies: { postcss: "8.0.0" }, - }); + jest.spyOn(utils, "parsePackageJson").mockReturnValue({ + dependencies: {}, + devDependencies: { postcss: "8.0.0" }, + }); + const stats = await getStats(); + expect(stats.compilation.warnings.length).toBe(0); + }); + + it("disable checking when package.json doesn't exist", async () => { + jest.spyOn(utils, "isFileExists").mockReturnValue(false); + jest.spyOn(utils, "parsePackageJson").mockReturnValue({ + dependencies: {}, + devDependencies: { postcss: "8.0.0" }, + }); const stats = await getStats(); expect(stats.compilation.warnings.length).toBe(0); }); From eac5f85dd7b578b7d6dbe67b38af8c5f6fc8ea32 Mon Sep 17 00:00:00 2001 From: Alexey Shlyk Date: Mon, 17 May 2021 08:57:45 +0300 Subject: [PATCH 8/9] feat: removed unnecessary default values in utils functions --- src/index.js | 4 ++-- src/utils.js | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/index.js b/src/index.js index 52cc295a..bcad4f12 100644 --- a/src/index.js +++ b/src/index.js @@ -114,8 +114,8 @@ export default async function loader(content, sourceMap, meta) { // not the functions from the `fs` module. if ( !hasExplicitDependencyOnPostCSS && - isFileExists(PACKAGE_JSON_PATH, this.fs.existsSync) && - postcssFactory().version.startsWith("7.") + postcssFactory().version.startsWith("7.") && + isFileExists(PACKAGE_JSON_PATH, this.fs.statSync) ) { const pkg = parsePackageJson(PACKAGE_JSON_PATH, this.fs.readFileSync); if (!pkg.dependencies.postcss && !pkg.devDependencies.postcss) { diff --git a/src/utils.js b/src/utils.js index ac294734..ab710877 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,5 +1,4 @@ import path from "path"; -import fs from "fs"; import Module from "module"; import { klona } from "klona/full"; @@ -409,15 +408,15 @@ function normalizeSourceMapAfterPostcss(map, resourceContext) { return newMap; } -function isFileExists(filePath, existsSync = fs.existsSync) { +function isFileExists(filePath, statSync) { try { - return existsSync(filePath); + return statSync(filePath).isFile(); } catch (error) { return false; } } -function parsePackageJson(filePath, readFileSync = fs.readFileSync) { +function parsePackageJson(filePath, readFileSync) { return JSON.parse(readFileSync(filePath, "utf8")); } From 833f56b1d90da0f7528de3681d3852b9f3f40a47 Mon Sep 17 00:00:00 2001 From: Alexey Shlyk Date: Mon, 17 May 2021 18:25:16 +0300 Subject: [PATCH 9/9] feat: search the package.json file in the current and parent directories --- src/index.js | 16 +++++++++++----- src/utils.js | 27 ++++++++++++++++++--------- test/loader.test.js | 6 +++--- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/src/index.js b/src/index.js index bcad4f12..7c72c08a 100644 --- a/src/index.js +++ b/src/index.js @@ -14,12 +14,11 @@ import { normalizeSourceMap, normalizeSourceMapAfterPostcss, parsePackageJson, - isFileExists, + findPackageJsonDir, } from "./utils"; let hasExplicitDependencyOnPostCSS = false; - -const PACKAGE_JSON_PATH = path.resolve(process.cwd(), "package.json"); +let packageJsonDir; /** * **PostCSS Loader** @@ -109,15 +108,22 @@ export default async function loader(content, sourceMap, meta) { processOptions ); } catch (error) { + // The `findPackageJsonDir` function returns `string` or `null`. + // This is used to do for caching, that is, an explicit comparison with `undefined` + // is used to make the condition body run once. + if (packageJsonDir === undefined) { + packageJsonDir = findPackageJsonDir(process.cwd(), this.fs.statSync); + } // Check postcss versions to avoid using PostCSS 7. // For caching reasons, we use the readFileSync and existsSync functions from the context, // not the functions from the `fs` module. if ( !hasExplicitDependencyOnPostCSS && postcssFactory().version.startsWith("7.") && - isFileExists(PACKAGE_JSON_PATH, this.fs.statSync) + packageJsonDir ) { - const pkg = parsePackageJson(PACKAGE_JSON_PATH, this.fs.readFileSync); + const filePath = path.resolve(packageJsonDir, "package.json"); + const pkg = parsePackageJson(filePath, this.fs.readFileSync); if (!pkg.dependencies.postcss && !pkg.devDependencies.postcss) { this.emitWarning( "Add postcss as project dependency. postcss is not a peer dependency for postcss-loader. " + diff --git a/src/utils.js b/src/utils.js index ab710877..1c847f5b 100644 --- a/src/utils.js +++ b/src/utils.js @@ -408,18 +408,27 @@ function normalizeSourceMapAfterPostcss(map, resourceContext) { return newMap; } -function isFileExists(filePath, statSync) { - try { - return statSync(filePath).isFile(); - } catch (error) { - return false; - } -} - function parsePackageJson(filePath, readFileSync) { return JSON.parse(readFileSync(filePath, "utf8")); } +function findPackageJsonDir(cwd, statSync) { + let dir = cwd; + for (;;) { + try { + if (statSync(path.join(dir, "package.json")).isFile()) break; + // eslint-disable-next-line no-empty + } catch (error) {} + const parent = path.dirname(dir); + if (dir === parent) { + dir = null; + break; + } + dir = parent; + } + return dir; +} + export { loadConfig, getPostcssOptions, @@ -427,5 +436,5 @@ export { normalizeSourceMap, normalizeSourceMapAfterPostcss, parsePackageJson, - isFileExists, + findPackageJsonDir, }; diff --git a/test/loader.test.js b/test/loader.test.js index 54e3e4f7..2d218943 100644 --- a/test/loader.test.js +++ b/test/loader.test.js @@ -224,7 +224,7 @@ describe("check postcss versions to avoid using PostCSS 7", async () => { expect(getWarnings(stats)).toMatchSnapshot("warnings"); }); - it("disable checking when postcss version is explicitly defined", async () => { + it("should not show a warning if postcss version is explicitly defined", async () => { jest.spyOn(utils, "parsePackageJson").mockReturnValue({ dependencies: {}, devDependencies: { postcss: "8.0.0" }, @@ -233,8 +233,8 @@ describe("check postcss versions to avoid using PostCSS 7", async () => { expect(stats.compilation.warnings.length).toBe(0); }); - it("disable checking when package.json doesn't exist", async () => { - jest.spyOn(utils, "isFileExists").mockReturnValue(false); + it("should not show a warning if the package.json file was not found", async () => { + jest.spyOn(utils, "findPackageJsonDir").mockReturnValue(null); jest.spyOn(utils, "parsePackageJson").mockReturnValue({ dependencies: {}, devDependencies: { postcss: "8.0.0" },