From 96141c88dcf6f10644d945346b6fa0bea64efcd8 Mon Sep 17 00:00:00 2001 From: Elizabeth Craig Date: Thu, 11 May 2023 23:21:04 -0700 Subject: [PATCH 1/3] Move path utilities to separate package --- ...-d8fc65a9-0286-4b6a-9e09-686e024b882b.json | 7 +++ ...-14b42056-f741-41ac-b7ad-c5079d191ab9.json | 7 +++ packages/grapher/package.json | 2 +- packages/paths/README.md | 5 ++ packages/paths/etc/paths.api.md | 18 +++++++ packages/paths/jest.config.js | 1 + packages/paths/package.json | 24 +++++++++ packages/paths/src/index.ts | 50 +++++++++++++++++++ packages/paths/tsconfig.json | 7 +++ .../etc/workspace-tools.api.md | 14 +++--- packages/workspace-tools/package.json | 5 +- .../src/git/getDefaultRemote.ts | 2 +- .../src/graph/createDependencyMap.ts | 1 - packages/workspace-tools/src/index.ts | 2 + .../workspace-tools/src/lockfile/index.ts | 2 +- packages/workspace-tools/src/paths.ts | 50 +------------------ .../getWorkspaceManagerAndRoot.ts | 2 +- scripts/package.json | 2 +- scripts/tsconfig.base.json | 1 + 19 files changed, 139 insertions(+), 63 deletions(-) create mode 100644 change/@ws-tools-paths-d8fc65a9-0286-4b6a-9e09-686e024b882b.json create mode 100644 change/workspace-tools-14b42056-f741-41ac-b7ad-c5079d191ab9.json create mode 100644 packages/paths/README.md create mode 100644 packages/paths/etc/paths.api.md create mode 100644 packages/paths/jest.config.js create mode 100644 packages/paths/package.json create mode 100644 packages/paths/src/index.ts create mode 100644 packages/paths/tsconfig.json diff --git a/change/@ws-tools-paths-d8fc65a9-0286-4b6a-9e09-686e024b882b.json b/change/@ws-tools-paths-d8fc65a9-0286-4b6a-9e09-686e024b882b.json new file mode 100644 index 00000000..e88d7db1 --- /dev/null +++ b/change/@ws-tools-paths-d8fc65a9-0286-4b6a-9e09-686e024b882b.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Create package", + "packageName": "@ws-tools/paths", + "email": "elcraig@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/workspace-tools-14b42056-f741-41ac-b7ad-c5079d191ab9.json b/change/workspace-tools-14b42056-f741-41ac-b7ad-c5079d191ab9.json new file mode 100644 index 00000000..7aac2f59 --- /dev/null +++ b/change/workspace-tools-14b42056-f741-41ac-b7ad-c5079d191ab9.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "Move path utilities to separate package (re-exported by this one)", + "packageName": "workspace-tools", + "email": "elcraig@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/grapher/package.json b/packages/grapher/package.json index 059df6cb..88666b70 100644 --- a/packages/grapher/package.json +++ b/packages/grapher/package.json @@ -15,7 +15,7 @@ "lib/!(__*)/**/*" ], "scripts": { - "build": "tsc", + "build": "tsc --pretty", "start": "tsc -w --preserveWatchOutput", "test": "jest" }, diff --git a/packages/paths/README.md b/packages/paths/README.md new file mode 100644 index 00000000..ece09d81 --- /dev/null +++ b/packages/paths/README.md @@ -0,0 +1,5 @@ +# @ws-tools/paths + +Helper methods related to paths in a repo or monorepo. The methods in this package are re-exported from [`workspace-tools`](https://www.npmjs.com/package/workspace-tools) for convenience. + +This package should contain only the most basic helpers with no or minimal dependencies. diff --git a/packages/paths/etc/paths.api.md b/packages/paths/etc/paths.api.md new file mode 100644 index 00000000..fd4e43c7 --- /dev/null +++ b/packages/paths/etc/paths.api.md @@ -0,0 +1,18 @@ +## API Report File for "@ws-tools/paths" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +// @public +export function findGitRoot(cwd: string): string; + +// @public +export function findPackageRoot(cwd: string): string | undefined; + +// @public +export function searchUp(filePath: string | string[], cwd: string): string | undefined; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/paths/jest.config.js b/packages/paths/jest.config.js new file mode 100644 index 00000000..5e67249c --- /dev/null +++ b/packages/paths/jest.config.js @@ -0,0 +1 @@ +module.exports = require("@ws-tools/scripts/jest/jest.config"); diff --git a/packages/paths/package.json b/packages/paths/package.json new file mode 100644 index 00000000..085af4c4 --- /dev/null +++ b/packages/paths/package.json @@ -0,0 +1,24 @@ +{ + "name": "@ws-tools/paths", + "version": "0.1.0", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/microsoft/workspace-tools" + }, + "main": "lib/index.js", + "types": "lib/index.d.ts", + "files": [ + "lib/!(__*)", + "lib/!(__*)/**" + ], + "scripts": { + "api": "ws-tools-scripts api", + "build": "tsc --pretty", + "start": "tsc -w --preserveWatchOutput", + "test": "jest" + }, + "devDependencies": { + "@ws-tools/scripts": "*" + } +} diff --git a/packages/paths/src/index.ts b/packages/paths/src/index.ts new file mode 100644 index 00000000..418d8665 --- /dev/null +++ b/packages/paths/src/index.ts @@ -0,0 +1,50 @@ +import path from "path"; +import fs from "fs"; +import { spawnSync } from "child_process"; + +/** + * Starting from `cwd`, searches up the directory hierarchy for `filePath`. + * If multiple strings are given, searches each directory level for any of them. + * @returns Full path to the item found, or undefined if not found. + */ +export function searchUp(filePath: string | string[], cwd: string) { + const paths = typeof filePath === "string" ? [filePath] : filePath; + // convert to an absolute path if needed + cwd = path.resolve(cwd); + const root = path.parse(cwd).root; + + let foundPath: string | undefined; + + while (!foundPath && cwd !== root) { + foundPath = paths.find((p) => fs.existsSync(path.join(cwd, p))); + if (foundPath) { + break; + } + + cwd = path.dirname(cwd); + } + + return foundPath ? path.join(cwd, foundPath) : undefined; +} + +/** + * Starting from `cwd`, uses `git rev-parse --show-toplevel` to find the root of the git repo. + * Throws if `cwd` is not in a Git repository. + */ +export function findGitRoot(cwd: string) { + const result = spawnSync("git", ["rev-parse", "--show-toplevel"], { cwd }); + + if (result.status !== 0) { + throw new Error(`Directory "${cwd}" is not in a git repository`); + } + + return path.normalize(result.stdout.toString().trim()); +} + +/** + * Starting from `cwd`, searches up the directory hierarchy for `package.json`. + */ +export function findPackageRoot(cwd: string) { + const jsonPath = searchUp("package.json", cwd); + return jsonPath && path.dirname(jsonPath); +} diff --git a/packages/paths/tsconfig.json b/packages/paths/tsconfig.json new file mode 100644 index 00000000..a5abcce9 --- /dev/null +++ b/packages/paths/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "@ws-tools/scripts/tsconfig.base.json", + "compilerOptions": { + "outDir": "lib" + }, + "include": ["src"] +} diff --git a/packages/workspace-tools/etc/workspace-tools.api.md b/packages/workspace-tools/etc/workspace-tools.api.md index 1342d40e..f7e66d02 100644 --- a/packages/workspace-tools/etc/workspace-tools.api.md +++ b/packages/workspace-tools/etc/workspace-tools.api.md @@ -6,6 +6,9 @@ /// +import { findGitRoot } from '@ws-tools/paths'; +import { findPackageRoot } from '@ws-tools/paths'; +import { searchUp } from '@ws-tools/paths'; import { SpawnSyncOptions } from 'child_process'; import { SpawnSyncReturns } from 'child_process'; @@ -56,11 +59,9 @@ export function fetchRemote(remote: string, cwd: string): void; // @public (undocumented) export function fetchRemoteBranch(remote: string, remoteBranch: string, cwd: string): void; -// @public -export function findGitRoot(cwd: string): string; +export { findGitRoot } -// @public -export function findPackageRoot(cwd: string): string | undefined; +export { findPackageRoot } // @public export function findProjectRoot(cwd: string): string; @@ -250,7 +251,7 @@ export type GitProcessOutput = { // @public (undocumented) export function init(cwd: string, email?: string, username?: string): void; -// @public (undocumented) +// @public @deprecated (undocumented) export function isChildOf(child: string, parent: string): boolean; // @public (undocumented) @@ -444,8 +445,7 @@ export function _resetPackageJsonFilesCache(): void; // @public (undocumented) export function revertLocalChanges(cwd: string): boolean; -// @public -export function searchUp(filePath: string | string[], cwd: string): string | undefined; +export { searchUp } // @public export function setCachingEnabled(enabled: boolean): void; diff --git a/packages/workspace-tools/package.json b/packages/workspace-tools/package.json index c7b5de40..673184f1 100644 --- a/packages/workspace-tools/package.json +++ b/packages/workspace-tools/package.json @@ -14,7 +14,7 @@ ], "scripts": { "api": "ws-tools-scripts api", - "build": "tsc", + "build": "tsc --pretty", "start": "tsc -w --preserveWatchOutput", "test": "jest" }, @@ -25,7 +25,8 @@ "globby": "^11.0.0", "jju": "^1.4.0", "js-yaml": "^4.1.0", - "micromatch": "^4.0.0" + "micromatch": "^4.0.0", + "@ws-tools/paths": "^0.1.0" }, "devDependencies": { "lodash": "^4.17.21", diff --git a/packages/workspace-tools/src/git/getDefaultRemote.ts b/packages/workspace-tools/src/git/getDefaultRemote.ts index 25512320..168d065d 100644 --- a/packages/workspace-tools/src/git/getDefaultRemote.ts +++ b/packages/workspace-tools/src/git/getDefaultRemote.ts @@ -1,6 +1,6 @@ import fs from "fs"; import path from "path"; -import { findGitRoot } from "../paths"; +import { findGitRoot } from "@ws-tools/paths"; import { PackageInfo } from "../types/PackageInfo"; import { getRepositoryName } from "./getRepositoryName"; import { git } from "./git"; diff --git a/packages/workspace-tools/src/graph/createDependencyMap.ts b/packages/workspace-tools/src/graph/createDependencyMap.ts index b19b35c3..c490d9da 100644 --- a/packages/workspace-tools/src/graph/createDependencyMap.ts +++ b/packages/workspace-tools/src/graph/createDependencyMap.ts @@ -1,6 +1,5 @@ import { getPackageDependencies, PackageDependenciesOptions } from "./getPackageDependencies"; import { PackageInfos } from "../types/PackageInfo"; -import { getPackageInfos, getPackageInfosAsync } from "../getPackageInfos"; export interface DependencyMap { dependencies: Map>; diff --git a/packages/workspace-tools/src/index.ts b/packages/workspace-tools/src/index.ts index 589eb2dc..efa8c44a 100644 --- a/packages/workspace-tools/src/index.ts +++ b/packages/workspace-tools/src/index.ts @@ -20,3 +20,5 @@ export * from "./workspaces/getChangedPackages"; export * from "./workspaces/getPackagesByFiles"; export * from "./workspaces/listOfWorkspacePackageNames"; export * from "./workspaces/getAllPackageJsonFiles"; + +export { searchUp, findGitRoot, findPackageRoot } from "@ws-tools/paths"; diff --git a/packages/workspace-tools/src/lockfile/index.ts b/packages/workspace-tools/src/lockfile/index.ts index fb2faba5..ca5f64b9 100644 --- a/packages/workspace-tools/src/lockfile/index.ts +++ b/packages/workspace-tools/src/lockfile/index.ts @@ -3,7 +3,7 @@ import fs from "fs"; import path from "path"; import { ParsedLock, PnpmLockFile, NpmLockFile, BerryLockFile } from "./types"; import { nameAtVersion } from "./nameAtVersion"; -import { searchUp } from "../paths"; +import { searchUp } from "@ws-tools/paths"; import { parsePnpmLock } from "./parsePnpmLock"; import { parseNpmLock } from "./parseNpmLock"; import { readYaml } from "./readYaml"; diff --git a/packages/workspace-tools/src/paths.ts b/packages/workspace-tools/src/paths.ts index 19b8c5d0..97c7415c 100644 --- a/packages/workspace-tools/src/paths.ts +++ b/packages/workspace-tools/src/paths.ts @@ -1,55 +1,8 @@ import path from "path"; -import fs from "fs"; +import { findGitRoot } from "@ws-tools/paths"; import { getWorkspaceRoot } from "./workspaces/getWorkspaceRoot"; -import { git } from "./git"; import { logVerboseWarning } from "./logging"; -/** - * Starting from `cwd`, searches up the directory hierarchy for `filePath`. - * If multiple strings are given, searches each directory level for any of them. - * @returns Full path to the item found, or undefined if not found. - */ -export function searchUp(filePath: string | string[], cwd: string) { - const paths = typeof filePath === "string" ? [filePath] : filePath; - // convert to an absolute path if needed - cwd = path.resolve(cwd); - const root = path.parse(cwd).root; - - let foundPath: string | undefined; - - while (!foundPath && cwd !== root) { - foundPath = paths.find((p) => fs.existsSync(path.join(cwd, p))); - if (foundPath) { - break; - } - - cwd = path.dirname(cwd); - } - - return foundPath ? path.join(cwd, foundPath) : undefined; -} - -/** - * Starting from `cwd`, uses `git rev-parse --show-toplevel` to find the root of the git repo. - * Throws if `cwd` is not in a Git repository. - */ -export function findGitRoot(cwd: string) { - const output = git(["rev-parse", "--show-toplevel"], { cwd }); - if (!output.success) { - throw new Error(`Directory "${cwd}" is not in a git repository`); - } - - return path.normalize(output.stdout); -} - -/** - * Starting from `cwd`, searches up the directory hierarchy for `package.json`. - */ -export function findPackageRoot(cwd: string) { - const jsonPath = searchUp("package.json", cwd); - return jsonPath && path.dirname(jsonPath); -} - /** * Starting from `cwd`, searches up the directory hierarchy for the workspace root, * falling back to the git root if no workspace is detected. @@ -68,6 +21,7 @@ export function findProjectRoot(cwd: string) { return workspaceRoot || findGitRoot(cwd); } +/** @deprecated */ export function isChildOf(child: string, parent: string) { const relativePath = path.relative(child, parent); return /^[.\/\\]+$/.test(relativePath); diff --git a/packages/workspace-tools/src/workspaces/implementations/getWorkspaceManagerAndRoot.ts b/packages/workspace-tools/src/workspaces/implementations/getWorkspaceManagerAndRoot.ts index 6505a713..b1c521c9 100644 --- a/packages/workspace-tools/src/workspaces/implementations/getWorkspaceManagerAndRoot.ts +++ b/packages/workspace-tools/src/workspaces/implementations/getWorkspaceManagerAndRoot.ts @@ -1,5 +1,5 @@ import path from "path"; -import { searchUp } from "../../paths"; +import { searchUp } from "@ws-tools/paths"; import { WorkspaceManager } from "../WorkspaceManager"; import { isCachingEnabled } from "../../isCachingEnabled"; diff --git a/scripts/package.json b/scripts/package.json index 1585a168..55003848 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -7,6 +7,6 @@ "ws-tools-scripts": "bin/ws-tools-scripts.js" }, "scripts": { - "build": "tsc --noEmit" + "build": "tsc --pretty --noEmit" } } diff --git a/scripts/tsconfig.base.json b/scripts/tsconfig.base.json index 9dc17ee8..41db6f3d 100644 --- a/scripts/tsconfig.base.json +++ b/scripts/tsconfig.base.json @@ -6,6 +6,7 @@ "declaration": true, "sourceMap": true, "strict": true, + "noUnusedLocals": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "skipLibCheck": true, From c4e1ab2dfe315b2369dc5daf88f4c0dc69ceb16f Mon Sep 17 00:00:00 2001 From: Elizabeth Craig Date: Thu, 11 May 2023 23:36:22 -0700 Subject: [PATCH 2/3] split paths package files --- packages/paths/package.json | 7 ++++ packages/paths/src/findGitRoot.ts | 16 ++++++++ packages/paths/src/findPackageRoot.ts | 10 +++++ packages/paths/src/index.ts | 53 ++------------------------- packages/paths/src/searchUp.ts | 27 ++++++++++++++ 5 files changed, 63 insertions(+), 50 deletions(-) create mode 100644 packages/paths/src/findGitRoot.ts create mode 100644 packages/paths/src/findPackageRoot.ts create mode 100644 packages/paths/src/searchUp.ts diff --git a/packages/paths/package.json b/packages/paths/package.json index 085af4c4..a5160151 100644 --- a/packages/paths/package.json +++ b/packages/paths/package.json @@ -2,12 +2,19 @@ "name": "@ws-tools/paths", "version": "0.1.0", "license": "MIT", + "description": "Helper methods related to paths in a repo or monorepo", "repository": { "type": "git", "url": "https://github.com/microsoft/workspace-tools" }, "main": "lib/index.js", "types": "lib/index.d.ts", + "exports": { + ".": { + "require": "./lib/index.js", + "types": "./lib/index.d.ts" + } + }, "files": [ "lib/!(__*)", "lib/!(__*)/**" diff --git a/packages/paths/src/findGitRoot.ts b/packages/paths/src/findGitRoot.ts new file mode 100644 index 00000000..cd20678e --- /dev/null +++ b/packages/paths/src/findGitRoot.ts @@ -0,0 +1,16 @@ +import path from "path"; +import { spawnSync } from "child_process"; + +/** + * Starting from `cwd`, uses `git rev-parse --show-toplevel` to find the root of the git repo. + * Throws if `cwd` is not in a Git repository. + */ +export function findGitRoot(cwd: string) { + const result = spawnSync("git", ["rev-parse", "--show-toplevel"], { cwd }); + + if (result.status !== 0) { + throw new Error(`Directory "${cwd}" is not in a git repository`); + } + + return path.normalize(result.stdout.toString().trim()); +} diff --git a/packages/paths/src/findPackageRoot.ts b/packages/paths/src/findPackageRoot.ts new file mode 100644 index 00000000..40762286 --- /dev/null +++ b/packages/paths/src/findPackageRoot.ts @@ -0,0 +1,10 @@ +import path from "path"; +import { searchUp } from "./searchUp"; + +/** + * Starting from `cwd`, searches up the directory hierarchy for `package.json`. + */ +export function findPackageRoot(cwd: string) { + const jsonPath = searchUp("package.json", cwd); + return jsonPath && path.dirname(jsonPath); +} diff --git a/packages/paths/src/index.ts b/packages/paths/src/index.ts index 418d8665..d02a6738 100644 --- a/packages/paths/src/index.ts +++ b/packages/paths/src/index.ts @@ -1,50 +1,3 @@ -import path from "path"; -import fs from "fs"; -import { spawnSync } from "child_process"; - -/** - * Starting from `cwd`, searches up the directory hierarchy for `filePath`. - * If multiple strings are given, searches each directory level for any of them. - * @returns Full path to the item found, or undefined if not found. - */ -export function searchUp(filePath: string | string[], cwd: string) { - const paths = typeof filePath === "string" ? [filePath] : filePath; - // convert to an absolute path if needed - cwd = path.resolve(cwd); - const root = path.parse(cwd).root; - - let foundPath: string | undefined; - - while (!foundPath && cwd !== root) { - foundPath = paths.find((p) => fs.existsSync(path.join(cwd, p))); - if (foundPath) { - break; - } - - cwd = path.dirname(cwd); - } - - return foundPath ? path.join(cwd, foundPath) : undefined; -} - -/** - * Starting from `cwd`, uses `git rev-parse --show-toplevel` to find the root of the git repo. - * Throws if `cwd` is not in a Git repository. - */ -export function findGitRoot(cwd: string) { - const result = spawnSync("git", ["rev-parse", "--show-toplevel"], { cwd }); - - if (result.status !== 0) { - throw new Error(`Directory "${cwd}" is not in a git repository`); - } - - return path.normalize(result.stdout.toString().trim()); -} - -/** - * Starting from `cwd`, searches up the directory hierarchy for `package.json`. - */ -export function findPackageRoot(cwd: string) { - const jsonPath = searchUp("package.json", cwd); - return jsonPath && path.dirname(jsonPath); -} +export { findGitRoot } from "./findGitRoot"; +export { findPackageRoot } from "./findPackageRoot"; +export { searchUp } from "./searchUp"; diff --git a/packages/paths/src/searchUp.ts b/packages/paths/src/searchUp.ts new file mode 100644 index 00000000..715b4bba --- /dev/null +++ b/packages/paths/src/searchUp.ts @@ -0,0 +1,27 @@ +import path from "path"; +import fs from "fs"; + +/** + * Starting from `cwd`, searches up the directory hierarchy for `filePath`. + * If multiple strings are given, searches each directory level for any of them. + * @returns Full path to the item found, or undefined if not found. + */ +export function searchUp(filePath: string | string[], cwd: string) { + const paths = typeof filePath === "string" ? [filePath] : filePath; + // convert to an absolute path if needed + cwd = path.resolve(cwd); + const root = path.parse(cwd).root; + + let foundPath: string | undefined; + + while (!foundPath && cwd !== root) { + foundPath = paths.find((p) => fs.existsSync(path.join(cwd, p))); + if (foundPath) { + break; + } + + cwd = path.dirname(cwd); + } + + return foundPath ? path.join(cwd, foundPath) : undefined; +} From 919faf89b9dbae9d4a9fee4435e8c5b96cfe78a7 Mon Sep 17 00:00:00 2001 From: Elizabeth Craig Date: Thu, 13 Nov 2025 17:43:08 -0800 Subject: [PATCH 3/3] Change files --- .../change-f2f87433-eff5-442f-aee9-504082645db4.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 change/change-f2f87433-eff5-442f-aee9-504082645db4.json diff --git a/change/change-f2f87433-eff5-442f-aee9-504082645db4.json b/change/change-f2f87433-eff5-442f-aee9-504082645db4.json new file mode 100644 index 00000000..9050dcb4 --- /dev/null +++ b/change/change-f2f87433-eff5-442f-aee9-504082645db4.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "type": "none", + "comment": "Update tsc command format", + "packageName": "@ws-tools/grapher", + "email": "elcraig@microsoft.com", + "dependentChangeType": "none" + } + ] +} \ No newline at end of file