diff --git a/scripts/update-readme.ts b/scripts/update-readme.ts index 2269c50c7..6c791fd97 100644 --- a/scripts/update-readme.ts +++ b/scripts/update-readme.ts @@ -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); diff --git a/scripts/update-version.ts b/scripts/update-version.ts index be18804d8..20e498707 100644 --- a/scripts/update-version.ts +++ b/scripts/update-version.ts @@ -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"; @@ -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); diff --git a/scripts/update-website.ts b/scripts/update-website.ts index 66e84b5c8..6ca84fe3b 100644 --- a/scripts/update-website.ts +++ b/scripts/update-website.ts @@ -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( - ([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((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); diff --git a/scripts/verify-lockfile.ts b/scripts/verify-lockfile.ts index 2c67ee534..1ffdd3d89 100644 --- a/scripts/verify-lockfile.ts +++ b/scripts/verify-lockfile.ts @@ -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*() { @@ -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);