From 8872a88693dc9fc8c3851ec0d34045be09a3f279 Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Wed, 9 Jul 2025 11:04:14 -0700 Subject: [PATCH] Convert additional data-directory files to TypeScript (#56396) --- .../lib/helpers/get-lintable-yml.js | 2 +- src/data-directory/lib/data-directory.d.ts | 4 -- .../{data-directory.js => data-directory.ts} | 56 +++++++++++++------ .../lib/data-schemas/{index.js => index.ts} | 8 ++- ...{filename-to-key.js => filename-to-key.ts} | 5 +- src/data-directory/tests/data-schemas.js | 2 +- src/data-directory/tests/filename-to-key.js | 2 +- src/data-directory/tests/index.js | 2 +- src/ghes-releases/scripts/version-utils.ts | 2 +- 9 files changed, 53 insertions(+), 30 deletions(-) delete mode 100644 src/data-directory/lib/data-directory.d.ts rename src/data-directory/lib/{data-directory.js => data-directory.ts} (54%) rename src/data-directory/lib/data-schemas/{index.js => index.ts} (83%) rename src/data-directory/lib/{filename-to-key.js => filename-to-key.ts} (86%) diff --git a/src/content-linter/lib/helpers/get-lintable-yml.js b/src/content-linter/lib/helpers/get-lintable-yml.js index 438d7690b7..104eaf95c6 100755 --- a/src/content-linter/lib/helpers/get-lintable-yml.js +++ b/src/content-linter/lib/helpers/get-lintable-yml.js @@ -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 diff --git a/src/data-directory/lib/data-directory.d.ts b/src/data-directory/lib/data-directory.d.ts deleted file mode 100644 index 4443af370c..0000000000 --- a/src/data-directory/lib/data-directory.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { FeatureData } from '@/types.js' - -declare function dataDirectory(dir: string, opts?: Object): FeatureData -export default dataDirectory diff --git a/src/data-directory/lib/data-directory.js b/src/data-directory/lib/data-directory.ts similarity index 54% rename from src/data-directory/lib/data-directory.js rename to src/data-directory/lib/data-directory.ts index 0150c7a66c..f9739b4f72 100644 --- a/src/data-directory/lib/data-directory.js +++ b/src/data-directory/lib/data-directory.ts @@ -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 = { + 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 } }) diff --git a/src/data-directory/lib/data-schemas/index.js b/src/data-directory/lib/data-schemas/index.ts similarity index 83% rename from src/data-directory/lib/data-schemas/index.js rename to src/data-directory/lib/data-schemas/index.ts index c7fb2d767c..6b60269fce 100644 --- a/src/data-directory/lib/data-schemas/index.js +++ b/src/data-directory/lib/data-schemas/index.ts @@ -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 diff --git a/src/data-directory/lib/filename-to-key.js b/src/data-directory/lib/filename-to-key.ts similarity index 86% rename from src/data-directory/lib/filename-to-key.js rename to src/data-directory/lib/filename-to-key.ts index 8fc07f05c8..96591e43f3 100644 --- a/src/data-directory/lib/filename-to-key.js +++ b/src/data-directory/lib/filename-to-key.ts @@ -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, '') diff --git a/src/data-directory/tests/data-schemas.js b/src/data-directory/tests/data-schemas.js index a68724bd89..067fe4f23c 100644 --- a/src/data-directory/tests/data-schemas.js +++ b/src/data-directory/tests/data-schemas.js @@ -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)) diff --git a/src/data-directory/tests/filename-to-key.js b/src/data-directory/tests/filename-to-key.js index b8bbcd0852..3141140d06 100644 --- a/src/data-directory/tests/filename-to-key.js +++ b/src/data-directory/tests/filename-to-key.js @@ -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', () => { diff --git a/src/data-directory/tests/index.js b/src/data-directory/tests/index.js index 462934603f..80961b5f29 100644 --- a/src/data-directory/tests/index.js +++ b/src/data-directory/tests/index.js @@ -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') diff --git a/src/ghes-releases/scripts/version-utils.ts b/src/ghes-releases/scripts/version-utils.ts index 25ea9ca82f..202aa48b88 100644 --- a/src/ghes-releases/scripts/version-utils.ts +++ b/src/ghes-releases/scripts/version-utils.ts @@ -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