Skip to content

Commit

Permalink
refactor: code
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-akait committed Jun 10, 2021
1 parent 342043e commit f318a0c
Show file tree
Hide file tree
Showing 7 changed files with 203 additions and 59 deletions.
74 changes: 47 additions & 27 deletions src/index.js
Expand Up @@ -13,12 +13,10 @@ import {
exec,
normalizeSourceMap,
normalizeSourceMapAfterPostcss,
parsePackageJson,
findPackageJsonDir,
findPackageJSONDir,
} from "./utils";

let hasExplicitDependencyOnPostCSS = false;
let packageJsonDir;

/**
* **PostCSS Loader**
Expand Down Expand Up @@ -101,36 +99,58 @@ export default async function loader(content, sourceMap, meta) {
}

let result;
let processor;

try {
result = await postcssFactory(plugins).process(
root || content,
processOptions
);
processor = postcssFactory(plugins);
result = await processor.process(root || content, 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 (!hasExplicitDependencyOnPostCSS && processor.version.startsWith("7.")) {
// 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.
const packageJSONDir = findPackageJSONDir(
process.cwd(),
this.fs.statSync
);

if (packageJSONDir) {
let bufferOfPackageJSON;

try {
bufferOfPackageJSON = this.fs.readFileSync(
path.resolve(packageJSONDir, "package.json"),
"utf8"
);
} catch (_error) {
// Nothing
}

if (bufferOfPackageJSON) {
let pkg;

try {
pkg = JSON.parse(bufferOfPackageJSON);
} catch (_error) {
// Nothing
}

if (pkg) {
if (!pkg.dependencies.postcss && !pkg.devDependencies.postcss) {
this.emitWarning(
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;
}
}
}
}
}

Expand Down
23 changes: 13 additions & 10 deletions src/utils.js
Expand Up @@ -408,24 +408,28 @@ function normalizeSourceMapAfterPostcss(map, resourceContext) {
return newMap;
}

function parsePackageJson(filePath, readFileSync) {
return JSON.parse(readFileSync(filePath, "utf8"));
}

function findPackageJsonDir(cwd, statSync) {
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) {}
if (statSync(path.join(dir, "package.json")).isFile()) {
break;
}
} catch (error) {
// Nothing
}

const parent = path.dirname(dir);

if (dir === parent) {
dir = null;
break;
}

dir = parent;
}

return dir;
}

Expand All @@ -435,6 +439,5 @@ export {
exec,
normalizeSourceMap,
normalizeSourceMapAfterPostcss,
parsePackageJson,
findPackageJsonDir,
findPackageJSONDir,
};
44 changes: 43 additions & 1 deletion test/__snapshots__/loader.test.js.snap
@@ -1,12 +1,54 @@
// 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: errors 1`] = `
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)",
]
`;

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\`",
Add postcss as project dependency. postcss is not a peer dependency for postcss-loader. Use \`npm install postcss\` or \`yarn add postcss\`",
]
`;

exports[`check postcss versions to avoid using PostCSS 7 should not show a warning if 'package.json' file was not found: errors 1`] = `
Array [
"ModuleBuildError: Module build failed (from \`replaced original path\`):
Error: Something went wrong.
at Processor.process (/test/loader.test.js:294:28)
at Object.loader (/src/index.js:106:30)",
]
`;

exports[`check postcss versions to avoid using PostCSS 7 should not show a warning if 'package.json' file was not found: warnings 1`] = `Array []`;

exports[`check postcss versions to avoid using PostCSS 7 should not show a warning if 'postcss' version is explicitly defined in 'dependencies': errors 1`] = `
Array [
"ModuleBuildError: Module build failed (from \`replaced original path\`):
Error: Something went wrong.
at Processor.process (/test/loader.test.js:242:26)
at Object.loader (/src/index.js:106:30)",
]
`;

exports[`check postcss versions to avoid using PostCSS 7 should not show a warning if 'postcss' version is explicitly defined in 'dependencies': warnings 1`] = `Array []`;

exports[`check postcss versions to avoid using PostCSS 7 should not show a warning if 'postcss' version is explicitly defined in 'devDependencies': errors 1`] = `
Array [
"ModuleBuildError: Module build failed (from \`replaced original path\`):
Error: Something went wrong.
at Processor.process (/test/loader.test.js:268:28)
at Object.loader (/src/index.js:106:30)",
]
`;

exports[`check postcss versions to avoid using PostCSS 7 should not show a warning if 'postcss' version is explicitly defined in 'devDependencies': warnings 1`] = `Array []`;

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
4 changes: 4 additions & 0 deletions test/fixtures/package-json-files/no-postcss/package.json
@@ -0,0 +1,4 @@
{
"dependencies": {},
"devDependencies": {}
}
@@ -0,0 +1,6 @@
{
"dependencies": {
"postcss": "^8.0.0"
},
"devDependencies": {}
}
@@ -0,0 +1,6 @@
{
"dependencies": {},
"devDependencies": {
"postcss": "^8.0.0"
}
}
105 changes: 84 additions & 21 deletions test/loader.test.js
Expand Up @@ -200,50 +200,113 @@ describe("loader", () => {
});

describe("check postcss versions to avoid using PostCSS 7", () => {
async function getStats() {
it("should emit a warning if postcss version is not explicitly specified when the loader is failed", async () => {
const spy = jest
.spyOn(utils, "findPackageJSONDir")
.mockReturnValue(
path.resolve(__dirname, "./fixtures/package-json-files/no-postcss")
);

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;
},
});
const stats = await compile(compiler);

expect(getWarnings(stats)).toMatchSnapshot("warnings");
expect(getErrors(stats)).toMatchSnapshot("errors");

return compile(compiler);
}
spy.mockRestore();
});

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: {} });
it("should not show a warning if 'postcss' version is explicitly defined in 'dependencies'", async () => {
const spy = jest
.spyOn(utils, "findPackageJSONDir")
.mockReturnValue(
path.resolve(
__dirname,
"./fixtures/package-json-files/postcss-v8-in-dependencies"
)
);

const compiler = getCompiler("./css/index.js", {
implementation: (...args) => {
const result = postcss(...args);

const stats = await getStats();
result.version = "7.0.0";
result.process = () =>
Promise.reject(new Error("Something went wrong."));

return result;
},
});
const stats = await compile(compiler);

expect(getWarnings(stats)).toMatchSnapshot("warnings");
expect(getErrors(stats)).toMatchSnapshot("errors");

spy.mockRestore();
});

it("should not show a warning if postcss version is explicitly defined", async () => {
jest.spyOn(utils, "parsePackageJson").mockReturnValue({
dependencies: {},
devDependencies: { postcss: "8.0.0" },
it("should not show a warning if 'postcss' version is explicitly defined in 'devDependencies'", async () => {
const spy = jest
.spyOn(utils, "findPackageJSONDir")
.mockReturnValue(
path.resolve(
__dirname,
"./fixtures/package-json-files/postcss-v8-in-devDependencies"
)
);

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;
},
});
const stats = await compile(compiler);

const stats = await getStats();
expect(getWarnings(stats)).toMatchSnapshot("warnings");
expect(getErrors(stats)).toMatchSnapshot("errors");

expect(stats.compilation.warnings.length).toBe(0);
spy.mockRestore();
});

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" },
it("should not show a warning if 'package.json' file was not found", async () => {
const spy = jest
.spyOn(utils, "findPackageJSONDir")
.mockReturnValue(
path.resolve(__dirname, "./fixtures/package-json-files/unknown")
);

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;
},
});
const stats = await compile(compiler);

const stats = await getStats();
expect(getWarnings(stats)).toMatchSnapshot("warnings");
expect(getErrors(stats)).toMatchSnapshot("errors");

expect(stats.compilation.warnings.length).toBe(0);
spy.mockRestore();
});
});

0 comments on commit f318a0c

Please sign in to comment.