Skip to content

Commit

Permalink
feat: update to PostCSS 8
Browse files Browse the repository at this point in the history
  • Loading branch information
Ffloriel committed Oct 24, 2020
1 parent b8831fd commit beb4a59
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 139 deletions.
32 changes: 16 additions & 16 deletions package.json
Expand Up @@ -13,27 +13,27 @@
"vue-cli-plugin-purgecss"
],
"devDependencies": {
"@types/jest": "^26.0.3",
"@types/node": "^14.0.5",
"@typescript-eslint/eslint-plugin": "^4.1.0",
"@typescript-eslint/parser": "^4.1.0",
"@vuepress/plugin-google-analytics": "^1.5.4",
"@vuepress/theme-default": "^1.5.4",
"@wessberg/rollup-plugin-ts": "^1.3.4",
"@types/jest": "^26.0.15",
"@types/node": "^14.14.2",
"@typescript-eslint/eslint-plugin": "^4.5.0",
"@typescript-eslint/parser": "^4.5.0",
"@vuepress/plugin-google-analytics": "^1.7.1",
"@vuepress/theme-default": "^1.7.1",
"@wessberg/rollup-plugin-ts": "^1.3.6",
"conventional-changelog-cli": "^2.1.0",
"eslint": "^7.8.0",
"eslint": "^7.11.0",
"husky": "^4.3.0",
"jest": "^26.4.0",
"jest": "^26.6.1",
"lerna": "^3.22.1",
"lint-staged": "^10.3.0",
"prettier": "^2.1.1",
"rollup": "^2.26.11",
"lint-staged": "^10.4.2",
"prettier": "^2.1.2",
"rollup": "^2.32.1",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-terser": "^7.0.1",
"ts-jest": "^26.3.0",
"rollup-plugin-terser": "^7.0.2",
"ts-jest": "^26.4.2",
"ts-node": "^9.0.0",
"typescript": "^4.0.2",
"vuepress": "^1.5.4"
"typescript": "^4.0.3",
"vuepress": "^1.7.1"
},
"scripts": {
"bootstrap": "lerna bootstrap",
Expand Down
72 changes: 34 additions & 38 deletions packages/postcss-purgecss/__tests__/index.test.ts
@@ -1,32 +1,30 @@
import fs from "fs";
import postcss from "postcss";
import { UserDefinedOptions } from "../src/types";
import postcss, { PluginCreator } from "postcss";

import purgeCSSPlugin from "../src";
import purgeCSSPlugin from "../src/";

describe("Purgecss postcss plugin", () => {
const files = ["simple", "font-keyframes"];

for (const file of files) {
it(`remove unused css with content option successfully: ${file}`, (done) => {
it(`remove unused css with content option successfully: ${file}`, async () => {
const input = fs
.readFileSync(`${__dirname}/fixtures/src/${file}/${file}.css`)
.toString();
const expected = fs
.readFileSync(`${__dirname}/fixtures/expected/${file}.css`)
.toString();
postcss([
purgeCSSPlugin({
const result = await postcss([
(purgeCSSPlugin as PluginCreator<UserDefinedOptions>)({
content: [`${__dirname}/fixtures/src/${file}/${file}.html`],
fontFace: true,
keyframes: true,
}),
])
.process(input, { from: undefined })
.then((result) => {
expect(result.css).toBe(expected);
expect(result.warnings().length).toBe(0);
done();
});
]).process(input, { from: undefined });

expect(result.css).toBe(expected);
expect(result.warnings().length).toBe(0);
});
}

Expand All @@ -45,7 +43,7 @@ describe("Purgecss postcss plugin", () => {
.mockReturnValue([`${__dirname}/fixtures/src/${file}/${file}.html`]);

postcss([
purgeCSSPlugin({
(purgeCSSPlugin as PluginCreator<UserDefinedOptions>)({
contentFunction,
fontFace: true,
keyframes: true,
Expand All @@ -62,29 +60,27 @@ describe("Purgecss postcss plugin", () => {
});
}

for (const file of ["simple"]) {
it(`queues messages when using reject flag: ${file}`, (done) => {
const input = fs
.readFileSync(`${__dirname}/fixtures/src/${file}/${file}.css`)
.toString();
const expected = fs
.readFileSync(`${__dirname}/fixtures/expected/${file}.css`)
.toString();
postcss([
purgeCSSPlugin({
content: [`${__dirname}/fixtures/src/${file}/${file}.html`],
rejected: true,
}),
])
.process(input, { from: undefined })
.then((result) => {
expect(result.css).toBe(expected);
expect(result.warnings().length).toBe(0);
expect(result.messages.length).toBeGreaterThan(0);
expect(result.messages[0].text).toMatch(/unused-class/);
expect(result.messages[0].text).toMatch(/another-one-not-found/);
done();
});
});
}
it(`queues messages when using reject flag: simple`, (done) => {
const input = fs
.readFileSync(`${__dirname}/fixtures/src/simple/simple.css`)
.toString();
const expected = fs
.readFileSync(`${__dirname}/fixtures/expected/simple.css`)
.toString();
postcss([
(purgeCSSPlugin as PluginCreator<UserDefinedOptions>)({
content: [`${__dirname}/fixtures/src/simple/simple.html`],
rejected: true,
}),
])
.process(input, { from: undefined })
.then((result) => {
expect(result.css).toBe(expected);
expect(result.warnings().length).toBe(0);
expect(result.messages.length).toBeGreaterThan(0);
expect(result.messages[0].text).toMatch(/unused-class/);
expect(result.messages[0].text).toMatch(/another-one-not-found/);
done();
});
});
});
8 changes: 7 additions & 1 deletion packages/postcss-purgecss/package.json
Expand Up @@ -26,7 +26,13 @@
"url": "https://github.com/FullHuman/purgecss/issues"
},
"dependencies": {
"postcss": "7.0.32",
"purgecss": "^3.0.0"
},
"devDependencies": {
"postcss": "^8.1.3",
"postcss7": "npm:postcss@7.0.34"
},
"peerDependencies": {
"postcss": "^8.1.3"
}
}
74 changes: 2 additions & 72 deletions packages/postcss-purgecss/src/index.ts
@@ -1,74 +1,4 @@
import postcss from "postcss";
import PurgeCSS, {
defaultOptions,
mergeExtractorSelectors,
standardizeSafelist,
} from "purgecss";
import { createPurgeCSSPlugin } from "./postCSSPurgeCSS";

import { RawContent, UserDefinedOptions } from "./types";

const purgeCSSPlugin = postcss.plugin<Omit<UserDefinedOptions, "css">>(
"postcss-plugin-purgecss",
function (opts) {
return async function (root, result): Promise<void> {
const purgeCSS = new PurgeCSS();
const options = {
...defaultOptions,
...opts,
safelist: standardizeSafelist(opts?.safelist),
};

if (opts && typeof opts.contentFunction === "function") {
options.content = opts.contentFunction(
(root.source && root.source.input.file) || ""
);
}

purgeCSS.options = options;

const { content, extractors } = options;

const fileFormatContents = content.filter(
(o) => typeof o === "string"
) as string[];
const rawFormatContents = content.filter(
(o) => typeof o === "object"
) as RawContent[];

const cssFileSelectors = await purgeCSS.extractSelectorsFromFiles(
fileFormatContents,
extractors
);
const cssRawSelectors = await purgeCSS.extractSelectorsFromString(
rawFormatContents,
extractors
);

const selectors = mergeExtractorSelectors(
cssFileSelectors,
cssRawSelectors
);

//purge unused selectors
purgeCSS.walkThroughCSS(root, selectors);

if (purgeCSS.options.fontFace) purgeCSS.removeUnusedFontFaces();
if (purgeCSS.options.keyframes) purgeCSS.removeUnusedKeyframes();
if (purgeCSS.options.variables) purgeCSS.removeUnusedCSSVariables();

if (purgeCSS.options.rejected && purgeCSS.selectorsRemoved.size > 0) {
result.messages.push({
type: "purgecss",
plugin: "postcss-purgecss",
text: `purging ${purgeCSS.selectorsRemoved.size} selectors:
${Array.from(purgeCSS.selectorsRemoved)
.map((selector) => selector.trim())
.join("\n ")}`,
});
purgeCSS.selectorsRemoved.clear();
}
};
}
);

export default purgeCSSPlugin;
export default createPurgeCSSPlugin(postcss);
113 changes: 113 additions & 0 deletions packages/postcss-purgecss/src/postCSSPurgeCSS.ts
@@ -0,0 +1,113 @@
import { Helpers, PluginCreator, Root } from "postcss";

import PurgeCSS, {
defaultOptions,
mergeExtractorSelectors,
standardizeSafelist,
} from "purgecss";

import {
PostCSSv7,
PostCSSv8,
PostCSSVersions,
RawContent,
UserDefinedOptions,
} from "./types";

const PLUGIN_NAME = "postcss-purgecss";

function isPostCSSv8(postcss: PostCSSVersions): postcss is PostCSSv8 {
return typeof (postcss as PostCSSv8).Root !== "undefined";
}

async function purgeCSS(
opts: UserDefinedOptions,
root: Root,
{ result }: Helpers
): Promise<void> {
const purgeCSS = new PurgeCSS();
const options = {
...defaultOptions,
...opts,
safelist: standardizeSafelist(opts?.safelist),
};

if (opts && typeof opts.contentFunction === "function") {
options.content = opts.contentFunction(
(root.source && root.source.input.file) || ""
);
}

purgeCSS.options = options;

const { content, extractors } = options;

const fileFormatContents = content.filter(
(o) => typeof o === "string"
) as string[];
const rawFormatContents = content.filter(
(o) => typeof o === "object"
) as RawContent[];

const cssFileSelectors = await purgeCSS.extractSelectorsFromFiles(
fileFormatContents,
extractors
);
const cssRawSelectors = await purgeCSS.extractSelectorsFromString(
rawFormatContents,
extractors
);

const selectors = mergeExtractorSelectors(cssFileSelectors, cssRawSelectors);

//purge unused selectors
purgeCSS.walkThroughCSS(root, selectors);

if (purgeCSS.options.fontFace) purgeCSS.removeUnusedFontFaces();
if (purgeCSS.options.keyframes) purgeCSS.removeUnusedKeyframes();
if (purgeCSS.options.variables) purgeCSS.removeUnusedCSSVariables();

if (purgeCSS.options.rejected && purgeCSS.selectorsRemoved.size > 0) {
result.messages.push({
type: "purgecss",
plugin: "postcss-purgecss",
text: `purging ${purgeCSS.selectorsRemoved.size} selectors:
${Array.from(purgeCSS.selectorsRemoved)
.map((selector) => selector.trim())
.join("\n ")}`,
});
purgeCSS.selectorsRemoved.clear();
}
}

const purgeCSSPlugin: PluginCreator<UserDefinedOptions> = function (opts) {
if (typeof opts === "undefined")
throw new Error("PurgeCSS plugin does not have the correct options");
return {
postcssPlugin: PLUGIN_NAME,
Once(root, helpers) {
return purgeCSS(opts, root, helpers);
},
};
};
purgeCSSPlugin.postcss = true;

// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function createPurgeCSSPlugin(postCSS: PostCSSVersions) {
if (isPostCSSv8(postCSS)) {
return purgeCSSPlugin;
}

return (postCSS as PostCSSv7).plugin(
"postcss-purgecss",
(opts?: UserDefinedOptions) => {
if (typeof opts === "undefined")
throw new Error("PurgeCSS plugin does not have the correct options");
return async function (root, helpers): Promise<void> {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return purgeCSS(opts, root, helpers);
};
}
);
}
8 changes: 8 additions & 0 deletions packages/postcss-purgecss/src/types/index.ts
Expand Up @@ -3,6 +3,9 @@ import {
StringRegExpArray,
} from "../../../purgecss/src/types/index";

import postCSS7 from "postcss7";
import * as postCSS8 from "postcss";

export interface RawContent<T = string> {
extension: string;
raw: T;
Expand Down Expand Up @@ -31,3 +34,8 @@ export interface UserDefinedOptions {
safelist?: UserDefinedSafelist;
blocklist?: StringRegExpArray;
}

export type PostCSSv7 = typeof postCSS7;
export type PostCSSv8 = typeof postCSS8.default;

export type PostCSSVersions = PostCSSv8 | PostCSSv7;
2 changes: 1 addition & 1 deletion packages/purgecss/package.json
Expand Up @@ -41,7 +41,7 @@
"dependencies": {
"commander": "^6.0.0",
"glob": "^7.0.0",
"postcss": "7.0.32",
"postcss": "^8.0.9",
"postcss-selector-parser": "^6.0.2"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/purgecss/src/VariablesStructure.ts
@@ -1,4 +1,4 @@
import postcss from "postcss";
import * as postcss from "postcss";
import { StringRegExpArray } from "./types";

class VariableNode {
Expand Down

0 comments on commit beb4a59

Please sign in to comment.