Skip to content

Commit

Permalink
fix(purgecss-webpack-plugin): add sourcemap support #409
Browse files Browse the repository at this point in the history
BREAKING CHANGE: drop webpack 4 support
  • Loading branch information
Ffloriel committed Feb 23, 2022
1 parent b2b92fc commit b3f73ed
Show file tree
Hide file tree
Showing 11 changed files with 117 additions and 57 deletions.
7 changes: 3 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion packages/postcss-purgecss/src/types/index.ts
Expand Up @@ -9,6 +9,7 @@ import {
*
* @public
*/
export interface UserDefinedOptions extends Omit<PurgeCSSUserDefinedOptions, "css"> {
export interface UserDefinedOptions extends Omit<PurgeCSSUserDefinedOptions,"content" | "css"> {
content?: PurgeCSSUserDefinedOptions['content'];
contentFunction?: (sourceFile: string) => Array<string | RawContent>;
}
Expand Up @@ -11,6 +11,7 @@ const PATHS = {

module.exports = {
mode: "development",
devtool: false,
entry: "./src/index.js",
context: path.resolve(__dirname),
optimization: {
Expand Down
Expand Up @@ -11,6 +11,7 @@ const PATHS = {

module.exports = {
mode: "development",
devtool: false,
entry: {
bundle: "./src/index.js",
legacy: "./src/legacy.js",
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Expand Up @@ -14,6 +14,7 @@ const PATHS = {

module.exports = {
mode: "development",
devtool: "source-map",
optimization: {
splitChunks: {
cacheGroups: {
Expand Down
9 changes: 4 additions & 5 deletions packages/purgecss-webpack-plugin/package.json
Expand Up @@ -38,19 +38,18 @@
},
"dependencies": {
"purgecss": "^4.1.3",
"webpack": "*",
"webpack-sources": "^3.2.0"
"webpack": ">=5.0.0"
},
"bugs": {
"url": "https://github.com/FullHuman/purgecss/issues"
},
"devDependencies": {
"@types/webpack-sources": "^3.2.0",
"css-loader": "^6.2.0",
"mini-css-extract-plugin": "^2.1.0"
"css-loader": "^6.6.0",
"mini-css-extract-plugin": "^2.5.0"
},
"peerDependencies": {
"webpack": "*"
"webpack": ">=5.0.0"
},
"publishConfig": {
"access": "public",
Expand Down
138 changes: 95 additions & 43 deletions packages/purgecss-webpack-plugin/src/index.ts
@@ -1,8 +1,12 @@
import * as fs from "fs";
import * as path from "path";
import { PurgeCSS, defaultOptions } from "purgecss";
import { Compilation, Compiler } from "webpack";
import { ConcatSource } from "webpack-sources";
import {
PurgeCSS,
defaultOptions,
ResultPurge,
UserDefinedOptions as PurgeCSSUserDefinedOptions,
} from "purgecss";
import { Compilation, Compiler, sources } from "webpack";
import { PurgedStats, UserDefinedOptions } from "./types";

export * from "./types";
Expand All @@ -12,6 +16,7 @@ const pluginName = "PurgeCSS";

/**
* Get the filename without ?hash
*
* @param fileName - file name
*/
function getFormattedFilename(fileName: string): string {
Expand All @@ -23,6 +28,7 @@ function getFormattedFilename(fileName: string): string {

/**
* Returns true if the filename is of types of one of the specified extensions
*
* @param filename - file name
* @param extensions - extensions
*/
Expand All @@ -31,6 +37,78 @@ function isFileOfTypes(filename: string, extensions: string[]): boolean {
return extensions.includes(extension);
}

function getPurgeCSSOptions(
pluginOptions: UserDefinedOptions,
filesToSearch: string[],
asset: sources.Source,
fileName: string,
sourceMap: boolean
): PurgeCSSUserDefinedOptions {
const options = {
...defaultOptions,
...pluginOptions,
content: filesToSearch,
css: [
{
raw: asset.source().toString(),
},
],
};

if (typeof options.safelist === "function") {
options.safelist = options.safelist();
}

if (typeof options.blocklist === "function") {
options.blocklist = options.blocklist();
}

return {
content: options.content,
css: options.css,
defaultExtractor: options.defaultExtractor,
extractors: options.extractors,
fontFace: options.fontFace,
keyframes: options.keyframes,
output: options.output,
rejected: options.rejected,
variables: options.variables,
safelist: options.safelist,
blocklist: options.blocklist,
sourceMap: sourceMap ? { inline: false, to: fileName } : false,
};
}

/**
* Create the Source instance result of PurgeCSS
*
* @param name - asset name
* @param asset - webpack asset
* @param purgeResult - result of PurgeCSS purge method
* @param sourceMap - wether sourceMap is enabled
* @returns the new Source
*/
function createSource(
name: string,
asset: sources.Source,
purgeResult: ResultPurge,
sourceMap: boolean
): sources.Source {
if (!sourceMap || !purgeResult.sourceMap) {
return new sources.RawSource(purgeResult.css);
}
const { source, map } = asset.sourceAndMap();

return new sources.SourceMapSource(
purgeResult.css,
name,
purgeResult.sourceMap,
source.toString(),
map,
false
);
}

/**
* @public
*/
Expand Down Expand Up @@ -80,60 +158,34 @@ export class PurgeCSSPlugin {
return this.options.only.some((only) => name.includes(only));
}

return Array.isArray(chunk.files)
? chunk.files.includes(name)
: chunk.files.has(name);
return chunk.files.has(name);
});

for (const [name, asset] of assetsToPurge) {
const filesToSearch = entryPaths.filter(
(v) => !styleExtensions.some((ext) => v.endsWith(ext))
);

// Compile through Purgecss and attach to output.
// This loses sourcemaps should there be any!
const options = {
...defaultOptions,
...this.options,
content: filesToSearch,
css: [
{
raw: asset.source().toString(),
},
],
};

if (typeof options.safelist === "function") {
options.safelist
options.safelist = options.safelist();
}

if (typeof options.blocklist === "function") {
options.blocklist = options.blocklist();
}
const sourceMapEnabled = !!compilation.compiler.options.devtool;
const purgeCSSOptions = getPurgeCSSOptions(
this.options,
filesToSearch,
asset,
name,
sourceMapEnabled
);

const purgecss = await new PurgeCSS().purge({
content: options.content,
css: options.css,
defaultExtractor: options.defaultExtractor,
extractors: options.extractors,
fontFace: options.fontFace,
keyframes: options.keyframes,
output: options.output,
rejected: options.rejected,
variables: options.variables,
safelist: options.safelist,
blocklist: options.blocklist,
});
const purgecss = await new PurgeCSS().purge(purgeCSSOptions);
const purged = purgecss[0];

if (purged.rejected) {
this.purgedStats[name] = purged.rejected;
}

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
compilation.updateAsset(name, new ConcatSource(purged.css));
compilation.updateAsset(
name,
createSource(name, asset, purged, sourceMapEnabled)
);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/purgecss-webpack-plugin/src/types/index.ts
Expand Up @@ -28,7 +28,7 @@ export type PurgedStats = {
/**
* @public
*/
export type UserDefinedOptions = Omit<PurgeCSSUserDefinedOptions, "css" | "content" | "safelist" | "blocklist"> & {
export type UserDefinedOptions = Omit<PurgeCSSUserDefinedOptions, "css" | "content" | "safelist" | "blocklist" | "sourceMap"> & {
paths: string[] | PathFunction;
moduleExtensions?: string[];
verbose?: boolean;
Expand Down
5 changes: 4 additions & 1 deletion packages/purgecss/src/index.ts
Expand Up @@ -606,7 +606,10 @@ class PurgeCSS {
if (this.options.keyframes) this.removeUnusedKeyframes();
if (this.options.variables) this.removeUnusedCSSVariables();

const postCSSResult = root.toResult({ map: this.options.sourceMap });
const postCSSResult = root.toResult({
map: this.options.sourceMap,
to: typeof this.options.sourceMap === 'object' ? this.options.sourceMap.to : undefined
});
const result: ResultPurge = {
css: postCSSResult.toString(),
file: typeof option === "string" ? option : option.name,
Expand Down
4 changes: 2 additions & 2 deletions packages/purgecss/src/types/index.ts
Expand Up @@ -136,7 +136,7 @@ export interface UserDefinedOptions {
/** {@inheritDoc Options.rejectedCss} */
rejectedCss?: boolean;
/** {@inheritDoc Options.sourceMap } */
sourceMap?: boolean | postcss.SourceMapOptions
sourceMap?: boolean | postcss.SourceMapOptions & { to?: string }
/** {@inheritDoc Options.stdin} */
stdin?: boolean;
/** {@inheritDoc Options.stdout} */
Expand Down Expand Up @@ -229,7 +229,7 @@ export interface Options {
rejected: boolean;
rejectedCss: boolean;
/** {@inheritDoc postcss#SourceMapOptions} */
sourceMap: boolean | postcss.SourceMapOptions
sourceMap: boolean | postcss.SourceMapOptions & { to?: string }
stdin: boolean;
stdout: boolean;
variables: boolean;
Expand Down

0 comments on commit b3f73ed

Please sign in to comment.