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
21 changes: 19 additions & 2 deletions scripts/update-readme.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
import fs from "fs";
import * as NodeContext from "@effect/platform-node/NodeContext";
import * as NodeRuntime from "@effect/platform-node/NodeRuntime";
import * as FileSystem from "@effect/platform/FileSystem";
import * as Effect from "effect/Effect";

fs.copyFileSync("README.md", "packages/plugins/eslint-plugin/README.md");
const program = Effect.gen(function*() {
const fs = yield* FileSystem.FileSystem;
const source = "README.md";
const destination = "packages/plugins/eslint-plugin/README.md";

// Ensure the destination directory exists
yield* fs.makeDirectory("packages/plugins/eslint-plugin", { recursive: true });

// Copy the README file
yield* fs.copyFile(source, destination);

yield* Effect.log(`Copied ${source} to ${destination}`);
});

program.pipe(Effect.provide(NodeContext.layer), NodeRuntime.runMain);
4 changes: 2 additions & 2 deletions scripts/update-version.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem";
import * as NodeContext from "@effect/platform-node/NodeContext";
import * as NodeRuntime from "@effect/platform-node/NodeRuntime";
import * as FileSystem from "@effect/platform/FileSystem";
import ansis from "ansis";
Expand Down Expand Up @@ -47,4 +47,4 @@ const program = Effect.gen(function*() {
return yield* Effect.all(packageJsonFiles.map(update), { concurrency: 8 });
});

program.pipe(Effect.provide(NodeFileSystem.layer), NodeRuntime.runMain);
program.pipe(Effect.provide(NodeContext.layer), NodeRuntime.runMain);
156 changes: 102 additions & 54 deletions scripts/update-website.ts
Original file line number Diff line number Diff line change
@@ -1,70 +1,118 @@
import fs from "node:fs";
import path from "node:path";
import * as NodeContext from "@effect/platform-node/NodeContext";
import * as NodeRuntime from "@effect/platform-node/NodeRuntime";
import * as FileSystem from "@effect/platform/FileSystem";
import * as Path from "@effect/platform/Path";
import ansis from "ansis";
import * as Effect from "effect/Effect";

import { glob } from "./utils/glob";

/**
* Build script for processing and copying documentation to the website
*
* This script:
* This script (Effect version):
* 1. Collects rule documentation from ESLint Plugins
* 2. Copies them to the website with proper naming
* 3. Processes the changelog
* 4. Sets up dependencies for the website build
* 4. (TODO) Generates meta.json / rules data
*/

// Find all rule documentation markdown files from the various plugins
const docs = glob(["packages/plugins/eslint-plugin-react-*/src/rules/*.md"]);

// TODO: Generate the meta.json file as well
// Process each documentation file:
// - Extract plugin name and rule name
// - Format destination path and rule title
// - Build arrays of file paths and rule metadata
const [
files,
// rules, // Currently commented out but would contain rule metadata
] = Array.from(docs).reduce<readonly [[string, string][], [string, string][]]>(
([files, rules], doc) => {
const DOCS_GLOB = ["packages/plugins/eslint-plugin-react-*/src/rules/*.md"];

interface RuleMeta {
name: string;
title: string;
destination: string;
source: string;
}

const collectDocs = Effect.gen(function*() {
const path = yield* Path.Path;
const docs = yield* Effect.sync(() => glob(DOCS_GLOB));
return docs.map<RuleMeta>((doc) => {
const catename = /^packages\/plugins\/eslint-plugin-react-([^/]+)/u.exec(doc)?.[1] ?? "";
const basename = path.parse(path.basename(doc)).name;

// Special handling for "react-x" plugin (the core plugin)
const isPluginX = catename === "x";

// Format the rule name differently based on which plugin it belongs to
const name = isPluginX
? basename // For react-x plugin: just use the rule name
: `${catename}-${basename}`; // For other plugins: prefix with category

// Format the rule title for display purposes
const title = isPluginX
? basename // For react-x plugin: just use the rule name
: `${catename}/${basename}`; // For other plugins: use category/rule format

// Define destination path in the website content directory
const dest = path.join("apps", "website", "content", "docs", "rules", `${name}.mdx`);

// Add to our accumulator arrays
return [[...files, [doc, dest]], [...rules, [name, title]]] as const;
},
[[], []],
);

// Copy all documentation files to their respective destinations in parallel
files.map(([src, dest]) => fs.copyFileSync(src, dest));

// Write rule metadata to a JSON file for the website
// fs.writeFileSync(path.join("apps", "website", "content", "docs", "rules", "data.json"), JSON.stringify(rules, null, 2));

// Process the changelog file by adding frontmatter for the documentation system
const changelog = [
"---",
"title: Changelog",
"---",
"",
fs.readFileSync("CHANGELOG.md", "utf-8"),
].join("\n");

// Write the processed changelog to the website content directory
fs.writeFileSync(path.join("apps", "website", "content", "docs", "changelog.md"), changelog);
const name = isPluginX ? basename : `${catename}-${basename}`;
const title = isPluginX ? basename : `${catename}/${basename}`;

const destination = path.join("apps", "website", "content", "docs", "rules", `${name}.mdx`);

return {
name,
title,
destination,
source: doc,
};
});
});

