这是indexloc提供的服务,不要输入任何密码
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: 1 addition & 2 deletions docs/link-checker/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@
"src"
],
"scripts": {
"check-links": "cd ../site/content && tsx ../../link-checker/src/validate-docs-links.ts"
"check-links": "cd ../site/content && node --experimental-strip-types ../../link-checker/src/validate-docs-links.ts"
},
"devDependencies": {
"@types/node": "22.7.8",
"tsx": "4.19.1",
"typescript": "5.5.4"
},
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion docs/link-checker/src/validate-docs-links.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { collectLinkErrors } from "./markdown";
import { collectLinkErrors } from "./markdown.ts";

/*
This script validates internal links in /docs and /errors including internal,
Expand Down
4 changes: 3 additions & 1 deletion docs/link-checker/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
"strict": true,
"noImplicitAny": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true
"esModuleInterop": true,
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true
},
"include": ["src", "types.d.ts"]
}
14 changes: 7 additions & 7 deletions docs/site/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@
"lint": "fumadocs-mdx && next lint",
"lint:fix": "fumadocs-mdx && next lint --fix",
"lint:prettier": "prettier --write -c . --cache --ignore-path=../../.prettierignore",
"index-docs": "tsx ./scripts/sync-algolia.mjs",
"rss": "node scripts/generate-rss.cjs",
"schema": "node scripts/copy-json-schema.mjs",
"index-docs": "node --experimental-strip-types ./scripts/sync-algolia.ts",
"rss": "node --experimental-strip-types scripts/generate-rss.ts",
"schema": "node --experimental-strip-types scripts/copy-json-schema.ts",
"start": "next start",
"check-types": "fumadocs-mdx && tsc --noEmit",
"generate-openapi": "node ./scripts/generate-docs.mjs",
"write-private-files": "node ./scripts/write-private-files.mjs",
"collect-examples-data": "node ./scripts/collect-examples-data.mjs"
"generate-openapi": "node --experimental-strip-types ./scripts/generate-docs.ts",
"write-private-files": "node --experimental-strip-types ./scripts/write-private-files.ts",
"collect-examples-data": "node --experimental-strip-types ./scripts/collect-examples-data.ts"
},
"dependencies": {
"@heroicons/react": "1.0.6",
Expand Down Expand Up @@ -71,6 +71,7 @@
"@types/node": "20.11.30",
"@types/react": "18.3.1",
"@types/react-dom": "18.3.0",
"@types/rss": "^0.0.32",
"@types/semver": "^7.3.13",
"@vercel/analytics": "1.5.0",
"@vercel/toolbar": "0.1.30",
Expand All @@ -81,7 +82,6 @@
"rss": "^1.2.2",
"spellchecker-cli": "^6.2.0",
"tailwindcss": "^3.4.1",
"tsx": "^4.6.2",
"typescript": "5.6.3"
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
// @ts-check
// @ts-nocheck
// This script exports examples data to a JSON file
// Use Node.js ESM syntax
// @ts-ignore

import fs from "node:fs";
import path from "node:path";
import { z } from "zod";
Expand Down Expand Up @@ -32,7 +26,7 @@ const examples = fs
dirent.name !== "node_modules"
)
.filter((dirent) => dirent.name !== "with-nextjs")
// @ts-expect-error
// @ts-expect-error -- TODO
.sort((a, b) => a.name - b.name)
.map((dirent) => dirent.name);

Expand All @@ -43,11 +37,13 @@ for (const example of examples) {
if (fs.existsSync(metaPath)) {
try {
const metaContent = fs.readFileSync(metaPath, "utf8");
const metaJson = JSON.parse(metaContent);
const metaJson = JSON.parse(metaContent) as z.infer<
typeof ExampleMetaSchema
>;
EXAMPLES.push({ ...metaJson, slug: example });
} catch (error) {
// @ts-expect-error
throw new Error(error);
// Ensure error is converted to string when creating new Error
throw new Error(error instanceof Error ? error.message : String(error));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,58 @@ import { generateFiles } from "fumadocs-openapi";

const out = "./content/openapi";

interface OpenAPISpec {
paths?: Record<
string,
Record<
string,
{
responses?: Record<
string,
{
description?: string;
headers?: Record<
string,
{
schema: {
type: string;
};
description: string;
}
>;
}
>;
}
>
>;
servers?: Array<{
url: string;
description?: string;
}>;
}

// Define a more specific type for the OpenAPI value structure
type OpenAPIValue =
| string
| number
| boolean
| null
| { [key: string]: OpenAPIValue }
| Array<OpenAPIValue>;

/* The Vercel Remote Cache spec has examples that show Vercel values.
* Removing them makes the self-hosted spec easier to use. */
const removeExamples = (obj) => {
const removeExamples = (obj: OpenAPIValue): OpenAPIValue => {
if (!obj || typeof obj !== "object") return obj;

if (Array.isArray(obj)) {
return obj.map((item) => removeExamples(item));
}

const result = {};
for (const [key, value] of Object.entries(obj)) {
const result: Record<string, OpenAPIValue> = {};
for (const [key, value] of Object.entries(
obj as Record<string, OpenAPIValue>
)) {
if (key !== "example") {
result[key] = removeExamples(value);
}
Expand All @@ -39,7 +80,7 @@ const removeExamples = (obj) => {

/* The Vercel Remote Cache spec has responses related to billing.
* Self-hosted users don't need these. */
function removeBillingRelated403Responses(spec) {
function removeBillingRelated403Responses(spec: OpenAPISpec): OpenAPISpec {
// Define billing-related phrases to filter out
const billingPhrases = [
"The customer has reached their spend cap limit and has been paused",
Expand All @@ -56,11 +97,11 @@ function removeBillingRelated403Responses(spec) {
const methodObj = pathObj[method];

// Check if the method has responses
if (methodObj?.responses["403"]) {
if (methodObj.responses?.["403"]) {
const description = methodObj.responses["403"].description;

// Split the description by newlines
const descriptionLines = description.split("\n");
const descriptionLines = description?.split("\n") ?? [];

// Filter out billing-related lines
const filteredLines = descriptionLines.filter((line) => {
Expand All @@ -83,15 +124,15 @@ function removeBillingRelated403Responses(spec) {
}

/* Add x-artifact-tag header to artifact download endpoint response */
function addArtifactTagHeader(spec) {
function addArtifactTagHeader(spec: OpenAPISpec): OpenAPISpec {
// Target only the specific /v8/artifacts/{hash} endpoint
const artifactEndpoint = "/v8/artifacts/{hash}";

if (spec.paths?.[artifactEndpoint]) {
// Get the GET method for this endpoint
const getMethod = spec.paths[artifactEndpoint]?.get;
const getMethod = spec.paths[artifactEndpoint].get;

if (getMethod?.responses?.["200"]) {
if (getMethod.responses?.["200"]) {
const response = getMethod.responses["200"];

// Add headers to the response if they don't exist
Expand All @@ -112,7 +153,7 @@ function addArtifactTagHeader(spec) {
return spec;
}

const updateServerDescription = (spec) => {
const updateServerDescription = (spec: OpenAPISpec): OpenAPISpec => {
if (spec.servers && spec.servers.length > 0) {
const serverIndex = spec.servers.findIndex(
(server) => server.url === "https://api.vercel.com"
Expand All @@ -124,11 +165,12 @@ const updateServerDescription = (spec) => {
}
return spec;
}
return spec;
};

const thing = await fetch("https://turborepo.com/api/remote-cache-spec")
.then((res) => res.json())
.then((json) => removeExamples(json))
.then((json: unknown) => removeExamples(json as OpenAPIValue) as OpenAPISpec)
.then((json) => removeBillingRelated403Responses(json))
.then((json) => addArtifactTagHeader(json))
.then((json) => updateServerDescription(json));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,30 @@
const { promises: fs, statSync } = require("node:fs");
const path = require("node:path");
const RSS = require("rss");
const matter = require("gray-matter");
import { promises as fs, statSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import RSS from "rss";
import matter from "gray-matter";

function dateSortDesc(a, b) {
interface FrontMatter {
data: {
date: string;
title: string;
description: string;
ogImage: string;
href?: string;
};
content: string;
slug?: string;
}

function dateSortDesc(a: FrontMatter, b: FrontMatter): number {
const date1 = new Date(a.data.date);
const date2 = new Date(b.data.date);
if (date1 > date2) return -1;
if (date1 < date2) return 1;
return 0;
}

async function generate() {
async function generate(): Promise<void> {
const feed = new RSS({
title: "Turborepo Blog",
description: "Turborepo news, updates, and announcements.",
Expand All @@ -20,29 +33,36 @@ async function generate() {
image_url: "https://turborepo.com/api/og",
});

const posts = await fs.readdir(path.join(__dirname, "..", "content", "blog"));
const currentDir = path.dirname(fileURLToPath(import.meta.url));

const promises = posts.map(async (post) => {
const posts = await fs.readdir(
path.join(currentDir, "..", "content", "blog")
);

const promises = posts.map(async (post: string) => {
if (post.startsWith("index.") || post.startsWith("_meta.json")) return;
const file = await fs.readFile(
path.join(__dirname, "..", "content", "blog", post)
path.join(currentDir, "..", "content", "blog", post)
);
const frontmatter = matter(file);
if (frontmatter.data.href) return;
return { ...frontmatter, slug: post.replace(".mdx", "") };
const { data, content } = matter(file);
if (data.href) return;
return { data, content, slug: post.replace(".mdx", "") } as FrontMatter;
});

const results = await Promise.all(promises);
const sortedData = results.filter(Boolean); // Remove null values
const sortedData = results.filter(
(item): item is FrontMatter & { slug: string } => Boolean(item)
);

// sort by date
sortedData.sort(dateSortDesc);

for (const frontmatter of sortedData) {
// get the og image size
const stat = statSync(
path.join(__dirname, "..", "public", frontmatter.data.ogImage)
path.join(currentDir, "..", "public", frontmatter.data.ogImage)
);

feed.item({
title: frontmatter.data.title,
url: `https://turborepo.com/blog/${frontmatter.slug}`, // intentionally including slash here
Expand All @@ -59,4 +79,4 @@ async function generate() {
await fs.writeFile("./public/feed.xml", feed.xml({ indent: true }));
}

generate();
void generate();
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as fs from "node:fs";
import algosearch from "algoliasearch";
import env from "@next/env";
import { sync } from "fumadocs-core/search/algolia";
import { sync, type DocumentRecord } from "fumadocs-core/search/algolia";

// We assume you're working in development if this is not provided.
if (!process.env.NEXT_PUBLIC_ALGOLIA_INDEX) {
Expand All @@ -19,12 +19,17 @@ const ALGOLIA_INDEX_NAME = process.env.NEXT_PUBLIC_ALGOLIA_INDEX ?? "_docs_dev";

const content = fs.readFileSync(".next/server/app/static.json.body");

/** @type {import('fumadocs-core/search/algolia').DocumentRecord[]} **/
const indexes = JSON.parse(content.toString()).filter(
const indexes = (
JSON.parse(content.toString()) as Array<DocumentRecord>
).filter(
// These path don't have information that we think people want in search.
(doc) => !["docs/community", "/docs"].includes(doc.url)
);

if (!process.env.ALGOLIA_APP_ID) {
throw new Error("No ALGOLIA_APP_ID found.");
}

const algoliaClient = algosearch(
process.env.ALGOLIA_APP_ID,
process.env.ALGOLIA_API_KEY
Expand Down Expand Up @@ -56,7 +61,7 @@ void sync(algoliaClient, {
.then(() => {
console.log(`Search index updated for ${ALGOLIA_INDEX_NAME}.`);
})
.catch((err) => {
.catch((err: unknown) => {
console.error(err);
throw new Error(err);
throw err instanceof Error ? err : new Error(String(err));
});
Loading
Loading