Skip to content

Commit 570db67

Browse files
author
Alexey Shlyk
authored
fix: check postcss as project dependency
1 parent daeac95 commit 570db67

File tree

4 files changed

+108
-1
lines changed

4 files changed

+108
-1
lines changed

src/index.js

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import path from "path";
2+
13
import postcss from "postcss";
24
import { satisfies } from "semver";
35
import postcssPackage from "postcss/package.json";
@@ -11,8 +13,13 @@ import {
1113
exec,
1214
normalizeSourceMap,
1315
normalizeSourceMapAfterPostcss,
16+
parsePackageJson,
17+
findPackageJsonDir,
1418
} from "./utils";
1519

20+
let hasExplicitDependencyOnPostCSS = false;
21+
let packageJsonDir;
22+
1623
/**
1724
* **PostCSS Loader**
1825
*
@@ -26,7 +33,6 @@ import {
2633
*
2734
* @return {callback} callback Result
2835
*/
29-
3036
export default async function loader(content, sourceMap, meta) {
3137
const options = this.getOptions(schema);
3238
const callback = this.async();
@@ -102,6 +108,32 @@ export default async function loader(content, sourceMap, meta) {
102108
processOptions
103109
);
104110
} catch (error) {
111+
// The `findPackageJsonDir` function returns `string` or `null`.
112+
// This is used to do for caching, that is, an explicit comparison with `undefined`
113+
// is used to make the condition body run once.
114+
if (packageJsonDir === undefined) {
115+
packageJsonDir = findPackageJsonDir(process.cwd(), this.fs.statSync);
116+
}
117+
// Check postcss versions to avoid using PostCSS 7.
118+
// For caching reasons, we use the readFileSync and existsSync functions from the context,
119+
// not the functions from the `fs` module.
120+
if (
121+
!hasExplicitDependencyOnPostCSS &&
122+
postcssFactory().version.startsWith("7.") &&
123+
packageJsonDir
124+
) {
125+
const filePath = path.resolve(packageJsonDir, "package.json");
126+
const pkg = parsePackageJson(filePath, this.fs.readFileSync);
127+
if (!pkg.dependencies.postcss && !pkg.devDependencies.postcss) {
128+
this.emitWarning(
129+
"Add postcss as project dependency. postcss is not a peer dependency for postcss-loader. " +
130+
"Use `npm install postcss` or `yarn add postcss`"
131+
);
132+
} else {
133+
hasExplicitDependencyOnPostCSS = true;
134+
}
135+
}
136+
105137
if (error.file) {
106138
this.addDependency(error.file);
107139
}

src/utils.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,10 +408,33 @@ function normalizeSourceMapAfterPostcss(map, resourceContext) {
408408
return newMap;
409409
}
410410

411+
function parsePackageJson(filePath, readFileSync) {
412+
return JSON.parse(readFileSync(filePath, "utf8"));
413+
}
414+
415+
function findPackageJsonDir(cwd, statSync) {
416+
let dir = cwd;
417+
for (;;) {
418+
try {
419+
if (statSync(path.join(dir, "package.json")).isFile()) break;
420+
// eslint-disable-next-line no-empty
421+
} catch (error) {}
422+
const parent = path.dirname(dir);
423+
if (dir === parent) {
424+
dir = null;
425+
break;
426+
}
427+
dir = parent;
428+
}
429+
return dir;
430+
}
431+
411432
export {
412433
loadConfig,
413434
getPostcssOptions,
414435
exec,
415436
normalizeSourceMap,
416437
normalizeSourceMapAfterPostcss,
438+
parsePackageJson,
439+
findPackageJsonDir,
417440
};

test/__snapshots__/loader.test.js.snap

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
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`] = `
4+
Array [
5+
"ModuleWarning: Module Warning (from \`replaced original path\`):
6+
(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\`",
7+
]
8+
`;
9+
310
exports[`loader should emit asset using the "messages" API: errors 1`] = `Array []`;
411

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

test/loader.test.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import path from "path";
22

33
import postcss from "postcss";
44

5+
// eslint-disable-next-line import/no-namespace
6+
import * as utils from "../src/utils";
7+
58
import {
69
compile,
710
getCompiler,
@@ -198,3 +201,45 @@ describe("loader", () => {
198201
expect(getErrors(stats)).toMatchSnapshot("errors");
199202
});
200203
});
204+
205+
describe("check postcss versions to avoid using PostCSS 7", async () => {
206+
async function getStats() {
207+
const compiler = getCompiler("./css/index.js", {
208+
implementation: (...args) => {
209+
const result = postcss(...args);
210+
result.version = "7.0.0";
211+
result.process = () =>
212+
Promise.reject(new Error("Something went wrong."));
213+
return result;
214+
},
215+
});
216+
return compile(compiler);
217+
}
218+
219+
it("should emit a warning if postcss version is not explicitly specified when the loader is failed", async () => {
220+
jest
221+
.spyOn(utils, "parsePackageJson")
222+
.mockReturnValue({ dependencies: {}, devDependencies: {} });
223+
const stats = await getStats();
224+
expect(getWarnings(stats)).toMatchSnapshot("warnings");
225+
});
226+
227+
it("should not show a warning if postcss version is explicitly defined", async () => {
228+
jest.spyOn(utils, "parsePackageJson").mockReturnValue({
229+
dependencies: {},
230+
devDependencies: { postcss: "8.0.0" },
231+
});
232+
const stats = await getStats();
233+
expect(stats.compilation.warnings.length).toBe(0);
234+
});
235+
236+
it("should not show a warning if the package.json file was not found", async () => {
237+
jest.spyOn(utils, "findPackageJsonDir").mockReturnValue(null);
238+
jest.spyOn(utils, "parsePackageJson").mockReturnValue({
239+
dependencies: {},
240+
devDependencies: { postcss: "8.0.0" },
241+
});
242+
const stats = await getStats();
243+
expect(stats.compilation.warnings.length).toBe(0);
244+
});
245+
});

0 commit comments

Comments
 (0)
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