Experimental TaskHashPlanInspector (#27809)
## Current Behavior
<!-- This is the behavior we have today -->
There is no easy way to inspect the hash plan for a task.
## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->
There is a `TaskHashPlanInspector` which can be used to provide details
about the hash plan for a task.
## Example Usage
```js
const { createProjectGraphAsync } = require('@nx/devkit');
const { HashPlanInspector } = require('nx/src/hasher/hash-plan-inspector');
(async () => {
const graph = await createProjectGraphAsync();
const hashPlanInspector = new HashPlanInspector(graph);
await hashPlanInspector.init();
const target = {
project: 'nx',
target: 'build-native',
};
console.log(
JSON.stringify(hashPlanInspector.inspectTask(target), null, 2)
);
})();
```
## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->
Fixes #
---------
Co-authored-by: Craigory Coppola <craigorycoppola@gmail.com>
This commit is contained in:
parent
61eb47f0d3
commit
92d9d13da4
479
packages/nx/src/hasher/hash-plan-inspector.spec.ts
Normal file
479
packages/nx/src/hasher/hash-plan-inspector.spec.ts
Normal file
@ -0,0 +1,479 @@
|
|||||||
|
import { HashPlanInspector } from './hash-plan-inspector';
|
||||||
|
import { ProjectGraph } from '../config/project-graph';
|
||||||
|
import { TempFs } from '../internal-testing-utils/temp-fs';
|
||||||
|
import { ProjectGraphBuilder } from '../project-graph/project-graph-builder';
|
||||||
|
|
||||||
|
describe('HashPlanInspector', () => {
|
||||||
|
let tempFs: TempFs;
|
||||||
|
let inspector: HashPlanInspector;
|
||||||
|
let projectGraph: ProjectGraph;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
tempFs = new TempFs('hash-plan-inspector');
|
||||||
|
|
||||||
|
// Create a minimal workspace structure
|
||||||
|
await tempFs.createFiles({
|
||||||
|
'package.json': JSON.stringify({
|
||||||
|
name: 'test-workspace',
|
||||||
|
devDependencies: {
|
||||||
|
nx: '0.0.0',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
'nx.json': JSON.stringify({
|
||||||
|
extends: 'nx/presets/npm.json',
|
||||||
|
targetDefaults: {
|
||||||
|
build: {
|
||||||
|
cache: true,
|
||||||
|
},
|
||||||
|
test: {
|
||||||
|
cache: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
namedInputs: {
|
||||||
|
default: ['{projectRoot}/**/*'],
|
||||||
|
production: ['default', '!{projectRoot}/**/*.spec.ts'],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
'apps/test-app/project.json': JSON.stringify({
|
||||||
|
name: 'test-app',
|
||||||
|
sourceRoot: 'apps/test-app/src',
|
||||||
|
targets: {
|
||||||
|
build: {
|
||||||
|
executor: '@nx/webpack:webpack',
|
||||||
|
options: {
|
||||||
|
outputPath: 'dist/apps/test-app',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
test: {
|
||||||
|
executor: '@nx/jest:jest',
|
||||||
|
options: {
|
||||||
|
jestConfig: 'apps/test-app/jest.config.ts',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
'apps/test-app/src/main.ts': 'console.log("Hello from test-app");',
|
||||||
|
'libs/test-lib/project.json': JSON.stringify({
|
||||||
|
name: 'test-lib',
|
||||||
|
sourceRoot: 'libs/test-lib/src',
|
||||||
|
targets: {
|
||||||
|
build: {
|
||||||
|
executor: '@nx/rollup:rollup',
|
||||||
|
options: {
|
||||||
|
outputPath: 'dist/libs/test-lib',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
test: {
|
||||||
|
executor: '@nx/jest:jest',
|
||||||
|
options: {
|
||||||
|
jestConfig: 'libs/test-lib/jest.config.ts',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
'libs/test-lib/src/index.ts': 'export const lib = "test-lib";',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Build project graph
|
||||||
|
const builder = new ProjectGraphBuilder();
|
||||||
|
|
||||||
|
builder.addNode({
|
||||||
|
name: 'test-app',
|
||||||
|
type: 'app',
|
||||||
|
data: {
|
||||||
|
root: 'apps/test-app',
|
||||||
|
sourceRoot: 'apps/test-app/src',
|
||||||
|
targets: {
|
||||||
|
build: {
|
||||||
|
executor: '@nx/webpack:webpack',
|
||||||
|
options: {
|
||||||
|
outputPath: 'dist/apps/test-app',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
test: {
|
||||||
|
executor: '@nx/jest:jest',
|
||||||
|
options: {
|
||||||
|
jestConfig: 'apps/test-app/jest.config.ts',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.addNode({
|
||||||
|
name: 'test-lib',
|
||||||
|
type: 'lib',
|
||||||
|
data: {
|
||||||
|
root: 'libs/test-lib',
|
||||||
|
sourceRoot: 'libs/test-lib/src',
|
||||||
|
targets: {
|
||||||
|
build: {
|
||||||
|
executor: '@nx/rollup:rollup',
|
||||||
|
options: {
|
||||||
|
outputPath: 'dist/libs/test-lib',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
test: {
|
||||||
|
executor: '@nx/jest:jest',
|
||||||
|
options: {
|
||||||
|
jestConfig: 'libs/test-lib/jest.config.ts',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.addImplicitDependency('test-app', 'test-lib');
|
||||||
|
|
||||||
|
projectGraph = builder.getUpdatedProjectGraph();
|
||||||
|
|
||||||
|
inspector = new HashPlanInspector(projectGraph, tempFs.tempDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
tempFs.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('inspectHashPlan', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await inspector.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should inspect hash plan for single project and target', () => {
|
||||||
|
const result = inspector.inspectHashPlan(['test-app'], ['build']);
|
||||||
|
|
||||||
|
// Should return a record mapping task IDs to arrays of hash instructions
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
expect(typeof result).toBe('object');
|
||||||
|
|
||||||
|
// Should only contain test-app:build (and not test-lib:build in this case)
|
||||||
|
expect(Object.keys(result)).toEqual(['test-app:build']);
|
||||||
|
|
||||||
|
const testAppPlan = result['test-app:build'];
|
||||||
|
expect(Array.isArray(testAppPlan)).toBe(true);
|
||||||
|
expect(testAppPlan.length).toBeGreaterThan(0);
|
||||||
|
|
||||||
|
// Should include nx.json
|
||||||
|
expect(testAppPlan).toContain('file:nx.json');
|
||||||
|
|
||||||
|
// Should include environment variable
|
||||||
|
expect(testAppPlan).toContain('env:NX_CLOUD_ENCRYPTION_KEY');
|
||||||
|
|
||||||
|
// Should include test-app files
|
||||||
|
expect(testAppPlan).toContain('file:apps/test-app/project.json');
|
||||||
|
expect(testAppPlan).toContain('file:apps/test-app/src/main.ts');
|
||||||
|
|
||||||
|
// Should include test-lib files (due to dependency)
|
||||||
|
expect(testAppPlan).toContain('file:libs/test-lib/project.json');
|
||||||
|
expect(testAppPlan).toContain('file:libs/test-lib/src/index.ts');
|
||||||
|
|
||||||
|
// Should include project configurations
|
||||||
|
expect(testAppPlan).toContain('test-app:ProjectConfiguration');
|
||||||
|
expect(testAppPlan).toContain('test-lib:ProjectConfiguration');
|
||||||
|
|
||||||
|
// Should include TypeScript configurations
|
||||||
|
expect(testAppPlan).toContain('test-app:TsConfig');
|
||||||
|
expect(testAppPlan).toContain('test-lib:TsConfig');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should inspect hash plan for multiple projects', () => {
|
||||||
|
const result = inspector.inspectHashPlan(
|
||||||
|
['test-app', 'test-lib'],
|
||||||
|
['build']
|
||||||
|
);
|
||||||
|
|
||||||
|
// Should have hash plans for both projects
|
||||||
|
expect(Object.keys(result).sort()).toEqual([
|
||||||
|
'test-app:build',
|
||||||
|
'test-lib:build',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Check test-app:build hash plan
|
||||||
|
const testAppPlan = result['test-app:build'];
|
||||||
|
expect(Array.isArray(testAppPlan)).toBe(true);
|
||||||
|
|
||||||
|
// test-app should include its own files
|
||||||
|
expect(testAppPlan).toContain('file:apps/test-app/project.json');
|
||||||
|
expect(testAppPlan).toContain('file:apps/test-app/src/main.ts');
|
||||||
|
|
||||||
|
// test-app should also include test-lib files (due to dependency)
|
||||||
|
expect(testAppPlan).toContain('file:libs/test-lib/project.json');
|
||||||
|
expect(testAppPlan).toContain('file:libs/test-lib/src/index.ts');
|
||||||
|
|
||||||
|
// Should include configurations for both projects
|
||||||
|
expect(testAppPlan).toContain('test-app:ProjectConfiguration');
|
||||||
|
expect(testAppPlan).toContain('test-lib:ProjectConfiguration');
|
||||||
|
expect(testAppPlan).toContain('test-app:TsConfig');
|
||||||
|
expect(testAppPlan).toContain('test-lib:TsConfig');
|
||||||
|
|
||||||
|
// Should include common files
|
||||||
|
expect(testAppPlan).toContain('file:nx.json');
|
||||||
|
expect(testAppPlan).toContain('env:NX_CLOUD_ENCRYPTION_KEY');
|
||||||
|
|
||||||
|
// Check test-lib:build hash plan
|
||||||
|
const testLibPlan = result['test-lib:build'];
|
||||||
|
expect(Array.isArray(testLibPlan)).toBe(true);
|
||||||
|
|
||||||
|
// test-lib should only include its own files (no dependencies)
|
||||||
|
expect(testLibPlan).toContain('file:libs/test-lib/project.json');
|
||||||
|
expect(testLibPlan).toContain('file:libs/test-lib/src/index.ts');
|
||||||
|
|
||||||
|
// Should not include test-app files
|
||||||
|
expect(testLibPlan).not.toContain('file:apps/test-app/project.json');
|
||||||
|
expect(testLibPlan).not.toContain('file:apps/test-app/src/main.ts');
|
||||||
|
|
||||||
|
// Should include only test-lib configurations
|
||||||
|
expect(testLibPlan).toContain('test-lib:ProjectConfiguration');
|
||||||
|
expect(testLibPlan).toContain('test-lib:TsConfig');
|
||||||
|
expect(testLibPlan).not.toContain('test-app:ProjectConfiguration');
|
||||||
|
expect(testLibPlan).not.toContain('test-app:TsConfig');
|
||||||
|
|
||||||
|
// Should include common files
|
||||||
|
expect(testLibPlan).toContain('file:nx.json');
|
||||||
|
expect(testLibPlan).toContain('env:NX_CLOUD_ENCRYPTION_KEY');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should inspect hash plan for multiple targets', () => {
|
||||||
|
const result = inspector.inspectHashPlan(['test-app'], ['build', 'test']);
|
||||||
|
|
||||||
|
expect(Object.keys(result)).toContain('test-app:build');
|
||||||
|
expect(Object.keys(result)).toContain('test-app:test');
|
||||||
|
expect(Array.isArray(result['test-app:build'])).toBe(true);
|
||||||
|
expect(Array.isArray(result['test-app:test'])).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle configuration parameter', () => {
|
||||||
|
const result = inspector.inspectHashPlan(
|
||||||
|
['test-app'],
|
||||||
|
['build'],
|
||||||
|
'production'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result['test-app:build']).toBeDefined();
|
||||||
|
expect(Array.isArray(result['test-app:build'])).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle overrides parameter', () => {
|
||||||
|
const overrides = { watch: true };
|
||||||
|
const result = inspector.inspectHashPlan(
|
||||||
|
['test-app'],
|
||||||
|
['build'],
|
||||||
|
undefined,
|
||||||
|
overrides
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result['test-app:build']).toBeDefined();
|
||||||
|
expect(Array.isArray(result['test-app:build'])).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle extraTargetDependencies parameter', () => {
|
||||||
|
const extraTargetDependencies = {
|
||||||
|
build: [{ target: 'test', projects: 'self' }],
|
||||||
|
};
|
||||||
|
const result = inspector.inspectHashPlan(
|
||||||
|
['test-app'],
|
||||||
|
['build'],
|
||||||
|
undefined,
|
||||||
|
{},
|
||||||
|
extraTargetDependencies
|
||||||
|
);
|
||||||
|
|
||||||
|
// Should include both build and test tasks due to extra dependency
|
||||||
|
expect(Object.keys(result)).toContain('test-app:build');
|
||||||
|
expect(Object.keys(result)).toContain('test-app:test');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle excludeTaskDependencies parameter', () => {
|
||||||
|
const result = inspector.inspectHashPlan(
|
||||||
|
['test-app'],
|
||||||
|
['build'],
|
||||||
|
undefined,
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result['test-app:build']).toBeDefined();
|
||||||
|
expect(Array.isArray(result['test-app:build'])).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle empty project names array', () => {
|
||||||
|
const result = inspector.inspectHashPlan([], ['build']);
|
||||||
|
|
||||||
|
expect(Object.keys(result)).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle empty targets array', () => {
|
||||||
|
const result = inspector.inspectHashPlan(['test-app'], []);
|
||||||
|
|
||||||
|
expect(Object.keys(result)).toHaveLength(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('inspectTask', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await inspector.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should inspect a single task', () => {
|
||||||
|
const target = { project: 'test-app', target: 'build' };
|
||||||
|
const result = inspector.inspectTask(target);
|
||||||
|
|
||||||
|
// Should only contain test-app:build
|
||||||
|
expect(Object.keys(result)).toEqual(['test-app:build']);
|
||||||
|
|
||||||
|
const testAppPlan = result['test-app:build'];
|
||||||
|
expect(Array.isArray(testAppPlan)).toBe(true);
|
||||||
|
|
||||||
|
// Should include test-app files
|
||||||
|
expect(testAppPlan).toContain('file:apps/test-app/project.json');
|
||||||
|
expect(testAppPlan).toContain('file:apps/test-app/src/main.ts');
|
||||||
|
|
||||||
|
// Should include test-lib files (due to dependency)
|
||||||
|
expect(testAppPlan).toContain('file:libs/test-lib/project.json');
|
||||||
|
expect(testAppPlan).toContain('file:libs/test-lib/src/index.ts');
|
||||||
|
|
||||||
|
// Should include common files
|
||||||
|
expect(testAppPlan).toContain('file:nx.json');
|
||||||
|
expect(testAppPlan).toContain('env:NX_CLOUD_ENCRYPTION_KEY');
|
||||||
|
|
||||||
|
// Should include configurations
|
||||||
|
expect(testAppPlan).toContain('test-app:TsConfig');
|
||||||
|
expect(testAppPlan).toContain('test-lib:TsConfig');
|
||||||
|
expect(testAppPlan).toContain('test-app:ProjectConfiguration');
|
||||||
|
expect(testAppPlan).toContain('test-lib:ProjectConfiguration');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should inspect task with configuration', () => {
|
||||||
|
const target = {
|
||||||
|
project: 'test-app',
|
||||||
|
target: 'build',
|
||||||
|
configuration: 'production',
|
||||||
|
};
|
||||||
|
const result = inspector.inspectTask(target);
|
||||||
|
|
||||||
|
expect(result['test-app:build']).toBeDefined();
|
||||||
|
expect(Array.isArray(result['test-app:build'])).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle parsed args parameter', () => {
|
||||||
|
const target = { project: 'test-app', target: 'build' };
|
||||||
|
const parsedArgs = { watch: true, verbose: true };
|
||||||
|
const result = inspector.inspectTask(target, parsedArgs);
|
||||||
|
|
||||||
|
expect(result['test-app:build']).toBeDefined();
|
||||||
|
expect(Array.isArray(result['test-app:build'])).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle extraTargetDependencies parameter', () => {
|
||||||
|
const target = { project: 'test-app', target: 'build' };
|
||||||
|
const extraTargetDependencies = {
|
||||||
|
build: [{ target: 'test', projects: 'self' }],
|
||||||
|
};
|
||||||
|
const result = inspector.inspectTask(target, {}, extraTargetDependencies);
|
||||||
|
|
||||||
|
// Should include both build and test tasks due to extra dependency
|
||||||
|
expect(Object.keys(result)).toContain('test-app:build');
|
||||||
|
expect(Object.keys(result)).toContain('test-app:test');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle excludeTaskDependencies parameter', () => {
|
||||||
|
const target = { project: 'test-app', target: 'build' };
|
||||||
|
const result = inspector.inspectTask(target, {}, {}, true);
|
||||||
|
|
||||||
|
expect(result['test-app:build']).toBeDefined();
|
||||||
|
expect(Array.isArray(result['test-app:build'])).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should inspect test target', () => {
|
||||||
|
const target = { project: 'test-app', target: 'test' };
|
||||||
|
const result = inspector.inspectTask(target);
|
||||||
|
|
||||||
|
expect(result['test-app:test']).toBeDefined();
|
||||||
|
expect(Array.isArray(result['test-app:test'])).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should inspect library project task', () => {
|
||||||
|
const target = { project: 'test-lib', target: 'build' };
|
||||||
|
const result = inspector.inspectTask(target);
|
||||||
|
|
||||||
|
expect(result['test-lib:build']).toBeDefined();
|
||||||
|
expect(Array.isArray(result['test-lib:build'])).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle complex parsed args with configuration override', () => {
|
||||||
|
const target = { project: 'test-app', target: 'build' };
|
||||||
|
const parsedArgs = {
|
||||||
|
configuration: 'development',
|
||||||
|
targets: ['build'],
|
||||||
|
parallel: 3,
|
||||||
|
maxParallel: 3,
|
||||||
|
};
|
||||||
|
const result = inspector.inspectTask(target, parsedArgs);
|
||||||
|
|
||||||
|
expect(result['test-app:build']).toBeDefined();
|
||||||
|
expect(Array.isArray(result['test-app:build'])).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('integration scenarios', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await inspector.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle both inspectHashPlan and inspectTask on same instance', () => {
|
||||||
|
const hashPlanResult = inspector.inspectHashPlan(['test-app'], ['build']);
|
||||||
|
const taskResult = inspector.inspectTask({
|
||||||
|
project: 'test-app',
|
||||||
|
target: 'build',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(hashPlanResult['test-app:build']).toBeDefined();
|
||||||
|
expect(taskResult['test-app:build']).toBeDefined();
|
||||||
|
expect(Array.isArray(hashPlanResult['test-app:build'])).toBe(true);
|
||||||
|
expect(Array.isArray(taskResult['test-app:build'])).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work with project dependencies', () => {
|
||||||
|
// test-app depends on test-lib, so building test-app should include test-lib
|
||||||
|
const result = inspector.inspectHashPlan(['test-app'], ['build']);
|
||||||
|
|
||||||
|
// Should only contain test-app:build (test-lib:build is not included as a separate task)
|
||||||
|
expect(Object.keys(result)).toEqual(['test-app:build']);
|
||||||
|
|
||||||
|
const testAppPlan = result['test-app:build'];
|
||||||
|
|
||||||
|
// Should include test-lib files in the hash plan due to the dependency
|
||||||
|
expect(testAppPlan).toContain('file:libs/test-lib/project.json');
|
||||||
|
expect(testAppPlan).toContain('file:libs/test-lib/src/index.ts');
|
||||||
|
|
||||||
|
// Should include both project configurations
|
||||||
|
expect(testAppPlan).toContain('test-app:ProjectConfiguration');
|
||||||
|
expect(testAppPlan).toContain('test-lib:ProjectConfiguration');
|
||||||
|
|
||||||
|
// Should include both TypeScript configurations
|
||||||
|
expect(testAppPlan).toContain('test-app:TsConfig');
|
||||||
|
expect(testAppPlan).toContain('test-lib:TsConfig');
|
||||||
|
|
||||||
|
// Should include common files
|
||||||
|
expect(testAppPlan).toContain('env:NX_CLOUD_ENCRYPTION_KEY');
|
||||||
|
expect(testAppPlan).toContain('file:nx.json');
|
||||||
|
|
||||||
|
// Should include test-app files
|
||||||
|
expect(testAppPlan).toContain('file:apps/test-app/project.json');
|
||||||
|
expect(testAppPlan).toContain('file:apps/test-app/src/main.ts');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error for non-existent project', () => {
|
||||||
|
expect(() => {
|
||||||
|
inspector.inspectHashPlan(['non-existent-project'], ['build']);
|
||||||
|
}).toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error for non-existent target', () => {
|
||||||
|
expect(() => {
|
||||||
|
inspector.inspectHashPlan(['test-app'], ['non-existent-target']);
|
||||||
|
}).toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
122
packages/nx/src/hasher/hash-plan-inspector.ts
Normal file
122
packages/nx/src/hasher/hash-plan-inspector.ts
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
import {
|
||||||
|
HashPlanInspector as NativeHashPlanInspector,
|
||||||
|
HashPlanner,
|
||||||
|
transferProjectGraph,
|
||||||
|
ExternalObject,
|
||||||
|
ProjectGraph as NativeProjectGraph,
|
||||||
|
} from '../native';
|
||||||
|
import { readNxJson, NxJsonConfiguration } from '../config/nx-json';
|
||||||
|
import { transformProjectGraphForRust } from '../native/transform-objects';
|
||||||
|
import { ProjectGraph } from '../config/project-graph';
|
||||||
|
import { workspaceRoot } from '../utils/workspace-root';
|
||||||
|
import { createProjectRootMappings } from '../project-graph/utils/find-project-for-path';
|
||||||
|
import { createTaskGraph } from '../tasks-runner/create-task-graph';
|
||||||
|
import type { Target } from '../command-line/run/run';
|
||||||
|
import { TargetDependencies } from '../config/nx-json';
|
||||||
|
import { TargetDependencyConfig } from '../config/workspace-json-project-json';
|
||||||
|
import { splitArgsIntoNxArgsAndOverrides } from '../utils/command-line-utils';
|
||||||
|
import { getNxWorkspaceFilesFromContext } from '../utils/workspace-context';
|
||||||
|
|
||||||
|
export class HashPlanInspector {
|
||||||
|
private readonly projectGraphRef: ExternalObject<NativeProjectGraph>;
|
||||||
|
private planner: HashPlanner;
|
||||||
|
private inspector: NativeHashPlanInspector;
|
||||||
|
private readonly nxJson: NxJsonConfiguration;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private projectGraph: ProjectGraph,
|
||||||
|
private readonly workspaceRootPath: string = workspaceRoot,
|
||||||
|
nxJson?: NxJsonConfiguration
|
||||||
|
) {
|
||||||
|
this.nxJson = nxJson ?? readNxJson(this.workspaceRootPath);
|
||||||
|
this.projectGraphRef = transferProjectGraph(
|
||||||
|
transformProjectGraphForRust(this.projectGraph)
|
||||||
|
);
|
||||||
|
this.planner = new HashPlanner(this.nxJson, this.projectGraphRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
const projectRootMap = createProjectRootMappings(this.projectGraph.nodes);
|
||||||
|
const map = Object.fromEntries(projectRootMap.entries());
|
||||||
|
const { externalReferences } = await getNxWorkspaceFilesFromContext(
|
||||||
|
this.workspaceRootPath,
|
||||||
|
map,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
this.inspector = new NativeHashPlanInspector(
|
||||||
|
externalReferences.allWorkspaceFiles,
|
||||||
|
this.projectGraphRef,
|
||||||
|
externalReferences.projectFiles
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a lower level method which will inspect the hash plan for a set of tasks.
|
||||||
|
*/
|
||||||
|
inspectHashPlan(
|
||||||
|
projectNames: string[],
|
||||||
|
targets: string[],
|
||||||
|
configuration?: string,
|
||||||
|
overrides: Object = {},
|
||||||
|
extraTargetDependencies: TargetDependencies = {},
|
||||||
|
excludeTaskDependencies: boolean = false
|
||||||
|
) {
|
||||||
|
const taskGraph = createTaskGraph(
|
||||||
|
this.projectGraph,
|
||||||
|
extraTargetDependencies,
|
||||||
|
projectNames,
|
||||||
|
targets,
|
||||||
|
configuration,
|
||||||
|
overrides,
|
||||||
|
excludeTaskDependencies
|
||||||
|
);
|
||||||
|
// Generate task IDs for ALL tasks in the task graph (including dependencies)
|
||||||
|
const taskIds = Object.keys(taskGraph.tasks);
|
||||||
|
|
||||||
|
const plansReference = this.planner.getPlansReference(taskIds, taskGraph);
|
||||||
|
|
||||||
|
return this.inspector.inspect(plansReference);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This inspects tasks involved in the execution of a task, including its dependencies by default.
|
||||||
|
*/
|
||||||
|
inspectTask(
|
||||||
|
{ project, target, configuration }: Target,
|
||||||
|
parsedArgs: { [k: string]: any } = {},
|
||||||
|
extraTargetDependencies: Record<
|
||||||
|
string,
|
||||||
|
(TargetDependencyConfig | string)[]
|
||||||
|
> = {},
|
||||||
|
excludeTaskDependencies: boolean = false
|
||||||
|
) {
|
||||||
|
// Mirror the exact flow from run-one.ts
|
||||||
|
const { nxArgs, overrides } = splitArgsIntoNxArgsAndOverrides(
|
||||||
|
{
|
||||||
|
...parsedArgs,
|
||||||
|
configuration: configuration,
|
||||||
|
targets: [target],
|
||||||
|
},
|
||||||
|
'run-one',
|
||||||
|
{ printWarnings: false },
|
||||||
|
this.nxJson
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create task graph exactly like run-one.ts does via createTaskGraphAndRunValidations
|
||||||
|
const taskGraph = createTaskGraph(
|
||||||
|
this.projectGraph,
|
||||||
|
extraTargetDependencies,
|
||||||
|
[project],
|
||||||
|
nxArgs.targets,
|
||||||
|
nxArgs.configuration,
|
||||||
|
overrides,
|
||||||
|
excludeTaskDependencies
|
||||||
|
);
|
||||||
|
|
||||||
|
// Generate task IDs for ALL tasks in the task graph (including dependencies)
|
||||||
|
const taskIds = Object.keys(taskGraph.tasks);
|
||||||
|
|
||||||
|
const plansReference = this.planner.getPlansReference(taskIds, taskGraph);
|
||||||
|
return this.inspector.inspect(plansReference);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
pub mod glob_files;
|
||||||
mod glob_group;
|
mod glob_group;
|
||||||
mod glob_parser;
|
mod glob_parser;
|
||||||
pub mod glob_transform;
|
pub mod glob_transform;
|
||||||
|
|||||||
@ -4,7 +4,7 @@ use crate::native::glob::build_glob_set;
|
|||||||
use crate::native::types::FileData;
|
use crate::native::types::FileData;
|
||||||
|
|
||||||
/// Get workspace config files based on provided globs
|
/// Get workspace config files based on provided globs
|
||||||
pub(super) fn glob_files(
|
pub fn glob_files(
|
||||||
files: &[FileData],
|
files: &[FileData],
|
||||||
globs: Vec<String>,
|
globs: Vec<String>,
|
||||||
exclude: Option<Vec<String>>,
|
exclude: Option<Vec<String>>,
|
||||||
5
packages/nx/src/native/index.d.ts
vendored
5
packages/nx/src/native/index.d.ts
vendored
@ -41,6 +41,11 @@ export declare class FileLock {
|
|||||||
lock(): void
|
lock(): void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export declare class HashPlanInspector {
|
||||||
|
constructor(allWorkspaceFiles: ExternalObject<Array<FileData>>, projectGraph: ExternalObject<ProjectGraph>, projectFileMap: ExternalObject<Record<string, Array<FileData>>>)
|
||||||
|
inspect(hashPlans: ExternalObject<Record<string, Array<HashInstruction>>>): Record<string, string[]>
|
||||||
|
}
|
||||||
|
|
||||||
export declare class HashPlanner {
|
export declare class HashPlanner {
|
||||||
constructor(nxJson: NxJson, projectGraph: ExternalObject<ProjectGraph>)
|
constructor(nxJson: NxJson, projectGraph: ExternalObject<ProjectGraph>)
|
||||||
getPlans(taskIds: Array<string>, taskGraph: TaskGraph): Record<string, string[]>
|
getPlans(taskIds: Array<string>, taskGraph: TaskGraph): Record<string, string[]>
|
||||||
|
|||||||
@ -364,6 +364,7 @@ if (!nativeBinding) {
|
|||||||
module.exports.AppLifeCycle = nativeBinding.AppLifeCycle
|
module.exports.AppLifeCycle = nativeBinding.AppLifeCycle
|
||||||
module.exports.ChildProcess = nativeBinding.ChildProcess
|
module.exports.ChildProcess = nativeBinding.ChildProcess
|
||||||
module.exports.FileLock = nativeBinding.FileLock
|
module.exports.FileLock = nativeBinding.FileLock
|
||||||
|
module.exports.HashPlanInspector = nativeBinding.HashPlanInspector
|
||||||
module.exports.HashPlanner = nativeBinding.HashPlanner
|
module.exports.HashPlanner = nativeBinding.HashPlanner
|
||||||
module.exports.HttpRemoteCache = nativeBinding.HttpRemoteCache
|
module.exports.HttpRemoteCache = nativeBinding.HttpRemoteCache
|
||||||
module.exports.ImportResult = nativeBinding.ImportResult
|
module.exports.ImportResult = nativeBinding.ImportResult
|
||||||
|
|||||||
81
packages/nx/src/native/tasks/hash_plan_inspector.rs
Normal file
81
packages/nx/src/native/tasks/hash_plan_inspector.rs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
use crate::native::project_graph::types::ProjectGraph;
|
||||||
|
use crate::native::tasks::hashers::{collect_project_files, get_workspace_files};
|
||||||
|
use crate::native::tasks::types::HashInstruction;
|
||||||
|
use crate::native::types::FileData;
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use napi::bindgen_prelude::*;
|
||||||
|
use rayon::prelude::*;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub struct HashPlanInspector {
|
||||||
|
all_workspace_files: External<Vec<FileData>>,
|
||||||
|
project_graph: External<ProjectGraph>,
|
||||||
|
project_file_map: External<HashMap<String, Vec<FileData>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
impl HashPlanInspector {
|
||||||
|
#[napi(constructor)]
|
||||||
|
pub fn new(
|
||||||
|
all_workspace_files: External<Vec<FileData>>,
|
||||||
|
project_graph: External<ProjectGraph>,
|
||||||
|
project_file_map: External<HashMap<String, Vec<FileData>>>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
all_workspace_files,
|
||||||
|
project_graph,
|
||||||
|
project_file_map,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(ts_return_type = "Record<string, string[]>")]
|
||||||
|
pub fn inspect(
|
||||||
|
&self,
|
||||||
|
hash_plans: External<HashMap<String, Vec<HashInstruction>>>,
|
||||||
|
) -> anyhow::Result<HashMap<String, Vec<String>>> {
|
||||||
|
let a: Vec<(&String, Vec<String>)> = hash_plans
|
||||||
|
.iter()
|
||||||
|
.flat_map(|(task_id, instructions)| {
|
||||||
|
instructions
|
||||||
|
.iter()
|
||||||
|
.map(move |instruction| (task_id, instruction))
|
||||||
|
})
|
||||||
|
.par_bridge()
|
||||||
|
.map(|(task_id, instruction)| match instruction {
|
||||||
|
HashInstruction::WorkspaceFileSet(workspace_file_set) => {
|
||||||
|
let files = get_workspace_files(workspace_file_set, &self.all_workspace_files)?
|
||||||
|
.map(|x| format!("file:{}", x.file))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok::<_, anyhow::Error>((task_id, files))
|
||||||
|
}
|
||||||
|
HashInstruction::ProjectFileSet(project_name, file_sets) => {
|
||||||
|
let project = self
|
||||||
|
.project_graph
|
||||||
|
.nodes
|
||||||
|
.get(project_name)
|
||||||
|
.ok_or_else(|| anyhow!("project {} not found", project_name))?;
|
||||||
|
|
||||||
|
let files = collect_project_files(
|
||||||
|
project_name,
|
||||||
|
&project.root,
|
||||||
|
file_sets,
|
||||||
|
&self.project_file_map,
|
||||||
|
)?
|
||||||
|
.iter()
|
||||||
|
.map(|x| format!("file:{}", x.file))
|
||||||
|
.collect();
|
||||||
|
Ok::<_, anyhow::Error>((task_id, files))
|
||||||
|
}
|
||||||
|
_ => Ok::<_, anyhow::Error>((task_id, vec![instruction.to_string()])),
|
||||||
|
})
|
||||||
|
.collect::<anyhow::Result<_>>()?;
|
||||||
|
|
||||||
|
Ok(a.into_iter()
|
||||||
|
.fold(HashMap::new(), |mut acc, (task_id, files)| {
|
||||||
|
acc.entry(task_id.clone()).or_default().extend(files);
|
||||||
|
acc
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -13,7 +13,8 @@ pub fn hash_project_files(
|
|||||||
project_file_map: &HashMap<String, Vec<FileData>>,
|
project_file_map: &HashMap<String, Vec<FileData>>,
|
||||||
) -> Result<String> {
|
) -> Result<String> {
|
||||||
let _span = trace_span!("hash_project_files", project_name).entered();
|
let _span = trace_span!("hash_project_files", project_name).entered();
|
||||||
let collected_files = collect_files(project_name, project_root, file_sets, project_file_map)?;
|
let collected_files =
|
||||||
|
collect_project_files(project_name, project_root, file_sets, project_file_map)?;
|
||||||
trace!("collected_files: {:?}", collected_files.len());
|
trace!("collected_files: {:?}", collected_files.len());
|
||||||
let mut hasher = xxhash_rust::xxh3::Xxh3::new();
|
let mut hasher = xxhash_rust::xxh3::Xxh3::new();
|
||||||
for file in collected_files {
|
for file in collected_files {
|
||||||
@ -24,7 +25,7 @@ pub fn hash_project_files(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// base function that should be testable (to make sure that we're getting the proper files back)
|
/// base function that should be testable (to make sure that we're getting the proper files back)
|
||||||
fn collect_files<'a>(
|
pub fn collect_project_files<'a>(
|
||||||
project_name: &str,
|
project_name: &str,
|
||||||
project_root: &str,
|
project_root: &str,
|
||||||
file_sets: &[String],
|
file_sets: &[String],
|
||||||
@ -100,11 +101,11 @@ mod tests {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
let result = collect_files(proj_name, proj_root, file_sets, &file_map).unwrap();
|
let result = collect_project_files(proj_name, proj_root, file_sets, &file_map).unwrap();
|
||||||
|
|
||||||
assert_eq!(result, vec![&tsfile_1, &tsfile_2]);
|
assert_eq!(result, vec![&tsfile_1, &tsfile_2]);
|
||||||
|
|
||||||
let result = collect_files(
|
let result = collect_project_files(
|
||||||
proj_name,
|
proj_name,
|
||||||
proj_root,
|
proj_root,
|
||||||
&["!{projectRoot}/**/*.spec.ts".into()],
|
&["!{projectRoot}/**/*.spec.ts".into()],
|
||||||
|
|||||||
@ -1,18 +1,16 @@
|
|||||||
|
use rayon::prelude::*;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::native::glob::build_glob_set;
|
||||||
|
use crate::native::glob::glob_files::glob_files;
|
||||||
|
use crate::native::hasher::hash;
|
||||||
|
use crate::native::types::FileData;
|
||||||
use anyhow::*;
|
use anyhow::*;
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use tracing::{debug, debug_span, trace, warn};
|
use tracing::{debug, debug_span, trace, warn};
|
||||||
|
|
||||||
use crate::native::types::FileData;
|
fn globs_from_workspace_inputs(workspace_file_sets: &[String]) -> Vec<String> {
|
||||||
use crate::native::{glob::build_glob_set, hasher::hash};
|
workspace_file_sets
|
||||||
|
|
||||||
pub fn hash_workspace_files(
|
|
||||||
workspace_file_sets: &[String],
|
|
||||||
all_workspace_files: &[FileData],
|
|
||||||
cache: Arc<DashMap<String, String>>,
|
|
||||||
) -> Result<String> {
|
|
||||||
let globs: Vec<String> = workspace_file_sets
|
|
||||||
.iter()
|
.iter()
|
||||||
.inspect(|&x| trace!("Workspace file set: {}", x))
|
.inspect(|&x| trace!("Workspace file set: {}", x))
|
||||||
.filter_map(|x| {
|
.filter_map(|x| {
|
||||||
@ -33,7 +31,23 @@ pub fn hash_workspace_files(
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_workspace_files<'a, 'b>(
|
||||||
|
workspace_file_sets: &'a [String],
|
||||||
|
all_workspace_files: &'b [FileData],
|
||||||
|
) -> napi::Result<impl ParallelIterator<Item = &'b FileData>> {
|
||||||
|
let globs = globs_from_workspace_inputs(workspace_file_sets);
|
||||||
|
glob_files(all_workspace_files, globs, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hash_workspace_files(
|
||||||
|
workspace_file_sets: &[String],
|
||||||
|
all_workspace_files: &[FileData],
|
||||||
|
cache: Arc<DashMap<String, String>>,
|
||||||
|
) -> Result<String> {
|
||||||
|
let globs = globs_from_workspace_inputs(workspace_file_sets);
|
||||||
|
|
||||||
if globs.is_empty() {
|
if globs.is_empty() {
|
||||||
return Ok(hash(b""));
|
return Ok(hash(b""));
|
||||||
@ -133,7 +147,6 @@ mod test {
|
|||||||
file: "packages/project/project.json".into(),
|
file: "packages/project/project.json".into(),
|
||||||
hash: "abc".into(),
|
hash: "abc".into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
for i in 0..1000 {
|
for i in 0..1000 {
|
||||||
let result = hash_workspace_files(
|
let result = hash_workspace_files(
|
||||||
&["{workspaceRoot}/**/*".to_string()],
|
&["{workspaceRoot}/**/*".to_string()],
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
mod dep_outputs;
|
mod dep_outputs;
|
||||||
|
mod hash_plan_inspector;
|
||||||
mod hash_planner;
|
mod hash_planner;
|
||||||
pub mod hashers;
|
pub mod hashers;
|
||||||
mod inputs;
|
mod inputs;
|
||||||
|
|||||||
@ -21,7 +21,7 @@ use crate::native::{
|
|||||||
};
|
};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use napi::bindgen_prelude::{Buffer, External};
|
use napi::bindgen_prelude::*;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use tracing::{debug, trace, trace_span};
|
use tracing::{debug, trace, trace_span};
|
||||||
|
|
||||||
|
|||||||
@ -42,7 +42,7 @@ pub struct TaskGraph {
|
|||||||
pub dependencies: HashMap<String, Vec<String>>,
|
pub dependencies: HashMap<String, Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
|
||||||
pub enum HashInstruction {
|
pub enum HashInstruction {
|
||||||
WorkspaceFileSet(Vec<String>),
|
WorkspaceFileSet(Vec<String>),
|
||||||
Runtime(String),
|
Runtime(String),
|
||||||
|
|||||||
@ -4,6 +4,7 @@ use std::ops::Deref;
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::native::glob::glob_files::glob_files;
|
||||||
use crate::native::hasher::hash;
|
use crate::native::hasher::hash;
|
||||||
use crate::native::logger::enable_logger;
|
use crate::native::logger::enable_logger;
|
||||||
use crate::native::project_graph::utils::{ProjectRootMappings, find_project_for_path};
|
use crate::native::project_graph::utils::{ProjectRootMappings, find_project_for_path};
|
||||||
@ -14,7 +15,7 @@ use crate::native::workspace::files_hashing::{full_files_hash, selective_files_h
|
|||||||
use crate::native::workspace::types::{
|
use crate::native::workspace::types::{
|
||||||
FileMap, NxWorkspaceFilesExternals, ProjectFiles, UpdatedWorkspaceFiles,
|
FileMap, NxWorkspaceFilesExternals, ProjectFiles, UpdatedWorkspaceFiles,
|
||||||
};
|
};
|
||||||
use crate::native::workspace::{config_files, types::NxWorkspaceFiles, workspace_files};
|
use crate::native::workspace::{types::NxWorkspaceFiles, workspace_files};
|
||||||
use napi::bindgen_prelude::External;
|
use napi::bindgen_prelude::External;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use tracing::{trace, warn};
|
use tracing::{trace, warn};
|
||||||
@ -229,7 +230,7 @@ impl WorkspaceContext {
|
|||||||
exclude: Option<Vec<String>>,
|
exclude: Option<Vec<String>>,
|
||||||
) -> napi::Result<Vec<String>> {
|
) -> napi::Result<Vec<String>> {
|
||||||
let file_data = self.all_file_data();
|
let file_data = self.all_file_data();
|
||||||
let globbed_files = config_files::glob_files(&file_data, globs, exclude)?;
|
let globbed_files = glob_files(&file_data, globs, exclude)?;
|
||||||
Ok(globbed_files.map(|file| file.file.to_owned()).collect())
|
Ok(globbed_files.map(|file| file.file.to_owned()).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,8 +249,7 @@ impl WorkspaceContext {
|
|||||||
globs
|
globs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|glob| {
|
.map(|glob| {
|
||||||
let globbed_files =
|
let globbed_files = glob_files(&file_data, vec![glob], exclude.clone())?;
|
||||||
config_files::glob_files(&file_data, vec![glob], exclude.clone())?;
|
|
||||||
Ok(globbed_files.map(|file| file.file.to_owned()).collect())
|
Ok(globbed_files.map(|file| file.file.to_owned()).collect())
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
@ -264,8 +264,7 @@ impl WorkspaceContext {
|
|||||||
let hashes = glob_groups
|
let hashes = glob_groups
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|globs| {
|
.map(|globs| {
|
||||||
let globbed_files =
|
let globbed_files = glob_files(files, globs, None)?.collect::<Vec<_>>();
|
||||||
config_files::glob_files(files, globs, None)?.collect::<Vec<_>>();
|
|
||||||
let mut hasher = xxh3::Xxh3::new();
|
let mut hasher = xxh3::Xxh3::new();
|
||||||
for file in globbed_files {
|
for file in globbed_files {
|
||||||
hasher.update(file.file.as_bytes());
|
hasher.update(file.file.as_bytes());
|
||||||
@ -285,7 +284,7 @@ impl WorkspaceContext {
|
|||||||
exclude: Option<Vec<String>>,
|
exclude: Option<Vec<String>>,
|
||||||
) -> napi::Result<String> {
|
) -> napi::Result<String> {
|
||||||
let files = &self.all_file_data();
|
let files = &self.all_file_data();
|
||||||
let globbed_files = config_files::glob_files(files, globs, exclude)?.collect::<Vec<_>>();
|
let globbed_files = glob_files(files, globs, exclude)?.collect::<Vec<_>>();
|
||||||
|
|
||||||
let mut hasher = xxh3::Xxh3::new();
|
let mut hasher = xxh3::Xxh3::new();
|
||||||
for file in globbed_files {
|
for file in globbed_files {
|
||||||
|
|||||||
@ -3,7 +3,6 @@ use crate::native::workspace::types::NxWorkspaceFilesExternals;
|
|||||||
use napi::bindgen_prelude::External;
|
use napi::bindgen_prelude::External;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub mod config_files;
|
|
||||||
pub mod context;
|
pub mod context;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod files_archive;
|
mod files_archive;
|
||||||
|
|||||||
@ -24,9 +24,10 @@ export function setupWorkspaceContext(workspaceRoot: string) {
|
|||||||
|
|
||||||
export async function getNxWorkspaceFilesFromContext(
|
export async function getNxWorkspaceFilesFromContext(
|
||||||
workspaceRoot: string,
|
workspaceRoot: string,
|
||||||
projectRootMap: Record<string, string>
|
projectRootMap: Record<string, string>,
|
||||||
|
useDaemonProcess: boolean = true
|
||||||
) {
|
) {
|
||||||
if (isOnDaemon() || !daemonClient.enabled()) {
|
if (!useDaemonProcess || isOnDaemon() || !daemonClient.enabled()) {
|
||||||
ensureContextAvailable(workspaceRoot);
|
ensureContextAvailable(workspaceRoot);
|
||||||
return workspaceContext.getWorkspaceFiles(projectRootMap);
|
return workspaceContext.getWorkspaceFiles(projectRootMap);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user