UNPKG

dns-webpack-plugin

Version:
168 lines (146 loc) 4.4 kB
// @ts-check // Import types /** @typedef {import("./typings").Options} DnsWebpackPluginOptions */ 'use strict'; const glob = require('glob'); const fs = require('fs'); const { parse } = require('node-html-parser'); const urlRegex = require('url-regex'); const path = require('path'); function isPlainObject(value) { if (Object.prototype.toString.call(value) !== '[object Object]') { return false; } const prototype = Object.getPrototypeOf(value); return prototype === null || prototype === Object.getPrototypeOf({}); } class DnsOptimizationWebpackPlugin { name = 'dns-optimization-webpack-plugin'; scanFileType = ['html', 'js', 'css']; /** * @param {DnsWebpackPluginOptions} [options] */ constructor(options = {}) { if (isPlainObject(options) === false) { throw new Error( ` ${this.name} only accepts an options object. See:https://www.npmjs.com/package/dns-webpack-plugin` ); } this.options = options; this.initialOptimization = false; } apply(compiler) { if (!compiler.options.output || !compiler.options.output.path) { console.warn( `${this.name}: options.output.path not defined. Plugin disabled...` ); return; } this.outputPath = compiler.options.output.path; const hooks = compiler.hooks; hooks.emit.tap(this.name, (compilation, callback) => { this.handleInitial(compilation); }); hooks.done.tap(this.name, stats => { this.handleDone(stats); }); } /** * 如果编译过程报错,停止本插件的操作 * @param {*} compilation */ handleInitial(compilation) { const stats = compilation.getStats(); if (stats.hasErrors()) { return; } } /** * 资源输出后守卫,如果编译过程报错,停止本插件的操作 */ handleDone(stats) { if (this.initialOptimization) { return; } if (stats.hasErrors()) { return; } this.initialOptimization = true; this.optimizateFiles(); } /** * 优化文件主函数 */ optimizateFiles() { //遍历整个目录,找到所有的文件 try { let scanFileTypes = this.filterScanFilesType(); let files = glob.sync(`${this.outputPath}/**/*.{${scanFileTypes}}`); const urlList = []; files.forEach(file => { urlList.push(...this.matchUrlFromFile(file)); }); this.handleHtmlFile(Array.from(new Set(urlList))); } catch (err) { console.log(err); } } /** * 从文件中匹配url * @param {*} file */ matchUrlFromFile(file) { const urlPattern = /(https?:\/\/[^/]*)/i; const urls = []; // 匹配fileContent中的所有url const fileContent = fs.readFileSync(file, 'utf8'); const matches = fileContent.match(urlRegex()); if (matches) { matches.forEach(match => { const url = match.match(urlPattern); if (url && url[1]) { urls.push(url[1]); } }); } return urls; } /** * 操作html文件 * @param {*} urlList */ handleHtmlFile(urlList) { //合并用户配置的dns this.options.dns && urlList.push(...Array.from(new Set(this.options.dns))); const files = glob.sync(`${this.outputPath}/**/*.html`); if (files.length === 0) return; let filterUrlList = urlList.filter(url => urlRegex().test(url)); const links = filterUrlList.map( url => `<link rel="dns-prefetch" href="${url}">` ); if (links.length === 0) return; for (const file of files) { const fileContent = fs.readFileSync(file, 'utf8'); const root = parse(fileContent); const head = root.querySelector('head'); head?.insertAdjacentHTML('afterbegin', links.join('')); fs.writeFileSync( this.options.as ? path.join(this.outputPath, this.options.as) : file, root.toString() ); } } /** * * @returns example 'html,js,css' */ filterScanFilesType() { const excludes = this.options.exclude || []; let result = this.scanFileType; excludes.forEach(item => { result = result.filter(fileType => fileType !== item); }); return result.join(','); } } module.exports = DnsOptimizationWebpackPlugin;