mirror of https://gitee.com/antv-l7/antv-l7
feat(add l7 site): add websites
This commit is contained in:
parent
7b1e06ec71
commit
47296a24ef
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"browser": true
|
||||||
|
},
|
||||||
|
"globals": {
|
||||||
|
"__PATH_PREFIX__": false,
|
||||||
|
"___emitter": false
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
["babel-preset-gatsby"]
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Dev loader loadPage should be successful when component can be loaded 1`] = `
|
||||||
|
Object {
|
||||||
|
"component": "instance",
|
||||||
|
"json": Object {
|
||||||
|
"pageContext": "something something",
|
||||||
|
},
|
||||||
|
"page": Object {
|
||||||
|
"componentChunkName": "chunk",
|
||||||
|
"matchPath": undefined,
|
||||||
|
"path": "/mypage/",
|
||||||
|
"webpackCompilationHash": "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Production loader loadPage should be successful when component can be loaded 1`] = `
|
||||||
|
Object {
|
||||||
|
"component": "instance",
|
||||||
|
"json": Object {
|
||||||
|
"pageContext": "something something",
|
||||||
|
},
|
||||||
|
"page": Object {
|
||||||
|
"componentChunkName": "chunk",
|
||||||
|
"matchPath": undefined,
|
||||||
|
"path": "/mypage/",
|
||||||
|
"webpackCompilationHash": "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
|
@ -0,0 +1,13 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`develop-static-entry onPreRenderHTML can be used to replace headComponents 1`] = `"<!DOCTYPE html><html><head><meta charSet=\\"utf-8\\"/><meta http-equiv=\\"x-ua-compatible\\" content=\\"ie=edge\\"/><meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1, shrink-to-fit=no\\"/><style> .style3 </style><style> .style2 </style><style> .style1 </style><meta name=\\"note\\" content=\\"environment=development\\"/><script src=\\"/socket.io/socket.io.js\\"></script></head><body><noscript id=\\"gatsby-noscript\\">This app works best with JavaScript enabled.</noscript><div id=\\"___gatsby\\"></div><script src=\\"/commons.js\\"></script></body></html>"`;
|
||||||
|
|
||||||
|
exports[`develop-static-entry onPreRenderHTML can be used to replace postBodyComponents 1`] = `"<!DOCTYPE html><html><head><meta charSet=\\"utf-8\\"/><meta http-equiv=\\"x-ua-compatible\\" content=\\"ie=edge\\"/><meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1, shrink-to-fit=no\\"/><meta name=\\"note\\" content=\\"environment=development\\"/><script src=\\"/socket.io/socket.io.js\\"></script></head><body><noscript id=\\"gatsby-noscript\\">This app works best with JavaScript enabled.</noscript><div id=\\"___gatsby\\"></div><div> div3 </div><div> div2 </div><div> div1 </div><script src=\\"/commons.js\\"></script></body></html>"`;
|
||||||
|
|
||||||
|
exports[`develop-static-entry onPreRenderHTML can be used to replace preBodyComponents 1`] = `"<!DOCTYPE html><html><head><meta charSet=\\"utf-8\\"/><meta http-equiv=\\"x-ua-compatible\\" content=\\"ie=edge\\"/><meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1, shrink-to-fit=no\\"/><meta name=\\"note\\" content=\\"environment=development\\"/><script src=\\"/socket.io/socket.io.js\\"></script></head><body><div> div3 </div><div> div2 </div><div> div1 </div><noscript id=\\"gatsby-noscript\\">This app works best with JavaScript enabled.</noscript><div id=\\"___gatsby\\"></div><script src=\\"/commons.js\\"></script></body></html>"`;
|
||||||
|
|
||||||
|
exports[`static-entry onPreRenderHTML can be used to replace headComponents 1`] = `"<!DOCTYPE html><html><head><meta charSet=\\"utf-8\\"/><meta http-equiv=\\"x-ua-compatible\\" content=\\"ie=edge\\"/><meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1, shrink-to-fit=no\\"/><link as=\\"fetch\\" rel=\\"preload\\" href=\\"/page-data/about/page-data.json\\" crossorigin=\\"anonymous\\"/><style> .style3 </style><style> .style2 </style><style> .style1 </style><meta name=\\"generator\\" content=\\"Gatsby 2.0.0\\"/></head><body><noscript id=\\"gatsby-noscript\\">This app works best with JavaScript enabled.</noscript><div id=\\"___gatsby\\"><div style=\\"outline:none\\" tabindex=\\"-1\\" role=\\"group\\" id=\\"gatsby-focus-wrapper\\"></div></div><script id=\\"gatsby-script-loader\\">/*<![CDATA[*/window.pagePath=\\"/about/\\";/*]]>*/</script><script id=\\"gatsby-chunk-mapping\\">/*<![CDATA[*/window.___chunkMapping={};/*]]>*/</script></body></html>"`;
|
||||||
|
|
||||||
|
exports[`static-entry onPreRenderHTML can be used to replace postBodyComponents 1`] = `"<!DOCTYPE html><html><head><meta charSet=\\"utf-8\\"/><meta http-equiv=\\"x-ua-compatible\\" content=\\"ie=edge\\"/><meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1, shrink-to-fit=no\\"/><meta name=\\"generator\\" content=\\"Gatsby 2.0.0\\"/><link as=\\"fetch\\" rel=\\"preload\\" href=\\"/page-data/about/page-data.json\\" crossorigin=\\"anonymous\\"/></head><body><noscript id=\\"gatsby-noscript\\">This app works best with JavaScript enabled.</noscript><div id=\\"___gatsby\\"><div style=\\"outline:none\\" tabindex=\\"-1\\" role=\\"group\\" id=\\"gatsby-focus-wrapper\\"></div></div><script id=\\"gatsby-chunk-mapping\\">/*<![CDATA[*/window.___chunkMapping={};/*]]>*/</script><script id=\\"gatsby-script-loader\\">/*<![CDATA[*/window.pagePath=\\"/about/\\";/*]]>*/</script><div> div3 </div><div> div2 </div><div> div1 </div></body></html>"`;
|
||||||
|
|
||||||
|
exports[`static-entry onPreRenderHTML can be used to replace preBodyComponents 1`] = `"<!DOCTYPE html><html><head><meta charSet=\\"utf-8\\"/><meta http-equiv=\\"x-ua-compatible\\" content=\\"ie=edge\\"/><meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1, shrink-to-fit=no\\"/><meta name=\\"generator\\" content=\\"Gatsby 2.0.0\\"/><link as=\\"fetch\\" rel=\\"preload\\" href=\\"/page-data/about/page-data.json\\" crossorigin=\\"anonymous\\"/></head><body><div> div3 </div><div> div2 </div><div> div1 </div><noscript id=\\"gatsby-noscript\\">This app works best with JavaScript enabled.</noscript><div id=\\"___gatsby\\"><div style=\\"outline:none\\" tabindex=\\"-1\\" role=\\"group\\" id=\\"gatsby-focus-wrapper\\"></div></div><script id=\\"gatsby-script-loader\\">/*<![CDATA[*/window.pagePath=\\"/about/\\";/*]]>*/</script><script id=\\"gatsby-chunk-mapping\\">/*<![CDATA[*/window.___chunkMapping={};/*]]>*/</script></body></html>"`;
|
|
@ -0,0 +1,539 @@
|
||||||
|
// This is by no means a full test file for loader.js so feel free to add more tests.
|
||||||
|
import mock from "xhr-mock"
|
||||||
|
import DevLoader from "../dev-loader"
|
||||||
|
import emitter from "../emitter"
|
||||||
|
|
||||||
|
jest.mock(`../emitter`)
|
||||||
|
jest.mock(`../socketIo`, () => {
|
||||||
|
return {
|
||||||
|
default: jest.fn(),
|
||||||
|
getPageData: jest.fn().mockResolvedValue(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
describe(`Dev loader`, () => {
|
||||||
|
describe(`loadPageDataJson`, () => {
|
||||||
|
let originalBasePath
|
||||||
|
let originalPathPrefix
|
||||||
|
let xhrCount
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} path
|
||||||
|
* @param {number} status
|
||||||
|
* @param {string|Object?} responseText
|
||||||
|
* @param {boolean?} json
|
||||||
|
*/
|
||||||
|
const mockPageData = (path, status, responseText = ``, json = false) => {
|
||||||
|
mock.get(`/page-data${path}/page-data.json`, (req, res) => {
|
||||||
|
xhrCount++
|
||||||
|
if (json) {
|
||||||
|
res.header(`content-type`, `application/json`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
.status(status)
|
||||||
|
.body(
|
||||||
|
typeof responseText === `string`
|
||||||
|
? responseText
|
||||||
|
: JSON.stringify(responseText)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultPayload = {
|
||||||
|
path: `/mypage/`,
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace the real XHR object with the mock XHR object before each test
|
||||||
|
beforeEach(() => {
|
||||||
|
originalBasePath = global.__BASE_PATH__
|
||||||
|
originalPathPrefix = global.__PATH_PREFIX__
|
||||||
|
global.__BASE_PATH__ = ``
|
||||||
|
global.__PATH_PREFIX__ = ``
|
||||||
|
xhrCount = 0
|
||||||
|
mock.setup()
|
||||||
|
})
|
||||||
|
|
||||||
|
// put the real XHR object back and clear the mocks after each test
|
||||||
|
afterEach(() => {
|
||||||
|
global.__BASE_PATH__ = originalBasePath
|
||||||
|
global.__PATH_PREFIX__ = originalPathPrefix
|
||||||
|
mock.teardown()
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should return a pageData json on success`, async () => {
|
||||||
|
const devLoader = new DevLoader(null, [])
|
||||||
|
|
||||||
|
mockPageData(`/mypage`, 200, defaultPayload, true)
|
||||||
|
|
||||||
|
const expectation = {
|
||||||
|
status: `success`,
|
||||||
|
pagePath: `/mypage`,
|
||||||
|
payload: defaultPayload,
|
||||||
|
}
|
||||||
|
expect(await devLoader.loadPageDataJson(`/mypage/`)).toEqual(expectation)
|
||||||
|
expect(devLoader.pageDataDb.get(`/mypage`)).toEqual(expectation)
|
||||||
|
expect(xhrCount).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should return a pageData json on success without contentType`, async () => {
|
||||||
|
const devLoader = new DevLoader(null, [])
|
||||||
|
|
||||||
|
mockPageData(`/mypage`, 200, defaultPayload)
|
||||||
|
|
||||||
|
const expectation = {
|
||||||
|
status: `success`,
|
||||||
|
pagePath: `/mypage`,
|
||||||
|
payload: defaultPayload,
|
||||||
|
}
|
||||||
|
expect(await devLoader.loadPageDataJson(`/mypage/`)).toEqual(expectation)
|
||||||
|
expect(devLoader.pageDataDb.get(`/mypage`)).toEqual(expectation)
|
||||||
|
expect(xhrCount).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should return a pageData json with an empty compilation hash (gatsby develop)`, async () => {
|
||||||
|
const devLoader = new DevLoader(null, [])
|
||||||
|
|
||||||
|
const payload = { ...defaultPayload, webpackCompilationHash: `` }
|
||||||
|
mockPageData(`/mypage`, 200, payload)
|
||||||
|
|
||||||
|
const expectation = {
|
||||||
|
status: `success`,
|
||||||
|
pagePath: `/mypage`,
|
||||||
|
payload,
|
||||||
|
}
|
||||||
|
expect(await devLoader.loadPageDataJson(`/mypage/`)).toEqual(expectation)
|
||||||
|
expect(devLoader.pageDataDb.get(`/mypage`)).toEqual(expectation)
|
||||||
|
expect(xhrCount).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should load a 404 page when page-path file is not a gatsby json`, async () => {
|
||||||
|
const devLoader = new DevLoader(null, [])
|
||||||
|
|
||||||
|
const payload = { ...defaultPayload, path: `/404.html/` }
|
||||||
|
mockPageData(`/unknown-page`, 200, { random: `string` }, true)
|
||||||
|
mockPageData(`/404.html`, 200, payload, true)
|
||||||
|
|
||||||
|
const expectation = {
|
||||||
|
status: `success`,
|
||||||
|
pagePath: `/404.html`,
|
||||||
|
notFound: true,
|
||||||
|
payload,
|
||||||
|
}
|
||||||
|
expect(await devLoader.loadPageDataJson(`/unknown-page/`)).toEqual(
|
||||||
|
expectation
|
||||||
|
)
|
||||||
|
expect(devLoader.pageDataDb.get(`/unknown-page`)).toEqual(expectation)
|
||||||
|
expect(xhrCount).toBe(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should load a 404 page when page-path file is not a json`, async () => {
|
||||||
|
const devLoader = new DevLoader(null, [])
|
||||||
|
|
||||||
|
const payload = { ...defaultPayload, path: `/404.html/` }
|
||||||
|
mockPageData(`/unknown-page`, 200)
|
||||||
|
mockPageData(`/404.html`, 200, payload, true)
|
||||||
|
|
||||||
|
const expectation = {
|
||||||
|
status: `success`,
|
||||||
|
pagePath: `/404.html`,
|
||||||
|
notFound: true,
|
||||||
|
payload,
|
||||||
|
}
|
||||||
|
expect(await devLoader.loadPageDataJson(`/unknown-page/`)).toEqual(
|
||||||
|
expectation
|
||||||
|
)
|
||||||
|
expect(devLoader.pageDataDb.get(`/unknown-page`)).toEqual(expectation)
|
||||||
|
expect(xhrCount).toBe(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should load a 404 page when path returns a 404`, async () => {
|
||||||
|
const devLoader = new DevLoader(null, [])
|
||||||
|
|
||||||
|
const payload = { ...defaultPayload, path: `/404.html/` }
|
||||||
|
mockPageData(`/unknown-page`, 200)
|
||||||
|
mockPageData(`/404.html`, 200, payload, true)
|
||||||
|
|
||||||
|
const expectation = {
|
||||||
|
status: `success`,
|
||||||
|
pagePath: `/404.html`,
|
||||||
|
notFound: true,
|
||||||
|
payload,
|
||||||
|
}
|
||||||
|
expect(await devLoader.loadPageDataJson(`/unknown-page/`)).toEqual(
|
||||||
|
expectation
|
||||||
|
)
|
||||||
|
expect(devLoader.pageDataDb.get(`/unknown-page`)).toEqual(expectation)
|
||||||
|
expect(xhrCount).toBe(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should return the dev-404-page when no 404 page can be found`, async () => {
|
||||||
|
const devLoader = new DevLoader(null, [])
|
||||||
|
|
||||||
|
const payload = { ...defaultPayload, path: `/dev-404-page/` }
|
||||||
|
mockPageData(`/unknown-page`, 404)
|
||||||
|
mockPageData(`/404.html`, 404)
|
||||||
|
mockPageData(`/dev-404-page`, 200, payload, true)
|
||||||
|
|
||||||
|
const expectation = {
|
||||||
|
status: `success`,
|
||||||
|
pagePath: `/dev-404-page`,
|
||||||
|
notFound: true,
|
||||||
|
payload,
|
||||||
|
}
|
||||||
|
expect(await devLoader.loadPageDataJson(`/unknown-page/`)).toEqual(
|
||||||
|
expectation
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(devLoader.pageDataDb.get(`/unknown-page`)).toEqual({
|
||||||
|
notFound: true,
|
||||||
|
pagePath: `/404.html`,
|
||||||
|
status: `failure`,
|
||||||
|
})
|
||||||
|
expect(xhrCount).toBe(3)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should return an error when status is 500`, async () => {
|
||||||
|
const devLoader = new DevLoader(null, [])
|
||||||
|
|
||||||
|
mockPageData(`/error-page`, 500)
|
||||||
|
|
||||||
|
const expectation = {
|
||||||
|
status: `error`,
|
||||||
|
pagePath: `/error-page`,
|
||||||
|
}
|
||||||
|
expect(await devLoader.loadPageDataJson(`/error-page/`)).toEqual(
|
||||||
|
expectation
|
||||||
|
)
|
||||||
|
expect(devLoader.pageDataDb.get(`/error-page`)).toEqual(expectation)
|
||||||
|
expect(xhrCount).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should retry 3 times before returning an error`, async () => {
|
||||||
|
const devLoader = new DevLoader(null, [])
|
||||||
|
|
||||||
|
mockPageData(`/blocked-page`, 0)
|
||||||
|
|
||||||
|
const expectation = {
|
||||||
|
status: `error`,
|
||||||
|
retries: 3,
|
||||||
|
pagePath: `/blocked-page`,
|
||||||
|
}
|
||||||
|
expect(await devLoader.loadPageDataJson(`/blocked-page/`)).toEqual(
|
||||||
|
expectation
|
||||||
|
)
|
||||||
|
expect(devLoader.pageDataDb.get(`/blocked-page`)).toEqual(expectation)
|
||||||
|
expect(xhrCount).toBe(4)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should recover if we get 1 failure`, async () => {
|
||||||
|
const devLoader = new DevLoader(null, [])
|
||||||
|
const payload = {
|
||||||
|
path: `/blocked-page/`,
|
||||||
|
}
|
||||||
|
|
||||||
|
let xhrCount = 0
|
||||||
|
mock.get(`/page-data/blocked-page/page-data.json`, (req, res) => {
|
||||||
|
if (xhrCount++ === 0) {
|
||||||
|
return res.status(0).body(``)
|
||||||
|
} else {
|
||||||
|
res.header(`content-type`, `application/json`)
|
||||||
|
return res.status(200).body(JSON.stringify(payload))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const expectation = {
|
||||||
|
status: `success`,
|
||||||
|
retries: 1,
|
||||||
|
pagePath: `/blocked-page`,
|
||||||
|
payload,
|
||||||
|
}
|
||||||
|
expect(await devLoader.loadPageDataJson(`/blocked-page/`)).toEqual(
|
||||||
|
expectation
|
||||||
|
)
|
||||||
|
expect(devLoader.pageDataDb.get(`/blocked-page`)).toEqual(expectation)
|
||||||
|
expect(xhrCount).toBe(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`shouldn't load pageData multiple times`, async () => {
|
||||||
|
const devLoader = new DevLoader(null, [])
|
||||||
|
|
||||||
|
mockPageData(`/mypage`, 200, defaultPayload, true)
|
||||||
|
|
||||||
|
const expectation = await devLoader.loadPageDataJson(`/mypage/`)
|
||||||
|
expect(await devLoader.loadPageDataJson(`/mypage/`)).toBe(expectation)
|
||||||
|
expect(xhrCount).toBe(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe(`loadPage`, () => {
|
||||||
|
const createSyncRequires = components => {
|
||||||
|
return {
|
||||||
|
components,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let originalPathPrefix
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
originalPathPrefix = global.__PATH_PREFIX__
|
||||||
|
global.__PATH_PREFIX__ = ``
|
||||||
|
mock.setup()
|
||||||
|
mock.get(`/app-data.json`, (req, res) =>
|
||||||
|
res
|
||||||
|
.status(200)
|
||||||
|
.header(`content-type`, `application/json`)
|
||||||
|
.body(
|
||||||
|
JSON.stringify({
|
||||||
|
webpackCompilationHash: `123`,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
emitter.emit.mockReset()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
global.__PATH_PREFIX__ = originalPathPrefix
|
||||||
|
mock.teardown()
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should be successful when component can be loaded`, async () => {
|
||||||
|
const syncRequires = createSyncRequires({
|
||||||
|
chunk: `instance`,
|
||||||
|
})
|
||||||
|
const devLoader = new DevLoader(syncRequires, [])
|
||||||
|
const pageData = {
|
||||||
|
path: `/mypage/`,
|
||||||
|
componentChunkName: `chunk`,
|
||||||
|
result: {
|
||||||
|
pageContext: `something something`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
devLoader.loadPageDataJson = jest.fn(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
payload: pageData,
|
||||||
|
status: `success`,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const expectation = await devLoader.loadPage(`/mypage/`)
|
||||||
|
expect(expectation).toMatchSnapshot()
|
||||||
|
expect(Object.keys(expectation)).toEqual([`component`, `json`, `page`])
|
||||||
|
expect(devLoader.pageDb.get(`/mypage`)).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
payload: expectation,
|
||||||
|
status: `success`,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
expect(emitter.emit).toHaveBeenCalledTimes(1)
|
||||||
|
expect(emitter.emit).toHaveBeenCalledWith(`onPostLoadPageResources`, {
|
||||||
|
page: expectation,
|
||||||
|
pageResources: expectation,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should set not found on finalResult`, async () => {
|
||||||
|
const syncRequires = createSyncRequires({
|
||||||
|
chunk: `instance`,
|
||||||
|
})
|
||||||
|
const devLoader = new DevLoader(syncRequires, [])
|
||||||
|
const pageData = {
|
||||||
|
path: `/mypage/`,
|
||||||
|
componentChunkName: `chunk`,
|
||||||
|
}
|
||||||
|
devLoader.loadPageDataJson = jest.fn(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
payload: pageData,
|
||||||
|
status: `success`,
|
||||||
|
notFound: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
await devLoader.loadPage(`/mypage/`)
|
||||||
|
const expectation = devLoader.pageDb.get(`/mypage`)
|
||||||
|
expect(expectation).toHaveProperty(`notFound`, true)
|
||||||
|
expect(emitter.emit).toHaveBeenCalledTimes(1)
|
||||||
|
expect(emitter.emit).toHaveBeenCalledWith(`onPostLoadPageResources`, {
|
||||||
|
page: expectation.payload,
|
||||||
|
pageResources: expectation.payload,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should return an error when component cannot be loaded`, async () => {
|
||||||
|
const syncRequires = createSyncRequires({
|
||||||
|
chunk: false,
|
||||||
|
})
|
||||||
|
const devLoader = new DevLoader(syncRequires, [])
|
||||||
|
const pageData = {
|
||||||
|
path: `/mypage/`,
|
||||||
|
componentChunkName: `chunk`,
|
||||||
|
}
|
||||||
|
devLoader.loadPageDataJson = jest.fn(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
payload: pageData,
|
||||||
|
status: `success`,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
await devLoader.loadPage(`/mypage/`)
|
||||||
|
const expectation = devLoader.pageDb.get(`/mypage`)
|
||||||
|
expect(expectation).toHaveProperty(`status`, `error`)
|
||||||
|
expect(emitter.emit).toHaveBeenCalledTimes(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should return an error pageData contains an error`, async () => {
|
||||||
|
const syncRequires = createSyncRequires({
|
||||||
|
chunk: `instance`,
|
||||||
|
})
|
||||||
|
const devLoader = new DevLoader(syncRequires, [])
|
||||||
|
const pageData = {
|
||||||
|
path: `/mypage/`,
|
||||||
|
componentChunkName: `chunk`,
|
||||||
|
}
|
||||||
|
devLoader.loadPageDataJson = jest.fn(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
payload: pageData,
|
||||||
|
status: `error`,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(await devLoader.loadPage(`/mypage/`)).toEqual({ status: `error` })
|
||||||
|
expect(devLoader.pageDb.size).toBe(0)
|
||||||
|
expect(emitter.emit).toHaveBeenCalledTimes(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should throw an error when 404 cannot be fetched`, async () => {
|
||||||
|
const devLoader = new DevLoader(null, [])
|
||||||
|
|
||||||
|
devLoader.loadPageDataJson = jest.fn(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
status: `failure`,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
await devLoader.loadPage(`/404.html/`)
|
||||||
|
} catch (err) {
|
||||||
|
expect(err.message).toEqual(
|
||||||
|
expect.stringContaining(`404 page could not be found`)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
expect(devLoader.pageDb.size).toBe(0)
|
||||||
|
expect(emitter.emit).toHaveBeenCalledTimes(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should cache the result of loadPage`, async () => {
|
||||||
|
const syncRequires = createSyncRequires({
|
||||||
|
chunk: `instance`,
|
||||||
|
})
|
||||||
|
const devLoader = new DevLoader(syncRequires, [])
|
||||||
|
devLoader.loadPageDataJson = jest.fn(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
payload: {
|
||||||
|
componentChunkName: `chunk`,
|
||||||
|
},
|
||||||
|
status: `success`,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const expectation = await devLoader.loadPage(`/mypage/`)
|
||||||
|
expect(await devLoader.loadPage(`/mypage/`)).toBe(expectation)
|
||||||
|
expect(devLoader.loadPageDataJson).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe(`loadPageSync`, () => {
|
||||||
|
it(`returns page resources when already fetched`, () => {
|
||||||
|
const devLoader = new DevLoader(null, [])
|
||||||
|
|
||||||
|
devLoader.pageDb.set(`/mypage`, { payload: true })
|
||||||
|
expect(devLoader.loadPageSync(`/mypage/`)).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`returns page resources when already fetched`, () => {
|
||||||
|
const devLoader = new DevLoader(null, [])
|
||||||
|
|
||||||
|
expect(devLoader.loadPageSync(`/mypage/`)).toBeUndefined()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe(`prefetch`, () => {
|
||||||
|
const flushPromises = () => new Promise(resolve => setImmediate(resolve))
|
||||||
|
|
||||||
|
it(`shouldn't prefetch when shouldPrefetch is false`, () => {
|
||||||
|
const devLoader = new DevLoader(null, [])
|
||||||
|
devLoader.shouldPrefetch = jest.fn(() => false)
|
||||||
|
devLoader.doPrefetch = jest.fn()
|
||||||
|
devLoader.apiRunner = jest.fn()
|
||||||
|
|
||||||
|
expect(devLoader.prefetch(`/mypath/`)).toBe(false)
|
||||||
|
expect(devLoader.shouldPrefetch).toHaveBeenCalledWith(`/mypath/`)
|
||||||
|
expect(devLoader.apiRunner).not.toHaveBeenCalled()
|
||||||
|
expect(devLoader.doPrefetch).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should trigger custom prefetch logic when core is disabled`, () => {
|
||||||
|
const devLoader = new DevLoader(null, [])
|
||||||
|
devLoader.shouldPrefetch = jest.fn(() => true)
|
||||||
|
devLoader.doPrefetch = jest.fn()
|
||||||
|
devLoader.apiRunner = jest.fn()
|
||||||
|
devLoader.prefetchDisabled = true
|
||||||
|
|
||||||
|
expect(devLoader.prefetch(`/mypath/`)).toBe(false)
|
||||||
|
expect(devLoader.shouldPrefetch).toHaveBeenCalledWith(`/mypath/`)
|
||||||
|
expect(devLoader.apiRunner).toHaveBeenCalledWith(`onPrefetchPathname`, {
|
||||||
|
pathname: `/mypath/`,
|
||||||
|
})
|
||||||
|
expect(devLoader.doPrefetch).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should prefetch when not yet triggered`, async () => {
|
||||||
|
jest.useFakeTimers()
|
||||||
|
const devLoader = new DevLoader(null, [])
|
||||||
|
devLoader.shouldPrefetch = jest.fn(() => true)
|
||||||
|
devLoader.apiRunner = jest.fn()
|
||||||
|
devLoader.doPrefetch = jest.fn(() => Promise.resolve({}))
|
||||||
|
|
||||||
|
expect(devLoader.prefetch(`/mypath/`)).toBe(true)
|
||||||
|
|
||||||
|
// wait for doPrefetchPromise
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
|
expect(devLoader.apiRunner).toHaveBeenCalledWith(`onPrefetchPathname`, {
|
||||||
|
pathname: `/mypath/`,
|
||||||
|
})
|
||||||
|
expect(devLoader.apiRunner).toHaveBeenNthCalledWith(
|
||||||
|
2,
|
||||||
|
`onPostPrefetchPathname`,
|
||||||
|
{
|
||||||
|
pathname: `/mypath/`,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should only run apis once`, async () => {
|
||||||
|
const devLoader = new DevLoader(null, [])
|
||||||
|
devLoader.shouldPrefetch = jest.fn(() => true)
|
||||||
|
devLoader.apiRunner = jest.fn()
|
||||||
|
devLoader.doPrefetch = jest.fn(() => Promise.resolve({}))
|
||||||
|
|
||||||
|
expect(devLoader.prefetch(`/mypath/`)).toBe(true)
|
||||||
|
expect(devLoader.prefetch(`/mypath/`)).toBe(true)
|
||||||
|
|
||||||
|
// wait for doPrefetchPromise
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
|
expect(devLoader.apiRunner).toHaveBeenCalledTimes(2)
|
||||||
|
expect(devLoader.apiRunner).toHaveBeenNthCalledWith(
|
||||||
|
1,
|
||||||
|
`onPrefetchPathname`,
|
||||||
|
expect.anything()
|
||||||
|
)
|
||||||
|
expect(devLoader.apiRunner).toHaveBeenNthCalledWith(
|
||||||
|
2,
|
||||||
|
`onPostPrefetchPathname`,
|
||||||
|
expect.anything()
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,59 @@
|
||||||
|
import "@babel/polyfill"
|
||||||
|
const {
|
||||||
|
reportError,
|
||||||
|
clearError,
|
||||||
|
errorMap,
|
||||||
|
} = require(`../error-overlay-handler`)
|
||||||
|
|
||||||
|
import * as ErrorOverlay from "react-error-overlay"
|
||||||
|
|
||||||
|
jest.mock(`react-error-overlay`, () => {
|
||||||
|
return {
|
||||||
|
reportBuildError: jest.fn(),
|
||||||
|
dismissBuildError: jest.fn(),
|
||||||
|
startReportingRuntimeErrors: jest.fn(),
|
||||||
|
setEditorHandler: jest.fn(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
ErrorOverlay.reportBuildError.mockClear()
|
||||||
|
ErrorOverlay.dismissBuildError.mockClear()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe(`errorOverlayHandler`, () => {
|
||||||
|
describe(`clearError()`, () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
reportError(`foo`, `error`)
|
||||||
|
reportError(`bar`, `error`)
|
||||||
|
})
|
||||||
|
afterAll(() => {
|
||||||
|
clearError(`foo`)
|
||||||
|
clearError(`bar`)
|
||||||
|
})
|
||||||
|
it(`should clear specific error type`, () => {
|
||||||
|
expect(Object.keys(errorMap)).toHaveLength(2)
|
||||||
|
clearError(`foo`)
|
||||||
|
expect(Object.keys(errorMap)).toHaveLength(1)
|
||||||
|
expect(ErrorOverlay.dismissBuildError).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should call ErrorOverlay to dismiss build errors`, () => {
|
||||||
|
clearError(`foo`)
|
||||||
|
clearError(`bar`)
|
||||||
|
expect(ErrorOverlay.dismissBuildError).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe(`reportErrorOverlay()`, () => {
|
||||||
|
it(`should not add error if it's empty and not call ErrorOverlay`, () => {
|
||||||
|
reportError(`foo`, null)
|
||||||
|
expect(Object.keys(errorMap)).toHaveLength(0)
|
||||||
|
expect(ErrorOverlay.reportBuildError).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
it(`should add error if it has a truthy value and call ErrorOverlay`, () => {
|
||||||
|
reportError(`foo`, `bar`)
|
||||||
|
expect(Object.keys(errorMap)).toHaveLength(1)
|
||||||
|
expect(ErrorOverlay.reportBuildError).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,109 @@
|
||||||
|
import { cleanPath, setMatchPaths, findMatchPath, findPath } from "../find-path"
|
||||||
|
|
||||||
|
describe(`find-path`, () => {
|
||||||
|
describe(`cleanPath`, () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
global.__BASE_PATH__ = ``
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should strip out ? & # from a pathname`, () => {
|
||||||
|
expect(cleanPath(`/mypath#anchor?gatsby=cool`)).toBe(`/mypath`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should convert a /index.html to root dir`, () => {
|
||||||
|
expect(cleanPath(`/index.html`)).toBe(`/`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`strip out a basePrefix`, () => {
|
||||||
|
global.__BASE_PATH__ = `/blog`
|
||||||
|
expect(cleanPath(`/blog/mypath`)).toBe(`/mypath`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe(`findMatchPath`, () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
// reset matchPaths
|
||||||
|
setMatchPaths([])
|
||||||
|
global.__BASE_PATH__ = ``
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should find a path when matchPath found`, () => {
|
||||||
|
setMatchPaths([
|
||||||
|
{
|
||||||
|
matchPath: `/app/*`,
|
||||||
|
path: `/app`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(findMatchPath(`/app/dynamic-page#anchor?gatsby=cool`)).toBe(`/app`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should return null when no matchPathFound`, () => {
|
||||||
|
setMatchPaths([
|
||||||
|
{
|
||||||
|
matchPath: `/app/*`,
|
||||||
|
path: `/app`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(findMatchPath(`/notanapp/dynamic-page`)).toBeNull()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe(`findPath`, () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
// reset matchPaths
|
||||||
|
setMatchPaths([])
|
||||||
|
global.__BASE_PATH__ = ``
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should use matchPath if found`, () => {
|
||||||
|
setMatchPaths([
|
||||||
|
{
|
||||||
|
matchPath: `/app/*`,
|
||||||
|
path: `/app`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(findPath(`/app/dynamic-page#anchor?gatsby=cool`)).toBe(`/app`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should return the cleaned up path when no matchPathFound`, () => {
|
||||||
|
setMatchPaths([
|
||||||
|
{
|
||||||
|
matchPath: `/app/*`,
|
||||||
|
path: `/app`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(findPath(`/notanapp/my-page#anchor?gatsby=cool`)).toBe(
|
||||||
|
`/notanapp/my-page`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should only process a request once`, () => {
|
||||||
|
jest.resetModules()
|
||||||
|
jest.mock(`@reach/router/lib/utils`)
|
||||||
|
const findPath = require(`../find-path`).findPath
|
||||||
|
const setMatchPaths = require(`../find-path`).setMatchPaths
|
||||||
|
const match = require(`@reach/router/lib/utils`).match
|
||||||
|
|
||||||
|
setMatchPaths([
|
||||||
|
{
|
||||||
|
matchPath: `/app/*`,
|
||||||
|
path: `/app`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(findPath(`/notanapp/my-page#anchor?gatsby=cool`)).toBe(
|
||||||
|
`/notanapp/my-page`
|
||||||
|
)
|
||||||
|
expect(findPath(`/notanapp/my-page#anchor?gatsby=cool`)).toBe(
|
||||||
|
`/notanapp/my-page`
|
||||||
|
)
|
||||||
|
expect(findPath(`/notanapp/my-page`)).toBe(`/notanapp/my-page`)
|
||||||
|
|
||||||
|
expect(match).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,554 @@
|
||||||
|
// This is by no means a full test file for loader.js so feel free to add more tests.
|
||||||
|
import mock from "xhr-mock"
|
||||||
|
import { ProdLoader } from "../loader"
|
||||||
|
import emitter from "../emitter"
|
||||||
|
|
||||||
|
jest.mock(`../emitter`)
|
||||||
|
|
||||||
|
describe(`Production loader`, () => {
|
||||||
|
describe(`loadPageDataJson`, () => {
|
||||||
|
let originalBasePath
|
||||||
|
let originalPathPrefix
|
||||||
|
let xhrCount
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} path
|
||||||
|
* @param {number} status
|
||||||
|
* @param {string|Object?} responseText
|
||||||
|
* @param {boolean?} json
|
||||||
|
*/
|
||||||
|
const mockPageData = (path, status, responseText = ``, json = false) => {
|
||||||
|
mock.get(`/page-data${path}/page-data.json`, (req, res) => {
|
||||||
|
xhrCount++
|
||||||
|
if (json) {
|
||||||
|
res.header(`content-type`, `application/json`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
.status(status)
|
||||||
|
.body(
|
||||||
|
typeof responseText === `string`
|
||||||
|
? responseText
|
||||||
|
: JSON.stringify(responseText)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultPayload = {
|
||||||
|
path: `/mypage/`,
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace the real XHR object with the mock XHR object before each test
|
||||||
|
beforeEach(() => {
|
||||||
|
originalBasePath = global.__BASE_PATH__
|
||||||
|
originalPathPrefix = global.__PATH_PREFIX__
|
||||||
|
global.__BASE_PATH__ = ``
|
||||||
|
global.__PATH_PREFIX__ = ``
|
||||||
|
xhrCount = 0
|
||||||
|
mock.setup()
|
||||||
|
})
|
||||||
|
|
||||||
|
// put the real XHR object back and clear the mocks after each test
|
||||||
|
afterEach(() => {
|
||||||
|
global.__BASE_PATH__ = originalBasePath
|
||||||
|
global.__PATH_PREFIX__ = originalPathPrefix
|
||||||
|
mock.teardown()
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should return a pageData json on success`, async () => {
|
||||||
|
const prodLoader = new ProdLoader(null, [])
|
||||||
|
|
||||||
|
mockPageData(`/mypage`, 200, defaultPayload, true)
|
||||||
|
|
||||||
|
const expectation = {
|
||||||
|
status: `success`,
|
||||||
|
pagePath: `/mypage`,
|
||||||
|
payload: defaultPayload,
|
||||||
|
}
|
||||||
|
expect(await prodLoader.loadPageDataJson(`/mypage/`)).toEqual(expectation)
|
||||||
|
expect(prodLoader.pageDataDb.get(`/mypage`)).toEqual(expectation)
|
||||||
|
expect(xhrCount).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should return a pageData json on success without contentType`, async () => {
|
||||||
|
const prodLoader = new ProdLoader(null, [])
|
||||||
|
|
||||||
|
mockPageData(`/mypage`, 200, defaultPayload)
|
||||||
|
|
||||||
|
const expectation = {
|
||||||
|
status: `success`,
|
||||||
|
pagePath: `/mypage`,
|
||||||
|
payload: defaultPayload,
|
||||||
|
}
|
||||||
|
expect(await prodLoader.loadPageDataJson(`/mypage/`)).toEqual(expectation)
|
||||||
|
expect(prodLoader.pageDataDb.get(`/mypage`)).toEqual(expectation)
|
||||||
|
expect(xhrCount).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should return a pageData json with an empty compilation hash (gatsby develop)`, async () => {
|
||||||
|
const prodLoader = new ProdLoader(null, [])
|
||||||
|
|
||||||
|
const payload = { ...defaultPayload, webpackCompilationHash: `` }
|
||||||
|
mockPageData(`/mypage`, 200, payload)
|
||||||
|
|
||||||
|
const expectation = {
|
||||||
|
status: `success`,
|
||||||
|
pagePath: `/mypage`,
|
||||||
|
payload,
|
||||||
|
}
|
||||||
|
expect(await prodLoader.loadPageDataJson(`/mypage/`)).toEqual(expectation)
|
||||||
|
expect(prodLoader.pageDataDb.get(`/mypage`)).toEqual(expectation)
|
||||||
|
expect(xhrCount).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should load a 404 page when page-path file is not a gatsby json`, async () => {
|
||||||
|
const prodLoader = new ProdLoader(null, [])
|
||||||
|
|
||||||
|
const payload = { ...defaultPayload, path: `/404.html/` }
|
||||||
|
mockPageData(`/unknown-page`, 200, { random: `string` }, true)
|
||||||
|
mockPageData(`/404.html`, 200, payload, true)
|
||||||
|
|
||||||
|
const expectation = {
|
||||||
|
status: `success`,
|
||||||
|
pagePath: `/404.html`,
|
||||||
|
notFound: true,
|
||||||
|
payload,
|
||||||
|
}
|
||||||
|
expect(await prodLoader.loadPageDataJson(`/unknown-page/`)).toEqual(
|
||||||
|
expectation
|
||||||
|
)
|
||||||
|
expect(prodLoader.pageDataDb.get(`/unknown-page`)).toEqual(expectation)
|
||||||
|
expect(xhrCount).toBe(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should load a 404 page when page-path file is not a json`, async () => {
|
||||||
|
const prodLoader = new ProdLoader(null, [])
|
||||||
|
|
||||||
|
const payload = { ...defaultPayload, path: `/404.html/` }
|
||||||
|
mockPageData(`/unknown-page`, 200)
|
||||||
|
mockPageData(`/404.html`, 200, payload, true)
|
||||||
|
|
||||||
|
const expectation = {
|
||||||
|
status: `success`,
|
||||||
|
pagePath: `/404.html`,
|
||||||
|
notFound: true,
|
||||||
|
payload,
|
||||||
|
}
|
||||||
|
expect(await prodLoader.loadPageDataJson(`/unknown-page/`)).toEqual(
|
||||||
|
expectation
|
||||||
|
)
|
||||||
|
expect(prodLoader.pageDataDb.get(`/unknown-page`)).toEqual(expectation)
|
||||||
|
expect(xhrCount).toBe(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should load a 404 page when path returns a 404`, async () => {
|
||||||
|
const prodLoader = new ProdLoader(null, [])
|
||||||
|
|
||||||
|
const payload = { ...defaultPayload, path: `/404.html/` }
|
||||||
|
mockPageData(`/unknown-page`, 200)
|
||||||
|
mockPageData(`/404.html`, 200, payload, true)
|
||||||
|
|
||||||
|
const expectation = {
|
||||||
|
status: `success`,
|
||||||
|
pagePath: `/404.html`,
|
||||||
|
notFound: true,
|
||||||
|
payload,
|
||||||
|
}
|
||||||
|
expect(await prodLoader.loadPageDataJson(`/unknown-page/`)).toEqual(
|
||||||
|
expectation
|
||||||
|
)
|
||||||
|
expect(prodLoader.pageDataDb.get(`/unknown-page`)).toEqual(expectation)
|
||||||
|
expect(xhrCount).toBe(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should return a failure when status is 404 and 404 page is fetched`, async () => {
|
||||||
|
const prodLoader = new ProdLoader(null, [])
|
||||||
|
|
||||||
|
mockPageData(`/unknown-page`, 404)
|
||||||
|
mockPageData(`/404.html`, 404)
|
||||||
|
|
||||||
|
const expectation = {
|
||||||
|
status: `failure`,
|
||||||
|
pagePath: `/404.html`,
|
||||||
|
notFound: true,
|
||||||
|
}
|
||||||
|
expect(await prodLoader.loadPageDataJson(`/unknown-page/`)).toEqual(
|
||||||
|
expectation
|
||||||
|
)
|
||||||
|
expect(prodLoader.pageDataDb.get(`/unknown-page`)).toEqual(expectation)
|
||||||
|
expect(xhrCount).toBe(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should return an error when status is 500`, async () => {
|
||||||
|
const prodLoader = new ProdLoader(null, [])
|
||||||
|
|
||||||
|
mockPageData(`/error-page`, 500)
|
||||||
|
|
||||||
|
const expectation = {
|
||||||
|
status: `error`,
|
||||||
|
pagePath: `/error-page`,
|
||||||
|
}
|
||||||
|
expect(await prodLoader.loadPageDataJson(`/error-page/`)).toEqual(
|
||||||
|
expectation
|
||||||
|
)
|
||||||
|
expect(prodLoader.pageDataDb.get(`/error-page`)).toEqual(expectation)
|
||||||
|
expect(xhrCount).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should retry 3 times before returning an error`, async () => {
|
||||||
|
const prodLoader = new ProdLoader(null, [])
|
||||||
|
|
||||||
|
mockPageData(`/blocked-page`, 0)
|
||||||
|
|
||||||
|
const expectation = {
|
||||||
|
status: `error`,
|
||||||
|
retries: 3,
|
||||||
|
pagePath: `/blocked-page`,
|
||||||
|
}
|
||||||
|
expect(await prodLoader.loadPageDataJson(`/blocked-page/`)).toEqual(
|
||||||
|
expectation
|
||||||
|
)
|
||||||
|
expect(prodLoader.pageDataDb.get(`/blocked-page`)).toEqual(expectation)
|
||||||
|
expect(xhrCount).toBe(4)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should recover if we get 1 failure`, async () => {
|
||||||
|
const prodLoader = new ProdLoader(null, [])
|
||||||
|
const payload = {
|
||||||
|
path: `/blocked-page/`,
|
||||||
|
}
|
||||||
|
|
||||||
|
let xhrCount = 0
|
||||||
|
mock.get(`/page-data/blocked-page/page-data.json`, (req, res) => {
|
||||||
|
if (xhrCount++ === 0) {
|
||||||
|
return res.status(0).body(``)
|
||||||
|
} else {
|
||||||
|
res.header(`content-type`, `application/json`)
|
||||||
|
return res.status(200).body(JSON.stringify(payload))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const expectation = {
|
||||||
|
status: `success`,
|
||||||
|
retries: 1,
|
||||||
|
pagePath: `/blocked-page`,
|
||||||
|
payload,
|
||||||
|
}
|
||||||
|
expect(await prodLoader.loadPageDataJson(`/blocked-page/`)).toEqual(
|
||||||
|
expectation
|
||||||
|
)
|
||||||
|
expect(prodLoader.pageDataDb.get(`/blocked-page`)).toEqual(expectation)
|
||||||
|
expect(xhrCount).toBe(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`shouldn't load pageData multiple times`, async () => {
|
||||||
|
const prodLoader = new ProdLoader(null, [])
|
||||||
|
|
||||||
|
mockPageData(`/mypage`, 200, defaultPayload, true)
|
||||||
|
|
||||||
|
const expectation = await prodLoader.loadPageDataJson(`/mypage/`)
|
||||||
|
expect(await prodLoader.loadPageDataJson(`/mypage/`)).toBe(expectation)
|
||||||
|
expect(xhrCount).toBe(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe(`loadPage`, () => {
|
||||||
|
const createAsyncRequires = components => {
|
||||||
|
return {
|
||||||
|
components,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let originalPathPrefix
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
originalPathPrefix = global.__PATH_PREFIX__
|
||||||
|
global.__PATH_PREFIX__ = ``
|
||||||
|
mock.setup()
|
||||||
|
mock.get(`/app-data.json`, (req, res) =>
|
||||||
|
res
|
||||||
|
.status(200)
|
||||||
|
.header(`content-type`, `application/json`)
|
||||||
|
.body(
|
||||||
|
JSON.stringify({
|
||||||
|
webpackCompilationHash: `123`,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
emitter.emit.mockReset()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
global.__PATH_PREFIX__ = originalPathPrefix
|
||||||
|
mock.teardown()
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should be successful when component can be loaded`, async () => {
|
||||||
|
const asyncRequires = createAsyncRequires({
|
||||||
|
chunk: () => Promise.resolve(`instance`),
|
||||||
|
})
|
||||||
|
const prodLoader = new ProdLoader(asyncRequires, [])
|
||||||
|
const pageData = {
|
||||||
|
path: `/mypage/`,
|
||||||
|
componentChunkName: `chunk`,
|
||||||
|
result: {
|
||||||
|
pageContext: `something something`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
prodLoader.loadPageDataJson = jest.fn(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
payload: pageData,
|
||||||
|
status: `success`,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const expectation = await prodLoader.loadPage(`/mypage/`)
|
||||||
|
expect(expectation).toMatchSnapshot()
|
||||||
|
expect(Object.keys(expectation)).toEqual([`component`, `json`, `page`])
|
||||||
|
expect(prodLoader.pageDb.get(`/mypage`)).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
payload: expectation,
|
||||||
|
status: `success`,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
expect(emitter.emit).toHaveBeenCalledTimes(1)
|
||||||
|
expect(emitter.emit).toHaveBeenCalledWith(`onPostLoadPageResources`, {
|
||||||
|
page: expectation,
|
||||||
|
pageResources: expectation,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should set not found on finalResult`, async () => {
|
||||||
|
const asyncRequires = createAsyncRequires({
|
||||||
|
chunk: () => Promise.resolve(`instance`),
|
||||||
|
})
|
||||||
|
const prodLoader = new ProdLoader(asyncRequires, [])
|
||||||
|
const pageData = {
|
||||||
|
path: `/mypage/`,
|
||||||
|
componentChunkName: `chunk`,
|
||||||
|
}
|
||||||
|
prodLoader.loadPageDataJson = jest.fn(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
payload: pageData,
|
||||||
|
status: `success`,
|
||||||
|
notFound: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
await prodLoader.loadPage(`/mypage/`)
|
||||||
|
const expectation = prodLoader.pageDb.get(`/mypage`)
|
||||||
|
expect(expectation).toHaveProperty(`notFound`, true)
|
||||||
|
expect(emitter.emit).toHaveBeenCalledTimes(1)
|
||||||
|
expect(emitter.emit).toHaveBeenCalledWith(`onPostLoadPageResources`, {
|
||||||
|
page: expectation.payload,
|
||||||
|
pageResources: expectation.payload,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should return an error when component cannot be loaded`, async () => {
|
||||||
|
const asyncRequires = createAsyncRequires({
|
||||||
|
chunk: () => Promise.resolve(false),
|
||||||
|
})
|
||||||
|
const prodLoader = new ProdLoader(asyncRequires, [])
|
||||||
|
const pageData = {
|
||||||
|
path: `/mypage/`,
|
||||||
|
componentChunkName: `chunk`,
|
||||||
|
}
|
||||||
|
prodLoader.loadPageDataJson = jest.fn(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
payload: pageData,
|
||||||
|
status: `success`,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
await prodLoader.loadPage(`/mypage/`)
|
||||||
|
const expectation = prodLoader.pageDb.get(`/mypage`)
|
||||||
|
expect(expectation).toHaveProperty(`status`, `error`)
|
||||||
|
expect(emitter.emit).toHaveBeenCalledTimes(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should return an error pageData contains an error`, async () => {
|
||||||
|
const asyncRequires = createAsyncRequires({
|
||||||
|
chunk: () => Promise.resolve(`instance`),
|
||||||
|
})
|
||||||
|
const prodLoader = new ProdLoader(asyncRequires, [])
|
||||||
|
const pageData = {
|
||||||
|
path: `/mypage/`,
|
||||||
|
componentChunkName: `chunk`,
|
||||||
|
}
|
||||||
|
prodLoader.loadPageDataJson = jest.fn(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
payload: pageData,
|
||||||
|
status: `error`,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(await prodLoader.loadPage(`/mypage/`)).toEqual({ status: `error` })
|
||||||
|
expect(prodLoader.pageDb.size).toBe(0)
|
||||||
|
expect(emitter.emit).toHaveBeenCalledTimes(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should throw an error when 404 cannot be fetched`, async () => {
|
||||||
|
const prodLoader = new ProdLoader(null, [])
|
||||||
|
|
||||||
|
prodLoader.loadPageDataJson = jest.fn(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
status: `failure`,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
await prodLoader.loadPage(`/404.html/`)
|
||||||
|
} catch (err) {
|
||||||
|
expect(err.message).toEqual(
|
||||||
|
expect.stringContaining(`404 page could not be found`)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
expect(prodLoader.pageDb.size).toBe(0)
|
||||||
|
expect(emitter.emit).toHaveBeenCalledTimes(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should cache the result of loadPage`, async () => {
|
||||||
|
const asyncRequires = createAsyncRequires({
|
||||||
|
chunk: () => Promise.resolve(`instance`),
|
||||||
|
})
|
||||||
|
const prodLoader = new ProdLoader(asyncRequires, [])
|
||||||
|
prodLoader.loadPageDataJson = jest.fn(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
payload: {
|
||||||
|
componentChunkName: `chunk`,
|
||||||
|
},
|
||||||
|
status: `success`,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const expectation = await prodLoader.loadPage(`/mypage/`)
|
||||||
|
expect(await prodLoader.loadPage(`/mypage/`)).toBe(expectation)
|
||||||
|
expect(prodLoader.loadPageDataJson).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should only run 1 network request even when called multiple times`, async () => {
|
||||||
|
const asyncRequires = createAsyncRequires({
|
||||||
|
chunk: () => Promise.resolve(`instance`),
|
||||||
|
})
|
||||||
|
const prodLoader = new ProdLoader(asyncRequires, [])
|
||||||
|
prodLoader.loadPageDataJson = jest.fn(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
payload: {
|
||||||
|
componentChunkName: `chunk`,
|
||||||
|
},
|
||||||
|
status: `success`,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const loadPagePromise = prodLoader.loadPage(`/test-page/`)
|
||||||
|
expect(prodLoader.inFlightDb.size).toBe(1)
|
||||||
|
expect(prodLoader.loadPage(`/test-page/`)).toBe(loadPagePromise)
|
||||||
|
expect(prodLoader.inFlightDb.size).toBe(1)
|
||||||
|
|
||||||
|
const expectation = await loadPagePromise
|
||||||
|
|
||||||
|
expect(prodLoader.inFlightDb.size).toBe(0)
|
||||||
|
expect(emitter.emit).toHaveBeenCalledTimes(1)
|
||||||
|
expect(emitter.emit).toHaveBeenCalledWith(`onPostLoadPageResources`, {
|
||||||
|
page: expectation,
|
||||||
|
pageResources: expectation,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe(`loadPageSync`, () => {
|
||||||
|
it(`returns page resources when already fetched`, () => {
|
||||||
|
const prodLoader = new ProdLoader(null, [])
|
||||||
|
|
||||||
|
prodLoader.pageDb.set(`/mypage`, { payload: true })
|
||||||
|
expect(prodLoader.loadPageSync(`/mypage/`)).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`returns page resources when already fetched`, () => {
|
||||||
|
const prodLoader = new ProdLoader(null, [])
|
||||||
|
|
||||||
|
expect(prodLoader.loadPageSync(`/mypage/`)).toBeUndefined()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe(`prefetch`, () => {
|
||||||
|
const flushPromises = () => new Promise(resolve => setImmediate(resolve))
|
||||||
|
|
||||||
|
it(`shouldn't prefetch when shouldPrefetch is false`, () => {
|
||||||
|
const prodLoader = new ProdLoader(null, [])
|
||||||
|
prodLoader.shouldPrefetch = jest.fn(() => false)
|
||||||
|
prodLoader.doPrefetch = jest.fn()
|
||||||
|
prodLoader.apiRunner = jest.fn()
|
||||||
|
|
||||||
|
expect(prodLoader.prefetch(`/mypath/`)).toBe(false)
|
||||||
|
expect(prodLoader.shouldPrefetch).toHaveBeenCalledWith(`/mypath/`)
|
||||||
|
expect(prodLoader.apiRunner).not.toHaveBeenCalled()
|
||||||
|
expect(prodLoader.doPrefetch).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should trigger custom prefetch logic when core is disabled`, () => {
|
||||||
|
const prodLoader = new ProdLoader(null, [])
|
||||||
|
prodLoader.shouldPrefetch = jest.fn(() => true)
|
||||||
|
prodLoader.doPrefetch = jest.fn()
|
||||||
|
prodLoader.apiRunner = jest.fn()
|
||||||
|
prodLoader.prefetchDisabled = true
|
||||||
|
|
||||||
|
expect(prodLoader.prefetch(`/mypath/`)).toBe(false)
|
||||||
|
expect(prodLoader.shouldPrefetch).toHaveBeenCalledWith(`/mypath/`)
|
||||||
|
expect(prodLoader.apiRunner).toHaveBeenCalledWith(`onPrefetchPathname`, {
|
||||||
|
pathname: `/mypath/`,
|
||||||
|
})
|
||||||
|
expect(prodLoader.doPrefetch).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should prefetch when not yet triggered`, async () => {
|
||||||
|
jest.useFakeTimers()
|
||||||
|
const prodLoader = new ProdLoader(null, [])
|
||||||
|
prodLoader.shouldPrefetch = jest.fn(() => true)
|
||||||
|
prodLoader.apiRunner = jest.fn()
|
||||||
|
prodLoader.doPrefetch = jest.fn(() => Promise.resolve({}))
|
||||||
|
|
||||||
|
expect(prodLoader.prefetch(`/mypath/`)).toBe(true)
|
||||||
|
|
||||||
|
// wait for doPrefetchPromise
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
|
expect(prodLoader.apiRunner).toHaveBeenCalledWith(`onPrefetchPathname`, {
|
||||||
|
pathname: `/mypath/`,
|
||||||
|
})
|
||||||
|
expect(prodLoader.apiRunner).toHaveBeenNthCalledWith(
|
||||||
|
2,
|
||||||
|
`onPostPrefetchPathname`,
|
||||||
|
{
|
||||||
|
pathname: `/mypath/`,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should only run apis once`, async () => {
|
||||||
|
const prodLoader = new ProdLoader(null, [])
|
||||||
|
prodLoader.shouldPrefetch = jest.fn(() => true)
|
||||||
|
prodLoader.apiRunner = jest.fn()
|
||||||
|
prodLoader.doPrefetch = jest.fn(() => Promise.resolve({}))
|
||||||
|
|
||||||
|
expect(prodLoader.prefetch(`/mypath/`)).toBe(true)
|
||||||
|
expect(prodLoader.prefetch(`/mypath/`)).toBe(true)
|
||||||
|
|
||||||
|
// wait for doPrefetchPromise
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
|
expect(prodLoader.apiRunner).toHaveBeenCalledTimes(2)
|
||||||
|
expect(prodLoader.apiRunner).toHaveBeenNthCalledWith(
|
||||||
|
1,
|
||||||
|
`onPrefetchPathname`,
|
||||||
|
expect.anything()
|
||||||
|
)
|
||||||
|
expect(prodLoader.apiRunner).toHaveBeenNthCalledWith(
|
||||||
|
2,
|
||||||
|
`onPostPrefetchPathname`,
|
||||||
|
expect.anything()
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,32 @@
|
||||||
|
const path = require(`path`)
|
||||||
|
const child = require(`child_process`)
|
||||||
|
|
||||||
|
it(`Builds cache-dir with minimal config`, done => {
|
||||||
|
const args = [
|
||||||
|
require.resolve(`@babel/cli/bin/babel.js`),
|
||||||
|
path.join(__dirname, `..`),
|
||||||
|
`--config-file`,
|
||||||
|
path.join(__dirname, `.babelrc`),
|
||||||
|
`--ignore`,
|
||||||
|
`**/__tests__`,
|
||||||
|
]
|
||||||
|
|
||||||
|
const spawn = child.spawn(process.execPath, args)
|
||||||
|
|
||||||
|
let stderr = ``
|
||||||
|
let stdout = ``
|
||||||
|
|
||||||
|
spawn.stderr.on(`data`, function(chunk) {
|
||||||
|
stderr += chunk
|
||||||
|
})
|
||||||
|
|
||||||
|
spawn.stdout.on(`data`, function(chunk) {
|
||||||
|
stdout += chunk
|
||||||
|
})
|
||||||
|
|
||||||
|
spawn.on(`close`, function() {
|
||||||
|
expect(stderr).toEqual(``)
|
||||||
|
expect(stdout).not.toEqual(``)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
}, 30000)
|
|
@ -0,0 +1,330 @@
|
||||||
|
import React from "react"
|
||||||
|
import fs from "fs"
|
||||||
|
const { join } = require(`path`)
|
||||||
|
|
||||||
|
import DevelopStaticEntry from "../develop-static-entry"
|
||||||
|
|
||||||
|
jest.mock(`fs`, () => {
|
||||||
|
const fs = jest.requireActual(`fs`)
|
||||||
|
return {
|
||||||
|
...fs,
|
||||||
|
readFileSync: jest.fn(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
jest.mock(`gatsby/package.json`, () => {
|
||||||
|
return {
|
||||||
|
version: `2.0.0`,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
jest.mock(
|
||||||
|
`../sync-requires`,
|
||||||
|
() => {
|
||||||
|
return {
|
||||||
|
components: {
|
||||||
|
"page-component---src-pages-test-js": () => null,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
virtual: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const MOCK_FILE_INFO = {
|
||||||
|
[`${process.cwd()}/public/webpack.stats.json`]: `{}`,
|
||||||
|
[`${process.cwd()}/public/chunk-map.json`]: `{}`,
|
||||||
|
[join(
|
||||||
|
process.cwd(),
|
||||||
|
`/public/page-data/about/page-data.json`
|
||||||
|
)]: JSON.stringify({
|
||||||
|
componentChunkName: `page-component---src-pages-test-js`,
|
||||||
|
path: `/about/`,
|
||||||
|
webpackCompilationHash: `1234567890abcdef1234`,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
let StaticEntry
|
||||||
|
beforeEach(() => {
|
||||||
|
fs.readFileSync.mockImplementation(file => MOCK_FILE_INFO[file])
|
||||||
|
StaticEntry = require(`../static-entry`).default
|
||||||
|
})
|
||||||
|
|
||||||
|
const reverseHeadersPlugin = {
|
||||||
|
plugin: {
|
||||||
|
onPreRenderHTML: ({ getHeadComponents, replaceHeadComponents }) => {
|
||||||
|
const headComponents = getHeadComponents()
|
||||||
|
headComponents.reverse()
|
||||||
|
replaceHeadComponents(headComponents)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const injectValuePlugin = (hookName, methodName, value) => {
|
||||||
|
return {
|
||||||
|
plugin: {
|
||||||
|
[hookName]: staticEntry => {
|
||||||
|
const method = staticEntry[methodName]
|
||||||
|
method(value)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkSanitized = components => {
|
||||||
|
expect(components.includes(null)).toBeFalsy()
|
||||||
|
expect(
|
||||||
|
components.find(val => Array.isArray(val) && val.length === 0)
|
||||||
|
).toBeFalsy()
|
||||||
|
expect(components.find(val => Array.isArray(val))).toBeFalsy()
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkNonEmptyHeadersPlugin = {
|
||||||
|
plugin: {
|
||||||
|
onPreRenderHTML: ({
|
||||||
|
getHeadComponents,
|
||||||
|
getPreBodyComponents,
|
||||||
|
getPostBodyComponents,
|
||||||
|
}) => {
|
||||||
|
const headComponents = getHeadComponents()
|
||||||
|
const preBodyComponents = getPreBodyComponents()
|
||||||
|
const postBodyComponents = getPostBodyComponents()
|
||||||
|
checkSanitized(headComponents)
|
||||||
|
checkSanitized(preBodyComponents)
|
||||||
|
checkSanitized(postBodyComponents)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const fakeStylesPlugin = {
|
||||||
|
plugin: {
|
||||||
|
onRenderBody: ({ setHeadComponents }) =>
|
||||||
|
setHeadComponents([
|
||||||
|
<style key="style1"> .style1 {} </style>,
|
||||||
|
<style key="style2"> .style2 {} </style>,
|
||||||
|
<style key="style3"> .style3 {} </style>,
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const reverseBodyComponentsPluginFactory = type => {
|
||||||
|
return {
|
||||||
|
plugin: {
|
||||||
|
onPreRenderHTML: props => {
|
||||||
|
const components = props[`get${type}BodyComponents`]()
|
||||||
|
components.reverse()
|
||||||
|
props[`replace${type}BodyComponents`](components)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fakeComponentsPluginFactory = type => {
|
||||||
|
return {
|
||||||
|
plugin: {
|
||||||
|
onRenderBody: props => {
|
||||||
|
props[`set${type}BodyComponents`]([
|
||||||
|
<div key="div1"> div1 </div>,
|
||||||
|
<div key="div2"> div2 </div>,
|
||||||
|
<div key="div3"> div3 </div>,
|
||||||
|
])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe(`develop-static-entry`, () => {
|
||||||
|
test(`onPreRenderHTML can be used to replace headComponents`, done => {
|
||||||
|
global.plugins = [fakeStylesPlugin, reverseHeadersPlugin]
|
||||||
|
|
||||||
|
DevelopStaticEntry(`/about/`, (_, html) => {
|
||||||
|
expect(html).toMatchSnapshot()
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`onPreRenderHTML can be used to replace postBodyComponents`, done => {
|
||||||
|
global.plugins = [
|
||||||
|
fakeComponentsPluginFactory(`Post`),
|
||||||
|
reverseBodyComponentsPluginFactory(`Post`),
|
||||||
|
]
|
||||||
|
|
||||||
|
DevelopStaticEntry(`/about/`, (_, html) => {
|
||||||
|
expect(html).toMatchSnapshot()
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`onPreRenderHTML can be used to replace preBodyComponents`, done => {
|
||||||
|
global.plugins = [
|
||||||
|
fakeComponentsPluginFactory(`Pre`),
|
||||||
|
reverseBodyComponentsPluginFactory(`Pre`),
|
||||||
|
]
|
||||||
|
|
||||||
|
DevelopStaticEntry(`/about/`, (_, html) => {
|
||||||
|
expect(html).toMatchSnapshot()
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`onPreRenderHTML adds metatag note for development environment`, done => {
|
||||||
|
DevelopStaticEntry(`/about/`, (_, html) => {
|
||||||
|
expect(html).toContain(
|
||||||
|
`<meta name="note" content="environment=development"/>`
|
||||||
|
)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`onPreRenderHTML adds metatag note for development environment after replaceHeadComponents`, done => {
|
||||||
|
global.plugins = [reverseHeadersPlugin]
|
||||||
|
|
||||||
|
DevelopStaticEntry(`/about/`, (_, html) => {
|
||||||
|
expect(html).toContain(
|
||||||
|
`<meta name="note" content="environment=development"/>`
|
||||||
|
)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe(`static-entry sanity checks`, () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
global.__PATH_PREFIX__ = ``
|
||||||
|
global.__BASE_PATH__ = ``
|
||||||
|
global.__ASSET_PREFIX__ = ``
|
||||||
|
})
|
||||||
|
|
||||||
|
const methodsToCheck = [
|
||||||
|
`replaceHeadComponents`,
|
||||||
|
`replacePreBodyComponents`,
|
||||||
|
`replacePostBodyComponents`,
|
||||||
|
]
|
||||||
|
|
||||||
|
methodsToCheck.forEach(methodName => {
|
||||||
|
test(`${methodName} can filter out null value`, done => {
|
||||||
|
const plugin = injectValuePlugin(`onPreRenderHTML`, methodName, null)
|
||||||
|
global.plugins = [plugin, checkNonEmptyHeadersPlugin]
|
||||||
|
|
||||||
|
StaticEntry(`/about/`, (_, html) => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`${methodName} can filter out null values`, done => {
|
||||||
|
const plugin = injectValuePlugin(`onPreRenderHTML`, methodName, [
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
])
|
||||||
|
global.plugins = [plugin, checkNonEmptyHeadersPlugin]
|
||||||
|
|
||||||
|
StaticEntry(`/about/`, (_, html) => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`${methodName} can filter out empty array`, done => {
|
||||||
|
const plugin = injectValuePlugin(`onPreRenderHTML`, methodName, [])
|
||||||
|
global.plugins = [plugin, checkNonEmptyHeadersPlugin]
|
||||||
|
|
||||||
|
StaticEntry(`/about/`, (_, html) => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`${methodName} can filter out empty arrays`, done => {
|
||||||
|
const plugin = injectValuePlugin(`onPreRenderHTML`, methodName, [[], []])
|
||||||
|
global.plugins = [plugin, checkNonEmptyHeadersPlugin]
|
||||||
|
|
||||||
|
StaticEntry(`/about/`, (_, html) => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`${methodName} can flatten arrays`, done => {
|
||||||
|
const plugin = injectValuePlugin(`onPreRenderHTML`, methodName, [
|
||||||
|
<style key="style1"> .style1 {} </style>,
|
||||||
|
<style key="style2"> .style2 {} </style>,
|
||||||
|
<style key="style3"> .style3 {} </style>,
|
||||||
|
[<style key="style4"> .style3 {} </style>],
|
||||||
|
])
|
||||||
|
global.plugins = [plugin, checkNonEmptyHeadersPlugin]
|
||||||
|
|
||||||
|
StaticEntry(`/about/`, (_, html) => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe(`static-entry`, () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
global.__PATH_PREFIX__ = ``
|
||||||
|
global.__BASE_PATH__ = ``
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`onPreRenderHTML can be used to replace headComponents`, done => {
|
||||||
|
global.plugins = [fakeStylesPlugin, reverseHeadersPlugin]
|
||||||
|
|
||||||
|
StaticEntry(`/about/`, (_, html) => {
|
||||||
|
expect(html).toMatchSnapshot()
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`onPreRenderHTML can be used to replace postBodyComponents`, done => {
|
||||||
|
global.plugins = [
|
||||||
|
fakeComponentsPluginFactory(`Post`),
|
||||||
|
reverseBodyComponentsPluginFactory(`Post`),
|
||||||
|
]
|
||||||
|
|
||||||
|
StaticEntry(`/about/`, (_, html) => {
|
||||||
|
expect(html).toMatchSnapshot()
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`onPreRenderHTML can be used to replace preBodyComponents`, done => {
|
||||||
|
global.plugins = [
|
||||||
|
fakeComponentsPluginFactory(`Pre`),
|
||||||
|
reverseBodyComponentsPluginFactory(`Pre`),
|
||||||
|
]
|
||||||
|
|
||||||
|
StaticEntry(`/about/`, (_, html) => {
|
||||||
|
expect(html).toMatchSnapshot()
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`onPreRenderHTML does not add metatag note for development environment`, done => {
|
||||||
|
StaticEntry(`/about/`, (_, html) => {
|
||||||
|
expect(html).not.toContain(
|
||||||
|
`<meta name="note" content="environment=development"/>`
|
||||||
|
)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe(`sanitizeComponents`, () => {
|
||||||
|
let sanitizeComponents
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fs.readFileSync.mockImplementation(file => MOCK_FILE_INFO[file])
|
||||||
|
sanitizeComponents = require(`../static-entry`).sanitizeComponents
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`strips assetPrefix for manifest link`, () => {
|
||||||
|
global.__PATH_PREFIX__ = `https://gatsbyjs.org/blog`
|
||||||
|
global.__BASE_PATH__ = `/blog`
|
||||||
|
global.__ASSET_PREFIX__ = `https://gatsbyjs.org`
|
||||||
|
|
||||||
|
const sanitizedComponents = sanitizeComponents([
|
||||||
|
<link
|
||||||
|
rel="manifest"
|
||||||
|
href="https://gatsbyjs.org/blog/manifest.webmanifest"
|
||||||
|
/>,
|
||||||
|
])
|
||||||
|
expect(sanitizedComponents[0].props.href).toBe(`/blog/manifest.webmanifest`)
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,23 @@
|
||||||
|
const stripPrefix = require(`../strip-prefix`).default
|
||||||
|
|
||||||
|
describe(`strip-prefix`, () => {
|
||||||
|
it(`strips a prefix`, () => {
|
||||||
|
expect(stripPrefix(`/foo/bar/`, `/foo`)).toBe(`/bar/`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`strips first instance only`, () => {
|
||||||
|
expect(stripPrefix(`/foo/foo/bar/`, `/foo`)).toBe(`/foo/bar/`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`ignores prefix appearing elsewhere in the string`, () => {
|
||||||
|
expect(stripPrefix(`/foo/bar/`, `bar`)).toBe(`/foo/bar/`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`ignores a non-existent prefix`, () => {
|
||||||
|
expect(stripPrefix(`/bar`, `/foo`)).toBe(`/bar`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`returns input str if no prefix is provided`, () => {
|
||||||
|
expect(stripPrefix(`/bar`)).toBe(`/bar`)
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,28 @@
|
||||||
|
module.exports = [{
|
||||||
|
plugin: require('../node_modules/gatsby-remark-autolink-headers/gatsby-browser.js'),
|
||||||
|
options: {"plugins":[]},
|
||||||
|
},{
|
||||||
|
plugin: require('../node_modules/gatsby-plugin-offline/gatsby-browser.js'),
|
||||||
|
options: {"plugins":[]},
|
||||||
|
},{
|
||||||
|
plugin: require('../node_modules/gatsby-plugin-manifest/gatsby-browser.js'),
|
||||||
|
options: {"plugins":[],"name":"gatsby-starter-default","short_name":"starter","start_url":"/","background_color":"#722ED1","theme_color":"#722ED1","display":"minimal-ui","icon":"/Users/lizhengxue/Documents/AntV/github/L7_2.0/L7/node_modules/@antv/gatsby-theme-antv/site/images/favicon.png"},
|
||||||
|
},{
|
||||||
|
plugin: require('../node_modules/gatsby-plugin-catch-links/gatsby-browser.js'),
|
||||||
|
options: {"plugins":[]},
|
||||||
|
},{
|
||||||
|
plugin: require('../node_modules/gatsby-plugin-nprogress/gatsby-browser.js'),
|
||||||
|
options: {"plugins":[]},
|
||||||
|
},{
|
||||||
|
plugin: require('../node_modules/gatsby-plugin-layout/gatsby-browser.js'),
|
||||||
|
options: {"plugins":[],"component":"/Users/lizhengxue/Documents/AntV/github/L7_2.0/L7/node_modules/@antv/gatsby-theme-antv/site/layouts/layout.tsx"},
|
||||||
|
},{
|
||||||
|
plugin: require('../node_modules/gatsby-plugin-nprogress/gatsby-browser.js'),
|
||||||
|
options: {"plugins":[],"color":"#722ED1"},
|
||||||
|
},{
|
||||||
|
plugin: require('../node_modules/@antv/gatsby-theme-antv/gatsby-browser.js'),
|
||||||
|
options: {"plugins":[],"pathPrefix":"/gatsby-theme-antv"},
|
||||||
|
},{
|
||||||
|
plugin: require('../gatsby-browser.js'),
|
||||||
|
options: {"plugins":[]},
|
||||||
|
}]
|
|
@ -0,0 +1,61 @@
|
||||||
|
const plugins = require(`./api-runner-browser-plugins`)
|
||||||
|
const {
|
||||||
|
getResourcesForPathname,
|
||||||
|
getResourcesForPathnameSync,
|
||||||
|
getResourceURLsForPathname,
|
||||||
|
loadPage,
|
||||||
|
loadPageSync,
|
||||||
|
} = require(`./loader`).publicLoader
|
||||||
|
|
||||||
|
exports.apiRunner = (api, args = {}, defaultReturn, argTransform) => {
|
||||||
|
// Hooks for gatsby-cypress's API handler
|
||||||
|
if (process.env.CYPRESS_SUPPORT) {
|
||||||
|
if (window.___apiHandler) {
|
||||||
|
window.___apiHandler(api)
|
||||||
|
} else if (window.___resolvedAPIs) {
|
||||||
|
window.___resolvedAPIs.push(api)
|
||||||
|
} else {
|
||||||
|
window.___resolvedAPIs = [api]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let results = plugins.map(plugin => {
|
||||||
|
if (!plugin.plugin[api]) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated April 2019. Use `loadPageSync` instead
|
||||||
|
args.getResourcesForPathnameSync = getResourcesForPathnameSync
|
||||||
|
// Deprecated April 2019. Use `loadPage` instead
|
||||||
|
args.getResourcesForPathname = getResourcesForPathname
|
||||||
|
args.getResourceURLsForPathname = getResourceURLsForPathname
|
||||||
|
args.loadPage = loadPage
|
||||||
|
args.loadPageSync = loadPageSync
|
||||||
|
|
||||||
|
const result = plugin.plugin[api](args, plugin.options)
|
||||||
|
if (result && argTransform) {
|
||||||
|
args = argTransform({ args, result, plugin })
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
|
||||||
|
// Filter out undefined results.
|
||||||
|
results = results.filter(result => typeof result !== `undefined`)
|
||||||
|
|
||||||
|
if (results.length > 0) {
|
||||||
|
return results
|
||||||
|
} else if (defaultReturn) {
|
||||||
|
return [defaultReturn]
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.apiRunnerAsync = (api, args, defaultReturn) =>
|
||||||
|
plugins.reduce(
|
||||||
|
(previous, next) =>
|
||||||
|
next.plugin[api]
|
||||||
|
? previous.then(() => next.plugin[api](args, next.options))
|
||||||
|
: previous,
|
||||||
|
Promise.resolve()
|
||||||
|
)
|
|
@ -0,0 +1,58 @@
|
||||||
|
var plugins = [{
|
||||||
|
plugin: require('/Users/lizhengxue/Documents/AntV/github/L7_2.0/L7/node_modules/gatsby-plugin-react-helmet/gatsby-ssr'),
|
||||||
|
options: {"plugins":[]},
|
||||||
|
},{
|
||||||
|
plugin: require('/Users/lizhengxue/Documents/AntV/github/L7_2.0/L7/node_modules/gatsby-remark-autolink-headers/gatsby-ssr'),
|
||||||
|
options: {"plugins":[]},
|
||||||
|
},{
|
||||||
|
plugin: require('/Users/lizhengxue/Documents/AntV/github/L7_2.0/L7/node_modules/gatsby-plugin-offline/gatsby-ssr'),
|
||||||
|
options: {"plugins":[]},
|
||||||
|
},{
|
||||||
|
plugin: require('/Users/lizhengxue/Documents/AntV/github/L7_2.0/L7/node_modules/gatsby-plugin-manifest/gatsby-ssr'),
|
||||||
|
options: {"plugins":[],"name":"gatsby-starter-default","short_name":"starter","start_url":"/","background_color":"#722ED1","theme_color":"#722ED1","display":"minimal-ui","icon":"/Users/lizhengxue/Documents/AntV/github/L7_2.0/L7/node_modules/@antv/gatsby-theme-antv/site/images/favicon.png"},
|
||||||
|
},{
|
||||||
|
plugin: require('/Users/lizhengxue/Documents/AntV/github/L7_2.0/L7/node_modules/gatsby-plugin-layout/gatsby-ssr'),
|
||||||
|
options: {"plugins":[],"component":"/Users/lizhengxue/Documents/AntV/github/L7_2.0/L7/node_modules/@antv/gatsby-theme-antv/site/layouts/layout.tsx"},
|
||||||
|
}]
|
||||||
|
// During bootstrap, we write requires at top of this file which looks like:
|
||||||
|
// var plugins = [
|
||||||
|
// {
|
||||||
|
// plugin: require("/path/to/plugin1/gatsby-ssr.js"),
|
||||||
|
// options: { ... },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// plugin: require("/path/to/plugin2/gatsby-ssr.js"),
|
||||||
|
// options: { ... },
|
||||||
|
// },
|
||||||
|
// ]
|
||||||
|
|
||||||
|
const apis = require(`./api-ssr-docs`)
|
||||||
|
|
||||||
|
// Run the specified API in any plugins that have implemented it
|
||||||
|
module.exports = (api, args, defaultReturn, argTransform) => {
|
||||||
|
if (!apis[api]) {
|
||||||
|
console.log(`This API doesn't exist`, api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run each plugin in series.
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
let results = plugins.map(plugin => {
|
||||||
|
if (!plugin.plugin[api]) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
const result = plugin.plugin[api](args, plugin.options)
|
||||||
|
if (result && argTransform) {
|
||||||
|
args = argTransform({ args, result })
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
|
||||||
|
// Filter out undefined results.
|
||||||
|
results = results.filter(result => typeof result !== `undefined`)
|
||||||
|
|
||||||
|
if (results.length > 0) {
|
||||||
|
return results
|
||||||
|
} else {
|
||||||
|
return [defaultReturn]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,198 @@
|
||||||
|
/**
|
||||||
|
* Object containing options defined in `gatsby-config.js`
|
||||||
|
* @typedef {object} pluginOptions
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace the default server renderer. This is useful for integration with
|
||||||
|
* Redux, css-in-js libraries, etc. that need custom setups for server
|
||||||
|
* rendering.
|
||||||
|
* @param {object} $0
|
||||||
|
* @param {string} $0.pathname The pathname of the page currently being rendered.
|
||||||
|
* @param {function} $0.replaceBodyHTMLString Call this with the HTML string
|
||||||
|
* you render. **WARNING** if multiple plugins implement this API it's the
|
||||||
|
* last plugin that "wins". TODO implement an automated warning against this.
|
||||||
|
* @param {function} $0.setHeadComponents Takes an array of components as its
|
||||||
|
* first argument which are added to the `headComponents` array which is passed
|
||||||
|
* to the `html.js` component.
|
||||||
|
* @param {function} $0.setHtmlAttributes Takes an object of props which will
|
||||||
|
* spread into the `<html>` component.
|
||||||
|
* @param {function} $0.setBodyAttributes Takes an object of props which will
|
||||||
|
* spread into the `<body>` component.
|
||||||
|
* @param {function} $0.setPreBodyComponents Takes an array of components as its
|
||||||
|
* first argument which are added to the `preBodyComponents` array which is passed
|
||||||
|
* to the `html.js` component.
|
||||||
|
* @param {function} $0.setPostBodyComponents Takes an array of components as its
|
||||||
|
* first argument which are added to the `postBodyComponents` array which is passed
|
||||||
|
* to the `html.js` component.
|
||||||
|
* @param {function} $0.setBodyProps Takes an object of data which
|
||||||
|
* is merged with other body props and passed to `html.js` as `bodyProps`.
|
||||||
|
* @param {pluginOptions} pluginOptions
|
||||||
|
* @example
|
||||||
|
* // From gatsby-plugin-glamor
|
||||||
|
* const { renderToString } = require("react-dom/server")
|
||||||
|
* const inline = require("glamor-inline")
|
||||||
|
*
|
||||||
|
* exports.replaceRenderer = ({ bodyComponent, replaceBodyHTMLString }) => {
|
||||||
|
* const bodyHTML = renderToString(bodyComponent)
|
||||||
|
* const inlinedHTML = inline(bodyHTML)
|
||||||
|
*
|
||||||
|
* replaceBodyHTMLString(inlinedHTML)
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
exports.replaceRenderer = true
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after every page Gatsby server renders while building HTML so you can
|
||||||
|
* set head and body components to be rendered in your `html.js`.
|
||||||
|
*
|
||||||
|
* Gatsby does a two-pass render for HTML. It loops through your pages first
|
||||||
|
* rendering only the body and then takes the result body HTML string and
|
||||||
|
* passes it as the `body` prop to your `html.js` to complete the render.
|
||||||
|
*
|
||||||
|
* It's often handy to be able to send custom components to your `html.js`.
|
||||||
|
* For example, it's a very common pattern for React.js libraries that
|
||||||
|
* support server rendering to pull out data generated during the render to
|
||||||
|
* add to your HTML.
|
||||||
|
*
|
||||||
|
* Using this API over [`replaceRenderer`](#replaceRenderer) is preferable as
|
||||||
|
* multiple plugins can implement this API where only one plugin can take
|
||||||
|
* over server rendering. However, if your plugin requires taking over server
|
||||||
|
* rendering then that's the one to
|
||||||
|
* use
|
||||||
|
* @param {object} $0
|
||||||
|
* @param {string} $0.pathname The pathname of the page currently being rendered.
|
||||||
|
* @param {function} $0.setHeadComponents Takes an array of components as its
|
||||||
|
* first argument which are added to the `headComponents` array which is passed
|
||||||
|
* to the `html.js` component.
|
||||||
|
* @param {function} $0.setHtmlAttributes Takes an object of props which will
|
||||||
|
* spread into the `<html>` component.
|
||||||
|
* @param {function} $0.setBodyAttributes Takes an object of props which will
|
||||||
|
* spread into the `<body>` component.
|
||||||
|
* @param {function} $0.setPreBodyComponents Takes an array of components as its
|
||||||
|
* first argument which are added to the `preBodyComponents` array which is passed
|
||||||
|
* to the `html.js` component.
|
||||||
|
* @param {function} $0.setPostBodyComponents Takes an array of components as its
|
||||||
|
* first argument which are added to the `postBodyComponents` array which is passed
|
||||||
|
* to the `html.js` component.
|
||||||
|
* @param {function} $0.setBodyProps Takes an object of data which
|
||||||
|
* is merged with other body props and passed to `html.js` as `bodyProps`.
|
||||||
|
* @param {pluginOptions} pluginOptions
|
||||||
|
* @example
|
||||||
|
* const { Helmet } = require("react-helmet")
|
||||||
|
*
|
||||||
|
* exports.onRenderBody = (
|
||||||
|
* { setHeadComponents, setHtmlAttributes, setBodyAttributes },
|
||||||
|
* pluginOptions
|
||||||
|
* ) => {
|
||||||
|
* const helmet = Helmet.renderStatic()
|
||||||
|
* setHtmlAttributes(helmet.htmlAttributes.toComponent())
|
||||||
|
* setBodyAttributes(helmet.bodyAttributes.toComponent())
|
||||||
|
* setHeadComponents([
|
||||||
|
* helmet.title.toComponent(),
|
||||||
|
* helmet.link.toComponent(),
|
||||||
|
* helmet.meta.toComponent(),
|
||||||
|
* helmet.noscript.toComponent(),
|
||||||
|
* helmet.script.toComponent(),
|
||||||
|
* helmet.style.toComponent(),
|
||||||
|
* ])
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
exports.onRenderBody = true
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after every page Gatsby server renders while building HTML so you can
|
||||||
|
* replace head components to be rendered in your `html.js`. This is useful if
|
||||||
|
* you need to reorder scripts or styles added by other plugins.
|
||||||
|
* @param {object} $0
|
||||||
|
* @param {string} $0.pathname The pathname of the page currently being rendered.
|
||||||
|
* @param {Array<ReactNode>} $0.getHeadComponents Returns the current `headComponents` array.
|
||||||
|
* @param {function} $0.replaceHeadComponents Takes an array of components as its
|
||||||
|
* first argument which replace the `headComponents` array which is passed
|
||||||
|
* to the `html.js` component. **WARNING** if multiple plugins implement this
|
||||||
|
* API it's the last plugin that "wins".
|
||||||
|
* @param {Array<ReactNode>} $0.getPreBodyComponents Returns the current `preBodyComponents` array.
|
||||||
|
* @param {function} $0.replacePreBodyComponents Takes an array of components as its
|
||||||
|
* first argument which replace the `preBodyComponents` array which is passed
|
||||||
|
* to the `html.js` component. **WARNING** if multiple plugins implement this
|
||||||
|
* API it's the last plugin that "wins".
|
||||||
|
* @param {Array<ReactNode>} $0.getPostBodyComponents Returns the current `postBodyComponents` array.
|
||||||
|
* @param {function} $0.replacePostBodyComponents Takes an array of components as its
|
||||||
|
* first argument which replace the `postBodyComponents` array which is passed
|
||||||
|
* to the `html.js` component. **WARNING** if multiple plugins implement this
|
||||||
|
* API it's the last plugin that "wins".
|
||||||
|
* @param {pluginOptions} pluginOptions
|
||||||
|
* @example
|
||||||
|
* // Move Typography.js styles to the top of the head section so they're loaded first.
|
||||||
|
* exports.onPreRenderHTML = ({ getHeadComponents, replaceHeadComponents }) => {
|
||||||
|
* const headComponents = getHeadComponents()
|
||||||
|
* headComponents.sort((x, y) => {
|
||||||
|
* if (x.key === 'TypographyStyle') {
|
||||||
|
* return -1
|
||||||
|
* } else if (y.key === 'TypographyStyle') {
|
||||||
|
* return 1
|
||||||
|
* }
|
||||||
|
* return 0
|
||||||
|
* })
|
||||||
|
* replaceHeadComponents(headComponents)
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
exports.onPreRenderHTML = true
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow a plugin to wrap the page element.
|
||||||
|
*
|
||||||
|
* This is useful for setting wrapper components around pages that won't get
|
||||||
|
* unmounted on page changes. For setting Provider components, use [wrapRootElement](#wrapRootElement).
|
||||||
|
*
|
||||||
|
* _Note:_
|
||||||
|
* There is an equivalent hook in Gatsby's [Browser API](/docs/browser-apis/#wrapPageElement).
|
||||||
|
* It is recommended to use both APIs together.
|
||||||
|
* For example usage, check out [Using i18n](https://github.com/gatsbyjs/gatsby/tree/master/examples/using-i18n).
|
||||||
|
* @param {object} $0
|
||||||
|
* @param {ReactNode} $0.element The "Page" React Element built by Gatsby.
|
||||||
|
* @param {object} $0.props Props object used by page.
|
||||||
|
* @param {pluginOptions} pluginOptions
|
||||||
|
* @returns {ReactNode} Wrapped element
|
||||||
|
* @example
|
||||||
|
* const React = require("react")
|
||||||
|
* const Layout = require("./src/components/layout").default
|
||||||
|
*
|
||||||
|
* exports.wrapPageElement = ({ element, props }) => {
|
||||||
|
* // props provide same data to Layout as Page element will get
|
||||||
|
* // including location, data, etc - you don't need to pass it
|
||||||
|
* return <Layout {...props}>{element}</Layout>
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
exports.wrapPageElement = true
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow a plugin to wrap the root element.
|
||||||
|
*
|
||||||
|
* This is useful to set up any Provider components that will wrap your application.
|
||||||
|
* For setting persistent UI elements around pages use [wrapPageElement](#wrapPageElement).
|
||||||
|
*
|
||||||
|
* _Note:_
|
||||||
|
* There is an equivalent hook in Gatsby's [Browser API](/docs/browser-apis/#wrapRootElement).
|
||||||
|
* It is recommended to use both APIs together.
|
||||||
|
* For example usage, check out [Using redux](https://github.com/gatsbyjs/gatsby/tree/master/examples/using-redux).
|
||||||
|
* @param {object} $0
|
||||||
|
* @param {ReactNode} $0.element The "Root" React Element built by Gatsby.
|
||||||
|
* @param {pluginOptions} pluginOptions
|
||||||
|
* @returns {ReactNode} Wrapped element
|
||||||
|
* @example
|
||||||
|
* const React = require("react")
|
||||||
|
* const { Provider } = require("react-redux")
|
||||||
|
*
|
||||||
|
* const createStore = require("./src/state/createStore")
|
||||||
|
* const store = createStore()
|
||||||
|
*
|
||||||
|
* exports.wrapRootElement = ({ element }) => {
|
||||||
|
* return (
|
||||||
|
* <Provider store={store}>
|
||||||
|
* {element}
|
||||||
|
* </Provider>
|
||||||
|
* )
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
exports.wrapRootElement = true
|
|
@ -0,0 +1,72 @@
|
||||||
|
import React from "react"
|
||||||
|
import ReactDOM from "react-dom"
|
||||||
|
import domReady from "@mikaelkristiansson/domready"
|
||||||
|
|
||||||
|
import socketIo from "./socketIo"
|
||||||
|
import emitter from "./emitter"
|
||||||
|
import { apiRunner, apiRunnerAsync } from "./api-runner-browser"
|
||||||
|
import { setLoader, publicLoader } from "./loader"
|
||||||
|
import DevLoader from "./dev-loader"
|
||||||
|
import syncRequires from "./sync-requires"
|
||||||
|
// Generated during bootstrap
|
||||||
|
import matchPaths from "./match-paths.json"
|
||||||
|
|
||||||
|
window.___emitter = emitter
|
||||||
|
|
||||||
|
const loader = new DevLoader(syncRequires, matchPaths)
|
||||||
|
setLoader(loader)
|
||||||
|
loader.setApiRunner(apiRunner)
|
||||||
|
|
||||||
|
window.___loader = publicLoader
|
||||||
|
|
||||||
|
// Let the site/plugins run code very early.
|
||||||
|
apiRunnerAsync(`onClientEntry`).then(() => {
|
||||||
|
// Hook up the client to socket.io on server
|
||||||
|
const socket = socketIo()
|
||||||
|
if (socket) {
|
||||||
|
socket.on(`reload`, () => {
|
||||||
|
window.location.reload()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service Workers are persistent by nature. They stick around,
|
||||||
|
* serving a cached version of the site if they aren't removed.
|
||||||
|
* This is especially frustrating when you need to test the
|
||||||
|
* production build on your local machine.
|
||||||
|
*
|
||||||
|
* Let's warn if we find service workers in development.
|
||||||
|
*/
|
||||||
|
if (`serviceWorker` in navigator) {
|
||||||
|
navigator.serviceWorker.getRegistrations().then(registrations => {
|
||||||
|
if (registrations.length > 0)
|
||||||
|
console.warn(
|
||||||
|
`Warning: found one or more service workers present.`,
|
||||||
|
`If your site isn't behaving as expected, you might want to remove these.`,
|
||||||
|
registrations
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const rootElement = document.getElementById(`___gatsby`)
|
||||||
|
|
||||||
|
const renderer = apiRunner(
|
||||||
|
`replaceHydrateFunction`,
|
||||||
|
undefined,
|
||||||
|
ReactDOM.render
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
Promise.all([
|
||||||
|
loader.loadPage(`/dev-404-page/`),
|
||||||
|
loader.loadPage(`/404.html`),
|
||||||
|
loader.loadPage(window.location.pathname),
|
||||||
|
]).then(() => {
|
||||||
|
const preferDefault = m => (m && m.default) || m
|
||||||
|
let Root = preferDefault(require(`./root`))
|
||||||
|
domReady(() => {
|
||||||
|
renderer(<Root />, rootElement, () => {
|
||||||
|
apiRunner(`onInitialClientRender`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,13 @@
|
||||||
|
// prefer default export if available
|
||||||
|
const preferDefault = m => m && m.default || m
|
||||||
|
|
||||||
|
exports.components = {
|
||||||
|
"component---node-modules-antv-gatsby-theme-antv-site-templates-document-tsx": () => import("../node_modules/@antv/gatsby-theme-antv/site/templates/document.tsx" /* webpackChunkName: "component---node-modules-antv-gatsby-theme-antv-site-templates-document-tsx" */),
|
||||||
|
"component---node-modules-antv-gatsby-theme-antv-site-templates-example-tsx": () => import("../node_modules/@antv/gatsby-theme-antv/site/templates/example.tsx" /* webpackChunkName: "component---node-modules-antv-gatsby-theme-antv-site-templates-example-tsx" */),
|
||||||
|
"component---cache-dev-404-page-js": () => import("dev-404-page.js" /* webpackChunkName: "component---cache-dev-404-page-js" */),
|
||||||
|
"component---node-modules-antv-gatsby-theme-antv-site-pages-index-tsx": () => import("../node_modules/@antv/gatsby-theme-antv/site/pages/index.tsx" /* webpackChunkName: "component---node-modules-antv-gatsby-theme-antv-site-pages-index-tsx" */),
|
||||||
|
"component---node-modules-antv-gatsby-theme-antv-site-pages-404-tsx": () => import("../node_modules/@antv/gatsby-theme-antv/site/pages/404.tsx" /* webpackChunkName: "component---node-modules-antv-gatsby-theme-antv-site-pages-404-tsx" */),
|
||||||
|
"component---site-pages-index-en-ts": () => import("../site/pages/index.en.ts" /* webpackChunkName: "component---site-pages-index-en-ts" */),
|
||||||
|
"component---site-pages-index-zh-ts": () => import("../site/pages/index.zh.ts" /* webpackChunkName: "component---site-pages-index-zh-ts" */)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
{
|
||||||
|
"stages": {
|
||||||
|
"develop": {
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "babel-plugin-import",
|
||||||
|
"options": {
|
||||||
|
"libraryName": "antd",
|
||||||
|
"style": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"presets": [
|
||||||
|
{
|
||||||
|
"name": "/Users/lizhengxue/Documents/AntV/github/L7_2.0/L7/node_modules/gatsby-plugin-typescript/node_modules/@babel/preset-typescript/lib/index.js",
|
||||||
|
"options": {
|
||||||
|
"plugins": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"options": {
|
||||||
|
"cacheDirectory": true,
|
||||||
|
"sourceType": "unambiguous"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"develop-html": {
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "babel-plugin-import",
|
||||||
|
"options": {
|
||||||
|
"libraryName": "antd",
|
||||||
|
"style": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"presets": [
|
||||||
|
{
|
||||||
|
"name": "/Users/lizhengxue/Documents/AntV/github/L7_2.0/L7/node_modules/gatsby-plugin-typescript/node_modules/@babel/preset-typescript/lib/index.js",
|
||||||
|
"options": {
|
||||||
|
"plugins": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"options": {
|
||||||
|
"cacheDirectory": true,
|
||||||
|
"sourceType": "unambiguous"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"build-html": {
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "babel-plugin-import",
|
||||||
|
"options": {
|
||||||
|
"libraryName": "antd",
|
||||||
|
"style": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"presets": [
|
||||||
|
{
|
||||||
|
"name": "/Users/lizhengxue/Documents/AntV/github/L7_2.0/L7/node_modules/gatsby-plugin-typescript/node_modules/@babel/preset-typescript/lib/index.js",
|
||||||
|
"options": {
|
||||||
|
"plugins": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"options": {
|
||||||
|
"cacheDirectory": true,
|
||||||
|
"sourceType": "unambiguous"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"build-javascript": {
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "babel-plugin-import",
|
||||||
|
"options": {
|
||||||
|
"libraryName": "antd",
|
||||||
|
"style": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"presets": [
|
||||||
|
{
|
||||||
|
"name": "/Users/lizhengxue/Documents/AntV/github/L7_2.0/L7/node_modules/gatsby-plugin-typescript/node_modules/@babel/preset-typescript/lib/index.js",
|
||||||
|
"options": {
|
||||||
|
"plugins": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"options": {
|
||||||
|
"cacheDirectory": true,
|
||||||
|
"sourceType": "unambiguous"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
">0.25%",
|
||||||
|
"not dead"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
{"expireTime":9007200827713851000,"key":"transformer-remark-markdown-ast-99b5b2f90b0432fb92044e231041ffca-gatsby-remark-prettiergatsby-remark-prismjsgatsby-remark-external-linksgatsby-remark-autolink-headersgatsby-remark-reading-time-","val":{"type":"root","children":[],"position":{"start":{"line":1,"column":1,"offset":0},"end":{"line":1,"column":1,"offset":0}}}}
|
|
@ -0,0 +1 @@
|
||||||
|
{"expireTime":9007200827713852000,"key":"transformer-remark-markdown-html-c9cb5e0c306ae6f6211ba629c1a3fc15-gatsby-remark-prettiergatsby-remark-prismjsgatsby-remark-external-linksgatsby-remark-autolink-headersgatsby-remark-reading-time-","val":"<p>初始 L7 地图实例</p>\n<h3 id=\"何时使用\"><a href=\"#%E4%BD%95%E6%97%B6%E4%BD%BF%E7%94%A8\" aria-label=\"何时使用 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>何时使用</h3>"}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
||||||
|
{"expireTime":9007200827750866000,"key":"transformer-remark-markdown-html-99b5b2f90b0432fb92044e231041ffca-gatsby-remark-prettiergatsby-remark-prismjsgatsby-remark-external-linksgatsby-remark-autolink-headersgatsby-remark-reading-time-","val":""}
|
|
@ -0,0 +1 @@
|
||||||
|
{"expireTime":9007200827713851000,"key":"transformer-remark-markdown-html-ast-57531815410aa78dc10e42270cb201dd-gatsby-remark-prettiergatsby-remark-prismjsgatsby-remark-external-linksgatsby-remark-autolink-headersgatsby-remark-reading-time-","val":{"type":"root","children":[{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"L7 地理空间可视化设计语言","position":{"start":{"line":1,"column":1,"offset":0},"end":{"line":1,"column":15,"offset":14}}}],"position":{"start":{"line":1,"column":1,"offset":0},"end":{"line":1,"column":15,"offset":14}}}],"position":{"start":{"line":1,"column":1,"offset":0},"end":{"line":2,"column":1,"offset":15}}}}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
||||||
|
{"expireTime":9007200827713851000,"key":"transformer-remark-markdown-html-51dbb367647851670b43ae45a9e937df-gatsby-remark-prettiergatsby-remark-prismjsgatsby-remark-external-linksgatsby-remark-autolink-headersgatsby-remark-reading-time-","val":"<p>内容</p>"}
|
|
@ -0,0 +1 @@
|
||||||
|
{"expireTime":9007200827713851000,"key":"transformer-remark-markdown-html-ast-51dbb367647851670b43ae45a9e937df-gatsby-remark-prettiergatsby-remark-prismjsgatsby-remark-external-linksgatsby-remark-autolink-headersgatsby-remark-reading-time-","val":{"type":"root","children":[{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"内容","position":{"start":{"line":2,"column":1,"offset":1},"end":{"line":2,"column":3,"offset":3}}}],"position":{"start":{"line":2,"column":1,"offset":1},"end":{"line":2,"column":3,"offset":3}}}],"position":{"start":{"line":1,"column":1,"offset":0},"end":{"line":3,"column":1,"offset":4}}}}
|
|
@ -0,0 +1 @@
|
||||||
|
{"expireTime":9007200827713851000,"key":"transformer-remark-markdown-ast-57531815410aa78dc10e42270cb201dd-gatsby-remark-prettiergatsby-remark-prismjsgatsby-remark-external-linksgatsby-remark-autolink-headersgatsby-remark-reading-time-","val":{"type":"root","children":[{"type":"paragraph","children":[{"type":"text","value":"L7 地理空间可视化设计语言","position":{"start":{"line":1,"column":1,"offset":0},"end":{"line":1,"column":15,"offset":14},"indent":[]}}],"position":{"start":{"line":1,"column":1,"offset":0},"end":{"line":1,"column":15,"offset":14},"indent":[]}}],"position":{"start":{"line":1,"column":1,"offset":0},"end":{"line":2,"column":1,"offset":15}}}}
|
|
@ -0,0 +1 @@
|
||||||
|
{"expireTime":9007200827713852000,"key":"transformer-remark-markdown-toc-d1a5a1e053cc8aaf5e0018566456ceec-gatsby-remark-prettiergatsby-remark-prismjsgatsby-remark-external-linksgatsby-remark-autolink-headersgatsby-remark-reading-time-{\"heading\":null,\"maxDepth\":6,\"pathToSlugField\":\"fields.slug\"}-","val":"<ul>\n<li>\n<p><a href=\"/zh/docs/API/component/marker/#%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0\">构造函数</a></p>\n<ul>\n<li>\n<ul>\n<li><a href=\"/zh/docs/API/component/marker/#option\">option</a></li>\n</ul>\n</li>\n</ul>\n</li>\n<li>\n<p><a href=\"/zh/docs/API/component/marker/#%E6%96%B9%E6%B3%95\">方法</a></p>\n<ul>\n<li>\n<ul>\n<li><a href=\"/zh/docs/API/component/marker/#setlnglat\">setLnglat</a></li>\n<li><a href=\"/zh/docs/API/component/marker/#addto\">addTo</a></li>\n<li><a href=\"/zh/docs/API/component/marker/#remove\">remove</a></li>\n<li><a href=\"/zh/docs/API/component/marker/#getelement\">getElement</a></li>\n<li><a href=\"/zh/docs/API/component/marker/#getlnglat\">getLngLat</a></li>\n<li><a href=\"/zh/docs/API/component/marker/#togglepopup\">togglePopup</a></li>\n<li><a href=\"/zh/docs/API/component/marker/#setpopup\">setPopup</a></li>\n<li><a href=\"/zh/docs/API/component/marker/#getpopup\">getPopup</a></li>\n</ul>\n</li>\n</ul>\n</li>\n<li>\n<p><a href=\"/zh/docs/API/component/marker/#%E7%A4%BA%E4%BE%8B%E4%BB%A3%E7%A0%81\">示例代码</a></p>\n<ul>\n<li>\n<ul>\n<li><a href=\"/zh/docs/API/component/marker/#%E9%BB%98%E8%AE%A4marker\">默认Marker</a></li>\n<li><a href=\"/zh/docs/API/component/marker/#%E8%87%AA%E5%AE%9A%E4%B9%89marker\">自定义Marker</a></li>\n<li><a href=\"/zh/docs/API/component/marker/#%E8%AE%BE%E7%BD%AE-popup\">设置 popup</a></li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>"}
|
|
@ -0,0 +1 @@
|
||||||
|
{"expireTime":9007200827713851000,"key":"transformer-remark-markdown-html-ast-99b5b2f90b0432fb92044e231041ffca-gatsby-remark-prettiergatsby-remark-prismjsgatsby-remark-external-linksgatsby-remark-autolink-headersgatsby-remark-reading-time-","val":{"type":"root","children":[],"position":{"start":{"line":1,"column":1,"offset":0},"end":{"line":1,"column":1,"offset":0}}}}
|
|
@ -0,0 +1 @@
|
||||||
|
{"expireTime":9007200827713852000,"key":"transformer-remark-markdown-toc-4e775806f930bb554f175748236303d7-gatsby-remark-prettiergatsby-remark-prismjsgatsby-remark-external-linksgatsby-remark-autolink-headersgatsby-remark-reading-time-{\"heading\":null,\"maxDepth\":6,\"pathToSlugField\":\"fields.slug\"}-","val":"<ul>\n<li>\n<p><a href=\"/zh/docs/API/component/control/#control\">control</a></p>\n<ul>\n<li>\n<p><a href=\"/zh/docs/API/component/control/#%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0\">构造函数</a></p>\n<ul>\n<li>\n<ul>\n<li><a href=\"/zh/docs/API/component/control/#option\">option</a></li>\n<li><a href=\"/zh/docs/API/component/control/#scene-%E5%86%85%E7%BD%AE%E5%9C%B0%E5%9B%BE%E7%BB%84%E4%BB%B6\">scene 内置地图组件</a></li>\n<li><a href=\"/zh/docs/API/component/control/#zoom\">Zoom</a></li>\n<li><a href=\"/zh/docs/API/component/control/#scale\">Scale</a></li>\n<li><a href=\"/zh/docs/API/component/control/#attribution\">attribution</a></li>\n<li><a href=\"/zh/docs/API/component/control/#layer\">layer</a></li>\n</ul>\n</li>\n</ul>\n</li>\n<li>\n<p><a href=\"/zh/docs/API/component/control/#%E6%96%B9%E6%B3%95\">方法</a></p>\n<ul>\n<li>\n<ul>\n<li><a href=\"/zh/docs/API/component/control/#onadd\">onAdd</a></li>\n<li><a href=\"/zh/docs/API/component/control/#addto\">addTo</a></li>\n<li><a href=\"/zh/docs/API/component/control/#setposition\">setPosition</a></li>\n<li><a href=\"/zh/docs/API/component/control/#remove\">remove</a></li>\n</ul>\n</li>\n</ul>\n</li>\n<li>\n<p><a href=\"/zh/docs/API/component/control/#%E7%A4%BA%E4%BE%8B%E4%BB%A3%E7%A0%81\">示例代码</a></p>\n<ul>\n<li>\n<ul>\n<li><a href=\"/zh/docs/API/component/control/#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%9B%BE%E4%BE%8B%E6%8E%A7%E4%BB%B6\">自定义图例控件</a></li>\n</ul>\n</li>\n</ul>\n</li>\n<li><a href=\"/zh/docs/API/component/control/#faq\">FAQ</a></li>\n</ul>\n</li>\n</ul>"}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
||||||
|
{"expireTime":9007200827713851000,"key":"transformer-remark-markdown-toc-b1a6eb4a5fb92e03f562537f31f11c68-gatsby-remark-prettiergatsby-remark-prismjsgatsby-remark-external-linksgatsby-remark-autolink-headersgatsby-remark-reading-time-{\"heading\":null,\"maxDepth\":6,\"pathToSlugField\":\"fields.slug\"}-","val":"<ul>\n<li><a href=\"/zh/docs/manual/tutorial/data/#%E6%95%B0%E6%8D%AE\">数据</a></li>\n<li><a href=\"/zh/docs/manual/tutorial/data/#geojson\">GeoJSON</a></li>\n<li><a href=\"/zh/docs/manual/tutorial/data/#%E5%9C%B0%E7%90%86%E7%BB%9F%E8%AE%A1%E5%88%86%E6%9E%90%E5%B7%A5%E5%85%B7\">地理统计分析工具</a></li>\n<li><a href=\"/zh/docs/manual/tutorial/data/#%E5%9C%A8%E7%BA%BF%E5%B7%A5%E5%85%B7\">在线工具</a></li>\n</ul>"}
|
|
@ -0,0 +1 @@
|
||||||
|
{"expireTime":9007200827720708000,"key":"transformer-remark-markdown-toc-99b5b2f90b0432fb92044e231041ffca-gatsby-remark-prettiergatsby-remark-prismjsgatsby-remark-external-linksgatsby-remark-autolink-headersgatsby-remark-reading-time-{\"heading\":null,\"maxDepth\":6,\"pathToSlugField\":\"fields.slug\"}-","val":""}
|
|
@ -0,0 +1 @@
|
||||||
|
{"expireTime":9007200827713852000,"key":"transformer-remark-markdown-toc-6c75c6b34b379d2a97680d76e3983681-gatsby-remark-prettiergatsby-remark-prismjsgatsby-remark-external-linksgatsby-remark-autolink-headersgatsby-remark-reading-time-{\"heading\":null,\"maxDepth\":6,\"pathToSlugField\":\"fields.slug\"}-","val":"<ul>\n<li>\n<p><a href=\"/zh/docs/API/Scene/#%E7%AE%80%E4%BB%8B\">简介</a></p>\n<ul>\n<li>\n<p><a href=\"/zh/docs/API/Scene/#%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0\">构造函数</a></p>\n<ul>\n<li><a href=\"/zh/docs/API/Scene/#%E7%8B%AC%E7%AB%8B%E5%AE%9E%E4%BE%8B%E5%8C%96-scene\">独立实例化 Scene</a></li>\n<li><a href=\"/zh/docs/API/Scene/#%E6%A0%B9%E6%8D%AEmap-%E5%AE%9E%E4%BE%8B%E5%88%9B%E5%BB%BAsence\">根据map 实例创建Sence</a></li>\n</ul>\n</li>\n</ul>\n</li>\n<li><a href=\"/zh/docs/API/Scene/#map\">map</a></li>\n<li>\n<p><a href=\"/zh/docs/API/Scene/#%E6%9E%84%E9%80%A0%E7%B1%BB\">构造类</a></p>\n<ul>\n<li><a href=\"/zh/docs/API/Scene/#pointlayer\">PointLayer</a></li>\n<li><a href=\"/zh/docs/API/Scene/#polylinelayer\">PolylineLayer</a></li>\n<li><a href=\"/zh/docs/API/Scene/#polygonlayer\">PolygonLayer</a></li>\n<li><a href=\"/zh/docs/API/Scene/#imagelayer\">ImageLayer</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"/zh/docs/API/Scene/#%E9%85%8D%E7%BD%AE%E9%A1%B9\">配置项</a></p>\n<ul>\n<li><a href=\"/zh/docs/API/Scene/#id\">id</a></li>\n<li><a href=\"/zh/docs/API/Scene/#zoom\">zoom</a></li>\n<li><a href=\"/zh/docs/API/Scene/#center\">center</a></li>\n<li><a href=\"/zh/docs/API/Scene/#pitch\">pitch</a></li>\n<li><a href=\"/zh/docs/API/Scene/#mapsyle\">mapSyle</a></li>\n<li><a href=\"/zh/docs/API/Scene/#minzoom\">minZoom</a></li>\n<li><a href=\"/zh/docs/API/Scene/#maxzoom\">maxZoom</a></li>\n<li><a href=\"/zh/docs/API/Scene/#rotateenable\">rotateEnable</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"/zh/docs/API/Scene/#%E6%96%B9%E6%B3%95\">方法</a></p>\n<ul>\n<li><a href=\"/zh/docs/API/Scene/#getzoom\">getZoom</a></li>\n<li><a href=\"/zh/docs/API/Scene/#getlayers\">getLayers()</a></li>\n<li><a href=\"/zh/docs/API/Scene/#getcenter\">getCenter()</a></li>\n<li><a href=\"/zh/docs/API/Scene/#getsize\">getSize()</a></li>\n<li><a href=\"/zh/docs/API/Scene/#getpitch\">getPitch()</a></li>\n<li><a href=\"/zh/docs/API/Scene/#setcenter\">setCenter()</a></li>\n<li><a href=\"/zh/docs/API/Scene/#setzoomandcenter\">setZoomAndCenter</a></li>\n<li><a href=\"/zh/docs/API/Scene/#setrotation\">setRotation</a></li>\n<li><a href=\"/zh/docs/API/Scene/#zoomin\">zoomIn</a></li>\n<li><a href=\"/zh/docs/API/Scene/#zoomout\">zoomOut</a></li>\n<li><a href=\"/zh/docs/API/Scene/#panto\">panTo</a></li>\n<li><a href=\"/zh/docs/API/Scene/#panby\">panBy</a></li>\n<li><a href=\"/zh/docs/API/Scene/#setpitch\">setPitch</a></li>\n<li><a href=\"/zh/docs/API/Scene/#setstatus\">setStatus</a></li>\n<li><a href=\"/zh/docs/API/Scene/#fitbounds\">fitBounds</a></li>\n<li><a href=\"/zh/docs/API/Scene/#removelayer\">removeLayer</a></li>\n<li><a href=\"/zh/docs/API/Scene/#getlayers-1\">getLayers</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"/zh/docs/API/Scene/#%E4%BA%8B%E4%BB%B6\">事件</a></p>\n<ul>\n<li>\n<p><a href=\"/zh/docs/API/Scene/#on\">on</a></p>\n<ul>\n<li><a href=\"/zh/docs/API/Scene/#%E5%8F%82%E6%95%B0\">参数</a></li>\n</ul>\n</li>\n<li><a href=\"/zh/docs/API/Scene/#off\">off</a></li>\n<li><a href=\"/zh/docs/API/Scene/#%E5%9C%B0%E5%9B%BE%E4%BA%8B%E4%BB%B6\">地图事件</a></li>\n<li><a href=\"/zh/docs/API/Scene/#%E9%BC%A0%E6%A0%87%E4%BA%8B%E4%BB%B6\">鼠标事件</a></li>\n<li><a href=\"/zh/docs/API/Scene/#%E5%85%B6%E5%AE%83%E4%BA%8B%E4%BB%B6\">其它事件</a></li>\n</ul>\n</li>\n</ul>"}
|
|
@ -0,0 +1 @@
|
||||||
|
{"expireTime":9007200827713851000,"key":"transformer-remark-markdown-html-57531815410aa78dc10e42270cb201dd-gatsby-remark-prettiergatsby-remark-prismjsgatsby-remark-external-linksgatsby-remark-autolink-headersgatsby-remark-reading-time-","val":"<p>L7 地理空间可视化设计语言</p>"}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
||||||
|
{"expireTime":9007200827713852000,"key":"transformer-remark-markdown-toc-a4fdd704fadc6272a50f61c3eb36ad4b-gatsby-remark-prettiergatsby-remark-prismjsgatsby-remark-external-linksgatsby-remark-autolink-headersgatsby-remark-reading-time-{\"heading\":null,\"maxDepth\":6,\"pathToSlugField\":\"fields.slug\"}-","val":"<ul>\n<li>\n<p><a href=\"/zh/docs/API/component/popup/#popup\">popup</a></p>\n<ul>\n<li>\n<p><a href=\"/zh/docs/API/component/popup/#%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0\">构造函数</a></p>\n<ul>\n<li>\n<ul>\n<li><a href=\"/zh/docs/API/component/popup/#option\">option</a></li>\n</ul>\n</li>\n</ul>\n</li>\n<li>\n<p><a href=\"/zh/docs/API/component/popup/#%E6%96%B9%E6%B3%95\">方法</a></p>\n<ul>\n<li>\n<ul>\n<li><a href=\"/zh/docs/API/component/popup/#setlnglat\">setLnglat</a></li>\n<li><a href=\"/zh/docs/API/component/popup/#addto\">addTo</a></li>\n<li><a href=\"/zh/docs/API/component/popup/#sethtml\">setHtml</a></li>\n<li><a href=\"/zh/docs/API/component/popup/#settext\">setText</a></li>\n<li><a href=\"/zh/docs/API/component/popup/#remove\">remove</a></li>\n</ul>\n</li>\n</ul>\n</li>\n<li>\n<p><a href=\"/zh/docs/API/component/popup/#%E4%BA%8B%E4%BB%B6\">事件</a></p>\n<ul>\n<li>\n<ul>\n<li><a href=\"/zh/docs/API/component/popup/#close\">close</a></li>\n</ul>\n</li>\n</ul>\n</li>\n<li>\n<p><a href=\"/zh/docs/API/component/popup/#%E7%A4%BA%E4%BE%8B%E4%BB%A3%E7%A0%81\">示例代码</a></p>\n<ul>\n<li>\n<ul>\n<li><a href=\"/zh/docs/API/component/popup/#%E6%B7%BB%E5%8A%A0popup\">添加popup</a></li>\n</ul>\n</li>\n<li><a href=\"/zh/docs/API/component/popup/#faq\">FAQ</a></li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>"}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
||||||
|
{"expireTime":9007200827713851000,"key":"transformer-remark-markdown-ast-51dbb367647851670b43ae45a9e937df-gatsby-remark-prettiergatsby-remark-prismjsgatsby-remark-external-linksgatsby-remark-autolink-headersgatsby-remark-reading-time-","val":{"type":"root","children":[{"type":"paragraph","children":[{"type":"text","value":"内容","position":{"start":{"line":2,"column":1,"offset":1},"end":{"line":2,"column":3,"offset":3},"indent":[]}}],"position":{"start":{"line":2,"column":1,"offset":1},"end":{"line":2,"column":3,"offset":3},"indent":[]}}],"position":{"start":{"line":1,"column":1,"offset":0},"end":{"line":3,"column":1,"offset":4}}}}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
||||||
|
{"expireTime":9007200827720708000,"key":"transformer-remark-markdown-toc-57531815410aa78dc10e42270cb201dd-gatsby-remark-prettiergatsby-remark-prismjsgatsby-remark-external-linksgatsby-remark-autolink-headersgatsby-remark-reading-time-{\"heading\":null,\"maxDepth\":6,\"pathToSlugField\":\"fields.slug\"}-","val":""}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
||||||
|
{"expireTime":9007200827713852000,"key":"transformer-remark-markdown-html-ast-c9cb5e0c306ae6f6211ba629c1a3fc15-gatsby-remark-prettiergatsby-remark-prismjsgatsby-remark-external-linksgatsby-remark-autolink-headersgatsby-remark-reading-time-","val":{"type":"root","children":[{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"初始 L7 地图实例","position":{"start":{"line":1,"column":1,"offset":0},"end":{"line":1,"column":11,"offset":10}}}],"position":{"start":{"line":1,"column":1,"offset":0},"end":{"line":1,"column":11,"offset":10}}},{"type":"text","value":"\n"},{"type":"element","tagName":"h3","properties":{"id":"何时使用"},"children":[{"type":"element","tagName":"a","properties":{"href":"#%E4%BD%95%E6%97%B6%E4%BD%BF%E7%94%A8","aria-label":"何时使用 permalink","class":"anchor"},"children":[{"type":"raw","value":"<svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg>"}]},{"type":"text","value":"何时使用","position":{"start":{"line":3,"column":5,"offset":16},"end":{"line":3,"column":9,"offset":20}}}],"position":{"start":{"line":3,"column":1,"offset":12},"end":{"line":3,"column":9,"offset":20}}}],"position":{"start":{"line":1,"column":1,"offset":0},"end":{"line":5,"column":1,"offset":22}}}}
|
|
@ -0,0 +1 @@
|
||||||
|
{"expireTime":9007200827713852000,"key":"transformer-remark-markdown-html-a2af20f3d9589fe523949960b78be6a8-gatsby-remark-prettiergatsby-remark-prismjsgatsby-remark-external-linksgatsby-remark-autolink-headersgatsby-remark-reading-time-","val":"<p>图表的基本描述。</p>\n<h3 id=\"何时使用\"><a href=\"#%E4%BD%95%E6%97%B6%E4%BD%BF%E7%94%A8\" aria-label=\"何时使用 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>何时使用</h3>"}
|
|
@ -0,0 +1 @@
|
||||||
|
{"expireTime":9007200827713852000,"key":"transformer-remark-markdown-ast-a2af20f3d9589fe523949960b78be6a8-gatsby-remark-prettiergatsby-remark-prismjsgatsby-remark-external-linksgatsby-remark-autolink-headersgatsby-remark-reading-time-","val":{"type":"root","children":[{"type":"paragraph","children":[{"type":"text","value":"图表的基本描述。","position":{"start":{"line":2,"column":1,"offset":1},"end":{"line":2,"column":9,"offset":9},"indent":[]}}],"position":{"start":{"line":2,"column":1,"offset":1},"end":{"line":2,"column":9,"offset":9},"indent":[]}},{"type":"heading","depth":3,"children":[{"type":"link","url":"#何时使用","title":null,"children":[],"data":{"hProperties":{"aria-label":"何时使用 permalink","class":"anchor"},"hChildren":[{"type":"raw","value":"<svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg>"}]}},{"type":"text","value":"何时使用","position":{"start":{"line":4,"column":5,"offset":15},"end":{"line":4,"column":9,"offset":19},"indent":[]}}],"position":{"start":{"line":4,"column":1,"offset":11},"end":{"line":4,"column":9,"offset":19},"indent":[]},"data":{"id":"何时使用","htmlAttributes":{"id":"何时使用"},"hProperties":{"id":"何时使用"}}}],"position":{"start":{"line":1,"column":1,"offset":0},"end":{"line":6,"column":1,"offset":21}}}}
|
|
@ -0,0 +1 @@
|
||||||
|
{"expireTime":9007200827713852000,"key":"transformer-remark-markdown-ast-c9cb5e0c306ae6f6211ba629c1a3fc15-gatsby-remark-prettiergatsby-remark-prismjsgatsby-remark-external-linksgatsby-remark-autolink-headersgatsby-remark-reading-time-","val":{"type":"root","children":[{"type":"paragraph","children":[{"type":"text","value":"初始 L7 地图实例","position":{"start":{"line":1,"column":1,"offset":0},"end":{"line":1,"column":11,"offset":10},"indent":[]}}],"position":{"start":{"line":1,"column":1,"offset":0},"end":{"line":1,"column":11,"offset":10},"indent":[]}},{"type":"heading","depth":3,"children":[{"type":"link","url":"#何时使用","title":null,"children":[],"data":{"hProperties":{"aria-label":"何时使用 permalink","class":"anchor"},"hChildren":[{"type":"raw","value":"<svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg>"}]}},{"type":"text","value":"何时使用","position":{"start":{"line":3,"column":5,"offset":16},"end":{"line":3,"column":9,"offset":20},"indent":[]}}],"position":{"start":{"line":3,"column":1,"offset":12},"end":{"line":3,"column":9,"offset":20},"indent":[]},"data":{"id":"何时使用","htmlAttributes":{"id":"何时使用"},"hProperties":{"id":"何时使用"}}}],"position":{"start":{"line":1,"column":1,"offset":0},"end":{"line":5,"column":1,"offset":22}}}}
|
|
@ -0,0 +1 @@
|
||||||
|
{"expireTime":9007200827713852000,"key":"transformer-remark-markdown-html-ast-a2af20f3d9589fe523949960b78be6a8-gatsby-remark-prettiergatsby-remark-prismjsgatsby-remark-external-linksgatsby-remark-autolink-headersgatsby-remark-reading-time-","val":{"type":"root","children":[{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"图表的基本描述。","position":{"start":{"line":2,"column":1,"offset":1},"end":{"line":2,"column":9,"offset":9}}}],"position":{"start":{"line":2,"column":1,"offset":1},"end":{"line":2,"column":9,"offset":9}}},{"type":"text","value":"\n"},{"type":"element","tagName":"h3","properties":{"id":"何时使用"},"children":[{"type":"element","tagName":"a","properties":{"href":"#%E4%BD%95%E6%97%B6%E4%BD%BF%E7%94%A8","aria-label":"何时使用 permalink","class":"anchor"},"children":[{"type":"raw","value":"<svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg>"}]},{"type":"text","value":"何时使用","position":{"start":{"line":4,"column":5,"offset":15},"end":{"line":4,"column":9,"offset":19}}}],"position":{"start":{"line":4,"column":1,"offset":11},"end":{"line":4,"column":9,"offset":19}}}],"position":{"start":{"line":1,"column":1,"offset":0},"end":{"line":6,"column":1,"offset":21}}}}
|
|
@ -0,0 +1 @@
|
||||||
|
{"expireTime":9007200827720709000,"key":"transformer-remark-markdown-toc-51dbb367647851670b43ae45a9e937df-gatsby-remark-prettiergatsby-remark-prismjsgatsby-remark-external-linksgatsby-remark-autolink-headersgatsby-remark-reading-time-{\"heading\":null,\"maxDepth\":6,\"pathToSlugField\":\"fields.slug\"}-","val":""}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,15 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// During bootstrap, we write requires at top of this file which looks
|
||||||
|
// basically like:
|
||||||
|
// module.exports = [
|
||||||
|
// {
|
||||||
|
// plugin: require("/path/to/plugin1/gatsby-browser.js"),
|
||||||
|
// options: { ... },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// plugin: require("/path/to/plugin2/gatsby-browser.js"),
|
||||||
|
// options: { ... },
|
||||||
|
// },
|
||||||
|
// ]
|
||||||
|
module.exports = [];
|
|
@ -0,0 +1,61 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const plugins = require(`./api-runner-browser-plugins`);
|
||||||
|
|
||||||
|
const {
|
||||||
|
getResourcesForPathname,
|
||||||
|
getResourcesForPathnameSync,
|
||||||
|
getResourceURLsForPathname,
|
||||||
|
loadPage,
|
||||||
|
loadPageSync
|
||||||
|
} = require(`./loader`).publicLoader;
|
||||||
|
|
||||||
|
exports.apiRunner = (api, args = {}, defaultReturn, argTransform) => {
|
||||||
|
// Hooks for gatsby-cypress's API handler
|
||||||
|
if (process.env.CYPRESS_SUPPORT) {
|
||||||
|
if (window.___apiHandler) {
|
||||||
|
window.___apiHandler(api);
|
||||||
|
} else if (window.___resolvedAPIs) {
|
||||||
|
window.___resolvedAPIs.push(api);
|
||||||
|
} else {
|
||||||
|
window.___resolvedAPIs = [api];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let results = plugins.map(plugin => {
|
||||||
|
if (!plugin.plugin[api]) {
|
||||||
|
return undefined;
|
||||||
|
} // Deprecated April 2019. Use `loadPageSync` instead
|
||||||
|
|
||||||
|
|
||||||
|
args.getResourcesForPathnameSync = getResourcesForPathnameSync; // Deprecated April 2019. Use `loadPage` instead
|
||||||
|
|
||||||
|
args.getResourcesForPathname = getResourcesForPathname;
|
||||||
|
args.getResourceURLsForPathname = getResourceURLsForPathname;
|
||||||
|
args.loadPage = loadPage;
|
||||||
|
args.loadPageSync = loadPageSync;
|
||||||
|
const result = plugin.plugin[api](args, plugin.options);
|
||||||
|
|
||||||
|
if (result && argTransform) {
|
||||||
|
args = argTransform({
|
||||||
|
args,
|
||||||
|
result,
|
||||||
|
plugin
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}); // Filter out undefined results.
|
||||||
|
|
||||||
|
results = results.filter(result => typeof result !== `undefined`);
|
||||||
|
|
||||||
|
if (results.length > 0) {
|
||||||
|
return results;
|
||||||
|
} else if (defaultReturn) {
|
||||||
|
return [defaultReturn];
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.apiRunnerAsync = (api, args, defaultReturn) => plugins.reduce((previous, next) => next.plugin[api] ? previous.then(() => next.plugin[api](args, next.options)) : previous, Promise.resolve());
|
|
@ -0,0 +1,48 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// During bootstrap, we write requires at top of this file which looks like:
|
||||||
|
// var plugins = [
|
||||||
|
// {
|
||||||
|
// plugin: require("/path/to/plugin1/gatsby-ssr.js"),
|
||||||
|
// options: { ... },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// plugin: require("/path/to/plugin2/gatsby-ssr.js"),
|
||||||
|
// options: { ... },
|
||||||
|
// },
|
||||||
|
// ]
|
||||||
|
const apis = require(`./api-ssr-docs`); // Run the specified API in any plugins that have implemented it
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = (api, args, defaultReturn, argTransform) => {
|
||||||
|
if (!apis[api]) {
|
||||||
|
console.log(`This API doesn't exist`, api);
|
||||||
|
} // Run each plugin in series.
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
|
||||||
|
|
||||||
|
let results = plugins.map(plugin => {
|
||||||
|
if (!plugin.plugin[api]) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = plugin.plugin[api](args, plugin.options);
|
||||||
|
|
||||||
|
if (result && argTransform) {
|
||||||
|
args = argTransform({
|
||||||
|
args,
|
||||||
|
result
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}); // Filter out undefined results.
|
||||||
|
|
||||||
|
results = results.filter(result => typeof result !== `undefined`);
|
||||||
|
|
||||||
|
if (results.length > 0) {
|
||||||
|
return results;
|
||||||
|
} else {
|
||||||
|
return [defaultReturn];
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,200 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object containing options defined in `gatsby-config.js`
|
||||||
|
* @typedef {object} pluginOptions
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace the default server renderer. This is useful for integration with
|
||||||
|
* Redux, css-in-js libraries, etc. that need custom setups for server
|
||||||
|
* rendering.
|
||||||
|
* @param {object} $0
|
||||||
|
* @param {string} $0.pathname The pathname of the page currently being rendered.
|
||||||
|
* @param {function} $0.replaceBodyHTMLString Call this with the HTML string
|
||||||
|
* you render. **WARNING** if multiple plugins implement this API it's the
|
||||||
|
* last plugin that "wins". TODO implement an automated warning against this.
|
||||||
|
* @param {function} $0.setHeadComponents Takes an array of components as its
|
||||||
|
* first argument which are added to the `headComponents` array which is passed
|
||||||
|
* to the `html.js` component.
|
||||||
|
* @param {function} $0.setHtmlAttributes Takes an object of props which will
|
||||||
|
* spread into the `<html>` component.
|
||||||
|
* @param {function} $0.setBodyAttributes Takes an object of props which will
|
||||||
|
* spread into the `<body>` component.
|
||||||
|
* @param {function} $0.setPreBodyComponents Takes an array of components as its
|
||||||
|
* first argument which are added to the `preBodyComponents` array which is passed
|
||||||
|
* to the `html.js` component.
|
||||||
|
* @param {function} $0.setPostBodyComponents Takes an array of components as its
|
||||||
|
* first argument which are added to the `postBodyComponents` array which is passed
|
||||||
|
* to the `html.js` component.
|
||||||
|
* @param {function} $0.setBodyProps Takes an object of data which
|
||||||
|
* is merged with other body props and passed to `html.js` as `bodyProps`.
|
||||||
|
* @param {pluginOptions} pluginOptions
|
||||||
|
* @example
|
||||||
|
* // From gatsby-plugin-glamor
|
||||||
|
* const { renderToString } = require("react-dom/server")
|
||||||
|
* const inline = require("glamor-inline")
|
||||||
|
*
|
||||||
|
* exports.replaceRenderer = ({ bodyComponent, replaceBodyHTMLString }) => {
|
||||||
|
* const bodyHTML = renderToString(bodyComponent)
|
||||||
|
* const inlinedHTML = inline(bodyHTML)
|
||||||
|
*
|
||||||
|
* replaceBodyHTMLString(inlinedHTML)
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
exports.replaceRenderer = true;
|
||||||
|
/**
|
||||||
|
* Called after every page Gatsby server renders while building HTML so you can
|
||||||
|
* set head and body components to be rendered in your `html.js`.
|
||||||
|
*
|
||||||
|
* Gatsby does a two-pass render for HTML. It loops through your pages first
|
||||||
|
* rendering only the body and then takes the result body HTML string and
|
||||||
|
* passes it as the `body` prop to your `html.js` to complete the render.
|
||||||
|
*
|
||||||
|
* It's often handy to be able to send custom components to your `html.js`.
|
||||||
|
* For example, it's a very common pattern for React.js libraries that
|
||||||
|
* support server rendering to pull out data generated during the render to
|
||||||
|
* add to your HTML.
|
||||||
|
*
|
||||||
|
* Using this API over [`replaceRenderer`](#replaceRenderer) is preferable as
|
||||||
|
* multiple plugins can implement this API where only one plugin can take
|
||||||
|
* over server rendering. However, if your plugin requires taking over server
|
||||||
|
* rendering then that's the one to
|
||||||
|
* use
|
||||||
|
* @param {object} $0
|
||||||
|
* @param {string} $0.pathname The pathname of the page currently being rendered.
|
||||||
|
* @param {function} $0.setHeadComponents Takes an array of components as its
|
||||||
|
* first argument which are added to the `headComponents` array which is passed
|
||||||
|
* to the `html.js` component.
|
||||||
|
* @param {function} $0.setHtmlAttributes Takes an object of props which will
|
||||||
|
* spread into the `<html>` component.
|
||||||
|
* @param {function} $0.setBodyAttributes Takes an object of props which will
|
||||||
|
* spread into the `<body>` component.
|
||||||
|
* @param {function} $0.setPreBodyComponents Takes an array of components as its
|
||||||
|
* first argument which are added to the `preBodyComponents` array which is passed
|
||||||
|
* to the `html.js` component.
|
||||||
|
* @param {function} $0.setPostBodyComponents Takes an array of components as its
|
||||||
|
* first argument which are added to the `postBodyComponents` array which is passed
|
||||||
|
* to the `html.js` component.
|
||||||
|
* @param {function} $0.setBodyProps Takes an object of data which
|
||||||
|
* is merged with other body props and passed to `html.js` as `bodyProps`.
|
||||||
|
* @param {pluginOptions} pluginOptions
|
||||||
|
* @example
|
||||||
|
* const { Helmet } = require("react-helmet")
|
||||||
|
*
|
||||||
|
* exports.onRenderBody = (
|
||||||
|
* { setHeadComponents, setHtmlAttributes, setBodyAttributes },
|
||||||
|
* pluginOptions
|
||||||
|
* ) => {
|
||||||
|
* const helmet = Helmet.renderStatic()
|
||||||
|
* setHtmlAttributes(helmet.htmlAttributes.toComponent())
|
||||||
|
* setBodyAttributes(helmet.bodyAttributes.toComponent())
|
||||||
|
* setHeadComponents([
|
||||||
|
* helmet.title.toComponent(),
|
||||||
|
* helmet.link.toComponent(),
|
||||||
|
* helmet.meta.toComponent(),
|
||||||
|
* helmet.noscript.toComponent(),
|
||||||
|
* helmet.script.toComponent(),
|
||||||
|
* helmet.style.toComponent(),
|
||||||
|
* ])
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
|
exports.onRenderBody = true;
|
||||||
|
/**
|
||||||
|
* Called after every page Gatsby server renders while building HTML so you can
|
||||||
|
* replace head components to be rendered in your `html.js`. This is useful if
|
||||||
|
* you need to reorder scripts or styles added by other plugins.
|
||||||
|
* @param {object} $0
|
||||||
|
* @param {string} $0.pathname The pathname of the page currently being rendered.
|
||||||
|
* @param {Array<ReactNode>} $0.getHeadComponents Returns the current `headComponents` array.
|
||||||
|
* @param {function} $0.replaceHeadComponents Takes an array of components as its
|
||||||
|
* first argument which replace the `headComponents` array which is passed
|
||||||
|
* to the `html.js` component. **WARNING** if multiple plugins implement this
|
||||||
|
* API it's the last plugin that "wins".
|
||||||
|
* @param {Array<ReactNode>} $0.getPreBodyComponents Returns the current `preBodyComponents` array.
|
||||||
|
* @param {function} $0.replacePreBodyComponents Takes an array of components as its
|
||||||
|
* first argument which replace the `preBodyComponents` array which is passed
|
||||||
|
* to the `html.js` component. **WARNING** if multiple plugins implement this
|
||||||
|
* API it's the last plugin that "wins".
|
||||||
|
* @param {Array<ReactNode>} $0.getPostBodyComponents Returns the current `postBodyComponents` array.
|
||||||
|
* @param {function} $0.replacePostBodyComponents Takes an array of components as its
|
||||||
|
* first argument which replace the `postBodyComponents` array which is passed
|
||||||
|
* to the `html.js` component. **WARNING** if multiple plugins implement this
|
||||||
|
* API it's the last plugin that "wins".
|
||||||
|
* @param {pluginOptions} pluginOptions
|
||||||
|
* @example
|
||||||
|
* // Move Typography.js styles to the top of the head section so they're loaded first.
|
||||||
|
* exports.onPreRenderHTML = ({ getHeadComponents, replaceHeadComponents }) => {
|
||||||
|
* const headComponents = getHeadComponents()
|
||||||
|
* headComponents.sort((x, y) => {
|
||||||
|
* if (x.key === 'TypographyStyle') {
|
||||||
|
* return -1
|
||||||
|
* } else if (y.key === 'TypographyStyle') {
|
||||||
|
* return 1
|
||||||
|
* }
|
||||||
|
* return 0
|
||||||
|
* })
|
||||||
|
* replaceHeadComponents(headComponents)
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
|
exports.onPreRenderHTML = true;
|
||||||
|
/**
|
||||||
|
* Allow a plugin to wrap the page element.
|
||||||
|
*
|
||||||
|
* This is useful for setting wrapper components around pages that won't get
|
||||||
|
* unmounted on page changes. For setting Provider components, use [wrapRootElement](#wrapRootElement).
|
||||||
|
*
|
||||||
|
* _Note:_
|
||||||
|
* There is an equivalent hook in Gatsby's [Browser API](/docs/browser-apis/#wrapPageElement).
|
||||||
|
* It is recommended to use both APIs together.
|
||||||
|
* For example usage, check out [Using i18n](https://github.com/gatsbyjs/gatsby/tree/master/examples/using-i18n).
|
||||||
|
* @param {object} $0
|
||||||
|
* @param {ReactNode} $0.element The "Page" React Element built by Gatsby.
|
||||||
|
* @param {object} $0.props Props object used by page.
|
||||||
|
* @param {pluginOptions} pluginOptions
|
||||||
|
* @returns {ReactNode} Wrapped element
|
||||||
|
* @example
|
||||||
|
* const React = require("react")
|
||||||
|
* const Layout = require("./src/components/layout").default
|
||||||
|
*
|
||||||
|
* exports.wrapPageElement = ({ element, props }) => {
|
||||||
|
* // props provide same data to Layout as Page element will get
|
||||||
|
* // including location, data, etc - you don't need to pass it
|
||||||
|
* return <Layout {...props}>{element}</Layout>
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
|
exports.wrapPageElement = true;
|
||||||
|
/**
|
||||||
|
* Allow a plugin to wrap the root element.
|
||||||
|
*
|
||||||
|
* This is useful to set up any Provider components that will wrap your application.
|
||||||
|
* For setting persistent UI elements around pages use [wrapPageElement](#wrapPageElement).
|
||||||
|
*
|
||||||
|
* _Note:_
|
||||||
|
* There is an equivalent hook in Gatsby's [Browser API](/docs/browser-apis/#wrapRootElement).
|
||||||
|
* It is recommended to use both APIs together.
|
||||||
|
* For example usage, check out [Using redux](https://github.com/gatsbyjs/gatsby/tree/master/examples/using-redux).
|
||||||
|
* @param {object} $0
|
||||||
|
* @param {ReactNode} $0.element The "Root" React Element built by Gatsby.
|
||||||
|
* @param {pluginOptions} pluginOptions
|
||||||
|
* @returns {ReactNode} Wrapped element
|
||||||
|
* @example
|
||||||
|
* const React = require("react")
|
||||||
|
* const { Provider } = require("react-redux")
|
||||||
|
*
|
||||||
|
* const createStore = require("./src/state/createStore")
|
||||||
|
* const store = createStore()
|
||||||
|
*
|
||||||
|
* exports.wrapRootElement = ({ element }) => {
|
||||||
|
* return (
|
||||||
|
* <Provider store={store}>
|
||||||
|
* {element}
|
||||||
|
* </Provider>
|
||||||
|
* )
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
|
exports.wrapRootElement = true;
|
|
@ -0,0 +1,69 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||||
|
|
||||||
|
var _react = _interopRequireDefault(require("react"));
|
||||||
|
|
||||||
|
var _reactDom = _interopRequireDefault(require("react-dom"));
|
||||||
|
|
||||||
|
var _domready = _interopRequireDefault(require("@mikaelkristiansson/domready"));
|
||||||
|
|
||||||
|
var _socketIo = _interopRequireDefault(require("./socketIo"));
|
||||||
|
|
||||||
|
var _emitter = _interopRequireDefault(require("./emitter"));
|
||||||
|
|
||||||
|
var _apiRunnerBrowser = require("./api-runner-browser");
|
||||||
|
|
||||||
|
var _loader = require("./loader");
|
||||||
|
|
||||||
|
var _devLoader = _interopRequireDefault(require("./dev-loader"));
|
||||||
|
|
||||||
|
var _syncRequires = _interopRequireDefault(require("./sync-requires"));
|
||||||
|
|
||||||
|
var _matchPaths = _interopRequireDefault(require("./match-paths.json"));
|
||||||
|
|
||||||
|
// Generated during bootstrap
|
||||||
|
window.___emitter = _emitter.default;
|
||||||
|
const loader = new _devLoader.default(_syncRequires.default, _matchPaths.default);
|
||||||
|
(0, _loader.setLoader)(loader);
|
||||||
|
loader.setApiRunner(_apiRunnerBrowser.apiRunner);
|
||||||
|
window.___loader = _loader.publicLoader; // Let the site/plugins run code very early.
|
||||||
|
|
||||||
|
(0, _apiRunnerBrowser.apiRunnerAsync)(`onClientEntry`).then(() => {
|
||||||
|
// Hook up the client to socket.io on server
|
||||||
|
const socket = (0, _socketIo.default)();
|
||||||
|
|
||||||
|
if (socket) {
|
||||||
|
socket.on(`reload`, () => {
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Service Workers are persistent by nature. They stick around,
|
||||||
|
* serving a cached version of the site if they aren't removed.
|
||||||
|
* This is especially frustrating when you need to test the
|
||||||
|
* production build on your local machine.
|
||||||
|
*
|
||||||
|
* Let's warn if we find service workers in development.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
if (`serviceWorker` in navigator) {
|
||||||
|
navigator.serviceWorker.getRegistrations().then(registrations => {
|
||||||
|
if (registrations.length > 0) console.warn(`Warning: found one or more service workers present.`, `If your site isn't behaving as expected, you might want to remove these.`, registrations);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const rootElement = document.getElementById(`___gatsby`);
|
||||||
|
const renderer = (0, _apiRunnerBrowser.apiRunner)(`replaceHydrateFunction`, undefined, _reactDom.default.render)[0];
|
||||||
|
Promise.all([loader.loadPage(`/dev-404-page/`), loader.loadPage(`/404.html`), loader.loadPage(window.location.pathname)]).then(() => {
|
||||||
|
const preferDefault = m => m && m.default || m;
|
||||||
|
|
||||||
|
let Root = preferDefault(require(`./root`));
|
||||||
|
(0, _domready.default)(() => {
|
||||||
|
renderer(_react.default.createElement(Root, null), rootElement, () => {
|
||||||
|
(0, _apiRunnerBrowser.apiRunner)(`onInitialClientRender`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,11 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||||
|
|
||||||
|
exports.__esModule = true;
|
||||||
|
exports.default = void 0;
|
||||||
|
|
||||||
|
var _react = _interopRequireDefault(require("react"));
|
||||||
|
|
||||||
|
var _default = _react.default.createContext;
|
||||||
|
exports.default = _default;
|
|
@ -0,0 +1,40 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||||
|
|
||||||
|
exports.__esModule = true;
|
||||||
|
exports.default = HTML;
|
||||||
|
|
||||||
|
var _react = _interopRequireDefault(require("react"));
|
||||||
|
|
||||||
|
var _propTypes = _interopRequireDefault(require("prop-types"));
|
||||||
|
|
||||||
|
function HTML(props) {
|
||||||
|
return _react.default.createElement("html", props.htmlAttributes, _react.default.createElement("head", null, _react.default.createElement("meta", {
|
||||||
|
charSet: "utf-8"
|
||||||
|
}), _react.default.createElement("meta", {
|
||||||
|
httpEquiv: "x-ua-compatible",
|
||||||
|
content: "ie=edge"
|
||||||
|
}), _react.default.createElement("meta", {
|
||||||
|
name: "viewport",
|
||||||
|
content: "width=device-width, initial-scale=1, shrink-to-fit=no"
|
||||||
|
}), props.headComponents), _react.default.createElement("body", props.bodyAttributes, props.preBodyComponents, _react.default.createElement("noscript", {
|
||||||
|
key: "noscript",
|
||||||
|
id: "gatsby-noscript"
|
||||||
|
}, "This app works best with JavaScript enabled."), _react.default.createElement("div", {
|
||||||
|
key: `body`,
|
||||||
|
id: "___gatsby",
|
||||||
|
dangerouslySetInnerHTML: {
|
||||||
|
__html: props.body
|
||||||
|
}
|
||||||
|
}), props.postBodyComponents));
|
||||||
|
}
|
||||||
|
|
||||||
|
HTML.propTypes = {
|
||||||
|
htmlAttributes: _propTypes.default.object,
|
||||||
|
headComponents: _propTypes.default.array,
|
||||||
|
bodyAttributes: _propTypes.default.object,
|
||||||
|
preBodyComponents: _propTypes.default.array,
|
||||||
|
body: _propTypes.default.string,
|
||||||
|
postBodyComponents: _propTypes.default.array
|
||||||
|
};
|
|
@ -0,0 +1,41 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
exports.__esModule = true;
|
||||||
|
exports.default = void 0;
|
||||||
|
|
||||||
|
var _loader = require("./loader");
|
||||||
|
|
||||||
|
var _findPath = require("./find-path");
|
||||||
|
|
||||||
|
class DevLoader extends _loader.BaseLoader {
|
||||||
|
constructor(syncRequires, matchPaths) {
|
||||||
|
const loadComponent = chunkName => Promise.resolve(syncRequires.components[chunkName]);
|
||||||
|
|
||||||
|
super(loadComponent, matchPaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadPage(pagePath) {
|
||||||
|
const realPath = (0, _findPath.findPath)(pagePath);
|
||||||
|
return super.loadPage(realPath).then(result => require(`./socketIo`).getPageData(realPath).then(() => result));
|
||||||
|
}
|
||||||
|
|
||||||
|
loadPageDataJson(rawPath) {
|
||||||
|
return super.loadPageDataJson(rawPath).then(data => {
|
||||||
|
// when we can't find a proper 404.html we fallback to dev-404-page
|
||||||
|
// we need to make sure to mark it as not found.
|
||||||
|
if (data.status === `failure`) {
|
||||||
|
return this.loadPageDataJson(`/dev-404-page/`).then(result => Object.assign({}, data, result));
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
doPrefetch(pagePath) {
|
||||||
|
return Promise.resolve(require(`./socketIo`).getPageData(pagePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var _default = DevLoader;
|
||||||
|
exports.default = _default;
|
|
@ -0,0 +1,135 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||||
|
|
||||||
|
exports.__esModule = true;
|
||||||
|
exports.default = void 0;
|
||||||
|
|
||||||
|
var _react = _interopRequireDefault(require("react"));
|
||||||
|
|
||||||
|
var _server = require("react-dom/server");
|
||||||
|
|
||||||
|
var _lodash = require("lodash");
|
||||||
|
|
||||||
|
var _apiRunnerSsr = _interopRequireDefault(require("./api-runner-ssr"));
|
||||||
|
|
||||||
|
// import testRequireError from "./test-require-error"
|
||||||
|
// For some extremely mysterious reason, webpack adds the above module *after*
|
||||||
|
// this module so that when this code runs, testRequireError is undefined.
|
||||||
|
// So in the meantime, we'll just inline it.
|
||||||
|
const testRequireError = (moduleName, err) => {
|
||||||
|
const regex = new RegExp(`Error: Cannot find module\\s.${moduleName}`);
|
||||||
|
const firstLine = err.toString().split(`\n`)[0];
|
||||||
|
return regex.test(firstLine);
|
||||||
|
};
|
||||||
|
|
||||||
|
let Html;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Html = require(`../src/html`);
|
||||||
|
} catch (err) {
|
||||||
|
if (testRequireError(`../src/html`, err)) {
|
||||||
|
Html = require(`./default-html`);
|
||||||
|
} else {
|
||||||
|
console.log(`There was an error requiring "src/html.js"\n\n`, err, `\n\n`);
|
||||||
|
process.exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Html = Html && Html.__esModule ? Html.default : Html;
|
||||||
|
|
||||||
|
var _default = (pagePath, callback) => {
|
||||||
|
let headComponents = [_react.default.createElement("meta", {
|
||||||
|
key: "environment",
|
||||||
|
name: "note",
|
||||||
|
content: "environment=development"
|
||||||
|
})];
|
||||||
|
let htmlAttributes = {};
|
||||||
|
let bodyAttributes = {};
|
||||||
|
let preBodyComponents = [];
|
||||||
|
let postBodyComponents = [];
|
||||||
|
let bodyProps = {};
|
||||||
|
let htmlStr;
|
||||||
|
|
||||||
|
const setHeadComponents = components => {
|
||||||
|
headComponents = headComponents.concat(components);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setHtmlAttributes = attributes => {
|
||||||
|
htmlAttributes = (0, _lodash.merge)(htmlAttributes, attributes);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setBodyAttributes = attributes => {
|
||||||
|
bodyAttributes = (0, _lodash.merge)(bodyAttributes, attributes);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setPreBodyComponents = components => {
|
||||||
|
preBodyComponents = preBodyComponents.concat(components);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setPostBodyComponents = components => {
|
||||||
|
postBodyComponents = postBodyComponents.concat(components);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setBodyProps = props => {
|
||||||
|
bodyProps = (0, _lodash.merge)({}, bodyProps, props);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getHeadComponents = () => headComponents;
|
||||||
|
|
||||||
|
const replaceHeadComponents = components => {
|
||||||
|
headComponents = components;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPreBodyComponents = () => preBodyComponents;
|
||||||
|
|
||||||
|
const replacePreBodyComponents = components => {
|
||||||
|
preBodyComponents = components;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPostBodyComponents = () => postBodyComponents;
|
||||||
|
|
||||||
|
const replacePostBodyComponents = components => {
|
||||||
|
postBodyComponents = components;
|
||||||
|
};
|
||||||
|
|
||||||
|
(0, _apiRunnerSsr.default)(`onRenderBody`, {
|
||||||
|
setHeadComponents,
|
||||||
|
setHtmlAttributes,
|
||||||
|
setBodyAttributes,
|
||||||
|
setPreBodyComponents,
|
||||||
|
setPostBodyComponents,
|
||||||
|
setBodyProps,
|
||||||
|
pathname: pagePath
|
||||||
|
});
|
||||||
|
(0, _apiRunnerSsr.default)(`onPreRenderHTML`, {
|
||||||
|
getHeadComponents,
|
||||||
|
replaceHeadComponents,
|
||||||
|
getPreBodyComponents,
|
||||||
|
replacePreBodyComponents,
|
||||||
|
getPostBodyComponents,
|
||||||
|
replacePostBodyComponents,
|
||||||
|
pathname: pagePath
|
||||||
|
});
|
||||||
|
|
||||||
|
const htmlElement = _react.default.createElement(Html, Object.assign({}, bodyProps, {
|
||||||
|
body: ``,
|
||||||
|
headComponents: headComponents.concat([_react.default.createElement("script", {
|
||||||
|
key: `io`,
|
||||||
|
src: "/socket.io/socket.io.js"
|
||||||
|
})]),
|
||||||
|
htmlAttributes,
|
||||||
|
bodyAttributes,
|
||||||
|
preBodyComponents,
|
||||||
|
postBodyComponents: postBodyComponents.concat([_react.default.createElement("script", {
|
||||||
|
key: `commons`,
|
||||||
|
src: "/commons.js"
|
||||||
|
})])
|
||||||
|
}));
|
||||||
|
|
||||||
|
htmlStr = (0, _server.renderToStaticMarkup)(htmlElement);
|
||||||
|
htmlStr = `<!DOCTYPE html>${htmlStr}`;
|
||||||
|
callback(null, htmlStr);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.default = _default;
|
|
@ -0,0 +1,12 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||||
|
|
||||||
|
exports.__esModule = true;
|
||||||
|
exports.default = void 0;
|
||||||
|
|
||||||
|
var _mitt = _interopRequireDefault(require("mitt"));
|
||||||
|
|
||||||
|
const emitter = (0, _mitt.default)();
|
||||||
|
var _default = emitter;
|
||||||
|
exports.default = _default;
|
|
@ -0,0 +1,94 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||||
|
|
||||||
|
exports.__esModule = true;
|
||||||
|
exports.default = void 0;
|
||||||
|
|
||||||
|
var _react = _interopRequireDefault(require("react"));
|
||||||
|
|
||||||
|
var _loader = _interopRequireDefault(require("./loader"));
|
||||||
|
|
||||||
|
var _shallowCompare = _interopRequireDefault(require("shallow-compare"));
|
||||||
|
|
||||||
|
class EnsureResources extends _react.default.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super();
|
||||||
|
const {
|
||||||
|
location,
|
||||||
|
pageResources
|
||||||
|
} = props;
|
||||||
|
this.state = {
|
||||||
|
location: Object.assign({}, location),
|
||||||
|
pageResources: pageResources || _loader.default.loadPageSync(location.pathname)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDerivedStateFromProps({
|
||||||
|
location
|
||||||
|
}, prevState) {
|
||||||
|
if (prevState.location.href !== location.href) {
|
||||||
|
const pageResources = _loader.default.loadPageSync(location.pathname);
|
||||||
|
|
||||||
|
return {
|
||||||
|
pageResources,
|
||||||
|
location: Object.assign({}, location)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
location: Object.assign({}, location)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
loadResources(rawPath) {
|
||||||
|
_loader.default.loadPage(rawPath).then(pageResources => {
|
||||||
|
if (pageResources && pageResources.status !== `error`) {
|
||||||
|
this.setState({
|
||||||
|
location: Object.assign({}, window.location),
|
||||||
|
pageResources
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
window.history.replaceState({}, ``, location.href);
|
||||||
|
window.location = rawPath;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
|
// Always return false if we're missing resources.
|
||||||
|
if (!nextState.pageResources) {
|
||||||
|
this.loadResources(nextProps.location.pathname);
|
||||||
|
return false;
|
||||||
|
} // Check if the component or json have changed.
|
||||||
|
|
||||||
|
|
||||||
|
if (this.state.pageResources !== nextState.pageResources) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state.pageResources.component !== nextState.pageResources.component) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state.pageResources.json !== nextState.pageResources.json) {
|
||||||
|
return true;
|
||||||
|
} // Check if location has changed on a page using internal routing
|
||||||
|
// via matchPath configuration.
|
||||||
|
|
||||||
|
|
||||||
|
if (this.state.location.key !== nextState.location.key && nextState.pageResources.page && (nextState.pageResources.page.matchPath || nextState.pageResources.page.path)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (0, _shallowCompare.default)(this, nextProps, nextState);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return this.props.children(this.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var _default = EnsureResources;
|
||||||
|
exports.default = _default;
|
|
@ -0,0 +1,67 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
|
||||||
|
|
||||||
|
exports.__esModule = true;
|
||||||
|
exports.errorMap = exports.reportError = exports.clearError = void 0;
|
||||||
|
|
||||||
|
var ErrorOverlay = _interopRequireWildcard(require("react-error-overlay"));
|
||||||
|
|
||||||
|
// Report runtime errors
|
||||||
|
ErrorOverlay.startReportingRuntimeErrors({
|
||||||
|
onError: () => {},
|
||||||
|
filename: `/commons.js`
|
||||||
|
});
|
||||||
|
ErrorOverlay.setEditorHandler(errorLocation => window.fetch(`/__open-stack-frame-in-editor?fileName=` + window.encodeURIComponent(errorLocation.fileName) + `&lineNumber=` + window.encodeURIComponent(errorLocation.lineNumber || 1)));
|
||||||
|
const errorMap = {};
|
||||||
|
exports.errorMap = errorMap;
|
||||||
|
|
||||||
|
function flat(arr) {
|
||||||
|
return Array.prototype.flat ? arr.flat() : [].concat(...arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleErrorOverlay = () => {
|
||||||
|
const errors = Object.values(errorMap);
|
||||||
|
let errorStringsToDisplay = [];
|
||||||
|
|
||||||
|
if (errors.length > 0) {
|
||||||
|
errorStringsToDisplay = flat(errors).map(error => {
|
||||||
|
if (typeof error === `string`) {
|
||||||
|
return error;
|
||||||
|
} else if (typeof error === `object`) {
|
||||||
|
const errorStrBuilder = [error.text];
|
||||||
|
|
||||||
|
if (error.filePath) {
|
||||||
|
errorStrBuilder.push(`File: ${error.filePath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return errorStrBuilder.join(`\n\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}).filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorStringsToDisplay.length > 0) {
|
||||||
|
ErrorOverlay.reportBuildError(errorStringsToDisplay.join(`\n\n`));
|
||||||
|
} else {
|
||||||
|
ErrorOverlay.dismissBuildError();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearError = errorID => {
|
||||||
|
delete errorMap[errorID];
|
||||||
|
handleErrorOverlay();
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.clearError = clearError;
|
||||||
|
|
||||||
|
const reportError = (errorID, error) => {
|
||||||
|
if (error) {
|
||||||
|
errorMap[errorID] = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleErrorOverlay();
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.reportError = reportError;
|
|
@ -0,0 +1,111 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||||
|
|
||||||
|
exports.__esModule = true;
|
||||||
|
exports.cleanPath = exports.findPath = exports.findMatchPath = exports.setMatchPaths = void 0;
|
||||||
|
|
||||||
|
var _utils = require("@reach/router/lib/utils");
|
||||||
|
|
||||||
|
var _stripPrefix = _interopRequireDefault(require("./strip-prefix"));
|
||||||
|
|
||||||
|
var _normalizePagePath = _interopRequireDefault(require("./normalize-page-path"));
|
||||||
|
|
||||||
|
const pathCache = new Map();
|
||||||
|
let matchPaths = [];
|
||||||
|
|
||||||
|
const trimPathname = rawPathname => {
|
||||||
|
let pathname = decodeURIComponent(rawPathname); // Remove the pathPrefix from the pathname.
|
||||||
|
|
||||||
|
let trimmedPathname = (0, _stripPrefix.default)(pathname, __BASE_PATH__) // Remove any hashfragment
|
||||||
|
.split(`#`)[0] // Remove search query
|
||||||
|
.split(`?`)[0];
|
||||||
|
return trimmedPathname;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Set list of matchPaths
|
||||||
|
*
|
||||||
|
* @param {Array<{path: string, matchPath: string}>} value collection of matchPaths
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
const setMatchPaths = value => {
|
||||||
|
matchPaths = value;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Return a matchpath url
|
||||||
|
* if `match-paths.json` contains `{ "/foo*": "/page1", ...}`, then
|
||||||
|
* `/foo?bar=far` => `/page1`
|
||||||
|
*
|
||||||
|
* @param {string} rawPathname A raw pathname
|
||||||
|
* @return {string|null}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
exports.setMatchPaths = setMatchPaths;
|
||||||
|
|
||||||
|
const findMatchPath = rawPathname => {
|
||||||
|
const trimmedPathname = cleanPath(rawPathname);
|
||||||
|
|
||||||
|
for (const {
|
||||||
|
matchPath,
|
||||||
|
path
|
||||||
|
} of matchPaths) {
|
||||||
|
if ((0, _utils.match)(matchPath, trimmedPathname)) {
|
||||||
|
return (0, _normalizePagePath.default)(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}; // Given a raw URL path, returns the cleaned version of it (trim off
|
||||||
|
// `#` and query params), or if it matches an entry in
|
||||||
|
// `match-paths.json`, its matched path is returned
|
||||||
|
//
|
||||||
|
// E.g `/foo?bar=far` => `/foo`
|
||||||
|
//
|
||||||
|
// Or if `match-paths.json` contains `{ "/foo*": "/page1", ...}`, then
|
||||||
|
// `/foo?bar=far` => `/page1`
|
||||||
|
|
||||||
|
|
||||||
|
exports.findMatchPath = findMatchPath;
|
||||||
|
|
||||||
|
const findPath = rawPathname => {
|
||||||
|
const trimmedPathname = trimPathname(rawPathname);
|
||||||
|
|
||||||
|
if (pathCache.has(trimmedPathname)) {
|
||||||
|
return pathCache.get(trimmedPathname);
|
||||||
|
}
|
||||||
|
|
||||||
|
let foundPath = findMatchPath(trimmedPathname);
|
||||||
|
|
||||||
|
if (!foundPath) {
|
||||||
|
foundPath = cleanPath(rawPathname);
|
||||||
|
}
|
||||||
|
|
||||||
|
pathCache.set(trimmedPathname, foundPath);
|
||||||
|
return foundPath;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Clean a url and converts /index.html => /
|
||||||
|
* E.g `/foo?bar=far` => `/foo`
|
||||||
|
*
|
||||||
|
* @param {string} rawPathname A raw pathname
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
exports.findPath = findPath;
|
||||||
|
|
||||||
|
const cleanPath = rawPathname => {
|
||||||
|
const trimmedPathname = trimPathname(rawPathname);
|
||||||
|
let foundPath = trimmedPathname;
|
||||||
|
|
||||||
|
if (foundPath === `/index.html`) {
|
||||||
|
foundPath = `/`;
|
||||||
|
}
|
||||||
|
|
||||||
|
foundPath = (0, _normalizePagePath.default)(foundPath);
|
||||||
|
return foundPath;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.cleanPath = cleanPath;
|
|
@ -0,0 +1,90 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
|
||||||
|
|
||||||
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||||
|
|
||||||
|
exports.__esModule = true;
|
||||||
|
exports.graphql = graphql;
|
||||||
|
exports.prefetchPathname = exports.useStaticQuery = exports.StaticQuery = exports.StaticQueryContext = void 0;
|
||||||
|
|
||||||
|
var _react = _interopRequireDefault(require("react"));
|
||||||
|
|
||||||
|
var _propTypes = _interopRequireDefault(require("prop-types"));
|
||||||
|
|
||||||
|
var _gatsbyLink = _interopRequireWildcard(require("gatsby-link"));
|
||||||
|
|
||||||
|
exports.Link = _gatsbyLink.default;
|
||||||
|
exports.withPrefix = _gatsbyLink.withPrefix;
|
||||||
|
exports.withAssetPrefix = _gatsbyLink.withAssetPrefix;
|
||||||
|
exports.navigate = _gatsbyLink.navigate;
|
||||||
|
exports.push = _gatsbyLink.push;
|
||||||
|
exports.replace = _gatsbyLink.replace;
|
||||||
|
exports.navigateTo = _gatsbyLink.navigateTo;
|
||||||
|
exports.parsePath = _gatsbyLink.parsePath;
|
||||||
|
|
||||||
|
var _publicPageRenderer = _interopRequireDefault(require("./public-page-renderer"));
|
||||||
|
|
||||||
|
exports.PageRenderer = _publicPageRenderer.default;
|
||||||
|
|
||||||
|
var _loader = _interopRequireDefault(require("./loader"));
|
||||||
|
|
||||||
|
const prefetchPathname = _loader.default.enqueue;
|
||||||
|
exports.prefetchPathname = prefetchPathname;
|
||||||
|
|
||||||
|
const StaticQueryContext = _react.default.createContext({});
|
||||||
|
|
||||||
|
exports.StaticQueryContext = StaticQueryContext;
|
||||||
|
|
||||||
|
function StaticQueryDataRenderer({
|
||||||
|
staticQueryData,
|
||||||
|
data,
|
||||||
|
query,
|
||||||
|
render
|
||||||
|
}) {
|
||||||
|
const finalData = data ? data.data : staticQueryData[query] && staticQueryData[query].data;
|
||||||
|
return _react.default.createElement(_react.default.Fragment, null, finalData && render(finalData), !finalData && _react.default.createElement("div", null, "Loading (StaticQuery)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
const StaticQuery = props => {
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
query,
|
||||||
|
render,
|
||||||
|
children
|
||||||
|
} = props;
|
||||||
|
return _react.default.createElement(StaticQueryContext.Consumer, null, staticQueryData => _react.default.createElement(StaticQueryDataRenderer, {
|
||||||
|
data: data,
|
||||||
|
query: query,
|
||||||
|
render: render || children,
|
||||||
|
staticQueryData: staticQueryData
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.StaticQuery = StaticQuery;
|
||||||
|
|
||||||
|
const useStaticQuery = query => {
|
||||||
|
if (typeof _react.default.useContext !== `function` && process.env.NODE_ENV === `development`) {
|
||||||
|
throw new Error(`You're likely using a version of React that doesn't support Hooks\n` + `Please update React and ReactDOM to 16.8.0 or later to use the useStaticQuery hook.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const context = _react.default.useContext(StaticQueryContext);
|
||||||
|
|
||||||
|
if (context[query] && context[query].data) {
|
||||||
|
return context[query].data;
|
||||||
|
} else {
|
||||||
|
throw new Error(`The result of this StaticQuery could not be fetched.\n\n` + `This is likely a bug in Gatsby and if refreshing the page does not fix it, ` + `please open an issue in https://github.com/gatsbyjs/gatsby/issues`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.useStaticQuery = useStaticQuery;
|
||||||
|
StaticQuery.propTypes = {
|
||||||
|
data: _propTypes.default.object,
|
||||||
|
query: _propTypes.default.string.isRequired,
|
||||||
|
render: _propTypes.default.func,
|
||||||
|
children: _propTypes.default.func
|
||||||
|
};
|
||||||
|
|
||||||
|
function graphql() {
|
||||||
|
throw new Error(`It appears like Gatsby is misconfigured. Gatsby related \`graphql\` calls ` + `are supposed to only be evaluated at compile time, and then compiled away. ` + `Unfortunately, something went wrong and the query was left in the compiled code.\n\n` + `Unless your site has a complex or custom babel/Gatsby configuration this is likely a bug in Gatsby.`);
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||||
|
|
||||||
|
exports.__esModule = true;
|
||||||
|
exports.default = void 0;
|
||||||
|
|
||||||
|
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
|
||||||
|
|
||||||
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
||||||
|
|
||||||
|
var _react = _interopRequireDefault(require("react"));
|
||||||
|
|
||||||
|
var _pageRenderer = _interopRequireDefault(require("./page-renderer"));
|
||||||
|
|
||||||
|
var _normalizePagePath = _interopRequireDefault(require("./normalize-page-path"));
|
||||||
|
|
||||||
|
var _gatsby = require("gatsby");
|
||||||
|
|
||||||
|
var _socketIo = require("./socketIo");
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === `production`) {
|
||||||
|
throw new Error(`It appears like Gatsby is misconfigured. JSONStore is Gatsby internal ` + `development-only component and should never be used in production.\n\n` + `Unless your site has a complex or custom webpack/Gatsby ` + `configuration this is likely a bug in Gatsby. ` + `Please report this at https://github.com/gatsbyjs/gatsby/issues ` + `with steps to reproduce this error.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getPathFromProps = props => props.pageResources && props.pageResources.page ? (0, _normalizePagePath.default)(props.pageResources.page.path) : undefined;
|
||||||
|
|
||||||
|
class JSONStore extends _react.default.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
(0, _defineProperty2.default)(this, "handleMittEvent", (type, event) => {
|
||||||
|
this.setState({
|
||||||
|
staticQueryData: (0, _socketIo.getStaticQueryData)(),
|
||||||
|
pageQueryData: (0, _socketIo.getPageQueryData)()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.state = {
|
||||||
|
staticQueryData: (0, _socketIo.getStaticQueryData)(),
|
||||||
|
pageQueryData: (0, _socketIo.getPageQueryData)(),
|
||||||
|
path: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
(0, _socketIo.registerPath)(getPathFromProps(this.props));
|
||||||
|
|
||||||
|
___emitter.on(`*`, this.handleMittEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
(0, _socketIo.unregisterPath)(this.state.path);
|
||||||
|
|
||||||
|
___emitter.off(`*`, this.handleMittEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDerivedStateFromProps(props, state) {
|
||||||
|
const newPath = getPathFromProps(props);
|
||||||
|
|
||||||
|
if (newPath !== state.path) {
|
||||||
|
(0, _socketIo.unregisterPath)(state.path);
|
||||||
|
(0, _socketIo.registerPath)(newPath);
|
||||||
|
return {
|
||||||
|
path: newPath
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
|
// We want to update this component when:
|
||||||
|
// - location changed
|
||||||
|
// - page data for path changed
|
||||||
|
// - static query results changed
|
||||||
|
return this.props.location !== nextProps.location || this.state.path !== nextState.path || this.state.pageQueryData[(0, _normalizePagePath.default)(nextState.path)] !== nextState.pageQueryData[(0, _normalizePagePath.default)(nextState.path)] || this.state.staticQueryData !== nextState.staticQueryData;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const data = this.state.pageQueryData[getPathFromProps(this.props)]; // eslint-disable-next-line
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return _react.default.createElement("div", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _react.default.createElement(_gatsby.StaticQueryContext.Provider, {
|
||||||
|
value: this.state.staticQueryData
|
||||||
|
}, _react.default.createElement(_pageRenderer.default, (0, _extends2.default)({}, this.props, data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var _default = JSONStore;
|
||||||
|
exports.default = _default;
|
|
@ -0,0 +1,434 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||||
|
|
||||||
|
exports.__esModule = true;
|
||||||
|
exports.default = exports.publicLoader = exports.setLoader = exports.ProdLoader = exports.BaseLoader = void 0;
|
||||||
|
|
||||||
|
var _prefetch = _interopRequireDefault(require("./prefetch"));
|
||||||
|
|
||||||
|
var _emitter = _interopRequireDefault(require("./emitter"));
|
||||||
|
|
||||||
|
var _findPath = require("./find-path");
|
||||||
|
|
||||||
|
const preferDefault = m => m && m.default || m;
|
||||||
|
|
||||||
|
const stripSurroundingSlashes = s => {
|
||||||
|
s = s[0] === `/` ? s.slice(1) : s;
|
||||||
|
s = s.endsWith(`/`) ? s.slice(0, -1) : s;
|
||||||
|
return s;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createPageDataUrl = path => {
|
||||||
|
const fixedPath = path === `/` ? `index` : stripSurroundingSlashes(path);
|
||||||
|
return `${__PATH_PREFIX__}/page-data/${fixedPath}/page-data.json`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const doFetch = (url, method = `GET`) => new Promise((resolve, reject) => {
|
||||||
|
const req = new XMLHttpRequest();
|
||||||
|
req.open(method, url, true);
|
||||||
|
|
||||||
|
req.onreadystatechange = () => {
|
||||||
|
if (req.readyState == 4) {
|
||||||
|
resolve(req);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
req.send(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
const loadPageDataJson = loadObj => {
|
||||||
|
const {
|
||||||
|
pagePath,
|
||||||
|
retries = 0
|
||||||
|
} = loadObj;
|
||||||
|
const url = createPageDataUrl(pagePath);
|
||||||
|
return doFetch(url).then(req => {
|
||||||
|
const {
|
||||||
|
status,
|
||||||
|
responseText
|
||||||
|
} = req; // Handle 200
|
||||||
|
|
||||||
|
if (status === 200) {
|
||||||
|
try {
|
||||||
|
const jsonPayload = JSON.parse(responseText);
|
||||||
|
|
||||||
|
if (jsonPayload.path === undefined) {
|
||||||
|
throw new Error(`not a valid pageData response`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.assign(loadObj, {
|
||||||
|
status: `success`,
|
||||||
|
payload: jsonPayload
|
||||||
|
});
|
||||||
|
} catch (err) {// continue regardless of error
|
||||||
|
}
|
||||||
|
} // Handle 404
|
||||||
|
|
||||||
|
|
||||||
|
if (status === 404 || status === 200) {
|
||||||
|
// If the request was for a 404 page and it doesn't exist, we're done
|
||||||
|
if (pagePath === `/404.html`) {
|
||||||
|
return Object.assign(loadObj, {
|
||||||
|
status: `failure`
|
||||||
|
});
|
||||||
|
} // Need some code here to cache the 404 request. In case
|
||||||
|
// multiple loadPageDataJsons result in 404s
|
||||||
|
|
||||||
|
|
||||||
|
return loadPageDataJson(Object.assign(loadObj, {
|
||||||
|
pagePath: `/404.html`,
|
||||||
|
notFound: true
|
||||||
|
}));
|
||||||
|
} // handle 500 response (Unrecoverable)
|
||||||
|
|
||||||
|
|
||||||
|
if (status === 500) {
|
||||||
|
return Object.assign(loadObj, {
|
||||||
|
status: `error`
|
||||||
|
});
|
||||||
|
} // Handle everything else, including status === 0, and 503s. Should retry
|
||||||
|
|
||||||
|
|
||||||
|
if (retries < 3) {
|
||||||
|
return loadPageDataJson(Object.assign(loadObj, {
|
||||||
|
retries: retries + 1
|
||||||
|
}));
|
||||||
|
} // Retried 3 times already, result is a failure.
|
||||||
|
|
||||||
|
|
||||||
|
return Object.assign(loadObj, {
|
||||||
|
status: `error`
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const doesConnectionSupportPrefetch = () => {
|
||||||
|
if (`connection` in navigator && typeof navigator.connection !== `undefined`) {
|
||||||
|
if ((navigator.connection.effectiveType || ``).includes(`2g`)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (navigator.connection.saveData) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const toPageResources = (pageData, component = null) => {
|
||||||
|
const page = {
|
||||||
|
componentChunkName: pageData.componentChunkName,
|
||||||
|
path: pageData.path,
|
||||||
|
webpackCompilationHash: pageData.webpackCompilationHash,
|
||||||
|
matchPath: pageData.matchPath
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
component,
|
||||||
|
json: pageData.result,
|
||||||
|
page
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
class BaseLoader {
|
||||||
|
constructor(loadComponent, matchPaths) {
|
||||||
|
// Map of pagePath -> Page. Where Page is an object with: {
|
||||||
|
// status: `success` || `error`,
|
||||||
|
// payload: PageResources, // undefined if `error`
|
||||||
|
// }
|
||||||
|
// PageResources is {
|
||||||
|
// component,
|
||||||
|
// json: pageData.result,
|
||||||
|
// page: {
|
||||||
|
// componentChunkName,
|
||||||
|
// path,
|
||||||
|
// webpackCompilationHash,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
this.pageDb = new Map();
|
||||||
|
this.inFlightDb = new Map();
|
||||||
|
this.pageDataDb = new Map();
|
||||||
|
this.prefetchTriggered = new Set();
|
||||||
|
this.prefetchCompleted = new Set();
|
||||||
|
this.loadComponent = loadComponent;
|
||||||
|
(0, _findPath.setMatchPaths)(matchPaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
setApiRunner(apiRunner) {
|
||||||
|
this.apiRunner = apiRunner;
|
||||||
|
this.prefetchDisabled = apiRunner(`disableCorePrefetching`).some(a => a);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadPageDataJson(rawPath) {
|
||||||
|
const pagePath = (0, _findPath.findPath)(rawPath);
|
||||||
|
|
||||||
|
if (this.pageDataDb.has(pagePath)) {
|
||||||
|
return Promise.resolve(this.pageDataDb.get(pagePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
return loadPageDataJson({
|
||||||
|
pagePath
|
||||||
|
}).then(pageData => {
|
||||||
|
this.pageDataDb.set(pagePath, pageData);
|
||||||
|
return pageData;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
findMatchPath(rawPath) {
|
||||||
|
return (0, _findPath.findMatchPath)(rawPath);
|
||||||
|
} // TODO check all uses of this and whether they use undefined for page resources not exist
|
||||||
|
|
||||||
|
|
||||||
|
loadPage(rawPath) {
|
||||||
|
const pagePath = (0, _findPath.findPath)(rawPath);
|
||||||
|
|
||||||
|
if (this.pageDb.has(pagePath)) {
|
||||||
|
const page = this.pageDb.get(pagePath);
|
||||||
|
return Promise.resolve(page.payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.inFlightDb.has(pagePath)) {
|
||||||
|
return this.inFlightDb.get(pagePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
const inFlight = Promise.all([this.loadAppData(), this.loadPageDataJson(pagePath)]).then(allData => {
|
||||||
|
const result = allData[1];
|
||||||
|
|
||||||
|
if (result.status === `error`) {
|
||||||
|
return {
|
||||||
|
status: `error`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.status === `failure`) {
|
||||||
|
// throw an error so error trackers can pick this up
|
||||||
|
throw new Error(`404 page could not be found. Checkout https://www.gatsbyjs.org/docs/add-404-page/`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let pageData = result.payload;
|
||||||
|
const {
|
||||||
|
componentChunkName
|
||||||
|
} = pageData;
|
||||||
|
return this.loadComponent(componentChunkName).then(component => {
|
||||||
|
const finalResult = {
|
||||||
|
createdAt: new Date()
|
||||||
|
};
|
||||||
|
let pageResources;
|
||||||
|
|
||||||
|
if (!component) {
|
||||||
|
finalResult.status = `error`;
|
||||||
|
} else {
|
||||||
|
finalResult.status = `success`;
|
||||||
|
|
||||||
|
if (result.notFound === true) {
|
||||||
|
finalResult.notFound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pageData = Object.assign(pageData, {
|
||||||
|
webpackCompilationHash: allData[0] ? allData[0].webpackCompilationHash : ``
|
||||||
|
});
|
||||||
|
pageResources = toPageResources(pageData, component);
|
||||||
|
finalResult.payload = pageResources;
|
||||||
|
|
||||||
|
_emitter.default.emit(`onPostLoadPageResources`, {
|
||||||
|
page: pageResources,
|
||||||
|
pageResources
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pageDb.set(pagePath, finalResult); // undefined if final result is an error
|
||||||
|
|
||||||
|
return pageResources;
|
||||||
|
});
|
||||||
|
}) // prefer duplication with then + catch over .finally to prevent problems in ie11 + firefox
|
||||||
|
.then(response => {
|
||||||
|
this.inFlightDb.delete(pagePath);
|
||||||
|
return response;
|
||||||
|
}).catch(err => {
|
||||||
|
this.inFlightDb.delete(pagePath);
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
this.inFlightDb.set(pagePath, inFlight);
|
||||||
|
return inFlight;
|
||||||
|
} // returns undefined if loading page ran into errors
|
||||||
|
|
||||||
|
|
||||||
|
loadPageSync(rawPath) {
|
||||||
|
const pagePath = (0, _findPath.findPath)(rawPath);
|
||||||
|
|
||||||
|
if (this.pageDb.has(pagePath)) {
|
||||||
|
return this.pageDb.get(pagePath).payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldPrefetch(pagePath) {
|
||||||
|
// Skip prefetching if we know user is on slow or constrained connection
|
||||||
|
if (!doesConnectionSupportPrefetch()) {
|
||||||
|
return false;
|
||||||
|
} // Check if the page exists.
|
||||||
|
|
||||||
|
|
||||||
|
if (this.pageDb.has(pagePath)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
prefetch(pagePath) {
|
||||||
|
if (!this.shouldPrefetch(pagePath)) {
|
||||||
|
return false;
|
||||||
|
} // Tell plugins with custom prefetching logic that they should start
|
||||||
|
// prefetching this path.
|
||||||
|
|
||||||
|
|
||||||
|
if (!this.prefetchTriggered.has(pagePath)) {
|
||||||
|
this.apiRunner(`onPrefetchPathname`, {
|
||||||
|
pathname: pagePath
|
||||||
|
});
|
||||||
|
this.prefetchTriggered.add(pagePath);
|
||||||
|
} // If a plugin has disabled core prefetching, stop now.
|
||||||
|
|
||||||
|
|
||||||
|
if (this.prefetchDisabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const realPath = (0, _findPath.findPath)(pagePath); // Todo make doPrefetch logic cacheable
|
||||||
|
// eslint-disable-next-line consistent-return
|
||||||
|
|
||||||
|
this.doPrefetch(realPath).then(() => {
|
||||||
|
if (!this.prefetchCompleted.has(pagePath)) {
|
||||||
|
this.apiRunner(`onPostPrefetchPathname`, {
|
||||||
|
pathname: pagePath
|
||||||
|
});
|
||||||
|
this.prefetchCompleted.add(pagePath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
doPrefetch(pagePath) {
|
||||||
|
throw new Error(`doPrefetch not implemented`);
|
||||||
|
}
|
||||||
|
|
||||||
|
hovering(rawPath) {
|
||||||
|
this.loadPage(rawPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
getResourceURLsForPathname(rawPath) {
|
||||||
|
const pagePath = (0, _findPath.findPath)(rawPath);
|
||||||
|
const page = this.pageDataDb.get(pagePath);
|
||||||
|
|
||||||
|
if (page) {
|
||||||
|
const pageResources = toPageResources(page.payload);
|
||||||
|
return [...createComponentUrls(pageResources.page.componentChunkName), createPageDataUrl(pagePath)];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isPageNotFound(rawPath) {
|
||||||
|
const pagePath = (0, _findPath.findPath)(rawPath);
|
||||||
|
const page = this.pageDb.get(pagePath);
|
||||||
|
return page && page.notFound === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadAppData(retries = 0) {
|
||||||
|
return doFetch(`${__PATH_PREFIX__}/page-data/app-data.json`).then(req => {
|
||||||
|
const {
|
||||||
|
status,
|
||||||
|
responseText
|
||||||
|
} = req;
|
||||||
|
let appData;
|
||||||
|
|
||||||
|
if (status !== 200 && retries < 3) {
|
||||||
|
// Retry 3 times incase of failures
|
||||||
|
return this.loadAppData(retries + 1);
|
||||||
|
} // Handle 200
|
||||||
|
|
||||||
|
|
||||||
|
if (status === 200) {
|
||||||
|
try {
|
||||||
|
const jsonPayload = JSON.parse(responseText);
|
||||||
|
|
||||||
|
if (jsonPayload.webpackCompilationHash === undefined) {
|
||||||
|
throw new Error(`not a valid app-data response`);
|
||||||
|
}
|
||||||
|
|
||||||
|
appData = jsonPayload;
|
||||||
|
} catch (err) {// continue regardless of error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return appData;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.BaseLoader = BaseLoader;
|
||||||
|
|
||||||
|
const createComponentUrls = componentChunkName => window.___chunkMapping[componentChunkName].map(chunk => __PATH_PREFIX__ + chunk);
|
||||||
|
|
||||||
|
class ProdLoader extends BaseLoader {
|
||||||
|
constructor(asyncRequires, matchPaths) {
|
||||||
|
const loadComponent = chunkName => asyncRequires.components[chunkName]().then(preferDefault);
|
||||||
|
|
||||||
|
super(loadComponent, matchPaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
doPrefetch(pagePath) {
|
||||||
|
const pageDataUrl = createPageDataUrl(pagePath);
|
||||||
|
return (0, _prefetch.default)(pageDataUrl).then(() => // This was just prefetched, so will return a response from
|
||||||
|
// the cache instead of making another request to the server
|
||||||
|
this.loadPageDataJson(pagePath)).then(result => {
|
||||||
|
if (result.status !== `success`) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
const pageData = result.payload;
|
||||||
|
const chunkName = pageData.componentChunkName;
|
||||||
|
const componentUrls = createComponentUrls(chunkName);
|
||||||
|
return Promise.all(componentUrls.map(_prefetch.default)).then(() => pageData);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.ProdLoader = ProdLoader;
|
||||||
|
let instance;
|
||||||
|
|
||||||
|
const setLoader = _loader => {
|
||||||
|
instance = _loader;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.setLoader = setLoader;
|
||||||
|
const publicLoader = {
|
||||||
|
// Deprecated methods. As far as we're aware, these are only used by
|
||||||
|
// core gatsby and the offline plugin, however there's a very small
|
||||||
|
// chance they're called by others.
|
||||||
|
getResourcesForPathname: rawPath => {
|
||||||
|
console.warn(`Warning: getResourcesForPathname is deprecated. Use loadPage instead`);
|
||||||
|
return instance.i.loadPage(rawPath);
|
||||||
|
},
|
||||||
|
getResourcesForPathnameSync: rawPath => {
|
||||||
|
console.warn(`Warning: getResourcesForPathnameSync is deprecated. Use loadPageSync instead`);
|
||||||
|
return instance.i.loadPageSync(rawPath);
|
||||||
|
},
|
||||||
|
enqueue: rawPath => instance.prefetch(rawPath),
|
||||||
|
// Real methods
|
||||||
|
getResourceURLsForPathname: rawPath => instance.getResourceURLsForPathname(rawPath),
|
||||||
|
loadPage: rawPath => instance.loadPage(rawPath),
|
||||||
|
loadPageSync: rawPath => instance.loadPageSync(rawPath),
|
||||||
|
prefetch: rawPath => instance.prefetch(rawPath),
|
||||||
|
isPageNotFound: rawPath => instance.isPageNotFound(rawPath),
|
||||||
|
hovering: rawPath => instance.hovering(rawPath),
|
||||||
|
loadAppData: () => instance.loadAppData()
|
||||||
|
};
|
||||||
|
exports.publicLoader = publicLoader;
|
||||||
|
var _default = publicLoader;
|
||||||
|
exports.default = _default;
|
|
@ -0,0 +1,233 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||||
|
|
||||||
|
exports.__esModule = true;
|
||||||
|
exports.init = init;
|
||||||
|
exports.shouldUpdateScroll = shouldUpdateScroll;
|
||||||
|
exports.RouteUpdates = void 0;
|
||||||
|
|
||||||
|
var _react = _interopRequireDefault(require("react"));
|
||||||
|
|
||||||
|
var _propTypes = _interopRequireDefault(require("prop-types"));
|
||||||
|
|
||||||
|
var _loader = _interopRequireDefault(require("./loader"));
|
||||||
|
|
||||||
|
var _redirects = _interopRequireDefault(require("./redirects.json"));
|
||||||
|
|
||||||
|
var _apiRunnerBrowser = require("./api-runner-browser");
|
||||||
|
|
||||||
|
var _emitter = _interopRequireDefault(require("./emitter"));
|
||||||
|
|
||||||
|
var _router = require("@reach/router");
|
||||||
|
|
||||||
|
var _gatsbyLink = require("gatsby-link");
|
||||||
|
|
||||||
|
const redirectMap = _redirects.default.reduce((map, redirect) => {
|
||||||
|
map[redirect.fromPath] = redirect;
|
||||||
|
return map;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
function maybeRedirect(pathname) {
|
||||||
|
const redirect = redirectMap[pathname];
|
||||||
|
|
||||||
|
if (redirect != null) {
|
||||||
|
if (process.env.NODE_ENV !== `production`) {
|
||||||
|
const pageResources = _loader.default.loadPageSync(pathname);
|
||||||
|
|
||||||
|
if (pageResources != null) {
|
||||||
|
console.error(`The route "${pathname}" matches both a page and a redirect; this is probably not intentional.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.___replace(redirect.toPath);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onPreRouteUpdate = (location, prevLocation) => {
|
||||||
|
if (!maybeRedirect(location.pathname)) {
|
||||||
|
(0, _apiRunnerBrowser.apiRunner)(`onPreRouteUpdate`, {
|
||||||
|
location,
|
||||||
|
prevLocation
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onRouteUpdate = (location, prevLocation) => {
|
||||||
|
if (!maybeRedirect(location.pathname)) {
|
||||||
|
(0, _apiRunnerBrowser.apiRunner)(`onRouteUpdate`, {
|
||||||
|
location,
|
||||||
|
prevLocation
|
||||||
|
}); // Temp hack while awaiting https://github.com/reach/router/issues/119
|
||||||
|
|
||||||
|
window.__navigatingToLink = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const navigate = (to, options = {}) => {
|
||||||
|
// Temp hack while awaiting https://github.com/reach/router/issues/119
|
||||||
|
if (!options.replace) {
|
||||||
|
window.__navigatingToLink = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let {
|
||||||
|
pathname
|
||||||
|
} = (0, _gatsbyLink.parsePath)(to);
|
||||||
|
const redirect = redirectMap[pathname]; // If we're redirecting, just replace the passed in pathname
|
||||||
|
// to the one we want to redirect to.
|
||||||
|
|
||||||
|
if (redirect) {
|
||||||
|
to = redirect.toPath;
|
||||||
|
pathname = (0, _gatsbyLink.parsePath)(to).pathname;
|
||||||
|
} // If we had a service worker update, no matter the path, reload window and
|
||||||
|
// reset the pathname whitelist
|
||||||
|
|
||||||
|
|
||||||
|
if (window.___swUpdated) {
|
||||||
|
window.location = pathname;
|
||||||
|
return;
|
||||||
|
} // Start a timer to wait for a second before transitioning and showing a
|
||||||
|
// loader in case resources aren't around yet.
|
||||||
|
|
||||||
|
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
_emitter.default.emit(`onDelayedLoadPageResources`, {
|
||||||
|
pathname
|
||||||
|
});
|
||||||
|
|
||||||
|
(0, _apiRunnerBrowser.apiRunner)(`onRouteUpdateDelayed`, {
|
||||||
|
location: window.location
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
_loader.default.loadPage(pathname).then(pageResources => {
|
||||||
|
// If no page resources, then refresh the page
|
||||||
|
// Do this, rather than simply `window.location.reload()`, so that
|
||||||
|
// pressing the back/forward buttons work - otherwise when pressing
|
||||||
|
// back, the browser will just change the URL and expect JS to handle
|
||||||
|
// the change, which won't always work since it might not be a Gatsby
|
||||||
|
// page.
|
||||||
|
if (!pageResources || pageResources.status === `error`) {
|
||||||
|
window.history.replaceState({}, ``, location.href);
|
||||||
|
window.location = pathname;
|
||||||
|
} // If the loaded page has a different compilation hash to the
|
||||||
|
// window, then a rebuild has occurred on the server. Reload.
|
||||||
|
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === `production` && pageResources) {
|
||||||
|
if (pageResources.page.webpackCompilationHash !== window.___webpackCompilationHash) {
|
||||||
|
// Purge plugin-offline cache
|
||||||
|
if (`serviceWorker` in navigator && navigator.serviceWorker.controller !== null && navigator.serviceWorker.controller.state === `activated`) {
|
||||||
|
navigator.serviceWorker.controller.postMessage({
|
||||||
|
gatsbyApi: `clearPathResources`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Site has changed on server. Reloading browser`);
|
||||||
|
window.location = pathname;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(0, _router.navigate)(to, options);
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function shouldUpdateScroll(prevRouterProps, {
|
||||||
|
location
|
||||||
|
}) {
|
||||||
|
const {
|
||||||
|
pathname,
|
||||||
|
hash
|
||||||
|
} = location;
|
||||||
|
const results = (0, _apiRunnerBrowser.apiRunner)(`shouldUpdateScroll`, {
|
||||||
|
prevRouterProps,
|
||||||
|
// `pathname` for backwards compatibility
|
||||||
|
pathname,
|
||||||
|
routerProps: {
|
||||||
|
location
|
||||||
|
},
|
||||||
|
getSavedScrollPosition: args => this._stateStorage.read(args)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (results.length > 0) {
|
||||||
|
// Use the latest registered shouldUpdateScroll result, this allows users to override plugin's configuration
|
||||||
|
// @see https://github.com/gatsbyjs/gatsby/issues/12038
|
||||||
|
return results[results.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prevRouterProps) {
|
||||||
|
const {
|
||||||
|
location: {
|
||||||
|
pathname: oldPathname
|
||||||
|
}
|
||||||
|
} = prevRouterProps;
|
||||||
|
|
||||||
|
if (oldPathname === pathname) {
|
||||||
|
// Scroll to element if it exists, if it doesn't, or no hash is provided,
|
||||||
|
// scroll to top.
|
||||||
|
return hash ? hash.slice(1) : [0, 0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
// Temp hack while awaiting https://github.com/reach/router/issues/119
|
||||||
|
window.__navigatingToLink = false;
|
||||||
|
|
||||||
|
window.___push = to => navigate(to, {
|
||||||
|
replace: false
|
||||||
|
});
|
||||||
|
|
||||||
|
window.___replace = to => navigate(to, {
|
||||||
|
replace: true
|
||||||
|
});
|
||||||
|
|
||||||
|
window.___navigate = (to, options) => navigate(to, options); // Check for initial page-load redirect
|
||||||
|
|
||||||
|
|
||||||
|
maybeRedirect(window.location.pathname);
|
||||||
|
} // Fire on(Pre)RouteUpdate APIs
|
||||||
|
|
||||||
|
|
||||||
|
class RouteUpdates extends _react.default.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
onPreRouteUpdate(props.location, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
onRouteUpdate(this.props.location, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps, prevState, shouldFireRouteUpdate) {
|
||||||
|
if (shouldFireRouteUpdate) {
|
||||||
|
onRouteUpdate(this.props.location, prevProps.location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getSnapshotBeforeUpdate(prevProps) {
|
||||||
|
if (this.props.location.pathname !== prevProps.location.pathname) {
|
||||||
|
onPreRouteUpdate(this.props.location, prevProps.location);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return this.props.children;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.RouteUpdates = RouteUpdates;
|
||||||
|
RouteUpdates.propTypes = {
|
||||||
|
location: _propTypes.default.object.isRequired
|
||||||
|
};
|
|
@ -0,0 +1,22 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
exports.__esModule = true;
|
||||||
|
exports.default = void 0;
|
||||||
|
|
||||||
|
var _default = path => {
|
||||||
|
if (path === undefined) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path === `/`) {
|
||||||
|
return `/`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.charAt(path.length - 1) === `/`) {
|
||||||
|
return path.slice(0, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.default = _default;
|
|
@ -0,0 +1,53 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||||
|
|
||||||
|
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
|
||||||
|
|
||||||
|
exports.__esModule = true;
|
||||||
|
exports.default = void 0;
|
||||||
|
|
||||||
|
var _react = _interopRequireWildcard(require("react"));
|
||||||
|
|
||||||
|
var _propTypes = _interopRequireDefault(require("prop-types"));
|
||||||
|
|
||||||
|
var _loader = require("./loader");
|
||||||
|
|
||||||
|
var _apiRunnerBrowser = require("./api-runner-browser");
|
||||||
|
|
||||||
|
class PageRenderer extends _react.default.Component {
|
||||||
|
render() {
|
||||||
|
const props = Object.assign({}, this.props, {
|
||||||
|
pathContext: this.props.pageContext
|
||||||
|
});
|
||||||
|
const [replacementElement] = (0, _apiRunnerBrowser.apiRunner)(`replaceComponentRenderer`, {
|
||||||
|
props: this.props,
|
||||||
|
loader: _loader.publicLoader
|
||||||
|
});
|
||||||
|
const pageElement = replacementElement || (0, _react.createElement)(this.props.pageResources.component, Object.assign({}, props, {
|
||||||
|
key: this.props.path || this.props.pageResources.page.path
|
||||||
|
}));
|
||||||
|
const wrappedPage = (0, _apiRunnerBrowser.apiRunner)(`wrapPageElement`, {
|
||||||
|
element: pageElement,
|
||||||
|
props
|
||||||
|
}, pageElement, ({
|
||||||
|
result
|
||||||
|
}) => {
|
||||||
|
return {
|
||||||
|
element: result,
|
||||||
|
props
|
||||||
|
};
|
||||||
|
}).pop();
|
||||||
|
return wrappedPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PageRenderer.propTypes = {
|
||||||
|
location: _propTypes.default.object.isRequired,
|
||||||
|
pageResources: _propTypes.default.object.isRequired,
|
||||||
|
data: _propTypes.default.object,
|
||||||
|
pageContext: _propTypes.default.object.isRequired
|
||||||
|
};
|
||||||
|
var _default = PageRenderer;
|
||||||
|
exports.default = _default;
|
|
@ -0,0 +1,76 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
exports.__esModule = true;
|
||||||
|
exports.default = void 0;
|
||||||
|
|
||||||
|
const support = function (feature) {
|
||||||
|
if (typeof document === `undefined`) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fakeLink = document.createElement(`link`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (fakeLink.relList && typeof fakeLink.relList.supports === `function`) {
|
||||||
|
return fakeLink.relList.supports(feature);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const linkPrefetchStrategy = function (url) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (typeof document === `undefined`) {
|
||||||
|
reject();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const link = document.createElement(`link`);
|
||||||
|
link.setAttribute(`rel`, `prefetch`);
|
||||||
|
link.setAttribute(`href`, url);
|
||||||
|
link.onload = resolve;
|
||||||
|
link.onerror = reject;
|
||||||
|
const parentElement = document.getElementsByTagName(`head`)[0] || document.getElementsByName(`script`)[0].parentNode;
|
||||||
|
parentElement.appendChild(link);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const xhrPrefetchStrategy = function (url) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const req = new XMLHttpRequest();
|
||||||
|
req.open(`GET`, url, true);
|
||||||
|
|
||||||
|
req.onload = () => {
|
||||||
|
if (req.status === 200) {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
req.send(null);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const supportedPrefetchStrategy = support(`prefetch`) ? linkPrefetchStrategy : xhrPrefetchStrategy;
|
||||||
|
const preFetched = {};
|
||||||
|
|
||||||
|
const prefetch = function (url) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
if (preFetched[url]) {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
supportedPrefetchStrategy(url).then(() => {
|
||||||
|
resolve();
|
||||||
|
preFetched[url] = true;
|
||||||
|
}).catch(() => {}); // 404s are logged to the console anyway
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var _default = prefetch;
|
||||||
|
exports.default = _default;
|
|
@ -0,0 +1,139 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||||
|
|
||||||
|
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
|
||||||
|
|
||||||
|
var _apiRunnerBrowser = require("./api-runner-browser");
|
||||||
|
|
||||||
|
var _react = _interopRequireDefault(require("react"));
|
||||||
|
|
||||||
|
var _reactDom = _interopRequireDefault(require("react-dom"));
|
||||||
|
|
||||||
|
var _router = require("@reach/router");
|
||||||
|
|
||||||
|
var _gatsbyReactRouterScroll = require("gatsby-react-router-scroll");
|
||||||
|
|
||||||
|
var _domready = _interopRequireDefault(require("@mikaelkristiansson/domready"));
|
||||||
|
|
||||||
|
var _navigation = require("./navigation");
|
||||||
|
|
||||||
|
var _emitter = _interopRequireDefault(require("./emitter"));
|
||||||
|
|
||||||
|
var _pageRenderer = _interopRequireDefault(require("./page-renderer"));
|
||||||
|
|
||||||
|
var _asyncRequires = _interopRequireDefault(require("./async-requires"));
|
||||||
|
|
||||||
|
var _loader = require("./loader");
|
||||||
|
|
||||||
|
var _ensureResources = _interopRequireDefault(require("./ensure-resources"));
|
||||||
|
|
||||||
|
var _stripPrefix = _interopRequireDefault(require("./strip-prefix"));
|
||||||
|
|
||||||
|
var _matchPaths = _interopRequireDefault(require("./match-paths.json"));
|
||||||
|
|
||||||
|
// Generated during bootstrap
|
||||||
|
const loader = new _loader.ProdLoader(_asyncRequires.default, _matchPaths.default);
|
||||||
|
(0, _loader.setLoader)(loader);
|
||||||
|
loader.setApiRunner(_apiRunnerBrowser.apiRunner);
|
||||||
|
window.asyncRequires = _asyncRequires.default;
|
||||||
|
window.___emitter = _emitter.default;
|
||||||
|
window.___loader = _loader.publicLoader;
|
||||||
|
(0, _navigation.init)();
|
||||||
|
(0, _apiRunnerBrowser.apiRunnerAsync)(`onClientEntry`).then(() => {
|
||||||
|
// Let plugins register a service worker. The plugin just needs
|
||||||
|
// to return true.
|
||||||
|
if ((0, _apiRunnerBrowser.apiRunner)(`registerServiceWorker`).length > 0) {
|
||||||
|
require(`./register-service-worker`);
|
||||||
|
} // In gatsby v2 if Router is used in page using matchPaths
|
||||||
|
// paths need to contain full path.
|
||||||
|
// For example:
|
||||||
|
// - page have `/app/*` matchPath
|
||||||
|
// - inside template user needs to use `/app/xyz` as path
|
||||||
|
// Resetting `basepath`/`baseuri` keeps current behaviour
|
||||||
|
// to not introduce breaking change.
|
||||||
|
// Remove this in v3
|
||||||
|
|
||||||
|
|
||||||
|
const RouteHandler = props => _react.default.createElement(_router.BaseContext.Provider, {
|
||||||
|
value: {
|
||||||
|
baseuri: `/`,
|
||||||
|
basepath: `/`
|
||||||
|
}
|
||||||
|
}, _react.default.createElement(_pageRenderer.default, props));
|
||||||
|
|
||||||
|
class LocationHandler extends _react.default.Component {
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
location
|
||||||
|
} = this.props;
|
||||||
|
return _react.default.createElement(_ensureResources.default, {
|
||||||
|
location: location
|
||||||
|
}, ({
|
||||||
|
pageResources,
|
||||||
|
location
|
||||||
|
}) => _react.default.createElement(_navigation.RouteUpdates, {
|
||||||
|
location: location
|
||||||
|
}, _react.default.createElement(_gatsbyReactRouterScroll.ScrollContext, {
|
||||||
|
location: location,
|
||||||
|
shouldUpdateScroll: _navigation.shouldUpdateScroll
|
||||||
|
}, _react.default.createElement(_router.Router, {
|
||||||
|
basepath: __BASE_PATH__,
|
||||||
|
location: location,
|
||||||
|
id: "gatsby-focus-wrapper"
|
||||||
|
}, _react.default.createElement(RouteHandler, (0, _extends2.default)({
|
||||||
|
path: encodeURI(pageResources.page.path === `/404.html` ? (0, _stripPrefix.default)(location.pathname, __BASE_PATH__) : pageResources.page.matchPath || pageResources.page.path)
|
||||||
|
}, this.props, {
|
||||||
|
location: location,
|
||||||
|
pageResources: pageResources
|
||||||
|
}, pageResources.json))))));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
pagePath,
|
||||||
|
location: browserLoc
|
||||||
|
} = window; // Explicitly call navigate if the canonical path (window.pagePath)
|
||||||
|
// is different to the browser path (window.location.pathname). But
|
||||||
|
// only if NONE of the following conditions hold:
|
||||||
|
//
|
||||||
|
// - The url matches a client side route (page.matchPath)
|
||||||
|
// - it's a 404 page
|
||||||
|
// - it's the offline plugin shell (/offline-plugin-app-shell-fallback/)
|
||||||
|
|
||||||
|
if (pagePath && __BASE_PATH__ + pagePath !== browserLoc.pathname && !(loader.findMatchPath((0, _stripPrefix.default)(browserLoc.pathname, __BASE_PATH__)) || pagePath === `/404.html` || pagePath.match(/^\/404\/?$/) || pagePath.match(/^\/offline-plugin-app-shell-fallback\/?$/))) {
|
||||||
|
(0, _router.navigate)(__BASE_PATH__ + pagePath + browserLoc.search + browserLoc.hash, {
|
||||||
|
replace: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_loader.publicLoader.loadPage(browserLoc.pathname).then(page => {
|
||||||
|
if (!page || page.status === `error`) {
|
||||||
|
throw new Error(`page resources for ${browserLoc.pathname} not found. Not rendering React`);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.___webpackCompilationHash = page.page.webpackCompilationHash;
|
||||||
|
|
||||||
|
const Root = () => _react.default.createElement(_router.Location, null, locationContext => _react.default.createElement(LocationHandler, locationContext));
|
||||||
|
|
||||||
|
const WrappedRoot = (0, _apiRunnerBrowser.apiRunner)(`wrapRootElement`, {
|
||||||
|
element: _react.default.createElement(Root, null)
|
||||||
|
}, _react.default.createElement(Root, null), ({
|
||||||
|
result
|
||||||
|
}) => {
|
||||||
|
return {
|
||||||
|
element: result
|
||||||
|
};
|
||||||
|
}).pop();
|
||||||
|
|
||||||
|
let NewRoot = () => WrappedRoot;
|
||||||
|
|
||||||
|
const renderer = (0, _apiRunnerBrowser.apiRunner)(`replaceHydrateFunction`, undefined, _reactDom.default.hydrate)[0];
|
||||||
|
(0, _domready.default)(() => {
|
||||||
|
renderer(_react.default.createElement(NewRoot, null), typeof window !== `undefined` ? document.getElementById(`___gatsby`) : void 0, () => {
|
||||||
|
(0, _apiRunnerBrowser.apiRunner)(`onInitialClientRender`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,33 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||||
|
|
||||||
|
exports.__esModule = true;
|
||||||
|
exports.default = void 0;
|
||||||
|
|
||||||
|
var _react = _interopRequireDefault(require("react"));
|
||||||
|
|
||||||
|
var _propTypes = _interopRequireDefault(require("prop-types"));
|
||||||
|
|
||||||
|
var _loader = _interopRequireDefault(require("./loader"));
|
||||||
|
|
||||||
|
var _jsonStore = _interopRequireDefault(require("./json-store"));
|
||||||
|
|
||||||
|
const DevPageRenderer = ({
|
||||||
|
location
|
||||||
|
}) => {
|
||||||
|
const pageResources = _loader.default.loadPageSync(location.pathname);
|
||||||
|
|
||||||
|
return _react.default.createElement(_jsonStore.default, {
|
||||||
|
location,
|
||||||
|
pageResources
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
DevPageRenderer.propTypes = {
|
||||||
|
location: _propTypes.default.shape({
|
||||||
|
pathname: _propTypes.default.string.isRequired
|
||||||
|
}).isRequired
|
||||||
|
};
|
||||||
|
var _default = DevPageRenderer;
|
||||||
|
exports.default = _default;
|
|
@ -0,0 +1,34 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||||
|
|
||||||
|
exports.__esModule = true;
|
||||||
|
exports.default = void 0;
|
||||||
|
|
||||||
|
var _react = _interopRequireDefault(require("react"));
|
||||||
|
|
||||||
|
var _propTypes = _interopRequireDefault(require("prop-types"));
|
||||||
|
|
||||||
|
var _pageRenderer = _interopRequireDefault(require("./page-renderer"));
|
||||||
|
|
||||||
|
const ProdPageRenderer = ({
|
||||||
|
location,
|
||||||
|
pageResources
|
||||||
|
}) => {
|
||||||
|
if (!pageResources) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _react.default.createElement(_pageRenderer.default, Object.assign({
|
||||||
|
location,
|
||||||
|
pageResources
|
||||||
|
}, pageResources.json));
|
||||||
|
};
|
||||||
|
|
||||||
|
ProdPageRenderer.propTypes = {
|
||||||
|
location: _propTypes.default.shape({
|
||||||
|
pathname: _propTypes.default.string.isRequired
|
||||||
|
}).isRequired
|
||||||
|
};
|
||||||
|
var _default = ProdPageRenderer;
|
||||||
|
exports.default = _default;
|
|
@ -0,0 +1,11 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const preferDefault = m => m && m.default || m;
|
||||||
|
|
||||||
|
if (process.env.BUILD_STAGE === `develop`) {
|
||||||
|
module.exports = preferDefault(require(`./public-page-renderer-dev`));
|
||||||
|
} else if (process.env.BUILD_STAGE === `build-javascript`) {
|
||||||
|
module.exports = preferDefault(require(`./public-page-renderer-prod`));
|
||||||
|
} else {
|
||||||
|
module.exports = () => null;
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
exports.polyfill = Component => Component;
|
|
@ -0,0 +1,66 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var _apiRunnerBrowser = require("./api-runner-browser");
|
||||||
|
|
||||||
|
if (window.location.protocol !== `https:` && window.location.hostname !== `localhost`) {
|
||||||
|
console.error(`Service workers can only be used over HTTPS, or on localhost for development`);
|
||||||
|
} else if (`serviceWorker` in navigator) {
|
||||||
|
navigator.serviceWorker.register(`${__BASE_PATH__}/sw.js`).then(function (reg) {
|
||||||
|
reg.addEventListener(`updatefound`, () => {
|
||||||
|
(0, _apiRunnerBrowser.apiRunner)(`onServiceWorkerUpdateFound`, {
|
||||||
|
serviceWorker: reg
|
||||||
|
}); // The updatefound event implies that reg.installing is set; see
|
||||||
|
// https://w3c.github.io/ServiceWorker/#service-worker-registration-updatefound-event
|
||||||
|
|
||||||
|
const installingWorker = reg.installing;
|
||||||
|
console.log(`installingWorker`, installingWorker);
|
||||||
|
installingWorker.addEventListener(`statechange`, () => {
|
||||||
|
switch (installingWorker.state) {
|
||||||
|
case `installed`:
|
||||||
|
if (navigator.serviceWorker.controller) {
|
||||||
|
// At this point, the old content will have been purged and the fresh content will
|
||||||
|
// have been added to the cache.
|
||||||
|
// We set a flag so Gatsby Link knows to refresh the page on next navigation attempt
|
||||||
|
window.___swUpdated = true; // We call the onServiceWorkerUpdateReady API so users can show update prompts.
|
||||||
|
|
||||||
|
(0, _apiRunnerBrowser.apiRunner)(`onServiceWorkerUpdateReady`, {
|
||||||
|
serviceWorker: reg
|
||||||
|
}); // If resources failed for the current page, reload.
|
||||||
|
|
||||||
|
if (window.___failedResources) {
|
||||||
|
console.log(`resources failed, SW updated - reloading`);
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// At this point, everything has been precached.
|
||||||
|
// It's the perfect time to display a "Content is cached for offline use." message.
|
||||||
|
console.log(`Content is now available offline!`); // Post to service worker that install is complete.
|
||||||
|
// Delay to allow time for the event listener to be added --
|
||||||
|
// otherwise fetch is called too soon and resources aren't cached.
|
||||||
|
|
||||||
|
(0, _apiRunnerBrowser.apiRunner)(`onServiceWorkerInstalled`, {
|
||||||
|
serviceWorker: reg
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case `redundant`:
|
||||||
|
console.error(`The installing service worker became redundant.`);
|
||||||
|
(0, _apiRunnerBrowser.apiRunner)(`onServiceWorkerRedundant`, {
|
||||||
|
serviceWorker: reg
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case `activated`:
|
||||||
|
(0, _apiRunnerBrowser.apiRunner)(`onServiceWorkerActive`, {
|
||||||
|
serviceWorker: reg
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}).catch(function (e) {
|
||||||
|
console.error(`Error during service worker registration:`, e);
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,131 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||||
|
|
||||||
|
exports.__esModule = true;
|
||||||
|
exports.default = void 0;
|
||||||
|
|
||||||
|
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
|
||||||
|
|
||||||
|
var _react = _interopRequireDefault(require("react"));
|
||||||
|
|
||||||
|
var _router = require("@reach/router");
|
||||||
|
|
||||||
|
var _gatsbyReactRouterScroll = require("gatsby-react-router-scroll");
|
||||||
|
|
||||||
|
var _navigation = require("./navigation");
|
||||||
|
|
||||||
|
var _apiRunnerBrowser = require("./api-runner-browser");
|
||||||
|
|
||||||
|
var _loader = _interopRequireDefault(require("./loader"));
|
||||||
|
|
||||||
|
var _jsonStore = _interopRequireDefault(require("./json-store"));
|
||||||
|
|
||||||
|
var _ensureResources = _interopRequireDefault(require("./ensure-resources"));
|
||||||
|
|
||||||
|
var _errorOverlayHandler = require("./error-overlay-handler");
|
||||||
|
|
||||||
|
if (window.__webpack_hot_middleware_reporter__ !== undefined) {
|
||||||
|
const overlayErrorID = `webpack`; // Report build errors
|
||||||
|
|
||||||
|
window.__webpack_hot_middleware_reporter__.useCustomOverlay({
|
||||||
|
showProblems(type, obj) {
|
||||||
|
if (type !== `errors`) {
|
||||||
|
(0, _errorOverlayHandler.clearError)(overlayErrorID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
(0, _errorOverlayHandler.reportError)(overlayErrorID, obj[0]);
|
||||||
|
},
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
(0, _errorOverlayHandler.clearError)(overlayErrorID);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
(0, _navigation.init)(); // In gatsby v2 if Router is used in page using matchPaths
|
||||||
|
// paths need to contain full path.
|
||||||
|
// For example:
|
||||||
|
// - page have `/app/*` matchPath
|
||||||
|
// - inside template user needs to use `/app/xyz` as path
|
||||||
|
// Resetting `basepath`/`baseuri` keeps current behaviour
|
||||||
|
// to not introduce breaking change.
|
||||||
|
// Remove this in v3
|
||||||
|
|
||||||
|
const RouteHandler = props => _react.default.createElement(_router.BaseContext.Provider, {
|
||||||
|
value: {
|
||||||
|
baseuri: `/`,
|
||||||
|
basepath: `/`
|
||||||
|
}
|
||||||
|
}, _react.default.createElement(_jsonStore.default, props));
|
||||||
|
|
||||||
|
class LocationHandler extends _react.default.Component {
|
||||||
|
render() {
|
||||||
|
let {
|
||||||
|
location
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
if (!_loader.default.isPageNotFound(location.pathname)) {
|
||||||
|
return _react.default.createElement(_ensureResources.default, {
|
||||||
|
location: location
|
||||||
|
}, locationAndPageResources => _react.default.createElement(_navigation.RouteUpdates, {
|
||||||
|
location: location
|
||||||
|
}, _react.default.createElement(_gatsbyReactRouterScroll.ScrollContext, {
|
||||||
|
location: location,
|
||||||
|
shouldUpdateScroll: _navigation.shouldUpdateScroll
|
||||||
|
}, _react.default.createElement(_router.Router, {
|
||||||
|
basepath: __BASE_PATH__,
|
||||||
|
location: location,
|
||||||
|
id: "gatsby-focus-wrapper"
|
||||||
|
}, _react.default.createElement(RouteHandler, (0, _extends2.default)({
|
||||||
|
path: encodeURI(locationAndPageResources.pageResources.page.matchPath || locationAndPageResources.pageResources.page.path)
|
||||||
|
}, this.props, locationAndPageResources))))));
|
||||||
|
}
|
||||||
|
|
||||||
|
const dev404PageResources = _loader.default.loadPageSync(`/dev-404-page`);
|
||||||
|
|
||||||
|
const real404PageResources = _loader.default.loadPageSync(`/404.html`);
|
||||||
|
|
||||||
|
let custom404;
|
||||||
|
|
||||||
|
if (real404PageResources) {
|
||||||
|
custom404 = _react.default.createElement(_jsonStore.default, (0, _extends2.default)({}, this.props, {
|
||||||
|
pageResources: real404PageResources
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
return _react.default.createElement(_navigation.RouteUpdates, {
|
||||||
|
location: location
|
||||||
|
}, _react.default.createElement(_router.Router, {
|
||||||
|
basepath: __BASE_PATH__,
|
||||||
|
location: location,
|
||||||
|
id: "gatsby-focus-wrapper"
|
||||||
|
}, _react.default.createElement(RouteHandler, {
|
||||||
|
path: location.pathname,
|
||||||
|
location: location,
|
||||||
|
pageResources: dev404PageResources,
|
||||||
|
custom404: custom404
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const Root = () => _react.default.createElement(_router.Location, null, locationContext => _react.default.createElement(LocationHandler, locationContext)); // Let site, plugins wrap the site e.g. for Redux.
|
||||||
|
|
||||||
|
|
||||||
|
const WrappedRoot = (0, _apiRunnerBrowser.apiRunner)(`wrapRootElement`, {
|
||||||
|
element: _react.default.createElement(Root, null)
|
||||||
|
}, _react.default.createElement(Root, null), ({
|
||||||
|
result,
|
||||||
|
plugin
|
||||||
|
}) => {
|
||||||
|
return {
|
||||||
|
element: result
|
||||||
|
};
|
||||||
|
}).pop();
|
||||||
|
|
||||||
|
var _default = () => WrappedRoot;
|
||||||
|
|
||||||
|
exports.default = _default;
|
|
@ -0,0 +1,122 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||||
|
|
||||||
|
exports.__esModule = true;
|
||||||
|
exports.default = socketIo;
|
||||||
|
exports.getPageData = getPageData;
|
||||||
|
exports.registerPath = registerPath;
|
||||||
|
exports.unregisterPath = unregisterPath;
|
||||||
|
exports.getIsInitialized = exports.getPageQueryData = exports.getStaticQueryData = void 0;
|
||||||
|
|
||||||
|
var _errorOverlayHandler = require("./error-overlay-handler");
|
||||||
|
|
||||||
|
var _normalizePagePath = _interopRequireDefault(require("./normalize-page-path"));
|
||||||
|
|
||||||
|
let socket = null;
|
||||||
|
let staticQueryData = {};
|
||||||
|
let pageQueryData = {};
|
||||||
|
let isInitialized = false;
|
||||||
|
|
||||||
|
const getStaticQueryData = () => staticQueryData;
|
||||||
|
|
||||||
|
exports.getStaticQueryData = getStaticQueryData;
|
||||||
|
|
||||||
|
const getPageQueryData = () => pageQueryData;
|
||||||
|
|
||||||
|
exports.getPageQueryData = getPageQueryData;
|
||||||
|
|
||||||
|
const getIsInitialized = () => isInitialized;
|
||||||
|
|
||||||
|
exports.getIsInitialized = getIsInitialized;
|
||||||
|
|
||||||
|
function socketIo() {
|
||||||
|
if (process.env.NODE_ENV !== `production`) {
|
||||||
|
if (!socket) {
|
||||||
|
// Try to initialize web socket if we didn't do it already
|
||||||
|
try {
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
socket = io();
|
||||||
|
|
||||||
|
const didDataChange = (msg, queryData) => {
|
||||||
|
const id = msg.type === `staticQueryResult` ? msg.payload.id : (0, _normalizePagePath.default)(msg.payload.id);
|
||||||
|
return !(id in queryData) || JSON.stringify(msg.payload.result) !== JSON.stringify(queryData[id]);
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.on(`message`, msg => {
|
||||||
|
if (msg.type === `staticQueryResult`) {
|
||||||
|
if (didDataChange(msg, staticQueryData)) {
|
||||||
|
staticQueryData = Object.assign({}, staticQueryData, {
|
||||||
|
[msg.payload.id]: msg.payload.result
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (msg.type === `pageQueryResult`) {
|
||||||
|
if (didDataChange(msg, pageQueryData)) {
|
||||||
|
pageQueryData = Object.assign({}, pageQueryData, {
|
||||||
|
[(0, _normalizePagePath.default)(msg.payload.id)]: msg.payload.result
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (msg.type === `overlayError`) {
|
||||||
|
if (msg.payload.message) {
|
||||||
|
(0, _errorOverlayHandler.reportError)(msg.payload.id, msg.payload.message);
|
||||||
|
} else {
|
||||||
|
(0, _errorOverlayHandler.clearError)(msg.payload.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.type && msg.payload) {
|
||||||
|
___emitter.emit(msg.type, msg.payload);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Could not connect to socket.io on dev server.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return socket;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const inFlightGetPageDataPromiseCache = {};
|
||||||
|
|
||||||
|
function getPageData(pathname) {
|
||||||
|
pathname = (0, _normalizePagePath.default)(pathname);
|
||||||
|
|
||||||
|
if (inFlightGetPageDataPromiseCache[pathname]) {
|
||||||
|
return inFlightGetPageDataPromiseCache[pathname];
|
||||||
|
} else {
|
||||||
|
inFlightGetPageDataPromiseCache[pathname] = new Promise(resolve => {
|
||||||
|
if (pageQueryData[pathname]) {
|
||||||
|
delete inFlightGetPageDataPromiseCache[pathname];
|
||||||
|
resolve(pageQueryData[pathname]);
|
||||||
|
} else {
|
||||||
|
const onPageDataCallback = msg => {
|
||||||
|
if (msg.type === `pageQueryResult` && (0, _normalizePagePath.default)(msg.payload.id) === pathname) {
|
||||||
|
socket.off(`message`, onPageDataCallback);
|
||||||
|
delete inFlightGetPageDataPromiseCache[pathname];
|
||||||
|
resolve(pageQueryData[pathname]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.on(`message`, onPageDataCallback);
|
||||||
|
socket.emit(`getDataForPath`, pathname);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return inFlightGetPageDataPromiseCache[pathname];
|
||||||
|
} // Tell websocket-manager.js the new path we're on.
|
||||||
|
// This will help the backend prioritize queries for this
|
||||||
|
// path.
|
||||||
|
|
||||||
|
|
||||||
|
function registerPath(path) {
|
||||||
|
socket.emit(`registerPath`, path);
|
||||||
|
} // Unregister the former path
|
||||||
|
|
||||||
|
|
||||||
|
function unregisterPath(path) {
|
||||||
|
socket.emit(`unregisterPath`, path);
|
||||||
|
}
|
|
@ -0,0 +1,406 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||||
|
|
||||||
|
exports.__esModule = true;
|
||||||
|
exports.default = exports.sanitizeComponents = void 0;
|
||||||
|
|
||||||
|
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
|
||||||
|
|
||||||
|
const React = require(`react`);
|
||||||
|
|
||||||
|
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,
|
||||||
|
flattenDeep,
|
||||||
|
replace
|
||||||
|
} = require(`lodash`);
|
||||||
|
|
||||||
|
const apiRunner = require(`./api-runner-ssr`);
|
||||||
|
|
||||||
|
const syncRequires = require(`./sync-requires`);
|
||||||
|
|
||||||
|
const {
|
||||||
|
version: gatsbyVersion
|
||||||
|
} = require(`gatsby/package.json`);
|
||||||
|
|
||||||
|
const stats = JSON.parse(fs.readFileSync(`${process.cwd()}/public/webpack.stats.json`, `utf-8`));
|
||||||
|
const chunkMapping = JSON.parse(fs.readFileSync(`${process.cwd()}/public/chunk-map.json`, `utf-8`)); // const testRequireError = require("./test-require-error")
|
||||||
|
// For some extremely mysterious reason, webpack adds the above module *after*
|
||||||
|
// this module so that when this code runs, testRequireError is undefined.
|
||||||
|
// So in the meantime, we'll just inline it.
|
||||||
|
|
||||||
|
const testRequireError = (moduleName, err) => {
|
||||||
|
const regex = new RegExp(`Error: Cannot find module\\s.${moduleName}`);
|
||||||
|
const firstLine = err.toString().split(`\n`)[0];
|
||||||
|
return regex.test(firstLine);
|
||||||
|
};
|
||||||
|
|
||||||
|
let Html;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Html = require(`../src/html`);
|
||||||
|
} catch (err) {
|
||||||
|
if (testRequireError(`../src/html`, err)) {
|
||||||
|
Html = require(`./default-html`);
|
||||||
|
} else {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Html = Html && Html.__esModule ? Html.default : Html;
|
||||||
|
|
||||||
|
const getPageDataPath = path => {
|
||||||
|
const fixedPagePath = path === `/` ? `index` : path;
|
||||||
|
return join(`page-data`, fixedPagePath, `page-data.json`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPageDataUrl = pagePath => {
|
||||||
|
const pageDataPath = getPageDataPath(pagePath);
|
||||||
|
return `${__PATH_PREFIX__}/${pageDataPath}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPageDataFile = pagePath => {
|
||||||
|
const pageDataPath = getPageDataPath(pagePath);
|
||||||
|
return join(process.cwd(), `public`, pageDataPath);
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadPageDataSync = pagePath => {
|
||||||
|
const pageDataPath = getPageDataPath(pagePath);
|
||||||
|
const pageDataFile = join(process.cwd(), `public`, pageDataPath);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const pageDataJson = fs.readFileSync(pageDataFile);
|
||||||
|
return JSON.parse(pageDataJson);
|
||||||
|
} catch (error) {
|
||||||
|
// not an error if file is not found. There's just no page data
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const createElement = React.createElement;
|
||||||
|
|
||||||
|
const sanitizeComponents = components => {
|
||||||
|
const componentsArray = ensureArray(components);
|
||||||
|
return componentsArray.map(component => {
|
||||||
|
// Ensure manifest is always loaded from content server
|
||||||
|
// And not asset server when an assetPrefix is used
|
||||||
|
if (__ASSET_PREFIX__ && component.props.rel === `manifest`) {
|
||||||
|
return React.cloneElement(component, {
|
||||||
|
href: replace(component.props.href, __ASSET_PREFIX__, ``)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return component;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.sanitizeComponents = sanitizeComponents;
|
||||||
|
|
||||||
|
const ensureArray = components => {
|
||||||
|
if (Array.isArray(components)) {
|
||||||
|
// remove falsy items and flatten
|
||||||
|
return flattenDeep(components.filter(val => Array.isArray(val) ? val.length > 0 : val));
|
||||||
|
} else {
|
||||||
|
// we also accept single components, so we need to handle this case as well
|
||||||
|
return components ? [components] : [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var _default = (pagePath, callback) => {
|
||||||
|
let bodyHtml = ``;
|
||||||
|
let headComponents = [React.createElement("meta", {
|
||||||
|
name: "generator",
|
||||||
|
content: `Gatsby ${gatsbyVersion}`,
|
||||||
|
key: `generator-${gatsbyVersion}`
|
||||||
|
})];
|
||||||
|
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));
|
||||||
|
};
|
||||||
|
|
||||||
|
const setPostBodyComponents = components => {
|
||||||
|
postBodyComponents = postBodyComponents.concat(sanitizeComponents(components));
|
||||||
|
};
|
||||||
|
|
||||||
|
const setBodyProps = props => {
|
||||||
|
bodyProps = merge({}, bodyProps, props);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getHeadComponents = () => headComponents;
|
||||||
|
|
||||||
|
const replaceHeadComponents = components => {
|
||||||
|
headComponents = sanitizeComponents(components);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPreBodyComponents = () => preBodyComponents;
|
||||||
|
|
||||||
|
const replacePreBodyComponents = components => {
|
||||||
|
preBodyComponents = sanitizeComponents(components);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPostBodyComponents = () => postBodyComponents;
|
||||||
|
|
||||||
|
const replacePostBodyComponents = components => {
|
||||||
|
postBodyComponents = sanitizeComponents(components);
|
||||||
|
};
|
||||||
|
|
||||||
|
const pageDataRaw = fs.readFileSync(getPageDataFile(pagePath));
|
||||||
|
const pageData = JSON.parse(pageDataRaw);
|
||||||
|
const pageDataUrl = getPageDataUrl(pagePath);
|
||||||
|
const {
|
||||||
|
componentChunkName
|
||||||
|
} = pageData;
|
||||||
|
|
||||||
|
class RouteHandler extends React.Component {
|
||||||
|
render() {
|
||||||
|
const props = Object.assign({}, this.props, {}, pageData.result, {
|
||||||
|
// pathContext was deprecated in v2. Renamed to pageContext
|
||||||
|
pathContext: pageData.result ? pageData.result.pageContext : undefined
|
||||||
|
});
|
||||||
|
const pageElement = createElement(syncRequires.components[componentChunkName], props);
|
||||||
|
const wrappedPage = apiRunner(`wrapPageElement`, {
|
||||||
|
element: pageElement,
|
||||||
|
props
|
||||||
|
}, pageElement, ({
|
||||||
|
result
|
||||||
|
}) => {
|
||||||
|
return {
|
||||||
|
element: result,
|
||||||
|
props
|
||||||
|
};
|
||||||
|
}).pop();
|
||||||
|
return wrappedPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const routerElement = createElement(ServerLocation, {
|
||||||
|
url: `${__BASE_PATH__}${pagePath}`
|
||||||
|
}, createElement(Router, {
|
||||||
|
id: `gatsby-focus-wrapper`,
|
||||||
|
baseuri: `${__BASE_PATH__}`
|
||||||
|
}, createElement(RouteHandler, {
|
||||||
|
path: `/*`
|
||||||
|
})));
|
||||||
|
const bodyComponent = apiRunner(`wrapRootElement`, {
|
||||||
|
element: routerElement,
|
||||||
|
pathname: pagePath
|
||||||
|
}, routerElement, ({
|
||||||
|
result
|
||||||
|
}) => {
|
||||||
|
return {
|
||||||
|
element: result,
|
||||||
|
pathname: pagePath
|
||||||
|
};
|
||||||
|
}).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;
|
||||||
|
}
|
||||||
|
} // Create paths to scripts
|
||||||
|
|
||||||
|
|
||||||
|
let scriptsAndStyles = flatten([`app`, componentChunkName].map(s => {
|
||||||
|
const fetchKey = `assetsByChunkName[${s}]`;
|
||||||
|
let chunks = get(stats, fetchKey);
|
||||||
|
let namedChunkGroups = get(stats, `namedChunkGroups`);
|
||||||
|
|
||||||
|
if (!chunks) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunks = chunks.map(chunk => {
|
||||||
|
if (chunk === `/`) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
rel: `preload`,
|
||||||
|
name: chunk
|
||||||
|
};
|
||||||
|
});
|
||||||
|
namedChunkGroups[s].assets.forEach(asset => chunks.push({
|
||||||
|
rel: `preload`,
|
||||||
|
name: asset
|
||||||
|
}));
|
||||||
|
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 => 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,
|
||||||
|
setHtmlAttributes,
|
||||||
|
setBodyAttributes,
|
||||||
|
setPreBodyComponents,
|
||||||
|
setPostBodyComponents,
|
||||||
|
setBodyProps,
|
||||||
|
pathname: pagePath,
|
||||||
|
loadPageDataSync,
|
||||||
|
bodyHtml,
|
||||||
|
scripts,
|
||||||
|
styles,
|
||||||
|
pathPrefix: __PATH_PREFIX__
|
||||||
|
});
|
||||||
|
scripts.slice(0).reverse().forEach(script => {
|
||||||
|
// Add preload/prefetch <link>s for scripts.
|
||||||
|
headComponents.push(React.createElement("link", {
|
||||||
|
as: "script",
|
||||||
|
rel: script.rel,
|
||||||
|
key: script.name,
|
||||||
|
href: `${__PATH_PREFIX__}/${script.name}`
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (pageData) {
|
||||||
|
headComponents.push(React.createElement("link", {
|
||||||
|
as: "fetch",
|
||||||
|
rel: "preload",
|
||||||
|
key: pageDataUrl,
|
||||||
|
href: pageDataUrl,
|
||||||
|
crossOrigin: "anonymous"
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
styles.slice(0).reverse().forEach(style => {
|
||||||
|
// Add <link>s for styles that should be prefetched
|
||||||
|
// otherwise, inline as a <style> tag
|
||||||
|
if (style.rel === `prefetch`) {
|
||||||
|
headComponents.push(React.createElement("link", {
|
||||||
|
as: "style",
|
||||||
|
rel: style.rel,
|
||||||
|
key: style.name,
|
||||||
|
href: `${__PATH_PREFIX__}/${style.name}`
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
headComponents.unshift(React.createElement("style", {
|
||||||
|
"data-href": `${__PATH_PREFIX__}/${style.name}`,
|
||||||
|
dangerouslySetInnerHTML: {
|
||||||
|
__html: fs.readFileSync(join(process.cwd(), `public`, style.name), `utf-8`)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}); // Add page metadata for the current page
|
||||||
|
|
||||||
|
const windowPageData = `/*<![CDATA[*/window.pagePath="${pagePath}";/*]]>*/`;
|
||||||
|
postBodyComponents.push(React.createElement("script", {
|
||||||
|
key: `script-loader`,
|
||||||
|
id: `gatsby-script-loader`,
|
||||||
|
dangerouslySetInnerHTML: {
|
||||||
|
__html: windowPageData
|
||||||
|
}
|
||||||
|
})); // Add chunk mapping metadata
|
||||||
|
|
||||||
|
const scriptChunkMapping = `/*<![CDATA[*/window.___chunkMapping=${JSON.stringify(chunkMapping)};/*]]>*/`;
|
||||||
|
postBodyComponents.push(React.createElement("script", {
|
||||||
|
key: `chunk-mapping`,
|
||||||
|
id: `gatsby-chunk-mapping`,
|
||||||
|
dangerouslySetInnerHTML: {
|
||||||
|
__html: scriptChunkMapping
|
||||||
|
}
|
||||||
|
})); // 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 = `${__PATH_PREFIX__}/${JSON.stringify(s.name).slice(1, -1)}`;
|
||||||
|
return React.createElement("script", {
|
||||||
|
key: scriptPath,
|
||||||
|
src: scriptPath,
|
||||||
|
async: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
postBodyComponents.push(...bodyScripts);
|
||||||
|
apiRunner(`onPreRenderHTML`, {
|
||||||
|
getHeadComponents,
|
||||||
|
replaceHeadComponents,
|
||||||
|
getPreBodyComponents,
|
||||||
|
replacePreBodyComponents,
|
||||||
|
getPostBodyComponents,
|
||||||
|
replacePostBodyComponents,
|
||||||
|
pathname: pagePath,
|
||||||
|
pathPrefix: __PATH_PREFIX__
|
||||||
|
});
|
||||||
|
const html = `<!DOCTYPE html>${renderToStaticMarkup(React.createElement(Html, (0, _extends2.default)({}, bodyProps, {
|
||||||
|
headComponents: headComponents,
|
||||||
|
htmlAttributes: htmlAttributes,
|
||||||
|
bodyAttributes: bodyAttributes,
|
||||||
|
preBodyComponents: preBodyComponents,
|
||||||
|
postBodyComponents: postBodyComponents,
|
||||||
|
body: bodyHtml,
|
||||||
|
path: pagePath
|
||||||
|
})))}`;
|
||||||
|
callback(null, html);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.default = _default;
|
|
@ -0,0 +1,15 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
exports.__esModule = true;
|
||||||
|
exports.default = void 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a prefix from a string. Return the input string if the given prefix
|
||||||
|
* isn't found.
|
||||||
|
*/
|
||||||
|
var _default = (str, prefix = ``) => {
|
||||||
|
if (str.substr(0, prefix.length) === prefix) return str.slice(prefix.length);
|
||||||
|
return str;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.default = _default;
|
|
@ -0,0 +1,3 @@
|
||||||
|
import React from "react"
|
||||||
|
|
||||||
|
export default React.createContext
|
|
@ -0,0 +1,39 @@
|
||||||
|
import React from "react"
|
||||||
|
import PropTypes from "prop-types"
|
||||||
|
|
||||||
|
export default function HTML(props) {
|
||||||
|
return (
|
||||||
|
<html {...props.htmlAttributes}>
|
||||||
|
<head>
|
||||||
|
<meta charSet="utf-8" />
|
||||||
|
<meta httpEquiv="x-ua-compatible" content="ie=edge" />
|
||||||
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="width=device-width, initial-scale=1, shrink-to-fit=no"
|
||||||
|
/>
|
||||||
|
{props.headComponents}
|
||||||
|
</head>
|
||||||
|
<body {...props.bodyAttributes}>
|
||||||
|
{props.preBodyComponents}
|
||||||
|
<noscript key="noscript" id="gatsby-noscript">
|
||||||
|
This app works best with JavaScript enabled.
|
||||||
|
</noscript>
|
||||||
|
<div
|
||||||
|
key={`body`}
|
||||||
|
id="___gatsby"
|
||||||
|
dangerouslySetInnerHTML={{ __html: props.body }}
|
||||||
|
/>
|
||||||
|
{props.postBodyComponents}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
HTML.propTypes = {
|
||||||
|
htmlAttributes: PropTypes.object,
|
||||||
|
headComponents: PropTypes.array,
|
||||||
|
bodyAttributes: PropTypes.object,
|
||||||
|
preBodyComponents: PropTypes.array,
|
||||||
|
body: PropTypes.string,
|
||||||
|
postBodyComponents: PropTypes.array,
|
||||||
|
}
|
|
@ -0,0 +1,145 @@
|
||||||
|
import React from "react"
|
||||||
|
import PropTypes from "prop-types"
|
||||||
|
import { graphql, Link } from "gatsby"
|
||||||
|
|
||||||
|
class Dev404Page extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
data: PropTypes.object,
|
||||||
|
custom404: PropTypes.element,
|
||||||
|
location: PropTypes.object,
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
const { data } = this.props
|
||||||
|
const pagePaths = data.allSitePage.nodes.map(node => node.path)
|
||||||
|
this.state = {
|
||||||
|
showCustom404: false,
|
||||||
|
initPagePaths: pagePaths,
|
||||||
|
pagePaths: pagePaths,
|
||||||
|
pagePathSearchTerms: ``,
|
||||||
|
}
|
||||||
|
this.showCustom404 = this.showCustom404.bind(this)
|
||||||
|
this.handlePagePathSearch = this.handlePagePathSearch.bind(this)
|
||||||
|
this.handleSearchTermChange = this.handleSearchTermChange.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
showCustom404() {
|
||||||
|
this.setState({ showCustom404: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSearchTermChange(event) {
|
||||||
|
this.setState({
|
||||||
|
pagePathSearchTerms: event.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePagePathSearch(event) {
|
||||||
|
event.preventDefault()
|
||||||
|
const tempPagePaths = [...this.state.initPagePaths]
|
||||||
|
const searchTerm = new RegExp(`${this.state.pagePathSearchTerms}`)
|
||||||
|
this.setState({
|
||||||
|
pagePaths: tempPagePaths.filter(pagePath => searchTerm.test(pagePath)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { pathname } = this.props.location
|
||||||
|
let newFilePath
|
||||||
|
if (pathname === `/`) {
|
||||||
|
newFilePath = `src/pages/index.js`
|
||||||
|
} else if (pathname.slice(-1) === `/`) {
|
||||||
|
newFilePath = `src/pages${pathname.slice(0, -1)}.js`
|
||||||
|
} else {
|
||||||
|
newFilePath = `src/pages${pathname}.js`
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.state.showCustom404 ? (
|
||||||
|
this.props.custom404
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
<h1>Gatsby.js development 404 page</h1>
|
||||||
|
<p>
|
||||||
|
{`There's not a page yet at `}
|
||||||
|
<code>{pathname}</code>
|
||||||
|
</p>
|
||||||
|
{this.props.custom404 ? (
|
||||||
|
<p>
|
||||||
|
<button onClick={this.showCustom404}>
|
||||||
|
Preview custom 404 page
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
<p>
|
||||||
|
{`A custom 404 page wasn't detected - if you would like to add one, create a component in your site directory at `}
|
||||||
|
<code>src/pages/404.js</code>.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
<p>
|
||||||
|
Create a React.js component in your site directory at
|
||||||
|
{` `}
|
||||||
|
<code>{newFilePath}</code>
|
||||||
|
{` `}
|
||||||
|
and this page will automatically refresh to show the new page
|
||||||
|
component you created.
|
||||||
|
</p>
|
||||||
|
{this.state.initPagePaths.length > 0 && (
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
If you were trying to reach another page, perhaps you can find it
|
||||||
|
below.
|
||||||
|
</p>
|
||||||
|
<h2>
|
||||||
|
Pages (
|
||||||
|
{this.state.pagePaths.length != this.state.initPagePaths.length
|
||||||
|
? `${this.state.pagePaths.length}/${this.state.initPagePaths.length}`
|
||||||
|
: this.state.initPagePaths.length}
|
||||||
|
)
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<form onSubmit={this.handlePagePathSearch}>
|
||||||
|
<label>
|
||||||
|
Search:
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="search"
|
||||||
|
placeholder="Search pages..."
|
||||||
|
value={this.state.pageSearchTerm}
|
||||||
|
onChange={this.handleSearchTermChange}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<input type="submit" value="Submit" />
|
||||||
|
</form>
|
||||||
|
<ul>
|
||||||
|
{this.state.pagePaths.map(
|
||||||
|
(pagePath, index) =>
|
||||||
|
index < 100 && (
|
||||||
|
<li key={pagePath}>
|
||||||
|
<Link to={pagePath}>{pagePath}</Link>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
{this.state.pagePaths.length > 100 && (
|
||||||
|
<p style={{ fontWeight: `bold` }}>
|
||||||
|
... and {this.state.pagePaths.length - 100} more.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Dev404Page
|
||||||
|
|
||||||
|
export const pagesQuery = graphql`
|
||||||
|
query PagesQuery {
|
||||||
|
allSitePage(filter: { path: { ne: "/dev-404-page/" } }) {
|
||||||
|
nodes {
|
||||||
|
path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
|
@ -0,0 +1,39 @@
|
||||||
|
import { BaseLoader } from "./loader"
|
||||||
|
import { findPath } from "./find-path"
|
||||||
|
|
||||||
|
class DevLoader extends BaseLoader {
|
||||||
|
constructor(syncRequires, matchPaths) {
|
||||||
|
const loadComponent = chunkName =>
|
||||||
|
Promise.resolve(syncRequires.components[chunkName])
|
||||||
|
super(loadComponent, matchPaths)
|
||||||
|
}
|
||||||
|
|
||||||
|
loadPage(pagePath) {
|
||||||
|
const realPath = findPath(pagePath)
|
||||||
|
return super.loadPage(realPath).then(result =>
|
||||||
|
require(`./socketIo`)
|
||||||
|
.getPageData(realPath)
|
||||||
|
.then(() => result)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
loadPageDataJson(rawPath) {
|
||||||
|
return super.loadPageDataJson(rawPath).then(data => {
|
||||||
|
// when we can't find a proper 404.html we fallback to dev-404-page
|
||||||
|
// we need to make sure to mark it as not found.
|
||||||
|
if (data.status === `failure`) {
|
||||||
|
return this.loadPageDataJson(`/dev-404-page/`).then(result =>
|
||||||
|
Object.assign({}, data, result)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
doPrefetch(pagePath) {
|
||||||
|
return Promise.resolve(require(`./socketIo`).getPageData(pagePath))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DevLoader
|
|
@ -0,0 +1,119 @@
|
||||||
|
import React from "react"
|
||||||
|
import { renderToStaticMarkup } from "react-dom/server"
|
||||||
|
import { merge } from "lodash"
|
||||||
|
import apiRunner from "./api-runner-ssr"
|
||||||
|
// import testRequireError from "./test-require-error"
|
||||||
|
// For some extremely mysterious reason, webpack adds the above module *after*
|
||||||
|
// this module so that when this code runs, testRequireError is undefined.
|
||||||
|
// So in the meantime, we'll just inline it.
|
||||||
|
const testRequireError = (moduleName, err) => {
|
||||||
|
const regex = new RegExp(`Error: Cannot find module\\s.${moduleName}`)
|
||||||
|
const firstLine = err.toString().split(`\n`)[0]
|
||||||
|
return regex.test(firstLine)
|
||||||
|
}
|
||||||
|
|
||||||
|
let Html
|
||||||
|
try {
|
||||||
|
Html = require(`../src/html`)
|
||||||
|
} catch (err) {
|
||||||
|
if (testRequireError(`../src/html`, err)) {
|
||||||
|
Html = require(`./default-html`)
|
||||||
|
} else {
|
||||||
|
console.log(`There was an error requiring "src/html.js"\n\n`, err, `\n\n`)
|
||||||
|
process.exit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Html = Html && Html.__esModule ? Html.default : Html
|
||||||
|
|
||||||
|
export default (pagePath, callback) => {
|
||||||
|
let headComponents = [
|
||||||
|
<meta key="environment" name="note" content="environment=development" />,
|
||||||
|
]
|
||||||
|
let htmlAttributes = {}
|
||||||
|
let bodyAttributes = {}
|
||||||
|
let preBodyComponents = []
|
||||||
|
let postBodyComponents = []
|
||||||
|
let bodyProps = {}
|
||||||
|
let htmlStr
|
||||||
|
|
||||||
|
const setHeadComponents = components => {
|
||||||
|
headComponents = headComponents.concat(components)
|
||||||
|
}
|
||||||
|
|
||||||
|
const setHtmlAttributes = attributes => {
|
||||||
|
htmlAttributes = merge(htmlAttributes, attributes)
|
||||||
|
}
|
||||||
|
|
||||||
|
const setBodyAttributes = attributes => {
|
||||||
|
bodyAttributes = merge(bodyAttributes, attributes)
|
||||||
|
}
|
||||||
|
|
||||||
|
const setPreBodyComponents = components => {
|
||||||
|
preBodyComponents = preBodyComponents.concat(components)
|
||||||
|
}
|
||||||
|
|
||||||
|
const setPostBodyComponents = components => {
|
||||||
|
postBodyComponents = postBodyComponents.concat(components)
|
||||||
|
}
|
||||||
|
|
||||||
|
const setBodyProps = props => {
|
||||||
|
bodyProps = merge({}, bodyProps, props)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getHeadComponents = () => headComponents
|
||||||
|
|
||||||
|
const replaceHeadComponents = components => {
|
||||||
|
headComponents = components
|
||||||
|
}
|
||||||
|
|
||||||
|
const getPreBodyComponents = () => preBodyComponents
|
||||||
|
|
||||||
|
const replacePreBodyComponents = components => {
|
||||||
|
preBodyComponents = components
|
||||||
|
}
|
||||||
|
|
||||||
|
const getPostBodyComponents = () => postBodyComponents
|
||||||
|
|
||||||
|
const replacePostBodyComponents = components => {
|
||||||
|
postBodyComponents = components
|
||||||
|
}
|
||||||
|
|
||||||
|
apiRunner(`onRenderBody`, {
|
||||||
|
setHeadComponents,
|
||||||
|
setHtmlAttributes,
|
||||||
|
setBodyAttributes,
|
||||||
|
setPreBodyComponents,
|
||||||
|
setPostBodyComponents,
|
||||||
|
setBodyProps,
|
||||||
|
pathname: pagePath,
|
||||||
|
})
|
||||||
|
|
||||||
|
apiRunner(`onPreRenderHTML`, {
|
||||||
|
getHeadComponents,
|
||||||
|
replaceHeadComponents,
|
||||||
|
getPreBodyComponents,
|
||||||
|
replacePreBodyComponents,
|
||||||
|
getPostBodyComponents,
|
||||||
|
replacePostBodyComponents,
|
||||||
|
pathname: pagePath,
|
||||||
|
})
|
||||||
|
|
||||||
|
const htmlElement = React.createElement(Html, {
|
||||||
|
...bodyProps,
|
||||||
|
body: ``,
|
||||||
|
headComponents: headComponents.concat([
|
||||||
|
<script key={`io`} src="/socket.io/socket.io.js" />,
|
||||||
|
]),
|
||||||
|
htmlAttributes,
|
||||||
|
bodyAttributes,
|
||||||
|
preBodyComponents,
|
||||||
|
postBodyComponents: postBodyComponents.concat([
|
||||||
|
<script key={`commons`} src="/commons.js" />,
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
htmlStr = renderToStaticMarkup(htmlElement)
|
||||||
|
htmlStr = `<!DOCTYPE html>${htmlStr}`
|
||||||
|
|
||||||
|
callback(null, htmlStr)
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
import mitt from "mitt"
|
||||||
|
|
||||||
|
const emitter = mitt()
|
||||||
|
export default emitter
|
|
@ -0,0 +1,81 @@
|
||||||
|
import React from "react"
|
||||||
|
import loader from "./loader"
|
||||||
|
import shallowCompare from "shallow-compare"
|
||||||
|
|
||||||
|
class EnsureResources extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super()
|
||||||
|
const { location, pageResources } = props
|
||||||
|
this.state = {
|
||||||
|
location: { ...location },
|
||||||
|
pageResources: pageResources || loader.loadPageSync(location.pathname),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDerivedStateFromProps({ location }, prevState) {
|
||||||
|
if (prevState.location.href !== location.href) {
|
||||||
|
const pageResources = loader.loadPageSync(location.pathname)
|
||||||
|
return {
|
||||||
|
pageResources,
|
||||||
|
location: { ...location },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
location: { ...location },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadResources(rawPath) {
|
||||||
|
loader.loadPage(rawPath).then(pageResources => {
|
||||||
|
if (pageResources && pageResources.status !== `error`) {
|
||||||
|
this.setState({
|
||||||
|
location: { ...window.location },
|
||||||
|
pageResources,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
window.history.replaceState({}, ``, location.href)
|
||||||
|
window.location = rawPath
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
|
// Always return false if we're missing resources.
|
||||||
|
if (!nextState.pageResources) {
|
||||||
|
this.loadResources(nextProps.location.pathname)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the component or json have changed.
|
||||||
|
if (this.state.pageResources !== nextState.pageResources) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
this.state.pageResources.component !== nextState.pageResources.component
|
||||||
|
) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state.pageResources.json !== nextState.pageResources.json) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Check if location has changed on a page using internal routing
|
||||||
|
// via matchPath configuration.
|
||||||
|
if (
|
||||||
|
this.state.location.key !== nextState.location.key &&
|
||||||
|
nextState.pageResources.page &&
|
||||||
|
(nextState.pageResources.page.matchPath ||
|
||||||
|
nextState.pageResources.page.path)
|
||||||
|
) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return shallowCompare(this, nextProps, nextState)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return this.props.children(this.state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EnsureResources
|
|
@ -0,0 +1,65 @@
|
||||||
|
import * as ErrorOverlay from "react-error-overlay"
|
||||||
|
|
||||||
|
// Report runtime errors
|
||||||
|
ErrorOverlay.startReportingRuntimeErrors({
|
||||||
|
onError: () => {},
|
||||||
|
filename: `/commons.js`,
|
||||||
|
})
|
||||||
|
ErrorOverlay.setEditorHandler(errorLocation =>
|
||||||
|
window.fetch(
|
||||||
|
`/__open-stack-frame-in-editor?fileName=` +
|
||||||
|
window.encodeURIComponent(errorLocation.fileName) +
|
||||||
|
`&lineNumber=` +
|
||||||
|
window.encodeURIComponent(errorLocation.lineNumber || 1)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
const errorMap = {}
|
||||||
|
|
||||||
|
function flat(arr) {
|
||||||
|
return Array.prototype.flat ? arr.flat() : [].concat(...arr)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleErrorOverlay = () => {
|
||||||
|
const errors = Object.values(errorMap)
|
||||||
|
let errorStringsToDisplay = []
|
||||||
|
if (errors.length > 0) {
|
||||||
|
errorStringsToDisplay = flat(errors)
|
||||||
|
.map(error => {
|
||||||
|
if (typeof error === `string`) {
|
||||||
|
return error
|
||||||
|
} else if (typeof error === `object`) {
|
||||||
|
const errorStrBuilder = [error.text]
|
||||||
|
|
||||||
|
if (error.filePath) {
|
||||||
|
errorStrBuilder.push(`File: ${error.filePath}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return errorStrBuilder.join(`\n\n`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorStringsToDisplay.length > 0) {
|
||||||
|
ErrorOverlay.reportBuildError(errorStringsToDisplay.join(`\n\n`))
|
||||||
|
} else {
|
||||||
|
ErrorOverlay.dismissBuildError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const clearError = errorID => {
|
||||||
|
delete errorMap[errorID]
|
||||||
|
handleErrorOverlay()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const reportError = (errorID, error) => {
|
||||||
|
if (error) {
|
||||||
|
errorMap[errorID] = error
|
||||||
|
}
|
||||||
|
handleErrorOverlay()
|
||||||
|
}
|
||||||
|
|
||||||
|
export { errorMap }
|
|
@ -0,0 +1,93 @@
|
||||||
|
import { match } from "@reach/router/lib/utils"
|
||||||
|
import stripPrefix from "./strip-prefix"
|
||||||
|
import normalizePagePath from "./normalize-page-path"
|
||||||
|
|
||||||
|
const pathCache = new Map()
|
||||||
|
let matchPaths = []
|
||||||
|
|
||||||
|
const trimPathname = rawPathname => {
|
||||||
|
let pathname = decodeURIComponent(rawPathname)
|
||||||
|
// Remove the pathPrefix from the pathname.
|
||||||
|
let trimmedPathname = stripPrefix(pathname, __BASE_PATH__)
|
||||||
|
// Remove any hashfragment
|
||||||
|
.split(`#`)[0]
|
||||||
|
// Remove search query
|
||||||
|
.split(`?`)[0]
|
||||||
|
|
||||||
|
return trimmedPathname
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set list of matchPaths
|
||||||
|
*
|
||||||
|
* @param {Array<{path: string, matchPath: string}>} value collection of matchPaths
|
||||||
|
*/
|
||||||
|
export const setMatchPaths = value => {
|
||||||
|
matchPaths = value
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a matchpath url
|
||||||
|
* if `match-paths.json` contains `{ "/foo*": "/page1", ...}`, then
|
||||||
|
* `/foo?bar=far` => `/page1`
|
||||||
|
*
|
||||||
|
* @param {string} rawPathname A raw pathname
|
||||||
|
* @return {string|null}
|
||||||
|
*/
|
||||||
|
export const findMatchPath = rawPathname => {
|
||||||
|
const trimmedPathname = cleanPath(rawPathname)
|
||||||
|
|
||||||
|
for (const { matchPath, path } of matchPaths) {
|
||||||
|
if (match(matchPath, trimmedPathname)) {
|
||||||
|
return normalizePagePath(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a raw URL path, returns the cleaned version of it (trim off
|
||||||
|
// `#` and query params), or if it matches an entry in
|
||||||
|
// `match-paths.json`, its matched path is returned
|
||||||
|
//
|
||||||
|
// E.g `/foo?bar=far` => `/foo`
|
||||||
|
//
|
||||||
|
// Or if `match-paths.json` contains `{ "/foo*": "/page1", ...}`, then
|
||||||
|
// `/foo?bar=far` => `/page1`
|
||||||
|
export const findPath = rawPathname => {
|
||||||
|
const trimmedPathname = trimPathname(rawPathname)
|
||||||
|
|
||||||
|
if (pathCache.has(trimmedPathname)) {
|
||||||
|
return pathCache.get(trimmedPathname)
|
||||||
|
}
|
||||||
|
|
||||||
|
let foundPath = findMatchPath(trimmedPathname)
|
||||||
|
|
||||||
|
if (!foundPath) {
|
||||||
|
foundPath = cleanPath(rawPathname)
|
||||||
|
}
|
||||||
|
|
||||||
|
pathCache.set(trimmedPathname, foundPath)
|
||||||
|
|
||||||
|
return foundPath
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean a url and converts /index.html => /
|
||||||
|
* E.g `/foo?bar=far` => `/foo`
|
||||||
|
*
|
||||||
|
* @param {string} rawPathname A raw pathname
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
export const cleanPath = rawPathname => {
|
||||||
|
const trimmedPathname = trimPathname(rawPathname)
|
||||||
|
|
||||||
|
let foundPath = trimmedPathname
|
||||||
|
if (foundPath === `/index.html`) {
|
||||||
|
foundPath = `/`
|
||||||
|
}
|
||||||
|
|
||||||
|
foundPath = normalizePagePath(foundPath)
|
||||||
|
|
||||||
|
return foundPath
|
||||||
|
}
|
|
@ -0,0 +1,321 @@
|
||||||
|
/* eslint-disable */
|
||||||
|
import { graphql } from "gatsby"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The simplest set of fields for fixed sharp images
|
||||||
|
* @type {Fragment}
|
||||||
|
* @example
|
||||||
|
* childImageSharp {
|
||||||
|
* fixed {
|
||||||
|
* ...GatsbyImageSharpFixed
|
||||||
|
* # ^ identical to using the following fields:
|
||||||
|
* # base64
|
||||||
|
* # width
|
||||||
|
* # height
|
||||||
|
* # src
|
||||||
|
* # srcSet
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
export const GatsbyImageSharpFixed = graphql`
|
||||||
|
fragment GatsbyImageSharpFixed on ImageSharpFixed {
|
||||||
|
base64
|
||||||
|
width
|
||||||
|
height
|
||||||
|
src
|
||||||
|
srcSet
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traced SVG fixed images
|
||||||
|
* @type {Fragment}
|
||||||
|
*/
|
||||||
|
export const GatsbyImageSharpFixed_tracedSVG = graphql`
|
||||||
|
fragment GatsbyImageSharpFixed_tracedSVG on ImageSharpFixed {
|
||||||
|
tracedSVG
|
||||||
|
width
|
||||||
|
height
|
||||||
|
src
|
||||||
|
srcSet
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Images using Webp for fixed images
|
||||||
|
* @type {Fragment}
|
||||||
|
*/
|
||||||
|
export const GatsbyImageSharpFixed_withWebp = graphql`
|
||||||
|
fragment GatsbyImageSharpFixed_withWebp on ImageSharpFixed {
|
||||||
|
base64
|
||||||
|
width
|
||||||
|
height
|
||||||
|
src
|
||||||
|
srcSet
|
||||||
|
srcWebp
|
||||||
|
srcSetWebp
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traced SVG images using Webp for fixed images
|
||||||
|
* @type {Fragment}
|
||||||
|
*/
|
||||||
|
export const GatsbyImageSharpFixed_withWebp_tracedSVG = graphql`
|
||||||
|
fragment GatsbyImageSharpFixed_withWebp_tracedSVG on ImageSharpFixed {
|
||||||
|
tracedSVG
|
||||||
|
width
|
||||||
|
height
|
||||||
|
src
|
||||||
|
srcSet
|
||||||
|
srcWebp
|
||||||
|
srcSetWebp
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fixed images without the blurred base64 image
|
||||||
|
* @type {Fragment}
|
||||||
|
*/
|
||||||
|
export const GatsbyImageSharpFixed_noBase64 = graphql`
|
||||||
|
fragment GatsbyImageSharpFixed_noBase64 on ImageSharpFixed {
|
||||||
|
width
|
||||||
|
height
|
||||||
|
src
|
||||||
|
srcSet
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fixed images without the blurred base64 image preferring Webp
|
||||||
|
* @type {Fragment}
|
||||||
|
*/
|
||||||
|
export const GatsbyImageSharpFixed_withWebp_noBase64 = graphql`
|
||||||
|
fragment GatsbyImageSharpFixed_withWebp_noBase64 on ImageSharpFixed {
|
||||||
|
width
|
||||||
|
height
|
||||||
|
src
|
||||||
|
srcSet
|
||||||
|
srcWebp
|
||||||
|
srcSetWebp
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The simplest set of fields for fluid images
|
||||||
|
* @type {Fragment}
|
||||||
|
*/
|
||||||
|
export const GatsbyImageSharpFluid = graphql`
|
||||||
|
fragment GatsbyImageSharpFluid on ImageSharpFluid {
|
||||||
|
base64
|
||||||
|
aspectRatio
|
||||||
|
src
|
||||||
|
srcSet
|
||||||
|
sizes
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traced SVG fluid images
|
||||||
|
* @type {Fragment}
|
||||||
|
*/
|
||||||
|
export const GatsbyImageSharpFluid_tracedSVG = graphql`
|
||||||
|
fragment GatsbyImageSharpFluid_tracedSVG on ImageSharpFluid {
|
||||||
|
tracedSVG
|
||||||
|
aspectRatio
|
||||||
|
src
|
||||||
|
srcSet
|
||||||
|
sizes
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fluid images that prefer Webp
|
||||||
|
* @type {Fragment}
|
||||||
|
*/
|
||||||
|
export const GatsbyImageSharpFluid_withWebp = graphql`
|
||||||
|
fragment GatsbyImageSharpFluid_withWebp on ImageSharpFluid {
|
||||||
|
base64
|
||||||
|
aspectRatio
|
||||||
|
src
|
||||||
|
srcSet
|
||||||
|
srcWebp
|
||||||
|
srcSetWebp
|
||||||
|
sizes
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traced SVG fluid images that prefer Webp
|
||||||
|
* @type {Fragment}
|
||||||
|
*/
|
||||||
|
export const GatsbyImageSharpFluid_withWebp_tracedSVG = graphql`
|
||||||
|
fragment GatsbyImageSharpFluid_withWebp_tracedSVG on ImageSharpFluid {
|
||||||
|
tracedSVG
|
||||||
|
aspectRatio
|
||||||
|
src
|
||||||
|
srcSet
|
||||||
|
srcWebp
|
||||||
|
srcSetWebp
|
||||||
|
sizes
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traced SVG fluid images without the blurred base64 image
|
||||||
|
* @type {Fragment}
|
||||||
|
*/
|
||||||
|
export const GatsbyImageSharpFluid_noBase64 = graphql`
|
||||||
|
fragment GatsbyImageSharpFluid_noBase64 on ImageSharpFluid {
|
||||||
|
aspectRatio
|
||||||
|
src
|
||||||
|
srcSet
|
||||||
|
sizes
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traced SVG fluid images without the blurred base64 image that prefer Webp
|
||||||
|
* @type {Fragment}
|
||||||
|
*/
|
||||||
|
export const GatsbyImageSharpFluid_withWebp_noBase64 = graphql`
|
||||||
|
fragment GatsbyImageSharpFluid_withWebp_noBase64 on ImageSharpFluid {
|
||||||
|
aspectRatio
|
||||||
|
src
|
||||||
|
srcSet
|
||||||
|
srcWebp
|
||||||
|
srcSetWebp
|
||||||
|
sizes
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
// TODO: in v3 remove these legacy fragments
|
||||||
|
export const GatsbyImageSharpResolutions = graphql`
|
||||||
|
fragment GatsbyImageSharpResolutions on ImageSharpResolutions {
|
||||||
|
base64
|
||||||
|
width
|
||||||
|
height
|
||||||
|
src
|
||||||
|
srcSet
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const GatsbyImageSharpResolutions_tracedSVG = graphql`
|
||||||
|
fragment GatsbyImageSharpResolutions_tracedSVG on ImageSharpResolutions {
|
||||||
|
tracedSVG
|
||||||
|
width
|
||||||
|
height
|
||||||
|
src
|
||||||
|
srcSet
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const GatsbyImageSharpResolutions_withWebp = graphql`
|
||||||
|
fragment GatsbyImageSharpResolutions_withWebp on ImageSharpResolutions {
|
||||||
|
base64
|
||||||
|
width
|
||||||
|
height
|
||||||
|
src
|
||||||
|
srcSet
|
||||||
|
srcWebp
|
||||||
|
srcSetWebp
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const GatsbyImageSharpResolutions_withWebp_tracedSVG = graphql`
|
||||||
|
fragment GatsbyImageSharpResolutions_withWebp_tracedSVG on ImageSharpResolutions {
|
||||||
|
tracedSVG
|
||||||
|
width
|
||||||
|
height
|
||||||
|
src
|
||||||
|
srcSet
|
||||||
|
srcWebp
|
||||||
|
srcSetWebp
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const GatsbyImageSharpResolutions_noBase64 = graphql`
|
||||||
|
fragment GatsbyImageSharpResolutions_noBase64 on ImageSharpResolutions {
|
||||||
|
width
|
||||||
|
height
|
||||||
|
src
|
||||||
|
srcSet
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const GatsbyImageSharpResolutions_withWebp_noBase64 = graphql`
|
||||||
|
fragment GatsbyImageSharpResolutions_withWebp_noBase64 on ImageSharpResolutions {
|
||||||
|
width
|
||||||
|
height
|
||||||
|
src
|
||||||
|
srcSet
|
||||||
|
srcWebp
|
||||||
|
srcSetWebp
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const GatsbyImageSharpSizes = graphql`
|
||||||
|
fragment GatsbyImageSharpSizes on ImageSharpSizes {
|
||||||
|
base64
|
||||||
|
aspectRatio
|
||||||
|
src
|
||||||
|
srcSet
|
||||||
|
sizes
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const GatsbyImageSharpSizes_tracedSVG = graphql`
|
||||||
|
fragment GatsbyImageSharpSizes_tracedSVG on ImageSharpSizes {
|
||||||
|
tracedSVG
|
||||||
|
aspectRatio
|
||||||
|
src
|
||||||
|
srcSet
|
||||||
|
sizes
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const GatsbyImageSharpSizes_withWebp = graphql`
|
||||||
|
fragment GatsbyImageSharpSizes_withWebp on ImageSharpSizes {
|
||||||
|
base64
|
||||||
|
aspectRatio
|
||||||
|
src
|
||||||
|
srcSet
|
||||||
|
srcWebp
|
||||||
|
srcSetWebp
|
||||||
|
sizes
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const GatsbyImageSharpSizes_withWebp_tracedSVG = graphql`
|
||||||
|
fragment GatsbyImageSharpSizes_withWebp_tracedSVG on ImageSharpSizes {
|
||||||
|
tracedSVG
|
||||||
|
aspectRatio
|
||||||
|
src
|
||||||
|
srcSet
|
||||||
|
srcWebp
|
||||||
|
srcSetWebp
|
||||||
|
sizes
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const GatsbyImageSharpSizes_noBase64 = graphql`
|
||||||
|
fragment GatsbyImageSharpSizes_noBase64 on ImageSharpSizes {
|
||||||
|
aspectRatio
|
||||||
|
src
|
||||||
|
srcSet
|
||||||
|
sizes
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const GatsbyImageSharpSizes_withWebp_noBase64 = graphql`
|
||||||
|
fragment GatsbyImageSharpSizes_withWebp_noBase64 on ImageSharpSizes {
|
||||||
|
aspectRatio
|
||||||
|
src
|
||||||
|
srcSet
|
||||||
|
srcWebp
|
||||||
|
srcSetWebp
|
||||||
|
sizes
|
||||||
|
}
|
||||||
|
`
|
|
@ -0,0 +1,102 @@
|
||||||
|
import React from "react"
|
||||||
|
import PropTypes from "prop-types"
|
||||||
|
import Link, {
|
||||||
|
withPrefix,
|
||||||
|
withAssetPrefix,
|
||||||
|
navigate,
|
||||||
|
push,
|
||||||
|
replace,
|
||||||
|
navigateTo,
|
||||||
|
parsePath,
|
||||||
|
} from "gatsby-link"
|
||||||
|
import PageRenderer from "./public-page-renderer"
|
||||||
|
import loader from "./loader"
|
||||||
|
|
||||||
|
const prefetchPathname = loader.enqueue
|
||||||
|
|
||||||
|
const StaticQueryContext = React.createContext({})
|
||||||
|
|
||||||
|
function StaticQueryDataRenderer({ staticQueryData, data, query, render }) {
|
||||||
|
const finalData = data
|
||||||
|
? data.data
|
||||||
|
: staticQueryData[query] && staticQueryData[query].data
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{finalData && render(finalData)}
|
||||||
|
{!finalData && <div>Loading (StaticQuery)</div>}
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const StaticQuery = props => {
|
||||||
|
const { data, query, render, children } = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StaticQueryContext.Consumer>
|
||||||
|
{staticQueryData => (
|
||||||
|
<StaticQueryDataRenderer
|
||||||
|
data={data}
|
||||||
|
query={query}
|
||||||
|
render={render || children}
|
||||||
|
staticQueryData={staticQueryData}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</StaticQueryContext.Consumer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStaticQuery = query => {
|
||||||
|
if (
|
||||||
|
typeof React.useContext !== `function` &&
|
||||||
|
process.env.NODE_ENV === `development`
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
`You're likely using a version of React that doesn't support Hooks\n` +
|
||||||
|
`Please update React and ReactDOM to 16.8.0 or later to use the useStaticQuery hook.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const context = React.useContext(StaticQueryContext)
|
||||||
|
if (context[query] && context[query].data) {
|
||||||
|
return context[query].data
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
`The result of this StaticQuery could not be fetched.\n\n` +
|
||||||
|
`This is likely a bug in Gatsby and if refreshing the page does not fix it, ` +
|
||||||
|
`please open an issue in https://github.com/gatsbyjs/gatsby/issues`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StaticQuery.propTypes = {
|
||||||
|
data: PropTypes.object,
|
||||||
|
query: PropTypes.string.isRequired,
|
||||||
|
render: PropTypes.func,
|
||||||
|
children: PropTypes.func,
|
||||||
|
}
|
||||||
|
|
||||||
|
function graphql() {
|
||||||
|
throw new Error(
|
||||||
|
`It appears like Gatsby is misconfigured. Gatsby related \`graphql\` calls ` +
|
||||||
|
`are supposed to only be evaluated at compile time, and then compiled away. ` +
|
||||||
|
`Unfortunately, something went wrong and the query was left in the compiled code.\n\n` +
|
||||||
|
`Unless your site has a complex or custom babel/Gatsby configuration this is likely a bug in Gatsby.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
Link,
|
||||||
|
withAssetPrefix,
|
||||||
|
withPrefix,
|
||||||
|
graphql,
|
||||||
|
parsePath,
|
||||||
|
navigate,
|
||||||
|
push, // TODO replace for v3
|
||||||
|
replace, // TODO remove replace for v3
|
||||||
|
navigateTo, // TODO: remove navigateTo for v3
|
||||||
|
StaticQueryContext,
|
||||||
|
StaticQuery,
|
||||||
|
PageRenderer,
|
||||||
|
useStaticQuery,
|
||||||
|
prefetchPathname,
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
import React from "react"
|
||||||
|
|
||||||
|
import PageRenderer from "./page-renderer"
|
||||||
|
import normalizePagePath from "./normalize-page-path"
|
||||||
|
import { StaticQueryContext } from "gatsby"
|
||||||
|
import {
|
||||||
|
getStaticQueryData,
|
||||||
|
getPageQueryData,
|
||||||
|
registerPath as socketRegisterPath,
|
||||||
|
unregisterPath as socketUnregisterPath,
|
||||||
|
} from "./socketIo"
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === `production`) {
|
||||||
|
throw new Error(
|
||||||
|
`It appears like Gatsby is misconfigured. JSONStore is Gatsby internal ` +
|
||||||
|
`development-only component and should never be used in production.\n\n` +
|
||||||
|
`Unless your site has a complex or custom webpack/Gatsby ` +
|
||||||
|
`configuration this is likely a bug in Gatsby. ` +
|
||||||
|
`Please report this at https://github.com/gatsbyjs/gatsby/issues ` +
|
||||||
|
`with steps to reproduce this error.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getPathFromProps = props =>
|
||||||
|
props.pageResources && props.pageResources.page
|
||||||
|
? normalizePagePath(props.pageResources.page.path)
|
||||||
|
: undefined
|
||||||
|
|
||||||
|
class JSONStore extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
this.state = {
|
||||||
|
staticQueryData: getStaticQueryData(),
|
||||||
|
pageQueryData: getPageQueryData(),
|
||||||
|
path: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMittEvent = (type, event) => {
|
||||||
|
this.setState({
|
||||||
|
staticQueryData: getStaticQueryData(),
|
||||||
|
pageQueryData: getPageQueryData(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
socketRegisterPath(getPathFromProps(this.props))
|
||||||
|
___emitter.on(`*`, this.handleMittEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
socketUnregisterPath(this.state.path)
|
||||||
|
___emitter.off(`*`, this.handleMittEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDerivedStateFromProps(props, state) {
|
||||||
|
const newPath = getPathFromProps(props)
|
||||||
|
if (newPath !== state.path) {
|
||||||
|
socketUnregisterPath(state.path)
|
||||||
|
socketRegisterPath(newPath)
|
||||||
|
return {
|
||||||
|
path: newPath,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
|
// We want to update this component when:
|
||||||
|
// - location changed
|
||||||
|
// - page data for path changed
|
||||||
|
// - static query results changed
|
||||||
|
|
||||||
|
return (
|
||||||
|
this.props.location !== nextProps.location ||
|
||||||
|
this.state.path !== nextState.path ||
|
||||||
|
this.state.pageQueryData[normalizePagePath(nextState.path)] !==
|
||||||
|
nextState.pageQueryData[normalizePagePath(nextState.path)] ||
|
||||||
|
this.state.staticQueryData !== nextState.staticQueryData
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const data = this.state.pageQueryData[getPathFromProps(this.props)]
|
||||||
|
// eslint-disable-next-line
|
||||||
|
if (!data) {
|
||||||
|
return <div />
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StaticQueryContext.Provider value={this.state.staticQueryData}>
|
||||||
|
<PageRenderer {...this.props} {...data} />
|
||||||
|
</StaticQueryContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default JSONStore
|
|
@ -0,0 +1,418 @@
|
||||||
|
import prefetchHelper from "./prefetch"
|
||||||
|
import emitter from "./emitter"
|
||||||
|
import { setMatchPaths, findPath, findMatchPath } from "./find-path"
|
||||||
|
|
||||||
|
const preferDefault = m => (m && m.default) || m
|
||||||
|
|
||||||
|
const stripSurroundingSlashes = s => {
|
||||||
|
s = s[0] === `/` ? s.slice(1) : s
|
||||||
|
s = s.endsWith(`/`) ? s.slice(0, -1) : s
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
const createPageDataUrl = path => {
|
||||||
|
const fixedPath = path === `/` ? `index` : stripSurroundingSlashes(path)
|
||||||
|
return `${__PATH_PREFIX__}/page-data/${fixedPath}/page-data.json`
|
||||||
|
}
|
||||||
|
|
||||||
|
const doFetch = (url, method = `GET`) =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
const req = new XMLHttpRequest()
|
||||||
|
req.open(method, url, true)
|
||||||
|
req.onreadystatechange = () => {
|
||||||
|
if (req.readyState == 4) {
|
||||||
|
resolve(req)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
req.send(null)
|
||||||
|
})
|
||||||
|
|
||||||
|
const loadPageDataJson = loadObj => {
|
||||||
|
const { pagePath, retries = 0 } = loadObj
|
||||||
|
const url = createPageDataUrl(pagePath)
|
||||||
|
return doFetch(url).then(req => {
|
||||||
|
const { status, responseText } = req
|
||||||
|
|
||||||
|
// Handle 200
|
||||||
|
if (status === 200) {
|
||||||
|
try {
|
||||||
|
const jsonPayload = JSON.parse(responseText)
|
||||||
|
if (jsonPayload.path === undefined) {
|
||||||
|
throw new Error(`not a valid pageData response`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.assign(loadObj, {
|
||||||
|
status: `success`,
|
||||||
|
payload: jsonPayload,
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
// continue regardless of error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle 404
|
||||||
|
if (status === 404 || status === 200) {
|
||||||
|
// If the request was for a 404 page and it doesn't exist, we're done
|
||||||
|
if (pagePath === `/404.html`) {
|
||||||
|
return Object.assign(loadObj, {
|
||||||
|
status: `failure`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need some code here to cache the 404 request. In case
|
||||||
|
// multiple loadPageDataJsons result in 404s
|
||||||
|
return loadPageDataJson(
|
||||||
|
Object.assign(loadObj, { pagePath: `/404.html`, notFound: true })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle 500 response (Unrecoverable)
|
||||||
|
if (status === 500) {
|
||||||
|
return Object.assign(loadObj, {
|
||||||
|
status: `error`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle everything else, including status === 0, and 503s. Should retry
|
||||||
|
if (retries < 3) {
|
||||||
|
return loadPageDataJson(Object.assign(loadObj, { retries: retries + 1 }))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retried 3 times already, result is a failure.
|
||||||
|
return Object.assign(loadObj, {
|
||||||
|
status: `error`,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const doesConnectionSupportPrefetch = () => {
|
||||||
|
if (
|
||||||
|
`connection` in navigator &&
|
||||||
|
typeof navigator.connection !== `undefined`
|
||||||
|
) {
|
||||||
|
if ((navigator.connection.effectiveType || ``).includes(`2g`)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (navigator.connection.saveData) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const toPageResources = (pageData, component = null) => {
|
||||||
|
const page = {
|
||||||
|
componentChunkName: pageData.componentChunkName,
|
||||||
|
path: pageData.path,
|
||||||
|
webpackCompilationHash: pageData.webpackCompilationHash,
|
||||||
|
matchPath: pageData.matchPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
component,
|
||||||
|
json: pageData.result,
|
||||||
|
page,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BaseLoader {
|
||||||
|
constructor(loadComponent, matchPaths) {
|
||||||
|
// Map of pagePath -> Page. Where Page is an object with: {
|
||||||
|
// status: `success` || `error`,
|
||||||
|
// payload: PageResources, // undefined if `error`
|
||||||
|
// }
|
||||||
|
// PageResources is {
|
||||||
|
// component,
|
||||||
|
// json: pageData.result,
|
||||||
|
// page: {
|
||||||
|
// componentChunkName,
|
||||||
|
// path,
|
||||||
|
// webpackCompilationHash,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
this.pageDb = new Map()
|
||||||
|
this.inFlightDb = new Map()
|
||||||
|
this.pageDataDb = new Map()
|
||||||
|
this.prefetchTriggered = new Set()
|
||||||
|
this.prefetchCompleted = new Set()
|
||||||
|
this.loadComponent = loadComponent
|
||||||
|
setMatchPaths(matchPaths)
|
||||||
|
}
|
||||||
|
|
||||||
|
setApiRunner(apiRunner) {
|
||||||
|
this.apiRunner = apiRunner
|
||||||
|
this.prefetchDisabled = apiRunner(`disableCorePrefetching`).some(a => a)
|
||||||
|
}
|
||||||
|
|
||||||
|
loadPageDataJson(rawPath) {
|
||||||
|
const pagePath = findPath(rawPath)
|
||||||
|
if (this.pageDataDb.has(pagePath)) {
|
||||||
|
return Promise.resolve(this.pageDataDb.get(pagePath))
|
||||||
|
}
|
||||||
|
|
||||||
|
return loadPageDataJson({ pagePath }).then(pageData => {
|
||||||
|
this.pageDataDb.set(pagePath, pageData)
|
||||||
|
|
||||||
|
return pageData
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
findMatchPath(rawPath) {
|
||||||
|
return findMatchPath(rawPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO check all uses of this and whether they use undefined for page resources not exist
|
||||||
|
loadPage(rawPath) {
|
||||||
|
const pagePath = findPath(rawPath)
|
||||||
|
if (this.pageDb.has(pagePath)) {
|
||||||
|
const page = this.pageDb.get(pagePath)
|
||||||
|
return Promise.resolve(page.payload)
|
||||||
|
}
|
||||||
|
if (this.inFlightDb.has(pagePath)) {
|
||||||
|
return this.inFlightDb.get(pagePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
const inFlight = Promise.all([
|
||||||
|
this.loadAppData(),
|
||||||
|
this.loadPageDataJson(pagePath),
|
||||||
|
])
|
||||||
|
.then(allData => {
|
||||||
|
const result = allData[1]
|
||||||
|
if (result.status === `error`) {
|
||||||
|
return {
|
||||||
|
status: `error`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result.status === `failure`) {
|
||||||
|
// throw an error so error trackers can pick this up
|
||||||
|
throw new Error(
|
||||||
|
`404 page could not be found. Checkout https://www.gatsbyjs.org/docs/add-404-page/`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let pageData = result.payload
|
||||||
|
const { componentChunkName } = pageData
|
||||||
|
return this.loadComponent(componentChunkName).then(component => {
|
||||||
|
const finalResult = { createdAt: new Date() }
|
||||||
|
let pageResources
|
||||||
|
if (!component) {
|
||||||
|
finalResult.status = `error`
|
||||||
|
} else {
|
||||||
|
finalResult.status = `success`
|
||||||
|
if (result.notFound === true) {
|
||||||
|
finalResult.notFound = true
|
||||||
|
}
|
||||||
|
pageData = Object.assign(pageData, {
|
||||||
|
webpackCompilationHash: allData[0]
|
||||||
|
? allData[0].webpackCompilationHash
|
||||||
|
: ``,
|
||||||
|
})
|
||||||
|
pageResources = toPageResources(pageData, component)
|
||||||
|
finalResult.payload = pageResources
|
||||||
|
emitter.emit(`onPostLoadPageResources`, {
|
||||||
|
page: pageResources,
|
||||||
|
pageResources,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.pageDb.set(pagePath, finalResult)
|
||||||
|
// undefined if final result is an error
|
||||||
|
return pageResources
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// prefer duplication with then + catch over .finally to prevent problems in ie11 + firefox
|
||||||
|
.then(response => {
|
||||||
|
this.inFlightDb.delete(pagePath)
|
||||||
|
return response
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this.inFlightDb.delete(pagePath)
|
||||||
|
throw err
|
||||||
|
})
|
||||||
|
|
||||||
|
this.inFlightDb.set(pagePath, inFlight)
|
||||||
|
return inFlight
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns undefined if loading page ran into errors
|
||||||
|
loadPageSync(rawPath) {
|
||||||
|
const pagePath = findPath(rawPath)
|
||||||
|
if (this.pageDb.has(pagePath)) {
|
||||||
|
return this.pageDb.get(pagePath).payload
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldPrefetch(pagePath) {
|
||||||
|
// Skip prefetching if we know user is on slow or constrained connection
|
||||||
|
if (!doesConnectionSupportPrefetch()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the page exists.
|
||||||
|
if (this.pageDb.has(pagePath)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
prefetch(pagePath) {
|
||||||
|
if (!this.shouldPrefetch(pagePath)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tell plugins with custom prefetching logic that they should start
|
||||||
|
// prefetching this path.
|
||||||
|
if (!this.prefetchTriggered.has(pagePath)) {
|
||||||
|
this.apiRunner(`onPrefetchPathname`, { pathname: pagePath })
|
||||||
|
this.prefetchTriggered.add(pagePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a plugin has disabled core prefetching, stop now.
|
||||||
|
if (this.prefetchDisabled) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const realPath = findPath(pagePath)
|
||||||
|
// Todo make doPrefetch logic cacheable
|
||||||
|
// eslint-disable-next-line consistent-return
|
||||||
|
this.doPrefetch(realPath).then(() => {
|
||||||
|
if (!this.prefetchCompleted.has(pagePath)) {
|
||||||
|
this.apiRunner(`onPostPrefetchPathname`, { pathname: pagePath })
|
||||||
|
this.prefetchCompleted.add(pagePath)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
doPrefetch(pagePath) {
|
||||||
|
throw new Error(`doPrefetch not implemented`)
|
||||||
|
}
|
||||||
|
|
||||||
|
hovering(rawPath) {
|
||||||
|
this.loadPage(rawPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
getResourceURLsForPathname(rawPath) {
|
||||||
|
const pagePath = findPath(rawPath)
|
||||||
|
const page = this.pageDataDb.get(pagePath)
|
||||||
|
if (page) {
|
||||||
|
const pageResources = toPageResources(page.payload)
|
||||||
|
|
||||||
|
return [
|
||||||
|
...createComponentUrls(pageResources.page.componentChunkName),
|
||||||
|
createPageDataUrl(pagePath),
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isPageNotFound(rawPath) {
|
||||||
|
const pagePath = findPath(rawPath)
|
||||||
|
const page = this.pageDb.get(pagePath)
|
||||||
|
return page && page.notFound === true
|
||||||
|
}
|
||||||
|
|
||||||
|
loadAppData(retries = 0) {
|
||||||
|
return doFetch(`${__PATH_PREFIX__}/page-data/app-data.json`).then(req => {
|
||||||
|
const { status, responseText } = req
|
||||||
|
|
||||||
|
let appData
|
||||||
|
|
||||||
|
if (status !== 200 && retries < 3) {
|
||||||
|
// Retry 3 times incase of failures
|
||||||
|
return this.loadAppData(retries + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle 200
|
||||||
|
if (status === 200) {
|
||||||
|
try {
|
||||||
|
const jsonPayload = JSON.parse(responseText)
|
||||||
|
if (jsonPayload.webpackCompilationHash === undefined) {
|
||||||
|
throw new Error(`not a valid app-data response`)
|
||||||
|
}
|
||||||
|
|
||||||
|
appData = jsonPayload
|
||||||
|
} catch (err) {
|
||||||
|
// continue regardless of error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return appData
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const createComponentUrls = componentChunkName =>
|
||||||
|
window.___chunkMapping[componentChunkName].map(
|
||||||
|
chunk => __PATH_PREFIX__ + chunk
|
||||||
|
)
|
||||||
|
|
||||||
|
export class ProdLoader extends BaseLoader {
|
||||||
|
constructor(asyncRequires, matchPaths) {
|
||||||
|
const loadComponent = chunkName =>
|
||||||
|
asyncRequires.components[chunkName]().then(preferDefault)
|
||||||
|
|
||||||
|
super(loadComponent, matchPaths)
|
||||||
|
}
|
||||||
|
|
||||||
|
doPrefetch(pagePath) {
|
||||||
|
const pageDataUrl = createPageDataUrl(pagePath)
|
||||||
|
return prefetchHelper(pageDataUrl)
|
||||||
|
.then(() =>
|
||||||
|
// This was just prefetched, so will return a response from
|
||||||
|
// the cache instead of making another request to the server
|
||||||
|
this.loadPageDataJson(pagePath)
|
||||||
|
)
|
||||||
|
.then(result => {
|
||||||
|
if (result.status !== `success`) {
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
const pageData = result.payload
|
||||||
|
const chunkName = pageData.componentChunkName
|
||||||
|
const componentUrls = createComponentUrls(chunkName)
|
||||||
|
return Promise.all(componentUrls.map(prefetchHelper)).then(
|
||||||
|
() => pageData
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let instance
|
||||||
|
|
||||||
|
export const setLoader = _loader => {
|
||||||
|
instance = _loader
|
||||||
|
}
|
||||||
|
|
||||||
|
export const publicLoader = {
|
||||||
|
// Deprecated methods. As far as we're aware, these are only used by
|
||||||
|
// core gatsby and the offline plugin, however there's a very small
|
||||||
|
// chance they're called by others.
|
||||||
|
getResourcesForPathname: rawPath => {
|
||||||
|
console.warn(
|
||||||
|
`Warning: getResourcesForPathname is deprecated. Use loadPage instead`
|
||||||
|
)
|
||||||
|
return instance.i.loadPage(rawPath)
|
||||||
|
},
|
||||||
|
getResourcesForPathnameSync: rawPath => {
|
||||||
|
console.warn(
|
||||||
|
`Warning: getResourcesForPathnameSync is deprecated. Use loadPageSync instead`
|
||||||
|
)
|
||||||
|
return instance.i.loadPageSync(rawPath)
|
||||||
|
},
|
||||||
|
enqueue: rawPath => instance.prefetch(rawPath),
|
||||||
|
|
||||||
|
// Real methods
|
||||||
|
getResourceURLsForPathname: rawPath =>
|
||||||
|
instance.getResourceURLsForPathname(rawPath),
|
||||||
|
loadPage: rawPath => instance.loadPage(rawPath),
|
||||||
|
loadPageSync: rawPath => instance.loadPageSync(rawPath),
|
||||||
|
prefetch: rawPath => instance.prefetch(rawPath),
|
||||||
|
isPageNotFound: rawPath => instance.isPageNotFound(rawPath),
|
||||||
|
hovering: rawPath => instance.hovering(rawPath),
|
||||||
|
loadAppData: () => instance.loadAppData(),
|
||||||
|
}
|
||||||
|
|
||||||
|
export default publicLoader
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue