From 04bc651d07566ed100167676d682b7c32f5c983e Mon Sep 17 00:00:00 2001 From: Dobes Vandermeer Date: Tue, 1 Apr 2025 13:58:51 -0700 Subject: [PATCH 1/3] Support referencing files in a specific package or at the workspace level in inputs --- packages/hasher/src/PackageTree.ts | 5 +- .../hasher/src/__tests__/TargetHasher.test.ts | 46 +++++++++++++++++++ packages/hasher/src/expandInputPatterns.ts | 7 ++- 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/packages/hasher/src/PackageTree.ts b/packages/hasher/src/PackageTree.ts index e770093bc..5b62dca87 100644 --- a/packages/hasher/src/PackageTree.ts +++ b/packages/hasher/src/PackageTree.ts @@ -110,7 +110,10 @@ export class PackageTree { getPackageFiles(packageName: string, patterns: string[]) { const { root, packageInfos } = this.options; - const packagePath = path.relative(root, path.dirname(packageInfos[packageName].packageJsonPath)).replace(/\\/g, "/"); + // If packageName is given, look up the directory if its package.json. Otherwise, return files that are not in any package. + const packagePath = packageName + ? path.relative(root, path.dirname(packageInfos[packageName].packageJsonPath)).replace(/\\/g, "/") + : ""; const packageFiles = this.#packageFiles[packagePath]; diff --git a/packages/hasher/src/__tests__/TargetHasher.test.ts b/packages/hasher/src/__tests__/TargetHasher.test.ts index f8411792d..d0bbdec49 100644 --- a/packages/hasher/src/__tests__/TargetHasher.test.ts +++ b/packages/hasher/src/__tests__/TargetHasher.test.ts @@ -126,6 +126,52 @@ describe("The main Hasher class", () => { monorepo2.cleanup(); }); + it("creates different hashes when a src file has changed for a specific package", async () => { + const monorepo1 = await setupFixture("monorepo"); + const hasher = new TargetHasher({ root: monorepo1.root, environmentGlob: [] }); + const target = createTarget(monorepo1.root, "package-a", "build"); + target.inputs = ["**/*", "package-b#src/index.ts"]; + + const hash = await getHash(hasher, target); + + const monorepo2 = await setupFixture("monorepo"); + await monorepo2.commitFiles({ "packages/package-b/src/index.ts": "console.log('hello world');" }); + + const hasher2 = new TargetHasher({ root: monorepo2.root, environmentGlob: [] }); + const target2 = createTarget(monorepo2.root, "package-a", "build"); + target2.inputs = ["**/*", "package-b#src/index.ts"]; + + const hash2 = await getHash(hasher2, target2); + + expect(hash).not.toEqual(hash2); + + monorepo1.cleanup(); + monorepo2.cleanup(); + }); + + it("creates different hashes when a src file has changed in the root package", async () => { + const monorepo1 = await setupFixture("monorepo"); + const hasher = new TargetHasher({ root: monorepo1.root, environmentGlob: [] }); + const target = createTarget(monorepo1.root, "package-a", "build"); + target.inputs = ["**/*", "#config.txt"]; + + const hash = await getHash(hasher, target); + + const monorepo2 = await setupFixture("monorepo"); + await monorepo2.commitFiles({ "config.txt": "hello" }); + + const hasher2 = new TargetHasher({ root: monorepo2.root, environmentGlob: [] }); + const target2 = createTarget(monorepo2.root, "package-a", "build"); + target2.inputs = ["**/*", "#config.txt"]; + + const hash2 = await getHash(hasher2, target2); + + expect(hash).not.toEqual(hash2); + + monorepo1.cleanup(); + monorepo2.cleanup(); + }); + it("creates different hashes when the target has a different env glob", async () => { const monorepo1 = await setupFixture("monorepo-with-global-files"); const hasher = new TargetHasher({ root: monorepo1.root, environmentGlob: [] }); diff --git a/packages/hasher/src/expandInputPatterns.ts b/packages/hasher/src/expandInputPatterns.ts index 775938956..e94fbc9f4 100644 --- a/packages/hasher/src/expandInputPatterns.ts +++ b/packages/hasher/src/expandInputPatterns.ts @@ -1,7 +1,7 @@ import { type Target } from "@lage-run/target-graph"; import { type DependencyMap } from "workspace-tools"; -export function expandInputPatterns(patterns: string[], target: Target, dependencyMap: DependencyMap) { +export function expandInputPatterns(patterns: string[], target: Target, dependencyMap: DependencyMap): Record { const expandedPatterns: Record = {}; for (const pattern of patterns) { @@ -34,6 +34,11 @@ export function expandInputPatterns(patterns: string[], target: Target, dependen } } } + } else if (pattern.includes("#")) { + // In this case they specified a specific package which an input file will be pulled from + const [pkg, matchPattern] = pattern.split("#"); + expandedPatterns[pkg] = expandedPatterns[pkg] ?? []; + expandedPatterns[pkg].push(matchPattern); } else { const pkg = target.packageName!; expandedPatterns[pkg] = expandedPatterns[pkg] ?? []; From 4f937dfc68e84222511c0af6b5d628ac2adb2799 Mon Sep 17 00:00:00 2001 From: Dobes Vandermeer Date: Fri, 18 Apr 2025 20:55:47 -0700 Subject: [PATCH 2/3] Change files --- .../change-e975674f-161c-4841-9fec-9751de3638dd.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 change/change-e975674f-161c-4841-9fec-9751de3638dd.json diff --git a/change/change-e975674f-161c-4841-9fec-9751de3638dd.json b/change/change-e975674f-161c-4841-9fec-9751de3638dd.json new file mode 100644 index 000000000..4476bc8d1 --- /dev/null +++ b/change/change-e975674f-161c-4841-9fec-9751de3638dd.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "type": "minor", + "comment": "Support referencing files in a specific package or at the workspace level in inputs", + "packageName": "@lage-run/hasher", + "email": "dobes@formative.com", + "dependentChangeType": "patch" + } + ] +} \ No newline at end of file From d9cf6aef7a90bd0796292bd3359e8c167929a355 Mon Sep 17 00:00:00 2001 From: Dobes Vandermeer Date: Fri, 18 Apr 2025 20:56:05 -0700 Subject: [PATCH 3/3] Improve comments --- packages/hasher/src/PackageTree.ts | 3 ++- packages/hasher/src/expandInputPatterns.ts | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/hasher/src/PackageTree.ts b/packages/hasher/src/PackageTree.ts index 5b62dca87..b96156710 100644 --- a/packages/hasher/src/PackageTree.ts +++ b/packages/hasher/src/PackageTree.ts @@ -110,7 +110,8 @@ export class PackageTree { getPackageFiles(packageName: string, patterns: string[]) { const { root, packageInfos } = this.options; - // If packageName is given, look up the directory if its package.json. Otherwise, return files that are not in any package. + // Look up the directory of the specified package. If packageName is "", that means they want to find a file + // relative to the root workspace. const packagePath = packageName ? path.relative(root, path.dirname(packageInfos[packageName].packageJsonPath)).replace(/\\/g, "/") : ""; diff --git a/packages/hasher/src/expandInputPatterns.ts b/packages/hasher/src/expandInputPatterns.ts index e94fbc9f4..acc25c54e 100644 --- a/packages/hasher/src/expandInputPatterns.ts +++ b/packages/hasher/src/expandInputPatterns.ts @@ -36,6 +36,8 @@ export function expandInputPatterns(patterns: string[], target: Target, dependen } } else if (pattern.includes("#")) { // In this case they specified a specific package which an input file will be pulled from + // Note that if the path starts with '#' the pkg is caluclated as "" and the file is resolved + // relative to the root workspace const [pkg, matchPattern] = pattern.split("#"); expandedPatterns[pkg] = expandedPatterns[pkg] ?? []; expandedPatterns[pkg].push(matchPattern);