diff --git a/integration-tests/artifacts/__tests__/index.js b/integration-tests/artifacts/__tests__/index.js index 94fdad3d220e6..099643ae9d09a 100644 --- a/integration-tests/artifacts/__tests__/index.js +++ b/integration-tests/artifacts/__tests__/index.js @@ -169,7 +169,11 @@ function assertHTMLCorrectness(runNumber) { ? `component---src-templates-deps-page-query-js` : `component---src-templates-deps-page-query-alternative-js` + const expectedBackground = + runNumber < 6 ? `white` : runNumber === 6 ? `yellow` : `green` + let pageDataContent + let htmlContent beforeAll(() => { pageDataContent = fs.readJsonSync( path.join( @@ -180,12 +184,8 @@ function assertHTMLCorrectness(runNumber) { `page-data.json` ) ) - }) - it(`html built is using correct template (${ - runNumber <= 1 ? `default` : `alternative` - })`, () => { - const htmlContent = fs.readFileSync( + htmlContent = fs.readFileSync( path.join( process.cwd(), `public`, @@ -194,7 +194,20 @@ function assertHTMLCorrectness(runNumber) { ), `utf-8` ) + }) + it(`correct css is inlined in html file (${expectedBackground})`, () => { + expect(htmlContent).toMatch( + new RegExp( + `
"`; - -exports[`static-entry onPreRenderHTML can be used to replace postBodyComponents 1`] = `"
div3
div2
div1
"`; - -exports[`static-entry onPreRenderHTML can be used to replace preBodyComponents 1`] = `"
div3
div2
div1
"`; +exports[`static-entry onPreRenderHTML can be used to replace headComponents 1`] = ` +Object { + "html": "
", + "unsafeBuiltinsUsage": Array [], +} +`; + +exports[`static-entry onPreRenderHTML can be used to replace postBodyComponents 1`] = ` +Object { + "html": "
div3
div2
div1
", + "unsafeBuiltinsUsage": Array [], +} +`; + +exports[`static-entry onPreRenderHTML can be used to replace preBodyComponents 1`] = ` +Object { + "html": "
div3
div2
div1
", + "unsafeBuiltinsUsage": Array [], +} +`; diff --git a/packages/gatsby/cache-dir/ssr-builtin-trackers/child_process.js b/packages/gatsby/cache-dir/ssr-builtin-trackers/child_process.js new file mode 100644 index 0000000000000..e953efda99e0e --- /dev/null +++ b/packages/gatsby/cache-dir/ssr-builtin-trackers/child_process.js @@ -0,0 +1,4 @@ +/* eslint-disable filenames/match-regex */ +const { wrapModuleWithTracking } = require(`./tracking-unsafe-module-wrapper`) + +module.exports = wrapModuleWithTracking(`child_process`) diff --git a/packages/gatsby/cache-dir/ssr-builtin-trackers/fs.js b/packages/gatsby/cache-dir/ssr-builtin-trackers/fs.js new file mode 100644 index 0000000000000..4ed58f29e01cb --- /dev/null +++ b/packages/gatsby/cache-dir/ssr-builtin-trackers/fs.js @@ -0,0 +1,3 @@ +const { wrapModuleWithTracking } = require(`./tracking-unsafe-module-wrapper`) + +module.exports = wrapModuleWithTracking(`fs`) diff --git a/packages/gatsby/cache-dir/ssr-builtin-trackers/http.js b/packages/gatsby/cache-dir/ssr-builtin-trackers/http.js new file mode 100644 index 0000000000000..7393f9e40d32e --- /dev/null +++ b/packages/gatsby/cache-dir/ssr-builtin-trackers/http.js @@ -0,0 +1,3 @@ +const { wrapModuleWithTracking } = require(`./tracking-unsafe-module-wrapper`) + +module.exports = wrapModuleWithTracking(`http`) diff --git a/packages/gatsby/cache-dir/ssr-builtin-trackers/http2.js b/packages/gatsby/cache-dir/ssr-builtin-trackers/http2.js new file mode 100644 index 0000000000000..30d956d6e6237 --- /dev/null +++ b/packages/gatsby/cache-dir/ssr-builtin-trackers/http2.js @@ -0,0 +1,3 @@ +const { wrapModuleWithTracking } = require(`./tracking-unsafe-module-wrapper`) + +module.exports = wrapModuleWithTracking(`http2`) diff --git a/packages/gatsby/cache-dir/ssr-builtin-trackers/https.js b/packages/gatsby/cache-dir/ssr-builtin-trackers/https.js new file mode 100644 index 0000000000000..fa4b85606e09a --- /dev/null +++ b/packages/gatsby/cache-dir/ssr-builtin-trackers/https.js @@ -0,0 +1,3 @@ +const { wrapModuleWithTracking } = require(`./tracking-unsafe-module-wrapper`) + +module.exports = wrapModuleWithTracking(`https`) diff --git a/packages/gatsby/cache-dir/ssr-builtin-trackers/tracking-unsafe-module-wrapper.js b/packages/gatsby/cache-dir/ssr-builtin-trackers/tracking-unsafe-module-wrapper.js new file mode 100644 index 0000000000000..df374dab131ad --- /dev/null +++ b/packages/gatsby/cache-dir/ssr-builtin-trackers/tracking-unsafe-module-wrapper.js @@ -0,0 +1,37 @@ +// initializing global here for unsafe builtin usage at import time +global.unsafeBuiltinUsage = [] + +function createProxyHandler(prefix) { + return { + get: function (target, key) { + const value = target[key] + if (typeof value === `function`) { + return function wrapper(...args) { + const myErrorHolder = { + name: `Unsafe builtin usage ${prefix}.${key}`, + } + Error.captureStackTrace(myErrorHolder, wrapper) + + global.unsafeBuiltinUsage.push(myErrorHolder.stack) + return value.apply(target, args) + } + } else if (typeof value === `object` && value !== null) { + return new Proxy( + value, + createProxyHandler( + key && key.toString ? `${prefix}.${key.toString()}` : prefix + ) + ) + } + + return value + }, + } +} + +function wrapModuleWithTracking(moduleName) { + const mod = require(moduleName) + return new Proxy(mod, createProxyHandler(moduleName)) +} + +exports.wrapModuleWithTracking = wrapModuleWithTracking diff --git a/packages/gatsby/cache-dir/static-entry.js b/packages/gatsby/cache-dir/static-entry.js index 015226a4e2298..86d68faf02356 100644 --- a/packages/gatsby/cache-dir/static-entry.js +++ b/packages/gatsby/cache-dir/static-entry.js @@ -103,315 +103,325 @@ export default ({ reversedStyles, reversedScripts, }) => { - let bodyHtml = `` - let headComponents = [ - , - ] - let htmlAttributes = {} - let bodyAttributes = {} - let preBodyComponents = [] - let postBodyComponents = [] - let bodyProps = {} - - const replaceBodyHTMLString = body => { - bodyHtml = body - } - - const setHeadComponents = components => { - headComponents = headComponents.concat(sanitizeComponents(components)) - } - - const setHtmlAttributes = attributes => { - htmlAttributes = merge(htmlAttributes, attributes) - } - - const setBodyAttributes = attributes => { - bodyAttributes = merge(bodyAttributes, attributes) - } - - const setPreBodyComponents = components => { - preBodyComponents = preBodyComponents.concat(sanitizeComponents(components)) - } + // for this to work we need this function to be sync or at least ensure there is single execution of it at a time + global.unsafeBuiltinUsage = [] + + try { + let bodyHtml = `` + let headComponents = [ + , + ] + let htmlAttributes = {} + let bodyAttributes = {} + let preBodyComponents = [] + let postBodyComponents = [] + let bodyProps = {} + + const replaceBodyHTMLString = body => { + bodyHtml = body + } - const setPostBodyComponents = components => { - postBodyComponents = postBodyComponents.concat( - sanitizeComponents(components) - ) - } + const setHeadComponents = components => { + headComponents = headComponents.concat(sanitizeComponents(components)) + } - const setBodyProps = props => { - bodyProps = merge({}, bodyProps, props) - } + const setHtmlAttributes = attributes => { + htmlAttributes = merge(htmlAttributes, attributes) + } - const getHeadComponents = () => headComponents + const setBodyAttributes = attributes => { + bodyAttributes = merge(bodyAttributes, attributes) + } - const replaceHeadComponents = components => { - headComponents = sanitizeComponents(components) - } + const setPreBodyComponents = components => { + preBodyComponents = preBodyComponents.concat( + sanitizeComponents(components) + ) + } - const getPreBodyComponents = () => preBodyComponents + const setPostBodyComponents = components => { + postBodyComponents = postBodyComponents.concat( + sanitizeComponents(components) + ) + } - const replacePreBodyComponents = components => { - preBodyComponents = sanitizeComponents(components) - } + const setBodyProps = props => { + bodyProps = merge({}, bodyProps, props) + } - const getPostBodyComponents = () => postBodyComponents + const getHeadComponents = () => headComponents - const replacePostBodyComponents = components => { - postBodyComponents = sanitizeComponents(components) - } + const replaceHeadComponents = components => { + headComponents = sanitizeComponents(components) + } - const pageDataUrl = getPageDataUrl(pagePath) + const getPreBodyComponents = () => preBodyComponents - const { componentChunkName, staticQueryHashes = [] } = pageData + const replacePreBodyComponents = components => { + preBodyComponents = sanitizeComponents(components) + } - const staticQueryUrls = staticQueryHashes.map(getStaticQueryUrl) + const getPostBodyComponents = () => postBodyComponents - class RouteHandler extends React.Component { - render() { - const props = { - ...this.props, - ...pageData.result, - params: { - ...grabMatchParams(this.props.location.pathname), - ...(pageData.result?.pageContext?.__params || {}), - }, - } + const replacePostBodyComponents = components => { + postBodyComponents = sanitizeComponents(components) + } - const pageElement = createElement( - syncRequires.components[componentChunkName], - props - ) + const pageDataUrl = getPageDataUrl(pagePath) - const wrappedPage = apiRunner( - `wrapPageElement`, - { element: pageElement, props }, - pageElement, - ({ result }) => { - return { element: result, props } - } - ).pop() + const { componentChunkName, staticQueryHashes = [] } = pageData - return wrappedPage - } - } + const staticQueryUrls = staticQueryHashes.map(getStaticQueryUrl) - const routerElement = ( - - - - -
- - ) - - const bodyComponent = ( - - {apiRunner( - `wrapRootElement`, - { element: routerElement, pathname: pagePath }, - routerElement, - ({ result }) => { - return { element: result, pathname: pagePath } + class RouteHandler extends React.Component { + render() { + const props = { + ...this.props, + ...pageData.result, + params: { + ...grabMatchParams(this.props.location.pathname), + ...(pageData.result?.pageContext?.__params || {}), + }, } - ).pop()} - - ) - - // Let the site or plugin render the page component. - apiRunner(`replaceRenderer`, { - bodyComponent, - replaceBodyHTMLString, - setHeadComponents, - setHtmlAttributes, - setBodyAttributes, - setPreBodyComponents, - setPostBodyComponents, - setBodyProps, - pathname: pagePath, - pathPrefix: __PATH_PREFIX__, - }) - // If no one stepped up, we'll handle it. - if (!bodyHtml) { - try { - bodyHtml = renderToString(bodyComponent) - } catch (e) { - // ignore @reach/router redirect errors - if (!isRedirect(e)) throw e + const pageElement = createElement( + syncRequires.components[componentChunkName], + props + ) + + const wrappedPage = apiRunner( + `wrapPageElement`, + { element: pageElement, props }, + pageElement, + ({ result }) => { + return { element: result, props } + } + ).pop() + + return wrappedPage + } } - } - apiRunner(`onRenderBody`, { - setHeadComponents, - setHtmlAttributes, - setBodyAttributes, - setPreBodyComponents, - setPostBodyComponents, - setBodyProps, - pathname: pagePath, - loadPageDataSync, - bodyHtml, - scripts, - styles, - pathPrefix: __PATH_PREFIX__, - }) - - reversedScripts.forEach(script => { - // Add preload/prefetch s for scripts. - headComponents.push( - + const routerElement = ( + + + + +
+ ) - }) - if (pageData) { - headComponents.push( - - ) - } - staticQueryUrls.forEach(staticQueryUrl => - headComponents.push( - + const bodyComponent = ( + + {apiRunner( + `wrapRootElement`, + { element: routerElement, pathname: pagePath }, + routerElement, + ({ result }) => { + return { element: result, pathname: pagePath } + } + ).pop()} + ) - ) - - const appDataUrl = getAppDataUrl() - if (appDataUrl) { - headComponents.push( - - ) - } - reversedStyles.forEach(style => { - // Add s for styles that should be prefetched - // otherwise, inline as a