diff --git a/docs/network.md b/docs/network.md index 160c8e8d56f..1a46e514377 100644 --- a/docs/network.md +++ b/docs/network.md @@ -136,6 +136,12 @@ type Movie = { releaseYear: string; }; +type MoviesResponse = { + title: string; + description: string; + movies: Movie[]; +}; + const App = () => { const [isLoading, setLoading] = useState(true); const [data, setData] = useState([]); @@ -143,7 +149,7 @@ const App = () => { const getMovies = async () => { try { const response = await fetch('https://reactnative.dev/movies.json'); - const json = await response.json(); + const json = (await response.json()) as MoviesResponse; setData(json.movies); } catch (error) { console.error(error); diff --git a/packages/lint-examples/bin/eslint-examples-jsx.js b/packages/lint-examples/bin/eslint-examples-jsx.ts similarity index 88% rename from packages/lint-examples/bin/eslint-examples-jsx.js rename to packages/lint-examples/bin/eslint-examples-jsx.ts index 803a295f41d..068c23ed996 100755 --- a/packages/lint-examples/bin/eslint-examples-jsx.js +++ b/packages/lint-examples/bin/eslint-examples-jsx.ts @@ -8,7 +8,7 @@ * @format */ -import lintExamples from '../src/lintExamples.js'; +import lintExamples from '../src/lintExamples.ts'; console.log('Linting JSX docs code examples...'); diff --git a/packages/lint-examples/bin/eslint-examples-tsx.js b/packages/lint-examples/bin/eslint-examples-tsx.ts similarity index 88% rename from packages/lint-examples/bin/eslint-examples-tsx.js rename to packages/lint-examples/bin/eslint-examples-tsx.ts index 628367a2fab..714b6aa90ff 100755 --- a/packages/lint-examples/bin/eslint-examples-tsx.js +++ b/packages/lint-examples/bin/eslint-examples-tsx.ts @@ -8,7 +8,7 @@ * @format */ -import lintExamples from '../src/lintExamples.js'; +import lintExamples from '../src/lintExamples.ts'; console.log('Linting TSX docs code examples...'); diff --git a/packages/lint-examples/bin/tsc-examples.js b/packages/lint-examples/bin/tsc-examples.ts similarity index 87% rename from packages/lint-examples/bin/tsc-examples.js rename to packages/lint-examples/bin/tsc-examples.ts index fee57d0e582..52c00f15abf 100755 --- a/packages/lint-examples/bin/tsc-examples.js +++ b/packages/lint-examples/bin/tsc-examples.ts @@ -8,7 +8,7 @@ * @format */ -import lintExamples from '../src/lintExamples.js'; +import lintExamples from '../src/lintExamples.ts'; console.log('Typechecking TSX docs code examples...'); diff --git a/packages/lint-examples/package.json b/packages/lint-examples/package.json index 862aadd9b7e..42a940dd710 100644 --- a/packages/lint-examples/package.json +++ b/packages/lint-examples/package.json @@ -4,12 +4,12 @@ "private": true, "type": "module", "bin": { - "eslint-examples-jsx": "./bin/eslint-examples-jsx.js", - "eslint-examples-tsx": "./bin/eslint-examples-tsx.js", - "tsc-examples": "./bin/tsc-examples.js" + "eslint-examples-jsx": "./bin/eslint-examples-jsx.ts", + "eslint-examples-tsx": "./bin/eslint-examples-tsx.ts", + "tsc-examples": "./bin/tsc-examples.ts" }, "scripts": { - "lint": "eslint" + "lint": "tsc --noEmit && eslint" }, "devDependencies": { "@babel/core": "^7.28.4", diff --git a/packages/lint-examples/src/lintExamples.js b/packages/lint-examples/src/lintExamples.ts similarity index 82% rename from packages/lint-examples/src/lintExamples.js rename to packages/lint-examples/src/lintExamples.ts index 5adc0209a57..a0fb701bb7a 100755 --- a/packages/lint-examples/src/lintExamples.js +++ b/packages/lint-examples/src/lintExamples.ts @@ -12,6 +12,16 @@ import fs from 'node:fs/promises'; import path from 'node:path'; import {glob} from 'glob'; +/** + * Represents a mapping between a document and its extracted example + */ +interface ExampleMapping { + documentPath: string; + examplePath: string; + offset: number; + length: number; +} + /** * The root document to search for documents */ @@ -38,12 +48,22 @@ const validExtensions = ['js', 'tsx']; * file made by the linter. Commands passed to node are passed to the * underlying command. * - * @param opts.command an npx command to run as the linter tool - * @param opts.args extra arguments to be passed to the linter - * @param opts.extension extension to treat the example as if it does not specify one - * @param opts.writeBack whether to update examples with mutations made by the linter + * @param command an npx command to run as the linter tool + * @param args extra arguments to be passed to the linter + * @param extension extension to treat the example as if it does not specify one + * @param writeBack whether to update examples with mutations made by the linter */ -async function lintExamples({command, args, extension, writeBack}) { +async function lintExamples({ + command, + args, + extension, + writeBack, +}: { + command: string; + args?: string[]; + extension: string; + writeBack: boolean; +}) { if (!validExtensions.includes(extension)) { console.error( `Invalid extension "${extension}" (should be one of ${JSON.stringify( @@ -73,12 +93,12 @@ async function lintExamples({command, args, extension, writeBack}) { * * @param extension extension to treat the example as if it does not specify */ -async function extractExamples(extension) { +async function extractExamples(extension: string): Promise { const documents = await glob.glob('**/*.md', { cwd: documentsRoot, absolute: true, }); - const mappings = []; + const mappings: ExampleMapping[] = []; await fs.mkdir(outputRoot, {recursive: true}); await fs.rm(outputRoot, {recursive: true}); @@ -99,7 +119,10 @@ async function extractExamples(extension) { * @param filename absolute filename of the documents root * @param extension extension to treat the example as if it does not specify */ -async function extractExamplesFromDocument(filename, extension) { +async function extractExamplesFromDocument( + filename: string, + extension: string, +): Promise { const fileContents = await fs.readFile(filename, { encoding: 'utf-8', }); @@ -167,7 +190,7 @@ async function extractExamplesFromDocument(filename, extension) { * @param command an npx command to run as the linter tool * @param args extra arguments to be passed to the linter */ -async function runLinter(command, args) { +async function runLinter(command: string, args: string[]): Promise { const combinedArgs = [...processArgs, ...args]; try { @@ -178,7 +201,14 @@ async function runLinter(command, args) { return 0; } catch (ex) { - return ex.status; + if ( + ex instanceof Error && + 'status' in ex && + typeof ex.status === 'number' + ) { + return ex.status; + } + return 1; } } @@ -187,8 +217,8 @@ async function runLinter(command, args) { * * @param mappings file mappings generated by extractExamples() */ -async function updateDocuments(mappings) { - const mappingsByDocument = {}; +async function updateDocuments(mappings: ExampleMapping[]) { + const mappingsByDocument: Record = {}; for (const mapping of mappings) { if (mappingsByDocument[mapping.documentPath] === undefined) { mappingsByDocument[mapping.documentPath] = []; diff --git a/packages/lint-examples/tsconfig.json b/packages/lint-examples/tsconfig.json index c740ba17ab7..9b9048b5dd7 100644 --- a/packages/lint-examples/tsconfig.json +++ b/packages/lint-examples/tsconfig.json @@ -1,4 +1,4 @@ { "extends": "@react-native/typescript-config/tsconfig.json", - "include": ["./out"] + "include": ["./src", "./bin", "./out"] } diff --git a/plugins/remark-codeblock-language-as-title/package.json b/plugins/remark-codeblock-language-as-title/package.json index 945996c07b7..a4628a5050f 100644 --- a/plugins/remark-codeblock-language-as-title/package.json +++ b/plugins/remark-codeblock-language-as-title/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "private": true, "description": "Remark plugin for using codeblock language as title", - "main": "src/index.js", + "main": "src/index.ts", "type": "module", "keywords": [ "remark", @@ -14,12 +14,14 @@ "src/*" ], "scripts": { - "lint": "eslint ." + "lint": "tsc --noEmit && eslint ." }, "dependencies": { "unist-util-visit": "^5.0.0" }, "devDependencies": { - "remark": "^15.0.1" + "@types/mdast": "^4.0.4", + "remark": "^15.0.1", + "typescript": "^5.9.2" } } diff --git a/plugins/remark-codeblock-language-as-title/src/index.js b/plugins/remark-codeblock-language-as-title/src/index.ts similarity index 81% rename from plugins/remark-codeblock-language-as-title/src/index.js rename to plugins/remark-codeblock-language-as-title/src/index.ts index 1b57e664bcf..64baeacc92c 100644 --- a/plugins/remark-codeblock-language-as-title/src/index.js +++ b/plugins/remark-codeblock-language-as-title/src/index.ts @@ -5,12 +5,10 @@ * LICENSE file in the root directory of this source tree. */ +import {Root} from 'mdast'; + export default function codeblockLanguageAsTitleRemarkPlugin() { - /** - * @param {import('mdast').Root} root - The root node of the Markdown AST - * @returns {Promise} - */ - return async root => { + return async (root: Root) => { const {visit} = await import('unist-util-visit'); visit(root, 'code', node => { if (node.lang) { diff --git a/plugins/remark-codeblock-language-as-title/tsconfig.json b/plugins/remark-codeblock-language-as-title/tsconfig.json new file mode 100644 index 00000000000..2f8f8b925cb --- /dev/null +++ b/plugins/remark-codeblock-language-as-title/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "@react-native/typescript-config/tsconfig.json", + "include": ["./src"], +} diff --git a/plugins/remark-lint-no-dead-urls/package.json b/plugins/remark-lint-no-dead-urls/package.json index 11571c0b29a..6aa1d96887b 100644 --- a/plugins/remark-lint-no-dead-urls/package.json +++ b/plugins/remark-lint-no-dead-urls/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "private": true, "description": "Remark linter rule to check for dead urls", - "main": "src/index.js", + "main": "src/index.ts", "type": "module", "keywords": [ "remark", @@ -14,17 +14,18 @@ "src/*" ], "scripts": { - "lint": "eslint .", + "lint": "tsc --noEmit && eslint .", "test": "yarn node --experimental-vm-modules $(yarn bin jest)" }, "dependencies": { - "got": "^13.0.0", + "got": "^14.6.3", "unified-lint-rule": "^3.0.0", "unist-util-visit": "^5.0.0" }, "devDependencies": { "dedent": "^1.5.3", "jest": "^29.4.3", - "remark": "^15.0.1" + "remark": "^15.0.1", + "typescript": "^5.9.2" } } diff --git a/plugins/remark-lint-no-dead-urls/src/index.js b/plugins/remark-lint-no-dead-urls/src/index.ts similarity index 59% rename from plugins/remark-lint-no-dead-urls/src/index.js rename to plugins/remark-lint-no-dead-urls/src/index.ts index 005a406bc96..40ca10305d6 100644 --- a/plugins/remark-lint-no-dead-urls/src/index.js +++ b/plugins/remark-lint-no-dead-urls/src/index.ts @@ -7,9 +7,12 @@ // Forked from: https://github.com/davidtheclark/remark-lint-no-dead-urls +import {Method, RequestError} from 'got'; +import {Root} from 'mdast'; import {URL} from 'node:url'; import {lintRule} from 'unified-lint-rule'; import {visit} from 'unist-util-visit'; +import type {VFile} from 'vfile'; import {fetch} from './lib.js'; @@ -23,12 +26,17 @@ const HTTP = { }; const uri = { - isLocalhost: url => /^(https?:\/\/)(localhost|127\.0\.0\.1)(:\d+)?/.test(url), - isExternal: url => /(https?:\/\/)/.test(url), - isPath: url => /^\/.*/.test(url), + isLocalhost: (url: string) => + /^(https?:\/\/)(localhost|127\.0\.0\.1)(:\d+)?/.test(url), + isExternal: (url: string) => /(https?:\/\/)/.test(url), + isPath: (url: string) => /^\/.*/.test(url), }; -async function cacheFetch(urlOrPath, method, options) { +async function cacheFetch( + urlOrPath: string, + method: Method, + options: {baseUrl?: string} & Record +) { if (linkCache.has(urlOrPath)) { return [urlOrPath, linkCache.get(urlOrPath)]; } @@ -42,7 +50,10 @@ async function cacheFetch(urlOrPath, method, options) { return [urlOrPath, code]; } -async function naiveLinkCheck(urls, options) { +async function naiveLinkCheck( + urls: string[], + options: {baseUrl?: string} & Record +) { return Promise.allSettled( urls.map(async url => { try { @@ -52,10 +63,10 @@ async function naiveLinkCheck(urls, options) { // Fallback, some endpoints don't support HEAD requests return await cacheFetch(url, 'GET', options); } catch (e) { - if (e.code === 'ERR_GOT_REQUEST_ERROR') { + if (!(e instanceof RequestError)) { throw e; } - const code = e.statusCode ?? e?.response?.statusCode ?? e.code; + const code = e.response?.statusCode ?? e.code; linkCache.set(url, code); return [url, code]; } @@ -64,14 +75,21 @@ async function naiveLinkCheck(urls, options) { ); } -async function noDeadUrls(ast, file, options = {}) { +async function noDeadUrls( + ast: Root, + file: VFile, + options: { + skipUrlPatterns?: string[]; + baseUrl?: string; + } & Record = {} +) { const urlToNodes = new Map(); const {skipUrlPatterns, ...clientOptions} = options; // Grab all possible urls from the markdown visit(ast, ['link', 'image', 'definition'], node => { - const {url} = node; + const {url} = node as {url?: string}; if ( !url || uri.isLocalhost(url) || @@ -97,26 +115,28 @@ async function noDeadUrls(ast, file, options = {}) { const results = await naiveLinkCheck([...urlToNodes.keys()], clientOptions); - for (const {value} of results) { - const [url, statusCode] = value; - const nodes = urlToNodes.get(url) ?? []; + for (const result of results) { + if (result.status === 'fulfilled') { + const [url, statusCode] = result.value; + const nodes = urlToNodes.get(url) ?? []; - if (statusCode === HTTP.OK || statusCode === HTTP.FOUND) { - continue; - } + if (statusCode === HTTP.OK || statusCode === HTTP.FOUND) { + continue; + } - for (const node of nodes) { - switch (statusCode) { - case 'ENOTFOUND': - file.message(`Link to ${url} is broken, domain not found`, node); - break; - case HTTP.TOO_MANY_REQUESTS: - file.message(`Link to ${url} is being rate limited`, node); - break; - case HTTP.NOT_FOUND: - default: - file.message(`Link to ${url} is broken`, node); - break; + for (const node of nodes) { + switch (statusCode) { + case 'ENOTFOUND': + file.message(`Link to ${url} is broken, domain not found`, node); + break; + case HTTP.TOO_MANY_REQUESTS: + file.message(`Link to ${url} is being rate limited`, node); + break; + case HTTP.NOT_FOUND: + default: + file.message(`Link to ${url} is broken`, node); + break; + } } } } diff --git a/plugins/remark-lint-no-dead-urls/src/lib.js b/plugins/remark-lint-no-dead-urls/src/lib.ts similarity index 74% rename from plugins/remark-lint-no-dead-urls/src/lib.js rename to plugins/remark-lint-no-dead-urls/src/lib.ts index 21a7a1a3531..0dfdcd6cc43 100644 --- a/plugins/remark-lint-no-dead-urls/src/lib.js +++ b/plugins/remark-lint-no-dead-urls/src/lib.ts @@ -5,9 +5,9 @@ * LICENSE file in the root directory of this source tree. */ -import got from 'got'; +import got, {Method} from 'got'; -export async function fetch(url, method, options = {}) { +export async function fetch(url: string, method: Method, options = {}) { const {statusCode} = await got(url, { ...options, method, diff --git a/plugins/remark-lint-no-dead-urls/tsconfig.json b/plugins/remark-lint-no-dead-urls/tsconfig.json new file mode 100644 index 00000000000..2f8f8b925cb --- /dev/null +++ b/plugins/remark-lint-no-dead-urls/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "@react-native/typescript-config/tsconfig.json", + "include": ["./src"], +} diff --git a/plugins/remark-snackplayer/package.json b/plugins/remark-snackplayer/package.json index dae3772a351..65990ace48b 100644 --- a/plugins/remark-snackplayer/package.json +++ b/plugins/remark-snackplayer/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "description": "Remark Expo Snack Plugin", - "main": "src/index.js", + "main": "src/index.ts", "type": "module", "keywords": [ "remark", @@ -16,8 +16,8 @@ "src" ], "scripts": { - "lint": "eslint .", - "test": "yarn tape tests/index.js" + "lint": "tsc --noEmit && eslint .", + "test": "yarn tape tests/index.ts" }, "dependencies": { "dedent": "^1.5.3", @@ -25,8 +25,11 @@ "unist-util-visit-parents": "^3.1.1" }, "devDependencies": { + "@types/object.fromentries": "^2.0.4", + "@types/tape": "^5.8.1", "remark": "^15.0.1", "remark-mdx": "^3.1.0", - "tape": "^5.7.0" + "tape": "^5.7.0", + "typescript": "^5.9.2" } } diff --git a/plugins/remark-snackplayer/src/index.js b/plugins/remark-snackplayer/src/index.ts similarity index 82% rename from plugins/remark-snackplayer/src/index.js rename to plugins/remark-snackplayer/src/index.ts index 8f69737c86f..9578105365b 100644 --- a/plugins/remark-snackplayer/src/index.js +++ b/plugins/remark-snackplayer/src/index.ts @@ -9,8 +9,10 @@ import visit from 'unist-util-visit-parents'; import fromEntries from 'object.fromentries'; +import type {Code} from 'mdast'; +import type {Node} from 'unist'; -const parseParams = (paramString = '') => { +function parseParams(paramString = '') { const params = fromEntries(new URLSearchParams(paramString)); if (!params.platform) { @@ -18,9 +20,9 @@ const parseParams = (paramString = '') => { } return params; -}; +} -function attr(name, value) { +function attr(name: string, value: string) { return { type: 'mdxJsxAttribute', name, @@ -28,8 +30,8 @@ function attr(name, value) { }; } -async function toJsxNode(node) { - const params = parseParams(node.meta); +async function toJsxNode(node: Code) { + const params = parseParams(node.meta ?? undefined); // Gather necessary Params const name = params.name ? decodeURIComponent(params.name) : 'Example'; @@ -80,16 +82,15 @@ async function toJsxNode(node) { }; // We "replace" the current node by a JSX node - Object.keys(node).forEach(key => delete node[key]); - Object.keys(jsxNode).forEach(key => (node[key] = jsxNode[key])); + Object.assign(node, jsxNode as Node); } export default function SnackPlayer() { - return async tree => { - const nodesToProcess = []; - visit(tree, 'code', (node, parent) => { - if (node.lang === 'SnackPlayer') { - nodesToProcess.push(toJsxNode(node, parent)); + return async (tree: Node) => { + const nodesToProcess: Promise[] = []; + visit(tree, 'code', (node: Node) => { + if ('lang' in node && node.lang === 'SnackPlayer') { + nodesToProcess.push(toJsxNode(node as Code)); } }); await Promise.all(nodesToProcess); diff --git a/plugins/remark-snackplayer/tests/index.js b/plugins/remark-snackplayer/tests/index.ts similarity index 92% rename from plugins/remark-snackplayer/tests/index.js rename to plugins/remark-snackplayer/tests/index.ts index 9db6faac712..2d6825eab1a 100644 --- a/plugins/remark-snackplayer/tests/index.js +++ b/plugins/remark-snackplayer/tests/index.ts @@ -11,9 +11,9 @@ import {remark} from 'remark'; import remarkMdx from 'remark-mdx'; import test from 'tape'; -import SnackPlayer from '../src/index.js'; +import SnackPlayer from '../src/index'; -function read(name) { +function read(name: string) { return fs.readFileSync(path.join(import.meta.dirname, name), 'utf8'); } diff --git a/plugins/remark-snackplayer/tsconfig.json b/plugins/remark-snackplayer/tsconfig.json new file mode 100644 index 00000000000..c357a2c304f --- /dev/null +++ b/plugins/remark-snackplayer/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "@react-native/typescript-config/tsconfig.json", + "include": ["./src", "./tests"], +} diff --git a/scripts/generate-llms-txt.js b/scripts/src/generate-llms-txt.ts similarity index 81% rename from scripts/generate-llms-txt.js rename to scripts/src/generate-llms-txt.ts index f98000e2da9..73e160f6a35 100644 --- a/scripts/generate-llms-txt.js +++ b/scripts/src/generate-llms-txt.ts @@ -38,10 +38,27 @@ const SLUG_TO_URL = { 'architecture-glossary': 'glossary', }; +type SidebarItem = + | string + | {type: string; id?: string; items?: SidebarItem[]; label?: string}; +type Items = SidebarItem[] | {items: SidebarItem[]}; + +type SidebarConfig = { + [section: string]: { + [category: string]: Items; + }; +}; + +type UrlData = { + url: string; + status: number | string; + error?: string | null; +}; + // Function to convert the TypeScript sidebar config to JSON -async function convertSidebarConfigToJson(fileName) { +async function convertSidebarConfigToJson(fileName: string) { const inputFileContent = fs.readFileSync( - path.join(import.meta.dirname, '../website', fileName), + path.join(import.meta.dirname, '../../website', fileName), 'utf8' ); const tempFilePath = path.join(import.meta.dirname, `temp-${fileName}.cjs`); @@ -70,8 +87,8 @@ async function convertSidebarConfigToJson(fileName) { } // Function to extract URLs from sidebar config -function extractUrlsFromSidebar(sidebarConfig, prefix) { - const urls = []; +function extractUrlsFromSidebar(sidebarConfig: SidebarConfig, prefix: string) { + const urls: string[] = []; // Process each section (docs, api, components) Object.entries(sidebarConfig).forEach(([, categories]) => { @@ -94,29 +111,24 @@ function extractUrlsFromSidebar(sidebarConfig, prefix) { } // Recursive function to process items and extract URLs -function processItemsForUrls(items, urls, prefix) { - if (typeof items === 'object' && Array.isArray(items.items)) { - processItemsForUrls(items.items, urls, prefix); - return; - } - - if (Array.isArray(items)) { - items.forEach(item => { - if (typeof item === 'string') { - urls.push(`${URL_PREFIX}${prefix}/${item}`); - } else if (typeof item === 'object') { - if (item.type === 'doc' && item.id) { - urls.push(`${URL_PREFIX}${prefix}/${item.id}`); - } else if (item.type === 'category' && Array.isArray(item.items)) { - processItemsForUrls(item.items, urls, prefix); - } +function processItemsForUrls(items: Items, urls: string[], prefix: string) { + const itemsArray = getItemsArray(items); + + itemsArray.forEach(item => { + if (typeof item === 'string') { + urls.push(`${URL_PREFIX}${prefix}/${item}`); + } else if (typeof item === 'object') { + if (item.type === 'doc' && item.id) { + urls.push(`${URL_PREFIX}${prefix}/${item.id}`); + } else if (item.type === 'category' && Array.isArray(item.items)) { + processItemsForUrls(item.items, urls, prefix); } - }); - } + } + }); } // Function to check URL status -function checkUrl(urlString) { +function checkUrl(urlString: string): Promise { return new Promise(resolve => { const parsedUrl = url.parse(urlString); @@ -130,7 +142,7 @@ function checkUrl(urlString) { const req = https.request(options, res => { resolve({ url: urlString, - status: res.statusCode, + status: res.statusCode ?? 0, }); }); @@ -155,8 +167,8 @@ function checkUrl(urlString) { } // Process each URL -async function processUrls(urls) { - const unavailableUrls = []; +async function processUrls(urls: string[]) { + const unavailableUrls: UrlData[] = []; for (const urlToCheck of urls) { const result = await checkUrl(urlToCheck); @@ -176,7 +188,7 @@ async function processUrls(urls) { } // Function to extract title from markdown frontmatter -function extractMetadataFromMarkdown(filePath) { +function extractMetadataFromMarkdown(filePath: string) { try { const content = fs.readFileSync(filePath, 'utf8'); const frontmatterMatch = content.match(/---\n([\s\S]*?)\n---/); @@ -188,7 +200,7 @@ function extractMetadataFromMarkdown(filePath) { return { title: titleMatch ? titleMatch[1].trim() - : filePath.split('/').pop().replace('.md', ''), + : filePath.split('/').pop()?.replace('.md', ''), slug: slugMatch ? slugMatch[1].trim().replace(/^\//, '') : null, }; } @@ -197,14 +209,18 @@ function extractMetadataFromMarkdown(filePath) { } // If no frontmatter found, on an error occurred use the filename return { - title: filePath.split('/').pop().replace('.md', ''), + title: filePath.split('/').pop()?.replace('.md', ''), slug: null, }; } // Function to map special cases for file names that don't match the sidebar -function mapDocPath(item, prefix) { - const specialCases = { +interface SpecialCases { + [key: string]: string; +} + +function mapDocPath(item: string | SidebarItem, prefix: string): string { + const specialCases: SpecialCases = { 'environment-setup': 'getting-started.md', 'native-platform': 'native-platforms.md', 'turbo-native-modules-introduction': 'turbo-native-modules.md', @@ -223,8 +239,21 @@ function mapDocPath(item, prefix) { return `${item}.md`; } +function getItemsArray(items: Items): SidebarItem[] { + if (Array.isArray(items)) { + return items; + } else { + return items.items; + } +} + // Function to generate output for each sidebar -function generateMarkdown(sidebarConfig, docPath, prefix, unavailableUrls) { +function generateMarkdown( + sidebarConfig: SidebarConfig, + docPath: string, + prefix: string, + unavailableUrls: UrlData[] +) { let markdown = ''; // Process each section (docs, api, components) @@ -235,12 +264,10 @@ function generateMarkdown(sidebarConfig, docPath, prefix, unavailableUrls) { Object.entries(categories).forEach(([categoryName, items]) => { markdown += `### ${categoryName === '0' ? 'General' : categoryName}\n\n`; - if (typeof items === 'object' && Array.isArray(items.items)) { - items = items.items; - } - const reorderedArray = items.every(item => typeof item === 'string') - ? items - : [...items].sort((a, b) => + const itemsArray = getItemsArray(items); + const reorderedArray = itemsArray.every(item => typeof item === 'string') + ? itemsArray + : [...itemsArray].sort((a, b) => typeof a === 'string' && typeof b !== 'string' ? -1 : typeof a !== 'string' && typeof b === 'string' @@ -268,7 +295,7 @@ function generateMarkdown(sidebarConfig, docPath, prefix, unavailableUrls) { } else if (item.type === 'category' && Array.isArray(item.items)) { // This is a category with nested items markdown += `#### ${item.label}\n\n`; - item.items.forEach(nestedItem => { + item.items.forEach((nestedItem: SidebarItem) => { if (typeof nestedItem === 'string') { const fullDocPath = `${docPath}${mapDocPath(nestedItem, prefix)}`; if (!isEntryUnavailable(unavailableUrls, fullDocPath)) { @@ -296,7 +323,7 @@ function generateMarkdown(sidebarConfig, docPath, prefix, unavailableUrls) { } async function generateOutput() { - const results = []; + const results: {markdown: string; prefix: string}[] = []; const promises = []; let output = `# ${TITLE}\n\n`; @@ -365,7 +392,7 @@ async function generateOutput() { }); } -function isEntryUnavailable(unavailableUrls, docPath) { +function isEntryUnavailable(unavailableUrls: UrlData[], docPath: string) { return !!unavailableUrls.find(entry => entry.url.endsWith(docPath.substring(1)) ); diff --git a/scripts/image-check.js b/scripts/src/image-check.ts similarity index 96% rename from scripts/image-check.js rename to scripts/src/image-check.ts index cf72d36c8e7..f2dc79878a4 100644 --- a/scripts/image-check.js +++ b/scripts/src/image-check.ts @@ -12,7 +12,7 @@ import path from 'node:path'; const imageReferenceRegExp = new RegExp(/!\[.*?\]\((.*)\)/g); async function main() { - const assets = []; + const assets: {imagePath: string; markdownPath: string}[] = []; const missingAssets = []; const queue = []; diff --git a/scripts/update-redirects.js b/scripts/src/update-redirects.ts similarity index 93% rename from scripts/update-redirects.js rename to scripts/src/update-redirects.ts index 668fa3dd80e..3b736897f21 100755 --- a/scripts/update-redirects.js +++ b/scripts/src/update-redirects.ts @@ -1,4 +1,3 @@ -#!/usr/bin/env node /** * Copyright (c) Meta Platforms, Inc. and affiliates. * @@ -48,5 +47,5 @@ Someone must have committed the _redirects after build.` fs.writeFileSync(redirects, before.replace(VERSION_KEY, latestPublicVersion)); console.log( - `Successfully added direct for /docs/${latestPublicVersion}/* -> /docs/*` + `Successfully added redirect for /docs/${latestPublicVersion}/* -> /docs/*` ); diff --git a/scripts/tsconfig.json b/scripts/tsconfig.json new file mode 100644 index 00000000000..2f8f8b925cb --- /dev/null +++ b/scripts/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "@react-native/typescript-config/tsconfig.json", + "include": ["./src"], +} diff --git a/website/docusaurus.config.ts b/website/docusaurus.config.ts index 10fc3a2b164..16296a92125 100644 --- a/website/docusaurus.config.ts +++ b/website/docusaurus.config.ts @@ -85,8 +85,8 @@ const config: Config = { url: 'https://reactnative.dev', baseUrl: '/', clientModules: [ - './modules/snackPlayerInitializer.js', - './modules/jumpToFragment.js', + './modules/snackPlayerInitializer.ts', + './modules/jumpToFragment.ts', ], trailingSlash: false, // because trailing slashes can break some existing relative links scripts: [ diff --git a/website/modules/jumpToFragment.js b/website/modules/jumpToFragment.ts similarity index 100% rename from website/modules/jumpToFragment.js rename to website/modules/jumpToFragment.ts diff --git a/website/modules/snackPlayerInitializer.js b/website/modules/snackPlayerInitializer.ts similarity index 97% rename from website/modules/snackPlayerInitializer.js rename to website/modules/snackPlayerInitializer.ts index 9570267a116..67349bfcd6e 100644 --- a/website/modules/snackPlayerInitializer.js +++ b/website/modules/snackPlayerInitializer.ts @@ -15,7 +15,7 @@ export default (() => { const updateSnacksTheme = () => { const theme = document.querySelector('html').dataset.theme; document.querySelectorAll('.snack-player').forEach(snack => { - snack.dataset.snackTheme = theme; + (snack as HTMLElement).dataset.snackTheme = theme; }); }; diff --git a/website/modules/types/expo-snack.d.ts b/website/modules/types/expo-snack.d.ts new file mode 100644 index 00000000000..2c5967031bd --- /dev/null +++ b/website/modules/types/expo-snack.d.ts @@ -0,0 +1,23 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +interface Window { + ExpoSnack?: { + /** + * Initialize all snack players on the page + */ + initialize(): void; + /** + * Remove a snack player container + */ + remove(container: Element): void; + /** + * Append/add a snack player container + */ + append(container: Element): void; + }; +} diff --git a/website/package.json b/website/package.json index 5f9bd87ff32..c731c51e43f 100644 --- a/website/package.json +++ b/website/package.json @@ -12,7 +12,7 @@ "scripts": { "docusaurus": "docusaurus", "start": "docusaurus start", - "build": "docusaurus build && node ../scripts/update-redirects.js ./build/_redirects ./versions.json && yarn run generate-llms-txt", + "build": "docusaurus build && yarn run update-redirects ./build/_redirects ./versions.json && yarn run generate-llms-txt", "build:fast": "PREVIEW_DEPLOY=true yarn run build", "tsc": "npx tsc --noEmit", "swizzle": "docusaurus swizzle", @@ -26,13 +26,14 @@ "prettier": "yarn format:style", "lint": "eslint .", "lint:examples": "eslint-examples-jsx && eslint-examples-tsx && tsc-examples", - "lint:markdown:images": "node ../scripts/image-check.js", + "lint:markdown:images": "node ../scripts/src/image-check.ts", "lint:markdown:links": "remark ../docs --quiet -r .remarkrc.withBrokenLinks.mjs", "language:lint": "cd ../ && alex && case-police 'docs/*.md' -d ./website/react-native-dict.json --disable SDK,URI", "language:lint:versioned": "cd ../ && alex . && case-police '**/*.md' -d ./website/react-native-dict.json --disable SDK,URI", "ci:lint": "yarn lint && yarn lint:examples && yarn language:lint:versioned && yarn lint:markdown:images && prettier --check src/**/*.scss", "pwa:generate": "npx pwa-asset-generator ./static/img/header_logo.svg ./static/img/pwa --padding '40px' --background 'rgb(32, 35, 42)' --icon-only --opaque true", - "generate-llms-txt": "node ../scripts/generate-llms-txt.js" + "generate-llms-txt": "node ../scripts/src/generate-llms-txt.ts", + "update-redirects": "node ../scripts/src/update-redirects.ts" }, "browserslist": { "production": [ diff --git a/yarn.lock b/yarn.lock index 93b9d757c40..4c9c3317d7c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3620,6 +3620,13 @@ __metadata: languageName: node linkType: hard +"@keyv/serialize@npm:^1.1.1": + version: 1.1.1 + resolution: "@keyv/serialize@npm:1.1.1" + checksum: 10c0/b0008cae4a54400c3abf587b8cc2474c6f528ee58969ce6cf9cb07a04006f80c73c85971d6be6544408318a2bc40108236a19a82aea0a6de95aae49533317374 + languageName: node + linkType: hard + "@leichtgewicht/ip-codec@npm:^2.0.1": version: 2.0.5 resolution: "@leichtgewicht/ip-codec@npm:2.0.5" @@ -3637,7 +3644,7 @@ __metadata: languageName: node linkType: hard -"@ljharb/through@npm:^2.3.13": +"@ljharb/through@npm:*, @ljharb/through@npm:^2.3.13": version: 2.3.14 resolution: "@ljharb/through@npm:2.3.14" dependencies: @@ -4188,9 +4195,9 @@ __metadata: react-native-safe-area-context: "npm:^5.6.1" typescript: "npm:^5.9.2" bin: - eslint-examples-jsx: ./bin/eslint-examples-jsx.js - eslint-examples-tsx: ./bin/eslint-examples-tsx.js - tsc-examples: ./bin/tsc-examples.js + eslint-examples-jsx: ./bin/eslint-examples-jsx.ts + eslint-examples-tsx: ./bin/eslint-examples-tsx.ts + tsc-examples: ./bin/tsc-examples.ts languageName: unknown linkType: soft @@ -4198,7 +4205,9 @@ __metadata: version: 0.0.0-use.local resolution: "@react-native-website/remark-codeblock-language-as-title@workspace:plugins/remark-codeblock-language-as-title" dependencies: + "@types/mdast": "npm:^4.0.4" remark: "npm:^15.0.1" + typescript: "npm:^5.9.2" unist-util-visit: "npm:^5.0.0" languageName: unknown linkType: soft @@ -4208,9 +4217,10 @@ __metadata: resolution: "@react-native-website/remark-lint-no-broken-external-links@workspace:plugins/remark-lint-no-dead-urls" dependencies: dedent: "npm:^1.5.3" - got: "npm:^13.0.0" + got: "npm:^14.6.3" jest: "npm:^29.4.3" remark: "npm:^15.0.1" + typescript: "npm:^5.9.2" unified-lint-rule: "npm:^3.0.0" unist-util-visit: "npm:^5.0.0" languageName: unknown @@ -4220,11 +4230,14 @@ __metadata: version: 0.0.0-use.local resolution: "@react-native-website/remark-snackplayer@workspace:plugins/remark-snackplayer" dependencies: + "@types/object.fromentries": "npm:^2.0.4" + "@types/tape": "npm:^5.8.1" dedent: "npm:^1.5.3" object.fromentries: "npm:^2.0.3" remark: "npm:^15.0.1" remark-mdx: "npm:^3.1.0" tape: "npm:^5.7.0" + typescript: "npm:^5.9.2" unist-util-visit-parents: "npm:^3.1.1" languageName: unknown linkType: soft @@ -4668,6 +4681,13 @@ __metadata: languageName: node linkType: hard +"@sec-ant/readable-stream@npm:^0.4.1": + version: 0.4.1 + resolution: "@sec-ant/readable-stream@npm:0.4.1" + checksum: 10c0/64e9e9cf161e848067a5bf60cdc04d18495dc28bb63a8d9f8993e4dd99b91ad34e4b563c85de17d91ffb177ec17a0664991d2e115f6543e73236a906068987af + languageName: node + linkType: hard + "@sideway/address@npm:^4.1.5": version: 4.1.5 resolution: "@sideway/address@npm:4.1.5" @@ -4712,6 +4732,13 @@ __metadata: languageName: node linkType: hard +"@sindresorhus/is@npm:^7.0.1": + version: 7.1.1 + resolution: "@sindresorhus/is@npm:7.1.1" + checksum: 10c0/96f021b8c5680e0687ceba5619c2e56fe6b089b190b3149280a1e418e6315c66839e3f1519cf1c89f7a888b5a0017a0ef1db17436d783ee398b7d5a515caa3ef + languageName: node + linkType: hard + "@sinonjs/commons@npm:^3.0.0": version: 3.0.1 resolution: "@sinonjs/commons@npm:3.0.1" @@ -5435,7 +5462,7 @@ __metadata: languageName: node linkType: hard -"@types/http-cache-semantics@npm:^4.0.2": +"@types/http-cache-semantics@npm:^4.0.2, @types/http-cache-semantics@npm:^4.0.4": version: 4.0.4 resolution: "@types/http-cache-semantics@npm:4.0.4" checksum: 10c0/51b72568b4b2863e0fe8d6ce8aad72a784b7510d72dc866215642da51d84945a9459fa89f49ec48f1e9a1752e6a78e85a4cda0ded06b1c73e727610c925f9ce6 @@ -5506,7 +5533,7 @@ __metadata: languageName: node linkType: hard -"@types/mdast@npm:^4.0.0, @types/mdast@npm:^4.0.2": +"@types/mdast@npm:^4.0.0, @types/mdast@npm:^4.0.2, @types/mdast@npm:^4.0.4": version: 4.0.4 resolution: "@types/mdast@npm:4.0.4" dependencies: @@ -5593,6 +5620,13 @@ __metadata: languageName: node linkType: hard +"@types/object.fromentries@npm:^2.0.4": + version: 2.0.4 + resolution: "@types/object.fromentries@npm:2.0.4" + checksum: 10c0/356399be5e80e75cb00b0746a04ce4ec642de8aee375aa7d445d71c3c6db0a9ea811c825f2b202eb232dd7b744f8a67fead865b325505b6186f77c0fc19aaa4e + languageName: node + linkType: hard + "@types/prismjs@npm:^1.26.0": version: 1.26.5 resolution: "@types/prismjs@npm:1.26.5" @@ -5738,6 +5772,17 @@ __metadata: languageName: node linkType: hard +"@types/tape@npm:^5.8.1": + version: 5.8.1 + resolution: "@types/tape@npm:5.8.1" + dependencies: + "@ljharb/through": "npm:*" + "@types/node": "npm:*" + mock-property: "npm:*" + checksum: 10c0/8962c5031d005d1d4c0a5a9c388298c0d6116604204ffb68080fa56d2ea4c77a652e6bddf0ddc8b789f4655ebc9a820dfdd079a27640c9455ab71d49ea433f0c + languageName: node + linkType: hard + "@types/text-table@npm:^0.2.0": version: 0.2.5 resolution: "@types/text-table@npm:0.2.5" @@ -7176,6 +7221,13 @@ __metadata: languageName: node linkType: hard +"byte-counter@npm:^0.1.0": + version: 0.1.0 + resolution: "byte-counter@npm:0.1.0" + checksum: 10c0/2e7b9cf902d06a6601f8ab893964a8b6b9e2b2dfc60fcee0d340e50b95aa3dc77c4d34ddf3e63cc374b4e5b1d0d694a942de6fbe8ee95d39418f3fdff666b6a4 + languageName: node + linkType: hard + "bytes@npm:3.0.0": version: 3.0.0 resolution: "bytes@npm:3.0.0" @@ -7232,6 +7284,21 @@ __metadata: languageName: node linkType: hard +"cacheable-request@npm:^13.0.12": + version: 13.0.14 + resolution: "cacheable-request@npm:13.0.14" + dependencies: + "@types/http-cache-semantics": "npm:^4.0.4" + get-stream: "npm:^9.0.1" + http-cache-semantics: "npm:^4.2.0" + keyv: "npm:^5.5.3" + mimic-response: "npm:^4.0.0" + normalize-url: "npm:^8.1.0" + responselike: "npm:^4.0.2" + checksum: 10c0/94ff7f7633f32495f0dc493a8957f7236ed7a1328aa52739611c3407676364048a0deb99006dae7a958cfbf5e80f5f468345ab7139031a78921a71c0b9abb845 + languageName: node + linkType: hard + "call-bind-apply-helpers@npm:^1.0.0, call-bind-apply-helpers@npm:^1.0.1, call-bind-apply-helpers@npm:^1.0.2": version: 1.0.2 resolution: "call-bind-apply-helpers@npm:1.0.2" @@ -8353,6 +8420,15 @@ __metadata: languageName: node linkType: hard +"decompress-response@npm:^10.0.0": + version: 10.0.0 + resolution: "decompress-response@npm:10.0.0" + dependencies: + mimic-response: "npm:^4.0.0" + checksum: 10c0/e8ce13b3f790fbac1e75a7be9ce4f77be62a6e5fcccfd9bd73e9d8b48b9a3b6c1b7b918ecd321095f3839b3bc9b6f6af2b1bd9c905eeddc0d1177d297b073232 + languageName: node + linkType: hard + "decompress-response@npm:^6.0.0": version: 6.0.0 resolution: "decompress-response@npm:6.0.0" @@ -10099,6 +10175,13 @@ __metadata: languageName: node linkType: hard +"form-data-encoder@npm:^4.0.2": + version: 4.1.0 + resolution: "form-data-encoder@npm:4.1.0" + checksum: 10c0/cbd655aa8ffff6f7c2733b1d8e95fa9a2fe8a88a90bde29fb54b8e02c9406e51f32a014bfe8297d67fbac9f77614d14a8b4bbc4fd0352838e67e97a881d06332 + languageName: node + linkType: hard + "format@npm:^0.2.0": version: 0.2.2 resolution: "format@npm:0.2.2" @@ -10283,6 +10366,16 @@ __metadata: languageName: node linkType: hard +"get-stream@npm:^9.0.1": + version: 9.0.1 + resolution: "get-stream@npm:9.0.1" + dependencies: + "@sec-ant/readable-stream": "npm:^0.4.1" + is-stream: "npm:^4.0.1" + checksum: 10c0/d70e73857f2eea1826ac570c3a912757dcfbe8a718a033fa0c23e12ac8e7d633195b01710e0559af574cbb5af101009b42df7b6f6b29ceec8dbdf7291931b948 + languageName: node + linkType: hard + "get-symbol-description@npm:^1.1.0": version: 1.1.0 resolution: "get-symbol-description@npm:1.1.0" @@ -10516,22 +10609,23 @@ __metadata: languageName: node linkType: hard -"got@npm:^13.0.0": - version: 13.0.0 - resolution: "got@npm:13.0.0" +"got@npm:^14.6.3": + version: 14.6.3 + resolution: "got@npm:14.6.3" dependencies: - "@sindresorhus/is": "npm:^5.2.0" - "@szmarczak/http-timer": "npm:^5.0.1" + "@sindresorhus/is": "npm:^7.0.1" + byte-counter: "npm:^0.1.0" cacheable-lookup: "npm:^7.0.0" - cacheable-request: "npm:^10.2.8" - decompress-response: "npm:^6.0.0" - form-data-encoder: "npm:^2.1.2" - get-stream: "npm:^6.0.1" - http2-wrapper: "npm:^2.1.10" + cacheable-request: "npm:^13.0.12" + decompress-response: "npm:^10.0.0" + form-data-encoder: "npm:^4.0.2" + http2-wrapper: "npm:^2.2.1" + keyv: "npm:^5.5.3" lowercase-keys: "npm:^3.0.0" - p-cancelable: "npm:^3.0.0" - responselike: "npm:^3.0.0" - checksum: 10c0/d6a4648dc46f1f9df2637b8730d4e664349a93cb6df62c66dfbb48f7887ba79742a1cc90739a4eb1c15f790ca838ff641c5cdecdc877993627274aeb0f02b92d + p-cancelable: "npm:^4.0.1" + responselike: "npm:^4.0.2" + type-fest: "npm:^4.26.1" + checksum: 10c0/b5a2a0a8bfd44399f804c0875d0cfe832c32dd0067a25a965c0800ea58742a42135272dc1b30da477d70f1f6fd875c2bf38f0946be0f9d44ec8864f9a889f613 languageName: node linkType: hard @@ -11106,10 +11200,10 @@ __metadata: languageName: node linkType: hard -"http-cache-semantics@npm:^4.1.1": - version: 4.1.1 - resolution: "http-cache-semantics@npm:4.1.1" - checksum: 10c0/ce1319b8a382eb3cbb4a37c19f6bfe14e5bb5be3d09079e885e8c513ab2d3cd9214902f8a31c9dc4e37022633ceabfc2d697405deeaf1b8f3552bb4ed996fdfc +"http-cache-semantics@npm:^4.1.1, http-cache-semantics@npm:^4.2.0": + version: 4.2.0 + resolution: "http-cache-semantics@npm:4.2.0" + checksum: 10c0/45b66a945cf13ec2d1f29432277201313babf4a01d9e52f44b31ca923434083afeca03f18417f599c9ab3d0e7b618ceb21257542338b57c54b710463b4a53e37 languageName: node linkType: hard @@ -11191,7 +11285,7 @@ __metadata: languageName: node linkType: hard -"http2-wrapper@npm:^2.1.10": +"http2-wrapper@npm:^2.1.10, http2-wrapper@npm:^2.2.1": version: 2.2.1 resolution: "http2-wrapper@npm:2.2.1" dependencies: @@ -11897,6 +11991,13 @@ __metadata: languageName: node linkType: hard +"is-stream@npm:^4.0.1": + version: 4.0.1 + resolution: "is-stream@npm:4.0.1" + checksum: 10c0/2706c7f19b851327ba374687bc4a3940805e14ca496dc672b9629e744d143b1ad9c6f1b162dece81c7bfbc0f83b32b61ccc19ad2e05aad2dd7af347408f60c7f + languageName: node + linkType: hard + "is-string@npm:^1.0.7, is-string@npm:^1.1.0, is-string@npm:^1.1.1": version: 1.1.1 resolution: "is-string@npm:1.1.1" @@ -12789,6 +12890,15 @@ __metadata: languageName: node linkType: hard +"keyv@npm:^5.5.3": + version: 5.5.3 + resolution: "keyv@npm:5.5.3" + dependencies: + "@keyv/serialize": "npm:^1.1.1" + checksum: 10c0/6890ed8a76e6b16034ceda89a4a7dc9cd1ebd05bf0ee1f7f3d3fe37ac3e4a6196d710ab2fef3d47cf8c394b61104b3bfcab17f23cc6e0dc2dcbe36483a43f84d + languageName: node + linkType: hard + "kind-of@npm:^6.0.0, kind-of@npm:^6.0.2, kind-of@npm:^6.0.3": version: 6.0.3 resolution: "kind-of@npm:6.0.3" @@ -15287,7 +15397,7 @@ __metadata: languageName: node linkType: hard -"mock-property@npm:^1.1.0": +"mock-property@npm:*, mock-property@npm:^1.1.0": version: 1.1.0 resolution: "mock-property@npm:1.1.0" dependencies: @@ -15579,10 +15689,10 @@ __metadata: languageName: node linkType: hard -"normalize-url@npm:^8.0.0": - version: 8.0.1 - resolution: "normalize-url@npm:8.0.1" - checksum: 10c0/eb439231c4b84430f187530e6fdac605c5048ef4ec556447a10c00a91fc69b52d8d8298d9d608e68d3e0f7dc2d812d3455edf425e0f215993667c3183bcab1ef +"normalize-url@npm:^8.0.0, normalize-url@npm:^8.1.0": + version: 8.1.0 + resolution: "normalize-url@npm:8.1.0" + checksum: 10c0/e9b68db5f0264ce74fc083e2120b4a40fb3248e5dceec5f795bddcee0311b3613f858c9a65f258614fac2776b8e9957023bea8fe7299db1496b3cd1c75976cfe languageName: node linkType: hard @@ -15891,6 +16001,13 @@ __metadata: languageName: node linkType: hard +"p-cancelable@npm:^4.0.1": + version: 4.0.1 + resolution: "p-cancelable@npm:4.0.1" + checksum: 10c0/12636623f46784ba962b6fe7a1f34d021f1d9a2cc12c43e270baa715ea872d5c8c7d9f086ed420b8b9817e91d9bbe92c14c90e5dddd4a9968c81a2a7aef7089d + languageName: node + linkType: hard + "p-finally@npm:^1.0.0": version: 1.0.0 resolution: "p-finally@npm:1.0.0" @@ -18479,6 +18596,15 @@ __metadata: languageName: node linkType: hard +"responselike@npm:^4.0.2": + version: 4.0.2 + resolution: "responselike@npm:4.0.2" + dependencies: + lowercase-keys: "npm:^3.0.0" + checksum: 10c0/8366407fc7f12466dd52682483a31dd6ca892481365caadea9a380196d8a6238650e064531087bebd25d7e9393f491efc2dad723fadc54db7a2b442dba8ef588 + languageName: node + linkType: hard + "retext-english@npm:^4.0.0": version: 4.1.0 resolution: "retext-english@npm:4.1.0" @@ -20191,6 +20317,13 @@ __metadata: languageName: node linkType: hard +"type-fest@npm:^4.26.1": + version: 4.41.0 + resolution: "type-fest@npm:4.41.0" + checksum: 10c0/f5ca697797ed5e88d33ac8f1fec21921839871f808dc59345c9cf67345bfb958ce41bd821165dbf3ae591cedec2bf6fe8882098dfdd8dc54320b859711a2c1e4 + languageName: node + linkType: hard + "type-is@npm:~1.6.18": version: 1.6.18 resolution: "type-is@npm:1.6.18"