UNPKG

html-bundler-webpack-plugin

Version:

Generates complete single-page or multi-page website from source assets. Built-in support for Markdown, Eta, EJS, Handlebars, Nunjucks, Pug. Alternative to html-webpack-plugin.

126 lines (103 loc) 4.29 kB
const vm = require('vm'); const { isWin, parseQuery } = require('../../Common/Helpers'); class ResponsiveLoader { pluginOption = null; isUsed = false; options = null; loaderOptions = new Map(); searchModuleString = '/node_modules/responsive-loader/'; constructor({ pluginOption }) { this.pluginOption = pluginOption; // bind this context to the method for using in any context as reference to this method this.getAsset = this.getAsset.bind(this); } /** * Initialize. * * @param {Object} compiler The webpack compiler object. */ init(compiler) { const { rules } = compiler.options.module || {}; this.loaderOptions.clear(); this.isUsed = false; if (rules) this.isUsed = JSON.stringify(rules).indexOf('"responsive-loader"') > 0; if (isWin) this.searchModuleString = '\\node_modules\\responsive-loader\\'; } /** * Find a loader option used in the module. * Note: different modules may have their own loader options. * * @param {Object} module The Webpack module of asset. * @return {null|Object} Return loader options if module found otherwise null. */ findModuleLoaderOptions(module) { const { rawRequest, loaders } = module; if (!this.loaderOptions.has(rawRequest)) { const loader = loaders.find((item) => item.loader.indexOf(this.searchModuleString) > 0); if (!loader) { this.loaderOptions.set(rawRequest, null); return null; } const options = loader.options ? loader.options : {}; this.loaderOptions.set(rawRequest, options); return options; } return this.loaderOptions.get(rawRequest); } /** * Get the result of resource processing via responsive-loader. * * Note: in the template is impossible to use `responsive-loader` as an object, * because processing happens in a later stage then used the result in template. * * @param {Object} module The Webpack module of asset. * @param {FileInfo} issuer The issuer of the module. * @return {null|string} The compiled result as string to replace required resource with this result. */ getAsset(module, issuer) { if (this.isUsed !== true) return null; const loaderOptions = this.findModuleLoaderOptions(module); if (loaderOptions == null) return null; const { resource: sourceFile, rawRequest, buildInfo } = module; const query = parseQuery(rawRequest); // the query `sizes` parameter has prio over options let sizes = query.sizes && query.sizes.length > 0 ? query.sizes : loaderOptions.sizes || []; let asset = null; // return one of image object properties: src, srcSet, width, height // but in reality is the `srcSet` property useful // note: if no query param `prop` and used the 'sizes' param, then return value of `srcSet` if (query.prop) { const prop = query.prop; const originalSource = module.originalSource(); const source = originalSource ? originalSource.source().toString() : null; let resultProp = null; if (source) { const contextObject = vm.createContext({ __webpack_public_path__: this.pluginOption.getOutputPath(issuer.filename), module: { exports: {} }, }); const script = new vm.Script(source, { filename: sourceFile }); const result = script.runInContext(contextObject); if (result && result.hasOwnProperty(prop)) { resultProp = result[prop].toString(); } } return resultProp; } // fallback: retrieve all generated assets as the coma-separated list // get the real filename of the asset by usage a loader for the resource, e.g. `responsive-loader` // and add the original asset file to trash to remove it from compilation const assets = buildInfo.assetsInfo != null ? Array.from(buildInfo.assetsInfo.keys()) : []; if (assets.length === 1) { asset = this.pluginOption.getOutputFilename(assets[0], issuer.filename); } else if (assets.length > 1 && sizes.length > 1) { asset = assets .map( (assetFile, index) => this.pluginOption.getOutputFilename(assetFile, issuer.filename) + ` ${sizes[index]}w` ) .join(','); } return asset; } } module.exports = ResponsiveLoader;