-
-
Save lukepolo/52016d00191eb7ef5bd4f0018a8f58d7 to your computer and use it in GitHub Desktop.
Webpack plugin for importing split library bundles within CRA environment.
This file contains hidden or 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 fs = require('fs'); | |
const webpack = require('webpack'); | |
const HtmlWebpackPlugin = require('html-webpack-plugin'); | |
// Used to replace vendor imports with empty string | |
const ConstDependency = require('webpack/lib/dependencies/ConstDependency'); | |
function hashString(str) { | |
var hash = 0, | |
i, | |
chr; | |
if (str.length === 0) return hash; | |
for (i = 0; i < str.length; i++) { | |
chr = str.charCodeAt(i); | |
hash = (hash << 5) - hash + chr; | |
hash |= 0; // Convert to 32bit integer | |
} | |
return hash; | |
} | |
const VENDORS_INJECTION_PLUGIN = 'VENDORS_INJECTION_PLUGIN'; | |
/** | |
* Intercept vendor imports and inject them to the head tag. | |
* Depending on webpack "lifecycle" as everything is asynchronous. | |
* Normal factory module should execute first and provide a list of vendors | |
* that are used later when Html injections comes to order. | |
* | |
* Usage example: | |
* import 'vendor:../packages/dist/vendors.index.js'; | |
* import 'vendor:../packages/dist/vendors.css'; | |
*/ | |
class VendorsInjectionPlugin { | |
apply(compiler) { | |
const vendorRgxp = /vendor:/; | |
const vendors = []; | |
// Ignore vendor files from webpack processing | |
new webpack.IgnorePlugin(vendorRgxp).apply(compiler); | |
// Handle import statements | |
compiler.hooks.normalModuleFactory.tap(VENDORS_INJECTION_PLUGIN, nmf => { | |
nmf.hooks.parser | |
.for('javascript/auto') | |
.tap(VENDORS_INJECTION_PLUGIN, parser => { | |
parser.hooks.import.tap( | |
VENDORS_INJECTION_PLUGIN, | |
(statement, source) => { | |
if (vendorRgxp.test(source)) { | |
// Register vendor import | |
const resource = source.split(':')[1]; | |
vendors.push(resource); | |
// Remove vendor import statement from source | |
// Not sure how this is working. Taken from Webpack Harmony import plugin. | |
parser.state.module.addDependency( | |
new ConstDependency('', statement.range), | |
); | |
return true; | |
} | |
}, | |
); | |
}); | |
}); | |
if (process.env.NODE_ENV === 'production') { | |
// In production libs don't split bundle thus | |
// no vendors needs to be injected | |
return; | |
} | |
// Inject vendors (chunks) | |
compiler.hooks.emit.tapPromise(VENDORS_INJECTION_PLUGIN, compilation => { | |
return new Promise(resolve => { | |
// "Subscribe" to HTML injections flow - place to add previously registered vendors | |
HtmlWebpackPlugin.getHooks(compilation).alterAssetTagGroups.tapAsync( | |
VENDORS_INJECTION_PLUGIN, | |
(data, callback) => { | |
Promise.all( | |
vendors.map( | |
resource => | |
new Promise(resolve => { | |
fs.readFile(resource, (err, source) => { | |
const ext = resource.split('.').pop(); | |
// Transform relative path to url safe path | |
const hashedResourcePath = `vendors/${hashString( | |
resource, | |
)}.${ext}`; | |
if (err) { | |
throw new Error(err); | |
} | |
compilation.assets[hashedResourcePath] = { | |
source: () => source, | |
size: () => source.length, | |
}; | |
const assetSrc = `/${hashedResourcePath}`; | |
let vendorTagDesc; | |
switch (ext) { | |
case 'js': | |
vendorTagDesc = { | |
tagName: 'script', | |
voidTag: false, | |
attributes: { src: assetSrc }, | |
}; | |
break; | |
case 'css': | |
vendorTagDesc = { | |
tagName: 'link', | |
voidTag: false, | |
attributes: { | |
type: 'text/css', | |
href: assetSrc, | |
rel: 'stylesheet', | |
}, | |
}; | |
break; | |
default: | |
throw new Error('Unknown vendor type'); | |
} | |
data.headTags.push(vendorTagDesc); | |
resolve(); | |
}); | |
}), | |
), | |
).then(() => callback(null, data)); | |
}, | |
); | |
resolve(compilation); | |
}); | |
}); | |
} | |
} | |
module.exports = VendorsInjectionPlugin; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment