Created
August 8, 2019 15:09
-
-
Save PutziSan/89711654c0c012ba9ea6882370c2cdf4 to your computer and use it in GitHub Desktop.
PurgeCSS via Webpack without glob
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const esprima = require("esprima"); | |
const Purgecss = require("purgecss"); | |
const LastCallWebpackPlugin = require("last-call-webpack-plugin"); | |
class Emitter extends require("events") {} | |
// by https://github.com/FullHuman/purgecss-from-js/blob/master/index.js | |
class PurgeFromJS { | |
static extract(content) { | |
const tokens = esprima.tokenize(content); | |
const selectors = tokens | |
.filter(token => { | |
return ( | |
token.type === "Identifier" || | |
token.type === "Template" || | |
token.type === "String" | |
); | |
}) | |
.reduce((acc, token) => { | |
if (token.type === "String") { | |
// cut single/double quotes from the string | |
// because esprima wraps string to a string | |
const unwrappedString = token.value.slice(1, token.value.length - 1); | |
return acc.concat(unwrappedString.split(" ")); // in case if string contains a list of classes | |
} else if (token.type === "Template") { | |
// cut backticks from the template | |
const len = token.value.length; | |
const isOpenedTemplate = token.value[0] === "`"; | |
const isClosedTemplate = token.value[len - 1] === "`"; | |
const unwrappedTemplate = token.value.slice( | |
isOpenedTemplate ? 1 : 0, | |
isClosedTemplate ? len - 1 : len | |
); | |
return acc.concat(unwrappedTemplate.split(" ")); | |
} | |
return acc.concat(token.value); | |
}, []) | |
// clear selectors from empty strings | |
.filter(Boolean); | |
return [...new Set(selectors)]; // remove duplicates | |
} | |
} | |
// use it in plugins like: | |
// optimization: { | |
// minimizer: [ | |
// new TerserPlugin({ sourceMap: true }), | |
// require("./purgecss-webpack-plugin.js")() | |
// ] | |
// } | |
module.exports = function newProcessor() { | |
let timeoutId; | |
const resolvedEvt = new Emitter(); | |
const _srcs = []; | |
function newSource(s) { | |
_srcs.push(s); | |
const tid = setTimeout(() => { | |
if (tid === timeoutId) { | |
resolvedEvt.emit("resolved", _srcs); | |
} | |
}, 100); | |
timeoutId = tid; | |
} | |
let allJsFilesP = new Promise(resolve => { | |
resolvedEvt.once("resolved", jsSources => { | |
resolve(jsSources); | |
}); | |
}); | |
return new LastCallWebpackPlugin({ | |
assetProcessors: [ | |
{ | |
phase: LastCallWebpackPlugin.PHASES.OPTIMIZE_CHUNK_ASSETS, | |
regExp: /\.js$/, | |
processor: (assetName, asset) => { | |
const s = asset.source(); | |
newSource(s); | |
return Promise.resolve(s); | |
} | |
}, | |
{ | |
phase: LastCallWebpackPlugin.PHASES.EMIT, | |
regExp: /\.css$/, | |
processor: async (assetName, asset, assets) => { | |
const jsSources = await allJsFilesP; | |
var purgecss = new Purgecss({ | |
extractors: [ | |
{ | |
extractor: PurgeFromJS, | |
extensions: ["js"] | |
} | |
], | |
content: jsSources.map(s => ({ raw: s, extension: "js" })), | |
css: [ | |
{ | |
raw: asset.source() | |
} | |
] | |
}); | |
const purged = purgecss.purge()[0]; | |
if (purged.rejected) { | |
throw new Error(JSON.stringify(purged.rejected)); | |
} | |
return purged.css; | |
} | |
} | |
], | |
canPrint: true | |
}); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment