UNPKG

nx

Version:

Smart, Fast and Extensible Build System

248 lines • 12.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.buildProjectGraphUsingProjectFileMap = exports.buildProjectGraph = void 0; const tslib_1 = require("tslib"); const workspace_root_1 = require("../utils/workspace-root"); const path_1 = require("path"); const perf_hooks_1 = require("perf_hooks"); const assert_workspace_validity_1 = require("../utils/assert-workspace-validity"); const nx_deps_cache_1 = require("./nx-deps-cache"); const build_dependencies_1 = require("./build-dependencies"); const build_nodes_1 = require("./build-nodes"); const os = require("os"); const build_explicit_typescript_and_package_json_dependencies_1 = require("./build-dependencies/build-explicit-typescript-and-package-json-dependencies"); const nx_plugin_1 = require("../utils/nx-plugin"); const file_hasher_1 = require("../hasher/file-hasher"); const file_map_utils_1 = require("./file-map-utils"); const typescript_1 = require("../utils/typescript"); const fileutils_1 = require("../utils/fileutils"); const logger_1 = require("../utils/logger"); const project_graph_builder_1 = require("./project-graph-builder"); const configuration_1 = require("../config/configuration"); function buildProjectGraph() { return tslib_1.__awaiter(this, void 0, void 0, function* () { const projectConfigurations = (0, configuration_1.readAllWorkspaceConfiguration)(); const { projectFileMap, allWorkspaceFiles } = (0, file_map_utils_1.createProjectFileMap)(projectConfigurations, file_hasher_1.defaultFileHasher.allFileData()); const cacheEnabled = process.env.NX_CACHE_PROJECT_GRAPH !== 'false'; let cache = cacheEnabled ? (0, nx_deps_cache_1.readCache)() : null; return (yield buildProjectGraphUsingProjectFileMap(projectConfigurations, projectFileMap, allWorkspaceFiles, cache, cacheEnabled)).projectGraph; }); } exports.buildProjectGraph = buildProjectGraph; function buildProjectGraphUsingProjectFileMap(projectsConfigurations, projectFileMap, allWorkspaceFiles, cache, shouldWriteCache) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const nxJson = (0, configuration_1.readNxJson)(); const projectGraphVersion = '5.0'; (0, assert_workspace_validity_1.assertWorkspaceValidity)(projectsConfigurations, nxJson); const packageJsonDeps = readCombinedDeps(); const rootTsConfig = readRootTsConfig(); let filesToProcess; let cachedFileData; if (cache && !(0, nx_deps_cache_1.shouldRecomputeWholeGraph)(cache, packageJsonDeps, projectsConfigurations, nxJson, rootTsConfig)) { const fromCache = (0, nx_deps_cache_1.extractCachedFileData)(projectFileMap, cache); filesToProcess = fromCache.filesToProcess; cachedFileData = fromCache.cachedFileData; } else { filesToProcess = projectFileMap; cachedFileData = {}; } const context = createContext(projectsConfigurations, nxJson, projectFileMap, filesToProcess); let projectGraph = yield buildProjectGraphUsingContext(nxJson, context, cachedFileData, projectGraphVersion, packageJsonDeps); const projectGraphCache = (0, nx_deps_cache_1.createCache)(nxJson, packageJsonDeps, projectGraph, rootTsConfig); if (shouldWriteCache) { (0, nx_deps_cache_1.writeCache)(projectGraphCache); } projectGraph.allWorkspaceFiles = allWorkspaceFiles; return { projectGraph, projectGraphCache, }; }); } exports.buildProjectGraphUsingProjectFileMap = buildProjectGraphUsingProjectFileMap; function readCombinedDeps() { const json = (0, fileutils_1.readJsonFile)((0, path_1.join)(workspace_root_1.workspaceRoot, 'package.json')); return Object.assign(Object.assign({}, json.dependencies), json.devDependencies); } function buildProjectGraphUsingContext(nxJson, ctx, cachedFileData, projectGraphVersion, packageJsonDeps) { return tslib_1.__awaiter(this, void 0, void 0, function* () { perf_hooks_1.performance.mark('build project graph:start'); const builder = new project_graph_builder_1.ProjectGraphBuilder(); (0, build_nodes_1.buildWorkspaceProjectNodes)(ctx, builder, nxJson); (0, build_nodes_1.buildNpmPackageNodes)(builder); for (const proj of Object.keys(cachedFileData)) { for (const f of builder.graph.nodes[proj].data.files) { const cached = cachedFileData[proj][f.file]; if (cached && cached.deps) { f.deps = [...cached.deps]; } } } yield buildExplicitDependencies(jsPluginConfig(nxJson, packageJsonDeps), ctx, builder); (0, build_dependencies_1.buildImplicitProjectDependencies)(ctx, builder); builder.setVersion(projectGraphVersion); const initProjectGraph = builder.getUpdatedProjectGraph(); const r = yield updateProjectGraphWithPlugins(ctx, initProjectGraph); perf_hooks_1.performance.mark('build project graph:end'); perf_hooks_1.performance.measure('build project graph', 'build project graph:start', 'build project graph:end'); return r; }); } function jsPluginConfig(nxJson, packageJsonDeps) { var _a, _b; if ((_a = nxJson === null || nxJson === void 0 ? void 0 : nxJson.pluginsConfig) === null || _a === void 0 ? void 0 : _a['@nrwl/js']) { return (_b = nxJson === null || nxJson === void 0 ? void 0 : nxJson.pluginsConfig) === null || _b === void 0 ? void 0 : _b['@nrwl/js']; } if (packageJsonDeps['@nrwl/workspace'] || packageJsonDeps['@nrwl/js'] || packageJsonDeps['@nrwl/node'] || packageJsonDeps['@nrwl/next'] || packageJsonDeps['@nrwl/react'] || packageJsonDeps['@nrwl/angular'] || packageJsonDeps['@nrwl/web']) { return { analyzePackageJson: true, analyzeSourceFiles: true }; } else { return { analyzePackageJson: true, analyzeSourceFiles: false }; } } function buildExplicitDependencies(jsPluginConfig, ctx, builder) { let totalNumOfFilesToProcess = totalNumberOfFilesToProcess(ctx); // using workers has an overhead, so we only do it when the number of // files we need to process is >= 100 and there are more than 2 CPUs // to be able to use at least 2 workers (1 worker per CPU and // 1 CPU for the main thread) if (totalNumOfFilesToProcess < 100 || getNumberOfWorkers() <= 2) { return buildExplicitDependenciesWithoutWorkers(jsPluginConfig, ctx, builder); } else { return buildExplicitDependenciesUsingWorkers(jsPluginConfig, ctx, totalNumOfFilesToProcess, builder); } } function totalNumberOfFilesToProcess(ctx) { let totalNumOfFilesToProcess = 0; Object.values(ctx.filesToProcess).forEach((t) => (totalNumOfFilesToProcess += t.length)); return totalNumOfFilesToProcess; } function splitFilesIntoBins(ctx, totalNumOfFilesToProcess, numberOfWorkers) { // we want to have numberOfWorkers * 5 bins const filesPerBin = Math.round(totalNumOfFilesToProcess / numberOfWorkers / 5) + 1; const bins = []; let currentProjectFileMap = {}; let currentNumberOfFiles = 0; for (const source of Object.keys(ctx.filesToProcess)) { for (const f of Object.values(ctx.filesToProcess[source])) { if (!currentProjectFileMap[source]) currentProjectFileMap[source] = []; currentProjectFileMap[source].push(f); currentNumberOfFiles++; if (currentNumberOfFiles >= filesPerBin) { bins.push(currentProjectFileMap); currentProjectFileMap = {}; currentNumberOfFiles = 0; } } } bins.push(currentProjectFileMap); return bins; } function createWorkerPool(numberOfWorkers) { const res = []; for (let i = 0; i < numberOfWorkers; ++i) { res.push(new (require('worker_threads').Worker)((0, path_1.join)(__dirname, './project-graph-worker.js'), { env: process.env, })); } return res; } function buildExplicitDependenciesWithoutWorkers(jsPluginConfig, ctx, builder) { (0, build_explicit_typescript_and_package_json_dependencies_1.buildExplicitTypescriptAndPackageJsonDependencies)(jsPluginConfig, ctx.workspace, builder.graph, ctx.filesToProcess).forEach((r) => { builder.addExplicitDependency(r.sourceProjectName, r.sourceProjectFile, r.targetProjectName); }); } function buildExplicitDependenciesUsingWorkers(jsPluginConfig, ctx, totalNumOfFilesToProcess, builder) { const numberOfWorkers = Math.min(totalNumOfFilesToProcess, getNumberOfWorkers()); const bins = splitFilesIntoBins(ctx, totalNumOfFilesToProcess, numberOfWorkers); const workers = createWorkerPool(numberOfWorkers); let numberOfExpectedResponses = bins.length; return new Promise((res, reject) => { for (let w of workers) { w.on('message', (explicitDependencies) => { explicitDependencies.forEach((r) => { builder.addExplicitDependency(r.sourceProjectName, r.sourceProjectFile, r.targetProjectName); }); if (bins.length > 0) { w.postMessage({ filesToProcess: bins.shift() }); } // we processed all the bins if (--numberOfExpectedResponses === 0) { for (let w of workers) { w.terminate(); } res(null); } }); w.on('error', reject); w.on('exit', (code) => { if (code !== 0) { reject(new Error(`Unable to complete project graph creation. Worker stopped with exit code: ${code}`)); } }); w.postMessage({ workspace: ctx.workspace, projectGraph: builder.graph, jsPluginConfig, }); w.postMessage({ filesToProcess: bins.shift() }); } }); } function getNumberOfWorkers() { return process.env.NX_PROJECT_GRAPH_MAX_WORKERS ? +process.env.NX_PROJECT_GRAPH_MAX_WORKERS : os.cpus().length - 1; } function createContext(projectsConfigurations, nxJson, fileMap, filesToProcess) { const projects = Object.keys(projectsConfigurations.projects).reduce((map, projectName) => { map[projectName] = Object.assign({}, projectsConfigurations.projects[projectName]); return map; }, {}); return { workspace: Object.assign(Object.assign(Object.assign({}, projectsConfigurations), nxJson), { projects }), fileMap, filesToProcess, }; } function updateProjectGraphWithPlugins(context, initProjectGraph) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const plugins = (0, nx_plugin_1.loadNxPlugins)(context.workspace.plugins).filter((x) => !!x.processProjectGraph); let graph = initProjectGraph; for (const plugin of plugins) { try { graph = yield plugin.processProjectGraph(graph, context); } catch (e) { const message = `Failed to process the project graph with "${plugin.name}". This will error in the future!`; if (process.env.NX_VERBOSE_LOGGING === 'true') { console.error(e); logger_1.logger.error(message); return graph; } else { logger_1.logger.warn(message); logger_1.logger.warn(`Run with NX_VERBOSE_LOGGING=true to see the error.`); } } } return graph; }); } function readRootTsConfig() { const tsConfigPath = (0, typescript_1.getRootTsConfigPath)(); if (tsConfigPath) { return (0, fileutils_1.readJsonFile)(tsConfigPath); } } //# sourceMappingURL=build-project-graph.js.map