1
0
mirror of synced 2025-12-19 18:10:59 -05:00

Merge pull request #40543 from github/repo-sync

Repo sync
This commit is contained in:
docs-bot
2025-09-25 08:34:40 -07:00
committed by GitHub
15 changed files with 239 additions and 120 deletions

View File

@@ -1,7 +1,7 @@
--- ---
title: Asking GitHub Copilot to create a pull request title: Asking GitHub Copilot to create a pull request
shortTitle: Create a PR shortTitle: Create a PR
intro: 'You can ask {% data variables.product.prodname_copilot_short %} to create a pull request from many places, including the agents panel, {% data variables.copilot.copilot_chat_short %}, and agentic coding tools and IDEs with Model Context Protocol (MCP) support.' intro: 'You can ask {% data variables.product.prodname_copilot_short %} to create a pull request from many places, including the agents panel, {% data variables.copilot.copilot_chat_short %}, the {% data variables.product.prodname_cli %}, and agentic coding tools and IDEs with Model Context Protocol (MCP) support.'
product: '{% data reusables.gated-features.copilot-coding-agent %}<br><a href="https://github.com/features/copilot/plans?ref_cta=Copilot+plans+signup&ref_loc=asking+copilot+to+create+a+pull+request&ref_page=docs" target="_blank" class="btn btn-primary mt-3 mr-3 no-underline"><span>Sign up for {% data variables.product.prodname_copilot_short %}</span> {% octicon "link-external" height:16 %}</a>' product: '{% data reusables.gated-features.copilot-coding-agent %}<br><a href="https://github.com/features/copilot/plans?ref_cta=Copilot+plans+signup&ref_loc=asking+copilot+to+create+a+pull+request&ref_page=docs" target="_blank" class="btn btn-primary mt-3 mr-3 no-underline"><span>Sign up for {% data variables.product.prodname_copilot_short %}</span> {% octicon "link-external" height:16 %}</a>'
versions: versions:
feature: copilot feature: copilot
@@ -28,6 +28,7 @@ You can ask {% data variables.product.prodname_copilot_short %} to work on a tas
* The agents panel, available across {% data variables.product.github %} * The agents panel, available across {% data variables.product.github %}
* The agents page on {% data variables.product.github %} * The agents page on {% data variables.product.github %}
* {% data variables.copilot.copilot_chat_short %} in {% data variables.product.prodname_vscode %}, {% data variables.product.prodname_vs %}, JetBrains IDEs and {% data variables.product.prodname_dotcom_the_website %} * {% data variables.copilot.copilot_chat_short %} in {% data variables.product.prodname_vscode %}, {% data variables.product.prodname_vs %}, JetBrains IDEs and {% data variables.product.prodname_dotcom_the_website %}
* The {% data variables.product.prodname_cli %}
* Your preferred IDE or agentic coding tool with Model Context Protocol (MCP) support * Your preferred IDE or agentic coding tool with Model Context Protocol (MCP) support
* The Raycast launcher on macOS * The Raycast launcher on macOS
@@ -100,6 +101,25 @@ You can ask {% data variables.product.prodname_copilot_short %} to open a pull r
{% data variables.product.prodname_copilot_short %} will start a new session and respond with a link to the pull request it creates. It will work on the task and push changes to the pull request, and then add you as a reviewer when it has finished, triggering a notification. {% data variables.product.prodname_copilot_short %} will start a new session and respond with a link to the pull request it creates. It will work on the task and push changes to the pull request, and then add you as a reviewer when it has finished, triggering a notification.
## Asking {% data variables.product.prodname_copilot_short %} to create a pull request from the {% data variables.product.prodname_cli %}
> [!NOTE]
> The `agent-task` command set is only available in v2.80.0 or later of the {% data variables.product.prodname_cli %}. This command set is a {% data variables.release-phases.public_preview %} and is subject to change.
You can start a new {% data variables.copilot.copilot_coding_agent %} session with the `gh agent-task create` command in the {% data variables.product.prodname_cli %}.
When you run the command without any arguments, you are asked to enter a prompt. {% data variables.copilot.copilot_coding_agent %} acts on the prompt and opens a pull request in the current repository.
You can use command line options to:
* Provide the prompt (`gh agent-task create "Example prompt"`)
* Choose a base branch, instead of using the repository's default branch (`--base`)
* Select a repository, instead of targeting the current repository (`--repo`)
* Follow the session log in real time (`--follow`)
To see all of the available options, run `gh agent-task create --help`.
## Asking {% data variables.product.prodname_copilot_short %} to create a pull request from the {% data variables.product.github %} MCP server ## Asking {% data variables.product.prodname_copilot_short %} to create a pull request from the {% data variables.product.github %} MCP server
As an alternative to using {% data variables.copilot.copilot_chat_short %}, you can use the remote {% data variables.product.github %} MCP server to trigger {% data variables.copilot.copilot_coding_agent %} from any MCP host. As an alternative to using {% data variables.copilot.copilot_chat_short %}, you can use the remote {% data variables.product.github %} MCP server to trigger {% data variables.copilot.copilot_coding_agent %} from any MCP host.

