diff --git a/.changeset/poor-clubs-kick.md b/.changeset/poor-clubs-kick.md new file mode 100644 index 0000000..4a16972 --- /dev/null +++ b/.changeset/poor-clubs-kick.md @@ -0,0 +1,5 @@ +--- +"@vlandoss/run-run": patch +--- + +Add `pkgs` command diff --git a/packages/run-run/package.json b/packages/run-run/package.json index c3d6b63..d10b3c5 100644 --- a/packages/run-run/package.json +++ b/packages/run-run/package.json @@ -36,6 +36,7 @@ "commander": "13.1.0", "glob": "^11.0.2", "is-ci": "4.1.0", + "memoize": "^10.2.0", "rimraf": "6.0.1", "typescript": "5.8.2" }, diff --git a/packages/run-run/src/program/__tests__/__snapshots__/snapshots.test.ts.snap b/packages/run-run/src/program/__tests__/__snapshots__/snapshots.test.ts.snap index 92bf829..e35b1f4 100644 --- a/packages/run-run/src/program/__tests__/__snapshots__/snapshots.test.ts.snap +++ b/packages/run-run/src/program/__tests__/__snapshots__/snapshots.test.ts.snap @@ -16,6 +16,8 @@ Commands: tsc|typecheck check if TypeScript code is well typed 🎨 clean [options] delete dirty folders or files such as node_modules, etc 🗑️ + pkgs|packages [options] list unique affected packages from list of files + 📦 help [command] display help for command Acknowledgment: @@ -43,6 +45,8 @@ Commands: tsc|typecheck check if TypeScript code is well typed 🎨 clean [options] delete dirty folders or files such as node_modules, etc 🗑️ + pkgs|packages [options] list unique affected packages from list of files + 📦 help [command] display help for command Acknowledgment: @@ -143,6 +147,18 @@ Under the hood, this command uses the rimraf.js to delete dirty folders or files " `; +exports[`should match help messages for all commands: help-command-pkgs 1`] = ` +"Usage: rr pkgs|packages [options] + +list unique affected packages from list of files 📦 + +Options: + --files list of files to check + --decorator [type] type of decorator to use (choices: "turbo") + -h, --help display help for command +" +`; + exports[`should match help messages for all commands: help-command-tools 1`] = ` "Usage: rr tools [options] [command] diff --git a/packages/run-run/src/program/commands/pkgs.ts b/packages/run-run/src/program/commands/pkgs.ts new file mode 100644 index 0000000..6be64e8 --- /dev/null +++ b/packages/run-run/src/program/commands/pkgs.ts @@ -0,0 +1,75 @@ +import path from "node:path"; +import { type Command, createCommand, Option } from "commander"; +import memoize from "memoize"; +import type { Context } from "#/services/ctx"; +import { logger } from "#/services/logger"; + +// Currently only "turbo" is supported, but this can be extended in the future +const decorators = ["turbo"] as const; + +type Options = { + files: string[]; + decorator?: (typeof decorators)[number]; +}; + +export function createPkgsCommand(ctx: Context) { + return createCommand("pkgs") + .alias("packages") + .description("list unique affected packages from list of files 📦") + .addOption(new Option("--files ", "list of files to check")) + .addOption(new Option("--decorator [type]", "type of decorator to use").choices(decorators)) + .action(async function pkgsAction({ files, decorator }: Options, cmd: Command) { + const { appPkg } = ctx; + + if (!appPkg.isMonorepo()) { + const cmdName = cmd.parent?.args[0] ?? cmd.args[0] ?? cmd.name(); + logger.error(`The \`${cmdName}\` command can only be run in a monorepo.`); + return process.exit(1); + } + + const projects = await appPkg.getWorkspaceProjects(); + + const getRelativeRootDir = memoize((rootDir: string) => { + const appDir = appPkg.dirPath; + + if (!path.isAbsolute(rootDir)) { + return rootDir; + } + + return path.relative(appDir, rootDir); + }); + + function getPackageForFile(filePath: string) { + for (const project of projects) { + const relativeRootDir = getRelativeRootDir(project.rootDir); + + if (filePath.startsWith(relativeRootDir)) { + return project; + } + } + + return null; + } + + const uniquePkgsNames = new Set(); + + files.forEach((file) => { + const pkg = getPackageForFile(file); + if (pkg?.manifest.name) { + uniquePkgsNames.add(pkg.manifest.name); + } + }); + + if (!uniquePkgsNames.size) { + return; + } + + const pkgsNames = Array.from(uniquePkgsNames); + + if (decorator === "turbo") { + console.log(...pkgsNames.map((name) => `--filter=...${name}`)); + } else { + console.log(...pkgsNames); + } + }); +} diff --git a/packages/run-run/src/program/index.ts b/packages/run-run/src/program/index.ts index ad0363b..01f4956 100644 --- a/packages/run-run/src/program/index.ts +++ b/packages/run-run/src/program/index.ts @@ -5,6 +5,7 @@ import { createCheckCommand } from "./commands/check"; import { createCleanCommand } from "./commands/clean"; import { createFormatCommand } from "./commands/format"; import { createLintCommand } from "./commands/lint"; +import { createPkgsCommand } from "./commands/pkgs"; import { createRunCommand } from "./commands/run"; import { createToolsCommand } from "./commands/tools"; import { createTypecheckCommand } from "./commands/typecheck"; @@ -31,6 +32,7 @@ export async function createProgram(options: Options) { .addCommand(createFormatCommand(ctx)) .addCommand(createTypecheckCommand(ctx)) .addCommand(createCleanCommand()) + .addCommand(createPkgsCommand(ctx)) .addCommand(createToolsCommand(ctx), { hidden: true, }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c5ca0ca..c25f78c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,7 +19,7 @@ importers: version: 1.0.4 '@types/bun': specifier: latest - version: 1.2.21(@types/react@19.1.9) + version: 1.3.2(@types/react@19.1.9) '@vlandoss/biome-config': specifier: workspace:* version: link:dotfiles/biome-config @@ -117,6 +117,9 @@ importers: is-ci: specifier: 4.1.0 version: 4.1.0 + memoize: + specifier: ^10.2.0 + version: 10.2.0 rimraf: specifier: 6.0.1 version: 6.0.1 @@ -1241,8 +1244,8 @@ packages: '@total-typescript/tsconfig@1.0.4': resolution: {integrity: sha512-fO4ctMPGz1kOFOQ4RCPBRBfMy3gDn+pegUfrGyUFRMv/Rd0ZM3/SHH3hFCYG4u6bPLG8OlmOGcBLDexvyr3A5w==} - '@types/bun@1.2.21': - resolution: {integrity: sha512-NiDnvEqmbfQ6dmZ3EeUO577s4P5bf4HCTXtI6trMc6f6RzirY5IrF3aIookuSpyslFzrnvv2lmEWv5HyC1X79A==} + '@types/bun@1.3.2': + resolution: {integrity: sha512-t15P7k5UIgHKkxwnMNkJbWlh/617rkDGEdSsDbu+qNHTaz9SKf7aC8fiIlUdD5RPpH6GEkP0cK7WlvmrEBRtWg==} '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} @@ -1439,8 +1442,8 @@ packages: builtins@5.1.0: resolution: {integrity: sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==} - bun-types@1.2.21: - resolution: {integrity: sha512-sa2Tj77Ijc/NTLS0/Odjq/qngmEPZfbfnOERi0KRUYhT9R8M4VBioWVmMWE5GrYbKMc+5lVybXygLdibHaqVqw==} + bun-types@1.3.2: + resolution: {integrity: sha512-i/Gln4tbzKNuxP70OWhJRZz1MRfvqExowP7U6JKoI8cntFrtxg7RJK3jvz7wQW54UuvNC8tbKHHri5fy74FVqg==} peerDependencies: '@types/react': ^19 @@ -2207,6 +2210,10 @@ packages: resolution: {integrity: sha512-qFCFUDs7U3b8mBDPyz5EToEKoAkgCzqquIgi9nkkR9bixxOVOre+09lbuH7+9Kn2NFpm56M3GUWVbU2hQgdACA==} engines: {node: '>=10'} + memoize@10.2.0: + resolution: {integrity: sha512-DeC6b7QBrZsRs3Y02A6A7lQyzFbsQbqgjI6UW0GigGWV+u1s25TycMr0XHZE4cJce7rY/vyw2ctMQqfDkIhUEA==} + engines: {node: '>=18'} + merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -2226,6 +2233,10 @@ packages: resolution: {integrity: sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==} engines: {node: '>=8'} + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + minimatch@10.0.3: resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} engines: {node: 20 || >=22} @@ -4771,9 +4782,9 @@ snapshots: '@total-typescript/tsconfig@1.0.4': {} - '@types/bun@1.2.21(@types/react@19.1.9)': + '@types/bun@1.3.2(@types/react@19.1.9)': dependencies: - bun-types: 1.2.21(@types/react@19.1.9) + bun-types: 1.3.2(@types/react@19.1.9) transitivePeerDependencies: - '@types/react' @@ -4982,7 +4993,7 @@ snapshots: dependencies: semver: 7.7.2 - bun-types@1.2.21(@types/react@19.1.9): + bun-types@1.3.2(@types/react@19.1.9): dependencies: '@types/node': 24.2.1 '@types/react': 19.1.9 @@ -5164,10 +5175,6 @@ snapshots: dataloader@1.4.0: {} - debug@4.4.1: - dependencies: - ms: 2.1.3 - debug@4.4.1(supports-color@10.0.0): dependencies: ms: 2.1.3 @@ -5770,6 +5777,10 @@ snapshots: map-age-cleaner: 0.1.3 mimic-fn: 3.1.0 + memoize@10.2.0: + dependencies: + mimic-function: 5.0.1 + merge-stream@2.0.0: {} merge2@1.4.1: {} @@ -5783,6 +5794,8 @@ snapshots: mimic-fn@3.1.0: {} + mimic-function@5.0.1: {} + minimatch@10.0.3: dependencies: '@isaacs/brace-expansion': 5.0.0 @@ -6589,7 +6602,7 @@ snapshots: cac: 6.7.14 chokidar: 4.0.3 consola: 3.4.2 - debug: 4.4.1 + debug: 4.4.1(supports-color@10.0.0) esbuild: 0.25.8 fix-dts-default-cjs-exports: 1.0.1 joycon: 3.1.1