I come across a dilemma build a plugin for our front-end pipeline, I'll explain first what we want to achieve then the problem. At first, we have a set of variables which have to be replaced at build time with 'true' or 'false', then a set of variables names which have to be renamed and some other customisations. I'll give an example.
- Every
DCE_HAS_MODULE_PRE_ORDERShas to be replaced withfalse - Any occurence of a
/ST__[A-Z_]+/g(ST__MODULE_SETTING) wil be renamed toST1orST53, everywhere it's used.
Till now we've read the output file of webpack, replaced, then write again on disk, it works. But this does not allow us to use the full functionality of webpack, so we want to implement this using the inner-power of webpack.
This is the code which does the trick.
function prependStorefrontDCE() {
let currentSTIdx = 1;
const files = fs.readdirSync(outputDirPath).filter((e) => e.endsWith('.js'));
files.forEach((filename) => {
const filePath = outputDirPath + '/' + filename;
let payload = fs.readFileSync(filePath, 'utf8');
// @TODO: allow a way to define custom DCEs
// set custom DCEs to false
payload = payload.replace(/DCE_HAS_MODULE_PRE_ORDERS/g, 'false');
{
// Collect unique settings variables
let stVariables = new Set(payload.match(/ST__[A-Z_]+/g));
// Define a unique id for each one
let stUniqueIds = {};
stVariables.forEach(st => {
stUniqueIds[st] = `ST${currentSTIdx++}`;
});
// Declare settings in upper scope
stVariables = Array.from(stVariables).map(st => `let ${st} = null;`);
stVariables.push('let DCE = null;');
stVariables.push('ST__MODULES = {1: {}, 46: {}, 53: {}};');
stVariables.push('let URL_HTTP_HOST = "";');
let dceJS = stVariables.join('');
// add closure JS
payload = dceJS + payload;
console.log(dceJS);
// obfuscation for settings variables using the unique id
payload = payload.replace(/ST__[A-Z_]+/g, (match) => stUniqueIds[match]);
}
{
// Collect unique settings variables
let stVariables = new Set(payload.match(/DCE_[A-Z_]+/g));
// Define a unique id for each one
let stUniqueIds = {};
stVariables.forEach(st => {
stUniqueIds[st] = `DCE${currentSTIdx++}`;
});
// Declare settings in upper scope
stVariables = Array.from(stVariables).map(st => `let ${st} = true;`);
let dceJS = stVariables.join('');
// add closure JS
payload = dceJS + payload;
// obfuscation for settings variables using the unique id
payload = payload.replace(/DCE_[A-Z_]+/g, (match) => stUniqueIds[match]);
}
// remove DCE from CSS
payload = payload.replace(dceInCssRegex, '');
// set all DCE booleans to true
payload = payload.replace(dceHasRegex, 'true');
payload = payload.replace(dceRequireRegex, 'true');
// write file
fs.writeFileSync(filePath, payload);
});
}
And this is how we use it.
compiler.hooks.afterEmit.tap('after-build', async () => {
if (IS_PRODUCTION) {
// step 1/2
mangleProps();
}
// step 2/2
prependStorefrontDCE();
});
But in order de replace in the webpack modules I've tried many ways, but no one seems to work as expected. I've looked at a lot of plugins and tried hooks and methods but without any result. One which seems to answer half of requirements is DefinePlugin but it does not allow to find variables based on regex and replace them with custom.
Here is a piece of what I've tried.
compiler.hooks.run.tapAsync(
'DCEs', (compilation) => {
compilation.hooks.processAssets.tap(() => {
// Get the assets of webpack
const assets = compilation.getAssets();
// When processing assets, use the function to replace variable name
assets.forEach(asset => {
console.log('\n UpdateAsset', asset.name, '\n');
compilation.updateAsset(asset.name, (source) => {
return prependStorefrontDCE(source.source());
});
});
});
},
);
Aucun commentaire:
Enregistrer un commentaire