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/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
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..a5160151
--- /dev/null
+++ b/packages/paths/package.json
@@ -0,0 +1,31 @@
+{
+ "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/!(__*)/**"
+ ],
+ "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/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
new file mode 100644
index 00000000..d02a6738
--- /dev/null
+++ b/packages/paths/src/index.ts
@@ -0,0 +1,3 @@
+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;
+}
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,