|
const fs = require('fs'); |
|
const contentfulExport = require('contentful-export') |
|
|
|
const defaultConfiguration = { |
|
spaceId: null, |
|
managementToken: null, |
|
topLevelEntryKey: null, |
|
outputDir: './src/content', |
|
outputFile: 'content.json' |
|
} |
|
|
|
module.exports = (config) => { |
|
/*** Helpers ***/ |
|
/** |
|
* Recursive function to make key value pairs of nested objects |
|
* @param {Object} - Object with keys: 'key', 'value' |
|
* @returns {Object} |
|
*/ |
|
const objectifyNestedKeyValues = ({ key, value }) => ({ |
|
[key]: Array.isArray(value) ? value.reduce((acc, cur) => ({ ...acc, [cur.key]: cur.value }), {}) : value |
|
}) |
|
|
|
/** |
|
* Recursive function to make key value pairs of nested objects |
|
* @param {String} locale |
|
* @param {Object} - Contentful entry |
|
* @param {Object} entries - All Contentful entries |
|
* @returns {Object} |
|
*/ |
|
const sanitiseContenfulItem = (locale, { fields }, entries) => { |
|
const key = fields.key[locale]; |
|
const values = fields.value[locale]; |
|
|
|
return { |
|
key, |
|
// eslint-disable-next-line no-use-before-define |
|
value: Array.isArray(values) ? values.map(value => (value.sys.type === 'Link' ? getLinkedItem(locale, value, entries) : value)) : values |
|
} |
|
}; |
|
|
|
/** |
|
* Recursive function to make key value pairs of nested objects |
|
* @param {String} locale |
|
* @param {Object} value - Contentful entry |
|
* @param {Object} entries - All Contentful entries |
|
* @returns {Object} |
|
*/ |
|
const getLinkedItem = (locale, value, entries) => { |
|
const { id } = value.sys; |
|
|
|
const linkedEntry = entries.find(entry => entry.sys.id === id); |
|
|
|
return linkedEntry ? sanitiseContenfulItem(locale, linkedEntry, entries) : {}; |
|
} |
|
|
|
/** |
|
* Recursive function to make key value pairs of nested objects |
|
* @param {Object} entries - All Contentful entries |
|
* @param {String} topLevelEntryKey - Contentful parent entry key to return |
|
* @returns {Array} |
|
*/ |
|
const getPages = (entries, topLevelEntryKey) => entries.filter(entry => entry.sys.contentType.sys.id === topLevelEntryKey) |
|
|
|
/** |
|
* Function to return list of locale codes |
|
* @param {Array} locales - All active Contentful locales |
|
* @returns {Array} |
|
*/ |
|
const getlocaleCodes = (locales) => locales.map(({ code }) => code); |
|
|
|
/** |
|
* Function to return list of locale codes |
|
* @param {String} locale |
|
* @param {Object} pages - All Contentful entries |
|
* @param {Object} entries - All Contentful entries |
|
* @returns {Array} |
|
*/ |
|
const generateLocaleObject = (locale, pages, entries) => pages.reduce((acc, cur) => ({ ...acc, ...objectifyNestedKeyValues(sanitiseContenfulItem(locale, cur, entries)) }), {}) |
|
|
|
/** |
|
* Function to return list of locale codes |
|
* @param {Array} locales - All active Contentful locales |
|
* @param {Array} pages - All Contentful pages |
|
* @param {Object} entries - All Contentful entries |
|
* @returns {Object} |
|
*/ |
|
const generateContentObject = (locales, pages, entries) => locales |
|
.reduce((acc, cur) => ({ |
|
...acc, |
|
[cur]: generateLocaleObject(cur, pages, entries) |
|
}), {}); |
|
|
|
/** |
|
* Recursive function to make key value pairs of nested objects |
|
* @param {Object} data - Contentful response |
|
* @param {String} topLevelEntryKey - Contentful parent entry key to return |
|
* @returns {Object} - key-value paired object |
|
*/ |
|
const sanitiseContentfulResponse = (data, topLevelEntryKey) => { |
|
const { entries, locales } = data; |
|
|
|
const localeCodes = getlocaleCodes(locales); |
|
const pages = getPages(entries, topLevelEntryKey); |
|
|
|
return generateContentObject(localeCodes, pages, entries) |
|
} |
|
|
|
/*** Configuration ***/ |
|
const configuration = { ...defaultConfiguration, ...config } |
|
|
|
if (!configuration.spaceId) { throw new Error('No spaceId provided') }; |
|
if (!configuration.managementToken) { throw new Error('No managementToken provided') }; |
|
if (!configuration.topLevelEntryKey) { throw new Error('No topLevelEntryKey provided') }; |
|
|
|
const options = { |
|
saveFile: false, |
|
skipEditorInterfaces: true, |
|
skipRoles: true, |
|
skipWebhooks: true, |
|
spaceId: configuration.spaceId, |
|
managementToken: configuration.managementToken |
|
}; |
|
|
|
return new Promise((resolve, reject) => { |
|
contentfulExport(options) |
|
.then((result) => { |
|
const content = sanitiseContentfulResponse(result, configuration.topLevelEntryKey); |
|
|
|
if (!fs.existsSync(configuration.outputDir)) { |
|
fs.mkdirSync(configuration.outputDir, { recursive: true }); |
|
} |
|
|
|
// eslint-disable-next-line no-magic-numbers |
|
fs.writeFileSync(`${configuration.outputDir}/${configuration.outputFile}`, JSON.stringify(content, null, 2)); |
|
resolve() |
|
}) |
|
.catch(reject) |
|
}) |
|
} |