Migrate 7 JavaScript files to TypeScript (#57816)
This commit is contained in:
@@ -1,15 +1,26 @@
|
|||||||
import { renderLiquid } from './liquid/index'
|
import { renderLiquid } from './liquid/index'
|
||||||
import { renderMarkdown, renderUnified } from './unified/index'
|
import { renderMarkdown, renderUnified } from './unified/index'
|
||||||
import { engine } from './liquid/engine'
|
import { engine } from './liquid/engine'
|
||||||
|
import type { Context } from '@/types'
|
||||||
|
|
||||||
const globalCache = new Map()
|
interface RenderOptions {
|
||||||
|
cache?: boolean | ((template: string, context: Context) => string)
|
||||||
|
filename?: string
|
||||||
|
textOnly?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const globalCache = new Map<string, string>()
|
||||||
|
|
||||||
// parse multiple times because some templates contain more templates. :]
|
// parse multiple times because some templates contain more templates. :]
|
||||||
export async function renderContent(template = '', context = {}, options = {}) {
|
export async function renderContent(
|
||||||
|
template = '',
|
||||||
|
context: Context = {} as Context,
|
||||||
|
options: RenderOptions = {},
|
||||||
|
): Promise<string> {
|
||||||
// If called with a falsy template, it can't ever become something
|
// If called with a falsy template, it can't ever become something
|
||||||
// when rendered. We can exit early to save some pointless work.
|
// when rendered. We can exit early to save some pointless work.
|
||||||
if (!template) return template
|
if (!template) return template
|
||||||
let cacheKey = null
|
let cacheKey: string | null = null
|
||||||
if (options && options.cache) {
|
if (options && options.cache) {
|
||||||
if (!context) throw new Error("If setting 'cache' in options, the 'context' must be set too")
|
if (!context) throw new Error("If setting 'cache' in options, the 'context' must be set too")
|
||||||
if (typeof options.cache === 'function') {
|
if (typeof options.cache === 'function') {
|
||||||
@@ -21,13 +32,13 @@ export async function renderContent(template = '', context = {}, options = {}) {
|
|||||||
throw new Error('cache option must return a string if truthy')
|
throw new Error('cache option must return a string if truthy')
|
||||||
}
|
}
|
||||||
if (globalCache.has(cacheKey)) {
|
if (globalCache.has(cacheKey)) {
|
||||||
return globalCache.get(cacheKey)
|
return globalCache.get(cacheKey) as string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
template = await renderLiquid(template, context)
|
template = await renderLiquid(template, context)
|
||||||
if (context.markdownRequested) {
|
if (context.markdownRequested) {
|
||||||
const md = await renderMarkdown(template, context, options)
|
const md = await renderMarkdown(template, context)
|
||||||
|
|
||||||
return md
|
return md
|
||||||
}
|
}
|
||||||
@@ -45,7 +56,7 @@ export async function renderContent(template = '', context = {}, options = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDefaultCacheKey(template, context) {
|
function getDefaultCacheKey(template: string, context: Context): string {
|
||||||
return `${template}:${context.currentVersion}:${context.currentLanguage}`
|
return `${template}:${context.currentVersion}:${context.currentLanguage}`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
import { Liquid } from 'liquidjs'
|
|
||||||
import GithubSlugger from 'github-slugger'
|
|
||||||
import IndentedDataReference from './indented-data-reference'
|
|
||||||
import Data from './data'
|
|
||||||
import Octicon from './octicon'
|
|
||||||
import Ifversion from './ifversion'
|
|
||||||
import { Tool, tags as toolTags } from './tool'
|
|
||||||
import { Spotlight, tags as spotlightTags } from './spotlight'
|
|
||||||
import { Prompt } from './prompt'
|
|
||||||
|
|
||||||
export const engine = new Liquid({
|
|
||||||
extname: '.html',
|
|
||||||
dynamicPartials: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
engine.registerTag('indented_data_reference', IndentedDataReference)
|
|
||||||
engine.registerTag('data', Data)
|
|
||||||
engine.registerTag('octicon', Octicon)
|
|
||||||
engine.registerTag('ifversion', Ifversion)
|
|
||||||
|
|
||||||
for (const tag of toolTags) {
|
|
||||||
engine.registerTag(tag, Tool)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const tag in spotlightTags) {
|
|
||||||
engine.registerTag(tag, Spotlight)
|
|
||||||
}
|
|
||||||
|
|
||||||
engine.registerTag('prompt', Prompt)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Like the `size` filter, but specifically for
|
|
||||||
* getting the number of keys in an object
|
|
||||||
*/
|
|
||||||
engine.registerFilter('obj_size', (input) => {
|
|
||||||
if (!input) return 0
|
|
||||||
return Object.keys(input).length
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the version number of a GHES version string
|
|
||||||
* ex: enterprise-server@2.22 => 2.22
|
|
||||||
*/
|
|
||||||
engine.registerFilter('version_num', (input) => {
|
|
||||||
return input.split('@')[1]
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert the input to a slug
|
|
||||||
*/
|
|
||||||
engine.registerFilter('slugify', (input) => {
|
|
||||||
const slugger = new GithubSlugger()
|
|
||||||
return slugger.slug(input)
|
|
||||||
})
|
|
||||||
66
src/content-render/liquid/engine.ts
Normal file
66
src/content-render/liquid/engine.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { Liquid } from 'liquidjs'
|
||||||
|
import GithubSlugger from 'github-slugger'
|
||||||
|
// Copilot: When converting data.js to TypeScript, remove the 'as any' assertion for anyData below
|
||||||
|
import Data from './data'
|
||||||
|
import Octicon from './octicon'
|
||||||
|
// Copilot: When converting ifversion.js to TypeScript, remove the 'as any' assertion for anyIfversion below
|
||||||
|
import Ifversion from './ifversion'
|
||||||
|
// Copilot: When converting tool.js to TypeScript, remove the 'as any' assertion for anyTool below
|
||||||
|
import { Tool, tags as toolTags } from './tool'
|
||||||
|
import { Spotlight, tags as spotlightTags } from './spotlight'
|
||||||
|
import { Prompt } from './prompt'
|
||||||
|
import IndentedDataReference from './indented-data-reference'
|
||||||
|
|
||||||
|
// Type assertions for .js files without type definitions
|
||||||
|
// Copilot: Remove these assertions when the corresponding .js files are converted to TypeScript
|
||||||
|
const anyData = Data as any
|
||||||
|
const anyIfversion = Ifversion as any
|
||||||
|
const anyTool = Tool as any
|
||||||
|
const anySpotlight = Spotlight as any
|
||||||
|
const anyPrompt = Prompt as any
|
||||||
|
const anyIndentedDataReference = IndentedDataReference as any
|
||||||
|
|
||||||
|
export const engine = new Liquid({
|
||||||
|
extname: '.html',
|
||||||
|
dynamicPartials: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
engine.registerTag('indented_data_reference', anyIndentedDataReference)
|
||||||
|
engine.registerTag('data', anyData)
|
||||||
|
engine.registerTag('octicon', Octicon)
|
||||||
|
engine.registerTag('ifversion', anyIfversion)
|
||||||
|
|
||||||
|
for (const tag of toolTags) {
|
||||||
|
engine.registerTag(tag, anyTool)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const tag in spotlightTags) {
|
||||||
|
engine.registerTag(tag, anySpotlight)
|
||||||
|
}
|
||||||
|
|
||||||
|
engine.registerTag('prompt', anyPrompt)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like the `size` filter, but specifically for
|
||||||
|
* getting the number of keys in an object
|
||||||
|
*/
|
||||||
|
engine.registerFilter('obj_size', (input: Record<string, unknown> | null | undefined): number => {
|
||||||
|
if (!input) return 0
|
||||||
|
return Object.keys(input).length
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the version number of a GHES version string
|
||||||
|
* ex: enterprise-server@2.22 => 2.22
|
||||||
|
*/
|
||||||
|
engine.registerFilter('version_num', (input: string): string => {
|
||||||
|
return input.split('@')[1]
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert the input to a slug
|
||||||
|
*/
|
||||||
|
engine.registerFilter('slugify', (input: string): string => {
|
||||||
|
const slugger = new GithubSlugger()
|
||||||
|
return slugger.slug(input)
|
||||||
|
})
|
||||||
@@ -3,6 +3,21 @@ import assert from 'assert'
|
|||||||
import { THROW_ON_EMPTY, IndentedDataReferenceError } from './error-handling'
|
import { THROW_ON_EMPTY, IndentedDataReferenceError } from './error-handling'
|
||||||
import { getDataByLanguage } from '@/data-directory/lib/get-data'
|
import { getDataByLanguage } from '@/data-directory/lib/get-data'
|
||||||
|
|
||||||
|
// Note: Using 'any' for liquidjs-related types because liquidjs doesn't provide comprehensive TypeScript definitions
|
||||||
|
interface LiquidTag {
|
||||||
|
markup: string
|
||||||
|
liquid: any
|
||||||
|
parse(tagToken: any): void
|
||||||
|
render(scope: any): Promise<string | undefined>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LiquidScope {
|
||||||
|
environments: {
|
||||||
|
currentLanguage: string
|
||||||
|
[key: string]: any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This class supports a tag that expects two parameters, a data reference and `spaces=NUMBER`:
|
// This class supports a tag that expects two parameters, a data reference and `spaces=NUMBER`:
|
||||||
//
|
//
|
||||||
// {% indented_data_reference foo.bar spaces=NUMBER %}
|
// {% indented_data_reference foo.bar spaces=NUMBER %}
|
||||||
@@ -13,12 +28,15 @@ import { getDataByLanguage } from '@/data-directory/lib/get-data'
|
|||||||
// reference is used inside a block element (like a list or nested list) without
|
// reference is used inside a block element (like a list or nested list) without
|
||||||
// affecting the formatting when the reference is used elsewhere via {{ site.data.foo.bar }}.
|
// affecting the formatting when the reference is used elsewhere via {{ site.data.foo.bar }}.
|
||||||
|
|
||||||
export default {
|
const IndentedDataReference: LiquidTag = {
|
||||||
parse(tagToken) {
|
markup: '',
|
||||||
|
liquid: null as any,
|
||||||
|
|
||||||
|
parse(tagToken: any): void {
|
||||||
this.markup = tagToken.args.trim()
|
this.markup = tagToken.args.trim()
|
||||||
},
|
},
|
||||||
|
|
||||||
async render(scope) {
|
async render(scope: LiquidScope): Promise<string | undefined> {
|
||||||
// obfuscate first legit space, remove all other spaces, then restore legit space
|
// obfuscate first legit space, remove all other spaces, then restore legit space
|
||||||
// this way we can support spaces=NUMBER as well as spaces = NUMBER
|
// this way we can support spaces=NUMBER as well as spaces = NUMBER
|
||||||
const input = this.markup
|
const input = this.markup
|
||||||
@@ -29,12 +47,15 @@ export default {
|
|||||||
const [dataReference, spaces] = input.split(' ')
|
const [dataReference, spaces] = input.split(' ')
|
||||||
|
|
||||||
// if no spaces are specified, default to 2
|
// if no spaces are specified, default to 2
|
||||||
const numSpaces = spaces ? spaces.replace(/spaces=/, '') : '2'
|
const numSpaces: string = spaces ? spaces.replace(/spaces=/, '') : '2'
|
||||||
|
|
||||||
assert(parseInt(numSpaces) || numSpaces === '0', '"spaces=NUMBER" must include a number')
|
assert(parseInt(numSpaces) || numSpaces === '0', '"spaces=NUMBER" must include a number')
|
||||||
|
|
||||||
// Get the referenced value from the context
|
// Get the referenced value from the context
|
||||||
const text = getDataByLanguage(dataReference, scope.environments.currentLanguage)
|
const text: string | undefined = getDataByLanguage(
|
||||||
|
dataReference,
|
||||||
|
scope.environments.currentLanguage,
|
||||||
|
)
|
||||||
if (text === undefined) {
|
if (text === undefined) {
|
||||||
if (scope.environments.currentLanguage === 'en') {
|
if (scope.environments.currentLanguage === 'en') {
|
||||||
const message = `Can't find the key 'indented_data_reference ${dataReference}' in the scope.`
|
const message = `Can't find the key 'indented_data_reference ${dataReference}' in the scope.`
|
||||||
@@ -47,8 +68,10 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add spaces to each line
|
// add spaces to each line
|
||||||
const renderedReferenceWithIndent = text.replace(/^/gm, ' '.repeat(numSpaces))
|
const renderedReferenceWithIndent: string = text.replace(/^/gm, ' '.repeat(parseInt(numSpaces)))
|
||||||
|
|
||||||
return this.liquid.parseAndRender(renderedReferenceWithIndent, scope.environments)
|
return this.liquid.parseAndRender(renderedReferenceWithIndent, scope.environments)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default IndentedDataReference
|
||||||
@@ -1,6 +1,22 @@
|
|||||||
import { TokenizationError } from 'liquidjs'
|
import { TokenizationError } from 'liquidjs'
|
||||||
|
// @ts-ignore - @primer/octicons doesn't provide TypeScript declarations
|
||||||
import octicons from '@primer/octicons'
|
import octicons from '@primer/octicons'
|
||||||
|
|
||||||
|
// Note: Using 'any' for liquidjs-related types because liquidjs doesn't provide comprehensive TypeScript definitions
|
||||||
|
interface LiquidTag {
|
||||||
|
icon: string
|
||||||
|
options: Record<string, string>
|
||||||
|
parse(tagToken: any): void
|
||||||
|
render(): Promise<string>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OcticonsMatch {
|
||||||
|
groups: {
|
||||||
|
icon: string
|
||||||
|
options?: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const OptionsSyntax = /([a-zA-Z-]+)="([\w\s-]+)"*/g
|
const OptionsSyntax = /([a-zA-Z-]+)="([\w\s-]+)"*/g
|
||||||
const Syntax = new RegExp('"(?<icon>[a-zA-Z-]+)"(?<options>(?:\\s' + OptionsSyntax.source + ')*)')
|
const Syntax = new RegExp('"(?<icon>[a-zA-Z-]+)"(?<options>(?:\\s' + OptionsSyntax.source + ')*)')
|
||||||
const SyntaxHelp = 'Syntax Error in tag \'octicon\' - Valid syntax: octicon "<name>" <key="value">'
|
const SyntaxHelp = 'Syntax Error in tag \'octicon\' - Valid syntax: octicon "<name>" <key="value">'
|
||||||
@@ -12,9 +28,12 @@ const SyntaxHelp = 'Syntax Error in tag \'octicon\' - Valid syntax: octicon "<na
|
|||||||
* {% octicon "check" %}
|
* {% octicon "check" %}
|
||||||
* {% octicon "check" width="64" aria-label="Example label" %}
|
* {% octicon "check" width="64" aria-label="Example label" %}
|
||||||
*/
|
*/
|
||||||
export default {
|
const Octicon: LiquidTag = {
|
||||||
parse(tagToken) {
|
icon: '',
|
||||||
const match = tagToken.args.match(Syntax)
|
options: {},
|
||||||
|
|
||||||
|
parse(tagToken: any): void {
|
||||||
|
const match: OcticonsMatch | null = tagToken.args.match(Syntax)
|
||||||
if (!match) {
|
if (!match) {
|
||||||
throw new TokenizationError(SyntaxHelp, tagToken)
|
throw new TokenizationError(SyntaxHelp, tagToken)
|
||||||
}
|
}
|
||||||
@@ -32,7 +51,7 @@ export default {
|
|||||||
|
|
||||||
// Memoize any options passed
|
// Memoize any options passed
|
||||||
if (match.groups.options) {
|
if (match.groups.options) {
|
||||||
let optionsMatch
|
let optionsMatch: RegExpExecArray | null
|
||||||
|
|
||||||
// Loop through each option matching the OptionsSyntax regex
|
// Loop through each option matching the OptionsSyntax regex
|
||||||
while ((optionsMatch = OptionsSyntax.exec(match.groups.options))) {
|
while ((optionsMatch = OptionsSyntax.exec(match.groups.options))) {
|
||||||
@@ -46,13 +65,15 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async render() {
|
async render(): Promise<string> {
|
||||||
// Throw an error if the requested octicon does not exist.
|
// Throw an error if the requested octicon does not exist.
|
||||||
if (!Object.prototype.hasOwnProperty.call(octicons, this.icon)) {
|
if (!Object.prototype.hasOwnProperty.call(octicons, this.icon)) {
|
||||||
throw new Error(`Octicon ${this.icon} does not exist`)
|
throw new Error(`Octicon ${this.icon} does not exist`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = octicons[this.icon].toSVG(this.options)
|
const result: string = octicons[this.icon].toSVG(this.options)
|
||||||
return result
|
return result
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default Octicon
|
||||||
@@ -5,18 +5,31 @@ import { readCompressedJsonFileFallback } from '@/frame/lib/read-json-file'
|
|||||||
import { getOpenApiVersion } from '@/versions/lib/all-versions'
|
import { getOpenApiVersion } from '@/versions/lib/all-versions'
|
||||||
import { categoriesWithoutSubcategories } from '../../rest/lib/index'
|
import { categoriesWithoutSubcategories } from '../../rest/lib/index'
|
||||||
|
|
||||||
|
interface AppsConfig {
|
||||||
|
pages: Record<string, unknown>
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: Using 'any' for AppsData to maintain compatibility with existing consumers that expect different shapes
|
||||||
|
type AppsData = any
|
||||||
|
|
||||||
const ENABLED_APPS_DIR = 'src/github-apps/data'
|
const ENABLED_APPS_DIR = 'src/github-apps/data'
|
||||||
const githubAppsData = new Map()
|
const githubAppsData = new Map<string, Map<string, AppsData>>()
|
||||||
|
|
||||||
// Initialize the Map with the page type keys listed under `pages`
|
// Initialize the Map with the page type keys listed under `pages`
|
||||||
// in the config.json file.
|
// in the config.json file.
|
||||||
const appsDataConfig = JSON.parse(fs.readFileSync('src/github-apps/lib/config.json', 'utf8'))
|
const appsDataConfig: AppsConfig = JSON.parse(
|
||||||
|
fs.readFileSync('src/github-apps/lib/config.json', 'utf8'),
|
||||||
|
)
|
||||||
for (const pageType of Object.keys(appsDataConfig.pages)) {
|
for (const pageType of Object.keys(appsDataConfig.pages)) {
|
||||||
githubAppsData.set(pageType, new Map())
|
githubAppsData.set(pageType, new Map<string, AppsData>())
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAppsData(pageType, docsVersion, apiVersion) {
|
export async function getAppsData(
|
||||||
const pageTypeMap = githubAppsData.get(pageType)
|
pageType: string,
|
||||||
|
docsVersion: string,
|
||||||
|
apiVersion?: string,
|
||||||
|
): Promise<AppsData> {
|
||||||
|
const pageTypeMap = githubAppsData.get(pageType)!
|
||||||
const filename = `${pageType}.json`
|
const filename = `${pageType}.json`
|
||||||
const openApiVersion = getOpenApiVersion(docsVersion) + (apiVersion ? `-${apiVersion}` : '')
|
const openApiVersion = getOpenApiVersion(docsVersion) + (apiVersion ? `-${apiVersion}` : '')
|
||||||
if (!pageTypeMap.has(openApiVersion)) {
|
if (!pageTypeMap.has(openApiVersion)) {
|
||||||
@@ -27,26 +40,34 @@ export async function getAppsData(pageType, docsVersion, apiVersion) {
|
|||||||
pageTypeMap.set(openApiVersion, data)
|
pageTypeMap.set(openApiVersion, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
return pageTypeMap.get(openApiVersion)
|
return pageTypeMap.get(openApiVersion)!
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAppsServerSideProps(context, pageType, { useDisplayTitle = false }) {
|
export async function getAppsServerSideProps(
|
||||||
|
context: any,
|
||||||
|
pageType: string,
|
||||||
|
{ useDisplayTitle = false }: { useDisplayTitle?: boolean },
|
||||||
|
): Promise<{
|
||||||
|
currentVersion: string
|
||||||
|
appsItems: AppsData
|
||||||
|
categoriesWithoutSubcategories: string[]
|
||||||
|
}> {
|
||||||
const { getAutomatedPageMiniTocItems } = await import('@/frame/lib/get-mini-toc-items')
|
const { getAutomatedPageMiniTocItems } = await import('@/frame/lib/get-mini-toc-items')
|
||||||
const { getAutomatedPageContextFromRequest } = await import(
|
const { getAutomatedPageContextFromRequest } = await import(
|
||||||
'@/automated-pipelines/components/AutomatedPageContext'
|
'@/automated-pipelines/components/AutomatedPageContext'
|
||||||
)
|
)
|
||||||
const currentVersion = context.query.versionId
|
const currentVersion: string = context.query.versionId
|
||||||
const allVersions = context.req.context.allVersions
|
const allVersions = context.req.context.allVersions
|
||||||
const queryApiVersion = context.query.apiVersion
|
const queryApiVersion: string = context.query.apiVersion
|
||||||
const apiVersion = allVersions[currentVersion].apiVersions.includes(queryApiVersion)
|
const apiVersion: string = allVersions[currentVersion].apiVersions.includes(queryApiVersion)
|
||||||
? queryApiVersion
|
? queryApiVersion
|
||||||
: allVersions[currentVersion].latestApiVersion
|
: allVersions[currentVersion].latestApiVersion
|
||||||
|
|
||||||
const appsItems = await getAppsData(pageType, currentVersion, apiVersion)
|
const appsItems: AppsData = await getAppsData(pageType, currentVersion, apiVersion)
|
||||||
// Create minitoc
|
// Create minitoc
|
||||||
const { miniTocItems } = getAutomatedPageContextFromRequest(context.req)
|
const { miniTocItems } = getAutomatedPageContextFromRequest(context.req)
|
||||||
const titles = useDisplayTitle
|
const titles: string[] = useDisplayTitle
|
||||||
? Object.values(appsItems).map((item) => item.displayTitle)
|
? Object.values(appsItems).map((item: any) => item.displayTitle!)
|
||||||
: Object.keys(appsItems)
|
: Object.keys(appsItems)
|
||||||
const appMiniToc = await getAutomatedPageMiniTocItems(titles, context)
|
const appMiniToc = await getAutomatedPageMiniTocItems(titles, context)
|
||||||
appMiniToc && miniTocItems.push(...appMiniToc)
|
appMiniToc && miniTocItems.push(...appMiniToc)
|
||||||
@@ -35,7 +35,7 @@ export default function FineGrainedTokenEndpoints({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps<Props> = async (context) => {
|
export const getServerSideProps: GetServerSideProps<Props> = async (context) => {
|
||||||
const { getAppsServerSideProps } = await import('@/github-apps/lib/index.js')
|
const { getAppsServerSideProps } = await import('@/github-apps/lib/index')
|
||||||
const { currentVersion, appsItems, categoriesWithoutSubcategories } =
|
const { currentVersion, appsItems, categoriesWithoutSubcategories } =
|
||||||
await getAppsServerSideProps(context, 'fine-grained-pat', { useDisplayTitle: false })
|
await getAppsServerSideProps(context, 'fine-grained-pat', { useDisplayTitle: false })
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export default function GitHubAppEndpoints({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps<Props> = async (context) => {
|
export const getServerSideProps: GetServerSideProps<Props> = async (context) => {
|
||||||
const { getAppsServerSideProps } = await import('@/github-apps/lib/index.js')
|
const { getAppsServerSideProps } = await import('@/github-apps/lib/index')
|
||||||
const { currentVersion, appsItems, categoriesWithoutSubcategories } =
|
const { currentVersion, appsItems, categoriesWithoutSubcategories } =
|
||||||
await getAppsServerSideProps(context, 'server-to-server-rest', { useDisplayTitle: false })
|
await getAppsServerSideProps(context, 'server-to-server-rest', { useDisplayTitle: false })
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export default function UserGitHubAppEndpoints({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps<Props> = async (context) => {
|
export const getServerSideProps: GetServerSideProps<Props> = async (context) => {
|
||||||
const { getAppsServerSideProps } = await import('@/github-apps/lib/index.js')
|
const { getAppsServerSideProps } = await import('@/github-apps/lib/index')
|
||||||
const { currentVersion, appsItems, categoriesWithoutSubcategories } =
|
const { currentVersion, appsItems, categoriesWithoutSubcategories } =
|
||||||
await getAppsServerSideProps(context, 'user-to-server-rest', { useDisplayTitle: false })
|
await getAppsServerSideProps(context, 'user-to-server-rest', { useDisplayTitle: false })
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export default function FineGrainedPatPermissions({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps<Props> = async (context) => {
|
export const getServerSideProps: GetServerSideProps<Props> = async (context) => {
|
||||||
const { getAppsServerSideProps } = await import('@/github-apps/lib/index.js')
|
const { getAppsServerSideProps } = await import('@/github-apps/lib/index')
|
||||||
const { currentVersion, appsItems, categoriesWithoutSubcategories } =
|
const { currentVersion, appsItems, categoriesWithoutSubcategories } =
|
||||||
await getAppsServerSideProps(context, 'fine-grained-pat-permissions', { useDisplayTitle: true })
|
await getAppsServerSideProps(context, 'fine-grained-pat-permissions', { useDisplayTitle: true })
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export default function GitHubAppPermissions({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps<Props> = async (context) => {
|
export const getServerSideProps: GetServerSideProps<Props> = async (context) => {
|
||||||
const { getAppsServerSideProps } = await import('@/github-apps/lib/index.js')
|
const { getAppsServerSideProps } = await import('@/github-apps/lib/index')
|
||||||
const { currentVersion, appsItems, categoriesWithoutSubcategories } =
|
const { currentVersion, appsItems, categoriesWithoutSubcategories } =
|
||||||
await getAppsServerSideProps(context, 'server-to-server-permissions', { useDisplayTitle: true })
|
await getAppsServerSideProps(context, 'server-to-server-permissions', { useDisplayTitle: true })
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,20 @@
|
|||||||
// src/github-apps/data/fine-grained-pat-permissions.json
|
// src/github-apps/data/fine-grained-pat-permissions.json
|
||||||
// and src/github-apps/data/server-to-server-permissions.json
|
// and src/github-apps/data/server-to-server-permissions.json
|
||||||
|
|
||||||
const permissionObjects = {
|
interface SchemaProperty {
|
||||||
|
type: string
|
||||||
|
enum?: string[]
|
||||||
|
description?: string
|
||||||
|
items?: object
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Schema {
|
||||||
|
type: string
|
||||||
|
required?: string[]
|
||||||
|
properties: Record<string, SchemaProperty>
|
||||||
|
}
|
||||||
|
|
||||||
|
const permissionObjects: Schema = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
required: ['access', 'category', 'subcategory', 'slug', 'verb', 'requestPath'],
|
required: ['access', 'category', 'subcategory', 'slug', 'verb', 'requestPath'],
|
||||||
properties: {
|
properties: {
|
||||||
@@ -32,7 +45,7 @@ const permissionObjects = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
const schema: Schema = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
required: ['title', 'displayTitle', 'permissions'],
|
required: ['title', 'displayTitle', 'permissions'],
|
||||||
properties: {
|
properties: {
|
||||||
@@ -52,3 +65,5 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default schema
|
||||||
@@ -10,6 +10,10 @@ import { globSync } from 'glob'
|
|||||||
import { program } from 'commander'
|
import { program } from 'commander'
|
||||||
import { createOperations, processOperations } from './utils/get-operations'
|
import { createOperations, processOperations } from './utils/get-operations'
|
||||||
|
|
||||||
|
interface ProgramOptions {
|
||||||
|
files: string[]
|
||||||
|
}
|
||||||
|
|
||||||
program
|
program
|
||||||
.description('Generate dereferenced OpenAPI and decorated schema files.')
|
.description('Generate dereferenced OpenAPI and decorated schema files.')
|
||||||
.requiredOption(
|
.requiredOption(
|
||||||
@@ -18,9 +22,9 @@ program
|
|||||||
)
|
)
|
||||||
.parse(process.argv)
|
.parse(process.argv)
|
||||||
|
|
||||||
const filenames = program.opts().files
|
const filenames: string[] = (program.opts() as ProgramOptions).files
|
||||||
|
|
||||||
const filesToCheck = filenames.flatMap((filename) => globSync(filename))
|
const filesToCheck: string[] = filenames.flatMap((filename: string) => globSync(filename))
|
||||||
|
|
||||||
if (filesToCheck.length) {
|
if (filesToCheck.length) {
|
||||||
check(filesToCheck)
|
check(filesToCheck)
|
||||||
@@ -29,22 +33,22 @@ if (filesToCheck.length) {
|
|||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function check(files) {
|
async function check(files: string[]): Promise<void> {
|
||||||
console.log('Verifying OpenAPI files are valid with decorator')
|
console.log('Verifying OpenAPI files are valid with decorator')
|
||||||
const documents = files.map((filename) => [
|
const documents: [string, any][] = files.map((filename: string) => [
|
||||||
filename,
|
filename,
|
||||||
JSON.parse(fs.readFileSync(path.join(filename))),
|
JSON.parse(fs.readFileSync(path.join(filename), 'utf8')),
|
||||||
])
|
])
|
||||||
|
|
||||||
for (const [filename, schema] of documents) {
|
for (const [filename, schema] of documents as [string, any][]) {
|
||||||
try {
|
try {
|
||||||
// munge OpenAPI definitions object in an array of operations objects
|
// munge OpenAPI definitions object in an array of operations objects
|
||||||
const operations = await createOperations(schema)
|
const operations = await createOperations(schema)
|
||||||
// process each operation, asynchronously rendering markdown and stuff
|
// process each operation, asynchronously rendering markdown and stuff
|
||||||
await processOperations(operations)
|
await processOperations(operations, {})
|
||||||
|
|
||||||
console.log(`Successfully could decorate OpenAPI operations for document ${filename}`)
|
console.log(`Successfully could decorate OpenAPI operations for document ${filename}`)
|
||||||
} catch (error) {
|
} catch (error: unknown) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
console.log(
|
console.log(
|
||||||
`🐛 Whoops! It looks like the decorator script wasn't able to parse the dereferenced schema in file ${filename}. A recent change may not yet be supported by the decorator. Please reach out in the #docs-engineering slack channel for help.`,
|
`🐛 Whoops! It looks like the decorator script wasn't able to parse the dereferenced schema in file ${filename}. A recent change may not yet be supported by the decorator. Please reach out in the #docs-engineering slack channel for help.`,
|
||||||
Reference in New Issue
Block a user