@lage-run/hasher 1.3.4 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.json CHANGED
@@ -2,7 +2,43 @@
2
2
  "name": "@lage-run/hasher",
3
3
  "entries": [
4
4
  {
5
- "date": "Fri, 13 Sep 2024 18:04:33 GMT",
5
+ "date": "Fri, 27 Sep 2024 20:03:27 GMT",
6
+ "version": "1.5.0",
7
+ "tag": "@lage-run/hasher_v1.5.0",
8
+ "comments": {
9
+ "minor": [
10
+ {
11
+ "author": "kchau@microsoft.com",
12
+ "package": "@lage-run/hasher",
13
+ "commit": "2919f9041f931dc6ef65017f7aedb9fef9dab66d",
14
+ "comment": "exposes getInputFiles, packageTrees"
15
+ },
16
+ {
17
+ "author": "beachball",
18
+ "package": "@lage-run/hasher",
19
+ "comment": "Bump @lage-run/globby to v14.1.0",
20
+ "commit": "not available"
21
+ }
22
+ ]
23
+ }
24
+ },
25
+ {
26
+ "date": "Wed, 25 Sep 2024 20:28:10 GMT",
27
+ "version": "1.4.0",
28
+ "tag": "@lage-run/hasher_v1.4.0",
29
+ "comments": {
30
+ "minor": [
31
+ {
32
+ "author": "kchau@microsoft.com",
33
+ "package": "@lage-run/hasher",
34
+ "commit": "09313be6a4a5667bf8c50b654428f778f6fa4028",
35
+ "comment": "writes out the \"inputs\" as hashes files inside node_modules\\.cache\\lage\\hashes\\**"
36
+ }
37
+ ]
38
+ }
39
+ },
40
+ {
41
+ "date": "Fri, 13 Sep 2024 18:05:04 GMT",
6
42
  "version": "1.3.4",
7
43
  "tag": "@lage-run/hasher_v1.3.4",
8
44
  "comments": {
package/CHANGELOG.md CHANGED
@@ -1,12 +1,29 @@
1
1
  # Change Log - @lage-run/hasher
2
2
 
3
- <!-- This log was last generated on Fri, 13 Sep 2024 18:04:33 GMT and should not be manually modified. -->
3
+ <!-- This log was last generated on Fri, 27 Sep 2024 20:03:27 GMT and should not be manually modified. -->
4
4
 
5
5
  <!-- Start content -->
6
6
 
7
+ ## 1.5.0
8
+
9
+ Fri, 27 Sep 2024 20:03:27 GMT
10
+
11
+ ### Minor changes
12
+
13
+ - exposes getInputFiles, packageTrees (kchau@microsoft.com)
14
+ - Bump @lage-run/globby to v14.1.0
15
+
16
+ ## 1.4.0
17
+
18
+ Wed, 25 Sep 2024 20:28:10 GMT
19
+
20
+ ### Minor changes
21
+
22
+ - writes out the "inputs" as hashes files inside node_modules\.cache\lage\hashes\** (kchau@microsoft.com)
23
+
7
24
  ## 1.3.4
8
25
 
9
- Fri, 13 Sep 2024 18:04:33 GMT
26
+ Fri, 13 Sep 2024 18:05:04 GMT
10
27
 
11
28
  ### Patches
12
29
 
@@ -30,6 +30,11 @@ export interface TargetManifest {
30
30
  */
31
31
  export declare class TargetHasher {
32
32
  private options;
33
+ targetHashesLog: Record<string, {
34
+ fileHashes: Record<string, string>;
35
+ globalFileHashes: Record<string, string>;
36
+ }>;
37
+ targetHashesDirectory: string;
33
38
  logger: Logger | undefined;
34
39
  fileHasher: FileHasher;
35
40
  packageTree: PackageTree | undefined;
@@ -40,13 +45,12 @@ export declare class TargetHasher {
40
45
  lockInfo: ParsedLock | undefined;
41
46
  targetHashes: Record<string, string>;
42
47
  dependencyMap: DependencyMap;
43
- memoizedEnvGlobResults: Map<string, string[]>;
44
- getMemorizedEnvGlobResults(envGlob: string[]): Promise<string[]>;
45
48
  getPackageInfos(workspacePackages: WorkspaceInfo): PackageInfos;
46
- expandInputPatterns(patterns: string[], target: Target): Record<string, string[]>;
47
49
  constructor(options: TargetHasherOptions);
48
50
  ensureInitialized(): void;
49
51
  initialize(): Promise<void>;
50
52
  hash(target: Target): Promise<string>;
53
+ writeTargetHashesManifest(): void;
54
+ getEnvironmentGlobHashes(root: string, target: Target): Promise<Record<string, string>>;
51
55
  cleanup(): Promise<void>;
52
56
  }
@@ -9,7 +9,7 @@ Object.defineProperty(exports, "TargetHasher", {
9
9
  }
10
10
  });
11
11
  const _globhasher = require("glob-hasher");
12
- const _fastglob = /*#__PURE__*/ _interop_require_default(require("fast-glob"));
12
+ const _globby = require("@lage-run/globby");
13
13
  const _fs = /*#__PURE__*/ _interop_require_default(require("fs"));
14
14
  const _path = /*#__PURE__*/ _interop_require_default(require("path"));
15
15
  const _workspacetools = require("workspace-tools");
@@ -19,6 +19,7 @@ const _resolveInternalDependencies = require("./resolveInternalDependencies.js")
19
19
  const _resolveExternalDependencies = require("./resolveExternalDependencies.js");
20
20
  const _FileHasher = require("./FileHasher.js");
21
21
  const _PackageTree = require("./PackageTree.js");
22
+ const _getInputFiles = require("./getInputFiles.js");
22
23
  function _define_property(obj, key, value) {
23
24
  if (key in obj) {
24
25
  Object.defineProperty(obj, key, {
@@ -38,17 +39,6 @@ function _interop_require_default(obj) {
38
39
  };
39
40
  }
40
41
  class TargetHasher {
41
- async getMemorizedEnvGlobResults(envGlob) {
42
- const key = envGlob.join("\0ENV_GLOB\0");
43
- const { root } = this.options;
44
- if (!this.memoizedEnvGlobResults.has(key)) {
45
- const files = await (0, _fastglob.default)(envGlob, {
46
- cwd: root
47
- });
48
- this.memoizedEnvGlobResults.set(key, files);
49
- }
50
- return this.memoizedEnvGlobResults.get(key) ?? [];
51
- }
52
42
  getPackageInfos(workspacePackages) {
53
43
  const { root } = this.options;
54
44
  const packageInfos = {};
@@ -72,43 +62,6 @@ class TargetHasher {
72
62
  }
73
63
  return packageInfos;
74
64
  }
75
- expandInputPatterns(patterns, target) {
76
- const expandedPatterns = {};
77
- for (const pattern of patterns){
78
- if (pattern.startsWith("^") || pattern.startsWith("!^")) {
79
- const matchPattern = pattern.replace("^", "");
80
- // get all the packages that are transitive deps and add them to the list
81
- const queue = [
82
- target.packageName
83
- ];
84
- const visited = new Set();
85
- while(queue.length > 0){
86
- const pkg = queue.pop();
87
- if (visited.has(pkg)) {
88
- continue;
89
- }
90
- visited.add(pkg);
91
- if (pkg !== target.packageName) {
92
- expandedPatterns[pkg] = expandedPatterns[pkg] ?? [];
93
- expandedPatterns[pkg].push(matchPattern);
94
- }
95
- if (this.dependencyMap.dependencies.has(pkg)) {
96
- const deps = this.dependencyMap.dependencies.get(pkg);
97
- if (deps) {
98
- for (const dep of deps){
99
- queue.push(dep);
100
- }
101
- }
102
- }
103
- }
104
- } else {
105
- const pkg = target.packageName;
106
- expandedPatterns[pkg] = expandedPatterns[pkg] ?? [];
107
- expandedPatterns[pkg].push(pattern);
108
- }
109
- }
110
- return expandedPatterns;
111
- }
112
65
  ensureInitialized() {
113
66
  if (!this.initializedPromise) {
114
67
  throw new Error("TargetHasher is not initialized");
@@ -121,7 +74,9 @@ class TargetHasher {
121
74
  return;
122
75
  }
123
76
  this.initializedPromise = Promise.all([
124
- this.fileHasher.readManifest().then(()=>this.getMemorizedEnvGlobResults(environmentGlob)).then((files)=>this.fileHasher.hash(files)).then((hash)=>this.globalInputsHash = hash),
77
+ this.fileHasher.readManifest().then(()=>(0, _globby.globAsync)(environmentGlob, {
78
+ cwd: root
79
+ })).then((files)=>this.fileHasher.hash(files)).then((hash)=>this.globalInputsHash = hash),
125
80
  (0, _workspacetools.getWorkspacesAsync)(root).then((workspaceInfo)=>this.workspaceInfo = workspaceInfo).then(()=>{
126
81
  this.packageInfos = this.getPackageInfos(this.workspaceInfo);
127
82
  this.dependencyMap = (0, _workspacetools.createDependencyMap)(this.packageInfos, {
@@ -142,9 +97,6 @@ class TargetHasher {
142
97
  if (this.logger !== undefined) {
143
98
  const globalInputsHash = (0, _hashStrings.hashStrings)(Object.values(this.globalInputsHash ?? {}));
144
99
  this.logger.verbose(`Global inputs hash: ${globalInputsHash}`);
145
- // Log global input hashs to log file
146
- const globalInputsHashJson = JSON.stringify(this.globalInputsHash, null, 2);
147
- this.logger.silly(globalInputsHashJson);
148
100
  }
149
101
  }
150
102
  async hash(target) {
@@ -154,7 +106,7 @@ class TargetHasher {
154
106
  if (!target.inputs) {
155
107
  throw new Error("Root-level targets must have `inputs` defined if it has cache enabled.");
156
108
  }
157
- const files = await (0, _fastglob.default)(target.inputs, {
109
+ const files = await (0, _globby.globAsync)(target.inputs, {
158
110
  cwd: root
159
111
  });
160
112
  const fileFashes = (0, _globhasher.hash)(files, {
@@ -178,22 +130,14 @@ class TargetHasher {
178
130
  ...internalDeps,
179
131
  ...externalDeps
180
132
  ].sort();
181
- const inputs = target.inputs ?? [
182
- "**/*"
183
- ];
184
- const packagePatterns = this.expandInputPatterns(inputs, target);
185
- const files = [];
186
- for (const [pkg, patterns] of Object.entries(packagePatterns)){
187
- const packageFiles = this.packageTree.getPackageFiles(pkg, patterns);
188
- files.push(...packageFiles);
189
- }
133
+ const files = (0, _getInputFiles.getInputFiles)(target, this.dependencyMap, this.packageTree);
190
134
  const fileHashes = this.fileHasher.hash(files) ?? {}; // this list is sorted by file name
191
135
  // get target hashes
192
136
  const targetDepHashes = target.dependencies?.sort().map((targetDep)=>this.targetHashes[targetDep]);
193
- const envGlobFiles = Object.values(target.environmentGlob ? this.fileHasher.hash(await this.getMemorizedEnvGlobResults(target.environmentGlob)) : this.globalInputsHash ?? {});
137
+ const globalFileHashes = await this.getEnvironmentGlobHashes(root, target);
194
138
  const combinedHashes = [
195
139
  // Environmental hashes
196
- ...envGlobFiles,
140
+ ...Object.values(globalFileHashes),
197
141
  `${target.id}|${JSON.stringify(this.options.cliArgs)}`,
198
142
  this.options.cacheKey || "",
199
143
  // File content hashes based on target.inputs
@@ -204,13 +148,40 @@ class TargetHasher {
204
148
  ].filter(Boolean);
205
149
  const hashString = (0, _hashStrings.hashStrings)(combinedHashes);
206
150
  this.targetHashes[target.id] = hashString;
151
+ this.targetHashesLog[target.id] = {
152
+ fileHashes,
153
+ globalFileHashes
154
+ };
207
155
  return hashString;
208
156
  }
157
+ writeTargetHashesManifest() {
158
+ for (const [id, { fileHashes , globalFileHashes }] of Object.entries(this.targetHashesLog)){
159
+ const targetHashesManifestPath = _path.default.join(this.targetHashesDirectory, `${id}.json`);
160
+ if (!_fs.default.existsSync(_path.default.dirname(targetHashesManifestPath))) {
161
+ _fs.default.mkdirSync(_path.default.dirname(targetHashesManifestPath), {
162
+ recursive: true
163
+ });
164
+ }
165
+ _fs.default.writeFileSync(targetHashesManifestPath, JSON.stringify({
166
+ fileHashes,
167
+ globalFileHashes
168
+ }), "utf-8");
169
+ }
170
+ }
171
+ async getEnvironmentGlobHashes(root, target) {
172
+ const globalFileHashes = target.environmentGlob ? this.fileHasher.hash(await (0, _globby.globAsync)(target.environmentGlob ?? [], {
173
+ cwd: root
174
+ })) : this.globalInputsHash ?? {};
175
+ return globalFileHashes;
176
+ }
209
177
  async cleanup() {
178
+ this.writeTargetHashesManifest();
210
179
  await this.fileHasher.writeManifest();
211
180
  }
212
181
  constructor(options){
213
182
  _define_property(this, "options", void 0);
183
+ _define_property(this, "targetHashesLog", void 0);
184
+ _define_property(this, "targetHashesDirectory", void 0);
214
185
  _define_property(this, "logger", void 0);
215
186
  _define_property(this, "fileHasher", void 0);
216
187
  _define_property(this, "packageTree", void 0);
@@ -221,19 +192,24 @@ class TargetHasher {
221
192
  _define_property(this, "lockInfo", void 0);
222
193
  _define_property(this, "targetHashes", void 0);
223
194
  _define_property(this, "dependencyMap", void 0);
224
- _define_property(this, "memoizedEnvGlobResults", void 0);
225
195
  this.options = options;
196
+ this.targetHashesLog = {};
226
197
  this.packageInfos = {};
227
198
  this.targetHashes = {};
228
199
  this.dependencyMap = {
229
200
  dependencies: new Map(),
230
201
  dependents: new Map()
231
202
  };
232
- this.memoizedEnvGlobResults = new Map();
233
203
  const { root , logger } = options;
234
204
  this.logger = logger;
235
205
  this.fileHasher = new _FileHasher.FileHasher({
236
206
  root
237
207
  });
208
+ this.targetHashesDirectory = _path.default.join(root, "node_modules", ".cache", "lage", "hashes");
209
+ if (!_fs.default.existsSync(this.targetHashesDirectory)) {
210
+ _fs.default.mkdirSync(this.targetHashesDirectory, {
211
+ recursive: true
212
+ });
213
+ }
238
214
  }
239
215
  }
@@ -36,6 +36,25 @@ describe("The main Hasher class", ()=>{
36
36
  task
37
37
  };
38
38
  }
39
+ it("creates different hashes given different global environment glob", async ()=>{
40
+ const monorepo1 = await setupFixture("monorepo-with-global-files");
41
+ const hasher = new _index.TargetHasher({
42
+ root: monorepo1.root,
43
+ environmentGlob: [
44
+ "some-global.config.js"
45
+ ]
46
+ });
47
+ const target = createTarget(monorepo1.root, "package-a", "build");
48
+ const hash = await getHash(hasher, target);
49
+ const hasher2 = new _index.TargetHasher({
50
+ root: monorepo1.root,
51
+ environmentGlob: []
52
+ });
53
+ const target2 = createTarget(monorepo1.root, "package-a", "build");
54
+ const hash2 = await getHash(hasher2, target2);
55
+ expect(hash).not.toEqual(hash2);
56
+ monorepo1.cleanup();
57
+ });
39
58
  it("creates different hashes given different fixtures", async ()=>{
40
59
  const monorepo1 = await setupFixture("monorepo");
41
60
  const hasher = new _index.TargetHasher({
@@ -0,0 +1,3 @@
1
+ import { type Target } from "@lage-run/target-graph";
2
+ import { type DependencyMap } from "workspace-tools";
3
+ export declare function expandInputPatterns(patterns: string[], target: Target, dependencyMap: DependencyMap): Record<string, string[]>;
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "expandInputPatterns", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return expandInputPatterns;
9
+ }
10
+ });
11
+ function expandInputPatterns(patterns, target, dependencyMap) {
12
+ const expandedPatterns = {};
13
+ for (const pattern of patterns){
14
+ if (pattern.startsWith("^") || pattern.startsWith("!^")) {
15
+ const matchPattern = pattern.replace("^", "");
16
+ // get all the packages that are transitive deps and add them to the list
17
+ const queue = [
18
+ target.packageName
19
+ ];
20
+ const visited = new Set();
21
+ while(queue.length > 0){
22
+ const pkg = queue.pop();
23
+ if (visited.has(pkg)) {
24
+ continue;
25
+ }
26
+ visited.add(pkg);
27
+ if (pkg !== target.packageName) {
28
+ expandedPatterns[pkg] = expandedPatterns[pkg] ?? [];
29
+ expandedPatterns[pkg].push(matchPattern);
30
+ }
31
+ if (dependencyMap.dependencies.has(pkg)) {
32
+ const deps = dependencyMap.dependencies.get(pkg);
33
+ if (deps) {
34
+ for (const dep of deps){
35
+ queue.push(dep);
36
+ }
37
+ }
38
+ }
39
+ }
40
+ } else {
41
+ const pkg = target.packageName;
42
+ expandedPatterns[pkg] = expandedPatterns[pkg] ?? [];
43
+ expandedPatterns[pkg].push(pattern);
44
+ }
45
+ }
46
+ return expandedPatterns;
47
+ }
@@ -0,0 +1,4 @@
1
+ import { type Target } from "@lage-run/target-graph";
2
+ import { type DependencyMap } from "workspace-tools";
3
+ import { type PackageTree } from "./PackageTree.js";
4
+ export declare function getInputFiles(target: Target, dependencyMap: DependencyMap, packageTree: PackageTree): string[];
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "getInputFiles", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return getInputFiles;
9
+ }
10
+ });
11
+ const _expandInputPatterns = require("./expandInputPatterns.js");
12
+ function getInputFiles(target, dependencyMap, packageTree) {
13
+ const inputs = target.inputs ?? [
14
+ "**/*"
15
+ ];
16
+ const packagePatterns = (0, _expandInputPatterns.expandInputPatterns)(inputs, target, dependencyMap);
17
+ const files = [];
18
+ for (const [pkg, patterns] of Object.entries(packagePatterns)){
19
+ const packageFiles = packageTree.getPackageFiles(pkg, patterns);
20
+ files.push(...packageFiles);
21
+ }
22
+ return files;
23
+ }
package/lib/index.d.ts CHANGED
@@ -1 +1,3 @@
1
1
  export { TargetHasher } from "./TargetHasher.js";
2
+ export { PackageTree } from "./PackageTree.js";
3
+ export { getInputFiles } from "./getInputFiles.js";
package/lib/index.js CHANGED
@@ -2,10 +2,23 @@
2
2
  Object.defineProperty(exports, "__esModule", {
3
3
  value: true
4
4
  });
5
- Object.defineProperty(exports, "TargetHasher", {
6
- enumerable: true,
7
- get: function() {
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: all[name]
9
+ });
10
+ }
11
+ _export(exports, {
12
+ TargetHasher: function() {
8
13
  return _TargetHasher.TargetHasher;
14
+ },
15
+ PackageTree: function() {
16
+ return _PackageTree.PackageTree;
17
+ },
18
+ getInputFiles: function() {
19
+ return _getInputFiles.getInputFiles;
9
20
  }
10
21
  });
11
22
  const _TargetHasher = require("./TargetHasher.js");
23
+ const _PackageTree = require("./PackageTree.js");
24
+ const _getInputFiles = require("./getInputFiles.js");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lage-run/hasher",
3
- "version": "1.3.4",
3
+ "version": "1.5.0",
4
4
  "description": "Hasher for Lage Targets",
5
5
  "repository": {
6
6
  "url": "https://github.com/microsoft/lage"
@@ -15,10 +15,10 @@
15
15
  "lint": "monorepo-scripts lint"
16
16
  },
17
17
  "dependencies": {
18
+ "@lage-run/globby": "^14.1.0",
18
19
  "@lage-run/logger": "^1.3.1",
19
20
  "@lage-run/target-graph": "^0.8.10",
20
21
  "execa": "5.1.1",
21
- "fast-glob": "3.3.2",
22
22
  "glob-hasher": "^1.4.2",
23
23
  "graceful-fs": "4.2.11",
24
24
  "micromatch": "4.0.8",