function copyRuleDoc(meta: RuleMeta) {
return Effect.gen(function*() {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const dir = path.dirname(meta.destination);
// Ensure destination directory exists
yield* fs.makeDirectory(dir, { recursive: true });
const content = yield* fs.readFileString(meta.source, "utf8");
yield* fs.writeFileString(meta.destination, content);
yield* Effect.log(ansis.green(`Copied ${meta.source} -> ${meta.destination}`));
return meta;
});
}

const processChangelog = Effect.gen(function*() {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const changelogPath = "CHANGELOG.md";
const targetPath = path.join("apps", "website", "content", "docs", "changelog.md");

const source = yield* fs.readFileString(changelogPath, "utf8");
const wrapped = [
"---",
"title: Changelog",
"---",
"",
source,
].join("\n");

const dir = path.dirname(targetPath);
yield* fs.makeDirectory(dir, { recursive: true });
yield* fs.writeFileString(targetPath, wrapped);
yield* Effect.log(ansis.cyan(`Processed changelog -> ${targetPath}`));
});

const program = Effect.gen(function*() {
yield* Effect.log(ansis.bold("Processing rule documentation..."));

const metas = yield* collectDocs;

yield* Effect.log(
metas.length === 0
? ansis.yellow("No documentation files found.")
: `Found ${ansis.bold(metas.length.toString())} rule documentation file(s).`,
);

// Copy in parallel with limited concurrency (adjust if needed)
yield* Effect.forEach(metas, copyRuleDoc, { concurrency: 8 });

// (Optional) Generate rules metadata JSON (still TODO)
// const rulesData = metas.map(({ name, title }) => ({ name, title }));
// const rulesDataPath = path.join("apps", "website", "content", "docs", "rules", "data.json");
// yield* FileSystem.FileSystem.flatMap(fs =>
// fs.makeDirectory(path.dirname(rulesDataPath), { recursive: true }).pipe(
// Effect.zipRight(
// fs.writeFileString(rulesDataPath, JSON.stringify(rulesData, null, 2) + "\n")
// ),
// Effect.tap(() => Effect.log(ansis.magenta(`Generated ${rulesDataPath}`)))
// )
// );

yield* processChangelog;

yield* Effect.log(ansis.bold.green("Documentation processing completed."));
});

program.pipe(Effect.provide(NodeContext.layer), NodeRuntime.runMain);
15 changes: 5 additions & 10 deletions scripts/verify-lockfile.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Command, CommandExecutor } from "@effect/platform";
import { NodeCommandExecutor, NodeFileSystem, NodeRuntime } from "@effect/platform-node";
import * as NodeContext from "@effect/platform-node/NodeContext";
import * as NodeRuntime from "@effect/platform-node/NodeRuntime";
import * as Command from "@effect/platform/Command";
import * as CommandExecutor from "@effect/platform/CommandExecutor";
import { Effect } from "effect";
import * as Fn from "effect/Function";
import * as Layer from "effect/Layer";

const command = Command.make("git", "diff", "HEAD@{1}", "--stat", "--", "./pnpm-lock.yaml");
const program = Effect.gen(function*() {
Expand All @@ -15,9 +15,4 @@ const program = Effect.gen(function*() {
yield* Effect.logWarning("Please run `pnpm install --fix-lockfile && pnpm dedupe` to update local dependencies.");
});

const MainLive = Fn.pipe(
NodeCommandExecutor.layer,
Layer.provideMerge(NodeFileSystem.layer),
);

program.pipe(Effect.provide(MainLive), NodeRuntime.runMain);
program.pipe(Effect.provide(NodeContext.layer), NodeRuntime.runMain);