View File

@@ -1,7 +1,7 @@
--- ---
title: Tracking GitHub Copilot's sessions title: Tracking GitHub Copilot's sessions
shortTitle: Track Copilot sessions shortTitle: Track Copilot sessions
intro: 'You can use the agents panel or page, {% data variables.product.prodname_vscode %}, Raycast and session logs to track {% data variables.product.prodname_copilot_short %}''s progress and understand its approach.' intro: 'You can use the agents panel or page, {% data variables.product.prodname_vscode %}, the {% data variables.product.prodname_cli %}, Raycast and session logs to track {% data variables.product.prodname_copilot_short %}''s progress and understand its approach.'
product: '{% data reusables.gated-features.copilot-coding-agent %}<br><a href="https://github.com/features/copilot/plans?ref_cta=Copilot+plans+signup&ref_loc=using+the+copilot+coding+agent+logs&ref_page=docs" target="_blank" class="btn btn-primary mt-3 mr-3 no-underline"><span>Sign up for {% data variables.product.prodname_copilot_short %}</span> {% octicon "link-external" height:16 %}</a>' product: '{% data reusables.gated-features.copilot-coding-agent %}<br><a href="https://github.com/features/copilot/plans?ref_cta=Copilot+plans+signup&ref_loc=using+the+copilot+coding+agent+logs&ref_page=docs" target="_blank" class="btn btn-primary mt-3 mr-3 no-underline"><span>Sign up for {% data variables.product.prodname_copilot_short %}</span> {% octicon "link-external" height:16 %}</a>'
versions: versions:
feature: copilot feature: copilot
@@ -24,7 +24,7 @@ contentType: how-tos
After you give {% data variables.product.prodname_copilot_short %} a task, it works autonomously in the background to complete it. See [AUTOTITLE](/copilot/concepts/about-copilot-coding-agent). After you give {% data variables.product.prodname_copilot_short %} a task, it works autonomously in the background to complete it. See [AUTOTITLE](/copilot/concepts/about-copilot-coding-agent).
The agents panel, [agents page](https://github.com/copilot/agents), and {% data variables.product.prodname_copilot %} extension for Raycast provide an overview of your agent sessions across repositories. You can use them to kick off new tasks and track {% data variables.product.prodname_copilot_short %}'s progress. The agents panel, [agents page](https://github.com/copilot/agents), the {% data variables.product.prodname_cli %}, and {% data variables.product.prodname_copilot %} extension for Raycast provide an overview of your agent sessions across repositories. You can use them to kick off new tasks and track {% data variables.product.prodname_copilot_short %}'s progress.
You can also track {% data variables.product.prodname_copilot_short %}'s sessions in a specific repository from {% data variables.product.prodname_vscode %}. You can also track {% data variables.product.prodname_copilot_short %}'s sessions in a specific repository from {% data variables.product.prodname_vscode %}.
@@ -46,6 +46,19 @@ To view the session logs, click through to the pull request in the list, then fi
You can also start new agent sessions from the page and panel. See [AUTOTITLE](/copilot/how-tos/agents/copilot-coding-agent/asking-copilot-to-create-a-pull-request). You can also start new agent sessions from the page and panel. See [AUTOTITLE](/copilot/how-tos/agents/copilot-coding-agent/asking-copilot-to-create-a-pull-request).
## Tracking agent sessions from the {% data variables.product.prodname_cli %}
> [!NOTE]
> The `agent-task` command set is only available in v2.80.0 or later of the {% data variables.product.prodname_cli %}. This command set is a {% data variables.release-phases.public_preview %} and is subject to change.
You can see a list of your running and past agent sessions from the {% data variables.product.prodname_cli %} with the `gh agent-task list` command. The output will show a list of your recent sessions.
To see more information on a specific session, use the `gh agent-task view` command. For example, to view information about the session associated with pull request #123 in the `monalisa/bookstore` repository, run `gh agent-task view --repo monalisa/bookstore 123`.
To view the session logs, add the `--log` option. Optionally, use the `--follow` option to stream live logs as the agent works.
To see all of the available options, run `gh agent-task list --help` or `gh agent-task view --help`.
## Tracking agent sessions from Raycast ## Tracking agent sessions from Raycast
{% data reusables.copilot.coding-agent.raycast-intro %} {% data reusables.copilot.coding-agent.raycast-intro %}

View File

@@ -5,14 +5,14 @@ import fs from 'fs'
// is unpredictable in GitHub Actions because of how it does `git clone`. // is unpredictable in GitHub Actions because of how it does `git clone`.
// So we rely on environment variables instead. // So we rely on environment variables instead.
export function getDiffFiles() { export function getDiffFiles(): string[] {
// Instead of testing every single file possible, if there's // Instead of testing every single file possible, if there's
// an environment variable called `DIFF_FILES` or one called // an environment variable called `DIFF_FILES` or one called
// `DIFF_FILE` then use that. // `DIFF_FILE` then use that.
// If `DIFF_FILES` is set, it's expected to be a space separated // If `DIFF_FILES` is set, it's expected to be a space separated
// string. If `DIFF_FILE` is set, it's expected to be a text file // string. If `DIFF_FILE` is set, it's expected to be a text file
// which contains a space separated string. // which contains a space separated string.
const diffFiles = [] const diffFiles: string[] = []
// Setting an environment variable called `DIFF_FILES` is optional. // Setting an environment variable called `DIFF_FILES` is optional.
// But if and only if it's set, we will respect it. // But if and only if it's set, we will respect it.
// And if it set, turn it into a cleaned up Set so it's made available // And if it set, turn it into a cleaned up Set so it's made available

View File

@@ -1,20 +1,24 @@
// @ts-ignore - markdownlint-rule-helpers doesn't provide TypeScript declarations
import { addError, filterTokens, newLineRe } from 'markdownlint-rule-helpers' import { addError, filterTokens, newLineRe } from 'markdownlint-rule-helpers'
export const codeFenceLineLength = { import type { RuleParams, RuleErrorCallback, MarkdownToken, Rule } from '@/content-linter/types'
export const codeFenceLineLength: Rule = {
names: ['GHD030', 'code-fence-line-length'], names: ['GHD030', 'code-fence-line-length'],
description: 'Code fence lines should not exceed a maximum length', description: 'Code fence lines should not exceed a maximum length',
tags: ['code', 'accessibility'], tags: ['code', 'accessibility'],
parser: 'markdownit', parser: 'markdownit',
function: (params, onError) => { function: (params: RuleParams, onError: RuleErrorCallback) => {
const MAX_LINE_LENGTH = String(params.config.maxLength || 60) const MAX_LINE_LENGTH: number = params.config?.maxLength || 60
filterTokens(params, 'fence', (token) => { filterTokens(params, 'fence', (token: MarkdownToken) => {
const lines = token.content.split(newLineRe) if (!token.content) return
lines.forEach((line, index) => { const lines: string[] = token.content.split(newLineRe)
lines.forEach((line: string, index: number) => {
if (line.length > MAX_LINE_LENGTH) { if (line.length > MAX_LINE_LENGTH) {
// The token line number is the line number of the first line of the // The token line number is the line number of the first line of the
// code fence. We want to report the line number of the content within // code fence. We want to report the line number of the content within
// the code fence so we need to add 1 + the index. // the code fence so we need to add 1 + the index.
const lineNumber = token.lineNumber + index + 1 const lineNumber: number = token.lineNumber + index + 1
addError( addError(
onError, onError,
lineNumber, lineNumber,

View File

@@ -1,16 +1,16 @@
// used below to remove extra newlines in TOC lists // used below to remove extra newlines in TOC lists
const endLine = '</a>\r?\n' const endLine: string = '</a>\r?\n'
const blankLine = '\\s*?[\r\n]*' const blankLine: string = '\\s*?[\r\n]*'
const startNextLine = '[^\\S\r\n]*?[-\\*] <a' const startNextLine: string = '[^\\S\r\n]*?[-\\*] <a'
const blankLineInList = new RegExp(`(${endLine})${blankLine}(${startNextLine})`, 'mg') const blankLineInList: RegExp = new RegExp(`(${endLine})${blankLine}(${startNextLine})`, 'mg')
export function processLiquidPost(template) { export function processLiquidPost(template: string): string {
template = cleanUpListEmptyLines(template) template = cleanUpListEmptyLines(template)
template = cleanUpExtraEmptyLines(template) template = cleanUpExtraEmptyLines(template)
return template return template
} }
function cleanUpListEmptyLines(template) { function cleanUpListEmptyLines(template: string): string {
// clean up empty lines in TOC lists left by unrendered list items (due to productVersions) // clean up empty lines in TOC lists left by unrendered list items (due to productVersions)
// for example, remove the blank line here: // for example, remove the blank line here:
// - <a>foo</a> // - <a>foo</a>
@@ -22,7 +22,7 @@ function cleanUpListEmptyLines(template) {
return template return template
} }
function cleanUpExtraEmptyLines(template) { function cleanUpExtraEmptyLines(template: string): string {
// this removes any extra newlines left by (now resolved) liquid // this removes any extra newlines left by (now resolved) liquid
// statements so that extra space doesn't mess with list numbering // statements so that extra space doesn't mess with list numbering
template = template.replace(/(\r?\n){3}/g, '\n\n') template = template.replace(/(\r?\n){3}/g, '\n\n')

View File

@@ -1,17 +1,26 @@
// src/content-render/liquid/prompt.js // src/content-render/liquid/prompt.ts
// Defines {% prompt %}…{% endprompt %} to wrap its content in <code> and append the Copilot icon. // Defines {% prompt %}…{% endprompt %} to wrap its content in <code> and append the Copilot icon.
// @ts-ignore - @primer/octicons doesn't provide TypeScript declarations
import octicons from '@primer/octicons' import octicons from '@primer/octicons'
export const Prompt = { interface LiquidTag {
type: 'block'
templates?: any[] // Note: Using 'any' because liquidjs doesn't provide proper types for template objects
// Note: Using 'any' for liquid-related parameters because liquidjs doesn't provide comprehensive TypeScript definitions
parse(tagToken: any, remainTokens: any): void
render(scope: any): Generator<any, string, unknown>
}
export const Prompt: LiquidTag = {
type: 'block', type: 'block',
// Collect everything until {% endprompt %} // Collect everything until {% endprompt %}
parse(tagToken, remainTokens) { parse(tagToken: any, remainTokens: any): void {
this.templates = [] this.templates = []
const stream = this.liquid.parser.parseStream(remainTokens) const stream = this.liquid.parser.parseStream(remainTokens)
stream stream
.on('template', (tpl) => this.templates.push(tpl)) .on('template', (tpl: any) => this.templates.push(tpl))
.on('tag:endprompt', () => stream.stop()) .on('tag:endprompt', () => stream.stop())
.on('end', () => { .on('end', () => {
throw new Error(`{% prompt %} tag not closed`) throw new Error(`{% prompt %} tag not closed`)
@@ -20,12 +29,12 @@ export const Prompt = {
}, },
// Render the inner Markdown, wrap in <code>, then append the SVG // Render the inner Markdown, wrap in <code>, then append the SVG
render: function* (scope) { render: function* (scope: any): Generator<any, string, unknown> {
const content = yield this.liquid.renderer.renderTemplates(this.templates, scope) const content = yield this.liquid.renderer.renderTemplates(this.templates, scope)
// build a URL with the prompt text encoded as query parameter // build a URL with the prompt text encoded as query parameter
const promptParam = encodeURIComponent(content) const promptParam: string = encodeURIComponent(content as string)
const href = `https://github.com/copilot?prompt=${promptParam}` const href: string = `https://github.com/copilot?prompt=${promptParam}`
return `<code>${content}</code><a href="${href}" target="_blank" class="tooltipped tooltipped-nw ml-1" aria-label="Run this prompt in Copilot Chat" style="text-decoration:none;">${octicons.copilot.toSVG()}</a>` return `<code>${content}</code><a href="${href}" target="_blank" class="tooltipped tooltipped-nw ml-1" aria-label="Run this prompt in Copilot Chat" style="text-decoration:none;">${octicons.copilot.toSVG()}</a>`
}, },
} }

View File

@@ -3,8 +3,8 @@ import { renderContent } from '@/content-render/index'
describe('prompt tag', () => { describe('prompt tag', () => {
test('wraps content in <code> and appends svg', async () => { test('wraps content in <code> and appends svg', async () => {
const input = 'Here is your prompt: {% prompt %}example prompt text{% endprompt %}.' const input: string = 'Here is your prompt: {% prompt %}example prompt text{% endprompt %}.'
const output = await renderContent(input) const output: string = await renderContent(input)
expect(output).toContain('<code>example prompt text</code><a') expect(output).toContain('<code>example prompt text</code><a')
expect(output).toContain('<svg') expect(output).toContain('<svg')
}) })

View File

@@ -1,32 +0,0 @@
// Based on https://spec.commonmark.org/0.30/#info-string
// Parse out info strings on fenced code blocks, example:
// ```javascript lineNumbers:left copy:all annotate
// becomes...
// node.lang = javascript
// node.meta = { lineNumbers: 'left', copy: 'all', annotate: true }
// Also parse equals signs, where id=some-id becomes { id: 'some-id' }
import { visit } from 'unist-util-visit'
const matcher = (node) => node.type === 'code' && node.lang
export default function parseInfoString() {
return (tree) => {
visit(tree, matcher, (node) => {
node.meta = strToObj(node.meta)
// Temporary, remove {:copy} to avoid highlight parse error in translations.
node.lang = node.lang.replace('{:copy}', '')
})
}
}
function strToObj(str) {
if (!str) return {}
return Object.fromEntries(
str
.split(/\s+/g)
.map((k) => k.split(/[:=]/)) // split by colon or equals sign
.map(([k, ...v]) => [k, v.length ? v.join(':') : true]),
)
}

View File

@@ -0,0 +1,45 @@
// Based on https://spec.commonmark.org/0.30/#info-string
// Parse out info strings on fenced code blocks, example:
// ```javascript lineNumbers:left copy:all annotate
// becomes...
// node.lang = javascript
// node.meta = { lineNumbers: 'left', copy: 'all', annotate: true }
// Also parse equals signs, where id=some-id becomes { id: 'some-id' }
import { visit } from 'unist-util-visit'
interface CodeNode {
type: 'code'
lang?: string
meta?: string | Record<string, string | boolean>
value: string
}
// Note: Using 'any' for node because unist-util-visit's type constraints
// don't easily allow for proper code node typing without complex generics
const matcher = (node: any): node is CodeNode => node.type === 'code' && node.lang
export default function parseInfoString() {
// Note: Using 'any' for tree because unified's AST types are complex and
// this function works with different tree types depending on the processor
return (tree: any) => {
visit(tree, matcher, (node: CodeNode) => {
node.meta = strToObj(node.meta as string)
// Temporary, remove {:copy} to avoid highlight parse error in translations.
if (node.lang) {
node.lang = node.lang.replace('{:copy}', '')
}
})
}
}
function strToObj(str?: string): Record<string, string | boolean> {
if (!str) return {}
return Object.fromEntries(
str
.split(/\s+/g)
.map((k: string) => k.split(/[:=]/)) // split by colon or equals sign
.map(([k, ...v]: string[]) => [k, v.length ? v.join(':') : true]),
)
}

View File

@@ -1,29 +0,0 @@
import GithubSlugger from 'github-slugger'
import { encode } from 'html-entities'
import { toString } from 'hast-util-to-string'
import { visit } from 'unist-util-visit'
const slugger = new GithubSlugger()
const matcher = (node) => node.type === 'element' && ['h2', 'h3', 'h4'].includes(node.tagName)
// replace translated IDs and links in headings with English
export default function useEnglishHeadings({ englishHeadings }) {
if (!englishHeadings) return
return (tree) => {
visit(tree, matcher, (node) => {
slugger.reset()
// Get the plain text content of the heading node
const text = toString(node)
// find English heading in the collection
const englishHeading = englishHeadings[encode(text)]
// get English slug
const englishSlug = slugger.slug(englishHeading)
// use English slug for heading ID and link
if (englishSlug) {
// only use English slug if there is one, otherwise we'll end up with
// empty IDs
node.properties.id = englishSlug
}
})
}
}

View File

@@ -0,0 +1,41 @@
import GithubSlugger from 'github-slugger'
import { encode } from 'html-entities'
import { toString } from 'hast-util-to-string'
import { visit } from 'unist-util-visit'
const slugger = new GithubSlugger()
// Note: Using 'any' for node because the unist/hast type system is complex and
// the visit function's type constraints don't easily allow for proper element typing
// without extensive type gymnastics. The runtime check ensures type safety.
const matcher = (node: any) => node.type === 'element' && ['h2', 'h3', 'h4'].includes(node.tagName)
interface UseEnglishHeadingsOptions {
englishHeadings?: Record<string, string>
}
// replace translated IDs and links in headings with English
export default function useEnglishHeadings({ englishHeadings }: UseEnglishHeadingsOptions) {
if (!englishHeadings) return
// Note: Using 'any' for tree because unified's AST types are complex and
// this function works with different tree types depending on the processor
return (tree: any) => {
// Note: Using 'any' for node because visit() callback typing is restrictive
// and doesn't easily allow for proper element typing without complex generics
visit(tree, matcher, (node: any) => {
slugger.reset()
// Get the plain text content of the heading node
const text: string = toString(node)
// find English heading in the collection
const englishHeading: string = englishHeadings[encode(text)]
// get English slug
const englishSlug: string = slugger.slug(englishHeading)
// use English slug for heading ID and link
if (englishSlug) {
// only use English slug if there is one, otherwise we'll end up with
// empty IDs
node.properties.id = englishSlug
}
})
}
}

View File

@@ -3,7 +3,23 @@
// src/github-apps/data/user-to-server-rest.json // src/github-apps/data/user-to-server-rest.json
// and src/github-apps/data/fine-grained-pat.json // and src/github-apps/data/fine-grained-pat.json
export default { interface SchemaProperty {
description: string
type: string
}
interface EnabledListSchema {
type: string
required: string[]
properties: {
slug: SchemaProperty
subcategory: SchemaProperty
verb: SchemaProperty
requestPath: SchemaProperty
}
}
const schema: EnabledListSchema = {
type: 'object', type: 'object',
required: ['slug', 'subcategory', 'verb', 'requestPath'], required: ['slug', 'subcategory', 'verb', 'requestPath'],
properties: { properties: {
@@ -25,3 +41,5 @@ export default {
}, },
}, },
} }
export default schema

View File

@@ -7,12 +7,14 @@ import { REST_DATA_DIR } from '../lib/index'
const clientSideRestAPIRedirects = readCompressedJsonFileFallbackLazily( const clientSideRestAPIRedirects = readCompressedJsonFileFallbackLazily(
path.join(REST_DATA_DIR, 'client-side-rest-api-redirects.json'), path.join(REST_DATA_DIR, 'client-side-rest-api-redirects.json'),
) ) as () => Record<string, string>
const router = express.Router() const router = express.Router()
// Returns a client side redirect if one exists for the given path. // Returns a client side redirect if one exists for the given path.
router.get('/', function redirects(req, res) { // Note: Using 'any' for req/res because Express types are complex and the
// function signature is constrained by the router.get() overloads
router.get('/', function redirects(req: any, res: any) {
if (!req.query.path) { if (!req.query.path) {
return res.status(400).send("Missing 'path' query string") return res.status(400).send("Missing 'path' query string")
} }
@@ -22,7 +24,7 @@ router.get('/', function redirects(req, res) {
defaultCacheControl(res) defaultCacheControl(res)
const redirectFrom = `${req.query.path}#${req.query.hash}` const redirectFrom: string = `${req.query.path}#${req.query.hash}`
res.status(200).send({ to: clientSideRestAPIRedirects()[redirectFrom] }) res.status(200).send({ to: clientSideRestAPIRedirects()[redirectFrom] })
}) })

View File

@@ -1,27 +0,0 @@
const childPage = {
type: 'object',
required: ['href', 'page'],
properties: {
href: {
type: 'string',
},
page: {
type: 'object',
required: ['title', 'relativePath', 'permalinks'],
properties: {
title: {
type: 'string',
},
relativePath: {
type: 'string',
},
permalinks: {
type: 'array',
minItems: 1,
},
},
},
},
}
export default { childPage }

View File

@@ -0,0 +1,55 @@
interface SchemaProperty {
type: string
minItems?: number
}
interface PageProperties {
title: SchemaProperty
relativePath: SchemaProperty
permalinks: SchemaProperty
}
interface PageSchema {
type: string
required: string[]
properties: PageProperties
}
interface ChildPageProperties {
href: SchemaProperty
page: PageSchema
}
interface ChildPageSchema {
type: string
required: string[]
properties: ChildPageProperties
}
const childPage: ChildPageSchema = {
type: 'object',
required: ['href', 'page'],
properties: {
href: {
type: 'string',
},
page: {
type: 'object',
required: ['title', 'relativePath', 'permalinks'],
properties: {
title: {
type: 'string',
},
relativePath: {
type: 'string',
},
permalinks: {
type: 'array',
minItems: 1,
},
},
},
},
}
export default { childPage }