import fetch from 'cross-fetch'; import querystring from 'querystring'; const fetchOptions = { method: 'GET', mode: 'cors', credentials: 'omit' }; export function api(apiOptions, params = {}) { const qs = Object.assign({ format: 'json', action: 'query', redirects: '1' }, params); // Remove undefined properties Object.keys(qs).forEach(key => { if (qs[key] === undefined) { delete qs[key]; } }); if (apiOptions.origin) { qs.origin = apiOptions.origin; } const url = `${apiOptions.apiUrl}?${querystring.stringify(qs)}`; const headers = Object.assign({ 'User-Agent': 'WikiJS Bot v1.0' }, apiOptions.headers); return fetch(url, Object.assign({ headers }, fetchOptions)) .then(res => { if (res.ok) { return res.json(); } throw new Error(`${res.status}: ${res.statusText}`); }) .then(res => { if (res.error) { throw new Error(res.error.info); } return res; }); } export function pagination(apiOptions, params, parseResults) { return api(apiOptions, params).then(res => { let resolution = {}; resolution.results = parseResults(res); resolution.query = params.srsearch; if (res['continue']) { const continueType = Object.keys(res['continue']).filter(key => key !== 'continue')[0]; const continueKey = res['continue'][continueType]; params[continueType] = continueKey; resolution.next = () => pagination(apiOptions, params, parseResults); } return resolution; }); } export function aggregatePagination(pagination, previousResults = []) { return pagination.then(res => { const results = [...previousResults, ...res.results]; if (res.next) { return aggregatePagination(res.next(), results); } else { return results; } }); } const pageLimit = 500; export function aggregate(apiOptions, params, list, key, prefix, results = []) { params.list = list; params[prefix + 'limit'] = pageLimit; return api(apiOptions, params).then(res => { const nextResults = [...results, ...res.query[list].map(e => e[key])]; const continueWith = res['query-continue'] || res.continue; if (continueWith) { const nextFromKey = (continueWith[list] && continueWith[list][prefix + 'from']) || (continueWith[list] && continueWith[list][prefix + 'continue']) || continueWith[prefix + 'continue']; params[prefix + 'continue'] = nextFromKey; params[prefix + 'from'] = nextFromKey; return aggregate(apiOptions, params, list, key, prefix, nextResults); } return nextResults; }); } const headingPattern = /(==+)(?:(?!\n)\s?)((?:(?!==|\n)[^])+)(?:(?!\n)\s?)(==+)/g; function getHeadings(text) { let match; const matches = []; while ((match = headingPattern.exec(text)) !== null) { matches.push({ level: match[1].trim().length, text: match[2].trim(), start: match.index, end: match.index + match[0].length }); } return matches; } export function parseContent(source) { const headings = getHeadings(source); const minLevel = Math.min(...headings.map(({ level }) => level)); const sections = headings.map((heading, index) => { const next = headings[index + 1]; const content = source .substring(heading.end, next ? next.start : undefined) .trim(); return { title: heading.text, level: heading.level - minLevel, id: index, content, items: [] }; }); const lastParentLevel = (index, level) => { if (level === 0) return null; for (let i = index - 1; i >= 0; i--) { if (sections[i].level < level) { return sections[i].id; } } return null; }; // Set parents sections.forEach((section, index) => { section.parent = lastParentLevel(index, section.level); }); const root = { items: [] }; const findSection = id => sections.find(s => id === s.id); // Organize sections.forEach(section => { if (section.parent === null) { root.items.push(section); } else { findSection(section.parent).items.push(section); } }); // Clean up sections.forEach(section => { delete section.id; delete section.parent; delete section.level; if (!section.items.length) { delete section.items; } }); return root.items; }