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
JavaScript
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}`));
});
}
}