Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ out/
tsconfig.tsbuildinfo

# llms.txt
public/llms.txt
public/llms.txt
public/llms-full.txt
126 changes: 35 additions & 91 deletions scripts/generate-llms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,100 +7,35 @@ import { getNewArchSupportStatus, NewArchSupportStatus } from '~/util/newArchSta

import data from '../assets/data.json';

const OUTPUT_PATH = path.resolve('public', 'llms.txt');
const LLMS_PATH = path.resolve('public', 'llms.txt');
const LLMS_FULL_PATH = path.resolve('public', 'llms-full.txt');

const API_DOCUMENTATION = await fs.readFile('API.md', 'utf8');
const README_MARKDOWN = await fs.readFile('README.md', 'utf8');

const INTRODUCTION = `# reactnative.directory

React Native Directory is a website where you can browse through all the libraries that are compatible with React Native.

## Library fields description

### βš™οΈ General

- #### ❗ \`githubUrl\` **(required)**

**(string)** - URL to the package GitHub repository (currently other Git hosts are not supported).

> [!WARNING]
> Package also needs to be published to the NPM registry, because it is a source of crucial data for the directory.

- #### \`npmPkg\`

**(string)** - npm package name, by default GitHub repository name will be used. Example: \`"@expo/react-native-action-sheet"\`.

> [!TIP]
> Fill \`npmPkg\` only when the GitHub repository name is different from the name of package published to npm, or the package is a part of monorepo.

- #### \`examples\`
**(array of strings)** - URLs to example projects or Snacks which demonstrates the library.
- #### \`images\`

**(array of strings)** - URLs to static images or GIFs that shows the library functionality.

> [!TIP]
> Please do not add logotypes or other branding materials to the \`images\` array, and please avoid linking multiple assets which shows the same feature.

### πŸ“± Platforms
React Native Directory is a website where you can browse through all the libraries that are compatible with React Native.`;

- #### \`android\`
**(boolean)** - works on Android device.
- #### \`ios\`
**(boolean)** - works on iOS device.
- #### \`web\`
**(boolean)** - can be used with [\`react-native-web\`](https://github.com/necolas/react-native-web).

### πŸ–₯️ Out-of-tree Platforms

> [!IMPORTANT]
> Adding out-of-tree platforms support requires an example or link to the app which uses the library on the given platform.

- #### \`windows\`
**(boolean)** - can be used with [\`react-native-windows\`](https://github.com/microsoft/react-native-windows).
- #### \`macos\`
**(boolean)** - can be used with [\`react-native-macos\`](https://github.com/microsoft/react-native-macos).
- #### \`tvos\`
**(boolean)** - can be used with [\`react-native-tvos\`](https://github.com/react-native-tvos/react-native-tvos).
- #### \`visionos\`
**(boolean)** - can be used with [\`react-native-visionos\`](https://github.com/callstack/react-native-visionos).

### βœ… Compatibility

> [!TIP]
> **Any** library can be used with Expo, if you use dev clients or prebuild.

- #### \`expoGo\`
**(boolean)** - works with [Expo Go](https://docs.expo.dev/get-started/expo-go/) β€” an open-source sandbox app, without using [dev clients](https://docs.expo.dev/develop/development-builds/introduction/) or [prebuild](https://docs.expo.dev/workflow/continuous-native-generation/).
- #### \`fireos\`
**(boolean)** - works on Amazon Fire OS.
- #### \`horizon\`
**(boolean)** - works on Meta Horizon OS.
- #### \`vegaos\`
**(boolean|string)** - works with [Vega OS](https://developer.amazon.com/apps-and-games/vega). It can also be a string containing npm package name, if a separate/additional package is required for full support.

### 🏷️ Tags
function normalizeNewlines(text: string) {
return text.replace(/\r\n/g, '\n');
}

- #### \`unmaintained\`
**(boolean)** - signify that a library is no longer maintained. You can provide alternative or replacement libraries with the \`alternatives\` field, if needed.
- #### \`dev\`
**(boolean)** - signify that a library is a development tool or is only a part of development process.
- #### \`template\`
**(boolean)** - signify that a library is a new project template.
- #### \`configPlugin\`
**(boolean \\| string \\[URL to third-party config plugin\\])** - Indicates if the library includes an [Expo config plugin](https://docs.expo.dev/config-plugins/introduction/). If the plugin is provided by a third party, supply the URL as a string. This field is optional and will be detected automatically if omitted.
- #### \`newArchitecture\`
**(boolean|'new-arch-only')** - signify that a library supports both, or not, the New Architecture and the Old Architecture or only the New Architecture. Skipping the field will result in "untested" status, unless automatic support detection returned a result. You can provide additional context with the \`newArchitectureNote\` field, if needed.
function extractReadmeSection(heading: string) {
const md = normalizeNewlines(README_MARKDOWN);

> [!TIP]
> Set \`newArchitecture\` field only when automatic architecture detection fails for your package, despite it supports the New Architecture.
const headingToFind = `## ${heading}`;
const startIndex = md.indexOf(headingToFind);

### πŸ“ Additional context for tags
if (startIndex === -1) {
throw new Error(`Could not find section heading in README.md: ${headingToFind}`);
}

- #### \`newArchitectureNote\`
**(string)** - provide a note for the New Architecture support status, if a boolean \`"true"\` or \`"false"\` is not sufficient to describe the state of New Architecture support.
const fromStart = md.slice(startIndex);
const nextSectionIndex = fromStart.search(/\n##\s+/);

- #### \`alternatives\`
**(array of strings)** - provide a list of alternatives to the library. eg: \`["expo-camera", "react-native-vision-camera"]\`. This is used to provide a list of alternatives to a library if it is unmaintained or does not support the New Architecture.`;
return (nextSectionIndex === -1 ? fromStart : fromStart.slice(0, nextSectionIndex)).trim();
}

