UNPKG

gatsby-plugin-perf-budgets

Version:

**gatsby-plugin-perf-budgets** is an *experimental* plugin to make browsing bundles on a page basis easier. It is used in conjunction with `gatsby-plugin-webpack-bundle-analyser-v2`.

312 lines (255 loc) 8.39 kB
const fs = require(`fs-extra`); const path = require(`path`); const { transform, kebabCase } = require(`lodash`); const gzipSize = require("gzip-size"); const ejs = require("ejs"); const opener = require("opener"); const { getViewerData } = require(`./lib/analyzer`); // const generateComponentChunkName = (componentPath, store) => { // const program = store.getState().program; // let directory = `/`; // if (program && program.directory) { // directory = program.directory; // } // const name = path.relative(directory, componentPath); // return `component---${kebabCase(name)}`; // }; console.log(`test`); const fixedPagePath = (pagePath) => (pagePath === `/` ? `index` : pagePath); const getFilePath = ({ publicDir }, pagePath) => path.join(publicDir, `page-data`, fixedPagePath(pagePath), `page-data.json`); class GatsbyWebpackPerfBudgetPlugin { constructor(options) { this.plugin = { name: `GatsbyWebpackPerfBudgetPlugin` }; this.opts = { storeStats: Promise.resolve(), ...(options || {}), }; } apply(compiler) { // console.log("saving meeee"); compiler.hooks.done.tapAsync(this.plugin.name, (stats, done) => { // console.log("saved meeee"); this.opts.storeStats(stats.toJson()).then(done); }); } } const STATS_CACHE_KEY = `stats`; exports.onCreateWebpackConfig = ({ actions, stage, cache, reporter }) => { if (stage === `build-javascript`) { reporter.info(`[gatsby-plugin-perf-budgets] hooking into webpack`); actions.setWebpackConfig({ plugins: [ new GatsbyWebpackPerfBudgetPlugin({ storeStats: async (stats) => { cache.set(STATS_CACHE_KEY, stats); }, }), ], }); } }; const assetsRoot = path.join(__dirname, "public"); function getAssetContent(filename) { const assetPath = path.join(assetsRoot, filename); if (!assetPath.startsWith(assetsRoot)) { throw new Error(`"${filename}" is outside of the assets root`); } return fs.readFileSync(assetPath, "utf8"); } // console.log("wat6"); function escapeJson(json) { return JSON.stringify(json).replace(/</gu, "\\u003c"); } exports.onPostBuild = async ({ cache, store, reporter }) => { const stats = await cache.get(STATS_CACHE_KEY); const state = store.getState(); const publicDir = path.join(state.program.directory, `public`); const data = getViewerData(stats, publicDir); // const componentPathToChunkName = {}; // state.components.forEach(({ componentPath }) => { // const chunkName = generateComponentChunkName(componentPath, store); // componentPathToChunkName[componentPath] = chunkName; // }); const traverse = (item, fn) => { fn(item); if (item.groups) { item.groups.forEach((subItem) => traverse(subItem, fn)); } }; // data.forEach((item) => // traverse(item, (item) => { // if (item.path === `./public/static/d`) { // item.label = "Static Queries Results"; // } else if ( // item.path && // item.path.startsWith(`./public/static/d`) && // item.path.endsWith(".json") // ) { // const hash = path.basename(item.path).replace(".json", ""); // const sqc = staticQueries[hash]; // item.label = `Static Query result (from "${sqc}")`; // item.type = `static-data`; // item.fetch = `/` + path.relative(`./public`, item.path); // } // }) // ); const chunksDataByChunkName = data.reduce((acc, item) => { acc[item.label] = { ...item, type: `module-chunk`, }; return acc; }, {}); const l = fs.readFileSync( path.join(publicDir, `webpack.stats.json`), `utf-8` ); const webpackStatsJson = JSON.parse(l); // const z = (await fs.readJson(path.join(publicDir, `webpack.stats.json`), { encoding: `utf-8`})) // console.log({ z, p:path.join(publicDir, `webpack.stats.json`), l }) const assetsByChunkName = transform( webpackStatsJson.assetsByChunkName, (result, names, key) => { result[key] = names.filter((v) => v.endsWith(`.js`)); // return }, {} ); Object.keys(assetsByChunkName).forEach((chunkName) => { // chunk.forEach(asse) if (chunkName.startsWith(`component---`)) { const assets = assetsByChunkName[chunkName]; assets.forEach((asset) => { chunksDataByChunkName[asset].type = `page-chunk`; }); } }); assetsByChunkName.app.forEach((appChunk) => { chunksDataByChunkName[appChunk].type = `app-chunk`; }); const pagesData = {}; // const moduleData = {}; state.pages.forEach(({ componentChunkName, path: pagePath }) => { const pageDataStatsPath = getFilePath({ publicDir }, pagePath); const content = fs.readFileSync(pageDataStatsPath, `utf-8`); const parsedContent = JSON.parse(content); const moduleDependencies = parsedContent.moduleDependencies || []; const staticQueryHashes = parsedContent.staticQueryHashes || []; const result = { parsedSize: Buffer.byteLength(content), gzipSize: gzipSize.sync(content), statSize: 5000, label: `Page-data`, path: path.relative(state.program.directory, pageDataStatsPath), fetch: `/` + path.relative(publicDir, pageDataStatsPath), isAsset: true, groups: [], componentChunkName, moduleDependencies, staticQueryHashes, pagePath, // path: pagePath, type: `page-data`, }; // moduleDependencies.forEach(); pagesData[pagePath] = result; }); const staticQueries = {}; state.staticQueryComponents.forEach((sqc) => { const componentPath = path.relative( state.program.directory, sqc.componentPath ); const staticQueryStatsPath = path.join( publicDir, `page-data`, `sq`, `d`, `${sqc.hash}.json` ); const content = fs.readFileSync(staticQueryStatsPath, `utf-8`); // console.log({ sqc }); staticQueries[sqc.hash] = { path: componentPath, parsedSize: Buffer.byteLength(content), gzipSize: gzipSize.sync(content), statSize: 5000, label: `Static query ${sqc.componentPath}`, type: `static-query-data`, fetch: `/` + path.relative(publicDir, staticQueryStatsPath), isAsset: true, groups: [], }; }); const appDataPath = path.join(publicDir, `page-data`, `app-data.json`); const content = fs.readFileSync(appDataPath, `utf-8`); const appData = { parsedSize: Buffer.byteLength(content), gzipSize: gzipSize.sync(content), statSize: 5000, label: `App-data`, path: path.relative(state.program.directory, appDataPath), fetch: `/` + path.relative(publicDir, appDataPath), isAsset: true, groups: [], // path: path.relative(publicDir, appDataPath), type: `app-data`, }; // const { assetsByChunkName } = ; const reportFilename = `_report.html`; // debugger; await new Promise((resolve, reject) => { ejs.renderFile( `${__dirname}/views/gatsby.ejs`, { mode: "static", title: "Gatsby page resources and bundle analyzer", // chartData, assetsByChunkName, pagesData, chunksDataByChunkName, appData, staticQueries, defaultSizes: `gzip`, enableWebSocket: false, // Helpers assetContent: getAssetContent, escapeJson, }, (err, reportHtml) => { try { if (err) { reporter.error(err); reject(err); return; } const reportFilepath = path.resolve( publicDir || process.cwd(), reportFilename ); fs.ensureDirSync(path.dirname(reportFilepath)); fs.writeFileSync(reportFilepath, reportHtml); // logger.info( // `${bold("Webpack Bundle Analyzer")} saved report to ${bold( // reportFilepath // )}` // ); // if (openBrowser) { // opener(`file://${reportFilepath}`); // } reporter.info( `[gatsby-plugin-perf-budgets] "${reportFilepath}" written` ); resolve(); } catch (e) { reject(e); } } ); }); // debugger; // // await fs.outputJSON(`./public/_stats.json`, stats); // console.log("written to file"); };