Skip to content

Commit

Permalink
[v2] Build-html static file using the webpack magic comment to provid…
Browse files Browse the repository at this point in the history
…e the link rel. (#5901)

* upgrade webpack version to 4.12.0 for webpackPrefetch propose

* use stats.toJson() instead of manually parse chunkGroups

* WIP: debug static entry

* add childAssets to rel link in static-entry 🎉

* use stats.toJson with less verbose

* WIP: unique link rel and give priority to preload

* fixed assetsByChunkName in webpack.stats different from current implementation

* add using-prefetching-preloading-modules examples

* Don't add prefetched bundles to end of html + move preloading json with other scripts
  • Loading branch information
pistachiology authored and KyleAMathews committed Jun 16, 2018
1 parent 0eb3f69 commit d30f1c2
Show file tree
Hide file tree
Showing 14 changed files with 247 additions and 26 deletions.
8 changes: 8 additions & 0 deletions examples/using-prefetching-preloading-modules/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"env": {
"browser": true
},
"globals": {
"graphql": false
}
}
6 changes: 6 additions & 0 deletions examples/using-prefetching-preloading-modules/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Using Prefetching/Preloading modules

https://using-prefetch-preload-module.gatsbyjs.org

## References
- [Prefetching/Preloading Modules](https://webpack.js.org/guides/code-splitting/#prefetching-preloading-modules).
16 changes: 16 additions & 0 deletions examples/using-prefetching-preloading-modules/gatsby-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module.exports = {
siteMetadata: {
title: `Gatsby with styled components`,
},
plugins: [
`gatsby-plugin-styled-components`,
{
resolve: `gatsby-plugin-typography`,
options: {
pathToConfigModule: `src/utils/typography.js`,
},
},
`gatsby-plugin-react-helmet`,
`gatsby-plugin-offline`,
],
}
33 changes: 33 additions & 0 deletions examples/using-prefetching-preloading-modules/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "gatsby-example-using-prefetching-preloading-modules",
"private": true,
"description": "Gatsby example site using Prefetching/Preloading modules",
"version": "2.0.0",
"author": "Nuttapol Laoticharoen <[email protected]>",
"dependencies": {
"babel-plugin-styled-components": "^1.5.1",
"gatsby": "next",
"gatsby-plugin-offline": "next",
"gatsby-plugin-react-helmet": "next",
"gatsby-plugin-styled-components": "next",
"gatsby-plugin-typography": "next",
"lodash": "^4.16.4",
"react": "^16.4.0",
"react-dom": "^16.4.0",
"react-helmet": "^5.2.0",
"react-typography": "^0.16.13",
"slash": "^1.0.0",
"styled-components": "^3.3.2",
"typography": "^0.16.17",
"typography-breakpoint-constants": "^0.15.10"
},
"keywords": [
"gatsby"
],
"license": "MIT",
"main": "n/a",
"scripts": {
"develop": "gatsby develop",
"build": "gatsby build"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react'

class DynamicComponent extends React.Component {
handleClick = () => {
console.log(`Sync-Click!`)
import(/* webpackChunkName: "async-console", webpackPreload: true */ `../utils/async-console`).then(module => {
const asyncConsole = module.default
asyncConsole(`Async-Log!`)
})
}

render() {
return <button onClick={this.handleClick}>
Dynamic Log
</button>
}
}

export default DynamicComponent
65 changes: 65 additions & 0 deletions examples/using-prefetching-preloading-modules/src/pages/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React from "react"
import Helmet from "react-helmet"
import styled from "styled-components"
import DynamicComponent from "../components/DynamicComponent"
import "./style.css"

// Create a Title component that'll render an <h1> tag with some styles
const Title = styled.h1`
font-size: 1.5em;
text-align: center;
color: brown;
`

// Create a Wrapper component that'll render a <section> tag with some styles
const Wrapper = styled.section`
padding: 4em;
background: papayawhip;
`


class IndexPage extends React.Component {
handleClick = () => {
console.log(`Sync-Click!`)
import(/* webpackChunkName: "async-alert", webpackPrefetch: true */ `../utils/async-alert`).then(module => {
const asyncAlert = module.default
asyncAlert(`Async-Click!`)
})
}

render() {
return (
<React.Fragment>
<Helmet>
<title>Gatsby Prefetching/Preloading modules</title>
<meta
name="description"
content="Gatsby example site using prefetching/preloading modules"
/>
<meta name="referrer" content="origin" />
</Helmet>
<div
style={{
margin: `0 auto`,
marginTop: `3rem`,
padding: `1.5rem`,
maxWidth: 900,
color: `red`,
}}
>
<Wrapper>
<Title>Hello World, this is my first prefetching/preloading component!</Title>
<p>
<button onClick={this.handleClick}>
Dynamic Alert!
</button>
<DynamicComponent />
</p>
</Wrapper>
</div>
</React.Fragment>
)
}
}

export default IndexPage
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

.changeable-title {
color: red;
}

button {
margin-right: 20px;
float: left;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@


module.exports = x => alert(x)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@


module.exports = (...x) => console.log(...x)
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import Typography from "typography"
import {
MOBILE_MEDIA_QUERY,
TABLET_MEDIA_QUERY,
} from "typography-breakpoint-constants"

const options = {
baseFontSize: `18px`,
baseLineHeight: 1.45,
blockMarginBottom: 0.75,
scaleRatio: 2.15,
overrideStyles: ({ rhythm, scale }, options) => {
return {
"h1,h2,h3,h4": {
lineHeight: 1.2,
},
[TABLET_MEDIA_QUERY]: {
// Make baseFontSize on mobile 17px.
html: {
fontSize: `${17 / 16 * 100}%`,
},
},
[MOBILE_MEDIA_QUERY]: {
// Make baseFontSize on mobile 16px.
html: {
fontSize: `${16 / 16 * 100}%`,
},
},
}
},
}

const typography = new Typography(options)

export default typography
2 changes: 1 addition & 1 deletion packages/gatsby/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@
"url-loader": "^1.0.1",
"uuid": "^3.1.0",
"v8-compile-cache": "^1.1.0",
"webpack": "^4.4.1",
"webpack": "^4.12.0",
"webpack-dev-middleware": "^3.0.1",
"webpack-dev-server": "^3.1.1",
"webpack-hot-middleware": "^2.21.0",
Expand Down
62 changes: 42 additions & 20 deletions packages/gatsby/src/cache-dir/static-entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const fs = require(`fs`)
const { join } = require(`path`)
const { renderToString, renderToStaticMarkup } = require(`react-dom/server`)
const { StaticRouter, Route } = require(`react-router-dom`)
const { get, merge, isString, flatten } = require(`lodash`)
const { get, merge, isObject, flatten, uniqBy } = require(`lodash`)

const apiRunner = require(`./api-runner-ssr`)
const syncRequires = require(`./sync-requires`)
Expand Down Expand Up @@ -150,26 +150,40 @@ export default (pagePath, callback) => {
}

// Create paths to scripts
const scriptsAndStyles = flatten(
let scriptsAndStyles = flatten(
[`app`, page.componentChunkName].map(s => {
const fetchKey = `assetsByChunkName[${s}]`

let chunks = get(stats, fetchKey)
let namedChunkGroups = get(stats, `namedChunkGroups`)

if (!chunks) {
return null
}

return chunks.map(chunk => {
chunks = chunks.map(chunk => {
if (chunk === `/`) {
return null
}
return chunk
return { rel: `preload`, name: chunk }
})

const childAssets = namedChunkGroups[s].childAssets
for (const rel in childAssets) {
chunks = merge(chunks, childAssets[rel].map(chunk => {
return { rel, name: chunk }
}))
}

return chunks
})
).filter(s => isString(s))
const scripts = scriptsAndStyles.filter(s => s.endsWith(`.js`))
const styles = scriptsAndStyles.filter(s => s.endsWith(`.css`))
).filter(s => isObject(s))
.sort((s1, s2) => s1.rel == `preload` ? -1 : 1) // given priority to preload

scriptsAndStyles = uniqBy(scriptsAndStyles, item => item.name)

const scripts = scriptsAndStyles.filter(script => script.name && script.name.endsWith(`.js`))
const styles = scriptsAndStyles.filter(style => style.name && style.name.endsWith(`.css`))

apiRunner(`onRenderBody`, {
setHeadComponents,
Expand All @@ -189,23 +203,20 @@ export default (pagePath, callback) => {
.slice(0)
.reverse()
.forEach(script => {
// Add preload <link>s for scripts.
// Add preload/prefetch <link>s for scripts.
headComponents.push(
<link
as="script"
rel="preload"
key={script}
href={urlJoin(pathPrefix, script)}
rel={script.rel}
key={script.name}
href={urlJoin(pathPrefix, script.name)}
/>
)
})

if (page.jsonName in dataPaths) {
const dataPath = `${pathPrefix}static/d/${dataPaths[page.jsonName]}.json`
// Insert json data path after commons and app
headComponents.splice(
1,
0,
headComponents.push(
<link
rel="preload"
key={dataPath}
Expand All @@ -221,21 +232,30 @@ export default (pagePath, callback) => {
.reverse()
.forEach(style => {
// Add <link>s for styles.
headComponents.push(
<link
as="style"
rel={style.rel}
key={style.name}
href={urlJoin(pathPrefix, style.name)}
/>
)

headComponents.unshift(
<style
type="text/css"
data-href={urlJoin(pathPrefix, style)}
data-href={urlJoin(pathPrefix, style.name)}
dangerouslySetInnerHTML={{
__html: fs.readFileSync(
join(process.cwd(), `public`, style),
join(process.cwd(), `public`, style.name),
`utf-8`
),
}}
/>
)
})

// Add script loader for page scripts to the end of body element (after webpack manifest).
// Add page metadata for the current page
const windowData = `/*<![CDATA[*/window.page=${JSON.stringify(page)};${
page.jsonName in dataPaths
? `window.dataPath="${dataPaths[page.jsonName]}";`
Expand All @@ -252,8 +272,10 @@ export default (pagePath, callback) => {
/>
)

const bodyScripts = scripts.map(s => {
const scriptPath = `${pathPrefix}${JSON.stringify(s).slice(1, -1)}`
// Filter out prefetched bundles as adding them as a script tag
// would force high priority fetching.
const bodyScripts = scripts.filter(s => s.rel !== `prefetch`).map(s => {
const scriptPath = `${pathPrefix}${JSON.stringify(s.name).slice(1, -1)}`
return <script key={scriptPath} src={scriptPath} async />
})

Expand Down
10 changes: 6 additions & 4 deletions packages/gatsby/src/utils/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,6 @@ module.exports = async (
`gatsby-webpack-stats-extractor`,
(stats, done) => {
let assets = {}

for (let chunkGroup of stats.compilation.chunkGroups) {
if (chunkGroup.name) {
let files = []
Expand All @@ -229,11 +228,14 @@ module.exports = async (
}
}

const webpackStats = {
...stats.toJson({ all: false, chunkGroups: true }),
assetsByChunkName: assets,
}

fs.writeFile(
path.join(`public`, `webpack.stats.json`),
JSON.stringify({
assetsByChunkName: assets,
}),
JSON.stringify(webpackStats),
done
)
}
Expand Down
2 changes: 1 addition & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -15434,7 +15434,7 @@ webpack-stats-plugin@^0.1.5:
version "0.1.5"
resolved "https://registry.yarnpkg.com/webpack-stats-plugin/-/webpack-stats-plugin-0.1.5.tgz#29e5f12ebfd53158d31d656a113ac1f7b86179d9"

webpack@^4.4.1:
webpack@^4.12.0:
version "4.12.0"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.12.0.tgz#14758e035ae69747f68dd0edf3c5a572a82bdee9"
dependencies:
Expand Down

0 comments on commit d30f1c2

Please sign in to comment.