Injecting nonRoute data into Sitecore JSS context using customizeContext
/* | |
When the app runs in disconnected mode, and Sitecore is not present, we need to give | |
the app copies of the Sitecore APIs it depends on (layout service, dictionary service, content service) | |
to talk to so that the app can run using the locally defined disconnected data. | |
This is accomplished by spinning up a small Express server that mocks the APIs, and then | |
telling the dev server to proxy requests to the API paths to this express instance. | |
*/ | |
/* eslint-disable no-console */ | |
const fs = require('fs'); | |
const path = require('path'); | |
const { createDefaultDisconnectedServer } = require('@sitecore-jss/sitecore-jss-dev-tools'); | |
const { remapFieldsArrayToFieldsObject } = require('../node_modules/@sitecore-jss/sitecore-jss-dev-tools/dist/disconnected-server/layout-service'); | |
const config = require('../package.json').config; | |
const touchToReloadFilePath = 'src/temp/config.js'; | |
// Retrieves the top most node in the nonRoutes array. | |
const retrieveContentNode = (currentManifest) => { | |
// `rootItemName` from content.sitecore.js | |
const contentItem = currentManifest.items.nonRoutes.find(nonRoute => nonRoute.name === 'Content'); | |
return contentItem; | |
} | |
const findContentItemById = (currentNode, id) => { | |
let foundNode; | |
// check to see if the current node is a match | |
const is_match = currentNode.id === id; | |
// if it is as match, set found_node to the current node | |
if (is_match) { | |
foundNode = currentNode; | |
} else if (currentNode.children) { | |
// otherwise, check the children of this node if they exist | |
currentNode.children.some(child => { | |
foundNode = findContentItemById(child, id); | |
return foundNode != null; | |
}); | |
} | |
return foundNode; | |
} | |
const convertContentItemToLayoutServiceFormat = (contentItem) => { | |
let item; | |
if (contentItem) { | |
item = JSON.parse(JSON.stringify(contentItem)); | |
if (item.fields) { | |
item.fields = remapFieldsArrayToFieldsObject(item.fields); | |
} | |
} | |
return item; | |
} | |
const proxyOptions = { | |
appRoot: path.join(__dirname, '..'), | |
appName: config.appName, | |
watchPaths: ['./data'], | |
language: config.language, | |
port: process.env.PROXY_PORT || 3042, | |
onManifestUpdated: (manifest) => { | |
// if we can resolve the config file, we can alter it to force reloading the app automatically | |
// instead of waiting for a manual reload. We must materially alter the _contents_ of the file to trigger | |
// an actual reload, so we append "// reloadnow" to the file each time. This will not cause a problem, | |
// since every build regenerates the config file from scratch and it's ignored from source control. | |
if (fs.existsSync(touchToReloadFilePath)) { | |
const currentFileContents = fs.readFileSync(touchToReloadFilePath, 'utf8'); | |
const newFileContents = `${currentFileContents}\n// reloadnow`; | |
fs.writeFileSync(touchToReloadFilePath, newFileContents, 'utf8'); | |
console.log('Manifest data updated. Reloading the browser.'); | |
} else { | |
console.log('Manifest data updated. Refresh the browser to see latest content!'); | |
} | |
}, | |
customizeContext: (context, route, currentManifest, request, response) => { | |
// Retrieves the top level content node for nonRoutes | |
const contentNode = retrieveContentNode(currentManifest); | |
// If it does not exist, something is wrong. | |
if (!contentNode) { | |
console.error('Unable to locate content node, check manifest.'); | |
} | |
// Find all of the items we want to inject into our context | |
const additionalContext = { | |
site_settings: findContentItemById(contentNode, 'site-settings') | |
}; | |
// Convert all of the found items to layout service structure | |
Object.keys(additionalContext).forEach(key => { | |
additionalContext[key] = convertContentItemToLayoutServiceFormat(additionalContext[key]) | |
}) | |
// Return the existing context with our injected properties added | |
return { ...additionalContext, ...context }; | |
} | |
}; | |
// Need to customize something that the proxy options don't support? | |
// createDefaultDisconnectedServer() is a boilerplate that you can copy from | |
// and customize the middleware registrations within as you see fit. | |
// See https://github.com/Sitecore/jss/blob/master/packages/sitecore-jss-dev-tools/src/disconnected-server/create-default-disconnected-server.ts | |
createDefaultDisconnectedServer(proxyOptions); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment