From 170c604ace4cd27b056412cadbbd008d87ec2379 Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Tue, 23 Feb 2021 12:23:03 +0100 Subject: [PATCH] fix(gatsby): re-add prefetch/preload links for child assets (#29693) * fix(gatsby): re-add prefetch/preload links for child assets * test(artifacts): add test for adding tags for webpack's magic comments --- .../artifacts/__tests__/index.js | 27 ++++++++++++ .../src/components/magic-comments/prefetch.js | 3 ++ .../src/components/magic-comments/preload.js | 3 ++ .../pages/dynamic-imports-magic-comments.js | 17 ++++++++ .../utils/gatsby-webpack-stats-extractor.ts | 23 +++++++++++ .../gatsby/src/utils/worker/render-html.ts | 41 ++++++++----------- 6 files changed, 89 insertions(+), 25 deletions(-) create mode 100644 integration-tests/artifacts/src/components/magic-comments/prefetch.js create mode 100644 integration-tests/artifacts/src/components/magic-comments/preload.js create mode 100644 integration-tests/artifacts/src/pages/dynamic-imports-magic-comments.js diff --git a/integration-tests/artifacts/__tests__/index.js b/integration-tests/artifacts/__tests__/index.js index 099643ae9d09a..417765a0bc5c2 100644 --- a/integration-tests/artifacts/__tests__/index.js +++ b/integration-tests/artifacts/__tests__/index.js @@ -396,6 +396,33 @@ describe(`First run (baseline)`, () => { manifest[runNumber].allPages.sort() ) }) + + describe(`should add for webpack's magic comments`, () => { + let htmlContent + beforeAll(() => { + htmlContent = fs.readFileSync( + path.join( + process.cwd(), + `public`, + `dynamic-imports-magic-comments`, + `index.html` + ), + `utf-8` + ) + }) + + it(`has prefetch link`, () => { + expect(htmlContent).toMatch( + //g + ) + }) + + it(`has preload link`, () => { + expect(htmlContent).toMatch( + //g + ) + }) + }) }) describe(`page-data files`, () => { diff --git a/integration-tests/artifacts/src/components/magic-comments/prefetch.js b/integration-tests/artifacts/src/components/magic-comments/prefetch.js new file mode 100644 index 0000000000000..8536a6030b7f3 --- /dev/null +++ b/integration-tests/artifacts/src/components/magic-comments/prefetch.js @@ -0,0 +1,3 @@ +export function forPrefetch() { + return `export-for-prefetch` +} diff --git a/integration-tests/artifacts/src/components/magic-comments/preload.js b/integration-tests/artifacts/src/components/magic-comments/preload.js new file mode 100644 index 0000000000000..d0d8ff1da8a5d --- /dev/null +++ b/integration-tests/artifacts/src/components/magic-comments/preload.js @@ -0,0 +1,3 @@ +export function forPreload() { + return `export-for-preload` +} diff --git a/integration-tests/artifacts/src/pages/dynamic-imports-magic-comments.js b/integration-tests/artifacts/src/pages/dynamic-imports-magic-comments.js new file mode 100644 index 0000000000000..b991d2e724da0 --- /dev/null +++ b/integration-tests/artifacts/src/pages/dynamic-imports-magic-comments.js @@ -0,0 +1,17 @@ +import * as React from "react" + +import( + /* webpackChunkName: "magic-comment-prefetch", webpackPrefetch: true */ `../components/magic-comments/prefetch` +).then(moduleForPrefetch => { + console.log({ forPrefetch: moduleForPrefetch.forPrefetch() }) +}) + +import( + /* webpackChunkName: "magic-comment-preload", webpackPreload: true */ `../components/magic-comments/preload` +).then(moduleForPreload => { + console.log({ forPreload: moduleForPreload.forPreload() }) +}) + +export default function DynamicImportsWithWebpackMagicComments() { + return
Sample for dynamic imports with webpack's magic comments
+} diff --git a/packages/gatsby/src/utils/gatsby-webpack-stats-extractor.ts b/packages/gatsby/src/utils/gatsby-webpack-stats-extractor.ts index fdaa7e189e7fb..038d2566a2db4 100644 --- a/packages/gatsby/src/utils/gatsby-webpack-stats-extractor.ts +++ b/packages/gatsby/src/utils/gatsby-webpack-stats-extractor.ts @@ -11,6 +11,7 @@ export class GatsbyWebpackStatsExtractor { compiler.hooks.done.tapAsync(this.plugin.name, (stats, done) => { const assets = {} const assetsMap = {} + const childAssets = {} for (const chunkGroup of stats.compilation.chunkGroups) { if (chunkGroup.name) { const files: Array = [] @@ -25,11 +26,33 @@ export class GatsbyWebpackStatsExtractor { f.slice(0, chunkGroup.name.length) === chunkGroup.name ) .map(filename => `/${filename}`) + + for (const [rel, childChunkGroups] of Object.entries( + chunkGroup.getChildrenByOrders( + stats.compilation.moduleGraph, + stats.compilation.chunkGraph + ) + )) { + if (!(chunkGroup.name in childAssets)) { + childAssets[chunkGroup.name] = {} + } + + const childFiles = [] + for (const childChunkGroup of childChunkGroups) { + for (const chunk of childChunkGroup.chunks) { + childFiles.push(...chunk.files) + } + } + + childAssets[chunkGroup.name][rel] = childFiles + } } } + const webpackStats = { ...stats.toJson({ all: false, chunkGroups: true }), assetsByChunkName: assets, + childAssetsByChunkName: childAssets, } fs.writeFile( path.join(`public`, `chunk-map.json`), diff --git a/packages/gatsby/src/utils/worker/render-html.ts b/packages/gatsby/src/utils/worker/render-html.ts index 7e812121e43b8..32521efe3f5ac 100644 --- a/packages/gatsby/src/utils/worker/render-html.ts +++ b/packages/gatsby/src/utils/worker/render-html.ts @@ -135,38 +135,29 @@ async function getScriptsAndStylesForTemplate( handleAsset(asset, `preload`) } - const namedChunkGroup = webpackStats.namedChunkGroups[chunkName] - if (!namedChunkGroup) { - continue - } - - for (const asset of namedChunkGroup.assets) { - handleAsset(asset.name, `preload`) - } - - // TODO: figure out childAssets for webpack@5 - there is no longer `childAssets` in webpack.stats.json // Handling for webpack magic comments, for example: // import(/* webpackChunkName: "", webpackPrefetch: true */ ``) - // will produce + // Shape of webpackStats.childAssetsByChunkName: // { - // namedChunkGroups: { + // childAssetsByChunkName: { // : { - // // [...] some fields we don't care about, - // childAssets: { - // prefetch: [ - // "-.js", - // "-.js.map", - // ] - // } + // prefetch: [ + // "-.js", + // ] // } // } // } - // for (const [rel, assets] of Object.entries(namedChunkGroup.childAssets)) { - // // @ts-ignore TS doesn't like that assets is not typed and especially that it doesn't know that it's Iterable - // for (const asset of assets) { - // handleAsset(asset, rel) - // } - // } + const childAssets = webpackStats.childAssetsByChunkName[chunkName] + if (!childAssets) { + continue + } + + for (const [rel, assets] of Object.entries(childAssets)) { + // @ts-ignore TS doesn't like that assets is not typed and especially that it doesn't know that it's Iterable + for (const asset of assets) { + handleAsset(asset, rel) + } + } } // create scripts array, making sure "preload" scripts have priority