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

Convert additional data-directory files to TypeScript (#56396)

This commit is contained in:
Kevin Heis
2025-07-09 11:04:14 -07:00
committed by GitHub
parent e5e13f8954
commit 8872a88693
9 changed files with 53 additions and 30 deletions

View File

@@ -1,7 +1,7 @@
import yaml from 'js-yaml'
import fs from 'fs/promises'
import dataSchemas from '#src/data-directory/lib/data-schemas/index.js'
import dataSchemas from '#src/data-directory/lib/data-schemas/index.ts'
import ajv from '#src/tests/lib/validate-json-schema.js'
// AJV already has a built-in way to extract out properties

View File

@@ -1,4 +0,0 @@
import { FeatureData } from '@/types.js'
declare function dataDirectory(dir: string, opts?: Object): FeatureData
export default dataDirectory

View File

@@ -4,45 +4,65 @@ import path from 'path'
import walk from 'walk-sync'
import yaml from 'js-yaml'
import { isRegExp, setWith } from 'lodash-es'
import filenameToKey from './filename-to-key.js'
import filenameToKey from './filename-to-key'
import matter from 'gray-matter'
export default function dataDirectory(dir, opts = {}) {
const defaultOpts = {
preprocess: (content) => {
interface DataDirectoryOptions {
preprocess?: (content: string) => string
ignorePatterns?: RegExp[]
extensions?: string[]
}
interface DataDirectoryResult {
[key: string]: any
}
export default function dataDirectory(
dir: string,
opts: DataDirectoryOptions = {},
): DataDirectoryResult {
const defaultOpts: Required<DataDirectoryOptions> = {
preprocess: (content: string) => {
return content
},
ignorePatterns: [/README\.md$/i],
extensions: ['.json', '.md', '.markdown', '.yml'],
}
opts = Object.assign({}, defaultOpts, opts)
const mergedOpts = Object.assign({}, defaultOpts, opts)
// validate input
assert(Array.isArray(opts.ignorePatterns))
assert(opts.ignorePatterns.every(isRegExp))
assert(Array.isArray(opts.extensions))
assert(opts.extensions.length)
assert(Array.isArray(mergedOpts.ignorePatterns))
assert(mergedOpts.ignorePatterns.every(isRegExp))
assert(Array.isArray(mergedOpts.extensions))
assert(mergedOpts.extensions.length)
// start with an empty data object
const data = {}
const data: DataDirectoryResult = {}
// find YAML and Markdown files in the given directory, recursively
const filenames = walk(dir, { includeBasePath: true }).filter((filename) => {
const filenames = walk(dir, { includeBasePath: true }).filter((filename: string) => {
// ignore files that match any of ignorePatterns regexes
if (opts.ignorePatterns.some((pattern) => pattern.test(filename))) return false
if (mergedOpts.ignorePatterns.some((pattern) => pattern.test(filename))) return false
// ignore files that don't have a whitelisted file extension
return opts.extensions.includes(path.extname(filename).toLowerCase())
return mergedOpts.extensions.includes(path.extname(filename).toLowerCase())
})
const files = filenames.map((filename) => [filename, fs.readFileSync(filename, 'utf8')])
const files: [string, string][] = filenames.map((filename: string) => [
filename,
fs.readFileSync(filename, 'utf8'),
])
files.forEach(([filename, fileContent]) => {
// derive `foo.bar.baz` object key from `foo/bar/baz.yml` filename
const key = filenameToKey(path.relative(dir, filename))
const extension = path.extname(filename).toLowerCase()
if (opts.preprocess) fileContent = opts.preprocess(fileContent)
let processedContent = fileContent
if (mergedOpts.preprocess) {
processedContent = mergedOpts.preprocess(fileContent)
}
// Add this file's data to the global data object.
// Note we want to use `setWith` instead of `set` so we can customize the type during path creation.
@@ -51,17 +71,17 @@ export default function dataDirectory(dir, opts = {}) {
// See https://lodash.com/docs#set for an explanation.
switch (extension) {
case '.json':
setWith(data, key, JSON.parse(fileContent), Object)
setWith(data, key, JSON.parse(processedContent), Object)
break
case '.yml':
setWith(data, key, yaml.load(fileContent, { filename }), Object)
setWith(data, key, yaml.load(processedContent, { filename }), Object)
break
case '.md':
case '.markdown':
// Use `matter` to drop frontmatter, since localized reusable Markdown files
// can potentially have frontmatter, but we want to prevent the frontmatter
// from being rendered.
setWith(data, key, matter(fileContent).content, Object)
setWith(data, key, matter(processedContent).content, Object)
break
}
})

View File

@@ -1,4 +1,8 @@
export default {
interface DataSchemas {
[key: string]: string
}
const dataSchemas: DataSchemas = {
'data/features': '#src/data-directory/lib/data-schemas/features.js',
'data/variables': '#src/data-directory/lib/data-schemas/variables.js',
'data/learning-tracks': '#src/data-directory/lib/data-schemas/learning-tracks.js',
@@ -7,3 +11,5 @@ export default {
'data/glossaries/candidates.yml': '#src/data-directory/lib/data-schemas/glossaries-candidates.js',
'data/glossaries/external.yml': '#src/data-directory/lib/data-schemas/glossaries-external.js',
}
export default dataSchemas

View File

@@ -1,5 +1,6 @@
import path from 'path'
import { escapeRegExp } from 'lodash-es'
/* eslint-disable prefer-regex-literals */
// slash at the beginning of a filename
@@ -14,8 +15,8 @@ const windowsPathSeparator = new RegExp('/', 'g')
const windowsDoubleSlashSeparator = new RegExp('\\\\', 'g')
// derive `foo.bar.baz` object key from `foo/bar/baz.yml` filename
export default function filenameToKey(filename) {
const extension = new RegExp(`${path.extname(filename)}$`)
export default function filenameToKey(filename: string): string {
const extension = new RegExp(`${escapeRegExp(path.extname(filename))}$`)
const key = filename
.replace(extension, '')
.replace(leadingPathSeparator, '')

View File

@@ -7,7 +7,7 @@ import { beforeAll, describe, expect, test } from 'vitest'
import { getJsonValidator, validateJson } from '#src/tests/lib/validate-json-schema.js'
import { formatAjvErrors } from '#src/tests/helpers/schemas.js'
import dataSchemas from '#src/data-directory/lib/data-schemas/index.js'
import dataSchemas from '#src/data-directory/lib/data-schemas/index.ts'
const schemaPaths = Object.keys(dataSchemas)
const singleFilesSchemas = schemaPaths.filter((schemaPath) => extname(schemaPath))

View File

@@ -1,6 +1,6 @@
import { describe, expect, test } from 'vitest'
import filenameToKey from '#src/data-directory/lib/filename-to-key.js'
import filenameToKey from '#src/data-directory/lib/filename-to-key.ts'
describe('filename-to-key', () => {
test('converts filenames to object keys', () => {

View File

@@ -3,7 +3,7 @@ import path from 'path'
import { describe, expect, test } from 'vitest'
import dataDirectory from '#src/data-directory/lib/data-directory.js'
import dataDirectory from '#src/data-directory/lib/data-directory.ts'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const fixturesDir = path.join(__dirname, 'fixtures')

View File

@@ -1,7 +1,7 @@
import semver from 'semver'
import { supported } from '@/versions/lib/enterprise-server-releases.js'
import getDataDirectory from '@/data-directory/lib/data-directory.js'
import getDataDirectory from '@/data-directory/lib/data-directory'
import { FeatureData, FrontmatterVersions } from '@/types.js'
// Return true if lowestSupportedVersion > semVerRange