| | import { createContext, useContext } from 'react' |
| | import pick from 'lodash/pick' |
| |
|
| | import type { BreadcrumbT } from '@/frame/components/page-header/Breadcrumbs' |
| | import type { FeatureFlags } from '@/frame/components/hooks/useFeatureFlags' |
| | import type { SidebarLink } from '@/types' |
| |
|
| | export type ProductT = { |
| | external: boolean |
| | href: string |
| | id: string |
| | name: string |
| | nameRendered: string |
| | } |
| |
|
| | export type VersionItem = { |
| | |
| | version: string |
| | versionTitle: string |
| | isGHES?: boolean |
| | apiVersions: string[] |
| | latestApiVersion: string |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | type FullVersionItem = VersionItem & { |
| | shortName: string |
| | } |
| |
|
| | function minimalAllVersions( |
| | allVersions: Record<string, FullVersionItem>, |
| | ): Record<string, VersionItem> { |
| | const all: Record<string, VersionItem> = {} |
| | for (const [plan, info] of Object.entries(allVersions)) { |
| | all[plan] = { |
| | version: info.version, |
| | versionTitle: info.versionTitle, |
| | apiVersions: info.apiVersions, |
| | latestApiVersion: info.latestApiVersion, |
| | } |
| | |
| | |
| | if (info.shortName === 'ghes') { |
| | all[plan].isGHES = true |
| | } |
| | } |
| | return all |
| | } |
| |
|
| | export type ProductTreeNode = { |
| | title: string |
| | href: string |
| | childPages: Array<ProductTreeNode> |
| | sidebarLink?: SidebarLink |
| | layout?: string |
| | } |
| |
|
| | type UIString = Record<string, string> |
| | export type UIStrings = UIString | { [key: string]: UIStrings } |
| |
|
| | export type EnterpriseDeprecation = { |
| | version_was_deprecated: string |
| | version_will_be_deprecated: string |
| | deprecation_details: string |
| | isOldestReleaseDeprecated?: boolean |
| | } |
| |
|
| | type DataReusables = { |
| | enterprise_deprecation?: EnterpriseDeprecation |
| | } |
| |
|
| | type DataT = { |
| | ui: UIStrings |
| | reusables: DataReusables |
| | variables: { |
| | release_candidate: { version: string | null } |
| | } |
| | } |
| |
|
| | type EnterpriseServerReleases = { |
| | isOldestReleaseDeprecated: boolean |
| | oldestSupported: string |
| | nextDeprecationDate: string |
| | supported: Array<string> |
| | } |
| |
|
| | export type MainContextT = { |
| | allVersions: Record<string, VersionItem> |
| | breadcrumbs: { |
| | product: BreadcrumbT |
| | category?: BreadcrumbT |
| | subcategory?: BreadcrumbT |
| | article?: BreadcrumbT |
| | } |
| | communityRedirect: { |
| | name: string |
| | href: string |
| | } |
| | currentCategory?: string |
| | currentPathWithoutLanguage: string |
| | currentProduct?: ProductT |
| | currentProductName: string |
| | currentProductTree?: ProductTreeNode | null |
| | currentLayoutName?: string |
| | currentVersion?: string |
| | data: DataT |
| | enterpriseServerReleases: EnterpriseServerReleases |
| | enterpriseServerVersions: Array<string> |
| | error: string |
| | featureFlags: FeatureFlags |
| | fullUrl: string |
| | isHomepageVersion: boolean |
| | nonEnterpriseDefaultVersion: string |
| | page: { |
| | documentType: string |
| | type?: string |
| | contentType?: string |
| | topics: Array<string> |
| | title: string |
| | fullTitle?: string |
| | introPlainText?: string |
| | hidden: boolean |
| | noEarlyAccessBanner: boolean |
| | applicableVersions: string[] |
| | } | null |
| | relativePath?: string |
| | sidebarTree?: ProductTreeNode | null |
| | status: number |
| | xHost?: string |
| | } |
| |
|
| | |
| | |
| | |
| | const DEFAULT_UI_NAMESPACES = [ |
| | 'alerts', |
| | 'header', |
| | 'search', |
| | 'old_search', |
| | 'survey', |
| | 'toc', |
| | 'meta', |
| | 'scroll_button', |
| | 'pages', |
| | 'picker', |
| | 'footer', |
| | 'contribution_cta', |
| | 'support', |
| | 'rest', |
| | 'cookbook_landing', |
| | ] |
| |
|
| | export function addUINamespaces(req: any, ui: UIStrings, namespaces: string[]) { |
| | const pool = req.context.site.data.ui |
| | for (const namespace of namespaces) { |
| | if (!(namespace in pool)) { |
| | throw new Error( |
| | `Invalid namespace "${namespace}". It's not present in data/ui.yml as a namespace. (not one of: ${Object.keys( |
| | pool, |
| | )})`, |
| | ) |
| | } |
| | ui[namespace] = pool[namespace] |
| | } |
| | } |
| |
|
| | export const getMainContext = async (req: any, res: any): Promise<MainContextT> => { |
| | |
| | |
| | |
| | |
| | if (req.context.site.data.ui.ms) { |
| | delete req.context.site.data.ui.ms |
| | } |
| |
|
| | const { page } = req.context |
| |
|
| | const documentType = page ? (page.documentType as string) : undefined |
| |
|
| | const ui: UIStrings = {} |
| | addUINamespaces(req, ui, DEFAULT_UI_NAMESPACES) |
| |
|
| | |
| | |
| | const includeFullProductTree = documentType === 'product' |
| | const includeSidebarTree = documentType !== 'homepage' |
| |
|
| | const reusables: DataReusables = {} |
| |
|
| | |
| | |
| | if (req.context.currentVersion.includes(req.context.enterpriseServerReleases.oldestSupported)) { |
| | reusables.enterprise_deprecation = { |
| | version_was_deprecated: req.context.getDottedData( |
| | 'reusables.enterprise_deprecation.version_was_deprecated', |
| | ), |
| | version_will_be_deprecated: req.context.getDottedData( |
| | 'reusables.enterprise_deprecation.version_will_be_deprecated', |
| | ), |
| | deprecation_details: req.context.getDottedData( |
| | 'reusables.enterprise_deprecation.deprecation_details', |
| | ), |
| | } |
| | } |
| |
|
| | |
| | |
| | const { releaseCandidate } = req.context.enterpriseServerReleases |
| | |
| | |
| | const releaseCandidateVersion = releaseCandidate ? `enterprise-server@${releaseCandidate}` : null |
| |
|
| | const pageInfo = |
| | (page && { |
| | documentType, |
| | type: req.context.page.type || null, |
| | contentType: req.context.page.contentType || null, |
| | title: req.context.page.title, |
| | fullTitle: req.context.page.fullTitle || null, |
| | topics: req.context.page.topics || [], |
| | introPlainText: req.context.page?.introPlainText || null, |
| | applicableVersions: req.context.page?.permalinks.map((obj: any) => obj.pageVersion) || [], |
| | hidden: req.context.page.hidden || false, |
| | noEarlyAccessBanner: req.context.page.noEarlyAccessBanner || false, |
| | }) || |
| | null |
| |
|
| | const currentProduct: ProductT = req.context.productMap[req.context.currentProduct] || null |
| | const currentProductName: string = req.context.currentProductName || '' |
| |
|
| | const props: MainContextT = { |
| | allVersions: minimalAllVersions(req.context.allVersions), |
| | breadcrumbs: req.context.breadcrumbs || {}, |
| | communityRedirect: req.context.page?.communityRedirect || {}, |
| | currentCategory: req.context.currentCategory || '', |
| | currentLayoutName: req.context.currentLayoutName || null, |
| | currentPathWithoutLanguage: req.context.currentPathWithoutLanguage, |
| | currentProduct, |
| | currentProductName, |
| | |
| | |
| | |
| | |
| | |
| | |
| | currentProductTree: |
| | (includeFullProductTree && req.context.currentProductTreeTitlesExcludeHidden) || null, |
| | currentVersion: req.context.currentVersion, |
| | data: { |
| | ui, |
| | reusables, |
| | variables: { |
| | release_candidate: { |
| | version: releaseCandidateVersion, |
| | }, |
| | }, |
| | }, |
| | enterpriseServerReleases: pick(req.context.enterpriseServerReleases, [ |
| | 'isOldestReleaseDeprecated', |
| | 'oldestSupported', |
| | 'nextDeprecationDate', |
| | 'supported', |
| | ]), |
| | enterpriseServerVersions: req.context.enterpriseServerVersions, |
| | error: req.context.error ? req.context.error.toString() : '', |
| | featureFlags: {}, |
| | fullUrl: `${req.protocol}://${req.hostname}${req.originalUrl}`, |
| | isHomepageVersion: req.context.page?.documentType === 'homepage', |
| | nonEnterpriseDefaultVersion: req.context.nonEnterpriseDefaultVersion, |
| | page: pageInfo, |
| | relativePath: req.context.page?.relativePath || null, |
| | |
| | |
| | sidebarTree: (includeSidebarTree && req.context.sidebarTree) || null, |
| | status: res.statusCode, |
| | xHost: req.get('x-host') || '', |
| | } |
| |
|
| | return props |
| | } |
| |
|
| | export const MainContext = createContext<MainContextT | null>(null) |
| |
|
| | export const useMainContext = (): MainContextT => { |
| | const context = useContext(MainContext) |
| |
|
| | if (!context) { |
| | throw new Error('"useMainContext" may only be used inside "MainContext.Provider"') |
| | } |
| |
|
| | return context |
| | } |
| |
|