-
Notifications
You must be signed in to change notification settings - Fork 10.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(gatsby): enable modern builds for gatsby #14289
Changes from 5 commits
16f2ba1
ba811f3
7fb16a5
b2119ce
39a60a8
7a61cf7
2479897
8ffefd1
d964eb0
3c7fcb3
d801a91
8d1b31d
6eb26de
bf9ecee
b570b2d
d4523b1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -3,7 +3,7 @@ const fs = require(`fs`) | |||||||
const { join } = require(`path`) | ||||||||
const { renderToString, renderToStaticMarkup } = require(`react-dom/server`) | ||||||||
const { ServerLocation, Router, isRedirect } = require(`@reach/router`) | ||||||||
const { get, merge, isObject, flatten, uniqBy } = require(`lodash`) | ||||||||
const { get, merge, mergeWith, isObject, flatten, uniqBy } = require(`lodash`) | ||||||||
|
||||||||
const apiRunner = require(`./api-runner-ssr`) | ||||||||
const syncRequires = require(`./sync-requires`) | ||||||||
|
@@ -14,13 +14,32 @@ const { version: gatsbyVersion } = require(`gatsby/package.json`) | |||||||
const pagesObjectMap = new Map() | ||||||||
pages.forEach(p => pagesObjectMap.set(p.path, p)) | ||||||||
|
||||||||
const stats = JSON.parse( | ||||||||
const statsLegacy = JSON.parse( | ||||||||
fs.readFileSync(`${process.cwd()}/public/webpack.stats.json`, `utf-8`) | ||||||||
) | ||||||||
const statsModern = JSON.parse( | ||||||||
fs.readFileSync(`${process.cwd()}/public/webpack.stats.modern.json`, `utf-8`) | ||||||||
) | ||||||||
|
||||||||
const chunkMapping = JSON.parse( | ||||||||
const chunkMappingLegacy = JSON.parse( | ||||||||
fs.readFileSync(`${process.cwd()}/public/chunk-map.json`, `utf-8`) | ||||||||
) | ||||||||
const chunkMappingModern = JSON.parse( | ||||||||
wardpeet marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
fs.readFileSync(`${process.cwd()}/public/chunk-map.modern.json`, `utf-8`) | ||||||||
wardpeet marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
) | ||||||||
|
||||||||
// eslint-disable-next-line consistent-return | ||||||||
const mergeWithArrayConcatenator = (objValue, srcValue) => { | ||||||||
if (Array.isArray(objValue)) { | ||||||||
return objValue.concat(srcValue) | ||||||||
} | ||||||||
} | ||||||||
const stats = mergeWith(statsLegacy, statsModern, mergeWithArrayConcatenator) | ||||||||
const chunkMapping = mergeWith( | ||||||||
chunkMappingLegacy, | ||||||||
chunkMappingModern, | ||||||||
mergeWithArrayConcatenator | ||||||||
) | ||||||||
|
||||||||
// const testRequireError = require("./test-require-error") | ||||||||
// For some extremely mysterious reason, webpack adds the above module *after* | ||||||||
|
@@ -210,6 +229,9 @@ export default (pagePath, callback) => { | |||||||
} | ||||||||
} | ||||||||
|
||||||||
const getPreloadValue = name => | ||||||||
name.endsWith(`.mjs`) ? `modulepreload` : `preload` | ||||||||
|
||||||||
// Create paths to scripts | ||||||||
let scriptsAndStyles = flatten( | ||||||||
[`app`, page.componentChunkName].map(s => { | ||||||||
|
@@ -226,11 +248,11 @@ export default (pagePath, callback) => { | |||||||
if (chunk === `/`) { | ||||||||
return null | ||||||||
} | ||||||||
return { rel: `preload`, name: chunk } | ||||||||
return { rel: getPreloadValue(chunk), name: chunk } | ||||||||
}) | ||||||||
|
||||||||
namedChunkGroups[s].assets.forEach(asset => | ||||||||
chunks.push({ rel: `preload`, name: asset }) | ||||||||
chunks.push({ rel: getPreloadValue(asset), name: asset }) | ||||||||
) | ||||||||
|
||||||||
const childAssets = namedChunkGroups[s].childAssets | ||||||||
|
@@ -247,12 +269,13 @@ export default (pagePath, callback) => { | |||||||
}) | ||||||||
) | ||||||||
.filter(s => isObject(s)) | ||||||||
.sort((s1, s2) => (s1.rel == `preload` ? -1 : 1)) // given priority to preload | ||||||||
.sort((s1, s2) => (s1.rel == getPreloadValue(s1.name) ? -1 : 1)) // given priority to preload | ||||||||
|
||||||||
scriptsAndStyles = uniqBy(scriptsAndStyles, item => item.name) | ||||||||
|
||||||||
const scripts = scriptsAndStyles.filter( | ||||||||
script => script.name && script.name.endsWith(`.js`) | ||||||||
script => | ||||||||
script.name && | ||||||||
(script.name.endsWith(`.js`) || script.name.endsWith(`.mjs`)) | ||||||||
) | ||||||||
const styles = scriptsAndStyles.filter( | ||||||||
style => style.name && style.name.endsWith(`.css`) | ||||||||
|
@@ -275,6 +298,8 @@ export default (pagePath, callback) => { | |||||||
scripts | ||||||||
.slice(0) | ||||||||
.reverse() | ||||||||
// remove legacy scripts from our preload as we preload our modern files instead | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm unsure if there is a workaround for this that we can have our cake and eat it too. A way to do this is document.write but I rather don't want to use it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. let's add a disclaimer to the PR to say that we lose preload for legacy browers on first run There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added the note! |
||||||||
.filter(script => script.name.endsWith(`.mjs`)) | ||||||||
.forEach(script => { | ||||||||
// Add preload/prefetch <link>s for scripts. | ||||||||
headComponents.push( | ||||||||
|
@@ -368,14 +393,44 @@ export default (pagePath, callback) => { | |||||||
// Filter out prefetched bundles as adding them as a script tag | ||||||||
// would force high priority fetching. | ||||||||
const bodyScripts = scripts | ||||||||
.filter(s => s.rel !== `prefetch`) | ||||||||
.filter(s => s.rel === `modulepreload`) | ||||||||
.map(s => { | ||||||||
const scriptPath = `${__PATH_PREFIX__}/${JSON.stringify(s.name).slice( | ||||||||
1, | ||||||||
-1 | ||||||||
)}` | ||||||||
return <script key={scriptPath} src={scriptPath} type="module" async /> | ||||||||
}) | ||||||||
|
||||||||
// Patches browsers who have a flawed noModule - module system | ||||||||
// Sadly we lose preload for legacy browsers | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
// @see https://caniuse.com/#feat=es6-module | ||||||||
// 1. Safari 10.1 supports modules, but does not support the `nomodule` attribute - it will load <script nomodule> anyway. | ||||||||
// 2. Edge does not executes noModule but still fetches it | ||||||||
const noModuleBugFixScripts = `(function(b){function c(e){var d=b.createElement("script");d.src=e;b.body.appendChild(d)}"noModule"in b.createElement("script")||/Version\\/10\\.1(\\.\\d+)* Safari|Version\\/10\\.\\d(\\.\\d+)*.*Safari|Edge\\/1[6-8]\\.\\d/i.test(navigator.userAgent)||(%scripts%)})(document)` | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had no clue on how to write this in a nicer way. Basically it detects if noModule exists or it does not exists and is safari 10.1.x or safari ios 10.x as they do have know
Suggested change
|
||||||||
|
||||||||
const legacyScrips = scripts | ||||||||
.filter(s => s.rel !== `prefetch` && s.rel !== `modulepreload`) | ||||||||
.map(s => { | ||||||||
const scriptPath = `${__PATH_PREFIX__}/${JSON.stringify(s.name).slice( | ||||||||
1, | ||||||||
-1 | ||||||||
)}` | ||||||||
return <script key={scriptPath} src={scriptPath} async /> | ||||||||
|
||||||||
return `c('${scriptPath}')` | ||||||||
}) | ||||||||
bodyScripts.push( | ||||||||
<script | ||||||||
key="noModuleFix" | ||||||||
dangerouslySetInnerHTML={{ | ||||||||
__html: noModuleBugFixScripts.replace( | ||||||||
`%scripts%`, | ||||||||
legacyScrips.join(`,`) | ||||||||
), | ||||||||
}} | ||||||||
noModule={true} | ||||||||
/> | ||||||||
) | ||||||||
|
||||||||
postBodyComponents.push(...bodyScripts) | ||||||||
|
||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lets filter inside static-entry to only save modern / legacy assets inside the
window.___chunkMapping
variable