UNPKG

next-era

Version:

Welcome to **Next Era**! A comprehensive library designed to supercharge your **Next.js** applications with powerful utilities and significant performance optimizations. Build faster, more efficient, and feature-rich Next.js projects with ease.

91 lines (90 loc) 4.32 kB
import * as crypto from "crypto"; import * as fs from "fs"; import _ from "lodash"; import * as path from "path"; import { interpolate, normalizePath } from "../utils/index.js"; export default class NextEraPlugin { #options; constructor(options) { this.#options = _.defaultsDeep({}, options, { sw: { input: "node_modules/next-era/dist/sw/public/sw.js", component: "node_modules/next-era/dist/sw/component.js", output: "public/sw.js", resources: [ ...(options.sw?.resources || []), ...this.readFiles("public", ["/sw.js"]), ], strategy: { cf: _.compact([...(options.sw?.strategy.cf || [])]), nf: _.compact([ ...(options.sw?.strategy.nf || []), process.env.NODE_ENV === "development" ? "/**" : undefined, ]), swr: _.compact([...(options.sw?.strategy.swr || [])]), }, }, }); } doReadFiles = (dir, parentPath, files) => { fs.readdirSync(dir, { withFileTypes: true }).map((entry) => { const file = path.join(parentPath, entry.name); const filePath = path.join(dir, entry.name); if (entry.isDirectory()) { this.doReadFiles(filePath, file, files); } else { files.push(normalizePath(file)); } }); }; readFiles(dir, exclude = []) { const files = []; this.doReadFiles(dir, "/", files); return _.without(files, ...exclude); } readMD5Sync(filePath) { const fileBuffer = fs.readFileSync(filePath); return crypto.createHash("md5").update(fileBuffer).digest("hex"); } apply(compiler) { compiler.hooks.environment.tap("NextEraPlugin", () => { const inputFile = path.resolve(this.#options.sw.input); const inputDir = path.dirname(inputFile); const outputFile = path.resolve(this.#options.sw.output); if (!fs.existsSync(inputDir)) { console.warn(`[NextEraPlugin] Directory not found: ${inputDir}`); return; } const sw = fs.readFileSync(inputFile, "utf8"); fs.writeFileSync(outputFile, interpolate(/(?:\/\/[ ]*{{([\s\S]+?)}}$)|(?:\/\*[ ]*{{([\s\S]+?)}}[ ]*\*\/).*$/gm) .template(sw) .compiled({ resourcesToCache: JSON.stringify(this.#options.sw.resources), task: { install: process.env.NODE_ENV === "development" ? "self.skipWaiting()" : undefined, activate: process.env.NODE_ENV === "development" ? "selve.clients.claim()" : undefined, }, strategy: { cf: JSON.stringify(this.#options.sw.strategy.cf), nf: JSON.stringify(this.#options.sw.strategy.nf), swr: JSON.stringify(this.#options.sw.strategy.swr), }, })); const md5 = this.readMD5Sync(outputFile); const resourcesSize = this.#options.sw.resources.length; const { nf, swr, cf } = this.#options.sw.strategy; console.debug(`[NextEraPlugin] Service worker's MD5: ${md5}`); console.debug(`[NextEraPlugin] Service worker's resources (${resourcesSize}/${resourcesSize}): ${JSON.stringify(this.#options.sw.resources, null, " ")}`); console.debug(`[NextEraPlugin] Service worker's CF URIs (${cf.length}/${cf.length}): ${JSON.stringify(cf, null, " ")}`); console.debug(`[NextEraPlugin] Service worker's NF URIs (${nf.length}/${nf.length}): ${JSON.stringify(nf, null, " ")}`); console.debug(`[NextEraPlugin] Service worker's SWR URIs (${swr.length}/${swr.length}): ${JSON.stringify(swr, null, " ")}`); const component = fs.readFileSync(this.#options.sw.component, "utf8"); fs.writeFileSync(this.#options.sw.component, component.replaceAll(/\/sw\.js(?:\?v=[\w]+)?/g, `/sw.js?v=${md5}`)); }); } }