Skip to content
1 change: 1 addition & 0 deletions docs/angular/guides/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,7 @@ Tasks runners can accept different options. The following are the options suppor
- `cacheDirectory` defines where the local cache is stored, which is `node_modules/.cache/nx` by default.
- `encryptionKey` (when using `"@nrwl/nx-cloud"` only) defines an encryption key to support end-to-end encryption of your cloud cache. You may also provide an environment variable with the key `NX_CLOUD_ENCRYPTION_KEY` that contains an encryption key as its value. The Nx Cloud task runner normalizes the key length, so any length of key is acceptable.
- `runtimeCacheInputs` defines the list of commands that are run by the runner to include into the computation hash value.
- `selectivelyCacheTsConfig` remove all the TypeScript alias paths in the `tsconfig.base.json` that are not the project being hashed. Defaults to `false`

`runtimeCacheInputs` are set as follows:

Expand Down
1 change: 1 addition & 0 deletions docs/node/guides/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,7 @@ Tasks runners can accept different options. The following are the options suppor
- `cacheDirectory` defines where the local cache is stored, which is `node_modules/.cache/nx` by default.
- `encryptionKey` (when using `"@nrwl/nx-cloud"` only) defines an encryption key to support end-to-end encryption of your cloud cache. You may also provide an environment variable with the key `NX_CLOUD_ENCRYPTION_KEY` that contains an encryption key as its value. The Nx Cloud task runner normalizes the key length, so any length of key is acceptable.
- `runtimeCacheInputs` defines the list of commands that are run by the runner to include into the computation hash value.
- `selectivelyCacheTsConfig` remove all the TypeScript alias paths in the `tsconfig.base.json` that are not the project being hashed. Defaults to `false`

`runtimeCacheInputs` are set as follows:

Expand Down
1 change: 1 addition & 0 deletions docs/react/guides/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,7 @@ Tasks runners can accept different options. The following are the options suppor
- `cacheDirectory` defines where the local cache is stored, which is `node_modules/.cache/nx` by default.
- `encryptionKey` (when using `"@nrwl/nx-cloud"` only) defines an encryption key to support end-to-end encryption of your cloud cache. You may also provide an environment variable with the key `NX_CLOUD_ENCRYPTION_KEY` that contains an encryption key as its value. The Nx Cloud task runner normalizes the key length, so any length of key is acceptable.
- `runtimeCacheInputs` defines the list of commands that are run by the runner to include into the computation hash value.
- `selectivelyCacheTsConfig` remove all the TypeScript alias paths in the `tsconfig.base.json` that are not the project being hashed. Defaults to `false`

`runtimeCacheInputs` are set as follows:

Expand Down
176 changes: 133 additions & 43 deletions packages/workspace/src/core/hasher/hasher.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,36 @@ import fs = require('fs');
jest.mock('fs');

