diff --git a/.eslintrc.json b/.eslintrc.json index 2aa0de27f3fd9..06a5b7e40138d 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -17,6 +17,10 @@ { "group": ["nx/src/plugins/js*"], "message": "Imports from 'nx/src/plugins/js' are not allowed. Use '@nx/js' instead" + }, + { + "group": ["**/native-bindings", "**/native-bindings.js", ""], + "message": "Direct imports from native-bindings.js are not allowed. Import from index.js instead." } ] } diff --git a/packages/nx/.eslintrc.json b/packages/nx/.eslintrc.json index feac867a7b146..e6ef126225d11 100644 --- a/packages/nx/.eslintrc.json +++ b/packages/nx/.eslintrc.json @@ -25,6 +25,10 @@ { "group": ["nx/*"], "message": "Circular import in 'nx' found. Use relative path." + }, + { + "group": ["**/native-bindings", "**/native-bindings.js"], + "message": "Direct imports from native-bindings.js are not allowed. Import from index.js instead." } ] } diff --git a/packages/nx/bin/nx.ts b/packages/nx/bin/nx.ts index 1c8476c204e45..cecd6acd499c3 100644 --- a/packages/nx/bin/nx.ts +++ b/packages/nx/bin/nx.ts @@ -25,7 +25,8 @@ function main() { if ( process.argv[2] !== 'report' && process.argv[2] !== '--version' && - process.argv[2] !== '--help' + process.argv[2] !== '--help' && + process.argv[2] !== 'reset' ) { assertSupportedPlatform(); } diff --git a/packages/nx/project.json b/packages/nx/project.json index 31f2db84de2b0..3ab29c77daf1d 100644 --- a/packages/nx/project.json +++ b/packages/nx/project.json @@ -10,7 +10,7 @@ "executor": "@monodon/rust:napi", "options": { "dist": "packages/nx/src/native", - "jsFile": "packages/nx/src/native/index.js", + "jsFile": "packages/nx/src/native/native-bindings.js", "release": true }, "configurations": { diff --git a/packages/nx/src/command-line/init/implementation/react/check-for-uncommitted-changes.ts b/packages/nx/src/command-line/init/implementation/react/check-for-uncommitted-changes.ts index 7f3bb1c6a81b5..170b146ba2bce 100644 --- a/packages/nx/src/command-line/init/implementation/react/check-for-uncommitted-changes.ts +++ b/packages/nx/src/command-line/init/implementation/react/check-for-uncommitted-changes.ts @@ -1,12 +1,17 @@ import { execSync } from 'child_process'; export function checkForUncommittedChanges() { - const gitResult = execSync(`git status --porcelain`); - if (gitResult.length > 0) { + const gitResult = execSync('git status --porcelain').toString(); + + const filteredResults = gitResult + .split('\n') + .filter((line) => !line.includes('.nx') && line.trim().length > 0); + + if (filteredResults.length > 0) { console.log('❗️ Careful!'); console.log('You have uncommitted changes in your repository.'); console.log(''); - console.log(gitResult.toString()); + console.log(filteredResults.join('\n').toString()); console.log('Please commit your changes before running the migrator!'); process.exit(1); } diff --git a/packages/nx/src/daemon/cache.ts b/packages/nx/src/daemon/cache.ts index fff36b3bdc0a1..b06b2301078e7 100644 --- a/packages/nx/src/daemon/cache.ts +++ b/packages/nx/src/daemon/cache.ts @@ -43,6 +43,25 @@ export async function safelyCleanUpExistingProcess(): Promise { if (daemonProcessJson && daemonProcessJson.processId) { try { process.kill(daemonProcessJson.processId); + // we wait for the process to actually shut down before returning + await new Promise((resolve, reject) => { + let count = 0; + const interval = setInterval(() => { + try { + // sending a signal 0 to a process checks if the process is running instead of actually killing it + process.kill(daemonProcessJson.processId, 0); + } catch (e) { + clearInterval(interval); + resolve(); + } + if ((count += 1) > 200) { + clearInterval(interval); + reject( + `Daemon process ${daemonProcessJson.processId} didn't exit after 2 seconds.` + ); + } + }, 10); + }); } catch {} } deleteDaemonJsonProcessCache(); diff --git a/packages/nx/src/daemon/client/client.ts b/packages/nx/src/daemon/client/client.ts index a2c6771853009..6ebf3d72e851c 100644 --- a/packages/nx/src/daemon/client/client.ts +++ b/packages/nx/src/daemon/client/client.ts @@ -25,8 +25,10 @@ import { safelyCleanUpExistingProcess } from '../cache'; import { Hash } from '../../hasher/task-hasher'; import { Task, TaskGraph } from '../../config/task-graph'; import { ConfigurationSourceMaps } from '../../project-graph/utils/project-configuration-utils'; -import { DaemonProjectGraphError } from '../daemon-project-graph-error'; -import { ProjectGraphError } from '../../project-graph/project-graph'; +import { + DaemonProjectGraphError, + ProjectGraphError, +} from '../../project-graph/error-types'; const DAEMON_ENV_SETTINGS = { NX_PROJECT_GLOB_CACHE: 'false', diff --git a/packages/nx/src/daemon/daemon-project-graph-error.ts b/packages/nx/src/daemon/daemon-project-graph-error.ts deleted file mode 100644 index 518af6db42d38..0000000000000 --- a/packages/nx/src/daemon/daemon-project-graph-error.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ProjectGraph } from '../config/project-graph'; -import { ConfigurationSourceMaps } from '../project-graph/utils/project-configuration-utils'; - -export class DaemonProjectGraphError extends Error { - constructor( - public errors: any[], - readonly projectGraph: ProjectGraph, - readonly sourceMaps: ConfigurationSourceMaps - ) { - super( - `The Daemon Process threw an error while calculating the project graph. Convert this error to a ProjectGraphError to get more information.` - ); - this.name = this.constructor.name; - } -} diff --git a/packages/nx/src/daemon/server/handle-hash-tasks.ts b/packages/nx/src/daemon/server/handle-hash-tasks.ts index c8cb7b7daec3e..9d99fa88a97e2 100644 --- a/packages/nx/src/daemon/server/handle-hash-tasks.ts +++ b/packages/nx/src/daemon/server/handle-hash-tasks.ts @@ -2,7 +2,7 @@ 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 { DaemonProjectGraphError } from '../daemon-project-graph-error'; +import { DaemonProjectGraphError } from '../../project-graph/error-types'; /** * We use this not to recreated hasher for every hash operation diff --git a/packages/nx/src/daemon/server/project-graph-incremental-recomputation.ts b/packages/nx/src/daemon/server/project-graph-incremental-recomputation.ts index da1285d192041..3d09d9ff0a5ef 100644 --- a/packages/nx/src/daemon/server/project-graph-incremental-recomputation.ts +++ b/packages/nx/src/daemon/server/project-graph-incremental-recomputation.ts @@ -33,10 +33,12 @@ import { notifyFileWatcherSockets } from './file-watching/file-watcher-sockets'; import { serverLogger } from './logger'; import { NxWorkspaceFilesExternals } from '../../native'; import { ConfigurationResult } from '../../project-graph/utils/project-configuration-utils'; -import { DaemonProjectGraphError } from '../daemon-project-graph-error'; import { LoadedNxPlugin } from '../../project-graph/plugins/internal-api'; import { getPlugins } from './plugins'; -import { ProjectConfigurationsError } from '../../project-graph/error-types'; +import { + DaemonProjectGraphError, + ProjectConfigurationsError, +} from '../../project-graph/error-types'; interface SerializedProjectGraph { error: Error | null; diff --git a/packages/nx/src/daemon/socket-utils.ts b/packages/nx/src/daemon/socket-utils.ts index 9796bae866a74..0ac574fc0d058 100644 --- a/packages/nx/src/daemon/socket-utils.ts +++ b/packages/nx/src/daemon/socket-utils.ts @@ -2,7 +2,7 @@ import { unlinkSync } from 'fs'; import { platform } from 'os'; import { join, resolve } from 'path'; import { DAEMON_SOCKET_PATH, socketDir } from './tmp-dir'; -import { DaemonProjectGraphError } from './daemon-project-graph-error'; +import { DaemonProjectGraphError } from '../project-graph/error-types'; export const isWindows = platform() === 'win32'; diff --git a/packages/nx/src/native/index.js b/packages/nx/src/native/index.js index 21277c6d1ed10..7f0ce5277f6f9 100644 --- a/packages/nx/src/native/index.js +++ b/packages/nx/src/native/index.js @@ -1,268 +1,77 @@ -const { existsSync, readFileSync } = require('fs') -const { join } = require('path') +const { join, basename } = require('path'); +const { copyFileSync, existsSync, mkdirSync } = require('fs'); +const Module = require('module'); +const { nxVersion} = require("../utils/versions") +const { cacheDir} = require("../utils/cache-directory") -const { platform, arch } = process +const nxPackages = new Set([ + '@nx/nx-android-arm64', + '@nx/nx-android-arm-eabi', + '@nx/nx-win32-x64-msvc', + '@nx/nx-win32-ia32-msvc', + '@nx/nx-win32-arm64-msvc', + '@nx/nx-darwin-universal', + '@nx/nx-darwin-x64', + '@nx/nx-darwin-arm64', + '@nx/nx-freebsd-x64', + '@nx/nx-linux-x64-musl', + '@nx/nx-linux-x64-gnu', + '@nx/nx-linux-arm64-musl', + '@nx/nx-linux-arm64-gnu', + '@nx/nx-linux-arm-gnueabihf', +]); -let nativeBinding = null -let localFileExisted = false -let loadError = null +const localNodeFiles = [ + 'nx.android-arm64.node', + 'nx.android-arm-eabi.node', + 'nx.win32-x64-msvc.node', + 'nx.win32-ia32-msvc.node', + 'nx.win32-arm64-msvc.node', + 'nx.darwin-universal.node', + 'nx.darwin-x64.node', + 'nx.darwin-arm64.node', + 'nx.freebsd-x64.node', + 'nx.linux-x64-musl.node', + 'nx.linux-x64-gnu.node', + 'nx.linux-arm64-musl.node', + 'nx.linux-arm64-gnu.node', + 'nx.linux-arm-gnueabihf.node', +]; -function isMusl() { - // For Node 10 - if (!process.report || typeof process.report.getReport !== 'function') { - try { - const lddPath = require('child_process').execSync('which ldd').toString().trim(); - return readFileSync(lddPath, 'utf8').includes('musl') - } catch (e) { - return true - } - } else { - const { glibcVersionRuntime } = process.report.getReport().header - return !glibcVersionRuntime - } -} +const originalLoad = Module._load; -switch (platform) { - case 'android': - switch (arch) { - case 'arm64': - localFileExisted = existsSync(join(__dirname, 'nx.android-arm64.node')) - try { - if (localFileExisted) { - nativeBinding = require('./nx.android-arm64.node') - } else { - nativeBinding = require('@nx/nx-android-arm64') - } - } catch (e) { - loadError = e - } - break - case 'arm': - localFileExisted = existsSync(join(__dirname, 'nx.android-arm-eabi.node')) - try { - if (localFileExisted) { - nativeBinding = require('./nx.android-arm-eabi.node') - } else { - nativeBinding = require('@nx/nx-android-arm-eabi') - } - } catch (e) { - loadError = e - } - break - default: - throw new Error(`Unsupported architecture on Android ${arch}`) - } - break - case 'win32': - switch (arch) { - case 'x64': - localFileExisted = existsSync( - join(__dirname, 'nx.win32-x64-msvc.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./nx.win32-x64-msvc.node') - } else { - nativeBinding = require('@nx/nx-win32-x64-msvc') - } - } catch (e) { - loadError = e - } - break - case 'ia32': - localFileExisted = existsSync( - join(__dirname, 'nx.win32-ia32-msvc.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./nx.win32-ia32-msvc.node') - } else { - nativeBinding = require('@nx/nx-win32-ia32-msvc') - } - } catch (e) { - loadError = e - } - break - case 'arm64': - localFileExisted = existsSync( - join(__dirname, 'nx.win32-arm64-msvc.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./nx.win32-arm64-msvc.node') - } else { - nativeBinding = require('@nx/nx-win32-arm64-msvc') - } - } catch (e) { - loadError = e - } - break - default: - throw new Error(`Unsupported architecture on Windows: ${arch}`) - } - break - case 'darwin': - localFileExisted = existsSync(join(__dirname, 'nx.darwin-universal.node')) - try { - if (localFileExisted) { - nativeBinding = require('./nx.darwin-universal.node') - } else { - nativeBinding = require('@nx/nx-darwin-universal') - } - break - } catch {} - switch (arch) { - case 'x64': - localFileExisted = existsSync(join(__dirname, 'nx.darwin-x64.node')) - try { - if (localFileExisted) { - nativeBinding = require('./nx.darwin-x64.node') - } else { - nativeBinding = require('@nx/nx-darwin-x64') - } - } catch (e) { - loadError = e - } - break - case 'arm64': - localFileExisted = existsSync( - join(__dirname, 'nx.darwin-arm64.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./nx.darwin-arm64.node') - } else { - nativeBinding = require('@nx/nx-darwin-arm64') - } - } catch (e) { - loadError = e - } - break - default: - throw new Error(`Unsupported architecture on macOS: ${arch}`) +// We override the _load function so that when a native file is required, +// we copy it to a cache directory and require it from there. +// This prevents the file being loaded from node_modules and causing file locking issues. +// Will only be called once because the require cache takes over afterwards. +Module._load = function (request, parent, isMain) { + const modulePath = request; + if ( + nxPackages.has(modulePath) || + localNodeFiles.some((f) => modulePath.endsWith(f)) + ) { + const nativeLocation = require.resolve(modulePath); + const fileName = basename(nativeLocation) + // we copy the file to the cache directory (.nx/cache by default) and prefix with nxVersion to avoid stale files being loaded + const tmpFile = join(cacheDir, nxVersion + '-' + fileName); + if (existsSync(tmpFile)) { + return originalLoad.apply(this, [tmpFile, parent, isMain]); } - break - case 'freebsd': - if (arch !== 'x64') { - throw new Error(`Unsupported architecture on FreeBSD: ${arch}`) + if (!existsSync(cacheDir)) { + mkdirSync(cacheDir, { recursive: true }); } - localFileExisted = existsSync(join(__dirname, 'nx.freebsd-x64.node')) - try { - if (localFileExisted) { - nativeBinding = require('./nx.freebsd-x64.node') - } else { - nativeBinding = require('@nx/nx-freebsd-x64') - } - } catch (e) { - loadError = e - } - break - case 'linux': - switch (arch) { - case 'x64': - if (isMusl()) { - localFileExisted = existsSync( - join(__dirname, 'nx.linux-x64-musl.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./nx.linux-x64-musl.node') - } else { - nativeBinding = require('@nx/nx-linux-x64-musl') - } - } catch (e) { - loadError = e - } - } else { - localFileExisted = existsSync( - join(__dirname, 'nx.linux-x64-gnu.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./nx.linux-x64-gnu.node') - } else { - nativeBinding = require('@nx/nx-linux-x64-gnu') - } - } catch (e) { - loadError = e - } - } - break - case 'arm64': - if (isMusl()) { - localFileExisted = existsSync( - join(__dirname, 'nx.linux-arm64-musl.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./nx.linux-arm64-musl.node') - } else { - nativeBinding = require('@nx/nx-linux-arm64-musl') - } - } catch (e) { - loadError = e - } - } else { - localFileExisted = existsSync( - join(__dirname, 'nx.linux-arm64-gnu.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./nx.linux-arm64-gnu.node') - } else { - nativeBinding = require('@nx/nx-linux-arm64-gnu') - } - } catch (e) { - loadError = e - } - } - break - case 'arm': - localFileExisted = existsSync( - join(__dirname, 'nx.linux-arm-gnueabihf.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./nx.linux-arm-gnueabihf.node') - } else { - nativeBinding = require('@nx/nx-linux-arm-gnueabihf') - } - } catch (e) { - loadError = e - } - break - default: - throw new Error(`Unsupported architecture on Linux: ${arch}`) - } - break - default: - throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`) -} - -if (!nativeBinding) { - if (loadError) { - throw loadError + copyFileSync(nativeLocation, tmpFile); + return originalLoad.apply(this, [tmpFile, parent, isMain]); + } else { + // call the original _load function for everything else + return originalLoad.apply(this, arguments); } - throw new Error(`Failed to load native binding`) -} +}; + +const indexModulePath = require.resolve('./native-bindings.js'); +delete require.cache[indexModulePath]; +const indexModule = require('./native-bindings.js'); -const { expandOutputs, getFilesForOutputs, remove, copy, hashArray, hashFile, ImportResult, findImports, transferProjectGraph, ChildProcess, RustPseudoTerminal, HashPlanner, TaskHasher, EventType, Watcher, WorkspaceContext, WorkspaceErrors, testOnlyTransferFileMap } = nativeBinding -module.exports.expandOutputs = expandOutputs -module.exports.getFilesForOutputs = getFilesForOutputs -module.exports.remove = remove -module.exports.copy = copy -module.exports.hashArray = hashArray -module.exports.hashFile = hashFile -module.exports.ImportResult = ImportResult -module.exports.findImports = findImports -module.exports.transferProjectGraph = transferProjectGraph -module.exports.ChildProcess = ChildProcess -module.exports.RustPseudoTerminal = RustPseudoTerminal -module.exports.HashPlanner = HashPlanner -module.exports.TaskHasher = TaskHasher -module.exports.EventType = EventType -module.exports.Watcher = Watcher -module.exports.WorkspaceContext = WorkspaceContext -module.exports.WorkspaceErrors = WorkspaceErrors -module.exports.testOnlyTransferFileMap = testOnlyTransferFileMap +module.exports = indexModule; +Module._load = originalLoad; diff --git a/packages/nx/src/native/native-bindings.js b/packages/nx/src/native/native-bindings.js new file mode 100644 index 0000000000000..005745e9166dc --- /dev/null +++ b/packages/nx/src/native/native-bindings.js @@ -0,0 +1,290 @@ +const { existsSync, readFileSync } = require('fs'); +const { join } = require('path'); + +const { platform, arch } = process; + +let nativeBinding = null; +let localFileExisted = false; +let loadError = null; + +function isMusl() { + // For Node 10 + if (!process.report || typeof process.report.getReport !== 'function') { + try { + const lddPath = require('child_process') + .execSync('which ldd') + .toString() + .trim(); + return readFileSync(lddPath, 'utf8').includes('musl'); + } catch (e) { + return true; + } + } else { + const { glibcVersionRuntime } = process.report.getReport().header; + return !glibcVersionRuntime; + } +} + +switch (platform) { + case 'android': + switch (arch) { + case 'arm64': + localFileExisted = existsSync(join(__dirname, 'nx.android-arm64.node')); + try { + if (localFileExisted) { + nativeBinding = require('./nx.android-arm64.node'); + } else { + nativeBinding = require('@nx/nx-android-arm64'); + } + } catch (e) { + loadError = e; + } + break; + case 'arm': + localFileExisted = existsSync( + join(__dirname, 'nx.android-arm-eabi.node') + ); + try { + if (localFileExisted) { + nativeBinding = require('./nx.android-arm-eabi.node'); + } else { + nativeBinding = require('@nx/nx-android-arm-eabi'); + } + } catch (e) { + loadError = e; + } + break; + default: + throw new Error(`Unsupported architecture on Android ${arch}`); + } + break; + case 'win32': + switch (arch) { + case 'x64': + localFileExisted = existsSync( + join(__dirname, 'nx.win32-x64-msvc.node') + ); + try { + if (localFileExisted) { + nativeBinding = require('./nx.win32-x64-msvc.node'); + } else { + nativeBinding = require('@nx/nx-win32-x64-msvc'); + } + } catch (e) { + loadError = e; + } + break; + case 'ia32': + localFileExisted = existsSync( + join(__dirname, 'nx.win32-ia32-msvc.node') + ); + try { + if (localFileExisted) { + nativeBinding = require('./nx.win32-ia32-msvc.node'); + } else { + nativeBinding = require('@nx/nx-win32-ia32-msvc'); + } + } catch (e) { + loadError = e; + } + break; + case 'arm64': + localFileExisted = existsSync( + join(__dirname, 'nx.win32-arm64-msvc.node') + ); + try { + if (localFileExisted) { + nativeBinding = require('./nx.win32-arm64-msvc.node'); + } else { + nativeBinding = require('@nx/nx-win32-arm64-msvc'); + } + } catch (e) { + loadError = e; + } + break; + default: + throw new Error(`Unsupported architecture on Windows: ${arch}`); + } + break; + case 'darwin': + localFileExisted = existsSync(join(__dirname, 'nx.darwin-universal.node')); + try { + if (localFileExisted) { + nativeBinding = require('./nx.darwin-universal.node'); + } else { + nativeBinding = require('@nx/nx-darwin-universal'); + } + break; + } catch {} + switch (arch) { + case 'x64': + localFileExisted = existsSync(join(__dirname, 'nx.darwin-x64.node')); + try { + if (localFileExisted) { + nativeBinding = require('./nx.darwin-x64.node'); + } else { + nativeBinding = require('@nx/nx-darwin-x64'); + } + } catch (e) { + loadError = e; + } + break; + case 'arm64': + localFileExisted = existsSync(join(__dirname, 'nx.darwin-arm64.node')); + try { + if (localFileExisted) { + nativeBinding = require('./nx.darwin-arm64.node'); + } else { + nativeBinding = require('@nx/nx-darwin-arm64'); + } + } catch (e) { + loadError = e; + } + break; + default: + throw new Error(`Unsupported architecture on macOS: ${arch}`); + } + break; + case 'freebsd': + if (arch !== 'x64') { + throw new Error(`Unsupported architecture on FreeBSD: ${arch}`); + } + localFileExisted = existsSync(join(__dirname, 'nx.freebsd-x64.node')); + try { + if (localFileExisted) { + nativeBinding = require('./nx.freebsd-x64.node'); + } else { + nativeBinding = require('@nx/nx-freebsd-x64'); + } + } catch (e) { + loadError = e; + } + break; + case 'linux': + switch (arch) { + case 'x64': + if (isMusl()) { + localFileExisted = existsSync( + join(__dirname, 'nx.linux-x64-musl.node') + ); + try { + if (localFileExisted) { + nativeBinding = require('./nx.linux-x64-musl.node'); + } else { + nativeBinding = require('@nx/nx-linux-x64-musl'); + } + } catch (e) { + loadError = e; + } + } else { + localFileExisted = existsSync( + join(__dirname, 'nx.linux-x64-gnu.node') + ); + try { + if (localFileExisted) { + nativeBinding = require('./nx.linux-x64-gnu.node'); + } else { + nativeBinding = require('@nx/nx-linux-x64-gnu'); + } + } catch (e) { + loadError = e; + } + } + break; + case 'arm64': + if (isMusl()) { + localFileExisted = existsSync( + join(__dirname, 'nx.linux-arm64-musl.node') + ); + try { + if (localFileExisted) { + nativeBinding = require('./nx.linux-arm64-musl.node'); + } else { + nativeBinding = require('@nx/nx-linux-arm64-musl'); + } + } catch (e) { + loadError = e; + } + } else { + localFileExisted = existsSync( + join(__dirname, 'nx.linux-arm64-gnu.node') + ); + try { + if (localFileExisted) { + nativeBinding = require('./nx.linux-arm64-gnu.node'); + } else { + nativeBinding = require('@nx/nx-linux-arm64-gnu'); + } + } catch (e) { + loadError = e; + } + } + break; + case 'arm': + localFileExisted = existsSync( + join(__dirname, 'nx.linux-arm-gnueabihf.node') + ); + try { + if (localFileExisted) { + nativeBinding = require('./nx.linux-arm-gnueabihf.node'); + } else { + nativeBinding = require('@nx/nx-linux-arm-gnueabihf'); + } + } catch (e) { + loadError = e; + } + break; + default: + throw new Error(`Unsupported architecture on Linux: ${arch}`); + } + break; + default: + throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`); +} + +if (!nativeBinding) { + if (loadError) { + throw loadError; + } + throw new Error(`Failed to load native binding`); +} + +const { + expandOutputs, + getFilesForOutputs, + remove, + copy, + hashArray, + hashFile, + ImportResult, + findImports, + transferProjectGraph, + ChildProcess, + RustPseudoTerminal, + HashPlanner, + TaskHasher, + EventType, + Watcher, + WorkspaceContext, + WorkspaceErrors, + testOnlyTransferFileMap, +} = nativeBinding; + +module.exports.expandOutputs = expandOutputs; +module.exports.getFilesForOutputs = getFilesForOutputs; +module.exports.remove = remove; +module.exports.copy = copy; +module.exports.hashArray = hashArray; +module.exports.hashFile = hashFile; +module.exports.ImportResult = ImportResult; +module.exports.findImports = findImports; +module.exports.transferProjectGraph = transferProjectGraph; +module.exports.ChildProcess = ChildProcess; +module.exports.RustPseudoTerminal = RustPseudoTerminal; +module.exports.HashPlanner = HashPlanner; +module.exports.TaskHasher = TaskHasher; +module.exports.EventType = EventType; +module.exports.Watcher = Watcher; +module.exports.WorkspaceContext = WorkspaceContext; +module.exports.WorkspaceErrors = WorkspaceErrors; +module.exports.testOnlyTransferFileMap = testOnlyTransferFileMap; diff --git a/packages/nx/src/project-graph/error-types.ts b/packages/nx/src/project-graph/error-types.ts index 4841355d6b8fe..33d49ec65b17e 100644 --- a/packages/nx/src/project-graph/error-types.ts +++ b/packages/nx/src/project-graph/error-types.ts @@ -1,6 +1,75 @@ import { CreateNodesResultWithContext } from './plugins/internal-api'; -import { ConfigurationResult } from './utils/project-configuration-utils'; +import { + ConfigurationResult, + ConfigurationSourceMaps, +} from './utils/project-configuration-utils'; import { ProjectConfiguration } from '../config/workspace-json-project-json'; +import { + ProcessDependenciesError, + ProcessProjectGraphError, +} from './build-project-graph'; +import { ProjectGraph } from '../config/project-graph'; + +export class ProjectGraphError extends Error { + readonly #errors: Array< + | CreateNodesError + | MergeNodesError + | ProjectsWithNoNameError + | ProjectsWithConflictingNamesError + | ProcessDependenciesError + | ProcessProjectGraphError + >; + readonly #partialProjectGraph: ProjectGraph; + readonly #partialSourceMaps: ConfigurationSourceMaps; + + constructor( + errors: Array< + | CreateNodesError + | MergeNodesError + | ProjectsWithNoNameError + | ProjectsWithConflictingNamesError + | ProcessDependenciesError + | ProcessProjectGraphError + >, + partialProjectGraph: ProjectGraph, + partialSourceMaps: ConfigurationSourceMaps + ) { + super(`Failed to process project graph.`); + this.name = this.constructor.name; + this.#errors = errors; + this.#partialProjectGraph = partialProjectGraph; + this.#partialSourceMaps = partialSourceMaps; + this.stack = `${this.message}\n ${errors + .map((error) => error.stack.split('\n').join('\n ')) + .join('\n')}`; + } + + /** + * The daemon cannot throw errors which contain methods as they are not serializable. + * + * This method creates a new {@link ProjectGraphError} from a {@link DaemonProjectGraphError} with the methods based on the same serialized data. + */ + static fromDaemonProjectGraphError(e: DaemonProjectGraphError) { + return new ProjectGraphError(e.errors, e.projectGraph, e.sourceMaps); + } + + /** + * This gets the partial project graph despite the errors which occured. + * This partial project graph may be missing nodes, properties of nodes, or dependencies. + * This is useful mostly for visualization/debugging. It should not be used for running tasks. + */ + getPartialProjectGraph() { + return this.#partialProjectGraph; + } + + getPartialSourcemaps() { + return this.#partialSourceMaps; + } + + getErrors() { + return this.#errors; + } +} export class ProjectsWithConflictingNamesError extends Error { constructor( @@ -153,3 +222,16 @@ export function isMergeNodesError(e: unknown): e is MergeNodesError { (typeof e === 'object' && 'name' in e && e?.name === MergeNodesError.name) ); } + +export class DaemonProjectGraphError extends Error { + constructor( + public errors: any[], + readonly projectGraph: ProjectGraph, + readonly sourceMaps: ConfigurationSourceMaps + ) { + super( + `The Daemon Process threw an error while calculating the project graph. Convert this error to a ProjectGraphError to get more information.` + ); + this.name = this.constructor.name; + } +} diff --git a/packages/nx/src/project-graph/project-graph.ts b/packages/nx/src/project-graph/project-graph.ts index 5fa8035661aed..f5c7ea72e0ae9 100644 --- a/packages/nx/src/project-graph/project-graph.ts +++ b/packages/nx/src/project-graph/project-graph.ts @@ -1,44 +1,33 @@ -import { - readFileMapCache, - readProjectGraphCache, - writeCache, -} from './nx-deps-cache'; -import { - CreateDependenciesError, - ProcessDependenciesError, - ProcessProjectGraphError, - buildProjectGraphUsingProjectFileMap, -} from './build-project-graph'; -import { output } from '../utils/output'; -import { markDaemonAsDisabled, writeDaemonLogs } from '../daemon/tmp-dir'; +import { performance } from 'perf_hooks'; +import { readNxJson } from '../config/nx-json'; import { ProjectGraph } from '../config/project-graph'; -import { stripIndents } from '../utils/strip-indents'; import { ProjectConfiguration, ProjectsConfigurations, } from '../config/workspace-json-project-json'; import { daemonClient } from '../daemon/client/client'; +import { markDaemonAsDisabled, writeDaemonLogs } from '../daemon/tmp-dir'; import { fileExists } from '../utils/fileutils'; +import { output } from '../utils/output'; +import { stripIndents } from '../utils/strip-indents'; import { workspaceRoot } from '../utils/workspace-root'; -import { performance } from 'perf_hooks'; +import { + CreateDependenciesError, + buildProjectGraphUsingProjectFileMap, +} from './build-project-graph'; +import { + readFileMapCache, + readProjectGraphCache, + writeCache, +} from './nx-deps-cache'; + +import { ProjectConfigurationsError, ProjectGraphError } from './error-types'; +import { loadNxPlugins } from './plugins/internal-api'; +import { ConfigurationResult } from './utils/project-configuration-utils'; import { retrieveProjectConfigurations, retrieveWorkspaceFiles, } from './utils/retrieve-workspace-files'; -import { readNxJson } from '../config/nx-json'; -import { - ConfigurationResult, - ConfigurationSourceMaps, -} from './utils/project-configuration-utils'; -import { - CreateNodesError, - MergeNodesError, - ProjectConfigurationsError, - ProjectsWithNoNameError, - ProjectsWithConflictingNamesError, -} from './error-types'; -import { DaemonProjectGraphError } from '../daemon/daemon-project-graph-error'; -import { loadNxPlugins, LoadedNxPlugin } from './plugins/internal-api'; /** * Synchronously reads the latest cached copy of the workspace's ProjectGraph. @@ -179,67 +168,6 @@ export async function buildProjectGraphAndSourceMapsWithoutDaemon() { } } -export class ProjectGraphError extends Error { - readonly #errors: Array< - | CreateNodesError - | MergeNodesError - | ProjectsWithNoNameError - | ProjectsWithConflictingNamesError - | ProcessDependenciesError - | ProcessProjectGraphError - >; - readonly #partialProjectGraph: ProjectGraph; - readonly #partialSourceMaps: ConfigurationSourceMaps; - - constructor( - errors: Array< - | CreateNodesError - | MergeNodesError - | ProjectsWithNoNameError - | ProjectsWithConflictingNamesError - | ProcessDependenciesError - | ProcessProjectGraphError - >, - partialProjectGraph: ProjectGraph, - partialSourceMaps: ConfigurationSourceMaps - ) { - super(`Failed to process project graph.`); - this.name = this.constructor.name; - this.#errors = errors; - this.#partialProjectGraph = partialProjectGraph; - this.#partialSourceMaps = partialSourceMaps; - this.stack = `${this.message}\n ${errors - .map((error) => error.stack.split('\n').join('\n ')) - .join('\n')}`; - } - - /** - * The daemon cannot throw errors which contain methods as they are not serializable. - * - * This method creates a new {@link ProjectGraphError} from a {@link DaemonProjectGraphError} with the methods based on the same serialized data. - */ - static fromDaemonProjectGraphError(e: DaemonProjectGraphError) { - return new ProjectGraphError(e.errors, e.projectGraph, e.sourceMaps); - } - - /** - * This gets the partial project graph despite the errors which occured. - * This partial project graph may be missing nodes, properties of nodes, or dependencies. - * This is useful mostly for visualization/debugging. It should not be used for running tasks. - */ - getPartialProjectGraph() { - return this.#partialProjectGraph; - } - - getPartialSourcemaps() { - return this.#partialSourceMaps; - } - - getErrors() { - return this.#errors; - } -} - function handleProjectGraphError(opts: { exitOnError: boolean }, e) { if (opts.exitOnError) { const isVerbose = process.env.NX_VERBOSE_LOGGING === 'true'; diff --git a/packages/nx/src/utils/params.ts b/packages/nx/src/utils/params.ts index b52bad742b1dd..aa0a08379f93c 100644 --- a/packages/nx/src/utils/params.ts +++ b/packages/nx/src/utils/params.ts @@ -5,7 +5,7 @@ import type { ProjectsConfigurations, } from '../config/workspace-json-project-json'; import { output } from './output'; -import type { ProjectGraphError } from '../project-graph/project-graph'; +import type { ProjectGraphError } from '../project-graph/error-types'; const LIST_CHOICE_DISPLAY_LIMIT = 10;