const NEW_ARCHITECTURE_STATUS_LABELS: Record<NewArchSupportStatus, string> = {
[NewArchSupportStatus.NewArchOnly]: 'Only Supports New Architecture',
Expand All @@ -121,7 +56,8 @@ function formatRecord(library: LibraryType): string {
const { npmPkg, npm, github, githubUrl } = library;

return [
`[${npmPkg}](${githubUrl}): ${github.description ?? 'No description'}`,
`## [${npmPkg}](${githubUrl})\n`,
`${github.description ?? 'No description'}\n`,
`Platforms: ${platforms.join(', ')}`,
compatibility.length > 0 ? `Compatibility: ${compatibility.join(', ')}` : undefined,
`New Architecture: ${newArch}`,
Expand Down Expand Up @@ -185,25 +121,33 @@ function formatDownloads({ npm }: LibraryType) {
return parts.join(', ');
}

async function generateLlmsFile() {
async function generateLLMSFiles() {
const librariesFields = extractReadmeSection('How do I add a library?');
const llmsContent = [INTRODUCTION, librariesFields, API_DOCUMENTATION].join('\n\n');

await fs.writeFile(LLMS_PATH, `${llmsContent}\n`, 'utf8');

console.log(`βœ… Generated ${path.relative(process.cwd(), LLMS_PATH)}`);

const { libraries } = data as DataAssetType;

const entries = libraries
.filter(library => !library.template)
.map(library => formatRecord(library));

const content = [
const llmsFullContent = [
INTRODUCTION,
librariesFields,
API_DOCUMENTATION,
'# List of available libraries',
entries.join('\n\n---\n\n'),
entries.join('\n\n'),
].join('\n\n');

await fs.writeFile(OUTPUT_PATH, `${content}\n`, 'utf8');
await fs.writeFile(LLMS_FULL_PATH, `${llmsFullContent}\n`, 'utf8');

console.log(
`βœ… Generated ${entries.length} entries in ${path.relative(process.cwd(), OUTPUT_PATH)}`
`βœ… Generated ${path.relative(process.cwd(), LLMS_PATH)} with ${entries.length} libraries data`
);
}

await generateLlmsFile();
await generateLLMSFiles();