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
4 changes: 4 additions & 0 deletions .github/actions/setup-docs/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ inputs:
runs:
using: "composite"
steps:
- name: Extract code snippets
shell: bash
run: node docs/scripts/extract-include-statements.js

- name: Checkout Alchemy Docs Repo
uses: actions/checkout@v4
with:
Expand Down
6 changes: 5 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,16 @@ To add new images:

### SDK References

SDK Refernces are automatically generated from relevant projects within the monorepo via the `docs-gen` package. In the root, run:
SDK References are automatically generated from relevant projects within the monorepo via the `docs-gen` package. In the root, to generate references from code you can run:

```shell
yarn fern-gen
```

### Injected Code Snippets

You can reference production code directly in code snippets using `[!include]` statements. The syntax is the same as Physical File Snippets from Vocs, so you can reference [their documentation](https://vocs.dev/docs/guides/code-snippets#physical-file-snippets).

### Local Development

**TBD:** Currently this would require distributing a GitHub Token to access Alchemy Docs repo. Will remove this requirement once those docs go live.
Expand Down
71 changes: 71 additions & 0 deletions docs/scripts/extract-include-statements.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// @ts-check
// This script is executed during a GitHub workflow that otherwise doesn't need to install dependencies
// So not using glob or Typescript is purely to optimize CD runtime (plus all types are inferred)
import fs from "fs";
import path from "path";

/**
* Recursively find all files matching a pattern in a directory
*
* @param {string} dir - Directory to search
* @param {string} pattern - File pattern to match (e.g., "*.mdx")
* @returns {string[]} - Array of matching file paths
*/
const findFiles = (dir, pattern) => {
let results = [];
const files = fs.readdirSync(dir);

for (const file of files) {
const filePath = path.join(dir, file);
const stat = fs.statSync(filePath);

if (stat.isDirectory()) {
results = results.concat(findFiles(filePath, pattern));
} else if (file.endsWith(pattern.replace("*", ""))) {
results.push(filePath);
}
}

return results;
};

const mdxFiles = findFiles("docs", "*.mdx");

for (const mdxFile of mdxFiles) {
let content = fs.readFileSync(mdxFile, "utf8");

const snippetRegex = /\s*\/\/\s*\[!include\s+([^\]]+)\]\s*/g;

const matches = [...content.matchAll(snippetRegex)];
if (matches.length === 0) {
continue;
}

content = content.replace(snippetRegex, (match, includePath) => {
const [filePath, region] = includePath.split(":");

const fullPath = path.resolve(process.cwd(), filePath.trim());

let fileContent = fs.readFileSync(fullPath, "utf8");

if (region) {
const regionStart = fileContent.indexOf(`// [!region ${region.trim()}]`);
const regionEnd = fileContent.indexOf(`// [!endregion ${region.trim()}]`);

if (regionStart !== -1 && regionEnd !== -1) {
fileContent = fileContent
.substring(
regionStart + `// [!region ${region.trim()}]`.length,
regionEnd
)
.trim();
} else {
throw new Error(`Region "${region.trim()}" not found in ${fullPath}`);
}
}

return `\n${fileContent}\n`;
});

fs.writeFileSync(mdxFile, content);
}
Loading