Skip to content

Commit

Permalink
feat(core): add WorkspaceContext class (#18999)
Browse files Browse the repository at this point in the history
  • Loading branch information
Cammisuli committed Sep 13, 2023
1 parent ae154e7 commit 537d7eb
Show file tree
Hide file tree
Showing 36 changed files with 556 additions and 388 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ tmp
jest.debug.config.js
.tool-versions
/.nx-cache
/.nx
/.verdaccio/build/local-registry
/graph/client/src/assets/environment.js
/graph/client/src/assets/dev/environment.js
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions e2e/nx-misc/src/watch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ describe('Nx Commands', () => {
runCLI(`generate @nx/js:lib ${proj3}`);
});

afterEach(() => {
runCLI('reset');
});

afterAll(() => cleanupProject());

it('should watch for project changes', async () => {
Expand Down
2 changes: 2 additions & 0 deletions e2e/utils/create-project-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
import { NxJsonConfiguration, output } from '@nx/devkit';
import { readFileSync } from 'fs';
import { join } from 'path';
import { resetWorkspaceContext } from 'nx/src/utils/workspace-context';

let projName: string;

Expand Down Expand Up @@ -566,4 +567,5 @@ export function cleanupProject({
removeSync(tmpProjPath());
} catch {}
}
resetWorkspaceContext();
}
1 change: 1 addition & 0 deletions packages/nx/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ ignore-files = "1.3.0"
itertools = "0.10.5"
once_cell = "1.18.0"
os_type = "2.6.0"
parking_lot = { version = "0.12.1", features = ["send_guard"] }
napi = { version = '2.12.6', default-features = false, features = ['anyhow', 'napi4', 'tokio_rt'] }
napi-derive = '2.9.3'
regex = "1.9.1"
Expand Down
7 changes: 7 additions & 0 deletions packages/nx/bin/nx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import { execSync } from 'child_process';
import { join } from 'path';
import { assertSupportedPlatform } from '../src/native/assert-supported-platform';
import { performance } from 'perf_hooks';
import { setupWorkspaceContext } from '../src/utils/workspace-context';
import { daemonClient } from '../src/daemon/client/client';

function main() {
if (
Expand Down Expand Up @@ -64,6 +66,11 @@ function main() {
) {
require('v8-compile-cache');
}

if (!daemonClient.enabled() && workspace !== null) {
setupWorkspaceContext(workspace.dir);
}

// polyfill rxjs observable to avoid issues with multiple version of Observable installed in node_modules
// https://twitter.com/BenLesh/status/1192478226385428483?s=20
if (!(Symbol as any).observable)
Expand Down
10 changes: 1 addition & 9 deletions packages/nx/src/command-line/affected/print-affected.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { NxJsonConfiguration } from '../../config/nx-json';
import { InProcessTaskHasher } from '../../hasher/task-hasher';
import { hashTask } from '../../hasher/hash-task';
import { getPackageManagerCommand } from '../../utils/package-manager';
import { fileHasher } from '../../hasher/file-hasher';
import { printAffectedDeprecationMessage } from './command-object';
import { logger, NX_PREFIX } from '../../utils/logger';

Expand Down Expand Up @@ -72,14 +71,7 @@ async function createTasks(
nxArgs.configuration,
overrides
);
const hasher = new InProcessTaskHasher(
{},
[],
projectGraph,
nxJson,
{},
fileHasher
);
const hasher = new InProcessTaskHasher({}, [], projectGraph, nxJson, {});
const execCommand = getPackageManagerCommand().exec;
const tasks = Object.values(taskGraph.tasks);

Expand Down
3 changes: 0 additions & 3 deletions packages/nx/src/command-line/graph/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,10 @@ import {
createTaskGraph,
mapTargetDefaultsToDependencies,
} from '../../tasks-runner/create-task-graph';
import { TargetDefaults, TargetDependencies } from '../../config/nx-json';
import { TaskGraph } from '../../config/task-graph';
import { daemonClient } from '../../daemon/client/client';
import { Server } from 'net';
import { readProjectFileMapCache } from '../../project-graph/nx-deps-cache';
import { fileHasher } from '../../hasher/file-hasher';
import { getAffectedGraphNodes } from '../affected/affected';
import { splitArgsIntoNxArgsAndOverrides } from '../../utils/command-line-utils';

Expand Down Expand Up @@ -574,7 +572,6 @@ async function createDepGraphClientResponse(
affected: string[] = []
): Promise<ProjectGraphClientResponse> {
performance.mark('project graph watch calculation:start');
await fileHasher.init();

let graph = pruneExternalNodes(
await createProjectGraphAsync({ exitOnError: true })
Expand Down
4 changes: 1 addition & 3 deletions packages/nx/src/daemon/server/handle-hash-tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { Task, TaskGraph } from '../../config/task-graph';
import { getCachedSerializedProjectGraphPromise } from './project-graph-incremental-recomputation';
import { InProcessTaskHasher } from '../../hasher/task-hasher';
import { readNxJson } from '../../config/configuration';
import { fileHasher } from '../../hasher/file-hasher';
import { setHashEnv } from '../../hasher/set-hash-env';

/**
Expand Down Expand Up @@ -31,8 +30,7 @@ export async function handleHashTasks(payload: {
allWorkspaceFiles,
projectGraph,
nxJson,
payload.runnerOptions,
fileHasher
payload.runnerOptions
);
}
const response = JSON.stringify(
Expand Down
5 changes: 3 additions & 2 deletions packages/nx/src/daemon/server/handle-request-file-data.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { fileHasher } from '../../hasher/file-hasher';
import { getAllFileDataInContext } from '../../utils/workspace-context';
import { workspaceRoot } from '../../utils/workspace-root';

export async function handleRequestFileData() {
const response = JSON.stringify(fileHasher.allFileData());
const response = JSON.stringify(getAllFileDataInContext(workspaceRoot));
return {
response,
description: 'handleRequestFileData',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { notifyFileWatcherSockets } from './file-watching/file-watcher-sockets';
import { serverLogger } from './logger';
import { workspaceRoot } from '../../utils/workspace-root';
import { execSync } from 'child_process';
import { fileHasher, hashArray } from '../../hasher/file-hasher';
import { hashArray } from '../../hasher/file-hasher';
import {
retrieveWorkspaceFiles,
retrieveProjectConfigurations,
Expand All @@ -27,6 +27,10 @@ import {
ProjectsConfigurations,
} from '../../config/workspace-json-project-json';
import { readNxJson } from '../../config/nx-json';
import {
resetWorkspaceContext,
updateFilesInContext,
} from '../../utils/workspace-context';

let cachedSerializedProjectGraphPromise: Promise<{
error: Error | null;
Expand Down Expand Up @@ -163,17 +167,17 @@ function filterUpdatedFiles(files: string[]) {
async function processCollectedUpdatedAndDeletedFiles() {
try {
performance.mark('hash-watched-changes-start');
const updatedFiles = await fileHasher.hashFiles(
filterUpdatedFiles([...collectedUpdatedFiles.values()])
);
const updatedFiles = filterUpdatedFiles([
...collectedUpdatedFiles.values(),
]);
const deletedFiles = [...collectedDeletedFiles.values()];
let updatedFileHashes = updateFilesInContext(updatedFiles, deletedFiles);
performance.mark('hash-watched-changes-end');
performance.measure(
'hash changed files from watcher',
'hash-watched-changes-start',
'hash-watched-changes-end'
);
fileHasher.incrementalUpdate(updatedFiles, deletedFiles);

const nxJson = readNxJson(workspaceRoot);

Expand Down Expand Up @@ -201,7 +205,7 @@ async function processCollectedUpdatedAndDeletedFiles() {
projectNodes,
projectFileMapWithFiles.projectFileMap,
projectFileMapWithFiles.allWorkspaceFiles,
updatedFiles,
new Map(Object.entries(updatedFileHashes)),
deletedFiles
);
} else {
Expand Down Expand Up @@ -330,8 +334,7 @@ async function resetInternalState() {
currentProjectGraph = undefined;
collectedUpdatedFiles.clear();
collectedDeletedFiles.clear();
fileHasher.clear();
await fileHasher.ensureInitialized();
resetWorkspaceContext();
waitPeriod = 100;
}

Expand Down
9 changes: 6 additions & 3 deletions packages/nx/src/daemon/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,10 @@ import { readJsonFile } from '../../utils/fileutils';
import { PackageJson } from '../../utils/package-json';
import { getDaemonProcessIdSync, writeDaemonJsonProcessCache } from '../cache';
import { handleHashTasks } from './handle-hash-tasks';
import { fileHasher, hashArray } from '../../hasher/file-hasher';
import { hashArray } from '../../hasher/file-hasher';
import { handleRequestFileData } from './handle-request-file-data';
import { setupWorkspaceContext } from '../../utils/workspace-context';
import { hashFile } from '../../native';

let performanceObserver: PerformanceObserver | undefined;
let workspaceWatcherError: Error | undefined;
Expand Down Expand Up @@ -283,7 +285,7 @@ function lockFileHashChanged(): boolean {
join(workspaceRoot, 'pnpm-lock.yaml'),
]
.filter((file) => existsSync(file))
.map((file) => fileHasher.hashFile(file));
.map((file) => hashFile(file));
const newHash = hashArray(lockHashes);
if (existingLockHash && newHash != existingLockHash) {
existingLockHash = newHash;
Expand Down Expand Up @@ -397,6 +399,8 @@ const handleOutputsChanges: FileWatcherCallback = async (err, changeEvents) => {
};

export async function startServer(): Promise<Server> {
setupWorkspaceContext(workspaceRoot);

// Persist metadata about the background process so that it can be cleaned up later if needed
await writeDaemonJsonProcessCache({
processId: process.pid,
Expand All @@ -409,7 +413,6 @@ export async function startServer(): Promise<Server> {

return new Promise(async (resolve, reject) => {
try {
await fileHasher.ensureInitialized();
server.listen(FULL_OS_SOCKET_PATH, async () => {
try {
serverLogger.log(`Started listening on: ${FULL_OS_SOCKET_PATH}`);
Expand Down
86 changes: 0 additions & 86 deletions packages/nx/src/hasher/file-hasher.ts
Original file line number Diff line number Diff line change
@@ -1,89 +1,3 @@
import { performance } from 'perf_hooks';
import { workspaceRoot } from '../utils/workspace-root';
import { FileData } from '../config/project-graph';

export class FileHasher {
private fileHashes: Map<string, string>;
private isInitialized = false;

async init(): Promise<void> {
performance.mark('init hashing:start');
// Import as needed. There is also an issue running unit tests in Nx repo if this is a top-level import.
const { hashFiles } = require('../native');
this.clear();
const filesObject = hashFiles(workspaceRoot);
this.fileHashes = new Map(Object.entries(filesObject));

performance.mark('init hashing:end');
performance.measure(
'init hashing',
'init hashing:start',
'init hashing:end'
);
}

hashFile(path: string): string {
// Import as needed. There is also an issue running unit tests in Nx repo if this is a top-level import.
const { hashFile } = require('../native');
return hashFile(path).hash;
}

clear(): void {
this.fileHashes = new Map<string, string>();
this.isInitialized = false;
}

async ensureInitialized() {
if (!this.isInitialized) {
await this.init();
}
}

async hashFiles(files: string[]): Promise<Map<string, string>> {
const r = new Map<string, string>();
for (let f of files) {
r.set(f, this.hashFile(f));
}
return r;
}

allFileData(): FileData[] {
const res = [];
this.fileHashes.forEach((hash, file) => {
res.push({
file,
hash,
});
});
res.sort((x, y) => x.file.localeCompare(y.file));
return res;
}

incrementalUpdate(
updatedFiles: Map<string, string>,
deletedFiles: string[] = []
): void {
performance.mark('incremental hashing:start');

updatedFiles.forEach((hash, filename) => {
this.fileHashes.set(filename, hash);
});

for (const deletedFile of deletedFiles) {
this.fileHashes.delete(deletedFile);
}

performance.mark('incremental hashing:end');
performance.measure(
'incremental hashing',
'incremental hashing:start',
'incremental hashing:end'
);
}
}

export const fileHasher = new FileHasher();

export function hashArray(content: string[]): string {
// Import as needed. There is also an issue running unit tests in Nx repo if this is a top-level import.
const { hashArray } = require('../native');
Expand Down
Loading

0 comments on commit 537d7eb

Please sign in to comment.