Skip to content

Commit

Permalink
fix: check postcss as project dependency
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexey Shlyk authored Jun 10, 2021
1 parent daeac95 commit 570db67
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 1 deletion.
34 changes: 33 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import path from "path";

import postcss from "postcss";
import { satisfies } from "semver";
import postcssPackage from "postcss/package.json";
Expand All @@ -11,8 +13,13 @@ import {
exec,
normalizeSourceMap,
normalizeSourceMapAfterPostcss,
parsePackageJson,
findPackageJsonDir,
} from "./utils";

let hasExplicitDependencyOnPostCSS = false;
let packageJsonDir;

/**
* **PostCSS Loader**
*
Expand All @@ -26,7 +33,6 @@ import {
*
* @return {callback} callback Result
*/

export default async function loader(content, sourceMap, meta) {
const options = this.getOptions(schema);
const callback = this.async();
Expand Down Expand Up @@ -102,6 +108,32 @@ 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.") &&
packageJsonDir
) {
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. " +
"Use `npm install postcss` or `yarn add postcss`"
);
} else {
hasExplicitDependencyOnPostCSS = true;
}
}

if (error.file) {
this.addDependency(error.file);
}
Expand Down
23 changes: 23 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -408,10 +408,33 @@ function normalizeSourceMapAfterPostcss(map, resourceContext) {
return newMap;
}

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,
exec,
normalizeSourceMap,
normalizeSourceMapAfterPostcss,
parsePackageJson,
findPackageJsonDir,
};
7 changes: 7 additions & 0 deletions test/__snapshots__/loader.test.js.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

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\`",
]
`;

exports[`loader should emit asset using the "messages" API: errors 1`] = `Array []`;

exports[`loader should emit asset using the "messages" API: warnings 1`] = `Array []`;
Expand Down
45 changes: 45 additions & 0 deletions test/loader.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -198,3 +201,45 @@ describe("loader", () => {
expect(getErrors(stats)).toMatchSnapshot("errors");
});
});

describe("check postcss versions to avoid using PostCSS 7", async () => {
async function getStats() {
const compiler = getCompiler("./css/index.js", {
implementation: (...args) => {
const result = postcss(...args);
result.version = "7.0.0";
result.process = () =>
Promise.reject(new Error("Something went wrong."));
return result;
},
});
return compile(compiler);
}

it("should emit a warning if postcss version is not explicitly specified when the loader is failed", async () => {
jest
.spyOn(utils, "parsePackageJson")
.mockReturnValue({ dependencies: {}, devDependencies: {} });
const stats = await getStats();
expect(getWarnings(stats)).toMatchSnapshot("warnings");
});

it("should not show a warning if postcss version is explicitly defined", async () => {
jest.spyOn(utils, "parsePackageJson").mockReturnValue({
dependencies: {},
devDependencies: { postcss: "8.0.0" },
});
const stats = await getStats();
expect(stats.compilation.warnings.length).toBe(0);
});

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" },
});
const stats = await getStats();
expect(stats.compilation.warnings.length).toBe(0);
});
});

0 comments on commit 570db67

Please sign in to comment.