这是indexloc提供的服务,不要输入任何密码
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
59 changes: 56 additions & 3 deletions packages/turbo-repository/__tests__/find-packages.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@ import { strict as assert } from "node:assert";
import * as path from "node:path";
import { Workspace, Package } from "../js/dist/index.js";

const MONOREPO_PATH = path.resolve(__dirname, "./fixtures/monorepo");

describe("findPackages", () => {
it("enumerates packages", async () => {
const workspace = await Workspace.find("./fixtures/monorepo");
const workspace = await Workspace.find(MONOREPO_PATH);
const packages: Package[] = await workspace.findPackages();
assert.notEqual(packages.length, 0);
});

it("returns a package graph", async () => {
const dir = path.resolve(__dirname, "./fixtures/monorepo");
const workspace = await Workspace.find(dir);
const workspace = await Workspace.find(MONOREPO_PATH);
const packages = await workspace.findPackagesWithGraph();

assert.equal(Object.keys(packages).length, 2);
Expand All @@ -26,4 +27,56 @@ describe("findPackages", () => {
assert.deepEqual(pkg2.dependencies, []);
assert.deepEqual(pkg2.dependents, ["apps/app"]);
});

it("returns the package for a given path", async () => {
const workspace = await Workspace.find(MONOREPO_PATH);

for (const [filePath, result] of [
["apps/app/src/util/useful-file.ts", "app-a"],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should have some tests for root files e.g. a root tsconfig.json, pnpm-lock.yaml, pnpm-workspace.yaml

[
"apps/app/src/very/deeply/nested/file/that/is/deep/as/can/be/with/a/package.ts",
"app-a",
],
["apps/app/src/util/non-typescript-file.txt", "app-a"],
["apps/app/src/a-directory", "app-a"],
["apps/app/package.json", "app-a"],
["apps/app/tsconfig.json", "app-a"],
["apps/app", "app-a"], // The root of a package is still "within" a package!
["apps/app/", "app-a"], // Trailing-slash should be ignored
["packages/ui/pretty-stuff.css", "ui"],
// This may be unintentional - I expected `findPackages` to return a nameless-package for `packages/blank` (whose
// `package.json` is missing a `name` field), but instead there is no such package returned.
["packages/blank/nothing.null", undefined],
["packages/not-in-a-package", undefined],
["packages/not-in-a-package/but/very/deep/within/nothingness", undefined],
["", undefined],
[".", undefined],
["..", undefined],
["apps/../apps/app/src", "app-a"],
["apps/app/src/util/../../../../apps/app", "app-a"],
["not a legal ^&(^) path", undefined],
["package.json", undefined],
["tsconfig.json", undefined],
]) {
if (result === undefined) {
assert.rejects(
() => workspace.findPackageByPath(filePath!),
`Expected rejection for ${filePath}`
);
} else {
workspace
.findPackageByPath(filePath!)
.then((pkg) => {
assert.equal(
pkg.name,
result,
`Expected ${result} for ${filePath}`
);
})
.catch((reason) => {
assert.fail(`Expected success for ${filePath}, but got ${reason}`);
});
}
}
});
});
10 changes: 10 additions & 0 deletions packages/turbo-repository/js/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,14 @@ export class Workspace {
base?: string | undefined | null,
optimizeGlobalInvalidations?: boolean | undefined | null
): Promise<Array<Package>>;
/**
* Given a path (relative to the workspace root), returns the
* package that contains it.
*
* This is a naive implementation that simply "iterates-up". If this function is
* expected to be called many times for files that are deep within the same
* package, we could optimize this by caching the containing-package of
* every ancestor.
*/
findPackageByPath(path: string): Promise<Package>;
}
37 changes: 36 additions & 1 deletion packages/turbo-repository/rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use turbopath::{AbsoluteSystemPath, AnchoredSystemPath, AnchoredSystemPathBuf};
use turborepo_repository::{
change_mapper::{
ChangeMapper, DefaultPackageChangeMapper, DefaultPackageChangeMapperWithLockfile,
LockfileContents, PackageChanges,
LockfileContents, PackageChangeMapper, PackageChanges,
},
inference::RepoState as WorkspaceState,
package_graph::{PackageGraph, PackageName, PackageNode, WorkspacePackage, ROOT_PKG_NAME},
Expand Down Expand Up @@ -291,4 +291,39 @@ impl Workspace {

Ok(serializable_packages)
}

/// Given a path (relative to the workspace root), returns the
/// package that contains it.
///
/// This is a naive implementation that simply "iterates-up". If this
/// function is expected to be called many times for files that are deep
/// within the same package, we could optimize this by caching the
/// containing-package of every ancestor.
#[napi]
pub async fn find_package_by_path(&self, path: String) -> Result<Package, Error> {
let package_mapper = DefaultPackageChangeMapper::new(&self.graph);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is something that I should've given you a code pointer on. This is the structure we use to determine which files affect which packages. We can use it raw here to do the same purpose of mapping files to packages.

Happy to chat a bit more about this!

let anchored_path = AnchoredSystemPath::new(&path)
.map_err(|e| Error::from_reason(e.to_string()))?
.clean();
match package_mapper.detect_package(&anchored_path) {
turborepo_repository::change_mapper::PackageMapping::All(
_all_package_change_reason,
) => Err(Error::from_reason("file belongs to many packages")),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An alternative here would be to possibly return the root package or return that the file change is considered "global".

turborepo_repository::change_mapper::PackageMapping::None => Err(Error::from_reason(
"iterated to the root of the workspace and found no package",
)),
turborepo_repository::change_mapper::PackageMapping::Package((package, _reason)) => {
let workspace_root = match AbsoluteSystemPath::new(&self.absolute_path) {
Ok(path) => path,
Err(e) => return Err(Error::from_reason(e.to_string())),
};
let package_path = workspace_root.resolve(&package.path);
Ok(Package::new(
package.name.to_string(),
workspace_root,
&package_path,
))
}
}
}
}
Loading