UNPKG

nx

Version:

The core Nx plugin contains the core functionality of Nx like the project graph, nx commands and task orchestration.

301 lines (300 loc) • 13.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.currentProjectGraph = exports.currentProjectFileMapCache = exports.fileMapWithFiles = void 0; exports.getCachedSerializedProjectGraphPromise = getCachedSerializedProjectGraphPromise; exports.addUpdatedAndDeletedFiles = addUpdatedAndDeletedFiles; exports.registerProjectGraphRecomputationListener = registerProjectGraphRecomputationListener; const perf_hooks_1 = require("perf_hooks"); const nx_json_1 = require("../../config/nx-json"); const file_hasher_1 = require("../../hasher/file-hasher"); const build_project_graph_1 = require("../../project-graph/build-project-graph"); const file_map_utils_1 = require("../../project-graph/file-map-utils"); const nx_deps_cache_1 = require("../../project-graph/nx-deps-cache"); const retrieve_workspace_files_1 = require("../../project-graph/utils/retrieve-workspace-files"); const fileutils_1 = require("../../utils/fileutils"); const workspace_context_1 = require("../../utils/workspace-context"); const workspace_root_1 = require("../../utils/workspace-root"); const file_watcher_sockets_1 = require("./file-watching/file-watcher-sockets"); const logger_1 = require("./logger"); const error_types_1 = require("../../project-graph/error-types"); const get_plugins_1 = require("../../project-graph/plugins/get-plugins"); let cachedSerializedProjectGraphPromise; const collectedUpdatedFiles = new Set(); const collectedDeletedFiles = new Set(); const projectGraphRecomputationListeners = new Set(); let storedWorkspaceConfigHash; let waitPeriod = 100; let scheduledTimeoutId; let knownExternalNodes = {}; async function getCachedSerializedProjectGraphPromise() { try { let wasScheduled = false; // recomputing it now on demand. we can ignore the scheduled timeout if (scheduledTimeoutId) { wasScheduled = true; clearTimeout(scheduledTimeoutId); scheduledTimeoutId = undefined; } // reset the wait time waitPeriod = 100; await resetInternalStateIfNxDepsMissing(); const plugins = await (0, get_plugins_1.getPlugins)(); if (collectedUpdatedFiles.size == 0 && collectedDeletedFiles.size == 0) { if (!cachedSerializedProjectGraphPromise) { cachedSerializedProjectGraphPromise = processFilesAndCreateAndSerializeProjectGraph(plugins); } } else { cachedSerializedProjectGraphPromise = processFilesAndCreateAndSerializeProjectGraph(plugins); } const result = await cachedSerializedProjectGraphPromise; if (wasScheduled) { notifyProjectGraphRecomputationListeners(result.projectGraph); } return result; } catch (e) { return { error: e, serializedProjectGraph: null, serializedSourceMaps: null, projectGraph: null, projectFileMapCache: null, fileMap: null, allWorkspaceFiles: null, rustReferences: null, }; } } function addUpdatedAndDeletedFiles(createdFiles, updatedFiles, deletedFiles) { for (let f of [...createdFiles, ...updatedFiles]) { collectedDeletedFiles.delete(f); collectedUpdatedFiles.add(f); } for (let f of deletedFiles) { collectedUpdatedFiles.delete(f); collectedDeletedFiles.add(f); } if (updatedFiles.length > 0 || deletedFiles.length > 0) { (0, file_watcher_sockets_1.notifyFileWatcherSockets)(null, updatedFiles, deletedFiles); } if (createdFiles.length > 0) { waitPeriod = 100; // reset it to process the graph faster } if (!scheduledTimeoutId) { scheduledTimeoutId = setTimeout(async () => { scheduledTimeoutId = undefined; if (waitPeriod < 4000) { waitPeriod = waitPeriod * 2; } cachedSerializedProjectGraphPromise = processFilesAndCreateAndSerializeProjectGraph(await (0, get_plugins_1.getPlugins)()); const { projectGraph } = await cachedSerializedProjectGraphPromise; if (createdFiles.length > 0) { (0, file_watcher_sockets_1.notifyFileWatcherSockets)(createdFiles, null, null); } notifyProjectGraphRecomputationListeners(projectGraph); }, waitPeriod); } } function registerProjectGraphRecomputationListener(listener) { projectGraphRecomputationListeners.add(listener); } function computeWorkspaceConfigHash(projectsConfigurations) { const projectConfigurationStrings = Object.entries(projectsConfigurations) .sort(([projectNameA], [projectNameB]) => projectNameA.localeCompare(projectNameB)) .map(([projectName, projectConfig]) => `${projectName}:${JSON.stringify(projectConfig)}`); return (0, file_hasher_1.hashArray)(projectConfigurationStrings); } async function processCollectedUpdatedAndDeletedFiles({ projects, externalNodes, projectRootMap }, updatedFileHashes, deletedFiles) { try { const workspaceConfigHash = computeWorkspaceConfigHash(projects); // when workspace config changes we cannot incrementally update project file map if (workspaceConfigHash !== storedWorkspaceConfigHash) { storedWorkspaceConfigHash = workspaceConfigHash; ({ ...exports.fileMapWithFiles } = await (0, retrieve_workspace_files_1.retrieveWorkspaceFiles)(workspace_root_1.workspaceRoot, projectRootMap)); knownExternalNodes = externalNodes; } else { if (exports.fileMapWithFiles) { exports.fileMapWithFiles = (0, file_map_utils_1.updateFileMap)(projects, exports.fileMapWithFiles.rustReferences, updatedFileHashes, deletedFiles); } else { exports.fileMapWithFiles = await (0, retrieve_workspace_files_1.retrieveWorkspaceFiles)(workspace_root_1.workspaceRoot, projectRootMap); } } collectedUpdatedFiles.clear(); collectedDeletedFiles.clear(); } catch (e) { // this is expected // for instance, project.json can be incorrect or a file we are trying to has // has been deleted // we are resetting internal state to start from scratch next time a file changes // given the user the opportunity to fix the error // if Nx requests the project graph prior to the error being fixed, // the error will be propagated logger_1.serverLogger.log(`Error detected when recomputing project file map: ${e.message}`); resetInternalState(); throw e; } } async function processFilesAndCreateAndSerializeProjectGraph(plugins) { try { perf_hooks_1.performance.mark('hash-watched-changes-start'); const updatedFiles = [...collectedUpdatedFiles.values()]; const deletedFiles = [...collectedDeletedFiles.values()]; let updatedFileHashes = (0, workspace_context_1.updateFilesInContext)(workspace_root_1.workspaceRoot, updatedFiles, deletedFiles); perf_hooks_1.performance.mark('hash-watched-changes-end'); perf_hooks_1.performance.measure('hash changed files from watcher', 'hash-watched-changes-start', 'hash-watched-changes-end'); logger_1.serverLogger.requestLog(`Updated workspace context based on watched changes, recomputing project graph...`); logger_1.serverLogger.requestLog([...updatedFiles.values()]); logger_1.serverLogger.requestLog([...deletedFiles]); const nxJson = (0, nx_json_1.readNxJson)(workspace_root_1.workspaceRoot); global.NX_GRAPH_CREATION = true; let projectConfigurationsResult; let projectConfigurationsError; try { projectConfigurationsResult = await (0, retrieve_workspace_files_1.retrieveProjectConfigurations)(plugins, workspace_root_1.workspaceRoot, nxJson); } catch (e) { if (e instanceof error_types_1.ProjectConfigurationsError) { projectConfigurationsResult = e.partialProjectConfigurationsResult; projectConfigurationsError = e; } else { throw e; } } await processCollectedUpdatedAndDeletedFiles(projectConfigurationsResult, updatedFileHashes, deletedFiles); const g = await createAndSerializeProjectGraph(projectConfigurationsResult); delete global.NX_GRAPH_CREATION; const errors = [...(projectConfigurationsError?.errors ?? [])]; if (g.error) { if ((0, error_types_1.isAggregateProjectGraphError)(g.error) && g.error.errors?.length) { errors.push(...g.error.errors); } else { return { error: g.error, projectGraph: null, projectFileMapCache: null, fileMap: null, rustReferences: null, allWorkspaceFiles: null, serializedProjectGraph: null, serializedSourceMaps: null, }; } } (0, nx_deps_cache_1.writeCache)(g.projectFileMapCache, g.projectGraph, projectConfigurationsResult.sourceMaps, errors); if (errors.length > 0) { return { error: new error_types_1.DaemonProjectGraphError(errors, g.projectGraph, projectConfigurationsResult.sourceMaps), projectGraph: null, projectFileMapCache: null, fileMap: null, rustReferences: null, allWorkspaceFiles: null, serializedProjectGraph: null, serializedSourceMaps: null, }; } else { return g; } } catch (err) { return { error: err, projectGraph: null, projectFileMapCache: null, fileMap: null, rustReferences: null, allWorkspaceFiles: null, serializedProjectGraph: null, serializedSourceMaps: null, }; } } function copyFileData(d) { return d.map((t) => ({ ...t })); } function copyFileMap(m) { const c = { nonProjectFiles: copyFileData(m.nonProjectFiles), projectFileMap: {}, }; for (let p of Object.keys(m.projectFileMap)) { c.projectFileMap[p] = copyFileData(m.projectFileMap[p]); } return c; } async function createAndSerializeProjectGraph({ projects, sourceMaps, }) { try { perf_hooks_1.performance.mark('create-project-graph-start'); const fileMap = copyFileMap(exports.fileMapWithFiles.fileMap); const allWorkspaceFiles = copyFileData(exports.fileMapWithFiles.allWorkspaceFiles); const rustReferences = exports.fileMapWithFiles.rustReferences; const { projectGraph, projectFileMapCache } = await (0, build_project_graph_1.buildProjectGraphUsingProjectFileMap)(projects, knownExternalNodes, fileMap, allWorkspaceFiles, rustReferences, exports.currentProjectFileMapCache || (0, nx_deps_cache_1.readFileMapCache)(), await (0, get_plugins_1.getPlugins)(), sourceMaps); exports.currentProjectFileMapCache = projectFileMapCache; exports.currentProjectGraph = projectGraph; perf_hooks_1.performance.mark('create-project-graph-end'); perf_hooks_1.performance.measure('total execution time for createProjectGraph()', 'create-project-graph-start', 'create-project-graph-end'); perf_hooks_1.performance.mark('json-stringify-start'); const serializedProjectGraph = JSON.stringify(projectGraph); const serializedSourceMaps = JSON.stringify(sourceMaps); perf_hooks_1.performance.mark('json-stringify-end'); perf_hooks_1.performance.measure('serialize graph', 'json-stringify-start', 'json-stringify-end'); return { error: null, projectGraph, projectFileMapCache, fileMap, allWorkspaceFiles, serializedProjectGraph, serializedSourceMaps, rustReferences, }; } catch (e) { logger_1.serverLogger.log(`Error detected when creating a project graph: ${e.message}`); return { error: e, projectGraph: null, projectFileMapCache: null, fileMap: null, allWorkspaceFiles: null, serializedProjectGraph: null, serializedSourceMaps: null, rustReferences: null, }; } } async function resetInternalState() { cachedSerializedProjectGraphPromise = undefined; exports.fileMapWithFiles = undefined; exports.currentProjectFileMapCache = undefined; exports.currentProjectGraph = undefined; collectedUpdatedFiles.clear(); collectedDeletedFiles.clear(); (0, workspace_context_1.resetWorkspaceContext)(); waitPeriod = 100; } async function resetInternalStateIfNxDepsMissing() { try { if (!(0, fileutils_1.fileExists)(nx_deps_cache_1.nxProjectGraph) && cachedSerializedProjectGraphPromise) { await resetInternalState(); } } catch (e) { await resetInternalState(); } } function notifyProjectGraphRecomputationListeners(projectGraph) { for (const listener of projectGraphRecomputationListeners) { listener(projectGraph); } }