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

Dynamically register data/tables schemas (#57905)

This commit is contained in:
Sarah Schneider
2025-10-10 16:13:26 -04:00
committed by GitHub
parent 75b169421e
commit 1c681bfc97
4 changed files with 65 additions and 11 deletions

View File

@@ -1,4 +1,4 @@
# Data-driven tables # YAML-powered tables
## Overview ## Overview
@@ -34,7 +34,7 @@ Every data-driven table needs **three files** that work together:
|-----------|----------|---------| |-----------|----------|---------|
| **Data file** | `data/tables/` | Stores the table content in YAML format | | **Data file** | `data/tables/` | Stores the table content in YAML format |
| **Content file** | `content/` | Displays the table using Liquid templating | | **Content file** | `content/` | Displays the table using Liquid templating |
| **Schema file** | `src/data-directory/lib/data-schemas/` | Validates the YAML structure | | **Schema file** | `src/data-directory/lib/data-schemas/tables/` | Validates the YAML structure |
**Estimated time**: 30-60 minutes for a new table **Estimated time**: 30-60 minutes for a new table
@@ -49,7 +49,7 @@ Create a new `.yml` file in `data/tables/` with a descriptive name.
Create a YAML structure that will allow me to generate a table that looks like: Create a YAML structure that will allow me to generate a table that looks like:
[describe your table headers, rows, and columns OR attach an example] [describe your table headers, rows, and columns OR attach an example]
See src/secret-scanning/data/public-docs.yml for an example. See data/tables/supported-code-languages.yml for an example.
``` ```
### Step 2: Create the content display ### Step 2: Create the content display
@@ -62,7 +62,7 @@ Create a Markdown table that is dynamically rendered using Liquid code.
Pull data from data/tables/TABLE_NAME.yml. Pull data from data/tables/TABLE_NAME.yml.
The table should look like: [describe your desired output OR attach an example] The table should look like: [describe your desired output OR attach an example]
See content/code-security/secret-scanning/introduction/supported-secret-scanning-patterns.md for an example. See content/get-started/learning-about-github/github-language-support.md for an example.
Liquid docs: https://shopify.github.io/liquid Liquid docs: https://shopify.github.io/liquid
``` ```
@@ -70,14 +70,14 @@ Liquid docs: https://shopify.github.io/liquid
### Step 3: Create the schema file ### Step 3: Create the schema file
Create a `.ts` file in `src/data-directory/lib/data-schemas/` with the same name as your YAML file. Create a `.ts` file in `src/data-directory/lib/data-schemas/tables/` with the same name as your YAML file.
**Copilot prompt template:** **Copilot prompt template:**
``` ```
Create a TypeScript schema following prior art under data-schemas that enforces Create a TypeScript schema following prior art under data-schemas that enforces
the structure of the data/TABLE_NAME.yml file. the structure of the data/TABLE_NAME.yml file.
See src/data-directory/lib/data-schemas/learning-tracks.ts for an example. See src/data-directory/lib/data-schemas/tables/supported-code-languages.ts for an example.
``` ```
## Testing and validation ## Testing and validation

View File

@@ -1,8 +1,37 @@
import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
interface DataSchemas { interface DataSchemas {
[key: string]: string [key: string]: string
} }
const dataSchemas: DataSchemas = { // Auto-discover table schemas from data/tables/ directory
function loadTableSchemas(): DataSchemas {
const tablesDir = path.join(process.cwd(), 'data/tables')
const schemasDir = path.join(__dirname, 'tables')
const tableSchemas: DataSchemas = {}
if (fs.existsSync(tablesDir)) {
const yamlFiles = fs.readdirSync(tablesDir).filter((file) => file.endsWith('.yml'))
for (const yamlFile of yamlFiles) {
const name = path.basename(yamlFile, '.yml')
const schemaPath = path.join(schemasDir, `${name}.ts`)
if (fs.existsSync(schemaPath)) {
tableSchemas[`data/tables/${yamlFile}`] = `@/data-directory/lib/data-schemas/tables/${name}`
}
}
}
return tableSchemas
}
// Manual schema registrations for non-table data
const manualSchemas: DataSchemas = {
'data/features': '@/data-directory/lib/data-schemas/features.js', 'data/features': '@/data-directory/lib/data-schemas/features.js',
'data/variables': '@/data-directory/lib/data-schemas/variables', 'data/variables': '@/data-directory/lib/data-schemas/variables',
'data/learning-tracks': '@/data-directory/lib/data-schemas/learning-tracks.js', 'data/learning-tracks': '@/data-directory/lib/data-schemas/learning-tracks.js',
@@ -10,8 +39,12 @@ const dataSchemas: DataSchemas = {
'data/code-languages.yml': '@/data-directory/lib/data-schemas/code-languages', 'data/code-languages.yml': '@/data-directory/lib/data-schemas/code-languages',
'data/glossaries/candidates.yml': '@/data-directory/lib/data-schemas/glossaries-candidates.js', 'data/glossaries/candidates.yml': '@/data-directory/lib/data-schemas/glossaries-candidates.js',
'data/glossaries/external.yml': '@/data-directory/lib/data-schemas/glossaries-external.js', 'data/glossaries/external.yml': '@/data-directory/lib/data-schemas/glossaries-external.js',
'data/tables/supported-code-languages.yml': }
'@/data-directory/lib/data-schemas/supported-code-languages.js',
// Combine manual registrations with auto-discovered table schemas
const dataSchemas: DataSchemas = {
...manualSchemas,
...loadTableSchemas(),
} }
export default dataSchemas export default dataSchemas

View File

@@ -1,6 +1,6 @@
import yaml from 'js-yaml' import yaml from 'js-yaml'
import { readFileSync } from 'fs' import { readFileSync, existsSync, readdirSync } from 'fs'
import { extname, basename } from 'path' import { extname, basename, join } from 'path'
import walk from 'walk-sync' import walk from 'walk-sync'
import { beforeAll, describe, expect, test } from 'vitest' import { beforeAll, describe, expect, test } from 'vitest'
@@ -49,3 +49,24 @@ describe('single data files', () => {
expect(isValid, formattedErrors).toBe(true) expect(isValid, formattedErrors).toBe(true)
}) })
}) })
describe('YAML-powered tables', () => {
test('all table files have corresponding schemas', () => {
const tablesDir = join(process.cwd(), 'data/tables')
const schemasDir = join(__dirname, '../lib/data-schemas/tables')
if (existsSync(tablesDir)) {
const yamlFiles = readdirSync(tablesDir).filter((file) => file.endsWith('.yml'))
for (const yamlFile of yamlFiles) {
const name = basename(yamlFile, '.yml')
const schemaPath = join(schemasDir, `${name}.ts`)
expect(existsSync(schemaPath)).toBe(true)
// Also verify it's registered in the dataSchemas
const dataKey = `data/tables/${yamlFile}`
expect(dataSchemas[dataKey]).toBeDefined()
}
}
})
})