describe('Hasher', () => {
const nxJson = {
npmScope: 'nrwl',
projects: {
parent: { implicitDependencies: [], tags: [] },
child: { implicitDependencies: [], tags: [] },
},
};

const workSpaceJson = {
projects: {
parent: { root: 'libs/parent' },
child: { root: 'libs/child' },
},
};

const tsConfigBaseJsonHash = JSON.stringify({
compilerOptions: {
paths: {
'@nrwl/parent': ['libs/parent/src/index.ts'],
'@nrwl/child': ['libs/child/src/index.ts'],
},
},
});
let hashes = {
'yarn.lock': 'yarn.lock.hash',
'nx.json': 'nx.json.hash',
'package-lock.json': 'package-lock.json.hash',
'package.json': 'package.json.hash',
'pnpm-lock.yaml': 'pnpm-lock.yaml.hash',
'tsconfig.base.json': 'tsconfig.base.json.hash',
'tsconfig.base.json': tsConfigBaseJsonHash,
'workspace.json': 'workspace.json.hash',
global1: 'global1.hash',
global2: 'global2.hash',
Expand All @@ -23,24 +46,28 @@ describe('Hasher', () => {
};
}

it('should create project hash', async () => {
beforeAll(() => {
fs.readFileSync = (file) => {
if (file === 'workspace.json') {
return JSON.stringify({
projects: { proj: { root: 'proj-from-workspace.json' } },
});
return JSON.stringify(workSpaceJson);
}
if (file === 'nx.json') {
return JSON.stringify({ projects: { proj: 'proj-from-nx.json' } });
return JSON.stringify(nxJson);
}
if (file === 'tsconfig.base.json') {
return tsConfigBaseJsonHash;
}
return file;
};
});

it('should create project hash', async () => {
hashes['/file'] = 'file.hash';
const hasher = new Hasher(
{
nodes: {
proj: {
name: 'proj',
parent: {
name: 'parent',
type: 'lib',
data: {
root: '',
Expand All @@ -49,7 +76,7 @@ describe('Hasher', () => {
},
},
dependencies: {
proj: [],
parent: [],
},
},
{} as any,
Expand All @@ -60,29 +87,86 @@ describe('Hasher', () => {
);

const hash = await hasher.hashTaskWithDepsAndContext({
target: { project: 'proj', target: 'build' },
id: 'proj-build',
target: { project: 'parent', target: 'build' },
id: 'parent-build',
overrides: { prop: 'prop-value' },
});

expect(hash.value).toContain('yarn.lock.hash'); //implicits
expect(hash.value).toContain('file.hash'); //project files
expect(hash.value).toContain('prop-value'); //overrides
expect(hash.value).toContain('proj'); //project
expect(hash.value).toContain('parent'); //project
expect(hash.value).toContain('build'); //target
expect(hash.value).toContain('runtime123'); //target
expect(hash.value).toContain('runtime456'); //target

expect(hash.details.command).toEqual('proj|build||{"prop":"prop-value"}');
expect(hash.details.command).toEqual('parent|build||{"prop":"prop-value"}');
expect(hash.details.nodes).toEqual({
proj: '/file|file.hash|{"root":"proj-from-workspace.json"}|"proj-from-nx.json"',
parent:
'/file|file.hash|{"root":"libs/parent"}|{"implicitDependencies":[],"tags":[]}|{"compilerOptions":{"paths":{"@nrwl/parent":["libs/parent/src/index.ts"],"@nrwl/child":["libs/child/src/index.ts"]}}}',
});
expect(hash.details.implicitDeps).toEqual({
'nx.json': '{"npmScope":"nrwl"}',
'yarn.lock': 'yarn.lock.hash',
'package-lock.json': 'package-lock.json.hash',
'pnpm-lock.yaml': 'pnpm-lock.yaml.hash',
});
expect(hash.details.runtime).toEqual({
'echo runtime123': 'runtime123',
'echo runtime456': 'runtime456',
});
});

it('should create project hash with tsconfig.base.json cache', async () => {
hashes['/file'] = 'file.hash';
const hasher = new Hasher(
{
nodes: {
parent: {
name: 'parent',
type: 'lib',
data: {
root: '',
files: [{ file: '/file', ext: '.ts', hash: 'file.hash' }],
},
},
},
dependencies: {
parent: [],
},
},
{} as any,
{
runtimeCacheInputs: ['echo runtime123', 'echo runtime456'],
selectivelyCacheTsConfig: true,
},
createHashing()
);

const hash = await hasher.hashTaskWithDepsAndContext({
target: { project: 'parent', target: 'build' },
id: 'parent-build',
overrides: { prop: 'prop-value' },
});

expect(hash.value).toContain('yarn.lock.hash'); //implicits
expect(hash.value).toContain('file.hash'); //project files
expect(hash.value).toContain('prop-value'); //overrides
expect(hash.value).toContain('parent'); //project
expect(hash.value).toContain('build'); //target
expect(hash.value).toContain('runtime123'); //target
expect(hash.value).toContain('runtime456'); //target

expect(hash.details.command).toEqual('parent|build||{"prop":"prop-value"}');
expect(hash.details.nodes).toEqual({
parent:
'/file|file.hash|{"root":"libs/parent"}|{"implicitDependencies":[],"tags":[]}|{"compilerOptions":{"paths":{"@nrwl/parent":["libs/parent/src/index.ts"]}}}',
});
expect(hash.details.implicitDeps).toEqual({
'nx.json': '{"npmScope":"nrwl"}',
'yarn.lock': 'yarn.lock.hash',
'nx.json': '{}',
'package-lock.json': 'package-lock.json.hash',
'pnpm-lock.yaml': 'pnpm-lock.yaml.hash',
'tsconfig.base.json': 'tsconfig.base.json.hash',
});
expect(hash.details.runtime).toEqual({
'echo runtime123': 'runtime123',
Expand All @@ -94,8 +178,8 @@ describe('Hasher', () => {
const hasher = new Hasher(
{
nodes: {
proj: {
name: 'proj',
parent: {
name: 'parent',
type: 'lib',
data: {
root: '',
Expand All @@ -104,7 +188,7 @@ describe('Hasher', () => {
},
},
dependencies: {
proj: [],
parent: [],
},
},
{} as any,
Expand All @@ -116,8 +200,8 @@ describe('Hasher', () => {

try {
await hasher.hashTaskWithDepsAndContext({
target: { project: 'proj', target: 'build' },
id: 'proj-build',
target: { project: 'parent', target: 'build' },
id: 'parent-build',
overrides: {},
});
fail('Should not be here');
Expand Down Expand Up @@ -172,8 +256,10 @@ describe('Hasher', () => {

// note that the parent hash is based on parent source files only!
expect(hash.details.nodes).toEqual({
parent: '/filea|a.hash|""|""',
child: '/fileb|b.hash|""|""',
child:
'/fileb|b.hash|{"root":"libs/child"}|{"implicitDependencies":[],"tags":[]}|{"compilerOptions":{"paths":{"@nrwl/parent":["libs/parent/src/index.ts"],"@nrwl/child":["libs/child/src/index.ts"]}}}',
parent:
'/filea|a.hash|{"root":"libs/parent"}|{"implicitDependencies":[],"tags":[]}|{"compilerOptions":{"paths":{"@nrwl/parent":["libs/parent/src/index.ts"],"@nrwl/child":["libs/child/src/index.ts"]}}}',
});
});

Expand All @@ -183,16 +269,16 @@ describe('Hasher', () => {
const hasher = new Hasher(
{
nodes: {
proja: {
name: 'proja',
parent: {
name: 'parent',
type: 'lib',
data: {
root: '',
files: [{ file: '/filea', ext: '.ts', hash: 'a.hash' }],
},
},
projb: {
name: 'projb',
child: {
name: 'child',
type: 'lib',
data: {
root: '',
Expand All @@ -201,8 +287,8 @@ describe('Hasher', () => {
},
},
dependencies: {
proja: [{ source: 'proja', target: 'projb', type: 'static' }],
projb: [{ source: 'projb', target: 'proja', type: 'static' }],
parent: [{ source: 'parent', target: 'child', type: 'static' }],
child: [{ source: 'child', target: 'parent', type: 'static' }],
},
},
{} as any,
Expand All @@ -211,37 +297,41 @@ describe('Hasher', () => {
);

const tasksHash = await hasher.hashTaskWithDepsAndContext({
target: { project: 'proja', target: 'build' },
id: 'proja-build',
target: { project: 'parent', target: 'build' },
id: 'parent-build',
overrides: { prop: 'prop-value' },
});

expect(tasksHash.value).toContain('yarn.lock.hash'); //implicits
expect(tasksHash.value).toContain('a.hash'); //project files
expect(tasksHash.value).toContain('b.hash'); //project files
expect(tasksHash.value).toContain('prop-value'); //overrides
expect(tasksHash.value).toContain('proj'); //project
expect(tasksHash.value).toContain('parent|build'); //project and target
expect(tasksHash.value).toContain('build'); //target
expect(tasksHash.details.nodes).toEqual({
proja: '/filea|a.hash|""|""',
projb: '/fileb|b.hash|""|""',
child:
'/fileb|b.hash|{"root":"libs/child"}|{"implicitDependencies":[],"tags":[]}|{"compilerOptions":{"paths":{"@nrwl/parent":["libs/parent/src/index.ts"],"@nrwl/child":["libs/child/src/index.ts"]}}}',
parent:
'/filea|a.hash|{"root":"libs/parent"}|{"implicitDependencies":[],"tags":[]}|{"compilerOptions":{"paths":{"@nrwl/parent":["libs/parent/src/index.ts"],"@nrwl/child":["libs/child/src/index.ts"]}}}',
});

const hashb = await hasher.hashTaskWithDepsAndContext({
target: { project: 'projb', target: 'build' },
id: 'projb-build',
target: { project: 'child', target: 'build' },
id: 'child-build',
overrides: { prop: 'prop-value' },
});

expect(hashb.value).toContain('yarn.lock.hash'); //implicits
expect(hashb.value).toContain('a.hash'); //project files
expect(hashb.value).toContain('b.hash'); //project files
expect(hashb.value).toContain('prop-value'); //overrides
expect(hashb.value).toContain('proj'); //project
expect(hashb.value).toContain('child|build'); //project and target
expect(hashb.value).toContain('build'); //target
expect(hashb.details.nodes).toEqual({
proja: '/filea|a.hash|""|""',
projb: '/fileb|b.hash|""|""',
child:
'/fileb|b.hash|{"root":"libs/child"}|{"implicitDependencies":[],"tags":[]}|{"compilerOptions":{"paths":{"@nrwl/parent":["libs/parent/src/index.ts"],"@nrwl/child":["libs/child/src/index.ts"]}}}',
parent:
'/filea|a.hash|{"root":"libs/parent"}|{"implicitDependencies":[],"tags":[]}|{"compilerOptions":{"paths":{"@nrwl/parent":["libs/parent/src/index.ts"],"@nrwl/child":["libs/child/src/index.ts"]}}}',
});
});

Expand All @@ -251,8 +341,8 @@ describe('Hasher', () => {
const hasher = new Hasher(
{
nodes: {
proja: {
name: 'proja',
parent: {
name: 'parent',
type: 'lib',
data: {
root: '',
Expand Down Expand Up @@ -284,8 +374,8 @@ describe('Hasher', () => {
);

const tasksHash = await hasher.hashTaskWithDepsAndContext({
target: { project: 'proja', target: 'build' },
id: 'proja-build',
target: { project: 'parent', target: 'build' },
id: 'parent-build',
overrides: { prop: 'prop-value' },
});

Expand Down
Loading