nx 20.0.0-canary.20240926-529ab94 → 20.0.0-canary.20241001-8fa7065

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,449 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.NodeTaskHasherImpl = void 0;
4
- const find_project_for_path_1 = require("../project-graph/utils/find-project-for-path");
5
- const file_hasher_1 = require("./file-hasher");
6
- const utils_1 = require("../tasks-runner/utils");
7
- const workspace_root_1 = require("../utils/workspace-root");
8
- const minimatch_1 = require("minimatch");
9
- const path_1 = require("path");
10
- const native_1 = require("../native");
11
- const project_graph_utils_1 = require("../utils/project-graph-utils");
12
- const find_matching_projects_1 = require("../utils/find-matching-projects");
13
- const child_process_1 = require("child_process");
14
- const task_hasher_1 = require("./task-hasher");
15
- const hasher_1 = require("../plugins/js/hasher/hasher");
16
- class NodeTaskHasherImpl {
17
- constructor(nxJson, legacyRuntimeInputs, legacyFilesetInputs, projectFileMap, allWorkspaceFiles, projectGraph, options) {
18
- this.nxJson = nxJson;
19
- this.legacyRuntimeInputs = legacyRuntimeInputs;
20
- this.legacyFilesetInputs = legacyFilesetInputs;
21
- this.projectFileMap = projectFileMap;
22
- this.allWorkspaceFiles = allWorkspaceFiles;
23
- this.projectGraph = projectGraph;
24
- this.options = options;
25
- this.filesetHashes = {};
26
- this.runtimeHashes = {};
27
- this.externalDependencyHashes = new Map();
28
- this.projectRootMappings = (0, find_project_for_path_1.createProjectRootMappings)(this.projectGraph.nodes);
29
- // External Dependencies are all calculated up front in a deterministic order
30
- this.calculateExternalDependencyHashes();
31
- }
32
- hashTasks(tasks, taskGraph, env) {
33
- return Promise.all(tasks.map((t) => this.hashTask(t, taskGraph, env, [])));
34
- }
35
- async hashTask(task, taskGraph, env, visited = []) {
36
- return Promise.resolve().then(async () => {
37
- const { selfInputs, depsInputs, depsOutputs, projectInputs } = (0, task_hasher_1.getInputs)(task, this.projectGraph, this.nxJson);
38
- const selfAndInputs = await this.hashSelfAndDepsInputs(task.target.project, task, selfInputs, depsInputs, depsOutputs, projectInputs, taskGraph, env, visited);
39
- const target = this.hashTarget(task.target.project, task.target.target, selfInputs);
40
- if (target) {
41
- return this.combinePartialHashes([selfAndInputs, target]);
42
- }
43
- return selfAndInputs;
44
- });
45
- }
46
- async hashNamedInputForDependencies(projectName, task, namedInput, taskGraph, env, visited) {
47
- const projectNode = this.projectGraph.nodes[projectName];
48
- const namedInputs = {
49
- default: [{ fileset: '{projectRoot}/**/*' }],
50
- ...this.nxJson.namedInputs,
51
- ...projectNode.data.namedInputs,
52
- };
53
- const expandedInputs = (0, task_hasher_1.expandNamedInput)(namedInput, namedInputs);
54
- const selfInputs = expandedInputs.filter(task_hasher_1.isSelfInput);
55
- const depsOutputs = expandedInputs.filter(task_hasher_1.isDepsOutput);
56
- const depsInputs = [{ input: namedInput, dependencies: true }]; // true is boolean by default
57
- return this.hashSelfAndDepsInputs(projectName, task, selfInputs, depsInputs, depsOutputs, [], taskGraph, env, visited);
58
- }
59
- async hashSelfAndDepsInputs(projectName, task, selfInputs, depsInputs, depsOutputs, projectInputs, taskGraph, env, visited) {
60
- const projectGraphDeps = this.projectGraph.dependencies[projectName] ?? [];
61
- // we don't want random order of dependencies to change the hash
62
- projectGraphDeps.sort((a, b) => a.target.localeCompare(b.target));
63
- const self = await this.hashSingleProjectInputs(projectName, selfInputs, env);
64
- const deps = await this.hashDepsInputs(task, depsInputs, projectGraphDeps, taskGraph, env, visited);
65
- const depsOut = await this.hashDepsOutputs(task, depsOutputs, taskGraph);
66
- const projects = await this.hashProjectInputs(projectInputs, env);
67
- return this.combinePartialHashes([
68
- ...self,
69
- ...deps,
70
- ...projects,
71
- ...depsOut,
72
- ]);
73
- }
74
- combinePartialHashes(partialHashes) {
75
- if (partialHashes.length === 1) {
76
- return partialHashes[0];
77
- }
78
- const details = {};
79
- const hashValues = [];
80
- for (const partial of partialHashes) {
81
- hashValues.push(partial.value);
82
- Object.assign(details, partial.details);
83
- }
84
- const value = (0, file_hasher_1.hashArray)(hashValues);
85
- return { value, details };
86
- }
87
- async hashDepsInputs(task, inputs, projectGraphDeps, taskGraph, env, visited) {
88
- return (await Promise.all(inputs.map(async (input) => {
89
- return await Promise.all(projectGraphDeps.map(async (d) => {
90
- if (visited.indexOf(d.target) > -1) {
91
- return null;
92
- }
93
- else {
94
- visited.push(d.target);
95
- if (this.projectGraph.nodes[d.target]) {
96
- return await this.hashNamedInputForDependencies(d.target, task, input.input || 'default', taskGraph, env, visited);
97
- }
98
- else {
99
- return this.getExternalDependencyHash(d.target);
100
- }
101
- }
102
- }));
103
- })))
104
- .flat()
105
- .filter((r) => !!r);
106
- }
107
- async hashDepsOutputs(task, depsOutputs, taskGraph) {
108
- if (depsOutputs.length === 0) {
109
- return [];
110
- }
111
- const result = [];
112
- for (const { dependentTasksOutputFiles, transitive } of depsOutputs) {
113
- result.push(...(await this.hashDepOuputs(task, dependentTasksOutputFiles, taskGraph, transitive)));
114
- }
115
- return result;
116
- }
117
- async hashDepOuputs(task, dependentTasksOutputFiles, taskGraph, transitive) {
118
- // task has no dependencies
119
- if (!taskGraph.dependencies[task.id]) {
120
- return [];
121
- }
122
- const partialHashes = [];
123
- for (const d of taskGraph.dependencies[task.id]) {
124
- const childTask = taskGraph.tasks[d];
125
- const outputs = (0, utils_1.getOutputsForTargetAndConfiguration)(childTask.target, childTask.overrides, this.projectGraph.nodes[childTask.target.project]);
126
- const { getFilesForOutputs } = require('../native');
127
- const outputFiles = getFilesForOutputs(workspace_root_1.workspaceRoot, outputs);
128
- const filteredFiles = outputFiles.filter((p) => p === dependentTasksOutputFiles ||
129
- (0, minimatch_1.minimatch)(p, dependentTasksOutputFiles, { dot: true }));
130
- const hashDetails = {};
131
- const hashes = [];
132
- for (const [file, hash] of this.hashFiles(filteredFiles.map((p) => (0, path_1.join)(workspace_root_1.workspaceRoot, p)))) {
133
- hashes.push(hash);
134
- }
135
- let hash = (0, file_hasher_1.hashArray)(hashes);
136
- partialHashes.push({
137
- value: hash,
138
- details: {
139
- [`${dependentTasksOutputFiles}:${outputs.join(',')}`]: hash,
140
- },
141
- });
142
- if (transitive) {
143
- partialHashes.push(...(await this.hashDepOuputs(childTask, dependentTasksOutputFiles, taskGraph, transitive)));
144
- }
145
- }
146
- return partialHashes;
147
- }
148
- hashFiles(files) {
149
- const r = new Map();
150
- for (let f of files) {
151
- r.set(f, (0, native_1.hashFile)(f));
152
- }
153
- return r;
154
- }
155
- getExternalDependencyHash(externalNodeName) {
156
- const combinedHash = this.combinePartialHashes(this.externalDependencyHashes.get(externalNodeName));
157
- // Set the combined hash into the hashes so it's not recalculated next time
158
- this.externalDependencyHashes.set(externalNodeName, [combinedHash]);
159
- return combinedHash;
160
- }
161
- hashSingleExternalDependency(externalNodeName) {
162
- const node = this.projectGraph.externalNodes[externalNodeName];
163
- if (node.data.hash) {
164
- // we already know the hash of this dependency
165
- return {
166
- value: node.data.hash,
167
- details: {
168
- [externalNodeName]: node.data.hash,
169
- },
170
- };
171
- }
172
- else {
173
- // we take version as a hash
174
- return {
175
- value: node.data.version,
176
- details: {
177
- [externalNodeName]: node.data.version,
178
- },
179
- };
180
- }
181
- }
182
- hashExternalDependency(externalNodeName) {
183
- const partialHashes = new Set();
184
- partialHashes.add(this.hashSingleExternalDependency(externalNodeName));
185
- const deps = (0, project_graph_utils_1.findAllProjectNodeDependencies)(externalNodeName, this.projectGraph, true);
186
- for (const dep of deps) {
187
- partialHashes.add(this.hashSingleExternalDependency(dep));
188
- }
189
- return Array.from(partialHashes);
190
- }
191
- hashTarget(projectName, targetName, selfInputs) {
192
- const projectNode = this.projectGraph.nodes[projectName];
193
- const target = projectNode.data.targets[targetName];
194
- if (!target) {
195
- return;
196
- }
197
- let hash;
198
- // we can only vouch for @nx packages's executor dependencies
199
- // if it's "run commands" or third-party we skip traversing since we have no info what this command depends on
200
- if (target.executor.startsWith(`@nrwl/`) ||
201
- target.executor.startsWith(`@nx/`)) {
202
- const executorPackage = target.executor.split(':')[0];
203
- const executorNodeName = this.findExternalDependencyNodeName(executorPackage);
204
- // This is either a local plugin or a non-existent executor
205
- if (!executorNodeName) {
206
- // TODO: This should not return null if it is a local plugin's executor
207
- return null;
208
- }
209
- return this.getExternalDependencyHash(executorNodeName);
210
- }
211
- else {
212
- // use command external dependencies if available to construct the hash
213
- const partialHashes = [];
214
- let hasCommandExternalDependencies = false;
215
- for (const input of selfInputs) {
216
- if (input['externalDependencies']) {
217
- // if we have externalDependencies with empty array we still want to override the default hash
218
- hasCommandExternalDependencies = true;
219
- const externalDependencies = input['externalDependencies'];
220
- for (let dep of externalDependencies) {
221
- dep = this.findExternalDependencyNodeName(dep);
222
- if (!dep) {
223
- throw new Error(`The externalDependency "${dep}" for "${projectName}:${targetName}" could not be found`);
224
- }
225
- partialHashes.push(this.getExternalDependencyHash(dep));
226
- }
227
- }
228
- }
229
- if (hasCommandExternalDependencies) {
230
- return this.combinePartialHashes(partialHashes);
231
- }
232
- else {
233
- // cache the hash of the entire external dependencies tree
234
- if (this.allExternalDependenciesHash) {
235
- return this.allExternalDependenciesHash;
236
- }
237
- else {
238
- hash = (0, file_hasher_1.hashObject)(this.projectGraph.externalNodes);
239
- this.allExternalDependenciesHash = {
240
- value: hash,
241
- details: {
242
- AllExternalDependencies: hash,
243
- },
244
- };
245
- return this.allExternalDependenciesHash;
246
- }
247
- }
248
- }
249
- }
250
- findExternalDependencyNodeName(packageName) {
251
- if (this.projectGraph.externalNodes[packageName]) {
252
- return packageName;
253
- }
254
- if (this.projectGraph.externalNodes[`npm:${packageName}`]) {
255
- return `npm:${packageName}`;
256
- }
257
- for (const node of Object.values(this.projectGraph.externalNodes)) {
258
- if (node.data.packageName === packageName) {
259
- return node.name;
260
- }
261
- }
262
- // not found
263
- return null;
264
- }
265
- async hashSingleProjectInputs(projectName, inputs, env) {
266
- const filesets = (0, task_hasher_1.extractPatternsFromFileSets)(inputs);
267
- const projectFilesets = [];
268
- const workspaceFilesets = [];
269
- let invalidFilesetNoPrefix = null;
270
- let invalidFilesetWorkspaceRootNegative = null;
271
- for (let f of filesets) {
272
- if (f.startsWith('{projectRoot}/') || f.startsWith('!{projectRoot}/')) {
273
- projectFilesets.push(f);
274
- }
275
- else if (f.startsWith('{workspaceRoot}/') ||
276
- f.startsWith('!{workspaceRoot}/')) {
277
- workspaceFilesets.push(f);
278
- }
279
- else {
280
- invalidFilesetNoPrefix = f;
281
- }
282
- }
283
- if (invalidFilesetNoPrefix) {
284
- throw new Error([
285
- `"${invalidFilesetNoPrefix}" is an invalid fileset.`,
286
- 'All filesets have to start with either {workspaceRoot} or {projectRoot}.',
287
- 'For instance: "!{projectRoot}/**/*.spec.ts" or "{workspaceRoot}/package.json".',
288
- `If "${invalidFilesetNoPrefix}" is a named input, make sure it is defined in, for instance, nx.json.`,
289
- ].join('\n'));
290
- }
291
- if (invalidFilesetWorkspaceRootNegative) {
292
- throw new Error([
293
- `"${invalidFilesetWorkspaceRootNegative}" is an invalid fileset.`,
294
- 'It is not possible to negative filesets starting with {workspaceRoot}.',
295
- ].join('\n'));
296
- }
297
- const notFilesets = inputs.filter((r) => !r['fileset']);
298
- return Promise.all([
299
- this.hashProjectFileset(projectName, projectFilesets),
300
- this.hashProjectConfig(projectName),
301
- this.hashTsConfig(projectName),
302
- ...(workspaceFilesets.length
303
- ? [this.hashRootFilesets(workspaceFilesets)]
304
- : []),
305
- this.hashRootFilesets(this.legacyFilesetInputs.map((r) => r.fileset)),
306
- ...[...notFilesets, ...this.legacyRuntimeInputs].map((r) => r['runtime']
307
- ? this.hashRuntime(env, r['runtime'])
308
- : this.hashEnv(env, r['env'])),
309
- ]);
310
- }
311
- async hashProjectInputs(projectInputs, env) {
312
- const partialHashes = [];
313
- for (const input of projectInputs) {
314
- const projects = (0, find_matching_projects_1.findMatchingProjects)(input.projects, this.projectGraph.nodes);
315
- for (const project of projects) {
316
- const namedInputs = (0, task_hasher_1.getNamedInputs)(this.nxJson, this.projectGraph.nodes[project]);
317
- const expandedInput = (0, task_hasher_1.expandSingleProjectInputs)([{ input: input.input }], namedInputs);
318
- partialHashes.push(this.hashSingleProjectInputs(project, expandedInput, env));
319
- }
320
- }
321
- return Promise.all(partialHashes).then((hashes) => hashes.flat());
322
- }
323
- async hashRootFilesets(filesets) {
324
- const mapKey = `workspace:[${filesets.join(',')}]`;
325
- if (!this.filesetHashes[mapKey]) {
326
- this.filesetHashes[mapKey] = new Promise(async (res) => {
327
- const parts = [];
328
- const negativePatterns = [];
329
- const positivePatterns = [];
330
- for (const fileset of filesets) {
331
- if (fileset.startsWith('!')) {
332
- negativePatterns.push(fileset.substring(17));
333
- }
334
- else {
335
- positivePatterns.push(fileset.substring(16));
336
- }
337
- }
338
- for (const fileset of positivePatterns) {
339
- const withoutWorkspaceRoot = fileset;
340
- // Used to shortcut minimatch if not necessary
341
- const matchingFile = this.allWorkspaceFiles.find((t) => t.file === withoutWorkspaceRoot);
342
- // shortcut because there is a direct match
343
- if (matchingFile) {
344
- if (!negativePatterns.some((p) => (0, minimatch_1.minimatch)(matchingFile.file, p))) {
345
- parts.push(matchingFile.hash);
346
- }
347
- // No direct match, check if pattern matched
348
- }
349
- else {
350
- this.allWorkspaceFiles
351
- .filter((f) => (0, minimatch_1.minimatch)(f.file, withoutWorkspaceRoot) &&
352
- !negativePatterns.some((p) => (0, minimatch_1.minimatch)(f.file, p)))
353
- .forEach((f) => {
354
- parts.push(f.hash);
355
- });
356
- }
357
- }
358
- const value = (0, file_hasher_1.hashArray)(parts);
359
- res({
360
- value,
361
- details: { [mapKey]: value },
362
- });
363
- });
364
- }
365
- return this.filesetHashes[mapKey];
366
- }
367
- hashProjectConfig(projectName) {
368
- const p = this.projectGraph.nodes[projectName];
369
- const projectConfig = (0, file_hasher_1.hashArray)([
370
- JSON.stringify({ ...p.data, files: undefined }),
371
- ]);
372
- return {
373
- value: projectConfig,
374
- details: {
375
- [`${projectName}:ProjectConfiguration`]: projectConfig,
376
- },
377
- };
378
- }
379
- hashTsConfig(projectName) {
380
- const p = this.projectGraph.nodes[projectName];
381
- const tsConfig = (0, file_hasher_1.hashArray)([
382
- (0, hasher_1.hashTsConfig)(p, this.projectRootMappings, this.options),
383
- ]);
384
- return {
385
- value: tsConfig,
386
- details: {
387
- [`${projectName}:TsConfig`]: tsConfig,
388
- },
389
- };
390
- }
391
- async hashProjectFileset(projectName, filesetPatterns) {
392
- const mapKey = `${projectName}:${filesetPatterns.join(',')}`;
393
- if (!this.filesetHashes[mapKey]) {
394
- this.filesetHashes[mapKey] = new Promise(async (res) => {
395
- const p = this.projectGraph.nodes[projectName];
396
- const filteredFiles = (0, task_hasher_1.filterUsingGlobPatterns)(p.data.root, this.projectFileMap[projectName] || [], filesetPatterns);
397
- const files = [];
398
- for (const { file, hash } of filteredFiles) {
399
- files.push(file, hash);
400
- }
401
- const value = (0, file_hasher_1.hashArray)(files);
402
- res({
403
- value,
404
- details: { [mapKey]: value },
405
- });
406
- });
407
- }
408
- return this.filesetHashes[mapKey];
409
- }
410
- async hashRuntime(env, runtime) {
411
- const env_key = JSON.stringify(env);
412
- const mapKey = `runtime:${runtime}-${env_key}`;
413
- if (!this.runtimeHashes[mapKey]) {
414
- this.runtimeHashes[mapKey] = new Promise((res, rej) => {
415
- (0, child_process_1.exec)(runtime, {
416
- windowsHide: true,
417
- cwd: workspace_root_1.workspaceRoot,
418
- env,
419
- }, (err, stdout, stderr) => {
420
- if (err) {
421
- rej(new Error(`Nx failed to execute {runtime: '${runtime}'}. ${err}.`));
422
- }
423
- else {
424
- const value = (0, file_hasher_1.hashArray)([`${stdout}${stderr}`.trim()]);
425
- res({
426
- details: { [`runtime:${runtime}`]: value },
427
- value,
428
- });
429
- }
430
- });
431
- });
432
- }
433
- return this.runtimeHashes[mapKey];
434
- }
435
- async hashEnv(env, envVarName) {
436
- const value = (0, file_hasher_1.hashArray)([env[envVarName] ?? '']);
437
- return {
438
- details: { [`env:${envVarName}`]: value },
439
- value,
440
- };
441
- }
442
- calculateExternalDependencyHashes() {
443
- const keys = Object.keys(this.projectGraph.externalNodes);
444
- for (const externalNodeName of keys) {
445
- this.externalDependencyHashes.set(externalNodeName, this.hashExternalDependency(externalNodeName));
446
- }
447
- }
448
- }
449
- exports.NodeTaskHasherImpl = NodeTaskHasherImpl;